📄 prcm_34xx.c
字号:
if (dpll_id == DPLL4_PER) { new_reg_val = *addr & valid; new_reg_val |= (freq_sel << 20); *addr = new_reg_val; } else { new_reg_val = *addr & valid; new_reg_val |= (freq_sel << 4); *addr = new_reg_val; } *addr_auto = dpll_autoidle; return PRCM_PASS;}/*============================================================================*//*======================== PUT DPLL IN BYPASS ================================*//*============================================================================*//*= This function will put the specified DPLL in various Bypass modes as =*//*= specified by bypass_mode. The function waits for the DPLL to go into =*//*= the given bypass_mode before returning =*//*============================================================================*/int prcm_put_dpll_in_bypass(u32 dpll_id, u32 bypass_mode){ u32 new_val; volatile u32 *addr, *addr_auto; u32 dpll_autoidle; u32 loop_cnt = 0, retries_cnt = 0; int ret = PRCM_FAIL; if (dpll_id > NO_OF_DPLL) return ret; /* Currently, the API does not allow putting CORE dpll in bypass mode * To safely put dpll in bypass mode, it is better to execute code * from sram so that there is no access to sdram */ if (dpll_id == DPLL3_CORE) return ret; DPRINTK("dpllid:%d\n", dpll_id); addr = get_addr_pll(dpll_id, REG_CLKEN_PLL); if (!addr) return ret; /* This is needed if the condition in while loop returns true the */ /*very first time*/ ret = PRCM_PASS; /* Store the DPLL autoidle */ addr_auto = get_addr_pll(dpll_id, REG_AUTOIDLE_PLL); dpll_autoidle = *addr_auto; *addr_auto = 0x0; if (dpll_id == DPLL1_MPU) { new_val = (*addr & DPLL_ENBIT_MASK) | LOW_POWER_BYPASS; *addr = new_val; while (*get_addr_pll(dpll_id, REG_IDLEST_PLL) & 0x1) { ret = loop_wait(&loop_cnt, &retries_cnt, 1000); if (ret != PRCM_PASS) break; } } else if (dpll_id == DPLL4_PER) { new_val = (*addr & DPLL4_ENBIT_MASK) | (LOW_POWER_STOP << 16); *addr = new_val; while (*get_addr_pll(dpll_id, REG_IDLEST_PLL) & 0x2) { ret = loop_wait(&loop_cnt, &retries_cnt, 1000); if (ret != PRCM_PASS) break; } } else { if ((dpll_id == DPLL5_PER2) && (bypass_mode != LOW_POWER_STOP)) return ret; new_val = (*addr & DPLL_ENBIT_MASK) | bypass_mode; *addr = new_val; while (*get_addr_pll(dpll_id, REG_IDLEST_PLL) & 0x1) { ret = loop_wait(&loop_cnt, &retries_cnt, 1000); if (ret != PRCM_PASS) break; } } if (ret != PRCM_PASS) printk(KERN_INFO "Loop count exceeded in " "prcm_put_dpll_in_bypass for dpll:%u\n", dpll_id); /* Restore the autoidle for the DPLL back */ *addr_auto = dpll_autoidle; return ret;}/*============================================================================*//*======================== DPLL AUTO CONTROL =================================*//*============================================================================*//*= This function will enable/disable the auto control feature of the =*//*= specified dpll. =*//*= The parameter passed as input should be DPLL_AUTOIDLE,DPLL_NO_AUTOIDLE =*//*= or DPLL_AUTOIDLE_BYPASS =*//*============================================================================*/int prcm_dpll_clock_auto_control(u32 dpll_id, u32 control){ u32 valid; volatile u32 *addr; u32 setting; if (dpll_id > NO_OF_DPLL) return PRCM_FAIL; addr = get_addr_pll(dpll_id, REG_AUTOIDLE_PLL); if (!addr) return PRCM_FAIL; valid = get_val_bits_pll(dpll_id, REG_AUTOIDLE_PLL); if (dpll_id == DPLL4_PER) setting = 1 << 3; else if (dpll_id == DPLL3_CORE) { *addr &= ~CORE3_DPLL_MASK; switch (control) { case DPLL_AUTOIDLE_BYPASS: setting = 5; break; case DPLL_NO_AUTOIDLE: case DPLL_AUTOIDLE: setting = 1; break; default: return PRCM_FAIL; } } else setting = 1; switch (control) { case DPLL_AUTOIDLE: *addr |= setting; break; case DPLL_NO_AUTOIDLE: *addr &= (~setting); break; case DPLL_AUTOIDLE_BYPASS: *addr |= setting; break; default: return PRCM_FAIL; } return PRCM_PASS;}/*============================================================================*//* This function returns the mn output of a dpll *//* (without m2,m3,m4,m5,m6 dividers *//* In other words, it returns CLKOUT = (Fref * m)/(n+1) in case dpll is locked*//* And bypass clock if it is in bypass mode. *//* The frequency value returned in mn_output is in Khz *//*============================================================================*/int prcm_get_dpll_mn_output(u32 dpll, u32 *mn_output){ u32 dpll_idlest_lock_bit, clksel1_pll, dpll_id; u32 sys_clkspeed, core_clkspeed; int bypassclk_divider; u32 mult, div; volatile u32 *addr; dpll_id = (dpll >> DPLL_NO_POS) & DPLL_NO_MASK; if (dpll_id > NO_OF_DPLL) return PRCM_FAIL; dpll_idlest_lock_bit = get_idlest_lock_bit(dpll_id); DPRINTK("dpll: %d\n", dpll_id); addr = get_addr_pll(dpll_id, REG_IDLEST_PLL); /* Get the sys clock speed */ sys_clkspeed = prcm_get_system_clock_speed(); DPRINTK("SYSCLKSPEED = %d\n", sys_clkspeed); if (*addr & dpll_idlest_lock_bit) { /* dpll locked */ get_dpll_m_n(dpll_id, &mult, &div); DPRINTK("DPLL not in bypass,M=%d, N=%d\n", mult, div); *mn_output = ((sys_clkspeed * mult) / (div + 1)); } else { /* dpll is in bypass mode */ DPRINTK("DPLL in bypass\n"); if ((dpll_id == DPLL3_CORE) || (dpll_id == DPLL4_PER) || (dpll_id == DPLL5_PER2)) *mn_output = sys_clkspeed; else { /* DPLL1 and DPLL2 * Check if DPLL3 is in bypass */ prcm_get_dpll_rate(PRCM_DPLL3_M2X2_CLK, &core_clkspeed); DPRINTK("Core clk speed: %d\n", core_clkspeed); if (dpll_id == DPLL1_MPU) clksel1_pll = CM_CLKSEL1_PLL_MPU; else clksel1_pll = CM_CLKSEL1_PLL_IVA2; bypassclk_divider = (clksel1_pll >> 19) & 0x3; *mn_output = core_clkspeed / bypassclk_divider; } } DPRINTK("DPLL_MN_OUTPUT: %d\n", *mn_output); return PRCM_PASS;}/*============================================================================*//*======================== GET DPLL RATE =====================================*//*============================================================================*//*= This function returns the rate for the specified dpll in KHz. It checks =*//*= if the DPLL is in locked mode or unlocked mode and accordingly calculates=*//*= the output. =*//*============================================================================*/int prcm_get_dpll_rate(u32 dpll, u32 *output){ u32 dpll_id, dpll_div, dpll_mxsft, id_type; u32 mx, omap; u32 mn_output; id_type = get_id_type(dpll); if (!(id_type & ID_DPLL_OP)) return PRCM_FAIL; /*Not dpll op */ omap = OMAP(dpll); if (cpu_is_omap3430() && !(omap & (AT_3430|AT_3430_ES2))) return PRCM_FAIL; dpll_id = (dpll >> DPLL_NO_POS) & DPLL_NO_MASK; if (dpll_id > NO_OF_DPLL) return PRCM_FAIL; dpll_div = (dpll >> DPLL_DIV_POS) & DPLL_DIV_MASK; dpll_mxsft = dpll_mx_shift[dpll_id-1][dpll_div]; DPRINTK("dpllmxsft:%d\n", dpll_mxsft); DPRINTK("Output required:%d\n", dpll_div); mx = get_dpll_mx(dpll_id, dpll_div, dpll_mxsft); DPRINTK("MX: %d\n", mx); prcm_get_dpll_mn_output(dpll, &mn_output); /* Except for DPLL_M2 all clocks are (mn_output*2)/mx */ if (dpll_div == DPLL_M2) *output = (mn_output) / mx; else *output = (2 * mn_output) / mx; DPRINTK("Output: %d\n", *output); return PRCM_PASS;}/*============================================================================*//*======================== CONFIGURE DPLL DIVIDER ============================*//*============================================================================*//*= Each DPLL can have upto six independent output clocks based on various =*//*= divider values. This function configures the different divider values for=*//*= the specified dpll. =*//*============================================================================*/int prcm_configure_dpll_divider(u32 dpll, u32 setting){ u32 dpll_id, valid, dpll_mxsft, omap, id_type; u32 dpll_div, new_val = 0x00000000; volatile u32 *addr; id_type = get_id_type(dpll); if (!(id_type & ID_DPLL_OP)) return PRCM_FAIL; /*Not DPLL OP */ omap = OMAP(dpll); if (cpu_is_omap3430() && !(omap & (AT_3430|AT_3430_ES2))) return PRCM_FAIL; dpll_id = (dpll >> DPLL_NO_POS) & DPLL_NO_MASK; if (dpll_id > NO_OF_DPLL) return PRCM_FAIL; if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0)) { /* Workaround for limitation 2.5 */ /* On ES 1.0, DPLL4 dividers cannot be changed */ if (dpll_id == DPLL4_PER) return PRCM_FAIL; } dpll_div = (dpll >> DPLL_DIV_POS) & DPLL_DIV_MASK; dpll_mxsft = dpll_mx_shift[dpll_id-1][dpll_div]; addr = get_addr_pll(dpll_id, dpll_div + MX_ARRAY_OFFSET); valid = get_val_bits_pll(dpll_id, dpll_div + MX_ARRAY_OFFSET); new_val = (*addr & valid) | (setting << dpll_mxsft); *addr = new_val; return PRCM_PASS;}/*============================================================================*//*======================== CLOCK DOMAIN STATE ===============================*//*============================================================================*//*= This function returns the state of the clock domain. It reads the =*//*= CM_CLKSTST_<DOMAIN> register and returns the result as PRCM_ENABLE if =*//*= the clock domain is active, else returns PRCM_DISABLE =*//*= a wake up event. If disabled, it masks the wake up event from the =*//*= specified module. This register will modify the PM_WKEN_<DOMAIN> =*//*= register. This function will return PRCM_FAIL if the parameters passed =*//*= are invalid, otherwise it will return PRCM_PASS. Valid parameters for =*//*= control are PRCM_ENABLE and PRCM_DISABLE. =*//*============================================================================*/int prcm_is_clock_domain_active(u32 domainid, u8 *result){ u32 valid; volatile u32 *addr; /* Core domain check is not supported */ if ((domainid == DOM_CORE1) || (domainid == DOM_CORE2)) { printk(KERN_INFO "Currently prcm_is_clock_domain_active for " "the following domains (DOM_CORE1/DOM_CORE2) " "is not supported\n"); return PRCM_FAIL; } addr = get_addr(domainid, REG_CLKSTST); valid = get_val_bits(domainid, REG_CLKSTST); if (!addr) return PRCM_FAIL; if (*addr & valid) { *result = PRCM_ENABLE; return PRCM_PASS; } else { *result = PRCM_DISABLE; return PRCM_PASS; }}/*============================================================================*//*======================== CLOCK DOMAIN STATUS ===============================*//*============================================================================*//* This function waits for the clock domain to transition to the desired state*//* It polls on the clock domain state and times out after a wait of ~500 micro*//* secs. It returns PRCM_PASS id the clock domain transitions to the desired *//* state within the timeout period, else return PRCM_FAIL *//*============================================================================*/static int prcm_check_clock_domain_status(u32 domainid, u8 desired_state){ u8 curr_state; u32 loop_cnt = 0, retries_cnt = 0; int ret; if (domainid > PRCM_NUM_DOMAINS) return PRCM_FAIL; /* Core domain check is not supported */ if ( (domainid == DOM_CORE1) || (domainid == DOM_CORE2) || (domainid == DOM_MPU)) { printk ("prcm_check_clock_domain_status : " "Not currently supported for " "DOM_CORE1, DOM_CORE2, DOM_MPU\n") ; return PRCM_FAIL; } ret = prcm_is_clock_domain_active(domainid, &curr_state); if (ret != PRCM_PASS) return ret; while (curr_state != desired_state) { ret = prcm_is_clock_domain_active(domainid, &curr_state); if (ret != PRCM_PASS) return ret; ret = loop_wait(&loop_cnt, &retries_cnt, 100); if (ret != PRCM_PASS) { printk (".");/* * Uncomment for debug. printk (KERN_INFO "prcm_check_clock_domain_status : " "Timeout waiting for clock active. " "(domain 0x%x) (%d) (%d)\n", domainid, curr_state, desired_state) ;*/ return ret; } } return PRCM_PASS;}/*============================================================================*//*======================== SET CLOCK DOMAIN STATE ============================*//*============================================================================*//* This function sets the clock domain state to the 'new_state' specified. In *//* software supervised mode it checks if all the pre-conditions for clock *//* domain transition are met. If check_accessibility is set to PRCM_TRUE the *//* function also waits for the clock domain transition to complete *//*============================================================================*/int prcm_set_clock_domain_state(u32 domainid, u8 new_state, u8 check_state){ volatile u32 *addr; u32 fclk_mask = 0, iclk_mask = 0, init_mask, dev_mask; u32 new_val, valid; /* * Validate the pre-conditions: * 1) Domain is valid. * 2) DOM_CORE1, DOM_CORE2 are currently not supported. * 3) Domain has a CLKSTCTRL register * 4) 'check_state' is valid for requested 'new_state' * 5) 'new_state' is valid for requested 'domainid' */ if (domainid > PRCM_NUM_DOMAINS) return PRCM_FAIL; if ( (domainid == DOM_CORE1) || (domainid == DOM_CORE2)) { printk (KERN_INFO "prcm_set_clock_domain_state : " "DOM_CORE1, DOM_CORE2 currently not supported.\n") ; return PRCM_FAIL; } addr = get_addr(domainid, REG_CLKSTCTRL); valid = get_val_bits(domainid, REG_CLKSTCTRL); if (!addr) { printk (KERN_INFO "prcm_set_clock_domain_state : " "No ClkStCtrl for domain %d\n", domainid) ; return PRCM_FAIL; } /* It is not appropriate to pass check_state = TRUE for states * PRCM_NO_AUTO and PRCM_HWSUP_AUTO. * In case of PRCM_NO_AUTO, hardware control of clock domain is disabled * In case of PRCM_HWSUP_AUTO, the clock domain will transition * automatically when conditions are satisfied */ if (check_state == PRCM_TRUE) { if ( (new_state == PRCM_NO_AUTO) || (new_state == PRCM_HWSUP_AUTO)) { printk(KERN_INFO "prcm_set_clock_domain_state : " "Cannot wait for change of state to 0x%x.\n", new_state) ; return PRCM_FAIL; } } /* Check preconditions for SWSUP sleep if check_state = TRUE */ if (new_state == PRCM_SWSUP_SLEEP) { if (domainid == DOM_MPU) { /* There is no SWSUPERVISED sleep for MPU*/ return PRCM_FAIL; } if (check_state == PRCM_TRUE) { /* * Are FCLK and ICLK disabled for the domain? */ prcm_get_domain_functional_clocks(domainid, &fclk_mask); prcm_get_domain_interface_clocks(domainid, &iclk_mask); if (fclk_mask || iclk_mask) { printk (KERN_INFO "prcm_set_clock_domain_state : " "Pre-condition not met. " "Domain : 0x%x "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -