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