Commit 0498cf84 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-v3.17' of git://git.infradead.org/battery-2.6

Pull power supply changes from Sebastian Reichel:
 - Added iPaq h3xxx battery driver
 - Added Broadcom STB reset driver
 - DT support for rx51-battery
 - misc. fixes

* tag 'for-v3.17' of git://git.infradead.org/battery-2.6:
  ipaq_micro_battery: fix sparse non static symbol warning
  power: add driver for battery reading on iPaq h3xxx
  power: twl4030_charger: detect battery presence prior to enabling charger
  power: reset: Add reboot driver for brcmstb
  power_supply: Fix sparse non static symbol warning
  power_supply: Add inlmt,iterm, min/max temp props
  charger: tps65090: Allow charger module to be used when no irq
  power/reset: Fix GPL v2 license string typo
  power: poweroff: gpio: convert to use descriptors
  bq27000: report missing device better.
  bq27x00_battery: Introduce the use of the managed version of kzalloc
  Documentation: DT: Document rx51-battery binding
  rx51_battery: convert to iio consumer
  bq2415x_charger: Fix Atomic Sleep Bug
parents 85417aef 5e37195f
Binding for Nokia N900 battery
The Nokia N900 battery status can be read via the TWL4030's A/D converter.
Required properties:
- compatible: Should contain one of the following:
* "nokia,n900-battery"
- io-channels: Should contain IIO channel specifiers
for each element in io-channel-names.
- io-channel-names: Should contain the following values:
* "temp" - The ADC channel for temperature reading
* "bsi" - The ADC channel for battery size identification
* "vbat" - The ADC channel to measure the battery voltage
Example from Nokia N900:
battery: n900-battery {
compatible = "nokia,n900-battery";
io-channels = <&twl4030_madc 0>,
<&twl4030_madc 4>,
<&twl4030_madc 12>;
io-channel-names = "temp",
"bsi",
"vbat";
};
...@@ -118,6 +118,10 @@ relative, time-based measurements. ...@@ -118,6 +118,10 @@ relative, time-based measurements.
CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger. CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger.
CONSTANT_CHARGE_CURRENT_MAX - maximum charge current supported by the CONSTANT_CHARGE_CURRENT_MAX - maximum charge current supported by the
power supply object. power supply object.
INPUT_CURRENT_LIMIT - input current limit programmed by charger. Indicates
the current drawn from a charging source.
CHARGE_TERM_CURRENT - Charge termination current used to detect the end of charge
condition.
CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger.
CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the
...@@ -140,6 +144,8 @@ TEMP_ALERT_MAX - maximum battery temperature alert. ...@@ -140,6 +144,8 @@ TEMP_ALERT_MAX - maximum battery temperature alert.
TEMP_AMBIENT - ambient temperature. TEMP_AMBIENT - ambient temperature.
TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert. TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert.
TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert. TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert.
TEMP_MIN - minimum operatable temperature
TEMP_MAX - maximum operatable temperature
TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e. TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e.
while battery powers a load) while battery powers a load)
......
...@@ -137,6 +137,13 @@ config BATTERY_COLLIE ...@@ -137,6 +137,13 @@ config BATTERY_COLLIE
Say Y to enable support for the battery on the Sharp Zaurus Say Y to enable support for the battery on the Sharp Zaurus
SL-5500 (collie) models. SL-5500 (collie) models.
config BATTERY_IPAQ_MICRO
tristate "iPAQ Atmel Micro ASIC battery driver"
depends on MFD_IPAQ_MICRO
help
Choose this option if you want to monitor battery status on
Compaq/HP iPAQ h3100 and h3600.
config BATTERY_WM97XX config BATTERY_WM97XX
bool "WM97xx generic battery driver" bool "WM97xx generic battery driver"
depends on TOUCHSCREEN_WM97XX=y depends on TOUCHSCREEN_WM97XX=y
......
...@@ -25,6 +25,7 @@ obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o ...@@ -25,6 +25,7 @@ obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o
obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
......
...@@ -840,8 +840,7 @@ static int bq2415x_notifier_call(struct notifier_block *nb, ...@@ -840,8 +840,7 @@ static int bq2415x_notifier_call(struct notifier_block *nb,
if (bq->automode < 1) if (bq->automode < 1)
return NOTIFY_OK; return NOTIFY_OK;
sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode"); schedule_delayed_work(&bq->work, 0);
bq2415x_set_mode(bq, bq->reported_mode);
return NOTIFY_OK; return NOTIFY_OK;
} }
...@@ -892,6 +891,11 @@ static void bq2415x_timer_work(struct work_struct *work) ...@@ -892,6 +891,11 @@ static void bq2415x_timer_work(struct work_struct *work)
int error; int error;
int boost; int boost;
if (bq->automode > 0 && (bq->reported_mode != bq->mode)) {
sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode");
bq2415x_set_mode(bq, bq->reported_mode);
}
if (!bq->autotimer) if (!bq->autotimer)
return; return;
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
* http://www.ti.com/product/bq27425-g1 * http://www.ti.com/product/bq27425-g1
*/ */
#include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/param.h> #include <linux/param.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
...@@ -415,6 +416,9 @@ static void bq27x00_update(struct bq27x00_device_info *di) ...@@ -415,6 +416,9 @@ static void bq27x00_update(struct bq27x00_device_info *di)
bool is_bq27425 = di->chip == BQ27425; bool is_bq27425 = di->chip == BQ27425;
cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
if ((cache.flags & 0xff) == 0xff)
/* read error */
cache.flags = -1;
if (cache.flags >= 0) { if (cache.flags >= 0) {
if (!is_bq27500 && !is_bq27425 if (!is_bq27500 && !is_bq27425
&& (cache.flags & BQ27000_FLAG_CI)) { && (cache.flags & BQ27000_FLAG_CI)) {
...@@ -804,7 +808,7 @@ static int bq27x00_battery_probe(struct i2c_client *client, ...@@ -804,7 +808,7 @@ static int bq27x00_battery_probe(struct i2c_client *client,
goto batt_failed_1; goto batt_failed_1;
} }
di = kzalloc(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"); dev_err(&client->dev, "failed to allocate device info data\n");
retval = -ENOMEM; retval = -ENOMEM;
...@@ -819,14 +823,12 @@ static int bq27x00_battery_probe(struct i2c_client *client, ...@@ -819,14 +823,12 @@ static int bq27x00_battery_probe(struct i2c_client *client,
retval = bq27x00_powersupply_init(di); retval = bq27x00_powersupply_init(di);
if (retval) if (retval)
goto batt_failed_3; goto batt_failed_2;
i2c_set_clientdata(client, di); i2c_set_clientdata(client, di);
return 0; return 0;
batt_failed_3:
kfree(di);
batt_failed_2: batt_failed_2:
kfree(name); kfree(name);
batt_failed_1: batt_failed_1:
...@@ -849,8 +851,6 @@ static int bq27x00_battery_remove(struct i2c_client *client) ...@@ -849,8 +851,6 @@ static int bq27x00_battery_remove(struct i2c_client *client)
idr_remove(&battery_id, di->id); idr_remove(&battery_id, di->id);
mutex_unlock(&battery_mutex); mutex_unlock(&battery_mutex);
kfree(di);
return 0; return 0;
} }
...@@ -933,7 +933,6 @@ static int bq27000_battery_probe(struct platform_device *pdev) ...@@ -933,7 +933,6 @@ static int bq27000_battery_probe(struct platform_device *pdev)
{ {
struct bq27x00_device_info *di; struct bq27x00_device_info *di;
struct bq27000_platform_data *pdata = pdev->dev.platform_data; struct bq27000_platform_data *pdata = pdev->dev.platform_data;
int ret;
if (!pdata) { if (!pdata) {
dev_err(&pdev->dev, "no platform_data supplied\n"); dev_err(&pdev->dev, "no platform_data supplied\n");
...@@ -945,7 +944,7 @@ static int bq27000_battery_probe(struct platform_device *pdev) ...@@ -945,7 +944,7 @@ static int bq27000_battery_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
di = kzalloc(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"); dev_err(&pdev->dev, "failed to allocate device info data\n");
return -ENOMEM; return -ENOMEM;
...@@ -959,16 +958,7 @@ static int bq27000_battery_probe(struct platform_device *pdev) ...@@ -959,16 +958,7 @@ static int bq27000_battery_probe(struct platform_device *pdev)
di->bat.name = pdata->name ?: dev_name(&pdev->dev); di->bat.name = pdata->name ?: dev_name(&pdev->dev);
di->bus.read = &bq27000_read_platform; di->bus.read = &bq27000_read_platform;
ret = bq27x00_powersupply_init(di); return bq27x00_powersupply_init(di);
if (ret)
goto err_free;
return 0;
err_free:
kfree(di);
return ret;
} }
static int bq27000_battery_remove(struct platform_device *pdev) static int bq27000_battery_remove(struct platform_device *pdev)
...@@ -977,8 +967,6 @@ static int bq27000_battery_remove(struct platform_device *pdev) ...@@ -977,8 +967,6 @@ static int bq27000_battery_remove(struct platform_device *pdev)
bq27x00_powersupply_unregister(di); bq27x00_powersupply_unregister(di);
kfree(di);
return 0; return 0;
} }
......
/*
* 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.
*
* h3xxx atmel micro companion support, battery subdevice
* based on previous kernel 2.4 version
* Author : Alessandro Gardich <gremlin@gremlin.it>
* Author : Linus Walleij <linus.walleij@linaro.org>
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mfd/ipaq-micro.h>
#include <linux/power_supply.h>
#include <linux/workqueue.h>
#define BATT_PERIOD 100000 /* 100 seconds in milliseconds */
#define MICRO_BATT_CHEM_ALKALINE 0x01
#define MICRO_BATT_CHEM_NICD 0x02
#define MICRO_BATT_CHEM_NIMH 0x03
#define MICRO_BATT_CHEM_LION 0x04
#define MICRO_BATT_CHEM_LIPOLY 0x05
#define MICRO_BATT_CHEM_NOT_INSTALLED 0x06
#define MICRO_BATT_CHEM_UNKNOWN 0xff
#define MICRO_BATT_STATUS_HIGH 0x01
#define MICRO_BATT_STATUS_LOW 0x02
#define MICRO_BATT_STATUS_CRITICAL 0x04
#define MICRO_BATT_STATUS_CHARGING 0x08
#define MICRO_BATT_STATUS_CHARGEMAIN 0x10
#define MICRO_BATT_STATUS_DEAD 0x20 /* Battery will not charge */
#define MICRO_BATT_STATUS_NOTINSTALLED 0x20 /* For expansion pack batteries */
#define MICRO_BATT_STATUS_FULL 0x40 /* Battery fully charged */
#define MICRO_BATT_STATUS_NOBATTERY 0x80
#define MICRO_BATT_STATUS_UNKNOWN 0xff
struct micro_battery {
struct ipaq_micro *micro;
struct workqueue_struct *wq;
struct delayed_work update;
u8 ac;
u8 chemistry;
unsigned int voltage;
u16 temperature;
u8 flag;
};
static void micro_battery_work(struct work_struct *work)
{
struct micro_battery *mb = container_of(work,
struct micro_battery, update.work);
struct ipaq_micro_msg msg_battery = {
.id = MSG_BATTERY,
};
struct ipaq_micro_msg msg_sensor = {
.id = MSG_THERMAL_SENSOR,
};
/* First send battery message */
ipaq_micro_tx_msg_sync(mb->micro, &msg_battery);
if (msg_battery.rx_len < 4)
pr_info("ERROR");
/*
* Returned message format:
* byte 0: 0x00 = Not plugged in
* 0x01 = AC adapter plugged in
* byte 1: chemistry
* byte 2: voltage LSB
* byte 3: voltage MSB
* byte 4: flags
* byte 5-9: same for battery 2
*/
mb->ac = msg_battery.rx_data[0];
mb->chemistry = msg_battery.rx_data[1];
mb->voltage = ((((unsigned short)msg_battery.rx_data[3] << 8) +
msg_battery.rx_data[2]) * 5000L) * 1000 / 1024;
mb->flag = msg_battery.rx_data[4];
if (msg_battery.rx_len == 9)
pr_debug("second battery ignored\n");
/* Then read the sensor */
ipaq_micro_tx_msg_sync(mb->micro, &msg_sensor);
mb->temperature = msg_sensor.rx_data[1] << 8 | msg_sensor.rx_data[0];
queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
}
static int get_capacity(struct power_supply *b)
{
struct micro_battery *mb = dev_get_drvdata(b->dev->parent);
switch (mb->flag & 0x07) {
case MICRO_BATT_STATUS_HIGH:
return 100;
break;
case MICRO_BATT_STATUS_LOW:
return 50;
break;
case MICRO_BATT_STATUS_CRITICAL:
return 5;
break;
default:
break;
}
return 0;
}
static int get_status(struct power_supply *b)
{
struct micro_battery *mb = dev_get_drvdata(b->dev->parent);
if (mb->flag == MICRO_BATT_STATUS_UNKNOWN)
return POWER_SUPPLY_STATUS_UNKNOWN;
if (mb->flag & MICRO_BATT_STATUS_FULL)
return POWER_SUPPLY_STATUS_FULL;
if ((mb->flag & MICRO_BATT_STATUS_CHARGING) ||
(mb->flag & MICRO_BATT_STATUS_CHARGEMAIN))
return POWER_SUPPLY_STATUS_CHARGING;
return POWER_SUPPLY_STATUS_DISCHARGING;
}
static int micro_batt_get_property(struct power_supply *b,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct micro_battery *mb = dev_get_drvdata(b->dev->parent);
switch (psp) {
case POWER_SUPPLY_PROP_TECHNOLOGY:
switch (mb->chemistry) {
case MICRO_BATT_CHEM_NICD:
val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd;
break;
case MICRO_BATT_CHEM_NIMH:
val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
break;
case MICRO_BATT_CHEM_LION:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case MICRO_BATT_CHEM_LIPOLY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
break;
default:
val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
break;
};
break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = get_status(b);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = 4700000;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = get_capacity(b);
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = mb->temperature;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = mb->voltage;
break;
default:
return -EINVAL;
};
return 0;
}
static int micro_ac_get_property(struct power_supply *b,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct micro_battery *mb = dev_get_drvdata(b->dev->parent);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval = mb->ac;
break;
default:
return -EINVAL;
};
return 0;
}
static enum power_supply_property micro_batt_power_props[] = {
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
};
static struct power_supply micro_batt_power = {
.name = "main-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = micro_batt_power_props,
.num_properties = ARRAY_SIZE(micro_batt_power_props),
.get_property = micro_batt_get_property,
.use_for_apm = 1,
};
static enum power_supply_property micro_ac_power_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static struct power_supply micro_ac_power = {
.name = "ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = micro_ac_power_props,
.num_properties = ARRAY_SIZE(micro_ac_power_props),
.get_property = micro_ac_get_property,
};
static int micro_batt_probe(struct platform_device *pdev)
{
struct micro_battery *mb;
mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
if (!mb)
return -ENOMEM;
mb->micro = dev_get_drvdata(pdev->dev.parent);
mb->wq = create_singlethread_workqueue("ipaq-battery-wq");
INIT_DELAYED_WORK(&mb->update, micro_battery_work);
platform_set_drvdata(pdev, mb);
queue_delayed_work(mb->wq, &mb->update, 1);
power_supply_register(&pdev->dev, &micro_batt_power);
power_supply_register(&pdev->dev, &micro_ac_power);
dev_info(&pdev->dev, "iPAQ micro battery driver\n");
return 0;
}
static int micro_batt_remove(struct platform_device *pdev)
{
struct micro_battery *mb = platform_get_drvdata(pdev);
power_supply_unregister(&micro_ac_power);
power_supply_unregister(&micro_batt_power);
cancel_delayed_work_sync(&mb->update);
return 0;
}
static int micro_batt_suspend(struct device *dev)
{
struct micro_battery *mb = dev_get_drvdata(dev);
cancel_delayed_work_sync(&mb->update);
return 0;
}
static int micro_batt_resume(struct device *dev)
{
struct micro_battery *mb = dev_get_drvdata(dev);
queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
return 0;
}
static const struct dev_pm_ops micro_batt_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(micro_batt_suspend, micro_batt_resume)
};
static struct platform_driver micro_batt_device_driver = {
.driver = {
.name = "ipaq-micro-battery",
.pm = &micro_batt_dev_pm_ops,
},
.probe = micro_batt_probe,
.remove = micro_batt_remove,
};
module_platform_driver(micro_batt_device_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("driver for iPAQ Atmel micro battery");
MODULE_ALIAS("platform:battery-ipaq-micro");
...@@ -537,7 +537,8 @@ static void psy_unregister_cooler(struct power_supply *psy) ...@@ -537,7 +537,8 @@ static void psy_unregister_cooler(struct power_supply *psy)
} }
#endif #endif
int __power_supply_register(struct device *parent, struct power_supply *psy, bool ws) static int __power_supply_register(struct device *parent,
struct power_supply *psy, bool ws)
{ {
struct device *dev; struct device *dev;
int rc; int rc;
......
...@@ -167,6 +167,7 @@ static struct device_attribute power_supply_attrs[] = { ...@@ -167,6 +167,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(constant_charge_voltage_max), POWER_SUPPLY_ATTR(constant_charge_voltage_max),
POWER_SUPPLY_ATTR(charge_control_limit), POWER_SUPPLY_ATTR(charge_control_limit),
POWER_SUPPLY_ATTR(charge_control_limit_max), POWER_SUPPLY_ATTR(charge_control_limit_max),
POWER_SUPPLY_ATTR(input_current_limit),
POWER_SUPPLY_ATTR(energy_full_design), POWER_SUPPLY_ATTR(energy_full_design),
POWER_SUPPLY_ATTR(energy_empty_design), POWER_SUPPLY_ATTR(energy_empty_design),
POWER_SUPPLY_ATTR(energy_full), POWER_SUPPLY_ATTR(energy_full),
...@@ -178,6 +179,8 @@ static struct device_attribute power_supply_attrs[] = { ...@@ -178,6 +179,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(capacity_alert_max), POWER_SUPPLY_ATTR(capacity_alert_max),
POWER_SUPPLY_ATTR(capacity_level), POWER_SUPPLY_ATTR(capacity_level),
POWER_SUPPLY_ATTR(temp), POWER_SUPPLY_ATTR(temp),
POWER_SUPPLY_ATTR(temp_max),
POWER_SUPPLY_ATTR(temp_min),
POWER_SUPPLY_ATTR(temp_alert_min), POWER_SUPPLY_ATTR(temp_alert_min),
POWER_SUPPLY_ATTR(temp_alert_max), POWER_SUPPLY_ATTR(temp_alert_max),
POWER_SUPPLY_ATTR(temp_ambient), POWER_SUPPLY_ATTR(temp_ambient),
...@@ -189,6 +192,7 @@ static struct device_attribute power_supply_attrs[] = { ...@@ -189,6 +192,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(time_to_full_avg), POWER_SUPPLY_ATTR(time_to_full_avg),
POWER_SUPPLY_ATTR(type), POWER_SUPPLY_ATTR(type),
POWER_SUPPLY_ATTR(scope), POWER_SUPPLY_ATTR(scope),
POWER_SUPPLY_ATTR(charge_term_current),
/* Properties of type `const char *' */ /* Properties of type `const char *' */
POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer), POWER_SUPPLY_ATTR(manufacturer),
......
...@@ -20,6 +20,17 @@ config POWER_RESET_AXXIA ...@@ -20,6 +20,17 @@ config POWER_RESET_AXXIA
Say Y if you have an Axxia family SoC. Say Y if you have an Axxia family SoC.
config POWER_RESET_BRCMSTB
bool "Broadcom STB reset driver" if COMPILE_TEST
depends on POWER_RESET && ARM
default ARCH_BRCMSTB
help
This driver provides restart support for ARM-based Broadcom STB
boards.
Say Y here if you have an ARM-based Broadcom STB board and you wish
to have restart support.
config POWER_RESET_GPIO config POWER_RESET_GPIO
bool "GPIO power-off driver" bool "GPIO power-off driver"
depends on OF_GPIO && POWER_RESET depends on OF_GPIO && POWER_RESET
......
obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o
obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
......
/*
* Copyright (C) 2013 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/smp.h>
#include <linux/mfd/syscon.h>
#include <asm/system_misc.h>
#define RESET_SOURCE_ENABLE_REG 1
#define SW_MASTER_RESET_REG 2
static struct regmap *regmap;
static u32 rst_src_en;
static u32 sw_mstr_rst;
static void brcmstb_reboot(enum reboot_mode mode, const char *cmd)
{
int rc;
u32 tmp;
rc = regmap_write(regmap, rst_src_en, 1);
if (rc) {
pr_err("failed to write rst_src_en (%d)\n", rc);
return;
}
rc = regmap_read(regmap, rst_src_en, &tmp);
if (rc) {
pr_err("failed to read rst_src_en (%d)\n", rc);
return;
}
rc = regmap_write(regmap, sw_mstr_rst, 1);
if (rc) {
pr_err("failed to write sw_mstr_rst (%d)\n", rc);
return;
}
rc = regmap_read(regmap, sw_mstr_rst, &tmp);
if (rc) {
pr_err("failed to read sw_mstr_rst (%d)\n", rc);
return;
}
while (1)
;
}
static int brcmstb_reboot_probe(struct platform_device *pdev)
{
int rc;
struct device_node *np = pdev->dev.of_node;
regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
if (IS_ERR(regmap)) {
pr_err("failed to get syscon phandle\n");
return -EINVAL;
}
rc = of_property_read_u32_index(np, "syscon", RESET_SOURCE_ENABLE_REG,
&rst_src_en);
if (rc) {
pr_err("can't get rst_src_en offset (%d)\n", rc);
return -EINVAL;
}
rc = of_property_read_u32_index(np, "syscon", SW_MASTER_RESET_REG,
&sw_mstr_rst);
if (rc) {
pr_err("can't get sw_mstr_rst offset (%d)\n", rc);
return -EINVAL;
}
arm_pm_restart = brcmstb_reboot;
return 0;
}
static const struct of_device_id of_match[] = {
{ .compatible = "brcm,brcmstb-reboot", },
{},
};
static struct platform_driver brcmstb_reboot_driver = {
.probe = brcmstb_reboot_probe,
.driver = {
.name = "brcmstb-reboot",
.owner = THIS_MODULE,
.of_match_table = of_match,
},
};
static int __init brcmstb_reboot_init(void)
{
return platform_driver_probe(&brcmstb_reboot_driver,
brcmstb_reboot_probe);
}
subsys_initcall(brcmstb_reboot_init);
...@@ -15,31 +15,29 @@ ...@@ -15,31 +15,29 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/gpio.h> #include <linux/gpio/consumer.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/module.h> #include <linux/module.h>
/* /*
* Hold configuration here, cannot be more than one instance of the driver * Hold configuration here, cannot be more than one instance of the driver
* since pm_power_off itself is global. * since pm_power_off itself is global.
*/ */
static int gpio_num = -1; static struct gpio_desc *reset_gpio;
static int gpio_active_low;
static void gpio_poweroff_do_poweroff(void) static void gpio_poweroff_do_poweroff(void)
{ {
BUG_ON(!gpio_is_valid(gpio_num)); BUG_ON(!reset_gpio);
/* drive it active, also inactive->active edge */ /* drive it active, also inactive->active edge */
gpio_direction_output(gpio_num, !gpio_active_low); gpiod_direction_output(reset_gpio, 1);
mdelay(100); mdelay(100);
/* drive inactive, also active->inactive edge */ /* drive inactive, also active->inactive edge */
gpio_set_value(gpio_num, gpio_active_low); gpiod_set_value(reset_gpio, 0);
mdelay(100); mdelay(100);
/* drive it active, also inactive->active edge */ /* drive it active, also inactive->active edge */
gpio_set_value(gpio_num, !gpio_active_low); gpiod_set_value(reset_gpio, 1);
/* give it some time */ /* give it some time */
mdelay(3000); mdelay(3000);
...@@ -49,54 +47,42 @@ static void gpio_poweroff_do_poweroff(void) ...@@ -49,54 +47,42 @@ static void gpio_poweroff_do_poweroff(void)
static int gpio_poweroff_probe(struct platform_device *pdev) static int gpio_poweroff_probe(struct platform_device *pdev)
{ {
enum of_gpio_flags flags;
bool input = false; bool input = false;
int ret;
/* If a pm_power_off function has already been added, leave it alone */ /* If a pm_power_off function has already been added, leave it alone */
if (pm_power_off != NULL) { if (pm_power_off != NULL) {
pr_err("%s: pm_power_off function already registered", dev_err(&pdev->dev,
"%s: pm_power_off function already registered",
__func__); __func__);
return -EBUSY; return -EBUSY;
} }
gpio_num = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); reset_gpio = devm_gpiod_get(&pdev->dev, NULL);
if (!gpio_is_valid(gpio_num)) if (IS_ERR(reset_gpio))
return gpio_num; return PTR_ERR(reset_gpio);
gpio_active_low = flags & OF_GPIO_ACTIVE_LOW;
input = of_property_read_bool(pdev->dev.of_node, "input"); input = of_property_read_bool(pdev->dev.of_node, "input");
ret = gpio_request(gpio_num, "poweroff-gpio");
if (ret) {
pr_err("%s: Could not get GPIO %d", __func__, gpio_num);
return ret;
}
if (input) { if (input) {
if (gpio_direction_input(gpio_num)) { if (gpiod_direction_input(reset_gpio)) {
pr_err("Could not set direction of GPIO %d to input", dev_err(&pdev->dev,
gpio_num); "Could not set direction of reset GPIO to input\n");
goto err; return -ENODEV;
} }
} else { } else {
if (gpio_direction_output(gpio_num, gpio_active_low)) { if (gpiod_direction_output(reset_gpio, 0)) {
pr_err("Could not set direction of GPIO %d", gpio_num); dev_err(&pdev->dev,
goto err; "Could not set direction of reset GPIO\n");
return -ENODEV;
} }
} }
pm_power_off = &gpio_poweroff_do_poweroff; pm_power_off = &gpio_poweroff_do_poweroff;
return 0; return 0;
err:
gpio_free(gpio_num);
return -ENODEV;
} }
static int gpio_poweroff_remove(struct platform_device *pdev) static int gpio_poweroff_remove(struct platform_device *pdev)
{ {
gpio_free(gpio_num);
if (pm_power_off == &gpio_poweroff_do_poweroff) if (pm_power_off == &gpio_poweroff_do_poweroff)
pm_power_off = NULL; pm_power_off = NULL;
......
...@@ -62,5 +62,5 @@ module_platform_driver(restart_poweroff_driver); ...@@ -62,5 +62,5 @@ module_platform_driver(restart_poweroff_driver);
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch"); MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch");
MODULE_DESCRIPTION("restart poweroff driver"); MODULE_DESCRIPTION("restart poweroff driver");
MODULE_LICENSE("GPLv2"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:poweroff-restart"); MODULE_ALIAS("platform:poweroff-restart");
...@@ -24,34 +24,27 @@ ...@@ -24,34 +24,27 @@
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/i2c/twl4030-madc.h> #include <linux/i2c/twl4030-madc.h>
#include <linux/iio/consumer.h>
/* RX51 specific channels */ #include <linux/of.h>
#define TWL4030_MADC_BTEMP_RX51 TWL4030_MADC_ADCIN0
#define TWL4030_MADC_BCI_RX51 TWL4030_MADC_ADCIN4
struct rx51_device_info { struct rx51_device_info {
struct device *dev; struct device *dev;
struct power_supply bat; struct power_supply bat;
struct iio_channel *channel_temp;
struct iio_channel *channel_bsi;
struct iio_channel *channel_vbat;
}; };
/* /*
* Read ADCIN channel value, code copied from maemo kernel * Read ADCIN channel value, code copied from maemo kernel
*/ */
static int rx51_battery_read_adc(int channel) static int rx51_battery_read_adc(struct iio_channel *channel)
{ {
struct twl4030_madc_request req; int val, err;
err = iio_read_channel_average_raw(channel, &val);
req.channels = channel; if (err < 0)
req.do_avg = 1; return err;
req.method = TWL4030_MADC_SW1; return val;
req.func_cb = NULL;
req.type = TWL4030_MADC_WAIT;
req.raw = true;
if (twl4030_madc_conversion(&req) <= 0)
return -ENODATA;
return req.rbuf[ffs(channel) - 1];
} }
/* /*
...@@ -60,10 +53,12 @@ static int rx51_battery_read_adc(int channel) ...@@ -60,10 +53,12 @@ static int rx51_battery_read_adc(int channel)
*/ */
static int rx51_battery_read_voltage(struct rx51_device_info *di) static int rx51_battery_read_voltage(struct rx51_device_info *di)
{ {
int voltage = rx51_battery_read_adc(TWL4030_MADC_VBAT); int voltage = rx51_battery_read_adc(di->channel_vbat);
if (voltage < 0) if (voltage < 0) {
dev_err(di->dev, "Could not read ADC: %d\n", voltage);
return voltage; return voltage;
}
return 1000 * (10000 * voltage / 1705); return 1000 * (10000 * voltage / 1705);
} }
...@@ -112,7 +107,10 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di) ...@@ -112,7 +107,10 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di)
{ {
int min = 0; int min = 0;
int max = ARRAY_SIZE(rx51_temp_table2) - 1; int max = ARRAY_SIZE(rx51_temp_table2) - 1;
int raw = rx51_battery_read_adc(TWL4030_MADC_BTEMP_RX51); int raw = rx51_battery_read_adc(di->channel_temp);
if (raw < 0)
dev_err(di->dev, "Could not read ADC: %d\n", raw);
/* Zero and negative values are undefined */ /* Zero and negative values are undefined */
if (raw <= 0) if (raw <= 0)
...@@ -146,10 +144,12 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di) ...@@ -146,10 +144,12 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di)
*/ */
static int rx51_battery_read_capacity(struct rx51_device_info *di) static int rx51_battery_read_capacity(struct rx51_device_info *di)
{ {
int capacity = rx51_battery_read_adc(TWL4030_MADC_BCI_RX51); int capacity = rx51_battery_read_adc(di->channel_bsi);
if (capacity < 0) if (capacity < 0) {
dev_err(di->dev, "Could not read ADC: %d\n", capacity);
return capacity; return capacity;
}
return 1280 * (1200 * capacity)/(1024 - capacity); return 1280 * (1200 * capacity)/(1024 - capacity);
} }
...@@ -213,17 +213,46 @@ static int rx51_battery_probe(struct platform_device *pdev) ...@@ -213,17 +213,46 @@ static int rx51_battery_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, di); platform_set_drvdata(pdev, di);
di->dev = &pdev->dev;
di->bat.name = dev_name(&pdev->dev); di->bat.name = dev_name(&pdev->dev);
di->bat.type = POWER_SUPPLY_TYPE_BATTERY; di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
di->bat.properties = rx51_battery_props; di->bat.properties = rx51_battery_props;
di->bat.num_properties = ARRAY_SIZE(rx51_battery_props); di->bat.num_properties = ARRAY_SIZE(rx51_battery_props);
di->bat.get_property = rx51_battery_get_property; di->bat.get_property = rx51_battery_get_property;
di->channel_temp = iio_channel_get(di->dev, "temp");
if (IS_ERR(di->channel_temp)) {
ret = PTR_ERR(di->channel_temp);
goto error;
}
di->channel_bsi = iio_channel_get(di->dev, "bsi");
if (IS_ERR(di->channel_bsi)) {
ret = PTR_ERR(di->channel_bsi);
goto error_channel_temp;
}
di->channel_vbat = iio_channel_get(di->dev, "vbat");
if (IS_ERR(di->channel_vbat)) {
ret = PTR_ERR(di->channel_vbat);
goto error_channel_bsi;
}
ret = power_supply_register(di->dev, &di->bat); ret = power_supply_register(di->dev, &di->bat);
if (ret) if (ret)
return ret; goto error_channel_vbat;
return 0; return 0;
error_channel_vbat:
iio_channel_release(di->channel_vbat);
error_channel_bsi:
iio_channel_release(di->channel_bsi);
error_channel_temp:
iio_channel_release(di->channel_temp);
error:
return ret;
} }
static int rx51_battery_remove(struct platform_device *pdev) static int rx51_battery_remove(struct platform_device *pdev)
...@@ -232,15 +261,28 @@ static int rx51_battery_remove(struct platform_device *pdev) ...@@ -232,15 +261,28 @@ static int rx51_battery_remove(struct platform_device *pdev)
power_supply_unregister(&di->bat); power_supply_unregister(&di->bat);
iio_channel_release(di->channel_vbat);
iio_channel_release(di->channel_bsi);
iio_channel_release(di->channel_temp);
return 0; return 0;
} }
#ifdef CONFIG_OF
static const struct of_device_id n900_battery_of_match[] = {
{.compatible = "nokia,n900-battery", },
{ },
};
MODULE_DEVICE_TABLE(of, n900_battery_of_match);
#endif
static struct platform_driver rx51_battery_driver = { static struct platform_driver rx51_battery_driver = {
.probe = rx51_battery_probe, .probe = rx51_battery_probe,
.remove = rx51_battery_remove, .remove = rx51_battery_remove,
.driver = { .driver = {
.name = "rx51-battery", .name = "rx51-battery",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(n900_battery_of_match),
}, },
}; };
module_platform_driver(rx51_battery_driver); module_platform_driver(rx51_battery_driver);
......
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
*/ */
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/freezer.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
...@@ -32,11 +34,15 @@ ...@@ -32,11 +34,15 @@
#define TPS65090_VACG BIT(1) #define TPS65090_VACG BIT(1)
#define TPS65090_NOITERM BIT(5) #define TPS65090_NOITERM BIT(5)
#define POLL_INTERVAL (HZ * 2) /* Used when no irq */
struct tps65090_charger { struct tps65090_charger {
struct device *dev; struct device *dev;
int ac_online; int ac_online;
int prev_ac_online; int prev_ac_online;
int irq; int irq;
struct task_struct *poll_task;
bool passive_mode;
struct power_supply ac; struct power_supply ac;
struct tps65090_platform_data *pdata; struct tps65090_platform_data *pdata;
}; };
...@@ -49,6 +55,9 @@ static int tps65090_low_chrg_current(struct tps65090_charger *charger) ...@@ -49,6 +55,9 @@ static int tps65090_low_chrg_current(struct tps65090_charger *charger)
{ {
int ret; int ret;
if (charger->passive_mode)
return 0;
ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5, ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5,
TPS65090_NOITERM); TPS65090_NOITERM);
if (ret < 0) { if (ret < 0) {
...@@ -64,6 +73,9 @@ static int tps65090_enable_charging(struct tps65090_charger *charger) ...@@ -64,6 +73,9 @@ static int tps65090_enable_charging(struct tps65090_charger *charger)
int ret; int ret;
uint8_t ctrl0 = 0; uint8_t ctrl0 = 0;
if (charger->passive_mode)
return 0;
ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0, ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0,
&ctrl0); &ctrl0);
if (ret < 0) { if (ret < 0) {
...@@ -87,6 +99,9 @@ static int tps65090_config_charger(struct tps65090_charger *charger) ...@@ -87,6 +99,9 @@ static int tps65090_config_charger(struct tps65090_charger *charger)
uint8_t intrmask = 0; uint8_t intrmask = 0;
int ret; int ret;
if (charger->passive_mode)
return 0;
if (charger->pdata->enable_low_current_chrg) { if (charger->pdata->enable_low_current_chrg) {
ret = tps65090_low_chrg_current(charger); ret = tps65090_low_chrg_current(charger);
if (ret < 0) { if (ret < 0) {
...@@ -164,10 +179,14 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id) ...@@ -164,10 +179,14 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
} }
/* Clear interrupts. */ /* Clear interrupts. */
ret = tps65090_write(charger->dev->parent, TPS65090_REG_INTR_STS, 0x00); if (!charger->passive_mode) {
if (ret < 0) { ret = tps65090_write(charger->dev->parent,
dev_err(charger->dev, "%s(): Error in writing reg 0x%x\n", TPS65090_REG_INTR_STS, 0x00);
if (ret < 0) {
dev_err(charger->dev,
"%s(): Error in writing reg 0x%x\n",
__func__, TPS65090_REG_INTR_STS); __func__, TPS65090_REG_INTR_STS);
}
} }
if (charger->prev_ac_online != charger->ac_online) if (charger->prev_ac_online != charger->ac_online)
...@@ -198,6 +217,18 @@ static struct tps65090_platform_data * ...@@ -198,6 +217,18 @@ static struct tps65090_platform_data *
} }
static int tps65090_charger_poll_task(void *data)
{
set_freezable();
while (!kthread_should_stop()) {
schedule_timeout_interruptible(POLL_INTERVAL);
try_to_freeze();
tps65090_charger_isr(-1, data);
}
return 0;
}
static int tps65090_charger_probe(struct platform_device *pdev) static int tps65090_charger_probe(struct platform_device *pdev)
{ {
struct tps65090_charger *cdata; struct tps65090_charger *cdata;
...@@ -244,22 +275,10 @@ static int tps65090_charger_probe(struct platform_device *pdev) ...@@ -244,22 +275,10 @@ static int tps65090_charger_probe(struct platform_device *pdev)
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq <= 0) { if (irq < 0)
dev_warn(&pdev->dev, "Unable to get charger irq = %d\n", irq); irq = -ENXIO;
ret = irq;
goto fail_unregister_supply;
}
cdata->irq = irq; cdata->irq = irq;
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
tps65090_charger_isr, 0, "tps65090-charger", cdata);
if (ret) {
dev_err(cdata->dev, "Unable to register irq %d err %d\n", irq,
ret);
goto fail_unregister_supply;
}
ret = tps65090_config_charger(cdata); ret = tps65090_config_charger(cdata);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "charger config failed, err %d\n", ret); dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
...@@ -285,6 +304,27 @@ static int tps65090_charger_probe(struct platform_device *pdev) ...@@ -285,6 +304,27 @@ static int tps65090_charger_probe(struct platform_device *pdev)
power_supply_changed(&cdata->ac); power_supply_changed(&cdata->ac);
} }
if (irq != -ENXIO) {
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
tps65090_charger_isr, 0, "tps65090-charger", cdata);
if (ret) {
dev_err(cdata->dev,
"Unable to register irq %d err %d\n", irq,
ret);
goto fail_unregister_supply;
}
} else {
cdata->poll_task = kthread_run(tps65090_charger_poll_task,
cdata, "ktps65090charger");
cdata->passive_mode = true;
if (IS_ERR(cdata->poll_task)) {
ret = PTR_ERR(cdata->poll_task);
dev_err(cdata->dev,
"Unable to run kthread err %d\n", ret);
goto fail_unregister_supply;
}
}
return 0; return 0;
fail_unregister_supply: fail_unregister_supply:
...@@ -297,6 +337,8 @@ static int tps65090_charger_remove(struct platform_device *pdev) ...@@ -297,6 +337,8 @@ static int tps65090_charger_remove(struct platform_device *pdev)
{ {
struct tps65090_charger *cdata = platform_get_drvdata(pdev); struct tps65090_charger *cdata = platform_get_drvdata(pdev);
if (cdata->irq == -ENXIO)
kthread_stop(cdata->poll_task);
power_supply_unregister(&cdata->ac); power_supply_unregister(&cdata->ac);
return 0; return 0;
......
...@@ -28,10 +28,13 @@ ...@@ -28,10 +28,13 @@
#define TWL4030_BCIICHG 0x08 #define TWL4030_BCIICHG 0x08
#define TWL4030_BCIVAC 0x0a #define TWL4030_BCIVAC 0x0a
#define TWL4030_BCIVBUS 0x0c #define TWL4030_BCIVBUS 0x0c
#define TWL4030_BCIMFSTS3 0x0F
#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_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_BCIAUTOUSB BIT(1) #define TWL4030_BCIAUTOUSB BIT(1)
...@@ -52,6 +55,9 @@ ...@@ -52,6 +55,9 @@
#define TWL4030_BBISEL_500uA 0x02 #define TWL4030_BBISEL_500uA 0x02
#define TWL4030_BBISEL_1000uA 0x03 #define TWL4030_BBISEL_1000uA 0x03
#define TWL4030_BATSTSPCHG BIT(2)
#define TWL4030_BATSTSMCHG BIT(6)
/* BCI interrupts */ /* BCI interrupts */
#define TWL4030_WOVF BIT(0) /* Watchdog overflow */ #define TWL4030_WOVF BIT(0) /* Watchdog overflow */
#define TWL4030_TMOVF BIT(1) /* Timer overflow */ #define TWL4030_TMOVF BIT(1) /* Timer overflow */
...@@ -144,6 +150,35 @@ static int twl4030bci_read_adc_val(u8 reg) ...@@ -144,6 +150,35 @@ static int twl4030bci_read_adc_val(u8 reg)
return temp | val; return temp | val;
} }
/*
* Check if Battery Pack was present
*/
static int twl4030_is_battery_present(struct twl4030_bci *bci)
{
int ret;
u8 val = 0;
/* Battery presence in Main charge? */
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMFSTS3);
if (ret)
return ret;
if (val & TWL4030_BATSTSMCHG)
return 0;
/*
* OK, It could be that bootloader did not enable main charger,
* pre-charge is h/w auto. So, Battery presence in Pre-charge?
*/
ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, &val,
TWL4030_BCIMFSTS1);
if (ret)
return ret;
if (val & TWL4030_BATSTSPCHG)
return 0;
return -ENODEV;
}
/* /*
* Check if VBUS power is present * Check if VBUS power is present
*/ */
...@@ -541,8 +576,14 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -541,8 +576,14 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
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);
platform_set_drvdata(pdev, bci); /* Only proceed further *IF* battery is physically present */
ret = twl4030_is_battery_present(bci);
if (ret) {
dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret);
goto fail_no_battery;
}
platform_set_drvdata(pdev, bci);
bci->ac.name = "twl4030_ac"; bci->ac.name = "twl4030_ac";
bci->ac.type = POWER_SUPPLY_TYPE_MAINS; bci->ac.type = POWER_SUPPLY_TYPE_MAINS;
bci->ac.properties = twl4030_charger_props; bci->ac.properties = twl4030_charger_props;
...@@ -633,6 +674,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -633,6 +674,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
fail_register_usb: fail_register_usb:
power_supply_unregister(&bci->ac); power_supply_unregister(&bci->ac);
fail_register_ac: fail_register_ac:
fail_no_battery:
kfree(bci); kfree(bci);
return ret; return ret;
......
...@@ -120,6 +120,7 @@ enum power_supply_property { ...@@ -120,6 +120,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
POWER_SUPPLY_PROP_ENERGY_FULL, POWER_SUPPLY_PROP_ENERGY_FULL,
...@@ -131,6 +132,8 @@ enum power_supply_property { ...@@ -131,6 +132,8 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_MAX,
POWER_SUPPLY_PROP_TEMP_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MIN, POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MAX, POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
POWER_SUPPLY_PROP_TEMP_AMBIENT, POWER_SUPPLY_PROP_TEMP_AMBIENT,
...@@ -142,6 +145,7 @@ enum power_supply_property { ...@@ -142,6 +145,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
/* Properties of type `const char *' */ /* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_MANUFACTURER,
......
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