create firmware image for the Ubiquiti LS-SR71 board
[openwrt/openwrt.git] / target / linux / s3c24xx / patches-2.6.24 / 1170-introduce-charging-led-behaviour.patch.patch
1 From bf74b8891e80dfceb72002f12c7d3c9978631dc0 Mon Sep 17 00:00:00 2001
2 From: Andy Green <andy@openmoko.com>
3 Date: Wed, 2 Jul 2008 22:37:47 +0100
4 Subject: [PATCH] introduce-charging-led-behaviour.patch
5
6 Creates a new behaviour requested by Will that the red LED on GTA02
7 is lit during battery charging.and goes out when the battery is full.
8
9 This is done by leveraging the PMU interrupts, but in one scenario
10 there is no interrupt that occurs, when the battery is replaced after
11 being removed with the USB power in all the while. So a sleepy work
12 function is started under those circumstances to watch for battery
13 reinsertion or USB cable pull.
14
15 100mA limit was not being observed under some conditions so this was
16 fixed and tested with a USB cable with D+/D- disconnected. 1A
17 charger behaviour was also tested.
18
19 Showing the charging action exposes some inconsistency in pcf50633
20 charging action. If your battery is nearly full, it will keep
21 charging it at decreasing current even after it thinks it is at
22 100% capacity for a long while. But if you pull that same battery
23 and re-insert it, the charger state machine in pcf50633 believe it is
24 full and won't charge it.
25
26 Signed-off-by: Andy Green <andy@openmoko.com>
27 ---
28 arch/arm/mach-s3c2440/mach-gta02.c | 8 ++
29 drivers/i2c/chips/pcf50633.c | 212 ++++++++++++++++++++++++++++++++++--
30 include/linux/pcf506xx.h | 2 +
31 3 files changed, 214 insertions(+), 8 deletions(-)
32
33 diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
34 index 601f7bc..d7882ea 100644
35 --- a/arch/arm/mach-s3c2440/mach-gta02.c
36 +++ b/arch/arm/mach-s3c2440/mach-gta02.c
37 @@ -437,6 +437,14 @@ static int pmu_callback(struct device *dev, unsigned int feature,
38 case PMU_EVT_USB_REMOVE:
39 pcf50633_charge_enable(pcf50633_global, 0);
40 break;
41 + case PMU_EVT_CHARGER_IDLE:
42 + /* printk(KERN_ERR"PMU_EVT_CHARGER_IDLE\n"); */
43 + neo1973_gpb_setpin(GTA02_GPIO_AUX_LED, 0);
44 + break;
45 + case PMU_EVT_CHARGER_ACTIVE:
46 + /* printk(KERN_ERR"PMU_EVT_CHARGER_ACTIVE\n"); */
47 + neo1973_gpb_setpin(GTA02_GPIO_AUX_LED, 1);
48 + break;
49 default:
50 break;
51 }
52 diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
53 index 2878baa..2ed6dc0 100644
54 --- a/drivers/i2c/chips/pcf50633.c
55 +++ b/drivers/i2c/chips/pcf50633.c
56 @@ -47,6 +47,7 @@
57 #include <linux/platform_device.h>
58 #include <linux/pcf50633.h>
59 #include <linux/apm-emulation.h>
60 +#include <linux/jiffies.h>
61
62 #include <asm/mach-types.h>
63 #include <asm/arch/gta02.h>
64 @@ -121,8 +122,23 @@ struct pcf50633_data {
65 int onkey_seconds;
66 int irq;
67 int have_been_suspended;
68 + int usb_removal_count;
69 unsigned char pcfirq_resume[5];
70
71 + /* if he pulls battery while charging, we notice that and correctly
72 + * report that the charger is idle. But there is no interrupt that
73 + * fires if he puts a battery back in and charging resumes. So when
74 + * the battery is pulled, we run this work function looking for
75 + * either charger resumption or USB cable pull
76 + */
77 + struct mutex working_lock_nobat;
78 + struct work_struct work_nobat;
79 + int working_nobat;
80 + int usb_removal_count_nobat;
81 + int jiffies_last_bat_ins;
82 +
83 + int last_curlim_set;
84 +
85 int coldplug_done; /* cleared by probe, set by first work service */
86 int flag_bat_voltage_read; /* ipc to /sys batt voltage read func */
87
88 @@ -259,6 +275,8 @@ static u_int16_t async_adc_complete(struct pcf50633_data *pcf)
89 (__reg_read(pcf, PCF50633_REG_ADCS3) &
90 PCF50633_ADCS3_ADCDAT1L_MASK);
91
92 + DEBUGPC("adc result = %d\n", ret);
93 +
94 return ret;
95 }
96
97 @@ -512,8 +530,7 @@ static void configure_pmu_for_charger(struct pcf50633_data *pcf,
98 {
99 switch (type) {
100 case CHARGER_TYPE_NONE:
101 - __reg_write(pcf, PCF50633_REG_MBCC7,
102 - PCF50633_MBCC7_USB_SUSPEND);
103 + pcf50633_usb_curlim_set(pcf, 0);
104 break;
105 /*
106 * the PCF50633 has a feature that it will supply only excess current
107 @@ -521,10 +538,19 @@ static void configure_pmu_for_charger(struct pcf50633_data *pcf,
108 * 500mA setting is "up to 500mA" according to that.
109 */
110 case CHARGER_TYPE_HOSTUSB:
111 - __reg_write(pcf, PCF50633_REG_MBCC7, PCF50633_MBCC7_USB_500mA);
112 + /* USB subsystem should call pcf50633_usb_curlim_set to set
113 + * what was negotiated with the host when it is enumerated
114 + * successfully. If we get called again after a good
115 + * negotiation, we keep what was negotiated. (Removal of
116 + * USB plug destroys pcf->last_curlim_set to 0)
117 + */
118 + if (pcf->last_curlim_set > 100)
119 + pcf50633_usb_curlim_set(pcf, pcf->last_curlim_set);
120 + else
121 + pcf50633_usb_curlim_set(pcf, 100);
122 break;
123 case CHARGER_TYPE_1A:
124 - __reg_write(pcf, PCF50633_REG_MBCC7, PCF50633_MBCC7_USB_1000mA);
125 + pcf50633_usb_curlim_set(pcf, 1000);
126 /*
127 * stop GPO / EN_HOSTUSB power driving out on the same
128 * USB power pins we have a 1A charger on right now!
129 @@ -536,6 +562,12 @@ static void configure_pmu_for_charger(struct pcf50633_data *pcf,
130 PCF50633_REG_GPIO1CFG) & 0xf0);
131 break;
132 }
133 +
134 + /* max out USB fast charge current -- actual current drawn is
135 + * additionally limited by USB limit so no worries
136 + */
137 + __reg_write(pcf, PCF50633_REG_MBCC5, 0xff);
138 +
139 }
140
141 static void trigger_next_adc_job_if_any(struct pcf50633_data *pcf)
142 @@ -562,6 +594,49 @@ static void add_request_to_adc_queue(struct pcf50633_data *pcf,
143 trigger_next_adc_job_if_any(pcf);
144 }
145
146 +/* we are run when we see a NOBAT situation, because there is no interrupt
147 + * source in pcf50633 that triggers on resuming charging. It watches to see
148 + * if charging resumes, it reassesses the charging source if it does. If the
149 + * USB power disappears, it is also a sign there must be a battery and it is
150 + * NOT being charged, so it exits since the next move must be USB insertion for
151 + * change of charger state
152 + */
153 +
154 +static void pcf50633_work_nobat(struct work_struct *work)
155 +{
156 + struct pcf50633_data *pcf =
157 + container_of(work, struct pcf50633_data, work_nobat);
158 +
159 + mutex_lock(&pcf->working_lock_nobat);
160 + pcf->working_nobat = 1;
161 + mutex_unlock(&pcf->working_lock_nobat);
162 +
163 + while (1) {
164 + msleep(1000);
165 +
166 + /* there's a battery in there now? */
167 + if (reg_read(pcf, PCF50633_REG_MBCS3) & 0x40) {
168 +
169 + pcf->jiffies_last_bat_ins = jiffies;
170 +
171 + /* figure out our charging stance */
172 + add_request_to_adc_queue(pcf, PCF50633_ADCC1_MUX_ADCIN1,
173 + PCF50633_ADCC1_AVERAGE_16);
174 + goto bail;
175 + }
176 +
177 + /* he pulled USB cable since we were started? exit then */
178 + if (pcf->usb_removal_count_nobat != pcf->usb_removal_count)
179 + goto bail;
180 + }
181 +
182 +bail:
183 + mutex_lock(&pcf->working_lock_nobat);
184 + pcf->working_nobat = 0;
185 + mutex_unlock(&pcf->working_lock_nobat);
186 +}
187 +
188 +
189 static void pcf50633_work(struct work_struct *work)
190 {
191 struct pcf50633_data *pcf =
192 @@ -674,20 +749,29 @@ static void pcf50633_work(struct work_struct *work)
193 if (pcf->pdata->cb)
194 pcf->pdata->cb(&pcf->client.dev,
195 PCF50633_FEAT_MBC, PMU_EVT_USB_INSERT);
196 + msleep(500); /* debounce, allow to see any ID resistor */
197 /* completion irq will figure out our charging stance */
198 add_request_to_adc_queue(pcf, PCF50633_ADCC1_MUX_ADCIN1,
199 PCF50633_ADCC1_AVERAGE_16);
200 }
201 if (pcfirq[0] & PCF50633_INT1_USBREM) {
202 DEBUGPC("USBREM ");
203 +
204 + pcf->usb_removal_count++;
205 +
206 /* only deal if we had understood it was in */
207 if (pcf->flags & PCF50633_F_USB_PRESENT) {
208 input_report_key(pcf->input_dev, KEY_POWER2, 0);
209 apm_queue_event(APM_POWER_STATUS_CHANGE);
210 pcf->flags &= ~PCF50633_F_USB_PRESENT;
211 +
212 if (pcf->pdata->cb)
213 pcf->pdata->cb(&pcf->client.dev,
214 PCF50633_FEAT_MBC, PMU_EVT_USB_REMOVE);
215 +
216 + /* destroy any memory of grant of power from host */
217 + pcf->last_curlim_set = 0;
218 +
219 /* completion irq will figure out our charging stance */
220 add_request_to_adc_queue(pcf, PCF50633_ADCC1_MUX_ADCIN1,
221 PCF50633_ADCC1_AVERAGE_16);
222 @@ -760,6 +844,33 @@ static void pcf50633_work(struct work_struct *work)
223
224 if (pcfirq[2] & PCF50633_INT3_BATFULL) {
225 DEBUGPC("BATFULL ");
226 +
227 + /* the problem is, we get a false BATFULL if we inserted battery
228 + * while USB powered. Defeat BATFULL if we recently inserted
229 + * battery
230 + */
231 +
232 + if ((jiffies - pcf->jiffies_last_bat_ins) < (HZ * 2)) {
233 +
234 + DEBUGPC("*** Ignoring BATFULL ***\n");
235 +
236 + ret = reg_read(pcf, PCF50633_REG_MBCC7) &
237 + PCF56033_MBCC7_USB_MASK;
238 +
239 +
240 + reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
241 + PCF56033_MBCC7_USB_MASK,
242 + PCF50633_MBCC7_USB_SUSPEND);
243 +
244 + reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
245 + PCF56033_MBCC7_USB_MASK,
246 + ret);
247 + } else {
248 + if (pcf->pdata->cb)
249 + pcf->pdata->cb(&pcf->client.dev,
250 + PCF50633_FEAT_MBC, PMU_EVT_CHARGER_IDLE);
251 + }
252 +
253 /* FIXME: signal this to userspace */
254 }
255 if (pcfirq[2] & PCF50633_INT3_CHGHALT) {
256 @@ -797,8 +908,7 @@ static void pcf50633_work(struct work_struct *work)
257
258 switch (pcf->adc_queue_mux[tail]) {
259 case PCF50633_ADCC1_MUX_BATSNS_RES: /* battery voltage */
260 - pcf->flag_bat_voltage_read =
261 - async_adc_complete(pcf);
262 + pcf->flag_bat_voltage_read = async_adc_complete(pcf);
263 break;
264 case PCF50633_ADCC1_MUX_ADCIN1: /* charger type */
265 pcf->charger_adc_result_raw = async_adc_complete(pcf);
266 @@ -830,8 +940,37 @@ static void pcf50633_work(struct work_struct *work)
267 (PCF50633_MBCS1_USBPRES | PCF50633_MBCS1_USBOK)) {
268 /*
269 * hey no need to freak out, we have some kind of
270 - * valid charger power
271 + * valid charger power to keep us going -- but note that
272 + * we are not actually charging anything
273 + */
274 + if (pcf->pdata->cb)
275 + pcf->pdata->cb(&pcf->client.dev,
276 + PCF50633_FEAT_MBC, PMU_EVT_CHARGER_IDLE);
277 +
278 + reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
279 + PCF50633_MBCC1_RESUME,
280 + PCF50633_MBCC1_RESUME);
281 +
282 + /*
283 + * Well, we are not charging anything right this second
284 + * ... however in the next ~30s before we get the next
285 + * NOBAT, he might insert a battery. So we schedule a
286 + * work function checking to see if
287 + * we started charging something during that time.
288 + * USB removal as well as charging terminates the work
289 + * function so we can't get terminally confused
290 */
291 + mutex_lock(&pcf->working_lock_nobat);
292 + if (!pcf->working_nobat) {
293 + pcf->usb_removal_count_nobat =
294 + pcf->usb_removal_count;
295 +
296 + if (!schedule_work(&pcf->work_nobat))
297 + DEBUGPC("failed to schedule nobat\n");
298 + }
299 + mutex_unlock(&pcf->working_lock_nobat);
300 +
301 +
302 DEBUGPC("(NO)BAT ");
303 } else {
304 /* Really low battery voltage, we have 8 seconds left */
305 @@ -1064,10 +1203,13 @@ void pcf50633_usb_curlim_set(struct pcf50633_data *pcf, int ma)
306 {
307 u_int8_t bits;
308
309 + pcf->last_curlim_set = ma;
310 +
311 dev_dbg(&pcf->client.dev, "setting usb current limit to %d ma", ma);
312
313 - if (ma >= 1000)
314 + if (ma >= 1000) {
315 bits = PCF50633_MBCC7_USB_1000mA;
316 + }
317 else if (ma >= 500)
318 bits = PCF50633_MBCC7_USB_500mA;
319 else if (ma >= 100)
320 @@ -1075,8 +1217,40 @@ void pcf50633_usb_curlim_set(struct pcf50633_data *pcf, int ma)
321 else
322 bits = PCF50633_MBCC7_USB_SUSPEND;
323
324 + DEBUGPC("pcf50633_usb_curlim_set -> %dmA\n", ma);
325 +
326 + if (!pcf->pdata->cb)
327 + goto set_it;
328 +
329 + switch (bits) {
330 + case PCF50633_MBCC7_USB_100mA:
331 + case PCF50633_MBCC7_USB_SUSPEND:
332 + /* no charging is gonna be happening */
333 + pcf->pdata->cb(&pcf->client.dev,
334 + PCF50633_FEAT_MBC, PMU_EVT_CHARGER_IDLE);
335 + break;
336 + default: /* right charging context that if there is power, we charge */
337 + if (pcf->flags & PCF50633_F_USB_PRESENT)
338 + pcf->pdata->cb(&pcf->client.dev,
339 + PCF50633_FEAT_MBC, PMU_EVT_CHARGER_ACTIVE);
340 + break;
341 + }
342 +
343 +set_it:
344 reg_set_bit_mask(pcf, PCF50633_REG_MBCC7, PCF56033_MBCC7_USB_MASK,
345 bits);
346 +
347 + /* clear batfull */
348 + reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
349 + PCF50633_MBCC1_AUTORES,
350 + 0);
351 + reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
352 + PCF50633_MBCC1_RESUME,
353 + PCF50633_MBCC1_RESUME);
354 + reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
355 + PCF50633_MBCC1_AUTORES,
356 + PCF50633_MBCC1_AUTORES);
357 +
358 }
359 EXPORT_SYMBOL_GPL(pcf50633_usb_curlim_set);
360
361 @@ -1106,16 +1280,36 @@ static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, NULL);
362 void pcf50633_charge_enable(struct pcf50633_data *pcf, int on)
363 {
364 u_int8_t bits;
365 + u_int8_t usblim;
366
367 if (!(pcf->pdata->used_features & PCF50633_FEAT_MBC))
368 return;
369
370 + DEBUGPC("pcf50633_charge_enable %d\n", on);
371 +
372 if (on) {
373 pcf->flags |= PCF50633_F_CHG_ENABLED;
374 bits = PCF50633_MBCC1_CHGENA;
375 + usblim = reg_read(pcf, PCF50633_REG_MBCC7) &
376 + PCF56033_MBCC7_USB_MASK;
377 + switch (usblim) {
378 + case PCF50633_MBCC7_USB_1000mA:
379 + case PCF50633_MBCC7_USB_500mA:
380 + if (pcf->flags & PCF50633_F_USB_PRESENT)
381 + if (pcf->pdata->cb)
382 + pcf->pdata->cb(&pcf->client.dev,
383 + PCF50633_FEAT_MBC,
384 + PMU_EVT_CHARGER_ACTIVE);
385 + break;
386 + default:
387 + break;
388 + }
389 } else {
390 pcf->flags &= ~PCF50633_F_CHG_ENABLED;
391 bits = 0;
392 + if (pcf->pdata->cb)
393 + pcf->pdata->cb(&pcf->client.dev,
394 + PCF50633_FEAT_MBC, PMU_EVT_CHARGER_IDLE);
395 }
396 reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, PCF50633_MBCC1_CHGENA,
397 bits);
398 @@ -1712,7 +1906,9 @@ static int pcf50633_detect(struct i2c_adapter *adapter, int address, int kind)
399
400 mutex_init(&data->lock);
401 mutex_init(&data->working_lock);
402 + mutex_init(&data->working_lock_nobat);
403 INIT_WORK(&data->work, pcf50633_work);
404 + INIT_WORK(&data->work_nobat, pcf50633_work_nobat);
405 data->irq = irq;
406 data->working = 0;
407 data->onkey_seconds = -1;
408 diff --git a/include/linux/pcf506xx.h b/include/linux/pcf506xx.h
409 index 33be73e..9069bd4 100644
410 --- a/include/linux/pcf506xx.h
411 +++ b/include/linux/pcf506xx.h
412 @@ -21,6 +21,8 @@ enum pmu_event {
413 PMU_EVT_USB_INSERT,
414 PMU_EVT_USB_REMOVE,
415 #endif
416 + PMU_EVT_CHARGER_ACTIVE,
417 + PMU_EVT_CHARGER_IDLE,
418 __NUM_PMU_EVTS
419 };
420
421 --
422 1.5.6.5
423