]>
Commit | Line | Data |
---|---|---|
03773439 PF |
1 | /* |
2 | * Take linux kernel driver drivers/gpio/gpio-pca953x.c for reference. | |
3 | * | |
4 | * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com> | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0+ | |
7 | * | |
8 | */ | |
9 | ||
10 | /* | |
11 | * Note: | |
12 | * The driver's compatible table is borrowed from Linux Kernel, | |
13 | * but now max supported gpio pins is 24 and only PCA953X_TYPE | |
14 | * is supported. PCA957X_TYPE is not supported now. | |
15 | * Also the Polarity Inversion feature is not supported now. | |
16 | * | |
17 | * TODO: | |
18 | * 1. Support PCA957X_TYPE | |
71db3270 | 19 | * 2. Support 24 gpio pins |
20 | * 3. Support Polarity Inversion | |
03773439 PF |
21 | */ |
22 | ||
23 | #include <common.h> | |
24 | #include <errno.h> | |
25 | #include <dm.h> | |
26 | #include <fdtdec.h> | |
27 | #include <i2c.h> | |
28 | #include <malloc.h> | |
29 | #include <asm/gpio.h> | |
30 | #include <asm/io.h> | |
31 | #include <dt-bindings/gpio/gpio.h> | |
32 | ||
33 | #define PCA953X_INPUT 0 | |
34 | #define PCA953X_OUTPUT 1 | |
35 | #define PCA953X_INVERT 2 | |
36 | #define PCA953X_DIRECTION 3 | |
37 | ||
38 | #define PCA_GPIO_MASK 0x00FF | |
39 | #define PCA_INT 0x0100 | |
40 | #define PCA953X_TYPE 0x1000 | |
41 | #define PCA957X_TYPE 0x2000 | |
42 | #define PCA_TYPE_MASK 0xF000 | |
43 | #define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK) | |
44 | ||
45 | enum { | |
46 | PCA953X_DIRECTION_IN, | |
47 | PCA953X_DIRECTION_OUT, | |
48 | }; | |
49 | ||
71db3270 | 50 | #define MAX_BANK 5 |
03773439 PF |
51 | #define BANK_SZ 8 |
52 | ||
53 | DECLARE_GLOBAL_DATA_PTR; | |
54 | ||
55 | /* | |
56 | * struct pca953x_info - Data for pca953x | |
57 | * | |
58 | * @dev: udevice structure for the device | |
59 | * @addr: i2c slave address | |
60 | * @invert: Polarity inversion or not | |
61 | * @gpio_count: the number of gpio pins that the device supports | |
62 | * @chip_type: indicate the chip type,PCA953X or PCA957X | |
63 | * @bank_count: the number of banks that the device supports | |
64 | * @reg_output: array to hold the value of output registers | |
65 | * @reg_direction: array to hold the value of direction registers | |
66 | */ | |
67 | struct pca953x_info { | |
68 | struct udevice *dev; | |
69 | int addr; | |
70 | int invert; | |
71 | int gpio_count; | |
72 | int chip_type; | |
73 | int bank_count; | |
74 | u8 reg_output[MAX_BANK]; | |
75 | u8 reg_direction[MAX_BANK]; | |
76 | }; | |
77 | ||
78 | static int pca953x_write_single(struct udevice *dev, int reg, u8 val, | |
79 | int offset) | |
80 | { | |
81 | struct pca953x_info *info = dev_get_platdata(dev); | |
82 | int bank_shift = fls((info->gpio_count - 1) / BANK_SZ); | |
83 | int off = offset / BANK_SZ; | |
84 | int ret = 0; | |
85 | ||
86 | ret = dm_i2c_write(dev, (reg << bank_shift) + off, &val, 1); | |
87 | if (ret) { | |
88 | dev_err(dev, "%s error\n", __func__); | |
89 | return ret; | |
90 | } | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
95 | static int pca953x_read_single(struct udevice *dev, int reg, u8 *val, | |
96 | int offset) | |
97 | { | |
98 | struct pca953x_info *info = dev_get_platdata(dev); | |
99 | int bank_shift = fls((info->gpio_count - 1) / BANK_SZ); | |
100 | int off = offset / BANK_SZ; | |
101 | int ret; | |
102 | u8 byte; | |
103 | ||
104 | ret = dm_i2c_read(dev, (reg << bank_shift) + off, &byte, 1); | |
105 | if (ret) { | |
106 | dev_err(dev, "%s error\n", __func__); | |
107 | return ret; | |
108 | } | |
109 | ||
110 | *val = byte; | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | static int pca953x_read_regs(struct udevice *dev, int reg, u8 *val) | |
116 | { | |
117 | struct pca953x_info *info = dev_get_platdata(dev); | |
118 | int ret = 0; | |
119 | ||
120 | if (info->gpio_count <= 8) { | |
121 | ret = dm_i2c_read(dev, reg, val, 1); | |
122 | } else if (info->gpio_count <= 16) { | |
123 | ret = dm_i2c_read(dev, reg << 1, val, info->bank_count); | |
71db3270 | 124 | } else if (info->gpio_count == 40) { |
125 | /* Auto increment */ | |
126 | ret = dm_i2c_read(dev, (reg << 3) | 0x80, val, info->bank_count); | |
03773439 PF |
127 | } else { |
128 | dev_err(dev, "Unsupported now\n"); | |
129 | return -EINVAL; | |
130 | } | |
131 | ||
132 | return ret; | |
133 | } | |
134 | ||
135 | static int pca953x_is_output(struct udevice *dev, int offset) | |
136 | { | |
137 | struct pca953x_info *info = dev_get_platdata(dev); | |
138 | ||
139 | int bank = offset / BANK_SZ; | |
140 | int off = offset % BANK_SZ; | |
141 | ||
142 | /*0: output; 1: input */ | |
143 | return !(info->reg_direction[bank] & (1 << off)); | |
144 | } | |
145 | ||
146 | static int pca953x_get_value(struct udevice *dev, unsigned offset) | |
147 | { | |
148 | int ret; | |
149 | u8 val = 0; | |
150 | ||
151 | ret = pca953x_read_single(dev, PCA953X_INPUT, &val, offset); | |
152 | if (ret) | |
153 | return ret; | |
154 | ||
155 | return (val >> offset) & 0x1; | |
156 | } | |
157 | ||
158 | static int pca953x_set_value(struct udevice *dev, unsigned offset, | |
159 | int value) | |
160 | { | |
161 | struct pca953x_info *info = dev_get_platdata(dev); | |
162 | int bank = offset / BANK_SZ; | |
163 | int off = offset % BANK_SZ; | |
164 | u8 val; | |
165 | int ret; | |
166 | ||
167 | if (value) | |
168 | val = info->reg_output[bank] | (1 << off); | |
169 | else | |
170 | val = info->reg_output[bank] & ~(1 << off); | |
171 | ||
172 | ret = pca953x_write_single(dev, PCA953X_OUTPUT, val, offset); | |
173 | if (ret) | |
174 | return ret; | |
175 | ||
176 | info->reg_output[bank] = val; | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | static int pca953x_set_direction(struct udevice *dev, unsigned offset, int dir) | |
182 | { | |
183 | struct pca953x_info *info = dev_get_platdata(dev); | |
184 | int bank = offset / BANK_SZ; | |
185 | int off = offset % BANK_SZ; | |
186 | u8 val; | |
187 | int ret; | |
188 | ||
189 | if (dir == PCA953X_DIRECTION_IN) | |
190 | val = info->reg_direction[bank] | (1 << off); | |
191 | else | |
192 | val = info->reg_direction[bank] & ~(1 << off); | |
193 | ||
194 | ret = pca953x_write_single(dev, PCA953X_DIRECTION, val, offset); | |
195 | if (ret) | |
196 | return ret; | |
197 | ||
198 | info->reg_direction[bank] = val; | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
203 | static int pca953x_direction_input(struct udevice *dev, unsigned offset) | |
204 | { | |
205 | return pca953x_set_direction(dev, offset, PCA953X_DIRECTION_IN); | |
206 | } | |
207 | ||
208 | static int pca953x_direction_output(struct udevice *dev, unsigned offset, | |
209 | int value) | |
210 | { | |
211 | /* Configure output value. */ | |
212 | pca953x_set_value(dev, offset, value); | |
213 | ||
214 | /* Configure direction as output. */ | |
215 | pca953x_set_direction(dev, offset, PCA953X_DIRECTION_OUT); | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | static int pca953x_get_function(struct udevice *dev, unsigned offset) | |
221 | { | |
222 | if (pca953x_is_output(dev, offset)) | |
223 | return GPIOF_OUTPUT; | |
224 | else | |
225 | return GPIOF_INPUT; | |
226 | } | |
227 | ||
228 | static int pca953x_xlate(struct udevice *dev, struct gpio_desc *desc, | |
229 | struct fdtdec_phandle_args *args) | |
230 | { | |
231 | desc->offset = args->args[0]; | |
232 | desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | static const struct dm_gpio_ops pca953x_ops = { | |
238 | .direction_input = pca953x_direction_input, | |
239 | .direction_output = pca953x_direction_output, | |
240 | .get_value = pca953x_get_value, | |
241 | .set_value = pca953x_set_value, | |
242 | .get_function = pca953x_get_function, | |
243 | .xlate = pca953x_xlate, | |
244 | }; | |
245 | ||
246 | static int pca953x_probe(struct udevice *dev) | |
247 | { | |
248 | struct pca953x_info *info = dev_get_platdata(dev); | |
249 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); | |
250 | struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); | |
251 | char name[32], *str; | |
252 | int addr; | |
253 | ulong driver_data; | |
254 | int ret; | |
255 | ||
256 | if (!info) { | |
257 | dev_err(dev, "platdata not ready\n"); | |
258 | return -ENOMEM; | |
259 | } | |
260 | ||
261 | if (!chip) { | |
262 | dev_err(dev, "i2c not ready\n"); | |
263 | return -ENODEV; | |
264 | } | |
265 | ||
266 | addr = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", 0); | |
267 | if (addr == 0) | |
268 | return -ENODEV; | |
269 | ||
270 | info->addr = addr; | |
271 | ||
272 | driver_data = dev_get_driver_data(dev); | |
273 | ||
274 | info->gpio_count = driver_data & PCA_GPIO_MASK; | |
275 | if (info->gpio_count > MAX_BANK * BANK_SZ) { | |
276 | dev_err(dev, "Max support %d pins now\n", MAX_BANK * BANK_SZ); | |
277 | return -EINVAL; | |
278 | } | |
279 | ||
280 | info->chip_type = PCA_CHIP_TYPE(driver_data); | |
281 | if (info->chip_type != PCA953X_TYPE) { | |
282 | dev_err(dev, "Only support PCA953X chip type now.\n"); | |
283 | return -EINVAL; | |
284 | } | |
285 | ||
286 | info->bank_count = DIV_ROUND_UP(info->gpio_count, BANK_SZ); | |
287 | ||
288 | ret = pca953x_read_regs(dev, PCA953X_OUTPUT, info->reg_output); | |
289 | if (ret) { | |
290 | dev_err(dev, "Error reading output register\n"); | |
291 | return ret; | |
292 | } | |
293 | ||
294 | ret = pca953x_read_regs(dev, PCA953X_DIRECTION, info->reg_direction); | |
295 | if (ret) { | |
296 | dev_err(dev, "Error reading direction register\n"); | |
297 | return ret; | |
298 | } | |
299 | ||
300 | snprintf(name, sizeof(name), "gpio@%x_", info->addr); | |
301 | str = strdup(name); | |
302 | if (!str) | |
303 | return -ENOMEM; | |
304 | uc_priv->bank_name = str; | |
305 | uc_priv->gpio_count = info->gpio_count; | |
306 | ||
307 | dev_dbg(dev, "%s is ready\n", str); | |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
312 | #define OF_953X(__nrgpio, __int) (ulong)(__nrgpio | PCA953X_TYPE | __int) | |
313 | #define OF_957X(__nrgpio, __int) (ulong)(__nrgpio | PCA957X_TYPE | __int) | |
314 | ||
315 | static const struct udevice_id pca953x_ids[] = { | |
316 | { .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), }, | |
317 | { .compatible = "nxp,pca9534", .data = OF_953X(8, PCA_INT), }, | |
318 | { .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), }, | |
319 | { .compatible = "nxp,pca9536", .data = OF_953X(4, 0), }, | |
320 | { .compatible = "nxp,pca9537", .data = OF_953X(4, PCA_INT), }, | |
321 | { .compatible = "nxp,pca9538", .data = OF_953X(8, PCA_INT), }, | |
322 | { .compatible = "nxp,pca9539", .data = OF_953X(16, PCA_INT), }, | |
323 | { .compatible = "nxp,pca9554", .data = OF_953X(8, PCA_INT), }, | |
324 | { .compatible = "nxp,pca9555", .data = OF_953X(16, PCA_INT), }, | |
325 | { .compatible = "nxp,pca9556", .data = OF_953X(8, 0), }, | |
326 | { .compatible = "nxp,pca9557", .data = OF_953X(8, 0), }, | |
327 | { .compatible = "nxp,pca9574", .data = OF_957X(8, PCA_INT), }, | |
328 | { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), }, | |
329 | { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), }, | |
330 | ||
331 | { .compatible = "maxim,max7310", .data = OF_953X(8, 0), }, | |
332 | { .compatible = "maxim,max7312", .data = OF_953X(16, PCA_INT), }, | |
333 | { .compatible = "maxim,max7313", .data = OF_953X(16, PCA_INT), }, | |
334 | { .compatible = "maxim,max7315", .data = OF_953X(8, PCA_INT), }, | |
335 | ||
336 | { .compatible = "ti,pca6107", .data = OF_953X(8, PCA_INT), }, | |
337 | { .compatible = "ti,tca6408", .data = OF_953X(8, PCA_INT), }, | |
338 | { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, | |
339 | { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, | |
340 | ||
341 | { .compatible = "onsemi,pca9654", .data = OF_953X(8, PCA_INT), }, | |
342 | ||
343 | { .compatible = "exar,xra1202", .data = OF_953X(8, 0), }, | |
344 | { } | |
345 | }; | |
346 | ||
347 | U_BOOT_DRIVER(pca953x) = { | |
348 | .name = "pca953x", | |
349 | .id = UCLASS_GPIO, | |
350 | .ops = &pca953x_ops, | |
351 | .probe = pca953x_probe, | |
352 | .platdata_auto_alloc_size = sizeof(struct pca953x_info), | |
353 | .of_match = pca953x_ids, | |
354 | }; |