📄 prcm_34xx.c
字号:
* * Checks whether a specified DPLL is locked or not. * Returns PRCM_TRUE if it is locked, PRCM_FALSE otherwise. */static int is_dpll_locked(u32 dpll_id, int *result){ volatile u32 *addr; u32 dpll_enbit_mask, dpll_idlest_lock_bit; addr = get_addr_pll(dpll_id, REG_CLKEN_PLL); if (!addr) return PRCM_FAIL; dpll_enbit_mask = get_dpll_enbitmask(dpll_id); dpll_idlest_lock_bit = get_idlest_lock_bit(dpll_id); if ((*addr & (~dpll_enbit_mask)) == (~dpll_enbit_mask)) *result = PRCM_TRUE; else *result = PRCM_FALSE; return PRCM_PASS;}/** * check_accessibility: Is device accessible? * @deviceid: Device id * @clk_type: * * Checks whether a specified device is accessible by probing FCLK and ICLK. * Returns PRCM_TRUE if it is accessible, PRCM_FALSE otherwise. */static int check_accessibility(u32 deviceid, u8 clk_type){ u32 valid = 0, enbit, type, domain; volatile u32 *addr = 0; enbit = DEV_BIT_POS(deviceid); domain = DOMAIN_ID(deviceid); type = DEV_TYPE(deviceid); if (type == TARGET) { /* Skip devices without CM_IDLEST register bit support */ addr = get_addr(domain, REG_IDLEST); valid = get_val_bits(domain, REG_IDLEST); if (!(addr) || !(valid & (1 << enbit))) return PRCM_PASS; /* Check if FCLK/ICLK is absent or ICLK is ON if present. */ if (clk_type == ICLK) { addr = get_addr(domain, REG_FCLKEN); valid = get_val_bits(domain, REG_FCLKEN); } else if (clk_type == FCLK) { addr = get_addr(domain, REG_ICLKEN); valid = get_val_bits(domain, REG_ICLKEN); } if (!(addr) || !(valid & (1 << enbit)) || (*addr & (1 << enbit))) { /* FCLK/ICLK present and is ON */ return check_device_status(deviceid, PRCM_ENABLE); } } else { /* type = INITIATOR or INITIATOR+TARGET * IDLEST bit cannot be polled, * it only specifies the * standby status * Check if the ICLK is present and ON */ if (clk_type == FCLK) { addr = get_addr(domain, REG_ICLKEN); valid = get_val_bits(domain, REG_ICLKEN); } else if (clk_type == ICLK) { addr = get_addr(domain, REG_FCLKEN); valid = get_val_bits(domain, REG_FCLKEN); } if (!(addr) || !(valid & (1 << enbit)) || (*addr & (1 << enbit))) { /* FCLK/ICLK present and is ON * Wait for sometime for the clocks to stabilize*/ DPRINTK("Adding delay\n"); omap_udelay(100); return PRCM_PASS; } } return PRCM_PASS;}/** * calc_dpll_lock_delay: Calculate DPLL lock time. * @dpll_id: dpll ID * @delay: Calculated delay * * Calculates the delay/wait time for a DPLL to relock. * Uses the following formula to calculate the worst possible delay. * delay = 2us + 350Fint_cycles (Fint_cycles = Fref/N+1) */static int calc_dpll_lock_delay(u32 dpll_id, u32 *delay){ u32 f_ref, f_int, m, n; f_ref = prcm_get_system_clock_speed(); if (f_ref == PRCM_FAIL) { printk(KERN_INFO "Unable to get system clock\n"); return PRCM_FAIL; } if (get_dpll_m_n(dpll_id, &m, &n) == PRCM_FAIL) { printk(KERN_INFO "Failed to get the M and N values\n"); return PRCM_FAIL; } f_int = f_ref / (n + 1); *delay = (2 + (350*1000)/f_int); return PRCM_PASS;}/*============================================================================*//*============================================================================*//*======================== CLOCK CONTROL ====================================*//*============================================================================*//*= This function will either enable or disable the fclk/iclk clock for the =*//*= specified module. This command will modify the CM_F/ICLKEN_<DOMAIN>/ =*//*= setting. The valid parameters for control are PRCM_ENABLE and =*//*= PRCM_DISABLE. This command will return PRCM_FAIL if the passed parameters=*//*= are not valid,otherwise it will return PRCM_PASS.If the parameter =*//*= checkaccessibility is PRCM_TRUE, the function waits till the device is =*//*= accessible before returning (provided both fclk/iclk clock are =*//*= enabled). =*//*============================================================================*/int prcm_clock_control(u32 deviceid, u8 clk_type, u8 control, u8 checkaccessibility){ u32 domain, omap, valid, enbit; volatile u32 *addr; DPRINTK("Clock:%d Control:%d\n", clk_type, control); omap = OMAP(deviceid); enbit = DEV_BIT_POS(deviceid); domain = DOMAIN_ID(deviceid); if (cpu_is_omap3430() && !(omap & (AT_3430|AT_3430_ES2))) return PRCM_FAIL; switch (clk_type) { case ICLK: addr = get_addr(domain, REG_ICLKEN); valid = get_val_bits(domain, REG_ICLKEN); break; case FCLK: addr = get_addr(domain, REG_FCLKEN); valid = get_val_bits(domain, REG_FCLKEN); break; default: return PRCM_FAIL; } DPRINTK("Address before: %x\n", *addr); /* No functional/Interface Clk control for the device */ if (!(addr) || !(valid & (1 << enbit))) return PRCM_FAIL; if (control == PRCM_ENABLE) *addr |= (1 << enbit); else if (control == PRCM_DISABLE) *addr &= ~(1 << enbit); DPRINTK("Address after: %x\n", *addr); if (checkaccessibility) { return check_accessibility(deviceid, clk_type); } return PRCM_PASS;}/*======================== DEVICE IDLE STATE =================================*//*============================================================================*//*= This function returns the specified module's idle status. It reads the =*//*= CM_IDLEST_<DOMAIN> register to determine whether the device is accessible=*//*= or not. The result returned is PRCM_TRUE if the device is accessible =*//*= else PRCM_FALSE. The function returns PRCM_FAIL if the parameters are =*//*= not valid. =*//*============================================================================*/int prcm_is_device_accessible(u32 deviceid, u8 *result){ u32 domain, omap, valid, enbit; volatile u32 *addr; omap = OMAP(deviceid); enbit = DEV_BIT_POS(deviceid); domain = DOMAIN_ID(deviceid); if (cpu_is_omap3430() && !(omap & (AT_3430|AT_3430_ES2))) return PRCM_FAIL; addr = get_addr(domain, REG_IDLEST); valid = get_val_bits(domain, REG_IDLEST); if (!(addr) || !(valid & (1 << enbit))) return PRCM_FAIL; if (!(*addr & (1 << enbit))) { *result = PRCM_TRUE; } else { *result = PRCM_FALSE; } return PRCM_PASS;}/** * prcm_interface_clock_autoidle: * @deviceid: Device ID * @control: One of the control arguments - PRCM_ENABLE or PRCM_DISABLE. * * Enable or Disable the interface clock autoidle. It modifies the * CM_AUTOIDLE_<DOMAIN> setting. * Returns PRCM_PASS on success, PRCM_FAIL otherwise. */int prcm_interface_clock_autoidle(u32 deviceid, u8 control){ u32 domain, omap, valid, enbit; volatile u32 *addr; omap = OMAP(deviceid); enbit = DEV_BIT_POS(deviceid); domain = DOMAIN_ID(deviceid); if (cpu_is_omap3430() && !(omap & (AT_3430|AT_3430_ES2))) return PRCM_FAIL; addr = get_addr(domain, REG_AUTOIDLE); valid = get_val_bits(domain, REG_AUTOIDLE); if (!(addr) || !(valid & (1 << enbit))) return PRCM_FAIL; if (control == PRCM_ENABLE) *addr |= (1 << enbit); else if (control == PRCM_DISABLE) *addr &= ~(1 << enbit); return PRCM_PASS;}/** * prcm_wakeup_event_control: Enable/ Disable the wakeup event for a device * @deviceid: Device ID * @control: One of the control arguments - PRCM_ENABLE or PRCM_DISABLE * * Enable or Disable the wakeup event for specified device. If enabled, * the device can generate a wake-up event. If disabled, the wake-up event * from the device is masked. It modifies the PM_WKEN_<DOMAIN> register. * Returns PRCM_PASS on success, PRCM_FAIL otherwise. */int prcm_wakeup_event_control(u32 deviceid, u8 control){ u32 domain, omap, valid, enbit; volatile u32 *addr; omap = OMAP(deviceid); enbit = DEV_BIT_POS(deviceid); domain = DOMAIN_ID(deviceid); if (cpu_is_omap3430() && !(omap & (AT_3430|AT_3430_ES2))) return PRCM_FAIL; addr = get_addr(domain, REG_WKEN); valid = get_val_bits(domain, REG_WKEN); if (!(addr) || !(valid & (1 << enbit))) return PRCM_FAIL; if (control == PRCM_ENABLE) *addr |= (1 << enbit); else if (control == PRCM_DISABLE) *addr &= ~(1 << enbit); return PRCM_PASS;}/*============================================================================*//*======================== ENABLE DPLL =======================================*//*============================================================================*//*= The function enables the specified DPLL and then wait for the DPLL to =*//*= lock before returning.The function modifies the CM_CLKEN_PLL_<DPLL> =*//*= setting. The function returns PRCM_FAIL if the dpllid passed is invalid =*//*============================================================================*/int prcm_enable_dpll(u32 dpll_id){ u32 dpll_idlest_lock_bit, dpll_enbit_mask, delay; volatile u32 *addr, *addr_auto; u32 dpll_autoidle; int ret, enabled; u32 loop_cnt = 0, retries_cnt = 0; if (dpll_id > NO_OF_DPLL) return PRCM_FAIL; /* Currently, this API does not allow locking of core DPLL */ /* Locking of core DPLL needs to be done without access to SDRAM */ /* This can be done safely if execution is done from SRAM */ if (dpll_id == DPLL3_CORE) return PRCM_FAIL; /* Store the DPLL autoidle */ addr_auto = get_addr_pll(dpll_id, REG_AUTOIDLE_PLL); dpll_autoidle = *addr_auto; *addr_auto = 0x0; ret = is_dpll_locked(dpll_id, &enabled); if (ret != PRCM_PASS) { *addr_auto = dpll_autoidle; return ret; } if (enabled == PRCM_TRUE) { DPRINTK("DPLL%d already enabled\n", dpll_id); *addr_auto = dpll_autoidle; return PRCM_PASS; } addr = get_addr_pll(dpll_id, REG_CLKEN_PLL); if (!addr) { *addr_auto = dpll_autoidle; return PRCM_FAIL; } dpll_enbit_mask = get_dpll_enbitmask(dpll_id); dpll_idlest_lock_bit = get_idlest_lock_bit(dpll_id); *addr |= ~dpll_enbit_mask; /* enable DPLL in lock mode */ if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0)) { /* WORKAROUND FOR SILICON ERRATA 1.56 */ ret = calc_dpll_lock_delay(dpll_id, &delay); if (ret != PRCM_PASS) { *addr_auto = dpll_autoidle; return ret; } DPRINTK("SILICON ERRATA 1.56, adding delay of %dus\n", delay); omap_udelay(delay); } ret = calc_dpll_lock_delay(dpll_id, &delay); if (ret != PRCM_PASS) { *addr_auto = dpll_autoidle; return ret; } DPRINTK("Waiting for %dus for dpll:%u to relock\n", delay, dpll_id); while (!(*get_addr_pll(dpll_id, REG_IDLEST_PLL) & dpll_idlest_lock_bit)) { /* wait for DPLL to lock */ ret = loop_wait(&loop_cnt, &retries_cnt, delay/5); if (ret != PRCM_PASS) { printk(KERN_INFO "Loop count exceeded in" "prcm_enable_dpll for dpll:%u\n", dpll_id); *addr_auto = dpll_autoidle; return ret; } } /* Restore the autoidle for the DPLL back */ *addr_auto = dpll_autoidle; return PRCM_PASS;}/*============================================================================*//*======================== CONFIGURE DPLL ====================================*//*============================================================================*//*= This function will set up the multiply and divide fields for the =*//*= specified DPLL with the passed mult and div values. It also sets the =*//*= frequency select field for the specified DPLL. The function returns =*//*= PRCM_FAIL if the parameters passed are not valid. =*//*= Note that this function only does the configuration. Locking is done =*//*= by prcm_enable_dpll() function =*//*============================================================================*/int prcm_configure_dpll(u32 dpll_id, u32 mult, u8 div, u8 freq_sel){ u32 valid; volatile u32 *addr, *addr_auto; u32 new_reg_val = 0x0; int ret, enabled, index; u32 sys_clkspeed, dpll_autoidle; if (dpll_id > NO_OF_DPLL) return PRCM_FAIL; if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0)) { /* WORKAROUND FOR Limitation 2.5 */ if (dpll_id == DPLL4_PER) return PRCM_FAIL; } /* Store the DPLL autoidle */ addr_auto = get_addr_pll(dpll_id, REG_AUTOIDLE_PLL); dpll_autoidle = *addr_auto; *addr_auto = 0x0; /* DPLL M,N,FreqSel values should be changed only if the DPLL * is in bypass mode. If it is not in bypass mode, return error */ ret = is_dpll_locked(dpll_id, &enabled); if (enabled == PRCM_TRUE) { printk(KERN_INFO "Dpll enabled - m,n values cannot be" "changed\n"); *addr_auto = dpll_autoidle; return PRCM_FAIL; } /* Configure M and N values */ addr = get_addr_pll(dpll_id, REG_CLKSEL1_PLL); if (!addr) return PRCM_FAIL; if (dpll_id == DPLL5_PER2) { /* get the M/N/freqsel values */ sys_clkspeed = prcm_get_system_clock_speed(); switch (sys_clkspeed) { case (int)(12000): index = 0; break; case (int)(13000): index = 1; break; case (int)(19200): index = 2; break; case (int)(26000): index = 3; break; case (int)(38400): index = 4; break; default: return PRCM_FAIL; } mult = usb_dpll_param[index].dpll_m; div = usb_dpll_param[index].dpll_n; freq_sel = usb_dpll_param[index].dpll_freqsel; } valid = get_val_bits_pll(dpll_id, REG_CLKSEL1_PLL); if (dpll_id == DPLL3_CORE) { new_reg_val = *addr & valid & DPLL3_N_MASK; new_reg_val |= (mult << 16) | (div << 8); *addr = new_reg_val; } else { new_reg_val = *addr & valid & DPLL_N_MASK; new_reg_val |= (mult << 8) | div; *addr = new_reg_val; } /* Configure FreqSel values */ addr = get_addr_pll(dpll_id, REG_CLKEN_PLL); if (!addr) return PRCM_FAIL; valid = get_val_bits_pll(dpll_id, REG_CLKEN_PLL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -