1e92739d3SPeter Tyser /*
2e92739d3SPeter Tyser * Copyright 2008 Extreme Engineering Solutions, Inc.
3e92739d3SPeter Tyser *
45b8031ccSTom Rini * SPDX-License-Identifier: GPL-2.0
5e92739d3SPeter Tyser */
6e92739d3SPeter Tyser
7e92739d3SPeter Tyser /*
85dec49caSChris Packham * Driver for NXP's 4, 8 and 16 bit I2C gpio expanders (eg pca9537, pca9557,
95dec49caSChris Packham * pca9539, etc)
10e92739d3SPeter Tyser */
11e92739d3SPeter Tyser
12e92739d3SPeter Tyser #include <common.h>
13e92739d3SPeter Tyser #include <i2c.h>
14e92739d3SPeter Tyser #include <pca953x.h>
15e92739d3SPeter Tyser
16e92739d3SPeter Tyser /* Default to an address that hopefully won't corrupt other i2c devices */
17e92739d3SPeter Tyser #ifndef CONFIG_SYS_I2C_PCA953X_ADDR
18e92739d3SPeter Tyser #define CONFIG_SYS_I2C_PCA953X_ADDR (~0)
19e92739d3SPeter Tyser #endif
20e92739d3SPeter Tyser
21e92739d3SPeter Tyser enum {
22e92739d3SPeter Tyser PCA953X_CMD_INFO,
23e92739d3SPeter Tyser PCA953X_CMD_DEVICE,
24e92739d3SPeter Tyser PCA953X_CMD_OUTPUT,
25e92739d3SPeter Tyser PCA953X_CMD_INPUT,
26e92739d3SPeter Tyser PCA953X_CMD_INVERT,
27e92739d3SPeter Tyser };
28e92739d3SPeter Tyser
295dec49caSChris Packham #ifdef CONFIG_SYS_I2C_PCA953X_WIDTH
305dec49caSChris Packham struct pca953x_chip_ngpio {
315dec49caSChris Packham uint8_t chip;
325dec49caSChris Packham uint8_t ngpio;
335dec49caSChris Packham };
345dec49caSChris Packham
355dec49caSChris Packham static struct pca953x_chip_ngpio pca953x_chip_ngpios[] =
365dec49caSChris Packham CONFIG_SYS_I2C_PCA953X_WIDTH;
375dec49caSChris Packham
385dec49caSChris Packham /*
395dec49caSChris Packham * Determine the number of GPIO pins supported. If we don't know we assume
405dec49caSChris Packham * 8 pins.
415dec49caSChris Packham */
pca953x_ngpio(uint8_t chip)425dec49caSChris Packham static int pca953x_ngpio(uint8_t chip)
435dec49caSChris Packham {
445dec49caSChris Packham int i;
455dec49caSChris Packham
46f2187617SAxel Lin for (i = 0; i < ARRAY_SIZE(pca953x_chip_ngpios); i++)
475dec49caSChris Packham if (pca953x_chip_ngpios[i].chip == chip)
485dec49caSChris Packham return pca953x_chip_ngpios[i].ngpio;
495dec49caSChris Packham
505dec49caSChris Packham return 8;
515dec49caSChris Packham }
525dec49caSChris Packham #else
pca953x_ngpio(uint8_t chip)535dec49caSChris Packham static int pca953x_ngpio(uint8_t chip)
545dec49caSChris Packham {
555dec49caSChris Packham return 8;
565dec49caSChris Packham }
575dec49caSChris Packham #endif
585dec49caSChris Packham
59e92739d3SPeter Tyser /*
60e92739d3SPeter Tyser * Modify masked bits in register
61e92739d3SPeter Tyser */
pca953x_reg_write(uint8_t chip,uint addr,uint mask,uint data)62e92739d3SPeter Tyser static int pca953x_reg_write(uint8_t chip, uint addr, uint mask, uint data)
63e92739d3SPeter Tyser {
645dec49caSChris Packham uint8_t valb;
655dec49caSChris Packham uint16_t valw;
66e92739d3SPeter Tyser
675dec49caSChris Packham if (pca953x_ngpio(chip) <= 8) {
685dec49caSChris Packham if (i2c_read(chip, addr, 1, &valb, 1))
69e92739d3SPeter Tyser return -1;
70e92739d3SPeter Tyser
715dec49caSChris Packham valb &= ~mask;
725dec49caSChris Packham valb |= data;
73e92739d3SPeter Tyser
745dec49caSChris Packham return i2c_write(chip, addr, 1, &valb, 1);
755dec49caSChris Packham } else {
765dec49caSChris Packham if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
775dec49caSChris Packham return -1;
785dec49caSChris Packham
79daa75b34SDirk Eibach valw = le16_to_cpu(valw);
805dec49caSChris Packham valw &= ~mask;
815dec49caSChris Packham valw |= data;
82daa75b34SDirk Eibach valw = cpu_to_le16(valw);
835dec49caSChris Packham
845dec49caSChris Packham return i2c_write(chip, addr << 1, 1, (u8*)&valw, 2);
855dec49caSChris Packham }
865dec49caSChris Packham }
875dec49caSChris Packham
pca953x_reg_read(uint8_t chip,uint addr,uint * data)885dec49caSChris Packham static int pca953x_reg_read(uint8_t chip, uint addr, uint *data)
895dec49caSChris Packham {
905dec49caSChris Packham uint8_t valb;
915dec49caSChris Packham uint16_t valw;
925dec49caSChris Packham
935dec49caSChris Packham if (pca953x_ngpio(chip) <= 8) {
945dec49caSChris Packham if (i2c_read(chip, addr, 1, &valb, 1))
955dec49caSChris Packham return -1;
965dec49caSChris Packham *data = (int)valb;
975dec49caSChris Packham } else {
985dec49caSChris Packham if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
995dec49caSChris Packham return -1;
100daa75b34SDirk Eibach *data = (uint)le16_to_cpu(valw);
1015dec49caSChris Packham }
1025dec49caSChris Packham return 0;
103e92739d3SPeter Tyser }
104e92739d3SPeter Tyser
105e92739d3SPeter Tyser /*
106e92739d3SPeter Tyser * Set output value of IO pins in 'mask' to corresponding value in 'data'
107e92739d3SPeter Tyser * 0 = low, 1 = high
108e92739d3SPeter Tyser */
pca953x_set_val(uint8_t chip,uint mask,uint data)109e92739d3SPeter Tyser int pca953x_set_val(uint8_t chip, uint mask, uint data)
110e92739d3SPeter Tyser {
111e92739d3SPeter Tyser return pca953x_reg_write(chip, PCA953X_OUT, mask, data);
112e92739d3SPeter Tyser }
113e92739d3SPeter Tyser
114e92739d3SPeter Tyser /*
115e92739d3SPeter Tyser * Set read polarity of IO pins in 'mask' to corresponding value in 'data'
116e92739d3SPeter Tyser * 0 = read pin value, 1 = read inverted pin value
117e92739d3SPeter Tyser */
pca953x_set_pol(uint8_t chip,uint mask,uint data)118e92739d3SPeter Tyser int pca953x_set_pol(uint8_t chip, uint mask, uint data)
119e92739d3SPeter Tyser {
120e92739d3SPeter Tyser return pca953x_reg_write(chip, PCA953X_POL, mask, data);
121e92739d3SPeter Tyser }
122e92739d3SPeter Tyser
123e92739d3SPeter Tyser /*
124e92739d3SPeter Tyser * Set direction of IO pins in 'mask' to corresponding value in 'data'
125e92739d3SPeter Tyser * 0 = output, 1 = input
126e92739d3SPeter Tyser */
pca953x_set_dir(uint8_t chip,uint mask,uint data)127e92739d3SPeter Tyser int pca953x_set_dir(uint8_t chip, uint mask, uint data)
128e92739d3SPeter Tyser {
129e92739d3SPeter Tyser return pca953x_reg_write(chip, PCA953X_CONF, mask, data);
130e92739d3SPeter Tyser }
131e92739d3SPeter Tyser
132e92739d3SPeter Tyser /*
133e92739d3SPeter Tyser * Read current logic level of all IO pins
134e92739d3SPeter Tyser */
pca953x_get_val(uint8_t chip)135e92739d3SPeter Tyser int pca953x_get_val(uint8_t chip)
136e92739d3SPeter Tyser {
1375dec49caSChris Packham uint val;
138e92739d3SPeter Tyser
1395dec49caSChris Packham if (pca953x_reg_read(chip, PCA953X_IN, &val) < 0)
140e92739d3SPeter Tyser return -1;
141e92739d3SPeter Tyser
142e92739d3SPeter Tyser return (int)val;
143e92739d3SPeter Tyser }
144e92739d3SPeter Tyser
145e92739d3SPeter Tyser #ifdef CONFIG_CMD_PCA953X
146e92739d3SPeter Tyser /*
147e92739d3SPeter Tyser * Display pca953x information
148e92739d3SPeter Tyser */
pca953x_info(uint8_t chip)149e92739d3SPeter Tyser static int pca953x_info(uint8_t chip)
150e92739d3SPeter Tyser {
151e92739d3SPeter Tyser int i;
1525dec49caSChris Packham uint data;
1535dec49caSChris Packham int nr_gpio = pca953x_ngpio(chip);
1545dec49caSChris Packham int msb = nr_gpio - 1;
155e92739d3SPeter Tyser
1565dec49caSChris Packham printf("pca953x@ 0x%x (%d pins):\n\n", chip, nr_gpio);
1575dec49caSChris Packham printf("gpio pins: ");
1585dec49caSChris Packham for (i = msb; i >= 0; i--)
1595dec49caSChris Packham printf("%x", i);
1605dec49caSChris Packham printf("\n");
1615dec49caSChris Packham for (i = 11 + nr_gpio; i > 0; i--)
1625dec49caSChris Packham printf("-");
1635dec49caSChris Packham printf("\n");
164e92739d3SPeter Tyser
1655dec49caSChris Packham if (pca953x_reg_read(chip, PCA953X_CONF, &data) < 0)
166e92739d3SPeter Tyser return -1;
167e92739d3SPeter Tyser printf("conf: ");
1685dec49caSChris Packham for (i = msb; i >= 0; i--)
169e92739d3SPeter Tyser printf("%c", data & (1 << i) ? 'i' : 'o');
170e92739d3SPeter Tyser printf("\n");
171e92739d3SPeter Tyser
1725dec49caSChris Packham if (pca953x_reg_read(chip, PCA953X_POL, &data) < 0)
173e92739d3SPeter Tyser return -1;
174e92739d3SPeter Tyser printf("invert: ");
1755dec49caSChris Packham for (i = msb; i >= 0; i--)
176e92739d3SPeter Tyser printf("%c", data & (1 << i) ? '1' : '0');
177e92739d3SPeter Tyser printf("\n");
178e92739d3SPeter Tyser
1795dec49caSChris Packham if (pca953x_reg_read(chip, PCA953X_IN, &data) < 0)
180e92739d3SPeter Tyser return -1;
181e92739d3SPeter Tyser printf("input: ");
1825dec49caSChris Packham for (i = msb; i >= 0; i--)
183e92739d3SPeter Tyser printf("%c", data & (1 << i) ? '1' : '0');
184e92739d3SPeter Tyser printf("\n");
185e92739d3SPeter Tyser
1865dec49caSChris Packham if (pca953x_reg_read(chip, PCA953X_OUT, &data) < 0)
187e92739d3SPeter Tyser return -1;
188e92739d3SPeter Tyser printf("output: ");
1895dec49caSChris Packham for (i = msb; i >= 0; i--)
190e92739d3SPeter Tyser printf("%c", data & (1 << i) ? '1' : '0');
191e92739d3SPeter Tyser printf("\n");
192e92739d3SPeter Tyser
193e92739d3SPeter Tyser return 0;
194e92739d3SPeter Tyser }
195e92739d3SPeter Tyser
196e92739d3SPeter Tyser cmd_tbl_t cmd_pca953x[] = {
197e92739d3SPeter Tyser U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCA953X_CMD_DEVICE, "", ""),
198e92739d3SPeter Tyser U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCA953X_CMD_OUTPUT, "", ""),
199e92739d3SPeter Tyser U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCA953X_CMD_INPUT, "", ""),
200e92739d3SPeter Tyser U_BOOT_CMD_MKENT(invert, 4, 0, (void *)PCA953X_CMD_INVERT, "", ""),
201e92739d3SPeter Tyser U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCA953X_CMD_INFO, "", ""),
202e92739d3SPeter Tyser };
203e92739d3SPeter Tyser
do_pca953x(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])20454841ab5SWolfgang Denk int do_pca953x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
205e92739d3SPeter Tyser {
206e92739d3SPeter Tyser static uint8_t chip = CONFIG_SYS_I2C_PCA953X_ADDR;
207633efe9cSLaurence Withers int ret = CMD_RET_USAGE, val;
208e92739d3SPeter Tyser ulong ul_arg2 = 0;
209e92739d3SPeter Tyser ulong ul_arg3 = 0;
210e92739d3SPeter Tyser cmd_tbl_t *c;
211e92739d3SPeter Tyser
212e92739d3SPeter Tyser c = find_cmd_tbl(argv[1], cmd_pca953x, ARRAY_SIZE(cmd_pca953x));
213e92739d3SPeter Tyser
214e92739d3SPeter Tyser /* All commands but "device" require 'maxargs' arguments */
215e92739d3SPeter Tyser if (!c || !((argc == (c->maxargs)) ||
216*01b2a699SMichal Simek (((long)c->cmd == PCA953X_CMD_DEVICE) &&
217e92739d3SPeter Tyser (argc == (c->maxargs - 1))))) {
218633efe9cSLaurence Withers return CMD_RET_USAGE;
219e92739d3SPeter Tyser }
220e92739d3SPeter Tyser
221e92739d3SPeter Tyser /* arg2 used as chip number or pin number */
222e92739d3SPeter Tyser if (argc > 2)
223e92739d3SPeter Tyser ul_arg2 = simple_strtoul(argv[2], NULL, 16);
224e92739d3SPeter Tyser
225e92739d3SPeter Tyser /* arg3 used as pin or invert value */
226e92739d3SPeter Tyser if (argc > 3)
227e92739d3SPeter Tyser ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 0x1;
228e92739d3SPeter Tyser
229*01b2a699SMichal Simek switch ((long)c->cmd) {
230e92739d3SPeter Tyser case PCA953X_CMD_INFO:
231633efe9cSLaurence Withers ret = pca953x_info(chip);
232633efe9cSLaurence Withers if (ret)
233633efe9cSLaurence Withers ret = CMD_RET_FAILURE;
234633efe9cSLaurence Withers break;
235633efe9cSLaurence Withers
236e92739d3SPeter Tyser case PCA953X_CMD_DEVICE:
237e92739d3SPeter Tyser if (argc == 3)
238e92739d3SPeter Tyser chip = (uint8_t)ul_arg2;
239e92739d3SPeter Tyser printf("Current device address: 0x%x\n", chip);
240633efe9cSLaurence Withers ret = CMD_RET_SUCCESS;
241633efe9cSLaurence Withers break;
242633efe9cSLaurence Withers
243e92739d3SPeter Tyser case PCA953X_CMD_INPUT:
244633efe9cSLaurence Withers ret = pca953x_set_dir(chip, (1 << ul_arg2),
245e92739d3SPeter Tyser PCA953X_DIR_IN << ul_arg2);
246e92739d3SPeter Tyser val = (pca953x_get_val(chip) & (1 << ul_arg2)) != 0;
247e92739d3SPeter Tyser
248633efe9cSLaurence Withers if (ret)
249633efe9cSLaurence Withers ret = CMD_RET_FAILURE;
250633efe9cSLaurence Withers else
251633efe9cSLaurence Withers printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2,
252633efe9cSLaurence Withers val);
253633efe9cSLaurence Withers break;
254633efe9cSLaurence Withers
255e92739d3SPeter Tyser case PCA953X_CMD_OUTPUT:
256633efe9cSLaurence Withers ret = pca953x_set_dir(chip, (1 << ul_arg2),
257e92739d3SPeter Tyser (PCA953X_DIR_OUT << ul_arg2));
258633efe9cSLaurence Withers if (!ret)
259633efe9cSLaurence Withers ret = pca953x_set_val(chip, (1 << ul_arg2),
260e92739d3SPeter Tyser (ul_arg3 << ul_arg2));
261633efe9cSLaurence Withers if (ret)
262633efe9cSLaurence Withers ret = CMD_RET_FAILURE;
263633efe9cSLaurence Withers break;
264633efe9cSLaurence Withers
265e92739d3SPeter Tyser case PCA953X_CMD_INVERT:
266633efe9cSLaurence Withers ret = pca953x_set_pol(chip, (1 << ul_arg2),
267e92739d3SPeter Tyser (ul_arg3 << ul_arg2));
268633efe9cSLaurence Withers if (ret)
269633efe9cSLaurence Withers ret = CMD_RET_FAILURE;
270633efe9cSLaurence Withers break;
271e92739d3SPeter Tyser }
272633efe9cSLaurence Withers
273633efe9cSLaurence Withers if (ret == CMD_RET_FAILURE)
274633efe9cSLaurence Withers eprintf("Error talking to chip at 0x%x\n", chip);
275633efe9cSLaurence Withers
276633efe9cSLaurence Withers return ret;
277e92739d3SPeter Tyser }
278e92739d3SPeter Tyser
279e92739d3SPeter Tyser U_BOOT_CMD(
280e92739d3SPeter Tyser pca953x, 5, 1, do_pca953x,
2812fb2604dSPeter Tyser "pca953x gpio access",
282e92739d3SPeter Tyser "device [dev]\n"
283e92739d3SPeter Tyser " - show or set current device address\n"
284e92739d3SPeter Tyser "pca953x info\n"
285e92739d3SPeter Tyser " - display info for current chip\n"
286e92739d3SPeter Tyser "pca953x output pin 0|1\n"
287e92739d3SPeter Tyser " - set pin as output and drive low or high\n"
288e92739d3SPeter Tyser "pca953x invert pin 0|1\n"
289e92739d3SPeter Tyser " - disable/enable polarity inversion for reads\n"
290d75bc03fSLaurence Withers "pca953x input pin\n"
291a89c33dbSWolfgang Denk " - set pin as input and read value"
292e92739d3SPeter Tyser );
293e92739d3SPeter Tyser
294e92739d3SPeter Tyser #endif /* CONFIG_CMD_PCA953X */
295