source: mainline/uspace/drv/infrastructure/rootamdm37x/rootamdm37x.c@ 0c12dfe

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

typos and comments

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