提交 13c07190 编写于 作者: L Linus Torvalds

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

Pull power supply and reset changes from Sebastian Reichel:
 "New drivers:
   - charger driver for Maxim 77693
   - battery gauge driver for LTC 2941/2943
   - battery gauge driver for RT5033
   - reset driver for R-Mobile platforms

  Convert drivers to restart handler framework:
   - arm-versatile
   - at91
   - st-poweroff

  Misc:
   - remove deprecated sun6i reboot driver
   - use alarmtimer instead of rtc in charger-manager
   - misc fixes"

* tag 'for-v3.20' of git://git.infradead.org/battery-2.6: (48 commits)
  power_supply: 88pm860x: Fix leaked power supply on probe fail
  power/reset: restart-poweroff: Remove arm dependencies
  power/reset: st-poweroff: Fix misleading Kconfig description
  power/reset: st-poweroff: Register with kernel restart handler
  power/reset: Remove sun6i reboot driver
  power/reset: at91: Register with kernel restart handler
  power/reset: arm-versatile: Register with kernel restart handler
  power: test_power: Use enum as index for array of supplies
  Add devicetree binding documentation for the LTC2941/LTC2943 driver
  Add LTC2941/LTC2943 Battery Gauge Driver
  power/reset: brcmstb: Add support for old 65nm chips
  power/reset: brcmstb: Use the DT "compatible" string to indicate bit positions
  power/reset: brcmstb: Make the driver buildable on MIPS
  power: charger-manager: Use alarmtimer for battery monitoring in suspend.
  power/reset: at91-poweroff: Fix error handling and other compiler warnings
  bq27x00_battery: Call power_supply_changed only when capacity changed
  bq27x00_battery: fix register offset for bq27425
  power: max14577: Remove SYSFS dependency from Kconfig
  power: bq24190_charger: suppress build warning
  power: reset: Add reset driver for R-Mobile platforms
  ...
...@@ -32,3 +32,45 @@ Description: ...@@ -32,3 +32,45 @@ Description:
Valid values: Valid values:
- 5, 6 or 7 (hours), - 5, 6 or 7 (hours),
- 0: disabled. - 0: disabled.
What: /sys/class/power_supply/max77693-charger/device/fast_charge_timer
Date: January 2015
KernelVersion: 3.19.0
Contact: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Description:
This entry shows and sets the maximum time the max77693
charger operates in fast-charge mode. When the timer expires
the device will terminate fast-charge mode (charging current
will drop to 0 A) and will trigger interrupt.
Valid values:
- 4 - 16 (hours), step by 2 (rounded down)
- 0: disabled.
What: /sys/class/power_supply/max77693-charger/device/top_off_threshold_current
Date: January 2015
KernelVersion: 3.19.0
Contact: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Description:
This entry shows and sets the charging current threshold for
entering top-off charging mode. When charging current in fast
charge mode drops below this value, the charger will trigger
interrupt and start top-off charging mode.
Valid values:
- 100000 - 200000 (microamps), step by 25000 (rounded down)
- 200000 - 350000 (microamps), step by 50000 (rounded down)
- 0: disabled.
What: /sys/class/power_supply/max77693-charger/device/top_off_timer
Date: January 2015
KernelVersion: 3.19.0
Contact: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Description:
This entry shows and sets the maximum time the max77693
charger operates in top-off charge mode. When the timer expires
the device will terminate top-off charge mode (charging current
will drop to 0 A) and will trigger interrupt.
Valid values:
- 0 - 70 (minutes), step by 10 (rounded down)
...@@ -79,7 +79,9 @@ reboot ...@@ -79,7 +79,9 @@ reboot
Required properties Required properties
- compatible - compatible
The string property "brcm,brcmstb-reboot". The string property "brcm,brcmstb-reboot" for 40nm/28nm chips with
the new SYS_CTRL interface, or "brcm,bcm7038-reboot" for 65nm
chips with the old SUN_TOP_CTRL interface.
- syscon - syscon
A phandle / integer array that points to the syscon node which describes A phandle / integer array that points to the syscon node which describes
......
...@@ -41,6 +41,41 @@ Optional properties: ...@@ -41,6 +41,41 @@ Optional properties:
To get more informations, please refer to documentaion. To get more informations, please refer to documentaion.
[*] refer Documentation/devicetree/bindings/pwm/pwm.txt [*] refer Documentation/devicetree/bindings/pwm/pwm.txt
- charger : Node configuring the charger driver.
If present, required properties:
- compatible : Must be "maxim,max77693-charger".
Optional properties (if not set, defaults will be used):
- maxim,constant-microvolt : Battery constant voltage in uV. The charger
will operate in fast charge constant current mode till battery voltage
reaches this level. Then the charger will switch to fast charge constant
voltage mode. Also vsys (system voltage) will be set to this value when
DC power is supplied but charger is not enabled.
Valid values: 3650000 - 4400000, step by 25000 (rounded down)
Default: 4200000
- maxim,min-system-microvolt : Minimal system voltage in uV.
Valid values: 3000000 - 3700000, step by 100000 (rounded down)
Default: 3600000
- maxim,thermal-regulation-celsius : Temperature in Celsius for entering
high temperature charging mode. If die temperature exceeds this value
the charging current will be reduced by 105 mA/Celsius.
Valid values: 70, 85, 100, 115
Default: 100
- maxim,battery-overcurrent-microamp : Overcurrent protection threshold
in uA (current from battery to system).
Valid values: 2000000 - 3500000, step by 250000 (rounded down)
Default: 3500000
- maxim,charge-input-threshold-microvolt : Threshold voltage in uV for
triggering input voltage regulation loop. If input voltage decreases
below this value, the input current will be reduced to reach the
threshold voltage.
Valid values: 4300000, 4700000, 4800000, 4900000
Default: 4300000
Example: Example:
max77693@66 { max77693@66 {
compatible = "maxim,max77693"; compatible = "maxim,max77693";
...@@ -73,4 +108,14 @@ Example: ...@@ -73,4 +108,14 @@ Example:
pwms = <&pwm 0 40000 0>; pwms = <&pwm 0 40000 0>;
pwm-names = "haptic"; pwm-names = "haptic";
}; };
charger {
compatible = "maxim,max77693-charger";
maxim,constant-microvolt = <4200000>;
maxim,min-system-microvolt = <3600000>;
maxim,thermal-regulation-celsius = <75>;
maxim,battery-overcurrent-microamp = <3000000>;
maxim,charge-input-threshold-microvolt = <4300000>;
};
}; };
binding for LTC2941 and LTC2943 battery gauges
Both the LTC2941 and LTC2943 measure battery capacity.
The LTC2943 is compatible with the LTC2941, it adds voltage and
temperature monitoring, and uses a slightly different conversion
formula for the charge counter.
Required properties:
- compatible: Should contain "ltc2941" or "ltc2943" which also indicates the
type of I2C chip attached.
- reg: The 7-bit I2C address.
- lltc,resistor-sense: The sense resistor value in milli-ohms. Can be a 32-bit
negative value when the battery has been connected to the wrong end of the
resistor.
- lltc,prescaler-exponent: The prescaler exponent as explained in the datasheet.
This determines the range and accuracy of the gauge. The value is programmed
into the chip only if it differs from the current setting. The setting is
lost when the battery is disconnected.
Example from the Topic Miami Florida board:
fuelgauge: ltc2943@64 {
compatible = "ltc2943";
reg = <0x64>;
lltc,resistor-sense = <15>;
lltc,prescaler-exponent = <5>; /* 2^(2*5) = 1024 */
};
Binding for the LTC2952 PowerPath controller Binding for the LTC2952 PowerPath controller
This chip is used to externally trigger a system shut down. Once the trigger has This chip is used to externally trigger a system shut down. Once the trigger has
been sent, the chips' watchdog has to be reset to gracefully shut down. been sent, the chip's watchdog has to be reset to gracefully shut down.
If the Linux systems decides to shut down it powers off the platform via the A full powerdown can be triggered via the kill signal.
kill signal.
Required properties: Required properties:
- compatible: Must contain: "lltc,ltc2952" - compatible: Must contain: "lltc,ltc2952"
- trigger-gpios: phandle + gpio-specifier for the GPIO connected to the
chip's trigger line
- watchdog-gpios: phandle + gpio-specifier for the GPIO connected to the - watchdog-gpios: phandle + gpio-specifier for the GPIO connected to the
chip's watchdog line chip's watchdog line
- kill-gpios: phandle + gpio-specifier for the GPIO connected to the - kill-gpios: phandle + gpio-specifier for the GPIO connected to the
chip's kill line chip's kill line
Optional properties:
- trigger-gpios: phandle + gpio-specifier for the GPIO connected to the
chip's trigger line. If this property is not set, the
trigger function is ignored and the chip is kept alive
until an explicit kill signal is received
Example: Example:
ltc2952 { ltc2952 {
......
...@@ -6165,6 +6165,13 @@ F: Documentation/devicetree/bindings/i2c/max6697.txt ...@@ -6165,6 +6165,13 @@ F: Documentation/devicetree/bindings/i2c/max6697.txt
F: drivers/hwmon/max6697.c F: drivers/hwmon/max6697.c
F: include/linux/platform_data/max6697.h F: include/linux/platform_data/max6697.h
MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
M: Krzysztof Kozlowski <k.kozlowski@samsung.com>
L: linux-pm@vger.kernel.org
S: Supported
F: drivers/power/max14577_charger.c
F: drivers/power/max77693_charger.c
MAXIRADIO FM RADIO RECEIVER DRIVER MAXIRADIO FM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl> M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org L: linux-media@vger.kernel.org
......
...@@ -711,6 +711,7 @@ static int pm860x_charger_probe(struct platform_device *pdev) ...@@ -711,6 +711,7 @@ static int pm860x_charger_probe(struct platform_device *pdev)
return 0; return 0;
out_irq: out_irq:
power_supply_unregister(&info->usb);
while (--i >= 0) while (--i >= 0)
free_irq(info->irq[i], info); free_irq(info->irq[i], info);
out: out:
......
...@@ -315,7 +315,7 @@ config CHARGER_GPIO ...@@ -315,7 +315,7 @@ config CHARGER_GPIO
config CHARGER_MANAGER config CHARGER_MANAGER
bool "Battery charger manager for multiple chargers" bool "Battery charger manager for multiple chargers"
depends on REGULATOR && RTC_CLASS depends on REGULATOR
select EXTCON select EXTCON
help help
Say Y to enable charger-manager support, which allows multiple Say Y to enable charger-manager support, which allows multiple
...@@ -327,11 +327,16 @@ config CHARGER_MANAGER ...@@ -327,11 +327,16 @@ config CHARGER_MANAGER
config CHARGER_MAX14577 config CHARGER_MAX14577
tristate "Maxim MAX14577/77836 battery charger driver" tristate "Maxim MAX14577/77836 battery charger driver"
depends on MFD_MAX14577 depends on MFD_MAX14577
depends on SYSFS
help help
Say Y to enable support for the battery charger control sysfs and Say Y to enable support for the battery charger control sysfs and
platform data of MAX14577/77836 MUICs. platform data of MAX14577/77836 MUICs.
config CHARGER_MAX77693
tristate "Maxim MAX77693 battery charger driver"
depends on MFD_MAX77693
help
Say Y to enable support for the Maxim MAX77693 battery charger.
config CHARGER_MAX8997 config CHARGER_MAX8997
tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver" tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
depends on MFD_MAX8997 && REGULATOR_MAX8997 depends on MFD_MAX8997 && REGULATOR_MAX8997
...@@ -383,6 +388,14 @@ config CHARGER_TPS65090 ...@@ -383,6 +388,14 @@ config CHARGER_TPS65090
Say Y here to enable support for battery charging with TPS65090 Say Y here to enable support for battery charging with TPS65090
PMIC chips. PMIC chips.
config BATTERY_GAUGE_LTC2941
tristate "LTC2941/LTC2943 Battery Gauge Driver"
depends on I2C
help
Say Y here to include support for LTC2941 and LTC2943 Battery
Gauge IC. The driver reports the charge count continuously, and
measures the voltage and temperature every 10 seconds.
config AB8500_BM config AB8500_BM
bool "AB8500 Battery Management Driver" bool "AB8500 Battery Management Driver"
depends on AB8500_CORE && AB8500_GPADC depends on AB8500_CORE && AB8500_GPADC
...@@ -397,6 +410,14 @@ config BATTERY_GOLDFISH ...@@ -397,6 +410,14 @@ config BATTERY_GOLDFISH
Say Y to enable support for the battery and AC power in the Say Y to enable support for the battery and AC power in the
Goldfish emulator. Goldfish emulator.
config BATTERY_RT5033
tristate "RT5033 fuel gauge support"
depends on MFD_RT5033
help
This adds support for battery fuel gauge in Richtek RT5033 PMIC.
The fuelgauge calculates and determines the battery state of charge
according to battery open circuit voltage.
source "drivers/power/reset/Kconfig" source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY endif # POWER_SUPPLY
......
...@@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o ...@@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
obj-$(CONFIG_BATTERY_GAUGE_LTC2941) += ltc2941-battery-gauge.o
obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
...@@ -34,6 +35,7 @@ obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o ...@@ -34,6 +35,7 @@ obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o
obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
...@@ -50,6 +52,7 @@ obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o ...@@ -50,6 +52,7 @@ obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
......
...@@ -2435,20 +2435,6 @@ static void ab8500_fg_reinit_work(struct work_struct *work) ...@@ -2435,20 +2435,6 @@ static void ab8500_fg_reinit_work(struct work_struct *work)
} }
} }
/**
* ab8500_fg_reinit() - forces FG algorithm to reinitialize with current values
*
* This function can be used to force the FG algorithm to recalculate a new
* voltage based battery capacity.
*/
void ab8500_fg_reinit(void)
{
struct ab8500_fg *di = ab8500_fg_get();
/* User won't be notified if a null pointer returned. */
if (di != NULL)
queue_delayed_work(di->fg_wq, &di->fg_reinit_work, 0);
}
/* Exposure to the sysfs interface */ /* Exposure to the sysfs interface */
struct ab8500_fg_sysfs_entry { struct ab8500_fg_sysfs_entry {
......
...@@ -929,7 +929,7 @@ static void bq24190_charger_init(struct power_supply *charger) ...@@ -929,7 +929,7 @@ static void bq24190_charger_init(struct power_supply *charger)
charger->properties = bq24190_charger_properties; charger->properties = bq24190_charger_properties;
charger->num_properties = ARRAY_SIZE(bq24190_charger_properties); charger->num_properties = ARRAY_SIZE(bq24190_charger_properties);
charger->supplied_to = bq24190_charger_supplied_to; charger->supplied_to = bq24190_charger_supplied_to;
charger->num_supplies = ARRAY_SIZE(bq24190_charger_supplied_to); charger->num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to);
charger->get_property = bq24190_charger_get_property; charger->get_property = bq24190_charger_get_property;
charger->set_property = bq24190_charger_set_property; charger->set_property = bq24190_charger_set_property;
charger->property_is_writeable = bq24190_charger_property_is_writeable; charger->property_is_writeable = bq24190_charger_property_is_writeable;
...@@ -1208,7 +1208,7 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) ...@@ -1208,7 +1208,7 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
{ {
struct bq24190_dev_info *bdi = data; struct bq24190_dev_info *bdi = data;
bool alert_userspace = false; bool alert_userspace = false;
u8 ss_reg, f_reg; u8 ss_reg = 0, f_reg = 0;
int ret; int ret;
pm_runtime_get_sync(bdi->dev); pm_runtime_get_sync(bdi->dev);
......
...@@ -76,7 +76,8 @@ ...@@ -76,7 +76,8 @@
/* bq27425 register addresses are same as bq27x00 addresses minus 4 */ /* bq27425 register addresses are same as bq27x00 addresses minus 4 */
#define BQ27425_REG_OFFSET 0x04 #define BQ27425_REG_OFFSET 0x04
#define BQ27425_REG_SOC 0x18 /* Register address plus offset */ #define BQ27425_REG_SOC (0x1C + BQ27425_REG_OFFSET)
#define BQ27425_REG_DCAP (0x3C + BQ27425_REG_OFFSET)
#define BQ27000_RS 20 /* Resistor sense */ #define BQ27000_RS 20 /* Resistor sense */
#define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000) #define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000)
...@@ -282,9 +283,12 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) ...@@ -282,9 +283,12 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
{ {
int ilmd; int ilmd;
if (bq27xxx_is_chip_version_higher(di)) if (bq27xxx_is_chip_version_higher(di)) {
ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); if (di->chip == BQ27425)
else ilmd = bq27x00_read(di, BQ27425_REG_DCAP, false);
else
ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
} else
ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
if (ilmd < 0) { if (ilmd < 0) {
...@@ -493,10 +497,11 @@ static void bq27x00_update(struct bq27x00_device_info *di) ...@@ -493,10 +497,11 @@ static void bq27x00_update(struct bq27x00_device_info *di)
di->charge_design_full = bq27x00_battery_read_ilmd(di); di->charge_design_full = bq27x00_battery_read_ilmd(di);
} }
if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) { if (di->cache.capacity != cache.capacity)
di->cache = cache;
power_supply_changed(&di->bat); power_supply_changed(&di->bat);
}
if (memcmp(&di->cache, &cache, sizeof(cache)) != 0)
di->cache = cache;
di->last_update = jiffies; di->last_update = jiffies;
} }
......
...@@ -69,16 +69,10 @@ static LIST_HEAD(cm_list); ...@@ -69,16 +69,10 @@ static LIST_HEAD(cm_list);
static DEFINE_MUTEX(cm_list_mtx); static DEFINE_MUTEX(cm_list_mtx);
/* About in-suspend (suspend-again) monitoring */ /* About in-suspend (suspend-again) monitoring */
static struct rtc_device *rtc_dev; static struct alarm *cm_timer;
/*
* Backup RTC alarm
* Save the wakeup alarm before entering suspend-to-RAM
*/
static struct rtc_wkalrm rtc_wkalarm_save;
/* Backup RTC alarm time in terms of seconds since 01-01-1970 00:00:00 */
static unsigned long rtc_wkalarm_save_time;
static bool cm_suspended; static bool cm_suspended;
static bool cm_rtc_set; static bool cm_timer_set;
static unsigned long cm_suspend_duration_ms; static unsigned long cm_suspend_duration_ms;
/* About normal (not suspended) monitoring */ /* About normal (not suspended) monitoring */
...@@ -87,9 +81,6 @@ static unsigned long next_polling; /* Next appointed polling time */ ...@@ -87,9 +81,6 @@ static unsigned long next_polling; /* Next appointed polling time */
static struct workqueue_struct *cm_wq; /* init at driver add */ static struct workqueue_struct *cm_wq; /* init at driver add */
static struct delayed_work cm_monitor_work; /* init at driver add */ static struct delayed_work cm_monitor_work; /* init at driver add */
/* Global charger-manager description */
static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
/** /**
* is_batt_present - See if the battery presents in place. * is_batt_present - See if the battery presents in place.
* @cm: the Charger Manager representing the battery. * @cm: the Charger Manager representing the battery.
...@@ -1047,10 +1038,13 @@ static bool cm_setup_timer(void) ...@@ -1047,10 +1038,13 @@ static bool cm_setup_timer(void)
{ {
struct charger_manager *cm; struct charger_manager *cm;
unsigned int wakeup_ms = UINT_MAX; unsigned int wakeup_ms = UINT_MAX;
bool ret = false; int timer_req = 0;
mutex_lock(&cm_list_mtx); if (time_after(next_polling, jiffies))
CM_MIN_VALID(wakeup_ms,
jiffies_to_msecs(next_polling - jiffies));
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry) { list_for_each_entry(cm, &cm_list, entry) {
unsigned int fbchk_ms = 0; unsigned int fbchk_ms = 0;
...@@ -1070,162 +1064,38 @@ static bool cm_setup_timer(void) ...@@ -1070,162 +1064,38 @@ static bool cm_setup_timer(void)
/* Skip if polling is not required for this CM */ /* Skip if polling is not required for this CM */
if (!is_polling_required(cm) && !cm->emergency_stop) if (!is_polling_required(cm) && !cm->emergency_stop)
continue; continue;
timer_req++;
if (cm->desc->polling_interval_ms == 0) if (cm->desc->polling_interval_ms == 0)
continue; continue;
CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms); CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
} }
mutex_unlock(&cm_list_mtx); mutex_unlock(&cm_list_mtx);
if (wakeup_ms < UINT_MAX && wakeup_ms > 0) { if (timer_req && cm_timer) {
pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); ktime_t now, add;
if (rtc_dev) {
struct rtc_wkalrm tmp;
unsigned long time, now;
unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000);
/*
* Set alarm with the polling interval (wakeup_ms)
* except when rtc_wkalarm_save comes first.
* However, the alarm time should be NOW +
* CM_RTC_SMALL or later.
*/
tmp.enabled = 1;
rtc_read_time(rtc_dev, &tmp.time);
rtc_tm_to_time(&tmp.time, &now);
if (add < CM_RTC_SMALL)
add = CM_RTC_SMALL;
time = now + add;
ret = true; /*
* Set alarm with the polling interval (wakeup_ms)
* The alarm time should be NOW + CM_RTC_SMALL or later.
*/
if (wakeup_ms == UINT_MAX ||
wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC)
wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC;
if (rtc_wkalarm_save.enabled && pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms);
rtc_wkalarm_save_time &&
rtc_wkalarm_save_time < time) {
if (rtc_wkalarm_save_time < now + CM_RTC_SMALL)
time = now + CM_RTC_SMALL;
else
time = rtc_wkalarm_save_time;
/* The timer is not appointed by CM */ now = ktime_get_boottime();
ret = false; add = ktime_set(wakeup_ms / MSEC_PER_SEC,
} (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC);
alarm_start(cm_timer, ktime_add(now, add));
pr_info("Waking up after %lu secs\n", time - now); cm_suspend_duration_ms = wakeup_ms;
rtc_time_to_tm(time, &tmp.time); return true;
rtc_set_alarm(rtc_dev, &tmp);
cm_suspend_duration_ms += wakeup_ms;
return ret;
}
} }
if (rtc_dev)
rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
return false; return false;
} }
static void _cm_fbchk_in_suspend(struct charger_manager *cm)
{
unsigned long jiffy_now = jiffies;
if (!cm->fullbatt_vchk_jiffies_at)
return;
if (g_desc && g_desc->assume_timer_stops_in_suspend)
jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
/* Execute now if it's going to be executed not too long after */
jiffy_now += CM_JIFFIES_SMALL;
if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at))
fullbatt_vchk(&cm->fullbatt_vchk_work.work);
}
/**
* cm_suspend_again - Determine whether suspend again or not
*
* Returns true if the system should be suspended again
* Returns false if the system should be woken up
*/
bool cm_suspend_again(void)
{
struct charger_manager *cm;
bool ret = false;
if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() ||
!cm_rtc_set)
return false;
if (cm_monitor())
goto out;
ret = true;
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry) {
_cm_fbchk_in_suspend(cm);
if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
cm->status_save_batt != is_batt_present(cm)) {
ret = false;
break;
}
}
mutex_unlock(&cm_list_mtx);
cm_rtc_set = cm_setup_timer();
out:
/* It's about the time when the non-CM appointed timer goes off */
if (rtc_wkalarm_save.enabled) {
unsigned long now;
struct rtc_time tmp;
rtc_read_time(rtc_dev, &tmp);
rtc_tm_to_time(&tmp, &now);
if (rtc_wkalarm_save_time &&
now + CM_RTC_SMALL >= rtc_wkalarm_save_time)
return false;
}
return ret;
}
EXPORT_SYMBOL_GPL(cm_suspend_again);
/**
* setup_charger_manager - initialize charger_global_desc data
* @gd: pointer to instance of charger_global_desc
*/
int setup_charger_manager(struct charger_global_desc *gd)
{
if (!gd)
return -EINVAL;
if (rtc_dev)
rtc_class_close(rtc_dev);
rtc_dev = NULL;
g_desc = NULL;
if (!gd->rtc_only_wakeup) {
pr_err("The callback rtc_only_wakeup is not given\n");
return -EINVAL;
}
if (gd->rtc_name) {
rtc_dev = rtc_class_open(gd->rtc_name);
if (IS_ERR_OR_NULL(rtc_dev)) {
rtc_dev = NULL;
/* Retry at probe. RTC may be not registered yet */
}
} else {
pr_warn("No wakeup timer is given for charger manager. "
"In-suspend monitoring won't work.\n");
}
g_desc = gd;
return 0;
}
EXPORT_SYMBOL_GPL(setup_charger_manager);
/** /**
* charger_extcon_work - enable/diable charger according to the state * charger_extcon_work - enable/diable charger according to the state
* of charger cable * of charger cable
...@@ -1719,6 +1589,12 @@ static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev) ...@@ -1719,6 +1589,12 @@ static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
return dev_get_platdata(&pdev->dev); return dev_get_platdata(&pdev->dev);
} }
static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
{
cm_timer_set = false;
return ALARMTIMER_NORESTART;
}
static int charger_manager_probe(struct platform_device *pdev) static int charger_manager_probe(struct platform_device *pdev)
{ {
struct charger_desc *desc = cm_get_drv_data(pdev); struct charger_desc *desc = cm_get_drv_data(pdev);
...@@ -1728,16 +1604,6 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1728,16 +1604,6 @@ static int charger_manager_probe(struct platform_device *pdev)
union power_supply_propval val; union power_supply_propval val;
struct power_supply *fuel_gauge; struct power_supply *fuel_gauge;
if (g_desc && !rtc_dev && g_desc->rtc_name) {
rtc_dev = rtc_class_open(g_desc->rtc_name);
if (IS_ERR_OR_NULL(rtc_dev)) {
rtc_dev = NULL;
dev_err(&pdev->dev, "Cannot get RTC %s\n",
g_desc->rtc_name);
return -ENODEV;
}
}
if (IS_ERR(desc)) { if (IS_ERR(desc)) {
dev_err(&pdev->dev, "No platform data (desc) found\n"); dev_err(&pdev->dev, "No platform data (desc) found\n");
return -ENODEV; return -ENODEV;
...@@ -1752,6 +1618,12 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1752,6 +1618,12 @@ static int charger_manager_probe(struct platform_device *pdev)
cm->dev = &pdev->dev; cm->dev = &pdev->dev;
cm->desc = desc; cm->desc = desc;
/* Initialize alarm timer */
if (alarmtimer_get_rtcdev()) {
cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
}
/* /*
* The following two do not need to be errors. * The following two do not need to be errors.
* Users may intentionally ignore those two features. * Users may intentionally ignore those two features.
...@@ -1993,38 +1865,41 @@ static int cm_suspend_noirq(struct device *dev) ...@@ -1993,38 +1865,41 @@ static int cm_suspend_noirq(struct device *dev)
return ret; return ret;
} }
static bool cm_need_to_awake(void)
{
struct charger_manager *cm;
if (cm_timer)
return false;
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry) {
if (is_charging(cm)) {
mutex_unlock(&cm_list_mtx);
return true;
}
}
mutex_unlock(&cm_list_mtx);
return false;
}
static int cm_suspend_prepare(struct device *dev) static int cm_suspend_prepare(struct device *dev)
{ {
struct charger_manager *cm = dev_get_drvdata(dev); struct charger_manager *cm = dev_get_drvdata(dev);
if (!cm_suspended) { if (cm_need_to_awake())
if (rtc_dev) { return -EBUSY;
struct rtc_time tmp;
unsigned long now;
rtc_read_alarm(rtc_dev, &rtc_wkalarm_save);
rtc_read_time(rtc_dev, &tmp);
if (rtc_wkalarm_save.enabled) { if (!cm_suspended)
rtc_tm_to_time(&rtc_wkalarm_save.time,
&rtc_wkalarm_save_time);
rtc_tm_to_time(&tmp, &now);
if (now > rtc_wkalarm_save_time)
rtc_wkalarm_save_time = 0;
} else {
rtc_wkalarm_save_time = 0;
}
}
cm_suspended = true; cm_suspended = true;
}
cancel_delayed_work(&cm->fullbatt_vchk_work); cm_timer_set = cm_setup_timer();
cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
cm->status_save_batt = is_batt_present(cm);
if (!cm_rtc_set) { if (cm_timer_set) {
cm_suspend_duration_ms = 0; cancel_work_sync(&setup_polling);
cm_rtc_set = cm_setup_timer(); cancel_delayed_work_sync(&cm_monitor_work);
cancel_delayed_work(&cm->fullbatt_vchk_work);
} }
return 0; return 0;
...@@ -2034,18 +1909,21 @@ static void cm_suspend_complete(struct device *dev) ...@@ -2034,18 +1909,21 @@ static void cm_suspend_complete(struct device *dev)
{ {
struct charger_manager *cm = dev_get_drvdata(dev); struct charger_manager *cm = dev_get_drvdata(dev);
if (cm_suspended) { if (cm_suspended)
if (rtc_dev) {
struct rtc_wkalrm tmp;
rtc_read_alarm(rtc_dev, &tmp);
rtc_wkalarm_save.pending = tmp.pending;
rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
}
cm_suspended = false; cm_suspended = false;
cm_rtc_set = false;
if (cm_timer_set) {
ktime_t remain;
alarm_cancel(cm_timer);
cm_timer_set = false;
remain = alarm_expires_remaining(cm_timer);
cm_suspend_duration_ms -= ktime_to_ms(remain);
schedule_work(&setup_polling);
} }
_cm_monitor(cm);
/* Re-enqueue delayed work (fullbatt_vchk_work) */ /* Re-enqueue delayed work (fullbatt_vchk_work) */
if (cm->fullbatt_vchk_jiffies_at) { if (cm->fullbatt_vchk_jiffies_at) {
unsigned long delay = 0; unsigned long delay = 0;
...@@ -2060,21 +1938,18 @@ static void cm_suspend_complete(struct device *dev) ...@@ -2060,21 +1938,18 @@ static void cm_suspend_complete(struct device *dev)
} }
/* /*
* Account for cm_suspend_duration_ms if * Account for cm_suspend_duration_ms with assuming that
* assume_timer_stops_in_suspend is active * timer stops in suspend.
*/ */
if (g_desc && g_desc->assume_timer_stops_in_suspend) { if (delay > cm_suspend_duration_ms)
if (delay > cm_suspend_duration_ms) delay -= cm_suspend_duration_ms;
delay -= cm_suspend_duration_ms; else
else delay = 0;
delay = 0;
}
queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
msecs_to_jiffies(delay)); msecs_to_jiffies(delay));
} }
device_set_wakeup_capable(cm->dev, false); device_set_wakeup_capable(cm->dev, false);
uevent_notify(cm, NULL);
} }
static const struct dev_pm_ops charger_manager_pm = { static const struct dev_pm_ops charger_manager_pm = {
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
static struct work_struct bat_work; static struct work_struct bat_work;
static struct ucb1x00 *ucb; static struct ucb1x00 *ucb;
static int wakeup_enabled;
struct collie_bat { struct collie_bat {
int status; int status;
...@@ -291,11 +292,21 @@ static int collie_bat_suspend(struct ucb1x00_dev *dev) ...@@ -291,11 +292,21 @@ static int collie_bat_suspend(struct ucb1x00_dev *dev)
{ {
/* flush all pending status updates */ /* flush all pending status updates */
flush_work(&bat_work); flush_work(&bat_work);
if (device_may_wakeup(&dev->ucb->dev) &&
collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
else
wakeup_enabled = 0;
return 0; return 0;
} }
static int collie_bat_resume(struct ucb1x00_dev *dev) static int collie_bat_resume(struct ucb1x00_dev *dev)
{ {
if (wakeup_enabled)
disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
/* things may have changed while we were away */ /* things may have changed while we were away */
schedule_work(&bat_work); schedule_work(&bat_work);
return 0; return 0;
...@@ -334,10 +345,15 @@ static int collie_bat_probe(struct ucb1x00_dev *dev) ...@@ -334,10 +345,15 @@ static int collie_bat_probe(struct ucb1x00_dev *dev)
collie_bat_gpio_isr, collie_bat_gpio_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"main full", &collie_bat_main); "main full", &collie_bat_main);
if (!ret) { if (ret)
schedule_work(&bat_work); goto err_irq;
return 0;
} device_init_wakeup(&ucb->dev, 1);
schedule_work(&bat_work);
return 0;
err_irq:
power_supply_unregister(&collie_bat_bu.psy); power_supply_unregister(&collie_bat_bu.psy);
err_psy_reg_bu: err_psy_reg_bu:
power_supply_unregister(&collie_bat_main.psy); power_supply_unregister(&collie_bat_main.psy);
......
...@@ -229,7 +229,7 @@ static int gpio_charger_suspend(struct device *dev) ...@@ -229,7 +229,7 @@ static int gpio_charger_suspend(struct device *dev)
if (device_may_wakeup(dev)) if (device_may_wakeup(dev))
gpio_charger->wakeup_enabled = gpio_charger->wakeup_enabled =
enable_irq_wake(gpio_charger->irq); !enable_irq_wake(gpio_charger->irq);
return 0; return 0;
} }
...@@ -239,7 +239,7 @@ static int gpio_charger_resume(struct device *dev) ...@@ -239,7 +239,7 @@ static int gpio_charger_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
if (gpio_charger->wakeup_enabled) if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled)
disable_irq_wake(gpio_charger->irq); disable_irq_wake(gpio_charger->irq);
power_supply_changed(&gpio_charger->charger); power_supply_changed(&gpio_charger->charger);
......
/*
* I2C client/driver for the Linear Technology LTC2941 and LTC2943
* Battery Gas Gauge IC
*
* Copyright (C) 2014 Topic Embedded Systems
*
* Author: Auryn Verwegen
* Author: Mike Looijmans
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/swab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/idr.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#define I16_MSB(x) ((x >> 8) & 0xFF)
#define I16_LSB(x) (x & 0xFF)
#define LTC294X_WORK_DELAY 10 /* Update delay in seconds */
#define LTC294X_MAX_VALUE 0xFFFF
#define LTC294X_MID_SUPPLY 0x7FFF
#define LTC2941_MAX_PRESCALER_EXP 7
#define LTC2943_MAX_PRESCALER_EXP 6
enum ltc294x_reg {
LTC294X_REG_STATUS = 0x00,
LTC294X_REG_CONTROL = 0x01,
LTC294X_REG_ACC_CHARGE_MSB = 0x02,
LTC294X_REG_ACC_CHARGE_LSB = 0x03,
LTC294X_REG_THRESH_HIGH_MSB = 0x04,
LTC294X_REG_THRESH_HIGH_LSB = 0x05,
LTC294X_REG_THRESH_LOW_MSB = 0x06,
LTC294X_REG_THRESH_LOW_LSB = 0x07,
LTC294X_REG_VOLTAGE_MSB = 0x08,
LTC294X_REG_VOLTAGE_LSB = 0x09,
LTC294X_REG_CURRENT_MSB = 0x0E,
LTC294X_REG_CURRENT_LSB = 0x0F,
LTC294X_REG_TEMPERATURE_MSB = 0x14,
LTC294X_REG_TEMPERATURE_LSB = 0x15,
};
#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6))
#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7)
#define LTC294X_REG_CONTROL_PRESCALER_MASK (BIT(5) | BIT(4) | BIT(3))
#define LTC294X_REG_CONTROL_SHUTDOWN_MASK (BIT(0))
#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \
((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK)
#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0
#define LTC2941_NUM_REGS 0x08
#define LTC2943_NUM_REGS 0x18
struct ltc294x_info {
struct i2c_client *client; /* I2C Client pointer */
struct power_supply supply; /* Supply pointer */
struct delayed_work work; /* Work scheduler */
int num_regs; /* Number of registers (chip type) */
int id; /* Identifier of ltc294x chip */
int charge; /* Last charge register content */
int r_sense; /* mOhm */
int Qlsb; /* nAh */
};
static DEFINE_IDR(ltc294x_id);
static DEFINE_MUTEX(ltc294x_lock);
static inline int convert_bin_to_uAh(
const struct ltc294x_info *info, int Q)
{
return ((Q * (info->Qlsb / 10))) / 100;
}
static inline int convert_uAh_to_bin(
const struct ltc294x_info *info, int uAh)
{
int Q;
Q = (uAh * 100) / (info->Qlsb/10);
return (Q < LTC294X_MAX_VALUE) ? Q : LTC294X_MAX_VALUE;
}
static int ltc294x_read_regs(struct i2c_client *client,
enum ltc294x_reg reg, u8 *buf, int num_regs)
{
int ret;
struct i2c_msg msgs[2] = { };
u8 reg_start = reg;
msgs[0].addr = client->addr;
msgs[0].len = 1;
msgs[0].buf = &reg_start;
msgs[1].addr = client->addr;
msgs[1].len = num_regs;
msgs[1].buf = buf;
msgs[1].flags = I2C_M_RD;
ret = i2c_transfer(client->adapter, &msgs[0], 2);
if (ret < 0) {
dev_err(&client->dev, "ltc2941 read_reg failed!\n");
return ret;
}
dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",
__func__, reg, num_regs, *buf);
return 0;
}
static int ltc294x_write_regs(struct i2c_client *client,
enum ltc294x_reg reg, const u8 *buf, int num_regs)
{
int ret;
u8 reg_start = reg;
ret = i2c_smbus_write_i2c_block_data(client, reg_start, num_regs, buf);
if (ret < 0) {
dev_err(&client->dev, "ltc2941 write_reg failed!\n");
return ret;
}
dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",
__func__, reg, num_regs, *buf);
return 0;
}
static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp)
{
int ret;
u8 value;
u8 control;
/* Read status and control registers */
ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1);
if (ret < 0) {
dev_err(&info->client->dev,
"Could not read registers from device\n");
goto error_exit;
}
control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) |
LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED;
/* Put the 2943 into "monitor" mode, so it measures every 10 sec */
if (info->num_regs == LTC2943_NUM_REGS)
control |= LTC2943_REG_CONTROL_MODE_SCAN;
if (value != control) {
ret = ltc294x_write_regs(info->client,
LTC294X_REG_CONTROL, &control, 1);
if (ret < 0) {
dev_err(&info->client->dev,
"Could not write register\n");
goto error_exit;
}
}
return 0;
error_exit:
return ret;
}
static int ltc294x_read_charge_register(const struct ltc294x_info *info)
{
int ret;
u8 datar[2];
ret = ltc294x_read_regs(info->client,
LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2);
if (ret < 0)
return ret;
return (datar[0] << 8) + datar[1];
}
static int ltc294x_get_charge_now(const struct ltc294x_info *info, int *val)
{
int value = ltc294x_read_charge_register(info);
if (value < 0)
return value;
/* When r_sense < 0, this counts up when the battery discharges */
if (info->Qlsb < 0)
value -= 0xFFFF;
*val = convert_bin_to_uAh(info, value);
return 0;
}
static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val)
{
int ret;
u8 dataw[2];
u8 ctrl_reg;
s32 value;
value = convert_uAh_to_bin(info, val);
/* Direction depends on how sense+/- were connected */
if (info->Qlsb < 0)
value += 0xFFFF;
if ((value < 0) || (value > 0xFFFF)) /* input validation */
return -EINVAL;
/* Read control register */
ret = ltc294x_read_regs(info->client,
LTC294X_REG_CONTROL, &ctrl_reg, 1);
if (ret < 0)
return ret;
/* Disable analog section */
ctrl_reg |= LTC294X_REG_CONTROL_SHUTDOWN_MASK;
ret = ltc294x_write_regs(info->client,
LTC294X_REG_CONTROL, &ctrl_reg, 1);
if (ret < 0)
return ret;
/* Set new charge value */
dataw[0] = I16_MSB(value);
dataw[1] = I16_LSB(value);
ret = ltc294x_write_regs(info->client,
LTC294X_REG_ACC_CHARGE_MSB, &dataw[0], 2);
if (ret < 0)
goto error_exit;
/* Enable analog section */
error_exit:
ctrl_reg &= ~LTC294X_REG_CONTROL_SHUTDOWN_MASK;
ret = ltc294x_write_regs(info->client,
LTC294X_REG_CONTROL, &ctrl_reg, 1);
return ret < 0 ? ret : 0;
}
static int ltc294x_get_charge_counter(
const struct ltc294x_info *info, int *val)
{
int value = ltc294x_read_charge_register(info);
if (value < 0)
return value;
value -= LTC294X_MID_SUPPLY;
*val = convert_bin_to_uAh(info, value);
return 0;
}
static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val)
{
int ret;
u8 datar[2];
u32 value;
ret = ltc294x_read_regs(info->client,
LTC294X_REG_VOLTAGE_MSB, &datar[0], 2);
value = (datar[0] << 8) | datar[1];
*val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */
return ret;
}
static int ltc294x_get_current(const struct ltc294x_info *info, int *val)
{
int ret;
u8 datar[2];
s32 value;
ret = ltc294x_read_regs(info->client,
LTC294X_REG_CURRENT_MSB, &datar[0], 2);
value = (datar[0] << 8) | datar[1];
value -= 0x7FFF;
/* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm,
* the formula below keeps everything in s32 range while preserving
* enough digits */
*val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */
return ret;
}
static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
{
int ret;
u8 datar[2];
u32 value;
ret = ltc294x_read_regs(info->client,
LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2);
value = (datar[0] << 8) | datar[1];
/* Full-scale is 510 Kelvin, convert to centidegrees */
*val = (((51000 * value) / 0xFFFF) - 27215);
return ret;
}
static int ltc294x_get_property(struct power_supply *psy,
enum power_supply_property prop,
union power_supply_propval *val)
{
struct ltc294x_info *info =
container_of(psy, struct ltc294x_info, supply);
switch (prop) {
case POWER_SUPPLY_PROP_CHARGE_NOW:
return ltc294x_get_charge_now(info, &val->intval);
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
return ltc294x_get_charge_counter(info, &val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
return ltc294x_get_voltage(info, &val->intval);
case POWER_SUPPLY_PROP_CURRENT_NOW:
return ltc294x_get_current(info, &val->intval);
case POWER_SUPPLY_PROP_TEMP:
return ltc294x_get_temperature(info, &val->intval);
default:
return -EINVAL;
}
}
static int ltc294x_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct ltc294x_info *info =
container_of(psy, struct ltc294x_info, supply);
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_NOW:
return ltc294x_set_charge_now(info, val->intval);
default:
return -EPERM;
}
}
static int ltc294x_property_is_writeable(
struct power_supply *psy, enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_NOW:
return 1;
default:
return 0;
}
}
static void ltc294x_update(struct ltc294x_info *info)
{
int charge = ltc294x_read_charge_register(info);
if (charge != info->charge) {
info->charge = charge;
power_supply_changed(&info->supply);
}
}
static void ltc294x_work(struct work_struct *work)
{
struct ltc294x_info *info;
info = container_of(work, struct ltc294x_info, work.work);
ltc294x_update(info);
schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
}
static enum power_supply_property ltc294x_properties[] = {
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_TEMP,
};
static int ltc294x_i2c_remove(struct i2c_client *client)
{
struct ltc294x_info *info = i2c_get_clientdata(client);
cancel_delayed_work(&info->work);
power_supply_unregister(&info->supply);
kfree(info->supply.name);
mutex_lock(&ltc294x_lock);
idr_remove(&ltc294x_id, info->id);
mutex_unlock(&ltc294x_lock);
return 0;
}
static int ltc294x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ltc294x_info *info;
int ret;
int num;
u32 prescaler_exp;
s32 r_sense;
struct device_node *np;
mutex_lock(&ltc294x_lock);
ret = idr_alloc(&ltc294x_id, client, 0, 0, GFP_KERNEL);
mutex_unlock(&ltc294x_lock);
if (ret < 0)
goto fail_id;
num = ret;
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL) {
ret = -ENOMEM;
goto fail_info;
}
i2c_set_clientdata(client, info);
info->num_regs = id->driver_data;
info->supply.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
if (!info->supply.name) {
ret = -ENOMEM;
goto fail_name;
}
np = of_node_get(client->dev.of_node);
/* r_sense can be negative, when sense+ is connected to the battery
* instead of the sense-. This results in reversed measurements. */
ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense);
if (ret < 0) {
dev_err(&client->dev,
"Could not find lltc,resistor-sense in devicetree\n");
goto fail_name;
}
info->r_sense = r_sense;
ret = of_property_read_u32(np, "lltc,prescaler-exponent",
&prescaler_exp);
if (ret < 0) {
dev_warn(&client->dev,
"lltc,prescaler-exponent not in devicetree\n");
prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
}
if (info->num_regs == LTC2943_NUM_REGS) {
if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP)
prescaler_exp = LTC2943_MAX_PRESCALER_EXP;
info->Qlsb = ((340 * 50000) / r_sense) /
(4096 / (1 << (2*prescaler_exp)));
} else {
if (prescaler_exp > LTC2941_MAX_PRESCALER_EXP)
prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
info->Qlsb = ((58 * 50000) / r_sense) /
(128 / (1 << prescaler_exp));
}
info->client = client;
info->id = num;
info->supply.type = POWER_SUPPLY_TYPE_BATTERY;
info->supply.properties = ltc294x_properties;
if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB)
info->supply.num_properties =
ARRAY_SIZE(ltc294x_properties);
else if (info->num_regs >= LTC294X_REG_CURRENT_LSB)
info->supply.num_properties =
ARRAY_SIZE(ltc294x_properties) - 1;
else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB)
info->supply.num_properties =
ARRAY_SIZE(ltc294x_properties) - 2;
else
info->supply.num_properties =
ARRAY_SIZE(ltc294x_properties) - 3;
info->supply.get_property = ltc294x_get_property;
info->supply.set_property = ltc294x_set_property;
info->supply.property_is_writeable = ltc294x_property_is_writeable;
info->supply.external_power_changed = NULL;
INIT_DELAYED_WORK(&info->work, ltc294x_work);
ret = ltc294x_reset(info, prescaler_exp);
if (ret < 0) {
dev_err(&client->dev, "Communication with chip failed\n");
goto fail_comm;
}
ret = power_supply_register(&client->dev, &info->supply);
if (ret) {
dev_err(&client->dev, "failed to register ltc2941\n");
goto fail_register;
} else {
schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
}
return 0;
fail_register:
kfree(info->supply.name);
fail_comm:
fail_name:
fail_info:
mutex_lock(&ltc294x_lock);
idr_remove(&ltc294x_id, num);
mutex_unlock(&ltc294x_lock);
fail_id:
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int ltc294x_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ltc294x_info *info = i2c_get_clientdata(client);
cancel_delayed_work(&info->work);
return 0;
}
static int ltc294x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ltc294x_info *info = i2c_get_clientdata(client);
schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
return 0;
}
static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume);
#define LTC294X_PM_OPS (&ltc294x_pm_ops)
#else
#define LTC294X_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
static const struct i2c_device_id ltc294x_i2c_id[] = {
{"ltc2941", LTC2941_NUM_REGS},
{"ltc2943", LTC2943_NUM_REGS},
{ },
};
MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);
static struct i2c_driver ltc294x_driver = {
.driver = {
.name = "LTC2941",
.pm = LTC294X_PM_OPS,
},
.probe = ltc294x_i2c_probe,
.remove = ltc294x_i2c_remove,
.id_table = ltc294x_i2c_id,
};
module_i2c_driver(ltc294x_driver);
MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems");
MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products");
MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver");
MODULE_LICENSE("GPL");
...@@ -658,7 +658,7 @@ max17042_get_pdata(struct device *dev) ...@@ -658,7 +658,7 @@ max17042_get_pdata(struct device *dev)
} }
#endif #endif
static struct regmap_config max17042_regmap_config = { static const struct regmap_config max17042_regmap_config = {
.reg_bits = 8, .reg_bits = 8,
.val_bits = 16, .val_bits = 16,
.val_format_endian = REGMAP_ENDIAN_NATIVE, .val_format_endian = REGMAP_ENDIAN_NATIVE,
......
/*
* max77693_charger.c - Battery charger driver for the Maxim 77693
*
* Copyright (C) 2014 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/regmap.h>
#include <linux/mfd/max77693.h>
#include <linux/mfd/max77693-private.h>
static const char *max77693_charger_name = "max77693-charger";
static const char *max77693_charger_model = "MAX77693";
static const char *max77693_charger_manufacturer = "Maxim Integrated";
struct max77693_charger {
struct device *dev;
struct max77693_dev *max77693;
struct power_supply charger;
u32 constant_volt;
u32 min_system_volt;
u32 thermal_regulation_temp;
u32 batttery_overcurrent;
u32 charge_input_threshold_volt;
};
static int max77693_get_charger_state(struct regmap *regmap)
{
int state;
unsigned int data;
if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0)
return POWER_SUPPLY_STATUS_UNKNOWN;
data &= CHG_DETAILS_01_CHG_MASK;
data >>= CHG_DETAILS_01_CHG_SHIFT;
switch (data) {
case MAX77693_CHARGING_PREQUALIFICATION:
case MAX77693_CHARGING_FAST_CONST_CURRENT:
case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
case MAX77693_CHARGING_TOP_OFF:
/* In high temp the charging current is reduced, but still charging */
case MAX77693_CHARGING_HIGH_TEMP:
state = POWER_SUPPLY_STATUS_CHARGING;
break;
case MAX77693_CHARGING_DONE:
state = POWER_SUPPLY_STATUS_FULL;
break;
case MAX77693_CHARGING_TIMER_EXPIRED:
case MAX77693_CHARGING_THERMISTOR_SUSPEND:
state = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case MAX77693_CHARGING_OFF:
case MAX77693_CHARGING_OVER_TEMP:
case MAX77693_CHARGING_WATCHDOG_EXPIRED:
state = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case MAX77693_CHARGING_RESERVED:
default:
state = POWER_SUPPLY_STATUS_UNKNOWN;
}
return state;
}
static int max77693_get_charge_type(struct regmap *regmap)
{
int state;
unsigned int data;
if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0)
return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
data &= CHG_DETAILS_01_CHG_MASK;
data >>= CHG_DETAILS_01_CHG_SHIFT;
switch (data) {
case MAX77693_CHARGING_PREQUALIFICATION:
/*
* Top-off: trickle or fast? In top-off the current varies between
* 100 and 250 mA. It is higher than prequalification current.
*/
case MAX77693_CHARGING_TOP_OFF:
state = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
break;
case MAX77693_CHARGING_FAST_CONST_CURRENT:
case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
/* In high temp the charging current is reduced, but still charging */
case MAX77693_CHARGING_HIGH_TEMP:
state = POWER_SUPPLY_CHARGE_TYPE_FAST;
break;
case MAX77693_CHARGING_DONE:
case MAX77693_CHARGING_TIMER_EXPIRED:
case MAX77693_CHARGING_THERMISTOR_SUSPEND:
case MAX77693_CHARGING_OFF:
case MAX77693_CHARGING_OVER_TEMP:
case MAX77693_CHARGING_WATCHDOG_EXPIRED:
state = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
case MAX77693_CHARGING_RESERVED:
default:
state = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
}
return state;
}
/*
* Supported health statuses:
* - POWER_SUPPLY_HEALTH_DEAD
* - POWER_SUPPLY_HEALTH_GOOD
* - POWER_SUPPLY_HEALTH_OVERVOLTAGE
* - POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE
* - POWER_SUPPLY_HEALTH_UNKNOWN
* - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
*/
static int max77693_get_battery_health(struct regmap *regmap)
{
int state;
unsigned int data;
if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0)
return POWER_SUPPLY_HEALTH_UNKNOWN;
data &= CHG_DETAILS_01_BAT_MASK;
data >>= CHG_DETAILS_01_BAT_SHIFT;
switch (data) {
case MAX77693_BATTERY_NOBAT:
state = POWER_SUPPLY_HEALTH_DEAD;
break;
case MAX77693_BATTERY_PREQUALIFICATION:
case MAX77693_BATTERY_GOOD:
case MAX77693_BATTERY_LOWVOLTAGE:
state = POWER_SUPPLY_HEALTH_GOOD;
break;
case MAX77693_BATTERY_TIMER_EXPIRED:
/*
* Took longer to charge than expected, charging suspended.
* Damaged battery?
*/
state = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
break;
case MAX77693_BATTERY_OVERVOLTAGE:
state = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
break;
case MAX77693_BATTERY_OVERCURRENT:
state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
break;
case MAX77693_BATTERY_RESERVED:
default:
state = POWER_SUPPLY_HEALTH_UNKNOWN;
break;
}
return state;
}
static int max77693_get_present(struct regmap *regmap)
{
unsigned int data;
/*
* Read CHG_INT_OK register. High DETBAT bit here should be
* equal to value 0x0 in CHG_DETAILS_01/BAT field.
*/
regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
if (data & CHG_INT_OK_DETBAT_MASK)
return 0;
return 1;
}
static int max77693_get_online(struct regmap *regmap)
{
unsigned int data;
regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
if (data & CHG_INT_OK_CHGIN_MASK)
return 1;
return 0;
}
static enum power_supply_property max77693_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 int max77693_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct max77693_charger *chg = container_of(psy,
struct max77693_charger,
charger);
struct regmap *regmap = chg->max77693->regmap;
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = max77693_get_charger_state(regmap);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = max77693_get_charge_type(regmap);
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = max77693_get_battery_health(regmap);
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = max77693_get_present(regmap);
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = max77693_get_online(regmap);
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = max77693_charger_model;
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = max77693_charger_manufacturer;
break;
default:
return -EINVAL;
}
return ret;
}
static ssize_t device_attr_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count,
int (*fn)(struct max77693_charger *, unsigned long))
{
struct max77693_charger *chg = dev_get_drvdata(dev);
unsigned long val;
int ret;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
ret = fn(chg, val);
if (ret)
return ret;
return count;
}
static ssize_t fast_charge_timer_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max77693_charger *chg = dev_get_drvdata(dev);
unsigned int data, val;
int ret;
ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_01,
&data);
if (ret < 0)
return ret;
data &= CHG_CNFG_01_FCHGTIME_MASK;
data >>= CHG_CNFG_01_FCHGTIME_SHIFT;
switch (data) {
case 0x1 ... 0x7:
/* Starting from 4 hours, step by 2 hours */
val = 4 + (data - 1) * 2;
break;
case 0x0:
default:
val = 0;
break;
}
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
}
static int max77693_set_fast_charge_timer(struct max77693_charger *chg,
unsigned long hours)
{
unsigned int data;
/*
* 0x00 - disable
* 0x01 - 4h
* 0x02 - 6h
* ...
* 0x07 - 16h
* Round down odd values.
*/
switch (hours) {
case 4 ... 16:
data = (hours - 4) / 2 + 1;
break;
case 0:
/* Disable */
data = 0;
break;
default:
return -EINVAL;
}
data <<= CHG_CNFG_01_FCHGTIME_SHIFT;
return regmap_update_bits(chg->max77693->regmap,
MAX77693_CHG_REG_CHG_CNFG_01,
CHG_CNFG_01_FCHGTIME_MASK, data);
}
static ssize_t fast_charge_timer_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
return device_attr_store(dev, attr, buf, count,
max77693_set_fast_charge_timer);
}
static ssize_t top_off_threshold_current_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max77693_charger *chg = dev_get_drvdata(dev);
unsigned int data, val;
int ret;
ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
&data);
if (ret < 0)
return ret;
data &= CHG_CNFG_03_TOITH_MASK;
data >>= CHG_CNFG_03_TOITH_SHIFT;
if (data <= 0x04)
val = 100000 + data * 25000;
else
val = data * 50000;
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
}
static int max77693_set_top_off_threshold_current(struct max77693_charger *chg,
unsigned long uamp)
{
unsigned int data;
if (uamp < 100000 || uamp > 350000)
return -EINVAL;
if (uamp <= 200000)
data = (uamp - 100000) / 25000;
else
/* (200000, 350000> */
data = uamp / 50000;
data <<= CHG_CNFG_03_TOITH_SHIFT;
return regmap_update_bits(chg->max77693->regmap,
MAX77693_CHG_REG_CHG_CNFG_03,
CHG_CNFG_03_TOITH_MASK, data);
}
static ssize_t top_off_threshold_current_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
return device_attr_store(dev, attr, buf, count,
max77693_set_top_off_threshold_current);
}
static ssize_t top_off_timer_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max77693_charger *chg = dev_get_drvdata(dev);
unsigned int data, val;
int ret;
ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
&data);
if (ret < 0)
return ret;
data &= CHG_CNFG_03_TOTIME_MASK;
data >>= CHG_CNFG_03_TOTIME_SHIFT;
val = data * 10;
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
}
static int max77693_set_top_off_timer(struct max77693_charger *chg,
unsigned long minutes)
{
unsigned int data;
if (minutes > 70)
return -EINVAL;
data = minutes / 10;
data <<= CHG_CNFG_03_TOTIME_SHIFT;
return regmap_update_bits(chg->max77693->regmap,
MAX77693_CHG_REG_CHG_CNFG_03,
CHG_CNFG_03_TOTIME_MASK, data);
}
static ssize_t top_off_timer_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
return device_attr_store(dev, attr, buf, count,
max77693_set_top_off_timer);
}
static DEVICE_ATTR_RW(fast_charge_timer);
static DEVICE_ATTR_RW(top_off_threshold_current);
static DEVICE_ATTR_RW(top_off_timer);
static int max77693_set_constant_volt(struct max77693_charger *chg,
unsigned int uvolt)
{
unsigned int data;
/*
* 0x00 - 3.650 V
* 0x01 - 3.675 V
* ...
* 0x1b - 4.325 V
* 0x1c - 4.340 V
* 0x1d - 4.350 V
* 0x1e - 4.375 V
* 0x1f - 4.400 V
*/
if (uvolt >= 3650000 && uvolt < 4340000)
data = (uvolt - 3650000) / 25000;
else if (uvolt >= 4340000 && uvolt < 4350000)
data = 0x1c;
else if (uvolt >= 4350000 && uvolt <= 4400000)
data = 0x1d + (uvolt - 4350000) / 25000;
else {
dev_err(chg->dev, "Wrong value for charging constant voltage\n");
return -EINVAL;
}
data <<= CHG_CNFG_04_CHGCVPRM_SHIFT;
dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt,
data);
return regmap_update_bits(chg->max77693->regmap,
MAX77693_CHG_REG_CHG_CNFG_04,
CHG_CNFG_04_CHGCVPRM_MASK, data);
}
static int max77693_set_min_system_volt(struct max77693_charger *chg,
unsigned int uvolt)
{
unsigned int data;
if (uvolt < 3000000 || uvolt > 3700000) {
dev_err(chg->dev, "Wrong value for minimum system regulation voltage\n");
return -EINVAL;
}
data = (uvolt - 3000000) / 100000;
data <<= CHG_CNFG_04_MINVSYS_SHIFT;
dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n",
uvolt, data);
return regmap_update_bits(chg->max77693->regmap,
MAX77693_CHG_REG_CHG_CNFG_04,
CHG_CNFG_04_MINVSYS_MASK, data);
}
static int max77693_set_thermal_regulation_temp(struct max77693_charger *chg,
unsigned int cels)
{
unsigned int data;
switch (cels) {
case 70:
case 85:
case 100:
case 115:
data = (cels - 70) / 15;
break;
default:
dev_err(chg->dev, "Wrong value for thermal regulation loop temperature\n");
return -EINVAL;
}
data <<= CHG_CNFG_07_REGTEMP_SHIFT;
dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n",
cels, data);
return regmap_update_bits(chg->max77693->regmap,
MAX77693_CHG_REG_CHG_CNFG_07,
CHG_CNFG_07_REGTEMP_MASK, data);
}
static int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
unsigned int uamp)
{
unsigned int data;
if (uamp && (uamp < 2000000 || uamp > 3500000)) {
dev_err(chg->dev, "Wrong value for battery overcurrent\n");
return -EINVAL;
}
if (uamp)
data = ((uamp - 2000000) / 250000) + 1;
else
data = 0; /* disable */
data <<= CHG_CNFG_12_B2SOVRC_SHIFT;
dev_dbg(chg->dev, "Battery overcurrent: %u (0x%x)\n", uamp, data);
return regmap_update_bits(chg->max77693->regmap,
MAX77693_CHG_REG_CHG_CNFG_12,
CHG_CNFG_12_B2SOVRC_MASK, data);
}
static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg,
unsigned int uvolt)
{
unsigned int data;
switch (uvolt) {
case 4300000:
data = 0x0;
break;
case 4700000:
case 4800000:
case 4900000:
data = (uvolt - 4700000) / 100000;
default:
dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n");
return -EINVAL;
}
data <<= CHG_CNFG_12_VCHGINREG_SHIFT;
dev_dbg(chg->dev, "Charge input voltage regulation threshold: %u (0x%x)\n",
uvolt, data);
return regmap_update_bits(chg->max77693->regmap,
MAX77693_CHG_REG_CHG_CNFG_12,
CHG_CNFG_12_VCHGINREG_MASK, data);
}
/*
* Sets charger registers to proper and safe default values.
*/
static int max77693_reg_init(struct max77693_charger *chg)
{
int ret;
unsigned int data;
/* Unlock charger register protection */
data = (0x3 << CHG_CNFG_06_CHGPROT_SHIFT);
ret = regmap_update_bits(chg->max77693->regmap,
MAX77693_CHG_REG_CHG_CNFG_06,
CHG_CNFG_06_CHGPROT_MASK, data);
if (ret) {
dev_err(chg->dev, "Error unlocking registers: %d\n", ret);
return ret;
}
ret = max77693_set_fast_charge_timer(chg, DEFAULT_FAST_CHARGE_TIMER);
if (ret)
return ret;
ret = max77693_set_top_off_threshold_current(chg,
DEFAULT_TOP_OFF_THRESHOLD_CURRENT);
if (ret)
return ret;
ret = max77693_set_top_off_timer(chg, DEFAULT_TOP_OFF_TIMER);
if (ret)
return ret;
ret = max77693_set_constant_volt(chg, chg->constant_volt);
if (ret)
return ret;
ret = max77693_set_min_system_volt(chg, chg->min_system_volt);
if (ret)
return ret;
ret = max77693_set_thermal_regulation_temp(chg,
chg->thermal_regulation_temp);
if (ret)
return ret;
ret = max77693_set_batttery_overcurrent(chg, chg->batttery_overcurrent);
if (ret)
return ret;
return max77693_set_charge_input_threshold_volt(chg,
chg->charge_input_threshold_volt);
}
#ifdef CONFIG_OF
static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
{
struct device_node *np = dev->of_node;
if (!np) {
dev_err(dev, "no charger OF node\n");
return -EINVAL;
}
if (of_property_read_u32(np, "maxim,constant-microvolt",
&chg->constant_volt))
chg->constant_volt = DEFAULT_CONSTANT_VOLT;
if (of_property_read_u32(np, "maxim,min-system-microvolt",
&chg->min_system_volt))
chg->min_system_volt = DEFAULT_MIN_SYSTEM_VOLT;
if (of_property_read_u32(np, "maxim,thermal-regulation-celsius",
&chg->thermal_regulation_temp))
chg->thermal_regulation_temp = DEFAULT_THERMAL_REGULATION_TEMP;
if (of_property_read_u32(np, "maxim,battery-overcurrent-microamp",
&chg->batttery_overcurrent))
chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT;
if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt",
&chg->charge_input_threshold_volt))
chg->charge_input_threshold_volt =
DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT;
return 0;
}
#else /* CONFIG_OF */
static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
{
return 0;
}
#endif /* CONFIG_OF */
static int max77693_charger_probe(struct platform_device *pdev)
{
struct max77693_charger *chg;
struct max77693_dev *max77693 = 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->max77693 = max77693;
ret = max77693_dt_init(&pdev->dev, chg);
if (ret)
return ret;
ret = max77693_reg_init(chg);
if (ret)
return ret;
chg->charger.name = max77693_charger_name;
chg->charger.type = POWER_SUPPLY_TYPE_BATTERY;
chg->charger.properties = max77693_charger_props;
chg->charger.num_properties = ARRAY_SIZE(max77693_charger_props);
chg->charger.get_property = max77693_charger_get_property;
ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
if (ret) {
dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n");
goto err;
}
ret = device_create_file(&pdev->dev,
&dev_attr_top_off_threshold_current);
if (ret) {
dev_err(&pdev->dev, "failed: create top off current sysfs entry\n");
goto err;
}
ret = device_create_file(&pdev->dev, &dev_attr_top_off_timer);
if (ret) {
dev_err(&pdev->dev, "failed: create top off timer sysfs entry\n");
goto err;
}
ret = power_supply_register(&pdev->dev, &chg->charger);
if (ret) {
dev_err(&pdev->dev, "failed: power supply register\n");
goto err;
}
return 0;
err:
device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
return ret;
}
static int max77693_charger_remove(struct platform_device *pdev)
{
struct max77693_charger *chg = platform_get_drvdata(pdev);
device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
power_supply_unregister(&chg->charger);
return 0;
}
static const struct platform_device_id max77693_charger_id[] = {
{ "max77693-charger", 0, },
{ }
};
MODULE_DEVICE_TABLE(platform, max77693_charger_id);
static struct platform_driver max77693_charger_driver = {
.driver = {
.name = "max77693-charger",
},
.probe = max77693_charger_probe,
.remove = max77693_charger_remove,
.id_table = max77693_charger_id,
};
module_platform_driver(max77693_charger_driver);
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
MODULE_DESCRIPTION("Maxim 77693 charger driver");
MODULE_LICENSE("GPL");
...@@ -39,14 +39,13 @@ config POWER_RESET_AXXIA ...@@ -39,14 +39,13 @@ config POWER_RESET_AXXIA
Say Y if you have an Axxia family SoC. Say Y if you have an Axxia family SoC.
config POWER_RESET_BRCMSTB config POWER_RESET_BRCMSTB
bool "Broadcom STB reset driver" if COMPILE_TEST bool "Broadcom STB reset driver"
depends on ARM depends on ARM || MIPS || COMPILE_TEST
default ARCH_BRCMSTB default ARCH_BRCMSTB
help help
This driver provides restart support for ARM-based Broadcom STB This driver provides restart support for Broadcom STB boards.
boards.
Say Y here if you have an ARM-based Broadcom STB board and you wish Say Y here if you have a Broadcom STB board and you wish
to have restart support. to have restart support.
config POWER_RESET_GPIO config POWER_RESET_GPIO
...@@ -104,23 +103,16 @@ config POWER_RESET_QNAP ...@@ -104,23 +103,16 @@ config POWER_RESET_QNAP
config POWER_RESET_RESTART config POWER_RESET_RESTART
bool "Restart power-off driver" bool "Restart power-off driver"
depends on ARM
help help
Some boards don't actually have the ability to power off. Some boards don't actually have the ability to power off.
Instead they restart, and u-boot holds the SoC until the Instead they restart, and u-boot holds the SoC until the
user presses a key. u-boot then boots into Linux. user presses a key. u-boot then boots into Linux.
config POWER_RESET_SUN6I
bool "Allwinner A31 SoC reset driver"
depends on ARCH_SUNXI
help
Reboot support for the Allwinner A31 SoCs.
config POWER_RESET_ST config POWER_RESET_ST
bool "ST restart power-off driver" bool "ST restart driver"
depends on ARCH_STI depends on ARCH_STI
help help
Power off and reset support for STMicroelectronics boards. Reset support for STMicroelectronics boards.
config POWER_RESET_VERSATILE config POWER_RESET_VERSATILE
bool "ARM Versatile family reboot driver" bool "ARM Versatile family reboot driver"
...@@ -159,5 +151,11 @@ config POWER_RESET_SYSCON ...@@ -159,5 +151,11 @@ config POWER_RESET_SYSCON
help help
Reboot support for generic SYSCON mapped register reset. Reboot support for generic SYSCON mapped register reset.
config POWER_RESET_RMOBILE
tristate "Renesas R-Mobile reset driver"
depends on ARCH_RMOBILE || COMPILE_TEST
help
Reboot support for Renesas R-Mobile and SH-Mobile SoCs.
endif endif
...@@ -11,10 +11,10 @@ obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o ...@@ -11,10 +11,10 @@ obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-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_SUN6I) += sun6i-reboot.o
obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
...@@ -13,16 +13,22 @@ ...@@ -13,16 +13,22 @@
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/of.h> #include <linux/of.h>
#include <asm/system_misc.h>
#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C
#define INTEGRATOR_HDR_LOCK_OFFSET 0x14
#define INTEGRATOR_CM_CTRL_RESET (1 << 3)
#define REALVIEW_SYS_LOCK_OFFSET 0x20 #define REALVIEW_SYS_LOCK_OFFSET 0x20
#define REALVIEW_SYS_LOCK_VAL 0xA05F
#define REALVIEW_SYS_RESETCTL_OFFSET 0x40 #define REALVIEW_SYS_RESETCTL_OFFSET 0x40
/* Magic unlocking token used on all Versatile boards */
#define VERSATILE_LOCK_VAL 0xA05F
/* /*
* We detect the different syscon types from the compatible strings. * We detect the different syscon types from the compatible strings.
*/ */
enum versatile_reboot { enum versatile_reboot {
INTEGRATOR_REBOOT_CM,
REALVIEW_REBOOT_EB, REALVIEW_REBOOT_EB,
REALVIEW_REBOOT_PB1176, REALVIEW_REBOOT_PB1176,
REALVIEW_REBOOT_PB11MP, REALVIEW_REBOOT_PB11MP,
...@@ -35,6 +41,10 @@ static struct regmap *syscon_regmap; ...@@ -35,6 +41,10 @@ static struct regmap *syscon_regmap;
static enum versatile_reboot versatile_reboot_type; static enum versatile_reboot versatile_reboot_type;
static const struct of_device_id versatile_reboot_of_match[] = { static const struct of_device_id versatile_reboot_of_match[] = {
{
.compatible = "arm,core-module-integrator",
.data = (void *)INTEGRATOR_REBOOT_CM
},
{ {
.compatible = "arm,realview-eb-syscon", .compatible = "arm,realview-eb-syscon",
.data = (void *)REALVIEW_REBOOT_EB, .data = (void *)REALVIEW_REBOOT_EB,
...@@ -55,31 +65,47 @@ static const struct of_device_id versatile_reboot_of_match[] = { ...@@ -55,31 +65,47 @@ static const struct of_device_id versatile_reboot_of_match[] = {
.compatible = "arm,realview-pbx-syscon", .compatible = "arm,realview-pbx-syscon",
.data = (void *)REALVIEW_REBOOT_PBX, .data = (void *)REALVIEW_REBOOT_PBX,
}, },
{},
}; };
static void versatile_reboot(enum reboot_mode mode, const char *cmd) static int versatile_reboot(struct notifier_block *this, unsigned long mode,
void *cmd)
{ {
/* Unlock the reset register */ /* Unlock the reset register */
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
REALVIEW_SYS_LOCK_VAL);
/* Then hit reset on the different machines */ /* Then hit reset on the different machines */
switch (versatile_reboot_type) { switch (versatile_reboot_type) {
case INTEGRATOR_REBOOT_CM:
regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET,
VERSATILE_LOCK_VAL);
regmap_update_bits(syscon_regmap,
INTEGRATOR_HDR_CTRL_OFFSET,
INTEGRATOR_CM_CTRL_RESET,
INTEGRATOR_CM_CTRL_RESET);
break;
case REALVIEW_REBOOT_EB: case REALVIEW_REBOOT_EB:
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
VERSATILE_LOCK_VAL);
regmap_write(syscon_regmap, regmap_write(syscon_regmap,
REALVIEW_SYS_RESETCTL_OFFSET, 0x0008); REALVIEW_SYS_RESETCTL_OFFSET, 0x0008);
break; break;
case REALVIEW_REBOOT_PB1176: case REALVIEW_REBOOT_PB1176:
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
VERSATILE_LOCK_VAL);
regmap_write(syscon_regmap, regmap_write(syscon_regmap,
REALVIEW_SYS_RESETCTL_OFFSET, 0x0100); REALVIEW_SYS_RESETCTL_OFFSET, 0x0100);
break; break;
case REALVIEW_REBOOT_PB11MP: case REALVIEW_REBOOT_PB11MP:
case REALVIEW_REBOOT_PBA8: case REALVIEW_REBOOT_PBA8:
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
VERSATILE_LOCK_VAL);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x0000); 0x0000);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x0004); 0x0004);
break; break;
case REALVIEW_REBOOT_PBX: case REALVIEW_REBOOT_PBX:
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
VERSATILE_LOCK_VAL);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x00f0); 0x00f0);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
...@@ -87,12 +113,20 @@ static void versatile_reboot(enum reboot_mode mode, const char *cmd) ...@@ -87,12 +113,20 @@ static void versatile_reboot(enum reboot_mode mode, const char *cmd)
break; break;
} }
dsb(); dsb();
return NOTIFY_DONE;
} }
static struct notifier_block versatile_reboot_nb = {
.notifier_call = versatile_reboot,
.priority = 192,
};
static int __init versatile_reboot_probe(void) static int __init versatile_reboot_probe(void)
{ {
const struct of_device_id *reboot_id; const struct of_device_id *reboot_id;
struct device_node *np; struct device_node *np;
int err;
np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match,
&reboot_id); &reboot_id);
...@@ -104,7 +138,10 @@ static int __init versatile_reboot_probe(void) ...@@ -104,7 +138,10 @@ static int __init versatile_reboot_probe(void)
if (IS_ERR(syscon_regmap)) if (IS_ERR(syscon_regmap))
return PTR_ERR(syscon_regmap); return PTR_ERR(syscon_regmap);
arm_pm_restart = versatile_reboot; err = register_restart_handler(&versatile_reboot_nb);
if (err)
return err;
pr_info("versatile reboot driver registered\n"); pr_info("versatile reboot driver registered\n");
return 0; return 0;
} }
......
...@@ -71,10 +71,11 @@ static void at91_poweroff(void) ...@@ -71,10 +71,11 @@ static void at91_poweroff(void)
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR); writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
} }
const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np) static int at91_poweroff_get_wakeup_mode(struct device_node *np)
{ {
const char *pm; const char *pm;
int err, i; unsigned int i;
int err;
err = of_property_read_string(np, "atmel,wakeup-mode", &pm); err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
if (err < 0) if (err < 0)
...@@ -90,7 +91,7 @@ const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np) ...@@ -90,7 +91,7 @@ const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np)
static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev) static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
enum wakeup_type wakeup_mode; int wakeup_mode;
u32 mode = 0, tmp; u32 mode = 0, tmp;
wakeup_mode = at91_poweroff_get_wakeup_mode(np); wakeup_mode = at91_poweroff_get_wakeup_mode(np);
......
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <asm/system_misc.h>
#include <soc/at91/at91sam9_ddrsdr.h> #include <soc/at91/at91sam9_ddrsdr.h>
#include <soc/at91/at91sam9_sdramc.h> #include <soc/at91/at91sam9_sdramc.h>
...@@ -54,7 +52,8 @@ static void __iomem *at91_ramc_base[2], *at91_rstc_base; ...@@ -54,7 +52,8 @@ static void __iomem *at91_ramc_base[2], *at91_rstc_base;
* reset register it can be left driving the data bus and * reset register it can be left driving the data bus and
* killing the chance of a subsequent boot from NAND * killing the chance of a subsequent boot from NAND
*/ */
static void at91sam9260_restart(enum reboot_mode mode, const char *cmd) static int at91sam9260_restart(struct notifier_block *this, unsigned long mode,
void *cmd)
{ {
asm volatile( asm volatile(
/* Align to cache lines */ /* Align to cache lines */
...@@ -76,9 +75,12 @@ static void at91sam9260_restart(enum reboot_mode mode, const char *cmd) ...@@ -76,9 +75,12 @@ static void at91sam9260_restart(enum reboot_mode mode, const char *cmd)
"r" (1), "r" (1),
"r" (AT91_SDRAMC_LPCB_POWER_DOWN), "r" (AT91_SDRAMC_LPCB_POWER_DOWN),
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)); "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST));
return NOTIFY_DONE;
} }
static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode,
void *cmd)
{ {
asm volatile( asm volatile(
/* /*
...@@ -117,6 +119,8 @@ static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) ...@@ -117,6 +119,8 @@ static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd)
"r" (AT91_DDRSDRC_LPCB_POWER_DOWN), "r" (AT91_DDRSDRC_LPCB_POWER_DOWN),
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST) "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)
: "r0"); : "r0");
return NOTIFY_DONE;
} }
static void __init at91_reset_status(struct platform_device *pdev) static void __init at91_reset_status(struct platform_device *pdev)
...@@ -161,6 +165,10 @@ static struct of_device_id at91_reset_of_match[] = { ...@@ -161,6 +165,10 @@ static struct of_device_id at91_reset_of_match[] = {
{ /* sentinel */ } { /* sentinel */ }
}; };
static struct notifier_block at91_restart_nb = {
.priority = 192,
};
static int at91_reset_of_probe(struct platform_device *pdev) static int at91_reset_of_probe(struct platform_device *pdev)
{ {
const struct of_device_id *match; const struct of_device_id *match;
...@@ -183,9 +191,8 @@ static int at91_reset_of_probe(struct platform_device *pdev) ...@@ -183,9 +191,8 @@ static int at91_reset_of_probe(struct platform_device *pdev)
} }
match = of_match_node(at91_reset_of_match, pdev->dev.of_node); match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
arm_pm_restart = match->data; at91_restart_nb.notifier_call = match->data;
return register_restart_handler(&at91_restart_nb);
return 0;
} }
static int at91_reset_platform_probe(struct platform_device *pdev) static int at91_reset_platform_probe(struct platform_device *pdev)
...@@ -212,10 +219,11 @@ static int at91_reset_platform_probe(struct platform_device *pdev) ...@@ -212,10 +219,11 @@ static int at91_reset_platform_probe(struct platform_device *pdev)
} }
match = platform_get_device_id(pdev); match = platform_get_device_id(pdev);
arm_pm_restart = (void (*)(enum reboot_mode, const char*)) at91_restart_nb.notifier_call =
match->driver_data; (int (*)(struct notifier_block *,
unsigned long, void *)) match->driver_data;
return 0; return register_restart_handler(&at91_restart_nb);
} }
static int at91_reset_probe(struct platform_device *pdev) static int at91_reset_probe(struct platform_device *pdev)
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <linux/bitops.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -34,13 +35,20 @@ static struct regmap *regmap; ...@@ -34,13 +35,20 @@ static struct regmap *regmap;
static u32 rst_src_en; static u32 rst_src_en;
static u32 sw_mstr_rst; static u32 sw_mstr_rst;
struct reset_reg_mask {
u32 rst_src_en_mask;
u32 sw_mstr_rst_mask;
};
static const struct reset_reg_mask *reset_masks;
static int brcmstb_restart_handler(struct notifier_block *this, static int brcmstb_restart_handler(struct notifier_block *this,
unsigned long mode, void *cmd) unsigned long mode, void *cmd)
{ {
int rc; int rc;
u32 tmp; u32 tmp;
rc = regmap_write(regmap, rst_src_en, 1); rc = regmap_write(regmap, rst_src_en, reset_masks->rst_src_en_mask);
if (rc) { if (rc) {
pr_err("failed to write rst_src_en (%d)\n", rc); pr_err("failed to write rst_src_en (%d)\n", rc);
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -52,7 +60,7 @@ static int brcmstb_restart_handler(struct notifier_block *this, ...@@ -52,7 +60,7 @@ static int brcmstb_restart_handler(struct notifier_block *this,
return NOTIFY_DONE; return NOTIFY_DONE;
} }
rc = regmap_write(regmap, sw_mstr_rst, 1); rc = regmap_write(regmap, sw_mstr_rst, reset_masks->sw_mstr_rst_mask);
if (rc) { if (rc) {
pr_err("failed to write sw_mstr_rst (%d)\n", rc); pr_err("failed to write sw_mstr_rst (%d)\n", rc);
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -75,10 +83,34 @@ static struct notifier_block brcmstb_restart_nb = { ...@@ -75,10 +83,34 @@ static struct notifier_block brcmstb_restart_nb = {
.priority = 128, .priority = 128,
}; };
static const struct reset_reg_mask reset_bits_40nm = {
.rst_src_en_mask = BIT(0),
.sw_mstr_rst_mask = BIT(0),
};
static const struct reset_reg_mask reset_bits_65nm = {
.rst_src_en_mask = BIT(3),
.sw_mstr_rst_mask = BIT(31),
};
static const struct of_device_id of_match[] = {
{ .compatible = "brcm,brcmstb-reboot", .data = &reset_bits_40nm },
{ .compatible = "brcm,bcm7038-reboot", .data = &reset_bits_65nm },
{},
};
static int brcmstb_reboot_probe(struct platform_device *pdev) static int brcmstb_reboot_probe(struct platform_device *pdev)
{ {
int rc; int rc;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id;
of_id = of_match_node(of_match, np);
if (!of_id) {
pr_err("failed to look up compatible string\n");
return -EINVAL;
}
reset_masks = of_id->data;
regmap = syscon_regmap_lookup_by_phandle(np, "syscon"); regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
if (IS_ERR(regmap)) { if (IS_ERR(regmap)) {
...@@ -108,11 +140,6 @@ static int brcmstb_reboot_probe(struct platform_device *pdev) ...@@ -108,11 +140,6 @@ static int brcmstb_reboot_probe(struct platform_device *pdev)
return rc; return rc;
} }
static const struct of_device_id of_match[] = {
{ .compatible = "brcm,brcmstb-reboot", },
{},
};
static struct platform_driver brcmstb_reboot_driver = { static struct platform_driver brcmstb_reboot_driver = {
.probe = brcmstb_reboot_probe, .probe = brcmstb_reboot_probe,
.driver = { .driver = {
......
...@@ -32,7 +32,9 @@ ...@@ -32,7 +32,9 @@
* - trigger (input) * - trigger (input)
* A level change indicates the shut-down trigger. If it's state reverts * A level change indicates the shut-down trigger. If it's state reverts
* within the time-out defined by trigger_delay, the shut down is not * within the time-out defined by trigger_delay, the shut down is not
* executed. * executed. If no pin is assigned to this input, the driver will start the
* watchdog toggle immediately. The chip will only power off the system if
* it is requested to do so through the kill line.
* *
* - watchdog (output) * - watchdog (output)
* Once a shut down is triggered, the driver will toggle this signal, * Once a shut down is triggered, the driver will toggle this signal,
...@@ -63,7 +65,7 @@ ...@@ -63,7 +65,7 @@
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/reboot.h> #include <linux/reboot.h>
struct ltc2952_poweroff_data { struct ltc2952_poweroff {
struct hrtimer timer_trigger; struct hrtimer timer_trigger;
struct hrtimer timer_wde; struct hrtimer timer_wde;
...@@ -72,22 +74,21 @@ struct ltc2952_poweroff_data { ...@@ -72,22 +74,21 @@ struct ltc2952_poweroff_data {
struct device *dev; struct device *dev;
unsigned int virq; struct gpio_desc *gpio_trigger;
struct gpio_desc *gpio_watchdog;
struct gpio_desc *gpio_kill;
/** bool kernel_panic;
* 0: trigger struct notifier_block panic_notifier;
* 1: watchdog
* 2: kill
*/
struct gpio_desc *gpio[3];
}; };
static int ltc2952_poweroff_panic; #define to_ltc2952(p, m) container_of(p, struct ltc2952_poweroff, m)
static struct ltc2952_poweroff_data *ltc2952_data;
#define POWERPATH_IO_TRIGGER 0 /*
#define POWERPATH_IO_WATCHDOG 1 * This global variable is only needed for pm_power_off. We should
#define POWERPATH_IO_KILL 2 * remove it entirely once we don't need the global state anymore.
*/
static struct ltc2952_poweroff *ltc2952_data;
/** /**
* ltc2952_poweroff_timer_wde - Timer callback * ltc2952_poweroff_timer_wde - Timer callback
...@@ -103,29 +104,24 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) ...@@ -103,29 +104,24 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer)
ktime_t now; ktime_t now;
int state; int state;
unsigned long overruns; unsigned long overruns;
struct ltc2952_poweroff *data = to_ltc2952(timer, timer_wde);
if (ltc2952_poweroff_panic) if (data->kernel_panic)
return HRTIMER_NORESTART; return HRTIMER_NORESTART;
state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]); state = gpiod_get_value(data->gpio_watchdog);
gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state); gpiod_set_value(data->gpio_watchdog, !state);
now = hrtimer_cb_get_time(timer); now = hrtimer_cb_get_time(timer);
overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval); overruns = hrtimer_forward(timer, now, data->wde_interval);
return HRTIMER_RESTART; return HRTIMER_RESTART;
} }
static enum hrtimer_restart ltc2952_poweroff_timer_trigger( static void ltc2952_poweroff_start_wde(struct ltc2952_poweroff *data)
struct hrtimer *timer)
{ {
int ret; if (hrtimer_start(&data->timer_wde, data->wde_interval,
HRTIMER_MODE_REL)) {
ret = hrtimer_start(&ltc2952_data->timer_wde,
ltc2952_data->wde_interval, HRTIMER_MODE_REL);
if (ret) {
dev_err(ltc2952_data->dev, "unable to start the timer\n");
/* /*
* The device will not toggle the watchdog reset, * The device will not toggle the watchdog reset,
* thus shut down is only safe if the PowerPath controller * thus shut down is only safe if the PowerPath controller
...@@ -134,10 +130,17 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger( ...@@ -134,10 +130,17 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
* *
* Only sending a warning as the system will power-off anyway * Only sending a warning as the system will power-off anyway
*/ */
dev_err(data->dev, "unable to start the timer\n");
} }
}
dev_info(ltc2952_data->dev, "executing shutdown\n"); static enum hrtimer_restart
ltc2952_poweroff_timer_trigger(struct hrtimer *timer)
{
struct ltc2952_poweroff *data = to_ltc2952(timer, timer_trigger);
ltc2952_poweroff_start_wde(data);
dev_info(data->dev, "executing shutdown\n");
orderly_poweroff(true); orderly_poweroff(true);
return HRTIMER_NORESTART; return HRTIMER_NORESTART;
...@@ -154,180 +157,161 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger( ...@@ -154,180 +157,161 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
*/ */
static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id)
{ {
int ret; struct ltc2952_poweroff *data = dev_id;
struct ltc2952_poweroff_data *data = dev_id;
if (ltc2952_poweroff_panic) if (data->kernel_panic || hrtimer_active(&data->timer_wde)) {
goto irq_ok;
if (hrtimer_active(&data->timer_wde)) {
/* shutdown is already triggered, nothing to do any more */ /* shutdown is already triggered, nothing to do any more */
goto irq_ok; return IRQ_HANDLED;
} }
if (!hrtimer_active(&data->timer_trigger)) { if (gpiod_get_value(data->gpio_trigger)) {
ret = hrtimer_start(&data->timer_trigger, data->trigger_delay, if (hrtimer_start(&data->timer_trigger, data->trigger_delay,
HRTIMER_MODE_REL); HRTIMER_MODE_REL))
if (ret)
dev_err(data->dev, "unable to start the wait timer\n"); dev_err(data->dev, "unable to start the wait timer\n");
} else { } else {
ret = hrtimer_cancel(&data->timer_trigger); hrtimer_cancel(&data->timer_trigger);
/* omitting return value check, timer should have been valid */ /* omitting return value check, timer should have been valid */
} }
irq_ok:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void ltc2952_poweroff_kill(void) static void ltc2952_poweroff_kill(void)
{ {
gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1); gpiod_set_value(ltc2952_data->gpio_kill, 1);
}
static int ltc2952_poweroff_suspend(struct platform_device *pdev,
pm_message_t state)
{
return -ENOSYS;
}
static int ltc2952_poweroff_resume(struct platform_device *pdev)
{
return -ENOSYS;
} }
static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) static void ltc2952_poweroff_default(struct ltc2952_poweroff *data)
{ {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(data->gpio); i++)
data->gpio[i] = NULL;
data->wde_interval = ktime_set(0, 300L*1E6L); data->wde_interval = ktime_set(0, 300L*1E6L);
data->trigger_delay = ktime_set(2, 500L*1E6L); data->trigger_delay = ktime_set(2, 500L*1E6L);
hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data->timer_trigger.function = &ltc2952_poweroff_timer_trigger; data->timer_trigger.function = ltc2952_poweroff_timer_trigger;
hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data->timer_wde.function = &ltc2952_poweroff_timer_wde; data->timer_wde.function = ltc2952_poweroff_timer_wde;
} }
static int ltc2952_poweroff_init(struct platform_device *pdev) static int ltc2952_poweroff_init(struct platform_device *pdev)
{ {
int ret, virq; int ret;
unsigned int i; struct ltc2952_poweroff *data = platform_get_drvdata(pdev);
struct ltc2952_poweroff_data *data;
static char *name[] = {
"trigger",
"watchdog",
"kill",
NULL
};
data = ltc2952_data;
ltc2952_poweroff_default(ltc2952_data);
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) {
ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]);
if (IS_ERR(ltc2952_data->gpio[i])) { ltc2952_poweroff_default(data);
ret = PTR_ERR(ltc2952_data->gpio[i]);
dev_err(&pdev->dev,
"unable to claim the following gpio: %s\n",
name[i]);
goto err_io;
}
}
ret = gpiod_direction_output( data->gpio_watchdog = devm_gpiod_get(&pdev->dev, "watchdog",
ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0); GPIOD_OUT_LOW);
if (ret) { if (IS_ERR(data->gpio_watchdog)) {
dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n"); ret = PTR_ERR(data->gpio_watchdog);
goto err_io; dev_err(&pdev->dev, "unable to claim gpio \"watchdog\"\n");
return ret;
} }
ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0); data->gpio_kill = devm_gpiod_get(&pdev->dev, "kill", GPIOD_OUT_LOW);
if (ret) { if (IS_ERR(data->gpio_kill)) {
dev_err(&pdev->dev, "unable to use kill-gpio as output\n"); ret = PTR_ERR(data->gpio_kill);
goto err_io; dev_err(&pdev->dev, "unable to claim gpio \"kill\"\n");
return ret;
} }
virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]); data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", GPIOD_IN);
if (virq < 0) { if (IS_ERR(data->gpio_trigger)) {
dev_err(&pdev->dev, "cannot map GPIO as interrupt"); /*
goto err_io; * It's not a problem if the trigger gpio isn't available, but
* it is worth a warning if its use was defined in the device
* tree.
*/
if (PTR_ERR(data->gpio_trigger) != -ENOENT)
dev_err(&pdev->dev,
"unable to claim gpio \"trigger\"\n");
data->gpio_trigger = NULL;
} }
ltc2952_data->virq = virq; if (devm_request_irq(&pdev->dev, gpiod_to_irq(data->gpio_trigger),
ret = request_irq(virq, ltc2952_poweroff_handler,
ltc2952_poweroff_handler, (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),
(IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), "ltc2952-poweroff",
"ltc2952-poweroff", data)) {
ltc2952_data /*
); * Some things may have happened:
* - No trigger input was defined
if (ret) { * - Claiming the GPIO failed
dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); * - We could not map to an IRQ
goto err_io; * - We couldn't register an interrupt handler
*
* None of these really are problems, but all of them
* disqualify the push button from controlling the power.
*
* It is therefore important to note that if the ltc2952
* detects a button press for long enough, it will still start
* its own powerdown window and cut the power on us if we don't
* start the watchdog trigger.
*/
if (data->gpio_trigger) {
dev_warn(&pdev->dev,
"unable to configure the trigger interrupt\n");
devm_gpiod_put(&pdev->dev, data->gpio_trigger);
data->gpio_trigger = NULL;
}
dev_info(&pdev->dev,
"power down trigger input will not be used\n");
ltc2952_poweroff_start_wde(data);
} }
return 0; return 0;
}
err_io: static int ltc2952_poweroff_notify_panic(struct notifier_block *nb,
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) unsigned long code, void *unused)
if (ltc2952_data->gpio[i]) {
gpiod_put(ltc2952_data->gpio[i]); struct ltc2952_poweroff *data = to_ltc2952(nb, panic_notifier);
return ret; data->kernel_panic = true;
return NOTIFY_DONE;
} }
static int ltc2952_poweroff_probe(struct platform_device *pdev) static int ltc2952_poweroff_probe(struct platform_device *pdev)
{ {
int ret; int ret;
struct ltc2952_poweroff *data;
if (pm_power_off) { if (pm_power_off) {
dev_err(&pdev->dev, "pm_power_off already registered"); dev_err(&pdev->dev, "pm_power_off already registered");
return -EBUSY; return -EBUSY;
} }
ltc2952_data = kzalloc(sizeof(*ltc2952_data), GFP_KERNEL); data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!ltc2952_data) if (!data)
return -ENOMEM; return -ENOMEM;
ltc2952_data->dev = &pdev->dev; data->dev = &pdev->dev;
platform_set_drvdata(pdev, data);
ret = ltc2952_poweroff_init(pdev); ret = ltc2952_poweroff_init(pdev);
if (ret) if (ret)
goto err; return ret;
pm_power_off = &ltc2952_poweroff_kill; /* TODO: remove ltc2952_data */
ltc2952_data = data;
pm_power_off = ltc2952_poweroff_kill;
data->panic_notifier.notifier_call = ltc2952_poweroff_notify_panic;
atomic_notifier_chain_register(&panic_notifier_list,
&data->panic_notifier);
dev_info(&pdev->dev, "probe successful\n"); dev_info(&pdev->dev, "probe successful\n");
return 0; return 0;
err:
kfree(ltc2952_data);
return ret;
} }
static int ltc2952_poweroff_remove(struct platform_device *pdev) static int ltc2952_poweroff_remove(struct platform_device *pdev)
{ {
unsigned int i; struct ltc2952_poweroff *data = platform_get_drvdata(pdev);
pm_power_off = NULL; pm_power_off = NULL;
hrtimer_cancel(&data->timer_trigger);
if (ltc2952_data) { hrtimer_cancel(&data->timer_wde);
free_irq(ltc2952_data->virq, ltc2952_data); atomic_notifier_chain_unregister(&panic_notifier_list,
&data->panic_notifier);
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++)
gpiod_put(ltc2952_data->gpio[i]);
kfree(ltc2952_data);
}
return 0; return 0;
} }
...@@ -344,41 +328,9 @@ static struct platform_driver ltc2952_poweroff_driver = { ...@@ -344,41 +328,9 @@ static struct platform_driver ltc2952_poweroff_driver = {
.name = "ltc2952-poweroff", .name = "ltc2952-poweroff",
.of_match_table = of_ltc2952_poweroff_match, .of_match_table = of_ltc2952_poweroff_match,
}, },
.suspend = ltc2952_poweroff_suspend,
.resume = ltc2952_poweroff_resume,
};
static int ltc2952_poweroff_notify_panic(struct notifier_block *nb,
unsigned long code, void *unused)
{
ltc2952_poweroff_panic = 1;
return NOTIFY_DONE;
}
static struct notifier_block ltc2952_poweroff_panic_nb = {
.notifier_call = ltc2952_poweroff_notify_panic,
}; };
static int __init ltc2952_poweroff_platform_init(void) module_platform_driver(ltc2952_poweroff_driver);
{
ltc2952_poweroff_panic = 0;
atomic_notifier_chain_register(&panic_notifier_list,
&ltc2952_poweroff_panic_nb);
return platform_driver_register(&ltc2952_poweroff_driver);
}
static void __exit ltc2952_poweroff_platform_exit(void)
{
atomic_notifier_chain_unregister(&panic_notifier_list,
&ltc2952_poweroff_panic_nb);
platform_driver_unregister(&ltc2952_poweroff_driver);
}
module_init(ltc2952_poweroff_platform_init);
module_exit(ltc2952_poweroff_platform_exit);
MODULE_AUTHOR("Ren Moll <rene.moll@xsens.com>"); MODULE_AUTHOR("Ren Moll <rene.moll@xsens.com>");
MODULE_DESCRIPTION("LTC PowerPath power-off driver"); MODULE_DESCRIPTION("LTC PowerPath power-off driver");
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <asm/system_misc.h>
static void restart_poweroff_do_poweroff(void) static void restart_poweroff_do_poweroff(void)
{ {
......
/*
* Renesas R-Mobile Reset Driver
*
* Copyright (C) 2014 Glider bvba
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/reboot.h>
/* SYSC Register Bank 2 */
#define RESCNT2 0x20 /* Reset Control Register 2 */
/* Reset Control Register 2 */
#define RESCNT2_PRES 0x80000000 /* Soft power-on reset */
static void __iomem *sysc_base2;
static int rmobile_reset_handler(struct notifier_block *this,
unsigned long mode, void *cmd)
{
pr_debug("%s %lu\n", __func__, mode);
/* Let's assume we have acquired the HPB semaphore */
writel(RESCNT2_PRES, sysc_base2 + RESCNT2);
return NOTIFY_DONE;
}
static struct notifier_block rmobile_reset_nb = {
.notifier_call = rmobile_reset_handler,
.priority = 192,
};
static int rmobile_reset_probe(struct platform_device *pdev)
{
int error;
sysc_base2 = of_iomap(pdev->dev.of_node, 1);
if (!sysc_base2)
return -ENODEV;
error = register_restart_handler(&rmobile_reset_nb);
if (error) {
dev_err(&pdev->dev,
"cannot register restart handler (err=%d)\n", error);
goto fail_unmap;
}
return 0;
fail_unmap:
iounmap(sysc_base2);
return error;
}
static int rmobile_reset_remove(struct platform_device *pdev)
{
unregister_restart_handler(&rmobile_reset_nb);
iounmap(sysc_base2);
return 0;
}
static const struct of_device_id rmobile_reset_of_match[] = {
{ .compatible = "renesas,sysc-rmobile", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rmobile_reset_of_match);
static struct platform_driver rmobile_reset_driver = {
.probe = rmobile_reset_probe,
.remove = rmobile_reset_remove,
.driver = {
.name = "rmobile_reset",
.of_match_table = rmobile_reset_of_match,
},
};
module_platform_driver(rmobile_reset_driver);
MODULE_DESCRIPTION("Renesas R-Mobile Reset Driver");
MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
MODULE_LICENSE("GPL v2");
...@@ -15,10 +15,9 @@ ...@@ -15,10 +15,9 @@
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/reboot.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <asm/system_misc.h>
struct reset_syscfg { struct reset_syscfg {
struct regmap *regmap; struct regmap *regmap;
/* syscfg used for reset */ /* syscfg used for reset */
...@@ -75,7 +74,8 @@ static struct reset_syscfg stid127_reset = { ...@@ -75,7 +74,8 @@ static struct reset_syscfg stid127_reset = {
static struct reset_syscfg *st_restart_syscfg; static struct reset_syscfg *st_restart_syscfg;
static void st_restart(enum reboot_mode reboot_mode, const char *cmd) static int st_restart(struct notifier_block *this, unsigned long mode,
void *cmd)
{ {
/* reset syscfg updated */ /* reset syscfg updated */
regmap_update_bits(st_restart_syscfg->regmap, regmap_update_bits(st_restart_syscfg->regmap,
...@@ -88,8 +88,15 @@ static void st_restart(enum reboot_mode reboot_mode, const char *cmd) ...@@ -88,8 +88,15 @@ static void st_restart(enum reboot_mode reboot_mode, const char *cmd)
st_restart_syscfg->offset_rst_msk, st_restart_syscfg->offset_rst_msk,
st_restart_syscfg->mask_rst_msk, st_restart_syscfg->mask_rst_msk,
0); 0);
return NOTIFY_DONE;
} }
static struct notifier_block st_restart_nb = {
.notifier_call = st_restart,
.priority = 192,
};
static struct of_device_id st_reset_of_match[] = { static struct of_device_id st_reset_of_match[] = {
{ {
.compatible = "st,stih415-restart", .compatible = "st,stih415-restart",
...@@ -126,9 +133,7 @@ static int st_reset_probe(struct platform_device *pdev) ...@@ -126,9 +133,7 @@ static int st_reset_probe(struct platform_device *pdev)
return PTR_ERR(st_restart_syscfg->regmap); return PTR_ERR(st_restart_syscfg->regmap);
} }
arm_pm_restart = st_restart; return register_restart_handler(&st_restart_nb);
return 0;
} }
static struct platform_driver st_reset_driver = { static struct platform_driver st_reset_driver = {
......
/*
* Allwinner A31 SoCs reset code
*
* Copyright (C) 2012-2014 Maxime Ripard
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <asm/system_misc.h>
#define SUN6I_WATCHDOG1_IRQ_REG 0x00
#define SUN6I_WATCHDOG1_CTRL_REG 0x10
#define SUN6I_WATCHDOG1_CTRL_RESTART BIT(0)
#define SUN6I_WATCHDOG1_CONFIG_REG 0x14
#define SUN6I_WATCHDOG1_CONFIG_RESTART BIT(0)
#define SUN6I_WATCHDOG1_CONFIG_IRQ BIT(1)
#define SUN6I_WATCHDOG1_MODE_REG 0x18
#define SUN6I_WATCHDOG1_MODE_ENABLE BIT(0)
static void __iomem *wdt_base;
static void sun6i_wdt_restart(enum reboot_mode mode, const char *cmd)
{
if (!wdt_base)
return;
/* Disable interrupts */
writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG);
/* We want to disable the IRQ and just reset the whole system */
writel(SUN6I_WATCHDOG1_CONFIG_RESTART,
wdt_base + SUN6I_WATCHDOG1_CONFIG_REG);
/* Enable timer. The default and lowest interval value is 0.5s */
writel(SUN6I_WATCHDOG1_MODE_ENABLE,
wdt_base + SUN6I_WATCHDOG1_MODE_REG);
/* Restart the watchdog. */
writel(SUN6I_WATCHDOG1_CTRL_RESTART,
wdt_base + SUN6I_WATCHDOG1_CTRL_REG);
while (1) {
mdelay(5);
writel(SUN6I_WATCHDOG1_MODE_ENABLE,
wdt_base + SUN6I_WATCHDOG1_MODE_REG);
}
}
static int sun6i_reboot_probe(struct platform_device *pdev)
{
wdt_base = of_iomap(pdev->dev.of_node, 0);
if (!wdt_base) {
WARN(1, "failed to map watchdog base address");
return -ENODEV;
}
arm_pm_restart = sun6i_wdt_restart;
return 0;
}
static struct of_device_id sun6i_reboot_of_match[] = {
{ .compatible = "allwinner,sun6i-a31-wdt" },
{}
};
static struct platform_driver sun6i_reboot_driver = {
.probe = sun6i_reboot_probe,
.driver = {
.name = "sun6i-reboot",
.of_match_table = sun6i_reboot_of_match,
},
};
module_platform_driver(sun6i_reboot_driver);
...@@ -111,23 +111,20 @@ static int _vexpress_register_restart_handler(struct device *dev) ...@@ -111,23 +111,20 @@ static int _vexpress_register_restart_handler(struct device *dev)
static int vexpress_reset_probe(struct platform_device *pdev) static int vexpress_reset_probe(struct platform_device *pdev)
{ {
enum vexpress_reset_func func;
const struct of_device_id *match = const struct of_device_id *match =
of_match_device(vexpress_reset_of_match, &pdev->dev); of_match_device(vexpress_reset_of_match, &pdev->dev);
struct regmap *regmap; struct regmap *regmap;
int ret = 0; int ret = 0;
if (match) if (!match)
func = (enum vexpress_reset_func)match->data; return -EINVAL;
else
func = pdev->id_entry->driver_data;
regmap = devm_regmap_init_vexpress_config(&pdev->dev); regmap = devm_regmap_init_vexpress_config(&pdev->dev);
if (IS_ERR(regmap)) if (IS_ERR(regmap))
return PTR_ERR(regmap); return PTR_ERR(regmap);
dev_set_drvdata(&pdev->dev, regmap); dev_set_drvdata(&pdev->dev, regmap);
switch (func) { switch ((enum vexpress_reset_func)match->data) {
case FUNC_SHUTDOWN: case FUNC_SHUTDOWN:
vexpress_power_off_device = &pdev->dev; vexpress_power_off_device = &pdev->dev;
pm_power_off = vexpress_power_off; pm_power_off = vexpress_power_off;
...@@ -144,20 +141,12 @@ static int vexpress_reset_probe(struct platform_device *pdev) ...@@ -144,20 +141,12 @@ static int vexpress_reset_probe(struct platform_device *pdev)
return ret; return ret;
} }
static const struct platform_device_id vexpress_reset_id_table[] = {
{ .name = "vexpress-reset", .driver_data = FUNC_RESET, },
{ .name = "vexpress-shutdown", .driver_data = FUNC_SHUTDOWN, },
{ .name = "vexpress-reboot", .driver_data = FUNC_REBOOT, },
{}
};
static struct platform_driver vexpress_reset_driver = { static struct platform_driver vexpress_reset_driver = {
.probe = vexpress_reset_probe, .probe = vexpress_reset_probe,
.driver = { .driver = {
.name = "vexpress-reset", .name = "vexpress-reset",
.of_match_table = vexpress_reset_of_match, .of_match_table = vexpress_reset_of_match,
}, },
.id_table = vexpress_reset_id_table,
}; };
static int __init vexpress_reset_init(void) static int __init vexpress_reset_init(void)
......
/*
* Fuel gauge driver for Richtek RT5033
*
* Copyright (C) 2014 Samsung Electronics, Co., Ltd.
* Author: Beomho Seo <beomho.seo@samsung.com>
*
* 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 bythe Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/mfd/rt5033-private.h>
#include <linux/mfd/rt5033.h>
static int rt5033_battery_get_capacity(struct i2c_client *client)
{
struct rt5033_battery *battery = i2c_get_clientdata(client);
u32 msb;
regmap_read(battery->regmap, RT5033_FUEL_REG_SOC_H, &msb);
return msb;
}
static int rt5033_battery_get_present(struct i2c_client *client)
{
struct rt5033_battery *battery = i2c_get_clientdata(client);
u32 val;
regmap_read(battery->regmap, RT5033_FUEL_REG_CONFIG_L, &val);
return (val & RT5033_FUEL_BAT_PRESENT) ? true : false;
}
static int rt5033_battery_get_watt_prop(struct i2c_client *client,
enum power_supply_property psp)
{
struct rt5033_battery *battery = i2c_get_clientdata(client);
unsigned int regh, regl;
int ret;
u32 msb, lsb;
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
regh = RT5033_FUEL_REG_VBAT_H;
regl = RT5033_FUEL_REG_VBAT_L;
break;
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
regh = RT5033_FUEL_REG_AVG_VOLT_H;
regl = RT5033_FUEL_REG_AVG_VOLT_L;
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
regh = RT5033_FUEL_REG_OCV_H;
regl = RT5033_FUEL_REG_OCV_L;
break;
default:
return -EINVAL;
}
regmap_read(battery->regmap, regh, &msb);
regmap_read(battery->regmap, regl, &lsb);
ret = ((msb << 4) + (lsb >> 4)) * 1250 / 1000;
return ret;
}
static int rt5033_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct rt5033_battery *battery = container_of(psy,
struct rt5033_battery, psy);
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
val->intval = rt5033_battery_get_watt_prop(battery->client,
psp);
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = rt5033_battery_get_present(battery->client);
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = rt5033_battery_get_capacity(battery->client);
break;
default:
return -EINVAL;
}
return 0;
}
static enum power_supply_property rt5033_battery_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY,
};
static struct regmap_config rt5033_battery_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = RT5033_FUEL_REG_END,
};
static int rt5033_battery_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct rt5033_battery *battery;
u32 ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
return -EIO;
battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL);
if (!battery)
return -EINVAL;
battery->client = client;
battery->regmap = devm_regmap_init_i2c(client,
&rt5033_battery_regmap_config);
if (IS_ERR(battery->regmap)) {
dev_err(&client->dev, "Failed to initialize regmap\n");
return -EINVAL;
}
i2c_set_clientdata(client, battery);
battery->psy.name = "rt5033-battery";
battery->psy.type = POWER_SUPPLY_TYPE_BATTERY;
battery->psy.get_property = rt5033_battery_get_property;
battery->psy.properties = rt5033_battery_props;
battery->psy.num_properties = ARRAY_SIZE(rt5033_battery_props);
ret = power_supply_register(&client->dev, &battery->psy);
if (ret) {
dev_err(&client->dev, "Failed to register power supply\n");
return ret;
}
return 0;
}
static int rt5033_battery_remove(struct i2c_client *client)
{
struct rt5033_battery *battery = i2c_get_clientdata(client);
power_supply_unregister(&battery->psy);
return 0;
}
static const struct i2c_device_id rt5033_battery_id[] = {
{ "rt5033-battery", },
{ }
};
MODULE_DEVICE_TABLE(platform, rt5033_battery_id);
static struct i2c_driver rt5033_battery_driver = {
.driver = {
.name = "rt5033-battery",
},
.probe = rt5033_battery_probe,
.remove = rt5033_battery_remove,
.id_table = rt5033_battery_id,
};
module_i2c_driver(rt5033_battery_driver);
MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver");
MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
MODULE_LICENSE("GPL");
...@@ -21,6 +21,13 @@ ...@@ -21,6 +21,13 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/vermagic.h> #include <linux/vermagic.h>
enum test_power_id {
TEST_AC,
TEST_BATTERY,
TEST_USB,
TEST_POWER_NUM,
};
static int ac_online = 1; static int ac_online = 1;
static int usb_online = 1; static int usb_online = 1;
static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING; static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
...@@ -147,7 +154,7 @@ static char *test_power_ac_supplied_to[] = { ...@@ -147,7 +154,7 @@ static char *test_power_ac_supplied_to[] = {
}; };
static struct power_supply test_power_supplies[] = { static struct power_supply test_power_supplies[] = {
{ [TEST_AC] = {
.name = "test_ac", .name = "test_ac",
.type = POWER_SUPPLY_TYPE_MAINS, .type = POWER_SUPPLY_TYPE_MAINS,
.supplied_to = test_power_ac_supplied_to, .supplied_to = test_power_ac_supplied_to,
...@@ -155,13 +162,15 @@ static struct power_supply test_power_supplies[] = { ...@@ -155,13 +162,15 @@ static struct power_supply test_power_supplies[] = {
.properties = test_power_ac_props, .properties = test_power_ac_props,
.num_properties = ARRAY_SIZE(test_power_ac_props), .num_properties = ARRAY_SIZE(test_power_ac_props),
.get_property = test_power_get_ac_property, .get_property = test_power_get_ac_property,
}, { },
[TEST_BATTERY] = {
.name = "test_battery", .name = "test_battery",
.type = POWER_SUPPLY_TYPE_BATTERY, .type = POWER_SUPPLY_TYPE_BATTERY,
.properties = test_power_battery_props, .properties = test_power_battery_props,
.num_properties = ARRAY_SIZE(test_power_battery_props), .num_properties = ARRAY_SIZE(test_power_battery_props),
.get_property = test_power_get_battery_property, .get_property = test_power_get_battery_property,
}, { },
[TEST_USB] = {
.name = "test_usb", .name = "test_usb",
.type = POWER_SUPPLY_TYPE_USB, .type = POWER_SUPPLY_TYPE_USB,
.supplied_to = test_power_ac_supplied_to, .supplied_to = test_power_ac_supplied_to,
...@@ -178,6 +187,8 @@ static int __init test_power_init(void) ...@@ -178,6 +187,8 @@ static int __init test_power_init(void)
int i; int i;
int ret; int ret;
BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_supplies));
for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) { for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) {
ret = power_supply_register(NULL, &test_power_supplies[i]); ret = power_supply_register(NULL, &test_power_supplies[i]);
if (ret) { if (ret) {
...@@ -309,7 +320,7 @@ static inline void signal_power_supply_changed(struct power_supply *psy) ...@@ -309,7 +320,7 @@ static inline void signal_power_supply_changed(struct power_supply *psy)
static int param_set_ac_online(const char *key, const struct kernel_param *kp) 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); ac_online = map_get_value(map_ac_online, key, ac_online);
signal_power_supply_changed(&test_power_supplies[0]); signal_power_supply_changed(&test_power_supplies[TEST_AC]);
return 0; return 0;
} }
...@@ -322,7 +333,7 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp) ...@@ -322,7 +333,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) 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); usb_online = map_get_value(map_ac_online, key, usb_online);
signal_power_supply_changed(&test_power_supplies[2]); signal_power_supply_changed(&test_power_supplies[TEST_USB]);
return 0; return 0;
} }
...@@ -336,7 +347,7 @@ static int param_set_battery_status(const char *key, ...@@ -336,7 +347,7 @@ static int param_set_battery_status(const char *key,
const struct kernel_param *kp) const struct kernel_param *kp)
{ {
battery_status = map_get_value(map_status, key, battery_status); battery_status = map_get_value(map_status, key, battery_status);
signal_power_supply_changed(&test_power_supplies[1]); signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
return 0; return 0;
} }
...@@ -350,7 +361,7 @@ static int param_set_battery_health(const char *key, ...@@ -350,7 +361,7 @@ static int param_set_battery_health(const char *key,
const struct kernel_param *kp) const struct kernel_param *kp)
{ {
battery_health = map_get_value(map_health, key, battery_health); battery_health = map_get_value(map_health, key, battery_health);
signal_power_supply_changed(&test_power_supplies[1]); signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
return 0; return 0;
} }
...@@ -364,7 +375,7 @@ static int param_set_battery_present(const char *key, ...@@ -364,7 +375,7 @@ static int param_set_battery_present(const char *key,
const struct kernel_param *kp) const struct kernel_param *kp)
{ {
battery_present = map_get_value(map_present, key, battery_present); battery_present = map_get_value(map_present, key, battery_present);
signal_power_supply_changed(&test_power_supplies[0]); signal_power_supply_changed(&test_power_supplies[TEST_AC]);
return 0; return 0;
} }
...@@ -380,7 +391,7 @@ static int param_set_battery_technology(const char *key, ...@@ -380,7 +391,7 @@ static int param_set_battery_technology(const char *key,
{ {
battery_technology = map_get_value(map_technology, key, battery_technology = map_get_value(map_technology, key,
battery_technology); battery_technology);
signal_power_supply_changed(&test_power_supplies[1]); signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
return 0; return 0;
} }
...@@ -401,7 +412,7 @@ static int param_set_battery_capacity(const char *key, ...@@ -401,7 +412,7 @@ static int param_set_battery_capacity(const char *key,
return -EINVAL; return -EINVAL;
battery_capacity = tmp; battery_capacity = tmp;
signal_power_supply_changed(&test_power_supplies[1]); signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
return 0; return 0;
} }
...@@ -416,7 +427,7 @@ static int param_set_battery_voltage(const char *key, ...@@ -416,7 +427,7 @@ static int param_set_battery_voltage(const char *key,
return -EINVAL; return -EINVAL;
battery_voltage = tmp; battery_voltage = tmp;
signal_power_supply_changed(&test_power_supplies[1]); signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
return 0; return 0;
} }
......
...@@ -461,7 +461,6 @@ struct ab8500_fg; ...@@ -461,7 +461,6 @@ struct ab8500_fg;
#ifdef CONFIG_AB8500_BM #ifdef CONFIG_AB8500_BM
extern struct abx500_bm_data ab8500_bm_data; extern struct abx500_bm_data ab8500_bm_data;
void ab8500_fg_reinit(void);
void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA); void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
struct ab8500_btemp *ab8500_btemp_get(void); struct ab8500_btemp *ab8500_btemp_get(void);
int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp); int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp);
......
...@@ -143,10 +143,118 @@ enum max77693_pmic_reg { ...@@ -143,10 +143,118 @@ enum max77693_pmic_reg {
#define FLASH_INT_FLED1_SHORT BIT(3) #define FLASH_INT_FLED1_SHORT BIT(3)
#define FLASH_INT_OVER_CURRENT BIT(4) #define FLASH_INT_OVER_CURRENT BIT(4)
/* Fast charge timer in in hours */
#define DEFAULT_FAST_CHARGE_TIMER 4
/* microamps */
#define DEFAULT_TOP_OFF_THRESHOLD_CURRENT 150000
/* minutes */
#define DEFAULT_TOP_OFF_TIMER 30
/* microvolts */
#define DEFAULT_CONSTANT_VOLT 4200000
/* microvolts */
#define DEFAULT_MIN_SYSTEM_VOLT 3600000
/* celsius */
#define DEFAULT_THERMAL_REGULATION_TEMP 100
/* microamps */
#define DEFAULT_BATTERY_OVERCURRENT 3500000
/* microvolts */
#define DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT 4300000
/* MAX77693_CHG_REG_CHG_INT_OK register */
#define CHG_INT_OK_BYP_SHIFT 0
#define CHG_INT_OK_BAT_SHIFT 3
#define CHG_INT_OK_CHG_SHIFT 4
#define CHG_INT_OK_CHGIN_SHIFT 6
#define CHG_INT_OK_DETBAT_SHIFT 7
#define CHG_INT_OK_BYP_MASK BIT(CHG_INT_OK_BYP_SHIFT)
#define CHG_INT_OK_BAT_MASK BIT(CHG_INT_OK_BAT_SHIFT)
#define CHG_INT_OK_CHG_MASK BIT(CHG_INT_OK_CHG_SHIFT)
#define CHG_INT_OK_CHGIN_MASK BIT(CHG_INT_OK_CHGIN_SHIFT)
#define CHG_INT_OK_DETBAT_MASK BIT(CHG_INT_OK_DETBAT_SHIFT)
/* MAX77693_CHG_REG_CHG_DETAILS_00 register */
#define CHG_DETAILS_00_CHGIN_SHIFT 5
#define CHG_DETAILS_00_CHGIN_MASK (0x3 << CHG_DETAILS_00_CHGIN_SHIFT)
/* MAX77693_CHG_REG_CHG_DETAILS_01 register */
#define CHG_DETAILS_01_CHG_SHIFT 0
#define CHG_DETAILS_01_BAT_SHIFT 4
#define CHG_DETAILS_01_TREG_SHIFT 7
#define CHG_DETAILS_01_CHG_MASK (0xf << CHG_DETAILS_01_CHG_SHIFT)
#define CHG_DETAILS_01_BAT_MASK (0x7 << CHG_DETAILS_01_BAT_SHIFT)
#define CHG_DETAILS_01_TREG_MASK BIT(7)
/* MAX77693_CHG_REG_CHG_DETAILS_01/CHG field */
enum max77693_charger_charging_state {
MAX77693_CHARGING_PREQUALIFICATION = 0x0,
MAX77693_CHARGING_FAST_CONST_CURRENT,
MAX77693_CHARGING_FAST_CONST_VOLTAGE,
MAX77693_CHARGING_TOP_OFF,
MAX77693_CHARGING_DONE,
MAX77693_CHARGING_HIGH_TEMP,
MAX77693_CHARGING_TIMER_EXPIRED,
MAX77693_CHARGING_THERMISTOR_SUSPEND,
MAX77693_CHARGING_OFF,
MAX77693_CHARGING_RESERVED,
MAX77693_CHARGING_OVER_TEMP,
MAX77693_CHARGING_WATCHDOG_EXPIRED,
};
/* MAX77693_CHG_REG_CHG_DETAILS_01/BAT field */
enum max77693_charger_battery_state {
MAX77693_BATTERY_NOBAT = 0x0,
/* Dead-battery or low-battery prequalification */
MAX77693_BATTERY_PREQUALIFICATION,
MAX77693_BATTERY_TIMER_EXPIRED,
MAX77693_BATTERY_GOOD,
MAX77693_BATTERY_LOWVOLTAGE,
MAX77693_BATTERY_OVERVOLTAGE,
MAX77693_BATTERY_OVERCURRENT,
MAX77693_BATTERY_RESERVED,
};
/* MAX77693_CHG_REG_CHG_DETAILS_02 register */
#define CHG_DETAILS_02_BYP_SHIFT 0
#define CHG_DETAILS_02_BYP_MASK (0xf << CHG_DETAILS_02_BYP_SHIFT)
/* MAX77693 CHG_CNFG_00 register */ /* MAX77693 CHG_CNFG_00 register */
#define CHG_CNFG_00_CHG_MASK 0x1 #define CHG_CNFG_00_CHG_MASK 0x1
#define CHG_CNFG_00_BUCK_MASK 0x4 #define CHG_CNFG_00_BUCK_MASK 0x4
/* MAX77693_CHG_REG_CHG_CNFG_01 register */
#define CHG_CNFG_01_FCHGTIME_SHIFT 0
#define CHG_CNFG_01_CHGRSTRT_SHIFT 4
#define CHG_CNFG_01_PQEN_SHIFT 7
#define CHG_CNFG_01_FCHGTIME_MASK (0x7 << CHG_CNFG_01_FCHGTIME_SHIFT)
#define CHG_CNFG_01_CHGRSTRT_MASK (0x3 << CHG_CNFG_01_CHGRSTRT_SHIFT)
#define CHG_CNFG_01_PQEN_MAKS BIT(CHG_CNFG_01_PQEN_SHIFT)
/* MAX77693_CHG_REG_CHG_CNFG_03 register */
#define CHG_CNFG_03_TOITH_SHIFT 0
#define CHG_CNFG_03_TOTIME_SHIFT 3
#define CHG_CNFG_03_TOITH_MASK (0x7 << CHG_CNFG_03_TOITH_SHIFT)
#define CHG_CNFG_03_TOTIME_MASK (0x7 << CHG_CNFG_03_TOTIME_SHIFT)
/* MAX77693_CHG_REG_CHG_CNFG_04 register */
#define CHG_CNFG_04_CHGCVPRM_SHIFT 0
#define CHG_CNFG_04_MINVSYS_SHIFT 5
#define CHG_CNFG_04_CHGCVPRM_MASK (0x1f << CHG_CNFG_04_CHGCVPRM_SHIFT)
#define CHG_CNFG_04_MINVSYS_MASK (0x7 << CHG_CNFG_04_MINVSYS_SHIFT)
/* MAX77693_CHG_REG_CHG_CNFG_06 register */
#define CHG_CNFG_06_CHGPROT_SHIFT 2
#define CHG_CNFG_06_CHGPROT_MASK (0x3 << CHG_CNFG_06_CHGPROT_SHIFT)
/* MAX77693_CHG_REG_CHG_CNFG_07 register */
#define CHG_CNFG_07_REGTEMP_SHIFT 5
#define CHG_CNFG_07_REGTEMP_MASK (0x3 << CHG_CNFG_07_REGTEMP_SHIFT)
/* MAX77693_CHG_REG_CHG_CNFG_12 register */
#define CHG_CNFG_12_B2SOVRC_SHIFT 0
#define CHG_CNFG_12_VCHGINREG_SHIFT 3
#define CHG_CNFG_12_B2SOVRC_MASK (0x7 << CHG_CNFG_12_B2SOVRC_SHIFT)
#define CHG_CNFG_12_VCHGINREG_MASK (0x3 << CHG_CNFG_12_VCHGINREG_SHIFT)
/* MAX77693 CHG_CNFG_09 Register */ /* MAX77693 CHG_CNFG_09 Register */
#define CHG_CNFG_09_CHGIN_ILIM_MASK 0x7F #define CHG_CNFG_09_CHGIN_ILIM_MASK 0x7F
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/extcon.h> #include <linux/extcon.h>
#include <linux/alarmtimer.h>
enum data_source { enum data_source {
CM_BATTERY_PRESENT, CM_BATTERY_PRESENT,
...@@ -44,29 +45,6 @@ enum cm_event_types { ...@@ -44,29 +45,6 @@ enum cm_event_types {
CM_EVENT_OTHERS, CM_EVENT_OTHERS,
}; };
/**
* struct charger_global_desc
* @rtc_name: the name of RTC used to wake up the system from suspend.
* @rtc_only_wakeup:
* If the system is woken up by waekup-sources other than the RTC or
* callbacks, Charger Manager should recognize with
* rtc_only_wakeup() returning false.
* If the RTC given to CM is the only wakeup reason,
* rtc_only_wakeup should return true.
* @assume_timer_stops_in_suspend:
* Assume that the jiffy timer stops in suspend-to-RAM.
* When enabled, CM does not rely on jiffies value in
* suspend_again and assumes that jiffies value does not
* change during suspend.
*/
struct charger_global_desc {
char *rtc_name;
bool (*rtc_only_wakeup)(void);
bool assume_timer_stops_in_suspend;
};
/** /**
* struct charger_cable * struct charger_cable
* @extcon_name: the name of extcon device. * @extcon_name: the name of extcon device.
...@@ -266,22 +244,14 @@ struct charger_manager { ...@@ -266,22 +244,14 @@ struct charger_manager {
char psy_name_buf[PSY_NAME_MAX + 1]; char psy_name_buf[PSY_NAME_MAX + 1];
struct power_supply charger_psy; struct power_supply charger_psy;
bool status_save_ext_pwr_inserted;
bool status_save_batt;
u64 charging_start_time; u64 charging_start_time;
u64 charging_end_time; u64 charging_end_time;
}; };
#ifdef CONFIG_CHARGER_MANAGER #ifdef CONFIG_CHARGER_MANAGER
extern int setup_charger_manager(struct charger_global_desc *gd);
extern bool cm_suspend_again(void);
extern void cm_notify_event(struct power_supply *psy, extern void cm_notify_event(struct power_supply *psy,
enum cm_event_types type, char *msg); enum cm_event_types type, char *msg);
#else #else
static inline int setup_charger_manager(struct charger_global_desc *gd)
{ return 0; }
static inline bool cm_suspend_again(void) { return false; }
static inline void cm_notify_event(struct power_supply *psy, static inline void cm_notify_event(struct power_supply *psy,
enum cm_event_types type, char *msg) { } enum cm_event_types type, char *msg) { }
#endif #endif
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册