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