Merge tag 'for-v4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 31 Aug 2015 22:25:16 +0000 (15:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 31 Aug 2015 22:25:16 +0000 (15:25 -0700)
Pull power supply and reset changes from Sebastian Reichel:
 - new reset driver for ZTE SoCs
 - add support for sama5d3 reset handling
 - overhaul of twl4030 charger driver
 - misc fixes and cleanups

* tag 'for-v4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (35 commits)
  bq2415x_charger: Allow to load and use driver even if notify device is not registered yet
  twl4030_charger: fix compile error when TWL4030_MADC not available.
  power: bq24190_charger: Fix charge type sysfs property
  power: Allow compile test of GPIO consumers if !GPIOLIB
  power: Export I2C module alias information in missing drivers
  twl4030_charger: Increase current carefully while watching voltage.
  twl4030_charger: add ac/mode to match usb/mode
  twl4030_charger: add software controlled linear charging mode.
  twl4030_charger: enable manual enable/disable of usb charging.
  twl4030_charger: allow max_current to be managed via sysfs.
  twl4030_charger: distinguish between USB current and 'AC' current
  twl4030_charger: allow fine control of charger current.
  twl4030_charger: split uA calculation into a function.
  twl4030_charger: trust phy to determine when USB power is available.
  twl4030_charger: correctly handle -EPROBE_DEFER from devm_usb_get_phy_by_node
  twl4030_charger: convert to module_platform_driver instead of ..._probe.
  twl4030_charger: use runtime_pm to keep usb phy active while charging.
  rx51-battery: Set name to rx51-battery
  MAINTAINERS: AVS is not maintained via power supply tree
  power: olpc_battery: clean up eeprom read function
  ...

22 files changed:
Documentation/ABI/testing/sysfs-class-power-twl4030 [new file with mode: 0644]
Documentation/devicetree/bindings/arm/atmel-at91.txt
MAINTAINERS
drivers/mfd/twl-core.c
drivers/power/Kconfig
drivers/power/bq2415x_charger.c
drivers/power/bq24190_charger.c
drivers/power/bq24735-charger.c
drivers/power/bq27x00_battery.c
drivers/power/ds2780_battery.c
drivers/power/ds2781_battery.c
drivers/power/ltc2941-battery-gauge.c
drivers/power/olpc_battery.c
drivers/power/pm2301_charger.c
drivers/power/reset/Kconfig
drivers/power/reset/Makefile
drivers/power/reset/at91-reset.c
drivers/power/reset/zx-reboot.c [new file with mode: 0644]
drivers/power/rt5033_battery.c
drivers/power/rt9455_charger.c
drivers/power/rx51_battery.c
drivers/power/twl4030_charger.c

diff --git a/Documentation/ABI/testing/sysfs-class-power-twl4030 b/Documentation/ABI/testing/sysfs-class-power-twl4030
new file mode 100644 (file)
index 0000000..be26af0
--- /dev/null
@@ -0,0 +1,45 @@
+What: /sys/class/power_supply/twl4030_ac/max_current
+      /sys/class/power_supply/twl4030_usb/max_current
+Description:
+       Read/Write limit on current which may
+       be drawn from the ac (Accessory Charger) or
+       USB port.
+
+       Value is in micro-Amps.
+
+       Value is set automatically to an appropriate
+       value when a cable is plugged or unplugged.
+
+       Value can the set by writing to the attribute.
+       The change will only persist until the next
+       plug event.  These event are reported via udev.
+
+
+What: /sys/class/power_supply/twl4030_usb/mode
+Description:
+       Changing mode for USB port.
+       Writing to this can disable charging.
+
+       Possible values are:
+               "auto" - draw power as appropriate for detected
+                        power source and battery status.
+               "off"  - do not draw any power.
+               "continuous"
+                      - activate mode described as "linear" in
+                        TWL data sheets.  This uses whatever
+                        current is available and doesn't switch off
+                        when voltage drops.
+
+                        This is useful for unstable power sources
+                        such as bicycle dynamo, but care should
+                        be taken that battery is not over-charged.
+
+What: /sys/class/power_supply/twl4030_ac/mode
+Description:
+       Changing mode for 'ac' port.
+       Writing to this can disable charging.
+
+       Possible values are:
+               "auto" - draw power as appropriate for detected
+                        power source and battery status.
+               "off"  - do not draw any power.
index 424ac8c..dd998b9 100644 (file)
@@ -87,7 +87,7 @@ One interrupt per TC channel in a TC block:
 
 RSTC Reset Controller required properties:
 - compatible: Should be "atmel,<chip>-rstc".
-  <chip> can be "at91sam9260" or "at91sam9g45"
+  <chip> can be "at91sam9260" or "at91sam9g45" or "sama5d3"
 - reg: Should contain registers location and length
 
 Example:
index 8b3115c..d539557 100644 (file)
@@ -8093,6 +8093,7 @@ T:        git git://git.infradead.org/battery-2.6.git
 S:     Maintained
 F:     include/linux/power_supply.h
 F:     drivers/power/
+X:     drivers/power/avs/
 
 PNP SUPPORT
 M:     "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
index 489674a..831696e 100644 (file)
@@ -788,9 +788,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
                static struct regulator_consumer_supply usb1v8 = {
                        .supply =       "usb1v8",
                };
-               static struct regulator_consumer_supply usb3v1[] = {
-                       { .supply =     "usb3v1" },
-                       { .supply =     "bci3v1" },
+               static struct regulator_consumer_supply usb3v1 = {
+                       .supply =       "usb3v1",
                };
 
        /* First add the regulators so that they can be used by transceiver */
@@ -818,7 +817,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
                                return PTR_ERR(child);
 
                        child = add_regulator_linked(TWL4030_REG_VUSB3V1,
-                                                     &usb_fixed, usb3v1, 2,
+                                                     &usb_fixed, &usb3v1, 1,
                                                      features);
                        if (IS_ERR(child))
                                return PTR_ERR(child);
@@ -838,7 +837,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
                if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child) {
                        usb1v5.dev_name = dev_name(child);
                        usb1v8.dev_name = dev_name(child);
-                       usb3v1[0].dev_name = dev_name(child);
+                       usb3v1.dev_name = dev_name(child);
                }
        }
 
index 08beeed..f8758d6 100644 (file)
@@ -333,7 +333,7 @@ config CHARGER_LP8788
 
 config CHARGER_GPIO
        tristate "GPIO charger"
-       depends on GPIOLIB
+       depends on GPIOLIB || COMPILE_TEST
        help
          Say Y to include support for chargers which report their online status
          through a GPIO pin.
@@ -391,26 +391,30 @@ config CHARGER_BQ2415X
 
 config CHARGER_BQ24190
        tristate "TI BQ24190 battery charger driver"
-       depends on I2C && GPIOLIB
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
        help
          Say Y to enable support for the TI BQ24190 battery charger.
 
 config CHARGER_BQ24257
        tristate "TI BQ24257 battery charger driver"
-       depends on I2C && GPIOLIB
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
        depends on REGMAP_I2C
        help
          Say Y to enable support for the TI BQ24257 battery charger.
 
 config CHARGER_BQ24735
        tristate "TI BQ24735 battery charger support"
-       depends on I2C && GPIOLIB
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
        help
          Say Y to enable support for the TI BQ24735 battery charger.
 
 config CHARGER_BQ25890
        tristate "TI BQ25890 battery charger driver"
-       depends on I2C && GPIOLIB
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
        select REGMAP_I2C
        help
          Say Y to enable support for the TI BQ25890 battery charger.
@@ -462,7 +466,8 @@ config BATTERY_RT5033
 
 config CHARGER_RT9455
        tristate "Richtek RT9455 battery charger driver"
-       depends on I2C && GPIOLIB
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
        select REGMAP_I2C
        help
          Say Y to enable support for Richtek RT9455 battery charger.
index e98dcb6..ec212b5 100644 (file)
@@ -170,7 +170,7 @@ struct bq2415x_device {
        struct power_supply *charger;
        struct power_supply_desc charger_desc;
        struct delayed_work work;
-       struct power_supply *notify_psy;
+       struct device_node *notify_node;
        struct notifier_block nb;
        enum bq2415x_mode reported_mode;/* mode reported by hook function */
        enum bq2415x_mode mode;         /* currently configured mode */
@@ -792,22 +792,47 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
 
 }
 
+static bool bq2415x_update_reported_mode(struct bq2415x_device *bq, int mA)
+{
+       enum bq2415x_mode mode;
+
+       if (mA == 0)
+               mode = BQ2415X_MODE_OFF;
+       else if (mA < 500)
+               mode = BQ2415X_MODE_NONE;
+       else if (mA < 1800)
+               mode = BQ2415X_MODE_HOST_CHARGER;
+       else
+               mode = BQ2415X_MODE_DEDICATED_CHARGER;
+
+       if (bq->reported_mode == mode)
+               return false;
+
+       bq->reported_mode = mode;
+       return true;
+}
+
 static int bq2415x_notifier_call(struct notifier_block *nb,
                unsigned long val, void *v)
 {
        struct bq2415x_device *bq =
                container_of(nb, struct bq2415x_device, nb);
        struct power_supply *psy = v;
-       enum bq2415x_mode mode;
        union power_supply_propval prop;
        int ret;
-       int mA;
 
        if (val != PSY_EVENT_PROP_CHANGED)
                return NOTIFY_OK;
 
-       if (psy != bq->notify_psy)
-               return NOTIFY_OK;
+       /* Ignore event if it was not send by notify_node/notify_device */
+       if (bq->notify_node) {
+               if (!psy->dev.parent ||
+                   psy->dev.parent->of_node != bq->notify_node)
+                       return NOTIFY_OK;
+       } else if (bq->init_data.notify_device) {
+               if (strcmp(psy->desc->name, bq->init_data.notify_device) != 0)
+                       return NOTIFY_OK;
+       }
 
        dev_dbg(bq->dev, "notifier call was called\n");
 
@@ -816,22 +841,9 @@ static int bq2415x_notifier_call(struct notifier_block *nb,
        if (ret != 0)
                return NOTIFY_OK;
 
-       mA = prop.intval;
-
-       if (mA == 0)
-               mode = BQ2415X_MODE_OFF;
-       else if (mA < 500)
-               mode = BQ2415X_MODE_NONE;
-       else if (mA < 1800)
-               mode = BQ2415X_MODE_HOST_CHARGER;
-       else
-               mode = BQ2415X_MODE_DEDICATED_CHARGER;
-
-       if (bq->reported_mode == mode)
+       if (!bq2415x_update_reported_mode(bq, prop.intval))
                return NOTIFY_OK;
 
-       bq->reported_mode = mode;
-
        /* if automode is not enabled do not tell about reported_mode */
        if (bq->automode < 1)
                return NOTIFY_OK;
@@ -1536,6 +1548,8 @@ static int bq2415x_probe(struct i2c_client *client,
        struct device_node *np = client->dev.of_node;
        struct bq2415x_platform_data *pdata = client->dev.platform_data;
        const struct acpi_device_id *acpi_id = NULL;
+       struct power_supply *notify_psy = NULL;
+       union power_supply_propval prop;
 
        if (!np && !pdata && !ACPI_HANDLE(&client->dev)) {
                dev_err(&client->dev, "Neither devicetree, nor platform data, nor ACPI support\n");
@@ -1569,25 +1583,6 @@ static int bq2415x_probe(struct i2c_client *client,
                goto error_2;
        }
 
-       if (np) {
-               bq->notify_psy = power_supply_get_by_phandle(np,
-                                               "ti,usb-charger-detection");
-
-               if (IS_ERR(bq->notify_psy)) {
-                       dev_info(&client->dev,
-                                "no 'ti,usb-charger-detection' property (err=%ld)\n",
-                               PTR_ERR(bq->notify_psy));
-                       bq->notify_psy = NULL;
-               } else if (!bq->notify_psy) {
-                       ret = -EPROBE_DEFER;
-                       goto error_2;
-               }
-       } else if (pdata && pdata->notify_device) {
-               bq->notify_psy = power_supply_get_by_name(pdata->notify_device);
-       } else {
-               bq->notify_psy = NULL;
-       }
-
        i2c_set_clientdata(client, bq);
 
        bq->id = num;
@@ -1607,32 +1602,35 @@ static int bq2415x_probe(struct i2c_client *client,
                                               "ti,current-limit",
                                               &bq->init_data.current_limit);
                if (ret)
-                       goto error_3;
+                       goto error_2;
                ret = device_property_read_u32(bq->dev,
                                        "ti,weak-battery-voltage",
                                        &bq->init_data.weak_battery_voltage);
                if (ret)
-                       goto error_3;
+                       goto error_2;
                ret = device_property_read_u32(bq->dev,
                                "ti,battery-regulation-voltage",
                                &bq->init_data.battery_regulation_voltage);
                if (ret)
-                       goto error_3;
+                       goto error_2;
                ret = device_property_read_u32(bq->dev,
                                               "ti,charge-current",
                                               &bq->init_data.charge_current);
                if (ret)
-                       goto error_3;
+                       goto error_2;
                ret = device_property_read_u32(bq->dev,
                                "ti,termination-current",
                                &bq->init_data.termination_current);
                if (ret)
-                       goto error_3;
+                       goto error_2;
                ret = device_property_read_u32(bq->dev,
                                               "ti,resistor-sense",
                                               &bq->init_data.resistor_sense);
                if (ret)
-                       goto error_3;
+                       goto error_2;
+               if (np)
+                       bq->notify_node = of_parse_phandle(np,
+                                               "ti,usb-charger-detection", 0);
        } else {
                memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
        }
@@ -1642,56 +1640,72 @@ static int bq2415x_probe(struct i2c_client *client,
        ret = bq2415x_power_supply_init(bq);
        if (ret) {
                dev_err(bq->dev, "failed to register power supply: %d\n", ret);
-               goto error_3;
+               goto error_2;
        }
 
        ret = bq2415x_sysfs_init(bq);
        if (ret) {
                dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
-               goto error_4;
+               goto error_3;
        }
 
        ret = bq2415x_set_defaults(bq);
        if (ret) {
                dev_err(bq->dev, "failed to set default values: %d\n", ret);
-               goto error_5;
+               goto error_4;
        }
 
-       if (bq->notify_psy) {
+       if (bq->notify_node || bq->init_data.notify_device) {
                bq->nb.notifier_call = bq2415x_notifier_call;
                ret = power_supply_reg_notifier(&bq->nb);
                if (ret) {
                        dev_err(bq->dev, "failed to reg notifier: %d\n", ret);
-                       goto error_6;
+                       goto error_4;
                }
 
-               /* Query for initial reported_mode and set it */
-               bq2415x_notifier_call(&bq->nb, PSY_EVENT_PROP_CHANGED,
-                                     bq->notify_psy);
-               bq2415x_set_mode(bq, bq->reported_mode);
-
                bq->automode = 1;
-               dev_info(bq->dev, "automode enabled\n");
+               dev_info(bq->dev, "automode supported, waiting for events\n");
        } else {
                bq->automode = -1;
                dev_info(bq->dev, "automode not supported\n");
        }
 
+       /* Query for initial reported_mode and set it */
+       if (bq->nb.notifier_call) {
+               if (np) {
+                       notify_psy = power_supply_get_by_phandle(np,
+                                               "ti,usb-charger-detection");
+                       if (IS_ERR(notify_psy))
+                               notify_psy = NULL;
+               } else if (bq->init_data.notify_device) {
+                       notify_psy = power_supply_get_by_name(
+                                               bq->init_data.notify_device);
+               }
+       }
+       if (notify_psy) {
+               ret = power_supply_get_property(notify_psy,
+                                       POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
+               power_supply_put(notify_psy);
+
+               if (ret == 0) {
+                       bq2415x_update_reported_mode(bq, prop.intval);
+                       bq2415x_set_mode(bq, bq->reported_mode);
+               }
+       }
+
        INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work);
        bq2415x_set_autotimer(bq, 1);
 
        dev_info(bq->dev, "driver registered\n");
        return 0;
 
-error_6:
-error_5:
-       bq2415x_sysfs_exit(bq);
 error_4:
-       bq2415x_power_supply_exit(bq);
+       bq2415x_sysfs_exit(bq);
 error_3:
-       if (bq->notify_psy)
-               power_supply_put(bq->notify_psy);
+       bq2415x_power_supply_exit(bq);
 error_2:
+       if (bq->notify_node)
+               of_node_put(bq->notify_node);
        kfree(name);
 error_1:
        mutex_lock(&bq2415x_id_mutex);
@@ -1707,10 +1721,11 @@ static int bq2415x_remove(struct i2c_client *client)
 {
        struct bq2415x_device *bq = i2c_get_clientdata(client);
 
-       if (bq->notify_psy) {
+       if (bq->nb.notifier_call)
                power_supply_unreg_notifier(&bq->nb);
-               power_supply_put(bq->notify_psy);
-       }
+
+       if (bq->notify_node)
+               of_node_put(bq->notify_node);
 
        bq2415x_sysfs_exit(bq);
        bq2415x_power_supply_exit(bq);
index 052db78..469a452 100644 (file)
@@ -902,7 +902,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy,
 }
 
 static enum power_supply_property bq24190_charger_properties[] = {
-       POWER_SUPPLY_PROP_TYPE,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
        POWER_SUPPLY_PROP_HEALTH,
        POWER_SUPPLY_PROP_ONLINE,
        POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
@@ -1515,6 +1515,7 @@ static const struct i2c_device_id bq24190_i2c_ids[] = {
        { "bq24190", BQ24190_REG_VPRS_PN_24190 },
        { },
 };
+MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
 
 #ifdef CONFIG_OF
 static const struct of_device_id bq24190_of_match[] = {
@@ -1534,7 +1535,6 @@ static struct i2c_driver bq24190_driver = {
        .id_table       = bq24190_i2c_ids,
        .driver = {
                .name           = "bq24190-charger",
-               .owner          = THIS_MODULE,
                .pm             = &bq24190_pm_ops,
                .of_match_table = of_match_ptr(bq24190_of_match),
        },
index 961a189..eb2b368 100644 (file)
@@ -267,8 +267,9 @@ static int bq24735_charger_probe(struct i2c_client *client,
 
        name = (char *)charger->pdata->name;
        if (!name) {
-               name = kasprintf(GFP_KERNEL, "bq24735@%s",
-                                dev_name(&client->dev));
+               name = devm_kasprintf(&client->dev, GFP_KERNEL,
+                                     "bq24735@%s",
+                                     dev_name(&client->dev));
                if (!name) {
                        dev_err(&client->dev, "Failed to alloc device name\n");
                        return -ENOMEM;
@@ -296,23 +297,21 @@ static int bq24735_charger_probe(struct i2c_client *client,
        if (ret < 0) {
                dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
                        ret);
-               goto err_free_name;
+               return ret;
        } else if (ret != 0x0040) {
                dev_err(&client->dev,
                        "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
-               ret = -ENODEV;
-               goto err_free_name;
+               return -ENODEV;
        }
 
        ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
        if (ret < 0) {
                dev_err(&client->dev, "Failed to read device id : %d\n", ret);
-               goto err_free_name;
+               return ret;
        } else if (ret != 0x000B) {
                dev_err(&client->dev,
                        "device id mismatch. 0x000b != 0x%04x\n", ret);
-               ret = -ENODEV;
-               goto err_free_name;
+               return -ENODEV;
        }
 
        if (gpio_is_valid(charger->pdata->status_gpio)) {
@@ -331,7 +330,7 @@ static int bq24735_charger_probe(struct i2c_client *client,
        ret = bq24735_config_charger(charger);
        if (ret < 0) {
                dev_err(&client->dev, "failed in configuring charger");
-               goto err_free_name;
+               return ret;
        }
 
        /* check for AC adapter presence */
@@ -339,17 +338,17 @@ static int bq24735_charger_probe(struct i2c_client *client,
                ret = bq24735_enable_charging(charger);
                if (ret < 0) {
                        dev_err(&client->dev, "Failed to enable charging\n");
-                       goto err_free_name;
+                       return ret;
                }
        }
 
-       charger->charger = power_supply_register(&client->dev, supply_desc,
-                                                &psy_cfg);
+       charger->charger = devm_power_supply_register(&client->dev, supply_desc,
+                                                     &psy_cfg);
        if (IS_ERR(charger->charger)) {
                ret = PTR_ERR(charger->charger);
                dev_err(&client->dev, "Failed to register power supply: %d\n",
                        ret);
-               goto err_free_name;
+               return ret;
        }
 
        if (client->irq) {
@@ -364,33 +363,10 @@ static int bq24735_charger_probe(struct i2c_client *client,
                        dev_err(&client->dev,
                                "Unable to register IRQ %d err %d\n",
                                client->irq, ret);
-                       goto err_unregister_supply;
+                       return ret;
                }
        }
 
-       return 0;
-err_unregister_supply:
-       power_supply_unregister(charger->charger);
-err_free_name:
-       if (name != charger->pdata->name)
-               kfree(name);
-
-       return ret;
-}
-
-static int bq24735_charger_remove(struct i2c_client *client)
-{
-       struct bq24735 *charger = i2c_get_clientdata(client);
-
-       if (charger->client->irq)
-               devm_free_irq(&charger->client->dev, charger->client->irq,
-                             &charger->charger);
-
-       power_supply_unregister(charger->charger);
-
-       if (charger->charger_desc.name != charger->pdata->name)
-               kfree(charger->charger_desc.name);
-
        return 0;
 }
 
@@ -409,11 +385,9 @@ MODULE_DEVICE_TABLE(of, bq24735_match_ids);
 static struct i2c_driver bq24735_charger_driver = {
        .driver = {
                .name = "bq24735-charger",
-               .owner = THIS_MODULE,
                .of_match_table = bq24735_match_ids,
        },
        .probe = bq24735_charger_probe,
-       .remove = bq24735_charger_remove,
        .id_table = bq24735_charger_id,
 };
 
index b6b9837..8287261 100644 (file)
 
 #include <linux/power/bq27x00_battery.h>
 
-#define DRIVER_VERSION                 "1.2.0"
-
-#define BQ27x00_REG_TEMP               0x06
-#define BQ27x00_REG_VOLT               0x08
-#define BQ27x00_REG_AI                 0x14
-#define BQ27x00_REG_FLAGS              0x0A
-#define BQ27x00_REG_TTE                        0x16
-#define BQ27x00_REG_TTF                        0x18
-#define BQ27x00_REG_TTECP              0x26
-#define BQ27x00_REG_NAC                        0x0C /* Nominal available capacity */
-#define BQ27x00_REG_LMD                        0x12 /* Last measured discharge */
-#define BQ27x00_REG_CYCT               0x2A /* Cycle count total */
-#define BQ27x00_REG_AE                 0x22 /* Available energy */
-#define BQ27x00_POWER_AVG              0x24
-
-#define BQ27000_REG_RSOC               0x0B /* Relative State-of-Charge */
-#define BQ27000_REG_ILMD               0x76 /* Initial last measured discharge */
-#define BQ27000_FLAG_EDVF              BIT(0) /* Final End-of-Discharge-Voltage flag */
-#define BQ27000_FLAG_EDV1              BIT(1) /* First End-of-Discharge-Voltage flag */
-#define BQ27000_FLAG_CI                        BIT(4) /* Capacity Inaccurate flag */
-#define BQ27000_FLAG_FC                        BIT(5)
-#define BQ27000_FLAG_CHGS              BIT(7) /* Charge state flag */
-
-#define BQ27500_REG_SOC                        0x2C
-#define BQ27500_REG_DCAP               0x3C /* Design capacity */
-#define BQ27500_FLAG_DSC               BIT(0)
-#define BQ27500_FLAG_SOCF              BIT(1) /* State-of-Charge threshold final */
-#define BQ27500_FLAG_SOC1              BIT(2) /* State-of-Charge threshold 1 */
-#define BQ27500_FLAG_FC                        BIT(9)
-#define BQ27500_FLAG_OTC               BIT(15)
-
-#define BQ27742_POWER_AVG              0x76
-
-#define BQ27510_REG_SOC                        0x20
-#define BQ27510_REG_DCAP               0x2E /* Design capacity */
-#define BQ27510_REG_CYCT               0x1E /* Cycle count total */
+#define DRIVER_VERSION         "1.2.0"
+
+#define BQ27XXX_MANUFACTURER   "Texas Instruments"
+
+#define BQ27x00_REG_TEMP       0x06
+#define BQ27x00_REG_VOLT       0x08
+#define BQ27x00_REG_AI         0x14
+#define BQ27x00_REG_FLAGS      0x0A
+#define BQ27x00_REG_TTE                0x16
+#define BQ27x00_REG_TTF                0x18
+#define BQ27x00_REG_TTECP      0x26
+#define BQ27x00_REG_NAC                0x0C /* Nominal available capacity */
+#define BQ27x00_REG_LMD                0x12 /* Last measured discharge */
+#define BQ27x00_REG_CYCT       0x2A /* Cycle count total */
+#define BQ27x00_REG_AE         0x22 /* Available energy */
+#define BQ27x00_POWER_AVG      0x24
+
+#define BQ27000_REG_RSOC       0x0B /* Relative State-of-Charge */
+#define BQ27000_REG_ILMD       0x76 /* Initial last measured discharge */
+#define BQ27000_FLAG_EDVF      BIT(0) /* Final End-of-Discharge-Voltage flag */
+#define BQ27000_FLAG_EDV1      BIT(1) /* First End-of-Discharge-Voltage flag */
+#define BQ27000_FLAG_CI                BIT(4) /* Capacity Inaccurate flag */
+#define BQ27000_FLAG_FC                BIT(5)
+#define BQ27000_FLAG_CHGS      BIT(7) /* Charge state flag */
+
+#define BQ27500_REG_SOC                0x2C
+#define BQ27500_REG_DCAP       0x3C /* Design capacity */
+#define BQ27500_FLAG_DSC       BIT(0)
+#define BQ27500_FLAG_SOCF      BIT(1) /* State-of-Charge threshold final */
+#define BQ27500_FLAG_SOC1      BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27500_FLAG_FC                BIT(9)
+#define BQ27500_FLAG_OTC       BIT(15)
+
+#define BQ27742_POWER_AVG      0x76
+
+#define BQ27510_REG_SOC                0x20
+#define BQ27510_REG_DCAP       0x2E /* Design capacity */
+#define BQ27510_REG_CYCT       0x1E /* Cycle count total */
 
 /* bq27425 register addresses are same as bq27x00 addresses minus 4 */
-#define BQ27425_REG_OFFSET             0x04
+#define BQ27425_REG_OFFSET     0x04
 #define BQ27425_REG_SOC                (0x1C + BQ27425_REG_OFFSET)
-#define BQ27425_REG_DCAP               (0x3C + BQ27425_REG_OFFSET)
+#define BQ27425_REG_DCAP       (0x3C + BQ27425_REG_OFFSET)
 
 #define BQ27000_RS                     20 /* Resistor sense */
 #define BQ27x00_POWER_CONSTANT         (256 * 29200 / 1000)
@@ -106,7 +108,7 @@ struct bq27x00_reg_cache {
 };
 
 struct bq27x00_device_info {
-       struct device           *dev;
+       struct device           *dev;
        int                     id;
        enum bq27x00_chip       chip;
 
@@ -142,6 +144,7 @@ static enum power_supply_property bq27x00_battery_props[] = {
        POWER_SUPPLY_PROP_ENERGY_NOW,
        POWER_SUPPLY_PROP_POWER_AVG,
        POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
 static enum power_supply_property bq27425_battery_props[] = {
@@ -156,6 +159,7 @@ static enum power_supply_property bq27425_battery_props[] = {
        POWER_SUPPLY_PROP_CHARGE_FULL,
        POWER_SUPPLY_PROP_CHARGE_NOW,
        POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
 static enum power_supply_property bq27742_battery_props[] = {
@@ -174,6 +178,7 @@ static enum power_supply_property bq27742_battery_props[] = {
        POWER_SUPPLY_PROP_CYCLE_COUNT,
        POWER_SUPPLY_PROP_POWER_AVG,
        POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
 static enum power_supply_property bq27510_battery_props[] = {
@@ -192,19 +197,20 @@ static enum power_supply_property bq27510_battery_props[] = {
        POWER_SUPPLY_PROP_CYCLE_COUNT,
        POWER_SUPPLY_PROP_POWER_AVG,
        POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
 static unsigned int poll_interval = 360;
 module_param(poll_interval, uint, 0644);
-MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \
-                               "0 disables polling");
+MODULE_PARM_DESC(poll_interval,
+                "battery poll interval in seconds - 0 disables polling");
 
 /*
  * Common code for BQ27x00 devices
  */
 
 static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg,
-               bool single)
+                              bool single)
 {
        if (di->chip == BQ27425)
                return di->bus.read(di, reg - BQ27425_REG_OFFSET, single);
@@ -313,8 +319,9 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
                        ilmd = bq27x00_read(di, BQ27510_REG_DCAP, false);
                else
                        ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
-       } else
+       } else {
                ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
+       }
 
        if (ilmd < 0) {
                dev_dbg(di->dev, "error reading initial last measured discharge\n");
@@ -445,7 +452,7 @@ static int bq27x00_battery_read_health(struct bq27x00_device_info *di)
                return tval;
        }
 
-       if ((di->chip == BQ27500)) {
+       if (di->chip == BQ27500) {
                if (tval & BQ27500_FLAG_SOCF)
                        tval = POWER_SUPPLY_HEALTH_DEAD;
                else if (tval & BQ27500_FLAG_OTC)
@@ -559,7 +566,7 @@ static void bq27x00_battery_poll(struct work_struct *work)
  * Or 0 if something fails.
  */
 static int bq27x00_battery_current(struct bq27x00_device_info *di,
-       union power_supply_propval *val)
+                                  union power_supply_propval *val)
 {
        int curr;
        int flags;
@@ -587,7 +594,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di,
 }
 
 static int bq27x00_battery_status(struct bq27x00_device_info *di,
-       union power_supply_propval *val)
+                                 union power_supply_propval *val)
 {
        int status;
 
@@ -615,7 +622,7 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,
 }
 
 static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
-       union power_supply_propval *val)
+                                         union power_supply_propval *val)
 {
        int level;
 
@@ -649,7 +656,7 @@ static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
  * Or < 0 if something fails.
  */
 static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
-       union power_supply_propval *val)
+                                  union power_supply_propval *val)
 {
        int volt;
 
@@ -665,7 +672,7 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
 }
 
 static int bq27x00_simple_value(int value,
-       union power_supply_propval *val)
+                               union power_supply_propval *val)
 {
        if (value < 0)
                return value;
@@ -749,6 +756,9 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_HEALTH:
                ret = bq27x00_simple_value(di->cache.health, val);
                break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = BQ27XXX_MANUFACTURER;
+               break;
        default:
                return -EINVAL;
        }
@@ -827,7 +837,6 @@ static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di)
        mutex_destroy(&di->lock);
 }
 
-
 /* i2c specific code */
 #ifdef CONFIG_BATTERY_BQ27X00_I2C
 
@@ -888,14 +897,12 @@ static int bq27x00_battery_probe(struct i2c_client *client,
 
        name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
        if (!name) {
-               dev_err(&client->dev, "failed to allocate device name\n");
                retval = -ENOMEM;
                goto batt_failed;
        }
 
        di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
        if (!di) {
-               dev_err(&client->dev, "failed to allocate device info data\n");
                retval = -ENOMEM;
                goto batt_failed;
        }
@@ -956,8 +963,9 @@ static struct i2c_driver bq27x00_battery_driver = {
 static inline int bq27x00_battery_i2c_init(void)
 {
        int ret = i2c_add_driver(&bq27x00_battery_driver);
+
        if (ret)
-               printk(KERN_ERR "Unable to register BQ27x00 i2c driver\n");
+               pr_err("Unable to register BQ27x00 i2c driver\n");
 
        return ret;
 }
@@ -978,7 +986,7 @@ static inline void bq27x00_battery_i2c_exit(void) {};
 #ifdef CONFIG_BATTERY_BQ27X00_PLATFORM
 
 static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg,
-                       bool single)
+                                bool single)
 {
        struct device *dev = di->dev;
        struct bq27000_platform_data *pdata = dev->platform_data;
@@ -1028,10 +1036,8 @@ static int bq27000_battery_probe(struct platform_device *pdev)
        }
 
        di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
-       if (!di) {
-               dev_err(&pdev->dev, "failed to allocate device info data\n");
+       if (!di)
                return -ENOMEM;
-       }
 
        platform_set_drvdata(pdev, di);
 
@@ -1064,8 +1070,9 @@ static struct platform_driver bq27000_battery_driver = {
 static inline int bq27x00_battery_platform_init(void)
 {
        int ret = platform_driver_register(&bq27000_battery_driver);
+
        if (ret)
-               printk(KERN_ERR "Unable to register BQ27000 platform driver\n");
+               pr_err("Unable to register BQ27000 platform driver\n");
 
        return ret;
 }
index a7a0427..d3743d0 100644 (file)
@@ -637,10 +637,6 @@ static ssize_t ds2780_read_param_eeprom_bin(struct file *filp,
        struct power_supply *psy = to_power_supply(dev);
        struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
 
-       count = min_t(loff_t, count,
-               DS2780_EEPROM_BLOCK1_END -
-               DS2780_EEPROM_BLOCK1_START + 1 - off);
-
        return ds2780_read_block(dev_info, buf,
                                DS2780_EEPROM_BLOCK1_START + off, count);
 }
@@ -655,10 +651,6 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
        struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
        int ret;
 
-       count = min_t(loff_t, count,
-               DS2780_EEPROM_BLOCK1_END -
-               DS2780_EEPROM_BLOCK1_START + 1 - off);
-
        ret = ds2780_write(dev_info, buf,
                                DS2780_EEPROM_BLOCK1_START + off, count);
        if (ret < 0)
@@ -676,7 +668,7 @@ static struct bin_attribute ds2780_param_eeprom_bin_attr = {
                .name = "param_eeprom",
                .mode = S_IRUGO | S_IWUSR,
        },
-       .size = DS2780_EEPROM_BLOCK1_END - DS2780_EEPROM_BLOCK1_START + 1,
+       .size = DS2780_PARAM_EEPROM_SIZE,
        .read = ds2780_read_param_eeprom_bin,
        .write = ds2780_write_param_eeprom_bin,
 };
@@ -690,10 +682,6 @@ static ssize_t ds2780_read_user_eeprom_bin(struct file *filp,
        struct power_supply *psy = to_power_supply(dev);
        struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
 
-       count = min_t(loff_t, count,
-               DS2780_EEPROM_BLOCK0_END -
-               DS2780_EEPROM_BLOCK0_START + 1 - off);
-
        return ds2780_read_block(dev_info, buf,
                                DS2780_EEPROM_BLOCK0_START + off, count);
 }
@@ -708,10 +696,6 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
        struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
        int ret;
 
-       count = min_t(loff_t, count,
-               DS2780_EEPROM_BLOCK0_END -
-               DS2780_EEPROM_BLOCK0_START + 1 - off);
-
        ret = ds2780_write(dev_info, buf,
                                DS2780_EEPROM_BLOCK0_START + off, count);
        if (ret < 0)
@@ -729,7 +713,7 @@ static struct bin_attribute ds2780_user_eeprom_bin_attr = {
                .name = "user_eeprom",
                .mode = S_IRUGO | S_IWUSR,
        },
-       .size = DS2780_EEPROM_BLOCK0_END - DS2780_EEPROM_BLOCK0_START + 1,
+       .size = DS2780_USER_EEPROM_SIZE,
        .read = ds2780_read_user_eeprom_bin,
        .write = ds2780_write_user_eeprom_bin,
 };
index 56d583d..c368002 100644 (file)
@@ -639,8 +639,6 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
        struct power_supply *psy = to_power_supply(dev);
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
 
-       count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
-
        return ds2781_read_block(dev_info, buf,
                                DS2781_EEPROM_BLOCK1_START + off, count);
 }
@@ -655,8 +653,6 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
        int ret;
 
-       count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
-
        ret = ds2781_write(dev_info, buf,
                                DS2781_EEPROM_BLOCK1_START + off, count);
        if (ret < 0)
@@ -688,8 +684,6 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
        struct power_supply *psy = to_power_supply(dev);
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
 
-       count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
-
        return ds2781_read_block(dev_info, buf,
                                DS2781_EEPROM_BLOCK0_START + off, count);
 
@@ -705,8 +699,6 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
        int ret;
 
-       count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
-
        ret = ds2781_write(dev_info, buf,
                                DS2781_EEPROM_BLOCK0_START + off, count);
        if (ret < 0)
index daeb086..4adf2ba 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/swab.h>
 #include <linux/i2c.h>
 #include <linux/delay.h>
-#include <linux/idr.h>
 #include <linux/power_supply.h>
 #include <linux/slab.h>
 
@@ -63,15 +62,11 @@ struct ltc294x_info {
        struct power_supply_desc supply_desc;   /* Supply description */
        struct delayed_work work;       /* Work scheduler */
        int num_regs;   /* Number of registers (chip type) */
-       int id;         /* Identifier of ltc294x chip */
        int charge;     /* Last charge register content */
        int r_sense;    /* mOhm */
        int Qlsb;       /* nAh */
 };
 
-static DEFINE_IDR(ltc294x_id);
-static DEFINE_MUTEX(ltc294x_lock);
-
 static inline int convert_bin_to_uAh(
        const struct ltc294x_info *info, int Q)
 {
@@ -371,10 +366,6 @@ static int ltc294x_i2c_remove(struct i2c_client *client)
 
        cancel_delayed_work(&info->work);
        power_supply_unregister(info->supply);
-       kfree(info->supply_desc.name);
-       mutex_lock(&ltc294x_lock);
-       idr_remove(&ltc294x_id, info->id);
-       mutex_unlock(&ltc294x_lock);
        return 0;
 }
 
@@ -384,44 +375,28 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
        struct power_supply_config psy_cfg = {};
        struct ltc294x_info *info;
        int ret;
-       int num;
        u32 prescaler_exp;
        s32 r_sense;
        struct device_node *np;
 
-       mutex_lock(&ltc294x_lock);
-       ret = idr_alloc(&ltc294x_id, client, 0, 0, GFP_KERNEL);
-       mutex_unlock(&ltc294x_lock);
-       if (ret < 0)
-               goto fail_id;
-
-       num = ret;
-
        info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
-       if (info == NULL) {
-               ret = -ENOMEM;
-               goto fail_info;
-       }
+       if (info == NULL)
+               return -ENOMEM;
 
        i2c_set_clientdata(client, info);
 
-       info->num_regs = id->driver_data;
-       info->supply_desc.name = kasprintf(GFP_KERNEL, "%s-%d", client->name,
-                                          num);
-       if (!info->supply_desc.name) {
-               ret = -ENOMEM;
-               goto fail_name;
-       }
-
        np = of_node_get(client->dev.of_node);
 
+       info->num_regs = id->driver_data;
+       info->supply_desc.name = np->name;
+
        /* r_sense can be negative, when sense+ is connected to the battery
         * instead of the sense-. This results in reversed measurements. */
        ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense);
        if (ret < 0) {
                dev_err(&client->dev,
                        "Could not find lltc,resistor-sense in devicetree\n");
-               goto fail_name;
+               return ret;
        }
        info->r_sense = r_sense;
 
@@ -446,7 +421,6 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
        }
 
        info->client = client;
-       info->id = num;
        info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY;
        info->supply_desc.properties = ltc294x_properties;
        if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB)
@@ -473,31 +447,19 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
        ret = ltc294x_reset(info, prescaler_exp);
        if (ret < 0) {
                dev_err(&client->dev, "Communication with chip failed\n");
-               goto fail_comm;
+               return ret;
        }
 
        info->supply = power_supply_register(&client->dev, &info->supply_desc,
                                             &psy_cfg);
        if (IS_ERR(info->supply)) {
                dev_err(&client->dev, "failed to register ltc2941\n");
-               ret = PTR_ERR(info->supply);
-               goto fail_register;
+               return PTR_ERR(info->supply);
        } else {
                schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
        }
 
        return 0;
-
-fail_register:
-       kfree(info->supply_desc.name);
-fail_comm:
-fail_name:
-fail_info:
-       mutex_lock(&ltc294x_lock);
-       idr_remove(&ltc294x_id, num);
-       mutex_unlock(&ltc294x_lock);
-fail_id:
-       return ret;
 }
 
 #ifdef CONFIG_PM_SLEEP
index a944338..9e29b13 100644 (file)
@@ -521,11 +521,6 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
        int ret;
        int i;
 
-       if (off >= EEPROM_SIZE)
-               return 0;
-       if (off + count > EEPROM_SIZE)
-               count = EEPROM_SIZE - off;
-
        for (i = 0; i < count; i++) {
                ec_byte = EEPROM_START + off + i;
                ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1);
@@ -545,7 +540,7 @@ static struct bin_attribute olpc_bat_eeprom = {
                .name = "eeprom",
                .mode = S_IRUGO,
        },
-       .size = 0,
+       .size = EEPROM_SIZE,
        .read = olpc_bat_eeprom_read,
 };
 
index cc0893f..3a45cc0 100644 (file)
@@ -1244,7 +1244,6 @@ static struct i2c_driver pm2xxx_charger_driver = {
        .remove = pm2xxx_wall_charger_remove,
        .driver = {
                .name = "pm2xxx-wall_charger",
-               .owner = THIS_MODULE,
                .pm = PM2XXX_PM_OPS,
        },
        .id_table = pm2xxx_id,
index 17d93a7..5a0189b 100644 (file)
@@ -166,5 +166,12 @@ config POWER_RESET_RMOBILE
        help
          Reboot support for Renesas R-Mobile and SH-Mobile SoCs.
 
+config POWER_RESET_ZX
+       tristate "ZTE SoCs reset driver"
+       depends on ARCH_ZX || COMPILE_TEST
+       depends on HAS_IOMEM
+       help
+         Reboot support for ZTE SoCs.
+
 endif
 
index dbe06c3..096fa67 100644 (file)
@@ -19,3 +19,4 @@ obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
 obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
 obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
 obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
+obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
index 36dc52f..c378d4e 100644 (file)
@@ -123,6 +123,15 @@ static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode,
        return NOTIFY_DONE;
 }
 
+static int sama5d3_restart(struct notifier_block *this, unsigned long mode,
+                          void *cmd)
+{
+       writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST),
+              at91_rstc_base);
+
+       return NOTIFY_DONE;
+}
+
 static void __init at91_reset_status(struct platform_device *pdev)
 {
        u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
@@ -155,13 +164,13 @@ static void __init at91_reset_status(struct platform_device *pdev)
 static const struct of_device_id at91_ramc_of_match[] = {
        { .compatible = "atmel,at91sam9260-sdramc", },
        { .compatible = "atmel,at91sam9g45-ddramc", },
-       { .compatible = "atmel,sama5d3-ddramc", },
        { /* sentinel */ }
 };
 
 static const struct of_device_id at91_reset_of_match[] = {
        { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
        { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
+       { .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart },
        { /* sentinel */ }
 };
 
@@ -181,13 +190,16 @@ static int at91_reset_of_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       for_each_matching_node(np, at91_ramc_of_match) {
-               at91_ramc_base[idx] = of_iomap(np, 0);
-               if (!at91_ramc_base[idx]) {
-                       dev_err(&pdev->dev, "Could not map ram controller address\n");
-                       return -ENODEV;
+       if (!of_device_is_compatible(pdev->dev.of_node, "atmel,sama5d3-rstc")) {
+               /* we need to shutdown the ddr controller, so get ramc base */
+               for_each_matching_node(np, at91_ramc_of_match) {
+                       at91_ramc_base[idx] = of_iomap(np, 0);
+                       if (!at91_ramc_base[idx]) {
+                               dev_err(&pdev->dev, "Could not map ram controller address\n");
+                               return -ENODEV;
+                       }
+                       idx++;
                }
-               idx++;
        }
 
        match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
diff --git a/drivers/power/reset/zx-reboot.c b/drivers/power/reset/zx-reboot.c
new file mode 100644 (file)
index 0000000..a5b0096
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * ZTE zx296702 SoC reset code
+ *
+ * Copyright (c) 2015 Linaro Ltd.
+ *
+ * Author: Jun Nie <jun.nie@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+
+static void __iomem *base;
+static void __iomem *pcu_base;
+
+static int zx_restart_handler(struct notifier_block *this,
+                             unsigned long mode, void *cmd)
+{
+       writel_relaxed(1, base + 0xb0);
+       writel_relaxed(1, pcu_base + 0x34);
+
+       mdelay(50);
+       pr_emerg("Unable to restart system\n");
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block zx_restart_nb = {
+       .notifier_call = zx_restart_handler,
+       .priority = 128,
+};
+
+static int zx_reboot_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       int err;
+
+       base = of_iomap(np, 0);
+       if (!base) {
+               WARN(1, "failed to map base address");
+               return -ENODEV;
+       }
+
+       np = of_find_compatible_node(NULL, NULL, "zte,zx296702-pcu");
+       pcu_base = of_iomap(np, 0);
+       if (!pcu_base) {
+               iounmap(base);
+               WARN(1, "failed to map pcu_base address");
+               return -ENODEV;
+       }
+
+       err = register_restart_handler(&zx_restart_nb);
+       if (err)
+               dev_err(&pdev->dev, "Register restart handler failed(err=%d)\n",
+                       err);
+
+       return err;
+}
+
+static const struct of_device_id zx_reboot_of_match[] = {
+       { .compatible = "zte,sysctrl" },
+       {}
+};
+
+static struct platform_driver zx_reboot_driver = {
+       .probe = zx_reboot_probe,
+       .driver = {
+               .name = "zx-reboot",
+               .of_match_table = zx_reboot_of_match,
+       },
+};
+module_platform_driver(zx_reboot_driver);
index a7a6877..bcdd830 100644 (file)
@@ -165,7 +165,7 @@ static const struct i2c_device_id rt5033_battery_id[] = {
        { "rt5033-battery", },
        { }
 };
-MODULE_DEVICE_TABLE(platform, rt5033_battery_id);
+MODULE_DEVICE_TABLE(i2c, rt5033_battery_id);
 
 static struct i2c_driver rt5033_battery_driver = {
        .driver = {
index 08baac6..a49a9d4 100644 (file)
@@ -973,7 +973,6 @@ static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info,
 
        if (irq2 & GET_MASK(F_CHRVPI)) {
                dev_dbg(dev, "Charger fault occurred\n");
-               alert_userspace = true;
                /*
                 * CHRVPI bit is set in 2 cases:
                 * 1. when the power source is connected to the charger.
@@ -981,6 +980,9 @@ static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info,
                 * To identify the case, PWR_RDY bit is checked. Because
                 * PWR_RDY bit is set / cleared after CHRVPI interrupt is
                 * triggered, it is used delayed_work to later read PWR_RDY bit.
+                * Also, do not set to true alert_userspace, because there is no
+                * need to notify userspace when CHRVPI interrupt has occurred.
+                * Userspace will be notified after PWR_RDY bit is read.
                 */
                queue_delayed_work(system_power_efficient_wq,
                                   &info->pwr_rdy_work,
@@ -1178,7 +1180,7 @@ static irqreturn_t rt9455_irq_handler_thread(int irq, void *data)
                /*
                 * Sometimes, an interrupt occurs while rt9455_probe() function
                 * is executing and power_supply_register() is not yet called.
-                * Do not call power_supply_charged() in this case.
+                * Do not call power_supply_changed() in this case.
                 */
                if (info->charger)
                        power_supply_changed(info->charger);
@@ -1478,6 +1480,11 @@ static void rt9455_pwr_rdy_work_callback(struct work_struct *work)
                                   RT9455_MAX_CHARGING_TIME * HZ);
                break;
        }
+       /*
+        * Notify userspace that the charger has been either connected to or
+        * disconnected from the power source.
+        */
+       power_supply_changed(info->charger);
 }
 
 static void rt9455_max_charging_time_work_callback(struct work_struct *work)
@@ -1533,6 +1540,11 @@ static void rt9455_batt_presence_work_callback(struct work_struct *work)
                        if (ret)
                                dev_err(dev, "Failed to unmask BATAB interrupt\n");
                }
+               /*
+                * Notify userspace that the battery is now connected to the
+                * charger.
+                */
+               power_supply_changed(info->charger);
        }
 }
 
index ac62069..af9383d 100644 (file)
@@ -215,7 +215,7 @@ static int rx51_battery_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, di);
 
        di->dev = &pdev->dev;
-       di->bat_desc.name = dev_name(&pdev->dev);
+       di->bat_desc.name = "rx51-battery";
        di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
        di->bat_desc.properties = rx51_battery_props;
        di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props);
index 022b891..f4f2c1f 100644 (file)
 #include <linux/power_supply.h>
 #include <linux/notifier.h>
 #include <linux/usb/otg.h>
-#include <linux/regulator/machine.h>
+#include <linux/i2c/twl4030-madc.h>
 
+#define TWL4030_BCIMDEN                0x00
+#define TWL4030_BCIMDKEY       0x01
 #define TWL4030_BCIMSTATEC     0x02
 #define TWL4030_BCIICHG                0x08
 #define TWL4030_BCIVAC         0x0a
 #define TWL4030_BCIMFSTS4      0x10
 #define TWL4030_BCICTL1                0x23
 #define TWL4030_BB_CFG         0x12
+#define TWL4030_BCIIREF1       0x27
+#define TWL4030_BCIIREF2       0x28
+#define TWL4030_BCIMFKEY       0x11
+#define TWL4030_BCIMFEN3       0x14
+#define TWL4030_BCIMFTH8       0x1d
+#define TWL4030_BCIMFTH9       0x1e
+#define TWL4030_BCIWDKEY       0x21
 
 #define TWL4030_BCIMFSTS1      0x01
 
 #define TWL4030_BCIAUTOWEN     BIT(5)
 #define TWL4030_CONFIG_DONE    BIT(4)
+#define TWL4030_CVENAC         BIT(2)
 #define TWL4030_BCIAUTOUSB     BIT(1)
 #define TWL4030_BCIAUTOAC      BIT(0)
 #define TWL4030_CGAIN          BIT(5)
 #define TWL4030_MSTATEC_COMPLETE1      0x0b
 #define TWL4030_MSTATEC_COMPLETE4      0x0e
 
+#if IS_ENABLED(CONFIG_TWL4030_MADC)
+/*
+ * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
+ * then AC is available.
+ */
+static inline int ac_available(void)
+{
+       return twl4030_get_madc_conversion(11) > 4500;
+}
+#else
+static inline int ac_available(void)
+{
+       return 0;
+}
+#endif
 static bool allow_usb;
 module_param(allow_usb, bool, 0644);
 MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
@@ -94,12 +119,39 @@ struct twl4030_bci {
        struct work_struct      work;
        int                     irq_chg;
        int                     irq_bci;
-       struct regulator        *usb_reg;
        int                     usb_enabled;
 
+       /*
+        * ichg_* and *_cur values in uA. If any are 'large', we set
+        * CGAIN to '1' which doubles the range for half the
+        * precision.
+        */
+       unsigned int            ichg_eoc, ichg_lo, ichg_hi;
+       unsigned int            usb_cur, ac_cur;
+       bool                    ac_is_active;
+       int                     usb_mode, ac_mode; /* charging mode requested */
+#define        CHARGE_OFF      0
+#define        CHARGE_AUTO     1
+#define        CHARGE_LINEAR   2
+
+       /* When setting the USB current we slowly increase the
+        * requested current until target is reached or the voltage
+        * drops below 4.75V.  In the latter case we step back one
+        * step.
+        */
+       unsigned int            usb_cur_target;
+       struct delayed_work     current_worker;
+#define        USB_CUR_STEP    20000   /* 20mA at a time */
+#define        USB_MIN_VOLT    4750000 /* 4.75V */
+#define        USB_CUR_DELAY   msecs_to_jiffies(100)
+#define        USB_MAX_CURRENT 1700000 /* TWL4030 caps at 1.7A */
+
        unsigned long           event;
 };
 
+/* strings for 'usb_mode' values */
+static char *modes[] = { "off", "auto", "continuous" };
+
 /*
  * clear and set bits on an given register on a given module
  */
@@ -180,27 +232,233 @@ static int twl4030_is_battery_present(struct twl4030_bci *bci)
 }
 
 /*
- * Check if VBUS power is present
+ * TI provided formulas:
+ * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
+ * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
+ * Here we use integer approximation of:
+ * CGAIN == 0: val * 1.6618 - 0.85 * 1000
+ * CGAIN == 1: (val * 1.6618 - 0.85 * 1000) * 2
+ */
+/*
+ * convert twl register value for currents into uA
+ */
+static int regval2ua(int regval, bool cgain)
+{
+       if (cgain)
+               return (regval * 16618 - 8500 * 1000) / 5;
+       else
+               return (regval * 16618 - 8500 * 1000) / 10;
+}
+
+/*
+ * convert uA currents into twl register value
  */
-static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
+static int ua2regval(int ua, bool cgain)
 {
        int ret;
-       u8 hwsts;
+       if (cgain)
+               ua /= 2;
+       ret = (ua * 10 + 8500 * 1000) / 16618;
+       /* rounding problems */
+       if (ret < 512)
+               ret = 512;
+       return ret;
+}
 
-       ret = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &hwsts,
-                             TWL4030_PM_MASTER_STS_HW_CONDITIONS);
-       if (ret < 0)
-               return 0;
+static int twl4030_charger_update_current(struct twl4030_bci *bci)
+{
+       int status;
+       int cur;
+       unsigned reg, cur_reg;
+       u8 bcictl1, oldreg, fullreg;
+       bool cgain = false;
+       u8 boot_bci;
 
-       dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts);
+       /*
+        * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
+        * and AC is enabled, set current for 'ac'
+        */
+       if (ac_available()) {
+               cur = bci->ac_cur;
+               bci->ac_is_active = true;
+       } else {
+               cur = bci->usb_cur;
+               bci->ac_is_active = false;
+               if (cur > bci->usb_cur_target) {
+                       cur = bci->usb_cur_target;
+                       bci->usb_cur = cur;
+               }
+               if (cur < bci->usb_cur_target)
+                       schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY);
+       }
+
+       /* First, check thresholds and see if cgain is needed */
+       if (bci->ichg_eoc >= 200000)
+               cgain = true;
+       if (bci->ichg_lo >= 400000)
+               cgain = true;
+       if (bci->ichg_hi >= 820000)
+               cgain = true;
+       if (cur > 852000)
+               cgain = true;
+
+       status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
+       if (status < 0)
+               return status;
+       if (twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &boot_bci,
+                           TWL4030_PM_MASTER_BOOT_BCI) < 0)
+               boot_bci = 0;
+       boot_bci &= 7;
+
+       if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN))
+               /* Need to turn for charging while we change the
+                * CGAIN bit.  Leave it off while everything is
+                * updated.
+                */
+               twl4030_clear_set_boot_bci(boot_bci, 0);
+
+       /*
+        * For ichg_eoc, the hardware only supports reg values matching
+        * 100XXXX000, and requires the XXXX be stored in the high nibble
+        * of TWL4030_BCIMFTH8.
+        */
+       reg = ua2regval(bci->ichg_eoc, cgain);
+       if (reg > 0x278)
+               reg = 0x278;
+       if (reg < 0x200)
+               reg = 0x200;
+       reg = (reg >> 3) & 0xf;
+       fullreg = reg << 4;
+
+       /*
+        * For ichg_lo, reg value must match 10XXXX0000.
+        * XXXX is stored in low nibble of TWL4030_BCIMFTH8.
+        */
+       reg = ua2regval(bci->ichg_lo, cgain);
+       if (reg > 0x2F0)
+               reg = 0x2F0;
+       if (reg < 0x200)
+               reg = 0x200;
+       reg = (reg >> 4) & 0xf;
+       fullreg |= reg;
+
+       /* ichg_eoc and ichg_lo live in same register */
+       status = twl4030_bci_read(TWL4030_BCIMFTH8, &oldreg);
+       if (status < 0)
+               return status;
+       if (oldreg != fullreg) {
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xF4,
+                                         TWL4030_BCIMFKEY);
+               if (status < 0)
+                       return status;
+               twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                fullreg, TWL4030_BCIMFTH8);
+       }
 
-       /* in case we also have STS_USB_ID, VBUS is driven by TWL itself */
-       if ((hwsts & TWL4030_STS_VBUS) && !(hwsts & TWL4030_STS_USB_ID))
-               return 1;
+       /* ichg_hi threshold must be 1XXXX01100 (I think) */
+       reg = ua2regval(bci->ichg_hi, cgain);
+       if (reg > 0x3E0)
+               reg = 0x3E0;
+       if (reg < 0x200)
+               reg = 0x200;
+       fullreg = (reg >> 5) & 0xF;
+       fullreg <<= 4;
+       status = twl4030_bci_read(TWL4030_BCIMFTH9, &oldreg);
+       if (status < 0)
+               return status;
+       if ((oldreg & 0xF0) != fullreg) {
+               fullreg |= (oldreg & 0x0F);
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
+                                         TWL4030_BCIMFKEY);
+               if (status < 0)
+                       return status;
+               twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                fullreg, TWL4030_BCIMFTH9);
+       }
 
+       /*
+        * And finally, set the current.  This is stored in
+        * two registers.
+        */
+       reg = ua2regval(cur, cgain);
+       /* we have only 10 bits */
+       if (reg > 0x3ff)
+               reg = 0x3ff;
+       status = twl4030_bci_read(TWL4030_BCIIREF1, &oldreg);
+       if (status < 0)
+               return status;
+       cur_reg = oldreg;
+       status = twl4030_bci_read(TWL4030_BCIIREF2, &oldreg);
+       if (status < 0)
+               return status;
+       cur_reg |= oldreg << 8;
+       if (reg != oldreg) {
+               /* disable write protection for one write access for
+                * BCIIREF */
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
+                                         TWL4030_BCIMFKEY);
+               if (status < 0)
+                       return status;
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                         (reg & 0x100) ? 3 : 2,
+                                         TWL4030_BCIIREF2);
+               if (status < 0)
+                       return status;
+               /* disable write protection for one write access for
+                * BCIIREF */
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
+                                         TWL4030_BCIMFKEY);
+               if (status < 0)
+                       return status;
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                         reg & 0xff,
+                                         TWL4030_BCIIREF1);
+       }
+       if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) {
+               /* Flip CGAIN and re-enable charging */
+               bcictl1 ^= TWL4030_CGAIN;
+               twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                bcictl1, TWL4030_BCICTL1);
+               twl4030_clear_set_boot_bci(0, boot_bci);
+       }
        return 0;
 }
 
+static int twl4030_charger_get_current(void);
+
+static void twl4030_current_worker(struct work_struct *data)
+{
+       int v, curr;
+       int res;
+       struct twl4030_bci *bci = container_of(data, struct twl4030_bci,
+                                              current_worker.work);
+
+       res = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
+       if (res < 0)
+               v = 0;
+       else
+               /* BCIVBUS uses ADCIN8, 7/1023 V/step */
+               v = res * 6843;
+       curr = twl4030_charger_get_current();
+
+       dev_dbg(bci->dev, "v=%d cur=%d limit=%d target=%d\n", v, curr,
+               bci->usb_cur, bci->usb_cur_target);
+
+       if (v < USB_MIN_VOLT) {
+               /* Back up and stop adjusting. */
+               bci->usb_cur -= USB_CUR_STEP;
+               bci->usb_cur_target = bci->usb_cur;
+       } else if (bci->usb_cur >= bci->usb_cur_target ||
+                  bci->usb_cur + USB_CUR_STEP > USB_MAX_CURRENT) {
+               /* Reached target and voltage is OK - stop */
+               return;
+       } else {
+               bci->usb_cur += USB_CUR_STEP;
+               schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY);
+       }
+       twl4030_charger_update_current(bci);
+}
+
 /*
  * Enable/Disable USB Charge functionality.
  */
@@ -208,45 +466,60 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
 {
        int ret;
 
-       if (enable) {
-               /* Check for USB charger connected */
-               if (!twl4030_bci_have_vbus(bci))
-                       return -ENODEV;
+       if (bci->usb_mode == CHARGE_OFF)
+               enable = false;
+       if (enable && !IS_ERR_OR_NULL(bci->transceiver)) {
 
-               /*
-                * Until we can find out what current the device can provide,
-                * require a module param to enable USB charging.
-                */
-               if (!allow_usb) {
-                       dev_warn(bci->dev, "USB charging is disabled.\n");
-                       return -EACCES;
-               }
+               twl4030_charger_update_current(bci);
 
-               /* Need to keep regulator on */
+               /* Need to keep phy powered */
                if (!bci->usb_enabled) {
-                       ret = regulator_enable(bci->usb_reg);
-                       if (ret) {
-                               dev_err(bci->dev,
-                                       "Failed to enable regulator\n");
-                               return ret;
-                       }
+                       pm_runtime_get_sync(bci->transceiver->dev);
                        bci->usb_enabled = 1;
                }
 
-               /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
-               ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
-               if (ret < 0)
-                       return ret;
+               if (bci->usb_mode == CHARGE_AUTO)
+                       /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
+                       ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
 
                /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
                ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0,
                        TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
+               if (bci->usb_mode == CHARGE_LINEAR) {
+                       twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC|TWL4030_CVENAC, 0);
+                       /* Watch dog key: WOVF acknowledge */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x33,
+                                              TWL4030_BCIWDKEY);
+                       /* 0x24 + EKEY6: off mode */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a,
+                                              TWL4030_BCIMDKEY);
+                       /* EKEY2: Linear charge: USB path */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x26,
+                                              TWL4030_BCIMDKEY);
+                       /* WDKEY5: stop watchdog count */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf3,
+                                              TWL4030_BCIWDKEY);
+                       /* enable MFEN3 access */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x9c,
+                                              TWL4030_BCIMFKEY);
+                        /* ICHGEOCEN - end-of-charge monitor (current < 80mA)
+                         *                      (charging continues)
+                         * ICHGLOWEN - current level monitor (charge continues)
+                         * don't monitor over-current or heat save
+                         */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf0,
+                                              TWL4030_BCIMFEN3);
+               }
        } else {
                ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
+               ret |= twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a,
+                                       TWL4030_BCIMDKEY);
                if (bci->usb_enabled) {
-                       regulator_disable(bci->usb_reg);
+                       pm_runtime_mark_last_busy(bci->transceiver->dev);
+                       pm_runtime_put_autosuspend(bci->transceiver->dev);
                        bci->usb_enabled = 0;
                }
+               bci->usb_cur = 0;
        }
 
        return ret;
@@ -255,10 +528,13 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
 /*
  * Enable/Disable AC Charge funtionality.
  */
-static int twl4030_charger_enable_ac(bool enable)
+static int twl4030_charger_enable_ac(struct twl4030_bci *bci, bool enable)
 {
        int ret;
 
+       if (bci->ac_mode == CHARGE_OFF)
+               enable = false;
+
        if (enable)
                ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC);
        else
@@ -318,6 +594,9 @@ static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
        struct twl4030_bci *bci = arg;
 
        dev_dbg(bci->dev, "CHG_PRES irq\n");
+       /* reset current on each 'plug' event */
+       bci->ac_cur = 500000;
+       twl4030_charger_update_current(bci);
        power_supply_changed(bci->ac);
        power_supply_changed(bci->usb);
 
@@ -350,6 +629,7 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
                power_supply_changed(bci->ac);
                power_supply_changed(bci->usb);
        }
+       twl4030_charger_update_current(bci);
 
        /* various monitoring events, for now we just log them here */
        if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
@@ -370,6 +650,63 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
        return IRQ_HANDLED;
 }
 
+/*
+ * Provide "max_current" attribute in sysfs.
+ */
+static ssize_t
+twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr,
+       const char *buf, size_t n)
+{
+       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
+       int cur = 0;
+       int status = 0;
+       status = kstrtoint(buf, 10, &cur);
+       if (status)
+               return status;
+       if (cur < 0)
+               return -EINVAL;
+       if (dev == &bci->ac->dev)
+               bci->ac_cur = cur;
+       else
+               bci->usb_cur_target = cur;
+
+       twl4030_charger_update_current(bci);
+       return n;
+}
+
+/*
+ * sysfs max_current show
+ */
+static ssize_t twl4030_bci_max_current_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int status = 0;
+       int cur = -1;
+       u8 bcictl1;
+       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
+
+       if (dev == &bci->ac->dev) {
+               if (!bci->ac_is_active)
+                       cur = bci->ac_cur;
+       } else {
+               if (bci->ac_is_active)
+                       cur = bci->usb_cur_target;
+       }
+       if (cur < 0) {
+               cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
+               if (cur < 0)
+                       return cur;
+               status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
+               if (status < 0)
+                       return status;
+               cur = regval2ua(cur, bcictl1 & TWL4030_CGAIN);
+       }
+       return scnprintf(buf, PAGE_SIZE, "%u\n", cur);
+}
+
+static DEVICE_ATTR(max_current, 0644, twl4030_bci_max_current_show,
+                       twl4030_bci_max_current_store);
+
 static void twl4030_bci_usb_work(struct work_struct *data)
 {
        struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work);
@@ -392,6 +729,12 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
 
        dev_dbg(bci->dev, "OTG notify %lu\n", val);
 
+       /* reset current on each 'plug' event */
+       if (allow_usb)
+               bci->usb_cur_target = 500000;
+       else
+               bci->usb_cur_target = 100000;
+
        bci->event = val;
        schedule_work(&bci->work);
 
@@ -399,13 +742,66 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
 }
 
 /*
- * TI provided formulas:
- * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
- * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
- * Here we use integer approximation of:
- * CGAIN == 0: val * 1.6618 - 0.85
- * CGAIN == 1: (val * 1.6618 - 0.85) * 2
+ * sysfs charger enabled store
+ */
+static ssize_t
+twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t n)
+{
+       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
+       int mode;
+       int status;
+
+       if (sysfs_streq(buf, modes[0]))
+               mode = 0;
+       else if (sysfs_streq(buf, modes[1]))
+               mode = 1;
+       else if (sysfs_streq(buf, modes[2]))
+               mode = 2;
+       else
+               return -EINVAL;
+       if (dev == &bci->ac->dev) {
+               if (mode == 2)
+                       return -EINVAL;
+               twl4030_charger_enable_ac(bci, false);
+               bci->ac_mode = mode;
+               status = twl4030_charger_enable_ac(bci, true);
+       } else {
+               twl4030_charger_enable_usb(bci, false);
+               bci->usb_mode = mode;
+               status = twl4030_charger_enable_usb(bci, true);
+       }
+       return (status == 0) ? n : status;
+}
+
+/*
+ * sysfs charger enabled show
  */
+static ssize_t
+twl4030_bci_mode_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
+       int len = 0;
+       int i;
+       int mode = bci->usb_mode;
+
+       if (dev == &bci->ac->dev)
+               mode = bci->ac_mode;
+
+       for (i = 0; i < ARRAY_SIZE(modes); i++)
+               if (mode == i)
+                       len += snprintf(buf+len, PAGE_SIZE-len,
+                                       "[%s] ", modes[i]);
+               else
+                       len += snprintf(buf+len, PAGE_SIZE-len,
+                                       "%s ", modes[i]);
+       buf[len-1] = '\n';
+       return len;
+}
+static DEVICE_ATTR(mode, 0644, twl4030_bci_mode_show,
+                  twl4030_bci_mode_store);
+
 static int twl4030_charger_get_current(void)
 {
        int curr;
@@ -420,11 +816,7 @@ static int twl4030_charger_get_current(void)
        if (ret)
                return ret;
 
-       ret = (curr * 16618 - 850 * 10000) / 10;
-       if (bcictl1 & TWL4030_CGAIN)
-               ret *= 2;
-
-       return ret;
+       return regval2ua(curr, bcictl1 & TWL4030_CGAIN);
 }
 
 /*
@@ -476,6 +868,17 @@ static int twl4030_bci_get_property(struct power_supply *psy,
                is_charging = state & TWL4030_MSTATEC_USB;
        else
                is_charging = state & TWL4030_MSTATEC_AC;
+       if (!is_charging) {
+               u8 s;
+               twl4030_bci_read(TWL4030_BCIMDEN, &s);
+               if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
+                       is_charging = s & 1;
+               else
+                       is_charging = s & 2;
+               if (is_charging)
+                       /* A little white lie */
+                       state = TWL4030_MSTATEC_QUICK1;
+       }
 
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
@@ -574,20 +977,31 @@ static const struct power_supply_desc twl4030_bci_usb_desc = {
        .get_property   = twl4030_bci_get_property,
 };
 
-static int __init twl4030_bci_probe(struct platform_device *pdev)
+static int twl4030_bci_probe(struct platform_device *pdev)
 {
        struct twl4030_bci *bci;
        const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
        int ret;
        u32 reg;
 
-       bci = kzalloc(sizeof(*bci), GFP_KERNEL);
+       bci = devm_kzalloc(&pdev->dev, sizeof(*bci), GFP_KERNEL);
        if (bci == NULL)
                return -ENOMEM;
 
        if (!pdata)
                pdata = twl4030_bci_parse_dt(&pdev->dev);
 
+       bci->ichg_eoc = 80100; /* Stop charging when current drops to here */
+       bci->ichg_lo = 241000; /* Low threshold */
+       bci->ichg_hi = 500000; /* High threshold */
+       bci->ac_cur = 500000; /* 500mA */
+       if (allow_usb)
+               bci->usb_cur_target = 500000;  /* 500mA */
+       else
+               bci->usb_cur_target = 100000;  /* 100mA */
+       bci->usb_mode = CHARGE_AUTO;
+       bci->ac_mode = CHARGE_AUTO;
+
        bci->dev = &pdev->dev;
        bci->irq_chg = platform_get_irq(pdev, 0);
        bci->irq_bci = platform_get_irq(pdev, 1);
@@ -596,47 +1010,46 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
        ret = twl4030_is_battery_present(bci);
        if  (ret) {
                dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret);
-               goto fail_no_battery;
+               return ret;
        }
 
        platform_set_drvdata(pdev, bci);
 
-       bci->ac = power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
-                                       NULL);
+       bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
+                                            NULL);
        if (IS_ERR(bci->ac)) {
                ret = PTR_ERR(bci->ac);
                dev_err(&pdev->dev, "failed to register ac: %d\n", ret);
-               goto fail_register_ac;
+               return ret;
        }
 
-       bci->usb_reg = regulator_get(bci->dev, "bci3v1");
-
-       bci->usb = power_supply_register(&pdev->dev, &twl4030_bci_usb_desc,
-                                        NULL);
+       bci->usb = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc,
+                                             NULL);
        if (IS_ERR(bci->usb)) {
                ret = PTR_ERR(bci->usb);
                dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
-               goto fail_register_usb;
+               return ret;
        }
 
-       ret = request_threaded_irq(bci->irq_chg, NULL,
+       ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL,
                        twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name,
                        bci);
        if (ret < 0) {
                dev_err(&pdev->dev, "could not request irq %d, status %d\n",
                        bci->irq_chg, ret);
-               goto fail_chg_irq;
+               return ret;
        }
 
-       ret = request_threaded_irq(bci->irq_bci, NULL,
+       ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL,
                        twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci);
        if (ret < 0) {
                dev_err(&pdev->dev, "could not request irq %d, status %d\n",
                        bci->irq_bci, ret);
-               goto fail_bci_irq;
+               return ret;
        }
 
        INIT_WORK(&bci->work, twl4030_bci_usb_work);
+       INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
 
        bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
        if (bci->dev->of_node) {
@@ -644,9 +1057,13 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
 
                phynode = of_find_compatible_node(bci->dev->of_node->parent,
                                                  NULL, "ti,twl4030-usb");
-               if (phynode)
+               if (phynode) {
                        bci->transceiver = devm_usb_get_phy_by_node(
                                bci->dev, phynode, &bci->usb_nb);
+                       if (IS_ERR(bci->transceiver) &&
+                           PTR_ERR(bci->transceiver) == -EPROBE_DEFER)
+                               return -EPROBE_DEFER;
+               }
        }
 
        /* Enable interrupts now. */
@@ -656,7 +1073,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
                               TWL4030_INTERRUPTS_BCIIMR1A);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
-               goto fail_unmask_interrupts;
+               return ret;
        }
 
        reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
@@ -665,8 +1082,23 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
        if (ret < 0)
                dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
 
-       twl4030_charger_enable_ac(true);
-       twl4030_charger_enable_usb(bci, true);
+       twl4030_charger_update_current(bci);
+       if (device_create_file(&bci->usb->dev, &dev_attr_max_current))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+       if (device_create_file(&bci->usb->dev, &dev_attr_mode))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+       if (device_create_file(&bci->ac->dev, &dev_attr_mode))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+       if (device_create_file(&bci->ac->dev, &dev_attr_max_current))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+
+       twl4030_charger_enable_ac(bci, true);
+       if (!IS_ERR_OR_NULL(bci->transceiver))
+               twl4030_bci_usb_ncb(&bci->usb_nb,
+                                   bci->transceiver->last_event,
+                                   NULL);
+       else
+               twl4030_charger_enable_usb(bci, false);
        if (pdata)
                twl4030_charger_enable_backup(pdata->bb_uvolt,
                                              pdata->bb_uamp);
@@ -674,42 +1106,26 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
                twl4030_charger_enable_backup(0, 0);
 
        return 0;
-
-fail_unmask_interrupts:
-       free_irq(bci->irq_bci, bci);
-fail_bci_irq:
-       free_irq(bci->irq_chg, bci);
-fail_chg_irq:
-       power_supply_unregister(bci->usb);
-fail_register_usb:
-       power_supply_unregister(bci->ac);
-fail_register_ac:
-fail_no_battery:
-       kfree(bci);
-
-       return ret;
 }
 
 static int __exit twl4030_bci_remove(struct platform_device *pdev)
 {
        struct twl4030_bci *bci = platform_get_drvdata(pdev);
 
-       twl4030_charger_enable_ac(false);
+       twl4030_charger_enable_ac(bci, false);
        twl4030_charger_enable_usb(bci, false);
        twl4030_charger_enable_backup(0, 0);
 
+       device_remove_file(&bci->usb->dev, &dev_attr_max_current);
+       device_remove_file(&bci->usb->dev, &dev_attr_mode);
+       device_remove_file(&bci->ac->dev, &dev_attr_max_current);
+       device_remove_file(&bci->ac->dev, &dev_attr_mode);
        /* mask interrupts */
        twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
                         TWL4030_INTERRUPTS_BCIIMR1A);
        twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
                         TWL4030_INTERRUPTS_BCIIMR2A);
 
-       free_irq(bci->irq_bci, bci);
-       free_irq(bci->irq_chg, bci);
-       power_supply_unregister(bci->usb);
-       power_supply_unregister(bci->ac);
-       kfree(bci);
-
        return 0;
 }
 
@@ -720,14 +1136,14 @@ static const struct of_device_id twl_bci_of_match[] = {
 MODULE_DEVICE_TABLE(of, twl_bci_of_match);
 
 static struct platform_driver twl4030_bci_driver = {
+       .probe = twl4030_bci_probe,
        .driver = {
                .name   = "twl4030_bci",
                .of_match_table = of_match_ptr(twl_bci_of_match),
        },
        .remove = __exit_p(twl4030_bci_remove),
 };
-
-module_platform_driver_probe(twl4030_bci_driver, twl4030_bci_probe);
+module_platform_driver(twl4030_bci_driver);
 
 MODULE_AUTHOR("GraÅžvydas Ignotas");
 MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");