1 /* Core Hardware driver for Hx4700 (Serial, ASIC3, EGPIOs)
3 * Copyright (c) 2005 SDG Systems, LLC
5 * 2005-03-29 Todd Blumer Converted basic structure to support hx4700
6 * 2005-04-30 Todd Blumer Add IRDA code from H2200
9 #include <linux/module.h>
10 #include <linux/version.h>
11 #include <linux/interrupt.h>
12 #include <linux/platform_device.h>
13 #include <linux/delay.h>
18 #include <asm/mach/irq.h>
19 #include <asm/arch/pxa-regs.h>
20 #include <asm/arch/pxa-pm_ll.h>
21 #include <asm/arch/hx4700-gpio.h>
22 #include <asm/arch/hx4700-asic.h>
23 #include <asm/arch/hx4700-core.h>
25 #include <linux/soc/asic3_base.h>
26 #include <asm/hardware/ipaq-asic3.h>
28 #define EGPIO_OFFSET 0
29 #define EGPIO_BASE (PXA_CS5_PHYS+EGPIO_OFFSET)
31 volatile u_int16_t
*egpios
;
34 static int htc_bootloader
= 0; /* Is the stock HTC bootloader installed? */
39 * may make sense to put egpios elsewhere, but they're here now
40 * since they share some of the same address space with the TI WLAN
42 * EGPIO register is write-only
46 hx4700_egpio_enable( u_int16_t bits
)
50 local_irq_save(flags
);
55 local_irq_restore(flags
);
57 EXPORT_SYMBOL(hx4700_egpio_enable
);
60 hx4700_egpio_disable( u_int16_t bits
)
64 local_irq_save(flags
);
69 local_irq_restore(flags
);
71 EXPORT_SYMBOL(hx4700_egpio_disable
);
74 hx4700_udc_detect( void )
76 return (asic3_get_gpio_status_d(&hx4700_asic3
.dev
)
77 & (1 << GPIOD_USBC_DETECT_N
)) ? 0 : 1;
80 static unsigned int serial_irq
= 0xffffffff;
83 serial_change_task_handler( void *x
)
88 statusd
= asic3_get_gpio_status_d( &hx4700_asic3
.dev
);
89 connected
= (statusd
& (1<<GPIOD_COM_DCD
)) != 0;
91 set_irq_type( serial_irq
, IRQT_FALLING
);
93 set_irq_type( serial_irq
, IRQT_RISING
);
95 SET_HX4700_GPIO( RS232_ON
, connected
);
96 printk( KERN_INFO
"serial_isr: com_dcd=%d\n", connected
);
98 DECLARE_WORK(serial_change_task
, serial_change_task_handler
, NULL
);
101 serial_isr(int irq
, void *dev_id
)
103 schedule_delayed_work( &serial_change_task
, 100 ); /* debounce */
108 static int hx4700_suspend(struct platform_device
*pdev
, pm_message_t state
)
110 /* Turn off external clocks here, because hx4700_power and asic3_mmc
111 * scared to do so to not hurt each other. (-5 mA) */
113 asic3_set_clock_cdex(&hx4700_asic3
.dev
,
114 CLOCK_CDEX_EX0
| CLOCK_CDEX_EX1
, 0 | 0);
116 /* 0x20c2 is HTC clock value
117 * CLOCK_CDEX_SOURCE 2
127 * CLOCK_CDEX_SD_HOST 0
128 * CLOCK_CDEX_SD_BUS 0
131 * CLOCK_CDEX_CONTROL_CX 0
135 asic3_set_clock_cdex(&hx4700_asic3
.dev
, 0xffff, 0x21c2);
137 *egpios
= 0; /* turn off all egpio power */
139 * Note that WEP1 wake up event is used by bootldr to set the
140 * LEDS when power is applied/removed for charging.
142 PWER
= PWER_RTC
| PWER_GPIO0
| PWER_GPIO1
| PWER_GPIO12
| PWER_WEP1
; // rtc + power + reset + asic3 + wep1
143 PFER
= PWER_GPIO1
; // Falling Edge Detect
144 PRER
= PWER_GPIO0
| PWER_GPIO12
; // Rising Edge Detect
149 /* PGSR3 = 0x00104202; */
152 /* These next checks are specifically for charging. We want to enable
153 * it if it is already enabled */
154 /* Check for charge enable, GPIO 72 */
155 if(GPLR2
& (1 << 8)) {
162 /* Check for USB_CHARGE_RATE, GPIO 96 */
163 if(GPLR3
& (1 << 0)) {
171 PCFR
= PCFR_GPROD
|PCFR_DC_EN
|PCFR_GPR_EN
|PCFR_OPDE
172 |PCFR_FP
|PCFR_PI2CEN
; /* was 0x1091; */
173 /* The 2<<2 below turns on the Power Island state preservation
174 * and counters. This allows us to wake up bootldr after a
175 * period of time, and it can set the LEDs correctly based on
176 * the power state. The bootldr turns it off when it's
179 PSLR
=0xc8000000 | (2 << 2);
182 * If we're using bootldr and not the stock HTC bootloader,
183 * we want to wake up periodically to see if the charge is full while
184 * it is suspended. We do this with the OS timer 4 in the pxa270.
186 if (!htc_bootloader
) {
187 OMCR4
= 0x4b; /* Periodic, self-resetting, 1-second timer */
188 OSMR4
= 5; /* Wake up bootldr after x seconds so it can
189 figure out what to do with the LEDs. */
190 OIER
|= 0x10; /* Enable interrupt source for Timer 4 */
191 OSCR4
= 0; /* This starts the timer */
194 asic3_set_extcf_select(&hx4700_asic3
.dev
, ASIC3_EXTCF_OWM_EN
, 0);
199 static int hx4700_resume(struct platform_device
*pdev
)
201 hx4700_egpio_enable(0);
202 /* check for changes to serial that may have occurred */
203 schedule_work( &serial_change_task
);
208 # define hx4700_suspend NULL
209 # define hx4700_resume NULL
213 hx4700_pxa_ll_pm_suspend(unsigned long resume_addr
)
218 /* Save the 13 words at 0xa0038000. */
219 for (p
= phys_to_virt(0xa0038000), i
= 0; i
< 13; i
++)
222 /* Save the first four words at 0xa0000000. */
223 for (p
= phys_to_virt(0xa0000000), i
= 0; i
< 4; i
++)
226 /* Set the first four words at 0xa0000000 to:
227 * resume address; MMU control; TLB base addr; domain id */
230 asm( "mrc\tp15, 0, %0, c1, c0, 0" : "=r" (tmp
) );
231 p
[1] = tmp
& ~(0x3987); /* mmu off */
233 asm( "mrc\tp15, 0, %0, c2, c0, 0" : "=r" (tmp
) );
234 p
[2] = tmp
; /* Shouldn't matter, since MMU will be off. */
236 asm( "mrc\tp15, 0, %0, c3, c0, 0" : "=r" (tmp
) );
237 p
[3] = tmp
; /* Shouldn't matter, since MMU will be off. */
239 /* Set PSPR to the checksum the HTC bootloader wants to see. */
240 for (csum
= 0, i
= 0; i
< 52; i
++) {
251 hx4700_pxa_ll_pm_resume(void)
256 /* Restore the first four words at 0xa0000000. */
257 for (p
= phys_to_virt(0xa0000000), i
= 0; i
< 4; i
++)
260 /* Restore the 13 words at 0xa0038000. */
261 for (p
= phys_to_virt(0xa0038000), i
= 0; i
< 13; i
++)
264 /* XXX Do we need to flush the cache? */
267 struct pxa_ll_pm_ops hx4700_ll_pm_ops
= {
268 .suspend
= hx4700_pxa_ll_pm_suspend
,
269 .resume
= hx4700_pxa_ll_pm_resume
,
274 hx4700_core_probe( struct platform_device
*pdev
)
276 unsigned int statusd
;
278 struct hx4700_core_funcs
*funcs
= pdev
->dev
.platform_data
;
282 printk( KERN_NOTICE
"hx4700 Core Hardware Driver\n" );
284 funcs
->udc_detect
= hx4700_udc_detect
;
286 egpios
= (volatile u_int16_t
*)ioremap_nocache( EGPIO_BASE
, sizeof *egpios
);
291 serial_irq
= asic3_irq_base( &hx4700_asic3
.dev
) + ASIC3_GPIOD_IRQ_BASE
293 statusd
= asic3_get_gpio_status_d( &hx4700_asic3
.dev
);
294 connected
= (statusd
& (1<<GPIOD_COM_DCD
)) != 0;
295 // printk( KERN_INFO "Serial: connected=%d\n", connected );
297 set_irq_type( serial_irq
, IRQT_FALLING
);
299 set_irq_type( serial_irq
, IRQT_RISING
);
301 if (request_irq( serial_irq
, serial_isr
, SA_INTERRUPT
,
302 "Hx4700 Serial", NULL
) != 0) {
303 printk( KERN_ERR
"Unable to configure serial port interrupt.\n" );
307 /* Is the stock HTC bootloader installed? */
309 bootldr
= (u32
*) ioremap(PXA_CS0_PHYS
, 1024 * 1024);
312 if (bootldr
[i
] == 0xe59f1360 && /* ldr r1, [pc, #864] ; power base */
313 bootldr
[i
+1] == 0xe5914008 && /* ldr r4, [r1, #8] ; PSPR */
314 bootldr
[i
+2] == 0xe1320004) { /* teq r2, r4 */
316 printk("Stock HTC WM2003 bootloader detected\n");
318 pxa_pm_set_ll_ops(&hx4700_ll_pm_ops
);
323 if (bootldr
[i
] == 0xe59f1354 && /* ldr r1, [pc, #852] ;power base */
324 bootldr
[i
+1] == 0xe5914008 && /* ldr r4, [r1, #8] ; PSPR */
325 bootldr
[i
+2] == 0xe1320004) { /* teq r2, r4 */
327 printk("Stock HTC WM2005 bootloader detected\n");
329 pxa_pm_set_ll_ops(&hx4700_ll_pm_ops
);
334 if (bootldr
[i
] == 0xe59f1354 && /* ldr r1, [pc, #852] ;power base */
335 bootldr
[i
+1] == 0xe5914008 && /* ldr r4, [r1, #8] ; PSPR */
336 bootldr
[i
+2] == 0xe1320004) { /* teq r2, r4 */
338 printk("Stock HTC WM2005 bootloader detected\n");
340 pxa_pm_set_ll_ops(&hx4700_ll_pm_ops
);
349 hx4700_core_remove( struct platform_device
*pdev
)
351 struct hx4700_core_funcs
*funcs
= pdev
->dev
.platform_data
;
354 iounmap( (void *)egpios
);
355 if (serial_irq
!= 0xffffffff)
356 free_irq( serial_irq
, NULL
);
357 funcs
->udc_detect
= NULL
;
361 static struct platform_driver hx4700_core_driver
= {
363 .name
= "hx4700-core",
365 .probe
= hx4700_core_probe
,
366 .remove
= hx4700_core_remove
,
367 .suspend
= hx4700_suspend
,
368 .resume
= hx4700_resume
,
372 hx4700_core_init( void )
374 return platform_driver_register( &hx4700_core_driver
);
379 hx4700_core_exit( void )
381 platform_driver_unregister( &hx4700_core_driver
);
384 module_init( hx4700_core_init
);
385 module_exit( hx4700_core_exit
);
387 MODULE_AUTHOR("Todd Blumer, SDG Systems, LLC");
388 MODULE_DESCRIPTION("hx4700 Core Hardware Driver");
389 MODULE_LICENSE("GPL");
391 /* vim600: set noexpandtab sw=8 ts=8 :*/