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 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 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 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 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 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 */ 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 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 */ 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 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 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 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 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 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)) { 361068a95efSWolfram Sang ret = i2c_mux_add_adapter(data->muxc, 0, i + 1); 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[] = { 392*ebacfa1fSUwe Kleine-König { "sbs-manager" }, 393*ebacfa1fSUwe Kleine-König { "ltc1760" }, 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 }, 412fe20b1dcSUwe 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