Commit | Line | Data |
---|---|---|
eecb3e4e AS |
1 | /* |
2 | * Mainly by David Woodhouse, somewhat modified by Jordan Crouse | |
3 | * | |
4 | * Copyright © 2006-2007 Red Hat, Inc. | |
5 | * Copyright © 2006-2007 Advanced Micro Devices, Inc. | |
6 | * Copyright © 2009 VIA Technology, Inc. | |
7 | * Copyright (c) 2010 Andres Salomon <dilinger@queued.net> | |
8 | * | |
9 | * This program is free software. You can redistribute it and/or | |
10 | * modify it under the terms of version 2 of the GNU General Public | |
11 | * License as published by the Free Software Foundation. | |
12 | */ | |
13 | ||
14 | #include <asm/olpc.h> | |
15 | ||
16 | #include "olpc_dcon.h" | |
17 | ||
18 | /* Base address of the GPIO registers */ | |
19 | static unsigned long gpio_base; | |
20 | ||
21 | /* | |
22 | * List of GPIOs that we care about: | |
23 | * (in) GPIO12 -- DCONBLANK | |
24 | * (in) GPIO[56] -- DCONSTAT[01] | |
25 | * (out) GPIO11 -- DCONLOAD | |
26 | */ | |
27 | ||
28 | #define IN_GPIOS ((1<<5) | (1<<6) | (1<<7) | (1<<12)) | |
29 | #define OUT_GPIOS (1<<11) | |
30 | ||
31 | static int dcon_init_xo_1(void) | |
32 | { | |
33 | unsigned long lo, hi; | |
34 | unsigned char lob; | |
35 | ||
36 | rdmsr(MSR_LBAR_GPIO, lo, hi); | |
37 | ||
38 | /* Check the mask and whether GPIO is enabled (sanity check) */ | |
39 | if (hi != 0x0000f001) { | |
40 | printk(KERN_ERR "GPIO not enabled -- cannot use DCON\n"); | |
41 | return -ENODEV; | |
42 | } | |
43 | ||
44 | /* Mask off the IO base address */ | |
45 | gpio_base = lo & 0x0000ff00; | |
46 | ||
47 | /* Turn off the event enable for GPIO7 just to be safe */ | |
48 | outl(1 << (16+7), gpio_base + GPIOx_EVNT_EN); | |
49 | ||
50 | /* Set the directions for the GPIO pins */ | |
51 | outl(OUT_GPIOS | (IN_GPIOS << 16), gpio_base + GPIOx_OUT_EN); | |
52 | outl(IN_GPIOS | (OUT_GPIOS << 16), gpio_base + GPIOx_IN_EN); | |
53 | ||
54 | /* Set up the interrupt mappings */ | |
55 | ||
56 | /* Set the IRQ to pair 2 */ | |
57 | geode_gpio_event_irq(OLPC_GPIO_DCON_IRQ, 2); | |
58 | ||
59 | /* Enable group 2 to trigger the DCON interrupt */ | |
60 | geode_gpio_set_irq(2, DCON_IRQ); | |
61 | ||
62 | /* Select edge level for interrupt (in PIC) */ | |
63 | lob = inb(0x4d0); | |
64 | lob &= ~(1 << DCON_IRQ); | |
65 | outb(lob, 0x4d0); | |
66 | ||
67 | /* Register the interupt handler */ | |
68 | if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", &dcon_driver)) | |
69 | return -EIO; | |
70 | ||
71 | /* Clear INV_EN for GPIO7 (DCONIRQ) */ | |
72 | outl((1<<(16+7)), gpio_base + GPIOx_INV_EN); | |
73 | ||
74 | /* Enable filter for GPIO12 (DCONBLANK) */ | |
75 | outl(1<<(12), gpio_base + GPIOx_IN_FLTR_EN); | |
76 | ||
77 | /* Disable filter for GPIO7 */ | |
78 | outl(1<<(16+7), gpio_base + GPIOx_IN_FLTR_EN); | |
79 | ||
80 | /* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */ | |
81 | ||
82 | outl(1<<(16+7), gpio_base + GPIOx_EVNTCNT_EN); | |
83 | outl(1<<(16+12), gpio_base + GPIOx_EVNTCNT_EN); | |
84 | ||
85 | /* Add GPIO12 to the Filter Event Pair #7 */ | |
86 | outb(12, gpio_base + GPIO_FE7_SEL); | |
87 | ||
88 | /* Turn off negative Edge Enable for GPIO12 */ | |
89 | outl(1<<(16+12), gpio_base + GPIOx_NEGEDGE_EN); | |
90 | ||
91 | /* Enable negative Edge Enable for GPIO7 */ | |
92 | outl(1<<7, gpio_base + GPIOx_NEGEDGE_EN); | |
93 | ||
94 | /* Zero the filter amount for Filter Event Pair #7 */ | |
95 | outw(0, gpio_base + GPIO_FLT7_AMNT); | |
96 | ||
97 | /* Clear the negative edge status for GPIO7 and GPIO12 */ | |
98 | outl((1<<7) | (1<<12), gpio_base+0x4c); | |
99 | ||
100 | /* FIXME: Clear the posiitive status as well, just to be sure */ | |
101 | outl((1<<7) | (1<<12), gpio_base+0x48); | |
102 | ||
103 | /* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */ | |
104 | outl((1<<(7))|(1<<12), gpio_base + GPIOx_EVNT_EN); | |
105 | ||
106 | /* Determine the current state by reading the GPIO bit */ | |
107 | /* Earlier stages of the boot process have established the state */ | |
108 | dcon_source = inl(gpio_base + GPIOx_OUT_VAL) & (1<<11) | |
109 | ? DCON_SOURCE_CPU | |
110 | : DCON_SOURCE_DCON; | |
111 | dcon_pending = dcon_source; | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | static void dcon_wiggle_xo_1(void) | |
117 | { | |
118 | int x; | |
119 | ||
120 | /* | |
121 | * According to HiMax, when powering the DCON up we should hold | |
122 | * SMB_DATA high for 8 SMB_CLK cycles. This will force the DCON | |
123 | * state machine to reset to a (sane) initial state. Mitch Bradley | |
124 | * did some testing and discovered that holding for 16 SMB_CLK cycles | |
125 | * worked a lot more reliably, so that's what we do here. | |
126 | * | |
127 | * According to the cs5536 spec, to set GPIO14 to SMB_CLK we must | |
128 | * simultaneously set AUX1 IN/OUT to GPIO14; ditto for SMB_DATA and | |
129 | * GPIO15. | |
130 | */ | |
131 | geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_VAL); | |
132 | geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_ENABLE); | |
133 | geode_gpio_clear(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1); | |
134 | geode_gpio_clear(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX2); | |
135 | geode_gpio_clear(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1); | |
136 | ||
137 | for (x = 0; x < 16; x++) { | |
138 | udelay(5); | |
139 | geode_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); | |
140 | udelay(5); | |
141 | geode_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); | |
142 | } | |
143 | udelay(5); | |
144 | geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1); | |
145 | geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1); | |
146 | } | |
147 | ||
148 | static void dcon_set_dconload_1(int val) | |
149 | { | |
150 | if (val) | |
151 | outl(1<<11, gpio_base + GPIOx_OUT_VAL); | |
152 | else | |
153 | outl(1<<(11 + 16), gpio_base + GPIOx_OUT_VAL); | |
154 | } | |
155 | ||
156 | static int dcon_read_status_xo_1(void) | |
157 | { | |
158 | int status = inl(gpio_base + GPIOx_READ_BACK) >> 5; | |
159 | ||
160 | /* Clear the negative edge status for GPIO7 */ | |
161 | outl(1 << 7, gpio_base + GPIOx_NEGEDGE_STS); | |
162 | ||
163 | return status; | |
164 | } | |
165 | ||
166 | static struct dcon_platform_data dcon_pdata_xo_1 = { | |
167 | .init = dcon_init_xo_1, | |
168 | .bus_stabilize_wiggle = dcon_wiggle_xo_1, | |
169 | .set_dconload = dcon_set_dconload_1, | |
170 | .read_status = dcon_read_status_xo_1, | |
171 | }; |