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