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