diff options
author | Michel JAOUEN <michel.jaouen@stericsson.com> | 2012-04-26 04:00:04 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2013-01-23 06:33:02 -0500 |
commit | 01ec8c5423901c4fe8d97f786ed9a0c31215b53a (patch) | |
tree | 51e12bce93f8d030f7896a7df8dc5a66ec052d3f /drivers/power | |
parent | 8fd526fd18233887ba652079a369f4eee0de9d9d (diff) |
pm2301: Provide u9540 support for the pm2301 charger
AC charger driver for the DB9540 based platforms.
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/ab8500_charger.c | 36 | ||||
-rw-r--r-- | drivers/power/pm2301_charger.c | 1455 |
4 files changed, 1486 insertions, 13 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 9f45e2f77d53..4811b59ba730 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -346,6 +346,13 @@ config AB8500_BM | |||
346 | help | 346 | help |
347 | Say Y to include support for AB8500 battery management. | 347 | Say Y to include support for AB8500 battery management. |
348 | 348 | ||
349 | config CHARGER_PM2301 | ||
350 | bool "PM2301 Battery Charger Driver" | ||
351 | depends on AB8500_BM | ||
352 | help | ||
353 | Say Y to include support for PM2301 charger driver. | ||
354 | Depends on AB8500 battery management core. | ||
355 | |||
349 | source "drivers/power/reset/Kconfig" | 356 | source "drivers/power/reset/Kconfig" |
350 | 357 | ||
351 | endif # POWER_SUPPLY | 358 | endif # POWER_SUPPLY |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b11e0c7ea0f1..aa966e806834 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -46,6 +46,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o | |||
46 | obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o | 46 | obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o |
47 | obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o | 47 | obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o |
48 | obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o | 48 | obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o |
49 | obj-$(CONFIG_CHARGER_PM2301) += pm2301_charger.o | ||
49 | obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o | 50 | obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o |
50 | obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o | 51 | obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o |
51 | obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o | 52 | obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o |
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index d5a8bdadb49a..43ec82ba4275 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c | |||
@@ -2830,8 +2830,11 @@ static int ab8500_charger_remove(struct platform_device *pdev) | |||
2830 | destroy_workqueue(di->charger_wq); | 2830 | destroy_workqueue(di->charger_wq); |
2831 | 2831 | ||
2832 | flush_scheduled_work(); | 2832 | flush_scheduled_work(); |
2833 | power_supply_unregister(&di->usb_chg.psy); | 2833 | if(di->usb_chg.enabled) |
2834 | power_supply_unregister(&di->ac_chg.psy); | 2834 | power_supply_unregister(&di->usb_chg.psy); |
2835 | if(di->ac_chg.enabled) | ||
2836 | power_supply_unregister(&di->ac_chg.psy); | ||
2837 | |||
2835 | platform_set_drvdata(pdev, NULL); | 2838 | platform_set_drvdata(pdev, NULL); |
2836 | 2839 | ||
2837 | return 0; | 2840 | return 0; |
@@ -2899,6 +2902,7 @@ static int ab8500_charger_probe(struct platform_device *pdev) | |||
2899 | ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; | 2902 | ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; |
2900 | di->ac_chg.max_out_curr = ab8500_charger_current_map[ | 2903 | di->ac_chg.max_out_curr = ab8500_charger_current_map[ |
2901 | ARRAY_SIZE(ab8500_charger_current_map) - 1]; | 2904 | ARRAY_SIZE(ab8500_charger_current_map) - 1]; |
2905 | di->ac_chg.enabled = di->pdata->ac_enabled; | ||
2902 | 2906 | ||
2903 | /* USB supply */ | 2907 | /* USB supply */ |
2904 | /* power_supply base class */ | 2908 | /* power_supply base class */ |
@@ -2917,7 +2921,7 @@ static int ab8500_charger_probe(struct platform_device *pdev) | |||
2917 | ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; | 2921 | ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; |
2918 | di->usb_chg.max_out_curr = ab8500_charger_current_map[ | 2922 | di->usb_chg.max_out_curr = ab8500_charger_current_map[ |
2919 | ARRAY_SIZE(ab8500_charger_current_map) - 1]; | 2923 | ARRAY_SIZE(ab8500_charger_current_map) - 1]; |
2920 | 2924 | di->usb_chg.enabled = di->pdata->usb_enabled; | |
2921 | 2925 | ||
2922 | /* Create a work queue for the charger */ | 2926 | /* Create a work queue for the charger */ |
2923 | di->charger_wq = | 2927 | di->charger_wq = |
@@ -2995,17 +2999,21 @@ static int ab8500_charger_probe(struct platform_device *pdev) | |||
2995 | } | 2999 | } |
2996 | 3000 | ||
2997 | /* Register AC charger class */ | 3001 | /* Register AC charger class */ |
2998 | ret = power_supply_register(di->dev, &di->ac_chg.psy); | 3002 | if(di->ac_chg.enabled) { |
2999 | if (ret) { | 3003 | ret = power_supply_register(di->dev, &di->ac_chg.psy); |
3000 | dev_err(di->dev, "failed to register AC charger\n"); | 3004 | if (ret) { |
3001 | goto free_charger_wq; | 3005 | dev_err(di->dev, "failed to register AC charger\n"); |
3006 | goto free_charger_wq; | ||
3007 | } | ||
3002 | } | 3008 | } |
3003 | 3009 | ||
3004 | /* Register USB charger class */ | 3010 | /* Register USB charger class */ |
3005 | ret = power_supply_register(di->dev, &di->usb_chg.psy); | 3011 | if(di->usb_chg.enabled) { |
3006 | if (ret) { | 3012 | ret = power_supply_register(di->dev, &di->usb_chg.psy); |
3007 | dev_err(di->dev, "failed to register USB charger\n"); | 3013 | if (ret) { |
3008 | goto free_ac; | 3014 | dev_err(di->dev, "failed to register USB charger\n"); |
3015 | goto free_ac; | ||
3016 | } | ||
3009 | } | 3017 | } |
3010 | 3018 | ||
3011 | di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); | 3019 | di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); |
@@ -3085,9 +3093,11 @@ free_irq: | |||
3085 | put_usb_phy: | 3093 | put_usb_phy: |
3086 | usb_put_phy(di->usb_phy); | 3094 | usb_put_phy(di->usb_phy); |
3087 | free_usb: | 3095 | free_usb: |
3088 | power_supply_unregister(&di->usb_chg.psy); | 3096 | if(di->usb_chg.enabled) |
3097 | power_supply_unregister(&di->usb_chg.psy); | ||
3089 | free_ac: | 3098 | free_ac: |
3090 | power_supply_unregister(&di->ac_chg.psy); | 3099 | if(di->ac_chg.enabled) |
3100 | power_supply_unregister(&di->ac_chg.psy); | ||
3091 | free_charger_wq: | 3101 | free_charger_wq: |
3092 | destroy_workqueue(di->charger_wq); | 3102 | destroy_workqueue(di->charger_wq); |
3093 | return ret; | 3103 | return ret; |
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c new file mode 100644 index 000000000000..60a30323151e --- /dev/null +++ b/drivers/power/pm2301_charger.c | |||
@@ -0,0 +1,1455 @@ | |||
1 | /* | ||
2 | * Power supply driver for ST Ericsson pm2xxx_charger charger | ||
3 | * | ||
4 | * Copyright 2012 ST Ericsson. | ||
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/init.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/power_supply.h> | ||
19 | #include <linux/completion.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/i2c.h> | ||
23 | #include <linux/workqueue.h> | ||
24 | #include <linux/kobject.h> | ||
25 | #include <linux/mfd/abx500.h> | ||
26 | #include <linux/mfd/abx500/ab8500.h> | ||
27 | #include <linux/mfd/abx500/ab8500-bm.h> | ||
28 | #include <linux/mfd/abx500/ab8500-gpadc.h> | ||
29 | #include <linux/mfd/abx500/ux500_chargalg.h> | ||
30 | #include <linux/pm2301_charger.h> | ||
31 | |||
32 | #define MAIN_WDOG_ENA 0x01 | ||
33 | #define MAIN_WDOG_KICK 0x02 | ||
34 | #define MAIN_WDOG_DIS 0x00 | ||
35 | #define CHARG_WD_KICK 0x01 | ||
36 | #define MAIN_CH_ENA 0x01 | ||
37 | #define MAIN_CH_NO_OVERSHOOT_ENA_N 0x02 | ||
38 | #define MAIN_CH_DET 0x01 | ||
39 | #define MAIN_CH_CV_ON 0x04 | ||
40 | #define OTP_ENABLE_WD 0x01 | ||
41 | |||
42 | #define MAIN_CH_INPUT_CURR_SHIFT 4 | ||
43 | |||
44 | #define LED_INDICATOR_PWM_ENA 0x01 | ||
45 | #define LED_INDICATOR_PWM_DIS 0x00 | ||
46 | #define LED_IND_CUR_5MA 0x04 | ||
47 | #define LED_INDICATOR_PWM_DUTY_252_256 0xBF | ||
48 | |||
49 | /* HW failure constants */ | ||
50 | #define MAIN_CH_TH_PROT 0x02 | ||
51 | #define MAIN_CH_NOK 0x01 | ||
52 | |||
53 | /* Watchdog timeout constant */ | ||
54 | #define WD_TIMER 0x30 /* 4min */ | ||
55 | #define WD_KICK_INTERVAL (60 * HZ) | ||
56 | |||
57 | /* Constant voltage/current */ | ||
58 | #define PM2XXX_CONST_CURR 0x0 | ||
59 | #define PM2XXX_CONST_VOLT 0x1 | ||
60 | |||
61 | /* Lowest charger voltage is 3.39V -> 0x4E */ | ||
62 | #define LOW_VOLT_REG 0x4E | ||
63 | |||
64 | #define PM2XXX_BATT_CTRL_REG1 0x00 | ||
65 | #define PM2XXX_BATT_CTRL_REG2 0x01 | ||
66 | #define PM2XXX_BATT_CTRL_REG3 0x02 | ||
67 | #define PM2XXX_BATT_CTRL_REG4 0x03 | ||
68 | #define PM2XXX_BATT_CTRL_REG5 0x04 | ||
69 | #define PM2XXX_BATT_CTRL_REG6 0x05 | ||
70 | #define PM2XXX_BATT_CTRL_REG7 0x06 | ||
71 | #define PM2XXX_BATT_CTRL_REG8 0x07 | ||
72 | #define PM2XXX_NTC_CTRL_REG1 0x08 | ||
73 | #define PM2XXX_NTC_CTRL_REG2 0x09 | ||
74 | #define PM2XXX_BATT_CTRL_REG9 0x0A | ||
75 | #define PM2XXX_BATT_STAT_REG1 0x0B | ||
76 | #define PM2XXX_INP_VOLT_VPWR2 0x11 | ||
77 | #define PM2XXX_INP_DROP_VPWR2 0x13 | ||
78 | #define PM2XXX_INP_VOLT_VPWR1 0x15 | ||
79 | #define PM2XXX_INP_DROP_VPWR1 0x17 | ||
80 | #define PM2XXX_INP_MODE_VPWR 0x18 | ||
81 | #define PM2XXX_BATT_WD_KICK 0x70 | ||
82 | #define PM2XXX_DEV_VER_STAT 0x0C | ||
83 | #define PM2XXX_THERM_WARN_CTRL_REG 0x20 | ||
84 | #define PM2XXX_BATT_DISC_REG 0x21 | ||
85 | #define PM2XXX_BATT_LOW_LEV_COMP_REG 0x22 | ||
86 | #define PM2XXX_BATT_LOW_LEV_VAL_REG 0x23 | ||
87 | #define PM2XXX_I2C_PAD_CTRL_REG 0x24 | ||
88 | #define PM2XXX_SW_CTRL_REG 0x26 | ||
89 | #define PM2XXX_LED_CTRL_REG 0x28 | ||
90 | |||
91 | #define PM2XXX_REG_INT1 0x40 | ||
92 | #define PM2XXX_MASK_REG_INT1 0x50 | ||
93 | #define PM2XXX_SRCE_REG_INT1 0x60 | ||
94 | #define PM2XXX_REG_INT2 0x41 | ||
95 | #define PM2XXX_MASK_REG_INT2 0x51 | ||
96 | #define PM2XXX_SRCE_REG_INT2 0x61 | ||
97 | #define PM2XXX_REG_INT3 0x42 | ||
98 | #define PM2XXX_MASK_REG_INT3 0x52 | ||
99 | #define PM2XXX_SRCE_REG_INT3 0x62 | ||
100 | #define PM2XXX_REG_INT4 0x43 | ||
101 | #define PM2XXX_MASK_REG_INT4 0x53 | ||
102 | #define PM2XXX_SRCE_REG_INT4 0x63 | ||
103 | #define PM2XXX_REG_INT5 0x44 | ||
104 | #define PM2XXX_MASK_REG_INT5 0x54 | ||
105 | #define PM2XXX_SRCE_REG_INT5 0x64 | ||
106 | #define PM2XXX_REG_INT6 0x45 | ||
107 | #define PM2XXX_MASK_REG_INT6 0x55 | ||
108 | #define PM2XXX_SRCE_REG_INT6 0x65 | ||
109 | |||
110 | #define VPWR_OVV 0x0 | ||
111 | #define VSYSTEM_OVV 0x1 | ||
112 | |||
113 | /* control Reg 1 */ | ||
114 | #define PM2XXX_CH_RESUME_EN 0x1 | ||
115 | #define PM2XXX_CH_RESUME_DIS 0x0 | ||
116 | |||
117 | /* control Reg 2 */ | ||
118 | #define PM2XXX_CH_AUTO_RESUME_EN 0X2 | ||
119 | #define PM2XXX_CH_AUTO_RESUME_DIS 0X0 | ||
120 | #define PM2XXX_CHARGER_ENA 0x4 | ||
121 | #define PM2XXX_CHARGER_DIS 0x0 | ||
122 | |||
123 | /* control Reg 3 */ | ||
124 | #define PM2XXX_CH_WD_CC_PHASE_OFF 0x0 | ||
125 | #define PM2XXX_CH_WD_CC_PHASE_5MIN 0x1 | ||
126 | #define PM2XXX_CH_WD_CC_PHASE_10MIN 0x2 | ||
127 | #define PM2XXX_CH_WD_CC_PHASE_30MIN 0x3 | ||
128 | #define PM2XXX_CH_WD_CC_PHASE_60MIN 0x4 | ||
129 | #define PM2XXX_CH_WD_CC_PHASE_120MIN 0x5 | ||
130 | #define PM2XXX_CH_WD_CC_PHASE_240MIN 0x6 | ||
131 | #define PM2XXX_CH_WD_CC_PHASE_360MIN 0x7 | ||
132 | |||
133 | #define PM2XXX_CH_WD_CV_PHASE_OFF (0x0<<3) | ||
134 | #define PM2XXX_CH_WD_CV_PHASE_5MIN (0x1<<3) | ||
135 | #define PM2XXX_CH_WD_CV_PHASE_10MIN (0x2<<3) | ||
136 | #define PM2XXX_CH_WD_CV_PHASE_30MIN (0x3<<3) | ||
137 | #define PM2XXX_CH_WD_CV_PHASE_60MIN (0x4<<3) | ||
138 | #define PM2XXX_CH_WD_CV_PHASE_120MIN (0x5<<3) | ||
139 | #define PM2XXX_CH_WD_CV_PHASE_240MIN (0x6<<3) | ||
140 | #define PM2XXX_CH_WD_CV_PHASE_360MIN (0x7<<3) | ||
141 | |||
142 | /* control Reg 4 */ | ||
143 | #define PM2XXX_CH_WD_PRECH_PHASE_OFF 0x0 | ||
144 | #define PM2XXX_CH_WD_PRECH_PHASE_1MIN 0x1 | ||
145 | #define PM2XXX_CH_WD_PRECH_PHASE_5MIN 0x2 | ||
146 | #define PM2XXX_CH_WD_PRECH_PHASE_10MIN 0x3 | ||
147 | #define PM2XXX_CH_WD_PRECH_PHASE_30MIN 0x4 | ||
148 | #define PM2XXX_CH_WD_PRECH_PHASE_60MIN 0x5 | ||
149 | #define PM2XXX_CH_WD_PRECH_PHASE_120MIN 0x6 | ||
150 | #define PM2XXX_CH_WD_PRECH_PHASE_240MIN 0x7 | ||
151 | |||
152 | /* control Reg 5 */ | ||
153 | #define PM2XXX_CH_WD_AUTO_TIMEOUT_NONE 0x0 | ||
154 | #define PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN 0x1 | ||
155 | |||
156 | /* control Reg 6 */ | ||
157 | #define PM2XXX_DIR_CH_CC_CURRENT_MASK 0x0F | ||
158 | #define PM2XXX_DIR_CH_CC_CURRENT_200MA 0x0 | ||
159 | #define PM2XXX_DIR_CH_CC_CURRENT_400MA 0x2 | ||
160 | #define PM2XXX_DIR_CH_CC_CURRENT_600MA 0x3 | ||
161 | #define PM2XXX_DIR_CH_CC_CURRENT_800MA 0x4 | ||
162 | #define PM2XXX_DIR_CH_CC_CURRENT_1000MA 0x5 | ||
163 | #define PM2XXX_DIR_CH_CC_CURRENT_1200MA 0x6 | ||
164 | #define PM2XXX_DIR_CH_CC_CURRENT_1400MA 0x7 | ||
165 | #define PM2XXX_DIR_CH_CC_CURRENT_1600MA 0x8 | ||
166 | #define PM2XXX_DIR_CH_CC_CURRENT_1800MA 0x9 | ||
167 | #define PM2XXX_DIR_CH_CC_CURRENT_2000MA 0xA | ||
168 | #define PM2XXX_DIR_CH_CC_CURRENT_2200MA 0xB | ||
169 | #define PM2XXX_DIR_CH_CC_CURRENT_2400MA 0xC | ||
170 | #define PM2XXX_DIR_CH_CC_CURRENT_2600MA 0xD | ||
171 | #define PM2XXX_DIR_CH_CC_CURRENT_2800MA 0xE | ||
172 | #define PM2XXX_DIR_CH_CC_CURRENT_3000MA 0xF | ||
173 | |||
174 | #define PM2XXX_CH_PRECH_CURRENT_MASK 0x30 | ||
175 | #define PM2XXX_CH_PRECH_CURRENT_25MA (0x0<<4) | ||
176 | #define PM2XXX_CH_PRECH_CURRENT_50MA (0x1<<4) | ||
177 | #define PM2XXX_CH_PRECH_CURRENT_75MA (0x2<<4) | ||
178 | #define PM2XXX_CH_PRECH_CURRENT_100MA (0x3<<4) | ||
179 | |||
180 | #define PM2XXX_CH_EOC_CURRENT_MASK 0xC0 | ||
181 | #define PM2XXX_CH_EOC_CURRENT_100MA (0x0<<6) | ||
182 | #define PM2XXX_CH_EOC_CURRENT_150MA (0x1<<6) | ||
183 | #define PM2XXX_CH_EOC_CURRENT_300MA (0x2<<6) | ||
184 | #define PM2XXX_CH_EOC_CURRENT_400MA (0x3<<6) | ||
185 | |||
186 | /* control Reg 7 */ | ||
187 | #define PM2XXX_CH_PRECH_VOL_2_5 0x0 | ||
188 | #define PM2XXX_CH_PRECH_VOL_2_7 0x1 | ||
189 | #define PM2XXX_CH_PRECH_VOL_2_9 0x2 | ||
190 | #define PM2XXX_CH_PRECH_VOL_3_1 0x3 | ||
191 | |||
192 | #define PM2XXX_CH_VRESUME_VOL_3_2 (0x0<<2) | ||
193 | #define PM2XXX_CH_VRESUME_VOL_3_4 (0x1<<2) | ||
194 | #define PM2XXX_CH_VRESUME_VOL_3_6 (0x2<<2) | ||
195 | #define PM2XXX_CH_VRESUME_VOL_3_8 (0x3<<2) | ||
196 | |||
197 | /* control Reg 8 */ | ||
198 | #define PM2XXX_CH_VOLT_MASK 0x3F | ||
199 | #define PM2XXX_CH_VOLT_3_5 0x0 | ||
200 | #define PM2XXX_CH_VOLT_3_5225 0x1 | ||
201 | #define PM2XXX_CH_VOLT_3_6 0x4 | ||
202 | #define PM2XXX_CH_VOLT_3_7 0x8 | ||
203 | #define PM2XXX_CH_VOLT_4_0 0x14 | ||
204 | #define PM2XXX_CH_VOLT_4_175 0x1B | ||
205 | #define PM2XXX_CH_VOLT_4_2 0x1C | ||
206 | #define PM2XXX_CH_VOLT_4_275 0x1F | ||
207 | #define PM2XXX_CH_VOLT_4_3 0x20 | ||
208 | |||
209 | /*NTC control register 1*/ | ||
210 | #define PM2XXX_BTEMP_HIGH_TH_45 0x0 | ||
211 | #define PM2XXX_BTEMP_HIGH_TH_50 0x1 | ||
212 | #define PM2XXX_BTEMP_HIGH_TH_55 0x2 | ||
213 | #define PM2XXX_BTEMP_HIGH_TH_60 0x3 | ||
214 | #define PM2XXX_BTEMP_HIGH_TH_65 0x4 | ||
215 | |||
216 | #define PM2XXX_BTEMP_LOW_TH_N5 (0x0<<3) | ||
217 | #define PM2XXX_BTEMP_LOW_TH_0 (0x1<<3) | ||
218 | #define PM2XXX_BTEMP_LOW_TH_5 (0x2<<3) | ||
219 | #define PM2XXX_BTEMP_LOW_TH_10 (0x3<<3) | ||
220 | |||
221 | /*NTC control register 2*/ | ||
222 | #define PM2XXX_NTC_BETA_COEFF_3477 0x0 | ||
223 | #define PM2XXX_NTC_BETA_COEFF_3964 0x1 | ||
224 | |||
225 | #define PM2XXX_NTC_RES_10K (0x0<<2) | ||
226 | #define PM2XXX_NTC_RES_47K (0x1<<2) | ||
227 | #define PM2XXX_NTC_RES_100K (0x2<<2) | ||
228 | #define PM2XXX_NTC_RES_NO_NTC (0x3<<2) | ||
229 | |||
230 | /* control Reg 9 */ | ||
231 | #define PM2XXX_CH_CC_MODEDROP_EN 1 | ||
232 | #define PM2XXX_CH_CC_MODEDROP_DIS 0 | ||
233 | |||
234 | #define PM2XXX_CH_CC_REDUCED_CURRENT_100MA (0x0<<1) | ||
235 | #define PM2XXX_CH_CC_REDUCED_CURRENT_200MA (0x1<<1) | ||
236 | #define PM2XXX_CH_CC_REDUCED_CURRENT_400MA (0x2<<1) | ||
237 | #define PM2XXX_CH_CC_REDUCED_CURRENT_IDENT (0x3<<1) | ||
238 | |||
239 | #define PM2XXX_CHARCHING_INFO_DIS (0<<3) | ||
240 | #define PM2XXX_CHARCHING_INFO_EN (1<<3) | ||
241 | |||
242 | #define PM2XXX_CH_150MV_DROP_300MV (0<<4) | ||
243 | #define PM2XXX_CH_150MV_DROP_150MV (1<<4) | ||
244 | |||
245 | |||
246 | /* charger status register */ | ||
247 | #define PM2XXX_CHG_STATUS_OFF 0x0 | ||
248 | #define PM2XXX_CHG_STATUS_ON 0x1 | ||
249 | #define PM2XXX_CHG_STATUS_FULL 0x2 | ||
250 | #define PM2XXX_CHG_STATUS_ERR 0x3 | ||
251 | #define PM2XXX_CHG_STATUS_WAIT 0x4 | ||
252 | #define PM2XXX_CHG_STATUS_NOBAT 0x5 | ||
253 | |||
254 | /* Input charger voltage VPWR2 */ | ||
255 | #define PM2XXX_VPWR2_OVV_6_0 0x0 | ||
256 | #define PM2XXX_VPWR2_OVV_6_3 0x1 | ||
257 | #define PM2XXX_VPWR2_OVV_10 0x2 | ||
258 | #define PM2XXX_VPWR2_OVV_NONE 0x3 | ||
259 | |||
260 | /* Input charger voltage VPWR1 */ | ||
261 | #define PM2XXX_VPWR1_OVV_6_0 0x0 | ||
262 | #define PM2XXX_VPWR1_OVV_6_3 0x1 | ||
263 | #define PM2XXX_VPWR1_OVV_10 0x2 | ||
264 | #define PM2XXX_VPWR1_OVV_NONE 0x3 | ||
265 | |||
266 | /* Battery low level comparator control register */ | ||
267 | #define PM2XXX_VBAT_LOW_MONITORING_DIS 0x0 | ||
268 | #define PM2XXX_VBAT_LOW_MONITORING_ENA 0x1 | ||
269 | |||
270 | /* Battery low level value control register */ | ||
271 | #define PM2XXX_VBAT_LOW_LEVEL_2_3 0x0 | ||
272 | #define PM2XXX_VBAT_LOW_LEVEL_2_4 0x1 | ||
273 | #define PM2XXX_VBAT_LOW_LEVEL_2_5 0x2 | ||
274 | #define PM2XXX_VBAT_LOW_LEVEL_2_6 0x3 | ||
275 | #define PM2XXX_VBAT_LOW_LEVEL_2_7 0x4 | ||
276 | #define PM2XXX_VBAT_LOW_LEVEL_2_8 0x5 | ||
277 | #define PM2XXX_VBAT_LOW_LEVEL_2_9 0x6 | ||
278 | #define PM2XXX_VBAT_LOW_LEVEL_3_0 0x7 | ||
279 | #define PM2XXX_VBAT_LOW_LEVEL_3_1 0x8 | ||
280 | #define PM2XXX_VBAT_LOW_LEVEL_3_2 0x9 | ||
281 | #define PM2XXX_VBAT_LOW_LEVEL_3_3 0xA | ||
282 | #define PM2XXX_VBAT_LOW_LEVEL_3_4 0xB | ||
283 | #define PM2XXX_VBAT_LOW_LEVEL_3_5 0xC | ||
284 | #define PM2XXX_VBAT_LOW_LEVEL_3_6 0xD | ||
285 | #define PM2XXX_VBAT_LOW_LEVEL_3_7 0xE | ||
286 | #define PM2XXX_VBAT_LOW_LEVEL_3_8 0xF | ||
287 | #define PM2XXX_VBAT_LOW_LEVEL_3_9 0x10 | ||
288 | #define PM2XXX_VBAT_LOW_LEVEL_4_0 0x11 | ||
289 | #define PM2XXX_VBAT_LOW_LEVEL_4_1 0x12 | ||
290 | #define PM2XXX_VBAT_LOW_LEVEL_4_2 0x13 | ||
291 | |||
292 | /* SW CTRL */ | ||
293 | #define PM2XXX_SWCTRL_HW 0x0 | ||
294 | #define PM2XXX_SWCTRL_SW 0x1 | ||
295 | |||
296 | |||
297 | /* LED Driver Control */ | ||
298 | #define PM2XXX_LED_CURRENT_MASK 0x0C | ||
299 | #define PM2XXX_LED_CURRENT_2_5MA (0X0<<2) | ||
300 | #define PM2XXX_LED_CURRENT_1MA (0X1<<2) | ||
301 | #define PM2XXX_LED_CURRENT_5MA (0X2<<2) | ||
302 | #define PM2XXX_LED_CURRENT_10MA (0X3<<2) | ||
303 | |||
304 | #define PM2XXX_LED_SELECT_MASK 0x02 | ||
305 | #define PM2XXX_LED_SELECT_EN (0X0<<1) | ||
306 | #define PM2XXX_LED_SELECT_DIS (0X1<<1) | ||
307 | |||
308 | #define PM2XXX_ANTI_OVERSHOOT_MASK 0x01 | ||
309 | #define PM2XXX_ANTI_OVERSHOOT_DIS 0X0 | ||
310 | #define PM2XXX_ANTI_OVERSHOOT_EN 0X1 | ||
311 | |||
312 | #define to_pm2xxx_charger_ac_device_info(x) container_of((x), \ | ||
313 | struct pm2xxx_charger, ac_chg) | ||
314 | |||
315 | static int pm2xxx_interrupt_registers[] = { | ||
316 | PM2XXX_REG_INT1, | ||
317 | PM2XXX_REG_INT2, | ||
318 | PM2XXX_REG_INT3, | ||
319 | PM2XXX_REG_INT4, | ||
320 | PM2XXX_REG_INT5, | ||
321 | PM2XXX_REG_INT6, | ||
322 | }; | ||
323 | |||
324 | enum pm2xxx_reg_int1 { | ||
325 | PM2XXX_INT1_ITVBATDISCONNECT = 0x02, | ||
326 | PM2XXX_INT1_ITVBATLOWR = 0x04, | ||
327 | PM2XXX_INT1_ITVBATLOWF = 0x08, | ||
328 | }; | ||
329 | |||
330 | enum pm2xxx_mask_reg_int1 { | ||
331 | PM2XXX_INT1_M_ITVBATDISCONNECT = 0x02, | ||
332 | PM2XXX_INT1_M_ITVBATLOWR = 0x04, | ||
333 | PM2XXX_INT1_M_ITVBATLOWF = 0x08, | ||
334 | }; | ||
335 | |||
336 | enum pm2xxx_source_reg_int1 { | ||
337 | PM2XXX_INT1_S_ITVBATDISCONNECT = 0x02, | ||
338 | PM2XXX_INT1_S_ITVBATLOWR = 0x04, | ||
339 | PM2XXX_INT1_S_ITVBATLOWF = 0x08, | ||
340 | }; | ||
341 | |||
342 | enum pm2xxx_reg_int2 { | ||
343 | PM2XXX_INT2_ITVPWR2PLUG = 0x01, | ||
344 | PM2XXX_INT2_ITVPWR2UNPLUG = 0x02, | ||
345 | PM2XXX_INT2_ITVPWR1PLUG = 0x04, | ||
346 | PM2XXX_INT2_ITVPWR1UNPLUG = 0x08, | ||
347 | }; | ||
348 | |||
349 | enum pm2xxx_mask_reg_int2 { | ||
350 | PM2XXX_INT2_M_ITVPWR2PLUG = 0x01, | ||
351 | PM2XXX_INT2_M_ITVPWR2UNPLUG = 0x02, | ||
352 | PM2XXX_INT2_M_ITVPWR1PLUG = 0x04, | ||
353 | PM2XXX_INT2_M_ITVPWR1UNPLUG = 0x08, | ||
354 | }; | ||
355 | |||
356 | enum pm2xxx_source_reg_int2 { | ||
357 | PM2XXX_INT2_S_ITVPWR2PLUG = 0x03, | ||
358 | PM2XXX_INT2_S_ITVPWR1PLUG = 0x0c, | ||
359 | }; | ||
360 | |||
361 | enum pm2xxx_reg_int3 { | ||
362 | PM2XXX_INT3_ITCHPRECHARGEWD = 0x01, | ||
363 | PM2XXX_INT3_ITCHCCWD = 0x02, | ||
364 | PM2XXX_INT3_ITCHCVWD = 0x04, | ||
365 | PM2XXX_INT3_ITAUTOTIMEOUTWD = 0x08, | ||
366 | }; | ||
367 | |||
368 | enum pm2xxx_mask_reg_int3 { | ||
369 | PM2XXX_INT3_M_ITCHPRECHARGEWD = 0x01, | ||
370 | PM2XXX_INT3_M_ITCHCCWD = 0x02, | ||
371 | PM2XXX_INT3_M_ITCHCVWD = 0x04, | ||
372 | PM2XXX_INT3_M_ITAUTOTIMEOUTWD = 0x08, | ||
373 | }; | ||
374 | |||
375 | enum pm2xxx_source_reg_int3 { | ||
376 | PM2XXX_INT3_S_ITCHPRECHARGEWD = 0x01, | ||
377 | PM2XXX_INT3_S_ITCHCCWD = 0x02, | ||
378 | PM2XXX_INT3_S_ITCHCVWD = 0x04, | ||
379 | PM2XXX_INT3_S_ITAUTOTIMEOUTWD = 0x08, | ||
380 | }; | ||
381 | |||
382 | enum pm2xxx_reg_int4 { | ||
383 | PM2XXX_INT4_ITBATTEMPCOLD = 0x01, | ||
384 | PM2XXX_INT4_ITBATTEMPHOT = 0x02, | ||
385 | PM2XXX_INT4_ITVPWR2OVV = 0x04, | ||
386 | PM2XXX_INT4_ITVPWR1OVV = 0x08, | ||
387 | PM2XXX_INT4_ITCHARGINGON = 0x10, | ||
388 | PM2XXX_INT4_ITVRESUME = 0x20, | ||
389 | PM2XXX_INT4_ITBATTFULL = 0x40, | ||
390 | PM2XXX_INT4_ITCVPHASE = 0x80, | ||
391 | }; | ||
392 | |||
393 | enum pm2xxx_mask_reg_int4 { | ||
394 | PM2XXX_INT4_M_ITBATTEMPCOLD = 0x01, | ||
395 | PM2XXX_INT4_M_ITBATTEMPHOT = 0x02, | ||
396 | PM2XXX_INT4_M_ITVPWR2OVV = 0x04, | ||
397 | PM2XXX_INT4_M_ITVPWR1OVV = 0x08, | ||
398 | PM2XXX_INT4_M_ITCHARGINGON = 0x10, | ||
399 | PM2XXX_INT4_M_ITVRESUME = 0x20, | ||
400 | PM2XXX_INT4_M_ITBATTFULL = 0x40, | ||
401 | PM2XXX_INT4_M_ITCVPHASE = 0x80, | ||
402 | }; | ||
403 | |||
404 | enum pm2xxx_source_reg_int4 { | ||
405 | PM2XXX_INT4_S_ITBATTEMPCOLD = 0x01, | ||
406 | PM2XXX_INT4_S_ITBATTEMPHOT = 0x02, | ||
407 | PM2XXX_INT4_S_ITVPWR2OVV = 0x04, | ||
408 | PM2XXX_INT4_S_ITVPWR1OVV = 0x08, | ||
409 | PM2XXX_INT4_S_ITCHARGINGON = 0x10, | ||
410 | PM2XXX_INT4_S_ITVRESUME = 0x20, | ||
411 | PM2XXX_INT4_S_ITBATTFULL = 0x40, | ||
412 | PM2XXX_INT4_S_ITCVPHASE = 0x80, | ||
413 | }; | ||
414 | |||
415 | enum pm2xxx_reg_int5 { | ||
416 | PM2XXX_INT5_ITTHERMALSHUTDOWNRISE = 0x01, | ||
417 | PM2XXX_INT5_ITTHERMALSHUTDOWNFALL = 0x02, | ||
418 | PM2XXX_INT5_ITTHERMALWARNINGRISE = 0x04, | ||
419 | PM2XXX_INT5_ITTHERMALWARNINGFALL = 0x08, | ||
420 | PM2XXX_INT5_ITVSYSTEMOVV = 0x10, | ||
421 | }; | ||
422 | |||
423 | enum pm2xxx_mask_reg_int5 { | ||
424 | PM2XXX_INT5_M_ITTHERMALSHUTDOWNRISE = 0x01, | ||
425 | PM2XXX_INT5_M_ITTHERMALSHUTDOWNFALL = 0x02, | ||
426 | PM2XXX_INT5_M_ITTHERMALWARNINGRISE = 0x04, | ||
427 | PM2XXX_INT5_M_ITTHERMALWARNINGFALL = 0x08, | ||
428 | PM2XXX_INT5_M_ITVSYSTEMOVV = 0x10, | ||
429 | }; | ||
430 | |||
431 | enum pm2xxx_source_reg_int5 { | ||
432 | PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE = 0x01, | ||
433 | PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL = 0x02, | ||
434 | PM2XXX_INT5_S_ITTHERMALWARNINGRISE = 0x04, | ||
435 | PM2XXX_INT5_S_ITTHERMALWARNINGFALL = 0x08, | ||
436 | PM2XXX_INT5_S_ITVSYSTEMOVV = 0x10, | ||
437 | }; | ||
438 | |||
439 | enum pm2xxx_reg_int6 { | ||
440 | PM2XXX_INT6_ITVPWR2DROP = 0x01, | ||
441 | PM2XXX_INT6_ITVPWR1DROP = 0x02, | ||
442 | PM2XXX_INT6_ITVPWR2VALIDRISE = 0x04, | ||
443 | PM2XXX_INT6_ITVPWR2VALIDFALL = 0x08, | ||
444 | PM2XXX_INT6_ITVPWR1VALIDRISE = 0x10, | ||
445 | PM2XXX_INT6_ITVPWR1VALIDFALL = 0x20, | ||
446 | }; | ||
447 | |||
448 | enum pm2xxx_mask_reg_int6 { | ||
449 | PM2XXX_INT6_M_ITVPWR2DROP = 0x01, | ||
450 | PM2XXX_INT6_M_ITVPWR1DROP = 0x02, | ||
451 | PM2XXX_INT6_M_ITVPWR2VALIDRISE = 0x04, | ||
452 | PM2XXX_INT6_M_ITVPWR2VALIDFALL = 0x08, | ||
453 | PM2XXX_INT6_M_ITVPWR1VALIDRISE = 0x10, | ||
454 | PM2XXX_INT6_M_ITVPWR1VALIDFALL = 0x20, | ||
455 | }; | ||
456 | |||
457 | enum pm2xxx_source_reg_int6 { | ||
458 | PM2XXX_INT6_S_ITVPWR2DROP = 0x01, | ||
459 | PM2XXX_INT6_S_ITVPWR1DROP = 0x02, | ||
460 | PM2XXX_INT6_S_ITVPWR2VALIDRISE = 0x04, | ||
461 | PM2XXX_INT6_S_ITVPWR2VALIDFALL = 0x08, | ||
462 | PM2XXX_INT6_S_ITVPWR1VALIDRISE = 0x10, | ||
463 | PM2XXX_INT6_S_ITVPWR1VALIDFALL = 0x20, | ||
464 | }; | ||
465 | |||
466 | static enum power_supply_property pm2xxx_charger_ac_props[] = { | ||
467 | POWER_SUPPLY_PROP_HEALTH, | ||
468 | POWER_SUPPLY_PROP_PRESENT, | ||
469 | POWER_SUPPLY_PROP_ONLINE, | ||
470 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
471 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | ||
472 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
473 | }; | ||
474 | |||
475 | static int pm2xxx_charger_voltage_map[] = { | ||
476 | 3500, | ||
477 | 3525, | ||
478 | 3550, | ||
479 | 3575, | ||
480 | 3600, | ||
481 | 3625, | ||
482 | 3650, | ||
483 | 3675, | ||
484 | 3700, | ||
485 | 3725, | ||
486 | 3750, | ||
487 | 3775, | ||
488 | 3800, | ||
489 | 3825, | ||
490 | 3850, | ||
491 | 3875, | ||
492 | 3900, | ||
493 | 3925, | ||
494 | 3950, | ||
495 | 3975, | ||
496 | 4000, | ||
497 | 4025, | ||
498 | 4050, | ||
499 | 4075, | ||
500 | 4100, | ||
501 | 4125, | ||
502 | 4150, | ||
503 | 4175, | ||
504 | 4200, | ||
505 | 4225, | ||
506 | 4250, | ||
507 | 4275, | ||
508 | 4300, | ||
509 | }; | ||
510 | |||
511 | static int pm2xxx_charger_current_map[] = { | ||
512 | 200, | ||
513 | 200, | ||
514 | 400, | ||
515 | 600, | ||
516 | 800, | ||
517 | 1000, | ||
518 | 1200, | ||
519 | 1400, | ||
520 | 1600, | ||
521 | 1800, | ||
522 | 2000, | ||
523 | 2200, | ||
524 | 2400, | ||
525 | 2600, | ||
526 | 2800, | ||
527 | 3000, | ||
528 | }; | ||
529 | |||
530 | struct pm2xxx_irq { | ||
531 | char *name; | ||
532 | irqreturn_t (*isr)(int irq, void *data); | ||
533 | }; | ||
534 | |||
535 | struct pm2xxx_charger_info { | ||
536 | int charger_connected; | ||
537 | int charger_online; | ||
538 | int charger_voltage; | ||
539 | int cv_active; | ||
540 | bool wd_expired; | ||
541 | }; | ||
542 | |||
543 | struct pm2xxx_charger_event_flags { | ||
544 | bool mainextchnotok; | ||
545 | bool main_thermal_prot; | ||
546 | bool ovv; | ||
547 | bool chgwdexp; | ||
548 | }; | ||
549 | |||
550 | struct pm2xxx_config { | ||
551 | struct i2c_client *pm2xxx_i2c; | ||
552 | struct i2c_device_id *pm2xxx_id; | ||
553 | }; | ||
554 | |||
555 | struct pm2xxx_charger { | ||
556 | struct device *dev; | ||
557 | u8 chip_id; | ||
558 | bool vddadc_en_ac; | ||
559 | struct pm2xxx_config config; | ||
560 | bool ac_conn; | ||
561 | unsigned int gpio_irq; | ||
562 | int vbat; | ||
563 | int old_vbat; | ||
564 | int failure_case; | ||
565 | int failure_input_ovv; | ||
566 | u8 pm2_int[6]; | ||
567 | struct ab8500_gpadc *gpadc; | ||
568 | struct regulator *regu; | ||
569 | struct pm2xxx_bm_data *bat; | ||
570 | struct mutex lock; | ||
571 | struct ab8500 *parent; | ||
572 | struct pm2xxx_charger_info ac; | ||
573 | struct pm2xxx_charger_platform_data *pdata; | ||
574 | struct workqueue_struct *charger_wq; | ||
575 | struct delayed_work check_vbat_work; | ||
576 | struct work_struct ac_work; | ||
577 | struct work_struct check_main_thermal_prot_work; | ||
578 | struct ux500_charger ac_chg; | ||
579 | struct pm2xxx_charger_event_flags flags; | ||
580 | }; | ||
581 | |||
582 | static const struct i2c_device_id pm2xxx_ident[] = { | ||
583 | { "pm2301", 0 }, | ||
584 | { } | ||
585 | }; | ||
586 | |||
587 | static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val) | ||
588 | { | ||
589 | int ret; | ||
590 | |||
591 | ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg, | ||
592 | 1, val); | ||
593 | if (ret < 0) | ||
594 | dev_err(pm2->dev, "Error reading register at 0x%x\n", reg); | ||
595 | |||
596 | return ret; | ||
597 | } | ||
598 | |||
599 | static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val) | ||
600 | { | ||
601 | int ret; | ||
602 | |||
603 | ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg, | ||
604 | 1, &val); | ||
605 | if (ret < 0) | ||
606 | dev_err(pm2->dev, "Error writing register at 0x%x\n", reg); | ||
607 | |||
608 | return ret; | ||
609 | } | ||
610 | |||
611 | static int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2) | ||
612 | { | ||
613 | int ret; | ||
614 | |||
615 | /* Enable charging */ | ||
616 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, | ||
617 | (PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA)); | ||
618 | |||
619 | return ret; | ||
620 | } | ||
621 | |||
622 | static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2) | ||
623 | { | ||
624 | int ret; | ||
625 | |||
626 | /* Disable charging */ | ||
627 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, | ||
628 | (PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS)); | ||
629 | |||
630 | return ret; | ||
631 | } | ||
632 | |||
633 | static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val) | ||
634 | { | ||
635 | queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); | ||
636 | |||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | |||
641 | int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val) | ||
642 | { | ||
643 | queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); | ||
644 | |||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val) | ||
649 | { | ||
650 | int ret = 0; | ||
651 | |||
652 | pm2->failure_input_ovv++; | ||
653 | if (pm2->failure_input_ovv < 4) { | ||
654 | ret = pm2xxx_charging_enable_mngt(pm2); | ||
655 | goto out; | ||
656 | } else { | ||
657 | pm2->failure_input_ovv = 0; | ||
658 | dev_err(pm2->dev, "Overvoltage detected\n"); | ||
659 | pm2->flags.ovv = true; | ||
660 | power_supply_changed(&pm2->ac_chg.psy); | ||
661 | } | ||
662 | |||
663 | out: | ||
664 | return ret; | ||
665 | } | ||
666 | |||
667 | static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val) | ||
668 | { | ||
669 | dev_dbg(pm2->dev , "20 minutes watchdog occured\n"); | ||
670 | |||
671 | pm2->ac.wd_expired = true; | ||
672 | power_supply_changed(&pm2->ac_chg.psy); | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val) | ||
678 | { | ||
679 | switch (val) { | ||
680 | case PM2XXX_INT1_ITVBATLOWR: | ||
681 | dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n"); | ||
682 | break; | ||
683 | |||
684 | case PM2XXX_INT1_ITVBATLOWF: | ||
685 | dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n"); | ||
686 | break; | ||
687 | |||
688 | default: | ||
689 | dev_err(pm2->dev, "Unknown VBAT level\n"); | ||
690 | } | ||
691 | |||
692 | return 0; | ||
693 | } | ||
694 | |||
695 | static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val) | ||
696 | { | ||
697 | dev_dbg(pm2->dev, "battery disconnected\n"); | ||
698 | |||
699 | return (pm2xxx_charging_disable_mngt(pm2)); | ||
700 | } | ||
701 | |||
702 | static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val) | ||
703 | { | ||
704 | int ret = 0; | ||
705 | |||
706 | ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val); | ||
707 | |||
708 | if (ret < 0) { | ||
709 | dev_err(pm2->dev, "Charger detection failed\n"); | ||
710 | goto out; | ||
711 | } | ||
712 | |||
713 | *val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG); | ||
714 | out: | ||
715 | return ret; | ||
716 | } | ||
717 | |||
718 | static int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val) | ||
719 | { | ||
720 | |||
721 | int ret; | ||
722 | u8 read_val; | ||
723 | |||
724 | /* | ||
725 | * Since we can't be sure that the events are received | ||
726 | * synchronously, we have the check if the main charger is | ||
727 | * connected by reading the interrupt source register. | ||
728 | */ | ||
729 | ret = pm2xxx_charger_detection(pm2, &read_val); | ||
730 | |||
731 | if ((ret == 0) && read_val) { | ||
732 | pm2->ac.charger_connected = 1; | ||
733 | pm2->ac_conn = true; | ||
734 | queue_work(pm2->charger_wq, &pm2->ac_work); | ||
735 | } | ||
736 | |||
737 | |||
738 | return ret; | ||
739 | } | ||
740 | |||
741 | static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2, | ||
742 | int val) | ||
743 | { | ||
744 | pm2->ac.charger_connected = 0; | ||
745 | queue_work(pm2->charger_wq, &pm2->ac_work); | ||
746 | |||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | static int pm2_int_reg0(struct pm2xxx_charger *pm2) | ||
751 | { | ||
752 | int ret = 0; | ||
753 | |||
754 | if (pm2->pm2_int[0] & | ||
755 | (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) { | ||
756 | ret = pm2xxx_charger_vbat_lsig_mngt(pm2, pm2->pm2_int[0] & | ||
757 | (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)); | ||
758 | } | ||
759 | |||
760 | if (pm2->pm2_int[0] & PM2XXX_INT1_ITVBATDISCONNECT) { | ||
761 | ret = pm2xxx_charger_bat_disc_mngt(pm2, | ||
762 | PM2XXX_INT1_ITVBATDISCONNECT); | ||
763 | } | ||
764 | |||
765 | return ret; | ||
766 | } | ||
767 | |||
768 | static int pm2_int_reg1(struct pm2xxx_charger *pm2) | ||
769 | { | ||
770 | int ret = 0; | ||
771 | |||
772 | if (pm2->pm2_int[1] & | ||
773 | (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) { | ||
774 | dev_dbg(pm2->dev , "Main charger plugged\n"); | ||
775 | ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, pm2->pm2_int[1] & | ||
776 | (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)); | ||
777 | } | ||
778 | |||
779 | if (pm2->pm2_int[1] & | ||
780 | (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) { | ||
781 | dev_dbg(pm2->dev , "Main charger unplugged\n"); | ||
782 | ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, pm2->pm2_int[1] & | ||
783 | (PM2XXX_INT2_ITVPWR1UNPLUG | | ||
784 | PM2XXX_INT2_ITVPWR2UNPLUG)); | ||
785 | } | ||
786 | |||
787 | return ret; | ||
788 | } | ||
789 | |||
790 | static int pm2_int_reg2(struct pm2xxx_charger *pm2) | ||
791 | { | ||
792 | int ret = 0; | ||
793 | |||
794 | if (pm2->pm2_int[2] & PM2XXX_INT3_ITAUTOTIMEOUTWD) | ||
795 | ret = pm2xxx_charger_wd_exp_mngt(pm2, pm2->pm2_int[2]); | ||
796 | |||
797 | if (pm2->pm2_int[2] & (PM2XXX_INT3_ITCHPRECHARGEWD | | ||
798 | PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) { | ||
799 | dev_dbg(pm2->dev, | ||
800 | "Watchdog occured for precharge, CC and CV charge\n"); | ||
801 | } | ||
802 | |||
803 | return ret; | ||
804 | } | ||
805 | |||
806 | static int pm2_int_reg3(struct pm2xxx_charger *pm2) | ||
807 | { | ||
808 | int ret = 0; | ||
809 | |||
810 | if (pm2->pm2_int[3] & (PM2XXX_INT4_ITCHARGINGON)) { | ||
811 | dev_dbg(pm2->dev , | ||
812 | "chargind operation has started\n"); | ||
813 | } | ||
814 | |||
815 | if (pm2->pm2_int[3] & (PM2XXX_INT4_ITVRESUME)) { | ||
816 | dev_dbg(pm2->dev, | ||
817 | "battery discharged down to VResume threshold\n"); | ||
818 | } | ||
819 | |||
820 | if (pm2->pm2_int[3] & (PM2XXX_INT4_ITBATTFULL)) { | ||
821 | dev_dbg(pm2->dev , "battery fully detected\n"); | ||
822 | } | ||
823 | |||
824 | if (pm2->pm2_int[3] & (PM2XXX_INT4_ITCVPHASE)) { | ||
825 | dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n"); | ||
826 | } | ||
827 | |||
828 | if (pm2->pm2_int[3] & | ||
829 | (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) { | ||
830 | pm2->failure_case = VPWR_OVV; | ||
831 | ret = pm2xxx_charger_ovv_mngt(pm2, pm2->pm2_int[3] & | ||
832 | (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)); | ||
833 | dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n"); | ||
834 | } | ||
835 | |||
836 | if (pm2->pm2_int[3] & (PM2XXX_INT4_S_ITBATTEMPCOLD | | ||
837 | PM2XXX_INT4_S_ITBATTEMPHOT)) { | ||
838 | ret = pm2xxx_charger_batt_therm_mngt(pm2, | ||
839 | pm2->pm2_int[3] & (PM2XXX_INT4_S_ITBATTEMPCOLD | | ||
840 | PM2XXX_INT4_S_ITBATTEMPHOT)); | ||
841 | dev_dbg(pm2->dev, "BTEMP is too Low/High\n"); | ||
842 | } | ||
843 | |||
844 | return ret; | ||
845 | } | ||
846 | |||
847 | static int pm2_int_reg4(struct pm2xxx_charger *pm2) | ||
848 | { | ||
849 | int ret = 0; | ||
850 | |||
851 | if (pm2->pm2_int[4] & PM2XXX_INT5_ITVSYSTEMOVV) { | ||
852 | pm2->failure_case = VSYSTEM_OVV; | ||
853 | ret = pm2xxx_charger_ovv_mngt(pm2, pm2->pm2_int[4] & | ||
854 | PM2XXX_INT5_ITVSYSTEMOVV); | ||
855 | dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n"); | ||
856 | } | ||
857 | |||
858 | if (pm2->pm2_int[4] & (PM2XXX_INT5_ITTHERMALWARNINGFALL | | ||
859 | PM2XXX_INT5_ITTHERMALWARNINGRISE | | ||
860 | PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | | ||
861 | PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) { | ||
862 | dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n"); | ||
863 | ret = pm2xxx_charger_die_therm_mngt(pm2, pm2->pm2_int[4] & | ||
864 | (PM2XXX_INT5_ITTHERMALWARNINGFALL | | ||
865 | PM2XXX_INT5_ITTHERMALWARNINGRISE | | ||
866 | PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | | ||
867 | PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)); | ||
868 | } | ||
869 | |||
870 | return ret; | ||
871 | } | ||
872 | |||
873 | static int pm2_int_reg5(struct pm2xxx_charger *pm2) | ||
874 | { | ||
875 | |||
876 | if (pm2->pm2_int[5] | ||
877 | & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) { | ||
878 | dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n"); | ||
879 | } | ||
880 | |||
881 | if (pm2->pm2_int[5] & (PM2XXX_INT6_ITVPWR2VALIDRISE | | ||
882 | PM2XXX_INT6_ITVPWR1VALIDRISE | | ||
883 | PM2XXX_INT6_ITVPWR2VALIDFALL | | ||
884 | PM2XXX_INT6_ITVPWR1VALIDFALL)) { | ||
885 | dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n"); | ||
886 | } | ||
887 | |||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | static irqreturn_t pm2xxx_irq_int(int irq, void *data) | ||
892 | { | ||
893 | struct pm2xxx_charger *pm2 = data; | ||
894 | int ret, i; | ||
895 | |||
896 | for (i = 0; i < ARRAY_SIZE(pm2->pm2_int); i++) { | ||
897 | ret = pm2xxx_reg_read(pm2, pm2xxx_interrupt_registers[i], | ||
898 | &(pm2->pm2_int[i])); | ||
899 | } | ||
900 | |||
901 | pm2_int_reg0(pm2); | ||
902 | pm2_int_reg1(pm2); | ||
903 | pm2_int_reg2(pm2); | ||
904 | pm2_int_reg3(pm2); | ||
905 | pm2_int_reg4(pm2); | ||
906 | pm2_int_reg5(pm2); | ||
907 | |||
908 | return IRQ_HANDLED; | ||
909 | } | ||
910 | |||
911 | static int pm2xxx_charger_get_ac_voltage(struct pm2xxx_charger *pm2) | ||
912 | { | ||
913 | int vch = 0; | ||
914 | |||
915 | if (pm2->ac.charger_connected) { | ||
916 | vch = ab8500_gpadc_convert(pm2->gpadc, MAIN_CHARGER_V); | ||
917 | if (vch < 0) | ||
918 | dev_err(pm2->dev, "%s gpadc conv failed,\n", __func__); | ||
919 | } | ||
920 | |||
921 | return vch; | ||
922 | } | ||
923 | |||
924 | static int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2) | ||
925 | { | ||
926 | int ret = 0; | ||
927 | u8 val; | ||
928 | |||
929 | if (pm2->ac.charger_connected && pm2->ac.charger_online) { | ||
930 | |||
931 | ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &val); | ||
932 | if (ret < 0) { | ||
933 | dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); | ||
934 | goto out; | ||
935 | } | ||
936 | |||
937 | if (val & PM2XXX_INT4_S_ITCVPHASE) | ||
938 | ret = PM2XXX_CONST_VOLT; | ||
939 | else | ||
940 | ret = PM2XXX_CONST_CURR; | ||
941 | } | ||
942 | out: | ||
943 | return ret; | ||
944 | } | ||
945 | |||
946 | static int pm2xxx_charger_get_ac_current(struct pm2xxx_charger *pm2) | ||
947 | { | ||
948 | int ich = 0; | ||
949 | |||
950 | if (pm2->ac.charger_online) { | ||
951 | ich = ab8500_gpadc_convert(pm2->gpadc, MAIN_CHARGER_C); | ||
952 | if (ich < 0) | ||
953 | dev_err(pm2->dev, "%s gpadc conv failed\n", __func__); | ||
954 | } | ||
955 | |||
956 | return ich; | ||
957 | } | ||
958 | |||
959 | static int pm2xxx_current_to_regval(int curr) | ||
960 | { | ||
961 | int i; | ||
962 | |||
963 | if (curr < pm2xxx_charger_current_map[0]) | ||
964 | return 0; | ||
965 | |||
966 | for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) { | ||
967 | if (curr < pm2xxx_charger_current_map[i]) | ||
968 | return (i - 1); | ||
969 | } | ||
970 | |||
971 | i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1; | ||
972 | if (curr == pm2xxx_charger_current_map[i]) | ||
973 | return i; | ||
974 | else | ||
975 | return -EINVAL; | ||
976 | } | ||
977 | |||
978 | static int pm2xxx_voltage_to_regval(int curr) | ||
979 | { | ||
980 | int i; | ||
981 | |||
982 | if (curr < pm2xxx_charger_voltage_map[0]) | ||
983 | return 0; | ||
984 | |||
985 | for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) { | ||
986 | if (curr < pm2xxx_charger_voltage_map[i]) | ||
987 | return i - 1; | ||
988 | } | ||
989 | |||
990 | i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1; | ||
991 | if (curr == pm2xxx_charger_voltage_map[i]) | ||
992 | return i; | ||
993 | else | ||
994 | return -EINVAL; | ||
995 | } | ||
996 | |||
997 | static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger, | ||
998 | int ich_out) | ||
999 | { | ||
1000 | int ret; | ||
1001 | int curr_index; | ||
1002 | struct pm2xxx_charger *pm2; | ||
1003 | u8 val; | ||
1004 | |||
1005 | if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) | ||
1006 | pm2 = to_pm2xxx_charger_ac_device_info(charger); | ||
1007 | else | ||
1008 | return -ENXIO; | ||
1009 | |||
1010 | curr_index = pm2xxx_current_to_regval(ich_out); | ||
1011 | if (curr_index < 0) { | ||
1012 | dev_err(pm2->dev, | ||
1013 | "Charger current too high: charging not started\n"); | ||
1014 | return -ENXIO; | ||
1015 | } | ||
1016 | |||
1017 | ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); | ||
1018 | if (ret >= 0) { | ||
1019 | val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; | ||
1020 | val |= curr_index; | ||
1021 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); | ||
1022 | if (ret < 0) { | ||
1023 | dev_err(pm2->dev, | ||
1024 | "%s write failed\n", __func__); | ||
1025 | } | ||
1026 | } | ||
1027 | else | ||
1028 | dev_err(pm2->dev, "%s read failed\n", __func__); | ||
1029 | |||
1030 | return ret; | ||
1031 | } | ||
1032 | |||
1033 | static int pm2xxx_charger_ac_get_property(struct power_supply *psy, | ||
1034 | enum power_supply_property psp, | ||
1035 | union power_supply_propval *val) | ||
1036 | { | ||
1037 | struct pm2xxx_charger *pm2; | ||
1038 | |||
1039 | pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy)); | ||
1040 | |||
1041 | switch (psp) { | ||
1042 | case POWER_SUPPLY_PROP_HEALTH: | ||
1043 | if (pm2->flags.mainextchnotok) | ||
1044 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
1045 | else if (pm2->ac.wd_expired) | ||
1046 | val->intval = POWER_SUPPLY_HEALTH_DEAD; | ||
1047 | else if (pm2->flags.main_thermal_prot) | ||
1048 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
1049 | else | ||
1050 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
1051 | break; | ||
1052 | case POWER_SUPPLY_PROP_ONLINE: | ||
1053 | val->intval = pm2->ac.charger_online; | ||
1054 | break; | ||
1055 | case POWER_SUPPLY_PROP_PRESENT: | ||
1056 | val->intval = pm2->ac.charger_connected; | ||
1057 | break; | ||
1058 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
1059 | pm2->ac.charger_voltage = pm2xxx_charger_get_ac_voltage(pm2); | ||
1060 | val->intval = pm2->ac.charger_voltage * 1000; | ||
1061 | break; | ||
1062 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | ||
1063 | pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2); | ||
1064 | val->intval = pm2->ac.cv_active; | ||
1065 | break; | ||
1066 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
1067 | val->intval = pm2xxx_charger_get_ac_current(pm2) * 1000; | ||
1068 | break; | ||
1069 | default: | ||
1070 | return -EINVAL; | ||
1071 | } | ||
1072 | return 0; | ||
1073 | } | ||
1074 | |||
1075 | static int pm2xxx_charging_init(struct pm2xxx_charger *pm2) | ||
1076 | { | ||
1077 | int ret = 0; | ||
1078 | |||
1079 | /* enable CC and CV watchdog */ | ||
1080 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3, | ||
1081 | (PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN)); | ||
1082 | if( ret < 0) | ||
1083 | return ret; | ||
1084 | |||
1085 | /* enable precharge watchdog */ | ||
1086 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4, | ||
1087 | PM2XXX_CH_WD_PRECH_PHASE_60MIN); | ||
1088 | |||
1089 | return ret; | ||
1090 | } | ||
1091 | |||
1092 | static int pm2xxx_charger_ac_en(struct ux500_charger *charger, | ||
1093 | int enable, int vset, int iset) | ||
1094 | { | ||
1095 | int ret; | ||
1096 | int volt_index; | ||
1097 | int curr_index; | ||
1098 | u8 val; | ||
1099 | |||
1100 | struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger); | ||
1101 | |||
1102 | if (enable) { | ||
1103 | if (!pm2->ac.charger_connected) { | ||
1104 | dev_dbg(pm2->dev, "AC charger not connected\n"); | ||
1105 | return -ENXIO; | ||
1106 | } | ||
1107 | |||
1108 | dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset); | ||
1109 | if (!pm2->vddadc_en_ac) { | ||
1110 | regulator_enable(pm2->regu); | ||
1111 | pm2->vddadc_en_ac = true; | ||
1112 | } | ||
1113 | |||
1114 | ret = pm2xxx_charging_init(pm2); | ||
1115 | if (ret < 0) { | ||
1116 | dev_err(pm2->dev, "%s charging init failed\n", | ||
1117 | __func__); | ||
1118 | goto error_occured; | ||
1119 | } | ||
1120 | |||
1121 | volt_index = pm2xxx_voltage_to_regval(vset); | ||
1122 | curr_index = pm2xxx_current_to_regval(iset); | ||
1123 | |||
1124 | if (volt_index < 0 || curr_index < 0) { | ||
1125 | dev_err(pm2->dev, | ||
1126 | "Charger voltage or current too high, " | ||
1127 | "charging not started\n"); | ||
1128 | return -ENXIO; | ||
1129 | } | ||
1130 | |||
1131 | ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val); | ||
1132 | if (ret >= 0) { | ||
1133 | val &= ~PM2XXX_CH_VOLT_MASK; | ||
1134 | val |= volt_index; | ||
1135 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, val); | ||
1136 | |||
1137 | if (ret < 0) { | ||
1138 | dev_err(pm2->dev, | ||
1139 | "%s write failed\n", __func__); | ||
1140 | goto error_occured; | ||
1141 | } | ||
1142 | else | ||
1143 | dev_err(pm2->dev, "%s read failed\n", __func__); | ||
1144 | } | ||
1145 | |||
1146 | ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); | ||
1147 | if (ret >= 0) { | ||
1148 | val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; | ||
1149 | val |= curr_index; | ||
1150 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); | ||
1151 | if (ret < 0) { | ||
1152 | dev_err(pm2->dev, | ||
1153 | "%s write failed\n", __func__); | ||
1154 | goto error_occured; | ||
1155 | } | ||
1156 | else | ||
1157 | dev_err(pm2->dev, "%s read failed\n", __func__); | ||
1158 | } | ||
1159 | |||
1160 | if (!pm2->bat->enable_overshoot) { | ||
1161 | ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val); | ||
1162 | if (ret >= 0) { | ||
1163 | val |= PM2XXX_ANTI_OVERSHOOT_EN; | ||
1164 | ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, | ||
1165 | val); | ||
1166 | if (ret < 0){ | ||
1167 | dev_err(pm2->dev, "%s write failed\n", | ||
1168 | __func__); | ||
1169 | goto error_occured; | ||
1170 | } | ||
1171 | } | ||
1172 | else | ||
1173 | dev_err(pm2->dev, "%s read failed\n", __func__); | ||
1174 | } | ||
1175 | |||
1176 | ret = pm2xxx_charging_enable_mngt(pm2); | ||
1177 | if (ret) { | ||
1178 | dev_err(pm2->dev, "%s write failed\n", __func__); | ||
1179 | goto error_occured; | ||
1180 | } | ||
1181 | |||
1182 | pm2->ac.charger_online = 1; | ||
1183 | } else { | ||
1184 | pm2->ac.charger_online = 0; | ||
1185 | pm2->ac.wd_expired = false; | ||
1186 | |||
1187 | /* Disable regulator if enabled */ | ||
1188 | if (pm2->vddadc_en_ac) { | ||
1189 | regulator_disable(pm2->regu); | ||
1190 | pm2->vddadc_en_ac = false; | ||
1191 | } | ||
1192 | |||
1193 | ret = pm2xxx_charging_disable_mngt(pm2); | ||
1194 | if (ret) { | ||
1195 | dev_err(pm2->dev, "%s write failed\n", __func__); | ||
1196 | return ret; | ||
1197 | } | ||
1198 | |||
1199 | dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n"); | ||
1200 | } | ||
1201 | power_supply_changed(&pm2->ac_chg.psy); | ||
1202 | |||
1203 | error_occured: | ||
1204 | return ret; | ||
1205 | } | ||
1206 | |||
1207 | static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger) | ||
1208 | { | ||
1209 | int ret; | ||
1210 | struct pm2xxx_charger *pm2; | ||
1211 | |||
1212 | if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) | ||
1213 | pm2 = to_pm2xxx_charger_ac_device_info(charger); | ||
1214 | else | ||
1215 | return -ENXIO; | ||
1216 | |||
1217 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER); | ||
1218 | if (ret) | ||
1219 | dev_err(pm2->dev, "Failed to kick WD!\n"); | ||
1220 | |||
1221 | return ret; | ||
1222 | } | ||
1223 | |||
1224 | static void pm2xxx_charger_ac_work(struct work_struct *work) | ||
1225 | { | ||
1226 | struct pm2xxx_charger *pm2 = container_of(work, | ||
1227 | struct pm2xxx_charger, ac_work); | ||
1228 | |||
1229 | |||
1230 | power_supply_changed(&pm2->ac_chg.psy); | ||
1231 | sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present"); | ||
1232 | }; | ||
1233 | |||
1234 | static void pm2xxx_charger_check_main_thermal_prot_work( | ||
1235 | struct work_struct *work) | ||
1236 | { | ||
1237 | }; | ||
1238 | |||
1239 | static struct pm2xxx_irq pm2xxx_charger_irq[] = { | ||
1240 | {"PM2XXX_IRQ_INT", pm2xxx_irq_int}, | ||
1241 | }; | ||
1242 | |||
1243 | static int pm2xxx_wall_charger_resume(struct i2c_client *i2c_client) | ||
1244 | { | ||
1245 | return 0; | ||
1246 | } | ||
1247 | |||
1248 | static int pm2xxx_wall_charger_suspend(struct i2c_client *i2c_client, | ||
1249 | pm_message_t state) | ||
1250 | { | ||
1251 | return 0; | ||
1252 | } | ||
1253 | |||
1254 | static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, | ||
1255 | const struct i2c_device_id *id) | ||
1256 | { | ||
1257 | struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data; | ||
1258 | struct pm2xxx_charger *pm2; | ||
1259 | int ret = 0; | ||
1260 | u8 val; | ||
1261 | |||
1262 | pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL); | ||
1263 | if (!pm2) { | ||
1264 | dev_err(pm2->dev, "pm2xxx_charger allocation failed\n"); | ||
1265 | return -ENOMEM; | ||
1266 | } | ||
1267 | |||
1268 | /* get parent data */ | ||
1269 | pm2->dev = &i2c_client->dev; | ||
1270 | pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); | ||
1271 | |||
1272 | /* get charger spcific platform data */ | ||
1273 | if (!pl_data->wall_charger) { | ||
1274 | dev_err(pm2->dev, "no charger platform data supplied\n"); | ||
1275 | ret = -EINVAL; | ||
1276 | goto free_device_info; | ||
1277 | } | ||
1278 | |||
1279 | pm2->pdata = pl_data->wall_charger; | ||
1280 | |||
1281 | /* get battery specific platform data */ | ||
1282 | if (!pl_data->battery) { | ||
1283 | dev_err(pm2->dev, "no battery platform data supplied\n"); | ||
1284 | ret = -EINVAL; | ||
1285 | goto free_device_info; | ||
1286 | } | ||
1287 | |||
1288 | pm2->bat = pl_data->battery; | ||
1289 | |||
1290 | if (!i2c_check_functionality(i2c_client->adapter, | ||
1291 | I2C_FUNC_SMBUS_BYTE_DATA | | ||
1292 | I2C_FUNC_SMBUS_READ_WORD_DATA)) { | ||
1293 | ret = -ENODEV; | ||
1294 | dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n"); | ||
1295 | goto free_device_info; | ||
1296 | } | ||
1297 | |||
1298 | pm2->config.pm2xxx_i2c = i2c_client; | ||
1299 | pm2->config.pm2xxx_id = (struct i2c_device_id *) id; | ||
1300 | i2c_set_clientdata(i2c_client, pm2); | ||
1301 | |||
1302 | /* AC supply */ | ||
1303 | /* power_supply base class */ | ||
1304 | pm2->ac_chg.psy.name = pm2->pdata->label; | ||
1305 | pm2->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS; | ||
1306 | pm2->ac_chg.psy.properties = pm2xxx_charger_ac_props; | ||
1307 | pm2->ac_chg.psy.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props); | ||
1308 | pm2->ac_chg.psy.get_property = pm2xxx_charger_ac_get_property; | ||
1309 | pm2->ac_chg.psy.supplied_to = pm2->pdata->supplied_to; | ||
1310 | pm2->ac_chg.psy.num_supplicants = pm2->pdata->num_supplicants; | ||
1311 | /* pm2xxx_charger sub-class */ | ||
1312 | pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en; | ||
1313 | pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick; | ||
1314 | pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current; | ||
1315 | pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[ | ||
1316 | ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1]; | ||
1317 | pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[ | ||
1318 | ARRAY_SIZE(pm2xxx_charger_current_map) - 1]; | ||
1319 | |||
1320 | /* Create a work queue for the charger */ | ||
1321 | pm2->charger_wq = | ||
1322 | create_singlethread_workqueue("pm2xxx_charger_wq"); | ||
1323 | if (pm2->charger_wq == NULL) { | ||
1324 | dev_err(pm2->dev, "failed to create work queue\n"); | ||
1325 | goto free_device_info; | ||
1326 | } | ||
1327 | |||
1328 | /* Init work for charger detection */ | ||
1329 | INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work); | ||
1330 | |||
1331 | /* Init work for checking HW status */ | ||
1332 | INIT_WORK(&pm2->check_main_thermal_prot_work, | ||
1333 | pm2xxx_charger_check_main_thermal_prot_work); | ||
1334 | |||
1335 | /* | ||
1336 | * VDD ADC supply needs to be enabled from this driver when there | ||
1337 | * is a charger connected to avoid erroneous BTEMP_HIGH/LOW | ||
1338 | * interrupts during charging | ||
1339 | */ | ||
1340 | pm2->regu = regulator_get(pm2->dev, "vddadc"); | ||
1341 | if (IS_ERR(pm2->regu)) { | ||
1342 | ret = PTR_ERR(pm2->regu); | ||
1343 | dev_err(pm2->dev, "failed to get vddadc regulator\n"); | ||
1344 | goto free_charger_wq; | ||
1345 | } | ||
1346 | |||
1347 | /* Register AC charger class */ | ||
1348 | ret = power_supply_register(pm2->dev, &pm2->ac_chg.psy); | ||
1349 | if (ret) { | ||
1350 | dev_err(pm2->dev, "failed to register AC charger\n"); | ||
1351 | goto free_regulator; | ||
1352 | } | ||
1353 | |||
1354 | /* Register interrupts */ | ||
1355 | ret = request_threaded_irq(pm2->pdata->irq_number, NULL, | ||
1356 | pm2xxx_charger_irq[0].isr, | ||
1357 | pm2->pdata->irq_type, | ||
1358 | pm2xxx_charger_irq[0].name, pm2); | ||
1359 | |||
1360 | if (ret != 0) { | ||
1361 | dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n", | ||
1362 | pm2xxx_charger_irq[0].name, pm2->pdata->irq_number, ret); | ||
1363 | goto unregister_pm2xxx_charger; | ||
1364 | } | ||
1365 | |||
1366 | /* | ||
1367 | * I2C Read/Write will fail, if AC adaptor is not connected. | ||
1368 | * fix the charger detection mechanism. | ||
1369 | */ | ||
1370 | ret = pm2xxx_charger_detection(pm2, &val); | ||
1371 | |||
1372 | if ((ret == 0) && val) { | ||
1373 | pm2->ac.charger_connected = 1; | ||
1374 | pm2->ac_conn = true; | ||
1375 | power_supply_changed(&pm2->ac_chg.psy); | ||
1376 | sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present"); | ||
1377 | } | ||
1378 | |||
1379 | return 0; | ||
1380 | |||
1381 | unregister_pm2xxx_charger: | ||
1382 | /* unregister power supply */ | ||
1383 | power_supply_unregister(&pm2->ac_chg.psy); | ||
1384 | free_regulator: | ||
1385 | /* disable the regulator */ | ||
1386 | regulator_put(pm2->regu); | ||
1387 | free_charger_wq: | ||
1388 | destroy_workqueue(pm2->charger_wq); | ||
1389 | free_device_info: | ||
1390 | kfree(pm2); | ||
1391 | return ret; | ||
1392 | } | ||
1393 | |||
1394 | static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client) | ||
1395 | { | ||
1396 | struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client); | ||
1397 | |||
1398 | /* Disable AC charging */ | ||
1399 | pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0); | ||
1400 | |||
1401 | /* Disable interrupts */ | ||
1402 | free_irq(pm2->pdata->irq_number, pm2); | ||
1403 | |||
1404 | /* Delete the work queue */ | ||
1405 | destroy_workqueue(pm2->charger_wq); | ||
1406 | |||
1407 | flush_scheduled_work(); | ||
1408 | |||
1409 | /* disable the regulator */ | ||
1410 | regulator_put(pm2->regu); | ||
1411 | |||
1412 | power_supply_unregister(&pm2->ac_chg.psy); | ||
1413 | |||
1414 | kfree(pm2); | ||
1415 | |||
1416 | return 0; | ||
1417 | } | ||
1418 | |||
1419 | static const struct i2c_device_id pm2xxx_id[] = { | ||
1420 | { "pm2301", 0 }, | ||
1421 | { } | ||
1422 | }; | ||
1423 | |||
1424 | MODULE_DEVICE_TABLE(i2c, pm2xxx_id); | ||
1425 | |||
1426 | static struct i2c_driver pm2xxx_charger_driver = { | ||
1427 | .probe = pm2xxx_wall_charger_probe, | ||
1428 | .remove = __devexit_p(pm2xxx_wall_charger_remove), | ||
1429 | .suspend = pm2xxx_wall_charger_suspend, | ||
1430 | .resume = pm2xxx_wall_charger_resume, | ||
1431 | .driver = { | ||
1432 | .name = "pm2xxx-wall_charger", | ||
1433 | .owner = THIS_MODULE, | ||
1434 | }, | ||
1435 | .id_table = pm2xxx_id, | ||
1436 | }; | ||
1437 | |||
1438 | static int __init pm2xxx_charger_init(void) | ||
1439 | { | ||
1440 | return i2c_add_driver(&pm2xxx_charger_driver); | ||
1441 | } | ||
1442 | |||
1443 | static void __exit pm2xxx_charger_exit(void) | ||
1444 | { | ||
1445 | i2c_del_driver(&pm2xxx_charger_driver); | ||
1446 | } | ||
1447 | |||
1448 | subsys_initcall_sync(pm2xxx_charger_init); | ||
1449 | module_exit(pm2xxx_charger_exit); | ||
1450 | |||
1451 | MODULE_LICENSE("GPL v2"); | ||
1452 | MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay"); | ||
1453 | MODULE_ALIAS("platform:pm2xxx-charger"); | ||
1454 | MODULE_DESCRIPTION("PM2xxx charger management driver"); | ||
1455 | |||