add preliminary LEON support

SVN-Revision: 25139
owl
Imre Kaloz 2011-01-27 12:51:25 +00:00
parent a361eab890
commit 0f325459ea
29 changed files with 2217 additions and 0 deletions

View File

@ -0,0 +1,22 @@
#
# Copyright (C) 2011 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
ARCH:=sparc
BOARD:=leon
BOARDNAME:=LEON
FEATURES+=fpu tgz
CFLAGS:=-Os -pipe -mcpu=v8 -funit-at-a-time
KERNELNAME:="uImage"
LINUX_VERSION:=2.6.36.2
MAINTAINER:=Imre Kaloz <kaloz@openwrt.org>
include $(INCLUDE_DIR)/target.mk
$(eval $(call BuildTarget))

View File

@ -0,0 +1,138 @@
# CONFIG_64BIT is not set
CONFIG_ARCH_DEFCONFIG="arch/sparc/configs/sparc32_defconfig"
CONFIG_ARCH_MAY_HAVE_PC_FDC=y
CONFIG_ARCH_NO_VIRT_TO_BUS=y
# CONFIG_ARCH_SUPPORTS_MSI is not set
CONFIG_ARCH_USES_GETTIMEOFFSET=y
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
# CONFIG_ARPD is not set
CONFIG_ATA=y
CONFIG_AUDIT_ARCH=y
CONFIG_BITS=32
# CONFIG_BLK_DEV_INITRD is not set
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_SD=y
CONFIG_BOUNCE=y
# CONFIG_BRIDGE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_COMPAT_BRK=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_DEBUG_BUGVERBOSE=y
CONFIG_DECOMPRESS_LZMA=y
CONFIG_DNOTIFY=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_ELF_CORE=y
CONFIG_EMULATED_CMPXCHG=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_FB=y
# CONFIG_FB_IGA is not set
# CONFIG_FB_SBUS is not set
# CONFIG_FB_SM7XX is not set
# CONFIG_FB_XGI is not set
# CONFIG_FIRMWARE_EDID is not set
CONFIG_FIRMWARE_IN_KERNEL=y
# CONFIG_FONTS is not set
# CONFIG_FONT_SUN12x22 is not set
CONFIG_FONT_SUN8x16=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
CONFIG_FS_POSIX_ACL=y
CONFIG_GENERIC_ACL=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_FIND_LAST_BIT=y
CONFIG_GENERIC_FIND_NEXT_BIT=y
CONFIG_GENERIC_ISA_DMA=y
# CONFIG_GRETH is not set
# CONFIG_HAMRADIO is not set
CONFIG_HAPPYMEAL=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAVE_ARCH_TRACEHOOK=y
CONFIG_HAVE_DMA_API_DEBUG=y
CONFIG_HAVE_DMA_ATTRS=y
# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
CONFIG_HAVE_IDE=y
CONFIG_HAVE_OPROFILE=y
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_HID=m
CONFIG_HID_SUPPORT=y
CONFIG_HIGHMEM=y
CONFIG_HW_CONSOLE=y
CONFIG_HZ=250
# CONFIG_HZ_100 is not set
CONFIG_HZ_250=y
CONFIG_INOTIFY_USER=y
CONFIG_INPUT=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_POLLDEV=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
CONFIG_LOCK_KERNEL=y
# CONFIG_MISC_DEVICES is not set
# CONFIG_MTD is not set
CONFIG_MUTEX_SPIN_ON_OWNER=y
# CONFIG_MYRI_SBUS is not set
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_SG_DMA_LENGTH=y
CONFIG_NR_CPUS=4
CONFIG_OF=y
CONFIG_OF_DEVICE=y
CONFIG_PAGEFLAGS_EXTENDED=y
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_PATA_CMD64X=y
CONFIG_PCI=y
CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_RELAY=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_BQ4802=y
CONFIG_RTC_DRV_M48T59=y
CONFIG_SATA_PMP=y
CONFIG_SBUS=y
CONFIG_SBUSCHAR=y
# CONFIG_SCHED_HRTICK is not set
CONFIG_SCSI=y
# CONFIG_SCSI_LOWLEVEL is not set
# CONFIG_SCSI_MULTI_LUN is not set
CONFIG_SERIAL_8250_NR_UARTS=4
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
CONFIG_SERIAL_CONSOLE=y
CONFIG_SERIAL_SUNCORE=y
CONFIG_SERIAL_SUNSAB=y
CONFIG_SERIAL_SUNSAB_CONSOLE=y
CONFIG_SERIAL_SUNSU=y
CONFIG_SERIAL_SUNSU_CONSOLE=y
# CONFIG_SERIAL_SUNZILOG is not set
# CONFIG_SLAB is not set
CONFIG_SLUB=y
CONFIG_SMP=y
CONFIG_SPARC=y
CONFIG_SPARC32=y
CONFIG_SPARC32_PCI=y
CONFIG_SPARC32_SMP=y
# CONFIG_SPARC64 is not set
# CONFIG_SPARC_LED is not set
CONFIG_SPARC_LEON=y
CONFIG_STOP_MACHINE=y
# CONFIG_SUNBMAC is not set
# CONFIG_SUNLANCE is not set
# CONFIG_SUNQE is not set
# CONFIG_SUN_JSFLASH is not set
CONFIG_SUN_OPENPROMFS=y
CONFIG_SUN_OPENPROMIO=y
CONFIG_SUN_PARTITION=y
CONFIG_SUN_PM=y
# CONFIG_TADPOLE_TS102_UCTRL is not set
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_TREE_RCU=y
CONFIG_UID16=y
CONFIG_USB_SUPPORT=y
# CONFIG_VLAN_8021Q is not set
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_WATCHDOG is not set

View File

@ -0,0 +1,19 @@
#
# Copyright (C) 2011 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/image.mk
define Image/Prepare
cp $(LINUX_DIR)/arch/sparc/boot/uImage $(KDIR)/uImage
endef
define Image/BuildKernel
mkdir -p $(BIN_DIR)
cp $(KDIR)/uImage $(BIN_DIR)/$(IMG_PREFIX)-uImage
endef
$(eval $(call BuildImage))

View File

@ -0,0 +1,48 @@
From af1da1d5a8701f39cdbae4a0ab8e04b450eef298 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 8 Sep 2010 18:05:38 +0200
Subject: [PATCH] SPARC/LEON: find IRQ and Timer via OF-Tree, instead of hardcoded.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/kernel/leon_kernel.c | 23 ++++++++++++++++++++++-
1 files changed, 22 insertions(+), 1 deletions(-)
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -105,13 +105,34 @@ static void leon_disable_irq(unsigned in
void __init leon_init_timers(irq_handler_t counter_fn)
{
int irq;
+ struct device_node *rootnp, *np;
+ struct property *pp;
+ int len;
leondebug_irq_disable = 0;
leon_debug_irqout = 0;
master_l10_counter = (unsigned int *)&dummy_master_l10_counter;
dummy_master_l10_counter = 0;
- if (leon3_gptimer_regs && leon3_irqctrl_regs) {
+ /* Find IRQMP IRQ Controller Registers base address otherwise bail out. */
+ rootnp = of_find_node_by_path("/ambapp0");
+ if (rootnp && (np=of_find_node_by_name(rootnp, "GAISLER_IRQMP"))) {
+ pp = of_find_property(np, "reg", &len);
+ if (pp)
+ leon3_irqctrl_regs = *(struct leon3_irqctrl_regs_map **)pp->value;
+ }
+
+ /* Find GPTIMER Timer Registers base address otherwise bail out. */
+ if (rootnp && (np=of_find_node_by_name(rootnp, "GAISLER_GPTIMER"))) {
+ pp = of_find_property(np, "reg", &len);
+ if (pp)
+ leon3_gptimer_regs = *(struct leon3_gptimer_regs_map **)pp->value;
+ pp = of_find_property(np, "interrupts", &len);
+ if (pp)
+ leon3_gptimer_irq = *(unsigned int *)pp->value;
+ }
+
+ if (leon3_gptimer_regs && leon3_irqctrl_regs && leon3_gptimer_irq) {
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].val, 0);
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].rld,
(((1000000 / 100) - 1)));

View File

@ -0,0 +1,79 @@
From 234ef25344b567b3b8dad62c0863ebe16377528f Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Tue, 14 Sep 2010 11:26:55 +0200
Subject: [PATCH] SPARC: added U-Boot build target: uImage
---
arch/sparc/Makefile | 3 ++-
arch/sparc/boot/Makefile | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+), 1 deletions(-)
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -88,7 +88,7 @@ boot := arch/sparc/boot
# Default target
all: zImage
-image zImage tftpboot.img vmlinux.aout: vmlinux
+image zImage uImage tftpboot.img vmlinux.aout: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
archclean:
@@ -102,6 +102,7 @@ ifeq ($(ARCH),sparc)
define archhelp
echo '* image - kernel image ($(boot)/image)'
echo '* zImage - stripped kernel image ($(boot)/zImage)'
+ echo ' uImage - U-Boot SPARC32/LEON Image'
echo ' tftpboot.img - image prepared for tftp'
endef
else
--- a/arch/sparc/boot/Makefile
+++ b/arch/sparc/boot/Makefile
@@ -5,6 +5,7 @@
ROOT_IMG := /usr/src/root.img
ELFTOAOUT := elftoaout
+MKIMAGE := $(srctree)/scripts/mkuboot.sh
hostprogs-y := piggyback_32 piggyback_64 btfixupprep
targets := tftpboot.img btfix.o btfix.S image zImage vmlinux.aout
@@ -90,5 +91,39 @@ $(obj)/tftpboot.img: $(obj)/image $(obj)
$(obj)/vmlinux.aout: vmlinux FORCE
$(call if_changed,elftoaout)
@echo ' kernel: $@ is ready'
+else
+
+# The following lines make a readable image for U-Boot.
+# uImage - Binary file read by U-boot
+# uImage.o - object file of uImage for loading with a
+# flash programmer understanding ELF.
+
+OBJCOPYFLAGS_image.bin := -S -O binary -R .note -R .comment
+$(obj)/image.bin: $(obj)/image FORCE
+ $(call if_changed,objcopy)
+
+$(obj)/image.gz: $(obj)/image.bin
+ $(call if_changed,gzip)
+
+# Start of Main memory
+ifndef UIMAGE_LOADADDR
+UIMAGE_LOADADDR=0x40004000
endif
+# The first sector after the U-Boot image (256k)
+ifndef UIMAGE_FLASHADDR
+UIMAGE_FLASHADDR=0x00040000
+endif
+
+quiet_cmd_uimage = UIMAGE $@
+ cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A sparc -O linux -T kernel \
+ -C gzip -a $(UIMAGE_LOADADDR) -e 0xf0004000 -n 'Linux-$(KERNELRELEASE)' \
+ -d $< $@
+
+targets += uImage
+$(obj)/uImage: $(obj)/image.gz
+ $(call if_changed,uimage)
+ sparc-linux-ld -Tdata $(UIMAGE_FLASHADDR) -r -b binary arch/sparc/boot/uImage -o arch/sparc/boot/uImage.o
+ @echo ' Image $@ is ready'
+
+endif

View File

@ -0,0 +1,23 @@
From 417bc6751fdd3c24df274f25e020ec3decd09280 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Thu, 16 Sep 2010 11:00:46 +0200
Subject: [PATCH] Fixed SPARC/LEON SMP CPU Stuck problem.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/kernel/leon_smp.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
--- a/arch/sparc/kernel/leon_smp.c
+++ b/arch/sparc/kernel/leon_smp.c
@@ -56,8 +56,8 @@ void __init leon_configure_cache_smp(voi
static inline unsigned long do_swap(volatile unsigned long *ptr,
unsigned long val)
{
- __asm__ __volatile__("swapa [%1] %2, %0\n\t" : "=&r"(val)
- : "r"(ptr), "i"(ASI_LEON_DCACHE_MISS)
+ __asm__ __volatile__("swapa [%2] %3, %0\n\t" : "=&r"(val)
+ : "0"(val), "r"(ptr), "i"(ASI_LEON_DCACHE_MISS)
: "memory");
return val;
}

View File

@ -0,0 +1,119 @@
From a729672f117df3602b6d3171d8ab7a84bf53b053 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Thu, 16 Sep 2010 11:12:41 +0200
Subject: [PATCH] SPARC/LEON: added support for Extended IRQ controller, partial patches are already in git tree.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/include/asm/irq_32.h | 4 ++++
arch/sparc/kernel/irq_32.c | 32 ++++++++++++++++++++++++++------
arch/sparc/kernel/leon_kernel.c | 8 +++++++-
3 files changed, 37 insertions(+), 7 deletions(-)
--- a/arch/sparc/include/asm/irq_32.h
+++ b/arch/sparc/include/asm/irq_32.h
@@ -6,7 +6,11 @@
#ifndef _SPARC_IRQ_H
#define _SPARC_IRQ_H
+#ifdef CONFIG_SPARC_LEON
+#define NR_IRQS 32
+#else
#define NR_IRQS 16
+#endif
#include <linux/interrupt.h>
--- a/arch/sparc/kernel/irq_32.c
+++ b/arch/sparc/kernel/irq_32.c
@@ -110,6 +110,11 @@ EXPORT_SYMBOL(__raw_local_irq_save);
EXPORT_SYMBOL(raw_local_irq_enable);
EXPORT_SYMBOL(raw_local_irq_restore);
+#ifdef CONFIG_SPARC_LEON
+extern unsigned int sparc_leon_eirq;
+extern int sparc_leon_eirq_get(int eirq, int cpu);
+#endif
+
/*
* Dave Redman (djhr@tadpole.co.uk)
*
@@ -222,10 +227,11 @@ void free_irq(unsigned int irq, void *de
return;
}
cpu_irq = irq & (NR_IRQS - 1);
- if (cpu_irq > 14) { /* 14 irq levels on the sparc */
- printk("Trying to free bogus IRQ %d\n", irq);
- return;
- }
+ /* 14 irq levels on the sparc, however some LEON systems have 31 IRQs */
+ if ((cpu_irq == 15) || (cpu_irq >= NR_IRQS)) {
+ printk("Trying to free bogus IRQ %d\n", irq);
+ return;
+ }
spin_lock_irqsave(&irq_action_lock, flags);
@@ -303,7 +309,14 @@ void unexpected_irq(int irq, void *dev_i
int i;
struct irqaction * action;
unsigned int cpu_irq;
-
+
+#ifdef CONFIG_SPARC_LEON
+ /* LEON Extended IRQ requires one extra IRQ Number fetch stage */
+ if ( sparc_leon_eirq == irq ) {
+ irq = sparc_leon_eirq_get(irq, smp_processor_id());
+ }
+#endif
+
cpu_irq = irq & (NR_IRQS - 1);
action = sparc_irq[cpu_irq].action;
@@ -330,6 +343,13 @@ void handler_irq(int irq, struct pt_regs
extern void smp4m_irq_rotate(int cpu);
#endif
+#ifdef CONFIG_SPARC_LEON
+ /* LEON Extended IRQ requires one extra IRQ Number fetch stage */
+ if ( sparc_leon_eirq == irq ) {
+ irq = sparc_leon_eirq_get(irq, cpu);
+ }
+#endif
+
old_regs = set_irq_regs(regs);
irq_enter();
disable_pil_irq(irq);
@@ -526,7 +546,7 @@ int request_irq(unsigned int irq,
return sun4d_request_irq(irq, handler, irqflags, devname, dev_id);
}
cpu_irq = irq & (NR_IRQS - 1);
- if(cpu_irq > 14) {
+ if(cpu_irq == 15) {
ret = -EINVAL;
goto out;
}
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -104,7 +104,7 @@ static void leon_disable_irq(unsigned in
void __init leon_init_timers(irq_handler_t counter_fn)
{
- int irq;
+ int irq, eirq;
struct device_node *rootnp, *np;
struct property *pp;
int len;
@@ -153,6 +153,12 @@ void __init leon_init_timers(irq_handler
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].ctrl, 0);
# endif
+ LEON3_BYPASS_STORE_PA(&(leon3_irqctrl_regs->mask[0]), 0);
+ eirq = (LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->mpstatus) >> 16) & 0xf;
+ if ( eirq != 0 ) {
+ /* Extended IRQ controller available */
+ sparc_leon_eirq_register(eirq);
+ }
} else {
printk(KERN_ERR "No Timer/irqctrl found\n");
BUG();

View File

@ -0,0 +1,58 @@
From 486a578298b7ab45c3edfdce8d4feaef93c3229b Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Thu, 16 Sep 2010 11:15:37 +0200
Subject: [PATCH] SPARC/LEON: to avoid name duplicates in openprom fs when REG is not available the NAME now includes NODE ID when REG not present
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/kernel/prom_32.c | 27 +++++++++++++++++++--------
1 files changed, 19 insertions(+), 8 deletions(-)
--- a/arch/sparc/kernel/prom_32.c
+++ b/arch/sparc/kernel/prom_32.c
@@ -136,18 +136,29 @@ static void __init ebus_path_component(s
/* "name:vendor:device@irq,addrlo" */
static void __init ambapp_path_component(struct device_node *dp, char *tmp_buf)
{
- struct amba_prom_registers *regs; unsigned int *intr;
- unsigned int *device, *vendor;
+ struct amba_prom_registers *regs;
+ unsigned int *intr, *device, *vendor, reg0;
struct property *prop;
+ int interrupt = 0;
+ /* In order to get a unique ID in the device tree (multiple AMBA devices
+ * may have the same name) the node number is printed
+ */
prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
- regs = prop->value;
+ if (!prop) {
+ reg0 = (unsigned int)dp->phandle;
+ } else {
+ regs = prop->value;
+ reg0 = regs->phys_addr;
+ }
+
+ /* Not all cores have Interrupt */
prop = of_find_property(dp, "interrupts", NULL);
if (!prop)
- return;
- intr = prop->value;
+ intr = &interrupt; /* IRQ0 does not exist */
+ else
+ intr = prop->value;
+
prop = of_find_property(dp, "vendor", NULL);
if (!prop)
return;
@@ -159,7 +170,7 @@ static void __init ambapp_path_component
sprintf(tmp_buf, "%s:%d:%d@%x,%x",
dp->name, *vendor, *device,
- *intr, regs->phys_addr);
+ *intr, reg0);
}
static void __init __build_path_component(struct device_node *dp, char *tmp_buf)

View File

@ -0,0 +1,85 @@
From 25a68b8cd8ea1553f8b56278418d6c1ecc12e247 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 22 Sep 2010 10:19:34 +0200
Subject: [PATCH] SPARC/LEON: added support for AMP systems with IRQAMP IRQ Controller.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/include/asm/leon.h | 12 ++++++++++++
arch/sparc/include/asm/leon_amba.h | 6 +++---
arch/sparc/kernel/leon_kernel.c | 14 ++++++++++++++
3 files changed, 29 insertions(+), 3 deletions(-)
--- a/arch/sparc/include/asm/leon.h
+++ b/arch/sparc/include/asm/leon.h
@@ -224,6 +224,18 @@ static inline void sparc_leon3_disable_c
"sta %%l2, [%%g0] 2\n\t" : : : "l1", "l2");
};
+static inline unsigned long sparc_leon3_asr17(void)
+{
+ u32 asr17;
+ __asm__ __volatile__ ("rd %%asr17, %0\n\t" : "=r"(asr17));
+ return asr17;
+};
+
+static inline int sparc_leon3_cpuid(void)
+{
+ return sparc_leon3_asr17() >> 28;
+}
+
#endif /*!__ASSEMBLY__*/
#ifdef CONFIG_SMP
--- a/arch/sparc/include/asm/leon_amba.h
+++ b/arch/sparc/include/asm/leon_amba.h
@@ -100,9 +100,8 @@ struct leon3_irqctrl_regs_map {
u32 mpbroadcast;
u32 notused02;
u32 notused03;
- u32 notused10;
- u32 notused11;
- u32 notused12;
+ u32 ampctrl;
+ u32 icsel[2];
u32 notused13;
u32 notused20;
u32 notused21;
@@ -112,6 +111,7 @@ struct leon3_irqctrl_regs_map {
u32 force[16];
/* Extended IRQ registers */
u32 intid[16]; /* 0xc0 */
+ u32 unused[(0x1000-0x100)/4];
};
struct leon3_apbuart_regs_map {
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -108,6 +108,7 @@ void __init leon_init_timers(irq_handler
struct device_node *rootnp, *np;
struct property *pp;
int len;
+ int cpu, icsel;
leondebug_irq_disable = 0;
leon_debug_irqout = 0;
@@ -153,6 +154,19 @@ void __init leon_init_timers(irq_handler
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].ctrl, 0);
# endif
+ /* The IRQ controller may (if implemented) consist of multiple
+ * IRQ controllers, each mapped on a 4Kb boundary.
+ * Each CPU may be routed to different IRQCTRLs, however
+ * we assume that all CPUs (in SMP system) is routed to the
+ * same IRQ Controller, and for non-SMP only one IRQCTRL is
+ * accessed anyway.
+ * In AMP systems, Linux may not be run on CPU0.
+ */
+ cpu = sparc_leon3_cpuid();
+ icsel = LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->icsel[cpu/8]);
+ icsel = (icsel >> ((7 - (cpu&0x7)) * 4)) & 0xf;
+ leon3_irqctrl_regs += icsel;
+
LEON3_BYPASS_STORE_PA(&(leon3_irqctrl_regs->mask[0]), 0);
eirq = (LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->mpstatus) >> 16) & 0xf;
if ( eirq != 0 ) {

View File

@ -0,0 +1,119 @@
From 1dffe06838c26b7c3fc99f9ddb7db78e378f6908 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 22 Sep 2010 13:21:13 +0200
Subject: [PATCH] SPARC/LEON: added support for selecting Timer Core and Timer within core, useful for AMP systems.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/kernel/leon_kernel.c | 41 +++++++++++++++++++++++++-------------
1 files changed, 27 insertions(+), 14 deletions(-)
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -23,15 +23,16 @@
#include "prom.h"
#include "irq.h"
-struct leon3_irqctrl_regs_map *leon3_irqctrl_regs; /* interrupt controller base address, initialized by amba_init() */
-struct leon3_gptimer_regs_map *leon3_gptimer_regs; /* timer controller base address, initialized by amba_init() */
+struct leon3_irqctrl_regs_map *leon3_irqctrl_regs = NULL; /* interrupt controller base address, initialized by amba_init() */
+struct leon3_gptimer_regs_map *leon3_gptimer_regs = NULL; /* timer controller base address, initialized by amba_init() */
struct amba_apb_device leon_percpu_timer_dev[16];
int leondebug_irq_disable;
int leon_debug_irqout;
static int dummy_master_l10_counter;
-unsigned long leon3_gptimer_irq; /* interrupt controller irq number, initialized by amba_init() */
+unsigned long leon3_gptimer_irq = 0; /* interrupt controller irq number, initialized by amba_init() */
+unsigned long leon3_gptimer_idx = 0; /* Timer Index (starting at 0) with Timer Core */
unsigned int sparc_leon_eirq;
#define LEON_IMASK ((&leon3_irqctrl_regs->mask[0]))
@@ -109,6 +110,7 @@ void __init leon_init_timers(irq_handler
struct property *pp;
int len;
int cpu, icsel;
+ int ampopts;
leondebug_irq_disable = 0;
leon_debug_irqout = 0;
@@ -124,24 +126,35 @@ void __init leon_init_timers(irq_handler
}
/* Find GPTIMER Timer Registers base address otherwise bail out. */
- if (rootnp && (np=of_find_node_by_name(rootnp, "GAISLER_GPTIMER"))) {
+ np = rootnp;
+ while (np && (np=of_find_node_by_name(np, "GAISLER_GPTIMER"))) {
+ ampopts = 0;
+ pp = of_find_property(np, "ampopts", &len);
+ if ( pp && ((ampopts = *(int *)pp->value) == 0) ) {
+ /* Skip this instance, resource already allocated by other OS */
+ continue;
+ }
+ /* Select Timer-Instance on Timer Core. Default is zero */
+ leon3_gptimer_idx = ampopts & 0x7;
+
pp = of_find_property(np, "reg", &len);
if (pp)
leon3_gptimer_regs = *(struct leon3_gptimer_regs_map **)pp->value;
pp = of_find_property(np, "interrupts", &len);
if (pp)
leon3_gptimer_irq = *(unsigned int *)pp->value;
+ break;
}
if (leon3_gptimer_regs && leon3_irqctrl_regs && leon3_gptimer_irq) {
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].val, 0);
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].rld,
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val, 0);
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld,
(((1000000 / 100) - 1)));
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].ctrl, 0);
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl, 0);
#ifdef CONFIG_SMP
leon_percpu_timer_dev[0].start = (int)leon3_gptimer_regs;
- leon_percpu_timer_dev[0].irq = leon3_gptimer_irq+1;
+ leon_percpu_timer_dev[0].irq = leon3_gptimer_irq+1+leon3_gptimer_idx;
if (!(LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->config) &
(1<<LEON3_GPTIMER_SEPIRQ))) {
@@ -149,9 +162,9 @@ void __init leon_init_timers(irq_handler
BUG();
}
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].val, 0);
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].rld, (((1000000/100) - 1)));
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].ctrl, 0);
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].val, 0);
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].rld, (((1000000/100) - 1)));
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl, 0);
# endif
/* The IRQ controller may (if implemented) consist of multiple
@@ -178,7 +191,7 @@ void __init leon_init_timers(irq_handler
BUG();
}
- irq = request_irq(leon3_gptimer_irq,
+ irq = request_irq(leon3_gptimer_irq+leon3_gptimer_idx,
counter_fn,
(IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL);
@@ -210,13 +223,13 @@ void __init leon_init_timers(irq_handler
# endif
if (leon3_gptimer_regs) {
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].ctrl,
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
LEON3_GPTIMER_EN |
LEON3_GPTIMER_RL |
LEON3_GPTIMER_LD | LEON3_GPTIMER_IRQEN);
#ifdef CONFIG_SMP
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].ctrl,
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl,
LEON3_GPTIMER_EN |
LEON3_GPTIMER_RL |
LEON3_GPTIMER_LD |

View File

@ -0,0 +1,30 @@
From e4d697dad4d43109f045a4f25cb1d706122045c0 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 22 Sep 2010 13:24:36 +0200
Subject: [PATCH] SPARC/LEON: removed constant timer initialization as if HZ=100, now it reflects the value of HZ
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/kernel/leon_kernel.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -149,7 +149,7 @@ void __init leon_init_timers(irq_handler
if (leon3_gptimer_regs && leon3_irqctrl_regs && leon3_gptimer_irq) {
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val, 0);
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld,
- (((1000000 / 100) - 1)));
+ (((1000000 / HZ) - 1)));
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl, 0);
#ifdef CONFIG_SMP
@@ -163,7 +163,7 @@ void __init leon_init_timers(irq_handler
}
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].val, 0);
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].rld, (((1000000/100) - 1)));
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].rld, (((1000000 / HZ) - 1)));
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl, 0);
# endif

View File

@ -0,0 +1,187 @@
From 300f3ee36c3019ee36f81befd91cd1b32544cefe Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 22 Sep 2010 15:39:05 +0200
Subject: [PATCH] SPARC/LEON: Removed the need for two timers, per-cpu ticker is shared with system clock timer.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/include/asm/leon.h | 2 +-
arch/sparc/include/asm/leon_amba.h | 3 +-
arch/sparc/kernel/entry.S | 3 +-
arch/sparc/kernel/leon_kernel.c | 37 ++++++++---------------------------
arch/sparc/kernel/leon_smp.c | 8 ++++++-
5 files changed, 21 insertions(+), 32 deletions(-)
--- a/arch/sparc/include/asm/leon.h
+++ b/arch/sparc/include/asm/leon.h
@@ -240,7 +240,7 @@ static inline int sparc_leon3_cpuid(void
#ifdef CONFIG_SMP
# define LEON3_IRQ_RESCHEDULE 13
-# define LEON3_IRQ_TICKER (leon_percpu_timer_dev[0].irq)
+# define LEON3_IRQ_TICKER (leon3_gptimer_irq + leon3_gptimer_idx)
# define LEON3_IRQ_CROSS_CALL 15
#endif
--- a/arch/sparc/include/asm/leon_amba.h
+++ b/arch/sparc/include/asm/leon_amba.h
@@ -182,11 +182,12 @@ void _amba_init(struct device_node *dp,
extern struct leon3_irqctrl_regs_map *leon3_irqctrl_regs;
extern struct leon3_gptimer_regs_map *leon3_gptimer_regs;
-extern struct amba_apb_device leon_percpu_timer_dev[16];
extern int leondebug_irq_disable;
extern int leon_debug_irqout;
extern unsigned long leon3_gptimer_irq;
+extern unsigned long leon3_gptimer_idx; /* Timer Index (starting at 0) with Timer Core */
extern unsigned int sparc_leon_eirq;
+extern unsigned long leon3_cpu_idx;
#endif /* __ASSEMBLY__ */
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -411,8 +411,9 @@ smpleon_ticker:
WRITE_PAUSE
wr %g2, PSR_ET, %psr
WRITE_PAUSE
+ mov %l7, %o0 ! irq level
call leon_percpu_timer_interrupt
- add %sp, STACKFRAME_SZ, %o0
+ add %sp, STACKFRAME_SZ, %o1 ! pt_regs
wr %l0, PSR_ET, %psr
WRITE_PAUSE
RESTORE_ALL
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -25,7 +25,6 @@
struct leon3_irqctrl_regs_map *leon3_irqctrl_regs = NULL; /* interrupt controller base address, initialized by amba_init() */
struct leon3_gptimer_regs_map *leon3_gptimer_regs = NULL; /* timer controller base address, initialized by amba_init() */
-struct amba_apb_device leon_percpu_timer_dev[16];
int leondebug_irq_disable;
int leon_debug_irqout;
@@ -34,6 +33,7 @@ static int dummy_master_l10_counter;
unsigned long leon3_gptimer_irq = 0; /* interrupt controller irq number, initialized by amba_init() */
unsigned long leon3_gptimer_idx = 0; /* Timer Index (starting at 0) with Timer Core */
unsigned int sparc_leon_eirq;
+unsigned long leon3_cpu_idx = 0; /* Boot CPU Index */
#define LEON_IMASK ((&leon3_irqctrl_regs->mask[0]))
/* Return the IRQ of the pending IRQ on the extended IRQ controller */
@@ -109,13 +109,14 @@ void __init leon_init_timers(irq_handler
struct device_node *rootnp, *np;
struct property *pp;
int len;
- int cpu, icsel;
+ int icsel;
int ampopts;
leondebug_irq_disable = 0;
leon_debug_irqout = 0;
master_l10_counter = (unsigned int *)&dummy_master_l10_counter;
dummy_master_l10_counter = 0;
+ leon3_cpu_idx = sparc_leon3_cpuid();
/* Find IRQMP IRQ Controller Registers base address otherwise bail out. */
rootnp = of_find_node_by_path("/ambapp0");
@@ -152,21 +153,11 @@ void __init leon_init_timers(irq_handler
(((1000000 / HZ) - 1)));
LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl, 0);
-#ifdef CONFIG_SMP
- leon_percpu_timer_dev[0].start = (int)leon3_gptimer_regs;
- leon_percpu_timer_dev[0].irq = leon3_gptimer_irq+1+leon3_gptimer_idx;
-
if (!(LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->config) &
(1<<LEON3_GPTIMER_SEPIRQ))) {
- prom_printf("irq timer not configured with separate irqs\n");
- BUG();
+ prom_printf("LEON-SMP: GPTIMER use shared irqs, using other timers will fail.\n");
}
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].val, 0);
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].rld, (((1000000 / HZ) - 1)));
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl, 0);
-# endif
-
/* The IRQ controller may (if implemented) consist of multiple
* IRQ controllers, each mapped on a 4Kb boundary.
* Each CPU may be routed to different IRQCTRLs, however
@@ -175,9 +166,8 @@ void __init leon_init_timers(irq_handler
* accessed anyway.
* In AMP systems, Linux may not be run on CPU0.
*/
- cpu = sparc_leon3_cpuid();
- icsel = LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->icsel[cpu/8]);
- icsel = (icsel >> ((7 - (cpu&0x7)) * 4)) & 0xf;
+ icsel = LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->icsel[leon3_cpu_idx/8]);
+ icsel = (icsel >> ((7 - (leon3_cpu_idx & 0x7)) * 4)) & 0xf;
leon3_irqctrl_regs += icsel;
LEON3_BYPASS_STORE_PA(&(leon3_irqctrl_regs->mask[0]), 0);
@@ -204,7 +194,8 @@ void __init leon_init_timers(irq_handler
# ifdef CONFIG_SMP
{
unsigned long flags;
- struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (leon_percpu_timer_dev[0].irq - 1)];
+ struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 +
+ (leon3_gptimer_irq + leon3_gptimer_idx - 1)];
/* For SMP we use the level 14 ticker, however the bootup code
* has copied the firmwares level 14 vector into boot cpu's
@@ -222,21 +213,11 @@ void __init leon_init_timers(irq_handler
}
# endif
- if (leon3_gptimer_regs) {
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
+ LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
LEON3_GPTIMER_EN |
LEON3_GPTIMER_RL |
LEON3_GPTIMER_LD | LEON3_GPTIMER_IRQEN);
-#ifdef CONFIG_SMP
- LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl,
- LEON3_GPTIMER_EN |
- LEON3_GPTIMER_RL |
- LEON3_GPTIMER_LD |
- LEON3_GPTIMER_IRQEN);
-#endif
-
- }
}
void leon_clear_clock_irq(void)
--- a/arch/sparc/kernel/leon_smp.c
+++ b/arch/sparc/kernel/leon_smp.c
@@ -52,6 +52,7 @@ extern volatile unsigned long cpu_callin
extern unsigned char boot_cpu_id;
extern cpumask_t smp_commenced_mask;
void __init leon_configure_cache_smp(void);
+extern void handler_irq(int irq, struct pt_regs * regs);
static inline unsigned long do_swap(volatile unsigned long *ptr,
unsigned long val)
@@ -385,7 +386,7 @@ void leon_cross_call_irq(void)
ccall_info.processors_out[i] = 1;
}
-void leon_percpu_timer_interrupt(struct pt_regs *regs)
+void leon_percpu_timer_interrupt(int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs;
int cpu = smp_processor_id();
@@ -406,6 +407,11 @@ void leon_percpu_timer_interrupt(struct
prof_counter(cpu) = prof_multiplier(cpu);
}
set_irq_regs(old_regs);
+
+ if ( cpu == leon3_cpu_idx ) {
+ /* Ticker Clock is shared with the System Clock */
+ handler_irq(irq, regs);
+ }
}
static void __init smp_setup_percpu_timer(void)

View File

@ -0,0 +1,41 @@
From 8129fa5437f3fe5f95bac180a43cd5a856cebdf3 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 20 Oct 2010 17:00:41 +0200
Subject: [PATCH] Added support for ampopts in APBUART driver. Used in AMP systems.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/serial/apbuart.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
--- a/drivers/serial/apbuart.c
+++ b/drivers/serial/apbuart.c
@@ -26,6 +26,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/serial_core.h>
@@ -573,7 +574,6 @@ static int __devinit apbuart_probe(struc
printk(KERN_INFO "grlib-apbuart at 0x%llx, irq %d\n",
(unsigned long long) port->mapbase, port->irq);
return 0;
-
}
static struct of_device_id __initdata apbuart_match[] = {
@@ -620,9 +620,12 @@ static void grlib_apbuart_configure(void
int *vendor = (int *) of_get_property(np, "vendor", NULL);
int *device = (int *) of_get_property(np, "device", NULL);
int *irqs = (int *) of_get_property(np, "interrupts", NULL);
+ int *ampopts = (int *) of_get_property(np, "ampopts", NULL);
regs = (struct amba_prom_registers *)
of_get_property(np, "reg", NULL);
+ if (ampopts && (*ampopts == 0))
+ continue; /* Ignore if used by another OS instance */
if (vendor)
v = *vendor;
if (device)

View File

@ -0,0 +1,21 @@
From 3d7788e8f5ae3d44e48f9b7476528acf3d9c8b32 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 20 Oct 2010 17:07:12 +0200
Subject: [PATCH] GRETH: Fixed potential future problem where unhandled IRQ is cleared.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -584,7 +584,7 @@ static irqreturn_t greth_interrupt(int i
if (status & (GRETH_INT_RX | GRETH_INT_TX)) {
/* Clear interrupt status */
- GRETH_REGORIN(greth->regs->status,
+ GRETH_REGSAVE(greth->regs->status,
status & (GRETH_INT_RX | GRETH_INT_TX));
retval = IRQ_HANDLED;

View File

@ -0,0 +1,22 @@
From ff5f2ee8b37f2278dd86946761313c3664c51836 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Thu, 21 Oct 2010 14:34:48 +0200
Subject: [PATCH] GRETH: added raw AMBA vendor/device number to match against.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -1600,6 +1600,9 @@ static struct of_device_id greth_of_matc
{
.name = "GAISLER_ETHMAC",
},
+ {
+ .name = "01_01d",
+ },
{},
};

View File

@ -0,0 +1,22 @@
From 1c14b8995c1b8212180b5009dc04ac222a449ecb Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Thu, 21 Oct 2010 14:35:09 +0200
Subject: [PATCH] APBUART: added raw AMBA vendor/device number to match against.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/serial/apbuart.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
--- a/drivers/serial/apbuart.c
+++ b/drivers/serial/apbuart.c
@@ -580,6 +580,9 @@ static struct of_device_id __initdata ap
{
.name = "GAISLER_APBUART",
},
+ {
+ .name = "01_00c",
+ },
{},
};

View File

@ -0,0 +1,32 @@
From 12782c44d0c687b5b0400a8224a9b1bf9eb9a428 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Tue, 26 Oct 2010 09:59:05 +0200
Subject: [PATCH] TIMER,IRQCTRL: added raw AMBA vendor/device number to match against.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/kernel/leon_kernel.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -120,7 +120,8 @@ void __init leon_init_timers(irq_handler
/* Find IRQMP IRQ Controller Registers base address otherwise bail out. */
rootnp = of_find_node_by_path("/ambapp0");
- if (rootnp && (np=of_find_node_by_name(rootnp, "GAISLER_IRQMP"))) {
+ if (rootnp && ((np=of_find_node_by_name(rootnp, "GAISLER_IRQMP")) ||
+ (np=of_find_node_by_name(rootnp, "01_00d")))) {
pp = of_find_property(np, "reg", &len);
if (pp)
leon3_irqctrl_regs = *(struct leon3_irqctrl_regs_map **)pp->value;
@@ -128,7 +129,8 @@ void __init leon_init_timers(irq_handler
/* Find GPTIMER Timer Registers base address otherwise bail out. */
np = rootnp;
- while (np && (np=of_find_node_by_name(np, "GAISLER_GPTIMER"))) {
+ while (np && ((np=of_find_node_by_name(np, "GAISLER_GPTIMER")) ||
+ (np=of_find_node_by_name(np, "01_011")))) {
ampopts = 0;
pp = of_find_property(np, "ampopts", &len);
if ( pp && ((ampopts = *(int *)pp->value) == 0) ) {

View File

@ -0,0 +1,296 @@
From c6d8f92cfd7f4f19eb3b16545b3b68c561978fe8 Mon Sep 17 00:00:00 2001
From: Kristoffer Glembo <kristoffer@gaisler.com>
Date: Mon, 7 Jun 2010 14:00:30 +0200
Subject: [PATCH] sparc32: Added LEON dma_ops.
Added leon3_dma_ops and mmu_inval_dma_area.
---
arch/sparc/kernel/ioport.c | 139 +++++++++++++++++++++++++++++++------------
1 files changed, 100 insertions(+), 39 deletions(-)
--- a/arch/sparc/kernel/ioport.c
+++ b/arch/sparc/kernel/ioport.c
@@ -50,10 +50,15 @@
#include <asm/io-unit.h>
#include <asm/leon.h>
-#ifdef CONFIG_SPARC_LEON
-#define mmu_inval_dma_area(p, l) leon_flush_dcache_all()
-#else
+#ifndef CONFIG_SPARC_LEON
#define mmu_inval_dma_area(p, l) /* Anton pulled it out for 2.4.0-xx */
+#else
+static inline void mmu_inval_dma_area(unsigned long va, unsigned long len)
+{
+ if (!sparc_leon3_snooping_enabled()) {
+ leon_flush_dcache_all();
+ }
+}
#endif
static struct resource *_sparc_find_resource(struct resource *r,
@@ -254,7 +259,7 @@ static void *sbus_alloc_coherent(struct
dma_addr_t *dma_addrp, gfp_t gfp)
{
struct platform_device *op = to_platform_device(dev);
- unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK;
+ unsigned long len_total = PAGE_ALIGN(len);
unsigned long va;
struct resource *res;
int order;
@@ -287,15 +292,19 @@ static void *sbus_alloc_coherent(struct
* XXX That's where sdev would be used. Currently we load
* all iommu tables with the same translations.
*/
- if (mmu_map_dma_area(dev, dma_addrp, va, res->start, len_total) != 0)
- goto err_noiommu;
-
- res->name = op->dev.of_node->name;
+#ifdef CONFIG_SPARC_LEON
+ sparc_mapiorange(0, virt_to_phys(va), res->start, len_total);
+ *dma_addrp = virt_to_phys(va);
+#else
+ if (mmu_map_dma_area(dev, dma_addrp, va, res->start, len_total) != 0) {
+ release_resource(res);
+ goto err_nova;
+ }
+#endif
+ res->name = op->node->name;
return (void *)(unsigned long)res->start;
-err_noiommu:
- release_resource(res);
err_nova:
free_pages(va, order);
err_nomem:
@@ -321,7 +330,7 @@ static void sbus_free_coherent(struct de
return;
}
- n = (n + PAGE_SIZE-1) & PAGE_MASK;
+ n = PAGE_ALIGN(n);
if ((res->end-res->start)+1 != n) {
printk("sbus_free_consistent: region 0x%lx asked 0x%zx\n",
(long)((res->end-res->start)+1), n);
@@ -333,7 +342,12 @@ static void sbus_free_coherent(struct de
/* mmu_inval_dma_area(va, n); */ /* it's consistent, isn't it */
pgv = virt_to_page(p);
- mmu_unmap_dma_area(dev, ba, n);
+
+#ifdef CONFIG_SPARC_LEON
+ sparc_unmapiorange((unsigned long)p, n);
+#else
+ mmu_unmap_dma_area(dev, ba, n);
+#endif
__free_pages(pgv, get_order(n));
}
@@ -408,9 +422,6 @@ struct dma_map_ops sbus_dma_ops = {
.sync_sg_for_device = sbus_sync_sg_for_device,
};
-struct dma_map_ops *dma_ops = &sbus_dma_ops;
-EXPORT_SYMBOL(dma_ops);
-
static int __init sparc_register_ioport(void)
{
register_proc_sparc_ioport();
@@ -422,7 +433,7 @@ arch_initcall(sparc_register_ioport);
#endif /* CONFIG_SBUS */
-#ifdef CONFIG_PCI
+#if defined(CONFIG_PCI) || defined(CONFIG_SPARC_LEON)
/* Allocate and map kernel buffer using consistent mode DMA for a device.
* hwdev should be valid struct pci_dev pointer for PCI devices.
@@ -430,7 +441,7 @@ arch_initcall(sparc_register_ioport);
static void *pci32_alloc_coherent(struct device *dev, size_t len,
dma_addr_t *pba, gfp_t gfp)
{
- unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK;
+ unsigned long len_total = PAGE_ALIGN(len);
unsigned long va;
struct resource *res;
int order;
@@ -463,10 +474,6 @@ static void *pci32_alloc_coherent(struct
return NULL;
}
mmu_inval_dma_area(va, len_total);
-#if 0
-/* P3 */ printk("pci_alloc_consistent: kva %lx uncva %lx phys %lx size %lx\n",
- (long)va, (long)res->start, (long)virt_to_phys(va), len_total);
-#endif
sparc_mapiorange(0, virt_to_phys(va), res->start, len_total);
*pba = virt_to_phys(va); /* equals virt_to_bus (R.I.P.) for us. */
@@ -498,7 +505,7 @@ static void pci32_free_coherent(struct d
return;
}
- n = (n + PAGE_SIZE-1) & PAGE_MASK;
+ n = PAGE_ALIGN(n);
if ((res->end-res->start)+1 != n) {
printk("pci_free_consistent: region 0x%lx asked 0x%lx\n",
(long)((res->end-res->start)+1), (long)n);
@@ -515,6 +522,14 @@ static void pci32_free_coherent(struct d
free_pages(pgp, get_order(n));
}
+static void pci32_unmap_page(struct device *dev, dma_addr_t ba, size_t size,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ if (dir != PCI_DMA_TODEVICE) {
+ mmu_inval_dma_area((unsigned long)phys_to_virt(ba), PAGE_ALIGN(size));
+ }
+}
+
/*
* Same as pci_map_single, but with pages.
*/
@@ -551,8 +566,7 @@ static int pci32_map_sg(struct device *d
/* IIep is write-through, not flushing. */
for_each_sg(sgl, sg, nents, n) {
- BUG_ON(page_address(sg_page(sg)) == NULL);
- sg->dma_address = virt_to_phys(sg_virt(sg));
+ sg->dma_address = sg_phys(sg);
sg->dma_length = sg->length;
}
return nents;
@@ -571,10 +585,7 @@ static void pci32_unmap_sg(struct device
if (dir != PCI_DMA_TODEVICE) {
for_each_sg(sgl, sg, nents, n) {
- BUG_ON(page_address(sg_page(sg)) == NULL);
- mmu_inval_dma_area(
- (unsigned long) page_address(sg_page(sg)),
- (sg->length + PAGE_SIZE-1) & PAGE_MASK);
+ mmu_inval_dma_area((unsigned long)sg_virt(sg), PAGE_ALIGN(sg->length));
}
}
}
@@ -594,7 +605,7 @@ static void pci32_sync_single_for_cpu(st
{
if (dir != PCI_DMA_TODEVICE) {
mmu_inval_dma_area((unsigned long)phys_to_virt(ba),
- (size + PAGE_SIZE-1) & PAGE_MASK);
+ PAGE_ALIGN(size));
}
}
@@ -621,10 +632,7 @@ static void pci32_sync_sg_for_cpu(struct
if (dir != PCI_DMA_TODEVICE) {
for_each_sg(sgl, sg, nents, n) {
- BUG_ON(page_address(sg_page(sg)) == NULL);
- mmu_inval_dma_area(
- (unsigned long) page_address(sg_page(sg)),
- (sg->length + PAGE_SIZE-1) & PAGE_MASK);
+ mmu_inval_dma_area((unsigned long)sg_virt(sg), PAGE_ALIGN(sg->length));
}
}
}
@@ -637,18 +645,38 @@ static void pci32_sync_sg_for_device(str
if (dir != PCI_DMA_TODEVICE) {
for_each_sg(sgl, sg, nents, n) {
- BUG_ON(page_address(sg_page(sg)) == NULL);
- mmu_inval_dma_area(
- (unsigned long) page_address(sg_page(sg)),
- (sg->length + PAGE_SIZE-1) & PAGE_MASK);
+ mmu_inval_dma_area((unsigned long)sg_virt(sg), PAGE_ALIGN(sg->length));
}
}
}
+/* LEON3 unmapping functions
+ *
+ * We can only invalidate the whole cache so unmap_page and unmap_sg do the same thing
+ */
+static void leon3_unmap_page(struct device *dev, dma_addr_t ba, size_t size,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ if (dir != PCI_DMA_TODEVICE) {
+ mmu_inval_dma_area(0, 0);
+ }
+}
+
+static void leon3_unmap_sg(struct device *dev, struct scatterlist *sgl,
+ int nents, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+
+ if (dir != PCI_DMA_TODEVICE) {
+ mmu_inval_dma_area(0, 0);
+ }
+}
+
struct dma_map_ops pci32_dma_ops = {
.alloc_coherent = pci32_alloc_coherent,
.free_coherent = pci32_free_coherent,
.map_page = pci32_map_page,
+ .unmap_page = pci32_unmap_page,
.map_sg = pci32_map_sg,
.unmap_sg = pci32_unmap_sg,
.sync_single_for_cpu = pci32_sync_single_for_cpu,
@@ -658,7 +686,30 @@ struct dma_map_ops pci32_dma_ops = {
};
EXPORT_SYMBOL(pci32_dma_ops);
-#endif /* CONFIG_PCI */
+struct dma_map_ops leon3_dma_ops = {
+ .alloc_coherent = sbus_alloc_coherent,
+ .free_coherent = sbus_free_coherent,
+ .map_page = pci32_map_page,
+ .unmap_page = leon3_unmap_page,
+ .map_sg = pci32_map_sg,
+ .unmap_sg = leon3_unmap_sg,
+ .sync_single_for_cpu = pci32_sync_single_for_cpu,
+ .sync_single_for_device = pci32_sync_single_for_device,
+ .sync_sg_for_cpu = pci32_sync_sg_for_cpu,
+ .sync_sg_for_device = pci32_sync_sg_for_device,
+};
+
+#endif /* CONFIG_PCI || CONFIG_SPARC_LEON */
+
+#ifdef CONFIG_SPARC_LEON
+struct dma_map_ops *dma_ops = &leon3_dma_ops;
+#else
+struct dma_map_ops *dma_ops = &sbus_dma_ops;
+#endif
+
+#ifdef CONFIG_SBUS
+EXPORT_SYMBOL(dma_ops);
+#endif
/*
* Return whether the given PCI device DMA address mask can be
@@ -676,6 +727,16 @@ int dma_supported(struct device *dev, u6
}
EXPORT_SYMBOL(dma_supported);
+int dma_set_mask(struct device *dev, u64 dma_mask)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type)
+ return pci_set_dma_mask(to_pci_dev(dev), dma_mask);
+#endif
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(dma_set_mask);
+
#ifdef CONFIG_PROC_FS
static int sparc_io_proc_show(struct seq_file *m, void *v)
@@ -717,7 +778,7 @@ static const struct file_operations spar
static struct resource *_sparc_find_resource(struct resource *root,
unsigned long hit)
{
- struct resource *tmp;
+ struct resource *tmp;
for (tmp = root->child; tmp != 0; tmp = tmp->sibling) {
if (tmp->start <= hit && tmp->end >= hit)

View File

@ -0,0 +1,51 @@
From 36cad96a50eb877d0c5cb3d8d93c1807ad9c774c Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 09:37:23 +0100
Subject: [PATCH] Fix kristoffers ioport.c patch for more recent kernel.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
arch/sparc/include/asm/dma-mapping.h | 13 +------------
arch/sparc/kernel/ioport.c | 3 ++-
2 files changed, 3 insertions(+), 13 deletions(-)
--- a/arch/sparc/include/asm/dma-mapping.h
+++ b/arch/sparc/include/asm/dma-mapping.h
@@ -51,17 +51,6 @@ static inline int dma_mapping_error(stru
return (dma_addr == DMA_ERROR_CODE);
}
-static inline int dma_set_mask(struct device *dev, u64 mask)
-{
-#ifdef CONFIG_PCI
- if (dev->bus == &pci_bus_type) {
- if (!dev->dma_mask || !dma_supported(dev, mask))
- return -EINVAL;
- *dev->dma_mask = mask;
- return 0;
- }
-#endif
- return -EINVAL;
-}
+extern int dma_set_mask(struct device *dev, u64 dma_mask);
#endif
--- a/arch/sparc/kernel/ioport.c
+++ b/arch/sparc/kernel/ioport.c
@@ -301,7 +301,7 @@ static void *sbus_alloc_coherent(struct
goto err_nova;
}
#endif
- res->name = op->node->name;
+ res->name = op->dev.of_node->name;
return (void *)(unsigned long)res->start;
@@ -737,6 +737,7 @@ int dma_set_mask(struct device *dev, u64
}
EXPORT_SYMBOL(dma_set_mask);
+
#ifdef CONFIG_PROC_FS
static int sparc_io_proc_show(struct seq_file *m, void *v)

View File

@ -0,0 +1,66 @@
From 2e864d18c31d4e0255269f9e9cfccc09512676c6 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 16:51:38 +0100
Subject: [PATCH] GRETH: added greth_no_gbit option
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 15 +++++++++++++--
drivers/net/greth.h | 1 +
2 files changed, 14 insertions(+), 2 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -66,6 +66,10 @@ static int greth_edcl = 1;
module_param(greth_edcl, int, 0);
MODULE_PARM_DESC(greth_edcl, "GRETH EDCL usage indicator. Set to 1 if EDCL is used.");
+static int no_gbit = 0;
+module_param(no_gbit, int, S_IRUGO);
+MODULE_PARM_DESC(no_gbit, "GRETH report GBit not supported by MAC enable. Only affects GRETH GBit MAC, default 0 (off).");
+
static int greth_open(struct net_device *dev);
static netdev_tx_t greth_start_xmit(struct sk_buff *skb,
struct net_device *dev);
@@ -1284,7 +1288,7 @@ static int greth_mdio_probe(struct net_d
}
ret = phy_connect_direct(dev, phy, &greth_link_change,
- 0, greth->gbit_mac ?
+ 0, greth->gbit_phy_support ?
PHY_INTERFACE_MODE_GMII :
PHY_INTERFACE_MODE_MII);
if (ret) {
@@ -1293,7 +1297,7 @@ static int greth_mdio_probe(struct net_d
return ret;
}
- if (greth->gbit_mac)
+ if (greth->gbit_phy_support)
phy->supported &= PHY_GBIT_FEATURES;
else
phy->supported &= PHY_BASIC_FEATURES;
@@ -1441,6 +1445,13 @@ static int __devinit greth_of_probe(stru
tmp = GRETH_REGLOAD(regs->control);
greth->gbit_mac = (tmp >> 27) & 1;
+ /* Let user skip GBit link mode by telling MDIO layer that MAC does
+ * not support GBIT (for debug) */
+ if (greth->gbit_mac && !no_gbit)
+ greth->gbit_phy_support = 1;
+ else
+ greth->gbit_phy_support = 0;
+
/* Check for multicast capability */
greth->multicast = (tmp >> 25) & 1;
--- a/drivers/net/greth.h
+++ b/drivers/net/greth.h
@@ -138,6 +138,7 @@ struct greth_private {
u8 gbit_mac;
u8 mdio_int_en;
u8 edcl;
+ u8 gbit_phy_support;
};
#endif

View File

@ -0,0 +1,41 @@
From a498d7076ee0f82ec3f508293a9cf1ccba15829e Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 16:52:19 +0100
Subject: [PATCH] GRETH: added greth_compat_mode module parameter
The greth_compat_mode option can be used to set a GRETH GBit capable MAC
in operate as if the GRETH 10/100 device was found. The GRETH GBit supports
TCP/UDP checksum offloading, unaligned frame buffers, scatter gather etc.
Enabling this mode allows the developer to test the GRETH 10/100 device
without all features mentioned above on a GBit MAC capable of the above.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -70,6 +70,11 @@ static int no_gbit = 0;
module_param(no_gbit, int, S_IRUGO);
MODULE_PARM_DESC(no_gbit, "GRETH report GBit not supported by MAC enable. Only affects GRETH GBit MAC, default 0 (off).");
+/* Use this option to enable GRETH 10/100 code on GRETH_GBIT hardware (debug legacy code option) */
+static int compat_mode = 0;
+module_param(compat_mode, int, S_IRUGO);
+MODULE_PARM_DESC(compat_mode, "GRETH 10/100 legacy mode enable. Only affects GRETH GBit MAC, default 0 (off).");
+
static int greth_open(struct net_device *dev);
static netdev_tx_t greth_start_xmit(struct sk_buff *skb,
struct net_device *dev);
@@ -1452,6 +1457,10 @@ static int __devinit greth_of_probe(stru
else
greth->gbit_phy_support = 0;
+ /* Force GBit MAC in legacy 10/100 mode (no offloading etc.) */
+ if (compat_mode == 1)
+ greth->gbit_mac = 0;
+
/* Check for multicast capability */
greth->multicast = (tmp >> 25) & 1;

View File

@ -0,0 +1,34 @@
From 6216bc809c5bdd586b14d096fbaf6dc25574b928 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 09:59:14 +0100
Subject: [PATCH] GRETH: fix opening/closing
When NAPI is disabled there is no point in having IRQs enabled, TX/RX
should be off before clearing the TX/RX descriptor rings.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -365,6 +365,8 @@ static int greth_open(struct net_device
dev_dbg(&dev->dev, " starting queue\n");
netif_start_queue(dev);
+ GRETH_REGSAVE(greth->regs->status, 0xFF);
+
napi_enable(&greth->napi);
greth_enable_irqs(greth);
@@ -380,7 +382,9 @@ static int greth_close(struct net_device
napi_disable(&greth->napi);
+ greth_disable_irqs(greth);
greth_disable_tx(greth);
+ greth_disable_rx(greth);
netif_stop_queue(dev);

View File

@ -0,0 +1,61 @@
From 544631281bed5cc37b8f2d3a99f44c9d4b97f9a8 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 10:07:12 +0100
Subject: [PATCH] GRETH: GBit transmit descriptor handling optimization
It is safe to enable all fragments before enabling the first descriptor,
this way all descriptors don't have to be processed twice, added extra
memory barrier.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 19 ++++++++++---------
1 files changed, 10 insertions(+), 9 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -512,7 +512,7 @@ greth_start_xmit_gbit(struct sk_buff *sk
greth->tx_skbuff[curr_tx] = NULL;
bdp = greth->tx_bd_base + curr_tx;
- status = GRETH_TXBD_CSALL;
+ status = GRETH_TXBD_CSALL | GRETH_BD_EN;
status |= frag->size & GRETH_BD_LEN;
/* Wrap around descriptor ring */
@@ -549,26 +549,27 @@ greth_start_xmit_gbit(struct sk_buff *sk
wmb();
- /* Enable the descriptors that we configured ... */
- for (i = 0; i < nr_frags + 1; i++) {
- bdp = greth->tx_bd_base + greth->tx_next;
- greth_write_bd(&bdp->stat, greth_read_bd(&bdp->stat) | GRETH_BD_EN);
- greth->tx_next = NEXT_TX(greth->tx_next);
- greth->tx_free--;
- }
+ /* Enable the descriptor chain by enabling the first descriptor */
+ bdp = greth->tx_bd_base + greth->tx_next;
+ greth_write_bd(&bdp->stat, greth_read_bd(&bdp->stat) | GRETH_BD_EN);
+ greth->tx_next = curr_tx;
+ greth->tx_free -= nr_frags + 1;
+
+ wmb();
greth_enable_tx(greth);
return NETDEV_TX_OK;
frag_map_error:
- /* Unmap SKB mappings that succeeded */
+ /* Unmap SKB mappings that succeeded and disable descriptor */
for (i = 0; greth->tx_next + i != curr_tx; i++) {
bdp = greth->tx_bd_base + greth->tx_next + i;
dma_unmap_single(greth->dev,
greth_read_bd(&bdp->addr),
greth_read_bd(&bdp->stat) & GRETH_BD_LEN,
DMA_TO_DEVICE);
+ greth_write_bd(&bdp->stat, 0);
}
map_error:
if (net_ratelimit())

View File

@ -0,0 +1,49 @@
From 54789a03adf9c924d0cf7b890323c9c1ca7ab042 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 10:26:09 +0100
Subject: [PATCH] GRETH: fixed skb buffer memory leak on frame errors
A new SKB buffer should not be allocated when the old SKB is reused.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 17 +++++++++++------
1 files changed, 11 insertions(+), 6 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -879,10 +879,8 @@ static int greth_rx_gbit(struct net_devi
}
}
- /* Allocate new skb to replace current */
- newskb = netdev_alloc_skb(dev, MAX_FRAME_SIZE + NET_IP_ALIGN);
-
- if (!bad && newskb) {
+ /* Allocate new skb to replace current, not needed if the current skb can be reused */
+ if (!bad && (newskb=netdev_alloc_skb(dev, MAX_FRAME_SIZE + NET_IP_ALIGN))) {
skb_reserve(newskb, NET_IP_ALIGN);
dma_addr = dma_map_single(greth->dev,
@@ -919,12 +917,19 @@ static int greth_rx_gbit(struct net_devi
if (net_ratelimit())
dev_warn(greth->dev, "Could not create DMA mapping, dropping packet\n");
dev_kfree_skb(newskb);
- dev->stats.rx_dropped++;
+ dev->stats.rx_dropped++; /* reusing current skb, so it is a drop */
}
+ } else if ( bad ) {
+ /* Bad Frame transfer, the skb is reused */
+ dev->stats.rx_dropped++;
} else {
+ /* Failed Allocating a new skb. This is rather stupid but the current "filled"
+ * skb is reused, as if transfer failure. One could argue that RX descriptor table
+ * handling should be divided into cleaning and filling as the TX part of the driver
+ */
if (net_ratelimit())
dev_warn(greth->dev, "Could not allocate SKB, dropping packet\n");
- dev->stats.rx_dropped++;
+ dev->stats.rx_dropped++; /* reusing current skb, so it is a drop */
}
status = GRETH_BD_EN | GRETH_BD_IE;

View File

@ -0,0 +1,50 @@
From c2e963057ca5635d6e5387623c3ad0c0b3123754 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 10:28:37 +0100
Subject: [PATCH] GRETH: avoid writing bad speed/duplex when setting transfer mode
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 19 ++++++++-----------
1 files changed, 8 insertions(+), 11 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -1236,29 +1236,26 @@ static void greth_link_change(struct net
struct greth_private *greth = netdev_priv(dev);
struct phy_device *phydev = greth->phy;
unsigned long flags;
-
int status_change = 0;
+ u32 ctrl;
spin_lock_irqsave(&greth->devlock, flags);
if (phydev->link) {
if ((greth->speed != phydev->speed) || (greth->duplex != phydev->duplex)) {
-
- GRETH_REGANDIN(greth->regs->control,
- ~(GRETH_CTRL_FD | GRETH_CTRL_SP | GRETH_CTRL_GB));
+ ctrl = GRETH_REGLOAD(greth->regs->control) &
+ ~(GRETH_CTRL_FD | GRETH_CTRL_SP | GRETH_CTRL_GB);
if (phydev->duplex)
- GRETH_REGORIN(greth->regs->control, GRETH_CTRL_FD);
-
- if (phydev->speed == SPEED_100) {
-
- GRETH_REGORIN(greth->regs->control, GRETH_CTRL_SP);
- }
+ ctrl |= GRETH_CTRL_FD;
+ if (phydev->speed == SPEED_100)
+ ctrl |= GRETH_CTRL_SP;
else if (phydev->speed == SPEED_1000)
- GRETH_REGORIN(greth->regs->control, GRETH_CTRL_GB);
+ ctrl |= GRETH_CTRL_GB;
+ GRETH_REGSAVE(greth->regs->control, ctrl);
greth->speed = phydev->speed;
greth->duplex = phydev->duplex;
status_change = 1;

View File

@ -0,0 +1,49 @@
From f62c047af910c9c9696db7b47472a3728b8100e8 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 11:56:22 +0100
Subject: [PATCH] GRETH: handle frame error interrupts
Not handling frame error interrupts are unlikly but may lead to dead
lock if 128 error frames are recieved in a row.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 5 +++--
drivers/net/greth.h | 2 ++
2 files changed, 5 insertions(+), 2 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -595,11 +595,12 @@ static irqreturn_t greth_interrupt(int i
status = GRETH_REGLOAD(greth->regs->status);
/* Handle rx and tx interrupts through poll */
- if (status & (GRETH_INT_RX | GRETH_INT_TX)) {
+ if (status & (GRETH_INT_RE | GRETH_INT_RX | GRETH_INT_TE | GRETH_INT_TX)) {
/* Clear interrupt status */
GRETH_REGSAVE(greth->regs->status,
- status & (GRETH_INT_RX | GRETH_INT_TX));
+ status & (GRETH_INT_RE | GRETH_INT_RX |
+ GRETH_INT_TE | GRETH_INT_TX));
retval = IRQ_HANDLED;
--- a/drivers/net/greth.h
+++ b/drivers/net/greth.h
@@ -23,6 +23,7 @@
#define GRETH_BD_LEN 0x7FF
#define GRETH_TXEN 0x1
+#define GRETH_INT_TE 0x2
#define GRETH_INT_TX 0x8
#define GRETH_TXI 0x4
#define GRETH_TXBD_STATUS 0x0001C000
@@ -35,6 +36,7 @@
#define GRETH_TXBD_ERR_UE 0x4000
#define GRETH_TXBD_ERR_AL 0x8000
+#define GRETH_INT_RE 0x1
#define GRETH_INT_RX 0x4
#define GRETH_RXEN 0x2
#define GRETH_RXI 0x8

View File

@ -0,0 +1,399 @@
From 4439d933884ee3c7e320b8d33bd2e268dd5b6fa5 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 11:40:19 +0100
Subject: [PATCH] GRETH: resolve SMP issues and other problems
Fixes the following:
1. POLL should not enable IRQ when work is not completed
2. No locking between TX descriptor cleaning and XMIT descriptor handling
3. No locking between RX POLL and XMIT modifying control register
4. Since TX cleaning (called from POLL) is running in parallel with XMIT
unnecessary locking is needed.
5. IRQ handler looks at RX frame status solely, this is wrong when IRQ is
temporarily disabled (in POLL), and when IRQ is shared.
6. IRQ handler clears IRQ status, which is unnecessary
7. TX queue was stopped in preventing cause when not MAX_SKB_FRAGS+1 descriptors
were available after a SKB been scheduled by XMIT. Instead the TX queue is
stopped first when not enough descriptors are available upon entering XMIT.
It was hard to split up this patch in smaller pieces since all are tied
together somehow.
Note the RX flag used in the interrupt handler does not signal that interrtupt
was asserted, but that a frame was received. Same goes for TX. Also, IRQ is not
asserted when the RX flag is set before enabling IRQ enable until a new frame is
received. So extra care must be taken to avoid enabling IRQ and all descriptors
are already used, hence dead lock will upon us. See new POLL implementation that
enableds IRQ then look at the RX flag to determine if one or more IRQs may have
been missed. TX/RX flags are cleared before handling previously enabled
descriptors, this ensures that the RX/TX flags are valid when determining if IRQ
should be turned on again.
By moving TX cleaning from POLL to XMIT in the standard case, removes some
locking trouble. Enabling TX cleaning from poll only when not enough TX
descriptors are available is safe because the TX queue is at the same time
stopped, thus XMIT will not be called. The TX queue is woken up again when
enough descriptrs are available.
TX Frames are always enabled with IRQ, however the TX IRQ Enable flag will not
be enabled until XMIT must wait for free descriptors.
Locking RX and XMIT parts of the driver from each other is needed because the
RX/TX enable bits share the same register.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 158 ++++++++++++++++++++++++++++++---------------------
1 files changed, 93 insertions(+), 65 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -1,7 +1,7 @@
/*
* Aeroflex Gaisler GRETH 10/100/1G Ethernet MAC.
*
- * 2005-2009 (c) Aeroflex Gaisler AB
+ * 2005-2010 (c) Aeroflex Gaisler AB
*
* This driver supports GRETH 10/100 and GRETH 10/100/1G Ethernet MACs
* available in the GRLIB VHDL IP core library.
@@ -401,12 +401,20 @@ greth_start_xmit(struct sk_buff *skb, st
struct greth_private *greth = netdev_priv(dev);
struct greth_bd *bdp;
int err = NETDEV_TX_OK;
- u32 status, dma_addr;
+ u32 status, dma_addr, ctrl;
+ unsigned long flags;
- bdp = greth->tx_bd_base + greth->tx_next;
+ /* Clean TX Ring */
+ greth_clean_tx(greth->netdev);
if (unlikely(greth->tx_free <= 0)) {
+ spin_lock_irqsave(&greth->devlock, flags); /*save from poll/irq*/
+ ctrl = GRETH_REGLOAD(greth->regs->control);
+ /* Enable TX IRQ only if not already in poll() routine */
+ if ( ctrl & GRETH_RXI )
+ GRETH_REGSAVE(greth->regs->control, ctrl | GRETH_TXI);
netif_stop_queue(dev);
+ spin_unlock_irqrestore(&greth->devlock, flags);
return NETDEV_TX_BUSY;
}
@@ -419,13 +427,14 @@ greth_start_xmit(struct sk_buff *skb, st
goto out;
}
+ bdp = greth->tx_bd_base + greth->tx_next;
dma_addr = greth_read_bd(&bdp->addr);
memcpy((unsigned char *) phys_to_virt(dma_addr), skb->data, skb->len);
dma_sync_single_for_device(greth->dev, dma_addr, skb->len, DMA_TO_DEVICE);
- status = GRETH_BD_EN | (skb->len & GRETH_BD_LEN);
+ status = GRETH_BD_EN | GRETH_BD_IE | (skb->len & GRETH_BD_LEN);
/* Wrap around descriptor ring */
if (greth->tx_next == GRETH_TXBD_NUM_MASK) {
@@ -435,22 +444,11 @@ greth_start_xmit(struct sk_buff *skb, st
greth->tx_next = NEXT_TX(greth->tx_next);
greth->tx_free--;
- /* No more descriptors */
- if (unlikely(greth->tx_free == 0)) {
-
- /* Free transmitted descriptors */
- greth_clean_tx(dev);
-
- /* If nothing was cleaned, stop queue & wait for irq */
- if (unlikely(greth->tx_free == 0)) {
- status |= GRETH_BD_IE;
- netif_stop_queue(dev);
- }
- }
-
/* Write descriptor control word and enable transmission */
greth_write_bd(&bdp->stat, status);
+ spin_lock_irqsave(&greth->devlock, flags); /*save from poll/irq*/
greth_enable_tx(greth);
+ spin_unlock_irqrestore(&greth->devlock, flags);
out:
dev_kfree_skb(skb);
@@ -463,13 +461,24 @@ greth_start_xmit_gbit(struct sk_buff *sk
{
struct greth_private *greth = netdev_priv(dev);
struct greth_bd *bdp;
- u32 status = 0, dma_addr;
+ u32 status = 0, dma_addr, ctrl;
int curr_tx, nr_frags, i, err = NETDEV_TX_OK;
+ unsigned long flags;
nr_frags = skb_shinfo(skb)->nr_frags;
+ /* Clean TX Ring */
+ greth_clean_tx_gbit(dev);
+
if (greth->tx_free < nr_frags + 1) {
+ spin_lock_irqsave(&greth->devlock, flags); /*save from poll/irq*/
+ ctrl = GRETH_REGLOAD(greth->regs->control);
+ /* Enable TX IRQ only if not already in poll() routine */
+ if ( ctrl & GRETH_RXI ) {
+ GRETH_REGSAVE(greth->regs->control, ctrl | GRETH_TXI);
+ }
netif_stop_queue(dev);
+ spin_unlock_irqrestore(&greth->devlock, flags);
err = NETDEV_TX_BUSY;
goto out;
}
@@ -522,14 +531,8 @@ greth_start_xmit_gbit(struct sk_buff *sk
/* More fragments left */
if (i < nr_frags - 1)
status |= GRETH_TXBD_MORE;
-
- /* ... last fragment, check if out of descriptors */
- else if (greth->tx_free - nr_frags - 1 < (MAX_SKB_FRAGS + 1)) {
-
- /* Enable interrupts and stop queue */
- status |= GRETH_BD_IE;
- netif_stop_queue(dev);
- }
+ else
+ status |= GRETH_BD_IE; /* enable IRQ on last fragment */
greth_write_bd(&bdp->stat, status);
@@ -557,7 +560,9 @@ greth_start_xmit_gbit(struct sk_buff *sk
wmb();
+ spin_lock_irqsave(&greth->devlock, flags); /*save from poll/irq*/
greth_enable_tx(greth);
+ spin_unlock_irqrestore(&greth->devlock, flags);
return NETDEV_TX_OK;
@@ -579,12 +584,11 @@ out:
return err;
}
-
static irqreturn_t greth_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct greth_private *greth;
- u32 status;
+ u32 status, ctrl;
irqreturn_t retval = IRQ_NONE;
greth = netdev_priv(dev);
@@ -594,13 +598,14 @@ static irqreturn_t greth_interrupt(int i
/* Get the interrupt events that caused us to be here. */
status = GRETH_REGLOAD(greth->regs->status);
- /* Handle rx and tx interrupts through poll */
- if (status & (GRETH_INT_RE | GRETH_INT_RX | GRETH_INT_TE | GRETH_INT_TX)) {
+ /* Must see if interrupts are enabled also, INT_TX|INT_RX flags may be set regardless
+ * of whether IRQ is enabled or not. Especially important when shared IRQ.
+ */
+ ctrl = GRETH_REGLOAD(greth->regs->control);
- /* Clear interrupt status */
- GRETH_REGSAVE(greth->regs->status,
- status & (GRETH_INT_RE | GRETH_INT_RX |
- GRETH_INT_TE | GRETH_INT_TX));
+ /* Handle rx and tx interrupts through poll */
+ if (((status & (GRETH_INT_RE | GRETH_INT_RX)) && (ctrl & GRETH_RXI)) ||
+ ((status & (GRETH_INT_TE | GRETH_INT_TX)) && (ctrl & GRETH_TXI))) {
retval = IRQ_HANDLED;
@@ -625,6 +630,8 @@ static void greth_clean_tx(struct net_de
while (1) {
bdp = greth->tx_bd_base + greth->tx_last;
+ GRETH_REGSAVE(greth->regs->status, GRETH_INT_TE | GRETH_INT_TX);
+ mb();
stat = greth_read_bd(&bdp->stat);
if (unlikely(stat & GRETH_BD_EN))
@@ -685,7 +692,10 @@ static void greth_clean_tx_gbit(struct n
/* We only clean fully completed SKBs */
bdp_last_frag = greth->tx_bd_base + SKIP_TX(greth->tx_last, nr_frags);
- stat = bdp_last_frag->stat;
+
+ GRETH_REGSAVE(greth->regs->status, GRETH_INT_TE | GRETH_INT_TX);
+ mb();
+ stat = greth_read_bd(&bdp_last_frag->stat);
if (stat & GRETH_BD_EN)
break;
@@ -717,23 +727,12 @@ static void greth_clean_tx_gbit(struct n
greth->tx_free += nr_frags+1;
dev_kfree_skb(skb);
}
- if (greth->tx_free > (MAX_SKB_FRAGS + 1)) {
+
+ if (netif_queue_stopped(dev) && (greth->tx_free > (MAX_SKB_FRAGS + 1))) {
netif_wake_queue(dev);
}
}
-static int greth_pending_packets(struct greth_private *greth)
-{
- struct greth_bd *bdp;
- u32 status;
- bdp = greth->rx_bd_base + greth->rx_cur;
- status = greth_read_bd(&bdp->stat);
- if (status & GRETH_BD_EN)
- return 0;
- else
- return 1;
-}
-
static int greth_rx(struct net_device *dev, int limit)
{
struct greth_private *greth;
@@ -742,20 +741,24 @@ static int greth_rx(struct net_device *d
int pkt_len;
int bad, count;
u32 status, dma_addr;
+ unsigned long flags;
greth = netdev_priv(dev);
for (count = 0; count < limit; ++count) {
bdp = greth->rx_bd_base + greth->rx_cur;
+ GRETH_REGSAVE(greth->regs->status, GRETH_INT_RE | GRETH_INT_RX);
+ mb();
status = greth_read_bd(&bdp->stat);
- dma_addr = greth_read_bd(&bdp->addr);
- bad = 0;
if (unlikely(status & GRETH_BD_EN)) {
break;
}
+ dma_addr = greth_read_bd(&bdp->addr);
+ bad = 0;
+
/* Check status for errors. */
if (unlikely(status & GRETH_RXBD_STATUS)) {
if (status & GRETH_RXBD_ERR_FT) {
@@ -817,7 +820,9 @@ static int greth_rx(struct net_device *d
dma_sync_single_for_device(greth->dev, dma_addr, MAX_FRAME_SIZE, DMA_FROM_DEVICE);
+ spin_lock_irqsave(&greth->devlock, flags); /* save from XMIT */
greth_enable_rx(greth);
+ spin_unlock_irqrestore(&greth->devlock, flags);
greth->rx_cur = NEXT_RX(greth->rx_cur);
}
@@ -851,6 +856,7 @@ static int greth_rx_gbit(struct net_devi
int pkt_len;
int bad, count = 0;
u32 status, dma_addr;
+ unsigned long flags;
greth = netdev_priv(dev);
@@ -858,6 +864,8 @@ static int greth_rx_gbit(struct net_devi
bdp = greth->rx_bd_base + greth->rx_cur;
skb = greth->rx_skbuff[greth->rx_cur];
+ GRETH_REGSAVE(greth->regs->status, GRETH_INT_RE | GRETH_INT_RX);
+ mb();
status = greth_read_bd(&bdp->stat);
bad = 0;
@@ -940,7 +948,9 @@ static int greth_rx_gbit(struct net_devi
wmb();
greth_write_bd(&bdp->stat, status);
+ spin_lock_irqsave(&greth->devlock, flags);
greth_enable_rx(greth);
+ spin_unlock_irqrestore(&greth->devlock, flags);
greth->rx_cur = NEXT_RX(greth->rx_cur);
}
@@ -952,15 +962,19 @@ static int greth_poll(struct napi_struct
{
struct greth_private *greth;
int work_done = 0;
+ unsigned long flags;
+ u32 mask, ctrl;
greth = container_of(napi, struct greth_private, napi);
- if (greth->gbit_mac) {
- greth_clean_tx_gbit(greth->netdev);
- } else {
- greth_clean_tx(greth->netdev);
+restart_txrx_poll:
+ if ( netif_queue_stopped(greth->netdev) ) {
+ if (greth->gbit_mac) {
+ greth_clean_tx_gbit(greth->netdev);
+ } else {
+ greth_clean_tx(greth->netdev);
+ }
}
-restart_poll:
if (greth->gbit_mac) {
work_done += greth_rx_gbit(greth->netdev, budget - work_done);
} else {
@@ -969,15 +983,29 @@ restart_poll:
if (work_done < budget) {
- napi_complete(napi);
+ spin_lock_irqsave(&greth->devlock, flags);
- if (greth_pending_packets(greth)) {
- napi_reschedule(napi);
- goto restart_poll;
+ ctrl = GRETH_REGLOAD(greth->regs->control);
+ if (netif_queue_stopped(greth->netdev)) {
+ GRETH_REGSAVE(greth->regs->control,
+ ctrl | GRETH_TXI | GRETH_RXI);
+ mask = GRETH_INT_RX | GRETH_INT_RE |
+ GRETH_INT_TX | GRETH_INT_TE;
+ } else {
+ GRETH_REGSAVE(greth->regs->control, ctrl | GRETH_RXI);
+ mask = GRETH_INT_RX | GRETH_INT_RE;
+ }
+
+ if (GRETH_REGLOAD(greth->regs->status) & mask) {
+ GRETH_REGSAVE(greth->regs->control, ctrl);
+ spin_unlock_irqrestore(&greth->devlock, flags);
+ goto restart_txrx_poll;
+ } else {
+ __napi_complete(napi);
+ spin_unlock_irqrestore(&greth->devlock, flags);
}
}
- greth_enable_irqs(greth);
return work_done;
}
@@ -1172,11 +1200,11 @@ static const struct ethtool_ops greth_et
};
static struct net_device_ops greth_netdev_ops = {
- .ndo_open = greth_open,
- .ndo_stop = greth_close,
- .ndo_start_xmit = greth_start_xmit,
- .ndo_set_mac_address = greth_set_mac_add,
- .ndo_validate_addr = eth_validate_addr,
+ .ndo_open = greth_open,
+ .ndo_stop = greth_close,
+ .ndo_start_xmit = greth_start_xmit,
+ .ndo_set_mac_address = greth_set_mac_add,
+ .ndo_validate_addr = eth_validate_addr,
};
static inline int wait_for_mdio(struct greth_private *greth)

View File

@ -0,0 +1,25 @@
From 66d5b37cc6d32291419bf99161e83e2946ea5f25 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 12:11:03 +0100
Subject: [PATCH] GRETH: added option to disable a device node from bootloader.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
drivers/net/greth.c | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -1428,6 +1428,12 @@ static int __devinit greth_of_probe(stru
int err;
int tmp;
unsigned long timeout;
+ int *ampopts;
+
+ /* Skip device if used by another OS instance */
+ ampopts = (int *) of_get_property(ofdev->dev.of_node, "ampopts", NULL);
+ if (ampopts && (*ampopts == 0))
+ return -EIO;
dev = alloc_etherdev(sizeof(struct greth_private));

View File

@ -0,0 +1,31 @@
From 50fcb51c42bc721f18f1bfa10f705519cd344a2a Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Wed, 1 Dec 2010 16:20:01 +0100
Subject: [PATCH] GRETH: Newer GBit MACs need setting EE bit when EDCL should be enabled
---
drivers/net/greth.c | 2 +-
drivers/net/greth.h | 1 +
2 files changed, 2 insertions(+), 1 deletions(-)
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -1511,7 +1511,7 @@ static int __devinit greth_of_probe(stru
/* If we have EDCL we disable the EDCL speed-duplex FSM so
* it doesn't interfere with the software */
if (greth->edcl != 0)
- GRETH_REGORIN(regs->control, GRETH_CTRL_DISDUPLEX);
+ GRETH_REGORIN(regs->control, GRETH_CTRL_DISDUPLEX|GRETH_CTRL_EE);
/* Check if MAC can handle MDIO interrupts */
greth->mdio_int_en = (tmp >> 26) & 1;
--- a/drivers/net/greth.h
+++ b/drivers/net/greth.h
@@ -15,6 +15,7 @@
#define GRETH_CTRL_PSTATIEN 0x400
#define GRETH_CTRL_MCEN 0x800
#define GRETH_CTRL_DISDUPLEX 0x1000
+#define GRETH_CTRL_EE 0x4000
#define GRETH_STATUS_PHYSTAT 0x100
#define GRETH_BD_EN 0x800