source: mainline/uspace/drv/infrastructure/rootamdm37x/rootamdm37x.c@ fde2dab9

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

rootamdm37x: Fix off by one freq calculations.

  • Property mode set to 100644
File size: 19.1 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 1
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
51#include "cm/core.h"
52#include "cm/clock_control.h"
53#include "cm/usbhost.h"
54#include "cm/mpu.h"
55#include "cm/iva2.h"
56
57#include "prm/clock_control.h"
58
59#define NAME "rootamdm37x"
60
61typedef struct {
62 uhh_regs_t *uhh;
63 tll_regs_t *tll;
64 struct {
65 mpu_cm_regs_t *mpu;
66 iva2_cm_regs_t *iva2;
67 core_cm_regs_t *core;
68 clock_control_cm_regs_t *clocks;
69 usbhost_cm_regs_t *usbhost;
70 } cm;
71 struct {
72 clock_control_prm_regs_t *clocks;
73 } prm;
74} amdm37x_t;
75
76static void log(const volatile void *place, uint32_t val, volatile void* base, size_t size, void *data, bool write)
77{
78 printf("PIO %s: %p(%p) %#"PRIx32"\n", write ? "WRITE" : "READ",
79 (place - base) + data, place, val);
80}
81
82static int amdm37x_hw_access_init(amdm37x_t *device)
83{
84 assert(device);
85 int ret = EOK;
86
87 ret = pio_enable((void*)USBHOST_CM_BASE_ADDRESS, USBHOST_CM_SIZE,
88 (void**)&device->cm.usbhost);
89 if (ret != EOK)
90 return ret;
91
92 ret = pio_enable((void*)CORE_CM_BASE_ADDRESS, CORE_CM_SIZE,
93 (void**)&device->cm.core);
94 if (ret != EOK)
95 return ret;
96
97 ret = pio_enable((void*)CLOCK_CONTROL_CM_BASE_ADDRESS,
98 CLOCK_CONTROL_CM_SIZE, (void**)&device->cm.clocks);
99 if (ret != EOK)
100 return ret;
101
102 ret = pio_enable((void*)MPU_CM_BASE_ADDRESS,
103 MPU_CM_SIZE, (void**)&device->cm.mpu);
104 if (ret != EOK)
105 return ret;
106
107 ret = pio_enable((void*)IVA2_CM_BASE_ADDRESS,
108 IVA2_CM_SIZE, (void**)&device->cm.iva2);
109 if (ret != EOK)
110 return ret;
111
112 ret = pio_enable((void*)CLOCK_CONTROL_PRM_BASE_ADDRESS,
113 CLOCK_CONTROL_PRM_SIZE, (void**)&device->prm.clocks);
114 if (ret != EOK)
115 return ret;
116
117 ret = pio_enable((void*)AMDM37x_USBTLL_BASE_ADDRESS,
118 AMDM37x_USBTLL_SIZE, (void**)&device->tll);
119 if (ret != EOK)
120 return ret;
121
122 ret = pio_enable((void*)AMDM37x_UHH_BASE_ADDRESS,
123 AMDM37x_UHH_SIZE, (void**)&device->uhh);
124 if (ret != EOK)
125 return ret;
126
127 if (DEBUG_CM) {
128 pio_trace_enable(device->tll, AMDM37x_USBTLL_SIZE, log, (void*)AMDM37x_USBTLL_BASE_ADDRESS);
129 pio_trace_enable(device->cm.clocks, CLOCK_CONTROL_CM_SIZE, log, (void*)CLOCK_CONTROL_CM_BASE_ADDRESS);
130 pio_trace_enable(device->cm.core, CORE_CM_SIZE, log, (void*)CORE_CM_BASE_ADDRESS);
131 pio_trace_enable(device->cm.mpu, MPU_CM_SIZE, log, (void*)MPU_CM_BASE_ADDRESS);
132 pio_trace_enable(device->cm.iva2, IVA2_CM_SIZE, log, (void*)IVA2_CM_BASE_ADDRESS);
133 pio_trace_enable(device->cm.usbhost, USBHOST_CM_SIZE, log, (void*)USBHOST_CM_BASE_ADDRESS);
134 pio_trace_enable(device->uhh, AMDM37x_UHH_SIZE, log, (void*)AMDM37x_UHH_BASE_ADDRESS);
135 pio_trace_enable(device->prm.clocks, CLOCK_CONTROL_PRM_SIZE, log, (void*)CLOCK_CONTROL_PRM_BASE_ADDRESS);
136 }
137 return EOK;
138}
139
140
141
142/** Set DPLLs 1,2,3,4,5 to ON (locked) and autoidle.
143 * @param device Register map.
144 *
145 * The idea is to get all DPLLs running and make hw control their power mode,
146 * based on the module requirements (module ICLKs and FCLKs).
147 */
148static void dpll_on_autoidle(amdm37x_t *device)
149{
150 assert(device);
151 /* Get SYS_CLK value, it is used as reference clock by all DPLLs,
152 * NFI who sets this or why it is set to specific value. */
153 const unsigned base_clk = pio_read_32(&device->prm.clocks->clksel)
154 & CLOCK_CONTROL_PRM_CLKSEL_SYS_CLKIN_MASK;
155 const unsigned base_freq = sys_clk_freq_kHz(base_clk);
156 ddf_msg(LVL_DEBUG, "Base frequency: %d.%dMhz",
157 base_freq / 1000, base_freq % 1000);
158
159
160 /* DPLL1 provides MPU(CPU) clock.
161 * It uses SYS_CLK as reference clock and core clock (DPLL3) as
162 * high frequency bypass (MPU then runs on L3 interconnect freq).
163 * It should be setup by fw or u-boot.*/
164 mpu_cm_regs_t *mpu = device->cm.mpu;
165
166 /* Current MPU frequency. */
167 if (pio_read_32(&mpu->clkstst) & MPU_CM_CLKSTST_CLKACTIVITY_MPU_ACTIVE_FLAG) {
168 if (pio_read_32(&mpu->idlest_pll) & MPU_CM_IDLEST_PLL_ST_MPU_CLK_LOCKED_FLAG) {
169 /* DPLL active and locked */
170 const uint32_t reg = pio_read_32(&mpu->clksel1_pll);
171 const unsigned multiplier =
172 (reg & MPU_CM_CLKSEL1_PLL_MPU_DPLL_MULT_MASK)
173 >> MPU_CM_CLKSEL1_PLL_MPU_DPLL_MULT_SHIFT;
174 const unsigned divisor =
175 (reg & MPU_CM_CLKSEL1_PLL_MPU_DPLL_MULT_MASK)
176 >> MPU_CM_CLKSEL1_PLL_MPU_DPLL_MULT_SHIFT;
177 const unsigned divisor2 =
178 (pio_read_32(&mpu->clksel2_pll)
179 & MPU_CM_CLKSEL2_PLL_MPU_DPLL_CLKOUT_DIV_MASK);
180 if (multiplier && divisor && divisor2) {
181 /** See AMDM37x TRM p. 300 for the formula */
182 const unsigned freq =
183 ((base_freq * multiplier) / (divisor + 1))
184 / divisor2;
185 ddf_msg(LVL_NOTE, "MPU running at %d.%d MHz",
186 freq / 1000, freq % 1000);
187 } else {
188 ddf_msg(LVL_WARN, "Frequency divisor and/or "
189 "multiplier value invalid: %d %d %d",
190 multiplier, divisor, divisor2);
191 }
192 } else {
193 /* DPLL in LP bypass mode */
194 const unsigned divisor =
195 MPU_CM_CLKSEL1_PLL_MPU_CLK_SRC_VAL(
196 pio_read_32(&mpu->clksel1_pll));
197 ddf_msg(LVL_NOTE, "MPU DPLL in bypass mode, running at"
198 " CORE CLK / %d MHz", divisor);
199 }
200 } else {
201 ddf_msg(LVL_WARN, "MPU clock domain is not active, we should not be running...");
202 }
203 // TODO: Enable this (automatic MPU downclocking):
204#if 0
205 /* Enable low power bypass mode, this will take effect the next lock or
206 * relock sequence. */
207 //TODO: We might need to force re-lock after enabling this
208 pio_set_32(&mpu->clken_pll, MPU_CM_CLKEN_PLL_EN_MPU_DPLL_LP_MODE_FLAG, 5);
209 /* Enable automatic relocking */
210 pio_change_32(&mpu->autoidle_pll, MPU_CM_AUTOIDLE_PLL_AUTO_MPU_DPLL_ENABLED, MPU_CM_AUTOIDLE_PLL_AUTO_MPU_DPLL_MASK, 5);
211#endif
212
213 /* DPLL2 provides IVA(video acceleration) clock.
214 * It uses SYS_CLK as reference clokc and core clock (DPLL3) as
215 * high frequency bypass (IVA runs on L3 freq).
216 */
217 // TODO: We can probably turn this off entirely. IVA is left unused.
218 /* Enable low power bypass mode, this will take effect the next lock or
219 * relock sequence. */
220 //TODO: We might need to force re-lock after enabling this
221 pio_set_32(&device->cm.iva2->clken_pll, MPU_CM_CLKEN_PLL_EN_MPU_DPLL_LP_MODE_FLAG, 5);
222 /* Enable automatic relocking */
223 pio_change_32(&device->cm.iva2->autoidle_pll, MPU_CM_AUTOIDLE_PLL_AUTO_MPU_DPLL_ENABLED, MPU_CM_AUTOIDLE_PLL_AUTO_MPU_DPLL_MASK, 5);
224
225 /* DPLL3 provides tons of clocks:
226 * CORE_CLK, COREX2_CLK, DSS_TV_CLK, 12M_CLK, 48M_CLK, 96M_CLK, L3_ICLK,
227 * and L4_ICLK. It uses SYS_CLK as reference clock and low frequency
228 * bypass. It should be setup by fw or u-boot as it controls critical
229 * interconnects.
230 */
231 if (pio_read_32(&device->cm.clocks->idlest_ckgen) & CLOCK_CONTROL_CM_IDLEST_CKGEN_ST_CORE_CLK_FLAG) {
232 /* DPLL active and locked */
233 const uint32_t reg =
234 pio_read_32(&device->cm.clocks->clksel1_pll);
235 const unsigned multiplier =
236 CLOCK_CONTROL_CM_CLKSEL1_PLL_CORE_DPLL_MULT_GET(reg);
237 const unsigned divisor =
238 CLOCK_CONTROL_CM_CLKSEL1_PLL_CORE_DPLL_DIV_GET(reg);
239 const unsigned divisor2 =
240 CLOCK_CONTROL_CM_CLKSEL1_PLL_CORE_DPLL_CLKOUT_DIV_GET(reg);
241 if (multiplier && divisor && divisor2) {
242 /** See AMDM37x TRM p. 300 for the formula */
243 const unsigned freq =
244 ((base_freq * multiplier) / (divisor + 1)) / divisor2;
245 ddf_msg(LVL_NOTE, "CORE CLK running at %d.%d MHz",
246 freq / 1000, freq % 1000);
247 const unsigned l3_div =
248 pio_read_32(&device->cm.core->clksel)
249 & CORE_CM_CLKSEL_CLKSEL_L3_MASK;
250 if (l3_div == CORE_CM_CLKSEL_CLKSEL_L3_DIVIDED1 ||
251 l3_div == CORE_CM_CLKSEL_CLKSEL_L3_DIVIDED2) {
252 ddf_msg(LVL_NOTE, "L3 interface at %d.%d MHz",
253 (freq / l3_div) / 1000,
254 (freq / l3_div) % 1000);
255 } else {
256 ddf_msg(LVL_WARN,"L3 interface clock divisor is"
257 " invalid: %d", l3_div);
258 }
259 } else {
260 ddf_msg(LVL_WARN, "DPLL3 frequency divisor and/or "
261 "multiplier value invalid: %d %d %d",
262 multiplier, divisor, divisor2);
263 }
264 } else {
265 ddf_msg(LVL_WARN, "CORE CLK in bypass mode, fruunig at SYS_CLK"
266 " frreq of %d.%d MHz", base_freq / 1000, base_freq % 1000);
267 }
268
269 /* Set DPLL3 to automatic to save power */
270 pio_change_32(&device->cm.clocks->autoidle_pll,
271 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_AUTOMATIC,
272 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_MASK, 5);
273
274 /* DPLL4 provides peripheral domain clocks:
275 * CAM_MCLK, EMU_PER_ALWON_CLK, DSS1_ALWON_FCLK, and 96M_ALWON_FCLK.
276 * It uses SYS_CLK as reference clock and low frequency bypass.
277 * 96M clock is used by McBSP[1,5], MMC[1,2,3], I2C[1,2,3], so
278 * we can probably turn this off entirely (DSS is still non-functional).
279 */
280 /* Set DPLL4 to automatic to save power */
281 pio_change_32(&device->cm.clocks->autoidle_pll,
282 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_AUTOMATIC,
283 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_MASK, 5);
284
285 /* DPLL5 provide peripheral domain clocks: 120M_FCLK.
286 * It uses SYS_CLK as reference clock and low frequency bypass.
287 * 120M clock is used by HS USB and USB TLL.
288 */
289 // TODO setup DPLL5
290 if ((pio_read_32(&device->cm.clocks->clken2_pll)
291 & CLOCK_CONTROL_CM_CLKEN2_PLL_EN_PERIPH2_DPLL_MASK)
292 != CLOCK_CONTROL_CM_CLKEN2_PLL_EN_PERIPH2_DPLL_LOCK) {
293 /* Compute divisors and multiplier
294 * See AMDM37x TRM p. 300 for the formula */
295 assert((base_freq % 100) == 0);
296 const unsigned mult = 1200;
297 const unsigned div = (base_freq / 100) - 1;
298 const unsigned div2 = 1;
299
300 /* Set multiplier */
301 pio_change_32(&device->cm.clocks->clksel4_pll,
302 CLOCK_CONTROL_CM_CLKSEL4_PLL_PERIPH2_DPLL_MULT_CREATE(mult),
303 CLOCK_CONTROL_CM_CLKSEL4_PLL_PERIPH2_DPLL_MULT_MASK, 10);
304
305 /* Set DPLL divisor */
306 pio_change_32(&device->cm.clocks->clksel4_pll,
307 CLOCK_CONTROL_CM_CLKSEL4_PLL_PERIPH2_DPLL_DIV_CREATE(div),
308 CLOCK_CONTROL_CM_CLKSEL4_PLL_PERIPH2_DPLL_DIV_MASK, 10);
309
310 /* Set output clock divisor */
311 pio_change_32(&device->cm.clocks->clksel5_pll,
312 CLOCK_CONTROL_CM_CLKSEL5_PLL_DIV120M_CREATE(div2),
313 CLOCK_CONTROL_CM_CLKSEL5_PLL_DIV120M_MASK, 10);
314
315 /* Start DPLL5 */
316 pio_change_32(&device->cm.clocks->clken2_pll,
317 CLOCK_CONTROL_CM_CLKEN2_PLL_EN_PERIPH2_DPLL_LOCK,
318 CLOCK_CONTROL_CM_CLKEN2_PLL_EN_PERIPH2_DPLL_MASK, 10);
319
320 }
321 /* Set DPLL5 to automatic to save power */
322 pio_change_32(&device->cm.clocks->autoidle2_pll,
323 CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_AUTOMATIC,
324 CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_MASK, 5);
325}
326
327/** Enable/disable function and interface clocks for USBTLL and USBHOST.
328 * @param device Register map.
329 * @param on True to swoitch clocks on.
330 */
331static void usb_clocks_enable(amdm37x_t *device, bool on)
332{
333 if (on) {
334 /* Enable interface and function clock for USB TLL */
335 pio_set_32(&device->cm.core->fclken3,
336 CORE_CM_FCLKEN3_EN_USBTLL_FLAG, 5);
337 pio_set_32(&device->cm.core->iclken3,
338 CORE_CM_ICLKEN3_EN_USBTLL_FLAG, 5);
339
340 /* Enable interface and function clock for USB hosts */
341 pio_set_32(&device->cm.usbhost->fclken,
342 USBHOST_CM_FCLKEN_EN_USBHOST1_FLAG |
343 USBHOST_CM_FCLKEN_EN_USBHOST2_FLAG, 5);
344 pio_set_32(&device->cm.usbhost->iclken,
345 USBHOST_CM_ICLKEN_EN_USBHOST, 5);
346
347 if (DEBUG_CM) {
348 printf("DPLL5 (and everything else) should be on: %"
349 PRIx32" %"PRIx32".\n",
350 pio_read_32(&device->cm.clocks->idlest_ckgen),
351 pio_read_32(&device->cm.clocks->idlest2_ckgen));
352 }
353 } else {
354 /* Disable interface and function clock for USB hosts */
355 pio_clear_32(&device->cm.usbhost->iclken,
356 USBHOST_CM_ICLKEN_EN_USBHOST, 5);
357 pio_clear_32(&device->cm.usbhost->fclken,
358 USBHOST_CM_FCLKEN_EN_USBHOST1_FLAG |
359 USBHOST_CM_FCLKEN_EN_USBHOST2_FLAG, 5);
360
361 /* Disable interface and function clock for USB TLL */
362 pio_clear_32(&device->cm.core->iclken3,
363 CORE_CM_ICLKEN3_EN_USBTLL_FLAG, 5);
364 pio_clear_32(&device->cm.core->fclken3,
365 CORE_CM_FCLKEN3_EN_USBTLL_FLAG, 5);
366 }
367}
368
369/** Initialize USB TLL port connections.
370 *
371 * Different modes are on page 3312 of the Manual Figure 22-34.
372 * Select mode than can operate in FS/LS.
373 */
374static int usb_tll_init(amdm37x_t *device)
375{
376 /* Check access */
377 if (pio_read_32(&device->cm.core->idlest3) & CORE_CM_IDLEST3_ST_USBTLL_FLAG) {
378 ddf_msg(LVL_ERROR, "USB TLL is not accessible");
379 return EIO;
380 }
381
382 /* Reset USB TLL */
383 pio_set_32(&device->tll->sysconfig, TLL_SYSCONFIG_SOFTRESET_FLAG, 5);
384 ddf_msg(LVL_DEBUG2, "Waiting for USB TLL reset");
385 while (!(pio_read_32(&device->tll->sysstatus) & TLL_SYSSTATUS_RESET_DONE_FLAG));
386 ddf_msg(LVL_DEBUG, "USB TLL Reset done.");
387
388 /* Setup idle mode (smart idle) */
389 pio_change_32(&device->tll->sysconfig,
390 TLL_SYSCONFIG_CLOCKACTIVITY_FLAG | TLL_SYSCONFIG_AUTOIDLE_FLAG |
391 TLL_SYSCONFIG_SIDLE_MODE_SMART, TLL_SYSCONFIG_SIDLE_MODE_MASK, 5);
392
393 /* Smart idle for UHH */
394 pio_change_32(&device->uhh->sysconfig,
395 UHH_SYSCONFIG_CLOCKACTIVITY_FLAG | UHH_SYSCONFIG_AUTOIDLE_FLAG |
396 UHH_SYSCONFIG_SIDLE_MODE_SMART, UHH_SYSCONFIG_SIDLE_MODE_MASK, 5);
397
398 /* Set all ports to go through TLL(UTMI)
399 * Direct connection can only work in HS mode */
400 pio_set_32(&device->uhh->hostconfig,
401 UHH_HOSTCONFIG_P1_ULPI_BYPASS_FLAG |
402 UHH_HOSTCONFIG_P2_ULPI_BYPASS_FLAG |
403 UHH_HOSTCONFIG_P3_ULPI_BYPASS_FLAG, 5);
404
405 /* What is this? */
406 pio_set_32(&device->tll->shared_conf, TLL_SHARED_CONF_FCLK_IS_ON_FLAG, 5);
407
408 for (unsigned i = 0; i < 3; ++i) {
409 /* Serial mode is the only one capable of FS/LS operation.
410 * Select FS/LS mode, no idea what the difference is
411 * one of bidirectional modes might be good choice
412 * 2 = 3pin bidi phy. */
413 pio_change_32(&device->tll->channel_conf[i],
414 TLL_CHANNEL_CONF_CHANMODE_UTMI_SERIAL_MODE |
415 TLL_CHANNEL_CONF_FSLSMODE_3PIN_BIDI_PHY,
416 TLL_CHANNEL_CONF_CHANMODE_MASK |
417 TLL_CHANNEL_CONF_FSLSMODE_MASK, 5);
418 }
419 return EOK;
420}
421
422typedef struct {
423 hw_resource_list_t hw_resources;
424} rootamdm37x_fun_t;
425
426#define OHCI_BASE_ADDRESS 0x48064400
427#define OHCI_SIZE 1024
428#define EHCI_BASE_ADDRESS 0x48064800
429#define EHCI_SIZE 1024
430
431static hw_resource_t ohci_res[] = {
432 {
433 .type = MEM_RANGE,
434 /* See amdm37x TRM page. 3316 for these values */
435 .res.io_range = {
436 .address = OHCI_BASE_ADDRESS,
437 .size = OHCI_SIZE,
438 .endianness = LITTLE_ENDIAN
439 },
440 },
441 {
442 .type = INTERRUPT,
443 .res.interrupt = { .irq = 76 },
444 },
445};
446
447static const rootamdm37x_fun_t ohci = {
448 .hw_resources = {
449 .resources = ohci_res,
450 .count = sizeof(ohci_res)/sizeof(ohci_res[0]),
451 }
452};
453
454static hw_resource_t ehci_res[] = {
455 {
456 .type = MEM_RANGE,
457 /* See amdm37x TRM page. 3316 for these values */
458 .res.io_range = {
459 .address = EHCI_BASE_ADDRESS,
460 .size = EHCI_SIZE,
461 .endianness = LITTLE_ENDIAN
462 },
463 },
464 {
465 .type = INTERRUPT,
466 .res.interrupt = { .irq = 77 },
467 },
468};
469
470static const rootamdm37x_fun_t ehci = {
471 .hw_resources = {
472 .resources = ehci_res,
473 .count = sizeof(ehci_res) / sizeof(ehci_res[0]),
474 }
475};
476
477static hw_resource_list_t *rootamdm37x_get_resources(ddf_fun_t *fnode);
478static bool rootamdm37x_enable_interrupt(ddf_fun_t *fun);
479
480static hw_res_ops_t fun_hw_res_ops = {
481 .get_resource_list = &rootamdm37x_get_resources,
482 .enable_interrupt = &rootamdm37x_enable_interrupt,
483};
484
485static ddf_dev_ops_t rootamdm37x_fun_ops =
486{
487 .interfaces[HW_RES_DEV_IFACE] = &fun_hw_res_ops
488};
489
490static bool rootamdm37x_add_fun(ddf_dev_t *dev, const char *name,
491 const char *str_match_id, const rootamdm37x_fun_t *fun)
492{
493 ddf_msg(LVL_DEBUG, "Adding new function '%s'.", name);
494
495 /* Create new device function. */
496 ddf_fun_t *fnode = ddf_fun_create(dev, fun_inner, name);
497 if (fnode == NULL)
498 return ENOMEM;
499
500
501 /* Add match id */
502 if (ddf_fun_add_match_id(fnode, str_match_id, 100) != EOK) {
503 ddf_fun_destroy(fnode);
504 return false;
505 }
506
507 /* Set provided operations to the device. */
508 ddf_fun_data_implant(fnode, (void*)fun);
509 ddf_fun_set_ops(fnode, &rootamdm37x_fun_ops);
510
511 /* Register function. */
512 if (ddf_fun_bind(fnode) != EOK) {
513 ddf_msg(LVL_ERROR, "Failed binding function %s.", name);
514 // TODO This will try to free our data!
515 ddf_fun_destroy(fnode);
516 return false;
517 }
518
519 return true;
520}
521
522/** Add the root device.
523 *
524 * @param dev Device which is root of the whole device tree
525 * (both of HW and pseudo devices).
526 *
527 * @return Zero on success, negative error number otherwise.
528 *
529 */
530static int rootamdm37x_dev_add(ddf_dev_t *dev)
531{
532 assert(dev);
533 amdm37x_t *device = ddf_dev_data_alloc(dev, sizeof(amdm37x_t));
534 if (!device)
535 return ENOMEM;
536 int ret = amdm37x_hw_access_init(device);
537 if (ret != EOK) {
538 ddf_msg(LVL_FATAL, "Failed to setup hw access!.\n");
539 return ret;
540 }
541
542 /* Set dplls to ON and automatic */
543 dpll_on_autoidle(device);
544
545 /* Enable function and interface clocks */
546 usb_clocks_enable(device, true);
547
548 /* Init TLL */
549 ret = usb_tll_init(device);
550 if (ret != EOK) {
551 ddf_msg(LVL_FATAL, "Failed to init USB TLL!.\n");
552 usb_clocks_enable(device, false);
553 return ret;
554 }
555
556 /* Register functions */
557 if (!rootamdm37x_add_fun(dev, "ohci", "usb/host=ohci", &ohci))
558 ddf_msg(LVL_ERROR, "Failed to add OHCI function for "
559 "BeagleBoard-xM platform.");
560 if (!rootamdm37x_add_fun(dev, "ehci", "usb/host=ehci", &ehci))
561 ddf_msg(LVL_ERROR, "Failed to add EHCI function for "
562 "BeagleBoard-xM platform.");
563
564 return EOK;
565}
566
567/** The root device driver's standard operations. */
568static driver_ops_t rootamdm37x_ops = {
569 .dev_add = &rootamdm37x_dev_add
570};
571
572/** The root device driver structure. */
573static driver_t rootamdm37x_driver = {
574 .name = NAME,
575 .driver_ops = &rootamdm37x_ops
576};
577
578static hw_resource_list_t *rootamdm37x_get_resources(ddf_fun_t *fnode)
579{
580 rootamdm37x_fun_t *fun = ddf_fun_data_get(fnode);
581 assert(fun != NULL);
582 return &fun->hw_resources;
583}
584
585static bool rootamdm37x_enable_interrupt(ddf_fun_t *fun)
586{
587 /* TODO */
588 return false;
589}
590
591int main(int argc, char *argv[])
592{
593 printf("%s: HelenOS AM/DM37x(OMAP37x) platform driver\n", NAME);
594 ddf_log_init(NAME);
595 return ddf_driver_main(&rootamdm37x_driver);
596}
597
598/**
599 * @}
600 */
Note: See TracBrowser for help on using the repository browser.