diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-14 12:25:59 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-14 12:25:59 -0500 |
commit | 5957e33d6aec266659a71cfabcf7cf2c593ad0d2 (patch) | |
tree | 6397dec8d71e4ed4f5982792c57d7c0ddb525d1e /drivers/power | |
parent | 0ad53eeefcbb2620b6a71ffdaad4add20b450b8b (diff) | |
parent | 359ab9f5b154cbd807a11e22792235f0f36b0cd5 (diff) |
Merge git://git.infradead.org/battery-2.6
* git://git.infradead.org/battery-2.6: (21 commits)
power_supply: Add MAX17042 Fuel Gauge Driver
olpc_battery: Fix up XO-1.5 properties list
olpc_battery: Add support for CURRENT_NOW and VOLTAGE_NOW
olpc_battery: Add support for CHARGE_NOW
olpc_battery: Add support for CHARGE_FULL_DESIGN
olpc_battery: Ambient temperature is not available on XO-1.5
jz4740-battery: Should include linux/io.h
s3c_adc_battery: Add gpio_inverted field to pdata
power_supply: Don't use flush_scheduled_work()
power_supply: Fix use after free and memory leak
gpio-charger: Fix potential race between irq handler and probe/remove
gpio-charger: Provide default name for the power_supply
gpio-charger: Check result of kzalloc
jz4740-battery: Check if platform_data is supplied
isp1704_charger: Detect charger after probe
isp1704_charger: Set isp->dev before anything needs it
isp1704_charger: Detect HUB/Host chargers
isp1704_charger: Correct length for storing model
power_supply: Add gpio charger driver
jz4740-battery: Protect against concurrent battery readings
...
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 20 | ||||
-rw-r--r-- | drivers/power/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/collie_battery.c | 13 | ||||
-rw-r--r-- | drivers/power/ds2760_battery.c | 2 | ||||
-rw-r--r-- | drivers/power/gpio-charger.c | 188 | ||||
-rw-r--r-- | drivers/power/intel_mid_battery.c | 2 | ||||
-rw-r--r-- | drivers/power/isp1704_charger.c | 201 | ||||
-rw-r--r-- | drivers/power/jz4740-battery.c | 13 | ||||
-rw-r--r-- | drivers/power/max17042_battery.c | 239 | ||||
-rw-r--r-- | drivers/power/olpc_battery.c | 114 | ||||
-rw-r--r-- | drivers/power/power_supply_core.c | 6 | ||||
-rw-r--r-- | drivers/power/s3c_adc_battery.c | 12 | ||||
-rw-r--r-- | drivers/power/tosa_battery.c | 13 | ||||
-rw-r--r-- | drivers/power/wm97xx_battery.c | 4 | ||||
-rw-r--r-- | drivers/power/z2_battery.c | 6 |
15 files changed, 766 insertions, 69 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 60d83d983a36..61bf5d724139 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -136,6 +136,16 @@ config BATTERY_MAX17040 | |||
136 | in handheld and portable equipment. The MAX17040 is configured | 136 | in handheld and portable equipment. The MAX17040 is configured |
137 | to operate with a single lithium cell | 137 | to operate with a single lithium cell |
138 | 138 | ||
139 | config BATTERY_MAX17042 | ||
140 | tristate "Maxim MAX17042/8997/8966 Fuel Gauge" | ||
141 | depends on I2C | ||
142 | help | ||
143 | MAX17042 is fuel-gauge systems for lithium-ion (Li+) batteries | ||
144 | in handheld and portable equipment. The MAX17042 is configured | ||
145 | to operate with a single lithium cell. MAX8997 and MAX8966 are | ||
146 | multi-function devices that include fuel gauages that are compatible | ||
147 | with MAX17042. | ||
148 | |||
139 | config BATTERY_Z2 | 149 | config BATTERY_Z2 |
140 | tristate "Z2 battery driver" | 150 | tristate "Z2 battery driver" |
141 | depends on I2C && MACH_ZIPIT2 | 151 | depends on I2C && MACH_ZIPIT2 |
@@ -185,4 +195,14 @@ config CHARGER_TWL4030 | |||
185 | help | 195 | help |
186 | Say Y here to enable support for TWL4030 Battery Charge Interface. | 196 | Say Y here to enable support for TWL4030 Battery Charge Interface. |
187 | 197 | ||
198 | config CHARGER_GPIO | ||
199 | tristate "GPIO charger" | ||
200 | depends on GPIOLIB | ||
201 | help | ||
202 | Say Y to include support for chargers which report their online status | ||
203 | through a GPIO pin. | ||
204 | |||
205 | This driver can be build as a module. If so, the module will be | ||
206 | called gpio-charger. | ||
207 | |||
188 | endif # POWER_SUPPLY | 208 | endif # POWER_SUPPLY |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index c75772eb157c..8385bfae8728 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -25,6 +25,7 @@ obj-$(CONFIG_BATTERY_BQ20Z75) += bq20z75.o | |||
25 | obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o | 25 | obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o |
26 | obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o | 26 | obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o |
27 | obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o | 27 | obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o |
28 | obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o | ||
28 | obj-$(CONFIG_BATTERY_Z2) += z2_battery.o | 29 | obj-$(CONFIG_BATTERY_Z2) += z2_battery.o |
29 | obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o | 30 | obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o |
30 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o | 31 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o |
@@ -32,3 +33,4 @@ obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o | |||
32 | obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o | 33 | obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o |
33 | obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o | 34 | obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o |
34 | obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o | 35 | obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o |
36 | obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o | ||
diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c index 039f41ae217d..548d263b1ad0 100644 --- a/drivers/power/collie_battery.c +++ b/drivers/power/collie_battery.c | |||
@@ -295,7 +295,7 @@ static struct { | |||
295 | static int collie_bat_suspend(struct ucb1x00_dev *dev, pm_message_t state) | 295 | static int collie_bat_suspend(struct ucb1x00_dev *dev, pm_message_t state) |
296 | { | 296 | { |
297 | /* flush all pending status updates */ | 297 | /* flush all pending status updates */ |
298 | flush_scheduled_work(); | 298 | flush_work_sync(&bat_work); |
299 | return 0; | 299 | return 0; |
300 | } | 300 | } |
301 | 301 | ||
@@ -362,7 +362,7 @@ err_psy_reg_bu: | |||
362 | err_psy_reg_main: | 362 | err_psy_reg_main: |
363 | 363 | ||
364 | /* see comment in collie_bat_remove */ | 364 | /* see comment in collie_bat_remove */ |
365 | flush_scheduled_work(); | 365 | cancel_work_sync(&bat_work); |
366 | 366 | ||
367 | i--; | 367 | i--; |
368 | err_gpio: | 368 | err_gpio: |
@@ -382,12 +382,11 @@ static void __devexit collie_bat_remove(struct ucb1x00_dev *dev) | |||
382 | power_supply_unregister(&collie_bat_main.psy); | 382 | power_supply_unregister(&collie_bat_main.psy); |
383 | 383 | ||
384 | /* | 384 | /* |
385 | * now flush all pending work. | 385 | * Now cancel the bat_work. We won't get any more schedules, |
386 | * we won't get any more schedules, since all | 386 | * since all sources (isr and external_power_changed) are |
387 | * sources (isr and external_power_changed) | 387 | * unregistered now. |
388 | * are unregistered now. | ||
389 | */ | 388 | */ |
390 | flush_scheduled_work(); | 389 | cancel_work_sync(&bat_work); |
391 | 390 | ||
392 | for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--) | 391 | for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--) |
393 | gpio_free(gpios[i].gpio); | 392 | gpio_free(gpios[i].gpio); |
diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index e7f89785beef..e534290f3256 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c | |||
@@ -212,7 +212,7 @@ static int ds2760_battery_read_status(struct ds2760_device_info *di) | |||
212 | if (di->rem_capacity > 100) | 212 | if (di->rem_capacity > 100) |
213 | di->rem_capacity = 100; | 213 | di->rem_capacity = 100; |
214 | 214 | ||
215 | if (di->current_uA >= 100L) | 215 | if (di->current_uA < -100L) |
216 | di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * 36L) | 216 | di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * 36L) |
217 | / (di->current_uA / 100L); | 217 | / (di->current_uA / 100L); |
218 | else | 218 | else |
diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c new file mode 100644 index 000000000000..25b88ac1d44c --- /dev/null +++ b/drivers/power/gpio-charger.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * Driver for chargers which report their online status through a GPIO pin | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/device.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/power_supply.h> | ||
24 | #include <linux/slab.h> | ||
25 | |||
26 | #include <linux/power/gpio-charger.h> | ||
27 | |||
28 | struct gpio_charger { | ||
29 | const struct gpio_charger_platform_data *pdata; | ||
30 | unsigned int irq; | ||
31 | |||
32 | struct power_supply charger; | ||
33 | }; | ||
34 | |||
35 | static irqreturn_t gpio_charger_irq(int irq, void *devid) | ||
36 | { | ||
37 | struct power_supply *charger = devid; | ||
38 | |||
39 | power_supply_changed(charger); | ||
40 | |||
41 | return IRQ_HANDLED; | ||
42 | } | ||
43 | |||
44 | static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) | ||
45 | { | ||
46 | return container_of(psy, struct gpio_charger, charger); | ||
47 | } | ||
48 | |||
49 | static int gpio_charger_get_property(struct power_supply *psy, | ||
50 | enum power_supply_property psp, union power_supply_propval *val) | ||
51 | { | ||
52 | struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy); | ||
53 | const struct gpio_charger_platform_data *pdata = gpio_charger->pdata; | ||
54 | |||
55 | switch (psp) { | ||
56 | case POWER_SUPPLY_PROP_ONLINE: | ||
57 | val->intval = gpio_get_value(pdata->gpio); | ||
58 | val->intval ^= pdata->gpio_active_low; | ||
59 | break; | ||
60 | default: | ||
61 | return -EINVAL; | ||
62 | } | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static enum power_supply_property gpio_charger_properties[] = { | ||
68 | POWER_SUPPLY_PROP_ONLINE, | ||
69 | }; | ||
70 | |||
71 | static int __devinit gpio_charger_probe(struct platform_device *pdev) | ||
72 | { | ||
73 | const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data; | ||
74 | struct gpio_charger *gpio_charger; | ||
75 | struct power_supply *charger; | ||
76 | int ret; | ||
77 | int irq; | ||
78 | |||
79 | if (!pdata) { | ||
80 | dev_err(&pdev->dev, "No platform data\n"); | ||
81 | return -EINVAL; | ||
82 | } | ||
83 | |||
84 | if (!gpio_is_valid(pdata->gpio)) { | ||
85 | dev_err(&pdev->dev, "Invalid gpio pin\n"); | ||
86 | return -EINVAL; | ||
87 | } | ||
88 | |||
89 | gpio_charger = kzalloc(sizeof(*gpio_charger), GFP_KERNEL); | ||
90 | if (!gpio_charger) { | ||
91 | dev_err(&pdev->dev, "Failed to alloc driver structure\n"); | ||
92 | return -ENOMEM; | ||
93 | } | ||
94 | |||
95 | charger = &gpio_charger->charger; | ||
96 | |||
97 | charger->name = pdata->name ? pdata->name : "gpio-charger"; | ||
98 | charger->type = pdata->type; | ||
99 | charger->properties = gpio_charger_properties; | ||
100 | charger->num_properties = ARRAY_SIZE(gpio_charger_properties); | ||
101 | charger->get_property = gpio_charger_get_property; | ||
102 | charger->supplied_to = pdata->supplied_to; | ||
103 | charger->num_supplicants = pdata->num_supplicants; | ||
104 | |||
105 | ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); | ||
106 | if (ret) { | ||
107 | dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret); | ||
108 | goto err_free; | ||
109 | } | ||
110 | ret = gpio_direction_input(pdata->gpio); | ||
111 | if (ret) { | ||
112 | dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret); | ||
113 | goto err_gpio_free; | ||
114 | } | ||
115 | |||
116 | gpio_charger->pdata = pdata; | ||
117 | |||
118 | ret = power_supply_register(&pdev->dev, charger); | ||
119 | if (ret < 0) { | ||
120 | dev_err(&pdev->dev, "Failed to register power supply: %d\n", | ||
121 | ret); | ||
122 | goto err_gpio_free; | ||
123 | } | ||
124 | |||
125 | irq = gpio_to_irq(pdata->gpio); | ||
126 | if (irq > 0) { | ||
127 | ret = request_any_context_irq(irq, gpio_charger_irq, | ||
128 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
129 | dev_name(&pdev->dev), charger); | ||
130 | if (ret) | ||
131 | dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); | ||
132 | else | ||
133 | gpio_charger->irq = irq; | ||
134 | } | ||
135 | |||
136 | platform_set_drvdata(pdev, gpio_charger); | ||
137 | |||
138 | return 0; | ||
139 | |||
140 | err_gpio_free: | ||
141 | gpio_free(pdata->gpio); | ||
142 | err_free: | ||
143 | kfree(gpio_charger); | ||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | static int __devexit gpio_charger_remove(struct platform_device *pdev) | ||
148 | { | ||
149 | struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); | ||
150 | |||
151 | if (gpio_charger->irq) | ||
152 | free_irq(gpio_charger->irq, &gpio_charger->charger); | ||
153 | |||
154 | power_supply_unregister(&gpio_charger->charger); | ||
155 | |||
156 | gpio_free(gpio_charger->pdata->gpio); | ||
157 | |||
158 | platform_set_drvdata(pdev, NULL); | ||
159 | kfree(gpio_charger); | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static struct platform_driver gpio_charger_driver = { | ||
165 | .probe = gpio_charger_probe, | ||
166 | .remove = __devexit_p(gpio_charger_remove), | ||
167 | .driver = { | ||
168 | .name = "gpio-charger", | ||
169 | .owner = THIS_MODULE, | ||
170 | }, | ||
171 | }; | ||
172 | |||
173 | static int __init gpio_charger_init(void) | ||
174 | { | ||
175 | return platform_driver_register(&gpio_charger_driver); | ||
176 | } | ||
177 | module_init(gpio_charger_init); | ||
178 | |||
179 | static void __exit gpio_charger_exit(void) | ||
180 | { | ||
181 | platform_driver_unregister(&gpio_charger_driver); | ||
182 | } | ||
183 | module_exit(gpio_charger_exit); | ||
184 | |||
185 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
186 | MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO"); | ||
187 | MODULE_LICENSE("GPL"); | ||
188 | MODULE_ALIAS("platform:gpio-charger"); | ||
diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c index 36cf402c0677..bce3a01da2f0 100644 --- a/drivers/power/intel_mid_battery.c +++ b/drivers/power/intel_mid_battery.c | |||
@@ -765,7 +765,7 @@ static int __devexit platform_pmic_battery_remove(struct platform_device *pdev) | |||
765 | power_supply_unregister(&pbi->usb); | 765 | power_supply_unregister(&pbi->usb); |
766 | power_supply_unregister(&pbi->batt); | 766 | power_supply_unregister(&pbi->batt); |
767 | 767 | ||
768 | flush_scheduled_work(); | 768 | cancel_work_sync(&pbi->handler); |
769 | kfree(pbi); | 769 | kfree(pbi); |
770 | return 0; | 770 | return 0; |
771 | } | 771 | } |
diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 72512185f3e2..2ad9b14a5ce3 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c | |||
@@ -59,11 +59,61 @@ struct isp1704_charger { | |||
59 | struct notifier_block nb; | 59 | struct notifier_block nb; |
60 | struct work_struct work; | 60 | struct work_struct work; |
61 | 61 | ||
62 | char model[7]; | 62 | /* properties */ |
63 | char model[8]; | ||
63 | unsigned present:1; | 64 | unsigned present:1; |
65 | unsigned online:1; | ||
66 | unsigned current_max; | ||
67 | |||
68 | /* temp storage variables */ | ||
69 | unsigned long event; | ||
70 | unsigned max_power; | ||
64 | }; | 71 | }; |
65 | 72 | ||
66 | /* | 73 | /* |
74 | * Determine is the charging port DCP (dedicated charger) or CDP (Host/HUB | ||
75 | * chargers). | ||
76 | * | ||
77 | * REVISIT: The method is defined in Battery Charging Specification and is | ||
78 | * applicable to any ULPI transceiver. Nothing isp170x specific here. | ||
79 | */ | ||
80 | static inline int isp1704_charger_type(struct isp1704_charger *isp) | ||
81 | { | ||
82 | u8 reg; | ||
83 | u8 func_ctrl; | ||
84 | u8 otg_ctrl; | ||
85 | int type = POWER_SUPPLY_TYPE_USB_DCP; | ||
86 | |||
87 | func_ctrl = otg_io_read(isp->otg, ULPI_FUNC_CTRL); | ||
88 | otg_ctrl = otg_io_read(isp->otg, ULPI_OTG_CTRL); | ||
89 | |||
90 | /* disable pulldowns */ | ||
91 | reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN; | ||
92 | otg_io_write(isp->otg, ULPI_CLR(ULPI_OTG_CTRL), reg); | ||
93 | |||
94 | /* full speed */ | ||
95 | otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL), | ||
96 | ULPI_FUNC_CTRL_XCVRSEL_MASK); | ||
97 | otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), | ||
98 | ULPI_FUNC_CTRL_FULL_SPEED); | ||
99 | |||
100 | /* Enable strong pull-up on DP (1.5K) and reset */ | ||
101 | reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET; | ||
102 | otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), reg); | ||
103 | usleep_range(1000, 2000); | ||
104 | |||
105 | reg = otg_io_read(isp->otg, ULPI_DEBUG); | ||
106 | if ((reg & 3) != 3) | ||
107 | type = POWER_SUPPLY_TYPE_USB_CDP; | ||
108 | |||
109 | /* recover original state */ | ||
110 | otg_io_write(isp->otg, ULPI_FUNC_CTRL, func_ctrl); | ||
111 | otg_io_write(isp->otg, ULPI_OTG_CTRL, otg_ctrl); | ||
112 | |||
113 | return type; | ||
114 | } | ||
115 | |||
116 | /* | ||
67 | * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger | 117 | * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger |
68 | * is actually a dedicated charger, the following steps need to be taken. | 118 | * is actually a dedicated charger, the following steps need to be taken. |
69 | */ | 119 | */ |
@@ -127,16 +177,19 @@ static inline int isp1704_charger_verify(struct isp1704_charger *isp) | |||
127 | static inline int isp1704_charger_detect(struct isp1704_charger *isp) | 177 | static inline int isp1704_charger_detect(struct isp1704_charger *isp) |
128 | { | 178 | { |
129 | unsigned long timeout; | 179 | unsigned long timeout; |
130 | u8 r; | 180 | u8 pwr_ctrl; |
131 | int ret = 0; | 181 | int ret = 0; |
132 | 182 | ||
183 | pwr_ctrl = otg_io_read(isp->otg, ISP1704_PWR_CTRL); | ||
184 | |||
133 | /* set SW control bit in PWR_CTRL register */ | 185 | /* set SW control bit in PWR_CTRL register */ |
134 | otg_io_write(isp->otg, ISP1704_PWR_CTRL, | 186 | otg_io_write(isp->otg, ISP1704_PWR_CTRL, |
135 | ISP1704_PWR_CTRL_SWCTRL); | 187 | ISP1704_PWR_CTRL_SWCTRL); |
136 | 188 | ||
137 | /* enable manual charger detection */ | 189 | /* enable manual charger detection */ |
138 | r = (ISP1704_PWR_CTRL_SWCTRL | ISP1704_PWR_CTRL_DPVSRC_EN); | 190 | otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL), |
139 | otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL), r); | 191 | ISP1704_PWR_CTRL_SWCTRL |
192 | | ISP1704_PWR_CTRL_DPVSRC_EN); | ||
140 | usleep_range(1000, 2000); | 193 | usleep_range(1000, 2000); |
141 | 194 | ||
142 | timeout = jiffies + msecs_to_jiffies(300); | 195 | timeout = jiffies + msecs_to_jiffies(300); |
@@ -147,7 +200,10 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp) | |||
147 | ret = isp1704_charger_verify(isp); | 200 | ret = isp1704_charger_verify(isp); |
148 | break; | 201 | break; |
149 | } | 202 | } |
150 | } while (!time_after(jiffies, timeout)); | 203 | } while (!time_after(jiffies, timeout) && isp->online); |
204 | |||
205 | /* recover original state */ | ||
206 | otg_io_write(isp->otg, ISP1704_PWR_CTRL, pwr_ctrl); | ||
151 | 207 | ||
152 | return ret; | 208 | return ret; |
153 | } | 209 | } |
@@ -155,52 +211,92 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp) | |||
155 | static void isp1704_charger_work(struct work_struct *data) | 211 | static void isp1704_charger_work(struct work_struct *data) |
156 | { | 212 | { |
157 | int detect; | 213 | int detect; |
214 | unsigned long event; | ||
215 | unsigned power; | ||
158 | struct isp1704_charger *isp = | 216 | struct isp1704_charger *isp = |
159 | container_of(data, struct isp1704_charger, work); | 217 | container_of(data, struct isp1704_charger, work); |
218 | static DEFINE_MUTEX(lock); | ||
160 | 219 | ||
161 | /* | 220 | event = isp->event; |
162 | * FIXME Only supporting dedicated chargers even though isp1704 can | 221 | power = isp->max_power; |
163 | * detect HUB and HOST chargers. If the device has already been | ||
164 | * enumerated, the detection will break the connection. | ||
165 | */ | ||
166 | if (isp->otg->state != OTG_STATE_B_IDLE) | ||
167 | return; | ||
168 | 222 | ||
169 | /* disable data pullups */ | 223 | mutex_lock(&lock); |
170 | if (isp->otg->gadget) | 224 | |
171 | usb_gadget_disconnect(isp->otg->gadget); | 225 | switch (event) { |
226 | case USB_EVENT_VBUS: | ||
227 | isp->online = true; | ||
228 | |||
229 | /* detect charger */ | ||
230 | detect = isp1704_charger_detect(isp); | ||
231 | |||
232 | if (detect) { | ||
233 | isp->present = detect; | ||
234 | isp->psy.type = isp1704_charger_type(isp); | ||
235 | } | ||
172 | 236 | ||
173 | /* detect charger */ | 237 | switch (isp->psy.type) { |
174 | detect = isp1704_charger_detect(isp); | 238 | case POWER_SUPPLY_TYPE_USB_DCP: |
175 | if (detect) { | 239 | isp->current_max = 1800; |
176 | isp->present = detect; | 240 | break; |
177 | power_supply_changed(&isp->psy); | 241 | case POWER_SUPPLY_TYPE_USB_CDP: |
242 | /* | ||
243 | * Only 500mA here or high speed chirp | ||
244 | * handshaking may break | ||
245 | */ | ||
246 | isp->current_max = 500; | ||
247 | /* FALLTHROUGH */ | ||
248 | case POWER_SUPPLY_TYPE_USB: | ||
249 | default: | ||
250 | /* enable data pullups */ | ||
251 | if (isp->otg->gadget) | ||
252 | usb_gadget_connect(isp->otg->gadget); | ||
253 | } | ||
254 | break; | ||
255 | case USB_EVENT_NONE: | ||
256 | isp->online = false; | ||
257 | isp->current_max = 0; | ||
258 | isp->present = 0; | ||
259 | isp->current_max = 0; | ||
260 | isp->psy.type = POWER_SUPPLY_TYPE_USB; | ||
261 | |||
262 | /* | ||
263 | * Disable data pullups. We need to prevent the controller from | ||
264 | * enumerating. | ||
265 | * | ||
266 | * FIXME: This is here to allow charger detection with Host/HUB | ||
267 | * chargers. The pullups may be enabled elsewhere, so this can | ||
268 | * not be the final solution. | ||
269 | */ | ||
270 | if (isp->otg->gadget) | ||
271 | usb_gadget_disconnect(isp->otg->gadget); | ||
272 | break; | ||
273 | case USB_EVENT_ENUMERATED: | ||
274 | if (isp->present) | ||
275 | isp->current_max = 1800; | ||
276 | else | ||
277 | isp->current_max = power; | ||
278 | break; | ||
279 | default: | ||
280 | goto out; | ||
178 | } | 281 | } |
179 | 282 | ||
180 | /* enable data pullups */ | 283 | power_supply_changed(&isp->psy); |
181 | if (isp->otg->gadget) | 284 | out: |
182 | usb_gadget_connect(isp->otg->gadget); | 285 | mutex_unlock(&lock); |
183 | } | 286 | } |
184 | 287 | ||
185 | static int isp1704_notifier_call(struct notifier_block *nb, | 288 | static int isp1704_notifier_call(struct notifier_block *nb, |
186 | unsigned long event, void *unused) | 289 | unsigned long event, void *power) |
187 | { | 290 | { |
188 | struct isp1704_charger *isp = | 291 | struct isp1704_charger *isp = |
189 | container_of(nb, struct isp1704_charger, nb); | 292 | container_of(nb, struct isp1704_charger, nb); |
190 | 293 | ||
191 | switch (event) { | 294 | isp->event = event; |
192 | case USB_EVENT_VBUS: | 295 | |
193 | schedule_work(&isp->work); | 296 | if (power) |
194 | break; | 297 | isp->max_power = *((unsigned *)power); |
195 | case USB_EVENT_NONE: | 298 | |
196 | if (isp->present) { | 299 | schedule_work(&isp->work); |
197 | isp->present = 0; | ||
198 | power_supply_changed(&isp->psy); | ||
199 | } | ||
200 | break; | ||
201 | default: | ||
202 | return NOTIFY_DONE; | ||
203 | } | ||
204 | 300 | ||
205 | return NOTIFY_OK; | 301 | return NOTIFY_OK; |
206 | } | 302 | } |
@@ -216,6 +312,12 @@ static int isp1704_charger_get_property(struct power_supply *psy, | |||
216 | case POWER_SUPPLY_PROP_PRESENT: | 312 | case POWER_SUPPLY_PROP_PRESENT: |
217 | val->intval = isp->present; | 313 | val->intval = isp->present; |
218 | break; | 314 | break; |
315 | case POWER_SUPPLY_PROP_ONLINE: | ||
316 | val->intval = isp->online; | ||
317 | break; | ||
318 | case POWER_SUPPLY_PROP_CURRENT_MAX: | ||
319 | val->intval = isp->current_max; | ||
320 | break; | ||
219 | case POWER_SUPPLY_PROP_MODEL_NAME: | 321 | case POWER_SUPPLY_PROP_MODEL_NAME: |
220 | val->strval = isp->model; | 322 | val->strval = isp->model; |
221 | break; | 323 | break; |
@@ -230,6 +332,8 @@ static int isp1704_charger_get_property(struct power_supply *psy, | |||
230 | 332 | ||
231 | static enum power_supply_property power_props[] = { | 333 | static enum power_supply_property power_props[] = { |
232 | POWER_SUPPLY_PROP_PRESENT, | 334 | POWER_SUPPLY_PROP_PRESENT, |
335 | POWER_SUPPLY_PROP_ONLINE, | ||
336 | POWER_SUPPLY_PROP_CURRENT_MAX, | ||
233 | POWER_SUPPLY_PROP_MODEL_NAME, | 337 | POWER_SUPPLY_PROP_MODEL_NAME, |
234 | POWER_SUPPLY_PROP_MANUFACTURER, | 338 | POWER_SUPPLY_PROP_MANUFACTURER, |
235 | }; | 339 | }; |
@@ -287,13 +391,13 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) | |||
287 | if (!isp->otg) | 391 | if (!isp->otg) |
288 | goto fail0; | 392 | goto fail0; |
289 | 393 | ||
394 | isp->dev = &pdev->dev; | ||
395 | platform_set_drvdata(pdev, isp); | ||
396 | |||
290 | ret = isp1704_test_ulpi(isp); | 397 | ret = isp1704_test_ulpi(isp); |
291 | if (ret < 0) | 398 | if (ret < 0) |
292 | goto fail1; | 399 | goto fail1; |
293 | 400 | ||
294 | isp->dev = &pdev->dev; | ||
295 | platform_set_drvdata(pdev, isp); | ||
296 | |||
297 | isp->psy.name = "isp1704"; | 401 | isp->psy.name = "isp1704"; |
298 | isp->psy.type = POWER_SUPPLY_TYPE_USB; | 402 | isp->psy.type = POWER_SUPPLY_TYPE_USB; |
299 | isp->psy.properties = power_props; | 403 | isp->psy.properties = power_props; |
@@ -318,6 +422,23 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) | |||
318 | 422 | ||
319 | dev_info(isp->dev, "registered with product id %s\n", isp->model); | 423 | dev_info(isp->dev, "registered with product id %s\n", isp->model); |
320 | 424 | ||
425 | /* | ||
426 | * Taking over the D+ pullup. | ||
427 | * | ||
428 | * FIXME: The device will be disconnected if it was already | ||
429 | * enumerated. The charger driver should be always loaded before any | ||
430 | * gadget is loaded. | ||
431 | */ | ||
432 | if (isp->otg->gadget) | ||
433 | usb_gadget_disconnect(isp->otg->gadget); | ||
434 | |||
435 | /* Detect charger if VBUS is valid (the cable was already plugged). */ | ||
436 | ret = otg_io_read(isp->otg, ULPI_USB_INT_STS); | ||
437 | if ((ret & ULPI_INT_VBUS_VALID) && !isp->otg->default_a) { | ||
438 | isp->event = USB_EVENT_VBUS; | ||
439 | schedule_work(&isp->work); | ||
440 | } | ||
441 | |||
321 | return 0; | 442 | return 0; |
322 | fail2: | 443 | fail2: |
323 | power_supply_unregister(&isp->psy); | 444 | power_supply_unregister(&isp->psy); |
diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index a8108a73593e..02414db6a94c 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | #include <linux/io.h> | ||
22 | 23 | ||
23 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
24 | #include <linux/gpio.h> | 25 | #include <linux/gpio.h> |
@@ -47,6 +48,8 @@ struct jz_battery { | |||
47 | 48 | ||
48 | struct power_supply battery; | 49 | struct power_supply battery; |
49 | struct delayed_work work; | 50 | struct delayed_work work; |
51 | |||
52 | struct mutex lock; | ||
50 | }; | 53 | }; |
51 | 54 | ||
52 | static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy) | 55 | static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy) |
@@ -68,6 +71,8 @@ static long jz_battery_read_voltage(struct jz_battery *battery) | |||
68 | unsigned long val; | 71 | unsigned long val; |
69 | long voltage; | 72 | long voltage; |
70 | 73 | ||
74 | mutex_lock(&battery->lock); | ||
75 | |||
71 | INIT_COMPLETION(battery->read_completion); | 76 | INIT_COMPLETION(battery->read_completion); |
72 | 77 | ||
73 | enable_irq(battery->irq); | 78 | enable_irq(battery->irq); |
@@ -91,6 +96,8 @@ static long jz_battery_read_voltage(struct jz_battery *battery) | |||
91 | battery->cell->disable(battery->pdev); | 96 | battery->cell->disable(battery->pdev); |
92 | disable_irq(battery->irq); | 97 | disable_irq(battery->irq); |
93 | 98 | ||
99 | mutex_unlock(&battery->lock); | ||
100 | |||
94 | return voltage; | 101 | return voltage; |
95 | } | 102 | } |
96 | 103 | ||
@@ -240,6 +247,11 @@ static int __devinit jz_battery_probe(struct platform_device *pdev) | |||
240 | struct jz_battery *jz_battery; | 247 | struct jz_battery *jz_battery; |
241 | struct power_supply *battery; | 248 | struct power_supply *battery; |
242 | 249 | ||
250 | if (!pdata) { | ||
251 | dev_err(&pdev->dev, "No platform_data supplied\n"); | ||
252 | return -ENXIO; | ||
253 | } | ||
254 | |||
243 | jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL); | 255 | jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL); |
244 | if (!jz_battery) { | 256 | if (!jz_battery) { |
245 | dev_err(&pdev->dev, "Failed to allocate driver structure\n"); | 257 | dev_err(&pdev->dev, "Failed to allocate driver structure\n"); |
@@ -291,6 +303,7 @@ static int __devinit jz_battery_probe(struct platform_device *pdev) | |||
291 | jz_battery->pdev = pdev; | 303 | jz_battery->pdev = pdev; |
292 | 304 | ||
293 | init_completion(&jz_battery->read_completion); | 305 | init_completion(&jz_battery->read_completion); |
306 | mutex_init(&jz_battery->lock); | ||
294 | 307 | ||
295 | INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work); | 308 | INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work); |
296 | 309 | ||
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c new file mode 100644 index 000000000000..c5c8805156cb --- /dev/null +++ b/drivers/power/max17042_battery.c | |||
@@ -0,0 +1,239 @@ | |||
1 | /* | ||
2 | * Fuel gauge driver for Maxim 17042 / 8966 / 8997 | ||
3 | * Note that Maxim 8966 and 8997 are mfd and this is its subdevice. | ||
4 | * | ||
5 | * Copyright (C) 2011 Samsung Electronics | ||
6 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (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, MA 02111-1307 USA | ||
21 | * | ||
22 | * This driver is based on max17040_battery.c | ||
23 | */ | ||
24 | |||
25 | #include <linux/init.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/i2c.h> | ||
28 | #include <linux/mod_devicetable.h> | ||
29 | #include <linux/power_supply.h> | ||
30 | #include <linux/power/max17042_battery.h> | ||
31 | |||
32 | enum max17042_register { | ||
33 | MAX17042_STATUS = 0x00, | ||
34 | MAX17042_VALRT_Th = 0x01, | ||
35 | MAX17042_TALRT_Th = 0x02, | ||
36 | MAX17042_SALRT_Th = 0x03, | ||
37 | MAX17042_AtRate = 0x04, | ||
38 | MAX17042_RepCap = 0x05, | ||
39 | MAX17042_RepSOC = 0x06, | ||
40 | MAX17042_Age = 0x07, | ||
41 | MAX17042_TEMP = 0x08, | ||
42 | MAX17042_VCELL = 0x09, | ||
43 | MAX17042_Current = 0x0A, | ||
44 | MAX17042_AvgCurrent = 0x0B, | ||
45 | MAX17042_Qresidual = 0x0C, | ||
46 | MAX17042_SOC = 0x0D, | ||
47 | MAX17042_AvSOC = 0x0E, | ||
48 | MAX17042_RemCap = 0x0F, | ||
49 | MAX17402_FullCAP = 0x10, | ||
50 | MAX17042_TTE = 0x11, | ||
51 | MAX17042_V_empty = 0x12, | ||
52 | |||
53 | MAX17042_RSLOW = 0x14, | ||
54 | |||
55 | MAX17042_AvgTA = 0x16, | ||
56 | MAX17042_Cycles = 0x17, | ||
57 | MAX17042_DesignCap = 0x18, | ||
58 | MAX17042_AvgVCELL = 0x19, | ||
59 | MAX17042_MinMaxTemp = 0x1A, | ||
60 | MAX17042_MinMaxVolt = 0x1B, | ||
61 | MAX17042_MinMaxCurr = 0x1C, | ||
62 | MAX17042_CONFIG = 0x1D, | ||
63 | MAX17042_ICHGTerm = 0x1E, | ||
64 | MAX17042_AvCap = 0x1F, | ||
65 | MAX17042_ManName = 0x20, | ||
66 | MAX17042_DevName = 0x21, | ||
67 | MAX17042_DevChem = 0x22, | ||
68 | |||
69 | MAX17042_TempNom = 0x24, | ||
70 | MAX17042_TempCold = 0x25, | ||
71 | MAX17042_TempHot = 0x26, | ||
72 | MAX17042_AIN = 0x27, | ||
73 | MAX17042_LearnCFG = 0x28, | ||
74 | MAX17042_SHFTCFG = 0x29, | ||
75 | MAX17042_RelaxCFG = 0x2A, | ||
76 | MAX17042_MiscCFG = 0x2B, | ||
77 | MAX17042_TGAIN = 0x2C, | ||
78 | MAx17042_TOFF = 0x2D, | ||
79 | MAX17042_CGAIN = 0x2E, | ||
80 | MAX17042_COFF = 0x2F, | ||
81 | |||
82 | MAX17042_Q_empty = 0x33, | ||
83 | MAX17042_T_empty = 0x34, | ||
84 | |||
85 | MAX17042_RCOMP0 = 0x38, | ||
86 | MAX17042_TempCo = 0x39, | ||
87 | MAX17042_Rx = 0x3A, | ||
88 | MAX17042_T_empty0 = 0x3B, | ||
89 | MAX17042_TaskPeriod = 0x3C, | ||
90 | MAX17042_FSTAT = 0x3D, | ||
91 | |||
92 | MAX17042_SHDNTIMER = 0x3F, | ||
93 | |||
94 | MAX17042_VFRemCap = 0x4A, | ||
95 | |||
96 | MAX17042_QH = 0x4D, | ||
97 | MAX17042_QL = 0x4E, | ||
98 | }; | ||
99 | |||
100 | struct max17042_chip { | ||
101 | struct i2c_client *client; | ||
102 | struct power_supply battery; | ||
103 | struct max17042_platform_data *pdata; | ||
104 | }; | ||
105 | |||
106 | static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value) | ||
107 | { | ||
108 | int ret = i2c_smbus_write_word_data(client, reg, value); | ||
109 | |||
110 | if (ret < 0) | ||
111 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
112 | |||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | static int max17042_read_reg(struct i2c_client *client, u8 reg) | ||
117 | { | ||
118 | int ret = i2c_smbus_read_word_data(client, reg); | ||
119 | |||
120 | if (ret < 0) | ||
121 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
122 | |||
123 | return ret; | ||
124 | } | ||
125 | |||
126 | static enum power_supply_property max17042_battery_props[] = { | ||
127 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
128 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | ||
129 | POWER_SUPPLY_PROP_CAPACITY, | ||
130 | }; | ||
131 | |||
132 | static int max17042_get_property(struct power_supply *psy, | ||
133 | enum power_supply_property psp, | ||
134 | union power_supply_propval *val) | ||
135 | { | ||
136 | struct max17042_chip *chip = container_of(psy, | ||
137 | struct max17042_chip, battery); | ||
138 | |||
139 | switch (psp) { | ||
140 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
141 | val->intval = max17042_read_reg(chip->client, | ||
142 | MAX17042_VCELL) * 83; /* 1000 / 12 = 83 */ | ||
143 | break; | ||
144 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | ||
145 | val->intval = max17042_read_reg(chip->client, | ||
146 | MAX17042_AvgVCELL) * 83; | ||
147 | break; | ||
148 | case POWER_SUPPLY_PROP_CAPACITY: | ||
149 | val->intval = max17042_read_reg(chip->client, | ||
150 | MAX17042_SOC) / 256; | ||
151 | break; | ||
152 | default: | ||
153 | return -EINVAL; | ||
154 | } | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int __devinit max17042_probe(struct i2c_client *client, | ||
159 | const struct i2c_device_id *id) | ||
160 | { | ||
161 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | ||
162 | struct max17042_chip *chip; | ||
163 | int ret; | ||
164 | |||
165 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) | ||
166 | return -EIO; | ||
167 | |||
168 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | ||
169 | if (!chip) | ||
170 | return -ENOMEM; | ||
171 | |||
172 | chip->client = client; | ||
173 | chip->pdata = client->dev.platform_data; | ||
174 | |||
175 | i2c_set_clientdata(client, chip); | ||
176 | |||
177 | chip->battery.name = "max17042_battery"; | ||
178 | chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; | ||
179 | chip->battery.get_property = max17042_get_property; | ||
180 | chip->battery.properties = max17042_battery_props; | ||
181 | chip->battery.num_properties = ARRAY_SIZE(max17042_battery_props); | ||
182 | |||
183 | ret = power_supply_register(&client->dev, &chip->battery); | ||
184 | if (ret) { | ||
185 | dev_err(&client->dev, "failed: power supply register\n"); | ||
186 | i2c_set_clientdata(client, NULL); | ||
187 | kfree(chip); | ||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | if (!chip->pdata->enable_current_sense) { | ||
192 | max17042_write_reg(client, MAX17042_CGAIN, 0x0000); | ||
193 | max17042_write_reg(client, MAX17042_MiscCFG, 0x0003); | ||
194 | max17042_write_reg(client, MAX17042_LearnCFG, 0x0007); | ||
195 | } | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static int __devexit max17042_remove(struct i2c_client *client) | ||
201 | { | ||
202 | struct max17042_chip *chip = i2c_get_clientdata(client); | ||
203 | |||
204 | power_supply_unregister(&chip->battery); | ||
205 | i2c_set_clientdata(client, NULL); | ||
206 | kfree(chip); | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static const struct i2c_device_id max17042_id[] = { | ||
211 | { "max17042", 0 }, | ||
212 | { } | ||
213 | }; | ||
214 | MODULE_DEVICE_TABLE(i2c, max17042_id); | ||
215 | |||
216 | static struct i2c_driver max17042_i2c_driver = { | ||
217 | .driver = { | ||
218 | .name = "max17042", | ||
219 | }, | ||
220 | .probe = max17042_probe, | ||
221 | .remove = __devexit_p(max17042_remove), | ||
222 | .id_table = max17042_id, | ||
223 | }; | ||
224 | |||
225 | static int __init max17042_init(void) | ||
226 | { | ||
227 | return i2c_add_driver(&max17042_i2c_driver); | ||
228 | } | ||
229 | module_init(max17042_init); | ||
230 | |||
231 | static void __exit max17042_exit(void) | ||
232 | { | ||
233 | i2c_del_driver(&max17042_i2c_driver); | ||
234 | } | ||
235 | module_exit(max17042_exit); | ||
236 | |||
237 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | ||
238 | MODULE_DESCRIPTION("MAX17042 Fuel Gauge"); | ||
239 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 5bc1dcf7785e..0b0ff3a936a6 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c | |||
@@ -201,6 +201,72 @@ static int olpc_bat_get_tech(union power_supply_propval *val) | |||
201 | return ret; | 201 | return ret; |
202 | } | 202 | } |
203 | 203 | ||
204 | static int olpc_bat_get_charge_full_design(union power_supply_propval *val) | ||
205 | { | ||
206 | uint8_t ec_byte; | ||
207 | union power_supply_propval tech; | ||
208 | int ret, mfr; | ||
209 | |||
210 | ret = olpc_bat_get_tech(&tech); | ||
211 | if (ret) | ||
212 | return ret; | ||
213 | |||
214 | ec_byte = BAT_ADDR_MFR_TYPE; | ||
215 | ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); | ||
216 | if (ret) | ||
217 | return ret; | ||
218 | |||
219 | mfr = ec_byte >> 4; | ||
220 | |||
221 | switch (tech.intval) { | ||
222 | case POWER_SUPPLY_TECHNOLOGY_NiMH: | ||
223 | switch (mfr) { | ||
224 | case 1: /* Gold Peak */ | ||
225 | val->intval = 3000000*.8; | ||
226 | break; | ||
227 | default: | ||
228 | return -EIO; | ||
229 | } | ||
230 | break; | ||
231 | |||
232 | case POWER_SUPPLY_TECHNOLOGY_LiFe: | ||
233 | switch (mfr) { | ||
234 | case 1: /* Gold Peak */ | ||
235 | val->intval = 2800000; | ||
236 | break; | ||
237 | case 2: /* BYD */ | ||
238 | val->intval = 3100000; | ||
239 | break; | ||
240 | default: | ||
241 | return -EIO; | ||
242 | } | ||
243 | break; | ||
244 | |||
245 | default: | ||
246 | return -EIO; | ||
247 | } | ||
248 | |||
249 | return ret; | ||
250 | } | ||
251 | |||
252 | static int olpc_bat_get_charge_now(union power_supply_propval *val) | ||
253 | { | ||
254 | uint8_t soc; | ||
255 | union power_supply_propval full; | ||
256 | int ret; | ||
257 | |||
258 | ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &soc, 1); | ||
259 | if (ret) | ||
260 | return ret; | ||
261 | |||
262 | ret = olpc_bat_get_charge_full_design(&full); | ||
263 | if (ret) | ||
264 | return ret; | ||
265 | |||
266 | val->intval = soc * (full.intval / 100); | ||
267 | return 0; | ||
268 | } | ||
269 | |||
204 | /********************************************************************* | 270 | /********************************************************************* |
205 | * Battery properties | 271 | * Battery properties |
206 | *********************************************************************/ | 272 | *********************************************************************/ |
@@ -267,6 +333,7 @@ static int olpc_bat_get_property(struct power_supply *psy, | |||
267 | return ret; | 333 | return ret; |
268 | break; | 334 | break; |
269 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | 335 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: |
336 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
270 | ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2); | 337 | ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2); |
271 | if (ret) | 338 | if (ret) |
272 | return ret; | 339 | return ret; |
@@ -274,6 +341,7 @@ static int olpc_bat_get_property(struct power_supply *psy, | |||
274 | val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32; | 341 | val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32; |
275 | break; | 342 | break; |
276 | case POWER_SUPPLY_PROP_CURRENT_AVG: | 343 | case POWER_SUPPLY_PROP_CURRENT_AVG: |
344 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
277 | ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); | 345 | ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); |
278 | if (ret) | 346 | if (ret) |
279 | return ret; | 347 | return ret; |
@@ -294,6 +362,16 @@ static int olpc_bat_get_property(struct power_supply *psy, | |||
294 | else | 362 | else |
295 | val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; | 363 | val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; |
296 | break; | 364 | break; |
365 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
366 | ret = olpc_bat_get_charge_full_design(val); | ||
367 | if (ret) | ||
368 | return ret; | ||
369 | break; | ||
370 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
371 | ret = olpc_bat_get_charge_now(val); | ||
372 | if (ret) | ||
373 | return ret; | ||
374 | break; | ||
297 | case POWER_SUPPLY_PROP_TEMP: | 375 | case POWER_SUPPLY_PROP_TEMP: |
298 | ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); | 376 | ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); |
299 | if (ret) | 377 | if (ret) |
@@ -331,16 +409,20 @@ static int olpc_bat_get_property(struct power_supply *psy, | |||
331 | return ret; | 409 | return ret; |
332 | } | 410 | } |
333 | 411 | ||
334 | static enum power_supply_property olpc_bat_props[] = { | 412 | static enum power_supply_property olpc_xo1_bat_props[] = { |
335 | POWER_SUPPLY_PROP_STATUS, | 413 | POWER_SUPPLY_PROP_STATUS, |
336 | POWER_SUPPLY_PROP_CHARGE_TYPE, | 414 | POWER_SUPPLY_PROP_CHARGE_TYPE, |
337 | POWER_SUPPLY_PROP_PRESENT, | 415 | POWER_SUPPLY_PROP_PRESENT, |
338 | POWER_SUPPLY_PROP_HEALTH, | 416 | POWER_SUPPLY_PROP_HEALTH, |
339 | POWER_SUPPLY_PROP_TECHNOLOGY, | 417 | POWER_SUPPLY_PROP_TECHNOLOGY, |
340 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | 418 | POWER_SUPPLY_PROP_VOLTAGE_AVG, |
419 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
341 | POWER_SUPPLY_PROP_CURRENT_AVG, | 420 | POWER_SUPPLY_PROP_CURRENT_AVG, |
421 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
342 | POWER_SUPPLY_PROP_CAPACITY, | 422 | POWER_SUPPLY_PROP_CAPACITY, |
343 | POWER_SUPPLY_PROP_CAPACITY_LEVEL, | 423 | POWER_SUPPLY_PROP_CAPACITY_LEVEL, |
424 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | ||
425 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
344 | POWER_SUPPLY_PROP_TEMP, | 426 | POWER_SUPPLY_PROP_TEMP, |
345 | POWER_SUPPLY_PROP_TEMP_AMBIENT, | 427 | POWER_SUPPLY_PROP_TEMP_AMBIENT, |
346 | POWER_SUPPLY_PROP_MANUFACTURER, | 428 | POWER_SUPPLY_PROP_MANUFACTURER, |
@@ -348,6 +430,27 @@ static enum power_supply_property olpc_bat_props[] = { | |||
348 | POWER_SUPPLY_PROP_CHARGE_COUNTER, | 430 | POWER_SUPPLY_PROP_CHARGE_COUNTER, |
349 | }; | 431 | }; |
350 | 432 | ||
433 | /* XO-1.5 does not have ambient temperature property */ | ||
434 | static enum power_supply_property olpc_xo15_bat_props[] = { | ||
435 | POWER_SUPPLY_PROP_STATUS, | ||
436 | POWER_SUPPLY_PROP_CHARGE_TYPE, | ||
437 | POWER_SUPPLY_PROP_PRESENT, | ||
438 | POWER_SUPPLY_PROP_HEALTH, | ||
439 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
440 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | ||
441 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
442 | POWER_SUPPLY_PROP_CURRENT_AVG, | ||
443 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
444 | POWER_SUPPLY_PROP_CAPACITY, | ||
445 | POWER_SUPPLY_PROP_CAPACITY_LEVEL, | ||
446 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | ||
447 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
448 | POWER_SUPPLY_PROP_TEMP, | ||
449 | POWER_SUPPLY_PROP_MANUFACTURER, | ||
450 | POWER_SUPPLY_PROP_SERIAL_NUMBER, | ||
451 | POWER_SUPPLY_PROP_CHARGE_COUNTER, | ||
452 | }; | ||
453 | |||
351 | /* EEPROM reading goes completely around the power_supply API, sadly */ | 454 | /* EEPROM reading goes completely around the power_supply API, sadly */ |
352 | 455 | ||
353 | #define EEPROM_START 0x20 | 456 | #define EEPROM_START 0x20 |
@@ -419,8 +522,6 @@ static struct device_attribute olpc_bat_error = { | |||
419 | static struct platform_device *bat_pdev; | 522 | static struct platform_device *bat_pdev; |
420 | 523 | ||
421 | static struct power_supply olpc_bat = { | 524 | static struct power_supply olpc_bat = { |
422 | .properties = olpc_bat_props, | ||
423 | .num_properties = ARRAY_SIZE(olpc_bat_props), | ||
424 | .get_property = olpc_bat_get_property, | 525 | .get_property = olpc_bat_get_property, |
425 | .use_for_apm = 1, | 526 | .use_for_apm = 1, |
426 | }; | 527 | }; |
@@ -466,6 +567,13 @@ static int __init olpc_bat_init(void) | |||
466 | goto ac_failed; | 567 | goto ac_failed; |
467 | 568 | ||
468 | olpc_bat.name = bat_pdev->name; | 569 | olpc_bat.name = bat_pdev->name; |
570 | if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */ | ||
571 | olpc_bat.properties = olpc_xo15_bat_props; | ||
572 | olpc_bat.num_properties = ARRAY_SIZE(olpc_xo15_bat_props); | ||
573 | } else { /* XO-1 */ | ||
574 | olpc_bat.properties = olpc_xo1_bat_props; | ||
575 | olpc_bat.num_properties = ARRAY_SIZE(olpc_xo1_bat_props); | ||
576 | } | ||
469 | 577 | ||
470 | ret = power_supply_register(&bat_pdev->dev, &olpc_bat); | 578 | ret = power_supply_register(&bat_pdev->dev, &olpc_bat); |
471 | if (ret) | 579 | if (ret) |
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 91606bb55318..970f7335d3a7 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c | |||
@@ -190,10 +190,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy) | |||
190 | goto success; | 190 | goto success; |
191 | 191 | ||
192 | create_triggers_failed: | 192 | create_triggers_failed: |
193 | device_unregister(psy->dev); | 193 | device_del(dev); |
194 | kobject_set_name_failed: | 194 | kobject_set_name_failed: |
195 | device_add_failed: | 195 | device_add_failed: |
196 | kfree(dev); | 196 | put_device(dev); |
197 | success: | 197 | success: |
198 | return rc; | 198 | return rc; |
199 | } | 199 | } |
@@ -201,7 +201,7 @@ EXPORT_SYMBOL_GPL(power_supply_register); | |||
201 | 201 | ||
202 | void power_supply_unregister(struct power_supply *psy) | 202 | void power_supply_unregister(struct power_supply *psy) |
203 | { | 203 | { |
204 | flush_scheduled_work(); | 204 | cancel_work_sync(&psy->changed_work); |
205 | power_supply_remove_triggers(psy); | 205 | power_supply_remove_triggers(psy); |
206 | device_unregister(psy->dev); | 206 | device_unregister(psy->dev); |
207 | } | 207 | } |
diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c index 4a8ae3935b3b..4255f2358b13 100644 --- a/drivers/power/s3c_adc_battery.c +++ b/drivers/power/s3c_adc_battery.c | |||
@@ -112,6 +112,13 @@ static int calc_full_volt(int volt_val, int cur_val, int impedance) | |||
112 | return volt_val + cur_val * impedance / 1000; | 112 | return volt_val + cur_val * impedance / 1000; |
113 | } | 113 | } |
114 | 114 | ||
115 | static int charge_finished(struct s3c_adc_bat *bat) | ||
116 | { | ||
117 | return bat->pdata->gpio_inverted ? | ||
118 | !gpio_get_value(bat->pdata->gpio_charge_finished) : | ||
119 | gpio_get_value(bat->pdata->gpio_charge_finished); | ||
120 | } | ||
121 | |||
115 | static int s3c_adc_bat_get_property(struct power_supply *psy, | 122 | static int s3c_adc_bat_get_property(struct power_supply *psy, |
116 | enum power_supply_property psp, | 123 | enum power_supply_property psp, |
117 | union power_supply_propval *val) | 124 | union power_supply_propval *val) |
@@ -140,7 +147,7 @@ static int s3c_adc_bat_get_property(struct power_supply *psy, | |||
140 | 147 | ||
141 | if (bat->cable_plugged && | 148 | if (bat->cable_plugged && |
142 | ((bat->pdata->gpio_charge_finished < 0) || | 149 | ((bat->pdata->gpio_charge_finished < 0) || |
143 | !gpio_get_value(bat->pdata->gpio_charge_finished))) { | 150 | !charge_finished(bat))) { |
144 | lut = bat->pdata->lut_acin; | 151 | lut = bat->pdata->lut_acin; |
145 | lut_size = bat->pdata->lut_acin_cnt; | 152 | lut_size = bat->pdata->lut_acin_cnt; |
146 | } | 153 | } |
@@ -236,8 +243,7 @@ static void s3c_adc_bat_work(struct work_struct *work) | |||
236 | } | 243 | } |
237 | } else { | 244 | } else { |
238 | if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) { | 245 | if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) { |
239 | is_charged = gpio_get_value( | 246 | is_charged = charge_finished(&main_bat); |
240 | main_bat.pdata->gpio_charge_finished); | ||
241 | if (is_charged) { | 247 | if (is_charged) { |
242 | if (bat->pdata->disable_charger) | 248 | if (bat->pdata->disable_charger) |
243 | bat->pdata->disable_charger(); | 249 | bat->pdata->disable_charger(); |
diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c index ee04936b2db5..53f0d3524fcd 100644 --- a/drivers/power/tosa_battery.c +++ b/drivers/power/tosa_battery.c | |||
@@ -332,7 +332,7 @@ static struct { | |||
332 | static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state) | 332 | static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state) |
333 | { | 333 | { |
334 | /* flush all pending status updates */ | 334 | /* flush all pending status updates */ |
335 | flush_scheduled_work(); | 335 | flush_work_sync(&bat_work); |
336 | return 0; | 336 | return 0; |
337 | } | 337 | } |
338 | 338 | ||
@@ -422,7 +422,7 @@ err_psy_reg_jacket: | |||
422 | err_psy_reg_main: | 422 | err_psy_reg_main: |
423 | 423 | ||
424 | /* see comment in tosa_bat_remove */ | 424 | /* see comment in tosa_bat_remove */ |
425 | flush_scheduled_work(); | 425 | cancel_work_sync(&bat_work); |
426 | 426 | ||
427 | i--; | 427 | i--; |
428 | err_gpio: | 428 | err_gpio: |
@@ -445,12 +445,11 @@ static int __devexit tosa_bat_remove(struct platform_device *dev) | |||
445 | power_supply_unregister(&tosa_bat_main.psy); | 445 | power_supply_unregister(&tosa_bat_main.psy); |
446 | 446 | ||
447 | /* | 447 | /* |
448 | * now flush all pending work. | 448 | * Now cancel the bat_work. We won't get any more schedules, |
449 | * we won't get any more schedules, since all | 449 | * since all sources (isr and external_power_changed) are |
450 | * sources (isr and external_power_changed) | 450 | * unregistered now. |
451 | * are unregistered now. | ||
452 | */ | 451 | */ |
453 | flush_scheduled_work(); | 452 | cancel_work_sync(&bat_work); |
454 | 453 | ||
455 | for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--) | 454 | for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--) |
456 | gpio_free(gpios[i].gpio); | 455 | gpio_free(gpios[i].gpio); |
diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index 5071d85ec12d..156559e56fa5 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c | |||
@@ -147,7 +147,7 @@ static irqreturn_t wm97xx_chrg_irq(int irq, void *data) | |||
147 | #ifdef CONFIG_PM | 147 | #ifdef CONFIG_PM |
148 | static int wm97xx_bat_suspend(struct device *dev) | 148 | static int wm97xx_bat_suspend(struct device *dev) |
149 | { | 149 | { |
150 | flush_scheduled_work(); | 150 | flush_work_sync(&bat_work); |
151 | return 0; | 151 | return 0; |
152 | } | 152 | } |
153 | 153 | ||
@@ -273,7 +273,7 @@ static int __devexit wm97xx_bat_remove(struct platform_device *dev) | |||
273 | free_irq(gpio_to_irq(pdata->charge_gpio), dev); | 273 | free_irq(gpio_to_irq(pdata->charge_gpio), dev); |
274 | gpio_free(pdata->charge_gpio); | 274 | gpio_free(pdata->charge_gpio); |
275 | } | 275 | } |
276 | flush_scheduled_work(); | 276 | cancel_work_sync(&bat_work); |
277 | power_supply_unregister(&bat_ps); | 277 | power_supply_unregister(&bat_ps); |
278 | kfree(prop); | 278 | kfree(prop); |
279 | return 0; | 279 | return 0; |
diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index 85064a9f649e..e5ed52d71937 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c | |||
@@ -254,7 +254,7 @@ static int __devexit z2_batt_remove(struct i2c_client *client) | |||
254 | struct z2_charger *charger = i2c_get_clientdata(client); | 254 | struct z2_charger *charger = i2c_get_clientdata(client); |
255 | struct z2_battery_info *info = charger->info; | 255 | struct z2_battery_info *info = charger->info; |
256 | 256 | ||
257 | flush_scheduled_work(); | 257 | cancel_work_sync(&charger->bat_work); |
258 | power_supply_unregister(&charger->batt_ps); | 258 | power_supply_unregister(&charger->batt_ps); |
259 | 259 | ||
260 | kfree(charger->batt_ps.properties); | 260 | kfree(charger->batt_ps.properties); |
@@ -271,7 +271,9 @@ static int __devexit z2_batt_remove(struct i2c_client *client) | |||
271 | #ifdef CONFIG_PM | 271 | #ifdef CONFIG_PM |
272 | static int z2_batt_suspend(struct i2c_client *client, pm_message_t state) | 272 | static int z2_batt_suspend(struct i2c_client *client, pm_message_t state) |
273 | { | 273 | { |
274 | flush_scheduled_work(); | 274 | struct z2_charger *charger = i2c_get_clientdata(client); |
275 | |||
276 | flush_work_sync(&charger->bat_work); | ||
275 | return 0; | 277 | return 0; |
276 | } | 278 | } |
277 | 279 | ||