hh.org updates
[hh.org.git] / arch / arm / mach-pxa / htcuniversal / htcuniversal_power.c
blob9789762f136117f386cd433d2ee016401f88e6c3
1 /*
2 * Copyright 2005, SDG Systems, LLC
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file COPYING in the main directory of this archive for
6 * more details.
8 * Sat 11 Jun 2005 Aric D. Blumer
9 */
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/interrupt.h>
14 #include <linux/soc/asic3_base.h>
15 #include <linux/delay.h>
16 #include <linux/battery.h>
17 #include <linux/irq.h>
19 #include <asm/io.h>
20 #include <asm/apm.h>
22 #include <asm/hardware/ipaq-asic3.h>
24 #include <asm/arch/htcuniversal-gpio.h>
25 #include <asm/arch/htcuniversal-asic.h>
27 #define DRIVER_NAME "htcuniversal_power"
28 static char driver_name[] = DRIVER_NAME;
30 #define BATTERY_ACR 1620 /* TODO: remove */
32 static void w1_init(void);
33 static void w1_deinit(void);
35 static enum {
36 POWER_NONE,
37 POWER_AC,
38 POWER_USB,
39 } power_status;
41 static int cdiv = 0x2;
42 static int net = 0x0;
44 module_param(cdiv, int, 0444);
45 MODULE_PARM_DESC(cdiv, "Clock Divisor");
46 module_param(net, int, 0444);
47 MODULE_PARM_DESC(net, "Use net address");
49 struct w1_data {
50 volatile unsigned short __iomem *base;
51 int w1_irq;
52 wait_queue_head_t irqwait;
53 unsigned short data_register;
54 unsigned short irqstatus;
55 int battery_class;
56 struct timer_list irqtimer;
58 unsigned int temp;
59 unsigned int voltage;
60 int Current;
61 int current_accum;
62 int acr_reset;
63 int minutes;
64 int battery_life;
66 int ac_irq;
67 int usb_irq;
69 int initialized;
71 char net_address[8];
73 u64 jiffies_64;
75 } module_data;
77 #define DS1WM_COMMAND 0
78 #define DS1WM_TXRX 1
79 #define DS1WM_IRQ 2
80 #define DS1WM_IRQ_EN 3
81 #define DS1WM_CLOCK_DIV 4
83 #define WRITE_REGISTER(r, value) do { \
84 /* printk("Write %p = 0x%04x\n", (module_data.base + (r)), value); */ \
85 ((*(module_data.base + (r))) = (value)); \
86 } while(0)
88 #define READ_REGISTER(R, r) do { \
89 (R = *(module_data.base + (r))); \
90 /* printk("Read %p = 0x%04x\n", (module_data.base + (r)), R); */ \
91 } while(0)
93 static int w1_detect(void);
94 static int w1_write_byte(unsigned short data);
95 static int w1_read_byte(unsigned int *data);
96 static void w1_send_net_address(void);
98 DECLARE_MUTEX(update_mutex);
100 static void
101 update_data(int force)
103 int flag = 0;
104 int retries = 30;
106 /* Only allow an update every second. */
107 if(!force && (module_data.jiffies_64 + 5 * HZ > jiffies_64)) {
108 return;
111 if(down_trylock(&update_mutex)) {
112 return;
115 try_again:
116 if(w1_detect()) {
117 unsigned int readval;
119 w1_send_net_address();
120 w1_write_byte(0x69); /* Read */
121 w1_write_byte(0x00); /* Starting at offset 0x0 */
122 w1_read_byte(&readval); /* 0x0 */
123 if(readval == 0xff) {
125 * We have an unknown read problem. Try again.
127 msleep(1 * retries);
128 if(0 == retries--) {
129 // printk("htcuniversal_power: Whoops\n");
130 goto exit_please;
132 goto try_again;
135 /* Get rid of stuff we don't want */
136 w1_read_byte(&readval); /* 0x1 */
137 w1_read_byte(&readval); /* 0x2 */
138 w1_read_byte(&readval); /* 0x3 */
139 w1_read_byte(&readval); /* 0x4 */
140 w1_read_byte(&readval); /* 0x5 */
141 w1_read_byte(&readval); /* 0x6 */
142 w1_read_byte(&readval); /* 0x7 */
143 w1_read_byte(&readval); /* 0x8 */
144 w1_read_byte(&readval); /* 0x9 */
145 w1_read_byte(&readval); /* 0xa */
146 w1_read_byte(&readval); /* 0xb */
148 /* VOLTAGE */
149 w1_read_byte(&readval); /* 0xc */
150 if(readval == 0xff) {
152 * We have an unknown read problem. Try again.
154 msleep(1 * retries);
155 if(0 == retries--) {
156 // printk("htcuniversal_power: Whoops2\n");
157 goto exit_please;
159 goto try_again;
161 module_data.voltage = readval << 8;
162 w1_read_byte(&readval); /* 0xd */
163 if(readval == 0xff) {
165 * We have an unknown read problem. Try again.
167 msleep(1 * retries);
168 if(0 == retries--) {
169 // printk("htcuniversal_power: Whoops2\n");
170 goto exit_please;
172 goto try_again;
174 module_data.voltage |= readval;
175 module_data.voltage >>= 5;
176 /* 4997 / 1024 is almost equal to 4880 / 1000 */
177 module_data.voltage *= 4997;
178 module_data.voltage /= 1024;
179 /****************/
181 /* Current */
182 w1_read_byte(&readval); /* 0xe */
183 if(readval == 0xff) {
185 * We have an unknown read problem. Try again.
187 msleep(1 * retries);
188 if(0 == retries--) {
189 // printk("htcuniversal_power: Whoops2\n");
190 goto exit_please;
192 goto try_again;
194 if(readval & (1 << 7)) {
195 /* The sign bit is set */
196 module_data.Current = -1 ^ 0x7fff;
197 } else {
198 module_data.Current = 0;
200 module_data.Current |= readval << 8;
201 w1_read_byte(&readval); /* 0xf */
202 if(readval == 0xff) {
204 * We have an unknown read problem. Try again.
206 msleep(1 * retries);
207 if(0 == retries--) {
208 // printk("htcuniversal_power: Whoops2\n");
209 goto exit_please;
211 goto try_again;
213 module_data.Current |= readval;
214 module_data.Current >>= 3;
215 /* 655360 / 1024*1024 is equal to 6250000 / 1000000 */
216 module_data.Current *= 655360;
217 module_data.Current /= 1024 * 1024;
218 /****************/
220 /* Current Accumulator */
221 w1_read_byte(&readval); /* 0x10 */
222 if(readval == 0xff) {
224 * We have an unknown read problem. Try again.
226 msleep(1 * retries);
227 if(0 == retries--) {
228 // printk("htcuniversal_power: Whoops2\n");
229 goto exit_please;
231 goto try_again;
233 if(readval & (1 << 7)) {
234 /* The sign bit is set */
235 module_data.current_accum = -1 ^ 0xffff;
236 } else {
237 module_data.current_accum = 0;
239 module_data.current_accum |= readval << 8;
240 w1_read_byte(&readval); /* 0x11 */
241 if(readval == 0xff) {
243 * We have an unknown read problem. Try again.
245 msleep(1 * retries);
246 if(0 == retries--) {
247 // printk("htcuniversal_power: Whoops2\n");
248 goto exit_please;
250 goto try_again;
252 module_data.current_accum |= readval;
253 /* Units are 250 uAh. We wan't mAh. So each unit is 1/4 mAh. But
254 * we want fractions of hours, so let's keep the 1/4 mAh units. */
255 /* Now convert to quarter hours by dividing by mA. */
256 if(module_data.Current == 0) {
257 module_data.minutes = module_data.current_accum * 15;
258 } else {
259 module_data.minutes = -((module_data.current_accum * 15) / module_data.Current);
261 /****************/
263 /* Get rid of stuff we don't want */
264 w1_read_byte(&readval); /* 0x12 */
265 w1_read_byte(&readval); /* 0x13 */
266 w1_read_byte(&readval); /* 0x14 */
267 w1_read_byte(&readval); /* 0x15 */
268 w1_read_byte(&readval); /* 0x16 */
269 w1_read_byte(&readval); /* 0x17 */
271 /* Temperature */
272 w1_read_byte(&readval); /* 0x18 */
273 if(readval == 0xff) {
275 * We have an unknown read problem. Try again.
277 msleep(1 * retries);
278 if(0 == retries--) {
279 // printk("htcuniversal_power: Whoops2\n");
280 goto exit_please;
282 goto try_again;
284 module_data.temp = readval << 8;
285 w1_read_byte(&readval); /* 0x19 */
286 if(readval == 0xff) {
288 * We have an unknown read problem. Try again.
290 msleep(1 * retries);
291 if(0 == retries--) {
292 // printk("htcuniversal_power: Whoops2\n");
293 goto exit_please;
295 goto try_again;
297 module_data.temp |= readval;
298 module_data.temp >>= 5;
299 module_data.temp += module_data.temp / 4;
300 /****************/
302 if(module_data.voltage > 8000) {
303 /* There is an unknown problem where things get into a strange
304 * state. We know it because the voltage is reported as too
305 * high. This is an attempt at robustness to correct the problem.
306 * I'd like to fix it permanently, though.
308 if(!flag) {
309 printk("%s: Bad state detected. Retrying.\n", driver_name);
310 flag = 1;
311 mdelay(1);
312 goto try_again;
313 } else {
314 printk("w1 fix didn't work. We give up.\n");
318 /* Reset ACR when battery gets full. */
319 if (module_data.Current >= 0 && module_data.Current < 32 &&
320 !module_data.acr_reset) {
321 printk(KERN_INFO "ACR reset.\n");
322 module_data.current_accum = BATTERY_ACR * 4;
323 #if 1
324 if (w1_detect()) {
325 w1_send_net_address();
326 w1_write_byte(0x6c); /* write */
327 w1_write_byte(0x10); /* current accum msb */
328 w1_write_byte((module_data.current_accum >> 8) & 0xff);
329 w1_write_byte(module_data.current_accum & 0xff);
330 module_data.acr_reset = 1;
332 #endif
335 module_data.battery_life = (module_data.current_accum * 25) / BATTERY_ACR;
336 if(module_data.battery_life > 100) module_data.battery_life = 100;
337 if(module_data.battery_life < 0) module_data.battery_life = 0;
338 } else {
339 printk("%s: Device not detected\n", driver_name);
340 module_data.temp = 0;
341 module_data.voltage = 0;
342 module_data.current_accum = 0;
343 module_data.Current = 0;
346 exit_please:
347 module_data.jiffies_64 = jiffies_64;
348 up(&update_mutex);
352 static irqreturn_t
353 w1_isr(int irq, void *opaque)
356 * Note that the w1 control logic is quite slow, running at 1 MHz. It is
357 * possible to read the IRQ status, and return and the interrupt still be
358 * active. In this case, we're OK because we only update the irqstatus
359 * when an actual interrupt causing bit is set.
362 unsigned short tmp;
363 /* Just reading the register clears the source */
364 READ_REGISTER(tmp, DS1WM_IRQ);
365 if( tmp & (1 << 0) /* Presence Detect */
366 || tmp & (1 << 4) /* Receive Buffer Full */
368 int junk, count;
369 module_data.irqstatus = tmp;
370 for(count = 0; count < 10; count++) {
371 READ_REGISTER(module_data.data_register, DS1WM_TXRX);
372 READ_REGISTER(junk, DS1WM_CLOCK_DIV); /* Delay */
373 READ_REGISTER(tmp, DS1WM_IRQ);
374 if(!(tmp & (1 << 4))) break;
376 wake_up_interruptible(&module_data.irqwait);
378 return IRQ_HANDLED;
382 static void
383 w1_init(void)
385 /* Turn on external clocks and the OWM clock */
386 asic3_set_clock_cdex(&htcuniversal_asic3.dev,
387 CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM,
388 CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM);
390 mdelay(1);
392 asic3_set_extcf_reset(&htcuniversal_asic3.dev, ASIC3_EXTCF_OWM_RESET, ASIC3_EXTCF_OWM_RESET);
393 mdelay(1);
394 asic3_set_extcf_reset(&htcuniversal_asic3.dev, ASIC3_EXTCF_OWM_RESET, 0);
395 mdelay(1);
397 /* Clear OWM_SMB, set OWM_EN */
398 asic3_set_extcf_select(&htcuniversal_asic3.dev,
399 ASIC3_EXTCF_OWM_SMB | ASIC3_EXTCF_OWM_EN,
400 0 | ASIC3_EXTCF_OWM_EN);
402 mdelay(1);
404 WRITE_REGISTER(DS1WM_CLOCK_DIV, cdiv);
405 /* Enable interrupts: RBF, PD */
406 /* Set Interrupt Active High (IAS = 1) */
407 WRITE_REGISTER(DS1WM_IRQ_EN,
408 (0 << 6) /* ENBSY */
409 | (0 << 5) /* ESINT */
410 | (1 << 4) /* ERBF */
411 | (0 << 3) /* ETMT */
412 | (0 << 2) /* ETBE */
413 | (1 << 1) /* IAS 0 = active low interrupt, 1 = active high */
414 | (1 << 0) /* EPD */
417 mdelay(1);
419 /* Set asic3 interrupt status to zero. Hmmmmm. not accesible in
420 * asic3_base.c */
423 static int
424 w1_detect(void)
426 int retry_count = 5;
428 try_again:
429 module_data.irqstatus = 0;
431 /* Set 1WR (one wire reset) bit */
432 WRITE_REGISTER(DS1WM_COMMAND, 1);
433 if(wait_event_interruptible_timeout(module_data.irqwait,
434 module_data.irqstatus & (1 << 0) /* PD set? */, HZ)) {
436 if(0 == (module_data.irqstatus & (1 << 1)) /* PDR set? */) {
437 mdelay(1);
438 return 1;
440 } else {
441 printk("%s: detect timeout\n", driver_name);
443 if(retry_count--) {
444 mdelay(1);
445 goto try_again;
447 return 0;
450 static int
451 w1_read_byte(unsigned int *data)
453 module_data.irqstatus = 0;
454 WRITE_REGISTER(DS1WM_TXRX, 0xff);
455 if(wait_event_interruptible_timeout(module_data.irqwait,
456 module_data.irqstatus & (1 << 4) /* RBF set? */, HZ)) {
457 *data = module_data.data_register;
458 return 1;
459 } else {
460 printk("%s: RBF not set\n", driver_name);
462 return 0;
465 static int
466 w1_write_byte(unsigned short data)
468 module_data.irqstatus = 0;
469 WRITE_REGISTER(DS1WM_TXRX, data);
470 if(wait_event_interruptible_timeout(module_data.irqwait,
471 module_data.irqstatus & (1 << 2) /* TBE set? */, HZ)) {
472 return 1;
473 } else {
474 printk("%s: TBE not set\n", driver_name);
476 return 0;
479 int get_min_voltage(struct battery *b)
481 return 3500;
483 int get_min_current(struct battery *b)
485 return -1000; /* negative 1000 mA */
487 int get_min_charge(struct battery *b)
489 return 0;
491 int get_max_voltage(struct battery *b)
493 return 4200; /* mV */
495 int get_max_current(struct battery *b)
497 return 1000; /* positive 1000 mA */
499 int get_max_charge(struct battery *b)
501 return BATTERY_ACR;
503 int get_temp(struct battery *b)
505 update_data(0);
506 return module_data.temp;
508 int get_voltage(struct battery *b)
510 update_data(0);
511 return module_data.voltage;
513 int get_Current(struct battery *b)
515 update_data(0);
516 return module_data.Current;
518 int get_charge(struct battery *b)
520 return module_data.current_accum / 4;
522 int get_status(struct battery *b)
524 return power_status == POWER_NONE? 0: 1;
527 static struct battery htcuniversal_power = {
528 .name = "htcuniversal_primary",
529 .id = "main",
530 .get_min_voltage = get_min_voltage,
531 .get_min_current = get_min_current,
532 .get_min_charge = get_min_charge,
533 .get_max_voltage = get_max_voltage,
534 .get_max_current = get_max_current,
535 .get_max_charge = get_max_charge,
536 .get_temp = get_temp,
537 .get_voltage = get_voltage,
538 .get_current = get_Current,
539 .get_charge = get_charge,
540 .get_status = get_status,
543 static void
544 set_leds(int status, int Current, int battery_life)
546 if (status == APM_AC_ONLINE) {
547 /* check life to update LEDs. LEDs are off when on battery */
549 * It has been observed that the Current is greater when the device is
550 * suspended compared to when it is awake. So we have to use
551 * different parameters here compared to bootldr
553 if((Current < 32) && (battery_life == 100)) {
554 /* Green LED on solid, amber off */
555 // htcuniversal_set_led(0, 0, 16);
556 // htcuniversal_set_led(1, 16, 16);
557 } else {
558 /* Amber LED blinking, green off */
559 // htcuniversal_set_led(0, 128, 256);
560 // htcuniversal_set_led(1, 0, 16);
562 } else {
563 /* No charging power is applied; both LEDs off */
564 // htcuniversal_set_led(0, 0, 16);
565 // htcuniversal_set_led(1, 0, 16);
570 static void
571 htcuniversal_apm_get_power_status( struct apm_power_info *info )
573 update_data(0);
575 info->ac_line_status = (power_status == POWER_NONE)
576 ? APM_AC_OFFLINE : APM_AC_ONLINE;
578 /* It is possible to be hooked up to USB and getting some power from
579 * it, but still having a current drain on the battery with a busy CPU
581 info->battery_life = module_data.battery_life;
583 if (info->ac_line_status == APM_AC_ONLINE) {
584 info->battery_status = APM_BATTERY_STATUS_CHARGING;
585 } else {
586 info->time = module_data.minutes;
587 info->units = APM_UNITS_MINS;
588 module_data.acr_reset = 0;
590 info->battery_status = APM_BATTERY_STATUS_CRITICAL;
591 if(info->battery_life > 5) {
592 info->battery_status = APM_BATTERY_STATUS_LOW;
594 if(info->battery_life > 20) {
595 info->battery_status = APM_BATTERY_STATUS_HIGH;
599 // set_leds(info->ac_line_status, module_data.Current, module_data.battery_life);
602 static void
603 power_change_task_handler(unsigned long enableirq)
605 // unsigned int retval;
606 int ac_in, usb_in;
608 if(!module_data.initialized) return;
610 ac_in = (GET_HTCUNIVERSAL_GPIO(POWER_DET) == 0);
611 usb_in = (GET_HTCUNIVERSAL_GPIO(USB_DET) == 0);
612 printk( KERN_INFO "htcuniversal power_change: ac_in=%d\n", ac_in );
613 printk( KERN_INFO "htcuniversal power_change: usb_in=%d\n", usb_in );
615 if (ac_in) {
616 /* If we're on AC, it doesn't matter if we're on USB or not, use AC
617 * only */
618 // SET_HX4700_GPIO_N( CHARGE_EN, 1 );
619 // SET_HX4700_GPIO( USB_CHARGE_RATE, 0 );
621 asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_CHARGE_EN, 0);
622 power_status = POWER_AC;
623 set_leds(APM_AC_ONLINE, module_data.Current, module_data.battery_life);
624 } else if (usb_in) {
625 /* We're not on AC, but we are on USB, so charge with that */
626 // SET_HX4700_GPIO( USB_CHARGE_RATE, 1 );
627 // SET_HX4700_GPIO_N( CHARGE_EN, 1 );
629 asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_CHARGE_EN, 0);
631 power_status = POWER_USB;
632 set_leds(APM_AC_ONLINE, module_data.Current, module_data.battery_life);
633 } else {
634 /* We're not on AC or USB, don't charge */
635 // SET_HX4700_GPIO_N( CHARGE_EN, 0 );
636 // SET_HX4700_GPIO( USB_CHARGE_RATE, 0 );
638 asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_CHARGE_EN, 1<<GPIOB_CHARGE_EN);
640 power_status = POWER_NONE;
641 // htcuniversal_clear_led(0);
642 // htcuniversal_clear_led(1);
643 // set_leds(APM_AC_OFFLINE, module_data.Current, module_data.battery_life);
644 module_data.acr_reset = 0;
648 /* update_data(1); */
649 module_data.jiffies_64 = 0; /* Force a re-read on next try */
651 if(enableirq) {
652 enable_irq(module_data.usb_irq);
653 enable_irq(module_data.ac_irq);
657 static int
658 attach_isr(int irq, void *dev_id)
660 if(irq != module_data.usb_irq
661 && irq != module_data.ac_irq) {
662 printk("Bad irq: %d, not %d or %d\n", irq, module_data.usb_irq, module_data.ac_irq);
664 if(module_data.initialized) {
665 // SET_HX4700_GPIO_N( CHARGE_EN, 0 );
667 asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_CHARGE_EN, 1<<GPIOB_CHARGE_EN);
669 mod_timer(&module_data.irqtimer, jiffies + HZ/10);
670 disable_irq(module_data.usb_irq);
671 disable_irq(module_data.ac_irq);
673 return IRQ_HANDLED;
676 static int
677 battery_class_uevent(struct class_device *dev, char **envp, int num_envp,
678 char *buffer, int buffer_size)
680 return 0;
683 static void
684 battery_class_release(struct class_device *dev)
688 static void
689 battery_class_class_release(struct class *class)
693 static void
694 w1_send_net_address(void)
696 if(net) {
697 int i;
698 w1_write_byte(0x55); /* Match Net Address */
699 for(i = 0; i < sizeof(module_data.net_address)/sizeof(module_data.net_address[0]); i++) {
700 w1_write_byte(module_data.net_address[i]);
702 } else {
703 w1_write_byte(0xcc); /* Skip Net Address */
707 static int
708 w1_probe(struct platform_device *dev)
710 int retval;
713 module_data.initialized = 0;
714 module_data.w1_irq = -1;
715 module_data.usb_irq = -1;
716 module_data.ac_irq = -1;
717 module_data.base = 0;
718 module_data.jiffies_64 = 0;
719 module_data.acr_reset = 0;
721 init_timer(&module_data.irqtimer);
722 module_data.irqtimer.function = power_change_task_handler;
723 module_data.irqtimer.data = 1;
725 init_waitqueue_head(&module_data.irqwait);
727 platform_set_drvdata(dev, &module_data);
729 module_data.base = ioremap_nocache(0x10000600, 64);
730 module_data.battery_class = 0;
732 if(!module_data.base) {
733 printk(KERN_NOTICE "%s: Unable to map device\n", driver_name);
734 return -ENODEV;
738 module_data.w1_irq = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_OWM_IRQ;
739 retval = request_irq(module_data.w1_irq, w1_isr, SA_INTERRUPT, driver_name, &module_data);
740 if(retval) {
741 printk(KERN_NOTICE "%s: Unable to get interrupt %d: %d\n",
742 driver_name, module_data.w1_irq, retval);
743 iounmap((void __iomem *)module_data.base);
744 module_data.base = 0;
745 module_data.w1_irq = -1;
746 return -ENODEV;
749 w1_init();
750 if(w1_detect()) {
751 unsigned int readval;
752 int i;
753 w1_write_byte(0x33);
754 for(i = 0; i < sizeof(module_data.net_address)/sizeof(module_data.net_address[0]); i++) {
755 w1_read_byte(&readval);
756 module_data.net_address[i] = readval & 0xff;
758 if(module_data.net_address[0] != 0x30) {
759 printk("Looks like wrong net address is "
760 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
761 module_data.net_address[0], module_data.net_address[1],
762 module_data.net_address[2], module_data.net_address[3],
763 module_data.net_address[4], module_data.net_address[5],
764 module_data.net_address[6], module_data.net_address[7]
767 else
769 printk("htcuniversal: your DS267x unique id is "
770 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
771 module_data.net_address[0], module_data.net_address[1],
772 module_data.net_address[2], module_data.net_address[3],
773 module_data.net_address[4], module_data.net_address[5],
774 module_data.net_address[6], module_data.net_address[7]
777 } else {
778 printk("Could not detect device for net address\n");
779 iounmap((void __iomem *)module_data.base);
780 free_irq(module_data.w1_irq, &module_data);
781 module_data.base = 0;
782 module_data.w1_irq = -1;
783 return -ENODEV;
785 if(w1_detect()) {
786 unsigned int readval;
787 if(battery_class_register(&htcuniversal_power)) {
788 printk(KERN_ERR "%s: Could not register battery class\n",
789 driver_name);
790 } else {
791 module_data.battery_class = 1;
792 htcuniversal_power.class_dev.class->uevent = battery_class_uevent;
793 htcuniversal_power.class_dev.class->release = battery_class_release;
794 htcuniversal_power.class_dev.class->class_release = battery_class_class_release;
796 w1_send_net_address();
797 w1_write_byte(0x69); /* Read */
798 w1_write_byte(0x08); /* Special Feature Address */
799 w1_read_byte(&readval);
800 if(!(readval & (1 << 7))) {
801 printk("PS is low. Writing 1.\n");
802 /* The PS signal is low. The docs say to write a 1 to it to ensure
803 * proper operation. */
804 if(w1_detect()) {
805 w1_send_net_address();
806 w1_write_byte(0x6c); /* Write */
807 w1_write_byte(0x08); /* Special Feature Address */
808 w1_write_byte(readval | (1 << 7)); /* Data */
809 } else {
810 printk("No detect when writing PS\n");
813 } else {
814 printk("%s: Device not detected on init\n", driver_name);
815 return -ENODEV;
818 module_data.usb_irq = HTCUNIVERSAL_IRQ(USB_DET);
819 module_data.ac_irq = HTCUNIVERSAL_IRQ(POWER_DET);
821 module_data.initialized = 1;
823 /* Get first power state */
824 power_change_task_handler(0);
826 /* USB IRQ */
827 if (request_irq( module_data.usb_irq, attach_isr, SA_INTERRUPT,
828 "HTC Universal USB Detect", NULL ) != 0) {
829 printk( KERN_ERR "Unable to configure USB detect interrupt.\n" );
830 module_data.usb_irq = -1;
833 set_irq_type( module_data.usb_irq, IRQ_TYPE_EDGE_BOTH );
835 /* AC IRQ */
836 if (request_irq( module_data.ac_irq, attach_isr, SA_INTERRUPT,
837 "HTC Universal AC Detect", NULL ) != 0) {
838 printk( KERN_ERR "Unable to configure AC detect interrupt.\n" );
839 module_data.ac_irq = -1;
842 set_irq_type( module_data.ac_irq, IRQ_TYPE_EDGE_BOTH );
844 #ifdef CONFIG_PM
845 apm_get_power_status = htcuniversal_apm_get_power_status;
846 #endif
848 return 0;
851 static void
852 w1_deinit(void)
854 /* Turn off OWM clock. I hate to touch the others because they might be
855 * used by other devices like MMC. */
856 asic3_set_clock_cdex(&htcuniversal_asic3.dev,
857 /* CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | */ CLOCK_CDEX_OWM,
858 /* CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | */ 0);
860 /* Clear OWM_EN */
861 asic3_set_extcf_select(&htcuniversal_asic3.dev,
862 ASIC3_EXTCF_OWM_EN, 0);
864 mdelay(1);
867 static int
868 w1_remove(struct platform_device *dev)
870 printk("w1_remove\n");
872 #ifdef CONFIG_PM
873 apm_get_power_status = NULL;
874 #endif
876 if(module_data.base) {
877 w1_deinit();
879 if(module_data.battery_class) {
880 battery_class_unregister(&htcuniversal_power);
881 printk("battery class unregistered\n");
883 if(module_data.base) {
884 iounmap((void __iomem *)module_data.base);
885 printk("Base unmapped\n");
887 if(module_data.w1_irq != -1) {
888 disable_irq(module_data.w1_irq);
889 free_irq(module_data.w1_irq, &module_data);
890 printk("w1 irq freed\n");
892 if (module_data.ac_irq != -1) {
893 disable_irq(module_data.ac_irq);
894 free_irq( module_data.ac_irq, NULL );
895 printk("ac irq freed\n");
897 if (module_data.usb_irq != -1) {
898 disable_irq(module_data.usb_irq);
899 free_irq( module_data.usb_irq, NULL );
900 printk("usb irq freed\n");
903 while(timer_pending(&module_data.irqtimer)) {
904 msleep(100);
906 del_timer(&module_data.irqtimer);
908 return 0;
911 static int
912 w1_suspend(struct platform_device *dev, pm_message_t state)
914 printk("htcuniversal: w1_suspend\n");
915 w1_deinit();
916 return 0;
919 static int
920 w1_resume(struct platform_device *dev)
922 printk("htcuniversal: w1_resume\n");
923 w1_init();
924 /* check for changes to power that may have occurred */
925 power_change_task_handler(0);
926 /* I think this will work to ensure the interrupt is unmasked. */
927 disable_irq(module_data.w1_irq);
928 enable_irq(module_data.w1_irq);
929 /* Clear OWM_SMB, set OWM_EN */
930 asic3_set_extcf_select(&htcuniversal_asic3.dev, ASIC3_EXTCF_OWM_EN, ASIC3_EXTCF_OWM_EN);
931 return 0;
934 static struct platform_driver w1_driver = {
935 .driver = {
936 .name = "htcuniversal_power",
938 .probe = w1_probe,
939 .remove = w1_remove,
940 .suspend = w1_suspend,
941 .resume = w1_resume
944 static int __init
945 w1init(void)
947 printk(KERN_NOTICE "htcuniversal Power Management Driver\n");
948 return platform_driver_register(&w1_driver);
951 static void __exit
952 w1exit(void)
954 platform_driver_unregister(&w1_driver);
957 module_init(w1init);
958 module_exit(w1exit);
960 MODULE_AUTHOR("Aric D. Blumer, SDG Systems, LLC");
961 MODULE_DESCRIPTION("HTC Universal Power Management Driver");
962 MODULE_LICENSE("GPL");
964 /* vim600: set expandtab: */