* [PATCH 06/11] powerpc/ppc64e: Fix link problem when building ppc64e_defconfig
From: Kumar Gala @ 2010-10-08 19:06 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1286564813-21209-5-git-send-email-galak@kernel.crashing.org>
arch/powerpc/platforms/built-in.o:(.toc1+0x18): undefined reference to `__early_start'
This is due to the 85xx/smp.c not handling the 64-bit side properly. We
need to set the entry point for secondary cores on ppc64e to
generic_secondary_smp_init instead of __early_start that we due on ppc32.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/platforms/85xx/smp.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c
index c89a370..5c91a99 100644
--- a/arch/powerpc/platforms/85xx/smp.c
+++ b/arch/powerpc/platforms/85xx/smp.c
@@ -80,6 +80,7 @@ smp_85xx_kick_cpu(int nr)
local_irq_save(flags);
out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr);
+#ifdef CONFIG_PPC32
out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start));
if (!ioremappable)
@@ -89,6 +90,12 @@ smp_85xx_kick_cpu(int nr)
/* Wait a bit for the CPU to ack. */
while ((__secondary_hold_acknowledge != nr) && (++n < 1000))
mdelay(1);
+#else
+ out_be64((u64 *)(bptr_vaddr + BOOT_ENTRY_ADDR_UPPER),
+ __pa((u64)*((unsigned long long *) generic_secondary_smp_init)));
+
+ smp_generic_kick_cpu(nr);
+#endif
local_irq_restore(flags);
--
1.7.2.3
^ permalink raw reply related
* [PATCH 05/11] powerpc: Fix compile error with paca code on ppc64e
From: Kumar Gala @ 2010-10-08 19:06 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1286564813-21209-4-git-send-email-galak@kernel.crashing.org>
arch/powerpc/kernel/paca.c: In function 'allocate_lppacas':
arch/powerpc/kernel/paca.c:111:1: error: parameter name omitted
arch/powerpc/kernel/paca.c:111:1: error: parameter name omitted
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/kernel/paca.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c
index 1e068a4..cefc0df 100644
--- a/arch/powerpc/kernel/paca.c
+++ b/arch/powerpc/kernel/paca.c
@@ -108,7 +108,7 @@ static void free_lppacas(void)
#else
-static inline void allocate_lppacas(int, unsigned long) { }
+static inline void allocate_lppacas(int nr_cpus, unsigned long limit) { }
static inline void free_lppacas(void) { }
#endif /* CONFIG_PPC_BOOK3S */
--
1.7.2.3
^ permalink raw reply related
* [PATCH 04/11] powerpc/fsl-booke: Add p3041 DS board support
From: Kumar Gala @ 2010-10-08 19:06 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1286564813-21209-3-git-send-email-galak@kernel.crashing.org>
The P3041DS is in the same family of boards as the P4080DS and thus
shares the corenet_ds code.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/platforms/85xx/Kconfig | 12 ++++++
arch/powerpc/platforms/85xx/Makefile | 1 +
arch/powerpc/platforms/85xx/p3041_ds.c | 64 ++++++++++++++++++++++++++++++++
3 files changed, 77 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/platforms/85xx/p3041_ds.c
diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig
index bea1f59..5bde1f2 100644
--- a/arch/powerpc/platforms/85xx/Kconfig
+++ b/arch/powerpc/platforms/85xx/Kconfig
@@ -153,6 +153,18 @@ config SBC8560
help
This option enables support for the Wind River SBC8560 board
+config P3041_DS
+ bool "Freescale P3041 DS"
+ select DEFAULT_UIMAGE
+ select PPC_FSL_BOOK3E
+ select PPC_E500MC
+ select PHYS_64BIT
+ select SWIOTLB
+ select MPC8xxx_GPIO
+ select HAS_RAPIDIO
+ help
+ This option enables support for the P3041 DS board
+
config P4080_DS
bool "Freescale P4080 DS"
select DEFAULT_UIMAGE
diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index a2ec3f8..c3ac071 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_MPC85xx_DS) += mpc85xx_ds.o
obj-$(CONFIG_MPC85xx_MDS) += mpc85xx_mds.o
obj-$(CONFIG_MPC85xx_RDB) += mpc85xx_rdb.o
obj-$(CONFIG_P1022_DS) += p1022_ds.o
+obj-$(CONFIG_P3041_DS) += p3041_ds.o corenet_ds.o
obj-$(CONFIG_P4080_DS) += p4080_ds.o corenet_ds.o
obj-$(CONFIG_STX_GP3) += stx_gp3.o
obj-$(CONFIG_TQM85xx) += tqm85xx.o
diff --git a/arch/powerpc/platforms/85xx/p3041_ds.c b/arch/powerpc/platforms/85xx/p3041_ds.c
new file mode 100644
index 0000000..0ed52e1
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/p3041_ds.c
@@ -0,0 +1,64 @@
+/*
+ * P3041 DS Setup
+ *
+ * Maintained by Kumar Gala (see MAINTAINERS for contact information)
+ *
+ * Copyright 2009-2010 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/phy.h>
+
+#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <mm/mmu_decl.h>
+#include <asm/prom.h>
+#include <asm/udbg.h>
+#include <asm/mpic.h>
+
+#include <linux/of_platform.h>
+#include <sysdev/fsl_soc.h>
+#include <sysdev/fsl_pci.h>
+
+#include "corenet_ds.h"
+
+/*
+ * Called very early, device-tree isn't unflattened
+ */
+static int __init p3041_ds_probe(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ return of_flat_dt_is_compatible(root, "fsl,P3041DS");
+}
+
+define_machine(p3041_ds) {
+ .name = "P3041 DS",
+ .probe = p3041_ds_probe,
+ .setup_arch = corenet_ds_setup_arch,
+ .init_IRQ = corenet_ds_pic_init,
+#ifdef CONFIG_PCI
+ .pcibios_fixup_bus = fsl_pcibios_fixup_bus,
+#endif
+ .get_irq = mpic_get_coreint_irq,
+ .restart = fsl_rstcr_restart,
+ .calibrate_decr = generic_calibrate_decr,
+ .progress = udbg_progress,
+};
+
+machine_device_initcall(p3041_ds, corenet_ds_publish_devices);
+
+#ifdef CONFIG_SWIOTLB
+machine_arch_initcall(p3041_ds, swiotlb_setup_bus_notifier);
+#endif
--
1.7.2.3
^ permalink raw reply related
* [PATCH 02/11] oprofile/fsl emb: Don't set MSR[PMM] until after clearing the interrupt.
From: Kumar Gala @ 2010-10-08 19:06 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Scott Wood
In-Reply-To: <1286564813-21209-1-git-send-email-galak@kernel.crashing.org>
From: Scott Wood <scottwood@freescale.com>
On an arch 2.06 hypervisor, a pending perfmon interrupt will be delivered
to the hypervisor at any point the guest is running, regardless of
MSR[EE]. In order to reflect this interrupt, the hypervisor has to mask
the interrupt in PMGC0 -- and set MSRP[PMMP] to intercept futher guest
accesses to the PMRs to detect when to unmask (and prevent the guest from
unmasking early, or seeing inconsistent state).
This has the side effect of ignoring any changes the guest makes to
MSR[PMM], so wait until after the interrupt is clear, and thus the
hypervisor should have cleared MSRP[PMMP], before setting MSR[PMM]. The
counters wil not actually run until PMGC0[FAC] is cleared in
pmc_start_ctrs(), so this will not reduce the effectiveness of PMM.
Signed-off-by: Scott Wood <scottwood@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/oprofile/op_model_fsl_emb.c | 15 ++++++++-------
1 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/oprofile/op_model_fsl_emb.c b/arch/powerpc/oprofile/op_model_fsl_emb.c
index 62312ab..d4e6507 100644
--- a/arch/powerpc/oprofile/op_model_fsl_emb.c
+++ b/arch/powerpc/oprofile/op_model_fsl_emb.c
@@ -2,7 +2,7 @@
* Freescale Embedded oprofile support, based on ppc64 oprofile support
* Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
*
- * Copyright (c) 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2004, 2010 Freescale Semiconductor, Inc
*
* Author: Andy Fleming
* Maintainer: Kumar Gala <galak@kernel.crashing.org>
@@ -321,9 +321,6 @@ static void fsl_emb_handle_interrupt(struct pt_regs *regs,
int val;
int i;
- /* set the PMM bit (see comment below) */
- mtmsr(mfmsr() | MSR_PMM);
-
pc = regs->nip;
is_kernel = is_kernel_addr(pc);
@@ -340,9 +337,13 @@ static void fsl_emb_handle_interrupt(struct pt_regs *regs,
}
/* The freeze bit was set by the interrupt. */
- /* Clear the freeze bit, and reenable the interrupt.
- * The counters won't actually start until the rfi clears
- * the PMM bit */
+ /* Clear the freeze bit, and reenable the interrupt. The
+ * counters won't actually start until the rfi clears the PMM
+ * bit. The PMM bit should not be set until after the interrupt
+ * is cleared to avoid it getting lost in some hypervisor
+ * environments.
+ */
+ mtmsr(mfmsr() | MSR_PMM);
pmc_start_ctrs(1);
}
--
1.7.2.3
^ permalink raw reply related
* [PATCH 03/11] powerpc/fsl-booke: Add PCI device ids for P2040/P3041/P5010/P5020 QoirQ chips
From: Kumar Gala @ 2010-10-08 19:06 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1286564813-21209-2-git-send-email-galak@kernel.crashing.org>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/sysdev/fsl_pci.c | 8 ++++++++
include/linux/pci_ids.h | 8 ++++++++
2 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 32012a2..4d0b249 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -417,10 +417,18 @@ DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P2010E, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P2010, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P2020E, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P2020, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P2040E, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P2040, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P3041E, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P3041, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P4040E, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P4040, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P4080E, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P4080, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P5010E, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P5010, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P5020E, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_P5020, quirk_fsl_pcie_header);
#endif /* CONFIG_FSL_SOC_BOOKE || CONFIG_PPC_86xx */
#if defined(CONFIG_PPC_83xx) || defined(CONFIG_PPC_MPC512x)
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 10d3330..cded8ed 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2312,6 +2312,14 @@
#define PCI_DEVICE_ID_P4080 0x0401
#define PCI_DEVICE_ID_P4040E 0x0408
#define PCI_DEVICE_ID_P4040 0x0409
+#define PCI_DEVICE_ID_P2040E 0x0410
+#define PCI_DEVICE_ID_P2040 0x0411
+#define PCI_DEVICE_ID_P3041E 0x041E
+#define PCI_DEVICE_ID_P3041 0x041F
+#define PCI_DEVICE_ID_P5020E 0x0420
+#define PCI_DEVICE_ID_P5020 0x0421
+#define PCI_DEVICE_ID_P5010E 0x0428
+#define PCI_DEVICE_ID_P5010 0x0429
#define PCI_DEVICE_ID_MPC8641 0x7010
#define PCI_DEVICE_ID_MPC8641D 0x7011
#define PCI_DEVICE_ID_MPC8610 0x7018
--
1.7.2.3
^ permalink raw reply related
* [PATCH 01/11] powerpc/mpc8xxx_gpio: Add support for 'qoriq-gpio' controllers
From: Kumar Gala @ 2010-10-08 19:06 UTC (permalink / raw)
To: linuxppc-dev
Add 'fsl,qoriq-gpio' compatiable to the list we search for to bind
against for mpc8xxx_gpio. This compatiable will be used on P1-P5xxx
QorIQ devices like P4080.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/sysdev/mpc8xxx_gpio.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/sysdev/mpc8xxx_gpio.c b/arch/powerpc/sysdev/mpc8xxx_gpio.c
index 2b69084..c0ea05e 100644
--- a/arch/powerpc/sysdev/mpc8xxx_gpio.c
+++ b/arch/powerpc/sysdev/mpc8xxx_gpio.c
@@ -330,6 +330,9 @@ static int __init mpc8xxx_add_gpiochips(void)
for_each_compatible_node(np, NULL, "fsl,mpc8610-gpio")
mpc8xxx_add_controller(np);
+ for_each_compatible_node(np, NULL, "fsl,qoriq-gpio")
+ mpc8xxx_add_controller(np);
+
return 0;
}
arch_initcall(mpc8xxx_add_gpiochips);
--
1.7.2.3
^ permalink raw reply related
* Re: [PATCH 1/3] Powerpc/4xx: Add suspend and idle support
From: Victor Gallardo @ 2010-10-08 17:32 UTC (permalink / raw)
To: Josh Boyer; +Cc: linuxppc-dev
In-Reply-To: <20101008134600.GB10590@zod.rchland.ibm.com>
On Fri, Oct 8, 2010 at 6:46 AM, Josh Boyer <jwboyer@linux.vnet.ibm.com> wro=
te:
> On Thu, Oct 07, 2010 at 12:07:16PM -0700, Victor Gallardo wrote:
>
> I hit send too soon on my earlier response. =A0My apologies.
>
>>--- /dev/null
>>+++ b/arch/powerpc/sysdev/ppc4xx_cpm.c
>>@@ -0,0 +1,339 @@
>>+/*
>>+ * PowerPC 4xx Clock and Power Management
>>+ *
>>+ * Copyright (C) 2010, Applied Micro Circuits Corporation
>>+ * Victor Gallardo (vgallardo@apm.com)
>>+ *
>>+ * Based on arch/powerpc/platforms/44x/idle.c:
>>+ * Jerone Young <jyoung5@us.ibm.com>
>>+ * Copyright 2008 IBM Corp.
>>+ *
>>+ * Based on arch/powerpc/sysdev/fsl_pmc.c:
>>+ * Anton Vorontsov <avorontsov@ru.mvista.com>
>>+ * Copyright 2009 =A0MontaVista Software, Inc.
>>+ *
>>+ * See file CREDITS for list of people who contributed to this
>>+ * project.
>>+ *
>>+ * This program is free software; you can redistribute it and/or
>>+ * modify it under the terms of the GNU General Public License as
>>+ * published by the Free Software Foundation; either version 2 of
>>+ * the License, or (at your option) any later version.
>>+ *
>>+ * This program is distributed in the hope that it will be useful,
>>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>+ * GNU General Public License for more details.
>>+ *
>>+ * You should have received a copy of the GNU General Public License
>>+ * along with this program; if not, write to the Free Software
>>+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
>>+ * MA 02111-1307 USA
>>+ */
>>+
>>+#include <linux/kernel.h>
>>+#include <linux/of_platform.h>
>>+#include <linux/sysfs.h>
>>+#include <linux/cpu.h>
>>+#include <linux/suspend.h>
>>+#include <asm/dcr.h>
>>+#include <asm/dcr-native.h>
>>+#include <asm/machdep.h>
>>+
>>+#define CPM_ER =A0 =A0 =A0 =A00
>>+#define CPM_FR =A0 =A0 =A0 =A01
>>+#define CPM_SR =A0 =A0 =A0 =A02
>>+
>>+#define CPM_IDLE_WAIT 0
>>+#define CPM_IDLE_DOZE 1
>>+
>>+struct cpm {
>>+ =A0 =A0 =A0dcr_host_t =A0 =A0 =A0dcr_host;
>>+ =A0 =A0 =A0unsigned int =A0 =A0dcr_offset[3];
>>+ =A0 =A0 =A0unsigned int =A0 =A0powersave_off;
>>+ =A0 =A0 =A0unsigned int =A0 =A0unused;
>>+ =A0 =A0 =A0unsigned int =A0 =A0idle_doze;
>>+ =A0 =A0 =A0unsigned int =A0 =A0standby;
>>+ =A0 =A0 =A0unsigned int =A0 =A0suspend;
>>+};
>>+
>>+static struct cpm cpm;
>>+
>>+struct cpm_idle_mode {
>>+ =A0 =A0 =A0unsigned int enabled;
>>+ =A0 =A0 =A0const char =A0*name;
>>+};
>>+
>>+static struct cpm_idle_mode idle_mode[] =3D {
>>+ =A0 =A0 =A0[CPM_IDLE_WAIT] =3D { 1, "wait" }, /* default */
>>+ =A0 =A0 =A0[CPM_IDLE_DOZE] =3D { 0, "doze" },
>>+};
>>+
>>+static void cpm_set(unsigned int cpm_reg, unsigned int mask)
>>+{
>>+ =A0 =A0 =A0unsigned int value;
>>+
>>+ =A0 =A0 =A0value =3D dcr_read(cpm.dcr_host, cpm.dcr_offset[cpm_reg]);
>>+ =A0 =A0 =A0value |=3D mask;
>>+ =A0 =A0 =A0dcr_write(cpm.dcr_host, cpm.dcr_offset[cpm_reg], value);
>>+}
>
> This doesn't seem to do any sort of verification that the class 2 and
> class 3 devices actually got disabled. =A0Or at least I don't see where w=
e
> verify anything in the SR.
>
> Maybe in practice it doesn't matter, but we should add a comment that say
> we just expect them to eventually go off when they can.
>
Yes, you are correct. I will add a comment here.
>>+
>>+static void cpm_idle_wait(void)
>>+{
>>+ =A0 =A0 =A0unsigned long msr_save;
>>+
>>+ =A0 =A0 =A0/* save off initial state */
>>+ =A0 =A0 =A0msr_save =3D mfmsr();
>>+ =A0 =A0 =A0/* sync required when CPM0_ER[CPU] is set */
>>+ =A0 =A0 =A0mb();
>>+ =A0 =A0 =A0/* set wait state MSR */
>>+ =A0 =A0 =A0mtmsr(msr_save|MSR_WE|MSR_EE|MSR_CE|MSR_DE);
>>+ =A0 =A0 =A0isync();
>>+ =A0 =A0 =A0/* return to initial state */
>>+ =A0 =A0 =A0mtmsr(msr_save);
>>+ =A0 =A0 =A0isync();
>>+}
>>+
>>+static void cpm_idle_sleep(unsigned int mask)
>>+{
>>+ =A0 =A0 =A0unsigned int er_save;
>>+
>>+ =A0 =A0 =A0/* update CPM_ER state */
>>+ =A0 =A0 =A0er_save =3D dcr_read(cpm.dcr_host, cpm.dcr_offset[CPM_ER]);
>>+ =A0 =A0 =A0dcr_write(cpm.dcr_host, cpm.dcr_offset[CPM_ER],
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0er_save | mask);
>>+
>>+ =A0 =A0 =A0/* go to wait state */
>>+ =A0 =A0 =A0cpm_idle_wait();
>>+
>>+ =A0 =A0 =A0/* restore CPM_ER state */
>>+ =A0 =A0 =A0dcr_write(cpm.dcr_host, cpm.dcr_offset[CPM_ER], er_save);
>>+}
>
> For my clarification, the CPU is a class 2 device and we expect that the
> logic between the CPU and the CPM allows us to do the write to make it
> sleep, but not actually sleep until we set the idle state?
>
Yes, you are correct again. I will add another comment here to a little
more clear.
> josh
>
^ permalink raw reply
* Re: [PATCH 1/3] Powerpc/4xx: Add suspend and idle support
From: Victor Gallardo @ 2010-10-08 17:30 UTC (permalink / raw)
To: Josh Boyer; +Cc: linuxppc-dev
In-Reply-To: <20101008133511.GA10590@zod.rchland.ibm.com>
Hi Josh,
On Fri, Oct 8, 2010 at 6:35 AM, Josh Boyer <jwboyer@linux.vnet.ibm.com> wro=
te:
> On Thu, Oct 07, 2010 at 12:07:16PM -0700, Victor Gallardo wrote:
>>Add suspend/resume support for all 4xx compatible CPUs.
>>See /sys/power/state for available power states configured in.
>>
>>Add two different idle states (idle-wait and idle-doze)
>>controlled via sysfs. Default is idle-wait.
>> =A0 =A0 =A0 cat /sys/devices/system/cpu/cpu0/idle
>> =A0 =A0 =A0 [wait] doze
>>
>>To save additional power, use idle-doze.
>> =A0 =A0 =A0 echo doze > /sys/devices/system/cpu/cpu0/idle
>> =A0 =A0 =A0 cat /sys/devices/system/cpu/cpu0/idle
>> =A0 =A0 =A0 wait [doze]
>>
>>Signed-off-by: Victor Gallardo <vgallardo@apm.com>
>>---
>> Documentation/powerpc/dts-bindings/4xx/cpm.txt | =A0 43 +++
>> arch/powerpc/Kconfig =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
| =A0 13 +-
>> arch/powerpc/platforms/44x/Makefile =A0 =A0 =A0 =A0 =A0 =A0| =A0 =A05 +-
>> arch/powerpc/sysdev/Makefile =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 | =A0 =
=A01 +
>> arch/powerpc/sysdev/ppc4xx_cpm.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 | =A0339 ++=
++++++++++++++++++++++
>> 5 files changed, 397 insertions(+), 4 deletions(-)
>> create mode 100644 Documentation/powerpc/dts-bindings/4xx/cpm.txt
>> create mode 100644 arch/powerpc/sysdev/ppc4xx_cpm.c
>>
>>diff --git a/Documentation/powerpc/dts-bindings/4xx/cpm.txt b/Documentati=
on/powerpc/dts-bindings/4xx/cpm.txt
>>new file mode 100644
>>index 0000000..9635df8
>>--- /dev/null
>>+++ b/Documentation/powerpc/dts-bindings/4xx/cpm.txt
>>@@ -0,0 +1,43 @@
>>+PPC4xx Clock Power Management (CPM) node
>>+
>>+Required properties:
>>+ =A0 =A0 =A0- compatible =A0 =A0 =A0 =A0 =A0 =A0: compatible list, curre=
ntly only "ibm,cpm"
>>+ =A0 =A0 =A0- dcr-access-method =A0 =A0 : "native"
>>+ =A0 =A0 =A0- dcr-reg =A0 =A0 =A0 =A0 =A0 =A0 =A0 : < DCR register range=
>
>>+
>>+Optional properties:
>>+ =A0 =A0 =A0- er-offset =A0 =A0 =A0 =A0 =A0 =A0 : All 4xx SoCs with a CP=
M controller have
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0one of t=
wo different order for the CPM
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0register=
s. Some have the CPM registers
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0in the f=
ollowing order (ER,FR,SR). The
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0others h=
ave them in the following order
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(SR,ER,F=
R). For the second case set
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0er-offse=
t =3D <1>.
>>+ =A0 =A0 =A0- unused-units =A0 =A0 =A0 =A0 =A0: specifier consist of one=
cell. For each
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0bit in t=
he cell, the corresponding bit
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0in CPM w=
ill be set to turn off unused
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0devices.
>>+ =A0 =A0 =A0- idle-doze =A0 =A0 =A0 =A0 =A0 =A0 : specifier consist of o=
ne cell. For each
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0bit in t=
he cell, the corresponding bit
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0in CPM w=
ill be set to turn off unused
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0devices.=
This is usually just CPM[CPU].
>>+ =A0 =A0 =A0- standby =A0 =A0 =A0 =A0 =A0 =A0 =A0 : specifier consist of=
one cell. For each
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0bit in t=
he cell, the corresponding bit
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0in CPM w=
ill be set on standby and
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0restored=
on resume.
>>+ =A0 =A0 =A0- suspend =A0 =A0 =A0 =A0 =A0 =A0 =A0 : specifier consist of=
one cell. For each
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0bit in t=
he cell, the corresponding bit
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0in CPM w=
ill be set on suspend (mem) and
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0restored=
on resume.
>
> So the difference, from what I can tell, between standby and suspend is
> really only what devices are turned off. =A0I don't see any code to put
> the DRAM into self-refresh mode, etc. =A0If that is the case, perhaps we
> could add a bit of description as to the different kinds of devices that
> may be disabled in one mode but not the other.
You are correct for this patch. I will introduce putting DDR in
self-refresh in a following patch for specific processors.
>
>>+
>>+Example:
>>+ =A0 =A0 =A0 =A0CPM0: cpm {
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0compatible =3D "ibm,cpm";
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0dcr-access-method =3D "native";
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0dcr-reg =3D <0x160 0x003>;
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0er-offset =3D <0>;
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0unused-units =3D <0x00000100>;
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0idle-doze =3D <0x02000000>;
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0standby =3D <0xfeff0000>;
>>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0standby =3D <0xfeff791d>;
>
> One of these two should be illustrating suspend.
Yes, you are correct. I will fix that.
-Victor Gallardo
^ permalink raw reply
* [RFC PATCH 10/11] ppc/iommu: add routines to pseries iommu to map tces 1-1
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc; +Cc: linux-kernel, miltonm, Paul Mackerras, Anton Blanchard,
linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
arch/powerpc/platforms/pseries/iommu.c | 98 ++++++++++++++++++++++++++++++++
1 files changed, 98 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 8ec81df..451d2d1 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -270,6 +270,104 @@ static unsigned long tce_get_pSeriesLP(struct iommu_table *tbl, long tcenum)
return tce_ret;
}
+/* this is compatable with cells for the device tree property */
+struct dynamic_dma_window_prop {
+ __be32 liobn; /* tce table number */
+ __be32 dma_base[2]; /* address hi,lo */
+ __be32 tce_shift; /* ilog2(tce_page_size) */
+ __be32 window_shift; /* ilog2(tce_window_size) */
+};
+
+static int tce_clearrange_multi_pSeriesLP(unsigned long start_pfn,
+ unsigned long num_pfn, void *arg)
+{
+ struct dynamic_dma_window_prop *maprange = arg;
+ int rc;
+ u64 tce_size, num_tce, dma_offset;
+ u32 tce_shift;
+
+ tce_shift = be32_to_cpu(maprange->tce_shift);
+ tce_size = 1ULL << tce_shift;
+ num_tce = num_pfn << PAGE_SHIFT;
+ dma_offset = start_pfn << PAGE_SHIFT;
+
+ /* round back to the beginning of the tce page size */
+ num_tce += dma_offset & (tce_size - 1);
+ dma_offset &= ~(tce_size - 1);
+
+ /* covert to number of tces */
+ num_tce |= tce_size - 1;
+ num_tce >>= tce_shift;
+
+ rc = plpar_tce_stuff(maprange->liobn, dma_offset, 0, num_tce);
+
+ return rc;
+}
+
+static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn,
+ unsigned long num_pfn, void *arg)
+{
+ struct dynamic_dma_window_prop *maprange = arg;
+ u64 *tcep, tce_size, num_tce, dma_offset, next, proto_tce;
+ u32 tce_shift;
+ long rc = 0;
+ long l, limit;
+
+ local_irq_disable(); /* to protect tcep and the page behind it */
+ tcep = __get_cpu_var(tce_page);
+
+ if (!tcep) {
+ tcep = (u64 *)__get_free_page(GFP_ATOMIC);
+ if (!tcep) {
+ local_irq_enable();
+ return -ENOMEM;
+ }
+ __get_cpu_var(tce_page) = tcep;
+ }
+
+ proto_tce = TCE_PCI_READ | TCE_PCI_WRITE;
+
+ tce_shift = be32_to_cpu(maprange->tce_shift);
+ tce_size = 1ULL << tce_shift;
+ next = start_pfn << PAGE_SHIFT;
+ num_tce = num_pfn << PAGE_SHIFT;
+
+ /* round back to the beginning of the tce page size */
+ num_tce += next & (tce_size - 1);
+ next &= ~(tce_size - 1);
+
+ /* covert to number of tces */
+ num_tce |= tce_size - 1;
+ num_tce >>= maprange->tce_shift;
+
+ /* We can map max one pageful of TCEs at a time */
+ do {
+ /*
+ * Set up the page with TCE data, looping through and setting
+ * the values.
+ */
+ limit = min_t(long, num_tce, 4096/TCE_ENTRY_SIZE);
+ dma_offset = next;
+
+ for (l = 0; l < limit; l++) {
+ tcep[l] = proto_tce | dma_offset;
+ next += tce_size;
+ }
+
+ rc = plpar_tce_put_indirect((u64)maprange->liobn,
+ (u64)dma_offset,
+ (u64)virt_to_abs(tcep),
+ limit);
+
+ num_tce -= limit;
+ } while (num_tce > 0 && !rc);
+
+ /* error cleanup: caller will clear whole range */
+
+ local_irq_enable();
+ return rc;
+}
+
#ifdef CONFIG_PCI
static void iommu_table_setparms(struct pci_controller *phb,
struct device_node *dn,
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 09/11] ppc/iommu: pass phb only to iommu_table_setparms_lpar
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc; +Cc: linux-kernel, miltonm, Paul Mackerras, Anton Blanchard,
linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
iommu_table_setparms_lpar needs either the phb or the subbusnumber
(not both), pass the phb to make it similar to iommu_table_setparms.
Note: In cases where a caller was passing bus->number previously to
iommu_table_setparms_lpar() rather than phb->bus->number, this can lead
to a different value in tbl->it_busno. The only example of this was the
removed pci_dma_dev_setup_pSeriesLP(), removed in "ppc/iommu: remove
unneeded pci_dma_dev_setup_pSeriesLP".
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
arch/powerpc/platforms/pseries/iommu.c | 8 +++-----
1 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index d8bb9be..8ec81df 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -323,14 +323,13 @@ static void iommu_table_setparms(struct pci_controller *phb,
static void iommu_table_setparms_lpar(struct pci_controller *phb,
struct device_node *dn,
struct iommu_table *tbl,
- const void *dma_window,
- int bussubno)
+ const void *dma_window)
{
unsigned long offset, size;
- tbl->it_busno = bussubno;
of_parse_dma_window(dn, dma_window, &tbl->it_index, &offset, &size);
+ tbl->it_busno = phb->bus->number;
tbl->it_base = 0;
tbl->it_blocksize = 16;
tbl->it_type = TCE_PCI;
@@ -493,8 +492,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
if (!pci->iommu_table) {
tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
pci->phb->node);
- iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window,
- pci->phb->bus->number);
+ iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window);
pci->iommu_table = iommu_init_table(tbl, pci->phb->node);
pr_debug(" created table: %p\n", pci->iommu_table);
} else {
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 04/11] ppc: add memory_hotplug_max
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc
Cc: linux-kernel, miltonm, H Hartley Sweeten, Paul Mackerras,
Anton Blanchard, David Rientjes, Andrew Morton, linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
Add a function to get the maximum address that can be hotplug added.
This is needed to calculate the size of the tce table needed to cover
all memory in 1:1 mode.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
Comments on where to export?
---
arch/powerpc/include/asm/mmzone.h | 5 +++++
arch/powerpc/mm/numa.c | 26 ++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/mmzone.h b/arch/powerpc/include/asm/mmzone.h
index aac87cb..fd3fd58 100644
--- a/arch/powerpc/include/asm/mmzone.h
+++ b/arch/powerpc/include/asm/mmzone.h
@@ -33,6 +33,9 @@ extern int numa_cpu_lookup_table[];
extern cpumask_var_t node_to_cpumask_map[];
#ifdef CONFIG_MEMORY_HOTPLUG
extern unsigned long max_pfn;
+u64 memory_hotplug_max(void);
+#else
+#define memory_hotplug_max() memblock_end_of_DRAM()
#endif
/*
@@ -42,6 +45,8 @@ extern unsigned long max_pfn;
#define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn)
#define node_end_pfn(nid) (NODE_DATA(nid)->node_end_pfn)
+#else
+#define memory_hotplug_max() memblock_end_of_DRAM()
#endif /* CONFIG_NEED_MULTIPLE_NODES */
#endif /* __KERNEL__ */
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 002878c..f98b0d2 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -1246,4 +1246,30 @@ int hot_add_scn_to_nid(unsigned long scn_addr)
return nid;
}
+static u64 hot_add_drconf_memory_max(void)
+{
+ struct device_node *memory = NULL;
+ unsigned int drconf_cell_cnt = 0;
+ u64 lmb_size = 0;
+ const u32 *dm = 0;
+
+ memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
+ if (memory) {
+ drconf_cell_cnt = of_get_drconf_memory(memory, &dm);
+ lmb_size = of_get_lmb_size(memory);
+ of_node_put(memory);
+ }
+ return lmb_size * drconf_cell_cnt;
+}
+
+/*
+ * memory_hotplug_max - return max address of memory that may be added
+ *
+ * This is currently only used on systems that support drconfig memory
+ * hotplug.
+ */
+u64 memory_hotplug_max(void)
+{
+ return max(hot_add_drconf_memory_max(), memblock_end_of_DRAM());
+}
#endif /* CONFIG_MEMORY_HOTPLUG */
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 07/11] ppc/iommu: do not need to check for dma_window == NULL
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc; +Cc: linux-kernel, miltonm, Paul Mackerras, Anton Blanchard,
linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
The block in pci_dma_dev_setup_pSeriesLP for dma_window == NULL can be
removed because we will only teminate the loop if we had already allocated
a iommu table for that node or we found a window. While there may be
no window for the device, the intresting part is if we are reusing a
table or creating it for the first device under it.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
arch/powerpc/platforms/pseries/iommu.c | 6 ------
1 files changed, 0 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 0ae5a60..9d564b9 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -530,12 +530,6 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
}
pr_debug(" parent is %s\n", pdn->full_name);
- if (dma_window == NULL) {
- pr_debug(" no dma window for device, linking to parent\n");
- set_iommu_table_base(&dev->dev, PCI_DN(pdn)->iommu_table);
- return;
- }
-
pci = PCI_DN(pdn);
if (!pci->iommu_table) {
tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 06/11] ppc: checking for pdn->parent is redundant
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc; +Cc: linux-kernel, miltonm, Paul Mackerras, Anton Blanchard,
linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
The device tree root is never a pci bus, and will not have a
PCI_DN(pdn), so the check for PCI_DN added in
650f7b3b2f0ead0673e90452cf3dedde97c537ba makes the check for pdn->parent
redundant and it can be removed.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
arch/powerpc/platforms/pseries/iommu.c | 5 +----
1 files changed, 1 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 8ab32da..0ae5a60 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -530,10 +530,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
}
pr_debug(" parent is %s\n", pdn->full_name);
- /* Check for parent == NULL so we don't try to setup the empty EADS
- * slots on POWER4 machines.
- */
- if (dma_window == NULL || pdn->parent == NULL) {
+ if (dma_window == NULL) {
pr_debug(" no dma window for device, linking to parent\n");
set_iommu_table_base(&dev->dev, PCI_DN(pdn)->iommu_table);
return;
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 03/11] ppc: Create ops to choose between direct window and iommu based on device mask
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc
Cc: Anton Vorontsov, miltonm, linux-kernel, FUJITA Tomonori,
Paul Mackerras, Scott Wood, Andrew Morton, linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
Also allow the coherent ops to be iommu if only the coherent mask is too
small, mostly for driver that do not set set the coherent mask but also
don't use the coherent api.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
arch/powerpc/include/asm/dma-mapping.h | 2 +
arch/powerpc/kernel/Makefile | 2 +-
arch/powerpc/kernel/dma-choose64.c | 167 ++++++++++++++++++++++++++++++++
3 files changed, 170 insertions(+), 1 deletions(-)
create mode 100644 arch/powerpc/kernel/dma-choose64.c
diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h
index 644103a..9ffb16a 100644
--- a/arch/powerpc/include/asm/dma-mapping.h
+++ b/arch/powerpc/include/asm/dma-mapping.h
@@ -68,6 +68,8 @@ static inline unsigned long device_to_mask(struct device *dev)
*/
#ifdef CONFIG_PPC64
extern struct dma_map_ops dma_iommu_ops;
+extern struct dma_map_ops dma_choose64_ops;
+extern struct dma_map_ops dma_iommu_coherent_ops;
#endif
extern struct dma_map_ops dma_direct_ops;
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 1dda701..21b8ea1 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -82,7 +82,7 @@ obj-y += time.o prom.o traps.o setup-common.o \
udbg.o misc.o io.o dma.o \
misc_$(CONFIG_WORD_SIZE).o
obj-$(CONFIG_PPC32) += entry_32.o setup_32.o
-obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o
+obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o dma-choose64.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o
obj-$(CONFIG_MODULES) += ppc_ksyms.o
diff --git a/arch/powerpc/kernel/dma-choose64.c b/arch/powerpc/kernel/dma-choose64.c
new file mode 100644
index 0000000..17c716f
--- /dev/null
+++ b/arch/powerpc/kernel/dma-choose64.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corporation
+ *
+ * Provide default implementations of the DMA mapping callbacks for
+ * directly mapped busses.
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/bug.h>
+
+/*
+ * DMA operations that choose between a 64-bit direct mapping and and iommu
+ *
+ * This set of dma ops chooses between directing to a static 1:1 mapping
+ * that may require a 64 bit address and a iommu based on the declared
+ * streaming and coherent masks for the device. The choice is made on
+ * the first dma map call.
+ */
+
+/* first BUG ops for calls out of sequence */
+
+void *dma_bug_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag)
+{
+ BUG();
+
+ return NULL;
+}
+
+void dma_bug_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
+{
+ BUG();
+}
+
+static int dma_bug_dma_supported(struct device *dev, u64 mask)
+{
+ BUG();
+
+ return 0;
+}
+
+static int dma_bug_map_sg(struct device *dev, struct scatterlist *sgl,
+ int nents, enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ BUG();
+
+ return 0;
+}
+
+
+static void dma_bug_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ BUG();
+}
+
+static dma_addr_t dma_bug_map_page(struct device *dev,
+ struct page *page,
+ unsigned long offset,
+ size_t size,
+ enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ BUG();
+
+ return DMA_ERROR_CODE;
+}
+
+
+static void dma_bug_unmap_page(struct device *dev,
+ dma_addr_t dma_address,
+ size_t size,
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ BUG();
+}
+
+
+static struct dma_map_ops *choose(struct device *dev)
+{
+ if (dma_direct_ops.dma_supported(dev, device_to_mask(dev))) {
+ if (dma_direct_ops.dma_supported(dev, dev->coherent_dma_mask))
+ return &dma_direct_ops;
+ return &dma_iommu_coherent_ops;
+ }
+ return &dma_iommu_ops;
+}
+
+void *dma_choose64_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag)
+{
+ struct dma_map_ops *new = choose(dev);
+
+ set_dma_ops(dev, new);
+ return new->alloc_coherent(dev, size, dma_handle, flag);
+}
+
+static int dma_choose64_map_sg(struct device *dev, struct scatterlist *sgl,
+ int nents, enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ struct dma_map_ops *new = choose(dev);
+
+ set_dma_ops(dev, new);
+ return new->map_sg(dev, sgl, nents, direction, attrs);
+}
+
+
+static int dma_choose64_dma_supported(struct device *dev, u64 mask)
+{
+ return dma_direct_ops.dma_supported(dev, mask) ||
+ dma_iommu_ops.dma_supported(dev, mask);
+}
+
+static dma_addr_t dma_choose64_map_page(struct device *dev,
+ struct page *page,
+ unsigned long offset,
+ size_t size,
+ enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ struct dma_map_ops *new = choose(dev);
+
+ set_dma_ops(dev, new);
+ return new->map_page(dev, page, offset, size, dir, attrs);
+}
+
+struct dma_map_ops dma_choose64_ops = {
+ .alloc_coherent = dma_choose64_alloc_coherent,
+ .free_coherent = dma_bug_free_coherent,
+ .map_sg = dma_choose64_map_sg,
+ .unmap_sg = dma_bug_unmap_sg,
+ .dma_supported = dma_choose64_dma_supported,
+ .map_page = dma_choose64_map_page,
+ .unmap_page = dma_bug_unmap_page,
+};
+EXPORT_SYMBOL(dma_choose64_ops);
+
+/* set these up to BUG() until we initialze them in the arch initcall below */
+struct dma_map_ops dma_iommu_coherent_ops = {
+ .alloc_coherent = dma_bug_alloc_coherent,
+ .free_coherent = dma_bug_free_coherent,
+ .map_sg = dma_bug_map_sg,
+ .unmap_sg = dma_bug_unmap_sg,
+ .dma_supported = dma_bug_dma_supported,
+ .map_page = dma_bug_map_page,
+ .unmap_page = dma_bug_unmap_page,
+};
+EXPORT_SYMBOL(dma_iommu_coherent_ops);
+
+static int setup_choose64_ops(void)
+{
+ dma_iommu_coherent_ops = dma_direct_ops;
+ dma_iommu_coherent_ops.alloc_coherent = dma_iommu_ops.alloc_coherent;
+ dma_iommu_coherent_ops.free_coherent = dma_iommu_ops.free_coherent;
+
+ /* should we be stricter? */
+ dma_iommu_coherent_ops.dma_supported = dma_choose64_dma_supported;
+
+ return 0;
+}
+arch_initcall(setup_choose64_ops);
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 02/11] ppc: allow direct and iommu to coexist
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc
Cc: FUJITA Tomonori, linux-kernel, miltonm, Paul Mackerras,
Andrew Morton, linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
Replace the union with just the multiple fields, ifdef on CONFIG_PPC64.
Future pseries boxes will allow a 64 bit dma mapping covering all
memory, coexisting with a smaller iommu window in 32 bit pci space.
The cell fixed mapping would also like both to coexist.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
I used the ifdef guard of CONFIG_PPC64 according to the current makefile
for iommu.c. One set is burried in the middle of iommu.h.
---
arch/powerpc/include/asm/device.h | 14 ++++++--------
arch/powerpc/include/asm/dma-mapping.h | 4 ++--
arch/powerpc/include/asm/iommu.h | 6 ++++--
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h
index 16d25c0..ed883ea 100644
--- a/arch/powerpc/include/asm/device.h
+++ b/arch/powerpc/include/asm/device.h
@@ -19,14 +19,12 @@ struct dev_archdata {
/* DMA operations on that device */
struct dma_map_ops *dma_ops;
- /*
- * When an iommu is in use, dma_data is used as a ptr to the base of the
- * iommu_table. Otherwise, it is a simple numerical offset.
- */
- union {
- dma_addr_t dma_offset;
- void *iommu_table_base;
- } dma_data;
+ /* dma_offset is used by swiotlb and direct dma ops, but no iommu */
+ dma_addr_t dma_offset;
+
+#ifdef CONFIG_PPC64
+ void *iommu_table_base;
+#endif
#ifdef CONFIG_SWIOTLB
dma_addr_t max_direct_dma_addr;
diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h
index 8c9c6ad..644103a 100644
--- a/arch/powerpc/include/asm/dma-mapping.h
+++ b/arch/powerpc/include/asm/dma-mapping.h
@@ -100,7 +100,7 @@ static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops)
static inline dma_addr_t get_dma_offset(struct device *dev)
{
if (dev)
- return dev->archdata.dma_data.dma_offset;
+ return dev->archdata.dma_offset;
return PCI_DRAM_OFFSET;
}
@@ -108,7 +108,7 @@ static inline dma_addr_t get_dma_offset(struct device *dev)
static inline void set_dma_offset(struct device *dev, dma_addr_t off)
{
if (dev)
- dev->archdata.dma_data.dma_offset = off;
+ dev->archdata.dma_offset = off;
}
/* this will be removed soon */
diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index edfc980..0f605a4 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -70,15 +70,17 @@ struct iommu_table {
struct scatterlist;
+#ifdef CONFIG_PPC64
static inline void set_iommu_table_base(struct device *dev, void *base)
{
- dev->archdata.dma_data.iommu_table_base = base;
+ dev->archdata.iommu_table_base = base;
}
static inline void *get_iommu_table_base(struct device *dev)
{
- return dev->archdata.dma_data.iommu_table_base;
+ return dev->archdata.iommu_table_base;
}
+#endif
/* Frees table for an individual device node */
extern void iommu_free_table(struct iommu_table *tbl, const char *node_name);
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 01/11] macio: ensure all dma routines get copied over
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc; +Cc: linux-kernel, miltonm, Andreas Schwab, Paul Mackerras,
linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
Also add a comment to dev_archdata, indicating that changes there need
to be verified against the driver code.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
arch/powerpc/include/asm/device.h | 6 ++++++
drivers/macintosh/macio_asic.c | 7 +++----
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h
index a3954e4..16d25c0 100644
--- a/arch/powerpc/include/asm/device.h
+++ b/arch/powerpc/include/asm/device.h
@@ -9,6 +9,12 @@
struct dma_map_ops;
struct device_node;
+/*
+ * Arch extensions to struct device.
+ *
+ * When adding fields, consider macio_add_one_device in
+ * drivers/macintosh/macio_asic.c
+ */
struct dev_archdata {
/* DMA operations on that device */
struct dma_map_ops *dma_ops;
diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c
index b6e7ddc..18bf7a9 100644
--- a/drivers/macintosh/macio_asic.c
+++ b/drivers/macintosh/macio_asic.c
@@ -387,11 +387,10 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip,
/* Set the DMA ops to the ones from the PCI device, this could be
* fishy if we didn't know that on PowerMac it's always direct ops
* or iommu ops that will work fine
+ *
+ * To get all the fields, copy all archdata
*/
- dev->ofdev.dev.archdata.dma_ops =
- chip->lbus.pdev->dev.archdata.dma_ops;
- dev->ofdev.dev.archdata.dma_data =
- chip->lbus.pdev->dev.archdata.dma_data;
+ dev->ofdev.dev.archdata = chip->lbus.pdev->dev.archdata;
#endif /* CONFIG_PCI */
#ifdef DEBUG
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 00/11] ppc: enable dynamic dma window support
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc; +Cc: linuxppc-dev, devicetree-discuss, linux-kernel, miltonm
Hi,
The following series, which builds upon the series of cleanups I posted
on 9/15 as "ppc iommu cleanups", enables the pseries firmware feature
dynamic dma windows. This feature will allow future devices to have a
64-bit DMA mapping covering all memory, coexisting with a smaller IOMMU
window in 32-bit PCI space.
Comments requested and welcome!
Nishanth Aravamudan (11):
macio: ensure all dma routines get copied over
ppc: allow direct and iommu to coexist
ppc: Create ops to choose between direct window and iommu based on
device mask
ppc: add memory_hotplug_max
ppc: do not search for dma-window property on dlpar remove
ppc: checking for pdn->parent is redundant
ppc/iommu: do not need to check for dma_window == NULL
ppc/iommu: remove unneeded pci_dma_bus_setup_pSeriesLP
ppc/iommu: pass phb only to iommu_table_setparms_lpar
ppc/iommu: add routines to pseries iommu to map tces 1-1
ppc: add dynamic dma window support
arch/powerpc/include/asm/device.h | 20 +-
arch/powerpc/include/asm/dma-mapping.h | 6 +-
arch/powerpc/include/asm/iommu.h | 6 +-
arch/powerpc/include/asm/mmzone.h | 5 +
arch/powerpc/kernel/Makefile | 2 +-
arch/powerpc/kernel/dma-choose64.c | 167 +++++++++++
arch/powerpc/mm/numa.c | 26 ++
arch/powerpc/platforms/pseries/iommu.c | 475 +++++++++++++++++++++++++++----
drivers/macintosh/macio_asic.c | 7 +-
9 files changed, 635 insertions(+), 79 deletions(-)
create mode 100644 arch/powerpc/kernel/dma-choose64.c
^ permalink raw reply
* [RFC PATCH 11/11] ppc: add dynamic dma window support
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc
Cc: devicetree-discuss, linux-kernel, miltonm, Paul Mackerras,
Anton Blanchard, linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
If firmware allows us to map all of a partition's memory for DMA on a
particular bridge, create a 1:1 mapping of that memory. Add hooks for
dealing with hotplug events. Dyanmic DMA windows can use larger than the
default page size, and we use the largest one possible.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
arch/powerpc/platforms/pseries/iommu.c | 319 +++++++++++++++++++++++++++++++-
1 files changed, 315 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 451d2d1..23ca0d1 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -33,6 +33,7 @@
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/crash_dump.h>
+#include <linux/memory.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/rtas.h>
@@ -45,6 +46,7 @@
#include <asm/tce.h>
#include <asm/ppc-pci.h>
#include <asm/udbg.h>
+#include <asm/mmzone.h>
#include "plpar_wrappers.h"
@@ -278,10 +280,19 @@ struct dynamic_dma_window_prop {
__be32 window_shift; /* ilog2(tce_window_size) */
};
+struct direct_window {
+ struct device_node *device;
+ const struct dynamic_dma_window_prop *prop;
+ struct list_head list;
+};
+static LIST_HEAD(direct_window_list);
+static DEFINE_SPINLOCK(direct_window_list_lock);
+#define DIRECT64_PROPNAME "linux,direct64-ddr-window-info"
+
static int tce_clearrange_multi_pSeriesLP(unsigned long start_pfn,
- unsigned long num_pfn, void *arg)
+ unsigned long num_pfn, const void *arg)
{
- struct dynamic_dma_window_prop *maprange = arg;
+ const struct dynamic_dma_window_prop *maprange = arg;
int rc;
u64 tce_size, num_tce, dma_offset;
u32 tce_shift;
@@ -305,9 +316,9 @@ static int tce_clearrange_multi_pSeriesLP(unsigned long start_pfn,
}
static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn,
- unsigned long num_pfn, void *arg)
+ unsigned long num_pfn, const void *arg)
{
- struct dynamic_dma_window_prop *maprange = arg;
+ const struct dynamic_dma_window_prop *maprange = arg;
u64 *tcep, tce_size, num_tce, dma_offset, next, proto_tce;
u32 tce_shift;
long rc = 0;
@@ -368,6 +379,12 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn,
return rc;
}
+static int tce_setrange_multi_pSeriesLP_walk(unsigned long start_pfn,
+ unsigned long num_pfn, void *arg)
+{
+ return tce_setrange_multi_pSeriesLP(start_pfn, num_pfn, arg);
+}
+
#ifdef CONFIG_PCI
static void iommu_table_setparms(struct pci_controller *phb,
struct device_node *dn,
@@ -553,6 +570,246 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
pci_name(dev));
}
+/*
+ * If the PE supports dynamic dma windows, and there is space for a table
+ * that can map all pages in a linear offset, then setup such a table,
+ * and record the dma-offset in the struct device.
+ *
+ * dev: the pci device we are checking
+ * pdn: the parent pe node with the ibm,dma_window property
+ * Future: also check if we can remap the base window for our base page size
+ */
+static void check_ddr_windowLP(struct pci_dev *dev, struct device_node *pdn)
+{
+ int len, ret;
+ u32 query[4], create[3], cfg_addr;
+ int page_shift;
+ u64 dma_addr, buid, max_addr;
+ struct pci_dn *pcidn;
+ const u32 *uninitialized_var(ddr_avail);
+ struct direct_window *window;
+ struct property *uninitialized_var(win64);
+ struct dynamic_dma_window_prop *ddwprop;
+ const struct dynamic_dma_window_prop *direct64;
+
+ spin_lock(&direct_window_list_lock);
+
+ /* check if we already created a window */
+ list_for_each_entry(window, &direct_window_list, list) {
+ if (window->device == pdn) {
+ direct64 = window->prop;
+ goto set_device;
+ }
+ }
+ /* check if we kexec'd with a window */
+ direct64 = of_get_property(pdn, DIRECT64_PROPNAME, &len);
+ if (direct64)
+ goto create_window_listent;
+
+ ddr_avail = of_get_property(pdn, "ibm,ddw-applicable", &len);
+
+ if (!ddr_avail || len < 4 * sizeof(u32))
+ return;
+ /*
+ * the ibm,ddw-applicable property holds the tokens for:
+ * ibm,query-pe-dma-window
+ * ibm,create-pe-dma-window
+ * ibm,remove-pe-dma-window
+ * for the given node in that order.
+ *
+ * Query if there is a second window of size to map the
+ * whole partition. Query returns number of windows, largest
+ * block assigned to PE (partition endpoint), and two bitmasks
+ * of page sizes: supported and supported for migrate-dma.
+ */
+
+ /*
+ * Get the config address and phb build of the PE window.
+ * Rely on eeh to retrieve this for us.
+ * Retrieve them from the node with the dma window property.
+ */
+ pcidn = PCI_DN(pdn);
+ cfg_addr = pcidn->eeh_config_addr;
+ if (pcidn->eeh_pe_config_addr)
+ cfg_addr = pcidn->eeh_pe_config_addr;
+ buid = pcidn->phb->buid;
+ ret = rtas_call(ddr_avail[0], 3, 5, &query[0],
+ cfg_addr, BUID_HI(buid), BUID_LO(buid));
+ if (ret != 0) {
+ dev_info(&dev->dev, "ibm,query-pe-dma-windows(%x) %x %x %x"
+ " returned %d\n", ddr_avail[0], cfg_addr, BUID_HI(buid),
+ BUID_LO(buid), ret);
+ goto out_unlock;
+ }
+
+ if (!query[0]) {
+ /*
+ * no additional windows are available for this device.
+ * We might be able to reallocate the existing window,
+ * trading in for a larger page size.
+ */
+ dev_dbg(&dev->dev, "no free dynamic windows");
+ goto out_unlock;
+ }
+ if (query[2] & 4) {
+ page_shift = 24; /* 16MB */
+ } else if (query[2] & 2) {
+ page_shift = 16; /* 64kB */
+ } else if (query[2] & 1) {
+ page_shift = 12; /* 4kB */
+ } else {
+ dev_dbg(&dev->dev, "no supported direct page size in mask %x",
+ query[2]);
+ goto out_unlock;
+ }
+ /* verify the window * number of ptes will map the partition */
+ /* check largest block * page size > max memory hotplug addr */
+ max_addr = memory_hotplug_max();
+ if (query[1] < (max_addr >> page_shift)) {
+ dev_dbg(&dev->dev, "can't map partiton max 0x%llx with %u "
+ "%llu-sized pages\n", max_addr, query[1],
+ 1ULL << page_shift);
+ goto out_unlock;
+ }
+ len = order_base_2(max_addr);
+ win64 = kzalloc(sizeof(struct property), GFP_KERNEL);
+ if (!win64) {
+ dev_info(&dev->dev,
+ "couldn't allocate property for 64bit dma window\n");
+ goto out_unlock;
+ }
+ win64->name = kstrdup(DIRECT64_PROPNAME, GFP_KERNEL);
+ win64->value = ddwprop = kmalloc(sizeof(*ddwprop), GFP_KERNEL);
+ if (!win64->name || !win64->value) {
+ dev_info(&dev->dev,
+ "couldn't allocate property name and value\n");
+ goto out_free_prop;
+ }
+ do {
+ /* extra outputs are LIOBN and dma-addr (hi, lo) */
+ ret = rtas_call(ddr_avail[1], 7, 4, &create[0], cfg_addr,
+ BUID_HI(buid), BUID_LO(buid), len, page_shift);
+ } while(rtas_busy_delay(ret));
+ if (ret) {
+ dev_info(&dev->dev,
+ "failed to create direct window: rtas returned %d"
+ " to ibm,create-pe-dma-window(%x) %x %x %x %x %x\n",
+ ret, ddr_avail[1], cfg_addr, BUID_HI(buid),
+ BUID_LO(buid), len, page_shift);
+ goto out_free_prop;
+ }
+
+ *ddwprop = (struct dynamic_dma_window_prop) {
+ .liobn = cpu_to_be32(create[0]),
+ .dma_base = {cpu_to_be32(create[1]), cpu_to_be32(create[2])},
+ .tce_shift = cpu_to_be32(page_shift),
+ .window_shift = cpu_to_be32(len)
+ };
+
+ dev_dbg(&dev->dev, "created tce table LIOBN 0x%x for %s\n",
+ create[0], pdn->full_name);
+
+ ret = walk_system_ram_range(0, memblock_end_of_DRAM() >> PAGE_SHIFT,
+ win64->value, tce_setrange_multi_pSeriesLP_walk);
+ if (ret) {
+ dev_info(&dev->dev, "failed to map direct window for %s\n",
+ pdn->full_name);
+
+ goto out_clear_window;
+ }
+
+ ret = prom_add_property(pdn, win64);
+ if (ret) {
+ pr_err("%s: unable to add dma window property: %d",
+ pdn->full_name, ret);
+ goto out_clear_window;
+ }
+
+ direct64 = ddwprop;
+
+create_window_listent:
+ window = kzalloc(sizeof(*window), GFP_KERNEL);
+ if (!window)
+ goto out_clear_window;
+ window->device = pdn;
+ window->prop = direct64;
+ list_add(&window->list, &direct_window_list);
+
+set_device:
+ dma_addr = of_read_number(&direct64->dma_base[0], 2);
+ set_dma_offset(&dev->dev, dma_addr);
+ set_dma_ops(&dev->dev, &dma_choose64_ops);
+
+ dev_dbg(&dev->dev, "Can use direct dma at %s (offset %llx)\n",
+ pdn->full_name, dma_addr);
+
+out_unlock:
+ spin_unlock(&direct_window_list_lock);
+ return;
+
+out_clear_window:
+ ret = tce_clearrange_multi_pSeriesLP(0,
+ memblock_end_of_DRAM() >> PAGE_SHIFT, win64->value);
+ if (ret)
+ dev_info(&dev->dev,
+ "failed to clear partial window for %s\n",
+ pdn->full_name);
+
+ ret = rtas_call(ddr_avail[2], 1, 1, NULL, direct64->liobn);
+ if (ret) {
+ dev_info(&dev->dev,
+ "failed to remove direct window: rtas returned "
+ "%d to ibm,remove-pe-dma-window(%x) %x\n",
+ ret, ddr_avail[2], direct64->liobn);
+ }
+
+out_free_prop:
+ kfree(win64->name);
+ kfree(win64->value);
+ kfree(win64);
+
+ goto out_unlock;
+}
+
+#if 1 //def CLEAN_WINDOW_ON_REMOVE
+static void remove_ddr_windowLP(struct device_node *np)
+{
+ struct dynamic_dma_window_prop *dwp;
+ struct property *win64;
+ const u32 *ddr_avail;
+ int len, ret;
+
+ ddr_avail = of_get_property(np, "ibm,ddw-applicable", &len);
+
+ win64 = of_find_property(np, DIRECT64_PROPNAME, NULL);
+
+ if (!win64 || !ddr_avail || len < 4 * sizeof(u32))
+ return;
+
+ dwp = win64->value;
+
+ /* clear the whole window, note the arg is in kernel pages */
+ ret = tce_clearrange_multi_pSeriesLP(0,
+ 1ULL << (dwp->window_shift - PAGE_SHIFT), dwp);
+ if (ret)
+ pr_warning("%s failed to clear tces in window.\n",
+ np->full_name);
+
+ ret = rtas_call(ddr_avail[2], 1, 1, NULL, dwp->liobn);
+ if (ret)
+ pr_warning("%s: failed to remove direct window: rtas returned "
+ "%d to ibm,remove-pe-dma-window(%x) %x\n",
+ np->full_name, ret, ddr_avail[2], dwp->liobn);
+
+ ret = prom_remove_property(np, win64);
+ if (ret)
+ pr_warning("%s: failed to remove direct window property (%i)\n",
+ np->full_name, ret);
+}
+#else
+static void remove_ddr_windowLP(struct device_node *np) {}
+#endif
+
static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
{
struct device_node *pdn, *dn;
@@ -598,6 +855,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
}
set_iommu_table_base(&dev->dev, pci->iommu_table);
+ check_ddr_windowLP(dev, pdn);
}
#else /* CONFIG_PCI */
#define pci_dma_bus_setup_pSeries NULL
@@ -605,16 +863,68 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
#define pci_dma_dev_setup_pSeriesLP NULL
#endif /* !CONFIG_PCI */
+static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct direct_window *window;
+ struct memory_notify *arg = data;
+ int ret = 0;
+
+ switch (action) {
+ case MEM_GOING_ONLINE:
+ spin_lock(&direct_window_list_lock);
+ list_for_each_entry(window, &direct_window_list, list) {
+ ret |= tce_setrange_multi_pSeriesLP(arg->start_pfn,
+ arg->nr_pages, window->prop);
+ /* XXX log error */
+ }
+ spin_unlock(&direct_window_list_lock);
+ break;
+ case MEM_CANCEL_ONLINE:
+ case MEM_OFFLINE:
+ spin_lock(&direct_window_list_lock);
+ list_for_each_entry(window, &direct_window_list, list) {
+ ret |= tce_clearrange_multi_pSeriesLP(arg->start_pfn,
+ arg->nr_pages, window->prop);
+ /* XXX log error */
+ }
+ spin_unlock(&direct_window_list_lock);
+ break;
+ default:
+ break;
+ }
+ if (ret && action != MEM_CANCEL_ONLINE)
+ return NOTIFY_BAD;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block iommu_mem_nb = {
+ .notifier_call = iommu_mem_notifier,
+};
+
static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node)
{
int err = NOTIFY_OK;
struct device_node *np = node;
struct pci_dn *pci = PCI_DN(np);
+ struct direct_window *window;
switch (action) {
case PSERIES_RECONFIG_REMOVE:
if (pci && pci->iommu_table)
iommu_free_table(pci->iommu_table, np->full_name);
+
+ spin_lock(&direct_window_list_lock);
+ list_for_each_entry(window, &direct_window_list, list) {
+ if (window->device == np) {
+ list_del(&window->list);
+ break;
+ }
+ }
+ spin_unlock(&direct_window_list_lock);
+
+ remove_ddr_windowLP(np);
break;
default:
err = NOTIFY_DONE;
@@ -653,6 +963,7 @@ void iommu_init_early_pSeries(void)
pSeries_reconfig_notifier_register(&iommu_reconfig_nb);
+ register_memory_notifier(&iommu_mem_nb);
set_pci_dma_ops(&dma_iommu_ops);
}
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 08/11] ppc/iommu: remove unneeded pci_dma_bus_setup_pSeriesLP
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc
Cc: devicetree-discuss, linux-kernel, miltonm, Paul Mackerras,
Anton Blanchard, linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
The work done in pci_dma_bus_setup_pSeriesLP will be done in
pci_dma_dev_setup_pSeriesLP, and therefore we can remove the bus setup
function for lpar.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
arch/powerpc/platforms/pseries/iommu.c | 43 --------------------------------
1 files changed, 0 insertions(+), 43 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 9d564b9..d8bb9be 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -417,47 +417,6 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
pr_debug("ISA/IDE, window size is 0x%llx\n", pci->phb->dma_window_size);
}
-
-static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
-{
- struct iommu_table *tbl;
- struct device_node *dn, *pdn;
- struct pci_dn *ppci;
- const void *dma_window = NULL;
-
- dn = pci_bus_to_OF_node(bus);
-
- pr_debug("pci_dma_bus_setup_pSeriesLP: setting up bus %s\n",
- dn->full_name);
-
- /* Find nearest ibm,dma-window, walking up the device tree */
- for (pdn = dn; pdn != NULL; pdn = pdn->parent) {
- dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
- if (dma_window != NULL)
- break;
- }
-
- if (dma_window == NULL) {
- pr_debug(" no ibm,dma-window property !\n");
- return;
- }
-
- ppci = PCI_DN(pdn);
-
- pr_debug(" parent is %s, iommu_table: 0x%p\n",
- pdn->full_name, ppci->iommu_table);
-
- if (!ppci->iommu_table) {
- tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
- ppci->phb->node);
- iommu_table_setparms_lpar(ppci->phb, pdn, tbl, dma_window,
- bus->number);
- ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node);
- pr_debug(" created table: %p\n", ppci->iommu_table);
- }
-}
-
-
static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
{
struct device_node *dn;
@@ -547,7 +506,6 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
#else /* CONFIG_PCI */
#define pci_dma_bus_setup_pSeries NULL
#define pci_dma_dev_setup_pSeries NULL
-#define pci_dma_bus_setup_pSeriesLP NULL
#define pci_dma_dev_setup_pSeriesLP NULL
#endif /* !CONFIG_PCI */
@@ -588,7 +546,6 @@ void iommu_init_early_pSeries(void)
ppc_md.tce_free = tce_free_pSeriesLP;
}
ppc_md.tce_get = tce_get_pSeriesLP;
- ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pSeriesLP;
ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pSeriesLP;
} else {
ppc_md.tce_build = tce_build_pSeries;
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 05/11] ppc: do not search for dma-window property on dlpar remove
From: Nishanth Aravamudan @ 2010-10-08 17:33 UTC (permalink / raw)
To: nacc
Cc: devicetree-discuss, linux-kernel, miltonm, Paul Mackerras,
Anton Blanchard, linuxppc-dev
In-Reply-To: <1286559192-10898-1-git-send-email-nacc@us.ibm.com>
The iommu_table pointer in the pci auxiliary struct of device_node has
not been used by the iommu ops since the dma refactor of
12d04eef927bf61328af2c7cbe756c96f98ac3bf, however this code still uses
it to find tables for dlpar. By only setting the PCI_DN iommu_table
pointer on nodes with dma window properties, we will be able to quickly
find the node for later checks, and can remove the table without looking
for the the dma window property on dlpar remove.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
arch/powerpc/platforms/pseries/iommu.c | 6 +-----
1 files changed, 1 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 9184db3..8ab32da 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -455,9 +455,6 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node);
pr_debug(" created table: %p\n", ppci->iommu_table);
}
-
- if (pdn != dn)
- PCI_DN(dn)->iommu_table = ppci->iommu_table;
}
@@ -571,8 +568,7 @@ static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long acti
switch (action) {
case PSERIES_RECONFIG_REMOVE:
- if (pci && pci->iommu_table &&
- of_get_property(np, "ibm,dma-window", NULL))
+ if (pci && pci->iommu_table)
iommu_free_table(pci->iommu_table, np->full_name);
break;
default:
--
1.7.1
^ permalink raw reply related
* Re: powerpc, fs_enet: scanning PHY after Linux is up
From: Grant Likely @ 2010-10-08 17:06 UTC (permalink / raw)
To: Holger brunck; +Cc: linuxppc-dev, devicetree-discuss, hs, Detlev Zundel, netdev
In-Reply-To: <4CAEDB6A.70600@keymile.com>
On Fri, Oct 08, 2010 at 10:50:50AM +0200, Holger brunck wrote:
> Hi Grant,
>
> On 10/06/2010 06:52 PM, Grant Likely wrote:
> > On Wed, Oct 6, 2010 at 3:53 AM, Heiko Schocher <hs@denx.de> wrote:
> >>>> So, the question is, is there a possibility to solve this problem?
> >>>>
> >>>> If there is no standard option, what would be with adding a
> >>>> "scan_phy" file in
> >>>>
> >>>> /proc/device-tree/soc\@f0000000/cpm\@119c0/mdio\@10d40
> >>>> (or better destination?)
> >>>>
> >>>> which with we could rescan a PHY with
> >>>> "echo addr > /proc/device-tree/soc\@f0000000/cpm\@119c0/mdio\@10d40/scan_phy"
> >>>> (so there is no need for using of_find_node_by_path(), as we should
> >>>> have the associated device node here, and can step through the child
> >>>> nodes with "for_each_child_of_node(np, child)" and check if reg == addr)
> >>>>
> >>>> or shouldn;t be at least, if the phy couldn;t be found when opening
> >>>> the port, retrigger a scanning, if the phy now is accessible?
> >>>
> >>> One option would be to still register a phy_device for each phy
> >>> described in the device tree, but defer binding a driver to each phy
> >>> that doesn't respond. Then at of_phy_find_device() time, if it
> >>
> >> Maybe I din;t get the trick, but the problem is, that
> >> you can;t register a phy_device in drivers/of/of_mdio.c
> >> of_mdiobus_register(), if the phy didn;t respond with the
> >> phy_id ... and of_phy_find_device() is not (yet) used in fs_enet
> >
> > I'm suggesting modifying the phy layer so that it is possible to
> > register a phy_device that doesn't (yet) respond.
> >
>
> yes this sounds reasonable.
>
> >>> matches with a phy_device that isn't bound to a driver yet, then
> >>> re-trigger the binding operation. At which point the phy id can be
> >>> probed and the correct driver can be chosen. If binding succeeds,
> >>> then return the phy_device handle. If not, then fail as it currently
> >>> does.
> >>
> >> Wouldn;t it be good, just if we need a PHY (on calling fs_enet_open)
> >> to look if there is one?
> >>
> >> Something like that (not tested):
> >>
> >> in drivers/net/fs_enet/fs_enet-main.c in fs_init_phy()
> >> called from fs_enet_open():
> >>
> >> Do first:
> >> phydev = of_phy_find_device(fep->fpi->phy_node);
> >>
> >> Look if there is a driver (phy_dev->drv == NULL ?)
> >>
> >> If not, call new function
> >> of_mdiobus_register_phy(mii_bus, fep->fpi->phy_node)
> >> see below patch for it.
> >>
> >> If this succeeds, all is OK, and we can use this phy,
> >> else ethernet not work.
> >
> > I don't like this approach because it muddies the concept of which
> > device is actually responsible for managing the phys on the bus. Is
> > it managed by the mdio bus device or the Ethernet device? It also has
> > a potential race condition. Whereas triggering a late driver bind
> > will be safe.
> >
> > Alternately, I'd also be okay with a common method to trigger a
> > reprobe of a particular phy from userspace, but I fear that would be a
> > significantly more complex solution.
> >
> >>
> >> !!just no idea, how to get mii_bus pointer ...
> >
> > You'd have to get the parent of the phy node, and then loop over all
> > the registered mdio busses looking for a bus that uses that node.
> >
>
> you say that you don't like the approach to probe the phy again in fs_enet_open,
> but currently I don't understand what would be the alternate trigger point to
> rescan the mdio bus?
Same trigger point, but different operation. At fs_enet_open time,
instead of registering the phy_device, the phy layer could sanity
check the already registered phy_device, and refuse to connect to it
if the phy isn't responding. If it is responding, then it could
re-attempt binding a phy_driver to it (although I just realized that
this has other problems, such as correct module loading. See below)
> I made a first patch to enhance the phy_device structure and rescan the mdio bus
> at time of fs_enet_open (because I didn't see a better trigger point). The
> advantage is that we got the mii_bus pointer and the phy addr stored in the
> already created phy device structure and is therefore easy to use. See the patch
> below for this modifications. Whats currently missing in the patch is to set the
> phy_id if the phy was scanned later after phy_device creation. For the mgcoge
> board it seems to solve our problem, but maybe I miss something important.
>
> Best regards
> Holger Brunck
>
> diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
> index ec2f503..6bc117f 100644
> --- a/drivers/net/fs_enet/fs_enet-main.c
> +++ b/drivers/net/fs_enet/fs_enet-main.c
> @@ -775,7 +774,8 @@ static int fs_enet_open(struct net_device *dev)
> {
> struct fs_enet_private *fep = netdev_priv(dev);
> int r;
> - int err;
> + int err = 0;
> + u32 phy_id = 0;
>
> /* to initialize the fep->cur_rx,... */
> /* not doing this, will cause a crash in fs_enet_rx_napi */
> @@ -795,13 +795,23 @@ static int fs_enet_open(struct net_device *dev)
> return -EINVAL;
> }
>
> - err = fs_init_phy(dev);
> - if (err) {
> + if (fep->phydev == NULL)
> + err = fs_init_phy(dev);
> +
> + if (!err && (fep->phydev->available == false))
> + r = get_phy_id(fep->phydev->bus, fep->phydev->addr, &phy_id);
> +
> + if (err || (phy_id == 0xffffffff)) {
> free_irq(fep->interrupt, dev);
> if (fep->fpi->use_napi)
> napi_disable(&fep->napi);
> - return err;
> + if (err)
> + return err;
> + else
> + return -EINVAL;
> }
> + else
> + fep->phydev->available = true;
> phy_start(fep->phydev);
>
> netif_start_queue(dev);
> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index adbc0fd..1f443cb 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c
> @@ -173,6 +173,10 @@ struct phy_device* phy_device_create(struct mii_bus *bus,
> int addr, int phy_id)
> dev->dev.bus = &mdio_bus_type;
> dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL;
> dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr);
> + if (phy_id == 0xffffffff)
> + dev->available = false;
> + else
> + dev->available = true;
This flag shouldn't be necessary. Just check whether or not
phy_device->phy_id is sane at phy_attach_direct() time. If it is
mostly f's, then don't attach.
>
> dev->state = PHY_DOWN;
>
> @@ -232,13 +236,11 @@ struct phy_device * get_phy_device(struct mii_bus *bus,
> int addr)
> int r;
>
> r = get_phy_id(bus, addr, &phy_id);
> - if (r)
> - return ERR_PTR(r);
>
> /* If the phy_id is mostly Fs, there is no device there */
> - if ((phy_id & 0x1fffffff) == 0x1fffffff)
> - return NULL;
> -
> + if (((phy_id & 0x1fffffff) == 0x1fffffff) || r)
> + phy_id = 0xffffffff;
> + /* create phy even if the phy is currently not available */
> dev = phy_device_create(bus, addr, phy_id);
Cannot do it this way because many phylib users probe the bus for phys
instead of the explicit creation used with the device tree. There
needs to be a method to explicitly skip this test when creating a phy;
possibly by having the device tree code call phy_device_create()
directly.
Hmmm.... I see another problem. Deferred probing of the phy will
potentially cause problems with module loading. If the binding is
deferred to phy connect time; then the phy driver may not have time to
get loaded before the phy layer decides there is no driver and binds
it to the generic one. Blech.
Okay, so it seems like a method of explicitly triggering a phy_device
rebind from userspace is necessary. This could be done with a
per-phy_device sysfs file I suppose. Just an empty file that when
read triggers a re-read of the phy id registers, and retries binding a
driver, including the request_module call in phy_device_create().
>
> return dev;
> diff --git a/include/linux/phy.h b/include/linux/phy.h
> index 6a7eb40..12dc3e4 100644
> --- a/include/linux/phy.h
> +++ b/include/linux/phy.h
> @@ -303,6 +303,9 @@ struct phy_device {
>
> int link_timeout;
>
> + /* Flag to support delayed availability */
> + bool available;
> +
> /*
> * Interrupt number for this PHY
> * -1 means no interrupt
>
^ permalink raw reply
* [git pull] Please pull powerpc.git merge branch
From: Kumar Gala @ 2010-10-08 16:04 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
Ben,
This isn't critical, but it does fix having the ppc64e_defconfig
build cleanly.
- k
The following changes since commit 6b0cd00bc396daf5c2dcf17a8d82055335341f46:
Merge branch 'hwpoison-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-mce-2.6 (2010-10-07 13:59:32 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/galak/powerpc.git merge
Kumar Gala (1):
powerpc/ppc64e: Fix link problem when building ppc64e_defconfig
arch/powerpc/platforms/85xx/smp.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
^ permalink raw reply
* [PATCH] powerpc/ppc64e: Fix link problem when building ppc64e_defconfig
From: Kumar Gala @ 2010-10-08 16:00 UTC (permalink / raw)
To: linuxppc-dev
arch/powerpc/platforms/built-in.o:(.toc1+0x18): undefined reference to `__early_start'
This is due to the 85xx/smp.c not handling the 64-bit side properly. We
need to set the entry point for secondary cores on ppc64e to
generic_secondary_smp_init instead of __early_start that we due on ppc32.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/platforms/85xx/smp.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c
index a6b1065..bd38b6a 100644
--- a/arch/powerpc/platforms/85xx/smp.c
+++ b/arch/powerpc/platforms/85xx/smp.c
@@ -79,6 +79,7 @@ smp_85xx_kick_cpu(int nr)
local_irq_save(flags);
out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr);
+#ifdef CONFIG_PPC32
out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start));
if (!ioremappable)
@@ -88,6 +89,12 @@ smp_85xx_kick_cpu(int nr)
/* Wait a bit for the CPU to ack. */
while ((__secondary_hold_acknowledge != nr) && (++n < 1000))
mdelay(1);
+#else
+ out_be64((u64 *)(bptr_vaddr + BOOT_ENTRY_ADDR_UPPER),
+ __pa((u64)*((unsigned long long *) generic_secondary_smp_init)));
+
+ smp_generic_kick_cpu(nr);
+#endif
local_irq_restore(flags);
--
1.7.2.3
^ permalink raw reply related
* Re: [PATCH 1/3] Powerpc/4xx: Add suspend and idle support
From: Josh Boyer @ 2010-10-08 13:46 UTC (permalink / raw)
To: Victor Gallardo; +Cc: linuxppc-dev
In-Reply-To: <1286478436-9942-1-git-send-email-vgallardo@apm.com>
On Thu, Oct 07, 2010 at 12:07:16PM -0700, Victor Gallardo wrote:
I hit send too soon on my earlier response. My apologies.
>--- /dev/null
>+++ b/arch/powerpc/sysdev/ppc4xx_cpm.c
>@@ -0,0 +1,339 @@
>+/*
>+ * PowerPC 4xx Clock and Power Management
>+ *
>+ * Copyright (C) 2010, Applied Micro Circuits Corporation
>+ * Victor Gallardo (vgallardo@apm.com)
>+ *
>+ * Based on arch/powerpc/platforms/44x/idle.c:
>+ * Jerone Young <jyoung5@us.ibm.com>
>+ * Copyright 2008 IBM Corp.
>+ *
>+ * Based on arch/powerpc/sysdev/fsl_pmc.c:
>+ * Anton Vorontsov <avorontsov@ru.mvista.com>
>+ * Copyright 2009 MontaVista Software, Inc.
>+ *
>+ * See file CREDITS for list of people who contributed to this
>+ * project.
>+ *
>+ * This program is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU General Public License as
>+ * published by the Free Software Foundation; either version 2 of
>+ * the License, or (at your option) any later version.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+ * GNU General Public License for more details.
>+ *
>+ * You should have received a copy of the GNU General Public License
>+ * along with this program; if not, write to the Free Software
>+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
>+ * MA 02111-1307 USA
>+ */
>+
>+#include <linux/kernel.h>
>+#include <linux/of_platform.h>
>+#include <linux/sysfs.h>
>+#include <linux/cpu.h>
>+#include <linux/suspend.h>
>+#include <asm/dcr.h>
>+#include <asm/dcr-native.h>
>+#include <asm/machdep.h>
>+
>+#define CPM_ER 0
>+#define CPM_FR 1
>+#define CPM_SR 2
>+
>+#define CPM_IDLE_WAIT 0
>+#define CPM_IDLE_DOZE 1
>+
>+struct cpm {
>+ dcr_host_t dcr_host;
>+ unsigned int dcr_offset[3];
>+ unsigned int powersave_off;
>+ unsigned int unused;
>+ unsigned int idle_doze;
>+ unsigned int standby;
>+ unsigned int suspend;
>+};
>+
>+static struct cpm cpm;
>+
>+struct cpm_idle_mode {
>+ unsigned int enabled;
>+ const char *name;
>+};
>+
>+static struct cpm_idle_mode idle_mode[] = {
>+ [CPM_IDLE_WAIT] = { 1, "wait" }, /* default */
>+ [CPM_IDLE_DOZE] = { 0, "doze" },
>+};
>+
>+static void cpm_set(unsigned int cpm_reg, unsigned int mask)
>+{
>+ unsigned int value;
>+
>+ value = dcr_read(cpm.dcr_host, cpm.dcr_offset[cpm_reg]);
>+ value |= mask;
>+ dcr_write(cpm.dcr_host, cpm.dcr_offset[cpm_reg], value);
>+}
This doesn't seem to do any sort of verification that the class 2 and
class 3 devices actually got disabled. Or at least I don't see where we
verify anything in the SR.
Maybe in practice it doesn't matter, but we should add a comment that say
we just expect them to eventually go off when they can.
>+
>+static void cpm_idle_wait(void)
>+{
>+ unsigned long msr_save;
>+
>+ /* save off initial state */
>+ msr_save = mfmsr();
>+ /* sync required when CPM0_ER[CPU] is set */
>+ mb();
>+ /* set wait state MSR */
>+ mtmsr(msr_save|MSR_WE|MSR_EE|MSR_CE|MSR_DE);
>+ isync();
>+ /* return to initial state */
>+ mtmsr(msr_save);
>+ isync();
>+}
>+
>+static void cpm_idle_sleep(unsigned int mask)
>+{
>+ unsigned int er_save;
>+
>+ /* update CPM_ER state */
>+ er_save = dcr_read(cpm.dcr_host, cpm.dcr_offset[CPM_ER]);
>+ dcr_write(cpm.dcr_host, cpm.dcr_offset[CPM_ER],
>+ er_save | mask);
>+
>+ /* go to wait state */
>+ cpm_idle_wait();
>+
>+ /* restore CPM_ER state */
>+ dcr_write(cpm.dcr_host, cpm.dcr_offset[CPM_ER], er_save);
>+}
For my clarification, the CPU is a class 2 device and we expect that the
logic between the CPU and the CPM allows us to do the write to make it
sleep, but not actually sleep until we set the idle state?
josh
^ permalink raw reply
* Re: [PATCH 1/3] Powerpc/4xx: Add suspend and idle support
From: Josh Boyer @ 2010-10-08 13:35 UTC (permalink / raw)
To: Victor Gallardo; +Cc: linuxppc-dev
In-Reply-To: <1286478436-9942-1-git-send-email-vgallardo@apm.com>
On Thu, Oct 07, 2010 at 12:07:16PM -0700, Victor Gallardo wrote:
>Add suspend/resume support for all 4xx compatible CPUs.
>See /sys/power/state for available power states configured in.
>
>Add two different idle states (idle-wait and idle-doze)
>controlled via sysfs. Default is idle-wait.
> cat /sys/devices/system/cpu/cpu0/idle
> [wait] doze
>
>To save additional power, use idle-doze.
> echo doze > /sys/devices/system/cpu/cpu0/idle
> cat /sys/devices/system/cpu/cpu0/idle
> wait [doze]
>
>Signed-off-by: Victor Gallardo <vgallardo@apm.com>
>---
> Documentation/powerpc/dts-bindings/4xx/cpm.txt | 43 +++
> arch/powerpc/Kconfig | 13 +-
> arch/powerpc/platforms/44x/Makefile | 5 +-
> arch/powerpc/sysdev/Makefile | 1 +
> arch/powerpc/sysdev/ppc4xx_cpm.c | 339 ++++++++++++++++++++++++
> 5 files changed, 397 insertions(+), 4 deletions(-)
> create mode 100644 Documentation/powerpc/dts-bindings/4xx/cpm.txt
> create mode 100644 arch/powerpc/sysdev/ppc4xx_cpm.c
>
>diff --git a/Documentation/powerpc/dts-bindings/4xx/cpm.txt b/Documentation/powerpc/dts-bindings/4xx/cpm.txt
>new file mode 100644
>index 0000000..9635df8
>--- /dev/null
>+++ b/Documentation/powerpc/dts-bindings/4xx/cpm.txt
>@@ -0,0 +1,43 @@
>+PPC4xx Clock Power Management (CPM) node
>+
>+Required properties:
>+ - compatible : compatible list, currently only "ibm,cpm"
>+ - dcr-access-method : "native"
>+ - dcr-reg : < DCR register range >
>+
>+Optional properties:
>+ - er-offset : All 4xx SoCs with a CPM controller have
>+ one of two different order for the CPM
>+ registers. Some have the CPM registers
>+ in the following order (ER,FR,SR). The
>+ others have them in the following order
>+ (SR,ER,FR). For the second case set
>+ er-offset = <1>.
>+ - unused-units : specifier consist of one cell. For each
>+ bit in the cell, the corresponding bit
>+ in CPM will be set to turn off unused
>+ devices.
>+ - idle-doze : specifier consist of one cell. For each
>+ bit in the cell, the corresponding bit
>+ in CPM will be set to turn off unused
>+ devices. This is usually just CPM[CPU].
>+ - standby : specifier consist of one cell. For each
>+ bit in the cell, the corresponding bit
>+ in CPM will be set on standby and
>+ restored on resume.
>+ - suspend : specifier consist of one cell. For each
>+ bit in the cell, the corresponding bit
>+ in CPM will be set on suspend (mem) and
>+ restored on resume.
So the difference, from what I can tell, between standby and suspend is
really only what devices are turned off. I don't see any code to put
the DRAM into self-refresh mode, etc. If that is the case, perhaps we
could add a bit of description as to the different kinds of devices that
may be disabled in one mode but not the other.
>+
>+Example:
>+ CPM0: cpm {
>+ compatible = "ibm,cpm";
>+ dcr-access-method = "native";
>+ dcr-reg = <0x160 0x003>;
>+ er-offset = <0>;
>+ unused-units = <0x00000100>;
>+ idle-doze = <0x02000000>;
>+ standby = <0xfeff0000>;
>+ standby = <0xfeff791d>;
One of these two should be illustrating suspend.
josh
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox