18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for SBS compliant Smart Battery System Managers
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * The device communicates via i2c at address 0x0a and multiplexes access to up
68c2ecf20Sopenharmony_ci * to four smart batteries at address 0x0b.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Via sysfs interface the online state and charge type are presented.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Datasheet SBSM:    http://sbs-forum.org/specs/sbsm100b.pdf
118c2ecf20Sopenharmony_ci * Datasheet LTC1760: http://cds.linear.com/docs/en/datasheet/1760fb.pdf
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Karl-Heinz Schneider <karl-heinz@schneider-inet.de>
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/gpio.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/i2c.h>
198c2ecf20Sopenharmony_ci#include <linux/i2c-mux.h>
208c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
218c2ecf20Sopenharmony_ci#include <linux/property.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define SBSM_MAX_BATS  4
248c2ecf20Sopenharmony_ci#define SBSM_RETRY_CNT 3
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* registers addresses */
278c2ecf20Sopenharmony_ci#define SBSM_CMD_BATSYSSTATE     0x01
288c2ecf20Sopenharmony_ci#define SBSM_CMD_BATSYSSTATECONT 0x02
298c2ecf20Sopenharmony_ci#define SBSM_CMD_BATSYSINFO      0x04
308c2ecf20Sopenharmony_ci#define SBSM_CMD_LTC             0x3c
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define SBSM_MASK_BAT_SUPPORTED  GENMASK(3, 0)
338c2ecf20Sopenharmony_ci#define SBSM_MASK_CHARGE_BAT     GENMASK(7, 4)
348c2ecf20Sopenharmony_ci#define SBSM_BIT_AC_PRESENT      BIT(0)
358c2ecf20Sopenharmony_ci#define SBSM_BIT_TURBO           BIT(7)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define SBSM_SMB_BAT_OFFSET      11
388c2ecf20Sopenharmony_cistruct sbsm_data {
398c2ecf20Sopenharmony_ci	struct i2c_client *client;
408c2ecf20Sopenharmony_ci	struct i2c_mux_core *muxc;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	struct power_supply *psy;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	u8 cur_chan;          /* currently selected channel */
458c2ecf20Sopenharmony_ci	struct gpio_chip chip;
468c2ecf20Sopenharmony_ci	bool is_ltc1760;      /* special capabilities */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	unsigned int supported_bats;
498c2ecf20Sopenharmony_ci	unsigned int last_state;
508c2ecf20Sopenharmony_ci	unsigned int last_state_cont;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic enum power_supply_property sbsm_props[] = {
548c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
558c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_TYPE,
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int sbsm_read_word(struct i2c_client *client, u8 address)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	int reg, retries;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	for (retries = SBSM_RETRY_CNT; retries > 0; retries--) {
638c2ecf20Sopenharmony_ci		reg = i2c_smbus_read_word_data(client, address);
648c2ecf20Sopenharmony_ci		if (reg >= 0)
658c2ecf20Sopenharmony_ci			break;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (reg < 0) {
698c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to read register 0x%02x\n",
708c2ecf20Sopenharmony_ci			address);
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return reg;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int sbsm_write_word(struct i2c_client *client, u8 address, u16 word)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	int ret, retries;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	for (retries = SBSM_RETRY_CNT; retries > 0; retries--) {
818c2ecf20Sopenharmony_ci		ret = i2c_smbus_write_word_data(client, address, word);
828c2ecf20Sopenharmony_ci		if (ret >= 0)
838c2ecf20Sopenharmony_ci			break;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci	if (ret < 0)
868c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to write to register 0x%02x\n",
878c2ecf20Sopenharmony_ci			address);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return ret;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int sbsm_get_property(struct power_supply *psy,
938c2ecf20Sopenharmony_ci			     enum power_supply_property psp,
948c2ecf20Sopenharmony_ci			     union power_supply_propval *val)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct sbsm_data *data = power_supply_get_drvdata(psy);
978c2ecf20Sopenharmony_ci	int regval = 0;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	switch (psp) {
1008c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
1018c2ecf20Sopenharmony_ci		regval = sbsm_read_word(data->client, SBSM_CMD_BATSYSSTATECONT);
1028c2ecf20Sopenharmony_ci		if (regval < 0)
1038c2ecf20Sopenharmony_ci			return regval;
1048c2ecf20Sopenharmony_ci		val->intval = !!(regval & SBSM_BIT_AC_PRESENT);
1058c2ecf20Sopenharmony_ci		break;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_TYPE:
1088c2ecf20Sopenharmony_ci		regval = sbsm_read_word(data->client, SBSM_CMD_BATSYSSTATE);
1098c2ecf20Sopenharmony_ci		if (regval < 0)
1108c2ecf20Sopenharmony_ci			return regval;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		if ((regval & SBSM_MASK_CHARGE_BAT) == 0) {
1138c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
1148c2ecf20Sopenharmony_ci			return 0;
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci		val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		if (data->is_ltc1760) {
1198c2ecf20Sopenharmony_ci			/* charge mode fast if turbo is active */
1208c2ecf20Sopenharmony_ci			regval = sbsm_read_word(data->client, SBSM_CMD_LTC);
1218c2ecf20Sopenharmony_ci			if (regval < 0)
1228c2ecf20Sopenharmony_ci				return regval;
1238c2ecf20Sopenharmony_ci			else if (regval & SBSM_BIT_TURBO)
1248c2ecf20Sopenharmony_ci				val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	default:
1298c2ecf20Sopenharmony_ci		return -EINVAL;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int sbsm_prop_is_writeable(struct power_supply *psy,
1368c2ecf20Sopenharmony_ci				  enum power_supply_property psp)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct sbsm_data *data = power_supply_get_drvdata(psy);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return (psp == POWER_SUPPLY_PROP_CHARGE_TYPE) && data->is_ltc1760;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic int sbsm_set_property(struct power_supply *psy,
1448c2ecf20Sopenharmony_ci			     enum power_supply_property psp,
1458c2ecf20Sopenharmony_ci			     const union power_supply_propval *val)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct sbsm_data *data = power_supply_get_drvdata(psy);
1488c2ecf20Sopenharmony_ci	int ret = -EINVAL;
1498c2ecf20Sopenharmony_ci	u16 regval;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	switch (psp) {
1528c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_TYPE:
1538c2ecf20Sopenharmony_ci		/* write 1 to TURBO if type fast is given */
1548c2ecf20Sopenharmony_ci		if (!data->is_ltc1760)
1558c2ecf20Sopenharmony_ci			break;
1568c2ecf20Sopenharmony_ci		regval = val->intval ==
1578c2ecf20Sopenharmony_ci			 POWER_SUPPLY_CHARGE_TYPE_FAST ? SBSM_BIT_TURBO : 0;
1588c2ecf20Sopenharmony_ci		ret = sbsm_write_word(data->client, SBSM_CMD_LTC, regval);
1598c2ecf20Sopenharmony_ci		break;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	default:
1628c2ecf20Sopenharmony_ci		break;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return ret;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/*
1698c2ecf20Sopenharmony_ci * Switch to battery
1708c2ecf20Sopenharmony_ci * Parameter chan is directly the content of SMB_BAT* nibble
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cistatic int sbsm_select(struct i2c_mux_core *muxc, u32 chan)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct sbsm_data *data = i2c_mux_priv(muxc);
1758c2ecf20Sopenharmony_ci	struct device *dev = &data->client->dev;
1768c2ecf20Sopenharmony_ci	int ret = 0;
1778c2ecf20Sopenharmony_ci	u16 reg;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (data->cur_chan == chan)
1808c2ecf20Sopenharmony_ci		return ret;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* chan goes from 1 ... 4 */
1838c2ecf20Sopenharmony_ci	reg = BIT(SBSM_SMB_BAT_OFFSET + chan);
1848c2ecf20Sopenharmony_ci	ret = sbsm_write_word(data->client, SBSM_CMD_BATSYSSTATE, reg);
1858c2ecf20Sopenharmony_ci	if (ret)
1868c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to select channel %i\n", chan);
1878c2ecf20Sopenharmony_ci	else
1888c2ecf20Sopenharmony_ci		data->cur_chan = chan;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return ret;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int sbsm_gpio_get_value(struct gpio_chip *gc, unsigned int off)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct sbsm_data *data = gpiochip_get_data(gc);
1968c2ecf20Sopenharmony_ci	int ret;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	ret = sbsm_read_word(data->client, SBSM_CMD_BATSYSSTATE);
1998c2ecf20Sopenharmony_ci	if (ret < 0)
2008c2ecf20Sopenharmony_ci		return ret;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return ret & BIT(off);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/*
2068c2ecf20Sopenharmony_ci * This needs to be defined or the GPIO lib fails to register the pin.
2078c2ecf20Sopenharmony_ci * But the 'gpio' is always an input.
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_cistatic int sbsm_gpio_direction_input(struct gpio_chip *gc, unsigned int off)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	return 0;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int sbsm_do_alert(struct device *dev, void *d)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct i2c_client *client = i2c_verify_client(dev);
2178c2ecf20Sopenharmony_ci	struct i2c_driver *driver;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (!client || client->addr != 0x0b)
2208c2ecf20Sopenharmony_ci		return 0;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	device_lock(dev);
2238c2ecf20Sopenharmony_ci	if (client->dev.driver) {
2248c2ecf20Sopenharmony_ci		driver = to_i2c_driver(client->dev.driver);
2258c2ecf20Sopenharmony_ci		if (driver->alert)
2268c2ecf20Sopenharmony_ci			driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT, 0);
2278c2ecf20Sopenharmony_ci		else
2288c2ecf20Sopenharmony_ci			dev_warn(&client->dev, "no driver alert()!\n");
2298c2ecf20Sopenharmony_ci	} else {
2308c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "alert with no driver\n");
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci	device_unlock(dev);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return -EBUSY;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void sbsm_alert(struct i2c_client *client, enum i2c_alert_protocol prot,
2388c2ecf20Sopenharmony_ci		       unsigned int d)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct sbsm_data *sbsm = i2c_get_clientdata(client);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	int ret, i, irq_bat = 0, state = 0;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	ret = sbsm_read_word(sbsm->client, SBSM_CMD_BATSYSSTATE);
2458c2ecf20Sopenharmony_ci	if (ret >= 0) {
2468c2ecf20Sopenharmony_ci		irq_bat = ret ^ sbsm->last_state;
2478c2ecf20Sopenharmony_ci		sbsm->last_state = ret;
2488c2ecf20Sopenharmony_ci		state = ret;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	ret = sbsm_read_word(sbsm->client, SBSM_CMD_BATSYSSTATECONT);
2528c2ecf20Sopenharmony_ci	if ((ret >= 0) &&
2538c2ecf20Sopenharmony_ci	    ((ret ^ sbsm->last_state_cont) & SBSM_BIT_AC_PRESENT)) {
2548c2ecf20Sopenharmony_ci		irq_bat |= sbsm->supported_bats & state;
2558c2ecf20Sopenharmony_ci		power_supply_changed(sbsm->psy);
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci	sbsm->last_state_cont = ret;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	for (i = 0; i < SBSM_MAX_BATS; i++) {
2608c2ecf20Sopenharmony_ci		if (irq_bat & BIT(i)) {
2618c2ecf20Sopenharmony_ci			device_for_each_child(&sbsm->muxc->adapter[i]->dev,
2628c2ecf20Sopenharmony_ci					      NULL, sbsm_do_alert);
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int sbsm_gpio_setup(struct sbsm_data *data)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct gpio_chip *gc = &data->chip;
2708c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
2718c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
2728c2ecf20Sopenharmony_ci	int ret;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (!device_property_present(dev, "gpio-controller"))
2758c2ecf20Sopenharmony_ci		return 0;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	ret  = sbsm_read_word(client, SBSM_CMD_BATSYSSTATE);
2788c2ecf20Sopenharmony_ci	if (ret < 0)
2798c2ecf20Sopenharmony_ci		return ret;
2808c2ecf20Sopenharmony_ci	data->last_state = ret;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	ret  = sbsm_read_word(client, SBSM_CMD_BATSYSSTATECONT);
2838c2ecf20Sopenharmony_ci	if (ret < 0)
2848c2ecf20Sopenharmony_ci		return ret;
2858c2ecf20Sopenharmony_ci	data->last_state_cont = ret;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	gc->get = sbsm_gpio_get_value;
2888c2ecf20Sopenharmony_ci	gc->direction_input  = sbsm_gpio_direction_input;
2898c2ecf20Sopenharmony_ci	gc->can_sleep = true;
2908c2ecf20Sopenharmony_ci	gc->base = -1;
2918c2ecf20Sopenharmony_ci	gc->ngpio = SBSM_MAX_BATS;
2928c2ecf20Sopenharmony_ci	gc->label = client->name;
2938c2ecf20Sopenharmony_ci	gc->parent = dev;
2948c2ecf20Sopenharmony_ci	gc->owner = THIS_MODULE;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(dev, gc, data);
2978c2ecf20Sopenharmony_ci	if (ret) {
2988c2ecf20Sopenharmony_ci		dev_err(dev, "devm_gpiochip_add_data failed: %d\n", ret);
2998c2ecf20Sopenharmony_ci		return ret;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	return ret;
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic const struct power_supply_desc sbsm_default_psy_desc = {
3068c2ecf20Sopenharmony_ci	.type = POWER_SUPPLY_TYPE_MAINS,
3078c2ecf20Sopenharmony_ci	.properties = sbsm_props,
3088c2ecf20Sopenharmony_ci	.num_properties = ARRAY_SIZE(sbsm_props),
3098c2ecf20Sopenharmony_ci	.get_property = &sbsm_get_property,
3108c2ecf20Sopenharmony_ci	.set_property = &sbsm_set_property,
3118c2ecf20Sopenharmony_ci	.property_is_writeable = &sbsm_prop_is_writeable,
3128c2ecf20Sopenharmony_ci};
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic int sbsm_probe(struct i2c_client *client,
3158c2ecf20Sopenharmony_ci		      const struct i2c_device_id *id)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
3188c2ecf20Sopenharmony_ci	struct sbsm_data *data;
3198c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
3208c2ecf20Sopenharmony_ci	struct power_supply_desc *psy_desc;
3218c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = {};
3228c2ecf20Sopenharmony_ci	int ret = 0, i;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/* Device listens only at address 0x0a */
3258c2ecf20Sopenharmony_ci	if (client->addr != 0x0a)
3268c2ecf20Sopenharmony_ci		return -EINVAL;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
3298c2ecf20Sopenharmony_ci		return -EPFNOSUPPORT;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
3328c2ecf20Sopenharmony_ci	if (!data)
3338c2ecf20Sopenharmony_ci		return -ENOMEM;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, data);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	data->client = client;
3388c2ecf20Sopenharmony_ci	data->is_ltc1760 = !!strstr(id->name, "ltc1760");
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	ret  = sbsm_read_word(client, SBSM_CMD_BATSYSINFO);
3418c2ecf20Sopenharmony_ci	if (ret < 0)
3428c2ecf20Sopenharmony_ci		return ret;
3438c2ecf20Sopenharmony_ci	data->supported_bats = ret & SBSM_MASK_BAT_SUPPORTED;
3448c2ecf20Sopenharmony_ci	data->muxc = i2c_mux_alloc(adapter, dev, SBSM_MAX_BATS, 0,
3458c2ecf20Sopenharmony_ci				   I2C_MUX_LOCKED, &sbsm_select, NULL);
3468c2ecf20Sopenharmony_ci	if (!data->muxc) {
3478c2ecf20Sopenharmony_ci		dev_err(dev, "failed to alloc i2c mux\n");
3488c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3498c2ecf20Sopenharmony_ci		goto err_mux_alloc;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci	data->muxc->priv = data;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* register muxed i2c channels. One for each supported battery */
3548c2ecf20Sopenharmony_ci	for (i = 0; i < SBSM_MAX_BATS; ++i) {
3558c2ecf20Sopenharmony_ci		if (data->supported_bats & BIT(i)) {
3568c2ecf20Sopenharmony_ci			ret = i2c_mux_add_adapter(data->muxc, 0, i + 1, 0);
3578c2ecf20Sopenharmony_ci			if (ret)
3588c2ecf20Sopenharmony_ci				break;
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci	if (ret) {
3628c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register i2c mux channel %d\n", i + 1);
3638c2ecf20Sopenharmony_ci		goto err_mux_register;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	psy_desc = devm_kmemdup(dev, &sbsm_default_psy_desc,
3678c2ecf20Sopenharmony_ci				sizeof(struct power_supply_desc),
3688c2ecf20Sopenharmony_ci				GFP_KERNEL);
3698c2ecf20Sopenharmony_ci	if (!psy_desc) {
3708c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3718c2ecf20Sopenharmony_ci		goto err_psy;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	psy_desc->name = devm_kasprintf(dev, GFP_KERNEL, "sbsm-%s",
3758c2ecf20Sopenharmony_ci					dev_name(&client->dev));
3768c2ecf20Sopenharmony_ci	if (!psy_desc->name) {
3778c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3788c2ecf20Sopenharmony_ci		goto err_psy;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci	ret = sbsm_gpio_setup(data);
3818c2ecf20Sopenharmony_ci	if (ret < 0)
3828c2ecf20Sopenharmony_ci		goto err_psy;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	psy_cfg.drv_data = data;
3858c2ecf20Sopenharmony_ci	psy_cfg.of_node = dev->of_node;
3868c2ecf20Sopenharmony_ci	data->psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
3878c2ecf20Sopenharmony_ci	if (IS_ERR(data->psy)) {
3888c2ecf20Sopenharmony_ci		ret = PTR_ERR(data->psy);
3898c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register power supply %s\n",
3908c2ecf20Sopenharmony_ci			psy_desc->name);
3918c2ecf20Sopenharmony_ci		goto err_psy;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cierr_psy:
3978c2ecf20Sopenharmony_cierr_mux_register:
3988c2ecf20Sopenharmony_ci	i2c_mux_del_adapters(data->muxc);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cierr_mux_alloc:
4018c2ecf20Sopenharmony_ci	return ret;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int sbsm_remove(struct i2c_client *client)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct sbsm_data *data = i2c_get_clientdata(client);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	i2c_mux_del_adapters(data->muxc);
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic const struct i2c_device_id sbsm_ids[] = {
4138c2ecf20Sopenharmony_ci	{ "sbs-manager", 0 },
4148c2ecf20Sopenharmony_ci	{ "ltc1760",     0 },
4158c2ecf20Sopenharmony_ci	{ }
4168c2ecf20Sopenharmony_ci};
4178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, sbsm_ids);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
4208c2ecf20Sopenharmony_cistatic const struct of_device_id sbsm_dt_ids[] = {
4218c2ecf20Sopenharmony_ci	{ .compatible = "sbs,sbs-manager" },
4228c2ecf20Sopenharmony_ci	{ .compatible = "lltc,ltc1760" },
4238c2ecf20Sopenharmony_ci	{ }
4248c2ecf20Sopenharmony_ci};
4258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sbsm_dt_ids);
4268c2ecf20Sopenharmony_ci#endif
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic struct i2c_driver sbsm_driver = {
4298c2ecf20Sopenharmony_ci	.driver = {
4308c2ecf20Sopenharmony_ci		.name = "sbsm",
4318c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(sbsm_dt_ids),
4328c2ecf20Sopenharmony_ci	},
4338c2ecf20Sopenharmony_ci	.probe		= sbsm_probe,
4348c2ecf20Sopenharmony_ci	.remove		= sbsm_remove,
4358c2ecf20Sopenharmony_ci	.alert		= sbsm_alert,
4368c2ecf20Sopenharmony_ci	.id_table	= sbsm_ids
4378c2ecf20Sopenharmony_ci};
4388c2ecf20Sopenharmony_cimodule_i2c_driver(sbsm_driver);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Karl-Heinz Schneider <karl-heinz@schneider-inet.de>");
4428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SBSM Smart Battery System Manager");
443