xref: /linux/drivers/power/supply/sbs-manager.c (revision dbc4deda03fe61a1c29d8218269714bf2c334b9b)
1*dbc4dedaSKarl-Heinz Schneider /*
2*dbc4dedaSKarl-Heinz Schneider  * Driver for SBS compliant Smart Battery System Managers
3*dbc4dedaSKarl-Heinz Schneider  *
4*dbc4dedaSKarl-Heinz Schneider  * The device communicates via i2c at address 0x0a and multiplexes access to up
5*dbc4dedaSKarl-Heinz Schneider  * to four smart batteries at address 0x0b.
6*dbc4dedaSKarl-Heinz Schneider  *
7*dbc4dedaSKarl-Heinz Schneider  * Via sysfs interface the online state and charge type are presented.
8*dbc4dedaSKarl-Heinz Schneider  *
9*dbc4dedaSKarl-Heinz Schneider  * Datasheet SBSM:    http://sbs-forum.org/specs/sbsm100b.pdf
10*dbc4dedaSKarl-Heinz Schneider  * Datasheet LTC1760: http://cds.linear.com/docs/en/datasheet/1760fb.pdf
11*dbc4dedaSKarl-Heinz Schneider  *
12*dbc4dedaSKarl-Heinz Schneider  * Karl-Heinz Schneider <karl-heinz@schneider-inet.de>
13*dbc4dedaSKarl-Heinz Schneider  *
14*dbc4dedaSKarl-Heinz Schneider  * This program is free software; you can redistribute it and/or modify
15*dbc4dedaSKarl-Heinz Schneider  * it under the terms of the GNU General Public License version 2 as
16*dbc4dedaSKarl-Heinz Schneider  * published by the Free Software Foundation.
17*dbc4dedaSKarl-Heinz Schneider  */
18*dbc4dedaSKarl-Heinz Schneider 
19*dbc4dedaSKarl-Heinz Schneider #include <linux/module.h>
20*dbc4dedaSKarl-Heinz Schneider #include <linux/i2c.h>
21*dbc4dedaSKarl-Heinz Schneider #include <linux/i2c-mux.h>
22*dbc4dedaSKarl-Heinz Schneider #include <linux/power_supply.h>
23*dbc4dedaSKarl-Heinz Schneider 
24*dbc4dedaSKarl-Heinz Schneider #define SBSM_MAX_BATS  4
25*dbc4dedaSKarl-Heinz Schneider #define SBSM_RETRY_CNT 3
26*dbc4dedaSKarl-Heinz Schneider 
27*dbc4dedaSKarl-Heinz Schneider /* registers addresses */
28*dbc4dedaSKarl-Heinz Schneider #define SBSM_CMD_BATSYSSTATE     0x01
29*dbc4dedaSKarl-Heinz Schneider #define SBSM_CMD_BATSYSSTATECONT 0x02
30*dbc4dedaSKarl-Heinz Schneider #define SBSM_CMD_BATSYSINFO      0x04
31*dbc4dedaSKarl-Heinz Schneider #define SBSM_CMD_LTC             0x3c
32*dbc4dedaSKarl-Heinz Schneider 
33*dbc4dedaSKarl-Heinz Schneider #define SBSM_MASK_BAT_SUPPORTED  GENMASK(3, 0)
34*dbc4dedaSKarl-Heinz Schneider #define SBSM_MASK_CHARGE_BAT     GENMASK(7, 4)
35*dbc4dedaSKarl-Heinz Schneider #define SBSM_BIT_AC_PRESENT      BIT(0)
36*dbc4dedaSKarl-Heinz Schneider #define SBSM_BIT_TURBO           BIT(7)
37*dbc4dedaSKarl-Heinz Schneider 
38*dbc4dedaSKarl-Heinz Schneider #define SBSM_SMB_BAT_OFFSET      11
39*dbc4dedaSKarl-Heinz Schneider struct sbsm_data {
40*dbc4dedaSKarl-Heinz Schneider 	struct i2c_client *client;
41*dbc4dedaSKarl-Heinz Schneider 	struct i2c_mux_core *muxc;
42*dbc4dedaSKarl-Heinz Schneider 
43*dbc4dedaSKarl-Heinz Schneider 	struct power_supply *psy;
44*dbc4dedaSKarl-Heinz Schneider 
45*dbc4dedaSKarl-Heinz Schneider 	u8 cur_chan;          /* currently selected channel */
46*dbc4dedaSKarl-Heinz Schneider 	bool is_ltc1760;      /* special capabilities */
47*dbc4dedaSKarl-Heinz Schneider };
48*dbc4dedaSKarl-Heinz Schneider 
49*dbc4dedaSKarl-Heinz Schneider static enum power_supply_property sbsm_props[] = {
50*dbc4dedaSKarl-Heinz Schneider 	POWER_SUPPLY_PROP_ONLINE,
51*dbc4dedaSKarl-Heinz Schneider 	POWER_SUPPLY_PROP_CHARGE_TYPE,
52*dbc4dedaSKarl-Heinz Schneider };
53*dbc4dedaSKarl-Heinz Schneider 
54*dbc4dedaSKarl-Heinz Schneider static int sbsm_read_word(struct i2c_client *client, u8 address)
55*dbc4dedaSKarl-Heinz Schneider {
56*dbc4dedaSKarl-Heinz Schneider 	int reg, retries;
57*dbc4dedaSKarl-Heinz Schneider 
58*dbc4dedaSKarl-Heinz Schneider 	for (retries = SBSM_RETRY_CNT; retries > 0; retries--) {
59*dbc4dedaSKarl-Heinz Schneider 		reg = i2c_smbus_read_word_data(client, address);
60*dbc4dedaSKarl-Heinz Schneider 		if (reg >= 0)
61*dbc4dedaSKarl-Heinz Schneider 			break;
62*dbc4dedaSKarl-Heinz Schneider 	}
63*dbc4dedaSKarl-Heinz Schneider 
64*dbc4dedaSKarl-Heinz Schneider 	if (reg < 0) {
65*dbc4dedaSKarl-Heinz Schneider 		dev_err(&client->dev, "failed to read register 0x%02x\n",
66*dbc4dedaSKarl-Heinz Schneider 			address);
67*dbc4dedaSKarl-Heinz Schneider 	}
68*dbc4dedaSKarl-Heinz Schneider 
69*dbc4dedaSKarl-Heinz Schneider 	return reg;
70*dbc4dedaSKarl-Heinz Schneider }
71*dbc4dedaSKarl-Heinz Schneider 
72*dbc4dedaSKarl-Heinz Schneider static int sbsm_write_word(struct i2c_client *client, u8 address, u16 word)
73*dbc4dedaSKarl-Heinz Schneider {
74*dbc4dedaSKarl-Heinz Schneider 	int ret, retries;
75*dbc4dedaSKarl-Heinz Schneider 
76*dbc4dedaSKarl-Heinz Schneider 	for (retries = SBSM_RETRY_CNT; retries > 0; retries--) {
77*dbc4dedaSKarl-Heinz Schneider 		ret = i2c_smbus_write_word_data(client, address, word);
78*dbc4dedaSKarl-Heinz Schneider 		if (ret >= 0)
79*dbc4dedaSKarl-Heinz Schneider 			break;
80*dbc4dedaSKarl-Heinz Schneider 	}
81*dbc4dedaSKarl-Heinz Schneider 	if (ret < 0)
82*dbc4dedaSKarl-Heinz Schneider 		dev_err(&client->dev, "failed to write to register 0x%02x\n",
83*dbc4dedaSKarl-Heinz Schneider 			address);
84*dbc4dedaSKarl-Heinz Schneider 
85*dbc4dedaSKarl-Heinz Schneider 	return ret;
86*dbc4dedaSKarl-Heinz Schneider }
87*dbc4dedaSKarl-Heinz Schneider 
88*dbc4dedaSKarl-Heinz Schneider static int sbsm_get_property(struct power_supply *psy,
89*dbc4dedaSKarl-Heinz Schneider 			     enum power_supply_property psp,
90*dbc4dedaSKarl-Heinz Schneider 			     union power_supply_propval *val)
91*dbc4dedaSKarl-Heinz Schneider {
92*dbc4dedaSKarl-Heinz Schneider 	struct sbsm_data *data = power_supply_get_drvdata(psy);
93*dbc4dedaSKarl-Heinz Schneider 	int regval = 0;
94*dbc4dedaSKarl-Heinz Schneider 
95*dbc4dedaSKarl-Heinz Schneider 	switch (psp) {
96*dbc4dedaSKarl-Heinz Schneider 	case POWER_SUPPLY_PROP_ONLINE:
97*dbc4dedaSKarl-Heinz Schneider 		regval = sbsm_read_word(data->client, SBSM_CMD_BATSYSSTATECONT);
98*dbc4dedaSKarl-Heinz Schneider 		if (regval < 0)
99*dbc4dedaSKarl-Heinz Schneider 			return regval;
100*dbc4dedaSKarl-Heinz Schneider 		val->intval = !!(regval & SBSM_BIT_AC_PRESENT);
101*dbc4dedaSKarl-Heinz Schneider 		break;
102*dbc4dedaSKarl-Heinz Schneider 
103*dbc4dedaSKarl-Heinz Schneider 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
104*dbc4dedaSKarl-Heinz Schneider 		regval = sbsm_read_word(data->client, SBSM_CMD_BATSYSSTATE);
105*dbc4dedaSKarl-Heinz Schneider 		if (regval < 0)
106*dbc4dedaSKarl-Heinz Schneider 			return regval;
107*dbc4dedaSKarl-Heinz Schneider 
108*dbc4dedaSKarl-Heinz Schneider 		if ((regval & SBSM_MASK_CHARGE_BAT) == 0) {
109*dbc4dedaSKarl-Heinz Schneider 			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
110*dbc4dedaSKarl-Heinz Schneider 			return 0;
111*dbc4dedaSKarl-Heinz Schneider 		}
112*dbc4dedaSKarl-Heinz Schneider 		val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
113*dbc4dedaSKarl-Heinz Schneider 
114*dbc4dedaSKarl-Heinz Schneider 		if (data->is_ltc1760) {
115*dbc4dedaSKarl-Heinz Schneider 			/* charge mode fast if turbo is active */
116*dbc4dedaSKarl-Heinz Schneider 			regval = sbsm_read_word(data->client, SBSM_CMD_LTC);
117*dbc4dedaSKarl-Heinz Schneider 			if (regval < 0)
118*dbc4dedaSKarl-Heinz Schneider 				return regval;
119*dbc4dedaSKarl-Heinz Schneider 			else if (regval & SBSM_BIT_TURBO)
120*dbc4dedaSKarl-Heinz Schneider 				val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
121*dbc4dedaSKarl-Heinz Schneider 		}
122*dbc4dedaSKarl-Heinz Schneider 		break;
123*dbc4dedaSKarl-Heinz Schneider 
124*dbc4dedaSKarl-Heinz Schneider 	default:
125*dbc4dedaSKarl-Heinz Schneider 		return -EINVAL;
126*dbc4dedaSKarl-Heinz Schneider 	}
127*dbc4dedaSKarl-Heinz Schneider 
128*dbc4dedaSKarl-Heinz Schneider 	return 0;
129*dbc4dedaSKarl-Heinz Schneider }
130*dbc4dedaSKarl-Heinz Schneider 
131*dbc4dedaSKarl-Heinz Schneider static int sbsm_prop_is_writeable(struct power_supply *psy,
132*dbc4dedaSKarl-Heinz Schneider 				  enum power_supply_property psp)
133*dbc4dedaSKarl-Heinz Schneider {
134*dbc4dedaSKarl-Heinz Schneider 	struct sbsm_data *data = power_supply_get_drvdata(psy);
135*dbc4dedaSKarl-Heinz Schneider 
136*dbc4dedaSKarl-Heinz Schneider 	return (psp == POWER_SUPPLY_PROP_CHARGE_TYPE) && data->is_ltc1760;
137*dbc4dedaSKarl-Heinz Schneider }
138*dbc4dedaSKarl-Heinz Schneider 
139*dbc4dedaSKarl-Heinz Schneider static int sbsm_set_property(struct power_supply *psy,
140*dbc4dedaSKarl-Heinz Schneider 			     enum power_supply_property psp,
141*dbc4dedaSKarl-Heinz Schneider 			     const union power_supply_propval *val)
142*dbc4dedaSKarl-Heinz Schneider {
143*dbc4dedaSKarl-Heinz Schneider 	struct sbsm_data *data = power_supply_get_drvdata(psy);
144*dbc4dedaSKarl-Heinz Schneider 	int ret = -EINVAL;
145*dbc4dedaSKarl-Heinz Schneider 	u16 regval;
146*dbc4dedaSKarl-Heinz Schneider 
147*dbc4dedaSKarl-Heinz Schneider 	switch (psp) {
148*dbc4dedaSKarl-Heinz Schneider 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
149*dbc4dedaSKarl-Heinz Schneider 		/* write 1 to TURBO if type fast is given */
150*dbc4dedaSKarl-Heinz Schneider 		if (!data->is_ltc1760)
151*dbc4dedaSKarl-Heinz Schneider 			break;
152*dbc4dedaSKarl-Heinz Schneider 		regval = val->intval ==
153*dbc4dedaSKarl-Heinz Schneider 			 POWER_SUPPLY_CHARGE_TYPE_FAST ? SBSM_BIT_TURBO : 0;
154*dbc4dedaSKarl-Heinz Schneider 		ret = sbsm_write_word(data->client, SBSM_CMD_LTC, regval);
155*dbc4dedaSKarl-Heinz Schneider 		break;
156*dbc4dedaSKarl-Heinz Schneider 
157*dbc4dedaSKarl-Heinz Schneider 	default:
158*dbc4dedaSKarl-Heinz Schneider 		break;
159*dbc4dedaSKarl-Heinz Schneider 	}
160*dbc4dedaSKarl-Heinz Schneider 
161*dbc4dedaSKarl-Heinz Schneider 	return ret;
162*dbc4dedaSKarl-Heinz Schneider }
163*dbc4dedaSKarl-Heinz Schneider 
164*dbc4dedaSKarl-Heinz Schneider /*
165*dbc4dedaSKarl-Heinz Schneider  * Switch to battery
166*dbc4dedaSKarl-Heinz Schneider  * Parameter chan is directly the content of SMB_BAT* nibble
167*dbc4dedaSKarl-Heinz Schneider  */
168*dbc4dedaSKarl-Heinz Schneider static int sbsm_select(struct i2c_mux_core *muxc, u32 chan)
169*dbc4dedaSKarl-Heinz Schneider {
170*dbc4dedaSKarl-Heinz Schneider 	struct sbsm_data *data = i2c_mux_priv(muxc);
171*dbc4dedaSKarl-Heinz Schneider 	struct device *dev = &data->client->dev;
172*dbc4dedaSKarl-Heinz Schneider 	int ret = 0;
173*dbc4dedaSKarl-Heinz Schneider 	u16 reg;
174*dbc4dedaSKarl-Heinz Schneider 
175*dbc4dedaSKarl-Heinz Schneider 	if (data->cur_chan == chan)
176*dbc4dedaSKarl-Heinz Schneider 		return ret;
177*dbc4dedaSKarl-Heinz Schneider 
178*dbc4dedaSKarl-Heinz Schneider 	/* chan goes from 1 ... 4 */
179*dbc4dedaSKarl-Heinz Schneider 	reg = 1 << BIT(SBSM_SMB_BAT_OFFSET + chan);
180*dbc4dedaSKarl-Heinz Schneider 	ret = sbsm_write_word(data->client, SBSM_CMD_BATSYSSTATE, reg);
181*dbc4dedaSKarl-Heinz Schneider 	if (ret)
182*dbc4dedaSKarl-Heinz Schneider 		dev_err(dev, "Failed to select channel %i\n", chan);
183*dbc4dedaSKarl-Heinz Schneider 	else
184*dbc4dedaSKarl-Heinz Schneider 		data->cur_chan = chan;
185*dbc4dedaSKarl-Heinz Schneider 
186*dbc4dedaSKarl-Heinz Schneider 	return ret;
187*dbc4dedaSKarl-Heinz Schneider }
188*dbc4dedaSKarl-Heinz Schneider 
189*dbc4dedaSKarl-Heinz Schneider static const struct power_supply_desc sbsm_default_psy_desc = {
190*dbc4dedaSKarl-Heinz Schneider 	.type = POWER_SUPPLY_TYPE_MAINS,
191*dbc4dedaSKarl-Heinz Schneider 	.properties = sbsm_props,
192*dbc4dedaSKarl-Heinz Schneider 	.num_properties = ARRAY_SIZE(sbsm_props),
193*dbc4dedaSKarl-Heinz Schneider 	.get_property = &sbsm_get_property,
194*dbc4dedaSKarl-Heinz Schneider 	.set_property = &sbsm_set_property,
195*dbc4dedaSKarl-Heinz Schneider 	.property_is_writeable = &sbsm_prop_is_writeable,
196*dbc4dedaSKarl-Heinz Schneider };
197*dbc4dedaSKarl-Heinz Schneider 
198*dbc4dedaSKarl-Heinz Schneider static int sbsm_probe(struct i2c_client *client,
199*dbc4dedaSKarl-Heinz Schneider 		      const struct i2c_device_id *id)
200*dbc4dedaSKarl-Heinz Schneider {
201*dbc4dedaSKarl-Heinz Schneider 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
202*dbc4dedaSKarl-Heinz Schneider 	struct sbsm_data *data;
203*dbc4dedaSKarl-Heinz Schneider 	struct device *dev = &client->dev;
204*dbc4dedaSKarl-Heinz Schneider 	struct power_supply_desc *psy_desc;
205*dbc4dedaSKarl-Heinz Schneider 	struct power_supply_config psy_cfg = {};
206*dbc4dedaSKarl-Heinz Schneider 	int ret = 0, i, supported_bats;
207*dbc4dedaSKarl-Heinz Schneider 
208*dbc4dedaSKarl-Heinz Schneider 	/* Device listens only at address 0x0a */
209*dbc4dedaSKarl-Heinz Schneider 	if (client->addr != 0x0a)
210*dbc4dedaSKarl-Heinz Schneider 		return -EINVAL;
211*dbc4dedaSKarl-Heinz Schneider 
212*dbc4dedaSKarl-Heinz Schneider 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
213*dbc4dedaSKarl-Heinz Schneider 		return -EPFNOSUPPORT;
214*dbc4dedaSKarl-Heinz Schneider 
215*dbc4dedaSKarl-Heinz Schneider 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
216*dbc4dedaSKarl-Heinz Schneider 	if (!data)
217*dbc4dedaSKarl-Heinz Schneider 		return -ENOMEM;
218*dbc4dedaSKarl-Heinz Schneider 
219*dbc4dedaSKarl-Heinz Schneider 	i2c_set_clientdata(client, data);
220*dbc4dedaSKarl-Heinz Schneider 
221*dbc4dedaSKarl-Heinz Schneider 	data->client = client;
222*dbc4dedaSKarl-Heinz Schneider 	data->is_ltc1760 = !!strstr(id->name, "ltc1760");
223*dbc4dedaSKarl-Heinz Schneider 
224*dbc4dedaSKarl-Heinz Schneider 	ret  = sbsm_read_word(client, SBSM_CMD_BATSYSINFO);
225*dbc4dedaSKarl-Heinz Schneider 	if (ret < 0)
226*dbc4dedaSKarl-Heinz Schneider 		return ret;
227*dbc4dedaSKarl-Heinz Schneider 	supported_bats = ret & SBSM_MASK_BAT_SUPPORTED;
228*dbc4dedaSKarl-Heinz Schneider 
229*dbc4dedaSKarl-Heinz Schneider 	data->muxc = i2c_mux_alloc(adapter, dev, SBSM_MAX_BATS, 0,
230*dbc4dedaSKarl-Heinz Schneider 				   I2C_MUX_LOCKED, &sbsm_select, NULL);
231*dbc4dedaSKarl-Heinz Schneider 	if (!data->muxc) {
232*dbc4dedaSKarl-Heinz Schneider 		dev_err(dev, "failed to alloc i2c mux\n");
233*dbc4dedaSKarl-Heinz Schneider 		ret = -ENOMEM;
234*dbc4dedaSKarl-Heinz Schneider 		goto err_mux_alloc;
235*dbc4dedaSKarl-Heinz Schneider 	}
236*dbc4dedaSKarl-Heinz Schneider 	data->muxc->priv = data;
237*dbc4dedaSKarl-Heinz Schneider 
238*dbc4dedaSKarl-Heinz Schneider 	/* register muxed i2c channels. One for each supported battery */
239*dbc4dedaSKarl-Heinz Schneider 	for (i = 0; i < SBSM_MAX_BATS; ++i) {
240*dbc4dedaSKarl-Heinz Schneider 		if (supported_bats & BIT(i)) {
241*dbc4dedaSKarl-Heinz Schneider 			ret = i2c_mux_add_adapter(data->muxc, 0, i + 1, 0);
242*dbc4dedaSKarl-Heinz Schneider 			if (ret)
243*dbc4dedaSKarl-Heinz Schneider 				break;
244*dbc4dedaSKarl-Heinz Schneider 		}
245*dbc4dedaSKarl-Heinz Schneider 	}
246*dbc4dedaSKarl-Heinz Schneider 	if (ret) {
247*dbc4dedaSKarl-Heinz Schneider 		dev_err(dev, "failed to register i2c mux channel %d\n", i + 1);
248*dbc4dedaSKarl-Heinz Schneider 		goto err_mux_register;
249*dbc4dedaSKarl-Heinz Schneider 	}
250*dbc4dedaSKarl-Heinz Schneider 
251*dbc4dedaSKarl-Heinz Schneider 	psy_desc = devm_kmemdup(dev, &sbsm_default_psy_desc,
252*dbc4dedaSKarl-Heinz Schneider 				sizeof(struct power_supply_desc),
253*dbc4dedaSKarl-Heinz Schneider 				GFP_KERNEL);
254*dbc4dedaSKarl-Heinz Schneider 	if (!psy_desc) {
255*dbc4dedaSKarl-Heinz Schneider 		ret = -ENOMEM;
256*dbc4dedaSKarl-Heinz Schneider 		goto err_psy;
257*dbc4dedaSKarl-Heinz Schneider 	}
258*dbc4dedaSKarl-Heinz Schneider 
259*dbc4dedaSKarl-Heinz Schneider 	psy_desc->name = devm_kasprintf(dev, GFP_KERNEL, "sbsm-%s",
260*dbc4dedaSKarl-Heinz Schneider 					dev_name(&client->dev));
261*dbc4dedaSKarl-Heinz Schneider 	if (!psy_desc->name) {
262*dbc4dedaSKarl-Heinz Schneider 		ret = -ENOMEM;
263*dbc4dedaSKarl-Heinz Schneider 		goto err_psy;
264*dbc4dedaSKarl-Heinz Schneider 	}
265*dbc4dedaSKarl-Heinz Schneider 
266*dbc4dedaSKarl-Heinz Schneider 	psy_cfg.drv_data = data;
267*dbc4dedaSKarl-Heinz Schneider 	psy_cfg.of_node = dev->of_node;
268*dbc4dedaSKarl-Heinz Schneider 	data->psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
269*dbc4dedaSKarl-Heinz Schneider 	if (IS_ERR(data->psy)) {
270*dbc4dedaSKarl-Heinz Schneider 		ret = PTR_ERR(data->psy);
271*dbc4dedaSKarl-Heinz Schneider 		dev_err(dev, "failed to register power supply %s\n",
272*dbc4dedaSKarl-Heinz Schneider 			psy_desc->name);
273*dbc4dedaSKarl-Heinz Schneider 		goto err_psy;
274*dbc4dedaSKarl-Heinz Schneider 	}
275*dbc4dedaSKarl-Heinz Schneider 
276*dbc4dedaSKarl-Heinz Schneider 	return 0;
277*dbc4dedaSKarl-Heinz Schneider 
278*dbc4dedaSKarl-Heinz Schneider err_psy:
279*dbc4dedaSKarl-Heinz Schneider err_mux_register:
280*dbc4dedaSKarl-Heinz Schneider 	i2c_mux_del_adapters(data->muxc);
281*dbc4dedaSKarl-Heinz Schneider 
282*dbc4dedaSKarl-Heinz Schneider err_mux_alloc:
283*dbc4dedaSKarl-Heinz Schneider 	return ret;
284*dbc4dedaSKarl-Heinz Schneider }
285*dbc4dedaSKarl-Heinz Schneider 
286*dbc4dedaSKarl-Heinz Schneider static int sbsm_remove(struct i2c_client *client)
287*dbc4dedaSKarl-Heinz Schneider {
288*dbc4dedaSKarl-Heinz Schneider 	struct sbsm_data *data = i2c_get_clientdata(client);
289*dbc4dedaSKarl-Heinz Schneider 
290*dbc4dedaSKarl-Heinz Schneider 	i2c_mux_del_adapters(data->muxc);
291*dbc4dedaSKarl-Heinz Schneider 	return 0;
292*dbc4dedaSKarl-Heinz Schneider }
293*dbc4dedaSKarl-Heinz Schneider 
294*dbc4dedaSKarl-Heinz Schneider static const struct i2c_device_id sbsm_ids[] = {
295*dbc4dedaSKarl-Heinz Schneider 	{ "sbs-manager", 0 },
296*dbc4dedaSKarl-Heinz Schneider 	{ "ltc1760",     0 },
297*dbc4dedaSKarl-Heinz Schneider 	{ }
298*dbc4dedaSKarl-Heinz Schneider };
299*dbc4dedaSKarl-Heinz Schneider MODULE_DEVICE_TABLE(i2c, sbsm_ids);
300*dbc4dedaSKarl-Heinz Schneider 
301*dbc4dedaSKarl-Heinz Schneider #ifdef CONFIG_OF
302*dbc4dedaSKarl-Heinz Schneider static const struct of_device_id sbsm_dt_ids[] = {
303*dbc4dedaSKarl-Heinz Schneider 	{ .compatible = "sbs,sbs-manager" },
304*dbc4dedaSKarl-Heinz Schneider 	{ .compatible = "lltc,ltc1760" },
305*dbc4dedaSKarl-Heinz Schneider 	{ }
306*dbc4dedaSKarl-Heinz Schneider };
307*dbc4dedaSKarl-Heinz Schneider MODULE_DEVICE_TABLE(of, sbsm_dt_ids);
308*dbc4dedaSKarl-Heinz Schneider #endif
309*dbc4dedaSKarl-Heinz Schneider 
310*dbc4dedaSKarl-Heinz Schneider static struct i2c_driver sbsm_driver = {
311*dbc4dedaSKarl-Heinz Schneider 	.driver = {
312*dbc4dedaSKarl-Heinz Schneider 		.name = "sbsm",
313*dbc4dedaSKarl-Heinz Schneider 		.of_match_table = of_match_ptr(sbsm_dt_ids),
314*dbc4dedaSKarl-Heinz Schneider 	},
315*dbc4dedaSKarl-Heinz Schneider 	.probe		= sbsm_probe,
316*dbc4dedaSKarl-Heinz Schneider 	.remove		= sbsm_remove,
317*dbc4dedaSKarl-Heinz Schneider 	.id_table	= sbsm_ids
318*dbc4dedaSKarl-Heinz Schneider };
319*dbc4dedaSKarl-Heinz Schneider module_i2c_driver(sbsm_driver);
320*dbc4dedaSKarl-Heinz Schneider 
321*dbc4dedaSKarl-Heinz Schneider MODULE_LICENSE("GPL");
322*dbc4dedaSKarl-Heinz Schneider MODULE_AUTHOR("Karl-Heinz Schneider <karl-heinz@schneider-inet.de>");
323*dbc4dedaSKarl-Heinz Schneider MODULE_DESCRIPTION("SBSM Smart Battery System Manager");
324