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)) {
361dbc4dedaSKarl-Heinz Schneider 			ret = i2c_mux_add_adapter(data->muxc, 0, i + 1, 0);
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[] = {
392dbc4dedaSKarl-Heinz Schneider 	{ "sbs-manager", 0 },
393dbc4dedaSKarl-Heinz Schneider 	{ "ltc1760",     0 },
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 	},
412*fe20b1dcSUwe 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