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