| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 | // 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 <wait_bit.h>#include <dm/lists.h>#include <asm/io.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		0x01struct 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,	.flags		= DM_FLAG_PRE_RELOC,	.ops		= &pic32_pic32_clk_ops,	.probe		= pic32_clk_probe,	.priv_auto_alloc_size = sizeof(struct pic32_clk_priv),};
 |