1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Linear Technology LTC4306 and LTC4305 I2C multiplexer/switch |
4 | * |
5 | * Copyright (C) 2017 Analog Devices Inc. |
6 | * |
7 | * Based on: i2c-mux-pca954x.c |
8 | * |
9 | * Datasheet: http://cds.linear.com/docs/en/datasheet/4306.pdf |
10 | */ |
11 | |
12 | #include <linux/gpio/consumer.h> |
13 | #include <linux/gpio/driver.h> |
14 | #include <linux/i2c-mux.h> |
15 | #include <linux/i2c.h> |
16 | #include <linux/module.h> |
17 | #include <linux/of.h> |
18 | #include <linux/property.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/slab.h> |
21 | |
22 | #define LTC4305_MAX_NCHANS 2 |
23 | #define LTC4306_MAX_NCHANS 4 |
24 | |
25 | #define LTC_REG_STATUS 0x0 |
26 | #define LTC_REG_CONFIG 0x1 |
27 | #define LTC_REG_MODE 0x2 |
28 | #define LTC_REG_SWITCH 0x3 |
29 | |
30 | #define LTC_DOWNSTREAM_ACCL_EN BIT(6) |
31 | #define LTC_UPSTREAM_ACCL_EN BIT(7) |
32 | |
33 | #define LTC_GPIO_ALL_INPUT 0xC0 |
34 | #define LTC_SWITCH_MASK 0xF0 |
35 | |
36 | enum ltc_type { |
37 | ltc_4305, |
38 | ltc_4306, |
39 | }; |
40 | |
41 | struct chip_desc { |
42 | u8 nchans; |
43 | u8 num_gpios; |
44 | }; |
45 | |
46 | struct ltc4306 { |
47 | struct regmap *regmap; |
48 | struct gpio_chip gpiochip; |
49 | const struct chip_desc *chip; |
50 | }; |
51 | |
52 | static const struct chip_desc chips[] = { |
53 | [ltc_4305] = { |
54 | .nchans = LTC4305_MAX_NCHANS, |
55 | }, |
56 | [ltc_4306] = { |
57 | .nchans = LTC4306_MAX_NCHANS, |
58 | .num_gpios = 2, |
59 | }, |
60 | }; |
61 | |
62 | static bool ltc4306_is_volatile_reg(struct device *dev, unsigned int reg) |
63 | { |
64 | return reg == LTC_REG_CONFIG; |
65 | } |
66 | |
67 | static const struct regmap_config ltc4306_regmap_config = { |
68 | .reg_bits = 8, |
69 | .val_bits = 8, |
70 | .max_register = LTC_REG_SWITCH, |
71 | .volatile_reg = ltc4306_is_volatile_reg, |
72 | .cache_type = REGCACHE_FLAT, |
73 | }; |
74 | |
75 | static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset) |
76 | { |
77 | struct ltc4306 *data = gpiochip_get_data(gc: chip); |
78 | unsigned int val; |
79 | int ret; |
80 | |
81 | ret = regmap_read(map: data->regmap, LTC_REG_CONFIG, val: &val); |
82 | if (ret < 0) |
83 | return ret; |
84 | |
85 | return !!(val & BIT(1 - offset)); |
86 | } |
87 | |
88 | static void ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset, |
89 | int value) |
90 | { |
91 | struct ltc4306 *data = gpiochip_get_data(gc: chip); |
92 | |
93 | regmap_update_bits(map: data->regmap, LTC_REG_CONFIG, BIT(5 - offset), |
94 | val: value ? BIT(5 - offset) : 0); |
95 | } |
96 | |
97 | static int ltc4306_gpio_get_direction(struct gpio_chip *chip, |
98 | unsigned int offset) |
99 | { |
100 | struct ltc4306 *data = gpiochip_get_data(gc: chip); |
101 | unsigned int val; |
102 | int ret; |
103 | |
104 | ret = regmap_read(map: data->regmap, LTC_REG_MODE, val: &val); |
105 | if (ret < 0) |
106 | return ret; |
107 | |
108 | return !!(val & BIT(7 - offset)); |
109 | } |
110 | |
111 | static int ltc4306_gpio_direction_input(struct gpio_chip *chip, |
112 | unsigned int offset) |
113 | { |
114 | struct ltc4306 *data = gpiochip_get_data(gc: chip); |
115 | |
116 | return regmap_update_bits(map: data->regmap, LTC_REG_MODE, |
117 | BIT(7 - offset), BIT(7 - offset)); |
118 | } |
119 | |
120 | static int ltc4306_gpio_direction_output(struct gpio_chip *chip, |
121 | unsigned int offset, int value) |
122 | { |
123 | struct ltc4306 *data = gpiochip_get_data(gc: chip); |
124 | |
125 | ltc4306_gpio_set(chip, offset, value); |
126 | return regmap_update_bits(map: data->regmap, LTC_REG_MODE, |
127 | BIT(7 - offset), val: 0); |
128 | } |
129 | |
130 | static int ltc4306_gpio_set_config(struct gpio_chip *chip, |
131 | unsigned int offset, unsigned long config) |
132 | { |
133 | struct ltc4306 *data = gpiochip_get_data(gc: chip); |
134 | unsigned int val; |
135 | |
136 | switch (pinconf_to_config_param(config)) { |
137 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: |
138 | val = 0; |
139 | break; |
140 | case PIN_CONFIG_DRIVE_PUSH_PULL: |
141 | val = BIT(4 - offset); |
142 | break; |
143 | default: |
144 | return -ENOTSUPP; |
145 | } |
146 | |
147 | return regmap_update_bits(map: data->regmap, LTC_REG_MODE, |
148 | BIT(4 - offset), val); |
149 | } |
150 | |
151 | static int ltc4306_gpio_init(struct ltc4306 *data) |
152 | { |
153 | struct device *dev = regmap_get_device(map: data->regmap); |
154 | |
155 | if (!data->chip->num_gpios) |
156 | return 0; |
157 | |
158 | data->gpiochip.label = dev_name(dev); |
159 | data->gpiochip.base = -1; |
160 | data->gpiochip.ngpio = data->chip->num_gpios; |
161 | data->gpiochip.parent = dev; |
162 | data->gpiochip.can_sleep = true; |
163 | data->gpiochip.get_direction = ltc4306_gpio_get_direction; |
164 | data->gpiochip.direction_input = ltc4306_gpio_direction_input; |
165 | data->gpiochip.direction_output = ltc4306_gpio_direction_output; |
166 | data->gpiochip.get = ltc4306_gpio_get; |
167 | data->gpiochip.set = ltc4306_gpio_set; |
168 | data->gpiochip.set_config = ltc4306_gpio_set_config; |
169 | data->gpiochip.owner = THIS_MODULE; |
170 | |
171 | /* gpiolib assumes all GPIOs default input */ |
172 | regmap_write(map: data->regmap, LTC_REG_MODE, LTC_GPIO_ALL_INPUT); |
173 | |
174 | return devm_gpiochip_add_data(dev, &data->gpiochip, data); |
175 | } |
176 | |
177 | static int ltc4306_select_mux(struct i2c_mux_core *muxc, u32 chan) |
178 | { |
179 | struct ltc4306 *data = i2c_mux_priv(muxc); |
180 | |
181 | return regmap_update_bits(map: data->regmap, LTC_REG_SWITCH, |
182 | LTC_SWITCH_MASK, BIT(7 - chan)); |
183 | } |
184 | |
185 | static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan) |
186 | { |
187 | struct ltc4306 *data = i2c_mux_priv(muxc); |
188 | |
189 | return regmap_update_bits(map: data->regmap, LTC_REG_SWITCH, |
190 | LTC_SWITCH_MASK, val: 0); |
191 | } |
192 | |
193 | static const struct i2c_device_id ltc4306_id[] = { |
194 | { "ltc4305" , ltc_4305 }, |
195 | { "ltc4306" , ltc_4306 }, |
196 | { } |
197 | }; |
198 | MODULE_DEVICE_TABLE(i2c, ltc4306_id); |
199 | |
200 | static const struct of_device_id ltc4306_of_match[] = { |
201 | { .compatible = "lltc,ltc4305" , .data = &chips[ltc_4305] }, |
202 | { .compatible = "lltc,ltc4306" , .data = &chips[ltc_4306] }, |
203 | { } |
204 | }; |
205 | MODULE_DEVICE_TABLE(of, ltc4306_of_match); |
206 | |
207 | static int ltc4306_probe(struct i2c_client *client) |
208 | { |
209 | struct i2c_adapter *adap = client->adapter; |
210 | const struct chip_desc *chip; |
211 | struct i2c_mux_core *muxc; |
212 | struct ltc4306 *data; |
213 | struct gpio_desc *gpio; |
214 | bool idle_disc; |
215 | unsigned int val = 0; |
216 | int num, ret; |
217 | |
218 | chip = of_device_get_match_data(dev: &client->dev); |
219 | |
220 | if (!chip) |
221 | chip = &chips[i2c_match_id(id: ltc4306_id, client)->driver_data]; |
222 | |
223 | idle_disc = device_property_read_bool(dev: &client->dev, |
224 | propname: "i2c-mux-idle-disconnect" ); |
225 | |
226 | muxc = i2c_mux_alloc(parent: adap, dev: &client->dev, |
227 | max_adapters: chip->nchans, sizeof_priv: sizeof(*data), |
228 | I2C_MUX_LOCKED, select: ltc4306_select_mux, |
229 | deselect: idle_disc ? ltc4306_deselect_mux : NULL); |
230 | if (!muxc) |
231 | return -ENOMEM; |
232 | data = i2c_mux_priv(muxc); |
233 | data->chip = chip; |
234 | |
235 | i2c_set_clientdata(client, data: muxc); |
236 | |
237 | data->regmap = devm_regmap_init_i2c(client, <c4306_regmap_config); |
238 | if (IS_ERR(ptr: data->regmap)) { |
239 | ret = PTR_ERR(ptr: data->regmap); |
240 | dev_err(&client->dev, "Failed to allocate register map: %d\n" , |
241 | ret); |
242 | return ret; |
243 | } |
244 | |
245 | /* Reset and enable the mux if an enable GPIO is specified. */ |
246 | gpio = devm_gpiod_get_optional(dev: &client->dev, con_id: "enable" , flags: GPIOD_OUT_LOW); |
247 | if (IS_ERR(ptr: gpio)) |
248 | return PTR_ERR(ptr: gpio); |
249 | |
250 | if (gpio) { |
251 | udelay(1); |
252 | gpiod_set_value(desc: gpio, value: 1); |
253 | } |
254 | |
255 | /* |
256 | * Write the mux register at addr to verify |
257 | * that the mux is in fact present. This also |
258 | * initializes the mux to disconnected state. |
259 | */ |
260 | if (regmap_write(map: data->regmap, LTC_REG_SWITCH, val: 0) < 0) { |
261 | dev_warn(&client->dev, "probe failed\n" ); |
262 | return -ENODEV; |
263 | } |
264 | |
265 | if (device_property_read_bool(dev: &client->dev, |
266 | propname: "ltc,downstream-accelerators-enable" )) |
267 | val |= LTC_DOWNSTREAM_ACCL_EN; |
268 | |
269 | if (device_property_read_bool(dev: &client->dev, |
270 | propname: "ltc,upstream-accelerators-enable" )) |
271 | val |= LTC_UPSTREAM_ACCL_EN; |
272 | |
273 | if (regmap_write(map: data->regmap, LTC_REG_CONFIG, val) < 0) |
274 | return -ENODEV; |
275 | |
276 | ret = ltc4306_gpio_init(data); |
277 | if (ret < 0) |
278 | return ret; |
279 | |
280 | /* Now create an adapter for each channel */ |
281 | for (num = 0; num < chip->nchans; num++) { |
282 | ret = i2c_mux_add_adapter(muxc, force_nr: 0, chan_id: num, class: 0); |
283 | if (ret) { |
284 | i2c_mux_del_adapters(muxc); |
285 | return ret; |
286 | } |
287 | } |
288 | |
289 | dev_info(&client->dev, |
290 | "registered %d multiplexed busses for I2C switch %s\n" , |
291 | num, client->name); |
292 | |
293 | return 0; |
294 | } |
295 | |
296 | static void ltc4306_remove(struct i2c_client *client) |
297 | { |
298 | struct i2c_mux_core *muxc = i2c_get_clientdata(client); |
299 | |
300 | i2c_mux_del_adapters(muxc); |
301 | } |
302 | |
303 | static struct i2c_driver ltc4306_driver = { |
304 | .driver = { |
305 | .name = "ltc4306" , |
306 | .of_match_table = of_match_ptr(ltc4306_of_match), |
307 | }, |
308 | .probe = ltc4306_probe, |
309 | .remove = ltc4306_remove, |
310 | .id_table = ltc4306_id, |
311 | }; |
312 | |
313 | module_i2c_driver(ltc4306_driver); |
314 | |
315 | MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>" ); |
316 | MODULE_DESCRIPTION("Linear Technology LTC4306, LTC4305 I2C mux/switch driver" ); |
317 | MODULE_LICENSE("GPL v2" ); |
318 | |