source: mainline/uspace/drv/infrastructure/rootamdm37x/rootamdm37x.c@ 7eb49f4

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

rootamdm37x: Set DPLL 3 and 4 to automatic too.

  • Property mode set to 100644
File size: 11.6 KB
RevLine 
[09a0a8f0]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 */
[2be2506a]37#define _DDF_DATA_IMPLANT
[09a0a8f0]38
[7290ca0]39#define DEBUG_CM
40
[09a0a8f0]41#include <ddf/driver.h>
42#include <ddf/log.h>
43#include <errno.h>
44#include <ops/hw_res.h>
45#include <stdio.h>
[712a10b]46#include <ddi.h>
[09a0a8f0]47
[063ae706]48#include "uhh.h"
49#include "usbtll.h"
[708ec60]50#include "core_cm.h"
[601fa93]51#include "clock_control_cm.h"
[708ec60]52#include "usbhost_cm.h"
[063ae706]53
[09a0a8f0]54#define NAME "rootamdm37x"
55
56typedef struct {
57 hw_resource_list_t hw_resources;
58} rootamdm37x_fun_t;
59
[063ae706]60#define OHCI_BASE_ADDRESS 0x48064400
61#define OHCI_SIZE 1024
62#define EHCI_BASE_ADDRESS 0x48064800
63#define EHCI_SIZE 1024
64
[09a0a8f0]65static hw_resource_t ohci_res[] = {
66 {
67 .type = MEM_RANGE,
68 /* See amdm37x TRM page. 3316 for these values */
69 .res.io_range = {
[063ae706]70 .address = OHCI_BASE_ADDRESS,
71 .size = OHCI_SIZE,
[09a0a8f0]72 .endianness = LITTLE_ENDIAN
73 },
74 },
75 {
76 .type = INTERRUPT,
77 .res.interrupt = { .irq = 76 },
78 },
79};
80
[063ae706]81static const rootamdm37x_fun_t ohci = {
82 .hw_resources = {
83 .resources = ohci_res,
84 .count = sizeof(ohci_res)/sizeof(ohci_res[0]),
85 }
86};
87
[09a0a8f0]88static hw_resource_t ehci_res[] = {
89 {
90 .type = MEM_RANGE,
91 /* See amdm37x TRM page. 3316 for these values */
92 .res.io_range = {
[063ae706]93 .address = EHCI_BASE_ADDRESS,
94 .size = EHCI_SIZE,
[09a0a8f0]95 .endianness = LITTLE_ENDIAN
96 },
97 },
98 {
99 .type = INTERRUPT,
100 .res.interrupt = { .irq = 77 },
101 },
102};
103
104static const rootamdm37x_fun_t ehci = {
105 .hw_resources = {
106 .resources = ehci_res,
107 .count = sizeof(ehci_res)/sizeof(ehci_res[0]),
108 }
109};
110
[063ae706]111static hw_resource_list_t *rootamdm37x_get_resources(ddf_fun_t *fnode);
112static bool rootamdm37x_enable_interrupt(ddf_fun_t *fun);
113
114static hw_res_ops_t fun_hw_res_ops = {
115 .get_resource_list = &rootamdm37x_get_resources,
116 .enable_interrupt = &rootamdm37x_enable_interrupt,
117};
118
119static ddf_dev_ops_t rootamdm37x_fun_ops =
120{
121 .interfaces[HW_RES_DEV_IFACE] = &fun_hw_res_ops
122};
123
124static int usb_clocks(bool on)
125{
[601fa93]126 static usbhost_cm_regs_t *usb_host_cm = NULL;
127 static core_cm_regs_t *l4_core_cm = NULL;
128 static clock_control_cm_regs_t *clock_control_cm = NULL;
129
130 if (!usb_host_cm) {
131 const int ret = pio_enable((void*)USBHOST_CM_BASE_ADDRESS,
132 USBHOST_CM_SIZE, (void**)&usb_host_cm);
133 if (ret != EOK)
134 return ret;
135 }
[063ae706]136
[601fa93]137 if (!l4_core_cm) {
138 const int ret = pio_enable((void*)CORE_CM_BASE_ADDRESS,
139 CORE_CM_SIZE, (void**)&l4_core_cm);
140 if (ret != EOK)
141 return ret;
142 }
[063ae706]143
[601fa93]144 if (!clock_control_cm) {
145 const int ret = pio_enable((void*)CLOCK_CONTROL_CM_BASE_ADDRESS,
146 CLOCK_CONTROL_CM_SIZE, (void**)&clock_control_cm);
147 if (ret != EOK)
148 return ret;
149 }
[09a0a8f0]150
[063ae706]151 assert(l4_core_cm);
152 assert(usb_host_cm);
[601fa93]153 assert(clock_control_cm);
154
[7eb49f4]155 uint32_t reg;
156
157 /* Set DPLL3 and DPLL4 to automatic */
158 reg = clock_control_cm->autoidle_pll;
159 reg &= ~(CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_MASK <<
160 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_SHIFT);
161 reg &= ~(CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_MASK <<
162 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_SHIFT);
163 reg |= (CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_AUTOMATIC <<
164 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_SHIFT);
165 reg |= (CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_AUTOMATIC <<
166 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_SHIFT);
167 clock_control_cm->autoidle_pll = reg;
168
169 /* Set DPLL5 to automatic */
170 reg = clock_control_cm->autoidle2_pll;
[601fa93]171 reg &= ~(CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_MASK <<
172 CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_SHIFT);
173 reg |= (CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_AUTOMATIC <<
174 CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_SHIFT);
175 clock_control_cm->autoidle2_pll = reg;
176
[7eb49f4]177
[7290ca0]178#ifdef DEBUG_CM
179 printf("DPLL5 could be on: %x %x.\n",
180 clock_control_cm->idlest_ckgen, clock_control_cm->idlest2_ckgen);
181#endif
182
[063ae706]183 if (on) {
[601fa93]184 /* Enable interface and function clock for USB TLL */
[708ec60]185 l4_core_cm->iclken3 |= CORE_CM_ICLKEN3_EN_USBTLL_FLAG;
186 l4_core_cm->fclken3 |= CORE_CM_FCLKEN3_EN_USBTLL_FLAG;
[601fa93]187
188 /* Enable interface and function clock for USB hosts */
[708ec60]189 usb_host_cm->iclken |= USBHOST_CM_ICLKEN_EN_USBHOST;
190 usb_host_cm->fclken |= USBHOST_CM_FCLKEN_EN_USBHOST1_FLAG;
191 usb_host_cm->fclken |= USBHOST_CM_FCLKEN_EN_USBHOST2_FLAG;
[7290ca0]192#ifdef DEBUG_CM
193 printf("DPLL5 (and everything else) should be on: %x %x.\n",
194 clock_control_cm->idlest_ckgen, clock_control_cm->idlest2_ckgen);
195#endif
[063ae706]196 } else {
[601fa93]197 /* Disable interface and function clock for USB hosts */
[708ec60]198 usb_host_cm->fclken &= ~USBHOST_CM_FCLKEN_EN_USBHOST2_FLAG;
199 usb_host_cm->fclken &= ~USBHOST_CM_FCLKEN_EN_USBHOST1_FLAG;
200 usb_host_cm->iclken &= ~USBHOST_CM_ICLKEN_EN_USBHOST;
[601fa93]201
202 /* Disable interface and function clock for USB TLL */
[708ec60]203 l4_core_cm->fclken3 &= ~CORE_CM_FCLKEN3_EN_USBTLL_FLAG;
204 l4_core_cm->iclken3 &= ~CORE_CM_ICLKEN3_EN_USBTLL_FLAG;
[063ae706]205 }
206
[601fa93]207 return EOK;
[063ae706]208}
209
[3a01483]210/** Initialize USB TLL port connections.
211 *
212 * Different modes are on page 3312 of the Manual Figure 22-34.
213 * Select mode than can operate in FS/LS.
214 */
[063ae706]215static int usb_tll_init()
216{
[708ec60]217 tll_regs_t *usb_tll = NULL;
218 uhh_regs_t *uhh_conf = NULL;
219
220 int ret = pio_enable((void*)AMDM37x_USBTLL_BASE_ADDRESS,
221 AMDM37x_USBTLL_SIZE, (void**)&usb_tll);
222 if (ret != EOK)
223 return ret;
224
225 ret = pio_enable((void*)AMDM37x_UHH_BASE_ADDRESS,
226 AMDM37x_UHH_SIZE, (void**)&uhh_conf);
227 if (ret != EOK)
228 return ret;
229
[3a01483]230 /* Reset USB TLL */
[708ec60]231 usb_tll->sysconfig |= TLL_SYSCONFIG_SOFTRESET_FLAG;
232 ddf_msg(LVL_DEBUG2, "Waiting for USB TLL reset");
233 while (!(usb_tll->sysstatus & TLL_SYSSTATUS_RESET_DONE_FLAG));
234 ddf_msg(LVL_DEBUG, "USB TLL Reset done.");
235
[3a01483]236 {
237 /* Setup idle mode (smart idle) */
238 uint32_t sysc = usb_tll->sysconfig;
239 sysc |= TLL_SYSCONFIG_CLOCKACTIVITY_FLAG | TLL_SYSCONFIG_AUTOIDLE_FLAG;
240 sysc = (sysc
241 & ~(TLL_SYSCONFIG_SIDLE_MODE_MASK << TLL_SYSCONFIG_SIDLE_MODE_SHIFT)
242 ) | (0x2 << TLL_SYSCONFIG_SIDLE_MODE_SHIFT);
243 usb_tll->sysconfig = sysc;
244 ddf_msg(LVL_DEBUG2, "Set TLL->sysconfig (%p) to %x:%x.",
245 &usb_tll->sysconfig, usb_tll->sysconfig, sysc);
246 }
247
248 {
249 /* Smart idle for UHH */
250 uint32_t sysc = uhh_conf->sysconfig;
251 sysc |= UHH_SYSCONFIG_CLOCKACTIVITY_FLAG | UHH_SYSCONFIG_AUTOIDLE_FLAG;
252 sysc = (sysc
253 & ~(UHH_SYSCONFIG_SIDLE_MODE_MASK << UHH_SYSCONFIG_SIDLE_MODE_SHIFT)
254 ) | (0x2 << UHH_SYSCONFIG_SIDLE_MODE_SHIFT);
255 sysc = (sysc
256 & ~(UHH_SYSCONFIG_MIDLE_MODE_MASK << UHH_SYSCONFIG_MIDLE_MODE_SHIFT)
257 ) | (0x2 << UHH_SYSCONFIG_MIDLE_MODE_SHIFT);
258 ddf_msg(LVL_DEBUG2, "Set UHH->sysconfig (%p) to %x.",
259 &uhh_conf->sysconfig, uhh_conf->sysconfig);
260 uhh_conf->sysconfig = sysc;
261
262 /* All ports are connected on BBxM */
263 uhh_conf->hostconfig |= (UHH_HOSTCONFIG_P1_CONNECT_STATUS_FLAG
264 | UHH_HOSTCONFIG_P2_CONNECT_STATUS_FLAG
265 | UHH_HOSTCONFIG_P3_CONNECT_STATUS_FLAG);
266
267 /* Set all ports to go through TLL(UTMI)
268 * Direct connection can only work in HS mode */
269 uhh_conf->hostconfig |= (UHH_HOSTCONFIG_P1_ULPI_BYPASS_FLAG
270 | UHH_HOSTCONFIG_P2_ULPI_BYPASS_FLAG
271 | UHH_HOSTCONFIG_P3_ULPI_BYPASS_FLAG);
272 ddf_msg(LVL_DEBUG2, "Set UHH->hostconfig (%p) to %x.",
273 &uhh_conf->hostconfig, uhh_conf->hostconfig);
274 }
275
276 usb_tll->shared_conf |= TLL_SHARED_CONF_FCLK_IS_ON_FLAG;
277 ddf_msg(LVL_DEBUG2, "Set shared conf port (%p) to %x.",
278 &usb_tll->shared_conf, usb_tll->shared_conf);
279
280 for (unsigned i = 0; i < 3; ++i) {
281 uint32_t ch = usb_tll->channel_conf[i];
282 /* Clear Channel mode and FSLS mode */
283 ch &= ~(TLL_CHANNEL_CONF_CHANMODE_MASK
284 << TLL_CHANNEL_CONF_CHANMODE_SHIFT)
285 & ~(TLL_CHANNEL_CONF_FSLSMODE_MASK
286 << TLL_CHANNEL_CONF_FSLSMODE_SHIFT);
287
288 /* Serial mode is the only one capable of FS/LS operation. */
289 ch |= (TLL_CHANNEL_CONF_CHANMODE_UTMI_SERIAL_MODE
290 << TLL_CHANNEL_CONF_CHANMODE_SHIFT);
291
292 /* Select FS/LS mode, no idea what the difference is
293 * one of bidirectional modes might be good choice
294 * 2 = 3pin bidi phy. */
295 ch |= (2 << TLL_CHANNEL_CONF_FSLSMODE_SHIFT);
296
297 /* Write to register */
298 ddf_msg(LVL_DEBUG2, "Setting port %u(%p) to %x.",
299 i, &usb_tll->channel_conf[i], ch);
300 usb_tll->channel_conf[i] = ch;
301 }
[063ae706]302 return EOK;
303}
[09a0a8f0]304
305static bool rootamdm37x_add_fun(ddf_dev_t *dev, const char *name,
306 const char *str_match_id, const rootamdm37x_fun_t *fun)
307{
308 ddf_msg(LVL_DEBUG, "Adding new function '%s'.", name);
309
[2be2506a]310 /* Create new device function. */
311 ddf_fun_t *fnode = ddf_fun_create(dev, fun_inner, name);
[09a0a8f0]312 if (fnode == NULL)
[2be2506a]313 return ENOMEM;
[09a0a8f0]314
315
[2be2506a]316 /* Add match id */
317 if (ddf_fun_add_match_id(fnode, str_match_id, 100) != EOK) {
318 ddf_fun_destroy(fnode);
319 return false;
320 }
[09a0a8f0]321
322 /* Set provided operations to the device. */
[2be2506a]323 ddf_fun_data_implant(fnode, (void*)fun);
324 ddf_fun_set_ops(fnode, &rootamdm37x_fun_ops);
[09a0a8f0]325
326 /* Register function. */
327 if (ddf_fun_bind(fnode) != EOK) {
328 ddf_msg(LVL_ERROR, "Failed binding function %s.", name);
[601fa93]329 // TODO This will try to free our data!
[09a0a8f0]330 ddf_fun_destroy(fnode);
[2be2506a]331 return false;
[09a0a8f0]332 }
333
[2be2506a]334 return true;
[09a0a8f0]335}
336
[063ae706]337/** Add the root device.
[09a0a8f0]338 *
339 * @param dev Device which is root of the whole device tree
340 * (both of HW and pseudo devices).
341 *
342 * @return Zero on success, negative error number otherwise.
343 *
344 */
345static int rootamdm37x_dev_add(ddf_dev_t *dev)
346{
[063ae706]347 int ret = usb_clocks(true);
348 if (ret != EOK) {
349 ddf_msg(LVL_FATAL, "Failed to enable USB HC clocks!.\n");
350 return ret;
351 }
352
353 ret = usb_tll_init();
354 if (ret != EOK) {
355 ddf_msg(LVL_FATAL, "Failed to init USB TLL!.\n");
356 usb_clocks(false);
357 return ret;
[712a10b]358 }
359
[09a0a8f0]360 /* Register functions */
361 if (!rootamdm37x_add_fun(dev, "ohci", "usb/host=ohci", &ohci))
362 ddf_msg(LVL_ERROR, "Failed to add OHCI function for "
363 "BeagleBoard-xM platform.");
364 if (!rootamdm37x_add_fun(dev, "ehci", "usb/host=ehci", &ehci))
365 ddf_msg(LVL_ERROR, "Failed to add EHCI function for "
366 "BeagleBoard-xM platform.");
367
368 return EOK;
369}
370
371/** The root device driver's standard operations. */
372static driver_ops_t rootamdm37x_ops = {
373 .dev_add = &rootamdm37x_dev_add
374};
375
376/** The root device driver structure. */
377static driver_t rootamdm37x_driver = {
378 .name = NAME,
379 .driver_ops = &rootamdm37x_ops
380};
381
382static hw_resource_list_t *rootamdm37x_get_resources(ddf_fun_t *fnode)
383{
[2be2506a]384 rootamdm37x_fun_t *fun = ddf_fun_data_get(fnode);
[09a0a8f0]385 assert(fun != NULL);
386 return &fun->hw_resources;
387}
388
389static bool rootamdm37x_enable_interrupt(ddf_fun_t *fun)
390{
391 /* TODO */
392 return false;
393}
394
395int main(int argc, char *argv[])
396{
397 printf("%s: HelenOS AM/DM37x(OMAP37x) platform driver\n", NAME);
398 ddf_log_init(NAME, LVL_ERROR);
399 return ddf_driver_main(&rootamdm37x_driver);
400}
401
402/**
403 * @}
404 */
Note: See TracBrowser for help on using the repository browser.