LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC/PATCH] Clock binding prototype implementation
From: Benjamin Herrenschmidt @ 2009-08-18  7:45 UTC (permalink / raw)
  To: devicetree-discuss; +Cc: linuxppc-dev list
In-Reply-To: <1250569288.19007.15.camel@pasglop>

powerpc: New implementation of the "clk" API with device-tree binding

This replaces the struct clk_interface, as far as I know only ever
used by the mpc512x platforms, with a new scheme:

struct clk is now an object containing function pointers for all the
base API routines. The implementation of those routines call into
those pointers. struct clk is meant to be embedded in the "real"
structure for a given clock type.

clk_get is hooked via ppc_md. A default implementation is provided
that uses the device-tree binding (still being discussed, a patch
to Documentation/* will come when it's final). It can also create
simple objects (no control, working get_rate()) based on nodes that
have a "clock-frequency" property.

Finally, the mpc512x code is adapted. The adaptation is minimal as
I don't have test gear, so I'm not using nor touching the device-tree
on this one, but instead hooking ppc_md.get_clk() and keeping the
old code mostly intact (just some type changes to embed the new
struct clk into the mpc512x specific variants which gets renamed
to struct mpc512x_clk).

No S-O-B yet as this is totally untested and the binding isn't
final yet neither, but gives an idea of where I'm going.

Ben.

Index: linux-work/arch/powerpc/include/asm/clk_interface.h
===================================================================
--- linux-work.orig/arch/powerpc/include/asm/clk_interface.h	2009-02-05 16:22:24.000000000 +1100
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,20 +0,0 @@
-#ifndef __ASM_POWERPC_CLK_INTERFACE_H
-#define __ASM_POWERPC_CLK_INTERFACE_H
-
-#include <linux/clk.h>
-
-struct clk_interface {
-	struct clk*	(*clk_get)	(struct device *dev, const char *id);
-	int		(*clk_enable)	(struct clk *clk);
-	void		(*clk_disable)	(struct clk *clk);
-	unsigned long	(*clk_get_rate)	(struct clk *clk);
-	void		(*clk_put)	(struct clk *clk);
-	long		(*clk_round_rate) (struct clk *clk, unsigned long rate);
-	int 		(*clk_set_rate)	(struct clk *clk, unsigned long rate);
-	int		(*clk_set_parent) (struct clk *clk, struct clk *parent);
-	struct clk*	(*clk_get_parent) (struct clk *clk);
-};
-
-extern struct clk_interface clk_functions;
-
-#endif /* __ASM_POWERPC_CLK_INTERFACE_H */
Index: linux-work/arch/powerpc/kernel/clock.c
===================================================================
--- linux-work.orig/arch/powerpc/kernel/clock.c	2009-02-05 16:22:24.000000000 +1100
+++ linux-work/arch/powerpc/kernel/clock.c	2009-08-18 17:38:08.000000000 +1000
@@ -1,82 +1,282 @@
 /*
- * Dummy clk implementations for powerpc.
- * These need to be overridden in platform code.
+ * Clock infrastructure for PowerPC platform
+ *
  */
 
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/module.h>
-#include <asm/clk_interface.h>
+#include <linux/of.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <asm/clk.h>
+#include <asm/machdep.h>
+
+struct clk_provider {
+	struct list_head	link;
+	struct device_node     	*node;
+
+	/* Return NULL if no such clock output, return PTR_ERR for
+	 * other errors
+	 */
+	struct clk *		(*get)(struct device_node *np,
+				       const char *output_id,
+				       void *data);
+	void			*data;
+};
+
+static LIST_HEAD(clk_providers);
+static DEFINE_MUTEX(clk_lock);
+
+int clk_add_provider(struct device_node *np,
+		     struct clk *(*clk_src_get)(struct device_node *np,
+						const char *output_id,
+						void *data),
+		     void *data)
+{
+	struct clk_provider *cp;
 
-struct clk_interface clk_functions;
+	cp = kzalloc(sizeof(struct clk_provider), GFP_KERNEL);
+	if (!cp)
+		return -ENOMEM;
+
+	cp->node = of_node_get(np);
+	cp->data = data;
+	cp->get = clk_src_get;
+
+	mutex_lock(&clk_lock);
+	list_add(&cp->link, &clk_providers);
+	mutex_unlock(&clk_lock);
 
-struct clk *clk_get(struct device *dev, const char *id)
+	return 0;
+}
+
+void clk_del_provider(struct device_node *np,
+		      struct clk *(*clk_src_get)(struct device_node *np,
+						 const char *output_id,
+						 void *data),
+		      void *data)
 {
-	if (clk_functions.clk_get)
-		return clk_functions.clk_get(dev, id);
-	return ERR_PTR(-ENOSYS);
+	struct clk_provider *cp, *tmp;
+
+	mutex_lock(&clk_lock);
+	list_for_each_entry_safe(cp, tmp, &clk_providers, link) {
+		if (cp->node == np && cp->get == clk_src_get && cp->data == data) {
+			list_del(&cp->link);
+			of_node_put(cp->node);
+			kfree(cp);
+			break;
+		}
+	}
+	mutex_unlock(&clk_lock);
 }
-EXPORT_SYMBOL(clk_get);
 
-void clk_put(struct clk *clk)
+static unsigned long clk_simple_get_rate(struct clk *clk)
 {
-	if (clk_functions.clk_put)
-		clk_functions.clk_put(clk);
+	struct clk_simple *cs = to_clk_simple(clk);
+
+	return cs->rate;
 }
-EXPORT_SYMBOL(clk_put);
+
+static void clk_simple_put(struct clk *clk)
+{
+	struct clk_simple *cs = to_clk_simple(clk);
+
+	kfree(cs);
+}
+
+static struct clk *__clk_get_simple(struct device_node *np)
+{
+	const u32 *freq;
+	struct clk_simple *cs;
+
+	freq = of_get_property(np, "clock-frequency", NULL);
+	if (!freq)
+		return ERR_PTR(-ENXIO);
+
+	cs = kzalloc(sizeof(struct clk_simple), GFP_KERNEL);
+	if (!cs)
+		return ERR_PTR(-ENOMEM);
+
+	cs->rate = *freq;
+	cs->clk.get_rate = clk_simple_get_rate;
+	cs->clk.put = clk_simple_put;
+
+	return &cs->clk;
+}
+
+static struct clk *__clk_get_from_provider(struct device_node *np, u32 index)
+{
+	struct clk_provider *provider;
+	const char *names, *id = NULL;
+	int sz, l, i;
+	struct clk *clk = NULL;
+
+	/* Look for "clock-output-names" in the provider's node, if it's missing
+	 * we don't bother looking for a clock provider, though we do fallback
+	 * to the "simple" clock case
+	 */
+	names = of_get_property(np, "clock-output-names", &sz);
+	if (!names)
+		return __clk_get_simple(np);
+
+	/* Lookup index */
+	for (i = 0; sz > 0; i++) {
+		if (index == i) {
+			id = names;
+			break;
+		}
+		l = strlen(names) + 1;
+		sz -= l;
+		names += l;
+	}
+	if (id == NULL)
+		return ERR_PTR(-ENXIO);
+
+	/* Check if we have such a provider in our array */
+	mutex_lock(&clk_lock);
+	list_for_each_entry(provider, &clk_providers, link) {
+		if (provider->node == np)
+			clk = provider->get(np, id, provider->data);
+		if (clk)
+			break;
+	}
+	mutex_unlock(&clk_lock);
+
+	/* Clock not found, return an error */
+	if (clk == NULL)
+		clk = ERR_PTR(-EINVAL);
+
+	return clk;
+}
+
+struct clk *of_clk_get(struct device_node *np, const char *id)
+{
+	int sz, l, index = 0;
+	struct device_node *provnode;
+	const u32 *map;
+	u32 provhandle, provindex;
+	struct clk *clk;
+
+	/* look for an id match in clock-names property */
+	if (id) {
+		const char *names = of_get_property(np, "clock-input-names", &sz);
+
+		/* no such property, fail for now. Maybe in the long run we might
+		 * consider allowing "generic" names such as "bus" for the bus
+		 * clock, effectively routing to the parent node...
+		 */
+		if (!names)
+			return ERR_PTR(-ENXIO);
+		while (sz > 0) {
+			if (strcasecmp(id, names) == 0)
+				break;
+			l = strlen(names) + 1;
+			sz -= l;
+			names += l;
+			index++;
+		}
+		if (sz < 1)
+			return ERR_PTR(-ENOENT);
+	}
+
+	/* now we have an index, lookup in clock-map */
+	map = of_get_property(np, "clock-map", &sz);
+	if (!map)
+		return ERR_PTR(-ENXIO);
+	sz /= sizeof(u32) * 2;
+	if (index >= sz)
+		return ERR_PTR(-EINVAL);
+	provhandle = map[index * 2];
+	provindex = map[index * 2] + 1;
+
+	provnode = of_find_node_by_phandle(provhandle);
+	if (!provnode)
+		return ERR_PTR(-EINVAL);
+
+	clk = __clk_get_from_provider(provnode, provindex);
+	of_node_put(provnode);
+
+	return clk;
+}
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+	if (ppc_md.clk_get)
+		return ppc_md.clk_get(dev, id);
+
+	if (dev->archdata.of_node)
+		return of_clk_get(dev->archdata.of_node, id);
+
+	return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL(clk_get);
+
+/*
+ * clk_* API is just wrappers to the function pointers
+ * inside of struct clk
+ */
 
 int clk_enable(struct clk *clk)
 {
-	if (clk_functions.clk_enable)
-		return clk_functions.clk_enable(clk);
-	return -ENOSYS;
+	if (clk->enable)
+		return clk->enable(clk);
+	return 0;
 }
 EXPORT_SYMBOL(clk_enable);
 
 void clk_disable(struct clk *clk)
 {
-	if (clk_functions.clk_disable)
-		clk_functions.clk_disable(clk);
+	if (clk->disable)
+		clk->disable(clk);
 }
 EXPORT_SYMBOL(clk_disable);
 
 unsigned long clk_get_rate(struct clk *clk)
 {
-	if (clk_functions.clk_get_rate)
-		return clk_functions.clk_get_rate(clk);
+	if (clk->get_rate)
+		return clk->get_rate(clk);
 	return 0;
 }
 EXPORT_SYMBOL(clk_get_rate);
 
+void clk_put(struct clk *clk)
+{
+	if (clk->put)
+		clk->put(clk);
+}
+EXPORT_SYMBOL(clk_put);
+
 long clk_round_rate(struct clk *clk, unsigned long rate)
 {
-	if (clk_functions.clk_round_rate)
-		return clk_functions.clk_round_rate(clk, rate);
+	if (clk->round_rate)
+		return clk->round_rate(clk, rate);
 	return -ENOSYS;
 }
 EXPORT_SYMBOL(clk_round_rate);
 
 int clk_set_rate(struct clk *clk, unsigned long rate)
 {
-	if (clk_functions.clk_set_rate)
-		return clk_functions.clk_set_rate(clk, rate);
+	if (clk->set_rate)
+		return clk->set_rate(clk, rate);
 	return -ENOSYS;
 }
 EXPORT_SYMBOL(clk_set_rate);
 
-struct clk *clk_get_parent(struct clk *clk)
-{
-	if (clk_functions.clk_get_parent)
-		return clk_functions.clk_get_parent(clk);
-	return ERR_PTR(-ENOSYS);
-}
-EXPORT_SYMBOL(clk_get_parent);
-
 int clk_set_parent(struct clk *clk, struct clk *parent)
 {
-	if (clk_functions.clk_set_parent)
-		return clk_functions.clk_set_parent(clk, parent);
+	if (clk->set_parent)
+		return clk->set_parent(clk, parent);
 	return -ENOSYS;
 }
 EXPORT_SYMBOL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+	if (clk->get_parent)
+		return clk->get_parent(clk);
+	return ERR_PTR(-ENOSYS);
+}
+EXPORT_SYMBOL(clk_get_parent);
Index: linux-work/arch/powerpc/include/asm/clk.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/include/asm/clk.h	2009-08-18 17:38:08.000000000 +1000
@@ -0,0 +1,61 @@
+/*
+ * Clock infrastructure for PowerPC platform
+ */
+#ifndef __ASM_POWERPC_CLK_H
+#define __ASM_POWERPC_CLK_H
+
+struct device_node;
+
+/* The definition of struct clk for arch/powerpc is global, it's
+ * expected that clock providers generate "subclasses" embedding
+ * struct clk
+ */
+struct clk {
+	int		(*enable)(struct clk *);
+	void		(*disable)(struct clk *);
+	unsigned long	(*get_rate)(struct clk *);
+	void		(*put)(struct clk *);
+	long		(*round_rate)(struct clk *, unsigned long);
+	int		(*set_rate)(struct clk *, unsigned long);
+	int		(*set_parent)(struct clk *, struct clk *);
+	struct clk*	(*get_parent)(struct clk *);
+};
+
+/* The simple clock is exposed here for implementations that want
+ * to re-use it in case they don't need anything else
+ */
+struct clk_simple {
+	struct clk	clk;
+	unsigned long	rate;
+};
+#define	to_clk_simple(n) container_of(n, struct clk_simple, clk)
+
+
+/**
+ * clk_add_provier - Attach a clock provider to a clock source device node
+ * @node: device node of the clock source
+ * @clk_src_get: function pointer called to obtain a struct clk
+ * @data: arbitrary data passed back to clk_src_get() when called
+ *
+ * This will attach the clk_src_get() function to a given device_node,
+ * that function will then be called whenever a drivers does a clk_get()
+ * and that driver's clock-map references that clock source node.
+ */
+extern int clk_add_provider(struct device_node *np,
+			    struct clk *(*clk_src_get)(struct device_node *np,
+						       const char *output_id,
+						       void *data),
+			    void *data);
+/**
+ * clk_del_provier - Remove a clock provider from a clock source device node
+ * @node: device node of the clock source
+ * @clk_src_get: function pointer used in clk_add_provider()
+ * @data: data used in clk_add_provider()
+ */
+extern void clk_del_provider(struct device_node *np,
+			     struct clk *(*clk_src_get)(struct device_node *np,
+							const char *output_id,
+							void *data),
+			     void *data);
+
+#endif /* __ASM_POWERPC_CLK_H */
Index: linux-work/arch/powerpc/include/asm/machdep.h
===================================================================
--- linux-work.orig/arch/powerpc/include/asm/machdep.h	2009-08-18 17:36:53.000000000 +1000
+++ linux-work/arch/powerpc/include/asm/machdep.h	2009-08-18 17:38:08.000000000 +1000
@@ -27,6 +27,8 @@ struct iommu_table;
 struct rtc_time;
 struct file;
 struct pci_controller;
+struct clk;
+struct device;
 #ifdef CONFIG_KEXEC
 struct kimage;
 #endif
@@ -266,7 +268,11 @@ struct machdep_calls {
 	 */
 	void (*suspend_disable_irqs)(void);
 	void (*suspend_enable_irqs)(void);
-#endif
+#endif /* CONFIG_SUSPEND */
+
+#ifdef CONFIG_PPC_CLOCK
+	struct clk *(*clk_get)(struct device *dev, const char *id);
+#endif /* CONFIG_PPC_CLOCK */
 };
 
 extern void e500_idle(void);
Index: linux-work/arch/powerpc/platforms/512x/clock.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/512x/clock.c	2009-08-18 17:33:15.000000000 +1000
+++ linux-work/arch/powerpc/platforms/512x/clock.c	2009-08-18 17:38:30.000000000 +1000
@@ -25,7 +25,7 @@
 
 #include <linux/of_platform.h>
 #include <asm/mpc5xxx.h>
-#include <asm/clk_interface.h>
+#include <asm/clk.h>
 
 #undef CLK_DEBUG
 
@@ -34,25 +34,28 @@ static int clocks_initialized;
 #define CLK_HAS_RATE	0x1	/* has rate in MHz */
 #define CLK_HAS_CTRL	0x2	/* has control reg and bit */
 
-struct clk {
+struct mpc512x_clk {
+	struct clk clk;
 	struct list_head node;
 	char name[32];
 	int flags;
 	struct device *dev;
 	unsigned long rate;
 	struct module *owner;
-	void (*calc) (struct clk *);
-	struct clk *parent;
+	void (*calc) (struct mpc512x_clk *);
+	struct mpc512x_clk *parent;
 	int reg, bit;		/* CLK_HAS_CTRL */
 	int div_shift;		/* only used by generic_div_clk_calc */
 };
+#define	to_mpc512x_clk(n) container_of(n, struct mpc512x_clk, clk)
 
 static LIST_HEAD(clocks);
 static DEFINE_MUTEX(clocks_mutex);
 
 static struct clk *mpc5121_clk_get(struct device *dev, const char *id)
 {
-	struct clk *p, *clk = ERR_PTR(-ENOENT);
+	struct clk *clk = ERR_PTR(-ENOENT);
+	struct mpc512x_clk *p;
 	int dev_match = 0;
 	int id_match = 0;
 
@@ -66,7 +69,7 @@ static struct clk *mpc5121_clk_get(struc
 		if (strcmp(id, p->name) == 0)
 			id_match++;
 		if ((dev_match || id_match) && try_module_get(p->owner)) {
-			clk = p;
+			clk = &p->clk;
 			break;
 		}
 	}
@@ -78,7 +81,7 @@ static struct clk *mpc5121_clk_get(struc
 #ifdef CLK_DEBUG
 static void dump_clocks(void)
 {
-	struct clk *p;
+	struct mpc512x_clk *p;
 
 	mutex_lock(&clocks_mutex);
 	printk(KERN_INFO "CLOCKS:\n");
@@ -101,7 +104,9 @@ static void dump_clocks(void)
 
 static void mpc5121_clk_put(struct clk *clk)
 {
-	module_put(clk->owner);
+	struct mpc512x_clk *mpclk = to_mpc512x_clk(clk);
+
+	module_put(mpclk->owner);
 }
 
 #define NRPSC 12
@@ -123,31 +128,35 @@ struct mpc512x_clockctl __iomem *clockct
 
 static int mpc5121_clk_enable(struct clk *clk)
 {
+	struct mpc512x_clk *mpclk = to_mpc512x_clk(clk);
 	unsigned int mask;
 
-	if (clk->flags & CLK_HAS_CTRL) {
-		mask = in_be32(&clockctl->sccr[clk->reg]);
-		mask |= 1 << clk->bit;
-		out_be32(&clockctl->sccr[clk->reg], mask);
+	if (mpclk->flags & CLK_HAS_CTRL) {
+		mask = in_be32(&clockctl->sccr[mpclk->reg]);
+		mask |= 1 << mpclk->bit;
+		out_be32(&clockctl->sccr[mpclk->reg], mask);
 	}
 	return 0;
 }
 
 static void mpc5121_clk_disable(struct clk *clk)
 {
+	struct mpc512x_clk *mpclk = to_mpc512x_clk(clk);
 	unsigned int mask;
 
-	if (clk->flags & CLK_HAS_CTRL) {
-		mask = in_be32(&clockctl->sccr[clk->reg]);
-		mask &= ~(1 << clk->bit);
-		out_be32(&clockctl->sccr[clk->reg], mask);
+	if (mpclk->flags & CLK_HAS_CTRL) {
+		mask = in_be32(&clockctl->sccr[mpclk->reg]);
+		mask &= ~(1 << mpclk->bit);
+		out_be32(&clockctl->sccr[mpclk->reg], mask);
 	}
 }
 
 static unsigned long mpc5121_clk_get_rate(struct clk *clk)
 {
-	if (clk->flags & CLK_HAS_RATE)
-		return clk->rate;
+	struct mpc512x_clk *mpclk = to_mpc512x_clk(clk);
+
+	if (mpclk->flags & CLK_HAS_RATE)
+		return mpclk->rate;
 	else
 		return 0;
 }
@@ -162,11 +171,21 @@ static int mpc5121_clk_set_rate(struct c
 	return 0;
 }
 
-static int clk_register(struct clk *clk)
+static int clk_register(struct mpc512x_clk *mpclk)
 {
+	mpclk.clk.enable	= mpc5121_clk_enable;
+	mpclk.clk.disable	= mpc5121_clk_disable;
+	mpclk.clk.get_rate	= mpc5121_clk_get_rate;
+	mpclk.clk.put		= mpc5121_clk_put;
+	mpclk.clk.round_rate	= mpc5121_clk_round_rate;
+	mpclk.clk.set_rate	= mpc5121_clk_set_rate;
+	mpclk.clk.set_paqrent	= NULL;
+	mpclk.clk.get_parent	= NULL;
+
 	mutex_lock(&clocks_mutex);
-	list_add(&clk->node, &clocks);
+	list_add(&mpclk->node, &clocks);
 	mutex_unlock(&clocks_mutex);
+
 	return 0;
 }
 
@@ -250,7 +269,7 @@ static unsigned long devtree_getfreq(cha
 	return val;
 }
 
-static void ref_clk_calc(struct clk *clk)
+static void ref_clk_calc(struct mpc512x_clk *mpclk)
 {
 	unsigned long rate;
 
@@ -260,26 +279,26 @@ static void ref_clk_calc(struct clk *clk
 		clk->rate = 0;
 		return;
 	}
-	clk->rate = ips_to_ref(rate);
+	mpclk->rate = ips_to_ref(rate);
 }
 
-static struct clk ref_clk = {
+static struct mpc512x_clk ref_clk = {
 	.name = "ref_clk",
 	.calc = ref_clk_calc,
 };
 
 
-static void sys_clk_calc(struct clk *clk)
+static void sys_clk_calc(struct mpc512x_clk *clk)
 {
 	clk->rate = ref_to_sys(ref_clk.rate);
 }
 
-static struct clk sys_clk = {
+static struct mpc512x_clk sys_clk = {
 	.name = "sys_clk",
 	.calc = sys_clk_calc,
 };
 
-static void diu_clk_calc(struct clk *clk)
+static void diu_clk_calc(struct mpc512x_clk *clk)
 {
 	int diudiv_x_2 = clockctl->scfr1 & 0xff;
 	unsigned long rate;
@@ -292,30 +311,30 @@ static void diu_clk_calc(struct clk *clk
 	clk->rate = rate;
 }
 
-static void half_clk_calc(struct clk *clk)
+static void half_clk_calc(struct mpc512x_clk *clk)
 {
 	clk->rate = clk->parent->rate / 2;
 }
 
-static void generic_div_clk_calc(struct clk *clk)
+static void generic_div_clk_calc(struct mpc512x_clk *clk)
 {
 	int div = (clockctl->scfr1 >> clk->div_shift) & 0x7;
 
 	clk->rate = clk->parent->rate / div;
 }
 
-static void unity_clk_calc(struct clk *clk)
+static void unity_clk_calc(struct mpc512x_clk *clk)
 {
 	clk->rate = clk->parent->rate;
 }
 
-static struct clk csb_clk = {
+static struct mpc512x_clk csb_clk = {
 	.name = "csb_clk",
 	.calc = half_clk_calc,
 	.parent = &sys_clk,
 };
 
-static void e300_clk_calc(struct clk *clk)
+static void e300_clk_calc(struct mpc512x_clk *clk)
 {
 	int spmf = (clockctl->spmr >> 16) & 0xf;
 	int ratex2 = clk->parent->rate * spmf;
@@ -323,13 +342,13 @@ static void e300_clk_calc(struct clk *cl
 	clk->rate = ratex2 / 2;
 }
 
-static struct clk e300_clk = {
+static struct mpc512x_clk e300_clk = {
 	.name = "e300_clk",
 	.calc = e300_clk_calc,
 	.parent = &csb_clk,
 };
 
-static struct clk ips_clk = {
+static struct mpc512x_clk ips_clk = {
 	.name = "ips_clk",
 	.calc = generic_div_clk_calc,
 	.parent = &csb_clk,
@@ -339,7 +358,7 @@ static struct clk ips_clk = {
 /*
  * Clocks controlled by SCCR1 (.reg = 0)
  */
-static struct clk lpc_clk = {
+static struct mpc512x_clk lpc_clk = {
 	.name = "lpc_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 0,
@@ -349,7 +368,7 @@ static struct clk lpc_clk = {
 	.div_shift = 11,
 };
 
-static struct clk nfc_clk = {
+static struct mpc512x_clk nfc_clk = {
 	.name = "nfc_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 0,
@@ -359,7 +378,7 @@ static struct clk nfc_clk = {
 	.div_shift = 8,
 };
 
-static struct clk pata_clk = {
+static struct mpc512x_clk pata_clk = {
 	.name = "pata_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 0,
@@ -373,7 +392,7 @@ static struct clk pata_clk = {
  * are setup elsewhere
  */
 
-static struct clk sata_clk = {
+static struct mpc512x_clk sata_clk = {
 	.name = "sata_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 0,
@@ -382,7 +401,7 @@ static struct clk sata_clk = {
 	.parent = &ips_clk,
 };
 
-static struct clk fec_clk = {
+static struct mpc512x_clk fec_clk = {
 	.name = "fec_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 0,
@@ -391,7 +410,7 @@ static struct clk fec_clk = {
 	.parent = &ips_clk,
 };
 
-static struct clk pci_clk = {
+static struct mpc512x_clk pci_clk = {
 	.name = "pci_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 0,
@@ -404,7 +423,7 @@ static struct clk pci_clk = {
 /*
  * Clocks controlled by SCCR2 (.reg = 1)
  */
-static struct clk diu_clk = {
+static struct mpc512x_clk diu_clk = {
 	.name = "diu_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -412,7 +431,7 @@ static struct clk diu_clk = {
 	.calc = diu_clk_calc,
 };
 
-static struct clk axe_clk = {
+static struct mpc512x_clk axe_clk = {
 	.name = "axe_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -421,7 +440,7 @@ static struct clk axe_clk = {
 	.parent = &csb_clk,
 };
 
-static struct clk usb1_clk = {
+static struct mpc512x_clk usb1_clk = {
 	.name = "usb1_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -430,7 +449,7 @@ static struct clk usb1_clk = {
 	.parent = &csb_clk,
 };
 
-static struct clk usb2_clk = {
+static struct mpc512x_clk usb2_clk = {
 	.name = "usb2_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -439,7 +458,7 @@ static struct clk usb2_clk = {
 	.parent = &csb_clk,
 };
 
-static struct clk i2c_clk = {
+static struct mpc512x_clk i2c_clk = {
 	.name = "i2c_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -448,7 +467,7 @@ static struct clk i2c_clk = {
 	.parent = &ips_clk,
 };
 
-static struct clk mscan_clk = {
+static struct mpc512x_clk mscan_clk = {
 	.name = "mscan_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -457,7 +476,7 @@ static struct clk mscan_clk = {
 	.parent = &ips_clk,
 };
 
-static struct clk sdhc_clk = {
+static struct mpc512x_clk sdhc_clk = {
 	.name = "sdhc_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -466,7 +485,7 @@ static struct clk sdhc_clk = {
 	.parent = &ips_clk,
 };
 
-static struct clk mbx_bus_clk = {
+static struct mpc512x_clk mbx_bus_clk = {
 	.name = "mbx_bus_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -475,7 +494,7 @@ static struct clk mbx_bus_clk = {
 	.parent = &csb_clk,
 };
 
-static struct clk mbx_clk = {
+static struct mpc512x_clk mbx_clk = {
 	.name = "mbx_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -484,7 +503,7 @@ static struct clk mbx_clk = {
 	.parent = &csb_clk,
 };
 
-static struct clk mbx_3d_clk = {
+static struct mpc512x_clk mbx_3d_clk = {
 	.name = "mbx_3d_clk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
@@ -494,44 +513,44 @@ static struct clk mbx_3d_clk = {
 	.div_shift = 14,
 };
 
-static void psc_mclk_in_calc(struct clk *clk)
+static void psc_mclk_in_calc(struct mpc512x_clk *clk)
 {
 	clk->rate = devtree_getfreq("psc_mclk_in");
 	if (!clk->rate)
 		clk->rate = 25000000;
 }
 
-static struct clk psc_mclk_in = {
+static struct mpc512x_clk psc_mclk_in = {
 	.name = "psc_mclk_in",
 	.calc = psc_mclk_in_calc,
 };
 
-static struct clk spdif_txclk = {
+static struct mpc512x_clk spdif_txclk = {
 	.name = "spdif_txclk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
 	.bit = 23,
 };
 
-static struct clk spdif_rxclk = {
+static struct mpc512x_clk spdif_rxclk = {
 	.name = "spdif_rxclk",
 	.flags = CLK_HAS_CTRL,
 	.reg = 1,
 	.bit = 23,
 };
 
-static void ac97_clk_calc(struct clk *clk)
+static void ac97_clk_calc(struct mpc512x_clk *clk)
 {
 	/* ac97 bit clock is always 24.567 MHz */
 	clk->rate = 24567000;
 }
 
-static struct clk ac97_clk = {
+static struct mpc512x_clk ac97_clk = {
 	.name = "ac97_clk_in",
 	.calc = ac97_clk_calc,
 };
 
-struct clk *rate_clks[] = {
+struct mpc512x_clk *rate_clks[] = {
 	&ref_clk,
 	&sys_clk,
 	&diu_clk,
@@ -560,7 +579,7 @@ struct clk *rate_clks[] = {
 	NULL
 };
 
-static void rate_clk_init(struct clk *clk)
+static void rate_clk_init(struct mpc512x_clk *clk)
 {
 	if (clk->calc) {
 		clk->calc(clk);
@@ -575,7 +594,7 @@ static void rate_clk_init(struct clk *cl
 
 static void rate_clks_init(void)
 {
-	struct clk **cpp, *clk;
+	struct mpc512x_clk **cpp, *clk;
 
 	cpp = rate_clks;
 	while ((clk = *cpp++))
@@ -586,16 +605,16 @@ static void rate_clks_init(void)
  * There are two clk enable registers with 32 enable bits each
  * psc clocks and device clocks are all stored in dev_clks
  */
-struct clk dev_clks[2][32];
+struct mpc512x_clk dev_clks[2][32];
 
 /*
  * Given a psc number return the dev_clk
  * associated with it
  */
-static struct clk *psc_dev_clk(int pscnum)
+static struct mpc512x_clk *psc_dev_clk(int pscnum)
 {
 	int reg, bit;
-	struct clk *clk;
+	struct mpc512x_clk *clk;
 
 	reg = 0;
 	bit = 27 - pscnum;
@@ -609,7 +628,7 @@ static struct clk *psc_dev_clk(int pscnu
 /*
  * PSC clock rate calculation
  */
-static void psc_calc_rate(struct clk *clk, int pscnum, struct device_node *np)
+static void psc_calc_rate(struct mpc512x_clk *clk, int pscnum, struct device_node *np)
 {
 	unsigned long mclk_src = sys_clk.rate;
 	unsigned long mclk_div;
@@ -666,7 +685,7 @@ static void psc_clks_init(void)
 		cell_index = of_get_property(np, "cell-index", NULL);
 		if (cell_index) {
 			int pscnum = *cell_index;
-			struct clk *clk = psc_dev_clk(pscnum);
+			struct mpc512x_clk *clk = psc_dev_clk(pscnum);
 
 			clk->flags = CLK_HAS_RATE | CLK_HAS_CTRL;
 			ofdev = of_find_device_by_node(np);
@@ -686,18 +705,6 @@ static void psc_clks_init(void)
 	}
 }
 
-static struct clk_interface mpc5121_clk_functions = {
-	.clk_get		= mpc5121_clk_get,
-	.clk_enable		= mpc5121_clk_enable,
-	.clk_disable		= mpc5121_clk_disable,
-	.clk_get_rate		= mpc5121_clk_get_rate,
-	.clk_put		= mpc5121_clk_put,
-	.clk_round_rate		= mpc5121_clk_round_rate,
-	.clk_set_rate		= mpc5121_clk_set_rate,
-	.clk_set_parent		= NULL,
-	.clk_get_parent		= NULL,
-};
-
 static int
 mpc5121_clk_init(void)
 {
@@ -721,7 +728,7 @@ mpc5121_clk_init(void)
 	/*iounmap(clockctl); */
 	DEBUG_CLK_DUMP();
 	clocks_initialized++;
-	clk_functions = mpc5121_clk_functions;
+	ppc_md.clk_get = mpc5121_clk_get;
 	return 0;
 }
 

^ permalink raw reply

* Re: powerpc/405ex: Support cuImage for PPC405EX
From: Josh Boyer @ 2009-08-18 13:28 UTC (permalink / raw)
  To: Tiejun Chen; +Cc: linuxppc-dev
In-Reply-To: <1250562484-16415-1-git-send-email-tiejun.chen@windriver.com>

On Tue, Aug 18, 2009 at 10:28:02AM +0800, Tiejun Chen wrote:
>Summary: powerpc/405ex: Support cuImage for PPC405EX
>Reviewers: Benjmain and linux-ppc
>----------------------------------------------------
>These patch series are used to support cuImage on the kilauea board based on PPC405ex.
>
>Tested on the amcc kilauea board:

Hm.  The U-Boot version that ships on the AMCC Kilauea board is FDT aware, so
cuImage shouldn't be needed at all.  I'm slightly confused why we need this.
Are you using it for some other board derived from 405EX that doesn't have
an FDT aware U-Boot?

josh

^ permalink raw reply

* [PATCH] mpc8xxx_spi: Fix polarity inversion in chipselect
From: Michael Barkowski @ 2009-08-18 15:08 UTC (permalink / raw)
  To: linuxppc-dev

If we use open firmware, then alow_flags handles active-low
chipselects, which is the same as SPI_CS_HIGH. This patch fixes the
double negative.

Signed-off-by: Michael Barkowski <michaelbarkowski@ruggedcom.com>
---
Anton,

thanks to your device table matching support patch I can use open firmware
to configure two spi devices on 8360E.  I use the gpios node, with flags set
to 1, matching OF_GPIO_ACTIVE_LOW.  When I do this I notice that the polarity
was already handled in a different way.  Did some searching and didn't find
it had been discussed.  Here's what I am doing now - feel free to use it or
abuse it :) (based in galak-powerpc/next plus your patches 1 and 2)

 drivers/spi/spi_mpc8xxx.c |   10 ++--------
 1 files changed, 2 insertions(+), 8 deletions(-)

diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 0fd0ec4..36bda94 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -159,14 +159,8 @@ static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
 {
 	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
 	struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data;
-	bool pol = spi->mode & SPI_CS_HIGH;
 	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
 
-	if (value == BITBANG_CS_INACTIVE) {
-		if (pdata->cs_control)
-			pdata->cs_control(spi, !pol);
-	}
-
 	if (value == BITBANG_CS_ACTIVE) {
 		u32 regval = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
 
@@ -189,9 +183,9 @@ static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
 			mpc8xxx_spi_write_reg(mode, regval);
 			local_irq_restore(flags);
 		}
-		if (pdata->cs_control)
-			pdata->cs_control(spi, pol);
 	}
+	if (pdata->cs_control)
+		pdata->cs_control(spi, value);
 }
 
 static
-- 
1.6.3.3

^ permalink raw reply related

* Re: [PATCH] mpc8xxx_spi: Fix polarity inversion in chipselect
From: Michael Barkowski @ 2009-08-18 15:16 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <4A8AC3E8.5070301@ruggedcom.com>

Michael Barkowski wrote:
> If we use open firmware, then alow_flags handles active-low
> chipselects, which is the same as SPI_CS_HIGH. This patch fixes the
> double negative.

Hang on - I missed your most recent patches to spi_mpc8xxx

--Michael

^ permalink raw reply

* [PATCH] net: add Xilinx emac lite device driver
From: John Linn @ 2009-08-18 15:30 UTC (permalink / raw)
  To: netdev, linuxppc-dev, jgarzik, davem
  Cc: Sadanand M, Michal Simek, John Linn, John Williams

This patch adds support for the Xilinx Ethernet Lite device.  The
soft logic core from Xilinx is typically used on Virtex and Spartan
designs attached to either a PowerPC or a Microblaze processor.

CC: Grant Likely <grant.likely@secretlab.ca>
CC: Josh Boyer <jwboyer@linux.vnet.ibm.com>
CC: John Williams <john.williams@petalogix.com>
CC: Michal Simek <michal.simek@petalogix.com>
Signed-off-by: Sadanand M <sadanan@xilinx.com>
Signed-off-by: John Linn <john.linn@xilinx.com>
---
 drivers/net/Kconfig           |    5 +
 drivers/net/Makefile          |    1 +
 drivers/net/xilinx_emaclite.c | 1040 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1046 insertions(+), 0 deletions(-)
 create mode 100755 drivers/net/xilinx_emaclite.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 5f6509a..84a5120 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1926,6 +1926,11 @@ config ATL2
 	  To compile this driver as a module, choose M here.  The module
 	  will be called atl2.
 
+config XILINX_EMACLITE
+	tristate "Xilinx 10/100 Ethernet Lite support"
+	help
+	  This driver supports the 10/100 Ethernet Lite from Xilinx.
+
 source "drivers/net/fs_enet/Kconfig"
 
 endif # NET_ETHERNET
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index ead8cab..99ae6d7 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -142,6 +142,7 @@ obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
 obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
 ll_temac-objs := ll_temac_main.o ll_temac_mdio.o
 obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o
+obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
 obj-$(CONFIG_QLA3XXX) += qla3xxx.o
 obj-$(CONFIG_QLGE) += qlge/
 
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c
new file mode 100755
index 0000000..3716e20
--- /dev/null
+++ b/drivers/net/xilinx_emaclite.c
@@ -0,0 +1,1040 @@
+/*
+ * Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device.
+ *
+ * This is a new flat driver which is based on the original emac_lite
+ * driver from John Williams <john.williams@petalogix.com>.
+ *
+ * 2007-2009 (c) Xilinx, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/io.h>
+
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#define DRIVER_NAME "xilinx_emaclite"
+
+/* Register offsets for the EmacLite Core */
+#define XEL_TXBUFF_OFFSET 	0x0		/* Transmit Buffer */
+#define XEL_GIER_OFFSET		0x07F8		/* GIE Register */
+#define XEL_TSR_OFFSET		0x07FC		/* Tx status */
+#define XEL_TPLR_OFFSET		0x07F4		/* Tx packet length */
+
+#define XEL_RXBUFF_OFFSET	0x1000		/* Receive Buffer */
+#define XEL_RPLR_OFFSET		0x100C		/* Rx packet length */
+#define XEL_RSR_OFFSET		0x17FC		/* Rx status */
+
+#define XEL_BUFFER_OFFSET	0x0800		/* Next Tx/Rx buffer's offset */
+
+/* Global Interrupt Enable Register (GIER) Bit Masks */
+#define XEL_GIER_GIE_MASK	0x80000000 	/* Global Enable */
+
+/* Transmit Status Register (TSR) Bit Masks */
+#define XEL_TSR_XMIT_BUSY_MASK	 0x00000001 	/* Tx complete */
+#define XEL_TSR_PROGRAM_MASK	 0x00000002 	/* Program the MAC address */
+#define XEL_TSR_XMIT_IE_MASK	 0x00000008 	/* Tx interrupt enable bit */
+#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000 	/* Buffer is active, SW bit
+						 * only. This is not documented
+						 * in the HW spec */
+
+/* Define for programming the MAC address into the EmacLite */
+#define XEL_TSR_PROG_MAC_ADDR	(XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK)
+
+/* Receive Status Register (RSR) */
+#define XEL_RSR_RECV_DONE_MASK	0x00000001 	/* Rx complete */
+#define XEL_RSR_RECV_IE_MASK	0x00000008 	/* Rx interrupt enable bit */
+
+/* Transmit Packet Length Register (TPLR) */
+#define XEL_TPLR_LENGTH_MASK	0x0000FFFF 	/* Tx packet length */
+
+/* Receive Packet Length Register (RPLR) */
+#define XEL_RPLR_LENGTH_MASK	0x0000FFFF 	/* Rx packet length */
+
+#define XEL_HEADER_OFFSET	12 		/* Offset to length field */
+#define XEL_HEADER_SHIFT	16 		/* Shift value for length */
+
+/* General Ethernet Definitions */
+#define XEL_ARP_PACKET_SIZE		28 	/* Max ARP packet size */
+#define XEL_HEADER_IP_LENGTH_OFFSET	16 	/* IP Length Offset */
+
+
+
+#define TX_TIMEOUT		(60*HZ)		/* Tx timeout is 60 seconds. */
+#define ALIGNMENT		4
+
+/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */
+#define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT)
+
+/**
+ * struct net_local - Our private per device data
+ * @ndev:		instance of the network device
+ * @tx_ping_pong:	indicates whether Tx Pong buffer is configured in HW
+ * @rx_ping_pong:	indicates whether Rx Pong buffer is configured in HW
+ * @next_tx_buf_to_use:	next Tx buffer to write to
+ * @next_rx_buf_to_use:	next Rx buffer to read from
+ * @base_addr:		base address of the Emaclite device
+ * @reset_lock:		lock used for synchronization
+ * @deferred_skb:	holds an skb (for transmission at a later time) when the
+ *			Tx buffer is not free
+ */
+struct net_local {
+
+	struct net_device *ndev;
+
+	bool tx_ping_pong;
+	bool rx_ping_pong;
+	u32 next_tx_buf_to_use;
+	u32 next_rx_buf_to_use;
+	void __iomem *base_addr;
+
+	spinlock_t reset_lock;
+	struct sk_buff *deferred_skb;
+};
+
+
+/*************************/
+/* EmacLite driver calls */
+/*************************/
+
+/**
+ * xemaclite_enable_interrupts - Enable the interrupts for the EmacLite device
+ * @drvdata:	Pointer to the Emaclite device private data
+ *
+ * This function enables the Tx and Rx interrupts for the Emaclite device along
+ * with the Global Interrupt Enable.
+ */
+static void xemaclite_enable_interrupts(struct net_local *drvdata)
+{
+	u32 reg_data;
+
+	/* Enable the Tx interrupts for the first Buffer */
+	reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET);
+	out_be32(drvdata->base_addr + XEL_TSR_OFFSET,
+		 reg_data | XEL_TSR_XMIT_IE_MASK);
+
+	/* Enable the Tx interrupts for the second Buffer if
+	 * configured in HW */
+	if (drvdata->tx_ping_pong != 0) {
+		reg_data = in_be32(drvdata->base_addr +
+				   XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+		out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+			 XEL_TSR_OFFSET,
+			 reg_data | XEL_TSR_XMIT_IE_MASK);
+	}
+
+	/* Enable the Rx interrupts for the first buffer */
+	reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET);
+	out_be32(drvdata->base_addr + XEL_RSR_OFFSET,
+		 reg_data | XEL_RSR_RECV_IE_MASK);
+
+	/* Enable the Rx interrupts for the second Buffer if
+	 * configured in HW */
+	if (drvdata->rx_ping_pong != 0) {
+		reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+				   XEL_RSR_OFFSET);
+		out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+			 XEL_RSR_OFFSET,
+			 reg_data | XEL_RSR_RECV_IE_MASK);
+	}
+
+	/* Enable the Global Interrupt Enable */
+	out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK);
+}
+
+/**
+ * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite device
+ * @drvdata:	Pointer to the Emaclite device private data
+ *
+ * This function disables the Tx and Rx interrupts for the Emaclite device,
+ * along with the Global Interrupt Enable.
+ */
+static void xemaclite_disable_interrupts(struct net_local *drvdata)
+{
+	u32 reg_data;
+
+	/* Disable the Global Interrupt Enable */
+	out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK);
+
+	/* Disable the Tx interrupts for the first buffer */
+	reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET);
+	out_be32(drvdata->base_addr + XEL_TSR_OFFSET,
+		 reg_data & (~XEL_TSR_XMIT_IE_MASK));
+
+	/* Disable the Tx interrupts for the second Buffer
+	 * if configured in HW */
+	if (drvdata->tx_ping_pong != 0) {
+		reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+				   XEL_TSR_OFFSET);
+		out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+			 XEL_TSR_OFFSET,
+			 reg_data & (~XEL_TSR_XMIT_IE_MASK));
+	}
+
+	/* Disable the Rx interrupts for the first buffer */
+	reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET);
+	out_be32(drvdata->base_addr + XEL_RSR_OFFSET,
+		 reg_data & (~XEL_RSR_RECV_IE_MASK));
+
+	/* Disable the Rx interrupts for the second buffer
+	 * if configured in HW */
+	if (drvdata->rx_ping_pong != 0) {
+
+		reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+				   XEL_RSR_OFFSET);
+		out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+			 XEL_RSR_OFFSET,
+			 reg_data & (~XEL_RSR_RECV_IE_MASK));
+	}
+}
+
+/**
+ * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned address
+ * @src_ptr:	Void pointer to the 16-bit aligned source address
+ * @dest_ptr:	Pointer to the 32-bit aligned destination address
+ * @length:	Number bytes to write from source to destination
+ *
+ * This function writes data from a 16-bit aligned buffer to a 32-bit aligned
+ * address in the EmacLite device.
+ */
+static void xemaclite_aligned_write(void *src_ptr, u32 *dest_ptr,
+				    unsigned length)
+{
+	u32 align_buffer;
+	u32 *to_u32_ptr;
+	u16 *from_u16_ptr, *to_u16_ptr;
+
+	to_u32_ptr = dest_ptr;
+	from_u16_ptr = (u16 *) src_ptr;
+	align_buffer = 0;
+
+	for (; length > 3; length -= 4) {
+		to_u16_ptr = (u16 *) ((void *) &align_buffer);
+		*to_u16_ptr++ = *from_u16_ptr++;
+		*to_u16_ptr++ = *from_u16_ptr++;
+
+		/* Output a word */
+		*to_u32_ptr++ = align_buffer;
+	}
+	if (length) {
+		u8 *from_u8_ptr, *to_u8_ptr;
+
+		/* Set up to output the remaining data */
+		align_buffer = 0;
+		to_u8_ptr = (u8 *) &align_buffer;
+		from_u8_ptr = (u8 *) from_u16_ptr;
+
+		/* Output the remaining data */
+		for (; length > 0; length--)
+			*to_u8_ptr++ = *from_u8_ptr++;
+
+		*to_u32_ptr = align_buffer;
+	}
+}
+
+/**
+ * xemaclite_aligned_read - Read from 32-bit aligned to 16-bit aligned buffer
+ * @src_ptr:	Pointer to the 32-bit aligned source address
+ * @dest_ptr:	Pointer to the 16-bit aligned destination address
+ * @length:	Number bytes to read from source to destination
+ *
+ * This function reads data from a 32-bit aligned address in the EmacLite device
+ * to a 16-bit aligned buffer.
+ */
+static void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr,
+				   unsigned length)
+{
+	u16 *to_u16_ptr, *from_u16_ptr;
+	u32 *from_u32_ptr;
+	u32 align_buffer;
+
+	from_u32_ptr = src_ptr;
+	to_u16_ptr = (u16 *) dest_ptr;
+
+	for (; length > 3; length -= 4) {
+		/* Copy each word into the temporary buffer */
+		align_buffer = *from_u32_ptr++;
+		from_u16_ptr = (u16 *)&align_buffer;
+
+		/* Read data from source */
+		*to_u16_ptr++ = *from_u16_ptr++;
+		*to_u16_ptr++ = *from_u16_ptr++;
+	}
+
+	if (length) {
+		u8 *to_u8_ptr, *from_u8_ptr;
+
+		/* Set up to read the remaining data */
+		to_u8_ptr = (u8 *) to_u16_ptr;
+		align_buffer = *from_u32_ptr++;
+		from_u8_ptr = (u8 *) &align_buffer;
+
+		/* Read the remaining data */
+		for (; length > 0; length--)
+			*to_u8_ptr = *from_u8_ptr;
+	}
+}
+
+/**
+ * xemaclite_send_data - Send an Ethernet frame
+ * @drvdata:	Pointer to the Emaclite device private data
+ * @data:	Pointer to the data to be sent
+ * @byte_count:	Total frame size, including header
+ *
+ * This function checks if the Tx buffer of the Emaclite device is free to send
+ * data. If so, it fills the Tx buffer with data for transmission. Otherwise, it
+ * returns an error.
+ *
+ * Return:	0 upon success or -1 if the buffer(s) are full.
+ *
+ * Note:	The maximum Tx packet size can not be more than Ethernet header
+ *		(14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS.
+ */
+static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
+			       unsigned int byte_count)
+{
+	u32 reg_data;
+	void __iomem *addr;
+
+	/* Determine the expected Tx buffer address */
+	addr = drvdata->base_addr + drvdata->next_tx_buf_to_use;
+
+	/* If the length is too large, truncate it */
+	if (byte_count > ETH_FRAME_LEN)
+		byte_count = ETH_FRAME_LEN;
+
+	/* Check if the expected buffer is available */
+	reg_data = in_be32(addr + XEL_TSR_OFFSET);
+	if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
+	     XEL_TSR_XMIT_ACTIVE_MASK)) == 0) {
+
+		/* Switch to next buffer if configured */
+		if (drvdata->tx_ping_pong != 0)
+			drvdata->next_tx_buf_to_use ^= XEL_BUFFER_OFFSET;
+	} else if (drvdata->tx_ping_pong != 0) {
+		/* If the expected buffer is full, try the other buffer,
+		 * if it is configured in HW */
+
+		addr = (void __iomem __force *)((u32 __force)addr ^
+						 XEL_BUFFER_OFFSET);
+		reg_data = in_be32(addr + XEL_TSR_OFFSET);
+
+		if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
+		     XEL_TSR_XMIT_ACTIVE_MASK)) != 0)
+			return -1; /* Buffers were full, return failure */
+	} else
+		return -1; /* Buffer was full, return failure */
+
+	/* Write the frame to the buffer */
+	xemaclite_aligned_write(data, (u32 __force *) addr, byte_count);
+
+	out_be32(addr + XEL_TPLR_OFFSET, (byte_count & XEL_TPLR_LENGTH_MASK));
+
+	/* Update the Tx Status Register to indicate that there is a
+	 * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which
+	 * is used by the interrupt handler to check whether a frame
+	 * has been transmitted */
+	reg_data = in_be32(addr + XEL_TSR_OFFSET);
+	reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK);
+	out_be32(addr + XEL_TSR_OFFSET, reg_data);
+
+	return 0;
+}
+
+/**
+ * xemaclite_recv_data - Receive a frame
+ * @drvdata:	Pointer to the Emaclite device private data
+ * @data:	Address where the data is to be received
+ *
+ * This function is intended to be called from the interrupt context or
+ * with a wrapper which waits for the receive frame to be available.
+ *
+ * Return:	Total number of bytes received
+ */
+static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
+{
+	void __iomem *addr;
+	u16 length, proto_type;
+	u32 reg_data;
+
+	/* Determine the expected buffer address */
+	addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use);
+
+	/* Verify which buffer has valid data */
+	reg_data = in_be32(addr + XEL_RSR_OFFSET);
+
+	if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) {
+		if (drvdata->rx_ping_pong != 0)
+			drvdata->next_rx_buf_to_use ^= XEL_BUFFER_OFFSET;
+	} else {
+		/* The instance is out of sync, try other buffer if other
+		 * buffer is configured, return 0 otherwise. If the instance is
+		 * out of sync, do not update the 'next_rx_buf_to_use' since it
+		 * will correct on subsequent calls */
+		if (drvdata->rx_ping_pong != 0)
+			addr = (void __iomem __force *)((u32 __force)addr ^
+							 XEL_BUFFER_OFFSET);
+		else
+			return 0;	/* No data was available */
+
+		/* Verify that buffer has valid data */
+		reg_data = in_be32(addr + XEL_RSR_OFFSET);
+		if ((reg_data & XEL_RSR_RECV_DONE_MASK) !=
+		     XEL_RSR_RECV_DONE_MASK)
+			return 0;	/* No data was available */
+	}
+
+	/* Get the protocol type of the ethernet frame that arrived */
+	proto_type = ((in_be32(addr + XEL_HEADER_OFFSET +
+			XEL_RXBUFF_OFFSET) >> XEL_HEADER_SHIFT) &
+			XEL_RPLR_LENGTH_MASK);
+
+	/* Check if received ethernet frame is a raw ethernet frame
+	 * or an IP packet or an ARP packet */
+	if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {
+
+		if (proto_type == ETH_P_IP) {
+			length = ((in_be32(addr +
+					XEL_HEADER_IP_LENGTH_OFFSET +
+					XEL_RXBUFF_OFFSET) >>
+					XEL_HEADER_SHIFT) &
+					XEL_RPLR_LENGTH_MASK);
+			length += ETH_HLEN + ETH_FCS_LEN;
+
+		} else if (proto_type == ETH_P_ARP)
+			length = XEL_ARP_PACKET_SIZE + ETH_HLEN + ETH_FCS_LEN;
+		else
+			/* Field contains type other than IP or ARP, use max
+			 * frame size and let user parse it */
+			length = ETH_FRAME_LEN + ETH_FCS_LEN;
+	} else
+		/* Use the length in the frame, plus the header and trailer */
+		length = proto_type + ETH_HLEN + ETH_FCS_LEN;
+
+	/* Read from the EmacLite device */
+	xemaclite_aligned_read((u32 __force *) (addr + XEL_RXBUFF_OFFSET),
+				data, length);
+
+	/* Acknowledge the frame */
+	reg_data = in_be32(addr + XEL_RSR_OFFSET);
+	reg_data &= ~XEL_RSR_RECV_DONE_MASK;
+	out_be32(addr + XEL_RSR_OFFSET, reg_data);
+
+	return length;
+}
+
+/**
+ * xemaclite_set_mac_address - Set the MAC address for this device
+ * @drvdata:	Pointer to the Emaclite device private data
+ * @address_ptr:Pointer to the MAC address (MAC address is a 48-bit value)
+ *
+ * Tx must be idle and Rx should be idle for deterministic results.
+ * It is recommended that this function should be called after the
+ * initialization and before transmission of any packets from the device.
+ * The MAC address can be programmed using any of the two transmit
+ * buffers (if configured).
+ */
+static void xemaclite_set_mac_address(struct net_local *drvdata,
+				      u8 *address_ptr)
+{
+	void __iomem *addr;
+	u32 reg_data;
+
+	/* Determine the expected Tx buffer address */
+	addr = drvdata->base_addr + drvdata->next_tx_buf_to_use;
+
+	xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN);
+
+	out_be32(addr + XEL_TPLR_OFFSET, ETH_ALEN);
+
+	/* Update the MAC address in the EmacLite */
+	reg_data = in_be32(addr + XEL_TSR_OFFSET);
+	out_be32(addr + XEL_TSR_OFFSET, reg_data | XEL_TSR_PROG_MAC_ADDR);
+
+	/* Wait for EmacLite to finish with the MAC address update */
+	while ((in_be32(addr + XEL_TSR_OFFSET) &
+		XEL_TSR_PROG_MAC_ADDR) != 0)
+		;
+}
+
+/**
+ * xemaclite_tx_timeout - Callback for Tx Timeout
+ * @dev:	Pointer to the network device
+ *
+ * This function is called when Tx time out occurs for Emaclite device.
+ */
+static void xemaclite_tx_timeout(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *) netdev_priv(dev);
+	unsigned long flags;
+
+	dev_err(&lp->ndev->dev, "Exceeded transmit timeout of %lu ms\n",
+		TX_TIMEOUT * 1000UL / HZ);
+
+	dev->stats.tx_errors++;
+
+	/* Reset the device */
+	spin_lock_irqsave(&lp->reset_lock, flags);
+
+	/* Shouldn't really be necessary, but shouldn't hurt */
+	netif_stop_queue(dev);
+
+	xemaclite_disable_interrupts(lp);
+	xemaclite_enable_interrupts(lp);
+
+	if (lp->deferred_skb) {
+		dev_kfree_skb(lp->deferred_skb);
+		lp->deferred_skb = NULL;
+		dev->stats.tx_errors++;
+	}
+
+	/* To exclude tx timeout */
+	dev->trans_start = 0xffffffff - TX_TIMEOUT - TX_TIMEOUT;
+
+	/* We're all ready to go. Start the queue */
+	netif_wake_queue(dev);
+	spin_unlock_irqrestore(&lp->reset_lock, flags);
+}
+
+/**********************/
+/* Interrupt Handlers */
+/**********************/
+
+/**
+ * xemaclite_tx_handler - Interrupt handler for frames sent
+ * @dev:	Pointer to the network device
+ *
+ * This function updates the number of packets transmitted and handles the
+ * deferred skb, if there is one.
+ */
+static void xemaclite_tx_handler(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *) netdev_priv(dev);
+
+	dev->stats.tx_packets++;
+	if (lp->deferred_skb) {
+		if (xemaclite_send_data(lp,
+					(u8 *) lp->deferred_skb->data,
+					lp->deferred_skb->len) != 0)
+			return;
+		else {
+			dev->stats.tx_bytes += lp->deferred_skb->len;
+			dev_kfree_skb_irq(lp->deferred_skb);
+			lp->deferred_skb = NULL;
+			dev->trans_start = jiffies;
+			netif_wake_queue(dev);
+		}
+	}
+}
+
+/**
+ * xemaclite_rx_handler- Interrupt handler for frames received
+ * @dev:	Pointer to the network device
+ *
+ * This function allocates memory for a socket buffer, fills it with data
+ * received and hands it over to the TCP/IP stack.
+ */
+static void xemaclite_rx_handler(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *) netdev_priv(dev);
+	struct sk_buff *skb;
+	unsigned int align;
+	u32 len;
+
+	len = ETH_FRAME_LEN + ETH_FCS_LEN;
+	skb = dev_alloc_skb(len + ALIGNMENT);
+	if (!skb) {
+		/* Couldn't get memory. */
+		dev->stats.rx_dropped++;
+		dev_err(&lp->ndev->dev, "Could not allocate receive buffer\n");
+		return;
+	}
+
+	/*
+	 * A new skb should have the data halfword aligned, but this code is
+	 * here just in case that isn't true. Calculate how many
+	 * bytes we should reserve to get the data to start on a word
+	 * boundary */
+	align = BUFFER_ALIGN(skb->data);
+	if (align)
+		skb_reserve(skb, align);
+
+	skb_reserve(skb, 2);
+
+	len = xemaclite_recv_data(lp, (u8 *) skb->data);
+
+	if (!len) {
+		dev->stats.rx_errors++;
+		dev_kfree_skb_irq(skb);
+		return;
+	}
+
+	skb_put(skb, len);	/* Tell the skb how much data we got */
+	skb->dev = dev;		/* Fill out required meta-data */
+
+	skb->protocol = eth_type_trans(skb, dev);
+	skb->ip_summed = CHECKSUM_NONE;
+
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += len;
+	dev->last_rx = jiffies;
+
+	netif_rx(skb);		/* Send the packet upstream */
+}
+
+/**
+ * xemaclite_interrupt - Interrupt handler for this driver
+ * @irq:	Irq of the Emaclite device
+ * @dev_id:	Void pointer to the network device instance used as callback
+ *		reference
+ *
+ * This function handles the Tx and Rx interrupts of the EmacLite device.
+ */
+static irqreturn_t xemaclite_interrupt(int irq, void *dev_id)
+{
+	bool tx_complete = 0;
+	struct net_device *dev = dev_id;
+	struct net_local *lp = (struct net_local *) netdev_priv(dev);
+	void __iomem *base_addr = lp->base_addr;
+	u32 tx_status;
+
+	/* Check if there is Rx Data available */
+	if ((in_be32(base_addr + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK) ||
+			(in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
+			 & XEL_RSR_RECV_DONE_MASK))
+
+		xemaclite_rx_handler(dev);
+
+	/* Check if the Transmission for the first buffer is completed */
+	tx_status = in_be32(base_addr + XEL_TSR_OFFSET);
+	if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
+		(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
+
+		tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
+		out_be32(base_addr + XEL_TSR_OFFSET, tx_status);
+
+		tx_complete = 1;
+	}
+
+	/* Check if the Transmission for the second buffer is completed */
+	tx_status = in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+	if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
+		(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
+
+		tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
+		out_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET,
+			 tx_status);
+
+		tx_complete = 1;
+	}
+
+	/* If there was a Tx interrupt, call the Tx Handler */
+	if (tx_complete != 0)
+		xemaclite_tx_handler(dev);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * xemaclite_open - Open the network device
+ * @dev:	Pointer to the network device
+ *
+ * This function sets the MAC address, requests an IRQ and enables interrupts
+ * for the Emaclite device and starts the Tx queue.
+ */
+static int xemaclite_open(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *) netdev_priv(dev);
+	int retval;
+
+	/* Just to be safe, stop the device first */
+	xemaclite_disable_interrupts(lp);
+
+	/* Set the MAC address each time opened */
+	xemaclite_set_mac_address(lp, dev->dev_addr);
+
+	/* Grab the IRQ */
+	retval = request_irq(dev->irq, &xemaclite_interrupt, 0, dev->name, dev);
+	if (retval) {
+		dev_err(&lp->ndev->dev, "Could not allocate interrupt %d\n",
+			dev->irq);
+		return retval;
+	}
+
+	/* Enable Interrupts */
+	xemaclite_enable_interrupts(lp);
+
+	/* We're ready to go */
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/**
+ * xemaclite_close - Close the network device
+ * @dev:	Pointer to the network device
+ *
+ * This function stops the Tx queue, disables interrupts and frees the IRQ for
+ * the Emaclite device.
+ */
+static int xemaclite_close(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *) netdev_priv(dev);
+
+	netif_stop_queue(dev);
+	xemaclite_disable_interrupts(lp);
+	free_irq(dev->irq, dev);
+
+	return 0;
+}
+
+/**
+ * xemaclite_get_stats - Get the stats for the net_device
+ * @dev:	Pointer to the network device
+ *
+ * This function returns the address of the 'net_device_stats' structure for the
+ * given network device. This structure holds usage statistics for the network
+ * device.
+ *
+ * Return:	Pointer to the net_device_stats structure.
+ */
+static struct net_device_stats *xemaclite_get_stats(struct net_device *dev)
+{
+	return &dev->stats;
+}
+
+/**
+ * xemaclite_send - Transmit a frame
+ * @orig_skb:	Pointer to the socket buffer to be transmitted
+ * @dev:	Pointer to the network device
+ *
+ * This function checks if the Tx buffer of the Emaclite device is free to send
+ * data. If so, it fills the Tx buffer with data from socket buffer data,
+ * updates the stats and frees the socket buffer. The Tx completion is signaled
+ * by an interrupt. If the Tx buffer isn't free, then the socket buffer is
+ * deferred and the Tx queue is stopped so that the deferred socket buffer can
+ * be transmitted when the Emaclite device is free to transmit data.
+ *
+ * Return:	0, always.
+ */
+static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *) netdev_priv(dev);
+	struct sk_buff *new_skb;
+	unsigned int len;
+	unsigned long flags;
+
+	len = orig_skb->len;
+
+	new_skb = orig_skb;
+
+	spin_lock_irqsave(&lp->reset_lock, flags);
+	if (xemaclite_send_data(lp, (u8 *) new_skb->data, len) != 0) {
+		/* If the Emaclite Tx buffer is busy, stop the Tx queue and
+		 * defer the skb for transmission at a later point when the
+		 * current transmission is complete */
+		netif_stop_queue(dev);
+		lp->deferred_skb = new_skb;
+		spin_unlock_irqrestore(&lp->reset_lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&lp->reset_lock, flags);
+
+	dev->stats.tx_bytes += len;
+	dev_kfree_skb(new_skb);
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
+/**
+ * xemaclite_ioctl - Perform IO Control operations on the network device
+ * @dev:	Pointer to the network device
+ * @rq:		Pointer to the interface request structure
+ * @cmd:	IOCTL command
+ *
+ * The only IOCTL operation supported by this function is setting the MAC
+ * address. An error is reported if any other operations are requested.
+ *
+ * Return:	0 to indicate success, or a negative error for failure.
+ */
+static int xemaclite_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct net_local *lp = (struct net_local *) netdev_priv(dev);
+	struct hw_addr_data *hw_addr = (struct hw_addr_data *) &rq->ifr_hwaddr;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return -EIO;
+
+	case SIOCSIFHWADDR:
+		dev_err(&lp->ndev->dev, "SIOCSIFHWADDR\n");
+
+		/* Copy MAC address in from user space */
+		copy_from_user((void __force *) dev->dev_addr,
+			       (void __user __force *) hw_addr,
+			       IFHWADDRLEN);
+		xemaclite_set_mac_address(lp, dev->dev_addr);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+/**
+ * xemaclite_remove_ndev - Free the network device
+ * @ndev:	Pointer to the network device to be freed
+ *
+ * This function un maps the IO region of the Emaclite device and frees the net
+ * device.
+ */
+static void xemaclite_remove_ndev(struct net_device *ndev)
+{
+	if (ndev) {
+		struct net_local *lp = (struct net_local *) netdev_priv(ndev);
+
+		if (lp->base_addr)
+			iounmap((void __iomem __force *) (lp->base_addr));
+		free_netdev(ndev);
+	}
+}
+
+/**
+ * get_bool - Get a parameter from the OF device
+ * @ofdev:	Pointer to OF device structure
+ * @s:		Property to be retrieved
+ *
+ * This function looks for a property in the device node and returns the value
+ * of the property if its found or 0 if the property is not found.
+ *
+ * Return:	Value of the parameter if the parameter is found, or 0 otherwise
+ */
+static bool get_bool(struct of_device *ofdev, const char *s)
+{
+	u32 *p = (u32 *)of_get_property(ofdev->node, s, NULL);
+
+	if (p) {
+		return (bool)*p;
+	} else {
+		dev_warn(&ofdev->dev, "Parameter %s not found,"
+			"defaulting to false\n", s);
+		return 0;
+	}
+}
+
+static struct net_device_ops xemaclite_netdev_ops;
+
+/**
+ * xemaclite_of_probe - Probe method for the Emaclite device.
+ * @ofdev:	Pointer to OF device structure
+ * @match:	Pointer to the structure used for matching a device
+ *
+ * This function probes for the Emaclite device in the device tree.
+ * It initializes the driver data structure and the hardware, sets the MAC
+ * address and registers the network device.
+ *
+ * Return:	0, if the driver is bound to the Emaclite device, or
+ *		a negative error if there is failure.
+ */
+static int __devinit xemaclite_of_probe(struct of_device *ofdev,
+					const struct of_device_id *match)
+{
+	struct resource r_irq; /* Interrupt resources */
+	struct resource r_mem; /* IO mem resources */
+	struct net_device *ndev = NULL;
+	struct net_local *lp = NULL;
+	struct device *dev = &ofdev->dev;
+	const void *mac_address;
+
+	int rc = 0;
+
+	dev_info(dev, "Device Tree Probing\n");
+
+	/* Get iospace for the device */
+	rc = of_address_to_resource(ofdev->node, 0, &r_mem);
+	if (rc) {
+		dev_err(dev, "invalid address\n");
+		return rc;
+	}
+
+	/* Get IRQ for the device */
+	rc = of_irq_to_resource(ofdev->node, 0, &r_irq);
+	if (rc == NO_IRQ) {
+		dev_err(dev, "no IRQ found\n");
+		return rc;
+	}
+
+	/* Create an ethernet device instance */
+	ndev = alloc_etherdev(sizeof(struct net_local));
+	if (!ndev) {
+		dev_err(dev, "Could not allocate network device\n");
+		return -ENOMEM;
+	}
+
+	dev_set_drvdata(dev, ndev);
+
+	ndev->irq = r_irq.start;
+	ndev->mem_start = r_mem.start;
+	ndev->mem_end = r_mem.end;
+
+	lp = netdev_priv(ndev);
+	lp->ndev = ndev;
+
+	if (!request_mem_region(ndev->mem_start,
+				ndev->mem_end - ndev->mem_start + 1,
+				DRIVER_NAME)) {
+		dev_err(dev, "Couldn't lock memory region at %p\n",
+			(void *)ndev->mem_start);
+		rc = -EBUSY;
+		goto error2;
+	}
+
+	/* Get the virtual base address for the device */
+	lp->base_addr = ioremap(r_mem.start, r_mem.end - r_mem.start + 1);
+	if (NULL == lp->base_addr) {
+		dev_err(dev, "EmacLite: Could not allocate iomem\n");
+		rc = -EIO;
+		goto error1;
+	}
+
+	lp->next_tx_buf_to_use = 0x0;
+	lp->next_rx_buf_to_use = 0x0;
+	lp->tx_ping_pong = get_bool(ofdev, "xlnx,tx-ping-pong");
+	lp->rx_ping_pong = get_bool(ofdev, "xlnx,rx-ping-pong");
+	mac_address = of_get_mac_address(ofdev->node);
+
+	if (mac_address)
+		/* Set the MAC address. */
+		memcpy(ndev->dev_addr, mac_address, 6);
+	else
+		dev_warn(dev, "No MAC address found\n");
+
+	/* Clear the Tx CSR's in case this is a restart */
+	out_be32(lp->base_addr + XEL_TSR_OFFSET, 0);
+	out_be32(lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, 0);
+
+	/* Set the MAC address in the EmacLite device */
+	xemaclite_set_mac_address(lp, ndev->dev_addr);
+
+	dev_info(dev,
+		 "MAC address is now %2x:%2x:%2x:%2x:%2x:%2x\n",
+		 ndev->dev_addr[0], ndev->dev_addr[1],
+		 ndev->dev_addr[2], ndev->dev_addr[3],
+		 ndev->dev_addr[4], ndev->dev_addr[5]);
+
+	ndev->netdev_ops = &xemaclite_netdev_ops;
+	ndev->flags &= ~IFF_MULTICAST;
+	ndev->watchdog_timeo = TX_TIMEOUT;
+
+	/* Finally, register the device */
+	rc = register_netdev(ndev);
+	if (rc) {
+		dev_err(dev,
+			"Cannot register network device, aborting\n");
+		goto error1;
+	}
+
+	dev_info(dev,
+		 "Xilinx EmacLite at 0x%08X mapped to 0x%08X, irq=%d\n",
+		 (unsigned int __force)ndev->mem_start,
+		 (unsigned int __force)lp->base_addr, ndev->irq);
+	return 0;
+
+error1:
+	release_mem_region(ndev->mem_start, r_mem.end - r_mem.start + 1);
+
+error2:
+	xemaclite_remove_ndev(ndev);
+	return rc;
+}
+
+/**
+ * xemaclite_of_remove - Unbind the driver from the Emaclite device.
+ * @of_dev:	Pointer to OF device structure
+ *
+ * This function is called if a device is physically removed from the system or
+ * if the driver module is being unloaded. It frees any resources allocated to
+ * the device.
+ *
+ * Return:	0, always.
+ */
+static int __devexit xemaclite_of_remove(struct of_device *of_dev)
+{
+	struct device *dev = &of_dev->dev;
+	struct net_device *ndev = dev_get_drvdata(dev);
+
+	unregister_netdev(ndev);
+
+	release_mem_region(ndev->mem_start, ndev->mem_end-ndev->mem_start + 1);
+
+	xemaclite_remove_ndev(ndev);
+
+	dev_set_drvdata(dev, NULL);
+
+	return 0;
+}
+
+static struct net_device_ops xemaclite_netdev_ops = {
+	.ndo_open		= xemaclite_open,
+	.ndo_stop		= xemaclite_close,
+	.ndo_start_xmit		= xemaclite_send,
+	.ndo_do_ioctl		= xemaclite_ioctl,
+	.ndo_tx_timeout		= xemaclite_tx_timeout,
+	.ndo_get_stats		= xemaclite_get_stats,
+};
+
+/* Match table for OF platform binding */
+static struct of_device_id xemaclite_of_match[] __devinitdata = {
+	{ .compatible = "xlnx,opb-ethernetlite-1.01.a", },
+	{ .compatible = "xlnx,opb-ethernetlite-1.01.b", },
+	{ .compatible = "xlnx,xps-ethernetlite-1.00.a", },
+	{ .compatible = "xlnx,xps-ethernetlite-2.00.a", },
+	{ .compatible = "xlnx,xps-ethernetlite-2.01.a", },
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, xemaclite_of_match);
+
+static struct of_platform_driver xemaclite_of_driver = {
+	.name		= DRIVER_NAME,
+	.match_table	= xemaclite_of_match,
+	.probe		= xemaclite_of_probe,
+	.remove		= __devexit_p(xemaclite_of_remove),
+};
+
+/**
+ * xgpiopss_init - Initial driver registration call
+ *
+ * Return:	0 upon success, or a negative error upon failure.
+ */
+static int __init xemaclite_init(void)
+{
+	/* No kernel boot options used, we just need to register the driver */
+	return of_register_platform_driver(&xemaclite_of_driver);
+}
+
+/**
+ * xemaclite_cleanup - Driver un-registration call
+ */
+static void __exit xemaclite_cleanup(void)
+{
+	of_unregister_platform_driver(&xemaclite_of_driver);
+}
+
+module_init(xemaclite_init);
+module_exit(xemaclite_cleanup);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx Ethernet MAC Lite driver");
+MODULE_LICENSE("GPL");
-- 
1.6.2.1



This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

^ permalink raw reply related

* Re: [PATCH] mpc8xxx_spi: Fix polarity inversion in chipselect
From: Michael Barkowski @ 2009-08-18 16:02 UTC (permalink / raw)
  Cc: linuxppc-dev
In-Reply-To: <4A8AC5D4.9070602@ruggedcom.com>

Anton - thanks for the offline correction.

SPI_CS_HIGH means that the device interprets high as active.
The alow flag means there is an inverter somewhere.

My mistake - patch retracted.

-- 
Michael

^ permalink raw reply

* Re: [PATCH/RFC] powerpc/mm: Cleanup handling of execute permission
From: Josh Boyer @ 2009-08-18 20:56 UTC (permalink / raw)
  To: Becky Bruce; +Cc: Kumar Gala, linuxppc-dev list
In-Reply-To: <FBB5798A-EE85-4B18-8883-F6182B203996@kernel.crashing.org>

On Fri, Aug 14, 2009 at 05:39:42PM -0500, Becky Bruce wrote:
> Ben,
>
> This breaks the boot on 8572.  I don't know why yet (and I'm probably  
> not going to figure it out before I go home, because, frankly, it's late 
> on a Friday afternoon and I need a glass of wine or, perhaps, a beer).
>
> Kumar and I will poke into this more and let you know what we find out - 
> in the meantime, if you have any brilliant flashes, pony up!

I tested this on a 440EPx NFS rootfs boot too.  It doesn't cause init itself
to crap out with a SIGILL like Becky's board, but it does do weird things
and cause a SIGILL elsewhere during my boot.

Reverting this patch from your testing branch allows things to work just fine.

josh

^ permalink raw reply

* [PATCH] qe_lib: Set gpio data before changing the direction to output
From: Michael Barkowski @ 2009-08-18 20:59 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Timur Tabi

This avoids having a short glitch if the desired initial value is not
the same as what was previously in the data register.

Signed-off-by: Michael Barkowski <michaelbarkowski@ruggedcom.com>
---
I can't think of a reason not to do this.  The data register has no
effect except when the pin is configured as an output, right?

Please enlighten me if this is not correct. The behaviour I see gels
with my thinking, but there may be a case I haven't thought of.

 arch/powerpc/sysdev/qe_lib/gpio.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c
index 3485288..e7bf136 100644
--- a/arch/powerpc/sysdev/qe_lib/gpio.c
+++ b/arch/powerpc/sysdev/qe_lib/gpio.c
@@ -107,12 +107,11 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
 
 	spin_lock_irqsave(&qe_gc->lock, flags);
 
+	qe_gpio_set(gc, gpio, val);
 	__par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_OUT, 0, 0, 0);
 
 	spin_unlock_irqrestore(&qe_gc->lock, flags);
 
-	qe_gpio_set(gc, gpio, val);
-
 	return 0;
 }
 
-- 
1.6.3.3

^ permalink raw reply related

* Re: [PATCH] net: add Xilinx emac lite device driver
From: Stephen Hemminger @ 2009-08-18 21:04 UTC (permalink / raw)
  To: John Linn
  Cc: Michal Simek, netdev, Sadanand M, davem, linuxppc-dev, jgarzik,
	John Linn, John Williams
In-Reply-To: <20090818153047.48E42ED8051@mail66-dub.bigfish.com>

On Tue, 18 Aug 2009 09:30:41 -0600
John Linn <john.linn@xilinx.com> wrote:

> +/**
> + * xemaclite_enable_interrupts - Enable the interrupts for the EmacLite device
> + * @drvdata:	Pointer to the Emaclite device private data
> + *
> + * This function enables the Tx and Rx interrupts for the Emaclite device along
> + * with the Global Interrupt Enable.
> + */
> +static void xemaclite_enable_interrupts(struct net_local *drvdata)

Docbook format is really a not necessary on local functions that
are only used in the driver.  It is fine if you want to use it, as
long as the file isn't processed by kernel make docs but
the docbook is intended for automatic generation of kernel API manuals.

-- 

^ permalink raw reply

* [PATCH v2] qe_lib: Set gpio data before changing the direction to output
From: Michael Barkowski @ 2009-08-18 21:20 UTC (permalink / raw)
  To: avorontsov; +Cc: linuxppc-dev, Timur Tabi
In-Reply-To: <20090818210805.GA1725@oksana.dev.rtsoft.ru>

This avoids having a short glitch if the desired initial value is not
the same as what was previously in the data register.

Signed-off-by: Michael Barkowski <michaelbarkowski@ruggedcom.com>
---
Anton Vorontsov wrote:
> There is a recursive locking bug: _set() takes the same spinlock.
> So you'd better move this call two lines upper. Otherwise the
> patch looks OK.
> 
> Thanks!

Thanks - here is v2.

 arch/powerpc/sysdev/qe_lib/gpio.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c
index 3485288..8e7a776 100644
--- a/arch/powerpc/sysdev/qe_lib/gpio.c
+++ b/arch/powerpc/sysdev/qe_lib/gpio.c
@@ -105,14 +105,14 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
 	struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
 	unsigned long flags;
 
+	qe_gpio_set(gc, gpio, val);
+
 	spin_lock_irqsave(&qe_gc->lock, flags);
 
 	__par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_OUT, 0, 0, 0);
 
 	spin_unlock_irqrestore(&qe_gc->lock, flags);
 
-	qe_gpio_set(gc, gpio, val);
-
 	return 0;
 }
 
-- 
1.6.3.3

^ permalink raw reply related

* Re: [PATCH] qe_lib: Set gpio data before changing the direction to output
From: Michael Barkowski @ 2009-08-18 21:23 UTC (permalink / raw)
  To: Timur Tabi; +Cc: linuxppc-dev
In-Reply-To: <4A8B183D.2030202@freescale.com>

Timur Tabi wrote:
> Michael Barkowski wrote:
> 
>> diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c
>> index 3485288..e7bf136 100644
>> --- a/arch/powerpc/sysdev/qe_lib/gpio.c
>> +++ b/arch/powerpc/sysdev/qe_lib/gpio.c
>> @@ -107,12 +107,11 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
>>  
>>  	spin_lock_irqsave(&qe_gc->lock, flags);
>>  
>> +	qe_gpio_set(gc, gpio, val);
> 
> qe_gpio_set already calls spin_lock_irqsave(), so you'll have nested spinlocks, which will lock up on SMP.
> 
> Let me guess, you didn't test this on a dual-core system?

That is correct.  See v2 and please test, YMMV, etc

-- 
Michael Barkowski

^ permalink raw reply

* Re: [PATCH v2] qe_lib: Set gpio data before changing the direction to output
From: Anton Vorontsov @ 2009-08-18 21:33 UTC (permalink / raw)
  To: Michael Barkowski; +Cc: linuxppc-dev, Timur Tabi
In-Reply-To: <4A8B1B2C.5090606@ruggedcom.com>

On Tue, Aug 18, 2009 at 05:20:44PM -0400, Michael Barkowski wrote:
> This avoids having a short glitch if the desired initial value is not
> the same as what was previously in the data register.
> 
> Signed-off-by: Michael Barkowski <michaelbarkowski@ruggedcom.com>

Acked-by: Anton Vorontsov <avorontsov@ru.mvista.com>

Thanks!

> ---
> Anton Vorontsov wrote:
> > There is a recursive locking bug: _set() takes the same spinlock.
> > So you'd better move this call two lines upper. Otherwise the
> > patch looks OK.
> > 
> > Thanks!
> 
> Thanks - here is v2.
> 
>  arch/powerpc/sysdev/qe_lib/gpio.c |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c
> index 3485288..8e7a776 100644
> --- a/arch/powerpc/sysdev/qe_lib/gpio.c
> +++ b/arch/powerpc/sysdev/qe_lib/gpio.c
> @@ -105,14 +105,14 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
>  	struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
>  	unsigned long flags;
>  
> +	qe_gpio_set(gc, gpio, val);
> +
>  	spin_lock_irqsave(&qe_gc->lock, flags);
>  
>  	__par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_OUT, 0, 0, 0);
>  
>  	spin_unlock_irqrestore(&qe_gc->lock, flags);
>  
> -	qe_gpio_set(gc, gpio, val);
> -
>  	return 0;
>  }
>  
> -- 
> 1.6.3.3
> 

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: [PATCH v2 0/6] Device table matching for SPI subsystem
From: Anton Vorontsov @ 2009-08-18 21:44 UTC (permalink / raw)
  To: Artem Bityutskiy
  Cc: Ben Dooks, David Brownell, linux-kernel, lm-sensors, linuxppc-dev,
	linux-mtd, Jean Delvare, Andrew Morton, David Woodhouse
In-Reply-To: <1249889723.19638.13.camel@localhost>

On Mon, Aug 10, 2009 at 10:35:23AM +0300, Artem Bityutskiy wrote:
> On Fri, 2009-07-31 at 04:39 +0400, Anton Vorontsov wrote:
> > Andrew,
> > 
> > This new patch set overwrites following patches:
> > 
> >   hwmon-lm70-convert-to-device-table-matching.patch
> >   hwmon-adxx-convert-to-device-table-matching.patch
> >   spi-merge-probe-and-probe_id-callbacks.patch
> >   spi-prefix-modalias-with-spi.patch
> >   of-remove-stmm25p40-alias.patch
> >   mtd-m25p80-convert-to-device-table-matching.patch
> >   spi-add-support-for-device-table-matching.patch
> 
> Are you going to send v3 and address David's comments?

No v3, but I'm going to address David's comments in a follow up
patch set where I'll change the probing code anyway.

> Do you want some of these patches to go via the MTD tree or
> they better go as a series via some other tree?

Um.. The MTD patches depend on SPI subsystem changes... If
David and Andrew are OK with SPI patches going through MTD tree,
then I'm fine with it as well.

Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: [PATCH 2/6] mtd: m25p80: Convert to device table matching
From: Anton Vorontsov @ 2009-08-18 21:44 UTC (permalink / raw)
  To: David Brownell
  Cc: Ben Dooks, linux-kernel, lm-sensors, linuxppc-dev, linux-mtd,
	Jean Delvare, Andrew Morton, David Woodhouse
In-Reply-To: <200908031954.50955.david-b@pacbell.net>

Hi David,

Thanks for the review, and sorry for the delayed response.

On Mon, Aug 03, 2009 at 07:54:50PM -0700, David Brownell wrote:
> On Thursday 30 July 2009, Anton Vorontsov wrote:
> > This patch converts the m25p80 driver so that now it uses .id_table
> > for device matching, making it properly detect devices on OpenFirmware
> > platforms (prior to this patch the driver misdetected non-JEDEC chips,
> > seeing all chips as "m25p80").
> 
> I suspect "detect" is a misnomer there.  It only "detects" JEDEC chips.

Currently the driver always tries JEDEC probing, and it wrongly "assumed"
that all non-JEDEC chips are m25p80, because an entry for m25p80 chips
had "0" in jedec id field, which isn't correct ID, but 0 is returned by
most non-JEDEC chips. Having 0 in the m25p80 entry was a hack.

> All others got explicit declarations ... so if there's misbehavior for
> other chips, it's because those declarations were poorly handled.  Maybe
> they were not properly flagged as non-JDEC

> > Also, now jedec_probe() only does jedec probing, nothing else. If it
> > is not able to detect a chip, NULL is returned and the driver fall
> > backs to the information specified by the platform (platform_data, or
> > exact ID).
> 
> I'd rather keep the warning, so there's a clue about what's really
> going on:  JEDEC chip found, but its ID is not handled.

We can't tell if the chip was actually found.

M25Px0 chips can be JEDEC and non-JEDEC, e.g. Nymonyx manufacturing
"M25P80" chips in two variants: "The RDID instruction is available only
for parts made with Technology T9HX (0.11μm), ..."

Most (but not all) non-JEDEC EEPROMs will return "0" for RDID opcode
though (in that case warning is misleading). And for the chips that
don't return 0, we shouldn't probe them with jedec at all.

[...]
> > @@ -608,32 +615,38 @@ static int __devinit m25p_probe(struct spi_device *spi)
> >  	 */
> >  	data = spi->dev.platform_data;
> >  	if (data && data->type) {
> 
> At this point I wonder why you're not changing the probe sequence
> more.

Yep, I was going to do it anyway, but for another reason: to support
CAT25 chips.

> There's a new error case of course:  new-style but data->type
> doesn't match id->name.
[...]
> > +		if (i == ARRAY_SIZE(m25p_ids) - 1) {
> 
> Better:  "if (info == NULL) ..."   You've got all the pointers
> in hand; don't use indices.
[...]
> > +			if (id != &m25p_ids[i]) {
> 
> Again, don't use indices except during the lookup.

Yep, good ideas. Though, the code will vanish anyway.

Patches on the way...

^ permalink raw reply

* [PATCH 1/2] mtd: m25p80: Rework probing/JEDEC code
From: Anton Vorontsov @ 2009-08-18 21:46 UTC (permalink / raw)
  To: Andrew Morton
  Cc: David Brownell, Artem Bityutskiy, linux-kernel, linuxppc-dev,
	linux-mtd, David Woodhouse
In-Reply-To: <20090818214449.GA12848@oksana.dev.rtsoft.ru>

Previosly the driver always tried JEDEC probing, assuming that non-JEDEC
chips will return '0'. But truly non-JEDEC chips (like CAT25) won't do
that, their behaviour on RDID command is undefined, so the driver should
not call jedec_probe() for these chips.

Also, be less strict on error conditions, don't fail to probe if JEDEC
found a chip that is different from what platform code told, instead
just print some warnings and use an information obtained via JEDEC. In
that case we should not trust partitions any longer, but they might be
still useful (i.e. they could protect some parts of the chip).

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/mtd/devices/m25p80.c |   69 ++++++++++++++++++++++++-----------------
 1 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 0d74b38..b75e319 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -581,6 +581,14 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
 	jedec = jedec << 8;
 	jedec |= id[2];
 
+	/*
+	 * Some chips (like Numonyx M25P80) have JEDEC and non-JEDEC variants,
+	 * which depend on technology process. Officially RDID command doesn't
+	 * exist for non-JEDEC chips, but for compatibility they return ID 0.
+	 */
+	if (jedec == 0)
+		return NULL;
+
 	ext_jedec = id[3] << 8 | id[4];
 
 	for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
@@ -602,7 +610,7 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
  */
 static int __devinit m25p_probe(struct spi_device *spi)
 {
-	const struct spi_device_id	*id;
+	const struct spi_device_id	*id = spi_get_device_id(spi);
 	struct flash_platform_data	*data;
 	struct m25p			*flash;
 	struct flash_info		*info;
@@ -615,41 +623,44 @@ static int __devinit m25p_probe(struct spi_device *spi)
 	 */
 	data = spi->dev.platform_data;
 	if (data && data->type) {
+		const struct spi_device_id *plat_id;
+
 		for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) {
-			id = &m25p_ids[i];
-			info = (void *)m25p_ids[i].driver_data;
-			if (strcmp(data->type, id->name))
+			plat_id = &m25p_ids[i];
+			if (strcmp(data->type, plat_id->name))
 				continue;
 			break;
 		}
 
-		/* unrecognized chip? */
-		if (i == ARRAY_SIZE(m25p_ids) - 1) {
-			DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
-					dev_name(&spi->dev), data->type);
-			info = NULL;
-
-		/* recognized; is that chip really what's there? */
-		} else if (info->jedec_id) {
-			id = jedec_probe(spi);
-
-			if (id != &m25p_ids[i]) {
-				dev_warn(&spi->dev, "found %s, expected %s\n",
-						id ? id->name : "UNKNOWN",
-						m25p_ids[i].name);
-				info = NULL;
-			}
-		}
-	} else {
-		id = jedec_probe(spi);
-		if (!id)
-			id = spi_get_device_id(spi);
-
-		info = (void *)id->driver_data;
+		if (plat_id)
+			id = plat_id;
+		else
+			dev_warn(&spi->dev, "unrecognized id %s\n", data->type);
 	}
 
-	if (!info)
-		return -ENODEV;
+	info = (void *)id->driver_data;
+
+	if (info->jedec_id) {
+		const struct spi_device_id *jid;
+
+		jid = jedec_probe(spi);
+		if (!jid) {
+			dev_info(&spi->dev, "non-JEDEC variant of %s\n",
+				 id->name);
+		} else if (jid != id) {
+			/*
+			 * JEDEC knows better, so overwrite platform ID. We
+			 * can't trust partitions any longer, but we'll let
+			 * mtd apply them anyway, since some partitions may be
+			 * marked read-only, and we don't want to lose that
+			 * information, even if it's not 100% accurate.
+			 */
+			dev_warn(&spi->dev, "found %s, expected %s\n",
+				 jid->name, id->name);
+			id = jid;
+			info = (void *)jid->driver_data;
+		}
+	}
 
 	flash = kzalloc(sizeof *flash, GFP_KERNEL);
 	if (!flash)
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 2/2] mtd: m25p80: Add support for CAT25xxx serial EEPROMs
From: Anton Vorontsov @ 2009-08-18 21:46 UTC (permalink / raw)
  To: Andrew Morton
  Cc: David Brownell, Artem Bityutskiy, linux-kernel, linuxppc-dev,
	linux-mtd, David Woodhouse
In-Reply-To: <20090818214449.GA12848@oksana.dev.rtsoft.ru>

CAT25 chips (as manufactured by On Semiconductor, previously Catalyst
Semiconductor) are similar to the original M25Px0 chips, except:

- Address width can vary (1-2 bytes, in contrast to 3 bytes in M25P
  chips). So, implement convenient m25p_addr2cmd() and m25p_cmdsz()
  calls, and place address width information into flash_info struct;

- Page size can vary, therefore we shouldn't hardcode it, so get rid
  of FLASH_PAGESIZE definition, and place the page size information
  into flash_info struct;

- CAT25 EEPROMs don't need to be erased, so add NO_ERASE flag, and
  propagate it to the mtd subsystem.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/mtd/devices/m25p80.c |  172 ++++++++++++++++++++++++------------------
 1 files changed, 97 insertions(+), 75 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index b75e319..8930266 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -29,9 +29,6 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
-
-#define FLASH_PAGESIZE		256
-
 /* Flash opcodes. */
 #define	OPCODE_WREN		0x06	/* Write enable */
 #define	OPCODE_RDSR		0x05	/* Read status register */
@@ -56,7 +53,7 @@
 
 /* Define max times to check status register before we give up. */
 #define	MAX_READY_WAIT_JIFFIES	(40 * HZ)	/* M25P16 specs 40s max chip erase */
-#define	CMD_SIZE		4
+#define	MAX_CMD_SIZE		4
 
 #ifdef CONFIG_M25PXX_USE_FAST_READ
 #define OPCODE_READ 	OPCODE_FAST_READ
@@ -73,8 +70,10 @@ struct m25p {
 	struct mutex		lock;
 	struct mtd_info		mtd;
 	unsigned		partitioned:1;
+	u16			page_size;
+	u16			addr_width;
 	u8			erase_opcode;
-	u8			command[CMD_SIZE + FAST_READ_DUMMY_BYTE];
+	u8			command[MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE];
 };
 
 static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
@@ -184,6 +183,19 @@ static int erase_chip(struct m25p *flash)
 	return 0;
 }
 
+static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd)
+{
+	/* opcode is in cmd[0] */
+	cmd[1] = addr >> (flash->addr_width * 8 -  8);
+	cmd[2] = addr >> (flash->addr_width * 8 - 16);
+	cmd[3] = addr >> (flash->addr_width * 8 - 24);
+}
+
+static int m25p_cmdsz(struct m25p *flash)
+{
+	return 1 + flash->addr_width;
+}
+
 /*
  * Erase one sector of flash memory at offset ``offset'' which is any
  * address within the sector which should be erased.
@@ -205,11 +217,9 @@ static int erase_sector(struct m25p *flash, u32 offset)
 
 	/* Set up command buffer. */
 	flash->command[0] = flash->erase_opcode;
-	flash->command[1] = offset >> 16;
-	flash->command[2] = offset >> 8;
-	flash->command[3] = offset;
+	m25p_addr2cmd(flash, offset, flash->command);
 
-	spi_write(flash->spi, flash->command, CMD_SIZE);
+	spi_write(flash->spi, flash->command, m25p_cmdsz(flash));
 
 	return 0;
 }
@@ -311,7 +321,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 	 * Should add 1 byte DUMMY_BYTE.
 	 */
 	t[0].tx_buf = flash->command;
-	t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE;
+	t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
 	spi_message_add_tail(&t[0], &m);
 
 	t[1].rx_buf = buf;
@@ -338,13 +348,11 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 
 	/* Set up the write data buffer. */
 	flash->command[0] = OPCODE_READ;
-	flash->command[1] = from >> 16;
-	flash->command[2] = from >> 8;
-	flash->command[3] = from;
+	m25p_addr2cmd(flash, from, flash->command);
 
 	spi_sync(flash->spi, &m);
 
-	*retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE;
+	*retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE;
 
 	mutex_unlock(&flash->lock);
 
@@ -382,7 +390,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
 	memset(t, 0, (sizeof t));
 
 	t[0].tx_buf = flash->command;
-	t[0].len = CMD_SIZE;
+	t[0].len = m25p_cmdsz(flash);
 	spi_message_add_tail(&t[0], &m);
 
 	t[1].tx_buf = buf;
@@ -400,41 +408,36 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 	/* Set up the opcode in the write buffer. */
 	flash->command[0] = OPCODE_PP;
-	flash->command[1] = to >> 16;
-	flash->command[2] = to >> 8;
-	flash->command[3] = to;
+	m25p_addr2cmd(flash, to, flash->command);
 
-	/* what page do we start with? */
-	page_offset = to % FLASH_PAGESIZE;
+	page_offset = to & (flash->page_size - 1);
 
 	/* do all the bytes fit onto one page? */
-	if (page_offset + len <= FLASH_PAGESIZE) {
+	if (page_offset + len <= flash->page_size) {
 		t[1].len = len;
 
 		spi_sync(flash->spi, &m);
 
-		*retlen = m.actual_length - CMD_SIZE;
+		*retlen = m.actual_length - m25p_cmdsz(flash);
 	} else {
 		u32 i;
 
 		/* the size of data remaining on the first page */
-		page_size = FLASH_PAGESIZE - page_offset;
+		page_size = flash->page_size - page_offset;
 
 		t[1].len = page_size;
 		spi_sync(flash->spi, &m);
 
-		*retlen = m.actual_length - CMD_SIZE;
+		*retlen = m.actual_length - m25p_cmdsz(flash);
 
-		/* write everything in PAGESIZE chunks */
+		/* write everything in flash->page_size chunks */
 		for (i = page_size; i < len; i += page_size) {
 			page_size = len - i;
-			if (page_size > FLASH_PAGESIZE)
-				page_size = FLASH_PAGESIZE;
+			if (page_size > flash->page_size)
+				page_size = flash->page_size;
 
 			/* write the next page to flash */
-			flash->command[1] = (to + i) >> 16;
-			flash->command[2] = (to + i) >> 8;
-			flash->command[3] = (to + i);
+			m25p_addr2cmd(flash, to + i, flash->command);
 
 			t[1].tx_buf = buf + i;
 			t[1].len = page_size;
@@ -446,7 +449,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
 			spi_sync(flash->spi, &m);
 
 			if (retlen)
-				*retlen += m.actual_length - CMD_SIZE;
+				*retlen += m.actual_length - m25p_cmdsz(flash);
 		}
 	}
 
@@ -476,16 +479,23 @@ struct flash_info {
 	unsigned	sector_size;
 	u16		n_sectors;
 
+	u16		page_size;
+	u16		addr_width;
+
 	u16		flags;
 #define	SECT_4K		0x01		/* OPCODE_BE_4K works uniformly */
+#define	M25P_NO_ERASE	0x02		/* No erase command needed */
 };
 
-#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\
+#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _page_size,	\
+		_addr_width, _flags)					\
 	((kernel_ulong_t)&(struct flash_info) {				\
 		.jedec_id = (_jedec_id),				\
 		.ext_id = (_ext_id),					\
 		.sector_size = (_sector_size),				\
 		.n_sectors = (_n_sectors),				\
+		.page_size = (_page_size),				\
+		.addr_width = (_addr_width),				\
 		.flags = (_flags),					\
 	})
 
@@ -495,63 +505,70 @@ struct flash_info {
  */
 static const struct spi_device_id m25p_ids[] = {
 	/* Atmel -- some are (confusingly) marketed as "DataFlash" */
-	{ "at25fs010",  INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) },
-	{ "at25fs040",  INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
+	{ "at25fs010",  INFO(0x1f6601, 0, 32 * 1024, 4, 256, 3, SECT_4K) },
+	{ "at25fs040",  INFO(0x1f6604, 0, 64 * 1024, 8, 256, 3, SECT_4K) },
 
-	{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) },
-	{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
+	{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, 256, 3, SECT_4K) },
+	{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, 256, 3, SECT_4K) },
 
-	{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) },
-	{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
-	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
-	{ "at26df321",  INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
+	{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024, 8, 256, 3, SECT_4K) },
+	{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, 256, 3, SECT_4K) },
+	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, 256, 3, SECT_4K) },
+	{ "at26df321",  INFO(0x1f4701, 0, 64 * 1024, 64, 256, 3, SECT_4K) },
 
 	/* Macronix */
-	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
+	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 256, 3, 0) },
 
 	/* Spansion -- single (large) sector size only, at least
 	 * for the chips listed here (without boot sectors).
 	 */
-	{ "s25sl004a",  INFO(0x010212, 0, 64 * 1024, 8, 0) },
-	{ "s25sl008a",  INFO(0x010213, 0, 64 * 1024, 16, 0) },
-	{ "s25sl016a",  INFO(0x010214, 0, 64 * 1024, 32, 0) },
-	{ "s25sl032a",  INFO(0x010215, 0, 64 * 1024, 64, 0) },
-	{ "s25sl064a",  INFO(0x010216, 0, 64 * 1024, 128, 0) },
-	{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
-	{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
+	{ "s25sl004a",  INFO(0x010212, 0, 64 * 1024, 8, 256, 3, 0) },
+	{ "s25sl008a",  INFO(0x010213, 0, 64 * 1024, 16, 256, 3, 0) },
+	{ "s25sl016a",  INFO(0x010214, 0, 64 * 1024, 32, 256, 3, 0) },
+	{ "s25sl032a",  INFO(0x010215, 0, 64 * 1024, 64, 256, 3, 0) },
+	{ "s25sl064a",  INFO(0x010216, 0, 64 * 1024, 128, 256, 3, 0) },
+	{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 256, 3, 0) },
+	{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 256, 3, 0) },
 
 	/* SST -- large erase sizes are "overlays", "sectors" are 4K */
-	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K) },
-	{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K) },
-	{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K) },
-	{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K) },
+	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, 256, 3, SECT_4K) },
+	{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, 256, 3, SECT_4K) },
+	{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, 256, 3, SECT_4K) },
+	{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, 256, 3, SECT_4K) },
 
 	/* ST Microelectronics -- newer production may have feature updates */
-	{ "m25p05",  INFO(0x202010,  0, 32 * 1024, 2, 0) },
-	{ "m25p10",  INFO(0x202011,  0, 32 * 1024, 4, 0) },
-	{ "m25p20",  INFO(0x202012,  0, 64 * 1024, 4, 0) },
-	{ "m25p40",  INFO(0x202013,  0, 64 * 1024, 8, 0) },
-	{ "m25p80",  INFO(0x202014,  0, 64 * 1024, 16, 0) },
-	{ "m25p16",  INFO(0x202015,  0, 64 * 1024, 32, 0) },
-	{ "m25p32",  INFO(0x202016,  0, 64 * 1024, 64, 0) },
-	{ "m25p64",  INFO(0x202017,  0, 64 * 1024, 128, 0) },
-	{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) },
-
-	{ "m45pe10", INFO(0x204011,  0, 64 * 1024, 2, 0) },
-	{ "m45pe80", INFO(0x204014,  0, 64 * 1024, 16, 0) },
-	{ "m45pe16", INFO(0x204015,  0, 64 * 1024, 32, 0) },
-
-	{ "m25pe80", INFO(0x208014,  0, 64 * 1024, 16, 0) },
-	{ "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SECT_4K) },
+	{ "m25p05",  INFO(0x202010,  0, 32 * 1024, 2, 256, 3, 0) },
+	{ "m25p10",  INFO(0x202011,  0, 32 * 1024, 4, 256, 3, 0) },
+	{ "m25p20",  INFO(0x202012,  0, 64 * 1024, 4, 256, 3, 0) },
+	{ "m25p40",  INFO(0x202013,  0, 64 * 1024, 8, 256, 3, 0) },
+	{ "m25p80",  INFO(0x202014,  0, 64 * 1024, 16, 256, 3, 0) },
+	{ "m25p16",  INFO(0x202015,  0, 64 * 1024, 32, 256, 3, 0) },
+	{ "m25p32",  INFO(0x202016,  0, 64 * 1024, 64, 256, 3, 0) },
+	{ "m25p64",  INFO(0x202017,  0, 64 * 1024, 128, 256, 3, 0) },
+	{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 256, 3, 0) },
+
+	{ "m45pe10", INFO(0x204011,  0, 64 * 1024, 2, 256, 3, 0) },
+	{ "m45pe80", INFO(0x204014,  0, 64 * 1024, 16, 256, 3, 0) },
+	{ "m45pe16", INFO(0x204015,  0, 64 * 1024, 32, 256, 3, 0) },
+
+	{ "m25pe80", INFO(0x208014,  0, 64 * 1024, 16, 256, 3, 0) },
+	{ "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, 256, 3, SECT_4K) },
 
 	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
-	{ "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) },
-	{ "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) },
-	{ "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) },
-	{ "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) },
-	{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
-	{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
-	{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
+	{ "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, 256, 3, SECT_4K) },
+	{ "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, 256, 3, SECT_4K) },
+	{ "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, 256, 3, SECT_4K) },
+	{ "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, 256, 3, SECT_4K) },
+	{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, 256, 3, SECT_4K) },
+	{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, 256, 3, SECT_4K) },
+	{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, 256, 3, SECT_4K) },
+
+	/* Catalyst / On Semiconductor -- non-JEDEC */
+	{ "cat25c11",  INFO(0x0, 0,   16, 8, 16, 1, M25P_NO_ERASE) },
+	{ "cat25c03",  INFO(0x0, 0,   32, 8, 16, 2, M25P_NO_ERASE) },
+	{ "cat25c09",  INFO(0x0, 0,  128, 8, 32, 2, M25P_NO_ERASE) },
+	{ "cat25c17",  INFO(0x0, 0,  256, 8, 32, 2, M25P_NO_ERASE) },
+	{ "cat25128",  INFO(0x0, 0, 2048, 8, 64, 2, M25P_NO_ERASE) },
 	{ },
 };
 MODULE_DEVICE_TABLE(spi, m25p_ids);
@@ -702,7 +719,12 @@ static int __devinit m25p_probe(struct spi_device *spi)
 		flash->mtd.erasesize = info->sector_size;
 	}
 
+	if (info->flags & M25P_NO_ERASE)
+		flash->mtd.flags |= MTD_NO_ERASE;
+
 	flash->mtd.dev.parent = &spi->dev;
+	flash->page_size = info->page_size;
+	flash->addr_width = info->addr_width;
 
 	dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
 			(long long)flash->mtd.size >> 10);
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH v2 0/8] spi_mpc8xxx: Add support for DMA transfers
From: Anton Vorontsov @ 2009-08-18 22:03 UTC (permalink / raw)
  To: David Brownell
  Cc: Greg Kroah-Hartman, linux-kernel, linuxppc-dev, spi-devel-general,
	Andrew Morton

Hi all,

In v2:

- Fix build issues in fsl_qe_udc;
- Some minor cosmetic changes in "Add support for QE DMA mode and
  CPM1/CPM2 chips" patch.


David/Greg, could you Ack drivers/usb/gadget/fsl_qe_udc.h's changes 
in "[PATCH 4/8] powerpc/qe&cpm: Implement static inline stubs for
non-QE/CPM builds", so we could merge that patch via powerpc tree?

Thanks!

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* [PATCH 1/8] powerpc/cpm: Remove SPI defines and spi structs
From: Anton Vorontsov @ 2009-08-18 22:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Greg Kroah-Hartman, linux-kernel, linuxppc-dev, spi-devel-general,
	Andrew Morton
In-Reply-To: <20090818220304.GA23640@oksana.dev.rtsoft.ru>

When cpm2.h included into spi_mpc8xxx driver, the SPI defines
in the header conflict with defines in the driver.

We don't need them in the header file, so remove them. Plus
remove "struct spi", we'll use a better version in the driver.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/include/asm/cpm1.h |   45 ---------------------------------------
 arch/powerpc/include/asm/cpm2.h |   39 ---------------------------------
 2 files changed, 0 insertions(+), 84 deletions(-)

diff --git a/arch/powerpc/include/asm/cpm1.h b/arch/powerpc/include/asm/cpm1.h
index 7685ffd..81b0119 100644
--- a/arch/powerpc/include/asm/cpm1.h
+++ b/arch/powerpc/include/asm/cpm1.h
@@ -478,51 +478,6 @@ typedef struct iic {
 	char	res2[2];	/* Reserved */
 } iic_t;
 
-/* SPI parameter RAM.
-*/
-typedef struct spi {
-	ushort	spi_rbase;	/* Rx Buffer descriptor base address */
-	ushort	spi_tbase;	/* Tx Buffer descriptor base address */
-	u_char	spi_rfcr;	/* Rx function code */
-	u_char	spi_tfcr;	/* Tx function code */
-	ushort	spi_mrblr;	/* Max receive buffer length */
-	uint	spi_rstate;	/* Internal */
-	uint	spi_rdp;	/* Internal */
-	ushort	spi_rbptr;	/* Internal */
-	ushort	spi_rbc;	/* Internal */
-	uint	spi_rxtmp;	/* Internal */
-	uint	spi_tstate;	/* Internal */
-	uint	spi_tdp;	/* Internal */
-	ushort	spi_tbptr;	/* Internal */
-	ushort	spi_tbc;	/* Internal */
-	uint	spi_txtmp;	/* Internal */
-	uint	spi_res;
-	ushort	spi_rpbase;	/* Relocation pointer */
-	ushort	spi_res2;
-} spi_t;
-
-/* SPI Mode register.
-*/
-#define SPMODE_LOOP	((ushort)0x4000)	/* Loopback */
-#define SPMODE_CI	((ushort)0x2000)	/* Clock Invert */
-#define SPMODE_CP	((ushort)0x1000)	/* Clock Phase */
-#define SPMODE_DIV16	((ushort)0x0800)	/* BRG/16 mode */
-#define SPMODE_REV	((ushort)0x0400)	/* Reversed Data */
-#define SPMODE_MSTR	((ushort)0x0200)	/* SPI Master */
-#define SPMODE_EN	((ushort)0x0100)	/* Enable */
-#define SPMODE_LENMSK	((ushort)0x00f0)	/* character length */
-#define SPMODE_LEN4	((ushort)0x0030)	/*  4 bits per char */
-#define SPMODE_LEN8	((ushort)0x0070)	/*  8 bits per char */
-#define SPMODE_LEN16	((ushort)0x00f0)	/* 16 bits per char */
-#define SPMODE_PMMSK	((ushort)0x000f)	/* prescale modulus */
-
-/* SPIE fields */
-#define SPIE_MME	0x20
-#define SPIE_TXE	0x10
-#define SPIE_BSY	0x04
-#define SPIE_TXB	0x02
-#define SPIE_RXB	0x01
-
 /*
  * RISC Controller Configuration Register definitons
  */
diff --git a/arch/powerpc/include/asm/cpm2.h b/arch/powerpc/include/asm/cpm2.h
index 990ff19..236cfa3 100644
--- a/arch/powerpc/include/asm/cpm2.h
+++ b/arch/powerpc/include/asm/cpm2.h
@@ -654,45 +654,6 @@ typedef struct iic {
 	uint	iic_txtmp;	/* Internal */
 } iic_t;
 
-/* SPI parameter RAM.
-*/
-typedef struct spi {
-	ushort	spi_rbase;	/* Rx Buffer descriptor base address */
-	ushort	spi_tbase;	/* Tx Buffer descriptor base address */
-	u_char	spi_rfcr;	/* Rx function code */
-	u_char	spi_tfcr;	/* Tx function code */
-	ushort	spi_mrblr;	/* Max receive buffer length */
-	uint	spi_rstate;	/* Internal */
-	uint	spi_rdp;	/* Internal */
-	ushort	spi_rbptr;	/* Internal */
-	ushort	spi_rbc;	/* Internal */
-	uint	spi_rxtmp;	/* Internal */
-	uint	spi_tstate;	/* Internal */
-	uint	spi_tdp;	/* Internal */
-	ushort	spi_tbptr;	/* Internal */
-	ushort	spi_tbc;	/* Internal */
-	uint	spi_txtmp;	/* Internal */
-	uint	spi_res;	/* Tx temp. */
-	uint	spi_res1[4];	/* SDMA temp. */
-} spi_t;
-
-/* SPI Mode register.
-*/
-#define SPMODE_LOOP	((ushort)0x4000)	/* Loopback */
-#define SPMODE_CI	((ushort)0x2000)	/* Clock Invert */
-#define SPMODE_CP	((ushort)0x1000)	/* Clock Phase */
-#define SPMODE_DIV16	((ushort)0x0800)	/* BRG/16 mode */
-#define SPMODE_REV	((ushort)0x0400)	/* Reversed Data */
-#define SPMODE_MSTR	((ushort)0x0200)	/* SPI Master */
-#define SPMODE_EN	((ushort)0x0100)	/* Enable */
-#define SPMODE_LENMSK	((ushort)0x00f0)	/* character length */
-#define SPMODE_PMMSK	((ushort)0x000f)	/* prescale modulus */
-
-#define SPMODE_LEN(x)	((((x)-1)&0xF)<<4)
-#define SPMODE_PM(x)	((x) &0xF)
-
-#define SPI_EB		((u_char)0x10)		/* big endian byte order */
-
 /* IDMA parameter RAM
 */
 typedef struct idma {
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 2/8] powerpc/qe&cpm2: Avoid redefinitions in CPM2 and QE headers
From: Anton Vorontsov @ 2009-08-18 22:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Greg Kroah-Hartman, linux-kernel, linuxppc-dev, spi-devel-general,
	Andrew Morton
In-Reply-To: <20090818220304.GA23640@oksana.dev.rtsoft.ru>

struct mcc defined in both immap_qe.h and immap_cpm2.h, so they will
conflic when included in a single file. The mcc struct is easy to deal
with, since it isn't used in any driver (yet), so let's just rename QE
version to qe_mcc.

The ucb_ctlr is a bit trickier, since it is used by fsl_qe_udc driver,
and the driver supports both CPM and QE UDCs, plus the QE version is
used to form a bigger immap struct.

I don't want to touch too much of USB code in this series, so for now
let's just copy most generic version into the common cpm.h header,
later we'll create cpm_usb.h where we'll place common USB structs that
are used by QE/CPM UDC and QE Host drivers (FHCI).

And as for the structs in qe.h and cpm2.h, just prefix them with qe_
and cpm_.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/include/asm/cpm.h        |   22 ++++++++++++++++++++++
 arch/powerpc/include/asm/immap_cpm2.h |    2 +-
 arch/powerpc/include/asm/immap_qe.h   |    8 ++++----
 3 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/include/asm/cpm.h b/arch/powerpc/include/asm/cpm.h
index 24d79e3..b5f1534 100644
--- a/arch/powerpc/include/asm/cpm.h
+++ b/arch/powerpc/include/asm/cpm.h
@@ -5,6 +5,28 @@
 #include <linux/types.h>
 #include <linux/of.h>
 
+/*
+ * USB Controller pram common to QE and CPM.
+ */
+struct usb_ctlr {
+	u8	usb_usmod;
+	u8	usb_usadr;
+	u8	usb_uscom;
+	u8	res1[1];
+	__be16	usb_usep[4];
+	u8	res2[4];
+	__be16	usb_usber;
+	u8	res3[2];
+	__be16	usb_usbmr;
+	u8	res4[1];
+	u8	usb_usbs;
+	/* Fields down below are QE-only */
+	__be16	usb_ussft;
+	u8	res5[2];
+	__be16	usb_usfrn;
+	u8	res6[0x22];
+} __attribute__ ((packed));
+
 /* Opcodes common to CPM1 and CPM2
 */
 #define CPM_CR_INIT_TRX		((ushort)0x0000)
diff --git a/arch/powerpc/include/asm/immap_cpm2.h b/arch/powerpc/include/asm/immap_cpm2.h
index d4f069b..7c64fda 100644
--- a/arch/powerpc/include/asm/immap_cpm2.h
+++ b/arch/powerpc/include/asm/immap_cpm2.h
@@ -549,7 +549,7 @@ typedef struct comm_proc {
 
 /* USB Controller.
 */
-typedef struct usb_ctlr {
+typedef struct cpm_usb_ctlr {
 	u8	usb_usmod;
 	u8	usb_usadr;
 	u8	usb_uscom;
diff --git a/arch/powerpc/include/asm/immap_qe.h b/arch/powerpc/include/asm/immap_qe.h
index c346d0b..4e10f50 100644
--- a/arch/powerpc/include/asm/immap_qe.h
+++ b/arch/powerpc/include/asm/immap_qe.h
@@ -210,7 +210,7 @@ struct sir {
 } __attribute__ ((packed));
 
 /* USB Controller */
-struct usb_ctlr {
+struct qe_usb_ctlr {
 	u8	usb_usmod;
 	u8	usb_usadr;
 	u8	usb_uscom;
@@ -229,7 +229,7 @@ struct usb_ctlr {
 } __attribute__ ((packed));
 
 /* MCC */
-struct mcc {
+struct qe_mcc {
 	__be32	mcce;		/* MCC event register */
 	__be32	mccm;		/* MCC mask register */
 	__be32	mccf;		/* MCC configuration register */
@@ -431,9 +431,9 @@ struct qe_immap {
 	struct qe_mux		qmx;		/* QE Multiplexer */
 	struct qe_timers	qet;		/* QE Timers */
 	struct spi		spi[0x2];	/* spi */
-	struct mcc		mcc;		/* mcc */
+	struct qe_mcc		mcc;		/* mcc */
 	struct qe_brg		brg;		/* brg */
-	struct usb_ctlr		usb;		/* USB */
+	struct qe_usb_ctlr	usb;		/* USB */
 	struct si1		si1;		/* SI */
 	u8			res11[0x800];
 	struct sir		sir;		/* SI Routing Tables */
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 3/8] powerpc/cpm: Move CPMFCR_* defines into cpm.h
From: Anton Vorontsov @ 2009-08-18 22:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Greg Kroah-Hartman, linux-kernel, linuxppc-dev, spi-devel-general,
	Andrew Morton
In-Reply-To: <20090818220304.GA23640@oksana.dev.rtsoft.ru>

The bits are generic to CPM devices, so let's move them to the
common header file, so drivers won't need to privately reintroduce
another bunch of the same bits (as we can't include cpm2.h header
together with cpm1.h).

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/include/asm/cpm.h  |   16 ++++++++++++++++
 arch/powerpc/include/asm/cpm2.h |    8 --------
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/include/asm/cpm.h b/arch/powerpc/include/asm/cpm.h
index b5f1534..ea3fdb9 100644
--- a/arch/powerpc/include/asm/cpm.h
+++ b/arch/powerpc/include/asm/cpm.h
@@ -27,6 +27,22 @@ struct usb_ctlr {
 	u8	res6[0x22];
 } __attribute__ ((packed));
 
+/*
+ * Function code bits, usually generic to devices.
+ */
+#ifdef CONFIG_CPM1
+#define CPMFCR_GBL	((u_char)0x00)	/* Flag doesn't exist in CPM1 */
+#define CPMFCR_TC2	((u_char)0x00)	/* Flag doesn't exist in CPM1 */
+#define CPMFCR_DTB	((u_char)0x00)	/* Flag doesn't exist in CPM1 */
+#define CPMFCR_BDB	((u_char)0x00)	/* Flag doesn't exist in CPM1 */
+#else
+#define CPMFCR_GBL	((u_char)0x20)	/* Set memory snooping */
+#define CPMFCR_TC2	((u_char)0x04)	/* Transfer code 2 value */
+#define CPMFCR_DTB	((u_char)0x02)	/* Use local bus for data when set */
+#define CPMFCR_BDB	((u_char)0x01)	/* Use local bus for BD when set */
+#endif
+#define CPMFCR_EB	((u_char)0x10)	/* Set big endian byte order */
+
 /* Opcodes common to CPM1 and CPM2
 */
 #define CPM_CR_INIT_TRX		((ushort)0x0000)
diff --git a/arch/powerpc/include/asm/cpm2.h b/arch/powerpc/include/asm/cpm2.h
index 236cfa3..f42e9ba 100644
--- a/arch/powerpc/include/asm/cpm2.h
+++ b/arch/powerpc/include/asm/cpm2.h
@@ -124,14 +124,6 @@ static inline void cpm2_fastbrg(uint brg, uint rate, int div16)
 	__cpm2_setbrg(brg, rate, CPM2_BRG_INT_CLK, div16, CPM_BRG_EXTC_INT);
 }
 
-/* Function code bits, usually generic to devices.
-*/
-#define CPMFCR_GBL	((u_char)0x20)	/* Set memory snooping */
-#define CPMFCR_EB	((u_char)0x10)	/* Set big endian byte order */
-#define CPMFCR_TC2	((u_char)0x04)	/* Transfer code 2 value */
-#define CPMFCR_DTB	((u_char)0x02)	/* Use local bus for data when set */
-#define CPMFCR_BDB	((u_char)0x01)	/* Use local bus for BD when set */
-
 /* Parameter RAM offsets from the base.
 */
 #define PROFF_SCC1		((uint)0x8000)
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 4/8] powerpc/qe&cpm: Implement static inline stubs for non-QE/CPM builds
From: Anton Vorontsov @ 2009-08-18 22:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Greg Kroah-Hartman, linux-kernel, linuxppc-dev, spi-devel-general,
	Andrew Morton
In-Reply-To: <20090818220304.GA23640@oksana.dev.rtsoft.ru>

This is needed to avoid ugly #ifdefs in drivers. Also update fsl_qe_udc
driver so that now it doesn't define its own versions that cause build
breakage when the generic stubs are used.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/include/asm/cpm.h  |   44 +++++++++++++++++++++++++++++++++++++++
 arch/powerpc/include/asm/qe.h   |   11 ++++++++-
 drivers/usb/gadget/fsl_qe_udc.h |   15 -------------
 3 files changed, 54 insertions(+), 16 deletions(-)

diff --git a/arch/powerpc/include/asm/cpm.h b/arch/powerpc/include/asm/cpm.h
index ea3fdb9..0835eb9 100644
--- a/arch/powerpc/include/asm/cpm.h
+++ b/arch/powerpc/include/asm/cpm.h
@@ -3,6 +3,7 @@
 
 #include <linux/compiler.h>
 #include <linux/types.h>
+#include <linux/errno.h>
 #include <linux/of.h>
 
 /*
@@ -131,13 +132,56 @@ typedef struct cpm_buf_desc {
 #define BD_I2C_START		(0x0400)
 
 int cpm_muram_init(void);
+
+#if defined(CONFIG_CPM) || defined(CONFIG_QUICC_ENGINE)
 unsigned long cpm_muram_alloc(unsigned long size, unsigned long align);
 int cpm_muram_free(unsigned long offset);
 unsigned long cpm_muram_alloc_fixed(unsigned long offset, unsigned long size);
 void __iomem *cpm_muram_addr(unsigned long offset);
 unsigned long cpm_muram_offset(void __iomem *addr);
 dma_addr_t cpm_muram_dma(void __iomem *addr);
+#else
+static inline unsigned long cpm_muram_alloc(unsigned long size,
+					    unsigned long align)
+{
+	return -ENOSYS;
+}
+
+static inline int cpm_muram_free(unsigned long offset)
+{
+	return -ENOSYS;
+}
+
+static inline unsigned long cpm_muram_alloc_fixed(unsigned long offset,
+						  unsigned long size)
+{
+	return -ENOSYS;
+}
+
+static inline void __iomem *cpm_muram_addr(unsigned long offset)
+{
+	return NULL;
+}
+
+static inline unsigned long cpm_muram_offset(void __iomem *addr)
+{
+	return -ENOSYS;
+}
+
+static inline dma_addr_t cpm_muram_dma(void __iomem *addr)
+{
+	return 0;
+}
+#endif /* defined(CONFIG_CPM) || defined(CONFIG_QUICC_ENGINE) */
+
+#ifdef CONFIG_CPM
 int cpm_command(u32 command, u8 opcode);
+#else
+static inline int cpm_command(u32 command, u8 opcode)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_CPM */
 
 int cpm2_gpiochip_add32(struct device_node *np);
 
diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h
index 157c5ca..791c67a 100644
--- a/arch/powerpc/include/asm/qe.h
+++ b/arch/powerpc/include/asm/qe.h
@@ -145,8 +145,17 @@ static inline void qe_pin_set_gpio(struct qe_pin *qe_pin) {}
 static inline void qe_pin_set_dedicated(struct qe_pin *pin) {}
 #endif /* CONFIG_QE_GPIO */
 
-/* QE internal API */
+#ifdef CONFIG_QUICC_ENGINE
 int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input);
+#else
+static inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol,
+			       u32 cmd_input)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_QUICC_ENGINE */
+
+/* QE internal API */
 enum qe_clock qe_clock_source(const char *source);
 unsigned int qe_get_brg_clk(void);
 int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier);
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
index 31b2710..bea5b82 100644
--- a/drivers/usb/gadget/fsl_qe_udc.h
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -419,19 +419,4 @@ struct qe_udc {
 #define CPM_USB_RESTART_TX_OPCODE 0x0b
 #define CPM_USB_EP_SHIFT 5
 
-#ifndef CONFIG_CPM
-inline int cpm_command(u32 command, u8 opcode)
-{
-	return -EOPNOTSUPP;
-}
-#endif
-
-#ifndef CONFIG_QUICC_ENGINE
-inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol,
-	u32 cmd_input)
-{
-	return -EOPNOTSUPP;
-}
-#endif
-
 #endif  /* __FSL_QE_UDC_H */
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 5/8] spi_mpc8xxx: Fix uninitialized variable
From: Anton Vorontsov @ 2009-08-18 22:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Greg Kroah-Hartman, linux-kernel, linuxppc-dev, spi-devel-general,
	Andrew Morton
In-Reply-To: <20090818220304.GA23640@oksana.dev.rtsoft.ru>

This patch fixes the following warning:

CC      drivers/spi/spi_mpc8xxx.o
  spi_mpc8xxx.c: In function 'of_mpc8xxx_spi_probe':
  spi_mpc8xxx.c:681: warning: 'ret' may be used uninitialized in this function

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/spi/spi_mpc8xxx.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 0fd0ec4..518671b 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -709,6 +709,7 @@ static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
 		gpio = of_get_gpio_flags(np, i, &flags);
 		if (!gpio_is_valid(gpio)) {
 			dev_err(dev, "invalid gpio #%d: %d\n", i, gpio);
+			ret = gpio;
 			goto err_loop;
 		}
 
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 6/8] spi_mpc8xxx: Factor out SPI mode change steps into a call
From: Anton Vorontsov @ 2009-08-18 22:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Greg Kroah-Hartman, linux-kernel, linuxppc-dev, spi-devel-general,
	Andrew Morton
In-Reply-To: <20090818220304.GA23640@oksana.dev.rtsoft.ru>

We'll add more steps soon, so get rid of the duplication.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/spi/spi_mpc8xxx.c |   56 +++++++++++++++++++-------------------------
 1 files changed, 24 insertions(+), 32 deletions(-)

diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 518671b..4b119ea 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -155,6 +155,26 @@ MPC83XX_SPI_TX_BUF(u8)
 MPC83XX_SPI_TX_BUF(u16)
 MPC83XX_SPI_TX_BUF(u32)
 
+static void mpc8xxx_spi_change_mode(struct spi_device *spi)
+{
+	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+	struct spi_mpc8xxx_cs *cs = spi->controller_state;
+	__be32 __iomem *mode = &mspi->base->mode;
+	unsigned long flags;
+
+	if (cs->hw_mode == mpc8xxx_spi_read_reg(mode))
+		return;
+
+	/* Turn off IRQs locally to minimize time that SPI is disabled. */
+	local_irq_save(flags);
+
+	/* Turn off SPI unit prior changing mode */
+	mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE);
+	mpc8xxx_spi_write_reg(mode, cs->hw_mode);
+
+	local_irq_restore(flags);
+}
+
 static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
 {
 	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
@@ -168,27 +188,13 @@ static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
 	}
 
 	if (value == BITBANG_CS_ACTIVE) {
-		u32 regval = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
-
 		mpc8xxx_spi->rx_shift = cs->rx_shift;
 		mpc8xxx_spi->tx_shift = cs->tx_shift;
 		mpc8xxx_spi->get_rx = cs->get_rx;
 		mpc8xxx_spi->get_tx = cs->get_tx;
 
-		if (cs->hw_mode != regval) {
-			unsigned long flags;
-			__be32 __iomem *mode = &mpc8xxx_spi->base->mode;
-
-			regval = cs->hw_mode;
-			/* Turn off IRQs locally to minimize time that
-			 * SPI is disabled
-			 */
-			local_irq_save(flags);
-			/* Turn off SPI unit prior changing mode */
-			mpc8xxx_spi_write_reg(mode, regval & ~SPMODE_ENABLE);
-			mpc8xxx_spi_write_reg(mode, regval);
-			local_irq_restore(flags);
-		}
+		mpc8xxx_spi_change_mode(spi);
+
 		if (pdata->cs_control)
 			pdata->cs_control(spi, pol);
 	}
@@ -198,7 +204,6 @@ static
 int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 {
 	struct mpc8xxx_spi *mpc8xxx_spi;
-	u32 regval;
 	u8 bits_per_word, pm;
 	u32 hz;
 	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
@@ -286,21 +291,8 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 		pm--;
 
 	cs->hw_mode |= SPMODE_PM(pm);
-	regval =  mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
-	if (cs->hw_mode != regval) {
-		unsigned long flags;
-		__be32 __iomem *mode = &mpc8xxx_spi->base->mode;
-
-		regval = cs->hw_mode;
-		/* Turn off IRQs locally to minimize time
-		 * that SPI is disabled
-		 */
-		local_irq_save(flags);
-		/* Turn off SPI unit prior changing mode */
-		mpc8xxx_spi_write_reg(mode, regval & ~SPMODE_ENABLE);
-		mpc8xxx_spi_write_reg(mode, regval);
-		local_irq_restore(flags);
-	}
+
+	mpc8xxx_spi_change_mode(spi);
 	return 0;
 }
 
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 7/8] spi_mpc8xxx: Turn qe_mode into flags
From: Anton Vorontsov @ 2009-08-18 22:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Greg Kroah-Hartman, linux-kernel, linuxppc-dev, spi-devel-general,
	Andrew Morton
In-Reply-To: <20090818220304.GA23640@oksana.dev.rtsoft.ru>

Soon there will be more flags introduced in subsequent patches, so
let's turn qe_mode into flags.

Also introduce mpc8xxx_spi_strmode() and print current SPI mode.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/spi/spi_mpc8xxx.c   |   30 +++++++++++++++++++-----------
 include/linux/fsl_devices.h |    2 +-
 2 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 4b119ea..80374df 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -96,7 +96,8 @@ struct mpc8xxx_spi {
 	u32 rx_shift;		/* RX data reg shift when in qe mode */
 	u32 tx_shift;		/* TX data reg shift when in qe mode */
 
-	bool qe_mode;
+	unsigned int flags;
+#define SPI_QE_CPU_MODE		(1 << 0) /* QE CPU ("PIO") mode */
 
 	struct workqueue_struct *workqueue;
 	struct work_struct work;
@@ -235,14 +236,14 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 	if (bits_per_word <= 8) {
 		cs->get_rx = mpc8xxx_spi_rx_buf_u8;
 		cs->get_tx = mpc8xxx_spi_tx_buf_u8;
-		if (mpc8xxx_spi->qe_mode) {
+		if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
 			cs->rx_shift = 16;
 			cs->tx_shift = 24;
 		}
 	} else if (bits_per_word <= 16) {
 		cs->get_rx = mpc8xxx_spi_rx_buf_u16;
 		cs->get_tx = mpc8xxx_spi_tx_buf_u16;
-		if (mpc8xxx_spi->qe_mode) {
+		if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
 			cs->rx_shift = 16;
 			cs->tx_shift = 16;
 		}
@@ -252,7 +253,8 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 	} else
 		return -EINVAL;
 
-	if (mpc8xxx_spi->qe_mode && spi->mode & SPI_LSB_FIRST) {
+	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE &&
+			spi->mode & SPI_LSB_FIRST) {
 		cs->tx_shift = 0;
 		if (bits_per_word <= 8)
 			cs->rx_shift = 8;
@@ -518,6 +520,13 @@ static void mpc8xxx_spi_cleanup(struct spi_device *spi)
 	kfree(spi->controller_state);
 }
 
+static const char *mpc8xxx_spi_strmode(unsigned int flags)
+{
+	if (flags & SPI_QE_CPU_MODE)
+		return "QE CPU";
+	return "CPU";
+}
+
 static struct spi_master * __devinit
 mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
 {
@@ -544,14 +553,14 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
 	master->cleanup = mpc8xxx_spi_cleanup;
 
 	mpc8xxx_spi = spi_master_get_devdata(master);
-	mpc8xxx_spi->qe_mode = pdata->qe_mode;
 	mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
 	mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
+	mpc8xxx_spi->flags = pdata->flags;
 	mpc8xxx_spi->spibrg = pdata->sysclk;
 
 	mpc8xxx_spi->rx_shift = 0;
 	mpc8xxx_spi->tx_shift = 0;
-	if (mpc8xxx_spi->qe_mode) {
+	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
 		mpc8xxx_spi->rx_shift = 16;
 		mpc8xxx_spi->tx_shift = 24;
 	}
@@ -584,7 +593,7 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
 
 	/* Enable SPI interface */
 	regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
-	if (pdata->qe_mode)
+	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
 		regval |= SPMODE_OP;
 
 	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
@@ -604,9 +613,8 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
 	if (ret < 0)
 		goto unreg_master;
 
-	printk(KERN_INFO
-	       "%s: MPC8xxx SPI Controller driver at 0x%p (irq = %d)\n",
-	       dev_name(dev), mpc8xxx_spi->base, mpc8xxx_spi->irq);
+	dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base,
+		 mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
 
 	return master;
 
@@ -797,7 +805,7 @@ static int __devinit of_mpc8xxx_spi_probe(struct of_device *ofdev,
 
 	prop = of_get_property(np, "mode", NULL);
 	if (prop && !strcmp(prop, "cpu-qe"))
-		pdata->qe_mode = 1;
+		pdata->flags = SPI_QE_CPU_MODE;
 
 	ret = of_mpc8xxx_spi_get_chipselects(dev);
 	if (ret)
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 43fc95d..39fd946 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -74,7 +74,7 @@ struct spi_device;
 struct fsl_spi_platform_data {
 	u32 	initial_spmode;	/* initial SPMODE value */
 	s16	bus_num;
-	bool	qe_mode;
+	unsigned int flags;
 	/* board specific information */
 	u16	max_chipselect;
 	void	(*cs_control)(struct spi_device *spi, bool on);
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 8/8] spi_mpc8xxx: Add support for QE DMA mode and CPM1/CPM2 chips
From: Anton Vorontsov @ 2009-08-18 22:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Greg Kroah-Hartman, linux-kernel, linuxppc-dev, spi-devel-general,
	Andrew Morton
In-Reply-To: <20090818220304.GA23640@oksana.dev.rtsoft.ru>

This patch adds QE buffer descriptors mode support for the
spi_mpc8xxx driver, and as a side effect we now support CPM1
and CPM2 SPI controllers.

That means that today we support almost all MPC SPI controllers:

- MPC834x-style controllers (support PIO mode only);
- CPM1 and CPM2 controllers (support DMA mode only);
- QE SPI controllers in CPU mode (PIO mode with shift quirks);
- QE SPI controllers in buffer descriptors (DMA) mode;

The only controller we don't currently support is a newer eSPI
(with a dedicated chip selects and a bit different registers map).

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/spi/Kconfig       |    3 -
 drivers/spi/spi_mpc8xxx.c |  540 +++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 500 insertions(+), 43 deletions(-)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 2c733c2..b22a4b4 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -146,9 +146,6 @@ config SPI_MPC8xxx
 	  This enables using the Freescale MPC8xxx SPI controllers in master
 	  mode.
 
-	  This driver uses a simple set of shift registers for data (opposed
-	  to the CPM based descriptor model).
-
 config SPI_OMAP_UWIRE
 	tristate "OMAP1 MicroWire"
 	depends on ARCH_OMAP1
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 80374df..394b658 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -5,6 +5,10 @@
  *
  * Copyright (C) 2006 Polycom, Inc.
  *
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009  MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
  * This program is free software; you can redistribute  it and/or modify it
  * under  the terms of  the GNU General  Public License as published by the
  * Free Software Foundation;  either version 2 of the  License, or (at your
@@ -27,6 +31,9 @@
 #include <linux/spi/spi_bitbang.h>
 #include <linux/platform_device.h>
 #include <linux/fsl_devices.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/gpio.h>
@@ -34,8 +41,19 @@
 #include <linux/of_spi.h>
 
 #include <sysdev/fsl_soc.h>
+#include <asm/cpm.h>
+#include <asm/qe.h>
 #include <asm/irq.h>
 
+/* CPM1 and CPM2 are mutually exclusive. */
+#ifdef CONFIG_CPM1
+#include <asm/cpm1.h>
+#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0)
+#else
+#include <asm/cpm2.h>
+#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0)
+#endif
+
 /* SPI Controller registers */
 struct mpc8xxx_spi_reg {
 	u8 res1[0x20];
@@ -47,6 +65,28 @@ struct mpc8xxx_spi_reg {
 	__be32 receive;
 };
 
+/* SPI Parameter RAM */
+struct spi_pram {
+	__be16	rbase;	/* Rx Buffer descriptor base address */
+	__be16	tbase;	/* Tx Buffer descriptor base address */
+	u8	rfcr;	/* Rx function code */
+	u8	tfcr;	/* Tx function code */
+	__be16	mrblr;	/* Max receive buffer length */
+	__be32	rstate;	/* Internal */
+	__be32	rdp;	/* Internal */
+	__be16	rbptr;	/* Internal */
+	__be16	rbc;	/* Internal */
+	__be32	rxtmp;	/* Internal */
+	__be32	tstate;	/* Internal */
+	__be32	tdp;	/* Internal */
+	__be16	tbptr;	/* Internal */
+	__be16	tbc;	/* Internal */
+	__be32	txtmp;	/* Internal */
+	__be32	res;	/* Tx temp. */
+	__be16  rpbase;	/* Relocation pointer (CPM1 only) */
+	__be16	res1;	/* Reserved */
+};
+
 /* SPI Controller mode register definitions */
 #define	SPMODE_LOOP		(1 << 30)
 #define	SPMODE_CI_INACTIVEHIGH	(1 << 29)
@@ -75,14 +115,40 @@ struct mpc8xxx_spi_reg {
 #define	SPIM_NE		0x00000200	/* Not empty */
 #define	SPIM_NF		0x00000100	/* Not full */
 
+#define	SPIE_TXB	0x00000200	/* Last char is written to tx fifo */
+#define	SPIE_RXB	0x00000100	/* Last char is written to rx buf */
+
+/* SPCOM register values */
+#define	SPCOM_STR	(1 << 23)	/* Start transmit */
+
+#define	SPI_PRAM_SIZE	0x100
+#define	SPI_MRBLR	((unsigned int)PAGE_SIZE)
+
 /* SPI Controller driver's private data. */
 struct mpc8xxx_spi {
+	struct device *dev;
 	struct mpc8xxx_spi_reg __iomem *base;
 
 	/* rx & tx bufs from the spi_transfer */
 	const void *tx;
 	void *rx;
 
+	int subblock;
+	struct spi_pram __iomem *pram;
+	struct cpm_buf_desc __iomem *tx_bd;
+	struct cpm_buf_desc __iomem *rx_bd;
+
+	struct spi_transfer *xfer_in_progress;
+
+	/* dma addresses for CPM transfers */
+	dma_addr_t tx_dma;
+	dma_addr_t rx_dma;
+	bool map_tx_dma;
+	bool map_rx_dma;
+
+	dma_addr_t dma_dummy_tx;
+	dma_addr_t dma_dummy_rx;
+
 	/* functions to deal with different sized buffers */
 	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
 	u32(*get_tx) (struct mpc8xxx_spi *);
@@ -98,6 +164,10 @@ struct mpc8xxx_spi {
 
 	unsigned int flags;
 #define SPI_QE_CPU_MODE		(1 << 0) /* QE CPU ("PIO") mode */
+#define SPI_CPM_MODE		(1 << 1) /* CPM/QE ("DMA") mode */
+#define SPI_CPM1		(1 << 2) /* SPI unit is in CPM1 block */
+#define SPI_CPM2		(1 << 3) /* SPI unit is in CPM2 block */
+#define SPI_QE			(1 << 4) /* SPI unit is in QE block */
 
 	struct workqueue_struct *workqueue;
 	struct work_struct work;
@@ -108,6 +178,10 @@ struct mpc8xxx_spi {
 	struct completion done;
 };
 
+static void *mpc8xxx_dummy_rx;
+static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock);
+static int mpc8xxx_dummy_rx_refcnt;
+
 struct spi_mpc8xxx_cs {
 	/* functions to deal with different sized buffers */
 	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
@@ -173,6 +247,22 @@ static void mpc8xxx_spi_change_mode(struct spi_device *spi)
 	mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE);
 	mpc8xxx_spi_write_reg(mode, cs->hw_mode);
 
+	/* When in CPM mode, we need to reinit tx and rx. */
+	if (mspi->flags & SPI_CPM_MODE) {
+		if (mspi->flags & SPI_QE) {
+			qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock,
+				     QE_CR_PROTOCOL_UNSPECIFIED, 0);
+		} else {
+			cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX);
+			if (mspi->flags & SPI_CPM1) {
+				out_be16(&mspi->pram->rbptr,
+					 in_be16(&mspi->pram->rbase));
+				out_be16(&mspi->pram->tbptr,
+					 in_be16(&mspi->pram->tbase));
+			}
+		}
+	}
+
 	local_irq_restore(flags);
 }
 
@@ -298,19 +388,133 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 	return 0;
 }
 
-static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
+static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
 {
-	struct mpc8xxx_spi *mpc8xxx_spi;
-	u32 word, len, bits_per_word;
+	struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
+	struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
+	unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
+	unsigned int xfer_ofs;
 
-	mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
+
+	out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs);
+	out_be16(&rx_bd->cbd_datlen, 0);
+	out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP);
+
+	out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs);
+	out_be16(&tx_bd->cbd_datlen, xfer_len);
+	out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP |
+				 BD_SC_LAST);
+
+	/* start transfer */
+	mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR);
+}
+
+static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
+				struct spi_transfer *t, bool is_dma_mapped)
+{
+	struct device *dev = mspi->dev;
+
+	if (is_dma_mapped) {
+		mspi->map_tx_dma = 0;
+		mspi->map_rx_dma = 0;
+	} else {
+		mspi->map_tx_dma = 1;
+		mspi->map_rx_dma = 1;
+	}
+
+	if (!t->tx_buf) {
+		mspi->tx_dma = mspi->dma_dummy_tx;
+		mspi->map_tx_dma = 0;
+	}
+
+	if (!t->rx_buf) {
+		mspi->rx_dma = mspi->dma_dummy_rx;
+		mspi->map_rx_dma = 0;
+	}
+
+	if (mspi->map_tx_dma) {
+		void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */
+
+		mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len,
+					      DMA_TO_DEVICE);
+		if (dma_mapping_error(dev, mspi->tx_dma)) {
+			dev_err(dev, "unable to map tx dma\n");
+			return -ENOMEM;
+		}
+	} else {
+		mspi->tx_dma = t->tx_dma;
+	}
+
+	if (mspi->map_rx_dma) {
+		mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len,
+					      DMA_FROM_DEVICE);
+		if (dma_mapping_error(dev, mspi->rx_dma)) {
+			dev_err(dev, "unable to map rx dma\n");
+			goto err_rx_dma;
+		}
+	} else {
+		mspi->rx_dma = t->rx_dma;
+	}
+
+	/* enable rx ints */
+	mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB);
+
+	mspi->xfer_in_progress = t;
+	mspi->count = t->len;
+
+	/* start CPM transfers */
+	mpc8xxx_spi_cpm_bufs_start(mspi);
+
+	return 0;
+
+err_rx_dma:
+	if (mspi->map_tx_dma)
+		dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
+	return -ENOMEM;
+}
+
+static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
+{
+	struct device *dev = mspi->dev;
+	struct spi_transfer *t = mspi->xfer_in_progress;
+
+	if (mspi->map_tx_dma)
+		dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
+	if (mspi->map_tx_dma)
+		dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE);
+	mspi->xfer_in_progress = NULL;
+}
+
+static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
+				struct spi_transfer *t, unsigned int len)
+{
+	u32 word;
+
+	mspi->count = len;
+
+	/* enable rx ints */
+	mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE);
+
+	/* transmit word */
+	word = mspi->get_tx(mspi);
+	mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+
+	return 0;
+}
+
+static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
+			    bool is_dma_mapped)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	unsigned int len = t->len;
+	u8 bits_per_word;
+	int ret;
 
-	mpc8xxx_spi->tx = t->tx_buf;
-	mpc8xxx_spi->rx = t->rx_buf;
 	bits_per_word = spi->bits_per_word;
 	if (t->bits_per_word)
 		bits_per_word = t->bits_per_word;
-	len = t->len;
+
 	if (bits_per_word > 8) {
 		/* invalid length? */
 		if (len & 1)
@@ -323,22 +527,27 @@ static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
 			return -EINVAL;
 		len /= 2;
 	}
-	mpc8xxx_spi->count = len;
 
-	INIT_COMPLETION(mpc8xxx_spi->done);
+	mpc8xxx_spi->tx = t->tx_buf;
+	mpc8xxx_spi->rx = t->rx_buf;
 
-	/* enable rx ints */
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, SPIM_NE);
+	INIT_COMPLETION(mpc8xxx_spi->done);
 
-	/* transmit word */
-	word = mpc8xxx_spi->get_tx(mpc8xxx_spi);
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->transmit, word);
+	if (mpc8xxx_spi->flags & SPI_CPM_MODE)
+		ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
+	else
+		ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len);
+	if (ret)
+		return ret;
 
 	wait_for_completion(&mpc8xxx_spi->done);
 
 	/* disable rx ints */
 	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
 
+	if (mpc8xxx_spi->flags & SPI_CPM_MODE)
+		mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi);
+
 	return mpc8xxx_spi->count;
 }
 
@@ -369,7 +578,7 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
 		}
 		cs_change = t->cs_change;
 		if (t->len)
-			status = mpc8xxx_spi_bufs(spi, t);
+			status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
 		if (status) {
 			status = -EMSGSIZE;
 			break;
@@ -458,45 +667,80 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
 	return 0;
 }
 
-static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
 {
-	struct mpc8xxx_spi *mpc8xxx_spi = context_data;
-	u32 event;
-	irqreturn_t ret = IRQ_NONE;
+	u16 len;
 
-	/* Get interrupt events(tx/rx) */
-	event = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->event);
+	dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
+		in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
 
-	/* We need handle RX first */
-	if (event & SPIE_NE) {
-		u32 rx_data = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->receive);
+	len = in_be16(&mspi->rx_bd->cbd_datlen);
+	if (len > mspi->count) {
+		WARN_ON(1);
+		len = mspi->count;
+	}
 
-		if (mpc8xxx_spi->rx)
-			mpc8xxx_spi->get_rx(rx_data, mpc8xxx_spi);
+	/* Clear the events */
+	mpc8xxx_spi_write_reg(&mspi->base->event, events);
 
-		ret = IRQ_HANDLED;
+	mspi->count -= len;
+	if (mspi->count)
+		mpc8xxx_spi_cpm_bufs_start(mspi);
+	else
+		complete(&mspi->done);
+}
+
+static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+{
+	/* We need handle RX first */
+	if (events & SPIE_NE) {
+		u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive);
+
+		if (mspi->rx)
+			mspi->get_rx(rx_data, mspi);
 	}
 
-	if ((event & SPIE_NF) == 0)
+	if ((events & SPIE_NF) == 0)
 		/* spin until TX is done */
-		while (((event =
-			 mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->event)) &
+		while (((events =
+			mpc8xxx_spi_read_reg(&mspi->base->event)) &
 						SPIE_NF) == 0)
 			cpu_relax();
 
-	mpc8xxx_spi->count -= 1;
-	if (mpc8xxx_spi->count) {
-		u32 word = mpc8xxx_spi->get_tx(mpc8xxx_spi);
-		mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->transmit, word);
+	/* Clear the events */
+	mpc8xxx_spi_write_reg(&mspi->base->event, events);
+
+	mspi->count -= 1;
+	if (mspi->count) {
+		u32 word = mspi->get_tx(mspi);
+
+		mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
 	} else {
-		complete(&mpc8xxx_spi->done);
+		complete(&mspi->done);
 	}
+}
 
-	/* Clear the events */
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, event);
+static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+{
+	struct mpc8xxx_spi *mspi = context_data;
+	irqreturn_t ret = IRQ_NONE;
+	u32 events;
+
+	/* Get interrupt events(tx/rx) */
+	events = mpc8xxx_spi_read_reg(&mspi->base->event);
+	if (events)
+		ret = IRQ_HANDLED;
+
+	dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
+
+	if (mspi->flags & SPI_CPM_MODE)
+		mpc8xxx_spi_cpm_irq(mspi, events);
+	else
+		mpc8xxx_spi_cpu_irq(mspi, events);
 
 	return ret;
 }
+
 static int mpc8xxx_spi_transfer(struct spi_device *spi,
 				struct spi_message *m)
 {
@@ -520,10 +764,212 @@ static void mpc8xxx_spi_cleanup(struct spi_device *spi)
 	kfree(spi->controller_state);
 }
 
+static void *mpc8xxx_spi_alloc_dummy_rx(void)
+{
+	mutex_lock(&mpc8xxx_dummy_rx_lock);
+
+	if (!mpc8xxx_dummy_rx)
+		mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
+	if (mpc8xxx_dummy_rx)
+		mpc8xxx_dummy_rx_refcnt++;
+
+	mutex_unlock(&mpc8xxx_dummy_rx_lock);
+
+	return mpc8xxx_dummy_rx;
+}
+
+static void mpc8xxx_spi_free_dummy_rx(void)
+{
+	mutex_lock(&mpc8xxx_dummy_rx_lock);
+
+	switch (mpc8xxx_dummy_rx_refcnt) {
+	case 0:
+		WARN_ON(1);
+		break;
+	case 1:
+		kfree(mpc8xxx_dummy_rx);
+		mpc8xxx_dummy_rx = NULL;
+		/* fall through */
+	default:
+		mpc8xxx_dummy_rx_refcnt--;
+		break;
+	}
+
+	mutex_unlock(&mpc8xxx_dummy_rx_lock);
+}
+
+static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
+{
+	struct device *dev = mspi->dev;
+	struct device_node *np = dev_archdata_get_node(&dev->archdata);
+	const u32 *iprop;
+	int size;
+	unsigned long spi_base_ofs;
+	unsigned long pram_ofs = -ENOMEM;
+
+	/* Can't use of_address_to_resource(), QE muram isn't at 0. */
+	iprop = of_get_property(np, "reg", &size);
+
+	/* QE with a fixed pram location? */
+	if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4)
+		return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE);
+
+	/* QE but with a dynamic pram location? */
+	if (mspi->flags & SPI_QE) {
+		pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
+		qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock,
+				QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs);
+		return pram_ofs;
+	}
+
+	/* CPM1 and CPM2 pram must be at a fixed addr. */
+	if (!iprop || size != sizeof(*iprop) * 4)
+		return -ENOMEM;
+
+	spi_base_ofs = cpm_muram_alloc_fixed(iprop[2], 2);
+	if (IS_ERR_VALUE(spi_base_ofs))
+		return -ENOMEM;
+
+	if (mspi->flags & SPI_CPM2) {
+		pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
+		if (!IS_ERR_VALUE(pram_ofs)) {
+			u16 __iomem *spi_base = cpm_muram_addr(spi_base_ofs);
+
+			out_be16(spi_base, pram_ofs);
+		}
+	} else {
+		struct spi_pram __iomem *pram = cpm_muram_addr(spi_base_ofs);
+		u16 rpbase = in_be16(&pram->rpbase);
+
+		/* Microcode relocation patch applied? */
+		if (rpbase)
+			pram_ofs = rpbase;
+		else
+			return spi_base_ofs;
+	}
+
+	cpm_muram_free(spi_base_ofs);
+	return pram_ofs;
+}
+
+static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
+{
+	struct device *dev = mspi->dev;
+	struct device_node *np = dev_archdata_get_node(&dev->archdata);
+	const u32 *iprop;
+	int size;
+	unsigned long pram_ofs;
+	unsigned long bds_ofs;
+
+	if (!(mspi->flags & SPI_CPM_MODE))
+		return 0;
+
+	if (!mpc8xxx_spi_alloc_dummy_rx())
+		return -ENOMEM;
+
+	if (mspi->flags & SPI_QE) {
+		iprop = of_get_property(np, "cell-index", &size);
+		if (iprop && size == sizeof(*iprop))
+			mspi->subblock = *iprop;
+
+		switch (mspi->subblock) {
+		default:
+			dev_warn(dev, "cell-index unspecified, assuming SPI1");
+			/* fall through */
+		case 0:
+			mspi->subblock = QE_CR_SUBBLOCK_SPI1;
+			break;
+		case 1:
+			mspi->subblock = QE_CR_SUBBLOCK_SPI2;
+			break;
+		}
+	}
+
+	pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi);
+	if (IS_ERR_VALUE(pram_ofs)) {
+		dev_err(dev, "can't allocate spi parameter ram\n");
+		goto err_pram;
+	}
+
+	bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) +
+				  sizeof(*mspi->rx_bd), 8);
+	if (IS_ERR_VALUE(bds_ofs)) {
+		dev_err(dev, "can't allocate bds\n");
+		goto err_bds;
+	}
+
+	mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE,
+					    DMA_TO_DEVICE);
+	if (dma_mapping_error(dev, mspi->dma_dummy_tx)) {
+		dev_err(dev, "unable to map dummy tx buffer\n");
+		goto err_dummy_tx;
+	}
+
+	mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR,
+					    DMA_FROM_DEVICE);
+	if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
+		dev_err(dev, "unable to map dummy rx buffer\n");
+		goto err_dummy_rx;
+	}
+
+	mspi->pram = cpm_muram_addr(pram_ofs);
+
+	mspi->tx_bd = cpm_muram_addr(bds_ofs);
+	mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
+
+	/* Initialize parameter ram. */
+	out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd));
+	out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd));
+	out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL);
+	out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL);
+	out_be16(&mspi->pram->mrblr, SPI_MRBLR);
+	out_be32(&mspi->pram->rstate, 0);
+	out_be32(&mspi->pram->rdp, 0);
+	out_be16(&mspi->pram->rbptr, 0);
+	out_be16(&mspi->pram->rbc, 0);
+	out_be32(&mspi->pram->rxtmp, 0);
+	out_be32(&mspi->pram->tstate, 0);
+	out_be32(&mspi->pram->tdp, 0);
+	out_be16(&mspi->pram->tbptr, 0);
+	out_be16(&mspi->pram->tbc, 0);
+	out_be32(&mspi->pram->txtmp, 0);
+
+	return 0;
+
+err_dummy_rx:
+	dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
+err_dummy_tx:
+	cpm_muram_free(bds_ofs);
+err_bds:
+	cpm_muram_free(pram_ofs);
+err_pram:
+	mpc8xxx_spi_free_dummy_rx();
+	return -ENOMEM;
+}
+
+static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
+{
+	struct device *dev = mspi->dev;
+
+	dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE);
+	dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
+	cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
+	cpm_muram_free(cpm_muram_offset(mspi->pram));
+	mpc8xxx_spi_free_dummy_rx();
+}
+
 static const char *mpc8xxx_spi_strmode(unsigned int flags)
 {
-	if (flags & SPI_QE_CPU_MODE)
+	if (flags & SPI_QE_CPU_MODE) {
 		return "QE CPU";
+	} else if (flags & SPI_CPM_MODE) {
+		if (flags & SPI_QE)
+			return "QE";
+		else if (flags & SPI_CPM2)
+			return "CPM2";
+		else
+			return "CPM1";
+	}
 	return "CPU";
 }
 
@@ -553,11 +999,16 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
 	master->cleanup = mpc8xxx_spi_cleanup;
 
 	mpc8xxx_spi = spi_master_get_devdata(master);
+	mpc8xxx_spi->dev = dev;
 	mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
 	mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
 	mpc8xxx_spi->flags = pdata->flags;
 	mpc8xxx_spi->spibrg = pdata->sysclk;
 
+	ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi);
+	if (ret)
+		goto err_cpm_init;
+
 	mpc8xxx_spi->rx_shift = 0;
 	mpc8xxx_spi->tx_shift = 0;
 	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
@@ -570,7 +1021,7 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
 	mpc8xxx_spi->base = ioremap(mem->start, mem->end - mem->start + 1);
 	if (mpc8xxx_spi->base == NULL) {
 		ret = -ENOMEM;
-		goto put_master;
+		goto err_ioremap;
 	}
 
 	mpc8xxx_spi->irq = irq;
@@ -624,7 +1075,9 @@ free_irq:
 	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
 unmap_io:
 	iounmap(mpc8xxx_spi->base);
-put_master:
+err_ioremap:
+	mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+err_cpm_init:
 	spi_master_put(master);
 err:
 	return ERR_PTR(ret);
@@ -644,6 +1097,7 @@ static int __devexit mpc8xxx_spi_remove(struct device *dev)
 
 	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
 	iounmap(mpc8xxx_spi->base);
+	mpc8xxx_spi_cpm_free(mpc8xxx_spi);
 
 	return 0;
 }
@@ -806,6 +1260,12 @@ static int __devinit of_mpc8xxx_spi_probe(struct of_device *ofdev,
 	prop = of_get_property(np, "mode", NULL);
 	if (prop && !strcmp(prop, "cpu-qe"))
 		pdata->flags = SPI_QE_CPU_MODE;
+	else if (prop && !strcmp(prop, "qe"))
+		pdata->flags = SPI_CPM_MODE | SPI_QE;
+	else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
+		pdata->flags = SPI_CPM_MODE | SPI_CPM2;
+	else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
+		pdata->flags = SPI_CPM_MODE | SPI_CPM1;
 
 	ret = of_mpc8xxx_spi_get_chipselects(dev);
 	if (ret)
-- 
1.6.3.3

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox