| 
									
										
										
										
											2012-03-09 09:11:55 +01:00
										 |  |  | #include <linux/clk.h>
 | 
					
						
							|  |  |  | #include <linux/clk-provider.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/err.h>
 | 
					
						
							| 
									
										
										
										
											2012-09-11 08:50:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-09 09:11:55 +01:00
										 |  |  | #include "clk.h"
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:01:00 +08:00
										 |  |  | #include "common.h"
 | 
					
						
							| 
									
										
										
										
											2012-09-14 14:14:45 +08:00
										 |  |  | #include "hardware.h"
 | 
					
						
							| 
									
										
										
										
											2012-03-09 09:11:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * pll v1 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @clk_hw	clock source | 
					
						
							|  |  |  |  * @parent	the parent clock name | 
					
						
							|  |  |  |  * @base	base address of pll registers | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * PLL clock version 1, found on i.MX1/21/25/27/31/35 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-11-10 13:34:49 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define MFN_BITS	(10)
 | 
					
						
							|  |  |  | #define MFN_SIGN	(BIT(MFN_BITS - 1))
 | 
					
						
							|  |  |  | #define MFN_MASK	(MFN_SIGN - 1)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-09 09:11:55 +01:00
										 |  |  | struct clk_pllv1 { | 
					
						
							|  |  |  | 	struct clk_hw	hw; | 
					
						
							|  |  |  | 	void __iomem	*base; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define to_clk_pllv1(clk) (container_of(clk, struct clk_pllv1, clk))
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-10 13:34:49 +04:00
										 |  |  | static inline bool mfn_is_negative(unsigned int mfn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return !cpu_is_mx1() && !cpu_is_mx21() && (mfn & MFN_SIGN); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-09 09:11:55 +01:00
										 |  |  | static unsigned long clk_pllv1_recalc_rate(struct clk_hw *hw, | 
					
						
							|  |  |  | 		unsigned long parent_rate) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct clk_pllv1 *pll = to_clk_pllv1(hw); | 
					
						
							| 
									
										
										
										
											2012-09-11 08:40:38 +02:00
										 |  |  | 	long long ll; | 
					
						
							|  |  |  | 	int mfn_abs; | 
					
						
							|  |  |  | 	unsigned int mfi, mfn, mfd, pd; | 
					
						
							|  |  |  | 	u32 reg; | 
					
						
							|  |  |  | 	unsigned long rate; | 
					
						
							| 
									
										
										
										
											2012-03-09 09:11:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-11 08:40:38 +02:00
										 |  |  | 	reg = readl(pll->base); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Get the resulting clock rate from a PLL register value and the input | 
					
						
							|  |  |  | 	 * frequency. PLLs with this register layout can be found on i.MX1, | 
					
						
							|  |  |  | 	 * i.MX21, i.MX27 and i,MX31 | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 *                  mfi + mfn / (mfd + 1) | 
					
						
							|  |  |  | 	 *  f = 2 * f_ref * -------------------- | 
					
						
							|  |  |  | 	 *                        pd + 1 | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mfi = (reg >> 10) & 0xf; | 
					
						
							|  |  |  | 	mfn = reg & 0x3ff; | 
					
						
							|  |  |  | 	mfd = (reg >> 16) & 0x3ff; | 
					
						
							|  |  |  | 	pd =  (reg >> 26) & 0xf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mfi = mfi <= 5 ? 5 : mfi; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mfn_abs = mfn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * On all i.MXs except i.MX1 and i.MX21 mfn is a 10bit | 
					
						
							| 
									
										
										
										
											2013-11-10 13:34:49 +04:00
										 |  |  | 	 * 2's complements number. | 
					
						
							|  |  |  | 	 * On i.MX27 the bit 9 is the sign bit. | 
					
						
							| 
									
										
										
										
											2012-09-11 08:40:38 +02:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-11-10 13:34:49 +04:00
										 |  |  | 	if (mfn_is_negative(mfn)) { | 
					
						
							|  |  |  | 		if (cpu_is_mx27()) | 
					
						
							|  |  |  | 			mfn_abs = mfn & MFN_MASK; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			mfn_abs = BIT(MFN_BITS) - mfn; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-09-11 08:40:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rate = parent_rate * 2; | 
					
						
							|  |  |  | 	rate /= pd + 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ll = (unsigned long long)rate * mfn_abs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do_div(ll, mfd + 1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-10 13:34:49 +04:00
										 |  |  | 	if (mfn_is_negative(mfn)) | 
					
						
							| 
									
										
										
										
											2012-09-11 08:40:38 +02:00
										 |  |  | 		ll = -ll; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ll = (rate * mfi) + ll; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ll; | 
					
						
							| 
									
										
										
										
											2012-03-09 09:11:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-25 09:20:33 -03:00
										 |  |  | static struct clk_ops clk_pllv1_ops = { | 
					
						
							| 
									
										
										
										
											2012-03-09 09:11:55 +01:00
										 |  |  | 	.recalc_rate = clk_pllv1_recalc_rate, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct clk *imx_clk_pllv1(const char *name, const char *parent, | 
					
						
							|  |  |  | 		void __iomem *base) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct clk_pllv1 *pll; | 
					
						
							|  |  |  | 	struct clk *clk; | 
					
						
							|  |  |  | 	struct clk_init_data init; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pll = kmalloc(sizeof(*pll), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!pll) | 
					
						
							|  |  |  | 		return ERR_PTR(-ENOMEM); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pll->base = base; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	init.name = name; | 
					
						
							|  |  |  | 	init.ops = &clk_pllv1_ops; | 
					
						
							|  |  |  | 	init.flags = 0; | 
					
						
							|  |  |  | 	init.parent_names = &parent; | 
					
						
							|  |  |  | 	init.num_parents = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pll->hw.init = &init; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clk = clk_register(NULL, &pll->hw); | 
					
						
							|  |  |  | 	if (IS_ERR(clk)) | 
					
						
							|  |  |  | 		kfree(pll); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return clk; | 
					
						
							|  |  |  | } |