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