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 | */ | |
7637c925 AS |
13 | #include <linux/cs5535.h> |
14 | #include <linux/gpio.h> | |
eecb3e4e AS |
15 | #include <asm/olpc.h> |
16 | ||
17 | #include "olpc_dcon.h" | |
18 | ||
bbe963f1 | 19 | static int dcon_init_xo_1(struct dcon_priv *dcon) |
eecb3e4e | 20 | { |
eecb3e4e AS |
21 | unsigned char lob; |
22 | ||
7637c925 AS |
23 | if (gpio_request(OLPC_GPIO_DCON_STAT0, "OLPC-DCON")) { |
24 | printk(KERN_ERR "olpc-dcon: failed to request STAT0 GPIO\n"); | |
25 | return -EIO; | |
26 | } | |
27 | if (gpio_request(OLPC_GPIO_DCON_STAT1, "OLPC-DCON")) { | |
28 | printk(KERN_ERR "olpc-dcon: failed to request STAT1 GPIO\n"); | |
29 | goto err_gp_stat1; | |
30 | } | |
31 | if (gpio_request(OLPC_GPIO_DCON_IRQ, "OLPC-DCON")) { | |
32 | printk(KERN_ERR "olpc-dcon: failed to request IRQ GPIO\n"); | |
33 | goto err_gp_irq; | |
34 | } | |
35 | if (gpio_request(OLPC_GPIO_DCON_LOAD, "OLPC-DCON")) { | |
36 | printk(KERN_ERR "olpc-dcon: failed to request LOAD GPIO\n"); | |
37 | goto err_gp_load; | |
38 | } | |
39 | if (gpio_request(OLPC_GPIO_DCON_BLANK, "OLPC-DCON")) { | |
40 | printk(KERN_ERR "olpc-dcon: failed to request BLANK GPIO\n"); | |
41 | goto err_gp_blank; | |
eecb3e4e | 42 | } |
eecb3e4e AS |
43 | |
44 | /* Turn off the event enable for GPIO7 just to be safe */ | |
7637c925 AS |
45 | cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE); |
46 | ||
47 | /* | |
48 | * Determine the current state by reading the GPIO bit; earlier | |
49 | * stages of the boot process have established the state. | |
50 | * | |
51 | * Note that we read GPIO_OUPUT_VAL rather than GPIO_READ_BACK here; | |
52 | * this is because OFW will disable input for the pin and set a value.. | |
53 | * READ_BACK will only contain a valid value if input is enabled and | |
54 | * then a value is set. So, future readings of the pin can use | |
55 | * READ_BACK, but the first one cannot. Awesome, huh? | |
56 | */ | |
bbe963f1 | 57 | dcon->curr_src = cs5535_gpio_isset(OLPC_GPIO_DCON_LOAD, GPIO_OUTPUT_VAL) |
7637c925 AS |
58 | ? DCON_SOURCE_CPU |
59 | : DCON_SOURCE_DCON; | |
bbe963f1 | 60 | dcon->pending_src = dcon->curr_src; |
eecb3e4e AS |
61 | |
62 | /* Set the directions for the GPIO pins */ | |
7637c925 AS |
63 | gpio_direction_input(OLPC_GPIO_DCON_STAT0); |
64 | gpio_direction_input(OLPC_GPIO_DCON_STAT1); | |
65 | gpio_direction_input(OLPC_GPIO_DCON_IRQ); | |
66 | gpio_direction_input(OLPC_GPIO_DCON_BLANK); | |
67 | gpio_direction_output(OLPC_GPIO_DCON_LOAD, | |
bbe963f1 | 68 | dcon->curr_src == DCON_SOURCE_CPU); |
eecb3e4e AS |
69 | |
70 | /* Set up the interrupt mappings */ | |
71 | ||
72 | /* Set the IRQ to pair 2 */ | |
7637c925 | 73 | cs5535_gpio_setup_event(OLPC_GPIO_DCON_IRQ, 2, 0); |
eecb3e4e AS |
74 | |
75 | /* Enable group 2 to trigger the DCON interrupt */ | |
7637c925 | 76 | cs5535_gpio_set_irq(2, DCON_IRQ); |
eecb3e4e AS |
77 | |
78 | /* Select edge level for interrupt (in PIC) */ | |
79 | lob = inb(0x4d0); | |
80 | lob &= ~(1 << DCON_IRQ); | |
81 | outb(lob, 0x4d0); | |
82 | ||
25985edc | 83 | /* Register the interrupt handler */ |
bbe963f1 | 84 | if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", dcon)) { |
7637c925 AS |
85 | printk(KERN_ERR "olpc-dcon: failed to request DCON's irq\n"); |
86 | goto err_req_irq; | |
87 | } | |
eecb3e4e AS |
88 | |
89 | /* Clear INV_EN for GPIO7 (DCONIRQ) */ | |
7637c925 | 90 | cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_INVERT); |
eecb3e4e AS |
91 | |
92 | /* Enable filter for GPIO12 (DCONBLANK) */ | |
7637c925 | 93 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_FILTER); |
eecb3e4e AS |
94 | |
95 | /* Disable filter for GPIO7 */ | |
7637c925 | 96 | cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_FILTER); |
eecb3e4e AS |
97 | |
98 | /* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */ | |
7637c925 AS |
99 | cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_EVENT_COUNT); |
100 | cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_EVENT_COUNT); | |
eecb3e4e AS |
101 | |
102 | /* Add GPIO12 to the Filter Event Pair #7 */ | |
7637c925 | 103 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_FE7_SEL); |
eecb3e4e AS |
104 | |
105 | /* Turn off negative Edge Enable for GPIO12 */ | |
7637c925 | 106 | cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_EN); |
eecb3e4e AS |
107 | |
108 | /* Enable negative Edge Enable for GPIO7 */ | |
7637c925 | 109 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_EN); |
eecb3e4e AS |
110 | |
111 | /* Zero the filter amount for Filter Event Pair #7 */ | |
7637c925 | 112 | cs5535_gpio_set(0, GPIO_FLTR7_AMOUNT); |
eecb3e4e AS |
113 | |
114 | /* Clear the negative edge status for GPIO7 and GPIO12 */ | |
7637c925 AS |
115 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS); |
116 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_STS); | |
eecb3e4e AS |
117 | |
118 | /* FIXME: Clear the posiitive status as well, just to be sure */ | |
7637c925 AS |
119 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_POSITIVE_EDGE_STS); |
120 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_POSITIVE_EDGE_STS); | |
eecb3e4e AS |
121 | |
122 | /* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */ | |
7637c925 AS |
123 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE); |
124 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_EVENTS_ENABLE); | |
eecb3e4e AS |
125 | |
126 | return 0; | |
7637c925 AS |
127 | |
128 | err_req_irq: | |
129 | gpio_free(OLPC_GPIO_DCON_BLANK); | |
130 | err_gp_blank: | |
131 | gpio_free(OLPC_GPIO_DCON_LOAD); | |
132 | err_gp_load: | |
133 | gpio_free(OLPC_GPIO_DCON_IRQ); | |
134 | err_gp_irq: | |
135 | gpio_free(OLPC_GPIO_DCON_STAT1); | |
136 | err_gp_stat1: | |
137 | gpio_free(OLPC_GPIO_DCON_STAT0); | |
138 | return -EIO; | |
eecb3e4e AS |
139 | } |
140 | ||
141 | static void dcon_wiggle_xo_1(void) | |
142 | { | |
143 | int x; | |
144 | ||
145 | /* | |
146 | * According to HiMax, when powering the DCON up we should hold | |
147 | * SMB_DATA high for 8 SMB_CLK cycles. This will force the DCON | |
148 | * state machine to reset to a (sane) initial state. Mitch Bradley | |
149 | * did some testing and discovered that holding for 16 SMB_CLK cycles | |
150 | * worked a lot more reliably, so that's what we do here. | |
151 | * | |
152 | * According to the cs5536 spec, to set GPIO14 to SMB_CLK we must | |
153 | * simultaneously set AUX1 IN/OUT to GPIO14; ditto for SMB_DATA and | |
154 | * GPIO15. | |
155 | */ | |
7637c925 AS |
156 | cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); |
157 | cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_VAL); | |
158 | cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_ENABLE); | |
159 | cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_ENABLE); | |
160 | cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1); | |
161 | cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1); | |
162 | cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX2); | |
163 | cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX2); | |
164 | cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1); | |
165 | cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1); | |
eecb3e4e AS |
166 | |
167 | for (x = 0; x < 16; x++) { | |
168 | udelay(5); | |
7637c925 | 169 | cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); |
eecb3e4e | 170 | udelay(5); |
7637c925 | 171 | cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); |
eecb3e4e AS |
172 | } |
173 | udelay(5); | |
7637c925 AS |
174 | cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1); |
175 | cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1); | |
176 | cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1); | |
177 | cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1); | |
eecb3e4e AS |
178 | } |
179 | ||
180 | static void dcon_set_dconload_1(int val) | |
181 | { | |
7637c925 | 182 | gpio_set_value(OLPC_GPIO_DCON_LOAD, val); |
eecb3e4e AS |
183 | } |
184 | ||
7637c925 | 185 | static u8 dcon_read_status_xo_1(void) |
eecb3e4e | 186 | { |
7637c925 AS |
187 | u8 status; |
188 | ||
189 | status = gpio_get_value(OLPC_GPIO_DCON_STAT0); | |
190 | status |= gpio_get_value(OLPC_GPIO_DCON_STAT1) << 1; | |
191 | ||
eecb3e4e | 192 | /* Clear the negative edge status for GPIO7 */ |
7637c925 | 193 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS); |
eecb3e4e AS |
194 | |
195 | return status; | |
196 | } | |
197 | ||
097cd83a | 198 | struct dcon_platform_data dcon_pdata_xo_1 = { |
eecb3e4e AS |
199 | .init = dcon_init_xo_1, |
200 | .bus_stabilize_wiggle = dcon_wiggle_xo_1, | |
201 | .set_dconload = dcon_set_dconload_1, | |
202 | .read_status = dcon_read_status_xo_1, | |
203 | }; |