All of lore.kernel.org
 help / color / mirror / Atom feed
From: Domen Puncer <domen.puncer@telargo.com>
To: linuxppc-dev@ozlabs.org
Cc: David Brownell <david-b@pacbell.net>, Sylvain Munaut <tnt@246tNt.com>
Subject: [PATCH 2/3] mpc52xx clk.h interface
Date: Wed, 11 Jul 2007 11:33:18 +0200	[thread overview]
Message-ID: <20070711093318.GG4375@moe.telargo.com> (raw)
In-Reply-To: <20070711093113.GE4375@moe.telargo.com>

clk.h interface for mpc52xx
Currently only psc_mclks are defined.


Signed-off-by: Domen Puncer <domen.puncer@telargo.com>

---
 arch/powerpc/platforms/52xx/Makefile         |    2 
 arch/powerpc/platforms/52xx/mpc52xx_clock.c  |  352 +++++++++++++++++++++++++++
 arch/powerpc/platforms/52xx/mpc52xx_common.c |    3 
 include/asm-powerpc/mpc52xx.h                |    2 
 4 files changed, 358 insertions(+), 1 deletion(-)

Index: work-powerpc.git/arch/powerpc/platforms/52xx/Makefile
===================================================================
--- work-powerpc.git.orig/arch/powerpc/platforms/52xx/Makefile
+++ work-powerpc.git/arch/powerpc/platforms/52xx/Makefile
@@ -2,7 +2,7 @@
 # Makefile for 52xx based boards
 #
 ifeq ($(CONFIG_PPC_MERGE),y)
-obj-y				+= mpc52xx_pic.o mpc52xx_common.o
+obj-y				+= mpc52xx_pic.o mpc52xx_common.o mpc52xx_clock.o
 obj-$(CONFIG_PCI)		+= mpc52xx_pci.o
 endif
 
Index: work-powerpc.git/arch/powerpc/platforms/52xx/mpc52xx_clock.c
===================================================================
--- /dev/null
+++ work-powerpc.git/arch/powerpc/platforms/52xx/mpc52xx_clock.c
@@ -0,0 +1,352 @@
+/*
+ * arch/powerpc/platforms/52xx/mpc52xx_clock.c
+ * based on linux/arch/arm/mach-at91/clock.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+#include <asm/clk_interface.h>
+
+
+struct clk {
+	struct list_head node;
+	const char	*name;		/* unique clock name */
+	struct device_node *of_node;	/* device node associated with this clock */
+	const char	*function;	/* clock function */
+	unsigned long	rate_hz;
+	struct clk	*parent;
+	void		(*mode)(struct clk *, int);
+	u16		users;
+	int		cdm_id;		/* == bit number in datasheet, or 0 */
+	long		(*set_rate)(struct clk *, unsigned long rate, int round_only);
+};
+
+#define CDM_ID_PSC3 24
+#define CDM_ID_PSC2 25
+#define CDM_ID_PSC1 26
+#define CDM_ID_PSC6 27
+
+static long psc_set_rate(struct clk *clk, unsigned long rate, int round_only);
+
+static struct clk clk_system = {
+	.name		= "system",
+	.rate_hz	= 528000000,	/* default if there's no system-frequency in dts */
+	.users		= 1,		/* always on */
+};
+static struct clk clk_psc1 = {
+	.name		= "psc1_mclk",
+	.cdm_id		= CDM_ID_PSC1,
+	.parent		= &clk_system,
+	.set_rate	= psc_set_rate,
+};
+static struct clk clk_psc2 = {
+	.name		= "psc2_mclk",
+	.cdm_id		= CDM_ID_PSC2,
+	.parent		= &clk_system,
+	.set_rate	= psc_set_rate,
+};
+static struct clk clk_psc3 = {
+	.name		= "psc3_mclk",
+	.cdm_id		= CDM_ID_PSC3,
+	.parent		= &clk_system,
+	.set_rate	= psc_set_rate,
+};
+static struct clk clk_psc6 = {
+	.name		= "psc6_mclk",
+	.cdm_id		= CDM_ID_PSC6,
+	.parent		= &clk_system,
+	.set_rate	= psc_set_rate,
+};
+
+
+
+static LIST_HEAD(clocks);
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct mpc52xx_cdm __iomem *cdm;
+static DEFINE_SPINLOCK(cdm_lock);
+
+
+static long psc_set_rate(struct clk *clk, unsigned long rate, int round_only)
+{
+	u16 mclkdiv;
+	u16 __iomem *divreg;
+
+	/* pick a divider that will get the closest clock */
+	mclkdiv = (clk->parent->rate_hz + rate/2) / rate - 1;
+
+	/* trim to closest possible. or should we return an error? */
+	mclkdiv = min(mclkdiv, (u16)0x1ff);
+	mclkdiv = max(mclkdiv, (u16)1);
+
+	rate = clk->parent->rate_hz / (mclkdiv + 1);
+	mclkdiv |= 0x8000;	/* enable (this is not clk_enable!) */
+
+	if (round_only)
+		return rate;
+
+	if (clk->cdm_id == CDM_ID_PSC1)
+		divreg = &cdm->mclken_div_psc1;
+	else if (clk->cdm_id == CDM_ID_PSC2)
+		divreg = &cdm->mclken_div_psc2;
+	else if (clk->cdm_id == CDM_ID_PSC3)
+		divreg = &cdm->mclken_div_psc3;
+	else if (clk->cdm_id == CDM_ID_PSC6)
+		divreg = &cdm->mclken_div_psc6;
+	else
+		return -ENODEV;
+
+	out_be16(divreg, mclkdiv);
+
+	return 0;
+}
+
+/* clocks cannot be de-registered no refcounting necessary */
+static struct clk *mpc52xx_clk_get(struct device *dev, const char *id)
+{
+	struct clk *clk;
+
+	list_for_each_entry(clk, &clocks, node) {
+		if (strcmp(id, clk->name) == 0)
+			return clk;
+		if (clk->function && strcmp(id, clk->function) == 0 && dev &&
+				dev->archdata.of_node == clk->of_node)
+			return clk;
+	}
+
+	return ERR_PTR(-ENOENT);
+}
+
+static void mpc52xx_clock_associate(const char *id, struct device_node *of_node,
+		const char *function)
+{
+	struct clk *clk = mpc52xx_clk_get(NULL, id);
+
+	if (IS_ERR(clk))
+		return;
+
+	clk->function = function;
+	clk->of_node = of_node;
+}
+
+static void mpc52xx_clk_put(struct clk *clk)
+{
+}
+
+
+static void clk_mode_cdm(int cdm_id, int enabled)
+{
+	unsigned long flags;
+	u32 clk_enables;
+
+	if (cdm_id < 12 || cdm_id > 31) {
+		printk(KERN_ERR "%s: %i invalid cdm_id: %i\n", __func__, __LINE__, cdm_id);
+		return;
+	}
+
+	spin_lock_irqsave(&cdm_lock, flags);
+	clk_enables = in_be32(&cdm->clk_enables);
+	if (enabled)
+		clk_enables |= 1 << (31-cdm_id);
+	else
+		clk_enables &= ~(1 << (31-cdm_id));
+
+	out_be32(&cdm->clk_enables, clk_enables);
+	spin_unlock_irqrestore(&cdm_lock, flags);
+}
+
+static void __clk_enable(struct clk *clk)
+{
+	if (clk->parent)
+		__clk_enable(clk->parent);
+	if (clk->users++ == 0 && clk->mode) {
+		if (clk->cdm_id)
+			clk_mode_cdm(clk->cdm_id, 1);
+		else
+			clk->mode(clk, 1);
+	}
+}
+
+static int mpc52xx_clk_enable(struct clk *clk)
+{
+	unsigned long	flags;
+
+	spin_lock_irqsave(&clk_lock, flags);
+	__clk_enable(clk);
+	spin_unlock_irqrestore(&clk_lock, flags);
+	return 0;
+}
+
+static void __clk_disable(struct clk *clk)
+{
+	BUG_ON(clk->users == 0);
+	if (--clk->users == 0 && clk->mode) {
+		if (clk->cdm_id)
+			clk_mode_cdm(clk->cdm_id, 0);
+		else
+			clk->mode(clk, 0);
+	}
+	if (clk->parent)
+		__clk_disable(clk->parent);
+}
+
+static void mpc52xx_clk_disable(struct clk *clk)
+{
+	unsigned long	flags;
+
+	spin_lock_irqsave(&clk_lock, flags);
+	__clk_disable(clk);
+	spin_unlock_irqrestore(&clk_lock, flags);
+}
+
+static unsigned long mpc52xx_clk_get_rate(struct clk *clk)
+{
+	unsigned long	flags;
+	unsigned long	rate;
+
+	spin_lock_irqsave(&clk_lock, flags);
+	for (;;) {
+		rate = clk->rate_hz;
+		if (rate || !clk->parent)
+			break;
+		clk = clk->parent;
+	}
+	spin_unlock_irqrestore(&clk_lock, flags);
+	return rate;
+}
+
+static long mpc52xx_clk_round_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long flags;
+	long ret;
+
+	if (!clk->set_rate)
+		return -EINVAL;
+
+	spin_lock_irqsave(&clk_lock, flags);
+	ret = clk->set_rate(clk, rate, 1);
+	spin_unlock_irqrestore(&clk_lock, flags);
+
+	return ret;
+}
+
+static int mpc52xx_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long flags;
+	int ret;
+
+	if (!clk->set_rate)
+		return -EINVAL;
+	if (clk->users)
+		return -EBUSY;
+
+	spin_lock_irqsave(&clk_lock, flags);
+	clk->rate_hz = clk->set_rate(clk, rate, 1);
+	ret = clk->set_rate(clk, rate, 0);
+	spin_unlock_irqrestore(&clk_lock, flags);
+
+	return ret;
+}
+
+static struct clk *mpc52xx_clk_get_parent(struct clk *clk)
+{
+	return clk->parent;
+}
+
+static int mpc52xx_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	unsigned long	flags;
+
+	if (clk->users)
+		return -EBUSY;
+	spin_lock_irqsave(&clk_lock, flags);
+
+	clk->rate_hz = parent->rate_hz;
+	clk->parent = parent;
+
+	spin_unlock_irqrestore(&clk_lock, flags);
+	return 0;
+}
+
+
+
+static struct clk *const mpc5200_clocks[] __initdata = {
+	&clk_system,
+	&clk_psc1,
+	&clk_psc2,
+	&clk_psc3,
+	&clk_psc6,
+};
+
+int __init mpc52xx_clock_init(void)
+{
+	int i;
+	struct device_node *np, *child = NULL;
+	const unsigned int *fp;
+
+	/* map Clock Distribution Module registers */
+	cdm = mpc52xx_find_and_map("mpc5200-cdm");
+	if (!cdm) {
+		printk(KERN_ERR "%s: %i couldn't map mpc5200-cdm\n", __func__, __LINE__);
+		return -EFAULT;
+	}
+
+	/* register clocks */
+	for (i = 0; i < ARRAY_SIZE(mpc5200_clocks); i++)
+		list_add_tail(&mpc5200_clocks[i]->node, &clocks);
+
+
+	/* get clk_system rate from device tree */
+	np = of_find_node_by_type(NULL, "soc");
+	if (!np)
+		return 0;
+
+	fp = of_get_property(np, "system-frequency", NULL);
+	if (fp && *fp)
+		clk_system.rate_hz = *fp;
+
+	/* associate psc_mclks with device_nodes */
+	while ((child = of_get_next_child(np, child))) {
+		if (of_device_is_compatible(child, "mpc5200-psc")) {
+			char clock[10];
+			int ci = -1;
+			const int *pci;
+
+			pci = of_get_property(child, "cell-index", NULL);
+			if (pci)
+				ci = *pci;
+			if (ci < 0 || ci > 5 || ci == 3 || ci == 4) {
+				printk(KERN_ALERT "%s: %i psc node '%s' has invalid "
+						"cell-index: %i\n", __func__, __LINE__,
+						child->name, ci);
+				continue;
+			}
+
+			snprintf(clock, 10, "psc%i_mclk", ci + 1);
+			mpc52xx_clock_associate(clock, child, "psc_mclk");
+		}
+	}
+	of_node_put(np);
+
+	/* register clocks */
+	clk_functions = (struct clk_interface) {
+		.clk_get	= mpc52xx_clk_get,
+		.clk_enable	= mpc52xx_clk_enable,
+		.clk_disable	= mpc52xx_clk_disable,
+		.clk_get_rate	= mpc52xx_clk_get_rate,
+		.clk_put	= mpc52xx_clk_put,
+		.clk_round_rate	= mpc52xx_clk_round_rate,
+		.clk_set_rate	= mpc52xx_clk_set_rate,
+		.clk_set_parent	= mpc52xx_clk_set_parent,
+		.clk_get_parent	= mpc52xx_clk_get_parent,
+	};
+	return 0;
+}
Index: work-powerpc.git/arch/powerpc/platforms/52xx/mpc52xx_common.c
===================================================================
--- work-powerpc.git.orig/arch/powerpc/platforms/52xx/mpc52xx_common.c
+++ work-powerpc.git/arch/powerpc/platforms/52xx/mpc52xx_common.c
@@ -110,6 +110,9 @@ mpc52xx_setup_cpu(void)
 	    transaction and re-enable it afterwards ...) */
 	out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_PLDIS);
 
+	/* setup clk system */
+	mpc52xx_clock_init();
+
 	/* Unmap zones */
 unmap_regs:
 	if (cdm) iounmap(cdm);
Index: work-powerpc.git/include/asm-powerpc/mpc52xx.h
===================================================================
--- work-powerpc.git.orig/include/asm-powerpc/mpc52xx.h
+++ work-powerpc.git/include/asm-powerpc/mpc52xx.h
@@ -274,5 +274,7 @@ extern char saved_sram[0x4000]; /* reuse
 #endif
 #endif /* CONFIG_PM */
 
+extern int __init mpc52xx_clock_init(void);
+
 #endif /* __ASM_POWERPC_MPC52xx_H__ */
 

  parent reply	other threads:[~2007-07-11  9:44 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-07-11  9:31 [PATCH 0/3] clock (clk.h) framework for mpc52xx Domen Puncer
2007-07-11  9:32 ` [PATCH 1/3] powerpc clk.h interface for platforms Domen Puncer
2007-07-11 10:36   ` Christoph Hellwig
2007-07-11 10:36     ` Christoph Hellwig
2007-07-11 15:56     ` David Brownell
2007-07-11 15:56       ` David Brownell
2007-07-11 16:16       ` Christoph Hellwig
2007-07-11 16:16         ` Christoph Hellwig
2007-07-11 17:02         ` David Brownell
2007-07-11 17:02           ` David Brownell
2007-07-11 20:34           ` Russell King
2007-07-11 20:34             ` Russell King
2007-07-11 20:52             ` David Brownell
2007-07-11 20:52               ` David Brownell
2007-07-13  9:12             ` Domen Puncer
2007-07-13  9:12               ` Domen Puncer
2007-08-01  7:28               ` Generic clk.h wrappers? [Was: Re: [PATCH 1/3] powerpc clk.h interface for platforms] Domen Puncer
2007-08-01  7:28                 ` Domen Puncer
2007-08-01 12:57                 ` Christoph Hellwig
2007-08-01 12:57                   ` Christoph Hellwig
2007-08-02 23:32                   ` David Brownell
2007-08-02 23:32                     ` David Brownell
2007-08-03  8:36                     ` Russell King
2007-08-03  8:36                       ` Russell King
2007-08-06  6:58   ` [PATCH 1/3] powerpc clk.h interface for platforms Domen Puncer
2007-09-19  3:47     ` Paul Mackerras
2007-09-19  5:11       ` Domen Puncer
2007-09-20  5:07         ` Paul Mackerras
2007-09-20 14:00           ` [PATCH 1/3 v2] " Domen Puncer
2007-07-11  9:33 ` Domen Puncer [this message]
2007-07-11  9:34 ` [PATCH 3/3] mpc52xx_psc_spi: use clk.h subsystem Domen Puncer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070711093318.GG4375@moe.telargo.com \
    --to=domen.puncer@telargo.com \
    --cc=david-b@pacbell.net \
    --cc=linuxppc-dev@ozlabs.org \
    --cc=tnt@246tNt.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.