source: mainline/uspace/drv/infrastructure/rootamdm37x/rootamdm37x.c@ 57912af3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 57912af3 was 57912af3, checked in by Jan Vesely <jano.vesely@…>, 13 years ago

rootamdm37x: Move Clock Management headers to separate directory

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 2012 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @defgroup root_amdm37x TI AM/DM37x platform driver.
31 * @brief HelenOS TI AM/DM37x platform driver.
32 * @{
33 */
34
35/** @file
36 */
37#define _DDF_DATA_IMPLANT
38
39#define DEBUG_CM
40
41#include <ddf/driver.h>
42#include <ddf/log.h>
43#include <errno.h>
44#include <ops/hw_res.h>
45#include <stdio.h>
46#include <ddi.h>
47
48#include "uhh.h"
49#include "usbtll.h"
50#include "cm/core.h"
51#include "cm/clock_control.h"
52#include "cm/usbhost.h"
53
54#define NAME "rootamdm37x"
55
56typedef struct {
57 uhh_regs_t *uhh;
58 tll_regs_t *tll;
59 struct {
60 core_cm_regs_t *core;
61 clock_control_cm_regs_t *clocks;
62 usbhost_cm_regs_t *usbhost;
63 } cm;
64} amdm37x_t;
65
66#ifdef DEBUG_CM
67static void log(volatile void *place, uint32_t val, volatile void* base, size_t size, void *data, bool write)
68{
69 printf("PIO %s: %p(%p) %#"PRIx32"\n", write ? "WRITE" : "READ",
70 (place - base) + data, place, val);
71}
72#endif
73
74static int amdm37x_hw_access_init(amdm37x_t *device)
75{
76 assert(device);
77 int ret = EOK;
78
79 ret = pio_enable((void*)USBHOST_CM_BASE_ADDRESS, USBHOST_CM_SIZE,
80 (void**)&device->cm.usbhost);
81 if (ret != EOK)
82 return ret;
83
84 ret = pio_enable((void*)CORE_CM_BASE_ADDRESS, CORE_CM_SIZE,
85 (void**)&device->cm.core);
86 if (ret != EOK)
87 return ret;
88
89 ret = pio_enable((void*)CLOCK_CONTROL_CM_BASE_ADDRESS,
90 CLOCK_CONTROL_CM_SIZE, (void**)&device->cm.clocks);
91 if (ret != EOK)
92 return ret;
93
94 ret = pio_enable((void*)AMDM37x_USBTLL_BASE_ADDRESS,
95 AMDM37x_USBTLL_SIZE, (void**)&device->tll);
96 if (ret != EOK)
97 return ret;
98
99 ret = pio_enable((void*)AMDM37x_UHH_BASE_ADDRESS,
100 AMDM37x_UHH_SIZE, (void**)&device->uhh);
101 if (ret != EOK)
102 return ret;
103
104#ifdef DEBUG_CM
105 pio_trace_enable(device->tll, AMDM37x_USBTLL_SIZE, log, (void*)AMDM37x_USBTLL_BASE_ADDRESS);
106 pio_trace_enable(device->cm.clocks, CLOCK_CONTROL_CM_SIZE, log, (void*)CLOCK_CONTROL_CM_BASE_ADDRESS);
107 pio_trace_enable(device->cm.core, CORE_CM_SIZE, log, (void*)CORE_CM_BASE_ADDRESS);
108 pio_trace_enable(device->cm.usbhost, USBHOST_CM_SIZE, log, (void*)USBHOST_CM_BASE_ADDRESS);
109 pio_trace_enable(device->uhh, AMDM37x_UHH_SIZE, log, (void*)AMDM37x_UHH_BASE_ADDRESS);
110#endif
111 return EOK;
112}
113
114static int usb_clocks(amdm37x_t *device, bool on)
115{
116 /* Set DPLL3 to automatic */
117 pio_change_32(&device->cm.clocks->autoidle_pll,
118 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_AUTOMATIC,
119 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_MASK, 5);
120
121 /* Set DPLL4 to automatic */
122 pio_change_32(&device->cm.clocks->autoidle_pll,
123 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_AUTOMATIC,
124 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_MASK, 5);
125
126 /* Set DPLL5 to automatic */
127 pio_change_32(&device->cm.clocks->autoidle2_pll,
128 CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_AUTOMATIC,
129 CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_MASK, 5);
130
131
132#ifdef DEBUG_CM
133 printf("DPLL5 could be on: %"PRIx32" %"PRIx32".\n",
134 pio_read_32((ioport32_t*)&device->cm.clocks->idlest_ckgen),
135 pio_read_32((ioport32_t*)&device->cm.clocks->idlest2_ckgen));
136#endif
137
138 if (on) {
139 /* Enable interface and function clock for USB TLL */
140 pio_set_32(&device->cm.core->fclken3,
141 CORE_CM_FCLKEN3_EN_USBTLL_FLAG, 5);
142 pio_set_32(&device->cm.core->iclken3,
143 CORE_CM_ICLKEN3_EN_USBTLL_FLAG, 5);
144
145 /* Enable interface and function clock for USB hosts */
146 pio_set_32(&device->cm.usbhost->fclken,
147 USBHOST_CM_FCLKEN_EN_USBHOST1_FLAG |
148 USBHOST_CM_FCLKEN_EN_USBHOST2_FLAG, 5);
149 pio_set_32(&device->cm.usbhost->iclken,
150 USBHOST_CM_ICLKEN_EN_USBHOST, 5);
151#ifdef DEBUG_CM
152 printf("DPLL5 (and everything else) should be on: %"PRIx32" %"PRIx32".\n",
153 pio_read_32((ioport32_t*)&device->cm.clocks->idlest_ckgen),
154 pio_read_32((ioport32_t*)&device->cm.clocks->idlest2_ckgen));
155#endif
156 } else {
157 /* Disable interface and function clock for USB hosts */
158 pio_clear_32(&device->cm.usbhost->iclken,
159 USBHOST_CM_ICLKEN_EN_USBHOST, 5);
160 pio_clear_32(&device->cm.usbhost->fclken,
161 USBHOST_CM_FCLKEN_EN_USBHOST1_FLAG |
162 USBHOST_CM_FCLKEN_EN_USBHOST2_FLAG, 5);
163
164 /* Disable interface and function clock for USB TLL */
165 pio_clear_32(&device->cm.core->iclken3,
166 CORE_CM_ICLKEN3_EN_USBTLL_FLAG, 5);
167 pio_clear_32(&device->cm.core->fclken3,
168 CORE_CM_FCLKEN3_EN_USBTLL_FLAG, 5);
169 }
170
171 return EOK;
172}
173
174/** Initialize USB TLL port connections.
175 *
176 * Different modes are on page 3312 of the Manual Figure 22-34.
177 * Select mode than can operate in FS/LS.
178 */
179static int usb_tll_init(amdm37x_t *device)
180{
181
182 /* Reset USB TLL */
183 pio_set_32(&device->tll->sysconfig, TLL_SYSCONFIG_SOFTRESET_FLAG, 5);
184 ddf_msg(LVL_DEBUG2, "Waiting for USB TLL reset");
185 while (!(pio_read_32(&device->tll->sysstatus) & TLL_SYSSTATUS_RESET_DONE_FLAG));
186 ddf_msg(LVL_DEBUG, "USB TLL Reset done.");
187
188 /* Setup idle mode (smart idle) */
189 pio_change_32(&device->tll->sysconfig,
190 TLL_SYSCONFIG_CLOCKACTIVITY_FLAG | TLL_SYSCONFIG_AUTOIDLE_FLAG |
191 TLL_SYSCONFIG_SIDLE_MODE_SMART, TLL_SYSCONFIG_SIDLE_MODE_MASK, 5);
192
193 /* Smart idle for UHH */
194 pio_change_32(&device->uhh->sysconfig,
195 UHH_SYSCONFIG_CLOCKACTIVITY_FLAG | UHH_SYSCONFIG_AUTOIDLE_FLAG |
196 UHH_SYSCONFIG_SIDLE_MODE_SMART, UHH_SYSCONFIG_SIDLE_MODE_MASK, 5);
197
198 /* Set all ports to go through TLL(UTMI)
199 * Direct connection can only work in HS mode */
200 pio_set_32(&device->uhh->hostconfig,
201 UHH_HOSTCONFIG_P1_ULPI_BYPASS_FLAG |
202 UHH_HOSTCONFIG_P2_ULPI_BYPASS_FLAG |
203 UHH_HOSTCONFIG_P3_ULPI_BYPASS_FLAG, 5);
204
205 /* What is this? */
206 pio_set_32(&device->tll->shared_conf, TLL_SHARED_CONF_FCLK_IS_ON_FLAG, 5);
207
208 for (unsigned i = 0; i < 3; ++i) {
209 /* Serial mode is the only one capable of FS/LS operation.
210 * Select FS/LS mode, no idea what the difference is
211 * one of bidirectional modes might be good choice
212 * 2 = 3pin bidi phy. */
213 pio_change_32(&device->tll->channel_conf[i],
214 TLL_CHANNEL_CONF_CHANMODE_UTMI_SERIAL_MODE |
215 TLL_CHANNEL_CONF_FSLSMODE_3PIN_BIDI_PHY,
216 TLL_CHANNEL_CONF_CHANMODE_MASK |
217 TLL_CHANNEL_CONF_FSLSMODE_MASK, 5);
218 }
219 return EOK;
220}
221
222typedef struct {
223 hw_resource_list_t hw_resources;
224} rootamdm37x_fun_t;
225
226#define OHCI_BASE_ADDRESS 0x48064400
227#define OHCI_SIZE 1024
228#define EHCI_BASE_ADDRESS 0x48064800
229#define EHCI_SIZE 1024
230
231static hw_resource_t ohci_res[] = {
232 {
233 .type = MEM_RANGE,
234 /* See amdm37x TRM page. 3316 for these values */
235 .res.io_range = {
236 .address = OHCI_BASE_ADDRESS,
237 .size = OHCI_SIZE,
238 .endianness = LITTLE_ENDIAN
239 },
240 },
241 {
242 .type = INTERRUPT,
243 .res.interrupt = { .irq = 76 },
244 },
245};
246
247static const rootamdm37x_fun_t ohci = {
248 .hw_resources = {
249 .resources = ohci_res,
250 .count = sizeof(ohci_res)/sizeof(ohci_res[0]),
251 }
252};
253
254static hw_resource_t ehci_res[] = {
255 {
256 .type = MEM_RANGE,
257 /* See amdm37x TRM page. 3316 for these values */
258 .res.io_range = {
259 .address = EHCI_BASE_ADDRESS,
260 .size = EHCI_SIZE,
261 .endianness = LITTLE_ENDIAN
262 },
263 },
264 {
265 .type = INTERRUPT,
266 .res.interrupt = { .irq = 77 },
267 },
268};
269
270static const rootamdm37x_fun_t ehci = {
271 .hw_resources = {
272 .resources = ehci_res,
273 .count = sizeof(ehci_res) / sizeof(ehci_res[0]),
274 }
275};
276
277static hw_resource_list_t *rootamdm37x_get_resources(ddf_fun_t *fnode);
278static bool rootamdm37x_enable_interrupt(ddf_fun_t *fun);
279
280static hw_res_ops_t fun_hw_res_ops = {
281 .get_resource_list = &rootamdm37x_get_resources,
282 .enable_interrupt = &rootamdm37x_enable_interrupt,
283};
284
285static ddf_dev_ops_t rootamdm37x_fun_ops =
286{
287 .interfaces[HW_RES_DEV_IFACE] = &fun_hw_res_ops
288};
289
290static bool rootamdm37x_add_fun(ddf_dev_t *dev, const char *name,
291 const char *str_match_id, const rootamdm37x_fun_t *fun)
292{
293 ddf_msg(LVL_DEBUG, "Adding new function '%s'.", name);
294
295 /* Create new device function. */
296 ddf_fun_t *fnode = ddf_fun_create(dev, fun_inner, name);
297 if (fnode == NULL)
298 return ENOMEM;
299
300
301 /* Add match id */
302 if (ddf_fun_add_match_id(fnode, str_match_id, 100) != EOK) {
303 ddf_fun_destroy(fnode);
304 return false;
305 }
306
307 /* Set provided operations to the device. */
308 ddf_fun_data_implant(fnode, (void*)fun);
309 ddf_fun_set_ops(fnode, &rootamdm37x_fun_ops);
310
311 /* Register function. */
312 if (ddf_fun_bind(fnode) != EOK) {
313 ddf_msg(LVL_ERROR, "Failed binding function %s.", name);
314 // TODO This will try to free our data!
315 ddf_fun_destroy(fnode);
316 return false;
317 }
318
319 return true;
320}
321
322/** Add the root device.
323 *
324 * @param dev Device which is root of the whole device tree
325 * (both of HW and pseudo devices).
326 *
327 * @return Zero on success, negative error number otherwise.
328 *
329 */
330static int rootamdm37x_dev_add(ddf_dev_t *dev)
331{
332 assert(dev);
333 amdm37x_t *device = ddf_dev_data_alloc(dev, sizeof(amdm37x_t));
334 if (!device)
335 return ENOMEM;
336 int ret = amdm37x_hw_access_init(device);
337 if (ret != EOK) {
338 ddf_msg(LVL_FATAL, "Failed to setup hw access!.\n");
339 return ret;
340 }
341
342 ret = usb_clocks(device, true);
343 if (ret != EOK) {
344 ddf_msg(LVL_FATAL, "Failed to enable USB HC clocks!.\n");
345 return ret;
346 }
347
348 ret = usb_tll_init(device);
349 if (ret != EOK) {
350 ddf_msg(LVL_FATAL, "Failed to init USB TLL!.\n");
351 usb_clocks(device, false);
352 return ret;
353 }
354
355 /* Register functions */
356 if (!rootamdm37x_add_fun(dev, "ohci", "usb/host=ohci", &ohci))
357 ddf_msg(LVL_ERROR, "Failed to add OHCI function for "
358 "BeagleBoard-xM platform.");
359 if (!rootamdm37x_add_fun(dev, "ehci", "usb/host=ehci", &ehci))
360 ddf_msg(LVL_ERROR, "Failed to add EHCI function for "
361 "BeagleBoard-xM platform.");
362
363 return EOK;
364}
365
366/** The root device driver's standard operations. */
367static driver_ops_t rootamdm37x_ops = {
368 .dev_add = &rootamdm37x_dev_add
369};
370
371/** The root device driver structure. */
372static driver_t rootamdm37x_driver = {
373 .name = NAME,
374 .driver_ops = &rootamdm37x_ops
375};
376
377static hw_resource_list_t *rootamdm37x_get_resources(ddf_fun_t *fnode)
378{
379 rootamdm37x_fun_t *fun = ddf_fun_data_get(fnode);
380 assert(fun != NULL);
381 return &fun->hw_resources;
382}
383
384static bool rootamdm37x_enable_interrupt(ddf_fun_t *fun)
385{
386 /* TODO */
387 return false;
388}
389
390int main(int argc, char *argv[])
391{
392 printf("%s: HelenOS AM/DM37x(OMAP37x) platform driver\n", NAME);
393 ddf_log_init(NAME);
394 return ddf_driver_main(&rootamdm37x_driver);
395}
396
397/**
398 * @}
399 */
Note: See TracBrowser for help on using the repository browser.