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