1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright 2023 Toradex
7 * Author: Emanuele Ghidoli <emanuele.ghidoli@toradex.com>
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>
17 #define FXL6408_REG_DEVICE_ID 0x01
18 #define FXL6408_MF_FAIRCHILD 0b101
19 #define FXL6408_MF_SHIFT 5
21 /* Bits set here indicate that the GPIO is an output. */
22 #define FXL6408_REG_IO_DIR 0x03
25 * Bits set here, when the corresponding bit of IO_DIR is set, drive
26 * the output high instead of low.
28 #define FXL6408_REG_OUTPUT 0x05
30 /* Bits here make the output High-Z, instead of the OUTPUT value. */
31 #define FXL6408_REG_OUTPUT_HIGH_Z 0x07
33 /* Returns the current status (1 = HIGH) of the input pins. */
34 #define FXL6408_REG_INPUT_STATUS 0x0f
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.
42 #define FXL6408_REG_INT_STS 0x13
44 #define FXL6408_NGPIO 8
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
},
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
},
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
},
63 static const struct regmap_access_table rd_table
= {
64 .yes_ranges
= rd_range
,
65 .n_yes_ranges
= ARRAY_SIZE(rd_range
),
68 static const struct regmap_access_table wr_table
= {
69 .yes_ranges
= wr_range
,
70 .n_yes_ranges
= ARRAY_SIZE(wr_range
),
73 static const struct regmap_access_table volatile_table
= {
74 .yes_ranges
= volatile_range
,
75 .n_yes_ranges
= ARRAY_SIZE(volatile_range
),
78 static const struct regmap_config regmap
= {
82 .max_register
= FXL6408_REG_INT_STS
,
83 .wr_table
= &wr_table
,
84 .rd_table
= &rd_table
,
85 .volatile_table
= &volatile_table
,
87 .cache_type
= REGCACHE_MAPLE
,
88 .num_reg_defaults_raw
= FXL6408_REG_INT_STS
+ 1,
91 static int fxl6408_identify(struct device
*dev
, struct regmap
*regmap
)
95 ret
= regmap_read(regmap
, FXL6408_REG_DEVICE_ID
, &val
);
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
);
104 static int fxl6408_probe(struct i2c_client
*client
)
106 struct device
*dev
= &client
->dev
;
108 struct gpio_regmap_config gpio_config
= {
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
,
117 gpio_config
.regmap
= devm_regmap_init_i2c(client
, ®map
);
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");
122 ret
= fxl6408_identify(dev
, gpio_config
.regmap
);
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);
129 return dev_err_probe(dev
, ret
, "failed to write 'output high Z' register\n");
131 return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev
, &gpio_config
));
134 static const __maybe_unused
struct of_device_id fxl6408_dt_ids
[] = {
135 { .compatible
= "fcs,fxl6408" },
138 MODULE_DEVICE_TABLE(of
, fxl6408_dt_ids
);
140 static const struct i2c_device_id fxl6408_id
[] = {
144 MODULE_DEVICE_TABLE(i2c
, fxl6408_id
);
146 static struct i2c_driver fxl6408_driver
= {
149 .of_match_table
= fxl6408_dt_ids
,
151 .probe
= fxl6408_probe
,
152 .id_table
= fxl6408_id
,
154 module_i2c_driver(fxl6408_driver
);
156 MODULE_AUTHOR("Emanuele Ghidoli <emanuele.ghidoli@toradex.com>");
157 MODULE_DESCRIPTION("FXL6408 GPIO driver");
158 MODULE_LICENSE("GPL");