power supply and reset changes for the v4.3 series

* new reset driver for ZTE SoCs
  * add support for sama5d3 reset handling
  * overhaul of twl4030 charger driver
  * misc. fixes and cleanups
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCgAGBQJV2xolAAoJENju1/PIO/qaoWwP/1vQ5xJxqyV0zyv2GIMRPbDi
 0hPrr8gC6K+++50weRFKVeFU/UGzpSB/QMryrUWmXP9INa9XnzRSh+qQxZao5dLi
 xm4DJ08bsszjsL94e8JwQuumreEerQnjEPB2FAcYr+Ep1pG40RlQ7H5yLuwN217i
 ufi2L7szaNBlmXPQ3lOfsVHGX3SLm2T3wIXpBKzG6hMr+GWbJP4sexs6W+iFU2cm
 802vWAkhyTHydlTFpjmP8JHtqq5CfgLA9wmPjlXCtEHNxOe/auSKN3dBXJhfUiFi
 ftiwXlShRb6NtBn+tQnOpG4ITaBiH5WKKfAZz3b9+4sxYbFwF/aptGumxOdu4Uvp
 B18M9Uw66DDVcDioi/I0L91D4OYJafTTzDxpBImHRlqzdKzWiUZ2V/+LVUOizGLj
 VH9EyUaA3u/EJ6pmWK8IEY/6OtgyjND1ZskRpWlI5u1CL16HiHpgmhASy0xajAjo
 fIESYDXdzEEatQbM6S5xbmOIm1syS7nSSSfSdmFQj6A6zQAHDnPBwZ7hiwUDjsf0
 4Y60bB3WxJGjne3kCpgWJ5xTdHYV+4bmVU5f82UYiWXLSawMmULtAii09KSd4Hie
 cYCztBQWvpLRM6FpjfrblsY+d8Wme8148/Y1X4IUmneNxoKpr5sMGBIMEl8ox8SP
 ZfJQvxNBOMmk5aSy3N6t
 =ENTy
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2015-08-31 15:25:16 -07:00
commit c8192ba416
22 changed files with 863 additions and 357 deletions

View file

@ -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.

View file

@ -87,7 +87,7 @@ One interrupt per TC channel in a TC block:
RSTC Reset Controller required properties:
- compatible: Should be "atmel,<chip>-rstc".
<chip> can be "at91sam9260" or "at91sam9g45"
<chip> can be "at91sam9260" or "at91sam9g45" or "sama5d3"
- reg: Should contain registers location and length
Example:

View file

@ -8093,6 +8093,7 @@ T: git git://git.infradead.org/battery-2.6.git
S: Maintained
F: include/linux/power_supply.h
F: drivers/power/
X: drivers/power/avs/
PNP SUPPORT
M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>

View file

@ -788,9 +788,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
static struct regulator_consumer_supply usb1v8 = {
.supply = "usb1v8",
};
static struct regulator_consumer_supply usb3v1[] = {
{ .supply = "usb3v1" },
{ .supply = "bci3v1" },
static struct regulator_consumer_supply usb3v1 = {
.supply = "usb3v1",
};
/* First add the regulators so that they can be used by transceiver */
@ -818,7 +817,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
return PTR_ERR(child);
child = add_regulator_linked(TWL4030_REG_VUSB3V1,
&usb_fixed, usb3v1, 2,
&usb_fixed, &usb3v1, 1,
features);
if (IS_ERR(child))
return PTR_ERR(child);
@ -838,7 +837,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child) {
usb1v5.dev_name = dev_name(child);
usb1v8.dev_name = dev_name(child);
usb3v1[0].dev_name = dev_name(child);
usb3v1.dev_name = dev_name(child);
}
}

View file

@ -333,7 +333,7 @@ config CHARGER_LP8788
config CHARGER_GPIO
tristate "GPIO charger"
depends on GPIOLIB
depends on GPIOLIB || COMPILE_TEST
help
Say Y to include support for chargers which report their online status
through a GPIO pin.
@ -391,26 +391,30 @@ config CHARGER_BQ2415X
config CHARGER_BQ24190
tristate "TI BQ24190 battery charger driver"
depends on I2C && GPIOLIB
depends on I2C
depends on GPIOLIB || COMPILE_TEST
help
Say Y to enable support for the TI BQ24190 battery charger.
config CHARGER_BQ24257
tristate "TI BQ24257 battery charger driver"
depends on I2C && GPIOLIB
depends on I2C
depends on GPIOLIB || COMPILE_TEST
depends on REGMAP_I2C
help
Say Y to enable support for the TI BQ24257 battery charger.
config CHARGER_BQ24735
tristate "TI BQ24735 battery charger support"
depends on I2C && GPIOLIB
depends on I2C
depends on GPIOLIB || COMPILE_TEST
help
Say Y to enable support for the TI BQ24735 battery charger.
config CHARGER_BQ25890
tristate "TI BQ25890 battery charger driver"
depends on I2C && GPIOLIB
depends on I2C
depends on GPIOLIB || COMPILE_TEST
select REGMAP_I2C
help
Say Y to enable support for the TI BQ25890 battery charger.
@ -462,7 +466,8 @@ config BATTERY_RT5033
config CHARGER_RT9455
tristate "Richtek RT9455 battery charger driver"
depends on I2C && GPIOLIB
depends on I2C
depends on GPIOLIB || COMPILE_TEST
select REGMAP_I2C
help
Say Y to enable support for Richtek RT9455 battery charger.

View file

@ -170,7 +170,7 @@ struct bq2415x_device {
struct power_supply *charger;
struct power_supply_desc charger_desc;
struct delayed_work work;
struct power_supply *notify_psy;
struct device_node *notify_node;
struct notifier_block nb;
enum bq2415x_mode reported_mode;/* mode reported by hook function */
enum bq2415x_mode mode; /* currently configured mode */
@ -792,31 +792,9 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
}
static int bq2415x_notifier_call(struct notifier_block *nb,
unsigned long val, void *v)
static bool bq2415x_update_reported_mode(struct bq2415x_device *bq, int mA)
{
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;
dev_dbg(bq->dev, "notifier call was called\n");
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
&prop);
if (ret != 0)
return NOTIFY_OK;
mA = prop.intval;
if (mA == 0)
mode = BQ2415X_MODE_OFF;
@ -828,9 +806,43 @@ static int bq2415x_notifier_call(struct notifier_block *nb,
mode = BQ2415X_MODE_DEDICATED_CHARGER;
if (bq->reported_mode == mode)
return NOTIFY_OK;
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;
union power_supply_propval prop;
int ret;
if (val != PSY_EVENT_PROP_CHANGED)
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");
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
&prop);
if (ret != 0)
return NOTIFY_OK;
if (!bq2415x_update_reported_mode(bq, prop.intval))
return NOTIFY_OK;
/* if automode is not enabled do not tell about reported_mode */
if (bq->automode < 1)
@ -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);

View file

@ -902,7 +902,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy,
}
static enum power_supply_property bq24190_charger_properties[] = {
POWER_SUPPLY_PROP_TYPE,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
@ -1515,6 +1515,7 @@ static const struct i2c_device_id bq24190_i2c_ids[] = {
{ "bq24190", BQ24190_REG_VPRS_PN_24190 },
{ },
};
MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
#ifdef CONFIG_OF
static const struct of_device_id bq24190_of_match[] = {
@ -1534,7 +1535,6 @@ static struct i2c_driver bq24190_driver = {
.id_table = bq24190_i2c_ids,
.driver = {
.name = "bq24190-charger",
.owner = THIS_MODULE,
.pm = &bq24190_pm_ops,
.of_match_table = of_match_ptr(bq24190_of_match),
},

View file

@ -267,7 +267,8 @@ static int bq24735_charger_probe(struct i2c_client *client,
name = (char *)charger->pdata->name;
if (!name) {
name = kasprintf(GFP_KERNEL, "bq24735@%s",
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");
@ -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,
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,32 +363,9 @@ 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 0;
err_unregister_supply:
power_supply_unregister(charger->charger);
err_free_name:
if (name != charger->pdata->name)
kfree(name);
return ret;
}
static int bq24735_charger_remove(struct i2c_client *client)
{
struct bq24735 *charger = i2c_get_clientdata(client);
if (charger->client->irq)
devm_free_irq(&charger->client->dev, charger->client->irq,
&charger->charger);
power_supply_unregister(charger->charger);
if (charger->charger_desc.name != charger->pdata->name)
kfree(charger->charger_desc.name);
}
return 0;
}
@ -409,11 +385,9 @@ MODULE_DEVICE_TABLE(of, bq24735_match_ids);
static struct i2c_driver bq24735_charger_driver = {
.driver = {
.name = "bq24735-charger",
.owner = THIS_MODULE,
.of_match_table = bq24735_match_ids,
},
.probe = bq24735_charger_probe,
.remove = bq24735_charger_remove,
.id_table = bq24735_charger_id,
};

View file

@ -41,6 +41,8 @@
#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
@ -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,12 +197,13 @@ 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
@ -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)
@ -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;
}
@ -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;
}

View file

@ -637,10 +637,6 @@ static ssize_t ds2780_read_param_eeprom_bin(struct file *filp,
struct power_supply *psy = to_power_supply(dev);
struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
count = min_t(loff_t, count,
DS2780_EEPROM_BLOCK1_END -
DS2780_EEPROM_BLOCK1_START + 1 - off);
return ds2780_read_block(dev_info, buf,
DS2780_EEPROM_BLOCK1_START + off, count);
}
@ -655,10 +651,6 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
int ret;
count = min_t(loff_t, count,
DS2780_EEPROM_BLOCK1_END -
DS2780_EEPROM_BLOCK1_START + 1 - off);
ret = ds2780_write(dev_info, buf,
DS2780_EEPROM_BLOCK1_START + off, count);
if (ret < 0)
@ -676,7 +668,7 @@ static struct bin_attribute ds2780_param_eeprom_bin_attr = {
.name = "param_eeprom",
.mode = S_IRUGO | S_IWUSR,
},
.size = DS2780_EEPROM_BLOCK1_END - DS2780_EEPROM_BLOCK1_START + 1,
.size = DS2780_PARAM_EEPROM_SIZE,
.read = ds2780_read_param_eeprom_bin,
.write = ds2780_write_param_eeprom_bin,
};
@ -690,10 +682,6 @@ static ssize_t ds2780_read_user_eeprom_bin(struct file *filp,
struct power_supply *psy = to_power_supply(dev);
struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
count = min_t(loff_t, count,
DS2780_EEPROM_BLOCK0_END -
DS2780_EEPROM_BLOCK0_START + 1 - off);
return ds2780_read_block(dev_info, buf,
DS2780_EEPROM_BLOCK0_START + off, count);
}
@ -708,10 +696,6 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
int ret;
count = min_t(loff_t, count,
DS2780_EEPROM_BLOCK0_END -
DS2780_EEPROM_BLOCK0_START + 1 - off);
ret = ds2780_write(dev_info, buf,
DS2780_EEPROM_BLOCK0_START + off, count);
if (ret < 0)
@ -729,7 +713,7 @@ static struct bin_attribute ds2780_user_eeprom_bin_attr = {
.name = "user_eeprom",
.mode = S_IRUGO | S_IWUSR,
},
.size = DS2780_EEPROM_BLOCK0_END - DS2780_EEPROM_BLOCK0_START + 1,
.size = DS2780_USER_EEPROM_SIZE,
.read = ds2780_read_user_eeprom_bin,
.write = ds2780_write_user_eeprom_bin,
};

View file

@ -639,8 +639,6 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
struct power_supply *psy = to_power_supply(dev);
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
return ds2781_read_block(dev_info, buf,
DS2781_EEPROM_BLOCK1_START + off, count);
}
@ -655,8 +653,6 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
int ret;
count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
ret = ds2781_write(dev_info, buf,
DS2781_EEPROM_BLOCK1_START + off, count);
if (ret < 0)
@ -688,8 +684,6 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
struct power_supply *psy = to_power_supply(dev);
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
return ds2781_read_block(dev_info, buf,
DS2781_EEPROM_BLOCK0_START + off, count);
@ -705,8 +699,6 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
int ret;
count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
ret = ds2781_write(dev_info, buf,
DS2781_EEPROM_BLOCK0_START + off, count);
if (ret < 0)

View file

@ -14,7 +14,6 @@
#include <linux/swab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/idr.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
@ -63,15 +62,11 @@ struct ltc294x_info {
struct power_supply_desc supply_desc; /* Supply description */
struct delayed_work work; /* Work scheduler */
int num_regs; /* Number of registers (chip type) */
int id; /* Identifier of ltc294x chip */
int charge; /* Last charge register content */
int r_sense; /* mOhm */
int Qlsb; /* nAh */
};
static DEFINE_IDR(ltc294x_id);
static DEFINE_MUTEX(ltc294x_lock);
static inline int convert_bin_to_uAh(
const struct ltc294x_info *info, int Q)
{
@ -371,10 +366,6 @@ static int ltc294x_i2c_remove(struct i2c_client *client)
cancel_delayed_work(&info->work);
power_supply_unregister(info->supply);
kfree(info->supply_desc.name);
mutex_lock(&ltc294x_lock);
idr_remove(&ltc294x_id, info->id);
mutex_unlock(&ltc294x_lock);
return 0;
}
@ -384,44 +375,28 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
struct power_supply_config psy_cfg = {};
struct ltc294x_info *info;
int ret;
int num;
u32 prescaler_exp;
s32 r_sense;
struct device_node *np;
mutex_lock(&ltc294x_lock);
ret = idr_alloc(&ltc294x_id, client, 0, 0, GFP_KERNEL);
mutex_unlock(&ltc294x_lock);
if (ret < 0)
goto fail_id;
num = ret;
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL) {
ret = -ENOMEM;
goto fail_info;
}
if (info == NULL)
return -ENOMEM;
i2c_set_clientdata(client, info);
info->num_regs = id->driver_data;
info->supply_desc.name = kasprintf(GFP_KERNEL, "%s-%d", client->name,
num);
if (!info->supply_desc.name) {
ret = -ENOMEM;
goto fail_name;
}
np = of_node_get(client->dev.of_node);
info->num_regs = id->driver_data;
info->supply_desc.name = np->name;
/* r_sense can be negative, when sense+ is connected to the battery
* instead of the sense-. This results in reversed measurements. */
ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense);
if (ret < 0) {
dev_err(&client->dev,
"Could not find lltc,resistor-sense in devicetree\n");
goto fail_name;
return ret;
}
info->r_sense = r_sense;
@ -446,7 +421,6 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
}
info->client = client;
info->id = num;
info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY;
info->supply_desc.properties = ltc294x_properties;
if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB)
@ -473,31 +447,19 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
ret = ltc294x_reset(info, prescaler_exp);
if (ret < 0) {
dev_err(&client->dev, "Communication with chip failed\n");
goto fail_comm;
return ret;
}
info->supply = power_supply_register(&client->dev, &info->supply_desc,
&psy_cfg);
if (IS_ERR(info->supply)) {
dev_err(&client->dev, "failed to register ltc2941\n");
ret = PTR_ERR(info->supply);
goto fail_register;
return PTR_ERR(info->supply);
} else {
schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
}
return 0;
fail_register:
kfree(info->supply_desc.name);
fail_comm:
fail_name:
fail_info:
mutex_lock(&ltc294x_lock);
idr_remove(&ltc294x_id, num);
mutex_unlock(&ltc294x_lock);
fail_id:
return ret;
}
#ifdef CONFIG_PM_SLEEP

View file

@ -521,11 +521,6 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
int ret;
int i;
if (off >= EEPROM_SIZE)
return 0;
if (off + count > EEPROM_SIZE)
count = EEPROM_SIZE - off;
for (i = 0; i < count; i++) {
ec_byte = EEPROM_START + off + i;
ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1);
@ -545,7 +540,7 @@ static struct bin_attribute olpc_bat_eeprom = {
.name = "eeprom",
.mode = S_IRUGO,
},
.size = 0,
.size = EEPROM_SIZE,
.read = olpc_bat_eeprom_read,
};

View file

@ -1244,7 +1244,6 @@ static struct i2c_driver pm2xxx_charger_driver = {
.remove = pm2xxx_wall_charger_remove,
.driver = {
.name = "pm2xxx-wall_charger",
.owner = THIS_MODULE,
.pm = PM2XXX_PM_OPS,
},
.id_table = pm2xxx_id,

View file

@ -166,5 +166,12 @@ config POWER_RESET_RMOBILE
help
Reboot support for Renesas R-Mobile and SH-Mobile SoCs.
config POWER_RESET_ZX
tristate "ZTE SoCs reset driver"
depends on ARCH_ZX || COMPILE_TEST
depends on HAS_IOMEM
help
Reboot support for ZTE SoCs.
endif

View file

@ -19,3 +19,4 @@ obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o

View file

@ -123,6 +123,15 @@ static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode,
return NOTIFY_DONE;
}
static int sama5d3_restart(struct notifier_block *this, unsigned long mode,
void *cmd)
{
writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST),
at91_rstc_base);
return NOTIFY_DONE;
}
static void __init at91_reset_status(struct platform_device *pdev)
{
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
@ -155,13 +164,13 @@ static void __init at91_reset_status(struct platform_device *pdev)
static const struct of_device_id at91_ramc_of_match[] = {
{ .compatible = "atmel,at91sam9260-sdramc", },
{ .compatible = "atmel,at91sam9g45-ddramc", },
{ .compatible = "atmel,sama5d3-ddramc", },
{ /* sentinel */ }
};
static const struct of_device_id at91_reset_of_match[] = {
{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart },
{ /* sentinel */ }
};
@ -181,6 +190,8 @@ static int at91_reset_of_probe(struct platform_device *pdev)
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]) {
@ -189,6 +200,7 @@ static int at91_reset_of_probe(struct platform_device *pdev)
}
idx++;
}
}
match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
at91_restart_nb.notifier_call = match->data;

View file

@ -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);

View file

@ -165,7 +165,7 @@ static const struct i2c_device_id rt5033_battery_id[] = {
{ "rt5033-battery", },
{ }
};
MODULE_DEVICE_TABLE(platform, rt5033_battery_id);
MODULE_DEVICE_TABLE(i2c, rt5033_battery_id);
static struct i2c_driver rt5033_battery_driver = {
.driver = {

View file

@ -973,7 +973,6 @@ static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info,
if (irq2 & GET_MASK(F_CHRVPI)) {
dev_dbg(dev, "Charger fault occurred\n");
alert_userspace = true;
/*
* CHRVPI bit is set in 2 cases:
* 1. when the power source is connected to the charger.
@ -981,6 +980,9 @@ static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info,
* To identify the case, PWR_RDY bit is checked. Because
* PWR_RDY bit is set / cleared after CHRVPI interrupt is
* triggered, it is used delayed_work to later read PWR_RDY bit.
* Also, do not set to true alert_userspace, because there is no
* need to notify userspace when CHRVPI interrupt has occurred.
* Userspace will be notified after PWR_RDY bit is read.
*/
queue_delayed_work(system_power_efficient_wq,
&info->pwr_rdy_work,
@ -1178,7 +1180,7 @@ static irqreturn_t rt9455_irq_handler_thread(int irq, void *data)
/*
* Sometimes, an interrupt occurs while rt9455_probe() function
* is executing and power_supply_register() is not yet called.
* Do not call power_supply_charged() in this case.
* Do not call power_supply_changed() in this case.
*/
if (info->charger)
power_supply_changed(info->charger);
@ -1478,6 +1480,11 @@ static void rt9455_pwr_rdy_work_callback(struct work_struct *work)
RT9455_MAX_CHARGING_TIME * HZ);
break;
}
/*
* Notify userspace that the charger has been either connected to or
* disconnected from the power source.
*/
power_supply_changed(info->charger);
}
static void rt9455_max_charging_time_work_callback(struct work_struct *work)
@ -1533,6 +1540,11 @@ static void rt9455_batt_presence_work_callback(struct work_struct *work)
if (ret)
dev_err(dev, "Failed to unmask BATAB interrupt\n");
}
/*
* Notify userspace that the battery is now connected to the
* charger.
*/
power_supply_changed(info->charger);
}
}

View file

@ -215,7 +215,7 @@ static int rx51_battery_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, di);
di->dev = &pdev->dev;
di->bat_desc.name = dev_name(&pdev->dev);
di->bat_desc.name = "rx51-battery";
di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
di->bat_desc.properties = rx51_battery_props;
di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props);

View file

@ -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,25 +232,231 @@ 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
*/
static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
/*
* 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 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)
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;
/*
* 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);
}
/* 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;
}
dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts);
static int twl4030_charger_get_current(void);
/* 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;
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);
return 0;
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);
}
/*
@ -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;
}
if (bci->usb_mode == CHARGE_AUTO)
/* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
if (ret < 0)
return ret;
/* 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,
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,
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");