1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Driver for SBS compliant Smart Battery System Managers |
4 | * |
5 | * The device communicates via i2c at address 0x0a and multiplexes access to up |
6 | * to four smart batteries at address 0x0b. |
7 | * |
8 | * Via sysfs interface the online state and charge type are presented. |
9 | * |
10 | * Datasheet SBSM: http://sbs-forum.org/specs/sbsm100b.pdf |
11 | * Datasheet LTC1760: http://cds.linear.com/docs/en/datasheet/1760fb.pdf |
12 | * |
13 | * Karl-Heinz Schneider <karl-heinz@schneider-inet.de> |
14 | */ |
15 | |
16 | #include <linux/gpio/driver.h> |
17 | #include <linux/module.h> |
18 | #include <linux/i2c.h> |
19 | #include <linux/i2c-mux.h> |
20 | #include <linux/power_supply.h> |
21 | #include <linux/property.h> |
22 | |
23 | #define SBSM_MAX_BATS 4 |
24 | #define SBSM_RETRY_CNT 3 |
25 | |
26 | /* registers addresses */ |
27 | #define SBSM_CMD_BATSYSSTATE 0x01 |
28 | #define SBSM_CMD_BATSYSSTATECONT 0x02 |
29 | #define SBSM_CMD_BATSYSINFO 0x04 |
30 | #define SBSM_CMD_LTC 0x3c |
31 | |
32 | #define SBSM_MASK_BAT_SUPPORTED GENMASK(3, 0) |
33 | #define SBSM_MASK_CHARGE_BAT GENMASK(7, 4) |
34 | #define SBSM_BIT_AC_PRESENT BIT(0) |
35 | #define SBSM_BIT_TURBO BIT(7) |
36 | |
37 | #define SBSM_SMB_BAT_OFFSET 11 |
38 | struct sbsm_data { |
39 | struct i2c_client *client; |
40 | struct i2c_mux_core *muxc; |
41 | |
42 | struct power_supply *psy; |
43 | |
44 | u8 cur_chan; /* currently selected channel */ |
45 | struct gpio_chip chip; |
46 | bool is_ltc1760; /* special capabilities */ |
47 | |
48 | unsigned int supported_bats; |
49 | unsigned int last_state; |
50 | unsigned int last_state_cont; |
51 | }; |
52 | |
53 | static enum power_supply_property sbsm_props[] = { |
54 | POWER_SUPPLY_PROP_ONLINE, |
55 | POWER_SUPPLY_PROP_CHARGE_TYPE, |
56 | }; |
57 | |
58 | static int sbsm_read_word(struct i2c_client *client, u8 address) |
59 | { |
60 | int reg, retries; |
61 | |
62 | for (retries = SBSM_RETRY_CNT; retries > 0; retries--) { |
63 | reg = i2c_smbus_read_word_data(client, command: address); |
64 | if (reg >= 0) |
65 | break; |
66 | } |
67 | |
68 | if (reg < 0) { |
69 | dev_err(&client->dev, "failed to read register 0x%02x\n" , |
70 | address); |
71 | } |
72 | |
73 | return reg; |
74 | } |
75 | |
76 | static int sbsm_write_word(struct i2c_client *client, u8 address, u16 word) |
77 | { |
78 | int ret, retries; |
79 | |
80 | for (retries = SBSM_RETRY_CNT; retries > 0; retries--) { |
81 | ret = i2c_smbus_write_word_data(client, command: address, value: word); |
82 | if (ret >= 0) |
83 | break; |
84 | } |
85 | if (ret < 0) |
86 | dev_err(&client->dev, "failed to write to register 0x%02x\n" , |
87 | address); |
88 | |
89 | return ret; |
90 | } |
91 | |
92 | static int sbsm_get_property(struct power_supply *psy, |
93 | enum power_supply_property psp, |
94 | union power_supply_propval *val) |
95 | { |
96 | struct sbsm_data *data = power_supply_get_drvdata(psy); |
97 | int regval = 0; |
98 | |
99 | switch (psp) { |
100 | case POWER_SUPPLY_PROP_ONLINE: |
101 | regval = sbsm_read_word(client: data->client, SBSM_CMD_BATSYSSTATECONT); |
102 | if (regval < 0) |
103 | return regval; |
104 | val->intval = !!(regval & SBSM_BIT_AC_PRESENT); |
105 | break; |
106 | |
107 | case POWER_SUPPLY_PROP_CHARGE_TYPE: |
108 | regval = sbsm_read_word(client: data->client, SBSM_CMD_BATSYSSTATE); |
109 | if (regval < 0) |
110 | return regval; |
111 | |
112 | if ((regval & SBSM_MASK_CHARGE_BAT) == 0) { |
113 | val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; |
114 | return 0; |
115 | } |
116 | val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; |
117 | |
118 | if (data->is_ltc1760) { |
119 | /* charge mode fast if turbo is active */ |
120 | regval = sbsm_read_word(client: data->client, SBSM_CMD_LTC); |
121 | if (regval < 0) |
122 | return regval; |
123 | else if (regval & SBSM_BIT_TURBO) |
124 | val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; |
125 | } |
126 | break; |
127 | |
128 | default: |
129 | return -EINVAL; |
130 | } |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | static int sbsm_prop_is_writeable(struct power_supply *psy, |
136 | enum power_supply_property psp) |
137 | { |
138 | struct sbsm_data *data = power_supply_get_drvdata(psy); |
139 | |
140 | return (psp == POWER_SUPPLY_PROP_CHARGE_TYPE) && data->is_ltc1760; |
141 | } |
142 | |
143 | static int sbsm_set_property(struct power_supply *psy, |
144 | enum power_supply_property psp, |
145 | const union power_supply_propval *val) |
146 | { |
147 | struct sbsm_data *data = power_supply_get_drvdata(psy); |
148 | int ret = -EINVAL; |
149 | u16 regval; |
150 | |
151 | switch (psp) { |
152 | case POWER_SUPPLY_PROP_CHARGE_TYPE: |
153 | /* write 1 to TURBO if type fast is given */ |
154 | if (!data->is_ltc1760) |
155 | break; |
156 | regval = val->intval == |
157 | POWER_SUPPLY_CHARGE_TYPE_FAST ? SBSM_BIT_TURBO : 0; |
158 | ret = sbsm_write_word(client: data->client, SBSM_CMD_LTC, word: regval); |
159 | break; |
160 | |
161 | default: |
162 | break; |
163 | } |
164 | |
165 | return ret; |
166 | } |
167 | |
168 | /* |
169 | * Switch to battery |
170 | * Parameter chan is directly the content of SMB_BAT* nibble |
171 | */ |
172 | static int sbsm_select(struct i2c_mux_core *muxc, u32 chan) |
173 | { |
174 | struct sbsm_data *data = i2c_mux_priv(muxc); |
175 | struct device *dev = &data->client->dev; |
176 | int ret = 0; |
177 | u16 reg; |
178 | |
179 | if (data->cur_chan == chan) |
180 | return ret; |
181 | |
182 | /* chan goes from 1 ... 4 */ |
183 | reg = BIT(SBSM_SMB_BAT_OFFSET + chan); |
184 | ret = sbsm_write_word(client: data->client, SBSM_CMD_BATSYSSTATE, word: reg); |
185 | if (ret) |
186 | dev_err(dev, "Failed to select channel %i\n" , chan); |
187 | else |
188 | data->cur_chan = chan; |
189 | |
190 | return ret; |
191 | } |
192 | |
193 | static int sbsm_gpio_get_value(struct gpio_chip *gc, unsigned int off) |
194 | { |
195 | struct sbsm_data *data = gpiochip_get_data(gc); |
196 | int ret; |
197 | |
198 | ret = sbsm_read_word(client: data->client, SBSM_CMD_BATSYSSTATE); |
199 | if (ret < 0) |
200 | return ret; |
201 | |
202 | return ret & BIT(off); |
203 | } |
204 | |
205 | /* |
206 | * This needs to be defined or the GPIO lib fails to register the pin. |
207 | * But the 'gpio' is always an input. |
208 | */ |
209 | static int sbsm_gpio_direction_input(struct gpio_chip *gc, unsigned int off) |
210 | { |
211 | return 0; |
212 | } |
213 | |
214 | static int sbsm_do_alert(struct device *dev, void *d) |
215 | { |
216 | struct i2c_client *client = i2c_verify_client(dev); |
217 | struct i2c_driver *driver; |
218 | |
219 | if (!client || client->addr != 0x0b) |
220 | return 0; |
221 | |
222 | device_lock(dev); |
223 | if (client->dev.driver) { |
224 | driver = to_i2c_driver(client->dev.driver); |
225 | if (driver->alert) |
226 | driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT, 0); |
227 | else |
228 | dev_warn(&client->dev, "no driver alert()!\n" ); |
229 | } else { |
230 | dev_dbg(&client->dev, "alert with no driver\n" ); |
231 | } |
232 | device_unlock(dev); |
233 | |
234 | return -EBUSY; |
235 | } |
236 | |
237 | static void sbsm_alert(struct i2c_client *client, enum i2c_alert_protocol prot, |
238 | unsigned int d) |
239 | { |
240 | struct sbsm_data *sbsm = i2c_get_clientdata(client); |
241 | |
242 | int ret, i, irq_bat = 0, state = 0; |
243 | |
244 | ret = sbsm_read_word(client: sbsm->client, SBSM_CMD_BATSYSSTATE); |
245 | if (ret >= 0) { |
246 | irq_bat = ret ^ sbsm->last_state; |
247 | sbsm->last_state = ret; |
248 | state = ret; |
249 | } |
250 | |
251 | ret = sbsm_read_word(client: sbsm->client, SBSM_CMD_BATSYSSTATECONT); |
252 | if ((ret >= 0) && |
253 | ((ret ^ sbsm->last_state_cont) & SBSM_BIT_AC_PRESENT)) { |
254 | irq_bat |= sbsm->supported_bats & state; |
255 | power_supply_changed(psy: sbsm->psy); |
256 | } |
257 | sbsm->last_state_cont = ret; |
258 | |
259 | for (i = 0; i < SBSM_MAX_BATS; i++) { |
260 | if (irq_bat & BIT(i)) { |
261 | device_for_each_child(dev: &sbsm->muxc->adapter[i]->dev, |
262 | NULL, fn: sbsm_do_alert); |
263 | } |
264 | } |
265 | } |
266 | |
267 | static int sbsm_gpio_setup(struct sbsm_data *data) |
268 | { |
269 | struct gpio_chip *gc = &data->chip; |
270 | struct i2c_client *client = data->client; |
271 | struct device *dev = &client->dev; |
272 | int ret; |
273 | |
274 | if (!device_property_present(dev, propname: "gpio-controller" )) |
275 | return 0; |
276 | |
277 | ret = sbsm_read_word(client, SBSM_CMD_BATSYSSTATE); |
278 | if (ret < 0) |
279 | return ret; |
280 | data->last_state = ret; |
281 | |
282 | ret = sbsm_read_word(client, SBSM_CMD_BATSYSSTATECONT); |
283 | if (ret < 0) |
284 | return ret; |
285 | data->last_state_cont = ret; |
286 | |
287 | gc->get = sbsm_gpio_get_value; |
288 | gc->direction_input = sbsm_gpio_direction_input; |
289 | gc->can_sleep = true; |
290 | gc->base = -1; |
291 | gc->ngpio = SBSM_MAX_BATS; |
292 | gc->label = client->name; |
293 | gc->parent = dev; |
294 | gc->owner = THIS_MODULE; |
295 | |
296 | ret = devm_gpiochip_add_data(dev, gc, data); |
297 | if (ret) |
298 | return dev_err_probe(dev, err: ret, fmt: "devm_gpiochip_add_data failed\n" ); |
299 | |
300 | return ret; |
301 | } |
302 | |
303 | static const struct power_supply_desc sbsm_default_psy_desc = { |
304 | .type = POWER_SUPPLY_TYPE_MAINS, |
305 | .properties = sbsm_props, |
306 | .num_properties = ARRAY_SIZE(sbsm_props), |
307 | .get_property = &sbsm_get_property, |
308 | .set_property = &sbsm_set_property, |
309 | .property_is_writeable = &sbsm_prop_is_writeable, |
310 | }; |
311 | |
312 | static void sbsm_del_mux_adapter(void *data) |
313 | { |
314 | struct sbsm_data *sbsm = data; |
315 | i2c_mux_del_adapters(muxc: sbsm->muxc); |
316 | } |
317 | |
318 | static int sbsm_probe(struct i2c_client *client) |
319 | { |
320 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
321 | struct i2c_adapter *adapter = client->adapter; |
322 | struct sbsm_data *data; |
323 | struct device *dev = &client->dev; |
324 | struct power_supply_desc *psy_desc; |
325 | struct power_supply_config psy_cfg = {}; |
326 | int ret = 0, i; |
327 | |
328 | /* Device listens only at address 0x0a */ |
329 | if (client->addr != 0x0a) |
330 | return -EINVAL; |
331 | |
332 | if (!i2c_check_functionality(adap: adapter, I2C_FUNC_SMBUS_WORD_DATA)) |
333 | return -EPFNOSUPPORT; |
334 | |
335 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
336 | if (!data) |
337 | return -ENOMEM; |
338 | |
339 | i2c_set_clientdata(client, data); |
340 | |
341 | data->client = client; |
342 | data->is_ltc1760 = !!strstr(id->name, "ltc1760" ); |
343 | |
344 | ret = sbsm_read_word(client, SBSM_CMD_BATSYSINFO); |
345 | if (ret < 0) |
346 | return ret; |
347 | data->supported_bats = ret & SBSM_MASK_BAT_SUPPORTED; |
348 | data->muxc = i2c_mux_alloc(parent: adapter, dev, SBSM_MAX_BATS, sizeof_priv: 0, |
349 | I2C_MUX_LOCKED, select: &sbsm_select, NULL); |
350 | if (!data->muxc) |
351 | return dev_err_probe(dev, err: -ENOMEM, fmt: "failed to alloc i2c mux\n" ); |
352 | data->muxc->priv = data; |
353 | |
354 | ret = devm_add_action_or_reset(dev, sbsm_del_mux_adapter, data); |
355 | if (ret) |
356 | return ret; |
357 | |
358 | /* register muxed i2c channels. One for each supported battery */ |
359 | for (i = 0; i < SBSM_MAX_BATS; ++i) { |
360 | if (data->supported_bats & BIT(i)) { |
361 | ret = i2c_mux_add_adapter(muxc: data->muxc, force_nr: 0, chan_id: i + 1, class: 0); |
362 | if (ret) |
363 | break; |
364 | } |
365 | } |
366 | if (ret) |
367 | return dev_err_probe(dev, err: ret, fmt: "failed to register i2c mux channel %d\n" , i + 1); |
368 | |
369 | psy_desc = devm_kmemdup(dev, src: &sbsm_default_psy_desc, len: sizeof(*psy_desc), GFP_KERNEL); |
370 | if (!psy_desc) |
371 | return -ENOMEM; |
372 | |
373 | psy_desc->name = devm_kasprintf(dev, GFP_KERNEL, fmt: "sbsm-%s" , dev_name(dev: &client->dev)); |
374 | if (!psy_desc->name) |
375 | return -ENOMEM; |
376 | |
377 | ret = sbsm_gpio_setup(data); |
378 | if (ret < 0) |
379 | return ret; |
380 | |
381 | psy_cfg.drv_data = data; |
382 | psy_cfg.of_node = dev->of_node; |
383 | data->psy = devm_power_supply_register(parent: dev, desc: psy_desc, cfg: &psy_cfg); |
384 | if (IS_ERR(ptr: data->psy)) |
385 | return dev_err_probe(dev, err: PTR_ERR(ptr: data->psy), |
386 | fmt: "failed to register power supply %s\n" , psy_desc->name); |
387 | |
388 | return 0; |
389 | } |
390 | |
391 | static const struct i2c_device_id sbsm_ids[] = { |
392 | { "sbs-manager" , 0 }, |
393 | { "ltc1760" , 0 }, |
394 | { } |
395 | }; |
396 | MODULE_DEVICE_TABLE(i2c, sbsm_ids); |
397 | |
398 | #ifdef CONFIG_OF |
399 | static const struct of_device_id sbsm_dt_ids[] = { |
400 | { .compatible = "sbs,sbs-manager" }, |
401 | { .compatible = "lltc,ltc1760" }, |
402 | { } |
403 | }; |
404 | MODULE_DEVICE_TABLE(of, sbsm_dt_ids); |
405 | #endif |
406 | |
407 | static struct i2c_driver sbsm_driver = { |
408 | .driver = { |
409 | .name = "sbsm" , |
410 | .of_match_table = of_match_ptr(sbsm_dt_ids), |
411 | }, |
412 | .probe = sbsm_probe, |
413 | .alert = sbsm_alert, |
414 | .id_table = sbsm_ids |
415 | }; |
416 | module_i2c_driver(sbsm_driver); |
417 | |
418 | MODULE_LICENSE("GPL" ); |
419 | MODULE_AUTHOR("Karl-Heinz Schneider <karl-heinz@schneider-inet.de>" ); |
420 | MODULE_DESCRIPTION("SBSM Smart Battery System Manager" ); |
421 | |