diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-power-twl4030 | 45 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/arm/atmel-at91.txt | 2 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | drivers/mfd/twl-core.c | 9 | ||||
-rw-r--r-- | drivers/power/Kconfig | 17 | ||||
-rw-r--r-- | drivers/power/bq2415x_charger.c | 143 | ||||
-rw-r--r-- | drivers/power/bq24190_charger.c | 4 | ||||
-rw-r--r-- | drivers/power/bq24735-charger.c | 52 | ||||
-rw-r--r-- | drivers/power/bq27x00_battery.c | 123 | ||||
-rw-r--r-- | drivers/power/ds2780_battery.c | 20 | ||||
-rw-r--r-- | drivers/power/ds2781_battery.c | 8 | ||||
-rw-r--r-- | drivers/power/ltc2941-battery-gauge.c | 54 | ||||
-rw-r--r-- | drivers/power/olpc_battery.c | 7 | ||||
-rw-r--r-- | drivers/power/pm2301_charger.c | 1 | ||||
-rw-r--r-- | drivers/power/reset/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/reset/at91-reset.c | 26 | ||||
-rw-r--r-- | drivers/power/reset/zx-reboot.c | 80 | ||||
-rw-r--r-- | drivers/power/rt5033_battery.c | 2 | ||||
-rw-r--r-- | drivers/power/rt9455_charger.c | 16 | ||||
-rw-r--r-- | drivers/power/rx51_battery.c | 2 | ||||
-rw-r--r-- | drivers/power/twl4030_charger.c | 598 |
22 files changed, 862 insertions, 356 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-power-twl4030 b/Documentation/ABI/testing/sysfs-class-power-twl4030 new file mode 100644 index 000000000000..be26af0f1895 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-power-twl4030 @@ -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. diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index 424ac8cbfa08..dd998b9c0433 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -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: diff --git a/MAINTAINERS b/MAINTAINERS index 8b3115cd762e..d53955790f7e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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> diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 489674a2497e..831696ee2472 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -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); } } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 08beeed5485d..f8758d6febf8 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -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. diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index e98dcb661cc9..ec212b5be755 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -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); diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index 052db78c3736..469a452cbe10 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -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), }, diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c index 961a18930027..eb2b3689de97 100644 --- a/drivers/power/bq24735-charger.c +++ b/drivers/power/bq24735-charger.c @@ -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,34 +363,11 @@ 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; } static const struct i2c_device_id bq24735_charger_id[] = { @@ -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, }; diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index b6b98378faa3..8287261fd978 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -39,47 +39,49 @@ #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; } diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c index a7a0427343f3..d3743d0ad55b 100644 --- a/drivers/power/ds2780_battery.c +++ b/drivers/power/ds2780_battery.c @@ -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, }; diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c index 56d583dae908..c3680024f399 100644 --- a/drivers/power/ds2781_battery.c +++ b/drivers/power/ds2781_battery.c @@ -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) diff --git a/drivers/power/ltc2941-battery-gauge.c b/drivers/power/ltc2941-battery-gauge.c index daeb0860736c..4adf2ba021ce 100644 --- a/drivers/power/ltc2941-battery-gauge.c +++ b/drivers/power/ltc2941-battery-gauge.c @@ -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(<c294x_lock); - idr_remove(<c294x_id, info->id); - mutex_unlock(<c294x_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(<c294x_lock); - ret = idr_alloc(<c294x_id, client, 0, 0, GFP_KERNEL); - mutex_unlock(<c294x_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(<c294x_lock); - idr_remove(<c294x_id, num); - mutex_unlock(<c294x_lock); -fail_id: - return ret; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index a944338a39de..9e29b1321648 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -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, }; diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index cc0893ffbf7e..3a45cc0c4dce 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -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, diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 17d93a73c513..5a0189bf19bb 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -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 diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index dbe06c368743..096fa67047f6 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -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 diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 36dc52fb2ec8..c378d4ec826f 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -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 index 000000000000..a5b009673d0e --- /dev/null +++ b/drivers/power/reset/zx-reboot.c @@ -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); diff --git a/drivers/power/rt5033_battery.c b/drivers/power/rt5033_battery.c index a7a6877b4e16..bcdd83048492 100644 --- a/drivers/power/rt5033_battery.c +++ b/drivers/power/rt5033_battery.c @@ -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 = { diff --git a/drivers/power/rt9455_charger.c b/drivers/power/rt9455_charger.c index 08baac6e3ada..a49a9d44bdda 100644 --- a/drivers/power/rt9455_charger.c +++ b/drivers/power/rt9455_charger.c @@ -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); } } diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c index ac6206951d58..af9383d23d12 100644 --- a/drivers/power/rx51_battery.c +++ b/drivers/power/rx51_battery.c @@ -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); diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 022b8910e443..f4f2c1f76c32 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -22,8 +22,10 @@ #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 @@ -32,11 +34,19 @@ #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) @@ -81,6 +91,21 @@ #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"); |