xref: /linux/drivers/power/supply/sbs-manager.c (revision a0b8839e2afc6613566ab723f8e3f0e698e9e007)
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 
19*a0b8839eSPhil 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>
24*a0b8839eSPhil 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 */
48*a0b8839eSPhil Reid 	struct gpio_chip chip;
49dbc4dedaSKarl-Heinz Schneider 	bool is_ltc1760;      /* special capabilities */
50*a0b8839eSPhil Reid 
51*a0b8839eSPhil Reid 	unsigned int supported_bats;
52*a0b8839eSPhil Reid 	unsigned int last_state;
53*a0b8839eSPhil 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 */
186dbc4dedaSKarl-Heinz Schneider 	reg = 1 << 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 
196*a0b8839eSPhil Reid static int sbsm_gpio_get_value(struct gpio_chip *gc, unsigned off)
197*a0b8839eSPhil Reid {
198*a0b8839eSPhil Reid 	struct sbsm_data *data = gpiochip_get_data(gc);
199*a0b8839eSPhil Reid 	int ret;
200*a0b8839eSPhil Reid 
201*a0b8839eSPhil Reid 	ret = sbsm_read_word(data->client, SBSM_CMD_BATSYSSTATE);
202*a0b8839eSPhil Reid 	if (ret < 0)
203*a0b8839eSPhil Reid 		return ret;
204*a0b8839eSPhil Reid 
205*a0b8839eSPhil Reid 	return ret & BIT(off);
206*a0b8839eSPhil Reid }
207*a0b8839eSPhil Reid 
208*a0b8839eSPhil Reid /*
209*a0b8839eSPhil Reid  * This needs to be defined or the GPIO lib fails to register the pin.
210*a0b8839eSPhil Reid  * But the 'gpio' is always an input.
211*a0b8839eSPhil Reid  */
212*a0b8839eSPhil Reid static int sbsm_gpio_direction_input(struct gpio_chip *gc, unsigned off)
213*a0b8839eSPhil Reid {
214*a0b8839eSPhil Reid 	return 0;
215*a0b8839eSPhil Reid }
216*a0b8839eSPhil Reid 
217*a0b8839eSPhil Reid static int sbsm_do_alert(struct device *dev, void *d)
218*a0b8839eSPhil Reid {
219*a0b8839eSPhil Reid 	struct i2c_client *client = i2c_verify_client(dev);
220*a0b8839eSPhil Reid 	struct i2c_driver *driver;
221*a0b8839eSPhil Reid 
222*a0b8839eSPhil Reid 	if (!client || client->addr != 0x0b)
223*a0b8839eSPhil Reid 		return 0;
224*a0b8839eSPhil Reid 
225*a0b8839eSPhil Reid 	device_lock(dev);
226*a0b8839eSPhil Reid 	if (client->dev.driver) {
227*a0b8839eSPhil Reid 		driver = to_i2c_driver(client->dev.driver);
228*a0b8839eSPhil Reid 		if (driver->alert)
229*a0b8839eSPhil Reid 			driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT, 0);
230*a0b8839eSPhil Reid 		else
231*a0b8839eSPhil Reid 			dev_warn(&client->dev, "no driver alert()!\n");
232*a0b8839eSPhil Reid 	} else
233*a0b8839eSPhil Reid 		dev_dbg(&client->dev, "alert with no driver\n");
234*a0b8839eSPhil Reid 	device_unlock(dev);
235*a0b8839eSPhil Reid 
236*a0b8839eSPhil Reid 	return -EBUSY;
237*a0b8839eSPhil Reid }
238*a0b8839eSPhil Reid 
239*a0b8839eSPhil Reid static void sbsm_alert(struct i2c_client *client, enum i2c_alert_protocol prot,
240*a0b8839eSPhil Reid 		       unsigned int d)
241*a0b8839eSPhil Reid {
242*a0b8839eSPhil Reid 	struct sbsm_data *sbsm = i2c_get_clientdata(client);
243*a0b8839eSPhil Reid 
244*a0b8839eSPhil Reid 	int ret, i, irq_bat = 0, state = 0;
245*a0b8839eSPhil Reid 
246*a0b8839eSPhil Reid 	ret = sbsm_read_word(sbsm->client, SBSM_CMD_BATSYSSTATE);
247*a0b8839eSPhil Reid 	if (ret >= 0) {
248*a0b8839eSPhil Reid 		irq_bat = ret ^ sbsm->last_state;
249*a0b8839eSPhil Reid 		sbsm->last_state = ret;
250*a0b8839eSPhil Reid 		state = ret;
251*a0b8839eSPhil Reid 	}
252*a0b8839eSPhil Reid 
253*a0b8839eSPhil Reid 	ret = sbsm_read_word(sbsm->client, SBSM_CMD_BATSYSSTATECONT);
254*a0b8839eSPhil Reid 	if ((ret >= 0) &&
255*a0b8839eSPhil Reid 	    ((ret ^ sbsm->last_state_cont) & SBSM_BIT_AC_PRESENT)) {
256*a0b8839eSPhil Reid 		irq_bat |= sbsm->supported_bats & state;
257*a0b8839eSPhil Reid 		power_supply_changed(sbsm->psy);
258*a0b8839eSPhil Reid 	}
259*a0b8839eSPhil Reid 	sbsm->last_state_cont = ret;
260*a0b8839eSPhil Reid 
261*a0b8839eSPhil Reid 	for (i = 0; i < SBSM_MAX_BATS; i++) {
262*a0b8839eSPhil Reid 		if (irq_bat & BIT(i)) {
263*a0b8839eSPhil Reid 			device_for_each_child(&sbsm->muxc->adapter[i]->dev,
264*a0b8839eSPhil Reid 					      NULL, sbsm_do_alert);
265*a0b8839eSPhil Reid 		}
266*a0b8839eSPhil Reid 	}
267*a0b8839eSPhil Reid }
268*a0b8839eSPhil Reid 
269*a0b8839eSPhil Reid static int sbsm_gpio_setup(struct sbsm_data *data)
270*a0b8839eSPhil Reid {
271*a0b8839eSPhil Reid 	struct gpio_chip *gc = &data->chip;
272*a0b8839eSPhil Reid 	struct i2c_client *client = data->client;
273*a0b8839eSPhil Reid 	struct device *dev = &client->dev;
274*a0b8839eSPhil Reid 	int ret;
275*a0b8839eSPhil Reid 
276*a0b8839eSPhil Reid 	if (!device_property_present(dev, "gpio-controller"))
277*a0b8839eSPhil Reid 		return 0;
278*a0b8839eSPhil Reid 
279*a0b8839eSPhil Reid 	ret  = sbsm_read_word(client, SBSM_CMD_BATSYSSTATE);
280*a0b8839eSPhil Reid 	if (ret < 0)
281*a0b8839eSPhil Reid 		return ret;
282*a0b8839eSPhil Reid 	data->last_state = ret;
283*a0b8839eSPhil Reid 
284*a0b8839eSPhil Reid 	ret  = sbsm_read_word(client, SBSM_CMD_BATSYSSTATECONT);
285*a0b8839eSPhil Reid 	if (ret < 0)
286*a0b8839eSPhil Reid 		return ret;
287*a0b8839eSPhil Reid 	data->last_state_cont = ret;
288*a0b8839eSPhil Reid 
289*a0b8839eSPhil Reid 	gc->get = sbsm_gpio_get_value;
290*a0b8839eSPhil Reid 	gc->direction_input  = sbsm_gpio_direction_input;
291*a0b8839eSPhil Reid 	gc->can_sleep = true;
292*a0b8839eSPhil Reid 	gc->base = -1;
293*a0b8839eSPhil Reid 	gc->ngpio = SBSM_MAX_BATS;
294*a0b8839eSPhil Reid 	gc->label = client->name;
295*a0b8839eSPhil Reid 	gc->parent = dev;
296*a0b8839eSPhil Reid 	gc->owner = THIS_MODULE;
297*a0b8839eSPhil Reid 
298*a0b8839eSPhil Reid 	ret = devm_gpiochip_add_data(dev, gc, data);
299*a0b8839eSPhil Reid 	if (ret) {
300*a0b8839eSPhil Reid 		dev_err(dev, "devm_gpiochip_add_data failed: %d\n", ret);
301*a0b8839eSPhil Reid 		return ret;
302*a0b8839eSPhil Reid 	}
303*a0b8839eSPhil Reid 
304*a0b8839eSPhil Reid 	return ret;
305*a0b8839eSPhil Reid }
306*a0b8839eSPhil Reid 
307dbc4dedaSKarl-Heinz Schneider static const struct power_supply_desc sbsm_default_psy_desc = {
308dbc4dedaSKarl-Heinz Schneider 	.type = POWER_SUPPLY_TYPE_MAINS,
309dbc4dedaSKarl-Heinz Schneider 	.properties = sbsm_props,
310dbc4dedaSKarl-Heinz Schneider 	.num_properties = ARRAY_SIZE(sbsm_props),
311dbc4dedaSKarl-Heinz Schneider 	.get_property = &sbsm_get_property,
312dbc4dedaSKarl-Heinz Schneider 	.set_property = &sbsm_set_property,
313dbc4dedaSKarl-Heinz Schneider 	.property_is_writeable = &sbsm_prop_is_writeable,
314dbc4dedaSKarl-Heinz Schneider };
315dbc4dedaSKarl-Heinz Schneider 
316dbc4dedaSKarl-Heinz Schneider static int sbsm_probe(struct i2c_client *client,
317dbc4dedaSKarl-Heinz Schneider 		      const struct i2c_device_id *id)
318dbc4dedaSKarl-Heinz Schneider {
319dbc4dedaSKarl-Heinz Schneider 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
320dbc4dedaSKarl-Heinz Schneider 	struct sbsm_data *data;
321dbc4dedaSKarl-Heinz Schneider 	struct device *dev = &client->dev;
322dbc4dedaSKarl-Heinz Schneider 	struct power_supply_desc *psy_desc;
323dbc4dedaSKarl-Heinz Schneider 	struct power_supply_config psy_cfg = {};
324*a0b8839eSPhil Reid 	int ret = 0, i;
325dbc4dedaSKarl-Heinz Schneider 
326dbc4dedaSKarl-Heinz Schneider 	/* Device listens only at address 0x0a */
327dbc4dedaSKarl-Heinz Schneider 	if (client->addr != 0x0a)
328dbc4dedaSKarl-Heinz Schneider 		return -EINVAL;
329dbc4dedaSKarl-Heinz Schneider 
330dbc4dedaSKarl-Heinz Schneider 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
331dbc4dedaSKarl-Heinz Schneider 		return -EPFNOSUPPORT;
332dbc4dedaSKarl-Heinz Schneider 
333dbc4dedaSKarl-Heinz Schneider 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
334dbc4dedaSKarl-Heinz Schneider 	if (!data)
335dbc4dedaSKarl-Heinz Schneider 		return -ENOMEM;
336dbc4dedaSKarl-Heinz Schneider 
337dbc4dedaSKarl-Heinz Schneider 	i2c_set_clientdata(client, data);
338dbc4dedaSKarl-Heinz Schneider 
339dbc4dedaSKarl-Heinz Schneider 	data->client = client;
340dbc4dedaSKarl-Heinz Schneider 	data->is_ltc1760 = !!strstr(id->name, "ltc1760");
341dbc4dedaSKarl-Heinz Schneider 
342dbc4dedaSKarl-Heinz Schneider 	ret  = sbsm_read_word(client, SBSM_CMD_BATSYSINFO);
343dbc4dedaSKarl-Heinz Schneider 	if (ret < 0)
344dbc4dedaSKarl-Heinz Schneider 		return ret;
345*a0b8839eSPhil Reid 	data->supported_bats = ret & SBSM_MASK_BAT_SUPPORTED;
346dbc4dedaSKarl-Heinz Schneider 	data->muxc = i2c_mux_alloc(adapter, dev, SBSM_MAX_BATS, 0,
347dbc4dedaSKarl-Heinz Schneider 				   I2C_MUX_LOCKED, &sbsm_select, NULL);
348dbc4dedaSKarl-Heinz Schneider 	if (!data->muxc) {
349dbc4dedaSKarl-Heinz Schneider 		dev_err(dev, "failed to alloc i2c mux\n");
350dbc4dedaSKarl-Heinz Schneider 		ret = -ENOMEM;
351dbc4dedaSKarl-Heinz Schneider 		goto err_mux_alloc;
352dbc4dedaSKarl-Heinz Schneider 	}
353dbc4dedaSKarl-Heinz Schneider 	data->muxc->priv = data;
354dbc4dedaSKarl-Heinz Schneider 
355dbc4dedaSKarl-Heinz Schneider 	/* register muxed i2c channels. One for each supported battery */
356dbc4dedaSKarl-Heinz Schneider 	for (i = 0; i < SBSM_MAX_BATS; ++i) {
357*a0b8839eSPhil Reid 		if (data->supported_bats & BIT(i)) {
358dbc4dedaSKarl-Heinz Schneider 			ret = i2c_mux_add_adapter(data->muxc, 0, i + 1, 0);
359dbc4dedaSKarl-Heinz Schneider 			if (ret)
360dbc4dedaSKarl-Heinz Schneider 				break;
361dbc4dedaSKarl-Heinz Schneider 		}
362dbc4dedaSKarl-Heinz Schneider 	}
363dbc4dedaSKarl-Heinz Schneider 	if (ret) {
364dbc4dedaSKarl-Heinz Schneider 		dev_err(dev, "failed to register i2c mux channel %d\n", i + 1);
365dbc4dedaSKarl-Heinz Schneider 		goto err_mux_register;
366dbc4dedaSKarl-Heinz Schneider 	}
367dbc4dedaSKarl-Heinz Schneider 
368dbc4dedaSKarl-Heinz Schneider 	psy_desc = devm_kmemdup(dev, &sbsm_default_psy_desc,
369dbc4dedaSKarl-Heinz Schneider 				sizeof(struct power_supply_desc),
370dbc4dedaSKarl-Heinz Schneider 				GFP_KERNEL);
371dbc4dedaSKarl-Heinz Schneider 	if (!psy_desc) {
372dbc4dedaSKarl-Heinz Schneider 		ret = -ENOMEM;
373dbc4dedaSKarl-Heinz Schneider 		goto err_psy;
374dbc4dedaSKarl-Heinz Schneider 	}
375dbc4dedaSKarl-Heinz Schneider 
376dbc4dedaSKarl-Heinz Schneider 	psy_desc->name = devm_kasprintf(dev, GFP_KERNEL, "sbsm-%s",
377dbc4dedaSKarl-Heinz Schneider 					dev_name(&client->dev));
378dbc4dedaSKarl-Heinz Schneider 	if (!psy_desc->name) {
379dbc4dedaSKarl-Heinz Schneider 		ret = -ENOMEM;
380dbc4dedaSKarl-Heinz Schneider 		goto err_psy;
381dbc4dedaSKarl-Heinz Schneider 	}
382*a0b8839eSPhil Reid 	ret = sbsm_gpio_setup(data);
383*a0b8839eSPhil Reid 	if (ret < 0)
384*a0b8839eSPhil Reid 		goto err_psy;
385dbc4dedaSKarl-Heinz Schneider 
386dbc4dedaSKarl-Heinz Schneider 	psy_cfg.drv_data = data;
387dbc4dedaSKarl-Heinz Schneider 	psy_cfg.of_node = dev->of_node;
388dbc4dedaSKarl-Heinz Schneider 	data->psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
389dbc4dedaSKarl-Heinz Schneider 	if (IS_ERR(data->psy)) {
390dbc4dedaSKarl-Heinz Schneider 		ret = PTR_ERR(data->psy);
391dbc4dedaSKarl-Heinz Schneider 		dev_err(dev, "failed to register power supply %s\n",
392dbc4dedaSKarl-Heinz Schneider 			psy_desc->name);
393dbc4dedaSKarl-Heinz Schneider 		goto err_psy;
394dbc4dedaSKarl-Heinz Schneider 	}
395dbc4dedaSKarl-Heinz Schneider 
396dbc4dedaSKarl-Heinz Schneider 	return 0;
397dbc4dedaSKarl-Heinz Schneider 
398dbc4dedaSKarl-Heinz Schneider err_psy:
399dbc4dedaSKarl-Heinz Schneider err_mux_register:
400dbc4dedaSKarl-Heinz Schneider 	i2c_mux_del_adapters(data->muxc);
401dbc4dedaSKarl-Heinz Schneider 
402dbc4dedaSKarl-Heinz Schneider err_mux_alloc:
403dbc4dedaSKarl-Heinz Schneider 	return ret;
404dbc4dedaSKarl-Heinz Schneider }
405dbc4dedaSKarl-Heinz Schneider 
406dbc4dedaSKarl-Heinz Schneider static int sbsm_remove(struct i2c_client *client)
407dbc4dedaSKarl-Heinz Schneider {
408dbc4dedaSKarl-Heinz Schneider 	struct sbsm_data *data = i2c_get_clientdata(client);
409dbc4dedaSKarl-Heinz Schneider 
410dbc4dedaSKarl-Heinz Schneider 	i2c_mux_del_adapters(data->muxc);
411dbc4dedaSKarl-Heinz Schneider 	return 0;
412dbc4dedaSKarl-Heinz Schneider }
413dbc4dedaSKarl-Heinz Schneider 
414dbc4dedaSKarl-Heinz Schneider static const struct i2c_device_id sbsm_ids[] = {
415dbc4dedaSKarl-Heinz Schneider 	{ "sbs-manager", 0 },
416dbc4dedaSKarl-Heinz Schneider 	{ "ltc1760",     0 },
417dbc4dedaSKarl-Heinz Schneider 	{ }
418dbc4dedaSKarl-Heinz Schneider };
419dbc4dedaSKarl-Heinz Schneider MODULE_DEVICE_TABLE(i2c, sbsm_ids);
420dbc4dedaSKarl-Heinz Schneider 
421dbc4dedaSKarl-Heinz Schneider #ifdef CONFIG_OF
422dbc4dedaSKarl-Heinz Schneider static const struct of_device_id sbsm_dt_ids[] = {
423dbc4dedaSKarl-Heinz Schneider 	{ .compatible = "sbs,sbs-manager" },
424dbc4dedaSKarl-Heinz Schneider 	{ .compatible = "lltc,ltc1760" },
425dbc4dedaSKarl-Heinz Schneider 	{ }
426dbc4dedaSKarl-Heinz Schneider };
427dbc4dedaSKarl-Heinz Schneider MODULE_DEVICE_TABLE(of, sbsm_dt_ids);
428dbc4dedaSKarl-Heinz Schneider #endif
429dbc4dedaSKarl-Heinz Schneider 
430dbc4dedaSKarl-Heinz Schneider static struct i2c_driver sbsm_driver = {
431dbc4dedaSKarl-Heinz Schneider 	.driver = {
432dbc4dedaSKarl-Heinz Schneider 		.name = "sbsm",
433dbc4dedaSKarl-Heinz Schneider 		.of_match_table = of_match_ptr(sbsm_dt_ids),
434dbc4dedaSKarl-Heinz Schneider 	},
435dbc4dedaSKarl-Heinz Schneider 	.probe		= sbsm_probe,
436dbc4dedaSKarl-Heinz Schneider 	.remove		= sbsm_remove,
437*a0b8839eSPhil Reid 	.alert		= sbsm_alert,
438dbc4dedaSKarl-Heinz Schneider 	.id_table	= sbsm_ids
439dbc4dedaSKarl-Heinz Schneider };
440dbc4dedaSKarl-Heinz Schneider module_i2c_driver(sbsm_driver);
441dbc4dedaSKarl-Heinz Schneider 
442dbc4dedaSKarl-Heinz Schneider MODULE_LICENSE("GPL");
443dbc4dedaSKarl-Heinz Schneider MODULE_AUTHOR("Karl-Heinz Schneider <karl-heinz@schneider-inet.de>");
444dbc4dedaSKarl-Heinz Schneider MODULE_DESCRIPTION("SBSM Smart Battery System Manager");
445