/* * _omap3_noncore_dpll_lock - instruct a DPLL to lock and wait for readiness * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to lock. Waits for the DPLL to report * readiness before returning. Will save and restore the DPLL's * autoidle state across the enable, per the CDP code. If the DPLL * locked successfully, return 0; if the DPLL did not lock in the time * allotted, or DPLL3 was passed in, return -EINVAL. */ static int _omap3_noncore_dpll_lock(struct clk *clk) { const struct dpll_data *dd; u8 ai; u8 state = 1; int r = 0; pr_debug("clock: locking DPLL %s\n", clk->name); dd = clk->dpll_data; state <<= __ffs(dd->idlest_mask); /* Check if already locked */ if ((__raw_readl(dd->idlest_reg) & dd->idlest_mask) == state) goto done; ai = omap3_dpll_autoidle_read(clk); omap3_dpll_deny_idle(clk); _omap3_dpll_write_clken(clk, DPLL_LOCKED); r = _omap3_wait_dpll_status(clk, 1); if (ai) omap3_dpll_allow_idle(clk); done: return r; }
/* * _omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enter low-power bypass mode. In * bypass mode, the DPLL's rate is set equal to its parent clock's * rate. Waits for the DPLL to report readiness before returning. * Will save and restore the DPLL's autoidle state across the enable, * per the CDP code. If the DPLL entered bypass mode successfully, * return 0; if the DPLL did not enter bypass in the time allotted, or * DPLL3 was passed in, or the DPLL does not support low-power bypass, * return -EINVAL. */ static int _omap3_noncore_dpll_bypass(struct clk *clk) { int r; u8 ai; if (clk == &dpll3_ck) return -EINVAL; if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) return -EINVAL; pr_debug("clock: configuring DPLL %s for low-power bypass\n", clk->name); ai = omap3_dpll_autoidle_read(clk); _omap3_dpll_write_clken(clk, DPLL_LOW_POWER_BYPASS); r = _omap3_wait_dpll_status(clk, 0); if (ai) omap3_dpll_allow_idle(clk); else omap3_dpll_deny_idle(clk); return r; }
/* * _omap3_noncore_dpll_stop - instruct a DPLL to stop * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enter low-power stop. Will save and * restore the DPLL's autoidle state across the stop, per the CDP * code. If DPLL3 was passed in, or the DPLL does not support * low-power stop, return -EINVAL; otherwise, return 0. */ static int _omap3_noncore_dpll_stop(struct clk_hw_omap *clk) { u8 ai; if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_STOP))) return -EINVAL; pr_debug("clock: stopping DPLL %s\n", __clk_get_name(clk->hw.clk)); ai = omap3_dpll_autoidle_read(clk); _omap3_dpll_write_clken(clk, DPLL_LOW_POWER_STOP); if (ai) omap3_dpll_allow_idle(clk); return 0; }
/* * _omap3_noncore_dpll_lock - instruct a DPLL to lock and wait for readiness * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to lock. Waits for the DPLL to report * readiness before returning. Will save and restore the DPLL's * autoidle state across the enable, per the CDP code. If the DPLL * locked successfully, return 0; if the DPLL did not lock in the time * allotted, or DPLL3 was passed in, return -EINVAL. */ static int _omap3_noncore_dpll_lock(struct clk *clk) { u8 ai; int r; pr_debug("clock: locking DPLL %s\n", clk->name); ai = omap3_dpll_autoidle_read(clk); omap3_dpll_deny_idle(clk); _omap3_dpll_write_clken(clk, DPLL_LOCKED); r = _omap3_wait_dpll_status(clk, 1); if (ai) omap3_dpll_allow_idle(clk); return r; }
/* * _omap3_noncore_dpll_stop - instruct a DPLL to stop * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enter low-power stop. Will save and * restore the DPLL's autoidle state across the stop, per the CDP * code. If DPLL3 was passed in, or the DPLL does not support * low-power stop, return -EINVAL; otherwise, return 0. */ static int _omap3_noncore_dpll_stop(struct clk *clk) { u8 ai; if (clk == &dpll3_ck) return -EINVAL; if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_STOP))) return -EINVAL; pr_debug("clock: stopping DPLL %s\n", clk->name); ai = omap3_dpll_autoidle_read(clk); _omap3_dpll_write_clken(clk, DPLL_LOW_POWER_STOP); if (ai) omap3_dpll_allow_idle(clk); else omap3_dpll_deny_idle(clk); return 0; }
/* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */ static int _omap3_wait_dpll_status(struct clk *clk, u8 state) { const struct dpll_data *dd; int i; int ret = -EINVAL; bool first_time = true; u32 reg; /* LGE_SJIT 2012-02-08 [[email protected]] fix the warning */ u32 orig_cm_div_m2_dpll_usb = 0; u32 orig_cm_clkdcoldo_dpll_usb = 0; retry: dd = clk->dpll_data; state <<= __ffs(dd->idlest_mask); i = 0; while (((__raw_readl(dd->idlest_reg) & dd->idlest_mask) != state) && i < MAX_DPLL_WAIT_TRIES) { i++; udelay(1); } /* restore back old values if hit work-around */ if (!first_time) { __raw_writel(orig_cm_div_m2_dpll_usb, OMAP4430_CM_DIV_M2_DPLL_USB); __raw_writel(orig_cm_clkdcoldo_dpll_usb, OMAP4430_CM_CLKDCOLDO_DPLL_USB); } if (i == MAX_DPLL_WAIT_TRIES) { printk(KERN_ERR "clock: %s failed transition to '%s'\n", clk->name, (state) ? "locked" : "bypassed"); /* Try Error Recovery: for failing usbdpll locking */ if (!strcmp(clk->name, "dpll_usb_ck")) { reg = __raw_readl(dd->mult_div1_reg); /* Put in MN bypass */ _omap3_dpll_write_clken(clk, DPLL_MN_BYPASS); i = 0; while (!(__raw_readl(dd->idlest_reg) & (1 << OMAP4430_ST_MN_BYPASS_SHIFT)) && i < MAX_DPLL_WAIT_TRIES) { i++; udelay(1); } /* MN bypass looses contents of CM_CLKSEL_DPLL_USB */ __raw_writel(reg, dd->mult_div1_reg); /* Force generate request to PRCM: put in Force mode */ /* a) CM_DIV_M2_DPLL_USB.DPLL_CLKOUT_GATE_CTRL = 1 */ orig_cm_div_m2_dpll_usb = __raw_readl(OMAP4430_CM_DIV_M2_DPLL_USB); __raw_writel(orig_cm_div_m2_dpll_usb | (1 << OMAP4430_DPLL_CLKOUT_GATE_CTRL_SHIFT), OMAP4430_CM_DIV_M2_DPLL_USB); /* b) CM_CLKDCOLDO_DPLL_USB.DPLL_CLKDCOLDO_GATE_CTRL = 1 */ orig_cm_clkdcoldo_dpll_usb = __raw_readl(OMAP4430_CM_CLKDCOLDO_DPLL_USB); __raw_writel(orig_cm_clkdcoldo_dpll_usb | (1 << OMAP4430_DPLL_CLKDCOLDO_GATE_CTRL_SHIFT), OMAP4430_CM_CLKDCOLDO_DPLL_USB); /* Put back to locked mode */ _omap3_dpll_write_clken(clk, DPLL_LOCKED); if (first_time) { first_time = false; goto retry; } } } else { pr_debug("clock: %s transition to '%s' in %d loops\n", clk->name, (state) ? "locked" : "bypassed", i); ret = 0; } return ret; }
/* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */ static int _omap3_wait_dpll_status(struct clk *clk, u8 state) { const struct dpll_data *dd; int i; int ret = -EINVAL; bool first_time = true; u32 reg; u32 orig_cm_div_m2_dpll_usb; u32 orig_cm_clkdcoldo_dpll_usb; retry: dd = clk->dpll_data; state <<= __ffs(dd->idlest_mask); i = 0; while (((__raw_readl(dd->idlest_reg) & dd->idlest_mask) != state) && i < MAX_DPLL_WAIT_TRIES) { i++; udelay(1); } /* restore back old values if hit work-around */ if (!first_time) { __raw_writel(orig_cm_div_m2_dpll_usb, OMAP4430_CM_DIV_M2_DPLL_USB); __raw_writel(orig_cm_clkdcoldo_dpll_usb, OMAP4430_CM_CLKDCOLDO_DPLL_USB); } if (i == MAX_DPLL_WAIT_TRIES) { printk(KERN_ERR "clock: %s failed transition to '%s'\n", clk->name, (state) ? "locked" : "bypassed"); /* Try Error Recovery: for failing usbdpll locking */ if (!strcmp(clk->name, "dpll_usb_ck")) { reg = __raw_readl(dd->mult_div1_reg); /* Put in MN bypass */ _omap3_dpll_write_clken(clk, DPLL_MN_BYPASS); i = 0; while (!(__raw_readl(dd->idlest_reg) & (1 << OMAP4430_ST_MN_BYPASS_SHIFT)) && i < MAX_DPLL_WAIT_TRIES) { i++; udelay(1); } /* MN bypass looses contents of CM_CLKSEL_DPLL_USB */ __raw_writel(reg, dd->mult_div1_reg); /* Force generate request to PRCM: put in Force mode */ /* a) CM_DIV_M2_DPLL_USB.DPLL_CLKOUT_GATE_CTRL = 1 */ orig_cm_div_m2_dpll_usb = __raw_readl(OMAP4430_CM_DIV_M2_DPLL_USB); __raw_writel(orig_cm_div_m2_dpll_usb | (1 << OMAP4430_DPLL_CLKOUT_GATE_CTRL_SHIFT), OMAP4430_CM_DIV_M2_DPLL_USB); /* b) CM_CLKDCOLDO_DPLL_USB.DPLL_CLKDCOLDO_GATE_CTRL = 1 */ orig_cm_clkdcoldo_dpll_usb = __raw_readl(OMAP4430_CM_CLKDCOLDO_DPLL_USB); __raw_writel(orig_cm_clkdcoldo_dpll_usb | (1 << OMAP4430_DPLL_CLKDCOLDO_GATE_CTRL_SHIFT), OMAP4430_CM_CLKDCOLDO_DPLL_USB); /* Put back to locked mode */ _omap3_dpll_write_clken(clk, DPLL_LOCKED); if (first_time) { first_time = false; goto retry; } pr_info("\n========== USB DPLL DUMP ===========\n"); pr_info("CM_CLKMODE_DPLL_USB :%08x\n", omap_readl(0x4A008180)); pr_info("CM_IDLEST_DPLL_USB :%08x\n", omap_readl(0x4A008184)); pr_info("CM_AUTOIDLE_DPLL_USB :%08x\n", omap_readl(0x4A008188)); pr_info("CM_CLKSEL_DPLL_USB :%08x\n", omap_readl(0x4A00818C)); pr_info("CM_DIV_M2_DPLL_USB :%08x\n", omap_readl(0x4A008190)); pr_info("CM_SSC_DELTAMSTEP_DPLL_USB :%08x\n", omap_readl(0x4A0081A8)); pr_info("CM_SSC_MODFREQDIV_DPLL_USB :%08x\n", omap_readl(0x4A0081AC)); pr_info("CM_CLKDCOLDO_DPLL_USB :%08x\n", omap_readl(0x4A0081B4)); pr_info("========== USB DPLL DUMP: End ===========\n"); } } else { pr_debug("clock: %s transition to '%s' in %d loops\n", clk->name, (state) ? "locked" : "bypassed", i); ret = 0; } return ret; }