Commit | Line | Data |
---|---|---|
c28b6699 | 1 | // SPDX-License-Identifier: GPL-2.0 |
53c43c5c GKH |
2 | /* |
3 | * Mainly by David Woodhouse, somewhat modified by Jordan Crouse | |
4 | * | |
5 | * Copyright © 2006-2007 Red Hat, Inc. | |
6 | * Copyright © 2006-2007 Advanced Micro Devices, Inc. | |
7 | * Copyright © 2009 VIA Technology, Inc. | |
8 | * Copyright (c) 2010 Andres Salomon <dilinger@queued.net> | |
53c43c5c GKH |
9 | */ |
10 | ||
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
12 | ||
13 | #include <linux/cs5535.h> | |
2159fb37 | 14 | #include <linux/gpio/consumer.h> |
53c43c5c | 15 | #include <linux/delay.h> |
2159fb37 | 16 | #include <linux/i2c.h> |
53c43c5c GKH |
17 | #include <asm/olpc.h> |
18 | ||
19 | #include "olpc_dcon.h" | |
20 | ||
2159fb37 NK |
21 | enum dcon_gpios { |
22 | OLPC_DCON_STAT0, | |
23 | OLPC_DCON_STAT1, | |
24 | OLPC_DCON_IRQ, | |
25 | OLPC_DCON_LOAD, | |
26 | OLPC_DCON_BLANK, | |
27 | }; | |
28 | ||
2159fb37 NK |
29 | static const struct dcon_gpio gpios_asis[] = { |
30 | [OLPC_DCON_STAT0] = { .name = "dcon_stat0", .flags = GPIOD_ASIS }, | |
31 | [OLPC_DCON_STAT1] = { .name = "dcon_stat1", .flags = GPIOD_ASIS }, | |
32 | [OLPC_DCON_IRQ] = { .name = "dcon_irq", .flags = GPIOD_ASIS }, | |
33 | [OLPC_DCON_LOAD] = { .name = "dcon_load", .flags = GPIOD_ASIS }, | |
34 | [OLPC_DCON_BLANK] = { .name = "dcon_blank", .flags = GPIOD_ASIS }, | |
35 | }; | |
36 | ||
523275a8 | 37 | static struct gpio_desc *gpios[5]; |
2159fb37 | 38 | |
53c43c5c GKH |
39 | static int dcon_init_xo_1(struct dcon_priv *dcon) |
40 | { | |
41 | unsigned char lob; | |
2159fb37 | 42 | int ret, i; |
ae0a6d20 | 43 | const struct dcon_gpio *pin = &gpios_asis[0]; |
2159fb37 NK |
44 | |
45 | for (i = 0; i < ARRAY_SIZE(gpios_asis); i++) { | |
46 | gpios[i] = devm_gpiod_get(&dcon->client->dev, pin[i].name, | |
47 | pin[i].flags); | |
48 | if (IS_ERR(gpios[i])) { | |
49 | ret = PTR_ERR(gpios[i]); | |
50 | pr_err("failed to request %s GPIO: %d\n", pin[i].name, | |
51 | ret); | |
52 | return ret; | |
53 | } | |
53c43c5c GKH |
54 | } |
55 | ||
56 | /* Turn off the event enable for GPIO7 just to be safe */ | |
57 | cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE); | |
58 | ||
59 | /* | |
60 | * Determine the current state by reading the GPIO bit; earlier | |
61 | * stages of the boot process have established the state. | |
62 | * | |
63 | * Note that we read GPIO_OUTPUT_VAL rather than GPIO_READ_BACK here; | |
64 | * this is because OFW will disable input for the pin and set a value.. | |
65 | * READ_BACK will only contain a valid value if input is enabled and | |
66 | * then a value is set. So, future readings of the pin can use | |
67 | * READ_BACK, but the first one cannot. Awesome, huh? | |
68 | */ | |
69 | dcon->curr_src = cs5535_gpio_isset(OLPC_GPIO_DCON_LOAD, GPIO_OUTPUT_VAL) | |
70 | ? DCON_SOURCE_CPU | |
71 | : DCON_SOURCE_DCON; | |
72 | dcon->pending_src = dcon->curr_src; | |
73 | ||
74 | /* Set the directions for the GPIO pins */ | |
2159fb37 NK |
75 | gpiod_direction_input(gpios[OLPC_DCON_STAT0]); |
76 | gpiod_direction_input(gpios[OLPC_DCON_STAT1]); | |
77 | gpiod_direction_input(gpios[OLPC_DCON_IRQ]); | |
78 | gpiod_direction_input(gpios[OLPC_DCON_BLANK]); | |
79 | gpiod_direction_output(gpios[OLPC_DCON_LOAD], | |
80 | dcon->curr_src == DCON_SOURCE_CPU); | |
53c43c5c GKH |
81 | |
82 | /* Set up the interrupt mappings */ | |
83 | ||
84 | /* Set the IRQ to pair 2 */ | |
85 | cs5535_gpio_setup_event(OLPC_GPIO_DCON_IRQ, 2, 0); | |
86 | ||
87 | /* Enable group 2 to trigger the DCON interrupt */ | |
88 | cs5535_gpio_set_irq(2, DCON_IRQ); | |
89 | ||
90 | /* Select edge level for interrupt (in PIC) */ | |
91 | lob = inb(0x4d0); | |
92 | lob &= ~(1 << DCON_IRQ); | |
93 | outb(lob, 0x4d0); | |
94 | ||
95 | /* Register the interrupt handler */ | |
96 | if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", dcon)) { | |
97 | pr_err("failed to request DCON's irq\n"); | |
2159fb37 | 98 | return -EIO; |
53c43c5c GKH |
99 | } |
100 | ||
101 | /* Clear INV_EN for GPIO7 (DCONIRQ) */ | |
102 | cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_INVERT); | |
103 | ||
104 | /* Enable filter for GPIO12 (DCONBLANK) */ | |
105 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_FILTER); | |
106 | ||
107 | /* Disable filter for GPIO7 */ | |
108 | cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_FILTER); | |
109 | ||
110 | /* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */ | |
111 | cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_EVENT_COUNT); | |
112 | cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_EVENT_COUNT); | |
113 | ||
114 | /* Add GPIO12 to the Filter Event Pair #7 */ | |
115 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_FE7_SEL); | |
116 | ||
117 | /* Turn off negative Edge Enable for GPIO12 */ | |
118 | cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_EN); | |
119 | ||
120 | /* Enable negative Edge Enable for GPIO7 */ | |
121 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_EN); | |
122 | ||
123 | /* Zero the filter amount for Filter Event Pair #7 */ | |
124 | cs5535_gpio_set(0, GPIO_FLTR7_AMOUNT); | |
125 | ||
126 | /* Clear the negative edge status for GPIO7 and GPIO12 */ | |
127 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS); | |
128 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_STS); | |
129 | ||
130 | /* FIXME: Clear the positive status as well, just to be sure */ | |
131 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_POSITIVE_EDGE_STS); | |
132 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_POSITIVE_EDGE_STS); | |
133 | ||
134 | /* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */ | |
135 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE); | |
136 | cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_EVENTS_ENABLE); | |
137 | ||
138 | return 0; | |
53c43c5c GKH |
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 | */ | |
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); | |
166 | ||
167 | for (x = 0; x < 16; x++) { | |
168 | udelay(5); | |
169 | cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); | |
170 | udelay(5); | |
171 | cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); | |
172 | } | |
173 | udelay(5); | |
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); | |
178 | } | |
179 | ||
180 | static void dcon_set_dconload_1(int val) | |
181 | { | |
2159fb37 | 182 | gpiod_set_value(gpios[OLPC_DCON_LOAD], val); |
53c43c5c GKH |
183 | } |
184 | ||
185 | static int dcon_read_status_xo_1(u8 *status) | |
186 | { | |
2159fb37 NK |
187 | *status = gpiod_get_value(gpios[OLPC_DCON_STAT0]); |
188 | *status |= gpiod_get_value(gpios[OLPC_DCON_STAT1]) << 1; | |
53c43c5c GKH |
189 | |
190 | /* Clear the negative edge status for GPIO7 */ | |
191 | cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS); | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
196 | struct dcon_platform_data dcon_pdata_xo_1 = { | |
197 | .init = dcon_init_xo_1, | |
198 | .bus_stabilize_wiggle = dcon_wiggle_xo_1, | |
199 | .set_dconload = dcon_set_dconload_1, | |
200 | .read_status = dcon_read_status_xo_1, | |
201 | }; |