upload tizen1.0 source
[kernel/linux-2.6.36.git] / arch / arm / mach-s5pv310 / charger-u1.c
1 /*
2  * linux/arch/arm/mach-s5pv310/charger-u1.c
3  * COPYRIGHT(C) 2011
4  * MyungJoo Ham <myungjoo.ham@samsung.com>
5  *
6  * U1 Board Charger Support with Charger-Manager Framework
7  *
8  */
9
10 #include <asm/io.h>
11
12 #include <linux/err.h>
13 #include <linux/platform_device.h>
14 #include <linux/ntc.h>
15 #include <linux/rtc/rtc-s3c.h>
16 #include <linux/regulator/consumer.h>
17 #include <linux/power/charger-manager.h>
18 #include <linux/power/max17042_battery.h>
19 #include <linux/mfd/max8997.h>
20
21 #include <plat/adc.h>
22 #include <plat/pm.h>
23
24 #include <mach/regs-pmu.h>
25 #include <mach/irqs.h>
26
27 #include "../../../fs/sysfs/sysfs.h"
28
29 /* Temperatures in milli-centigrade */
30 #define SECBATTSPEC_TEMP_HIGH           (65 * 1000)
31 #define SECBATTSPEC_TEMP_HIGH_REC       (43 * 1000)
32 #define SECBATTSPEC_TEMP_LOW            (-3 * 1000)
33 #define SECBATTSPEC_TEMP_LOW_REC        (0 * 1000)
34
35 #define SECBATT_CHANGE_MODE_TEMP        (38 * 1000)
36 #define SECBATT_CHANGE_MODE_TEMP_REC    (35 * 1000)
37
38 #ifdef CONFIG_SENSORS_NTC_THERMISTOR
39 static unsigned int read_thermistor_uV(void)
40 {
41         struct s3c_adc_request req = {
42                 .list           = NULL,
43                 .callback       = NULL,
44                 .param          = 0,
45                 .channel        = 7, /* XADCAIN_7 for LCD in u1 */
46         };
47
48         return s3c_adc_convert_uV(s3c_adc_get(&req));
49 }
50
51 static unsigned int read_thermistor_uV_pmic(void)
52 {
53         struct s3c_adc_request req = {
54                 .list           = NULL,
55                 .callback       = NULL,
56                 .param          = 0,
57                 .channel        = 6, /* XADCAIN_6 for PMIC in u1 */
58         };
59
60         return s3c_adc_convert_uV(s3c_adc_get(&req));
61 }
62
63 static struct ntc_thermistor_platform_data ncp15wb473_pdata = {
64         .read_uV        = read_thermistor_uV,
65         .pullup_uV      = 3300000, /* VADC_3.3V_C210 */
66         .pullup_ohm     = 100000, /* R613 in SLP 7 0105 */
67         .pulldown_ohm   = 100000, /* R615 in SLP 7 0105 */
68         .connect        = NTC_CONNECTED_GROUND,
69 };
70
71 static struct ntc_thermistor_platform_data ncp15wb473_pdata_pmic = {
72         .read_uV        = read_thermistor_uV_pmic,
73         .pullup_uV      = 3300000, /* VADC_3.3V_C210 */
74         .pullup_ohm     = 100000, /* R613 in SLP 7 0105 */
75         .pulldown_ohm   = 100000, /* R615 in SLP 7 0105 */
76         .connect        = NTC_CONNECTED_GROUND,
77 };
78
79 struct platform_device u1_ncp15wb473_thermistor;
80 static const char *thermistor_dirent = "temp1_input";
81 static struct device_attribute *thermistor_attr;
82 static int __read_thermistor_mC(void)
83 {
84         int err = 0;
85         int mC = 25000;
86         char buf[32];
87         int count;
88
89         if (thermistor_attr == NULL) {
90                 struct kobject *ntc;
91                 struct sysfs_dirent *ntc_d;
92
93                 ntc = &u1_ncp15wb473_thermistor.dev.kobj;
94                 ntc_d = sysfs_get_dirent(ntc->sd,
95                                         get_ktype(ntc)->namespace(ntc),
96                                         thermistor_dirent);
97                 if (!ntc_d || sysfs_type(ntc_d) != SYSFS_KOBJ_ATTR) {
98                         err = -ENODEV;
99                         dev_err(&u1_ncp15wb473_thermistor.dev, "Cannot init"
100                                         "ialize ncp15wb473's dirent info.\n");
101                         if (ntc_d)
102                                 sysfs_put(ntc_d);
103                         goto out_seterr;
104                 }
105                 thermistor_attr = container_of(ntc_d->s_attr.attr,
106                                                 struct device_attribute, attr);
107
108                 sysfs_put(ntc_d);
109         }
110
111         if (IS_ERR(thermistor_attr)) {
112                 dev_warn(&u1_ncp15wb473_thermistor.dev, "Cannot read.\n");
113                 goto out;
114         }
115
116         count = thermistor_attr->show(&u1_ncp15wb473_thermistor.dev,
117                                         thermistor_attr, buf);
118         WARN_ON(count >= 31);
119         sscanf(buf, "%d", &mC);
120
121         goto out;
122 out_seterr:
123         thermistor_attr = ERR_PTR(err);
124 out:
125         return mC;
126 }
127 #else /* CONFIG_SENSORS_NTC_THERMISTOR */
128 static int __read_thermistor_mC(void)
129 {
130         return 25000; /* 25 mili-Centigrade */
131 }
132 #endif /* CONFIG_SENSORS_NTC_THERMISTOR */
133
134 static bool s3c_wksrc_rtc_alarm(void)
135 {
136         u32 reg = s3c_suspend_wakeup_stat & S5P_WAKEUP_STAT_WKSRC_MASK;
137
138         if ((reg & S5P_WAKEUP_STAT_RTCALARM) &&
139             !(reg & ~S5P_WAKEUP_STAT_RTCALARM))
140                 return true; /* yes, it is */
141
142         return false;
143 }
144
145 enum temp_stat { TEMP_OK = 0, TEMP_HOT = 1, TEMP_COLD = -1 };
146
147 static int u1_change_charge_mode(enum temp_stat state, int mC)
148 {
149         static struct regulator *regulator;
150         int ret;
151
152         if (regulator == NULL) {
153                 regulator = regulator_get(NULL, "vinchg2_mach");
154                 if (IS_ERR_OR_NULL(regulator)) {
155                         pr_err("%s : failed to get regulator\n", __func__);
156                         regulator = NULL;
157                         ret = -ENODEV;
158                         goto out;
159                 }
160         }
161
162         if (state == TEMP_OK) {
163                 /* USB mode */
164                 if (mC >= SECBATT_CHANGE_MODE_TEMP)
165                         regulator_set_current_limit(regulator, 395000, 450000);
166
167                 /* TA mode */
168                 if (mC <= SECBATT_CHANGE_MODE_TEMP_REC)
169                         regulator_set_current_limit(regulator, 600000, 650000);
170         }
171
172         ret = 0;
173 out:
174         return ret;
175 }
176
177 static int u1_thermistor_ck(int *mC)
178 {
179         static enum temp_stat state = TEMP_OK;
180
181         *mC = __read_thermistor_mC();
182         switch (state) {
183         case TEMP_OK:
184                 if (*mC >= SECBATTSPEC_TEMP_HIGH)
185                         state = TEMP_HOT;
186                 else if (*mC <= SECBATTSPEC_TEMP_LOW)
187                         state = TEMP_COLD;
188                 break;
189         case TEMP_HOT:
190                 if (*mC <= SECBATTSPEC_TEMP_LOW)
191                         state = TEMP_COLD;
192                 else if (*mC < SECBATTSPEC_TEMP_HIGH_REC)
193                         state = TEMP_OK;
194                 break;
195         case TEMP_COLD:
196                 if (*mC >= SECBATTSPEC_TEMP_HIGH)
197                         state = TEMP_HOT;
198                 else if (*mC > SECBATTSPEC_TEMP_LOW_REC)
199                         state = TEMP_OK;
200         default:
201                 pr_err("%s has invalid state %d\n", __func__, state);
202         }
203
204         u1_change_charge_mode(state, *mC);
205
206         return state;
207 }
208
209 static char *u1_charger_stats[] = {
210 #if defined(CONFIG_CHARGERCTRL_MAX8922)
211         "max8922-charger",
212 #endif
213 #if defined(CONFIG_CHARGERCTRL_MAX8997)
214         "max8997_pmic",
215 #endif
216         NULL };
217
218 static struct regulator_bulk_data u1_chargers[] = {
219         { .supply = "vinchg2", },
220 };
221 static struct charger_irq u1_chg_irqs[] = {
222         { IRQ_PMIC_BASE + MAX8997_PMICIRQ_TOPOFFR, 0, CM_IRQ_BATT_FULL,
223           true, "Batt Full" },
224         { IRQ_FUEL_BASE + MAX17042_IRQ_Battery_Removed, 0, CM_IRQ_BATT_OUT,
225           true, "Batt Out" },
226         { IRQ_PMIC_BASE + MAX8997_PMICIRQ_CHGRSTF, 0, CM_IRQ_CHG_START_STOP,
227           true, "CHG STOP" },
228         { IRQ_PMIC_BASE + MAX8997_PMICIRQ_MBCHGTMEXPD, 0, CM_IRQ_OTHERS,
229           true, "CHG TIMEOUT" },
230         { IRQ_PMIC_BASE + MAX8997_PMICIRQ_DCINOVP, 0, CM_IRQ_OTHERS,
231           true, "CHG OVERVOLT" },
232         { IRQ_PMIC_BASE + MAX8997_PMICIRQ_CHGINS, 0, CM_IRQ_EXT_PWR_IN_OUT,
233           true, "CHG PWR Insert" },
234         { IRQ_PMIC_BASE + MAX8997_PMICIRQ_CHGRM, 0, CM_IRQ_EXT_PWR_IN_OUT,
235           true, "CHG PWR Remove" },
236         { 0, 0, 0, false, NULL },
237
238 };
239 static struct charger_desc u1_charger_desc = {
240         .psy_name               = "battery",
241         .polling_interval_ms    = 30000,
242         .polling_mode           = CM_POLL_EXTERNAL_POWER_ONLY,
243         .fullbatt_vchkdrop_ms   = 30000,
244         .fullbatt_vchkdrop_uV   = 50000,
245         .fullbatt_uV            = 4200000,
246         .battery_present        = CM_CHARGER_STAT,
247         .psy_charger_stat       = u1_charger_stats,
248         .charger_regulators     = u1_chargers,
249         .num_charger_regulators = ARRAY_SIZE(u1_chargers),
250         .psy_fuel_gauge         = "max17042_battery",
251         .irqs                   = u1_chg_irqs,
252
253         .is_temperature_error   = u1_thermistor_ck,
254         .measure_ambient_temp   = true,
255         .measure_battery_temp   = true,
256         .soc_margin             = 0,
257 };
258
259 struct charger_global_desc u1_charger_g_desc = {
260         .rtc = "rtc1",
261         .is_rtc_only_wakeup_reason = s3c_wksrc_rtc_alarm,
262         .assume_timer_stops_in_suspend  = true,
263 };
264
265 #ifdef CONFIG_SENSORS_NTC_THERMISTOR
266 struct platform_device u1_ncp15wb473_thermistor = {
267         .name                   = "ncp15wb473",
268         .id                             = 0,
269         .dev                    = {
270                 .platform_data = &ncp15wb473_pdata,
271         },
272 };
273
274 struct platform_device u1_ncp15wb473_thermistor_pmic = {
275         .name                   = "ncp15wb473",
276         .id                             = 1,
277         .dev                    = {
278                 .platform_data = &ncp15wb473_pdata_pmic,
279         },
280 };
281
282 #endif
283
284 struct platform_device u1_charger_manager = {
285         .name                   = "charger-manager",
286         .dev                    = {
287                 .platform_data = &u1_charger_desc,
288         },
289 };