* Re: [PATCH] powerpc/windfarm: fix overtemperature clearing
From: Benjamin Herrenschmidt @ 2013-07-01 1:52 UTC (permalink / raw)
To: Aaro Koskinen; +Cc: linuxppc-dev
In-Reply-To: <1372618842-27429-1-git-send-email-aaro.koskinen@iki.fi>
On Sun, 2013-06-30 at 22:00 +0300, Aaro Koskinen wrote:
> With pm81/pm91/pm121, when the overtemperature state is entered, and
> when it remains on after skipped ticks, the driver will try to leave
> it too soon (immediately on the next tick). This is because the active
> FAILURE_OVERTEMP state is not visible in "new_failure" variable of the
> current tick. Furthermore, the driver will keep trying to clear condition
> in subsequent ticks as FAILURE_OVERTEMP remains set in the "last_failure"
> variable. These will start to trigger WARNINGS from windfarm core:
Thanks, looks good. Applied.
Cheers,
Ben.
> [ 100.082735] windfarm: Clamping CPU frequency to minimum !
> [ 100.108132] windfarm: Overtemp condition detected !
> [ 101.952908] windfarm: Overtemp condition cleared !
> [...]
> [ 102.980388] WARNING: at drivers/macintosh/windfarm_core.c:463
> [...]
> [ 103.982227] WARNING: at drivers/macintosh/windfarm_core.c:463
> [...]
> [ 105.030494] WARNING: at drivers/macintosh/windfarm_core.c:463
> [...]
> [ 105.973666] WARNING: at drivers/macintosh/windfarm_core.c:463
> [...]
> [ 106.977913] WARNING: at drivers/macintosh/windfarm_core.c:463
>
> Fix by adding a helper global variable. We leave the overtemp state only
> after all failure bits have been cleared.
>
> I saw this error on iMac G5 iSight (pm121). Also pm81/pm91 are fixed
> based on the observation that these are almost identical/copy-pasted code.
>
> Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
> ---
> drivers/macintosh/windfarm_pm121.c | 6 +++++-
> drivers/macintosh/windfarm_pm81.c | 6 +++++-
> drivers/macintosh/windfarm_pm91.c | 6 +++++-
> 3 files changed, 15 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
> index af605e9..7fe58b0 100644
> --- a/drivers/macintosh/windfarm_pm121.c
> +++ b/drivers/macintosh/windfarm_pm121.c
> @@ -276,6 +276,7 @@ static const char *loop_names[N_LOOPS] = {
>
> static unsigned int pm121_failure_state;
> static int pm121_readjust, pm121_skipping;
> +static bool pm121_overtemp;
> static s32 average_power;
>
> struct pm121_correction {
> @@ -847,6 +848,7 @@ static void pm121_tick(void)
> if (new_failure & FAILURE_OVERTEMP) {
> wf_set_overtemp();
> pm121_skipping = 2;
> + pm121_overtemp = true;
> }
>
> /* We only clear the overtemp condition if overtemp is cleared
> @@ -855,8 +857,10 @@ static void pm121_tick(void)
> * the control loop levels, but we don't want to keep it clear
> * here in this case
> */
> - if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
> + if (!pm121_failure_state && pm121_overtemp) {
> wf_clear_overtemp();
> + pm121_overtemp = false;
> + }
> }
>
>
> diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c
> index f84933f..2a5e1b1 100644
> --- a/drivers/macintosh/windfarm_pm81.c
> +++ b/drivers/macintosh/windfarm_pm81.c
> @@ -149,6 +149,7 @@ static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started;
>
> static unsigned int wf_smu_failure_state;
> static int wf_smu_readjust, wf_smu_skipping;
> +static bool wf_smu_overtemp;
>
> /*
> * ****** System Fans Control Loop ******
> @@ -593,6 +594,7 @@ static void wf_smu_tick(void)
> if (new_failure & FAILURE_OVERTEMP) {
> wf_set_overtemp();
> wf_smu_skipping = 2;
> + wf_smu_overtemp = true;
> }
>
> /* We only clear the overtemp condition if overtemp is cleared
> @@ -601,8 +603,10 @@ static void wf_smu_tick(void)
> * the control loop levels, but we don't want to keep it clear
> * here in this case
> */
> - if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
> + if (!wf_smu_failure_state && wf_smu_overtemp) {
> wf_clear_overtemp();
> + wf_smu_overtemp = false;
> + }
> }
>
> static void wf_smu_new_control(struct wf_control *ct)
> diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c
> index 2eb484f..a8ac66c 100644
> --- a/drivers/macintosh/windfarm_pm91.c
> +++ b/drivers/macintosh/windfarm_pm91.c
> @@ -76,6 +76,7 @@ static struct wf_control *cpufreq_clamp;
>
> /* Set to kick the control loop into life */
> static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started;
> +static bool wf_smu_overtemp;
>
> /* Failure handling.. could be nicer */
> #define FAILURE_FAN 0x01
> @@ -517,6 +518,7 @@ static void wf_smu_tick(void)
> if (new_failure & FAILURE_OVERTEMP) {
> wf_set_overtemp();
> wf_smu_skipping = 2;
> + wf_smu_overtemp = true;
> }
>
> /* We only clear the overtemp condition if overtemp is cleared
> @@ -525,8 +527,10 @@ static void wf_smu_tick(void)
> * the control loop levels, but we don't want to keep it clear
> * here in this case
> */
> - if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
> + if (!wf_smu_failure_state && wf_smu_overtemp) {
> wf_clear_overtemp();
> + wf_smu_overtemp = false;
> + }
> }
>
>
^ permalink raw reply
* [PATCH] powerpc/windfarm: fix overtemperature clearing
From: Aaro Koskinen @ 2013-06-30 19:00 UTC (permalink / raw)
To: Benjamin Herrenschmidt, linuxppc-dev; +Cc: Aaro Koskinen
With pm81/pm91/pm121, when the overtemperature state is entered, and
when it remains on after skipped ticks, the driver will try to leave
it too soon (immediately on the next tick). This is because the active
FAILURE_OVERTEMP state is not visible in "new_failure" variable of the
current tick. Furthermore, the driver will keep trying to clear condition
in subsequent ticks as FAILURE_OVERTEMP remains set in the "last_failure"
variable. These will start to trigger WARNINGS from windfarm core:
[ 100.082735] windfarm: Clamping CPU frequency to minimum !
[ 100.108132] windfarm: Overtemp condition detected !
[ 101.952908] windfarm: Overtemp condition cleared !
[...]
[ 102.980388] WARNING: at drivers/macintosh/windfarm_core.c:463
[...]
[ 103.982227] WARNING: at drivers/macintosh/windfarm_core.c:463
[...]
[ 105.030494] WARNING: at drivers/macintosh/windfarm_core.c:463
[...]
[ 105.973666] WARNING: at drivers/macintosh/windfarm_core.c:463
[...]
[ 106.977913] WARNING: at drivers/macintosh/windfarm_core.c:463
Fix by adding a helper global variable. We leave the overtemp state only
after all failure bits have been cleared.
I saw this error on iMac G5 iSight (pm121). Also pm81/pm91 are fixed
based on the observation that these are almost identical/copy-pasted code.
Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
---
drivers/macintosh/windfarm_pm121.c | 6 +++++-
drivers/macintosh/windfarm_pm81.c | 6 +++++-
drivers/macintosh/windfarm_pm91.c | 6 +++++-
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
index af605e9..7fe58b0 100644
--- a/drivers/macintosh/windfarm_pm121.c
+++ b/drivers/macintosh/windfarm_pm121.c
@@ -276,6 +276,7 @@ static const char *loop_names[N_LOOPS] = {
static unsigned int pm121_failure_state;
static int pm121_readjust, pm121_skipping;
+static bool pm121_overtemp;
static s32 average_power;
struct pm121_correction {
@@ -847,6 +848,7 @@ static void pm121_tick(void)
if (new_failure & FAILURE_OVERTEMP) {
wf_set_overtemp();
pm121_skipping = 2;
+ pm121_overtemp = true;
}
/* We only clear the overtemp condition if overtemp is cleared
@@ -855,8 +857,10 @@ static void pm121_tick(void)
* the control loop levels, but we don't want to keep it clear
* here in this case
*/
- if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+ if (!pm121_failure_state && pm121_overtemp) {
wf_clear_overtemp();
+ pm121_overtemp = false;
+ }
}
diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c
index f84933f..2a5e1b1 100644
--- a/drivers/macintosh/windfarm_pm81.c
+++ b/drivers/macintosh/windfarm_pm81.c
@@ -149,6 +149,7 @@ static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started;
static unsigned int wf_smu_failure_state;
static int wf_smu_readjust, wf_smu_skipping;
+static bool wf_smu_overtemp;
/*
* ****** System Fans Control Loop ******
@@ -593,6 +594,7 @@ static void wf_smu_tick(void)
if (new_failure & FAILURE_OVERTEMP) {
wf_set_overtemp();
wf_smu_skipping = 2;
+ wf_smu_overtemp = true;
}
/* We only clear the overtemp condition if overtemp is cleared
@@ -601,8 +603,10 @@ static void wf_smu_tick(void)
* the control loop levels, but we don't want to keep it clear
* here in this case
*/
- if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+ if (!wf_smu_failure_state && wf_smu_overtemp) {
wf_clear_overtemp();
+ wf_smu_overtemp = false;
+ }
}
static void wf_smu_new_control(struct wf_control *ct)
diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c
index 2eb484f..a8ac66c 100644
--- a/drivers/macintosh/windfarm_pm91.c
+++ b/drivers/macintosh/windfarm_pm91.c
@@ -76,6 +76,7 @@ static struct wf_control *cpufreq_clamp;
/* Set to kick the control loop into life */
static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started;
+static bool wf_smu_overtemp;
/* Failure handling.. could be nicer */
#define FAILURE_FAN 0x01
@@ -517,6 +518,7 @@ static void wf_smu_tick(void)
if (new_failure & FAILURE_OVERTEMP) {
wf_set_overtemp();
wf_smu_skipping = 2;
+ wf_smu_overtemp = true;
}
/* We only clear the overtemp condition if overtemp is cleared
@@ -525,8 +527,10 @@ static void wf_smu_tick(void)
* the control loop levels, but we don't want to keep it clear
* here in this case
*/
- if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+ if (!wf_smu_failure_state && wf_smu_overtemp) {
wf_clear_overtemp();
+ wf_smu_overtemp = false;
+ }
}
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH] powerpc/mpc512x: update defconfig
From: Gerhard Sittig @ 2013-06-30 16:23 UTC (permalink / raw)
To: Anatolij Gustschin; +Cc: linuxppc-dev
In-Reply-To: <1372142237-20554-1-git-send-email-agust@denx.de>
On Tue, Jun 25, 2013 at 08:37 +0200, Anatolij Gustschin wrote:
>
> From: Gerhard Sittig <gsi@denx.de>
>
> This patch does not change the content, it merely re-orders
> configuration items and drops explicit options which already
> apply as the default.
>
> Signed-off-by: Gerhard Sittig <gsi@denx.de>
> Signed-off-by: Anatolij Gustschin <agust@denx.de>
In hindsight, I'd prefer to reword the subject to something more
specific, unless it's too late for this now. I suggest:
[PATCH] powerpc/mpc512x: commit re-generated defconfig
But if the patch is already queued somewhere, or is acceptable
with the more generic subject, that's fine with me as well.
virtually yours
Gerhard Sittig
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de
^ permalink raw reply
* [PATCH] mac: Make cuda_init_via() __init
From: Geert Uytterhoeven @ 2013-06-30 10:03 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: Geert Uytterhoeven, linux-m68k, linuxppc-dev, linux-kernel
cuda_init_via() is called from find_via_cuda() only, which is __init.
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
drivers/macintosh/via-cuda.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
index 86511c5..d61f271 100644
--- a/drivers/macintosh/via-cuda.c
+++ b/drivers/macintosh/via-cuda.c
@@ -259,7 +259,7 @@ cuda_probe(void)
} while (0)
static int
-cuda_init_via(void)
+__init cuda_init_via(void)
{
out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ); /* TACK & TIP out */
out_8(&via[B], in_8(&via[B]) | TACK | TIP); /* negate them */
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH 2/2] powerpc/fsl_booke: enable the relocatable for the kdump kernel
From: Kevin Hao @ 2013-06-30 7:35 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc
In-Reply-To: <1372385946.8183.64@snotra>
[-- Attachment #1: Type: text/plain, Size: 3879 bytes --]
On Thu, Jun 27, 2013 at 09:19:06PM -0500, Scott Wood wrote:
> On 06/26/2013 09:00:34 PM, Kevin Hao wrote:
> >diff --git a/arch/powerpc/include/asm/mmu-book3e.h
> >b/arch/powerpc/include/asm/mmu-book3e.h
> >index 936db36..bf422db 100644
> >--- a/arch/powerpc/include/asm/mmu-book3e.h
> >+++ b/arch/powerpc/include/asm/mmu-book3e.h
> >@@ -214,6 +214,11 @@
> > #define TLBILX_T_CLASS2 6
> > #define TLBILX_T_CLASS3 7
> >
> >+#ifdef CONFIG_PPC32
> >+/* The max size that one tlb can map in a 32bit kernel. */
> >+#define PPC_PIN_SIZE (1 << 28) /* 256M */
> >+#endif
>
> That comment is not true for all chips.
This is not for the hardware limitation.
>
> >@@ -177,11 +178,34 @@ unsigned long map_mem_in_cams(unsigned long
> >ram, int max_cam_idx)
> > unsigned long virt = PAGE_OFFSET;
> > phys_addr_t phys = memstart_addr;
> > unsigned long amount_mapped = 0;
> >-
> >+ unsigned long cam_sz;
> >+
> >+#if defined(CONFIG_RELOCATABLE) && defined(CONFIG_PPC32)
> >+ /*
> >+ * For a relocatable kernel, we would not map from
> >memstart_addr.
> >+ * We first align to PPC_PIN_SIZE (256M), then map the
> >PAGE_OFFSET
> >+ * from there.
> >+ */
> >+ phys &= ~(PPC_PIN_SIZE - 1);
> >+ ram += memstart_addr & (PPC_PIN_SIZE - 1);
>
> You should not map anything before memstart_addr. If memstart_addr
> isn't 256M-aligned, you'll have to either use smaller pages or
> consider that region to be "high"mem (assuming Linux supports
> highmem existing below lowmem -- I'm skeptical).
OK, I will try to find a another way to resolve this issue.
>
> >+ /*
> >+ * For a kdump kernel, we may use a memory area reserved by the
> >boot
> >+ * kernel by using a kernel option like this
> >'crashkernel=32M@64M'.
> >+ * In this case, the ram is 96M. The kernel will try to map the
> >first
> >+ * 64M in the first tlb entry. The kernel will definitely get
> >stuck,
> >+ * since the kernel is running above the 64M. So we have to make
> >sure
> >+ * that the first tlb cover the current kernel running address
> >at least.
> >+ */
>
> Maybe we should be running from AS1 when we set this up, to avoid
> problems replacing an entry while it's in use?
I thought about this. The reason that I don't use this method is
we also have to do another relocation if we just want to map
the reserved memory for the kernel.
>
> Pardon my ignorance about how kdump/kexec works, but I'm a bit
> confused by exactly what the situation is with crashkernel. How do
> we know that we are the crash kernel, and that we should limit our
> RAM usage to that area?
The kexec tool will parse the command line of the boot kernel and get
the reserved memory info (such as start address, size) and then pass
these informations to the kdump kernel via device tree.
> I'm wondering if this code is assuming that
> the crashkernel area is from where the kernel starts to the end of
> RAM.
No. We get these information from device tree.
>
> >+ while (1) {
> >+ cam_sz = calc_cam_sz(ram, virt, phys);
> >+ if (cam_sz + phys > PHYSICAL_START + _end - _stext)
> >+ break;
> >+ ram = 1 << (ilog2(ram) + 1);
> >+ }
>
> The ram that was passed in is as much as you have. Don't map more.
>
> What happens if (e.g.) memstart_addr is 512M, with a size of 512M,
> and the kernel starts at 768M? Increasing the size will never get
> you a mapping that covers kernstart, because calc_cam_sz will never
> return more than 256M.
Yes, the current code still can't handle this case. We always assume
that the kernel is in the memory region which can be covered by the
first tlb entry.
>
> When does memory below the rounded-down kernel start get mapped?
This only get mapped via __ioremap when we need to read from it such as
creating the vmcore image.
Thanks,
Kevin
>
> -Scott
[-- Attachment #2: Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply
* Re: [PATCH 1/2] powerpc: enable the relocatable support for the fsl booke 32bit kernel
From: Kevin Hao @ 2013-06-30 7:34 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc
In-Reply-To: <1372384340.8183.62@snotra>
[-- Attachment #1: Type: text/plain, Size: 1468 bytes --]
On Thu, Jun 27, 2013 at 08:52:20PM -0500, Scott Wood wrote:
> On 06/27/2013 08:36:37 PM, Kevin Hao wrote:
> >On Thu, Jun 27, 2013 at 02:58:34PM -0500, Scott Wood wrote:
> >> On 06/26/2013 09:00:33 PM, Kevin Hao wrote:
> >> >This is based on the codes in the head_44x.S. Since we always
> >align to
> >> >256M before mapping the PAGE_OFFSET for a relocatable kernel,
> >we also
> >> >change the init tlb map to 256M size.
> >>
> >> Why 256M?
> >
> >For two reasons:
> > 1. This is the size which both e500v1 and e500v2 support.
> > 2. Since we always use the PAGE_OFFSET as 0xc0000000, the 256M is
> > max alignment value we can use for this virtual address.
>
> 0xc0000000 is 1G-aligned, so I don't see why 256M is the maximum
> (after verifying that enough memory is present with the right
> alignment, of course). The TLB1 savings would probably not be
> enough to justify figuring that out, though.
Sorry, I didn't make myself clear. Yes, the 0xc0000000 is 1G-aligned.
For a 32bit kernel we only have a 1G memory region for the kernel space,
but we can't use all of it for direct map. So we always set the
__max_low_memory to 0x30000000. And for e500 core, it doesn't support
512M page size. So the 256M is the max page size we can use for a
32bit kernel. And since we always cover the boot code in the first
tlb entry, we then use the 256M as the alignment value for the
relocatable kernel.
Thanks,
Kevin
>
> -Scott
[-- Attachment #2: Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply
* Re: [PATCH 1/2] powerpc: enable the relocatable support for the fsl booke 32bit kernel
From: Kevin Hao @ 2013-06-30 7:33 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc
In-Reply-To: <1372384047.8183.61@snotra>
[-- Attachment #1: Type: text/plain, Size: 2928 bytes --]
On Thu, Jun 27, 2013 at 08:47:27PM -0500, Scott Wood wrote:
> On 06/27/2013 08:36:37 PM, Kevin Hao wrote:
> >On Thu, Jun 27, 2013 at 02:58:34PM -0500, Scott Wood wrote:
> >> On 06/26/2013 09:00:33 PM, Kevin Hao wrote:
> >> >This is based on the codes in the head_44x.S. Since we always
> >align to
> >> >256M before mapping the PAGE_OFFSET for a relocatable kernel,
> >we also
> >> >change the init tlb map to 256M size.
> >>
> >> Why 256M?
> >
> >For two reasons:
> > 1. This is the size which both e500v1 and e500v2 support.
> > 2. Since we always use the PAGE_OFFSET as 0xc0000000, the 256M is
> > max alignment value we can use for this virtual address.
>
> Is there any reason why 64M won't continue to work here?
Yes. In general we would map the 0 ~ 256M memory region in the first
tlb1 entry. If we align to 64M, the relocatable kernel would not work
if loaded above 64M memory. For example, if we load a relocatable kernel
at 64M memory, we will relocate it as:
__pa(PAGE_OFFSET) = 0x4000000
But in map_mem_in_cams function, it will create a memory map as:
__pa(PAGE_OFFSET) = 0x0
The kernel will definitely not work in this case.
>
> >> This tightens the alignment requirement for dynamic memstart.
> >
> >Yes. But since RELOCATABLE is a superset of DYNAMIC_MEMSTART, we
> >can always
> >use RELOCATABLE instead of DYNAMIC_MEMSTART for fsl booke board in
> >any cases.
>
> The extra flexibility of RELOCATABLE may help some use cases, but
> you'd still require the entire 256M naturally aligned region
> containing the kernel to be present and owned by this instance of
> Linux.
>
> >So DYNAMIC_MEMSTART will seem not so useful after we enable this
> >feature.
>
> Then why doesn't this patch remove it?
According to the Kconfig it is still used by 44x. And maybe someone
still want to use this relocation method.
>
> >> And
> >> what about boards with less than 256 MiB of RAM?
> >
> >It should be fine. We just create the map in the tlb. The MM still use
> >the real size of memory.
>
> No, you must not map anything that is not present with a mapping
> that is executable and/or not guarded, or you could get speculative
> accesses to who-knows-what.
Yes, there may be speculative access in this case.
> Even if RAM is present there but owned
> by some other entity, you could be creating illegal aliases if that
> other entity mapped it cache-inhibited or similar.
Fair enough. So it seems error prone if we map this 256M memory region
blindly. But if we don't do this, it seems that we have to do twice relocation.
The first time we just align to a predefined value (64M for example), and
then parse the device tree and get the real memstart_addr. After that we
should relocate the kernel to the real start address. It seems a little
complicated. Do you have any better ideas?
Thanks,
Kevin
>
> -Scott
[-- Attachment #2: Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply
* Re: [PATCH 8/8] powernv/eeh: Do hotplug on devices without EEH aware driver
From: Benjamin Herrenschmidt @ 2013-06-30 6:25 UTC (permalink / raw)
To: Gavin Shan; +Cc: linuxppc-dev
In-Reply-To: <1372312009-13710-9-git-send-email-shangw@linux.vnet.ibm.com>
On Thu, 2013-06-27 at 13:46 +0800, Gavin Shan wrote:
> During recovery for EEH errors, the device driver requires reset
> explicitly (most of cases). The EEH core doesn't do hotplug during
> reset. However, there might have some device drivers that can't
> support EEH. So the deivce can't be put into quite state during
> the reset and possibly requesting PCI config or MMIO access. That
> would lead to the failure of the reset, and we don't expect that.
>
> The patch intends to fix that by removing those devices whose drivers
> can't support EEH before reset and added into the system after reset.
> In the result, it would avoid the race condition mentioned as above.
> The idea was proposed by Ben.
Ok so, while the basic idea is sane, I have some problems with the
implementation.
First, you add a lot of code conditional to eeh_probe_mode_dev() and
I don't see why. There is no reason not to do the exact same remove/add
operations under pHyp.
This is actually a problem I have with the "new" EEH code overall, there
are too much gratuitous differences between the two "modes" and it's
very tricky to figure out exactly what and why, meaning this is going to
be a source of bugs. I think it's worthwhile trying to reconcile some of
that.
But then, there is *already* code to do a similar unplug/replug in EEH,
except that it's done inside eeh_reset_device() in the "other" case
(and indeed not necessarily in the best way).
Let's look a bit more:
> +extern void pcibios_setup_device(struct pci_dev *dev);
Unrelated: The above is gone as an exported symbol from upstream
as of today. I'll merge upstream in before applying your patches
so don't add that. You shouldn't need anyway, see below.
> extern void pcibios_setup_bus_devices(struct pci_bus *bus);
> extern void pcibios_setup_bus_self(struct pci_bus *bus);
> extern void pcibios_setup_phb_io_space(struct pci_controller *hose);
> diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
> index 2b1ce17..cb3baab 100644
> --- a/arch/powerpc/kernel/eeh_driver.c
> +++ b/arch/powerpc/kernel/eeh_driver.c
> @@ -244,6 +244,7 @@ static void *eeh_report_reset(void *data, void *userdata)
> struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> enum pci_ers_result rc, *res = userdata;
> struct pci_driver *driver;
> + bool enable_irq = true;
>
> if (!dev) return NULL;
> dev->error_state = pci_channel_io_normal;
> @@ -251,7 +252,21 @@ static void *eeh_report_reset(void *data, void *userdata)
> driver = eeh_pcid_get(dev);
> if (!driver) return NULL;
>
> - eeh_enable_irq(dev);
So I would very much like to understand a bit more that irq business,
for example, what does it do when MSIs are enabled, etc... but that
is also probably something to look at separately (you have a TODO
list ?)
> + /*
> + * For those PCI devices just added, we reloaded its driver
> + * and needn't to enable the interrupt. The driver should
> + * take care of that. Otherwise, complaint raised from IRQ
> + * subsystem.
> + */
> + if (eeh_probe_mode_dev() && (edev->mode & EEH_DEV_REMOVED)) {
> + edev->mode &= ~(EEH_DEV_REMOVED | EEH_DEV_IRQ_DISABLED);
> + edev->bus = NULL;
> + enable_irq = false;
> +
> + }
Ok, yet another eeh_probe_mode_dev() ... they irk me :-) There should be
no functional differences between EEH operations on powernv and pseries
at a high level and this is high level...
> + if (enable_irq)
> + eeh_enable_irq(dev);
>
> if (!driver->err_handler ||
> !driver->err_handler->slot_reset) {
> @@ -338,6 +353,115 @@ static void *eeh_report_failure(void *data, void *userdata)
> return NULL;
> }
>
> +static void *eeh_rmv_device(void *data, void *userdata)
> +{
> + struct pci_driver *driver;
> + struct eeh_dev *edev = (struct eeh_dev *)data;
> + struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> + int *removed = (int *)userdata;
> +
> + /*
> + * Actually, we should remove the PCI bridges as well.
> + * However, that's lots of complexity to do that,
> + * particularly some of devices under the bridge might
> + * support EEH. So we just care about PCI devices for
> + * simplicity here.
> + */
> + if (!dev || (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE))
> + return NULL;
> + driver = eeh_pcid_get(dev);
> + if (driver && driver->err_handler)
> + return NULL;
So here we end up basically re-inventing PCI hotplug:
> + /* If the driver doesn't support EEH, remove it */
> + pr_info("EEH: Removing device %s without EEH support\n",
> + pci_name(dev));
> +
> + /* Detach EEH device from PCI device */
> + edev->pdev = NULL;
> + dev->dev.archdata.edev = NULL;
> + pci_dev_put(dev);
> +
> + /* Remove and address cache */
> + eeh_addr_cache_rmv_dev(dev);
> + eeh_sysfs_remove_device(dev);
> +
> + /* Remove it from PCI subsystem */
> + edev->mode |= EEH_DEV_REMOVED;
> + edev->bus = dev->bus;
> + pci_stop_and_remove_bus_device(dev);
> + (*removed)++;
> +
> + return NULL;
> +}
And ...
> +static void *eeh_add_device(void *data, void *userdata)
> +{
> + struct eeh_dev *edev = (struct eeh_dev *)data;
> + struct pci_dev *dev;
> + struct pci_bus *bus;
> + struct resource *r;
> + int *removed = (int *)userdata;
> + int devfn, i;
> +
> + if (!edev || !(edev->mode & EEH_DEV_REMOVED))
> + return NULL;
> + if (*removed <= 0)
> + return edev;
> +
> + /*
> + * We don't clear EEH_DEV_REMOVED flag here.
> + * Instead, do that before enabling IRQ to
> + * avoid complain from IRQ subsystem.
> + */
> + *removed -= 1;
> + bus = edev->bus;
> + devfn = edev->config_addr & 0xFF;
> + pr_info("EEH: Adding PCI device %04x:%02x:%02x.%01x\n",
> + edev->phb->global_number, bus->number,
> + PCI_SLOT(devfn), PCI_FUNC(devfn));
> +
> + /* Scan PCI function */
> + dev = pci_scan_single_device(bus, devfn);
> + if (!dev) {
> + pr_err("%s: Can't scan PCI function %02x:%02x.%01x\n",
> + __func__, bus->number, PCI_SLOT(devfn),
> + PCI_FUNC(devfn));
> + return NULL;
> + }
Wow, that's a hard scan ... we shouldn't do that on pseries (which you
don't since you don't use that code but still ...). See below for more
details.
> + /*
> + * Setup the PCI device. It's not enough to
> + * claim the resource and we need assign or
> + * reassign that.
> + */
> + pcibios_setup_device(dev);
> + for (i = 0; i < PCI_NUM_RESOURCES; i++) {
> + r = &dev->resource[i];
> + if (r->parent || !r->flags)
> + continue;
> + if (pci_assign_resource(dev, i)) {
Are we really supposed to play with resource assignment here ? We should
just restore the BARs to their former values which are normally cached
in the eeh dev...
> + pr_err("%s: Can't allocate %pR for %s\n",
> + __func__, r, pci_name(dev));
> + /* Clear it out */
> + r->start = 0;
> + r->end = 0;
> + r->flags = 0;
> + }
> + }
> +
> + /* Associate EEH device with PCI device */
> + pci_dev_get(dev);
> + edev->pdev = dev;
> + dev->dev.archdata.edev = edev;
> + eeh_addr_cache_insert_dev(dev);
> +
> + pci_bus_add_devices(bus);
> + eeh_sysfs_add_device(dev);
So all that is a duplicate of:
> + return NULL;
> +}
> +
> /**
> * eeh_reset_device - Perform actual reset of a pci slot
> * @pe: EEH PE
> @@ -351,6 +475,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
> {
> struct timeval tstamp;
> int cnt, rc;
> + int removed = 0;
>
> /* pcibios will clear the counter; save the value */
> cnt = pe->freeze_count;
> @@ -364,6 +489,8 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
> */
> if (bus)
> __pcibios_remove_pci_devices(bus, 0);
^^^^^^^^^^ this
and ...
> + else if (eeh_probe_mode_dev())
> + eeh_pe_dev_traverse(pe, eeh_rmv_device, &removed);
>
> /* Reset the pci controller. (Asserts RST#; resets config space).
> * Reconfigure bridges and devices. Don't try to bring the system
> @@ -384,8 +511,13 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
> * potentially weird things happen.
> */
> if (bus) {
> + pr_info("EEH: Hold for 5 seconds after reset\n");
> ssleep(5);
> pcibios_add_pci_devices(bus);
^^^^^^^^^^^^^ this
Except of course that the above are working on a "full bus" basis while
you try to deal with individual devices. More discussion about that
further down. Note also that the 2 above functions are in
arch/powerpc/platform/pseries. They should probably be moved (along with
their dependents) somewhere else such as in
arch/powerpc/kernel/pci-common.c as they are generally useful for
anything doing hotplug and as-is, it may not build with pseries enabled
in the .config
> + } else if (eeh_probe_mode_dev() && removed) {
> + pr_info("EEH: Hold for 5 seconds after reset\n");
> + ssleep(5);
> + eeh_pe_dev_traverse(pe, eeh_add_device, &removed);
> }
>
> pe->tstamp = tstamp;
So, basically, the existing code tries to unplug the whole PE (which is
always a bus) under pseries, if *any* device in the PE doesn't do EEH.
Your new code tries to handle individual devices in the PE (does it ?)
and remove only those who don't handle EEH (am I correct ?) in case of
an EEH error, and re-plug them.
I don't see a reason why the latter shouldn't work for the former case.
IE Why can't we also do individual devices instead of removing the whole
lot on pseries ?
Also, the code to do the actual removal and adding which is currently
in pci_dlpar.c properly handles "device-tree" style probing *provided*
that you restore the BARs before you call it.
So at this point, I suggest you consider one of two approaches:
- You can either break down the code in pci_dlpar.c to handle
individual devices instead of entire busses (and still provide a wrapper
for entire busses since that's used by the PCI hotplug code). Then you
use that for both pseries and powernv.
- Or you can make powernv do like pseries and unplug the entire PE
which is always a bus for now (at least until we do SR-IOV) and use the
existing pci_dlpar.c functions more/less unmodified... but you probably
will have to switch to the first option when we do SR-IOV.
Cheers,
Ben.
^ permalink raw reply
* [git pull] Please pull powerpc.git merge branch
From: Benjamin Herrenschmidt @ 2013-06-30 4:42 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linuxppc-dev, Linux Kernel list
Hi Linus !
Earlier today I mentioned that while we had fixed the kernel crashes,
EEH error recovery didn't always recover... It appears that I had
a fix for that already in powerpc-next (with a stable CC).
I cherry-picked it today and did a few tests and it seems that things
now work quite well. The patch is also pretty simple, so I see no reason
to wait before merging it.
Please pull.
Cheers,
Ben.
The following changes since commit 6c355beafdbd0a62add3a3d89825ca87cf8ecec0:
Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc (2013-06-29 17:02:48 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git merge
for you to fetch changes up to ea461abf61753b4b79e625a7c20650105b990f21:
powerpc/eeh: Fix fetching bus for single-dev-PE (2013-06-30 14:08:34 +1000)
----------------------------------------------------------------
Gavin Shan (1):
powerpc/eeh: Fix fetching bus for single-dev-PE
arch/powerpc/platforms/pseries/eeh_pe.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
^ permalink raw reply
* Re: [PATCH] powerpc/pci: Avoid overriding MSI interrupt
From: Benjamin Herrenschmidt @ 2013-06-30 4:06 UTC (permalink / raw)
To: Gavin Shan; +Cc: Yuanquan.Chen, linuxppc-dev, Guenter Roeck
In-Reply-To: <20130630015137.GA7919@shangw.(null)>
On Sun, 2013-06-30 at 09:51 +0800, Gavin Shan wrote:
> Ben, I think one patch was lost from mainline and that fixes the problem.
>
> https://git.kernel.org/cgit/linux/kernel/git/next/linux-next.git/commit/?id=5fb621698e94e3af8b413d9439041fde48e2784d
>
> I had the patch applied to /home/benh/linux-test and have following commands
> to inject errors, everything looks good :-)
>
> errinjct eeh -v -f 0 -p U78AB.001.WZSGBJ6-P1-C5-T1 -a 0x0 -m 0x0
> errinjct eeh -v -f 0 -p U78AB.001.WZSGBJ6-P1-C6-T1 -a 0x0 -m 0x0
Ok, thanks, it's in -next and not upstream yet. I'll ask Linus to pull
that one.
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH] powerpc/pci: Avoid overriding MSI interrupt
From: Gavin Shan @ 2013-06-30 1:51 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: Yuanquan.Chen, linuxppc-dev, Gavin Shan, Guenter Roeck
In-Reply-To: <1372547360.18612.76.camel@pasglop>
On Sun, Jun 30, 2013 at 09:09:20AM +1000, Benjamin Herrenschmidt wrote:
>On Fri, 2013-06-28 at 21:10 +0800, Gavin Shan wrote:
.../...
>I'm running some tests, so far it looks good. However, Gavin, when you
>have a chance on vpl3, try injecting errors to other adapters, for
>example the VGA adapter (you need to do lspci to trigger the EEH
>detection after that since there's no driver and use the "loc code"
>variant off errinjct) or eth2 (the cxgb3).
>
>All I get from EEH with these is:
>
>[ 362.962564] EEH: Detected PCI bus error on PHB#7-PE#10000
>[ 362.962570] eeh_handle_event: Cannot find PCI bus for PHB#7-PE#10000
>
>and
>
>[ 424.381083] EEH: Detected PCI bus error on PHB#6-PE#10000
>[ 424.381089] eeh_handle_event: Cannot find PCI bus for PHB#6-PE#10000
>
>Followed by ... nothing.
>
Ben, I think one patch was lost from mainline and that fixes the problem.
https://git.kernel.org/cgit/linux/kernel/git/next/linux-next.git/commit/?id=5fb621698e94e3af8b413d9439041fde48e2784d
I had the patch applied to /home/benh/linux-test and have following commands
to inject errors, everything looks good :-)
errinjct eeh -v -f 0 -p U78AB.001.WZSGBJ6-P1-C5-T1 -a 0x0 -m 0x0
errinjct eeh -v -f 0 -p U78AB.001.WZSGBJ6-P1-C6-T1 -a 0x0 -m 0x0
Thanks,
Gavin
^ permalink raw reply
* Re: [PATCH] powerpc/pci: Avoid overriding MSI interrupt
From: Guenter Roeck @ 2013-06-30 1:29 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Yuanquan.Chen, linuxppc-dev, Gavin Shan
In-Reply-To: <1372547360.18612.76.camel@pasglop>
On Sun, Jun 30, 2013 at 09:09:20AM +1000, Benjamin Herrenschmidt wrote:
> On Fri, 2013-06-28 at 21:10 +0800, Gavin Shan wrote:
> > The issue was introduced by commit 37f02195 ("powerpc/pci: fix
> > PCI-e devices rescan issue on powerpc platform"). The field
> > (struct pci_dev::irq) is reused by PCI core to trace the base
> > MSI interrupt number if the MSI stuff is enabled on the corresponding
> > device. When running to pcibios_setup_device(), we possibly still
> > have enabled MSI interrupt on the device. That means "pci_dev->irq"
> > still have the base MSI interrupt number and it will be overwritten
> > if we're going fix "pci_dev->irq" again by pci_read_irq_line().
> > Eventually, when we enable the device, it runs to kernel crash caused
> > by fetching the the MSI interrupt descriptor (struct msi_desc) from
> > non-MSI interrupt and using the NULL descriptor.
>
> So finally I decided instead to apply Guenter patch
>
> [PATCH v2] powerpc/pci: Improve device hotplug initialization
>
> Which fixes the underlying problem instead.
>
Guess I am not hitting above bug because I have my own patch applied ;).
Thanks a lot!
Guenter
^ permalink raw reply
* [git pull] Please pull powerpc.git merge branch
From: Benjamin Herrenschmidt @ 2013-06-29 23:16 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linuxppc-dev, Linux Kernel list, Guenter Roeck
Hi Linus !
We discovered some breakage in our "EEH" (PCI Error Handling) code while
doing error injection, due to a couple of regressions. One of them is
due to a patch (37f02195b) that, in hindsight, I shouldn't have merged
considering that it caused more problems than it solved.
Please pull those two fixes. One for a simple EEH address cache
initialization issue. The other one is a patch from Guenter that I had
originally planned to put in 3.11 but which happens to also fix that
other regression (a kernel oops during EEH error handling and possibly
hotplug).
With those two, the couple of test machines I've hammered with error
injection are remaining up now. EEH appears to still fail to recover on
some devices, so there is another problem that Gavin is looking into but
at least it's no longer crashing the kernel.
Cheers,
Ben.
The following changes since commit b37e161388ac3980d5dfb73050e85874b84253eb:
powerpc/pci: Fix boot panic on mpc83xx (regression) (2013-06-24 16:54:09 -0500)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git merge
for you to fetch changes up to 7846de406f43df98ac9864212dcfe3f2816bdb04:
powerpc/pci: Improve device hotplug initialization (2013-06-30 08:46:46 +1000)
----------------------------------------------------------------
Guenter Roeck (1):
powerpc/pci: Improve device hotplug initialization
Thadeu Lima de Souza Cascardo (1):
powerpc/eeh: Add eeh_dev to the cache during boot
arch/powerpc/kernel/pci-common.c | 17 ++++++++++++-----
arch/powerpc/platforms/pseries/eeh_cache.c | 4 ++--
2 files changed, 14 insertions(+), 7 deletions(-)
^ permalink raw reply
* Re: [PATCH] powerpc/pci: Avoid overriding MSI interrupt
From: Benjamin Herrenschmidt @ 2013-06-29 23:09 UTC (permalink / raw)
To: Gavin Shan; +Cc: Yuanquan.Chen, linuxppc-dev, Guenter Roeck
In-Reply-To: <1372425030-5759-1-git-send-email-shangw@linux.vnet.ibm.com>
On Fri, 2013-06-28 at 21:10 +0800, Gavin Shan wrote:
> The issue was introduced by commit 37f02195 ("powerpc/pci: fix
> PCI-e devices rescan issue on powerpc platform"). The field
> (struct pci_dev::irq) is reused by PCI core to trace the base
> MSI interrupt number if the MSI stuff is enabled on the corresponding
> device. When running to pcibios_setup_device(), we possibly still
> have enabled MSI interrupt on the device. That means "pci_dev->irq"
> still have the base MSI interrupt number and it will be overwritten
> if we're going fix "pci_dev->irq" again by pci_read_irq_line().
> Eventually, when we enable the device, it runs to kernel crash caused
> by fetching the the MSI interrupt descriptor (struct msi_desc) from
> non-MSI interrupt and using the NULL descriptor.
So finally I decided instead to apply Guenter patch
[PATCH v2] powerpc/pci: Improve device hotplug initialization
Which fixes the underlying problem instead.
I'm running some tests, so far it looks good. However, Gavin, when you
have a chance on vpl3, try injecting errors to other adapters, for
example the VGA adapter (you need to do lspci to trigger the EEH
detection after that since there's no driver and use the "loc code"
variant off errinjct) or eth2 (the cxgb3).
All I get from EEH with these is:
[ 362.962564] EEH: Detected PCI bus error on PHB#7-PE#10000
[ 362.962570] eeh_handle_event: Cannot find PCI bus for PHB#7-PE#10000
and
[ 424.381083] EEH: Detected PCI bus error on PHB#6-PE#10000
[ 424.381089] eeh_handle_event: Cannot find PCI bus for PHB#6-PE#10000
Followed by ... nothing.
This is a tree which has Cascardo patch and Gunther patch (usual
location on vpl3).
Can you have a look ?
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH] of: Specify initrd location using 64-bit
From: Geert Uytterhoeven @ 2013-06-29 8:34 UTC (permalink / raw)
To: Santosh Shilimkar, Rob Herring
Cc: Nicolas Pitre, linux-mips, Aurelien Jacquiot, Catalin Marinas,
Sebastian Andrzej Siewior, Will Deacon, Max Filippov,
Paul Mackerras, Jonas Bonn, Russell King, linux-c6x-dev,
the arch/x86 maintainers, arm@kernel.org, Grant Likely,
Jean-Christophe PLAGNIOL-VILLARD, linux-xtensa, James Hogan,
devicetree-discuss, Rob Herring,
linux-arm-kernel@lists.infradead.org, Chris Zankel, Vineet Gupta,
Linux Kernel Mailing List, Ralf Baechle,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <51CE1F92.3070802@ti.com>
On Sat, Jun 29, 2013 at 1:43 AM, Santosh Shilimkar
<santosh.shilimkar@ti.com> wrote:
>>>>> Rob,
>>>>> Are you ok with phys_addr_t since your concern was about rest
>>>>> of the memory specific bits of the device-tree code use u64 ?
>>>>
>>>> No. I still think it should be u64 for same reasons I said originally.
>>>
>>> +1
>>>
>> +1
>>
>> fix type
>>
> Apart from waste of 32bit, what is the other concern you
> have ? I really want to converge on this patch because it
> has been a open ended discussion for quite some time. Does
> that really break any thing on x86 or your concern is more
> from semantics of the physical address.
As the "original reasons" were not in this thread, I had to search a bit.
I suppose you mean this one: https://lkml.org/lkml/2012/9/13/544 ?
Summarized:
| The address to load the initrd is decided by the bootloader/user and set
| at that point later in time.
| The dtb should not be tied to the kernel you are booting.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH -V2 4/4] powerpc/kvm: Use 256K chunk to track both RMA and hash page table allocation.
From: Paul Mackerras @ 2013-06-29 7:43 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: m.szyprowski, linuxppc-dev, mina86, linux-mm
In-Reply-To: <1372410662-3748-4-git-send-email-aneesh.kumar@linux.vnet.ibm.com>
On Fri, Jun 28, 2013 at 02:41:02PM +0530, Aneesh Kumar K.V wrote:
> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>
> Both RMA and hash page table request will be a multiple of 256K. We can use
> a chunk size of 256K to track the free/used 256K chunk in the bitmap. This
> should help to reduce the bitmap size.
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Looks good overall, just some minor comments below:
> + int chunk_count, nr_chunk;
I get a little nervous when I see "int" used for variables storing a
number of pages or related things such as chunks. Yes, int is enough
today but one day it won't be, and there is no time or space penalty
to using "long" instead, and in fact the code generated "long"
variables can be slightly shorter. So please make variables like this
"long". (That will require changes to earlier patches in this
series.)
> + * aling mask with chunk size. The bit tracks pages in chunk size
Should be "align".
Paul.
^ permalink raw reply
* Re: [PATCH -V2 3/4] powerpc: Contiguous memory allocator based RMA allocation
From: Paul Mackerras @ 2013-06-29 7:28 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: m.szyprowski, linuxppc-dev, mina86, linux-mm
In-Reply-To: <1372410662-3748-3-git-send-email-aneesh.kumar@linux.vnet.ibm.com>
On Fri, Jun 28, 2013 at 02:41:01PM +0530, Aneesh Kumar K.V wrote:
> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>
> Use CMA for allocation of RMA region for guest. Also remove linear allocator
> now that it is not used
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Acked-by: Paul Mackerras <paulus@samba.org>
... though it could use a more extensive patch description.
Paul.
^ permalink raw reply
* Re: [PATCH -V2 2/4] powerpc: Contiguous memory allocator based hash page allocation
From: Paul Mackerras @ 2013-06-29 7:25 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: m.szyprowski, linuxppc-dev, mina86, linux-mm
In-Reply-To: <1372410662-3748-2-git-send-email-aneesh.kumar@linux.vnet.ibm.com>
On Fri, Jun 28, 2013 at 02:41:00PM +0530, Aneesh Kumar K.V wrote:
> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>
> Use CMA for allocation of guest hash page.
"page table" not just "page". This patch description seems a bit
brief for a patch of this length. Please describe a little more of
the motivation and the design decisions.
> + if (selected_size) {
> + pr_debug("%s: reserving %ld MiB for global area\n", __func__,
> + (unsigned long)selected_size / SZ_1M);
> + align_size = hpt_align_pages << PAGE_SHIFT;
> + kvm_cma_declare_contiguous(selected_size, align_size);
The alignment you declare here has to be at least as large as the
largest alignment that we will be requesting for any block later on.
This alignment is fine for POWER7, but PPC970 requires the HPT to be
aligned on a multiple of its size. For PPC970 we should make sure
align_size is at least as large as any block that we could allocate.
Thus align_size should be at least __rounddown_pow_of_two(selected_size)
for PPC970.
Paul.
^ permalink raw reply
* Re: [PATCH] powerpc/pci: Avoid overriding MSI interrupt
From: Benjamin Herrenschmidt @ 2013-06-29 7:20 UTC (permalink / raw)
To: Gavin Shan; +Cc: Yuanquan.Chen, linuxppc-dev
In-Reply-To: <1372425030-5759-1-git-send-email-shangw@linux.vnet.ibm.com>
On Fri, 2013-06-28 at 21:10 +0800, Gavin Shan wrote:
> The issue was introduced by commit 37f02195 ("powerpc/pci: fix
> PCI-e devices rescan issue on powerpc platform"). The field
That "fix" caused more problems than it solved. There's a better
approach floating around that I will merge eventually next week.
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH -V2 1/4] mm/cma: Move dma contiguous changes into a seperate config
From: Paul Mackerras @ 2013-06-29 7:14 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: m.szyprowski, linuxppc-dev, mina86, linux-mm
In-Reply-To: <1372410662-3748-1-git-send-email-aneesh.kumar@linux.vnet.ibm.com>
On Fri, Jun 28, 2013 at 02:40:59PM +0530, Aneesh Kumar K.V wrote:
> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>
> We want to use CMA for allocating hash page table and real mode area for
> PPC64. Hence move DMA contiguous related changes into a seperate config
> so that ppc64 can enable CMA without requiring DMA contiguous.
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Acked-by: Paul Mackerras <paulus@samba.org>
When you send out the next version, please cc kvm-ppc@vger.kernel.org,
kvm@vger.kernel.org and Alexander Graf <agraf@suse.de>.
Paul.
^ permalink raw reply
* Re: [PATCH] of: Specify initrd location using 64-bit
From: Santosh Shilimkar @ 2013-06-28 23:43 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: Nicolas Pitre, linux-mips, Aurelien Jacquiot, Catalin Marinas,
Will Deacon, Max Filippov, Paul Mackerras, Jonas Bonn,
Russell King, linux-c6x-dev, x86, arm, Rob Herring, Grant Likely,
Jean-Christophe PLAGNIOL-VILLARD, linux-xtensa, James Hogan,
devicetree-discuss, Rob Herring,
linux-arm-kernel@lists.infradead.org, Chris Zankel, Vineet Gupta,
Linux Kernel Mailing List, Ralf Baechle,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20130628134931.GD21034@game.jcrosoft.org>
Sebastian,
On Friday 28 June 2013 09:49 AM, Jean-Christophe PLAGNIOL-VILLARD wrote:
> On 10:59 Fri 28 Jun , Grant Likely wrote:
>> On Thu, Jun 27, 2013 at 9:54 PM, Rob Herring <robherring2@gmail.com> wrote:
>>> On 06/21/2013 12:20 PM, Santosh Shilimkar wrote:
>>>> On Friday 21 June 2013 05:04 AM, Sebastian Andrzej Siewior wrote:
>>>>> On 06/21/2013 02:52 AM, Santosh Shilimkar wrote:
>>>>>> diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c
>>>>>> index 0a2c68f..62e2e8f 100644
>>>>>> --- a/arch/microblaze/kernel/prom.c
>>>>>> +++ b/arch/microblaze/kernel/prom.c
>>>>>> @@ -136,8 +136,7 @@ void __init early_init_devtree(void *params)
>>>>>> }
>>>>>>
>>>>>> #ifdef CONFIG_BLK_DEV_INITRD
>>>>>> -void __init early_init_dt_setup_initrd_arch(unsigned long start,
>>>>>> - unsigned long end)
>>>>>> +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end)
>>>>>> {
>>>>>> initrd_start = (unsigned long)__va(start);
>>>>>> initrd_end = (unsigned long)__va(end);
>>>>>
>>>>> I think it would better to go here for phys_addr_t instead of u64. This
>>>>> would force you in of_flat_dt_match() to check if the value passed from
>>>>> DT specifies a memory address outside of 32bit address space and the
>>>>> kernel can't deal with this because its phys_addr_t is 32bit only due
>>>>> to a Kconfig switch.
>>>>>
>>>>> For x86, the initrd has to remain in the 32bit address space so passing
>>>>> the initrd in the upper range would violate the ABI. Not sure if this
>>>>> is true for other archs as well (ARM obviously not).
>>>>>
>>>> That pretty much means phys_addr_t. It will work for me as well but
>>>> in last thread from consistency with memory and reserved node, Rob
>>>> insisted to keep it as u64. So before I re-spin another version,
>>>> would like to here what Rob has to say considering the x86 requirement.
>>>>
>>>> Rob,
>>>> Are you ok with phys_addr_t since your concern was about rest
>>>> of the memory specific bits of the device-tree code use u64 ?
>>>
>>> No. I still think it should be u64 for same reasons I said originally.
>>
>> +1
>>
> +1
>
> fix type
>
Apart from waste of 32bit, what is the other concern you
have ? I really want to converge on this patch because it
has been a open ended discussion for quite some time. Does
that really break any thing on x86 or your concern is more
from semantics of the physical address.
Thanks for help.
Regards,
Santosh
^ permalink raw reply
* Re: [PATCH] of: Specify initrd location using 64-bit
From: Jean-Christophe PLAGNIOL-VILLARD @ 2013-06-28 13:49 UTC (permalink / raw)
To: Grant Likely
Cc: Nicolas Pitre, linux-mips, Aurelien Jacquiot, Catalin Marinas,
Sebastian Andrzej Siewior, Will Deacon, Max Filippov,
Paul Mackerras, Jonas Bonn, Russell King, linux-c6x-dev, x86, arm,
Rob Herring, linux-xtensa, James Hogan, devicetree-discuss,
Rob Herring, linux-arm-kernel@lists.infradead.org, Chris Zankel,
Vineet Gupta, Linux Kernel Mailing List, Ralf Baechle,
Santosh Shilimkar, linuxppc-dev@lists.ozlabs.org
In-Reply-To: <CACxGe6vOH0sCFVVXrYqD3dbYdOvithVu7-d1cvy5885i8x_Myw@mail.gmail.com>
On 10:59 Fri 28 Jun , Grant Likely wrote:
> On Thu, Jun 27, 2013 at 9:54 PM, Rob Herring <robherring2@gmail.com> wrote:
> > On 06/21/2013 12:20 PM, Santosh Shilimkar wrote:
> >> On Friday 21 June 2013 05:04 AM, Sebastian Andrzej Siewior wrote:
> >>> On 06/21/2013 02:52 AM, Santosh Shilimkar wrote:
> >>>> diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c
> >>>> index 0a2c68f..62e2e8f 100644
> >>>> --- a/arch/microblaze/kernel/prom.c
> >>>> +++ b/arch/microblaze/kernel/prom.c
> >>>> @@ -136,8 +136,7 @@ void __init early_init_devtree(void *params)
> >>>> }
> >>>>
> >>>> #ifdef CONFIG_BLK_DEV_INITRD
> >>>> -void __init early_init_dt_setup_initrd_arch(unsigned long start,
> >>>> - unsigned long end)
> >>>> +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end)
> >>>> {
> >>>> initrd_start = (unsigned long)__va(start);
> >>>> initrd_end = (unsigned long)__va(end);
> >>>
> >>> I think it would better to go here for phys_addr_t instead of u64. This
> >>> would force you in of_flat_dt_match() to check if the value passed from
> >>> DT specifies a memory address outside of 32bit address space and the
> >>> kernel can't deal with this because its phys_addr_t is 32bit only due
> >>> to a Kconfig switch.
> >>>
> >>> For x86, the initrd has to remain in the 32bit address space so passing
> >>> the initrd in the upper range would violate the ABI. Not sure if this
> >>> is true for other archs as well (ARM obviously not).
> >>>
> >> That pretty much means phys_addr_t. It will work for me as well but
> >> in last thread from consistency with memory and reserved node, Rob
> >> insisted to keep it as u64. So before I re-spin another version,
> >> would like to here what Rob has to say considering the x86 requirement.
> >>
> >> Rob,
> >> Are you ok with phys_addr_t since your concern was about rest
> >> of the memory specific bits of the device-tree code use u64 ?
> >
> > No. I still think it should be u64 for same reasons I said originally.
>
> +1
>
+1
fix type
Best Regards,
J.
^ permalink raw reply
* Re: [1/4] Device tree entry for Freescale TDM controller
From: Scott Wood @ 2013-06-28 22:15 UTC (permalink / raw)
To: Sandeep Singh; +Cc: Poonam Aggrwal, linuxppc-dev, linux-arm-kernel
In-Reply-To: <1362655668-26721-1-git-send-email-Sandeep@freescale.com>
On Thu, Mar 07, 2013 at 04:57:45PM +0530, Sandeep Singh wrote:
> +tdm@16000 {
> + compatible = "fsl,tdm1.0";
> + reg = <0x16000 0x200 0x2c000 0x2000>;
> + clock-frequency = <0>;
> + tdm_tx_clk = <2048000>;
> + interrupts = <62 8 0 0>;
> + fsl,max-time-slots = <128>;
> +};
tdm_tx_clk isn't in the binding (and should be named fsl,tdm-tx-clk if it
is meant to be here at all).
-Scott
^ permalink raw reply
* [PATCH 1/7] rapidio: convert switch drivers to modules
From: Alexandre Bounine @ 2013-06-28 19:18 UTC (permalink / raw)
To: Andrew Morton, linux-kernel, linuxppc-dev
Cc: Stef van Os, Micha Nelissen, Andre van Herk, Alexandre Bounine,
Jean Delvare
In-Reply-To: <1372447136-5751-1-git-send-email-alexandre.bounine@idt.com>
Rework RapidIO switch drivers to add an option to build them as loadable
kernel modules.
This patch removes RapidIO-specific vmlinux section and converts switch drivers
to be compatible with LDM driver registration method. To simplify registration
of device-specific callback routines this patch introduces rio_switch_ops data
structure. The sw_sysfs() callback is removed from the list of device-specific
operations because under the new structure its functions can be handled by
switch driver's probe() and remove() routines.
If a specific switch device driver is not loaded the RapidIO subsystem core will
use default standard-based operations to configure a switch. Because the current
implementation of RapidIO enumeration/discovery method relies on availability of
device-specific operations for error management, switch device drivers must be
loaded before the RapidIO enumeration/discovery starts.
This patch also moves several common routines from enumeration/discovery module
into the RapidIO core code to make switch-specific operations accessible to all
components of RapidIO subsystem.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Andre van Herk <andre.van.herk@Prodrive.nl>
Cc: Micha Nelissen <micha.nelissen@Prodrive.nl>
Cc: Stef van Os <stef.van.os@Prodrive.nl>
Cc: Jean Delvare <jdelvare@suse.de>
---
drivers/rapidio/Kconfig | 5 +
drivers/rapidio/rio-scan.c | 171 +--------------------
drivers/rapidio/rio-sysfs.c | 4 -
drivers/rapidio/rio.c | 286 +++++++++++++++++++++++++++++------
drivers/rapidio/rio.h | 41 +----
drivers/rapidio/switches/Kconfig | 12 +-
drivers/rapidio/switches/idt_gen2.c | 98 ++++++++++---
drivers/rapidio/switches/idtcps.c | 86 +++++++++--
drivers/rapidio/switches/tsi568.c | 71 ++++++++-
drivers/rapidio/switches/tsi57x.c | 81 ++++++++--
include/asm-generic/vmlinux.lds.h | 7 -
include/linux/rio.h | 51 +++----
12 files changed, 568 insertions(+), 345 deletions(-)
diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig
index 5ab0564..3e3be57 100644
--- a/drivers/rapidio/Kconfig
+++ b/drivers/rapidio/Kconfig
@@ -67,4 +67,9 @@ config RAPIDIO_ENUM_BASIC
endchoice
+menu "RapidIO Switch drivers"
+ depends on RAPIDIO
+
source "drivers/rapidio/switches/Kconfig"
+
+endmenu
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 4c15dbf..61c913f 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -406,6 +406,7 @@ static struct rio_dev *rio_setup_device(struct rio_net *net,
rio_mport_write_config_32(port, destid, hopcount,
RIO_COMPONENT_TAG_CSR, next_comptag);
rdev->comp_tag = next_comptag++;
+ rdev->do_enum = true;
} else {
rio_mport_read_config_32(port, destid, hopcount,
RIO_COMPONENT_TAG_CSR,
@@ -434,6 +435,7 @@ static struct rio_dev *rio_setup_device(struct rio_net *net,
rswitch = rdev->rswitch;
rswitch->switchid = rdev->comp_tag & RIO_CTAG_UDEVID;
rswitch->port_ok = 0;
+ spin_lock_init(&rswitch->lock);
rswitch->route_table = kzalloc(sizeof(u8)*
RIO_MAX_ROUTE_ENTRIES(port->sys_size),
GFP_KERNEL);
@@ -445,11 +447,9 @@ static struct rio_dev *rio_setup_device(struct rio_net *net,
rswitch->route_table[rdid] = RIO_INVALID_ROUTE;
dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->net->id,
rswitch->switchid);
- rio_switch_init(rdev, do_enum);
- if (do_enum && rswitch->clr_table)
- rswitch->clr_table(port, destid, hopcount,
- RIO_GLOBAL_TABLE);
+ if (do_enum)
+ rio_route_clr_table(rdev, RIO_GLOBAL_TABLE, 0);
list_add_tail(&rswitch->node, &net->switches);
@@ -533,156 +533,6 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport)
}
/**
- * rio_lock_device - Acquires host device lock for specified device
- * @port: Master port to send transaction
- * @destid: Destination ID for device/switch
- * @hopcount: Hopcount to reach switch
- * @wait_ms: Max wait time in msec (0 = no timeout)
- *
- * Attepts to acquire host device lock for specified device
- * Returns 0 if device lock acquired or EINVAL if timeout expires.
- */
-static int
-rio_lock_device(struct rio_mport *port, u16 destid, u8 hopcount, int wait_ms)
-{
- u32 result;
- int tcnt = 0;
-
- /* Attempt to acquire device lock */
- rio_mport_write_config_32(port, destid, hopcount,
- RIO_HOST_DID_LOCK_CSR, port->host_deviceid);
- rio_mport_read_config_32(port, destid, hopcount,
- RIO_HOST_DID_LOCK_CSR, &result);
-
- while (result != port->host_deviceid) {
- if (wait_ms != 0 && tcnt == wait_ms) {
- pr_debug("RIO: timeout when locking device %x:%x\n",
- destid, hopcount);
- return -EINVAL;
- }
-
- /* Delay a bit */
- mdelay(1);
- tcnt++;
- /* Try to acquire device lock again */
- rio_mport_write_config_32(port, destid,
- hopcount,
- RIO_HOST_DID_LOCK_CSR,
- port->host_deviceid);
- rio_mport_read_config_32(port, destid,
- hopcount,
- RIO_HOST_DID_LOCK_CSR, &result);
- }
-
- return 0;
-}
-
-/**
- * rio_unlock_device - Releases host device lock for specified device
- * @port: Master port to send transaction
- * @destid: Destination ID for device/switch
- * @hopcount: Hopcount to reach switch
- *
- * Returns 0 if device lock released or EINVAL if fails.
- */
-static int
-rio_unlock_device(struct rio_mport *port, u16 destid, u8 hopcount)
-{
- u32 result;
-
- /* Release device lock */
- rio_mport_write_config_32(port, destid,
- hopcount,
- RIO_HOST_DID_LOCK_CSR,
- port->host_deviceid);
- rio_mport_read_config_32(port, destid, hopcount,
- RIO_HOST_DID_LOCK_CSR, &result);
- if ((result & 0xffff) != 0xffff) {
- pr_debug("RIO: badness when releasing device lock %x:%x\n",
- destid, hopcount);
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * rio_route_add_entry- Add a route entry to a switch routing table
- * @rdev: RIO device
- * @table: Routing table ID
- * @route_destid: Destination ID to be routed
- * @route_port: Port number to be routed
- * @lock: lock switch device flag
- *
- * Calls the switch specific add_entry() method to add a route entry
- * on a switch. The route table can be specified using the @table
- * argument if a switch has per port routing tables or the normal
- * use is to specific all tables (or the global table) by passing
- * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL
- * on failure.
- */
-static int
-rio_route_add_entry(struct rio_dev *rdev,
- u16 table, u16 route_destid, u8 route_port, int lock)
-{
- int rc;
-
- if (lock) {
- rc = rio_lock_device(rdev->net->hport, rdev->destid,
- rdev->hopcount, 1000);
- if (rc)
- return rc;
- }
-
- rc = rdev->rswitch->add_entry(rdev->net->hport, rdev->destid,
- rdev->hopcount, table,
- route_destid, route_port);
- if (lock)
- rio_unlock_device(rdev->net->hport, rdev->destid,
- rdev->hopcount);
-
- return rc;
-}
-
-/**
- * rio_route_get_entry- Read a route entry in a switch routing table
- * @rdev: RIO device
- * @table: Routing table ID
- * @route_destid: Destination ID to be routed
- * @route_port: Pointer to read port number into
- * @lock: lock switch device flag
- *
- * Calls the switch specific get_entry() method to read a route entry
- * in a switch. The route table can be specified using the @table
- * argument if a switch has per port routing tables or the normal
- * use is to specific all tables (or the global table) by passing
- * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL
- * on failure.
- */
-static int
-rio_route_get_entry(struct rio_dev *rdev, u16 table,
- u16 route_destid, u8 *route_port, int lock)
-{
- int rc;
-
- if (lock) {
- rc = rio_lock_device(rdev->net->hport, rdev->destid,
- rdev->hopcount, 1000);
- if (rc)
- return rc;
- }
-
- rc = rdev->rswitch->get_entry(rdev->net->hport, rdev->destid,
- rdev->hopcount, table,
- route_destid, route_port);
- if (lock)
- rio_unlock_device(rdev->net->hport, rdev->destid,
- rdev->hopcount);
-
- return rc;
-}
-
-/**
* rio_get_host_deviceid_lock- Reads the Host Device ID Lock CSR on a device
* @port: Master port to send transaction
* @hopcount: Number of hops to the device
@@ -1094,12 +944,9 @@ static void rio_update_route_tables(struct rio_net *net)
sport = RIO_GET_PORT_NUM(swrdev->swpinfo);
- if (rswitch->add_entry) {
- rio_route_add_entry(swrdev,
- RIO_GLOBAL_TABLE, destid,
- sport, 0);
- rswitch->route_table[destid] = sport;
- }
+ rio_route_add_entry(swrdev, RIO_GLOBAL_TABLE,
+ destid, sport, 0);
+ rswitch->route_table[destid] = sport;
}
}
}
@@ -1115,8 +962,8 @@ static void rio_update_route_tables(struct rio_net *net)
static void rio_init_em(struct rio_dev *rdev)
{
if (rio_is_switch(rdev) && (rdev->em_efptr) &&
- (rdev->rswitch->em_init)) {
- rdev->rswitch->em_init(rdev);
+ rdev->rswitch->ops && rdev->rswitch->ops->em_init) {
+ rdev->rswitch->ops->em_init(rdev);
}
}
diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c
index 66d4acd..864e52f 100644
--- a/drivers/rapidio/rio-sysfs.c
+++ b/drivers/rapidio/rio-sysfs.c
@@ -257,8 +257,6 @@ int rio_create_sysfs_dev_files(struct rio_dev *rdev)
err |= device_create_file(&rdev->dev, &dev_attr_routes);
err |= device_create_file(&rdev->dev, &dev_attr_lnext);
err |= device_create_file(&rdev->dev, &dev_attr_hopcount);
- if (!err && rdev->rswitch->sw_sysfs)
- err = rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_CREATE);
}
if (err)
@@ -281,8 +279,6 @@ void rio_remove_sysfs_dev_files(struct rio_dev *rdev)
device_remove_file(&rdev->dev, &dev_attr_routes);
device_remove_file(&rdev->dev, &dev_attr_lnext);
device_remove_file(&rdev->dev, &dev_attr_hopcount);
- if (rdev->rswitch->sw_sysfs)
- rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_REMOVE);
}
}
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index cb1c089..b17d521 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -7,7 +7,6 @@
*
* Copyright 2009 Integrated Device Technology, Inc.
* Alex Bounine <alexandre.bounine@idt.com>
- * - Added Port-Write/Error Management initialization and handling
*
* 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
@@ -580,44 +579,6 @@ int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock)
EXPORT_SYMBOL_GPL(rio_set_port_lockout);
/**
- * rio_switch_init - Sets switch operations for a particular vendor switch
- * @rdev: RIO device
- * @do_enum: Enumeration/Discovery mode flag
- *
- * Searches the RIO switch ops table for known switch types. If the vid
- * and did match a switch table entry, then call switch initialization
- * routine to setup switch-specific routines.
- */
-void rio_switch_init(struct rio_dev *rdev, int do_enum)
-{
- struct rio_switch_ops *cur = __start_rio_switch_ops;
- struct rio_switch_ops *end = __end_rio_switch_ops;
-
- while (cur < end) {
- if ((cur->vid == rdev->vid) && (cur->did == rdev->did)) {
- pr_debug("RIO: calling init routine for %s\n",
- rio_name(rdev));
- cur->init_hook(rdev, do_enum);
- break;
- }
- cur++;
- }
-
- if ((cur >= end) && (rdev->pef & RIO_PEF_STD_RT)) {
- pr_debug("RIO: adding STD routing ops for %s\n",
- rio_name(rdev));
- rdev->rswitch->add_entry = rio_std_route_add_entry;
- rdev->rswitch->get_entry = rio_std_route_get_entry;
- rdev->rswitch->clr_table = rio_std_route_clr_table;
- }
-
- if (!rdev->rswitch->add_entry || !rdev->rswitch->get_entry)
- printk(KERN_ERR "RIO: missing routing ops for %s\n",
- rio_name(rdev));
-}
-EXPORT_SYMBOL_GPL(rio_switch_init);
-
-/**
* rio_enable_rx_tx_port - enable input receiver and output transmitter of
* given port
* @port: Master port associated with the RIO network
@@ -970,8 +931,8 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
/*
* Process the port-write notification from switch
*/
- if (rdev->rswitch->em_handle)
- rdev->rswitch->em_handle(rdev, portnum);
+ if (rdev->rswitch->ops && rdev->rswitch->ops->em_handle)
+ rdev->rswitch->ops->em_handle(rdev, portnum);
rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
@@ -1207,8 +1168,9 @@ struct rio_dev *rio_get_device(u16 vid, u16 did, struct rio_dev *from)
* @route_destid: destID entry in the RT
* @route_port: destination port for specified destID
*/
-int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
- u16 table, u16 route_destid, u8 route_port)
+static int
+rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table, u16 route_destid, u8 route_port)
{
if (table == RIO_GLOBAL_TABLE) {
rio_mport_write_config_32(mport, destid, hopcount,
@@ -1234,8 +1196,9 @@ int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
* @route_destid: destID entry in the RT
* @route_port: returned destination port for specified destID
*/
-int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
- u16 table, u16 route_destid, u8 *route_port)
+static int
+rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table, u16 route_destid, u8 *route_port)
{
u32 result;
@@ -1259,8 +1222,9 @@ int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
* @hopcount: Number of switch hops to the device
* @table: routing table ID (global or port-specific)
*/
-int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
- u16 table)
+static int
+rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table)
{
u32 max_destid = 0xff;
u32 i, pef, id_inc = 1, ext_cfg = 0;
@@ -1301,6 +1265,234 @@ int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
return 0;
}
+/**
+ * rio_lock_device - Acquires host device lock for specified device
+ * @port: Master port to send transaction
+ * @destid: Destination ID for device/switch
+ * @hopcount: Hopcount to reach switch
+ * @wait_ms: Max wait time in msec (0 = no timeout)
+ *
+ * Attepts to acquire host device lock for specified device
+ * Returns 0 if device lock acquired or EINVAL if timeout expires.
+ */
+int rio_lock_device(struct rio_mport *port, u16 destid,
+ u8 hopcount, int wait_ms)
+{
+ u32 result;
+ int tcnt = 0;
+
+ /* Attempt to acquire device lock */
+ rio_mport_write_config_32(port, destid, hopcount,
+ RIO_HOST_DID_LOCK_CSR, port->host_deviceid);
+ rio_mport_read_config_32(port, destid, hopcount,
+ RIO_HOST_DID_LOCK_CSR, &result);
+
+ while (result != port->host_deviceid) {
+ if (wait_ms != 0 && tcnt == wait_ms) {
+ pr_debug("RIO: timeout when locking device %x:%x\n",
+ destid, hopcount);
+ return -EINVAL;
+ }
+
+ /* Delay a bit */
+ mdelay(1);
+ tcnt++;
+ /* Try to acquire device lock again */
+ rio_mport_write_config_32(port, destid,
+ hopcount,
+ RIO_HOST_DID_LOCK_CSR,
+ port->host_deviceid);
+ rio_mport_read_config_32(port, destid,
+ hopcount,
+ RIO_HOST_DID_LOCK_CSR, &result);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rio_lock_device);
+
+/**
+ * rio_unlock_device - Releases host device lock for specified device
+ * @port: Master port to send transaction
+ * @destid: Destination ID for device/switch
+ * @hopcount: Hopcount to reach switch
+ *
+ * Returns 0 if device lock released or EINVAL if fails.
+ */
+int rio_unlock_device(struct rio_mport *port, u16 destid, u8 hopcount)
+{
+ u32 result;
+
+ /* Release device lock */
+ rio_mport_write_config_32(port, destid,
+ hopcount,
+ RIO_HOST_DID_LOCK_CSR,
+ port->host_deviceid);
+ rio_mport_read_config_32(port, destid, hopcount,
+ RIO_HOST_DID_LOCK_CSR, &result);
+ if ((result & 0xffff) != 0xffff) {
+ pr_debug("RIO: badness when releasing device lock %x:%x\n",
+ destid, hopcount);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rio_unlock_device);
+
+/**
+ * rio_route_add_entry- Add a route entry to a switch routing table
+ * @rdev: RIO device
+ * @table: Routing table ID
+ * @route_destid: Destination ID to be routed
+ * @route_port: Port number to be routed
+ * @lock: apply a hardware lock on switch device flag (1=lock, 0=no_lock)
+ *
+ * If available calls the switch specific add_entry() method to add a route
+ * entry into a switch routing table. Otherwise uses standard RT update method
+ * as defined by RapidIO specification. A specific routing table can be selected
+ * using the @table argument if a switch has per port routing tables or
+ * the standard (or global) table may be used by passing
+ * %RIO_GLOBAL_TABLE in @table.
+ *
+ * Returns %0 on success or %-EINVAL on failure.
+ */
+int rio_route_add_entry(struct rio_dev *rdev,
+ u16 table, u16 route_destid, u8 route_port, int lock)
+{
+ int rc = -EINVAL;
+ struct rio_switch_ops *ops = rdev->rswitch->ops;
+
+ if (lock) {
+ rc = rio_lock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount, 1000);
+ if (rc)
+ return rc;
+ }
+
+ spin_lock(&rdev->rswitch->lock);
+
+ if (ops == NULL || ops->add_entry == NULL) {
+ rc = rio_std_route_add_entry(rdev->net->hport, rdev->destid,
+ rdev->hopcount, table,
+ route_destid, route_port);
+ } else if (try_module_get(ops->owner)) {
+ rc = ops->add_entry(rdev->net->hport, rdev->destid,
+ rdev->hopcount, table, route_destid,
+ route_port);
+ module_put(ops->owner);
+ }
+
+ spin_unlock(&rdev->rswitch->lock);
+
+ if (lock)
+ rio_unlock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rio_route_add_entry);
+
+/**
+ * rio_route_get_entry- Read an entry from a switch routing table
+ * @rdev: RIO device
+ * @table: Routing table ID
+ * @route_destid: Destination ID to be routed
+ * @route_port: Pointer to read port number into
+ * @lock: apply a hardware lock on switch device flag (1=lock, 0=no_lock)
+ *
+ * If available calls the switch specific get_entry() method to fetch a route
+ * entry from a switch routing table. Otherwise uses standard RT read method
+ * as defined by RapidIO specification. A specific routing table can be selected
+ * using the @table argument if a switch has per port routing tables or
+ * the standard (or global) table may be used by passing
+ * %RIO_GLOBAL_TABLE in @table.
+ *
+ * Returns %0 on success or %-EINVAL on failure.
+ */
+int rio_route_get_entry(struct rio_dev *rdev, u16 table,
+ u16 route_destid, u8 *route_port, int lock)
+{
+ int rc = -EINVAL;
+ struct rio_switch_ops *ops = rdev->rswitch->ops;
+
+ if (lock) {
+ rc = rio_lock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount, 1000);
+ if (rc)
+ return rc;
+ }
+
+ spin_lock(&rdev->rswitch->lock);
+
+ if (ops == NULL || ops->get_entry == NULL) {
+ rc = rio_std_route_get_entry(rdev->net->hport, rdev->destid,
+ rdev->hopcount, table,
+ route_destid, route_port);
+ } else if (try_module_get(ops->owner)) {
+ rc = ops->get_entry(rdev->net->hport, rdev->destid,
+ rdev->hopcount, table, route_destid,
+ route_port);
+ module_put(ops->owner);
+ }
+
+ spin_unlock(&rdev->rswitch->lock);
+
+ if (lock)
+ rio_unlock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rio_route_get_entry);
+
+/**
+ * rio_route_clr_table - Clear a switch routing table
+ * @rdev: RIO device
+ * @table: Routing table ID
+ * @lock: apply a hardware lock on switch device flag (1=lock, 0=no_lock)
+ *
+ * If available calls the switch specific clr_table() method to clear a switch
+ * routing table. Otherwise uses standard RT write method as defined by RapidIO
+ * specification. A specific routing table can be selected using the @table
+ * argument if a switch has per port routing tables or the standard (or global)
+ * table may be used by passing %RIO_GLOBAL_TABLE in @table.
+ *
+ * Returns %0 on success or %-EINVAL on failure.
+ */
+int rio_route_clr_table(struct rio_dev *rdev, u16 table, int lock)
+{
+ int rc = -EINVAL;
+ struct rio_switch_ops *ops = rdev->rswitch->ops;
+
+ if (lock) {
+ rc = rio_lock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount, 1000);
+ if (rc)
+ return rc;
+ }
+
+ spin_lock(&rdev->rswitch->lock);
+
+ if (ops == NULL || ops->clr_table == NULL) {
+ rc = rio_std_route_clr_table(rdev->net->hport, rdev->destid,
+ rdev->hopcount, table);
+ } else if (try_module_get(ops->owner)) {
+ rc = ops->clr_table(rdev->net->hport, rdev->destid,
+ rdev->hopcount, table);
+
+ module_put(ops->owner);
+ }
+
+ spin_unlock(&rdev->rswitch->lock);
+
+ if (lock)
+ rio_unlock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rio_route_clr_table);
+
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
static bool rio_chan_filter(struct dma_chan *chan, void *arg)
diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h
index c14f864..d595877 100644
--- a/drivers/rapidio/rio.h
+++ b/drivers/rapidio/rio.h
@@ -28,18 +28,17 @@ extern u32 rio_mport_get_efb(struct rio_mport *port, int local, u16 destid,
extern int rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid,
u8 hopcount);
extern int rio_create_sysfs_dev_files(struct rio_dev *rdev);
-extern int rio_std_route_add_entry(struct rio_mport *mport, u16 destid,
- u8 hopcount, u16 table, u16 route_destid,
- u8 route_port);
-extern int rio_std_route_get_entry(struct rio_mport *mport, u16 destid,
- u8 hopcount, u16 table, u16 route_destid,
- u8 *route_port);
-extern int rio_std_route_clr_table(struct rio_mport *mport, u16 destid,
- u8 hopcount, u16 table);
+extern int rio_lock_device(struct rio_mport *port, u16 destid,
+ u8 hopcount, int wait_ms);
+extern int rio_unlock_device(struct rio_mport *port, u16 destid, u8 hopcount);
+extern int rio_route_add_entry(struct rio_dev *rdev,
+ u16 table, u16 route_destid, u8 route_port, int lock);
+extern int rio_route_get_entry(struct rio_dev *rdev, u16 table,
+ u16 route_destid, u8 *route_port, int lock);
+extern int rio_route_clr_table(struct rio_dev *rdev, u16 table, int lock);
extern int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock);
extern struct rio_dev *rio_get_comptag(u32 comp_tag, struct rio_dev *from);
extern int rio_add_device(struct rio_dev *rdev);
-extern void rio_switch_init(struct rio_dev *rdev, int do_enum);
extern int rio_enable_rx_tx_port(struct rio_mport *port, int local, u16 destid,
u8 hopcount, u8 port_num);
extern int rio_register_scan(int mport_id, struct rio_scan *scan_ops);
@@ -51,29 +50,5 @@ extern struct rio_mport *rio_find_mport(int mport_id);
extern struct device_attribute rio_dev_attrs[];
extern struct bus_attribute rio_bus_attrs[];
-extern struct rio_switch_ops __start_rio_switch_ops[];
-extern struct rio_switch_ops __end_rio_switch_ops[];
-
-/* Helpers internal to the RIO core code */
-#define DECLARE_RIO_SWITCH_SECTION(section, name, vid, did, init_hook) \
- static const struct rio_switch_ops __rio_switch_##name __used \
- __section(section) = { vid, did, init_hook };
-
-/**
- * DECLARE_RIO_SWITCH_INIT - Registers switch initialization routine
- * @vid: RIO vendor ID
- * @did: RIO device ID
- * @init_hook: Callback that performs switch-specific initialization
- *
- * Manipulating switch route tables and error management in RIO
- * is switch specific. This registers a switch by vendor and device ID with
- * initialization callback for setting up switch operations and (if required)
- * hardware initialization. A &struct rio_switch_ops is initialized with
- * pointer to the init routine and placed into a RIO-specific kernel section.
- */
-#define DECLARE_RIO_SWITCH_INIT(vid, did, init_hook) \
- DECLARE_RIO_SWITCH_SECTION(.rio_switch_ops, vid##did, \
- vid, did, init_hook)
-
#define RIO_GET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x00ff0000) >> 16))
#define RIO_SET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x000000ff) << 16))
diff --git a/drivers/rapidio/switches/Kconfig b/drivers/rapidio/switches/Kconfig
index 62d4a06..3458415 100644
--- a/drivers/rapidio/switches/Kconfig
+++ b/drivers/rapidio/switches/Kconfig
@@ -2,27 +2,23 @@
# RapidIO switches configuration
#
config RAPIDIO_TSI57X
- bool "IDT Tsi57x SRIO switches support"
- depends on RAPIDIO
+ tristate "IDT Tsi57x SRIO switches support"
---help---
Includes support for IDT Tsi57x family of serial RapidIO switches.
config RAPIDIO_CPS_XX
- bool "IDT CPS-xx SRIO switches support"
- depends on RAPIDIO
+ tristate "IDT CPS-xx SRIO switches support"
---help---
Includes support for IDT CPS-16/12/10/8 serial RapidIO switches.
config RAPIDIO_TSI568
- bool "Tsi568 SRIO switch support"
- depends on RAPIDIO
+ tristate "Tsi568 SRIO switch support"
default n
---help---
Includes support for IDT Tsi568 serial RapidIO switch.
config RAPIDIO_CPS_GEN2
- bool "IDT CPS Gen.2 SRIO switch support"
- depends on RAPIDIO
+ tristate "IDT CPS Gen.2 SRIO switch support"
default n
---help---
Includes support for ITD CPS Gen.2 serial RapidIO switches.
diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c
index 809b7a3..9126d4b 100644
--- a/drivers/rapidio/switches/idt_gen2.c
+++ b/drivers/rapidio/switches/idt_gen2.c
@@ -15,6 +15,7 @@
#include <linux/rio_drv.h>
#include <linux/rio_ids.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include "../rio.h"
#define LOCAL_RTE_CONF_DESTID_SEL 0x010070
@@ -387,12 +388,12 @@ idtg2_show_errlog(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR(errlog, S_IRUGO, idtg2_show_errlog, NULL);
-static int idtg2_sysfs(struct rio_dev *rdev, int create)
+static int idtg2_sysfs(struct rio_dev *rdev, bool create)
{
struct device *dev = &rdev->dev;
int err = 0;
- if (create == RIO_SW_SYSFS_CREATE) {
+ if (create) {
/* Initialize sysfs entries */
err = device_create_file(dev, &dev_attr_errlog);
if (err)
@@ -403,29 +404,90 @@ static int idtg2_sysfs(struct rio_dev *rdev, int create)
return err;
}
-static int idtg2_switch_init(struct rio_dev *rdev, int do_enum)
+static struct rio_switch_ops idtg2_switch_ops = {
+ .owner = THIS_MODULE,
+ .add_entry = idtg2_route_add_entry,
+ .get_entry = idtg2_route_get_entry,
+ .clr_table = idtg2_route_clr_table,
+ .set_domain = idtg2_set_domain,
+ .get_domain = idtg2_get_domain,
+ .em_init = idtg2_em_init,
+ .em_handle = idtg2_em_handler,
+};
+
+static int idtg2_probe(struct rio_dev *rdev, const struct rio_device_id *id)
{
pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
- rdev->rswitch->add_entry = idtg2_route_add_entry;
- rdev->rswitch->get_entry = idtg2_route_get_entry;
- rdev->rswitch->clr_table = idtg2_route_clr_table;
- rdev->rswitch->set_domain = idtg2_set_domain;
- rdev->rswitch->get_domain = idtg2_get_domain;
- rdev->rswitch->em_init = idtg2_em_init;
- rdev->rswitch->em_handle = idtg2_em_handler;
- rdev->rswitch->sw_sysfs = idtg2_sysfs;
-
- if (do_enum) {
+
+ spin_lock(&rdev->rswitch->lock);
+
+ if (rdev->rswitch->ops) {
+ spin_unlock(&rdev->rswitch->lock);
+ return -EINVAL;
+ }
+
+ rdev->rswitch->ops = &idtg2_switch_ops;
+
+ if (rdev->do_enum) {
/* Ensure that default routing is disabled on startup */
rio_write_config_32(rdev,
RIO_STD_RTE_DEFAULT_PORT, IDT_NO_ROUTE);
}
+ /* Create device-specific sysfs attributes */
+ idtg2_sysfs(rdev, true);
+
+ spin_unlock(&rdev->rswitch->lock);
return 0;
}
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1848, idtg2_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1616, idtg2_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTVPS1616, idtg2_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTSPS1616, idtg2_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1432, idtg2_switch_init);
+static void idtg2_remove(struct rio_dev *rdev)
+{
+ pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
+ spin_lock(&rdev->rswitch->lock);
+ if (rdev->rswitch->ops != &idtg2_switch_ops) {
+ spin_unlock(&rdev->rswitch->lock);
+ return;
+ }
+ rdev->rswitch->ops = NULL;
+
+ /* Remove device-specific sysfs attributes */
+ idtg2_sysfs(rdev, false);
+
+ spin_unlock(&rdev->rswitch->lock);
+}
+
+static struct rio_device_id idtg2_id_table[] = {
+ {RIO_DEVICE(RIO_DID_IDTCPS1848, RIO_VID_IDT)},
+ {RIO_DEVICE(RIO_DID_IDTCPS1616, RIO_VID_IDT)},
+ {RIO_DEVICE(RIO_DID_IDTVPS1616, RIO_VID_IDT)},
+ {RIO_DEVICE(RIO_DID_IDTSPS1616, RIO_VID_IDT)},
+ {RIO_DEVICE(RIO_DID_IDTCPS1432, RIO_VID_IDT)},
+ { 0, } /* terminate list */
+};
+
+static struct rio_driver idtg2_driver = {
+ .name = "idt_gen2",
+ .id_table = idtg2_id_table,
+ .probe = idtg2_probe,
+ .remove = idtg2_remove,
+};
+
+static int __init idtg2_init(void)
+{
+ return rio_register_driver(&idtg2_driver);
+}
+
+static void __exit idtg2_exit(void)
+{
+ pr_debug("RIO: %s\n", __func__);
+ rio_unregister_driver(&idtg2_driver);
+ pr_debug("RIO: %s done\n", __func__);
+}
+
+device_initcall(idtg2_init);
+module_exit(idtg2_exit);
+
+MODULE_DESCRIPTION("IDT CPS Gen.2 Serial RapidIO switch family driver");
+MODULE_AUTHOR("Integrated Device Technology, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rapidio/switches/idtcps.c b/drivers/rapidio/switches/idtcps.c
index d06ee2d..7fbb60d 100644
--- a/drivers/rapidio/switches/idtcps.c
+++ b/drivers/rapidio/switches/idtcps.c
@@ -13,6 +13,7 @@
#include <linux/rio.h>
#include <linux/rio_drv.h>
#include <linux/rio_ids.h>
+#include <linux/module.h>
#include "../rio.h"
#define CPS_DEFAULT_ROUTE 0xde
@@ -118,18 +119,31 @@ idtcps_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
return 0;
}
-static int idtcps_switch_init(struct rio_dev *rdev, int do_enum)
+static struct rio_switch_ops idtcps_switch_ops = {
+ .owner = THIS_MODULE,
+ .add_entry = idtcps_route_add_entry,
+ .get_entry = idtcps_route_get_entry,
+ .clr_table = idtcps_route_clr_table,
+ .set_domain = idtcps_set_domain,
+ .get_domain = idtcps_get_domain,
+ .em_init = NULL,
+ .em_handle = NULL,
+};
+
+static int idtcps_probe(struct rio_dev *rdev, const struct rio_device_id *id)
{
pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
- rdev->rswitch->add_entry = idtcps_route_add_entry;
- rdev->rswitch->get_entry = idtcps_route_get_entry;
- rdev->rswitch->clr_table = idtcps_route_clr_table;
- rdev->rswitch->set_domain = idtcps_set_domain;
- rdev->rswitch->get_domain = idtcps_get_domain;
- rdev->rswitch->em_init = NULL;
- rdev->rswitch->em_handle = NULL;
-
- if (do_enum) {
+
+ spin_lock(&rdev->rswitch->lock);
+
+ if (rdev->rswitch->ops) {
+ spin_unlock(&rdev->rswitch->lock);
+ return -EINVAL;
+ }
+
+ rdev->rswitch->ops = &idtcps_switch_ops;
+
+ if (rdev->do_enum) {
/* set TVAL = ~50us */
rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8);
@@ -138,12 +152,52 @@ static int idtcps_switch_init(struct rio_dev *rdev, int do_enum)
RIO_STD_RTE_DEFAULT_PORT, CPS_NO_ROUTE);
}
+ spin_unlock(&rdev->rswitch->lock);
return 0;
}
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS6Q, idtcps_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS8, idtcps_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS10Q, idtcps_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS12, idtcps_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS16, idtcps_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDT70K200, idtcps_switch_init);
+static void idtcps_remove(struct rio_dev *rdev)
+{
+ pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
+ spin_lock(&rdev->rswitch->lock);
+ if (rdev->rswitch->ops != &idtcps_switch_ops) {
+ spin_unlock(&rdev->rswitch->lock);
+ return;
+ }
+ rdev->rswitch->ops = NULL;
+ spin_unlock(&rdev->rswitch->lock);
+}
+
+static struct rio_device_id idtcps_id_table[] = {
+ {RIO_DEVICE(RIO_DID_IDTCPS6Q, RIO_VID_IDT)},
+ {RIO_DEVICE(RIO_DID_IDTCPS8, RIO_VID_IDT)},
+ {RIO_DEVICE(RIO_DID_IDTCPS10Q, RIO_VID_IDT)},
+ {RIO_DEVICE(RIO_DID_IDTCPS12, RIO_VID_IDT)},
+ {RIO_DEVICE(RIO_DID_IDTCPS16, RIO_VID_IDT)},
+ {RIO_DEVICE(RIO_DID_IDT70K200, RIO_VID_IDT)},
+ { 0, } /* terminate list */
+};
+
+static struct rio_driver idtcps_driver = {
+ .name = "idtcps",
+ .id_table = idtcps_id_table,
+ .probe = idtcps_probe,
+ .remove = idtcps_remove,
+};
+
+static int __init idtcps_init(void)
+{
+ return rio_register_driver(&idtcps_driver);
+}
+
+static void __exit idtcps_exit(void)
+{
+ rio_unregister_driver(&idtcps_driver);
+}
+
+device_initcall(idtcps_init);
+module_exit(idtcps_exit);
+
+MODULE_DESCRIPTION("IDT CPS Gen.1 Serial RapidIO switch family driver");
+MODULE_AUTHOR("Integrated Device Technology, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rapidio/switches/tsi568.c b/drivers/rapidio/switches/tsi568.c
index 3994c00..8a43561 100644
--- a/drivers/rapidio/switches/tsi568.c
+++ b/drivers/rapidio/switches/tsi568.c
@@ -19,6 +19,7 @@
#include <linux/rio_drv.h>
#include <linux/rio_ids.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include "../rio.h"
/* Global (broadcast) route registers */
@@ -129,18 +130,70 @@ tsi568_em_init(struct rio_dev *rdev)
return 0;
}
-static int tsi568_switch_init(struct rio_dev *rdev, int do_enum)
+static struct rio_switch_ops tsi568_switch_ops = {
+ .owner = THIS_MODULE,
+ .add_entry = tsi568_route_add_entry,
+ .get_entry = tsi568_route_get_entry,
+ .clr_table = tsi568_route_clr_table,
+ .set_domain = NULL,
+ .get_domain = NULL,
+ .em_init = tsi568_em_init,
+ .em_handle = NULL,
+};
+
+static int tsi568_probe(struct rio_dev *rdev, const struct rio_device_id *id)
{
pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
- rdev->rswitch->add_entry = tsi568_route_add_entry;
- rdev->rswitch->get_entry = tsi568_route_get_entry;
- rdev->rswitch->clr_table = tsi568_route_clr_table;
- rdev->rswitch->set_domain = NULL;
- rdev->rswitch->get_domain = NULL;
- rdev->rswitch->em_init = tsi568_em_init;
- rdev->rswitch->em_handle = NULL;
+ spin_lock(&rdev->rswitch->lock);
+
+ if (rdev->rswitch->ops) {
+ spin_unlock(&rdev->rswitch->lock);
+ return -EINVAL;
+ }
+
+ rdev->rswitch->ops = &tsi568_switch_ops;
+ spin_unlock(&rdev->rswitch->lock);
return 0;
}
-DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI568, tsi568_switch_init);
+static void tsi568_remove(struct rio_dev *rdev)
+{
+ pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
+ spin_lock(&rdev->rswitch->lock);
+ if (rdev->rswitch->ops != &tsi568_switch_ops) {
+ spin_unlock(&rdev->rswitch->lock);
+ return;
+ }
+ rdev->rswitch->ops = NULL;
+ spin_unlock(&rdev->rswitch->lock);
+}
+
+static struct rio_device_id tsi568_id_table[] = {
+ {RIO_DEVICE(RIO_DID_TSI568, RIO_VID_TUNDRA)},
+ { 0, } /* terminate list */
+};
+
+static struct rio_driver tsi568_driver = {
+ .name = "tsi568",
+ .id_table = tsi568_id_table,
+ .probe = tsi568_probe,
+ .remove = tsi568_remove,
+};
+
+static int __init tsi568_init(void)
+{
+ return rio_register_driver(&tsi568_driver);
+}
+
+static void __exit tsi568_exit(void)
+{
+ rio_unregister_driver(&tsi568_driver);
+}
+
+device_initcall(tsi568_init);
+module_exit(tsi568_exit);
+
+MODULE_DESCRIPTION("IDT Tsi568 Serial RapidIO switch driver");
+MODULE_AUTHOR("Integrated Device Technology, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c
index db8b802..42c8b01 100644
--- a/drivers/rapidio/switches/tsi57x.c
+++ b/drivers/rapidio/switches/tsi57x.c
@@ -19,6 +19,7 @@
#include <linux/rio_drv.h>
#include <linux/rio_ids.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include "../rio.h"
/* Global (broadcast) route registers */
@@ -292,27 +293,79 @@ exit_es:
return 0;
}
-static int tsi57x_switch_init(struct rio_dev *rdev, int do_enum)
+static struct rio_switch_ops tsi57x_switch_ops = {
+ .owner = THIS_MODULE,
+ .add_entry = tsi57x_route_add_entry,
+ .get_entry = tsi57x_route_get_entry,
+ .clr_table = tsi57x_route_clr_table,
+ .set_domain = tsi57x_set_domain,
+ .get_domain = tsi57x_get_domain,
+ .em_init = tsi57x_em_init,
+ .em_handle = tsi57x_em_handler,
+};
+
+static int tsi57x_probe(struct rio_dev *rdev, const struct rio_device_id *id)
{
pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
- rdev->rswitch->add_entry = tsi57x_route_add_entry;
- rdev->rswitch->get_entry = tsi57x_route_get_entry;
- rdev->rswitch->clr_table = tsi57x_route_clr_table;
- rdev->rswitch->set_domain = tsi57x_set_domain;
- rdev->rswitch->get_domain = tsi57x_get_domain;
- rdev->rswitch->em_init = tsi57x_em_init;
- rdev->rswitch->em_handle = tsi57x_em_handler;
-
- if (do_enum) {
+
+ spin_lock(&rdev->rswitch->lock);
+
+ if (rdev->rswitch->ops) {
+ spin_unlock(&rdev->rswitch->lock);
+ return -EINVAL;
+ }
+ rdev->rswitch->ops = &tsi57x_switch_ops;
+
+ if (rdev->do_enum) {
/* Ensure that default routing is disabled on startup */
rio_write_config_32(rdev, RIO_STD_RTE_DEFAULT_PORT,
RIO_INVALID_ROUTE);
}
+ spin_unlock(&rdev->rswitch->lock);
return 0;
}
-DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI572, tsi57x_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI574, tsi57x_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI577, tsi57x_switch_init);
-DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI578, tsi57x_switch_init);
+static void tsi57x_remove(struct rio_dev *rdev)
+{
+ pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
+ spin_lock(&rdev->rswitch->lock);
+ if (rdev->rswitch->ops != &tsi57x_switch_ops) {
+ spin_unlock(&rdev->rswitch->lock);
+ return;
+ }
+ rdev->rswitch->ops = NULL;
+ spin_unlock(&rdev->rswitch->lock);
+}
+
+static struct rio_device_id tsi57x_id_table[] = {
+ {RIO_DEVICE(RIO_DID_TSI572, RIO_VID_TUNDRA)},
+ {RIO_DEVICE(RIO_DID_TSI574, RIO_VID_TUNDRA)},
+ {RIO_DEVICE(RIO_DID_TSI577, RIO_VID_TUNDRA)},
+ {RIO_DEVICE(RIO_DID_TSI578, RIO_VID_TUNDRA)},
+ { 0, } /* terminate list */
+};
+
+static struct rio_driver tsi57x_driver = {
+ .name = "tsi57x",
+ .id_table = tsi57x_id_table,
+ .probe = tsi57x_probe,
+ .remove = tsi57x_remove,
+};
+
+static int __init tsi57x_init(void)
+{
+ return rio_register_driver(&tsi57x_driver);
+}
+
+static void __exit tsi57x_exit(void)
+{
+ rio_unregister_driver(&tsi57x_driver);
+}
+
+device_initcall(tsi57x_init);
+module_exit(tsi57x_exit);
+
+MODULE_DESCRIPTION("IDT Tsi57x Serial RapidIO switch family driver");
+MODULE_AUTHOR("Integrated Device Technology, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index eb58d2d..43d3cb3 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -285,13 +285,6 @@
VMLINUX_SYMBOL(__end_builtin_fw) = .; \
} \
\
- /* RapidIO route ops */ \
- .rio_ops : AT(ADDR(.rio_ops) - LOAD_OFFSET) { \
- VMLINUX_SYMBOL(__start_rio_switch_ops) = .; \
- *(.rio_switch_ops) \
- VMLINUX_SYMBOL(__end_rio_switch_ops) = .; \
- } \
- \
TRACEDATA \
\
/* Kernel symbol table: Normal symbols */ \
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 18e0993..fcd492e 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -94,6 +94,23 @@ union rio_pw_msg;
* @switchid: Switch ID that is unique across a network
* @route_table: Copy of switch routing table
* @port_ok: Status of each port (one bit per port) - OK=1 or UNINIT=0
+ * @ops: pointer to switch-specific operations
+ * @lock: lock to serialize operations updates
+ * @nextdev: Array of per-port pointers to the next attached device
+ */
+struct rio_switch {
+ struct list_head node;
+ u16 switchid;
+ u8 *route_table;
+ u32 port_ok;
+ struct rio_switch_ops *ops;
+ spinlock_t lock;
+ struct rio_dev *nextdev[0];
+};
+
+/**
+ * struct rio_switch_ops - Per-switch operations
+ * @owner: The module owner of this structure
* @add_entry: Callback for switch-specific route add function
* @get_entry: Callback for switch-specific route get function
* @clr_table: Callback for switch-specific clear route table function
@@ -101,14 +118,12 @@ union rio_pw_msg;
* @get_domain: Callback for switch-specific domain get function
* @em_init: Callback for switch-specific error management init function
* @em_handle: Callback for switch-specific error management handler function
- * @sw_sysfs: Callback that initializes switch-specific sysfs attributes
- * @nextdev: Array of per-port pointers to the next attached device
+ *
+ * Defines the operations that are necessary to initialize/control
+ * a particular RIO switch device.
*/
-struct rio_switch {
- struct list_head node;
- u16 switchid;
- u8 *route_table;
- u32 port_ok;
+struct rio_switch_ops {
+ struct module *owner;
int (*add_entry) (struct rio_mport *mport, u16 destid, u8 hopcount,
u16 table, u16 route_destid, u8 route_port);
int (*get_entry) (struct rio_mport *mport, u16 destid, u8 hopcount,
@@ -121,8 +136,6 @@ struct rio_switch {
u8 *sw_domain);
int (*em_init) (struct rio_dev *dev);
int (*em_handle) (struct rio_dev *dev, u8 swport);
- int (*sw_sysfs) (struct rio_dev *dev, int create);
- struct rio_dev *nextdev[0];
};
/**
@@ -130,6 +143,7 @@ struct rio_switch {
* @global_list: Node in list of all RIO devices
* @net_list: Node in list of RIO devices in a network
* @net: Network this device is a part of
+ * @do_enum: Enumeration flag
* @did: Device ID
* @vid: Vendor ID
* @device_rev: Device revision
@@ -158,6 +172,7 @@ struct rio_dev {
struct list_head global_list; /* node in list of all RIO devices */
struct list_head net_list; /* node in per net list */
struct rio_net *net; /* RIO net this device resides in */
+ bool do_enum;
u16 did;
u16 vid;
u32 device_rev;
@@ -297,10 +312,6 @@ struct rio_net {
struct rio_id_table destid_table; /* destID allocation table */
};
-/* Definitions used by switch sysfs initialization callback */
-#define RIO_SW_SYSFS_CREATE 1 /* Create switch attributes */
-#define RIO_SW_SYSFS_REMOVE 0 /* Remove switch attributes */
-
/* Low-level architecture-dependent routines */
/**
@@ -400,20 +411,6 @@ struct rio_device_id {
u16 asm_did, asm_vid;
};
-/**
- * struct rio_switch_ops - Per-switch operations
- * @vid: RIO vendor ID
- * @did: RIO device ID
- * @init_hook: Callback that performs switch device initialization
- *
- * Defines the operations that are necessary to initialize/control
- * a particular RIO switch device.
- */
-struct rio_switch_ops {
- u16 vid, did;
- int (*init_hook) (struct rio_dev *rdev, int do_enum);
-};
-
union rio_pw_msg {
struct {
u32 comptag; /* Component Tag CSR */
--
1.7.8.4
^ permalink raw reply related
* [PATCH 2/7] rapidio/rionet: rework driver initialization and removal
From: Alexandre Bounine @ 2013-06-28 19:18 UTC (permalink / raw)
To: Andrew Morton, linux-kernel, linuxppc-dev
Cc: Stef van Os, Micha Nelissen, Andre van Herk, Alexandre Bounine,
David S. Miller, Jean Delvare
In-Reply-To: <1372447136-5751-1-git-send-email-alexandre.bounine@idt.com>
Rework probe/remove routines to prevent rionet driver from monopolizing
target RapidIO devices. Fix conflict with modular RapidIO switch drivers.
Using one of RapidIO messaging channels rionet driver provides a service layer
common to all endpoint devices in a system's RapidIO network. These devices may
also require their own specific device driver which will be blocked from
attaching to the target device by rionet (or block rionet if loaded earlier).
To avoid conflict with device-specific drivers, the rionet driver is reworked
to be registered as a subsystem interface on the RapidIO bus.
The reworked rio_remove_dev() and rionet_exit() routines also include handling
of individual rionet peer device removal which was not supported before.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Andre van Herk <andre.van.herk@Prodrive.nl>
Cc: Micha Nelissen <micha.nelissen@Prodrive.nl>
Cc: Stef van Os <stef.van.os@Prodrive.nl>
Cc: Jean Delvare <jdelvare@suse.de>
---
drivers/net/rionet.c | 103 +++++++++++++++++++++++++++++++++++++------------
1 files changed, 78 insertions(+), 25 deletions(-)
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index f433b59..6d1f6ed 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -208,6 +208,17 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
if (nets[rnet->mport->id].active[destid])
rionet_queue_tx_msg(skb, ndev,
nets[rnet->mport->id].active[destid]);
+ else {
+ /*
+ * If the target device was removed from the list of
+ * active peers but we still have TX packets targeting
+ * it just report sending a packet to the target
+ * (without actual packet transfer).
+ */
+ dev_kfree_skb_any(skb);
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ }
}
spin_unlock_irqrestore(&rnet->tx_lock, flags);
@@ -385,24 +396,28 @@ static int rionet_close(struct net_device *ndev)
return 0;
}
-static void rionet_remove(struct rio_dev *rdev)
+static int rionet_remove_dev(struct device *dev, struct subsys_interface *sif)
{
- struct net_device *ndev = rio_get_drvdata(rdev);
+ struct rio_dev *rdev = to_rio_dev(dev);
unsigned char netid = rdev->net->hport->id;
struct rionet_peer *peer, *tmp;
- unregister_netdev(ndev);
-
- free_pages((unsigned long)nets[netid].active, get_order(sizeof(void *) *
- RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size)));
- nets[netid].active = NULL;
+ if (dev_rionet_capable(rdev)) {
+ list_for_each_entry_safe(peer, tmp, &nets[netid].peers, node) {
+ if (peer->rdev == rdev) {
+ if (nets[netid].active[rdev->destid]) {
+ nets[netid].active[rdev->destid] = NULL;
+ nets[netid].nact--;
+ }
- list_for_each_entry_safe(peer, tmp, &nets[netid].peers, node) {
- list_del(&peer->node);
- kfree(peer);
+ list_del(&peer->node);
+ kfree(peer);
+ break;
+ }
+ }
}
- free_netdev(ndev);
+ return 0;
}
static void rionet_get_drvinfo(struct net_device *ndev,
@@ -503,12 +518,13 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
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)
+static int rionet_add_dev(struct device *dev, struct subsys_interface *sif)
{
int rc = -ENODEV;
u32 lsrc_ops, ldst_ops;
struct rionet_peer *peer;
struct net_device *ndev = NULL;
+ struct rio_dev *rdev = to_rio_dev(dev);
unsigned char netid = rdev->net->hport->id;
int oldnet;
@@ -518,8 +534,9 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)
oldnet = test_and_set_bit(netid, net_table);
/*
- * First time through, make sure local device is rionet
- * capable, setup netdev (will be skipped on later probes)
+ * If first time through this net, make sure local device is rionet
+ * capable and setup netdev (this step will be skipped in later probes
+ * on the same net).
*/
if (!oldnet) {
rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR,
@@ -541,6 +558,12 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)
}
nets[netid].ndev = ndev;
rc = rionet_setup_netdev(rdev->net->hport, ndev);
+ if (rc) {
+ printk(KERN_ERR "%s: failed to setup netdev (rc=%d)\n",
+ DRV_NAME, rc);
+ goto out;
+ }
+
INIT_LIST_HEAD(&nets[netid].peers);
nets[netid].nact = 0;
} else if (nets[netid].ndev == NULL)
@@ -559,31 +582,61 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)
list_add_tail(&peer->node, &nets[netid].peers);
}
- rio_set_drvdata(rdev, nets[netid].ndev);
-
- out:
+ return 0;
+out:
return rc;
}
+#ifdef MODULE
static struct rio_device_id rionet_id_table[] = {
- {RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)}
+ {RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)},
+ { 0, } /* terminate list */
};
-static struct rio_driver rionet_driver = {
- .name = "rionet",
- .id_table = rionet_id_table,
- .probe = rionet_probe,
- .remove = rionet_remove,
+MODULE_DEVICE_TABLE(rapidio, rionet_id_table);
+#endif
+
+static struct subsys_interface rionet_interface = {
+ .name = "rionet",
+ .subsys = &rio_bus_type,
+ .add_dev = rionet_add_dev,
+ .remove_dev = rionet_remove_dev,
};
static int __init rionet_init(void)
{
- return rio_register_driver(&rionet_driver);
+ return subsys_interface_register(&rionet_interface);
}
static void __exit rionet_exit(void)
{
- rio_unregister_driver(&rionet_driver);
+ struct rionet_private *rnet;
+ struct net_device *ndev;
+ struct rionet_peer *peer, *tmp;
+ int i;
+
+ for (i = 0; i < RIONET_MAX_NETS; i++) {
+ if (nets[i].ndev != NULL) {
+ ndev = nets[i].ndev;
+ rnet = netdev_priv(ndev);
+ unregister_netdev(ndev);
+
+ list_for_each_entry_safe(peer,
+ tmp, &nets[i].peers, node) {
+ list_del(&peer->node);
+ kfree(peer);
+ }
+
+ free_pages((unsigned long)nets[i].active,
+ get_order(sizeof(void *) *
+ RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size)));
+ nets[i].active = NULL;
+
+ free_netdev(ndev);
+ }
+ }
+
+ subsys_interface_unregister(&rionet_interface);
}
late_initcall(rionet_init);
--
1.7.8.4
^ permalink raw reply related
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