Highlights:

- OpenFirmware/DeviceTree support for the Power Supply core: the core now
   automatically populates supplied_from hierarchy from the device tree.
   With these patches chargers and batteries can now lookup each other
   without the board files support shim. Rhyland Klein at NVIDIA did the
   work;
 
 - New ST-Ericsson ABX500 hwmon driver. The driver is heavily using the
   AB85xx core and depends on some recent changes to it, so that is why the
   driver comes through the battery tree. It has an appropriate ack from
   the hwmon maintainer (i.e. Guenter Roeck). Martin Persson at ST-Ericsson
   and Hongbo Zhang at Linaro authored the driver;
 
 - Final bits to sync AB85xx ST-Ericsson changes into mainline. The changes
   touch mfd parts, but these were acked by the appropriate MFD maintainer
   (i.e. Samuel Ortiz). Lee Jones at Linaro did most of the work and lead
   the submission process.
 
 Minor changes, but still worth mentioning:
 
 - Battery temperature reporting fix for Nokia N900 phones;
 - Versatile Express poweroff driver moved into drivers/power/reset/.
 - Tree-wise: use devm_kzalloc() where appropriate;
 - Tree-wise: dev_pm_ops cleanups/fixes.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.19 (GNU/Linux)
 
 iQIcBAABAgAGBQJRf1BPAAoJEGgI9fZJve1bwyoP/3Gv+qStzbN7mUtIVEvH3EAe
 aVJwlODFzEZjk5xoiw7Dc8PuBE8O948hWOnQyCuUQ8+OfK6SyNIjexPYy3Z25a0F
 cX9JMj7rtPWHvxo2q/YuKwBPZoxj/JIPyxwUT7akwXoHAV059fvcy9R1DfFX2Qur
 hSP0NXTg+guvEpxGV4bC2l+LWZPmDFK9n0RsorttYaBvsiRDWl0c2TY2byofYlBw
 ++m/rI8Qgl8db8pKq/WDue62HtMt3kmZj6ZIgej3Wb0+GIRmYHMyPIyAkf82Wlw2
 g2sGNPT7cstrSNOozegzJ7UghJObcYDFf10NCgvFMNjmAT1dAwdneQHEWy6Ek7pT
 9X3e0LmaFqVbufFp4xFiLkMutsCLLTnGyXIbzs7RkTm3XBVIUqiDWtI6I6X44ohG
 6PJn8vUlufu7owXrqFpgSBar2U1vfoQdhInmz4hbQeff0qn2nX/BGNwhxYptZ549
 TudsI9WGzJ6fvYQ56zh6+BfiA0FmjhUiSKOtrXImrhxE6gUf3IOJyMQlkxLx5t8D
 uuhBmO0J6kDi2lqF6alOEo+UDefJj4mUJn2tnIdis90+lNQlSV02GEtiwFT1zt1z
 LFW0xshQkxZ4lMa28h35FB1/Z11ApUOe4Es+OKADDJhAnxdZzXcAwIRyPRRPgdsy
 jTnJno+Kxk9wXLcekxVE
 =5BdE
 -----END PGP SIGNATURE-----

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

Pull battery updates from Anton Vorontsov:
 "Highlights:

   - OpenFirmware/DeviceTree support for the Power Supply core: the core
     now automatically populates supplied_from hierarchy from the device
     tree.  With these patches chargers and batteries can now lookup
     each other without the board files support shim.  Rhyland Klein at
     NVIDIA did the work

   - New ST-Ericsson ABX500 hwmon driver.  The driver is heavily using
     the AB85xx core and depends on some recent changes to it, so that
     is why the driver comes through the battery tree.  It has an
     appropriate ack from the hwmon maintainer (i.e.  Guenter Roeck).
     Martin Persson at ST-Ericsson and Hongbo Zhang at Linaro authored
     the driver

   - Final bits to sync AB85xx ST-Ericsson changes into mainline.  The
     changes touch mfd parts, but these were acked by the appropriate
     MFD maintainer (ie Samuel Ortiz).  Lee Jones at Linaro did most of
     the work and lead the submission process.

  Minor changes, but still worth mentioning:

   - Battery temperature reporting fix for Nokia N900 phones
   - Versatile Express poweroff driver moved into drivers/power/reset/
   - Tree-wide: use devm_kzalloc() where appropriate
   - Tree-wide: dev_pm_ops cleanups/fixes"

* tag 'for-v3.10' of git://git.infradead.org/battery-2.6: (112 commits)
  pm2301-charger: Fix suspend/resume
  charger-manager: Use kmemdup instead of kzalloc + memcpy
  power_supply: Populate supplied_from hierarchy from the device tree
  power_supply: Add core support for supplied_from
  power_supply: Define Binding for power-supplies
  rx51_battery: Fix reporting temperature
  hwmon: Add ST-Ericsson ABX500 hwmon driver
  ab8500_bmdata: Export abx500_res_to_temp tables for hwmon
  ab8500_{bmdata,fg}: Add const attributes to some data arrays
  ab8500_bmdata: Eliminate CamelCase warning of some variables
  ab8500_btemp: Make ab8500_btemp_get* interfaces public
  goldfish_battery: Use resource_size()
  lp8788-charger: Use PAGE_SIZE for the sysfs read operation
  max8925_power: Use devm_kzalloc()
  da9030_battery: Use devm_kzalloc()
  da9052-battery: Use devm_kzalloc()
  ds2760_battery: Use devm_kzalloc()
  ds2780_battery: Use devm_kzalloc()
  gpio-charger: Use devm_kzalloc()
  isp1704_charger: Use devm_kzalloc()
  ...
This commit is contained in:
Linus Torvalds 2013-04-30 15:15:24 -07:00
commit 151173e8ce
64 changed files with 6024 additions and 1131 deletions

View File

@ -0,0 +1,23 @@
Power Supply Core Support
Optional Properties:
- power-supplies : This property is added to a supply in order to list the
devices which supply it power, referenced by their phandles.
Example:
usb-charger: power@e {
compatible = "some,usb-charger";
...
};
ac-charger: power@c {
compatible = "some,ac-charger";
...
};
battery@b {
compatible = "some,battery";
...
power-supplies = <&usb-charger>, <&ac-charger>;
};

View File

@ -0,0 +1,17 @@
TPS65090 Frontend PMU with Switchmode Charger
Required Properties:
-compatible: "ti,tps65090-charger"
Optional Properties:
-ti,enable-low-current-chrg: Enables charging when a low current is detected
while the default logic is to stop charging.
This node is a subnode of the tps65090 PMIC.
Example:
tps65090-charger {
compatible = "ti,tps65090-charger";
ti,enable-low-current-chrg;
};

View File

@ -0,0 +1,22 @@
Kernel driver ab8500
====================
Supported chips:
* ST-Ericsson AB8500
Prefix: 'ab8500'
Addresses scanned: -
Datasheet: http://www.stericsson.com/developers/documentation.jsp
Authors:
Martin Persson <martin.persson@stericsson.com>
Hongbo Zhang <hongbo.zhang@linaro.org>
Description
-----------
See also Documentation/hwmon/abx500. This is the ST-Ericsson AB8500 specific
driver.
Currently only the AB8500 internal sensor and one external sensor for battery
temperature are monitored. Other GPADC channels can also be monitored if needed
in future.

View File

@ -0,0 +1,28 @@
Kernel driver abx500
====================
Supported chips:
* ST-Ericsson ABx500 series
Prefix: 'abx500'
Addresses scanned: -
Datasheet: http://www.stericsson.com/developers/documentation.jsp
Authors:
Martin Persson <martin.persson@stericsson.com>
Hongbo Zhang <hongbo.zhang@linaro.org>
Description
-----------
Every ST-Ericsson Ux500 SOC consists of both ABx500 and DBx500 physically,
this is kernel hwmon driver for ABx500.
There are some GPADCs inside ABx500 which are designed for connecting to
thermal sensors, and there is also a thermal sensor inside ABx500 too, which
raises interrupt when critical temperature reached.
This abx500 is a common layer which can monitor all of the sensors, every
specific abx500 chip has its special configurations in its own file, e.g. some
sensors can be configured invisible if they are not available on that chip, and
the corresponding gpadc_addr should be set to 0, thus this sensor won't be
polled.

View File

@ -17,6 +17,9 @@ config ARCH_VEXPRESS
select NO_IOPORT
select PLAT_VERSATILE
select PLAT_VERSATILE_CLCD
select POWER_RESET
select POWER_RESET_VEXPRESS
select POWER_SUPPLY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
select VEXPRESS_CONFIG
help

View File

@ -4,7 +4,7 @@
ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \
-I$(srctree)/arch/arm/plat-versatile/include
obj-y := v2m.o reset.o
obj-y := v2m.o
obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o
obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o

View File

@ -363,8 +363,6 @@ static void __init v2m_init(void)
for (i = 0; i < ARRAY_SIZE(v2m_amba_devs); i++)
amba_device_register(v2m_amba_devs[i], &iomem_resource);
pm_power_off = vexpress_power_off;
ct_desc->init_tile();
}
@ -376,7 +374,6 @@ MACHINE_START(VEXPRESS, "ARM-Versatile Express")
.init_irq = v2m_init_irq,
.init_time = v2m_timer_init,
.init_machine = v2m_init,
.restart = vexpress_restart,
MACHINE_END
static struct map_desc v2m_rs1_io_desc __initdata = {
@ -470,7 +467,6 @@ static void __init v2m_dt_init(void)
{
l2x0_of_init(0x00400000, 0xfe0fffff);
of_platform_populate(NULL, v2m_dt_bus_match, NULL, NULL);
pm_power_off = vexpress_power_off;
}
static const char * const v2m_dt_match[] __initconst = {
@ -487,5 +483,4 @@ DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
.init_irq = irqchip_init,
.init_time = v2m_dt_timer_init,
.init_machine = v2m_dt_init,
.restart = vexpress_restart,
MACHINE_END

View File

@ -39,6 +39,19 @@ config HWMON_DEBUG_CHIP
comment "Native drivers"
config SENSORS_AB8500
tristate "AB8500 thermal monitoring"
depends on AB8500_GPADC && AB8500_BM
default n
help
If you say yes here you get support for the thermal sensor part
of the AB8500 chip. The driver includes thermal management for
AB8500 die and two GPADC channels. The GPADC channel are preferably
used to access sensors outside the AB8500 chip.
This driver can also be built as a module. If so, the module
will be called abx500-temp.
config SENSORS_ABITUGURU
tristate "Abit uGuru (rev 1 & 2)"
depends on X86 && DMI

View File

@ -19,6 +19,7 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
obj-$(CONFIG_SENSORS_AD7314) += ad7314.o

206
drivers/hwmon/ab8500.c Normal file
View File

@ -0,0 +1,206 @@
/*
* Copyright (C) ST-Ericsson 2010 - 2013
* Author: Martin Persson <martin.persson@stericsson.com>
* Hongbo Zhang <hongbo.zhang@linaro.org>
* License Terms: GNU General Public License v2
*
* When the AB8500 thermal warning temperature is reached (threshold cannot
* be changed by SW), an interrupt is set, and if no further action is taken
* within a certain time frame, pm_power off will be called.
*
* When AB8500 thermal shutdown temperature is reached a hardware shutdown of
* the AB8500 will occur.
*/
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power/ab8500.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "abx500.h"
#define DEFAULT_POWER_OFF_DELAY (HZ * 10)
#define THERMAL_VCC 1800
#define PULL_UP_RESISTOR 47000
/* Number of monitored sensors should not greater than NUM_SENSORS */
#define NUM_MONITORED_SENSORS 4
struct ab8500_gpadc_cfg {
const struct abx500_res_to_temp *temp_tbl;
int tbl_sz;
int vcc;
int r_up;
};
struct ab8500_temp {
struct ab8500_gpadc *gpadc;
struct ab8500_btemp *btemp;
struct delayed_work power_off_work;
struct ab8500_gpadc_cfg cfg;
struct abx500_temp *abx500_data;
};
/*
* The hardware connection is like this:
* VCC----[ R_up ]-----[ NTC ]----GND
* where R_up is pull-up resistance, and GPADC measures voltage on NTC.
* and res_to_temp table is strictly sorted by falling resistance values.
*/
static int ab8500_voltage_to_temp(struct ab8500_gpadc_cfg *cfg,
int v_ntc, int *temp)
{
int r_ntc, i = 0, tbl_sz = cfg->tbl_sz;
const struct abx500_res_to_temp *tbl = cfg->temp_tbl;
if (cfg->vcc < 0 || v_ntc >= cfg->vcc)
return -EINVAL;
r_ntc = v_ntc * cfg->r_up / (cfg->vcc - v_ntc);
if (r_ntc > tbl[0].resist || r_ntc < tbl[tbl_sz - 1].resist)
return -EINVAL;
while (!(r_ntc <= tbl[i].resist && r_ntc > tbl[i + 1].resist) &&
i < tbl_sz - 2)
i++;
/* return milli-Celsius */
*temp = tbl[i].temp * 1000 + ((tbl[i + 1].temp - tbl[i].temp) * 1000 *
(r_ntc - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
return 0;
}
static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp)
{
int voltage, ret;
struct ab8500_temp *ab8500_data = data->plat_data;
if (sensor == BAT_CTRL) {
*temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp);
} else if (sensor == BTEMP_BALL) {
*temp = ab8500_btemp_get_temp(ab8500_data->btemp);
} else {
voltage = ab8500_gpadc_convert(ab8500_data->gpadc, sensor);
if (voltage < 0)
return voltage;
ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp);
if (ret < 0)
return ret;
}
return 0;
}
static void ab8500_thermal_power_off(struct work_struct *work)
{
struct ab8500_temp *ab8500_data = container_of(work,
struct ab8500_temp, power_off_work.work);
struct abx500_temp *abx500_data = ab8500_data->abx500_data;
dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n");
pm_power_off();
}
static ssize_t ab8500_show_name(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return sprintf(buf, "ab8500\n");
}
static ssize_t ab8500_show_label(struct device *dev,
struct device_attribute *devattr, char *buf)
{
char *label;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int index = attr->index;
switch (index) {
case 1:
label = "ext_adc1";
break;
case 2:
label = "ext_adc2";
break;
case 3:
label = "bat_temp";
break;
case 4:
label = "bat_ctrl";
break;
default:
return -EINVAL;
}
return sprintf(buf, "%s\n", label);
}
static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data)
{
struct ab8500_temp *ab8500_data = data->plat_data;
dev_warn(&data->pdev->dev, "Power off in %d s\n",
DEFAULT_POWER_OFF_DELAY / HZ);
schedule_delayed_work(&ab8500_data->power_off_work,
DEFAULT_POWER_OFF_DELAY);
return 0;
}
int abx500_hwmon_init(struct abx500_temp *data)
{
struct ab8500_temp *ab8500_data;
ab8500_data = devm_kzalloc(&data->pdev->dev, sizeof(*ab8500_data),
GFP_KERNEL);
if (!ab8500_data)
return -ENOMEM;
ab8500_data->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
if (IS_ERR(ab8500_data->gpadc))
return PTR_ERR(ab8500_data->gpadc);
ab8500_data->btemp = ab8500_btemp_get();
if (IS_ERR(ab8500_data->btemp))
return PTR_ERR(ab8500_data->btemp);
INIT_DELAYED_WORK(&ab8500_data->power_off_work,
ab8500_thermal_power_off);
ab8500_data->cfg.vcc = THERMAL_VCC;
ab8500_data->cfg.r_up = PULL_UP_RESISTOR;
ab8500_data->cfg.temp_tbl = ab8500_temp_tbl_a_thermistor;
ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size;
data->plat_data = ab8500_data;
/*
* ADC_AUX1 and ADC_AUX2, connected to external NTC
* BTEMP_BALL and BAT_CTRL, fixed usage
*/
data->gpadc_addr[0] = ADC_AUX1;
data->gpadc_addr[1] = ADC_AUX2;
data->gpadc_addr[2] = BTEMP_BALL;
data->gpadc_addr[3] = BAT_CTRL;
data->monitored_sensors = NUM_MONITORED_SENSORS;
data->ops.read_sensor = ab8500_read_sensor;
data->ops.irq_handler = ab8500_temp_irq_handler;
data->ops.show_name = ab8500_show_name;
data->ops.show_label = ab8500_show_label;
data->ops.is_visible = NULL;
return 0;
}
EXPORT_SYMBOL(abx500_hwmon_init);
MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@linaro.org>");
MODULE_DESCRIPTION("AB8500 temperature driver");
MODULE_LICENSE("GPL");

491
drivers/hwmon/abx500.c Normal file
View File

@ -0,0 +1,491 @@
/*
* Copyright (C) ST-Ericsson 2010 - 2013
* Author: Martin Persson <martin.persson@stericsson.com>
* Hongbo Zhang <hongbo.zhang@linaro.org>
* License Terms: GNU General Public License v2
*
* ABX500 does not provide auto ADC, so to monitor the required temperatures,
* a periodic work is used. It is more important to not wake up the CPU than
* to perform this job, hence the use of a deferred delay.
*
* A deferred delay for thermal monitor is considered safe because:
* If the chip gets too hot during a sleep state it's most likely due to
* external factors, such as the surrounding temperature. I.e. no SW decisions
* will make any difference.
*/
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/workqueue.h>
#include "abx500.h"
#define DEFAULT_MONITOR_DELAY HZ
#define DEFAULT_MAX_TEMP 130
static inline void schedule_monitor(struct abx500_temp *data)
{
data->work_active = true;
schedule_delayed_work(&data->work, DEFAULT_MONITOR_DELAY);
}
static void threshold_updated(struct abx500_temp *data)
{
int i;
for (i = 0; i < data->monitored_sensors; i++)
if (data->max[i] != 0 || data->min[i] != 0) {
schedule_monitor(data);
return;
}
dev_dbg(&data->pdev->dev, "No active thresholds.\n");
cancel_delayed_work_sync(&data->work);
data->work_active = false;
}
static void gpadc_monitor(struct work_struct *work)
{
int temp, i, ret;
char alarm_node[30];
bool updated_min_alarm, updated_max_alarm;
struct abx500_temp *data;
data = container_of(work, struct abx500_temp, work.work);
mutex_lock(&data->lock);
for (i = 0; i < data->monitored_sensors; i++) {
/* Thresholds are considered inactive if set to 0 */
if (data->max[i] == 0 && data->min[i] == 0)
continue;
if (data->max[i] < data->min[i])
continue;
ret = data->ops.read_sensor(data, data->gpadc_addr[i], &temp);
if (ret < 0) {
dev_err(&data->pdev->dev, "GPADC read failed\n");
continue;
}
updated_min_alarm = false;
updated_max_alarm = false;
if (data->min[i] != 0) {
if (temp < data->min[i]) {
if (data->min_alarm[i] == false) {
data->min_alarm[i] = true;
updated_min_alarm = true;
}
} else {
if (data->min_alarm[i] == true) {
data->min_alarm[i] = false;
updated_min_alarm = true;
}
}
}
if (data->max[i] != 0) {
if (temp > data->max[i]) {
if (data->max_alarm[i] == false) {
data->max_alarm[i] = true;
updated_max_alarm = true;
}
} else if (temp < data->max[i] - data->max_hyst[i]) {
if (data->max_alarm[i] == true) {
data->max_alarm[i] = false;
updated_max_alarm = true;
}
}
}
if (updated_min_alarm) {
ret = sprintf(alarm_node, "temp%d_min_alarm", i + 1);
sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
}
if (updated_max_alarm) {
ret = sprintf(alarm_node, "temp%d_max_alarm", i + 1);
sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
}
}
schedule_monitor(data);
mutex_unlock(&data->lock);
}
/* HWMON sysfs interfaces */
static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct abx500_temp *data = dev_get_drvdata(dev);
/* Show chip name */
return data->ops.show_name(dev, devattr, buf);
}
static ssize_t show_label(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct abx500_temp *data = dev_get_drvdata(dev);
/* Show each sensor label */
return data->ops.show_label(dev, devattr, buf);
}
static ssize_t show_input(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int ret, temp;
struct abx500_temp *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
u8 gpadc_addr = data->gpadc_addr[attr->index];
ret = data->ops.read_sensor(data, gpadc_addr, &temp);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", temp);
}
/* Set functions (RW nodes) */
static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
unsigned long val;
struct abx500_temp *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int res = kstrtol(buf, 10, &val);
if (res < 0)
return res;
val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
mutex_lock(&data->lock);
data->min[attr->index] = val;
threshold_updated(data);
mutex_unlock(&data->lock);
return count;
}
static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
unsigned long val;
struct abx500_temp *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int res = kstrtol(buf, 10, &val);
if (res < 0)
return res;
val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
mutex_lock(&data->lock);
data->max[attr->index] = val;
threshold_updated(data);
mutex_unlock(&data->lock);
return count;
}
static ssize_t set_max_hyst(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
unsigned long val;
struct abx500_temp *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int res = kstrtoul(buf, 10, &val);
if (res < 0)
return res;
val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
mutex_lock(&data->lock);
data->max_hyst[attr->index] = val;
threshold_updated(data);
mutex_unlock(&data->lock);
return count;
}
/* Show functions (RO nodes) */
static ssize_t show_min(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct abx500_temp *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
return sprintf(buf, "%ld\n", data->min[attr->index]);
}
static ssize_t show_max(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct abx500_temp *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
return sprintf(buf, "%ld\n", data->max[attr->index]);
}
static ssize_t show_max_hyst(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct abx500_temp *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
return sprintf(buf, "%ld\n", data->max_hyst[attr->index]);
}
static ssize_t show_min_alarm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct abx500_temp *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
return sprintf(buf, "%d\n", data->min_alarm[attr->index]);
}
static ssize_t show_max_alarm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct abx500_temp *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
return sprintf(buf, "%d\n", data->max_alarm[attr->index]);
}
static mode_t abx500_attrs_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct abx500_temp *data = dev_get_drvdata(dev);
if (data->ops.is_visible)
return data->ops.is_visible(attr, n);
return attr->mode;
}
/* Chip name, required by hwmon */
static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
/* GPADC - SENSOR1 */
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 0);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 0);
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
show_max_hyst, set_max_hyst, 0);
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 0);
/* GPADC - SENSOR2 */
static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IWUSR | S_IRUGO,
show_max_hyst, set_max_hyst, 1);
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
/* GPADC - SENSOR3 */
static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_label, NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, 2);
static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, 2);
static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IWUSR | S_IRUGO,
show_max_hyst, set_max_hyst, 2);
static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_min_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_max_alarm, NULL, 2);
/* GPADC - SENSOR4 */
static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_label, NULL, 3);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_input, NULL, 3);
static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_min, set_min, 3);
static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_max, set_max, 3);
static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IWUSR | S_IRUGO,
show_max_hyst, set_max_hyst, 3);
static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_min_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_max_alarm, NULL, 3);
struct attribute *abx500_temp_attributes[] = {
&sensor_dev_attr_name.dev_attr.attr,
&sensor_dev_attr_temp1_label.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_label.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_min.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_label.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp3_min.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_label.dev_attr.attr,
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp4_min.dev_attr.attr,
&sensor_dev_attr_temp4_max.dev_attr.attr,
&sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
NULL
};
static const struct attribute_group abx500_temp_group = {
.attrs = abx500_temp_attributes,
.is_visible = abx500_attrs_visible,
};
static irqreturn_t abx500_temp_irq_handler(int irq, void *irq_data)
{
struct platform_device *pdev = irq_data;
struct abx500_temp *data = platform_get_drvdata(pdev);
data->ops.irq_handler(irq, data);
return IRQ_HANDLED;
}
static int setup_irqs(struct platform_device *pdev)
{
int ret;
int irq = platform_get_irq_byname(pdev, "ABX500_TEMP_WARM");
if (irq < 0) {
dev_err(&pdev->dev, "Get irq by name failed\n");
return irq;
}
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
abx500_temp_irq_handler, IRQF_NO_SUSPEND, "abx500-temp", pdev);
if (ret < 0)
dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
return ret;
}
static int abx500_temp_probe(struct platform_device *pdev)
{
struct abx500_temp *data;
int err;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->pdev = pdev;
mutex_init(&data->lock);
/* Chip specific initialization */
err = abx500_hwmon_init(data);
if (err < 0 || !data->ops.read_sensor || !data->ops.show_name ||
!data->ops.show_label)
return err;
INIT_DEFERRABLE_WORK(&data->work, gpadc_monitor);
platform_set_drvdata(pdev, data);
err = sysfs_create_group(&pdev->dev.kobj, &abx500_temp_group);
if (err < 0) {
dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
return err;
}
data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
goto exit_sysfs_group;
}
if (data->ops.irq_handler) {
err = setup_irqs(pdev);
if (err < 0)
goto exit_hwmon_reg;
}
return 0;
exit_hwmon_reg:
hwmon_device_unregister(data->hwmon_dev);
exit_sysfs_group:
sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
return err;
}
static int abx500_temp_remove(struct platform_device *pdev)
{
struct abx500_temp *data = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&data->work);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
return 0;
}
static int abx500_temp_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct abx500_temp *data = platform_get_drvdata(pdev);
if (data->work_active)
cancel_delayed_work_sync(&data->work);
return 0;
}
static int abx500_temp_resume(struct platform_device *pdev)
{
struct abx500_temp *data = platform_get_drvdata(pdev);
if (data->work_active)
schedule_monitor(data);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id abx500_temp_match[] = {
{ .compatible = "stericsson,abx500-temp" },
{},
};
#endif
static struct platform_driver abx500_temp_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "abx500-temp",
.of_match_table = of_match_ptr(abx500_temp_match),
},
.suspend = abx500_temp_suspend,
.resume = abx500_temp_resume,
.probe = abx500_temp_probe,
.remove = abx500_temp_remove,
};
module_platform_driver(abx500_temp_driver);
MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>");
MODULE_DESCRIPTION("ABX500 temperature driver");
MODULE_LICENSE("GPL");

69
drivers/hwmon/abx500.h Normal file
View File

@ -0,0 +1,69 @@
/*
* Copyright (C) ST-Ericsson 2010 - 2013
* License terms: GNU General Public License v2
* Author: Martin Persson <martin.persson@stericsson.com>
* Hongbo Zhang <hongbo.zhang@linaro.com>
*/
#ifndef _ABX500_H
#define _ABX500_H
#define NUM_SENSORS 5
struct abx500_temp;
/*
* struct abx500_temp_ops - abx500 chip specific ops
* @read_sensor: reads gpadc output
* @irq_handler: irq handler
* @show_name: hwmon device name
* @show_label: hwmon attribute label
* @is_visible: is attribute visible
*/
struct abx500_temp_ops {
int (*read_sensor)(struct abx500_temp *, u8, int *);
int (*irq_handler)(int, struct abx500_temp *);
ssize_t (*show_name)(struct device *,
struct device_attribute *, char *);
ssize_t (*show_label) (struct device *,
struct device_attribute *, char *);
int (*is_visible)(struct attribute *, int);
};
/*
* struct abx500_temp - representation of temp mon device
* @pdev: platform device
* @hwmon_dev: hwmon device
* @ops: abx500 chip specific ops
* @gpadc_addr: gpadc channel address
* @min: sensor temperature min value
* @max: sensor temperature max value
* @max_hyst: sensor temperature hysteresis value for max limit
* @min_alarm: sensor temperature min alarm
* @max_alarm: sensor temperature max alarm
* @work: delayed work scheduled to monitor temperature periodically
* @work_active: True if work is active
* @lock: mutex
* @monitored_sensors: number of monitored sensors
* @plat_data: private usage, usually points to platform specific data
*/
struct abx500_temp {
struct platform_device *pdev;
struct device *hwmon_dev;
struct abx500_temp_ops ops;
u8 gpadc_addr[NUM_SENSORS];
unsigned long min[NUM_SENSORS];
unsigned long max[NUM_SENSORS];
unsigned long max_hyst[NUM_SENSORS];
bool min_alarm[NUM_SENSORS];
bool max_alarm[NUM_SENSORS];
struct delayed_work work;
bool work_active;
struct mutex lock;
int monitored_sensors;
void *plat_data;
};
int abx500_hwmon_init(struct abx500_temp *data);
#endif /* _ABX500_H */

View File

@ -95,6 +95,7 @@
#define AB8500_IT_MASK22_REG 0x55
#define AB8500_IT_MASK23_REG 0x56
#define AB8500_IT_MASK24_REG 0x57
#define AB8500_IT_MASK25_REG 0x58
/*
* latch hierarchy registers
@ -102,15 +103,25 @@
#define AB8500_IT_LATCHHIER1_REG 0x60
#define AB8500_IT_LATCHHIER2_REG 0x61
#define AB8500_IT_LATCHHIER3_REG 0x62
#define AB8540_IT_LATCHHIER4_REG 0x63
#define AB8500_IT_LATCHHIER_NUM 3
#define AB8540_IT_LATCHHIER_NUM 4
#define AB8500_REV_REG 0x80
#define AB8500_IC_NAME_REG 0x82
#define AB8500_SWITCH_OFF_STATUS 0x00
#define AB8500_TURN_ON_STATUS 0x00
#define AB8505_TURN_ON_STATUS_2 0x04
#define AB8500_CH_USBCH_STAT1_REG 0x02
#define VBUS_DET_DBNC100 0x02
#define VBUS_DET_DBNC1 0x01
static DEFINE_SPINLOCK(on_stat_lock);
static u8 turn_on_stat_mask = 0xFF;
static u8 turn_on_stat_set;
static bool no_bm; /* No battery management */
module_param(no_bm, bool, S_IRUGO);
@ -130,9 +141,15 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21,
};
/* AB9540 support */
/* AB9540 / AB8505 support */
static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = {
0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24,
0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23
};
/* AB8540 support */
static const int ab8540_irq_regoffset[AB8540_NUM_IRQ_REGS] = {
0, 1, 2, 3, 4, -1, -1, -1, -1, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23,
25, 26, 27, 28, 29, 30, 31,
};
static const char ab8500_version_str[][7] = {
@ -352,6 +369,9 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
is_ab8500_1p1_or_earlier(ab8500))
continue;
if (ab8500->irq_reg_offset[i] < 0)
continue;
ab8500->oldmask[i] = new;
reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];
@ -423,6 +443,18 @@ static struct irq_chip ab8500_irq_chip = {
.irq_set_type = ab8500_irq_set_type,
};
static void update_latch_offset(u8 *offset, int i)
{
/* Fix inconsistent ITFromLatch25 bit mapping... */
if (unlikely(*offset == 17))
*offset = 24;
/* Fix inconsistent ab8540 bit mapping... */
if (unlikely(*offset == 16))
*offset = 25;
if ((i==3) && (*offset >= 24))
*offset += 2;
}
static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
int latch_offset, u8 latch_val)
{
@ -474,9 +506,7 @@ static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500,
latch_bit = __ffs(hier_val);
latch_offset = (hier_offset << 3) + latch_bit;
/* Fix inconsistent ITFromLatch25 bit mapping... */
if (unlikely(latch_offset == 17))
latch_offset = 24;
update_latch_offset(&latch_offset, hier_offset);
status = get_register_interruptible(ab8500,
AB8500_INTERRUPT,
@ -504,7 +534,7 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
dev_vdbg(ab8500->dev, "interrupt\n");
/* Hierarchical interrupt version */
for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) {
for (i = 0; i < (ab8500->it_latchhier_num); i++) {
int status;
u8 hier_val;
@ -520,63 +550,6 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
return IRQ_HANDLED;
}
/**
* ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
*
* @ab8500: ab8500_irq controller to operate on.
* @irq: index of the interrupt requested in the chip IRQs
*
* Useful for drivers to request their own IRQs.
*/
static int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq)
{
if (!ab8500)
return -EINVAL;
return irq_create_mapping(ab8500->domain, irq);
}
static irqreturn_t ab8500_irq(int irq, void *dev)
{
struct ab8500 *ab8500 = dev;
int i;
dev_vdbg(ab8500->dev, "interrupt\n");
atomic_inc(&ab8500->transfer_ongoing);
for (i = 0; i < ab8500->mask_size; i++) {
int regoffset = ab8500->irq_reg_offset[i];
int status;
u8 value;
/*
* Interrupt register 12 doesn't exist prior to AB8500 version
* 2.0
*/
if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500))
continue;
status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
AB8500_IT_LATCH1_REG + regoffset, &value);
if (status < 0 || value == 0)
continue;
do {
int bit = __ffs(value);
int line = i * 8 + bit;
int virq = ab8500_irq_get_virq(ab8500, line);
handle_nested_irq(virq);
ab8500_debug_register_interrupt(line);
value &= ~(1 << bit);
} while (value);
}
atomic_dec(&ab8500->transfer_ongoing);
return IRQ_HANDLED;
}
static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq)
{
@ -607,7 +580,9 @@ static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
{
int num_irqs;
if (is_ab9540(ab8500))
if (is_ab8540(ab8500))
num_irqs = AB8540_NR_IRQS;
else if (is_ab9540(ab8500))
num_irqs = AB9540_NR_IRQS;
else if (is_ab8505(ab8500))
num_irqs = AB8505_NR_IRQS;
@ -650,6 +625,15 @@ static struct resource ab8500_gpadc_resources[] = {
},
};
static struct resource ab8505_gpadc_resources[] = {
{
.name = "SW_CONV_END",
.start = AB8500_INT_GP_SW_ADC_CONV_END,
.end = AB8500_INT_GP_SW_ADC_CONV_END,
.flags = IORESOURCE_IRQ,
},
};
static struct resource ab8500_rtc_resources[] = {
{
.name = "60S",
@ -973,6 +957,30 @@ static struct resource ab8505_iddet_resources[] = {
.end = AB8505_INT_KEYSTUCK,
.flags = IORESOURCE_IRQ,
},
{
.name = "VBUS_DET_R",
.start = AB8500_INT_VBUS_DET_R,
.end = AB8500_INT_VBUS_DET_R,
.flags = IORESOURCE_IRQ,
},
{
.name = "VBUS_DET_F",
.start = AB8500_INT_VBUS_DET_F,
.end = AB8500_INT_VBUS_DET_F,
.flags = IORESOURCE_IRQ,
},
{
.name = "ID_DET_PLUGR",
.start = AB8500_INT_ID_DET_PLUGR,
.end = AB8500_INT_ID_DET_PLUGR,
.flags = IORESOURCE_IRQ,
},
{
.name = "ID_DET_PLUGF",
.start = AB8500_INT_ID_DET_PLUGF,
.end = AB8500_INT_ID_DET_PLUGF,
.flags = IORESOURCE_IRQ,
},
};
static struct resource ab8500_temp_resources[] = {
@ -984,82 +992,6 @@ static struct resource ab8500_temp_resources[] = {
},
};
static struct mfd_cell abx500_common_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
.of_compatible = "stericsson,ab8500-debug",
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
.resources = ab8500_debug_resources,
},
#endif
{
.name = "ab8500-sysctrl",
.of_compatible = "stericsson,ab8500-sysctrl",
},
{
.name = "ab8500-regulator",
.of_compatible = "stericsson,ab8500-regulator",
},
{
.name = "abx500-clk",
.of_compatible = "stericsson,abx500-clk",
},
{
.name = "ab8500-gpadc",
.of_compatible = "stericsson,ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
.resources = ab8500_gpadc_resources,
},
{
.name = "ab8500-rtc",
.of_compatible = "stericsson,ab8500-rtc",
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
{
.name = "ab8500-acc-det",
.of_compatible = "stericsson,ab8500-acc-det",
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
.resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
.of_compatible = "stericsson,ab8500-poweron-key",
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
.resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
.of_compatible = "stericsson,ab8500-pwm",
.id = 1,
},
{
.name = "ab8500-pwm",
.of_compatible = "stericsson,ab8500-pwm",
.id = 2,
},
{
.name = "ab8500-pwm",
.of_compatible = "stericsson,ab8500-pwm",
.id = 3,
},
{
.name = "ab8500-leds",
.of_compatible = "stericsson,ab8500-leds",
},
{
.name = "ab8500-denc",
.of_compatible = "stericsson,ab8500-denc",
},
{
.name = "abx500-temp",
.of_compatible = "stericsson,abx500-temp",
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
.resources = ab8500_temp_resources,
},
};
static struct mfd_cell ab8500_bm_devs[] = {
{
.name = "ab8500-charger",
@ -1096,23 +1028,144 @@ static struct mfd_cell ab8500_bm_devs[] = {
};
static struct mfd_cell ab8500_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "pinctrl-ab8500",
.name = "ab8500-debug",
.of_compatible = "stericsson,ab8500-debug",
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
.resources = ab8500_debug_resources,
},
#endif
{
.name = "ab8500-sysctrl",
.of_compatible = "stericsson,ab8500-sysctrl",
},
{
.name = "ab8500-regulator",
.of_compatible = "stericsson,ab8500-regulator",
},
{
.name = "abx500-clk",
.of_compatible = "stericsson,abx500-clk",
},
{
.name = "ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
.resources = ab8500_gpadc_resources,
},
{
.name = "ab8500-rtc",
.of_compatible = "stericsson,ab8500-rtc",
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
{
.name = "ab8500-acc-det",
.of_compatible = "stericsson,ab8500-acc-det",
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
.resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
.of_compatible = "stericsson,ab8500-poweron-key",
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
.resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
.of_compatible = "stericsson,ab8500-pwm",
.id = 1,
},
{
.name = "ab8500-pwm",
.of_compatible = "stericsson,ab8500-pwm",
.id = 2,
},
{
.name = "ab8500-pwm",
.of_compatible = "stericsson,ab8500-pwm",
.id = 3,
},
{
.name = "ab8500-leds",
.of_compatible = "stericsson,ab8500-leds",
},
{
.name = "ab8500-denc",
.of_compatible = "stericsson,ab8500-denc",
},
{
.name = "ab8500-gpio",
.of_compatible = "stericsson,ab8500-gpio",
},
{
.name = "abx500-temp",
.of_compatible = "stericsson,abx500-temp",
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
.resources = ab8500_temp_resources,
},
{
.name = "ab8500-usb",
.of_compatible = "stericsson,ab8500-usb",
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
.resources = ab8500_usb_resources,
},
{
.name = "ab8500-codec",
.of_compatible = "stericsson,ab8500-codec",
},
};
static struct mfd_cell ab9540_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
.resources = ab8500_debug_resources,
},
#endif
{
.name = "ab8500-sysctrl",
},
{
.name = "ab8500-regulator",
},
{
.name = "abx500-clk",
.of_compatible = "stericsson,abx500-clk",
},
{
.name = "ab8500-gpadc",
.of_compatible = "stericsson,ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
.resources = ab8500_gpadc_resources,
},
{
.name = "ab8500-rtc",
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
{
.name = "ab8500-acc-det",
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
.resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
.resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
.id = 1,
},
{
.name = "ab8500-leds",
},
{
.name = "abx500-temp",
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
.resources = ab8500_temp_resources,
},
{
.name = "pinctrl-ab9540",
.of_compatible = "stericsson,ab9540-gpio",
@ -1125,10 +1178,138 @@ static struct mfd_cell ab9540_devs[] = {
{
.name = "ab9540-codec",
},
{
.name = "ab-iddet",
.num_resources = ARRAY_SIZE(ab8505_iddet_resources),
.resources = ab8505_iddet_resources,
},
};
/* Device list common to ab9540 and ab8505 */
static struct mfd_cell ab9540_ab8505_devs[] = {
/* Device list for ab8505 */
static struct mfd_cell ab8505_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
.resources = ab8500_debug_resources,
},
#endif
{
.name = "ab8500-sysctrl",
},
{
.name = "ab8500-regulator",
},
{
.name = "abx500-clk",
.of_compatible = "stericsson,abx500-clk",
},
{
.name = "ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
.resources = ab8505_gpadc_resources,
},
{
.name = "ab8500-rtc",
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
{
.name = "ab8500-acc-det",
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
.resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
.resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
.id = 1,
},
{
.name = "ab8500-leds",
},
{
.name = "ab8500-gpio",
},
{
.name = "ab8500-usb",
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
.resources = ab8500_usb_resources,
},
{
.name = "ab8500-codec",
},
{
.name = "ab-iddet",
.num_resources = ARRAY_SIZE(ab8505_iddet_resources),
.resources = ab8505_iddet_resources,
},
};
static struct mfd_cell ab8540_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
.resources = ab8500_debug_resources,
},
#endif
{
.name = "ab8500-sysctrl",
},
{
.name = "ab8500-regulator",
},
{
.name = "abx500-clk",
.of_compatible = "stericsson,abx500-clk",
},
{
.name = "ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
.resources = ab8505_gpadc_resources,
},
{
.name = "ab8500-rtc",
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
{
.name = "ab8500-acc-det",
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
.resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
.resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
.id = 1,
},
{
.name = "ab8500-leds",
},
{
.name = "abx500-temp",
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
.resources = ab8500_temp_resources,
},
{
.name = "ab8500-gpio",
},
{
.name = "ab8540-usb",
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
.resources = ab8500_usb_resources,
},
{
.name = "ab8540-codec",
},
{
.name = "ab-iddet",
.num_resources = ARRAY_SIZE(ab8505_iddet_resources),
@ -1142,6 +1323,7 @@ static ssize_t show_chip_id(struct device *dev,
struct ab8500 *ab8500;
ab8500 = dev_get_drvdata(dev);
return sprintf(buf, "%#x\n", ab8500 ? ab8500->chip_id : -EINVAL);
}
@ -1171,6 +1353,15 @@ static ssize_t show_switch_off_status(struct device *dev,
return sprintf(buf, "%#x\n", value);
}
/* use mask and set to override the register turn_on_stat value */
void ab8500_override_turn_on_stat(u8 mask, u8 set)
{
spin_lock(&on_stat_lock);
turn_on_stat_mask = mask;
turn_on_stat_set = set;
spin_unlock(&on_stat_lock);
}
/*
* ab8500 has turned on due to (TURN_ON_STATUS):
* 0x01 PORnVbat
@ -1194,9 +1385,38 @@ static ssize_t show_turn_on_status(struct device *dev,
AB8500_TURN_ON_STATUS, &value);
if (ret < 0)
return ret;
/*
* In L9540, turn_on_status register is not updated correctly if
* the device is rebooted with AC/USB charger connected. Due to
* this, the device boots android instead of entering into charge
* only mode. Read the AC/USB status register to detect the charger
* presence and update the turn on status manually.
*/
if (is_ab9540(ab8500)) {
spin_lock(&on_stat_lock);
value = (value & turn_on_stat_mask) | turn_on_stat_set;
spin_unlock(&on_stat_lock);
}
return sprintf(buf, "%#x\n", value);
}
static ssize_t show_turn_on_status_2(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
u8 value;
struct ab8500 *ab8500;
ab8500 = dev_get_drvdata(dev);
ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
AB8505_TURN_ON_STATUS_2, &value);
if (ret < 0)
return ret;
return sprintf(buf, "%#x\n", (value & 0x1));
}
static ssize_t show_ab9540_dbbrstn(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -1253,6 +1473,7 @@ exit:
static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
static DEVICE_ATTR(turn_on_status_2, S_IRUGO, show_turn_on_status_2, NULL);
static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR,
show_ab9540_dbbrstn, store_ab9540_dbbrstn);
@ -1263,6 +1484,11 @@ static struct attribute *ab8500_sysfs_entries[] = {
NULL,
};
static struct attribute *ab8505_sysfs_entries[] = {
&dev_attr_turn_on_status_2.attr,
NULL,
};
static struct attribute *ab9540_sysfs_entries[] = {
&dev_attr_chip_id.attr,
&dev_attr_switch_off_status.attr,
@ -1275,6 +1501,10 @@ static struct attribute_group ab8500_attr_group = {
.attrs = ab8500_sysfs_entries,
};
static struct attribute_group ab8505_attr_group = {
.attrs = ab8505_sysfs_entries,
};
static struct attribute_group ab9540_attr_group = {
.attrs = ab9540_sysfs_entries,
};
@ -1290,6 +1520,15 @@ static int ab8500_probe(struct platform_device *pdev)
"Battery level lower than power on reset threshold",
"Power on key 1 pressed longer than 10 seconds",
"DB8500 thermal shutdown"};
static char *turn_on_status[] = {
"Battery rising (Vbat)",
"Power On Key 1 dbF",
"Power On Key 2 dbF",
"RTC Alarm",
"Main Charger Detect",
"Vbus Detect (USB)",
"USB ID Detect",
"UART Factory Mode Detect"};
struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
const struct platform_device_id *platid = platform_get_device_id(pdev);
enum ab8500_version version = AB8500_VERSION_UNDEFINED;
@ -1351,13 +1590,20 @@ static int ab8500_probe(struct platform_device *pdev)
ab8500->chip_id >> 4,
ab8500->chip_id & 0x0F);
/* Configure AB8500 or AB9540 IRQ */
if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
/* Configure AB8540 */
if (is_ab8540(ab8500)) {
ab8500->mask_size = AB8540_NUM_IRQ_REGS;
ab8500->irq_reg_offset = ab8540_irq_regoffset;
ab8500->it_latchhier_num = AB8540_IT_LATCHHIER_NUM;
}/* Configure AB8500 or AB9540 IRQ */
else if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
ab8500->mask_size = AB9540_NUM_IRQ_REGS;
ab8500->irq_reg_offset = ab9540_irq_regoffset;
ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM;
} else {
ab8500->mask_size = AB8500_NUM_IRQ_REGS;
ab8500->irq_reg_offset = ab8500_irq_regoffset;
ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM;
}
ab8500->mask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL);
if (!ab8500->mask)
@ -1396,10 +1642,36 @@ static int ab8500_probe(struct platform_device *pdev)
} else {
printk(KERN_CONT " None\n");
}
ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
AB8500_TURN_ON_STATUS, &value);
if (ret < 0)
return ret;
dev_info(ab8500->dev, "turn on reason(s) (%#x): ", value);
if (value) {
for (i = 0; i < ARRAY_SIZE(turn_on_status); i++) {
if (value & 1)
printk("\"%s\" ", turn_on_status[i]);
value = value >> 1;
}
printk("\n");
} else {
printk("None\n");
}
if (plat && plat->init)
plat->init(ab8500);
if (is_ab9540(ab8500)) {
ret = get_register_interruptible(ab8500, AB8500_CHARGER,
AB8500_CH_USBCH_STAT1_REG, &value);
if (ret < 0)
return ret;
if ((value & VBUS_DET_DBNC1) && (value & VBUS_DET_DBNC100))
ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON,
AB8500_VBUS_DET);
}
/* Clear and mask all interrupts */
for (i = 0; i < ab8500->mask_size; i++) {
/*
@ -1410,6 +1682,9 @@ static int ab8500_probe(struct platform_device *pdev)
is_ab8500_1p1_or_earlier(ab8500))
continue;
if (ab8500->irq_reg_offset[i] < 0)
continue;
get_register_interruptible(ab8500, AB8500_INTERRUPT,
AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i],
&value);
@ -1428,26 +1703,10 @@ static int ab8500_probe(struct platform_device *pdev)
if (ret)
return ret;
/* Activate this feature only in ab9540 */
/* till tests are done on ab8500 1p2 or later*/
if (is_ab9540(ab8500)) {
ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
ab8500_hierarchical_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
"ab8500", ab8500);
}
else {
ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
ab8500_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
"ab8500", ab8500);
if (ret)
return ret;
}
ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
ARRAY_SIZE(abx500_common_devs), NULL,
ab8500->irq_base, ab8500->domain);
ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
ab8500_hierarchical_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
"ab8500", ab8500);
if (ret)
return ret;
@ -1455,6 +1714,14 @@ static int ab8500_probe(struct platform_device *pdev)
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
ARRAY_SIZE(ab9540_devs), NULL,
ab8500->irq_base, ab8500->domain);
else if (is_ab8540(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab8540_devs,
ARRAY_SIZE(ab8540_devs), NULL,
ab8500->irq_base, ab8500->domain);
else if (is_ab8505(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab8505_devs,
ARRAY_SIZE(ab8505_devs), NULL,
ab8500->irq_base, ab8500->domain);
else
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
ARRAY_SIZE(ab8500_devs), NULL,
@ -1462,13 +1729,6 @@ static int ab8500_probe(struct platform_device *pdev)
if (ret)
return ret;
if (is_ab9540(ab8500) || is_ab8505(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
ARRAY_SIZE(ab9540_ab8505_devs), NULL,
ab8500->irq_base, ab8500->domain);
if (ret)
return ret;
if (!no_bm) {
/* Add battery management devices */
ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
@ -1478,12 +1738,19 @@ static int ab8500_probe(struct platform_device *pdev)
dev_err(ab8500->dev, "error adding bm devices\n");
}
if (is_ab9540(ab8500))
if (((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
ab8500->chip_id >= AB8500_CUT2P0) || is_ab8540(ab8500))
ret = sysfs_create_group(&ab8500->dev->kobj,
&ab9540_attr_group);
else
ret = sysfs_create_group(&ab8500->dev->kobj,
&ab8500_attr_group);
if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
ab8500->chip_id >= AB8500_CUT2P0)
ret = sysfs_create_group(&ab8500->dev->kobj,
&ab8505_attr_group);
if (ret)
dev_err(ab8500->dev, "error creating sysfs entries\n");
@ -1494,11 +1761,16 @@ static int ab8500_remove(struct platform_device *pdev)
{
struct ab8500 *ab8500 = platform_get_drvdata(pdev);
if (is_ab9540(ab8500))
if (((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
ab8500->chip_id >= AB8500_CUT2P0) || is_ab8540(ab8500))
sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
else
sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
ab8500->chip_id >= AB8500_CUT2P0)
sysfs_remove_group(&ab8500->dev->kobj, &ab8505_attr_group);
mfd_remove_devices(ab8500->dev);
return 0;

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,13 @@
#define AB8500_GPADC_AUTODATAL_REG 0x07
#define AB8500_GPADC_AUTODATAH_REG 0x08
#define AB8500_GPADC_MUX_CTRL_REG 0x09
#define AB8540_GPADC_MANDATA2L_REG 0x09
#define AB8540_GPADC_MANDATA2H_REG 0x0A
#define AB8540_GPADC_APEAAX_REG 0x10
#define AB8540_GPADC_APEAAT_REG 0x11
#define AB8540_GPADC_APEAAM_REG 0x12
#define AB8540_GPADC_APEAAH_REG 0x13
#define AB8540_GPADC_APEAAL_REG 0x14
/*
* OTP register offsets
@ -49,19 +56,29 @@
#define AB8500_GPADC_CAL_5 0x13
#define AB8500_GPADC_CAL_6 0x14
#define AB8500_GPADC_CAL_7 0x15
/* New calibration for 8540 */
#define AB8540_GPADC_OTP4_REG_7 0x38
#define AB8540_GPADC_OTP4_REG_6 0x39
#define AB8540_GPADC_OTP4_REG_5 0x3A
/* gpadc constants */
#define EN_VINTCORE12 0x04
#define EN_VTVOUT 0x02
#define EN_GPADC 0x01
#define DIS_GPADC 0x00
#define SW_AVG_16 0x60
#define AVG_1 0x00
#define AVG_4 0x20
#define AVG_8 0x40
#define AVG_16 0x60
#define ADC_SW_CONV 0x04
#define EN_ICHAR 0x80
#define BTEMP_PULL_UP 0x08
#define EN_BUF 0x40
#define DIS_ZERO 0x00
#define GPADC_BUSY 0x01
#define EN_FALLING 0x10
#define EN_TRIG_EDGE 0x02
#define EN_VBIAS_XTAL_TEMP 0x02
/* GPADC constants from AB8500 spec, UM0836 */
#define ADC_RESOLUTION 1024
@ -80,8 +97,21 @@
#define ADC_CH_BKBAT_MIN 0
#define ADC_CH_BKBAT_MAX 3200
/* GPADC constants from AB8540 spec */
#define ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat*/
#define ADC_CH_IBAT_MAX 6000
#define ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat*/
#define ADC_CH_IBAT_MAX_V 60
#define IBAT_VDROP_L (-56) /* mV */
#define IBAT_VDROP_H 56
/* This is used to not lose precision when dividing to get gain and offset */
#define CALIB_SCALE 1000
#define CALIB_SCALE 1000
/*
* Number of bits shift used to not lose precision
* when dividing to get ibat gain.
*/
#define CALIB_SHIFT_IBAT 20
/* Time in ms before disabling regulator */
#define GPADC_AUDOSUSPEND_DELAY 1
@ -92,6 +122,7 @@ enum cal_channels {
ADC_INPUT_VMAIN = 0,
ADC_INPUT_BTEMP,
ADC_INPUT_VBAT,
ADC_INPUT_IBAT,
NBR_CAL_INPUTS,
};
@ -102,8 +133,10 @@ enum cal_channels {
* @offset: Offset of the ADC channel
*/
struct adc_cal_data {
u64 gain;
u64 offset;
s64 gain;
s64 offset;
u16 otp_calib_hi;
u16 otp_calib_lo;
};
/**
@ -116,7 +149,10 @@ struct adc_cal_data {
* the completion of gpadc conversion
* @ab8500_gpadc_lock: structure of type mutex
* @regu: pointer to the struct regulator
* @irq: interrupt number that is used by gpadc
* @irq_sw: interrupt number that is used by gpadc for Sw
* conversion
* @irq_hw: interrupt number that is used by gpadc for Hw
* conversion
* @cal_data array of ADC calibration data structs
*/
struct ab8500_gpadc {
@ -126,7 +162,8 @@ struct ab8500_gpadc {
struct completion ab8500_gpadc_complete;
struct mutex ab8500_gpadc_lock;
struct regulator *regu;
int irq;
int irq_sw;
int irq_hw;
struct adc_cal_data cal_data[NBR_CAL_INPUTS];
};
@ -171,6 +208,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE;
break;
case XTAL_TEMP:
case BAT_CTRL:
case BTEMP_BALL:
case ACC_DETECT1:
@ -189,6 +227,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
break;
case MAIN_BAT_V:
case VBAT_TRUE_MEAS:
/* For some reason we don't have calibrated data */
if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) {
res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX -
@ -232,6 +271,20 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
ADC_RESOLUTION;
break;
case IBAT_VIRTUAL_CHANNEL:
/* For some reason we don't have calibrated data */
if (!gpadc->cal_data[ADC_INPUT_IBAT].gain) {
res = ADC_CH_IBAT_MIN + (ADC_CH_IBAT_MAX -
ADC_CH_IBAT_MIN) * ad_value /
ADC_RESOLUTION;
break;
}
/* Here we can use the calibrated data */
res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_IBAT].gain +
gpadc->cal_data[ADC_INPUT_IBAT].offset)
>> CALIB_SHIFT_IBAT;
break;
default:
dev_err(gpadc->dev,
"unknown channel, not possible to convert\n");
@ -244,25 +297,35 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
/**
* ab8500_gpadc_convert() - gpadc conversion
* ab8500_gpadc_sw_hw_convert() - gpadc conversion
* @channel: analog channel to be converted to digital data
* @avg_sample: number of ADC sample to average
* @trig_egde: selected ADC trig edge
* @trig_timer: selected ADC trigger delay timer
* @conv_type: selected conversion type (HW or SW conversion)
*
* This function converts the selected analog i/p to digital
* data.
*/
int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
{
int ad_value;
int voltage;
ad_value = ab8500_gpadc_read_raw(gpadc, channel);
if (ad_value < 0) {
dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel);
ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
trig_edge, trig_timer, conv_type);
/* On failure retry a second time */
if (ad_value < 0)
ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
trig_edge, trig_timer, conv_type);
if (ad_value < 0) {
dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n",
channel);
return ad_value;
}
voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
if (voltage < 0)
dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
" %d AD: 0x%x\n", channel, ad_value);
@ -274,21 +337,46 @@ EXPORT_SYMBOL(ab8500_gpadc_convert);
/**
* ab8500_gpadc_read_raw() - gpadc read
* @channel: analog channel to be read
* @avg_sample: number of ADC sample to average
* @trig_edge: selected trig edge
* @trig_timer: selected ADC trigger delay timer
* @conv_type: selected conversion type (HW or SW conversion)
*
* This function obtains the raw ADC value, this then needs
* to be converted by calling ab8500_gpadc_ad_to_voltage()
* This function obtains the raw ADC value for an hardware conversion,
* this then needs to be converted by calling ab8500_gpadc_ad_to_voltage()
*/
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
{
int raw_data;
raw_data = ab8500_gpadc_double_read_raw(gpadc, channel,
avg_sample, trig_edge, trig_timer, conv_type, NULL);
return raw_data;
}
int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type,
int *ibat)
{
int ret;
int looplimit = 0;
u8 val, low_data, high_data;
unsigned long completion_timeout;
u8 val, low_data, high_data, low_data2, high_data2;
u8 val_reg1 = 0;
unsigned int delay_min = 0;
unsigned int delay_max = 0;
u8 data_low_addr, data_high_addr;
if (!gpadc)
return -ENODEV;
mutex_lock(&gpadc->ab8500_gpadc_lock);
/* check if convertion is supported */
if ((gpadc->irq_sw < 0) && (conv_type == ADC_SW))
return -ENOTSUPP;
if ((gpadc->irq_hw < 0) && (conv_type == ADC_HW))
return -ENOTSUPP;
mutex_lock(&gpadc->ab8500_gpadc_lock);
/* Enable VTVout LDO this is required for GPADC */
pm_runtime_get_sync(gpadc->dev);
@ -309,16 +397,34 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
}
/* Enable GPADC */
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC);
if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n");
goto out;
val_reg1 |= EN_GPADC;
/* Select the channel source and set average samples */
switch (avg_sample) {
case SAMPLE_1:
val = channel | AVG_1;
break;
case SAMPLE_4:
val = channel | AVG_4;
break;
case SAMPLE_8:
val = channel | AVG_8;
break;
default:
val = channel | AVG_16;
break;
}
/* Select the channel source and set average samples to 16 */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
if (conv_type == ADC_HW) {
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL3_REG, val);
val_reg1 |= EN_TRIG_EDGE;
if (trig_edge)
val_reg1 |= EN_FALLING;
}
else
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: set avg samples failed\n");
@ -333,71 +439,129 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
switch (channel) {
case MAIN_CHARGER_C:
case USB_CHARGER_C:
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
EN_BUF | EN_ICHAR,
EN_BUF | EN_ICHAR);
val_reg1 |= EN_BUF | EN_ICHAR;
break;
case BTEMP_BALL:
if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
/* Turn on btemp pull-up on ABB 3.0 */
ret = abx500_mask_and_set_register_interruptible(
gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
EN_BUF | BTEMP_PULL_UP,
EN_BUF | BTEMP_PULL_UP);
/*
* Delay might be needed for ABB8500 cut 3.0, if not, remove
* when hardware will be available
*/
usleep_range(1000, 1000);
val_reg1 |= EN_BUF | BTEMP_PULL_UP;
/*
* Delay might be needed for ABB8500 cut 3.0, if not,
* remove when hardware will be availible
*/
delay_min = 1000; /* Delay in micro seconds */
delay_max = 10000; /* large range to optimise sleep mode */
break;
}
/* Intentional fallthrough */
default:
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
val_reg1 |= EN_BUF;
break;
}
/* Write configuration to register */
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, val_reg1);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: select falling edge failed\n");
"gpadc_conversion: set Control register failed\n");
goto out;
}
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: start s/w conversion failed\n");
goto out;
if (delay_min != 0)
usleep_range(delay_min, delay_max);
if (conv_type == ADC_HW) {
/* Set trigger delay timer */
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, trig_timer);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: trig timer failed\n");
goto out;
}
completion_timeout = 2 * HZ;
data_low_addr = AB8500_GPADC_AUTODATAL_REG;
data_high_addr = AB8500_GPADC_AUTODATAH_REG;
} else {
/* Start SW conversion */
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
ADC_SW_CONV, ADC_SW_CONV);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: start s/w conv failed\n");
goto out;
}
completion_timeout = msecs_to_jiffies(CONVERSION_TIME);
data_low_addr = AB8500_GPADC_MANDATAL_REG;
data_high_addr = AB8500_GPADC_MANDATAH_REG;
}
/* wait for completion of conversion */
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
msecs_to_jiffies(CONVERSION_TIME))) {
completion_timeout)) {
dev_err(gpadc->dev,
"timeout: didn't receive GPADC conversion interrupt\n");
"timeout didn't receive GPADC conv interrupt\n");
ret = -EINVAL;
goto out;
}
/* Read the converted RAW data */
ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_MANDATAL_REG, &low_data);
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, data_low_addr, &low_data);
if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n");
goto out;
}
ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_MANDATAH_REG, &high_data);
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, data_high_addr, &high_data);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: read high data failed\n");
dev_err(gpadc->dev, "gpadc_conversion: read high data failed\n");
goto out;
}
/* Check if double convertion is required */
if ((channel == BAT_CTRL_AND_IBAT) ||
(channel == VBAT_MEAS_AND_IBAT) ||
(channel == VBAT_TRUE_MEAS_AND_IBAT) ||
(channel == BAT_TEMP_AND_IBAT)) {
if (conv_type == ADC_HW) {
/* not supported */
ret = -ENOTSUPP;
dev_err(gpadc->dev,
"gpadc_conversion: only SW double conversion supported\n");
goto out;
} else {
/* Read the converted RAW data 2 */
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG,
&low_data2);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: read sw low data 2 failed\n");
goto out;
}
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG,
&high_data2);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: read sw high data 2 failed\n");
goto out;
}
if (ibat != NULL) {
*ibat = (high_data2 << 8) | low_data2;
} else {
dev_warn(gpadc->dev,
"gpadc_conversion: ibat not stored\n");
}
}
}
/* Disable GPADC */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
@ -406,6 +570,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
goto out;
}
/* Disable VTVout LDO this is required for GPADC */
pm_runtime_mark_last_busy(gpadc->dev);
pm_runtime_put_autosuspend(gpadc->dev);
@ -422,9 +587,7 @@ out:
*/
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
pm_runtime_put(gpadc->dev);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev,
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
@ -433,16 +596,16 @@ out:
EXPORT_SYMBOL(ab8500_gpadc_read_raw);
/**
* ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
* ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion
* @irq: irq number
* @data: pointer to the data passed during request irq
*
* This is a interrupt service routine for s/w gpadc conversion completion.
* This is a interrupt service routine for gpadc conversion completion.
* Notifies the gpadc completion is completed and the converted raw value
* can be read from the registers.
* Returns IRQ status(IRQ_HANDLED)
*/
static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc)
static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *_gpadc)
{
struct ab8500_gpadc *gpadc = _gpadc;
@ -461,15 +624,27 @@ static int otp_cal_regs[] = {
AB8500_GPADC_CAL_7,
};
static int otp4_cal_regs[] = {
AB8540_GPADC_OTP4_REG_7,
AB8540_GPADC_OTP4_REG_6,
AB8540_GPADC_OTP4_REG_5,
};
static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
{
int i;
int ret[ARRAY_SIZE(otp_cal_regs)];
u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)];
int ret_otp4[ARRAY_SIZE(otp4_cal_regs)];
u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)];
int vmain_high, vmain_low;
int btemp_high, btemp_low;
int vbat_high, vbat_low;
int ibat_high, ibat_low;
s64 V_gain, V_offset, V2A_gain, V2A_offset;
struct ab8500 *ab8500;
ab8500 = gpadc->parent;
/* First we read all OTP registers and store the error code */
for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
@ -489,7 +664,7 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
* bt_h/l = btemp_high/low
* vb_h/l = vbat_high/low
*
* Data bits:
* Data bits 8500/9540:
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
* |.......|.......|.......|.......|.......|.......|.......|.......
* | | vm_h9 | vm_h8
@ -507,6 +682,35 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
* | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
* |.......|.......|.......|.......|.......|.......|.......|.......
*
* Data bits 8540:
* OTP2
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
* |.......|.......|.......|.......|.......|.......|.......|.......
* |
* |.......|.......|.......|.......|.......|.......|.......|.......
* | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2
* |.......|.......|.......|.......|.......|.......|.......|.......
* | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9
* |.......|.......|.......|.......|.......|.......|.......|.......
* | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1
* |.......|.......|.......|.......|.......|.......|.......|.......
* | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8
* |.......|.......|.......|.......|.......|.......|.......|.......
* | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0
* |.......|.......|.......|.......|.......|.......|.......|.......
* | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
* |.......|.......|.......|.......|.......|.......|.......|.......
*
* Data bits 8540:
* OTP4
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
* |.......|.......|.......|.......|.......|.......|.......|.......
* | | ib_h9 | ib_h8 | ib_h7
* |.......|.......|.......|.......|.......|.......|.......|.......
* | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5
* |.......|.......|.......|.......|.......|.......|.......|.......
* | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 |
*
*
* Ideal output ADC codes corresponding to injected input voltages
* during manufacturing is:
@ -519,38 +723,116 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
* vbat_low: Vin = 2380mV / ADC ideal code = 33
*/
/* Calculate gain and offset for VMAIN if all reads succeeded */
if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
((gpadc_cal[1] & 0x3F) << 2) |
((gpadc_cal[2] & 0xC0) >> 6));
if (is_ab8540(ab8500)) {
/* Calculate gain and offset for VMAIN if all reads succeeded*/
if (!(ret[1] < 0 || ret[2] < 0)) {
vmain_high = (((gpadc_cal[1] & 0xFF) << 2) |
((gpadc_cal[2] & 0xC0) >> 6));
vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
(u16)vmain_high;
gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
(u16)vmain_low;
gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
(19500 - 315) / (vmain_high - vmain_low);
gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 -
(CALIB_SCALE * (19500 - 315) /
(vmain_high - vmain_low)) * vmain_high;
} else {
gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
(19500 - 315) / (vmain_high - vmain_low);
gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
19500 - (CALIB_SCALE * (19500 - 315) /
(vmain_high - vmain_low)) * vmain_high;
} else {
gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
}
/* Read IBAT calibration Data */
for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) {
ret_otp4[i] = abx500_get_register_interruptible(
gpadc->dev, AB8500_OTP_EMUL,
otp4_cal_regs[i], &gpadc_otp4[i]);
if (ret_otp4[i] < 0)
dev_err(gpadc->dev,
"%s: read otp4 reg 0x%02x failed\n",
__func__, otp4_cal_regs[i]);
}
/* Calculate gain and offset for IBAT if all reads succeeded */
if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) {
ibat_high = (((gpadc_otp4[0] & 0x07) << 7) |
((gpadc_otp4[1] & 0xFE) >> 1));
ibat_low = (((gpadc_otp4[1] & 0x01) << 5) |
((gpadc_otp4[2] & 0xF8) >> 3));
gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi =
(u16)ibat_high;
gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo =
(u16)ibat_low;
V_gain = ((IBAT_VDROP_H - IBAT_VDROP_L)
<< CALIB_SHIFT_IBAT) / (ibat_high - ibat_low);
V_offset = (IBAT_VDROP_H << CALIB_SHIFT_IBAT) -
(((IBAT_VDROP_H - IBAT_VDROP_L) <<
CALIB_SHIFT_IBAT) / (ibat_high - ibat_low))
* ibat_high;
/*
* Result obtained is in mV (at a scale factor),
* we need to calculate gain and offset to get mA
*/
V2A_gain = (ADC_CH_IBAT_MAX - ADC_CH_IBAT_MIN)/
(ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
V2A_offset = ((ADC_CH_IBAT_MAX_V * ADC_CH_IBAT_MIN -
ADC_CH_IBAT_MAX * ADC_CH_IBAT_MIN_V)
<< CALIB_SHIFT_IBAT)
/ (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
gpadc->cal_data[ADC_INPUT_IBAT].gain = V_gain * V2A_gain;
gpadc->cal_data[ADC_INPUT_IBAT].offset = V_offset *
V2A_gain + V2A_offset;
} else {
gpadc->cal_data[ADC_INPUT_IBAT].gain = 0;
}
dev_dbg(gpadc->dev, "IBAT gain %llu offset %llu\n",
gpadc->cal_data[ADC_INPUT_IBAT].gain,
gpadc->cal_data[ADC_INPUT_IBAT].offset);
} else {
/* Calculate gain and offset for VMAIN if all reads succeeded */
if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
((gpadc_cal[1] & 0x3F) << 2) |
((gpadc_cal[2] & 0xC0) >> 6));
vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
(u16)vmain_high;
gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
(u16)vmain_low;
gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
(19500 - 315) / (vmain_high - vmain_low);
gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
19500 - (CALIB_SCALE * (19500 - 315) /
(vmain_high - vmain_low)) * vmain_high;
} else {
gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
}
}
/* Calculate gain and offset for BTEMP if all reads succeeded */
if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) {
btemp_high = (((gpadc_cal[2] & 0x01) << 9) |
(gpadc_cal[3] << 1) |
((gpadc_cal[4] & 0x80) >> 7));
(gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7));
btemp_low = ((gpadc_cal[4] & 0x7C) >> 2);
gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi = (u16)btemp_high;
gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo = (u16)btemp_low;
gpadc->cal_data[ADC_INPUT_BTEMP].gain =
CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low);
gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 -
(CALIB_SCALE * (1300 - 21) /
(btemp_high - btemp_low)) * btemp_high;
(CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low))
* btemp_high;
} else {
gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0;
}
@ -560,9 +842,11 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]);
vbat_low = ((gpadc_cal[6] & 0xFC) >> 2);
gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi = (u16)vbat_high;
gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo = (u16)vbat_low;
gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE *
(4700 - 2380) / (vbat_high - vbat_low);
gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 -
(CALIB_SCALE * (4700 - 2380) /
(vbat_high - vbat_low)) * vbat_high;
@ -608,6 +892,31 @@ static int ab8500_gpadc_runtime_idle(struct device *dev)
return 0;
}
static int ab8500_gpadc_suspend(struct device *dev)
{
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
mutex_lock(&gpadc->ab8500_gpadc_lock);
pm_runtime_get_sync(dev);
regulator_disable(gpadc->regu);
return 0;
}
static int ab8500_gpadc_resume(struct device *dev)
{
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
regulator_enable(gpadc->regu);
pm_runtime_mark_last_busy(gpadc->dev);
pm_runtime_put_autosuspend(gpadc->dev);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
return 0;
}
static int ab8500_gpadc_probe(struct platform_device *pdev)
{
int ret = 0;
@ -619,13 +928,13 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
return -ENOMEM;
}
gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
if (gpadc->irq < 0) {
dev_err(&pdev->dev, "failed to get platform irq-%d\n",
gpadc->irq);
ret = gpadc->irq;
goto fail;
}
gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
if (gpadc->irq_sw < 0)
dev_err(gpadc->dev, "failed to get platform sw_conv_end irq\n");
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
if (gpadc->irq_hw < 0)
dev_err(gpadc->dev, "failed to get platform hw_conv_end irq\n");
gpadc->dev = &pdev->dev;
gpadc->parent = dev_get_drvdata(pdev->dev.parent);
@ -634,15 +943,31 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
/* Initialize completion used to notify completion of conversion */
init_completion(&gpadc->ab8500_gpadc_complete);
/* Register interrupt - SwAdcComplete */
ret = request_threaded_irq(gpadc->irq, NULL,
ab8500_bm_gpswadcconvend_handler,
IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED,
"ab8500-gpadc", gpadc);
if (ret < 0) {
dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
gpadc->irq);
goto fail;
/* Register interrupts */
if (gpadc->irq_sw >= 0) {
ret = request_threaded_irq(gpadc->irq_sw, NULL,
ab8500_bm_gpadcconvend_handler,
IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw",
gpadc);
if (ret < 0) {
dev_err(gpadc->dev,
"Failed to register interrupt irq: %d\n",
gpadc->irq_sw);
goto fail;
}
}
if (gpadc->irq_hw >= 0) {
ret = request_threaded_irq(gpadc->irq_hw, NULL,
ab8500_bm_gpadcconvend_handler,
IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw",
gpadc);
if (ret < 0) {
dev_err(gpadc->dev,
"Failed to register interrupt irq: %d\n",
gpadc->irq_hw);
goto fail_irq;
}
}
/* VTVout LDO used to power up ab8500-GPADC */
@ -669,11 +994,13 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
ab8500_gpadc_read_calibration_data(gpadc);
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
dev_dbg(gpadc->dev, "probe success\n");
return 0;
fail_enable:
fail_irq:
free_irq(gpadc->irq, gpadc);
free_irq(gpadc->irq_sw, gpadc);
free_irq(gpadc->irq_hw, gpadc);
fail:
kfree(gpadc);
gpadc = NULL;
@ -687,7 +1014,10 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
/* remove this gpadc entry from the list */
list_del(&gpadc->node);
/* remove interrupt - completion of Sw ADC conversion */
free_irq(gpadc->irq, gpadc);
if (gpadc->irq_sw >= 0)
free_irq(gpadc->irq_sw, gpadc);
if (gpadc->irq_hw >= 0)
free_irq(gpadc->irq_hw, gpadc);
pm_runtime_get_sync(gpadc->dev);
pm_runtime_disable(gpadc->dev);
@ -707,6 +1037,9 @@ static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
ab8500_gpadc_runtime_resume,
ab8500_gpadc_runtime_idle)
SET_SYSTEM_SLEEP_PM_OPS(ab8500_gpadc_suspend,
ab8500_gpadc_resume)
};
static struct platform_driver ab8500_gpadc_driver = {
@ -729,10 +1062,30 @@ static void __exit ab8500_gpadc_exit(void)
platform_driver_unregister(&ab8500_gpadc_driver);
}
/**
* ab8540_gpadc_get_otp() - returns OTP values
*
*/
void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc,
u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h,
u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h)
{
*vmain_l = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo;
*vmain_h = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi;
*btemp_l = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo;
*btemp_h = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi;
*vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo;
*vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi;
*ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo;
*ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi;
return ;
}
subsys_initcall_sync(ab8500_gpadc_init);
module_exit(ab8500_gpadc_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson");
MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson,"
"M'boumba Cedric Madianga");
MODULE_ALIAS("platform:ab8500_gpadc");
MODULE_DESCRIPTION("AB8500 GPADC driver");

View File

@ -15,19 +15,30 @@
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
/* RtcCtrl bits */
#define AB8500_ALARM_MIN_LOW 0x08
#define AB8500_ALARM_MIN_MID 0x09
#define RTC_CTRL 0x0B
#define RTC_ALARM_ENABLE 0x4
static struct device *sysctrl_dev;
void ab8500_power_off(void)
{
sigset_t old;
sigset_t all;
static char *pss[] = {"ab8500_ac", "ab8500_usb"};
static char *pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"};
int i;
bool charger_present = false;
union power_supply_propval val;
struct power_supply *psy;
int ret;
if (sysctrl_dev == NULL) {
pr_err("%s: sysctrl not initialized\n", __func__);
return;
}
/*
* If we have a charger connected and we're powering off,
* reboot into charge-only mode.
@ -74,6 +85,63 @@ shutdown:
}
}
/*
* Use the AB WD to reset the platform. It will perform a hard
* reset instead of a soft reset. Write the reset reason to
* the AB before reset, which can be read upon restart.
*/
void ab8500_restart(char mode, const char *cmd)
{
struct ab8500_platform_data *plat;
struct ab8500_sysctrl_platform_data *pdata;
u16 reason = 0;
u8 val;
if (sysctrl_dev == NULL) {
pr_err("%s: sysctrl not initialized\n", __func__);
return;
}
plat = dev_get_platdata(sysctrl_dev->parent);
pdata = plat->sysctrl;
if (pdata->reboot_reason_code)
reason = pdata->reboot_reason_code(cmd);
else
pr_warn("[%s] No reboot reason set. Default reason %d\n",
__func__, reason);
/*
* Disable RTC alarm, just a precaution so that no alarm
* is running when WD reset is executed.
*/
abx500_get_register_interruptible(sysctrl_dev, AB8500_RTC,
RTC_CTRL , &val);
abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
RTC_CTRL , (val & ~RTC_ALARM_ENABLE));
/*
* Android is not using the RTC alarm registers during reboot
* so we borrow them for writing the reason of reset
*/
/* reason[8 LSB] */
val = reason & 0xFF;
abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
AB8500_ALARM_MIN_LOW , val);
/* reason[8 MSB] */
val = (reason>>8) & 0xFF;
abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
AB8500_ALARM_MIN_MID , val);
/* Setting WD timeout to 0 */
ab8500_sysctrl_write(AB8500_MAINWDOGTIMER, 0xFF, 0x0);
/* Setting the parameters to AB8500 WD*/
ab8500_sysctrl_write(AB8500_MAINWDOGCTRL, 0xFF, (AB8500_ENABLE_WD |
AB8500_WD_RESTART_ON_EXPIRE | AB8500_KICK_WD));
}
static inline bool valid_bank(u8 bank)
{
return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
@ -85,7 +153,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
u8 bank;
if (sysctrl_dev == NULL)
return -EAGAIN;
return -EINVAL;
bank = (reg >> 8);
if (!valid_bank(bank))
@ -101,7 +169,7 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
u8 bank;
if (sysctrl_dev == NULL)
return -EAGAIN;
return -EINVAL;
bank = (reg >> 8);
if (!valid_bank(bank))
@ -114,28 +182,36 @@ EXPORT_SYMBOL(ab8500_sysctrl_write);
static int ab8500_sysctrl_probe(struct platform_device *pdev)
{
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
struct ab8500_platform_data *plat;
struct ab8500_sysctrl_platform_data *pdata;
sysctrl_dev = &pdev->dev;
plat = dev_get_platdata(pdev->dev.parent);
if (!(plat && plat->sysctrl))
return -EINVAL;
if (plat->pm_power_off)
pm_power_off = ab8500_power_off;
pdata = plat->sysctrl;
if (pdata) {
int ret, i, j;
int last, ret, i, j;
for (i = AB8500_SYSCLKREQ1RFCLKBUF;
i <= AB8500_SYSCLKREQ8RFCLKBUF; i++) {
if (is_ab8505(ab8500))
last = AB8500_SYSCLKREQ4RFCLKBUF;
else
last = AB8500_SYSCLKREQ8RFCLKBUF;
for (i = AB8500_SYSCLKREQ1RFCLKBUF; i <= last; i++) {
j = i - AB8500_SYSCLKREQ1RFCLKBUF;
ret = ab8500_sysctrl_write(i, 0xff,
pdata->initial_req_buf_config[j]);
pdata->initial_req_buf_config[j]);
dev_dbg(&pdev->dev,
"Setting SysClkReq%dRfClkBuf 0x%X\n",
j + 1,
pdata->initial_req_buf_config[j]);
"Setting SysClkReq%dRfClkBuf 0x%X\n",
j + 1,
pdata->initial_req_buf_config[j]);
if (ret < 0) {
dev_err(&pdev->dev,
"unable to set sysClkReq%dRfClkBuf: "

View File

@ -714,7 +714,6 @@ out_irq:
while (--i >= 0)
free_irq(info->irq[i], info);
out:
kfree(info);
return ret;
}
@ -728,7 +727,6 @@ static int pm860x_charger_remove(struct platform_device *pdev)
free_irq(info->irq[0], info);
for (i = 0; i < info->irq_nums; i++)
free_irq(info->irq[i], info);
kfree(info);
return 0;
}

View File

@ -340,6 +340,13 @@ config CHARGER_SMB347
Say Y to include support for Summit Microelectronics SMB347
Battery Charger.
config CHARGER_TPS65090
tristate "TPS65090 battery charger driver"
depends on MFD_TPS65090
help
Say Y here to enable support for battery charging with TPS65090
PMIC chips.
config AB8500_BM
bool "AB8500 Battery Management Driver"
depends on AB8500_CORE && AB8500_GPADC
@ -353,13 +360,6 @@ config BATTERY_GOLDFISH
Say Y to enable support for the battery and AC power in the
Goldfish emulator.
config CHARGER_PM2301
bool "PM2301 Battery Charger Driver"
depends on AB8500_BM
help
Say Y to include support for PM2301 charger driver.
Depends on AB8500 battery management core.
source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY

View File

@ -39,7 +39,7 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
@ -47,10 +47,10 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o
obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_PM2301) += pm2301_charger.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
obj-$(CONFIG_POWER_AVS) += avs/
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_POWER_RESET) += reset/

View File

@ -11,7 +11,7 @@
* Note that the res_to_temp table must be strictly sorted by falling resistance
* values to work.
*/
static struct abx500_res_to_temp temp_tbl_A_thermistor[] = {
const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
{-5, 53407},
{ 0, 48594},
{ 5, 43804},
@ -28,8 +28,12 @@ static struct abx500_res_to_temp temp_tbl_A_thermistor[] = {
{60, 13437},
{65, 12500},
};
EXPORT_SYMBOL(ab8500_temp_tbl_a_thermistor);
static struct abx500_res_to_temp temp_tbl_B_thermistor[] = {
const int ab8500_temp_tbl_a_size = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor);
EXPORT_SYMBOL(ab8500_temp_tbl_a_size);
const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
{-5, 200000},
{ 0, 159024},
{ 5, 151921},
@ -46,8 +50,12 @@ static struct abx500_res_to_temp temp_tbl_B_thermistor[] = {
{60, 85461},
{65, 82869},
};
EXPORT_SYMBOL(ab8500_temp_tbl_b_thermistor);
static struct abx500_v_to_cap cap_tbl_A_thermistor[] = {
const int ab8500_temp_tbl_b_size = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor);
EXPORT_SYMBOL(ab8500_temp_tbl_b_size);
static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
{4171, 100},
{4114, 95},
{4009, 83},
@ -70,7 +78,7 @@ static struct abx500_v_to_cap cap_tbl_A_thermistor[] = {
{3247, 0},
};
static struct abx500_v_to_cap cap_tbl_B_thermistor[] = {
static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
{4161, 100},
{4124, 98},
{4044, 90},
@ -93,7 +101,7 @@ static struct abx500_v_to_cap cap_tbl_B_thermistor[] = {
{3250, 0},
};
static struct abx500_v_to_cap cap_tbl[] = {
static const struct abx500_v_to_cap cap_tbl[] = {
{4186, 100},
{4163, 99},
{4114, 95},
@ -124,7 +132,7 @@ static struct abx500_v_to_cap cap_tbl[] = {
* Note that the res_to_temp table must be strictly sorted by falling
* resistance values to work.
*/
static struct abx500_res_to_temp temp_tbl[] = {
static const struct abx500_res_to_temp temp_tbl[] = {
{-5, 214834},
{ 0, 162943},
{ 5, 124820},
@ -146,7 +154,7 @@ static struct abx500_res_to_temp temp_tbl[] = {
* Note that the batres_vs_temp table must be strictly sorted by falling
* temperature values to work.
*/
static struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
static const struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
{ 40, 120},
{ 30, 135},
{ 20, 165},
@ -160,7 +168,7 @@ static struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
* Note that the batres_vs_temp table must be strictly sorted by falling
* temperature values to work.
*/
static struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
static const struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
{ 60, 300},
{ 30, 300},
{ 20, 300},
@ -171,7 +179,7 @@ static struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
};
/* battery resistance table for LI ION 9100 battery */
static struct batres_vs_temp temp_to_batres_tbl_9100[] = {
static const struct batres_vs_temp temp_to_batres_tbl_9100[] = {
{ 60, 180},
{ 30, 180},
{ 20, 180},
@ -230,10 +238,10 @@ static struct abx500_battery_type bat_type_thermistor[] = {
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor),
.r_to_t_tbl = temp_tbl_A_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor),
.v_to_cap_tbl = cap_tbl_A_thermistor,
.n_temp_tbl_elements = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor),
.r_to_t_tbl = ab8500_temp_tbl_a_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_a_thermistor),
.v_to_cap_tbl = cap_tbl_a_thermistor,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
@ -258,10 +266,10 @@ static struct abx500_battery_type bat_type_thermistor[] = {
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor),
.r_to_t_tbl = temp_tbl_B_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor),
.v_to_cap_tbl = cap_tbl_B_thermistor,
.n_temp_tbl_elements = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor),
.r_to_t_tbl = ab8500_temp_tbl_b_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_b_thermistor),
.v_to_cap_tbl = cap_tbl_b_thermistor,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
@ -407,15 +415,27 @@ static const struct abx500_fg_parameters fg = {
.battok_raising_th_sel1 = 2860,
.maint_thres = 95,
.user_cap_limit = 15,
.pcut_enable = 1,
.pcut_max_time = 127,
.pcut_flag_time = 112,
.pcut_max_restart = 15,
.pcut_debounce_time = 2,
};
static const struct abx500_maxim_parameters maxi_params = {
static const struct abx500_maxim_parameters ab8500_maxi_params = {
.ena_maxi = true,
.chg_curr = 910,
.wait_cycles = 10,
.charger_curr_step = 100,
};
static const struct abx500_maxim_parameters abx540_maxi_params = {
.ena_maxi = true,
.chg_curr = 3000,
.wait_cycles = 10,
.charger_curr_step = 200,
};
static const struct abx500_bm_charger_parameters chg = {
.usb_volt_max = 5500,
.usb_curr_max = 1500,
@ -423,6 +443,46 @@ static const struct abx500_bm_charger_parameters chg = {
.ac_curr_max = 1500,
};
/*
* This array maps the raw hex value to charger output current used by the
* AB8500 values
*/
static int ab8500_charge_output_curr_map[] = {
100, 200, 300, 400, 500, 600, 700, 800,
900, 1000, 1100, 1200, 1300, 1400, 1500, 1500,
};
static int ab8540_charge_output_curr_map[] = {
0, 0, 0, 75, 100, 125, 150, 175,
200, 225, 250, 275, 300, 325, 350, 375,
400, 425, 450, 475, 500, 525, 550, 575,
600, 625, 650, 675, 700, 725, 750, 775,
800, 825, 850, 875, 900, 925, 950, 975,
1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175,
1200, 1225, 1250, 1275, 1300, 1325, 1350, 1375,
1400, 1425, 1450, 1500, 1600, 1700, 1900, 2000,
};
/*
* This array maps the raw hex value to charger input current used by the
* AB8500 values
*/
static int ab8500_charge_input_curr_map[] = {
50, 98, 193, 290, 380, 450, 500, 600,
700, 800, 900, 1000, 1100, 1300, 1400, 1500,
};
static int ab8540_charge_input_curr_map[] = {
25, 50, 75, 100, 125, 150, 175, 200,
225, 250, 275, 300, 325, 350, 375, 400,
425, 450, 475, 500, 525, 550, 575, 600,
625, 650, 675, 700, 725, 750, 775, 800,
825, 850, 875, 900, 925, 950, 975, 1000,
1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200,
1225, 1250, 1275, 1300, 1325, 1350, 1375, 1400,
1425, 1450, 1475, 1500, 1500, 1500, 1500, 1500,
};
struct abx500_bm_data ab8500_bm_data = {
.temp_under = 3,
.temp_low = 8,
@ -442,22 +502,60 @@ struct abx500_bm_data ab8500_bm_data = {
.fg_res = 100,
.cap_levels = &cap_levels,
.bat_type = bat_type_thermistor,
.n_btypes = 3,
.n_btypes = ARRAY_SIZE(bat_type_thermistor),
.batt_id = 0,
.interval_charging = 5,
.interval_not_charging = 120,
.temp_hysteresis = 3,
.gnd_lift_resistance = 34,
.maxi = &maxi_params,
.chg_output_curr = ab8500_charge_output_curr_map,
.n_chg_out_curr = ARRAY_SIZE(ab8500_charge_output_curr_map),
.maxi = &ab8500_maxi_params,
.chg_params = &chg,
.fg_params = &fg,
.chg_input_curr = ab8500_charge_input_curr_map,
.n_chg_in_curr = ARRAY_SIZE(ab8500_charge_input_curr_map),
};
struct abx500_bm_data ab8540_bm_data = {
.temp_under = 3,
.temp_low = 8,
.temp_high = 43,
.temp_over = 48,
.main_safety_tmr_h = 4,
.temp_interval_chg = 20,
.temp_interval_nochg = 120,
.usb_safety_tmr_h = 4,
.bkup_bat_v = BUP_VCH_SEL_2P6V,
.bkup_bat_i = BUP_ICH_SEL_150UA,
.no_maintenance = false,
.capacity_scaling = false,
.adc_therm = ABx500_ADC_THERM_BATCTRL,
.chg_unknown_bat = false,
.enable_overshoot = false,
.fg_res = 100,
.cap_levels = &cap_levels,
.bat_type = bat_type_thermistor,
.n_btypes = ARRAY_SIZE(bat_type_thermistor),
.batt_id = 0,
.interval_charging = 5,
.interval_not_charging = 120,
.temp_hysteresis = 3,
.gnd_lift_resistance = 0,
.maxi = &abx540_maxi_params,
.chg_params = &chg,
.fg_params = &fg,
.chg_output_curr = ab8540_charge_output_curr_map,
.n_chg_out_curr = ARRAY_SIZE(ab8540_charge_output_curr_map),
.chg_input_curr = ab8540_charge_input_curr_map,
.n_chg_in_curr = ARRAY_SIZE(ab8540_charge_input_curr_map),
};
int ab8500_bm_of_probe(struct device *dev,
struct device_node *np,
struct abx500_bm_data *bm)
{
struct batres_vs_temp *tmp_batres_tbl;
const struct batres_vs_temp *tmp_batres_tbl;
struct device_node *battery_node;
const char *btech;
int i;

View File

@ -42,6 +42,9 @@
#define BTEMP_BATCTRL_CURR_SRC_16UA 16
#define BTEMP_BATCTRL_CURR_SRC_18UA 18
#define BTEMP_BATCTRL_CURR_SRC_60UA 60
#define BTEMP_BATCTRL_CURR_SRC_120UA 120
#define to_ab8500_btemp_device_info(x) container_of((x), \
struct ab8500_btemp, btemp_psy);
@ -76,8 +79,8 @@ struct ab8500_btemp_ranges {
* @dev: Pointer to the structure device
* @node: List of AB8500 BTEMPs, hence prepared for reentrance
* @curr_source: What current source we use, in uA
* @bat_temp: Battery temperature in degree Celcius
* @prev_bat_temp Last dispatched battery temperature
* @bat_temp: Dispatched battery temperature in degree Celcius
* @prev_bat_temp Last measured battery temperature in degree Celcius
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
* @fg: Pointer to the struct fg
@ -128,6 +131,7 @@ struct ab8500_btemp *ab8500_btemp_get(void)
return btemp;
}
EXPORT_SYMBOL(ab8500_btemp_get);
/**
* ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance
@ -155,7 +159,7 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
/*
* If the battery has internal NTC, we use the current
* source to calculate the resistance, 7uA or 20uA
* source to calculate the resistance.
*/
rbs = (v_batctrl * 1000
- di->bm->gnd_lift_resistance * inst_curr)
@ -216,7 +220,12 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
/* Only do this for batteries with internal NTC */
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
if (is_ab8540(di->parent)) {
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_60UA)
curr = BAT_CTRL_60U_ENA;
else
curr = BAT_CTRL_120U_ENA;
} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA)
curr = BAT_CTRL_16U_ENA;
else
@ -257,7 +266,14 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
} else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
dev_dbg(di->dev, "Disable BATCTRL curr source\n");
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
if (is_ab8540(di->parent)) {
/* Write 0 to the curr bits */
ret = abx500_mask_and_set_register_interruptible(
di->dev,
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA,
~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA));
} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
/* Write 0 to the curr bits */
ret = abx500_mask_and_set_register_interruptible(
di->dev,
@ -314,7 +330,13 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
* if we got an error above
*/
disable_curr_source:
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
if (is_ab8540(di->parent)) {
/* Write 0 to the curr bits */
ret = abx500_mask_and_set_register_interruptible(di->dev,
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA,
~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA));
} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
/* Write 0 to the curr bits */
ret = abx500_mask_and_set_register_interruptible(di->dev,
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
@ -541,7 +563,9 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
{
int res;
u8 i;
if (is_ab9540(di->parent) || is_ab8505(di->parent))
if (is_ab8540(di->parent))
di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA;
else if (is_ab9540(di->parent) || is_ab8505(di->parent))
di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
else
di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
@ -579,12 +603,17 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
/*
* We only have to change current source if the
* detected type is Type 1, else we use the 7uA source
* detected type is Type 1.
*/
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
di->bm->batt_id == 1) {
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
dev_dbg(di->dev, "Set BATCTRL current source to 16uA\n");
di->bm->batt_id == 1) {
if (is_ab8540(di->parent)) {
dev_dbg(di->dev,
"Set BATCTRL current source to 60uA\n");
di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA;
} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
dev_dbg(di->dev,
"Set BATCTRL current source to 16uA\n");
di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
} else {
dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
@ -604,22 +633,37 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
static void ab8500_btemp_periodic_work(struct work_struct *work)
{
int interval;
int bat_temp;
struct ab8500_btemp *di = container_of(work,
struct ab8500_btemp, btemp_periodic_work.work);
if (!di->initialized) {
di->initialized = true;
/* Identify the battery */
if (ab8500_btemp_id(di) < 0)
dev_warn(di->dev, "failed to identify the battery\n");
}
di->bat_temp = ab8500_btemp_measure_temp(di);
if (di->bat_temp != di->prev_bat_temp) {
di->prev_bat_temp = di->bat_temp;
bat_temp = ab8500_btemp_measure_temp(di);
/*
* Filter battery temperature.
* Allow direct updates on temperature only if two samples result in
* same temperature. Else only allow 1 degree change from previous
* reported value in the direction of the new measurement.
*/
if ((bat_temp == di->prev_bat_temp) || !di->initialized) {
if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) {
di->initialized = true;
di->bat_temp = bat_temp;
power_supply_changed(&di->btemp_psy);
}
} else if (bat_temp < di->prev_bat_temp) {
di->bat_temp--;
power_supply_changed(&di->btemp_psy);
} else if (bat_temp > di->prev_bat_temp) {
di->bat_temp++;
power_supply_changed(&di->btemp_psy);
}
di->prev_bat_temp = bat_temp;
if (di->events.ac_conn || di->events.usb_conn)
interval = di->bm->temp_interval_chg;
@ -772,7 +816,7 @@ static void ab8500_btemp_periodic(struct ab8500_btemp *di,
*
* Returns battery temperature
*/
static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
int ab8500_btemp_get_temp(struct ab8500_btemp *di)
{
int temp = 0;
@ -808,6 +852,7 @@ static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
}
return temp;
}
EXPORT_SYMBOL(ab8500_btemp_get_temp);
/**
* ab8500_btemp_get_batctrl_temp() - get the temperature
@ -819,6 +864,7 @@ int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp)
{
return btemp->bat_temp * 1000;
}
EXPORT_SYMBOL(ab8500_btemp_get_batctrl_temp);
/**
* ab8500_btemp_get_property() - get the btemp properties

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,7 @@
#define MILLI_TO_MICRO 1000
#define FG_LSB_IN_MA 1627
#define QLSB_NANO_AMP_HOURS_X10 1129
#define QLSB_NANO_AMP_HOURS_X10 1071
#define INS_CURR_TIMEOUT (3 * HZ)
#define SEC_TO_SAMPLE(S) (S * 4)
@ -672,11 +672,11 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
/*
* Convert to unit value in mA
* Full scale input voltage is
* 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
* 63.160mV => LSB = 63.160mV/(4096*res) = 1.542mA
* Given a 250ms conversion cycle time the LSB corresponds
* to 112.9 nAh. Convert to current by dividing by the conversion
* to 107.1 nAh. Convert to current by dividing by the conversion
* time in hours (250ms = 1 / (3600 * 4)h)
* 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
* 107.1nAh assumes 10mOhm, but fg_res is in 0.1mOhm
*/
val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) /
(1000 * di->bm->fg_res);
@ -863,7 +863,7 @@ static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
{
int i, tbl_size;
struct abx500_v_to_cap *tbl;
const struct abx500_v_to_cap *tbl;
int cap = 0;
tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl,
@ -915,7 +915,7 @@ static int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di)
static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
{
int i, tbl_size;
struct batres_vs_temp *tbl;
const struct batres_vs_temp *tbl;
int resist = 0;
tbl = di->bm->bat_type[di->bm->batt_id].batres_tbl;
@ -1354,9 +1354,6 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
* algorithm says.
*/
di->bat_cap.prev_percent = 1;
di->bat_cap.permille = 1;
di->bat_cap.prev_mah = 1;
di->bat_cap.mah = 1;
percent = 1;
changed = true;
@ -1683,7 +1680,6 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
break;
case AB8500_FG_DISCHARGE_WAKEUP:
ab8500_fg_coulomb_counter(di, true);
ab8500_fg_calc_cap_discharge_voltage(di, true);
di->fg_samples = SEC_TO_SAMPLE(
@ -1768,9 +1764,10 @@ static void ab8500_fg_algorithm(struct ab8500_fg *di)
ab8500_fg_algorithm_discharging(di);
}
dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d "
dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d %d "
"%d %d %d %d %d %d %d\n",
di->bat_cap.max_mah_design,
di->bat_cap.max_mah,
di->bat_cap.mah,
di->bat_cap.permille,
di->bat_cap.level,
@ -1982,7 +1979,7 @@ static void ab8500_fg_instant_work(struct work_struct *work)
}
/**
* ab8500_fg_cc_data_end_handler() - isr to get battery avg current.
* ab8500_fg_cc_data_end_handler() - end of data conversion isr.
* @irq: interrupt number
* @_di: pointer to the ab8500_fg structure
*
@ -2002,7 +1999,7 @@ static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
}
/**
* ab8500_fg_cc_convend_handler() - isr to get battery avg current.
* ab8500_fg_cc_int_calib_handler () - end of calibration isr.
* @irq: interrupt number
* @_di: pointer to the ab8500_fg structure
*
@ -2153,9 +2150,7 @@ static int ab8500_fg_get_property(struct power_supply *psy,
val->intval = di->bat_cap.prev_mah;
break;
case POWER_SUPPLY_PROP_CAPACITY:
if (di->bm->capacity_scaling)
val->intval = di->bat_cap.cap_scale.scaled_cap;
else if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
di->flags.batt_id_received)
val->intval = 100;
else
@ -2344,6 +2339,50 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
dev_err(di->dev, "BattOk init write failed.\n");
goto out;
}
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
abx500_get_chip_id(di->dev) >= AB8500_CUT2P0)
|| is_ab8540(di->parent)) {
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time);
if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__);
goto out;
};
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time);
if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__);
goto out;
};
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart);
if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__);
goto out;
};
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time);
if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__);
goto out;
};
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable);
if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__);
goto out;
};
}
out:
return ret;
}
@ -2546,6 +2585,428 @@ static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
return ret;
}
static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_FLAG_TIME_REG, &reg_value);
if (ret < 0) {
dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n");
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
fail:
return ret;
}
static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
long unsigned reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
reg_value = simple_strtoul(buf, NULL, 10);
if (reg_value > 0x7F) {
dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n");
goto fail;
}
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value);
if (ret < 0)
dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n");
fail:
return count;
}
static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_MAX_TIME_REG, &reg_value);
if (ret < 0) {
dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n");
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
fail:
return ret;
}
static ssize_t ab8505_powercut_maxtime_write(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
int reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
reg_value = simple_strtoul(buf, NULL, 10);
if (reg_value > 0x7F) {
dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n");
goto fail;
}
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value);
if (ret < 0)
dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n");
fail:
return count;
}
static ssize_t ab8505_powercut_restart_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_RESTART_REG, &reg_value);
if (ret < 0) {
dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF));
fail:
return ret;
}
static ssize_t ab8505_powercut_restart_write(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
int reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
reg_value = simple_strtoul(buf, NULL, 10);
if (reg_value > 0xF) {
dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n");
goto fail;
}
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value);
if (ret < 0)
dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n");
fail:
return count;
}
static ssize_t ab8505_powercut_timer_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_TIME_REG, &reg_value);
if (ret < 0) {
dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n");
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
fail:
return ret;
}
static ssize_t ab8505_powercut_restart_counter_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_RESTART_REG, &reg_value);
if (ret < 0) {
dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4);
fail:
return ret;
}
static ssize_t ab8505_powercut_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
if (ret < 0)
goto fail;
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1));
fail:
return ret;
}
static ssize_t ab8505_powercut_write(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
int reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
reg_value = simple_strtoul(buf, NULL, 10);
if (reg_value > 0x1) {
dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n");
goto fail;
}
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value);
if (ret < 0)
dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n");
fail:
return count;
}
static ssize_t ab8505_powercut_flag_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
if (ret < 0) {
dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4));
fail:
return ret;
}
static ssize_t ab8505_powercut_debounce_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_DEBOUNCE_REG, &reg_value);
if (ret < 0) {
dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n");
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7));
fail:
return ret;
}
static ssize_t ab8505_powercut_debounce_write(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
int reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
reg_value = simple_strtoul(buf, NULL, 10);
if (reg_value > 0x7) {
dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n");
goto fail;
}
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value);
if (ret < 0)
dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n");
fail:
return count;
}
static ssize_t ab8505_powercut_enable_status_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
if (ret < 0) {
dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5));
fail:
return ret;
}
static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
__ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP),
ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write),
__ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP),
ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write),
__ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP),
ab8505_powercut_restart_read, ab8505_powercut_restart_write),
__ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL),
__ATTR(powercut_restart_counter, S_IRUGO,
ab8505_powercut_restart_counter_read, NULL),
__ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
ab8505_powercut_read, ab8505_powercut_write),
__ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL),
__ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP),
ab8505_powercut_debounce_read, ab8505_powercut_debounce_write),
__ATTR(powercut_enable_status, S_IRUGO,
ab8505_powercut_enable_status_read, NULL),
};
static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev)
{
unsigned int i, j;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0)
|| is_ab8540(di->parent)) {
for (j = 0; j < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); j++)
if (device_create_file(dev, &ab8505_fg_sysfs_psy_attrs[j]))
goto sysfs_psy_create_attrs_failed_ab8505;
}
return 0;
sysfs_psy_create_attrs_failed_ab8505:
dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n");
while (j--)
device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
return -EIO;
}
static void ab8500_fg_sysfs_psy_remove_attrs(struct device *dev)
{
unsigned int i;
struct power_supply *psy = dev_get_drvdata(dev);
struct ab8500_fg *di;
di = to_ab8500_fg_device_info(psy);
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0)
|| is_ab8540(di->parent)) {
for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
(void)device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
}
}
/* Exposure to the sysfs interface <<END>> */
#if defined(CONFIG_PM)
@ -2607,6 +3068,7 @@ static int ab8500_fg_remove(struct platform_device *pdev)
ab8500_fg_sysfs_exit(di);
flush_scheduled_work();
ab8500_fg_sysfs_psy_remove_attrs(di->fg_psy.dev);
power_supply_unregister(&di->fg_psy);
platform_set_drvdata(pdev, NULL);
return ret;
@ -2772,6 +3234,13 @@ static int ab8500_fg_probe(struct platform_device *pdev)
goto free_irq;
}
ret = ab8500_fg_sysfs_psy_create_attrs(di->fg_psy.dev);
if (ret) {
dev_err(di->dev, "failed to create FG psy\n");
ab8500_fg_sysfs_exit(di);
goto free_irq;
}
/* Calibrate the fg first time */
di->flags.calibrate = true;
di->calib_state = AB8500_FG_CALIB_INIT;

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) ST-Ericsson SA 2012
* Copyright (c) 2012 Sony Mobile Communications AB
*
* Charging algorithm driver for abx500 variants
*
@ -8,11 +9,13 @@
* Johan Palsson <johan.palsson@stericsson.com>
* Karl Komierowski <karl.komierowski@stericsson.com>
* Arun R Murthy <arun.murthy@stericsson.com>
* Author: Imre Sunyi <imre.sunyi@sonymobile.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
@ -24,8 +27,10 @@
#include <linux/of.h>
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/notifier.h>
/* Watchdog kick interval */
#define CHG_WD_INTERVAL (6 * HZ)
@ -33,6 +38,18 @@
/* End-of-charge criteria counter */
#define EOC_COND_CNT 10
/* One hour expressed in seconds */
#define ONE_HOUR_IN_SECONDS 3600
/* Five minutes expressed in seconds */
#define FIVE_MINUTES_IN_SECONDS 300
/* Plus margin for the low battery threshold */
#define BAT_PLUS_MARGIN (100)
#define CHARGALG_CURR_STEP_LOW 0
#define CHARGALG_CURR_STEP_HIGH 100
#define to_abx500_chargalg_device_info(x) container_of((x), \
struct abx500_chargalg, chargalg_psy);
@ -66,6 +83,11 @@ struct abx500_chargalg_suspension_status {
bool usb_suspended;
};
struct abx500_chargalg_current_step_status {
bool curr_step_change;
int curr_step;
};
struct abx500_chargalg_battery_data {
int temp;
int volt;
@ -82,6 +104,7 @@ enum abx500_chargalg_states {
STATE_HW_TEMP_PROTECT_INIT,
STATE_HW_TEMP_PROTECT,
STATE_NORMAL_INIT,
STATE_USB_PP_PRE_CHARGE,
STATE_NORMAL,
STATE_WAIT_FOR_RECHARGE_INIT,
STATE_WAIT_FOR_RECHARGE,
@ -113,6 +136,7 @@ static const char *states[] = {
"HW_TEMP_PROTECT_INIT",
"HW_TEMP_PROTECT",
"NORMAL_INIT",
"USB_PP_PRE_CHARGE",
"NORMAL",
"WAIT_FOR_RECHARGE_INIT",
"WAIT_FOR_RECHARGE",
@ -204,6 +228,8 @@ enum maxim_ret {
* @batt_data: data of the battery
* @susp_status: current charger suspension status
* @bm: Platform specific battery management information
* @curr_status: Current step status for over-current protection
* @parent: pointer to the struct abx500
* @chargalg_psy: structure that holds the battery properties exposed by
* the charging algorithm
* @events: structure for information about events triggered
@ -227,6 +253,8 @@ struct abx500_chargalg {
struct abx500_chargalg_charger_info chg_info;
struct abx500_chargalg_battery_data batt_data;
struct abx500_chargalg_suspension_status susp_status;
struct ab8500 *parent;
struct abx500_chargalg_current_step_status curr_status;
struct abx500_bm_data *bm;
struct power_supply chargalg_psy;
struct ux500_charger *ac_chg;
@ -236,51 +264,69 @@ struct abx500_chargalg {
struct delayed_work chargalg_periodic_work;
struct delayed_work chargalg_wd_work;
struct work_struct chargalg_work;
struct timer_list safety_timer;
struct timer_list maintenance_timer;
struct hrtimer safety_timer;
struct hrtimer maintenance_timer;
struct kobject chargalg_kobject;
};
/*External charger prepare notifier*/
BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
/* Main battery properties */
static enum power_supply_property abx500_chargalg_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
};
struct abx500_chargalg_sysfs_entry {
struct attribute attr;
ssize_t (*show)(struct abx500_chargalg *, char *);
ssize_t (*store)(struct abx500_chargalg *, const char *, size_t);
};
/**
* abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
* @data: pointer to the abx500_chargalg structure
* @timer: pointer to the hrtimer structure
*
* This function gets called when the safety timer for the charger
* expires
*/
static void abx500_chargalg_safety_timer_expired(unsigned long data)
static enum hrtimer_restart
abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
{
struct abx500_chargalg *di = (struct abx500_chargalg *) data;
struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
safety_timer);
dev_err(di->dev, "Safety timer expired\n");
di->events.safety_timer_expired = true;
/* Trigger execution of the algorithm instantly */
queue_work(di->chargalg_wq, &di->chargalg_work);
return HRTIMER_NORESTART;
}
/**
* abx500_chargalg_maintenance_timer_expired() - Expiration of
* the maintenance timer
* @i: pointer to the abx500_chargalg structure
* @timer: pointer to the timer structure
*
* This function gets called when the maintenence timer
* expires
*/
static void abx500_chargalg_maintenance_timer_expired(unsigned long data)
static enum hrtimer_restart
abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
{
struct abx500_chargalg *di = (struct abx500_chargalg *) data;
struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
maintenance_timer);
dev_dbg(di->dev, "Maintenance timer expired\n");
di->events.maintenance_timer_expired = true;
/* Trigger execution of the algorithm instantly */
queue_work(di->chargalg_wq, &di->chargalg_work);
return HRTIMER_NORESTART;
}
/**
@ -303,6 +349,30 @@ static void abx500_chargalg_state_to(struct abx500_chargalg *di,
di->charge_state = state;
}
static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
{
switch (di->charge_state) {
case STATE_NORMAL:
case STATE_MAINTENANCE_A:
case STATE_MAINTENANCE_B:
break;
default:
return 0;
}
if (di->chg_info.charger_type & USB_CHG) {
return di->usb_chg->ops.check_enable(di->usb_chg,
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
} else if ((di->chg_info.charger_type & AC_CHG) &&
!(di->ac_chg->external)) {
return di->ac_chg->ops.check_enable(di->ac_chg,
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
}
return 0;
}
/**
* abx500_chargalg_check_charger_connection() - Check charger connection change
* @di: pointer to the abx500_chargalg structure
@ -347,6 +417,22 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
return di->chg_info.conn_chg;
}
/**
* abx500_chargalg_check_current_step_status() - Check charging current
* step status.
* @di: pointer to the abx500_chargalg structure
*
* This function will check if there is a change in the charging current step
* and change charge state accordingly.
*/
static void abx500_chargalg_check_current_step_status
(struct abx500_chargalg *di)
{
if (di->curr_status.curr_step_change)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
di->curr_status.curr_step_change = false;
}
/**
* abx500_chargalg_start_safety_timer() - Start charging safety timer
* @di: pointer to the abx500_chargalg structure
@ -356,19 +442,16 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
*/
static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
{
unsigned long timer_expiration = 0;
/* Charger-dependent expiration time in hours*/
int timer_expiration = 0;
switch (di->chg_info.charger_type) {
case AC_CHG:
timer_expiration =
round_jiffies(jiffies +
(di->bm->main_safety_tmr_h * 3600 * HZ));
timer_expiration = di->bm->main_safety_tmr_h;
break;
case USB_CHG:
timer_expiration =
round_jiffies(jiffies +
(di->bm->usb_safety_tmr_h * 3600 * HZ));
timer_expiration = di->bm->usb_safety_tmr_h;
break;
default:
@ -377,11 +460,10 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
}
di->events.safety_timer_expired = false;
di->safety_timer.expires = timer_expiration;
if (!timer_pending(&di->safety_timer))
add_timer(&di->safety_timer);
else
mod_timer(&di->safety_timer, timer_expiration);
hrtimer_set_expires_range(&di->safety_timer,
ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0),
ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL);
}
/**
@ -392,8 +474,8 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
*/
static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
{
di->events.safety_timer_expired = false;
del_timer(&di->safety_timer);
if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
di->events.safety_timer_expired = false;
}
/**
@ -408,17 +490,11 @@ static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
int duration)
{
unsigned long timer_expiration;
/* Convert from hours to jiffies */
timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ));
hrtimer_set_expires_range(&di->maintenance_timer,
ktime_set(duration * ONE_HOUR_IN_SECONDS, 0),
ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
di->events.maintenance_timer_expired = false;
di->maintenance_timer.expires = timer_expiration;
if (!timer_pending(&di->maintenance_timer))
add_timer(&di->maintenance_timer);
else
mod_timer(&di->maintenance_timer, timer_expiration);
hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
}
/**
@ -430,8 +506,8 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
*/
static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
{
di->events.maintenance_timer_expired = false;
del_timer(&di->maintenance_timer);
if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
di->events.maintenance_timer_expired = false;
}
/**
@ -477,6 +553,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
int vset, int iset)
{
static int abx500_chargalg_ex_ac_enable_toggle;
if (!di->ac_chg || !di->ac_chg->ops.enable)
return -ENXIO;
@ -489,6 +567,14 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
di->chg_info.ac_iset = iset;
di->chg_info.ac_vset = vset;
/* Enable external charger */
if (enable && di->ac_chg->external &&
!abx500_chargalg_ex_ac_enable_toggle) {
blocking_notifier_call_chain(&charger_notifier_list,
0, di->dev);
abx500_chargalg_ex_ac_enable_toggle++;
}
return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
}
@ -520,6 +606,37 @@ static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
}
/**
* ab8540_chargalg_usb_pp_en() - Enable/ disable USB power path
* @di: pointer to the abx500_chargalg structure
* @enable: power path enable/disable
*
* The USB power path will be enable/ disable
*/
static int ab8540_chargalg_usb_pp_en(struct abx500_chargalg *di, bool enable)
{
if (!di->usb_chg || !di->usb_chg->ops.pp_enable)
return -ENXIO;
return di->usb_chg->ops.pp_enable(di->usb_chg, enable);
}
/**
* ab8540_chargalg_usb_pre_chg_en() - Enable/ disable USB pre-charge
* @di: pointer to the abx500_chargalg structure
* @enable: USB pre-charge enable/disable
*
* The USB USB pre-charge will be enable/ disable
*/
static int ab8540_chargalg_usb_pre_chg_en(struct abx500_chargalg *di,
bool enable)
{
if (!di->usb_chg || !di->usb_chg->ops.pre_chg_enable)
return -ENXIO;
return di->usb_chg->ops.pre_chg_enable(di->usb_chg, enable);
}
/**
* abx500_chargalg_update_chg_curr() - Update charger current
* @di: pointer to the abx500_chargalg structure
@ -613,8 +730,6 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
int vset, int iset)
{
bool start_chargalg_wd = true;
switch (di->chg_info.charger_type) {
case AC_CHG:
dev_dbg(di->dev,
@ -632,12 +747,8 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
default:
dev_err(di->dev, "Unknown charger to charge from\n");
start_chargalg_wd = false;
break;
}
if (start_chargalg_wd && !delayed_work_pending(&di->chargalg_wd_work))
queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
}
/**
@ -725,6 +836,9 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
di->batt_data.avg_curr > 0) {
if (++di->eoc_cnt >= EOC_COND_CNT) {
di->eoc_cnt = 0;
if ((di->chg_info.charger_type & USB_CHG) &&
(di->usb_chg->power_path))
ab8540_chargalg_usb_pp_en(di, true);
di->charge_status = POWER_SUPPLY_STATUS_FULL;
di->maintenance_chg = true;
dev_dbg(di->dev, "EOC reached!\n");
@ -1217,6 +1331,8 @@ static void abx500_chargalg_external_power_changed(struct power_supply *psy)
static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
{
int charger_status;
int ret;
int curr_step_lvl;
/* Collect data from all power_supply class devices */
class_for_each_device(power_supply_class, NULL,
@ -1227,6 +1343,15 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
abx500_chargalg_check_charger_voltage(di);
charger_status = abx500_chargalg_check_charger_connection(di);
abx500_chargalg_check_current_step_status(di);
if (is_ab8500(di->parent)) {
ret = abx500_chargalg_check_charger_enable(di);
if (ret < 0)
dev_err(di->dev, "Checking charger is enabled error"
": Returned Value %d\n", ret);
}
/*
* First check if we have a charger connected.
* Also we don't allow charging of unknown batteries if configured
@ -1416,9 +1541,34 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
break;
case STATE_NORMAL_INIT:
abx500_chargalg_start_charging(di,
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
if ((di->chg_info.charger_type & USB_CHG) &&
di->usb_chg->power_path) {
if (di->batt_data.volt >
(di->bm->fg_params->lowbat_threshold +
BAT_PLUS_MARGIN)) {
ab8540_chargalg_usb_pre_chg_en(di, false);
ab8540_chargalg_usb_pp_en(di, false);
} else {
ab8540_chargalg_usb_pp_en(di, true);
ab8540_chargalg_usb_pre_chg_en(di, true);
abx500_chargalg_state_to(di,
STATE_USB_PP_PRE_CHARGE);
break;
}
}
if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW)
abx500_chargalg_stop_charging(di);
else {
curr_step_lvl = di->bm->bat_type[
di->bm->batt_id].normal_cur_lvl
* di->curr_status.curr_step
/ CHARGALG_CURR_STEP_HIGH;
abx500_chargalg_start_charging(di,
di->bm->bat_type[di->bm->batt_id]
.normal_vol_lvl, curr_step_lvl);
}
abx500_chargalg_state_to(di, STATE_NORMAL);
abx500_chargalg_start_safety_timer(di);
abx500_chargalg_stop_maintenance_timer(di);
@ -1430,6 +1580,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
break;
case STATE_USB_PP_PRE_CHARGE:
if (di->batt_data.volt >
(di->bm->fg_params->lowbat_threshold +
BAT_PLUS_MARGIN))
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_NORMAL:
handle_maxim_chg_curr(di);
if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
@ -1653,99 +1810,134 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
/* Exposure to the sysfs interface */
/**
* abx500_chargalg_sysfs_show() - sysfs show operations
* @kobj: pointer to the struct kobject
* @attr: pointer to the struct attribute
* @buf: buffer that holds the parameter to send to userspace
*
* Returns a buffer to be displayed in user space
*/
static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di,
char *buf)
{
struct abx500_chargalg *di = container_of(kobj,
struct abx500_chargalg, chargalg_kobject);
return sprintf(buf, "%d\n", di->curr_status.curr_step);
}
static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di,
const char *buf, size_t length)
{
long int param;
int ret;
ret = kstrtol(buf, 10, &param);
if (ret < 0)
return ret;
di->curr_status.curr_step = param;
if (di->curr_status.curr_step >= CHARGALG_CURR_STEP_LOW &&
di->curr_status.curr_step <= CHARGALG_CURR_STEP_HIGH) {
di->curr_status.curr_step_change = true;
queue_work(di->chargalg_wq, &di->chargalg_work);
} else
dev_info(di->dev, "Wrong current step\n"
"Enter 0. Disable AC/USB Charging\n"
"1--100. Set AC/USB charging current step\n"
"100. Enable AC/USB Charging\n");
return strlen(buf);
}
static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di,
char *buf)
{
return sprintf(buf, "%d\n",
di->susp_status.ac_suspended &&
di->susp_status.usb_suspended);
}
/**
* abx500_chargalg_sysfs_charger() - sysfs store operations
* @kobj: pointer to the struct kobject
* @attr: pointer to the struct attribute
* @buf: buffer that holds the parameter passed from userspace
* @length: length of the parameter passed
*
* Returns length of the buffer(input taken from user space) on success
* else error code on failure
* The operation to be performed on passing the parameters from the user space.
*/
static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t length)
static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
const char *buf, size_t length)
{
struct abx500_chargalg *di = container_of(kobj,
struct abx500_chargalg, chargalg_kobject);
long int param;
int ac_usb;
int ret;
char entry = *attr->name;
switch (entry) {
case 'c':
ret = strict_strtol(buf, 10, &param);
if (ret < 0)
return ret;
ret = kstrtol(buf, 10, &param);
if (ret < 0)
return ret;
ac_usb = param;
switch (ac_usb) {
case 0:
/* Disable charging */
di->susp_status.ac_suspended = true;
di->susp_status.usb_suspended = true;
di->susp_status.suspended_change = true;
/* Trigger a state change */
queue_work(di->chargalg_wq,
&di->chargalg_work);
break;
case 1:
/* Enable AC Charging */
di->susp_status.ac_suspended = false;
di->susp_status.suspended_change = true;
/* Trigger a state change */
queue_work(di->chargalg_wq,
&di->chargalg_work);
break;
case 2:
/* Enable USB charging */
di->susp_status.usb_suspended = false;
di->susp_status.suspended_change = true;
/* Trigger a state change */
queue_work(di->chargalg_wq,
&di->chargalg_work);
break;
default:
dev_info(di->dev, "Wrong input\n"
"Enter 0. Disable AC/USB Charging\n"
"1. Enable AC charging\n"
"2. Enable USB Charging\n");
};
ac_usb = param;
switch (ac_usb) {
case 0:
/* Disable charging */
di->susp_status.ac_suspended = true;
di->susp_status.usb_suspended = true;
di->susp_status.suspended_change = true;
/* Trigger a state change */
queue_work(di->chargalg_wq,
&di->chargalg_work);
break;
case 1:
/* Enable AC Charging */
di->susp_status.ac_suspended = false;
di->susp_status.suspended_change = true;
/* Trigger a state change */
queue_work(di->chargalg_wq,
&di->chargalg_work);
break;
case 2:
/* Enable USB charging */
di->susp_status.usb_suspended = false;
di->susp_status.suspended_change = true;
/* Trigger a state change */
queue_work(di->chargalg_wq,
&di->chargalg_work);
break;
default:
dev_info(di->dev, "Wrong input\n"
"Enter 0. Disable AC/USB Charging\n"
"1. Enable AC charging\n"
"2. Enable USB Charging\n");
};
return strlen(buf);
}
static struct attribute abx500_chargalg_en_charger = \
static struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger =
__ATTR(chargalg, 0644, abx500_chargalg_en_show,
abx500_chargalg_en_store);
static struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step =
__ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show,
abx500_chargalg_curr_step_store);
static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
.name = "chargalg",
.mode = S_IRUGO | S_IWUSR,
};
struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
struct abx500_chargalg_sysfs_entry, attr);
struct abx500_chargalg *di = container_of(kobj,
struct abx500_chargalg, chargalg_kobject);
if (!entry->show)
return -EIO;
return entry->show(di, buf);
}
static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t length)
{
struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
struct abx500_chargalg_sysfs_entry, attr);
struct abx500_chargalg *di = container_of(kobj,
struct abx500_chargalg, chargalg_kobject);
if (!entry->store)
return -EIO;
return entry->store(di, buf, length);
}
static struct attribute *abx500_chargalg_chg[] = {
&abx500_chargalg_en_charger,
NULL
&abx500_chargalg_en_charger.attr,
&abx500_chargalg_curr_step.attr,
NULL,
};
static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
@ -1832,10 +2024,16 @@ static int abx500_chargalg_remove(struct platform_device *pdev)
/* sysfs interface to enable/disbale charging from user space */
abx500_chargalg_sysfs_exit(di);
hrtimer_cancel(&di->safety_timer);
hrtimer_cancel(&di->maintenance_timer);
cancel_delayed_work_sync(&di->chargalg_periodic_work);
cancel_delayed_work_sync(&di->chargalg_wd_work);
cancel_work_sync(&di->chargalg_work);
/* Delete the work queue */
destroy_workqueue(di->chargalg_wq);
flush_scheduled_work();
power_supply_unregister(&di->chargalg_psy);
platform_set_drvdata(pdev, NULL);
@ -1873,8 +2071,9 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
}
}
/* get device struct */
/* get device struct and parent */
di->dev = &pdev->dev;
di->parent = dev_get_drvdata(pdev->dev.parent);
/* chargalg supply */
di->chargalg_psy.name = "abx500_chargalg";
@ -1888,15 +2087,13 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
abx500_chargalg_external_power_changed;
/* Initilialize safety timer */
init_timer(&di->safety_timer);
hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
di->safety_timer.function = abx500_chargalg_safety_timer_expired;
di->safety_timer.data = (unsigned long) di;
/* Initilialize maintenance timer */
init_timer(&di->maintenance_timer);
hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
di->maintenance_timer.function =
abx500_chargalg_maintenance_timer_expired;
di->maintenance_timer.data = (unsigned long) di;
/* Create a work queue for the chargalg */
di->chargalg_wq =
@ -1933,6 +2130,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
dev_err(di->dev, "failed to create sysfs entry\n");
goto free_psy;
}
di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH;
/* Run the charging algorithm */
queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
@ -1964,18 +2162,7 @@ static struct platform_driver abx500_chargalg_driver = {
},
};
static int __init abx500_chargalg_init(void)
{
return platform_driver_register(&abx500_chargalg_driver);
}
static void __exit abx500_chargalg_exit(void)
{
platform_driver_unregister(&abx500_chargalg_driver);
}
module_init(abx500_chargalg_init);
module_exit(abx500_chargalg_exit);
module_platform_driver(abx500_chargalg_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Johan Palsson, Karl Komierowski");

View File

@ -1485,13 +1485,12 @@ static int charger_manager_probe(struct platform_device *pdev)
/* Basic Values. Unspecified are Null or 0 */
cm->dev = &pdev->dev;
cm->desc = kzalloc(sizeof(struct charger_desc), GFP_KERNEL);
cm->desc = kmemdup(desc, sizeof(struct charger_desc), GFP_KERNEL);
if (!cm->desc) {
dev_err(&pdev->dev, "Cannot allocate memory.\n");
ret = -ENOMEM;
goto err_alloc_desc;
}
memcpy(cm->desc, desc, sizeof(struct charger_desc));
cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
/*

View File

@ -505,7 +505,7 @@ static int da9030_battery_probe(struct platform_device *pdev)
pdata->charge_millivolt > 4350)
return -EINVAL;
charger = kzalloc(sizeof(*charger), GFP_KERNEL);
charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
if (charger == NULL)
return -ENOMEM;
@ -557,8 +557,6 @@ err_notifier:
cancel_delayed_work(&charger->work);
err_charger_init:
kfree(charger);
return ret;
}
@ -575,8 +573,6 @@ static int da9030_battery_remove(struct platform_device *dev)
da9030_set_charge(charger, 0);
power_supply_unregister(&charger->psy);
kfree(charger);
return 0;
}

View File

@ -594,7 +594,8 @@ static s32 da9052_bat_probe(struct platform_device *pdev)
int ret;
int i;
bat = kzalloc(sizeof(struct da9052_battery), GFP_KERNEL);
bat = devm_kzalloc(&pdev->dev, sizeof(struct da9052_battery),
GFP_KERNEL);
if (!bat)
return -ENOMEM;
@ -635,7 +636,6 @@ err:
while (--i >= 0)
da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
kfree(bat);
return ret;
}
static int da9052_bat_remove(struct platform_device *pdev)
@ -647,7 +647,6 @@ static int da9052_bat_remove(struct platform_device *pdev)
da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
power_supply_unregister(&bat->psy);
kfree(bat);
return 0;
}

View File

@ -512,7 +512,7 @@ static int ds2760_battery_probe(struct platform_device *pdev)
int retval = 0;
struct ds2760_device_info *di;
di = kzalloc(sizeof(*di), GFP_KERNEL);
di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
if (!di) {
retval = -ENOMEM;
goto di_alloc_failed;
@ -576,7 +576,6 @@ static int ds2760_battery_probe(struct platform_device *pdev)
workqueue_failed:
power_supply_unregister(&di->bat);
batt_failed:
kfree(di);
di_alloc_failed:
success:
return retval;
@ -590,7 +589,6 @@ static int ds2760_battery_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&di->set_charged_work);
destroy_workqueue(di->monitor_wqueue);
power_supply_unregister(&di->bat);
kfree(di);
return 0;
}

View File

@ -760,7 +760,7 @@ static int ds2780_battery_probe(struct platform_device *pdev)
int ret = 0;
struct ds2780_device_info *dev_info;
dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
if (!dev_info) {
ret = -ENOMEM;
goto fail;
@ -779,7 +779,7 @@ static int ds2780_battery_probe(struct platform_device *pdev)
ret = power_supply_register(&pdev->dev, &dev_info->bat);
if (ret) {
dev_err(dev_info->dev, "failed to register battery\n");
goto fail_free_info;
goto fail;
}
ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2780_attr_group);
@ -813,8 +813,6 @@ fail_remove_group:
sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group);
fail_unregister:
power_supply_unregister(&dev_info->bat);
fail_free_info:
kfree(dev_info);
fail:
return ret;
}
@ -828,7 +826,6 @@ static int ds2780_battery_remove(struct platform_device *pdev)
power_supply_unregister(&dev_info->bat);
kfree(dev_info);
return 0;
}

View File

@ -332,32 +332,32 @@ static int ds278x_battery_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int ds278x_suspend(struct i2c_client *client,
pm_message_t state)
static int ds278x_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds278x_info *info = i2c_get_clientdata(client);
cancel_delayed_work(&info->bat_work);
return 0;
}
static int ds278x_resume(struct i2c_client *client)
static int ds278x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds278x_info *info = i2c_get_clientdata(client);
schedule_delayed_work(&info->bat_work, DS278x_DELAY);
return 0;
}
static SIMPLE_DEV_PM_OPS(ds278x_battery_pm_ops, ds278x_suspend, ds278x_resume);
#define DS278X_BATTERY_PM_OPS (&ds278x_battery_pm_ops)
#else
#define ds278x_suspend NULL
#define ds278x_resume NULL
#endif /* CONFIG_PM */
#define DS278X_BATTERY_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
enum ds278x_num_id {
DS2782 = 0,
@ -460,11 +460,10 @@ MODULE_DEVICE_TABLE(i2c, ds278x_id);
static struct i2c_driver ds278x_battery_driver = {
.driver = {
.name = "ds2782-battery",
.pm = DS278X_BATTERY_PM_OPS,
},
.probe = ds278x_battery_probe,
.remove = ds278x_battery_remove,
.suspend = ds278x_suspend,
.resume = ds278x_resume,
.id_table = ds278x_id,
};
module_i2c_driver(ds278x_battery_driver);

View File

@ -178,7 +178,7 @@ static int goldfish_battery_probe(struct platform_device *pdev)
return -ENODEV;
}
data->reg_base = devm_ioremap(&pdev->dev, r->start, r->end - r->start + 1);
data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
if (data->reg_base == NULL) {
dev_err(&pdev->dev, "unable to remap MMIO\n");
return -ENOMEM;

View File

@ -86,7 +86,8 @@ static int gpio_charger_probe(struct platform_device *pdev)
return -EINVAL;
}
gpio_charger = kzalloc(sizeof(*gpio_charger), GFP_KERNEL);
gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),
GFP_KERNEL);
if (!gpio_charger) {
dev_err(&pdev->dev, "Failed to alloc driver structure\n");
return -ENOMEM;
@ -140,7 +141,6 @@ static int gpio_charger_probe(struct platform_device *pdev)
err_gpio_free:
gpio_free(pdata->gpio);
err_free:
kfree(gpio_charger);
return ret;
}
@ -156,7 +156,6 @@ static int gpio_charger_remove(struct platform_device *pdev)
gpio_free(gpio_charger->pdata->gpio);
platform_set_drvdata(pdev, NULL);
kfree(gpio_charger);
return 0;
}

View File

@ -411,7 +411,7 @@ static int isp1704_charger_probe(struct platform_device *pdev)
struct isp1704_charger *isp;
int ret = -ENODEV;
isp = kzalloc(sizeof *isp, GFP_KERNEL);
isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
if (!isp)
return -ENOMEM;
@ -477,8 +477,6 @@ fail1:
isp1704_charger_set_power(isp, 0);
usb_put_phy(isp->phy);
fail0:
kfree(isp);
dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
return ret;
@ -492,7 +490,6 @@ static int isp1704_charger_remove(struct platform_device *pdev)
power_supply_unregister(&isp->psy);
usb_put_phy(isp->phy);
isp1704_charger_set_power(isp, 0);
kfree(isp);
return 0;
}

View File

@ -49,7 +49,6 @@
#define LP8788_CHG_START 0x11
#define LP8788_CHG_END 0x1C
#define LP8788_BUF_SIZE 40
#define LP8788_ISEL_MAX 23
#define LP8788_ISEL_STEP 50
#define LP8788_VTERM_MIN 4100
@ -633,7 +632,7 @@ static ssize_t lp8788_show_charger_status(struct device *dev,
lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
return scnprintf(buf, LP8788_BUF_SIZE, "%s\n", desc[state]);
return scnprintf(buf, PAGE_SIZE, "%s\n", desc[state]);
}
static ssize_t lp8788_show_eoc_time(struct device *dev,
@ -647,7 +646,7 @@ static ssize_t lp8788_show_eoc_time(struct device *dev,
lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
val = (val & LP8788_CHG_EOC_TIME_M) >> LP8788_CHG_EOC_TIME_S;
return scnprintf(buf, LP8788_BUF_SIZE, "End Of Charge Time: %s\n",
return scnprintf(buf, PAGE_SIZE, "End Of Charge Time: %s\n",
stime[val]);
}
@ -667,8 +666,7 @@ static ssize_t lp8788_show_eoc_level(struct device *dev,
val = (val & LP8788_CHG_EOC_LEVEL_M) >> LP8788_CHG_EOC_LEVEL_S;
level = mode ? abs_level[val] : relative_level[val];
return scnprintf(buf, LP8788_BUF_SIZE, "End Of Charge Level: %s\n",
level);
return scnprintf(buf, PAGE_SIZE, "End Of Charge Level: %s\n", level);
}
static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL);

View File

@ -246,31 +246,34 @@ static int max17040_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int max17040_suspend(struct i2c_client *client,
pm_message_t state)
static int max17040_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max17040_chip *chip = i2c_get_clientdata(client);
cancel_delayed_work(&chip->work);
return 0;
}
static int max17040_resume(struct i2c_client *client)
static int max17040_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max17040_chip *chip = i2c_get_clientdata(client);
schedule_delayed_work(&chip->work, MAX17040_DELAY);
return 0;
}
static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
#define MAX17040_PM_OPS (&max17040_pm_ops)
#else
#define max17040_suspend NULL
#define max17040_resume NULL
#define MAX17040_PM_OPS NULL
#endif /* CONFIG_PM */
#endif /* CONFIG_PM_SLEEP */
static const struct i2c_device_id max17040_id[] = {
{ "max17040", 0 },
@ -281,11 +284,10 @@ MODULE_DEVICE_TABLE(i2c, max17040_id);
static struct i2c_driver max17040_i2c_driver = {
.driver = {
.name = "max17040",
.pm = MAX17040_PM_OPS,
},
.probe = max17040_probe,
.remove = max17040_remove,
.suspend = max17040_suspend,
.resume = max17040_resume,
.id_table = max17040_id,
};
module_i2c_driver(max17040_i2c_driver);

View File

@ -189,7 +189,7 @@ static int max8903_probe(struct platform_device *pdev)
int ta_in = 0;
int usb_in = 0;
data = kzalloc(sizeof(struct max8903_data), GFP_KERNEL);
data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
if (data == NULL) {
dev_err(dev, "Cannot allocate memory.\n");
return -ENOMEM;
@ -341,7 +341,6 @@ err_dc_irq:
err_psy:
power_supply_unregister(&data->psy);
err:
kfree(data);
return ret;
}
@ -359,7 +358,6 @@ static int max8903_remove(struct platform_device *pdev)
if (pdata->dc_valid)
free_irq(gpio_to_irq(pdata->dok), data);
power_supply_unregister(&data->psy);
kfree(data);
}
return 0;

View File

@ -489,7 +489,8 @@ static int max8925_power_probe(struct platform_device *pdev)
return -EINVAL;
}
info = kzalloc(sizeof(struct max8925_power_info), GFP_KERNEL);
info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
info->chip = chip;
@ -546,7 +547,6 @@ out_battery:
out_usb:
power_supply_unregister(&info->ac);
out:
kfree(info);
return ret;
}
@ -559,7 +559,6 @@ static int max8925_power_remove(struct platform_device *pdev)
power_supply_unregister(&info->usb);
power_supply_unregister(&info->battery);
max8925_deinit_charger(info);
kfree(info);
}
return 0;
}

View File

@ -138,7 +138,8 @@ static int max8997_battery_probe(struct platform_device *pdev)
return ret;
}
charger = kzalloc(sizeof(struct charger_data), GFP_KERNEL);
charger = devm_kzalloc(&pdev->dev, sizeof(struct charger_data),
GFP_KERNEL);
if (charger == NULL) {
dev_err(&pdev->dev, "Cannot allocate memory.\n");
return -ENOMEM;
@ -158,13 +159,10 @@ static int max8997_battery_probe(struct platform_device *pdev)
ret = power_supply_register(&pdev->dev, &charger->battery);
if (ret) {
dev_err(&pdev->dev, "failed: power supply register\n");
goto err;
return ret;
}
return 0;
err:
kfree(charger);
return ret;
}
static int max8997_battery_remove(struct platform_device *pdev)
@ -172,7 +170,6 @@ static int max8997_battery_remove(struct platform_device *pdev)
struct charger_data *charger = platform_get_drvdata(pdev);
power_supply_unregister(&charger->battery);
kfree(charger);
return 0;
}

View File

@ -88,7 +88,8 @@ static int max8998_battery_probe(struct platform_device *pdev)
return -ENODEV;
}
max8998 = kzalloc(sizeof(struct max8998_battery_data), GFP_KERNEL);
max8998 = devm_kzalloc(&pdev->dev, sizeof(struct max8998_battery_data),
GFP_KERNEL);
if (!max8998)
return -ENOMEM;
@ -174,7 +175,6 @@ static int max8998_battery_probe(struct platform_device *pdev)
return 0;
err:
kfree(max8998);
return ret;
}
@ -183,7 +183,6 @@ static int max8998_battery_remove(struct platform_device *pdev)
struct max8998_battery_data *max8998 = platform_get_drvdata(pdev);
power_supply_unregister(&max8998->battery);
kfree(max8998);
return 0;
}

View File

@ -373,7 +373,7 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
int i;
u8 mbcs1;
mbc = kzalloc(sizeof(*mbc), GFP_KERNEL);
mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL);
if (!mbc)
return -ENOMEM;
@ -413,7 +413,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
ret = power_supply_register(&pdev->dev, &mbc->adapter);
if (ret) {
dev_err(mbc->pcf->dev, "failed to register adapter\n");
kfree(mbc);
return ret;
}
@ -421,7 +420,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
if (ret) {
dev_err(mbc->pcf->dev, "failed to register usb\n");
power_supply_unregister(&mbc->adapter);
kfree(mbc);
return ret;
}
@ -430,7 +428,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
dev_err(mbc->pcf->dev, "failed to register ac\n");
power_supply_unregister(&mbc->adapter);
power_supply_unregister(&mbc->usb);
kfree(mbc);
return ret;
}
@ -461,8 +458,6 @@ static int pcf50633_mbc_remove(struct platform_device *pdev)
power_supply_unregister(&mbc->adapter);
power_supply_unregister(&mbc->ac);
kfree(mbc);
return 0;
}

View File

@ -16,24 +16,25 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/kobject.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
#include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/pm2301_charger.h>
#include <linux/gpio.h>
#include <linux/pm_runtime.h>
#include <linux/pm.h>
#include "pm2301_charger.h"
#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
struct pm2xxx_charger, ac_chg)
#define SLEEP_MIN 50
#define SLEEP_MAX 100
#define PM2XXX_AUTOSUSPEND_DELAY 500
static int pm2xxx_interrupt_registers[] = {
PM2XXX_REG_INT1,
@ -113,33 +114,24 @@ static const struct i2c_device_id pm2xxx_ident[] = {
static void set_lpn_pin(struct pm2xxx_charger *pm2)
{
if (pm2->ac.charger_connected)
return;
gpio_set_value(pm2->lpn_pin, 1);
return;
if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) {
gpio_set_value(pm2->lpn_pin, 1);
usleep_range(SLEEP_MIN, SLEEP_MAX);
}
}
static void clear_lpn_pin(struct pm2xxx_charger *pm2)
{
if (pm2->ac.charger_connected)
return;
gpio_set_value(pm2->lpn_pin, 0);
return;
if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin))
gpio_set_value(pm2->lpn_pin, 0);
}
static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
{
int ret;
/*
* When AC adaptor is unplugged, the host
* must put LPN high to be able to
* communicate by I2C with PM2301
* and receive I2C "acknowledge" from PM2301.
*/
mutex_lock(&pm2->lock);
set_lpn_pin(pm2);
/* wake up the device */
pm_runtime_get_sync(pm2->dev);
ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
1, val);
@ -147,8 +139,8 @@ static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
dev_err(pm2->dev, "Error reading register at 0x%x\n", reg);
else
ret = 0;
clear_lpn_pin(pm2);
mutex_unlock(&pm2->lock);
pm_runtime_put_sync(pm2->dev);
return ret;
}
@ -156,14 +148,9 @@ static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
{
int ret;
/*
* When AC adaptor is unplugged, the host
* must put LPN high to be able to
* communicate by I2C with PM2301
* and receive I2C "acknowledge" from PM2301.
*/
mutex_lock(&pm2->lock);
set_lpn_pin(pm2);
/* wake up the device */
pm_runtime_get_sync(pm2->dev);
ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
1, &val);
@ -171,8 +158,8 @@ static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
dev_err(pm2->dev, "Error writing register at 0x%x\n", reg);
else
ret = 0;
clear_lpn_pin(pm2);
mutex_unlock(&pm2->lock);
pm_runtime_put_sync(pm2->dev);
return ret;
}
@ -192,11 +179,22 @@ static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2)
{
int ret;
/* Disable SW EOC ctrl */
ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, PM2XXX_SWCTRL_HW);
if (ret < 0) {
dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
return ret;
}
/* Disable charging */
ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
(PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS));
if (ret < 0) {
dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
return ret;
}
return ret;
return 0;
}
static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
@ -216,21 +214,14 @@ int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val)
{
int ret = 0;
dev_err(pm2->dev, "Overvoltage detected\n");
pm2->flags.ovv = true;
power_supply_changed(&pm2->ac_chg.psy);
pm2->failure_input_ovv++;
if (pm2->failure_input_ovv < 4) {
ret = pm2xxx_charging_enable_mngt(pm2);
goto out;
} else {
pm2->failure_input_ovv = 0;
dev_err(pm2->dev, "Overvoltage detected\n");
pm2->flags.ovv = true;
power_supply_changed(&pm2->ac_chg.psy);
}
/* Schedule a new HW failure check */
queue_delayed_work(pm2->charger_wq, &pm2->check_hw_failure_work, 0);
out:
return ret;
return 0;
}
static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
@ -245,13 +236,29 @@ static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val)
{
int ret;
switch (val) {
case PM2XXX_INT1_ITVBATLOWR:
dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n");
/* Enable SW EOC ctrl */
ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
PM2XXX_SWCTRL_SW);
if (ret < 0) {
dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
return ret;
}
break;
case PM2XXX_INT1_ITVBATLOWF:
dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n");
/* Disable SW EOC ctrl */
ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
PM2XXX_SWCTRL_HW);
if (ret < 0) {
dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
return ret;
}
break;
default:
@ -322,16 +329,27 @@ static int pm2_int_reg0(void *pm2_data, int val)
struct pm2xxx_charger *pm2 = pm2_data;
int ret = 0;
if (val & (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) {
ret = pm2xxx_charger_vbat_lsig_mngt(pm2, val &
(PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF));
if (val & PM2XXX_INT1_ITVBATLOWR) {
ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
PM2XXX_INT1_ITVBATLOWR);
if (ret < 0)
goto out;
}
if (val & PM2XXX_INT1_ITVBATLOWF) {
ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
PM2XXX_INT1_ITVBATLOWF);
if (ret < 0)
goto out;
}
if (val & PM2XXX_INT1_ITVBATDISCONNECT) {
ret = pm2xxx_charger_bat_disc_mngt(pm2,
PM2XXX_INT1_ITVBATDISCONNECT);
if (ret < 0)
goto out;
}
out:
return ret;
}
@ -447,7 +465,6 @@ static int pm2_int_reg5(void *pm2_data, int val)
struct pm2xxx_charger *pm2 = pm2_data;
int ret = 0;
if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n");
}
@ -468,14 +485,22 @@ static irqreturn_t pm2xxx_irq_int(int irq, void *data)
struct pm2xxx_interrupts *interrupt = pm2->pm2_int;
int i;
for (i = 0; i < PM2XXX_NUM_INT_REG; i++) {
pm2xxx_reg_read(pm2,
/* wake up the device */
pm_runtime_get_sync(pm2->dev);
do {
for (i = 0; i < PM2XXX_NUM_INT_REG; i++) {
pm2xxx_reg_read(pm2,
pm2xxx_interrupt_registers[i],
&(interrupt->reg[i]));
if (interrupt->reg[i] > 0)
interrupt->handler[i](pm2, interrupt->reg[i]);
}
if (interrupt->reg[i] > 0)
interrupt->handler[i](pm2, interrupt->reg[i]);
}
} while (gpio_get_value(pm2->pdata->gpio_irq_number) == 0);
pm_runtime_mark_last_busy(pm2->dev);
pm_runtime_put_autosuspend(pm2->dev);
return IRQ_HANDLED;
}
@ -592,6 +617,8 @@ static int pm2xxx_charger_ac_get_property(struct power_supply *psy,
val->intval = POWER_SUPPLY_HEALTH_DEAD;
else if (pm2->flags.main_thermal_prot)
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
else if (pm2->flags.ovv)
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
else
val->intval = POWER_SUPPLY_HEALTH_GOOD;
break;
@ -674,10 +701,6 @@ static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG,
PM2XXX_VBAT_LOW_MONITORING_ENA);
/* Disable LED */
ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
PM2XXX_LED_SELECT_DIS);
return ret;
}
@ -822,10 +845,54 @@ static void pm2xxx_charger_ac_work(struct work_struct *work)
sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
};
static void pm2xxx_charger_check_hw_failure_work(struct work_struct *work)
{
u8 reg_value;
struct pm2xxx_charger *pm2 = container_of(work,
struct pm2xxx_charger, check_hw_failure_work.work);
if (pm2->flags.ovv) {
pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &reg_value);
if (!(reg_value & (PM2XXX_INT4_S_ITVPWR1OVV |
PM2XXX_INT4_S_ITVPWR2OVV))) {
pm2->flags.ovv = false;
power_supply_changed(&pm2->ac_chg.psy);
}
}
/* If we still have a failure, schedule a new check */
if (pm2->flags.ovv) {
queue_delayed_work(pm2->charger_wq,
&pm2->check_hw_failure_work, round_jiffies(HZ));
}
}
static void pm2xxx_charger_check_main_thermal_prot_work(
struct work_struct *work)
{
};
int ret;
u8 val;
struct pm2xxx_charger *pm2 = container_of(work, struct pm2xxx_charger,
check_main_thermal_prot_work);
/* Check if die temp warning is still active */
ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT5, &val);
if (ret < 0) {
dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
return;
}
if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGRISE
| PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE))
pm2->flags.main_thermal_prot = true;
else if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGFALL
| PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL))
pm2->flags.main_thermal_prot = false;
power_supply_changed(&pm2->ac_chg.psy);
}
static struct pm2xxx_interrupts pm2xxx_int = {
.handler[0] = pm2_int_reg0,
@ -840,24 +907,105 @@ static struct pm2xxx_irq pm2xxx_charger_irq[] = {
{"PM2XXX_IRQ_INT", pm2xxx_irq_int},
};
static int pm2xxx_wall_charger_resume(struct i2c_client *i2c_client)
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int pm2xxx_wall_charger_resume(struct device *dev)
{
struct i2c_client *i2c_client = to_i2c_client(dev);
struct pm2xxx_charger *pm2;
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
set_lpn_pin(pm2);
/* If we still have a HW failure, schedule a new check */
if (pm2->flags.ovv)
queue_delayed_work(pm2->charger_wq,
&pm2->check_hw_failure_work, 0);
return 0;
}
static int pm2xxx_wall_charger_suspend(struct i2c_client *i2c_client,
pm_message_t state)
static int pm2xxx_wall_charger_suspend(struct device *dev)
{
struct i2c_client *i2c_client = to_i2c_client(dev);
struct pm2xxx_charger *pm2;
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
clear_lpn_pin(pm2);
/* Cancel any pending HW failure check */
if (delayed_work_pending(&pm2->check_hw_failure_work))
cancel_delayed_work(&pm2->check_hw_failure_work);
flush_work(&pm2->ac_work);
flush_work(&pm2->check_main_thermal_prot_work);
return 0;
}
static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
#endif
#ifdef CONFIG_PM_RUNTIME
static int pm2xxx_runtime_suspend(struct device *dev)
{
struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
struct pm2xxx_charger *pm2;
int ret = 0;
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
if (!pm2) {
dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
ret = -EINVAL;
return ret;
}
clear_lpn_pin(pm2);
return ret;
}
static int pm2xxx_runtime_resume(struct device *dev)
{
struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
struct pm2xxx_charger *pm2;
int ret = 0;
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
if (!pm2) {
dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
ret = -EINVAL;
return ret;
}
if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0)
set_lpn_pin(pm2);
return ret;
}
#endif
static const struct dev_pm_ops pm2xxx_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm2xxx_wall_charger_suspend,
pm2xxx_wall_charger_resume)
SET_RUNTIME_PM_OPS(pm2xxx_runtime_suspend, pm2xxx_runtime_resume, NULL)
};
#define PM2XXX_PM_OPS (&pm2xxx_pm_ops)
#else
#define PM2XXX_PM_OPS NULL
#endif
static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data;
struct pm2xxx_charger *pm2;
int ret = 0;
u8 val;
int i;
pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL);
if (!pm2) {
@ -867,7 +1015,6 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
/* get parent data */
pm2->dev = &i2c_client->dev;
pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
pm2->pm2_int = &pm2xxx_int;
@ -889,14 +1036,6 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
pm2->bat = pl_data->battery;
/*get lpn GPIO from platform data*/
if (!pm2->pdata->lpn_gpio) {
dev_err(pm2->dev, "no lpn gpio data supplied\n");
ret = -EINVAL;
goto free_device_info;
}
pm2->lpn_pin = pm2->pdata->lpn_gpio;
if (!i2c_check_functionality(i2c_client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
@ -945,6 +1084,10 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
INIT_WORK(&pm2->check_main_thermal_prot_work,
pm2xxx_charger_check_main_thermal_prot_work);
/* Init work for HW failure check */
INIT_DEFERRABLE_WORK(&pm2->check_hw_failure_work,
pm2xxx_charger_check_hw_failure_work);
/*
* VDD ADC supply needs to be enabled from this driver when there
* is a charger connected to avoid erroneous BTEMP_HIGH/LOW
@ -965,40 +1108,72 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
}
/* Register interrupts */
ret = request_threaded_irq(pm2->pdata->irq_number, NULL,
ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number),
NULL,
pm2xxx_charger_irq[0].isr,
pm2->pdata->irq_type,
pm2xxx_charger_irq[0].name, pm2);
if (ret != 0) {
dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n",
pm2xxx_charger_irq[0].name, pm2->pdata->irq_number, ret);
pm2xxx_charger_irq[0].name,
gpio_to_irq(pm2->pdata->gpio_irq_number), ret);
goto unregister_pm2xxx_charger;
}
/*Initialize lock*/
ret = pm_runtime_set_active(pm2->dev);
if (ret)
dev_err(pm2->dev, "set active Error\n");
pm_runtime_enable(pm2->dev);
pm_runtime_set_autosuspend_delay(pm2->dev, PM2XXX_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(pm2->dev);
pm_runtime_resume(pm2->dev);
/* pm interrupt can wake up system */
ret = enable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
if (ret) {
dev_err(pm2->dev, "failed to set irq wake\n");
goto unregister_pm2xxx_interrupt;
}
mutex_init(&pm2->lock);
/*
* Charger detection mechanism requires pulling up the LPN pin
* while i2c communication if Charger is not connected
* LPN pin of PM2301 is GPIO60 of AB9540
*/
ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio");
if (ret < 0) {
dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n");
goto unregister_pm2xxx_charger;
}
ret = gpio_direction_output(pm2->lpn_pin, 0);
if (ret < 0) {
dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n");
goto free_gpio;
if (gpio_is_valid(pm2->pdata->lpn_gpio)) {
/* get lpn GPIO from platform data */
pm2->lpn_pin = pm2->pdata->lpn_gpio;
/*
* Charger detection mechanism requires pulling up the LPN pin
* while i2c communication if Charger is not connected
* LPN pin of PM2301 is GPIO60 of AB9540
*/
ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio");
if (ret < 0) {
dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n");
goto disable_pm2_irq_wake;
}
ret = gpio_direction_output(pm2->lpn_pin, 0);
if (ret < 0) {
dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n");
goto free_gpio;
}
set_lpn_pin(pm2);
}
/* read interrupt registers */
for (i = 0; i < PM2XXX_NUM_INT_REG; i++)
pm2xxx_reg_read(pm2,
pm2xxx_interrupt_registers[i],
&val);
ret = pm2xxx_charger_detection(pm2, &val);
if ((ret == 0) && val) {
pm2->ac.charger_connected = 1;
ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON,
AB8500_MAIN_CH_DET);
pm2->ac_conn = true;
power_supply_changed(&pm2->ac_chg.psy);
sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
@ -1007,7 +1182,13 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
return 0;
free_gpio:
gpio_free(pm2->lpn_pin);
if (gpio_is_valid(pm2->lpn_pin))
gpio_free(pm2->lpn_pin);
disable_pm2_irq_wake:
disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
unregister_pm2xxx_interrupt:
/* disable interrupt */
free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
unregister_pm2xxx_charger:
/* unregister power supply */
power_supply_unregister(&pm2->ac_chg.psy);
@ -1018,18 +1199,24 @@ free_charger_wq:
destroy_workqueue(pm2->charger_wq);
free_device_info:
kfree(pm2);
return ret;
}
static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
{
struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client);
/* Disable pm_runtime */
pm_runtime_disable(pm2->dev);
/* Disable AC charging */
pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0);
/* Disable wake by pm interrupt */
disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
/* Disable interrupts */
free_irq(pm2->pdata->irq_number, pm2);
free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
/* Delete the work queue */
destroy_workqueue(pm2->charger_wq);
@ -1041,8 +1228,8 @@ static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
power_supply_unregister(&pm2->ac_chg.psy);
/*Free GPIO60*/
gpio_free(pm2->lpn_pin);
if (gpio_is_valid(pm2->lpn_pin))
gpio_free(pm2->lpn_pin);
kfree(pm2);
@ -1058,12 +1245,11 @@ MODULE_DEVICE_TABLE(i2c, pm2xxx_id);
static struct i2c_driver pm2xxx_charger_driver = {
.probe = pm2xxx_wall_charger_probe,
.remove = __devexit_p(pm2xxx_wall_charger_remove),
.suspend = pm2xxx_wall_charger_suspend,
.resume = pm2xxx_wall_charger_resume,
.remove = pm2xxx_wall_charger_remove,
.driver = {
.name = "pm2xxx-wall_charger",
.owner = THIS_MODULE,
.pm = PM2XXX_PM_OPS,
},
.id_table = pm2xxx_id,
};
@ -1078,11 +1264,10 @@ static void __exit pm2xxx_charger_exit(void)
i2c_del_driver(&pm2xxx_charger_driver);
}
subsys_initcall_sync(pm2xxx_charger_init);
device_initcall_sync(pm2xxx_charger_init);
module_exit(pm2xxx_charger_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
MODULE_ALIAS("platform:pm2xxx-charger");
MODULE_DESCRIPTION("PM2xxx charger management driver");

View File

@ -9,27 +9,6 @@
#ifndef PM2301_CHARGER_H
#define PM2301_CHARGER_H
#define MAIN_WDOG_ENA 0x01
#define MAIN_WDOG_KICK 0x02
#define MAIN_WDOG_DIS 0x00
#define CHARG_WD_KICK 0x01
#define MAIN_CH_ENA 0x01
#define MAIN_CH_NO_OVERSHOOT_ENA_N 0x02
#define MAIN_CH_DET 0x01
#define MAIN_CH_CV_ON 0x04
#define OTP_ENABLE_WD 0x01
#define MAIN_CH_INPUT_CURR_SHIFT 4
#define LED_INDICATOR_PWM_ENA 0x01
#define LED_INDICATOR_PWM_DIS 0x00
#define LED_IND_CUR_5MA 0x04
#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
/* HW failure constants */
#define MAIN_CH_TH_PROT 0x02
#define MAIN_CH_NOK 0x01
/* Watchdog timeout constant */
#define WD_TIMER 0x30 /* 4min */
#define WD_KICK_INTERVAL (30 * HZ)
@ -495,7 +474,6 @@ struct pm2xxx_charger {
int failure_input_ovv;
unsigned int lpn_pin;
struct pm2xxx_interrupts *pm2_int;
struct ab8500_gpadc *gpadc;
struct regulator *regu;
struct pm2xxx_bm_data *bat;
struct mutex lock;
@ -506,6 +484,7 @@ struct pm2xxx_charger {
struct delayed_work check_vbat_work;
struct work_struct ac_work;
struct work_struct check_main_thermal_prot_work;
struct delayed_work check_hw_failure_work;
struct ux500_charger ac_chg;
struct pm2xxx_charger_event_flags flags;
};

View File

@ -26,17 +26,42 @@ EXPORT_SYMBOL_GPL(power_supply_class);
static struct device_type power_supply_dev_type;
static bool __power_supply_is_supplied_by(struct power_supply *supplier,
struct power_supply *supply)
{
int i;
if (!supply->supplied_from && !supplier->supplied_to)
return false;
/* Support both supplied_to and supplied_from modes */
if (supply->supplied_from) {
if (!supplier->name)
return false;
for (i = 0; i < supply->num_supplies; i++)
if (!strcmp(supplier->name, supply->supplied_from[i]))
return true;
} else {
if (!supply->name)
return false;
for (i = 0; i < supplier->num_supplicants; i++)
if (!strcmp(supplier->supplied_to[i], supply->name))
return true;
}
return false;
}
static int __power_supply_changed_work(struct device *dev, void *data)
{
struct power_supply *psy = (struct power_supply *)data;
struct power_supply *pst = dev_get_drvdata(dev);
int i;
for (i = 0; i < psy->num_supplicants; i++)
if (!strcmp(psy->supplied_to[i], pst->name)) {
if (pst->external_power_changed)
pst->external_power_changed(pst);
}
if (__power_supply_is_supplied_by(psy, pst)) {
if (pst->external_power_changed)
pst->external_power_changed(pst);
}
return 0;
}
@ -63,22 +88,151 @@ void power_supply_changed(struct power_supply *psy)
}
EXPORT_SYMBOL_GPL(power_supply_changed);
#ifdef CONFIG_OF
#include <linux/of.h>
static int __power_supply_populate_supplied_from(struct device *dev,
void *data)
{
struct power_supply *psy = (struct power_supply *)data;
struct power_supply *epsy = dev_get_drvdata(dev);
struct device_node *np;
int i = 0;
do {
np = of_parse_phandle(psy->of_node, "power-supplies", i++);
if (!np)
continue;
if (np == epsy->of_node) {
dev_info(psy->dev, "%s: Found supply : %s\n",
psy->name, epsy->name);
psy->supplied_from[i-1] = (char *)epsy->name;
psy->num_supplies++;
break;
}
} while (np);
return 0;
}
static int power_supply_populate_supplied_from(struct power_supply *psy)
{
int error;
error = class_for_each_device(power_supply_class, NULL, psy,
__power_supply_populate_supplied_from);
dev_dbg(psy->dev, "%s %d\n", __func__, error);
return error;
}
static int __power_supply_find_supply_from_node(struct device *dev,
void *data)
{
struct device_node *np = (struct device_node *)data;
struct power_supply *epsy = dev_get_drvdata(dev);
/* return error breaks out of class_for_each_device loop */
if (epsy->of_node == np)
return -EINVAL;
return 0;
}
static int power_supply_find_supply_from_node(struct device_node *supply_node)
{
int error;
struct device *dev;
struct class_dev_iter iter;
/*
* Use iterator to see if any other device is registered.
* This is required since class_for_each_device returns 0
* if there are no devices registered.
*/
class_dev_iter_init(&iter, power_supply_class, NULL, NULL);
dev = class_dev_iter_next(&iter);
if (!dev)
return -EPROBE_DEFER;
/*
* We have to treat the return value as inverted, because if
* we return error on not found, then it won't continue looking.
* So we trick it by returning error on success to stop looking
* once the matching device is found.
*/
error = class_for_each_device(power_supply_class, NULL, supply_node,
__power_supply_find_supply_from_node);
return error ? 0 : -EPROBE_DEFER;
}
static int power_supply_check_supplies(struct power_supply *psy)
{
struct device_node *np;
int cnt = 0;
/* If there is already a list honor it */
if (psy->supplied_from && psy->num_supplies > 0)
return 0;
/* No device node found, nothing to do */
if (!psy->of_node)
return 0;
do {
int ret;
np = of_parse_phandle(psy->of_node, "power-supplies", cnt++);
if (!np)
continue;
ret = power_supply_find_supply_from_node(np);
if (ret) {
dev_dbg(psy->dev, "Failed to find supply, defer!\n");
return -EPROBE_DEFER;
}
} while (np);
/* All supplies found, allocate char ** array for filling */
psy->supplied_from = devm_kzalloc(psy->dev, sizeof(psy->supplied_from),
GFP_KERNEL);
if (!psy->supplied_from) {
dev_err(psy->dev, "Couldn't allocate memory for supply list\n");
return -ENOMEM;
}
*psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * cnt,
GFP_KERNEL);
if (!*psy->supplied_from) {
dev_err(psy->dev, "Couldn't allocate memory for supply list\n");
return -ENOMEM;
}
return power_supply_populate_supplied_from(psy);
}
#else
static inline int power_supply_check_supplies(struct power_supply *psy)
{
return 0;
}
#endif
static int __power_supply_am_i_supplied(struct device *dev, void *data)
{
union power_supply_propval ret = {0,};
struct power_supply *psy = (struct power_supply *)data;
struct power_supply *epsy = dev_get_drvdata(dev);
int i;
for (i = 0; i < epsy->num_supplicants; i++) {
if (!strcmp(epsy->supplied_to[i], psy->name)) {
if (epsy->get_property(epsy,
POWER_SUPPLY_PROP_ONLINE, &ret))
continue;
if (__power_supply_is_supplied_by(epsy, psy))
if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) {
if (ret.intval)
return ret.intval;
}
}
return 0;
}
@ -336,6 +490,12 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
INIT_WORK(&psy->changed_work, power_supply_changed_work);
rc = power_supply_check_supplies(psy);
if (rc) {
dev_info(dev, "Not all required supplies found, defer probe\n");
goto check_supplies_failed;
}
rc = kobject_set_name(&dev->kobj, "%s", psy->name);
if (rc)
goto kobject_set_name_failed;
@ -368,6 +528,7 @@ register_thermal_failed:
device_del(dev);
kobject_set_name_failed:
device_add_failed:
check_supplies_failed:
put_device(dev);
success:
return rc;

View File

@ -30,3 +30,10 @@ config POWER_RESET_RESTART
Some boards don't actually have the ability to power off.
Instead they restart, and u-boot holds the SoC until the
user presses a key. u-boot then boots into Linux.
config POWER_RESET_VEXPRESS
bool
depends on POWER_RESET
help
Power off and reset support for the ARM Ltd. Versatile
Express boards.

View File

@ -1,3 +1,4 @@
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o

View File

@ -18,6 +18,8 @@
#include <linux/stat.h>
#include <linux/vexpress.h>
#include <asm/system_misc.h>
static void vexpress_reset_do(struct device *dev, const char *what)
{
int err = -ENOENT;
@ -39,14 +41,14 @@ static void vexpress_reset_do(struct device *dev, const char *what)
static struct device *vexpress_power_off_device;
void vexpress_power_off(void)
static void vexpress_power_off(void)
{
vexpress_reset_do(vexpress_power_off_device, "power off");
}
static struct device *vexpress_restart_device;
void vexpress_restart(char str, const char *cmd)
static void vexpress_restart(char str, const char *cmd)
{
vexpress_reset_do(vexpress_restart_device, "restart");
}
@ -103,14 +105,17 @@ static int vexpress_reset_probe(struct platform_device *pdev)
switch (func) {
case FUNC_SHUTDOWN:
vexpress_power_off_device = &pdev->dev;
pm_power_off = vexpress_power_off;
break;
case FUNC_RESET:
if (!vexpress_restart_device)
vexpress_restart_device = &pdev->dev;
arm_pm_restart = vexpress_restart;
device_create_file(&pdev->dev, &dev_attr_active);
break;
case FUNC_REBOOT:
vexpress_restart_device = &pdev->dev;
arm_pm_restart = vexpress_restart;
device_create_file(&pdev->dev, &dev_attr_active);
break;
};

View File

@ -119,7 +119,7 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di)
/* First check for temperature in first direct table */
if (raw < ARRAY_SIZE(rx51_temp_table1))
return rx51_temp_table1[raw] * 100;
return rx51_temp_table1[raw] * 10;
/* Binary search RAW value in second inverse table */
while (max - min > 1) {
@ -132,7 +132,7 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di)
break;
}
return (rx51_temp_table2_first - min) * 100;
return (rx51_temp_table2_first - min) * 10;
}
/*
@ -202,7 +202,7 @@ static int rx51_battery_probe(struct platform_device *pdev)
struct rx51_device_info *di;
int ret;
di = kzalloc(sizeof(*di), GFP_KERNEL);
di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
if (!di)
return -ENOMEM;
@ -217,7 +217,6 @@ static int rx51_battery_probe(struct platform_device *pdev)
ret = power_supply_register(di->dev, &di->bat);
if (ret) {
platform_set_drvdata(pdev, NULL);
kfree(di);
return ret;
}
@ -230,7 +229,6 @@ static int rx51_battery_remove(struct platform_device *pdev)
power_supply_unregister(&di->bat);
platform_set_drvdata(pdev, NULL);
kfree(di);
return 0;
}

View File

@ -145,14 +145,17 @@ static int s3c_adc_bat_get_property(struct power_supply *psy,
int new_level;
int full_volt;
const struct s3c_adc_bat_thresh *lut = bat->pdata->lut_noac;
unsigned int lut_size = bat->pdata->lut_noac_cnt;
const struct s3c_adc_bat_thresh *lut;
unsigned int lut_size;
if (!bat) {
dev_err(psy->dev, "no battery infos ?!\n");
return -EINVAL;
}
lut = bat->pdata->lut_noac;
lut_size = bat->pdata->lut_noac_cnt;
if (bat->volt_value < 0 || bat->cur_value < 0 ||
jiffies_to_msecs(jiffies - bat->timestamp) >
BAT_POLL_INTERVAL) {

View File

@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/power/sbs-battery.h>
@ -667,7 +668,6 @@ of_out:
return pdata;
}
#else
#define sbs_dt_ids NULL
static struct sbs_platform_data *sbs_of_populate_pdata(
struct i2c_client *client)
{
@ -820,10 +820,11 @@ static int sbs_remove(struct i2c_client *client)
return 0;
}
#if defined CONFIG_PM
static int sbs_suspend(struct i2c_client *client,
pm_message_t state)
#if defined CONFIG_PM_SLEEP
static int sbs_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct sbs_info *chip = i2c_get_clientdata(client);
s32 ret;
@ -838,11 +839,13 @@ static int sbs_suspend(struct i2c_client *client,
return 0;
}
static SIMPLE_DEV_PM_OPS(sbs_pm_ops, sbs_suspend, NULL);
#define SBS_PM_OPS (&sbs_pm_ops)
#else
#define sbs_suspend NULL
#define SBS_PM_OPS NULL
#endif
/* any smbus transaction will wake up sbs */
#define sbs_resume NULL
static const struct i2c_device_id sbs_id[] = {
{ "bq20z75", 0 },
@ -854,12 +857,11 @@ MODULE_DEVICE_TABLE(i2c, sbs_id);
static struct i2c_driver sbs_battery_driver = {
.probe = sbs_probe,
.remove = sbs_remove,
.suspend = sbs_suspend,
.resume = sbs_resume,
.id_table = sbs_id,
.driver = {
.name = "sbs-battery",
.of_match_table = sbs_dt_ids,
.of_match_table = of_match_ptr(sbs_dt_ids),
.pm = SBS_PM_OPS,
},
};
module_i2c_driver(sbs_battery_driver);

View File

@ -30,6 +30,8 @@ static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION;
static int battery_capacity = 50;
static int battery_voltage = 3300;
static bool module_initialized;
static int test_power_get_ac_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@ -185,6 +187,7 @@ static int __init test_power_init(void)
}
}
module_initialized = true;
return 0;
failed:
while (--i >= 0)
@ -209,6 +212,8 @@ static void __exit test_power_exit(void)
for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
power_supply_unregister(&test_power_supplies[i]);
module_initialized = false;
}
module_exit(test_power_exit);
@ -221,8 +226,8 @@ struct battery_property_map {
};
static struct battery_property_map map_ac_online[] = {
{ 0, "on" },
{ 1, "off" },
{ 0, "off" },
{ 1, "on" },
{ -1, NULL },
};
@ -295,10 +300,16 @@ static const char *map_get_key(struct battery_property_map *map, int value,
return def_key;
}
static inline void signal_power_supply_changed(struct power_supply *psy)
{
if (module_initialized)
power_supply_changed(psy);
}
static int param_set_ac_online(const char *key, const struct kernel_param *kp)
{
ac_online = map_get_value(map_ac_online, key, ac_online);
power_supply_changed(&test_power_supplies[0]);
signal_power_supply_changed(&test_power_supplies[0]);
return 0;
}
@ -311,7 +322,7 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp)
static int param_set_usb_online(const char *key, const struct kernel_param *kp)
{
usb_online = map_get_value(map_ac_online, key, usb_online);
power_supply_changed(&test_power_supplies[2]);
signal_power_supply_changed(&test_power_supplies[2]);
return 0;
}
@ -325,7 +336,7 @@ static int param_set_battery_status(const char *key,
const struct kernel_param *kp)
{
battery_status = map_get_value(map_status, key, battery_status);
power_supply_changed(&test_power_supplies[1]);
signal_power_supply_changed(&test_power_supplies[1]);
return 0;
}
@ -339,7 +350,7 @@ static int param_set_battery_health(const char *key,
const struct kernel_param *kp)
{
battery_health = map_get_value(map_health, key, battery_health);
power_supply_changed(&test_power_supplies[1]);
signal_power_supply_changed(&test_power_supplies[1]);
return 0;
}
@ -353,7 +364,7 @@ static int param_set_battery_present(const char *key,
const struct kernel_param *kp)
{
battery_present = map_get_value(map_present, key, battery_present);
power_supply_changed(&test_power_supplies[0]);
signal_power_supply_changed(&test_power_supplies[0]);
return 0;
}
@ -369,7 +380,7 @@ static int param_set_battery_technology(const char *key,
{
battery_technology = map_get_value(map_technology, key,
battery_technology);
power_supply_changed(&test_power_supplies[1]);
signal_power_supply_changed(&test_power_supplies[1]);
return 0;
}
@ -390,7 +401,7 @@ static int param_set_battery_capacity(const char *key,
return -EINVAL;
battery_capacity = tmp;
power_supply_changed(&test_power_supplies[1]);
signal_power_supply_changed(&test_power_supplies[1]);
return 0;
}
@ -405,7 +416,7 @@ static int param_set_battery_voltage(const char *key,
return -EINVAL;
battery_voltage = tmp;
power_supply_changed(&test_power_supplies[1]);
signal_power_supply_changed(&test_power_supplies[1]);
return 0;
}

View File

@ -0,0 +1,320 @@
/*
* Battery charger driver for TI's tps65090
*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/mfd/tps65090.h>
#define TPS65090_REG_INTR_STS 0x00
#define TPS65090_REG_CG_CTRL0 0x04
#define TPS65090_REG_CG_CTRL1 0x05
#define TPS65090_REG_CG_CTRL2 0x06
#define TPS65090_REG_CG_CTRL3 0x07
#define TPS65090_REG_CG_CTRL4 0x08
#define TPS65090_REG_CG_CTRL5 0x09
#define TPS65090_REG_CG_STATUS1 0x0a
#define TPS65090_REG_CG_STATUS2 0x0b
#define TPS65090_CHARGER_ENABLE BIT(0)
#define TPS65090_VACG BIT(1)
#define TPS65090_NOITERM BIT(5)
struct tps65090_charger {
struct device *dev;
int ac_online;
int prev_ac_online;
int irq;
struct power_supply ac;
struct tps65090_platform_data *pdata;
};
static enum power_supply_property tps65090_ac_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static int tps65090_low_chrg_current(struct tps65090_charger *charger)
{
int ret;
ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5,
TPS65090_NOITERM);
if (ret < 0) {
dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
__func__, TPS65090_REG_CG_CTRL5);
return ret;
}
return 0;
}
static int tps65090_enable_charging(struct tps65090_charger *charger,
uint8_t enable)
{
int ret;
uint8_t ctrl0 = 0;
ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0,
&ctrl0);
if (ret < 0) {
dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
__func__, TPS65090_REG_CG_CTRL0);
return ret;
}
ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL0,
(ctrl0 | TPS65090_CHARGER_ENABLE));
if (ret < 0) {
dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
__func__, TPS65090_REG_CG_CTRL0);
return ret;
}
return 0;
}
static int tps65090_config_charger(struct tps65090_charger *charger)
{
int ret;
if (charger->pdata->enable_low_current_chrg) {
ret = tps65090_low_chrg_current(charger);
if (ret < 0) {
dev_err(charger->dev,
"error configuring low charge current\n");
return ret;
}
}
return 0;
}
static int tps65090_ac_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct tps65090_charger *charger = container_of(psy,
struct tps65090_charger, ac);
if (psp == POWER_SUPPLY_PROP_ONLINE) {
val->intval = charger->ac_online;
charger->prev_ac_online = charger->ac_online;
return 0;
}
return -EINVAL;
}
static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
{
struct tps65090_charger *charger = dev_id;
int ret;
uint8_t status1 = 0;
uint8_t intrsts = 0;
ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_STATUS1,
&status1);
if (ret < 0) {
dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
__func__, TPS65090_REG_CG_STATUS1);
return IRQ_HANDLED;
}
msleep(75);
ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_STS,
&intrsts);
if (ret < 0) {
dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
__func__, TPS65090_REG_INTR_STS);
return IRQ_HANDLED;
}
if (intrsts & TPS65090_VACG) {
ret = tps65090_enable_charging(charger, 1);
if (ret < 0)
return IRQ_HANDLED;
charger->ac_online = 1;
} else {
charger->ac_online = 0;
}
if (charger->prev_ac_online != charger->ac_online)
power_supply_changed(&charger->ac);
return IRQ_HANDLED;
}
#if defined(CONFIG_OF)
#include <linux/of_device.h>
static struct tps65090_platform_data *
tps65090_parse_dt_charger_data(struct platform_device *pdev)
{
struct tps65090_platform_data *pdata;
struct device_node *np = pdev->dev.of_node;
unsigned int prop;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n");
return NULL;
}
prop = of_property_read_bool(np, "ti,enable-low-current-chrg");
pdata->enable_low_current_chrg = prop;
pdata->irq_base = -1;
return pdata;
}
#else
static struct tps65090_platform_data *
tps65090_parse_dt_charger_data(struct platform_device *pdev)
{
return NULL;
}
#endif
static int tps65090_charger_probe(struct platform_device *pdev)
{
struct tps65090_charger *cdata;
struct tps65090_platform_data *pdata;
uint8_t status1 = 0;
int ret;
int irq;
pdata = dev_get_platdata(pdev->dev.parent);
if (!pdata && pdev->dev.of_node)
pdata = tps65090_parse_dt_charger_data(pdev);
if (!pdata) {
dev_err(&pdev->dev, "%s():no platform data available\n",
__func__);
return -ENODEV;
}
cdata = devm_kzalloc(&pdev->dev, sizeof(*cdata), GFP_KERNEL);
if (!cdata) {
dev_err(&pdev->dev, "failed to allocate memory status\n");
return -ENOMEM;
}
dev_set_drvdata(&pdev->dev, cdata);
cdata->dev = &pdev->dev;
cdata->pdata = pdata;
cdata->ac.name = "tps65090-ac";
cdata->ac.type = POWER_SUPPLY_TYPE_MAINS;
cdata->ac.get_property = tps65090_ac_get_property;
cdata->ac.properties = tps65090_ac_props;
cdata->ac.num_properties = ARRAY_SIZE(tps65090_ac_props);
cdata->ac.supplied_to = pdata->supplied_to;
cdata->ac.num_supplicants = pdata->num_supplicants;
ret = power_supply_register(&pdev->dev, &cdata->ac);
if (ret) {
dev_err(&pdev->dev, "failed: power supply register\n");
return ret;
}
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_warn(&pdev->dev, "Unable to get charger irq = %d\n", irq);
ret = irq;
goto fail_unregister_supply;
}
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_free_irq;
}
ret = tps65090_config_charger(cdata);
if (ret < 0) {
dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
goto fail_free_irq;
}
/* Check for charger presence */
ret = tps65090_read(cdata->dev->parent, TPS65090_REG_CG_STATUS1,
&status1);
if (ret < 0) {
dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__,
TPS65090_REG_CG_STATUS1);
goto fail_free_irq;
}
if (status1 != 0) {
ret = tps65090_enable_charging(cdata, 1);
if (ret < 0) {
dev_err(cdata->dev, "error enabling charger\n");
goto fail_free_irq;
}
cdata->ac_online = 1;
power_supply_changed(&cdata->ac);
}
return 0;
fail_free_irq:
devm_free_irq(cdata->dev, irq, cdata);
fail_unregister_supply:
power_supply_unregister(&cdata->ac);
return ret;
}
static int tps65090_charger_remove(struct platform_device *pdev)
{
struct tps65090_charger *cdata = dev_get_drvdata(&pdev->dev);
devm_free_irq(cdata->dev, cdata->irq, cdata);
power_supply_unregister(&cdata->ac);
return 0;
}
static struct of_device_id of_tps65090_charger_match[] = {
{ .compatible = "ti,tps65090-charger", },
{ /* end */ }
};
static struct platform_driver tps65090_charger_driver = {
.driver = {
.name = "tps65090-charger",
.of_match_table = of_tps65090_charger_match,
.owner = THIS_MODULE,
},
.probe = tps65090_charger_probe,
.remove = tps65090_charger_remove,
};
module_platform_driver(tps65090_charger_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>");
MODULE_DESCRIPTION("tps65090 battery charger driver");

View File

@ -636,17 +636,7 @@ static struct platform_driver twl4030_bci_driver = {
.remove = __exit_p(twl4030_bci_remove),
};
static int __init twl4030_bci_init(void)
{
return platform_driver_probe(&twl4030_bci_driver, twl4030_bci_probe);
}
module_init(twl4030_bci_init);
static void __exit twl4030_bci_exit(void)
{
platform_driver_unregister(&twl4030_bci_driver);
}
module_exit(twl4030_bci_exit);
module_platform_driver_probe(twl4030_bci_driver, twl4030_bci_probe);
MODULE_AUTHOR("Gražvydas Ignotas");
MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");

View File

@ -169,7 +169,8 @@ static int wm831x_backup_probe(struct platform_device *pdev)
struct power_supply *backup;
int ret;
devdata = kzalloc(sizeof(struct wm831x_backup), GFP_KERNEL);
devdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_backup),
GFP_KERNEL);
if (devdata == NULL)
return -ENOMEM;
@ -197,14 +198,8 @@ static int wm831x_backup_probe(struct platform_device *pdev)
backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
backup->get_property = wm831x_backup_get_prop;
ret = power_supply_register(&pdev->dev, backup);
if (ret)
goto err_kmalloc;
return ret;
err_kmalloc:
kfree(devdata);
return ret;
}
static int wm831x_backup_remove(struct platform_device *pdev)
@ -213,7 +208,6 @@ static int wm831x_backup_remove(struct platform_device *pdev)
power_supply_unregister(&devdata->backup);
kfree(devdata->backup.name);
kfree(devdata);
return 0;
}

View File

@ -89,6 +89,11 @@ struct abx500_fg;
* points.
* @maint_thres This is the threshold where we stop reporting
* battery full while in maintenance, in per cent
* @pcut_enable: Enable power cut feature in ab8505
* @pcut_max_time: Max time threshold
* @pcut_flag_time: Flagtime threshold
* @pcut_max_restart: Max number of restarts
* @pcut_debounce_time: Sets battery debounce time
*/
struct abx500_fg_parameters {
int recovery_sleep_timer;
@ -106,6 +111,11 @@ struct abx500_fg_parameters {
int battok_raising_th_sel1;
int user_cap_limit;
int maint_thres;
bool pcut_enable;
u8 pcut_max_time;
u8 pcut_flag_time;
u8 pcut_max_restart;
u8 pcut_debounce_time;
};
/**
@ -173,11 +183,11 @@ struct abx500_battery_type {
int low_high_vol_lvl;
int battery_resistance;
int n_temp_tbl_elements;
struct abx500_res_to_temp *r_to_t_tbl;
const struct abx500_res_to_temp *r_to_t_tbl;
int n_v_cap_tbl_elements;
struct abx500_v_to_cap *v_to_cap_tbl;
const struct abx500_v_to_cap *v_to_cap_tbl;
int n_batres_tbl_elements;
struct batres_vs_temp *batres_tbl;
const struct batres_vs_temp *batres_tbl;
};
/**
@ -236,7 +246,11 @@ struct abx500_bm_charger_parameters {
* @interval_not_charging charge alg cycle period time when not charging (sec)
* @temp_hysteresis temperature hysteresis
* @gnd_lift_resistance Battery ground to phone ground resistance (mOhm)
* @maxi: maximization parameters
* @n_chg_out_curr number of elements in array chg_output_curr
* @n_chg_in_curr number of elements in array chg_input_curr
* @chg_output_curr charger output current level map
* @chg_input_curr charger input current level map
* @maxi maximization parameters
* @cap_levels capacity in percent for the different capacity levels
* @bat_type table of supported battery types
* @chg_params charger parameters
@ -257,6 +271,7 @@ struct abx500_bm_data {
bool autopower_cfg;
bool ac_enabled;
bool usb_enabled;
bool usb_power_path;
bool no_maintenance;
bool capacity_scaling;
bool chg_unknown_bat;
@ -270,6 +285,10 @@ struct abx500_bm_data {
int interval_not_charging;
int temp_hysteresis;
int gnd_lift_resistance;
int n_chg_out_curr;
int n_chg_in_curr;
int *chg_output_curr;
int *chg_input_curr;
const struct abx500_maxim_parameters *maxi;
const struct abx500_bm_capacity_levels *cap_levels;
struct abx500_battery_type *bat_type;

View File

@ -23,6 +23,7 @@
* Bank : 0x5
*/
#define AB8500_USB_LINE_STAT_REG 0x80
#define AB8500_USB_LINE_CTRL2_REG 0x82
#define AB8500_USB_LINK1_STAT_REG 0x94
/*
@ -33,7 +34,7 @@
#define AB8500_CH_STATUS2_REG 0x01
#define AB8500_CH_USBCH_STAT1_REG 0x02
#define AB8500_CH_USBCH_STAT2_REG 0x03
#define AB8500_CH_FSM_STAT_REG 0x04
#define AB8540_CH_USBCH_STAT3_REG 0x04
#define AB8500_CH_STAT_REG 0x05
/*
@ -69,6 +70,8 @@
#define AB8500_USBCH_CTRL1_REG 0xC0
#define AB8500_USBCH_CTRL2_REG 0xC1
#define AB8500_USBCH_IPT_CRNTLVL_REG 0xC2
#define AB8540_USB_PP_MODE_REG 0xC5
#define AB8540_USB_PP_CHR_REG 0xC6
/*
* Gas Gauge register offsets
@ -105,6 +108,7 @@
#define AB8500_RTC_BACKUP_CHG_REG 0x0C
#define AB8500_RTC_CC_CONF_REG 0x01
#define AB8500_RTC_CTRL_REG 0x0B
#define AB8500_RTC_CTRL1_REG 0x11
/*
* OTP register offsets
@ -154,6 +158,7 @@
#define CH_OP_CUR_LVL_1P4 0x0D
#define CH_OP_CUR_LVL_1P5 0x0E
#define CH_OP_CUR_LVL_1P6 0x0F
#define CH_OP_CUR_LVL_2P 0x3F
/* BTEMP High thermal limits */
#define BTEMP_HIGH_TH_57_0 0x00
@ -179,10 +184,25 @@
#define BUP_ICH_SEL_300UA 0x08
#define BUP_ICH_SEL_700UA 0x0C
#define BUP_VCH_SEL_2P5V 0x00
#define BUP_VCH_SEL_2P6V 0x01
#define BUP_VCH_SEL_2P8V 0x02
#define BUP_VCH_SEL_3P1V 0x03
enum bup_vch_sel {
BUP_VCH_SEL_2P5V,
BUP_VCH_SEL_2P6V,
BUP_VCH_SEL_2P8V,
BUP_VCH_SEL_3P1V,
/*
* Note that the following 5 values 2.7v, 2.9v, 3.0v, 3.2v, 3.3v
* are only available on ab8540. You can't choose these 5
* voltage on ab8500/ab8505/ab9540.
*/
BUP_VCH_SEL_2P7V,
BUP_VCH_SEL_2P9V,
BUP_VCH_SEL_3P0V,
BUP_VCH_SEL_3P2V,
BUP_VCH_SEL_3P3V,
};
#define BUP_VCH_RANGE 0x02
#define VBUP33_VRTCN 0x01
/* Battery OVV constants */
#define BATT_OVV_ENA 0x02
@ -228,6 +248,8 @@
#define BAT_CTRL_20U_ENA 0x02
#define BAT_CTRL_18U_ENA 0x01
#define BAT_CTRL_16U_ENA 0x02
#define BAT_CTRL_60U_ENA 0x01
#define BAT_CTRL_120U_ENA 0x02
#define BAT_CTRL_CMP_ENA 0x04
#define FORCE_BAT_CTRL_CMP_HIGH 0x08
#define BAT_CTRL_PULL_UP_ENA 0x10
@ -235,6 +257,24 @@
/* Battery type */
#define BATTERY_UNKNOWN 00
/* Registers for pcut feature in ab8505 and ab9540 */
#define AB8505_RTC_PCUT_CTL_STATUS_REG 0x12
#define AB8505_RTC_PCUT_TIME_REG 0x13
#define AB8505_RTC_PCUT_MAX_TIME_REG 0x14
#define AB8505_RTC_PCUT_FLAG_TIME_REG 0x15
#define AB8505_RTC_PCUT_RESTART_REG 0x16
#define AB8505_RTC_PCUT_DEBOUNCE_REG 0x17
/* USB Power Path constants for ab8540 */
#define BUS_VSYS_VOL_SELECT_MASK 0x06
#define BUS_VSYS_VOL_SELECT_3P6V 0x00
#define BUS_VSYS_VOL_SELECT_3P325V 0x02
#define BUS_VSYS_VOL_SELECT_3P9V 0x04
#define BUS_VSYS_VOL_SELECT_4P3V 0x06
#define BUS_POWER_PATH_MODE_ENA 0x01
#define BUS_PP_PRECHG_CURRENT_MASK 0x0E
#define BUS_POWER_PATH_PRECHG_ENA 0x01
/**
* struct res_to_temp - defines one point in a temp to res curve. To
* be used in battery packs that combines the identification resistor with a
@ -283,6 +323,11 @@ struct ab8500_fg;
* points.
* @maint_thres This is the threshold where we stop reporting
* battery full while in maintenance, in per cent
* @pcut_enable: Enable power cut feature in ab8505
* @pcut_max_time: Max time threshold
* @pcut_flag_time: Flagtime threshold
* @pcut_max_restart: Max number of restarts
* @pcut_debunce_time: Sets battery debounce time
*/
struct ab8500_fg_parameters {
int recovery_sleep_timer;
@ -299,6 +344,11 @@ struct ab8500_fg_parameters {
int battok_raising_th_sel1;
int user_cap_limit;
int maint_thres;
bool pcut_enable;
u8 pcut_max_time;
u8 pcut_flag_time;
u8 pcut_max_restart;
u8 pcut_debunce_time;
};
/**
@ -415,6 +465,7 @@ void ab8500_fg_reinit(void);
void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
struct ab8500_btemp *ab8500_btemp_get(void);
int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp);
int ab8500_btemp_get_temp(struct ab8500_btemp *btemp);
struct ab8500_fg *ab8500_fg_get(void);
int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev);
int ab8500_fg_inst_curr_start(struct ab8500_fg *di);

View File

@ -4,32 +4,72 @@
*
* Author: Arun R Murthy <arun.murthy@stericsson.com>
* Author: Daniel Willerud <daniel.willerud@stericsson.com>
* Author: M'boumba Cedric Madianga <cedric.madianga@stericsson.com>
*/
#ifndef _AB8500_GPADC_H
#define _AB8500_GPADC_H
/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2) */
#define BAT_CTRL 0x01
#define BTEMP_BALL 0x02
#define MAIN_CHARGER_V 0x03
#define ACC_DETECT1 0x04
#define ACC_DETECT2 0x05
#define ADC_AUX1 0x06
#define ADC_AUX2 0x07
#define MAIN_BAT_V 0x08
#define VBUS_V 0x09
#define MAIN_CHARGER_C 0x0A
#define USB_CHARGER_C 0x0B
#define BK_BAT_V 0x0C
#define DIE_TEMP 0x0D
/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2
* and ADCHwSel[4:0] in GPADCCtrl3 ) */
#define BAT_CTRL 0x01
#define BTEMP_BALL 0x02
#define MAIN_CHARGER_V 0x03
#define ACC_DETECT1 0x04
#define ACC_DETECT2 0x05
#define ADC_AUX1 0x06
#define ADC_AUX2 0x07
#define MAIN_BAT_V 0x08
#define VBUS_V 0x09
#define MAIN_CHARGER_C 0x0A
#define USB_CHARGER_C 0x0B
#define BK_BAT_V 0x0C
#define DIE_TEMP 0x0D
#define USB_ID 0x0E
#define XTAL_TEMP 0x12
#define VBAT_TRUE_MEAS 0x13
#define BAT_CTRL_AND_IBAT 0x1C
#define VBAT_MEAS_AND_IBAT 0x1D
#define VBAT_TRUE_MEAS_AND_IBAT 0x1E
#define BAT_TEMP_AND_IBAT 0x1F
/* Virtual channel used only for ibat convertion to ampere
* Battery current conversion (ibat) cannot be requested as a single conversion
* but it is always in combination with other input requests
*/
#define IBAT_VIRTUAL_CHANNEL 0xFF
#define SAMPLE_1 1
#define SAMPLE_4 4
#define SAMPLE_8 8
#define SAMPLE_16 16
#define RISING_EDGE 0
#define FALLING_EDGE 1
/* Arbitrary ADC conversion type constants */
#define ADC_SW 0
#define ADC_HW 1
struct ab8500_gpadc;
struct ab8500_gpadc *ab8500_gpadc_get(char *name);
int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel);
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel);
int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type);
static inline int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
{
return ab8500_gpadc_sw_hw_convert(gpadc, channel,
SAMPLE_16, 0, 0, ADC_SW);
}
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type);
int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type,
int *ibat);
int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc,
u8 channel, int ad_value);
u8 channel, int ad_value);
void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc,
u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h,
u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h);
#endif /* _AB8500_GPADC_H */

View File

@ -12,6 +12,7 @@
int ab8500_sysctrl_read(u16 reg, u8 *value);
int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value);
void ab8500_restart(char mode, const char *cmd);
#else
@ -40,6 +41,7 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits)
/* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */
struct ab8500_sysctrl_platform_data {
u8 initial_req_buf_config[8];
u16 (*reboot_reason_code)(const char *cmd);
};
/* Registers */
@ -299,4 +301,8 @@ struct ab8500_sysctrl_platform_data {
#define AB9540_SYSCLK12BUF4VALID_SYSCLK12BUF4VALID_MASK 0xFF
#define AB9540_SYSCLK12BUF4VALID_SYSCLK12BUF4VALID_SHIFT 0
#define AB8500_ENABLE_WD 0x1
#define AB8500_KICK_WD 0x2
#define AB8500_WD_RESTART_ON_EXPIRE 0x10
#endif /* __AB8500_SYSCTRL_H */

View File

@ -362,6 +362,7 @@ struct ab8500 {
u8 *oldmask;
int mask_size;
const int *irq_reg_offset;
int it_latchhier_num;
};
struct ab8500_regulator_platform_data;
@ -505,6 +506,8 @@ static inline int is_ab9540_2p0_or_earlier(struct ab8500 *ab)
return (is_ab9540(ab) && (ab->chip_id < AB8500_CUT2P0));
}
void ab8500_override_turn_on_stat(u8 mask, u8 set);
#ifdef CONFIG_AB8500_DEBUG
void ab8500_dump_all_banks(struct device *dev);
void ab8500_debug_register_interrupt(int line);

View File

@ -17,8 +17,11 @@ struct ux500_charger;
struct ux500_charger_ops {
int (*enable) (struct ux500_charger *, int, int, int);
int (*check_enable) (struct ux500_charger *, int, int);
int (*kick_wd) (struct ux500_charger *);
int (*update_curr) (struct ux500_charger *, int);
int (*pp_enable) (struct ux500_charger *, bool);
int (*pre_chg_enable) (struct ux500_charger *, bool);
};
/**
@ -29,6 +32,7 @@ struct ux500_charger_ops {
* @max_out_curr maximum output charger current in mA
* @enabled indicates if this charger is used or not
* @external external charger unit (pm2xxx)
* @power_path USB power path support
*/
struct ux500_charger {
struct power_supply psy;
@ -38,6 +42,9 @@ struct ux500_charger {
int wdt_refresh;
bool enabled;
bool external;
bool power_path;
};
extern struct blocking_notifier_head charger_notifier_list;
#endif

View File

@ -86,6 +86,11 @@ struct tps65090_regulator_plat_data {
struct tps65090_platform_data {
int irq_base;
char **supplied_to;
size_t num_supplicants;
int enable_low_current_chrg;
struct tps65090_regulator_plat_data *reg_pdata[TPS65090_REGULATOR_MAX];
};

View File

@ -48,7 +48,7 @@ struct pm2xxx_charger_platform_data {
size_t num_supplicants;
int i2c_bus;
const char *label;
int irq_number;
int gpio_irq_number;
unsigned int lpn_gpio;
int irq_type;
};

View File

@ -0,0 +1,16 @@
/*
* Copyright (C) ST-Ericsson 2013
* Author: Hongbo Zhang <hongbo.zhang@linaro.com>
* License terms: GNU General Public License v2
*/
#ifndef PWR_AB8500_H
#define PWR_AB8500_H
extern const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[];
extern const int ab8500_temp_tbl_a_size;
extern const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[];
extern const int ab8500_temp_tbl_b_size;
#endif /* PWR_AB8500_H */

View File

@ -171,6 +171,12 @@ struct power_supply {
char **supplied_to;
size_t num_supplicants;
char **supplied_from;
size_t num_supplies;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif
int (*get_property)(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val);

View File

@ -115,9 +115,6 @@ unsigned __vexpress_get_site(struct device *dev, struct device_node *node);
void vexpress_sysreg_early_init(void __iomem *base);
void vexpress_sysreg_of_early_init(void);
void vexpress_power_off(void);
void vexpress_restart(char str, const char *cmd);
/* Clocks */
struct clk *vexpress_osc_setup(struct device *dev);