]>
Commit | Line | Data |
---|---|---|
f93022c3 JR |
1 | /* |
2 | * Copyright (C) 2011 Jana Rapava <fermata7@gmail.com> | |
3 | * Copyright (C) 2011 CompuLab, Ltd. <www.compulab.co.il> | |
4 | * | |
5 | * Authors: Jana Rapava <fermata7@gmail.com> | |
6 | * Igor Grinberg <grinberg@compulab.co.il> | |
7 | * | |
8 | * Based on: | |
9 | * linux/drivers/usb/otg/ulpi.c | |
10 | * Generic ULPI USB transceiver support | |
11 | * | |
12 | * Original Copyright follow: | |
13 | * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de> | |
14 | * | |
15 | * Based on sources from | |
16 | * | |
17 | * Sascha Hauer <s.hauer@pengutronix.de> | |
18 | * Freescale Semiconductors | |
19 | * | |
1a459660 | 20 | * SPDX-License-Identifier: GPL-2.0+ |
f93022c3 JR |
21 | */ |
22 | ||
23 | #include <common.h> | |
24 | #include <exports.h> | |
25 | #include <usb/ulpi.h> | |
26 | ||
27 | #define ULPI_ID_REGS_COUNT 4 | |
28 | #define ULPI_TEST_VALUE 0x55 /* 0x55 == 0b01010101 */ | |
29 | ||
30 | static struct ulpi_regs *ulpi = (struct ulpi_regs *)0; | |
31 | ||
3e6e809f | 32 | static int ulpi_integrity_check(struct ulpi_viewport *ulpi_vp) |
f93022c3 | 33 | { |
4256101f IG |
34 | u32 val, tval = ULPI_TEST_VALUE; |
35 | int err, i; | |
f93022c3 JR |
36 | |
37 | /* Use the 'special' test value to check all bits */ | |
38 | for (i = 0; i < 2; i++, tval <<= 1) { | |
3e6e809f | 39 | err = ulpi_write(ulpi_vp, &ulpi->scratch, tval); |
f93022c3 JR |
40 | if (err) |
41 | return err; | |
42 | ||
3e6e809f | 43 | val = ulpi_read(ulpi_vp, &ulpi->scratch); |
f93022c3 JR |
44 | if (val != tval) { |
45 | printf("ULPI integrity check failed\n"); | |
46 | return val; | |
47 | } | |
48 | } | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
3e6e809f | 53 | int ulpi_init(struct ulpi_viewport *ulpi_vp) |
f93022c3 JR |
54 | { |
55 | u32 val, id = 0; | |
56 | u8 *reg = &ulpi->product_id_high; | |
57 | int i; | |
58 | ||
59 | /* Assemble ID from four ULPI ID registers (8 bits each). */ | |
60 | for (i = 0; i < ULPI_ID_REGS_COUNT; i++) { | |
3e6e809f | 61 | val = ulpi_read(ulpi_vp, reg - i); |
f93022c3 JR |
62 | if (val == ULPI_ERROR) |
63 | return val; | |
64 | ||
65 | id = (id << 8) | val; | |
66 | } | |
67 | ||
68 | /* Split ID into vendor and product ID. */ | |
69 | debug("ULPI transceiver ID 0x%04x:0x%04x\n", id >> 16, id & 0xffff); | |
70 | ||
3e6e809f | 71 | return ulpi_integrity_check(ulpi_vp); |
f93022c3 JR |
72 | } |
73 | ||
3e6e809f | 74 | int ulpi_select_transceiver(struct ulpi_viewport *ulpi_vp, unsigned speed) |
f93022c3 | 75 | { |
1113a79b | 76 | u32 tspeed = ULPI_FC_FULL_SPEED; |
f93022c3 JR |
77 | u32 val; |
78 | ||
79 | switch (speed) { | |
80 | case ULPI_FC_HIGH_SPEED: | |
81 | case ULPI_FC_FULL_SPEED: | |
82 | case ULPI_FC_LOW_SPEED: | |
83 | case ULPI_FC_FS4LS: | |
84 | tspeed = speed; | |
85 | break; | |
86 | default: | |
cf9f95f2 IG |
87 | printf("ULPI: %s: wrong transceiver speed specified: %u, " |
88 | "falling back to full speed\n", __func__, speed); | |
f93022c3 JR |
89 | } |
90 | ||
3e6e809f | 91 | val = ulpi_read(ulpi_vp, &ulpi->function_ctrl); |
f93022c3 JR |
92 | if (val == ULPI_ERROR) |
93 | return val; | |
94 | ||
95 | /* clear the previous speed setting */ | |
96 | val = (val & ~ULPI_FC_XCVRSEL_MASK) | tspeed; | |
97 | ||
3e6e809f | 98 | return ulpi_write(ulpi_vp, &ulpi->function_ctrl, val); |
f93022c3 JR |
99 | } |
100 | ||
141288b3 | 101 | int ulpi_set_vbus(struct ulpi_viewport *ulpi_vp, int on, int ext_power) |
f93022c3 JR |
102 | { |
103 | u32 flags = ULPI_OTG_DRVVBUS; | |
104 | u8 *reg = on ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; | |
105 | ||
106 | if (ext_power) | |
107 | flags |= ULPI_OTG_DRVVBUS_EXT; | |
f93022c3 | 108 | |
3e6e809f | 109 | return ulpi_write(ulpi_vp, reg, flags); |
f93022c3 JR |
110 | } |
111 | ||
141288b3 LS |
112 | int ulpi_set_vbus_indicator(struct ulpi_viewport *ulpi_vp, int external, |
113 | int passthu, int complement) | |
114 | { | |
115 | u32 flags, val; | |
116 | u8 *reg; | |
117 | ||
118 | reg = external ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; | |
119 | val = ulpi_write(ulpi_vp, reg, ULPI_OTG_EXTVBUSIND); | |
120 | if (val) | |
121 | return val; | |
122 | ||
123 | flags = passthu ? ULPI_IFACE_PASSTHRU : 0; | |
124 | flags |= complement ? ULPI_IFACE_EXTVBUS_COMPLEMENT : 0; | |
125 | ||
126 | val = ulpi_read(ulpi_vp, &ulpi->iface_ctrl); | |
127 | if (val == ULPI_ERROR) | |
128 | return val; | |
129 | ||
130 | val = val & ~(ULPI_IFACE_PASSTHRU & ULPI_IFACE_EXTVBUS_COMPLEMENT); | |
131 | val |= flags; | |
132 | val = ulpi_write(ulpi_vp, &ulpi->iface_ctrl, val); | |
133 | if (val) | |
134 | return val; | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
3e6e809f | 139 | int ulpi_set_pd(struct ulpi_viewport *ulpi_vp, int enable) |
f93022c3 JR |
140 | { |
141 | u32 val = ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN; | |
142 | u8 *reg = enable ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; | |
143 | ||
3e6e809f | 144 | return ulpi_write(ulpi_vp, reg, val); |
f93022c3 JR |
145 | } |
146 | ||
3e6e809f | 147 | int ulpi_opmode_sel(struct ulpi_viewport *ulpi_vp, unsigned opmode) |
f93022c3 | 148 | { |
1113a79b | 149 | u32 topmode = ULPI_FC_OPMODE_NORMAL; |
f93022c3 JR |
150 | u32 val; |
151 | ||
152 | switch (opmode) { | |
153 | case ULPI_FC_OPMODE_NORMAL: | |
154 | case ULPI_FC_OPMODE_NONDRIVING: | |
155 | case ULPI_FC_OPMODE_DISABLE_NRZI: | |
156 | case ULPI_FC_OPMODE_NOSYNC_NOEOP: | |
157 | topmode = opmode; | |
158 | break; | |
159 | default: | |
cf9f95f2 IG |
160 | printf("ULPI: %s: wrong OpMode specified: %u, " |
161 | "falling back to OpMode Normal\n", __func__, opmode); | |
f93022c3 JR |
162 | } |
163 | ||
3e6e809f | 164 | val = ulpi_read(ulpi_vp, &ulpi->function_ctrl); |
f93022c3 JR |
165 | if (val == ULPI_ERROR) |
166 | return val; | |
167 | ||
168 | /* clear the previous opmode setting */ | |
169 | val = (val & ~ULPI_FC_OPMODE_MASK) | topmode; | |
170 | ||
3e6e809f | 171 | return ulpi_write(ulpi_vp, &ulpi->function_ctrl, val); |
f93022c3 JR |
172 | } |
173 | ||
3e6e809f | 174 | int ulpi_serial_mode_enable(struct ulpi_viewport *ulpi_vp, unsigned smode) |
f93022c3 JR |
175 | { |
176 | switch (smode) { | |
177 | case ULPI_IFACE_6_PIN_SERIAL_MODE: | |
178 | case ULPI_IFACE_3_PIN_SERIAL_MODE: | |
179 | break; | |
180 | default: | |
cf9f95f2 IG |
181 | printf("ULPI: %s: unrecognized Serial Mode specified: %u\n", |
182 | __func__, smode); | |
f93022c3 JR |
183 | return ULPI_ERROR; |
184 | } | |
185 | ||
3e6e809f | 186 | return ulpi_write(ulpi_vp, &ulpi->iface_ctrl_set, smode); |
f93022c3 JR |
187 | } |
188 | ||
3e6e809f | 189 | int ulpi_suspend(struct ulpi_viewport *ulpi_vp) |
f93022c3 | 190 | { |
4256101f | 191 | int err; |
f93022c3 | 192 | |
3e6e809f | 193 | err = ulpi_write(ulpi_vp, &ulpi->function_ctrl_clear, |
f93022c3 JR |
194 | ULPI_FC_SUSPENDM); |
195 | if (err) | |
196 | printf("ULPI: %s: failed writing the suspend bit\n", __func__); | |
197 | ||
198 | return err; | |
199 | } | |
200 | ||
201 | /* | |
202 | * Wait for ULPI PHY reset to complete. | |
203 | * Actual wait for reset must be done in a view port specific way, | |
204 | * because it involves checking the DIR line. | |
205 | */ | |
3e6e809f | 206 | static int __ulpi_reset_wait(struct ulpi_viewport *ulpi_vp) |
f93022c3 JR |
207 | { |
208 | u32 val; | |
209 | int timeout = CONFIG_USB_ULPI_TIMEOUT; | |
210 | ||
211 | /* Wait for the RESET bit to become zero */ | |
212 | while (--timeout) { | |
213 | /* | |
214 | * This function is generic and suppose to work | |
215 | * with any viewport, so we cheat here and don't check | |
216 | * for the error of ulpi_read(), if there is one, then | |
217 | * there will be a timeout. | |
218 | */ | |
3e6e809f | 219 | val = ulpi_read(ulpi_vp, &ulpi->function_ctrl); |
f93022c3 JR |
220 | if (!(val & ULPI_FC_RESET)) |
221 | return 0; | |
222 | ||
223 | udelay(1); | |
224 | } | |
225 | ||
226 | printf("ULPI: %s: reset timed out\n", __func__); | |
227 | ||
228 | return ULPI_ERROR; | |
229 | } | |
3e6e809f G |
230 | int ulpi_reset_wait(struct ulpi_viewport *ulpi_vp) |
231 | __attribute__((weak, alias("__ulpi_reset_wait"))); | |
f93022c3 | 232 | |
3e6e809f | 233 | int ulpi_reset(struct ulpi_viewport *ulpi_vp) |
f93022c3 | 234 | { |
4256101f | 235 | int err; |
f93022c3 | 236 | |
3e6e809f | 237 | err = ulpi_write(ulpi_vp, |
f93022c3 JR |
238 | &ulpi->function_ctrl_set, ULPI_FC_RESET); |
239 | if (err) { | |
240 | printf("ULPI: %s: failed writing reset bit\n", __func__); | |
241 | return err; | |
242 | } | |
243 | ||
3e6e809f | 244 | return ulpi_reset_wait(ulpi_vp); |
f93022c3 | 245 | } |