xref: /rk3399_rockchip-uboot/drivers/gpio/pca953x.c (revision ba7cc6c6c5c667f8510824cb0c47ea7ab3865de4)
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