linux/drivers/power/supply/sbs-manager.c
<<
>>
Prefs
   1/*
   2 * Driver for SBS compliant Smart Battery System Managers
   3 *
   4 * The device communicates via i2c at address 0x0a and multiplexes access to up
   5 * to four smart batteries at address 0x0b.
   6 *
   7 * Via sysfs interface the online state and charge type are presented.
   8 *
   9 * Datasheet SBSM:    http://sbs-forum.org/specs/sbsm100b.pdf
  10 * Datasheet LTC1760: http://cds.linear.com/docs/en/datasheet/1760fb.pdf
  11 *
  12 * Karl-Heinz Schneider <karl-heinz@schneider-inet.de>
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License version 2 as
  16 * published by the Free Software Foundation.
  17 */
  18
  19#include <linux/gpio.h>
  20#include <linux/module.h>
  21#include <linux/i2c.h>
  22#include <linux/i2c-mux.h>
  23#include <linux/power_supply.h>
  24#include <linux/property.h>
  25
  26#define SBSM_MAX_BATS  4
  27#define SBSM_RETRY_CNT 3
  28
  29/* registers addresses */
  30#define SBSM_CMD_BATSYSSTATE     0x01
  31#define SBSM_CMD_BATSYSSTATECONT 0x02
  32#define SBSM_CMD_BATSYSINFO      0x04
  33#define SBSM_CMD_LTC             0x3c
  34
  35#define SBSM_MASK_BAT_SUPPORTED  GENMASK(3, 0)
  36#define SBSM_MASK_CHARGE_BAT     GENMASK(7, 4)
  37#define SBSM_BIT_AC_PRESENT      BIT(0)
  38#define SBSM_BIT_TURBO           BIT(7)
  39
  40#define SBSM_SMB_BAT_OFFSET      11
  41struct sbsm_data {
  42        struct i2c_client *client;
  43        struct i2c_mux_core *muxc;
  44
  45        struct power_supply *psy;
  46
  47        u8 cur_chan;          /* currently selected channel */
  48        struct gpio_chip chip;
  49        bool is_ltc1760;      /* special capabilities */
  50
  51        unsigned int supported_bats;
  52        unsigned int last_state;
  53        unsigned int last_state_cont;
  54};
  55
  56static enum power_supply_property sbsm_props[] = {
  57        POWER_SUPPLY_PROP_ONLINE,
  58        POWER_SUPPLY_PROP_CHARGE_TYPE,
  59};
  60
  61static int sbsm_read_word(struct i2c_client *client, u8 address)
  62{
  63        int reg, retries;
  64
  65        for (retries = SBSM_RETRY_CNT; retries > 0; retries--) {
  66                reg = i2c_smbus_read_word_data(client, address);
  67                if (reg >= 0)
  68                        break;
  69        }
  70
  71        if (reg < 0) {
  72                dev_err(&client->dev, "failed to read register 0x%02x\n",
  73                        address);
  74        }
  75
  76        return reg;
  77}
  78
  79static int sbsm_write_word(struct i2c_client *client, u8 address, u16 word)
  80{
  81        int ret, retries;
  82
  83        for (retries = SBSM_RETRY_CNT; retries > 0; retries--) {
  84                ret = i2c_smbus_write_word_data(client, address, word);
  85                if (ret >= 0)
  86                        break;
  87        }
  88        if (ret < 0)
  89                dev_err(&client->dev, "failed to write to register 0x%02x\n",
  90                        address);
  91
  92        return ret;
  93}
  94
  95static int sbsm_get_property(struct power_supply *psy,
  96                             enum power_supply_property psp,
  97                             union power_supply_propval *val)
  98{
  99        struct sbsm_data *data = power_supply_get_drvdata(psy);
 100        int regval = 0;
 101
 102        switch (psp) {
 103        case POWER_SUPPLY_PROP_ONLINE:
 104                regval = sbsm_read_word(data->client, SBSM_CMD_BATSYSSTATECONT);
 105                if (regval < 0)
 106                        return regval;
 107                val->intval = !!(regval & SBSM_BIT_AC_PRESENT);
 108                break;
 109
 110        case POWER_SUPPLY_PROP_CHARGE_TYPE:
 111                regval = sbsm_read_word(data->client, SBSM_CMD_BATSYSSTATE);
 112                if (regval < 0)
 113                        return regval;
 114
 115                if ((regval & SBSM_MASK_CHARGE_BAT) == 0) {
 116                        val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
 117                        return 0;
 118                }
 119                val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
 120
 121                if (data->is_ltc1760) {
 122                        /* charge mode fast if turbo is active */
 123                        regval = sbsm_read_word(data->client, SBSM_CMD_LTC);
 124                        if (regval < 0)
 125                                return regval;
 126                        else if (regval & SBSM_BIT_TURBO)
 127                                val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
 128                }
 129                break;
 130
 131        default:
 132                return -EINVAL;
 133        }
 134
 135        return 0;
 136}
 137
 138static int sbsm_prop_is_writeable(struct power_supply *psy,
 139                                  enum power_supply_property psp)
 140{
 141        struct sbsm_data *data = power_supply_get_drvdata(psy);
 142
 143        return (psp == POWER_SUPPLY_PROP_CHARGE_TYPE) && data->is_ltc1760;
 144}
 145
 146static int sbsm_set_property(struct power_supply *psy,
 147                             enum power_supply_property psp,
 148                             const union power_supply_propval *val)
 149{
 150        struct sbsm_data *data = power_supply_get_drvdata(psy);
 151        int ret = -EINVAL;
 152        u16 regval;
 153
 154        switch (psp) {
 155        case POWER_SUPPLY_PROP_CHARGE_TYPE:
 156                /* write 1 to TURBO if type fast is given */
 157                if (!data->is_ltc1760)
 158                        break;
 159                regval = val->intval ==
 160                         POWER_SUPPLY_CHARGE_TYPE_FAST ? SBSM_BIT_TURBO : 0;
 161                ret = sbsm_write_word(data->client, SBSM_CMD_LTC, regval);
 162                break;
 163
 164        default:
 165                break;
 166        }
 167
 168        return ret;
 169}
 170
 171/*
 172 * Switch to battery
 173 * Parameter chan is directly the content of SMB_BAT* nibble
 174 */
 175static int sbsm_select(struct i2c_mux_core *muxc, u32 chan)
 176{
 177        struct sbsm_data *data = i2c_mux_priv(muxc);
 178        struct device *dev = &data->client->dev;
 179        int ret = 0;
 180        u16 reg;
 181
 182        if (data->cur_chan == chan)
 183                return ret;
 184
 185        /* chan goes from 1 ... 4 */
 186        reg = BIT(SBSM_SMB_BAT_OFFSET + chan);
 187        ret = sbsm_write_word(data->client, SBSM_CMD_BATSYSSTATE, reg);
 188        if (ret)
 189                dev_err(dev, "Failed to select channel %i\n", chan);
 190        else
 191                data->cur_chan = chan;
 192
 193        return ret;
 194}
 195
 196static int sbsm_gpio_get_value(struct gpio_chip *gc, unsigned int off)
 197{
 198        struct sbsm_data *data = gpiochip_get_data(gc);
 199        int ret;
 200
 201        ret = sbsm_read_word(data->client, SBSM_CMD_BATSYSSTATE);
 202        if (ret < 0)
 203                return ret;
 204
 205        return ret & BIT(off);
 206}
 207
 208/*
 209 * This needs to be defined or the GPIO lib fails to register the pin.
 210 * But the 'gpio' is always an input.
 211 */
 212static int sbsm_gpio_direction_input(struct gpio_chip *gc, unsigned int off)
 213{
 214        return 0;
 215}
 216
 217static int sbsm_do_alert(struct device *dev, void *d)
 218{
 219        struct i2c_client *client = i2c_verify_client(dev);
 220        struct i2c_driver *driver;
 221
 222        if (!client || client->addr != 0x0b)
 223                return 0;
 224
 225        device_lock(dev);
 226        if (client->dev.driver) {
 227                driver = to_i2c_driver(client->dev.driver);
 228                if (driver->alert)
 229                        driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT, 0);
 230                else
 231                        dev_warn(&client->dev, "no driver alert()!\n");
 232        } else {
 233                dev_dbg(&client->dev, "alert with no driver\n");
 234        }
 235        device_unlock(dev);
 236
 237        return -EBUSY;
 238}
 239
 240static void sbsm_alert(struct i2c_client *client, enum i2c_alert_protocol prot,
 241                       unsigned int d)
 242{
 243        struct sbsm_data *sbsm = i2c_get_clientdata(client);
 244
 245        int ret, i, irq_bat = 0, state = 0;
 246
 247        ret = sbsm_read_word(sbsm->client, SBSM_CMD_BATSYSSTATE);
 248        if (ret >= 0) {
 249                irq_bat = ret ^ sbsm->last_state;
 250                sbsm->last_state = ret;
 251                state = ret;
 252        }
 253
 254        ret = sbsm_read_word(sbsm->client, SBSM_CMD_BATSYSSTATECONT);
 255        if ((ret >= 0) &&
 256            ((ret ^ sbsm->last_state_cont) & SBSM_BIT_AC_PRESENT)) {
 257                irq_bat |= sbsm->supported_bats & state;
 258                power_supply_changed(sbsm->psy);
 259        }
 260        sbsm->last_state_cont = ret;
 261
 262        for (i = 0; i < SBSM_MAX_BATS; i++) {
 263                if (irq_bat & BIT(i)) {
 264                        device_for_each_child(&sbsm->muxc->adapter[i]->dev,
 265                                              NULL, sbsm_do_alert);
 266                }
 267        }
 268}
 269
 270static int sbsm_gpio_setup(struct sbsm_data *data)
 271{
 272        struct gpio_chip *gc = &data->chip;
 273        struct i2c_client *client = data->client;
 274        struct device *dev = &client->dev;
 275        int ret;
 276
 277        if (!device_property_present(dev, "gpio-controller"))
 278                return 0;
 279
 280        ret  = sbsm_read_word(client, SBSM_CMD_BATSYSSTATE);
 281        if (ret < 0)
 282                return ret;
 283        data->last_state = ret;
 284
 285        ret  = sbsm_read_word(client, SBSM_CMD_BATSYSSTATECONT);
 286        if (ret < 0)
 287                return ret;
 288        data->last_state_cont = ret;
 289
 290        gc->get = sbsm_gpio_get_value;
 291        gc->direction_input  = sbsm_gpio_direction_input;
 292        gc->can_sleep = true;
 293        gc->base = -1;
 294        gc->ngpio = SBSM_MAX_BATS;
 295        gc->label = client->name;
 296        gc->parent = dev;
 297        gc->owner = THIS_MODULE;
 298
 299        ret = devm_gpiochip_add_data(dev, gc, data);
 300        if (ret) {
 301                dev_err(dev, "devm_gpiochip_add_data failed: %d\n", ret);
 302                return ret;
 303        }
 304
 305        return ret;
 306}
 307
 308static const struct power_supply_desc sbsm_default_psy_desc = {
 309        .type = POWER_SUPPLY_TYPE_MAINS,
 310        .properties = sbsm_props,
 311        .num_properties = ARRAY_SIZE(sbsm_props),
 312        .get_property = &sbsm_get_property,
 313        .set_property = &sbsm_set_property,
 314        .property_is_writeable = &sbsm_prop_is_writeable,
 315};
 316
 317static int sbsm_probe(struct i2c_client *client,
 318                      const struct i2c_device_id *id)
 319{
 320        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
 321        struct sbsm_data *data;
 322        struct device *dev = &client->dev;
 323        struct power_supply_desc *psy_desc;
 324        struct power_supply_config psy_cfg = {};
 325        int ret = 0, i;
 326
 327        /* Device listens only at address 0x0a */
 328        if (client->addr != 0x0a)
 329                return -EINVAL;
 330
 331        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
 332                return -EPFNOSUPPORT;
 333
 334        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 335        if (!data)
 336                return -ENOMEM;
 337
 338        i2c_set_clientdata(client, data);
 339
 340        data->client = client;
 341        data->is_ltc1760 = !!strstr(id->name, "ltc1760");
 342
 343        ret  = sbsm_read_word(client, SBSM_CMD_BATSYSINFO);
 344        if (ret < 0)
 345                return ret;
 346        data->supported_bats = ret & SBSM_MASK_BAT_SUPPORTED;
 347        data->muxc = i2c_mux_alloc(adapter, dev, SBSM_MAX_BATS, 0,
 348                                   I2C_MUX_LOCKED, &sbsm_select, NULL);
 349        if (!data->muxc) {
 350                dev_err(dev, "failed to alloc i2c mux\n");
 351                ret = -ENOMEM;
 352                goto err_mux_alloc;
 353        }
 354        data->muxc->priv = data;
 355
 356        /* register muxed i2c channels. One for each supported battery */
 357        for (i = 0; i < SBSM_MAX_BATS; ++i) {
 358                if (data->supported_bats & BIT(i)) {
 359                        ret = i2c_mux_add_adapter(data->muxc, 0, i + 1, 0);
 360                        if (ret)
 361                                break;
 362                }
 363        }
 364        if (ret) {
 365                dev_err(dev, "failed to register i2c mux channel %d\n", i + 1);
 366                goto err_mux_register;
 367        }
 368
 369        psy_desc = devm_kmemdup(dev, &sbsm_default_psy_desc,
 370                                sizeof(struct power_supply_desc),
 371                                GFP_KERNEL);
 372        if (!psy_desc) {
 373                ret = -ENOMEM;
 374                goto err_psy;
 375        }
 376
 377        psy_desc->name = devm_kasprintf(dev, GFP_KERNEL, "sbsm-%s",
 378                                        dev_name(&client->dev));
 379        if (!psy_desc->name) {
 380                ret = -ENOMEM;
 381                goto err_psy;
 382        }
 383        ret = sbsm_gpio_setup(data);
 384        if (ret < 0)
 385                goto err_psy;
 386
 387        psy_cfg.drv_data = data;
 388        psy_cfg.of_node = dev->of_node;
 389        data->psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
 390        if (IS_ERR(data->psy)) {
 391                ret = PTR_ERR(data->psy);
 392                dev_err(dev, "failed to register power supply %s\n",
 393                        psy_desc->name);
 394                goto err_psy;
 395        }
 396
 397        return 0;
 398
 399err_psy:
 400err_mux_register:
 401        i2c_mux_del_adapters(data->muxc);
 402
 403err_mux_alloc:
 404        return ret;
 405}
 406
 407static int sbsm_remove(struct i2c_client *client)
 408{
 409        struct sbsm_data *data = i2c_get_clientdata(client);
 410
 411        i2c_mux_del_adapters(data->muxc);
 412        return 0;
 413}
 414
 415static const struct i2c_device_id sbsm_ids[] = {
 416        { "sbs-manager", 0 },
 417        { "ltc1760",     0 },
 418        { }
 419};
 420MODULE_DEVICE_TABLE(i2c, sbsm_ids);
 421
 422#ifdef CONFIG_OF
 423static const struct of_device_id sbsm_dt_ids[] = {
 424        { .compatible = "sbs,sbs-manager" },
 425        { .compatible = "lltc,ltc1760" },
 426        { }
 427};
 428MODULE_DEVICE_TABLE(of, sbsm_dt_ids);
 429#endif
 430
 431static struct i2c_driver sbsm_driver = {
 432        .driver = {
 433                .name = "sbsm",
 434                .of_match_table = of_match_ptr(sbsm_dt_ids),
 435        },
 436        .probe          = sbsm_probe,
 437        .remove         = sbsm_remove,
 438        .alert          = sbsm_alert,
 439        .id_table       = sbsm_ids
 440};
 441module_i2c_driver(sbsm_driver);
 442
 443MODULE_LICENSE("GPL");
 444MODULE_AUTHOR("Karl-Heinz Schneider <karl-heinz@schneider-inet.de>");
 445MODULE_DESCRIPTION("SBSM Smart Battery System Manager");
 446