* Interrupt sense value in device tree
From: Vijay Nikam @ 2009-02-24 6:23 UTC (permalink / raw)
To: linuxppc-dev, Benjamin Herrenschmidt
Hello,
I created a device node for gpio-controller for evaluation board
mpc83313erdb. I created the dtb and loaded on target.
But in dts at interrupts = <74 0x2> I set sense as Edge (sense== 2:
Edge, high-to-low change), when I load the driver module and checked
the entry in /proc/interrupts it shows the sense as 'Level' i.e. 8
(sense == 8: Level, low assertion) as follows;
74: 1 IPIC Level gpio
Following is gpio-controller device tree node;
gpio-controller@c00 {
#gpio-cells = <2>;
compatible = "fsl,mpc8313-gpio", "fsl,mpc8349-gpio";
reg = <0xc00 0x100>;
interrupts = <74 0x2>;
interrupt-parent = <&ipic>;
gpio-controller;
};
Could anyone please tell what could be the reason ? ? ? Am I missing
something ? ? ?
Kindly please acknowledge ... thank you ...
Kind Regards,
Vijay Nikam
##################### Start ############################
cat /proc/interrupts
CPU0
16: 162 IPIC Level serial
19: 43 IPIC Level i2c-mpc
20: 1 IPIC Level i2c-mpc
22: 2 IPIC Edge phy_interrupt
32: 0 IPIC Level enet_error
33: 765 IPIC Level enet_rx
34: 3 IPIC Level enet_tx
35: 0 IPIC Level enet_error
36: 0 IPIC Level enet_rx
37: 15 IPIC Level enet_tx
38: 0 IPIC Edge ehci_hcd:usb1
72: 0 IPIC Level gtm timer
74: 1 IPIC Level gpio
77: 51451 IPIC Level fsl-elbc
80: 0 IPIC Level pmc
BAD: 0
###################### END ############################
^ permalink raw reply
* [PATCH] powerpc/kconfig: Kill PPC_MULTIPLATFORM
From: Benjamin Herrenschmidt @ 2009-02-24 5:56 UTC (permalink / raw)
To: linuxppc-dev
CONFIG_PPC_MULTIPLATFORM is a remain of the pre-powerpc days and isn't
really meaningful anymore. It was basically equivalent to PPC64 || 6xx.
This removes it along with the following changes:
- 32-bit platforms that relied on PPC32 && PPC_MULTIPLATFORM now rely
on 6xx which is what they want anyway.
- A new symbol, PPC_BOOK3S, is defined that represent compliance with
the "Server" variant of the architecture. This is set when either 6xx
or PPC64 is set and open the door for future BOOK3E 64-bit.
- 64-bit platforms that relied on PPC64 && PPC_MULTIPLATFORM now use
PPC64 && PPC_BOOK3S
- A separate and selectable CONFIG_PPC_OF_BOOT_TRAMPOLINE option is now
used to control the use of prom_init.c
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/Kconfig | 2 +-
arch/powerpc/Kconfig.debug | 2 +-
arch/powerpc/kernel/Makefile | 2 +-
arch/powerpc/kernel/head_32.S | 7 +++++--
arch/powerpc/kernel/head_64.S | 6 +++++-
arch/powerpc/platforms/512x/Kconfig | 4 ++--
arch/powerpc/platforms/52xx/Kconfig | 2 +-
arch/powerpc/platforms/82xx/Kconfig | 2 +-
arch/powerpc/platforms/83xx/Kconfig | 2 +-
arch/powerpc/platforms/86xx/Kconfig | 2 +-
arch/powerpc/platforms/Kconfig | 27 +++++++++++++++------------
arch/powerpc/platforms/Kconfig.cputype | 18 +++++++++++++-----
arch/powerpc/platforms/amigaone/Kconfig | 2 +-
arch/powerpc/platforms/cell/Kconfig | 6 +++---
arch/powerpc/platforms/chrp/Kconfig | 2 +-
arch/powerpc/platforms/embedded6xx/Kconfig | 2 +-
arch/powerpc/platforms/iseries/Kconfig | 2 +-
arch/powerpc/platforms/maple/Kconfig | 2 +-
arch/powerpc/platforms/pasemi/Kconfig | 2 +-
arch/powerpc/platforms/powermac/Kconfig | 2 +-
arch/powerpc/platforms/prep/Kconfig | 2 +-
arch/powerpc/platforms/ps3/Kconfig | 2 +-
arch/powerpc/platforms/pseries/Kconfig | 2 +-
23 files changed, 60 insertions(+), 42 deletions(-)
--- linux-work.orig/arch/powerpc/Kconfig 2009-02-24 15:04:13.000000000 +1100
+++ linux-work/arch/powerpc/Kconfig 2009-02-24 15:31:51.000000000 +1100
@@ -313,7 +313,7 @@ config ARCH_ENABLE_MEMORY_HOTREMOVE
config KEXEC
bool "kexec system call (EXPERIMENTAL)"
- depends on (PPC_PRPMC2800 || PPC_MULTIPLATFORM) && EXPERIMENTAL
+ depends on BOOK3S && EXPERIMENTAL
help
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
Index: linux-work/arch/powerpc/kernel/Makefile
===================================================================
--- linux-work.orig/arch/powerpc/kernel/Makefile 2009-02-24 15:05:49.000000000 +1100
+++ linux-work/arch/powerpc/kernel/Makefile 2009-02-24 15:12:20.000000000 +1100
@@ -75,7 +75,7 @@ obj-y += time.o prom.o traps.o setup-
obj-$(CONFIG_PPC32) += entry_32.o setup_32.o
obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o
obj-$(CONFIG_KGDB) += kgdb.o
-obj-$(CONFIG_PPC_MULTIPLATFORM) += prom_init.o
+obj-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o
obj-$(CONFIG_MODULES) += ppc_ksyms.o
obj-$(CONFIG_BOOTX_TEXT) += btext.o
obj-$(CONFIG_SMP) += smp.o
Index: linux-work/arch/powerpc/kernel/head_32.S
===================================================================
--- linux-work.orig/arch/powerpc/kernel/head_32.S 2009-02-24 15:12:55.000000000 +1100
+++ linux-work/arch/powerpc/kernel/head_32.S 2009-02-24 16:24:44.000000000 +1100
@@ -108,18 +108,21 @@ __start:
* because OF may have I/O devices mapped into that area
* (particularly on CHRP).
*/
-#ifdef CONFIG_PPC_MULTIPLATFORM
cmpwi 0,r5,0
beq 1f
+#ifdef CONFIG_PPC_OF_BOOT_TRAMPOLINE
/* find out where we are now */
bcl 20,31,$+4
0: mflr r8 /* r8 = runtime addr here */
addis r8,r8,(_stext - 0b)@ha
addi r8,r8,(_stext - 0b)@l /* current runtime base addr */
bl prom_init
+#endif /* CONFIG_PPC_OF_BOOT_TRAMPOLINE */
+
+ /* We never return. We also hit that trap if trying to boot
+ * from OF while CONFIG_PPC_OF_BOOT_TRAMPOLINE isn't selected */
trap
-#endif
/*
* Check for BootX signature when supporting PowerMac and branch to
Index: linux-work/arch/powerpc/platforms/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/Kconfig 2009-02-24 15:02:47.000000000 +1100
+++ linux-work/arch/powerpc/platforms/Kconfig 2009-02-24 15:35:02.000000000 +1100
@@ -1,14 +1,5 @@
menu "Platform support"
-config PPC_MULTIPLATFORM
- bool
- depends on PPC64 || 6xx
- default y
-
-config CLASSIC32
- def_bool y
- depends on 6xx && PPC_MULTIPLATFORM
-
source "arch/powerpc/platforms/pseries/Kconfig"
source "arch/powerpc/platforms/iseries/Kconfig"
source "arch/powerpc/platforms/chrp/Kconfig"
@@ -32,12 +23,24 @@ source "arch/powerpc/platforms/amigaone/
config PPC_NATIVE
bool
- depends on PPC_MULTIPLATFORM
+ depends on 6xx || PPC64
help
Support for running natively on the hardware, i.e. without
a hypervisor. This option is not user-selectable but should
be selected by all platforms that need it.
+config PPC_OF_BOOT_TRAMPOLINE
+ bool "Support booting from Open Firmware or yaboot"
+ depends on 6xx || PPC64
+ default y
+ help
+ Support from booting from Open Firmware or yaboot using an
+ Open Firmware client interface. This enables the kernel to
+ communicate with open firmware to retrieve system informations
+ such as the device tree.
+
+ In case of doubt, say Y
+
config UDBG_RTAS_CONSOLE
bool "RTAS based debug console"
depends on PPC_RTAS
@@ -71,7 +74,7 @@ config PPC_I8259
config U3_DART
bool
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64
default n
config PPC_RTAS
@@ -188,7 +191,7 @@ config PPC601_SYNC_FIX
config TAU
bool "On-chip CPU temperature sensor support"
- depends on CLASSIC32
+ depends on 6xx
help
G3 and G4 processors have an on-chip temperature sensor called the
'Thermal Assist Unit (TAU)', which, in theory, can measure the on-die
Index: linux-work/arch/powerpc/platforms/512x/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/512x/Kconfig 2009-02-24 15:13:42.000000000 +1100
+++ linux-work/arch/powerpc/platforms/512x/Kconfig 2009-02-24 15:14:02.000000000 +1100
@@ -12,7 +12,7 @@ config PPC_MPC5121
config MPC5121_ADS
bool "Freescale MPC5121E ADS"
- depends on PPC_MULTIPLATFORM && PPC32
+ depends on 6xx
select DEFAULT_UIMAGE
select PPC_MPC5121
select MPC5121_ADS_CPLD
@@ -21,7 +21,7 @@ config MPC5121_ADS
config MPC5121_GENERIC
bool "Generic support for simple MPC5121 based boards"
- depends on PPC_MULTIPLATFORM && PPC32
+ depends on 6xx
select DEFAULT_UIMAGE
select PPC_MPC5121
help
Index: linux-work/arch/powerpc/platforms/52xx/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/52xx/Kconfig 2009-02-24 15:14:15.000000000 +1100
+++ linux-work/arch/powerpc/platforms/52xx/Kconfig 2009-02-24 15:14:18.000000000 +1100
@@ -1,6 +1,6 @@
config PPC_MPC52xx
bool "52xx-based boards"
- depends on PPC_MULTIPLATFORM && PPC32
+ depends on 6xx
select PPC_CLOCK
select PPC_PCI_CHOICE
Index: linux-work/arch/powerpc/platforms/82xx/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/82xx/Kconfig 2009-02-24 15:14:27.000000000 +1100
+++ linux-work/arch/powerpc/platforms/82xx/Kconfig 2009-02-24 15:14:32.000000000 +1100
@@ -1,6 +1,6 @@
menuconfig PPC_82xx
bool "82xx-based boards (PQ II)"
- depends on 6xx && PPC_MULTIPLATFORM
+ depends on 6xx
if PPC_82xx
Index: linux-work/arch/powerpc/platforms/83xx/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/83xx/Kconfig 2009-02-24 15:14:38.000000000 +1100
+++ linux-work/arch/powerpc/platforms/83xx/Kconfig 2009-02-24 15:14:43.000000000 +1100
@@ -1,6 +1,6 @@
menuconfig PPC_83xx
bool "83xx-based boards"
- depends on 6xx && PPC_MULTIPLATFORM
+ depends on 6xx
select PPC_UDBG_16550
select PPC_PCI_CHOICE
select FSL_PCI if PCI
Index: linux-work/arch/powerpc/platforms/86xx/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/86xx/Kconfig 2009-02-24 15:14:52.000000000 +1100
+++ linux-work/arch/powerpc/platforms/86xx/Kconfig 2009-02-24 15:14:58.000000000 +1100
@@ -1,7 +1,7 @@
config PPC_86xx
menuconfig PPC_86xx
bool "86xx-based boards"
- depends on 6xx && PPC_MULTIPLATFORM
+ depends on 6xx
select FSL_SOC
select ALTIVEC
help
Index: linux-work/arch/powerpc/platforms/Kconfig.cputype
===================================================================
--- linux-work.orig/arch/powerpc/platforms/Kconfig.cputype 2009-02-24 15:22:46.000000000 +1100
+++ linux-work/arch/powerpc/platforms/Kconfig.cputype 2009-02-24 15:35:09.000000000 +1100
@@ -57,9 +57,17 @@ config E200
endchoice
+# Until we have a choice of exclusive CPU types on 64-bit, we always
+# use PPC_BOOK3S. On 32-bit, this is equivalent to 6xx which is
+# "classic" MMU
+
+config PPC_BOOK3S
+ def_bool y
+ depends on PPC64 || 6xx
+
config POWER4_ONLY
bool "Optimize for POWER4"
- depends on PPC64
+ depends on PPC64 && PPC_BOOK3S
default n
---help---
Cause the compiler to optimize for POWER4/POWER5/PPC970 processors.
@@ -68,16 +76,16 @@ config POWER4_ONLY
config POWER3
bool
- depends on PPC64
+ depends on PPC64 && PPC_BOOK3S
default y if !POWER4_ONLY
config POWER4
- depends on PPC64
+ depends on PPC64 && PPC_BOOK3S
def_bool y
config TUNE_CELL
bool "Optimize for Cell Broadband Engine"
- depends on PPC64
+ depends on PPC64 && PPC_BOOK3S
help
Cause the compiler to optimize for the PPE of the Cell Broadband
Engine. This will make the code run considerably faster on Cell
@@ -147,7 +155,7 @@ config PHYS_64BIT
config ALTIVEC
bool "AltiVec Support"
- depends on CLASSIC32 || POWER4
+ depends on 6xx || POWER4
---help---
This option enables kernel support for the Altivec extensions to the
PowerPC processor. The kernel currently supports saving and restoring
Index: linux-work/arch/powerpc/platforms/amigaone/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/amigaone/Kconfig 2009-02-24 15:27:56.000000000 +1100
+++ linux-work/arch/powerpc/platforms/amigaone/Kconfig 2009-02-24 15:28:08.000000000 +1100
@@ -1,6 +1,6 @@
config AMIGAONE
bool "Eyetech AmigaOne/MAI Teron"
- depends on PPC32 && BROKEN_ON_SMP && PPC_MULTIPLATFORM
+ depends on 6xx && BROKEN_ON_SMP
select PPC_I8259
select PPC_INDIRECT_PCI
select PPC_UDBG_16550
Index: linux-work/arch/powerpc/platforms/cell/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/cell/Kconfig 2009-02-24 15:15:08.000000000 +1100
+++ linux-work/arch/powerpc/platforms/cell/Kconfig 2009-02-24 15:25:15.000000000 +1100
@@ -23,7 +23,7 @@ config PPC_CELL_NATIVE
config PPC_IBM_CELL_BLADE
bool "IBM Cell Blade"
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64 && PPC_BOOK3S
select PPC_CELL_NATIVE
select MMIO_NVRAM
select PPC_UDBG_16550
@@ -31,7 +31,7 @@ config PPC_IBM_CELL_BLADE
config PPC_CELLEB
bool "Toshiba's Cell Reference Set 'Celleb' Architecture"
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64 && PPC_BOOK3S
select PPC_CELL_NATIVE
select HAS_TXX9_SERIAL
select PPC_UDBG_BEAT
@@ -40,7 +40,7 @@ config PPC_CELLEB
config PPC_CELL_QPACE
bool "IBM Cell - QPACE"
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64 && PPC_BOOK3S
select PPC_CELL_COMMON
menu "Cell Broadband Engine options"
Index: linux-work/arch/powerpc/platforms/chrp/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/chrp/Kconfig 2009-02-24 15:15:31.000000000 +1100
+++ linux-work/arch/powerpc/platforms/chrp/Kconfig 2009-02-24 15:15:44.000000000 +1100
@@ -1,6 +1,6 @@
config PPC_CHRP
bool "Common Hardware Reference Platform (CHRP) based machines"
- depends on PPC_MULTIPLATFORM && PPC32
+ depends on 6xx
select MPIC
select PPC_I8259
select PPC_INDIRECT_PCI
Index: linux-work/arch/powerpc/platforms/embedded6xx/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/embedded6xx/Kconfig 2009-02-24 15:15:58.000000000 +1100
+++ linux-work/arch/powerpc/platforms/embedded6xx/Kconfig 2009-02-24 15:16:21.000000000 +1100
@@ -1,6 +1,6 @@
config EMBEDDED6xx
bool "Embedded 6xx/7xx/7xxx-based boards"
- depends on PPC32 && BROKEN_ON_SMP && PPC_MULTIPLATFORM
+ depends on 6xx && BROKEN_ON_SMP
config LINKSTATION
bool "Linkstation / Kurobox(HG) from Buffalo"
Index: linux-work/arch/powerpc/platforms/iseries/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/iseries/Kconfig 2009-02-24 15:21:42.000000000 +1100
+++ linux-work/arch/powerpc/platforms/iseries/Kconfig 2009-02-24 15:25:35.000000000 +1100
@@ -1,6 +1,6 @@
config PPC_ISERIES
bool "IBM Legacy iSeries"
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64 && PPC_BOOK3S
select PPC_INDIRECT_IO
select PPC_PCI_CHOICE if EMBEDDED
Index: linux-work/arch/powerpc/platforms/maple/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/maple/Kconfig 2009-02-24 15:22:00.000000000 +1100
+++ linux-work/arch/powerpc/platforms/maple/Kconfig 2009-02-24 15:25:41.000000000 +1100
@@ -1,5 +1,5 @@
config PPC_MAPLE
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64 && PPC_BOOK3S
bool "Maple 970FX Evaluation Board"
select PCI
select MPIC
Index: linux-work/arch/powerpc/platforms/pasemi/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/pasemi/Kconfig 2009-02-24 15:22:18.000000000 +1100
+++ linux-work/arch/powerpc/platforms/pasemi/Kconfig 2009-02-24 15:25:50.000000000 +1100
@@ -1,5 +1,5 @@
config PPC_PASEMI
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64 && PPC_BOOK3S
bool "PA Semi SoC-based platforms"
default n
select MPIC
Index: linux-work/arch/powerpc/platforms/powermac/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/powermac/Kconfig 2009-02-24 15:26:00.000000000 +1100
+++ linux-work/arch/powerpc/platforms/powermac/Kconfig 2009-02-24 15:26:11.000000000 +1100
@@ -1,6 +1,6 @@
config PPC_PMAC
bool "Apple PowerMac based machines"
- depends on PPC_MULTIPLATFORM
+ depends on PPC_BOOK3S
select MPIC
select PCI
select PPC_INDIRECT_PCI if PPC32
Index: linux-work/arch/powerpc/platforms/prep/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/prep/Kconfig 2009-02-24 15:26:55.000000000 +1100
+++ linux-work/arch/powerpc/platforms/prep/Kconfig 2009-02-24 15:27:04.000000000 +1100
@@ -1,6 +1,6 @@
config PPC_PREP
bool "PowerPC Reference Platform (PReP) based machines"
- depends on PPC_MULTIPLATFORM && PPC32 && BROKEN
+ depends on 6xx && BROKEN
select MPIC
select PPC_I8259
select PPC_INDIRECT_PCI
Index: linux-work/arch/powerpc/platforms/ps3/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/ps3/Kconfig 2009-02-24 15:27:20.000000000 +1100
+++ linux-work/arch/powerpc/platforms/ps3/Kconfig 2009-02-24 15:27:31.000000000 +1100
@@ -1,6 +1,6 @@
config PPC_PS3
bool "Sony PS3"
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64 && PPC_BOOK3S
select PPC_CELL
select USB_ARCH_HAS_OHCI
select USB_OHCI_LITTLE_ENDIAN
Index: linux-work/arch/powerpc/platforms/pseries/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/platforms/pseries/Kconfig 2009-02-24 15:27:36.000000000 +1100
+++ linux-work/arch/powerpc/platforms/pseries/Kconfig 2009-02-24 15:27:47.000000000 +1100
@@ -1,5 +1,5 @@
config PPC_PSERIES
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64 && PPC_BOOK3S
bool "IBM pSeries & new (POWER5-based) iSeries"
select MPIC
select PPC_I8259
Index: linux-work/arch/powerpc/Kconfig.debug
===================================================================
--- linux-work.orig/arch/powerpc/Kconfig.debug 2009-02-24 15:30:26.000000000 +1100
+++ linux-work/arch/powerpc/Kconfig.debug 2009-02-24 15:31:01.000000000 +1100
@@ -129,7 +129,7 @@ config BDI_SWITCH
config BOOTX_TEXT
bool "Support for early boot text console (BootX or OpenFirmware only)"
- depends on PPC_OF && PPC_MULTIPLATFORM
+ depends on PPC_OF && PPC_BOOK3S
help
Say Y here to see progress messages from the boot firmware in text
mode. Requires either BootX or Open Firmware.
Index: linux-work/arch/powerpc/kernel/head_64.S
===================================================================
--- linux-work.orig/arch/powerpc/kernel/head_64.S 2009-02-24 16:23:30.000000000 +1100
+++ linux-work/arch/powerpc/kernel/head_64.S 2009-02-24 16:25:07.000000000 +1100
@@ -1360,6 +1360,7 @@ _GLOBAL(__start_initialization_multiplat
b .__after_prom_start
_INIT_STATIC(__boot_from_prom)
+#ifdef CONFIG_PPC_OF_BOOT_TRAMPOLINE
/* Save parameters */
mr r31,r3
mr r30,r4
@@ -1390,7 +1391,10 @@ _INIT_STATIC(__boot_from_prom)
/* Do all of the interaction with OF client interface */
mr r8,r26
bl .prom_init
- /* We never return */
+#endif /* #CONFIG_PPC_OF_BOOT_TRAMPOLINE */
+
+ /* We never return. We also hit that trap if trying to boot
+ * from OF while CONFIG_PPC_OF_BOOT_TRAMPOLINE isn't selected */
trap
_STATIC(__after_prom_start)
^ permalink raw reply
* Interrupt sense value in device tree
From: Vijay Nikam @ 2009-02-24 4:19 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Timur Tabi
Hello,
I created a device node for gpio-controller for evaluation board
mpc83313erdb. I created the dtb and loaded on target.
But in dts at interrupts = <74 0x2> I set sense as Edge (sense== 2:
Edge, high-to-low change), when I load the driver module and checked
the entry in /proc/interrupts it shows the sense as 'Level' i.e. 8
(sense == 8: Level, low assertion) as follows;
74: 1 IPIC Level gpio
Following is gpio-controller device tree node;
gpio-controller@c00 {
#gpio-cells = <2>;
compatible = "fsl,mpc8313-gpio", "fsl,mpc8349-gpio";
reg = <0xc00 0x100>;
interrupts = <74 0x2>;
interrupt-parent = <&ipic>;
gpio-controller;
};
Could anyone please tell what could be the reason ? ? ? Am I missing
something ? ? ?
Kindly please acknowledge ... thank you ...
Kind Regards,
Vijay Nikam
##################### Start ############################
cat /proc/interrupts
CPU0
16: 162 IPIC Level serial
19: 43 IPIC Level i2c-mpc
20: 1 IPIC Level i2c-mpc
22: 2 IPIC Edge phy_interrupt
32: 0 IPIC Level enet_error
33: 765 IPIC Level enet_rx
34: 3 IPIC Level enet_tx
35: 0 IPIC Level enet_error
36: 0 IPIC Level enet_rx
37: 15 IPIC Level enet_tx
38: 0 IPIC Edge ehci_hcd:usb1
72: 0 IPIC Level gtm timer
74: 1 IPIC Level gpio
77: 51451 IPIC Level fsl-elbc
80: 0 IPIC Level pmc
BAD: 0
###################### END ############################
^ permalink raw reply
* RE: soc vs localbus for flash in device tree
From: Dushara Jayasinghe @ 2009-02-24 3:47 UTC (permalink / raw)
To: linuxppc-dev@ozlabs.org; +Cc: 'Michael Bergandi', 'Ira Snyder'
In-Reply-To: <20090224000641.GB578@ovro.caltech.edu>
> > Does the flash node HAVE to be within a localbus node or can it
> reside within the soc node? I've been basing my work on mpc834x_mds.dts
> > And hence haven't defined a localbus node.
> >
>=20
> I'm also working on a custom board based on MPC8349EMDS, and have based
> my dts off of mpc834x_mds.dts. I've added the localbus node with my
> chipselects and flash. It works, but there are no guarantees of
> correctness.
>=20
This worked for me. Thanks.
D
^ permalink raw reply
* [PATCH] Remove unused asm-offsets entries for cpu_spec
From: Michael Ellerman @ 2009-02-24 3:40 UTC (permalink / raw)
To: linuxppc-dev
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/kernel/asm-offsets.c | 3 ---
1 files changed, 0 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 9937fe4..addac07 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -280,9 +280,6 @@ int main(void)
#endif /* ! CONFIG_PPC64 */
/* About the CPU features table */
- DEFINE(CPU_SPEC_ENTRY_SIZE, sizeof(struct cpu_spec));
- DEFINE(CPU_SPEC_PVR_MASK, offsetof(struct cpu_spec, pvr_mask));
- DEFINE(CPU_SPEC_PVR_VALUE, offsetof(struct cpu_spec, pvr_value));
DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features));
DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup));
DEFINE(CPU_SPEC_RESTORE, offsetof(struct cpu_spec, cpu_restore));
--
1.5.5
^ permalink raw reply related
* 2.6.29-rc6-rt2 fails for powerpc
From: jeff angielski @ 2009-02-24 3:28 UTC (permalink / raw)
To: linuxppc-dev
1. Git clone of the latest 2.6.29-rc6.
2. Applied the 2.6.29-rc6-rt2 patch.
3. Copied XXX config from arch/powerpc/configs to .config
4. make -j2 ARCH=powerpc oldconfig
5. make -j2 ARCH=powerpc
It fails with redefinition errors of the rwsem stuff from
arch/powerpc/include/asm/rwsem.h and include/linux/rt_lock.h.
Anybody else having this problem? Google was not my friend, nor the RT
Wiki...
The detailed compile output is here:
$ make -j2 ARCH=powerpc
scripts/kconfig/conf -s arch/powerpc/Kconfig
CHK include/linux/version.h
CHK include/linux/utsrelease.h
SYMLINK include/asm -> include/asm-powerpc
CC arch/powerpc/kernel/asm-offsets.s
In file included from include/linux/rwsem.h:37,
from include/linux/mm_types.h:11,
from include/linux/sched.h:61,
from arch/powerpc/kernel/asm-offsets.c:17:
/home/jaa/src/linux-2.6/arch/powerpc/include/asm/rwsem.h:24: error: redefinition of 'struct rw_semaphore'
In file included from include/linux/rwsem.h:37,
from include/linux/mm_types.h:11,
from include/linux/sched.h:61,
from arch/powerpc/kernel/asm-offsets.c:17:
/home/jaa/src/linux-2.6/arch/powerpc/include/asm/rwsem.h:46:1: warning: "__RWSEM_INITIALIZER" redefined
In file included from include/linux/spinlock.h:118,
from include/linux/seqlock.h:34,
from include/linux/time.h:8,
from include/linux/timex.h:56,
from include/linux/sched.h:54,
from arch/powerpc/kernel/asm-offsets.c:17:
include/linux/rt_lock.h:191:1: warning: this is the location of the previous definition
In file included from include/linux/rwsem.h:37,
from include/linux/mm_types.h:11,
from include/linux/sched.h:61,
from arch/powerpc/kernel/asm-offsets.c:17:
/home/jaa/src/linux-2.6/arch/powerpc/include/asm/rwsem.h:50:1: warning: "DECLARE_RWSEM" redefined
In file included from include/linux/spinlock.h:118,
from include/linux/seqlock.h:34,
from include/linux/time.h:8,
from include/linux/timex.h:56,
from include/linux/sched.h:54,
from arch/powerpc/kernel/asm-offsets.c:17:
include/linux/rt_lock.h:195:1: warning: this is the location of the previous definition
In file included from include/linux/rwsem.h:37,
from include/linux/mm_types.h:11,
from include/linux/sched.h:61,
from arch/powerpc/kernel/asm-offsets.c:17:
/home/jaa/src/linux-2.6/arch/powerpc/include/asm/rwsem.h:61:1: warning: "init_rwsem" redefined
In file included from include/linux/spinlock.h:118,
from include/linux/seqlock.h:34,
from include/linux/time.h:8,
from include/linux/timex.h:56,
from include/linux/sched.h:54,
from arch/powerpc/kernel/asm-offsets.c:17:
include/linux/rt_lock.h:232:1: warning: this is the location of the previous definition
/home/jaa/src/linux-2.6/arch/powerpc/include/asm/rwsem.h:167: error: expected identifier or '(' before '{' token
make[1]: *** [arch/powerpc/kernel/asm-offsets.s] Error 1
make: *** [prepare0] Error 2
--
Jeff Angielski
The PTR Group
www.theptrgroup.com
^ permalink raw reply
* [PATCH] powerpc: Split the various pgtable-* headers based on MMU type
From: Benjamin Herrenschmidt @ 2009-02-24 2:56 UTC (permalink / raw)
To: linuxppc-dev
This patch moves the definition of the PTE format for each MMU type
to separate files instead of all in one file. This improves overall
maintainability and will make it easier to add new types.
There are still some type-related ifdef's in the generic headers,
we might remove them in the long run, but this page shouldn't result
in any code change, just definitions being moved around.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/pgtable-40x.h | 64 +++++
arch/powerpc/include/asm/pgtable-44x.h | 102 ++++++++
arch/powerpc/include/asm/pgtable-8xx.h | 64 +++++
arch/powerpc/include/asm/pgtable-fsl-booke.h | 46 +++
arch/powerpc/include/asm/pgtable-hash32.h | 49 +++
arch/powerpc/include/asm/pgtable-hash64-4k.h | 117 +++++++++
arch/powerpc/include/asm/pgtable-hash64-64k.h | 155 ++++++++++++
arch/powerpc/include/asm/pgtable-hash64.h | 47 +++
arch/powerpc/include/asm/pgtable-ppc32.h | 321 +-------------------------
arch/powerpc/include/asm/pgtable-ppc64.h | 53 ----
10 files changed, 676 insertions(+), 342 deletions(-)
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/include/asm/pgtable-40x.h 2009-02-24 11:37:41.000000000 +1100
@@ -0,0 +1,64 @@
+#ifndef _ASM_POWERPC_PGTABLE_40x_H
+#define _ASM_POWERPC_PGTABLE_40x_H
+#ifdef __KERNEL__
+
+/*
+ * At present, all PowerPC 400-class processors share a similar TLB
+ * architecture. The instruction and data sides share a unified,
+ * 64-entry, fully-associative TLB which is maintained totally under
+ * software control. In addition, the instruction side has a
+ * hardware-managed, 4-entry, fully-associative TLB which serves as a
+ * first level to the shared TLB. These two TLBs are known as the UTLB
+ * and ITLB, respectively (see "mmu.h" for definitions).
+ *
+ * There are several potential gotchas here. The 40x hardware TLBLO
+ * field looks like this:
+ *
+ * 0 1 2 3 4 ... 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+ * RPN..................... 0 0 EX WR ZSEL....... W I M G
+ *
+ * Where possible we make the Linux PTE bits match up with this
+ *
+ * - bits 20 and 21 must be cleared, because we use 4k pages (40x can
+ * support down to 1k pages), this is done in the TLBMiss exception
+ * handler.
+ * - We use only zones 0 (for kernel pages) and 1 (for user pages)
+ * of the 16 available. Bit 24-26 of the TLB are cleared in the TLB
+ * miss handler. Bit 27 is PAGE_USER, thus selecting the correct
+ * zone.
+ * - PRESENT *must* be in the bottom two bits because swap cache
+ * entries use the top 30 bits. Because 40x doesn't support SMP
+ * anyway, M is irrelevant so we borrow it for PAGE_PRESENT. Bit 30
+ * is cleared in the TLB miss handler before the TLB entry is loaded.
+ * - All other bits of the PTE are loaded into TLBLO without
+ * modification, leaving us only the bits 20, 21, 24, 25, 26, 30 for
+ * software PTE bits. We actually use use bits 21, 24, 25, and
+ * 30 respectively for the software bits: ACCESSED, DIRTY, RW, and
+ * PRESENT.
+ */
+
+#define _PAGE_GUARDED 0x001 /* G: page is guarded from prefetch */
+#define _PAGE_FILE 0x001 /* when !present: nonlinear file mapping */
+#define _PAGE_PRESENT 0x002 /* software: PTE contains a translation */
+#define _PAGE_NO_CACHE 0x004 /* I: caching is inhibited */
+#define _PAGE_WRITETHRU 0x008 /* W: caching is write-through */
+#define _PAGE_USER 0x010 /* matches one of the zone permission bits */
+#define _PAGE_RW 0x040 /* software: Writes permitted */
+#define _PAGE_DIRTY 0x080 /* software: dirty page */
+#define _PAGE_HWWRITE 0x100 /* hardware: Dirty & RW, set in exception */
+#define _PAGE_HWEXEC 0x200 /* hardware: EX permission */
+#define _PAGE_ACCESSED 0x400 /* software: R: page referenced */
+
+#define _PMD_PRESENT 0x400 /* PMD points to page of PTEs */
+#define _PMD_BAD 0x802
+#define _PMD_SIZE 0x0e0 /* size field, != 0 for large-page PMD entry */
+#define _PMD_SIZE_4M 0x0c0
+#define _PMD_SIZE_16M 0x0e0
+
+#define PMD_PAGE_SIZE(pmdval) (1024 << (((pmdval) & _PMD_SIZE) >> 4))
+
+/* Until my rework is finished, 40x still needs atomic PTE updates */
+#define PTE_ATOMIC_UPDATES 1
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_PGTABLE_40x_H */
Index: linux-work/arch/powerpc/include/asm/pgtable-44x.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/include/asm/pgtable-44x.h 2009-02-24 11:40:02.000000000 +1100
@@ -0,0 +1,102 @@
+#ifndef _ASM_POWERPC_PGTABLE_44x_H
+#define _ASM_POWERPC_PGTABLE_44x_H
+#ifdef __KERNEL__
+
+/*
+ * Definitions for PPC440
+ *
+ * Because of the 3 word TLB entries to support 36-bit addressing,
+ * the attribute are difficult to map in such a fashion that they
+ * are easily loaded during exception processing. I decided to
+ * organize the entry so the ERPN is the only portion in the
+ * upper word of the PTE and the attribute bits below are packed
+ * in as sensibly as they can be in the area below a 4KB page size
+ * oriented RPN. This at least makes it easy to load the RPN and
+ * ERPN fields in the TLB. -Matt
+ *
+ * This isn't entirely true anymore, at least some bits are now
+ * easier to move into the TLB from the PTE. -BenH.
+ *
+ * Note that these bits preclude future use of a page size
+ * less than 4KB.
+ *
+ *
+ * PPC 440 core has following TLB attribute fields;
+ *
+ * TLB1:
+ * 0 1 2 3 4 ... 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+ * RPN................................. - - - - - - ERPN.......
+ *
+ * TLB2:
+ * 0 1 2 3 4 ... 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+ * - - - - - - U0 U1 U2 U3 W I M G E - UX UW UR SX SW SR
+ *
+ * Newer 440 cores (440x6 as used on AMCC 460EX/460GT) have additional
+ * TLB2 storage attibute fields. Those are:
+ *
+ * TLB2:
+ * 0...10 11 12 13 14 15 16...31
+ * no change WL1 IL1I IL1D IL2I IL2D no change
+ *
+ * There are some constrains and options, to decide mapping software bits
+ * into TLB entry.
+ *
+ * - PRESENT *must* be in the bottom three bits because swap cache
+ * entries use the top 29 bits for TLB2.
+ *
+ * - FILE *must* be in the bottom three bits because swap cache
+ * entries use the top 29 bits for TLB2.
+ *
+ * - CACHE COHERENT bit (M) has no effect on original PPC440 cores,
+ * because it doesn't support SMP. However, some later 460 variants
+ * have -some- form of SMP support and so I keep the bit there for
+ * future use
+ *
+ * With the PPC 44x Linux implementation, the 0-11th LSBs of the PTE are used
+ * for memory protection related functions (see PTE structure in
+ * include/asm-ppc/mmu.h). The _PAGE_XXX definitions in this file map to the
+ * above bits. Note that the bit values are CPU specific, not architecture
+ * specific.
+ *
+ * The kernel PTE entry holds an arch-dependent swp_entry structure under
+ * certain situations. In other words, in such situations some portion of
+ * the PTE bits are used as a swp_entry. In the PPC implementation, the
+ * 3-24th LSB are shared with swp_entry, however the 0-2nd three LSB still
+ * hold protection values. That means the three protection bits are
+ * reserved for both PTE and SWAP entry at the most significant three
+ * LSBs.
+ *
+ * There are three protection bits available for SWAP entry:
+ * _PAGE_PRESENT
+ * _PAGE_FILE
+ * _PAGE_HASHPTE (if HW has)
+ *
+ * So those three bits have to be inside of 0-2nd LSB of PTE.
+ *
+ */
+
+#define _PAGE_PRESENT 0x00000001 /* S: PTE valid */
+#define _PAGE_RW 0x00000002 /* S: Write permission */
+#define _PAGE_FILE 0x00000004 /* S: nonlinear file mapping */
+#define _PAGE_HWEXEC 0x00000004 /* H: Execute permission */
+#define _PAGE_ACCESSED 0x00000008 /* S: Page referenced */
+#define _PAGE_DIRTY 0x00000010 /* S: Page dirty */
+#define _PAGE_SPECIAL 0x00000020 /* S: Special page */
+#define _PAGE_USER 0x00000040 /* S: User page */
+#define _PAGE_ENDIAN 0x00000080 /* H: E bit */
+#define _PAGE_GUARDED 0x00000100 /* H: G bit */
+#define _PAGE_COHERENT 0x00000200 /* H: M bit */
+#define _PAGE_NO_CACHE 0x00000400 /* H: I bit */
+#define _PAGE_WRITETHRU 0x00000800 /* H: W bit */
+
+/* TODO: Add large page lowmem mapping support */
+#define _PMD_PRESENT 0
+#define _PMD_PRESENT_MASK (PAGE_MASK)
+#define _PMD_BAD (~PAGE_MASK)
+
+/* ERPN in a PTE never gets cleared, ignore it */
+#define _PTE_NONE_MASK 0xffffffff00000000ULL
+
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_PGTABLE_44x_H */
Index: linux-work/arch/powerpc/include/asm/pgtable-8xx.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/include/asm/pgtable-8xx.h 2009-02-24 11:35:17.000000000 +1100
@@ -0,0 +1,64 @@
+#ifndef _ASM_POWERPC_PGTABLE_8xx_H
+#define _ASM_POWERPC_PGTABLE_8xx_H
+#ifdef __KERNEL__
+
+/*
+ * The PowerPC MPC8xx uses a TLB with hardware assisted, software tablewalk.
+ * We also use the two level tables, but we can put the real bits in them
+ * needed for the TLB and tablewalk. These definitions require Mx_CTR.PPM = 0,
+ * Mx_CTR.PPCS = 0, and MD_CTR.TWAM = 1. The level 2 descriptor has
+ * additional page protection (when Mx_CTR.PPCS = 1) that allows TLB hit
+ * based upon user/super access. The TLB does not have accessed nor write
+ * protect. We assume that if the TLB get loaded with an entry it is
+ * accessed, and overload the changed bit for write protect. We use
+ * two bits in the software pte that are supposed to be set to zero in
+ * the TLB entry (24 and 25) for these indicators. Although the level 1
+ * descriptor contains the guarded and writethrough/copyback bits, we can
+ * set these at the page level since they get copied from the Mx_TWC
+ * register when the TLB entry is loaded. We will use bit 27 for guard, since
+ * that is where it exists in the MD_TWC, and bit 26 for writethrough.
+ * These will get masked from the level 2 descriptor at TLB load time, and
+ * copied to the MD_TWC before it gets loaded.
+ * Large page sizes added. We currently support two sizes, 4K and 8M.
+ * This also allows a TLB hander optimization because we can directly
+ * load the PMD into MD_TWC. The 8M pages are only used for kernel
+ * mapping of well known areas. The PMD (PGD) entries contain control
+ * flags in addition to the address, so care must be taken that the
+ * software no longer assumes these are only pointers.
+ */
+
+/* Definitions for 8xx embedded chips. */
+#define _PAGE_PRESENT 0x0001 /* Page is valid */
+#define _PAGE_FILE 0x0002 /* when !present: nonlinear file mapping */
+#define _PAGE_NO_CACHE 0x0002 /* I: cache inhibit */
+#define _PAGE_SHARED 0x0004 /* No ASID (context) compare */
+
+/* These five software bits must be masked out when the entry is loaded
+ * into the TLB.
+ */
+#define _PAGE_EXEC 0x0008 /* software: i-cache coherency required */
+#define _PAGE_GUARDED 0x0010 /* software: guarded access */
+#define _PAGE_DIRTY 0x0020 /* software: page changed */
+#define _PAGE_RW 0x0040 /* software: user write access allowed */
+#define _PAGE_ACCESSED 0x0080 /* software: page referenced */
+
+/* Setting any bits in the nibble with the follow two controls will
+ * require a TLB exception handler change. It is assumed unused bits
+ * are always zero.
+ */
+#define _PAGE_HWWRITE 0x0100 /* h/w write enable: never set in Linux PTE */
+#define _PAGE_USER 0x0800 /* One of the PP bits, the other is USER&~RW */
+
+#define _PMD_PRESENT 0x0001
+#define _PMD_BAD 0x0ff0
+#define _PMD_PAGE_MASK 0x000c
+#define _PMD_PAGE_8M 0x000c
+
+#define _PTE_NONE_MASK _PAGE_ACCESSED
+
+/* Until my rework is finished, 8xx still needs atomic PTE updates */
+#define PTE_ATOMIC_UPDATES 1
+
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_PGTABLE_8xx_H */
Index: linux-work/arch/powerpc/include/asm/pgtable-fsl-booke.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/include/asm/pgtable-fsl-booke.h 2009-02-24 11:41:32.000000000 +1100
@@ -0,0 +1,46 @@
+#ifndef _ASM_POWERPC_PGTABLE_FSL_BOOKE_H
+#define _ASM_POWERPC_PGTABLE_FSL_BOOKE_H
+#ifdef __KERNEL__
+
+/* PTE bit definitions for Freescale BookE SW loaded TLB MMU based
+ * processors
+ *
+ MMU Assist Register 3:
+
+ 32 33 34 35 36 ... 50 51 52 53 54 55 56 57 58 59 60 61 62 63
+ RPN...................... 0 0 U0 U1 U2 U3 UX SX UW SW UR SR
+
+ - PRESENT *must* be in the bottom three bits because swap cache
+ entries use the top 29 bits.
+
+ - FILE *must* be in the bottom three bits because swap cache
+ entries use the top 29 bits.
+*/
+
+/* Definitions for FSL Book-E Cores */
+#define _PAGE_PRESENT 0x00001 /* S: PTE contains a translation */
+#define _PAGE_USER 0x00002 /* S: User page (maps to UR) */
+#define _PAGE_FILE 0x00002 /* S: when !present: nonlinear file mapping */
+#define _PAGE_RW 0x00004 /* S: Write permission (SW) */
+#define _PAGE_DIRTY 0x00008 /* S: Page dirty */
+#define _PAGE_HWEXEC 0x00010 /* H: SX permission */
+#define _PAGE_ACCESSED 0x00020 /* S: Page referenced */
+
+#define _PAGE_ENDIAN 0x00040 /* H: E bit */
+#define _PAGE_GUARDED 0x00080 /* H: G bit */
+#define _PAGE_COHERENT 0x00100 /* H: M bit */
+#define _PAGE_NO_CACHE 0x00200 /* H: I bit */
+#define _PAGE_WRITETHRU 0x00400 /* H: W bit */
+#define _PAGE_SPECIAL 0x00800 /* S: Special page */
+
+#ifdef CONFIG_PTE_64BIT
+/* ERPN in a PTE never gets cleared, ignore it */
+#define _PTE_NONE_MASK 0xffffffffffff0000ULL
+#endif
+
+#define _PMD_PRESENT 0
+#define _PMD_PRESENT_MASK (PAGE_MASK)
+#define _PMD_BAD (~PAGE_MASK)
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_PGTABLE_FSL_BOOKE_H */
Index: linux-work/arch/powerpc/include/asm/pgtable-hash32.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/include/asm/pgtable-hash32.h 2009-02-24 11:26:58.000000000 +1100
@@ -0,0 +1,49 @@
+#ifndef _ASM_POWERPC_PGTABLE_HASH32_H
+#define _ASM_POWERPC_PGTABLE_HASH32_H
+#ifdef __KERNEL__
+
+/*
+ * The "classic" 32-bit implementation of the PowerPC MMU uses a hash
+ * table containing PTEs, together with a set of 16 segment registers,
+ * to define the virtual to physical address mapping.
+ *
+ * We use the hash table as an extended TLB, i.e. a cache of currently
+ * active mappings. We maintain a two-level page table tree, much
+ * like that used by the i386, for the sake of the Linux memory
+ * management code. Low-level assembler code in hash_low_32.S
+ * (procedure hash_page) is responsible for extracting ptes from the
+ * tree and putting them into the hash table when necessary, and
+ * updating the accessed and modified bits in the page table tree.
+ */
+
+#define _PAGE_PRESENT 0x001 /* software: pte contains a translation */
+#define _PAGE_HASHPTE 0x002 /* hash_page has made an HPTE for this pte */
+#define _PAGE_FILE 0x004 /* when !present: nonlinear file mapping */
+#define _PAGE_USER 0x004 /* usermode access allowed */
+#define _PAGE_GUARDED 0x008 /* G: prohibit speculative access */
+#define _PAGE_COHERENT 0x010 /* M: enforce memory coherence (SMP systems) */
+#define _PAGE_NO_CACHE 0x020 /* I: cache inhibit */
+#define _PAGE_WRITETHRU 0x040 /* W: cache write-through */
+#define _PAGE_DIRTY 0x080 /* C: page changed */
+#define _PAGE_ACCESSED 0x100 /* R: page referenced */
+#define _PAGE_EXEC 0x200 /* software: i-cache coherency required */
+#define _PAGE_RW 0x400 /* software: user write access allowed */
+#define _PAGE_SPECIAL 0x800 /* software: Special page */
+
+#ifdef CONFIG_PTE_64BIT
+/* We never clear the high word of the pte */
+#define _PTE_NONE_MASK (0xffffffff00000000ULL | _PAGE_HASHPTE)
+#else
+#define _PTE_NONE_MASK _PAGE_HASHPTE
+#endif
+
+#define _PMD_PRESENT 0
+#define _PMD_PRESENT_MASK (PAGE_MASK)
+#define _PMD_BAD (~PAGE_MASK)
+
+/* Hash table based platforms need atomic updates of the linux PTE */
+#define PTE_ATOMIC_UPDATES 1
+
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_PGTABLE_HASH32_H */
Index: linux-work/arch/powerpc/include/asm/pgtable-ppc32.h
===================================================================
--- linux-work.orig/arch/powerpc/include/asm/pgtable-ppc32.h 2009-02-24 11:21:44.000000000 +1100
+++ linux-work/arch/powerpc/include/asm/pgtable-ppc32.h 2009-02-24 11:47:04.000000000 +1100
@@ -19,55 +19,6 @@ extern int icache_44x_need_flush;
#endif /* __ASSEMBLY__ */
/*
- * The PowerPC MMU uses a hash table containing PTEs, together with
- * a set of 16 segment registers (on 32-bit implementations), to define
- * the virtual to physical address mapping.
- *
- * We use the hash table as an extended TLB, i.e. a cache of currently
- * active mappings. We maintain a two-level page table tree, much
- * like that used by the i386, for the sake of the Linux memory
- * management code. Low-level assembler code in hashtable.S
- * (procedure hash_page) is responsible for extracting ptes from the
- * tree and putting them into the hash table when necessary, and
- * updating the accessed and modified bits in the page table tree.
- */
-
-/*
- * The PowerPC MPC8xx uses a TLB with hardware assisted, software tablewalk.
- * We also use the two level tables, but we can put the real bits in them
- * needed for the TLB and tablewalk. These definitions require Mx_CTR.PPM = 0,
- * Mx_CTR.PPCS = 0, and MD_CTR.TWAM = 1. The level 2 descriptor has
- * additional page protection (when Mx_CTR.PPCS = 1) that allows TLB hit
- * based upon user/super access. The TLB does not have accessed nor write
- * protect. We assume that if the TLB get loaded with an entry it is
- * accessed, and overload the changed bit for write protect. We use
- * two bits in the software pte that are supposed to be set to zero in
- * the TLB entry (24 and 25) for these indicators. Although the level 1
- * descriptor contains the guarded and writethrough/copyback bits, we can
- * set these at the page level since they get copied from the Mx_TWC
- * register when the TLB entry is loaded. We will use bit 27 for guard, since
- * that is where it exists in the MD_TWC, and bit 26 for writethrough.
- * These will get masked from the level 2 descriptor at TLB load time, and
- * copied to the MD_TWC before it gets loaded.
- * Large page sizes added. We currently support two sizes, 4K and 8M.
- * This also allows a TLB hander optimization because we can directly
- * load the PMD into MD_TWC. The 8M pages are only used for kernel
- * mapping of well known areas. The PMD (PGD) entries contain control
- * flags in addition to the address, so care must be taken that the
- * software no longer assumes these are only pointers.
- */
-
-/*
- * At present, all PowerPC 400-class processors share a similar TLB
- * architecture. The instruction and data sides share a unified,
- * 64-entry, fully-associative TLB which is maintained totally under
- * software control. In addition, the instruction side has a
- * hardware-managed, 4-entry, fully-associative TLB which serves as a
- * first level to the shared TLB. These two TLBs are known as the UTLB
- * and ITLB, respectively (see "mmu.h" for definitions).
- */
-
-/*
* The normal case is that PTEs are 32-bits and we have a 1-page
* 1024-entry pgdir pointing to 1-page 1024-entry PTE pages. -- paulus
*
@@ -135,261 +86,25 @@ extern int icache_44x_need_flush;
*/
#if defined(CONFIG_40x)
-
-/* There are several potential gotchas here. The 40x hardware TLBLO
- field looks like this:
-
- 0 1 2 3 4 ... 18 19 20 21 22 23 24 25 26 27 28 29 30 31
- RPN..................... 0 0 EX WR ZSEL....... W I M G
-
- Where possible we make the Linux PTE bits match up with this
-
- - bits 20 and 21 must be cleared, because we use 4k pages (40x can
- support down to 1k pages), this is done in the TLBMiss exception
- handler.
- - We use only zones 0 (for kernel pages) and 1 (for user pages)
- of the 16 available. Bit 24-26 of the TLB are cleared in the TLB
- miss handler. Bit 27 is PAGE_USER, thus selecting the correct
- zone.
- - PRESENT *must* be in the bottom two bits because swap cache
- entries use the top 30 bits. Because 40x doesn't support SMP
- anyway, M is irrelevant so we borrow it for PAGE_PRESENT. Bit 30
- is cleared in the TLB miss handler before the TLB entry is loaded.
- - All other bits of the PTE are loaded into TLBLO without
- modification, leaving us only the bits 20, 21, 24, 25, 26, 30 for
- software PTE bits. We actually use use bits 21, 24, 25, and
- 30 respectively for the software bits: ACCESSED, DIRTY, RW, and
- PRESENT.
-*/
-
-/* Definitions for 40x embedded chips. */
-#define _PAGE_GUARDED 0x001 /* G: page is guarded from prefetch */
-#define _PAGE_FILE 0x001 /* when !present: nonlinear file mapping */
-#define _PAGE_PRESENT 0x002 /* software: PTE contains a translation */
-#define _PAGE_NO_CACHE 0x004 /* I: caching is inhibited */
-#define _PAGE_WRITETHRU 0x008 /* W: caching is write-through */
-#define _PAGE_USER 0x010 /* matches one of the zone permission bits */
-#define _PAGE_RW 0x040 /* software: Writes permitted */
-#define _PAGE_DIRTY 0x080 /* software: dirty page */
-#define _PAGE_HWWRITE 0x100 /* hardware: Dirty & RW, set in exception */
-#define _PAGE_HWEXEC 0x200 /* hardware: EX permission */
-#define _PAGE_ACCESSED 0x400 /* software: R: page referenced */
-
-#define _PMD_PRESENT 0x400 /* PMD points to page of PTEs */
-#define _PMD_BAD 0x802
-#define _PMD_SIZE 0x0e0 /* size field, != 0 for large-page PMD entry */
-#define _PMD_SIZE_4M 0x0c0
-#define _PMD_SIZE_16M 0x0e0
-#define PMD_PAGE_SIZE(pmdval) (1024 << (((pmdval) & _PMD_SIZE) >> 4))
-
-/* Until my rework is finished, 40x still needs atomic PTE updates */
-#define PTE_ATOMIC_UPDATES 1
-
+#include <asm/pgtable-40x.h>
#elif defined(CONFIG_44x)
-/*
- * Definitions for PPC440
- *
- * Because of the 3 word TLB entries to support 36-bit addressing,
- * the attribute are difficult to map in such a fashion that they
- * are easily loaded during exception processing. I decided to
- * organize the entry so the ERPN is the only portion in the
- * upper word of the PTE and the attribute bits below are packed
- * in as sensibly as they can be in the area below a 4KB page size
- * oriented RPN. This at least makes it easy to load the RPN and
- * ERPN fields in the TLB. -Matt
- *
- * Note that these bits preclude future use of a page size
- * less than 4KB.
- *
- *
- * PPC 440 core has following TLB attribute fields;
- *
- * TLB1:
- * 0 1 2 3 4 ... 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
- * RPN................................. - - - - - - ERPN.......
- *
- * TLB2:
- * 0 1 2 3 4 ... 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
- * - - - - - - U0 U1 U2 U3 W I M G E - UX UW UR SX SW SR
- *
- * Newer 440 cores (440x6 as used on AMCC 460EX/460GT) have additional
- * TLB2 storage attibute fields. Those are:
- *
- * TLB2:
- * 0...10 11 12 13 14 15 16...31
- * no change WL1 IL1I IL1D IL2I IL2D no change
- *
- * There are some constrains and options, to decide mapping software bits
- * into TLB entry.
- *
- * - PRESENT *must* be in the bottom three bits because swap cache
- * entries use the top 29 bits for TLB2.
- *
- * - FILE *must* be in the bottom three bits because swap cache
- * entries use the top 29 bits for TLB2.
- *
- * - CACHE COHERENT bit (M) has no effect on original PPC440 cores,
- * because it doesn't support SMP. However, some later 460 variants
- * have -some- form of SMP support and so I keep the bit there for
- * future use
- *
- * With the PPC 44x Linux implementation, the 0-11th LSBs of the PTE are used
- * for memory protection related functions (see PTE structure in
- * include/asm-ppc/mmu.h). The _PAGE_XXX definitions in this file map to the
- * above bits. Note that the bit values are CPU specific, not architecture
- * specific.
- *
- * The kernel PTE entry holds an arch-dependent swp_entry structure under
- * certain situations. In other words, in such situations some portion of
- * the PTE bits are used as a swp_entry. In the PPC implementation, the
- * 3-24th LSB are shared with swp_entry, however the 0-2nd three LSB still
- * hold protection values. That means the three protection bits are
- * reserved for both PTE and SWAP entry at the most significant three
- * LSBs.
- *
- * There are three protection bits available for SWAP entry:
- * _PAGE_PRESENT
- * _PAGE_FILE
- * _PAGE_HASHPTE (if HW has)
- *
- * So those three bits have to be inside of 0-2nd LSB of PTE.
- *
- */
-
-#define _PAGE_PRESENT 0x00000001 /* S: PTE valid */
-#define _PAGE_RW 0x00000002 /* S: Write permission */
-#define _PAGE_FILE 0x00000004 /* S: nonlinear file mapping */
-#define _PAGE_HWEXEC 0x00000004 /* H: Execute permission */
-#define _PAGE_ACCESSED 0x00000008 /* S: Page referenced */
-#define _PAGE_DIRTY 0x00000010 /* S: Page dirty */
-#define _PAGE_SPECIAL 0x00000020 /* S: Special page */
-#define _PAGE_USER 0x00000040 /* S: User page */
-#define _PAGE_ENDIAN 0x00000080 /* H: E bit */
-#define _PAGE_GUARDED 0x00000100 /* H: G bit */
-#define _PAGE_COHERENT 0x00000200 /* H: M bit */
-#define _PAGE_NO_CACHE 0x00000400 /* H: I bit */
-#define _PAGE_WRITETHRU 0x00000800 /* H: W bit */
-
-/* TODO: Add large page lowmem mapping support */
-#define _PMD_PRESENT 0
-#define _PMD_PRESENT_MASK (PAGE_MASK)
-#define _PMD_BAD (~PAGE_MASK)
-
-/* ERPN in a PTE never gets cleared, ignore it */
-#define _PTE_NONE_MASK 0xffffffff00000000ULL
-
-#define __HAVE_ARCH_PTE_SPECIAL
-
+#include <asm/pgtable-44x.h>
#elif defined(CONFIG_FSL_BOOKE)
-/*
- MMU Assist Register 3:
-
- 32 33 34 35 36 ... 50 51 52 53 54 55 56 57 58 59 60 61 62 63
- RPN...................... 0 0 U0 U1 U2 U3 UX SX UW SW UR SR
-
- - PRESENT *must* be in the bottom three bits because swap cache
- entries use the top 29 bits.
-
- - FILE *must* be in the bottom three bits because swap cache
- entries use the top 29 bits.
-*/
-
-/* Definitions for FSL Book-E Cores */
-#define _PAGE_PRESENT 0x00001 /* S: PTE contains a translation */
-#define _PAGE_USER 0x00002 /* S: User page (maps to UR) */
-#define _PAGE_FILE 0x00002 /* S: when !present: nonlinear file mapping */
-#define _PAGE_RW 0x00004 /* S: Write permission (SW) */
-#define _PAGE_DIRTY 0x00008 /* S: Page dirty */
-#define _PAGE_HWEXEC 0x00010 /* H: SX permission */
-#define _PAGE_ACCESSED 0x00020 /* S: Page referenced */
-
-#define _PAGE_ENDIAN 0x00040 /* H: E bit */
-#define _PAGE_GUARDED 0x00080 /* H: G bit */
-#define _PAGE_COHERENT 0x00100 /* H: M bit */
-#define _PAGE_NO_CACHE 0x00200 /* H: I bit */
-#define _PAGE_WRITETHRU 0x00400 /* H: W bit */
-#define _PAGE_SPECIAL 0x00800 /* S: Special page */
-
-#ifdef CONFIG_PTE_64BIT
-/* ERPN in a PTE never gets cleared, ignore it */
-#define _PTE_NONE_MASK 0xffffffffffff0000ULL
-#endif
-
-#define _PMD_PRESENT 0
-#define _PMD_PRESENT_MASK (PAGE_MASK)
-#define _PMD_BAD (~PAGE_MASK)
-
-#define __HAVE_ARCH_PTE_SPECIAL
-
+#include <asm/pgtable-fsl-booke.h>
#elif defined(CONFIG_8xx)
-/* Definitions for 8xx embedded chips. */
-#define _PAGE_PRESENT 0x0001 /* Page is valid */
-#define _PAGE_FILE 0x0002 /* when !present: nonlinear file mapping */
-#define _PAGE_NO_CACHE 0x0002 /* I: cache inhibit */
-#define _PAGE_SHARED 0x0004 /* No ASID (context) compare */
-
-/* These five software bits must be masked out when the entry is loaded
- * into the TLB.
- */
-#define _PAGE_EXEC 0x0008 /* software: i-cache coherency required */
-#define _PAGE_GUARDED 0x0010 /* software: guarded access */
-#define _PAGE_DIRTY 0x0020 /* software: page changed */
-#define _PAGE_RW 0x0040 /* software: user write access allowed */
-#define _PAGE_ACCESSED 0x0080 /* software: page referenced */
-
-/* Setting any bits in the nibble with the follow two controls will
- * require a TLB exception handler change. It is assumed unused bits
- * are always zero.
- */
-#define _PAGE_HWWRITE 0x0100 /* h/w write enable: never set in Linux PTE */
-#define _PAGE_USER 0x0800 /* One of the PP bits, the other is USER&~RW */
-
-#define _PMD_PRESENT 0x0001
-#define _PMD_BAD 0x0ff0
-#define _PMD_PAGE_MASK 0x000c
-#define _PMD_PAGE_8M 0x000c
-
-#define _PTE_NONE_MASK _PAGE_ACCESSED
-
-/* Until my rework is finished, 8xx still needs atomic PTE updates */
-#define PTE_ATOMIC_UPDATES 1
-
+#include <asm/pgtable-8xx.h>
#else /* CONFIG_6xx */
-/* Definitions for 60x, 740/750, etc. */
-#define _PAGE_PRESENT 0x001 /* software: pte contains a translation */
-#define _PAGE_HASHPTE 0x002 /* hash_page has made an HPTE for this pte */
-#define _PAGE_FILE 0x004 /* when !present: nonlinear file mapping */
-#define _PAGE_USER 0x004 /* usermode access allowed */
-#define _PAGE_GUARDED 0x008 /* G: prohibit speculative access */
-#define _PAGE_COHERENT 0x010 /* M: enforce memory coherence (SMP systems) */
-#define _PAGE_NO_CACHE 0x020 /* I: cache inhibit */
-#define _PAGE_WRITETHRU 0x040 /* W: cache write-through */
-#define _PAGE_DIRTY 0x080 /* C: page changed */
-#define _PAGE_ACCESSED 0x100 /* R: page referenced */
-#define _PAGE_EXEC 0x200 /* software: i-cache coherency required */
-#define _PAGE_RW 0x400 /* software: user write access allowed */
-#define _PAGE_SPECIAL 0x800 /* software: Special page */
-
-#ifdef CONFIG_PTE_64BIT
-/* We never clear the high word of the pte */
-#define _PTE_NONE_MASK (0xffffffff00000000ULL | _PAGE_HASHPTE)
-#else
-#define _PTE_NONE_MASK _PAGE_HASHPTE
+#include <asm/pgtable-hash32.h>
#endif
-#define _PMD_PRESENT 0
-#define _PMD_PRESENT_MASK (PAGE_MASK)
-#define _PMD_BAD (~PAGE_MASK)
-
-/* Hash table based platforms need atomic updates of the linux PTE */
-#define PTE_ATOMIC_UPDATES 1
-
+/* If _PAGE_SPECIAL is defined, then we advertise our support for it */
+#ifdef _PAGE_SPECIAL
#define __HAVE_ARCH_PTE_SPECIAL
-
#endif
/*
- * Some bits are only used on some cpu families...
+ * Some bits are only used on some cpu families... Make sure that all
+ * the undefined gets defined as 0
*/
#ifndef _PAGE_HASHPTE
#define _PAGE_HASHPTE 0
@@ -600,11 +315,19 @@ extern void flush_hash_entry(struct mm_s
unsigned long address);
/*
- * Atomic PTE updates.
- *
- * pte_update clears and sets bit atomically, and returns
- * the old pte value. In the 64-bit PTE case we lock around the
- * low PTE word since we expect ALL flag bits to be there
+ * PTE updates. This function is called whenever an existing
+ * valid PTE is updated. This does -not- include set_pte_at()
+ * which nowadays only sets a new PTE.
+ *
+ * Depending on the type of MMU, we may need to use atomic updates
+ * and the PTE may be either 32 or 64 bit wide. In the later case,
+ * when using atomic updates, only the low part of the PTE is
+ * accessed atomically.
+ *
+ * In addition, on 44x, we also maintain a global flag indicating
+ * that an executable user mapping was modified, which is needed
+ * to properly flush the virtually tagged instruction cache of
+ * those implementations.
*/
#ifndef CONFIG_PTE_64BIT
static inline unsigned long pte_update(pte_t *p,
Index: linux-work/arch/powerpc/include/asm/pgtable-hash64-4k.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/include/asm/pgtable-hash64-4k.h 2009-02-18 13:20:35.000000000 +1100
@@ -0,0 +1,117 @@
+#ifndef _ASM_POWERPC_PGTABLE_4K_H
+#define _ASM_POWERPC_PGTABLE_4K_H
+/*
+ * Entries per page directory level. The PTE level must use a 64b record
+ * for each page table entry. The PMD and PGD level use a 32b record for
+ * each entry by assuming that each entry is page aligned.
+ */
+#define PTE_INDEX_SIZE 9
+#define PMD_INDEX_SIZE 7
+#define PUD_INDEX_SIZE 7
+#define PGD_INDEX_SIZE 9
+
+#ifndef __ASSEMBLY__
+#define PTE_TABLE_SIZE (sizeof(pte_t) << PTE_INDEX_SIZE)
+#define PMD_TABLE_SIZE (sizeof(pmd_t) << PMD_INDEX_SIZE)
+#define PUD_TABLE_SIZE (sizeof(pud_t) << PUD_INDEX_SIZE)
+#define PGD_TABLE_SIZE (sizeof(pgd_t) << PGD_INDEX_SIZE)
+#endif /* __ASSEMBLY__ */
+
+#define PTRS_PER_PTE (1 << PTE_INDEX_SIZE)
+#define PTRS_PER_PMD (1 << PMD_INDEX_SIZE)
+#define PTRS_PER_PUD (1 << PMD_INDEX_SIZE)
+#define PTRS_PER_PGD (1 << PGD_INDEX_SIZE)
+
+/* PMD_SHIFT determines what a second-level page table entry can map */
+#define PMD_SHIFT (PAGE_SHIFT + PTE_INDEX_SIZE)
+#define PMD_SIZE (1UL << PMD_SHIFT)
+#define PMD_MASK (~(PMD_SIZE-1))
+
+/* With 4k base page size, hugepage PTEs go at the PMD level */
+#define MIN_HUGEPTE_SHIFT PMD_SHIFT
+
+/* PUD_SHIFT determines what a third-level page table entry can map */
+#define PUD_SHIFT (PMD_SHIFT + PMD_INDEX_SIZE)
+#define PUD_SIZE (1UL << PUD_SHIFT)
+#define PUD_MASK (~(PUD_SIZE-1))
+
+/* PGDIR_SHIFT determines what a fourth-level page table entry can map */
+#define PGDIR_SHIFT (PUD_SHIFT + PUD_INDEX_SIZE)
+#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE-1))
+
+/* PTE bits */
+#define _PAGE_HASHPTE 0x0400 /* software: pte has an associated HPTE */
+#define _PAGE_SECONDARY 0x8000 /* software: HPTE is in secondary group */
+#define _PAGE_GROUP_IX 0x7000 /* software: HPTE index within group */
+#define _PAGE_F_SECOND _PAGE_SECONDARY
+#define _PAGE_F_GIX _PAGE_GROUP_IX
+#define _PAGE_SPECIAL 0x10000 /* software: special page */
+#define __HAVE_ARCH_PTE_SPECIAL
+
+/* PTE flags to conserve for HPTE identification */
+#define _PAGE_HPTEFLAGS (_PAGE_BUSY | _PAGE_HASHPTE | \
+ _PAGE_SECONDARY | _PAGE_GROUP_IX)
+
+/* There is no 4K PFN hack on 4K pages */
+#define _PAGE_4K_PFN 0
+
+/* PAGE_MASK gives the right answer below, but only by accident */
+/* It should be preserving the high 48 bits and then specifically */
+/* preserving _PAGE_SECONDARY | _PAGE_GROUP_IX */
+#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY | \
+ _PAGE_HPTEFLAGS | _PAGE_SPECIAL)
+
+/* Bits to mask out from a PMD to get to the PTE page */
+#define PMD_MASKED_BITS 0
+/* Bits to mask out from a PUD to get to the PMD page */
+#define PUD_MASKED_BITS 0
+/* Bits to mask out from a PGD to get to the PUD page */
+#define PGD_MASKED_BITS 0
+
+/* shift to put page number into pte */
+#define PTE_RPN_SHIFT (17)
+
+#ifdef STRICT_MM_TYPECHECKS
+#define __real_pte(e,p) ((real_pte_t){(e)})
+#define __rpte_to_pte(r) ((r).pte)
+#else
+#define __real_pte(e,p) (e)
+#define __rpte_to_pte(r) (__pte(r))
+#endif
+#define __rpte_to_hidx(r,index) (pte_val(__rpte_to_pte(r)) >> 12)
+
+#define pte_iterate_hashed_subpages(rpte, psize, va, index, shift) \
+ do { \
+ index = 0; \
+ shift = mmu_psize_defs[psize].shift; \
+
+#define pte_iterate_hashed_end() } while(0)
+
+#ifdef CONFIG_PPC_HAS_HASH_64K
+#define pte_pagesize_index(mm, addr, pte) get_slice_psize(mm, addr)
+#else
+#define pte_pagesize_index(mm, addr, pte) MMU_PAGE_4K
+#endif
+
+/*
+ * 4-level page tables related bits
+ */
+
+#define pgd_none(pgd) (!pgd_val(pgd))
+#define pgd_bad(pgd) (pgd_val(pgd) == 0)
+#define pgd_present(pgd) (pgd_val(pgd) != 0)
+#define pgd_clear(pgdp) (pgd_val(*(pgdp)) = 0)
+#define pgd_page_vaddr(pgd) (pgd_val(pgd) & ~PGD_MASKED_BITS)
+#define pgd_page(pgd) virt_to_page(pgd_page_vaddr(pgd))
+
+#define pud_offset(pgdp, addr) \
+ (((pud_t *) pgd_page_vaddr(*(pgdp))) + \
+ (((addr) >> PUD_SHIFT) & (PTRS_PER_PUD - 1)))
+
+#define pud_ERROR(e) \
+ printk("%s:%d: bad pud %08lx.\n", __FILE__, __LINE__, pud_val(e))
+
+#define remap_4k_pfn(vma, addr, pfn, prot) \
+ remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE, (prot))
+#endif /* _ASM_POWERPC_PGTABLE_4K_H */
Index: linux-work/arch/powerpc/include/asm/pgtable-hash64-64k.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/include/asm/pgtable-hash64-64k.h 2009-02-18 13:20:35.000000000 +1100
@@ -0,0 +1,155 @@
+#ifndef _ASM_POWERPC_PGTABLE_64K_H
+#define _ASM_POWERPC_PGTABLE_64K_H
+
+#include <asm-generic/pgtable-nopud.h>
+
+
+#define PTE_INDEX_SIZE 12
+#define PMD_INDEX_SIZE 12
+#define PUD_INDEX_SIZE 0
+#define PGD_INDEX_SIZE 4
+
+#ifndef __ASSEMBLY__
+#define PTE_TABLE_SIZE (sizeof(real_pte_t) << PTE_INDEX_SIZE)
+#define PMD_TABLE_SIZE (sizeof(pmd_t) << PMD_INDEX_SIZE)
+#define PGD_TABLE_SIZE (sizeof(pgd_t) << PGD_INDEX_SIZE)
+
+#define PTRS_PER_PTE (1 << PTE_INDEX_SIZE)
+#define PTRS_PER_PMD (1 << PMD_INDEX_SIZE)
+#define PTRS_PER_PGD (1 << PGD_INDEX_SIZE)
+
+#ifdef CONFIG_PPC_SUBPAGE_PROT
+/*
+ * For the sub-page protection option, we extend the PGD with one of
+ * these. Basically we have a 3-level tree, with the top level being
+ * the protptrs array. To optimize speed and memory consumption when
+ * only addresses < 4GB are being protected, pointers to the first
+ * four pages of sub-page protection words are stored in the low_prot
+ * array.
+ * Each page of sub-page protection words protects 1GB (4 bytes
+ * protects 64k). For the 3-level tree, each page of pointers then
+ * protects 8TB.
+ */
+struct subpage_prot_table {
+ unsigned long maxaddr; /* only addresses < this are protected */
+ unsigned int **protptrs[2];
+ unsigned int *low_prot[4];
+};
+
+#undef PGD_TABLE_SIZE
+#define PGD_TABLE_SIZE ((sizeof(pgd_t) << PGD_INDEX_SIZE) + \
+ sizeof(struct subpage_prot_table))
+
+#define SBP_L1_BITS (PAGE_SHIFT - 2)
+#define SBP_L2_BITS (PAGE_SHIFT - 3)
+#define SBP_L1_COUNT (1 << SBP_L1_BITS)
+#define SBP_L2_COUNT (1 << SBP_L2_BITS)
+#define SBP_L2_SHIFT (PAGE_SHIFT + SBP_L1_BITS)
+#define SBP_L3_SHIFT (SBP_L2_SHIFT + SBP_L2_BITS)
+
+extern void subpage_prot_free(pgd_t *pgd);
+
+static inline struct subpage_prot_table *pgd_subpage_prot(pgd_t *pgd)
+{
+ return (struct subpage_prot_table *)(pgd + PTRS_PER_PGD);
+}
+#endif /* CONFIG_PPC_SUBPAGE_PROT */
+#endif /* __ASSEMBLY__ */
+
+/* With 4k base page size, hugepage PTEs go at the PMD level */
+#define MIN_HUGEPTE_SHIFT PAGE_SHIFT
+
+/* PMD_SHIFT determines what a second-level page table entry can map */
+#define PMD_SHIFT (PAGE_SHIFT + PTE_INDEX_SIZE)
+#define PMD_SIZE (1UL << PMD_SHIFT)
+#define PMD_MASK (~(PMD_SIZE-1))
+
+/* PGDIR_SHIFT determines what a third-level page table entry can map */
+#define PGDIR_SHIFT (PMD_SHIFT + PMD_INDEX_SIZE)
+#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE-1))
+
+/* Additional PTE bits (don't change without checking asm in hash_low.S) */
+#define __HAVE_ARCH_PTE_SPECIAL
+#define _PAGE_SPECIAL 0x00000400 /* software: special page */
+#define _PAGE_HPTE_SUB 0x0ffff000 /* combo only: sub pages HPTE bits */
+#define _PAGE_HPTE_SUB0 0x08000000 /* combo only: first sub page */
+#define _PAGE_COMBO 0x10000000 /* this is a combo 4k page */
+#define _PAGE_4K_PFN 0x20000000 /* PFN is for a single 4k page */
+
+/* For 64K page, we don't have a separate _PAGE_HASHPTE bit. Instead,
+ * we set that to be the whole sub-bits mask. The C code will only
+ * test this, so a multi-bit mask will work. For combo pages, this
+ * is equivalent as effectively, the old _PAGE_HASHPTE was an OR of
+ * all the sub bits. For real 64k pages, we now have the assembly set
+ * _PAGE_HPTE_SUB0 in addition to setting the HIDX bits which overlap
+ * that mask. This is fine as long as the HIDX bits are never set on
+ * a PTE that isn't hashed, which is the case today.
+ *
+ * A little nit is for the huge page C code, which does the hashing
+ * in C, we need to provide which bit to use.
+ */
+#define _PAGE_HASHPTE _PAGE_HPTE_SUB
+
+/* Note the full page bits must be in the same location as for normal
+ * 4k pages as the same asssembly will be used to insert 64K pages
+ * wether the kernel has CONFIG_PPC_64K_PAGES or not
+ */
+#define _PAGE_F_SECOND 0x00008000 /* full page: hidx bits */
+#define _PAGE_F_GIX 0x00007000 /* full page: hidx bits */
+
+/* PTE flags to conserve for HPTE identification */
+#define _PAGE_HPTEFLAGS (_PAGE_BUSY | _PAGE_HASHPTE | _PAGE_COMBO)
+
+/* Shift to put page number into pte.
+ *
+ * That gives us a max RPN of 34 bits, which means a max of 50 bits
+ * of addressable physical space, or 46 bits for the special 4k PFNs.
+ */
+#define PTE_RPN_SHIFT (30)
+#define PTE_RPN_MAX (1UL << (64 - PTE_RPN_SHIFT))
+#define PTE_RPN_MASK (~((1UL<<PTE_RPN_SHIFT)-1))
+
+/* _PAGE_CHG_MASK masks of bits that are to be preserved accross
+ * pgprot changes
+ */
+#define _PAGE_CHG_MASK (PTE_RPN_MASK | _PAGE_HPTEFLAGS | _PAGE_DIRTY | \
+ _PAGE_ACCESSED | _PAGE_SPECIAL)
+
+/* Bits to mask out from a PMD to get to the PTE page */
+#define PMD_MASKED_BITS 0x1ff
+/* Bits to mask out from a PGD/PUD to get to the PMD page */
+#define PUD_MASKED_BITS 0x1ff
+
+/* Manipulate "rpte" values */
+#define __real_pte(e,p) ((real_pte_t) { \
+ (e), pte_val(*((p) + PTRS_PER_PTE)) })
+#define __rpte_to_hidx(r,index) ((pte_val((r).pte) & _PAGE_COMBO) ? \
+ (((r).hidx >> ((index)<<2)) & 0xf) : ((pte_val((r).pte) >> 12) & 0xf))
+#define __rpte_to_pte(r) ((r).pte)
+#define __rpte_sub_valid(rpte, index) \
+ (pte_val(rpte.pte) & (_PAGE_HPTE_SUB0 >> (index)))
+
+
+/* Trick: we set __end to va + 64k, which happens works for
+ * a 16M page as well as we want only one iteration
+ */
+#define pte_iterate_hashed_subpages(rpte, psize, va, index, shift) \
+ do { \
+ unsigned long __end = va + PAGE_SIZE; \
+ unsigned __split = (psize == MMU_PAGE_4K || \
+ psize == MMU_PAGE_64K_AP); \
+ shift = mmu_psize_defs[psize].shift; \
+ for (index = 0; va < __end; index++, va += (1L << shift)) { \
+ if (!__split || __rpte_sub_valid(rpte, index)) do { \
+
+#define pte_iterate_hashed_end() } while(0); } } while(0)
+
+#define pte_pagesize_index(mm, addr, pte) \
+ (((pte) & _PAGE_COMBO)? MMU_PAGE_4K: MMU_PAGE_64K)
+
+#define remap_4k_pfn(vma, addr, pfn, prot) \
+ remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE, \
+ __pgprot(pgprot_val((prot)) | _PAGE_4K_PFN))
+
+#endif /* _ASM_POWERPC_PGTABLE_64K_H */
Index: linux-work/arch/powerpc/include/asm/pgtable-hash64.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/include/asm/pgtable-hash64.h 2009-02-24 11:54:48.000000000 +1100
@@ -0,0 +1,47 @@
+#ifndef _ASM_POWERPC_PGTABLE_HASH64_H
+#define _ASM_POWERPC_PGTABLE_HASH64_H
+#ifdef __KERNEL__
+
+#ifdef CONFIG_PPC_64K_PAGES
+#include <asm/pgtable-hash64-64k.h>
+#else
+#include <asm/pgtable-hash64-4k.h>
+#endif
+
+/*
+ * Common bits between 4K and 64K pages in a linux-style PTE.
+ * These match the bits in the (hardware-defined) PowerPC PTE as closely
+ * as possible. Additional bits may be defined in pgtable-hash64-*.h
+ */
+#define _PAGE_PRESENT 0x0001 /* software: pte contains a translation */
+#define _PAGE_USER 0x0002 /* matches one of the PP bits */
+#define _PAGE_FILE 0x0002 /* (!present only) software: pte holds file offset */
+#define _PAGE_EXEC 0x0004 /* No execute on POWER4 and newer (we invert) */
+#define _PAGE_GUARDED 0x0008
+#define _PAGE_COHERENT 0x0010 /* M: enforce memory coherence (SMP systems) */
+#define _PAGE_NO_CACHE 0x0020 /* I: cache inhibit */
+#define _PAGE_WRITETHRU 0x0040 /* W: cache write-through */
+#define _PAGE_DIRTY 0x0080 /* C: page changed */
+#define _PAGE_ACCESSED 0x0100 /* R: page referenced */
+#define _PAGE_RW 0x0200 /* software: user write access allowed */
+#define _PAGE_BUSY 0x0800 /* software: PTE & hash are busy */
+
+/* Strong Access Ordering */
+#define _PAGE_SAO (_PAGE_WRITETHRU | _PAGE_NO_CACHE | _PAGE_COHERENT)
+
+#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_COHERENT)
+
+#define _PAGE_WRENABLE (_PAGE_RW | _PAGE_DIRTY)
+
+/* PTEIDX nibble */
+#define _PTEIDX_SECONDARY 0x8
+#define _PTEIDX_GROUP_IX 0x7
+
+#define PAGE_PROT_BITS (_PAGE_GUARDED | _PAGE_COHERENT | \
+ _PAGE_NO_CACHE | _PAGE_WRITETHRU | \
+ _PAGE_4K_PFN | _PAGE_RW | _PAGE_USER | \
+ _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_EXEC)
+
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_PGTABLE_HASH64_H */
Index: linux-work/arch/powerpc/include/asm/pgtable-ppc64.h
===================================================================
--- linux-work.orig/arch/powerpc/include/asm/pgtable-ppc64.h 2009-02-24 11:52:20.000000000 +1100
+++ linux-work/arch/powerpc/include/asm/pgtable-ppc64.h 2009-02-24 12:42:24.000000000 +1100
@@ -10,11 +10,6 @@
#include <asm/tlbflush.h>
#endif /* __ASSEMBLY__ */
-#ifdef CONFIG_PPC_64K_PAGES
-#include <asm/pgtable-64k.h>
-#else
-#include <asm/pgtable-4k.h>
-#endif
#define FIRST_USER_ADDRESS 0
@@ -25,15 +20,6 @@
PUD_INDEX_SIZE + PGD_INDEX_SIZE + PAGE_SHIFT)
#define PGTABLE_RANGE (ASM_CONST(1) << PGTABLE_EADDR_SIZE)
-#if TASK_SIZE_USER64 > PGTABLE_RANGE
-#error TASK_SIZE_USER64 exceeds pagetable range
-#endif
-
-#if TASK_SIZE_USER64 > (1UL << (USER_ESID_BITS + SID_SHIFT))
-#error TASK_SIZE_USER64 exceeds user VSID range
-#endif
-
-
/*
* Define the address range of the vmalloc VM area.
*/
@@ -75,30 +61,17 @@
#define vmemmap ((struct page *)VMEMMAP_BASE)
-/*
- * Common bits in a linux-style PTE. These match the bits in the
- * (hardware-defined) PowerPC PTE as closely as possible. Additional
- * bits may be defined in pgtable-*.h
- */
-#define _PAGE_PRESENT 0x0001 /* software: pte contains a translation */
-#define _PAGE_USER 0x0002 /* matches one of the PP bits */
-#define _PAGE_FILE 0x0002 /* (!present only) software: pte holds file offset */
-#define _PAGE_EXEC 0x0004 /* No execute on POWER4 and newer (we invert) */
-#define _PAGE_GUARDED 0x0008
-#define _PAGE_COHERENT 0x0010 /* M: enforce memory coherence (SMP systems) */
-#define _PAGE_NO_CACHE 0x0020 /* I: cache inhibit */
-#define _PAGE_WRITETHRU 0x0040 /* W: cache write-through */
-#define _PAGE_DIRTY 0x0080 /* C: page changed */
-#define _PAGE_ACCESSED 0x0100 /* R: page referenced */
-#define _PAGE_RW 0x0200 /* software: user write access allowed */
-#define _PAGE_BUSY 0x0800 /* software: PTE & hash are busy */
+#include <asm/pgtable-hash64.h>
-/* Strong Access Ordering */
-#define _PAGE_SAO (_PAGE_WRITETHRU | _PAGE_NO_CACHE | _PAGE_COHERENT)
+/* Some sanity checking */
+#if TASK_SIZE_USER64 > PGTABLE_RANGE
+#error TASK_SIZE_USER64 exceeds pagetable range
+#endif
-#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_COHERENT)
+#if TASK_SIZE_USER64 > (1UL << (USER_ESID_BITS + SID_SHIFT))
+#error TASK_SIZE_USER64 exceeds user VSID range
+#endif
-#define _PAGE_WRENABLE (_PAGE_RW | _PAGE_DIRTY)
/* __pgprot defined in arch/powerpc/include/asm/page.h */
#define PAGE_NONE __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
@@ -117,16 +90,10 @@
#define PAGE_AGP __pgprot(_PAGE_BASE | _PAGE_WRENABLE | _PAGE_NO_CACHE)
#define HAVE_PAGE_AGP
-#define PAGE_PROT_BITS (_PAGE_GUARDED | _PAGE_COHERENT | \
- _PAGE_NO_CACHE | _PAGE_WRITETHRU | \
- _PAGE_4K_PFN | _PAGE_RW | _PAGE_USER | \
- _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_EXEC)
-/* PTEIDX nibble */
-#define _PTEIDX_SECONDARY 0x8
-#define _PTEIDX_GROUP_IX 0x7
-
/* To make some generic powerpc code happy */
+#ifndef _PAGE_HWEXEC
#define _PAGE_HWEXEC 0
+#endif
/*
* POWER4 and newer have per page execute protection, older chips can only
^ permalink raw reply
* Re: soc vs localbus for flash in device tree
From: Grant Likely @ 2009-02-24 2:12 UTC (permalink / raw)
To: Michael Bergandi; +Cc: linuxppc-dev@ozlabs.org, Dushara Jayasinghe
In-Reply-To: <1c641bc80902231611j57d53abcx56b32439f26cdf8@mail.gmail.com>
On Mon, Feb 23, 2009 at 5:11 PM, Michael Bergandi <mbergandi@gmail.com> wrote:
>>
>> I'm working on the kernel version Linux-2.6.29-rc5 with U-Boot
>> 1.2.0-g88e21e7b-dirty.
>
> I recommend you use a more current version of U-Boot. That one is quite old.
>
>> Does the flash node HAVE to be within a localbus node or can it reside
>> within the soc node? I've been basing my work on mpc834x_mds.dts
>> And hence haven't defined a localbus node.
>
> I think it can be in either, but the most common place I've seen is in the
> soc node.
If flash hangs off the local bus, then it belongs in the local bus
node. It doesn't make any sense to put it in the SoC node because the
SoC node describes the internal memory mapped devices. The local bus
node should have a ranges property that describes the translation
between physical address and individual chip selects.
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: soc vs localbus for flash in device tree
From: Michael Bergandi @ 2009-02-24 0:11 UTC (permalink / raw)
To: Dushara Jayasinghe; +Cc: linuxppc-dev@ozlabs.org
In-Reply-To: <DE0CCFFBFF707949BEABD4537A14ACBA0C04B40559@mailsvr>
[-- Attachment #1: Type: text/plain, Size: 626 bytes --]
>
>
> I'm working on the kernel version Linux-2.6.29-rc5 with U-Boot
> 1.2.0-g88e21e7b-dirty.
I recommend you use a more current version of U-Boot. That one is quite old.
Does the flash node HAVE to be within a localbus node or can it reside
> within the soc node? I've been basing my work on mpc834x_mds.dts
> And hence haven't defined a localbus node.
I think it can be in either, but the most common place I've seen is in the
soc node.
> BTW is there a way to search the ML archive? Currently I browse each month
> to search for prior postings.
Yes, Google "site:ozlabs.org <what ever you are looking for>"
Mike
[-- Attachment #2: Type: text/html, Size: 1220 bytes --]
^ permalink raw reply
* Re: soc vs localbus for flash in device tree
From: Ira Snyder @ 2009-02-24 0:06 UTC (permalink / raw)
To: Dushara Jayasinghe; +Cc: linuxppc-dev@ozlabs.org
In-Reply-To: <DE0CCFFBFF707949BEABD4537A14ACBA0C04B40559@mailsvr>
On Tue, Feb 24, 2009 at 10:31:37AM +1100, Dushara Jayasinghe wrote:
> Hi,
>
> I'm working on the kernel version Linux-2.6.29-rc5 with U-Boot 1.2.0-g88e21e7b-dirty.
>
> Does the flash node HAVE to be within a localbus node or can it reside within the soc node? I've been basing my work on mpc834x_mds.dts
> And hence haven't defined a localbus node.
>
I'm also working on a custom board based on MPC8349EMDS, and have based
my dts off of mpc834x_mds.dts. I've added the localbus node with my
chipselects and flash. It works, but there are no guarantees of
correctness.
I've appended the important portion below, just in case it helps.
Ira
localbus@e0005000 {
#address-cells = <2>;
#size-cells = <1>;
compatible = "fsl,mpc8349-elbc", "fsl,elbc", "simple-bus";
reg = <0xe0005000 0x1000>;
interrupts = <77 0x8>;
interrupt-parent = <&ipic>;
// CS0 and CS1 -- probably wrong...
ranges = <0x0 0x0 0xfc000000 0x04000000
0x1 0x0 0xf0000000 0x00004000>;
flash@0,0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "cfi-flash";
reg = <0x0 0x0 0x04000000>;
bank-width = <2>;
device-width = <1>;
u-boot@0 {
reg = <0x0 0x40000>;
};
u-boot-env@60000 {
reg = <0x60000 0x20000>;
};
sys-fpga@3de0000 {
reg = <0x3de0000 0x220000>;
};
};
};
^ permalink raw reply
* [RFC v2] virtio: add virtio-over-PCI driver
From: Ira Snyder @ 2009-02-24 0:00 UTC (permalink / raw)
To: linux-kernel
Cc: linuxppc-dev, Rusty Russell, netdev, Arnd Bergmann,
Jan-Bernd Themann
This adds support to Linux for using virtio between two computers linked by
a PCI interface. This allows the use of virtio_net to create a familiar,
fast interface for communication. It should be possible to use other virtio
devices in the future, but this has not been tested.
I have implemented guest support for the Freescale MPC8349EMDS board, which
is capable of running in PCI agent mode (It acts like a PCI card, but is a
complete computer system, running Linux). The driver is trivial to port to
any MPC83xx system.
It was developed to work in a CompactPCI crate of computers, one of which
is a standard x86 system (acting as the host) and many PowerPC systems
(acting as guests).
I have only tested this driver with a single board in my system. The host
is a 1066MHz Pentium3-M, and the guest is a 533MHz PowerPC. I am able
achieve transfer rates of about 150 mbit host->guest and 350 mbit
guest->host. A few tests showed that using an mtu of 4000 provided much
better results than an mtu of 1500. Using an mtu of 64000 significantly
dropped performance. The performance is equivalent to my PCINet driver for
host->guest, and about 20% faster for guest->host transfers.
I have included a short document explaining what I think is the most
complicated part of the driver: using the DMA engine to transfer data. I
hope everything else is readily obvious from the code. Questions are
welcome.
I will not be able to work on this full time for at least a few weeks, so I
would appreciate actual review of this driver. Nitpicks are fine, I just
won't be able to respond to them quickly.
RFCv1 -> RFCv2:
* fix major brokenness of host detach_buf()
* support VIRTIO_NET_F_CSUM
* support VIRTIO_NET_F_GSO
* support VIRTIO_NET_F_MRG_RXBUF
* rewrote DMA transfers to support merged rxbufs
* added a hack to fix the endianness of virtio_net's metadata
* lots more performance for guest->host transfers (~40MB/sec)
* updated documentation
* allocate 128 feature bits instead of 32
Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
Yes, the commit message has too much information. This is an RFC after
all. I fully expect to have to make changes. In fact, I posting this
more to "get it out there" than anything else, since I have other tasks
that need doing.
I'd appreciate a serious review of the design by the people who have
been pressuring me to use virtio. I'm very happy to answer any questions
you have.
Thanks to everyone who gave feedback for RFCv1!
Ira
Documentation/virtio-over-PCI.txt | 60 +
arch/powerpc/boot/dts/mpc834x_mds.dts | 7 +
drivers/virtio/Kconfig | 22 +
drivers/virtio/Makefile | 2 +
drivers/virtio/vop.h | 119 ++
drivers/virtio/vop_fsl.c | 2020 +++++++++++++++++++++++++++++++++
drivers/virtio/vop_host.c | 1071 +++++++++++++++++
drivers/virtio/vop_hw.h | 80 ++
8 files changed, 3381 insertions(+), 0 deletions(-)
create mode 100644 Documentation/virtio-over-PCI.txt
create mode 100644 drivers/virtio/vop.h
create mode 100644 drivers/virtio/vop_fsl.c
create mode 100644 drivers/virtio/vop_host.c
create mode 100644 drivers/virtio/vop_hw.h
diff --git a/Documentation/virtio-over-PCI.txt b/Documentation/virtio-over-PCI.txt
new file mode 100644
index 0000000..e4520d4
--- /dev/null
+++ b/Documentation/virtio-over-PCI.txt
@@ -0,0 +1,60 @@
+The implementation of virtio-over-PCI was driven with the following goals:
+* Avoid MMIO reads, try to use only MMIO writes
+* Use the onboard DMA engine, for speed
+
+The implementation also borrows many of the details from the only other
+implementation, virtio_ring.
+
+It succeeds in avoiding all MMIO reads on the critical paths. I did not
+see any reason to avoid the use of MMIO reads during device probing, since
+it is not a critical path.
+
+=== Avoiding MMIO reads ===
+To avoid MMIO reads, both the host and guest systems have a copy of the
+descriptors. Both sides need to read the descriptors after they have been
+written, but only the host system writes to them. This allows us to keep a
+local copy for later use.
+
+=== Using the DMA engine ===
+This is the only truly complicated part of the system. Since this
+implementation was designed for use with virtio_net, it may be biased
+towards virtio_net's usage of the virtio interface.
+
+In merged rxbufs mode, the virtio_net driver provides a receive ring, which
+it fills with empty PAGE_SIZE buffers. The DMA code sets up transfers
+directly from the guest transmit queue to the empty packets in the host
+receive queue. Data transfer in the other direction works in a similar
+fashion.
+
+The guest (PowerPC) system keeps its own local set of descriptors, which are
+filled by the virtio add_buf() call. Whenever this happens, the avail ring is
+changed, and therefore we try to transfer data.
+
+The algorithm is essentially as follows:
+1) Check for an available local or remote entry
+2) Check that the other side has enough room for the packet
+3) Transfer the chain, joining small packets and splitting large packets
+4) Move the entries to the used rings, but do not update the used index
+5) Schedule a DMA callback to happen when the transfer completes
+6) Start the DMA transfer
+7) When the DMA finishes, the callback updates the used indices and
+ triggers any necessary callbacks
+
+The algorithm can only handle chains that are to be coalesced together. It
+puts all data sequentially into the PAGE_SIZE buffers exposed by the
+receiving side, including both the virtio_net header and packet data.
+
+=== Startup Sequence ===
+There are currently problems in the startup sequence between the host and
+guest drivers. The current scheme assumes that the guest is up and waiting
+before the host is ready. I am having a very hard time coming up with a scheme
+that is perfectly safe, where either side could win the race and be ready
+first.
+
+Even harder is a situation where you would like to use the "network device"
+from your bootloader to tftp a kernel, then boot Linux. In this case,
+Linux has no knowledge of where the device descriptors were before it booted.
+You'd need to stop and re-start the host driver to make sure it re-initializes
+the new descriptor memory after Linux has booted.
+
+This is a definite "needs work" item.
diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts
index d9adba0..5c7617d 100644
--- a/arch/powerpc/boot/dts/mpc834x_mds.dts
+++ b/arch/powerpc/boot/dts/mpc834x_mds.dts
@@ -104,6 +104,13 @@
mode = "cpu";
};
+ message-unit@8030 {
+ compatible = "fsl,mpc8349-mu";
+ reg = <0x8030 0xd0>;
+ interrupts = <69 0x8>;
+ interrupt-parent = <&ipic>;
+ };
+
dma@82a8 {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 3dd6294..efcf56b 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -33,3 +33,25 @@ config VIRTIO_BALLOON
If unsure, say M.
+config VIRTIO_OVER_PCI_HOST
+ tristate "Virtio-over-PCI Host support (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select VIRTIO
+ ---help---
+ This driver provides the host support necessary for using virtio
+ over the PCI bus with a Freescale MPC8349EMDS evaluation board.
+
+ If unsure, say N.
+
+config VIRTIO_OVER_PCI_FSL
+ tristate "Virtio-over-PCI Guest support (EXPERIMENTAL)"
+ depends on MPC834x_MDS && EXPERIMENTAL
+ select VIRTIO
+ select DMA_ENGINE
+ select FSL_DMA
+ ---help---
+ This driver provides the guest support necessary for using virtio
+ over the PCI bus.
+
+ If unsure, say N.
+
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 6738c44..f31afaa 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -2,3 +2,5 @@ obj-$(CONFIG_VIRTIO) += virtio.o
obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
+obj-$(CONFIG_VIRTIO_OVER_PCI_HOST) += vop_host.o
+obj-$(CONFIG_VIRTIO_OVER_PCI_FSL) += vop_fsl.o
diff --git a/drivers/virtio/vop.h b/drivers/virtio/vop.h
new file mode 100644
index 0000000..5f77228
--- /dev/null
+++ b/drivers/virtio/vop.h
@@ -0,0 +1,119 @@
+/*
+ * Virtio-over-PCI definitions
+ *
+ * Copyright (c) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef VOP_H
+#define VOP_H
+
+#include <linux/types.h>
+
+/* The number of entries per ring (MUST be a power of two) */
+#define VOP_RING_SIZE 64
+
+/* Marks a buffer as continuing via the next field */
+#define VOP_DESC_F_NEXT 1
+/* Marks a buffer as write-only (otherwise read-only) */
+#define VOP_DESC_F_WRITE 2
+
+/* Interrupts should not be generated when adding to avail or used */
+#define VOP_F_NO_INTERRUPT 1
+
+/* Virtio-over-PCI descriptors: 12 bytes. These can chain together via "next" */
+struct vop_desc {
+ /* Address (host physical) */
+ __le32 addr;
+ /* Length (bytes) */
+ __le32 len;
+ /* Flags */
+ __le16 flags;
+ /* Chaining for descriptors */
+ __le16 next;
+} __attribute__((packed));
+
+/* Virtio-over-PCI used descriptor chains: 8 bytes */
+struct vop_used_elem {
+ /* Start index of used descriptor chain */
+ __le32 id;
+ /* Total length of the descriptor chain which was used (written to) */
+ __le32 len;
+} __attribute__((packed));
+
+/* The ring in host memory, only written by the guest */
+/* NOTE: with VOP_RING_SIZE == 64, this is 520 bytes */
+struct vop_host_ring {
+ /* The flags, so the guest can indicate that it doesn't want
+ * interrupts when things are added to the avail ring */
+ __le16 flags;
+
+ /* The index, which points at the next slot where a chain index
+ * will be added to the used ring */
+ __le16 used_idx;
+
+ /* The used ring */
+ struct vop_used_elem used[VOP_RING_SIZE];
+} __attribute__((packed));
+
+/* The ring in guest memory, only written by the host */
+/* NOTE: with VOP_RING_SIZE == 64, this is 904 bytes! */
+struct vop_guest_ring {
+ /* The descriptors */
+ struct vop_desc desc[VOP_RING_SIZE];
+
+ /* The flags, so the host can indicate that it doesn't want
+ * interrupts when things are added to the used ring */
+ __le16 flags;
+
+ /* The index, which points at the next slot where a chain index
+ * will be added to the avail ring */
+ __le16 avail_idx;
+
+ /* The avail ring */
+ __le16 avail[VOP_RING_SIZE];
+} __attribute__((packed));
+
+/*
+ * This is the status structure holding the virtio_device status
+ * as well as the feature bits for this device and the configuration
+ * space.
+ *
+ * NOTE: it is for the LOCAL device. This is the slow path, so
+ * NOTE: the mmio reads won't cause any speed problems
+ */
+struct vop_status {
+ /* Status bits for the device */
+ __le32 status;
+
+ /* Feature bits for the device (128 bits) */
+ __le32 features[4];
+
+ /* Configuration space (different for each device type) */
+ u8 config[1004];
+
+} __attribute__((packed));
+
+/*
+ * Layout in memory
+ *
+ * |--------------------------|
+ * | 0: local device status |
+ * |--------------------------|
+ * | 1024: host/guest ring 1 |
+ * |--------------------------|
+ * | 2048: host/guest ring 2 |
+ * |--------------------------|
+ * | 3072: host/guest ring 3 |
+ * |--------------------------|
+ *
+ * Now, you have one of these for each virtio device, and
+ * then you're pretty much set. You can expose 16K of memory
+ * out on the bus (on each side) and have 4 virtio devices,
+ * each with a different type, and 3 virtqueues
+ */
+
+#endif /* VOP_H */
diff --git a/drivers/virtio/vop_fsl.c b/drivers/virtio/vop_fsl.c
new file mode 100644
index 0000000..7cb3cdd
--- /dev/null
+++ b/drivers/virtio/vop_fsl.c
@@ -0,0 +1,2020 @@
+/*
+ * Virtio-over-PCI MPC8349EMDS Guest Driver
+ *
+ * Copyright (c) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_net.h>
+#include <linux/interrupt.h>
+#include <linux/virtio_net.h>
+#include <linux/dmaengine.h>
+#include <linux/workqueue.h>
+#include <linux/etherdevice.h>
+
+/* MPC8349EMDS specific get_immrbase() */
+#include <sysdev/fsl_soc.h>
+
+#include "vop_hw.h"
+#include "vop.h"
+
+/*
+ * These are internal use only versions of the structures that
+ * are exported over PCI by this driver
+ *
+ * They are used internally to keep track of the PowerPC queues so that
+ * we don't have to keep flipping endianness all the time
+ */
+struct vop_loc_desc {
+ u32 addr;
+ u32 len;
+ u16 flags;
+ u16 next;
+};
+
+struct vop_loc_avail {
+ u16 index;
+ u16 ring[VOP_RING_SIZE];
+};
+
+struct vop_loc_used_elem {
+ u32 id;
+ u32 len;
+};
+
+struct vop_loc_used {
+ u16 index;
+ struct vop_loc_used_elem ring[VOP_RING_SIZE];
+};
+
+/*
+ * DMA Resolver state information
+ */
+struct vop_dma_info {
+ struct dma_chan *chan;
+
+ /* The currently processing avail entry */
+ u16 loc_avail;
+ u16 rem_avail;
+
+ /* The currently processing used entries */
+ u16 loc_used;
+ u16 rem_used;
+};
+
+struct vop_vq {
+
+ /* The actual virtqueue itself */
+ struct virtqueue vq;
+ struct device *dev;
+
+ /* The host ring address */
+ struct vop_host_ring __iomem *host;
+
+ /* The guest ring address */
+ struct vop_guest_ring *guest;
+
+ /* Our own memory descriptors */
+ struct vop_loc_desc desc[VOP_RING_SIZE];
+ struct vop_loc_avail avail;
+ struct vop_loc_used used;
+ unsigned int flags;
+
+ /* Data tokens from add_buf() */
+ void *data[VOP_RING_SIZE];
+
+ unsigned int num_free; /* number of free descriptors in desc */
+ unsigned int free_head; /* start of the free descriptors in desc */
+ unsigned int num_added; /* number of entries added to desc */
+
+ u16 loc_last_used; /* the last local used entry processed */
+ u16 rem_last_used; /* the current value of remote used_idx */
+
+ /* DMA resolver state */
+ struct vop_dma_info dma;
+ struct work_struct work;
+ int (*resolve)(struct vop_vq *vq);
+
+ void __iomem *immr;
+ int kick_val;
+};
+
+/* Convert from a struct virtqueue to a struct vop_vq */
+#define to_vop_vq(X) container_of(X, struct vop_vq, vq)
+
+/*
+ * This represents a virtio_device for our driver. It follows the memory
+ * layout shown above. It has pointers to all of the host and guest memory
+ * areas that we need to access
+ */
+struct vop_vdev {
+
+ /* The specific virtio device (console, net, blk) */
+ struct virtio_device vdev;
+
+ #define VOP_DEVICE_REGISTERED 1
+ int status;
+
+ /* Start address of local and remote memory */
+ void *loc;
+ void __iomem *rem;
+
+ /*
+ * These are the status, feature, and configuration information
+ * for this virtio device. They are exposed in our memory block
+ * starting at offset 0.
+ */
+ struct vop_status __iomem *host_status;
+
+ /*
+ * These are the status, feature, and configuration information
+ * for the guest virtio device. They are exposed in the guest
+ * memory block starting at offset 0.
+ */
+ struct vop_status *guest_status;
+
+ /*
+ * These are the virtqueues for the virtio driver running this
+ * device to use. The host portions are exposed in our memory block
+ * starting at offset 1024. The exposed areas are aligned to 1024 byte
+ * boundaries, so they appear at offets 1024, 2048, and 3072
+ * respectively.
+ */
+ struct vop_vq virtqueues[3];
+};
+
+#define to_vop_vdev(X) container_of(X, struct vop_vdev, vdev)
+
+struct vop_dev {
+
+ struct of_device *op;
+ struct device *dev;
+
+ /* Reset and start */
+ struct mutex mutex;
+ struct work_struct reset_work;
+ struct work_struct start_work;
+
+ int irq;
+
+ /* Our board control registers */
+ void __iomem *immr;
+
+ /* The guest memory, exposed at PCI BAR1 */
+ #define VOP_GUEST_MEM_SIZE 16384
+ void *guest_mem;
+ dma_addr_t guest_mem_addr;
+
+ /* Host memory, given to us by host in OMR0 */
+ #define VOP_HOST_MEM_SIZE 16384
+ void __iomem *host_mem;
+
+ /* The virtio devices */
+ struct vop_vdev devices[4];
+ struct dma_chan *chan;
+};
+
+/*
+ * DMA callback information
+ */
+struct vop_dma_cbinfo {
+ struct vop_vq *vq;
+
+ /* The amount to increment the used rings */
+ unsigned int loc;
+ unsigned int rem;
+};
+
+static const char driver_name[] = "vdev";
+static struct kmem_cache *dma_cache;
+
+/*----------------------------------------------------------------------------*/
+/* Whole-descriptor access helpers */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Return a copy of a local descriptor in native format for easy use
+ * of all fields
+ *
+ * @vq the virtqueue
+ * @idx the descriptor index
+ * @desc pointer to the structure to copy into
+ */
+static void vop_loc_desc(struct vop_vq *vq, unsigned int idx,
+ struct vop_loc_desc *desc)
+{
+ BUG_ON(idx >= VOP_RING_SIZE);
+ BUG_ON(!desc);
+
+ desc->addr = vq->desc[idx].addr;
+ desc->len = vq->desc[idx].len;
+ desc->flags = vq->desc[idx].flags;
+ desc->next = vq->desc[idx].next;
+}
+
+/*
+ * Return a copy of a remote descriptor in native format for easy use
+ * of all fields
+ *
+ * @vq the virtqueue
+ * @idx the descriptor index
+ * @desc pointer to the structure to copy into
+ */
+static void vop_rem_desc(struct vop_vq *vq, unsigned int idx,
+ struct vop_loc_desc *desc)
+{
+ BUG_ON(idx >= VOP_RING_SIZE);
+ BUG_ON(!desc);
+
+ desc->addr = le32_to_cpu(vq->guest->desc[idx].addr);
+ desc->len = le32_to_cpu(vq->guest->desc[idx].len);
+ desc->flags = le16_to_cpu(vq->guest->desc[idx].flags);
+ desc->next = le16_to_cpu(vq->guest->desc[idx].next);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Local descriptor ring access helpers */
+/*----------------------------------------------------------------------------*/
+
+static void vop_set_desc_addr(struct vop_vq *vq, unsigned int idx, u32 addr)
+{
+ vq->desc[idx].addr = addr;
+}
+
+static void vop_set_desc_len(struct vop_vq *vq, unsigned int idx, u32 len)
+{
+ vq->desc[idx].len = len;
+}
+
+static void vop_set_desc_flags(struct vop_vq *vq, unsigned int idx, u16 flags)
+{
+ vq->desc[idx].flags = flags;
+}
+
+static void vop_set_desc_next(struct vop_vq *vq, unsigned int idx, u16 next)
+{
+ vq->desc[idx].next = next;
+}
+
+static u16 vop_get_desc_flags(struct vop_vq *vq, unsigned int idx)
+{
+ return vq->desc[idx].flags;
+}
+
+static u16 vop_get_desc_next(struct vop_vq *vq, unsigned int idx)
+{
+ return vq->desc[idx].next;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Status Helpers */
+/*----------------------------------------------------------------------------*/
+
+static u32 vop_get_host_status(struct vop_vdev *vdev)
+{
+ return ioread32(&vdev->host_status->status);
+}
+
+static u32 vop_get_host_features(struct vop_vdev *vdev)
+{
+ return ioread32(&vdev->host_status->features[0]);
+}
+
+static u16 vop_get_host_flags(struct vop_vq *vq)
+{
+ return le16_to_cpu(vq->guest->flags);
+}
+
+/*
+ * Set the guest's flags variable (lives in host memory)
+ */
+static void vop_set_guest_flags(struct vop_vq *vq, u16 flags)
+{
+ iowrite16(flags, &vq->host->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Remote Ring Debugging Helpers */
+/*----------------------------------------------------------------------------*/
+
+#ifdef DEBUG_DUMP_RINGS
+static void dump_rem_desc(struct vop_vq *vq)
+{
+ struct vop_loc_desc desc;
+ int i;
+
+ dev_dbg(vq->dev, "REM DESC 0xADDRESSX LENGTH 0xFLAG NEXT\n");
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ vop_rem_desc(vq, i, &desc);
+ dev_dbg(vq->dev, "DESC %.2d: 0x%.8x %.6d 0x%.4x %.2d\n",
+ i, desc.addr, desc.len, desc.flags, desc.next);
+ }
+}
+
+static void dump_rem_avail(struct vop_vq *vq)
+{
+ int i;
+
+ dev_dbg(vq->dev, "REM AVAIL IDX %.2d\n", le16_to_cpu(vq->guest->avail_idx));
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ dev_dbg(vq->dev, "REM AVAIL %.2d: %.2d\n",
+ i, le16_to_cpu(vq->guest->avail[i]));
+ }
+}
+
+static void dump_rem_used(struct vop_vq *vq)
+{
+ int i;
+
+ dev_dbg(vq->dev, "REM USED IDX %.2d\n", ioread16(&vq->host->used_idx));
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ dev_dbg(vq->dev, "REM USED %.2d: %.2d %.6d\n", i,
+ ioread32(&vq->host->used[i].id),
+ ioread32(&vq->host->used[i].len));
+ }
+}
+
+static void dump_rem_rings(struct vop_vq *vq)
+{
+ dump_rem_desc(vq);
+ dump_rem_avail(vq);
+ dump_rem_used(vq);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Local Ring Debugging Helpers */
+/*----------------------------------------------------------------------------*/
+
+static void dump_loc_desc(struct vop_vq *vq)
+{
+ struct vop_loc_desc desc;
+ int i;
+
+ dev_dbg(vq->dev, "LOC DESC 0xADDRESSX LENGTH 0xFLAG NEXT\n");
+ for (i = 0 ; i < VOP_RING_SIZE; i++) {
+ vop_loc_desc(vq, i, &desc);
+ dev_dbg(vq->dev, "DESC %.2d: 0x%.8x %.6d 0x%.4x %.2d\n",
+ i, desc.addr, desc.len, desc.flags, desc.next);
+ }
+}
+
+static void dump_loc_avail(struct vop_vq *vq)
+{
+ int i;
+
+ dev_dbg(vq->dev, "LOC AVAIL IDX %.2d\n", vq->avail.index);
+ for (i = 0; i < VOP_RING_SIZE; i++)
+ dev_dbg(vq->dev, "LOC AVAIL %.2d: %.2d\n", i, vq->avail.ring[i]);
+}
+
+static void dump_loc_used(struct vop_vq *vq)
+{
+ int i;
+
+ dev_dbg(vq->dev, "LOC USED IDX %.2hu\n", vq->used.index);
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ dev_dbg(vq->dev, "LOC USED %.2d: %.2d %.6d\n", i,
+ vq->used.ring[i].id, vq->used.ring[i].len);
+ }
+}
+
+static void dump_loc_rings(struct vop_vq *vq)
+{
+ dump_loc_desc(vq);
+ dump_loc_avail(vq);
+ dump_loc_used(vq);
+}
+
+static void debug_dump_rings(struct vop_vq *vq, const char *msg)
+{
+ dev_dbg(vq->dev, "\n");
+ dev_dbg(vq->dev, "%s\n", msg);
+ dump_loc_rings(vq);
+ dump_rem_rings(vq);
+ dev_dbg(vq->dev, "\n");
+}
+#else
+static void debug_dump_rings(struct vop_vq *vq, const char *msg)
+{
+ /* Nothing */
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+/* Scatterlist DMA helpers */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * This function abuses some of the scatterlist code and implements
+ * dma_map_sg() in such a way that we don't need to keep the scatterlist
+ * around in order to unmap it.
+ *
+ * It is also designed to never merge scatterlist entries, which is
+ * never what we want for virtio.
+ *
+ * When it is time to unmap the buffer, you can use dma_unmap_single() to
+ * unmap each entry in the chain. Get the address, length, and direction
+ * from the descriptors! (keep a local copy for speed)
+ */
+static int vop_dma_map_sg(struct device *dev, struct scatterlist sg[],
+ unsigned int out, unsigned int in)
+{
+ dma_addr_t addr;
+ enum dma_data_direction dir;
+ struct scatterlist *start;
+ unsigned int i, failure;
+
+ start = sg;
+
+ for (i = 0; i < out + in; i++) {
+
+ /* Check for scatterlist chaining abuse */
+ BUG_ON(sg == NULL);
+
+ dir = (i < out) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ addr = dma_map_single(dev, sg_virt(sg), sg->length, dir);
+
+ if (dma_mapping_error(dev, addr))
+ goto unwind;
+
+ sg_dma_address(sg) = addr;
+ sg = sg_next(sg);
+ }
+
+ return 0;
+
+unwind:
+ failure = i;
+ sg = start;
+
+ for (i = 0; i < failure; i++) {
+ dir = (i < out) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ addr = sg_dma_address(sg);
+
+ dma_unmap_single(dev, addr, sg->length, dir);
+ sg = sg_next(sg);
+ }
+
+ return -ENOMEM;
+}
+
+/*----------------------------------------------------------------------------*/
+/* DMA Helpers */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Transfer data between two physical addresses with DMA
+ *
+ * NOTE: does not automatically unmap the src and dst addresses
+ *
+ * @chan the channel to use
+ * @dst the physical destination address
+ * @src the physical source address
+ * @len the length to transfer (in bytes)
+ * @return a valid cookie, or -ERRNO
+ */
+static dma_cookie_t dma_async_memcpy_raw_to_raw(struct dma_chan *chan,
+ dma_addr_t dst,
+ dma_addr_t src,
+ size_t len)
+{
+ struct dma_device *dev = chan->device;
+ struct dma_async_tx_descriptor *tx;
+ enum dma_ctrl_flags flags;
+ dma_cookie_t cookie;
+ int cpu;
+
+ flags = DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP;
+ tx = dev->device_prep_dma_memcpy(chan, dst, src, len, flags);
+ if (!tx)
+ return -ENOMEM;
+
+ tx->callback = NULL;
+ cookie = tx->tx_submit(tx);
+
+ cpu = get_cpu();
+ per_cpu_ptr(chan->local, cpu)->bytes_transferred += len;
+ per_cpu_ptr(chan->local, cpu)->memcpy_count++;
+ put_cpu();
+
+ return cookie;
+}
+
+/*
+ * Trigger an interrupt after all DMA issued up to this point
+ * have been processed
+ *
+ * @chan the channel to use
+ * @callback the function to call (must not sleep)
+ * @data the data to send to the callback
+ *
+ * @return a valid cookie, or -ERRNO
+ */
+static dma_cookie_t dma_async_interrupt(struct dma_chan *chan,
+ dma_async_tx_callback callback,
+ void *data)
+{
+ struct dma_device *dev = chan->device;
+ struct dma_async_tx_descriptor *tx;
+
+ /* Set up the DMA */
+ tx = dev->device_prep_dma_interrupt(chan, DMA_PREP_INTERRUPT);
+ if (!tx)
+ return -ENOMEM;
+
+ tx->callback = callback;
+ tx->callback_param = data;
+
+ return tx->tx_submit(tx);
+}
+
+/*----------------------------------------------------------------------------*/
+/* DMA Resolver */
+/*----------------------------------------------------------------------------*/
+
+static void vop_remote_used_changed(struct vop_vq *vq)
+{
+ if (!(vop_get_host_flags(vq) & VOP_F_NO_INTERRUPT)) {
+ dev_dbg(vq->dev, "notifying the host (new buffers in used)\n");
+ iowrite32(vq->kick_val, vq->immr + ODR_OFFSET);
+ }
+}
+
+static void vop_local_used_changed(struct vop_vq *vq)
+{
+ if (!(vq->flags & VOP_F_NO_INTERRUPT)) {
+ dev_dbg(vq->dev, "notifying self (new buffers in used)\n");
+ vq->vq.callback(&vq->vq);
+ }
+}
+
+/*
+ * DMA callback function for merged rxbufs
+ *
+ * This is called every time a DMA transfer completes, and will update the
+ * indices in the local and remote used rings, then notify both sides that
+ * their used ring has changed
+ *
+ * You must be sure that the data was actually written to the used rings before
+ * this function is called
+ */
+static void dma_callback(void *data)
+{
+ struct vop_dma_cbinfo *cb = data;
+ struct vop_vq *vq = cb->vq;
+
+ dev_dbg(vq->dev, "%s: vq %p loc %d rem %d\n", __func__, vq, cb->loc, cb->rem);
+
+ /* Write the local used index */
+ vq->used.index += cb->loc;
+
+ /* Write the remote used index */
+ vq->rem_last_used += cb->rem;
+ iowrite16(vq->rem_last_used, &vq->host->used_idx);
+
+ /* Make sure the indices are written before triggering callbacks */
+ wmb();
+
+ /* Trigger the local used callback */
+ dev_dbg(vq->dev, "local used changed, running callback\n");
+ vop_local_used_changed(vq);
+
+ /* Trigger the remote used callback */
+ dev_dbg(vq->dev, "remote used changed, running callback\n");
+ vop_remote_used_changed(vq);
+
+ /* Free the callback data */
+ kmem_cache_free(dma_cache, cb);
+}
+
+/*
+ * Take an entry from the local avail ring and add it to the local
+ * used ring with the given length
+ *
+ * NOTE: does not update the used index
+ *
+ * @vq the virtqueue
+ * @avail_idx the index in the avail ring to take the entry from
+ * @used_idx the index in the used ring to put the entry
+ * @used_len the length used
+ */
+static void vop_loc_avail_to_used(struct vop_vq *vq, unsigned int avail_idx,
+ unsigned int used_idx, u32 used_len)
+{
+ u16 id;
+
+ /* Make sure the indices are inside the rings */
+ avail_idx &= (VOP_RING_SIZE - 1);
+ used_idx &= (VOP_RING_SIZE - 1);
+
+ /* Get the index stored in the avail ring */
+ id = vq->avail.ring[avail_idx];
+
+ /* Copy the index and length to the used ring */
+ vq->used.ring[used_idx].id = id;
+ vq->used.ring[used_idx].len = used_len;
+}
+
+/*
+ * Take an entry from the remote avail ring and add it to the remote
+ * used ring with the given length
+ *
+ * NOTE: does not update the used index
+ *
+ * @vq the virtqueue
+ * @avail_idx the index in the avail ring to take the entry from
+ * @used_idx the index in the used ring to put the entry
+ * @used_len the length used
+ */
+static void vop_rem_avail_to_used(struct vop_vq *vq, unsigned int avail_idx,
+ unsigned int used_idx, u32 used_len)
+{
+ u16 id;
+
+ /* Make sure the indices are inside the rings */
+ avail_idx &= (VOP_RING_SIZE - 1);
+ used_idx &= (VOP_RING_SIZE - 1);
+
+ /* Get the index stored in the avail ring */
+ id = le16_to_cpu(vq->guest->avail[avail_idx]);
+
+ /* Copy the index and length to the used ring */
+ iowrite32(id, &vq->host->used[used_idx].id);
+ iowrite32(used_len, &vq->host->used[used_idx].len);
+}
+
+/*
+ * Return the number of entries available in the local avail ring
+ */
+static unsigned int loc_num_avail(struct vop_vq *vq)
+{
+ return vq->avail.index - vq->dma.loc_avail;
+}
+
+/*
+ * Return the number of entries available in the remote avail ring
+ */
+static unsigned int rem_num_avail(struct vop_vq *vq)
+{
+ return le16_to_cpu(vq->guest->avail_idx) - vq->dma.rem_avail;
+}
+
+/*
+ * Return a descriptor id from the local avail ring
+ *
+ * @vq the virtqueue
+ * @idx the index to return the id from
+ */
+static u16 vop_loc_avail_id(struct vop_vq *vq, unsigned int idx)
+{
+ idx &= (VOP_RING_SIZE - 1);
+ return vq->avail.ring[idx];
+}
+
+/*
+ * Return a descriptor id from the remote avail ring
+ *
+ * @vq the virtqueue
+ * @idx the index to return the id from
+ */
+static u16 vop_rem_avail_id(struct vop_vq *vq, unsigned int idx)
+{
+ idx &= (VOP_RING_SIZE - 1);
+ return le16_to_cpu(vq->guest->avail[idx]);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Extra helpers for mergeable DMA */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * TODO: the number of bytes being transmitted could be added to the avail
+ * TODO: ring, rather than just an index. I'm not sure it would make much
+ * TODO: difference, though.
+ */
+
+/*
+ * Calculate the number of bytes used in a local descriptor chain
+ *
+ * @vq the virtqueue
+ * @idx the start descriptor index
+ * @return the number of bytes
+ */
+static unsigned int loc_num_bytes(struct vop_vq *vq, unsigned int idx)
+{
+ struct vop_loc_desc desc;
+ unsigned int bytes = 0;
+
+ while (true) {
+ vop_loc_desc(vq, idx, &desc);
+ bytes += desc.len;
+
+ if (!(desc.flags & VOP_DESC_F_NEXT))
+ break;
+
+ idx = desc.next;
+ }
+
+ return bytes;
+}
+
+/*
+ * Calculate the number of bytes used in a remote descriptor chain
+ *
+ * @vq the virtqueue
+ * @idx the start descriptor index
+ * @return the number of bytes
+ */
+static unsigned int rem_num_bytes(struct vop_vq *vq, unsigned int idx)
+{
+ struct vop_loc_desc desc;
+ unsigned int bytes = 0;
+
+ while (true) {
+ vop_rem_desc(vq, idx, &desc);
+ bytes += desc.len;
+
+ if (!(desc.flags & VOP_DESC_F_NEXT))
+ break;
+
+ idx = desc.next;
+ }
+
+ return bytes;
+}
+
+/*
+ * Transmit the next local available entry to the remote side, splitting
+ * up the local descriptor as needed
+ *
+ * This routine makes the following assumptions:
+ * 1) The header already has the correct number of buffers set
+ * 2) The available buffers are all PAGE_SIZE
+ */
+static int vop_dma_xmit(struct vop_vq *vq)
+{
+ struct vop_dma_info *dma = &vq->dma;
+ struct dma_chan *chan = dma->chan;
+ dma_cookie_t cookie;
+
+ unsigned int loc_idx, rem_idx;
+ struct vop_loc_desc loc, rem;
+
+ struct vop_dma_cbinfo *cb;
+ dma_addr_t src, dst;
+ size_t len;
+
+ unsigned int loc_total = 0;
+ unsigned int rem_total = 0;
+ unsigned int bufs_used = 0;
+
+ /* Check that there is a local descriptor available */
+ if (!loc_num_avail(vq)) {
+ dev_dbg(vq->dev, "No local descriptors available\n");
+ return -ENOSPC;
+ }
+
+ /* Get the starting entry from each available ring */
+ loc_idx = vop_loc_avail_id(vq, dma->loc_avail);
+ rem_idx = vop_rem_avail_id(vq, dma->rem_avail);
+
+ dev_dbg(vq->dev, "rem_avail %d loc_num_bytes %d\n", rem_num_avail(vq), loc_num_bytes(vq, loc_idx));
+
+ /* Check that there are enough remote buffers available */
+ if (rem_num_avail(vq) * PAGE_SIZE < loc_num_bytes(vq, loc_idx)) {
+ dev_dbg(vq->dev, "Insufficient remote descriptors available\n");
+ return -ENOSPC;
+ }
+
+ /* Allocate DMA callback data */
+ cb = kmem_cache_alloc(dma_cache, GFP_KERNEL);
+ if (!cb) {
+ dev_dbg(vq->dev, "Unable to allocate DMA callback data\n");
+ return -ENOMEM;
+ }
+
+ /* Load the starting descriptors */
+ vop_loc_desc(vq, loc_idx, &loc);
+ vop_rem_desc(vq, rem_idx, &rem);
+
+ while (true) {
+
+ dst = rem.addr + 0x80000000;
+ src = loc.addr;
+ len = min(loc.len, rem.len);
+
+ dev_dbg(vq->dev, "DMA xmit dst %.8x src %.8x len %d\n", dst, src, len);
+ cookie = dma_async_memcpy_raw_to_raw(chan, dst, src, len);
+ if (dma_submit_error(cookie)) {
+ dev_err(vq->dev, "DMA submit error\n");
+ goto out_free_cb;
+ }
+
+ loc.len -= len;
+ rem.len -= len;
+ loc.addr += len;
+ rem.addr += len;
+
+ loc_total += len;
+ rem_total += len;
+
+ dev_dbg(vq->dev, "loc.len %d rem.len %d\n", loc.len, rem.len);
+ dev_dbg(vq->dev, "loc.addr %.8x rem.addr %.8x\n", loc.addr, rem.addr);
+ dev_dbg(vq->dev, "loc_total %d rem_total %d\n", loc_total, rem_total);
+
+ if (loc.len == 0) {
+ dev_dbg(vq->dev, "local: descriptor depleted, loading next\n");
+
+ if (!(loc.flags & VOP_DESC_F_NEXT)) {
+ dev_dbg(vq->dev, "local: no next descriptor, chain finished\n");
+ break;
+ }
+
+ dev_dbg(vq->dev, "local: fetching next descriptor\n");
+ loc_idx = loc.next;
+ vop_loc_desc(vq, loc_idx, &loc);
+ }
+
+ if (rem.len == 0) {
+ dev_dbg(vq->dev, "remote: descriptor depleted, adding to used\n");
+ vop_rem_avail_to_used(vq, dma->rem_avail + bufs_used, dma->rem_used + bufs_used, rem_total);
+ bufs_used++;
+
+ dev_dbg(vq->dev, "remote: fetching next descriptor\n");
+ rem_idx = vop_rem_avail_id(vq, dma->rem_avail + bufs_used);
+ vop_rem_desc(vq, rem_idx, &rem);
+ rem_total = 0;
+ }
+ }
+
+ /* Add the last remote descriptor to the used ring */
+ BUG_ON(rem_total == 0);
+ dev_dbg(vq->dev, "adding last remote descriptor to used ring\n");
+ vop_rem_avail_to_used(vq, dma->rem_avail + bufs_used, dma->rem_used + bufs_used, rem_total);
+ bufs_used++;
+
+ /* Add the local descriptor to the sude ring */
+ dev_dbg(vq->dev, "adding only local descriptor to used ring\n");
+ vop_loc_avail_to_used(vq, dma->loc_avail, dma->loc_used, loc_total);
+
+ /* Make very sure that everything written to the rings actually happened
+ * bofer the DMA callback can be triggered */
+ wmb();
+
+ /* Set up the DMA callback information */
+ cb->vq = vq;
+ cb->loc = 1;
+ cb->rem = bufs_used;
+
+ dev_dbg(vq->dev, "setup DMA callback vq %p loc %d rem %d\n", vq, 1, bufs_used);
+
+ /* Trigger an interrupt when the DMA completes to update the used
+ * indices and trigger the necessary callbacks */
+ cookie = dma_async_interrupt(chan, dma_callback, cb);
+ if (dma_submit_error(cookie)) {
+ dev_err(vq->dev, "DMA interrupt submit error\n");
+ goto out_free_cb;
+ }
+
+ /* Everything was successful, so update the DMA resolver's state */
+ dma->loc_avail++;
+ dma->rem_avail += bufs_used;
+ dma->loc_used++;
+ dma->rem_used += bufs_used;
+
+ /* Start the DMA */
+ dev_dbg(vq->dev, "DMA xmit setup successful, starting\n");
+ dma_async_memcpy_issue_pending(chan);
+
+ return 0;
+
+out_free_cb:
+ kmem_cache_free(dma_cache, cb);
+ return -ENOMEM;
+}
+
+/*
+ * Receive the next remote available entry to the local side, splitting
+ * up the remote descriptor as needed
+ *
+ * This routine makes the following assumptions:
+ * 1) The header already has the correct number of buffers set
+ * 2) The available buffers are all PAGE_SIZE
+ */
+static int vop_dma_recv(struct vop_vq *vq)
+{
+ struct vop_dma_info *dma = &vq->dma;
+ struct dma_chan *chan = dma->chan;
+ dma_cookie_t cookie;
+
+ unsigned int loc_idx, rem_idx;
+ struct vop_loc_desc loc, rem;
+
+ struct vop_dma_cbinfo *cb;
+ dma_addr_t src, dst;
+ size_t len;
+
+ unsigned int loc_total = 0;
+ unsigned int rem_total = 0;
+ unsigned int bufs_used = 0;
+
+ /* Check that there is a remote descriptor available */
+ if (!rem_num_avail(vq)) {
+ dev_dbg(vq->dev, "No remote descriptors available\n");
+ return -ENOSPC;
+ }
+
+ /* Get the starting entry from each available ring */
+ loc_idx = vop_loc_avail_id(vq, dma->loc_avail);
+ rem_idx = vop_rem_avail_id(vq, dma->rem_avail);
+
+ /* Check that there are enough local buffers available */
+ if (loc_num_avail(vq) * PAGE_SIZE < rem_num_bytes(vq, rem_idx)) {
+ dev_dbg(vq->dev, "Insufficient local descriptors available\n");
+ return -ENOSPC;
+ }
+
+ /* Allocate DMA callback data */
+ cb = kmem_cache_alloc(dma_cache, GFP_KERNEL);
+ if (!cb) {
+ dev_dbg(vq->dev, "Unable to allocate DMA callback data\n");
+ return -ENOMEM;
+ }
+
+ /* Load the starting descriptors */
+ vop_loc_desc(vq, loc_idx, &loc);
+ vop_rem_desc(vq, rem_idx, &rem);
+
+ while (true) {
+
+ dst = loc.addr;
+ src = rem.addr + 0x80000000;
+ len = min(loc.len, rem.len);
+
+ dev_dbg(vq->dev, "DMA recv dst %.8x src %.8x len %d\n", dst, src, len);
+ cookie = dma_async_memcpy_raw_to_raw(chan, dst, src, len);
+ if (dma_submit_error(cookie)) {
+ dev_err(vq->dev, "DMA submit error\n");
+ goto out_free_cb;
+ }
+
+ loc.len -= len;
+ rem.len -= len;
+ loc.addr += len;
+ rem.addr += len;
+
+ loc_total += len;
+ rem_total += len;
+
+ if (rem.len == 0) {
+ if (!(rem.flags & VOP_DESC_F_NEXT))
+ break;
+
+ rem_idx = rem.next;
+ vop_rem_desc(vq, rem_idx, &rem);
+ }
+
+ if (loc.len == 0) {
+ vop_loc_avail_to_used(vq, dma->loc_avail + bufs_used, dma->loc_used + bufs_used, loc_total);
+ bufs_used++;
+
+ loc_idx = vop_loc_avail_id(vq, dma->loc_avail + bufs_used);
+ vop_loc_desc(vq, loc_idx, &loc);
+ loc_total = 0;
+ }
+ }
+
+ /* Add the last local descriptor to the used ring */
+ BUG_ON(loc_total == 0);
+ vop_loc_avail_to_used(vq, dma->loc_avail + bufs_used, dma->loc_used + bufs_used, loc_total);
+ bufs_used++;
+
+ /* Add the remote descriptor to the used ring */
+ vop_rem_avail_to_used(vq, dma->rem_avail, dma->rem_used, rem_total);
+
+ /* Make very sure that everything written to the rings actually happened
+ * before the DMA callback can be triggered */
+ wmb();
+
+ /* Set up the DMA callback information */
+ cb->vq = vq;
+ cb->loc = bufs_used;
+ cb->rem = 1;
+
+ /* Trigger an interrupt when the DMA completes to update the used
+ * indices and trigger the necessary callbacks */
+ cookie = dma_async_interrupt(chan, dma_callback, cb);
+ if (dma_submit_error(cookie)) {
+ dev_err(vq->dev, "DMA interrupt submit error\n");
+ goto out_free_cb;
+ }
+
+ /* Everything was successful, so update the DMA resolver's state */
+ dma->loc_avail += bufs_used;
+ dma->rem_avail++;
+ dma->loc_used += bufs_used;
+ dma->rem_used++;
+
+ /* Start the DMA */
+ dev_dbg(vq->dev, "DMA recv setup successful, starting\n");
+ dma_async_memcpy_issue_pending(chan);
+
+ return 0;
+
+out_free_cb:
+ kmem_cache_free(dma_cache, cb);
+ return -ENOMEM;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Virtqueue Ops Infrastructure */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Modify the struct virtio_net_hdr_mrg_rxbuf's num_buffers field to account
+ * for the split that will happen in the DMA xmit routine
+ *
+ * This assumes that both sides have the same PAGE_SIZE
+ */
+static void vop_fixup_vnet_mrg_hdr(struct scatterlist sg[], unsigned int out)
+{
+ struct virtio_net_hdr *hdr;
+ struct virtio_net_hdr_mrg_rxbuf *mhdr;
+ unsigned int bytes = 0;
+
+ /* There must be a header + data, at the least */
+ BUG_ON(out < 2);
+
+ /* The first entry must be the structure */
+ BUG_ON(sg->length != sizeof(struct virtio_net_hdr_mrg_rxbuf));
+
+ hdr = sg_virt(sg);
+ mhdr = sg_virt(sg);
+
+ /* We merge buffers together, so just count up the number of bytes
+ * needed, then figure out how many pages that will be */
+ for (/* none */; out; out--, sg = sg_next(sg))
+ bytes += sg->length;
+
+ /* Of course, nobody ever imagined that we might actually use
+ * this on machines with different endianness...
+ *
+ * We force little-endian for now, since that's what our host is */
+ mhdr->num_buffers = cpu_to_le16(DIV_ROUND_UP(bytes, PAGE_SIZE));
+
+ /* Might as well fix up the other fields while we're at it */
+ hdr->hdr_len = cpu_to_le16(hdr->hdr_len);
+ hdr->gso_size = cpu_to_le16(hdr->gso_size);
+ hdr->csum_start = cpu_to_le16(hdr->csum_start);
+ hdr->csum_offset = cpu_to_le16(hdr->csum_offset);
+}
+
+/*
+ * Add a buffer to our local descriptors and the local avail ring
+ *
+ * NOTE: there hasn't been any transfer yet, just adding to local
+ * NOTE: rings. The kick() will process any DMA that needs to happen
+ *
+ * @return 0 on success, -ERRNO otherwise
+ */
+static int vop_add_buf(struct virtqueue *_vq, struct scatterlist sg[],
+ unsigned int out, unsigned int in, void *data)
+{
+ /* For now, we'll just add the buffers to our local descriptors and
+ * avail ring */
+ struct vop_vq *vq = to_vop_vq(_vq);
+ unsigned int i, avail, head, uninitialized_var(prev);
+
+ BUG_ON(data == NULL);
+ BUG_ON(out + in == 0);
+
+ /* Make sure we have space for this to succeed */
+ if (vq->num_free < out + in) {
+ dev_dbg(vq->dev, "No free space left: len=%d free=%d\n",
+ out + in, vq->num_free);
+ return -ENOSPC;
+ }
+
+ /* If this is an xmit buffer from virtio_net, fixup the header */
+ if (out > 1) {
+ dev_dbg(vq->dev, "Fixing up virtio_net header\n");
+ vop_fixup_vnet_mrg_hdr(sg, out);
+ }
+
+ head = vq->free_head;
+
+ /* DMA map the scatterlist */
+ if (vop_dma_map_sg(vq->dev, sg, out, in)) {
+ dev_err(vq->dev, "Failed to DMA map scatterlist\n");
+ return -ENOMEM;
+ }
+
+ /* We're about to use some buffers from the free list */
+ vq->num_free -= out + in;
+
+ for (i = vq->free_head; out; i = vop_get_desc_next(vq, i), out--) {
+ vop_set_desc_flags(vq, i, VOP_DESC_F_NEXT);
+ vop_set_desc_addr(vq, i, sg_dma_address(sg));
+ vop_set_desc_len(vq, i, sg->length);
+
+ prev = i;
+ sg = sg_next(sg);
+ }
+
+ for (/* none */; in; i = vop_get_desc_next(vq, i), in--) {
+ vop_set_desc_flags(vq, i, VOP_DESC_F_NEXT | VOP_DESC_F_WRITE);
+ vop_set_desc_addr(vq, i, sg_dma_address(sg));
+ vop_set_desc_len(vq, i, sg->length);
+
+ prev = i;
+ sg = sg_next(sg);
+ }
+
+ /* Last one doesn't continue */
+ vop_set_desc_flags(vq, prev, vop_get_desc_flags(vq, prev) & ~VOP_DESC_F_NEXT);
+
+ /* Update the free pointer */
+ vq->free_head = i;
+
+ /* Set token */
+ vq->data[head] = data;
+
+ /* Add an entry for the head of the chain into the avail array, but
+ * don't update avail->idx until kick() */
+ avail = (vq->avail.index + vq->num_added++) & (VOP_RING_SIZE - 1);
+ vq->avail.ring[avail] = head;
+
+ dev_dbg(vq->dev, "Added buffer head %i to %p\n", head, vq);
+ debug_dump_rings(vq, "Added buffer(s), dumping rings");
+
+ return 0;
+}
+
+static inline bool loc_more_used(const struct vop_vq *vq)
+{
+ return vq->loc_last_used != vq->used.index;
+}
+
+static void detach_buf(struct vop_vq *vq, unsigned int head)
+{
+ dma_addr_t addr;
+ unsigned int idx, len;
+ enum dma_data_direction dir;
+ struct vop_loc_desc desc;
+
+ /* Clear data pointer */
+ vq->data[head] = NULL;
+
+ /* Put the chain back on the free list, unmapping as we go */
+ idx = head;
+ while (true) {
+ vop_loc_desc(vq, idx, &desc);
+
+ addr = desc.addr;
+ len = desc.len;
+ dir = (desc.flags & VOP_DESC_F_WRITE) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ /* Unmap the entry */
+ dma_unmap_single(vq->dev, addr, len, dir);
+ vq->num_free++;
+
+ /* If there is no next descriptor, we're done */
+ if (!(desc.flags & VOP_DESC_F_NEXT))
+ break;
+
+ idx = desc.next;
+ }
+
+ vop_set_desc_next(vq, idx, vq->free_head);
+ vq->free_head = head;
+}
+
+/*
+ * Get a buffer from the used ring
+ *
+ * @return the data token given to add_buf(), or NULL if there
+ * are no remaining buffers
+ */
+static void *vop_get_buf(struct virtqueue *_vq, unsigned int *len)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+ unsigned int head, used;
+ void *ret;
+
+ if (!loc_more_used(vq)) {
+ dev_dbg(vq->dev, "No more buffers in queue\n");
+ return NULL;
+ }
+
+ used = vq->loc_last_used & (VOP_RING_SIZE - 1);
+ head = vq->used.ring[used].id;
+ *len = vq->used.ring[used].len;
+
+ BUG_ON(head >= VOP_RING_SIZE);
+ BUG_ON(!vq->data[head]);
+
+ /* detach_buf() clears data, save it now */
+ ret = vq->data[head];
+ detach_buf(vq, head);
+
+ /* Update the last local used_idx */
+ vq->loc_last_used++;
+
+ return ret;
+}
+
+/*
+ * The avail ring changed, so we need to start as much DMA as we can
+ */
+static void vop_kick(struct virtqueue *_vq)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+
+ dev_dbg(vq->dev, "kick: making %d new buffers available\n", vq->num_added);
+ vq->avail.index += vq->num_added;
+ vq->num_added = 0;
+
+ /* Run the DMA resolver */
+ dev_dbg(vq->dev, "kick: using resolver %pS\n", vq->resolve);
+ schedule_work(&vq->work);
+}
+
+/*
+ * Try to disable callbacks on the used ring (unreliable)
+ */
+static void vop_disable_cb(struct virtqueue *_vq)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+ struct virtio_device *vdev = _vq->vdev;
+
+ dev_dbg(&vdev->dev, "disable callbacks\n");
+ vq->flags = VOP_F_NO_INTERRUPT;
+#if 0
+ /*
+ * FIXME: using this causes the host -> guest transfer rate to
+ * FIXME: intermittently slow to 1/10th of the normal rate
+ */
+ vop_set_guest_flags(vq, vq->flags);
+#endif
+}
+
+/*
+ * Enable callbacks on changes to the used ring
+ *
+ * @return false if there are more pending buffers
+ * true otherwise
+ */
+static bool vop_enable_cb(struct virtqueue *_vq)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+
+ /* We optimistically enable interrupts, then check if there
+ * was more work to do */
+ dev_dbg(vq->dev, "enable callbacks\n");
+ vq->flags = 0;
+#if 0
+ /*
+ * FIXME: using this causes the host -> guest transfer rate to
+ * FIXME: intermittently slow to 1/10th of the normal rate
+ */
+ vop_set_guest_flags(vq, vq->flags);
+#endif
+
+ if (unlikely(loc_more_used(vq)))
+ return false;
+
+ return true;
+}
+
+static struct virtqueue_ops vop_vq_ops = {
+ .add_buf = vop_add_buf,
+ .get_buf = vop_get_buf,
+ .kick = vop_kick,
+ .disable_cb = vop_disable_cb,
+ .enable_cb = vop_enable_cb,
+};
+
+/*----------------------------------------------------------------------------*/
+/* Virtio Device Infrastructure */
+/*----------------------------------------------------------------------------*/
+
+/* Read some bytes from the host's configuration area */
+static void vopc_get(struct virtio_device *_vdev, unsigned offset, void *buf,
+ unsigned len)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ void __iomem *config = vdev->host_status->config;
+
+ memcpy_fromio(buf, config + offset, len);
+}
+
+/* Write some bytes to the host's configuration area */
+static void vopc_set(struct virtio_device *_vdev, unsigned offset,
+ const void *buf, unsigned len)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ void __iomem *config = vdev->host_status->config;
+
+ memcpy_toio(config + offset, buf, len);
+}
+
+/* Read your own status bits */
+static u8 vopc_get_status(struct virtio_device *_vdev)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ u32 status;
+
+ status = le32_to_cpu(vdev->guest_status->status);
+ dev_dbg(&vdev->vdev.dev, "%s(): -> 0x%.2x\n", __func__, (u8)status);
+
+ return (u8)status;
+}
+
+static void vopc_set_status(struct virtio_device *_vdev, u8 status)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ u32 old_status;
+
+ old_status = le32_to_cpu(vdev->guest_status->status);
+ vdev->guest_status->status = cpu_to_le32(status);
+
+ dev_dbg(&vdev->vdev.dev, "%s(): <- 0x%.2x (was 0x%.2x)\n",
+ __func__, status, old_status);
+
+ /*
+ * FIXME: we really need to notify the other side when status changes
+ * FIXME: happen, so that they can take some action
+ */
+}
+
+static void vopc_reset(struct virtio_device *_vdev)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+
+ dev_dbg(&vdev->vdev.dev, "%s(): status reset\n", __func__);
+ vdev->guest_status->status = cpu_to_le32(0);
+}
+
+/* Find the given virtqueue */
+static struct virtqueue *vopc_find_vq(struct virtio_device *_vdev,
+ unsigned index,
+ void (*cb)(struct virtqueue *vq))
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ struct vop_vq *vq = &vdev->virtqueues[index];
+ int i;
+
+ /* Check that we support the virtqueue at this index */
+ if (index >= ARRAY_SIZE(vdev->virtqueues)) {
+ dev_err(&vdev->vdev.dev, "no virtqueue for index %d\n", index);
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* HACK: we only support virtio_net for now */
+ if (vdev->vdev.id.device != VIRTIO_ID_NET) {
+ dev_err(&vdev->vdev.dev, "only virtio_net is supported\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* Initialize the virtqueue to a clean state */
+ vq->num_free = VOP_RING_SIZE;
+ vq->dev = &vdev->vdev.dev;
+ vq->vq.vq_ops = &vop_vq_ops;
+
+ /* Hook up the local virtqueues to the corresponding remote virtqueues */
+ /* TODO: maybe move this to the setup_virtio_net() function */
+ switch (index) {
+ case 0: /* x86 xmit virtqueue, hook to ppc recv virtqueue */
+ vq->guest = vdev->loc + 2048;
+ vq->host = vdev->rem + 2048;
+ vq->resolve = vop_dma_recv;
+ vq->kick_val = 0x8;
+ break;
+ case 1: /* x86 recv virtqueue, hook to ppc xmit virtqueue */
+ vq->guest = vdev->loc + 1024;
+ vq->host = vdev->rem + 1024;
+ vq->resolve = vop_dma_xmit;
+ vq->kick_val = 0x4;
+ break;
+ case 2: /* x86 ctrl virtqueue -- ppc ctrl virtqueue */
+ default:
+ dev_err(vq->dev, "Unsupported virtqueue\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ dev_dbg(vq->dev, "vq %d guest %p host %p\n", index, vq->guest, vq->host);
+
+ /* Initialize the descriptor, avail, and used rings */
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ vop_set_desc_addr(vq, i, 0x0);
+ vop_set_desc_len(vq, i, 0);
+ vop_set_desc_flags(vq, i, 0);
+ vop_set_desc_next(vq, i, (i + 1) & (VOP_RING_SIZE - 1));
+
+ vq->avail.ring[i] = 0;
+ vq->used.ring[i].id = 0;
+ vq->used.ring[i].len = 0;
+ }
+
+ vq->avail.index = 0;
+ vop_set_guest_flags(vq, 0);
+
+ /* This is the guest, the host has already initialized the rings for us */
+ debug_dump_rings(vq, "found a virtqueue, dumping rings");
+
+ vq->vq.callback = cb;
+ vq->vq.vdev = &vdev->vdev;
+
+ return &vq->vq;
+}
+
+static void vopc_del_vq(struct virtqueue *_vq)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+ int i;
+
+ /* FIXME: make sure that DMA has stopped by this point */
+
+ /* Unmap and remove all outstanding descriptors from the ring */
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ if (vq->data[i]) {
+ dev_dbg(vq->dev, "cleanup detach buffer at index %d\n", i);
+ detach_buf(vq, i);
+ }
+ }
+
+ debug_dump_rings(vq, "virtqueue destroyed, dumping rings");
+}
+
+/* Read the host's advertised features */
+static u32 vopc_get_features(struct virtio_device *_vdev)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ u32 ret;
+
+ ret = vop_get_host_features(vdev);
+ dev_dbg(&vdev->vdev.dev, "%s(): host features 0x%.8x\n", __func__, ret);
+
+ return ret;
+}
+
+/* At this point, we've chosen whichever features we can use and
+ * put them into the vdev->features array. We should probably notify
+ * the host at this point, but how will virtio react? */
+static void vopc_finalize_features(struct virtio_device *_vdev)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ struct device *dev = &vdev->vdev.dev;
+
+ /*
+ * TODO: notify the other side at this point
+ */
+
+ vdev->guest_status->features[0] = cpu_to_le32(vdev->vdev.features[0]);
+ dev_dbg(dev, "%s(): final features 0x%.8lx\n", __func__, vdev->vdev.features[0]);
+}
+
+static struct virtio_config_ops vop_config_ops = {
+ .get = vopc_get,
+ .set = vopc_set,
+ .get_status = vopc_get_status,
+ .set_status = vopc_set_status,
+ .reset = vopc_reset,
+ .find_vq = vopc_find_vq,
+ .del_vq = vopc_del_vq,
+ .get_features = vopc_get_features,
+ .finalize_features = vopc_finalize_features,
+};
+
+/*----------------------------------------------------------------------------*/
+/* Last-minute device setup code */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Do the last minute setup for virtio_net, now that the host memory is
+ * valid. This includes setting up pointers to the correct queues so that
+ * we can just start the virtqueues when the driver registers
+ */
+static void setup_virtio_net(struct vop_vdev *vdev)
+{
+ /* TODO: move some of the setup code from find_vq() here */
+}
+
+/*
+ * Do any last minute setup for a device just before starting it
+ *
+ * The host memory is now valid, so you should be setting up any pointers
+ * the device needs to the host memory
+ */
+static int vop_setup_device(struct vop_dev *priv, int devnum)
+{
+ struct vop_vdev *vdev = &priv->devices[devnum];
+ struct device *dev = priv->dev;
+
+ if (devnum >= ARRAY_SIZE(priv->devices)) {
+ dev_err(dev, "Unknown virtio_device %d\n", devnum);
+ return -ENODEV;
+ }
+
+ /* Setup the device's pointers to host memory */
+ vdev->rem = priv->host_mem + (devnum * 4096);
+ vdev->host_status = vdev->rem;
+
+ switch (devnum) {
+ case 0: /* virtio_net */
+ setup_virtio_net(vdev);
+ break;
+ default:
+ dev_err(dev, "Device %d not implemented\n", devnum);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize and attempt to register a virtio_device
+ *
+ * @priv the driver data
+ * @devnum the virtio_device number (index into priv->devices)
+ */
+static int vop_start_device(struct vop_dev *priv, int devnum)
+{
+ struct vop_vdev *vdev = &priv->devices[devnum];
+ struct device *dev = priv->dev;
+ int ret;
+
+ /* Check that we know about the device */
+ if (devnum >= ARRAY_SIZE(priv->devices)) {
+ dev_err(dev, "Unknown virtio_device %d\n", devnum);
+ return -ENODEV;
+ }
+
+ vdev->status = 0;
+
+ /* Do any last minute device-specific setup now that the
+ * host memory is valid */
+ ret = vop_setup_device(priv, devnum);
+ if (ret) {
+ dev_err(dev, "Unable to setup device %d\n", devnum);
+ return ret;
+ }
+
+ /* Register the device with the virtio subsystem */
+ ret = register_virtio_device(&vdev->vdev);
+ if (ret) {
+ dev_err(dev, "Unable to register device %d\n", devnum);
+ return ret;
+ }
+
+ vdev->status = VOP_DEVICE_REGISTERED;
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Work Functions */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Start as much DMA as we can on the given virtqueue
+ *
+ * This is put on the system shared queue, and will start us much DMA as is
+ * available when it is called. This should be triggered when the host adds
+ * things to the avail rings, and when the guest adds things to the internal
+ * avail rings
+ *
+ * Make sure it doesn't sleep for too long, you're on the shared queue
+ */
+static void vop_dma_work(struct work_struct *work)
+{
+ struct vop_vq *vq = container_of(work, struct vop_vq, work);
+ int ret;
+
+ /* Start as many DMA transactions as we can, immediately */
+ while (true) {
+ ret = vq->resolve(vq);
+ if (ret)
+ break;
+ }
+}
+
+/*
+ * Remove all virtio devices immediately
+ *
+ * This will be called by the host to make sure that we are in a stopped
+ * state. It should be callable when everything is already stopped.
+ *
+ * Make sure it doesn't sleep for too long, you're on the shared queue
+ */
+static void vop_reset_work(struct work_struct *work)
+{
+ struct vop_dev *priv = container_of(work, struct vop_dev, reset_work);
+ struct device *dev = priv->dev;
+ struct vop_vdev *vdev;
+ int i;
+
+ dev_dbg(dev, "Resetting all virtio devices\n");
+ mutex_lock(&priv->mutex);
+
+ for (i = 0; i < ARRAY_SIZE(priv->devices); i++) {
+ vdev = &priv->devices[i];
+
+ if (vdev->status & VOP_DEVICE_REGISTERED) {
+ dev_dbg(dev, "Unregistering virtio_device #%d\n", i);
+ unregister_virtio_device(&vdev->vdev);
+ }
+
+ vdev->status &= ~VOP_DEVICE_REGISTERED;
+ }
+
+ if (priv->host_mem) {
+ iounmap(priv->host_mem);
+ priv->host_mem = NULL;
+ }
+
+ mutex_unlock(&priv->mutex);
+}
+
+/*
+ * This will map the host's memory, as well as start the devices that the host
+ * requested
+ *
+ * Mailbox registers contents:
+ * IMR0 - the host memory physical address (must be <1GB)
+ * IMR1 - the devices the host wants started
+ */
+static void vop_start_work(struct work_struct *work)
+{
+ struct vop_dev *priv = container_of(work, struct vop_dev, start_work);
+ struct device *dev = priv->dev;
+ struct vop_vdev *vdev;
+ u32 address, devices;
+ int i;
+
+ dev_dbg(dev, "Starting requested virtio devices\n");
+ mutex_lock(&priv->mutex);
+
+ /* Read the requested address and devices from the mailbox registers */
+ address = ioread32(priv->immr + IMR0_OFFSET);
+ devices = ioread32(priv->immr + IMR1_OFFSET);
+
+ dev_dbg(dev, "address 0x%.8x\n", address);
+ dev_dbg(dev, "devices 0x%.8x\n", devices);
+
+ /* Remap the host's registers */
+ priv->host_mem = ioremap(address + 0x80000000, VOP_HOST_MEM_SIZE);
+ if (!priv->host_mem) {
+ dev_err(dev, "Unable to ioremap host memory\n");
+ goto out_unlock;
+ }
+
+ /* Start the requested devices */
+ for (i = 0; i < ARRAY_SIZE(priv->devices); i++) {
+ vdev = &priv->devices[i];
+
+ if (devices & (1 << i)) {
+ dev_dbg(dev, "Starting virtio_device #%d\n", i);
+ vop_start_device(priv, i);
+ }
+ }
+
+out_unlock:
+ mutex_unlock(&priv->mutex);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Interrupt Handling */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Schedule the work function for a given virtqueue only if the associated
+ * device is up and running. Otherwise, ignore the request
+ *
+ * @priv the private driver data
+ * @dev the virtio_device number in priv->devices[]
+ * @queue the virtqueue in vdev->virtqueues[]
+ */
+static void schedule_work_if_ready(struct vop_dev *priv, int dev, int queue)
+{
+ struct vop_vdev *vdev = &priv->devices[dev];
+ struct vop_vq *vq = &vdev->virtqueues[queue];
+
+ if (vdev->status & VOP_DEVICE_REGISTERED)
+ schedule_work(&vq->work);
+}
+
+static irqreturn_t vdev_interrupt(int irq, void *dev_id)
+{
+ struct vop_dev *priv = dev_id;
+ struct device *dev = priv->dev;
+ u32 imisr, idr;
+
+ imisr = ioread32(priv->immr + IMISR_OFFSET);
+ idr = ioread32(priv->immr + IDR_OFFSET);
+
+ dev_dbg(dev, "INTERRUPT idr 0x%.8x\n", idr);
+
+ /* Check the status register for doorbell interrupts */
+ if (!(imisr & 0x8))
+ return IRQ_NONE;
+
+ /* Clear all doorbell interrupts */
+ iowrite32(idr, priv->immr + IDR_OFFSET);
+
+ /* Reset */
+ if (idr & 0x1)
+ schedule_work(&priv->reset_work);
+
+ /* Start */
+ if (idr & 0x2)
+ schedule_work(&priv->start_work);
+
+ /* vdev 0 vq 1 kick */
+ if (idr & 0x4)
+ schedule_work_if_ready(priv, 0, 1);
+
+ /* vdev 0 vq 0 kick */
+ if (idr & 0x8)
+ schedule_work_if_ready(priv, 0, 0);
+
+ if (idr & 0xfffffff0)
+ dev_dbg(dev, "INTERRUPT unhandled 0x%.8x\n", idr & 0xfffffff0);
+
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Driver insertion time virtio device initialization */
+/*----------------------------------------------------------------------------*/
+
+static void vdev_release(struct device *dev)
+{
+ /* TODO: this should probably do something useful */
+ dev_dbg(dev, "%s: called\n", __func__);
+}
+
+/*
+ * Do any device-specific setup for a virtio device
+ *
+ * This would include things like setting the feature bits for the
+ * device, as well as the device type.
+ *
+ * There is no access to host memory at this point, so don't access it
+ */
+static void vop_setup_virtio_device(struct vop_dev *priv, int devnum)
+{
+ struct vop_vdev *vdev = &priv->devices[devnum];
+ struct virtio_net_config *config;
+ unsigned long features = 0;
+
+ /* HACK: we only support device #0 (virtio_net) right now */
+ if (devnum != 0)
+ return;
+
+ /* Generate a random ethernet address for the host to have
+ *
+ * This way, we could do something board-specific and get an
+ * ethernet address that is consistent per-slot
+ */
+ config = (struct virtio_net_config *)vdev->guest_status->config;
+ random_ether_addr(config->mac);
+ dev_info(priv->dev, "Generated MAC %pM\n", config->mac);
+
+ /* Set the feature bits for the device */
+ set_bit(VIRTIO_NET_F_MAC, &features);
+ set_bit(VIRTIO_NET_F_CSUM, &features);
+ set_bit(VIRTIO_NET_F_GSO, &features);
+ set_bit(VIRTIO_NET_F_MRG_RXBUF, &features);
+
+ vdev->guest_status->features[0] = cpu_to_le32(features);
+ vdev->vdev.id.device = VIRTIO_ID_NET;
+}
+
+/*
+ * Do all of the initialization of all of the virtqueues for a given virtio
+ * device. There is no access to host memory at this point, so don't access it
+ *
+ * @devnum the device number in the priv->devices[] array
+ */
+static void vop_initialize_virtqueues(struct vop_dev *priv, int devnum)
+{
+ struct vop_vdev *vdev = &priv->devices[devnum];
+ struct vop_vq *vq;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vdev->virtqueues); i++) {
+ vq = &vdev->virtqueues[i];
+
+ memset(vq, 0, sizeof(struct vop_vq));
+ vq->immr = priv->immr;
+ vq->dma.chan = priv->chan;
+ INIT_WORK(&vq->work, vop_dma_work);
+ }
+}
+
+/*
+ * Do all of the initialization for the virtio devices that is possible without
+ * access to the host memory
+ *
+ * This includes setting up the pointers that you can and setting the feature
+ * bits so that the host can read them before he starts us
+ */
+static void vop_initialize_devices(struct vop_dev *priv)
+{
+ struct device *parent = priv->dev;
+ struct vop_vdev *vdev;
+ struct device *vdev_dev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->devices); i++) {
+ vdev = &priv->devices[i];
+ vdev_dev = &vdev->vdev.dev;
+
+ /* Set up access to the guest memory, host memory isn't valid
+ * yet, and will have to be set up just before we start */
+ vdev->loc = priv->guest_mem + (i * 4096);
+ vdev->guest_status = vdev->loc;
+
+ /* Initialize all of the device's virtqueues */
+ vop_initialize_virtqueues(priv, i);
+
+ /* Zero the configuration space */
+ memset(vdev->guest_status, 0, 1024);
+
+ /* Copy parent DMA parameters to this device */
+ vdev_dev->dma_mask = parent->dma_mask;
+ vdev_dev->dma_parms = parent->dma_parms;
+ vdev_dev->coherent_dma_mask = parent->coherent_dma_mask;
+
+ vdev_dev->release = &vdev_release;
+ vdev_dev->parent = parent;
+ vdev->vdev.config = &vop_config_ops;
+
+ /* Do any device-specific setup */
+ vop_setup_virtio_device(priv, i);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* OpenFirmware Device Subsystem */
+/*----------------------------------------------------------------------------*/
+
+static int vdev_of_probe(struct of_device *op, const struct of_device_id *match)
+{
+ struct vop_dev *priv;
+ dma_cap_mask_t mask;
+ int ret;
+
+ /* Allocate private data */
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&op->dev, "Unable to allocate device private data\n");
+ ret = -ENOMEM;
+ goto out_return;
+ }
+
+ dev_set_drvdata(&op->dev, priv);
+ priv->dev = &op->dev;
+ mutex_init(&priv->mutex);
+ INIT_WORK(&priv->reset_work, vop_reset_work);
+ INIT_WORK(&priv->start_work, vop_start_work);
+
+ /* Get a DMA channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ dma_cap_set(DMA_INTERRUPT, mask);
+ priv->chan = dma_request_channel(mask, NULL, NULL);
+ if (!priv->chan) {
+ dev_err(&op->dev, "Unable to get DMA channel\n");
+ ret = -ENODEV;
+ goto out_free_priv;
+ }
+
+ /* Remap IMMR */
+ priv->immr = ioremap(get_immrbase(), 0x100000);
+ if (!priv->immr) {
+ dev_err(&op->dev, "Unable to remap IMMR registers\n");
+ ret = -ENOMEM;
+ goto out_dma_release_channel;
+ }
+
+ /* Set up a static 1GB window into host memory */
+ iowrite32be(LAWAR0_ENABLE | 0x1D, priv->immr + LAWAR0_OFFSET);
+ iowrite32be(POCMR0_ENABLE | 0xC0000, priv->immr + POCMR0_OFFSET);
+ iowrite32be(0x0, priv->immr + POTAR0_OFFSET);
+
+ /* Allocate guest memory */
+ priv->guest_mem = dma_alloc_coherent(&op->dev, VOP_GUEST_MEM_SIZE,
+ &priv->guest_mem_addr, GFP_KERNEL);
+ if (!priv->guest_mem) {
+ dev_err(&op->dev, "Unable to allocate guest memory\n");
+ ret = -ENOMEM;
+ goto out_iounmap_immr;
+ }
+
+ memset(priv->guest_mem, 0, VOP_GUEST_MEM_SIZE);
+
+ /* Program BAR1 so that it will hit the guest memory */
+ iowrite32be(priv->guest_mem_addr >> 12, priv->immr + PITAR0_OFFSET);
+
+ /* Initialize all of the virtio devices with their features, etc */
+ vop_initialize_devices(priv);
+
+ /* Disable mailbox interrupts */
+ iowrite32(0x2 | 0x1, priv->immr + IMIMR_OFFSET);
+
+ /* Hook up the irq handler */
+ priv->irq = irq_of_parse_and_map(op->node, 0);
+ ret = request_irq(priv->irq, vdev_interrupt, IRQF_SHARED, driver_name, priv);
+ if (ret)
+ goto out_free_guest_mem;
+
+ dev_info(&op->dev, "Virtio-over-PCI guest driver installed\n");
+ dev_info(&op->dev, "Physical memory @ 0x%.8x\n", priv->guest_mem_addr);
+ dev_info(&op->dev, "Descriptor ring size: %d entries\n", VOP_RING_SIZE);
+ return 0;
+
+out_free_guest_mem:
+ dma_free_coherent(&op->dev, VOP_GUEST_MEM_SIZE, priv->guest_mem,
+ priv->guest_mem_addr);
+out_iounmap_immr:
+ iounmap(priv->immr);
+out_dma_release_channel:
+ dma_release_channel(priv->chan);
+out_free_priv:
+ kfree(priv);
+out_return:
+ return ret;
+}
+
+static int vdev_of_remove(struct of_device *op)
+{
+ struct vop_dev *priv = dev_get_drvdata(&op->dev);
+
+ /* Stop the irq handler */
+ free_irq(priv->irq, priv);
+
+ /* Unregister and reset all of the devices */
+ schedule_work(&priv->reset_work);
+ flush_scheduled_work();
+
+ dma_free_coherent(&op->dev, VOP_GUEST_MEM_SIZE, priv->guest_mem,
+ priv->guest_mem_addr);
+ iounmap(priv->immr);
+ dma_release_channel(priv->chan);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct of_device_id vdev_of_match[] = {
+ { .compatible = "fsl,mpc8349-mu", },
+ {},
+};
+
+static struct of_platform_driver vdev_of_driver = {
+ .owner = THIS_MODULE,
+ .name = driver_name,
+ .match_table = vdev_of_match,
+ .probe = vdev_of_probe,
+ .remove = vdev_of_remove,
+};
+
+/*----------------------------------------------------------------------------*/
+/* Module Init / Exit */
+/*----------------------------------------------------------------------------*/
+
+static int __init vdev_init(void)
+{
+ dma_cache = KMEM_CACHE(vop_dma_cbinfo, 0);
+ if (!dma_cache) {
+ pr_err("%s: unable to create dma cache\n", driver_name);
+ return -ENOMEM;
+ }
+
+ return of_register_platform_driver(&vdev_of_driver);
+}
+
+static void __exit vdev_exit(void)
+{
+ of_unregister_platform_driver(&vdev_of_driver);
+ kmem_cache_destroy(dma_cache);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Freescale Virtio-over-PCI Test Driver");
+MODULE_LICENSE("GPL");
+
+module_init(vdev_init);
+module_exit(vdev_exit);
diff --git a/drivers/virtio/vop_host.c b/drivers/virtio/vop_host.c
new file mode 100644
index 0000000..814fa8a
--- /dev/null
+++ b/drivers/virtio/vop_host.c
@@ -0,0 +1,1071 @@
+/*
+ * Virtio-over-PCI Host Driver for MPC8349EMDS Guest
+ *
+ * Copyright (c) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_net.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+
+#include <linux/etherdevice.h>
+
+#include "vop_hw.h"
+#include "vop.h"
+
+static const char driver_name[] = "vdev";
+
+struct vop_loc_desc {
+ u32 addr;
+ u32 len;
+ u16 flags;
+ u16 next;
+};
+
+struct vop_vq {
+
+ /* The actual virtqueue itself */
+ struct virtqueue vq;
+
+ struct device *dev;
+
+ /* The host ring address */
+ struct vop_host_ring *host;
+
+ /* The guest ring address */
+ struct vop_guest_ring __iomem *guest;
+
+ /* Local copy of the descriptors for fast access */
+ struct vop_loc_desc desc[VOP_RING_SIZE];
+
+ /* The data token from add_buf() */
+ void *data[VOP_RING_SIZE];
+
+ unsigned int num_free;
+ unsigned int free_head;
+ unsigned int num_added;
+
+ u16 avail_idx;
+ u16 last_used_idx;
+
+ /* The doorbell to kick() */
+ unsigned int kick_val;
+ void __iomem *immr;
+};
+
+/* Convert from a struct virtqueue to a struct vop_vq */
+#define to_vop_vq(X) container_of(X, struct vop_vq, vq)
+
+/*
+ * This represents a virtio_device for our driver. It follows the memory
+ * layout shown above. It has pointers to all of the host and guest memory
+ * areas that we need to access
+ */
+struct vop_vdev {
+
+ /* The specific virtio device (console, net, blk) */
+ struct virtio_device vdev;
+
+ /* Local and remote memory */
+ void *loc;
+ void __iomem *rem;
+
+ /*
+ * These are the status, feature, and configuration information
+ * for this virtio device. They are exposed in our memory block
+ * starting at offset 0.
+ */
+ struct vop_status *host_status;
+
+ /*
+ * These are the status, feature, and configuration information
+ * for the guest virtio device. They are exposed in the guest
+ * memory block starting at offset 0.
+ */
+ struct vop_status __iomem *guest_status;
+
+ /*
+ * These are the virtqueues for the virtio driver running this
+ * device to use. The host portions are exposed in our memory block
+ * starting at offset 1024. The exposed areas are aligned to 1024 byte
+ * boundaries, so they appear at offets 1024, 2048, and 3072
+ * respectively.
+ */
+ struct vop_vq virtqueues[3];
+};
+
+#define to_vop_vdev(X) container_of(X, struct vop_vdev, vdev)
+
+/*
+ * This is information from the PCI subsystem about each MPC8349EMDS board
+ *
+ * It holds information for all of the possible virtio_devices that are
+ * attached to this board.
+ */
+struct vop_dev {
+
+ struct pci_dev *pdev;
+ struct device *dev;
+
+ /* PowerPC memory (PCI BAR0 and BAR1, respectively) */
+ #define VOP_GUEST_MEM_SIZE 16384
+ void __iomem *immr;
+ void __iomem *netregs;
+
+ /* Host memory, visible to the PowerPC */
+ #define VOP_HOST_MEM_SIZE 16384
+ void *host_mem;
+ dma_addr_t host_mem_addr;
+
+ /* The virtio devices */
+ struct vop_vdev devices[4];
+};
+
+/*----------------------------------------------------------------------------*/
+/* Ring Debugging Helpers */
+/*----------------------------------------------------------------------------*/
+
+#ifdef DEBUG_DUMP_RINGS
+static void dump_guest_descriptors(struct vop_vq *vq)
+{
+ int i;
+ struct vop_desc __iomem *desc;
+
+ pr_debug("DESC BG: 0xADDRESSX LENGTH 0xFLAG 0xNEXT\n");
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ desc = &vq->guest->desc[i];
+ pr_debug("DESC %.2d: 0x%.8x %.6d 0x%.4x 0x%.4x\n", i,
+ ioread32(&desc->addr), ioread32(&desc->len),
+ ioread16(&desc->flags), ioread16(&desc->next));
+ }
+ pr_debug("DESC ED\n");
+}
+
+static void dump_guest_avail(struct vop_vq *vq)
+{
+ int i;
+
+ pr_debug("BEGIN AVAIL DUMP\n");
+ for (i = 0; i < VOP_RING_SIZE; i++)
+ pr_debug("AVAIL %.2d: 0x%.4x\n", i, ioread16(&vq->guest->avail[i]));
+ pr_debug("END AVAIL DUMP\n");
+}
+
+static void dump_guest_ring(struct vop_vq *vq)
+{
+ pr_debug("BEGIN GUEST RING DUMP\n");
+ dump_guest_descriptors(vq);
+ pr_debug("GUEST FLAGS: 0x%.4x\n", ioread16(&vq->guest->flags));
+ pr_debug("GUEST AVAIL_IDX: %d\n", ioread16(&vq->guest->avail_idx));
+ dump_guest_avail(vq);
+ pr_debug("END GUEST RING DUMP\n");
+}
+
+static void dump_host_used(struct vop_vq *vq)
+{
+ int i;
+ struct vop_used_elem *used;
+
+ pr_debug("USED BG: 0xIDID LENGTH\n");
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ used = &vq->host->used[i];
+ pr_debug("USED %.2d: 0x%.4x %.6d\n", i, used->id, used->len);
+ }
+ pr_debug("USED ED\n");
+}
+
+static void dump_host_ring(struct vop_vq *vq)
+{
+ pr_debug("BEGIN HOST RING DUMP\n");
+ pr_debug("HOST FLAGS: 0x%.4x\n", vq->host->flags);
+ pr_debug("HOST USED_IDX: 0x%.2d\n", vq->host->used_idx);
+ dump_host_used(vq);
+ pr_debug("END HOST RING DUMP\n");
+}
+
+static void debug_dump_rings(struct vop_vq *vq, const char *msg)
+{
+ dev_dbg(vq->dev, "%s\n", msg);
+ dump_guest_ring(vq);
+ dump_host_ring(vq);
+ pr_debug("\n");
+}
+#else
+static void debug_dump_rings(struct vop_vq *vq, const char *msg)
+{
+ /* Nothing */
+}
+#endif /* DEBUG_DUMP_RINGS */
+
+/*----------------------------------------------------------------------------*/
+/* Ring Access Helpers */
+/*----------------------------------------------------------------------------*/
+
+static void vop_set_desc_addr(struct vop_vq *vq, unsigned int idx, u32 addr)
+{
+ vq->desc[idx].addr = addr;
+ iowrite32(addr, &vq->guest->desc[idx].addr);
+}
+
+static void vop_set_desc_len(struct vop_vq *vq, unsigned int idx, u32 len)
+{
+ vq->desc[idx].len = len;
+ iowrite32(len, &vq->guest->desc[idx].len);
+}
+
+static void vop_set_desc_flags(struct vop_vq *vq, unsigned int idx, u16 flags)
+{
+ vq->desc[idx].flags = flags;
+ iowrite16(flags, &vq->guest->desc[idx].flags);
+}
+
+static void vop_set_desc_next(struct vop_vq *vq, unsigned int idx, u16 next)
+{
+ vq->desc[idx].next = next;
+ iowrite16(next, &vq->guest->desc[idx].next);
+}
+
+static u32 vop_get_desc_addr(struct vop_vq *vq, unsigned int idx)
+{
+ return vq->desc[idx].addr;
+}
+
+static u32 vop_get_desc_len(struct vop_vq *vq, unsigned int idx)
+{
+ return vq->desc[idx].len;
+}
+
+static u16 vop_get_desc_flags(struct vop_vq *vq, unsigned int idx)
+{
+ return vq->desc[idx].flags;
+}
+
+static u16 vop_get_desc_next(struct vop_vq *vq, unsigned int idx)
+{
+ return vq->desc[idx].next;
+}
+
+/*
+ * Add an entry to the available ring at avail_idx pointing to the descriptor
+ * chain at index head
+ *
+ * @vq the virtqueue
+ * @idx the index in the avail ring
+ * @val the value to write
+ */
+static void vop_set_avail_entry(struct vop_vq *vq, u16 idx, u16 val)
+{
+ iowrite16(val, &vq->guest->avail[idx]);
+}
+
+/*
+ * Set the available index so the guest knows about buffers that were added
+ * with vop_set_avail_entry()
+ *
+ * @vq the virtqueue
+ * @idx the new avail_idx that the guest sees
+ */
+static void vop_set_avail_idx(struct vop_vq *vq, u16 idx)
+{
+ iowrite16(idx, &vq->guest->avail_idx);
+}
+
+/*
+ * Set the host's flags (in the guest memory)
+ *
+ * @vq the virtqueue
+ * @flags the new flags that the guest will see
+ */
+static void vop_set_host_flags(struct vop_vq *vq, u16 flags)
+{
+ iowrite16(flags, &vq->guest->flags);
+}
+
+/*
+ * Read the guests flags (in local memory)
+ *
+ * @vq the virtqueue
+ * @return the guest's flags
+ */
+static u16 vop_get_guest_flags(struct vop_vq *vq)
+{
+ return le16_to_cpu(vq->host->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Remote status helpers */
+/*----------------------------------------------------------------------------*/
+
+static u32 vop_get_guest_status(struct vop_vdev *vdev)
+{
+ return ioread32(&vdev->guest_status->status);
+}
+
+static u32 vop_get_guest_features(struct vop_vdev *vdev)
+{
+ return ioread32(&vdev->guest_status->features[0]);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Scatterlist DMA helpers */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * This function abuses some of the scatterlist code and implements
+ * dma_map_sg() in such a way that we don't need to keep the scatterlist
+ * around in order to unmap it.
+ *
+ * It is also designed to never merge scatterlist entries, which is
+ * never what we want for virtio.
+ *
+ * When it is time to unmap the buffer, you can use dma_unmap_single() to
+ * unmap each entry in the chain. Get the address, length, and direction
+ * from the descriptors! (keep a local copy for speed)
+ */
+static int vop_dma_map_sg(struct device *dev, struct scatterlist sg[],
+ unsigned int out, unsigned int in)
+{
+ dma_addr_t addr;
+ enum dma_data_direction dir;
+ struct scatterlist *start;
+ unsigned int i, failure;
+
+ start = sg;
+
+ for (i = 0; i < out + in; i++) {
+
+ /* Check for scatterlist chaining abuse */
+ BUG_ON(sg == NULL);
+
+ dir = (i < out) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ addr = dma_map_single(dev, sg_virt(sg), sg->length, dir);
+
+ if (dma_mapping_error(dev, addr))
+ goto unwind;
+
+ sg_dma_address(sg) = addr;
+ sg = sg_next(sg);
+ }
+
+ return 0;
+
+unwind:
+ failure = i;
+ sg = start;
+
+ for (i = 0; i < failure; i++) {
+ dir = (i < out) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ addr = sg_dma_address(sg);
+
+ dma_unmap_single(dev, addr, sg->length, dir);
+ sg = sg_next(sg);
+ }
+
+ return -ENOMEM;
+}
+
+/*----------------------------------------------------------------------------*/
+/* struct virtqueue_ops infrastructure */
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Modify the struct virtio_net_hdr_mrg_rxbuf's num_buffers field to account
+ * for the split that will happen in the DMA xmit routine
+ *
+ * This assumes that both sides have the same PAGE_SIZE
+ */
+static void vop_fixup_vnet_mrg_hdr(struct scatterlist sg[], unsigned int out)
+{
+ struct virtio_net_hdr *hdr;
+ struct virtio_net_hdr_mrg_rxbuf *mhdr;
+ unsigned int bytes = 0;
+
+ /* There must be a header + data, at the least */
+ BUG_ON(out < 2);
+
+ /* The first entry must be the structure */
+ BUG_ON(sg->length != sizeof(struct virtio_net_hdr_mrg_rxbuf));
+
+ hdr = sg_virt(sg);
+ mhdr = sg_virt(sg);
+
+ /* We merge buffers together, so just count up the number of bytes
+ * needed, then figure out how many pages that will be */
+ for (/* none */; out; out--, sg = sg_next(sg))
+ bytes += sg->length;
+
+ /* Of course, nobody ever imagined that we might actually use
+ * this on machines with different endianness...
+ *
+ * We force big-endian for now, since that's what our guest is */
+ mhdr->num_buffers = cpu_to_be16(DIV_ROUND_UP(bytes, PAGE_SIZE));
+
+ /* Might as well fix up the other fields while we're at it */
+ hdr->hdr_len = cpu_to_be16(hdr->hdr_len);
+ hdr->gso_size = cpu_to_be16(hdr->gso_size);
+ hdr->csum_start = cpu_to_be16(hdr->csum_start);
+ hdr->csum_offset = cpu_to_be16(hdr->csum_offset);
+}
+
+static int vop_add_buf(struct virtqueue *_vq, struct scatterlist sg[],
+ unsigned int out, unsigned int in, void *data)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+ unsigned int i, avail, head, uninitialized_var(prev);
+
+ BUG_ON(data == NULL);
+ BUG_ON(out + in == 0);
+
+ /* Make sure we have space for this to succeed */
+ if (vq->num_free < out + in) {
+ dev_dbg(vq->dev, "No free space left: len=%d free=%d\n",
+ out + in, vq->num_free);
+ return -ENOSPC;
+ }
+
+ /* If this is an xmit buffer from virtio_net, fixup the header */
+ if (out > 1) {
+ dev_dbg(vq->dev, "Fixing up virtio_net header\n");
+ vop_fixup_vnet_mrg_hdr(sg, out);
+ }
+
+ head = vq->free_head;
+
+ /* DMA map the scatterlist */
+ if (vop_dma_map_sg(vq->dev, sg, out, in)) {
+ dev_err(vq->dev, "Failed to DMA map scatterlist\n");
+ return -ENOMEM;
+ }
+
+ /* We're about to use some buffers from the free list */
+ vq->num_free -= out + in;
+
+ for (i = vq->free_head; out; i = vop_get_desc_next(vq, i), out--) {
+ vop_set_desc_flags(vq, i, VOP_DESC_F_NEXT);
+ vop_set_desc_addr(vq, i, sg_dma_address(sg));
+ vop_set_desc_len(vq, i, sg->length);
+
+ prev = i;
+ sg = sg_next(sg);
+ }
+
+ for (/* none */; in; i = vop_get_desc_next(vq, i), in--) {
+ vop_set_desc_flags(vq, i, VOP_DESC_F_NEXT | VOP_DESC_F_WRITE);
+ vop_set_desc_addr(vq, i, sg_dma_address(sg));
+ vop_set_desc_len(vq, i, sg->length);
+
+ prev = i;
+ sg = sg_next(sg);
+ }
+
+ /* Last one doesn't continue */
+ vop_set_desc_flags(vq, prev, vop_get_desc_flags(vq, prev) & ~VOP_DESC_F_NEXT);
+
+ /* Update the free pointer */
+ vq->free_head = i;
+
+ /* Set token */
+ vq->data[head] = data;
+
+ /* Add an entry for the head of the chain into the avail array, but
+ * don't update avail->idx until kick() */
+ avail = (vq->avail_idx + vq->num_added++) & (VOP_RING_SIZE - 1);
+ vop_set_avail_entry(vq, avail, head);
+
+ dev_dbg(vq->dev, "Added buffer head %i to %p (num_free %d)\n", head, vq, vq->num_free);
+ debug_dump_rings(vq, "Added buffer(s), dumping rings");
+
+ return 0;
+}
+
+static inline bool more_used(const struct vop_vq *vq)
+{
+ return vq->last_used_idx != le16_to_cpu(vq->host->used_idx);
+}
+
+static void detach_buf(struct vop_vq *vq, unsigned int head)
+{
+ unsigned int i, len;
+ dma_addr_t addr;
+ enum dma_data_direction dir;
+
+ /* Clear data pointer */
+ vq->data[head] = NULL;
+
+ /* Put the chain back on the free list, unmapping as we go */
+ i = head;
+ while (true) {
+ addr = vop_get_desc_addr(vq, i);
+ len = vop_get_desc_len(vq, i);
+ dir = (vop_get_desc_flags(vq, i) & VOP_DESC_F_WRITE) ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ /* Unmap the entry */
+ dma_unmap_single(vq->dev, addr, len, dir);
+ vq->num_free++;
+
+ /* Check for end-of-chain */
+ if (!(vop_get_desc_flags(vq, i) & VOP_DESC_F_NEXT))
+ break;
+
+ i = vop_get_desc_next(vq, i);
+ }
+
+ vop_set_desc_next(vq, i, vq->free_head);
+ vq->free_head = head;
+}
+
+static void *vop_get_buf(struct virtqueue *_vq, unsigned int *len)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+ unsigned int head, used_idx;
+ void *ret;
+
+ if (!more_used(vq)) {
+ dev_dbg(vq->dev, "No more buffers in queue\n");
+ return NULL;
+ }
+
+ used_idx = vq->last_used_idx & (VOP_RING_SIZE - 1);
+ head = le32_to_cpu(vq->host->used[used_idx].id);
+ *len = le32_to_cpu(vq->host->used[used_idx].len);
+
+ dev_dbg(vq->dev, "REMOVE buffer head %i from %p (len %d)\n", head, vq, *len);
+ debug_dump_rings(vq, "Removing buffer, dumping rings");
+
+ BUG_ON(head >= VOP_RING_SIZE);
+ BUG_ON(!vq->data[head]);
+
+ /* detach_buf() clears data, save it now */
+ ret = vq->data[head];
+ detach_buf(vq, head);
+
+ /* Update the last used_idx we've consumed */
+ vq->last_used_idx++;
+ return ret;
+}
+
+static void vop_kick(struct virtqueue *_vq)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+
+ dev_dbg(vq->dev, "making %d new buffers available to guest\n", vq->num_added);
+ vq->avail_idx += vq->num_added;
+ vq->num_added = 0;
+ vop_set_avail_idx(vq, vq->avail_idx);
+
+ if (!(vop_get_guest_flags(vq) & VOP_F_NO_INTERRUPT)) {
+ dev_dbg(vq->dev, "kicking the guest (new buffers in avail)\n");
+ iowrite32(vq->kick_val, vq->immr + IDR_OFFSET);
+ debug_dump_rings(vq, "ran a kick, dumping rings");
+ }
+}
+
+/* Write to the guest's flags register to disable interrupts */
+static void vop_disable_cb(struct virtqueue *_vq)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+
+ vop_set_host_flags(vq, VOP_F_NO_INTERRUPT);
+}
+
+static bool vop_enable_cb(struct virtqueue *_vq)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+
+ /* We optimistically enable interrupts, then check if
+ * there was more to do */
+ vop_set_host_flags(vq, 0);
+
+ if (unlikely(more_used(vq)))
+ return false;
+
+ return true;
+}
+
+static struct virtqueue_ops vop_vq_ops = {
+ .add_buf = vop_add_buf,
+ .get_buf = vop_get_buf,
+ .kick = vop_kick,
+ .disable_cb = vop_disable_cb,
+ .enable_cb = vop_enable_cb,
+};
+
+/*----------------------------------------------------------------------------*/
+/* struct virtio_device infrastructure */
+/*----------------------------------------------------------------------------*/
+
+/* Get something that the other side wants you to have, from configuration
+ * space. This is used to transfer the MAC address from the guest to the host,
+ * for example. It should be reading something from the guest, in this case */
+static void vopc_get(struct virtio_device *_vdev, unsigned offset, void *buf,
+ unsigned len)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ void __iomem *config = vdev->guest_status->config;
+
+ memcpy_fromio(buf, config + offset, len);
+}
+
+/* Set something in the configuration space (currently unused) */
+static void vopc_set(struct virtio_device *_vdev, unsigned offset,
+ const void *buf, unsigned len)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ void __iomem *config = vdev->guest_status->config;
+
+ memcpy_toio(config + offset, buf, len);
+}
+
+/* Get your own status */
+static u8 vopc_get_status(struct virtio_device *_vdev)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ u32 status;
+
+ status = le32_to_cpu(vdev->host_status->status);
+ dev_dbg(&vdev->vdev.dev, "%s(): -> 0x%.2x\n", __func__, (u8)status);
+
+ return (u8)status;
+}
+
+/* Set your own status */
+static void vopc_set_status(struct virtio_device *_vdev, u8 status)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ u32 old_status;
+
+ old_status = le32_to_cpu(vdev->host_status->status);
+ vdev->host_status->status = cpu_to_le32(status);
+
+ dev_dbg(&vdev->vdev.dev, "%s(): <- 0x%.2x (was 0x%.2x)\n",
+ __func__, status, old_status);
+
+ /*
+ * FIXME: we really need to notify the other side when status changes
+ * FIXME: happen, so that they can take some action
+ */
+}
+
+/* Reset your own status */
+static void vopc_reset(struct virtio_device *_vdev)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+
+ dev_dbg(&vdev->vdev.dev, "%s(): status reset\n", __func__);
+ vdev->host_status->status = cpu_to_le32(0);
+}
+
+static struct virtqueue *vopc_find_vq(struct virtio_device *_vdev,
+ unsigned index,
+ void (*cb)(struct virtqueue *vq))
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ struct vop_vq *vq = &vdev->virtqueues[index];
+ int i;
+
+ /* Check that we support the virtqueue at this index */
+ if (index >= ARRAY_SIZE(vdev->virtqueues)) {
+ dev_err(&vdev->vdev.dev, "no virtqueue for index %d\n", index);
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* HACK: we only support virtio_net for now */
+ if (vdev->vdev.id.device != VIRTIO_ID_NET) {
+ dev_err(&vdev->vdev.dev, "only virtio_net is supported\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* Initialize the virtqueue to a clean state */
+ vq->num_free = VOP_RING_SIZE;
+ vq->dev = &vdev->vdev.dev;
+
+ switch (index) {
+ case 0: /* x86 recv virtqueue -- ppc xmit virtqueue */
+ vq->guest = vdev->rem + 1024;
+ vq->host = vdev->loc + 1024;
+ break;
+ case 1: /* x86 xmit virtqueue -- ppc recv virtqueue */
+ vq->guest = vdev->rem + 2048;
+ vq->host = vdev->loc + 2048;
+ break;
+ default:
+ dev_err(vq->dev, "unknown virtqueue %d\n", index);
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* Initialize the descriptor, avail, and used rings */
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ vop_set_desc_addr(vq, i, 0x0);
+ vop_set_desc_len(vq, i, 0);
+ vop_set_desc_flags(vq, i, 0);
+ vop_set_desc_next(vq, i, (i + 1) & (VOP_RING_SIZE - 1));
+
+ vop_set_avail_entry(vq, i, 0);
+ vq->host->used[i].id = cpu_to_le32(0);
+ vq->host->used[i].len = cpu_to_le32(0);
+ }
+
+ vq->avail_idx = 0;
+ vop_set_avail_idx(vq, 0);
+ vop_set_host_flags(vq, 0);
+
+ debug_dump_rings(vq, "found a virtqueue, dumping rings");
+
+ vq->vq.callback = cb;
+ vq->vq.vdev = &vdev->vdev;
+ vq->vq.vq_ops = &vop_vq_ops;
+
+ return &vq->vq;
+}
+
+static void vopc_del_vq(struct virtqueue *_vq)
+{
+ struct vop_vq *vq = to_vop_vq(_vq);
+ int i;
+
+ /* FIXME: make sure that DMA has stopped by this point */
+
+ /* Unmap and remove all outstanding descriptors from the ring */
+ for (i = 0; i < VOP_RING_SIZE; i++) {
+ if (vq->data[i]) {
+ dev_dbg(vq->dev, "cleanup detach buffer at index %d\n", i);
+ detach_buf(vq, i);
+ }
+ }
+
+ debug_dump_rings(vq, "virtqueue destroyed, dumping rings");
+}
+
+static u32 vopc_get_features(struct virtio_device *_vdev)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+ u32 ret;
+
+ ret = vop_get_guest_features(vdev);
+ dev_info(&vdev->vdev.dev, "%s(): guest features 0x%.8x\n", __func__, ret);
+
+ return ret;
+}
+
+static void vopc_finalize_features(struct virtio_device *_vdev)
+{
+ struct vop_vdev *vdev = to_vop_vdev(_vdev);
+
+ /*
+ * TODO: notify the other side at this point
+ */
+
+ vdev->host_status->features[0] = cpu_to_le32(vdev->vdev.features[0]);
+ dev_info(&vdev->vdev.dev, "%s(): final features 0x%.8lx\n", __func__, vdev->vdev.features[0]);
+}
+
+static struct virtio_config_ops vop_config_ops = {
+ .get = vopc_get,
+ .set = vopc_set,
+ .get_status = vopc_get_status,
+ .set_status = vopc_set_status,
+ .reset = vopc_reset,
+ .find_vq = vopc_find_vq,
+ .del_vq = vopc_del_vq,
+ .get_features = vopc_get_features,
+ .finalize_features = vopc_finalize_features,
+};
+
+/*----------------------------------------------------------------------------*/
+/* Setup code for virtio devices */
+/*----------------------------------------------------------------------------*/
+
+static void vop_release(struct device *dev)
+{
+ dev_dbg(dev, "calling device release\n");
+}
+
+static int setup_virtio_device(struct vop_dev *priv, int devnum)
+{
+ struct vop_vdev *vdev = &priv->devices[devnum];
+ struct device *dev = priv->dev;
+ int i;
+
+ /* Set up the pointers to the guest and host memory areas */
+ vdev->loc = priv->host_mem + (devnum * 4096);
+ vdev->rem = priv->netregs + (devnum * 4096);
+ dev_dbg(dev, "memory guest 0x%p host 0x%p\n", vdev->rem, vdev->loc);
+
+ /* Set up the pointers to the guest and host status areas */
+ vdev->guest_status = vdev->rem;
+ vdev->host_status = vdev->loc;
+ dev_dbg(dev, "status guest 0x%p host 0x%p\n", vdev->rem, vdev->loc);
+
+ /* The find_vq() must set up the correct mappings to virtqueues itself,
+ * so we cannot do it here */
+ for (i = 0; i < ARRAY_SIZE(vdev->virtqueues); i++) {
+ memset(&vdev->virtqueues[i], 0, sizeof(struct vop_vq));
+ vdev->virtqueues[i].immr = priv->immr;
+ vdev->virtqueues[i].kick_val = 1 << ((devnum * 4) + i + 2);
+ dev_dbg(dev, "vq %d cleared, kick %d\n", i, (devnum * 4) + i + 2);
+ }
+
+ /* Zero out the configuration space completely */
+ memset(vdev->host_status, 0, 1024);
+
+ /* Copy the parent DMA parameters to this virtio_device */
+ vdev->vdev.dev.dma_mask = dev->dma_mask;
+ vdev->vdev.dev.dma_parms = dev->dma_parms;
+ vdev->vdev.dev.coherent_dma_mask = dev->coherent_dma_mask;
+
+ /* Setup everything except the device type */
+ vdev->vdev.dev.release = &vop_release;
+ vdev->vdev.dev.parent = dev;
+ vdev->vdev.config = &vop_config_ops;
+
+ return 0;
+}
+
+static int register_virtio_net(struct vop_dev *priv)
+{
+ struct vop_vdev *vdev = &priv->devices[0];
+ struct virtio_net_config *config;
+ unsigned long features = 0;
+ int ret;
+
+ /* Run the common setup routine */
+ ret = setup_virtio_device(priv, 0);
+ if (ret) {
+ dev_err(priv->dev, "unable to setup virtio_net\n");
+ return ret;
+ }
+
+ /* Generate a random ethernet address for the other side
+ *
+ * This is necessary so we can allow it to give us a consistent
+ * MAC address for itself, using something board-specific
+ *
+ * The feature bits must match for it to work correctly
+ */
+ config = (struct virtio_net_config *)vdev->host_status->config;
+ random_ether_addr(config->mac);
+ dev_info(priv->dev, "Generated MAC %pM\n", config->mac);
+
+ /* Set the feature bits for the device */
+ set_bit(VIRTIO_NET_F_MAC, &features);
+ set_bit(VIRTIO_NET_F_CSUM, &features);
+ set_bit(VIRTIO_NET_F_GSO, &features);
+ set_bit(VIRTIO_NET_F_MRG_RXBUF, &features);
+
+ vdev->host_status->features[0] = cpu_to_le32(features);
+ vdev->vdev.id.device = VIRTIO_ID_NET;
+
+ /* Register the virtio device */
+ return register_virtio_device(&vdev->vdev);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Interrupt Handling */
+/*----------------------------------------------------------------------------*/
+
+static irqreturn_t vdev_interrupt(int irq, void *dev_id)
+{
+ struct vop_dev *priv = dev_id;
+ struct virtqueue *vq;
+ u32 omisr, odr;
+
+ omisr = ioread32(priv->immr + OMISR_OFFSET);
+ odr = ioread32(priv->immr + ODR_OFFSET);
+
+ /* Check the status register for doorbell interrupts */
+ if (!(omisr & 0x8))
+ return IRQ_NONE;
+
+ /* Clear all doorbell interrupts */
+ iowrite32(odr, priv->immr + ODR_OFFSET);
+
+ if (odr & 0x4) {
+ vq = &priv->devices[0].virtqueues[0].vq;
+ vq->callback(vq);
+ }
+
+ if (odr & 0x8) {
+ vq = &priv->devices[0].virtqueues[1].vq;
+ vq->callback(vq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------------*/
+/* PCI Subsystem */
+/*----------------------------------------------------------------------------*/
+
+static int vop_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct vop_dev *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto out_return;
+ }
+
+ pci_set_drvdata(dev, priv);
+ priv->dev = &dev->dev;
+
+ /* Hardware Initialization */
+ ret = pci_enable_device(dev);
+ if (ret)
+ goto out_kfree_priv;
+
+ pci_set_master(dev);
+ ret = pci_request_regions(dev, driver_name);
+ if (ret)
+ goto out_pci_disable_device;
+
+ priv->immr = pci_ioremap_bar(dev, 0);
+ if (!priv->immr) {
+ ret = -ENOMEM;
+ goto out_pci_release_regions;
+ }
+
+ priv->netregs = pci_ioremap_bar(dev, 1);
+ if (!priv->netregs) {
+ ret = -ENOMEM;
+ goto out_iounmap_immr;
+ }
+
+ /* The device can only see the lowest 1GB of memory over the bus */
+ dev->dev.coherent_dma_mask = DMA_BIT_MASK(30);
+ ret = dma_set_mask(&dev->dev, DMA_BIT_MASK(30));
+ if (ret) {
+ dev_err(&dev->dev, "Unable to set DMA mask\n");
+ goto out_iounmap_netregs;
+ }
+
+ /* Allocate the host memory, for writing by the guest */
+ priv->host_mem = dma_alloc_coherent(&dev->dev, VOP_HOST_MEM_SIZE,
+ &priv->host_mem_addr, GFP_KERNEL);
+ if (!priv->host_mem) {
+ dev_err(&dev->dev, "Unable to allocate host memory\n");
+ ret = -ENOMEM;
+ goto out_iounmap_netregs;
+ }
+
+ /* We use the guest's mailbox 0 to hold the host memory address */
+ iowrite32(priv->host_mem_addr, priv->immr + IMR0_OFFSET);
+
+ /* Reset all of the devices */
+ iowrite32(0x1, priv->immr + IDR_OFFSET);
+
+ /* Mask all of the MBOX interrupts */
+ iowrite32(0x1 | 0x2, priv->immr + OMIMR_OFFSET);
+
+ /* Setup the virtio_net instance */
+ ret = register_virtio_net(priv);
+ if (ret) {
+ dev_err(&dev->dev, "Unable to register virtio_net\n");
+ goto out_free_host_mem;
+ }
+
+ /* Hook up the interrupt handler */
+ ret = request_irq(dev->irq, vdev_interrupt, IRQF_SHARED, driver_name, priv);
+ if (ret) {
+ dev_err(&dev->dev, "Unable to register interrupt handler\n");
+ goto out_unregister_virtio_net;
+ }
+
+ /* Start virtio_net */
+ iowrite32(0x1, priv->immr + IMR1_OFFSET);
+ iowrite32(0x2, priv->immr + IDR_OFFSET);
+
+ return 0;
+
+out_unregister_virtio_net:
+ unregister_virtio_device(&priv->devices[0].vdev);
+out_free_host_mem:
+ dma_free_coherent(&dev->dev, VOP_HOST_MEM_SIZE, priv->host_mem,
+ priv->host_mem_addr);
+out_iounmap_netregs:
+ iounmap(priv->netregs);
+out_iounmap_immr:
+ iounmap(priv->immr);
+out_pci_release_regions:
+ pci_release_regions(dev);
+out_pci_disable_device:
+ pci_disable_device(dev);
+out_kfree_priv:
+ kfree(priv);
+out_return:
+ return ret;
+}
+
+static void vop_remove(struct pci_dev *dev)
+{
+ struct vop_dev *priv = pci_get_drvdata(dev);
+
+ free_irq(dev->irq, priv);
+
+ /* Reset everything */
+ iowrite32(0x1, priv->immr + IDR_OFFSET);
+
+ /* Unregister virtio_net */
+ unregister_virtio_device(&priv->devices[0].vdev);
+
+ /* Clear the host memory address from the guest's mailbox 0 */
+ iowrite32(0x0, priv->immr + IMR0_OFFSET);
+ iowrite32(0x0, priv->immr + IMR1_OFFSET);
+
+ dma_free_coherent(&dev->dev, VOP_HOST_MEM_SIZE, priv->host_mem,
+ priv->host_mem_addr);
+ iounmap(priv->netregs);
+ iounmap(priv->immr);
+ pci_release_regions(dev);
+ pci_disable_device(dev);
+ kfree(priv);
+}
+
+#define PCI_DEVID_FSL_MPC8349EMDS 0x0080
+
+/* The list of devices that this module will support */
+static struct pci_device_id vop_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVID_FSL_MPC8349EMDS), },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, vop_ids);
+
+static struct pci_driver vop_pci_driver = {
+ .name = (char *)driver_name,
+ .id_table = vop_ids,
+ .probe = vop_probe,
+ .remove = vop_remove,
+};
+
+/*----------------------------------------------------------------------------*/
+/* Module Init / Exit */
+/*----------------------------------------------------------------------------*/
+
+static int __init vop_init(void)
+{
+ return pci_register_driver(&vop_pci_driver);
+}
+
+static void __exit vop_exit(void)
+{
+ pci_unregister_driver(&vop_pci_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Virtio-PCI-Host Test Driver");
+MODULE_LICENSE("GPL");
+
+module_init(vop_init);
+module_exit(vop_exit);
diff --git a/drivers/virtio/vop_hw.h b/drivers/virtio/vop_hw.h
new file mode 100644
index 0000000..8a19d3f
--- /dev/null
+++ b/drivers/virtio/vop_hw.h
@@ -0,0 +1,80 @@
+/*
+ * Register offsets for the MPC8349EMDS Message Unit from the IMMR base address
+ *
+ * Copyright (c) 2008 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef PCINET_HW_H
+#define PCINET_HW_H
+
+#define SGPRL_OFFSET 0x0100
+#define SGPRH_OFFSET 0x0104
+
+/* mpc8349emds message unit register offsets */
+#define OMISR_OFFSET 0x8030
+#define OMIMR_OFFSET 0x8034
+#define IMR0_OFFSET 0x8050
+#define IMR1_OFFSET 0x8054
+#define OMR0_OFFSET 0x8058
+#define OMR1_OFFSET 0x805C
+#define ODR_OFFSET 0x8060
+#define IDR_OFFSET 0x8068
+#define IMISR_OFFSET 0x8080
+#define IMIMR_OFFSET 0x8084
+
+
+/* mpc8349emds pci and local access window register offsets */
+#define LAWAR0_OFFSET 0x0064
+#define LAWAR0_ENABLE (1<<31)
+
+#define POCMR0_OFFSET 0x8410
+#define POCMR0_ENABLE (1<<31)
+
+#define POTAR0_OFFSET 0x8400
+
+#define LAWAR1_OFFSET 0x006c
+#define LAWAR1_ENABLE (1<<31)
+
+#define POCMR1_OFFSET 0x8428
+#define POCMR1_ENABLE (1<<31)
+
+#define POTAR1_OFFSET 0x8418
+
+
+/* mpc8349emds dma controller register offsets */
+#define DMAMR0_OFFSET 0x8100
+#define DMASR0_OFFSET 0x8104
+#define DMASAR0_OFFSET 0x8110
+#define DMADAR0_OFFSET 0x8118
+#define DMABCR0_OFFSET 0x8120
+
+#define DMA_CHANNEL_BUSY (1<<2)
+
+#define DMA_DIRECT_MODE_SNOOP (1<<20)
+#define DMA_CHANNEL_MODE_DIRECT (1<<2)
+#define DMA_CHANNEL_START (1<<0)
+
+
+/* mpc8349emds pci and local access window register offsets */
+#define LAWAR0_OFFSET 0x0064
+#define LAWAR0_ENABLE (1<<31)
+
+#define POCMR0_OFFSET 0x8410
+#define POCMR0_ENABLE (1<<31)
+
+#define POTAR0_OFFSET 0x8400
+
+
+/* mpc8349emds pci and inbound window register offsets */
+#define PITAR0_OFFSET 0x8568
+#define PIWAR0_OFFSET 0x8578
+
+#define PIWAR0_ENABLED (1<<31)
+#define PIWAR0_PREFETCH (1<<29)
+#define PIWAR0_IWS_4K 0xb
+
+#endif /* PCINET_HW_H */
--
1.5.4.3
^ permalink raw reply related
* soc vs localbus for flash in device tree
From: Dushara Jayasinghe @ 2009-02-23 23:31 UTC (permalink / raw)
To: linuxppc-dev@ozlabs.org
Hi,
I'm working on the kernel version Linux-2.6.29-rc5 with U-Boot 1.2.0-g88e21=
e7b-dirty.
Does the flash node HAVE to be within a localbus node or can it reside with=
in the soc node? I've been basing my work on mpc834x_mds.dts
And hence haven't defined a localbus node.
BTW is there a way to search the ML archive? Currently I browse each month =
to search for prior postings.
Thanks
D
^ permalink raw reply
* [PATCH v2] powerpc/fsl-booke: Add support for tlbilx instructions
From: Kumar Gala @ 2009-02-23 21:53 UTC (permalink / raw)
To: linuxppc-dev
The e500mc core supports the new tlbilx instructions that do core
local invalidates and also provide us the ability to take down
all TLB entries matching a given PID.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
* Fixed BOOKE_PAGESZ_4K build error, we renamed to BOOK3E_PAGESZ_4K
arch/powerpc/include/asm/mmu.h | 4 +-
arch/powerpc/kernel/cputable.c | 3 +-
arch/powerpc/mm/tlb_nohash_low.S | 44 +++++++++++++++++++++++++++++++------
3 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h
index 5c78079..dc82dcd 100644
--- a/arch/powerpc/include/asm/mmu.h
+++ b/arch/powerpc/include/asm/mmu.h
@@ -36,9 +36,9 @@
*/
#define MMU_FTR_USE_TLBIVAX_BCAST ASM_CONST(0x00040000)
-/* Enable use of tlbilx invalidate-by-PID variant.
+/* Enable use of tlbilx invalidate instructions.
*/
-#define MMU_FTR_USE_TLBILX_PID ASM_CONST(0x00080000)
+#define MMU_FTR_USE_TLBILX ASM_CONST(0x00080000)
/* This indicates that the processor cannot handle multiple outstanding
* broadcast tlbivax or tlbsync. This makes the code use a spinlock
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index 9fdf1b8..d68f343 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -1740,7 +1740,8 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "e500mc",
.cpu_features = CPU_FTRS_E500MC,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
- .mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS,
+ .mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS |
+ MMU_FTR_USE_TLBILX,
.icache_bsize = 64,
.dcache_bsize = 64,
.num_pmcs = 4,
diff --git a/arch/powerpc/mm/tlb_nohash_low.S b/arch/powerpc/mm/tlb_nohash_low.S
index f900a39..788b87c 100644
--- a/arch/powerpc/mm/tlb_nohash_low.S
+++ b/arch/powerpc/mm/tlb_nohash_low.S
@@ -118,25 +118,50 @@ _GLOBAL(_tlbil_pid)
#elif defined(CONFIG_FSL_BOOKE)
/*
- * FSL BookE implementations. Currently _pid and _all are the
- * same. This will change when tlbilx is actually supported and
- * performs invalidate-by-PID. This change will be driven by
- * mmu_features conditional
+ * FSL BookE implementations.
+ *
+ * Since feature sections are using _SECTION_ELSE we need
+ * to have the larger code path before the _SECTION_ELSE
*/
+#define MMUCSR0_TLBFI (MMUCSR0_TLB0FI | MMUCSR0_TLB1FI | \
+ MMUCSR0_TLB2FI | MMUCSR0_TLB3FI)
/*
* Flush MMU TLB on the local processor
*/
-_GLOBAL(_tlbil_pid)
_GLOBAL(_tlbil_all)
-#define MMUCSR0_TLBFI (MMUCSR0_TLB0FI | MMUCSR0_TLB1FI | \
- MMUCSR0_TLB2FI | MMUCSR0_TLB3FI)
+BEGIN_MMU_FTR_SECTION
+ li r3,(MMUCSR0_TLBFI)@l
+ mtspr SPRN_MMUCSR0, r3
+1:
+ mfspr r3,SPRN_MMUCSR0
+ andi. r3,r3,MMUCSR0_TLBFI@l
+ bne 1b
+MMU_FTR_SECTION_ELSE
+ PPC_TLBILX_ALL(0,0)
+ALT_MMU_FTR_SECTION_END_IFCLR(MMU_FTR_USE_TLBILX)
+ msync
+ isync
+ blr
+
+_GLOBAL(_tlbil_pid)
+BEGIN_MMU_FTR_SECTION
+ slwi r3,r3,16
+ mfmsr r10
+ wrteei 0
+ mfspr r4,SPRN_MAS6 /* save MAS6 */
+ mtspr SPRN_MAS6,r3
+ PPC_TLBILX_PID(0,0)
+ mtspr SPRN_MAS6,r4 /* restore MAS6 */
+ wrtee r10
+MMU_FTR_SECTION_ELSE
li r3,(MMUCSR0_TLBFI)@l
mtspr SPRN_MMUCSR0, r3
1:
mfspr r3,SPRN_MMUCSR0
andi. r3,r3,MMUCSR0_TLBFI@l
bne 1b
+ALT_MMU_FTR_SECTION_END_IFSET(MMU_FTR_USE_TLBILX)
msync
isync
blr
@@ -149,7 +174,9 @@ _GLOBAL(_tlbil_va)
mfmsr r10
wrteei 0
slwi r4,r4,16
+ ori r4,r4,(MAS6_ISIZE(BOOK3E_PAGESZ_4K))@l
mtspr SPRN_MAS6,r4 /* assume AS=0 for now */
+BEGIN_MMU_FTR_SECTION
tlbsx 0,r3
mfspr r4,SPRN_MAS1 /* check valid */
andis. r3,r4,MAS1_VALID@h
@@ -157,6 +184,9 @@ _GLOBAL(_tlbil_va)
rlwinm r4,r4,0,1,31
mtspr SPRN_MAS1,r4
tlbwe
+MMU_FTR_SECTION_ELSE
+ PPC_TLBILX_VA(0,r3)
+ALT_MMU_FTR_SECTION_END_IFCLR(MMU_FTR_USE_TLBILX)
msync
isync
1: wrtee r10
--
1.5.6.6
^ permalink raw reply related
* Re: 86xx: e600 and cputable.c
From: Kumar Gala @ 2009-02-23 19:13 UTC (permalink / raw)
To: Régis Odeyé; +Cc: linuxppc-dev
In-Reply-To: <49A2D364.8090305@kontron.com>
On Feb 23, 2009, at 10:48 AM, R=E9gis Odey=E9 wrote:
> Hi,
>
> On our board based on 8641D, cat /proc/cpuinfo gives:
> -bash-3.2# cat /proc/cpuinfo
> processor : 0
> cpu : 7448, altivec supported
> clock : 1000.000000MHz
> revision : 0.2 (pvr 8004 0202)
> bogomips : 199.68
>
> processor : 1
> cpu : 7448, altivec supported
> clock : 1000.000000MHz
> revision : 0.2 (pvr 8004 0202)
> bogomips : 199.68
>
> total bogomips : 399.36
> timebase : 100000000
> platform : VM6250
> Vendor : Kontron Modular Computers
> Machine : VM6250
> SVR : 0x80900121
> Memory : 1024 MB
>
> Is there a plan to deal with e600 core as it is done for e500 in =20
> cputable.c to be able to discriminate "classic" ppc against =20
> freescale cores ?
> Already a patch ?
>
what kernel version. I believe this is fixed:
commit a501d8f30e4fcca563e4ee462be00f96e51181d5
Author: Martin Langer <martin-langer@gmx.de>
Date: Sun Sep 7 17:51:32 2008 +1000
powerpc: Fix major revision number for Freescale cores
Some 74xx cores by Freescale are using the configuration field =20
instead
of the major revision field for their revision number. This =20
corrects
the wrong behaviour for those ppc cores including my one.
...
- k=
^ permalink raw reply
* RE: ioremap fails for a device in PCI-E slot on AMCC katmai board
From: Shubhada Pugaonkar @ 2009-02-23 18:17 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
Great...That works!!!
Thank you so much.
Shubhada
-----Original Message-----
From: Benjamin Herrenschmidt [mailto:benh@kernel.crashing.org]=20
Sent: Friday, February 20, 2009 8:38 PM
To: Shubhada Pugaonkar
Cc: linuxppc-dev@ozlabs.org
Subject: Re: ioremap fails for a device in PCI-E slot on AMCC katmai
board
On Fri, 2009-02-20 at 17:35 -0800, Shubhada Pugaonkar wrote:
> ****************************************************
>=20
> cxgb3_main.c:
>=20
> mmio_start =3D pci_resource_start(pdev, 0);
>=20
> mmio_len =3D pci_resource_len(pdev, 0);
>=20
> ai =3D t3_get_adapter_info(ent->driver_data);
>=20
My bet is that mmio_start is an unsigned long instead of a
resource_size_t and thus gets cropped (ie, driver bug).
(/me goes read the source)
Yes, indeed, that's the problem. Change the definition
of mmio_start and mmio_len to resource_size_t, that should
fix it. I suspect the other driver has the same problem.
Note: They will still get cropped, I suspect, when copied
to netdev->mem_start, nothing much to do here, but fortunately
those fields aren't used.
BTW. If you do patches to fix those drivers, please send them
to the netdev@vger.kernel.org mailing list too.
Cheers,
Ben.
^ permalink raw reply
* Re: Newby trying to get Ethernet going on MPC83xx series device.
From: Michael Bergandi @ 2009-02-23 18:14 UTC (permalink / raw)
To: Dushara Jayasinghe
Cc: linuxppc-dev@ozlabs.org, Aggrwal Poonam-B10812, Timur Tabi
In-Reply-To: <DE0CCFFBFF707949BEABD4537A14ACBA0C04B40557@mailsvr>
[-- Attachment #1: Type: text/plain, Size: 883 bytes --]
Great. Glad you have it working.
I actually did the very same thing not too long ago. Amazing how well
things work, when
you pass the right information :)
On Mon, Feb 23, 2009 at 12:28 AM, Dushara Jayasinghe
<DusharaJ@optiscan.com>wrote:
> > -----Original Message-----
> > From: linuxppc-dev-bounces+dusharaj=optiscan.com@ozlabs.org
> > [mailto:linuxppc-dev-bounces+dusharaj <linuxppc-dev-bounces%2Bdusharaj>=
> optiscan.com@ozlabs.org] On
> > Behalf Of Dushara Jayasinghe
> > Sent: Friday, 20 February 2009 6:18 PM
> > To: 'Michael Bergandi'
> > Cc: linuxppc-dev@ozlabs.org; Aggrwal Poonam-B10812; Timur Tabi
> > Subject: RE: Newby trying to get Ethernet going on MPC83xx series
> > device.
>
> Hi all, I've solved this issue. I was passing the wrong .dtb file to the
> kernel. Sorry for adding noise the list.
>
> Thanks again for all you help.
> D
>
>
--
Michael Bergandi
[-- Attachment #2: Type: text/html, Size: 1583 bytes --]
^ permalink raw reply
* Re: Problem with decrementer interrupt
From: sumedh tirodkar @ 2009-02-23 18:02 UTC (permalink / raw)
To: sjoyeau; +Cc: linuxppc-dev
In-Reply-To: <d194b8ce0902230933w2b985134m55b4bf69e8ba20ae@mail.gmail.com>
I have initialised to stack pointer(r1) properly...actually...i went
thru the object dump...bt when i juz use
bla <function_name_handler>
the link register is not getting pushed on to the stack in the prolog
of that function...so the stack is basically not coming into the
picture...
@IRQ originator, that doesn't seem to be a problem...
The only thing that i am able to think of is that when i do a "bla",
the return address is not getting stored in link register...and i m
not able to figure out why...
Regards,
Sumedh
On Mon, Feb 23, 2009 at 11:03 PM, sjoyeau@wanadoo.fr <sjoyeau@wanadoo.fr> w=
rote:
> Hi Sumedh,
>
> You may check the context in which your CPU in running the C code from
> interrupt context (ie stack pointer (r1), kernel locks disabling
> rescheduling etc..) and double check the IRQ originator (the decrementer)=
is
> acknowlegded somewhere your handler before enabling back interrupts, else
> your handler gets fired.
>
> --
> sj
>
> 2009/2/23 sumedh tirodkar <sumedhtirodkar@gmail.com>
>>
>> Alright...I am trying to develop a system of my own..
>> Consider that i am not using any linux kernel...I m writing some
>> program right from scratch.........
>> The major steps that i have taken are...
>>
>> 1. Started with a assembly file...
>> 2. Have relocated the interrupt handlers to there respective
>> positions...The interrupt handlers are written in assembly language...
>> 3. Initialised Decrementer register to get an interrupt after some
>> interval...
>> 4. Jump to some function using
>>
>> bl <function_name_main>
>>
>> function_name_main which will have a infinite while loop..
>> This works fine i.e. the interrupts(decrementer interrupt to be more
>> specific) work fine...I have initialised serial port to get the
>> output...
>>
>> Now, the problem that i am facing....
>>
>> If in interrupt handler of the decrementer, i make a call to some C
>> function in some other C file...using the follwing statement...
>>
>> Dec_handler: /* I have relocated this to interrupt vector address of
>> decrementer interrupt*/
>> /*code to print using serial port*/
>> bla <function_name_handler> /*code to call some function in C file*/
>> /*code to print using serial port---but i m never able to see this
>> output*/
>> RFI
>>
>> This starts creating a problem...somehow we dont return to this code
>> after the end of the function_name_handler...
>> Consider the following code for the function_name_handler:
>> void function_name_handler(void)
>> {
>> /*Some action*/
>> }
>>
>> So, if its possible for anyone to help me with this...please reply...
>>
>> Regards,
>> Sumedh
>>
>>
>> On Mon, Feb 23, 2009 at 8:18 PM, Matt Gessner <mgessner@gmail.com> wrote=
:
>> >
>> >
>> > On Mon, Feb 23, 2009 at 8:03 AM, sumedh tirodkar
>> > <sumedhtirodkar@gmail.com>
>> > wrote:
>> >>
>> >> I am using PowerPC 7447A...I am trying to port SA-RTL on PowerPC...
>> >
>> > What I said earlier was: You need to tell people what cpu you're using=
,
>> > what
>> > linux kernel, etc etc etc.
>> >
>> > Fine, we know the CPU. What kernel are you using? Is it ancient?
>> >
>> > I doubt the information below is going to be useful...
>> >
>> >>
>> >> I am using
>> >>
>> >> bla <function_name>
>> >>
>> >> from the assembly code to call the function in C file...This i am
>> >> doing from interrupt handler of the decrementer...
>> >> If any more details are required, please let me know...
>> >
>> >
>> _______________________________________________
>> Linuxppc-dev mailing list
>> Linuxppc-dev@ozlabs.org
>> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>
>
>
> --
> ------------------
> Sylvain JOYEAU
> Freelance Engineer
> Software RT-OS R&D
> sylvain.joyeau@gmail.com
> T=E9l: +33-(0)667 477 052
> "A good idea is one side of the coin. The other side is the practical
> usefulness". J. Liedke.
>
^ permalink raw reply
* Re: Problem with decrementer interrupt
From: sjoyeau @ 2009-02-23 17:33 UTC (permalink / raw)
To: sumedh tirodkar; +Cc: linuxppc-dev
In-Reply-To: <d2b9ccd90902230904h148827fey293aebd2962c892a@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 3213 bytes --]
Hi Sumedh,
You may check the context in which your CPU in running the C code from
interrupt context (ie stack pointer (r1), kernel locks disabling
rescheduling etc..) and double check the IRQ originator (the decrementer) is
acknowlegded somewhere your handler before enabling back interrupts, else
your handler gets fired.
--
sj
2009/2/23 sumedh tirodkar <sumedhtirodkar@gmail.com>
> Alright...I am trying to develop a system of my own..
> Consider that i am not using any linux kernel...I m writing some
> program right from scratch.........
> The major steps that i have taken are...
>
> 1. Started with a assembly file...
> 2. Have relocated the interrupt handlers to there respective
> positions...The interrupt handlers are written in assembly language...
> 3. Initialised Decrementer register to get an interrupt after some
> interval...
> 4. Jump to some function using
>
> bl <function_name_main>
>
> function_name_main which will have a infinite while loop..
> This works fine i.e. the interrupts(decrementer interrupt to be more
> specific) work fine...I have initialised serial port to get the
> output...
>
> Now, the problem that i am facing....
>
> If in interrupt handler of the decrementer, i make a call to some C
> function in some other C file...using the follwing statement...
>
> Dec_handler: /* I have relocated this to interrupt vector address of
> decrementer interrupt*/
> /*code to print using serial port*/
> bla <function_name_handler> /*code to call some function in C file*/
> /*code to print using serial port---but i m never able to see this
> output*/
> RFI
>
> This starts creating a problem...somehow we dont return to this code
> after the end of the function_name_handler...
> Consider the following code for the function_name_handler:
> void function_name_handler(void)
> {
> /*Some action*/
> }
>
> So, if its possible for anyone to help me with this...please reply...
>
> Regards,
> Sumedh
>
>
> On Mon, Feb 23, 2009 at 8:18 PM, Matt Gessner <mgessner@gmail.com> wrote:
> >
> >
> > On Mon, Feb 23, 2009 at 8:03 AM, sumedh tirodkar <
> sumedhtirodkar@gmail.com>
> > wrote:
> >>
> >> I am using PowerPC 7447A...I am trying to port SA-RTL on PowerPC...
> >
> > What I said earlier was: You need to tell people what cpu you're using,
> what
> > linux kernel, etc etc etc.
> >
> > Fine, we know the CPU. What kernel are you using? Is it ancient?
> >
> > I doubt the information below is going to be useful...
> >
> >>
> >> I am using
> >>
> >> bla <function_name>
> >>
> >> from the assembly code to call the function in C file...This i am
> >> doing from interrupt handler of the decrementer...
> >> If any more details are required, please let me know...
> >
> >
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>
--
------------------
Sylvain JOYEAU
Freelance Engineer
Software RT-OS R&D
sylvain.joyeau@gmail.com
Tél: +33-(0)667 477 052
"A good idea is one side of the coin. The other side is the practical
usefulness". J. Liedke.
[-- Attachment #2: Type: text/html, Size: 4271 bytes --]
^ permalink raw reply
* Re: Problem with decrementer interrupt
From: sumedh tirodkar @ 2009-02-23 17:04 UTC (permalink / raw)
To: Matt Gessner; +Cc: linuxppc-dev
In-Reply-To: <85a5c2010902230648m72b63eeds70b4e7a5ec4d8c51@mail.gmail.com>
Alright...I am trying to develop a system of my own..
Consider that i am not using any linux kernel...I m writing some
program right from scratch.........
The major steps that i have taken are...
1. Started with a assembly file...
2. Have relocated the interrupt handlers to there respective
positions...The interrupt handlers are written in assembly language...
3. Initialised Decrementer register to get an interrupt after some interval...
4. Jump to some function using
bl <function_name_main>
function_name_main which will have a infinite while loop..
This works fine i.e. the interrupts(decrementer interrupt to be more
specific) work fine...I have initialised serial port to get the
output...
Now, the problem that i am facing....
If in interrupt handler of the decrementer, i make a call to some C
function in some other C file...using the follwing statement...
Dec_handler: /* I have relocated this to interrupt vector address of
decrementer interrupt*/
/*code to print using serial port*/
bla <function_name_handler> /*code to call some function in C file*/
/*code to print using serial port---but i m never able to see this output*/
RFI
This starts creating a problem...somehow we dont return to this code
after the end of the function_name_handler...
Consider the following code for the function_name_handler:
void function_name_handler(void)
{
/*Some action*/
}
So, if its possible for anyone to help me with this...please reply...
Regards,
Sumedh
On Mon, Feb 23, 2009 at 8:18 PM, Matt Gessner <mgessner@gmail.com> wrote:
>
>
> On Mon, Feb 23, 2009 at 8:03 AM, sumedh tirodkar <sumedhtirodkar@gmail.com>
> wrote:
>>
>> I am using PowerPC 7447A...I am trying to port SA-RTL on PowerPC...
>
> What I said earlier was: You need to tell people what cpu you're using, what
> linux kernel, etc etc etc.
>
> Fine, we know the CPU. What kernel are you using? Is it ancient?
>
> I doubt the information below is going to be useful...
>
>>
>> I am using
>>
>> bla <function_name>
>>
>> from the assembly code to call the function in C file...This i am
>> doing from interrupt handler of the decrementer...
>> If any more details are required, please let me know...
>
>
^ permalink raw reply
* 86xx: e600 and cputable.c
From: Régis Odeyé @ 2009-02-23 16:48 UTC (permalink / raw)
To: linuxppc-dev
Hi,
On our board based on 8641D, cat /proc/cpuinfo gives:
-bash-3.2# cat /proc/cpuinfo
processor : 0
cpu : 7448, altivec supported
clock : 1000.000000MHz
revision : 0.2 (pvr 8004 0202)
bogomips : 199.68
processor : 1
cpu : 7448, altivec supported
clock : 1000.000000MHz
revision : 0.2 (pvr 8004 0202)
bogomips : 199.68
total bogomips : 399.36
timebase : 100000000
platform : VM6250
Vendor : Kontron Modular Computers
Machine : VM6250
SVR : 0x80900121
Memory : 1024 MB
Is there a plan to deal with e600 core as it is done for e500 in
cputable.c to be able to discriminate "classic" ppc against freescale
cores ?
Already a patch ?
Regards.
--
Régis ODEYE
Kontron Modular Computers SA
150, rue M. Berthelot / ZI Toulon Est / BP 244 / Fr 83078 TOULON Cedex 9
Phone: (33) 4 98 16 34 86 Fax: (33) 4 98 16 34 01
E-mail: regis.odeye@kontron.com Web : www.kontron.com
^ permalink raw reply
* Re: Lock-up on PPC64
From: Geoff Levand @ 2009-02-23 16:36 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: linuxppc-dev, malc, linux-kernel
In-Reply-To: <200901051646.03654.arnd@arndb.de>
On 01/05/2009 07:46 AM, Arnd Bergmann wrote:
> On Sunday 28 December 2008, malc wrote:
>> Now to the Christmas cheer, i've tried v2.6.28 and couldn't help but
>> notice that the problem is gone, bisecting v2.6.27 (which funnily i
>> had to mark good) to v2.6.28 (which has to be marked bad) wasn't fun
>> but eventually converged at ab598b6680f1e74c267d1547ee352f3e1e530f89
>>
>> commit ab598b6680f1e74c267d1547ee352f3e1e530f89
>> Author: Paul Mackerras <paulus@samba.org>
>> Date: Sun Nov 30 11:49:45 2008 +0000
>>
>> powerpc: Fix system calls on Cell entered with XER.SO=1
>>
>> Now the lock-up is gone, however the code never exercises the path
>> taken during the lock-up so i guess it, at least, deserves a better
>> look by PPC64 care takers.
>
>
> Yes, this change was suspected to help with Mono as well, not just
> Java, because both of them use their own syscall path rather than
> going through glibc. The reason why you see the lock-up in a different
> place is because the bug manifested in getting incorrect syscall
> return codes, which probably made mono go into a normally unused
> error handling case.
Just FYI, I looked into a problem of Mono that was reported during
its build. During the build Mono itself is used, and it failed due
to lack of memory. Mono kept using more and more memory until all
was consumed. I didn't look at why, I just saw it with the Gnome
system monitor.
-Geoff
^ permalink raw reply
* Please pull from 'next' branch
From: Kumar Gala @ 2009-02-23 16:26 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
Please pull from 'next' branch of
master.kernel.org:/pub/scm/linux/kernel/git/galak/powerpc.git next
to receive the following updates:
arch/powerpc/include/asm/cputable.h | 4 ++-
arch/powerpc/include/asm/dbell.h | 43 ++++++++++++++++++++++++++++++++++
arch/powerpc/include/asm/mmu.h | 4 +--
arch/powerpc/kernel/Makefile | 2 -
arch/powerpc/kernel/cputable.c | 3 +-
arch/powerpc/kernel/dbell.c | 44 +++++++++++++++++++++++++++++++++++
arch/powerpc/kernel/head_fsl_booke.S | 6 +++-
arch/powerpc/kernel/traps.c | 21 ++++++++++++++++
arch/powerpc/mm/tlb_nohash_low.S | 44 +++++++++++++++++++++++++++++------
arch/powerpc/platforms/83xx/usb.c | 3 +-
arch/powerpc/sysdev/fsl_soc.c | 4 +++
arch/powerpc/sysdev/ipic.c | 6 +---
drivers/i2c/busses/i2c-mpc.c | 9 ++-----
13 files changed, 169 insertions(+), 24 deletions(-)
Anton Vorontsov (1):
powerpc/83xx: Do not configure or probe disabled FSL DR USB controllers
Kumar Gala (2):
powerpc/fsl-booke: Add support for tlbilx instructions
powerpc: Add support for using doorbells for SMP IPI
Timur Tabi (1):
i2c-mpc: do not allow interruptions when waiting for I2C to complete
dayu@datangmobile.cn (1):
powerpc/83xx: Fix the interrupt loss problem on ipic
^ permalink raw reply
* Re: [PATCH] powerpc: Add support for using doorbells for SMP IPI
From: Kumar Gala @ 2009-02-23 16:23 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1235357031.8805.222.camel@pasglop>
On Feb 22, 2009, at 8:43 PM, Benjamin Herrenschmidt wrote:
> On Thu, 2009-02-12 at 17:54 -0600, Kumar Gala wrote:
>> The e500mc supports the new msgsnd/doorbell mechanisms that were
>> added in
>> the Power ISA 2.05 architecture. We use the normal level doorbell
>> for
>> doing SMP IPIs at this point.
>
> Any reason why you don't use the tag ? I'm not too familiar with the
> doorbell stuff just yet but can't you use that instead of doing those
> atomics ?
tag just conveys the type of doorbell (normal, critical, etc.). So
for the 4 IPI msgs we support we need software to keep track.
> On thing we also need to look at is change our low level msg send to
> take a CPU mask.
>
> This will be better generically and we need a good cpu mask based IPI
> for the TLB stuff anyway. Volunteer ? :-)
yeah.. It something on the list :)
- k
>> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
>> ---
>> arch/powerpc/include/asm/cputable.h | 4 ++-
>> arch/powerpc/include/asm/dbell.h | 43 ++++++++++++++++++++++++
>> +++++++++
>> arch/powerpc/kernel/Makefile | 2 +-
>> arch/powerpc/kernel/dbell.c | 44 ++++++++++++++++++++++++
>> ++++++++++
>> arch/powerpc/kernel/head_fsl_booke.S | 6 ++++-
>> arch/powerpc/kernel/traps.c | 21 ++++++++++++++++
>> 6 files changed, 117 insertions(+), 3 deletions(-)
>> create mode 100644 arch/powerpc/include/asm/dbell.h
>> create mode 100644 arch/powerpc/kernel/dbell.c
>>
>> diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/
>> include/asm/cputable.h
>> index 4911104..fca1611 100644
>> --- a/arch/powerpc/include/asm/cputable.h
>> +++ b/arch/powerpc/include/asm/cputable.h
>> @@ -145,6 +145,7 @@ extern const char *powerpc_base_platform;
>> #define CPU_FTR_USE_TB ASM_CONST(0x0000000000000040)
>> #define CPU_FTR_L2CSR ASM_CONST(0x0000000000000080)
>> #define CPU_FTR_601 ASM_CONST(0x0000000000000100)
>> +#define CPU_FTR_DBELL ASM_CONST(0x0000000000000200)
>> #define CPU_FTR_CAN_NAP ASM_CONST(0x0000000000000400)
>> #define CPU_FTR_L3CR ASM_CONST(0x0000000000000800)
>> #define CPU_FTR_L3_DISABLE_NAP ASM_CONST(0x0000000000001000)
>> @@ -373,7 +374,8 @@ extern const char *powerpc_base_platform;
>> CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE)
>> #define CPU_FTRS_E500MC (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \
>> CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN | \
>> - CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE)
>> + CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \
>> + CPU_FTR_DBELL)
>> #define CPU_FTRS_GENERIC_32 (CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN)
>>
>> /* 64-bit CPUs */
>> diff --git a/arch/powerpc/include/asm/dbell.h b/arch/powerpc/
>> include/asm/dbell.h
>> new file mode 100644
>> index 0000000..501189a
>> --- /dev/null
>> +++ b/arch/powerpc/include/asm/dbell.h
>> @@ -0,0 +1,43 @@
>> +/*
>> + * Copyright 2009 Freescale Semicondutor, 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.
>> + *
>> + * provides masks and opcode images for use by code generation,
>> emulation
>> + * and for instructions that older assemblers might not know about
>> + */
>> +#ifndef _ASM_POWERPC_DBELL_H
>> +#define _ASM_POWERPC_DBELL_H
>> +
>> +#include <linux/smp.h>
>> +#include <linux/threads.h>
>> +
>> +#include <asm/ppc-opcode.h>
>> +
>> +#define PPC_DBELL_MSG_BRDCAST (0x04000000)
>> +#define PPC_DBELL_TYPE(x) (((x) & 0xf) << 28)
>> +enum ppc_dbell {
>> + PPC_DBELL = 0, /* doorbell */
>> + PPC_DBELL_CRIT = 1, /* critical doorbell */
>> + PPC_G_DBELL = 2, /* guest doorbell */
>> + PPC_G_DBELL_CRIT = 3, /* guest critical doorbell */
>> + PPC_G_DBELL_MC = 4, /* guest mcheck doorbell */
>> +};
>> +
>> +#ifdef CONFIG_SMP
>> +extern unsigned long dbell_smp_message[NR_CPUS];
>> +extern void smp_dbell_message_pass(int target, int msg);
>> +#endif
>> +
>> +static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32
>> tag)
>> +{
>> + u32 msg = PPC_DBELL_TYPE(type) | (flags & PPC_DBELL_MSG_BRDCAST) |
>> + (tag & 0x07ffffff);
>> +
>> + __asm__ __volatile__ (PPC_MSGSND(%0) : : "r" (msg));
>> +}
>> +
>> +#endif /* _ASM_POWERPC_DBELL_H */
>> diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/
>> Makefile
>> index d159921..f420844 100644
>> --- a/arch/powerpc/kernel/Makefile
>> +++ b/arch/powerpc/kernel/Makefile
>> @@ -61,7 +61,7 @@ obj-$(CONFIG_HIBERNATION) += swsusp.o suspend.o \
>> obj64-$(CONFIG_HIBERNATION) += swsusp_asm64.o
>> obj-$(CONFIG_MODULES) += module.o module_$(CONFIG_WORD_SIZE).o
>> obj-$(CONFIG_44x) += cpu_setup_44x.o
>> -obj-$(CONFIG_FSL_BOOKE) += cpu_setup_fsl_booke.o
>> +obj-$(CONFIG_FSL_BOOKE) += cpu_setup_fsl_booke.o dbell.o
>>
>> extra-$(CONFIG_PPC_STD_MMU) := head_32.o
>> extra-$(CONFIG_PPC64) := head_64.o
>> diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/
>> dbell.c
>> new file mode 100644
>> index 0000000..1493734
>> --- /dev/null
>> +++ b/arch/powerpc/kernel/dbell.c
>> @@ -0,0 +1,44 @@
>> +/*
>> + * Author: Kumar Gala <galak@kernel.crashing.org>
>> + *
>> + * Copyright 2009 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/stddef.h>
>> +#include <linux/kernel.h>
>> +#include <linux/smp.h>
>> +#include <linux/threads.h>
>> +
>> +#include <asm/dbell.h>
>> +
>> +#ifdef CONFIG_SMP
>> +unsigned long dbell_smp_message[NR_CPUS];
>> +
>> +void smp_dbell_message_pass(int target, int msg)
>> +{
>> + int i;
>> +
>> + if(target < NR_CPUS) {
>> + set_bit(msg, &dbell_smp_message[target]);
>> + ppc_msgsnd(PPC_DBELL, 0, target);
>> + }
>> + else if(target == MSG_ALL_BUT_SELF) {
>> + for_each_online_cpu(i) {
>> + if (i == smp_processor_id())
>> + continue;
>> + set_bit(msg, &dbell_smp_message[i]);
>> + ppc_msgsnd(PPC_DBELL, 0, i);
>> + }
>> + }
>> + else { /* target == MSG_ALL */
>> + for_each_online_cpu(i)
>> + set_bit(msg, &dbell_smp_message[i]);
>> + ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0);
>> + }
>> +}
>> +#endif
>> diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/
>> kernel/head_fsl_booke.S
>> index 4ea6e1a..4c22620 100644
>> --- a/arch/powerpc/kernel/head_fsl_booke.S
>> +++ b/arch/powerpc/kernel/head_fsl_booke.S
>> @@ -698,7 +698,9 @@ interrupt_base:
>> /* Performance Monitor */
>> EXCEPTION(0x2060, PerformanceMonitor,
>> performance_monitor_exception, EXC_XFER_STD)
>>
>> - EXCEPTION(0x2070, Doorbell, unknown_exception, EXC_XFER_STD)
>> + EXCEPTION(0x2070, Doorbell, doorbell_exception, EXC_XFER_STD)
>> +
>> + CRITICAL_EXCEPTION(0x2080, CriticalDoorbell, unknown_exception)
>>
>> /* Debug Interrupt */
>> DEBUG_DEBUG_EXCEPTION
>> @@ -921,6 +923,8 @@ _GLOBAL(__setup_e500mc_ivors)
>> mtspr SPRN_IVOR35,r3
>> li r3,Doorbell@l
>> mtspr SPRN_IVOR36,r3
>> + li r3,CriticalDoorbell@l
>> + mtspr SPRN_IVOR37,r3
>> sync
>> blr
>>
>> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/
>> traps.c
>> index 970d66e..678fbff 100644
>> --- a/arch/powerpc/kernel/traps.c
>> +++ b/arch/powerpc/kernel/traps.c
>> @@ -53,6 +53,9 @@
>> #endif
>> #include <asm/kexec.h>
>> #include <asm/ppc-opcode.h>
>> +#ifdef CONFIG_FSL_BOOKE
>> +#include <asm/dbell.h>
>> +#endif
>>
>> #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
>> int (*__debugger)(struct pt_regs *regs);
>> @@ -1122,6 +1125,24 @@ void vsx_assist_exception(struct pt_regs
>> *regs)
>> #endif /* CONFIG_VSX */
>>
>> #ifdef CONFIG_FSL_BOOKE
>> +
>> +void doorbell_exception(struct pt_regs *regs)
>> +{
>> +#ifdef CONFIG_SMP
>> + int cpu = smp_processor_id();
>> + int msg;
>> +
>> + if (num_online_cpus() < 2)
>> + return;
>> +
>> + for (msg = 0; msg < 4; msg++)
>> + if (test_and_clear_bit(msg, &dbell_smp_message[cpu]))
>> + smp_message_recv(msg);
>> +#else
>> + printk(KERN_WARNING "Received doorbell on non-smp system\n");
>> +#endif
>> +}
>> +
>> void CacheLockingException(struct pt_regs *regs, unsigned long
>> address,
>> unsigned long error_code)
>> {
^ permalink raw reply
* Re: Crash (ext3 ) during 2.6.29-rc6 boot
From: Jan Kara @ 2009-02-23 15:51 UTC (permalink / raw)
To: Paul Mackerras
Cc: Jan Kara, Mel Gorman, linux-kernel, linuxppc-dev, Andrew Morton,
linux-ext4
In-Reply-To: <18850.31567.212454.514549@cargo.ozlabs.ibm.com>
> Andrew Morton writes:
>
> > It looks like we died in ext3_xattr_block_get():
> >
> > memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs),
> > size);
> >
> > Perhaps entry->e_value_offs is no good. I wonder if the filesystem is
> > corrupted and this snuck through the defenses.
> >
> > I also wonder if there is enough info in that trace for a ppc person to
> > be able to determine whether the faulting address is in the source or
> > destination of the memcpy() (please)?
>
> It appears to have faulted on a load, implicating the source. The
> address being referenced (0xc00000003f380000) doesn't look
> outlandish. I wonder if this kernel has CONFIG_DEBUG_PAGEALLOC turned
> on, and what page size is selected?
Hmm, OK. But then I'm not sure how that can happen. Obviously, memcpy
somehow got beyond end of the page referenced by bh->b_data. So it means
that le16_to_cpu(entry->e_value_offs) + size > page_size. But
ext3_xattr_find_entry() calls ext3_xattr_check_entry() which in
particular checks whether e_value_offs + e_value_size isn't greater than
bh->b_size. So I see no way how memcpy can get beyond end of the page.
Sachin, is the problem reproducible? If yes, can you send us contents
of the page just before the faulting address (i.e., for current fault it
would be 0xc00000003f370000-0xc00000003f37ffff). As far as I can
remember powerpc monitor could dump it.
BTW, I suppose you use 4KB blocksize on the filesystem, right?
Honza
--
Jan Kara <jack@suse.cz>
SuSE CR Labs
^ permalink raw reply
* Re: [rtc-linux] Re: [PATCH/RFC 0/5] Generic RTC class driver
From: Alessandro Zummo @ 2009-02-23 15:05 UTC (permalink / raw)
To: rtc-linux
Cc: Richard Zidlicky, linux-parisc, Linux Kernel Development, David,
Kyle McMartin, Linux/PPC Development, Linux/m68k,
Geert.Uytterhoeven, Woodhouse
In-Reply-To: <alpine.LRH.2.00.0902231319170.22762@vixen.sonytel.be>
On Mon, 23 Feb 2009 13:34:49 +0100 (CET)
Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> wrote:
> > Hello,
> >
> > my opinion on this kind of stuff is that I want to avoid the layering
> > of implementations under the rtc subsystem. I'd rather prefer that each
> > rtc device had its own driver.
> >
> > I've made error in the past, by accepting such kind of drivers, and
> > would like to avoid that it happens again.
>
> So you want us to kill the ppc_md.[gs]et_rtc_time() [ppc], mach_hwclk() [m68k],
> mach_gettod() [m68knommu] (and probably a few other) abstractions, and move all
> RTC code out of arch/ into seperate drivers under drivers/rtc/ instead?
not all at once :)
I'd start writing a working driver and then see how we should eventually
adapt the rtc subsystem to cope with your needs.
> What about ppc_md.get_boot_time() [ppc]?
> Please note that the functions above may also be used for very early clock
> setting (e.g. time_init()) and in read_persistent_clock().
> How should we handle these?
read_persistent_clock is something that should be reconsidered
as well along with all the ntp stuff.
> Even on x86 there seems to be way too much RTC logic in arch/x86/kernel/rtc.c
> (e.g. mach_get_cmos_time()), which is duplicated in drivers/rtc/rtc-cmos.c
> through the inline function __get_rtc_time() in include/asm-generic/rtc.h
> (clever, hardware-specific stuff in asm-generic ;-)
yep, I know :( that hardcoded rtc stuff seems to be everywhere!
--
Best regards,
Alessandro Zummo,
Tower Technologies - Torino, Italy
http://www.towertech.it
^ 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