Changeset 7a6d91b in mainline
- Timestamp:
- 2012-11-21T16:04:13Z (12 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 878b764
- Parents:
- 167c7b3
- Location:
- uspace/drv/infrastructure/rootamdm37x
- Files:
-
- 2 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/infrastructure/rootamdm37x/Makefile
r167c7b3 r7a6d91b 33 33 34 34 SOURCES = \ 35 amdm37x.c \ 35 36 rootamdm37x.c 36 37 -
uspace/drv/infrastructure/rootamdm37x/rootamdm37x.c
r167c7b3 r7a6d91b 39 39 #define DEBUG_CM 0 40 40 41 #include <ddf/driver.h>42 41 #include <ddf/log.h> 43 42 #include <errno.h> 44 43 #include <ops/hw_res.h> 45 44 #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 #include "prm/global_reg.h" 45 46 #include "amdm37x.h" 59 47 60 48 #define NAME "rootamdm37x" 61 62 typedef struct {63 uhh_regs_t *uhh;64 tll_regs_t *tll;65 struct {66 mpu_cm_regs_t *mpu;67 iva2_cm_regs_t *iva2;68 core_cm_regs_t *core;69 clock_control_cm_regs_t *clocks;70 usbhost_cm_regs_t *usbhost;71 } cm;72 struct {73 clock_control_prm_regs_t *clocks;74 global_reg_prm_regs_t *global;75 } prm;76 } amdm37x_t;77 78 static void log(const volatile void *place, uint32_t val, volatile void* base, size_t size, void *data, bool write)79 {80 printf("PIO %s: %p(%p) %#"PRIx32"\n", write ? "WRITE" : "READ",81 (place - base) + data, place, val);82 }83 84 static int amdm37x_hw_access_init(amdm37x_t *device)85 {86 assert(device);87 int ret = EOK;88 89 ret = pio_enable((void*)USBHOST_CM_BASE_ADDRESS, USBHOST_CM_SIZE,90 (void**)&device->cm.usbhost);91 if (ret != EOK)92 return ret;93 94 ret = pio_enable((void*)CORE_CM_BASE_ADDRESS, CORE_CM_SIZE,95 (void**)&device->cm.core);96 if (ret != EOK)97 return ret;98 99 ret = pio_enable((void*)CLOCK_CONTROL_CM_BASE_ADDRESS,100 CLOCK_CONTROL_CM_SIZE, (void**)&device->cm.clocks);101 if (ret != EOK)102 return ret;103 104 ret = pio_enable((void*)MPU_CM_BASE_ADDRESS,105 MPU_CM_SIZE, (void**)&device->cm.mpu);106 if (ret != EOK)107 return ret;108 109 ret = pio_enable((void*)IVA2_CM_BASE_ADDRESS,110 IVA2_CM_SIZE, (void**)&device->cm.iva2);111 if (ret != EOK)112 return ret;113 114 ret = pio_enable((void*)CLOCK_CONTROL_PRM_BASE_ADDRESS,115 CLOCK_CONTROL_PRM_SIZE, (void**)&device->prm.clocks);116 if (ret != EOK)117 return ret;118 119 ret = pio_enable((void*)GLOBAL_REG_PRM_BASE_ADDRESS,120 GLOBAL_REG_PRM_SIZE, (void**)&device->prm.global);121 if (ret != EOK)122 return ret;123 124 ret = pio_enable((void*)AMDM37x_USBTLL_BASE_ADDRESS,125 AMDM37x_USBTLL_SIZE, (void**)&device->tll);126 if (ret != EOK)127 return ret;128 129 ret = pio_enable((void*)AMDM37x_UHH_BASE_ADDRESS,130 AMDM37x_UHH_SIZE, (void**)&device->uhh);131 if (ret != EOK)132 return ret;133 134 if (DEBUG_CM) {135 pio_trace_enable(device->tll, AMDM37x_USBTLL_SIZE, log, (void*)AMDM37x_USBTLL_BASE_ADDRESS);136 pio_trace_enable(device->cm.clocks, CLOCK_CONTROL_CM_SIZE, log, (void*)CLOCK_CONTROL_CM_BASE_ADDRESS);137 pio_trace_enable(device->cm.core, CORE_CM_SIZE, log, (void*)CORE_CM_BASE_ADDRESS);138 pio_trace_enable(device->cm.mpu, MPU_CM_SIZE, log, (void*)MPU_CM_BASE_ADDRESS);139 pio_trace_enable(device->cm.iva2, IVA2_CM_SIZE, log, (void*)IVA2_CM_BASE_ADDRESS);140 pio_trace_enable(device->cm.usbhost, USBHOST_CM_SIZE, log, (void*)USBHOST_CM_BASE_ADDRESS);141 pio_trace_enable(device->uhh, AMDM37x_UHH_SIZE, log, (void*)AMDM37x_UHH_BASE_ADDRESS);142 pio_trace_enable(device->prm.clocks, CLOCK_CONTROL_PRM_SIZE, log, (void*)CLOCK_CONTROL_PRM_BASE_ADDRESS);143 pio_trace_enable(device->prm.global, GLOBAL_REG_PRM_SIZE, log, (void*)GLOBAL_REG_PRM_BASE_ADDRESS);144 }145 return EOK;146 }147 148 149 150 /** Set DPLLs 1,2,3,4,5 to ON (locked) and autoidle.151 * @param device Register map.152 *153 * The idea is to get all DPLLs running and make hw control their power mode,154 * based on the module requirements (module ICLKs and FCLKs).155 */156 static void dpll_on_autoidle(amdm37x_t *device)157 {158 assert(device);159 /* Get SYS_CLK value, it is used as reference clock by all DPLLs,160 * NFI who sets this or why it is set to specific value. */161 const unsigned osc_clk = pio_read_32(&device->prm.clocks->clksel)162 & CLOCK_CONTROL_PRM_CLKSEL_SYS_CLKIN_MASK;163 const unsigned clk_reg = pio_read_32(&device->prm.global->clksrc_ctrl);164 const unsigned base_freq = sys_clk_freq_kHz(osc_clk)165 / GLOBAL_REG_PRM_CLKSRC_CTRL_SYSCLKDIV_GET(clk_reg);166 ddf_msg(LVL_NOTE, "Base frequency: %d.%dMhz",167 base_freq / 1000, base_freq % 1000);168 169 170 /* DPLL1 provides MPU(CPU) clock.171 * It uses SYS_CLK as reference clock and core clock (DPLL3) as172 * high frequency bypass (MPU then runs on L3 interconnect freq).173 * It should be setup by fw or u-boot.*/174 mpu_cm_regs_t *mpu = device->cm.mpu;175 176 /* Current MPU frequency. */177 if (pio_read_32(&mpu->clkstst) & MPU_CM_CLKSTST_CLKACTIVITY_MPU_ACTIVE_FLAG) {178 if (pio_read_32(&mpu->idlest_pll) & MPU_CM_IDLEST_PLL_ST_MPU_CLK_LOCKED_FLAG) {179 /* DPLL active and locked */180 const uint32_t reg = pio_read_32(&mpu->clksel1_pll);181 const unsigned multiplier =182 (reg & MPU_CM_CLKSEL1_PLL_MPU_DPLL_MULT_MASK)183 >> MPU_CM_CLKSEL1_PLL_MPU_DPLL_MULT_SHIFT;184 const unsigned divisor =185 (reg & MPU_CM_CLKSEL1_PLL_MPU_DPLL_DIV_MASK)186 >> MPU_CM_CLKSEL1_PLL_MPU_DPLL_DIV_SHIFT;187 const unsigned divisor2 =188 (pio_read_32(&mpu->clksel2_pll)189 & MPU_CM_CLKSEL2_PLL_MPU_DPLL_CLKOUT_DIV_MASK);190 if (multiplier && divisor && divisor2) {191 /** See AMDM37x TRM p. 300 for the formula */192 const unsigned freq =193 ((base_freq * multiplier) / (divisor + 1))194 / divisor2;195 ddf_msg(LVL_NOTE, "MPU running at %d.%d MHz",196 freq / 1000, freq % 1000);197 } else {198 ddf_msg(LVL_WARN, "Frequency divisor and/or "199 "multiplier value invalid: %d %d %d",200 multiplier, divisor, divisor2);201 }202 } else {203 /* DPLL in LP bypass mode */204 const unsigned divisor =205 MPU_CM_CLKSEL1_PLL_MPU_CLK_SRC_VAL(206 pio_read_32(&mpu->clksel1_pll));207 ddf_msg(LVL_NOTE, "MPU DPLL in bypass mode, running at"208 " CORE CLK / %d MHz", divisor);209 }210 } else {211 ddf_msg(LVL_WARN, "MPU clock domain is not active, we should not be running...");212 }213 // TODO: Enable this (automatic MPU downclocking):214 #if 0215 /* Enable low power bypass mode, this will take effect the next lock or216 * relock sequence. */217 //TODO: We might need to force re-lock after enabling this218 pio_set_32(&mpu->clken_pll, MPU_CM_CLKEN_PLL_EN_MPU_DPLL_LP_MODE_FLAG, 5);219 /* Enable automatic relocking */220 pio_change_32(&mpu->autoidle_pll, MPU_CM_AUTOIDLE_PLL_AUTO_MPU_DPLL_ENABLED, MPU_CM_AUTOIDLE_PLL_AUTO_MPU_DPLL_MASK, 5);221 #endif222 223 /* DPLL2 provides IVA(video acceleration) clock.224 * It uses SYS_CLK as reference clokc and core clock (DPLL3) as225 * high frequency bypass (IVA runs on L3 freq).226 */227 // TODO: We can probably turn this off entirely. IVA is left unused.228 /* Enable low power bypass mode, this will take effect the next lock or229 * relock sequence. */230 //TODO: We might need to force re-lock after enabling this231 pio_set_32(&device->cm.iva2->clken_pll, MPU_CM_CLKEN_PLL_EN_MPU_DPLL_LP_MODE_FLAG, 5);232 /* Enable automatic relocking */233 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);234 235 /* DPLL3 provides tons of clocks:236 * CORE_CLK, COREX2_CLK, DSS_TV_CLK, 12M_CLK, 48M_CLK, 96M_CLK, L3_ICLK,237 * and L4_ICLK. It uses SYS_CLK as reference clock and low frequency238 * bypass. It should be setup by fw or u-boot as it controls critical239 * interconnects.240 */241 if (pio_read_32(&device->cm.clocks->idlest_ckgen) & CLOCK_CONTROL_CM_IDLEST_CKGEN_ST_CORE_CLK_FLAG) {242 /* DPLL active and locked */243 const uint32_t reg =244 pio_read_32(&device->cm.clocks->clksel1_pll);245 const unsigned multiplier =246 CLOCK_CONTROL_CM_CLKSEL1_PLL_CORE_DPLL_MULT_GET(reg);247 const unsigned divisor =248 CLOCK_CONTROL_CM_CLKSEL1_PLL_CORE_DPLL_DIV_GET(reg);249 const unsigned divisor2 =250 CLOCK_CONTROL_CM_CLKSEL1_PLL_CORE_DPLL_CLKOUT_DIV_GET(reg);251 if (multiplier && divisor && divisor2) {252 /** See AMDM37x TRM p. 300 for the formula */253 const unsigned freq =254 ((base_freq * multiplier) / (divisor + 1)) / divisor2;255 ddf_msg(LVL_NOTE, "CORE CLK running at %d.%d MHz",256 freq / 1000, freq % 1000);257 const unsigned l3_div =258 pio_read_32(&device->cm.core->clksel)259 & CORE_CM_CLKSEL_CLKSEL_L3_MASK;260 if (l3_div == CORE_CM_CLKSEL_CLKSEL_L3_DIVIDED1 ||261 l3_div == CORE_CM_CLKSEL_CLKSEL_L3_DIVIDED2) {262 ddf_msg(LVL_NOTE, "L3 interface at %d.%d MHz",263 (freq / l3_div) / 1000,264 (freq / l3_div) % 1000);265 } else {266 ddf_msg(LVL_WARN,"L3 interface clock divisor is"267 " invalid: %d", l3_div);268 }269 } else {270 ddf_msg(LVL_WARN, "DPLL3 frequency divisor and/or "271 "multiplier value invalid: %d %d %d",272 multiplier, divisor, divisor2);273 }274 } else {275 ddf_msg(LVL_WARN, "CORE CLK in bypass mode, fruunig at SYS_CLK"276 " frreq of %d.%d MHz", base_freq / 1000, base_freq % 1000);277 }278 279 /* Set DPLL3 to automatic to save power */280 pio_change_32(&device->cm.clocks->autoidle_pll,281 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_AUTOMATIC,282 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_CORE_DPLL_MASK, 5);283 284 /* DPLL4 provides peripheral domain clocks:285 * CAM_MCLK, EMU_PER_ALWON_CLK, DSS1_ALWON_FCLK, and 96M_ALWON_FCLK.286 * It uses SYS_CLK as reference clock and low frequency bypass.287 * 96M clock is used by McBSP[1,5], MMC[1,2,3], I2C[1,2,3], so288 * we can probably turn this off entirely (DSS is still non-functional).289 */290 /* Set DPLL4 to automatic to save power */291 pio_change_32(&device->cm.clocks->autoidle_pll,292 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_AUTOMATIC,293 CLOCK_CONTROL_CM_AUTOIDLE_PLL_AUTO_PERIPH_DPLL_MASK, 5);294 295 /* DPLL5 provide peripheral domain clocks: 120M_FCLK.296 * It uses SYS_CLK as reference clock and low frequency bypass.297 * 120M clock is used by HS USB and USB TLL.298 */299 // TODO setup DPLL5300 if ((pio_read_32(&device->cm.clocks->clken2_pll)301 & CLOCK_CONTROL_CM_CLKEN2_PLL_EN_PERIPH2_DPLL_MASK)302 != CLOCK_CONTROL_CM_CLKEN2_PLL_EN_PERIPH2_DPLL_LOCK) {303 /* Compute divisors and multiplier304 * See AMDM37x TRM p. 300 for the formula */305 assert((base_freq % 100) == 0);306 const unsigned mult = 1200;307 const unsigned div = (base_freq / 100) - 1;308 const unsigned div2 = 1;309 310 /* Set multiplier */311 pio_change_32(&device->cm.clocks->clksel4_pll,312 CLOCK_CONTROL_CM_CLKSEL4_PLL_PERIPH2_DPLL_MULT_CREATE(mult),313 CLOCK_CONTROL_CM_CLKSEL4_PLL_PERIPH2_DPLL_MULT_MASK, 10);314 315 /* Set DPLL divisor */316 pio_change_32(&device->cm.clocks->clksel4_pll,317 CLOCK_CONTROL_CM_CLKSEL4_PLL_PERIPH2_DPLL_DIV_CREATE(div),318 CLOCK_CONTROL_CM_CLKSEL4_PLL_PERIPH2_DPLL_DIV_MASK, 10);319 320 /* Set output clock divisor */321 pio_change_32(&device->cm.clocks->clksel5_pll,322 CLOCK_CONTROL_CM_CLKSEL5_PLL_DIV120M_CREATE(div2),323 CLOCK_CONTROL_CM_CLKSEL5_PLL_DIV120M_MASK, 10);324 325 /* Start DPLL5 */326 pio_change_32(&device->cm.clocks->clken2_pll,327 CLOCK_CONTROL_CM_CLKEN2_PLL_EN_PERIPH2_DPLL_LOCK,328 CLOCK_CONTROL_CM_CLKEN2_PLL_EN_PERIPH2_DPLL_MASK, 10);329 330 }331 /* Set DPLL5 to automatic to save power */332 pio_change_32(&device->cm.clocks->autoidle2_pll,333 CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_AUTOMATIC,334 CLOCK_CONTROL_CM_AUTOIDLE2_PLL_AUTO_PERIPH2_DPLL_MASK, 5);335 }336 337 /** Enable/disable function and interface clocks for USBTLL and USBHOST.338 * @param device Register map.339 * @param on True to switch clocks on.340 */341 static void usb_clocks_enable(amdm37x_t *device, bool on)342 {343 if (on) {344 /* Enable interface and function clock for USB TLL */345 pio_set_32(&device->cm.core->fclken3,346 CORE_CM_FCLKEN3_EN_USBTLL_FLAG, 5);347 pio_set_32(&device->cm.core->iclken3,348 CORE_CM_ICLKEN3_EN_USBTLL_FLAG, 5);349 350 /* Enable interface and function clock for USB hosts */351 pio_set_32(&device->cm.usbhost->fclken,352 USBHOST_CM_FCLKEN_EN_USBHOST1_FLAG |353 USBHOST_CM_FCLKEN_EN_USBHOST2_FLAG, 5);354 pio_set_32(&device->cm.usbhost->iclken,355 USBHOST_CM_ICLKEN_EN_USBHOST, 5);356 357 if (DEBUG_CM) {358 printf("DPLL5 (and everything else) should be on: %"359 PRIx32" %"PRIx32".\n",360 pio_read_32(&device->cm.clocks->idlest_ckgen),361 pio_read_32(&device->cm.clocks->idlest2_ckgen));362 }363 } else {364 /* Disable interface and function clock for USB hosts */365 pio_clear_32(&device->cm.usbhost->iclken,366 USBHOST_CM_ICLKEN_EN_USBHOST, 5);367 pio_clear_32(&device->cm.usbhost->fclken,368 USBHOST_CM_FCLKEN_EN_USBHOST1_FLAG |369 USBHOST_CM_FCLKEN_EN_USBHOST2_FLAG, 5);370 371 /* Disable interface and function clock for USB TLL */372 pio_clear_32(&device->cm.core->iclken3,373 CORE_CM_ICLKEN3_EN_USBTLL_FLAG, 5);374 pio_clear_32(&device->cm.core->fclken3,375 CORE_CM_FCLKEN3_EN_USBTLL_FLAG, 5);376 }377 }378 379 /** Initialize USB TLL port connections.380 *381 * Different modes are on page 3312 of the Manual Figure 22-34.382 * Select mode than can operate in FS/LS.383 */384 static int usb_tll_init(amdm37x_t *device)385 {386 /* Check access */387 if (pio_read_32(&device->cm.core->idlest3) & CORE_CM_IDLEST3_ST_USBTLL_FLAG) {388 ddf_msg(LVL_ERROR, "USB TLL is not accessible");389 return EIO;390 }391 392 /* Reset USB TLL */393 pio_set_32(&device->tll->sysconfig, TLL_SYSCONFIG_SOFTRESET_FLAG, 5);394 ddf_msg(LVL_DEBUG2, "Waiting for USB TLL reset");395 while (!(pio_read_32(&device->tll->sysstatus) & TLL_SYSSTATUS_RESET_DONE_FLAG));396 ddf_msg(LVL_DEBUG, "USB TLL Reset done.");397 398 /* Setup idle mode (smart idle) */399 pio_change_32(&device->tll->sysconfig,400 TLL_SYSCONFIG_CLOCKACTIVITY_FLAG | TLL_SYSCONFIG_AUTOIDLE_FLAG |401 TLL_SYSCONFIG_SIDLE_MODE_SMART, TLL_SYSCONFIG_SIDLE_MODE_MASK, 5);402 403 /* Smart idle for UHH */404 pio_change_32(&device->uhh->sysconfig,405 UHH_SYSCONFIG_CLOCKACTIVITY_FLAG | UHH_SYSCONFIG_AUTOIDLE_FLAG |406 UHH_SYSCONFIG_SIDLE_MODE_SMART, UHH_SYSCONFIG_SIDLE_MODE_MASK, 5);407 408 /* Set all ports to go through TLL(UTMI)409 * Direct connection can only work in HS mode */410 pio_set_32(&device->uhh->hostconfig,411 UHH_HOSTCONFIG_P1_ULPI_BYPASS_FLAG |412 UHH_HOSTCONFIG_P2_ULPI_BYPASS_FLAG |413 UHH_HOSTCONFIG_P3_ULPI_BYPASS_FLAG, 5);414 415 /* What is this? */416 pio_set_32(&device->tll->shared_conf, TLL_SHARED_CONF_FCLK_IS_ON_FLAG, 5);417 418 for (unsigned i = 0; i < 3; ++i) {419 /* Serial mode is the only one capable of FS/LS operation.420 * Select FS/LS mode, no idea what the difference is421 * one of bidirectional modes might be good choice422 * 2 = 3pin bidi phy. */423 pio_change_32(&device->tll->channel_conf[i],424 TLL_CHANNEL_CONF_CHANMODE_UTMI_SERIAL_MODE |425 TLL_CHANNEL_CONF_FSLSMODE_3PIN_BIDI_PHY,426 TLL_CHANNEL_CONF_CHANMODE_MASK |427 TLL_CHANNEL_CONF_FSLSMODE_MASK, 5);428 }429 return EOK;430 }431 49 432 50 typedef struct { … … 544 162 if (!device) 545 163 return ENOMEM; 546 int ret = amdm37x_ hw_access_init(device);164 int ret = amdm37x_init(device, DEBUG_CM); 547 165 if (ret != EOK) { 548 166 ddf_msg(LVL_FATAL, "Failed to setup hw access!.\n"); … … 551 169 552 170 /* Set dplls to ON and automatic */ 553 dpll_on_autoidle(device);171 amdm37x_setup_dpll_on_autoidle(device); 554 172 555 173 /* Enable function and interface clocks */ 556 usb_clocks_enable(device, true);174 amdm37x_usb_clocks_set(device, true); 557 175 558 176 /* Init TLL */ 559 ret = usb_tll_init(device);177 ret = amdm37x_usb_tll_init(device); 560 178 if (ret != EOK) { 561 179 ddf_msg(LVL_FATAL, "Failed to init USB TLL!.\n"); 562 usb_clocks_enable(device, false);180 amdm37x_usb_clocks_set(device, false); 563 181 return ret; 564 182 } … … 586 204 }; 587 205 588 static hw_resource_list_t * rootamdm37x_get_resources(ddf_fun_t *fnode)206 static hw_resource_list_t * rootamdm37x_get_resources(ddf_fun_t *fnode) 589 207 { 590 208 rootamdm37x_fun_t *fun = ddf_fun_data_get(fnode); … … 595 213 static bool rootamdm37x_enable_interrupt(ddf_fun_t *fun) 596 214 { 597 / * TODO */215 //TODO: Implement 598 216 return false; 599 217 }
Note:
See TracChangeset
for help on using the changeset viewer.