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 <vigne...@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

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to