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