[linux-yocto] [PATCH 30/70] powerpc/acp34xx: add clk_get/_rate support for acp board

Paul Butler butler.paul at gmail.com
Mon Jun 10 18:45:53 PDT 2013


From: Jiang Lu <lu.jiang at windriver.com>

Some drivers used by acp34xx depend on the clk_get/_rate methods
to get the device frequency. So implement these methods by checking
the "clock-frequency" property in the device node.

Signed-off-by: Jiang Lu <lu.jiang at windriver.com>
---
 arch/powerpc/platforms/44x/acpclock.c | 305 ++++++++++++++++++++++++++++++++++
 arch/powerpc/platforms/44x/acpclock.h |   6 +
 2 files changed, 311 insertions(+)
 create mode 100644 arch/powerpc/platforms/44x/acpclock.c
 create mode 100644 arch/powerpc/platforms/44x/acpclock.h

diff --git a/arch/powerpc/platforms/44x/acpclock.c b/arch/powerpc/platforms/44x/acpclock.c
new file mode 100644
index 0000000..71c13f8
--- /dev/null
+++ b/arch/powerpc/platforms/44x/acpclock.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2010-2012 Wind River Systems
+ *
+ * Some drivers like pl022 need clk interface. But now these drivers
+ * can work without it. So define a dummy clk driver for compile issue
+ * and future extend.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/kref.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+#include <asm/dcr-native.h>
+#include <asm/clk_interface.h>
+
+#define CLK_DEBUG
+
+#define CLK_REF0 125000000
+
+static unsigned char ps[] = {1, 3, 2, 4};
+
+static unsigned char idiv[] = {
+	1, 16, 17, 30, 13, 18, 7, 31, 14, 11, 19, 21, 27, 8, 23, 32,
+	15, 29, 12, 6, 10, 20, 26, 22, 28, 5, 9, 25, 4, 24, 3, 2};
+
+static unsigned char odiv[] = {
+	1, 2, 28, 27, 22, 21, 30, 29, 24, 23, 12, 11, 16, 15, 32, 31,
+	26, 25, 20, 19, 10, 9, 14, 13, 18, 17, 8, 7, 6, 5, 4, 3};
+
+static unsigned char mult[] = {
+	1, 123, 117, 251, 245, 69, 111, 125, 119, 95, 105, 197, 239,
+	163, 63, 253, 247, 187, 57, 223, 233, 207, 157, 71, 113, 15,
+	89, 37, 191, 19, 99, 127, 121, 109, 93, 61, 185, 155, 13, 97,
+	107, 11, 9, 81, 31, 49, 83, 199, 241, 33, 181, 143, 217, 173,
+	51, 165, 65, 85, 151, 147, 227, 41, 201, 255, 249, 243, 195,
+	237, 221, 231, 35, 189, 59, 183, 79, 29, 141, 215, 145, 225,
+	235, 219, 27, 139, 137, 135, 175, 209, 159, 53, 45, 177, 211,
+	23, 167, 73, 115, 67, 103, 161, 55, 205, 87, 17, 91, 153, 7,
+	47, 179, 171, 149, 39, 193, 229, 77, 213, 25, 133, 43, 21,
+	101, 203, 5, 169, 75, 131, 3, 129, 1, 250, 244, 124, 118, 196,
+	238, 252, 246, 222, 232, 70, 112, 36, 190, 126, 120, 60, 184,
+	96, 106, 80, 30, 198, 240, 142, 216, 164, 64, 146, 226, 254,
+	248, 236, 220, 188, 58, 28, 140, 224, 234, 138, 136, 208, 158,
+	176, 210, 72, 114, 160, 54, 16, 90, 46, 178, 38, 192, 212, 24,
+	20, 100, 168, 74, 128, 122, 116, 68, 110, 94, 104, 162, 62,
+	186, 56, 206, 156, 14, 88, 18, 98, 108, 92, 154, 12, 10, 8,
+	48, 82, 32, 180, 172, 50, 84, 150, 40, 200, 242, 194, 230, 34,
+	182, 78, 214, 144, 218, 26, 134, 174, 52, 44, 22, 166, 66,
+	102, 204, 86, 152, 6, 170, 148, 228, 76, 132, 42, 202, 4, 130,
+	2
+};
+
+#define PREDIV(value)       (idiv[(((value) & 0xfc000000) >> 26)])
+#define MULTINT(value)      (((value) & 0x00000ffe) >> 1)
+#define MULTINT_PRE(value)  (ps[((MULTINT((value)) & 0x300) >> 8)])
+#define MULTINT_MAIN(value) (mult[(MULTINT((value)) & 0xff)])
+#define RANGEA(value)       (((value) & 0x03f80000) >> 19)
+#define RANGEA_PRE(value)   (ps[((RANGEA((value)) & 0x60) >> 5)])
+#define RANGEA_MAIN(value)  (odiv[(RANGEA((value)) & 0x1f)])
+#define RANGEB(value)       (((value) & 0x0007f000) >> 12)
+#define RANGEB_PRE(value)   (ps[((RANGEB((value)) & 0x60) >> 5)])
+#define RANGEB_MAIN(value)  (odiv[(RANGEB((value)) & 0x1f)])
+
+
+#define CLK_HAS_RATE	0x1	/* has rate in MHz */
+#define CLK_HAS_CTRL	0x2	/* has control reg and bit */
+
+struct clk {
+	struct list_head node;
+	char name[32];
+	int flags;
+	struct device *dev;
+	unsigned long rate;
+	void (*calc) (struct clk *);
+	struct clk *parent;
+	int reg, bit;		/* CLK_HAS_CTRL */
+	int div_shift;		/* only used by generic_div_clk_calc */
+	struct kref kref;
+};
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+
+struct clk *acp_clk_get(struct device *dev, const char *id)
+{
+	struct clk *clk;
+
+	if (!dev)
+		return NULL;
+
+	mutex_lock(&clocks_mutex);
+	list_for_each_entry(clk, &clocks, node) {
+		if (clk->dev == dev) {
+			kref_get(&clk->kref);
+			mutex_unlock(&clocks_mutex);
+			return clk;
+		}
+	}
+
+	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
+	if (!clk) {
+		dev_err(dev, "Create clock error\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	INIT_LIST_HEAD(&clk->node);
+	kref_init(&clk->kref);
+	clk->dev = dev;
+	list_add(&clk->node, &clocks);
+	mutex_unlock(&clocks_mutex);
+
+	return clk;
+}
+
+static void acp_clk_release(struct kref *kref)
+{
+	struct clk *clk = container_of(kref, struct clk, kref);
+
+	mutex_lock(&clocks_mutex);
+	list_del(&clk->node);
+	mutex_lock(&clocks_mutex);
+
+	kfree(clk);
+}
+
+void acp_clk_put(struct clk *clk)
+{
+	kref_put(&clk->kref, acp_clk_release);
+	return;
+}
+
+int acp_clk_enable(struct clk *clk)
+{
+	return 0;
+}
+
+void acp_clk_disable(struct clk *clk)
+{
+	return;
+}
+
+unsigned long acp_clk_get_rate(struct clk *clk)
+{
+	struct device_node *np = clk->dev->of_node;
+	const int *prop;
+	int len;
+
+	if (!np)
+		return 0;
+
+	prop = of_get_property(np, "clock-frequency", &len);
+	if (!prop || len != sizeof(*prop)) {
+		dev_err(clk->dev, "Get the clock frequency error\n");
+		return -1;
+	}
+
+	return *prop;
+}
+
+long acp_clk_round_rate(struct clk *clk, unsigned long rate)
+{
+	return 0;
+}
+
+int acp_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	return 0;
+}
+
+static int clk_register(struct clk *clk)
+{
+	mutex_lock(&clocks_mutex);
+	list_add(&clk->node, &clocks);
+	mutex_unlock(&clocks_mutex);
+	return 0;
+}
+
+/*
+  ------------------------------------------------------------------------------
+  get_ppc_pll
+
+  Assumes the the PPC PLL is locked...
+*/
+
+static void get_ppc_pll(unsigned long *dco, unsigned long *pllouta, unsigned long *plloutb)
+{
+	unsigned long ctrl;
+
+	ctrl = mfdcr(0xd02);
+
+	*dco = (CLK_REF0 / 1000 / PREDIV(ctrl));
+	*dco *= (MULTINT_PRE(ctrl) * MULTINT_MAIN(ctrl));
+	*pllouta = *dco / (RANGEA_PRE(ctrl) * RANGEA_MAIN(ctrl));
+	*plloutb = *dco / (RANGEB_PRE(ctrl) * RANGEB_MAIN(ctrl));
+}
+
+
+static void ppc_clk_calc(struct clk *clk)
+{
+	unsigned long mcgc = mfdcr(0xd00);
+
+	if (!(mcgc & 0xc0000000)) {
+		/* ppc_clk is clk_ref0 */
+		clk->rate = CLK_REF0 / 1000;
+	} else {
+		unsigned long dco = 0;
+		unsigned long pllouta = 0;
+		unsigned long plloutb = 0;
+
+		get_ppc_pll(&dco, &pllouta, &plloutb);
+		if (((mcgc & 0xc0000000) >> 30) == 1)
+			clk->rate = pllouta;
+		else
+			clk->rate = pllouta >> 1;
+	}
+}
+static struct clk ppc_clk = {
+	.name = "ppc_clk",
+	.calc = ppc_clk_calc,
+};
+
+static void pbus_clk_calc(struct clk *clk)
+{
+	unsigned long mcgc = mfdcr(0xd00);
+	unsigned long mcgs = mfdcr(0xd01);
+
+	if (!(mcgc & 0x08000000)) {
+		/* clk_per is clk_ref0 */
+		clk->rate = CLK_REF0 / 1000;
+	} else if (mcgs & 0x80000000) {
+		unsigned long dco = 0;
+		unsigned long pllouta = 0;
+		unsigned long plloutb = 0;
+
+		get_ppc_pll(&dco, &pllouta, &plloutb);
+		clk->rate = plloutb;
+	} else
+		clk->rate = -1;
+}
+
+static struct clk pbus_clk = {
+	.name = "clk_per",
+	.calc = pbus_clk_calc,
+};
+
+struct clk *rate_clks[] = {
+	&ppc_clk,
+	&pbus_clk,
+	NULL
+};
+
+static void rate_clk_init(struct clk *clk)
+{
+	if (clk->calc) {
+		clk->calc(clk);
+		clk->flags |= CLK_HAS_RATE;
+		clk_register(clk);
+	} else {
+		pr_err("Could not initialize clk %s without a calc routine\n",
+		       clk->name);
+	}
+}
+
+static void rate_clks_init(void)
+{
+	struct clk **cpp, *clk;
+
+	cpp = rate_clks;
+	while ((clk = *cpp++))
+		rate_clk_init(clk);
+}
+
+
+
+static struct clk_interface acp_clk_functions = {
+	.clk_get                = acp_clk_get,
+	.clk_enable             = acp_clk_enable,
+	.clk_disable            = acp_clk_disable,
+	.clk_get_rate           = acp_clk_get_rate,
+	.clk_put                = acp_clk_put,
+	.clk_round_rate         = acp_clk_round_rate,
+	.clk_set_rate           = acp_clk_set_rate,
+	.clk_set_parent         = NULL,
+	.clk_get_parent         = NULL,
+};
+
+int __init acp_clk_init(void)
+{
+	rate_clks_init();
+
+	clk_functions = acp_clk_functions;
+	return 0;
+}
+EXPORT_SYMBOL(acp_clk_init);
diff --git a/arch/powerpc/platforms/44x/acpclock.h b/arch/powerpc/platforms/44x/acpclock.h
new file mode 100644
index 0000000..caab2a6
--- /dev/null
+++ b/arch/powerpc/platforms/44x/acpclock.h
@@ -0,0 +1,6 @@
+#ifndef __ARCH_POWERPC_ACP_CLOCK_H
+#define __ARCH_POWERPC_ACP_CLOCK_H
+
+int acp_clk_init(void);
+
+#endif
-- 
1.8.3




More information about the linux-yocto mailing list