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

Commit ea01d31a authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'imx-common-clk' of git://git.pengutronix.de/git/imx/linux-2.6 into next/clock

Sascha Hauer <s.hauer@pengutronix.de> writes:

 ARM i.MX common clock framework support

 Same as with Shawns series this one depends on:
   git://git.linaro.org/people/mturquette/linux.git clk-next
   http://ftp.arm.linux.org.uk/pub/linux/arm/kernel/git-cur/linux-arm.git clkdev

* tag 'imx-common-clk' of git://git.pengutronix.de/git/imx/linux-2.6

: (34 commits)
  ARM i.MX: remove now unused clock files
  ARM: i.MX6: implement clocks using common clock framework
  ARM i.MX35: implement clocks using common clock framework
  ARM i.MX5: implement clocks using common clock framework
  ARM i.MX31: implement clocks using common clock framework
  ARM i.MX27: implement clocks using common clock framework
  ARM i.MX21: implement clocks using common clock framework
  ARM i.MX1: implement clocks using common clock framework
  ARM i.MX25: implement clocks using common clock framework
  ARM: imx: add common clock support for clk busy
  ARM: imx: add common clock support for pfd
  ARM i.MX: Add common clock support for 2bit gate
  ARM: imx: add common clock support for pllv3
  ARM i.MX: Add common clock support for pllv2
  ARM i.MX: Add common clock support for pllv1
  ARM i.MX: prepare for common clock framework
  ARM i.MX3: Make ccm base address a variable
  ARM i.MX timer: request correct clock
  ARM i.MX5: prepare gpc_dvfs_clk
  rtc: imx dryice: Add missing clk_prepare
  ...

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents e12ff344 c818f97b
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ config ARCH_MX53
config SOC_IMX1
	bool
	select ARCH_MX1
	select COMMON_CLK
	select CPU_ARM920T
	select IMX_HAVE_IOMUX_V1
	select MXC_AVIC
@@ -42,12 +43,14 @@ config SOC_IMX21
	bool
	select MACH_MX21
	select CPU_ARM926T
	select COMMON_CLK
	select IMX_HAVE_IOMUX_V1
	select MXC_AVIC

config SOC_IMX25
	bool
	select ARCH_MX25
	select COMMON_CLK
	select CPU_ARM926T
	select ARCH_MXC_IOMUX_V3
	select MXC_AVIC
@@ -56,6 +59,7 @@ config SOC_IMX27
	bool
	select MACH_MX27
	select CPU_ARM926T
	select COMMON_CLK
	select IMX_HAVE_IOMUX_V1
	select MXC_AVIC

@@ -64,12 +68,14 @@ config SOC_IMX31
	select CPU_V6
	select IMX_HAVE_PLATFORM_MXC_RNGA
	select MXC_AVIC
	select COMMON_CLK
	select SMP_ON_UP if SMP

config SOC_IMX35
	bool
	select CPU_V6
	select ARCH_MXC_IOMUX_V3
	select COMMON_CLK
	select HAVE_EPIT
	select MXC_AVIC
	select SMP_ON_UP if SMP
@@ -77,6 +83,7 @@ config SOC_IMX35
config SOC_IMX5
	select CPU_V7
	select MXC_TZIC
	select COMMON_CLK
	select ARCH_MXC_IOMUX_V3
	select ARCH_HAS_CPUFREQ
	select ARCH_MX5
@@ -836,6 +843,7 @@ config SOC_IMX6Q
	bool "i.MX6 Quad support"
	select ARM_CPU_SUSPEND if PM
	select ARM_GIC
	select COMMON_CLK
	select CPU_V7
	select HAVE_ARM_SCU
	select HAVE_IMX_GPC
+11 −8
Original line number Diff line number Diff line
obj-$(CONFIG_SOC_IMX1) += clock-imx1.o mm-imx1.o
obj-$(CONFIG_SOC_IMX21) += clock-imx21.o mm-imx21.o
obj-$(CONFIG_SOC_IMX1) += clk-imx1.o mm-imx1.o
obj-$(CONFIG_SOC_IMX21) += clk-imx21.o mm-imx21.o

obj-$(CONFIG_SOC_IMX25) += clock-imx25.o mm-imx25.o ehci-imx25.o cpu-imx25.o
obj-$(CONFIG_SOC_IMX25) += clk-imx25.o mm-imx25.o ehci-imx25.o cpu-imx25.o

obj-$(CONFIG_SOC_IMX27) += cpu-imx27.o pm-imx27.o
obj-$(CONFIG_SOC_IMX27) += clock-imx27.o mm-imx27.o ehci-imx27.o
obj-$(CONFIG_SOC_IMX27) += clk-imx27.o mm-imx27.o ehci-imx27.o

obj-$(CONFIG_SOC_IMX31) += mm-imx3.o cpu-imx31.o clock-imx31.o iomux-imx31.o ehci-imx31.o pm-imx3.o
obj-$(CONFIG_SOC_IMX35) += mm-imx3.o cpu-imx35.o clock-imx35.o ehci-imx35.o pm-imx3.o
obj-$(CONFIG_SOC_IMX31) += mm-imx3.o cpu-imx31.o clk-imx31.o iomux-imx31.o ehci-imx31.o pm-imx3.o
obj-$(CONFIG_SOC_IMX35) += mm-imx3.o cpu-imx35.o clk-imx35.o ehci-imx35.o pm-imx3.o

obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clock-mx51-mx53.o ehci-imx5.o pm-imx5.o cpu_op-mx51.o
obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clk-imx51-imx53.o ehci-imx5.o pm-imx5.o cpu_op-mx51.o

obj-$(CONFIG_COMMON_CLK) += clk-pllv1.o clk-pllv2.o clk-pllv3.o clk-gate2.o \
			    clk-pfd.o clk-busy.o

# Support for CMOS sensor interface
obj-$(CONFIG_MX1_VIDEO) += mx1-camera-fiq.o mx1-camera-fiq-ksym.o
@@ -70,7 +73,7 @@ obj-$(CONFIG_CPU_V7) += head-v7.o
AFLAGS_head-v7.o :=-Wa,-march=armv7-a
obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_SOC_IMX6Q) += clock-imx6q.o mach-imx6q.o
obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o

ifeq ($(CONFIG_PM),y)
obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o
+189 −0
Original line number Diff line number Diff line
/*
 * Copyright 2012 Freescale Semiconductor, Inc.
 * Copyright 2012 Linaro Ltd.
 *
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/err.h>
#include "clk.h"

static int clk_busy_wait(void __iomem *reg, u8 shift)
{
	unsigned long timeout = jiffies + msecs_to_jiffies(10);

	while (readl_relaxed(reg) & (1 << shift))
		if (time_after(jiffies, timeout))
			return -ETIMEDOUT;

	return 0;
}

struct clk_busy_divider {
	struct clk_divider div;
	const struct clk_ops *div_ops;
	void __iomem *reg;
	u8 shift;
};

static inline struct clk_busy_divider *to_clk_busy_divider(struct clk_hw *hw)
{
	struct clk_divider *div = container_of(hw, struct clk_divider, hw);

	return container_of(div, struct clk_busy_divider, div);
}

static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw,
						  unsigned long parent_rate)
{
	struct clk_busy_divider *busy = to_clk_busy_divider(hw);

	return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate);
}

static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate,
					unsigned long *prate)
{
	struct clk_busy_divider *busy = to_clk_busy_divider(hw);

	return busy->div_ops->round_rate(&busy->div.hw, rate, prate);
}

static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate,
		unsigned long parent_rate)
{
	struct clk_busy_divider *busy = to_clk_busy_divider(hw);
	int ret;

	ret = busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate);
	if (!ret)
		ret = clk_busy_wait(busy->reg, busy->shift);

	return ret;
}

static struct clk_ops clk_busy_divider_ops = {
	.recalc_rate = clk_busy_divider_recalc_rate,
	.round_rate = clk_busy_divider_round_rate,
	.set_rate = clk_busy_divider_set_rate,
};

struct clk *imx_clk_busy_divider(const char *name, const char *parent_name,
				 void __iomem *reg, u8 shift, u8 width,
				 void __iomem *busy_reg, u8 busy_shift)
{
	struct clk_busy_divider *busy;
	struct clk *clk;
	struct clk_init_data init;

	busy = kzalloc(sizeof(*busy), GFP_KERNEL);
	if (!busy)
		return ERR_PTR(-ENOMEM);

	busy->reg = busy_reg;
	busy->shift = busy_shift;

	busy->div.reg = reg;
	busy->div.shift = shift;
	busy->div.width = width;
	busy->div.lock = &imx_ccm_lock;
	busy->div_ops = &clk_divider_ops;

	init.name = name;
	init.ops = &clk_busy_divider_ops;
	init.flags = CLK_SET_RATE_PARENT;
	init.parent_names = &parent_name;
	init.num_parents = 1;

	busy->div.hw.init = &init;

	clk = clk_register(NULL, &busy->div.hw);
	if (!clk)
		kfree(busy);

	return clk;
}

struct clk_busy_mux {
	struct clk_mux mux;
	const struct clk_ops *mux_ops;
	void __iomem *reg;
	u8 shift;
};

static inline struct clk_busy_mux *to_clk_busy_mux(struct clk_hw *hw)
{
	struct clk_mux *mux = container_of(hw, struct clk_mux, hw);

	return container_of(mux, struct clk_busy_mux, mux);
}

static u8 clk_busy_mux_get_parent(struct clk_hw *hw)
{
	struct clk_busy_mux *busy = to_clk_busy_mux(hw);

	return busy->mux_ops->get_parent(&busy->mux.hw);
}

static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index)
{
	struct clk_busy_mux *busy = to_clk_busy_mux(hw);
	int ret;

	ret = busy->mux_ops->set_parent(&busy->mux.hw, index);
	if (!ret)
		ret = clk_busy_wait(busy->reg, busy->shift);

	return ret;
}

struct clk_ops clk_busy_mux_ops = {
	.get_parent = clk_busy_mux_get_parent,
	.set_parent = clk_busy_mux_set_parent,
};

struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
			     u8 width, void __iomem *busy_reg, u8 busy_shift,
			     const char **parent_names, int num_parents)
{
	struct clk_busy_mux *busy;
	struct clk *clk;
	struct clk_init_data init;

	busy = kzalloc(sizeof(*busy), GFP_KERNEL);
	if (!busy)
		return ERR_PTR(-ENOMEM);

	busy->reg = busy_reg;
	busy->shift = busy_shift;

	busy->mux.reg = reg;
	busy->mux.shift = shift;
	busy->mux.width = width;
	busy->mux.lock = &imx_ccm_lock;
	busy->mux_ops = &clk_mux_ops;

	init.name = name;
	init.ops = &clk_busy_mux_ops;
	init.flags = 0;
	init.parent_names = parent_names;
	init.num_parents = num_parents;

	busy->mux.hw.init = &init;

	clk = clk_register(NULL, &busy->mux.hw);
	if (IS_ERR(clk))
		kfree(busy);

	return clk;
}
+118 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Gated clock implementation
 */

#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/string.h>

/**
 * DOC: basic gatable clock which can gate and ungate it's ouput
 *
 * Traits of this clock:
 * prepare - clk_(un)prepare only ensures parent is (un)prepared
 * enable - clk_enable and clk_disable are functional & control gating
 * rate - inherits rate from parent.  No clk_set_rate support
 * parent - fixed parent.  No clk_set_parent support
 */

#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)

static int clk_gate2_enable(struct clk_hw *hw)
{
	struct clk_gate *gate = to_clk_gate(hw);
	u32 reg;
	unsigned long flags = 0;

	if (gate->lock)
		spin_lock_irqsave(gate->lock, flags);

	reg = readl(gate->reg);
	reg |= 3 << gate->bit_idx;
	writel(reg, gate->reg);

	if (gate->lock)
		spin_unlock_irqrestore(gate->lock, flags);

	return 0;
}

static void clk_gate2_disable(struct clk_hw *hw)
{
	struct clk_gate *gate = to_clk_gate(hw);
	u32 reg;
	unsigned long flags = 0;

	if (gate->lock)
		spin_lock_irqsave(gate->lock, flags);

	reg = readl(gate->reg);
	reg &= ~(3 << gate->bit_idx);
	writel(reg, gate->reg);

	if (gate->lock)
		spin_unlock_irqrestore(gate->lock, flags);
}

static int clk_gate2_is_enabled(struct clk_hw *hw)
{
	u32 reg;
	struct clk_gate *gate = to_clk_gate(hw);

	reg = readl(gate->reg);

	if (((reg >> gate->bit_idx) & 3) == 3)
		return 1;

	return 0;
}

static struct clk_ops clk_gate2_ops = {
	.enable = clk_gate2_enable,
	.disable = clk_gate2_disable,
	.is_enabled = clk_gate2_is_enabled,
};

struct clk *clk_register_gate2(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		void __iomem *reg, u8 bit_idx,
		u8 clk_gate2_flags, spinlock_t *lock)
{
	struct clk_gate *gate;
	struct clk *clk;
	struct clk_init_data init;

	gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
	if (!gate)
		return ERR_PTR(-ENOMEM);

	/* struct clk_gate assignments */
	gate->reg = reg;
	gate->bit_idx = bit_idx;
	gate->flags = clk_gate2_flags;
	gate->lock = lock;

	init.name = name;
	init.ops = &clk_gate2_ops;
	init.flags = flags;
	init.parent_names = parent_name ? &parent_name : NULL;
	init.num_parents = parent_name ? 1 : 0;

	gate->hw.init = &init;

	clk = clk_register(dev, &gate->hw);
	if (IS_ERR(clk))
		kfree(clk);

	return clk;
}
+115 −0
Original line number Diff line number Diff line
/*
 *  Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/clkdev.h>
#include <linux/err.h>

#include <mach/hardware.h>
#include <mach/common.h>
#include "clk.h"

/* CCM register addresses */
#define IO_ADDR_CCM(off)	(MX1_IO_ADDRESS(MX1_CCM_BASE_ADDR + (off)))

#define CCM_CSCR	IO_ADDR_CCM(0x0)
#define CCM_MPCTL0	IO_ADDR_CCM(0x4)
#define CCM_SPCTL0	IO_ADDR_CCM(0xc)
#define CCM_PCDR	IO_ADDR_CCM(0x20)

/* SCM register addresses */
#define IO_ADDR_SCM(off)	(MX1_IO_ADDRESS(MX1_SCM_BASE_ADDR + (off)))

#define SCM_GCCR	IO_ADDR_SCM(0xc)

static const char *prem_sel_clks[] = { "clk32_premult", "clk16m", };
static const char *clko_sel_clks[] = { "per1", "hclk", "clk48m", "clk16m", "prem",
				"fclk", };
enum imx1_clks {
	dummy, clk32, clk16m_ext, clk16m, clk32_premult, prem, mpll, spll, mcu,
	fclk, hclk, clk48m, per1, per2, per3, clko, dma_gate, csi_gate,
	mma_gate, usbd_gate, clk_max
};

static struct clk *clk[clk_max];

int __init mx1_clocks_init(unsigned long fref)
{
	int i;

	clk[dummy] = imx_clk_fixed("dummy", 0);
	clk[clk32] = imx_clk_fixed("clk32", fref);
	clk[clk16m_ext] = imx_clk_fixed("clk16m_ext", 16000000);
	clk[clk16m] = imx_clk_gate("clk16m", "clk16m_ext", CCM_CSCR, 17);
	clk[clk32_premult] = imx_clk_fixed_factor("clk32_premult", "clk32", 512, 1);
	clk[prem] = imx_clk_mux("prem", CCM_CSCR, 16, 1, prem_sel_clks,
			ARRAY_SIZE(prem_sel_clks));
	clk[mpll] = imx_clk_pllv1("mpll", "clk32_premult", CCM_MPCTL0);
	clk[spll] = imx_clk_pllv1("spll", "prem", CCM_SPCTL0);
	clk[mcu] = imx_clk_divider("mcu", "clk32_premult", CCM_CSCR, 15, 1);
	clk[fclk] = imx_clk_divider("fclk", "mpll", CCM_CSCR, 15, 1);
	clk[hclk] = imx_clk_divider("hclk", "spll", CCM_CSCR, 10, 4);
	clk[clk48m] = imx_clk_divider("clk48m", "spll", CCM_CSCR, 26, 3);
	clk[per1] = imx_clk_divider("per1", "spll", CCM_PCDR, 0, 4);
	clk[per2] = imx_clk_divider("per2", "spll", CCM_PCDR, 4, 4);
	clk[per3] = imx_clk_divider("per3", "spll", CCM_PCDR, 16, 7);
	clk[clko] = imx_clk_mux("clko", CCM_CSCR, 29, 3, clko_sel_clks,
			ARRAY_SIZE(clko_sel_clks));
	clk[dma_gate] = imx_clk_gate("dma_gate", "hclk", SCM_GCCR, 4);
	clk[csi_gate] = imx_clk_gate("csi_gate", "hclk", SCM_GCCR, 2);
	clk[mma_gate] = imx_clk_gate("mma_gate", "hclk", SCM_GCCR, 1);
	clk[usbd_gate] = imx_clk_gate("usbd_gate", "clk48m", SCM_GCCR, 0);

	for (i = 0; i < ARRAY_SIZE(clk); i++)
		if (IS_ERR(clk[i]))
			pr_err("imx1 clk %d: register failed with %ld\n",
				i, PTR_ERR(clk[i]));

	clk_register_clkdev(clk[dma_gate], "ahb", "imx-dma");
	clk_register_clkdev(clk[csi_gate], NULL, "mx1-camera.0");
	clk_register_clkdev(clk[mma_gate], "mma", NULL);
	clk_register_clkdev(clk[usbd_gate], NULL, "imx_udc.0");
	clk_register_clkdev(clk[per1], "per", "imx-gpt.0");
	clk_register_clkdev(clk[hclk], "ipg", "imx-gpt.0");
	clk_register_clkdev(clk[per1], "per", "imx1-uart.0");
	clk_register_clkdev(clk[hclk], "ipg", "imx1-uart.0");
	clk_register_clkdev(clk[per1], "per", "imx1-uart.1");
	clk_register_clkdev(clk[hclk], "ipg", "imx1-uart.1");
	clk_register_clkdev(clk[per1], "per", "imx1-uart.2");
	clk_register_clkdev(clk[hclk], "ipg", "imx1-uart.2");
	clk_register_clkdev(clk[hclk], NULL, "imx-i2c.0");
	clk_register_clkdev(clk[per2], "per", "imx1-cspi.0");
	clk_register_clkdev(clk[dummy], "ipg", "imx1-cspi.0");
	clk_register_clkdev(clk[per2], "per", "imx1-cspi.1");
	clk_register_clkdev(clk[dummy], "ipg", "imx1-cspi.1");
	clk_register_clkdev(clk[per2], NULL, "imx-mmc.0");
	clk_register_clkdev(clk[per2], "per", "imx-fb.0");
	clk_register_clkdev(clk[dummy], "ipg", "imx-fb.0");
	clk_register_clkdev(clk[dummy], "ahb", "imx-fb.0");
	clk_register_clkdev(clk[hclk], "mshc", NULL);
	clk_register_clkdev(clk[per3], "ssi", NULL);
	clk_register_clkdev(clk[clk32], NULL, "mxc_rtc.0");
	clk_register_clkdev(clk[clko], "clko", NULL);

	mxc_timer_init(NULL, MX1_IO_ADDRESS(MX1_TIM1_BASE_ADDR),
			MX1_TIM1_INT);

	return 0;
}
Loading