]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Hardware definitions for Compaq iPAQ H3xxx Handheld Computers | |
3 | * | |
4 | * Copyright 2000,1 Compaq Computer Corporation. | |
5 | * | |
6 | * Use consistent with the GNU GPL is permitted, | |
7 | * provided that this copyright notice is | |
8 | * preserved in its entirety in all copies and derived works. | |
9 | * | |
10 | * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, | |
11 | * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS | |
12 | * FITNESS FOR ANY PARTICULAR PURPOSE. | |
13 | * | |
14 | * Author: Jamey Hicks. | |
15 | * | |
16 | * History: | |
17 | * | |
18 | * 2001-10-?? Andrew Christian Added support for iPAQ H3800 | |
19 | * and abstracted EGPIO interface. | |
20 | * | |
21 | */ | |
1da177e4 LT |
22 | #include <linux/module.h> |
23 | #include <linux/init.h> | |
24 | #include <linux/kernel.h> | |
25 | #include <linux/tty.h> | |
26 | #include <linux/pm.h> | |
27 | #include <linux/device.h> | |
28 | #include <linux/mtd/mtd.h> | |
29 | #include <linux/mtd/partitions.h> | |
30 | #include <linux/serial_core.h> | |
31 | ||
32 | #include <asm/irq.h> | |
33 | #include <asm/hardware.h> | |
34 | #include <asm/mach-types.h> | |
35 | #include <asm/setup.h> | |
36 | ||
37 | #include <asm/mach/irq.h> | |
38 | #include <asm/mach/arch.h> | |
39 | #include <asm/mach/flash.h> | |
40 | #include <asm/mach/irda.h> | |
41 | #include <asm/mach/map.h> | |
42 | #include <asm/mach/serial_sa1100.h> | |
43 | ||
44 | #include <asm/arch/h3600.h> | |
45 | ||
46 | #if defined (CONFIG_SA1100_H3600) || defined (CONFIG_SA1100_H3100) | |
47 | #include <asm/arch/h3600_gpio.h> | |
48 | #endif | |
49 | ||
50 | #ifdef CONFIG_SA1100_H3800 | |
51 | #include <asm/arch/h3600_asic.h> | |
52 | #endif | |
53 | ||
54 | #include "generic.h" | |
55 | ||
56 | struct ipaq_model_ops ipaq_model_ops; | |
57 | EXPORT_SYMBOL(ipaq_model_ops); | |
58 | ||
59 | static struct mtd_partition h3xxx_partitions[] = { | |
60 | { | |
61 | .name = "H3XXX boot firmware", | |
62 | .size = 0x00040000, | |
63 | .offset = 0, | |
64 | .mask_flags = MTD_WRITEABLE, /* force read-only */ | |
65 | }, { | |
66 | #ifdef CONFIG_MTD_2PARTS_IPAQ | |
67 | .name = "H3XXX root jffs2", | |
68 | .size = MTDPART_SIZ_FULL, | |
69 | .offset = 0x00040000, | |
70 | #else | |
71 | .name = "H3XXX kernel", | |
72 | .size = 0x00080000, | |
73 | .offset = 0x00040000, | |
74 | }, { | |
75 | .name = "H3XXX params", | |
76 | .size = 0x00040000, | |
77 | .offset = 0x000C0000, | |
78 | }, { | |
79 | #ifdef CONFIG_JFFS2_FS | |
80 | .name = "H3XXX root jffs2", | |
81 | .size = MTDPART_SIZ_FULL, | |
82 | .offset = 0x00100000, | |
83 | #else | |
84 | .name = "H3XXX initrd", | |
85 | .size = 0x00100000, | |
86 | .offset = 0x00100000, | |
87 | }, { | |
88 | .name = "H3XXX root cramfs", | |
89 | .size = 0x00300000, | |
90 | .offset = 0x00200000, | |
91 | }, { | |
92 | .name = "H3XXX usr cramfs", | |
93 | .size = 0x00800000, | |
94 | .offset = 0x00500000, | |
95 | }, { | |
96 | .name = "H3XXX usr local", | |
97 | .size = MTDPART_SIZ_FULL, | |
98 | .offset = 0x00d00000, | |
99 | #endif | |
100 | #endif | |
101 | } | |
102 | }; | |
103 | ||
104 | static void h3xxx_set_vpp(int vpp) | |
105 | { | |
106 | assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); | |
107 | } | |
108 | ||
109 | static struct flash_platform_data h3xxx_flash_data = { | |
110 | .map_name = "cfi_probe", | |
111 | .set_vpp = h3xxx_set_vpp, | |
112 | .parts = h3xxx_partitions, | |
113 | .nr_parts = ARRAY_SIZE(h3xxx_partitions), | |
114 | }; | |
115 | ||
116 | static struct resource h3xxx_flash_resource = { | |
117 | .start = SA1100_CS0_PHYS, | |
118 | .end = SA1100_CS0_PHYS + SZ_32M - 1, | |
119 | .flags = IORESOURCE_MEM, | |
120 | }; | |
121 | ||
122 | /* | |
123 | * This turns the IRDA power on or off on the Compaq H3600 | |
124 | */ | |
125 | static int h3600_irda_set_power(struct device *dev, unsigned int state) | |
126 | { | |
127 | assign_h3600_egpio( IPAQ_EGPIO_IR_ON, state ); | |
128 | ||
129 | return 0; | |
130 | } | |
131 | ||
58c02ec4 | 132 | static void h3600_irda_set_speed(struct device *dev, unsigned int speed) |
1da177e4 LT |
133 | { |
134 | if (speed < 4000000) { | |
135 | clr_h3600_egpio(IPAQ_EGPIO_IR_FSEL); | |
136 | } else { | |
137 | set_h3600_egpio(IPAQ_EGPIO_IR_FSEL); | |
138 | } | |
139 | } | |
140 | ||
141 | static struct irda_platform_data h3600_irda_data = { | |
142 | .set_power = h3600_irda_set_power, | |
143 | .set_speed = h3600_irda_set_speed, | |
144 | }; | |
145 | ||
146 | static void h3xxx_mach_init(void) | |
147 | { | |
148 | sa11x0_set_flash_data(&h3xxx_flash_data, &h3xxx_flash_resource, 1); | |
149 | sa11x0_set_irda_data(&h3600_irda_data); | |
150 | } | |
151 | ||
152 | /* | |
153 | * low-level UART features | |
154 | */ | |
155 | ||
156 | static void h3600_uart_set_mctrl(struct uart_port *port, u_int mctrl) | |
157 | { | |
158 | if (port->mapbase == _Ser3UTCR0) { | |
159 | if (mctrl & TIOCM_RTS) | |
160 | GPCR = GPIO_H3600_COM_RTS; | |
161 | else | |
162 | GPSR = GPIO_H3600_COM_RTS; | |
163 | } | |
164 | } | |
165 | ||
166 | static u_int h3600_uart_get_mctrl(struct uart_port *port) | |
167 | { | |
168 | u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; | |
169 | ||
170 | if (port->mapbase == _Ser3UTCR0) { | |
171 | int gplr = GPLR; | |
172 | /* DCD and CTS bits are inverted in GPLR by RS232 transceiver */ | |
173 | if (gplr & GPIO_H3600_COM_DCD) | |
174 | ret &= ~TIOCM_CD; | |
175 | if (gplr & GPIO_H3600_COM_CTS) | |
176 | ret &= ~TIOCM_CTS; | |
177 | } | |
178 | ||
179 | return ret; | |
180 | } | |
181 | ||
182 | static void h3600_uart_pm(struct uart_port *port, u_int state, u_int oldstate) | |
183 | { | |
184 | if (port->mapbase == _Ser2UTCR0) { /* TODO: REMOVE THIS */ | |
185 | assign_h3600_egpio(IPAQ_EGPIO_IR_ON, !state); | |
186 | } else if (port->mapbase == _Ser3UTCR0) { | |
187 | assign_h3600_egpio(IPAQ_EGPIO_RS232_ON, !state); | |
188 | } | |
189 | } | |
190 | ||
191 | /* | |
192 | * Enable/Disable wake up events for this serial port. | |
193 | * Obviously, we only support this on the normal COM port. | |
194 | */ | |
195 | static int h3600_uart_set_wake(struct uart_port *port, u_int enable) | |
196 | { | |
197 | int err = -EINVAL; | |
198 | ||
199 | if (port->mapbase == _Ser3UTCR0) { | |
200 | if (enable) | |
201 | PWER |= PWER_GPIO23 | PWER_GPIO25; /* DCD and CTS */ | |
202 | else | |
203 | PWER &= ~(PWER_GPIO23 | PWER_GPIO25); /* DCD and CTS */ | |
204 | err = 0; | |
205 | } | |
206 | return err; | |
207 | } | |
208 | ||
209 | static struct sa1100_port_fns h3600_port_fns __initdata = { | |
210 | .set_mctrl = h3600_uart_set_mctrl, | |
211 | .get_mctrl = h3600_uart_get_mctrl, | |
212 | .pm = h3600_uart_pm, | |
213 | .set_wake = h3600_uart_set_wake, | |
214 | }; | |
215 | ||
216 | /* | |
217 | * helper for sa1100fb | |
218 | */ | |
219 | static void h3xxx_lcd_power(int enable) | |
220 | { | |
221 | assign_h3600_egpio(IPAQ_EGPIO_LCD_POWER, enable); | |
222 | } | |
223 | ||
224 | static struct map_desc h3600_io_desc[] __initdata = { | |
92519d82 DS |
225 | { /* static memory bank 2 CS#2 */ |
226 | .virtual = H3600_BANK_2_VIRT, | |
227 | .pfn = __phys_to_pfn(SA1100_CS2_PHYS), | |
228 | .length = 0x02800000, | |
229 | .type = MT_DEVICE | |
230 | }, { /* static memory bank 4 CS#4 */ | |
231 | .virtual = H3600_BANK_4_VIRT, | |
232 | .pfn = __phys_to_pfn(SA1100_CS4_PHYS), | |
233 | .length = 0x00800000, | |
234 | .type = MT_DEVICE | |
235 | }, { /* EGPIO 0 CS#5 */ | |
236 | .virtual = H3600_EGPIO_VIRT, | |
237 | .pfn = __phys_to_pfn(H3600_EGPIO_PHYS), | |
238 | .length = 0x01000000, | |
239 | .type = MT_DEVICE | |
240 | } | |
1da177e4 LT |
241 | }; |
242 | ||
243 | /* | |
244 | * Common map_io initialization | |
245 | */ | |
246 | ||
247 | static void __init h3xxx_map_io(void) | |
248 | { | |
249 | sa1100_map_io(); | |
250 | iotable_init(h3600_io_desc, ARRAY_SIZE(h3600_io_desc)); | |
251 | ||
252 | sa1100_register_uart_fns(&h3600_port_fns); | |
253 | sa1100_register_uart(0, 3); /* Common serial port */ | |
254 | // sa1100_register_uart(1, 1); /* Microcontroller on 3100/3600 */ | |
255 | ||
256 | /* Ensure those pins are outputs and driving low */ | |
257 | PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; | |
258 | PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); | |
259 | ||
260 | /* Configure suspend conditions */ | |
261 | PGSR = 0; | |
262 | PWER = PWER_GPIO0 | PWER_RTC; | |
263 | PCFR = PCFR_OPDE; | |
264 | PSDR = 0; | |
265 | ||
266 | sa1100fb_lcd_power = h3xxx_lcd_power; | |
267 | } | |
268 | ||
269 | static __inline__ void do_blank(int setp) | |
270 | { | |
271 | if (ipaq_model_ops.blank_callback) | |
272 | ipaq_model_ops.blank_callback(1-setp); | |
273 | } | |
274 | ||
275 | /************************* H3100 *************************/ | |
276 | ||
277 | #ifdef CONFIG_SA1100_H3100 | |
278 | ||
279 | #define H3100_EGPIO (*(volatile unsigned int *)H3600_EGPIO_VIRT) | |
280 | static unsigned int h3100_egpio = 0; | |
281 | ||
282 | static void h3100_control_egpio(enum ipaq_egpio_type x, int setp) | |
283 | { | |
284 | unsigned int egpio = 0; | |
285 | long gpio = 0; | |
286 | unsigned long flags; | |
287 | ||
288 | switch (x) { | |
289 | case IPAQ_EGPIO_LCD_POWER: | |
290 | egpio |= EGPIO_H3600_LCD_ON; | |
291 | gpio |= GPIO_H3100_LCD_3V_ON; | |
292 | do_blank(setp); | |
293 | break; | |
294 | case IPAQ_EGPIO_LCD_ENABLE: | |
295 | break; | |
296 | case IPAQ_EGPIO_CODEC_NRESET: | |
297 | egpio |= EGPIO_H3600_CODEC_NRESET; | |
298 | break; | |
299 | case IPAQ_EGPIO_AUDIO_ON: | |
300 | gpio |= GPIO_H3100_AUD_PWR_ON | |
301 | | GPIO_H3100_AUD_ON; | |
302 | break; | |
303 | case IPAQ_EGPIO_QMUTE: | |
304 | gpio |= GPIO_H3100_QMUTE; | |
305 | break; | |
306 | case IPAQ_EGPIO_OPT_NVRAM_ON: | |
307 | egpio |= EGPIO_H3600_OPT_NVRAM_ON; | |
308 | break; | |
309 | case IPAQ_EGPIO_OPT_ON: | |
310 | egpio |= EGPIO_H3600_OPT_ON; | |
311 | break; | |
312 | case IPAQ_EGPIO_CARD_RESET: | |
313 | egpio |= EGPIO_H3600_CARD_RESET; | |
314 | break; | |
315 | case IPAQ_EGPIO_OPT_RESET: | |
316 | egpio |= EGPIO_H3600_OPT_RESET; | |
317 | break; | |
318 | case IPAQ_EGPIO_IR_ON: | |
319 | gpio |= GPIO_H3100_IR_ON; | |
320 | break; | |
321 | case IPAQ_EGPIO_IR_FSEL: | |
322 | gpio |= GPIO_H3100_IR_FSEL; | |
323 | break; | |
324 | case IPAQ_EGPIO_RS232_ON: | |
325 | egpio |= EGPIO_H3600_RS232_ON; | |
326 | break; | |
327 | case IPAQ_EGPIO_VPP_ON: | |
328 | egpio |= EGPIO_H3600_VPP_ON; | |
329 | break; | |
330 | } | |
331 | ||
332 | if (egpio || gpio) { | |
333 | local_irq_save(flags); | |
334 | if (setp) { | |
335 | h3100_egpio |= egpio; | |
336 | GPSR = gpio; | |
337 | } else { | |
338 | h3100_egpio &= ~egpio; | |
339 | GPCR = gpio; | |
340 | } | |
341 | H3100_EGPIO = h3100_egpio; | |
342 | local_irq_restore(flags); | |
343 | } | |
344 | } | |
345 | ||
346 | static unsigned long h3100_read_egpio(void) | |
347 | { | |
348 | return h3100_egpio; | |
349 | } | |
350 | ||
351 | static int h3100_pm_callback(int req) | |
352 | { | |
353 | if (ipaq_model_ops.pm_callback_aux) | |
354 | return ipaq_model_ops.pm_callback_aux(req); | |
355 | return 0; | |
356 | } | |
357 | ||
358 | static struct ipaq_model_ops h3100_model_ops __initdata = { | |
359 | .generic_name = "3100", | |
360 | .control = h3100_control_egpio, | |
361 | .read = h3100_read_egpio, | |
362 | .pm_callback = h3100_pm_callback | |
363 | }; | |
364 | ||
365 | #define H3100_DIRECT_EGPIO (GPIO_H3100_BT_ON \ | |
366 | | GPIO_H3100_GPIO3 \ | |
367 | | GPIO_H3100_QMUTE \ | |
368 | | GPIO_H3100_LCD_3V_ON \ | |
369 | | GPIO_H3100_AUD_ON \ | |
370 | | GPIO_H3100_AUD_PWR_ON \ | |
371 | | GPIO_H3100_IR_ON \ | |
372 | | GPIO_H3100_IR_FSEL) | |
373 | ||
374 | static void __init h3100_map_io(void) | |
375 | { | |
376 | h3xxx_map_io(); | |
377 | ||
378 | /* Initialize h3100-specific values here */ | |
379 | GPCR = 0x0fffffff; /* All outputs are set low by default */ | |
380 | GPDR = GPIO_H3600_COM_RTS | GPIO_H3600_L3_CLOCK | | |
381 | GPIO_H3600_L3_MODE | GPIO_H3600_L3_DATA | | |
382 | GPIO_H3600_CLK_SET1 | GPIO_H3600_CLK_SET0 | | |
383 | H3100_DIRECT_EGPIO; | |
384 | ||
385 | /* Older bootldrs put GPIO2-9 in alternate mode on the | |
386 | assumption that they are used for video */ | |
387 | GAFR &= ~H3100_DIRECT_EGPIO; | |
388 | ||
389 | H3100_EGPIO = h3100_egpio; | |
390 | ipaq_model_ops = h3100_model_ops; | |
391 | } | |
392 | ||
393 | MACHINE_START(H3100, "Compaq iPAQ H3100") | |
e9dea0c6 RK |
394 | .phys_io = 0x80000000, |
395 | .io_pg_offst = ((0xf8000000) >> 18) & 0xfffc, | |
396 | .boot_params = 0xc0000100, | |
397 | .map_io = h3100_map_io, | |
398 | .init_irq = sa1100_init_irq, | |
1da177e4 LT |
399 | .timer = &sa1100_timer, |
400 | .init_machine = h3xxx_mach_init, | |
401 | MACHINE_END | |
402 | ||
403 | #endif /* CONFIG_SA1100_H3100 */ | |
404 | ||
405 | /************************* H3600 *************************/ | |
406 | ||
407 | #ifdef CONFIG_SA1100_H3600 | |
408 | ||
409 | #define H3600_EGPIO (*(volatile unsigned int *)H3600_EGPIO_VIRT) | |
410 | static unsigned int h3600_egpio = EGPIO_H3600_RS232_ON; | |
411 | ||
412 | static void h3600_control_egpio(enum ipaq_egpio_type x, int setp) | |
413 | { | |
414 | unsigned int egpio = 0; | |
415 | unsigned long flags; | |
416 | ||
417 | switch (x) { | |
418 | case IPAQ_EGPIO_LCD_POWER: | |
419 | egpio |= EGPIO_H3600_LCD_ON | | |
420 | EGPIO_H3600_LCD_PCI | | |
421 | EGPIO_H3600_LCD_5V_ON | | |
422 | EGPIO_H3600_LVDD_ON; | |
423 | do_blank(setp); | |
424 | break; | |
425 | case IPAQ_EGPIO_LCD_ENABLE: | |
426 | break; | |
427 | case IPAQ_EGPIO_CODEC_NRESET: | |
428 | egpio |= EGPIO_H3600_CODEC_NRESET; | |
429 | break; | |
430 | case IPAQ_EGPIO_AUDIO_ON: | |
431 | egpio |= EGPIO_H3600_AUD_AMP_ON | | |
432 | EGPIO_H3600_AUD_PWR_ON; | |
433 | break; | |
434 | case IPAQ_EGPIO_QMUTE: | |
435 | egpio |= EGPIO_H3600_QMUTE; | |
436 | break; | |
437 | case IPAQ_EGPIO_OPT_NVRAM_ON: | |
438 | egpio |= EGPIO_H3600_OPT_NVRAM_ON; | |
439 | break; | |
440 | case IPAQ_EGPIO_OPT_ON: | |
441 | egpio |= EGPIO_H3600_OPT_ON; | |
442 | break; | |
443 | case IPAQ_EGPIO_CARD_RESET: | |
444 | egpio |= EGPIO_H3600_CARD_RESET; | |
445 | break; | |
446 | case IPAQ_EGPIO_OPT_RESET: | |
447 | egpio |= EGPIO_H3600_OPT_RESET; | |
448 | break; | |
449 | case IPAQ_EGPIO_IR_ON: | |
450 | egpio |= EGPIO_H3600_IR_ON; | |
451 | break; | |
452 | case IPAQ_EGPIO_IR_FSEL: | |
453 | egpio |= EGPIO_H3600_IR_FSEL; | |
454 | break; | |
455 | case IPAQ_EGPIO_RS232_ON: | |
456 | egpio |= EGPIO_H3600_RS232_ON; | |
457 | break; | |
458 | case IPAQ_EGPIO_VPP_ON: | |
459 | egpio |= EGPIO_H3600_VPP_ON; | |
460 | break; | |
461 | } | |
462 | ||
463 | if (egpio) { | |
464 | local_irq_save(flags); | |
465 | if (setp) | |
466 | h3600_egpio |= egpio; | |
467 | else | |
468 | h3600_egpio &= ~egpio; | |
469 | H3600_EGPIO = h3600_egpio; | |
470 | local_irq_restore(flags); | |
471 | } | |
472 | } | |
473 | ||
474 | static unsigned long h3600_read_egpio(void) | |
475 | { | |
476 | return h3600_egpio; | |
477 | } | |
478 | ||
479 | static int h3600_pm_callback(int req) | |
480 | { | |
481 | if (ipaq_model_ops.pm_callback_aux) | |
482 | return ipaq_model_ops.pm_callback_aux(req); | |
483 | return 0; | |
484 | } | |
485 | ||
486 | static struct ipaq_model_ops h3600_model_ops __initdata = { | |
487 | .generic_name = "3600", | |
488 | .control = h3600_control_egpio, | |
489 | .read = h3600_read_egpio, | |
490 | .pm_callback = h3600_pm_callback | |
491 | }; | |
492 | ||
493 | static void __init h3600_map_io(void) | |
494 | { | |
495 | h3xxx_map_io(); | |
496 | ||
497 | /* Initialize h3600-specific values here */ | |
498 | ||
499 | GPCR = 0x0fffffff; /* All outputs are set low by default */ | |
500 | GPDR = GPIO_H3600_COM_RTS | GPIO_H3600_L3_CLOCK | | |
501 | GPIO_H3600_L3_MODE | GPIO_H3600_L3_DATA | | |
502 | GPIO_H3600_CLK_SET1 | GPIO_H3600_CLK_SET0 | | |
503 | GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | | |
504 | GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8; | |
505 | ||
506 | H3600_EGPIO = h3600_egpio; /* Maintains across sleep? */ | |
507 | ipaq_model_ops = h3600_model_ops; | |
508 | } | |
509 | ||
510 | MACHINE_START(H3600, "Compaq iPAQ H3600") | |
e9dea0c6 RK |
511 | .phys_io = 0x80000000, |
512 | .io_pg_offst = ((0xf8000000) >> 18) & 0xfffc, | |
513 | .boot_params = 0xc0000100, | |
514 | .map_io = h3600_map_io, | |
515 | .init_irq = sa1100_init_irq, | |
1da177e4 LT |
516 | .timer = &sa1100_timer, |
517 | .init_machine = h3xxx_mach_init, | |
518 | MACHINE_END | |
519 | ||
520 | #endif /* CONFIG_SA1100_H3600 */ | |
521 | ||
522 | #ifdef CONFIG_SA1100_H3800 | |
523 | ||
524 | #define SET_ASIC1(x) \ | |
525 | do {if (setp) { H3800_ASIC1_GPIO_OUT |= (x); } else { H3800_ASIC1_GPIO_OUT &= ~(x); }} while(0) | |
526 | ||
527 | #define SET_ASIC2(x) \ | |
528 | do {if (setp) { H3800_ASIC2_GPIOPIOD |= (x); } else { H3800_ASIC2_GPIOPIOD &= ~(x); }} while(0) | |
529 | ||
530 | #define CLEAR_ASIC1(x) \ | |
531 | do {if (setp) { H3800_ASIC1_GPIO_OUT &= ~(x); } else { H3800_ASIC1_GPIO_OUT |= (x); }} while(0) | |
532 | ||
533 | #define CLEAR_ASIC2(x) \ | |
534 | do {if (setp) { H3800_ASIC2_GPIOPIOD &= ~(x); } else { H3800_ASIC2_GPIOPIOD |= (x); }} while(0) | |
535 | ||
536 | ||
537 | /* | |
538 | On screen enable, we get | |
539 | ||
540 | h3800_video_power_on(1) | |
541 | LCD controller starts | |
542 | h3800_video_lcd_enable(1) | |
543 | ||
544 | On screen disable, we get | |
545 | ||
546 | h3800_video_lcd_enable(0) | |
547 | LCD controller stops | |
548 | h3800_video_power_on(0) | |
549 | */ | |
550 | ||
551 | ||
552 | static void h3800_video_power_on(int setp) | |
553 | { | |
554 | if (setp) { | |
555 | H3800_ASIC1_GPIO_OUT |= GPIO1_LCD_ON; | |
556 | msleep(30); | |
557 | H3800_ASIC1_GPIO_OUT |= GPIO1_VGL_ON; | |
558 | msleep(5); | |
559 | H3800_ASIC1_GPIO_OUT |= GPIO1_VGH_ON; | |
560 | msleep(50); | |
561 | H3800_ASIC1_GPIO_OUT |= GPIO1_LCD_5V_ON; | |
562 | msleep(5); | |
563 | } else { | |
564 | msleep(5); | |
565 | H3800_ASIC1_GPIO_OUT &= ~GPIO1_LCD_5V_ON; | |
566 | msleep(50); | |
567 | H3800_ASIC1_GPIO_OUT &= ~GPIO1_VGL_ON; | |
568 | msleep(5); | |
569 | H3800_ASIC1_GPIO_OUT &= ~GPIO1_VGH_ON; | |
570 | msleep(100); | |
571 | H3800_ASIC1_GPIO_OUT &= ~GPIO1_LCD_ON; | |
572 | } | |
573 | } | |
574 | ||
575 | static void h3800_video_lcd_enable(int setp) | |
576 | { | |
577 | if (setp) { | |
578 | msleep(17); // Wait one from before turning on | |
579 | H3800_ASIC1_GPIO_OUT |= GPIO1_LCD_PCI; | |
580 | } else { | |
581 | H3800_ASIC1_GPIO_OUT &= ~GPIO1_LCD_PCI; | |
582 | msleep(30); // Wait before turning off | |
583 | } | |
584 | } | |
585 | ||
586 | ||
587 | static void h3800_control_egpio(enum ipaq_egpio_type x, int setp) | |
588 | { | |
589 | switch (x) { | |
590 | case IPAQ_EGPIO_LCD_POWER: | |
591 | h3800_video_power_on(setp); | |
592 | break; | |
593 | case IPAQ_EGPIO_LCD_ENABLE: | |
594 | h3800_video_lcd_enable(setp); | |
595 | break; | |
596 | case IPAQ_EGPIO_CODEC_NRESET: | |
597 | case IPAQ_EGPIO_AUDIO_ON: | |
598 | case IPAQ_EGPIO_QMUTE: | |
599 | printk("%s: error - should not be called\n", __FUNCTION__); | |
600 | break; | |
601 | case IPAQ_EGPIO_OPT_NVRAM_ON: | |
602 | SET_ASIC2(GPIO2_OPT_ON_NVRAM); | |
603 | break; | |
604 | case IPAQ_EGPIO_OPT_ON: | |
605 | SET_ASIC2(GPIO2_OPT_ON); | |
606 | break; | |
607 | case IPAQ_EGPIO_CARD_RESET: | |
608 | SET_ASIC2(GPIO2_OPT_PCM_RESET); | |
609 | break; | |
610 | case IPAQ_EGPIO_OPT_RESET: | |
611 | SET_ASIC2(GPIO2_OPT_RESET); | |
612 | break; | |
613 | case IPAQ_EGPIO_IR_ON: | |
614 | CLEAR_ASIC1(GPIO1_IR_ON_N); | |
615 | break; | |
616 | case IPAQ_EGPIO_IR_FSEL: | |
617 | break; | |
618 | case IPAQ_EGPIO_RS232_ON: | |
619 | SET_ASIC1(GPIO1_RS232_ON); | |
620 | break; | |
621 | case IPAQ_EGPIO_VPP_ON: | |
622 | H3800_ASIC2_FlashWP_VPP_ON = setp; | |
623 | break; | |
624 | } | |
625 | } | |
626 | ||
627 | static unsigned long h3800_read_egpio(void) | |
628 | { | |
629 | return H3800_ASIC1_GPIO_OUT | (H3800_ASIC2_GPIOPIOD << 16); | |
630 | } | |
631 | ||
632 | /* We need to fix ASIC2 GPIO over suspend/resume. At the moment, | |
633 | it doesn't appear that ASIC1 GPIO has the same problem */ | |
634 | ||
635 | static int h3800_pm_callback(int req) | |
636 | { | |
637 | static u16 asic1_data; | |
638 | static u16 asic2_data; | |
639 | int result = 0; | |
640 | ||
641 | printk("%s %d\n", __FUNCTION__, req); | |
642 | ||
643 | switch (req) { | |
644 | case PM_RESUME: | |
645 | MSC2 = (MSC2 & 0x0000ffff) | 0xE4510000; /* Set MSC2 correctly */ | |
646 | ||
647 | H3800_ASIC2_GPIOPIOD = asic2_data; | |
648 | H3800_ASIC2_GPIODIR = GPIO2_PEN_IRQ | |
649 | | GPIO2_SD_DETECT | |
650 | | GPIO2_EAR_IN_N | |
651 | | GPIO2_USB_DETECT_N | |
652 | | GPIO2_SD_CON_SLT; | |
653 | ||
654 | H3800_ASIC1_GPIO_OUT = asic1_data; | |
655 | ||
656 | if (ipaq_model_ops.pm_callback_aux) | |
657 | result = ipaq_model_ops.pm_callback_aux(req); | |
658 | break; | |
659 | ||
660 | case PM_SUSPEND: | |
661 | if (ipaq_model_ops.pm_callback_aux && | |
662 | ((result = ipaq_model_ops.pm_callback_aux(req)) != 0)) | |
663 | return result; | |
664 | ||
665 | asic1_data = H3800_ASIC1_GPIO_OUT; | |
666 | asic2_data = H3800_ASIC2_GPIOPIOD; | |
667 | break; | |
668 | default: | |
669 | printk("%s: unrecognized PM callback\n", __FUNCTION__); | |
670 | break; | |
671 | } | |
672 | return result; | |
673 | } | |
674 | ||
675 | static struct ipaq_model_ops h3800_model_ops __initdata = { | |
676 | .generic_name = "3800", | |
677 | .control = h3800_control_egpio, | |
678 | .read = h3800_read_egpio, | |
679 | .pm_callback = h3800_pm_callback | |
680 | }; | |
681 | ||
682 | #define MAX_ASIC_ISR_LOOPS 20 | |
683 | ||
684 | /* The order of these is important - see #include <asm/arch/irqs.h> */ | |
685 | static u32 kpio_irq_mask[] = { | |
686 | KPIO_KEY_ALL, | |
687 | KPIO_SPI_INT, | |
688 | KPIO_OWM_INT, | |
689 | KPIO_ADC_INT, | |
690 | KPIO_UART_0_INT, | |
691 | KPIO_UART_1_INT, | |
692 | KPIO_TIMER_0_INT, | |
693 | KPIO_TIMER_1_INT, | |
694 | KPIO_TIMER_2_INT | |
695 | }; | |
696 | ||
697 | static u32 gpio_irq_mask[] = { | |
698 | GPIO2_PEN_IRQ, | |
699 | GPIO2_SD_DETECT, | |
700 | GPIO2_EAR_IN_N, | |
701 | GPIO2_USB_DETECT_N, | |
702 | GPIO2_SD_CON_SLT, | |
703 | }; | |
704 | ||
705 | static void h3800_IRQ_demux(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) | |
706 | { | |
707 | int i; | |
708 | ||
709 | if (0) printk("%s: interrupt received\n", __FUNCTION__); | |
710 | ||
711 | desc->chip->ack(irq); | |
712 | ||
713 | for (i = 0; i < MAX_ASIC_ISR_LOOPS && (GPLR & GPIO_H3800_ASIC); i++) { | |
714 | u32 irq; | |
715 | int j; | |
716 | ||
717 | /* KPIO */ | |
718 | irq = H3800_ASIC2_KPIINTFLAG; | |
719 | if (0) printk("%s KPIO 0x%08X\n", __FUNCTION__, irq); | |
720 | for (j = 0; j < H3800_KPIO_IRQ_COUNT; j++) | |
721 | if (irq & kpio_irq_mask[j]) | |
722 | do_edge_IRQ(H3800_KPIO_IRQ_COUNT + j, irq_desc + H3800_KPIO_IRQ_COUNT + j, regs); | |
723 | ||
724 | /* GPIO2 */ | |
725 | irq = H3800_ASIC2_GPIINTFLAG; | |
726 | if (0) printk("%s GPIO 0x%08X\n", __FUNCTION__, irq); | |
727 | for (j = 0; j < H3800_GPIO_IRQ_COUNT; j++) | |
728 | if (irq & gpio_irq_mask[j]) | |
729 | do_edge_IRQ(H3800_GPIO_IRQ_COUNT + j, irq_desc + H3800_GPIO_IRQ_COUNT + j , regs); | |
730 | } | |
731 | ||
732 | if (i >= MAX_ASIC_ISR_LOOPS) | |
733 | printk("%s: interrupt processing overrun\n", __FUNCTION__); | |
734 | ||
735 | /* For level-based interrupts */ | |
736 | desc->chip->unmask(irq); | |
737 | ||
738 | } | |
739 | ||
740 | static struct irqaction h3800_irq = { | |
741 | .name = "h3800_asic", | |
742 | .handler = h3800_IRQ_demux, | |
52e405ea | 743 | .flags = IRQF_DISABLED | IRQF_TIMER, |
1da177e4 LT |
744 | }; |
745 | ||
746 | u32 kpio_int_shadow = 0; | |
747 | ||
748 | ||
749 | /* mask_ack <- IRQ is first serviced. | |
750 | mask <- IRQ is disabled. | |
751 | unmask <- IRQ is enabled | |
752 | ||
753 | The INTCLR registers are poorly documented. I believe that writing | |
754 | a "1" to the register clears the specific interrupt, but the documentation | |
755 | indicates writing a "0" clears the interrupt. In any case, they shouldn't | |
756 | be read (that's the INTFLAG register) | |
757 | */ | |
758 | ||
759 | static void h3800_mask_ack_kpio_irq(unsigned int irq) | |
760 | { | |
761 | u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START]; | |
762 | kpio_int_shadow &= ~mask; | |
763 | H3800_ASIC2_KPIINTSTAT = kpio_int_shadow; | |
764 | H3800_ASIC2_KPIINTCLR = mask; | |
765 | } | |
766 | ||
767 | static void h3800_mask_kpio_irq(unsigned int irq) | |
768 | { | |
769 | u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START]; | |
770 | kpio_int_shadow &= ~mask; | |
771 | H3800_ASIC2_KPIINTSTAT = kpio_int_shadow; | |
772 | } | |
773 | ||
774 | static void h3800_unmask_kpio_irq(unsigned int irq) | |
775 | { | |
776 | u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START]; | |
777 | kpio_int_shadow |= mask; | |
778 | H3800_ASIC2_KPIINTSTAT = kpio_int_shadow; | |
779 | } | |
780 | ||
781 | static void h3800_mask_ack_gpio_irq(unsigned int irq) | |
782 | { | |
783 | u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START]; | |
784 | H3800_ASIC2_GPIINTSTAT &= ~mask; | |
785 | H3800_ASIC2_GPIINTCLR = mask; | |
786 | } | |
787 | ||
788 | static void h3800_mask_gpio_irq(unsigned int irq) | |
789 | { | |
790 | u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START]; | |
791 | H3800_ASIC2_GPIINTSTAT &= ~mask; | |
792 | } | |
793 | ||
794 | static void h3800_unmask_gpio_irq(unsigned int irq) | |
795 | { | |
796 | u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START]; | |
797 | H3800_ASIC2_GPIINTSTAT |= mask; | |
798 | } | |
799 | ||
800 | static void __init h3800_init_irq(void) | |
801 | { | |
802 | int i; | |
803 | ||
804 | /* Initialize standard IRQs */ | |
805 | sa1100_init_irq(); | |
806 | ||
807 | /* Disable all IRQs and set up clock */ | |
808 | H3800_ASIC2_KPIINTSTAT = 0; /* Disable all interrupts */ | |
809 | H3800_ASIC2_GPIINTSTAT = 0; | |
810 | ||
811 | H3800_ASIC2_KPIINTCLR = 0; /* Clear all KPIO interrupts */ | |
812 | H3800_ASIC2_GPIINTCLR = 0; /* Clear all GPIO interrupts */ | |
813 | ||
814 | // H3800_ASIC2_KPIINTCLR = 0xffff; /* Clear all KPIO interrupts */ | |
815 | // H3800_ASIC2_GPIINTCLR = 0xffff; /* Clear all GPIO interrupts */ | |
816 | ||
817 | H3800_ASIC2_CLOCK_Enable |= ASIC2_CLOCK_EX0; /* 32 kHZ crystal on */ | |
818 | H3800_ASIC2_INTR_ClockPrescale |= ASIC2_INTCPS_SET; | |
819 | H3800_ASIC2_INTR_ClockPrescale = ASIC2_INTCPS_CPS(0x0e) | ASIC2_INTCPS_SET; | |
820 | H3800_ASIC2_INTR_TimerSet = 1; | |
821 | ||
822 | #if 0 | |
823 | for (i = 0; i < H3800_KPIO_IRQ_COUNT; i++) { | |
824 | int irq = i + H3800_KPIO_IRQ_START; | |
825 | irq_desc[irq].valid = 1; | |
826 | irq_desc[irq].probe_ok = 1; | |
827 | set_irq_chip(irq, &h3800_kpio_irqchip); | |
828 | } | |
829 | ||
830 | for (i = 0; i < H3800_GPIO_IRQ_COUNT; i++) { | |
831 | int irq = i + H3800_GPIO_IRQ_START; | |
832 | irq_desc[irq].valid = 1; | |
833 | irq_desc[irq].probe_ok = 1; | |
834 | set_irq_chip(irq, &h3800_gpio_irqchip); | |
835 | } | |
836 | #endif | |
837 | set_irq_type(IRQ_GPIO_H3800_ASIC, IRQT_RISING); | |
119c641c | 838 | set_irq_chained_handler(IRQ_GPIO_H3800_ASIC, h3800_IRQ_demux); |
1da177e4 LT |
839 | } |
840 | ||
841 | ||
842 | #define ASIC1_OUTPUTS 0x7fff /* First 15 bits are used */ | |
843 | ||
844 | static void __init h3800_map_io(void) | |
845 | { | |
846 | h3xxx_map_io(); | |
847 | ||
848 | /* Add wakeup on AC plug/unplug */ | |
849 | PWER |= PWER_GPIO12; | |
850 | ||
851 | /* Initialize h3800-specific values here */ | |
852 | GPCR = 0x0fffffff; /* All outputs are set low by default */ | |
853 | GAFR = GPIO_H3800_CLK_OUT | | |
854 | GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | | |
855 | GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8; | |
856 | GPDR = GPIO_H3800_CLK_OUT | | |
857 | GPIO_H3600_COM_RTS | GPIO_H3600_L3_CLOCK | | |
858 | GPIO_H3600_L3_MODE | GPIO_H3600_L3_DATA | | |
859 | GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | | |
860 | GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8; | |
861 | TUCR = TUCR_3_6864MHz; /* Seems to be used only for the Bluetooth UART */ | |
862 | ||
863 | /* Fix the memory bus */ | |
864 | MSC2 = (MSC2 & 0x0000ffff) | 0xE4510000; | |
865 | ||
866 | /* Set up ASIC #1 */ | |
867 | H3800_ASIC1_GPIO_DIR = ASIC1_OUTPUTS; /* All outputs */ | |
868 | H3800_ASIC1_GPIO_MASK = ASIC1_OUTPUTS; /* No interrupts */ | |
869 | H3800_ASIC1_GPIO_SLEEP_MASK = ASIC1_OUTPUTS; | |
870 | H3800_ASIC1_GPIO_SLEEP_DIR = ASIC1_OUTPUTS; | |
871 | H3800_ASIC1_GPIO_SLEEP_OUT = GPIO1_EAR_ON_N; | |
872 | H3800_ASIC1_GPIO_BATT_FAULT_DIR = ASIC1_OUTPUTS; | |
873 | H3800_ASIC1_GPIO_BATT_FAULT_OUT = GPIO1_EAR_ON_N; | |
874 | ||
875 | H3800_ASIC1_GPIO_OUT = GPIO1_IR_ON_N | |
876 | | GPIO1_RS232_ON | |
877 | | GPIO1_EAR_ON_N; | |
878 | ||
879 | /* Set up ASIC #2 */ | |
880 | H3800_ASIC2_GPIOPIOD = GPIO2_IN_Y1_N | GPIO2_IN_X1_N; | |
881 | H3800_ASIC2_GPOBFSTAT = GPIO2_IN_Y1_N | GPIO2_IN_X1_N; | |
882 | ||
883 | H3800_ASIC2_GPIODIR = GPIO2_PEN_IRQ | |
884 | | GPIO2_SD_DETECT | |
885 | | GPIO2_EAR_IN_N | |
886 | | GPIO2_USB_DETECT_N | |
887 | | GPIO2_SD_CON_SLT; | |
888 | ||
889 | /* TODO : Set sleep states & battery fault states */ | |
890 | ||
891 | /* Clear VPP Enable */ | |
892 | H3800_ASIC2_FlashWP_VPP_ON = 0; | |
893 | ipaq_model_ops = h3800_model_ops; | |
894 | } | |
895 | ||
896 | MACHINE_START(H3800, "Compaq iPAQ H3800") | |
e9dea0c6 RK |
897 | .phys_io = 0x80000000, |
898 | .io_pg_offst = ((0xf8000000) >> 18) & 0xfffc, | |
899 | .boot_params = 0xc0000100, | |
900 | .map_io = h3800_map_io, | |
901 | .init_irq = h3800_init_irq, | |
1da177e4 LT |
902 | .timer = &sa1100_timer, |
903 | .init_machine = h3xxx_mach_init, | |
904 | MACHINE_END | |
905 | ||
906 | #endif /* CONFIG_SA1100_H3800 */ |