Browse Source

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

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

* tag 'for-v3.17' of git://git.infradead.org/battery-2.6:
  ipaq_micro_battery: fix sparse non static symbol warning
  power: add driver for battery reading on iPaq h3xxx
  power: twl4030_charger: detect battery presence prior to enabling charger
  power: reset: Add reboot driver for brcmstb
  power_supply: Fix sparse non static symbol warning
  power_supply: Add inlmt,iterm, min/max temp props
  charger: tps65090: Allow charger module to be used when no irq
  power/reset: Fix GPL v2 license string typo
  power: poweroff: gpio: convert to use descriptors
  bq27000: report missing device better.
  bq27x00_battery: Introduce the use of the managed version of kzalloc
  Documentation: DT: Document rx51-battery binding
  rx51_battery: convert to iio consumer
  bq2415x_charger: Fix Atomic Sleep Bug
Linus Torvalds 11 years ago
parent
commit
0498cf8429

+ 25 - 0
Documentation/devicetree/bindings/power/rx51-battery.txt

@@ -0,0 +1,25 @@
+Binding for Nokia N900 battery
+
+The Nokia N900 battery status can be read via the TWL4030's A/D converter.
+
+Required properties:
+- compatible: Should contain one of the following:
+ * "nokia,n900-battery"
+- io-channels: Should contain IIO channel specifiers
+               for each element in io-channel-names.
+- io-channel-names: Should contain the following values:
+ * "temp" - The ADC channel for temperature reading
+ * "bsi"  - The ADC channel for battery size identification
+ * "vbat" - The ADC channel to measure the battery voltage
+
+Example from Nokia N900:
+
+battery: n900-battery {
+	compatible = "nokia,n900-battery";
+	io-channels = <&twl4030_madc 0>,
+		      <&twl4030_madc 4>,
+		      <&twl4030_madc 12>;
+	io-channel-names = "temp",
+			   "bsi",
+			   "vbat";
+};

+ 6 - 0
Documentation/power/power_supply_class.txt

@@ -118,6 +118,10 @@ relative, time-based measurements.
 CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger.
 CONSTANT_CHARGE_CURRENT_MAX - maximum charge current supported by the
 power supply object.
+INPUT_CURRENT_LIMIT - input current limit programmed by charger. Indicates
+the current drawn from a charging source.
+CHARGE_TERM_CURRENT - Charge termination current used to detect the end of charge
+condition.
 
 CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger.
 CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the
@@ -140,6 +144,8 @@ TEMP_ALERT_MAX - maximum battery temperature alert.
 TEMP_AMBIENT - ambient temperature.
 TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert.
 TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert.
+TEMP_MIN - minimum operatable temperature
+TEMP_MAX - maximum operatable temperature
 
 TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e.
 while battery powers a load)

+ 7 - 0
drivers/power/Kconfig

@@ -137,6 +137,13 @@ config BATTERY_COLLIE
 	  Say Y to enable support for the battery on the Sharp Zaurus
 	  SL-5500 (collie) models.
 
+config BATTERY_IPAQ_MICRO
+	tristate "iPAQ Atmel Micro ASIC battery driver"
+	depends on MFD_IPAQ_MICRO
+	help
+	  Choose this option if you want to monitor battery status on
+	  Compaq/HP iPAQ h3100 and h3600.
+
 config BATTERY_WM97XX
 	bool "WM97xx generic battery driver"
 	depends on TOUCHSCREEN_WM97XX=y

+ 1 - 0
drivers/power/Makefile

@@ -25,6 +25,7 @@ obj-$(CONFIG_BATTERY_PMU)	+= pmu_battery.o
 obj-$(CONFIG_BATTERY_OLPC)	+= olpc_battery.o
 obj-$(CONFIG_BATTERY_TOSA)	+= tosa_battery.o
 obj-$(CONFIG_BATTERY_COLLIE)	+= collie_battery.o
+obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
 obj-$(CONFIG_BATTERY_WM97XX)	+= wm97xx_battery.o
 obj-$(CONFIG_BATTERY_SBS)	+= sbs-battery.o
 obj-$(CONFIG_BATTERY_BQ27x00)	+= bq27x00_battery.o

+ 6 - 2
drivers/power/bq2415x_charger.c

@@ -840,8 +840,7 @@ static int bq2415x_notifier_call(struct notifier_block *nb,
 	if (bq->automode < 1)
 		return NOTIFY_OK;
 
-	sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode");
-	bq2415x_set_mode(bq, bq->reported_mode);
+	schedule_delayed_work(&bq->work, 0);
 
 	return NOTIFY_OK;
 }
@@ -892,6 +891,11 @@ static void bq2415x_timer_work(struct work_struct *work)
 	int error;
 	int boost;
 
+	if (bq->automode > 0 && (bq->reported_mode != bq->mode)) {
+		sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode");
+		bq2415x_set_mode(bq, bq->reported_mode);
+	}
+
 	if (!bq->autotimer)
 		return;
 

+ 8 - 20
drivers/power/bq27x00_battery.c

@@ -25,6 +25,7 @@
  * http://www.ti.com/product/bq27425-g1
  */
 
+#include <linux/device.h>
 #include <linux/module.h>
 #include <linux/param.h>
 #include <linux/jiffies.h>
@@ -415,6 +416,9 @@ static void bq27x00_update(struct bq27x00_device_info *di)
 	bool is_bq27425 = di->chip == BQ27425;
 
 	cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
+	if ((cache.flags & 0xff) == 0xff)
+		/* read error */
+		cache.flags = -1;
 	if (cache.flags >= 0) {
 		if (!is_bq27500 && !is_bq27425
 				&& (cache.flags & BQ27000_FLAG_CI)) {
@@ -804,7 +808,7 @@ static int bq27x00_battery_probe(struct i2c_client *client,
 		goto batt_failed_1;
 	}
 
-	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
 	if (!di) {
 		dev_err(&client->dev, "failed to allocate device info data\n");
 		retval = -ENOMEM;
@@ -819,14 +823,12 @@ static int bq27x00_battery_probe(struct i2c_client *client,
 
 	retval = bq27x00_powersupply_init(di);
 	if (retval)
-		goto batt_failed_3;
+		goto batt_failed_2;
 
 	i2c_set_clientdata(client, di);
 
 	return 0;
 
-batt_failed_3:
-	kfree(di);
 batt_failed_2:
 	kfree(name);
 batt_failed_1:
@@ -849,8 +851,6 @@ static int bq27x00_battery_remove(struct i2c_client *client)
 	idr_remove(&battery_id, di->id);
 	mutex_unlock(&battery_mutex);
 
-	kfree(di);
-
 	return 0;
 }
 
@@ -933,7 +933,6 @@ static int bq27000_battery_probe(struct platform_device *pdev)
 {
 	struct bq27x00_device_info *di;
 	struct bq27000_platform_data *pdata = pdev->dev.platform_data;
-	int ret;
 
 	if (!pdata) {
 		dev_err(&pdev->dev, "no platform_data supplied\n");
@@ -945,7 +944,7 @@ static int bq27000_battery_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
 	if (!di) {
 		dev_err(&pdev->dev, "failed to allocate device info data\n");
 		return -ENOMEM;
@@ -959,16 +958,7 @@ static int bq27000_battery_probe(struct platform_device *pdev)
 	di->bat.name = pdata->name ?: dev_name(&pdev->dev);
 	di->bus.read = &bq27000_read_platform;
 
-	ret = bq27x00_powersupply_init(di);
-	if (ret)
-		goto err_free;
-
-	return 0;
-
-err_free:
-	kfree(di);
-
-	return ret;
+	return bq27x00_powersupply_init(di);
 }
 
 static int bq27000_battery_remove(struct platform_device *pdev)
@@ -977,8 +967,6 @@ static int bq27000_battery_remove(struct platform_device *pdev)
 
 	bq27x00_powersupply_unregister(di);
 
-	kfree(di);
-
 	return 0;
 }
 

+ 290 - 0
drivers/power/ipaq_micro_battery.c

@@ -0,0 +1,290 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * h3xxx atmel micro companion support, battery subdevice
+ * based on previous kernel 2.4 version
+ * Author : Alessandro Gardich <gremlin@gremlin.it>
+ * Author : Linus Walleij <linus.walleij@linaro.org>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ipaq-micro.h>
+#include <linux/power_supply.h>
+#include <linux/workqueue.h>
+
+#define BATT_PERIOD 100000 /* 100 seconds in milliseconds */
+
+#define MICRO_BATT_CHEM_ALKALINE	0x01
+#define MICRO_BATT_CHEM_NICD		0x02
+#define MICRO_BATT_CHEM_NIMH		0x03
+#define MICRO_BATT_CHEM_LION		0x04
+#define MICRO_BATT_CHEM_LIPOLY		0x05
+#define MICRO_BATT_CHEM_NOT_INSTALLED	0x06
+#define MICRO_BATT_CHEM_UNKNOWN		0xff
+
+#define MICRO_BATT_STATUS_HIGH		0x01
+#define MICRO_BATT_STATUS_LOW		0x02
+#define MICRO_BATT_STATUS_CRITICAL	0x04
+#define MICRO_BATT_STATUS_CHARGING	0x08
+#define MICRO_BATT_STATUS_CHARGEMAIN	0x10
+#define MICRO_BATT_STATUS_DEAD		0x20 /* Battery will not charge */
+#define MICRO_BATT_STATUS_NOTINSTALLED	0x20 /* For expansion pack batteries */
+#define MICRO_BATT_STATUS_FULL		0x40 /* Battery fully charged */
+#define MICRO_BATT_STATUS_NOBATTERY	0x80
+#define MICRO_BATT_STATUS_UNKNOWN	0xff
+
+struct micro_battery {
+	struct ipaq_micro *micro;
+	struct workqueue_struct *wq;
+	struct delayed_work update;
+	u8 ac;
+	u8 chemistry;
+	unsigned int voltage;
+	u16 temperature;
+	u8 flag;
+};
+
+static void micro_battery_work(struct work_struct *work)
+{
+	struct micro_battery *mb = container_of(work,
+				struct micro_battery, update.work);
+	struct ipaq_micro_msg msg_battery = {
+		.id = MSG_BATTERY,
+	};
+	struct ipaq_micro_msg msg_sensor = {
+		.id = MSG_THERMAL_SENSOR,
+	};
+
+	/* First send battery message */
+	ipaq_micro_tx_msg_sync(mb->micro, &msg_battery);
+	if (msg_battery.rx_len < 4)
+		pr_info("ERROR");
+
+	/*
+	 * Returned message format:
+	 * byte 0:   0x00 = Not plugged in
+	 *           0x01 = AC adapter plugged in
+	 * byte 1:   chemistry
+	 * byte 2:   voltage LSB
+	 * byte 3:   voltage MSB
+	 * byte 4:   flags
+	 * byte 5-9: same for battery 2
+	 */
+	mb->ac = msg_battery.rx_data[0];
+	mb->chemistry = msg_battery.rx_data[1];
+	mb->voltage = ((((unsigned short)msg_battery.rx_data[3] << 8) +
+			msg_battery.rx_data[2]) * 5000L) * 1000 / 1024;
+	mb->flag = msg_battery.rx_data[4];
+
+	if (msg_battery.rx_len == 9)
+		pr_debug("second battery ignored\n");
+
+	/* Then read the sensor */
+	ipaq_micro_tx_msg_sync(mb->micro, &msg_sensor);
+	mb->temperature = msg_sensor.rx_data[1] << 8 | msg_sensor.rx_data[0];
+
+	queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
+}
+
+static int get_capacity(struct power_supply *b)
+{
+	struct micro_battery *mb = dev_get_drvdata(b->dev->parent);
+
+	switch (mb->flag & 0x07) {
+	case MICRO_BATT_STATUS_HIGH:
+		return 100;
+		break;
+	case MICRO_BATT_STATUS_LOW:
+		return 50;
+		break;
+	case MICRO_BATT_STATUS_CRITICAL:
+		return 5;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int get_status(struct power_supply *b)
+{
+	struct micro_battery *mb = dev_get_drvdata(b->dev->parent);
+
+	if (mb->flag == MICRO_BATT_STATUS_UNKNOWN)
+		return POWER_SUPPLY_STATUS_UNKNOWN;
+
+	if (mb->flag & MICRO_BATT_STATUS_FULL)
+		return POWER_SUPPLY_STATUS_FULL;
+
+	if ((mb->flag & MICRO_BATT_STATUS_CHARGING) ||
+		(mb->flag & MICRO_BATT_STATUS_CHARGEMAIN))
+		return POWER_SUPPLY_STATUS_CHARGING;
+
+	return POWER_SUPPLY_STATUS_DISCHARGING;
+}
+
+static int micro_batt_get_property(struct power_supply *b,
+					enum power_supply_property psp,
+					union power_supply_propval *val)
+{
+	struct micro_battery *mb = dev_get_drvdata(b->dev->parent);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		switch (mb->chemistry) {
+		case MICRO_BATT_CHEM_NICD:
+			val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd;
+			break;
+		case MICRO_BATT_CHEM_NIMH:
+			val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
+			break;
+		case MICRO_BATT_CHEM_LION:
+			val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+			break;
+		case MICRO_BATT_CHEM_LIPOLY:
+			val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
+			break;
+		default:
+			val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+			break;
+		};
+		break;
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = get_status(b);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = 4700000;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = get_capacity(b);
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = mb->temperature;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = mb->voltage;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	return 0;
+}
+
+static int micro_ac_get_property(struct power_supply *b,
+				 enum power_supply_property psp,
+				 union power_supply_propval *val)
+{
+	struct micro_battery *mb = dev_get_drvdata(b->dev->parent);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = mb->ac;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	return 0;
+}
+
+static enum power_supply_property micro_batt_power_props[] = {
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static struct power_supply micro_batt_power = {
+	.name			= "main-battery",
+	.type			= POWER_SUPPLY_TYPE_BATTERY,
+	.properties		= micro_batt_power_props,
+	.num_properties		= ARRAY_SIZE(micro_batt_power_props),
+	.get_property		= micro_batt_get_property,
+	.use_for_apm		= 1,
+};
+
+static enum power_supply_property micro_ac_power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static struct power_supply micro_ac_power = {
+	.name			= "ac",
+	.type			= POWER_SUPPLY_TYPE_MAINS,
+	.properties		= micro_ac_power_props,
+	.num_properties		= ARRAY_SIZE(micro_ac_power_props),
+	.get_property		= micro_ac_get_property,
+};
+
+static int micro_batt_probe(struct platform_device *pdev)
+{
+	struct micro_battery *mb;
+
+	mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
+	if (!mb)
+		return -ENOMEM;
+
+	mb->micro = dev_get_drvdata(pdev->dev.parent);
+	mb->wq = create_singlethread_workqueue("ipaq-battery-wq");
+	INIT_DELAYED_WORK(&mb->update, micro_battery_work);
+	platform_set_drvdata(pdev, mb);
+	queue_delayed_work(mb->wq, &mb->update, 1);
+	power_supply_register(&pdev->dev, &micro_batt_power);
+	power_supply_register(&pdev->dev, &micro_ac_power);
+
+	dev_info(&pdev->dev, "iPAQ micro battery driver\n");
+	return 0;
+}
+
+static int micro_batt_remove(struct platform_device *pdev)
+
+{
+	struct micro_battery *mb = platform_get_drvdata(pdev);
+
+	power_supply_unregister(&micro_ac_power);
+	power_supply_unregister(&micro_batt_power);
+	cancel_delayed_work_sync(&mb->update);
+
+	return 0;
+}
+
+static int micro_batt_suspend(struct device *dev)
+{
+	struct micro_battery *mb = dev_get_drvdata(dev);
+
+	cancel_delayed_work_sync(&mb->update);
+	return 0;
+}
+
+static int micro_batt_resume(struct device *dev)
+{
+	struct micro_battery *mb = dev_get_drvdata(dev);
+
+	queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
+	return 0;
+}
+
+static const struct dev_pm_ops micro_batt_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(micro_batt_suspend, micro_batt_resume)
+};
+
+static struct platform_driver micro_batt_device_driver = {
+	.driver		= {
+		.name	= "ipaq-micro-battery",
+		.pm	= &micro_batt_dev_pm_ops,
+	},
+	.probe		= micro_batt_probe,
+	.remove		= micro_batt_remove,
+};
+module_platform_driver(micro_batt_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("driver for iPAQ Atmel micro battery");
+MODULE_ALIAS("platform:battery-ipaq-micro");

+ 2 - 1
drivers/power/power_supply_core.c

@@ -537,7 +537,8 @@ static void psy_unregister_cooler(struct power_supply *psy)
 }
 #endif
 
-int __power_supply_register(struct device *parent, struct power_supply *psy, bool ws)
+static int __power_supply_register(struct device *parent,
+				   struct power_supply *psy, bool ws)
 {
 	struct device *dev;
 	int rc;

+ 4 - 0
drivers/power/power_supply_sysfs.c

@@ -167,6 +167,7 @@ static struct device_attribute power_supply_attrs[] = {
 	POWER_SUPPLY_ATTR(constant_charge_voltage_max),
 	POWER_SUPPLY_ATTR(charge_control_limit),
 	POWER_SUPPLY_ATTR(charge_control_limit_max),
+	POWER_SUPPLY_ATTR(input_current_limit),
 	POWER_SUPPLY_ATTR(energy_full_design),
 	POWER_SUPPLY_ATTR(energy_empty_design),
 	POWER_SUPPLY_ATTR(energy_full),
@@ -178,6 +179,8 @@ static struct device_attribute power_supply_attrs[] = {
 	POWER_SUPPLY_ATTR(capacity_alert_max),
 	POWER_SUPPLY_ATTR(capacity_level),
 	POWER_SUPPLY_ATTR(temp),
+	POWER_SUPPLY_ATTR(temp_max),
+	POWER_SUPPLY_ATTR(temp_min),
 	POWER_SUPPLY_ATTR(temp_alert_min),
 	POWER_SUPPLY_ATTR(temp_alert_max),
 	POWER_SUPPLY_ATTR(temp_ambient),
@@ -189,6 +192,7 @@ static struct device_attribute power_supply_attrs[] = {
 	POWER_SUPPLY_ATTR(time_to_full_avg),
 	POWER_SUPPLY_ATTR(type),
 	POWER_SUPPLY_ATTR(scope),
+	POWER_SUPPLY_ATTR(charge_term_current),
 	/* Properties of type `const char *' */
 	POWER_SUPPLY_ATTR(model_name),
 	POWER_SUPPLY_ATTR(manufacturer),

+ 11 - 0
drivers/power/reset/Kconfig

@@ -20,6 +20,17 @@ config POWER_RESET_AXXIA
 
 	  Say Y if you have an Axxia family SoC.
 
+config POWER_RESET_BRCMSTB
+	bool "Broadcom STB reset driver" if COMPILE_TEST
+	depends on POWER_RESET && ARM
+	default ARCH_BRCMSTB
+	help
+	  This driver provides restart support for ARM-based Broadcom STB
+	  boards.
+
+	  Say Y here if you have an ARM-based Broadcom STB board and you wish
+	  to have restart support.
+
 config POWER_RESET_GPIO
 	bool "GPIO power-off driver"
 	depends on OF_GPIO && POWER_RESET

+ 1 - 0
drivers/power/reset/Makefile

@@ -1,5 +1,6 @@
 obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o
 obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
+obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
 obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
 obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
 obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o

+ 120 - 0
drivers/power/reset/brcmstb-reboot.c

@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+#include <linux/smp.h>
+#include <linux/mfd/syscon.h>
+
+#include <asm/system_misc.h>
+
+#define RESET_SOURCE_ENABLE_REG 1
+#define SW_MASTER_RESET_REG 2
+
+static struct regmap *regmap;
+static u32 rst_src_en;
+static u32 sw_mstr_rst;
+
+static void brcmstb_reboot(enum reboot_mode mode, const char *cmd)
+{
+	int rc;
+	u32 tmp;
+
+	rc = regmap_write(regmap, rst_src_en, 1);
+	if (rc) {
+		pr_err("failed to write rst_src_en (%d)\n", rc);
+		return;
+	}
+
+	rc = regmap_read(regmap, rst_src_en, &tmp);
+	if (rc) {
+		pr_err("failed to read rst_src_en (%d)\n", rc);
+		return;
+	}
+
+	rc = regmap_write(regmap, sw_mstr_rst, 1);
+	if (rc) {
+		pr_err("failed to write sw_mstr_rst (%d)\n", rc);
+		return;
+	}
+
+	rc = regmap_read(regmap, sw_mstr_rst, &tmp);
+	if (rc) {
+		pr_err("failed to read sw_mstr_rst (%d)\n", rc);
+		return;
+	}
+
+	while (1)
+		;
+}
+
+static int brcmstb_reboot_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct device_node *np = pdev->dev.of_node;
+
+	regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
+	if (IS_ERR(regmap)) {
+		pr_err("failed to get syscon phandle\n");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32_index(np, "syscon", RESET_SOURCE_ENABLE_REG,
+					&rst_src_en);
+	if (rc) {
+		pr_err("can't get rst_src_en offset (%d)\n", rc);
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32_index(np, "syscon", SW_MASTER_RESET_REG,
+					&sw_mstr_rst);
+	if (rc) {
+		pr_err("can't get sw_mstr_rst offset (%d)\n", rc);
+		return -EINVAL;
+	}
+
+	arm_pm_restart = brcmstb_reboot;
+
+	return 0;
+}
+
+static const struct of_device_id of_match[] = {
+	{ .compatible = "brcm,brcmstb-reboot", },
+	{},
+};
+
+static struct platform_driver brcmstb_reboot_driver = {
+	.probe = brcmstb_reboot_probe,
+	.driver = {
+		.name = "brcmstb-reboot",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match,
+	},
+};
+
+static int __init brcmstb_reboot_init(void)
+{
+	return platform_driver_probe(&brcmstb_reboot_driver,
+					brcmstb_reboot_probe);
+}
+subsys_initcall(brcmstb_reboot_init);

+ 19 - 33
drivers/power/reset/gpio-poweroff.c

@@ -15,31 +15,29 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/of_platform.h>
-#include <linux/of_gpio.h>
 #include <linux/module.h>
 
 /*
  * Hold configuration here, cannot be more than one instance of the driver
  * since pm_power_off itself is global.
  */
-static int gpio_num = -1;
-static int gpio_active_low;
+static struct gpio_desc *reset_gpio;
 
 static void gpio_poweroff_do_poweroff(void)
 {
-	BUG_ON(!gpio_is_valid(gpio_num));
+	BUG_ON(!reset_gpio);
 
 	/* drive it active, also inactive->active edge */
-	gpio_direction_output(gpio_num, !gpio_active_low);
+	gpiod_direction_output(reset_gpio, 1);
 	mdelay(100);
 	/* drive inactive, also active->inactive edge */
-	gpio_set_value(gpio_num, gpio_active_low);
+	gpiod_set_value(reset_gpio, 0);
 	mdelay(100);
 
 	/* drive it active, also inactive->active edge */
-	gpio_set_value(gpio_num, !gpio_active_low);
+	gpiod_set_value(reset_gpio, 1);
 
 	/* give it some time */
 	mdelay(3000);
@@ -49,54 +47,42 @@ static void gpio_poweroff_do_poweroff(void)
 
 static int gpio_poweroff_probe(struct platform_device *pdev)
 {
-	enum of_gpio_flags flags;
 	bool input = false;
-	int ret;
 
 	/* If a pm_power_off function has already been added, leave it alone */
 	if (pm_power_off != NULL) {
-		pr_err("%s: pm_power_off function already registered",
+		dev_err(&pdev->dev,
+			"%s: pm_power_off function already registered",
 		       __func__);
 		return -EBUSY;
 	}
 
-	gpio_num = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
-	if (!gpio_is_valid(gpio_num))
-		return gpio_num;
-
-	gpio_active_low = flags & OF_GPIO_ACTIVE_LOW;
+	reset_gpio = devm_gpiod_get(&pdev->dev, NULL);
+	if (IS_ERR(reset_gpio))
+		return PTR_ERR(reset_gpio);
 
 	input = of_property_read_bool(pdev->dev.of_node, "input");
 
-	ret = gpio_request(gpio_num, "poweroff-gpio");
-	if (ret) {
-		pr_err("%s: Could not get GPIO %d", __func__, gpio_num);
-		return ret;
-	}
 	if (input) {
-		if (gpio_direction_input(gpio_num)) {
-			pr_err("Could not set direction of GPIO %d to input",
-			       gpio_num);
-			goto err;
+		if (gpiod_direction_input(reset_gpio)) {
+			dev_err(&pdev->dev,
+				"Could not set direction of reset GPIO to input\n");
+			return -ENODEV;
 		}
 	} else {
-		if (gpio_direction_output(gpio_num, gpio_active_low)) {
-			pr_err("Could not set direction of GPIO %d", gpio_num);
-			goto err;
+		if (gpiod_direction_output(reset_gpio, 0)) {
+			dev_err(&pdev->dev,
+				"Could not set direction of reset GPIO\n");
+			return -ENODEV;
 		}
 	}
 
 	pm_power_off = &gpio_poweroff_do_poweroff;
 	return 0;
-
-err:
-	gpio_free(gpio_num);
-	return -ENODEV;
 }
 
 static int gpio_poweroff_remove(struct platform_device *pdev)
 {
-	gpio_free(gpio_num);
 	if (pm_power_off == &gpio_poweroff_do_poweroff)
 		pm_power_off = NULL;
 

+ 1 - 1
drivers/power/reset/restart-poweroff.c

@@ -62,5 +62,5 @@ module_platform_driver(restart_poweroff_driver);
 
 MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch");
 MODULE_DESCRIPTION("restart poweroff driver");
-MODULE_LICENSE("GPLv2");
+MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("platform:poweroff-restart");

+ 66 - 24
drivers/power/rx51_battery.c

@@ -24,34 +24,27 @@
 #include <linux/power_supply.h>
 #include <linux/slab.h>
 #include <linux/i2c/twl4030-madc.h>
-
-/* RX51 specific channels */
-#define TWL4030_MADC_BTEMP_RX51	TWL4030_MADC_ADCIN0
-#define TWL4030_MADC_BCI_RX51	TWL4030_MADC_ADCIN4
+#include <linux/iio/consumer.h>
+#include <linux/of.h>
 
 struct rx51_device_info {
 	struct device *dev;
 	struct power_supply bat;
+	struct iio_channel *channel_temp;
+	struct iio_channel *channel_bsi;
+	struct iio_channel *channel_vbat;
 };
 
 /*
  * Read ADCIN channel value, code copied from maemo kernel
  */
-static int rx51_battery_read_adc(int channel)
+static int rx51_battery_read_adc(struct iio_channel *channel)
 {
-	struct twl4030_madc_request req;
-
-	req.channels = channel;
-	req.do_avg = 1;
-	req.method = TWL4030_MADC_SW1;
-	req.func_cb = NULL;
-	req.type = TWL4030_MADC_WAIT;
-	req.raw = true;
-
-	if (twl4030_madc_conversion(&req) <= 0)
-		return -ENODATA;
-
-	return req.rbuf[ffs(channel) - 1];
+	int val, err;
+	err = iio_read_channel_average_raw(channel, &val);
+	if (err < 0)
+		return err;
+	return val;
 }
 
 /*
@@ -60,10 +53,12 @@ static int rx51_battery_read_adc(int channel)
  */
 static int rx51_battery_read_voltage(struct rx51_device_info *di)
 {
-	int voltage = rx51_battery_read_adc(TWL4030_MADC_VBAT);
+	int voltage = rx51_battery_read_adc(di->channel_vbat);
 
-	if (voltage < 0)
+	if (voltage < 0) {
+		dev_err(di->dev, "Could not read ADC: %d\n", voltage);
 		return voltage;
+	}
 
 	return 1000 * (10000 * voltage / 1705);
 }
@@ -112,7 +107,10 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di)
 {
 	int min = 0;
 	int max = ARRAY_SIZE(rx51_temp_table2) - 1;
-	int raw = rx51_battery_read_adc(TWL4030_MADC_BTEMP_RX51);
+	int raw = rx51_battery_read_adc(di->channel_temp);
+
+	if (raw < 0)
+		dev_err(di->dev, "Could not read ADC: %d\n", raw);
 
 	/* Zero and negative values are undefined */
 	if (raw <= 0)
@@ -146,10 +144,12 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di)
  */
 static int rx51_battery_read_capacity(struct rx51_device_info *di)
 {
-	int capacity = rx51_battery_read_adc(TWL4030_MADC_BCI_RX51);
+	int capacity = rx51_battery_read_adc(di->channel_bsi);
 
-	if (capacity < 0)
+	if (capacity < 0) {
+		dev_err(di->dev, "Could not read ADC: %d\n", capacity);
 		return capacity;
+	}
 
 	return 1280 * (1200 * capacity)/(1024 - capacity);
 }
@@ -213,17 +213,46 @@ static int rx51_battery_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, di);
 
+	di->dev = &pdev->dev;
 	di->bat.name = dev_name(&pdev->dev);
 	di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
 	di->bat.properties = rx51_battery_props;
 	di->bat.num_properties = ARRAY_SIZE(rx51_battery_props);
 	di->bat.get_property = rx51_battery_get_property;
 
+	di->channel_temp = iio_channel_get(di->dev, "temp");
+	if (IS_ERR(di->channel_temp)) {
+		ret = PTR_ERR(di->channel_temp);
+		goto error;
+	}
+
+	di->channel_bsi  = iio_channel_get(di->dev, "bsi");
+	if (IS_ERR(di->channel_bsi)) {
+		ret = PTR_ERR(di->channel_bsi);
+		goto error_channel_temp;
+	}
+
+	di->channel_vbat = iio_channel_get(di->dev, "vbat");
+	if (IS_ERR(di->channel_vbat)) {
+		ret = PTR_ERR(di->channel_vbat);
+		goto error_channel_bsi;
+	}
+
 	ret = power_supply_register(di->dev, &di->bat);
 	if (ret)
-		return ret;
+		goto error_channel_vbat;
 
 	return 0;
+
+error_channel_vbat:
+	iio_channel_release(di->channel_vbat);
+error_channel_bsi:
+	iio_channel_release(di->channel_bsi);
+error_channel_temp:
+	iio_channel_release(di->channel_temp);
+error:
+
+	return ret;
 }
 
 static int rx51_battery_remove(struct platform_device *pdev)
@@ -232,15 +261,28 @@ static int rx51_battery_remove(struct platform_device *pdev)
 
 	power_supply_unregister(&di->bat);
 
+	iio_channel_release(di->channel_vbat);
+	iio_channel_release(di->channel_bsi);
+	iio_channel_release(di->channel_temp);
+
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id n900_battery_of_match[] = {
+	{.compatible = "nokia,n900-battery", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, n900_battery_of_match);
+#endif
+
 static struct platform_driver rx51_battery_driver = {
 	.probe = rx51_battery_probe,
 	.remove = rx51_battery_remove,
 	.driver = {
 		.name = "rx51-battery",
 		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(n900_battery_of_match),
 	},
 };
 module_platform_driver(rx51_battery_driver);

+ 59 - 17
drivers/power/tps65090-charger.c

@@ -17,9 +17,11 @@
  */
 #include <linux/delay.h>
 #include <linux/err.h>
+#include <linux/freezer.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/kthread.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
@@ -32,11 +34,15 @@
 #define TPS65090_VACG		BIT(1)
 #define TPS65090_NOITERM	BIT(5)
 
+#define POLL_INTERVAL		(HZ * 2)	/* Used when no irq */
+
 struct tps65090_charger {
 	struct	device	*dev;
 	int	ac_online;
 	int	prev_ac_online;
 	int	irq;
+	struct task_struct	*poll_task;
+	bool			passive_mode;
 	struct power_supply	ac;
 	struct tps65090_platform_data *pdata;
 };
@@ -49,6 +55,9 @@ static int tps65090_low_chrg_current(struct tps65090_charger *charger)
 {
 	int ret;
 
+	if (charger->passive_mode)
+		return 0;
+
 	ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5,
 			TPS65090_NOITERM);
 	if (ret < 0) {
@@ -64,6 +73,9 @@ static int tps65090_enable_charging(struct tps65090_charger *charger)
 	int ret;
 	uint8_t ctrl0 = 0;
 
+	if (charger->passive_mode)
+		return 0;
+
 	ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0,
 			    &ctrl0);
 	if (ret < 0) {
@@ -87,6 +99,9 @@ static int tps65090_config_charger(struct tps65090_charger *charger)
 	uint8_t intrmask = 0;
 	int ret;
 
+	if (charger->passive_mode)
+		return 0;
+
 	if (charger->pdata->enable_low_current_chrg) {
 		ret = tps65090_low_chrg_current(charger);
 		if (ret < 0) {
@@ -164,10 +179,14 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
 	}
 
 	/* Clear interrupts. */
-	ret = tps65090_write(charger->dev->parent, TPS65090_REG_INTR_STS, 0x00);
-	if (ret < 0) {
-		dev_err(charger->dev, "%s(): Error in writing reg 0x%x\n",
+	if (!charger->passive_mode) {
+		ret = tps65090_write(charger->dev->parent,
+				     TPS65090_REG_INTR_STS, 0x00);
+		if (ret < 0) {
+			dev_err(charger->dev,
+				"%s(): Error in writing reg 0x%x\n",
 				__func__, TPS65090_REG_INTR_STS);
+		}
 	}
 
 	if (charger->prev_ac_online != charger->ac_online)
@@ -198,6 +217,18 @@ static struct tps65090_platform_data *
 
 }
 
+static int tps65090_charger_poll_task(void *data)
+{
+	set_freezable();
+
+	while (!kthread_should_stop()) {
+		schedule_timeout_interruptible(POLL_INTERVAL);
+		try_to_freeze();
+		tps65090_charger_isr(-1, data);
+	}
+	return 0;
+}
+
 static int tps65090_charger_probe(struct platform_device *pdev)
 {
 	struct tps65090_charger *cdata;
@@ -244,22 +275,10 @@ static int tps65090_charger_probe(struct platform_device *pdev)
 	}
 
 	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;
-	}
-
+	if (irq < 0)
+		irq = -ENXIO;
 	cdata->irq = irq;
 
-	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
-		tps65090_charger_isr, 0, "tps65090-charger", cdata);
-	if (ret) {
-		dev_err(cdata->dev, "Unable to register irq %d err %d\n", irq,
-			ret);
-		goto fail_unregister_supply;
-	}
-
 	ret = tps65090_config_charger(cdata);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
@@ -285,6 +304,27 @@ static int tps65090_charger_probe(struct platform_device *pdev)
 		power_supply_changed(&cdata->ac);
 	}
 
+	if (irq != -ENXIO) {
+		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+			tps65090_charger_isr, 0, "tps65090-charger", cdata);
+		if (ret) {
+			dev_err(cdata->dev,
+				"Unable to register irq %d err %d\n", irq,
+				ret);
+			goto fail_unregister_supply;
+		}
+	} else {
+		cdata->poll_task = kthread_run(tps65090_charger_poll_task,
+					      cdata, "ktps65090charger");
+		cdata->passive_mode = true;
+		if (IS_ERR(cdata->poll_task)) {
+			ret = PTR_ERR(cdata->poll_task);
+			dev_err(cdata->dev,
+				"Unable to run kthread err %d\n", ret);
+			goto fail_unregister_supply;
+		}
+	}
+
 	return 0;
 
 fail_unregister_supply:
@@ -297,6 +337,8 @@ static int tps65090_charger_remove(struct platform_device *pdev)
 {
 	struct tps65090_charger *cdata = platform_get_drvdata(pdev);
 
+	if (cdata->irq == -ENXIO)
+		kthread_stop(cdata->poll_task);
 	power_supply_unregister(&cdata->ac);
 
 	return 0;

+ 43 - 1
drivers/power/twl4030_charger.c

@@ -28,10 +28,13 @@
 #define TWL4030_BCIICHG		0x08
 #define TWL4030_BCIVAC		0x0a
 #define TWL4030_BCIVBUS		0x0c
+#define TWL4030_BCIMFSTS3	0x0F
 #define TWL4030_BCIMFSTS4	0x10
 #define TWL4030_BCICTL1		0x23
 #define TWL4030_BB_CFG		0x12
 
+#define TWL4030_BCIMFSTS1	0x01
+
 #define TWL4030_BCIAUTOWEN	BIT(5)
 #define TWL4030_CONFIG_DONE	BIT(4)
 #define TWL4030_BCIAUTOUSB	BIT(1)
@@ -52,6 +55,9 @@
 #define TWL4030_BBISEL_500uA	0x02
 #define TWL4030_BBISEL_1000uA	0x03
 
+#define TWL4030_BATSTSPCHG	BIT(2)
+#define TWL4030_BATSTSMCHG	BIT(6)
+
 /* BCI interrupts */
 #define TWL4030_WOVF		BIT(0) /* Watchdog overflow */
 #define TWL4030_TMOVF		BIT(1) /* Timer overflow */
@@ -144,6 +150,35 @@ static int twl4030bci_read_adc_val(u8 reg)
 	return temp | val;
 }
 
+/*
+ * Check if Battery Pack was present
+ */
+static int twl4030_is_battery_present(struct twl4030_bci *bci)
+{
+	int ret;
+	u8 val = 0;
+
+	/* Battery presence in Main charge? */
+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMFSTS3);
+	if (ret)
+		return ret;
+	if (val & TWL4030_BATSTSMCHG)
+		return 0;
+
+	/*
+	 * OK, It could be that bootloader did not enable main charger,
+	 * pre-charge is h/w auto. So, Battery presence in Pre-charge?
+	 */
+	ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, &val,
+			      TWL4030_BCIMFSTS1);
+	if (ret)
+		return ret;
+	if (val & TWL4030_BATSTSPCHG)
+		return 0;
+
+	return -ENODEV;
+}
+
 /*
  * Check if VBUS power is present
  */
@@ -541,8 +576,14 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
 	bci->irq_chg = platform_get_irq(pdev, 0);
 	bci->irq_bci = platform_get_irq(pdev, 1);
 
-	platform_set_drvdata(pdev, bci);
+	/* Only proceed further *IF* battery is physically present */
+	ret = twl4030_is_battery_present(bci);
+	if  (ret) {
+		dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret);
+		goto fail_no_battery;
+	}
 
+	platform_set_drvdata(pdev, bci);
 	bci->ac.name = "twl4030_ac";
 	bci->ac.type = POWER_SUPPLY_TYPE_MAINS;
 	bci->ac.properties = twl4030_charger_props;
@@ -633,6 +674,7 @@ fail_chg_irq:
 fail_register_usb:
 	power_supply_unregister(&bci->ac);
 fail_register_ac:
+fail_no_battery:
 	kfree(bci);
 
 	return ret;

+ 4 - 0
include/linux/power_supply.h

@@ -120,6 +120,7 @@ enum power_supply_property {
 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
 	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
 	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
+	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
 	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
 	POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
 	POWER_SUPPLY_PROP_ENERGY_FULL,
@@ -131,6 +132,8 @@ enum power_supply_property {
 	POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
 	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
 	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TEMP_MAX,
+	POWER_SUPPLY_PROP_TEMP_MIN,
 	POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
 	POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
 	POWER_SUPPLY_PROP_TEMP_AMBIENT,
@@ -142,6 +145,7 @@ enum power_supply_property {
 	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
 	POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
 	POWER_SUPPLY_PROP_SCOPE,
+	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
 	/* Properties of type `const char *' */
 	POWER_SUPPLY_PROP_MODEL_NAME,
 	POWER_SUPPLY_PROP_MANUFACTURER,