[U-Boot] [U-Boot PATCH 1/3] gpio: pcf8575: Add pcf8575 driver to control gpio lines
Vignesh R
vigneshr at ti.com
Tue Mar 10 16:11:21 CET 2015
TI's pcf8575 is a 16-bit I2C based GPIO expander.The device features a
16-bit quasi-bidirectional I/O ports. Each quasi-bidirectional I/O can
be used as an input or output without the use of a data-direction
control signal. The I/Os should be high before being used as inputs.
This driver is based on pcf857x driver available in Linux 4.0 kernel.
It supports basic reading and writing of gpio pins.
Signed-off-by: Vignesh R <vigneshr at ti.com>
---
drivers/gpio/Makefile | 1 +
drivers/gpio/pcf8575.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++++
include/pcf8575.h | 25 +++++
3 files changed, 274 insertions(+)
create mode 100644 drivers/gpio/pcf8575.c
create mode 100644 include/pcf8575.h
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index aa11f15423a4..1e0e521b4d95 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -9,6 +9,7 @@ ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_DM_GPIO) += gpio-uclass.o
endif
+obj-$(CONFIG_PCF8575) += pcf8575.o
obj-$(CONFIG_AT91_GPIO) += at91_gpio.o
obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o
obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o
diff --git a/drivers/gpio/pcf8575.c b/drivers/gpio/pcf8575.c
new file mode 100644
index 000000000000..1ee92a29760a
--- /dev/null
+++ b/drivers/gpio/pcf8575.c
@@ -0,0 +1,248 @@
+/*
+ * PCF8575 I2C GPIO EXPANDER DRIVER
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver for TI pcf-8575 16 bit I2C gpio expander. Based on
+ * gpio-pcf857x Linux 4.0 kernel driver and pca953x driver in u-boot
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <pcf8575.h>
+
+enum {
+ PCF8575_CMD_INFO,
+ PCF8575_CMD_DEVICE,
+ PCF8575_CMD_OUTPUT,
+ PCF8575_CMD_INPUT,
+};
+
+struct pcf8575_chip {
+ uint8_t addr;
+/* current direction of the pcf lines */
+ unsigned int out;
+};
+
+
+/* NOTE: these chips have strange "quasi-bidirectional" I/O pins.
+ * We can't actually know whether a pin is configured (a) as output
+ * and driving the signal low, or (b) as input and reporting a low
+ * value ... without knowing the last value written since the chip
+ * came out of reset (if any). We can't read the latched output.
+ * In short, the only reliable solution for setting up pin direction
+ * is to do it explicitly.
+ *
+ * Using "out" avoids that trouble. It flags the status of the pins at
+ * boot.
+ *
+ * Each struct stores address of an instance of pcf
+ * and state(direction) of each gpio line for that instance.
+ */
+static struct pcf8575_chip pcf8575_chips[] =
+ CONFIG_SYS_I2C_PCF8575_CHIP;
+
+static struct pcf8575_chip *pcf8575_chip_get(uint8_t addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pcf8575_chips); i++)
+ if (pcf8575_chips[i].addr == addr)
+ return &pcf8575_chips[i];
+
+ return 0;
+}
+
+
+/* Read/Write to 16-bit I/O expander */
+
+static int pcf8575_i2c_write(uint8_t addr, unsigned word)
+{
+ unsigned word_be = ((word & 0xff) << 8) |
+ ((word & 0xff00) >> 8);
+ uint8_t buf = 0;
+ int status;
+
+ status = i2c_write(addr, word_be, 2, &buf, 1);
+
+ return (status < 0) ? status : 0;
+}
+
+static int pcf8575_i2c_read(uint8_t addr)
+{
+ u8 buf[2];
+ int status;
+
+ status = i2c_read(addr, 0, 1, buf, 2);
+ if (status < 0)
+ return status;
+
+ return (buf[1] << 8) | buf[0];
+}
+
+int pcf8575_input(uint8_t addr, unsigned offset)
+{
+ struct pcf8575_chip *chip = pcf8575_chip_get(addr);
+ int status;
+
+ chip->out |= (1 << offset);
+ status = pcf8575_i2c_write(addr, chip->out);
+
+ return status;
+}
+
+int pcf8575_get_val(uint8_t addr, unsigned offset)
+{
+ int value;
+
+ value = pcf8575_i2c_read(addr);
+ return (value < 0) ? 0 : (value & (1 << offset));
+}
+
+int pcf8575_output(uint8_t addr, unsigned offset, int value)
+{
+ struct pcf8575_chip *chip = pcf8575_chip_get(addr);
+ unsigned bit = 1 << offset;
+ int status;
+
+ if (value)
+ chip->out |= bit;
+ else
+ chip->out &= ~bit;
+ status = pcf8575_i2c_write(addr, chip->out);
+
+ return status;
+}
+
+/*
+ * Display pcf8575 information
+ */
+int pcf8575_info(uint8_t addr)
+{
+ int i;
+ uint data;
+ struct pcf8575_chip *chip = pcf8575_chip_get(addr);
+ int nr_gpio = 16;
+ int msb = nr_gpio - 1;
+
+ printf("pcf8575@ 0x%x (%d pins):\n\n", addr, nr_gpio);
+ printf("gpio pins: ");
+ for (i = msb; i >= 0; i--)
+ printf("%x", i);
+ printf("\n");
+ for (i = 11 + nr_gpio; i > 0; i--)
+ printf("-");
+ printf("\n");
+
+ data = chip->out;
+ printf("dir: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? 'i' : 'o');
+ printf("\n");
+
+ data = pcf8575_i2c_read(addr);
+ if (data < 0)
+ return -1;
+ printf("input: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? '1' : '0');
+ printf("\n");
+
+ return 0;
+}
+
+cmd_tbl_t cmd_pcf8575[] = {
+ U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCF8575_CMD_DEVICE, "", ""),
+ U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCF8575_CMD_OUTPUT, "", ""),
+ U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCF8575_CMD_INPUT, "", ""),
+ U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCF8575_CMD_INFO, "", ""),
+};
+
+
+
+int do_pcf8575(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ static uint8_t chip = 0x21;
+ int ret = CMD_RET_USAGE, val;
+ ulong ul_arg2 = 0;
+ ulong ul_arg3 = 0;
+ cmd_tbl_t *c;
+
+ c = find_cmd_tbl(argv[1], cmd_pcf8575, ARRAY_SIZE(cmd_pcf8575));
+
+ /* All commands but "device" require 'maxargs' arguments */
+ if (!c || !((argc == (c->maxargs)) ||
+ (((int)c->cmd == PCF8575_CMD_DEVICE) &&
+ (argc == (c->maxargs - 1))))) {
+ return CMD_RET_USAGE;
+ }
+
+ /* arg2 used as chip number or pin number */
+ if (argc > 2)
+ ul_arg2 = simple_strtoul(argv[2], NULL, 16);
+
+ /* arg3 used as pin or invert value */
+ if (argc > 3)
+ ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 0x1;
+
+ switch ((int)c->cmd) {
+ case PCF8575_CMD_INFO:
+ ret = pcf8575_info(chip);
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ break;
+
+ case PCF8575_CMD_DEVICE:
+ if (argc == 3)
+ chip = (uint8_t)ul_arg2;
+ printf("Current device address: 0x%x\n", chip);
+ ret = CMD_RET_SUCCESS;
+ break;
+
+ case PCF8575_CMD_INPUT:
+ ret = pcf8575_input(chip, ul_arg2);
+ val = pcf8575_get_val(chip, ul_arg2);
+
+ if (val)
+ ret = CMD_RET_FAILURE;
+ else
+ printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2,
+ val);
+ break;
+
+ case PCF8575_CMD_OUTPUT:
+ ret = pcf8575_output(chip, ul_arg2, ul_arg3);
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ break;
+ }
+
+ if (ret == CMD_RET_FAILURE)
+ eprintf("Error talking to chip at 0x%x\n", chip);
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ pcf8575, 5, 1, do_pcf8575,
+ "pcf8575 gpio access",
+ "device [dev]\n"
+ " - show or set current device address\n"
+ "pcf8575 info\n"
+ " - display info for current chip\n"
+ "pcf8575 output pin 0|1\n"
+ " - set pin as output and drive low or high\n"
+ "pcf8575 input pin\n"
+ " - set pin as input and read value"
+);
diff --git a/include/pcf8575.h b/include/pcf8575.h
new file mode 100644
index 000000000000..a082554b4a5a
--- /dev/null
+++ b/include/pcf8575.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __PCF8575_H_
+#define __PCF8575_H_
+
+#define PCF8575_OUT_LOW 0
+#define PCF8575_OUT_HIGH 1
+
+
+int pcf8575_input(uint8_t addr, unsigned offset);
+int pcf8575_get_val(uint8_t addr, unsigned offset);
+int pcf8575_output(uint8_t addr, unsigned offset, int value);
+
+#endif /* __PCF8575_H_ */
--
1.9.1
More information about the U-Boot
mailing list