Skip to content

Commit 0381003

Browse files
eghidoliBartosz Golaszewski
authored andcommitted
gpio: fxl6408: add I2C GPIO expander driver
Add minimal driver for Fairchild FXL6408 8-bit I2C-controlled GPIO expander using the generic regmap based GPIO driver (GPIO_REGMAP). The driver implements setting the GPIO direction, reading inputs and writing outputs. In addition to that the FXL6408 has the following functionalities: - allows to monitor input ports for data transitions with an interrupt pin - all inputs can be configured with pull-up or pull-down resistors Datasheet: https://www.onsemi.com/download/data-sheet/pdf/fxl6408-d.pdf Signed-off-by: Emanuele Ghidoli <emanuele.ghidoli@toradex.com> Co-developed-by: Francesco Dolcini <francesco.dolcini@toradex.com> Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> [Bartosz: order includes alphabetically] Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
1 parent d41116f commit 0381003

3 files changed

Lines changed: 169 additions & 0 deletions

File tree

drivers/gpio/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,16 @@ config GPIO_ADNP
10071007
enough to represent all pins, but the driver will assume a
10081008
register layout for 64 pins (8 registers).
10091009

1010+
config GPIO_FXL6408
1011+
tristate "FXL6408 I2C GPIO expander"
1012+
select GPIO_REGMAP
1013+
select REGMAP_I2C
1014+
help
1015+
GPIO driver for Fairchild Semiconductor FXL6408 GPIO expander.
1016+
1017+
To compile this driver as a module, choose M here: the module will
1018+
be called gpio-fxl6408.
1019+
10101020
config GPIO_GW_PLD
10111021
tristate "Gateworks PLD GPIO Expander"
10121022
depends on OF_GPIO

drivers/gpio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
6060
obj-$(CONFIG_GPIO_EXAR) += gpio-exar.o
6161
obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
6262
obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o
63+
obj-$(CONFIG_GPIO_FXL6408) += gpio-fxl6408.o
6364
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
6465
obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o
6566
obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o

drivers/gpio/gpio-fxl6408.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* FXL6408 GPIO driver
4+
*
5+
* Copyright 2023 Toradex
6+
*
7+
* Author: Emanuele Ghidoli <emanuele.ghidoli@toradex.com>
8+
*/
9+
10+
#include <linux/err.h>
11+
#include <linux/gpio/regmap.h>
12+
#include <linux/i2c.h>
13+
#include <linux/kernel.h>
14+
#include <linux/module.h>
15+
#include <linux/regmap.h>
16+
17+
#define FXL6408_REG_DEVICE_ID 0x01
18+
#define FXL6408_MF_FAIRCHILD 0b101
19+
#define FXL6408_MF_SHIFT 5
20+
21+
/* Bits set here indicate that the GPIO is an output. */
22+
#define FXL6408_REG_IO_DIR 0x03
23+
24+
/*
25+
* Bits set here, when the corresponding bit of IO_DIR is set, drive
26+
* the output high instead of low.
27+
*/
28+
#define FXL6408_REG_OUTPUT 0x05
29+
30+
/* Bits here make the output High-Z, instead of the OUTPUT value. */
31+
#define FXL6408_REG_OUTPUT_HIGH_Z 0x07
32+
33+
/* Returns the current status (1 = HIGH) of the input pins. */
34+
#define FXL6408_REG_INPUT_STATUS 0x0f
35+
36+
/*
37+
* Return the current interrupt status
38+
* This bit is HIGH if input GPIO != default state (register 09h).
39+
* The flag is cleared after being read (bit returns to 0).
40+
* The input must go back to default state and change again before this flag is raised again.
41+
*/
42+
#define FXL6408_REG_INT_STS 0x13
43+
44+
#define FXL6408_NGPIO 8
45+
46+
static const struct regmap_range rd_range[] = {
47+
{ FXL6408_REG_DEVICE_ID, FXL6408_REG_DEVICE_ID },
48+
{ FXL6408_REG_IO_DIR, FXL6408_REG_OUTPUT },
49+
{ FXL6408_REG_INPUT_STATUS, FXL6408_REG_INPUT_STATUS },
50+
};
51+
52+
static const struct regmap_range wr_range[] = {
53+
{ FXL6408_REG_DEVICE_ID, FXL6408_REG_DEVICE_ID },
54+
{ FXL6408_REG_IO_DIR, FXL6408_REG_OUTPUT },
55+
{ FXL6408_REG_OUTPUT_HIGH_Z, FXL6408_REG_OUTPUT_HIGH_Z },
56+
};
57+
58+
static const struct regmap_range volatile_range[] = {
59+
{ FXL6408_REG_DEVICE_ID, FXL6408_REG_DEVICE_ID },
60+
{ FXL6408_REG_INPUT_STATUS, FXL6408_REG_INPUT_STATUS },
61+
};
62+
63+
static const struct regmap_access_table rd_table = {
64+
.yes_ranges = rd_range,
65+
.n_yes_ranges = ARRAY_SIZE(rd_range),
66+
};
67+
68+
static const struct regmap_access_table wr_table = {
69+
.yes_ranges = wr_range,
70+
.n_yes_ranges = ARRAY_SIZE(wr_range),
71+
};
72+
73+
static const struct regmap_access_table volatile_table = {
74+
.yes_ranges = volatile_range,
75+
.n_yes_ranges = ARRAY_SIZE(volatile_range),
76+
};
77+
78+
static const struct regmap_config regmap = {
79+
.reg_bits = 8,
80+
.val_bits = 8,
81+
82+
.max_register = FXL6408_REG_INT_STS,
83+
.wr_table = &wr_table,
84+
.rd_table = &rd_table,
85+
.volatile_table = &volatile_table,
86+
87+
.cache_type = REGCACHE_RBTREE,
88+
.num_reg_defaults_raw = FXL6408_REG_INT_STS + 1,
89+
};
90+
91+
static int fxl6408_identify(struct device *dev, struct regmap *regmap)
92+
{
93+
int val, ret;
94+
95+
ret = regmap_read(regmap, FXL6408_REG_DEVICE_ID, &val);
96+
if (ret)
97+
return dev_err_probe(dev, ret, "error reading DEVICE_ID\n");
98+
if (val >> FXL6408_MF_SHIFT != FXL6408_MF_FAIRCHILD)
99+
return dev_err_probe(dev, -ENODEV, "invalid device id 0x%02x\n", val);
100+
101+
return 0;
102+
}
103+
104+
static int fxl6408_probe(struct i2c_client *client)
105+
{
106+
struct device *dev = &client->dev;
107+
int ret;
108+
struct gpio_regmap_config gpio_config = {
109+
.parent = dev,
110+
.ngpio = FXL6408_NGPIO,
111+
.reg_dat_base = GPIO_REGMAP_ADDR(FXL6408_REG_INPUT_STATUS),
112+
.reg_set_base = GPIO_REGMAP_ADDR(FXL6408_REG_OUTPUT),
113+
.reg_dir_out_base = GPIO_REGMAP_ADDR(FXL6408_REG_IO_DIR),
114+
.ngpio_per_reg = FXL6408_NGPIO,
115+
};
116+
117+
gpio_config.regmap = devm_regmap_init_i2c(client, &regmap);
118+
if (IS_ERR(gpio_config.regmap))
119+
return dev_err_probe(dev, PTR_ERR(gpio_config.regmap),
120+
"failed to allocate register map\n");
121+
122+
ret = fxl6408_identify(dev, gpio_config.regmap);
123+
if (ret)
124+
return ret;
125+
126+
/* Disable High-Z of outputs, so that our OUTPUT updates actually take effect. */
127+
ret = regmap_write(gpio_config.regmap, FXL6408_REG_OUTPUT_HIGH_Z, 0);
128+
if (ret)
129+
return dev_err_probe(dev, ret, "failed to write 'output high Z' register\n");
130+
131+
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
132+
}
133+
134+
static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = {
135+
{ .compatible = "fcs,fxl6408" },
136+
{ }
137+
};
138+
MODULE_DEVICE_TABLE(of, fxl6408_dt_ids);
139+
140+
static const struct i2c_device_id fxl6408_id[] = {
141+
{ "fxl6408", 0 },
142+
{ }
143+
};
144+
MODULE_DEVICE_TABLE(i2c, fxl6408_id);
145+
146+
static struct i2c_driver fxl6408_driver = {
147+
.driver = {
148+
.name = "fxl6408",
149+
.of_match_table = fxl6408_dt_ids,
150+
},
151+
.probe_new = fxl6408_probe,
152+
.id_table = fxl6408_id,
153+
};
154+
module_i2c_driver(fxl6408_driver);
155+
156+
MODULE_AUTHOR("Emanuele Ghidoli <emanuele.ghidoli@toradex.com>");
157+
MODULE_DESCRIPTION("FXL6408 GPIO driver");
158+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)