mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-22 08:38:15 +01:00 
			
		
		
		
	Move this uncommon header out of the common header. Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			428 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <clk-uclass.h>
 | |
| #include <dm.h>
 | |
| #include <div64.h>
 | |
| #include <time.h>
 | |
| #include <wait_bit.h>
 | |
| #include <dm/lists.h>
 | |
| #include <asm/io.h>
 | |
| #include <linux/bitops.h>
 | |
| #include <linux/bug.h>
 | |
| #include <mach/pic32.h>
 | |
| #include <dt-bindings/clock/microchip,clock.h>
 | |
| 
 | |
| DECLARE_GLOBAL_DATA_PTR;
 | |
| 
 | |
| /* Primary oscillator */
 | |
| #define SYS_POSC_CLK_HZ	24000000
 | |
| 
 | |
| /* FRC clk rate */
 | |
| #define SYS_FRC_CLK_HZ	8000000
 | |
| 
 | |
| /* Clock Registers */
 | |
| #define OSCCON		0x0000
 | |
| #define OSCTUNE		0x0010
 | |
| #define SPLLCON		0x0020
 | |
| #define REFO1CON	0x0080
 | |
| #define REFO1TRIM	0x0090
 | |
| #define PB1DIV		0x0140
 | |
| 
 | |
| /* SPLL */
 | |
| #define ICLK_MASK	0x00000080
 | |
| #define PLLIDIV_MASK	0x00000007
 | |
| #define PLLODIV_MASK	0x00000007
 | |
| #define CUROSC_MASK	0x00000007
 | |
| #define PLLMUL_MASK	0x0000007F
 | |
| #define FRCDIV_MASK	0x00000007
 | |
| 
 | |
| /* PBCLK */
 | |
| #define PBDIV_MASK	0x00000007
 | |
| 
 | |
| /* SYSCLK MUX */
 | |
| #define SCLK_SRC_FRC1	0
 | |
| #define SCLK_SRC_SPLL	1
 | |
| #define SCLK_SRC_POSC	2
 | |
| #define SCLK_SRC_FRC2	7
 | |
| 
 | |
| /* Reference Oscillator Control Reg fields */
 | |
| #define REFO_SEL_MASK	0x0f
 | |
| #define REFO_SEL_SHIFT	0
 | |
| #define REFO_ACTIVE	BIT(8)
 | |
| #define REFO_DIVSW_EN	BIT(9)
 | |
| #define REFO_OE		BIT(12)
 | |
| #define REFO_ON		BIT(15)
 | |
| #define REFO_DIV_SHIFT	16
 | |
| #define REFO_DIV_MASK	0x7fff
 | |
| 
 | |
| /* Reference Oscillator Trim Register Fields */
 | |
| #define REFO_TRIM_REG	0x10
 | |
| #define REFO_TRIM_MASK	0x1ff
 | |
| #define REFO_TRIM_SHIFT	23
 | |
| #define REFO_TRIM_MAX	511
 | |
| 
 | |
| #define ROCLK_SRC_SCLK		0x0
 | |
| #define ROCLK_SRC_SPLL		0x7
 | |
| #define ROCLK_SRC_ROCLKI	0x8
 | |
| 
 | |
| /* Memory PLL */
 | |
| #define MPLL_IDIV		0x3f
 | |
| #define MPLL_MULT		0xff
 | |
| #define MPLL_ODIV1		0x7
 | |
| #define MPLL_ODIV2		0x7
 | |
| #define MPLL_VREG_RDY		BIT(23)
 | |
| #define MPLL_RDY		BIT(31)
 | |
| #define MPLL_IDIV_SHIFT		0
 | |
| #define MPLL_MULT_SHIFT		8
 | |
| #define MPLL_ODIV1_SHIFT	24
 | |
| #define MPLL_ODIV2_SHIFT	27
 | |
| #define MPLL_IDIV_INIT		0x03
 | |
| #define MPLL_MULT_INIT		0x32
 | |
| #define MPLL_ODIV1_INIT		0x02
 | |
| #define MPLL_ODIV2_INIT		0x01
 | |
| 
 | |
| struct pic32_clk_priv {
 | |
| 	void __iomem *iobase;
 | |
| 	void __iomem *syscfg_base;
 | |
| };
 | |
| 
 | |
| static ulong pic32_get_pll_rate(struct pic32_clk_priv *priv)
 | |
| {
 | |
| 	u32 iclk, idiv, odiv, mult;
 | |
| 	ulong plliclk, v;
 | |
| 
 | |
| 	v = readl(priv->iobase + SPLLCON);
 | |
| 	iclk = (v & ICLK_MASK);
 | |
| 	idiv = ((v >> 8) & PLLIDIV_MASK) + 1;
 | |
| 	odiv = ((v >> 24) & PLLODIV_MASK);
 | |
| 	mult = ((v >> 16) & PLLMUL_MASK) + 1;
 | |
| 
 | |
| 	plliclk = iclk ? SYS_FRC_CLK_HZ : SYS_POSC_CLK_HZ;
 | |
| 
 | |
| 	if (odiv < 2)
 | |
| 		odiv = 2;
 | |
| 	else if (odiv < 5)
 | |
| 		odiv = (1 << odiv);
 | |
| 	else
 | |
| 		odiv = 32;
 | |
| 
 | |
| 	return ((plliclk / idiv) * mult) / odiv;
 | |
| }
 | |
| 
 | |
| static ulong pic32_get_sysclk(struct pic32_clk_priv *priv)
 | |
| {
 | |
| 	ulong v;
 | |
| 	ulong hz;
 | |
| 	ulong div, frcdiv;
 | |
| 	ulong curr_osc;
 | |
| 
 | |
| 	/* get clk source */
 | |
| 	v = readl(priv->iobase + OSCCON);
 | |
| 	curr_osc = (v >> 12) & CUROSC_MASK;
 | |
| 	switch (curr_osc) {
 | |
| 	case SCLK_SRC_FRC1:
 | |
| 	case SCLK_SRC_FRC2:
 | |
| 		frcdiv = ((v >> 24) & FRCDIV_MASK);
 | |
| 		div = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
 | |
| 		hz = SYS_FRC_CLK_HZ / div;
 | |
| 		break;
 | |
| 
 | |
| 	case SCLK_SRC_SPLL:
 | |
| 		hz = pic32_get_pll_rate(priv);
 | |
| 		break;
 | |
| 
 | |
| 	case SCLK_SRC_POSC:
 | |
| 		hz = SYS_POSC_CLK_HZ;
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		hz = 0;
 | |
| 		printf("clk: unknown sclk_src.\n");
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return hz;
 | |
| }
 | |
| 
 | |
| static ulong pic32_get_pbclk(struct pic32_clk_priv *priv, int periph)
 | |
| {
 | |
| 	void __iomem *reg;
 | |
| 	ulong div, clk_freq;
 | |
| 
 | |
| 	WARN_ON((periph < PB1CLK) || (periph > PB7CLK));
 | |
| 
 | |
| 	clk_freq = pic32_get_sysclk(priv);
 | |
| 
 | |
| 	reg = priv->iobase + PB1DIV + (periph - PB1CLK) * 0x10;
 | |
| 	div = (readl(reg) & PBDIV_MASK) + 1;
 | |
| 
 | |
| 	return clk_freq / div;
 | |
| }
 | |
| 
 | |
| static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
 | |
| {
 | |
| 	return pic32_get_pbclk(priv, PB7CLK);
 | |
| }
 | |
| 
 | |
| static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph,
 | |
| 			      int parent_rate, int rate, int parent_id)
 | |
| {
 | |
| 	void __iomem *reg;
 | |
| 	u32 div, trim, v;
 | |
| 	u64 frac;
 | |
| 
 | |
| 	WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
 | |
| 
 | |
| 	/* calculate dividers,
 | |
| 	 *   rate = parent_rate / [2 * (div + (trim / 512))]
 | |
| 	 */
 | |
| 	if (parent_rate <= rate) {
 | |
| 		div = 0;
 | |
| 		trim = 0;
 | |
| 	} else {
 | |
| 		div = parent_rate / (rate << 1);
 | |
| 		frac = parent_rate;
 | |
| 		frac <<= 8;
 | |
| 		do_div(frac, rate);
 | |
| 		frac -= (u64)(div << 9);
 | |
| 		trim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u32)frac;
 | |
| 	}
 | |
| 
 | |
| 	reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
 | |
| 
 | |
| 	/* disable clk */
 | |
| 	writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
 | |
| 
 | |
| 	/* wait till previous src change is active */
 | |
| 	wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE,
 | |
| 			  false, CONFIG_SYS_HZ, false);
 | |
| 
 | |
| 	/* parent_id */
 | |
| 	v = readl(reg);
 | |
| 	v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
 | |
| 	v |= (parent_id << REFO_SEL_SHIFT);
 | |
| 
 | |
| 	/* apply rodiv */
 | |
| 	v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
 | |
| 	v |= (div << REFO_DIV_SHIFT);
 | |
| 	writel(v, reg);
 | |
| 
 | |
| 	/* apply trim */
 | |
| 	v = readl(reg + REFO_TRIM_REG);
 | |
| 	v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
 | |
| 	v |= (trim << REFO_TRIM_SHIFT);
 | |
| 	writel(v, reg + REFO_TRIM_REG);
 | |
| 
 | |
| 	/* enable clk */
 | |
| 	writel(REFO_ON | REFO_OE, reg + _SET_OFFSET);
 | |
| 
 | |
| 	/* switch divider */
 | |
| 	writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
 | |
| 
 | |
| 	/* wait for divider switching to complete */
 | |
| 	return wait_for_bit_le32(reg, REFO_DIVSW_EN, false,
 | |
| 				 CONFIG_SYS_HZ, false);
 | |
| }
 | |
| 
 | |
| static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph)
 | |
| {
 | |
| 	u32 rodiv, rotrim, rosel, v, parent_rate;
 | |
| 	void __iomem *reg;
 | |
| 	u64 rate64;
 | |
| 
 | |
| 	WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
 | |
| 
 | |
| 	reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
 | |
| 	v = readl(reg);
 | |
| 	/* get rosel */
 | |
| 	rosel = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
 | |
| 	/* get div */
 | |
| 	rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
 | |
| 
 | |
| 	/* get trim */
 | |
| 	v = readl(reg + REFO_TRIM_REG);
 | |
| 	rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
 | |
| 
 | |
| 	if (!rodiv)
 | |
| 		return 0;
 | |
| 
 | |
| 	/* get parent rate */
 | |
| 	switch (rosel) {
 | |
| 	case ROCLK_SRC_SCLK:
 | |
| 		parent_rate = pic32_get_cpuclk(priv);
 | |
| 		break;
 | |
| 	case ROCLK_SRC_SPLL:
 | |
| 		parent_rate = pic32_get_pll_rate(priv);
 | |
| 		break;
 | |
| 	default:
 | |
| 		parent_rate = 0;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/* Calculation
 | |
| 	 * rate = parent_rate / [2 * (div + (trim / 512))]
 | |
| 	 */
 | |
| 	if (rotrim) {
 | |
| 		rodiv <<= 9;
 | |
| 		rodiv += rotrim;
 | |
| 		rate64 = parent_rate;
 | |
| 		rate64 <<= 8;
 | |
| 		do_div(rate64, rodiv);
 | |
| 		v = (u32)rate64;
 | |
| 	} else {
 | |
| 		v = parent_rate / (rodiv << 1);
 | |
| 	}
 | |
| 	return v;
 | |
| }
 | |
| 
 | |
| static ulong pic32_get_mpll_rate(struct pic32_clk_priv *priv)
 | |
| {
 | |
| 	u32 v, idiv, mul;
 | |
| 	u32 odiv1, odiv2;
 | |
| 	u64 rate;
 | |
| 
 | |
| 	v = readl(priv->syscfg_base + CFGMPLL);
 | |
| 	idiv = v & MPLL_IDIV;
 | |
| 	mul = (v >> MPLL_MULT_SHIFT) & MPLL_MULT;
 | |
| 	odiv1 = (v >> MPLL_ODIV1_SHIFT) & MPLL_ODIV1;
 | |
| 	odiv2 = (v >> MPLL_ODIV2_SHIFT) & MPLL_ODIV2;
 | |
| 
 | |
| 	rate = (SYS_POSC_CLK_HZ / idiv) * mul;
 | |
| 	do_div(rate, odiv1);
 | |
| 	do_div(rate, odiv2);
 | |
| 
 | |
| 	return (ulong)rate;
 | |
| }
 | |
| 
 | |
| static int pic32_mpll_init(struct pic32_clk_priv *priv)
 | |
| {
 | |
| 	u32 v, mask;
 | |
| 
 | |
| 	/* initialize */
 | |
| 	v = (MPLL_IDIV_INIT << MPLL_IDIV_SHIFT) |
 | |
| 	    (MPLL_MULT_INIT << MPLL_MULT_SHIFT) |
 | |
| 	    (MPLL_ODIV1_INIT << MPLL_ODIV1_SHIFT) |
 | |
| 	    (MPLL_ODIV2_INIT << MPLL_ODIV2_SHIFT);
 | |
| 
 | |
| 	writel(v, priv->syscfg_base + CFGMPLL);
 | |
| 
 | |
| 	/* Wait for ready */
 | |
| 	mask = MPLL_RDY | MPLL_VREG_RDY;
 | |
| 	return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask,
 | |
| 				 true, get_tbclk(), false);
 | |
| }
 | |
| 
 | |
| static void pic32_clk_init(struct udevice *dev)
 | |
| {
 | |
| 	const void *blob = gd->fdt_blob;
 | |
| 	struct pic32_clk_priv *priv;
 | |
| 	ulong rate, pll_hz;
 | |
| 	char propname[50];
 | |
| 	int i;
 | |
| 
 | |
| 	priv = dev_get_priv(dev);
 | |
| 	pll_hz = pic32_get_pll_rate(priv);
 | |
| 
 | |
| 	/* Initialize REFOs as not initialized and enabled on reset. */
 | |
| 	for (i = REF1CLK; i <= REF5CLK; i++) {
 | |
| 		snprintf(propname, sizeof(propname),
 | |
| 			 "microchip,refo%d-frequency", i - REF1CLK + 1);
 | |
| 		rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0);
 | |
| 		if (rate)
 | |
| 			pic32_set_refclk(priv, i, pll_hz, rate, ROCLK_SRC_SPLL);
 | |
| 	}
 | |
| 
 | |
| 	/* Memory PLL */
 | |
| 	pic32_mpll_init(priv);
 | |
| }
 | |
| 
 | |
| static ulong pic32_get_rate(struct clk *clk)
 | |
| {
 | |
| 	struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
 | |
| 	ulong rate;
 | |
| 
 | |
| 	switch (clk->id) {
 | |
| 	case PB1CLK ... PB7CLK:
 | |
| 		rate = pic32_get_pbclk(priv, clk->id);
 | |
| 		break;
 | |
| 	case REF1CLK ... REF5CLK:
 | |
| 		rate = pic32_get_refclk(priv, clk->id);
 | |
| 		break;
 | |
| 	case PLLCLK:
 | |
| 		rate = pic32_get_pll_rate(priv);
 | |
| 		break;
 | |
| 	case MPLL:
 | |
| 		rate = pic32_get_mpll_rate(priv);
 | |
| 		break;
 | |
| 	default:
 | |
| 		rate = 0;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return rate;
 | |
| }
 | |
| 
 | |
| static ulong pic32_set_rate(struct clk *clk, ulong rate)
 | |
| {
 | |
| 	struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
 | |
| 	ulong pll_hz;
 | |
| 
 | |
| 	switch (clk->id) {
 | |
| 	case REF1CLK ... REF5CLK:
 | |
| 		pll_hz = pic32_get_pll_rate(priv);
 | |
| 		pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return rate;
 | |
| }
 | |
| 
 | |
| static struct clk_ops pic32_pic32_clk_ops = {
 | |
| 	.set_rate = pic32_set_rate,
 | |
| 	.get_rate = pic32_get_rate,
 | |
| };
 | |
| 
 | |
| static int pic32_clk_probe(struct udevice *dev)
 | |
| {
 | |
| 	struct pic32_clk_priv *priv = dev_get_priv(dev);
 | |
| 	fdt_addr_t addr;
 | |
| 	fdt_size_t size;
 | |
| 
 | |
| 	addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
 | |
| 				    &size);
 | |
| 	if (addr == FDT_ADDR_T_NONE)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	priv->iobase = ioremap(addr, size);
 | |
| 	if (!priv->iobase)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	priv->syscfg_base = pic32_get_syscfg_base();
 | |
| 
 | |
| 	/* initialize clocks */
 | |
| 	pic32_clk_init(dev);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct udevice_id pic32_clk_ids[] = {
 | |
| 	{ .compatible = "microchip,pic32mzda-clk"},
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| U_BOOT_DRIVER(pic32_clk) = {
 | |
| 	.name		= "pic32_clk",
 | |
| 	.id		= UCLASS_CLK,
 | |
| 	.of_match	= pic32_clk_ids,
 | |
| 	.ops		= &pic32_pic32_clk_ops,
 | |
| 	.probe		= pic32_clk_probe,
 | |
| 	.priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
 | |
| };
 |