1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright 2008 Extreme Engineering Solutions, Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun /*
8*4882a593Smuzhiyun * Driver for NXP's 4, 8 and 16 bit I2C gpio expanders (eg pca9537, pca9557,
9*4882a593Smuzhiyun * pca9539, etc)
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <common.h>
13*4882a593Smuzhiyun #include <i2c.h>
14*4882a593Smuzhiyun #include <pca953x.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun /* Default to an address that hopefully won't corrupt other i2c devices */
17*4882a593Smuzhiyun #ifndef CONFIG_SYS_I2C_PCA953X_ADDR
18*4882a593Smuzhiyun #define CONFIG_SYS_I2C_PCA953X_ADDR (~0)
19*4882a593Smuzhiyun #endif
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun enum {
22*4882a593Smuzhiyun PCA953X_CMD_INFO,
23*4882a593Smuzhiyun PCA953X_CMD_DEVICE,
24*4882a593Smuzhiyun PCA953X_CMD_OUTPUT,
25*4882a593Smuzhiyun PCA953X_CMD_INPUT,
26*4882a593Smuzhiyun PCA953X_CMD_INVERT,
27*4882a593Smuzhiyun };
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun #ifdef CONFIG_SYS_I2C_PCA953X_WIDTH
30*4882a593Smuzhiyun struct pca953x_chip_ngpio {
31*4882a593Smuzhiyun uint8_t chip;
32*4882a593Smuzhiyun uint8_t ngpio;
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun static struct pca953x_chip_ngpio pca953x_chip_ngpios[] =
36*4882a593Smuzhiyun CONFIG_SYS_I2C_PCA953X_WIDTH;
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun /*
39*4882a593Smuzhiyun * Determine the number of GPIO pins supported. If we don't know we assume
40*4882a593Smuzhiyun * 8 pins.
41*4882a593Smuzhiyun */
pca953x_ngpio(uint8_t chip)42*4882a593Smuzhiyun static int pca953x_ngpio(uint8_t chip)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun int i;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(pca953x_chip_ngpios); i++)
47*4882a593Smuzhiyun if (pca953x_chip_ngpios[i].chip == chip)
48*4882a593Smuzhiyun return pca953x_chip_ngpios[i].ngpio;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun return 8;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun #else
pca953x_ngpio(uint8_t chip)53*4882a593Smuzhiyun static int pca953x_ngpio(uint8_t chip)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun return 8;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun #endif
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun /*
60*4882a593Smuzhiyun * Modify masked bits in register
61*4882a593Smuzhiyun */
pca953x_reg_write(uint8_t chip,uint addr,uint mask,uint data)62*4882a593Smuzhiyun static int pca953x_reg_write(uint8_t chip, uint addr, uint mask, uint data)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun uint8_t valb;
65*4882a593Smuzhiyun uint16_t valw;
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun if (pca953x_ngpio(chip) <= 8) {
68*4882a593Smuzhiyun if (i2c_read(chip, addr, 1, &valb, 1))
69*4882a593Smuzhiyun return -1;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun valb &= ~mask;
72*4882a593Smuzhiyun valb |= data;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun return i2c_write(chip, addr, 1, &valb, 1);
75*4882a593Smuzhiyun } else {
76*4882a593Smuzhiyun if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
77*4882a593Smuzhiyun return -1;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun valw = le16_to_cpu(valw);
80*4882a593Smuzhiyun valw &= ~mask;
81*4882a593Smuzhiyun valw |= data;
82*4882a593Smuzhiyun valw = cpu_to_le16(valw);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun return i2c_write(chip, addr << 1, 1, (u8*)&valw, 2);
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
pca953x_reg_read(uint8_t chip,uint addr,uint * data)88*4882a593Smuzhiyun static int pca953x_reg_read(uint8_t chip, uint addr, uint *data)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun uint8_t valb;
91*4882a593Smuzhiyun uint16_t valw;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun if (pca953x_ngpio(chip) <= 8) {
94*4882a593Smuzhiyun if (i2c_read(chip, addr, 1, &valb, 1))
95*4882a593Smuzhiyun return -1;
96*4882a593Smuzhiyun *data = (int)valb;
97*4882a593Smuzhiyun } else {
98*4882a593Smuzhiyun if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
99*4882a593Smuzhiyun return -1;
100*4882a593Smuzhiyun *data = (uint)le16_to_cpu(valw);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun return 0;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun /*
106*4882a593Smuzhiyun * Set output value of IO pins in 'mask' to corresponding value in 'data'
107*4882a593Smuzhiyun * 0 = low, 1 = high
108*4882a593Smuzhiyun */
pca953x_set_val(uint8_t chip,uint mask,uint data)109*4882a593Smuzhiyun int pca953x_set_val(uint8_t chip, uint mask, uint data)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun return pca953x_reg_write(chip, PCA953X_OUT, mask, data);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /*
115*4882a593Smuzhiyun * Set read polarity of IO pins in 'mask' to corresponding value in 'data'
116*4882a593Smuzhiyun * 0 = read pin value, 1 = read inverted pin value
117*4882a593Smuzhiyun */
pca953x_set_pol(uint8_t chip,uint mask,uint data)118*4882a593Smuzhiyun int pca953x_set_pol(uint8_t chip, uint mask, uint data)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun return pca953x_reg_write(chip, PCA953X_POL, mask, data);
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun /*
124*4882a593Smuzhiyun * Set direction of IO pins in 'mask' to corresponding value in 'data'
125*4882a593Smuzhiyun * 0 = output, 1 = input
126*4882a593Smuzhiyun */
pca953x_set_dir(uint8_t chip,uint mask,uint data)127*4882a593Smuzhiyun int pca953x_set_dir(uint8_t chip, uint mask, uint data)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun return pca953x_reg_write(chip, PCA953X_CONF, mask, data);
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun /*
133*4882a593Smuzhiyun * Read current logic level of all IO pins
134*4882a593Smuzhiyun */
pca953x_get_val(uint8_t chip)135*4882a593Smuzhiyun int pca953x_get_val(uint8_t chip)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun uint val;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun if (pca953x_reg_read(chip, PCA953X_IN, &val) < 0)
140*4882a593Smuzhiyun return -1;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun return (int)val;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun #ifdef CONFIG_CMD_PCA953X
146*4882a593Smuzhiyun /*
147*4882a593Smuzhiyun * Display pca953x information
148*4882a593Smuzhiyun */
pca953x_info(uint8_t chip)149*4882a593Smuzhiyun static int pca953x_info(uint8_t chip)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun int i;
152*4882a593Smuzhiyun uint data;
153*4882a593Smuzhiyun int nr_gpio = pca953x_ngpio(chip);
154*4882a593Smuzhiyun int msb = nr_gpio - 1;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun printf("pca953x@ 0x%x (%d pins):\n\n", chip, nr_gpio);
157*4882a593Smuzhiyun printf("gpio pins: ");
158*4882a593Smuzhiyun for (i = msb; i >= 0; i--)
159*4882a593Smuzhiyun printf("%x", i);
160*4882a593Smuzhiyun printf("\n");
161*4882a593Smuzhiyun for (i = 11 + nr_gpio; i > 0; i--)
162*4882a593Smuzhiyun printf("-");
163*4882a593Smuzhiyun printf("\n");
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun if (pca953x_reg_read(chip, PCA953X_CONF, &data) < 0)
166*4882a593Smuzhiyun return -1;
167*4882a593Smuzhiyun printf("conf: ");
168*4882a593Smuzhiyun for (i = msb; i >= 0; i--)
169*4882a593Smuzhiyun printf("%c", data & (1 << i) ? 'i' : 'o');
170*4882a593Smuzhiyun printf("\n");
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun if (pca953x_reg_read(chip, PCA953X_POL, &data) < 0)
173*4882a593Smuzhiyun return -1;
174*4882a593Smuzhiyun printf("invert: ");
175*4882a593Smuzhiyun for (i = msb; i >= 0; i--)
176*4882a593Smuzhiyun printf("%c", data & (1 << i) ? '1' : '0');
177*4882a593Smuzhiyun printf("\n");
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun if (pca953x_reg_read(chip, PCA953X_IN, &data) < 0)
180*4882a593Smuzhiyun return -1;
181*4882a593Smuzhiyun printf("input: ");
182*4882a593Smuzhiyun for (i = msb; i >= 0; i--)
183*4882a593Smuzhiyun printf("%c", data & (1 << i) ? '1' : '0');
184*4882a593Smuzhiyun printf("\n");
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun if (pca953x_reg_read(chip, PCA953X_OUT, &data) < 0)
187*4882a593Smuzhiyun return -1;
188*4882a593Smuzhiyun printf("output: ");
189*4882a593Smuzhiyun for (i = msb; i >= 0; i--)
190*4882a593Smuzhiyun printf("%c", data & (1 << i) ? '1' : '0');
191*4882a593Smuzhiyun printf("\n");
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun return 0;
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun cmd_tbl_t cmd_pca953x[] = {
197*4882a593Smuzhiyun U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCA953X_CMD_DEVICE, "", ""),
198*4882a593Smuzhiyun U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCA953X_CMD_OUTPUT, "", ""),
199*4882a593Smuzhiyun U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCA953X_CMD_INPUT, "", ""),
200*4882a593Smuzhiyun U_BOOT_CMD_MKENT(invert, 4, 0, (void *)PCA953X_CMD_INVERT, "", ""),
201*4882a593Smuzhiyun U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCA953X_CMD_INFO, "", ""),
202*4882a593Smuzhiyun };
203*4882a593Smuzhiyun
do_pca953x(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])204*4882a593Smuzhiyun int do_pca953x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun static uint8_t chip = CONFIG_SYS_I2C_PCA953X_ADDR;
207*4882a593Smuzhiyun int ret = CMD_RET_USAGE, val;
208*4882a593Smuzhiyun ulong ul_arg2 = 0;
209*4882a593Smuzhiyun ulong ul_arg3 = 0;
210*4882a593Smuzhiyun cmd_tbl_t *c;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun c = find_cmd_tbl(argv[1], cmd_pca953x, ARRAY_SIZE(cmd_pca953x));
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun /* All commands but "device" require 'maxargs' arguments */
215*4882a593Smuzhiyun if (!c || !((argc == (c->maxargs)) ||
216*4882a593Smuzhiyun (((long)c->cmd == PCA953X_CMD_DEVICE) &&
217*4882a593Smuzhiyun (argc == (c->maxargs - 1))))) {
218*4882a593Smuzhiyun return CMD_RET_USAGE;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun /* arg2 used as chip number or pin number */
222*4882a593Smuzhiyun if (argc > 2)
223*4882a593Smuzhiyun ul_arg2 = simple_strtoul(argv[2], NULL, 16);
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /* arg3 used as pin or invert value */
226*4882a593Smuzhiyun if (argc > 3)
227*4882a593Smuzhiyun ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 0x1;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun switch ((long)c->cmd) {
230*4882a593Smuzhiyun case PCA953X_CMD_INFO:
231*4882a593Smuzhiyun ret = pca953x_info(chip);
232*4882a593Smuzhiyun if (ret)
233*4882a593Smuzhiyun ret = CMD_RET_FAILURE;
234*4882a593Smuzhiyun break;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun case PCA953X_CMD_DEVICE:
237*4882a593Smuzhiyun if (argc == 3)
238*4882a593Smuzhiyun chip = (uint8_t)ul_arg2;
239*4882a593Smuzhiyun printf("Current device address: 0x%x\n", chip);
240*4882a593Smuzhiyun ret = CMD_RET_SUCCESS;
241*4882a593Smuzhiyun break;
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun case PCA953X_CMD_INPUT:
244*4882a593Smuzhiyun ret = pca953x_set_dir(chip, (1 << ul_arg2),
245*4882a593Smuzhiyun PCA953X_DIR_IN << ul_arg2);
246*4882a593Smuzhiyun val = (pca953x_get_val(chip) & (1 << ul_arg2)) != 0;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun if (ret)
249*4882a593Smuzhiyun ret = CMD_RET_FAILURE;
250*4882a593Smuzhiyun else
251*4882a593Smuzhiyun printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2,
252*4882a593Smuzhiyun val);
253*4882a593Smuzhiyun break;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun case PCA953X_CMD_OUTPUT:
256*4882a593Smuzhiyun ret = pca953x_set_dir(chip, (1 << ul_arg2),
257*4882a593Smuzhiyun (PCA953X_DIR_OUT << ul_arg2));
258*4882a593Smuzhiyun if (!ret)
259*4882a593Smuzhiyun ret = pca953x_set_val(chip, (1 << ul_arg2),
260*4882a593Smuzhiyun (ul_arg3 << ul_arg2));
261*4882a593Smuzhiyun if (ret)
262*4882a593Smuzhiyun ret = CMD_RET_FAILURE;
263*4882a593Smuzhiyun break;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun case PCA953X_CMD_INVERT:
266*4882a593Smuzhiyun ret = pca953x_set_pol(chip, (1 << ul_arg2),
267*4882a593Smuzhiyun (ul_arg3 << ul_arg2));
268*4882a593Smuzhiyun if (ret)
269*4882a593Smuzhiyun ret = CMD_RET_FAILURE;
270*4882a593Smuzhiyun break;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun if (ret == CMD_RET_FAILURE)
274*4882a593Smuzhiyun eprintf("Error talking to chip at 0x%x\n", chip);
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun return ret;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun U_BOOT_CMD(
280*4882a593Smuzhiyun pca953x, 5, 1, do_pca953x,
281*4882a593Smuzhiyun "pca953x gpio access",
282*4882a593Smuzhiyun "device [dev]\n"
283*4882a593Smuzhiyun " - show or set current device address\n"
284*4882a593Smuzhiyun "pca953x info\n"
285*4882a593Smuzhiyun " - display info for current chip\n"
286*4882a593Smuzhiyun "pca953x output pin 0|1\n"
287*4882a593Smuzhiyun " - set pin as output and drive low or high\n"
288*4882a593Smuzhiyun "pca953x invert pin 0|1\n"
289*4882a593Smuzhiyun " - disable/enable polarity inversion for reads\n"
290*4882a593Smuzhiyun "pca953x input pin\n"
291*4882a593Smuzhiyun " - set pin as input and read value"
292*4882a593Smuzhiyun );
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun #endif /* CONFIG_CMD_PCA953X */
295