Commit c8192ba4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-v4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply

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
  ...
parents 9c6a019c b68c3161
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.
...@@ -87,7 +87,7 @@ One interrupt per TC channel in a TC block: ...@@ -87,7 +87,7 @@ One interrupt per TC channel in a TC block:
RSTC Reset Controller required properties: RSTC Reset Controller required properties:
- compatible: Should be "atmel,<chip>-rstc". - 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 - reg: Should contain registers location and length
Example: Example:
......
...@@ -8093,6 +8093,7 @@ T: git git://git.infradead.org/battery-2.6.git ...@@ -8093,6 +8093,7 @@ T: git git://git.infradead.org/battery-2.6.git
S: Maintained S: Maintained
F: include/linux/power_supply.h F: include/linux/power_supply.h
F: drivers/power/ F: drivers/power/
X: drivers/power/avs/
PNP SUPPORT PNP SUPPORT
M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
......
...@@ -788,9 +788,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, ...@@ -788,9 +788,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
static struct regulator_consumer_supply usb1v8 = { static struct regulator_consumer_supply usb1v8 = {
.supply = "usb1v8", .supply = "usb1v8",
}; };
static struct regulator_consumer_supply usb3v1[] = { static struct regulator_consumer_supply usb3v1 = {
{ .supply = "usb3v1" }, .supply = "usb3v1",
{ .supply = "bci3v1" },
}; };
/* First add the regulators so that they can be used by transceiver */ /* 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, ...@@ -818,7 +817,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
return PTR_ERR(child); return PTR_ERR(child);
child = add_regulator_linked(TWL4030_REG_VUSB3V1, child = add_regulator_linked(TWL4030_REG_VUSB3V1,
&usb_fixed, usb3v1, 2, &usb_fixed, &usb3v1, 1,
features); features);
if (IS_ERR(child)) if (IS_ERR(child))
return PTR_ERR(child); return PTR_ERR(child);
...@@ -838,7 +837,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, ...@@ -838,7 +837,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child) { if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child) {
usb1v5.dev_name = dev_name(child); usb1v5.dev_name = dev_name(child);
usb1v8.dev_name = dev_name(child); usb1v8.dev_name = dev_name(child);
usb3v1[0].dev_name = dev_name(child); usb3v1.dev_name = dev_name(child);
} }
} }
......
...@@ -333,7 +333,7 @@ config CHARGER_LP8788 ...@@ -333,7 +333,7 @@ config CHARGER_LP8788
config CHARGER_GPIO config CHARGER_GPIO
tristate "GPIO charger" tristate "GPIO charger"
depends on GPIOLIB depends on GPIOLIB || COMPILE_TEST
help help
Say Y to include support for chargers which report their online status Say Y to include support for chargers which report their online status
through a GPIO pin. through a GPIO pin.
...@@ -391,26 +391,30 @@ config CHARGER_BQ2415X ...@@ -391,26 +391,30 @@ config CHARGER_BQ2415X
config CHARGER_BQ24190 config CHARGER_BQ24190
tristate "TI BQ24190 battery charger driver" tristate "TI BQ24190 battery charger driver"
depends on I2C && GPIOLIB depends on I2C
depends on GPIOLIB || COMPILE_TEST
help help
Say Y to enable support for the TI BQ24190 battery charger. Say Y to enable support for the TI BQ24190 battery charger.
config CHARGER_BQ24257 config CHARGER_BQ24257
tristate "TI BQ24257 battery charger driver" tristate "TI BQ24257 battery charger driver"
depends on I2C && GPIOLIB depends on I2C
depends on GPIOLIB || COMPILE_TEST
depends on REGMAP_I2C depends on REGMAP_I2C
help help
Say Y to enable support for the TI BQ24257 battery charger. Say Y to enable support for the TI BQ24257 battery charger.
config CHARGER_BQ24735 config CHARGER_BQ24735
tristate "TI BQ24735 battery charger support" tristate "TI BQ24735 battery charger support"
depends on I2C && GPIOLIB depends on I2C
depends on GPIOLIB || COMPILE_TEST
help help
Say Y to enable support for the TI BQ24735 battery charger. Say Y to enable support for the TI BQ24735 battery charger.
config CHARGER_BQ25890 config CHARGER_BQ25890
tristate "TI BQ25890 battery charger driver" tristate "TI BQ25890 battery charger driver"
depends on I2C && GPIOLIB depends on I2C
depends on GPIOLIB || COMPILE_TEST
select REGMAP_I2C select REGMAP_I2C
help help
Say Y to enable support for the TI BQ25890 battery charger. Say Y to enable support for the TI BQ25890 battery charger.
...@@ -462,7 +466,8 @@ config BATTERY_RT5033 ...@@ -462,7 +466,8 @@ config BATTERY_RT5033
config CHARGER_RT9455 config CHARGER_RT9455
tristate "Richtek RT9455 battery charger driver" tristate "Richtek RT9455 battery charger driver"
depends on I2C && GPIOLIB depends on I2C
depends on GPIOLIB || COMPILE_TEST
select REGMAP_I2C select REGMAP_I2C
help help
Say Y to enable support for Richtek RT9455 battery charger. Say Y to enable support for Richtek RT9455 battery charger.
......
...@@ -170,7 +170,7 @@ struct bq2415x_device { ...@@ -170,7 +170,7 @@ struct bq2415x_device {
struct power_supply *charger; struct power_supply *charger;
struct power_supply_desc charger_desc; struct power_supply_desc charger_desc;
struct delayed_work work; struct delayed_work work;
struct power_supply *notify_psy; struct device_node *notify_node;
struct notifier_block nb; struct notifier_block nb;
enum bq2415x_mode reported_mode;/* mode reported by hook function */ enum bq2415x_mode reported_mode;/* mode reported by hook function */
enum bq2415x_mode mode; /* currently configured mode */ enum bq2415x_mode mode; /* currently configured mode */
...@@ -792,22 +792,47 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode 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, static int bq2415x_notifier_call(struct notifier_block *nb,
unsigned long val, void *v) unsigned long val, void *v)
{ {
struct bq2415x_device *bq = struct bq2415x_device *bq =
container_of(nb, struct bq2415x_device, nb); container_of(nb, struct bq2415x_device, nb);
struct power_supply *psy = v; struct power_supply *psy = v;
enum bq2415x_mode mode;
union power_supply_propval prop; union power_supply_propval prop;
int ret; int ret;
int mA;
if (val != PSY_EVENT_PROP_CHANGED) if (val != PSY_EVENT_PROP_CHANGED)
return NOTIFY_OK; return NOTIFY_OK;
if (psy != bq->notify_psy) /* 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; 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"); dev_dbg(bq->dev, "notifier call was called\n");
...@@ -816,22 +841,9 @@ static int bq2415x_notifier_call(struct notifier_block *nb, ...@@ -816,22 +841,9 @@ static int bq2415x_notifier_call(struct notifier_block *nb,
if (ret != 0) if (ret != 0)
return NOTIFY_OK; return NOTIFY_OK;
mA = prop.intval; if (!bq2415x_update_reported_mode(bq, 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)
return NOTIFY_OK; return NOTIFY_OK;
bq->reported_mode = mode;
/* if automode is not enabled do not tell about reported_mode */ /* if automode is not enabled do not tell about reported_mode */
if (bq->automode < 1) if (bq->automode < 1)
return NOTIFY_OK; return NOTIFY_OK;
...@@ -1536,6 +1548,8 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1536,6 +1548,8 @@ static int bq2415x_probe(struct i2c_client *client,
struct device_node *np = client->dev.of_node; struct device_node *np = client->dev.of_node;
struct bq2415x_platform_data *pdata = client->dev.platform_data; struct bq2415x_platform_data *pdata = client->dev.platform_data;
const struct acpi_device_id *acpi_id = NULL; 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)) { if (!np && !pdata && !ACPI_HANDLE(&client->dev)) {
dev_err(&client->dev, "Neither devicetree, nor platform data, nor ACPI support\n"); 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, ...@@ -1569,25 +1583,6 @@ static int bq2415x_probe(struct i2c_client *client,
goto error_2; 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); i2c_set_clientdata(client, bq);
bq->id = num; bq->id = num;
...@@ -1607,32 +1602,35 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1607,32 +1602,35 @@ static int bq2415x_probe(struct i2c_client *client,
"ti,current-limit", "ti,current-limit",
&bq->init_data.current_limit); &bq->init_data.current_limit);
if (ret) if (ret)
goto error_3; goto error_2;
ret = device_property_read_u32(bq->dev, ret = device_property_read_u32(bq->dev,
"ti,weak-battery-voltage", "ti,weak-battery-voltage",
&bq->init_data.weak_battery_voltage); &bq->init_data.weak_battery_voltage);
if (ret) if (ret)
goto error_3; goto error_2;
ret = device_property_read_u32(bq->dev, ret = device_property_read_u32(bq->dev,
"ti,battery-regulation-voltage", "ti,battery-regulation-voltage",
&bq->init_data.battery_regulation_voltage); &bq->init_data.battery_regulation_voltage);
if (ret) if (ret)
goto error_3; goto error_2;
ret = device_property_read_u32(bq->dev, ret = device_property_read_u32(bq->dev,
"ti,charge-current", "ti,charge-current",
&bq->init_data.charge_current); &bq->init_data.charge_current);
if (ret) if (ret)
goto error_3; goto error_2;
ret = device_property_read_u32(bq->dev, ret = device_property_read_u32(bq->dev,
"ti,termination-current", "ti,termination-current",
&bq->init_data.termination_current); &bq->init_data.termination_current);
if (ret) if (ret)
goto error_3; goto error_2;
ret = device_property_read_u32(bq->dev, ret = device_property_read_u32(bq->dev,
"ti,resistor-sense", "ti,resistor-sense",
&bq->init_data.resistor_sense); &bq->init_data.resistor_sense);
if (ret) if (ret)
goto error_3; goto error_2;
if (np)
bq->notify_node = of_parse_phandle(np,
"ti,usb-charger-detection", 0);
} else { } else {
memcpy(&bq->init_data, pdata, sizeof(bq->init_data)); memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
} }
...@@ -1642,56 +1640,72 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1642,56 +1640,72 @@ static int bq2415x_probe(struct i2c_client *client,
ret = bq2415x_power_supply_init(bq); ret = bq2415x_power_supply_init(bq);
if (ret) { if (ret) {
dev_err(bq->dev, "failed to register power supply: %d\n", ret); dev_err(bq->dev, "failed to register power supply: %d\n", ret);
goto error_3; goto error_2;
} }
ret = bq2415x_sysfs_init(bq); ret = bq2415x_sysfs_init(bq);
if (ret) { if (ret) {
dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret); dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
goto error_4; goto error_3;
} }
ret = bq2415x_set_defaults(bq); ret = bq2415x_set_defaults(bq);
if (ret) { if (ret) {
dev_err(bq->dev, "failed to set default values: %d\n", 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; bq->nb.notifier_call = bq2415x_notifier_call;
ret = power_supply_reg_notifier(&bq->nb); ret = power_supply_reg_notifier(&bq->nb);
if (ret) { if (ret) {
dev_err(bq->dev, "failed to reg notifier: %d\n", 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; bq->automode = 1;
dev_info(bq->dev, "automode enabled\n"); dev_info(bq->dev, "automode supported, waiting for events\n");
} else { } else {
bq->automode = -1; bq->automode = -1;
dev_info(bq->dev, "automode not supported\n"); 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); INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work);
bq2415x_set_autotimer(bq, 1); bq2415x_set_autotimer(bq, 1);
dev_info(bq->dev, "driver registered\n"); dev_info(bq->dev, "driver registered\n");
return 0; return 0;
error_6:
error_5:
bq2415x_sysfs_exit(bq);
error_4: error_4:
bq2415x_power_supply_exit(bq); bq2415x_sysfs_exit(bq);
error_3: error_3:
if (bq->notify_psy) bq2415x_power_supply_exit(bq);
power_supply_put(bq->notify_psy);
error_2: error_2:
if (bq->notify_node)
of_node_put(bq->notify_node);
kfree(name); kfree(name);
error_1: error_1:
mutex_lock(&bq2415x_id_mutex); mutex_lock(&bq2415x_id_mutex);
...@@ -1707,10 +1721,11 @@ static int bq2415x_remove(struct i2c_client *client) ...@@ -1707,10 +1721,11 @@ static int bq2415x_remove(struct i2c_client *client)
{ {
struct bq2415x_device *bq = i2c_get_clientdata(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_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_sysfs_exit(bq);
bq2415x_power_supply_exit(bq); bq2415x_power_supply_exit(bq);
......
...@@ -902,7 +902,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, ...@@ -902,7 +902,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy,
} }
static enum power_supply_property bq24190_charger_properties[] = { static enum power_supply_property bq24190_charger_properties[] = {
POWER_SUPPLY_PROP_TYPE, POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
...@@ -1515,6 +1515,7 @@ static const struct i2c_device_id bq24190_i2c_ids[] = { ...@@ -1515,6 +1515,7 @@ static const struct i2c_device_id bq24190_i2c_ids[] = {
{ "bq24190", BQ24190_REG_VPRS_PN_24190 }, { "bq24190", BQ24190_REG_VPRS_PN_24190 },
{ }, { },
}; };
MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id bq24190_of_match[] = { static const struct of_device_id bq24190_of_match[] = {
...@@ -1534,7 +1535,6 @@ static struct i2c_driver bq24190_driver = { ...@@ -1534,7 +1535,6 @@ static struct i2c_driver bq24190_driver = {
.id_table = bq24190_i2c_ids, .id_table = bq24190_i2c_ids,
.driver = { .driver = {
.name = "bq24190-charger", .name = "bq24190-charger",
.owner = THIS_MODULE,
.pm = &bq24190_pm_ops, .pm = &bq24190_pm_ops,
.of_match_table = of_match_ptr(bq24190_of_match), .of_match_table = of_match_ptr(bq24190_of_match),
}, },
......
...@@ -267,7 +267,8 @@ static int bq24735_charger_probe(struct i2c_client *client, ...@@ -267,7 +267,8 @@ static int bq24735_charger_probe(struct i2c_client *client,
name = (char *)charger->pdata->name; name = (char *)charger->pdata->name;
if (!name) { if (!name) {
name = kasprintf(GFP_KERNEL, "bq24735@%s", name = devm_kasprintf(&client->dev, GFP_KERNEL,
"bq24735@%s",
dev_name(&client->dev)); dev_name(&client->dev));
if (!name) { if (!name) {
dev_err(&client->dev, "Failed to alloc device name\n"); dev_err(&client->dev, "Failed to alloc device name\n");
...@@ -296,23 +297,21 @@ static int bq24735_charger_probe(struct i2c_client *client, ...@@ -296,23 +297,21 @@ static int bq24735_charger_probe(struct i2c_client *client,
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "Failed to read manufacturer id : %d\n", dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
ret); ret);
goto err_free_name; return ret;
} else if (ret != 0x0040) { } else if (ret != 0x0040) {
dev_err(&client->dev, dev_err(&client->dev,
"manufacturer id mismatch. 0x0040 != 0x%04x\n", ret); "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
ret = -ENODEV; return -ENODEV;
goto err_free_name;
} }
ret = bq24735_read_word(client, BQ24735_DEVICE_ID); ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "Failed to read device id : %d\n", ret); dev_err(&client->dev, "Failed to read device id : %d\n", ret);
goto err_free_name; return ret;
} else if (ret != 0x000B) { } else if (ret != 0x000B) {
dev_err(&client->dev, dev_err(&client->dev,
"device id mismatch. 0x000b != 0x%04x\n", ret); "device id mismatch. 0x000b != 0x%04x\n", ret);
ret = -ENODEV; return -ENODEV;
goto err_free_name;
} }
if (gpio_is_valid(charger->pdata->status_gpio)) { if (gpio_is_valid(charger->pdata->status_gpio)) {
...@@ -331,7 +330,7 @@ static int bq24735_charger_probe(struct i2c_client *client, ...@@ -331,7 +330,7 @@ static int bq24735_charger_probe(struct i2c_client *client,
ret = bq24735_config_charger(charger); ret = bq24735_config_charger(charger);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "failed in configuring charger"); dev_err(&client->dev, "failed in configuring charger");
goto err_free_name; return ret;
} }
/* check for AC adapter presence */ /* check for AC adapter presence */
...@@ -339,17 +338,17 @@ static int bq24735_charger_probe(struct i2c_client *client, ...@@ -339,17 +338,17 @@ static int bq24735_charger_probe(struct i2c_client *client,
ret = bq24735_enable_charging(charger); ret = bq24735_enable_charging(charger);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "Failed to enable charging\n"); dev_err(&client->dev, "Failed to enable charging\n");
goto err_free_name; return ret;
} }
} }
charger->charger = power_supply_register(&client->dev, supply_desc, charger->charger = devm_power_supply_register(&client->dev, supply_desc,
&psy_cfg); &psy_cfg);
if (IS_ERR(charger->charger)) { if (IS_ERR(charger->charger)) {
ret = PTR_ERR(charger->charger); ret = PTR_ERR(charger->charger);
dev_err(&client->dev, "Failed to register power supply: %d\n", dev_err(&client->dev, "Failed to register power supply: %d\n",
ret); ret);
goto err_free_name; return ret;
} }
if (client->irq) { if (client->irq) {
...@@ -364,33 +363,10 @@ static int bq24735_charger_probe(struct i2c_client *client, ...@@ -364,33 +363,10 @@ static int bq24735_charger_probe(struct i2c_client *client,
dev_err(&client->dev, dev_err(&client->dev,
"Unable to register IRQ %d err %d\n", "Unable to register IRQ %d err %d\n",
client->irq, ret); 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; return 0;
} }
...@@ -409,11 +385,9 @@ MODULE_DEVICE_TABLE(of, bq24735_match_ids); ...@@ -409,11 +385,9 @@ MODULE_DEVICE_TABLE(of, bq24735_match_ids);
static struct i2c_driver bq24735_charger_driver = { static struct i2c_driver bq24735_charger_driver = {
.driver = { .driver = {
.name = "bq24735-charger", .name = "bq24735-charger",
.owner = THIS_MODULE,
.of_match_table = bq24735_match_ids, .of_match_table = bq24735_match_ids,
}, },
.probe = bq24735_charger_probe, .probe = bq24735_charger_probe,
.remove = bq24735_charger_remove,
.id_table = bq24735_charger_id, .id_table = bq24735_charger_id,
}; };
......
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#define DRIVER_VERSION "1.2.0" #define DRIVER_VERSION "1.2.0"
#define BQ27XXX_MANUFACTURER "Texas Instruments"
#define BQ27x00_REG_TEMP 0x06 #define BQ27x00_REG_TEMP 0x06
#define BQ27x00_REG_VOLT 0x08 #define BQ27x00_REG_VOLT 0x08
#define BQ27x00_REG_AI 0x14 #define BQ27x00_REG_AI 0x14
...@@ -142,6 +144,7 @@ static enum power_supply_property bq27x00_battery_props[] = { ...@@ -142,6 +144,7 @@ static enum power_supply_property bq27x00_battery_props[] = {
POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_ENERGY_NOW,
POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
}; };
static enum power_supply_property bq27425_battery_props[] = { static enum power_supply_property bq27425_battery_props[] = {
...@@ -156,6 +159,7 @@ 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_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_MANUFACTURER,
}; };
static enum power_supply_property bq27742_battery_props[] = { static enum power_supply_property bq27742_battery_props[] = {
...@@ -174,6 +178,7 @@ 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_CYCLE_COUNT,
POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
}; };
static enum power_supply_property bq27510_battery_props[] = { static enum power_supply_property bq27510_battery_props[] = {
...@@ -192,12 +197,13 @@ static enum power_supply_property bq27510_battery_props[] = { ...@@ -192,12 +197,13 @@ static enum power_supply_property bq27510_battery_props[] = {
POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
}; };
static unsigned int poll_interval = 360; static unsigned int poll_interval = 360;
module_param(poll_interval, uint, 0644); module_param(poll_interval, uint, 0644);
MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ MODULE_PARM_DESC(poll_interval,
"0 disables polling"); "battery poll interval in seconds - 0 disables polling");
/* /*
* Common code for BQ27x00 devices * Common code for BQ27x00 devices
...@@ -313,8 +319,9 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) ...@@ -313,8 +319,9 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
ilmd = bq27x00_read(di, BQ27510_REG_DCAP, false); ilmd = bq27x00_read(di, BQ27510_REG_DCAP, false);
else else
ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
} else } else {
ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
}
if (ilmd < 0) { if (ilmd < 0) {
dev_dbg(di->dev, "error reading initial last measured discharge\n"); 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) ...@@ -445,7 +452,7 @@ static int bq27x00_battery_read_health(struct bq27x00_device_info *di)
return tval; return tval;
} }
if ((di->chip == BQ27500)) { if (di->chip == BQ27500) {
if (tval & BQ27500_FLAG_SOCF) if (tval & BQ27500_FLAG_SOCF)
tval = POWER_SUPPLY_HEALTH_DEAD; tval = POWER_SUPPLY_HEALTH_DEAD;
else if (tval & BQ27500_FLAG_OTC) else if (tval & BQ27500_FLAG_OTC)
...@@ -749,6 +756,9 @@ static int bq27x00_battery_get_property(struct power_supply *psy, ...@@ -749,6 +756,9 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_HEALTH: case POWER_SUPPLY_PROP_HEALTH:
ret = bq27x00_simple_value(di->cache.health, val); ret = bq27x00_simple_value(di->cache.health, val);
break; break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = BQ27XXX_MANUFACTURER;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -827,7 +837,6 @@ static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di) ...@@ -827,7 +837,6 @@ static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di)
mutex_destroy(&di->lock); mutex_destroy(&di->lock);
} }
/* i2c specific code */ /* i2c specific code */
#ifdef CONFIG_BATTERY_BQ27X00_I2C #ifdef CONFIG_BATTERY_BQ27X00_I2C
...@@ -888,14 +897,12 @@ static int bq27x00_battery_probe(struct i2c_client *client, ...@@ -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); name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
if (!name) { if (!name) {
dev_err(&client->dev, "failed to allocate device name\n");
retval = -ENOMEM; retval = -ENOMEM;
goto batt_failed; goto batt_failed;
} }
di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL); di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
if (!di) { if (!di) {
dev_err(&client->dev, "failed to allocate device info data\n");
retval = -ENOMEM; retval = -ENOMEM;
goto batt_failed; goto batt_failed;
} }
...@@ -956,8 +963,9 @@ static struct i2c_driver bq27x00_battery_driver = { ...@@ -956,8 +963,9 @@ static struct i2c_driver bq27x00_battery_driver = {
static inline int bq27x00_battery_i2c_init(void) static inline int bq27x00_battery_i2c_init(void)
{ {
int ret = i2c_add_driver(&bq27x00_battery_driver); int ret = i2c_add_driver(&bq27x00_battery_driver);
if (ret) if (ret)
printk(KERN_ERR "Unable to register BQ27x00 i2c driver\n"); pr_err("Unable to register BQ27x00 i2c driver\n");
return ret; return ret;
} }
...@@ -1028,10 +1036,8 @@ static int bq27000_battery_probe(struct platform_device *pdev) ...@@ -1028,10 +1036,8 @@ static int bq27000_battery_probe(struct platform_device *pdev)
} }
di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
if (!di) { if (!di)
dev_err(&pdev->dev, "failed to allocate device info data\n");
return -ENOMEM; return -ENOMEM;
}
platform_set_drvdata(pdev, di); platform_set_drvdata(pdev, di);
...@@ -1064,8 +1070,9 @@ static struct platform_driver bq27000_battery_driver = { ...@@ -1064,8 +1070,9 @@ static struct platform_driver bq27000_battery_driver = {
static inline int bq27x00_battery_platform_init(void) static inline int bq27x00_battery_platform_init(void)
{ {
int ret = platform_driver_register(&bq27000_battery_driver); int ret = platform_driver_register(&bq27000_battery_driver);
if (ret) if (ret)
printk(KERN_ERR "Unable to register BQ27000 platform driver\n"); pr_err("Unable to register BQ27000 platform driver\n");
return ret; return ret;
} }
......
...@@ -637,10 +637,6 @@ static ssize_t ds2780_read_param_eeprom_bin(struct file *filp, ...@@ -637,10 +637,6 @@ static ssize_t ds2780_read_param_eeprom_bin(struct file *filp,
struct power_supply *psy = to_power_supply(dev); struct power_supply *psy = to_power_supply(dev);
struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); 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, return ds2780_read_block(dev_info, buf,
DS2780_EEPROM_BLOCK1_START + off, count); DS2780_EEPROM_BLOCK1_START + off, count);
} }
...@@ -655,10 +651,6 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp, ...@@ -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); struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
int ret; int ret;
count = min_t(loff_t, count,
DS2780_EEPROM_BLOCK1_END -
DS2780_EEPROM_BLOCK1_START + 1 - off);
ret = ds2780_write(dev_info, buf, ret = ds2780_write(dev_info, buf,
DS2780_EEPROM_BLOCK1_START + off, count); DS2780_EEPROM_BLOCK1_START + off, count);
if (ret < 0) if (ret < 0)
...@@ -676,7 +668,7 @@ static struct bin_attribute ds2780_param_eeprom_bin_attr = { ...@@ -676,7 +668,7 @@ static struct bin_attribute ds2780_param_eeprom_bin_attr = {
.name = "param_eeprom", .name = "param_eeprom",
.mode = S_IRUGO | S_IWUSR, .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, .read = ds2780_read_param_eeprom_bin,
.write = ds2780_write_param_eeprom_bin, .write = ds2780_write_param_eeprom_bin,
}; };
...@@ -690,10 +682,6 @@ static ssize_t ds2780_read_user_eeprom_bin(struct file *filp, ...@@ -690,10 +682,6 @@ static ssize_t ds2780_read_user_eeprom_bin(struct file *filp,
struct power_supply *psy = to_power_supply(dev); struct power_supply *psy = to_power_supply(dev);
struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); 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, return ds2780_read_block(dev_info, buf,
DS2780_EEPROM_BLOCK0_START + off, count); DS2780_EEPROM_BLOCK0_START + off, count);
} }
...@@ -708,10 +696,6 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp, ...@@ -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); struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
int ret; int ret;
count = min_t(loff_t, count,
DS2780_EEPROM_BLOCK0_END -
DS2780_EEPROM_BLOCK0_START + 1 - off);
ret = ds2780_write(dev_info, buf, ret = ds2780_write(dev_info, buf,
DS2780_EEPROM_BLOCK0_START + off, count); DS2780_EEPROM_BLOCK0_START + off, count);
if (ret < 0) if (ret < 0)
...@@ -729,7 +713,7 @@ static struct bin_attribute ds2780_user_eeprom_bin_attr = { ...@@ -729,7 +713,7 @@ static struct bin_attribute ds2780_user_eeprom_bin_attr = {
.name = "user_eeprom", .name = "user_eeprom",
.mode = S_IRUGO | S_IWUSR, .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, .read = ds2780_read_user_eeprom_bin,
.write = ds2780_write_user_eeprom_bin, .write = ds2780_write_user_eeprom_bin,
}; };
......
...@@ -639,8 +639,6 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp, ...@@ -639,8 +639,6 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
struct power_supply *psy = to_power_supply(dev); struct power_supply *psy = to_power_supply(dev);
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); 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, return ds2781_read_block(dev_info, buf,
DS2781_EEPROM_BLOCK1_START + off, count); DS2781_EEPROM_BLOCK1_START + off, count);
} }
...@@ -655,8 +653,6 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, ...@@ -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); struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
int ret; int ret;
count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
ret = ds2781_write(dev_info, buf, ret = ds2781_write(dev_info, buf,
DS2781_EEPROM_BLOCK1_START + off, count); DS2781_EEPROM_BLOCK1_START + off, count);
if (ret < 0) if (ret < 0)
...@@ -688,8 +684,6 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp, ...@@ -688,8 +684,6 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
struct power_supply *psy = to_power_supply(dev); struct power_supply *psy = to_power_supply(dev);
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); 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, return ds2781_read_block(dev_info, buf,
DS2781_EEPROM_BLOCK0_START + off, count); DS2781_EEPROM_BLOCK0_START + off, count);
...@@ -705,8 +699,6 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, ...@@ -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); struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
int ret; int ret;
count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
ret = ds2781_write(dev_info, buf, ret = ds2781_write(dev_info, buf,
DS2781_EEPROM_BLOCK0_START + off, count); DS2781_EEPROM_BLOCK0_START + off, count);
if (ret < 0) if (ret < 0)
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/swab.h> #include <linux/swab.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/idr.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -63,15 +62,11 @@ struct ltc294x_info { ...@@ -63,15 +62,11 @@ struct ltc294x_info {
struct power_supply_desc supply_desc; /* Supply description */ struct power_supply_desc supply_desc; /* Supply description */
struct delayed_work work; /* Work scheduler */ struct delayed_work work; /* Work scheduler */
int num_regs; /* Number of registers (chip type) */ int num_regs; /* Number of registers (chip type) */
int id; /* Identifier of ltc294x chip */
int charge; /* Last charge register content */ int charge; /* Last charge register content */
int r_sense; /* mOhm */ int r_sense; /* mOhm */
int Qlsb; /* nAh */ int Qlsb; /* nAh */
}; };
static DEFINE_IDR(ltc294x_id);
static DEFINE_MUTEX(ltc294x_lock);
static inline int convert_bin_to_uAh( static inline int convert_bin_to_uAh(
const struct ltc294x_info *info, int Q) const struct ltc294x_info *info, int Q)
{ {
...@@ -371,10 +366,6 @@ static int ltc294x_i2c_remove(struct i2c_client *client) ...@@ -371,10 +366,6 @@ static int ltc294x_i2c_remove(struct i2c_client *client)
cancel_delayed_work(&info->work); cancel_delayed_work(&info->work);
power_supply_unregister(info->supply); 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; return 0;
} }
...@@ -384,44 +375,28 @@ static int ltc294x_i2c_probe(struct i2c_client *client, ...@@ -384,44 +375,28 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
struct ltc294x_info *info; struct ltc294x_info *info;
int ret; int ret;
int num;
u32 prescaler_exp; u32 prescaler_exp;
s32 r_sense; s32 r_sense;
struct device_node *np; 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); info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL) { if (info == NULL)
ret = -ENOMEM; return -ENOMEM;
goto fail_info;
}
i2c_set_clientdata(client, info); 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); 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 /* r_sense can be negative, when sense+ is connected to the battery
* instead of the sense-. This results in reversed measurements. */ * instead of the sense-. This results in reversed measurements. */
ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense); ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, dev_err(&client->dev,
"Could not find lltc,resistor-sense in devicetree\n"); "Could not find lltc,resistor-sense in devicetree\n");
goto fail_name; return ret;
} }
info->r_sense = r_sense; info->r_sense = r_sense;
...@@ -446,7 +421,6 @@ static int ltc294x_i2c_probe(struct i2c_client *client, ...@@ -446,7 +421,6 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
} }
info->client = client; info->client = client;
info->id = num;
info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY; info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY;
info->supply_desc.properties = ltc294x_properties; info->supply_desc.properties = ltc294x_properties;
if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB) if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB)
...@@ -473,31 +447,19 @@ static int ltc294x_i2c_probe(struct i2c_client *client, ...@@ -473,31 +447,19 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
ret = ltc294x_reset(info, prescaler_exp); ret = ltc294x_reset(info, prescaler_exp);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "Communication with chip failed\n"); dev_err(&client->dev, "Communication with chip failed\n");
goto fail_comm; return ret;
} }
info->supply = power_supply_register(&client->dev, &info->supply_desc, info->supply = power_supply_register(&client->dev, &info->supply_desc,
&psy_cfg); &psy_cfg);
if (IS_ERR(info->supply)) { if (IS_ERR(info->supply)) {
dev_err(&client->dev, "failed to register ltc2941\n"); dev_err(&client->dev, "failed to register ltc2941\n");
ret = PTR_ERR(info->supply); return PTR_ERR(info->supply);
goto fail_register;
} else { } else {
schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
} }
return 0; 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 #ifdef CONFIG_PM_SLEEP
......
...@@ -521,11 +521,6 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj, ...@@ -521,11 +521,6 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
int ret; int ret;
int i; int i;
if (off >= EEPROM_SIZE)
return 0;
if (off + count > EEPROM_SIZE)
count = EEPROM_SIZE - off;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
ec_byte = EEPROM_START + off + i; ec_byte = EEPROM_START + off + i;
ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1); ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1);
...@@ -545,7 +540,7 @@ static struct bin_attribute olpc_bat_eeprom = { ...@@ -545,7 +540,7 @@ static struct bin_attribute olpc_bat_eeprom = {
.name = "eeprom", .name = "eeprom",
.mode = S_IRUGO, .mode = S_IRUGO,
}, },
.size = 0, .size = EEPROM_SIZE,
.read = olpc_bat_eeprom_read, .read = olpc_bat_eeprom_read,
}; };
......
...@@ -1244,7 +1244,6 @@ static struct i2c_driver pm2xxx_charger_driver = { ...@@ -1244,7 +1244,6 @@ static struct i2c_driver pm2xxx_charger_driver = {
.remove = pm2xxx_wall_charger_remove, .remove = pm2xxx_wall_charger_remove,
.driver = { .driver = {
.name = "pm2xxx-wall_charger", .name = "pm2xxx-wall_charger",
.owner = THIS_MODULE,
.pm = PM2XXX_PM_OPS, .pm = PM2XXX_PM_OPS,
}, },
.id_table = pm2xxx_id, .id_table = pm2xxx_id,
......
...@@ -166,5 +166,12 @@ config POWER_RESET_RMOBILE ...@@ -166,5 +166,12 @@ config POWER_RESET_RMOBILE
help help
Reboot support for Renesas R-Mobile and SH-Mobile SoCs. 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 endif
...@@ -19,3 +19,4 @@ obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o ...@@ -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) += syscon-reboot.o
obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
...@@ -123,6 +123,15 @@ static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode, ...@@ -123,6 +123,15 @@ static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode,
return NOTIFY_DONE; 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) static void __init at91_reset_status(struct platform_device *pdev)
{ {
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR); u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
...@@ -155,13 +164,13 @@ static void __init at91_reset_status(struct platform_device *pdev) ...@@ -155,13 +164,13 @@ static void __init at91_reset_status(struct platform_device *pdev)
static const struct of_device_id at91_ramc_of_match[] = { static const struct of_device_id at91_ramc_of_match[] = {
{ .compatible = "atmel,at91sam9260-sdramc", }, { .compatible = "atmel,at91sam9260-sdramc", },
{ .compatible = "atmel,at91sam9g45-ddramc", }, { .compatible = "atmel,at91sam9g45-ddramc", },
{ .compatible = "atmel,sama5d3-ddramc", },
{ /* sentinel */ } { /* sentinel */ }
}; };
static const struct of_device_id at91_reset_of_match[] = { static const struct of_device_id at91_reset_of_match[] = {
{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart }, { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart }, { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart },
{ /* sentinel */ } { /* sentinel */ }
}; };
...@@ -181,6 +190,8 @@ static int at91_reset_of_probe(struct platform_device *pdev) ...@@ -181,6 +190,8 @@ static int at91_reset_of_probe(struct platform_device *pdev)
return -ENODEV; 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) { for_each_matching_node(np, at91_ramc_of_match) {
at91_ramc_base[idx] = of_iomap(np, 0); at91_ramc_base[idx] = of_iomap(np, 0);
if (!at91_ramc_base[idx]) { if (!at91_ramc_base[idx]) {
...@@ -189,6 +200,7 @@ static int at91_reset_of_probe(struct platform_device *pdev) ...@@ -189,6 +200,7 @@ static int at91_reset_of_probe(struct platform_device *pdev)
} }
idx++; idx++;
} }
}
match = of_match_node(at91_reset_of_match, pdev->dev.of_node); match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
at91_restart_nb.notifier_call = match->data; at91_restart_nb.notifier_call = match->data;
......
/*
* 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);
...@@ -165,7 +165,7 @@ static const struct i2c_device_id rt5033_battery_id[] = { ...@@ -165,7 +165,7 @@ static const struct i2c_device_id rt5033_battery_id[] = {
{ "rt5033-battery", }, { "rt5033-battery", },
{ } { }
}; };
MODULE_DEVICE_TABLE(platform, rt5033_battery_id); MODULE_DEVICE_TABLE(i2c, rt5033_battery_id);
static struct i2c_driver rt5033_battery_driver = { static struct i2c_driver rt5033_battery_driver = {
.driver = { .driver = {
......
...@@ -973,7 +973,6 @@ static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info, ...@@ -973,7 +973,6 @@ static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info,
if (irq2 & GET_MASK(F_CHRVPI)) { if (irq2 & GET_MASK(F_CHRVPI)) {
dev_dbg(dev, "Charger fault occurred\n"); dev_dbg(dev, "Charger fault occurred\n");
alert_userspace = true;
/* /*
* CHRVPI bit is set in 2 cases: * CHRVPI bit is set in 2 cases:
* 1. when the power source is connected to the charger. * 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, ...@@ -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 * To identify the case, PWR_RDY bit is checked. Because
* PWR_RDY bit is set / cleared after CHRVPI interrupt is * PWR_RDY bit is set / cleared after CHRVPI interrupt is
* triggered, it is used delayed_work to later read PWR_RDY bit. * 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, queue_delayed_work(system_power_efficient_wq,
&info->pwr_rdy_work, &info->pwr_rdy_work,
...@@ -1178,7 +1180,7 @@ static irqreturn_t rt9455_irq_handler_thread(int irq, void *data) ...@@ -1178,7 +1180,7 @@ static irqreturn_t rt9455_irq_handler_thread(int irq, void *data)
/* /*
* Sometimes, an interrupt occurs while rt9455_probe() function * Sometimes, an interrupt occurs while rt9455_probe() function
* is executing and power_supply_register() is not yet called. * 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) if (info->charger)
power_supply_changed(info->charger); power_supply_changed(info->charger);
...@@ -1478,6 +1480,11 @@ static void rt9455_pwr_rdy_work_callback(struct work_struct *work) ...@@ -1478,6 +1480,11 @@ static void rt9455_pwr_rdy_work_callback(struct work_struct *work)
RT9455_MAX_CHARGING_TIME * HZ); RT9455_MAX_CHARGING_TIME * HZ);
break; 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) 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) ...@@ -1533,6 +1540,11 @@ static void rt9455_batt_presence_work_callback(struct work_struct *work)
if (ret) if (ret)
dev_err(dev, "Failed to unmask BATAB interrupt\n"); 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);
} }
} }
......
...@@ -215,7 +215,7 @@ static int rx51_battery_probe(struct platform_device *pdev) ...@@ -215,7 +215,7 @@ static int rx51_battery_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, di); platform_set_drvdata(pdev, di);
di->dev = &pdev->dev; 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.type = POWER_SUPPLY_TYPE_BATTERY;
di->bat_desc.properties = rx51_battery_props; di->bat_desc.properties = rx51_battery_props;
di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props); di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props);
......
...@@ -22,8 +22,10 @@ ...@@ -22,8 +22,10 @@
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/usb/otg.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_BCIMSTATEC 0x02
#define TWL4030_BCIICHG 0x08 #define TWL4030_BCIICHG 0x08
#define TWL4030_BCIVAC 0x0a #define TWL4030_BCIVAC 0x0a
...@@ -32,11 +34,19 @@ ...@@ -32,11 +34,19 @@
#define TWL4030_BCIMFSTS4 0x10 #define TWL4030_BCIMFSTS4 0x10
#define TWL4030_BCICTL1 0x23 #define TWL4030_BCICTL1 0x23
#define TWL4030_BB_CFG 0x12 #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_BCIMFSTS1 0x01
#define TWL4030_BCIAUTOWEN BIT(5) #define TWL4030_BCIAUTOWEN BIT(5)
#define TWL4030_CONFIG_DONE BIT(4) #define TWL4030_CONFIG_DONE BIT(4)
#define TWL4030_CVENAC BIT(2)
#define TWL4030_BCIAUTOUSB BIT(1) #define TWL4030_BCIAUTOUSB BIT(1)
#define TWL4030_BCIAUTOAC BIT(0) #define TWL4030_BCIAUTOAC BIT(0)
#define TWL4030_CGAIN BIT(5) #define TWL4030_CGAIN BIT(5)
...@@ -81,6 +91,21 @@ ...@@ -81,6 +91,21 @@
#define TWL4030_MSTATEC_COMPLETE1 0x0b #define TWL4030_MSTATEC_COMPLETE1 0x0b
#define TWL4030_MSTATEC_COMPLETE4 0x0e #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; static bool allow_usb;
module_param(allow_usb, bool, 0644); module_param(allow_usb, bool, 0644);
MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current"); MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
...@@ -94,12 +119,39 @@ struct twl4030_bci { ...@@ -94,12 +119,39 @@ struct twl4030_bci {
struct work_struct work; struct work_struct work;
int irq_chg; int irq_chg;
int irq_bci; int irq_bci;
struct regulator *usb_reg;
int usb_enabled; 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; 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 * 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) ...@@ -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; 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, static int twl4030_charger_update_current(struct twl4030_bci *bci)
TWL4030_PM_MASTER_STS_HW_CONDITIONS); {
if (ret < 0) int status;
return 0; int cur;
unsigned reg, cur_reg;
u8 bcictl1, oldreg, fullreg;
bool cgain = false;
u8 boot_bci;
/*
* 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);
}
dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts); /* 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);
/* in case we also have STS_USB_ID, VBUS is driven by TWL itself */ /*
if ((hwsts & TWL4030_STS_VBUS) && !(hwsts & TWL4030_STS_USB_ID)) * For ichg_eoc, the hardware only supports reg values matching
return 1; * 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);
}
/* 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; 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. * Enable/Disable USB Charge functionality.
*/ */
...@@ -208,45 +466,60 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) ...@@ -208,45 +466,60 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
{ {
int ret; int ret;
if (enable) { if (bci->usb_mode == CHARGE_OFF)
/* Check for USB charger connected */ enable = false;
if (!twl4030_bci_have_vbus(bci)) if (enable && !IS_ERR_OR_NULL(bci->transceiver)) {
return -ENODEV;
/* twl4030_charger_update_current(bci);
* 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;
}
/* Need to keep regulator on */ /* Need to keep phy powered */
if (!bci->usb_enabled) { if (!bci->usb_enabled) {
ret = regulator_enable(bci->usb_reg); pm_runtime_get_sync(bci->transceiver->dev);
if (ret) {
dev_err(bci->dev,
"Failed to enable regulator\n");
return ret;
}
bci->usb_enabled = 1; bci->usb_enabled = 1;
} }
if (bci->usb_mode == CHARGE_AUTO)
/* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB); ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
if (ret < 0)
return ret;
/* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0, ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0,
TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); 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 { } else {
ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); 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) { 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_enabled = 0;
} }
bci->usb_cur = 0;
} }
return ret; return ret;
...@@ -255,10 +528,13 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) ...@@ -255,10 +528,13 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
/* /*
* Enable/Disable AC Charge funtionality. * 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; int ret;
if (bci->ac_mode == CHARGE_OFF)
enable = false;
if (enable) if (enable)
ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC); ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC);
else else
...@@ -318,6 +594,9 @@ static irqreturn_t twl4030_charger_interrupt(int irq, void *arg) ...@@ -318,6 +594,9 @@ static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
struct twl4030_bci *bci = arg; struct twl4030_bci *bci = arg;
dev_dbg(bci->dev, "CHG_PRES irq\n"); 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->ac);
power_supply_changed(bci->usb); power_supply_changed(bci->usb);
...@@ -350,6 +629,7 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) ...@@ -350,6 +629,7 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
power_supply_changed(bci->ac); power_supply_changed(bci->ac);
power_supply_changed(bci->usb); power_supply_changed(bci->usb);
} }
twl4030_charger_update_current(bci);
/* various monitoring events, for now we just log them here */ /* various monitoring events, for now we just log them here */
if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1)) if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
...@@ -370,6 +650,63 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) ...@@ -370,6 +650,63 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
return IRQ_HANDLED; 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) static void twl4030_bci_usb_work(struct work_struct *data)
{ {
struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work); 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, ...@@ -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); 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; bci->event = val;
schedule_work(&bci->work); schedule_work(&bci->work);
...@@ -399,13 +742,66 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, ...@@ -399,13 +742,66 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
} }
/* /*
* TI provided formulas: * sysfs charger enabled store
* 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
*/ */
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) static int twl4030_charger_get_current(void)
{ {
int curr; int curr;
...@@ -420,11 +816,7 @@ static int twl4030_charger_get_current(void) ...@@ -420,11 +816,7 @@ static int twl4030_charger_get_current(void)
if (ret) if (ret)
return ret; return ret;
ret = (curr * 16618 - 850 * 10000) / 10; return regval2ua(curr, bcictl1 & TWL4030_CGAIN);
if (bcictl1 & TWL4030_CGAIN)
ret *= 2;
return ret;
} }
/* /*
...@@ -476,6 +868,17 @@ static int twl4030_bci_get_property(struct power_supply *psy, ...@@ -476,6 +868,17 @@ static int twl4030_bci_get_property(struct power_supply *psy,
is_charging = state & TWL4030_MSTATEC_USB; is_charging = state & TWL4030_MSTATEC_USB;
else else
is_charging = state & TWL4030_MSTATEC_AC; 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) { switch (psp) {
case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_STATUS:
...@@ -574,20 +977,31 @@ static const struct power_supply_desc twl4030_bci_usb_desc = { ...@@ -574,20 +977,31 @@ static const struct power_supply_desc twl4030_bci_usb_desc = {
.get_property = twl4030_bci_get_property, .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; struct twl4030_bci *bci;
const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
int ret; int ret;
u32 reg; u32 reg;
bci = kzalloc(sizeof(*bci), GFP_KERNEL); bci = devm_kzalloc(&pdev->dev, sizeof(*bci), GFP_KERNEL);
if (bci == NULL) if (bci == NULL)
return -ENOMEM; return -ENOMEM;
if (!pdata) if (!pdata)
pdata = twl4030_bci_parse_dt(&pdev->dev); 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->dev = &pdev->dev;
bci->irq_chg = platform_get_irq(pdev, 0); bci->irq_chg = platform_get_irq(pdev, 0);
bci->irq_bci = platform_get_irq(pdev, 1); bci->irq_bci = platform_get_irq(pdev, 1);
...@@ -596,47 +1010,46 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -596,47 +1010,46 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
ret = twl4030_is_battery_present(bci); ret = twl4030_is_battery_present(bci);
if (ret) { if (ret) {
dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret); dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret);
goto fail_no_battery; return ret;
} }
platform_set_drvdata(pdev, bci); platform_set_drvdata(pdev, bci);
bci->ac = power_supply_register(&pdev->dev, &twl4030_bci_ac_desc, bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
NULL); NULL);
if (IS_ERR(bci->ac)) { if (IS_ERR(bci->ac)) {
ret = PTR_ERR(bci->ac); ret = PTR_ERR(bci->ac);
dev_err(&pdev->dev, "failed to register ac: %d\n", ret); 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 = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc,
bci->usb = power_supply_register(&pdev->dev, &twl4030_bci_usb_desc,
NULL); NULL);
if (IS_ERR(bci->usb)) { if (IS_ERR(bci->usb)) {
ret = PTR_ERR(bci->usb); ret = PTR_ERR(bci->usb);
dev_err(&pdev->dev, "failed to register usb: %d\n", ret); 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, twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name,
bci); bci);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "could not request irq %d, status %d\n", dev_err(&pdev->dev, "could not request irq %d, status %d\n",
bci->irq_chg, ret); 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); twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "could not request irq %d, status %d\n", dev_err(&pdev->dev, "could not request irq %d, status %d\n",
bci->irq_bci, ret); bci->irq_bci, ret);
goto fail_bci_irq; return ret;
} }
INIT_WORK(&bci->work, twl4030_bci_usb_work); 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; bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
if (bci->dev->of_node) { if (bci->dev->of_node) {
...@@ -644,9 +1057,13 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -644,9 +1057,13 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
phynode = of_find_compatible_node(bci->dev->of_node->parent, phynode = of_find_compatible_node(bci->dev->of_node->parent,
NULL, "ti,twl4030-usb"); NULL, "ti,twl4030-usb");
if (phynode) if (phynode) {
bci->transceiver = devm_usb_get_phy_by_node( bci->transceiver = devm_usb_get_phy_by_node(
bci->dev, phynode, &bci->usb_nb); bci->dev, phynode, &bci->usb_nb);
if (IS_ERR(bci->transceiver) &&
PTR_ERR(bci->transceiver) == -EPROBE_DEFER)
return -EPROBE_DEFER;
}
} }
/* Enable interrupts now. */ /* Enable interrupts now. */
...@@ -656,7 +1073,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -656,7 +1073,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
TWL4030_INTERRUPTS_BCIIMR1A); TWL4030_INTERRUPTS_BCIIMR1A);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret); 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); reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
...@@ -665,8 +1082,23 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -665,8 +1082,23 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret); dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
twl4030_charger_enable_ac(true); twl4030_charger_update_current(bci);
twl4030_charger_enable_usb(bci, true); 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) if (pdata)
twl4030_charger_enable_backup(pdata->bb_uvolt, twl4030_charger_enable_backup(pdata->bb_uvolt,
pdata->bb_uamp); pdata->bb_uamp);
...@@ -674,42 +1106,26 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -674,42 +1106,26 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
twl4030_charger_enable_backup(0, 0); twl4030_charger_enable_backup(0, 0);
return 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) static int __exit twl4030_bci_remove(struct platform_device *pdev)
{ {
struct twl4030_bci *bci = platform_get_drvdata(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_usb(bci, false);
twl4030_charger_enable_backup(0, 0); 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 */ /* mask interrupts */
twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
TWL4030_INTERRUPTS_BCIIMR1A); TWL4030_INTERRUPTS_BCIIMR1A);
twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
TWL4030_INTERRUPTS_BCIIMR2A); 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; return 0;
} }
...@@ -720,14 +1136,14 @@ static const struct of_device_id twl_bci_of_match[] = { ...@@ -720,14 +1136,14 @@ static const struct of_device_id twl_bci_of_match[] = {
MODULE_DEVICE_TABLE(of, twl_bci_of_match); MODULE_DEVICE_TABLE(of, twl_bci_of_match);
static struct platform_driver twl4030_bci_driver = { static struct platform_driver twl4030_bci_driver = {
.probe = twl4030_bci_probe,
.driver = { .driver = {
.name = "twl4030_bci", .name = "twl4030_bci",
.of_match_table = of_match_ptr(twl_bci_of_match), .of_match_table = of_match_ptr(twl_bci_of_match),
}, },
.remove = __exit_p(twl4030_bci_remove), .remove = __exit_p(twl4030_bci_remove),
}; };
module_platform_driver(twl4030_bci_driver);
module_platform_driver_probe(twl4030_bci_driver, twl4030_bci_probe);
MODULE_AUTHOR("Gražvydas Ignotas"); MODULE_AUTHOR("Gražvydas Ignotas");
MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment