1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2021 Toradex
4 * Copyright (C) 2016 Broadcom
8 * DOC: FXL6408 I2C to GPIO expander.
10 * This chip has 8 GPIO lines out of it, and is controlled by an I2C
11 * bus (a pair of lines), providing 4x expansion of GPIO lines. It
12 * also provides an interrupt line out for notifying of state changes.
14 * Any preconfigured state will be left in place until the GPIO lines
15 * get activated. At power on, everything is treated as an input,
16 * default input is HIGH and pulled-up, all interrupts are masked.
18 * Documentation can be found at:
19 * ------------------------------
21 * https://www.fairchildsemi.com/datasheets/FX/FXL6408.pdf
23 * This driver bases on:
24 * ---------------------
26 * - the original driver by Eric Anholt <eric@anholt.net>:
27 * https://patchwork.kernel.org/patch/9148419/
28 * - the Toradex version by Max Krummenacher <max.krummenacher@toradex.com>:
29 * http://git.toradex.com/cgit/linux-toradex.git/tree/drivers/gpio/gpio-fxl6408.c?h=toradex_5.4-2.3.x-imx
30 * - the U-Boot PCA953x driver by Peng Fan <van.freenix@gmail.com>:
31 * drivers/gpio/pca953x_gpio.c
34 * - Add interrupts support
35 * - Replace deprecated callbacks direction_input/output() with set_flags()
38 #include <asm-generic/gpio.h>
39 #include <asm/global_data.h>
42 #include <dm/device_compat.h>
43 #include <dt-bindings/gpio/gpio.h>
45 #include <linux/bitops.h>
48 #define REG_DEVID_CTRL 0x1
49 # define SW_RST BIT(0)
50 # define RST_INT BIT(1)
51 /** 0b101 is the Manufacturer's ID assigned to Fairchild by Nokia */
52 # define MF_ID_FAIRCHILD 5
54 /** Bits set here indicate that the GPIO is an output */
55 #define REG_IO_DIR 0x3
58 * REG_OUT_STATE - a high-output state register address
60 * Bits set here, when the corresponding bit of REG_IO_DIR is set,
61 * drive the output high instead of low.
63 #define REG_OUT_STATE 0x5
65 /** Bits here make the output High-Z, instead of the OUTPUT value */
66 #define REG_OUT_HIGH_Z 0x7
69 * REG_IN_DEFAULT_STATE - an interrupt state register address
71 * Bits here define the expected input state of the GPIO.
72 * INTERRUPT_STATUS bits will be set when the INPUT transitions away
75 #define REG_IN_DEFAULT_STATE 0x9
78 * REG_PULL_ENABLE - a pull-up/down enable state register address
80 * Bits here enable either pull up or pull down according to
83 #define REG_PULL_ENABLE 0xb
86 * REG_PULL_MODE - a pull-up/pull-down mode state register address
88 * Bits set here selects a pull-up/pull-down state of pin, which
89 * is configured as Input and the corresponding REG_PULL_ENABLE bit is
92 #define REG_PULL_MODE 0xd
94 /** Returns the current status (1 = HIGH) of the input pins */
95 #define REG_IN_STATUS 0xf
97 /** Mask of pins which can generate interrupts */
98 #define REG_INT_MASK 0x11
100 /** Mask of pins which have generated an interrupt. Cleared on read */
101 #define REG_INT_STATUS 0x13
103 /* Manufacturer's ID getting from Device ID & Ctrl register */
105 MF_ID_MASK = GENMASK(7, 5),
109 /* Firmware revision getting from Device ID & Ctrl register */
111 FW_REV_MASK = GENMASK(4, 2),
121 * struct fxl6408_info - Data for fxl6408
123 * @dev: udevice structure for the device
124 * @addr: i2c slave address
125 * @device_id: hold the value of device id register
126 * @reg_io_dir: hold the value of direction register
127 * @reg_output: hold the value of output register
129 struct fxl6408_info {
137 static inline int fxl6408_write(struct udevice *dev, int reg, u8 val)
139 return dm_i2c_write(dev, reg, &val, 1);
142 static int fxl6408_read(struct udevice *dev, int reg)
147 ret = dm_i2c_read(dev, reg, &tmp, 1);
155 * fxl6408_is_output() - check whether the gpio configures as either
158 * @dev: an instance of a driver
159 * @offset: a gpio offset
161 * Return: false - input, true - output.
163 static bool fxl6408_is_output(struct udevice *dev, int offset)
165 struct fxl6408_info *info = dev_get_plat(dev);
167 return info->reg_io_dir & BIT(offset);
170 static int fxl6408_get_value(struct udevice *dev, uint offset)
172 int ret, reg = fxl6408_is_output(dev, offset) ? REG_OUT_STATE : REG_IN_STATUS;
174 ret = fxl6408_read(dev, reg);
178 return !!(ret & BIT(offset));
181 static int fxl6408_set_value(struct udevice *dev, uint offset, int value)
183 struct fxl6408_info *info = dev_get_plat(dev);
188 val = info->reg_output | BIT(offset);
190 val = info->reg_output & ~BIT(offset);
192 ret = fxl6408_write(dev, REG_OUT_STATE, val);
196 info->reg_output = val;
201 static int fxl6408_set_direction(struct udevice *dev, uint offset,
202 enum io_direction dir)
204 struct fxl6408_info *info = dev_get_plat(dev);
209 val = info->reg_io_dir & ~BIT(offset);
211 val = info->reg_io_dir | BIT(offset);
213 ret = fxl6408_write(dev, REG_IO_DIR, val);
217 info->reg_io_dir = val;
222 static int fxl6408_direction_input(struct udevice *dev, uint offset)
224 return fxl6408_set_direction(dev, offset, DIR_IN);
227 static int fxl6408_direction_output(struct udevice *dev, uint offset, int value)
231 /* Configure output value */
232 ret = fxl6408_set_value(dev, offset, value);
236 /* Configure direction as output */
237 fxl6408_set_direction(dev, offset, DIR_OUT);
242 static int fxl6408_get_function(struct udevice *dev, uint offset)
244 if (fxl6408_is_output(dev, offset))
250 static int fxl6408_xlate(struct udevice *dev, struct gpio_desc *desc,
251 struct ofnode_phandle_args *args)
253 desc->offset = args->args[0];
254 desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
259 static const struct dm_gpio_ops fxl6408_ops = {
260 .direction_input = fxl6408_direction_input,
261 .direction_output = fxl6408_direction_output,
262 .get_value = fxl6408_get_value,
263 .set_value = fxl6408_set_value,
264 .get_function = fxl6408_get_function,
265 .xlate = fxl6408_xlate,
268 static int fxl6408_probe(struct udevice *dev)
270 struct fxl6408_info *info = dev_get_plat(dev);
271 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
272 char bank_name[32], *tmp_str;
276 addr = dev_read_addr(dev);
283 * Check the device ID register to see if it's responding.
284 * This also clears RST_INT as a side effect, so we won't get
285 * the "we've been power cycled" interrupt once interrupts
288 ret = fxl6408_read(dev, REG_DEVID_CTRL);
290 dev_err(dev, "FXL6408 probe returned %d\n", ret);
294 if ((ret & MF_ID_MASK) >> MF_ID_SHIFT != MF_ID_FAIRCHILD) {
295 dev_err(dev, "FXL6408 probe: wrong Manufacturer's ID: 0x%02x\n", ret);
298 info->device_id = ret;
301 * Disable High-Z of outputs, so that the OUTPUT updates
302 * actually take effect.
304 ret = fxl6408_write(dev, REG_OUT_HIGH_Z, (u8)0);
306 dev_err(dev, "Error writing High-Z register\n");
311 * If configured, set initial output state and direction,
312 * otherwise read them from the chip.
314 if (dev_read_u32(dev, "initial_io_dir", &val32)) {
315 ret = fxl6408_read(dev, REG_IO_DIR);
317 dev_err(dev, "Error reading direction register\n");
320 info->reg_io_dir = ret;
322 info->reg_io_dir = val32 & 0xFF;
323 ret = fxl6408_write(dev, REG_IO_DIR, info->reg_io_dir);
325 dev_err(dev, "Error setting direction register\n");
330 if (dev_read_u32(dev, "initial_output", &val32)) {
331 ret = fxl6408_read(dev, REG_OUT_STATE);
333 dev_err(dev, "Error reading output register\n");
336 info->reg_output = ret;
338 info->reg_output = val32 & 0xFF;
339 ret = fxl6408_write(dev, REG_OUT_STATE, info->reg_output);
341 dev_err(dev, "Error setting output register\n");
346 tmp_str = (char *)dev_read_prop(dev, "bank-name", &size);
348 snprintf(bank_name, sizeof(bank_name), "%s@%x_", tmp_str,
351 snprintf(bank_name, sizeof(bank_name), "gpio@%x_", info->addr);
354 tmp_str = strdup(bank_name);
358 uc_priv->bank_name = tmp_str;
359 uc_priv->gpio_count = dev_get_driver_data(dev);
360 uc_priv->gpio_base = -1;
362 dev_dbg(dev, "%s (FW rev. %d) is ready\n", bank_name,
363 (info->device_id & FW_REV_MASK) >> FW_REV_SHIFT);
368 static const struct udevice_id fxl6408_ids[] = {
369 { .compatible = "fcs,fxl6408", .data = 8 },
373 U_BOOT_DRIVER(fxl6408_gpio) = {
374 .name = "fxl6408_gpio",
377 .probe = fxl6408_probe,
378 .of_match = fxl6408_ids,
379 .plat_auto = sizeof(struct fxl6408_info),