| // SPDX-License-Identifier: GPL-2.0+ | 
 | /* | 
 |  *  Copyright (C) 2019 Microchip Technology Inc. | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux/bitfield.h> | 
 | #include <linux/clk.h> | 
 | #include <linux/clk-provider.h> | 
 | #include <linux/clkdev.h> | 
 | #include <linux/clk/at91_pmc.h> | 
 | #include <linux/of.h> | 
 | #include <linux/mfd/syscon.h> | 
 | #include <linux/regmap.h> | 
 |  | 
 | #include "pmc.h" | 
 |  | 
 | #define	PMC_PLL_CTRL0_DIV_MSK	GENMASK(7, 0) | 
 | #define	PMC_PLL_CTRL1_MUL_MSK	GENMASK(31, 24) | 
 | #define	PMC_PLL_CTRL1_FRACR_MSK	GENMASK(21, 0) | 
 |  | 
 | #define PLL_DIV_MAX		(FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1) | 
 | #define UPLL_DIV		2 | 
 | #define PLL_MUL_MAX		(FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1) | 
 |  | 
 | #define PLL_MAX_ID		9 | 
 |  | 
 | struct sam9x60_pll_core { | 
 | 	struct regmap *regmap; | 
 | 	spinlock_t *lock; | 
 | 	const struct clk_pll_characteristics *characteristics; | 
 | 	const struct clk_pll_layout *layout; | 
 | 	struct clk_hw hw; | 
 | 	u8 id; | 
 | }; | 
 |  | 
 | struct sam9x60_frac { | 
 | 	struct sam9x60_pll_core core; | 
 | 	struct at91_clk_pms pms; | 
 | 	u32 frac; | 
 | 	u16 mul; | 
 | }; | 
 |  | 
 | struct sam9x60_div { | 
 | 	struct sam9x60_pll_core core; | 
 | 	struct at91_clk_pms pms; | 
 | 	u8 div; | 
 | 	u8 safe_div; | 
 | }; | 
 |  | 
 | #define to_sam9x60_pll_core(hw)	container_of(hw, struct sam9x60_pll_core, hw) | 
 | #define to_sam9x60_frac(core)	container_of(core, struct sam9x60_frac, core) | 
 | #define to_sam9x60_div(core)	container_of(core, struct sam9x60_div, core) | 
 |  | 
 | static struct sam9x60_div *notifier_div; | 
 |  | 
 | static inline bool sam9x60_pll_ready(struct regmap *regmap, int id) | 
 | { | 
 | 	unsigned int status; | 
 |  | 
 | 	regmap_read(regmap, AT91_PMC_PLL_ISR0, &status); | 
 |  | 
 | 	return !!(status & BIT(id)); | 
 | } | 
 |  | 
 | static bool sam9x60_frac_pll_ready(struct regmap *regmap, u8 id) | 
 | { | 
 | 	return sam9x60_pll_ready(regmap, id); | 
 | } | 
 |  | 
 | static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw, | 
 | 						  unsigned long parent_rate) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct sam9x60_frac *frac = to_sam9x60_frac(core); | 
 | 	unsigned long freq; | 
 |  | 
 | 	freq = parent_rate * (frac->mul + 1) + | 
 | 		DIV_ROUND_CLOSEST_ULL((u64)parent_rate * frac->frac, (1 << 22)); | 
 |  | 
 | 	if (core->layout->div2) | 
 | 		freq >>= 1; | 
 |  | 
 | 	return freq; | 
 | } | 
 |  | 
 | static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core) | 
 | { | 
 | 	struct sam9x60_frac *frac = to_sam9x60_frac(core); | 
 | 	struct regmap *regmap = core->regmap; | 
 | 	unsigned int val, cfrac, cmul; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(core->lock, flags); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_ID_MSK, core->id); | 
 | 	regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val); | 
 | 	cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift; | 
 | 	cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift; | 
 |  | 
 | 	if (sam9x60_frac_pll_ready(regmap, core->id) && | 
 | 	    (cmul == frac->mul && cfrac == frac->frac)) | 
 | 		goto unlock; | 
 |  | 
 | 	/* Recommended value for PMC_PLL_ACR */ | 
 | 	if (core->characteristics->upll) | 
 | 		val = AT91_PMC_PLL_ACR_DEFAULT_UPLL; | 
 | 	else | 
 | 		val = AT91_PMC_PLL_ACR_DEFAULT_PLLA; | 
 | 	regmap_write(regmap, AT91_PMC_PLL_ACR, val); | 
 |  | 
 | 	regmap_write(regmap, AT91_PMC_PLL_CTRL1, | 
 | 		     (frac->mul << core->layout->mul_shift) | | 
 | 		     (frac->frac << core->layout->frac_shift)); | 
 |  | 
 | 	if (core->characteristics->upll) { | 
 | 		/* Enable the UTMI internal bandgap */ | 
 | 		val |= AT91_PMC_PLL_ACR_UTMIBG; | 
 | 		regmap_write(regmap, AT91_PMC_PLL_ACR, val); | 
 |  | 
 | 		udelay(10); | 
 |  | 
 | 		/* Enable the UTMI internal regulator */ | 
 | 		val |= AT91_PMC_PLL_ACR_UTMIVR; | 
 | 		regmap_write(regmap, AT91_PMC_PLL_ACR, val); | 
 |  | 
 | 		udelay(10); | 
 | 	} | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | core->id); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, | 
 | 			   AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL, | 
 | 			   AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | core->id); | 
 |  | 
 | 	while (!sam9x60_pll_ready(regmap, core->id)) | 
 | 		cpu_relax(); | 
 |  | 
 | unlock: | 
 | 	spin_unlock_irqrestore(core->lock, flags); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int sam9x60_frac_pll_prepare(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 |  | 
 | 	return sam9x60_frac_pll_set(core); | 
 | } | 
 |  | 
 | static void sam9x60_frac_pll_unprepare(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct regmap *regmap = core->regmap; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(core->lock, flags); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_ID_MSK, core->id); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, AT91_PMC_PLL_CTRL0_ENPLL, 0); | 
 |  | 
 | 	if (core->characteristics->upll) | 
 | 		regmap_update_bits(regmap, AT91_PMC_PLL_ACR, | 
 | 				   AT91_PMC_PLL_ACR_UTMIBG | AT91_PMC_PLL_ACR_UTMIVR, 0); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | core->id); | 
 |  | 
 | 	spin_unlock_irqrestore(core->lock, flags); | 
 | } | 
 |  | 
 | static int sam9x60_frac_pll_is_prepared(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 |  | 
 | 	return sam9x60_pll_ready(core->regmap, core->id); | 
 | } | 
 |  | 
 | static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core, | 
 | 					      unsigned long rate, | 
 | 					      unsigned long parent_rate, | 
 | 					      bool update) | 
 | { | 
 | 	struct sam9x60_frac *frac = to_sam9x60_frac(core); | 
 | 	unsigned long tmprate, remainder; | 
 | 	unsigned long nmul = 0; | 
 | 	unsigned long nfrac = 0; | 
 |  | 
 | 	if (rate < core->characteristics->core_output[0].min || | 
 | 	    rate > core->characteristics->core_output[0].max) | 
 | 		return -ERANGE; | 
 |  | 
 | 	/* | 
 | 	 * Calculate the multiplier associated with the current | 
 | 	 * divider that provide the closest rate to the requested one. | 
 | 	 */ | 
 | 	nmul = mult_frac(rate, 1, parent_rate); | 
 | 	tmprate = mult_frac(parent_rate, nmul, 1); | 
 | 	remainder = rate - tmprate; | 
 |  | 
 | 	if (remainder) { | 
 | 		nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22), | 
 | 					      parent_rate); | 
 |  | 
 | 		tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate, | 
 | 						 (1 << 22)); | 
 | 	} | 
 |  | 
 | 	/* Check if resulted rate is a valid.  */ | 
 | 	if (tmprate < core->characteristics->core_output[0].min || | 
 | 	    tmprate > core->characteristics->core_output[0].max) | 
 | 		return -ERANGE; | 
 |  | 
 | 	if (update) { | 
 | 		frac->mul = nmul - 1; | 
 | 		frac->frac = nfrac; | 
 | 	} | 
 |  | 
 | 	return tmprate; | 
 | } | 
 |  | 
 | static long sam9x60_frac_pll_round_rate(struct clk_hw *hw, unsigned long rate, | 
 | 					unsigned long *parent_rate) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 |  | 
 | 	return sam9x60_frac_pll_compute_mul_frac(core, rate, *parent_rate, false); | 
 | } | 
 |  | 
 | static int sam9x60_frac_pll_set_rate(struct clk_hw *hw, unsigned long rate, | 
 | 				     unsigned long parent_rate) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 |  | 
 | 	return sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true); | 
 | } | 
 |  | 
 | static int sam9x60_frac_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate, | 
 | 					 unsigned long parent_rate) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct sam9x60_frac *frac = to_sam9x60_frac(core); | 
 | 	struct regmap *regmap = core->regmap; | 
 | 	unsigned long irqflags; | 
 | 	unsigned int val, cfrac, cmul; | 
 | 	long ret; | 
 |  | 
 | 	ret = sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true); | 
 | 	if (ret <= 0) | 
 | 		return ret; | 
 |  | 
 | 	spin_lock_irqsave(core->lock, irqflags); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   core->id); | 
 | 	regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val); | 
 | 	cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift; | 
 | 	cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift; | 
 |  | 
 | 	if (cmul == frac->mul && cfrac == frac->frac) | 
 | 		goto unlock; | 
 |  | 
 | 	regmap_write(regmap, AT91_PMC_PLL_CTRL1, | 
 | 		     (frac->mul << core->layout->mul_shift) | | 
 | 		     (frac->frac << core->layout->frac_shift)); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | core->id); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, | 
 | 			   AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL, | 
 | 			   AT91_PMC_PLL_CTRL0_ENLOCK | | 
 | 			   AT91_PMC_PLL_CTRL0_ENPLL); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | core->id); | 
 |  | 
 | 	while (!sam9x60_pll_ready(regmap, core->id)) | 
 | 		cpu_relax(); | 
 |  | 
 | unlock: | 
 | 	spin_unlock_irqrestore(core->lock, irqflags); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int sam9x60_frac_pll_save_context(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct sam9x60_frac *frac = to_sam9x60_frac(core); | 
 |  | 
 | 	frac->pms.status = sam9x60_pll_ready(core->regmap, core->id); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void sam9x60_frac_pll_restore_context(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct sam9x60_frac *frac = to_sam9x60_frac(core); | 
 |  | 
 | 	if (frac->pms.status) | 
 | 		sam9x60_frac_pll_set(core); | 
 | } | 
 |  | 
 | static const struct clk_ops sam9x60_frac_pll_ops = { | 
 | 	.prepare = sam9x60_frac_pll_prepare, | 
 | 	.unprepare = sam9x60_frac_pll_unprepare, | 
 | 	.is_prepared = sam9x60_frac_pll_is_prepared, | 
 | 	.recalc_rate = sam9x60_frac_pll_recalc_rate, | 
 | 	.round_rate = sam9x60_frac_pll_round_rate, | 
 | 	.set_rate = sam9x60_frac_pll_set_rate, | 
 | 	.save_context = sam9x60_frac_pll_save_context, | 
 | 	.restore_context = sam9x60_frac_pll_restore_context, | 
 | }; | 
 |  | 
 | static const struct clk_ops sam9x60_frac_pll_ops_chg = { | 
 | 	.prepare = sam9x60_frac_pll_prepare, | 
 | 	.unprepare = sam9x60_frac_pll_unprepare, | 
 | 	.is_prepared = sam9x60_frac_pll_is_prepared, | 
 | 	.recalc_rate = sam9x60_frac_pll_recalc_rate, | 
 | 	.round_rate = sam9x60_frac_pll_round_rate, | 
 | 	.set_rate = sam9x60_frac_pll_set_rate_chg, | 
 | 	.save_context = sam9x60_frac_pll_save_context, | 
 | 	.restore_context = sam9x60_frac_pll_restore_context, | 
 | }; | 
 |  | 
 | /* This function should be called with spinlock acquired. */ | 
 | static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div, | 
 | 				    bool enable) | 
 | { | 
 | 	struct regmap *regmap = core->regmap; | 
 | 	u32 ena_msk = enable ? core->layout->endiv_mask : 0; | 
 | 	u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0; | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, | 
 | 			   core->layout->div_mask | ena_msk, | 
 | 			   (div << core->layout->div_shift) | ena_val); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | core->id); | 
 |  | 
 | 	while (!sam9x60_pll_ready(regmap, core->id)) | 
 | 		cpu_relax(); | 
 | } | 
 |  | 
 | static int sam9x60_div_pll_set(struct sam9x60_pll_core *core) | 
 | { | 
 | 	struct sam9x60_div *div = to_sam9x60_div(core); | 
 | 	struct regmap *regmap = core->regmap; | 
 | 	unsigned long flags; | 
 | 	unsigned int val, cdiv; | 
 |  | 
 | 	spin_lock_irqsave(core->lock, flags); | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_ID_MSK, core->id); | 
 | 	regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); | 
 | 	cdiv = (val & core->layout->div_mask) >> core->layout->div_shift; | 
 |  | 
 | 	/* Stop if enabled an nothing changed. */ | 
 | 	if (!!(val & core->layout->endiv_mask) && cdiv == div->div) | 
 | 		goto unlock; | 
 |  | 
 | 	sam9x60_div_pll_set_div(core, div->div, 1); | 
 |  | 
 | unlock: | 
 | 	spin_unlock_irqrestore(core->lock, flags); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int sam9x60_div_pll_prepare(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 |  | 
 | 	return sam9x60_div_pll_set(core); | 
 | } | 
 |  | 
 | static void sam9x60_div_pll_unprepare(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct regmap *regmap = core->regmap; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(core->lock, flags); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_ID_MSK, core->id); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, | 
 | 			   core->layout->endiv_mask, 0); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   AT91_PMC_PLL_UPDT_UPDATE | core->id); | 
 |  | 
 | 	spin_unlock_irqrestore(core->lock, flags); | 
 | } | 
 |  | 
 | static int sam9x60_div_pll_is_prepared(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct regmap *regmap = core->regmap; | 
 | 	unsigned long flags; | 
 | 	unsigned int val; | 
 |  | 
 | 	spin_lock_irqsave(core->lock, flags); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_ID_MSK, core->id); | 
 | 	regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); | 
 |  | 
 | 	spin_unlock_irqrestore(core->lock, flags); | 
 |  | 
 | 	return !!(val & core->layout->endiv_mask); | 
 | } | 
 |  | 
 | static unsigned long sam9x60_div_pll_recalc_rate(struct clk_hw *hw, | 
 | 						 unsigned long parent_rate) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct sam9x60_div *div = to_sam9x60_div(core); | 
 |  | 
 | 	return DIV_ROUND_CLOSEST_ULL(parent_rate, (div->div + 1)); | 
 | } | 
 |  | 
 | static unsigned long sam9x60_fixed_div_pll_recalc_rate(struct clk_hw *hw, | 
 | 						       unsigned long parent_rate) | 
 | { | 
 | 	return parent_rate >> 1; | 
 | } | 
 |  | 
 | static long sam9x60_div_pll_compute_div(struct sam9x60_pll_core *core, | 
 | 					unsigned long *parent_rate, | 
 | 					unsigned long rate) | 
 | { | 
 | 	const struct clk_pll_characteristics *characteristics = | 
 | 							core->characteristics; | 
 | 	struct clk_hw *parent = clk_hw_get_parent(&core->hw); | 
 | 	unsigned long tmp_rate, tmp_parent_rate, tmp_diff; | 
 | 	long best_diff = -1, best_rate = -EINVAL; | 
 | 	u32 divid; | 
 |  | 
 | 	if (!rate) | 
 | 		return 0; | 
 |  | 
 | 	if (rate < characteristics->output[0].min || | 
 | 	    rate > characteristics->output[0].max) | 
 | 		return -ERANGE; | 
 |  | 
 | 	for (divid = 1; divid < core->layout->div_mask; divid++) { | 
 | 		tmp_parent_rate = clk_hw_round_rate(parent, rate * divid); | 
 | 		if (!tmp_parent_rate) | 
 | 			continue; | 
 |  | 
 | 		tmp_rate = DIV_ROUND_CLOSEST_ULL(tmp_parent_rate, divid); | 
 | 		tmp_diff = abs(rate - tmp_rate); | 
 |  | 
 | 		if (best_diff < 0 || best_diff > tmp_diff) { | 
 | 			*parent_rate = tmp_parent_rate; | 
 | 			best_rate = tmp_rate; | 
 | 			best_diff = tmp_diff; | 
 | 		} | 
 |  | 
 | 		if (!best_diff) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	if (best_rate < characteristics->output[0].min || | 
 | 	    best_rate > characteristics->output[0].max) | 
 | 		return -ERANGE; | 
 |  | 
 | 	return best_rate; | 
 | } | 
 |  | 
 | static long sam9x60_div_pll_round_rate(struct clk_hw *hw, unsigned long rate, | 
 | 				       unsigned long *parent_rate) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 |  | 
 | 	return sam9x60_div_pll_compute_div(core, parent_rate, rate); | 
 | } | 
 |  | 
 | static int sam9x60_div_pll_set_rate(struct clk_hw *hw, unsigned long rate, | 
 | 				    unsigned long parent_rate) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct sam9x60_div *div = to_sam9x60_div(core); | 
 |  | 
 | 	div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate, | 
 | 					unsigned long parent_rate) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct sam9x60_div *div = to_sam9x60_div(core); | 
 | 	struct regmap *regmap = core->regmap; | 
 | 	unsigned long irqflags; | 
 | 	unsigned int val, cdiv; | 
 |  | 
 | 	div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1; | 
 |  | 
 | 	spin_lock_irqsave(core->lock, irqflags); | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   core->id); | 
 | 	regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); | 
 | 	cdiv = (val & core->layout->div_mask) >> core->layout->div_shift; | 
 |  | 
 | 	/* Stop if nothing changed. */ | 
 | 	if (cdiv == div->div) | 
 | 		goto unlock; | 
 |  | 
 | 	sam9x60_div_pll_set_div(core, div->div, 0); | 
 |  | 
 | unlock: | 
 | 	spin_unlock_irqrestore(core->lock, irqflags); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int sam9x60_div_pll_save_context(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct sam9x60_div *div = to_sam9x60_div(core); | 
 |  | 
 | 	div->pms.status = sam9x60_div_pll_is_prepared(hw); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void sam9x60_div_pll_restore_context(struct clk_hw *hw) | 
 | { | 
 | 	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); | 
 | 	struct sam9x60_div *div = to_sam9x60_div(core); | 
 |  | 
 | 	if (div->pms.status) | 
 | 		sam9x60_div_pll_set(core); | 
 | } | 
 |  | 
 | static int sam9x60_div_pll_notifier_fn(struct notifier_block *notifier, | 
 | 				       unsigned long code, void *data) | 
 | { | 
 | 	struct sam9x60_div *div = notifier_div; | 
 | 	struct sam9x60_pll_core core = div->core; | 
 | 	struct regmap *regmap = core.regmap; | 
 | 	unsigned long irqflags; | 
 | 	u32 val, cdiv; | 
 | 	int ret = NOTIFY_DONE; | 
 |  | 
 | 	if (code != PRE_RATE_CHANGE) | 
 | 		return ret; | 
 |  | 
 | 	/* | 
 | 	 * We switch to safe divider to avoid overclocking of other domains | 
 | 	 * feed by us while the frac PLL (our parent) is changed. | 
 | 	 */ | 
 | 	div->div = div->safe_div; | 
 |  | 
 | 	spin_lock_irqsave(core.lock, irqflags); | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, | 
 | 			   core.id); | 
 | 	regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); | 
 | 	cdiv = (val & core.layout->div_mask) >> core.layout->div_shift; | 
 |  | 
 | 	/* Stop if nothing changed. */ | 
 | 	if (cdiv == div->safe_div) | 
 | 		goto unlock; | 
 |  | 
 | 	sam9x60_div_pll_set_div(&core, div->div, 0); | 
 | 	ret = NOTIFY_OK; | 
 |  | 
 | unlock: | 
 | 	spin_unlock_irqrestore(core.lock, irqflags); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static struct notifier_block sam9x60_div_pll_notifier = { | 
 | 	.notifier_call = sam9x60_div_pll_notifier_fn, | 
 | }; | 
 |  | 
 | static const struct clk_ops sam9x60_div_pll_ops = { | 
 | 	.prepare = sam9x60_div_pll_prepare, | 
 | 	.unprepare = sam9x60_div_pll_unprepare, | 
 | 	.is_prepared = sam9x60_div_pll_is_prepared, | 
 | 	.recalc_rate = sam9x60_div_pll_recalc_rate, | 
 | 	.round_rate = sam9x60_div_pll_round_rate, | 
 | 	.set_rate = sam9x60_div_pll_set_rate, | 
 | 	.save_context = sam9x60_div_pll_save_context, | 
 | 	.restore_context = sam9x60_div_pll_restore_context, | 
 | }; | 
 |  | 
 | static const struct clk_ops sam9x60_div_pll_ops_chg = { | 
 | 	.prepare = sam9x60_div_pll_prepare, | 
 | 	.unprepare = sam9x60_div_pll_unprepare, | 
 | 	.is_prepared = sam9x60_div_pll_is_prepared, | 
 | 	.recalc_rate = sam9x60_div_pll_recalc_rate, | 
 | 	.round_rate = sam9x60_div_pll_round_rate, | 
 | 	.set_rate = sam9x60_div_pll_set_rate_chg, | 
 | 	.save_context = sam9x60_div_pll_save_context, | 
 | 	.restore_context = sam9x60_div_pll_restore_context, | 
 | }; | 
 |  | 
 | static const struct clk_ops sam9x60_fixed_div_pll_ops = { | 
 | 	.prepare = sam9x60_div_pll_prepare, | 
 | 	.unprepare = sam9x60_div_pll_unprepare, | 
 | 	.is_prepared = sam9x60_div_pll_is_prepared, | 
 | 	.recalc_rate = sam9x60_fixed_div_pll_recalc_rate, | 
 | 	.round_rate = sam9x60_div_pll_round_rate, | 
 | 	.save_context = sam9x60_div_pll_save_context, | 
 | 	.restore_context = sam9x60_div_pll_restore_context, | 
 | }; | 
 |  | 
 | struct clk_hw * __init | 
 | sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock, | 
 | 			      const char *name, const char *parent_name, | 
 | 			      struct clk_hw *parent_hw, u8 id, | 
 | 			      const struct clk_pll_characteristics *characteristics, | 
 | 			      const struct clk_pll_layout *layout, u32 flags) | 
 | { | 
 | 	struct sam9x60_frac *frac; | 
 | 	struct clk_hw *hw; | 
 | 	struct clk_init_data init = {}; | 
 | 	unsigned long parent_rate, irqflags; | 
 | 	unsigned int val; | 
 | 	int ret; | 
 |  | 
 | 	if (id > PLL_MAX_ID || !lock || !parent_hw) | 
 | 		return ERR_PTR(-EINVAL); | 
 |  | 
 | 	frac = kzalloc(sizeof(*frac), GFP_KERNEL); | 
 | 	if (!frac) | 
 | 		return ERR_PTR(-ENOMEM); | 
 |  | 
 | 	init.name = name; | 
 | 	if (parent_name) | 
 | 		init.parent_names = &parent_name; | 
 | 	else | 
 | 		init.parent_hws = (const struct clk_hw **)&parent_hw; | 
 | 	init.num_parents = 1; | 
 | 	if (flags & CLK_SET_RATE_GATE) | 
 | 		init.ops = &sam9x60_frac_pll_ops; | 
 | 	else | 
 | 		init.ops = &sam9x60_frac_pll_ops_chg; | 
 |  | 
 | 	init.flags = flags; | 
 |  | 
 | 	frac->core.id = id; | 
 | 	frac->core.hw.init = &init; | 
 | 	frac->core.characteristics = characteristics; | 
 | 	frac->core.layout = layout; | 
 | 	frac->core.regmap = regmap; | 
 | 	frac->core.lock = lock; | 
 |  | 
 | 	spin_lock_irqsave(frac->core.lock, irqflags); | 
 | 	if (sam9x60_pll_ready(regmap, id)) { | 
 | 		regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 				   AT91_PMC_PLL_UPDT_ID_MSK, id); | 
 | 		regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val); | 
 | 		frac->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val); | 
 | 		frac->frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val); | 
 | 	} else { | 
 | 		/* | 
 | 		 * This means the PLL is not setup by bootloaders. In this | 
 | 		 * case we need to set the minimum rate for it. Otherwise | 
 | 		 * a clock child of this PLL may be enabled before setting | 
 | 		 * its rate leading to enabling this PLL with unsupported | 
 | 		 * rate. This will lead to PLL not being locked at all. | 
 | 		 */ | 
 | 		parent_rate = clk_hw_get_rate(parent_hw); | 
 | 		if (!parent_rate) { | 
 | 			hw = ERR_PTR(-EINVAL); | 
 | 			goto free; | 
 | 		} | 
 |  | 
 | 		ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, | 
 | 							characteristics->core_output[0].min, | 
 | 							parent_rate, true); | 
 | 		if (ret < 0) { | 
 | 			hw = ERR_PTR(ret); | 
 | 			goto free; | 
 | 		} | 
 | 	} | 
 | 	spin_unlock_irqrestore(frac->core.lock, irqflags); | 
 |  | 
 | 	hw = &frac->core.hw; | 
 | 	ret = clk_hw_register(NULL, hw); | 
 | 	if (ret) { | 
 | 		kfree(frac); | 
 | 		hw = ERR_PTR(ret); | 
 | 	} | 
 |  | 
 | 	return hw; | 
 |  | 
 | free: | 
 | 	spin_unlock_irqrestore(frac->core.lock, irqflags); | 
 | 	kfree(frac); | 
 | 	return hw; | 
 | } | 
 |  | 
 | struct clk_hw * __init | 
 | sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, | 
 | 			     const char *name, const char *parent_name, | 
 | 			     struct clk_hw *parent_hw, u8 id, | 
 | 			     const struct clk_pll_characteristics *characteristics, | 
 | 			     const struct clk_pll_layout *layout, u32 flags, | 
 | 			     u32 safe_div) | 
 | { | 
 | 	struct sam9x60_div *div; | 
 | 	struct clk_hw *hw; | 
 | 	struct clk_init_data init = {}; | 
 | 	unsigned long irqflags; | 
 | 	unsigned int val; | 
 | 	int ret; | 
 |  | 
 | 	/* We only support one changeable PLL. */ | 
 | 	if (id > PLL_MAX_ID || !lock || (safe_div && notifier_div)) | 
 | 		return ERR_PTR(-EINVAL); | 
 |  | 
 | 	if (safe_div >= PLL_DIV_MAX) | 
 | 		safe_div = PLL_DIV_MAX - 1; | 
 |  | 
 | 	div = kzalloc(sizeof(*div), GFP_KERNEL); | 
 | 	if (!div) | 
 | 		return ERR_PTR(-ENOMEM); | 
 |  | 
 | 	init.name = name; | 
 | 	if (parent_hw) | 
 | 		init.parent_hws = (const struct clk_hw **)&parent_hw; | 
 | 	else | 
 | 		init.parent_names = &parent_name; | 
 | 	init.num_parents = 1; | 
 |  | 
 | 	if (layout->div2) | 
 | 		init.ops = &sam9x60_fixed_div_pll_ops; | 
 | 	else if (flags & CLK_SET_RATE_GATE) | 
 | 		init.ops = &sam9x60_div_pll_ops; | 
 | 	else | 
 | 		init.ops = &sam9x60_div_pll_ops_chg; | 
 |  | 
 | 	init.flags = flags; | 
 |  | 
 | 	div->core.id = id; | 
 | 	div->core.hw.init = &init; | 
 | 	div->core.characteristics = characteristics; | 
 | 	div->core.layout = layout; | 
 | 	div->core.regmap = regmap; | 
 | 	div->core.lock = lock; | 
 | 	div->safe_div = safe_div; | 
 |  | 
 | 	spin_lock_irqsave(div->core.lock, irqflags); | 
 |  | 
 | 	regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, | 
 | 			   AT91_PMC_PLL_UPDT_ID_MSK, id); | 
 | 	regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); | 
 | 	div->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val); | 
 |  | 
 | 	spin_unlock_irqrestore(div->core.lock, irqflags); | 
 |  | 
 | 	hw = &div->core.hw; | 
 | 	ret = clk_hw_register(NULL, hw); | 
 | 	if (ret) { | 
 | 		kfree(div); | 
 | 		hw = ERR_PTR(ret); | 
 | 	} else if (div->safe_div) { | 
 | 		notifier_div = div; | 
 | 		clk_notifier_register(hw->clk, &sam9x60_div_pll_notifier); | 
 | 	} | 
 |  | 
 | 	return hw; | 
 | } | 
 |  |