blob: 2fb14590c0fec134dee400c0e2e493b8799b237c [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0
Peter Tyser9902e422008-12-17 16:36:21 -06002/*
3 * Copyright 2008 Extreme Engineering Solutions, Inc.
Peter Tyser9902e422008-12-17 16:36:21 -06004 */
5
6/*
Chris Packham9b383202010-12-19 10:12:13 +00007 * Driver for NXP's 4, 8 and 16 bit I2C gpio expanders (eg pca9537, pca9557,
8 * pca9539, etc)
Peter Tyser9902e422008-12-17 16:36:21 -06009 */
10
Tom Rinidec7ea02024-05-20 13:35:03 -060011#include <config.h>
Simon Glassed38aef2020-05-10 11:40:03 -060012#include <command.h>
Peter Tyser9902e422008-12-17 16:36:21 -060013#include <i2c.h>
14#include <pca953x.h>
Tom Rinidec7ea02024-05-20 13:35:03 -060015#include <vsprintf.h>
Peter Tyser9902e422008-12-17 16:36:21 -060016
17/* Default to an address that hopefully won't corrupt other i2c devices */
Tom Rini6a5dccc2022-11-16 13:10:41 -050018#ifndef CFG_SYS_I2C_PCA953X_ADDR
19#define CFG_SYS_I2C_PCA953X_ADDR (~0)
Peter Tyser9902e422008-12-17 16:36:21 -060020#endif
21
22enum {
23 PCA953X_CMD_INFO,
24 PCA953X_CMD_DEVICE,
25 PCA953X_CMD_OUTPUT,
26 PCA953X_CMD_INPUT,
27 PCA953X_CMD_INVERT,
28};
29
Tom Rini6a5dccc2022-11-16 13:10:41 -050030#ifdef CFG_SYS_I2C_PCA953X_WIDTH
Chris Packham9b383202010-12-19 10:12:13 +000031struct pca953x_chip_ngpio {
32 uint8_t chip;
33 uint8_t ngpio;
34};
35
36static struct pca953x_chip_ngpio pca953x_chip_ngpios[] =
Tom Rini6a5dccc2022-11-16 13:10:41 -050037 CFG_SYS_I2C_PCA953X_WIDTH;
Chris Packham9b383202010-12-19 10:12:13 +000038
Chris Packham9b383202010-12-19 10:12:13 +000039/*
40 * Determine the number of GPIO pins supported. If we don't know we assume
41 * 8 pins.
42 */
43static int pca953x_ngpio(uint8_t chip)
44{
45 int i;
46
Axel Line9770432013-06-22 18:22:48 +080047 for (i = 0; i < ARRAY_SIZE(pca953x_chip_ngpios); i++)
Chris Packham9b383202010-12-19 10:12:13 +000048 if (pca953x_chip_ngpios[i].chip == chip)
49 return pca953x_chip_ngpios[i].ngpio;
50
51 return 8;
52}
53#else
54static int pca953x_ngpio(uint8_t chip)
55{
56 return 8;
57}
58#endif
59
Peter Tyser9902e422008-12-17 16:36:21 -060060/*
61 * Modify masked bits in register
62 */
63static int pca953x_reg_write(uint8_t chip, uint addr, uint mask, uint data)
64{
Chris Packham9b383202010-12-19 10:12:13 +000065 uint8_t valb;
66 uint16_t valw;
Peter Tyser9902e422008-12-17 16:36:21 -060067
Chris Packham9b383202010-12-19 10:12:13 +000068 if (pca953x_ngpio(chip) <= 8) {
69 if (i2c_read(chip, addr, 1, &valb, 1))
70 return -1;
71
72 valb &= ~mask;
73 valb |= data;
74
75 return i2c_write(chip, addr, 1, &valb, 1);
76 } else {
77 if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
78 return -1;
Peter Tyser9902e422008-12-17 16:36:21 -060079
Dirk Eibach4f515fb2015-10-29 13:51:27 +010080 valw = le16_to_cpu(valw);
Chris Packham9b383202010-12-19 10:12:13 +000081 valw &= ~mask;
82 valw |= data;
Dirk Eibach4f515fb2015-10-29 13:51:27 +010083 valw = cpu_to_le16(valw);
Peter Tyser9902e422008-12-17 16:36:21 -060084
Chris Packham9b383202010-12-19 10:12:13 +000085 return i2c_write(chip, addr << 1, 1, (u8*)&valw, 2);
86 }
Peter Tyser9902e422008-12-17 16:36:21 -060087}
88
Chris Packham9b383202010-12-19 10:12:13 +000089static int pca953x_reg_read(uint8_t chip, uint addr, uint *data)
90{
91 uint8_t valb;
92 uint16_t valw;
93
94 if (pca953x_ngpio(chip) <= 8) {
95 if (i2c_read(chip, addr, 1, &valb, 1))
96 return -1;
97 *data = (int)valb;
98 } else {
99 if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
100 return -1;
Dirk Eibach4f515fb2015-10-29 13:51:27 +0100101 *data = (uint)le16_to_cpu(valw);
Chris Packham9b383202010-12-19 10:12:13 +0000102 }
103 return 0;
104}
105
Peter Tyser9902e422008-12-17 16:36:21 -0600106/*
107 * Set output value of IO pins in 'mask' to corresponding value in 'data'
108 * 0 = low, 1 = high
109 */
110int pca953x_set_val(uint8_t chip, uint mask, uint data)
111{
112 return pca953x_reg_write(chip, PCA953X_OUT, mask, data);
113}
114
115/*
116 * Set read polarity of IO pins in 'mask' to corresponding value in 'data'
117 * 0 = read pin value, 1 = read inverted pin value
118 */
119int pca953x_set_pol(uint8_t chip, uint mask, uint data)
120{
121 return pca953x_reg_write(chip, PCA953X_POL, mask, data);
122}
123
124/*
125 * Set direction of IO pins in 'mask' to corresponding value in 'data'
126 * 0 = output, 1 = input
127 */
128int pca953x_set_dir(uint8_t chip, uint mask, uint data)
129{
130 return pca953x_reg_write(chip, PCA953X_CONF, mask, data);
131}
132
133/*
134 * Read current logic level of all IO pins
135 */
136int pca953x_get_val(uint8_t chip)
137{
Chris Packham9b383202010-12-19 10:12:13 +0000138 uint val;
Peter Tyser9902e422008-12-17 16:36:21 -0600139
Chris Packham9b383202010-12-19 10:12:13 +0000140 if (pca953x_reg_read(chip, PCA953X_IN, &val) < 0)
Peter Tyser9902e422008-12-17 16:36:21 -0600141 return -1;
142
143 return (int)val;
144}
145
Simon Glass7ec24132024-09-29 19:49:48 -0600146#if defined(CONFIG_CMD_PCA953X) && !defined(CONFIG_XPL_BUILD)
Peter Tyser9902e422008-12-17 16:36:21 -0600147/*
148 * Display pca953x information
149 */
150static int pca953x_info(uint8_t chip)
151{
152 int i;
Chris Packham9b383202010-12-19 10:12:13 +0000153 uint data;
154 int nr_gpio = pca953x_ngpio(chip);
155 int msb = nr_gpio - 1;
Peter Tyser9902e422008-12-17 16:36:21 -0600156
Chris Packham9b383202010-12-19 10:12:13 +0000157 printf("pca953x@ 0x%x (%d pins):\n\n", chip, nr_gpio);
158 printf("gpio pins: ");
159 for (i = msb; i >= 0; i--)
160 printf("%x", i);
161 printf("\n");
162 for (i = 11 + nr_gpio; i > 0; i--)
163 printf("-");
164 printf("\n");
Peter Tyser9902e422008-12-17 16:36:21 -0600165
Chris Packham9b383202010-12-19 10:12:13 +0000166 if (pca953x_reg_read(chip, PCA953X_CONF, &data) < 0)
Peter Tyser9902e422008-12-17 16:36:21 -0600167 return -1;
168 printf("conf: ");
Chris Packham9b383202010-12-19 10:12:13 +0000169 for (i = msb; i >= 0; i--)
Peter Tyser9902e422008-12-17 16:36:21 -0600170 printf("%c", data & (1 << i) ? 'i' : 'o');
171 printf("\n");
172
Chris Packham9b383202010-12-19 10:12:13 +0000173 if (pca953x_reg_read(chip, PCA953X_POL, &data) < 0)
Peter Tyser9902e422008-12-17 16:36:21 -0600174 return -1;
175 printf("invert: ");
Chris Packham9b383202010-12-19 10:12:13 +0000176 for (i = msb; i >= 0; i--)
Peter Tyser9902e422008-12-17 16:36:21 -0600177 printf("%c", data & (1 << i) ? '1' : '0');
178 printf("\n");
179
Chris Packham9b383202010-12-19 10:12:13 +0000180 if (pca953x_reg_read(chip, PCA953X_IN, &data) < 0)
Peter Tyser9902e422008-12-17 16:36:21 -0600181 return -1;
182 printf("input: ");
Chris Packham9b383202010-12-19 10:12:13 +0000183 for (i = msb; i >= 0; i--)
Peter Tyser9902e422008-12-17 16:36:21 -0600184 printf("%c", data & (1 << i) ? '1' : '0');
185 printf("\n");
186
Chris Packham9b383202010-12-19 10:12:13 +0000187 if (pca953x_reg_read(chip, PCA953X_OUT, &data) < 0)
Peter Tyser9902e422008-12-17 16:36:21 -0600188 return -1;
189 printf("output: ");
Chris Packham9b383202010-12-19 10:12:13 +0000190 for (i = msb; i >= 0; i--)
Peter Tyser9902e422008-12-17 16:36:21 -0600191 printf("%c", data & (1 << i) ? '1' : '0');
192 printf("\n");
193
194 return 0;
195}
Peter Tyser9902e422008-12-17 16:36:21 -0600196
Simon Glassed38aef2020-05-10 11:40:03 -0600197static struct cmd_tbl cmd_pca953x[] = {
Peter Tyser9902e422008-12-17 16:36:21 -0600198 U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCA953X_CMD_DEVICE, "", ""),
199 U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCA953X_CMD_OUTPUT, "", ""),
200 U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCA953X_CMD_INPUT, "", ""),
201 U_BOOT_CMD_MKENT(invert, 4, 0, (void *)PCA953X_CMD_INVERT, "", ""),
Peter Tyser9902e422008-12-17 16:36:21 -0600202 U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCA953X_CMD_INFO, "", ""),
Peter Tyser9902e422008-12-17 16:36:21 -0600203};
204
Simon Glassed38aef2020-05-10 11:40:03 -0600205static int do_pca953x(struct cmd_tbl *cmdtp, int flag, int argc,
206 char *const argv[])
Peter Tyser9902e422008-12-17 16:36:21 -0600207{
Tom Rini6a5dccc2022-11-16 13:10:41 -0500208 static uint8_t chip = CFG_SYS_I2C_PCA953X_ADDR;
Laurence Withers46a409d2012-07-25 03:55:48 +0000209 int ret = CMD_RET_USAGE, val;
Peter Tyser9902e422008-12-17 16:36:21 -0600210 ulong ul_arg2 = 0;
211 ulong ul_arg3 = 0;
Simon Glassed38aef2020-05-10 11:40:03 -0600212 struct cmd_tbl *c;
Peter Tyser9902e422008-12-17 16:36:21 -0600213
214 c = find_cmd_tbl(argv[1], cmd_pca953x, ARRAY_SIZE(cmd_pca953x));
215
216 /* All commands but "device" require 'maxargs' arguments */
217 if (!c || !((argc == (c->maxargs)) ||
Michal Simekfc2ef432016-04-11 13:51:26 +0200218 (((long)c->cmd == PCA953X_CMD_DEVICE) &&
Peter Tyser9902e422008-12-17 16:36:21 -0600219 (argc == (c->maxargs - 1))))) {
Laurence Withers46a409d2012-07-25 03:55:48 +0000220 return CMD_RET_USAGE;
Peter Tyser9902e422008-12-17 16:36:21 -0600221 }
222
223 /* arg2 used as chip number or pin number */
224 if (argc > 2)
Simon Glass3ff49ec2021-07-24 09:03:29 -0600225 ul_arg2 = hextoul(argv[2], NULL);
Peter Tyser9902e422008-12-17 16:36:21 -0600226
227 /* arg3 used as pin or invert value */
228 if (argc > 3)
Simon Glass3ff49ec2021-07-24 09:03:29 -0600229 ul_arg3 = hextoul(argv[3], NULL) & 0x1;
Peter Tyser9902e422008-12-17 16:36:21 -0600230
Michal Simekfc2ef432016-04-11 13:51:26 +0200231 switch ((long)c->cmd) {
Peter Tyser9902e422008-12-17 16:36:21 -0600232 case PCA953X_CMD_INFO:
Laurence Withers46a409d2012-07-25 03:55:48 +0000233 ret = pca953x_info(chip);
234 if (ret)
235 ret = CMD_RET_FAILURE;
236 break;
Laurence Withers46a409d2012-07-25 03:55:48 +0000237
Peter Tyser9902e422008-12-17 16:36:21 -0600238 case PCA953X_CMD_DEVICE:
239 if (argc == 3)
240 chip = (uint8_t)ul_arg2;
241 printf("Current device address: 0x%x\n", chip);
Laurence Withers46a409d2012-07-25 03:55:48 +0000242 ret = CMD_RET_SUCCESS;
243 break;
244
Peter Tyser9902e422008-12-17 16:36:21 -0600245 case PCA953X_CMD_INPUT:
Laurence Withers46a409d2012-07-25 03:55:48 +0000246 ret = pca953x_set_dir(chip, (1 << ul_arg2),
Peter Tyser9902e422008-12-17 16:36:21 -0600247 PCA953X_DIR_IN << ul_arg2);
248 val = (pca953x_get_val(chip) & (1 << ul_arg2)) != 0;
249
Laurence Withers46a409d2012-07-25 03:55:48 +0000250 if (ret)
251 ret = CMD_RET_FAILURE;
252 else
253 printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2,
254 val);
255 break;
256
Peter Tyser9902e422008-12-17 16:36:21 -0600257 case PCA953X_CMD_OUTPUT:
Laurence Withers46a409d2012-07-25 03:55:48 +0000258 ret = pca953x_set_dir(chip, (1 << ul_arg2),
Peter Tyser9902e422008-12-17 16:36:21 -0600259 (PCA953X_DIR_OUT << ul_arg2));
Laurence Withers46a409d2012-07-25 03:55:48 +0000260 if (!ret)
261 ret = pca953x_set_val(chip, (1 << ul_arg2),
262 (ul_arg3 << ul_arg2));
263 if (ret)
264 ret = CMD_RET_FAILURE;
265 break;
266
Peter Tyser9902e422008-12-17 16:36:21 -0600267 case PCA953X_CMD_INVERT:
Laurence Withers46a409d2012-07-25 03:55:48 +0000268 ret = pca953x_set_pol(chip, (1 << ul_arg2),
Peter Tyser9902e422008-12-17 16:36:21 -0600269 (ul_arg3 << ul_arg2));
Laurence Withers46a409d2012-07-25 03:55:48 +0000270 if (ret)
271 ret = CMD_RET_FAILURE;
272 break;
Peter Tyser9902e422008-12-17 16:36:21 -0600273 }
Laurence Withers46a409d2012-07-25 03:55:48 +0000274
275 if (ret == CMD_RET_FAILURE)
276 eprintf("Error talking to chip at 0x%x\n", chip);
277
278 return ret;
Peter Tyser9902e422008-12-17 16:36:21 -0600279}
280
281U_BOOT_CMD(
282 pca953x, 5, 1, do_pca953x,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600283 "pca953x gpio access",
Peter Tyser9902e422008-12-17 16:36:21 -0600284 "device [dev]\n"
285 " - show or set current device address\n"
Peter Tyser9902e422008-12-17 16:36:21 -0600286 "pca953x info\n"
287 " - display info for current chip\n"
Peter Tyser9902e422008-12-17 16:36:21 -0600288 "pca953x output pin 0|1\n"
289 " - set pin as output and drive low or high\n"
290 "pca953x invert pin 0|1\n"
291 " - disable/enable polarity inversion for reads\n"
Laurence Withers7a63c652012-07-25 03:55:47 +0000292 "pca953x input pin\n"
Wolfgang Denkc54781c2009-05-24 17:06:54 +0200293 " - set pin as input and read value"
Peter Tyser9902e422008-12-17 16:36:21 -0600294);
295
296#endif /* CONFIG_CMD_PCA953X */