/** * zynqmp_pll_recalc_rate - Recalculate clock frequency * @hw: Handle between common and hardware-specific interfaces * @parent_rate: Clock frequency of parent clock * Return: Current clock frequency */ static unsigned long zynqmp_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct zynqmp_pll *clk = to_zynqmp_pll(hw); u32 fbdiv, div2, data; unsigned long rate, frac; /* * makes probably sense to redundantly save fbdiv in the struct * zynqmp_pll to save the IO access. */ fbdiv = (zynqmp_pm_mmio_readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >> PLLCTRL_FBDIV_SHIFT; div2 = (zynqmp_pm_mmio_readl(clk->pll_ctrl) & PLLCTRL_DIV2_MASK) >> PLLCTRL_DIV2_SHIFT; if (div2) fbdiv = fbdiv * 2; rate = parent_rate * fbdiv; if (pll_frac_get_mode(hw) == PLL_MODE_FRAC) { data = (zynqmp_pm_mmio_readl(clk->pll_ctrl + FRAC_OFFSET) & 0xffff); frac = (rate * data) / FRAC_DIV; rate = rate + frac; } return rate; }
/** * zynqmp_pll_enable - Enable clock * @hw: Handle between common and hardware-specific interfaces * * Return: 0 always */ static int zynqmp_pll_enable(struct clk_hw *hw) { u32 reg; struct zynqmp_pll *clk = to_zynqmp_pll(hw); if (zynqmp_pll_is_enabled(hw)) return 0; pr_info("PLL: enable\n"); reg = zynqmp_pm_mmio_readl(clk->pll_ctrl); reg |= PLLCTRL_BP_MASK; zynqmp_pm_mmio_writel(reg, clk->pll_ctrl); reg |= PLLCTRL_RESET_MASK; zynqmp_pm_mmio_writel(reg, clk->pll_ctrl); reg &= ~(PLLCTRL_RESET_MASK); zynqmp_pm_mmio_writel(reg, clk->pll_ctrl); while (!(zynqmp_pm_mmio_readl(clk->pll_status) & (1 << clk->lockbit))) cpu_relax(); reg &= ~PLLCTRL_BP_MASK; zynqmp_pm_mmio_writel(reg, clk->pll_ctrl); return 0; }
static inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw) { struct zynqmp_pll *clk = to_zynqmp_pll(hw); u32 reg; reg = zynqmp_pm_mmio_readl(clk->pll_ctrl + FRAC_OFFSET); reg = reg & PLLFCFG_FRAC_EN; return reg ? PLL_MODE_FRAC : PLL_MODE_INT; }
/** * zynqmp_pll_is_enabled - Check if a clock is enabled * @hw: Handle between common and hardware-specific interfaces * * Return: 1 if the clock is enabled, 0 otherwise */ static int zynqmp_pll_is_enabled(struct clk_hw *hw) { u32 reg; struct zynqmp_pll *clk = to_zynqmp_pll(hw); reg = zynqmp_pm_mmio_readl(clk->pll_ctrl); return !(reg & (PLLCTRL_RESET_MASK)); }
/** * zynqmp_pll_disable - Disable clock * @hw: Handle between common and hardware-specific interfaces * */ static void zynqmp_pll_disable(struct clk_hw *hw) { struct zynqmp_pll *clk = to_zynqmp_pll(hw); if (!zynqmp_pll_is_enabled(hw)) return; pr_info("PLL: shutdown\n"); /* shut down PLL */ zynqmp_pm_mmio_write((u32)(ulong)clk->pll_ctrl, PLLCTRL_RESET_MASK, PLLCTRL_RESET_VAL); }
/** * zynqmp_pll_is_enabled - Check if a clock is enabled * @hw: Handle between common and hardware-specific interfaces * * Return: 1 if the clock is enabled, 0 otherwise */ static int zynqmp_pll_is_enabled(struct clk_hw *hw) { u32 reg; struct zynqmp_pll *clk = to_zynqmp_pll(hw); int ret; ret = zynqmp_pm_mmio_read((u32)(ulong)clk->pll_ctrl, ®); if (ret) pr_warn_once("Read fail pll address: %x\n", (u32)(ulong)clk->pll_ctrl); return !(reg & (PLLCTRL_RESET_MASK)); }
static int zynqmp_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct zynqmp_pll *clk = to_zynqmp_pll(hw); u32 fbdiv, reg; u32 data; long rate_div, frac, m, f; if (pll_frac_get_mode(hw) == PLL_MODE_FRAC) { unsigned int children; /* * We're running on a ZynqMP compatible machine, make sure the * VPLL only has one child. */ children = clk_get_children("vpll"); /* Account for vpll_to_lpd and dp_video_ref */ if (children > 2) WARN(1, "Two devices are using vpll which is forbidden\n"); rate_div = ((rate * FRAC_DIV) / parent_rate); m = rate_div / FRAC_DIV; f = rate_div % FRAC_DIV; m = clamp_t(u32, m, (PLL_FBDIV_MIN), (PLL_FBDIV_MAX)); rate = parent_rate * m; frac = (parent_rate * f) / FRAC_DIV; reg = zynqmp_pm_mmio_readl(clk->pll_ctrl); reg &= ~PLLCTRL_FBDIV_MASK; reg |= m << PLLCTRL_FBDIV_SHIFT; zynqmp_pm_mmio_writel(reg, clk->pll_ctrl); reg = zynqmp_pm_mmio_readl(clk->pll_ctrl + FRAC_OFFSET); reg &= ~0xffff; data = (FRAC_DIV * f) / FRAC_DIV; data = data & 0xffff; reg |= data; zynqmp_pm_mmio_writel(reg, clk->pll_ctrl + FRAC_OFFSET); return (rate + frac); } fbdiv = DIV_ROUND_CLOSEST(rate, parent_rate); fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX); reg = zynqmp_pm_mmio_readl(clk->pll_ctrl); reg &= ~PLLCTRL_FBDIV_MASK; reg |= fbdiv << PLLCTRL_FBDIV_SHIFT; zynqmp_pm_mmio_writel(reg, clk->pll_ctrl); return parent_rate * fbdiv; }
/** * pll_frac_set_mode - Set the fractional mode * @hw: Handle between common and hardware-specific interfaces * @on: Flag to determine the mode */ static inline void pll_frac_set_mode(struct clk_hw *hw, bool on) { struct zynqmp_pll *clk = to_zynqmp_pll(hw); u32 reg = 0; int ret; if (on) reg = PLLFCFG_FRAC_EN; ret = zynqmp_pm_mmio_write((u32)(ulong)(clk->pll_ctrl + FRAC_OFFSET), PLLFCFG_FRAC_EN, reg); if (ret) pr_warn_once("Write fail pll address: %x\n", (u32)(ulong)(clk->pll_ctrl + FRAC_OFFSET)); }
static inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw) { struct zynqmp_pll *clk = to_zynqmp_pll(hw); u32 reg; int ret; ret = zynqmp_pm_mmio_read((u32)(ulong)(clk->pll_ctrl + FRAC_OFFSET), ®); if (ret) pr_warn_once("Read fail pll address: %x\n", (u32)(ulong)(clk->pll_ctrl + FRAC_OFFSET)); reg = reg & PLLFCFG_FRAC_EN; return reg ? PLL_MODE_FRAC : PLL_MODE_INT; }