* [PATCH 4/5] Rename the drivers/of prom_* functions to of_*
From: Nathan Fontenot @ 2012-10-03 2:58 UTC (permalink / raw)
To: devicetree-discuss, cbe-oss-dev, LKML, linuxppc-dev
In-Reply-To: <506B2E63.5090900@linux.vnet.ibm.com>
Rename the prom_*_property routines of the generic OF code to of_*_property.
This brings them in line with the naming used by the rest of the OF code.
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
---
arch/powerpc/kernel/machine_kexec.c | 12 ++++++------
arch/powerpc/kernel/machine_kexec_64.c | 8 ++++----
arch/powerpc/kernel/pci_32.c | 2 +-
arch/powerpc/platforms/ps3/os-area.c | 6 +++---
arch/powerpc/platforms/pseries/iommu.c | 4 ++--
arch/powerpc/platforms/pseries/mobility.c | 6 +++---
arch/powerpc/platforms/pseries/reconfig.c | 8 ++++----
drivers/macintosh/smu.c | 2 +-
drivers/of/base.c | 15 +++++++--------
include/linux/of.h | 9 ++++-----
10 files changed, 35 insertions(+), 37 deletions(-)
Index: dt-next/include/linux/of.h
===================================================================
--- dt-next.orig/include/linux/of.h 2012-10-02 08:50:22.000000000 -0500
+++ dt-next/include/linux/of.h 2012-10-02 09:07:23.000000000 -0500
@@ -263,11 +263,10 @@
extern int of_machine_is_compatible(const char *compat);
-extern int prom_add_property(struct device_node* np, struct property* prop);
-extern int prom_remove_property(struct device_node *np, struct property *prop);
-extern int prom_update_property(struct device_node *np,
- struct property *newprop,
- struct property *oldprop);
+extern int of_add_property(struct device_node *np, struct property *prop);
+extern int of_remove_property(struct device_node *np, struct property *prop);
+extern int of_update_property(struct device_node *np, struct property *newprop,
+ struct property *oldprop);
#if defined(CONFIG_OF_DYNAMIC)
/* For updating the device tree at runtime */
Index: dt-next/arch/powerpc/kernel/pci_32.c
===================================================================
--- dt-next.orig/arch/powerpc/kernel/pci_32.c 2012-10-02 08:30:22.000000000 -0500
+++ dt-next/arch/powerpc/kernel/pci_32.c 2012-10-02 09:01:10.000000000 -0500
@@ -208,7 +208,7 @@
of_prop->name = "pci-OF-bus-map";
of_prop->length = 256;
of_prop->value = &of_prop[1];
- prom_add_property(dn, of_prop);
+ of_add_property(dn, of_prop);
of_node_put(dn);
}
}
Index: dt-next/arch/powerpc/kernel/machine_kexec.c
===================================================================
--- dt-next.orig/arch/powerpc/kernel/machine_kexec.c 2012-10-02 08:30:22.000000000 -0500
+++ dt-next/arch/powerpc/kernel/machine_kexec.c 2012-10-02 09:01:10.000000000 -0500
@@ -212,16 +212,16 @@
* be sure what's in them, so remove them. */
prop = of_find_property(node, "linux,crashkernel-base", NULL);
if (prop)
- prom_remove_property(node, prop);
+ of_remove_property(node, prop);
prop = of_find_property(node, "linux,crashkernel-size", NULL);
if (prop)
- prom_remove_property(node, prop);
+ of_remove_property(node, prop);
if (crashk_res.start != 0) {
- prom_add_property(node, &crashk_base_prop);
+ of_add_property(node, &crashk_base_prop);
crashk_size = resource_size(&crashk_res);
- prom_add_property(node, &crashk_size_prop);
+ of_add_property(node, &crashk_size_prop);
}
}
@@ -237,11 +237,11 @@
/* remove any stale properties so ours can be found */
prop = of_find_property(node, kernel_end_prop.name, NULL);
if (prop)
- prom_remove_property(node, prop);
+ of_remove_property(node, prop);
/* information needed by userspace when using default_machine_kexec */
kernel_end = __pa(_end);
- prom_add_property(node, &kernel_end_prop);
+ of_add_property(node, &kernel_end_prop);
export_crashk_values(node);
Index: dt-next/arch/powerpc/kernel/machine_kexec_64.c
===================================================================
--- dt-next.orig/arch/powerpc/kernel/machine_kexec_64.c 2012-10-02 08:30:22.000000000 -0500
+++ dt-next/arch/powerpc/kernel/machine_kexec_64.c 2012-10-02 09:01:10.000000000 -0500
@@ -389,14 +389,14 @@
/* remove any stale propertys so ours can be found */
prop = of_find_property(node, htab_base_prop.name, NULL);
if (prop)
- prom_remove_property(node, prop);
+ of_remove_property(node, prop);
prop = of_find_property(node, htab_size_prop.name, NULL);
if (prop)
- prom_remove_property(node, prop);
+ of_remove_property(node, prop);
htab_base = __pa(htab_address);
- prom_add_property(node, &htab_base_prop);
- prom_add_property(node, &htab_size_prop);
+ of_add_property(node, &htab_base_prop);
+ of_add_property(node, &htab_size_prop);
of_node_put(node);
return 0;
Index: dt-next/arch/powerpc/platforms/pseries/reconfig.c
===================================================================
--- dt-next.orig/arch/powerpc/platforms/pseries/reconfig.c 2012-10-02 08:45:12.000000000 -0500
+++ dt-next/arch/powerpc/platforms/pseries/reconfig.c 2012-10-02 09:14:31.000000000 -0500
@@ -326,7 +326,7 @@
if (!prop)
return -ENOMEM;
- prom_add_property(np, prop);
+ of_add_property(np, prop);
return 0;
}
@@ -350,7 +350,7 @@
prop = of_find_property(np, buf, NULL);
- return prom_remove_property(np, prop);
+ return of_remove_property(np, prop);
}
static int do_update_property(char *buf, size_t bufsize)
@@ -380,11 +380,11 @@
oldprop = of_find_property(np, name,NULL);
if (!oldprop) {
if (strlen(name))
- return prom_add_property(np, newprop);
+ return of_add_property(np, newprop);
return -ENODEV;
}
- rc = prom_update_property(np, newprop, oldprop);
+ rc = of_update_property(np, newprop, oldprop);
return rc;
}
Index: dt-next/arch/powerpc/platforms/pseries/mobility.c
===================================================================
--- dt-next.orig/arch/powerpc/platforms/pseries/mobility.c 2012-10-02 08:30:23.000000000 -0500
+++ dt-next/arch/powerpc/platforms/pseries/mobility.c 2012-10-02 09:03:54.000000000 -0500
@@ -119,9 +119,9 @@
if (!more) {
old_prop = of_find_property(dn, new_prop->name, NULL);
if (old_prop)
- prom_update_property(dn, new_prop, old_prop);
+ of_update_property(dn, new_prop, old_prop);
else
- prom_add_property(dn, new_prop);
+ of_add_property(dn, new_prop);
new_prop = NULL;
}
@@ -178,7 +178,7 @@
case 0x80000000:
prop = of_find_property(dn, prop_name, NULL);
- prom_remove_property(dn, prop);
+ of_remove_property(dn, prop);
prop = NULL;
break;
Index: dt-next/arch/powerpc/platforms/pseries/iommu.c
===================================================================
--- dt-next.orig/arch/powerpc/platforms/pseries/iommu.c 2012-10-02 08:43:40.000000000 -0500
+++ dt-next/arch/powerpc/platforms/pseries/iommu.c 2012-10-02 09:01:10.000000000 -0500
@@ -747,7 +747,7 @@
np->full_name, ret, ddw_avail[2], liobn);
delprop:
- ret = prom_remove_property(np, win64);
+ ret = of_remove_property(np, win64);
if (ret)
pr_warning("%s: failed to remove direct window property: %d\n",
np->full_name, ret);
@@ -991,7 +991,7 @@
goto out_free_window;
}
- ret = prom_add_property(pdn, win64);
+ ret = of_add_property(pdn, win64);
if (ret) {
dev_err(&dev->dev, "unable to add dma window property for %s: %d",
pdn->full_name, ret);
Index: dt-next/arch/powerpc/platforms/ps3/os-area.c
===================================================================
--- dt-next.orig/arch/powerpc/platforms/ps3/os-area.c 2012-10-02 08:30:23.000000000 -0500
+++ dt-next/arch/powerpc/platforms/ps3/os-area.c 2012-10-02 09:01:10.000000000 -0500
@@ -280,13 +280,13 @@
if (tmp) {
pr_debug("%s:%d found %s\n", __func__, __LINE__, prop->name);
- prom_remove_property(node, tmp);
+ of_remove_property(node, tmp);
}
- result = prom_add_property(node, prop);
+ result = of_add_property(node, prop);
if (result)
- pr_debug("%s:%d prom_set_property failed\n", __func__,
+ pr_debug("%s:%d of_set_property failed\n", __func__,
__LINE__);
}
Index: dt-next/drivers/macintosh/smu.c
===================================================================
--- dt-next.orig/drivers/macintosh/smu.c 2012-10-02 08:30:35.000000000 -0500
+++ dt-next/drivers/macintosh/smu.c 2012-10-02 09:01:10.000000000 -0500
@@ -998,7 +998,7 @@
"%02x !\n", id, hdr->id);
goto failure;
}
- if (prom_add_property(smu->of_node, prop)) {
+ if (of_add_property(smu->of_node, prop)) {
printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x "
"property !\n", id);
goto failure;
Index: dt-next/drivers/of/base.c
===================================================================
--- dt-next.orig/drivers/of/base.c 2012-10-02 08:58:55.000000000 -0500
+++ dt-next/drivers/of/base.c 2012-10-02 09:05:37.000000000 -0500
@@ -997,9 +997,9 @@
#endif
/**
- * prom_add_property - Add a property to a node
+ * of_add_property - Add a property to a node
*/
-int prom_add_property(struct device_node *np, struct property *prop)
+int of_add_property(struct device_node *np, struct property *prop)
{
struct property **next;
unsigned long flags;
@@ -1033,14 +1033,14 @@
}
/**
- * prom_remove_property - Remove a property from a node.
+ * of_remove_property - Remove a property from a node.
*
* Note that we don't actually remove it, since we have given out
* who-knows-how-many pointers to the data using get-property.
* Instead we just move the property to the "dead properties"
* list, so it won't be found any more.
*/
-int prom_remove_property(struct device_node *np, struct property *prop)
+int of_remove_property(struct device_node *np, struct property *prop)
{
struct property **next;
unsigned long flags;
@@ -1079,16 +1079,15 @@
}
/*
- * prom_update_property - Update a property in a node.
+ * of_update_property - Update a property in a node.
*
* Note that we don't actually remove it, since we have given out
* who-knows-how-many pointers to the data using get-property.
* Instead we just move the property to the "dead properties" list,
* and add the new property to the property list
*/
-int prom_update_property(struct device_node *np,
- struct property *newprop,
- struct property *oldprop)
+int of_update_property(struct device_node *np, struct property *newprop,
+ struct property *oldprop)
{
struct property **next;
unsigned long flags;
^ permalink raw reply
* [PATCH 5/5] Remove the pSeries_reconfig.h file
From: Nathan Fontenot @ 2012-10-03 2:59 UTC (permalink / raw)
To: devicetree-discuss, cbe-oss-dev, LKML, linuxppc-dev
In-Reply-To: <506B2E63.5090900@linux.vnet.ibm.com>
Remove the pSeries_reconfig.h header file. At this point there is only one
definition in the file, pSeries_coalesce_init(), which can be
moved to rtas.h.
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/pSeries_reconfig.h | 15 ---------------
arch/powerpc/include/asm/rtas.h | 5 +++++
arch/powerpc/kernel/rtas.c | 1 -
arch/powerpc/platforms/pseries/smp.c | 1 -
4 files changed, 5 insertions(+), 17 deletions(-)
Index: dt-next/arch/powerpc/include/asm/pSeries_reconfig.h
===================================================================
--- dt-next.orig/arch/powerpc/include/asm/pSeries_reconfig.h 2012-10-02 09:14:01.000000000 -0500
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,15 +0,0 @@
-#ifndef _PPC64_PSERIES_RECONFIG_H
-#define _PPC64_PSERIES_RECONFIG_H
-#ifdef __KERNEL__
-
-#ifdef CONFIG_PPC_PSERIES
-/* Not the best place to put this, will be fixed when we move some
- * of the rtas suspend-me stuff to pseries */
-extern void pSeries_coalesce_init(void);
-#else /* !CONFIG_PPC_PSERIES */
-static inline void pSeries_coalesce_init(void) { }
-#endif /* CONFIG_PPC_PSERIES */
-
-
-#endif /* __KERNEL__ */
-#endif /* _PPC64_PSERIES_RECONFIG_H */
Index: dt-next/arch/powerpc/include/asm/rtas.h
===================================================================
--- dt-next.orig/arch/powerpc/include/asm/rtas.h 2012-10-02 09:14:01.000000000 -0500
+++ dt-next/arch/powerpc/include/asm/rtas.h 2012-10-02 09:14:40.000000000 -0500
@@ -353,8 +353,13 @@
return 1;
return 0;
}
+
+/* Not the best place to put pSeries_coalesce_init, will be fixed when we
+ * move some of the rtas suspend-me stuff to pseries */
+extern void pSeries_coalesce_init(void);
#else
static inline int page_is_rtas_user_buf(unsigned long pfn) { return 0;}
+static inline void pSeries_coalesce_init(void) { }
#endif
extern int call_rtas(const char *, int, int, unsigned long *, ...);
Index: dt-next/arch/powerpc/kernel/rtas.c
===================================================================
--- dt-next.orig/arch/powerpc/kernel/rtas.c 2012-10-02 09:14:01.000000000 -0500
+++ dt-next/arch/powerpc/kernel/rtas.c 2012-10-02 09:14:40.000000000 -0500
@@ -42,7 +42,6 @@
#include <asm/time.h>
#include <asm/mmu.h>
#include <asm/topology.h>
-#include <asm/pSeries_reconfig.h>
struct rtas_t rtas = {
.lock = __ARCH_SPIN_LOCK_UNLOCKED
Index: dt-next/arch/powerpc/platforms/pseries/smp.c
===================================================================
--- dt-next.orig/arch/powerpc/platforms/pseries/smp.c 2012-10-02 09:14:01.000000000 -0500
+++ dt-next/arch/powerpc/platforms/pseries/smp.c 2012-10-02 09:14:40.000000000 -0500
@@ -38,7 +38,6 @@
#include <asm/cputable.h>
#include <asm/firmware.h>
#include <asm/rtas.h>
-#include <asm/pSeries_reconfig.h>
#include <asm/mpic.h>
#include <asm/vdso_datapage.h>
#include <asm/cputhreads.h>
^ permalink raw reply
* Re: [REGRESSION] nfsd crashing with 3.6.0-rc7 on PowerPC
From: Benjamin Herrenschmidt @ 2012-10-03 4:22 UTC (permalink / raw)
To: Nishanth Aravamudan; +Cc: linuxppc-dev, Alexander Graf, anton, LKML List
In-Reply-To: <20121002214327.GA29218@linux.vnet.ibm.com>
On Tue, 2012-10-02 at 14:43 -0700, Nishanth Aravamudan wrote:
>
> Started looking into this. If your suspicion were accurate, wouldn't
> the
> bisection have stopped at 0e4bc95d87394364f408627067238453830bdbf3
> ("powerpc/iommu: Reduce spinlock coverage in iommu_alloc and
> iommu_free")?
>
> Alex, the error is reproducible, right? Does it go away by reverting
> that commit against mainline? Just trying to narrow down my focus.
My suspiction is, I'm afraid, a real bug but not that bug since it would
only happen on U3 and this is an U4 machine ... so we have two bugs, one
of them still unidentified.
Cleaning up the CC list for now...
Cheers,
Ben.
^ permalink raw reply
* [PATCH] powerpc/mpic: add global timer support
From: Wang Dongsheng @ 2012-10-03 10:38 UTC (permalink / raw)
To: b07421, galak; +Cc: Wang Dongsheng, linuxppc-dev, Wang Dongsheng
The MPIC global timer is a hardware timer inside the Freescale PIC comply
to Open-PIC standard. When the timer is timeout of the specified interval,
the hardware timer generates an interrupt. The driver currently is only
tested on fsl chip, but it can potentially support other global timers
complying to Open-PIC standard.
The two independent groups of global timer on fsl chip, group A and group B,
are identical in their functionality, except that they appear at different
locations within the PIC register map. The hardware timer can be cascaded to
create timers larger than the default 31-bit global timers. Timer cascade
fields allow configuration of up to two 63-bit timers. But These two groups
of timers cannot be cascaded together.
It can be used as a wakeup source for low power modes. It also could be used
as periodical timer for protocols, drivers and etc.
Signed-off-by: Wang Dongsheng <Dongsheng.Wang@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
---
arch/powerpc/include/asm/mpic_timer.h | 39 +++
arch/powerpc/platforms/Kconfig | 12 +
arch/powerpc/sysdev/Makefile | 1 +
arch/powerpc/sysdev/mpic_timer.c | 597 +++++++++++++++++++++++++++++++++
4 files changed, 649 insertions(+)
create mode 100644 arch/powerpc/include/asm/mpic_timer.h
create mode 100644 arch/powerpc/sysdev/mpic_timer.c
diff --git a/arch/powerpc/include/asm/mpic_timer.h b/arch/powerpc/include/asm/mpic_timer.h
new file mode 100644
index 0000000..2428972
--- /dev/null
+++ b/arch/powerpc/include/asm/mpic_timer.h
@@ -0,0 +1,39 @@
+/*
+ * arch/powerpc/include/asm/mpic_timer.h
+ *
+ * Mpic Global Timer Header
+ *
+ * Copyright 2012 Freescale Semicondutor, Inc.
+ *
+ * Author: Wang Dongsheng <Dongsheng.Wang@freescale.com>
+ * Li Yang <leoli@freescale.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
+ * option) any later version.
+ */
+
+#ifndef __MPIC_TIMER__
+#define __MPIC_TIMER__
+
+#include <linux/interrupt.h>
+#include <linux/time.h>
+
+#ifdef CONFIG_MPIC_TIMER
+struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev,
+ const struct timeval *time);
+void mpic_start_timer(struct mpic_timer *handle);
+void mpic_stop_timer(struct mpic_timer *handle);
+void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time);
+void mpic_free_timer(struct mpic_timer *handle);
+#else
+struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev,
+ const struct timeval *time) { return NULL; }
+void mpic_start_timer(struct mpic_timer *handle) { }
+void mpic_stop_timer(struct mpic_timer *handle) { }
+void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time) { }
+void mpic_free_timer(struct mpic_timer *handle) { }
+#endif
+
+#endif
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index e7a896a..b190a6e 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -87,6 +87,18 @@ config MPIC
bool
default n
+config MPIC_TIMER
+ bool "MPIC Global Timer"
+ depends on MPIC && FSL_SOC
+ default n
+ help
+ The MPIC global timer is a hardware timer inside the
+ Freescale PIC comply to Open-PIC standard. When the
+ timer is timeout of the specified interval, the hardware
+ timer generates an interrupt. The driver currently is
+ only tested on fsl chip, but it can potentially support
+ other global timers complying to Open-PIC standard.
+
config PPC_EPAPR_HV_PIC
bool
default n
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 1bd7ecb..b1a9e4d 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -6,6 +6,7 @@ mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o
obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y)
mpic-msgr-obj-$(CONFIG_MPIC_MSGR) += mpic_msgr.o
obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) $(mpic-msgr-obj-y)
+obj-$(CONFIG_MPIC_TIMER) += mpic_timer.o
obj-$(CONFIG_PPC_EPAPR_HV_PIC) += ehv_pic.o
fsl-msi-obj-$(CONFIG_PCI_MSI) += fsl_msi.o
obj-$(CONFIG_PPC_MSI_BITMAP) += msi_bitmap.o
diff --git a/arch/powerpc/sysdev/mpic_timer.c b/arch/powerpc/sysdev/mpic_timer.c
new file mode 100644
index 0000000..20a4b5e
--- /dev/null
+++ b/arch/powerpc/sysdev/mpic_timer.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Author: Dongsheng Wang <Dongsheng.Wang@freescale.com>
+ * Li Yang <leoli@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <sysdev/fsl_soc.h>
+#include <asm/mpic_timer.h>
+
+#define FSL_GLOBAL_TIMER 0x1
+
+#define MPIC_TIMER_TCR_CLKDIV_64 0x00000300
+#define MPIC_TIMER_TCR_ROVR_OFFSET 24
+
+#define TIMER_STOP 0x80000000
+#define TIMERS_PER_GROUP 4
+#define MAX_TICKS (~0U>>1)
+#define MAX_TICKS_CASCADE (~0U)
+#define TIMER_OFFSET(num) (1 << (TIMERS_PER_GROUP - 1 - num))
+
+/* tv_usec should be less than ONE_SECOND, otherwise use tv_sec */
+#define ONE_SECOND 1000000
+
+struct timer_regs {
+ u32 gtccr;
+ u32 res0[3];
+ u32 gtbcr;
+ u32 res1[3];
+ u32 gtvpr;
+ u32 res2[3];
+ u32 gtdr;
+ u32 res3[3];
+};
+
+struct cascade_priv {
+ u32 tcr_value; /* TCR register: CASC & ROVR value */
+ unsigned int cascade_map; /* cascade map */
+ unsigned int timer_num; /* cascade control timer */
+};
+
+struct mpic_timer {
+ void *dev;
+ struct cascade_priv *cascade_handle;
+ unsigned int num;
+ int irq;
+};
+
+struct timer_group_priv {
+ struct timer_regs __iomem *regs;
+ struct mpic_timer timer[TIMERS_PER_GROUP];
+ struct list_head node;
+ u32 ccbfreq;
+ unsigned int timerfreq;
+ unsigned int idle;
+ unsigned int flags;
+ spinlock_t lock;
+ void __iomem *group_tcr;
+};
+
+static struct cascade_priv cascade_timer[] = {
+ /* cascade timer 0 and 1 */
+ {0x1, 0xc, 0x1},
+ /* cascade timer 1 and 2 */
+ {0x2, 0x6, 0x2},
+ /* cascade timer 2 and 3 */
+ {0x4, 0x3, 0x3}
+};
+
+static LIST_HEAD(timer_group_list);
+
+static void convert_ticks_to_time(struct timer_group_priv *priv,
+ const u64 ticks, struct timeval *time)
+{
+ u64 tmp_sec;
+ u32 rem_us;
+ u32 div;
+
+ if (!(priv->flags & FSL_GLOBAL_TIMER)) {
+ time->tv_sec = (long)div_u64_rem(ticks, priv->timerfreq,
+ &rem_us);
+ tmp_sec = (u64)time->tv_sec * (u64)priv->timerfreq;
+ time->tv_usec = (long)div_u64((ticks - tmp_sec) * (u64)1000000,
+ priv->timerfreq);
+
+ return;
+ }
+
+ div = (1 << (MPIC_TIMER_TCR_CLKDIV_64 >> 8)) * 8;
+
+ time->tv_sec = (long)div_u64(ticks, priv->ccbfreq / div);
+ tmp_sec = div_u64((u64)time->tv_sec * (u64)priv->ccbfreq, div);
+
+ time->tv_usec = (long)div_u64((ticks - tmp_sec) * (u64)1000000,
+ priv->ccbfreq / div);
+
+ return;
+}
+
+/* the time set by the user is converted to "ticks" */
+static int convert_time_to_ticks(struct timer_group_priv *priv,
+ const struct timeval *time, u64 *ticks)
+{
+ u64 max_value; /* prevent u64 overflow */
+ u64 tmp = 0;
+
+ u64 tmp_sec;
+ u64 tmp_ms;
+ u64 tmp_us;
+ u32 div;
+
+ if (priv->flags & FSL_GLOBAL_TIMER)
+ max_value = div_u64(ULLONG_MAX, priv->ccbfreq);
+ else
+ max_value = div_u64(ULLONG_MAX, priv->timerfreq);
+
+ if (time->tv_sec > max_value ||
+ (time->tv_sec == max_value && time->tv_usec > 0))
+ return -EINVAL;
+
+ if (!(priv->flags & FSL_GLOBAL_TIMER)) {
+ tmp_sec = time->tv_sec * priv->timerfreq;
+ tmp_ms = time->tv_usec / 1000 * priv->timerfreq / 1000;
+ tmp_us = time->tv_usec % 1000 * priv->timerfreq / 1000000;
+
+ *ticks = tmp_sec + tmp_ms + tmp_us;
+
+ return 0;
+ }
+
+ div = (1 << (MPIC_TIMER_TCR_CLKDIV_64 >> 8)) * 8;
+
+ tmp_sec = div_u64((u64)time->tv_sec * (u64)priv->ccbfreq, div);
+ tmp += tmp_sec;
+
+ tmp_ms = time->tv_usec / 1000;
+ tmp_ms = div_u64((u64)tmp_ms * (u64)priv->ccbfreq, div * 1000);
+ tmp += tmp_ms;
+
+ tmp_us = time->tv_usec % 1000;
+ tmp_us = div_u64((u64)tmp_us * (u64)priv->ccbfreq, div * 1000000);
+ tmp += tmp_us;
+
+ *ticks = tmp;
+
+ return 0;
+}
+
+/* detect whether there is a cascade timer available */
+static struct mpic_timer *detect_idle_cascade_timer(
+ struct timer_group_priv *priv)
+{
+ struct cascade_priv *casc_priv;
+ unsigned int tmp;
+ unsigned int array_size = ARRAY_SIZE(cascade_timer);
+ unsigned int num;
+ unsigned int i;
+ unsigned long flags;
+
+ casc_priv = cascade_timer;
+ for (i = 0; i < array_size; i++) {
+ spin_lock_irqsave(&priv->lock, flags);
+ tmp = casc_priv->cascade_map & priv->idle;
+ if (tmp == casc_priv->cascade_map) {
+ num = casc_priv->timer_num;
+ priv->timer[num].cascade_handle = casc_priv;
+
+ /* set timer busy */
+ priv->idle &= ~casc_priv->cascade_map;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return &priv->timer[num];
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ casc_priv++;
+ }
+
+ return NULL;
+}
+
+static int set_cascade_timer(struct timer_group_priv *priv, u64 ticks,
+ unsigned int num)
+{
+ struct cascade_priv *casc_priv;
+ u32 tmp;
+ u32 tmp_ticks;
+ u32 rem_ticks;
+
+ /* set group tcr reg for cascade */
+ casc_priv = priv->timer[num].cascade_handle;
+ if (!casc_priv)
+ return -EINVAL;
+
+ tmp = casc_priv->tcr_value |
+ (casc_priv->tcr_value << MPIC_TIMER_TCR_ROVR_OFFSET);
+ setbits32(priv->group_tcr, tmp);
+
+ tmp_ticks = div_u64_rem(ticks, MAX_TICKS_CASCADE, &rem_ticks);
+
+ out_be32(&priv->regs[num].gtccr, 0);
+ out_be32(&priv->regs[num].gtbcr, tmp_ticks | TIMER_STOP);
+
+ out_be32(&priv->regs[num - 1].gtccr, 0);
+ out_be32(&priv->regs[num - 1].gtbcr, rem_ticks);
+
+ return 0;
+}
+
+static struct mpic_timer *get_cascade_timer(struct timer_group_priv *priv,
+ u64 ticks)
+{
+ struct mpic_timer *allocated_timer = NULL;
+
+ /* Two cascade timers: Support the maximum time */
+ const u64 max_ticks = (u64)MAX_TICKS * (u64)MAX_TICKS_CASCADE;
+ int ret;
+
+ if (ticks > max_ticks)
+ return NULL;
+
+ /* detect idle timer */
+ allocated_timer = detect_idle_cascade_timer(priv);
+ if (!allocated_timer)
+ return NULL;
+
+ /* set ticks to timer */
+ ret = set_cascade_timer(priv, ticks, allocated_timer->num);
+ if (ret < 0)
+ return NULL;
+
+ return allocated_timer;
+}
+
+static struct mpic_timer *get_timer(const struct timeval *time)
+{
+ struct timer_group_priv *priv;
+ struct mpic_timer *timer = NULL;
+
+ u64 ticks = 0;
+ unsigned int num;
+ unsigned int i;
+ unsigned long flags;
+ int ret = 0;
+
+ list_for_each_entry(priv, &timer_group_list, node) {
+ ret = convert_time_to_ticks(priv, time, &ticks);
+ if (ret < 0)
+ return NULL;
+
+ if (ticks > MAX_TICKS) {
+ if (!(priv->flags & FSL_GLOBAL_TIMER))
+ return NULL;
+
+ timer = get_cascade_timer(priv, ticks);
+ if (!timer)
+ continue;
+ else
+ return timer;
+ }
+
+ for (i = 0; i < TIMERS_PER_GROUP; i++) {
+ /* one timer: Reverse allocation */
+ num = TIMERS_PER_GROUP - 1 - i;
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->idle & (1 << i)) {
+ /* set timer busy */
+ priv->idle &= ~(1 << i);
+ /* set ticks & stop timer */
+ out_be32(&priv->regs[num].gtbcr,
+ ticks | TIMER_STOP);
+ out_be32(&priv->regs[num].gtccr, 0);
+ priv->timer[num].cascade_handle = NULL;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return &priv->timer[num];
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * mpic_start_timer - start hardware timer
+ * @handle: the timer to be started.
+ *
+ * It will do ->fn(->dev) callback from the hardware interrupt at
+ * the ->timeval point in the future.
+ */
+void mpic_start_timer(struct mpic_timer *handle)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+
+ clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP);
+}
+EXPORT_SYMBOL(mpic_start_timer);
+
+/**
+ * mpic_stop_timer - stop hardware timer
+ * @handle: the timer to be stoped
+ *
+ * The timer periodically generates an interrupt. Unless user stops the timer.
+ */
+void mpic_stop_timer(struct mpic_timer *handle)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+ struct cascade_priv *casc_priv;
+
+ setbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP);
+
+ casc_priv = priv->timer[handle->num].cascade_handle;
+ if (casc_priv) {
+ out_be32(&priv->regs[handle->num].gtccr, 0);
+ out_be32(&priv->regs[handle->num - 1].gtccr, 0);
+ } else {
+ out_be32(&priv->regs[handle->num].gtccr, 0);
+ }
+}
+EXPORT_SYMBOL(mpic_stop_timer);
+
+/**
+ * mpic_get_remain_time - get timer time
+ * @handle: the timer to be selected.
+ * @time: time for timer
+ *
+ * Query timer remaining time.
+ */
+void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+ struct cascade_priv *casc_priv = NULL;
+
+ u64 ticks;
+ u64 tmp_ticks;
+
+ casc_priv = priv->timer[handle->num].cascade_handle;
+ if (casc_priv) {
+ tmp_ticks = in_be32(&priv->regs[handle->num].gtccr);
+ ticks = tmp_ticks * (u64)MAX_TICKS_CASCADE;
+ tmp_ticks = in_be32(&priv->regs[handle->num - 1].gtccr);
+ ticks += tmp_ticks;
+ } else {
+ ticks = in_be32(&priv->regs[handle->num].gtccr);
+ }
+
+ convert_ticks_to_time(priv, ticks, time);
+}
+EXPORT_SYMBOL(mpic_get_remain_time);
+
+/**
+ * mpic_free_timer - free hardware timer
+ * @handle: the timer to be removed.
+ *
+ * Free the timer.
+ *
+ * Note: can not be used in interrupt context.
+ */
+void mpic_free_timer(struct mpic_timer *handle)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+
+ struct cascade_priv *casc_priv = NULL;
+ unsigned long flags;
+
+ mpic_stop_timer(handle);
+
+ casc_priv = priv->timer[handle->num].cascade_handle;
+
+ free_irq(priv->timer[handle->num].irq, priv->timer[handle->num].dev);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (casc_priv) {
+ u32 tmp;
+ tmp = casc_priv->tcr_value | (casc_priv->tcr_value <<
+ MPIC_TIMER_TCR_ROVR_OFFSET);
+ clrbits32(priv->group_tcr, tmp);
+ priv->idle |= casc_priv->cascade_map;
+ priv->timer[handle->num].cascade_handle = NULL;
+ } else {
+ priv->idle |= TIMER_OFFSET(handle->num);
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+EXPORT_SYMBOL(mpic_free_timer);
+
+/**
+ * mpic_request_timer - get a hardware timer
+ * @fn: interrupt handler function
+ * @dev: callback function of the data
+ * @time: time for timer
+ *
+ * This executes the "request_irq", returning NULL
+ * else "handle" on success.
+ */
+struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev,
+ const struct timeval *time)
+{
+ struct mpic_timer *allocated_timer = NULL;
+
+ int ret = 0;
+
+ if (list_empty(&timer_group_list))
+ return NULL;
+
+ if ((time->tv_sec + time->tv_usec) == 0 ||
+ time->tv_sec < 0 || time->tv_usec < 0)
+ return NULL;
+
+ if (time->tv_usec > ONE_SECOND)
+ return NULL;
+
+ allocated_timer = get_timer(time);
+ if (!allocated_timer)
+ return NULL;
+
+ ret = request_irq(allocated_timer->irq, fn,
+ IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND,
+ "global-timer", dev);
+ if (ret) {
+ mpic_free_timer(allocated_timer);
+ return NULL;
+ }
+
+ allocated_timer->dev = dev;
+
+ return allocated_timer;
+}
+EXPORT_SYMBOL(mpic_request_timer);
+
+static int timer_group_get_freq(struct device_node *np,
+ struct timer_group_priv *priv)
+{
+ if (priv->flags & FSL_GLOBAL_TIMER) {
+ priv->ccbfreq = fsl_get_sys_freq();
+ priv->timerfreq = priv->ccbfreq;
+ } else {
+ const u32 *timerfreq;
+ int size;
+
+ timerfreq = of_get_property(np, "clock-frequency", &size);
+ if (!timerfreq || size != sizeof(*timerfreq) ||
+ *timerfreq == 0)
+ priv->timerfreq = 0;
+ else
+ priv->timerfreq = *timerfreq;
+ }
+
+ if (priv->timerfreq == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int timer_group_get_irq(struct device_node *np,
+ struct timer_group_priv *priv)
+{
+ const u32 all_timer[] = { 0, TIMERS_PER_GROUP };
+ const u32 *p = NULL;
+ u32 offset;
+ u32 count;
+
+ unsigned int i = 0;
+ unsigned int j = 0;
+ unsigned int irq_index = 0;
+ int irq = 0;
+ int len = 0;
+
+ p = of_get_property(np, "available-ranges", &len);
+ if (!p)
+ p = of_get_property(np, "fsl,available-ranges", &len);
+
+ if (p && len % (2 * sizeof(u32)) != 0) {
+ pr_err("%s: malformed available-ranges property.\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ if (!p) {
+ p = all_timer;
+ len = sizeof(all_timer);
+ }
+
+ len /= 2 * sizeof(u32);
+
+ for (i = 0; i < len; i++) {
+ offset = p[i * 2];
+ count = p[i * 2 + 1];
+ for (j = 0; j < count; j++) {
+ irq = irq_of_parse_and_map(np, irq_index);
+ if (!irq)
+ break;
+ /* Set timer idle */
+ priv->idle |= TIMER_OFFSET((offset + j));
+ priv->timer[offset + j].irq = irq;
+ priv->timer[offset + j].num = offset + j;
+ irq_index++;
+ }
+ }
+
+ return 0;
+}
+
+static void timer_group_init(struct device_node *np)
+{
+ struct timer_group_priv *priv = NULL;
+ unsigned int i = 0;
+ int ret = 0;
+
+ priv = kzalloc(sizeof(struct timer_group_priv), GFP_KERNEL);
+ if (!priv) {
+ pr_err("%s: cannot allocate memory for group.\n",
+ np->full_name);
+ return;
+ }
+
+ if (of_device_is_compatible(np, "fsl,mpic-global-timer"))
+ priv->flags |= FSL_GLOBAL_TIMER;
+
+ priv->regs = of_iomap(np, i++);
+ if (!priv->regs) {
+ pr_err("%s: cannot ioremap timer register address.\n",
+ np->full_name);
+ goto out;
+ }
+
+ if (priv->flags & FSL_GLOBAL_TIMER) {
+ priv->group_tcr = of_iomap(np, i++);
+ if (!priv->group_tcr) {
+ pr_err("%s: cannot ioremap tcr address.\n",
+ np->full_name);
+ goto out;
+ }
+ }
+
+ ret = timer_group_get_freq(np, priv);
+ if (ret < 0)
+ goto out;
+
+ ret = timer_group_get_irq(np, priv);
+ if (ret < 0)
+ goto out;
+
+ spin_lock_init(&priv->lock);
+
+ /* Init FSL timer hardware */
+ if (priv->flags & FSL_GLOBAL_TIMER)
+ setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV_64);
+
+ list_add_tail(&priv->node, &timer_group_list);
+
+ return;
+
+out:
+ if (priv->regs)
+ iounmap(priv->regs);
+
+ if (priv->group_tcr)
+ iounmap(priv->group_tcr);
+
+ kfree(priv);
+}
+
+static const struct of_device_id mpic_timer_ids[] = {
+ { .compatible = "fsl,mpic-global-timer", },
+ {},
+};
+
+static int __init mpic_timer_init(void)
+{
+ struct device_node *np = NULL;
+
+ for_each_matching_node(np, mpic_timer_ids)
+ timer_group_init(np);
+
+ if (list_empty(&timer_group_list))
+ return -ENODEV;
+
+ return 0;
+}
+subsys_initcall(mpic_timer_init);
--
1.7.9.5
^ permalink raw reply related
* [RFC PATCH] powerpc/fsl: add timer wakeup source
From: Wang Dongsheng @ 2012-10-03 10:42 UTC (permalink / raw)
To: b07421, galak; +Cc: Wang Dongsheng, linuxppc-dev, Wang Dongsheng
This is only for freescale powerpc platform. The driver provides a way
to wake up system. Proc interface(/proc/powerpc/wakeup_timer_seconds).
eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds
after the system will be woken up. echo another time into proc interface
to update the time.
Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
---
arch/powerpc/platforms/Kconfig | 23 +++
arch/powerpc/platforms/Makefile | 1 +
arch/powerpc/platforms/fsl_timer_wakeup.c | 217 +++++++++++++++++++++++++++++
3 files changed, 241 insertions(+)
create mode 100644 arch/powerpc/platforms/fsl_timer_wakeup.c
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index b190a6e..7b9232a 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -99,6 +99,29 @@ config MPIC_TIMER
only tested on fsl chip, but it can potentially support
other global timers complying to Open-PIC standard.
+menuconfig FSL_WAKEUP_SOURCE
+ bool "Freescale wakeup source"
+ depends on FSL_SOC && SUSPEND
+ default n
+ help
+ This option enables wakeup source for wake up system
+ features. This is only for freescale powerpc platform.
+
+if FSL_WAKEUP_SOURCE
+
+config FSL_TIMER_WAKEUP
+ tristate "Freescale mpic global timer wakeup event"
+ default n
+ help
+ This is only for freescale powerpc platform. The driver
+ provides a way to wake up system.
+ Proc interface(/proc/powerpc/wakeup_timer_seconds).
+ eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds",
+ 5 seconds after the system will be woken up. echo another
+ time into proc interface to update the time.
+
+endif
+
config PPC_EPAPR_HV_PIC
bool
default n
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
index 879b4a4..8e9a04f 100644
--- a/arch/powerpc/platforms/Makefile
+++ b/arch/powerpc/platforms/Makefile
@@ -2,6 +2,7 @@
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
obj-$(CONFIG_FSL_ULI1575) += fsl_uli1575.o
+obj-$(CONFIG_FSL_TIMER_WAKEUP) += fsl_timer_wakeup.o
obj-$(CONFIG_PPC_PMAC) += powermac/
obj-$(CONFIG_PPC_CHRP) += chrp/
diff --git a/arch/powerpc/platforms/fsl_timer_wakeup.c b/arch/powerpc/platforms/fsl_timer_wakeup.c
new file mode 100644
index 0000000..f20199f
--- /dev/null
+++ b/arch/powerpc/platforms/fsl_timer_wakeup.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Wang Dongsheng <Dongsheng.Wang@freescale.com>
+ * Li Yang <leoli@freescale.com>
+ *
+ * This is only for freescale powerpc platform. The driver provides a way
+ * to wake up system. Proc interface(/proc/powerpc/wakeup_timer_seconds).
+ *
+ * eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds
+ * after the system will be woken up. echo another time into proc interface
+ * to update the time.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <asm/mpic_timer.h>
+
+struct fsl_timer_wakeup {
+ struct mpic_timer *timer;
+ struct work_struct free_work;
+ struct mutex mutex;
+ struct proc_dir_entry *proc_timer_wakeup;
+ struct timeval time;
+};
+
+static struct fsl_timer_wakeup *priv;
+
+static void timer_event_wakeup_free_work(struct work_struct *ws)
+{
+ struct fsl_timer_wakeup *priv =
+ container_of(ws, struct fsl_timer_wakeup, free_work);
+
+ mutex_lock(&priv->mutex);
+ mpic_free_timer(priv->timer);
+ priv->timer = NULL;
+ mutex_unlock(&priv->mutex);
+}
+
+static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
+{
+ struct fsl_timer_wakeup *priv = dev_id;
+
+ schedule_work(&priv->free_work);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t timer_wakeup_read(struct file *file, char __user *buf,
+ size_t count, loff_t *offp)
+{
+ struct fsl_timer_wakeup *priv;
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct proc_dir_entry *dp;
+
+ int ret;
+ char *kbuf;
+
+ dp = PDE(inode);
+ priv = dp->data;
+
+ mutex_lock(&priv->mutex);
+
+ if (!priv->timer ||
+ (priv->time.tv_sec >= 0 && priv->time.tv_usec >= 0)) {
+ priv->time.tv_sec = -1;
+ priv->time.tv_usec = -1;
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+ }
+
+ mpic_get_remain_time(priv->timer, &priv->time);
+
+ mutex_unlock(&priv->mutex);
+
+ kbuf = kzalloc(count, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ sprintf(kbuf, "%ld\n%c", priv->time.tv_sec + 1, '\0');
+
+ ret = strlen(kbuf);
+
+ copy_to_user(buf, kbuf, count);
+
+ kfree(kbuf);
+
+ return ret;
+}
+
+static ssize_t timer_wakeup_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct fsl_timer_wakeup *priv;
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct proc_dir_entry *dp;
+ struct timeval time;
+ char *kbuf;
+
+ dp = PDE(inode);
+ priv = dp->data;
+
+ kbuf = kzalloc(count + 1, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ if (copy_from_user(kbuf, buf, count)) {
+ kfree(kbuf);
+ return -EFAULT;
+ }
+
+ kbuf[count] = '\0';
+
+ if (kstrtol(kbuf, 0, &time.tv_sec)) {
+ kfree(kbuf);
+ return -EINVAL;
+ }
+
+ kfree(kbuf);
+
+ time.tv_usec = 0;
+
+ mutex_lock(&priv->mutex);
+
+ if (!time.tv_sec) {
+ if (priv->timer) {
+ mpic_free_timer(priv->timer);
+ priv->timer = NULL;
+ }
+ mutex_unlock(&priv->mutex);
+
+ return count;
+ }
+
+ if (priv->timer) {
+ mpic_free_timer(priv->timer);
+ priv->timer = NULL;
+ }
+
+ priv->timer = mpic_request_timer(timer_event_interrupt, priv, &time);
+ if (!priv->timer) {
+ mutex_unlock(&priv->mutex);
+
+ return -EINVAL;
+ }
+
+ mpic_start_timer(priv->timer);
+
+ mutex_unlock(&priv->mutex);
+
+ return count;
+}
+
+static const struct file_operations timer_wakeup_fops = {
+ .owner = THIS_MODULE,
+ .read = timer_wakeup_read,
+ .write = timer_wakeup_write,
+ .llseek = no_llseek,
+};
+
+static int timer_wakeup_init(void)
+{
+ struct proc_dir_entry *ent;
+
+ priv = kzalloc(sizeof(struct fsl_timer_wakeup), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->mutex);
+ INIT_WORK(&priv->free_work, timer_event_wakeup_free_work);
+ priv->time.tv_sec = -1;
+ priv->time.tv_usec = -1;
+
+ ent = proc_create_data("powerpc/wakeup_timer_seconds",
+ S_IRUSR | S_IWUSR, NULL, &timer_wakeup_fops, priv);
+ if (!ent) {
+ kfree(priv);
+ return -ENOMEM;
+ }
+
+ priv->proc_timer_wakeup = ent;
+
+ return 0;
+}
+
+static void timer_wakeup_exit(void)
+{
+ if (priv->timer)
+ mpic_free_timer(priv->timer);
+
+ remove_proc_entry("wakeup_timer_seconds",
+ priv->proc_timer_wakeup->parent);
+
+ kfree(priv);
+}
+
+module_init(timer_wakeup_init);
+module_exit(timer_wakeup_exit);
+
+MODULE_DESCRIPTION("Freescale mpic global timer event wake-up driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Wang Dongsheng <dongsheng.wang@freescale.com>");
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH 1/3] edac: Use ccsr_pci structure instead of hardcoded define
From: Gala Kumar-B11780 @ 2012-10-03 13:22 UTC (permalink / raw)
To: Lan Chunhe-B25806; +Cc: <linuxppc-dev@lists.ozlabs.org>
In-Reply-To: <1348772523-17587-1-git-send-email-Chunhe.Lan@freescale.com>
On Sep 27, 2012, at 2:02 PM, Chunhe Lan wrote:
> There are some differences of register offset and definition between
> pci and pcie error management registers. While, some other pci/pcie
> error management registers are nearly the same.
>=20
> To merge pci and pcie edac code into one, it is easier to use ccsr_pci
> structure than the hardcoded define. So remove the hardcoded define and
> add pci/pcie error management register in ccsr_pci structure.
>=20
> Signed-off-by: Chunhe Lan <Chunhe.Lan@freescale.com>
> ---
> arch/powerpc/sysdev/fsl_pci.h | 46 +++++++++++++++++++++++++++++++++---=
----
> drivers/edac/mpc85xx_edac.h | 12 +---------
> 2 files changed, 40 insertions(+), 18 deletions(-)
This patch breaks building the kernel. You can't remove the defines MPC85X=
X_PCI_ERR_* and not change the code that uses them
- k=
^ permalink raw reply
* [git pull] Please pull powerpc.git next branch
From: Kumar Gala @ 2012-10-03 13:25 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
Ben,
A few minor patches for v3.7.
The following changes since commit 7844663a31e97930e3949430573452ac245bfdd5:
powerpc/eeh: Don't release eeh_mutex in eeh_phb_pe_get (2012-09-27 12:51:07 +1000)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/galak/powerpc.git next
for you to fetch changes up to 10bfa766efa19c23f72f50727952a26cb1512256:
driver/mtd:IFC NAND:Initialise internal SRAM before any write (2012-09-27 07:32:25 -0500)
----------------------------------------------------------------
Minghuan Lian (1):
powerpc/fsl-pci: use 'Header Type' to identify PCIE mode
Prabhakar Kushwaha (1):
driver/mtd:IFC NAND:Initialise internal SRAM before any write
arch/powerpc/sysdev/fsl_pci.c | 37 +++++++++++++++++---------
drivers/mtd/nand/fsl_ifc_nand.c | 56 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 79 insertions(+), 14 deletions(-)
^ permalink raw reply
* Re: [RFC PATCH] powerpc/fsl: add timer wakeup source
From: Kumar Gala @ 2012-10-03 13:35 UTC (permalink / raw)
To: Wang Dongsheng
Cc: Wood Scott-B07421, linuxppc-dev@lists.ozlabs.org list,
Wang Dongsheng, linux-pm
In-Reply-To: <1349260948-15828-1-git-send-email-dongsheng.wds@gmail.com>
On Oct 3, 2012, at 5:42 AM, Wang Dongsheng wrote:
> This is only for freescale powerpc platform. The driver provides a way
> to wake up system. Proc interface(/proc/powerpc/wakeup_timer_seconds).
>=20
> eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds
> after the system will be woken up. echo another time into proc =
interface
> to update the time.
>=20
> Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> Signed-off-by: Li Yang <leoli@freescale.com>
> ---
> arch/powerpc/platforms/Kconfig | 23 +++
> arch/powerpc/platforms/Makefile | 1 +
> arch/powerpc/platforms/fsl_timer_wakeup.c | 217 =
+++++++++++++++++++++++++++++
> 3 files changed, 241 insertions(+)
> create mode 100644 arch/powerpc/platforms/fsl_timer_wakeup.c
Adding the Linux PM list to see if there is some existing support for =
this on other arch's in kernel.
I'm pretty sure /proc/ is NOT where we want this exposed.
- k
>=20
> diff --git a/arch/powerpc/platforms/Kconfig =
b/arch/powerpc/platforms/Kconfig
> index b190a6e..7b9232a 100644
> --- a/arch/powerpc/platforms/Kconfig
> +++ b/arch/powerpc/platforms/Kconfig
> @@ -99,6 +99,29 @@ config MPIC_TIMER
> only tested on fsl chip, but it can potentially support
> other global timers complying to Open-PIC standard.
>=20
> +menuconfig FSL_WAKEUP_SOURCE
> + bool "Freescale wakeup source"
> + depends on FSL_SOC && SUSPEND
> + default n
> + help
> + This option enables wakeup source for wake up system
> + features. This is only for freescale powerpc platform.
> +
> +if FSL_WAKEUP_SOURCE
> +
> +config FSL_TIMER_WAKEUP
> + tristate "Freescale mpic global timer wakeup event"
> + default n
> + help
> + This is only for freescale powerpc platform. The driver
> + provides a way to wake up system.
> + Proc interface(/proc/powerpc/wakeup_timer_seconds).
> + eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds",
> + 5 seconds after the system will be woken up. echo another
> + time into proc interface to update the time.
> +
> +endif
> +
> config PPC_EPAPR_HV_PIC
> bool
> default n
> diff --git a/arch/powerpc/platforms/Makefile =
b/arch/powerpc/platforms/Makefile
> index 879b4a4..8e9a04f 100644
> --- a/arch/powerpc/platforms/Makefile
> +++ b/arch/powerpc/platforms/Makefile
> @@ -2,6 +2,7 @@
> subdir-ccflags-$(CONFIG_PPC_WERROR) :=3D -Werror
>=20
> obj-$(CONFIG_FSL_ULI1575) +=3D fsl_uli1575.o
> +obj-$(CONFIG_FSL_TIMER_WAKEUP) +=3D fsl_timer_wakeup.o
>=20
> obj-$(CONFIG_PPC_PMAC) +=3D powermac/
> obj-$(CONFIG_PPC_CHRP) +=3D chrp/
> diff --git a/arch/powerpc/platforms/fsl_timer_wakeup.c =
b/arch/powerpc/platforms/fsl_timer_wakeup.c
> new file mode 100644
> index 0000000..f20199f
> --- /dev/null
> +++ b/arch/powerpc/platforms/fsl_timer_wakeup.c
> @@ -0,0 +1,217 @@
> +/*
> + * Copyright 2012 Freescale Semiconductor, Inc.
> + * Wang Dongsheng <Dongsheng.Wang@freescale.com>
> + * Li Yang <leoli@freescale.com>
> + *
> + * This is only for freescale powerpc platform. The driver provides a =
way
> + * to wake up system. Proc =
interface(/proc/powerpc/wakeup_timer_seconds).
> + *
> + * eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds
> + * after the system will be woken up. echo another time into proc =
interface
> + * to update the time.
> + *
> + * This program is free software; you can redistribute it and/or =
modify it
> + * under the terms of the GNU General Public License version 2 as =
published
> + * by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/fs.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/sysfs.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/pm.h>
> +#include <linux/proc_fs.h>
> +#include <asm/uaccess.h>
> +#include <asm/mpic_timer.h>
> +
> +struct fsl_timer_wakeup {
> + struct mpic_timer *timer;
> + struct work_struct free_work;
> + struct mutex mutex;
> + struct proc_dir_entry *proc_timer_wakeup;
> + struct timeval time;
> +};
> +
> +static struct fsl_timer_wakeup *priv;
> +
> +static void timer_event_wakeup_free_work(struct work_struct *ws)
> +{
> + struct fsl_timer_wakeup *priv =3D
> + container_of(ws, struct fsl_timer_wakeup, free_work);
> +
> + mutex_lock(&priv->mutex);
> + mpic_free_timer(priv->timer);
> + priv->timer =3D NULL;
> + mutex_unlock(&priv->mutex);
> +}
> +
> +static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
> +{
> + struct fsl_timer_wakeup *priv =3D dev_id;
> +
> + schedule_work(&priv->free_work);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static ssize_t timer_wakeup_read(struct file *file, char __user *buf,
> + size_t count, loff_t *offp)
> +{
> + struct fsl_timer_wakeup *priv;
> + struct inode *inode =3D file->f_path.dentry->d_inode;
> + struct proc_dir_entry *dp;
> +
> + int ret;
> + char *kbuf;
> +
> + dp =3D PDE(inode);
> + priv =3D dp->data;
> +
> + mutex_lock(&priv->mutex);
> +
> + if (!priv->timer ||
> + (priv->time.tv_sec >=3D 0 && priv->time.tv_usec =
>=3D 0)) {
> + priv->time.tv_sec =3D -1;
> + priv->time.tv_usec =3D -1;
> +
> + mutex_unlock(&priv->mutex);
> +
> + return 0;
> + }
> +
> + mpic_get_remain_time(priv->timer, &priv->time);
> +
> + mutex_unlock(&priv->mutex);
> +
> + kbuf =3D kzalloc(count, GFP_KERNEL);
> + if (!kbuf)
> + return -ENOMEM;
> +
> + sprintf(kbuf, "%ld\n%c", priv->time.tv_sec + 1, '\0');
> +
> + ret =3D strlen(kbuf);
> +
> + copy_to_user(buf, kbuf, count);
> +
> + kfree(kbuf);
> +
> + return ret;
> +}
> +
> +static ssize_t timer_wakeup_write(struct file *file, const char =
__user *buf,
> + size_t count, loff_t *off)
> +{
> + struct fsl_timer_wakeup *priv;
> + struct inode *inode =3D file->f_path.dentry->d_inode;
> + struct proc_dir_entry *dp;
> + struct timeval time;
> + char *kbuf;
> +
> + dp =3D PDE(inode);
> + priv =3D dp->data;
> +
> + kbuf =3D kzalloc(count + 1, GFP_KERNEL);
> + if (!kbuf)
> + return -ENOMEM;
> +
> + if (copy_from_user(kbuf, buf, count)) {
> + kfree(kbuf);
> + return -EFAULT;
> + }
> +
> + kbuf[count] =3D '\0';
> +
> + if (kstrtol(kbuf, 0, &time.tv_sec)) {
> + kfree(kbuf);
> + return -EINVAL;
> + }
> +
> + kfree(kbuf);
> +
> + time.tv_usec =3D 0;
> +
> + mutex_lock(&priv->mutex);
> +
> + if (!time.tv_sec) {
> + if (priv->timer) {
> + mpic_free_timer(priv->timer);
> + priv->timer =3D NULL;
> + }
> + mutex_unlock(&priv->mutex);
> +
> + return count;
> + }
> +
> + if (priv->timer) {
> + mpic_free_timer(priv->timer);
> + priv->timer =3D NULL;
> + }
> +
> + priv->timer =3D mpic_request_timer(timer_event_interrupt, priv, =
&time);
> + if (!priv->timer) {
> + mutex_unlock(&priv->mutex);
> +
> + return -EINVAL;
> + }
> +
> + mpic_start_timer(priv->timer);
> +
> + mutex_unlock(&priv->mutex);
> +
> + return count;
> +}
> +
> +static const struct file_operations timer_wakeup_fops =3D {
> + .owner =3D THIS_MODULE,
> + .read =3D timer_wakeup_read,
> + .write =3D timer_wakeup_write,
> + .llseek =3D no_llseek,
> +};
> +
> +static int timer_wakeup_init(void)
> +{
> + struct proc_dir_entry *ent;
> +
> + priv =3D kzalloc(sizeof(struct fsl_timer_wakeup), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + mutex_init(&priv->mutex);
> + INIT_WORK(&priv->free_work, timer_event_wakeup_free_work);
> + priv->time.tv_sec =3D -1;
> + priv->time.tv_usec =3D -1;
> +
> + ent =3D proc_create_data("powerpc/wakeup_timer_seconds",
> + S_IRUSR | S_IWUSR, NULL, &timer_wakeup_fops, =
priv);
> + if (!ent) {
> + kfree(priv);
> + return -ENOMEM;
> + }
> +
> + priv->proc_timer_wakeup =3D ent;
> +
> + return 0;
> +}
> +
> +static void timer_wakeup_exit(void)
> +{
> + if (priv->timer)
> + mpic_free_timer(priv->timer);
> +
> + remove_proc_entry("wakeup_timer_seconds",
> + priv->proc_timer_wakeup->parent);
> +
> + kfree(priv);
> +}
> +
> +module_init(timer_wakeup_init);
> +module_exit(timer_wakeup_exit);
> +
> +MODULE_DESCRIPTION("Freescale mpic global timer event wake-up =
driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Wang Dongsheng <dongsheng.wang@freescale.com>");
> --=20
> 1.7.9.5
^ permalink raw reply
* Re: [PATCH] powerpc/pcm030: add pcm030-audio-fabric to dts
From: Anatolij Gustschin @ 2012-10-03 15:28 UTC (permalink / raw)
To: Eric Millbrandt; +Cc: linuxppc-dev, devicetree-discuss
In-Reply-To: <1348525007-14413-1-git-send-email-emillbrandt@dekaresearch.com>
Hi,
On Mon, 24 Sep 2012 18:16:47 -0400
Eric Millbrandt <emillbrandt@dekaresearch.com> wrote:
> Add a node for the pcm030-audio-fabric ASoC driver
>
> Signed-off-by: Eric Millbrandt <emillbrandt@dekaresearch.com>
> ---
> arch/powerpc/boot/dts/pcm030.dts | 7 ++++++-
> 1 files changed, 6 insertions(+), 1 deletions(-)
Appied to next, thanks.
Anatolij
^ permalink raw reply
* [PATCH 3/5] rapidio: run discovery as an asynchronous process
From: Alexandre Bounine @ 2012-10-03 19:18 UTC (permalink / raw)
To: Andrew Morton, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine
In-Reply-To: <1349291923-22860-1-git-send-email-alexandre.bounine@idt.com>
Modify mport initialization routine to run the RapidIO discovery process
asynchronously. This allows to have an arbitrary order of enumerating and
discovering ports in systems with multiple RapidIO controllers without
creating a deadlock situation if enumerator port is registered after a
discovering one.
Making netID matching to mportID ensures consistent net ID assignment in
multiport RapidIO systems with asynchronous discovery process (global counter
implementation is affected by race between threads).
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
---
drivers/rapidio/rio-scan.c | 3 +-
drivers/rapidio/rio.c | 51 ++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 8b7c4bc..745670f 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -44,7 +44,6 @@ static void rio_init_em(struct rio_dev *rdev);
DEFINE_SPINLOCK(rio_global_list_lock);
static int next_destid = 0;
-static int next_net = 0;
static int next_comptag = 1;
static int rio_mport_phys_table[] = {
@@ -1062,7 +1061,7 @@ static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port)
INIT_LIST_HEAD(&net->mports);
list_add_tail(&port->nnode, &net->mports);
net->hport = port;
- net->id = next_net++;
+ net->id = port->id;
}
return net;
}
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index d7b68cc..7cdc3e6d 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -1260,15 +1260,62 @@ static int __devinit rio_init(void)
return 0;
}
+static struct workqueue_struct *rio_wq;
+
+struct rio_disc_work {
+ struct work_struct work;
+ struct rio_mport *mport;
+};
+
+static void __devinit disc_work_handler(struct work_struct *_work)
+{
+ struct rio_disc_work *work = container_of(_work,
+ struct rio_disc_work, work);
+
+ pr_debug("RIO: discovery work for mport %d %s\n",
+ work->mport->id, work->mport->name);
+ rio_disc_mport(work->mport);
+
+ kfree(work);
+}
+
int __devinit rio_init_mports(void)
{
struct rio_mport *port;
+ struct rio_disc_work *work;
+ int no_disc = 0;
list_for_each_entry(port, &rio_mports, node) {
if (port->host_deviceid >= 0)
rio_enum_mport(port);
- else
- rio_disc_mport(port);
+ else if (!no_disc) {
+ if (!rio_wq) {
+ rio_wq = alloc_workqueue("riodisc", 0, 0);
+ if (!rio_wq) {
+ pr_err("RIO: unable allocate rio_wq\n");
+ no_disc = 1;
+ continue;
+ }
+ }
+
+ work = kzalloc(sizeof *work, GFP_KERNEL);
+ if (!work) {
+ pr_err("RIO: no memory for work struct\n");
+ no_disc = 1;
+ continue;
+ }
+
+ work->mport = port;
+ INIT_WORK(&work->work, disc_work_handler);
+ queue_work(rio_wq, &work->work);
+ }
+ }
+
+ if (rio_wq) {
+ pr_debug("RIO: flush discovery workqueue\n");
+ flush_workqueue(rio_wq);
+ pr_debug("RIO: flush discovery workqueue finished\n");
+ destroy_workqueue(rio_wq);
}
rio_init();
--
1.7.8.4
^ permalink raw reply related
* [PATCH 1/5] rapidio: fix blocking wait for discovery ready
From: Alexandre Bounine @ 2012-10-03 19:18 UTC (permalink / raw)
To: Andrew Morton, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine
In-Reply-To: <1349291923-22860-1-git-send-email-alexandre.bounine@idt.com>
Fix blocking wait loop in the RapidIO discovery routine to avoid warning
dumps about stalled CPU on x86 platforms.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
---
drivers/rapidio/rio-scan.c | 62 ++++++++++++++-----------------------------
1 files changed, 20 insertions(+), 42 deletions(-)
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 02e686b..0a27253 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -31,6 +31,7 @@
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
+#include <linux/sched.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
@@ -39,8 +40,6 @@
LIST_HEAD(rio_devices);
static LIST_HEAD(rio_switches);
-static void rio_enum_timeout(unsigned long);
-
static void rio_init_em(struct rio_dev *rdev);
DEFINE_SPINLOCK(rio_global_list_lock);
@@ -49,9 +48,6 @@ static int next_destid = 0;
static int next_net = 0;
static int next_comptag = 1;
-static struct timer_list rio_enum_timer =
-TIMER_INITIALIZER(rio_enum_timeout, 0, 0);
-
static int rio_mport_phys_table[] = {
RIO_EFB_PAR_EP_ID,
RIO_EFB_PAR_EP_REC_ID,
@@ -1234,20 +1230,6 @@ static void rio_build_route_tables(void)
}
/**
- * rio_enum_timeout- Signal that enumeration timed out
- * @data: Address of timeout flag.
- *
- * When the enumeration complete timer expires, set a flag that
- * signals to the discovery process that enumeration did not
- * complete in a sane amount of time.
- */
-static void rio_enum_timeout(unsigned long data)
-{
- /* Enumeration timed out, set flag */
- *(int *)data = 1;
-}
-
-/**
* rio_disc_mport- Start discovery through a master port
* @mport: Master port to send transactions
*
@@ -1260,34 +1242,33 @@ static void rio_enum_timeout(unsigned long data)
int __devinit rio_disc_mport(struct rio_mport *mport)
{
struct rio_net *net = NULL;
- int enum_timeout_flag = 0;
+ unsigned long to_end;
printk(KERN_INFO "RIO: discover master port %d, %s\n", mport->id,
mport->name);
/* If master port has an active link, allocate net and discover peers */
if (rio_mport_is_active(mport)) {
- if (!(net = rio_alloc_net(mport))) {
- printk(KERN_ERR "RIO: Failed to allocate new net\n");
- goto bail;
- }
+ pr_debug("RIO: wait for enumeration to complete...\n");
- pr_debug("RIO: wait for enumeration complete...");
-
- rio_enum_timer.expires =
- jiffies + CONFIG_RAPIDIO_DISC_TIMEOUT * HZ;
- rio_enum_timer.data = (unsigned long)&enum_timeout_flag;
- add_timer(&rio_enum_timer);
- while (!rio_enum_complete(mport)) {
- mdelay(1);
- if (enum_timeout_flag) {
- del_timer_sync(&rio_enum_timer);
- goto timeout;
- }
+ to_end = jiffies + CONFIG_RAPIDIO_DISC_TIMEOUT * HZ;
+ while (time_before(jiffies, to_end)) {
+ if (rio_enum_complete(mport))
+ goto enum_done;
+ schedule_timeout_uninterruptible(msecs_to_jiffies(10));
}
- del_timer_sync(&rio_enum_timer);
- pr_debug("done\n");
+ pr_debug("RIO: discovery timeout on mport %d %s\n",
+ mport->id, mport->name);
+ goto bail;
+enum_done:
+ pr_debug("RIO: ... enumeration done\n");
+
+ net = rio_alloc_net(mport);
+ if (!net) {
+ printk(KERN_ERR "RIO: Failed to allocate new net\n");
+ goto bail;
+ }
/* Read DestID assigned by enumerator */
rio_local_read_config_32(mport, RIO_DID_CSR,
@@ -1307,9 +1288,6 @@ int __devinit rio_disc_mport(struct rio_mport *mport)
}
return 0;
-
- timeout:
- pr_debug("timeout\n");
- bail:
+bail:
return -EBUSY;
}
--
1.7.8.4
^ permalink raw reply related
* [PATCH 0/5] rapidio: patches to support multiple master ports
From: Alexandre Bounine @ 2012-10-03 19:18 UTC (permalink / raw)
To: Andrew Morton, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine
The following set of patches provides modifications targeting support of multiple
RapidIO master port (mport) devices on a CPU-side of RapidIO-capable board.
While the RapidIO subsystem code has definitions suitable for
multi-controller/multi-net support, the existing implementation cannot be
considered ready for multiple mport configurations.
=========== NOTES: =============
a) The patches below do not address RapidIO side view of multiport processing
elements defined in Part 6 of RapidIO spec Rev.2.1 (section 6.4.1).
These devices have Base Device ID CSR (0x60) and Component Tag CSR (0x6C) shared
by all SRIO ports. For example, Freescale's P4080, P3041 and P5020 have a
dual-port SRIO controller implemented according the specification.
Enumeration/discovery of such devices from RapidIO side may require
device-specific fixups.
b) Devices referenced above may also require implementation specific code to setup
a host device ID for mport device. These operations are not addressed by patches
in this package.
=================================
Details about provided patches:
1. Fix blocking wait for discovery ready
While it does not happen on PowerPC based platforms, there is possibility of
stalled CPU warning dump on x86 based platforms that run RapidIO discovery
process if they wait too long for being enumerated.
Currently users can avoid it by disabling the soft-lockup detector using
"nosoftlockup" kernel parameter OR by ensuring that enumeration is completed
before soft-lockup is detected.
This patch eliminates blocking wait and keeps a scheduler running.
It also is required for patch 3 below which introduces asynchronous discovery
process.
2. Use device lists handling on per-net basis
This patch allows to correctly support multiple RapidIO nets and resolves possible
issues caused by using single global list of devices during RapidIO system
enumeration/discovery. The most common sign of existing issue is incorrect
contents of switch routing tables in systems with multiple mport controllers
while single-port configuration performs as expected.
The patch does not eliminate the global RapidIO device list but changes some
routines in enumeration/discovery to use per-net device lists instead. This way
compatibility with upper layer RIO routines is preserved.
3. Run discovery as an asynchronous process
This patch modifies RapidIO initialization routine to asynchronously run the
discovery process for each corresponding mport. This allows having an arbitrary
order of enumerating and discovering mports without creating a deadlock situation
if an enumerator port was registered after a discovering one.
On boards with multiple discovering mports it also eliminates order dependency
between mports and may reduce total time of RapidIO subsystem initialization.
Making netID matching to mportID ensures consistent netID assignment in
multiport RapidIO systems with asynchronous discovery process (global counter
implementation is affected by race between threads).
4. Rework RIONET to support multiple RIO master ports
In the current version of the driver rionet_probe() has comment
"XXX Make multi-net safe". Now it is a good time to address this comment.
This patch makes RIONET driver multi-net safe/capable by introducing per-net
lists of RapidIO network peers. It also enables to register network adapters for
all available mport devices.
5. Add destination ID allocation mechanism
The patch replaces a single global destination ID counter with per-net allocation
mechanism to allow independent destID management for each available RapidIO
network. Using bitmap based mechanism instead of counters allows destination ID
release and reuse in systems that support hot-swap.
Alexandre Bounine (5):
rapidio: fix blocking wait for discovery ready
rapidio: use device lists handling on per-net basis
rapidio: run discovery as an asynchronous process
rapidio/rionet: rework to support multiple RIO master ports
rapidio: add destination ID allocation mechanism
drivers/net/rionet.c | 133 ++++++++++---------
drivers/rapidio/rio-scan.c | 326 +++++++++++++++++++++++++++++--------------
drivers/rapidio/rio.c | 51 +++++++-
include/linux/rio.h | 10 ++
4 files changed, 349 insertions(+), 171 deletions(-)
--
1.7.8.4
^ permalink raw reply
* [PATCH 2/5] rapidio: use device lists handling on per-net basis
From: Alexandre Bounine @ 2012-10-03 19:18 UTC (permalink / raw)
To: Andrew Morton, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine
In-Reply-To: <1349291923-22860-1-git-send-email-alexandre.bounine@idt.com>
Modify handling of device lists to resolve issues caused by using single global
list of RIO devices during enumeration/discovery. The most common sign of
existing issue is incorrect contents of switch routing tables in systems with
multiple mport controllers while single-port configuration performs as expected.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
---
drivers/rapidio/rio-scan.c | 60 ++++++++++++++++++++++---------------------
include/linux/rio.h | 1 +
2 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 0a27253..8b7c4bc 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -38,7 +38,6 @@
#include "rio.h"
LIST_HEAD(rio_devices);
-static LIST_HEAD(rio_switches);
static void rio_init_em(struct rio_dev *rdev);
@@ -104,14 +103,15 @@ static void rio_local_set_device_id(struct rio_mport *port, u16 did)
/**
* rio_clear_locks- Release all host locks and signal enumeration complete
- * @port: Master port to issue transaction
+ * @net: RIO network to run on
*
* Marks the component tag CSR on each device with the enumeration
* complete flag. When complete, it then release the host locks on
* each device. Returns 0 on success or %-EINVAL on failure.
*/
-static int rio_clear_locks(struct rio_mport *port)
+static int rio_clear_locks(struct rio_net *net)
{
+ struct rio_mport *port = net->hport;
struct rio_dev *rdev;
u32 result;
int ret = 0;
@@ -126,7 +126,7 @@ static int rio_clear_locks(struct rio_mport *port)
result);
ret = -EINVAL;
}
- list_for_each_entry(rdev, &rio_devices, global_list) {
+ list_for_each_entry(rdev, &net->devices, net_list) {
rio_write_config_32(rdev, RIO_HOST_DID_LOCK_CSR,
port->host_deviceid);
rio_read_config_32(rdev, RIO_HOST_DID_LOCK_CSR, &result);
@@ -479,7 +479,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
rswitch->clr_table(port, destid, hopcount,
RIO_GLOBAL_TABLE);
- list_add_tail(&rswitch->node, &rio_switches);
+ list_add_tail(&rswitch->node, &net->switches);
} else {
if (do_enum)
@@ -1058,6 +1058,7 @@ static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port)
if (net) {
INIT_LIST_HEAD(&net->node);
INIT_LIST_HEAD(&net->devices);
+ INIT_LIST_HEAD(&net->switches);
INIT_LIST_HEAD(&net->mports);
list_add_tail(&port->nnode, &net->mports);
net->hport = port;
@@ -1068,24 +1069,24 @@ static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port)
/**
* rio_update_route_tables- Updates route tables in switches
- * @port: Master port associated with the RIO network
+ * @net: RIO network to run update on
*
* For each enumerated device, ensure that each switch in a system
* has correct routing entries. Add routes for devices that where
* unknown dirung the first enumeration pass through the switch.
*/
-static void rio_update_route_tables(struct rio_mport *port)
+static void rio_update_route_tables(struct rio_net *net)
{
struct rio_dev *rdev, *swrdev;
struct rio_switch *rswitch;
u8 sport;
u16 destid;
- list_for_each_entry(rdev, &rio_devices, global_list) {
+ list_for_each_entry(rdev, &net->devices, net_list) {
destid = rdev->destid;
- list_for_each_entry(rswitch, &rio_switches, node) {
+ list_for_each_entry(rswitch, &net->switches, node) {
if (rio_is_switch(rdev) && (rdev->rswitch == rswitch))
continue;
@@ -1181,12 +1182,12 @@ int __devinit rio_enum_mport(struct rio_mport *mport)
printk(KERN_INFO
"RIO: master port %d device has lost enumeration to a remote host\n",
mport->id);
- rio_clear_locks(mport);
+ rio_clear_locks(net);
rc = -EBUSY;
goto out;
}
- rio_update_route_tables(mport);
- rio_clear_locks(mport);
+ rio_update_route_tables(net);
+ rio_clear_locks(net);
rio_pw_enable(mport, 1);
} else {
printk(KERN_INFO "RIO: master port %d link inactive\n",
@@ -1200,33 +1201,34 @@ int __devinit rio_enum_mport(struct rio_mport *mport)
/**
* rio_build_route_tables- Generate route tables from switch route entries
+ * @net: RIO network to run route tables scan on
*
* For each switch device, generate a route table by copying existing
* route entries from the switch.
*/
-static void rio_build_route_tables(void)
+static void rio_build_route_tables(struct rio_net *net)
{
+ struct rio_switch *rswitch;
struct rio_dev *rdev;
int i;
u8 sport;
- list_for_each_entry(rdev, &rio_devices, global_list)
- if (rio_is_switch(rdev)) {
- rio_lock_device(rdev->net->hport, rdev->destid,
- rdev->hopcount, 1000);
- for (i = 0;
- i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size);
- i++) {
- if (rio_route_get_entry(rdev,
- RIO_GLOBAL_TABLE, i, &sport, 0) < 0)
- continue;
- rdev->rswitch->route_table[i] = sport;
- }
+ list_for_each_entry(rswitch, &net->switches, node) {
+ rdev = sw_to_rio_dev(rswitch);
- rio_unlock_device(rdev->net->hport,
- rdev->destid,
- rdev->hopcount);
+ rio_lock_device(net->hport, rdev->destid,
+ rdev->hopcount, 1000);
+ for (i = 0;
+ i < RIO_MAX_ROUTE_ENTRIES(net->hport->sys_size);
+ i++) {
+ if (rio_route_get_entry(rdev, RIO_GLOBAL_TABLE,
+ i, &sport, 0) < 0)
+ continue;
+ rswitch->route_table[i] = sport;
}
+
+ rio_unlock_device(net->hport, rdev->destid, rdev->hopcount);
+ }
}
/**
@@ -1284,7 +1286,7 @@ enum_done:
goto bail;
}
- rio_build_route_tables();
+ rio_build_route_tables(net);
}
return 0;
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 4d1a104..7ea02c4 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -275,6 +275,7 @@ struct rio_mport {
struct rio_net {
struct list_head node; /* node in list of networks */
struct list_head devices; /* list of devices in this net */
+ struct list_head switches; /* list of switches in this net */
struct list_head mports; /* list of ports accessing net */
struct rio_mport *hport; /* primary port for accessing net */
unsigned char id; /* RIO network ID */
--
1.7.8.4
^ permalink raw reply related
* [PATCH 5/5] rapidio: add destination ID allocation mechanism
From: Alexandre Bounine @ 2012-10-03 19:18 UTC (permalink / raw)
To: Andrew Morton, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine
In-Reply-To: <1349291923-22860-1-git-send-email-alexandre.bounine@idt.com>
Replace the single global destination ID counter with per-net allocation
mechanism to allow independent destID management for each available RapidIO
network. Using bitmap based mechanism instead of counters allows
destination ID release and reuse in systems that support hot-swap.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
---
drivers/rapidio/rio-scan.c | 205 ++++++++++++++++++++++++++++++++++++--------
include/linux/rio.h | 9 ++
2 files changed, 179 insertions(+), 35 deletions(-)
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 745670f..48e9041 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -54,6 +54,114 @@ static int rio_mport_phys_table[] = {
-1,
};
+
+/*
+ * rio_destid_alloc - Allocate next available destID for given network
+ * net: RIO network
+ *
+ * Returns next available device destination ID for the specified RIO network.
+ * Marks allocated ID as one in use.
+ * Returns RIO_INVALID_DESTID if new destID is not available.
+ */
+static u16 rio_destid_alloc(struct rio_net *net)
+{
+ int destid;
+ struct rio_id_table *idtab = &net->destid_table;
+
+ spin_lock(&idtab->lock);
+ destid = find_next_zero_bit(idtab->table, idtab->max, idtab->next);
+ if (destid >= idtab->max)
+ destid = find_first_zero_bit(idtab->table, idtab->max);
+
+ if (destid < idtab->max) {
+ idtab->next = destid + 1;
+ if (idtab->next >= idtab->max)
+ idtab->next = 0;
+ set_bit(destid, idtab->table);
+ destid += idtab->start;
+ } else
+ destid = RIO_INVALID_DESTID;
+
+ spin_unlock(&idtab->lock);
+ return (u16)destid;
+}
+
+/*
+ * rio_destid_reserve - Reserve the specivied destID
+ * net: RIO network
+ * destid: destID to reserve
+ *
+ * Tries to reserve the specified destID.
+ * Returns 0 if successfull.
+ */
+static int rio_destid_reserve(struct rio_net *net, u16 destid)
+{
+ int oldbit;
+ struct rio_id_table *idtab = &net->destid_table;
+
+ destid -= idtab->start;
+ spin_lock(&idtab->lock);
+ oldbit = test_and_set_bit(destid, idtab->table);
+ spin_unlock(&idtab->lock);
+ return oldbit;
+}
+
+/*
+ * rio_destid_free - free a previously allocated destID
+ * net: RIO network
+ * destid: destID to free
+ *
+ * Makes the specified destID available for use.
+ */
+static void rio_destid_free(struct rio_net *net, u16 destid)
+{
+ struct rio_id_table *idtab = &net->destid_table;
+
+ destid -= idtab->start;
+ spin_lock(&idtab->lock);
+ clear_bit(destid, idtab->table);
+ spin_unlock(&idtab->lock);
+}
+
+/*
+ * rio_destid_first - return first destID in use
+ * net: RIO network
+ */
+static u16 rio_destid_first(struct rio_net *net)
+{
+ int destid;
+ struct rio_id_table *idtab = &net->destid_table;
+
+ spin_lock(&idtab->lock);
+ destid = find_first_bit(idtab->table, idtab->max);
+ if (destid >= idtab->max)
+ destid = RIO_INVALID_DESTID;
+ else
+ destid += idtab->start;
+ spin_unlock(&idtab->lock);
+ return (u16)destid;
+}
+
+/*
+ * rio_destid_next - return next destID in use
+ * net: RIO network
+ * from: destination ID from which search shall continue
+ */
+static u16 rio_destid_next(struct rio_net *net, u16 from)
+{
+ int destid;
+ struct rio_id_table *idtab = &net->destid_table;
+
+ spin_lock(&idtab->lock);
+ destid = find_next_bit(idtab->table, idtab->max, from);
+ if (destid >= idtab->max)
+ destid = RIO_INVALID_DESTID;
+ else
+ destid += idtab->start;
+ spin_unlock(&idtab->lock);
+ return (u16)destid;
+}
+
/**
* rio_get_device_id - Get the base/extended device id for a device
* @port: RIO master port
@@ -171,10 +279,6 @@ static int rio_enum_host(struct rio_mport *port)
/* Set master port destid and init destid ctr */
rio_local_set_device_id(port, port->host_deviceid);
-
- if (next_destid == port->host_deviceid)
- next_destid++;
-
return 0;
}
@@ -441,9 +545,8 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
if (rio_device_has_destid(port, rdev->src_ops, rdev->dst_ops)) {
if (do_enum) {
rio_set_device_id(port, destid, hopcount, next_destid);
- rdev->destid = next_destid++;
- if (next_destid == port->host_deviceid)
- next_destid++;
+ rdev->destid = next_destid;
+ next_destid = rio_destid_alloc(net);
} else
rdev->destid = rio_get_device_id(port, destid, hopcount);
@@ -742,12 +845,7 @@ static u16 rio_get_host_deviceid_lock(struct rio_mport *port, u8 hopcount)
static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
u8 hopcount, struct rio_dev *prev, int prev_port)
{
- int port_num;
- int cur_destid;
- int sw_destid;
- int sw_inport;
struct rio_dev *rdev;
- u16 destid;
u32 regval;
int tmp;
@@ -813,19 +911,26 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
return -1;
if (rio_is_switch(rdev)) {
+ int sw_destid;
+ int cur_destid;
+ int sw_inport;
+ u16 destid;
+ int port_num;
+
sw_inport = RIO_GET_PORT_NUM(rdev->swpinfo);
rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
port->host_deviceid, sw_inport, 0);
rdev->rswitch->route_table[port->host_deviceid] = sw_inport;
- for (destid = 0; destid < next_destid; destid++) {
- if (destid == port->host_deviceid)
- continue;
- rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
- destid, sw_inport, 0);
- rdev->rswitch->route_table[destid] = sw_inport;
+ destid = rio_destid_first(net);
+ while (destid != RIO_INVALID_DESTID && destid < next_destid) {
+ if (destid != port->host_deviceid) {
+ rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
+ destid, sw_inport, 0);
+ rdev->rswitch->route_table[destid] = sw_inport;
+ }
+ destid = rio_destid_next(net, destid + 1);
}
-
pr_debug(
"RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
rio_name(rdev), rdev->vid, rdev->did,
@@ -863,19 +968,22 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
return -1;
/* Update routing tables */
- if (next_destid > cur_destid) {
+ destid = rio_destid_next(net, cur_destid + 1);
+ if (destid != RIO_INVALID_DESTID) {
for (destid = cur_destid;
- destid < next_destid; destid++) {
- if (destid == port->host_deviceid)
- continue;
- rio_route_add_entry(rdev,
+ destid < next_destid;) {
+ if (destid != port->host_deviceid) {
+ rio_route_add_entry(rdev,
RIO_GLOBAL_TABLE,
destid,
port_num,
0);
- rdev->rswitch->
- route_table[destid] =
- port_num;
+ rdev->rswitch->
+ route_table[destid] =
+ port_num;
+ }
+ destid = rio_destid_next(net,
+ destid + 1);
}
}
} else {
@@ -901,11 +1009,8 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
rio_init_em(rdev);
/* Check for empty switch */
- if (next_destid == sw_destid) {
- next_destid++;
- if (next_destid == port->host_deviceid)
- next_destid++;
- }
+ if (next_destid == sw_destid)
+ next_destid = rio_destid_alloc(net);
rdev->destid = sw_destid;
} else
@@ -1043,17 +1148,39 @@ static int rio_mport_is_active(struct rio_mport *port)
/**
* rio_alloc_net- Allocate and configure a new RIO network
* @port: Master port associated with the RIO network
+ * @do_enum: Enumeration/Discovery mode flag
+ * @start: logical minimal start id for new net
*
* Allocates a RIO network structure, initializes per-network
* list heads, and adds the associated master port to the
* network list of associated master ports. Returns a
* RIO network pointer on success or %NULL on failure.
*/
-static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port)
+static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port,
+ int do_enum, u16 start)
{
struct rio_net *net;
net = kzalloc(sizeof(struct rio_net), GFP_KERNEL);
+ if (net && do_enum) {
+ net->destid_table.table = kzalloc(
+ BITS_TO_LONGS(RIO_MAX_ROUTE_ENTRIES(port->sys_size)) *
+ sizeof(long),
+ GFP_KERNEL);
+
+ if (net->destid_table.table == NULL) {
+ pr_err("RIO: failed to allocate destID table\n");
+ kfree(net);
+ net = NULL;
+ } else {
+ net->destid_table.start = start;
+ net->destid_table.next = 0;
+ net->destid_table.max =
+ RIO_MAX_ROUTE_ENTRIES(port->sys_size);
+ spin_lock_init(&net->destid_table.lock);
+ }
+ }
+
if (net) {
INIT_LIST_HEAD(&net->node);
INIT_LIST_HEAD(&net->devices);
@@ -1163,12 +1290,16 @@ int __devinit rio_enum_mport(struct rio_mport *mport)
/* If master port has an active link, allocate net and enum peers */
if (rio_mport_is_active(mport)) {
- if (!(net = rio_alloc_net(mport))) {
+ net = rio_alloc_net(mport, 1, 0);
+ if (!net) {
printk(KERN_ERR "RIO: failed to allocate new net\n");
rc = -ENOMEM;
goto out;
}
+ /* reserve mport destID in new net */
+ rio_destid_reserve(net, mport->host_deviceid);
+
/* Enable Input Output Port (transmitter reviever) */
rio_enable_rx_tx_port(mport, 1, 0, 0, 0);
@@ -1176,6 +1307,8 @@ int __devinit rio_enum_mport(struct rio_mport *mport)
rio_local_write_config_32(mport, RIO_COMPONENT_TAG_CSR,
next_comptag++);
+ next_destid = rio_destid_alloc(net);
+
if (rio_enum_peer(net, mport, 0, NULL, 0) < 0) {
/* A higher priority host won enumeration, bail. */
printk(KERN_INFO
@@ -1185,6 +1318,8 @@ int __devinit rio_enum_mport(struct rio_mport *mport)
rc = -EBUSY;
goto out;
}
+ /* free the last allocated destID (unused) */
+ rio_destid_free(net, next_destid);
rio_update_route_tables(net);
rio_clear_locks(net);
rio_pw_enable(mport, 1);
@@ -1265,7 +1400,7 @@ int __devinit rio_disc_mport(struct rio_mport *mport)
enum_done:
pr_debug("RIO: ... enumeration done\n");
- net = rio_alloc_net(mport);
+ net = rio_alloc_net(mport, 0, 0);
if (!net) {
printk(KERN_ERR "RIO: Failed to allocate new net\n");
goto bail;
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 7ea02c4..d2dff22 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -264,6 +264,14 @@ struct rio_mport {
#endif
};
+struct rio_id_table {
+ u16 start; /* logical minimal id */
+ u16 next; /* hint for find */
+ u32 max; /* max number of IDs in table */
+ spinlock_t lock;
+ unsigned long *table;
+};
+
/**
* struct rio_net - RIO network info
* @node: Node in global list of RIO networks
@@ -279,6 +287,7 @@ struct rio_net {
struct list_head mports; /* list of ports accessing net */
struct rio_mport *hport; /* primary port for accessing net */
unsigned char id; /* RIO network ID */
+ struct rio_id_table destid_table; /* destID allocation table */
};
/* Definitions used by switch sysfs initialization callback */
--
1.7.8.4
^ permalink raw reply related
* [PATCH 4/5] rapidio/rionet: rework to support multiple RIO master ports
From: Alexandre Bounine @ 2012-10-03 19:18 UTC (permalink / raw)
To: Andrew Morton, linux-kernel, linuxppc-dev
Cc: Alexandre Bounine, David S. Miller
In-Reply-To: <1349291923-22860-1-git-send-email-alexandre.bounine@idt.com>
Make RIONET driver multi-net safe/capable by introducing per-net lists of
RapidIO network peers. Rework registration of network adapters to support
all available RIO master port devices.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: David S. Miller <davem@davemloft.net>
---
drivers/net/rionet.c | 133 ++++++++++++++++++++++++++------------------------
1 files changed, 70 insertions(+), 63 deletions(-)
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index 1470d3e..d8b9b1e 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -26,7 +26,7 @@
#include <linux/ethtool.h>
#define DRV_NAME "rionet"
-#define DRV_VERSION "0.2"
+#define DRV_VERSION "0.3"
#define DRV_AUTHOR "Matt Porter <mporter@kernel.crashing.org>"
#define DRV_DESC "Ethernet over RapidIO"
@@ -47,8 +47,7 @@ MODULE_LICENSE("GPL");
#define RIONET_TX_RING_SIZE CONFIG_RIONET_TX_SIZE
#define RIONET_RX_RING_SIZE CONFIG_RIONET_RX_SIZE
-
-static LIST_HEAD(rionet_peers);
+#define RIONET_MAX_NETS 8
struct rionet_private {
struct rio_mport *mport;
@@ -69,17 +68,14 @@ struct rionet_peer {
struct resource *res;
};
-static int rionet_check = 0;
-static int rionet_capable = 1;
+struct rionet_net {
+ struct net_device *ndev;
+ struct list_head peers;
+ struct rio_dev **active;
+ int nact; /* number of active peers */
+};
-/*
- * This is a fast lookup table for translating TX
- * Ethernet packets into a destination RIO device. It
- * could be made into a hash table to save memory depending
- * on system trade-offs.
- */
-static struct rio_dev **rionet_active;
-static int nact; /* total number of active rionet peers */
+static struct rionet_net nets[RIONET_MAX_NETS];
#define is_rionet_capable(src_ops, dst_ops) \
((src_ops & RIO_SRC_OPS_DATA_MSG) && \
@@ -185,7 +181,7 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
}
if (is_multicast_ether_addr(eth->h_dest))
- add_num = nact;
+ add_num = nets[rnet->mport->id].nact;
if ((rnet->tx_cnt + add_num) > RIONET_TX_RING_SIZE) {
netif_stop_queue(ndev);
@@ -197,19 +193,21 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
if (is_multicast_ether_addr(eth->h_dest)) {
int count = 0;
+
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size);
i++)
- if (rionet_active[i]) {
+ if (nets[rnet->mport->id].active[i]) {
rionet_queue_tx_msg(skb, ndev,
- rionet_active[i]);
+ nets[rnet->mport->id].active[i]);
if (count)
atomic_inc(&skb->users);
count++;
}
} else if (RIONET_MAC_MATCH(eth->h_dest)) {
destid = RIONET_GET_DESTID(eth->h_dest);
- if (rionet_active[destid])
- rionet_queue_tx_msg(skb, ndev, rionet_active[destid]);
+ if (nets[rnet->mport->id].active[destid])
+ rionet_queue_tx_msg(skb, ndev,
+ nets[rnet->mport->id].active[destid]);
}
spin_unlock_irqrestore(&rnet->tx_lock, flags);
@@ -228,19 +226,21 @@ static void rionet_dbell_event(struct rio_mport *mport, void *dev_id, u16 sid, u
printk(KERN_INFO "%s: doorbell sid %4.4x tid %4.4x info %4.4x",
DRV_NAME, sid, tid, info);
if (info == RIONET_DOORBELL_JOIN) {
- if (!rionet_active[sid]) {
- list_for_each_entry(peer, &rionet_peers, node) {
+ if (!nets[rnet->mport->id].active[sid]) {
+ list_for_each_entry(peer,
+ &nets[rnet->mport->id].peers, node) {
if (peer->rdev->destid == sid) {
- rionet_active[sid] = peer->rdev;
- nact++;
+ nets[rnet->mport->id].active[sid] =
+ peer->rdev;
+ nets[rnet->mport->id].nact++;
}
}
rio_mport_send_doorbell(mport, sid,
RIONET_DOORBELL_JOIN);
}
} else if (info == RIONET_DOORBELL_LEAVE) {
- rionet_active[sid] = NULL;
- nact--;
+ nets[rnet->mport->id].active[sid] = NULL;
+ nets[rnet->mport->id].nact--;
} else {
if (netif_msg_intr(rnet))
printk(KERN_WARNING "%s: unhandled doorbell\n",
@@ -334,7 +334,8 @@ static int rionet_open(struct net_device *ndev)
netif_carrier_on(ndev);
netif_start_queue(ndev);
- list_for_each_entry_safe(peer, tmp, &rionet_peers, node) {
+ list_for_each_entry_safe(peer, tmp,
+ &nets[rnet->mport->id].peers, node) {
if (!(peer->res = rio_request_outb_dbell(peer->rdev,
RIONET_DOORBELL_JOIN,
RIONET_DOORBELL_LEAVE)))
@@ -359,7 +360,7 @@ static int rionet_close(struct net_device *ndev)
int i;
if (netif_msg_ifup(rnet))
- printk(KERN_INFO "%s: close\n", DRV_NAME);
+ printk(KERN_INFO "%s: close %s\n", DRV_NAME, ndev->name);
netif_stop_queue(ndev);
netif_carrier_off(ndev);
@@ -367,10 +368,11 @@ static int rionet_close(struct net_device *ndev)
for (i = 0; i < RIONET_RX_RING_SIZE; i++)
kfree_skb(rnet->rx_skb[i]);
- list_for_each_entry_safe(peer, tmp, &rionet_peers, node) {
- if (rionet_active[peer->rdev->destid]) {
+ list_for_each_entry_safe(peer, tmp,
+ &nets[rnet->mport->id].peers, node) {
+ if (nets[rnet->mport->id].active[peer->rdev->destid]) {
rio_send_doorbell(peer->rdev, RIONET_DOORBELL_LEAVE);
- rionet_active[peer->rdev->destid] = NULL;
+ nets[rnet->mport->id].active[peer->rdev->destid] = NULL;
}
rio_release_outb_dbell(peer->rdev, peer->res);
}
@@ -386,17 +388,21 @@ static int rionet_close(struct net_device *ndev)
static void rionet_remove(struct rio_dev *rdev)
{
struct net_device *ndev = rio_get_drvdata(rdev);
+ unsigned char netid = rdev->net->hport->id;
struct rionet_peer *peer, *tmp;
- free_pages((unsigned long)rionet_active, get_order(sizeof(void *) *
- RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size)));
unregister_netdev(ndev);
- free_netdev(ndev);
- list_for_each_entry_safe(peer, tmp, &rionet_peers, node) {
+ free_pages((unsigned long)nets[netid].active, get_order(sizeof(void *) *
+ RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size)));
+ nets[netid].active = NULL;
+
+ list_for_each_entry_safe(peer, tmp, &nets[netid].peers, node) {
list_del(&peer->node);
kfree(peer);
}
+
+ free_netdev(ndev);
}
static void rionet_get_drvinfo(struct net_device *ndev,
@@ -448,13 +454,13 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
const size_t rionet_active_bytes = sizeof(void *) *
RIO_MAX_ROUTE_ENTRIES(mport->sys_size);
- rionet_active = (struct rio_dev **)__get_free_pages(GFP_KERNEL,
- get_order(rionet_active_bytes));
- if (!rionet_active) {
+ nets[mport->id].active = (struct rio_dev **)__get_free_pages(GFP_KERNEL,
+ get_order(rionet_active_bytes));
+ if (!nets[mport->id].active) {
rc = -ENOMEM;
goto out;
}
- memset((void *)rionet_active, 0, rionet_active_bytes);
+ memset((void *)nets[mport->id].active, 0, rionet_active_bytes);
/* Set up private area */
rnet = netdev_priv(ndev);
@@ -483,61 +489,62 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
if (rc != 0)
goto out;
- printk("%s: %s %s Version %s, MAC %pM\n",
+ printk(KERN_INFO "%s: %s %s Version %s, MAC %pM, %s\n",
ndev->name,
DRV_NAME,
DRV_DESC,
DRV_VERSION,
- ndev->dev_addr);
+ ndev->dev_addr,
+ mport->name);
out:
return rc;
}
-/*
- * XXX Make multi-net safe
- */
+static unsigned long net_table[RIONET_MAX_NETS/sizeof(unsigned long) + 1];
+
static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)
{
int rc = -ENODEV;
u32 lsrc_ops, ldst_ops;
struct rionet_peer *peer;
struct net_device *ndev = NULL;
+ unsigned char netid = rdev->net->hport->id;
+ int oldnet;
- /* If local device is not rionet capable, give up quickly */
- if (!rionet_capable)
- goto out;
+ if (netid >= RIONET_MAX_NETS)
+ return rc;
- /* Allocate our net_device structure */
- ndev = alloc_etherdev(sizeof(struct rionet_private));
- if (ndev == NULL) {
- rc = -ENOMEM;
- goto out;
- }
+ oldnet = test_and_set_bit(netid, net_table);
/*
* First time through, make sure local device is rionet
- * capable, setup netdev, and set flags so this is skipped
- * on later probes
+ * capable, setup netdev (will be skipped on later probes)
*/
- if (!rionet_check) {
+ if (!oldnet) {
rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR,
&lsrc_ops);
rio_local_read_config_32(rdev->net->hport, RIO_DST_OPS_CAR,
&ldst_ops);
if (!is_rionet_capable(lsrc_ops, ldst_ops)) {
printk(KERN_ERR
- "%s: local device is not network capable\n",
- DRV_NAME);
- rionet_check = 1;
- rionet_capable = 0;
+ "%s: local device %s is not network capable\n",
+ DRV_NAME, rdev->net->hport->name);
goto out;
}
+ /* Allocate our net_device structure */
+ ndev = alloc_etherdev(sizeof(struct rionet_private));
+ if (ndev == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ nets[netid].ndev = ndev;
rc = rionet_setup_netdev(rdev->net->hport, ndev);
- rionet_check = 1;
- nact = 0;
- }
+ INIT_LIST_HEAD(&nets[netid].peers);
+ nets[netid].nact = 0;
+ } else if (nets[netid].ndev == NULL)
+ goto out;
/*
* If the remote device has mailbox/doorbell capabilities,
@@ -549,10 +556,10 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)
goto out;
}
peer->rdev = rdev;
- list_add_tail(&peer->node, &rionet_peers);
+ list_add_tail(&peer->node, &nets[netid].peers);
}
- rio_set_drvdata(rdev, ndev);
+ rio_set_drvdata(rdev, nets[netid].ndev);
out:
return rc;
--
1.7.8.4
^ permalink raw reply related
* Re: [PATCH 1/5] rapidio: fix blocking wait for discovery ready
From: Andrew Morton @ 2012-10-03 22:20 UTC (permalink / raw)
To: Alexandre Bounine; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1349291923-22860-2-git-send-email-alexandre.bounine@idt.com>
On Wed, 3 Oct 2012 15:18:39 -0400
Alexandre Bounine <alexandre.bounine@idt.com> wrote:
> Fix blocking wait loop in the RapidIO discovery routine to avoid warning
> dumps about stalled CPU on x86 platforms.
>
> ...
>
> + to_end = jiffies + CONFIG_RAPIDIO_DISC_TIMEOUT * HZ;
> + while (time_before(jiffies, to_end)) {
> + if (rio_enum_complete(mport))
> + goto enum_done;
> + schedule_timeout_uninterruptible(msecs_to_jiffies(10));
I think a simple msleep(10) would suffice here?
^ permalink raw reply
* Re: [RFC PATCH] powerpc/fsl: add timer wakeup source
From: Scott Wood @ 2012-10-03 22:20 UTC (permalink / raw)
To: Kumar Gala
Cc: Wood Scott-B07421, linux-pm, Wang Dongsheng, Wang Dongsheng,
linuxppc-dev@lists.ozlabs.org list
In-Reply-To: <6170650E-9D3C-4686-BF13-6C41DBFFB79D@kernel.crashing.org>
On 10/03/2012 08:35:58 AM, Kumar Gala wrote:
>=20
> On Oct 3, 2012, at 5:42 AM, Wang Dongsheng wrote:
>=20
> > This is only for freescale powerpc platform. The driver provides a =20
> way
> > to wake up system. Proc =20
> interface(/proc/powerpc/wakeup_timer_seconds).
> >
> > eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds
> > after the system will be woken up. echo another time into proc =20
> interface
> > to update the time.
> >
> > Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> > Signed-off-by: Li Yang <leoli@freescale.com>
> > ---
> > arch/powerpc/platforms/Kconfig | 23 +++
> > arch/powerpc/platforms/Makefile | 1 +
> > arch/powerpc/platforms/fsl_timer_wakeup.c | 217 =20
> +++++++++++++++++++++++++++++
> > 3 files changed, 241 insertions(+)
> > create mode 100644 arch/powerpc/platforms/fsl_timer_wakeup.c
>=20
> Adding the Linux PM list to see if there is some existing support for =20
> this on other arch's in kernel.
>=20
> I'm pretty sure /proc/ is NOT where we want this exposed.
Should probably go under the sysfs directory of the mpic device. Or =20
better, make a generic interface for timer-based suspend wakeup (if =20
there isn't one already). This current approach sits in an unpleasant =20
middle ground between generic and device-specific.
> > diff --git a/arch/powerpc/platforms/Kconfig =20
> b/arch/powerpc/platforms/Kconfig
> > index b190a6e..7b9232a 100644
> > --- a/arch/powerpc/platforms/Kconfig
> > +++ b/arch/powerpc/platforms/Kconfig
> > @@ -99,6 +99,29 @@ config MPIC_TIMER
> > only tested on fsl chip, but it can potentially support
> > other global timers complying to Open-PIC standard.
> >
> > +menuconfig FSL_WAKEUP_SOURCE
> > + bool "Freescale wakeup source"
> > + depends on FSL_SOC && SUSPEND
> > + default n
> > + help
> > + This option enables wakeup source for wake up system
> > + features. This is only for freescale powerpc platform.
> > +
> > +if FSL_WAKEUP_SOURCE
> > +
> > +config FSL_TIMER_WAKEUP
> > + tristate "Freescale mpic global timer wakeup event"
> > + default n
> > + help
> > + This is only for freescale powerpc platform. The driver
> > + provides a way to wake up system.
> > + Proc interface(/proc/powerpc/wakeup_timer_seconds).
> > + eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds",
> > + 5 seconds after the system will be woken up. echo another
> > + time into proc interface to update the time.
> > +
> > +endif
Use depends rather than if/else. Why do you need FSL_WAKEUP_SOURCE?
These names are overly broad -- this is only for FSL MPIC, not for =20
other FSL chips (e.g. mpc83xx has a different global timer =20
implementation, and there's FSL ARM chips, etc).
> > +static ssize_t timer_wakeup_write(struct file *file, const char =20
> __user *buf,
> > + size_t count, loff_t *off)
> > +{
> > + struct fsl_timer_wakeup *priv;
> > + struct inode *inode =3D file->f_path.dentry->d_inode;
> > + struct proc_dir_entry *dp;
> > + struct timeval time;
> > + char *kbuf;
> > +
> > + dp =3D PDE(inode);
> > + priv =3D dp->data;
> > +
> > + kbuf =3D kzalloc(count + 1, GFP_KERNEL);
> > + if (!kbuf)
> > + return -ENOMEM;
> > +
> > + if (copy_from_user(kbuf, buf, count)) {
> > + kfree(kbuf);
> > + return -EFAULT;
> > + }
> > +
> > + kbuf[count] =3D '\0';
> > +
> > + if (kstrtol(kbuf, 0, &time.tv_sec)) {
> > + kfree(kbuf);
> > + return -EINVAL;
> > + }
> > +
> > + kfree(kbuf);
> > +
> > + time.tv_usec =3D 0;
> > +
> > + mutex_lock(&priv->mutex);
> > +
> > + if (!time.tv_sec) {
> > + if (priv->timer) {
> > + mpic_free_timer(priv->timer);
> > + priv->timer =3D NULL;
> > + }
> > + mutex_unlock(&priv->mutex);
> > +
> > + return count;
> > + }
> > +
> > + if (priv->timer) {
> > + mpic_free_timer(priv->timer);
> > + priv->timer =3D NULL;
> > + }
> > +
> > + priv->timer =3D mpic_request_timer(timer_event_interrupt, priv, =20
> &time);
If the new time is zero, consider that a cancellation of the timer and =20
don't request a new one or return -EINVAL.
-Scott=
^ permalink raw reply
* Re: [PATCH 3/5] rapidio: run discovery as an asynchronous process
From: Andrew Morton @ 2012-10-03 22:29 UTC (permalink / raw)
To: Alexandre Bounine; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1349291923-22860-4-git-send-email-alexandre.bounine@idt.com>
On Wed, 3 Oct 2012 15:18:41 -0400
Alexandre Bounine <alexandre.bounine@idt.com> wrote:
> Modify mport initialization routine to run the RapidIO discovery process
> asynchronously. This allows to have an arbitrary order of enumerating and
> discovering ports in systems with multiple RapidIO controllers without
> creating a deadlock situation if enumerator port is registered after a
> discovering one.
>
> Making netID matching to mportID ensures consistent net ID assignment in
> multiport RapidIO systems with asynchronous discovery process (global counter
> implementation is affected by race between threads).
>
>
> ...
>
> +static void __devinit disc_work_handler(struct work_struct *_work)
> +{
> + struct rio_disc_work *work = container_of(_work,
> + struct rio_disc_work, work);
There's a nice simple way to avoid such ugliness:
--- a/drivers/rapidio/rio.c~rapidio-run-discovery-as-an-asynchronous-process-fix
+++ a/drivers/rapidio/rio.c
@@ -1269,9 +1269,9 @@ struct rio_disc_work {
static void __devinit disc_work_handler(struct work_struct *_work)
{
- struct rio_disc_work *work = container_of(_work,
- struct rio_disc_work, work);
+ struct rio_disc_work *work;
+ work = container_of(_work, struct rio_disc_work, work);
pr_debug("RIO: discovery work for mport %d %s\n",
work->mport->id, work->mport->name);
rio_disc_mport(work->mport);
_
> + pr_debug("RIO: discovery work for mport %d %s\n",
> + work->mport->id, work->mport->name);
> + rio_disc_mport(work->mport);
> +
> + kfree(work);
> +}
> +
> int __devinit rio_init_mports(void)
> {
> struct rio_mport *port;
> + struct rio_disc_work *work;
> + int no_disc = 0;
>
> list_for_each_entry(port, &rio_mports, node) {
> if (port->host_deviceid >= 0)
> rio_enum_mport(port);
> - else
> - rio_disc_mport(port);
> + else if (!no_disc) {
> + if (!rio_wq) {
> + rio_wq = alloc_workqueue("riodisc", 0, 0);
> + if (!rio_wq) {
> + pr_err("RIO: unable allocate rio_wq\n");
> + no_disc = 1;
> + continue;
> + }
> + }
> +
> + work = kzalloc(sizeof *work, GFP_KERNEL);
> + if (!work) {
> + pr_err("RIO: no memory for work struct\n");
> + no_disc = 1;
> + continue;
> + }
> +
> + work->mport = port;
> + INIT_WORK(&work->work, disc_work_handler);
> + queue_work(rio_wq, &work->work);
> + }
> + }
I'm having a lot of trouble with `no_disc'. afacit what it does is to
cease running async discovery for any remaining devices if the workqueue
allocation failed (vaguely reasonable) or if the allocation of a single
work item failed (incomprehensible).
But if we don't run discovery, the subsystem is permanently busted for
at least some devices, isn't it?
And this code is basically untestable unless the programmer does
deliberate fault injection, which makes it pretty much unmaintainable.
So... if I haven't totally misunderstood, I suggest a rethink is in
order?
> + if (rio_wq) {
> + pr_debug("RIO: flush discovery workqueue\n");
> + flush_workqueue(rio_wq);
> + pr_debug("RIO: flush discovery workqueue finished\n");
> + destroy_workqueue(rio_wq);
> }
^ permalink raw reply
* Re: [PATCH 5/5] rapidio: add destination ID allocation mechanism
From: Andrew Morton @ 2012-10-03 22:36 UTC (permalink / raw)
To: Alexandre Bounine; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1349291923-22860-6-git-send-email-alexandre.bounine@idt.com>
On Wed, 3 Oct 2012 15:18:43 -0400
Alexandre Bounine <alexandre.bounine@idt.com> wrote:
> Replace the single global destination ID counter with per-net allocation
> mechanism to allow independent destID management for each available RapidIO
> network. Using bitmap based mechanism instead of counters allows
> destination ID release and reuse in systems that support hot-swap.
>
>
> ...
>
> +static u16 rio_destid_alloc(struct rio_net *net)
> +{
> + int destid;
> + struct rio_id_table *idtab = &net->destid_table;
> +
> + spin_lock(&idtab->lock);
> + destid = find_next_zero_bit(idtab->table, idtab->max, idtab->next);
> + if (destid >= idtab->max)
> + destid = find_first_zero_bit(idtab->table, idtab->max);
> +
> + if (destid < idtab->max) {
> + idtab->next = destid + 1;
> + if (idtab->next >= idtab->max)
> + idtab->next = 0;
> + set_bit(destid, idtab->table);
> + destid += idtab->start;
> + } else
> + destid = RIO_INVALID_DESTID;
> +
> + spin_unlock(&idtab->lock);
> + return (u16)destid;
> +}
This is round-robin rather than the simpler first-fit, and this reader
doesn't know why. Suggest the addition of a code comment explaining
this decision.
> +/*
> + * rio_destid_reserve - Reserve the specivied destID
> + * net: RIO network
> + * destid: destID to reserve
> + *
> + * Tries to reserve the specified destID.
> + * Returns 0 if successfull.
> + */
> +static int rio_destid_reserve(struct rio_net *net, u16 destid)
> +{
> + int oldbit;
> + struct rio_id_table *idtab = &net->destid_table;
> +
> + destid -= idtab->start;
> + spin_lock(&idtab->lock);
> + oldbit = test_and_set_bit(destid, idtab->table);
> + spin_unlock(&idtab->lock);
> + return oldbit;
> +}
> +
> +/*
> + * rio_destid_free - free a previously allocated destID
> + * net: RIO network
> + * destid: destID to free
> + *
> + * Makes the specified destID available for use.
> + */
> +static void rio_destid_free(struct rio_net *net, u16 destid)
> +{
> + struct rio_id_table *idtab = &net->destid_table;
> +
> + destid -= idtab->start;
> + spin_lock(&idtab->lock);
> + clear_bit(destid, idtab->table);
> + spin_unlock(&idtab->lock);
> +}
> +
> +/*
> + * rio_destid_first - return first destID in use
> + * net: RIO network
> + */
> +static u16 rio_destid_first(struct rio_net *net)
> +{
> + int destid;
> + struct rio_id_table *idtab = &net->destid_table;
> +
> + spin_lock(&idtab->lock);
> + destid = find_first_bit(idtab->table, idtab->max);
> + if (destid >= idtab->max)
> + destid = RIO_INVALID_DESTID;
> + else
> + destid += idtab->start;
> + spin_unlock(&idtab->lock);
> + return (u16)destid;
> +}
> +
> +/*
> + * rio_destid_next - return next destID in use
> + * net: RIO network
> + * from: destination ID from which search shall continue
> + */
All these code comments look like kerneldoc, but they aren't. kerneldoc
uses /** and identifiers have a leading `@'. And that's OK - one
doesn't *have* to use kerneldoc. But a lot of
drivers/rapidio/rio-scan.c is already using kerneldoc so the
inconsistency is odd.
>
> ...
>
> -static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port)
> +static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port,
> + int do_enum, u16 start)
> {
> struct rio_net *net;
>
> net = kzalloc(sizeof(struct rio_net), GFP_KERNEL);
> + if (net && do_enum) {
> + net->destid_table.table = kzalloc(
> + BITS_TO_LONGS(RIO_MAX_ROUTE_ENTRIES(port->sys_size)) *
> + sizeof(long),
> + GFP_KERNEL);
kcalloc() would be idiomatic here.
> + if (net->destid_table.table == NULL) {
> + pr_err("RIO: failed to allocate destID table\n");
> + kfree(net);
> + net = NULL;
> + } else {
> + net->destid_table.start = start;
> + net->destid_table.next = 0;
> + net->destid_table.max =
> + RIO_MAX_ROUTE_ENTRIES(port->sys_size);
> + spin_lock_init(&net->destid_table.lock);
> + }
> + }
> +
>
> ...
>
^ permalink raw reply
* Re: [REGRESSION] nfsd crashing with 3.6.0-rc7 on PowerPC
From: Anton Blanchard @ 2012-10-04 0:26 UTC (permalink / raw)
To: Alexander Graf
Cc: linux-nfs, Jan Kara, Nishanth Aravamudan, linuxppc-dev, LKML List,
J. Bruce Fields, skinsbursky, bfields, Linus Torvalds
In-Reply-To: <E141CA01-F58C-47B1-8ED5-A314D1DEC968@suse.de>
Hi,
> Yes. With that patch applied, things work for me again.
Thanks Alex, Nish. We can reproduce this on one of our Biminis, looking
into it now.
Anton
^ permalink raw reply
* [PATCH v1 0/3] cpuidle: (pSeries) pseries idle driver fixes.
From: Deepthi Dharwar @ 2012-10-04 4:42 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel, linuxppc-dev
The following series consists of a bunch of fixes for
cpuidle back-end driver for ppc64 (pSeries) platform.
The series applies on 3.6-rc7 and has been tested on ppc64 pSeries
POWER7 system with snooze and nap states.
With these fixes, avg idle residency for nap on 50 percent idle system
increased from 96 to 99.4 percent and idle residency for snooze for
the same decreased from 4 to 0.6 percent, determined running spec power
benchmark runs.
Deepthi Dharwar (3):
cpuidle: (POWER) Fix snooze state problem persistant in the current cpuidle design on pseries.
cpuidle: (POWER) Fix smt_snooze_delay functionality.
cpuidle: (POWER) Fix target residency initialisation in pseries cpuidle
arch/powerpc/include/asm/processor.h | 4 +
arch/powerpc/kernel/sysfs.c | 2 -
arch/powerpc/platforms/pseries/processor_idle.c | 62 +++++++++++------------
3 files changed, 32 insertions(+), 36 deletions(-)
-- Deepthi
^ permalink raw reply
* [PATCH v1 1/3] cpuidle: (POWER) Fix target residency initialisation in pseries cpuidle
From: Deepthi Dharwar @ 2012-10-04 4:42 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel, linuxppc-dev
In-Reply-To: <20121004044154.15921.51256.stgit@deepthi.in.ibm.com>
Remove the redundant target residency initialisation in pseries_cpuidle_driver_init().
This is currently over-writing the residency time updated as part of the static
table, resulting in all the idle states having the same target
residency of 100us which is incorrect. This may result in the menu governor making
wrong state decisions.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/processor_idle.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index 455760b..02e425a 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -246,10 +246,6 @@ static int pseries_cpuidle_driver_init(void)
drv->states[drv->state_count] = /* structure copy */
cpuidle_state_table[idle_state];
- if (cpuidle_state_table == dedicated_states)
- drv->states[drv->state_count].target_residency =
- __get_cpu_var(smt_snooze_delay);
-
drv->state_count += 1;
}
^ permalink raw reply related
* [PATCH v1 2/3] cpuidle: (POWER) Fix smt_snooze_delay functionality.
From: Deepthi Dharwar @ 2012-10-04 4:42 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel, linuxppc-dev
In-Reply-To: <20121004044154.15921.51256.stgit@deepthi.in.ibm.com>
smt_snooze_delay was designed to delay idle loop's nap entry
in the native idle code before it got ported over to use as part of
the cpuidle framework.
A -ve value assigned to smt_snooze_delay should result in
busy looping, in other words disabling the entry to nap state.
- https://lists.ozlabs.org/pipermail/linuxppc-dev/2010-May/082450.html
This particular functionality can be achieved currently by
echo 1 > /sys/devices/system/cpu/cpu*/state1/disable
but it is broken when one assigns -ve value to the smt_snooze_delay
variable either via sysfs entry or ppc64_cpu util.
This patch aims to fix this, by disabling nap state when smt_snooze_delay
variable is set to -ve value.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/processor.h | 4 ++--
arch/powerpc/kernel/sysfs.c | 2 +-
arch/powerpc/platforms/pseries/processor_idle.c | 24 ++++++++++++++++-------
3 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 54b73a2..d0a5fb8 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -389,9 +389,9 @@ extern int powersave_nap; /* set if nap mode can be used in idle loop */
extern void power7_nap(void);
#ifdef CONFIG_PSERIES_IDLE
-extern void update_smt_snooze_delay(int snooze);
+extern void update_smt_snooze_delay(int cpu, int residency);
#else
-static inline void update_smt_snooze_delay(int snooze) {}
+static inline void update_smt_snooze_delay(int cpu, int residency) {}
#endif
extern void flush_instruction_cache(void);
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index 8302af6..cf357a0 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -50,7 +50,7 @@ static ssize_t store_smt_snooze_delay(struct device *dev,
return -EINVAL;
per_cpu(smt_snooze_delay, cpu->dev.id) = snooze;
- update_smt_snooze_delay(snooze);
+ update_smt_snooze_delay(cpu->dev.id, snooze);
return count;
}
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index 02e425a..a32d56d 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -33,13 +33,6 @@ static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
static struct cpuidle_device __percpu *pseries_cpuidle_devices;
static struct cpuidle_state *cpuidle_state_table;
-void update_smt_snooze_delay(int snooze)
-{
- struct cpuidle_driver *drv = cpuidle_get_driver();
- if (drv)
- drv->states[0].target_residency = snooze;
-}
-
static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before)
{
@@ -190,6 +183,23 @@ static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
.enter = &shared_cede_loop },
};
+void update_smt_snooze_delay(int cpu, int residency)
+{
+ struct cpuidle_driver *drv = cpuidle_get_driver();
+ struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
+
+ if (cpuidle_state_table != dedicated_states)
+ return;
+
+ if (residency < 0) {
+ /* Disable the Nap state on that cpu */
+ if (dev)
+ dev->states_usage[1].disable = 1;
+ } else
+ if (drv)
+ drv->states[0].target_residency = residency;
+}
+
static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
unsigned long action, void *hcpu)
{
^ permalink raw reply related
* [PATCH v1 3/3] cpuidle: (POWER) Fix snooze state problem persistant in the current cpuidle design on pseries.
From: Deepthi Dharwar @ 2012-10-04 4:42 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel, linuxppc-dev
In-Reply-To: <20121004044154.15921.51256.stgit@deepthi.in.ibm.com>
Earlier without cpuidle framework on pseries, the native arch
idle routine comprised of both snooze and nap
states. smt_snooze_delay variable was used to delay
the idle process entry to deeper idle state like nap.
With the coming of cpuidle, this arch specific idle was replaced
by two different idle routines, one for supporting snooze and other
for nap. This enabled addition of more
low level idle states on pseries in the future.
On adopting the generic cpuidle framework for POWER systems,
the decision of which idle state to choose from, given a predicted
idle time is taken by the menu governor based on
target_residency and exit_latency of the idle states.
target_residency is the minimum time to be resident in that idle state.
Exit_latency is time taken to exit out of idle state.
Deeper the idle state, both the target residency and exit latency
would be higher.
In the current design, smt_snooze_delay is used as target_residency
for the snooze state which is incorrect, as it is not the
minimum but the maximum duration to be in snooze state.
This would result in the governor in taking bad decision,
as presently target_residency of nap < target_residency of snooze
inspite of nap being deeper idle state.
This patch aims to fix this problem by replacing the smt_snooze_delay loop
in snooze state, with the need_resched() as the governor is aware of
entry and exit of various idle transitions based on which
next idle time prediction.
The governor is intelligent enough to determine the idle state the needs to
be transitioned to and maintains a whole of heuristics including
io load, previous idle states predictions etc for the same, based on
which idle state entry decision is taken.
With this fix, of setting target_residency of snooze to 0
nap to smt_snooze_delay
if the predicted idle time is less
than smt_snooze_delay (target_residency of nap)
value governor would pick snooze state, else nap. This adhers to the
previous native idle design.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/processor_idle.c | 36 ++++++++---------------
1 file changed, 13 insertions(+), 23 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index a32d56d..45d00e5 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -59,32 +59,22 @@ static int snooze_loop(struct cpuidle_device *dev,
{
unsigned long in_purr;
ktime_t kt_before;
- unsigned long start_snooze;
- long snooze = drv->states[0].target_residency;
+ int cpu = dev->cpu;
idle_loop_prolog(&in_purr, &kt_before);
+ local_irq_enable();
+ set_thread_flag(TIF_POLLING_NRFLAG);
- if (snooze) {
- start_snooze = get_tb() + snooze * tb_ticks_per_usec;
- local_irq_enable();
- set_thread_flag(TIF_POLLING_NRFLAG);
-
- while ((snooze < 0) || (get_tb() < start_snooze)) {
- if (need_resched() || cpu_is_offline(dev->cpu))
- goto out;
- ppc64_runlatch_off();
- HMT_low();
- HMT_very_low();
- }
-
- HMT_medium();
- clear_thread_flag(TIF_POLLING_NRFLAG);
- smp_mb();
- local_irq_disable();
+ while ((!need_resched()) && cpu_online(cpu)) {
+ ppc64_runlatch_off();
+ HMT_low();
+ HMT_very_low();
}
-out:
HMT_medium();
+ clear_thread_flag(TIF_POLLING_NRFLAG);
+ smp_mb();
+
dev->last_residency =
(int)idle_loop_epilog(in_purr, kt_before);
return index;
@@ -165,8 +155,8 @@ static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
.name = "CEDE",
.desc = "CEDE",
.flags = CPUIDLE_FLAG_TIME_VALID,
- .exit_latency = 1,
- .target_residency = 10,
+ .exit_latency = 10,
+ .target_residency = 100,
.enter = &dedicated_cede_loop },
};
@@ -197,7 +187,7 @@ void update_smt_snooze_delay(int cpu, int residency)
dev->states_usage[1].disable = 1;
} else
if (drv)
- drv->states[0].target_residency = residency;
+ drv->states[1].target_residency = residency;
}
static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
^ permalink raw reply related
* [PATCH] powerpc/iommu: Fix multiple issues with IOMMU pools code
From: Anton Blanchard @ 2012-10-04 4:57 UTC (permalink / raw)
To: Alexander Graf
Cc: linux-nfs, Jan Kara, Nishanth Aravamudan, linuxppc-dev, LKML List,
J. Bruce Fields, skinsbursky, bfields, Linus Torvalds
In-Reply-To: <E141CA01-F58C-47B1-8ED5-A314D1DEC968@suse.de>
Hi Alex,
Looks to be a preempt issue with the iommu pools code. I did find a
couple more bugs along the way too.
Anton
--
There are a number of issues in the recent IOMMU pools code:
- On a preempt kernel we might switch CPUs in the middle of building
a scatter gather list. When this happens the handle hint passed in
no longer falls within the local CPU's pool. Check for this and
fall back to the pool hint.
- We were missing a spin_unlock/spin_lock in one spot where we
switch pools.
- We need to provide locking around dart_tlb_invalidate_all and
dart_tlb_invalidate_one now that the global lock is gone.
Reported-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Anton Blanchard <anton@samba.org>
---
There is still an issue with the lazy u3 flushing, but I wanted
to get this out for testing.
Index: b/arch/powerpc/kernel/iommu.c
===================================================================
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -215,7 +215,8 @@ static unsigned long iommu_range_alloc(s
spin_lock_irqsave(&(pool->lock), flags);
again:
- if ((pass == 0) && handle && *handle)
+ if ((pass == 0) && handle && *handle &&
+ (*handle >= pool->start) && (*handle < pool->end))
start = *handle;
else
start = pool->hint;
@@ -236,7 +237,9 @@ again:
* but on second pass, start at 0 in pool 0.
*/
if ((start & mask) >= limit || pass > 0) {
+ spin_unlock(&(pool->lock));
pool = &(tbl->pools[0]);
+ spin_lock(&(pool->lock));
start = pool->start;
} else {
start &= mask;
Index: b/arch/powerpc/sysdev/dart_iommu.c
===================================================================
--- a/arch/powerpc/sysdev/dart_iommu.c
+++ b/arch/powerpc/sysdev/dart_iommu.c
@@ -74,11 +74,16 @@ static int dart_is_u4;
#define DBG(...)
+static DEFINE_SPINLOCK(invalidate_lock);
+
static inline void dart_tlb_invalidate_all(void)
{
unsigned long l = 0;
unsigned int reg, inv_bit;
unsigned long limit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&invalidate_lock, flags);
DBG("dart: flush\n");
@@ -111,12 +116,17 @@ retry:
panic("DART: TLB did not flush after waiting a long "
"time. Buggy U3 ?");
}
+
+ spin_unlock_irqrestore(&invalidate_lock, flags);
}
static inline void dart_tlb_invalidate_one(unsigned long bus_rpn)
{
unsigned int reg;
unsigned int l, limit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&invalidate_lock, flags);
reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE |
(bus_rpn & DART_CNTL_U4_IONE_MASK);
@@ -138,6 +148,8 @@ wait_more:
panic("DART: TLB did not flush after waiting a long "
"time. Buggy U4 ?");
}
+
+ spin_unlock_irqrestore(&invalidate_lock, flags);
}
static void dart_flush(struct iommu_table *tbl)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox