Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c0c86d5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'clk-for-linus' of git://git.linaro.org/people/mturquette/linux

Pull clk framework update from Michael Turquette:
 "The common clk framework changes for 3.7 are dominated by ARM platform
  ports to the framework along with one MIPS port, one MFD port, one
  minor framework enhancement and one helper function for platforms
  expressing their clock data through device tree."

* tag 'clk-for-linus' of git://git.linaro.org/people/mturquette/linux:
  clk: add of_clk_src_onecell_get() support
  clk: ux500: Define smp_twd clock for u8500
  mfd: dbx500: Provide a more accurate smp_twd clock
  clk: ux500: Support for prmcu_rate clock
  clk: Provide option for clk_get_rate to issue hw for new rate
  clock: max77686: Add driver for Maxim 77686 32Khz crystal oscillator.
  ARM: ux500: Switch to use common clock framework
  clk: ux500: Clock definitions for u8500
  clk: ux500: First version of clock definitions for ux500
  clk: ux500: Adapt PRCMU and PRCC clocks for common clk
  clk: versatile: make config option boolean
  clk: add Loongson1B clock support
  arm: mmp: make all SOCs use common clock by default
  clk: mmp: add clock definition for mmp2
  clk: mmp: add clock definition for pxa910
  clk: mmp: add clock definition for pxa168
  clk: mmp: add mmp specific clocks
  clk: convert ARM RealView to common clk
  clk: prima2: move from arch/arm/mach to drivers/clk
  ARM: PRIMA2: convert to common clk and finish full clk tree
parents fdb2f9c2 494bfec9
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -273,7 +273,7 @@ config ARCH_INTEGRATOR
	select ARM_AMBA
	select ARCH_HAS_CPUFREQ
	select COMMON_CLK
	select CLK_VERSATILE
	select COMMON_CLK_VERSATILE
	select HAVE_TCM
	select ICST
	select GENERIC_CLOCKEVENTS
@@ -289,13 +289,12 @@ config ARCH_INTEGRATOR
config ARCH_REALVIEW
	bool "ARM Ltd. RealView family"
	select ARM_AMBA
	select CLKDEV_LOOKUP
	select HAVE_MACH_CLKDEV
	select COMMON_CLK
	select COMMON_CLK_VERSATILE
	select ICST
	select GENERIC_CLOCKEVENTS
	select ARCH_WANT_OPTIONAL_GPIOLIB
	select PLAT_VERSATILE
	select PLAT_VERSATILE_CLOCK
	select PLAT_VERSATILE_CLCD
	select ARM_TIMER_SP804
	select GPIO_PL061 if GPIOLIB
@@ -413,7 +412,7 @@ config ARCH_PRIMA2
	select NO_IOPORT
	select ARCH_REQUIRE_GPIOLIB
	select GENERIC_CLOCKEVENTS
	select CLKDEV_LOOKUP
	select COMMON_CLK
	select GENERIC_IRQ_CHIP
	select MIGHT_HAVE_CACHE_L2X0
	select PINCTRL
+3 −0
Original line number Diff line number Diff line
@@ -108,18 +108,21 @@ endmenu
config CPU_PXA168
	bool
	select CPU_MOHAWK
	select COMMON_CLK
	help
	  Select code specific to PXA168

config CPU_PXA910
	bool
	select CPU_MOHAWK
	select COMMON_CLK
	help
	  Select code specific to PXA910

config CPU_MMP2
	bool
	select CPU_PJ4
	select COMMON_CLK
	help
	  Select code specific to MMP2. MMP2 is ARMv7 compatible.

+0 −1
Original line number Diff line number Diff line
obj-y := timer.o
obj-y += irq.o
obj-y += clock.o
obj-y += rstc.o
obj-y += prima2.o
obj-y += rtciobrg.o

arch/arm/mach-prima2/clock.c

deleted100644 → 0
+0 −510
Original line number Diff line number Diff line
/*
 * Clock tree for CSR SiRFprimaII
 *
 * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
 *
 * Licensed under GPLv2 or later.
 */

#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/clkdev.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <mach/map.h>

#define SIRFSOC_CLKC_CLK_EN0    0x0000
#define SIRFSOC_CLKC_CLK_EN1    0x0004
#define SIRFSOC_CLKC_REF_CFG    0x0014
#define SIRFSOC_CLKC_CPU_CFG    0x0018
#define SIRFSOC_CLKC_MEM_CFG    0x001c
#define SIRFSOC_CLKC_SYS_CFG    0x0020
#define SIRFSOC_CLKC_IO_CFG     0x0024
#define SIRFSOC_CLKC_DSP_CFG    0x0028
#define SIRFSOC_CLKC_GFX_CFG    0x002c
#define SIRFSOC_CLKC_MM_CFG     0x0030
#define SIRFSOC_LKC_LCD_CFG     0x0034
#define SIRFSOC_CLKC_MMC_CFG    0x0038
#define SIRFSOC_CLKC_PLL1_CFG0  0x0040
#define SIRFSOC_CLKC_PLL2_CFG0  0x0044
#define SIRFSOC_CLKC_PLL3_CFG0  0x0048
#define SIRFSOC_CLKC_PLL1_CFG1  0x004c
#define SIRFSOC_CLKC_PLL2_CFG1  0x0050
#define SIRFSOC_CLKC_PLL3_CFG1  0x0054
#define SIRFSOC_CLKC_PLL1_CFG2  0x0058
#define SIRFSOC_CLKC_PLL2_CFG2  0x005c
#define SIRFSOC_CLKC_PLL3_CFG2  0x0060

#define SIRFSOC_CLOCK_VA_BASE		SIRFSOC_VA(0x005000)

#define KHZ     1000
#define MHZ     (KHZ * KHZ)

struct clk_ops {
	unsigned long (*get_rate)(struct clk *clk);
	long (*round_rate)(struct clk *clk, unsigned long rate);
	int (*set_rate)(struct clk *clk, unsigned long rate);
	int (*enable)(struct clk *clk);
	int (*disable)(struct clk *clk);
	struct clk *(*get_parent)(struct clk *clk);
	int (*set_parent)(struct clk *clk, struct clk *parent);
};

struct clk {
	struct clk *parent;     /* parent clk */
	unsigned long rate;     /* clock rate in Hz */
	signed char usage;      /* clock enable count */
	signed char enable_bit; /* enable bit: 0 ~ 63 */
	unsigned short regofs;  /* register offset */
	struct clk_ops *ops;    /* clock operation */
};

static DEFINE_SPINLOCK(clocks_lock);

static inline unsigned long clkc_readl(unsigned reg)
{
	return readl(SIRFSOC_CLOCK_VA_BASE + reg);
}

static inline void clkc_writel(u32 val, unsigned reg)
{
	writel(val, SIRFSOC_CLOCK_VA_BASE + reg);
}

/*
 * osc_rtc - real time oscillator - 32.768KHz
 * osc_sys - high speed oscillator - 26MHz
 */

static struct clk clk_rtc = {
	.rate = 32768,
};

static struct clk clk_osc = {
	.rate = 26 * MHZ,
};

/*
 * std pll
 */
static unsigned long std_pll_get_rate(struct clk *clk)
{
	unsigned long fin = clk_get_rate(clk->parent);
	u32 regcfg2 = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 -
		SIRFSOC_CLKC_PLL1_CFG0;

	if (clkc_readl(regcfg2) & BIT(2)) {
		/* pll bypass mode */
		clk->rate = fin;
	} else {
		/* fout = fin * nf / nr / od */
		u32 cfg0 = clkc_readl(clk->regofs);
		u32 nf = (cfg0 & (BIT(13) - 1)) + 1;
		u32 nr = ((cfg0 >> 13) & (BIT(6) - 1)) + 1;
		u32 od = ((cfg0 >> 19) & (BIT(4) - 1)) + 1;
		WARN_ON(fin % MHZ);
		clk->rate = fin / MHZ * nf / nr / od * MHZ;
	}

	return clk->rate;
}

static int std_pll_set_rate(struct clk *clk, unsigned long rate)
{
	unsigned long fin, nf, nr, od, reg;

	/*
	 * fout = fin * nf / (nr * od);
	 * set od = 1, nr = fin/MHz, so fout = nf * MHz
	 */

	nf = rate / MHZ;
	if (unlikely((rate % MHZ) || nf > BIT(13) || nf < 1))
		return -EINVAL;

	fin = clk_get_rate(clk->parent);
	BUG_ON(fin < MHZ);

	nr = fin / MHZ;
	BUG_ON((fin % MHZ) || nr > BIT(6));

	od = 1;

	reg = (nf - 1) | ((nr - 1) << 13) | ((od - 1) << 19);
	clkc_writel(reg, clk->regofs);

	reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG1 - SIRFSOC_CLKC_PLL1_CFG0;
	clkc_writel((nf >> 1) - 1, reg);

	reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 - SIRFSOC_CLKC_PLL1_CFG0;
	while (!(clkc_readl(reg) & BIT(6)))
		cpu_relax();

	clk->rate = 0; /* set to zero will force recalculation */
	return 0;
}

static struct clk_ops std_pll_ops = {
	.get_rate = std_pll_get_rate,
	.set_rate = std_pll_set_rate,
};

static struct clk clk_pll1 = {
	.parent = &clk_osc,
	.regofs = SIRFSOC_CLKC_PLL1_CFG0,
	.ops = &std_pll_ops,
};

static struct clk clk_pll2 = {
	.parent = &clk_osc,
	.regofs = SIRFSOC_CLKC_PLL2_CFG0,
	.ops = &std_pll_ops,
};

static struct clk clk_pll3 = {
	.parent = &clk_osc,
	.regofs = SIRFSOC_CLKC_PLL3_CFG0,
	.ops = &std_pll_ops,
};

/*
 * clock domains - cpu, mem, sys/io
 */

static struct clk clk_mem;

static struct clk *dmn_get_parent(struct clk *clk)
{
	struct clk *clks[] = {
		&clk_osc, &clk_rtc, &clk_pll1, &clk_pll2, &clk_pll3
	};
	u32 cfg = clkc_readl(clk->regofs);
	WARN_ON((cfg & (BIT(3) - 1)) > 4);
	return clks[cfg & (BIT(3) - 1)];
}

static int dmn_set_parent(struct clk *clk, struct clk *parent)
{
	const struct clk *clks[] = {
		&clk_osc, &clk_rtc, &clk_pll1, &clk_pll2, &clk_pll3
	};
	u32 cfg = clkc_readl(clk->regofs);
	int i;
	for (i = 0; i < ARRAY_SIZE(clks); i++) {
		if (clks[i] == parent) {
			cfg &= ~(BIT(3) - 1);
			clkc_writel(cfg | i, clk->regofs);
			/* BIT(3) - switching status: 1 - busy, 0 - done */
			while (clkc_readl(clk->regofs) & BIT(3))
				cpu_relax();
			return 0;
		}
	}
	return -EINVAL;
}

static unsigned long dmn_get_rate(struct clk *clk)
{
	unsigned long fin = clk_get_rate(clk->parent);
	u32 cfg = clkc_readl(clk->regofs);
	if (cfg & BIT(24)) {
		/* fcd bypass mode */
		clk->rate = fin;
	} else {
		/*
		 * wait count: bit[19:16], hold count: bit[23:20]
		 */
		u32 wait = (cfg >> 16) & (BIT(4) - 1);
		u32 hold = (cfg >> 20) & (BIT(4) - 1);

		clk->rate = fin / (wait + hold + 2);
	}

	return clk->rate;
}

static int dmn_set_rate(struct clk *clk, unsigned long rate)
{
	unsigned long fin;
	unsigned ratio, wait, hold, reg;
	unsigned bits = (clk == &clk_mem) ? 3 : 4;

	fin = clk_get_rate(clk->parent);
	ratio = fin / rate;

	if (unlikely(ratio < 2 || ratio > BIT(bits + 1)))
		return -EINVAL;

	WARN_ON(fin % rate);

	wait = (ratio >> 1) - 1;
	hold = ratio - wait - 2;

	reg = clkc_readl(clk->regofs);
	reg &= ~(((BIT(bits) - 1) << 16) | ((BIT(bits) - 1) << 20));
	reg |= (wait << 16) | (hold << 20) | BIT(25);
	clkc_writel(reg, clk->regofs);

	/* waiting FCD been effective */
	while (clkc_readl(clk->regofs) & BIT(25))
		cpu_relax();

	clk->rate = 0; /* set to zero will force recalculation */

	return 0;
}

/*
 * cpu clock has no FCD register in Prima2, can only change pll
 */
static int cpu_set_rate(struct clk *clk, unsigned long rate)
{
	int ret1, ret2;
	struct clk *cur_parent, *tmp_parent;

	cur_parent = dmn_get_parent(clk);
	BUG_ON(cur_parent == NULL || cur_parent->usage > 1);

	/* switch to tmp pll before setting parent clock's rate */
	tmp_parent = cur_parent == &clk_pll1 ? &clk_pll2 : &clk_pll1;
	ret1 = dmn_set_parent(clk, tmp_parent);
	BUG_ON(ret1);

	ret2 = clk_set_rate(cur_parent, rate);

	ret1 = dmn_set_parent(clk, cur_parent);

	clk->rate = 0; /* set to zero will force recalculation */

	return ret2 ? ret2 : ret1;
}

static struct clk_ops cpu_ops = {
	.get_parent = dmn_get_parent,
	.set_parent = dmn_set_parent,
	.set_rate = cpu_set_rate,
};

static struct clk clk_cpu = {
	.parent = &clk_pll1,
	.regofs = SIRFSOC_CLKC_CPU_CFG,
	.ops = &cpu_ops,
};


static struct clk_ops msi_ops = {
	.set_rate = dmn_set_rate,
	.get_rate = dmn_get_rate,
	.set_parent = dmn_set_parent,
	.get_parent = dmn_get_parent,
};

static struct clk clk_mem = {
	.parent = &clk_pll2,
	.regofs = SIRFSOC_CLKC_MEM_CFG,
	.ops = &msi_ops,
};

static struct clk clk_sys = {
	.parent = &clk_pll3,
	.regofs = SIRFSOC_CLKC_SYS_CFG,
	.ops = &msi_ops,
};

static struct clk clk_io = {
	.parent = &clk_pll3,
	.regofs = SIRFSOC_CLKC_IO_CFG,
	.ops = &msi_ops,
};

/*
 * on-chip clock sets
 */
static struct clk_lookup onchip_clks[] = {
	{
		.dev_id = "rtc",
		.clk = &clk_rtc,
	}, {
		.dev_id = "osc",
		.clk = &clk_osc,
	}, {
		.dev_id = "pll1",
		.clk = &clk_pll1,
	}, {
		.dev_id = "pll2",
		.clk = &clk_pll2,
	}, {
		.dev_id = "pll3",
		.clk = &clk_pll3,
	}, {
		.dev_id = "cpu",
		.clk = &clk_cpu,
	}, {
		.dev_id = "mem",
		.clk = &clk_mem,
	}, {
		.dev_id = "sys",
		.clk = &clk_sys,
	}, {
		.dev_id = "io",
		.clk = &clk_io,
	},
};

int clk_enable(struct clk *clk)
{
	unsigned long flags;

	if (unlikely(IS_ERR_OR_NULL(clk)))
		return -EINVAL;

	if (clk->parent)
		clk_enable(clk->parent);

	spin_lock_irqsave(&clocks_lock, flags);
	if (!clk->usage++ && clk->ops && clk->ops->enable)
		clk->ops->enable(clk);
	spin_unlock_irqrestore(&clocks_lock, flags);
	return 0;
}
EXPORT_SYMBOL(clk_enable);

void clk_disable(struct clk *clk)
{
	unsigned long flags;

	if (unlikely(IS_ERR_OR_NULL(clk)))
		return;

	WARN_ON(!clk->usage);

	spin_lock_irqsave(&clocks_lock, flags);
	if (--clk->usage == 0 && clk->ops && clk->ops->disable)
		clk->ops->disable(clk);
	spin_unlock_irqrestore(&clocks_lock, flags);

	if (clk->parent)
		clk_disable(clk->parent);
}
EXPORT_SYMBOL(clk_disable);

unsigned long clk_get_rate(struct clk *clk)
{
	if (unlikely(IS_ERR_OR_NULL(clk)))
		return 0;

	if (clk->rate)
		return clk->rate;

	if (clk->ops && clk->ops->get_rate)
		return clk->ops->get_rate(clk);

	return clk_get_rate(clk->parent);
}
EXPORT_SYMBOL(clk_get_rate);

long clk_round_rate(struct clk *clk, unsigned long rate)
{
	if (unlikely(IS_ERR_OR_NULL(clk)))
		return 0;

	if (clk->ops && clk->ops->round_rate)
		return clk->ops->round_rate(clk, rate);

	return 0;
}
EXPORT_SYMBOL(clk_round_rate);

int clk_set_rate(struct clk *clk, unsigned long rate)
{
	if (unlikely(IS_ERR_OR_NULL(clk)))
		return -EINVAL;

	if (!clk->ops || !clk->ops->set_rate)
		return -EINVAL;

	return clk->ops->set_rate(clk, rate);
}
EXPORT_SYMBOL(clk_set_rate);

int clk_set_parent(struct clk *clk, struct clk *parent)
{
	int ret;
	unsigned long flags;

	if (unlikely(IS_ERR_OR_NULL(clk)))
		return -EINVAL;

	if (!clk->ops || !clk->ops->set_parent)
		return -EINVAL;

	spin_lock_irqsave(&clocks_lock, flags);
	ret = clk->ops->set_parent(clk, parent);
	if (!ret) {
		parent->usage += clk->usage;
		clk->parent->usage -= clk->usage;
		BUG_ON(clk->parent->usage < 0);
		clk->parent = parent;
	}
	spin_unlock_irqrestore(&clocks_lock, flags);
	return ret;
}
EXPORT_SYMBOL(clk_set_parent);

struct clk *clk_get_parent(struct clk *clk)
{
	unsigned long flags;

	if (unlikely(IS_ERR_OR_NULL(clk)))
		return NULL;

	if (!clk->ops || !clk->ops->get_parent)
		return clk->parent;

	spin_lock_irqsave(&clocks_lock, flags);
	clk->parent = clk->ops->get_parent(clk);
	spin_unlock_irqrestore(&clocks_lock, flags);
	return clk->parent;
}
EXPORT_SYMBOL(clk_get_parent);

static void __init sirfsoc_clk_init(void)
{
	clkdev_add_table(onchip_clks, ARRAY_SIZE(onchip_clks));
}

static struct of_device_id clkc_ids[] = {
	{ .compatible = "sirf,prima2-clkc" },
	{},
};

void __init sirfsoc_of_clk_init(void)
{
	struct device_node *np;
	struct resource res;
	struct map_desc sirfsoc_clkc_iodesc = {
		.virtual = SIRFSOC_CLOCK_VA_BASE,
		.type    = MT_DEVICE,
	};

	np = of_find_matching_node(NULL, clkc_ids);
	if (!np)
		panic("unable to find compatible clkc node in dtb\n");

	if (of_address_to_resource(np, 0, &res))
		panic("unable to find clkc range in dtb");
	of_node_put(np);

	sirfsoc_clkc_iodesc.pfn = __phys_to_pfn(res.start);
	sirfsoc_clkc_iodesc.length = 1 + res.end - res.start;

	iotable_init(&sirfsoc_clkc_iodesc, 1);

	sirfsoc_clk_init();
}
+0 −1
Original line number Diff line number Diff line
@@ -38,7 +38,6 @@ static const char *prima2cb_dt_match[] __initdata = {
MACHINE_START(PRIMA2_EVB, "prima2cb")
	/* Maintainer: Barry Song <baohua.song@csr.com> */
	.atag_offset	= 0x100,
	.init_early     = sirfsoc_of_clk_init,
	.map_io         = sirfsoc_map_lluart,
	.init_irq	= sirfsoc_of_irq_init,
	.timer		= &sirfsoc_timer,
Loading