uboot/drivers/gpio/pcf8575_gpio.c
<<
>>
Prefs
   1/*
   2 * PCF8575 I2C GPIO EXPANDER DRIVER
   3 *
   4 * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
   5 *
   6 * Vignesh R <vigneshr@ti.com>
   7 *
   8 * SPDX-License-Identifier:     GPL-2.0
   9 *
  10 *
  11 * Driver for TI PCF-8575 16-bit I2C gpio expander. Based on
  12 * gpio-pcf857x Linux Kernel(v4.7) driver.
  13 *
  14 * Copyright (C) 2007 David Brownell
  15 *
  16 */
  17
  18/*
  19 * NOTE: The driver and devicetree bindings are borrowed from Linux
  20 * Kernel, but driver does not support all PCF857x devices. It currently
  21 * supports PCF8575 16-bit expander by TI and NXP.
  22 *
  23 * TODO(vigneshr@ti.com):
  24 * Support 8 bit PCF857x compatible expanders.
  25 */
  26
  27#include <common.h>
  28#include <dm.h>
  29#include <i2c.h>
  30#include <asm-generic/gpio.h>
  31
  32DECLARE_GLOBAL_DATA_PTR;
  33
  34struct pcf8575_chip {
  35        int gpio_count;         /* No. GPIOs supported by the chip */
  36
  37        /* NOTE:  these chips have strange "quasi-bidirectional" I/O pins.
  38         * We can't actually know whether a pin is configured (a) as output
  39         * and driving the signal low, or (b) as input and reporting a low
  40         * value ... without knowing the last value written since the chip
  41         * came out of reset (if any).  We can't read the latched output.
  42         * In short, the only reliable solution for setting up pin direction
  43         * is to do it explicitly.
  44         *
  45         * Using "out" avoids that trouble.  When left initialized to zero,
  46         * our software copy of the "latch" then matches the chip's all-ones
  47         * reset state.  Otherwise it flags pins to be driven low.
  48         */
  49        unsigned int out;       /* software latch */
  50        const char *bank_name;  /* Name of the expander bank */
  51};
  52
  53/* Read/Write to 16-bit I/O expander */
  54
  55static int pcf8575_i2c_write_le16(struct udevice *dev, unsigned int word)
  56{
  57        struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
  58        u8 buf[2] = { word & 0xff, word >> 8, };
  59        int ret;
  60
  61        ret = dm_i2c_write(dev, 0, buf, 2);
  62        if (ret)
  63                printf("%s i2c write failed to addr %x\n", __func__,
  64                       chip->chip_addr);
  65
  66        return ret;
  67}
  68
  69static int pcf8575_i2c_read_le16(struct udevice *dev)
  70{
  71        struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
  72        u8 buf[2];
  73        int ret;
  74
  75        ret = dm_i2c_read(dev, 0, buf, 2);
  76        if (ret) {
  77                printf("%s i2c read failed from addr %x\n", __func__,
  78                       chip->chip_addr);
  79                return ret;
  80        }
  81
  82        return (buf[1] << 8) | buf[0];
  83}
  84
  85static int pcf8575_direction_input(struct udevice *dev, unsigned offset)
  86{
  87        struct pcf8575_chip *plat = dev_get_platdata(dev);
  88        int status;
  89
  90        plat->out |= BIT(offset);
  91        status = pcf8575_i2c_write_le16(dev, plat->out);
  92
  93        return status;
  94}
  95
  96static int pcf8575_direction_output(struct udevice *dev,
  97                                    unsigned int offset, int value)
  98{
  99        struct pcf8575_chip *plat = dev_get_platdata(dev);
 100        int ret;
 101
 102        if (value)
 103                plat->out |= BIT(offset);
 104        else
 105                plat->out &= ~BIT(offset);
 106
 107        ret = pcf8575_i2c_write_le16(dev, plat->out);
 108
 109        return ret;
 110}
 111
 112static int pcf8575_get_value(struct udevice *dev, unsigned int offset)
 113{
 114        int             value;
 115
 116        value = pcf8575_i2c_read_le16(dev);
 117
 118        return (value < 0) ? value : ((value & BIT(offset)) >> offset);
 119}
 120
 121static int pcf8575_set_value(struct udevice *dev, unsigned int offset,
 122                             int value)
 123{
 124        return pcf8575_direction_output(dev, offset, value);
 125}
 126
 127static int pcf8575_ofdata_platdata(struct udevice *dev)
 128{
 129        struct pcf8575_chip *plat = dev_get_platdata(dev);
 130        struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 131
 132        int n_latch;
 133
 134        uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
 135                                             "gpio-count", 16);
 136        uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
 137                                         "gpio-bank-name", NULL);
 138        if (!uc_priv->bank_name)
 139                uc_priv->bank_name = fdt_get_name(gd->fdt_blob,
 140                                                  dev_of_offset(dev), NULL);
 141
 142        n_latch = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
 143                                  "lines-initial-states", 0);
 144        plat->out = ~n_latch;
 145
 146        return 0;
 147}
 148
 149static int pcf8575_gpio_probe(struct udevice  *dev)
 150{
 151        struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 152
 153        debug("%s GPIO controller with %d gpios probed\n",
 154              uc_priv->bank_name, uc_priv->gpio_count);
 155
 156        return 0;
 157}
 158
 159static const struct dm_gpio_ops pcf8575_gpio_ops = {
 160        .direction_input        = pcf8575_direction_input,
 161        .direction_output       = pcf8575_direction_output,
 162        .get_value              = pcf8575_get_value,
 163        .set_value              = pcf8575_set_value,
 164};
 165
 166static const struct udevice_id pcf8575_gpio_ids[] = {
 167        { .compatible = "nxp,pcf8575" },
 168        { .compatible = "ti,pcf8575" },
 169        { }
 170};
 171
 172U_BOOT_DRIVER(gpio_pcf8575) = {
 173        .name   = "gpio_pcf8575",
 174        .id     = UCLASS_GPIO,
 175        .ops    = &pcf8575_gpio_ops,
 176        .of_match = pcf8575_gpio_ids,
 177        .ofdata_to_platdata = pcf8575_ofdata_platdata,
 178        .probe  = pcf8575_gpio_probe,
 179        .platdata_auto_alloc_size = sizeof(struct pcf8575_chip),
 180};
 181