diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-11 01:58:14 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-11 01:58:14 -0400 |
commit | a22a0fdba4191473581f86c9dd5361cf581521d3 (patch) | |
tree | ef5d3992f791641d6c8c16cee781f214fecbb105 /drivers/power | |
parent | bf83e61464803d386d0ec3fc92e5449d7963a409 (diff) | |
parent | db15e6312efd537e2deb2cbad110c23f98704a3c (diff) |
Merge tag 'for-v3.12' of git://git.infradead.org/battery-2.6
Pull battery/power supply driver updates from Anton Vorontsov:
"New drivers:
- APM X-Gene system reboot driver by Feng Kan and Loc Ho (APM).
- Qualcomm MSM reboot/poweroff driver by Abhimanyu Kapur (Codeaurora).
- Texas Instruments BQ24190 charger driver by Mark A. Greer (Animal
Creek Technologies).
- Texas Instruments TWL4030 MADC battery driver by Lukas Märdian and
Marek Belisko (Golden Delicious Computers). The driver is used on
Freerunner GTA04 phones.
Highlighted fixes and improvements:
- Suspend/wakeup logic improvements: power supply objects will block
system suspend until all power supply events are processed. Thanks
to Zoran Markovic (Linaro), Arve Hjonnevag and Todd Poynor (Google)"
* tag 'for-v3.12' of git://git.infradead.org/battery-2.6:
rx51_battery: Fix channel number when reading adc value
power: Add twl4030_madc battery driver.
bq24190_charger: Workaround SS definition problem on i386 builds
power_supply: Prevent suspend until power supply events are processed
vexpress-poweroff: Should depend on the required infrastructure
twl4030-charger: Fix compiler warning with regulator_enable()
rx51_battery: Replace hardcoded channels values.
bq24190_charger: Add support for TI BQ24190 Battery Charger
ab8500-charger: We print an unintended error message
max8925_power: Fix missing of_node_put
power_supply: Replace strict_strtol() with kstrtol()
power: Add APM X-Gene system reboot driver
power_supply: tosa_battery: Get rid of irq_to_gpio usage
power supply: collie_battery: Convert to use dev_pm_ops
power_supply: Make goldfish_battery depend on GOLDFISH || COMPILE_TEST
power: reset: Add msm restart support
MAINTAINERS: drivers/power: add entry for SmartReflex AVS drivers
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 15 | ||||
-rw-r--r-- | drivers/power/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/ab8500_charger.c | 1 | ||||
-rw-r--r-- | drivers/power/bq24190_charger.c | 1549 | ||||
-rw-r--r-- | drivers/power/collie_battery.c | 2 | ||||
-rw-r--r-- | drivers/power/max8925_power.c | 1 | ||||
-rw-r--r-- | drivers/power/power_supply_core.c | 38 | ||||
-rw-r--r-- | drivers/power/power_supply_sysfs.c | 2 | ||||
-rw-r--r-- | drivers/power/reset/Kconfig | 15 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/reset/msm-poweroff.c | 73 | ||||
-rw-r--r-- | drivers/power/reset/xgene-reboot.c | 103 | ||||
-rw-r--r-- | drivers/power/rx51_battery.c | 14 | ||||
-rw-r--r-- | drivers/power/tosa_battery.c | 2 | ||||
-rw-r--r-- | drivers/power/twl4030_charger.c | 7 | ||||
-rw-r--r-- | drivers/power/twl4030_madc_battery.c | 245 |
16 files changed, 2054 insertions, 17 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 7b8979c63f48..bb49ab684f9a 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -216,6 +216,13 @@ config BATTERY_S3C_ADC | |||
216 | help | 216 | help |
217 | Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery | 217 | Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery |
218 | 218 | ||
219 | config BATTERY_TWL4030_MADC | ||
220 | tristate "TWL4030 MADC battery driver" | ||
221 | depends on TWL4030_MADC | ||
222 | help | ||
223 | Say Y here to enable this dumb driver for batteries managed | ||
224 | through the TWL4030 MADC. | ||
225 | |||
219 | config CHARGER_88PM860X | 226 | config CHARGER_88PM860X |
220 | tristate "Marvell 88PM860x Charger driver" | 227 | tristate "Marvell 88PM860x Charger driver" |
221 | depends on MFD_88PM860X && BATTERY_88PM860X | 228 | depends on MFD_88PM860X && BATTERY_88PM860X |
@@ -334,6 +341,12 @@ config CHARGER_BQ2415X | |||
334 | You'll need this driver to charge batteries on e.g. Nokia | 341 | You'll need this driver to charge batteries on e.g. Nokia |
335 | RX-51/N900. | 342 | RX-51/N900. |
336 | 343 | ||
344 | config CHARGER_BQ24190 | ||
345 | tristate "TI BQ24190 battery charger driver" | ||
346 | depends on I2C && GPIOLIB | ||
347 | help | ||
348 | Say Y to enable support for the TI BQ24190 battery charger. | ||
349 | |||
337 | config CHARGER_SMB347 | 350 | config CHARGER_SMB347 |
338 | tristate "Summit Microelectronics SMB347 Battery Charger" | 351 | tristate "Summit Microelectronics SMB347 Battery Charger" |
339 | depends on I2C | 352 | depends on I2C |
@@ -357,7 +370,7 @@ config AB8500_BM | |||
357 | 370 | ||
358 | config BATTERY_GOLDFISH | 371 | config BATTERY_GOLDFISH |
359 | tristate "Goldfish battery driver" | 372 | tristate "Goldfish battery driver" |
360 | depends on GENERIC_HARDIRQS | 373 | depends on GENERIC_HARDIRQS && (GOLDFISH || COMPILE_TEST) |
361 | help | 374 | help |
362 | Say Y to enable support for the battery and AC power in the | 375 | Say Y to enable support for the battery and AC power in the |
363 | Goldfish emulator. | 376 | Goldfish emulator. |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 653bf6ceff30..a4b74177706f 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o | |||
34 | obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o | 34 | obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o |
35 | obj-$(CONFIG_BATTERY_Z2) += z2_battery.o | 35 | obj-$(CONFIG_BATTERY_Z2) += z2_battery.o |
36 | obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o | 36 | obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o |
37 | obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o | ||
37 | obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o | 38 | obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o |
38 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o | 39 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o |
39 | obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o | 40 | obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o |
@@ -50,6 +51,7 @@ obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o | |||
50 | obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o | 51 | obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o |
51 | obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o | 52 | obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o |
52 | obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o | 53 | obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o |
54 | obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o | ||
53 | obj-$(CONFIG_POWER_AVS) += avs/ | 55 | obj-$(CONFIG_POWER_AVS) += avs/ |
54 | obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o | 56 | obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o |
55 | obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o | 57 | obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o |
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index f098fdafee9f..a4c4a10b3a41 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c | |||
@@ -774,6 +774,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, | |||
774 | di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; | 774 | di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; |
775 | dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status, | 775 | dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status, |
776 | di->max_usb_in_curr.usb_type_max); | 776 | di->max_usb_in_curr.usb_type_max); |
777 | break; | ||
777 | case USB_STAT_NOT_VALID_LINK: | 778 | case USB_STAT_NOT_VALID_LINK: |
778 | dev_err(di->dev, "USB Type invalid - try charging anyway\n"); | 779 | dev_err(di->dev, "USB Type invalid - try charging anyway\n"); |
779 | di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; | 780 | di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; |
diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c new file mode 100644 index 000000000000..ad3ff8fbfbbb --- /dev/null +++ b/drivers/power/bq24190_charger.c | |||
@@ -0,0 +1,1549 @@ | |||
1 | /* | ||
2 | * Driver for the TI bq24190 battery charger. | ||
3 | * | ||
4 | * Author: Mark A. Greer <mgreer@animalcreek.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/of_irq.h> | ||
15 | #include <linux/of_device.h> | ||
16 | #include <linux/pm_runtime.h> | ||
17 | #include <linux/power_supply.h> | ||
18 | #include <linux/gpio.h> | ||
19 | #include <linux/i2c.h> | ||
20 | |||
21 | #include <linux/power/bq24190_charger.h> | ||
22 | |||
23 | |||
24 | #define BQ24190_MANUFACTURER "Texas Instruments" | ||
25 | |||
26 | #define BQ24190_REG_ISC 0x00 /* Input Source Control */ | ||
27 | #define BQ24190_REG_ISC_EN_HIZ_MASK BIT(7) | ||
28 | #define BQ24190_REG_ISC_EN_HIZ_SHIFT 7 | ||
29 | #define BQ24190_REG_ISC_VINDPM_MASK (BIT(6) | BIT(5) | BIT(4) | \ | ||
30 | BIT(3)) | ||
31 | #define BQ24190_REG_ISC_VINDPM_SHIFT 3 | ||
32 | #define BQ24190_REG_ISC_IINLIM_MASK (BIT(2) | BIT(1) | BIT(0)) | ||
33 | #define BQ24190_REG_ISC_IINLIM_SHIFT 0 | ||
34 | |||
35 | #define BQ24190_REG_POC 0x01 /* Power-On Configuration */ | ||
36 | #define BQ24190_REG_POC_RESET_MASK BIT(7) | ||
37 | #define BQ24190_REG_POC_RESET_SHIFT 7 | ||
38 | #define BQ24190_REG_POC_WDT_RESET_MASK BIT(6) | ||
39 | #define BQ24190_REG_POC_WDT_RESET_SHIFT 6 | ||
40 | #define BQ24190_REG_POC_CHG_CONFIG_MASK (BIT(5) | BIT(4)) | ||
41 | #define BQ24190_REG_POC_CHG_CONFIG_SHIFT 4 | ||
42 | #define BQ24190_REG_POC_SYS_MIN_MASK (BIT(3) | BIT(2) | BIT(1)) | ||
43 | #define BQ24190_REG_POC_SYS_MIN_SHIFT 1 | ||
44 | #define BQ24190_REG_POC_BOOST_LIM_MASK BIT(0) | ||
45 | #define BQ24190_REG_POC_BOOST_LIM_SHIFT 0 | ||
46 | |||
47 | #define BQ24190_REG_CCC 0x02 /* Charge Current Control */ | ||
48 | #define BQ24190_REG_CCC_ICHG_MASK (BIT(7) | BIT(6) | BIT(5) | \ | ||
49 | BIT(4) | BIT(3) | BIT(2)) | ||
50 | #define BQ24190_REG_CCC_ICHG_SHIFT 2 | ||
51 | #define BQ24190_REG_CCC_FORCE_20PCT_MASK BIT(0) | ||
52 | #define BQ24190_REG_CCC_FORCE_20PCT_SHIFT 0 | ||
53 | |||
54 | #define BQ24190_REG_PCTCC 0x03 /* Pre-charge/Termination Current Cntl */ | ||
55 | #define BQ24190_REG_PCTCC_IPRECHG_MASK (BIT(7) | BIT(6) | BIT(5) | \ | ||
56 | BIT(4)) | ||
57 | #define BQ24190_REG_PCTCC_IPRECHG_SHIFT 4 | ||
58 | #define BQ24190_REG_PCTCC_ITERM_MASK (BIT(3) | BIT(2) | BIT(1) | \ | ||
59 | BIT(0)) | ||
60 | #define BQ24190_REG_PCTCC_ITERM_SHIFT 0 | ||
61 | |||
62 | #define BQ24190_REG_CVC 0x04 /* Charge Voltage Control */ | ||
63 | #define BQ24190_REG_CVC_VREG_MASK (BIT(7) | BIT(6) | BIT(5) | \ | ||
64 | BIT(4) | BIT(3) | BIT(2)) | ||
65 | #define BQ24190_REG_CVC_VREG_SHIFT 2 | ||
66 | #define BQ24190_REG_CVC_BATLOWV_MASK BIT(1) | ||
67 | #define BQ24190_REG_CVC_BATLOWV_SHIFT 1 | ||
68 | #define BQ24190_REG_CVC_VRECHG_MASK BIT(0) | ||
69 | #define BQ24190_REG_CVC_VRECHG_SHIFT 0 | ||
70 | |||
71 | #define BQ24190_REG_CTTC 0x05 /* Charge Term/Timer Control */ | ||
72 | #define BQ24190_REG_CTTC_EN_TERM_MASK BIT(7) | ||
73 | #define BQ24190_REG_CTTC_EN_TERM_SHIFT 7 | ||
74 | #define BQ24190_REG_CTTC_TERM_STAT_MASK BIT(6) | ||
75 | #define BQ24190_REG_CTTC_TERM_STAT_SHIFT 6 | ||
76 | #define BQ24190_REG_CTTC_WATCHDOG_MASK (BIT(5) | BIT(4)) | ||
77 | #define BQ24190_REG_CTTC_WATCHDOG_SHIFT 4 | ||
78 | #define BQ24190_REG_CTTC_EN_TIMER_MASK BIT(3) | ||
79 | #define BQ24190_REG_CTTC_EN_TIMER_SHIFT 3 | ||
80 | #define BQ24190_REG_CTTC_CHG_TIMER_MASK (BIT(2) | BIT(1)) | ||
81 | #define BQ24190_REG_CTTC_CHG_TIMER_SHIFT 1 | ||
82 | #define BQ24190_REG_CTTC_JEITA_ISET_MASK BIT(0) | ||
83 | #define BQ24190_REG_CTTC_JEITA_ISET_SHIFT 0 | ||
84 | |||
85 | #define BQ24190_REG_ICTRC 0x06 /* IR Comp/Thermal Regulation Control */ | ||
86 | #define BQ24190_REG_ICTRC_BAT_COMP_MASK (BIT(7) | BIT(6) | BIT(5)) | ||
87 | #define BQ24190_REG_ICTRC_BAT_COMP_SHIFT 5 | ||
88 | #define BQ24190_REG_ICTRC_VCLAMP_MASK (BIT(4) | BIT(3) | BIT(2)) | ||
89 | #define BQ24190_REG_ICTRC_VCLAMP_SHIFT 2 | ||
90 | #define BQ24190_REG_ICTRC_TREG_MASK (BIT(1) | BIT(0)) | ||
91 | #define BQ24190_REG_ICTRC_TREG_SHIFT 0 | ||
92 | |||
93 | #define BQ24190_REG_MOC 0x07 /* Misc. Operation Control */ | ||
94 | #define BQ24190_REG_MOC_DPDM_EN_MASK BIT(7) | ||
95 | #define BQ24190_REG_MOC_DPDM_EN_SHIFT 7 | ||
96 | #define BQ24190_REG_MOC_TMR2X_EN_MASK BIT(6) | ||
97 | #define BQ24190_REG_MOC_TMR2X_EN_SHIFT 6 | ||
98 | #define BQ24190_REG_MOC_BATFET_DISABLE_MASK BIT(5) | ||
99 | #define BQ24190_REG_MOC_BATFET_DISABLE_SHIFT 5 | ||
100 | #define BQ24190_REG_MOC_JEITA_VSET_MASK BIT(4) | ||
101 | #define BQ24190_REG_MOC_JEITA_VSET_SHIFT 4 | ||
102 | #define BQ24190_REG_MOC_INT_MASK_MASK (BIT(1) | BIT(0)) | ||
103 | #define BQ24190_REG_MOC_INT_MASK_SHIFT 0 | ||
104 | |||
105 | #define BQ24190_REG_SS 0x08 /* System Status */ | ||
106 | #define BQ24190_REG_SS_VBUS_STAT_MASK (BIT(7) | BIT(6)) | ||
107 | #define BQ24190_REG_SS_VBUS_STAT_SHIFT 6 | ||
108 | #define BQ24190_REG_SS_CHRG_STAT_MASK (BIT(5) | BIT(4)) | ||
109 | #define BQ24190_REG_SS_CHRG_STAT_SHIFT 4 | ||
110 | #define BQ24190_REG_SS_DPM_STAT_MASK BIT(3) | ||
111 | #define BQ24190_REG_SS_DPM_STAT_SHIFT 3 | ||
112 | #define BQ24190_REG_SS_PG_STAT_MASK BIT(2) | ||
113 | #define BQ24190_REG_SS_PG_STAT_SHIFT 2 | ||
114 | #define BQ24190_REG_SS_THERM_STAT_MASK BIT(1) | ||
115 | #define BQ24190_REG_SS_THERM_STAT_SHIFT 1 | ||
116 | #define BQ24190_REG_SS_VSYS_STAT_MASK BIT(0) | ||
117 | #define BQ24190_REG_SS_VSYS_STAT_SHIFT 0 | ||
118 | |||
119 | #define BQ24190_REG_F 0x09 /* Fault */ | ||
120 | #define BQ24190_REG_F_WATCHDOG_FAULT_MASK BIT(7) | ||
121 | #define BQ24190_REG_F_WATCHDOG_FAULT_SHIFT 7 | ||
122 | #define BQ24190_REG_F_BOOST_FAULT_MASK BIT(6) | ||
123 | #define BQ24190_REG_F_BOOST_FAULT_SHIFT 6 | ||
124 | #define BQ24190_REG_F_CHRG_FAULT_MASK (BIT(5) | BIT(4)) | ||
125 | #define BQ24190_REG_F_CHRG_FAULT_SHIFT 4 | ||
126 | #define BQ24190_REG_F_BAT_FAULT_MASK BIT(3) | ||
127 | #define BQ24190_REG_F_BAT_FAULT_SHIFT 3 | ||
128 | #define BQ24190_REG_F_NTC_FAULT_MASK (BIT(2) | BIT(1) | BIT(0)) | ||
129 | #define BQ24190_REG_F_NTC_FAULT_SHIFT 0 | ||
130 | |||
131 | #define BQ24190_REG_VPRS 0x0A /* Vendor/Part/Revision Status */ | ||
132 | #define BQ24190_REG_VPRS_PN_MASK (BIT(5) | BIT(4) | BIT(3)) | ||
133 | #define BQ24190_REG_VPRS_PN_SHIFT 3 | ||
134 | #define BQ24190_REG_VPRS_PN_24190 0x4 | ||
135 | #define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193 */ | ||
136 | #define BQ24190_REG_VPRS_PN_24192I 0x3 | ||
137 | #define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2) | ||
138 | #define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2 | ||
139 | #define BQ24190_REG_VPRS_DEV_REG_MASK (BIT(1) | BIT(0)) | ||
140 | #define BQ24190_REG_VPRS_DEV_REG_SHIFT 0 | ||
141 | |||
142 | /* | ||
143 | * The FAULT register is latched by the bq24190 (except for NTC_FAULT) | ||
144 | * so the first read after a fault returns the latched value and subsequent | ||
145 | * reads return the current value. In order to return the fault status | ||
146 | * to the user, have the interrupt handler save the reg's value and retrieve | ||
147 | * it in the appropriate health/status routine. Each routine has its own | ||
148 | * flag indicating whether it should use the value stored by the last run | ||
149 | * of the interrupt handler or do an actual reg read. That way each routine | ||
150 | * can report back whatever fault may have occured. | ||
151 | */ | ||
152 | struct bq24190_dev_info { | ||
153 | struct i2c_client *client; | ||
154 | struct device *dev; | ||
155 | struct power_supply charger; | ||
156 | struct power_supply battery; | ||
157 | char model_name[I2C_NAME_SIZE]; | ||
158 | kernel_ulong_t model; | ||
159 | unsigned int gpio_int; | ||
160 | unsigned int irq; | ||
161 | struct mutex f_reg_lock; | ||
162 | bool first_time; | ||
163 | bool charger_health_valid; | ||
164 | bool battery_health_valid; | ||
165 | bool battery_status_valid; | ||
166 | u8 f_reg; | ||
167 | u8 ss_reg; | ||
168 | u8 watchdog; | ||
169 | }; | ||
170 | |||
171 | /* | ||
172 | * The tables below provide a 2-way mapping for the value that goes in | ||
173 | * the register field and the real-world value that it represents. | ||
174 | * The index of the array is the value that goes in the register; the | ||
175 | * number at that index in the array is the real-world value that it | ||
176 | * represents. | ||
177 | */ | ||
178 | /* REG02[7:2] (ICHG) in uAh */ | ||
179 | static const int bq24190_ccc_ichg_values[] = { | ||
180 | 512000, 576000, 640000, 704000, 768000, 832000, 896000, 960000, | ||
181 | 1024000, 1088000, 1152000, 1216000, 1280000, 1344000, 1408000, 1472000, | ||
182 | 1536000, 1600000, 1664000, 1728000, 1792000, 1856000, 1920000, 1984000, | ||
183 | 2048000, 2112000, 2176000, 2240000, 2304000, 2368000, 2432000, 2496000, | ||
184 | 2560000, 2624000, 2688000, 2752000, 2816000, 2880000, 2944000, 3008000, | ||
185 | 3072000, 3136000, 3200000, 3264000, 3328000, 3392000, 3456000, 3520000, | ||
186 | 3584000, 3648000, 3712000, 3776000, 3840000, 3904000, 3968000, 4032000, | ||
187 | 4096000, 4160000, 4224000, 4288000, 4352000, 4416000, 4480000, 4544000 | ||
188 | }; | ||
189 | |||
190 | /* REG04[7:2] (VREG) in uV */ | ||
191 | static const int bq24190_cvc_vreg_values[] = { | ||
192 | 3504000, 3520000, 3536000, 3552000, 3568000, 3584000, 3600000, 3616000, | ||
193 | 3632000, 3648000, 3664000, 3680000, 3696000, 3712000, 3728000, 3744000, | ||
194 | 3760000, 3776000, 3792000, 3808000, 3824000, 3840000, 3856000, 3872000, | ||
195 | 3888000, 3904000, 3920000, 3936000, 3952000, 3968000, 3984000, 4000000, | ||
196 | 4016000, 4032000, 4048000, 4064000, 4080000, 4096000, 4112000, 4128000, | ||
197 | 4144000, 4160000, 4176000, 4192000, 4208000, 4224000, 4240000, 4256000, | ||
198 | 4272000, 4288000, 4304000, 4320000, 4336000, 4352000, 4368000, 4384000, | ||
199 | 4400000 | ||
200 | }; | ||
201 | |||
202 | /* REG06[1:0] (TREG) in tenths of degrees Celcius */ | ||
203 | static const int bq24190_ictrc_treg_values[] = { | ||
204 | 600, 800, 1000, 1200 | ||
205 | }; | ||
206 | |||
207 | /* | ||
208 | * Return the index in 'tbl' of greatest value that is less than or equal to | ||
209 | * 'val'. The index range returned is 0 to 'tbl_size' - 1. Assumes that | ||
210 | * the values in 'tbl' are sorted from smallest to largest and 'tbl_size' | ||
211 | * is less than 2^8. | ||
212 | */ | ||
213 | static u8 bq24190_find_idx(const int tbl[], int tbl_size, int v) | ||
214 | { | ||
215 | int i; | ||
216 | |||
217 | for (i = 1; i < tbl_size; i++) | ||
218 | if (v < tbl[i]) | ||
219 | break; | ||
220 | |||
221 | return i - 1; | ||
222 | } | ||
223 | |||
224 | /* Basic driver I/O routines */ | ||
225 | |||
226 | static int bq24190_read(struct bq24190_dev_info *bdi, u8 reg, u8 *data) | ||
227 | { | ||
228 | int ret; | ||
229 | |||
230 | ret = i2c_smbus_read_byte_data(bdi->client, reg); | ||
231 | if (ret < 0) | ||
232 | return ret; | ||
233 | |||
234 | *data = ret; | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int bq24190_write(struct bq24190_dev_info *bdi, u8 reg, u8 data) | ||
239 | { | ||
240 | return i2c_smbus_write_byte_data(bdi->client, reg, data); | ||
241 | } | ||
242 | |||
243 | static int bq24190_read_mask(struct bq24190_dev_info *bdi, u8 reg, | ||
244 | u8 mask, u8 shift, u8 *data) | ||
245 | { | ||
246 | u8 v; | ||
247 | int ret; | ||
248 | |||
249 | ret = bq24190_read(bdi, reg, &v); | ||
250 | if (ret < 0) | ||
251 | return ret; | ||
252 | |||
253 | v &= mask; | ||
254 | v >>= shift; | ||
255 | *data = v; | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int bq24190_write_mask(struct bq24190_dev_info *bdi, u8 reg, | ||
261 | u8 mask, u8 shift, u8 data) | ||
262 | { | ||
263 | u8 v; | ||
264 | int ret; | ||
265 | |||
266 | ret = bq24190_read(bdi, reg, &v); | ||
267 | if (ret < 0) | ||
268 | return ret; | ||
269 | |||
270 | v &= ~mask; | ||
271 | v |= ((data << shift) & mask); | ||
272 | |||
273 | return bq24190_write(bdi, reg, v); | ||
274 | } | ||
275 | |||
276 | static int bq24190_get_field_val(struct bq24190_dev_info *bdi, | ||
277 | u8 reg, u8 mask, u8 shift, | ||
278 | const int tbl[], int tbl_size, | ||
279 | int *val) | ||
280 | { | ||
281 | u8 v; | ||
282 | int ret; | ||
283 | |||
284 | ret = bq24190_read_mask(bdi, reg, mask, shift, &v); | ||
285 | if (ret < 0) | ||
286 | return ret; | ||
287 | |||
288 | v = (v >= tbl_size) ? (tbl_size - 1) : v; | ||
289 | *val = tbl[v]; | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int bq24190_set_field_val(struct bq24190_dev_info *bdi, | ||
295 | u8 reg, u8 mask, u8 shift, | ||
296 | const int tbl[], int tbl_size, | ||
297 | int val) | ||
298 | { | ||
299 | u8 idx; | ||
300 | |||
301 | idx = bq24190_find_idx(tbl, tbl_size, val); | ||
302 | |||
303 | return bq24190_write_mask(bdi, reg, mask, shift, idx); | ||
304 | } | ||
305 | |||
306 | #ifdef CONFIG_SYSFS | ||
307 | /* | ||
308 | * There are a numerous options that are configurable on the bq24190 | ||
309 | * that go well beyond what the power_supply properties provide access to. | ||
310 | * Provide sysfs access to them so they can be examined and possibly modified | ||
311 | * on the fly. They will be provided for the charger power_supply object only | ||
312 | * and will be prefixed by 'f_' to make them easier to recognize. | ||
313 | */ | ||
314 | |||
315 | #define BQ24190_SYSFS_FIELD(_name, r, f, m, store) \ | ||
316 | { \ | ||
317 | .attr = __ATTR(f_##_name, m, bq24190_sysfs_show, store), \ | ||
318 | .reg = BQ24190_REG_##r, \ | ||
319 | .mask = BQ24190_REG_##r##_##f##_MASK, \ | ||
320 | .shift = BQ24190_REG_##r##_##f##_SHIFT, \ | ||
321 | } | ||
322 | |||
323 | #define BQ24190_SYSFS_FIELD_RW(_name, r, f) \ | ||
324 | BQ24190_SYSFS_FIELD(_name, r, f, S_IWUSR | S_IRUGO, \ | ||
325 | bq24190_sysfs_store) | ||
326 | |||
327 | #define BQ24190_SYSFS_FIELD_RO(_name, r, f) \ | ||
328 | BQ24190_SYSFS_FIELD(_name, r, f, S_IRUGO, NULL) | ||
329 | |||
330 | static ssize_t bq24190_sysfs_show(struct device *dev, | ||
331 | struct device_attribute *attr, char *buf); | ||
332 | static ssize_t bq24190_sysfs_store(struct device *dev, | ||
333 | struct device_attribute *attr, const char *buf, size_t count); | ||
334 | |||
335 | struct bq24190_sysfs_field_info { | ||
336 | struct device_attribute attr; | ||
337 | u8 reg; | ||
338 | u8 mask; | ||
339 | u8 shift; | ||
340 | }; | ||
341 | |||
342 | /* On i386 ptrace-abi.h defines SS that breaks the macro calls below. */ | ||
343 | #undef SS | ||
344 | |||
345 | static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl[] = { | ||
346 | /* sysfs name reg field in reg */ | ||
347 | BQ24190_SYSFS_FIELD_RW(en_hiz, ISC, EN_HIZ), | ||
348 | BQ24190_SYSFS_FIELD_RW(vindpm, ISC, VINDPM), | ||
349 | BQ24190_SYSFS_FIELD_RW(iinlim, ISC, IINLIM), | ||
350 | BQ24190_SYSFS_FIELD_RW(chg_config, POC, CHG_CONFIG), | ||
351 | BQ24190_SYSFS_FIELD_RW(sys_min, POC, SYS_MIN), | ||
352 | BQ24190_SYSFS_FIELD_RW(boost_lim, POC, BOOST_LIM), | ||
353 | BQ24190_SYSFS_FIELD_RW(ichg, CCC, ICHG), | ||
354 | BQ24190_SYSFS_FIELD_RW(force_20_pct, CCC, FORCE_20PCT), | ||
355 | BQ24190_SYSFS_FIELD_RW(iprechg, PCTCC, IPRECHG), | ||
356 | BQ24190_SYSFS_FIELD_RW(iterm, PCTCC, ITERM), | ||
357 | BQ24190_SYSFS_FIELD_RW(vreg, CVC, VREG), | ||
358 | BQ24190_SYSFS_FIELD_RW(batlowv, CVC, BATLOWV), | ||
359 | BQ24190_SYSFS_FIELD_RW(vrechg, CVC, VRECHG), | ||
360 | BQ24190_SYSFS_FIELD_RW(en_term, CTTC, EN_TERM), | ||
361 | BQ24190_SYSFS_FIELD_RW(term_stat, CTTC, TERM_STAT), | ||
362 | BQ24190_SYSFS_FIELD_RO(watchdog, CTTC, WATCHDOG), | ||
363 | BQ24190_SYSFS_FIELD_RW(en_timer, CTTC, EN_TIMER), | ||
364 | BQ24190_SYSFS_FIELD_RW(chg_timer, CTTC, CHG_TIMER), | ||
365 | BQ24190_SYSFS_FIELD_RW(jeta_iset, CTTC, JEITA_ISET), | ||
366 | BQ24190_SYSFS_FIELD_RW(bat_comp, ICTRC, BAT_COMP), | ||
367 | BQ24190_SYSFS_FIELD_RW(vclamp, ICTRC, VCLAMP), | ||
368 | BQ24190_SYSFS_FIELD_RW(treg, ICTRC, TREG), | ||
369 | BQ24190_SYSFS_FIELD_RW(dpdm_en, MOC, DPDM_EN), | ||
370 | BQ24190_SYSFS_FIELD_RW(tmr2x_en, MOC, TMR2X_EN), | ||
371 | BQ24190_SYSFS_FIELD_RW(batfet_disable, MOC, BATFET_DISABLE), | ||
372 | BQ24190_SYSFS_FIELD_RW(jeita_vset, MOC, JEITA_VSET), | ||
373 | BQ24190_SYSFS_FIELD_RO(int_mask, MOC, INT_MASK), | ||
374 | BQ24190_SYSFS_FIELD_RO(vbus_stat, SS, VBUS_STAT), | ||
375 | BQ24190_SYSFS_FIELD_RO(chrg_stat, SS, CHRG_STAT), | ||
376 | BQ24190_SYSFS_FIELD_RO(dpm_stat, SS, DPM_STAT), | ||
377 | BQ24190_SYSFS_FIELD_RO(pg_stat, SS, PG_STAT), | ||
378 | BQ24190_SYSFS_FIELD_RO(therm_stat, SS, THERM_STAT), | ||
379 | BQ24190_SYSFS_FIELD_RO(vsys_stat, SS, VSYS_STAT), | ||
380 | BQ24190_SYSFS_FIELD_RO(watchdog_fault, F, WATCHDOG_FAULT), | ||
381 | BQ24190_SYSFS_FIELD_RO(boost_fault, F, BOOST_FAULT), | ||
382 | BQ24190_SYSFS_FIELD_RO(chrg_fault, F, CHRG_FAULT), | ||
383 | BQ24190_SYSFS_FIELD_RO(bat_fault, F, BAT_FAULT), | ||
384 | BQ24190_SYSFS_FIELD_RO(ntc_fault, F, NTC_FAULT), | ||
385 | BQ24190_SYSFS_FIELD_RO(pn, VPRS, PN), | ||
386 | BQ24190_SYSFS_FIELD_RO(ts_profile, VPRS, TS_PROFILE), | ||
387 | BQ24190_SYSFS_FIELD_RO(dev_reg, VPRS, DEV_REG), | ||
388 | }; | ||
389 | |||
390 | static struct attribute * | ||
391 | bq24190_sysfs_attrs[ARRAY_SIZE(bq24190_sysfs_field_tbl) + 1]; | ||
392 | |||
393 | static const struct attribute_group bq24190_sysfs_attr_group = { | ||
394 | .attrs = bq24190_sysfs_attrs, | ||
395 | }; | ||
396 | |||
397 | static void bq24190_sysfs_init_attrs(void) | ||
398 | { | ||
399 | int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl); | ||
400 | |||
401 | for (i = 0; i < limit; i++) | ||
402 | bq24190_sysfs_attrs[i] = &bq24190_sysfs_field_tbl[i].attr.attr; | ||
403 | |||
404 | bq24190_sysfs_attrs[limit] = NULL; /* Has additional entry for this */ | ||
405 | } | ||
406 | |||
407 | static struct bq24190_sysfs_field_info *bq24190_sysfs_field_lookup( | ||
408 | const char *name) | ||
409 | { | ||
410 | int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl); | ||
411 | |||
412 | for (i = 0; i < limit; i++) | ||
413 | if (!strcmp(name, bq24190_sysfs_field_tbl[i].attr.attr.name)) | ||
414 | break; | ||
415 | |||
416 | if (i >= limit) | ||
417 | return NULL; | ||
418 | |||
419 | return &bq24190_sysfs_field_tbl[i]; | ||
420 | } | ||
421 | |||
422 | static ssize_t bq24190_sysfs_show(struct device *dev, | ||
423 | struct device_attribute *attr, char *buf) | ||
424 | { | ||
425 | struct power_supply *psy = dev_get_drvdata(dev); | ||
426 | struct bq24190_dev_info *bdi = | ||
427 | container_of(psy, struct bq24190_dev_info, charger); | ||
428 | struct bq24190_sysfs_field_info *info; | ||
429 | int ret; | ||
430 | u8 v; | ||
431 | |||
432 | info = bq24190_sysfs_field_lookup(attr->attr.name); | ||
433 | if (!info) | ||
434 | return -EINVAL; | ||
435 | |||
436 | ret = bq24190_read_mask(bdi, info->reg, info->mask, info->shift, &v); | ||
437 | if (ret) | ||
438 | return ret; | ||
439 | |||
440 | return scnprintf(buf, PAGE_SIZE, "%hhx\n", v); | ||
441 | } | ||
442 | |||
443 | static ssize_t bq24190_sysfs_store(struct device *dev, | ||
444 | struct device_attribute *attr, const char *buf, size_t count) | ||
445 | { | ||
446 | struct power_supply *psy = dev_get_drvdata(dev); | ||
447 | struct bq24190_dev_info *bdi = | ||
448 | container_of(psy, struct bq24190_dev_info, charger); | ||
449 | struct bq24190_sysfs_field_info *info; | ||
450 | int ret; | ||
451 | u8 v; | ||
452 | |||
453 | info = bq24190_sysfs_field_lookup(attr->attr.name); | ||
454 | if (!info) | ||
455 | return -EINVAL; | ||
456 | |||
457 | ret = kstrtou8(buf, 0, &v); | ||
458 | if (ret < 0) | ||
459 | return ret; | ||
460 | |||
461 | ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v); | ||
462 | if (ret) | ||
463 | return ret; | ||
464 | |||
465 | return count; | ||
466 | } | ||
467 | |||
468 | static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) | ||
469 | { | ||
470 | bq24190_sysfs_init_attrs(); | ||
471 | |||
472 | return sysfs_create_group(&bdi->charger.dev->kobj, | ||
473 | &bq24190_sysfs_attr_group); | ||
474 | } | ||
475 | |||
476 | static void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) | ||
477 | { | ||
478 | sysfs_remove_group(&bdi->charger.dev->kobj, &bq24190_sysfs_attr_group); | ||
479 | } | ||
480 | #else | ||
481 | static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) | ||
482 | { | ||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {} | ||
487 | #endif | ||
488 | |||
489 | /* | ||
490 | * According to the "Host Mode and default Mode" section of the | ||
491 | * manual, a write to any register causes the bq24190 to switch | ||
492 | * from default mode to host mode. It will switch back to default | ||
493 | * mode after a WDT timeout unless the WDT is turned off as well. | ||
494 | * So, by simply turning off the WDT, we accomplish both with the | ||
495 | * same write. | ||
496 | */ | ||
497 | static int bq24190_set_mode_host(struct bq24190_dev_info *bdi) | ||
498 | { | ||
499 | int ret; | ||
500 | u8 v; | ||
501 | |||
502 | ret = bq24190_read(bdi, BQ24190_REG_CTTC, &v); | ||
503 | if (ret < 0) | ||
504 | return ret; | ||
505 | |||
506 | bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >> | ||
507 | BQ24190_REG_CTTC_WATCHDOG_SHIFT); | ||
508 | v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK; | ||
509 | |||
510 | return bq24190_write(bdi, BQ24190_REG_CTTC, v); | ||
511 | } | ||
512 | |||
513 | static int bq24190_register_reset(struct bq24190_dev_info *bdi) | ||
514 | { | ||
515 | int ret, limit = 100; | ||
516 | u8 v; | ||
517 | |||
518 | /* Reset the registers */ | ||
519 | ret = bq24190_write_mask(bdi, BQ24190_REG_POC, | ||
520 | BQ24190_REG_POC_RESET_MASK, | ||
521 | BQ24190_REG_POC_RESET_SHIFT, | ||
522 | 0x1); | ||
523 | if (ret < 0) | ||
524 | return ret; | ||
525 | |||
526 | /* Reset bit will be cleared by hardware so poll until it is */ | ||
527 | do { | ||
528 | ret = bq24190_read_mask(bdi, BQ24190_REG_POC, | ||
529 | BQ24190_REG_POC_RESET_MASK, | ||
530 | BQ24190_REG_POC_RESET_SHIFT, | ||
531 | &v); | ||
532 | if (ret < 0) | ||
533 | return ret; | ||
534 | |||
535 | if (!v) | ||
536 | break; | ||
537 | |||
538 | udelay(10); | ||
539 | } while (--limit); | ||
540 | |||
541 | if (!limit) | ||
542 | return -EIO; | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | /* Charger power supply property routines */ | ||
548 | |||
549 | static int bq24190_charger_get_charge_type(struct bq24190_dev_info *bdi, | ||
550 | union power_supply_propval *val) | ||
551 | { | ||
552 | u8 v; | ||
553 | int type, ret; | ||
554 | |||
555 | ret = bq24190_read_mask(bdi, BQ24190_REG_POC, | ||
556 | BQ24190_REG_POC_CHG_CONFIG_MASK, | ||
557 | BQ24190_REG_POC_CHG_CONFIG_SHIFT, | ||
558 | &v); | ||
559 | if (ret < 0) | ||
560 | return ret; | ||
561 | |||
562 | /* If POC[CHG_CONFIG] (REG01[5:4]) == 0, charge is disabled */ | ||
563 | if (!v) { | ||
564 | type = POWER_SUPPLY_CHARGE_TYPE_NONE; | ||
565 | } else { | ||
566 | ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, | ||
567 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | ||
568 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, | ||
569 | &v); | ||
570 | if (ret < 0) | ||
571 | return ret; | ||
572 | |||
573 | type = (v) ? POWER_SUPPLY_CHARGE_TYPE_TRICKLE : | ||
574 | POWER_SUPPLY_CHARGE_TYPE_FAST; | ||
575 | } | ||
576 | |||
577 | val->intval = type; | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi, | ||
583 | const union power_supply_propval *val) | ||
584 | { | ||
585 | u8 chg_config, force_20pct, en_term; | ||
586 | int ret; | ||
587 | |||
588 | /* | ||
589 | * According to the "Termination when REG02[0] = 1" section of | ||
590 | * the bq24190 manual, the trickle charge could be less than the | ||
591 | * termination current so it recommends turning off the termination | ||
592 | * function. | ||
593 | * | ||
594 | * Note: AFAICT from the datasheet, the user will have to manually | ||
595 | * turn off the charging when in 20% mode. If its not turned off, | ||
596 | * there could be battery damage. So, use this mode at your own risk. | ||
597 | */ | ||
598 | switch (val->intval) { | ||
599 | case POWER_SUPPLY_CHARGE_TYPE_NONE: | ||
600 | chg_config = 0x0; | ||
601 | break; | ||
602 | case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: | ||
603 | chg_config = 0x1; | ||
604 | force_20pct = 0x1; | ||
605 | en_term = 0x0; | ||
606 | break; | ||
607 | case POWER_SUPPLY_CHARGE_TYPE_FAST: | ||
608 | chg_config = 0x1; | ||
609 | force_20pct = 0x0; | ||
610 | en_term = 0x1; | ||
611 | break; | ||
612 | default: | ||
613 | return -EINVAL; | ||
614 | } | ||
615 | |||
616 | if (chg_config) { /* Enabling the charger */ | ||
617 | ret = bq24190_write_mask(bdi, BQ24190_REG_CCC, | ||
618 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | ||
619 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, | ||
620 | force_20pct); | ||
621 | if (ret < 0) | ||
622 | return ret; | ||
623 | |||
624 | ret = bq24190_write_mask(bdi, BQ24190_REG_CTTC, | ||
625 | BQ24190_REG_CTTC_EN_TERM_MASK, | ||
626 | BQ24190_REG_CTTC_EN_TERM_SHIFT, | ||
627 | en_term); | ||
628 | if (ret < 0) | ||
629 | return ret; | ||
630 | } | ||
631 | |||
632 | return bq24190_write_mask(bdi, BQ24190_REG_POC, | ||
633 | BQ24190_REG_POC_CHG_CONFIG_MASK, | ||
634 | BQ24190_REG_POC_CHG_CONFIG_SHIFT, chg_config); | ||
635 | } | ||
636 | |||
637 | static int bq24190_charger_get_health(struct bq24190_dev_info *bdi, | ||
638 | union power_supply_propval *val) | ||
639 | { | ||
640 | u8 v; | ||
641 | int health, ret; | ||
642 | |||
643 | mutex_lock(&bdi->f_reg_lock); | ||
644 | |||
645 | if (bdi->charger_health_valid) { | ||
646 | v = bdi->f_reg; | ||
647 | bdi->charger_health_valid = false; | ||
648 | mutex_unlock(&bdi->f_reg_lock); | ||
649 | } else { | ||
650 | mutex_unlock(&bdi->f_reg_lock); | ||
651 | |||
652 | ret = bq24190_read(bdi, BQ24190_REG_F, &v); | ||
653 | if (ret < 0) | ||
654 | return ret; | ||
655 | } | ||
656 | |||
657 | if (v & BQ24190_REG_F_BOOST_FAULT_MASK) { | ||
658 | /* | ||
659 | * This could be over-current or over-voltage but there's | ||
660 | * no way to tell which. Return 'OVERVOLTAGE' since there | ||
661 | * isn't an 'OVERCURRENT' value defined that we can return | ||
662 | * even if it was over-current. | ||
663 | */ | ||
664 | health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | ||
665 | } else { | ||
666 | v &= BQ24190_REG_F_CHRG_FAULT_MASK; | ||
667 | v >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; | ||
668 | |||
669 | switch (v) { | ||
670 | case 0x0: /* Normal */ | ||
671 | health = POWER_SUPPLY_HEALTH_GOOD; | ||
672 | break; | ||
673 | case 0x1: /* Input Fault (VBUS OVP or VBAT<VBUS<3.8V) */ | ||
674 | /* | ||
675 | * This could be over-voltage or under-voltage | ||
676 | * and there's no way to tell which. Instead | ||
677 | * of looking foolish and returning 'OVERVOLTAGE' | ||
678 | * when its really under-voltage, just return | ||
679 | * 'UNSPEC_FAILURE'. | ||
680 | */ | ||
681 | health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
682 | break; | ||
683 | case 0x2: /* Thermal Shutdown */ | ||
684 | health = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
685 | break; | ||
686 | case 0x3: /* Charge Safety Timer Expiration */ | ||
687 | health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; | ||
688 | break; | ||
689 | default: | ||
690 | health = POWER_SUPPLY_HEALTH_UNKNOWN; | ||
691 | } | ||
692 | } | ||
693 | |||
694 | val->intval = health; | ||
695 | |||
696 | return 0; | ||
697 | } | ||
698 | |||
699 | static int bq24190_charger_get_online(struct bq24190_dev_info *bdi, | ||
700 | union power_supply_propval *val) | ||
701 | { | ||
702 | u8 v; | ||
703 | int ret; | ||
704 | |||
705 | ret = bq24190_read_mask(bdi, BQ24190_REG_SS, | ||
706 | BQ24190_REG_SS_PG_STAT_MASK, | ||
707 | BQ24190_REG_SS_PG_STAT_SHIFT, &v); | ||
708 | if (ret < 0) | ||
709 | return ret; | ||
710 | |||
711 | val->intval = v; | ||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | static int bq24190_charger_get_current(struct bq24190_dev_info *bdi, | ||
716 | union power_supply_propval *val) | ||
717 | { | ||
718 | u8 v; | ||
719 | int curr, ret; | ||
720 | |||
721 | ret = bq24190_get_field_val(bdi, BQ24190_REG_CCC, | ||
722 | BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, | ||
723 | bq24190_ccc_ichg_values, | ||
724 | ARRAY_SIZE(bq24190_ccc_ichg_values), &curr); | ||
725 | if (ret < 0) | ||
726 | return ret; | ||
727 | |||
728 | ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, | ||
729 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | ||
730 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v); | ||
731 | if (ret < 0) | ||
732 | return ret; | ||
733 | |||
734 | /* If FORCE_20PCT is enabled, then current is 20% of ICHG value */ | ||
735 | if (v) | ||
736 | curr /= 5; | ||
737 | |||
738 | val->intval = curr; | ||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | static int bq24190_charger_get_current_max(struct bq24190_dev_info *bdi, | ||
743 | union power_supply_propval *val) | ||
744 | { | ||
745 | int idx = ARRAY_SIZE(bq24190_ccc_ichg_values) - 1; | ||
746 | |||
747 | val->intval = bq24190_ccc_ichg_values[idx]; | ||
748 | return 0; | ||
749 | } | ||
750 | |||
751 | static int bq24190_charger_set_current(struct bq24190_dev_info *bdi, | ||
752 | const union power_supply_propval *val) | ||
753 | { | ||
754 | u8 v; | ||
755 | int ret, curr = val->intval; | ||
756 | |||
757 | ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, | ||
758 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | ||
759 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v); | ||
760 | if (ret < 0) | ||
761 | return ret; | ||
762 | |||
763 | /* If FORCE_20PCT is enabled, have to multiply value passed in by 5 */ | ||
764 | if (v) | ||
765 | curr *= 5; | ||
766 | |||
767 | return bq24190_set_field_val(bdi, BQ24190_REG_CCC, | ||
768 | BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, | ||
769 | bq24190_ccc_ichg_values, | ||
770 | ARRAY_SIZE(bq24190_ccc_ichg_values), curr); | ||
771 | } | ||
772 | |||
773 | static int bq24190_charger_get_voltage(struct bq24190_dev_info *bdi, | ||
774 | union power_supply_propval *val) | ||
775 | { | ||
776 | int voltage, ret; | ||
777 | |||
778 | ret = bq24190_get_field_val(bdi, BQ24190_REG_CVC, | ||
779 | BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT, | ||
780 | bq24190_cvc_vreg_values, | ||
781 | ARRAY_SIZE(bq24190_cvc_vreg_values), &voltage); | ||
782 | if (ret < 0) | ||
783 | return ret; | ||
784 | |||
785 | val->intval = voltage; | ||
786 | return 0; | ||
787 | } | ||
788 | |||
789 | static int bq24190_charger_get_voltage_max(struct bq24190_dev_info *bdi, | ||
790 | union power_supply_propval *val) | ||
791 | { | ||
792 | int idx = ARRAY_SIZE(bq24190_cvc_vreg_values) - 1; | ||
793 | |||
794 | val->intval = bq24190_cvc_vreg_values[idx]; | ||
795 | return 0; | ||
796 | } | ||
797 | |||
798 | static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi, | ||
799 | const union power_supply_propval *val) | ||
800 | { | ||
801 | return bq24190_set_field_val(bdi, BQ24190_REG_CVC, | ||
802 | BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT, | ||
803 | bq24190_cvc_vreg_values, | ||
804 | ARRAY_SIZE(bq24190_cvc_vreg_values), val->intval); | ||
805 | } | ||
806 | |||
807 | static int bq24190_charger_get_property(struct power_supply *psy, | ||
808 | enum power_supply_property psp, union power_supply_propval *val) | ||
809 | { | ||
810 | struct bq24190_dev_info *bdi = | ||
811 | container_of(psy, struct bq24190_dev_info, charger); | ||
812 | int ret; | ||
813 | |||
814 | dev_dbg(bdi->dev, "prop: %d\n", psp); | ||
815 | |||
816 | pm_runtime_get_sync(bdi->dev); | ||
817 | |||
818 | switch (psp) { | ||
819 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | ||
820 | ret = bq24190_charger_get_charge_type(bdi, val); | ||
821 | break; | ||
822 | case POWER_SUPPLY_PROP_HEALTH: | ||
823 | ret = bq24190_charger_get_health(bdi, val); | ||
824 | break; | ||
825 | case POWER_SUPPLY_PROP_ONLINE: | ||
826 | ret = bq24190_charger_get_online(bdi, val); | ||
827 | break; | ||
828 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | ||
829 | ret = bq24190_charger_get_current(bdi, val); | ||
830 | break; | ||
831 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: | ||
832 | ret = bq24190_charger_get_current_max(bdi, val); | ||
833 | break; | ||
834 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | ||
835 | ret = bq24190_charger_get_voltage(bdi, val); | ||
836 | break; | ||
837 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: | ||
838 | ret = bq24190_charger_get_voltage_max(bdi, val); | ||
839 | break; | ||
840 | case POWER_SUPPLY_PROP_SCOPE: | ||
841 | val->intval = POWER_SUPPLY_SCOPE_SYSTEM; | ||
842 | ret = 0; | ||
843 | break; | ||
844 | case POWER_SUPPLY_PROP_MODEL_NAME: | ||
845 | val->strval = bdi->model_name; | ||
846 | ret = 0; | ||
847 | break; | ||
848 | case POWER_SUPPLY_PROP_MANUFACTURER: | ||
849 | val->strval = BQ24190_MANUFACTURER; | ||
850 | ret = 0; | ||
851 | break; | ||
852 | default: | ||
853 | ret = -ENODATA; | ||
854 | } | ||
855 | |||
856 | pm_runtime_put_sync(bdi->dev); | ||
857 | return ret; | ||
858 | } | ||
859 | |||
860 | static int bq24190_charger_set_property(struct power_supply *psy, | ||
861 | enum power_supply_property psp, | ||
862 | const union power_supply_propval *val) | ||
863 | { | ||
864 | struct bq24190_dev_info *bdi = | ||
865 | container_of(psy, struct bq24190_dev_info, charger); | ||
866 | int ret; | ||
867 | |||
868 | dev_dbg(bdi->dev, "prop: %d\n", psp); | ||
869 | |||
870 | pm_runtime_get_sync(bdi->dev); | ||
871 | |||
872 | switch (psp) { | ||
873 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | ||
874 | ret = bq24190_charger_set_charge_type(bdi, val); | ||
875 | break; | ||
876 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | ||
877 | ret = bq24190_charger_set_current(bdi, val); | ||
878 | break; | ||
879 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | ||
880 | ret = bq24190_charger_set_voltage(bdi, val); | ||
881 | break; | ||
882 | default: | ||
883 | ret = -EINVAL; | ||
884 | } | ||
885 | |||
886 | pm_runtime_put_sync(bdi->dev); | ||
887 | return ret; | ||
888 | } | ||
889 | |||
890 | static int bq24190_charger_property_is_writeable(struct power_supply *psy, | ||
891 | enum power_supply_property psp) | ||
892 | { | ||
893 | int ret; | ||
894 | |||
895 | switch (psp) { | ||
896 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | ||
897 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | ||
898 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | ||
899 | ret = 1; | ||
900 | break; | ||
901 | default: | ||
902 | ret = 0; | ||
903 | } | ||
904 | |||
905 | return ret; | ||
906 | } | ||
907 | |||
908 | static enum power_supply_property bq24190_charger_properties[] = { | ||
909 | POWER_SUPPLY_PROP_TYPE, | ||
910 | POWER_SUPPLY_PROP_HEALTH, | ||
911 | POWER_SUPPLY_PROP_ONLINE, | ||
912 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, | ||
913 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, | ||
914 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, | ||
915 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, | ||
916 | POWER_SUPPLY_PROP_SCOPE, | ||
917 | POWER_SUPPLY_PROP_MODEL_NAME, | ||
918 | POWER_SUPPLY_PROP_MANUFACTURER, | ||
919 | }; | ||
920 | |||
921 | static char *bq24190_charger_supplied_to[] = { | ||
922 | "main-battery", | ||
923 | }; | ||
924 | |||
925 | static void bq24190_charger_init(struct power_supply *charger) | ||
926 | { | ||
927 | charger->name = "bq24190-charger"; | ||
928 | charger->type = POWER_SUPPLY_TYPE_USB; | ||
929 | charger->properties = bq24190_charger_properties; | ||
930 | charger->num_properties = ARRAY_SIZE(bq24190_charger_properties); | ||
931 | charger->supplied_to = bq24190_charger_supplied_to; | ||
932 | charger->num_supplies = ARRAY_SIZE(bq24190_charger_supplied_to); | ||
933 | charger->get_property = bq24190_charger_get_property; | ||
934 | charger->set_property = bq24190_charger_set_property; | ||
935 | charger->property_is_writeable = bq24190_charger_property_is_writeable; | ||
936 | } | ||
937 | |||
938 | /* Battery power supply property routines */ | ||
939 | |||
940 | static int bq24190_battery_get_status(struct bq24190_dev_info *bdi, | ||
941 | union power_supply_propval *val) | ||
942 | { | ||
943 | u8 ss_reg, chrg_fault; | ||
944 | int status, ret; | ||
945 | |||
946 | mutex_lock(&bdi->f_reg_lock); | ||
947 | |||
948 | if (bdi->battery_status_valid) { | ||
949 | chrg_fault = bdi->f_reg; | ||
950 | bdi->battery_status_valid = false; | ||
951 | mutex_unlock(&bdi->f_reg_lock); | ||
952 | } else { | ||
953 | mutex_unlock(&bdi->f_reg_lock); | ||
954 | |||
955 | ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault); | ||
956 | if (ret < 0) | ||
957 | return ret; | ||
958 | } | ||
959 | |||
960 | chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK; | ||
961 | chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; | ||
962 | |||
963 | ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg); | ||
964 | if (ret < 0) | ||
965 | return ret; | ||
966 | |||
967 | /* | ||
968 | * The battery must be discharging when any of these are true: | ||
969 | * - there is no good power source; | ||
970 | * - there is a charge fault. | ||
971 | * Could also be discharging when in "supplement mode" but | ||
972 | * there is no way to tell when its in that mode. | ||
973 | */ | ||
974 | if (!(ss_reg & BQ24190_REG_SS_PG_STAT_MASK) || chrg_fault) { | ||
975 | status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
976 | } else { | ||
977 | ss_reg &= BQ24190_REG_SS_CHRG_STAT_MASK; | ||
978 | ss_reg >>= BQ24190_REG_SS_CHRG_STAT_SHIFT; | ||
979 | |||
980 | switch (ss_reg) { | ||
981 | case 0x0: /* Not Charging */ | ||
982 | status = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
983 | break; | ||
984 | case 0x1: /* Pre-charge */ | ||
985 | case 0x2: /* Fast Charging */ | ||
986 | status = POWER_SUPPLY_STATUS_CHARGING; | ||
987 | break; | ||
988 | case 0x3: /* Charge Termination Done */ | ||
989 | status = POWER_SUPPLY_STATUS_FULL; | ||
990 | break; | ||
991 | default: | ||
992 | ret = -EIO; | ||
993 | } | ||
994 | } | ||
995 | |||
996 | if (!ret) | ||
997 | val->intval = status; | ||
998 | |||
999 | return ret; | ||
1000 | } | ||
1001 | |||
1002 | static int bq24190_battery_get_health(struct bq24190_dev_info *bdi, | ||
1003 | union power_supply_propval *val) | ||
1004 | { | ||
1005 | u8 v; | ||
1006 | int health, ret; | ||
1007 | |||
1008 | mutex_lock(&bdi->f_reg_lock); | ||
1009 | |||
1010 | if (bdi->battery_health_valid) { | ||
1011 | v = bdi->f_reg; | ||
1012 | bdi->battery_health_valid = false; | ||
1013 | mutex_unlock(&bdi->f_reg_lock); | ||
1014 | } else { | ||
1015 | mutex_unlock(&bdi->f_reg_lock); | ||
1016 | |||
1017 | ret = bq24190_read(bdi, BQ24190_REG_F, &v); | ||
1018 | if (ret < 0) | ||
1019 | return ret; | ||
1020 | } | ||
1021 | |||
1022 | if (v & BQ24190_REG_F_BAT_FAULT_MASK) { | ||
1023 | health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | ||
1024 | } else { | ||
1025 | v &= BQ24190_REG_F_NTC_FAULT_MASK; | ||
1026 | v >>= BQ24190_REG_F_NTC_FAULT_SHIFT; | ||
1027 | |||
1028 | switch (v) { | ||
1029 | case 0x0: /* Normal */ | ||
1030 | health = POWER_SUPPLY_HEALTH_GOOD; | ||
1031 | break; | ||
1032 | case 0x1: /* TS1 Cold */ | ||
1033 | case 0x3: /* TS2 Cold */ | ||
1034 | case 0x5: /* Both Cold */ | ||
1035 | health = POWER_SUPPLY_HEALTH_COLD; | ||
1036 | break; | ||
1037 | case 0x2: /* TS1 Hot */ | ||
1038 | case 0x4: /* TS2 Hot */ | ||
1039 | case 0x6: /* Both Hot */ | ||
1040 | health = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
1041 | break; | ||
1042 | default: | ||
1043 | health = POWER_SUPPLY_HEALTH_UNKNOWN; | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | val->intval = health; | ||
1048 | return 0; | ||
1049 | } | ||
1050 | |||
1051 | static int bq24190_battery_get_online(struct bq24190_dev_info *bdi, | ||
1052 | union power_supply_propval *val) | ||
1053 | { | ||
1054 | u8 batfet_disable; | ||
1055 | int ret; | ||
1056 | |||
1057 | ret = bq24190_read_mask(bdi, BQ24190_REG_MOC, | ||
1058 | BQ24190_REG_MOC_BATFET_DISABLE_MASK, | ||
1059 | BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, &batfet_disable); | ||
1060 | if (ret < 0) | ||
1061 | return ret; | ||
1062 | |||
1063 | val->intval = !batfet_disable; | ||
1064 | return 0; | ||
1065 | } | ||
1066 | |||
1067 | static int bq24190_battery_set_online(struct bq24190_dev_info *bdi, | ||
1068 | const union power_supply_propval *val) | ||
1069 | { | ||
1070 | return bq24190_write_mask(bdi, BQ24190_REG_MOC, | ||
1071 | BQ24190_REG_MOC_BATFET_DISABLE_MASK, | ||
1072 | BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, !val->intval); | ||
1073 | } | ||
1074 | |||
1075 | static int bq24190_battery_get_temp_alert_max(struct bq24190_dev_info *bdi, | ||
1076 | union power_supply_propval *val) | ||
1077 | { | ||
1078 | int temp, ret; | ||
1079 | |||
1080 | ret = bq24190_get_field_val(bdi, BQ24190_REG_ICTRC, | ||
1081 | BQ24190_REG_ICTRC_TREG_MASK, | ||
1082 | BQ24190_REG_ICTRC_TREG_SHIFT, | ||
1083 | bq24190_ictrc_treg_values, | ||
1084 | ARRAY_SIZE(bq24190_ictrc_treg_values), &temp); | ||
1085 | if (ret < 0) | ||
1086 | return ret; | ||
1087 | |||
1088 | val->intval = temp; | ||
1089 | return 0; | ||
1090 | } | ||
1091 | |||
1092 | static int bq24190_battery_set_temp_alert_max(struct bq24190_dev_info *bdi, | ||
1093 | const union power_supply_propval *val) | ||
1094 | { | ||
1095 | return bq24190_set_field_val(bdi, BQ24190_REG_ICTRC, | ||
1096 | BQ24190_REG_ICTRC_TREG_MASK, | ||
1097 | BQ24190_REG_ICTRC_TREG_SHIFT, | ||
1098 | bq24190_ictrc_treg_values, | ||
1099 | ARRAY_SIZE(bq24190_ictrc_treg_values), val->intval); | ||
1100 | } | ||
1101 | |||
1102 | static int bq24190_battery_get_property(struct power_supply *psy, | ||
1103 | enum power_supply_property psp, union power_supply_propval *val) | ||
1104 | { | ||
1105 | struct bq24190_dev_info *bdi = | ||
1106 | container_of(psy, struct bq24190_dev_info, battery); | ||
1107 | int ret; | ||
1108 | |||
1109 | dev_dbg(bdi->dev, "prop: %d\n", psp); | ||
1110 | |||
1111 | pm_runtime_get_sync(bdi->dev); | ||
1112 | |||
1113 | switch (psp) { | ||
1114 | case POWER_SUPPLY_PROP_STATUS: | ||
1115 | ret = bq24190_battery_get_status(bdi, val); | ||
1116 | break; | ||
1117 | case POWER_SUPPLY_PROP_HEALTH: | ||
1118 | ret = bq24190_battery_get_health(bdi, val); | ||
1119 | break; | ||
1120 | case POWER_SUPPLY_PROP_ONLINE: | ||
1121 | ret = bq24190_battery_get_online(bdi, val); | ||
1122 | break; | ||
1123 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
1124 | /* Could be Li-on or Li-polymer but no way to tell which */ | ||
1125 | val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; | ||
1126 | ret = 0; | ||
1127 | break; | ||
1128 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | ||
1129 | ret = bq24190_battery_get_temp_alert_max(bdi, val); | ||
1130 | break; | ||
1131 | case POWER_SUPPLY_PROP_SCOPE: | ||
1132 | val->intval = POWER_SUPPLY_SCOPE_SYSTEM; | ||
1133 | ret = 0; | ||
1134 | break; | ||
1135 | default: | ||
1136 | ret = -ENODATA; | ||
1137 | } | ||
1138 | |||
1139 | pm_runtime_put_sync(bdi->dev); | ||
1140 | return ret; | ||
1141 | } | ||
1142 | |||
1143 | static int bq24190_battery_set_property(struct power_supply *psy, | ||
1144 | enum power_supply_property psp, | ||
1145 | const union power_supply_propval *val) | ||
1146 | { | ||
1147 | struct bq24190_dev_info *bdi = | ||
1148 | container_of(psy, struct bq24190_dev_info, battery); | ||
1149 | int ret; | ||
1150 | |||
1151 | dev_dbg(bdi->dev, "prop: %d\n", psp); | ||
1152 | |||
1153 | pm_runtime_put_sync(bdi->dev); | ||
1154 | |||
1155 | switch (psp) { | ||
1156 | case POWER_SUPPLY_PROP_ONLINE: | ||
1157 | ret = bq24190_battery_set_online(bdi, val); | ||
1158 | break; | ||
1159 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | ||
1160 | ret = bq24190_battery_set_temp_alert_max(bdi, val); | ||
1161 | break; | ||
1162 | default: | ||
1163 | ret = -EINVAL; | ||
1164 | } | ||
1165 | |||
1166 | pm_runtime_put_sync(bdi->dev); | ||
1167 | return ret; | ||
1168 | } | ||
1169 | |||
1170 | static int bq24190_battery_property_is_writeable(struct power_supply *psy, | ||
1171 | enum power_supply_property psp) | ||
1172 | { | ||
1173 | int ret; | ||
1174 | |||
1175 | switch (psp) { | ||
1176 | case POWER_SUPPLY_PROP_ONLINE: | ||
1177 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | ||
1178 | ret = 1; | ||
1179 | break; | ||
1180 | default: | ||
1181 | ret = 0; | ||
1182 | } | ||
1183 | |||
1184 | return ret; | ||
1185 | } | ||
1186 | |||
1187 | static enum power_supply_property bq24190_battery_properties[] = { | ||
1188 | POWER_SUPPLY_PROP_STATUS, | ||
1189 | POWER_SUPPLY_PROP_HEALTH, | ||
1190 | POWER_SUPPLY_PROP_ONLINE, | ||
1191 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
1192 | POWER_SUPPLY_PROP_TEMP_ALERT_MAX, | ||
1193 | POWER_SUPPLY_PROP_SCOPE, | ||
1194 | }; | ||
1195 | |||
1196 | static void bq24190_battery_init(struct power_supply *battery) | ||
1197 | { | ||
1198 | battery->name = "bq24190-battery"; | ||
1199 | battery->type = POWER_SUPPLY_TYPE_BATTERY; | ||
1200 | battery->properties = bq24190_battery_properties; | ||
1201 | battery->num_properties = ARRAY_SIZE(bq24190_battery_properties); | ||
1202 | battery->get_property = bq24190_battery_get_property; | ||
1203 | battery->set_property = bq24190_battery_set_property; | ||
1204 | battery->property_is_writeable = bq24190_battery_property_is_writeable; | ||
1205 | } | ||
1206 | |||
1207 | static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) | ||
1208 | { | ||
1209 | struct bq24190_dev_info *bdi = data; | ||
1210 | bool alert_userspace = false; | ||
1211 | u8 ss_reg, f_reg; | ||
1212 | int ret; | ||
1213 | |||
1214 | pm_runtime_get_sync(bdi->dev); | ||
1215 | |||
1216 | ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg); | ||
1217 | if (ret < 0) { | ||
1218 | dev_err(bdi->dev, "Can't read SS reg: %d\n", ret); | ||
1219 | goto out; | ||
1220 | } | ||
1221 | |||
1222 | if (ss_reg != bdi->ss_reg) { | ||
1223 | /* | ||
1224 | * The device is in host mode so when PG_STAT goes from 1->0 | ||
1225 | * (i.e., power removed) HIZ needs to be disabled. | ||
1226 | */ | ||
1227 | if ((bdi->ss_reg & BQ24190_REG_SS_PG_STAT_MASK) && | ||
1228 | !(ss_reg & BQ24190_REG_SS_PG_STAT_MASK)) { | ||
1229 | ret = bq24190_write_mask(bdi, BQ24190_REG_ISC, | ||
1230 | BQ24190_REG_ISC_EN_HIZ_MASK, | ||
1231 | BQ24190_REG_ISC_EN_HIZ_SHIFT, | ||
1232 | 0); | ||
1233 | if (ret < 0) | ||
1234 | dev_err(bdi->dev, "Can't access ISC reg: %d\n", | ||
1235 | ret); | ||
1236 | } | ||
1237 | |||
1238 | bdi->ss_reg = ss_reg; | ||
1239 | alert_userspace = true; | ||
1240 | } | ||
1241 | |||
1242 | mutex_lock(&bdi->f_reg_lock); | ||
1243 | |||
1244 | ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg); | ||
1245 | if (ret < 0) { | ||
1246 | mutex_unlock(&bdi->f_reg_lock); | ||
1247 | dev_err(bdi->dev, "Can't read F reg: %d\n", ret); | ||
1248 | goto out; | ||
1249 | } | ||
1250 | |||
1251 | if (f_reg != bdi->f_reg) { | ||
1252 | bdi->f_reg = f_reg; | ||
1253 | bdi->charger_health_valid = true; | ||
1254 | bdi->battery_health_valid = true; | ||
1255 | bdi->battery_status_valid = true; | ||
1256 | |||
1257 | alert_userspace = true; | ||
1258 | } | ||
1259 | |||
1260 | mutex_unlock(&bdi->f_reg_lock); | ||
1261 | |||
1262 | /* | ||
1263 | * Sometimes bq24190 gives a steady trickle of interrupts even | ||
1264 | * though the watchdog timer is turned off and neither the STATUS | ||
1265 | * nor FAULT registers have changed. Weed out these sprurious | ||
1266 | * interrupts so userspace isn't alerted for no reason. | ||
1267 | * In addition, the chip always generates an interrupt after | ||
1268 | * register reset so we should ignore that one (the very first | ||
1269 | * interrupt received). | ||
1270 | */ | ||
1271 | if (alert_userspace && !bdi->first_time) { | ||
1272 | power_supply_changed(&bdi->charger); | ||
1273 | power_supply_changed(&bdi->battery); | ||
1274 | bdi->first_time = false; | ||
1275 | } | ||
1276 | |||
1277 | out: | ||
1278 | pm_runtime_put_sync(bdi->dev); | ||
1279 | |||
1280 | dev_dbg(bdi->dev, "ss_reg: 0x%02x, f_reg: 0x%02x\n", ss_reg, f_reg); | ||
1281 | |||
1282 | return IRQ_HANDLED; | ||
1283 | } | ||
1284 | |||
1285 | static int bq24190_hw_init(struct bq24190_dev_info *bdi) | ||
1286 | { | ||
1287 | u8 v; | ||
1288 | int ret; | ||
1289 | |||
1290 | pm_runtime_get_sync(bdi->dev); | ||
1291 | |||
1292 | /* First check that the device really is what its supposed to be */ | ||
1293 | ret = bq24190_read_mask(bdi, BQ24190_REG_VPRS, | ||
1294 | BQ24190_REG_VPRS_PN_MASK, | ||
1295 | BQ24190_REG_VPRS_PN_SHIFT, | ||
1296 | &v); | ||
1297 | if (ret < 0) | ||
1298 | goto out; | ||
1299 | |||
1300 | if (v != bdi->model) { | ||
1301 | ret = -ENODEV; | ||
1302 | goto out; | ||
1303 | } | ||
1304 | |||
1305 | ret = bq24190_register_reset(bdi); | ||
1306 | if (ret < 0) | ||
1307 | goto out; | ||
1308 | |||
1309 | ret = bq24190_set_mode_host(bdi); | ||
1310 | out: | ||
1311 | pm_runtime_put_sync(bdi->dev); | ||
1312 | return ret; | ||
1313 | } | ||
1314 | |||
1315 | #ifdef CONFIG_OF | ||
1316 | static int bq24190_setup_dt(struct bq24190_dev_info *bdi) | ||
1317 | { | ||
1318 | bdi->irq = irq_of_parse_and_map(bdi->dev->of_node, 0); | ||
1319 | if (bdi->irq <= 0) | ||
1320 | return -1; | ||
1321 | |||
1322 | return 0; | ||
1323 | } | ||
1324 | #else | ||
1325 | static int bq24190_setup_dt(struct bq24190_dev_info *bdi) | ||
1326 | { | ||
1327 | return -1; | ||
1328 | } | ||
1329 | #endif | ||
1330 | |||
1331 | static int bq24190_setup_pdata(struct bq24190_dev_info *bdi, | ||
1332 | struct bq24190_platform_data *pdata) | ||
1333 | { | ||
1334 | int ret; | ||
1335 | |||
1336 | if (!gpio_is_valid(pdata->gpio_int)) | ||
1337 | return -1; | ||
1338 | |||
1339 | ret = gpio_request(pdata->gpio_int, dev_name(bdi->dev)); | ||
1340 | if (ret < 0) | ||
1341 | return -1; | ||
1342 | |||
1343 | ret = gpio_direction_input(pdata->gpio_int); | ||
1344 | if (ret < 0) | ||
1345 | goto out; | ||
1346 | |||
1347 | bdi->irq = gpio_to_irq(pdata->gpio_int); | ||
1348 | if (!bdi->irq) | ||
1349 | goto out; | ||
1350 | |||
1351 | bdi->gpio_int = pdata->gpio_int; | ||
1352 | return 0; | ||
1353 | |||
1354 | out: | ||
1355 | gpio_free(pdata->gpio_int); | ||
1356 | return -1; | ||
1357 | } | ||
1358 | |||
1359 | static int bq24190_probe(struct i2c_client *client, | ||
1360 | const struct i2c_device_id *id) | ||
1361 | { | ||
1362 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | ||
1363 | struct device *dev = &client->dev; | ||
1364 | struct bq24190_platform_data *pdata = client->dev.platform_data; | ||
1365 | struct bq24190_dev_info *bdi; | ||
1366 | int ret; | ||
1367 | |||
1368 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { | ||
1369 | dev_err(dev, "No support for SMBUS_BYTE_DATA\n"); | ||
1370 | return -ENODEV; | ||
1371 | } | ||
1372 | |||
1373 | bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL); | ||
1374 | if (!bdi) { | ||
1375 | dev_err(dev, "Can't alloc bdi struct\n"); | ||
1376 | return -ENOMEM; | ||
1377 | } | ||
1378 | |||
1379 | bdi->client = client; | ||
1380 | bdi->dev = dev; | ||
1381 | bdi->model = id->driver_data; | ||
1382 | strncpy(bdi->model_name, id->name, I2C_NAME_SIZE); | ||
1383 | mutex_init(&bdi->f_reg_lock); | ||
1384 | bdi->first_time = true; | ||
1385 | bdi->charger_health_valid = false; | ||
1386 | bdi->battery_health_valid = false; | ||
1387 | bdi->battery_status_valid = false; | ||
1388 | |||
1389 | i2c_set_clientdata(client, bdi); | ||
1390 | |||
1391 | if (dev->of_node) | ||
1392 | ret = bq24190_setup_dt(bdi); | ||
1393 | else | ||
1394 | ret = bq24190_setup_pdata(bdi, pdata); | ||
1395 | |||
1396 | if (ret) { | ||
1397 | dev_err(dev, "Can't get irq info\n"); | ||
1398 | return -EINVAL; | ||
1399 | } | ||
1400 | |||
1401 | ret = devm_request_threaded_irq(dev, bdi->irq, NULL, | ||
1402 | bq24190_irq_handler_thread, | ||
1403 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
1404 | "bq24190-charger", bdi); | ||
1405 | if (ret < 0) { | ||
1406 | dev_err(dev, "Can't set up irq handler\n"); | ||
1407 | goto out1; | ||
1408 | } | ||
1409 | |||
1410 | pm_runtime_enable(dev); | ||
1411 | pm_runtime_resume(dev); | ||
1412 | |||
1413 | ret = bq24190_hw_init(bdi); | ||
1414 | if (ret < 0) { | ||
1415 | dev_err(dev, "Hardware init failed\n"); | ||
1416 | goto out2; | ||
1417 | } | ||
1418 | |||
1419 | bq24190_charger_init(&bdi->charger); | ||
1420 | |||
1421 | ret = power_supply_register(dev, &bdi->charger); | ||
1422 | if (ret) { | ||
1423 | dev_err(dev, "Can't register charger\n"); | ||
1424 | goto out2; | ||
1425 | } | ||
1426 | |||
1427 | bq24190_battery_init(&bdi->battery); | ||
1428 | |||
1429 | ret = power_supply_register(dev, &bdi->battery); | ||
1430 | if (ret) { | ||
1431 | dev_err(dev, "Can't register battery\n"); | ||
1432 | goto out3; | ||
1433 | } | ||
1434 | |||
1435 | ret = bq24190_sysfs_create_group(bdi); | ||
1436 | if (ret) { | ||
1437 | dev_err(dev, "Can't create sysfs entries\n"); | ||
1438 | goto out4; | ||
1439 | } | ||
1440 | |||
1441 | return 0; | ||
1442 | |||
1443 | out4: | ||
1444 | power_supply_unregister(&bdi->battery); | ||
1445 | out3: | ||
1446 | power_supply_unregister(&bdi->charger); | ||
1447 | out2: | ||
1448 | pm_runtime_disable(dev); | ||
1449 | out1: | ||
1450 | if (bdi->gpio_int) | ||
1451 | gpio_free(bdi->gpio_int); | ||
1452 | |||
1453 | return ret; | ||
1454 | } | ||
1455 | |||
1456 | static int bq24190_remove(struct i2c_client *client) | ||
1457 | { | ||
1458 | struct bq24190_dev_info *bdi = i2c_get_clientdata(client); | ||
1459 | |||
1460 | pm_runtime_get_sync(bdi->dev); | ||
1461 | bq24190_register_reset(bdi); | ||
1462 | pm_runtime_put_sync(bdi->dev); | ||
1463 | |||
1464 | bq24190_sysfs_remove_group(bdi); | ||
1465 | power_supply_unregister(&bdi->battery); | ||
1466 | power_supply_unregister(&bdi->charger); | ||
1467 | pm_runtime_disable(bdi->dev); | ||
1468 | |||
1469 | if (bdi->gpio_int) | ||
1470 | gpio_free(bdi->gpio_int); | ||
1471 | |||
1472 | return 0; | ||
1473 | } | ||
1474 | |||
1475 | #ifdef CONFIG_PM_SLEEP | ||
1476 | static int bq24190_pm_suspend(struct device *dev) | ||
1477 | { | ||
1478 | struct i2c_client *client = to_i2c_client(dev); | ||
1479 | struct bq24190_dev_info *bdi = i2c_get_clientdata(client); | ||
1480 | |||
1481 | pm_runtime_get_sync(bdi->dev); | ||
1482 | bq24190_register_reset(bdi); | ||
1483 | pm_runtime_put_sync(bdi->dev); | ||
1484 | |||
1485 | return 0; | ||
1486 | } | ||
1487 | |||
1488 | static int bq24190_pm_resume(struct device *dev) | ||
1489 | { | ||
1490 | struct i2c_client *client = to_i2c_client(dev); | ||
1491 | struct bq24190_dev_info *bdi = i2c_get_clientdata(client); | ||
1492 | |||
1493 | bdi->charger_health_valid = false; | ||
1494 | bdi->battery_health_valid = false; | ||
1495 | bdi->battery_status_valid = false; | ||
1496 | |||
1497 | pm_runtime_get_sync(bdi->dev); | ||
1498 | bq24190_register_reset(bdi); | ||
1499 | pm_runtime_put_sync(bdi->dev); | ||
1500 | |||
1501 | /* Things may have changed while suspended so alert upper layer */ | ||
1502 | power_supply_changed(&bdi->charger); | ||
1503 | power_supply_changed(&bdi->battery); | ||
1504 | |||
1505 | return 0; | ||
1506 | } | ||
1507 | #endif | ||
1508 | |||
1509 | static SIMPLE_DEV_PM_OPS(bq24190_pm_ops, bq24190_pm_suspend, bq24190_pm_resume); | ||
1510 | |||
1511 | /* | ||
1512 | * Only support the bq24190 right now. The bq24192, bq24192i, and bq24193 | ||
1513 | * are similar but not identical so the driver needs to be extended to | ||
1514 | * support them. | ||
1515 | */ | ||
1516 | static const struct i2c_device_id bq24190_i2c_ids[] = { | ||
1517 | { "bq24190", BQ24190_REG_VPRS_PN_24190 }, | ||
1518 | { }, | ||
1519 | }; | ||
1520 | |||
1521 | #ifdef CONFIG_OF | ||
1522 | static const struct of_device_id bq24190_of_match[] = { | ||
1523 | { .compatible = "ti,bq24190", }, | ||
1524 | { }, | ||
1525 | }; | ||
1526 | MODULE_DEVICE_TABLE(of, bq24190_of_match); | ||
1527 | #else | ||
1528 | static const struct of_device_id bq24190_of_match[] = { | ||
1529 | { }, | ||
1530 | }; | ||
1531 | #endif | ||
1532 | |||
1533 | static struct i2c_driver bq24190_driver = { | ||
1534 | .probe = bq24190_probe, | ||
1535 | .remove = bq24190_remove, | ||
1536 | .id_table = bq24190_i2c_ids, | ||
1537 | .driver = { | ||
1538 | .name = "bq24190-charger", | ||
1539 | .owner = THIS_MODULE, | ||
1540 | .pm = &bq24190_pm_ops, | ||
1541 | .of_match_table = of_match_ptr(bq24190_of_match), | ||
1542 | }, | ||
1543 | }; | ||
1544 | module_i2c_driver(bq24190_driver); | ||
1545 | |||
1546 | MODULE_LICENSE("GPL"); | ||
1547 | MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>"); | ||
1548 | MODULE_ALIAS("i2c:bq24190-charger"); | ||
1549 | MODULE_DESCRIPTION("TI BQ24190 Charger Driver"); | ||
diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c index c58d0e31bdef..d02ae02a7590 100644 --- a/drivers/power/collie_battery.c +++ b/drivers/power/collie_battery.c | |||
@@ -287,7 +287,7 @@ static struct gpio collie_batt_gpios[] = { | |||
287 | }; | 287 | }; |
288 | 288 | ||
289 | #ifdef CONFIG_PM | 289 | #ifdef CONFIG_PM |
290 | static int collie_bat_suspend(struct ucb1x00_dev *dev, pm_message_t state) | 290 | static int collie_bat_suspend(struct ucb1x00_dev *dev) |
291 | { | 291 | { |
292 | /* flush all pending status updates */ | 292 | /* flush all pending status updates */ |
293 | flush_work(&bat_work); | 293 | flush_work(&bat_work); |
diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index 0ee1e14f76e9..b4513f284bbc 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c | |||
@@ -458,6 +458,7 @@ max8925_power_dt_init(struct platform_device *pdev) | |||
458 | of_property_read_u32(np, "fast-charge", &fast_charge); | 458 | of_property_read_u32(np, "fast-charge", &fast_charge); |
459 | of_property_read_u32(np, "no-insert-detect", &no_insert_detect); | 459 | of_property_read_u32(np, "no-insert-detect", &no_insert_detect); |
460 | of_property_read_u32(np, "no-temp-support", &no_temp_support); | 460 | of_property_read_u32(np, "no-temp-support", &no_temp_support); |
461 | of_node_put(np); | ||
461 | 462 | ||
462 | pdata->batt_detect = batt_detect; | 463 | pdata->batt_detect = batt_detect; |
463 | pdata->fast_charge = fast_charge; | 464 | pdata->fast_charge = fast_charge; |
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 3b2d5df45e7a..00e667296360 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c | |||
@@ -67,23 +67,42 @@ static int __power_supply_changed_work(struct device *dev, void *data) | |||
67 | 67 | ||
68 | static void power_supply_changed_work(struct work_struct *work) | 68 | static void power_supply_changed_work(struct work_struct *work) |
69 | { | 69 | { |
70 | unsigned long flags; | ||
70 | struct power_supply *psy = container_of(work, struct power_supply, | 71 | struct power_supply *psy = container_of(work, struct power_supply, |
71 | changed_work); | 72 | changed_work); |
72 | 73 | ||
73 | dev_dbg(psy->dev, "%s\n", __func__); | 74 | dev_dbg(psy->dev, "%s\n", __func__); |
74 | 75 | ||
75 | class_for_each_device(power_supply_class, NULL, psy, | 76 | spin_lock_irqsave(&psy->changed_lock, flags); |
76 | __power_supply_changed_work); | 77 | if (psy->changed) { |
77 | 78 | psy->changed = false; | |
78 | power_supply_update_leds(psy); | 79 | spin_unlock_irqrestore(&psy->changed_lock, flags); |
79 | 80 | class_for_each_device(power_supply_class, NULL, psy, | |
80 | kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); | 81 | __power_supply_changed_work); |
82 | power_supply_update_leds(psy); | ||
83 | kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); | ||
84 | spin_lock_irqsave(&psy->changed_lock, flags); | ||
85 | } | ||
86 | /* | ||
87 | * Dependent power supplies (e.g. battery) may have changed state | ||
88 | * as a result of this event, so poll again and hold the | ||
89 | * wakeup_source until all events are processed. | ||
90 | */ | ||
91 | if (!psy->changed) | ||
92 | pm_relax(psy->dev); | ||
93 | spin_unlock_irqrestore(&psy->changed_lock, flags); | ||
81 | } | 94 | } |
82 | 95 | ||
83 | void power_supply_changed(struct power_supply *psy) | 96 | void power_supply_changed(struct power_supply *psy) |
84 | { | 97 | { |
98 | unsigned long flags; | ||
99 | |||
85 | dev_dbg(psy->dev, "%s\n", __func__); | 100 | dev_dbg(psy->dev, "%s\n", __func__); |
86 | 101 | ||
102 | spin_lock_irqsave(&psy->changed_lock, flags); | ||
103 | psy->changed = true; | ||
104 | pm_stay_awake(psy->dev); | ||
105 | spin_unlock_irqrestore(&psy->changed_lock, flags); | ||
87 | schedule_work(&psy->changed_work); | 106 | schedule_work(&psy->changed_work); |
88 | } | 107 | } |
89 | EXPORT_SYMBOL_GPL(power_supply_changed); | 108 | EXPORT_SYMBOL_GPL(power_supply_changed); |
@@ -500,6 +519,11 @@ int power_supply_register(struct device *parent, struct power_supply *psy) | |||
500 | goto check_supplies_failed; | 519 | goto check_supplies_failed; |
501 | } | 520 | } |
502 | 521 | ||
522 | spin_lock_init(&psy->changed_lock); | ||
523 | rc = device_init_wakeup(dev, true); | ||
524 | if (rc) | ||
525 | goto wakeup_init_failed; | ||
526 | |||
503 | rc = kobject_set_name(&dev->kobj, "%s", psy->name); | 527 | rc = kobject_set_name(&dev->kobj, "%s", psy->name); |
504 | if (rc) | 528 | if (rc) |
505 | goto kobject_set_name_failed; | 529 | goto kobject_set_name_failed; |
@@ -529,6 +553,7 @@ create_triggers_failed: | |||
529 | register_cooler_failed: | 553 | register_cooler_failed: |
530 | psy_unregister_thermal(psy); | 554 | psy_unregister_thermal(psy); |
531 | register_thermal_failed: | 555 | register_thermal_failed: |
556 | wakeup_init_failed: | ||
532 | device_del(dev); | 557 | device_del(dev); |
533 | kobject_set_name_failed: | 558 | kobject_set_name_failed: |
534 | device_add_failed: | 559 | device_add_failed: |
@@ -546,6 +571,7 @@ void power_supply_unregister(struct power_supply *psy) | |||
546 | power_supply_remove_triggers(psy); | 571 | power_supply_remove_triggers(psy); |
547 | psy_unregister_cooler(psy); | 572 | psy_unregister_cooler(psy); |
548 | psy_unregister_thermal(psy); | 573 | psy_unregister_thermal(psy); |
574 | device_init_wakeup(psy->dev, false); | ||
549 | device_unregister(psy->dev); | 575 | device_unregister(psy->dev); |
550 | } | 576 | } |
551 | EXPORT_SYMBOL_GPL(power_supply_unregister); | 577 | EXPORT_SYMBOL_GPL(power_supply_unregister); |
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 29178f78d73c..44420d1e9094 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c | |||
@@ -118,7 +118,7 @@ static ssize_t power_supply_store_property(struct device *dev, | |||
118 | long long_val; | 118 | long long_val; |
119 | 119 | ||
120 | /* TODO: support other types than int */ | 120 | /* TODO: support other types than int */ |
121 | ret = strict_strtol(buf, 10, &long_val); | 121 | ret = kstrtol(buf, 10, &long_val); |
122 | if (ret < 0) | 122 | if (ret < 0) |
123 | return ret; | 123 | return ret; |
124 | 124 | ||
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index ee039dcead04..9b3ea535b472 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig | |||
@@ -14,6 +14,12 @@ config POWER_RESET_GPIO | |||
14 | If your board needs a GPIO high/low to power down, say Y and | 14 | If your board needs a GPIO high/low to power down, say Y and |
15 | create a binding in your devicetree. | 15 | create a binding in your devicetree. |
16 | 16 | ||
17 | config POWER_RESET_MSM | ||
18 | bool "Qualcomm MSM power-off driver" | ||
19 | depends on POWER_RESET && ARCH_MSM | ||
20 | help | ||
21 | Power off and restart support for Qualcomm boards. | ||
22 | |||
17 | config POWER_RESET_QNAP | 23 | config POWER_RESET_QNAP |
18 | bool "QNAP power-off driver" | 24 | bool "QNAP power-off driver" |
19 | depends on OF_GPIO && POWER_RESET && PLAT_ORION | 25 | depends on OF_GPIO && POWER_RESET && PLAT_ORION |
@@ -34,7 +40,14 @@ config POWER_RESET_RESTART | |||
34 | config POWER_RESET_VEXPRESS | 40 | config POWER_RESET_VEXPRESS |
35 | bool "ARM Versatile Express power-off and reset driver" | 41 | bool "ARM Versatile Express power-off and reset driver" |
36 | depends on ARM || ARM64 | 42 | depends on ARM || ARM64 |
37 | depends on POWER_RESET | 43 | depends on POWER_RESET && VEXPRESS_CONFIG |
38 | help | 44 | help |
39 | Power off and reset support for the ARM Ltd. Versatile | 45 | Power off and reset support for the ARM Ltd. Versatile |
40 | Express boards. | 46 | Express boards. |
47 | |||
48 | config POWER_RESET_XGENE | ||
49 | bool "APM SoC X-Gene reset driver" | ||
50 | depends on ARM64 | ||
51 | depends on POWER_RESET | ||
52 | help | ||
53 | Reboot support for the APM SoC X-Gene Eval boards. | ||
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 372807fd83f7..3e6ed88725ac 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile | |||
@@ -1,4 +1,6 @@ | |||
1 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o | 1 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o |
2 | obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o | ||
2 | obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o | 3 | obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o |
3 | obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o | 4 | obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o |
4 | obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o | 5 | obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o |
6 | obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o | ||
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c new file mode 100644 index 000000000000..774f9a3b310d --- /dev/null +++ b/drivers/power/reset/msm-poweroff.c | |||
@@ -0,0 +1,73 @@ | |||
1 | /* Copyright (c) 2013, The Linux Foundation. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 and | ||
5 | * only version 2 as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/delay.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/reboot.h> | ||
23 | |||
24 | #include <asm/system_misc.h> | ||
25 | |||
26 | static void __iomem *msm_ps_hold; | ||
27 | |||
28 | static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd) | ||
29 | { | ||
30 | writel(0, msm_ps_hold); | ||
31 | mdelay(10000); | ||
32 | } | ||
33 | |||
34 | static void do_msm_poweroff(void) | ||
35 | { | ||
36 | /* TODO: Add poweroff capability */ | ||
37 | do_msm_restart(REBOOT_HARD, NULL); | ||
38 | } | ||
39 | |||
40 | static int msm_restart_probe(struct platform_device *pdev) | ||
41 | { | ||
42 | struct device *dev = &pdev->dev; | ||
43 | struct resource *mem; | ||
44 | |||
45 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
46 | msm_ps_hold = devm_ioremap_resource(dev, mem); | ||
47 | if (IS_ERR(msm_ps_hold)) | ||
48 | return PTR_ERR(msm_ps_hold); | ||
49 | |||
50 | pm_power_off = do_msm_poweroff; | ||
51 | arm_pm_restart = do_msm_restart; | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static const struct of_device_id of_msm_restart_match[] = { | ||
56 | { .compatible = "qcom,pshold", }, | ||
57 | {}, | ||
58 | }; | ||
59 | MODULE_DEVICE_TABLE(of, of_msm_restart_match); | ||
60 | |||
61 | static struct platform_driver msm_restart_driver = { | ||
62 | .probe = msm_restart_probe, | ||
63 | .driver = { | ||
64 | .name = "msm-restart", | ||
65 | .of_match_table = of_match_ptr(of_msm_restart_match), | ||
66 | }, | ||
67 | }; | ||
68 | |||
69 | static int __init msm_restart_init(void) | ||
70 | { | ||
71 | return platform_driver_register(&msm_restart_driver); | ||
72 | } | ||
73 | device_initcall(msm_restart_init); | ||
diff --git a/drivers/power/reset/xgene-reboot.c b/drivers/power/reset/xgene-reboot.c new file mode 100644 index 000000000000..ecd55f81b9d1 --- /dev/null +++ b/drivers/power/reset/xgene-reboot.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * AppliedMicro X-Gene SoC Reboot Driver | ||
3 | * | ||
4 | * Copyright (c) 2013, Applied Micro Circuits Corporation | ||
5 | * Author: Feng Kan <fkan@apm.com> | ||
6 | * Author: Loc Ho <lho@apm.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation; either version 2 of | ||
11 | * the License, or (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
21 | * MA 02111-1307 USA | ||
22 | * | ||
23 | * This driver provides system reboot functionality for APM X-Gene SoC. | ||
24 | * For system shutdown, this is board specify. If a board designer | ||
25 | * implements GPIO shutdown, use the gpio-poweroff.c driver. | ||
26 | */ | ||
27 | #include <linux/io.h> | ||
28 | #include <linux/of_device.h> | ||
29 | #include <linux/of_address.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/stat.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <asm/system_misc.h> | ||
34 | |||
35 | struct xgene_reboot_context { | ||
36 | struct platform_device *pdev; | ||
37 | void *csr; | ||
38 | u32 mask; | ||
39 | }; | ||
40 | |||
41 | static struct xgene_reboot_context *xgene_restart_ctx; | ||
42 | |||
43 | static void xgene_restart(char str, const char *cmd) | ||
44 | { | ||
45 | struct xgene_reboot_context *ctx = xgene_restart_ctx; | ||
46 | unsigned long timeout; | ||
47 | |||
48 | /* Issue the reboot */ | ||
49 | if (ctx) | ||
50 | writel(ctx->mask, ctx->csr); | ||
51 | |||
52 | timeout = jiffies + HZ; | ||
53 | while (time_before(jiffies, timeout)) | ||
54 | cpu_relax(); | ||
55 | |||
56 | dev_emerg(&ctx->pdev->dev, "Unable to restart system\n"); | ||
57 | } | ||
58 | |||
59 | static int xgene_reboot_probe(struct platform_device *pdev) | ||
60 | { | ||
61 | struct xgene_reboot_context *ctx; | ||
62 | |||
63 | ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); | ||
64 | if (!ctx) { | ||
65 | dev_err(&pdev->dev, "out of memory for context\n"); | ||
66 | return -ENODEV; | ||
67 | } | ||
68 | |||
69 | ctx->csr = of_iomap(pdev->dev.of_node, 0); | ||
70 | if (!ctx->csr) { | ||
71 | devm_kfree(&pdev->dev, ctx); | ||
72 | dev_err(&pdev->dev, "can not map resource\n"); | ||
73 | return -ENODEV; | ||
74 | } | ||
75 | |||
76 | if (of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask)) | ||
77 | ctx->mask = 0xFFFFFFFF; | ||
78 | |||
79 | ctx->pdev = pdev; | ||
80 | arm_pm_restart = xgene_restart; | ||
81 | xgene_restart_ctx = ctx; | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static struct of_device_id xgene_reboot_of_match[] = { | ||
87 | { .compatible = "apm,xgene-reboot" }, | ||
88 | {} | ||
89 | }; | ||
90 | |||
91 | static struct platform_driver xgene_reboot_driver = { | ||
92 | .probe = xgene_reboot_probe, | ||
93 | .driver = { | ||
94 | .name = "xgene-reboot", | ||
95 | .of_match_table = xgene_reboot_of_match, | ||
96 | }, | ||
97 | }; | ||
98 | |||
99 | static int __init xgene_reboot_init(void) | ||
100 | { | ||
101 | return platform_driver_register(&xgene_reboot_driver); | ||
102 | } | ||
103 | device_initcall(xgene_reboot_init); | ||
diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c index 8a6288d87056..1bc5857b8bd5 100644 --- a/drivers/power/rx51_battery.c +++ b/drivers/power/rx51_battery.c | |||
@@ -25,6 +25,10 @@ | |||
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/i2c/twl4030-madc.h> | 26 | #include <linux/i2c/twl4030-madc.h> |
27 | 27 | ||
28 | /* RX51 specific channels */ | ||
29 | #define TWL4030_MADC_BTEMP_RX51 TWL4030_MADC_ADCIN0 | ||
30 | #define TWL4030_MADC_BCI_RX51 TWL4030_MADC_ADCIN4 | ||
31 | |||
28 | struct rx51_device_info { | 32 | struct rx51_device_info { |
29 | struct device *dev; | 33 | struct device *dev; |
30 | struct power_supply bat; | 34 | struct power_supply bat; |
@@ -37,7 +41,7 @@ static int rx51_battery_read_adc(int channel) | |||
37 | { | 41 | { |
38 | struct twl4030_madc_request req; | 42 | struct twl4030_madc_request req; |
39 | 43 | ||
40 | req.channels = 1 << channel; | 44 | req.channels = channel; |
41 | req.do_avg = 1; | 45 | req.do_avg = 1; |
42 | req.method = TWL4030_MADC_SW1; | 46 | req.method = TWL4030_MADC_SW1; |
43 | req.func_cb = NULL; | 47 | req.func_cb = NULL; |
@@ -47,7 +51,7 @@ static int rx51_battery_read_adc(int channel) | |||
47 | if (twl4030_madc_conversion(&req) <= 0) | 51 | if (twl4030_madc_conversion(&req) <= 0) |
48 | return -ENODATA; | 52 | return -ENODATA; |
49 | 53 | ||
50 | return req.rbuf[channel]; | 54 | return req.rbuf[ffs(channel) - 1]; |
51 | } | 55 | } |
52 | 56 | ||
53 | /* | 57 | /* |
@@ -56,7 +60,7 @@ static int rx51_battery_read_adc(int channel) | |||
56 | */ | 60 | */ |
57 | static int rx51_battery_read_voltage(struct rx51_device_info *di) | 61 | static int rx51_battery_read_voltage(struct rx51_device_info *di) |
58 | { | 62 | { |
59 | int voltage = rx51_battery_read_adc(12); | 63 | int voltage = rx51_battery_read_adc(TWL4030_MADC_VBAT); |
60 | 64 | ||
61 | if (voltage < 0) | 65 | if (voltage < 0) |
62 | return voltage; | 66 | return voltage; |
@@ -108,7 +112,7 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di) | |||
108 | { | 112 | { |
109 | int min = 0; | 113 | int min = 0; |
110 | int max = ARRAY_SIZE(rx51_temp_table2) - 1; | 114 | int max = ARRAY_SIZE(rx51_temp_table2) - 1; |
111 | int raw = rx51_battery_read_adc(0); | 115 | int raw = rx51_battery_read_adc(TWL4030_MADC_BTEMP_RX51); |
112 | 116 | ||
113 | /* Zero and negative values are undefined */ | 117 | /* Zero and negative values are undefined */ |
114 | if (raw <= 0) | 118 | if (raw <= 0) |
@@ -142,7 +146,7 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di) | |||
142 | */ | 146 | */ |
143 | static int rx51_battery_read_capacity(struct rx51_device_info *di) | 147 | static int rx51_battery_read_capacity(struct rx51_device_info *di) |
144 | { | 148 | { |
145 | int capacity = rx51_battery_read_adc(4); | 149 | int capacity = rx51_battery_read_adc(TWL4030_MADC_BCI_RX51); |
146 | 150 | ||
147 | if (capacity < 0) | 151 | if (capacity < 0) |
148 | return capacity; | 152 | return capacity; |
diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c index 0224de50c540..f4d80df627c7 100644 --- a/drivers/power/tosa_battery.c +++ b/drivers/power/tosa_battery.c | |||
@@ -150,7 +150,7 @@ static void tosa_bat_external_power_changed(struct power_supply *psy) | |||
150 | 150 | ||
151 | static irqreturn_t tosa_bat_gpio_isr(int irq, void *data) | 151 | static irqreturn_t tosa_bat_gpio_isr(int irq, void *data) |
152 | { | 152 | { |
153 | pr_info("tosa_bat_gpio irq: %d\n", gpio_get_value(irq_to_gpio(irq))); | 153 | pr_info("tosa_bat_gpio irq\n"); |
154 | schedule_work(&bat_work); | 154 | schedule_work(&bat_work); |
155 | return IRQ_HANDLED; | 155 | return IRQ_HANDLED; |
156 | } | 156 | } |
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index be98e70380f9..d98abe911e37 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c | |||
@@ -189,7 +189,12 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) | |||
189 | 189 | ||
190 | /* Need to keep regulator on */ | 190 | /* Need to keep regulator on */ |
191 | if (!bci->usb_enabled) { | 191 | if (!bci->usb_enabled) { |
192 | regulator_enable(bci->usb_reg); | 192 | ret = regulator_enable(bci->usb_reg); |
193 | if (ret) { | ||
194 | dev_err(bci->dev, | ||
195 | "Failed to enable regulator\n"); | ||
196 | return ret; | ||
197 | } | ||
193 | bci->usb_enabled = 1; | 198 | bci->usb_enabled = 1; |
194 | } | 199 | } |
195 | 200 | ||
diff --git a/drivers/power/twl4030_madc_battery.c b/drivers/power/twl4030_madc_battery.c new file mode 100644 index 000000000000..7ef445a6cfa6 --- /dev/null +++ b/drivers/power/twl4030_madc_battery.c | |||
@@ -0,0 +1,245 @@ | |||
1 | /* | ||
2 | * Dumb driver for LiIon batteries using TWL4030 madc. | ||
3 | * | ||
4 | * Copyright 2013 Golden Delicious Computers | ||
5 | * Lukas Märdian <lukas@goldelico.com> | ||
6 | * | ||
7 | * Based on dumb driver for gta01 battery | ||
8 | * Copyright 2009 Openmoko, Inc | ||
9 | * Balaji Rao <balajirrao@openmoko.org> | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/param.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/workqueue.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/power_supply.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/sort.h> | ||
20 | #include <linux/i2c/twl4030-madc.h> | ||
21 | #include <linux/power/twl4030_madc_battery.h> | ||
22 | |||
23 | struct twl4030_madc_battery { | ||
24 | struct power_supply psy; | ||
25 | struct twl4030_madc_bat_platform_data *pdata; | ||
26 | }; | ||
27 | |||
28 | static enum power_supply_property twl4030_madc_bat_props[] = { | ||
29 | POWER_SUPPLY_PROP_PRESENT, | ||
30 | POWER_SUPPLY_PROP_STATUS, | ||
31 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
32 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
33 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
34 | POWER_SUPPLY_PROP_CAPACITY, | ||
35 | POWER_SUPPLY_PROP_CHARGE_FULL, | ||
36 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
37 | POWER_SUPPLY_PROP_TEMP, | ||
38 | POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, | ||
39 | }; | ||
40 | |||
41 | static int madc_read(int index) | ||
42 | { | ||
43 | struct twl4030_madc_request req; | ||
44 | int val; | ||
45 | |||
46 | req.channels = index; | ||
47 | req.method = TWL4030_MADC_SW2; | ||
48 | req.type = TWL4030_MADC_WAIT; | ||
49 | req.do_avg = 0; | ||
50 | req.raw = false; | ||
51 | req.func_cb = NULL; | ||
52 | |||
53 | val = twl4030_madc_conversion(&req); | ||
54 | if (val < 0) | ||
55 | return val; | ||
56 | |||
57 | return req.rbuf[ffs(index) - 1]; | ||
58 | } | ||
59 | |||
60 | static int twl4030_madc_bat_get_charging_status(void) | ||
61 | { | ||
62 | return (madc_read(TWL4030_MADC_ICHG) > 0) ? 1 : 0; | ||
63 | } | ||
64 | |||
65 | static int twl4030_madc_bat_get_voltage(void) | ||
66 | { | ||
67 | return madc_read(TWL4030_MADC_VBAT); | ||
68 | } | ||
69 | |||
70 | static int twl4030_madc_bat_get_current(void) | ||
71 | { | ||
72 | return madc_read(TWL4030_MADC_ICHG) * 1000; | ||
73 | } | ||
74 | |||
75 | static int twl4030_madc_bat_get_temp(void) | ||
76 | { | ||
77 | return madc_read(TWL4030_MADC_BTEMP) * 10; | ||
78 | } | ||
79 | |||
80 | static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat, | ||
81 | int volt) | ||
82 | { | ||
83 | struct twl4030_madc_bat_calibration *calibration; | ||
84 | int i, res = 0; | ||
85 | |||
86 | /* choose charging curve */ | ||
87 | if (twl4030_madc_bat_get_charging_status()) | ||
88 | calibration = bat->pdata->charging; | ||
89 | else | ||
90 | calibration = bat->pdata->discharging; | ||
91 | |||
92 | if (volt > calibration[0].voltage) { | ||
93 | res = calibration[0].level; | ||
94 | } else { | ||
95 | for (i = 0; calibration[i+1].voltage >= 0; i++) { | ||
96 | if (volt <= calibration[i].voltage && | ||
97 | volt >= calibration[i+1].voltage) { | ||
98 | /* interval found - interpolate within range */ | ||
99 | res = calibration[i].level - | ||
100 | ((calibration[i].voltage - volt) * | ||
101 | (calibration[i].level - | ||
102 | calibration[i+1].level)) / | ||
103 | (calibration[i].voltage - | ||
104 | calibration[i+1].voltage); | ||
105 | break; | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | return res; | ||
110 | } | ||
111 | |||
112 | static int twl4030_madc_bat_get_property(struct power_supply *psy, | ||
113 | enum power_supply_property psp, | ||
114 | union power_supply_propval *val) | ||
115 | { | ||
116 | struct twl4030_madc_battery *bat = container_of(psy, | ||
117 | struct twl4030_madc_battery, psy); | ||
118 | |||
119 | switch (psp) { | ||
120 | case POWER_SUPPLY_PROP_STATUS: | ||
121 | if (twl4030_madc_bat_voltscale(bat, | ||
122 | twl4030_madc_bat_get_voltage()) > 95) | ||
123 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
124 | else { | ||
125 | if (twl4030_madc_bat_get_charging_status()) | ||
126 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
127 | else | ||
128 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
129 | } | ||
130 | break; | ||
131 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
132 | val->intval = twl4030_madc_bat_get_voltage() * 1000; | ||
133 | break; | ||
134 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
135 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | ||
136 | break; | ||
137 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
138 | val->intval = twl4030_madc_bat_get_current(); | ||
139 | break; | ||
140 | case POWER_SUPPLY_PROP_PRESENT: | ||
141 | /* assume battery is always present */ | ||
142 | val->intval = 1; | ||
143 | break; | ||
144 | case POWER_SUPPLY_PROP_CHARGE_NOW: { | ||
145 | int percent = twl4030_madc_bat_voltscale(bat, | ||
146 | twl4030_madc_bat_get_voltage()); | ||
147 | val->intval = (percent * bat->pdata->capacity) / 100; | ||
148 | break; | ||
149 | } | ||
150 | case POWER_SUPPLY_PROP_CAPACITY: | ||
151 | val->intval = twl4030_madc_bat_voltscale(bat, | ||
152 | twl4030_madc_bat_get_voltage()); | ||
153 | break; | ||
154 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
155 | val->intval = bat->pdata->capacity; | ||
156 | break; | ||
157 | case POWER_SUPPLY_PROP_TEMP: | ||
158 | val->intval = twl4030_madc_bat_get_temp(); | ||
159 | break; | ||
160 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: { | ||
161 | int percent = twl4030_madc_bat_voltscale(bat, | ||
162 | twl4030_madc_bat_get_voltage()); | ||
163 | /* in mAh */ | ||
164 | int chg = (percent * (bat->pdata->capacity/1000))/100; | ||
165 | |||
166 | /* assume discharge with 400 mA (ca. 1.5W) */ | ||
167 | val->intval = (3600l * chg) / 400; | ||
168 | break; | ||
169 | } | ||
170 | default: | ||
171 | return -EINVAL; | ||
172 | } | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static void twl4030_madc_bat_ext_changed(struct power_supply *psy) | ||
178 | { | ||
179 | struct twl4030_madc_battery *bat = container_of(psy, | ||
180 | struct twl4030_madc_battery, psy); | ||
181 | |||
182 | power_supply_changed(&bat->psy); | ||
183 | } | ||
184 | |||
185 | static int twl4030_cmp(const void *a, const void *b) | ||
186 | { | ||
187 | return ((struct twl4030_madc_bat_calibration *)b)->voltage - | ||
188 | ((struct twl4030_madc_bat_calibration *)a)->voltage; | ||
189 | } | ||
190 | |||
191 | static int twl4030_madc_battery_probe(struct platform_device *pdev) | ||
192 | { | ||
193 | struct twl4030_madc_battery *twl4030_madc_bat; | ||
194 | struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data; | ||
195 | |||
196 | twl4030_madc_bat = kzalloc(sizeof(*twl4030_madc_bat), GFP_KERNEL); | ||
197 | if (!twl4030_madc_bat) | ||
198 | return -ENOMEM; | ||
199 | |||
200 | twl4030_madc_bat->psy.name = "twl4030_battery"; | ||
201 | twl4030_madc_bat->psy.type = POWER_SUPPLY_TYPE_BATTERY; | ||
202 | twl4030_madc_bat->psy.properties = twl4030_madc_bat_props; | ||
203 | twl4030_madc_bat->psy.num_properties = | ||
204 | ARRAY_SIZE(twl4030_madc_bat_props); | ||
205 | twl4030_madc_bat->psy.get_property = twl4030_madc_bat_get_property; | ||
206 | twl4030_madc_bat->psy.external_power_changed = | ||
207 | twl4030_madc_bat_ext_changed; | ||
208 | |||
209 | /* sort charging and discharging calibration data */ | ||
210 | sort(pdata->charging, pdata->charging_size, | ||
211 | sizeof(struct twl4030_madc_bat_calibration), | ||
212 | twl4030_cmp, NULL); | ||
213 | sort(pdata->discharging, pdata->discharging_size, | ||
214 | sizeof(struct twl4030_madc_bat_calibration), | ||
215 | twl4030_cmp, NULL); | ||
216 | |||
217 | twl4030_madc_bat->pdata = pdata; | ||
218 | platform_set_drvdata(pdev, twl4030_madc_bat); | ||
219 | power_supply_register(&pdev->dev, &twl4030_madc_bat->psy); | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int twl4030_madc_battery_remove(struct platform_device *pdev) | ||
225 | { | ||
226 | struct twl4030_madc_battery *bat = platform_get_drvdata(pdev); | ||
227 | |||
228 | power_supply_unregister(&bat->psy); | ||
229 | kfree(bat); | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | static struct platform_driver twl4030_madc_battery_driver = { | ||
235 | .driver = { | ||
236 | .name = "twl4030_madc_battery", | ||
237 | }, | ||
238 | .probe = twl4030_madc_battery_probe, | ||
239 | .remove = twl4030_madc_battery_remove, | ||
240 | }; | ||
241 | module_platform_driver(twl4030_madc_battery_driver); | ||
242 | |||
243 | MODULE_LICENSE("GPL"); | ||
244 | MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>"); | ||
245 | MODULE_DESCRIPTION("twl4030_madc battery driver"); | ||