Skip to contentSkip to conversation

AR7: core support

PATCH In review Matteo Croce 6 participants 16 messages Sep 8, 2007 – Sep 13, 2007 1 message not in the archive

Subsystems (7)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

#1 lore raw

PATCH 1/7 AR7: core support

Support for memory mapping, clock and the vlynq bus

arch/mips/Kconfig +14 -0
index 4a54d21..1bc65f0 100644
@@ -15,6 +15,20 @@ choice
 	prompt "System type"
 	default SGI_IP22
 
+config AR7
+	bool "Texas Instruments AR7"
+	select BOOT_ELF32
+	select DMA_NONCOHERENT
+	select HW_HAS_PCI
+	select IRQ_CPU
+	select SWAP_IO_SPACE
+	select SYS_HAS_CPU_MIPS32_R1
+	select SYS_HAS_EARLY_PRINTK
+	select SYS_SUPPORTS_32BIT_KERNEL
+	select SYS_SUPPORTS_KGDB
+	select SYS_SUPPORTS_LITTLE_ENDIAN
+	select GENERIC_GPIO
+
 config MACH_ALCHEMY
 	bool "Alchemy processor based machines"
 
arch/mips/Makefile +7 -0
index 32c1c8f..3989a8b 100644
@@ -161,6 +161,13 @@ libs-$(CONFIG_SIBYTE_CFE)	+= arch/mips/sibyte/cfe/
 #
 
 #
+# Texas Instruments AR7
+#
+core-$(CONFIG_AR7)		+= arch/mips/ar7/
+cflags-$(CONFIG_AR7)		+= -Iinclude/asm-mips/ar7
+load-$(CONFIG_AR7)		+= 0xffffffff94100000
+
+#
 # Acer PICA 61, Mips Magnum 4000 and Olivetti M700.
 #
 core-$(CONFIG_MACH_JAZZ)	+= arch/mips/jazz/
arch/mips/ar7/Makefile new file +13 -0
new file mode 100644
index 0000000..e6ba02c
@@ -0,0 +1,13 @@
+
+obj-y := \
+	prom.o \
+	setup.o \
+	memory.o \
+	irq.o \
+	time.o \
+	platform.o \
+	gpio.o \
+	clock.o \
+	vlynq.o
+
+EXTRA_AFLAGS := $(CFLAGS)
arch/mips/ar7/clock.c new file +491 -0

491 lines not on this page · raw message

arch/mips/ar7/gpio.c new file +55 -0

55 lines not on this page · raw message

arch/mips/ar7/irq.c new file +203 -0

203 lines not on this page · raw message

arch/mips/ar7/memory.c new file +72 -0

72 lines not on this page · raw message

arch/mips/ar7/platform.c new file +408 -0

408 lines not on this page · raw message

arch/mips/ar7/prom.c new file +320 -0

320 lines not on this page · raw message

arch/mips/ar7/setup.c new file +119 -0

119 lines not on this page · raw message

arch/mips/ar7/time.c new file +46 -0

46 lines not on this page · raw message

arch/mips/ar7/vlynq.c new file +554 -0

554 lines not on this page · raw message

arch/mips/kernel/traps.c +14 -0
index 6379003..75a46ba 100644
@@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
 
 	exception_handlers[n] = handler;
 	if (n == 0 && cpu_has_divec) {
+#ifdef CONFIG_AR7
+		/* lui k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x200) =
+				0x3c1a0000 | (handler >> 16);
+		/* ori k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x204) =
+				0x375a0000 | (handler & 0xffff);
+		/* jr k0 */
+		*(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
+		/* nop */
+		*(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
+		flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
+#else
 		*(volatile u32 *)(ebase + 0x200) = 0x08000000 |
 		                                 (0x03ffffff & (handler >> 2));
 		flush_icache_range(ebase + 0x200, ebase + 0x204);
+#endif
 	}
 	return (void *)old_handler;
 }
include/asm-mips/ar7/ar7.h new file +168 -0

168 lines not on this page · raw message

include/asm-mips/ar7/gpio.h new file +120 -0

120 lines not on this page · raw message

include/asm-mips/ar7/mmzone.h new file +27 -0
new file mode 100644
index 0000000..2789689
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 Felix Fietkau, Eugene Konev
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _ASM_MACH_MMZONE_H
+#define _ASM_MACH_MMZONE_H
+
+extern pg_data_t __node_data[];
+#define NODE_DATA(nid)          (&__node_data[nid])
+#define NODE_MEM_MAP(nid)       (NODE_DATA(nid)->node_mem_map)
+#define pa_to_nid(addr) (((addr) >= ARCH_PFN_OFFSET << PAGE_SHIFT) ? 0 : -1)
+
+#endif /* _ASM_MACH_MMZONE_H */
include/asm-mips/ar7/prom.h new file +25 -0
new file mode 100644
index 0000000..62d7d5c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2006, 2007 Florian Fainelli <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __PROM_H__
+#define __PROM_H__
+
+extern char *prom_getenv(char *name);
+extern void prom_printf(char *fmt, ...);
+
+#endif /* __PROM_H__ */
include/asm-mips/ar7/spaces.h new file +32 -0
new file mode 100644
index 0000000..f4d1237
@@ -0,0 +1,32 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994 - 1999, 2000, 03, 04 Ralf Baechle
+ * Copyright (C) 2000, 2002  Maciej W. Rozycki
+ * Copyright (C) 1990, 1999, 2000 Silicon Graphics, Inc.
+ */
+#ifndef _ASM_AR7_SPACES_H
+#define _ASM_AR7_SPACES_H
+
+#define CAC_BASE		0x80000000
+#define IO_BASE			0xa0000000
+#define UNCAC_BASE		0xa0000000
+#define MAP_BASE		0xc0000000
+
+/*
+ * This handles the memory map.
+ * We handle pages at KSEG0 for kernels with 32 bit address space.
+ */
+#define PAGE_OFFSET		0x94000000UL
+#define PHYS_OFFSET		0x14000000UL
+
+/*
+ * Memory above this physical address will be considered highmem.
+ */
+#ifndef HIGHMEM_START
+#define HIGHMEM_START		0x40000000UL
+#endif
+
+#endif /* __ASM_AR7_SPACES_H */
include/asm-mips/ar7/vlynq.h new file +90 -0

90 lines not on this page · raw message

#2 lore raw
Quoted 1 line · Matteo Croce

Support for memory mapping, clock and the vlynq bus

Just some random comments.

Quoted 9 lines
arch/mips/ar7/Makefile new file +3 -0
new file mode 100644
index 0000000..e6ba02c
@@ -0,0 +1,13 @@
+
+obj-y := \
+	prom.o \

arch/mips/ar7/Makefile line 13

Quoted 10 lines
arch/mips/ar7/Makefile new file +10 -0
+	setup.o \
+	memory.o \
+	irq.o \
+	time.o \
+	platform.o \
+	gpio.o \
+	clock.o \
+	vlynq.o
+
+EXTRA_AFLAGS := $(CFLAGS)

This EXTRA_AFLAGS line is redundant. You can drop it safely.

Quoted 5 lines
arch/mips/ar7/irq.c new file +0 -0
new file mode 100644
index 0000000..f131301

...

arch/mips/ar7/irq.c line 22

Quoted 4 lines
arch/mips/ar7/irq.c new file +4 -0
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>

No need to include <linux/irq.h>

Quoted 1 line

+#define REG(addr) (*(volatile u32 *)(KSEG1ADDR(AR7_REGS_IRQ) + addr))

You can remove this "volatile" rewriting something like:

#define REG_RD(addr) \
	readl((void __iomem *)(KSEG1ADDR(AR7_REGS_IRQ) + (addr)))
#define REG_WR(val, addr) \
	writel(val, (void __iomem *)(KSEG1ADDR(AR7_REGS_IRQ) + (addr)))

arch/mips/ar7/irq.c line 60

Quoted 2 lines
arch/mips/ar7/irq.c new file +2 -0
+static struct irq_chip ar7_irq_type = {
+	.typename = "AR7",

The typename is obsolete and not needed if you have name field.

Quoted 25 lines
arch/mips/ar7/irq.c new file +25 -0
+static void ar7_unmask_irq(unsigned int irq)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	/* enable the interrupt channel  bit */
+	REG(ESR_OFFSET(irq)) = 1 << ((irq - ar7_irq_base) % 32);
+	local_irq_restore(flags);
+}
+
+static void ar7_mask_irq(unsigned int irq)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	/* disable the interrupt channel bit */
+	REG(ECR_OFFSET(irq)) = 1 << ((irq - ar7_irq_base) % 32);
+	local_irq_restore(flags);
+}
+
+static void ar7_unmask_secondary_irq(unsigned int irq)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	/* enable the interrupt channel  bit */
+	REG(SEC_ESR_OFFSET) = 1 << (irq - ar7_irq_base - 40);
+	local_irq_restore(flags);

arch/mips/ar7/irq.c line 116

Quoted 10 lines
arch/mips/ar7/irq.c new file +10 -0
+}
+
+static void ar7_mask_secondary_irq(unsigned int irq)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	/* disable the interrupt channel bit */
+	REG(SEC_ECR_OFFSET) = 1 << (irq - ar7_irq_base - 40);
+	local_irq_restore(flags);
+}

The mask, unmask routines are always called irq disabled. So you can drop these local_irqsave/restore pairs.

Quoted 5 lines
arch/mips/ar7/irq.c new file +5 -0
+	for (i = 0; i < 40; i++) {
+		REG(CHNL_OFFSET(i)) = i;
+		/* Primary IRQ's */
+		irq_desc[i + base].status = IRQ_DISABLED;
+		irq_desc[i + base].action = NULL;

arch/mips/ar7/irq.c line 152

Quoted 10 lines
arch/mips/ar7/irq.c new file +10 -0
+		irq_desc[i + base].depth = 1;
+		irq_desc[i + base].chip = &ar7_irq_type;
+		/* Secondary IRQ's */
+		if (i < 32) {
+			irq_desc[i + base + 40].status = IRQ_DISABLED;
+			irq_desc[i + base + 40].action = NULL;
+			irq_desc[i + base + 40].depth = 1;
+			irq_desc[i + base + 40].chip = &ar7_secondary_irq_type;
+		}
+	}

Use set_irq_chip() or its variants instead of touching irq_desc[] directly. It seems this platform can set GENERIC_HARDIRQS_NO__DO_IRQ in Kconfig, using handle_level_irq irq handler.

arch/mips/ar7/irq.c line 203

Quoted 10 lines
arch/mips/ar7/irq.c new file +10 -0
+asmlinkage void plat_irq_dispatch(void)
+{
+	unsigned int pending = read_c0_status() & read_c0_cause();
+	if (pending & STATUSF_IP7)		/* cpu timer */
+		do_IRQ(7);
+	else if (pending & STATUSF_IP2)		/* int0 hardware line */
+		do_IRQ(2);
+	else
+		spurious_interrupt();
+}

It would be safer to mask 'pending' with ST0_IM.

Quoted 3 lines
arch/mips/ar7/prom.c new file +0 -0
new file mode 100644
index 0000000..0437f65

...

Quoted 11 lines
arch/mips/ar7/prom.c new file +11 -0
+/* from adm5120/prom.c */
+void prom_printf(char *fmt, ...)
+{
+	va_list args;
+	int l;
+	char *p, *buf_end;
+	char buf[1024];
+
+	va_start(args, fmt);
+	l = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf) */
+	va_end(args);

arch/mips/ar7/prom.c line 308

Quoted 10 lines
arch/mips/ar7/prom.c new file +10 -0
+
+	buf_end = buf + l;
+
+	for (p = buf; p < buf_end; p++) {
+		/* Crude cr/nl handling is better than none */
+		if (*p == '\n')
+			prom_putchar('\r');
+		prom_putchar(*p);
+	}
+}

prom_printf() is not used in recent linux-mips.

Quoted 5 lines
arch/mips/ar7/time.c new file +0 -0
new file mode 100644
index 0000000..fea75c1

...

Quoted 4 lines
arch/mips/ar7/time.c new file +4 -0
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>

arch/mips/ar7/time.c line 34

Quoted 10 lines
arch/mips/ar7/time.c new file +10 -0
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/mc146818rtc.h>
+#include <linux/ptrace.h>
+#include <linux/hardirq.h>
+#include <linux/irq.h>
+#include <linux/cpu.h>
+#include <asm/time.h>

You do not have to include so many headers.

Quoted 5 lines
arch/mips/ar7/vlynq.c new file +0 -0
new file mode 100644
index 0000000..dd4796e

...

Quoted 5 lines
arch/mips/ar7/vlynq.c new file +5 -0
+struct vlynq_regs {
+	volatile u32 revision;
+	volatile u32 control;
+	volatile u32 status;
+	volatile u32 int_prio;

arch/mips/ar7/vlynq.c line 74

Quoted 10 lines
arch/mips/ar7/vlynq.c new file +10 -0
+	volatile u32 int_status;
+	volatile u32 int_pending;
+	volatile u32 int_ptr;
+	volatile u32 tx_offset;
+	volatile struct vlynq_mapping rx_mapping[4];
+	volatile u32 chip;
+	volatile u32 autonego;
+	volatile u32 unused[6];
+	volatile u32 int_device[8];
+} __attribute__ ((packed));

You can drop these volatile, using readl/writel for accessing these members.

arch/mips/ar7/vlynq.c line 117

Quoted 4 lines
arch/mips/ar7/vlynq.c new file +4 -0
+static void vlynq_irq_unmask(unsigned int irq)
+{
+	volatile u32 val;
+	struct vlynq_device *dev = irq_desc[irq].chip_data;

This "volatile" should be removed.
Use get_irq_chip_data(irq) instead of using irq_desc[] directly.

arch/mips/ar7/vlynq.c line 129

Quoted 3 lines
arch/mips/ar7/vlynq.c new file +3 -0
+static void vlynq_irq_mask(unsigned int irq)
+{
+	volatile u32 val;

This "volatile" should be removed.

arch/mips/ar7/vlynq.c line 142

Quoted 3 lines
arch/mips/ar7/vlynq.c new file +3 -0
+static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
+{
+	volatile u32 val;

This "volatile" should be removed.

arch/mips/ar7/vlynq.c line 202

Quoted 2 lines
arch/mips/ar7/vlynq.c new file +2 -0
+static struct irq_chip vlynq_irq_chip = {
+	.typename = "VLYNQ",

The typename is obsolete and not needed if you have name field.

arch/mips/ar7/vlynq.c line 241

Quoted 10 lines
arch/mips/ar7/vlynq.c new file +10 -0
+	for (i = 0; i < PER_DEVICE_IRQS; i++) {
+		if ((i == dev->local_irq) || (i == dev->remote_irq))
+			continue;
+		irq_desc[dev->irq_start + i].status = IRQ_DISABLED;
+		irq_desc[dev->irq_start + i].action = 0;
+		irq_desc[dev->irq_start + i].depth = 1;
+		irq_desc[dev->irq_start + i].chip = &vlynq_irq_chip;
+		irq_desc[dev->irq_start + i].chip_data = dev;
+		dev->remote->int_device[i >> 2] = 0;
+	}

Use set_irq_chip() or its variants.

arch/mips/ar7/vlynq.c line 435

Quoted 7 lines
arch/mips/ar7/vlynq.c new file +7 -0
+	dev = kmalloc(sizeof(struct vlynq_device), GFP_KERNEL);
+	if (!dev) {
+		printk(KERN_ERR "vlynq: failed to allocate device structure\n");
+		return -ENOMEM;
+	}
+
+	memset(dev, 0, sizeof(struct vlynq_device));

Use kzalloc().

Quoted 1 line

+int __init vlynq_init(void)

This can be static.

Quoted 18 lines
arch/mips/kernel/traps.c +10 -0
index 6379003..75a46ba 100644
@@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
 
 	exception_handlers[n] = handler;
 	if (n == 0 && cpu_has_divec) {
+#ifdef CONFIG_AR7
+		/* lui k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x200) =
+				0x3c1a0000 | (handler >> 16);
+		/* ori k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x204) =
+				0x375a0000 | (handler & 0xffff);
+		/* jr k0 */
+		*(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
+		/* nop */

arch/mips/kernel/traps.c void *set_except_vector(int n, void *addr) · line 1097

Quoted 10 lines
arch/mips/kernel/traps.c +4 -0
+		*(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
+		flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
+#else
 		*(volatile u32 *)(ebase + 0x200) = 0x08000000 |
 		                                 (0x03ffffff & (handler >> 2));
 		flush_icache_range(ebase + 0x200, ebase + 0x204);
+#endif
 	}
 	return (void *)old_handler;
 }

Runtime checking, something like this would be better than ifdef:

	if ((handler ^ (ebase + 4)) & 0xfc000000)
		/* use jr */
		...
	} else {
		/* use j */
		...
	}

---
Atsushi Nemoto

#3 lore raw

Il Saturday 08 September 2007 19:40:20 Atsushi Nemoto ha scritto:

Quoted 31 lines
Quoted 18 lines
arch/mips/kernel/traps.c +10 -0
index 6379003..75a46ba 100644
@@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
 
 	exception_handlers[n] = handler;
 	if (n == 0 && cpu_has_divec) {
+#ifdef CONFIG_AR7
+		/* lui k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x200) =
+				0x3c1a0000 | (handler >> 16);
+		/* ori k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x204) =
+				0x375a0000 | (handler & 0xffff);
+		/* jr k0 */
+		*(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
+		/* nop */

arch/mips/kernel/traps.c void *set_except_vector(int n, void *addr) · line 1097

Quoted 10 lines
arch/mips/kernel/traps.c +4 -0
+		*(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
+		flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
+#else
 		*(volatile u32 *)(ebase + 0x200) = 0x08000000 |
 		                                 (0x03ffffff & (handler >> 2));
 		flush_icache_range(ebase + 0x200, ebase + 0x204);
+#endif
 	}
 	return (void *)old_handler;
 }

Runtime checking, something like this would be better than ifdef:

Quoted 7 lines
	if ((handler ^ (ebase + 4)) & 0xfc000000)
		/* use jr */
		...
	} else {
		/* use j */
		...
	}

This will not make the code bigger? What's wrong with #ifdef?

#4 lore raw
Quoted 9 lines · Matteo Croce
Quoted 9 lines

arch/mips/kernel/traps.c void *set_except_vector(int n, void *addr) · line 1080

Quoted 9 lines
arch/mips/kernel/traps.c +3 -0
@@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
 
 	exception_handlers[n] = handler;
 	if (n == 0 && cpu_has_divec) {
+#ifdef CONFIG_AR7
+		/* lui k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x200) =

s/CAC_BASE/ebase/

Quoted 9 lines
Quoted 9 lines

arch/mips/kernel/traps.c void *set_except_vector(int n, void *addr) · line 1089

Quoted 9 lines
arch/mips/kernel/traps.c +9 -0
+				0x3c1a0000 | (handler >> 16);
+		/* ori k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x204) =
+				0x375a0000 | (handler & 0xffff);
+		/* jr k0 */
+		*(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
+		/* nop */
+		*(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
+		flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);

All these volatile keywords are unnecessary btw.

You may want to read Documentation/volatile-considered-harmful.txt on why volatile is almost always a bad idea.

Quoted 3 lines
Quoted 3 lines

arch/mips/kernel/traps.c void *set_except_vector(int n, void *addr) · line 1092

Quoted 3 lines
arch/mips/kernel/traps.c +1 -0
+#else
 		*(volatile u32 *)(ebase + 0x200) = 0x08000000 |
 		                                 (0x03ffffff & (handler >> 2));

Just like this one, so I will remove it now.

Quoted 8 lines
Quoted 8 lines

arch/mips/kernel/traps.c void *set_except_vector(int n, void *addr) · line 1097

Quoted 5 lines
arch/mips/kernel/traps.c +1 -0
 		flush_icache_range(ebase + 0x200, ebase + 0x204);
+#endif
 	}
 	return (void *)old_handler;
 }

Runtime checking, something like this would be better than ifdef:

Quoted 8 lines
Quoted 7 lines
	if ((handler ^ (ebase + 4)) & 0xfc000000)
		/* use jr */
		...
	} else {
		/* use j */
		...
	}

This will not make the code bigger?

It will by a miniscule amount. Which hardly matters because the function is (whops, should be ...) __init code anyway.

Quoted 1 line

What's wrong with #ifdef?

#ifdef makes for harder to read code (To paraphrase Linus - the kernel is write once and read 10 million times) , is less flexible and has a tendence to hide bugs in the deactivated part. So generally avoid.

And actually in this specific case it also should be a runtime decission simply because in the not so distant future the will be hardware which will simply need that.

Ralf

#5 lore raw
In reply to Matteo Croce
Quoted 32 lines · Matteo Croce

Il Saturday 08 September 2007 19:40:20 Atsushi Nemoto ha scritto:

Quoted 31 lines
Quoted 18 lines
arch/mips/kernel/traps.c +10 -0
index 6379003..75a46ba 100644
@@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
 
 	exception_handlers[n] = handler;
 	if (n == 0 && cpu_has_divec) {
+#ifdef CONFIG_AR7
+		/* lui k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x200) =
+				0x3c1a0000 | (handler >> 16);
+		/* ori k0, 0x0000 */
+		*(volatile u32 *)(CAC_BASE+0x204) =
+				0x375a0000 | (handler & 0xffff);
+		/* jr k0 */
+		*(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
+		/* nop */

arch/mips/kernel/traps.c void *set_except_vector(int n, void *addr) · line 1097

Quoted 10 lines
arch/mips/kernel/traps.c +4 -0
+		*(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
+		flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
+#else
 		*(volatile u32 *)(ebase + 0x200) = 0x08000000 |
 		                                 (0x03ffffff & (handler >> 2));
 		flush_icache_range(ebase + 0x200, ebase + 0x204);
+#endif
 	}
 	return (void *)old_handler;
 }

Runtime checking, something like this would be better than ifdef:

Quoted 8 lines
Quoted 7 lines
	if ((handler ^ (ebase + 4)) & 0xfc000000)
		/* use jr */
		...
	} else {
		/* use j */
		...
	}

This will not make the code bigger? What's wrong with #ifdef?

probably nothing, but having a generic decision whether we need a version with j or jr will help other platforms as well. Why make AR7 a special case ?

Thomas.

Signature · 2 lines
Crap can work. Given enough thrust pigs will fly, but it's not necessary a
good idea.                                                [ RFC1925, 2.3 ]
#6 lore raw
Quoted 3 lines · Thomas Bogendoerfer

probably nothing, but having a generic decision whether we need a version with j or jr will help other platforms as well. Why make AR7 a special case ?

The destination is potencially outside the range of the j instruction. Which is something that reasonably be expected on other systems as well. One scenario for example is having a few k of RAM at phys 0 then the rest starting at 256MB phys.

It's somewhat easier with an ebase register of course but older cores don't have that.

Ralf

#7 lore raw

PATCH 2/7 AR7: mtd partition map

In reply to a message not in the archive

Partition map support

drivers/mtd/Kconfig +6 -0
index fbec8cd..c1b2508 100644
@@ -150,6 +150,12 @@ config MTD_AFS_PARTS
 	  for your particular device. It won't happen automatically. The
 	  'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example.
 
+config MTD_AR7_PARTS
+	tristate "TI AR7 partitioning support"
+	depends on MTD_PARTITIONS
+	---help---
+	  TI AR7 partitioning support
+
 comment "User Modules And Translation Layers"
 
 config MTD_CHAR
drivers/mtd/Makefile +1 -0
index 6d958a4..8451c64 100644
@@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT)	+= mtdconcat.o
 obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
 obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
 obj-$(CONFIG_MTD_AFS_PARTS)	+= afs.o
+obj-$(CONFIG_MTD_AR7_PARTS)	+= ar7part.o
 
 # 'Users' - code which presents functionality to userspace.
 obj-$(CONFIG_MTD_CHAR)		+= mtdchar.o
drivers/mtd/ar7part.c new file +140 -0

140 lines not on this page · raw message

#8 lore raw

PATCH 3/7 AR7: gpio char device

In reply to a message not in the archive

Char device to access GPIO pins

drivers/char/Kconfig +9 -0
index b391776..d56cfd7 100644
@@ -928,6 +928,15 @@ config MWAVE
 	  To compile this driver as a module, choose M here: the
 	  module will be called mwave.
 
+config AR7_GPIO
+	tristate "TI AR7 GPIO Support"
+	depends on AR7
+	help
+	  Give userspace access to the GPIO pins on the Texas Instruments AR7
+	  processors.
+
+	  If compiled as a module, it will be called ar7_gpio.
+
 config SCx200_GPIO
 	tristate "NatSemi SCx200 GPIO Support"
 	depends on SCx200
drivers/char/Makefile +1 -0
index d68ddbe..804319e 100644
@@ -89,6 +89,7 @@ obj-$(CONFIG_COBALT_LCD)	+= lcd.o
 obj-$(CONFIG_PPDEV)		+= ppdev.o
 obj-$(CONFIG_NWBUTTON)		+= nwbutton.o
 obj-$(CONFIG_NWFLASH)		+= nwflash.o
+obj-$(CONFIG_AR7_GPIO)		+= ar7_gpio.o
 obj-$(CONFIG_SCx200_GPIO)	+= scx200_gpio.o
 obj-$(CONFIG_PC8736x_GPIO)	+= pc8736x_gpio.o
 obj-$(CONFIG_NSC_GPIO)		+= nsc_gpio.o
drivers/char/ar7_gpio.c new file +158 -0

158 lines not on this page · raw message

#9 lore raw

PATCH 4/7 AR7: leds driver

In reply to a message not in the archive

Support for the leds in front of the board usually used to show power status, network traffic, connected eth devices etc.

drivers/leds/Kconfig +6 -0
index 4468cb3..b1c7a32 100644
@@ -18,6 +18,12 @@ config LEDS_CLASS
 
 comment "LED drivers"
 
+config LEDS_AR7
+	tristate "LED Support for the TI AR7"
+	depends LEDS_CLASS && AR7
+	help
+	  This option enables support for the LEDs on TI AR7.
+
 config LEDS_CORGI
 	tristate "LED Support for the Sharp SL-C7x0 series"
 	depends on LEDS_CLASS && PXA_SHARP_C7xx
drivers/leds/Makefile +1 -0
index f8995c9..6d78192 100644
@@ -5,6 +5,7 @@ obj-$(CONFIG_LEDS_CLASS)		+= led-class.o
 obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
 
 # LED Platform Drivers
+obj-$(CONFIG_LEDS_AR7)			+= leds-ar7.o
 obj-$(CONFIG_LEDS_CORGI)		+= leds-corgi.o
 obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
 obj-$(CONFIG_LEDS_SPITZ)		+= leds-spitz.o
drivers/leds/leds-ar7.c new file +130 -0

130 lines not on this page · raw message

#10 lore raw

Re: [PATCH][MIPS][4/7] AR7: leds driver

Quoted 5 lines · Matteo Croce

Support for the leds in front of the board usually used to show power status, network traffic, connected eth devices etc.

The usual approach to drivers like this is to add the device definition (ar7_leds_device) to the platform specific code which is probably in arch/mips/ somewhere? See arch/arm/mach-pxa/{corgi|poodle|spitz}.c for example.

Also, does MIPS have any kind of generic GPIO framework? A quick look at the code suggests you might be able to use drivers/leds/leds-gpio.c although you might need to add a definition of gpio_set_value_cansleep() since MIPS appears to lack it.

Regards,

Richard
(LED Maintainer)

#11 lore raw

PATCH 5/7 AR7: watchdog timer

In reply to a message not in the archive

Driver for the watchdog timer. It worked with 2.4, doesn't does with 2.6. Apart that it doesn't reboots the device it works :)

drivers/char/watchdog/Kconfig +6 -0
index 37bddc1..78d4940 100644
@@ -583,6 +583,12 @@ config SBC_EPX_C3_WATCHDOG
 
 # MIPS Architecture
 
+config AR7_WDT
+	tristate "TI AR7 Watchdog Timer"
+	depends on WATCHDOG && AR7
+	help
+	  Hardware driver for the TI AR7 Watchdog Timer.
+
 config INDYDOG
 	tristate "Indy/I2 Hardware Watchdog"
 	depends on SGI_IP22
drivers/char/watchdog/Makefile +1 -0
index 389f8b1..76424f2 100644
@@ -87,6 +87,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
 # M68KNOMMU Architecture
 
 # MIPS Architecture
+obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
 obj-$(CONFIG_INDYDOG) += indydog.o
 obj-$(CONFIG_WDT_MTX1)	+= mtx-1_wdt.o
 obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o
drivers/char/watchdog/ar7_wdt.c new file +346 -0

346 lines not on this page · raw message

#12 lore raw

PATCH 6/7 AR7: serial

In reply to a message not in the archive

Serial support, should not broke other archs

drivers/serial/8250.c +11 -0
index f94109c..94253b7 100644
@@ -267,6 +267,13 @@ static const struct serial8250_config uart_config[] = {
 		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
 		.flags		= UART_CAP_FIFO,
 	},
+	[PORT_AR7] = {
+		.name		= "TI-AR7",
+		.fifo_size	= 16,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
+		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
+	},
 };
 
 #if defined (CONFIG_SERIAL_8250_AU1X00)
@@ -2453,7 +2460,11 @@ static void serial8250_console_putchar(struct uart_port *port, int ch)
 {
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 
+#ifdef CONFIG_AR7
+	wait_for_xmitr(up, BOTH_EMPTY);
+#else
 	wait_for_xmitr(up, UART_LSR_THRE);
+#endif
 	serial_out(up, UART_TX, ch);
 }
 
include/linux/serialP.h +3 -0
index e811a61..ba5734a 100644
@@ -135,6 +135,9 @@ struct rs_multiport_struct {
  * the interrupt line _up_ instead of down, so if we register the IRQ
  * while the UART is in that state, we die in an IRQ storm. */
 #define ALPHA_KLUDGE_MCR (UART_MCR_OUT2)
+#elif defined(CONFIG_AR7)
+/* This is how it is set up by bootloader... */
+#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2|UART_MCR_OUT1|UART_MCR_RTS|UART_MCR_DTR)
 #else
 #define ALPHA_KLUDGE_MCR 0
 #endif
include/linux/serial_core.h +1 -0
index 09d17b0..8ad2c3b 100644
@@ -40,6 +40,7 @@
 #define PORT_NS16550A	14
 #define PORT_XSCALE	15
 #define PORT_RM9000	16	/* PMC-Sierra RM9xxx internal UART */
+#define PORT_AR7	16
 #define PORT_MAX_8250	16	/* max port ID */
 
 /*
#13 lore raw

PATCH 7/7 AR7: ethernet

In reply to a message not in the archive

Driver for the cpmac 100M ethernet driver.
It works fine disabling napi support, enabling it gives a kernel panic when the first IPv6 packet has to be forwarded.
Other than that works fine.

drivers/net/Kconfig +9 -0
index d9b7d9c..6f38a84 100644
@@ -1822,6 +1822,15 @@ config SC92031
 	  To compile this driver as a module, choose M here: the module
 	  will be called sc92031.  This is recommended.
 
+config CPMAC
+	tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)"
+	depends on NET_ETHERNET && EXPERIMENTAL && AR7
+	select PHYLIB
+	select FIXED_PHY
+	select FIXED_MII_100_FDX
+	help
+	  TI AR7 CPMAC Ethernet support
+
 config NET_POCKET
 	bool "Pocket and portable adapters"
 	depends on PARPORT
drivers/net/Makefile +1 -0
index 535d2a0..bb22df9 100644
@@ -156,6 +156,7 @@ obj-$(CONFIG_8139CP) += 8139cp.o
 obj-$(CONFIG_8139TOO) += 8139too.o
 obj-$(CONFIG_ZNET) += znet.o
 obj-$(CONFIG_LAN_SAA9730) += saa9730.o
+obj-$(CONFIG_CPMAC) += cpmac.o
 obj-$(CONFIG_DEPCA) += depca.o
 obj-$(CONFIG_EWRK3) += ewrk3.o
 obj-$(CONFIG_ATP) += atp.o
drivers/net/cpmac.c new file +1194 -0

1,194 lines not on this page · raw message

#14 lore raw

Re: [PATCH][MIPS][7/7] AR7: ethernet

Quoted 13 lines · Matteo Croce

Driver for the cpmac 100M ethernet driver.
It works fine disabling napi support, enabling it gives a kernel panic when the first IPv6 packet has to be forwarded.
Other than that works fine.

drivers/net/Kconfig +0 -0
index d9b7d9c..6f38a84 100644

drivers/net/Kconfig config SC92031 · line 1827

Quoted 6 lines
drivers/net/Kconfig +3 -0
@@ -1822,6 +1822,15 @@ config SC92031
 	  To compile this driver as a module, choose M here: the module
 	  will be called sc92031.  This is recommended.
 
+config CPMAC
+	tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)"
+	depends on NET_ETHERNET && EXPERIMENTAL && AR7

The dependency on NET_ETHERNET is not needed because this config block is enclosed in a

if NET_ETHERNET
...
endif # NET_ETHERNET

block.

Quoted 98 lines
drivers/net/Kconfig +6 -0
+	select PHYLIB
+	select FIXED_PHY
+	select FIXED_MII_100_FDX
+	help
+	  TI AR7 CPMAC Ethernet support
+
 config NET_POCKET
 	bool "Pocket and portable adapters"
 	depends on PARPORT
drivers/net/Makefile +1 -0
index 535d2a0..bb22df9 100644
@@ -156,6 +156,7 @@ obj-$(CONFIG_8139CP) += 8139cp.o
 obj-$(CONFIG_8139TOO) += 8139too.o
 obj-$(CONFIG_ZNET) += znet.o
 obj-$(CONFIG_LAN_SAA9730) += saa9730.o
+obj-$(CONFIG_CPMAC) += cpmac.o
 obj-$(CONFIG_DEPCA) += depca.o
 obj-$(CONFIG_EWRK3) += ewrk3.o
 obj-$(CONFIG_ATP) += atp.o
drivers/net/cpmac.c new file +71 -0
new file mode 100644
index 0000000..c10ab08
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (C) 2006, 2007 Eugene Konev
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <asm/ar7/ar7.h>
+#include <gpio.h>
+
+MODULE_AUTHOR("Eugene Konev");
+MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)");
+MODULE_LICENSE("GPL");
+
+static int rx_ring_size = 64;
+static int disable_napi;
+module_param(rx_ring_size, int, 64);
+module_param(disable_napi, int, 0);
+MODULE_PARM_DESC(rx_ring_size, "Size of rx ring (in skbs)");
+MODULE_PARM_DESC(disable_napi, "Disable NAPI polling");
+
+/* Register definitions */
+struct cpmac_control_regs {
+	u32 revision;
+	u32 control;
+	u32 teardown;
+	u32 unused;
+} __attribute__ ((packed));
+
+struct cpmac_int_regs {
+	u32 stat_raw;
+	u32 stat_masked;
+	u32 enable;
+	u32 clear;
+} __attribute__ ((packed));
+
+struct cpmac_stats {
+	u32 good;
+	u32 bcast;
+	u32 mcast;
+	u32 pause;

drivers/net/cpmac.c line 81

Quoted 10 lines
drivers/net/cpmac.c new file +10 -0
+	u32 crc_error;
+	u32 align_error;
+	u32 oversized;
+	u32 jabber;
+	u32 undersized;
+	u32 fragment;
+	u32 filtered;
+	u32 qos_filtered;
+	u32 octets;
+} __attribute__ ((packed));

All struct members here are sized such that there is no padding needed, so the packed attribute doesn't buy you anything - unless of course the entire structure is missaligned but I don't see how that would be possible in this driver so the __attribute__ ((packed)) should go - it result in somwhat larger and slower code.

In any case, the __packed attribute is prefered over __attribute__ ((packed)) for readability sake.

Quoted 818 lines
drivers/net/cpmac.c new file +818 -0
+
+struct cpmac_regs {
+	struct cpmac_control_regs tx_ctrl;
+	struct cpmac_control_regs rx_ctrl;
+	u32 unused1[56];
+	u32 mbp;
+/* MBP bits */
+#define MBP_RXPASSCRC         0x40000000
+#define MBP_RXQOS             0x20000000
+#define MBP_RXNOCHAIN         0x10000000
+#define MBP_RXCMF             0x01000000
+#define MBP_RXSHORT           0x00800000
+#define MBP_RXCEF             0x00400000
+#define MBP_RXPROMISC         0x00200000
+#define MBP_PROMISCCHAN(chan) (((chan) & 0x7) << 16)
+#define MBP_RXBCAST           0x00002000
+#define MBP_BCASTCHAN(chan)   (((chan) & 0x7) << 8)
+#define MBP_RXMCAST           0x00000020
+#define MBP_MCASTCHAN(chan)   ((chan) & 0x7)
+	u32 unicast_enable;
+	u32 unicast_clear;
+	u32 max_len;
+	u32 buffer_offset;
+	u32 filter_flow_threshold;
+	u32 unused2[2];
+	u32 flow_thre[8];
+	u32 free_buffer[8];
+	u32 mac_control;
+#define MAC_TXPTYPE  0x00000200
+#define MAC_TXPACE   0x00000040
+#define MAC_MII      0x00000020
+#define MAC_TXFLOW   0x00000010
+#define MAC_RXFLOW   0x00000008
+#define MAC_MTEST    0x00000004
+#define MAC_LOOPBACK 0x00000002
+#define MAC_FDX      0x00000001
+	u32 mac_status;
+#define MACST_QOS    0x4
+#define MACST_RXFLOW 0x2
+#define MACST_TXFLOW 0x1
+	u32 emc_control;
+	u32 unused3;
+	struct cpmac_int_regs tx_int;
+	u32 mac_int_vector;
+/* Int Status bits */
+#define INTST_STATUS 0x80000
+#define INTST_HOST   0x40000
+#define INTST_RX     0x20000
+#define INTST_TX     0x10000
+	u32 mac_eoi_vector;
+	u32 unused4[2];
+	struct cpmac_int_regs rx_int;
+	u32 mac_int_stat_raw;
+	u32 mac_int_stat_masked;
+	u32 mac_int_enable;
+	u32 mac_int_clear;
+	u32 mac_addr_low[8];
+	u32 mac_addr_mid;
+	u32 mac_addr_high;
+	u32 mac_hash_low;
+	u32 mac_hash_high;
+	u32 boff_test;
+	u32 pac_test;
+	u32 rx_pause;
+	u32 tx_pause;
+	u32 unused5[2];
+	struct cpmac_stats rx_stats;
+	struct cpmac_stats tx_stats;
+	u32 unused6[232];
+	u32 tx_ptr[8];
+	u32 rx_ptr[8];
+	u32 tx_ack[8];
+	u32 rx_ack[8];
+
+} __attribute__ ((packed));
+
+struct cpmac_mdio_regs {
+	u32 version;
+	u32 control;
+#define MDIOC_IDLE        0x80000000
+#define MDIOC_ENABLE      0x40000000
+#define MDIOC_PREAMBLE    0x00100000
+#define MDIOC_FAULT       0x00080000
+#define MDIOC_FAULTDETECT 0x00040000
+#define MDIOC_INTTEST     0x00020000
+#define MDIOC_CLKDIV(div) ((div) & 0xff)
+	u32 alive;
+	u32 link;
+	struct cpmac_int_regs link_int;
+	struct cpmac_int_regs user_int;
+	u32 unused[20];
+	volatile u32 access;
+#define MDIO_BUSY       0x80000000
+#define MDIO_WRITE      0x40000000
+#define MDIO_REG(reg)   (((reg) & 0x1f) << 21)
+#define MDIO_PHY(phy)   (((phy) & 0x1f) << 16)
+#define MDIO_DATA(data) ((data) & 0xffff)
+	u32 physel;
+} __attribute__ ((packed));
+
+/* Descriptor */
+struct cpmac_desc {
+	u32 hw_next;
+	u32 hw_data;
+	u16 buflen;
+	u16 bufflags;
+	u16 datalen;
+	u16 dataflags;
+/* Flags bits */
+#define CPMAC_SOP 0x8000
+#define CPMAC_EOP 0x4000
+#define CPMAC_OWN 0x2000
+#define CPMAC_EOQ 0x1000
+	struct sk_buff *skb;
+	struct cpmac_desc *next;
+} __attribute__ ((packed));
+
+struct cpmac_priv {
+	struct net_device_stats stats;
+	spinlock_t lock; /* irq{save,restore} */
+	struct sk_buff *skb_pool;
+	int free_skbs;
+	struct cpmac_desc *rx_head;
+	int tx_head, tx_tail;
+	struct cpmac_desc *desc_ring;
+	struct cpmac_regs *regs;
+	struct mii_bus *mii_bus;
+	struct phy_device *phy;
+	char phy_name[BUS_ID_SIZE];
+	struct plat_cpmac_data *config;
+	int oldlink, oldspeed, oldduplex;
+	u32 msg_enable;
+	struct net_device *dev;
+	struct work_struct alloc_work;
+};
+
+static irqreturn_t cpmac_irq(int, void *);
+static void cpmac_reset(struct net_device *dev);
+static void cpmac_hw_init(struct net_device *dev);
+static int cpmac_stop(struct net_device *dev);
+static int cpmac_open(struct net_device *dev);
+
+#undef CPMAC_DEBUG
+#define CPMAC_LOW_THRESH 32
+#define CPMAC_ALLOC_SIZE 64
+#define CPMAC_SKB_SIZE 1518
+#define CPMAC_TX_RING_SIZE 8
+
+#ifdef CPMAC_DEBUG
+static void cpmac_dump_regs(u32 *base, int count)
+{
+	int i;
+	for (i = 0; i < (count + 3) / 4; i++) {
+		if (i % 4 == 0) printk(KERN_DEBUG "\nCPMAC[0x%04x]:", i * 4);
+		printk(KERN_DEBUG " 0x%08x", *(base + i));
+	}
+	printk(KERN_DEBUG "\n");
+}
+
+static const char *cpmac_dump_buf(const uint8_t *buf, unsigned size)
+{
+	static char buffer[3 * 25 + 1];
+	char *p = &buffer[0];
+	if (size > 20)
+		size = 20;
+	while (size-- > 0)
+		p += sprintf(p, " %02x", *buf++);
+	return buffer;
+}
+#endif
+
+static int cpmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+	struct cpmac_mdio_regs *regs = bus->priv;
+	u32 val;
+
+	while ((val = regs->access) & MDIO_BUSY);
+	regs->access = MDIO_BUSY | MDIO_REG(regnum & 0x1f) |
+		MDIO_PHY(phy_id & 0x1f);
+	while ((val = regs->access) & MDIO_BUSY);
+
+	return val & 0xffff;
+}
+
+static int cpmac_mdio_write(struct mii_bus *bus, int phy_id,
+				int regnum, u16 val)
+{
+	struct cpmac_mdio_regs *regs = bus->priv;
+
+	while (regs->access & MDIO_BUSY);
+	regs->access = MDIO_BUSY | MDIO_WRITE |
+		MDIO_REG(regnum & 0x1f) | MDIO_PHY(phy_id & 0x1f) | val;
+
+	return 0;
+}
+
+static int cpmac_mdio_reset(struct mii_bus *bus)
+{
+	ar7_device_reset(AR7_RESET_BIT_MDIO);
+	((struct cpmac_mdio_regs *)bus->priv)->control = MDIOC_ENABLE |
+		MDIOC_CLKDIV(ar7_cpmac_freq() / 2200000 - 1);
+
+	return 0;
+}
+
+static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, };
+
+static struct mii_bus cpmac_mii = {
+	.name = "cpmac-mii",
+	.read = cpmac_mdio_read,
+	.write = cpmac_mdio_write,
+	.reset = cpmac_mdio_reset,
+	.irq = mii_irqs,
+};
+
+static int cpmac_config(struct net_device *dev, struct ifmap *map)
+{
+	if (dev->flags & IFF_UP)
+		return -EBUSY;
+
+	/* Don't allow changing the I/O address */
+	if (map->base_addr != dev->base_addr)
+		return -EOPNOTSUPP;
+
+	/* ignore other fields */
+	return 0;
+}
+
+static int cpmac_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sa = addr;
+
+	if (dev->flags & IFF_UP)
+		return -EBUSY;
+
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+	return 0;
+}
+
+static void cpmac_set_multicast_list(struct net_device *dev)
+{
+	struct dev_mc_list *iter;
+	int i;
+	int hash, tmp;
+	int hashlo = 0, hashhi = 0;
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	if (dev->flags & IFF_PROMISC) {
+		priv->regs->mbp &= ~MBP_PROMISCCHAN(0); /* promisc channel 0 */
+		priv->regs->mbp |= MBP_RXPROMISC;
+	} else {
+		priv->regs->mbp &= ~MBP_RXPROMISC;
+		if (dev->flags & IFF_ALLMULTI) {
+			/* enable all multicast mode */
+			priv->regs->mac_hash_low = 0xffffffff;
+			priv->regs->mac_hash_high = 0xffffffff;
+		} else {
+			for (i = 0, iter = dev->mc_list; i < dev->mc_count;
+			    i++, iter = iter->next) {
+				hash = 0;
+				tmp = iter->dmi_addr[0];
+				hash  ^= (tmp >> 2) ^ (tmp << 4);
+				tmp = iter->dmi_addr[1];
+				hash  ^= (tmp >> 4) ^ (tmp << 2);
+				tmp = iter->dmi_addr[2];
+				hash  ^= (tmp >> 6) ^ tmp;
+				tmp = iter->dmi_addr[4];
+				hash  ^= (tmp >> 2) ^ (tmp << 4);
+				tmp = iter->dmi_addr[5];
+				hash  ^= (tmp >> 4) ^ (tmp << 2);
+				tmp = iter->dmi_addr[6];
+				hash  ^= (tmp >> 6) ^ tmp;
+				hash &= 0x3f;
+				if (hash < 32) {
+					hashlo |= 1<<hash;
+				} else {
+					hashhi |= 1<<(hash - 32);
+				}
+			}
+
+			priv->regs->mac_hash_low = hashlo;
+			priv->regs->mac_hash_high = hashhi;
+		}
+	}
+}
+
+static struct sk_buff *cpmac_get_skb(struct net_device *dev)
+{
+	struct sk_buff *skb;
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	skb = priv->skb_pool;
+	if (likely(skb))
+		priv->skb_pool = skb->next;
+	else {
+		skb = dev_alloc_skb(CPMAC_SKB_SIZE + 2);
+		if (skb) {
+			skb->next = NULL;
+			skb_reserve(skb, 2);
+			skb->dev = priv->dev;
+		}
+	}
+
+	if (likely(priv->free_skbs))
+		priv->free_skbs--;
+
+	if (priv->free_skbs < CPMAC_LOW_THRESH)
+		schedule_work(&priv->alloc_work);
+
+	return skb;
+}
+
+static struct sk_buff *cpmac_rx_one(struct net_device *dev,
+					   struct cpmac_priv *priv,
+					   struct cpmac_desc *desc)
+{
+	unsigned long flags;
+	char *data;
+	struct sk_buff *skb, *result = NULL;
+
+	priv->regs->rx_ack[0] = virt_to_phys(desc);
+	if (unlikely(!desc->datalen)) {
+		if (printk_ratelimit())
+			printk(KERN_WARNING "%s: rx: spurious interrupt\n",
+			       dev->name);
+		priv->stats.rx_errors++;
+		return NULL;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	skb = cpmac_get_skb(dev);
+	if (likely(skb)) {
+		data = (char *)phys_to_virt(desc->hw_data);
+		dma_cache_inv((u32)data, desc->datalen);
+		skb_put(desc->skb, desc->datalen);
+		desc->skb->protocol = eth_type_trans(desc->skb, dev);
+		desc->skb->ip_summed = CHECKSUM_NONE;
+		priv->stats.rx_packets++;
+		priv->stats.rx_bytes += desc->datalen;
+		result = desc->skb;
+		desc->skb = skb;
+	} else {
+#ifdef CPMAC_DEBUG
+		if (printk_ratelimit())
+			printk(KERN_NOTICE "%s: low on skbs, dropping packet\n",
+			       dev->name);
+#endif
+		priv->stats.rx_dropped++;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	desc->hw_data = virt_to_phys(desc->skb->data);
+	desc->buflen = CPMAC_SKB_SIZE;
+	desc->dataflags = CPMAC_OWN;
+	dma_cache_wback((u32)desc, 16);
+
+	return result;
+}
+
+static void cpmac_rx(struct net_device *dev)
+{
+	struct sk_buff *skb;
+	struct cpmac_desc *desc;
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	spin_lock(&priv->lock);
+	if (unlikely(!priv->rx_head)) {
+		spin_unlock(&priv->lock);
+		return;
+	}
+
+	desc = priv->rx_head;
+	dma_cache_inv((u32)desc, 16);
+#ifdef CPMAC_DEBUG
+	printk(KERN_DEBUG "%s: len=%d, %s\n", __func__, pkt->datalen,
+		cpmac_dump_buf(data, pkt->datalen));
+#endif
+
+	while ((desc->dataflags & CPMAC_OWN) == 0) {
+		skb = cpmac_rx_one(dev, priv, desc);
+		if (likely(skb))
+			netif_rx(skb);
+		desc = desc->next;
+		dma_cache_inv((u32)desc, 16);
+	}
+
+	priv->rx_head = desc;
+	priv->regs->rx_ptr[0] = virt_to_phys(desc);
+	spin_unlock(&priv->lock);
+}
+
+static int cpmac_poll(struct net_device *dev, int *budget)
+{
+	struct sk_buff *skb;
+	struct cpmac_desc *desc;
+	int received = 0, quota = min(dev->quota, *budget);
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	if (unlikely(!priv->rx_head)) {
+		if (printk_ratelimit())
+			printk(KERN_NOTICE "%s: rx: polling, but no queue\n",
+			       dev->name);
+		netif_rx_complete(dev);
+		return 0;
+	}
+
+	desc = priv->rx_head;
+	dma_cache_inv((u32)desc, 16);
+
+	while ((received < quota) && ((desc->dataflags & CPMAC_OWN) == 0)) {
+		skb = cpmac_rx_one(dev, priv, desc);
+		if (likely(skb)) {
+			netif_receive_skb(skb);
+			received++;
+		}
+		desc = desc->next;
+		priv->rx_head = desc;
+		dma_cache_inv((u32)desc, 16);
+	}
+
+	*budget -= received;
+	dev->quota -= received;
+#ifdef CPMAC_DEBUG
+	printk(KERN_DEBUG "%s: processed %d packets\n", dev->name, received);
+#endif
+	if (desc->dataflags & CPMAC_OWN) {
+		priv->regs->rx_ptr[0] = virt_to_phys(desc);
+		netif_rx_complete(dev);
+		priv->regs->rx_int.enable = 0x1;
+		priv->regs->rx_int.clear = 0xfe;
+		return 0;
+	}
+
+	return 1;
+}
+
+static void
+cpmac_alloc_skbs(struct work_struct *work)
+{
+	struct cpmac_priv *priv = container_of(work, struct cpmac_priv,
+			alloc_work);
+	unsigned long flags;
+	int i, num_skbs = 0;
+	struct sk_buff *skb, *skbs = NULL;
+
+	for (i = 0; i < CPMAC_ALLOC_SIZE; i++) {
+		skb = alloc_skb(CPMAC_SKB_SIZE + 2, GFP_KERNEL);
+		if (!skb)
+			break;
+		skb->next = skbs;
+		skb_reserve(skb, 2);
+		skb->dev = priv->dev;
+		num_skbs++;
+		skbs = skb;
+	}
+
+	if (skbs) {
+		spin_lock_irqsave(&priv->lock, flags);
+		for (skb = priv->skb_pool; skb && skb->next; skb = skb->next);
+		if (!skb)
+			priv->skb_pool = skbs;
+		else
+			skb->next = skbs;
+		priv->free_skbs += num_skbs;
+		spin_unlock_irqrestore(&priv->lock, flags);
+#ifdef CPMAC_DEBUG
+		printk(KERN_DEBUG "%s: allocated %d skbs\n",
+			priv->dev->name, num_skbs);
+#endif
+	}
+}
+
+static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	unsigned long flags;
+	int len, chan;
+	struct cpmac_desc *desc;
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	len = skb->len;
+#ifdef CPMAC_DEBUG
+	printk(KERN_DEBUG "%s: len=%d\n", __func__, len);
+	/* cpmac_dump_buf(const uint8_t * buf, unsigned size) */
+#endif
+	if (unlikely(len < ETH_ZLEN)) {
+		if (unlikely(skb_padto(skb, ETH_ZLEN))) {
+			if (printk_ratelimit())
+				printk(KERN_NOTICE
+					"%s: padding failed, dropping\n",
+								dev->name);
+			spin_lock_irqsave(&priv->lock, flags);
+			priv->stats.tx_dropped++;
+			spin_unlock_irqrestore(&priv->lock, flags);
+			return -ENOMEM;
+		}
+		len = ETH_ZLEN;
+	}
+	spin_lock_irqsave(&priv->lock, flags);
+	chan = priv->tx_tail++;
+	priv->tx_tail %= 8;
+	if (priv->tx_tail == priv->tx_head)
+		netif_stop_queue(dev);
+
+	desc = &priv->desc_ring[chan];
+	dma_cache_inv((u32)desc, 16);
+	if (desc->dataflags & CPMAC_OWN) {
+		printk(KERN_NOTICE "%s: tx dma ring full, dropping\n",
+								dev->name);
+		priv->stats.tx_dropped++;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return -ENOMEM;
+	}
+
+	dev->trans_start = jiffies;
+	desc->dataflags = CPMAC_SOP | CPMAC_EOP | CPMAC_OWN;
+	desc->skb = skb;
+	desc->hw_data = virt_to_phys(skb->data);
+	dma_cache_wback((u32)skb->data, len);
+	desc->buflen = len;
+	desc->datalen = len;
+	desc->hw_next = 0;
+	dma_cache_wback((u32)desc, 16);
+	priv->regs->tx_ptr[chan] = virt_to_phys(desc);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static void cpmac_end_xmit(struct net_device *dev, int channel)
+{
+	struct cpmac_desc *desc;
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	spin_lock(&priv->lock);
+	desc = &priv->desc_ring[channel];
+	priv->regs->tx_ack[channel] = virt_to_phys(desc);
+	if (likely(desc->skb)) {
+		priv->stats.tx_packets++;
+		priv->stats.tx_bytes += desc->skb->len;
+		dev_kfree_skb_irq(desc->skb);
+		if (netif_queue_stopped(dev))
+			netif_wake_queue(dev);
+	} else
+		if (printk_ratelimit())
+			printk(KERN_NOTICE "%s: end_xmit: spurious interrupt\n",
+			       dev->name);
+	spin_unlock(&priv->lock);
+}
+
+static void cpmac_reset(struct net_device *dev)
+{
+	int i;
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	ar7_device_reset(priv->config->reset_bit);
+	priv->regs->rx_ctrl.control &= ~1;
+	priv->regs->tx_ctrl.control &= ~1;
+	for (i = 0; i < 8; i++) {
+		priv->regs->tx_ptr[i] = 0;
+		priv->regs->rx_ptr[i] = 0;
+	}
+	priv->regs->mac_control &= ~MAC_MII; /* disable mii */
+}
+
+static inline void cpmac_free_rx_ring(struct net_device *dev)
+{
+	struct cpmac_desc *desc;
+	int i;
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	if (unlikely(!priv->rx_head))
+		return;
+
+	desc = priv->rx_head;
+	dma_cache_inv((u32)desc, 16);
+
+	for (i = 0; i < rx_ring_size; i++) {
+		desc->buflen = CPMAC_SKB_SIZE;
+		if ((desc->dataflags & CPMAC_OWN) == 0) {
+			desc->dataflags = CPMAC_OWN;
+			priv->stats.rx_dropped++;
+		}
+		dma_cache_wback((u32)desc, 16);
+		desc = desc->next;
+		dma_cache_inv((u32)desc, 16);
+	}
+}
+
+static irqreturn_t cpmac_irq(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct cpmac_priv *priv = netdev_priv(dev);
+	u32 status;
+
+	if (!dev)
+		return IRQ_NONE;
+
+	status = priv->regs->mac_int_vector;
+
+	if (status & INTST_TX)
+		cpmac_end_xmit(dev, (status & 7));
+
+	if (status & INTST_RX) {
+		if (disable_napi)
+			cpmac_rx(dev);
+		else {
+			priv->regs->rx_int.enable = 0;
+			priv->regs->rx_int.clear = 0xff;
+			netif_rx_schedule(dev);
+		}
+	}
+
+	priv->regs->mac_eoi_vector = 0;
+
+	if (unlikely(status & (INTST_HOST | INTST_STATUS))) {
+		if (printk_ratelimit())
+			printk(KERN_ERR "%s: hw error, resetting...\n",
+								dev->name);
+		spin_lock(&priv->lock);
+		phy_stop(priv->phy);
+		cpmac_reset(dev);
+		cpmac_free_rx_ring(dev);
+		cpmac_hw_init(dev);
+		spin_unlock(&priv->lock);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void cpmac_tx_timeout(struct net_device *dev)
+{
+	struct cpmac_priv *priv = netdev_priv(dev);
+	struct cpmac_desc *desc;
+
+	priv->stats.tx_errors++;
+	desc = &priv->desc_ring[priv->tx_head++];
+	priv->tx_head %= 8;
+	printk(KERN_NOTICE "%s: transmit timeout\n", dev->name);
+	if (desc->skb)
+		dev_kfree_skb(desc->skb);
+	netif_wake_queue(dev);
+}
+
+static int cpmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct cpmac_priv *priv = netdev_priv(dev);
+	if (!(netif_running(dev)))
+		return -EINVAL;
+	if (!priv->phy)
+		return -EINVAL;
+	if ((cmd == SIOCGMIIPHY) || (cmd == SIOCGMIIREG) ||
+	    (cmd == SIOCSMIIREG))
+		return phy_mii_ioctl(priv->phy, if_mii(ifr), cmd);
+
+	return -EINVAL;
+}
+
+static int cpmac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	if (priv->phy)
+		return phy_ethtool_gset(priv->phy, cmd);
+
+	return -EINVAL;
+}
+
+static int cpmac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (priv->phy)
+		return phy_ethtool_sset(priv->phy, cmd);
+
+	return -EINVAL;
+}
+
+static void cpmac_get_drvinfo(struct net_device *dev,
+			      struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, "cpmac");
+	strcpy(info->version, "0.0.3");
+	info->fw_version[0] = '\0';
+	sprintf(info->bus_info, "%s", "cpmac");
+	info->regdump_len = 0;
+}
+
+static const struct ethtool_ops cpmac_ethtool_ops = {
+	.get_settings = cpmac_get_settings,
+	.set_settings = cpmac_set_settings,
+	.get_drvinfo = cpmac_get_drvinfo,
+	.get_link = ethtool_op_get_link,
+};
+
+static struct net_device_stats *cpmac_stats(struct net_device *dev)
+{
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	if (netif_device_present(dev))
+		return &priv->stats;
+
+	return NULL;
+}
+
+static int cpmac_change_mtu(struct net_device *dev, int mtu)
+{
+	unsigned long flags;
+	struct cpmac_priv *priv = netdev_priv(dev);
+	spinlock_t *lock = &priv->lock;
+
+	if ((mtu < 68) || (mtu > 1500))
+		return -EINVAL;
+
+	spin_lock_irqsave(lock, flags);
+	dev->mtu = mtu;
+	spin_unlock_irqrestore(lock, flags);
+
+	return 0;
+}
+
+static void cpmac_adjust_link(struct net_device *dev)
+{
+	struct cpmac_priv *priv = netdev_priv(dev);
+	unsigned long flags;
+	int new_state = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->phy->link) {
+		if (priv->phy->duplex != priv->oldduplex) {
+			new_state = 1;
+			priv->oldduplex = priv->phy->duplex;
+		}
+
+		if (priv->phy->speed != priv->oldspeed) {
+			new_state = 1;
+			priv->oldspeed = priv->phy->speed;
+		}
+
+		if (!priv->oldlink) {
+			new_state = 1;
+			priv->oldlink = 1;
+			netif_schedule(dev);
+		}
+	} else if (priv->oldlink) {
+		new_state = 1;
+		priv->oldlink = 0;
+		priv->oldspeed = 0;
+		priv->oldduplex = -1;
+	}
+
+	if (new_state)
+		phy_print_status(priv->phy);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void cpmac_hw_init(struct net_device *dev)
+{
+	int i;
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	for (i = 0; i < 8; i++)
+		priv->regs->tx_ptr[i] = 0;
+	priv->regs->rx_ptr[0] = virt_to_phys(priv->rx_head);
+
+	priv->regs->mbp = MBP_RXSHORT | MBP_RXBCAST | MBP_RXMCAST;
+	priv->regs->unicast_enable = 0x1;
+	priv->regs->unicast_clear = 0xfe;
+	priv->regs->buffer_offset = 0;
+	for (i = 0; i < 8; i++)
+		priv->regs->mac_addr_low[i] = dev->dev_addr[5];
+	priv->regs->mac_addr_mid = dev->dev_addr[4];
+	priv->regs->mac_addr_high = dev->dev_addr[0] | (dev->dev_addr[1] << 8)
+		| (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
+	priv->regs->max_len = CPMAC_SKB_SIZE;
+	priv->regs->rx_int.enable = 0x1;
+	priv->regs->rx_int.clear = 0xfe;
+	priv->regs->tx_int.enable = 0xff;
+	priv->regs->tx_int.clear = 0;
+	priv->regs->mac_int_enable = 3;
+	priv->regs->mac_int_clear = 0xfc;
+
+	priv->regs->rx_ctrl.control |= 1;
+	priv->regs->tx_ctrl.control |= 1;
+	priv->regs->mac_control |= MAC_MII | MAC_FDX;
+
+	priv->phy->state = PHY_CHANGELINK;
+	phy_start(priv->phy);
+}
+
+static int cpmac_open(struct net_device *dev)
+{
+	int i, size, res;
+	struct cpmac_priv *priv = netdev_priv(dev);
+	struct cpmac_desc *desc;
+	struct sk_buff *skb;
+
+	priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link,
+				0, PHY_INTERFACE_MODE_MII);
+	if (IS_ERR(priv->phy)) {
+		printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+		return PTR_ERR(priv->phy);
+	}
+
+	if (!request_mem_region(dev->mem_start, dev->mem_end -
+				dev->mem_start, dev->name)) {
+		printk(KERN_ERR "%s: failed to request registers\n",
+		       dev->name);
+		res = -ENXIO;
+		goto fail_reserve;
+	}
+
+	priv->regs = ioremap_nocache(dev->mem_start, dev->mem_end -
+				     dev->mem_start);

drivers/net/cpmac.c line 909

Quoted 10 lines
drivers/net/cpmac.c new file +10 -0
+	if (!priv->regs) {
+		printk(KERN_ERR "%s: failed to remap registers\n", dev->name);
+		res = -ENXIO;
+		goto fail_remap;
+	}
+
+	priv->rx_head = NULL;
+	size = sizeof(struct cpmac_desc) * (rx_ring_size +
+					    CPMAC_TX_RING_SIZE);
+	priv->desc_ring = (struct cpmac_desc *)kmalloc(size, GFP_KERNEL);

kmalloc returns void * so no cast to another pointer type necessary.

drivers/net/cpmac.c line 915

Quoted 6 lines
drivers/net/cpmac.c new file +6 -0
+	if (!priv->desc_ring) {
+		res = -ENOMEM;
+		goto fail_alloc;
+	}
+
+	memset((char *)priv->desc_ring, 0, size);

Use kzalloc instead of kmalloc. kzalloc is like kmalloc but returns zero'd memory.

Quoted 131 lines
drivers/net/cpmac.c new file +131 -0
+	priv->skb_pool = NULL;
+	priv->free_skbs = 0;
+	priv->rx_head = &priv->desc_ring[CPMAC_TX_RING_SIZE];
+
+	INIT_WORK(&priv->alloc_work, cpmac_alloc_skbs);
+	schedule_work(&priv->alloc_work);
+	flush_scheduled_work();
+
+	for (i = 0; i < rx_ring_size; i++) {
+		desc = &priv->rx_head[i];
+		skb = cpmac_get_skb(dev);
+		if (!skb) {
+			res = -ENOMEM;
+			goto fail_desc;
+		}
+		desc->skb = skb;
+		desc->hw_data = virt_to_phys(skb->data);
+		desc->buflen = CPMAC_SKB_SIZE;
+		desc->dataflags = CPMAC_OWN;
+		desc->next = &priv->rx_head[(i + 1) % rx_ring_size];
+		desc->hw_next = virt_to_phys(desc->next);
+		dma_cache_wback((u32)desc, 16);
+	}
+
+	if ((res = request_irq(dev->irq, cpmac_irq, SA_INTERRUPT,
+			      dev->name, dev))) {
+		printk(KERN_ERR "%s: failed to obtain irq\n", dev->name);
+		goto fail_irq;
+	}
+
+	cpmac_reset(dev);
+	cpmac_hw_init(dev);
+
+	netif_start_queue(dev);
+	return 0;
+
+fail_irq:
+fail_desc:
+	for (i = 0; i < rx_ring_size; i++)
+		if (priv->rx_head[i].skb)
+			kfree_skb(priv->rx_head[i].skb);
+fail_alloc:
+	kfree(priv->desc_ring);
+
+	for (skb = priv->skb_pool; skb; skb = priv->skb_pool) {
+		priv->skb_pool = skb->next;
+		kfree_skb(skb);
+	}
+
+	iounmap(priv->regs);
+
+fail_remap:
+	release_mem_region(dev->mem_start, dev->mem_end -
+			   dev->mem_start);
+
+fail_reserve:
+	phy_disconnect(priv->phy);
+
+	return res;
+}
+
+static int cpmac_stop(struct net_device *dev)
+{
+	int i;
+	struct sk_buff *skb;
+	struct cpmac_priv *priv = netdev_priv(dev);
+
+	netif_stop_queue(dev);
+
+	phy_stop(priv->phy);
+	phy_disconnect(priv->phy);
+	priv->phy = NULL;
+
+	cpmac_reset(dev);
+
+	for (i = 0; i < 8; i++) {
+		priv->regs->rx_ptr[i] = 0;
+		priv->regs->tx_ptr[i] = 0;
+		priv->regs->mbp = 0;
+	}
+
+	free_irq(dev->irq, dev);
+	release_mem_region(dev->mem_start, dev->mem_end -
+			   dev->mem_start);
+
+	cancel_delayed_work(&priv->alloc_work);
+	flush_scheduled_work();
+
+	priv->rx_head = &priv->desc_ring[CPMAC_TX_RING_SIZE];
+	for (i = 0; i < rx_ring_size; i++)
+		if (priv->rx_head[i].skb)
+			kfree_skb(priv->rx_head[i].skb);
+
+	kfree(priv->desc_ring);
+
+	for (skb = priv->skb_pool; skb; skb = priv->skb_pool) {
+		priv->skb_pool = skb->next;
+		kfree_skb(skb);
+	}
+
+	return 0;
+}
+
+static int external_switch;
+
+static int __devinit cpmac_probe(struct platform_device *pdev)
+{
+	int i, rc, phy_id;
+	struct resource *res;
+	struct cpmac_priv *priv;
+	struct net_device *dev;
+	struct plat_cpmac_data *pdata;
+
+	pdata = pdev->dev.platform_data;
+
+	for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
+		if (!(pdata->phy_mask & (1 << phy_id)))
+			continue;
+		if (!cpmac_mii.phy_map[phy_id])
+			continue;
+		break;
+	}
+
+	if (phy_id == PHY_MAX_ADDR) {
+		if (external_switch)
+			phy_id = 0;
+		else {
+			printk(KERN_ERR "cpmac: no PHY present\n");
+			return -ENODEV;
+		}
+	}

drivers/net/cpmac.c line 1057

Quoted 10 lines
drivers/net/cpmac.c new file +10 -0
+
+	dev = alloc_etherdev(sizeof(struct cpmac_priv));
+
+	if (!dev) {
+		printk(KERN_ERR
+			"cpmac: Unable to allocate net_device structure!\n");
+		return -ENOMEM;
+	}
+
+	SET_MODULE_OWNER(dev);

Set SET_MODULE_OWNER is a useless nop which only exists in 2.6 for driver source compatibility with 2.4. So you can remove this call.

I used the opportunity to send out a patch to remove SET_MODULE_OWNER from the kernel entirely.

Quoted 4 lines
drivers/net/cpmac.c new file +4 -0
+	platform_set_drvdata(pdev, dev);
+	priv = netdev_priv(dev);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");

drivers/net/cpmac.c line 1071

Quoted 10 lines
drivers/net/cpmac.c new file +10 -0
+	if (!res) {
+		rc = -ENODEV;
+		goto fail;
+	}
+
+	dev->mem_start = res->start;
+	dev->mem_end = res->end;
+	dev->irq = platform_get_irq_byname(pdev, "irq");
+
+	dev->mtu                = 1500;

Initializing this field is redundant; alloc_etherdev has already done it, so remove this line.

Quoted 7 lines
drivers/net/cpmac.c new file +7 -0
+	dev->open               = cpmac_open;
+	dev->stop               = cpmac_stop;
+	dev->set_config         = cpmac_config;
+	dev->hard_start_xmit    = cpmac_start_xmit;
+	dev->do_ioctl           = cpmac_ioctl;
+	dev->get_stats          = cpmac_stats;
+	dev->change_mtu         = cpmac_change_mtu;

drivers/net/cpmac.c line 1088

Quoted 10 lines
drivers/net/cpmac.c new file +10 -0
+	dev->set_mac_address    = cpmac_set_mac_address;
+	dev->set_multicast_list = cpmac_set_multicast_list;
+	dev->tx_timeout         = cpmac_tx_timeout;
+	dev->ethtool_ops        = &cpmac_ethtool_ops;
+	if (!disable_napi) {
+		dev->poll = cpmac_poll;
+		dev->weight = min(rx_ring_size, 64);
+	}
+
+	memset(priv, 0, sizeof(struct cpmac_priv));

Useless, alloc_etherdev does that already.

Quoted 32 lines
drivers/net/cpmac.c new file +32 -0
+	spin_lock_init(&priv->lock);
+	priv->msg_enable = netif_msg_init(NETIF_MSG_WOL, 0x3fff);
+	priv->config = pdata;
+	priv->dev = dev;
+	memcpy(dev->dev_addr, priv->config->dev_addr, sizeof(dev->dev_addr));
+	if (phy_id == 31)
+		snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT,
+			 cpmac_mii.id, phy_id);
+	else
+		snprintf(priv->phy_name, BUS_ID_SIZE, "fixed@%d:%d", 100, 1);
+
+	if ((rc = register_netdev(dev))) {
+		printk(KERN_ERR "cpmac: error %i registering device %s\n",
+		       rc, dev->name);
+		goto fail;
+	}
+
+	printk(KERN_INFO "cpmac: device %s (regs: %p, irq: %d, phy: %s, mac: ",
+	       dev->name, (u32 *)dev->mem_start, dev->irq,
+	       priv->phy_name);
+	for (i = 0; i < 6; i++)
+		printk("%02x%s", dev->dev_addr[i], i < 5 ? ":" : ")\n");
+
+	return 0;
+
+fail:
+	free_netdev(dev);
+	return rc;
+}
+
+static int __devexit cpmac_remove(struct platform_device *pdev)
+{

drivers/net/cpmac.c line 1130

Quoted 10 lines
drivers/net/cpmac.c new file +10 -0
+	struct net_device *dev = platform_get_drvdata(pdev);
+	unregister_netdev(dev);
+	free_netdev(dev);
+	return 0;
+}
+
+static struct platform_driver cpmac_driver = {
+	.driver.name = "cpmac",
+	.probe = cpmac_probe,
+	.remove = cpmac_remove,

This should be:

.remove = __devexit_p(cpmac_remove),

to avoid the final link from blowing up when the driver is built into the kernel.

drivers/net/cpmac.c line 1133

Quoted 3 lines
drivers/net/cpmac.c new file +3 -0
+};
+
+int __devinit cpmac_init(void)

Make this function static; no need to export.

drivers/net/cpmac.c line 1145

Quoted 12 lines
drivers/net/cpmac.c new file +12 -0
+{
+	u32 mask;
+	int i, res;
+	cpmac_mii.priv =
+		ioremap_nocache(AR7_REGS_MDIO, sizeof(struct cpmac_mdio_regs));
+
+	if (!cpmac_mii.priv) {
+		printk(KERN_ERR "Can't ioremap mdio registers\n");
+		return -ENXIO;
+	}
+
+#warning FIXME: unhardcode gpio&reset bits

Seeing such warnings always gives me a warm fuzzy feeling ;-)

Quoted 39 lines
drivers/net/cpmac.c new file +39 -0
+	ar7_gpio_disable(26);
+	ar7_gpio_disable(27);
+	ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
+	ar7_device_reset(AR7_RESET_BIT_CPMAC_HI);
+	ar7_device_reset(AR7_RESET_BIT_EPHY);
+
+	cpmac_mii.reset(&cpmac_mii);
+
+	for (i = 0; i < 300000; i++) {
+		mask = ((struct cpmac_mdio_regs *)cpmac_mii.priv)->alive;
+		if (mask)
+			break;
+	}
+
+/*	mask &= 0x7fffffff;
+	if (mask & (mask - 1)) {*/
+		external_switch = 1;
+		mask = 0;
+/*	}*/
+
+	cpmac_mii.phy_mask = ~(mask | 0x80000000);
+
+	res = mdiobus_register(&cpmac_mii);
+	if (res)
+		goto fail_mii;
+
+	res = platform_driver_register(&cpmac_driver);
+	if (res)
+		goto fail_cpmac;
+
+	return 0;
+
+fail_cpmac:
+	mdiobus_unregister(&cpmac_mii);
+
+fail_mii:
+	iounmap(cpmac_mii.priv);
+
+	return res;

drivers/net/cpmac.c line 1194

Quoted 10 lines
drivers/net/cpmac.c new file +10 -0
+}
+
+void __devexit cpmac_exit(void)
+{
+	platform_driver_unregister(&cpmac_driver);
+	mdiobus_unregister(&cpmac_mii);
+}
+
+module_init(cpmac_init);
+module_exit(cpmac_exit);

Time to run ...

Ralf

#15 lore raw

Re: [PATCH][MIPS][7/7] AR7: ethernet

Quoted 1 line · Ralf Baechle

On Sat, Sep 08, 2007 at 02:23:00AM +0200, Matteo Croce wrote:

[snip]

Quoted 28 lines
Quoted 28 lines
drivers/net/cpmac.c new file +28 -0
+/* Register definitions */
+struct cpmac_control_regs {
+	u32 revision;
+	u32 control;
+	u32 teardown;
+	u32 unused;
+} __attribute__ ((packed));
+
+struct cpmac_int_regs {
+	u32 stat_raw;
+	u32 stat_masked;
+	u32 enable;
+	u32 clear;
+} __attribute__ ((packed));
+
+struct cpmac_stats {
+	u32 good;
+	u32 bcast;
+	u32 mcast;
+	u32 pause;
+	u32 crc_error;
+	u32 align_error;
+	u32 oversized;
+	u32 jabber;
+	u32 undersized;
+	u32 fragment;
+	u32 filtered;
+	u32 qos_filtered;
Quoted 8 lines

drivers/net/cpmac.c line 81

Quoted 2 lines
drivers/net/cpmac.c new file +2 -0
+	u32 octets;
+} __attribute__ ((packed));

All struct members here are sized such that there is no padding needed, so the packed attribute doesn't buy you anything - unless of course the entire structure is missaligned but I don't see how that would be possible in this driver so the __attribute__ ((packed)) should go - it result in somwhat larger and slower code.

FWIW, a modern gcc will warn about such superfluous packed attributes, that's another reason to remove those.

Thiemo

#16 lore raw

Re: [PATCH][MIPS][7/7] AR7: ethernet

Quoted 8 lines · Thiemo Seufer
Quoted 5 lines

All struct members here are sized such that there is no padding needed, so the packed attribute doesn't buy you anything - unless of course the entire structure is missaligned but I don't see how that would be possible in this driver so the __attribute__ ((packed)) should go - it result in somwhat larger and slower code.

FWIW, a modern gcc will warn about such superfluous packed attributes, that's another reason to remove those.

I doubt it will in this case; the packed structure is dereferenced by a pointer so no way for gcc to know the alignment.

Ralf