1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* MCP23S08 I2C GPIO driver */ |
3 | |
4 | #include <linux/i2c.h> |
5 | #include <linux/mod_devicetable.h> |
6 | #include <linux/module.h> |
7 | #include <linux/regmap.h> |
8 | |
9 | #include "pinctrl-mcp23s08.h" |
10 | |
11 | static int mcp230xx_probe(struct i2c_client *client) |
12 | { |
13 | const struct mcp23s08_info *info; |
14 | struct device *dev = &client->dev; |
15 | struct mcp23s08 *mcp; |
16 | int ret; |
17 | |
18 | mcp = devm_kzalloc(dev, size: sizeof(*mcp), GFP_KERNEL); |
19 | if (!mcp) |
20 | return -ENOMEM; |
21 | |
22 | info = i2c_get_match_data(client); |
23 | if (!info) |
24 | return dev_err_probe(dev, err: -EINVAL, fmt: "invalid device type\n" ); |
25 | |
26 | mcp->reg_shift = info->reg_shift; |
27 | mcp->chip.ngpio = info->ngpio; |
28 | mcp->chip.label = info->label; |
29 | mcp->regmap = devm_regmap_init_i2c(client, info->regmap); |
30 | if (IS_ERR(ptr: mcp->regmap)) |
31 | return PTR_ERR(ptr: mcp->regmap); |
32 | |
33 | mcp->irq = client->irq; |
34 | mcp->pinctrl_desc.name = "mcp23xxx-pinctrl" ; |
35 | |
36 | ret = mcp23s08_probe_one(mcp, dev, addr: client->addr, type: info->type, base: -1); |
37 | if (ret) |
38 | return ret; |
39 | |
40 | i2c_set_clientdata(client, data: mcp); |
41 | |
42 | return 0; |
43 | } |
44 | |
45 | static const struct mcp23s08_info mcp23008_i2c = { |
46 | .regmap = &mcp23x08_regmap, |
47 | .label = "mcp23008" , |
48 | .type = MCP_TYPE_008, |
49 | .ngpio = 8, |
50 | .reg_shift = 0, |
51 | }; |
52 | |
53 | static const struct mcp23s08_info mcp23017_i2c = { |
54 | .regmap = &mcp23x17_regmap, |
55 | .label = "mcp23017" , |
56 | .type = MCP_TYPE_017, |
57 | .ngpio = 16, |
58 | .reg_shift = 1, |
59 | }; |
60 | |
61 | static const struct mcp23s08_info mcp23018_i2c = { |
62 | .regmap = &mcp23x17_regmap, |
63 | .label = "mcp23018" , |
64 | .type = MCP_TYPE_018, |
65 | .ngpio = 16, |
66 | .reg_shift = 1, |
67 | }; |
68 | |
69 | static const struct i2c_device_id mcp230xx_id[] = { |
70 | { "mcp23008" , (kernel_ulong_t)&mcp23008_i2c }, |
71 | { "mcp23017" , (kernel_ulong_t)&mcp23017_i2c }, |
72 | { "mcp23018" , (kernel_ulong_t)&mcp23018_i2c }, |
73 | { } |
74 | }; |
75 | MODULE_DEVICE_TABLE(i2c, mcp230xx_id); |
76 | |
77 | static const struct of_device_id mcp23s08_i2c_of_match[] = { |
78 | { .compatible = "microchip,mcp23008" , .data = &mcp23008_i2c }, |
79 | { .compatible = "microchip,mcp23017" , .data = &mcp23017_i2c }, |
80 | { .compatible = "microchip,mcp23018" , .data = &mcp23018_i2c }, |
81 | /* NOTE: The use of the mcp prefix is deprecated and will be removed. */ |
82 | { .compatible = "mcp,mcp23008" , .data = &mcp23008_i2c }, |
83 | { .compatible = "mcp,mcp23017" , .data = &mcp23017_i2c }, |
84 | { } |
85 | }; |
86 | MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match); |
87 | |
88 | static struct i2c_driver mcp230xx_driver = { |
89 | .driver = { |
90 | .name = "mcp230xx" , |
91 | .of_match_table = mcp23s08_i2c_of_match, |
92 | }, |
93 | .probe = mcp230xx_probe, |
94 | .id_table = mcp230xx_id, |
95 | }; |
96 | |
97 | static int __init mcp23s08_i2c_init(void) |
98 | { |
99 | return i2c_add_driver(&mcp230xx_driver); |
100 | } |
101 | |
102 | /* |
103 | * Register after I²C postcore initcall and before |
104 | * subsys initcalls that may rely on these GPIOs. |
105 | */ |
106 | subsys_initcall(mcp23s08_i2c_init); |
107 | |
108 | static void mcp23s08_i2c_exit(void) |
109 | { |
110 | i2c_del_driver(driver: &mcp230xx_driver); |
111 | } |
112 | module_exit(mcp23s08_i2c_exit); |
113 | |
114 | MODULE_LICENSE("GPL" ); |
115 | |