Commit 03d11a0e authored by Linus Torvalds's avatar Linus Torvalds

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

Pull battery updates from Dmitry Eremin-Solenikov:
 "I'm picking up power supply maintainership from Anton Vorontov.  Could
  you please pull battery-2.6 git tree changes prepared for the v3.14
  release.

  Highlights:

   - Power supply notifier

   - Several drivers gained DT support

   - Added Maxim 14577 driver

   - Change of maintainer"

* tag 'for-v3.14' of git://git.infradead.org/battery-2.6:
  MAINTAINERS: Pick up power supply maintainership
  max17042_battery: Add IRQF_ONESHOT flag to use default irq handler
  gpio-charger: Support wakeup events
  power_supply: Add charger support for Maxim 14577
  dt: Binding documentation for isp1704 charger
  isp1704_charger: Add DT support
  charger-manager: of_cm_parse_desc() should be static
  bq2415x_charger: Add DT support
  power_supply: Add power_supply_get_by_phandle
  bq2415x_charger: Use power_supply notifier for automode
  power: reset: Add as3722 power-off driver
  mfd: AS3722: Add dt node properties for system power controller
  charger-manager: Support deivce tree in charger manager driver
  charger-manager: Modify the way of checking battery's temperature
  power_supply: Add power_supply notifier
parents ac266635 57318935
......@@ -112,6 +112,15 @@ Following are properties of regulator subnode.
ams,enable-tracking: Enable tracking with SD1, only supported
by LDO3.
Power-off:
=========
AS3722 supports the system power off by turning off all its rail. This
is provided through pm_power_off.
The device node should have the following properties to enable this
functionality
ams,system-power-controller: Boolean, to enable the power off functionality
through this device.
Example:
--------
#include <dt-bindings/mfd/as3722.h>
......@@ -120,6 +129,8 @@ ams3722 {
compatible = "ams,as3722";
reg = <0x48>;
ams,system-power-controller;
interrupt-parent = <&intc>;
interrupt-controller;
#interrupt-cells = <2>;
......
Binding for NXP ISP1704 USB Charger Detection
Required properties:
- compatible: Should contain one of the following:
* "nxp,isp1704"
- nxp,enable-gpio: Should contain a phandle + gpio-specifier
to the GPIO pin connected to the chip's enable pin.
- usb-phy: Should contain a phandle to the USB PHY
the ISP1704 is connected to.
Example:
isp1704 {
compatible = "nxp,isp1704";
nxp,enable-gpio = <&gpio3 3 GPIO_ACTIVE_LOW>;
usb-phy = <&usb2_phy>;
};
charger-manager bindings
~~~~~~~~~~~~~~~~~~~~~~~~
Required properties :
- compatible : "charger-manager"
- <>-supply : for regulator consumer
- cm-num-chargers : number of chargers
- cm-chargers : name of chargers
- cm-fuel-gauge : name of battery fuel gauge
- subnode <regulator> :
- cm-regulator-name : name of charger regulator
- subnode <cable> :
- cm-cable-name : name of charger cable
- cm-cable-extcon : name of extcon dev
(optional) - cm-cable-min : minimum current of cable
(optional) - cm-cable-max : maximum current of cable
Optional properties :
- cm-name : charger manager's name (default : "battery")
- cm-poll-mode : polling mode (enum polling_modes)
- cm-poll-interval : polling interval
- cm-battery-stat : battery status (enum data_source)
- cm-fullbatt-* : data for full battery checking
- cm-thermal-zone : name of external thermometer's thermal zone
- cm-battery-* : threshold battery temperature for charging
-cold : critical cold temperature of battery for charging
-cold-in-minus : flag that cold temerature is in minus degree
-hot : critical hot temperature of battery for charging
-temp-diff : temperature difference to allow recharging
- cm-dis/charging-max = limits of charging duration
Example :
charger-manager@0 {
compatible = "charger-manager";
chg-reg-supply = <&charger_regulator>;
cm-name = "battery";
/* Always polling ON : 30s */
cm-poll-mode = <1>;
cm-poll-interval = <30000>;
cm-fullbatt-vchkdrop-ms = <30000>;
cm-fullbatt-vchkdrop-volt = <150000>;
cm-fullbatt-soc = <100>;
cm-battery-stat = <3>;
cm-num-chargers = <3>;
cm-chargers = "charger0", "charger1", "charger2";
cm-fuel-gauge = "fuelgauge0";
cm-thermal-zone = "thermal_zone.1"
/* in deci centigrade */
cm-battery-cold = <50>;
cm-battery-cold-in-minus;
cm-battery-hot = <800>;
cm-battery-temp-diff = <100>;
/* Allow charging for 5hr */
cm-charging-max = <18000000>;
/* Allow discharging for 2hr */
cm-discharging-max = <7200000>;
regulator@0 {
cm-regulator-name = "chg-reg";
cable@0 {
cm-cable-name = "USB";
cm-cable-extcon = "extcon-dev.0";
cm-cable-min = <475000>;
cm-cable-max = <500000>;
};
cable@1 {
cm-cable-name = "TA";
cm-cable-extcon = "extcon-dev.0";
cm-cable-min = <650000>;
cm-cable-max = <675000>;
};
};
};
......@@ -6712,7 +6712,7 @@ F: include/linux/timer*
F: kernel/*timer*
POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS
M: Anton Vorontsov <anton@enomsg.org>
M: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
M: David Woodhouse <dwmw2@infradead.org>
T: git git://git.infradead.org/battery-2.6.git
S: Maintained
......
......@@ -317,6 +317,13 @@ config CHARGER_MANAGER
runtime and in suspend-to-RAM by waking up the system periodically
with help of suspend_again support.
config CHARGER_MAX14577
tristate "Maxim MAX14577 MUIC battery charger driver"
depends on MFD_MAX14577
help
Say Y to enable support for the battery charger control sysfs and
platform data of MAX14577 MUICs.
config CHARGER_MAX8997
tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
depends on MFD_MAX8997 && REGULATOR_MAX8997
......
......@@ -48,6 +48,7 @@ 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_MAX14577) += max14577_charger.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
......
/*
* bq2415x charger driver
*
* Copyright (C) 2011-2012 Pali Rohár <pali.rohar@gmail.com>
* Copyright (C) 2011-2013 Pali Rohár <pali.rohar@gmail.com>
*
* 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
......@@ -170,6 +170,8 @@ struct bq2415x_device {
struct bq2415x_platform_data init_data;
struct power_supply charger;
struct delayed_work work;
struct power_supply *notify_psy;
struct notifier_block nb;
enum bq2415x_mode reported_mode;/* mode reported by hook function */
enum bq2415x_mode mode; /* current configured mode */
enum bq2415x_chip chip;
......@@ -795,24 +797,53 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
}
/* hook function called by other driver which set reported mode */
static void bq2415x_hook_function(enum bq2415x_mode mode, void *data)
static int bq2415x_notifier_call(struct notifier_block *nb,
unsigned long val, void *v)
{
struct bq2415x_device *bq = data;
struct bq2415x_device *bq =
container_of(nb, struct bq2415x_device, nb);
struct power_supply *psy = v;
enum bq2415x_mode mode;
union power_supply_propval prop;
int ret;
int mA;
if (!bq)
return;
if (val != PSY_EVENT_PROP_CHANGED)
return NOTIFY_OK;
if (psy != bq->notify_psy)
return NOTIFY_OK;
dev_dbg(bq->dev, "notifier call was called\n");
ret = psy->get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
if (ret != 0)
return NOTIFY_OK;
mA = prop.intval;
if (mA == 0)
mode = BQ2415X_MODE_OFF;
else if (mA < 500)
mode = BQ2415X_MODE_NONE;
else if (mA < 1800)
mode = BQ2415X_MODE_HOST_CHARGER;
else
mode = BQ2415X_MODE_DEDICATED_CHARGER;
if (bq->reported_mode == mode)
return NOTIFY_OK;
dev_dbg(bq->dev, "hook function was called\n");
bq->reported_mode = mode;
/* if automode is not enabled do not tell about reported_mode */
if (bq->automode < 1)
return;
return NOTIFY_OK;
sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode");
bq2415x_set_mode(bq, bq->reported_mode);
return NOTIFY_OK;
}
/**** timer functions ****/
......@@ -1512,9 +1543,11 @@ static int bq2415x_probe(struct i2c_client *client,
int num;
char *name;
struct bq2415x_device *bq;
struct device_node *np = client->dev.of_node;
struct bq2415x_platform_data *pdata = client->dev.platform_data;
if (!client->dev.platform_data) {
dev_err(&client->dev, "platform data not set\n");
if (!np && !pdata) {
dev_err(&client->dev, "platform data missing\n");
return -ENODEV;
}
......@@ -1539,6 +1572,17 @@ static int bq2415x_probe(struct i2c_client *client,
goto error_2;
}
if (np) {
bq->notify_psy = power_supply_get_by_phandle(np, "ti,usb-charger-detection");
if (!bq->notify_psy)
return -EPROBE_DEFER;
}
else if (pdata->notify_device)
bq->notify_psy = power_supply_get_by_name(pdata->notify_device);
else
bq->notify_psy = NULL;
i2c_set_clientdata(client, bq);
bq->id = num;
......@@ -1550,8 +1594,34 @@ static int bq2415x_probe(struct i2c_client *client,
bq->autotimer = 0;
bq->automode = 0;
memcpy(&bq->init_data, client->dev.platform_data,
sizeof(bq->init_data));
if (np) {
ret = of_property_read_u32(np, "ti,current-limit",
&bq->init_data.current_limit);
if (ret)
return ret;
ret = of_property_read_u32(np, "ti,weak-battery-voltage",
&bq->init_data.weak_battery_voltage);
if (ret)
return ret;
ret = of_property_read_u32(np, "ti,battery-regulation-voltage",
&bq->init_data.battery_regulation_voltage);
if (ret)
return ret;
ret = of_property_read_u32(np, "ti,charge-current",
&bq->init_data.charge_current);
if (ret)
return ret;
ret = of_property_read_u32(np, "ti,termination-current",
&bq->init_data.termination_current);
if (ret)
return ret;
ret = of_property_read_u32(np, "ti,resistor-sense",
&bq->init_data.resistor_sense);
if (ret)
return ret;
} else {
memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
}
bq2415x_reset_chip(bq);
......@@ -1573,16 +1643,20 @@ static int bq2415x_probe(struct i2c_client *client,
goto error_4;
}
if (bq->init_data.set_mode_hook) {
if (bq->init_data.set_mode_hook(
bq2415x_hook_function, bq)) {
bq->automode = 1;
bq2415x_set_mode(bq, bq->reported_mode);
dev_info(bq->dev, "automode enabled\n");
} else {
bq->automode = -1;
dev_info(bq->dev, "automode failed\n");
if (bq->notify_psy) {
bq->nb.notifier_call = bq2415x_notifier_call;
ret = power_supply_reg_notifier(&bq->nb);
if (ret) {
dev_err(bq->dev, "failed to reg notifier: %d\n", ret);
goto error_5;
}
/* Query for initial reported_mode and set it */
bq2415x_notifier_call(&bq->nb, PSY_EVENT_PROP_CHANGED, bq->notify_psy);
bq2415x_set_mode(bq, bq->reported_mode);
bq->automode = 1;
dev_info(bq->dev, "automode enabled\n");
} else {
bq->automode = -1;
dev_info(bq->dev, "automode not supported\n");
......@@ -1594,6 +1668,7 @@ static int bq2415x_probe(struct i2c_client *client,
dev_info(bq->dev, "driver registered\n");
return 0;
error_5:
error_4:
bq2415x_sysfs_exit(bq);
error_3:
......@@ -1614,8 +1689,8 @@ static int bq2415x_remove(struct i2c_client *client)
{
struct bq2415x_device *bq = i2c_get_clientdata(client);
if (bq->init_data.set_mode_hook)
bq->init_data.set_mode_hook(NULL, NULL);
if (bq->notify_psy)
power_supply_unreg_notifier(&bq->nb);
bq2415x_sysfs_exit(bq);
bq2415x_power_supply_exit(bq);
......
This diff is collapsed.
......@@ -28,6 +28,7 @@
struct gpio_charger {
const struct gpio_charger_platform_data *pdata;
unsigned int irq;
bool wakeup_enabled;
struct power_supply charger;
};
......@@ -136,6 +137,8 @@ static int gpio_charger_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, gpio_charger);
device_init_wakeup(&pdev->dev, 1);
return 0;
err_gpio_free:
......@@ -159,18 +162,32 @@ static int gpio_charger_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
static int gpio_charger_suspend(struct device *dev)
{
struct gpio_charger *gpio_charger = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
gpio_charger->wakeup_enabled =
enable_irq_wake(gpio_charger->irq);
return 0;
}
static int gpio_charger_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
if (gpio_charger->wakeup_enabled)
disable_irq_wake(gpio_charger->irq);
power_supply_changed(&gpio_charger->charger);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, NULL, gpio_charger_resume);
static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops,
gpio_charger_suspend, gpio_charger_resume);
static struct platform_driver gpio_charger_driver = {
.probe = gpio_charger_probe,
......
......@@ -29,6 +29,8 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/usb/otg.h>
#include <linux/usb/ulpi.h>
......@@ -88,6 +90,8 @@ static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
if (board && board->set_power)
board->set_power(on);
else if (board)
gpio_set_value(board->enable_gpio, on);
}
/*
......@@ -400,12 +404,47 @@ static int isp1704_charger_probe(struct platform_device *pdev)
struct isp1704_charger *isp;
int ret = -ENODEV;
struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev);
struct device_node *np = pdev->dev.of_node;
if (np) {
int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0);
if (gpio < 0)
return gpio;
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct isp1704_charger_data), GFP_KERNEL);
pdata->enable_gpio = gpio;
dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio);
ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
GPIOF_OUT_INIT_HIGH, "isp1704_reset");
if (ret)
goto fail0;
}
if (!pdata) {
dev_err(&pdev->dev, "missing platform data!\n");
return -ENODEV;
}
isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
if (!isp)
return -ENOMEM;
isp->phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(isp->phy))
if (np)
isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
else
isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR(isp->phy)) {
ret = PTR_ERR(isp->phy);
goto fail0;
}
if (!isp->phy)
goto fail0;
isp->dev = &pdev->dev;
......@@ -464,7 +503,6 @@ static int isp1704_charger_probe(struct platform_device *pdev)
power_supply_unregister(&isp->psy);
fail1:
isp1704_charger_set_power(isp, 0);
usb_put_phy(isp->phy);
fail0:
dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
......@@ -477,15 +515,23 @@ static int isp1704_charger_remove(struct platform_device *pdev)
usb_unregister_notifier(isp->phy, &isp->nb);
power_supply_unregister(&isp->psy);
usb_put_phy(isp->phy);
isp1704_charger_set_power(isp, 0);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id omap_isp1704_of_match[] = {
{ .compatible = "nxp,isp1704", },
{},
};
MODULE_DEVICE_TABLE(of, omap_isp1704_of_match);
#endif
static struct platform_driver isp1704_charger_driver = {
.driver = {
.name = "isp1704_charger",
.of_match_table = of_match_ptr(omap_isp1704_of_match),
},
.probe = isp1704_charger_probe,
.remove = isp1704_charger_remove,
......
/*
* Battery charger driver for the Maxim 14577
*
* Copyright (C) 2013 Samsung Electronics
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/mfd/max14577-private.h>
struct max14577_charger {
struct device *dev;
struct max14577 *max14577;
struct power_supply charger;
unsigned int charging_state;
unsigned int battery_state;
};
static int max14577_get_charger_state(struct max14577_charger *chg)
{
struct regmap *rmap = chg->max14577->regmap;
int state = POWER_SUPPLY_STATUS_DISCHARGING;
u8 reg_data;
/*
* Charging occurs only if:
* - CHGCTRL2/MBCHOSTEN == 1
* - STATUS2/CGMBC == 1
*
* TODO:
* - handle FULL after Top-off timer (EOC register may be off
* and the charger won't be charging although MBCHOSTEN is on)
* - handle properly dead-battery charging (respect timer)
* - handle timers (fast-charge and prequal) /MBCCHGERR/
*/
max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL2, &reg_data);
if ((reg_data & CHGCTRL2_MBCHOSTEN_MASK) == 0)
goto state_set;
max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, &reg_data);
if (reg_data & STATUS3_CGMBC_MASK) {
/* Charger or USB-cable is connected */
if (reg_data & STATUS3_EOC_MASK)
state = POWER_SUPPLY_STATUS_FULL;
else
state = POWER_SUPPLY_STATUS_CHARGING;
goto state_set;
}
state_set:
chg->charging_state = state;
return state;
}
/*
* Supported charge types:
* - POWER_SUPPLY_CHARGE_TYPE_NONE
* - POWER_SUPPLY_CHARGE_TYPE_FAST
*/
static int max14577_get_charge_type(struct max14577_charger *chg)
{
/*
* TODO: CHARGE_TYPE_TRICKLE (VCHGR_RC or EOC)?
* As spec says:
* [after reaching EOC interrupt]
* "When the battery is fully charged, the 30-minute (typ)
* top-off timer starts. The device continues to trickle
* charge the battery until the top-off timer runs out."
*/
if (max14577_get_charger_state(chg) == POWER_SUPPLY_STATUS_CHARGING)
return POWER_SUPPLY_CHARGE_TYPE_FAST;
return POWER_SUPPLY_CHARGE_TYPE_NONE;
}
static int max14577_get_online(struct max14577_charger *chg)
{
struct regmap *rmap = chg->max14577->regmap;
u8 reg_data;
max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, &reg_data);
reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
switch (reg_data) {
case MAX14577_CHARGER_TYPE_USB:
case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
case MAX14577_CHARGER_TYPE_SPECIAL_1A:
case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
return 1;
case MAX14577_CHARGER_TYPE_NONE:
case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
case MAX14577_CHARGER_TYPE_RESERVED:
default:
return 0;
}
}
/*
* Supported health statuses:
* - POWER_SUPPLY_HEALTH_DEAD
* - POWER_SUPPLY_HEALTH_OVERVOLTAGE
* - POWER_SUPPLY_HEALTH_GOOD
*/
static int max14577_get_battery_health(struct max14577_charger *chg)
{
struct regmap *rmap = chg->max14577->regmap;
int state = POWER_SUPPLY_HEALTH_GOOD;
u8 reg_data;
max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, &reg_data);
reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
if (reg_data == MAX14577_CHARGER_TYPE_DEAD_BATTERY) {
state = POWER_SUPPLY_HEALTH_DEAD;
goto state_set;
}
max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, &reg_data);
if (reg_data & STATUS3_OVP_MASK) {
state = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
goto state_set;
}
state_set:
chg->battery_state = state;
return state;
}
/*
* Always returns 1.
* The max14577 chip doesn't report any status of battery presence.
* Lets assume that it will always be used with some battery.
*/
static int max14577_get_present(struct max14577_charger *chg)
{
return 1;
}
/*
* Sets charger registers to proper and safe default values.
* Some of these values are equal to defaults in MAX14577E
* data sheet but there are minor differences.
*/
static void max14577_charger_reg_init(struct max14577_charger *chg)
{
struct regmap *rmap = chg->max14577->regmap;
u8 reg_data;
/*
* Charger-Type Manual Detection, default off (set CHGTYPMAN to 0)
* Charger-Detection Enable, default on (set CHGDETEN to 1)
* Combined mask of CHGDETEN and CHGTYPMAN will zero the CHGTYPMAN bit
*/
reg_data = 0x1 << CDETCTRL1_CHGDETEN_SHIFT;
max14577_update_reg(rmap, MAX14577_REG_CDETCTRL1,
CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK,
reg_data);
/* Battery Fast-Charge Timer, from SM-V700: 6hrs */
reg_data = 0x3 << CHGCTRL1_TCHW_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL1, reg_data);
/*
* Wall-Adapter Rapid Charge, default on
* Battery-Charger, default on
*/
reg_data = 0x1 << CHGCTRL2_VCHGR_RC_SHIFT;
reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data);
/* Battery-Charger Constant Voltage (CV) Mode, from SM-V700: 4.35V */
reg_data = 0xf << CHGCTRL3_MBCCVWRC_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL3, reg_data);
/*
* Fast Battery-Charge Current Low, default 200-950mA
* Fast Battery-Charge Current High, from SM-V700: 450mA
*/
reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
reg_data |= 0x5 << CHGCTRL4_MBCICHWRCH_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL4, reg_data);
/* End-of-Charge Current, from SM-V700: 50mA */
reg_data = 0x0 << CHGCTRL5_EOCS_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL5, reg_data);
/* Auto Charging Stop, default off */
reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data);
/* Overvoltage-Protection Threshold, from SM-V700: 6.5V */
reg_data = 0x2 << CHGCTRL7_OTPCGHCVS_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data);
}
/* Support property from charger */
static enum power_supply_property max14577_charger_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
static const char *model_name = "MAX14577";
static const char *manufacturer = "Maxim Integrated";
static int max14577_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct max14577_charger *chg = container_of(psy,
struct max14577_charger,
charger);
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = max14577_get_charger_state(chg);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = max14577_get_charge_type(chg);
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = max14577_get_battery_health(chg);
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = max14577_get_present(chg);
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = max14577_get_online(chg);
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = model_name;
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = manufacturer;
break;
default:
return -EINVAL;
}
return ret;
}
static int max14577_charger_probe(struct platform_device *pdev)
{
struct max14577_charger *chg;
struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent);
int ret;
chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
if (!chg)
return -ENOMEM;
platform_set_drvdata(pdev, chg);
chg->dev = &pdev->dev;
chg->max14577 = max14577;
max14577_charger_reg_init(chg);
chg->charger.name = "max14577-charger",
chg->charger.type = POWER_SUPPLY_TYPE_BATTERY,
chg->charger.properties = max14577_charger_props,
chg->charger.num_properties = ARRAY_SIZE(max14577_charger_props),
chg->charger.get_property = max14577_charger_get_property,
ret = power_supply_register(&pdev->dev, &chg->charger);
if (ret) {
dev_err(&pdev->dev, "failed: power supply register\n");
return ret;
}
return 0;
}
static int max14577_charger_remove(struct platform_device *pdev)
{
struct max14577_charger *chg = platform_get_drvdata(pdev);
power_supply_unregister(&chg->charger);
return 0;
}
static struct platform_driver max14577_charger_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "max14577-charger",
},
.probe = max14577_charger_probe,
.remove = max14577_charger_remove,
};
module_platform_driver(max14577_charger_driver);
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
MODULE_DESCRIPTION("MAXIM 14577 charger driver");
MODULE_LICENSE("GPL");
......@@ -741,9 +741,9 @@ static int max17042_probe(struct i2c_client *client,
if (client->irq) {
ret = request_threaded_irq(client->irq, NULL,
max17042_thread_handler,
IRQF_TRIGGER_FALLING,
chip->battery.name, chip);
max17042_thread_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
chip->battery.name, chip);
if (!ret) {
regmap_read(chip->regmap, MAX17042_CONFIG, &val);
val |= CONFIG_ALRT_BIT_ENBL;
......
......@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/err.h>
#include <linux/power_supply.h>
#include <linux/thermal.h>
......@@ -24,6 +25,9 @@
struct class *power_supply_class;
EXPORT_SYMBOL_GPL(power_supply_class);
ATOMIC_NOTIFIER_HEAD(power_supply_notifier);
EXPORT_SYMBOL_GPL(power_supply_notifier);
static struct device_type power_supply_dev_type;
static bool __power_supply_is_supplied_by(struct power_supply *supplier,
......@@ -80,6 +84,8 @@ static void power_supply_changed_work(struct work_struct *work)
class_for_each_device(power_supply_class, NULL, psy,
__power_supply_changed_work);
power_supply_update_leds(psy);
atomic_notifier_call_chain(&power_supply_notifier,
PSY_EVENT_PROP_CHANGED, psy);
kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
spin_lock_irqsave(&psy->changed_lock, flags);
}
......@@ -335,6 +341,32 @@ struct power_supply *power_supply_get_by_name(const char *name)
}
EXPORT_SYMBOL_GPL(power_supply_get_by_name);
#ifdef CONFIG_OF
static int power_supply_match_device_node(struct device *dev, const void *data)
{
return dev->parent && dev->parent->of_node == data;
}
struct power_supply *power_supply_get_by_phandle(struct device_node *np,
const char *property)
{
struct device_node *power_supply_np;
struct device *dev;
power_supply_np = of_parse_phandle(np, property, 0);
if (!power_supply_np)
return ERR_PTR(-ENODEV);
dev = class_find_device(power_supply_class, NULL, power_supply_np,
power_supply_match_device_node);
of_node_put(power_supply_np);
return dev ? dev_get_drvdata(dev) : NULL;
}
EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
#endif /* CONFIG_OF */
int power_supply_powers(struct power_supply *psy, struct device *dev)
{
return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers");
......@@ -347,6 +379,18 @@ static void power_supply_dev_release(struct device *dev)
kfree(dev);
}
int power_supply_reg_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&power_supply_notifier, nb);
}
EXPORT_SYMBOL_GPL(power_supply_reg_notifier);
void power_supply_unreg_notifier(struct notifier_block *nb)
{
atomic_notifier_chain_unregister(&power_supply_notifier, nb);
}
EXPORT_SYMBOL_GPL(power_supply_unreg_notifier);
#ifdef CONFIG_THERMAL
static int power_supply_read_temp(struct thermal_zone_device *tzd,
unsigned long *temp)
......
......@@ -6,6 +6,12 @@ menuconfig POWER_RESET
Say Y here to enable board reset and power off
config POWER_RESET_AS3722
bool "ams AS3722 power-off driver"
depends on MFD_AS3722 && POWER_RESET
help
This driver supports turning off board via a ams AS3722 power-off.
config POWER_RESET_GPIO
bool "GPIO power-off driver"
depends on OF_GPIO && POWER_RESET
......
obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.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
......
/*
* Power off driver for ams AS3722 device.
*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
*
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*
* 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.
*/
#include <linux/mfd/as3722.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
struct as3722_poweroff {
struct device *dev;
struct as3722 *as3722;
};
static struct as3722_poweroff *as3722_pm_poweroff;
static void as3722_pm_power_off(void)
{
int ret;
if (!as3722_pm_poweroff) {
pr_err("AS3722 poweroff is not initialised\n");
return;
}
ret = as3722_update_bits(as3722_pm_poweroff->as3722,
AS3722_RESET_CONTROL_REG, AS3722_POWER_OFF, AS3722_POWER_OFF);
if (ret < 0)
dev_err(as3722_pm_poweroff->dev,
"RESET_CONTROL_REG update failed, %d\n", ret);
}
static int as3722_poweroff_probe(struct platform_device *pdev)
{
struct as3722_poweroff *as3722_poweroff;
struct device_node *np = pdev->dev.parent->of_node;
if (!np)
return -EINVAL;
if (!of_property_read_bool(np, "ams,system-power-controller"))
return 0;
as3722_poweroff = devm_kzalloc(&pdev->dev, sizeof(*as3722_poweroff),
GFP_KERNEL);
if (!as3722_poweroff)
return -ENOMEM;
as3722_poweroff->as3722 = dev_get_drvdata(pdev->dev.parent);
as3722_poweroff->dev = &pdev->dev;
as3722_pm_poweroff = as3722_poweroff;
if (!pm_power_off)
pm_power_off = as3722_pm_power_off;
return 0;
}
static int as3722_poweroff_remove(struct platform_device *pdev)
{
if (pm_power_off == as3722_pm_power_off)
pm_power_off = NULL;
as3722_pm_poweroff = NULL;
return 0;
}
static struct platform_driver as3722_poweroff_driver = {
.driver = {
.name = "as3722-power-off",
.owner = THIS_MODULE,
},
.probe = as3722_poweroff_probe,
.remove = as3722_poweroff_remove,
};
module_platform_driver(as3722_poweroff_driver);
MODULE_DESCRIPTION("Power off driver for ams AS3722 PMIC Device");
MODULE_ALIAS("platform:as3722-power-off");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_LICENSE("GPL v2");
/*
* bq2415x charger driver
*
* Copyright (C) 2011-2012 Pali Rohár <pali.rohar@gmail.com>
* Copyright (C) 2011-2013 Pali Rohár <pali.rohar@gmail.com>
*
* 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
......@@ -31,46 +31,9 @@
* termination current. It it is less or equal to zero, configuring charge
* and termination current will not be possible.
*
* Function set_mode_hook is needed for automode (setting correct current
* limit when charger is connected/disconnected or setting boost mode).
* When is NULL, automode function is disabled. When is not NULL, it must
* have this prototype:
*
* int (*set_mode_hook)(
* void (*hook)(enum bq2415x_mode mode, void *data),
* void *data)
*
* hook is hook function (see below) and data is pointer to driver private
* data
*
* bq2415x driver will call it as:
*
* platform_data->set_mode_hook(bq2415x_hook_function, bq2415x_device);
*
* Board/platform function set_mode_hook return non zero value when hook
* function was successful registered. Platform code should call that hook
* function (which get from pointer, with data) every time when charger
* was connected/disconnected or require to enable boost mode. bq2415x
* driver then will set correct current limit, enable/disable charger or
* boost mode.
*
* Hook function has this prototype:
*
* void hook(enum bq2415x_mode mode, void *data);
*
* mode is bq2415x mode (charger or boost)
* data is pointer to driver private data (which get from
* set_charger_type_hook)
*
* When bq driver is being unloaded, it call function:
*
* platform_data->set_mode_hook(NULL, NULL);
*
* (hook function and driver private data are NULL)
*
* After that board/platform code must not call driver hook function! It
* is possible that pointer to hook function will not be valid and calling
* will cause undefined result.
* For automode support is needed to provide name of power supply device
* in value notify_device. Device driver must immediately report property
* POWER_SUPPLY_PROP_CURRENT_MAX when current changed.
*/
/* Supported modes with maximal current limit */
......@@ -89,8 +52,7 @@ struct bq2415x_platform_data {
int charge_current; /* mA */
int termination_current; /* mA */
int resistor_sense; /* m ohm */
int (*set_mode_hook)(void (*hook)(enum bq2415x_mode mode, void *data),
void *data);
const char *notify_device; /* name */
};
#endif
......@@ -37,6 +37,8 @@ enum cm_event_types {
CM_EVENT_BATT_FULL,
CM_EVENT_BATT_IN,
CM_EVENT_BATT_OUT,
CM_EVENT_BATT_OVERHEAT,
CM_EVENT_BATT_COLD,
CM_EVENT_EXT_PWR_IN_OUT,
CM_EVENT_CHG_START_STOP,
CM_EVENT_OTHERS,
......@@ -173,11 +175,10 @@ struct charger_regulator {
* @num_charger_regulator: the number of entries in charger_regulators
* @charger_regulators: array of charger regulators
* @psy_fuel_gauge: the name of power-supply for fuel gauge
* @temperature_out_of_range:
* Determine whether the status is overheat or cold or normal.
* return_value > 0: overheat
* return_value == 0: normal
* return_value < 0: cold
* @thermal_zone : the name of thermal zone for battery
* @temp_min : Minimum battery temperature for charging.
* @temp_max : Maximum battery temperature for charging.
* @temp_diff : Temperature diffential to restart charging.
* @measure_battery_temp:
* true: measure battery temperature
* false: measure ambient temperature
......@@ -190,7 +191,7 @@ struct charger_regulator {
* max_duration_ms', cm start charging.
*/
struct charger_desc {
char *psy_name;
const char *psy_name;
enum polling_modes polling_mode;
unsigned int polling_interval_ms;
......@@ -203,18 +204,23 @@ struct charger_desc {
enum data_source battery_present;
char **psy_charger_stat;
const char **psy_charger_stat;
int num_charger_regulators;
struct charger_regulator *charger_regulators;
char *psy_fuel_gauge;
const char *psy_fuel_gauge;
const char *thermal_zone;
int temp_min;
int temp_max;
int temp_diff;
int (*temperature_out_of_range)(int *mC);
bool measure_battery_temp;
u64 charging_max_duration_ms;
u64 discharging_max_duration_ms;
u32 charging_max_duration_ms;
u32 discharging_max_duration_ms;
};
#define PSY_NAME_MAX 30
......@@ -226,13 +232,13 @@ struct charger_desc {
* @desc: instance of charger_desc
* @fuel_gauge: power_supply for fuel gauge
* @charger_stat: array of power_supply for chargers
* @tzd_batt : thermal zone device for battery
* @charger_enabled: the state of charger
* @fullbatt_vchk_jiffies_at:
* jiffies at the time full battery check will occur.
* @fullbatt_vchk_work: work queue for full battery check
* @emergency_stop:
* When setting true, stop charging
* @last_temp_mC: the measured temperature in milli-Celsius
* @psy_name_buf: the name of power-supply-class for charger manager
* @charger_psy: power_supply for charger manager
* @status_save_ext_pwr_inserted:
......@@ -250,13 +256,15 @@ struct charger_manager {
struct power_supply *fuel_gauge;
struct power_supply **charger_stat;
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd_batt;
#endif
bool charger_enabled;
unsigned long fullbatt_vchk_jiffies_at;
struct delayed_work fullbatt_vchk_work;
int emergency_stop;
int last_temp_mC;
char psy_name_buf[PSY_NAME_MAX + 1];
struct power_supply charger_psy;
......
......@@ -24,6 +24,7 @@
struct isp1704_charger_data {
void (*set_power)(bool on);
int enable_gpio;
};
#endif
......@@ -16,6 +16,7 @@
#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
struct device;
......@@ -158,6 +159,10 @@ enum power_supply_type {
POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
};
enum power_supply_notifier_events {
PSY_EVENT_PROP_CHANGED,
};
union power_supply_propval {
int intval;
const char *strval;
......@@ -235,7 +240,18 @@ struct power_supply_info {
int use_for_apm;
};
extern struct atomic_notifier_head power_supply_notifier;
extern int power_supply_reg_notifier(struct notifier_block *nb);
extern void power_supply_unreg_notifier(struct notifier_block *nb);
extern struct power_supply *power_supply_get_by_name(const char *name);
#ifdef CONFIG_OF
extern struct power_supply *power_supply_get_by_phandle(struct device_node *np,
const char *property);
#else /* !CONFIG_OF */
static inline struct power_supply *
power_supply_get_by_phandle(struct device_node *np, const char *property)
{ return NULL; }
#endif /* CONFIG_OF */
extern void power_supply_changed(struct power_supply *psy);
extern int power_supply_am_i_supplied(struct power_supply *psy);
extern int power_supply_set_battery_charged(struct power_supply *psy);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment