hh.org updates
[hh.org.git] / arch / arm / mach-pxa / hx4700 / hx4700_core.c
blob7f03d8714794c5849ef2d1fa5ecf96863a44fef3
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
7 */
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>
14 #include <linux/pm.h>
16 #include <asm/irq.h>
17 #include <asm/io.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;
32 u_int16_t egpio_reg;
34 static int htc_bootloader = 0; /* Is the stock HTC bootloader installed? */
35 static u32 save[4];
36 static u32 save2[13];
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
45 void
46 hx4700_egpio_enable( u_int16_t bits )
48 unsigned long flags;
50 local_irq_save(flags);
52 egpio_reg |= bits;
53 *egpios = egpio_reg;
55 local_irq_restore(flags);
57 EXPORT_SYMBOL(hx4700_egpio_enable);
59 void
60 hx4700_egpio_disable( u_int16_t bits )
62 unsigned long flags;
64 local_irq_save(flags);
66 egpio_reg &= ~bits;
67 *egpios = egpio_reg;
69 local_irq_restore(flags);
71 EXPORT_SYMBOL(hx4700_egpio_disable);
73 int
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;
82 static void
83 serial_change_task_handler( void *x )
85 unsigned int statusd;
86 int connected;
88 statusd = asic3_get_gpio_status_d( &hx4700_asic3.dev );
89 connected = (statusd & (1<<GPIOD_COM_DCD)) != 0;
90 if (connected)
91 set_irq_type( serial_irq, IRQT_FALLING );
92 else
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);
100 static int
101 serial_isr(int irq, void *dev_id)
103 schedule_delayed_work( &serial_change_task, 100 ); /* debounce */
104 return IRQ_HANDLED;
107 #ifdef CONFIG_PM
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) */
112 #if 0
113 asic3_set_clock_cdex(&hx4700_asic3.dev,
114 CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1, 0 | 0);
115 #endif
116 /* 0x20c2 is HTC clock value
117 * CLOCK_CDEX_SOURCE 2
118 * CLOCK_CDEX_SPI 0
119 * CLOCK_CDEX_OWM 0
121 * CLOCK_CDEX_PWM0 0
122 * CLOCK_CDEX_PWM1 0
123 * CLOCK_CDEX_LED0 1
124 * CLOCK_CDEX_LED1 1
126 * CLOCK_CDEX_LED2 0
127 * CLOCK_CDEX_SD_HOST 0
128 * CLOCK_CDEX_SD_BUS 0
129 * CLOCK_CDEX_SMBUS 0
131 * CLOCK_CDEX_CONTROL_CX 0
132 * CLOCK_CDEX_EX0 1
133 * CLOCK_CDEX_EX1 0
134 * */
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
146 PGSR0 = 0x080DC01C;
147 PGSR1 = 0x34CF0002;
148 PGSR2 = 0x0123C18C;
149 /* PGSR3 = 0x00104202; */
150 PGSR3 = 0x00100202;
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)) {
156 /* Set it */
157 PGSR2 |= (1U << 8);
158 } else {
159 /* Clear it */
160 PGSR2 &= ~(1U << 8);
162 /* Check for USB_CHARGE_RATE, GPIO 96 */
163 if(GPLR3 & (1 << 0)) {
164 /* Set it */
165 PGSR3 |= (1U << 0);
166 } else {
167 /* Clear it */
168 PGSR3 &= ~(1U << 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
177 * charged.
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);
196 return 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 );
205 return 0;
207 #else
208 # define hx4700_suspend NULL
209 # define hx4700_resume NULL
210 #endif
212 static void
213 hx4700_pxa_ll_pm_suspend(unsigned long resume_addr)
215 int i;
216 u32 csum, tmp, *p;
218 /* Save the 13 words at 0xa0038000. */
219 for (p = phys_to_virt(0xa0038000), i = 0; i < 13; i++)
220 save2[i] = p[i];
222 /* Save the first four words at 0xa0000000. */
223 for (p = phys_to_virt(0xa0000000), i = 0; i < 4; i++)
224 save[i] = p[i];
226 /* Set the first four words at 0xa0000000 to:
227 * resume address; MMU control; TLB base addr; domain id */
228 p[0] = resume_addr;
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++) {
241 tmp = p[i] & 0x1;
242 tmp = tmp << 31;
243 tmp |= tmp >> 1;
244 csum += tmp;
247 PSPR = csum;
250 static void
251 hx4700_pxa_ll_pm_resume(void)
253 int i;
254 u32 *p;
256 /* Restore the first four words at 0xa0000000. */
257 for (p = phys_to_virt(0xa0000000), i = 0; i < 4; i++)
258 p[i] = save[i];
260 /* Restore the 13 words at 0xa0038000. */
261 for (p = phys_to_virt(0xa0038000), i = 0; i < 13; i++)
262 p[i] = save2[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,
273 static int
274 hx4700_core_probe( struct platform_device *pdev )
276 unsigned int statusd;
277 int connected;
278 struct hx4700_core_funcs *funcs = pdev->dev.platform_data;
279 u32 *bootldr;
280 int i;
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 );
287 if (!egpios)
288 return -ENODEV;
290 /* UART IRQ */
291 serial_irq = asic3_irq_base( &hx4700_asic3.dev ) + ASIC3_GPIOD_IRQ_BASE
292 + GPIOD_COM_DCD;
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 );
296 if (connected)
297 set_irq_type( serial_irq, IRQT_FALLING );
298 else
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" );
304 return -ENODEV;
307 /* Is the stock HTC bootloader installed? */
309 bootldr = (u32 *) ioremap(PXA_CS0_PHYS, 1024 * 1024);
310 i = 0x000414dc / 4;
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");
317 htc_bootloader = 1;
318 pxa_pm_set_ll_ops(&hx4700_ll_pm_ops);
321 i = 0x0041d68 / 4;
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");
328 htc_bootloader = 1;
329 pxa_pm_set_ll_ops(&hx4700_ll_pm_ops);
332 i = 0x00041340 / 4;
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");
339 htc_bootloader = 1;
340 pxa_pm_set_ll_ops(&hx4700_ll_pm_ops);
343 iounmap(bootldr);
345 return 0;
348 static int
349 hx4700_core_remove( struct platform_device *pdev )
351 struct hx4700_core_funcs *funcs = pdev->dev.platform_data;
353 if (egpios != NULL)
354 iounmap( (void *)egpios );
355 if (serial_irq != 0xffffffff)
356 free_irq( serial_irq, NULL );
357 funcs->udc_detect = NULL;
358 return 0;
361 static struct platform_driver hx4700_core_driver = {
362 .driver = {
363 .name = "hx4700-core",
365 .probe = hx4700_core_probe,
366 .remove = hx4700_core_remove,
367 .suspend = hx4700_suspend,
368 .resume = hx4700_resume,
371 static int __init
372 hx4700_core_init( void )
374 return platform_driver_register( &hx4700_core_driver );
378 static void __exit
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 :*/