--- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ #include +#include /* * Debugging stuff @@ -330,7 +331,7 @@ params: ldr r0, =params_phys * This routine must preserve: * r4, r5, r6, r7, r8 */ - .align 5 + .align L1_CACHE_SHIFT cache_on: mov r3, #8 @ cache_on function b call_cache_fn @@ -499,7 +500,7 @@ __common_mmu_cache_on: mcr p15, 0, r3, c2, c0, 0 @ load page table pointer mcr p15, 0, r1, c3, c0, 0 @ load domain access control b 1f - .align 5 @ cache line aligned + .align L1_CACHE_SHIFT @ cache line aligned 1: mcr p15, 0, r0, c1, c0, 0 @ load control register mrc p15, 0, r0, c1, c0, 0 @ and read it back to sub pc, lr, r0, lsr #32 @ properly flush pipeline @@ -518,7 +519,7 @@ __common_mmu_cache_on: * r8 = atags pointer * r9-r14 = corrupted */ - .align 5 + .align L1_CACHE_SHIFT reloc_start: add r9, r5, r0 sub r9, r9, #128 @ do not copy the stack debug_reloc_start @@ -722,7 +723,7 @@ proc_types: * On exit, r0, r1, r2, r3, r12 corrupted * This routine must preserve: r4, r6, r7 */ - .align 5 + .align L1_CACHE_SHIFT cache_off: mov r3, #12 @ cache_off function b call_cache_fn @@ -791,7 +792,7 @@ __armv3_mmu_cache_off: * This routine must preserve: * r0, r4, r5, r6, r7 */ - .align 5 + .align L1_CACHE_SHIFT cache_clean_flush: mov r3, #16 b call_cache_fn --- a/arch/arm/include/asm/cache.h +++ b/arch/arm/include/asm/cache.h @@ -4,7 +4,11 @@ #ifndef __ASMARM_CACHE_H #define __ASMARM_CACHE_H +#ifdef CONFIG_CPU_FA526 +#define L1_CACHE_SHIFT 4 +#else #define L1_CACHE_SHIFT 5 +#endif #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) #endif --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -98,7 +98,7 @@ static inline int dma_set_mask(struct de static inline int dma_get_cache_alignment(void) { - return 32; + return L1_CACHE_BYTES; } static inline int dma_is_consistent(struct device *dev, dma_addr_t handle) --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -824,7 +824,7 @@ config ISA_DMA_API bool config PCI - bool "PCI support" if ARCH_INTEGRATOR_AP || ARCH_VERSATILE_PB || ARCH_IXP4XX || ARCH_KS8695 || MACH_ARMCORE + bool "PCI support" if ARCH_INTEGRATOR_AP || ARCH_VERSATILE_PB || ARCH_IXP4XX || ARCH_KS8695 || MACH_ARMCORE || ARCH_GEMINI help Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -21,6 +21,7 @@ #include #include #include +#include #include "entry-header.S" @@ -153,7 +154,7 @@ ENDPROC(__und_invalid) stmia r5, {r0 - r4} .endm - .align 5 + .align L1_CACHE_SHIFT __dabt_svc: svc_entry @@ -202,7 +203,7 @@ __dabt_svc: UNWIND(.fnend ) ENDPROC(__dabt_svc) - .align 5 + .align L1_CACHE_SHIFT __irq_svc: svc_entry @@ -247,7 +248,7 @@ svc_preempt: b 1b #endif - .align 5 + .align L1_CACHE_SHIFT __und_svc: #ifdef CONFIG_KPROBES @ If a kprobe is about to simulate a "stmdb sp..." instruction, @@ -286,7 +287,7 @@ __und_svc: UNWIND(.fnend ) ENDPROC(__und_svc) - .align 5 + .align L1_CACHE_SHIFT __pabt_svc: svc_entry @@ -329,7 +330,7 @@ __pabt_svc: UNWIND(.fnend ) ENDPROC(__pabt_svc) - .align 5 + .align L1_CACHE_SHIFT .LCcralign: .word cr_alignment #ifdef MULTI_DABORT @@ -400,7 +401,7 @@ ENDPROC(__pabt_svc) #endif .endm - .align 5 + .align L1_CACHE_SHIFT __dabt_usr: usr_entry kuser_cmpxchg_check @@ -432,7 +433,7 @@ __dabt_usr: UNWIND(.fnend ) ENDPROC(__dabt_usr) - .align 5 + .align L1_CACHE_SHIFT __irq_usr: usr_entry kuser_cmpxchg_check @@ -465,7 +466,7 @@ ENDPROC(__irq_usr) .ltorg - .align 5 + .align L1_CACHE_SHIFT __und_usr: usr_entry @@ -668,7 +669,7 @@ __und_usr_unknown: b do_undefinstr ENDPROC(__und_usr_unknown) - .align 5 + .align L1_CACHE_SHIFT __pabt_usr: usr_entry @@ -778,7 +779,7 @@ ENDPROC(__switch_to) #endif .endm - .align 5 + .align L1_CACHE_SHIFT .globl __kuser_helper_start __kuser_helper_start: @@ -821,7 +822,7 @@ __kuser_memory_barrier: @ 0xffff0fa0 #endif usr_ret lr - .align 5 + .align L1_CACHE_SHIFT /* * Reference prototype: @@ -953,7 +954,7 @@ kuser_cmpxchg_fixup: #endif - .align 5 + .align L1_CACHE_SHIFT /* * Reference prototype: @@ -1035,7 +1036,7 @@ __kuser_helper_end: * of which is copied into r0 for the mode specific abort handler. */ .macro vector_stub, name, mode, correction=0 - .align 5 + .align L1_CACHE_SHIFT vector_\name: .if \correction @@ -1160,7 +1161,7 @@ __stubs_start: .long __und_invalid @ e .long __und_invalid @ f - .align 5 + .align L1_CACHE_SHIFT /*============================================================================= * Undefined FIQs @@ -1190,7 +1191,7 @@ vector_addrexcptn: * We group all the following data together to optimise * for CPUs with separate I & D caches. */ - .align 5 + .align L1_CACHE_SHIFT .LCvswi: .word vector_swi --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -10,13 +10,14 @@ #include #include +#include #include #include #include "entry-header.S" - .align 5 + .align L1_CACHE_SHIFT /* * This is the fast syscall return path. We do as little as * possible here, and this includes saving r0 back into the SVC @@ -178,7 +179,7 @@ ftrace_stub: #define A710(code...) #endif - .align 5 + .align L1_CACHE_SHIFT ENTRY(vector_swi) sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ Calling r0 - r12 @@ -306,7 +307,7 @@ __sys_trace_return: bl syscall_trace b ret_slow_syscall - .align 5 + .align L1_CACHE_SHIFT #ifdef CONFIG_ALIGNMENT_TRAP .type __cr_alignment, #object __cr_alignment: --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -21,6 +21,7 @@ #include #include #include +#include #if (PHYS_OFFSET & 0x001fffff) #error "PHYS_OFFSET must be at an even 2MiB boundary!" @@ -187,7 +188,7 @@ ENDPROC(__enable_mmu) * * other registers depend on the function called upon completion */ - .align 5 + .align L1_CACHE_SHIFT __turn_mmu_on: mov r0, r0 mcr p15, 0, r0, c1, c0, 0 @ write control reg --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -6,6 +6,7 @@ #include #include #include +#include OUTPUT_ARCH(arm) ENTRY(stext) @@ -58,7 +59,7 @@ SECTIONS *(.security_initcall.init) __security_initcall_end = .; #ifdef CONFIG_BLK_DEV_INITRD - . = ALIGN(32); + . = ALIGN(L1_CACHE_BYTES); __initramfs_start = .; usr/built-in.o(.init.ramfs) __initramfs_end = .; @@ -165,13 +166,13 @@ SECTIONS /* * then the cacheline aligned data */ - . = ALIGN(32); + . = ALIGN(L1_CACHE_BYTES); *(.data.cacheline_aligned) /* * The exception fixup table (might need resorting at runtime) */ - . = ALIGN(32); + . = ALIGN(L1_CACHE_BYTES); __start___ex_table = .; #ifdef CONFIG_MMU *(__ex_table) --- a/arch/arm/lib/copy_page.S +++ b/arch/arm/lib/copy_page.S @@ -12,11 +12,12 @@ #include #include #include +#include #define COPY_COUNT (PAGE_SZ/64 PLD( -1 )) .text - .align 5 + .align L1_CACHE_SHIFT /* * StrongARM optimised copy_page routine * now 1.78bytes/cycle, was 1.60 bytes/cycle (50MHz bus -> 89MB/s) --- a/arch/arm/lib/memchr.S +++ b/arch/arm/lib/memchr.S @@ -11,9 +11,10 @@ */ #include #include +#include .text - .align 5 + .align L1_CACHE_SHIFT ENTRY(memchr) 1: subs r2, r2, #1 bmi 2f --- a/arch/arm/lib/memset.S +++ b/arch/arm/lib/memset.S @@ -11,9 +11,10 @@ */ #include #include +#include .text - .align 5 + .align L1_CACHE_SHIFT .word 0 1: subs r2, r2, #4 @ 1 do we have enough --- a/arch/arm/lib/memzero.S +++ b/arch/arm/lib/memzero.S @@ -9,9 +9,10 @@ */ #include #include +#include .text - .align 5 + .align L1_CACHE_SHIFT .word 0 /* * Align the pointer in r0. r3 contains the number of bytes that we are --- a/arch/arm/lib/strchr.S +++ b/arch/arm/lib/strchr.S @@ -11,9 +11,10 @@ */ #include #include +#include .text - .align 5 + .align L1_CACHE_SHIFT ENTRY(strchr) and r1, r1, #0xff 1: ldrb r2, [r0], #1 --- a/arch/arm/lib/strncpy_from_user.S +++ b/arch/arm/lib/strncpy_from_user.S @@ -10,9 +10,10 @@ #include #include #include +#include .text - .align 5 + .align L1_CACHE_SHIFT /* * Copy a string from user space to kernel space. --- a/arch/arm/lib/strnlen_user.S +++ b/arch/arm/lib/strnlen_user.S @@ -10,9 +10,10 @@ #include #include #include +#include .text - .align 5 + .align L1_CACHE_SHIFT /* Prototype: unsigned long __strnlen_user(const char *str, long n) * Purpose : get length of a string in user memory --- a/arch/arm/lib/strrchr.S +++ b/arch/arm/lib/strrchr.S @@ -11,9 +11,10 @@ */ #include #include +#include .text - .align 5 + .align L1_CACHE_SHIFT ENTRY(strrchr) mov r3, #0 1: ldrb r2, [r0], #1 --- /dev/null +++ b/arch/arm/mach-gemini/board-nas4220b.c @@ -0,0 +1,160 @@ +/* + * Support for Raidsonic NAS-4220-B + * + * Copyright (C) 2009 Janos Laube + * + * based on rut1xx.c + * Copyright (C) 2008 Paulius Zaleckas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "common.h" + +static struct sys_timer ib4220b_timer = { + .init = gemini_timer_init, +}; + +static struct gpio_led ib4220b_leds[] = { + { + .name = "nas4220b:orange:hdd", + .default_trigger = "ide-disk", + .gpio = 60, + }, + { + .name = "nas4220b:green:os", + .default_trigger = "heartbeat", + .gpio = 62, + }, +}; + +static struct gpio_led_platform_data ib4220b_leds_data = { + .num_leds = ARRAY_SIZE(ib4220b_leds), + .leds = ib4220b_leds, +}; + +static struct platform_device ib4220b_led_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &ib4220b_leds_data, + }, +}; + +static struct gpio_keys_button ib4220b_keys[] = { + { + .code = KEY_SETUP, + .gpio = 61, + .active_low = 1, + .desc = "Backup Button", + .type = EV_KEY, + }, + { + .code = KEY_RESTART, + .gpio = 63, + .active_low = 1, + .desc = "Softreset Button", + .type = EV_KEY, + }, +}; + +static struct gpio_keys_platform_data ib4220b_keys_data = { + .buttons = ib4220b_keys, + .nbuttons = ARRAY_SIZE(ib4220b_keys), +}; + +static struct platform_device ib4220b_key_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &ib4220b_keys_data, + }, +}; + +static struct mdio_gpio_platform_data ib4220b_mdio = { + .mdc = 22, + .mdio = 21, + .phy_mask = ~(1 << 1), +}; + +static struct platform_device ib4220b_phy_device = { + .name = "mdio-gpio", + .id = 0, + .dev = { + .platform_data = &ib4220b_mdio, + }, +}; + +static struct gemini_gmac_platform_data ib4220b_gmac_data = { + .bus_id[0] = "0:01", + .interface[0] = PHY_INTERFACE_MODE_RGMII, +}; + +static void __init gmac_ib4220b_init(void) +{ + unsigned int val; + + val = __raw_readl(IO_ADDRESS(GEMINI_GLOBAL_BASE) + + GLOBAL_IO_DRIVING_CTRL); + val |= (0x3 << GMAC0_PADS_SHIFT) | (0x3 << GMAC1_PADS_SHIFT); + __raw_writel(val, IO_ADDRESS(GEMINI_GLOBAL_BASE) + + GLOBAL_IO_DRIVING_CTRL); + + val = (0x0 << GMAC0_RXDV_SKEW_SHIFT) | (0xf << GMAC0_RXC_SKEW_SHIFT) | + (0x7 << GMAC0_TXEN_SKEW_SHIFT) | (0xa << GMAC0_TXC_SKEW_SHIFT) | + (0x0 << GMAC1_RXDV_SKEW_SHIFT) | (0xf << GMAC1_RXC_SKEW_SHIFT) | + (0x7 << GMAC1_TXEN_SKEW_SHIFT) | (0xa << GMAC1_TXC_SKEW_SHIFT); + __raw_writel(val, IO_ADDRESS(GEMINI_GLOBAL_BASE) + + GLOBAL_GMAC_CTRL_SKEW_CTRL); + + val = __raw_readl(IO_ADDRESS(GEMINI_GLOBAL_BASE) + + GLOBAL_ARBITRATION1_CTRL); + val |= (0x20 << BURST_LENGTH_SHIFT); + __raw_writel(val, IO_ADDRESS(GEMINI_GLOBAL_BASE) + + GLOBAL_ARBITRATION1_CTRL); +} + +static void __init ib4220b_init(void) +{ + gemini_gpio_init(); + gmac_ib4220b_init(); + platform_register_uart(); + platform_register_usb(0); + platform_register_usb(1); + platform_register_pflash(SZ_16M, NULL, 0); + platform_device_register(&ib4220b_led_device); + platform_device_register(&ib4220b_key_device); + platform_device_register(&ib4220b_phy_device); + platform_register_ethernet(&ib4220b_gmac_data); +} + +MACHINE_START(NAS4220B, "Raidsonic NAS IB-4220-B") + .phys_io = 0x7fffc000, + .io_pg_offst = ((0xffffc000) >> 18) & 0xfffc, + .boot_params = 0x100, + .map_io = gemini_map_io, + .init_irq = gemini_init_irq, + .timer = &ib4220b_timer, + .init_machine = ib4220b_init, +MACHINE_END --- a/arch/arm/mach-gemini/board-rut1xx.c +++ b/arch/arm/mach-gemini/board-rut1xx.c @@ -14,13 +14,35 @@ #include #include #include +#include #include #include #include +#include + #include "common.h" +static struct mdio_gpio_platform_data rut1xx_mdio = { + .mdc = 22, + .mdio = 21, + .phy_mask = ~(1 << 1), +}; + +static struct platform_device rut1xx_phy_device = { + .name = "mdio-gpio", + .id = 0, + .dev = { + .platform_data = &rut1xx_mdio, + }, +}; + +static struct gemini_gmac_platform_data gmac_data = { + .bus_id[0] = "0:01", + .interface[0] = PHY_INTERFACE_MODE_MII, +}; + static struct gpio_keys_button rut1xx_keys[] = { { .code = KEY_SETUP, @@ -79,9 +101,13 @@ static void __init rut1xx_init(void) { gemini_gpio_init(); platform_register_uart(); + platform_register_watchdog(); + platform_register_usb(0); platform_register_pflash(SZ_8M, NULL, 0); platform_device_register(&rut1xx_leds); platform_device_register(&rut1xx_keys_device); + platform_device_register(&rut1xx_phy_device); + platform_register_ethernet(&gmac_data); } MACHINE_START(RUT100, "Teltonika RUT100") --- a/arch/arm/mach-gemini/common.h +++ b/arch/arm/mach-gemini/common.h @@ -13,6 +13,7 @@ #define __GEMINI_COMMON_H__ struct mtd_partition; +struct gemini_gmac_platform_data; extern void gemini_map_io(void); extern void gemini_init_irq(void); @@ -21,8 +22,11 @@ extern void gemini_gpio_init(void); /* Common platform devices registration functions */ extern int platform_register_uart(void); +extern int platform_register_ethernet(struct gemini_gmac_platform_data *pdata); extern int platform_register_pflash(unsigned int size, struct mtd_partition *parts, unsigned int nr_parts); +extern int platform_register_usb(unsigned int i); +extern int platform_register_watchdog(void); #endif /* __GEMINI_COMMON_H__ */ --- a/arch/arm/mach-gemini/devices.c +++ b/arch/arm/mach-gemini/devices.c @@ -17,6 +17,7 @@ #include #include #include +#include static struct plat_serial8250_port serial_platform_data[] = { { @@ -45,6 +46,116 @@ int platform_register_uart(void) return platform_device_register(&serial_device); } +static struct resource usb0_resources[] = { + { + .start = 0x68000000, + .end = 0x68000fff, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_USB0, + .end = IRQ_USB0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource usb1_resources[] = { + { + .start = 0x69000000, + .end = 0x69000fff, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_USB1, + .end = IRQ_USB1, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 usb0_dmamask = 0xffffffffUL; + +static u64 usb1_dmamask = 0xffffffffUL; + +static struct platform_device usb_device[] = { + { + .name = "ehci-fotg2xx", + .id = 0, + .dev = { + .dma_mask = &usb0_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(usb0_resources), + .resource = usb0_resources, + }, + { + .name = "ehci-fotg2xx", + .id = 1, + .dev = { + .dma_mask = &usb1_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(usb1_resources), + .resource = usb1_resources, + }, +}; + +int platform_register_usb(unsigned int i) +{ + if (i > 1) + return -EINVAL; + + return platform_device_register(&usb_device[i]); +} + +static struct resource gmac_resources[] = { + { + .start = 0x60000000, + .end = 0x6000ffff, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_GMAC0, + .end = IRQ_GMAC0, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_GMAC1, + .end = IRQ_GMAC1, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 gmac_dmamask = 0xffffffffUL; + +static struct platform_device ethernet_device = { + .name = "gemini-gmac", + .id = 0, + .dev = { + .dma_mask = &gmac_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(gmac_resources), + .resource = gmac_resources, +}; + +int platform_register_ethernet(struct gemini_gmac_platform_data *pdata) +{ + unsigned int reg = __raw_readl(IO_ADDRESS(GEMINI_GLOBAL_BASE) + GLOBAL_MISC_CTRL); + + reg &= ~(GMAC_GMII | GMAC_1_ENABLE); + + if (pdata->bus_id[1]) + reg |= GMAC_1_ENABLE; + else if (pdata->interface[0] == PHY_INTERFACE_MODE_GMII) + reg |= GMAC_GMII; + + __raw_writel(reg, IO_ADDRESS(GEMINI_GLOBAL_BASE) + GLOBAL_MISC_CTRL); + + ethernet_device.dev.platform_data = pdata; + + return platform_device_register(ðernet_device); +} + static struct resource flash_resource = { .start = GEMINI_FLASH_BASE, .flags = IORESOURCE_MEM, @@ -90,3 +201,21 @@ int platform_register_pflash(unsigned in return platform_device_register(&pflash_device); } + +static struct resource wdt_resource = { + .start = GEMINI_WAQTCHDOG_BASE, + .end = GEMINI_WAQTCHDOG_BASE + 0x18, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device wdt_device = { + .name = "gemini-wdt", + .id = 0, + .resource = &wdt_resource, + .num_resources = 1, +}; + +int platform_register_watchdog(void) +{ + return platform_device_register(&wdt_device); +} --- /dev/null +++ b/arch/arm/mach-gemini/include/mach/gmac.h @@ -0,0 +1,21 @@ +/* + * Gemini GMAC specific defines + * + * Copyright (C) 2008, Paulius Zaleckas + * + * 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. + */ +#ifndef __MACH_GMAC_H__ +#define __MACH_GMAC_H__ + +#include + +struct gemini_gmac_platform_data { + char *bus_id[2]; /* NULL means that this port is not used */ + phy_interface_t interface[2]; +}; + +#endif /* __MACH_GMAC_H__ */ --- a/arch/arm/mach-gemini/include/mach/hardware.h +++ b/arch/arm/mach-gemini/include/mach/hardware.h @@ -72,4 +72,12 @@ */ #define IO_ADDRESS(x) ((((x) & 0xFFF00000) >> 4) | ((x) & 0x000FFFFF) | 0xF0000000) +/* + * PCI subsystem macros + */ +#define PCIBIOS_MIN_IO 0x00000100 +#define PCIBIOS_MIN_MEM 0x00000000 + +#define pcibios_assign_all_busses() 1 + #endif --- a/arch/arm/mach-gemini/include/mach/irqs.h +++ b/arch/arm/mach-gemini/include/mach/irqs.h @@ -43,11 +43,14 @@ #define NORMAL_IRQ_NUM 32 -#define GPIO_IRQ_BASE NORMAL_IRQ_NUM +#define PCI_IRQ_BASE NORMAL_IRQ_NUM +#define PCI_IRQ_NUM 4 + +#define GPIO_IRQ_BASE (NORMAL_IRQ_NUM + PCI_IRQ_NUM) #define GPIO_IRQ_NUM (3 * 32) #define ARCH_TIMER_IRQ IRQ_TIMER2 -#define NR_IRQS (NORMAL_IRQ_NUM + GPIO_IRQ_NUM) +#define NR_IRQS (NORMAL_IRQ_NUM + PCI_IRQ_NUM + GPIO_IRQ_NUM) #endif /* __MACH_IRQS_H__ */ --- a/arch/arm/mach-gemini/Kconfig +++ b/arch/arm/mach-gemini/Kconfig @@ -2,6 +2,13 @@ if ARCH_GEMINI menu "Cortina Systems Gemini Implementations" +config MACH_NAS4220B + bool "Raidsonic NAS-4220-B" + select GEMINI_MEM_SWAP + help + Say Y here if you intend to run this kernel on a + Raidsonic NAS-4220-B. + config MACH_RUT100 bool "Teltonika RUT100" select GEMINI_MEM_SWAP --- a/arch/arm/mach-gemini/Makefile +++ b/arch/arm/mach-gemini/Makefile @@ -6,5 +6,8 @@ obj-y := irq.o mm.o time.o devices.o gpio.o +obj-$(CONFIG_PCI) += pci.o + # Board-specific support +obj-$(CONFIG_MACH_NAS4220B) += board-nas4220b.o obj-$(CONFIG_MACH_RUT100) += board-rut1xx.o --- a/arch/arm/mach-gemini/mm.c +++ b/arch/arm/mach-gemini/mm.c @@ -59,6 +59,11 @@ static struct map_desc gemini_io_desc[] .length = SZ_512K, .type = MT_DEVICE, }, { + .virtual = IO_ADDRESS(GEMINI_PCI_IO_BASE), + .pfn = __phys_to_pfn(GEMINI_PCI_IO_BASE), + .length = SZ_512K, + .type = MT_DEVICE, + }, { .virtual = IO_ADDRESS(GEMINI_FLASH_CTRL_BASE), .pfn = __phys_to_pfn(GEMINI_FLASH_CTRL_BASE), .length = SZ_512K, --- /dev/null +++ b/arch/arm/mach-gemini/pci.c @@ -0,0 +1,315 @@ +/* + * Support for Gemini PCI Controller + * + * Copyright (C) 2009 Janos Laube + * Copyright (C) 2009 Paulius Zaleckas + * + * based on SL2312 PCI controller code + * Storlink (C) 2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include +#include + +#include + +#define GEMINI_PCI_IOSIZE_1M 0x0000 + +#define GEMINI_PCI_PMC 0x40 +#define GEMINI_PCI_PMCSR 0x44 +#define GEMINI_PCI_CTRL1 0x48 +#define GEMINI_PCI_CTRL2 0x4C +#define GEMINI_PCI_MEM1_BASE_SIZE 0x50 +#define GEMINI_PCI_MEM2_BASE_SIZE 0x54 +#define GEMINI_PCI_MEM3_BASE_SIZE 0x58 + +#define PCI_CTRL2_INTSTS_OFFSET 28 +#define PCI_CTRL2_INTMASK_OFFSET 22 + +#define GEMINI_PCI_DMA_MASK 0xFFF00000 +#define GEMINI_PCI_DMA_MEM1_BASE 0x00000000 +#define GEMINI_PCI_DMA_MEM2_BASE 0x00000000 +#define GEMINI_PCI_DMA_MEM3_BASE 0x00000000 +#define GEMINI_PCI_DMA_MEM1_SIZE 7 +#define GEMINI_PCI_DMA_MEM2_SIZE 6 +#define GEMINI_PCI_DMA_MEM3_SIZE 6 + +#define PCI_CONF_ENABLE (1 << 31) +#define PCI_CONF_WHERE(r) ((r) & 0xFC) +#define PCI_CONF_BUS(b) (((b) & 0xFF) << 16) +#define PCI_CONF_DEVICE(d) (((d) & 0x1F) << 11) +#define PCI_CONF_FUNCTION(f) (((f) & 0x07) << 8) + +#define PCI_IOSIZE_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE)) +#define PCI_PROT_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x04) +#define PCI_CTRL_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x08) +#define PCI_SOFTRST_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x10) +#define PCI_CONFIG_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x28) +#define PCI_DATA_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x2C) + + +static DEFINE_SPINLOCK(gemini_pci_lock); + +static struct resource gemini_pci_resource_io = { + .name = "PCI I/O Space", + .start = IO_ADDRESS(GEMINI_PCI_IO_BASE), + .end = IO_ADDRESS(GEMINI_PCI_IO_BASE) + SZ_1M - 1, + .flags = IORESOURCE_IO, +}; + +static struct resource gemini_pci_resource_mem = { + .name = "PCI Memory Space", + .start = GEMINI_PCI_MEM_BASE, + .end = GEMINI_PCI_MEM_BASE + SZ_128M - 1, + .flags = IORESOURCE_MEM, +}; + +static int gemini_pci_read_config(struct pci_bus* bus, unsigned int fn, + int config, int size, u32* value) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&gemini_pci_lock, irq_flags); + + __raw_writel(PCI_CONF_BUS(bus->number) | + PCI_CONF_DEVICE(PCI_SLOT(fn)) | + PCI_CONF_FUNCTION(PCI_FUNC(fn)) | + PCI_CONF_WHERE(config) | + PCI_CONF_ENABLE, + PCI_CONFIG_REG); + + *value = __raw_readl(PCI_DATA_REG); + + if (size == 1) + *value = (*value >> (8 * (config & 3))) & 0xFF; + else if (size == 2) + *value = (*value >> (8 * (config & 3))) & 0xFFFF; + + spin_unlock_irqrestore(&gemini_pci_lock, irq_flags); + + dev_dbg(&bus->dev, + "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", + PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); + + return PCIBIOS_SUCCESSFUL; +} + +static int gemini_pci_write_config(struct pci_bus* bus, unsigned int fn, + int config, int size, u32 value) +{ + unsigned long irq_flags = 0; + int ret = PCIBIOS_SUCCESSFUL; + + dev_dbg(&bus->dev, + "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", + PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); + + spin_lock_irqsave(&gemini_pci_lock, irq_flags); + + __raw_writel(PCI_CONF_BUS(bus->number) | + PCI_CONF_DEVICE(PCI_SLOT(fn)) | + PCI_CONF_FUNCTION(PCI_FUNC(fn)) | + PCI_CONF_WHERE(config) | + PCI_CONF_ENABLE, + PCI_CONFIG_REG); + + switch(size) { + case 4: + __raw_writel(value, PCI_DATA_REG); + break; + case 2: + __raw_writew(value, PCI_DATA_REG + (config & 3)); + break; + case 1: + __raw_writeb(value, PCI_DATA_REG + (config & 3)); + break; + default: + ret = PCIBIOS_BAD_REGISTER_NUMBER; + } + + spin_unlock_irqrestore(&gemini_pci_lock, irq_flags); + + return ret; +} + +static struct pci_ops gemini_pci_ops = { + .read = gemini_pci_read_config, + .write = gemini_pci_write_config, +}; + +static int __init gemini_pci_request_resources(struct pci_sys_data *sys) +{ + if (request_resource(&ioport_resource, &gemini_pci_resource_io)) + goto bad_resources; + if (request_resource(&iomem_resource, &gemini_pci_resource_mem)) + goto bad_resources; + + sys->resource[0] = &gemini_pci_resource_io; + sys->resource[1] = &gemini_pci_resource_mem; + sys->resource[2] = 0; + + return 0; + +bad_resources: + pr_err("Gemini PCI: request_resource() failed. " + "Abort PCI bus enumeration.\n"); + return -1; +} + +static int __init gemini_pci_setup(int nr, struct pci_sys_data *sys) +{ + unsigned int cmd; + + if ((nr > 0) || gemini_pci_request_resources(sys)) + return 0; + + /* setup I/O space to 1MB size */ + __raw_writel(GEMINI_PCI_IOSIZE_1M, PCI_IOSIZE_REG); + + /* setup hostbridge */ + cmd = __raw_readl(PCI_CTRL_REG); + cmd |= PCI_COMMAND_IO; + cmd |= PCI_COMMAND_MEMORY; + cmd |= PCI_COMMAND_MASTER; + __raw_writel(cmd, PCI_CTRL_REG); + + return 1; +} + +static struct pci_bus* __init gemini_pci_scan_bus(int nr, struct pci_sys_data* sys) +{ + unsigned int reg = 0; + struct pci_bus* bus = 0; + + bus = pci_scan_bus(nr, &gemini_pci_ops, sys); + if (bus) { + dev_dbg(&bus->dev, "setting up PCI DMA\n"); + reg = (GEMINI_PCI_DMA_MEM1_BASE & GEMINI_PCI_DMA_MASK) + | (GEMINI_PCI_DMA_MEM1_SIZE << 16); + gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM1_BASE_SIZE, 4, reg); + reg = (GEMINI_PCI_DMA_MEM2_BASE & GEMINI_PCI_DMA_MASK) + | (GEMINI_PCI_DMA_MEM2_SIZE << 16); + gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM2_BASE_SIZE, 4, reg); + reg = (GEMINI_PCI_DMA_MEM3_BASE & GEMINI_PCI_DMA_MASK) + | (GEMINI_PCI_DMA_MEM3_SIZE << 16); + gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM3_BASE_SIZE, 4, reg); + } + + return bus; +} + +/* Should work with all boards based on original Storlink EVB */ +static int __init gemini_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + if (slot < 9 || slot > 12) + return -1; + + return PCI_IRQ_BASE + (((slot - 9) + (pin - 1)) & 0x3); +} + +static struct hw_pci gemini_hw_pci __initdata = { + .nr_controllers = 1, + .setup = gemini_pci_setup, + .scan = gemini_pci_scan_bus, + .swizzle = pci_std_swizzle, + .map_irq = gemini_pci_map_irq, +}; + +/* we need this for muxed PCI interrupts handling */ +static struct pci_bus bogus_pci_bus; + +static void gemini_pci_ack_irq(unsigned int irq) +{ + unsigned int reg; + + gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, ®); + reg &= ~(0xF << PCI_CTRL2_INTSTS_OFFSET); + reg |= 1 << (irq - PCI_IRQ_BASE + PCI_CTRL2_INTSTS_OFFSET); + gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, reg); +} + +static void gemini_pci_mask_irq(unsigned int irq) +{ + unsigned int reg; + + gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, ®); + reg &= ~((0xF << PCI_CTRL2_INTSTS_OFFSET) + | (1 << (irq - PCI_IRQ_BASE + PCI_CTRL2_INTMASK_OFFSET))); + gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, reg); +} + +static void gemini_pci_unmask_irq(unsigned int irq) +{ + unsigned int reg; + + gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, ®); + reg &= ~(0xF << PCI_CTRL2_INTSTS_OFFSET); + reg |= 1 << (irq - PCI_IRQ_BASE + PCI_CTRL2_INTMASK_OFFSET); + gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, reg); +} + +static void gemini_pci_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + unsigned int pci_irq_no, irq_stat, reg, i; + + gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, ®); + irq_stat = reg >> PCI_CTRL2_INTSTS_OFFSET; + + for (i = 0; i < 4; i++) { + + if ((irq_stat & (1 << i)) == 0) + continue; + + pci_irq_no = PCI_IRQ_BASE + i; + + BUG_ON(!(irq_desc[pci_irq_no].handle_irq)); + irq_desc[pci_irq_no].handle_irq(pci_irq_no, + &irq_desc[pci_irq_no]); + } +} + +static struct irq_chip gemini_pci_irq_chip = { + .name = "PCI", + .ack = gemini_pci_ack_irq, + .mask = gemini_pci_mask_irq, + .unmask = gemini_pci_unmask_irq, +}; + +static int __init gemini_pci_init(void) +{ + int i; + + for (i = 72; i <= 95; i++) + gpio_request(i, "PCI"); + + /* initialize our bogus bus */ + dev_set_name(&bogus_pci_bus.dev, "PCI IRQ handler"); + bogus_pci_bus.number = 0; + + /* mask and clear all interrupts */ + gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2 + 2, 2, + 0xF000); + + for (i = PCI_IRQ_BASE; i < PCI_IRQ_BASE + 4; i++) { + set_irq_chip(i, &gemini_pci_irq_chip); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + } + + set_irq_chained_handler(IRQ_PCI, gemini_pci_irq_handler); + + pci_common_init(&gemini_hw_pci); + + return 0; +} + +subsys_initcall(gemini_pci_init); --- a/arch/arm/mm/abort-ev4.S +++ b/arch/arm/mm/abort-ev4.S @@ -1,5 +1,6 @@ #include #include +#include /* * Function: v4_early_abort * @@ -17,7 +18,7 @@ * abort here if the I-TLB and D-TLB aren't seeing the same * picture. Unfortunately, this does happen. We live with it. */ - .align 5 + .align L1_CACHE_SHIFT ENTRY(v4_early_abort) mrc p15, 0, r1, c5, c0, 0 @ get FSR mrc p15, 0, r0, c6, c0, 0 @ get FAR --- a/arch/arm/mm/abort-nommu.S +++ b/arch/arm/mm/abort-nommu.S @@ -1,5 +1,6 @@ #include #include +#include /* * Function: nommu_early_abort * @@ -12,7 +13,7 @@ * Note: There is no FSR/FAR on !CPU_CP15_MMU cores. * Just fill zero into the registers. */ - .align 5 + .align L1_CACHE_SHIFT ENTRY(nommu_early_abort) mov r0, #0 @ clear r0, r1 (no FSR/FAR) mov r1, #0 --- /dev/null +++ b/drivers/net/gemini_negmac/gm_gmac.c @@ -0,0 +1,1350 @@ +/* + * Ethernet device driver for Gemini SoC. + * + * Copyright (C) 2006, Storlink, Corp. + * Copyright (C) 2008-2009, Paulius Zaleckas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gm_gmac.h" + +/* #define GMAX_TX_INTR_DISABLED */ +#define DO_HW_CHKSUM +/* #define ENABLE_TSO */ +#define GMAC_USE_TXQ0 +/* #define GMAC_LEN_1_2_ISSUE */ + +#define DEFAULT_RXQ_MAX_CNT 256 + +/* define chip information */ +#define DRV_VERSION "0.2" +#define SL351x_DRIVER_NAME "Gemini Ethernet driver " DRV_VERSION + +#ifdef GMAC_LEN_1_2_ISSUE + #define _DEBUG_PREFETCH_NUM 256 +static int _debug_prefetch_cnt; +static char _debug_prefetch_buf[_DEBUG_PREFETCH_NUM][4] __attribute__((aligned(4))); +#endif + +static inline void gmac_write_reg(unsigned int base, unsigned int offset, unsigned int data, unsigned int bit_mask) +{ + unsigned int reg_val; + + reg_val = (__raw_readl(base + offset) & (~bit_mask)) | (data & bit_mask); + __raw_writel(reg_val, base + offset); +} + +/*---------------------------------------------------------------------- +* toe_init_free_queue +* (1) Initialize the Free Queue Descriptor Base Address & size +* Register: TOE_GLOBAL_BASE + 0x0004 +* (2) Initialize DMA Read/Write pointer for +* SW Free Queue and HW Free Queue +* (3) Initialize DMA Descriptors for +* SW Free Queue and HW Free Queue, +*----------------------------------------------------------------------*/ +static void toe_init_free_queue(struct toe_private *toe) +{ + int i; + DMA_RWPTR_T rwptr_reg; + void *desc_buf; + GMAC_RXDESC_T *sw_desc_ptr; + struct sk_buff *skb; + + desc_buf = dma_alloc_coherent(toe->dev, TOE_SW_FREEQ_DESC_NUM * sizeof(GMAC_RXDESC_T), + &toe->sw_freeq_desc_base_dma, GFP_KERNEL); + sw_desc_ptr = (GMAC_RXDESC_T *)desc_buf; + if (!desc_buf) { + dev_err(toe->dev, "%s::DMA ALLOC fail\n", __func__); + return; + } + memset(desc_buf, 0, TOE_SW_FREEQ_DESC_NUM * sizeof(GMAC_RXDESC_T)); + + /* DMA Queue Base & Size */ + __raw_writel((toe->sw_freeq_desc_base_dma & DMA_Q_BASE_MASK) | TOE_SW_FREEQ_DESC_POWER, + toe->global_base + GLOBAL_SW_FREEQ_BASE_SIZE_REG); + + /* init descriptor base */ + toe->swfq_desc_base = (unsigned int)desc_buf; + + /* SW Free Queue Descriptors */ + for (i = 0; i < TOE_SW_FREEQ_DESC_NUM; i++) { + sw_desc_ptr->word0.bits.buffer_size = SW_RX_BUF_SIZE; + skb = dev_alloc_skb(SW_RX_BUF_SIZE); /* allocate socket buffer */ + if (!skb) { + dev_err(toe->dev, "%s::skb buffer allocation fail\n", __func__); + return; + } + REG32(skb->data) = (unsigned int)skb; + skb_reserve(skb, SKB_RESERVE_BYTES); + sw_desc_ptr->word2.buf_adr = dma_map_single(toe->dev, skb->data, + SW_RX_BUF_SIZE - SKB_RESERVE_BYTES, + DMA_FROM_DEVICE); + sw_desc_ptr++; + } + + dma_sync_single_for_device(toe->dev, toe->sw_freeq_desc_base_dma, + TOE_SW_FREEQ_DESC_NUM * sizeof(GMAC_RXDESC_T), + DMA_TO_DEVICE); + + /* SW Free Queue Read/Write Pointer */ + rwptr_reg.bits.wptr = TOE_SW_FREEQ_DESC_NUM - 1; + rwptr_reg.bits.rptr = 0; + __raw_writel(rwptr_reg.bits32, toe->global_base + GLOBAL_SWFQ_RWPTR_REG); + + /* DMA Queue Base & Size */ + __raw_writel(TOE_HW_FREEQ_DESC_POWER, + toe->global_base + GLOBAL_HW_FREEQ_BASE_SIZE_REG); + rwptr_reg.bits.wptr = TOE_HW_FREEQ_DESC_NUM - 1; + rwptr_reg.bits.rptr = 0; + __raw_writel(rwptr_reg.bits32, toe->global_base + GLOBAL_HWFQ_RWPTR_REG); +} + +/*---------------------------------------------------------------------- +* toe_init_swtx_queue +* (2) Initialize the GMAC 0/1 SW TXQ Queue Descriptor Base Address & sizeup +* GMAC_SW_TX_QUEUE_BASE_REG(0x0050) +* (2) Initialize DMA Read/Write pointer for +* GMAC 0/1 SW TX Q0-5 +*----------------------------------------------------------------------*/ +static void toe_init_swtx_queue(struct net_device *dev) +{ + int i; + struct gmac_private *gmac = netdev_priv(dev); + struct toe_private *toe = dev->ml_priv; + DMA_RWPTR_T rwptr_reg; + unsigned int rwptr_addr; + void *desc_buf; + unsigned int offset; + + desc_buf = dma_alloc_coherent(toe->dev, TOE_GMAC_SWTXQ_DESC_NUM * TOE_SW_TXQ_NUM * sizeof(GMAC_TXDESC_T), + &gmac->swtxq_desc_base_dma, GFP_KERNEL); + gmac->swtxq_desc_base = (unsigned int)desc_buf; + if (!desc_buf) { + dev_err(toe->dev, "%s::DMA ALLOC fail\n", __func__); + return; + } + memset(desc_buf, 0, TOE_GMAC_SWTXQ_DESC_NUM * TOE_SW_TXQ_NUM * sizeof(GMAC_TXDESC_T)); + dma_sync_single_for_device(toe->dev, gmac->swtxq_desc_base_dma, + TOE_GMAC_SWTXQ_DESC_NUM * TOE_SW_TXQ_NUM * sizeof(GMAC_TXDESC_T), + DMA_TO_DEVICE); + __raw_writel((gmac->swtxq_desc_base_dma & DMA_Q_BASE_MASK) | TOE_GMAC_SWTXQ_DESC_POWER, + gmac->dma_base_addr + GMAC_SW_TX_QUEUE_BASE_REG); + + /* GMAC0 SW TX Q0-Q5 */ + offset = 0; + rwptr_reg.bits.wptr = 0; + rwptr_reg.bits.rptr = 0; + rwptr_addr = gmac->dma_base_addr + GMAC_SW_TX_QUEUE0_PTR_REG; + for (i = 0; i < TOE_SW_TXQ_NUM; i++) { + gmac->swtxq[i].rwptr_reg = rwptr_addr; + gmac->swtxq[i].desc_base_dma = (unsigned int)gmac->swtxq_desc_base_dma + offset; + gmac->swtxq[i].desc_base = (unsigned int)desc_buf + offset; + offset += TOE_GMAC_SWTXQ_DESC_NUM * sizeof(GMAC_TXDESC_T); + __raw_writel(rwptr_reg.bits32, rwptr_addr); + rwptr_addr += 4; + } +} + +/*---------------------------------------------------------------------- +* toe_init_default_queue +* (1) Initialize the default 0/1 Queue Header +* Register: TOE_DEFAULT_Q0_HDR_BASE (0x60002000) +* TOE_DEFAULT_Q1_HDR_BASE (0x60002008) +* (2) Initialize Descriptors of Default Queue 0/1 +*----------------------------------------------------------------------*/ +static void toe_init_default_queue(struct net_device *dev) +{ + struct gmac_private *gmac = netdev_priv(dev); + struct toe_private *toe = dev->ml_priv; + volatile NONTOE_QHDR_T *qhdr; + GMAC_RXDESC_T *desc_ptr; + + desc_ptr = dma_alloc_coherent(toe->dev, TOE_DEFAULT_Q_DESC_NUM * sizeof(GMAC_RXDESC_T), + &gmac->default_desc_base_dma, GFP_KERNEL); + if (!desc_ptr) { + dev_err(toe->dev, "%s::DMA ALLOC fail\n", __func__); + return; + } + memset(desc_ptr, 0, TOE_DEFAULT_Q_DESC_NUM * sizeof(GMAC_RXDESC_T)); + dma_sync_single_for_device(toe->dev, gmac->default_desc_base_dma, + TOE_DEFAULT_Q_DESC_NUM * sizeof(GMAC_RXDESC_T), + DMA_TO_DEVICE); + gmac->default_desc_base = (unsigned int)desc_ptr; + qhdr = (volatile NONTOE_QHDR_T *)(toe->global_base + TOE_DEFAULT_Q_HDR_BASE(gmac->port_id)); + qhdr->word0.base_size = ((unsigned int)gmac->default_desc_base_dma & NONTOE_QHDR0_BASE_MASK) | TOE_DEFAULT_Q_DESC_POWER; + qhdr->word1.bits32 = 0; + gmac->default_qhdr = (NONTOE_QHDR_T *)qhdr; +} + +/*---------------------------------------------------------------------- +* toe_init_interrupt_config +* Interrupt Select Registers are used to map interrupt to int0 or int1 +* Int0 and int1 are wired to CPU 0/1 GMAC 0/1 +* Interrupt Device Inteface data are used to pass device info to +* upper device driver or store status/statistics +* ISR handler +* (1) If status bit ON but masked, the prinf error message (bug issue) +* (2) If select bits are for me, handle it, else skip to let +* the other ISR handles it. +* Notes: +* GMACx init routine (for eCOS) or open routine (for Linux) +* enable the interrupt bits only which are selected for it. +* +* Default Setting: +* GMAC0 intr bits ------> int0 ----> eth0 +* GMAC1 intr bits ------> int1 ----> eth1 +* TOE intr -------------> int0 ----> eth0 +* Classification Intr --> int0 ----> eth0 +* Default Q0 -----------> int0 ----> eth0 +* Default Q1 -----------> int1 ----> eth1 +*----------------------------------------------------------------------*/ +static void toe_init_interrupt_config(struct toe_private *toe) +{ + /* clear all status bits */ + __raw_writel(0xffffffff, toe->global_base + GLOBAL_INTERRUPT_STATUS_0_REG); + __raw_writel(0xffffffff, toe->global_base + GLOBAL_INTERRUPT_STATUS_1_REG); + __raw_writel(0xffffffff, toe->global_base + GLOBAL_INTERRUPT_STATUS_2_REG); + __raw_writel(0xffffffff, toe->global_base + GLOBAL_INTERRUPT_STATUS_3_REG); + __raw_writel(0xffffffff, toe->global_base + GLOBAL_INTERRUPT_STATUS_4_REG); + + /* Init select registers */ + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_SELECT_0_REG); + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_SELECT_1_REG); + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_SELECT_2_REG); + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_SELECT_3_REG); + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_SELECT_4_REG); + + /* disable all interrupt */ + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_ENABLE_0_REG); + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_ENABLE_1_REG); + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_ENABLE_2_REG); + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_ENABLE_3_REG); + __raw_writel(0, toe->global_base + GLOBAL_INTERRUPT_ENABLE_4_REG); +} + +static void toe_gmac_hw_start(struct gmac_private *gmac) +{ + GMAC_DMA_CTRL_T dma_ctrl; + + /* program dma control register */ + dma_ctrl.bits32 = __raw_readl(gmac->dma_base_addr + GMAC_DMA_CTRL_REG); + dma_ctrl.bits.rd_enable = 1; + dma_ctrl.bits.td_enable = 1; + dma_ctrl.bits.loopback = 0; + dma_ctrl.bits.drop_small_ack = 0; + dma_ctrl.bits.rd_prot = 0; + dma_ctrl.bits.rd_burst_size = 3; + dma_ctrl.bits.rd_insert_bytes = RX_INSERT_BYTES; + dma_ctrl.bits.rd_bus = 3; + dma_ctrl.bits.td_prot = 0; + dma_ctrl.bits.td_burst_size = 3; + dma_ctrl.bits.td_bus = 3; + + __raw_writel(dma_ctrl.bits32, gmac->dma_base_addr + GMAC_DMA_CTRL_REG); +} + +static void toe_gmac_hw_stop(struct gmac_private *gmac) +{ + GMAC_DMA_CTRL_T dma_ctrl; + + /* program dma control register */ + dma_ctrl.bits32 = __raw_readl(gmac->dma_base_addr + GMAC_DMA_CTRL_REG); + dma_ctrl.bits.rd_enable = 0; + dma_ctrl.bits.td_enable = 0; + __raw_writel(dma_ctrl.bits32, gmac->dma_base_addr + GMAC_DMA_CTRL_REG); +} + +static void toe_gmac_init_chip(struct net_device *dev) +{ + struct gmac_private *gmac = netdev_priv(dev); + GMAC_CONFIG2_T config2_val; + GMAC_CONFIG0_T config0; + GMAC_CONFIG1_T config1; + GMAC_STATUS_T status; + GMAC_TX_WCR0_T hw_weigh; + GMAC_TX_WCR1_T sw_weigh; + GMAC_RX_FLTR_T rx_filter; + + /* set RX_FLTR register to receive all multicast packet */ + rx_filter.bits32 = __raw_readl(dev->base_addr + GMAC_RX_FLTR); + rx_filter.bits.unicast = 1; + rx_filter.bits.multicast = 1; + rx_filter.bits.broadcast = 1; + __raw_writel(rx_filter.bits32, dev->base_addr + GMAC_RX_FLTR); + + /* set flow control threshold */ + config1.bits32 = 0; + config1.bits.set_threshold = 32 / 2; + config1.bits.rel_threshold = 32 / 4 * 3; + __raw_writel(config1.bits32, dev->base_addr + GMAC_CONFIG1); + + /* set flow control threshold */ + config2_val.bits32 = 0; + config2_val.bits.set_threshold = TOE_SW_FREEQ_DESC_NUM / 4; + config2_val.bits.rel_threshold = TOE_SW_FREEQ_DESC_NUM / 2; + __raw_writel(config2_val.bits32, dev->base_addr + GMAC_CONFIG2); + + /* disable TX/RX and disable internal loop back */ + config0.bits32 = __raw_readl(dev->base_addr + GMAC_CONFIG0); + + config0.bits.max_len = 2; + + gmac->flow_control_enable = 0; + + config0.bits.tx_fc_en = 0; /* disable tx flow control */ + config0.bits.rx_fc_en = 0; /* disable rx flow control */ + config0.bits.dis_rx = 1; /* disable rx */ + config0.bits.dis_tx = 1; /* disable tx */ + config0.bits.loop_back = 0; /* enable/disable GMAC loopback */ + config0.bits.rx_err_detect = 1; + config0.bits.rgmii_en = 0; + config0.bits.rgmm_edge = 1; + config0.bits.rxc_inv = 0; + config0.bits.ipv4_rx_chksum = 1; /* enable H/W to check ip checksum */ + config0.bits.ipv6_rx_chksum = 1; /* enable H/W to check ip checksum */ + config0.bits.port0_chk_hwq = 1; + config0.bits.port1_chk_hwq = 1; + config0.bits.port0_chk_toeq = 1; + config0.bits.port1_chk_toeq = 1; + config0.bits.port0_chk_classq = 1; + config0.bits.port1_chk_classq = 1; + + __raw_writel(config0.bits32, dev->base_addr + GMAC_CONFIG0); + + hw_weigh.bits32 = 0; + hw_weigh.bits.hw_tq3 = 1; + hw_weigh.bits.hw_tq2 = 1; + hw_weigh.bits.hw_tq1 = 1; + hw_weigh.bits.hw_tq0 = 1; + __raw_writel(hw_weigh.bits32, gmac->dma_base_addr + GMAC_TX_WEIGHTING_CTRL_0_REG); + + sw_weigh.bits32 = 0; + sw_weigh.bits.sw_tq5 = 1; + sw_weigh.bits.sw_tq4 = 1; + sw_weigh.bits.sw_tq3 = 1; + sw_weigh.bits.sw_tq2 = 1; + sw_weigh.bits.sw_tq1 = 1; + sw_weigh.bits.sw_tq0 = 1; + __raw_writel(sw_weigh.bits32, gmac->dma_base_addr + GMAC_TX_WEIGHTING_CTRL_1_REG); + + /* set interface type */ + status.bits32 = __raw_readl(dev->base_addr + GMAC_STATUS); + + switch (gmac->phydev->interface) { + case PHY_INTERFACE_MODE_MII: + status.bits.mii_rmii = GMAC_PHY_MII; + break; + case PHY_INTERFACE_MODE_GMII: + status.bits.mii_rmii = GMAC_PHY_GMII; + break; + case PHY_INTERFACE_MODE_RGMII: + status.bits.mii_rmii = GMAC_PHY_RGMII_100_10; + break; + default: + dev_err(&dev->dev, "Unsupported MII interface\n"); + return; + } + + __raw_writel(status.bits32, dev->base_addr + GMAC_STATUS); +} + +static void toe_init_gmac(struct net_device *dev) +{ + struct gmac_private *gmac = netdev_priv(dev); + struct toe_private *toe = dev->ml_priv; + u32 data; + + /* GMAC initialization */ + toe_gmac_init_chip(dev); + + /* ----------------------------------------------------------- + Enable GMAC interrupt & disable loopback + Notes: + GMACx init routine (for eCOS) or open routine (for Linux) + enable the interrupt bits only which are selected for him. + --------------------------------------------------------------*/ + + /* Enable Interrupt Bits */ + if (gmac->port_id == 0) { + gmac->intr0_selected = GMAC0_TXDERR_INT_BIT | GMAC0_TXPERR_INT_BIT | + GMAC0_RXDERR_INT_BIT | GMAC0_RXPERR_INT_BIT | + GMAC0_SWTQ05_FIN_INT_BIT | GMAC0_SWTQ05_EOF_INT_BIT | + GMAC0_SWTQ04_FIN_INT_BIT | GMAC0_SWTQ04_EOF_INT_BIT | + GMAC0_SWTQ03_FIN_INT_BIT | GMAC0_SWTQ03_EOF_INT_BIT | + GMAC0_SWTQ02_FIN_INT_BIT | GMAC0_SWTQ02_EOF_INT_BIT | + GMAC0_SWTQ01_FIN_INT_BIT | GMAC0_SWTQ01_EOF_INT_BIT | + GMAC0_SWTQ00_FIN_INT_BIT | GMAC0_SWTQ00_EOF_INT_BIT; + +#ifdef GMAX_TX_INTR_DISABLED + gmac->intr0_enabled = 0; +#else + gmac->intr0_enabled = GMAC0_SWTQ00_FIN_INT_BIT | GMAC0_SWTQ00_EOF_INT_BIT; +#endif + + gmac->intr1_selected = TOE_IQ_ALL_BITS | TOE_CLASS_RX_INT_BITS | + GMAC0_HWTQ03_EOF_INT_BIT | GMAC0_HWTQ02_EOF_INT_BIT | + GMAC0_HWTQ01_EOF_INT_BIT | GMAC0_HWTQ00_EOF_INT_BIT | + DEFAULT_Q0_INT_BIT; + gmac->intr1_enabled = DEFAULT_Q0_INT_BIT | TOE_IQ_ALL_BITS; + gmac->intr2_selected = 0xffffffff; /* TOE Queue 32-63 FUUL Intr */ + gmac->intr2_enabled = 0xffffffff; + gmac->intr3_selected = 0xffffffff; /* TOE Queue 0-31 FUUL Intr */ + gmac->intr3_enabled = 0xffffffff; + gmac->intr4_selected = GMAC0_INT_BITS | CLASS_RX_FULL_INT_BITS | + HWFQ_EMPTY_INT_BIT | SWFQ_EMPTY_INT_BIT; + gmac->intr4_enabled = GMAC0_INT_BITS | SWFQ_EMPTY_INT_BIT; + + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_0_REG) & ~gmac->intr0_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_0_REG); + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_1_REG) & ~gmac->intr1_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_1_REG); + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_2_REG) & ~gmac->intr2_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_2_REG); + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_3_REG) & ~gmac->intr3_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_3_REG); + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_4_REG) & ~gmac->intr4_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_4_REG); + } else { + gmac->intr0_selected = GMAC1_TXDERR_INT_BIT | GMAC1_TXPERR_INT_BIT | + GMAC1_RXDERR_INT_BIT | GMAC1_RXPERR_INT_BIT | + GMAC1_SWTQ15_FIN_INT_BIT | GMAC1_SWTQ15_EOF_INT_BIT | + GMAC1_SWTQ14_FIN_INT_BIT | GMAC1_SWTQ14_EOF_INT_BIT | + GMAC1_SWTQ13_FIN_INT_BIT | GMAC1_SWTQ13_EOF_INT_BIT | + GMAC1_SWTQ12_FIN_INT_BIT | GMAC1_SWTQ12_EOF_INT_BIT | + GMAC1_SWTQ11_FIN_INT_BIT | GMAC1_SWTQ11_EOF_INT_BIT | + GMAC1_SWTQ10_FIN_INT_BIT | GMAC1_SWTQ10_EOF_INT_BIT; +#ifdef GMAX_TX_INTR_DISABLED + gmac->intr0_enabled = 0; +#else + gmac->intr0_enabled = GMAC1_SWTQ10_FIN_INT_BIT | GMAC1_SWTQ10_EOF_INT_BIT; +#endif + + gmac->intr1_selected = DEFAULT_Q1_INT_BIT; + gmac->intr1_enabled = DEFAULT_Q1_INT_BIT | TOE_IQ_ALL_BITS; + gmac->intr2_selected = 0; /* TOE Queue 32-63 FUUL Intr */ + gmac->intr2_enabled = 0; + gmac->intr3_selected = 0; /* TOE Queue 0-31 FUUL Intr */ + gmac->intr3_enabled = 0; + gmac->intr4_selected = GMAC1_INT_BITS; + gmac->intr4_enabled = GMAC1_INT_BITS; + + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_0_REG) | gmac->intr0_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_0_REG); + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_1_REG) | gmac->intr1_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_1_REG); + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_2_REG) | gmac->intr2_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_2_REG); + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_3_REG) | gmac->intr3_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_3_REG); + data = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_SELECT_4_REG) | gmac->intr4_selected; + __raw_writel(data, toe->global_base + GLOBAL_INTERRUPT_SELECT_4_REG); + } + + /* enable only selected bits */ + gmac_write_reg(toe->global_base, GLOBAL_INTERRUPT_ENABLE_0_REG, + gmac->intr0_enabled, gmac->intr0_selected); + gmac_write_reg(toe->global_base, GLOBAL_INTERRUPT_ENABLE_1_REG, + gmac->intr1_enabled, gmac->intr1_selected); + gmac_write_reg(toe->global_base, GLOBAL_INTERRUPT_ENABLE_2_REG, + gmac->intr2_enabled, gmac->intr2_selected); + gmac_write_reg(toe->global_base, GLOBAL_INTERRUPT_ENABLE_3_REG, + gmac->intr3_enabled, gmac->intr3_selected); + gmac_write_reg(toe->global_base, GLOBAL_INTERRUPT_ENABLE_4_REG, + gmac->intr4_enabled, gmac->intr4_selected); + + /* start DMA process */ + toe_gmac_hw_start(gmac); +} + +static void toe_gmac_enable_tx_rx(struct net_device *dev) +{ + GMAC_CONFIG0_T config0; + + /* enable TX/RX */ + config0.bits32 = __raw_readl(dev->base_addr + GMAC_CONFIG0); + config0.bits.dis_rx = 0; /* enable rx */ + config0.bits.dis_tx = 0; /* enable tx */ + __raw_writel(config0.bits32, dev->base_addr + GMAC_CONFIG0); +} + +static void toe_gmac_disable_tx_rx(struct net_device *dev) +{ + GMAC_CONFIG0_T config0; + + /* enable TX/RX */ + config0.bits32 = __raw_readl(dev->base_addr + GMAC_CONFIG0); + config0.bits.dis_rx = 1; /* disable rx */ + config0.bits.dis_tx = 1; /* disable tx */ + __raw_writel(config0.bits32, dev->base_addr + GMAC_CONFIG0); +} + +static void toe_gmac_tx_complete(struct net_device *dev, unsigned int tx_qid) +{ + struct gmac_private *gmac = netdev_priv(dev); + struct toe_private *toe = dev->ml_priv; + GMAC_TXDESC_T *curr_desc; + GMAC_TXDESC_0_T word0; + GMAC_TXDESC_1_T word1; + unsigned int desc_count; + GMAC_SWTXQ_T *swtxq; + DMA_RWPTR_T rwptr; + + /* get tx H/W completed descriptor virtual address */ + /* check tx status and accumulate tx statistics */ + swtxq = &gmac->swtxq[tx_qid]; + for (;;) { + rwptr.bits32 = __raw_readl(swtxq->rwptr_reg); + if (rwptr.bits.rptr == swtxq->finished_idx) + break; + curr_desc = (GMAC_TXDESC_T *)swtxq->desc_base + swtxq->finished_idx; + dma_sync_single_range_for_device(toe->dev, swtxq->desc_base_dma, + swtxq->finished_idx * sizeof(GMAC_TXDESC_T), + sizeof(GMAC_TXDESC_T), + DMA_FROM_DEVICE); + word0.bits32 = curr_desc->word0.bits32; + word1.bits32 = curr_desc->word1.bits32; + + if (word0.bits.status_tx_ok) { + dev->stats.tx_bytes += word1.bits.byte_count; + desc_count = word0.bits.desc_count; + if (desc_count == 0) { + dev_err(&dev->dev, "%s::Desc 0x%x = 0x%x, desc_count=%d\n", __func__, (u32)curr_desc, word0.bits32, desc_count); + BUG(); + } + while (--desc_count) { + word0.bits.status_tx_ok = 0; + curr_desc->word0.bits32 = word0.bits32; + dma_sync_single_range_for_device(toe->dev, swtxq->desc_base_dma, + swtxq->finished_idx * sizeof(GMAC_TXDESC_T), + sizeof(GMAC_TXDESC_T), + DMA_TO_DEVICE); + swtxq->finished_idx = RWPTR_ADVANCE_ONE(swtxq->finished_idx, TOE_GMAC_SWTXQ_DESC_NUM); + curr_desc = (GMAC_TXDESC_T *)swtxq->desc_base + swtxq->finished_idx; + dma_sync_single_range_for_device(toe->dev, swtxq->desc_base_dma, + swtxq->finished_idx * sizeof(GMAC_TXDESC_T), + sizeof(GMAC_TXDESC_T), + DMA_FROM_DEVICE); + word0.bits32 = curr_desc->word0.bits32; + } + + word0.bits.status_tx_ok = 0; + dev_kfree_skb_any(swtxq->tx_skb[swtxq->finished_idx]); + swtxq->tx_skb[swtxq->finished_idx] = NULL; + + curr_desc->word0.bits32 = word0.bits32; + dma_sync_single_range_for_device(toe->dev, swtxq->desc_base_dma, + swtxq->finished_idx * sizeof(GMAC_TXDESC_T), + sizeof(GMAC_TXDESC_T), + DMA_TO_DEVICE); + dev->stats.tx_packets++; + swtxq->finished_idx = RWPTR_ADVANCE_ONE(swtxq->finished_idx, TOE_GMAC_SWTXQ_DESC_NUM); + } else { + break; + } + } + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); +} + +static int gmac_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct gmac_private *gmac = netdev_priv(dev); + struct toe_private *toe = dev->ml_priv; + DMA_RWPTR_T rwptr; + GMAC_TXDESC_T *curr_desc; + int snd_pages = skb_shinfo(skb)->nr_frags + 1; /* get number of descriptor */ + int frag_id = 0; + int len, total_len = skb->len; + struct net_device_stats *isPtr = &dev->stats; + unsigned int free_desc; + GMAC_SWTXQ_T *swtxq; + register unsigned long word0, word1, word2, word3; + unsigned short wptr, rptr; + +#ifdef GMAC_LEN_1_2_ISSUE + int total_pages; + total_pages = snd_pages; +#endif + + if (skb->len >= 0x10000) { + isPtr->tx_dropped++; + dev_err(&dev->dev, "%s::skb->len %d >= 64K\n", __func__, skb->len); + netif_stop_queue(dev); + return 1; + } + +#ifdef GMAC_USE_TXQ0 + #define tx_qid 0 +#endif + + swtxq = &gmac->swtxq[tx_qid]; + + rwptr.bits32 = __raw_readl(swtxq->rwptr_reg); + wptr = rwptr.bits.wptr; + rptr = rwptr.bits.rptr; + + /* + * check finished desc or empty BD + * cannot check by read ptr of RW PTR register, + * because the HW complete to send but the SW may NOT handle it + */ +#ifdef GMAX_TX_INTR_DISABLED + toe_gmac_tx_complete(dev, tx_qid); +#endif + if (wptr >= swtxq->finished_idx) + free_desc = TOE_GMAC_SWTXQ_DESC_NUM - wptr + swtxq->finished_idx; + else + free_desc = swtxq->finished_idx - wptr; + + if (free_desc < snd_pages) { + isPtr->tx_dropped++; + netif_stop_queue(dev); + return 1; + } + + while (snd_pages) { + char *pkt_datap; + + curr_desc = (GMAC_TXDESC_T *)swtxq->desc_base + wptr; + if (frag_id == 0) { + len = skb_headlen(skb); + pkt_datap = dma_map_single(toe->dev, skb->data, len, DMA_TO_DEVICE); + } else { + skb_frag_t *frag = &skb_shinfo(skb)->frags[frag_id - 1]; + len = frag->size; + pkt_datap = dma_map_page(toe->dev, frag->page, frag->page_offset, len, DMA_TO_DEVICE); + } + + /* set TX descriptor */ + word0 = len; + word3 = (dev->mtu + 14) | EOFIE_BIT; + +#ifdef DO_HW_CHKSUM + if (total_len <= 1514 && ip_hdr(skb) && (ip_hdr(skb)->frag_off & __constant_htons(0x3fff))) + word1 = total_len | + TSS_IP_CHKSUM_BIT | + TSS_IPV6_ENABLE_BIT | + TSS_MTU_ENABLE_BIT; + else + word1 = total_len | + TSS_UDP_CHKSUM_BIT | + TSS_TCP_CHKSUM_BIT | + TSS_IP_CHKSUM_BIT | + TSS_IPV6_ENABLE_BIT | + TSS_MTU_ENABLE_BIT; +#else + word1 = total_len | TSS_MTU_ENABLE_BIT; +#endif + word2 = pkt_datap; + + if (frag_id == 0) + word3 |= SOF_BIT; + + if (snd_pages == 1) { + word3 |= EOF_BIT; + swtxq->tx_skb[wptr] = skb; + } else + swtxq->tx_skb[wptr] = NULL; + +#ifdef GMAC_LEN_1_2_ISSUE + if ((total_pages != snd_pages) && (len == 1 || len == 2) && ((u32)pkt_datap & 0x03)) { + memcpy((void *)&_debug_prefetch_buf[_debug_prefetch_cnt][0], pkt_datap, len); + pkt_datap = (char *)&_debug_prefetch_buf[_debug_prefetch_cnt][0]; + word2 = (unsigned long)__pa(pkt_datap); + _debug_prefetch_cnt++; + if (_debug_prefetch_cnt >= _DEBUG_PREFETCH_NUM) + _debug_prefetch_cnt = 0; + } +#endif + curr_desc->word0.bits32 = word0; + curr_desc->word1.bits32 = word1; + curr_desc->word2.bits32 = word2; + curr_desc->word3.bits32 = word3; + free_desc--; + dma_sync_single_range_for_device(toe->dev, swtxq->desc_base_dma, + wptr * sizeof(GMAC_TXDESC_T), + sizeof(GMAC_TXDESC_T), + DMA_TO_DEVICE); + wptr = RWPTR_ADVANCE_ONE(wptr, TOE_GMAC_SWTXQ_DESC_NUM); + frag_id++; + snd_pages--; + } + + SET_WPTR(swtxq->rwptr_reg, wptr); + dev->trans_start = jiffies; + + return 0; +} + +static void __gmac_set_mac_address(struct net_device *dev) +{ + unsigned int reg_val; + + reg_val = dev->dev_addr[0] + (dev->dev_addr[1] << 8) + + (dev->dev_addr[2] << 16) + (dev->dev_addr[3] << 24); + __raw_writel(reg_val, dev->base_addr + GMAC_STA_ADD0); + reg_val = (__raw_readl(dev->base_addr + GMAC_STA_ADD1) & 0xFFFF0000) + + dev->dev_addr[4] + (dev->dev_addr[5] << 8); + __raw_writel(reg_val, dev->base_addr + GMAC_STA_ADD1); +} + +static int gmac_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = addr; + + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + + __gmac_set_mac_address(dev); + + return 0; +} + +static void gmac_get_mac_address(struct net_device *dev) +{ + unsigned int reg_val; + + reg_val = __raw_readl(dev->base_addr + GMAC_STA_ADD0); + dev->dev_addr[0] = reg_val & 0xFF; + dev->dev_addr[1] = (reg_val >> 8) & 0xFF; + dev->dev_addr[2] = (reg_val >> 16) & 0xFF; + dev->dev_addr[3] = (reg_val >> 24) & 0xFF; + reg_val = __raw_readl(dev->base_addr + GMAC_STA_ADD1); + dev->dev_addr[4] = reg_val & 0xFF; + dev->dev_addr[5] = (reg_val >> 8) & 0xFF; + + if (!is_valid_ether_addr(dev->dev_addr)) { + random_ether_addr(dev->dev_addr); + __gmac_set_mac_address(dev); + } +} + +struct net_device_stats *gmac_get_stats(struct net_device *dev) +{ + if (netif_running(dev)) { + unsigned short multicast; + + multicast = __raw_readw(dev->base_addr + GMAC_IN_MCAST) + + __raw_readw(dev->base_addr + GMAC_IN_BCAST); + + dev->stats.rx_dropped += __raw_readw(dev->base_addr + GMAC_IN_DISCARDS); + dev->stats.rx_errors += __raw_readw(dev->base_addr + GMAC_IN_ERRORS); + dev->stats.rx_packets += __raw_readl(dev->base_addr + GMAC_IN_MAC1) + multicast; + dev->stats.multicast += multicast; + } + + return &dev->stats; +} + +/* TODO: If possible use crc32 from kernel lib */ +static unsigned const ethernet_polynomial = 0x04c11db7U; +static unsigned int ether_crc(int length, unsigned char *data) +{ + int crc = -1; + unsigned int i; + unsigned int crc_val = 0; + + while (--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) + crc = (crc << 1) ^ ((crc < 0) ^ (current_octet & 1) ? + ethernet_polynomial : 0); + } + crc = ~crc; + for (i = 0; i < 32; i++) + crc_val = crc_val + (((crc << i) & 0x80000000) >> (31 - i)); + + return crc_val; +} + +/*---------------------------------------------------------------------- +* toe_gmac_fill_free_q +* allocate buffers for free queue. +*----------------------------------------------------------------------*/ +static void toe_gmac_fill_free_q(struct toe_private *toe) +{ + struct sk_buff *skb; + DMA_RWPTR_T fq_rwptr; + GMAC_RXDESC_T *fq_desc; + unsigned long flags; + + spin_lock_irqsave(&toe->freeq_lock, flags); + fq_rwptr.bits32 = __raw_readl(toe->global_base + GLOBAL_SWFQ_RWPTR_REG); + while ((unsigned short)RWPTR_ADVANCE_ONE(fq_rwptr.bits.wptr, + TOE_SW_FREEQ_DESC_NUM) != fq_rwptr.bits.rptr) { + skb = dev_alloc_skb(SW_RX_BUF_SIZE); + if (skb == NULL) { + dev_err(toe->dev, "%s::skb allocation fail\n", __func__); + break; + } + REG32(skb->data) = (unsigned int)skb; + skb_reserve(skb, SKB_RESERVE_BYTES); + fq_rwptr.bits.wptr = RWPTR_ADVANCE_ONE(fq_rwptr.bits.wptr, + TOE_SW_FREEQ_DESC_NUM); + fq_desc = (GMAC_RXDESC_T *)toe->swfq_desc_base + fq_rwptr.bits.wptr; + fq_desc->word2.buf_adr = dma_map_single(toe->dev, skb->data, + SW_RX_BUF_SIZE - SKB_RESERVE_BYTES, + DMA_FROM_DEVICE); + dma_sync_single_range_for_device(toe->dev, + toe->sw_freeq_desc_base_dma, + fq_rwptr.bits.wptr * sizeof(GMAC_RXDESC_T), + sizeof(GMAC_RXDESC_T), + DMA_TO_DEVICE); + SET_WPTR(toe->global_base + GLOBAL_SWFQ_RWPTR_REG, fq_rwptr.bits.wptr); + } + spin_unlock_irqrestore(&toe->freeq_lock, flags); +} + +static void fill_free_q_worker(struct work_struct *work) +{ + struct toe_private *toe = container_of(work, struct toe_private, freq_work); + + toe_gmac_fill_free_q(toe); +} + +/*---------------------------------------------------------------------- +* toe_gmac_handle_default_rxq +* (1) Get rx Buffer for default Rx queue +* (2) notify or call upper-routine to handle it +* (3) get a new buffer and insert it into SW free queue +* (4) Note: The SW free queue Read-Write Pointer should be locked when accessing +*----------------------------------------------------------------------*/ +static void toe_gmac_handle_default_rxq(struct net_device *dev) +{ + struct gmac_private *gmac = netdev_priv(dev); + struct toe_private *toe = dev->ml_priv; + GMAC_RXDESC_T *curr_desc; + struct sk_buff *skb; + DMA_RWPTR_T rwptr; + unsigned int pkt_size; + int max_cnt; + unsigned int desc_count; + unsigned int chksum_status, rx_status; + struct net_device_stats *isPtr = &dev->stats; + + rwptr.bits32 = __raw_readl(&gmac->default_qhdr->word1); + max_cnt = DEFAULT_RXQ_MAX_CNT; + while ((--max_cnt) && rwptr.bits.rptr != rwptr.bits.wptr) { + curr_desc = (GMAC_RXDESC_T *)gmac->default_desc_base + rwptr.bits.rptr; + dma_sync_single_range_for_device(toe->dev, + gmac->default_desc_base_dma, + rwptr.bits.rptr * sizeof(GMAC_RXDESC_T), + sizeof(GMAC_RXDESC_T), + DMA_FROM_DEVICE); + rx_status = curr_desc->word0.bits.status; + chksum_status = curr_desc->word0.bits.chksum_status; + pkt_size = curr_desc->word1.bits.byte_count; /* total byte count in a frame */ + desc_count = curr_desc->word0.bits.desc_count; /* get descriptor count per frame */ + skb = (struct sk_buff *)(REG32(__va(curr_desc->word2.buf_adr) - SKB_RESERVE_BYTES)); + + if ((curr_desc->word0.bits32 & (GMAC_RXDESC_0_T_derr | GMAC_RXDESC_0_T_perr)) + || (pkt_size < 60) || (chksum_status & 0x4) || rx_status) { + if (curr_desc->word0.bits32 & GMAC_RXDESC_0_T_derr) + dev_err(&dev->dev, "%s::derr\n", __func__); + if (curr_desc->word0.bits32 & GMAC_RXDESC_0_T_perr) + dev_err(&dev->dev, "%s::perr\n", __func__); + if (rx_status && (rx_status == 4 || rx_status == 7)) + isPtr->rx_crc_errors++; + + dev_kfree_skb_irq(skb); + goto bad_frame; + } + + if (curr_desc->word0.bits.drop) + dev_warn(&dev->dev, "%s::Drop\n", __func__); + + /* get frame information from the first descriptor of the frame */ + skb_reserve(skb, RX_INSERT_BYTES); /* 16 byte align the IP fields. */ + skb_put(skb, pkt_size); + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + if (chksum_status == RX_CHKSUM_IP_UDP_TCP_OK || chksum_status == RX_CHKSUM_IP_OK_ONLY) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + netif_rx(skb); /* socket rx */ + dev->last_rx = jiffies; + + isPtr->rx_bytes += pkt_size; + +bad_frame: + /* advance one for Rx default Q 0/1 */ + rwptr.bits.rptr = RWPTR_ADVANCE_ONE(rwptr.bits.rptr, TOE_DEFAULT_Q_DESC_NUM); + SET_RPTR(&gmac->default_qhdr->word1, rwptr.bits.rptr); + } + + schedule_work(&toe->freq_work); +} + +static irqreturn_t toe_gmac_interrupt(int irq, void *dev_instance) +{ + struct net_device *dev = dev_instance; + struct gmac_private *gmac = netdev_priv(dev); + struct toe_private *toe = dev->ml_priv; + unsigned int status0; + unsigned int status1; + unsigned int status2; + unsigned int status3; + unsigned int status4; + int handled = 0; + + /* read Interrupt status */ + status0 = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_STATUS_0_REG); + status1 = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_STATUS_1_REG); + status2 = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_STATUS_2_REG); + status3 = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_STATUS_3_REG); + status4 = __raw_readl(toe->global_base + GLOBAL_INTERRUPT_STATUS_4_REG); + + /* clear interrupts */ + if (status0) + __raw_writel(status0, toe->global_base + GLOBAL_INTERRUPT_STATUS_0_REG); + if (status1) + __raw_writel(status1, toe->global_base + GLOBAL_INTERRUPT_STATUS_1_REG); + if (status2) + __raw_writel(status2, toe->global_base + GLOBAL_INTERRUPT_STATUS_2_REG); + if (status3) + __raw_writel(status3, toe->global_base + GLOBAL_INTERRUPT_STATUS_3_REG); + if (status4) + __raw_writel(status4, toe->global_base + GLOBAL_INTERRUPT_STATUS_4_REG); + + /* handle freeq interrupt first */ + if (status4 & gmac->intr4_enabled) { + if ((status4 & SWFQ_EMPTY_INT_BIT) && (gmac->intr4_enabled & SWFQ_EMPTY_INT_BIT)) { + toe_gmac_fill_free_q(toe); + handled = 1; + } + } + + /* Interrupt Status 1 */ + if (status1 & gmac->intr1_enabled) { + /* + * Handle GMAC 0/1 HW Tx queue 0-3 EOF events + * Only count + * TOE, Classification, and default queues interrupts are handled by ISR + * because they should pass packets to upper layer + */ + if (gmac->port_id == 0) { + if (netif_running(dev) && (status1 & DEFAULT_Q0_INT_BIT) && (gmac->intr1_enabled & DEFAULT_Q0_INT_BIT)) { + toe_gmac_handle_default_rxq(dev); + handled = 1; + } + } else if (gmac->port_id == 1) { + if (netif_running(dev) && (status1 & DEFAULT_Q1_INT_BIT) && (gmac->intr1_enabled & DEFAULT_Q1_INT_BIT)) { + toe_gmac_handle_default_rxq(dev); + handled = 1; + } + } + } + + /* Interrupt Status 0 */ + if (status0 & gmac->intr0_enabled) { +#ifndef GMAX_TX_INTR_DISABLED + if (gmac->port_id == 1 && netif_running(dev) && + (((status0 & GMAC1_SWTQ10_FIN_INT_BIT) && (gmac->intr0_enabled & GMAC1_SWTQ10_FIN_INT_BIT)) + || + ((status0 & GMAC1_SWTQ10_EOF_INT_BIT) && (gmac->intr0_enabled & GMAC1_SWTQ10_EOF_INT_BIT)))) { + toe_gmac_tx_complete(dev, 0); + handled = 1; + } + + if (gmac->port_id == 0 && netif_running(dev) && + (((status0 & GMAC0_SWTQ00_FIN_INT_BIT) && (gmac->intr0_enabled & GMAC0_SWTQ00_FIN_INT_BIT)) + || + ((status0 & GMAC0_SWTQ00_EOF_INT_BIT) && (gmac->intr0_enabled & GMAC0_SWTQ00_EOF_INT_BIT)))) { + toe_gmac_tx_complete(dev, 0); + handled = 1; + } +#endif + } + + return IRQ_RETVAL(handled); +} + +static int gmac_open(struct net_device *dev) +{ + struct gmac_private *gmac = netdev_priv(dev); + int retval; + + /* hook ISR */ + retval = request_irq(dev->irq, toe_gmac_interrupt, 0, dev->name, dev); + if (retval) + return retval; + + toe_init_gmac(dev); + + netif_carrier_off(dev); + phy_start(gmac->phydev); + + netif_start_queue(dev); + + return 0; +} + +static int gmac_close(struct net_device *dev) +{ + struct gmac_private *gmac = netdev_priv(dev); + + netif_stop_queue(dev); + mdelay(20); + + if (gmac->phydev) + phy_stop(gmac->phydev); + + /* stop tx/rx packet */ + toe_gmac_disable_tx_rx(dev); + mdelay(20); + + /* stop the chip's Tx and Rx DMA processes */ + toe_gmac_hw_stop(gmac); + + disable_irq(dev->irq); + free_irq(dev->irq, dev); + + return 0; +} + +static void gmac_get_phy_status(struct net_device *dev) +{ + struct gmac_private *gmac = netdev_priv(dev); + GMAC_CONFIG0_T config0; + GMAC_STATUS_T status, old_status; + struct phy_device *phydev = gmac->phydev; + + old_status.bits32 = status.bits32 = __raw_readl(dev->base_addr + GMAC_STATUS); + + status.bits.link = phydev->link; + status.bits.duplex = phydev->duplex; + + switch (phydev->speed) { + case 1000: + status.bits.speed = GMAC_SPEED_1000; + if (phydev->interface == PHY_INTERFACE_MODE_RGMII) + status.bits.mii_rmii = GMAC_PHY_RGMII_1000; + break; + case 100: + status.bits.speed = GMAC_SPEED_100; + if (phydev->interface == PHY_INTERFACE_MODE_RGMII) + status.bits.mii_rmii = GMAC_PHY_RGMII_100_10; + break; + case 10: + status.bits.speed = GMAC_SPEED_10; + if (phydev->interface == PHY_INTERFACE_MODE_RGMII) + status.bits.mii_rmii = GMAC_PHY_RGMII_100_10; + break; + default: + dev_warn(&dev->dev, "Not supported PHY speed (%d)\n", phydev->speed); + } + + if (phydev->pause) { + if (gmac->flow_control_enable == 0) { + config0.bits32 = __raw_readl(dev->base_addr + GMAC_CONFIG0); + config0.bits.tx_fc_en = 1; /* enable tx flow control */ + config0.bits.rx_fc_en = 1; /* enable rx flow control */ + __raw_writel(config0.bits32, dev->base_addr + GMAC_CONFIG0); + dev_info(&dev->dev, "MII flow control enabled\n"); + } + gmac->flow_control_enable = 1; + } else { + if (gmac->flow_control_enable == 1) { + config0.bits32 = __raw_readl(dev->base_addr + GMAC_CONFIG0); + config0.bits.tx_fc_en = 0; /* disable tx flow control */ + config0.bits.rx_fc_en = 0; /* disable rx flow control */ + __raw_writel(config0.bits32, dev->base_addr + GMAC_CONFIG0); + dev_info(&dev->dev, "MII flow control disabled\n"); + } + gmac->flow_control_enable = 0; + } + + if (old_status.bits32 != status.bits32) { + toe_gmac_disable_tx_rx(dev); + phy_print_status(phydev); + mdelay(10); /* let GMAC consume packet */ + __raw_writel(status.bits32, dev->base_addr + GMAC_STATUS); + if (status.bits.link) + toe_gmac_enable_tx_rx(dev); + } +} + +static void gmac_set_rx_mode(struct net_device *dev) +{ + GMAC_RX_FLTR_T filter; + unsigned int mc_filter[2]; /* Multicast hash filter */ + int bit_nr; + unsigned int i; + + filter.bits32 = 0; + filter.bits.error = 0; + if (dev->flags & IFF_PROMISC) { + filter.bits.error = 1; + filter.bits.promiscuous = 1; + filter.bits.broadcast = 1; + filter.bits.multicast = 1; + filter.bits.unicast = 1; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if (dev->flags & IFF_ALLMULTI) { + filter.bits.broadcast = 1; + filter.bits.multicast = 1; + filter.bits.unicast = 1; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else { + struct dev_mc_list *mclist; + + filter.bits.broadcast = 1; + filter.bits.multicast = 1; + filter.bits.unicast = 1; + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { + bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x3f; + if (bit_nr <= 32) + mc_filter[0] = mc_filter[0] | (1 << bit_nr); + else + mc_filter[1] = mc_filter[1] | (1 << (bit_nr - 32)); + } + } + __raw_writel(filter.bits32, dev->base_addr + GMAC_RX_FLTR); + __raw_writel(mc_filter[0], dev->base_addr + GMAC_MCAST_FIL0); + __raw_writel(mc_filter[1], dev->base_addr + GMAC_MCAST_FIL1); +} + +static void gmac_tx_timeout(struct net_device *dev) +{ + if (!netif_queue_stopped(dev)) + netif_wake_queue(dev); + + dev_warn(&dev->dev, "TX timeout\n"); +} + +const static struct net_device_ops gemini_gmac_ops = { + .ndo_open = gmac_open, + .ndo_stop = gmac_close, + .ndo_start_xmit = gmac_start_xmit, + .ndo_get_stats = gmac_get_stats, + .ndo_set_multicast_list = gmac_set_rx_mode, + .ndo_set_mac_address = gmac_set_mac_address, + .ndo_tx_timeout = gmac_tx_timeout, +}; + +static void __init mac_init_drv(struct toe_private *toe) +{ + QUEUE_THRESHOLD_T threshold; + DMA_SKB_SIZE_T skb_size; + + /* clear non TOE Queue Header Area */ + memset(toe->global_base + TOE_NONTOE_QUE_HDR_BASE, 0, + NONTOE_Q_HDR_AREA_END - TOE_NONTOE_QUE_HDR_BASE); + + /* clear TOE Queue Header Area */ + memset(toe->global_base + TOE_TOE_QUE_HDR_BASE, 0, + TOE_Q_HDR_AREA_END - TOE_TOE_QUE_HDR_BASE); + + /* Write GLOBAL_QUEUE_THRESHOLD_REG */ + threshold.bits32 = 0; + threshold.bits.swfq_empty = (TOE_SW_FREEQ_DESC_NUM > 256) ? 255 : + TOE_SW_FREEQ_DESC_NUM / 2; + threshold.bits.hwfq_empty = (TOE_HW_FREEQ_DESC_NUM > 256) ? 256 / 4 : + TOE_HW_FREEQ_DESC_NUM / 4; + threshold.bits.toe_class = (TOE_TOE_DESC_NUM > 256) ? 256 / 4 : + TOE_TOE_DESC_NUM / 4; + threshold.bits.intrq = (TOE_INTR_DESC_NUM > 256) ? 256 / 4 : + TOE_INTR_DESC_NUM / 4; + __raw_writel(threshold.bits32, toe->global_base + GLOBAL_QUEUE_THRESHOLD_REG); + + /* Init skb size */ + skb_size.bits.hw_skb_size = HW_RX_BUF_SIZE; + skb_size.bits.sw_skb_size = SW_RX_BUF_SIZE; + __raw_writel(skb_size.bits32, toe->global_base + GLOBAL_DMA_SKB_SIZE_REG); + + toe_init_free_queue(toe); + toe_init_interrupt_config(toe); +} + +static int __init gmac_init_eth(struct platform_device *pdev, unsigned int num) +{ + struct gmac_private *gmac; + struct net_device *dev; + struct toe_private *toe = platform_get_drvdata(pdev); + struct gemini_gmac_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata->bus_id[num]) + return 0; + + dev = alloc_etherdev(sizeof(*gmac)); + if (dev == NULL) { + dev_err(&pdev->dev, "Can't allocate ethernet device #%d\n", num); + return -ENOMEM; + } + + gmac = netdev_priv(dev); + dev->ml_priv = toe; + toe->net_dev[num] = dev; + + gmac->dma_base_addr = toe->global_base + TOE_GMAC_DMA_BASE(num); + gmac->port_id = num; + + dev->base_addr = toe->global_base + TOE_GMAC_BASE(num); + dev->irq = platform_get_irq(pdev, num); + dev->netdev_ops = &gemini_gmac_ops; + dev->watchdog_timeo = GMAC_DEV_TX_TIMEOUT; + dev->tx_queue_len = TOE_GMAC_SWTXQ_DESC_NUM; + +#ifdef DO_HW_CHKSUM + dev->features = NETIF_F_SG | NETIF_F_HW_CSUM; +#ifdef ENABLE_TSO + dev->features |= NETIF_F_TSO; +#endif +#endif + + toe_init_swtx_queue(dev); + toe_init_default_queue(dev); + + gmac_get_mac_address(dev); + + /* TODO: Do we need this? */ + __raw_writel(0x55aa55aa, dev->base_addr + GMAC_STA_ADD2); + + if (register_netdev(dev)) + return -1; + + gmac->phydev = phy_connect(dev, pdata->bus_id[num], &gmac_get_phy_status, 0, + pdata->interface[num]); + if (IS_ERR(gmac->phydev)) + return PTR_ERR(gmac->phydev); + + gmac->phydev->supported &= PHY_GBIT_FEATURES | SUPPORTED_Pause; + gmac->phydev->advertising = gmac->phydev->supported; + + return 0; +} + +static int __init gmac_probe(struct platform_device *pdev) +{ + struct resource *res; + struct toe_private *toe; + int retval; + + if (!pdev->dev.platform_data) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get device resources\n"); + return -ENODEV; + } + + toe = kzalloc(sizeof(struct toe_private), GFP_KERNEL); + if (!toe) + return -ENOMEM; + + toe->dev = &pdev->dev; + + toe->global_base = ioremap(res->start, resource_size(res)); + if (!toe->global_base) { + dev_err(toe->dev, "ioremap failed\n"); + retval = -EIO; + goto err_data; + } + + platform_set_drvdata(pdev, toe); + + mac_init_drv(toe); + + INIT_WORK(&toe->freq_work, fill_free_q_worker); + spin_lock_init(&toe->freeq_lock); + + retval = gmac_init_eth(pdev, GMAC_PORT0); + if (retval) + goto err_unmap; + retval = gmac_init_eth(pdev, GMAC_PORT1); + if (retval) + goto err_unmap; + + dev_info(&pdev->dev, SL351x_DRIVER_NAME "\n"); + + return 0; + +err_unmap: + iounmap(toe->global_base); +err_data: + kfree(toe); + return retval; +} + +static int __exit gmac_remove(struct platform_device *pdev) +{ + struct toe_private *toe = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < 2; i++) + if (toe->net_dev[i]) { + unregister_netdev(toe->net_dev[i]); + kfree(toe->net_dev[i]); + } + + iounmap(toe->global_base); + + kfree(toe); + + return 0; +} + +static struct platform_driver gemini_gmac_driver = { + .probe = gmac_probe, + .remove = __exit_p(gmac_remove), + + .driver = { + .name = "gemini-gmac", + .owner = THIS_MODULE, + }, +}; + +static int __init gemini_gmac_init(void) +{ + return platform_driver_register(&gemini_gmac_driver); +} + +static void __exit gemini_gmac_exit(void) +{ + platform_driver_unregister(&gemini_gmac_driver); +} + +module_init(gemini_gmac_init); +module_exit(gemini_gmac_exit); + +MODULE_AUTHOR("Paulius Zaleckas"); +MODULE_DESCRIPTION("Ethernet device driver for Gemini SoC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gemini-gmac"); --- /dev/null +++ b/drivers/net/gemini_negmac/gm_gmac.h @@ -0,0 +1,1488 @@ +/* + * Register definitions for Gemini Ethernet device driver. + * + * Copyright (C) 2006, Storlink, Corp. + * Copyright (C) 2008-2009, Paulius Zaleckas + * + * 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. + */ +#ifndef _GMAC_SL351x_H +#define _GMAC_SL351x_H +#include + +#define _PACKED_ __attribute__ ((aligned(1), packed)) + +#ifndef BIT +#define BIT(x) (1 << (x)) +#endif + +#define REG32(addr) (*(volatile unsigned long * const)(addr)) + +/* Define frame size */ +#define GMAC_MAX_ETH_FRAME_SIZE 1514 +#define GMAC_TX_BUF_SIZE ((GMAC_MAX_ETH_FRAME_SIZE + 31) & (~31)) + +#define SW_RX_BUF_SIZE 1536 +#define HW_RX_BUF_SIZE 1536 + +#define GMAC_DEV_TX_TIMEOUT (10*HZ) +#define SKB_RESERVE_BYTES 16 + +/* + * Base Registers + */ +#define TOE_NONTOE_QUE_HDR_BASE 0x2000 +#define TOE_TOE_QUE_HDR_BASE 0x3000 +#define TOE_V_BIT_BASE 0x4000 +#define TOE_A_BIT_BASE 0x6000 +#define TOE_GMAC_DMA_BASE(x) (0x8000 + 0x4000 * (x)) +#define TOE_GMAC_BASE(x) (0xA000 + 0x4000 * (x)) + +/* + * Queue ID + */ +#define TOE_SW_FREE_QID 0x00 +#define TOE_HW_FREE_QID 0x01 +#define TOE_GMAC0_SW_TXQ0_QID 0x02 +#define TOE_GMAC0_SW_TXQ1_QID 0x03 +#define TOE_GMAC0_SW_TXQ2_QID 0x04 +#define TOE_GMAC0_SW_TXQ3_QID 0x05 +#define TOE_GMAC0_SW_TXQ4_QID 0x06 +#define TOE_GMAC0_SW_TXQ5_QID 0x07 +#define TOE_GMAC0_HW_TXQ0_QID 0x08 +#define TOE_GMAC0_HW_TXQ1_QID 0x09 +#define TOE_GMAC0_HW_TXQ2_QID 0x0A +#define TOE_GMAC0_HW_TXQ3_QID 0x0B +#define TOE_GMAC1_SW_TXQ0_QID 0x12 +#define TOE_GMAC1_SW_TXQ1_QID 0x13 +#define TOE_GMAC1_SW_TXQ2_QID 0x14 +#define TOE_GMAC1_SW_TXQ3_QID 0x15 +#define TOE_GMAC1_SW_TXQ4_QID 0x16 +#define TOE_GMAC1_SW_TXQ5_QID 0x17 +#define TOE_GMAC1_HW_TXQ0_QID 0x18 +#define TOE_GMAC1_HW_TXQ1_QID 0x19 +#define TOE_GMAC1_HW_TXQ2_QID 0x1A +#define TOE_GMAC1_HW_TXQ3_QID 0x1B +#define TOE_GMAC0_DEFAULT_QID 0x20 +#define TOE_GMAC1_DEFAULT_QID 0x21 +#define TOE_CLASSIFICATION_QID(x) (0x22 + x) // 0x22 ~ 0x2F +#define TOE_TOE_QID(x) (0x40 + x) // 0x40 ~ 0x7F + +/* + * TOE DMA Queue Number should be 2^n, n = 6...12 + * TOE DMA Queues are the following queue types: + * SW Free Queue, HW Free Queue, + * GMAC 0/1 SW TX Q0-5, and GMAC 0/1 HW TX Q0-5 + * They have same descriptor numbers. + * The base address and descriptor number are configured at + * DMA Queues Descriptor Ring Base Address/Size Register (offset 0x0004) + */ +#define TOE_SW_FREEQ_DESC_POWER 8 +#define TOE_SW_FREEQ_DESC_NUM (1<2.5M 01->25M 10->125M) */ + unsigned int duplex : 1; /* duplex mode */ + unsigned int reserved : 1; + unsigned int mii_rmii : 2; /* PHY interface type */ + unsigned int : 25; + } bits; +} GMAC_STATUS_T; + +#define GMAC_SPEED_10 0 +#define GMAC_SPEED_100 1 +#define GMAC_SPEED_1000 2 + +#define GMAC_PHY_MII 0 +#define GMAC_PHY_GMII 1 +#define GMAC_PHY_RGMII_100_10 2 +#define GMAC_PHY_RGMII_1000 3 + +/* + * Queue Header + * (1) TOE Queue Header + * (2) Non-TOE Queue Header + * (3) Interrupt Queue Header + * + * memory Layout + * TOE Queue Header + * 0x60003000 +---------------------------+ 0x0000 + * | TOE Queue 0 Header | + * | 8 * 4 Bytes | + * +---------------------------+ 0x0020 + * | TOE Queue 1 Header | + * | 8 * 4 Bytes | + * +---------------------------+ 0x0040 + * | ...... | + * | | + * +---------------------------+ + * + * Non TOE Queue Header + * 0x60002000 +---------------------------+ 0x0000 + * | Default Queue 0 Header | + * | 2 * 4 Bytes | + * +---------------------------+ 0x0008 + * | Default Queue 1 Header | + * | 2 * 4 Bytes | + * +---------------------------+ 0x0010 + * | Classification Queue 0 | + * | 2 * 4 Bytes | + * +---------------------------+ + * | Classification Queue 1 | + * | 2 * 4 Bytes | + * +---------------------------+ (n * 8 + 0x10) + * | ... | + * | 2 * 4 Bytes | + * +---------------------------+ (13 * 8 + 0x10) + * | Classification Queue 13 | + * | 2 * 4 Bytes | + * +---------------------------+ 0x80 + * | Interrupt Queue 0 | + * | 2 * 4 Bytes | + * +---------------------------+ + * | Interrupt Queue 1 | + * | 2 * 4 Bytes | + * +---------------------------+ + * | Interrupt Queue 2 | + * | 2 * 4 Bytes | + * +---------------------------+ + * | Interrupt Queue 3 | + * | 2 * 4 Bytes | + * +---------------------------+ + * + */ +#define TOE_QUEUE_HDR_ADDR(n) (TOE_TOE_QUE_HDR_BASE + n * 32) +#define TOE_Q_HDR_AREA_END (TOE_QUEUE_HDR_ADDR(TOE_TOE_QUEUE_MAX + 1)) +#define TOE_DEFAULT_Q_HDR_BASE(x) (TOE_NONTOE_QUE_HDR_BASE + 0x08 * (x)) +#define TOE_CLASS_Q_HDR_BASE (TOE_NONTOE_QUE_HDR_BASE + 0x10) +#define TOE_INTR_Q_HDR_BASE (TOE_NONTOE_QUE_HDR_BASE + 0x80) +#define INTERRUPT_QUEUE_HDR_ADDR(n) (TOE_INTR_Q_HDR_BASE + n * 8) +#define NONTOE_Q_HDR_AREA_END (INTERRUPT_QUEUE_HDR_ADDR(TOE_INTR_QUEUE_MAX + 1)) +/* + * TOE Queue Header Word 0 + */ +typedef union { + unsigned int bits32; + unsigned int base_size; +} TOE_QHDR0_T; + +#define TOE_QHDR0_BASE_MASK (~0x0f) + +/* + * TOE Queue Header Word 1 + */ +typedef union { + unsigned int bits32; + struct bit_qhdr1 { + unsigned int rptr : 16; // bit 15:0 + unsigned int wptr : 16; // bit 31:16 + } bits; +} TOE_QHDR1_T; + +/* + * TOE Queue Header Word 2 + */ +typedef union { + unsigned int bits32; + struct bit_qhdr2 { + unsigned int TotalPktSize : 17; // bit 16: 0 Total packet size + unsigned int reserved : 7; // bit 23:17 + unsigned int dack : 1; // bit 24 1: Duplicated ACK + unsigned int abn : 1; // bit 25 1: Abnormal case Found + unsigned int tcp_opt : 1; // bit 26 1: Have TCP option + unsigned int ip_opt : 1; // bit 27 1: have IPV4 option or IPV6 Extension header + unsigned int sat : 1; // bit 28 1: SeqCnt > SeqThreshold, or AckCnt > AckThreshold + unsigned int osq : 1; // bit 29 1: out of sequence + unsigned int ctl : 1; // bit 30 1: have control flag bits (except ack) + unsigned int usd : 1; // bit 31 0: if no data assembled yet + } bits; +} TOE_QHDR2_T; + +/* + * TOE Queue Header Word 3 + */ +typedef union { + unsigned int bits32; + unsigned int seq_num; +} TOE_QHDR3_T; + +/* + * TOE Queue Header Word 4 + */ +typedef union { + unsigned int bits32; + unsigned int ack_num; +} TOE_QHDR4_T; + +/* + * TOE Queue Header Word 5 + */ +typedef union { + unsigned int bits32; + struct bit_qhdr5 { + unsigned int AckCnt : 16; // bit 15:0 + unsigned int SeqCnt : 16; // bit 31:16 + } bits; +} TOE_QHDR5_T; + +/* + * TOE Queue Header Word 6 + */ +typedef union { + unsigned int bits32; + struct bit_qhdr6 { + unsigned int WinSize : 16; // bit 15:0 + unsigned int iq_num : 2; // bit 17:16 + unsigned int MaxPktSize : 14; // bit 31:18 + } bits; +} TOE_QHDR6_T; + +/* + * TOE Queue Header Word 7 + */ +typedef union { + unsigned int bits32; + struct bit_qhdr7 { + unsigned int AckThreshold : 16; // bit 15:0 + unsigned int SeqThreshold : 16; // bit 31:16 + } bits; +} TOE_QHDR7_T; + +/* + * TOE Queue Header + */ +typedef struct { + TOE_QHDR0_T word0; + TOE_QHDR1_T word1; + TOE_QHDR2_T word2; + TOE_QHDR3_T word3; + TOE_QHDR4_T word4; + TOE_QHDR5_T word5; + TOE_QHDR6_T word6; + TOE_QHDR7_T word7; +} TOE_QHDR_T; + +/* + * NONTOE Queue Header Word 0 + */ +typedef union { + unsigned int bits32; + unsigned int base_size; +} NONTOE_QHDR0_T; + +#define NONTOE_QHDR0_BASE_MASK (~0x0f) + +/* + * NONTOE Queue Header Word 1 + */ +typedef union { + unsigned int bits32; + struct bit_nonqhdr1 { + unsigned int rptr : 16; // bit 15:0 + unsigned int wptr : 16; // bit 31:16 + } bits; +} NONTOE_QHDR1_T; + +/* + * Non-TOE Queue Header + */ +typedef struct { + NONTOE_QHDR0_T word0; + NONTOE_QHDR1_T word1; +} NONTOE_QHDR_T; + +/* + * Interrupt Queue Header Word 0 + */ +typedef union { + unsigned int bits32; + struct bit_intrqhdr0 { + unsigned int win_size : 16; // bit 15:0 Descriptor Ring Size + unsigned int wptr : 16; // bit 31:16 Write Pointer where hw stopped + } bits; +} INTR_QHDR0_T; + +/* + * Interrupt Queue Header Word 1 + */ +typedef union { + unsigned int bits32; + struct bit_intrqhdr1 { + unsigned int TotalPktSize : 17; // bit 16: 0 Total packet size + unsigned int tcp_qid : 8; // bit 24:17 TCP Queue ID + unsigned int dack : 1; // bit 25 1: Duplicated ACK + unsigned int abn : 1; // bit 26 1: Abnormal case Found + unsigned int tcp_opt : 1; // bit 27 1: Have TCP option + unsigned int ip_opt : 1; // bit 28 1: have IPV4 option or IPV6 Extension header + unsigned int sat : 1; // bit 29 1: SeqCnt > SeqThreshold, or AckCnt > AckThreshold + unsigned int osq : 1; // bit 30 1: out of sequence + unsigned int ctl : 1; // bit 31 1: have control flag bits (except ack) + } bits; +} INTR_QHDR1_T; + +/* + * Interrupt Queue Header Word 2 + */ +typedef union { + unsigned int bits32; + unsigned int seq_num; +} INTR_QHDR2_T; + +/* + * Interrupt Queue Header Word 3 + */ +typedef union { + unsigned int bits32; + unsigned int ack_num; +} INTR_QHDR3_T; + +/* + * Interrupt Queue Header Word 4 + */ +typedef union { + unsigned int bits32; + struct bit_intrqhdr4 { + unsigned int AckCnt : 16; // bit 15:0 Ack# change since last ack# intr. + unsigned int SeqCnt : 16; // bit 31:16 Seq# change since last seq# intr. + } bits; +} INTR_QHDR4_T; + +/* + * Interrupt Queue Header + */ +typedef struct { + INTR_QHDR0_T word0; + INTR_QHDR1_T word1; + INTR_QHDR2_T word2; + INTR_QHDR3_T word3; + INTR_QHDR4_T word4; + unsigned int word5; + unsigned int word6; + unsigned int word7; +} INTR_QHDR_T; + +/* + * GMAC private data + */ +typedef struct { + unsigned int rwptr_reg; + unsigned int desc_base; + unsigned int desc_base_dma; + unsigned short finished_idx; + struct sk_buff *tx_skb[TOE_GMAC_SWTXQ_DESC_NUM]; +} GMAC_SWTXQ_T; + +struct gmac_private { + struct phy_device *phydev; + unsigned int port_id; + unsigned int dma_base_addr; + unsigned int swtxq_desc_base; + GMAC_SWTXQ_T swtxq[TOE_SW_TXQ_NUM]; + NONTOE_QHDR_T *default_qhdr; + unsigned int default_desc_base; + dma_addr_t default_desc_base_dma; + dma_addr_t swtxq_desc_base_dma; + unsigned int flow_control_enable; + unsigned int intr0_enabled; + unsigned int intr1_enabled; + unsigned int intr2_enabled; + unsigned int intr3_enabled; + unsigned int intr4_enabled; + unsigned int intr0_selected; + unsigned int intr1_selected; + unsigned int intr2_selected; + unsigned int intr3_selected; + unsigned int intr4_selected; +}; + +struct toe_private { + void __iomem *global_base; + struct net_device *net_dev[2]; + struct device *dev; + struct work_struct freq_work; + spinlock_t freeq_lock; + unsigned int swfq_desc_base; + unsigned int hwfq_desc_base; + unsigned int hwfq_buf_base; + dma_addr_t sw_freeq_desc_base_dma; + dma_addr_t hw_freeq_desc_base_dma; + dma_addr_t hwfq_buf_base_dma; + dma_addr_t hwfq_buf_end_dma; +}; + +#define GMAC_PORT0 0 +#define GMAC_PORT1 1 + +#endif /* _GMAC_SL351x_H */ --- /dev/null +++ b/drivers/net/gemini_negmac/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_GEMINI_NET_ENGINE_GMAC)+= gemini_negmac.o + +gemini_negmac-objs := gm_gmac.o --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2087,6 +2087,13 @@ config ACENIC_OMIT_TIGON_I The safe and default value for this is N. +config GEMINI_NET_ENGINE_GMAC + tristate "Gemini Gigabit Ethernet support" + depends on ARCH_GEMINI + select PHYLIB + help + This driver supports Gemini TOE and NAT dual Gigabit Ethernet. + config DL2K tristate "DL2000/TC902x-based Gigabit Ethernet support" depends on PCI --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -234,6 +234,7 @@ pasemi_mac_driver-objs := pasemi_mac.o p obj-$(CONFIG_MLX4_CORE) += mlx4/ obj-$(CONFIG_ENC28J60) += enc28j60.o obj-$(CONFIG_ETHOC) += ethoc.o +obj-$(CONFIG_GEMINI_NET_ENGINE_GMAC) += gemini_negmac/ obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o --- /dev/null +++ b/drivers/usb/host/ehci-fotg2xx.c @@ -0,0 +1,459 @@ +/* + * EHCI Host Controller driver + * + * Copyright (C) 2006 Sony Computer Entertainment Inc. + * Copyright 2006 Sony Corp. + * + * 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; version 2 of the License. + */ + +#include +#include + +#define otg_set(port, bits) writel(readl(hcd->regs + port) | bits, hcd->regs + port) + +#define otg_clear(port, bits) writel(readl(hcd->regs + port) & ~bits, hcd->regs + port) + +#define GLOBAL_ISR 0xC0 +#define GLOBAL_ICR 0xC4 + +#define HCD_MISC 0x40 + +#define OTGC_SCR 0x80 +#define OTGC_INT_EN 0x88 + +#define GLOBAL_INT_POLARITY (1 << 3) +#define GLOBAL_INT_MASK_HC (1 << 2) +#define GLOBAL_INT_MASK_OTG (1 << 1) +#define GLOBAL_INT_MASK_DEV (1 << 0) + +#define OTGC_SCR_ID (1 << 21) +#define OTGC_SCR_CROLE (1 << 20) +#define OTGC_SCR_VBUS_VLD (1 << 19) +#define OTGC_SCR_A_SRP_RESP_TYPE (1 << 8) +#define OTGC_SCR_A_SRP_DET_EN (1 << 7) +#define OTGC_SCR_A_SET_B_HNP_EN (1 << 6) +#define OTGC_SCR_A_BUS_DROP (1 << 5) +#define OTGC_SCR_A_BUS_REQ (1 << 4) + +#define OTGC_INT_APLGRMV (1 << 12) +#define OTGC_INT_BPLGRMV (1 << 11) +#define OTGC_INT_OVC (1 << 10) +#define OTGC_INT_IDCHG (1 << 9) +#define OTGC_INT_RLCHG (1 << 8) +#define OTGC_INT_AVBUSERR (1 << 5) +#define OTGC_INT_ASRPDET (1 << 4) +#define OTGC_INT_BSRPDN (1 << 0) + +#define OTGC_INT_A_TYPE (OTGC_INT_ASRPDET|OTGC_INT_AVBUSERR|OTGC_INT_OVC|OTGC_INT_RLCHG|OTGC_INT_IDCHG|OTGC_INT_APLGRMV) +#define OTGC_INT_B_TYPE (OTGC_INT_AVBUSERR|OTGC_INT_OVC|OTGC_INT_RLCHG|OTGC_INT_IDCHG) + +static void fotg2xx_otgc_role_change(struct usb_hcd *hcd); + +static void fotg2xx_otgc_init(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + unsigned int reg; + + reg = __raw_readl(hcd->regs + OTGC_SCR); + ehci_info(ehci, "role detected: %s, ", + (reg & OTGC_SCR_CROLE) ? "Peripheral" : "Host"); + + if (reg & OTGC_SCR_ID) + ehci_info(ehci, "B-Device (may be unsupported!)\n"); + else + ehci_info(ehci, "A-Device\n"); + + /* Enable the SRP detect */ + reg &= ~OTGC_SCR_A_SRP_RESP_TYPE; + __raw_writel(reg, hcd->regs + OTGC_SCR); + + reg = __raw_readl(hcd->regs + OTGC_INT_EN); + /* clear INT B: bits AVBUSERR | OVC | RLCHG | IDCHG */ + reg &= ~OTGC_INT_B_TYPE; + /* set INT A: bits ASRPDET | AVBUSERR | OVC | RLCHG | IDCHG | APLGRMV */ + reg |= OTGC_INT_A_TYPE; + __raw_writel(reg, hcd->regs + OTGC_INT_EN); + + reg = __raw_readl(hcd->regs + GLOBAL_ICR); + reg &= ~GLOBAL_INT_MASK_OTG; + __raw_writel(reg, hcd->regs + GLOBAL_ICR); + + /* setup MISC register, fixes timing problems */ + reg = __raw_readl(hcd->regs + HCD_MISC); + reg |= 0xD; + __raw_writel(reg, hcd->regs + HCD_MISC); + + fotg2xx_otgc_role_change(hcd); +} + +static void fotg2xx_otgh_close(struct usb_hcd *hcd) +{ + unsigned int reg; + + /* <1>.Enable Interrupt Mask */ + reg = __raw_readl(hcd->regs + GLOBAL_ICR); + reg |= GLOBAL_INT_MASK_HC; + __raw_writel(reg, hcd->regs + GLOBAL_ICR); + + /* <2>.Clear the Interrupt status */ + reg = __raw_readl(hcd->regs + 0x18); + reg &= 0x0000003F; + __raw_writel(reg, hcd->regs + 0x14); +} + +static void fotg2xx_otgh_open(struct usb_hcd *hcd) +{ + unsigned int reg; + + reg = __raw_readl(hcd->regs + OTGC_SCR); + reg &= ~OTGC_SCR_A_SRP_DET_EN; + __raw_writel(reg, hcd->regs + OTGC_SCR); + + reg = __raw_readl(hcd->regs + GLOBAL_ICR); + reg &= ~GLOBAL_INT_MASK_HC; + __raw_writel(reg, hcd->regs + GLOBAL_ICR); +} + +/* change to host role */ +static void fotg2xx_otgc_role_change(struct usb_hcd *hcd) +{ + + /* clear A_SET_B_HNP_EN */ + otg_clear(0x80, BIT(6)); + + /*** Enable VBUS driving */ + if (readl(hcd->regs + 0x80) & BIT(19)) + printk(KERN_INFO "VBUS already enabled\n"); + else { + int cnt = 0; + + /* clear A_BUS_DROP */ + otg_clear(0x80, BIT(5)); + + /* set A_BUS_REQ */ + otg_set(0x80, BIT(4)); + + /* set global bus reg to VBUS on */ + writel(readl(IO_ADDRESS(0x40000000) + 0x30) | ((BIT(21)|BIT(22))), + IO_ADDRESS(0x40000000) + 0x30); + + if (readl(hcd->regs + 0x80) & (1<<19)) { + printk(KERN_INFO "Waiting for VBus"); + while (!(readl(hcd->regs + 0x80) & (1<<19)) && (cnt < 80)) { + printk(KERN_CONT "."); + cnt++; + } + printk(KERN_CONT "\n"); + } else + printk(KERN_INFO "VBUS enabled.\n"); + + mdelay(1); + } + fotg2xx_otgh_open(hcd); +} + +static int fotg2xx_ehci_hc_reset(struct usb_hcd *hcd) +{ + int result; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + hcd->has_tt = 1; + + result = ehci_halt(ehci); + if (result) + return result; + + result = ehci_init(hcd); + if (result) + return result; + + ehci_port_power(ehci, 0); + + return result; +} + +/* + * Name: OTGC_INT_ISR + * Description:This interrupt service routine belongs to the OTG-Controller + * <1>.Check for ID_Change + * <2>.Check for RL_Change + * <3>.Error Detect + * Input: wINTStatus + * Output:void + */ +void fotg2xx_int_isr(struct usb_hcd *hcd, u32 wINTStatus) +{ + /* <1>.Check for ID_Change */ + if (wINTStatus&OTGC_INT_IDCHG) { + if ((readl(hcd->regs + 0x80) & BIT(21)) != 0) + fotg2xx_otgc_init(hcd); /* Change to B Type */ + else + fotg2xx_otgc_init(hcd); /* Change to A Type */ + + return; + } + + /* <2>.Check for RL_Change */ + if (wINTStatus&OTGC_INT_RLCHG) + fotg2xx_otgc_role_change(hcd); + + /* <3>.Error Detect */ + if (wINTStatus&OTGC_INT_AVBUSERR) + printk(KERN_ERR "VBus error!\n"); + + if (wINTStatus&OTGC_INT_OVC) + printk(KERN_WARNING "Overcurrent detected!\n"); + + /* <3>.Check for Type-A/Type-B Interrupt */ + if ((readl(hcd->regs + 0x80) & BIT(21)) == 0) { /*For Type-A Interrupt*/ + if (wINTStatus & (OTGC_INT_A_TYPE | OTGC_INT_ASRPDET)) { + /* <1>.SRP detected => then set global variable */ + printk(KERN_WARNING "SRP detected, but not implemented!\n"); + +#if 0 + u32 wTempCounter; + /* <2>.Turn on the V Bus */ + pFTC_OTG->otg.state = OTG_STATE_A_WAIT_VRISE; + OTGC_enable_vbus_draw_storlink(1); + pFTC_OTG->otg.state = OTG_STATE_A_HOST; + /* <3>.Should waiting for Device-Connect Wait 300ms */ + INFO(pFTC_OTG, ">>> OTG-A Waiting for OTG-B Connect,\n"); + wTempCounter = 0; + while (mwHost20_PORTSC_ConnectStatus_Rd() == 0) { + mdelay(1); + wTempCounter++; + /* Waiting for 300 ms */ + if (wTempCounter > 300) { + mdwOTGC_Control_A_SRP_DET_EN_Clr(); + INFO(pFTC_OTG, ">>> OTG-B do not connect under 300 ms...\n"); + break; + } + } + /* <4>.If Connect => issue quick Reset */ + if (mwHost20_PORTSC_ConnectStatus_Rd() > 0) { + mdelay(300); /* For OPT-A Test */ + OTGH_host_quick_Reset(); + OTGH_Open(); + pFTC_OTG->otg.host->A_Disable_Set_Feature_HNP = 0; + } +#endif + } + } else { /* For Type-B Interrupt */ + BUG(); + } +} + +static irqreturn_t fotg2xx_ehci_irq(int irq, void *devid) +{ + struct usb_hcd *hcd = devid; + u32 val; + + /* OTG Interrupt Status Register */ + val = readl(hcd->regs + 0x84); + + /* OTG stuff */ + if (val) { + /* supposed to do "INT STS Clr" - XXX */ + writel(readl(hcd->regs + 0x84) | val, hcd->regs + 0x84); + + fotg2xx_int_isr(hcd, val); + + /* supposed to do "INT STS Clr" - XXX */ + writel(readl(hcd->regs + 0x84) | val, hcd->regs + 0x84); + + return IRQ_HANDLED; + } + + if ((readl(hcd->regs + 0x80) & BIT(20)) == 0) { /* Role is HOST */ + if (readl(hcd->regs + 0xC0) & BIT(2)) { /* INT STS HOST */ + /* leave this for ehci irq handler */ + return IRQ_NONE; + } + } else + printk(KERN_WARNING + "received irq for peripheral - don't know what to do!\n"); + + /* do not call the ehci irq handler */ + return IRQ_HANDLED; +} + +static int fotg2xx_ehci_run(struct usb_hcd *hcd) +{ + int retval; + + retval = ehci_run(hcd); + + fotg2xx_otgh_close(hcd); + fotg2xx_otgc_init(hcd); + + return retval; +} + +static const struct hc_driver fotg2xx_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "FOTG2XX EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + .reset = fotg2xx_ehci_hc_reset, + .start = fotg2xx_ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .get_frame_number = ehci_get_frame, + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#if defined(CONFIG_PM) + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int fotg2xx_ehci_probe(struct platform_device *pdev) +{ + const struct hc_driver *driver = &fotg2xx_ehci_hc_driver; + struct usb_hcd *hcd; + struct resource *res; + int irq; + int retval; + + pr_debug("initializing FOTG2XX-SOC USB Controller\n"); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + irq = res->start; + + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + retval = -ENODEV; + goto err2; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + retval = -EBUSY; + goto err2; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto err3; + } + + + /* set global reg to mini-A host */ + writel(readl(IO_ADDRESS(0x40000000) + 0x30) & ~(BIT(30)|BIT(29)), + IO_ADDRESS(0x40000000) + 0x30); + + /* USB0&USB1 - VBUS off */ + writel(readl(IO_ADDRESS(0x40000000) + 0x30) & ~(BIT(21)|BIT(22)), + IO_ADDRESS(0x40000000) + 0x30); + + if ((readl(hcd->regs) == 0x01000010) && + (readl(hcd->regs + 4) == 0x00000001) && + (readl(hcd->regs + 8) == 0x00000006)) { + dev_info(&pdev->dev, + "Found Faraday OTG 2XX controller (base = 0x%08lX)\n", + (unsigned long) hcd->rsrc_start); + } else { + dev_err(&pdev->dev, "fotg2xx id mismatch: found %d.%d.%d\n", + readl(hcd->regs + 0x00), + readl(hcd->regs + 0x04), + readl(hcd->regs + 0x08)); + retval = -ENODEV; + goto err4; + } + + platform_set_drvdata(pdev, hcd); + + /* mask interrupts - peripheral, otg, host, hi-active (bits 0,1,2,3) */ + otg_set(0xc4, BIT(3)); /* hi active */ + + otg_set(0xc4, BIT(2)); /* host */ + otg_set(0xc4, BIT(1)); /* otg */ + otg_set(0xc4, BIT(0)); /* peripheral */ + + /* register additional interrupt - here we check otg status */ + if ((request_irq(irq, &fotg2xx_ehci_irq, IRQF_SHARED | IRQF_DISABLED, + hcd->irq_descr, hcd)) != 0) { + dev_dbg(&pdev->dev, "error requesting irq %d\n", irq); + retval = -EFAULT; + goto err4; + } + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval != 0) + goto err4; + return retval; + +err4: + iounmap(hcd->regs); +err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err2: + usb_put_hcd(hcd); +err1: + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval); + return retval; +} + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +int fotg2xx_ehci_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = + (struct usb_hcd *)platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + iounmap(hcd->regs); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +MODULE_ALIAS("platform:ehci-fotg2xx"); + +static struct platform_driver fotg2xx_ehci_driver = { + .probe = fotg2xx_ehci_probe, + .remove = fotg2xx_ehci_remove, + .driver = { + .name = "ehci-fotg2xx", + }, +}; --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -541,7 +541,12 @@ static inline unsigned int ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) { if (ehci_is_TDI(ehci)) { +#ifdef CONFIG_ARCH_GEMINI + portsc = readl(ehci_to_hcd(ehci)->regs + 0x80); + switch ((portsc>>22)&3) { +#else switch ((portsc>>26)&3) { +#endif case 0: return 0; case 1: --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -192,9 +192,11 @@ static int ehci_halt (struct ehci_hcd *e if ((temp & STS_HALT) != 0) return 0; +#ifndef CONFIG_ARCH_GEMINI temp = ehci_readl(ehci, &ehci->regs->command); temp &= ~CMD_RUN; ehci_writel(ehci, temp, &ehci->regs->command); +#endif return handshake (ehci, &ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); } @@ -250,8 +252,8 @@ static int ehci_reset (struct ehci_hcd * if (retval) return retval; - if (ehci_is_TDI(ehci)) - tdi_reset (ehci); +// if (ehci_is_TDI(ehci)) +// tdi_reset (ehci); return retval; } @@ -381,12 +383,13 @@ static void ehci_silence_controller(stru { ehci_halt(ehci); ehci_turn_off_all_ports(ehci); - +#ifndef CONFIG_ARCH_GEMINI /* make BIOS/etc use companion controller during reboot */ ehci_writel(ehci, 0, &ehci->regs->configured_flag); /* unblock posted writes */ ehci_readl(ehci, &ehci->regs->configured_flag); +#endif } /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). @@ -631,7 +634,9 @@ static int ehci_run (struct usb_hcd *hcd // Philips, Intel, and maybe others need CMD_RUN before the // root hub will detect new devices (why?); NEC doesn't ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); +#ifndef CONFIG_ARCH_GEMINI ehci->command |= CMD_RUN; +#endif ehci_writel(ehci, ehci->command, &ehci->regs->command); dbg_cmd (ehci, "init", ehci->command); @@ -651,9 +656,11 @@ static int ehci_run (struct usb_hcd *hcd */ down_write(&ehci_cf_port_reset_rwsem); hcd->state = HC_STATE_RUNNING; +#ifndef CONFIG_ARCH_GEMINI ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ msleep(5); +#endif up_write(&ehci_cf_port_reset_rwsem); temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); @@ -744,9 +751,10 @@ static irqreturn_t ehci_irq (struct usb_ pcd_status = status; /* resume root hub? */ +#ifndef CONFIG_ARCH_GEMINI if (!(cmd & CMD_RUN)) usb_hcd_resume_root_hub(hcd); - +#endif while (i--) { int pstatus = ehci_readl(ehci, &ehci->regs->port_status [i]); @@ -778,7 +786,9 @@ static irqreturn_t ehci_irq (struct usb_ ehci_halt(ehci); dead: ehci_reset(ehci); +#ifndef CONFIG_ARCH_GEMINI ehci_writel(ehci, 0, &ehci->regs->configured_flag); +#endif /* generic layer kills/unlinks all urbs, then * uses ehci_stop to clean up the rest */ @@ -1042,6 +1052,11 @@ MODULE_LICENSE ("GPL"); #define PCI_DRIVER ehci_pci_driver #endif +#ifdef CONFIG_ARCH_GEMINI +#include "ehci-fotg2xx.c" +#define PLATFORM_DRIVER fotg2xx_ehci_driver +#endif + #ifdef CONFIG_USB_EHCI_FSL #include "ehci-fsl.c" #define PLATFORM_DRIVER ehci_fsl_driver --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -749,6 +749,12 @@ static int ehci_hub_control ( /* see what we found out */ temp = check_reset_complete (ehci, wIndex, status_reg, ehci_readl(ehci, status_reg)); +#ifdef CONFIG_ARCH_GEMINI + /* restart schedule */ + ehci_writel(ehci, ehci_readl(ehci, &ehci->regs->command) | CMD_RUN, &ehci->regs->command); + +// hcd->state = HC_STATE_RUNNING; +#endif } if (!(temp & (PORT_RESUME|PORT_RESET))) --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -57,6 +57,7 @@ config USB_ARCH_HAS_EHCI default y if PPC_83xx default y if SOC_AU1200 default y if ARCH_IXP4XX + default y if ARCH_GEMINI default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. --- /dev/null +++ b/drivers/watchdog/gemini_wdt.c @@ -0,0 +1,368 @@ +/* + * Watchdog driver for Cortina Systems Gemini SoC + * + * Copyright (C) 2009 Paulius Zaleckas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GEMINI_WDCOUNTER 0x0 +#define GEMINI_WDLOAD 0x4 +#define GEMINI_WDRESTART 0x8 + +#define WDRESTART_MAGIC 0x5AB9 + +#define GEMINI_WDCR 0xC + +#define WDCR_CLOCK_5MHZ (1 << 4) +#define WDCR_SYS_RST (1 << 1) +#define WDCR_ENABLE (1 << 0) + +#define WDT_CLOCK 5000000 /* 5 MHz */ +#define WDT_DEFAULT_TIMEOUT 13 +#define WDT_MAX_TIMEOUT (0xFFFFFFFF / WDT_CLOCK) + +/* status bits */ +#define WDT_ACTIVE 0 +#define WDT_OK_TO_CLOSE 1 + +static unsigned int timeout = WDT_DEFAULT_TIMEOUT; +static int nowayout = WATCHDOG_NOWAYOUT; + +static DEFINE_SPINLOCK(gemini_wdt_lock); + +static struct platform_device *gemini_wdt_dev; + +struct gemini_wdt_struct { + struct resource *res; + struct device *dev; + void __iomem *base; + unsigned long status; +}; + +static struct watchdog_info gemini_wdt_info = { + .identity = "Gemini watchdog", + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT, +}; + +/* Disable the watchdog. */ +static void gemini_wdt_stop(struct gemini_wdt_struct *gemini_wdt) +{ + spin_lock(&gemini_wdt_lock); + + __raw_writel(0, gemini_wdt->base + GEMINI_WDCR); + + clear_bit(WDT_ACTIVE, &gemini_wdt->status); + + spin_unlock(&gemini_wdt_lock); +} + +/* Service the watchdog */ +static void gemini_wdt_service(struct gemini_wdt_struct *gemini_wdt) +{ + __raw_writel(WDRESTART_MAGIC, gemini_wdt->base + GEMINI_WDRESTART); +} + +/* Enable and reset the watchdog. */ +static void gemini_wdt_start(struct gemini_wdt_struct *gemini_wdt) +{ + spin_lock(&gemini_wdt_lock); + + __raw_writel(timeout * WDT_CLOCK, gemini_wdt->base + GEMINI_WDLOAD); + + gemini_wdt_service(gemini_wdt); + + /* set clock before enabling */ + __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, + gemini_wdt->base + GEMINI_WDCR); + + __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, + gemini_wdt->base + GEMINI_WDCR); + + set_bit(WDT_ACTIVE, &gemini_wdt->status); + + spin_unlock(&gemini_wdt_lock); +} + +/* Watchdog device is opened, and watchdog starts running. */ +static int gemini_wdt_open(struct inode *inode, struct file *file) +{ + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(gemini_wdt_dev); + + if (test_bit(WDT_ACTIVE, &gemini_wdt->status)) + return -EBUSY; + + file->private_data = gemini_wdt; + + gemini_wdt_start(gemini_wdt); + + return nonseekable_open(inode, file); +} + +/* Close the watchdog device. */ +static int gemini_wdt_close(struct inode *inode, struct file *file) +{ + struct gemini_wdt_struct *gemini_wdt = file->private_data; + + /* Disable the watchdog if possible */ + if (test_bit(WDT_OK_TO_CLOSE, &gemini_wdt->status)) + gemini_wdt_stop(gemini_wdt); + else + dev_warn(gemini_wdt->dev, "Device closed unexpectedly - timer will not stop\n"); + + return 0; +} + +/* Handle commands from user-space. */ +static int gemini_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct gemini_wdt_struct *gemini_wdt = file->private_data; + + int value; + + switch (cmd) { + case WDIOC_KEEPALIVE: + gemini_wdt_service(gemini_wdt); + return 0; + + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &gemini_wdt_info, + sizeof(gemini_wdt_info)) ? -EFAULT : 0; + + case WDIOC_SETTIMEOUT: + if (get_user(value, (int *)arg)) + return -EFAULT; + + if ((value < 1) || (value > WDT_MAX_TIMEOUT)) + return -EINVAL; + + timeout = value; + + /* restart wdt to use new timeout */ + gemini_wdt_stop(gemini_wdt); + gemini_wdt_start(gemini_wdt); + + /* Fall through */ + case WDIOC_GETTIMEOUT: + return put_user(timeout, (int *)arg); + + case WDIOC_GETTIMELEFT: + value = __raw_readl(gemini_wdt->base + GEMINI_WDCOUNTER); + return put_user(value / WDT_CLOCK, (int *)arg); + + default: + return -ENOTTY; + } +} + +/* Refresh the watchdog whenever device is written to. */ +static ssize_t gemini_wdt_write(struct file *file, const char *data, + size_t len, loff_t *ppos) +{ + struct gemini_wdt_struct *gemini_wdt = file->private_data; + + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &gemini_wdt->status); + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, + &gemini_wdt->status); + } + } + gemini_wdt_service(gemini_wdt); + } + + return len; +} + +static const struct file_operations gemini_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = gemini_wdt_ioctl, + .open = gemini_wdt_open, + .release = gemini_wdt_close, + .write = gemini_wdt_write, +}; + +static struct miscdevice gemini_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &gemini_wdt_fops, +}; + +static void gemini_wdt_shutdown(struct platform_device *pdev) +{ + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev); + + gemini_wdt_stop(gemini_wdt); +} + +static int __init gemini_wdt_probe(struct platform_device *pdev) +{ + int ret; + int res_size; + struct resource *res; + void __iomem *base; + struct gemini_wdt_struct *gemini_wdt; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get device resources\n"); + return -ENODEV; + } + + res_size = resource_size(res); + if (!request_mem_region(res->start, res_size, res->name)) { + dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", + res_size, res->start); + return -ENOMEM; + } + + base = ioremap(res->start, res_size); + if (!base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -EIO; + goto fail0; + } + + gemini_wdt = kzalloc(sizeof(struct gemini_wdt_struct), GFP_KERNEL); + if (!gemini_wdt) { + dev_err(&pdev->dev, "can't allocate interface\n"); + ret = -ENOMEM; + goto fail1; + } + + /* Setup gemini_wdt driver structure */ + gemini_wdt->base = base; + gemini_wdt->res = res; + + /* Set up platform driver data */ + platform_set_drvdata(pdev, gemini_wdt); + gemini_wdt_dev = pdev; + + if (gemini_wdt_miscdev.parent) { + ret = -EBUSY; + goto fail2; + } + + gemini_wdt_miscdev.parent = &pdev->dev; + + ret = misc_register(&gemini_wdt_miscdev); + if (ret) + goto fail2; + + return 0; + +fail2: + platform_set_drvdata(pdev, NULL); + kfree(gemini_wdt); +fail1: + iounmap(base); +fail0: + release_mem_region(res->start, res_size); + + return ret; +} + +static int __exit gemini_wdt_remove(struct platform_device *pdev) +{ + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + misc_deregister(&gemini_wdt_miscdev); + gemini_wdt_dev = NULL; + iounmap(gemini_wdt->base); + release_mem_region(gemini_wdt->res->start, resource_size(gemini_wdt->res)); + + kfree(gemini_wdt); + + return 0; +} + +#ifdef CONFIG_PM +static int gemini_wdt_suspend(struct platform_device *pdev, pm_message_t message) +{ + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev); + unsigned int reg; + + reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR); + reg &= ~(WDCR_WDENABLE); + __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR); + + return 0; +} + +static int gemini_wdt_resume(struct platform_device *pdev) +{ + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev); + unsigned int reg; + + if (gemini_wdt->status) { + reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR); + reg |= WDCR_WDENABLE; + __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR); + } + + return 0; +} +#else +#define gemini_wdt_suspend NULL +#define gemini_wdt_resume NULL +#endif + +static struct platform_driver gemini_wdt_driver = { + .probe = gemini_wdt_probe, + .remove = __exit_p(gemini_wdt_remove), + .shutdown = gemini_wdt_shutdown, + .suspend = gemini_wdt_suspend, + .resume = gemini_wdt_resume, + .driver = { + .name = "gemini-wdt", + .owner = THIS_MODULE, + }, +}; + +static int __init gemini_wdt_init(void) +{ + return platform_driver_probe(&gemini_wdt_driver, gemini_wdt_probe); +} + +static void __exit gemini_wdt_exit(void) +{ + platform_driver_unregister(&gemini_wdt_driver); +} + +module_init(gemini_wdt_init); +module_exit(gemini_wdt_exit); + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +MODULE_AUTHOR("Paulius Zaleckas"); +MODULE_DESCRIPTION("Watchdog driver for Gemini"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS("platform:gemini-wdt"); --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -104,6 +104,16 @@ config 977_WATCHDOG Not sure? It's safe to say N. +config GEMINI_WATCHDOG + tristate "Gemini watchdog" + depends on ARCH_GEMINI + help + Say Y here if to include support for the watchdog timer + embedded in the Cortina Systems Gemini family of devices. + + To compile this driver as a module, choose M here: the + module will be called gemini_wdt. + config IXP2000_WATCHDOG tristate "IXP2000 Watchdog" depends on ARCH_IXP2000 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91 obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o +obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -91,9 +91,9 @@ struct ehci_regs { u32 frame_list; /* points to periodic list */ /* ASYNCLISTADDR: offset 0x18 */ u32 async_next; /* address of next async queue head */ - +#ifndef CONFIG_ARCH_GEMINI u32 reserved [9]; - +#endif /* CONFIGFLAG: offset 0x40 */ u32 configured_flag; #define FLAG_CF (1<<0) /* true: we'll support "high speed" */