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

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

rootamdm37x: Use new io functions to set dplls.

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