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