Index: arch/arm/include/asm/io.h =================================================================== --- arch/arm/include/asm/io.h (revision 1) +++ arch/arm/include/asm/io.h (working copy) @@ -36,6 +36,12 @@ #define isa_bus_to_virt phys_to_virt /* + * Atomic MMIO-wide IO modify + */ +extern void atomic_io_modify(void __iomem *reg, u32 mask, u32 set); +extern void atomic_io_modify_relaxed(void __iomem *reg, u32 mask, u32 set); + +/* * Generic IO read/write. These perform native-endian accesses. Note * that some architectures will want to re-define __raw_{read,write}w. */ Index: arch/arm/kernel/io.c =================================================================== --- arch/arm/kernel/io.c (revision 1) +++ arch/arm/kernel/io.c (working copy) @@ -1,8 +1,43 @@ #include #include #include +#include +static DEFINE_RAW_SPINLOCK(__io_lock); + /* + * Generic atomic MMIO modify. + * + * Allows thread-safe access to registers shared by unrelated subsystems. + * The access is protected by a single MMIO-wide lock. + */ +void atomic_io_modify_relaxed(void __iomem *reg, u32 mask, u32 set) +{ + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&__io_lock, flags); + value = readl_relaxed(reg) & ~mask; + value |= (set & mask); + writel_relaxed(value, reg); + raw_spin_unlock_irqrestore(&__io_lock, flags); +} +EXPORT_SYMBOL(atomic_io_modify_relaxed); + +void atomic_io_modify(void __iomem *reg, u32 mask, u32 set) +{ + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&__io_lock, flags); + value = readl_relaxed(reg) & ~mask; + value |= (set & mask); + writel(value, reg); + raw_spin_unlock_irqrestore(&__io_lock, flags); +} +EXPORT_SYMBOL(atomic_io_modify); + +/* * Copy data from IO memory space to "real" memory space. * This needs to be optimized. */ Index: arch/arm/mach-mvebu/armada-38x.c =================================================================== --- arch/arm/mach-mvebu/armada-38x.c (revision 1) +++ arch/arm/mach-mvebu/armada-38x.c (working copy) @@ -13,11 +13,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -97,6 +99,43 @@ l2x0_of_init(0, ~0UL); } +static struct of_device_id of_mbusc_table[] = { + { .compatible = "marvell,mbus-controller" }, + { }, +}; + +static void __init armada_380_mbus_optimization(void) +{ + struct device_node *np; + void __iomem *mbus_units_base; + u32 val; + + np = of_find_matching_node(NULL, of_mbusc_table); + if (np) { + mbus_units_base = of_iomap(np, 3); + BUG_ON(!mbus_units_base); + + /* MBUS Units Priority Control Register - + Prioritize XOR, PCIe and GbEs (ID=4,6,3,7,8) DRAM access + GbE is High and others are Med */ + __raw_writel(0x19180, mbus_units_base); + + /* Fabric Units Priority Control Register - + Prioritize CPUs requests */ + __raw_writel(0xA, mbus_units_base + 0x4); + + /* MBUS Units Prefetch Control Register - + Pre-fetch enable for all IO masters */ + __raw_writel(0xFFFF, mbus_units_base + 0x8); + + /* Fabric Units Prefetch Control Register - + Enable the CPUs Instruction and Data prefetch */ + __raw_writel(0x303, mbus_units_base + 0xC); + + iounmap(mbus_units_base); + } +} + static void __iomem * armada_380_ioremap_caller(unsigned long phys_addr, size_t size, unsigned int mtype, void *caller) @@ -117,6 +156,7 @@ mvebu_clocks_init(); clocksource_of_init(); + armada_380_mbus_optimization(); armada_380_scu_enable(); BUG_ON(mvebu_mbus_dt_init(coherency_available())); arch_ioremap_caller = armada_380_ioremap_caller; @@ -127,17 +167,70 @@ static const char * const armada_380_dt_compat[] = { "marvell,armada380", + "marvell,armada381", + "marvell,armada382", "marvell,armada385", "marvell,armada388", NULL, }; -DT_MACHINE_START(ARMADA_XP_DT, "Marvell Armada 380/385/388 (Device Tree)") +/* + * When returning from suspend, the platform goes through the + * bootloader, which executes its DDR3 training code. This code has + * the unfortunate idea of using the first 10 KB of each DRAM bank to + * exercise the RAM and calculate the optimal timings. Therefore, this + * area of RAM is overwritten, and shouldn't be used by the kernel if + * suspend/resume is supported. + */ + +#ifdef CONFIG_SUSPEND +#define MVEBU_DDR_TRAINING_AREA_SZ (10 * SZ_1K) +static int __init mvebu_scan_mem(unsigned long node, const char *uname, + int depth, void *data) +{ + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *reg, *endp; + int l; + + if (type == NULL || strcmp(type, "memory")) + return 0; + + reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); + if (reg == NULL) + reg = of_get_flat_dt_prop(node, "reg", &l); + if (reg == NULL) + return 0; + + endp = reg + (l / sizeof(__be32)); + while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { + u64 base, size; + + base = dt_mem_next_cell(dt_root_addr_cells, ®); + size = dt_mem_next_cell(dt_root_size_cells, ®); + + memblock_reserve(base, MVEBU_DDR_TRAINING_AREA_SZ); + } + + return 0; +} + +static void __init mvebu_memblock_reserve(void) +{ + of_scan_flat_dt(mvebu_scan_mem, NULL); +} +#else +static void __init mvebu_memblock_reserve(void) {} +#endif + + + +DT_MACHINE_START(ARMADA_XP_DT, "Marvell Armada 380/381/382/385/388 (Device Tree)") .smp = smp_ops(armada_380_smp_ops), .map_io = debug_ll_io_init, .init_irq = irqchip_init, .init_time = armada_380_timer_and_clk_init, .restart = mvebu_restart, + .reserve = mvebu_memblock_reserve, .dt_compat = armada_380_dt_compat, .flags = (MACHINE_NEEDS_CPOLICY_WRITEALLOC | MACHINE_NEEDS_SHAREABLE_PAGES), Index: arch/arm/mach-mvebu/coherency.c =================================================================== --- arch/arm/mach-mvebu/coherency.c (revision 1) +++ arch/arm/mach-mvebu/coherency.c (working copy) @@ -29,7 +29,8 @@ #include #include "armada-370-xp.h" - +extern void armada_380_scu_enable(void); +static int coherency_type(void); unsigned long coherency_phys_base; void __iomem *coherency_base; static void __iomem *coherency_cpu_base; @@ -70,14 +71,20 @@ int set_cpu_coherent(void) { + int type = coherency_type(); + + if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) { if (!coherency_base) { pr_warn("Can't make current CPU cache coherent.\n"); pr_warn("Coherency fabric is not initialized\n"); return 1; } - ll_add_cpu_to_smp_group(); return ll_enable_coherency(); + } else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380) + armada_380_scu_enable(); + + return 0; } static inline void mvebu_hwcc_sync_io_barrier(void) Index: arch/arm/mach-mvebu/dump_mv_regs.c =================================================================== --- arch/arm/mach-mvebu/dump_mv_regs.c (revision 1) +++ arch/arm/mach-mvebu/dump_mv_regs.c (working copy) @@ -139,6 +139,9 @@ asm volatile ("mrc p15, 0, %0, c10, c2, 1" : "=r" (value)); seq_printf(m, "Memory Attribute NMRR: 0x%08x\n", value); + asm volatile ("mrc p15, 0, %0, c15, c0, 0" : "=r" (value)); + seq_printf(m, "Power Control Register: 0x%08x\n", value); + return 0; } Index: arch/arm/mach-mvebu/include/mach/mvCommon.h =================================================================== --- arch/arm/mach-mvebu/include/mach/mvCommon.h (revision 1) +++ arch/arm/mach-mvebu/include/mach/mvCommon.h (working copy) @@ -245,6 +245,9 @@ #define _250MHz 250000000 #define _266MHz 266666667 #define _300MHz 300000000 +#define _800MHz 800000000 +#define _1GHz 1000000000UL +#define _2GHz 2000000000UL /* Supported clocks */ #define MV_BOARD_TCLK_100MHZ 100000000 Index: arch/arm/mach-mvebu/linux_oss/mvOs.h =================================================================== --- arch/arm/mach-mvebu/linux_oss/mvOs.h (revision 1) +++ arch/arm/mach-mvebu/linux_oss/mvOs.h (working copy) @@ -103,6 +103,7 @@ #define MV_ASM (__asm__ __volatile__) #define INLINE inline #define _INIT __init +#define MV_TRC_REC(...) #define mvOsPrintf printk #define mvOsEarlyPrintf mv_early_printk #define mvOsOutput printk @@ -309,6 +310,7 @@ /* the beginning of the cache line automatically and the size will be adjusted accordingly. */ static inline void mvOsCacheMultiLineFlush(void *handle, void *addr, int size) { + if (unlikely(!COHERENCY_FABRIC_HARD_MODE())) dma_map_single(handle, addr, size, DMA_TO_DEVICE); } @@ -320,6 +322,7 @@ /* DO NOT USE this function unless you are certain of this! */ static inline void mvOsCacheMultiLineInv(void *handle, void *addr, int size) { + if (unlikely(!COHERENCY_FABRIC_HARD_MODE())) dma_map_single(handle, addr, size, DMA_FROM_DEVICE); } @@ -328,6 +331,7 @@ /* the beginning of the cache line automatically and the size will be adjusted accordingly. */ static inline void mvOsCacheMultiLineFlushInv(void *handle, void *addr, int size) { + if (unlikely(!COHERENCY_FABRIC_HARD_MODE())) dma_map_single(handle, addr, size, DMA_BIDIRECTIONAL); } Index: arch/arm/mach-mvebu/Makefile =================================================================== --- arch/arm/mach-mvebu/Makefile (revision 1) +++ arch/arm/mach-mvebu/Makefile (working copy) @@ -12,10 +12,10 @@ obj-y += system-controller.o mvebu-soc-id.o obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o obj-$(CONFIG_MACH_ARMADA_375) += armada-375.o -obj-$(CONFIG_MACH_ARMADA_380) += armada-38x.o pm.o +obj-$(CONFIG_MACH_ARMADA_380) += armada-38x.o obj-$(CONFIG_ARCH_MVEBU) += \ coherency.o coherency_ll.o pmsu.o pmsu_ll.o cpu-reset.o \ - common.o usb-cluster.o usb-utmi.o serdes.o + common.o usb-cluster.o usb-utmi.o serdes.o pm.o pm-board.o ifeq ($(CONFIG_SMP),y) obj-$(CONFIG_MACH_ARMADA_XP) += platsmp.o headsmp.o obj-$(CONFIG_MACH_ARMADA_375) += platsmp-375.o headsmp-375.o Index: arch/arm/mach-mvebu/mvebu-soc-id.c =================================================================== --- arch/arm/mach-mvebu/mvebu-soc-id.c (revision 1) +++ arch/arm/mach-mvebu/mvebu-soc-id.c (working copy) @@ -27,9 +27,12 @@ #define PCIE_DEV_ID_OFF 0x0 #define PCIE_DEV_REV_OFF 0x8 +#define A38X_DEV_ID_OFF 0x38 +#define A38X_DEV_REV_OFF 0x3C #define SOC_ID_MASK 0xFFFF0000 #define SOC_REV_MASK 0xFF +#define A38X_REV_MASK 0xF static u32 soc_dev_id; static u32 soc_rev; @@ -41,6 +44,12 @@ {}, }; +static const struct of_device_id mvebu_a38x_of_match_table[] = { + { .compatible = "marvell,armada-380-system-controller", }, + {}, +}; + + int mvebu_get_soc_id(u32 *dev, u32 *rev) { if (is_id_valid) { @@ -55,13 +64,19 @@ { struct device_node *np; int ret = 0; - void __iomem *pci_base; + void __iomem *reg_base; struct clk *clk; struct device_node *child; + bool is_pcie_id; np = of_find_matching_node(NULL, mvebu_pcie_of_match_table); + if (!np) {/* If no pcie for soc-id, try A38x deicated register */ + np = of_find_matching_node(NULL, mvebu_a38x_of_match_table); if (!np) return ret; + is_pcie_id = false; + } else { + is_pcie_id = true; /* * ID and revision are available from any port, so we @@ -86,30 +101,51 @@ pr_err("cannot enable clock\n"); goto clk_err; } - - pci_base = of_iomap(child, 0); - if (IS_ERR(pci_base)) { + } + if (is_pcie_id == true) + reg_base = of_iomap(child, 0); + else + reg_base = of_iomap(np, 0); + if (IS_ERR(reg_base)) { pr_err("cannot map registers\n"); ret = -ENOMEM; goto res_ioremap; } + if (is_pcie_id == true) { /* SoC ID */ - soc_dev_id = readl(pci_base + PCIE_DEV_ID_OFF) >> 16; - + soc_dev_id = readl(reg_base + PCIE_DEV_ID_OFF) >> 16; /* SoC revision */ - soc_rev = readl(pci_base + PCIE_DEV_REV_OFF) & SOC_REV_MASK; + soc_rev = readl(reg_base + PCIE_DEV_REV_OFF) & SOC_REV_MASK; + } else { + /* SoC ID */ + soc_dev_id = readl(reg_base + A38X_DEV_ID_OFF) >> 16; + /* SoC revision */ + soc_rev = (readl(reg_base + A38X_DEV_REV_OFF) >> 8) & A38X_REV_MASK; + } is_id_valid = true; pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); - iounmap(pci_base); + iounmap(reg_base); res_ioremap: + /* + * If the PCIe unit is actually enabled and we have PCI + * support in the kernel, we intentionally do not release the + * reference to the clock. We want to keep it running since + * the bootloader does some PCIe link configuration that the + * kernel is for now unable to do, and gating the clock would + * make us loose this precious configuration. + */ + if (!of_device_is_available(child) || !IS_ENABLED(CONFIG_PCI_MVEBU)) { clk_disable_unprepare(clk); + clk_put(clk); + } clk_err: + if (is_pcie_id == true) of_node_put(child); of_node_put(np); Index: arch/arm/mach-mvebu/mvebu-soc-id.h =================================================================== --- arch/arm/mach-mvebu/mvebu-soc-id.h (revision 1) +++ arch/arm/mach-mvebu/mvebu-soc-id.h (working copy) @@ -11,6 +11,13 @@ #ifndef __LINUX_MVEBU_SOC_ID_H #define __LINUX_MVEBU_SOC_ID_H +/* Armada 370 ID */ +#define MV6710_DEV_ID 0x6710 +#define MV6707_DEV_ID 0x6707 + +/* Armada 370 Revision */ +#define MV6710_A1_REV 0x1 + /* Armada XP ID */ #define MV78230_DEV_ID 0x7823 #define MV78260_DEV_ID 0x7826 @@ -20,6 +27,28 @@ #define MV78XX0_A0_REV 0x1 #define MV78XX0_B0_REV 0x2 +/* Armada A375 ID */ +#define MV88F6720_DEV_ID 0x6720 + +/* Armada A375 Revision */ +#define MV88F6720_A0_REV 0x1 + +/* Armada A38x ID */ +#define MV88F6810_DEV_ID 0x6810 +#define MV88F6811_DEV_ID 0x6811 +#define MV88F6820_DEV_ID 0x6820 +#define MV88F6828_DEV_ID 0x6828 + +/* Armada A38x Revision */ +#define MV88F68xx_Z1_REV 0x0 +#define MV88F68xx_A0_REV 0x4 + +/* Armada KW2 ID */ +#define MV88F6510_DEV_ID 0x6510 +#define MV88F6530_DEV_ID 0x6530 +#define MV88F6560_DEV_ID 0x6560 +#define MV88F6601_DEV_ID 0x6601 + #ifdef CONFIG_ARCH_MVEBU int mvebu_get_soc_id(u32 *dev, u32 *rev); #else Index: arch/arm/mach-mvebu/platsmp-380.c =================================================================== --- arch/arm/mach-mvebu/platsmp-380.c (revision 1) +++ arch/arm/mach-mvebu/platsmp-380.c (working copy) @@ -79,12 +79,23 @@ register_cpu_notifier(&armada_380_secondary_cpu_notifier); } +#ifdef CONFIG_HOTPLUG_CPU +static void armada_38x_cpu_die(unsigned int cpu) +{ + /* + * CPU hotplug is implemented by putting offline CPUs into the + * deep idle sleep state. + */ + armada_38x_do_cpu_suspend(true); +} +#endif + struct smp_operations armada_380_smp_ops __initdata = { .smp_init_cpus = armada_380_smp_init_cpus, .smp_prepare_cpus = armada_380_smp_prepare_cpus, .smp_boot_secondary = armada_380_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU - .cpu_die = armada_xp_cpu_die, + .cpu_die = armada_38x_cpu_die, #endif }; Index: arch/arm/mach-mvebu/pm.c =================================================================== --- arch/arm/mach-mvebu/pm.c (revision 1) +++ arch/arm/mach-mvebu/pm.c (working copy) @@ -1,5 +1,5 @@ /* -* Suspend/resume + * Suspend/resume support. Currently supporting Armada XP only. * * Copyright (C) 2014 Marvell * @@ -11,131 +11,182 @@ */ #include -#include +#include +#include #include +#include +#include +#include #include - #include -#include +#include #include + #include "coherency.h" -#include -#include -#include +#include "pmsu.h" -static void __iomem *ddr_base; -static void __iomem *gpio_base; +#define SDRAM_CONFIG_OFFS 0x0 +#define SDRAM_CONFIG_SR_MODE_BIT BIT(24) +#define SDRAM_OPERATION_OFFS 0x18 +#define SDRAM_OPERATION_SELF_REFRESH 0x7 +#define SDRAM_DLB_EVICTION_OFFS 0x30c +#define SDRAM_DLB_EVICTION_THRESHOLD_MASK 0xff -static struct of_device_id of_pm_table[] = { - {.compatible = "marvell,armada-380-pm"}, - { /* end of list */ }, -}; - -extern void armada_380_scu_enable(void); extern void armada_38x_cpu_mem_resume(void); -extern void armada_380_resume(void); -extern void armada_380_timer_resume(void); -extern void mvebu_mbus_suspend(void); -extern void mvebu_mbus_resume(void); -extern void armada_370_xp_mpic_suspend(void); -extern void armada_370_xp_mpic_resume(void); -extern int armada_38x_cpuidle_init(void); -extern void mvebu_pcie_suspend(void); -extern void mvebu_pcie_resume(void); +extern int mvebu_pcie_resume(void); +static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd); +static void __iomem *sdram_ctrl; + +static int mvebu_pm_powerdown(unsigned long data) +{ + u32 reg, srcmd; + + flush_cache_all(); + outer_flush_all(); + /* - * Store boot information used by bin header + * Issue a Data Synchronization Barrier instruction to ensure + * that all state saving has been completed. */ -#define BOOT_INFO_ADDR (0x3000) -#define BOOT_MAGIC_WORD (0xDEADB002) -#define REG_LIST_END (0xFFFFFFFF) -#define BOOTROM_INTER_REGS_PHYS_BASE (0xD0000000) -#define INTER_REGS_PHYS_BASE (0xF1000000) + dsb(); + /* Flush the DLB and wait ~7 usec */ + reg = readl(sdram_ctrl + SDRAM_DLB_EVICTION_OFFS); + reg &= ~SDRAM_DLB_EVICTION_THRESHOLD_MASK; + writel(reg, sdram_ctrl + SDRAM_DLB_EVICTION_OFFS); + + udelay(7); + + /* Set DRAM in battery backup mode */ + reg = readl(sdram_ctrl + SDRAM_CONFIG_OFFS); + reg &= ~SDRAM_CONFIG_SR_MODE_BIT; + writel(reg, sdram_ctrl + SDRAM_CONFIG_OFFS); + + /* Prepare to go to self-refresh */ + + srcmd = readl(sdram_ctrl + SDRAM_OPERATION_OFFS); + srcmd &= ~0x1F; + srcmd |= SDRAM_OPERATION_SELF_REFRESH; + + mvebu_board_pm_enter(sdram_ctrl + SDRAM_OPERATION_OFFS, srcmd); + + return 0; +} + +#define BOOT_INFO_ADDR 0x3000 +#define BOOT_MAGIC_WORD 0xdeadb002 +#define BOOT_MAGIC_LIST_END 0xffffffff + +/* + * Those registers are accessed before switching the internal register + * base, which is why we hardcode the 0xd0000000 base address, the one + * used by the SoC out of reset. + */ +#define MBUS_WINDOW_12_CTRL 0xd00200b0 +#define MBUS_INTERNAL_REG_ADDRESS 0xd0020080 + #define SDRAM_WIN_BASE_REG(x) (0x20180 + (0x8*x)) #define SDRAM_WIN_CTRL_REG(x) (0x20184 + (0x8*x)) -#define MAX_CS_COUNT 4 -extern void mvebu_mbus_get_sdram_window(int win, u32 *base, u32 *size); -void armada_store_boot_info(void) +static phys_addr_t mvebu_internal_reg_base(void) { - int *store_addr = (int *)BOOT_INFO_ADDR; - int *resume_pc, win; - u32 base, size; + struct device_node *np; + __be32 in_addr[2]; - store_addr = (int *)phys_to_virt((int)store_addr); - resume_pc = (int *)virt_to_phys(armada_38x_cpu_mem_resume); - /* - * Store magic word indicating suspend to ram and return address. - * Use writel to avoid endianes issues - */ - writel((int)(BOOT_MAGIC_WORD), store_addr++); - writel((int)(resume_pc), store_addr++); + np = of_find_node_by_name(NULL, "internal-regs"); + BUG_ON(!np); /* - * Now store registers that need to be proggrammed before - * comming back to linux. format is addr->value + * Ask the DT what is the internal register address on this + * platform. In the mvebu-mbus DT binding, 0xf0010000 + * corresponds to the internal register window. */ + in_addr[0] = cpu_to_be32(0xf0010000); + in_addr[1] = 0x0; - /* Save AXI windows data */ - for (win = 0; win < 4; win++) { - mvebu_mbus_get_sdram_window(win, &base, &size); + return of_translate_address(np, in_addr); +} - writel(BOOTROM_INTER_REGS_PHYS_BASE + SDRAM_WIN_BASE_REG(win), store_addr++); - writel(base, store_addr++); +static void mvebu_pm_store_bootinfo(void) +{ + u32 *store_addr; + phys_addr_t resume_pc; - writel(BOOTROM_INTER_REGS_PHYS_BASE + SDRAM_WIN_CTRL_REG(win), store_addr++); - writel(size, store_addr++); - } + store_addr = phys_to_virt(BOOT_INFO_ADDR); + /* TBD - Fix support for Armada XP */ + /* resume_pc = virt_to_phys(armada_370_xp_cpu_resume); */ + resume_pc = virt_to_phys(armada_38x_cpu_mem_resume); - /* Mark the end of the boot info*/ - writel(REG_LIST_END, store_addr); -} + /* + * The bootloader expects the first two words to be a magic + * value (BOOT_MAGIC_WORD), followed by the address of the + * resume code to jump to. Then, it expects a sequence of + * (address, value) pairs, which can be used to restore the + * value of certain registers. This sequence must end with the + * BOOT_MAGIC_LIST_END magic value. + */ -/* Function defined in coherency_ll.S */ -int enter_mem_suspend(void __iomem *ddr_addr, void __iomem *gpio_addr); + writel(BOOT_MAGIC_WORD, store_addr++); + writel(resume_pc, store_addr++); -static int mvebu_powerdown(unsigned long val) -{ - /* flush L1 dcache: v7_flush_kern_cache_all */ - v7_flush_kern_cache_all(); + /* + * Some platforms remap their internal register base address + * to 0xf1000000. However, out of reset, window 12 starts at + * 0xf0000000 and ends at 0xf7ffffff, which would overlap with + * the internal registers. Therefore, disable window 12. + */ + writel(MBUS_WINDOW_12_CTRL, store_addr++); + writel(0x0, store_addr++); - /* flush L2 */ - outer_flush_all(); + /* + * Set the internal register base address to the value + * expected by Linux, as read from the Device Tree. + */ + /* TBD - Fix support for Armada XP */ + /* Skip this part for now for Armada 38x - will be done in Linux resume function */ +#if 0 + writel(MBUS_INTERNAL_REG_ADDRESS, store_addr++); */ + writel(mvebu_internal_reg_base(), store_addr++); +#endif - enter_mem_suspend((void *)ddr_base, (void *)gpio_base); + /* + * Ask the mvebu-mbus driver to store the SDRAM window + * configuration, which has to be restored by the bootloader + * before re-entering the kernel on resume. + */ + store_addr += mvebu_mbus_save_cpu_target(store_addr); - return 0; + writel(BOOT_MAGIC_LIST_END, store_addr); } static int mvebu_pm_enter(suspend_state_t state) { - pr_info("Suspending Armada 38x\n"); + if (state != PM_SUSPEND_MEM) + return -EINVAL; cpu_pm_enter(); - cpu_cluster_pm_enter(); - armada_store_boot_info(); + mvebu_pm_store_bootinfo(); - mvebu_pcie_suspend(); - mvebu_mbus_suspend(); - armada_370_xp_mpic_suspend(); + outer_flush_all(); + outer_disable(); - cpu_suspend(0, mvebu_powerdown); + cpu_suspend(0, mvebu_pm_powerdown); - pr_info("Restoring Armada 38x\n"); + /* Disable the L2 Cache after it was being used by the bootROM */ + outer_disable(); + outer_resume(); - armada_380_scu_enable(); - armada_370_xp_mpic_resume(); - mvebu_mbus_resume(); - armada_380_timer_resume(); + mvebu_v7_pmsu_idle_exit(); + + set_cpu_coherent(); + + /* Eraly resume of PCIe to avoid PCIe resume failures - TBD */ mvebu_pcie_resume(); - cpu_cluster_pm_exit(); cpu_pm_exit(); - armada_38x_cpuidle_init(); - return 0; } @@ -144,20 +195,43 @@ .valid = suspend_valid_only_mem, }; -static int __init mvebu_pm_init(void) +int mvebu_pm_init(void (*board_pm_enter)(void __iomem *sdram_reg, u32 srcmd)) { struct device_node *np; + struct resource res; - np = of_find_matching_node(NULL, of_pm_table); - if (np) { - pr_info("Initializing Suspend to RAM\n"); - ddr_base = of_iomap(np, 0); - gpio_base = of_iomap(np, 1); + if (!of_machine_is_compatible("marvell,armadaxp") && + !of_machine_is_compatible("marvell,armada38x")) + return -ENODEV; + + np = of_find_compatible_node(NULL, NULL, + "marvell,armada-xp-sdram-controller"); + if (!np) + return -ENODEV; + + if (of_address_to_resource(np, 0, &res)) { of_node_put(np); + return -ENODEV; } + + if (!request_mem_region(res.start, resource_size(&res), + np->full_name)) { + of_node_put(np); + return -EBUSY; + } + + sdram_ctrl = ioremap(res.start, resource_size(&res)); + if (!sdram_ctrl) { + release_mem_region(res.start, resource_size(&res)); + of_node_put(np); + return -ENOMEM; + } + + of_node_put(np); + + mvebu_board_pm_enter = board_pm_enter; + suspend_set_ops(&mvebu_pm_ops); return 0; } - -arch_initcall(mvebu_pm_init); Index: arch/arm/mach-mvebu/pmsu.c =================================================================== --- arch/arm/mach-mvebu/pmsu.c (revision 1) +++ arch/arm/mach-mvebu/pmsu.c (working copy) @@ -18,9 +18,14 @@ #define pr_fmt(fmt) "mvebu-pmsu: " fmt +#include #include -#include +#include +#include +#include #include +#include +#include #include #include #include @@ -27,8 +32,10 @@ #include #include #include +#include +#include #include -#include +#include #include #include #include @@ -36,6 +43,7 @@ #include #include #include "common.h" +#include "armada-370-xp.h" #define PMSU_BASE_OFFSET 0x100 @@ -60,6 +68,10 @@ #define PMSU_STATUS_AND_MASK_IRQ_MASK BIT(24) #define PMSU_STATUS_AND_MASK_FIQ_MASK BIT(25) +#define PMSU_EVENT_STATUS_AND_MASK(cpu) ((cpu * 0x100) + 0x120) +#define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE BIT(1) +#define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK BIT(17) + #define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) /* PMSU fabric registers */ @@ -90,7 +102,7 @@ extern void armada_370_xp_cpu_resume(void); extern void armada_38x_cpu_resume(void); - +extern struct clk *get_cpu_clk(int cpu); void __iomem *scu_base; static phys_addr_t pmsu_mp_phys_base; @@ -97,6 +109,8 @@ static void __iomem *pmsu_mp_base; static void *mvebu_cpu_resume; +static int (*mvebu_pmsu_dfs_request_ptr)(int cpu); + static void __iomem *sram_wa_virt_base[2]; static struct of_device_id of_pmsu_table[] = { @@ -328,7 +342,7 @@ return cpu_suspend(deepidle, armada_370_xp_pmsu_idle_enter); } -static int armada_38x_do_cpu_suspend(unsigned long deepidle) +int armada_38x_do_cpu_suspend(unsigned long deepidle) { unsigned long flags = 0; @@ -488,8 +502,10 @@ /* Disable the L2 cache clean function as it is being used in the cpu_suspend * Flow and violates the PCIe deadlock WA. * We cannot disable this function in the L2 cache driver as it will break SMP boot */ + /* TBD - Disable cpuidle outer cache WA as it affects Suspend to RAM */ +#if 0 outer_cache.clean_range = NULL; - +#endif return 0; } @@ -538,3 +554,191 @@ arch_initcall(mvebu_v7_cpu_pm_init); early_initcall(mvebu_v7_pmsu_init); + +static void mvebu_pmsu_dfs_request_local(void *data) +{ + u32 reg; + u32 cpu = smp_processor_id(); + unsigned long flags; + + local_irq_save(flags); + + /* Clear any previous DFS DONE event & Mask the DFS done interrupt */ + reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(cpu)); + reg &= ~PMSU_EVENT_STATUS_AND_MASK_DFS_DONE; + reg |= PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK; + writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(cpu)); + + /* Prepare to enter idle */ + reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); + reg |= PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT | + PMSU_STATUS_AND_MASK_IRQ_MASK | + PMSU_STATUS_AND_MASK_FIQ_MASK; + writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); + + /* Request the DFS transition */ + reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(cpu)); + reg |= PMSU_CONTROL_AND_CONFIG_DFS_REQ; + writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(cpu)); + + /* The fact of entering idle will trigger the DFS transition */ + wfi(); + + /* + * We're back from idle, the DFS transition has completed, + * clear the idle wait indication. + */ + reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); + reg &= ~PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT; + writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); + + /* Restore the DFS mask to its original state */ + reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(cpu)); + reg &= ~PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK; + writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(cpu)); + + local_irq_restore(flags); +} + +int armada_xp_pmsu_dfs_request(int cpu) +{ + unsigned long timeout; + int hwcpu = cpu_logical_map(cpu); + u32 reg; + + /* Trigger the DFS on the appropriate CPU */ + smp_call_function_single(cpu, mvebu_pmsu_dfs_request_local, + NULL, false); + + /* Poll until the DFS done event is generated */ + timeout = jiffies + HZ; + while (time_before(jiffies, timeout)) { + reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); + if (reg & PMSU_EVENT_STATUS_AND_MASK_DFS_DONE) + break; + udelay(10); + } + + if (time_after(jiffies, timeout)) + return -ETIME; + + return 0; +} + +int armada_380_pmsu_dfs_request(int cpu) +{ + /* Trigger the DFS on the appropriate CPU */ + on_each_cpu(mvebu_pmsu_dfs_request_local, + NULL, false); + return 0; +} + +int mvebu_pmsu_dfs_request(int cpu) +{ + return mvebu_pmsu_dfs_request_ptr(cpu); +} +#if 0 +struct cpufreq_dt_platform_data armada_xp_cpufreq_dt_pd = { + .independent_clocks = true, +}; + +struct cpufreq_dt_platform_data armada_380_cpufreq_dt_pd = { + .independent_clocks = false, +}; +#endif +static int mvebu_v7_pmsu_register_cpufreq(int cpu) +{ + struct device *cpu_dev; + struct clk *clk; + int ret; + + /* + * registers the operating points + * supported (which are the nominal CPU frequency and half of + * it), and registers the clock notifier that will take care + * of doing the PMSU part of a frequency transition. + */ + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("Cannot get CPU %d\n", cpu); + return 0; + } + + clk = clk_get(cpu_dev, 0); + if (!clk) { + pr_err("Cannot get clock for CPU %d\n", cpu); + return -ENODEV; + } + + /* + * In case of a failure of dev_pm_opp_add(), we don't + * bother with cleaning up the registered OPP (there's + * no function to do so), and simply cancel the + * registration of the cpufreq device. + */ + ret = opp_add(cpu_dev, clk_get_rate(clk), 0); + if (ret) { + clk_put(clk); + return ret; + } + + ret = opp_add(cpu_dev, clk_get_rate(clk) / 2, 0); + if (ret) { + clk_put(clk); + return ret; + } + + return 0; + +} + +static int __init mvebu_v7_pmsu_cpufreq_init(void) +{ + struct device_node *np; + struct resource res; + int ret, cpu; + + /* + * In order to have proper cpufreq handling, we need to ensure + * that the Device Tree description of the CPU clock includes + * the definition of the PMU DFS registers. If not, we do not + * register the clock notifier and the cpufreq driver. This + * piece of code is only for compatibility with old Device + * Trees. + */ + np = of_find_compatible_node(NULL, NULL, "marvell,armada-xp-cpu-clock"); + if (!np) + return 0; + + ret = of_address_to_resource(np, 1, &res); + if (ret) { + pr_warn(FW_WARN "not enabling cpufreq, deprecated armada-xp-cpu-clock binding\n"); + of_node_put(np); + return 0; + } + + of_node_put(np); + + /* register cpu clock for each cpu */ + for_each_possible_cpu(cpu) { + ret = mvebu_v7_pmsu_register_cpufreq(cpu); + if (ret) + return ret; + } +#if 0 + if (of_machine_is_compatible("marvell,armada38x")) { + mvebu_pmsu_dfs_request_ptr = armada_380_pmsu_dfs_request; + platform_device_register_data(NULL, "cpufreq-dt", -1, + &armada_380_cpufreq_dt_pd, sizeof(armada_380_cpufreq_dt_pd)); + } else if (of_machine_is_compatible("marvell,armadaxp")) { + mvebu_pmsu_dfs_request_ptr = armada_xp_pmsu_dfs_request; + platform_device_register_data(NULL, "cpufreq-dt", -1, + &armada_xp_cpufreq_dt_pd, sizeof(armada_xp_cpufreq_dt_pd)); + } else + return 0; +#endif + return 0; +} + +device_initcall(mvebu_v7_pmsu_cpufreq_init); Index: arch/arm/mach-mvebu/pmsu.h =================================================================== --- arch/arm/mach-mvebu/pmsu.h (revision 1) +++ arch/arm/mach-mvebu/pmsu.h (working copy) @@ -17,5 +17,6 @@ phys_addr_t resume_addr_reg); void mvebu_v7_pmsu_idle_exit(void); - +void armada_370_xp_cpu_resume(void); +int armada_38x_do_cpu_suspend(unsigned long deepidle); #endif /* __MACH_370_XP_PMSU_H */ Index: arch/arm/mach-mvebu/pmsu_ll.S =================================================================== --- arch/arm/mach-mvebu/pmsu_ll.S (revision 1) +++ arch/arm/mach-mvebu/pmsu_ll.S (working copy) @@ -19,6 +19,14 @@ */ ENTRY(armada_370_xp_cpu_resume) ARM_BE8(setend be ) @ go BE8 if entered LE + /* + * Disable the MMU that might have been enabled in BootROM if + * this code is used in the resume path of a suspend/resume + * cycle. + */ + mrc p15, 0, r1, c1, c0, 0 + bic r1, #1 + mcr p15, 0, r1, c1, c0, 0 bl ll_add_cpu_to_smp_group bl ll_enable_coherency b cpu_resume @@ -58,6 +66,7 @@ ENDPROC(armada_38x_cpu_resume) ENTRY(armada_38x_cpu_mem_resume) + ARM_BE8(setend be) /* Disable MMU that was enabled in bootROM */ mrc p15, 0, r1, c1, c0, 0 @@ -64,20 +73,9 @@ bic r1, #0x1 mcr p15, 0, r1, c1, c0, 0 - /* Unlock L2 from being used as SRAM */ - ldr r0, =0xd0008000 - mov r1, #0x0 - str r1,[r0, #0x900] /* @Data lock */ - str r1,[r0, #0x904] /* @Instruction lock */ - str r1,[r0, #0x100] /* @Instruction lock */ - - /* Disable open MBUS window @ 0xF0000000 */ - mov r1, #0 - ldr r0, =0xD00200B0 - str r1, [r0] - /* Restore internal registers Base address @ 0xF1000000 */ ldr r1, =0xf1000000 +ARM_BE8(rev r1, r1) ldr r0, =0xD0020080 str r1, [r0] @@ -86,14 +84,24 @@ add r1, r1, r2 /* r1 = INTER_REG_BASE + SCU_OFFSET */ mcr p15, 4, r1, c15, c0, 0 /* Write SCU base register */ - b armada_38x_cpu_resume + bl v7_invalidate_l1 + mrc p15, 4, r1, c15, c0 @ get SCU base address + orr r1, r1, #0x8 @ SCU CPU Power Status Register + mrc 15, 0, r0, cr0, cr0, 5 @ get the CPU ID + and r0, r0, #15 + add r1, r1, r0 + mov r2, #0x0 + strb r2, [r1] @ switch SCU power state to Normal mode + b cpu_resume + ENDPROC(armada_38x_cpu_mem_resume) #define GPIO_32_47_DATA_OUT_EN_CTRL_REG_ADDR (0x144) /* 0x18144 */ #define GPIO_32_47_DATA_OUT_REG_ADDR (0x140) /* 0x18140 */ -#define GPIO_CMD_VALUE (0x8000) -#define GPIO_PIN_MASK (0xFFFF7FFF) +#define GPIO_CMD_VALUE (0x2) +#define GPIO_ACK_VALUE (0xE) +#define GPIO_PIN_MASK (0xFFFFFFFE) #define SDRAM_DLB_EVICT_OFFS_REG (0x30C) /* 0x170C */ #define SDRAM_OPERATION_REG (0x18) /* 0x1418 */ #define SDRAM_CONFIG_REG (0x00) /* 0x1400 */ @@ -113,7 +121,9 @@ ldr r2, =SDRAM_DLB_EVICT_OFFS_REG orr r2, r2, r0 ldr r3, [r2] +ARM_BE8(rev r3, r3) bic r3, #0x000000FF +ARM_BE8(rev r3, r3) str r3, [r2] /* Wait ~7 us */ @@ -126,7 +136,9 @@ ldr r2, =SDRAM_CONFIG_REG orr r2, r2, r0 ldr r3, [r2] +ARM_BE8(rev r3, r3) bic r3, #0x01000000 +ARM_BE8(rev r3, r3) str r3, [r2] /* Prepare to go to self-refresh */ @@ -134,27 +146,49 @@ ldr r2, =(SDRAM_OPERATION_REG) orr r2, r2, r0 ldr r3, [r2] +ARM_BE8(rev r3, r3) ldr r4, =0x00000007 orr r3, r3, r4 +ARM_BE8(rev r3, r3) - /* Configure GPIOs 47 for communicating with PIC */ - /* Prepare value for GPIO 47 */ + /* Configure GPIOs 33-35 for communicating with PIC */ + /* Prepare command value for GPIOs 33-35 */ ldr r4, =(GPIO_32_47_DATA_OUT_REG_ADDR) orr r4, r4, r1 ldr r5, =(GPIO_PIN_MASK) ldr r6, =(GPIO_CMD_VALUE) ldr r7, [r4] +ARM_BE8(rev r7, r7) and r7, r7, r5 orr r7, r7, r6 +ARM_BE8(rev r7, r7) - /* Set GPIO 47 as out */ + /* Set GPIO 33-35 as out */ ldr r0, =(GPIO_32_47_DATA_OUT_EN_CTRL_REG_ADDR) orr r0, r0, r1 ldr r6, [r0] +ARM_BE8(rev r6, r6) and r6, r6, r5 +ARM_BE8(rev r6, r6) str r6, [r0] + /* Issue the cmd */ + str r7, [r4] + + /* Prepare the cmd ack */ + ldr r6, =(GPIO_ACK_VALUE) +ARM_BE8(rev r7, r7) + orr r7, r7, r6 +ARM_BE8(rev r7, r7) + /* + * Wait between cmd (0x1) and cmd ack (0x7) + */ + ldr r1, =1000000000 +1: subs r1,r1,#1 + bne 1b + + /* * Put Dram into self refresh. From here on we can perform * 8 instructions to ensure execution from I-Cache */ Index: arch/arm/mach-orion5x/include/mach/bridge-regs.h =================================================================== --- arch/arm/mach-orion5x/include/mach/bridge-regs.h (revision 1) +++ arch/arm/mach-orion5x/include/mach/bridge-regs.h (working copy) @@ -18,6 +18,7 @@ #define CPU_CTRL (ORION5X_BRIDGE_VIRT_BASE + 0x104) #define RSTOUTn_MASK (ORION5X_BRIDGE_VIRT_BASE + 0x108) +#define RSTOUTn_MASK_PHYS (ORION5X_BRIDGE_PHYS_BASE + 0x108) #define WDT_RESET_OUT_EN 0x0002 #define CPU_SOFT_RESET (ORION5X_BRIDGE_VIRT_BASE + 0x10c) Index: arch/arm/mm/Kconfig =================================================================== --- arch/arm/mm/Kconfig (revision 1) +++ arch/arm/mm/Kconfig (working copy) @@ -862,6 +862,22 @@ instead of this option, thus preventing the user from inadvertently configuring a broken kernel. +config CPU_DYNAMIC_CLOCK_GATING_ENABLE + bool "Enable CPU Dynamic Clock Gating - Power optimization" + depends on CPU_V7 + default y if CPU_V7 && ARCH_MVEBU + help + Say Y here to enable CPU Dynamic Clock Gating. + If unsure, say N. + When dynamic clock gating is enabled, the clock of the system control block is cut + in the following cases: + - there are no system control coprocessor instructions being executed + - there are no system control coprocessor instructions present in the pipeline + - performance events are not enabled + When dynamic clock gating is enabled, the clock of the data engine is cut when there is no + data engine instruction in the data engine and no data engine instruction in the pipeline. + CPU Dynamic Clock Gating is performance feature, + config CPU_L1_CACHE_PREF_ENABLE bool "Enable CPU L1 Cache Prefetch" depends on CPU_V7 Index: arch/arm/mm/proc-v7.S =================================================================== --- arch/arm/mm/proc-v7.S (revision 1) +++ arch/arm/mm/proc-v7.S (working copy) @@ -183,6 +183,11 @@ #ifdef CONFIG_CPU_L1_CACHE_PREF_ENABLE orr r10, r10, #(1 << 2) #endif +#ifdef CONFIG_CPU_DYNAMIC_CLOCK_GATING_ENABLE + mrc p15, 0, r0, c15, c0, 0 + orr r0, r0, #(1 << 0) + mcr p15, 0, r0, c15, c0, 0 +#endif b 1f __v7_ca7mp_setup: __v7_ca15mp_setup: Index: arch/arm/plat-orion/common.c =================================================================== --- arch/arm/plat-orion/common.c (revision 1) +++ arch/arm/plat-orion/common.c (working copy) @@ -594,14 +594,16 @@ /***************************************************************************** * Watchdog ****************************************************************************/ -static struct resource orion_wdt_resource = - DEFINE_RES_MEM(TIMER_PHYS_BASE, 0x28); +static struct resource orion_wdt_resource[] = { + DEFINE_RES_MEM(TIMER_PHYS_BASE, 0x04), + DEFINE_RES_MEM(RSTOUTn_MASK_PHYS, 0x04), +}; static struct platform_device orion_wdt_device = { .name = "orion_wdt", .id = -1, - .num_resources = 1, - .resource = &orion_wdt_resource, + .num_resources = ARRAY_SIZE(orion_wdt_resource), + .resource = orion_wdt_resource, }; void __init orion_wdt_init(void) Index: drivers/ata/sata_mv.c =================================================================== --- drivers/ata/sata_mv.c (revision 1) +++ drivers/ata/sata_mv.c (working copy) @@ -2413,10 +2413,19 @@ { struct mv_port_priv *pp = ap->private_data; struct ata_queued_cmd *qc; + struct ata_link *link = NULL; if (pp->pp_flags & MV_PP_FLAG_NCQ_EN) return NULL; - qc = ata_qc_from_tag(ap, ap->link.active_tag); + + ata_for_each_link(link, ap, EDGE) + if (ata_link_active(link)) + break; + + if (!link) + link = &ap->link; + + qc = ata_qc_from_tag(ap, link->active_tag); if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) return qc; return NULL; Index: drivers/bus/mvebu-mbus.c =================================================================== --- drivers/bus/mvebu-mbus.c (revision 1) +++ drivers/bus/mvebu-mbus.c (working copy) @@ -56,6 +56,8 @@ #include #include #include +#include +#include /* #define MBUS_DEBUG */ @@ -101,9 +103,15 @@ #define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) -#define WIN_REGS_SAVE_NUM 64 +/* Relative to mbusbridge_base */ +#define MBUS_BRIDGE_CTRL_OFF 0x0 +#define MBUS_BRIDGE_SIZE_MASK 0xffff0000 +#define MBUS_BRIDGE_BASE_OFF 0x4 +#define MBUS_BRIDGE_BASE_MASK 0xffff0000 -static u32 mbus_save[WIN_REGS_SAVE_NUM]; +/* Maximum number of windows, for all known platforms */ +#define MBUS_WINS_MAX 20 + struct mvebu_mbus_state; struct mvebu_mbus_soc_data { @@ -110,14 +118,29 @@ unsigned int num_wins; unsigned int (*win_cfg_offset)(const int win); unsigned int (*win_remap_offset)(const int win); + bool has_mbus_bridge; void (*setup_cpu_target)(struct mvebu_mbus_state *s); + int (*save_cpu_target)(struct mvebu_mbus_state *s, + u32 *store_addr); int (*show_cpu_target)(struct mvebu_mbus_state *s, struct seq_file *seq, void *v); }; +/* + * Used to store the state of one MBus window accross suspend/resume. + */ +struct mvebu_mbus_win_data { + u32 ctrl; + u32 base; + u32 remap_lo; + u32 remap_hi; +}; + struct mvebu_mbus_state { void __iomem *mbuswins_base; void __iomem *sdramwins_base; + void __iomem *mbusbridge_base; + phys_addr_t sdramwins_phys_base; struct dentry *debugfs_root; struct dentry *debugfs_sdram; struct dentry *debugfs_devs; @@ -125,6 +148,11 @@ struct resource pcie_io_aperture; const struct mvebu_mbus_soc_data *soc; int hw_io_coherency; + + /* Used during suspend/resume */ + u32 mbus_bridge_ctrl; + u32 mbus_bridge_base; + struct mvebu_mbus_win_data wins[MBUS_WINS_MAX]; }; static struct mvebu_mbus_state mbus_state; @@ -299,6 +327,17 @@ mbus->soc->win_remap_offset(win); u32 ctrl, remap_addr; + if (!is_power_of_2(size)) { + WARN(true, "Invalid MBus window size: 0x%zx\n", size); + return -EINVAL; + } + + if ((base & (phys_addr_t)(size - 1)) != 0) { + WARN(true, "Invalid MBus base/size: %pa len 0x%zx\n", &base, + size); + return -EINVAL; + } + ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) | (attr << WIN_CTRL_ATTR_SHIFT) | (target << WIN_CTRL_TGT_SHIFT) | @@ -462,6 +501,10 @@ win, (unsigned long long)wbase, (unsigned long long)(wbase + wsize), wtarget, wattr); + if (!is_power_of_2(wsize) || + ((wbase & (u64)(wsize - 1)) != 0)) + seq_puts(seq, " (Invalid base/size!!)"); + if (mvebu_mbus_window_is_remappable(mbus, win)) { seq_printf(seq, " (remap %016llx)\n", (unsigned long long)wremap); @@ -560,40 +603,110 @@ { int i; int cs; + struct mvebu_mbus_state *s = &mbus_state; + u32 mbus_bridge_base = 0, mbus_bridge_size = 0; + u64 mbus_bridge_end = 0; + if (s->mbusbridge_base) { + mbus_bridge_base = + (readl(s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF) & + MBUS_BRIDGE_BASE_MASK); + mbus_bridge_size = + (readl(s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF) | + ~MBUS_BRIDGE_SIZE_MASK) + 1; + mbus_bridge_end = (u64)mbus_bridge_base + mbus_bridge_size; + } + mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR; for (i = 0, cs = 0; i < 4; i++) { - u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); - u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); + u64 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); + u64 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); + u64 end; + struct mbus_dram_window *w; - dprintk("%s: base 0x%x, size 0x%x\n", __func__, base, size); + /* Ignore entries that are not enabled */ + if (!(size & DDR_SIZE_ENABLED)) + continue; /* - * We only take care of entries for which the chip - * select is enabled, and that don't have high base - * address bits set (devices can only access the first - * 32 bits of the memory). + * Ignore entries whose base address is above 2^32, + * since devices cannot DMA to such high addresses */ - if ((size & DDR_SIZE_ENABLED) && - !(base & DDR_BASE_CS_HIGH_MASK)) { - struct mbus_dram_window *w; + if (base & DDR_BASE_CS_HIGH_MASK) + continue; + base = base & DDR_BASE_CS_LOW_MASK; + size = (size | ~DDR_SIZE_MASK) + 1; + end = base + size; + + /* + * Adjust base/size of the current CS to make sure it + * doesn't overlap with the MBus bridge window. This + * is particularly important for devices that do DMA + * from DRAM to a SRAM mapped in a MBus window, such + * as the CESA cryptographic engine. + */ + + if (s->mbusbridge_base) { + /* + * The CS is fully enclosed inside the MBus bridge + * area, so ignore it. + */ + if (base >= mbus_bridge_base && end <= mbus_bridge_end) + continue; + + /* + * Beginning of CS overlaps with end of MBus, raise CS + * base address, and shrink its size. + */ + if (base >= mbus_bridge_base && end > mbus_bridge_end) { + pr_info(" ==> 1\n"); + size -= mbus_bridge_end - base; + base = mbus_bridge_end; + } + + /* + * End of CS overlaps with beginning of MBus, shrink + * CS size. + */ + if (base < mbus_bridge_base && end > mbus_bridge_base) + size -= end - mbus_bridge_base; + } + w = &mvebu_mbus_dram_info.cs[cs++]; w->cs_index = i; w->mbus_attr = 0xf & ~(1 << i); if (mbus->hw_io_coherency) w->mbus_attr |= ATTR_HW_COHERENCY; - w->base = base & DDR_BASE_CS_LOW_MASK; - w->size = (size | ~DDR_SIZE_MASK) + 1; - - dprintk("%s win%d, w->base %x, w->size %x\n", - __func__, i, w->base, w->size); + w->base = base; + w->size = size; } - } mvebu_mbus_dram_info.num_cs = cs; } +static int +mvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus, + u32 *store_addr) +{ + int i; + + for (i = 0; i < 4; i++) { + u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); + u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); + + writel(mbus->sdramwins_phys_base + DDR_BASE_CS_OFF(i), + store_addr++); + writel(base, store_addr++); + writel(mbus->sdramwins_phys_base + DDR_SIZE_CS_OFF(i), + store_addr++); + writel(size, store_addr++); + } + + /* We've written 16 words to the store address */ + return 16; +} + static void __init mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) { @@ -624,10 +737,17 @@ mvebu_mbus_dram_info.num_cs = cs; } +int mvebu_mbus_save_cpu_target(u32 *store_addr) +{ + return mbus_state.soc->save_cpu_target(&mbus_state, store_addr); +} + + static const struct mvebu_mbus_soc_data armada_370_mbus_data = { .num_wins = 20, .win_cfg_offset = armada_370_xp_mbus_win_cfg_offset, .win_remap_offset = generic_mbus_win_remap_8_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, }; @@ -634,8 +754,10 @@ static const struct mvebu_mbus_soc_data armada_xp_mbus_data = { .num_wins = 20, + .has_mbus_bridge = true, .win_cfg_offset = armada_370_xp_mbus_win_cfg_offset, .win_remap_offset = armada_xp_mbus_win_remap_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, }; @@ -768,15 +890,36 @@ *res = mbus_state.pcie_io_aperture; } -void mvebu_mbus_get_sdram_window(int win, u32 *base, u32 *size) +int mvebu_mbus_get_addr_win_info(phys_addr_t phyaddr, u8 *trg_id, u8 *attr) { - struct mvebu_mbus_state *mbus = &mbus_state; + const struct mbus_dram_target_info *dram; + int i; - if (!mbus->sdramwins_base) - return; + if (NULL == trg_id || NULL == attr) { + pr_err("%s: Invalid parameter\n", __func__); + return -EINVAL; + } + /* Get dram info */ + dram = mv_mbus_dram_info(); + if (!dram) { + pr_err("%s: No DRAM information\n", __func__); + return -ENODEV; + } + /* Check addr in the range or not */ + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; + if (cs->base <= phyaddr && phyaddr <= (cs->base + cs->size)) { + *trg_id = dram->mbus_dram_target_id; + *attr = cs->mbus_attr; + break; + } + } + if (i == dram->num_cs) { + pr_err("%s: Invalid dram address 0x%x\n", __func__, phyaddr); + return -EINVAL; + } - *base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(win)); - *size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(win)); + return 0; } static __init int mvebu_mbus_debugfs_init(void) @@ -805,11 +948,71 @@ } fs_initcall(mvebu_mbus_debugfs_init); +static int mvebu_mbus_suspend(void) +{ + struct mvebu_mbus_state *s = &mbus_state; + int win; + + if (!s->mbusbridge_base) + return -ENODEV; + + for (win = 0; win < s->soc->num_wins; win++) { + void __iomem *addr = s->mbuswins_base + + s->soc->win_cfg_offset(win); + + s->wins[win].base = readl(addr + WIN_BASE_OFF); + s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF); + + if (mvebu_mbus_window_is_remappable(s, win)) { + s->wins[win].remap_lo = readl(addr + WIN_REMAP_LO_OFF); + s->wins[win].remap_hi = readl(addr + WIN_REMAP_HI_OFF); + } + } + + s->mbus_bridge_ctrl = readl(s->mbusbridge_base + + MBUS_BRIDGE_CTRL_OFF); + s->mbus_bridge_base = readl(s->mbusbridge_base + + MBUS_BRIDGE_BASE_OFF); + + return 0; +} + +static void mvebu_mbus_resume(void) +{ + struct mvebu_mbus_state *s = &mbus_state; + int win; + + writel(s->mbus_bridge_ctrl, + s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF); + writel(s->mbus_bridge_base, + s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF); + + for (win = 0; win < s->soc->num_wins; win++) { + void __iomem *addr = s->mbuswins_base + + s->soc->win_cfg_offset(win); + + writel(s->wins[win].base, addr + WIN_BASE_OFF); + writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF); + + if (mvebu_mbus_window_is_remappable(s, win)) { + writel(s->wins[win].remap_lo, addr + WIN_REMAP_LO_OFF); + writel(s->wins[win].remap_hi, addr + WIN_REMAP_HI_OFF); + } + } +} + +struct syscore_ops mvebu_mbus_syscore_ops = { + .suspend = mvebu_mbus_suspend, + .resume = mvebu_mbus_resume, +}; + static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, phys_addr_t mbuswins_phys_base, size_t mbuswins_size, phys_addr_t sdramwins_phys_base, - size_t sdramwins_size) + size_t sdramwins_size, + phys_addr_t mbusbridge_phys_base, + size_t mbusbridge_size) { int win; @@ -823,11 +1026,26 @@ return -ENOMEM; } + mbus->sdramwins_phys_base = sdramwins_phys_base; + + if (mbusbridge_phys_base) { + mbus->mbusbridge_base = ioremap(mbusbridge_phys_base, + mbusbridge_size); + if (!mbus->mbusbridge_base) { + iounmap(mbus->sdramwins_base); + iounmap(mbus->mbuswins_base); + return -ENOMEM; + } + } else + mbus->mbusbridge_base = NULL; + for (win = 0; win < mbus->soc->num_wins; win++) mvebu_mbus_disable_window(mbus, win); mbus->soc->setup_cpu_target(mbus); + register_syscore_ops(&mvebu_mbus_syscore_ops); + return 0; } @@ -853,7 +1071,7 @@ mbuswins_phys_base, mbuswins_size, sdramwins_phys_base, - sdramwins_size); + sdramwins_size, 0, 0); } #ifdef CONFIG_OF @@ -887,7 +1105,7 @@ return 0; } -static int __init +static int mbus_parse_ranges(struct device_node *node, int *addr_cells, int *c_addr_cells, int *c_size_cells, int *cell_count, const __be32 **ranges_start, @@ -978,7 +1196,7 @@ ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg)); if (!ret) { mem->start = reg[0]; - mem->end = mem->start + reg[1]; + mem->end = mem->start + reg[1] - 1; mem->flags = IORESOURCE_MEM; } @@ -985,7 +1203,7 @@ ret = of_property_read_u32_array(np, "pcie-io-aperture", reg, ARRAY_SIZE(reg)); if (!ret) { io->start = reg[0]; - io->end = io->start + reg[1]; + io->end = io->start + reg[1] - 1; io->flags = IORESOURCE_IO; } } @@ -992,7 +1210,7 @@ int __init mvebu_mbus_dt_init(bool is_coherent) { - struct resource mbuswins_res, sdramwins_res; + struct resource mbuswins_res, sdramwins_res, mbusbridge_res; struct device_node *np, *controller; const struct of_device_id *of_id; const __be32 *prop; @@ -1029,6 +1247,19 @@ return -EINVAL; } + /* + * Set the resource to 0 so that it can be left unmapped by + * mvebu_mbus_common_init() if the DT doesn't carry the + * necessary information. This is needed to preserve backward + * compatibility. + */ + memset(&mbusbridge_res, 0, sizeof(mbusbridge_res)); + + if (mbus_state.soc->has_mbus_bridge) { + if (of_address_to_resource(controller, 2, &mbusbridge_res)) + pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n"); + } + mbus_state.hw_io_coherency = is_coherent; /* Get optional pcie-{mem,io}-aperture properties */ @@ -1039,7 +1270,9 @@ mbuswins_res.start, resource_size(&mbuswins_res), sdramwins_res.start, - resource_size(&sdramwins_res)); + resource_size(&sdramwins_res), + mbusbridge_res.start, + resource_size(&mbusbridge_res)); if (ret) return ret; @@ -1047,6 +1280,52 @@ return mbus_dt_setup(&mbus_state, np); } +int mvebu_mbus_win_addr_get(u8 target_id, u8 attribute, u32 *phy_base, u32 *size) +{ + int addr_cells, c_addr_cells, c_size_cells; + int i, ret, cell_count; + const __be32 *r, *ranges_start, *ranges_end; + struct device_node *np; + + np = of_find_matching_node(NULL, of_mvebu_mbus_ids); + if (!np) { + pr_err("could not find a matching SoC family\n"); + return -ENODEV; + } + + ret = mbus_parse_ranges(np, &addr_cells, &c_addr_cells, + &c_size_cells, &cell_count, + &ranges_start, &ranges_end); + if (ret < 0) + return ret; + + *phy_base = 0; + *size = 0; + for (i = 0, r = ranges_start; r < ranges_end; r += cell_count, i++) { + u32 windowid; + u8 target, attr; + + /* + * An entry with a non-zero custom field do not + * correspond to a static window, so skip it. + */ + windowid = of_read_number(r, 1); + if (CUSTOM(windowid)) + continue; + + target = TARGET(windowid); + attr = ATTR(windowid); + if (target_id != target || attr != attribute) + continue; + + *phy_base = of_read_number(r + c_addr_cells, addr_cells); + *size = of_read_number(r + c_addr_cells + addr_cells, + c_size_cells); + break; + } + return 0; +} + #ifdef MBUS_DEBUG void mbus_debug_window() { @@ -1097,21 +1376,4 @@ } #endif /* MBUS_DEBUG */ -#ifdef CONFIG_PM -void mvebu_mbus_suspend(void) -{ - int reg; - - for (reg = 0; reg < WIN_REGS_SAVE_NUM; reg++) - mbus_save[reg] = readl_relaxed(mbus_state.mbuswins_base + reg * 0x4); -} - -void mvebu_mbus_resume(void) -{ - int reg; - - for (reg = 0; reg < WIN_REGS_SAVE_NUM; reg++) - writel_relaxed(mbus_save[reg], mbus_state.mbuswins_base + reg * 0x4); -} #endif -#endif Index: drivers/clk/mvebu/clk-corediv.c =================================================================== --- drivers/clk/mvebu/clk-corediv.c (revision 1) +++ drivers/clk/mvebu/clk-corediv.c (working copy) @@ -102,14 +102,9 @@ static long clk_corediv_round_rate(struct clk_hw *hwclk, unsigned long rate, unsigned long *parent_rate) { - /* Valid ratio are 1:4, 1:5, 1:6 and 1:8 */ u32 div; - div = *parent_rate / rate; - if (div < 4) - div = 4; - else if (div > 6) - div = 8; + div = DIV_ROUND_UP(*parent_rate, rate); return *parent_rate / div; } Index: drivers/clk/mvebu/clk-cpu.c =================================================================== --- drivers/clk/mvebu/clk-cpu.c (revision 1) +++ drivers/clk/mvebu/clk-cpu.c (working copy) @@ -16,12 +16,39 @@ #include #include #include +#include +#include #define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 -#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC +#define SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL 0xff +#define SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT 8 +#define SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET 0x8 +#define SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT 16 #define SYS_CTRL_CLK_DIVIDER_MASK 0x3F +#define SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_OFFSET 0x0 +#define SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_RELOAD_SMOOTH_MASK 0xff +#define SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_RELOAD_SMOOTH_SHIFT 0x8 +#define SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_RELOAD_SMOOTH_PCLK 0x10 +#define SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_OFFSET 0x4 +#define SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_RESET_MASK_MASK 0xff +#define SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_RESET_MASK_SHIFT 0x0 +#define SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_RESET_MASK_PCLK 0x10 +#define SYS_CTRL_ACTIVATE_IF_CTRL_OFFSET 0x3c +#define SYS_CTRL_ACTIVATE_IF_CTRL_PMU_DFS_OVRD_EN_MASK 0xff +#define SYS_CTRL_ACTIVATE_IF_CTRL_PMU_DFS_OVRD_EN_SHIFT 17 +#define SYS_CTRL_ACTIVATE_IF_CTRL_PMU_DFS_OVRD_EN 0x1 + + +#define PMU_DFS_RATIO_SHIFT 16 +#define PMU_DFS_RATIO_MASK 0x3F + #define MAX_CPU 4 + +struct cpu_clk_regs { + u32 clk_divider_value_offset; +}; + struct cpu_clk { struct clk_hw hw; int cpu; @@ -28,6 +55,9 @@ const char *clk_name; const char *parent_name; void __iomem *reg_base; + void __iomem *pmu_dfs; + void __iomem *dfx_server_base; + const struct cpu_clk_regs *clk_regs; }; static struct clk **clks; @@ -36,17 +66,34 @@ #define to_cpu_clk(p) container_of(p, struct cpu_clk, hw) -static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk, +static unsigned long armada_xp_clk_cpu_recalc_rate(struct clk_hw *hwclk, unsigned long parent_rate) { struct cpu_clk *cpuclk = to_cpu_clk(hwclk); u32 reg, div; - reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + reg = readl(cpuclk->reg_base + cpuclk->clk_regs->clk_divider_value_offset); div = (reg >> (cpuclk->cpu * 8)) & SYS_CTRL_CLK_DIVIDER_MASK; return parent_rate / div; } +static unsigned long armada_380_clk_cpu_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + u32 reg, div; + + if (__clk_is_enabled(hwclk->clk) == false) { + /* for clock init - don't use divider, set maximal rate */ + return parent_rate; + } + + reg = readl(cpuclk->reg_base + cpuclk->clk_regs->clk_divider_value_offset); + div = reg & SYS_CTRL_CLK_DIVIDER_MASK; + return parent_rate / div; +} + + static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate, unsigned long *parent_rate) { @@ -62,8 +109,9 @@ return *parent_rate / div; } -static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, +static int armada_xp_clk_cpu_off_set_rate(struct clk_hw *hwclk, unsigned long rate, unsigned long parent_rate) + { struct cpu_clk *cpuclk = to_cpu_clk(hwclk); u32 reg, div; @@ -70,10 +118,10 @@ u32 reload_mask; div = parent_rate / rate; - reg = (readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET) + reg = (readl(cpuclk->reg_base + cpuclk->clk_regs->clk_divider_value_offset) & (~(SYS_CTRL_CLK_DIVIDER_MASK << (cpuclk->cpu * 8)))) | (div << (cpuclk->cpu * 8)); - writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + writel(reg, cpuclk->reg_base + cpuclk->clk_regs->clk_divider_value_offset); /* Set clock divider reload smooth bit mask */ reload_mask = 1 << (20 + cpuclk->cpu); @@ -95,18 +143,143 @@ return 0; } -static const struct clk_ops cpu_ops = { - .recalc_rate = clk_cpu_recalc_rate, +static int armada_xp_clk_cpu_on_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + u32 reg; + unsigned long fabric_div, target_div, cur_rate; + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + + /* + * PMU DFS registers are not mapped, Device Tree does not + * describes them. We cannot change the frequency dynamically. + */ + if (!cpuclk->pmu_dfs) + return -ENODEV; + + cur_rate = __clk_get_rate(hwclk->clk); + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET); + fabric_div = (reg >> SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT) & + SYS_CTRL_CLK_DIVIDER_MASK; + + /* Frequency is going up */ + if (rate == 2 * cur_rate) + target_div = fabric_div / 2; + /* Frequency is going down */ + else + target_div = fabric_div; + + if (target_div == 0) + target_div = 1; + + reg = readl(cpuclk->pmu_dfs); + reg &= ~(PMU_DFS_RATIO_MASK << PMU_DFS_RATIO_SHIFT); + reg |= (target_div << PMU_DFS_RATIO_SHIFT); + writel(reg, cpuclk->pmu_dfs); + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + reg |= (SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL << + SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + return mvebu_pmsu_dfs_request(cpuclk->cpu); +} + +static int armada_xp_clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + if (__clk_is_enabled(hwclk->clk)) + return armada_xp_clk_cpu_on_set_rate(hwclk, rate, parent_rate); + else + return armada_xp_clk_cpu_off_set_rate(hwclk, rate, parent_rate); +} + + +static int armada_380_clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + u32 reg; + u32 target_div; + unsigned long cur_rate; + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + + /* + * PMU DFS registers are not mapped, Device Tree does not + * describes them. We cannot change the frequency dynamically. + */ + if (!cpuclk->pmu_dfs) + return -ENODEV; + + cur_rate = __clk_get_rate(hwclk->clk); + + /* Frequency is going up */ + if (rate >= cur_rate) + target_div = 1; + /* Frequency is going down */ + else + target_div = 2; + + reg = readl(cpuclk->dfx_server_base + SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_OFFSET); + reg &= ~(SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_RELOAD_SMOOTH_MASK << + SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_RELOAD_SMOOTH_SHIFT); + reg |= (SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_RELOAD_SMOOTH_PCLK << + SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_RELOAD_SMOOTH_SHIFT); + writel(reg, cpuclk->dfx_server_base + SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL0_OFFSET); + + reg = readl(cpuclk->dfx_server_base + SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_OFFSET); + reg &= ~(SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_RESET_MASK_MASK << + SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_RESET_MASK_SHIFT); + reg |= (SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_RESET_MASK_PCLK << + SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_RESET_MASK_SHIFT); + writel(reg, cpuclk->dfx_server_base + SYS_CTRL_CPU_PLL_CLOCK_DIVIDER_CTRL1_OFFSET); + + reg = readl(cpuclk->pmu_dfs); + reg &= ~(PMU_DFS_RATIO_MASK << PMU_DFS_RATIO_SHIFT); + reg |= (target_div << PMU_DFS_RATIO_SHIFT); + writel(reg, cpuclk->pmu_dfs); + + reg = readl(cpuclk->pmu_dfs + SYS_CTRL_ACTIVATE_IF_CTRL_OFFSET); + reg &= ~(SYS_CTRL_ACTIVATE_IF_CTRL_PMU_DFS_OVRD_EN_MASK << + SYS_CTRL_ACTIVATE_IF_CTRL_PMU_DFS_OVRD_EN_SHIFT); + reg |= (SYS_CTRL_ACTIVATE_IF_CTRL_PMU_DFS_OVRD_EN << + SYS_CTRL_ACTIVATE_IF_CTRL_PMU_DFS_OVRD_EN_SHIFT); + writel(reg, cpuclk->pmu_dfs + SYS_CTRL_ACTIVATE_IF_CTRL_OFFSET); + + return mvebu_pmsu_dfs_request(cpuclk->cpu); +} + +static const struct clk_ops armada_xp_cpu_ops = { + .recalc_rate = armada_xp_clk_cpu_recalc_rate, .round_rate = clk_cpu_round_rate, - .set_rate = clk_cpu_set_rate, + .set_rate = armada_xp_clk_cpu_set_rate, }; +static const struct clk_ops armada_380_cpu_ops = { + .recalc_rate = armada_380_clk_cpu_recalc_rate, + .round_rate = clk_cpu_round_rate, + .set_rate = armada_380_clk_cpu_set_rate, +}; + +static const struct cpu_clk_regs armada_xp_cpu_clk_regs = { + .clk_divider_value_offset = 0xC, +}; + +static const struct cpu_clk_regs armada_380_cpu_clk_regs = { + .clk_divider_value_offset = 0x4, +}; + void __init of_cpu_clk_setup(struct device_node *node) { struct cpu_clk *cpuclk; void __iomem *clock_complex_base = of_iomap(node, 0); + void __iomem *pmu_dfs_base = of_iomap(node, 1); + void __iomem *dfx_server_base = of_iomap(node, 2); int ncpus = 0; struct device_node *dn; + const struct clk_ops *cpu_ops = NULL; + const struct cpu_clk_regs *cpu_regs = NULL; + bool independent_clocks = true; if (clock_complex_base == NULL) { pr_err("%s: clock-complex base register not set\n", @@ -114,8 +287,26 @@ return; } + if (pmu_dfs_base == NULL) + pr_warn("%s: pmu-dfs base register not set, dynamic frequency scaling not available\n", + __func__); + + if (of_machine_is_compatible("marvell,armada380")) { + if (dfx_server_base == NULL) { + pr_err("%s: DFX server base register not set\n", + __func__); + return; + } + cpu_ops = &armada_380_cpu_ops; + cpu_regs = &armada_380_cpu_clk_regs; + independent_clocks = false; + ncpus = 1; + } else { + cpu_ops = &armada_xp_cpu_ops; + cpu_regs = &armada_xp_cpu_clk_regs; for_each_node_by_type(dn, "cpu") ncpus++; + } cpuclk = kzalloc(ncpus * sizeof(*cpuclk), GFP_KERNEL); if (WARN_ON(!cpuclk)) @@ -146,10 +337,15 @@ cpuclk[cpu].clk_name = clk_name; cpuclk[cpu].cpu = cpu; cpuclk[cpu].reg_base = clock_complex_base; + if (pmu_dfs_base) + cpuclk[cpu].pmu_dfs = pmu_dfs_base + 4 * cpu; + + cpuclk[cpu].dfx_server_base = dfx_server_base; cpuclk[cpu].hw.init = &init; + cpuclk[cpu].clk_regs = cpu_regs; init.name = cpuclk[cpu].clk_name; - init.ops = &cpu_ops; + init.ops = cpu_ops; init.flags = 0; init.parent_names = &cpuclk[cpu].parent_name; init.num_parents = 1; @@ -158,7 +354,12 @@ if (WARN_ON(IS_ERR(clk))) goto bail_out; clks[cpu] = clk; + + if (independent_clocks == false) { + /* use 1 clock to all cpus */ + break; } + } clk_data.clk_num = MAX_CPU; clk_data.clks = clks; of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data); Index: drivers/clk/mvebu/clk-gating-ctrl.c =================================================================== --- drivers/clk/mvebu/clk-gating-ctrl.c (revision 1) +++ drivers/clk/mvebu/clk-gating-ctrl.c (working copy) @@ -17,11 +17,14 @@ #include #include #include +#include struct mvebu_gating_ctrl { spinlock_t lock; struct clk **gates; int num_gates; + void __iomem *base; + u32 saved_reg; }; struct mvebu_soc_descr { @@ -32,10 +35,11 @@ #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) +static struct mvebu_gating_ctrl *ctrl; + static struct clk *mvebu_clk_gating_get_src( struct of_phandle_args *clkspec, void *data) { - struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data; int n; if (clkspec->args_count < 1) @@ -50,15 +54,35 @@ return ERR_PTR(-ENODEV); } +static int mvebu_clk_gating_suspend(void) +{ + ctrl->saved_reg = readl(ctrl->base); + return 0; +} + +static void mvebu_clk_gating_resume(void) +{ + writel(ctrl->saved_reg, ctrl->base); +} + +static struct syscore_ops clk_gate_syscore_ops = { + .suspend = mvebu_clk_gating_suspend, + .resume = mvebu_clk_gating_resume, +}; + static void __init mvebu_clk_gating_setup( struct device_node *np, const struct mvebu_soc_descr *descr) { - struct mvebu_gating_ctrl *ctrl; struct clk *clk; void __iomem *base; const char *default_parent = NULL; int n; + if (ctrl) { + pr_err("mvebu-clk-gating: cannot instantiate more than one gatable clock device\n"); + return; + } + base = of_iomap(np, 0); clk = of_clk_get(np, 0); @@ -73,6 +97,8 @@ spin_lock_init(&ctrl->lock); + ctrl->base = base; + /* * Count, allocate, and register clock gates */ @@ -106,6 +132,8 @@ WARN_ON(IS_ERR(ctrl->gates[n])); } of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); + + register_syscore_ops(&clk_gate_syscore_ops); } /* @@ -191,6 +219,7 @@ { "crypto0", NULL, 23 }, { "tdm", NULL, 25 }, { "xor1", NULL, 28 }, + { "pnc", NULL, 29 }, { "sata1", NULL, 30 }, { } }; Index: drivers/clocksource/time-armada-370-xp.c =================================================================== --- drivers/clocksource/time-armada-370-xp.c (revision 1) +++ drivers/clocksource/time-armada-370-xp.c (working copy) @@ -49,6 +49,8 @@ #include #include #include +#include + /* * Timer block registers. */ @@ -230,6 +232,28 @@ .stop = armada_370_xp_timer_stop, }; +static u32 timer0_ctrl_reg, timer0_local_ctrl_reg; + +static int armada_370_xp_timer_suspend(void) +{ + timer0_ctrl_reg = readl(timer_base + TIMER_CTRL_OFF); + timer0_local_ctrl_reg = readl(local_base + TIMER_CTRL_OFF); + return 0; +} + +static void armada_370_xp_timer_resume(void) +{ + writel(0xffffffff, timer_base + TIMER0_VAL_OFF); + writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); + writel(timer0_ctrl_reg, timer_base + TIMER_CTRL_OFF); + writel(timer0_local_ctrl_reg, local_base + TIMER_CTRL_OFF); +} + +struct syscore_ops armada_370_xp_timer_syscore_ops = { + .suspend = armada_370_xp_timer_suspend, + .resume = armada_370_xp_timer_resume, +}; + static void __init armada_370_xp_timer_common_init(struct device_node *np) { u32 clr = 0, set = 0; @@ -295,6 +319,8 @@ local_timer_register(&armada_370_xp_local_timer_ops); #endif } + + register_syscore_ops(&armada_370_xp_timer_syscore_ops); } static void __init armada_xp_timer_init(struct device_node *np) @@ -348,28 +374,3 @@ } CLOCKSOURCE_OF_DECLARE(armada_380, "marvell,armada-380-timer", armada_380_timer_init); - -#ifdef CONFIG_PM -void armada_380_timer_resume(void) -{ - u32 clr = 0, set = 0; - int res; - - if (timer25Mhz) - set = TIMER0_25MHZ; - else - clr = TIMER0_25MHZ; - timer_ctrl_clrset(clr, set); - local_timer_ctrl_clrset(clr, set); - - /* - * Setup free-running clocksource timer (interrupts - * disabled). - */ - writel(0xffffffff, timer_base + TIMER0_VAL_OFF); - writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); - - timer_ctrl_clrset(0, TIMER0_EN | TIMER0_RELOAD_EN | - TIMER0_DIV(TIMER_DIVIDER_SHIFT)); -} -#endif Index: drivers/crypto/mvebu_cesa/cesa_apps/openssl/README =================================================================== --- drivers/crypto/mvebu_cesa/cesa_apps/openssl/README (revision 1) +++ drivers/crypto/mvebu_cesa/cesa_apps/openssl/README (working copy) @@ -7,8 +7,22 @@ o In case missing, create a crypto device: mknod /dev/crypto c 10 70 o Download latest openssl source package from http://www.openssl.org, then untar+unzip it. - o In case missing, copy from kernel crypto/ocf/cryptodev.h to file-system path: /usr/include/crypto . - o Run: ./config -DHAVE_CRYPTODEV no-shared +o copy from kernel crypto/ocf/cryptodev.h to openSSL build file-system path: /usr/include/crypto . +o If using Marvell Cross complier, set the following flags according to your file location (OpenSSL sources and Marvell toolchain location) + export INSTALLDIR=/home//work/openSSL/openssl-1.0.2 + export PATH=$INSTALLDIR/bin:$PATH + export TARGETMACH=arm-none-linux-gnueabi + export BUILDMACH=i686-pc-linux-gnu + export CROSS=/opt/armv7-marvell-linux-gnueabi-hard-4.6.4_i686_20140402/bin/arm-marvell-linux-gnueabi + export CC=${CROSS}-gc + export CC=${CROSS}-gcc + export LD=${CROSS}-ld + export AS=${CROSS}-as + export AR=${CROSS}-ar + ./Configure -DHAVE_CRYPTODEV no-shared --openssldir=/home//work/openSSL/openssl-1.0.2/final os/compiler:arm-none-linux-gnueabi- + make RANLIB=${CROSS}ranlib + sudo -E make install +o If using ARM native comiler, run: ./config -DHAVE_CRYPTODEV no-shared o and compile ...('make' and 'make install') you can run a speed test to make sure everything is working: Index: drivers/crypto/mvebu_cesa/cesa_if.c =================================================================== --- drivers/crypto/mvebu_cesa/cesa_if.c (revision 1) +++ drivers/crypto/mvebu_cesa/cesa_if.c (working copy) @@ -99,6 +99,14 @@ u32 mv_cesa_time_threshold, mv_cesa_threshold, mv_cesa_channels; enum cesa_feature mv_cesa_feature = CESA_UNKNOWN; +struct cesa_s2r_reg { + uint32_t desc_offset; + uint32_t config_reg; + uint32_t int_coal_th; + uint32_t int_time_th; + uint32_t tdma_ctrl; +}; + MV_STATUS mvCesaIfInit(int numOfSession, int queueDepth, void *osHandle, MV_CESA_HAL_DATA *halData) { MV_U8 chan = 0; @@ -682,7 +690,16 @@ struct device_node *np, *np_sram; struct resource res; int err, ret; +#ifdef CONFIG_PM + struct cesa_s2r_reg (*s2r_reg)[MV_CESA_CHANNELS]; + s2r_reg = devm_kzalloc(&pdev->dev, MV_CESA_CHANNELS * sizeof(struct cesa_s2r_reg), GFP_KERNEL); + if (!s2r_reg) + return -ENOMEM; + + platform_set_drvdata(pdev, s2r_reg); +#endif + np_sram = of_find_compatible_node(NULL, NULL, "marvell,cesa-sram"); if (!np_sram) { dev_err(&pdev->dev, "Cannot find 'marvell,cesa-sram' node"); @@ -763,3 +780,46 @@ return status; } + +#ifdef CONFIG_PM +int cesa_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct cesa_s2r_reg (*s2r_reg)[MV_CESA_CHANNELS] = platform_get_drvdata(pdev); + uint8_t chan; + + for (chan = 0; chan < MV_CESA_CHANNELS; chan++) { + s2r_reg[chan]->desc_offset = MV_REG_READ(MV_CESA_CHAN_DESC_OFFSET_REG(chan)); + s2r_reg[chan]->config_reg = MV_REG_READ(MV_CESA_CFG_REG(chan)); + s2r_reg[chan]->int_coal_th = MV_REG_READ(MV_CESA_INT_COAL_TH_REG(chan)); + s2r_reg[chan]->int_time_th = MV_REG_READ(MV_CESA_INT_TIME_TH_REG(chan)); + s2r_reg[chan]->tdma_ctrl = MV_REG_READ(MV_CESA_TDMA_CTRL_REG(chan)); + } + + return 0; +} + +int cesa_resume(struct platform_device *pdev) +{ + struct cesa_s2r_reg (*s2r_reg)[MV_CESA_CHANNELS] = platform_get_drvdata(pdev); + const struct mbus_dram_target_info *dram; + uint8_t chan; + + dram = mv_mbus_dram_info(); + + for (chan = 0; chan < MV_CESA_CHANNELS; chan++) { + mv_cesa_conf_mbus_windows(dram, chan); + + MV_REG_WRITE(MV_CESA_CHAN_DESC_OFFSET_REG(chan), s2r_reg[chan]->desc_offset); + MV_REG_WRITE(MV_CESA_CFG_REG(chan), s2r_reg[chan]->config_reg); + MV_REG_WRITE(MV_CESA_INT_COAL_TH_REG(chan), s2r_reg[chan]->int_coal_th); + MV_REG_WRITE(MV_CESA_INT_TIME_TH_REG(chan), s2r_reg[chan]->int_time_th); + MV_REG_WRITE(MV_CESA_TDMA_CTRL_REG(chan), s2r_reg[chan]->tdma_ctrl); + + /* clear and unmask Int */ + MV_REG_WRITE(MV_CESA_ISR_CAUSE_REG(chan), 0); + MV_REG_WRITE(MV_CESA_ISR_MASK_REG(chan), MV_CESA_CAUSE_EOP_COAL_MASK); + } + + return 0; +} +#endif Index: drivers/crypto/mvebu_cesa/cesa_if.h =================================================================== --- drivers/crypto/mvebu_cesa/cesa_if.h (revision 1) +++ drivers/crypto/mvebu_cesa/cesa_if.h (working copy) @@ -87,6 +87,11 @@ #include #include +#ifdef CONFIG_PM +int cesa_suspend(struct platform_device *pdev, pm_message_t state); +int cesa_resume(struct platform_device *pdev); +#endif + /* #define CESA_DEBUGS */ #ifdef CESA_DEBUGS #define dprintk(a...) printk(a) Index: drivers/crypto/mvebu_cesa/cesa_ocf_drv.c =================================================================== --- drivers/crypto/mvebu_cesa/cesa_ocf_drv.c (revision 1) +++ drivers/crypto/mvebu_cesa/cesa_ocf_drv.c (working copy) @@ -83,7 +83,7 @@ /* general defines */ #define CESA_OCF_MAX_SES 128 -#define CESA_Q_SIZE 64 +#define CESA_Q_SIZE 256 #define CESA_RESULT_Q_SIZE (CESA_Q_SIZE * MV_CESA_CHANNELS * 2) #define CESA_OCF_POOL_SIZE (CESA_Q_SIZE * MV_CESA_CHANNELS * 2) @@ -1356,6 +1356,10 @@ .probe = cesa_ocf_probe, .remove = cesa_ocf_remove, .shutdown = cesa_ocf_shutdown, +#ifdef CONFIG_PM + .resume = cesa_resume, + .suspend = cesa_suspend, +#endif }; static int __init cesa_ocf_init(void) Index: drivers/crypto/mvebu_cesa/cesa_test.c =================================================================== --- drivers/crypto/mvebu_cesa/cesa_test.c (revision 1) +++ drivers/crypto/mvebu_cesa/cesa_test.c (working copy) @@ -3142,6 +3142,10 @@ }, .probe = cesa_test_probe, .remove = cesa_test_remove, +#ifdef CONFIG_PM + .resume = cesa_resume, + .suspend = cesa_suspend, +#endif }; static int __init cesa_test_init(void) Index: drivers/dma/mv_xor.c =================================================================== --- drivers/dma/mv_xor.c (revision 1) +++ drivers/dma/mv_xor.c (working copy) @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,11 @@ unsigned int dummy2[MV_XOR_MIN_BYTE_COUNT]; dma_addr_t dummy1_addr, dummy2_addr; +enum mv_xor_mode { + XOR_MODE_IN_REG, + XOR_MODE_IN_DESC, +}; + static void mv_xor_issue_pending(struct dma_chan *chan); #define to_mv_xor_chan(chan) \ @@ -57,8 +63,9 @@ hw_desc->status = (1 << 31); hw_desc->phy_next_desc = 0; + if (flags & DMA_PREP_INTERRUPT) - command = (1 << 31); + command |= (1 << 31); if (desc->type == DMA_CRC32C) command |= (1 << 30); /* CRCLast */ @@ -66,6 +73,27 @@ hw_desc->desc_command = command; } +static void mv_desc_set_mode(struct mv_xor_desc_slot *desc) +{ + struct mv_xor_desc *hw_desc = desc->hw_desc; + + switch (desc->type) { + case DMA_XOR: + case DMA_INTERRUPT: + hw_desc->desc_command |= XOR_DESC_OPERATION_XOR; + break; + case DMA_CRC32C: + hw_desc->desc_command |= XOR_DESC_OPERATION_CRC32C; + break; + case DMA_MEMCPY: + hw_desc->desc_command |= XOR_DESC_OPERATION_MEMCPY; + break; + default: + BUG(); + return; + } +} + static u32 mv_desc_get_dest_addr(struct mv_xor_desc_slot *desc) { struct mv_xor_desc *hw_desc = desc->hw_desc; @@ -79,7 +107,6 @@ return hw_desc->phy_src_addr[mv_phy_src_idx(src_idx)]; } - static void mv_desc_set_byte_count(struct mv_xor_desc_slot *desc, u32 byte_count) { @@ -95,12 +122,6 @@ hw_desc->phy_next_desc = next_desc_addr; } -static void mv_desc_clear_next_desc(struct mv_xor_desc_slot *desc) -{ - struct mv_xor_desc *hw_desc = desc->hw_desc; - hw_desc->phy_next_desc = 0; -} - static void mv_desc_set_dest_addr(struct mv_xor_desc_slot *desc, dma_addr_t addr) { @@ -200,6 +221,19 @@ chan->current_type = type; } +static void mv_set_mode_on_desc(struct mv_xor_chan *chan) +{ + u32 op_mode; + u32 config = readl_relaxed(XOR_CONFIG(chan)); + + op_mode = XOR_OPERATION_MODE_IN_DESC; + + config &= ~0x7; + config |= op_mode; + + writel_relaxed(config, XOR_CONFIG(chan)); +} + static void mv_chan_activate(struct mv_xor_chan *chan) { dev_dbg(mv_chan_to_devp(chan), "activate chan %d\n", chan->dmadev.dev_id); @@ -218,21 +252,6 @@ return (state == 1) ? 1 : 0; } -/** - * mv_xor_free_slots - flags descriptor slots for reuse - * @slot: Slot to free - * Caller must hold &mv_chan->lock while calling this function - */ -static void mv_xor_free_slots(struct mv_xor_chan *mv_chan, - struct mv_xor_desc_slot *slot) -{ - dev_dbg(mv_chan_to_devp(mv_chan), "%s %d slot %p\n", - __func__, __LINE__, slot); - - slot->slot_used = 0; - -} - /* * mv_xor_start_new_chain - program the engine to operate on new chain headed by * sw_desc @@ -316,13 +335,11 @@ dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__); list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots, - completed_node) { + node) { - if (async_tx_test_ack(&iter->async_tx)) { - list_del(&iter->completed_node); - mv_xor_free_slots(mv_chan, iter); + if (async_tx_test_ack(&iter->async_tx)) + list_move_tail(&iter->node, &mv_chan->free_slots); } - } return 0; } @@ -332,17 +349,16 @@ { dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: desc %p flags %d\n", __func__, __LINE__, desc, desc->async_tx.flags); - list_del(&desc->chain_node); + /* the client is allowed to attach dependent operations * until 'ack' is set */ - if (!async_tx_test_ack(&desc->async_tx)) { + if (!async_tx_test_ack(&desc->async_tx)) /* move this slot to the completed_slots */ - list_add_tail(&desc->completed_node, &mv_chan->completed_slots); - return 0; - } + list_move_tail(&desc->node, &mv_chan->completed_slots); + else + list_move_tail(&desc->node, &mv_chan->free_slots); - mv_xor_free_slots(mv_chan, desc); return 0; } @@ -352,7 +368,8 @@ dma_cookie_t cookie = 0; int busy = mv_chan_is_busy(mv_chan); u32 current_desc = mv_chan_get_current_desc(mv_chan); - int seen_current = 0; + int current_cleaned = 0; + struct mv_xor_desc *hw_desc; struct dma_chan *dma_chan; dma_chan = &mv_chan->dmachan; @@ -372,26 +389,10 @@ */ list_for_each_entry_safe(iter, _iter, &mv_chan->chain, - chain_node) { - prefetch(_iter); - prefetch(&_iter->async_tx); - - /* do not advance past the current descriptor loaded into the - * hardware channel, subsequent descriptors are either in - * process or have not been submitted - */ - if (seen_current) - break; - - /* stop the search if we reach the current descriptor and the - * channel is busy - */ - if (iter->async_tx.phys == current_desc) { - seen_current = 1; - if (busy) - break; - } - + node) { + /* clean finished descriptors */ + hw_desc = iter->hw_desc; + if (hw_desc->status & XOR_DESC_SUCCESS) { if (iter->type == DMA_CRC32C) { struct mv_xor_desc *hw_desc = iter->hw_desc; BUG_ON(!iter->crc32_result); @@ -400,17 +401,40 @@ cookie = mv_xor_run_tx_complete_actions(iter, mv_chan, cookie); + /* done processing desc, clean slot */ mv_xor_clean_slot(iter, mv_chan); + + /* break if we did cleaned the current */ + if (iter->async_tx.phys == current_desc) { + current_cleaned = 1; + break; } + } else { + if (iter->async_tx.phys == current_desc) { + current_cleaned = 0; + break; + } + } + } if ((busy == 0) && !list_empty(&mv_chan->chain)) { - struct mv_xor_desc_slot *chain_head; - chain_head = list_entry(mv_chan->chain.next, + if (current_cleaned) { + /* current descriptor cleaned and removed, run from list head */ + iter = list_entry(mv_chan->chain.next, struct mv_xor_desc_slot, - chain_node); - - mv_xor_start_new_chain(mv_chan, chain_head); + node); + mv_xor_start_new_chain(mv_chan, iter); + } else { + if (!list_is_last(&iter->node, &mv_chan->chain)) { + /* descriptors are still waiting after current, trigger them */ + iter = list_entry(iter->node.next, struct mv_xor_desc_slot, node); + mv_xor_start_new_chain(mv_chan, iter); + } else { + /* some descriptors are still waiting to be cleaned */ + tasklet_schedule(&mv_chan->irq_tasklet); } + } + } if (cookie > 0) mv_chan->dmachan.completed_cookie = cookie; @@ -430,51 +454,30 @@ mv_xor_slot_cleanup(chan); } -static struct mv_xor_desc_slot * -mv_xor_alloc_slot(struct mv_xor_chan *mv_chan) +static struct mv_xor_desc_slot *mv_xor_alloc_slot(struct mv_xor_chan *mv_chan) { - struct mv_xor_desc_slot *iter, *_iter; - int retry = 0; + struct mv_xor_desc_slot *iter; - /* start search from the last allocated descrtiptor - * if a contiguous allocation can not be found start searching - * from the beginning of the list - */ -retry: - if (retry == 0) - iter = mv_chan->last_used; - else - iter = list_entry(&mv_chan->all_slots, + spin_lock_bh(&mv_chan->lock); + + if (!list_empty(&mv_chan->free_slots)) { + iter = list_first_entry(&mv_chan->free_slots, struct mv_xor_desc_slot, - slot_node); + node); - list_for_each_entry_safe_continue( - iter, _iter, &mv_chan->all_slots, slot_node) { - prefetch(_iter); - prefetch(&_iter->async_tx); - if (iter->slot_used) { - /* give up after finding the first busy slot - * on the second pass through the list - */ - if (retry) - break; - continue; - } + list_move_tail(&iter->node, &mv_chan->allocated_slots); + spin_unlock_bh(&mv_chan->lock); + /* pre-ack descriptor */ async_tx_ack(&iter->async_tx); - - iter->slot_used = 1; - INIT_LIST_HEAD(&iter->chain_node); iter->async_tx.cookie = -EBUSY; - mv_chan->last_used = iter; - mv_desc_clear_next_desc(iter); return iter; } - if (!retry++) - goto retry; + spin_unlock_bh(&mv_chan->lock); + /* try to free some slots if the allocation fails */ tasklet_schedule(&mv_chan->irq_tasklet); @@ -500,14 +503,14 @@ cookie = dma_cookie_assign(tx); if (list_empty(&mv_chan->chain)) - list_add_tail(&sw_desc->chain_node, &mv_chan->chain); + list_move_tail(&sw_desc->node, &mv_chan->chain); else { new_hw_chain = 0; old_chain_tail = list_entry(mv_chan->chain.prev, struct mv_xor_desc_slot, - chain_node); - list_add_tail(&sw_desc->chain_node, &mv_chan->chain); + node); + list_move_tail(&sw_desc->node, &mv_chan->chain); dev_dbg(mv_chan_to_devp(mv_chan), "Append to last desc %x\n", old_chain_tail->async_tx.phys); @@ -558,8 +561,7 @@ dma_async_tx_descriptor_init(&slot->async_tx, chan); slot->async_tx.tx_submit = mv_xor_tx_submit; - INIT_LIST_HEAD(&slot->chain_node); - INIT_LIST_HEAD(&slot->slot_node); + INIT_LIST_HEAD(&slot->node); hw_desc = (char *) mv_chan->dma_desc_pool; slot->async_tx.phys = (dma_addr_t) &hw_desc[idx * MV_XOR_SLOT_SIZE]; @@ -567,18 +569,13 @@ spin_lock_bh(&mv_chan->lock); mv_chan->slots_allocated = idx; - list_add_tail(&slot->slot_node, &mv_chan->all_slots); + list_add_tail(&slot->node, &mv_chan->free_slots); spin_unlock_bh(&mv_chan->lock); } - if (mv_chan->slots_allocated && !mv_chan->last_used) - mv_chan->last_used = list_entry(mv_chan->all_slots.next, - struct mv_xor_desc_slot, - slot_node); - dev_dbg(mv_chan_to_devp(mv_chan), - "allocated %d descriptor slots last_used: %p\n", - mv_chan->slots_allocated, mv_chan->last_used); + "allocated %d descriptor slots\n", + mv_chan->slots_allocated); return mv_chan->slots_allocated ? : -ENOMEM; } @@ -593,13 +590,13 @@ "%s flags: %ld\n", __func__, flags); - spin_lock_bh(&mv_chan->lock); - sw_desc = mv_xor_alloc_slot(mv_chan); if (sw_desc) { sw_desc->type = DMA_XOR; sw_desc->async_tx.flags = flags; mv_desc_init(sw_desc, DMA_PREP_INTERRUPT); + if (mv_chan->op_in_desc == XOR_MODE_IN_DESC) + mv_desc_set_mode(sw_desc); /* the byte count field is the same as in memcpy desc*/ mv_desc_set_byte_count(sw_desc, MV_XOR_MIN_BYTE_COUNT); mv_desc_set_dest_addr(sw_desc, dummy1_addr); @@ -607,7 +604,7 @@ sw_desc->unmap_len = 0; mv_desc_set_src_addr(sw_desc, 1, dummy2_addr); } - spin_unlock_bh(&mv_chan->lock); + dev_dbg(mv_chan_to_devp(mv_chan), "%s sw_desc %p async_tx %p\n", __func__, sw_desc, &sw_desc->async_tx); @@ -629,13 +626,16 @@ BUG_ON(len > MV_XOR_MAX_BYTE_COUNT); - spin_lock_bh(&mv_chan->lock); - sw_desc = mv_xor_alloc_slot(mv_chan); if (sw_desc) { + if (mv_chan->op_in_desc == XOR_MODE_IN_DESC) + sw_desc->type = DMA_MEMCPY; + else sw_desc->type = DMA_XOR; sw_desc->async_tx.flags = flags; mv_desc_init(sw_desc, flags); + if (mv_chan->op_in_desc == XOR_MODE_IN_DESC) + mv_desc_set_mode(sw_desc); mv_desc_set_byte_count(sw_desc, len); mv_desc_set_dest_addr(sw_desc, dest); mv_desc_set_src_addr(sw_desc, 0, src); @@ -642,7 +642,6 @@ sw_desc->unmap_src_cnt = 1; sw_desc->unmap_len = len; } - spin_unlock_bh(&mv_chan->lock); dev_dbg(mv_chan_to_devp(mv_chan), "%s sw_desc %p async_tx %p\n", @@ -667,13 +666,13 @@ "%s src_cnt: %d len: dest %x %u flags: %ld\n", __func__, src_cnt, len, dest, flags); - spin_lock_bh(&mv_chan->lock); - sw_desc = mv_xor_alloc_slot(mv_chan); if (sw_desc) { sw_desc->type = DMA_XOR; sw_desc->async_tx.flags = flags; mv_desc_init(sw_desc, flags); + if (mv_chan->op_in_desc == XOR_MODE_IN_DESC) + mv_desc_set_mode(sw_desc); /* the byte count field is the same as in memcpy desc*/ mv_desc_set_byte_count(sw_desc, len); mv_desc_set_dest_addr(sw_desc, dest); @@ -682,7 +681,7 @@ while (src_cnt--) mv_desc_set_src_addr(sw_desc, src_cnt, src[src_cnt]); } - spin_unlock_bh(&mv_chan->lock); + dev_dbg(mv_chan_to_devp(mv_chan), "%s sw_desc %p async_tx %p\n", __func__, sw_desc, &sw_desc->async_tx); @@ -707,13 +706,13 @@ unlikely(len > XOR_MAX_BYTE_COUNT)) return NULL; - spin_lock_bh(&mv_chan->lock); - sw_desc = mv_xor_alloc_slot(mv_chan); if (sw_desc) { sw_desc->type = DMA_CRC32C; sw_desc->async_tx.flags = flags; mv_desc_init(sw_desc, flags); + if (mv_chan->op_in_desc == XOR_MODE_IN_DESC) + mv_desc_set_mode(sw_desc); mv_desc_set_byte_count(sw_desc, len); mv_desc_set_src_addr(sw_desc, 0, src); sw_desc->unmap_src_cnt = 1; @@ -720,7 +719,6 @@ sw_desc->unmap_len = len; sw_desc->crc32_result = seed; } - spin_unlock_bh(&mv_chan->lock); dev_dbg(mv_chan_to_devp(mv_chan), "%s sw_desc %p async_tx %p\n", __func__, sw_desc, &sw_desc->async_tx); @@ -738,22 +736,26 @@ spin_lock_bh(&mv_chan->lock); list_for_each_entry_safe(iter, _iter, &mv_chan->chain, - chain_node) { + node) { in_use_descs++; - list_del(&iter->chain_node); + list_move_tail(&iter->node, &mv_chan->free_slots); } list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots, - completed_node) { + node) { in_use_descs++; - list_del(&iter->completed_node); + list_move_tail(&iter->node, &mv_chan->free_slots); } + list_for_each_entry_safe(iter, _iter, &mv_chan->allocated_slots, + node) { + in_use_descs++; + list_move_tail(&iter->node, &mv_chan->free_slots); + } list_for_each_entry_safe_reverse( - iter, _iter, &mv_chan->all_slots, slot_node) { - list_del(&iter->slot_node); + iter, _iter, &mv_chan->free_slots, node) { + list_del(&iter->node); kfree(iter); mv_chan->slots_allocated--; } - mv_chan->last_used = NULL; dev_dbg(mv_chan_to_devp(mv_chan), "%s slots_allocated %d\n", __func__, mv_chan->slots_allocated); @@ -1124,7 +1126,7 @@ static struct mv_xor_chan * mv_xor_channel_add(struct mv_xor_device *xordev, struct platform_device *pdev, - int idx, dma_cap_mask_t cap_mask, int irq) + int idx, dma_cap_mask_t cap_mask, int irq, int op_in_desc) { int ret = 0; struct mv_xor_chan *mv_chan; @@ -1138,6 +1140,7 @@ mv_chan->idx = idx; mv_chan->irq = irq; + mv_chan->op_in_desc = op_in_desc; dma_dev = &mv_chan->dmadev; @@ -1194,6 +1197,9 @@ mv_chan_unmask_interrupts(mv_chan); + if (mv_chan->op_in_desc == XOR_MODE_IN_DESC) + mv_set_mode_on_desc(mv_chan); + else { if (dma_has_cap(DMA_CRC32C, dma_dev->cap_mask)) { /* channel can support CRC or XOR mode only, not both */ if (dma_has_cap(DMA_XOR, dma_dev->cap_mask) || @@ -1206,11 +1212,13 @@ mv_set_mode(mv_chan, DMA_CRC32C); } else mv_set_mode(mv_chan, DMA_XOR); + } spin_lock_init(&mv_chan->lock); INIT_LIST_HEAD(&mv_chan->chain); INIT_LIST_HEAD(&mv_chan->completed_slots); - INIT_LIST_HEAD(&mv_chan->all_slots); + INIT_LIST_HEAD(&mv_chan->free_slots); + INIT_LIST_HEAD(&mv_chan->allocated_slots); mv_chan->dmachan.device = dma_dev; dma_cookie_init(&mv_chan->dmachan); @@ -1237,7 +1245,8 @@ goto err_free_irq; } - dev_info(&pdev->dev, "Marvell XOR: ( %s%s%s%s)\n", + dev_info(&pdev->dev, "Marvell XOR (%s): ( %s%s%s%s)\n", + mv_chan->op_in_desc ? "Descriptor Mode" : "Registers Mode", dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "", @@ -1287,6 +1296,15 @@ writel(0, base + WINDOW_OVERRIDE_CTRL(1)); } +#ifdef CONFIG_OF +static struct of_device_id mv_xor_dt_ids[] = { + { .compatible = "marvell,orion-xor", .data = (void *)XOR_MODE_IN_REG }, + { .compatible = "marvell,a38x-xor", .data = (void *)XOR_MODE_IN_DESC }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_xor_dt_ids); +#endif + static int mv_xor_probe(struct platform_device *pdev) { const struct mbus_dram_target_info *dram; @@ -1294,6 +1312,7 @@ struct mv_xor_platform_data *pdata = pdev->dev.platform_data; struct resource *res; int i, ret; + int op_in_desc; dev_notice(&pdev->dev, "Marvell shared XOR driver\n"); @@ -1343,10 +1362,13 @@ if (pdev->dev.of_node) { struct device_node *np; int i = 0; + const struct of_device_id *of_id = + of_match_device(of_match_ptr(mv_xor_dt_ids), &pdev->dev); for_each_child_of_node(pdev->dev.of_node, np) { dma_cap_mask_t cap_mask; int irq; + op_in_desc = (int)of_id->data; dma_cap_zero(cap_mask); if (of_property_read_bool(np, "dmacap,memcpy")) @@ -1366,7 +1388,7 @@ xordev->channels[i] = mv_xor_channel_add(xordev, pdev, i, - cap_mask, irq); + cap_mask, irq, op_in_desc); if (IS_ERR(xordev->channels[i])) { ret = PTR_ERR(xordev->channels[i]); xordev->channels[i] = NULL; @@ -1395,7 +1417,7 @@ xordev->channels[i] = mv_xor_channel_add(xordev, pdev, i, - cd->cap_mask, irq); + cd->cap_mask, irq, XOR_MODE_IN_REG); if (IS_ERR(xordev->channels[i])) { ret = PTR_ERR(xordev->channels[i]); goto err_channel_add; @@ -1449,18 +1471,53 @@ } } -#ifdef CONFIG_OF -static struct of_device_id mv_xor_dt_ids[] = { - { .compatible = "marvell,orion-xor", }, - {}, -}; -MODULE_DEVICE_TABLE(of, mv_xor_dt_ids); -#endif +static int mv_xor_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mv_xor_device *xordev = platform_get_drvdata(dev); + int i; + for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) { + if (xordev->channels[i]) { + struct mv_xor_chan *mv_chan = xordev->channels[i]; + + mv_chan->suspend_regs.config = readl_relaxed(XOR_CONFIG(mv_chan)); + mv_chan->suspend_regs.int_mask = readl_relaxed(XOR_INTR_MASK(mv_chan)); + } + } + return 0; +} + +static int mv_xor_resume(struct platform_device *dev) +{ + struct mv_xor_device *xordev = platform_get_drvdata(dev); + int i; + const struct mbus_dram_target_info *dram; + + /* + * (Re-)program MBUS remapping windows on resume. + */ + dram = mv_mbus_dram_info(); + if (dram) + mv_xor_conf_mbus_windows(xordev, dram); + + for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) { + if (xordev->channels[i]) { + struct mv_xor_chan *mv_chan = xordev->channels[i]; + + writel_relaxed(mv_chan->suspend_regs.config, XOR_CONFIG(mv_chan)); + writel_relaxed(mv_chan->suspend_regs.int_mask, XOR_INTR_MASK(mv_chan)); + } + } + + return 0; +} + static struct platform_driver mv_xor_driver = { .probe = mv_xor_probe, .remove = mv_xor_remove, .shutdown = mv_xor_shutdown, + .suspend = mv_xor_suspend, + .resume = mv_xor_resume, .driver = { .owner = THIS_MODULE, .name = MV_XOR_NAME, Index: drivers/dma/mv_xor.h =================================================================== --- drivers/dma/mv_xor.h (revision 1) +++ drivers/dma/mv_xor.h (working copy) @@ -34,8 +34,14 @@ #define XOR_OPERATION_MODE_XOR 0 #define XOR_OPERATION_MODE_CRC32C 1 #define XOR_OPERATION_MODE_MEMCPY 2 +#define XOR_OPERATION_MODE_IN_DESC 7 #define XOR_DESCRIPTOR_SWAP BIT(14) +#define XOR_DESC_SUCCESS 0x40000000 +#define XOR_DESC_OPERATION_XOR (0 << 24) +#define XOR_DESC_OPERATION_CRC32C (1 << 24) +#define XOR_DESC_OPERATION_MEMCPY (2 << 24) + #define XOR_CURR_DESC(chan) (chan->mmr_base + 0x210 + (chan->idx * 4)) #define XOR_NEXT_DESC(chan) (chan->mmr_base + 0x200 + (chan->idx * 4)) #define XOR_BYTE_COUNT(chan) (chan->mmr_base + 0x220 + (chan->idx * 4)) @@ -65,6 +71,13 @@ struct mv_xor_chan *channels[MV_XOR_MAX_CHANNELS]; }; + +/* Stores certain registers during suspend to RAM */ +struct mv_xor_suspend_regs { + int config; + int int_mask; +}; + /** * struct mv_xor_chan - internal representation of a XOR channel * @pending: allows batching of hardware operations @@ -72,13 +85,14 @@ * @mmr_base: memory mapped register base * @idx: the index of the xor channel * @chain: device chain view of the descriptors + * @free_slots: free slots usable by the channel + * @allocated_slots: slots allocated by the driver * @completed_slots: slots completed by HW but still need to be acked * @device: parent device * @common: common dmaengine channel object members - * @last_used: place holder for allocation to continue from where it left off - * @all_slots: complete domain of slots usable by the channel * @slots_allocated: records the actual size of the descriptor slot pool * @irq_tasklet: bottom half where mv_xor_slot_cleanup runs + * @op_in_desc: new mode of driver, each op is writen to descriptor. */ struct mv_xor_chan { int pending; @@ -87,7 +101,10 @@ unsigned int idx; int irq; enum dma_transaction_type current_type; + struct mv_xor_suspend_regs suspend_regs; struct list_head chain; + struct list_head free_slots; + struct list_head allocated_slots; struct list_head completed_slots; dma_addr_t dma_desc_pool; void *dma_desc_pool_virt; @@ -94,10 +111,9 @@ size_t pool_size; struct dma_device dmadev; struct dma_chan dmachan; - struct mv_xor_desc_slot *last_used; - struct list_head all_slots; int slots_allocated; struct tasklet_struct irq_tasklet; + int op_in_desc; #ifdef USE_TIMER unsigned long cleanup_time; u32 current_on_last_cleanup; @@ -106,9 +122,7 @@ /** * struct mv_xor_desc_slot - software descriptor - * @slot_node: node on the mv_xor_chan.all_slots list - * @chain_node: node on the mv_xor_chan.chain list - * @completed_node: node on the mv_xor_chan.completed_slots list + * @node: node on the mv_xor_chan lists * @hw_desc: virtual address of the hardware descriptor chain * @phys: hardware address of the hardware descriptor chain * @slot_used: slot in use or not @@ -120,12 +134,9 @@ * @crc32_result: result crc calculation */ struct mv_xor_desc_slot { - struct list_head slot_node; - struct list_head chain_node; - struct list_head completed_node; + struct list_head node; enum dma_transaction_type type; void *hw_desc; - u16 slot_used; u16 idx; u16 unmap_src_cnt; u32 value; Index: drivers/gpio/gpio-mvebu.c =================================================================== --- drivers/gpio/gpio-mvebu.c (revision 1) +++ drivers/gpio/gpio-mvebu.c (working copy) @@ -83,6 +83,12 @@ int irqbase; struct irq_domain *domain; int soc_variant; + u32 out_reg; + u32 io_conf_reg; + u32 blink_en_reg; + u32 in_pol_reg; + u32 edge_mask_regs[4]; + u32 level_mask_regs[4]; }; /* @@ -554,6 +560,93 @@ }; MODULE_DEVICE_TABLE(of, mvebu_gpio_of_match); +static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); + int i; + + mvchip->out_reg = readl(mvebu_gpioreg_out(mvchip)); + mvchip->io_conf_reg = readl(mvebu_gpioreg_io_conf(mvchip)); + mvchip->blink_en_reg = readl(mvebu_gpioreg_blink(mvchip)); + mvchip->in_pol_reg = readl(mvebu_gpioreg_in_pol(mvchip)); + + switch (mvchip->soc_variant) { + case MVEBU_GPIO_SOC_VARIANT_ORION: + mvchip->edge_mask_regs[0] = + readl(mvchip->membase + GPIO_EDGE_MASK_OFF); + mvchip->level_mask_regs[0] = + readl(mvchip->membase + GPIO_LEVEL_MASK_OFF); + break; + case MVEBU_GPIO_SOC_VARIANT_MV78200: + for (i = 0; i < 2; i++) { + mvchip->edge_mask_regs[i] = + readl(mvchip->membase + + GPIO_EDGE_MASK_MV78200_OFF(i)); + mvchip->level_mask_regs[i] = + readl(mvchip->membase + + GPIO_LEVEL_MASK_MV78200_OFF(i)); + } + break; + case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: + for (i = 0; i < 4; i++) { + mvchip->edge_mask_regs[i] = + readl(mvchip->membase + + GPIO_EDGE_MASK_ARMADAXP_OFF(i)); + mvchip->level_mask_regs[i] = + readl(mvchip->membase + + GPIO_LEVEL_MASK_ARMADAXP_OFF(i)); + } + break; + default: + BUG(); + } + + return 0; +} + +static int mvebu_gpio_resume(struct platform_device *pdev) +{ + struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); + int i; + + writel(mvchip->out_reg, mvebu_gpioreg_out(mvchip)); + writel(mvchip->io_conf_reg, mvebu_gpioreg_io_conf(mvchip)); + writel(mvchip->blink_en_reg, mvebu_gpioreg_blink(mvchip)); + writel(mvchip->in_pol_reg, mvebu_gpioreg_in_pol(mvchip)); + + switch (mvchip->soc_variant) { + case MVEBU_GPIO_SOC_VARIANT_ORION: + writel(mvchip->edge_mask_regs[0], + mvchip->membase + GPIO_EDGE_MASK_OFF); + writel(mvchip->level_mask_regs[0], + mvchip->membase + GPIO_LEVEL_MASK_OFF); + break; + case MVEBU_GPIO_SOC_VARIANT_MV78200: + for (i = 0; i < 2; i++) { + writel(mvchip->edge_mask_regs[i], + mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(i)); + writel(mvchip->level_mask_regs[i], + mvchip->membase + + GPIO_LEVEL_MASK_MV78200_OFF(i)); + } + break; + case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: + for (i = 0; i < 4; i++) { + writel(mvchip->edge_mask_regs[i], + mvchip->membase + + GPIO_EDGE_MASK_ARMADAXP_OFF(i)); + writel(mvchip->level_mask_regs[i], + mvchip->membase + + GPIO_LEVEL_MASK_ARMADAXP_OFF(i)); + } + break; + default: + BUG(); + } + + return 0; +} + static int mvebu_gpio_probe(struct platform_device *pdev) { struct mvebu_gpio_chip *mvchip; @@ -585,6 +678,8 @@ return -ENOMEM; } + platform_set_drvdata(pdev, mvchip); + if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) { dev_err(&pdev->dev, "Missing ngpios OF property\n"); return -ENODEV; @@ -742,6 +837,8 @@ .of_match_table = mvebu_gpio_of_match, }, .probe = mvebu_gpio_probe, + .suspend = mvebu_gpio_suspend, + .resume = mvebu_gpio_resume, }; static int __init mvebu_gpio_init(void) Index: drivers/i2c/busses/i2c-mv64xxx.c =================================================================== --- drivers/i2c/busses/i2c-mv64xxx.c (revision 1) +++ drivers/i2c/busses/i2c-mv64xxx.c (working copy) @@ -656,6 +656,13 @@ if (rc < 0) ret = rc; + /* Sleep for >=5ms after sending the STOP condition which starts the + * internal write cycle. This is needed while working with some EEPROMs + * which max Twr = 5ms (Write Cycle Time). + */ + if (!(msgs->flags & I2C_M_RD)) + usleep_range(5000, 5500); + drv_data->num_msgs = 0; drv_data->msgs = NULL; @@ -719,7 +726,7 @@ { const struct of_device_id *device; struct device_node *np = dev->of_node; - u32 bus_freq, tclk; + u32 bus_freq, tclk, timeout; int rc = 0; /* CLK is mandatory when using DT to describe the i2c bus. We @@ -747,10 +754,9 @@ } drv_data->irq = irq_of_parse_and_map(np, 0); - /* Its not yet defined how timeouts will be specified in device tree. - * So hard code the value to 1 second. - */ - drv_data->adapter.timeout = HZ; + if (of_property_read_u32(np, "timeout-ms", &timeout)) + timeout = 1000; /* 1000ms by default */ + drv_data->adapter.timeout = msecs_to_jiffies(timeout); device = of_match_device(mv64xxx_i2c_of_match_table, dev); if (!device) @@ -895,9 +901,21 @@ return 0; } +static int mv64xxx_i2c_resume(struct platform_device *dev) +{ + struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(dev); + + mv64xxx_i2c_hw_init(drv_data); + + return 0; +} + static struct platform_driver mv64xxx_i2c_driver = { .probe = mv64xxx_i2c_probe, .remove = mv64xxx_i2c_remove, +#ifdef CONFIG_PM + .resume = mv64xxx_i2c_resume, +#endif .driver = { .owner = THIS_MODULE, .name = MV64XXX_I2C_CTLR_NAME, Index: drivers/irqchip/irq-armada-370-xp.c =================================================================== --- drivers/irqchip/irq-armada-370-xp.c (revision 1) +++ drivers/irqchip/irq-armada-370-xp.c (working copy) @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,8 @@ #define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) #define ARMADA_370_XP_CPU_SUBSYS_PERF_CNT (3) -#define ARMADA_370_XP_MAX_HW_IRQS (115) +#define ARMADA_370_XP_GBE0_PER_CPU_IRQ (8) +#define ARMADA_370_XP_GBE3_PER_CPU_IRQ (15) #define IPI_DOORBELL_START (0) #define IPI_DOORBELL_END (8) @@ -69,10 +71,11 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock); #endif +static void __iomem *cpus_int_base; static void __iomem *per_cpu_int_base; static void __iomem *main_int_base; -static u32 mpic_save[ARMADA_370_XP_MAX_HW_IRQS]; static struct irq_domain *armada_370_xp_mpic_domain; +static u32 doorbell_mask_reg; #ifdef CONFIG_PCI_MSI static struct irq_domain *armada_370_xp_msi_domain; static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); @@ -90,6 +93,8 @@ irq_hw_number_t hwirq = irqd_to_hwirq(d); #ifdef CONFIG_SMP + int cpu; + if (hwirq > ARMADA_370_XP_MAX_PER_CPU_IRQS) #else if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) @@ -96,6 +101,17 @@ #endif writel(hwirq, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); +#ifdef CONFIG_SMP + /* In case of Network Per CPU IRQ and SMP - Mask all CPUs */ + else if ((hwirq >= ARMADA_370_XP_GBE0_PER_CPU_IRQ) && + (hwirq <= ARMADA_370_XP_GBE3_PER_CPU_IRQ) && + (nr_cpu_ids > 1)) { + for_each_possible_cpu(cpu) { + if (cpumask_test_cpu(cpu, d->affinity)) + writel(hwirq, cpus_int_base + 0x100 * cpu + ARMADA_370_XP_INT_SET_MASK_OFFS); + } + } +#endif else writel(hwirq, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); @@ -106,6 +122,8 @@ irq_hw_number_t hwirq = irqd_to_hwirq(d); #ifdef CONFIG_SMP + int cpu; + if (hwirq > ARMADA_370_XP_MAX_PER_CPU_IRQS) #else if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) @@ -112,6 +130,17 @@ #endif writel(hwirq, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); +#ifdef CONFIG_SMP + /* In case of Network Per CPU IRQ and SMP - Set correct affinity to the IRQ */ + else if ((hwirq >= ARMADA_370_XP_GBE0_PER_CPU_IRQ) && + (hwirq <= ARMADA_370_XP_GBE3_PER_CPU_IRQ) && + (nr_cpu_ids > 1)) { + for_each_possible_cpu(cpu) { + if (cpumask_test_cpu(cpu, d->affinity)) + writel(hwirq, cpus_int_base + 0x100 * cpu + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + } + } +#endif else writel(hwirq, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); @@ -275,8 +304,10 @@ * Forbid mutlicore interrupt affinity * This is required since the MPIC HW doesn't limit * several CPUs from acknowledging the same interrupt. + * Note: Allow GBE interrupt set affinity. */ - if (count > 1) + if (count > 1 && (hwirq < ARMADA_370_XP_GBE0_PER_CPU_IRQ || + hwirq > ARMADA_370_XP_GBE3_PER_CPU_IRQ)) return -EINVAL; for_each_cpu(cpu, cpu_online_mask) @@ -433,7 +464,7 @@ struct irq_desc *desc) { struct irq_chip *chip = irq_get_chip(irq); - unsigned long irqmap, irqn, cpuid; + unsigned long irqmap, irqn, cpuid, irqsrc; unsigned int cascade_irq; #ifdef CONFIG_SMP struct irq_data *irqd; @@ -444,6 +475,15 @@ cpuid = raw_smp_processor_id(); irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE); for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) { + + irqsrc = readl_relaxed(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(irqn)); + /* + * Check if the interrupt is not masked on current CPU. + * Test IRQ (0-1) and FIQ (8-9) mask bits. + */ + if ((irqsrc & (0x101 << cpuid)) == 0) + continue; + if (irqn == 1) { armada_370_xp_mpic_handle_cascade_msi(); } else { @@ -532,10 +572,58 @@ } while (1); } +static int armada_370_xp_mpic_suspend(void) +{ + doorbell_mask_reg = readl(per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + return 0; +} + +static void armada_370_xp_mpic_resume(void) +{ + int nirqs; + irq_hw_number_t irq; + + /* Re-enable interrupts */ + nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff; + for (irq = 0; irq < nirqs; irq++) { + struct irq_data *data; + int virq; + + virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); + if (virq == 0) + continue; + + if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + writel(irq, per_cpu_int_base + + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + else + writel(irq, main_int_base + + ARMADA_370_XP_INT_SET_ENABLE_OFFS); + + data = irq_get_irq_data(virq); + if (!irqd_irq_disabled(data)) + armada_370_xp_irq_unmask(data); + } + + /* Reconfigure doorbells for IPIs and MSIs */ + writel(doorbell_mask_reg, + per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + if (doorbell_mask_reg & IPI_DOORBELL_MASK) + writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); +} + +struct syscore_ops armada_370_xp_mpic_syscore_ops = { + .suspend = armada_370_xp_mpic_suspend, + .resume = armada_370_xp_mpic_resume, +}; + static int __init armada_370_xp_mpic_of_init(struct device_node *node, struct device_node *parent) { - struct resource main_int_res, per_cpu_int_res; + struct resource main_int_res, per_cpu_int_res, cpus_int_res; int parent_irq; u32 control; @@ -557,6 +645,16 @@ resource_size(&per_cpu_int_res)); BUG_ON(!per_cpu_int_base); + if (nr_cpu_ids > 1) { + BUG_ON(of_address_to_resource(node, 2, &cpus_int_res)); + BUG_ON(!request_mem_region(cpus_int_res.start, + resource_size(&cpus_int_res), + node->full_name)); + cpus_int_base = ioremap(cpus_int_res.start, + resource_size(&cpus_int_res)); + BUG_ON(!cpus_int_base); + } + control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); armada_370_xp_mpic_domain = @@ -589,25 +687,9 @@ armada_370_xp_mpic_handle_cascade_irq); } + register_syscore_ops(&armada_370_xp_mpic_syscore_ops); + return 0; } IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init); - -#ifdef CONFIG_PM -void armada_370_xp_mpic_suspend(void) -{ - int hwirq; - - for (hwirq = 0; hwirq < ARMADA_370_XP_MAX_HW_IRQS; hwirq++) - mpic_save[hwirq] = readl_relaxed(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); -} - -void armada_370_xp_mpic_resume(void) -{ - int hwirq; - - for (hwirq = 0; hwirq < ARMADA_370_XP_MAX_HW_IRQS; hwirq++) - writel_relaxed(mpic_save[hwirq], main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); -} -#endif Index: drivers/memory/mvebu-devbus.c =================================================================== --- drivers/memory/mvebu-devbus.c (revision 1) +++ drivers/memory/mvebu-devbus.c (working copy) @@ -116,9 +116,20 @@ node->full_name); return err; } - /* Convert bit width to byte width */ - r.bus_width /= 8; + /* + * The bus width is encoded into the register as 0 for 8 bits, + * and 1 for 16 bits, so we do the necessary conversion here. + */ + if (r.bus_width == 8) + r.bus_width = 0; + else if (r.bus_width == 16) + r.bus_width = 1; + else { + dev_err(devbus->dev, "invalid bus width %d\n", r.bus_width); + return -EINVAL; + } + err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps", &r.badr_skew); if (err < 0) Index: drivers/mmc/core/mmc.c =================================================================== --- drivers/mmc/core/mmc.c (revision 1) +++ drivers/mmc/core/mmc.c (working copy) @@ -293,7 +293,7 @@ } card->ext_csd.rev = ext_csd[EXT_CSD_REV]; - if (card->ext_csd.rev > 6) { + if (card->ext_csd.rev > 7) { pr_err("%s: unrecognised EXT_CSD revision %d\n", mmc_hostname(card->host), card->ext_csd.rev); err = -EINVAL; @@ -908,6 +908,12 @@ } /* + * Call the optional init_card function to handle quirks. + */ + if (host->ops->init_card) + host->ops->init_card(host, card); + + /* * For native busses: set card RCA and quit open drain mode. */ if (!mmc_host_is_spi(host)) { Index: drivers/mmc/core/sd.c =================================================================== --- drivers/mmc/core/sd.c (revision 1) +++ drivers/mmc/core/sd.c (working copy) @@ -932,6 +932,12 @@ } /* + * Call the optional init_card function to handle quirks. + */ + if (host->ops->init_card) + host->ops->init_card(host, card); + + /* * For native busses: get card RCA and quit open drain mode. */ if (!mmc_host_is_spi(host)) { Index: drivers/mmc/host/sdhci-pxav3.c =================================================================== --- drivers/mmc/host/sdhci-pxav3.c (revision 1) +++ drivers/mmc/host/sdhci-pxav3.c (working copy) @@ -46,13 +46,19 @@ #define SDCLK_DELAY_SHIFT 9 #define SDCLK_DELAY_MASK 0x1f -#define SD_CFG_FIFO_PARAM 0x100 +#define SD_EXTRA_PARAM_REG 0x100 #define SDCFG_GEN_PAD_CLK_ON (1<<6) #define SDCFG_GEN_PAD_CLK_CNT_MASK 0xFF #define SDCFG_GEN_PAD_CLK_CNT_SHIFT 24 +#define SD_FIFO_PARAM_REG 0x104 +#define SD_USE_DAT3 BIT(7) +#define SD_OVRRD_CLK_OEN BIT(11) +#define SD_FORCE_CLK_ON BIT(12) + #define SD_SPI_MODE 0x108 #define SD_CE_ATA_1 0x10C +#define SDCE_MMC_CARD BIT(28) #define SD_CE_ATA_2 0x10E #define SDCE_MISC_INT (1<<2) @@ -66,30 +72,21 @@ #define SDHCI_WINDOW_BASE(i) (0x84 + ((i) << 3)) #define SDHCI_MAX_WIN_NUM 8 -static int mv_conf_mbus_windows(struct platform_device *pdev, +/* Fields below belong to SDIO3 Configuration Register (third register region) + */ +#define SDIO3_CONF_CLK_INV BIT(0) +#define SDIO3_CONF_SD_FB_CLK BIT(2) + +static int mv_conf_mbus_windows(struct device *dev, void __iomem *regs, const struct mbus_dram_target_info *dram) { int i; - void __iomem *regs; - struct resource *res; if (!dram) { - dev_err(&pdev->dev, "no mbus dram info\n"); + dev_err(dev, "no mbus dram info\n"); return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(&pdev->dev, "cannot get mbus registers\n"); - return -EINVAL; - } - - regs = ioremap(res->start, resource_size(res)); - if (!regs) { - dev_err(&pdev->dev, "cannot map mbus registers\n"); - return -ENOMEM; - } - for (i = 0; i < SDHCI_MAX_WIN_NUM; i++) { writel(0, regs + SDHCI_WINDOW_CTRL(i)); writel(0, regs + SDHCI_WINDOW_BASE(i)); @@ -107,8 +104,6 @@ writel(cs->base, regs + SDHCI_WINDOW_BASE(i)); } - iounmap(regs); - return 0; } @@ -116,6 +111,8 @@ { struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; + u32 reg_val; if (mask == SDHCI_RESET_ALL) { /* @@ -131,9 +128,40 @@ tmp |= SDCLK_SEL; writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); } + + if (of_device_is_compatible(np, "marvell,armada-380-sdhci") && + host->quirks2 & SDHCI_QUIRK2_KEEP_INT_CLK_ON) { + reg_val = sdhci_readl(host, SD_FIFO_PARAM_REG); + reg_val |= SD_USE_DAT3 | SD_OVRRD_CLK_OEN | + SD_FORCE_CLK_ON; + sdhci_writel(host, reg_val, SD_FIFO_PARAM_REG); + + /* For HW detection purpose keep internal clk switched + * on after controller reset. + */ + reg_val = sdhci_readl(host, SDHCI_CLOCK_CONTROL); + reg_val |= SDHCI_CLOCK_INT_EN; + sdhci_writel(host, reg_val, SDHCI_CLOCK_CONTROL); } } +} +static void pxav3_init_card(struct sdhci_host *host, struct mmc_card *card) +{ + struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); + struct device_node *np = pdev->dev.of_node; + u32 reg_val; + + if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { + reg_val = sdhci_readl(host, SD_CE_ATA_1); + if (mmc_card_mmc(card)) + reg_val |= SDCE_MMC_CARD; + else + reg_val &= ~SDCE_MMC_CARD; + sdhci_writel(host, reg_val, SD_CE_ATA_1); + } +} + #define MAX_WAIT_COUNT 5 static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode) { @@ -158,9 +186,9 @@ writew(tmp, host->ioaddr + SD_CE_ATA_2); /* start sending the 74 clocks */ - tmp = readw(host->ioaddr + SD_CFG_FIFO_PARAM); + tmp = readw(host->ioaddr + SD_EXTRA_PARAM_REG); tmp |= SDCFG_GEN_PAD_CLK_ON; - writew(tmp, host->ioaddr + SD_CFG_FIFO_PARAM); + writew(tmp, host->ioaddr + SD_EXTRA_PARAM_REG); /* slowest speed is about 100KHz or 10usec per clock */ udelay(740); @@ -186,7 +214,12 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) { + struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); + struct device_node *np = pdev->dev.of_node; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; u16 ctrl_2; + u8 reg_val; /* * Set V18_EN -- UHS modes do not work without this. @@ -214,6 +247,22 @@ break; } + /* Update SDIO3 Configuration register according to + * erratum 'FE-2946959'. + */ + if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { + reg_val = readb(pxa->sdio3_conf_reg); + if (uhs == MMC_TIMING_UHS_SDR50 || + uhs == MMC_TIMING_UHS_DDR50) { + reg_val &= ~SDIO3_CONF_CLK_INV; + reg_val |= SDIO3_CONF_SD_FB_CLK; + } else { + reg_val |= SDIO3_CONF_CLK_INV; + reg_val &= ~SDIO3_CONF_SD_FB_CLK; + } + writeb(reg_val, pxa->sdio3_conf_reg); + } + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); dev_dbg(mmc_dev(host->mmc), "%s uhs = %d, ctrl_2 = %04X\n", @@ -227,6 +276,7 @@ .set_uhs_signaling = pxav3_set_uhs_signaling, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .init_card = pxav3_init_card, }; static struct sdhci_pltfm_data sdhci_pxav3_pdata = { @@ -233,7 +283,8 @@ .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | SDHCI_QUIRK_32BIT_ADMA_SIZE - | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN + | SDHCI_QUIRK_MISSING_CAPS, .ops = &pxav3_sdhci_ops, }; @@ -282,6 +333,7 @@ struct device_node *np = pdev->dev.of_node; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; + struct resource *res; const struct of_device_id *match; const struct sdhci_pltfm_data *sdhci_pltfm_data; @@ -306,7 +358,22 @@ } if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { - ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pxa->mbus_win_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pxa->mbus_win_regs)) { + ret = PTR_ERR(pxa->mbus_win_regs); + goto err_clk_get; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + pxa->sdio3_conf_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pxa->sdio3_conf_reg)) { + ret = PTR_ERR(pxa->sdio3_conf_reg); + goto err_clk_get; + } + + ret = mv_conf_mbus_windows(&pdev->dev, pxa->mbus_win_regs, + mv_mbus_dram_info()); if (ret < 0) goto err_clk_get; } @@ -331,7 +398,30 @@ mmc_of_parse(host->mmc); sdhci_get_of_property(pdev); pdata = pxav3_get_mmc_pdata(dev); + host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + /* Modify capabilities of Armada 38x SDHCI controller according + * to erratum ERR-7878951: + */ + if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { + if (of_get_property(np, "no-1-8-v", NULL)) { + host->caps &= ~SDHCI_CAN_VDD_180; + host->mmc->caps &= ~MMC_CAP_1_8V_DDR; + } else + host->caps &= ~SDHCI_CAN_VDD_330; + + host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | + SDHCI_USE_SDR50_TUNING); + + /* The interface clock enable is also used as control + * for the A38x SDIO IP, so it can't be powered down + * when using HW-based card detection. + */ + if (of_property_read_bool(np, "dat3-cd") && + !of_property_read_bool(np, "broken-cd")) + host->quirks2 |= SDHCI_QUIRK2_KEEP_INT_CLK_ON; + } } else if (pdata) { /* on-chip device */ if (pdata->flags & PXA_FLAG_CARD_PERMANENT) @@ -439,7 +529,14 @@ { int ret; struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; + struct device_node *np = dev->of_node; + if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) + ret = mv_conf_mbus_windows(dev, pxa->mbus_win_regs, + mv_mbus_dram_info()); + pm_runtime_get_sync(dev); ret = sdhci_resume_host(host); pm_runtime_mark_last_busy(dev); Index: drivers/mmc/host/sdhci.c =================================================================== --- drivers/mmc/host/sdhci.c (revision 1) +++ drivers/mmc/host/sdhci.c (working copy) @@ -1132,7 +1132,10 @@ return; } - sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + /* Some controllers need to keep internal clk always enabled */ + if (host->quirks2 & SDHCI_QUIRK2_KEEP_INT_CLK_ON) + clk = SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); if (clock == 0) goto out; @@ -2067,6 +2070,14 @@ spin_unlock_irqrestore(&host->lock, flags); } +static void sdhci_init_card(struct mmc_host *mmc, struct mmc_card *card) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (host->ops->init_card) + host->ops->init_card(host, card); +} + static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .set_ios = sdhci_set_ios, @@ -2078,6 +2089,7 @@ .execute_tuning = sdhci_execute_tuning, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, + .init_card = sdhci_init_card, }; /*****************************************************************************\ Index: drivers/mmc/host/sdhci.h =================================================================== --- drivers/mmc/host/sdhci.h (revision 1) +++ drivers/mmc/host/sdhci.h (working copy) @@ -294,6 +294,7 @@ void (*platform_resume)(struct sdhci_host *host); void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*platform_init)(struct sdhci_host *host); + void (*init_card)(struct sdhci_host *host, struct mmc_card *card); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS Index: drivers/mtd/nand/mvebu_nfc/hal/mvNfc.c =================================================================== --- drivers/mtd/nand/mvebu_nfc/hal/mvNfc.c (revision 1) +++ drivers/mtd/nand/mvebu_nfc/hal/mvNfc.c (working copy) @@ -134,6 +134,44 @@ #define NFC_CMD_BUFF_ADDR (NFC_COMMAND_BUFF_0_REG_4PDMA) #define NFC_DATA_BUFF_ADDR (NFC_DATA_BUFF_REG_4PDMA) + +#define TIMING_MAX_tADL 0x1f +#define TIMING_DEF_SEL_CNTR 0x1 +#define TIMING_MAX_RD_CNT_DEL 0x0 +#define TIMING_MAX_tCH 0x7 +#define TIMING_MAX_tCS 0x7 +#define TIMING_MAX_tWH 0x7 +#define TIMING_MAX_tWP 0x7 +#define TIMING_MAX_etRP 0x1 +#define TIMING_MAX_tRH 0x7 +#define TIMING_MAX_tRP 0x7 + +#define MV_NDTR0CS0_REG ((TIMING_MAX_tADL << 27) | \ + (TIMING_DEF_SEL_CNTR << 26) | \ + (TIMING_MAX_RD_CNT_DEL << 22) | \ + (TIMING_MAX_tCH << 19) | \ + (TIMING_MAX_tCS << 16) | \ + (TIMING_MAX_tWH << 11) | \ + (TIMING_MAX_tWP << 8) | \ + (TIMING_MAX_etRP << 6) | \ + (TIMING_MAX_tRH << 3) | \ + (TIMING_MAX_tRP)) + +#define TIMING_tR 0xff +#define TIMING_WAIT_MODE 0x1 /* Work with RnB signal (1) or ignore it (0) */ +#define TIMING_PRESCALE 0x0 /* no prescalling */ +#define TIMING_MAX_tRHW 0x0 +#define TIMING_MAX_tWHR 0xf +#define TIMING_MAX_tAR 0xf + +#define MV_NDTR1CS0_REG ((TIMING_tR << 16) | \ + (TIMING_WAIT_MODE << 15) | \ + (TIMING_PRESCALE << 14) | \ + (TIMING_MAX_tRHW << 8) | \ + (TIMING_MAX_tWHR << 4) | \ + (TIMING_MAX_tAR)) + + /**********/ /* Macros */ /**********/ @@ -473,6 +511,29 @@ .bb_page = 127, /* Manufacturer Bad block marking page in block */ .flags = NFC_CLOCK_UPSCALE_200M }, + { /* Micron 32Gb */ + .tADL = 200, /* tADL, Address to write data delay */ + .tCH = 20, /* tCH, Enable signal hold time */ + .tCS = 70, /* tCS, Enable signal setup time */ + .tWC = 100, /* tWC, ND_nWE cycle duration */ + .tWH = 30, /* tWH, ND_nWE high duration */ + .tWP = 50, /* tWP, ND_nWE pulse time */ + .tRC = 100, /* tWC, ND_nRE cycle duration */ + .tRH = 45, /* tRH, ND_nRE high duration */ + .tRP = 50, /* tRP, ND_nRE pulse width */ + .tR = 35000, /* tR = data transfer from cell to register */ + .tWHR = 120, /* tWHR, ND_nWE high to ND_nRE low delay for status read */ + .tAR = 25, /* tAR, ND_ALE low to ND_nRE low delay */ + .tRHW = 200, /* tRHW, ND_nRE high to ND_nWE low delay */ + .pgPrBlk = 128, /* Pages per block - detected */ + .pgSz = 8192, /* Page size */ + .oobSz = 160, /* Spare size */ + .blkNum = 4096, /* Number of blocks/sectors in the flash */ + .id = 0x682C, /* Device ID 0xDevice,Vendor */ + .model = "Micron 32Gb 8bit", + .bb_page = 0, /* Manufacturer Bad block marking page in block */ + .flags = NFC_CLOCK_UPSCALE_200M + }, { /* Micron 64Gb */ .tADL = 0, /* tADL, Address to write data delay */ .tCH = 20, /* tCH, Enable signal hold time */ @@ -543,6 +604,29 @@ .bb_page = 0, /* Manufacturer Bad block marking page in block */ .flags = (NFC_CLOCK_UPSCALE_200M | NFC_FLAGS_ONFI_MODE_3_SET) }, + { /* Micron 256MB */ + .tADL = 100, /* tADL, Address to write data delay */ + .tCH = 5, /* tCH, Enable signal hold time */ + .tCS = 25, /* tCS, Enable signal setup time */ + .tWC = 30, /* tWC, ND_nWE cycle duration */ + .tWH = 15, /* tWH, ND_nWE high duration */ + .tWP = 15, /* tWP, ND_nWE pulse time */ + .tRC = 30, /* tWC, ND_nRE cycle duration */ + .tRH = 10, /* tRH, ND_nRE high duration */ + .tRP = 15, /* tRP, ND_nRE pulse width */ + .tR = 25241, /* tR = data transfer from cell to register tR = tR+tRR+tWB+1 */ + .tWHR = 60, /* tWHR, ND_nWE high to ND_nRE low delay for status read */ + .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */ + .tRHW = 100, /* tRHW, ND_nRE high to ND_nWE low delay */ + .pgPrBlk = 64, /* Pages per block - detected */ + .pgSz = 2048, /* Page size */ + .oobSz = 224, /* Spare size */ + .blkNum = 2048, /* Number of blocks/sectors in the flash */ + .id = 0xda2C, /* Device ID 0xDevice,Vendor */ + .model = "Micron 256MB", + .bb_page = 0, /* Manufacturer Bad block marking page in block */ + .flags = (NFC_CLOCK_UPSCALE_200M | NFC_FLAGS_ONFI_MODE_3_SET) + }, { /* Micron 8Gb ABACA */ /* timing Asynchronous mode 3 */ .tADL = 100, /* tADL, Address to write data delay */ @@ -659,7 +743,7 @@ /**************/ static MV_STATUS mvDfcWait4Complete(MV_U32 statMask, MV_U32 usec); static MV_STATUS mvNfcReadIdNative(MV_NFC_CHIP_SEL cs, MV_U16 *id); -static MV_STATUS mvNfcTimingSet(MV_U32 tclk, MV_NFC_FLASH_INFO *flInfo); +static MV_STATUS mvNfcTimingSet(MV_U32 nand_clock, MV_NFC_FLASH_INFO *flInfo); static MV_U32 mvNfcColBits(MV_U32 pg_size); static MV_STATUS mvNfcDeviceFeatureSet(MV_NFC_CTRL *nfcCtrl, MV_U8 cmd, MV_U8 addr, MV_U32 data0, MV_U32 data1); static MV_STATUS mvNfcDeviceFeatureGet(MV_NFC_CTRL *nfcCtrl, MV_U8 cmd, MV_U8 addr, MV_U32 *data0, MV_U32 *data1); @@ -706,13 +790,16 @@ ECC engine clock = (2Ghz / divider) NFC clock = ECC clock / 2 */ - halData->mvCtrlNandClkSetFunction(8); /* setNANDClock(8); Go down to 125MHz */ - nand_clock = 125000000; + nand_clock = halData->mvCtrlNandClkSetFunction(_100MHz); /* Go down to 100MHz */ + if (nand_clock != _100MHz) + DB(mvOsPrintf("%s: Warning: set NFC Clock frequency to %dHz instead of %dHz\n", + __func__, nand_clock, _100MHz)); + DB(mvOsPrintf("mvNfcInit: set nand clock to %d\n", nand_clock)); /* Relax Timing configurations to avoid timing violations after flash reset */ - MV_NAND_REG_WRITE(NFC_TIMING_0_REG, 0xFC3F3F7F); - MV_NAND_REG_WRITE(NFC_TIMING_1_REG, 0x00FF83FF); + MV_NAND_REG_WRITE(NFC_TIMING_0_REG, MV_NDTR0CS0_REG); + MV_NAND_REG_WRITE(NFC_TIMING_1_REG, MV_NDTR1CS0_REG); /* make sure ECC is disabled at this point - will be enabled only when issuing certain commands */ MV_NAND_REG_BIT_RESET(NFC_CONTROL_REG, NFC_CTRL_ECC_EN_MASK); @@ -808,10 +895,12 @@ /* Critical Initialization done. Raise NFC clock if needed */ if (flashDeviceInfo[i].flags & NFC_CLOCK_UPSCALE_200M) { + nand_clock = halData->mvCtrlNandClkSetFunction(_200MHz); /* raise NFC clk to 200MHz */ + if (nand_clock != _200MHz) + DB(mvOsPrintf("%s: Warning: set NFC Clock frequency to %dHz instead of %dHz\n", + __func__, nand_clock, _200MHz)); + } - halData->mvCtrlNandClkSetFunction(5); /* setNANDClock(5); */ - nand_clock = 200000000; - } DB(mvOsPrintf("mvNfcInit: set nand clock to %d\n", nand_clock)); /* Configure the command set based on page size */ @@ -2739,8 +2828,8 @@ * Set all flash timing parameters for optimized operation * * INPUT: -* tclk: Tclk frequency, - flInfo: timing information + * nand_clock - nand clock frequency, + * flInfo - timing information * * OUTPUT: * None. @@ -2749,7 +2838,7 @@ * MV_OK - On success, * MV_FAIL - On failure *******************************************************************************/ -static MV_STATUS mvNfcTimingSet(MV_U32 tclk, MV_NFC_FLASH_INFO *flInfo) +static MV_STATUS mvNfcTimingSet(MV_U32 nand_clock, MV_NFC_FLASH_INFO *flInfo) { MV_U32 reg, i; MV_U32 clk2ns; @@ -2759,22 +2848,7 @@ MV_U32 tr_pre_nfc = 0; /* MV_U32 ret = MV_OK; */ - switch (tclk) { - case 125000000: - clk2ns = 8; - break; - case 166666667: - clk2ns = 6; - break; - case 200000000: - clk2ns = 5; - break; - case 250000000: - clk2ns = 4; - break; - default: - return MV_FAIL; - }; + clk2ns = DIV_ROUND_UP(_1GHz, nand_clock); /* Calculate legal read timing */ trc = ns_clk(flInfo->tRC, clk2ns); Index: drivers/mtd/nand/mvebu_nfc/hal/mvNfc.h =================================================================== --- drivers/mtd/nand/mvebu_nfc/hal/mvNfc.h (revision 1) +++ drivers/mtd/nand/mvebu_nfc/hal/mvNfc.h (working copy) @@ -396,7 +396,7 @@ } MV_NFC_CMD; struct MV_NFC_HAL_DATA { - void (*mvCtrlNandClkSetFunction) (int); /* Controller NAND clock div */ + int (*mvCtrlNandClkSetFunction) (int); /* Controller NAND clock div */ }; /** Micron MT29F NAND driver (ONFI): Parameter Page Data */ struct parameter_page_t { Index: drivers/mtd/nand/mvebu_nfc/nand_nfc.c =================================================================== --- drivers/mtd/nand/mvebu_nfc/nand_nfc.c (revision 1) +++ drivers/mtd/nand/mvebu_nfc/nand_nfc.c (working copy) @@ -1495,15 +1495,17 @@ nand->chip_delay = 25; } -static void mvCtrlNandClkSet(int nClock) +static int mvCtrlNandClkSet(int nfc_clk_freq) { - unsigned long rate; + /* NAND clock is derived from ecc_clk according to equation + * nfc_clk_freq = ecc_clk / 2 + */ + clk_set_rate(ecc_clk, nfc_clk_freq * 2); - /* Calculate target ECC clock rate basing - * on 2GHz frequency and divider used in HAL */ - rate = ARMADA_MAIN_PLL_FREQ / nClock; + /* Return calculated nand clock frequency */ + nfc_clk_freq = clk_get_rate(ecc_clk) / 2; - clk_set_rate(ecc_clk, rate); + return nfc_clk_freq; } static MV_STATUS mvSysNfcInit(MV_NFC_INFO *nfcInfo, MV_NFC_CTRL *nfcCtrl) Index: drivers/net/ethernet/Kconfig =================================================================== --- drivers/net/ethernet/Kconfig (revision 1) +++ drivers/net/ethernet/Kconfig (working copy) @@ -87,6 +87,10 @@ source "drivers/net/ethernet/marvell/Kconfig" source "drivers/net/ethernet/mvebu_net/Kconfig" +source "drivers/net/ethernet/mvebu_net/neta/Kconfig" +source "drivers/net/ethernet/mvebu_net/pp2/Kconfig" +source "drivers/net/ethernet/mvebu_net/prestera/Kconfig" + source "drivers/net/ethernet/mellanox/Kconfig" source "drivers/net/ethernet/micrel/Kconfig" source "drivers/net/ethernet/microchip/Kconfig" Index: drivers/net/ethernet/mvebu_net/common/mv802_3.h =================================================================== --- drivers/net/ethernet/mvebu_net/common/mv802_3.h (revision 1) +++ drivers/net/ethernet/mvebu_net/common/mv802_3.h (working copy) @@ -98,6 +98,8 @@ MV_TAG tx_tag; MV_TAG rx_tag_ptrn; MV_TAG rx_tag_mask; + MV_BOOL leave_tag; + MV_U16 proto_type; } MV_MUX_TAG; typedef enum { Index: drivers/net/ethernet/mvebu_net/common/mvCommon.h =================================================================== --- drivers/net/ethernet/mvebu_net/common/mvCommon.h (revision 1) +++ drivers/net/ethernet/mvebu_net/common/mvCommon.h (working copy) @@ -248,6 +248,9 @@ #define _250MHz 250000000 #define _266MHz 266666667 #define _300MHz 300000000 +#define _800MHz 800000000 +#define _1GHz 1000000000UL +#define _2GHz 2000000000UL /* Supported clocks */ #define MV_BOARD_TCLK_100MHZ 100000000 Index: drivers/net/ethernet/mvebu_net/common/mvDeviceId.h =================================================================== --- drivers/net/ethernet/mvebu_net/common/mvDeviceId.h (revision 1) +++ drivers/net/ethernet/mvebu_net/common/mvDeviceId.h (working copy) @@ -427,6 +427,17 @@ /* BobCat2 Family */ #define MV_BOBCAT2_DEV_ID 0xFC00 +/* BobCat2 Revisions */ +#define MV_BOBCAT2_A0_ID 0x0 +#define MV_BOBCAT2_A0_NAME "A0" +#define MV_BOBCAT2_B0_ID 0x1 +#define MV_BOBCAT2_B0_NAME "B0" + +#define MV_BOBCAT2_ID_ARRAY { \ + MV_BOBCAT2_A0_NAME,\ + MV_BOBCAT2_B0_NAME,\ +} + /* Lion2 Family */ #define MV_LION2_DEV_ID 0x8000 @@ -433,6 +444,20 @@ /* AlleyCat3 Family */ #define MV_ALLEYCAT3_DEV_ID 0xF400 +/* AlleyCat3 Revisions */ +#define MV_ALLEYCAT3_A0_ID 0x3 +#define MV_ALLEYCAT3_A0_NAME "A0" +#define MV_ALLEYCAT3_A1_ID 0x4 +#define MV_ALLEYCAT3_A1_NAME "A1" + +#define MV_ALLEYCAT3_ID_ARRAY { \ + NULL,\ + NULL,\ + NULL,\ + MV_ALLEYCAT3_A0_NAME,\ + MV_ALLEYCAT3_A1_NAME,\ +} + /* IDT Swicth */ #define PCI_VENDOR_ID_IDT_SWITCH 0x111D #define MV_IDT_SWITCH_DEV_ID_808E 0x808E Index: drivers/net/ethernet/mvebu_net/Kconfig =================================================================== --- drivers/net/ethernet/mvebu_net/Kconfig (revision 1) +++ drivers/net/ethernet/mvebu_net/Kconfig (working copy) @@ -31,12 +31,15 @@ the allocator to make a fastpath for very skb consuming network applications. -config NET_SKB_RECYCLE_DEF - depends on NET_SKB_RECYCLE - int "Default value for SKB recycle: 0 - disable, 1 - enable" - default 1 +endmenu + +menu "Marvell DSOHO SDT Features" +config MV_INCLUDE_SWITCH + bool "Marvell Armada 38X network switch support" + default n ---help--- - + This driver supports the network switch units in the + Marvell ARMADA 38x SoC family. endmenu menu "Marvell Network PON Support" @@ -58,29 +61,6 @@ Enable run-time enable/disable enter debug code blocks endmenu - -config MV_ETH_NETA - tristate "Marvell Armada 380 network interface support" - depends on MACH_ARMADA_380 - ---help--- - This driver supports the network interface units in the - Marvell ARMADA 38x SoC family. - -if MV_ETH_NETA -source drivers/net/ethernet/mvebu_net/neta/Kconfig -endif - -config MV_ETH_PP2 - tristate "Marvell Armada 375 network interface support" - depends on MACH_ARMADA_375 - ---help--- - This driver supports the network interface units in the - Marvell ARMADA 375 SoC family. - -if MV_ETH_PP2 -source drivers/net/ethernet/mvebu_net/pp2/Kconfig -endif - config MV_ETH_INCLUDE_PHY bool "Choose to compile Ethernet PHY support" depends on MV_ETH_NETA || MV_ETH_PP2 @@ -100,4 +80,11 @@ Use Marvell propriotary NETMUX driver for Virtual Networking interfaces support. The driver located uner directory mvebu_net/netmux +config MV_ETH_INCLUDE_NET_COMPLEX + bool "Choose to compile Marvell Net Complex support" + depends on MV_PP3 + default y + ---help--- + Use Marvell propriotary netcomplex driver for A390. + The driver located uner directory mvebu_net/a390_nc. endif # NET_VENDOR_MVEBU Index: drivers/net/ethernet/mvebu_net/linux/mv_neta.h =================================================================== --- drivers/net/ethernet/mvebu_net/linux/mv_neta.h (revision 1) +++ drivers/net/ethernet/mvebu_net/linux/mv_neta.h (working copy) @@ -116,6 +116,10 @@ */ int rx_queue_size; int tx_queue_size; + /* PNC TCAM size*/ +#ifdef CONFIG_MV_ETH_PNC + unsigned int pnc_tcam_size; +#endif }; Index: drivers/net/ethernet/mvebu_net/linux/mv_pp3.h =================================================================== --- drivers/net/ethernet/mvebu_net/linux/mv_pp3.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/linux/mv_pp3.h (working copy) @@ -0,0 +1,120 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/* mv_pp3.h */ + +#ifndef LINUX_MV_PP3_H +#define LINUX_MV_PP3_H + +#define MV_PP3_PORT_NAME "mv_pp3_port" +#define MV_PP3_SHARED_NAME "mv_pp3_shared" + +struct mv_pp3_mac_data { + /* Whether a PHY is present, and if yes, at which address. */ + int phy_addr; + int port_mode; +}; + +/* PP3 system shared data */ +struct mv_pp3_plat_data { + + /* Global parameters common for all ports */ + unsigned int tclk; + int max_port; + + /* Controller Model (Device ID) and Revision */ + unsigned int ctrl_model; + unsigned int ctrl_rev; + unsigned int nss_mac_mask; + struct mv_pp3_mac_data macs_data[4]; +}; + +/* PP3 per port data */ +struct mv_pp3_port_data { + + unsigned int cpu_mask; + int mtu; + + /* Use this MAC address if it is valid */ + u8 mac_addr[6]; + + /* + * How many RX/TX queues to use per cpu. + */ + int num_rxqs_per_cpu; + int num_txqs_per_cpu; + + /* + * Override default RX/TX queue sizes if nonzero. + */ + int rx_queue_size; + int tx_queue_size; + + unsigned int flags; +}; + +/* mv_pp3_port_data flags */ +#define MV_PP3_PORT_DATA_F_NSS_BIT 0 +#define MV_PP3_PORT_DATA_F_NSS (1 << MV_PP3_PORT_DATA_F_NSS_BIT) + +#endif /* LINUX_MV_PP3_H */ Index: drivers/net/ethernet/mvebu_net/linux/mv_switch.h =================================================================== --- drivers/net/ethernet/mvebu_net/linux/mv_switch.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/linux/mv_switch.h (working copy) @@ -0,0 +1,58 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or on the worldwide web +at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +*******************************************************************************/ +/* mv_switch.h */ + +#ifndef LINUX_MV_SWITCH_H +#define LINUX_MV_SWITCH_H + +#define MV_SWITCH_SOHO_NAME "mv_soho_switch" + +struct mv_switch_pdata { + int index; + int phy_addr; + int gbe_port; + int switch_cpu_port; + unsigned int tag_mode; + unsigned int preset; + int vid; + unsigned int port_mask; + unsigned int connected_port_mask; + unsigned int forced_link_port_mask; + unsigned int mtu; + unsigned int smi_scan_mode; + int qsgmii_module; + int gephy_on_port; + int rgmiia_on_port; + int switch_irq; + int is_speed_2000; + int rgmii_rx_timing_delay; + int rgmii_tx_timing_delay; +}; + + + +#endif /* LINUX_MV_SWITCH_H */ Index: drivers/net/ethernet/mvebu_net/Makefile =================================================================== --- drivers/net/ethernet/mvebu_net/Makefile (revision 1) +++ drivers/net/ethernet/mvebu_net/Makefile (working copy) @@ -16,24 +16,76 @@ PLAT_DIR := drivers/net/ethernet/mvebu_net export PLAT_DIR +ifeq ($(CONFIG_MV_INCLUDE_SWITCH),y) +QD_DIR:= switch/qd-dsdt-3.3 +export QD-DIR +endif + INCLUDE_DIRS += -I$(PLAT_DIR) INCLUDE_DIRS += -I$(PLAT_DIR)/common INCLUDE_DIRS += -I$(srctree)/arch/arm/mach-mvebu/linux_oss INCLUDE_DIRS += -I$(PLAT_DIR)/switch +ifeq ($(CONFIG_MV_INCLUDE_SWITCH),y) +INCLUDE_DIRS += -I$(PLAT_DIR)/$(QD_DIR)/Include +INCLUDE_DIRS += -I$(PLAT_DIR)/$(QD_DIR)/Include/h/msApi +INCLUDE_DIRS += -I$(PLAT_DIR)/$(QD_DIR)/Include/h/driver +INCLUDE_DIRS += -I$(PLAT_DIR)/$(QD_DIR)/Include/h/platform +endif INCLUDE_DIRS += -I$(PLAT_DIR)/netmux INCLUDE_DIRS += -I$(PLAT_DIR)/phy +INCLUDE_DIRS += -I$(PLAT_DIR)/pp3 +INCLUDE_DIRS += -I$(srctree)/arch/arm/mach-mvebu + export INCLUDE_DIRS ccflags-y += $(INCLUDE_DIRS) -obj-y += common/mvCommon.o common/mvStack.o common/mvDebug.o +ifneq ($(CONFIG_MV_ETH_PP2)$(CONFIG_MV_ETH_NETA),) +obj-y += common/mvCommon.o common/mvStack.o common/mvDebug.o common/mvList.o +endif -obj-$(CONFIG_MV_ETH_PP2_1) += common/mvList.o - obj-$(CONFIG_MV_ETH_INCLUDE_PHY) += phy/mvEthPhy.o phy/phy_sysfs.o -obj-$(CONFIG_MV_ETH_INCLUDE_NETMUX) += netmux/ +obj-$(CONFIG_MV_ETH_INCLUDE_NET_COMPLEX) += net_complex/mv_net_complex_a39x.o -obj-$(CONFIG_MV_ETH_NETA) += neta/ -obj-$(CONFIG_MV_ETH_PP2) += pp2/ +ifeq ($(CONFIG_MV_ETH_INCLUDE_NETMUX),y) +obj-y += netmux/ +endif +ifeq ($(CONFIG_MV_ETH_NETA),y) +obj-y += neta/ +endif +ifeq ($(CONFIG_MV_ETH_PP2),y) +obj-y += pp2/ +endif +ifeq ($(CONFIG_MV_PP3),y) +obj-y += pp3/ +endif +ifeq ($(CONFIG_MV_INCLUDE_PRESTERA),y) +obj-y += prestera/ +endif +ifeq ($(CONFIG_MV_INCLUDE_PRESTERA_PCI),y) +obj-y += prestera/pci/ +endif +ifeq ($(CONFIG_MV_INCLUDE_SWITCH),y) +DSDT_OBJS = $(QD_DIR)/src/driver/gtDrvConfig.o $(QD_DIR)/src/driver/gtDrvEvents.o \ + $(QD_DIR)/src/driver/gtHwCntl.o $(QD_DIR)/src/platform/gtMiiSmiIf.o \ + $(QD_DIR)/src/platform/platformDeps.o $(QD_DIR)/src/platform/gtSem.o \ + $(QD_DIR)/src/platform/gtDebug.o $(QD_DIR)/src/msapi/gtBrgFdb.o \ + $(QD_DIR)/src/msapi/gtBrgStp.o $(QD_DIR)/src/msapi/gtBrgVlan.o \ + $(QD_DIR)/src/msapi/gtEvents.o $(QD_DIR)/src/msapi/gtPortCtrl.o \ + $(QD_DIR)/src/msapi/gtPortStat.o $(QD_DIR)/src/msapi/gtPortStatus.o \ + $(QD_DIR)/src/msapi/gtQosMap.o $(QD_DIR)/src/msapi/gtPIRL.o \ + $(QD_DIR)/src/msapi/gtPhyCtrl.o $(QD_DIR)/src/msapi/gtPhyInt.o \ + $(QD_DIR)/src/msapi/gtSysConfig.o $(QD_DIR)/src/msapi/gtSysCtrl.o \ + $(QD_DIR)/src/msapi/gtVersion.o $(QD_DIR)/src/msapi/gtUtils.o \ + $(QD_DIR)/src/msapi/gtBrgVtu.o $(QD_DIR)/src/msapi/gtPortRmon.o \ + $(QD_DIR)/src/msapi/gtSysStatus.o $(QD_DIR)/src/msapi/gtPortRateCtrl.o\ + $(QD_DIR)/src/msapi/gtPortPav.o $(QD_DIR)/src/msapi/gtVct.o \ + $(QD_DIR)/src/msapi/gtPIRL2.o $(QD_DIR)/src/msapi/gtCCPVT.o \ + $(QD_DIR)/src/msapi/gtPCSCtrl.o $(QD_DIR)/src/msapi/gtWeight.o \ + $(QD_DIR)/src/msapi/gtBrgStu.o + +obj-$(CONFIG_MV_INCLUDE_SWITCH) += $(DSDT_OBJS) +obj-$(CONFIG_MV_INCLUDE_SWITCH) += switch/mv_switch.o switch/mv_switch_sysfs.o switch/mv_phy.o +endif Index: drivers/net/ethernet/mvebu_net/mvNetConfig.h =================================================================== --- drivers/net/ethernet/mvebu_net/mvNetConfig.h (revision 1) +++ drivers/net/ethernet/mvebu_net/mvNetConfig.h (working copy) @@ -67,6 +67,11 @@ #define MV_ETH_MAX_PORTS 4 +#if defined(CONFIG_MV_PP3) +#define INTER_REGS_PHYS_BASE 0xF1000000 +#define INTER_REGS_SIZE 0x1000000 +#endif + #if defined(CONFIG_MV_ETH_PP2) || defined(CONFIG_MV_ETH_PP2_MODULE) #define INTER_REGS_PHYS_BASE 0xF1000000 @@ -117,6 +122,10 @@ #define MV_ETH_MAX_TXQ 8 #define MV_ETH_TX_CSUM_MAX_SIZE 9800 #define MV_PNC_TCAM_LINES 1024 /* TCAM num of entries */ +#define MV_BM_WIN_ID 12 +#define MV_PNC_WIN_ID 11 +#define MV_BM_WIN_ATTR 0x4 +#define MV_PNC_WIN_ATTR 0x4 /* New GMAC module is used */ #define MV_ETH_GMAC_NEW @@ -124,6 +133,8 @@ #define MV_ETH_WRR_NEW /* IPv6 parsing support for Legacy parser */ #define MV_ETH_LEGACY_PARSER_IPV6 +#define MV_ETH_PNC_NEW +#define MV_ETH_PNC_LB #endif /* CONFIG_MV_ETH_NETA */ Index: drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.c =================================================================== --- drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.c (working copy) @@ -0,0 +1,654 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or on the worldwide web +at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +*******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARCH_MVEBU +#include "mvNetConfig.h" +#endif /* CONFIG_ARCH_MVEBU */ + +#include "mv_net_complex_a39x.h" + +static u32 mv_net_complex_vbase_addr; +static u32 mv_net_complex_misc_vbase_addr; +static u32 mv_net_complex_phy_vbase_addr; +static u32 mv_net_reg_virt_base; + +static struct resource mv_net_complex_resources[] = { + { + .name = "netcomplex_misc", + .start = INTER_REGS_PHYS_BASE | 0x18200, + .end = (INTER_REGS_PHYS_BASE | 0x18200) + 0x100 - 1, /* 256 B */ + .flags = IORESOURCE_MEM, + }, { + .name = "netcomplex_phy", + .start = INTER_REGS_PHYS_BASE | 0x18300, + .end = (INTER_REGS_PHYS_BASE | 0x18300) + 0x200 - 1, /* 512 B */ + .flags = IORESOURCE_MEM, + }, { + .name = "netcomplex_base", + .start = INTER_REGS_PHYS_BASE | 0x18a00, + .end = (INTER_REGS_PHYS_BASE | 0x18a00) + 0x1000 - 1, /* 4 KB */ + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device mv_net_complex_plat = { + .name = MV_NET_COMPLEX_NAME, + .num_resources = ARRAY_SIZE(mv_net_complex_resources), + .resource = mv_net_complex_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + } +}; + +static void mv_net_assert_load_config(void) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_SYSTEM_SOFT_RESET); + reg &= ~NETC_NSS_SRAM_LOAD_CONF_MASK; + reg &= ~NETC_NSS_PPC_LOAD_CONF_MASK; + reg &= ~NETC_NSS_MACS_LOAD_CONF_MASK; + reg &= ~NETC_NSS_QM1_LOAD_CONF_MASK; + MV_REG_WRITE(MV_NETCOMP_SYSTEM_SOFT_RESET, reg); +} + +static void mv_net_de_assert_load_config(void) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_SYSTEM_SOFT_RESET); + reg |= NETC_NSS_SRAM_LOAD_CONF_MASK; + reg |= NETC_NSS_PPC_LOAD_CONF_MASK; + reg |= NETC_NSS_MACS_LOAD_CONF_MASK; + reg |= NETC_NSS_QM1_LOAD_CONF_MASK; + MV_REG_WRITE(MV_NETCOMP_SYSTEM_SOFT_RESET, reg); +} + +static void mv_net_pm_clock_down(void) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CLOCK_GATING); + + reg &= ~NETC_CLOCK_GATING_SRAM_X2_MASK; + reg &= ~NETC_CLOCK_GATING_SRAM_MASK; + reg &= ~NETC_CLOCK_GATING_PPC_CMAC_MASK; + reg &= ~NETC_CLOCK_GATING_PPC_PP_MASK; + reg &= ~NETC_CLOCK_GATING_PPC_NSS_MASK; + reg &= ~NETC_CLOCK_GATING_CMAC_MASK; + reg &= ~NETC_CLOCK_GATING_NSS_MASK; + reg &= ~NETC_CLOCK_GATING_QM2_MASK; + reg &= ~NETC_CLOCK_GATING_QM1_X2_MASK; + reg &= ~NETC_CLOCK_GATING_QM1_MASK; + + MV_REG_WRITE(MV_NETCOMP_CLOCK_GATING, reg); +} + +static void mv_net_pm_clock_up(void) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CLOCK_GATING); + + reg |= NETC_CLOCK_GATING_SRAM_X2_MASK; + reg |= NETC_CLOCK_GATING_SRAM_MASK; + reg |= NETC_CLOCK_GATING_PPC_CMAC_MASK; + reg |= NETC_CLOCK_GATING_PPC_PP_MASK; + reg |= NETC_CLOCK_GATING_PPC_NSS_MASK; + reg |= NETC_CLOCK_GATING_CMAC_MASK; + reg |= NETC_CLOCK_GATING_NSS_MASK; + reg |= NETC_CLOCK_GATING_QM2_MASK; + reg |= NETC_CLOCK_GATING_QM1_X2_MASK; + reg |= NETC_CLOCK_GATING_QM1_MASK; + + MV_REG_WRITE(MV_NETCOMP_CLOCK_GATING, reg); +} + +static void mv_net_restore_regs_defaults(void) +{ + /* WA for A390 Z1 - when NSS wake up from reset + registers default values are wrong */ + + mv_net_pm_clock_down(); + mv_net_assert_load_config(); + mv_net_pm_clock_up(); + mv_net_pm_clock_down(); + mv_net_de_assert_load_config(); + mv_net_pm_clock_up(); +} + +void mv_net_complex_nss_select(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_FUNCTION_ENABLE_CTRL_1); + reg &= ~NETC_PACKET_PROCESS_MASK; + + val <<= NETC_PACKET_PROCESS_OFFSET; + val &= NETC_PACKET_PROCESS_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_FUNCTION_ENABLE_CTRL_1, reg); +} + +static void mv_net_complex_active_port(u32 port, u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_1); + reg &= ~NETC_PORTS_ACTIVE_MASK(port); + + val <<= NETC_PORTS_ACTIVE_OFFSET(port); + val &= NETC_PORTS_ACTIVE_MASK(port); + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_1, reg); +} + +static void mv_net_complex_xaui_enable(u32 port, u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CONTROL_0); + reg &= ~NETC_CTRL_ENA_XAUI_MASK; + + val <<= NETC_CTRL_ENA_XAUI_OFFSET; + val &= NETC_CTRL_ENA_XAUI_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_CONTROL_0, reg); +} + +static void mv_net_complex_rxaui_enable(u32 port, u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CONTROL_0); + reg &= ~NETC_CTRL_ENA_RXAUI_MASK; + + val <<= NETC_CTRL_ENA_RXAUI_OFFSET; + val &= NETC_CTRL_ENA_RXAUI_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_CONTROL_0, reg); +} + +static void mv_net_complex_gop_reset(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_SYSTEM_SOFT_RESET); + reg &= ~NETC_GOP_SOFT_RESET_MASK; + + val <<= NETC_GOP_SOFT_RESET_OFFSET; + val &= NETC_GOP_SOFT_RESET_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_SYSTEM_SOFT_RESET, reg); +} + +static void mv_net_complex_gop_clock_logic_set(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_0); + reg &= ~NETC_CLK_DIV_PHASE_MASK; + + val <<= NETC_CLK_DIV_PHASE_OFFSET; + val &= NETC_CLK_DIV_PHASE_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_0, reg); +} + +static void mv_net_complex_port_rf_reset(u32 port, u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_1); + reg &= ~NETC_PORT_GIG_RF_RESET_MASK(port); + + val <<= NETC_PORT_GIG_RF_RESET_OFFSET(port); + val &= NETC_PORT_GIG_RF_RESET_MASK(port); + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_1, reg); +} + +static void mv_net_complex_gbe_mode_select(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CONTROL_0); + reg &= ~NETC_GBE_PORT1_MODE_MASK; + + val <<= NETC_GBE_PORT1_MODE_OFFSET; + val &= NETC_GBE_PORT1_MODE_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_CONTROL_0, reg); +} + +static void mv_net_complex_bus_width_select(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_0); + reg &= ~NETC_BUS_WIDTH_SELECT_MASK; + + val <<= NETC_BUS_WIDTH_SELECT_OFFSET; + val &= NETC_BUS_WIDTH_SELECT_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_0, reg); +} + +static void mv_net_complex_sample_stages_timing(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_0); + reg &= ~NETC_GIG_RX_DATA_SAMPLE_MASK; + + val <<= NETC_GIG_RX_DATA_SAMPLE_OFFSET; + val &= NETC_GIG_RX_DATA_SAMPLE_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_0, reg); +} + +static void mv_net_complex_com_phy_selector_config(u32 netComplex) +{ + u32 selector = MV_REG_READ(COMMON_PHYS_SELECTORS_REG); + + /* Change the value of the selector from the legacy mode to NSS mode */ + if (netComplex & MV_NETCOMP_GE_MAC0_2_SGMII_L0) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(0); + selector |= 0x4 << COMMON_PHYS_SELECTOR_LANE_OFFSET(0); + } + if (netComplex & MV_NETCOMP_GE_MAC0_2_SGMII_L1) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(1); + selector |= 0x8 << COMMON_PHYS_SELECTOR_LANE_OFFSET(1); + } + if (netComplex & MV_NETCOMP_GE_MAC1_2_SGMII_L1) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(1); + selector |= 0x9 << COMMON_PHYS_SELECTOR_LANE_OFFSET(1); + } + if (netComplex & MV_NETCOMP_GE_MAC1_2_SGMII_L2) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(2); + selector |= 0x5 << COMMON_PHYS_SELECTOR_LANE_OFFSET(2); + } + if (netComplex & MV_NETCOMP_GE_MAC2_2_SGMII_L3) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(3); + selector |= 0x7 << COMMON_PHYS_SELECTOR_LANE_OFFSET(3); + } + if (netComplex & MV_NETCOMP_GE_MAC3_2_SGMII_L4) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(4); + selector |= 0x8 << COMMON_PHYS_SELECTOR_LANE_OFFSET(4); + } + if (netComplex & MV_NETCOMP_GE_MAC2_2_SGMII_L5) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(5); + selector |= 0x6 << COMMON_PHYS_SELECTOR_LANE_OFFSET(5); + } + if (netComplex & MV_NETCOMP_GE_MAC3_2_SGMII_L6) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(6); + selector |= 0x2 << COMMON_PHYS_SELECTOR_LANE_OFFSET(6); + } + + MV_REG_WRITE(COMMON_PHYS_SELECTORS_REG, selector); +} + +static void mv_net_complex_qsgmii_ctrl_config(void) +{ + u32 reg; + /* Reset the QSGMII controller */ + reg = (MV_REG_READ(MV_NETCOMP_QSGMII_CTRL_1) & (~NETC_QSGMII_CTRL_RSTN_MASK)); + reg |= 0 << NETC_QSGMII_CTRL_RSTN_OFFSET; + MV_REG_WRITE(MV_NETCOMP_QSGMII_CTRL_1, reg); + /* Set the QSGMII controller to work with NSS */ + reg = (MV_REG_READ(MV_NETCOMP_QSGMII_CTRL_1) & (~NETC_QSGMII_CTRL_VERSION_MASK)); + reg |= 1 << NETC_QSGMII_CTRL_VERSION_OFFSET; + MV_REG_WRITE(MV_NETCOMP_QSGMII_CTRL_1, reg); + /* Enable the QSGMII Serdes-GOP path */ + reg = (MV_REG_READ(MV_NETCOMP_QSGMII_CTRL_1) & (~NETC_QSGMII_CTRL_V3ACTIVE_MASK)); + reg |= 0 << NETC_QSGMII_CTRL_V3ACTIVE_OFFSET; + MV_REG_WRITE(MV_NETCOMP_QSGMII_CTRL_1, reg); + /* De-assert the QSGMII controller */ + reg = (MV_REG_READ(MV_NETCOMP_QSGMII_CTRL_1) & (~NETC_QSGMII_CTRL_RSTN_MASK)); + reg |= 1 << NETC_QSGMII_CTRL_RSTN_OFFSET; + MV_REG_WRITE(MV_NETCOMP_QSGMII_CTRL_1, reg); +} + +static void mv_net_complex_mac_to_rgmii(u32 port, enum mvNetComplexPhase phase) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* Set Bus Width to HB mode = 1 */ + mv_net_complex_bus_width_select(1); + /* Select RGMII mode */ + mv_net_complex_gbe_mode_select(1); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +static void mv_net_complex_mac_to_qsgmii(u32 port, enum mvNetComplexPhase phase) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* Set Bus Width to FB mode = 0 */ + mv_net_complex_bus_width_select(0); + /* Select SGMII mode */ + mv_net_complex_gbe_mode_select(0); + /* Configure the sample stages */ + mv_net_complex_sample_stages_timing(0); + /* config QSGMII */ + mv_net_complex_qsgmii_ctrl_config(); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +static void mv_net_complex_mac_to_sgmii(u32 port, enum mvNetComplexPhase phase, u32 netComplex) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* Set Bus Width to HB mode = 1 */ + mv_net_complex_bus_width_select(1); + /* Select SGMII mode */ + mv_net_complex_gbe_mode_select(0); + /* Configure the sample stages */ + mv_net_complex_sample_stages_timing(0); + /* Configure the ComPhy Selector */ + mv_net_complex_com_phy_selector_config(netComplex); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +static void mv_net_complex_mac_to_rxaui(u32 port, enum mvNetComplexPhase phase) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* RXAUI Serdes/s Clock alignment */ + mv_net_complex_rxaui_enable(port, 1); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +static void mv_net_complex_mac_to_xaui(u32 port, enum mvNetComplexPhase phase) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* RXAUI Serdes/s Clock alignment */ + mv_net_complex_xaui_enable(port, 1); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +int mv_net_complex_init(u32 net_comp_config, enum mvNetComplexPhase phase) +{ + u32 reg; + u32 c = net_comp_config, i; + + if (phase == MV_NETC_FIRST_PHASE) { + /* fix the base address for transactions from the AXI to MBUS */ + reg = (MV_REG_READ(MV_NETCOMP_AMB_ACCESS_CTRL_0) & (~NETC_AMB_ACCESS_CTRL_MASK)); + reg |= (mv_net_reg_virt_base & NETC_AMB_ACCESS_CTRL_MASK); + MV_REG_WRITE(MV_NETCOMP_AMB_ACCESS_CTRL_0, reg); + + /* Reset the GOP unit */ + mv_net_complex_gop_reset(0); + /* Active the GOP 4 ports */ + for (i = 0; i < 4; i++) + mv_net_complex_active_port(i, 1); + } + + if (c & MV_NETCOMP_GE_MAC0_2_RXAUI) + mv_net_complex_mac_to_rxaui(0, phase); + + if (c & MV_NETCOMP_GE_MAC0_2_XAUI) + mv_net_complex_mac_to_xaui(0, phase); + + if (c & (MV_NETCOMP_GE_MAC0_2_SGMII_L0 | MV_NETCOMP_GE_MAC0_2_SGMII_L1)) + mv_net_complex_mac_to_sgmii(0, phase, c); + + if (c & MV_NETCOMP_GE_MAC0_2_QSGMII) + mv_net_complex_mac_to_qsgmii(0, phase); + + if (c & (MV_NETCOMP_GE_MAC1_2_SGMII_L1 | MV_NETCOMP_GE_MAC1_2_SGMII_L2 | + MV_NETCOMP_GE_MAC1_2_SGMII_L4)) + mv_net_complex_mac_to_sgmii(1, phase, c); + + if (c & MV_NETCOMP_GE_MAC1_2_QSGMII) + mv_net_complex_mac_to_qsgmii(1, phase); + + if (c & MV_NETCOMP_GE_MAC1_2_RGMII1) + mv_net_complex_mac_to_rgmii(1, phase); + + if (c & (MV_NETCOMP_GE_MAC2_2_SGMII_L3 | MV_NETCOMP_GE_MAC2_2_SGMII_L5)) + mv_net_complex_mac_to_sgmii(2, phase, c); + + if (c & MV_NETCOMP_GE_MAC2_2_QSGMII) + mv_net_complex_mac_to_qsgmii(2, phase); + + if (c & (MV_NETCOMP_GE_MAC3_2_SGMII_L4 | MV_NETCOMP_GE_MAC3_2_SGMII_L6)) + mv_net_complex_mac_to_sgmii(3, phase, c); + + if (c & MV_NETCOMP_GE_MAC3_2_QSGMII) + mv_net_complex_mac_to_qsgmii(3, phase); + + if (phase == MV_NETC_FIRST_PHASE) + /* Enable the NSS (PPv3) instead of the NetA (PPv1) */ + mv_net_complex_nss_select(1); + + else if (phase == MV_NETC_SECOND_PHASE) { + /* Enable the GOP internal clock logic */ + mv_net_complex_gop_clock_logic_set(1); + /* De-assert GOP unit reset */ + mv_net_complex_gop_reset(1); + + /* WA for A390 Z1 - when NSS wake up from reset + registers default values are wrong */ + mv_net_restore_regs_defaults(); + } + + return 0; +} + +static int mv_net_complex_plat_data_get(struct platform_device *pdev) +{ + struct resource *res; + + /* map Misc registers space */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "netcomplex_misc"); + if (!res) { + pr_err("Can not find SoC control registers base address, aborting\n"); + return -1; + } + + mv_net_complex_misc_vbase_addr = (u32)devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mv_net_complex_misc_vbase_addr) { + pr_err("Cannot map netcomplex misc registers, aborting\n"); + return -1; + } + pr_info("Net complex misc registers base: PHYS = 0x%x, VIRT = 0x%0x, size = %d Bytes\n", + res->start, mv_net_complex_misc_vbase_addr, resource_size(res)); + + /* map PHY registers space */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "netcomplex_phy"); + if (!res) { + pr_err("Can not find PHY registers base address, aborting\n"); + return -1; + } + + mv_net_complex_phy_vbase_addr = (u32)devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mv_net_complex_phy_vbase_addr) { + pr_err("Cannot map netcomplex phy registers, aborting\n"); + return -1; + } + pr_info("PP3 netcomplex PHY registers base: PHYS = 0x%x, VIRT = 0x%0x, size = %d Bytes\n", + res->start, mv_net_complex_phy_vbase_addr, resource_size(res)); + + /* map PHY registers space */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "netcomplex_base"); + if (!res) { + pr_err("Can not find PHY registers base address, aborting\n"); + return -1; + } + + mv_net_complex_vbase_addr = (u32)devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mv_net_complex_vbase_addr) { + pr_err("Cannot map netcomplex base registers, aborting\n"); + return -1; + } + pr_info("PP3 netcomplex base registers base: PHYS = 0x%x, VIRT = 0x%0x, size = %d Bytes\n", + res->start, mv_net_complex_vbase_addr, resource_size(res)); + + /* map register physical addr */ + mv_net_reg_virt_base = (u32)ioremap(INTER_REGS_PHYS_BASE, INTER_REGS_SIZE); + if (!mv_net_reg_virt_base) { + pr_err("Cannot map base registers, aborting\n"); + return -1; + } + + return 0; +} + +static int mv_net_complex_probe(struct platform_device *pdev) +{ + int ret; + u32 net_complex; + + ret = mv_net_complex_plat_data_get(pdev); + if (ret) { + pr_err("net complex data get fail\n"); + return -1; + } + + /* TODO -- Static initialize net complex temp, after fdt ready, fix it */ + net_complex = 0x421; + mv_net_complex_init(net_complex, 0); + mv_net_complex_init(net_complex, 1); + + return 0; +} + +static int mv_net_complex_remove(struct platform_device *pdev) +{ + /* free all shared resources */ + return 0; +} + +static struct platform_driver mv_net_complex_driver = { + .probe = mv_net_complex_probe, + .remove = mv_net_complex_remove, + .driver = { + .name = MV_NET_COMPLEX_NAME, + .owner = THIS_MODULE, + }, +}; + +static int mv_net_complex_device_register(void) +{ + /* Register netcomplex device */ + platform_device_register(&mv_net_complex_plat); + + return 0; +} + +static int __init mv_net_complex_init_module(void) +{ + int rc; + + /* register net device for static initialization, remove it when FDT ready */ + rc = mv_net_complex_device_register(); + if (rc < 0) { + pr_err("%s Net device register fail. rc=%d\n", __func__, rc); + return rc; + } + + rc = platform_driver_register(&mv_net_complex_driver); + if (rc) { + pr_err("%s: Can't register %s driver. rc=%d\n", + __func__, mv_net_complex_driver.driver.name, rc); + return rc; + } + pr_info("%s platform driver registered\n\n", mv_net_complex_driver.driver.name); + + return rc; +} +module_init(mv_net_complex_init_module); + +/*---------------------------------------------------------------------------*/ + +static void __exit mv_net_complex_cleanup_module(void) +{ + platform_driver_unregister(&mv_net_complex_driver); +} +module_exit(mv_net_complex_cleanup_module); Index: drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.h =================================================================== --- drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.h (working copy) @@ -0,0 +1,218 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or on the worldwide web +at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +*******************************************************************************/ + +#ifndef LINUX_MV_NETCOMPLEX_A39X_H +#define LINUX_MV_NETCOMPLEX_A39X_H + +#define MV_NET_COMPLEX_NAME "mv_net_complex" +#define MV_NET_COMPLEX_OFFSET (mv_net_complex_vbase_addr) +#define MV_MISC_REGS_OFFSET (mv_net_complex_misc_vbase_addr) +#define MV_COMMON_PHY_REGS_OFFSET (mv_net_complex_phy_vbase_addr) +#define MV_IP_CONFIG_REGS_OFFSET (mv_net_complex_phy_vbase_addr + 0x100) + +#define MV_REG_READ(offset) \ + readl((void *)(offset)) + +#define MV_REG_WRITE(offset, val) \ + writel((val), (void *)(offset)) + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +enum mvNetComplexTopology { + MV_NETCOMP_GE_MAC0_2_RXAUI = BIT0, + MV_NETCOMP_GE_MAC0_2_XAUI = BIT1, + MV_NETCOMP_GE_MAC0_2_SGMII_L0 = BIT2, + MV_NETCOMP_GE_MAC0_2_SGMII_L1 = BIT3, + MV_NETCOMP_GE_MAC0_2_QSGMII = BIT4, + MV_NETCOMP_GE_MAC1_2_SGMII_L1 = BIT5, + MV_NETCOMP_GE_MAC1_2_RGMII1 = BIT6, + MV_NETCOMP_GE_MAC1_2_SGMII_L2 = BIT7, + MV_NETCOMP_GE_MAC1_2_SGMII_L4 = BIT8, + MV_NETCOMP_GE_MAC1_2_QSGMII = BIT9, + MV_NETCOMP_GE_MAC2_2_SGMII_L3 = BIT10, + MV_NETCOMP_GE_MAC2_2_SGMII_L5 = BIT11, + MV_NETCOMP_GE_MAC2_2_QSGMII = BIT12, + MV_NETCOMP_GE_MAC3_2_SGMII_L4 = BIT13, + MV_NETCOMP_GE_MAC3_2_SGMII_L6 = BIT14, + MV_NETCOMP_GE_MAC3_2_QSGMII = BIT15 +}; + +enum mvNetComplexPhase { + MV_NETC_FIRST_PHASE, + MV_NETC_SECOND_PHASE, +}; + +/******************************************************************************/ +/* Power managment clock control1 */ +#define MV_NETCOMP_CLOCK_GATING (MV_NET_COMPLEX_OFFSET) + +#define NETC_CLOCK_GATING_SRAM_X2_OFFSET 8 +#define NETC_CLOCK_GATING_SRAM_X2_MASK (0x1 << NETC_CLOCK_GATING_SRAM_X2_OFFSET) + +#define NETC_CLOCK_GATING_SRAM_OFFSET 9 +#define NETC_CLOCK_GATING_SRAM_MASK (0x1 << NETC_CLOCK_GATING_SRAM_OFFSET) + +#define NETC_CLOCK_GATING_PPC_CMAC_OFFSET 10 +#define NETC_CLOCK_GATING_PPC_CMAC_MASK (0x1 << NETC_CLOCK_GATING_PPC_CMAC_OFFSET) + +#define NETC_CLOCK_GATING_PPC_PP_OFFSET 11 +#define NETC_CLOCK_GATING_PPC_PP_MASK (0x1 << NETC_CLOCK_GATING_PPC_PP_OFFSET) + +#define NETC_CLOCK_GATING_PPC_NSS_OFFSET 12 +#define NETC_CLOCK_GATING_PPC_NSS_MASK (0x1 << NETC_CLOCK_GATING_PPC_NSS_OFFSET) + +#define NETC_CLOCK_GATING_CMAC_OFFSET 13 +#define NETC_CLOCK_GATING_CMAC_MASK (0x1 << NETC_CLOCK_GATING_CMAC_OFFSET) + +#define NETC_CLOCK_GATING_NSS_OFFSET 14 +#define NETC_CLOCK_GATING_NSS_MASK (0x1 << NETC_CLOCK_GATING_NSS_OFFSET) + +#define NETC_CLOCK_GATING_QM2_OFFSET 15 +#define NETC_CLOCK_GATING_QM2_MASK (0x1 << NETC_CLOCK_GATING_QM2_OFFSET) + +#define NETC_CLOCK_GATING_QM1_X2_OFFSET 16 +#define NETC_CLOCK_GATING_QM1_X2_MASK (0x1 << NETC_CLOCK_GATING_QM1_X2_OFFSET) + +#define NETC_CLOCK_GATING_QM1_OFFSET 17 +#define NETC_CLOCK_GATING_QM1_MASK (0x1 << NETC_CLOCK_GATING_QM1_OFFSET) + +/* System Soft Reset 1 */ +#define MV_NETCOMP_SYSTEM_SOFT_RESET (MV_NET_COMPLEX_OFFSET + 0x8) + +#define NETC_GOP_SOFT_RESET_OFFSET 6 +#define NETC_GOP_SOFT_RESET_MASK (0x1 << NETC_GOP_SOFT_RESET_OFFSET) + +#define NETC_NSS_SRAM_LOAD_CONF_OFFSET 10 +#define NETC_NSS_SRAM_LOAD_CONF_MASK (0x1 << NETC_NSS_SRAM_LOAD_CONF_OFFSET) + +#define NETC_NSS_PPC_LOAD_CONF_OFFSET 12 +#define NETC_NSS_PPC_LOAD_CONF_MASK (0x1 << NETC_NSS_PPC_LOAD_CONF_OFFSET) + +#define NETC_NSS_MACS_LOAD_CONF_OFFSET 14 +#define NETC_NSS_MACS_LOAD_CONF_MASK (0x1 << NETC_NSS_MACS_LOAD_CONF_OFFSET) + +#define NETC_NSS_QM1_LOAD_CONF_OFFSET 17 +#define NETC_NSS_QM1_LOAD_CONF_MASK (0x1 << NETC_NSS_QM1_LOAD_CONF_OFFSET) + +/* Ports Control 0 */ +#define MV_NETCOMP_PORTS_CONTROL_0 (MV_NET_COMPLEX_OFFSET + 0x10) + +#define NETC_CLK_DIV_PHASE_OFFSET 31 +#define NETC_CLK_DIV_PHASE_MASK (0x1 << NETC_CLK_DIV_PHASE_OFFSET) + +#define NETC_GIG_RX_DATA_SAMPLE_OFFSET 29 +#define NETC_GIG_RX_DATA_SAMPLE_MASK (0x1 << NETC_GIG_RX_DATA_SAMPLE_OFFSET) + +#define NETC_BUS_WIDTH_SELECT_OFFSET 1 +#define NETC_BUS_WIDTH_SELECT_MASK (0x1 << NETC_BUS_WIDTH_SELECT_OFFSET) + +/* Ports Control 1 */ +#define MV_NETCOMP_PORTS_CONTROL_1 (MV_NET_COMPLEX_OFFSET + 0x14) + +#define NETC_PORT_GIG_RF_RESET_OFFSET(port) (28 + port) +#define NETC_PORT_GIG_RF_RESET_MASK(port) (0x1 << NETC_PORT_GIG_RF_RESET_OFFSET(port)) + +#define NETC_PORTS_ACTIVE_OFFSET(port) (0 + port) +#define NETC_PORTS_ACTIVE_MASK(port) (0x1 << NETC_PORTS_ACTIVE_OFFSET(port)) + +/* Networking Complex Control 0 */ +#define MV_NETCOMP_CONTROL_0 (MV_NET_COMPLEX_OFFSET + 0x20) + +#define NETC_CTRL_ENA_XAUI_OFFSET 11 +#define NETC_CTRL_ENA_XAUI_MASK (0x1 << NETC_CTRL_ENA_XAUI_OFFSET) + +#define NETC_CTRL_ENA_RXAUI_OFFSET 10 +#define NETC_CTRL_ENA_RXAUI_MASK (0x1 << NETC_CTRL_ENA_RXAUI_OFFSET) + +#define NETC_GBE_PORT1_MODE_OFFSET 1 +#define NETC_GBE_PORT1_MODE_MASK (0x1 << NETC_GBE_PORT1_MODE_OFFSET) + +/* Networking Complex AMB Access Control 0 */ +#define MV_NETCOMP_AMB_ACCESS_CTRL_0 (MV_NET_COMPLEX_OFFSET + 0xC0) + +#define NETC_AMB_ACCESS_CTRL_OFFSET 24 +#define NETC_AMB_ACCESS_CTRL_MASK (0xff << NETC_AMB_ACCESS_CTRL_OFFSET) + +/* QSGMII Control 1 */ +#define MV_NETCOMP_QSGMII_CTRL_1 (MV_IP_CONFIG_REGS_OFFSET + 0x94) + +#define NETC_QSGMII_CTRL_RSTN_OFFSET 31 +#define NETC_QSGMII_CTRL_RSTN_MASK (0x1 << NETC_QSGMII_CTRL_RSTN_OFFSET) + +#define NETC_QSGMII_CTRL_V3ACTIVE_OFFSET 29 +#define NETC_QSGMII_CTRL_V3ACTIVE_MASK (0x1 << NETC_QSGMII_CTRL_V3ACTIVE_OFFSET) + +#define NETC_QSGMII_CTRL_VERSION_OFFSET 28 +#define NETC_QSGMII_CTRL_VERSION_MASK (0x1 << NETC_QSGMII_CTRL_VERSION_OFFSET) + +/* Function Enable Control 1 */ +#define MV_NETCOMP_FUNCTION_ENABLE_CTRL_1 (MV_MISC_REGS_OFFSET + 0x88) + +#define NETC_PACKET_PROCESS_OFFSET 1 +#define NETC_PACKET_PROCESS_MASK (0x1 << NETC_PACKET_PROCESS_OFFSET) + +/* ComPhy Selector */ +#define COMMON_PHYS_SELECTORS_REG (MV_COMMON_PHY_REGS_OFFSET + 0xFC) + +#define COMMON_PHYS_SELECTOR_LANE_OFFSET(lane) (4 * lane) +#define COMMON_PHYS_SELECTOR_LANE_MASK(lane) (0xF << COMMON_PHYS_SELECTOR_LANE_OFFSET(lane)) + +int mv_net_complex_init(u32 net_comp_config, enum mvNetComplexPhase phase); +void mv_net_complex_nss_select(u32 val); + +#endif /* LINUX_MV_NETCOMPLEX_A39X_H */ Index: drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.c =================================================================== --- drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.c (working copy) @@ -0,0 +1,654 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or on the worldwide web +at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +*******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARCH_MVEBU +#include "mvNetConfig.h" +#endif /* CONFIG_ARCH_MVEBU */ + +#include "mv_net_complex_a39x.h" + +static u32 mv_net_complex_vbase_addr; +static u32 mv_net_complex_misc_vbase_addr; +static u32 mv_net_complex_phy_vbase_addr; +static u32 mv_net_reg_virt_base; + +static struct resource mv_net_complex_resources[] = { + { + .name = "netcomplex_misc", + .start = INTER_REGS_PHYS_BASE | 0x18200, + .end = (INTER_REGS_PHYS_BASE | 0x18200) + 0x100 - 1, /* 256 B */ + .flags = IORESOURCE_MEM, + }, { + .name = "netcomplex_phy", + .start = INTER_REGS_PHYS_BASE | 0x18300, + .end = (INTER_REGS_PHYS_BASE | 0x18300) + 0x200 - 1, /* 512 B */ + .flags = IORESOURCE_MEM, + }, { + .name = "netcomplex_base", + .start = INTER_REGS_PHYS_BASE | 0x18a00, + .end = (INTER_REGS_PHYS_BASE | 0x18a00) + 0x1000 - 1, /* 4 KB */ + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device mv_net_complex_plat = { + .name = MV_NET_COMPLEX_NAME, + .num_resources = ARRAY_SIZE(mv_net_complex_resources), + .resource = mv_net_complex_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + } +}; + +static void mv_net_assert_load_config(void) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_SYSTEM_SOFT_RESET); + reg &= ~NETC_NSS_SRAM_LOAD_CONF_MASK; + reg &= ~NETC_NSS_PPC_LOAD_CONF_MASK; + reg &= ~NETC_NSS_MACS_LOAD_CONF_MASK; + reg &= ~NETC_NSS_QM1_LOAD_CONF_MASK; + MV_REG_WRITE(MV_NETCOMP_SYSTEM_SOFT_RESET, reg); +} + +static void mv_net_de_assert_load_config(void) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_SYSTEM_SOFT_RESET); + reg |= NETC_NSS_SRAM_LOAD_CONF_MASK; + reg |= NETC_NSS_PPC_LOAD_CONF_MASK; + reg |= NETC_NSS_MACS_LOAD_CONF_MASK; + reg |= NETC_NSS_QM1_LOAD_CONF_MASK; + MV_REG_WRITE(MV_NETCOMP_SYSTEM_SOFT_RESET, reg); +} + +static void mv_net_pm_clock_down(void) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CLOCK_GATING); + + reg &= ~NETC_CLOCK_GATING_SRAM_X2_MASK; + reg &= ~NETC_CLOCK_GATING_SRAM_MASK; + reg &= ~NETC_CLOCK_GATING_PPC_CMAC_MASK; + reg &= ~NETC_CLOCK_GATING_PPC_PP_MASK; + reg &= ~NETC_CLOCK_GATING_PPC_NSS_MASK; + reg &= ~NETC_CLOCK_GATING_CMAC_MASK; + reg &= ~NETC_CLOCK_GATING_NSS_MASK; + reg &= ~NETC_CLOCK_GATING_QM2_MASK; + reg &= ~NETC_CLOCK_GATING_QM1_X2_MASK; + reg &= ~NETC_CLOCK_GATING_QM1_MASK; + + MV_REG_WRITE(MV_NETCOMP_CLOCK_GATING, reg); +} + +static void mv_net_pm_clock_up(void) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CLOCK_GATING); + + reg |= NETC_CLOCK_GATING_SRAM_X2_MASK; + reg |= NETC_CLOCK_GATING_SRAM_MASK; + reg |= NETC_CLOCK_GATING_PPC_CMAC_MASK; + reg |= NETC_CLOCK_GATING_PPC_PP_MASK; + reg |= NETC_CLOCK_GATING_PPC_NSS_MASK; + reg |= NETC_CLOCK_GATING_CMAC_MASK; + reg |= NETC_CLOCK_GATING_NSS_MASK; + reg |= NETC_CLOCK_GATING_QM2_MASK; + reg |= NETC_CLOCK_GATING_QM1_X2_MASK; + reg |= NETC_CLOCK_GATING_QM1_MASK; + + MV_REG_WRITE(MV_NETCOMP_CLOCK_GATING, reg); +} + +static void mv_net_restore_regs_defaults(void) +{ + /* WA for A390 Z1 - when NSS wake up from reset + registers default values are wrong */ + + mv_net_pm_clock_down(); + mv_net_assert_load_config(); + mv_net_pm_clock_up(); + mv_net_pm_clock_down(); + mv_net_de_assert_load_config(); + mv_net_pm_clock_up(); +} + +void mv_net_complex_nss_select(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_FUNCTION_ENABLE_CTRL_1); + reg &= ~NETC_PACKET_PROCESS_MASK; + + val <<= NETC_PACKET_PROCESS_OFFSET; + val &= NETC_PACKET_PROCESS_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_FUNCTION_ENABLE_CTRL_1, reg); +} + +static void mv_net_complex_active_port(u32 port, u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_1); + reg &= ~NETC_PORTS_ACTIVE_MASK(port); + + val <<= NETC_PORTS_ACTIVE_OFFSET(port); + val &= NETC_PORTS_ACTIVE_MASK(port); + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_1, reg); +} + +static void mv_net_complex_xaui_enable(u32 port, u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CONTROL_0); + reg &= ~NETC_CTRL_ENA_XAUI_MASK; + + val <<= NETC_CTRL_ENA_XAUI_OFFSET; + val &= NETC_CTRL_ENA_XAUI_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_CONTROL_0, reg); +} + +static void mv_net_complex_rxaui_enable(u32 port, u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CONTROL_0); + reg &= ~NETC_CTRL_ENA_RXAUI_MASK; + + val <<= NETC_CTRL_ENA_RXAUI_OFFSET; + val &= NETC_CTRL_ENA_RXAUI_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_CONTROL_0, reg); +} + +static void mv_net_complex_gop_reset(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_SYSTEM_SOFT_RESET); + reg &= ~NETC_GOP_SOFT_RESET_MASK; + + val <<= NETC_GOP_SOFT_RESET_OFFSET; + val &= NETC_GOP_SOFT_RESET_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_SYSTEM_SOFT_RESET, reg); +} + +static void mv_net_complex_gop_clock_logic_set(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_0); + reg &= ~NETC_CLK_DIV_PHASE_MASK; + + val <<= NETC_CLK_DIV_PHASE_OFFSET; + val &= NETC_CLK_DIV_PHASE_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_0, reg); +} + +static void mv_net_complex_port_rf_reset(u32 port, u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_1); + reg &= ~NETC_PORT_GIG_RF_RESET_MASK(port); + + val <<= NETC_PORT_GIG_RF_RESET_OFFSET(port); + val &= NETC_PORT_GIG_RF_RESET_MASK(port); + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_1, reg); +} + +static void mv_net_complex_gbe_mode_select(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_CONTROL_0); + reg &= ~NETC_GBE_PORT1_MODE_MASK; + + val <<= NETC_GBE_PORT1_MODE_OFFSET; + val &= NETC_GBE_PORT1_MODE_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_CONTROL_0, reg); +} + +static void mv_net_complex_bus_width_select(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_0); + reg &= ~NETC_BUS_WIDTH_SELECT_MASK; + + val <<= NETC_BUS_WIDTH_SELECT_OFFSET; + val &= NETC_BUS_WIDTH_SELECT_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_0, reg); +} + +static void mv_net_complex_sample_stages_timing(u32 val) +{ + u32 reg; + + reg = MV_REG_READ(MV_NETCOMP_PORTS_CONTROL_0); + reg &= ~NETC_GIG_RX_DATA_SAMPLE_MASK; + + val <<= NETC_GIG_RX_DATA_SAMPLE_OFFSET; + val &= NETC_GIG_RX_DATA_SAMPLE_MASK; + + reg |= val; + + MV_REG_WRITE(MV_NETCOMP_PORTS_CONTROL_0, reg); +} + +static void mv_net_complex_com_phy_selector_config(u32 netComplex) +{ + u32 selector = MV_REG_READ(COMMON_PHYS_SELECTORS_REG); + + /* Change the value of the selector from the legacy mode to NSS mode */ + if (netComplex & MV_NETCOMP_GE_MAC0_2_SGMII_L0) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(0); + selector |= 0x4 << COMMON_PHYS_SELECTOR_LANE_OFFSET(0); + } + if (netComplex & MV_NETCOMP_GE_MAC0_2_SGMII_L1) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(1); + selector |= 0x8 << COMMON_PHYS_SELECTOR_LANE_OFFSET(1); + } + if (netComplex & MV_NETCOMP_GE_MAC1_2_SGMII_L1) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(1); + selector |= 0x9 << COMMON_PHYS_SELECTOR_LANE_OFFSET(1); + } + if (netComplex & MV_NETCOMP_GE_MAC1_2_SGMII_L2) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(2); + selector |= 0x5 << COMMON_PHYS_SELECTOR_LANE_OFFSET(2); + } + if (netComplex & MV_NETCOMP_GE_MAC2_2_SGMII_L3) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(3); + selector |= 0x7 << COMMON_PHYS_SELECTOR_LANE_OFFSET(3); + } + if (netComplex & MV_NETCOMP_GE_MAC3_2_SGMII_L4) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(4); + selector |= 0x8 << COMMON_PHYS_SELECTOR_LANE_OFFSET(4); + } + if (netComplex & MV_NETCOMP_GE_MAC2_2_SGMII_L5) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(5); + selector |= 0x6 << COMMON_PHYS_SELECTOR_LANE_OFFSET(5); + } + if (netComplex & MV_NETCOMP_GE_MAC3_2_SGMII_L6) { + selector &= ~COMMON_PHYS_SELECTOR_LANE_MASK(6); + selector |= 0x2 << COMMON_PHYS_SELECTOR_LANE_OFFSET(6); + } + + MV_REG_WRITE(COMMON_PHYS_SELECTORS_REG, selector); +} + +static void mv_net_complex_qsgmii_ctrl_config(void) +{ + u32 reg; + /* Reset the QSGMII controller */ + reg = (MV_REG_READ(MV_NETCOMP_QSGMII_CTRL_1) & (~NETC_QSGMII_CTRL_RSTN_MASK)); + reg |= 0 << NETC_QSGMII_CTRL_RSTN_OFFSET; + MV_REG_WRITE(MV_NETCOMP_QSGMII_CTRL_1, reg); + /* Set the QSGMII controller to work with NSS */ + reg = (MV_REG_READ(MV_NETCOMP_QSGMII_CTRL_1) & (~NETC_QSGMII_CTRL_VERSION_MASK)); + reg |= 1 << NETC_QSGMII_CTRL_VERSION_OFFSET; + MV_REG_WRITE(MV_NETCOMP_QSGMII_CTRL_1, reg); + /* Enable the QSGMII Serdes-GOP path */ + reg = (MV_REG_READ(MV_NETCOMP_QSGMII_CTRL_1) & (~NETC_QSGMII_CTRL_V3ACTIVE_MASK)); + reg |= 0 << NETC_QSGMII_CTRL_V3ACTIVE_OFFSET; + MV_REG_WRITE(MV_NETCOMP_QSGMII_CTRL_1, reg); + /* De-assert the QSGMII controller */ + reg = (MV_REG_READ(MV_NETCOMP_QSGMII_CTRL_1) & (~NETC_QSGMII_CTRL_RSTN_MASK)); + reg |= 1 << NETC_QSGMII_CTRL_RSTN_OFFSET; + MV_REG_WRITE(MV_NETCOMP_QSGMII_CTRL_1, reg); +} + +static void mv_net_complex_mac_to_rgmii(u32 port, enum mvNetComplexPhase phase) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* Set Bus Width to HB mode = 1 */ + mv_net_complex_bus_width_select(1); + /* Select RGMII mode */ + mv_net_complex_gbe_mode_select(1); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +static void mv_net_complex_mac_to_qsgmii(u32 port, enum mvNetComplexPhase phase) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* Set Bus Width to FB mode = 0 */ + mv_net_complex_bus_width_select(0); + /* Select SGMII mode */ + mv_net_complex_gbe_mode_select(0); + /* Configure the sample stages */ + mv_net_complex_sample_stages_timing(0); + /* config QSGMII */ + mv_net_complex_qsgmii_ctrl_config(); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +static void mv_net_complex_mac_to_sgmii(u32 port, enum mvNetComplexPhase phase, u32 netComplex) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* Set Bus Width to HB mode = 1 */ + mv_net_complex_bus_width_select(1); + /* Select SGMII mode */ + mv_net_complex_gbe_mode_select(0); + /* Configure the sample stages */ + mv_net_complex_sample_stages_timing(0); + /* Configure the ComPhy Selector */ + mv_net_complex_com_phy_selector_config(netComplex); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +static void mv_net_complex_mac_to_rxaui(u32 port, enum mvNetComplexPhase phase) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* RXAUI Serdes/s Clock alignment */ + mv_net_complex_rxaui_enable(port, 1); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +static void mv_net_complex_mac_to_xaui(u32 port, enum mvNetComplexPhase phase) +{ + switch (phase) { + case MV_NETC_FIRST_PHASE: + /* RXAUI Serdes/s Clock alignment */ + mv_net_complex_xaui_enable(port, 1); + break; + case MV_NETC_SECOND_PHASE: + /* De-assert the relevant port HB reset */ + mv_net_complex_port_rf_reset(port, 1); + break; + } +} + +int mv_net_complex_init(u32 net_comp_config, enum mvNetComplexPhase phase) +{ + u32 reg; + u32 c = net_comp_config, i; + + if (phase == MV_NETC_FIRST_PHASE) { + /* fix the base address for transactions from the AXI to MBUS */ + reg = (MV_REG_READ(MV_NETCOMP_AMB_ACCESS_CTRL_0) & (~NETC_AMB_ACCESS_CTRL_MASK)); + reg |= (mv_net_reg_virt_base & NETC_AMB_ACCESS_CTRL_MASK); + MV_REG_WRITE(MV_NETCOMP_AMB_ACCESS_CTRL_0, reg); + + /* Reset the GOP unit */ + mv_net_complex_gop_reset(0); + /* Active the GOP 4 ports */ + for (i = 0; i < 4; i++) + mv_net_complex_active_port(i, 1); + } + + if (c & MV_NETCOMP_GE_MAC0_2_RXAUI) + mv_net_complex_mac_to_rxaui(0, phase); + + if (c & MV_NETCOMP_GE_MAC0_2_XAUI) + mv_net_complex_mac_to_xaui(0, phase); + + if (c & (MV_NETCOMP_GE_MAC0_2_SGMII_L0 | MV_NETCOMP_GE_MAC0_2_SGMII_L1)) + mv_net_complex_mac_to_sgmii(0, phase, c); + + if (c & MV_NETCOMP_GE_MAC0_2_QSGMII) + mv_net_complex_mac_to_qsgmii(0, phase); + + if (c & (MV_NETCOMP_GE_MAC1_2_SGMII_L1 | MV_NETCOMP_GE_MAC1_2_SGMII_L2 | + MV_NETCOMP_GE_MAC1_2_SGMII_L4)) + mv_net_complex_mac_to_sgmii(1, phase, c); + + if (c & MV_NETCOMP_GE_MAC1_2_QSGMII) + mv_net_complex_mac_to_qsgmii(1, phase); + + if (c & MV_NETCOMP_GE_MAC1_2_RGMII1) + mv_net_complex_mac_to_rgmii(1, phase); + + if (c & (MV_NETCOMP_GE_MAC2_2_SGMII_L3 | MV_NETCOMP_GE_MAC2_2_SGMII_L5)) + mv_net_complex_mac_to_sgmii(2, phase, c); + + if (c & MV_NETCOMP_GE_MAC2_2_QSGMII) + mv_net_complex_mac_to_qsgmii(2, phase); + + if (c & (MV_NETCOMP_GE_MAC3_2_SGMII_L4 | MV_NETCOMP_GE_MAC3_2_SGMII_L6)) + mv_net_complex_mac_to_sgmii(3, phase, c); + + if (c & MV_NETCOMP_GE_MAC3_2_QSGMII) + mv_net_complex_mac_to_qsgmii(3, phase); + + if (phase == MV_NETC_FIRST_PHASE) + /* Enable the NSS (PPv3) instead of the NetA (PPv1) */ + mv_net_complex_nss_select(1); + + else if (phase == MV_NETC_SECOND_PHASE) { + /* Enable the GOP internal clock logic */ + mv_net_complex_gop_clock_logic_set(1); + /* De-assert GOP unit reset */ + mv_net_complex_gop_reset(1); + + /* WA for A390 Z1 - when NSS wake up from reset + registers default values are wrong */ + mv_net_restore_regs_defaults(); + } + + return 0; +} + +static int mv_net_complex_plat_data_get(struct platform_device *pdev) +{ + struct resource *res; + + /* map Misc registers space */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "netcomplex_misc"); + if (!res) { + pr_err("Can not find SoC control registers base address, aborting\n"); + return -1; + } + + mv_net_complex_misc_vbase_addr = (u32)devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mv_net_complex_misc_vbase_addr) { + pr_err("Cannot map netcomplex misc registers, aborting\n"); + return -1; + } + pr_info("Net complex misc registers base: PHYS = 0x%x, VIRT = 0x%0x, size = %d Bytes\n", + res->start, mv_net_complex_misc_vbase_addr, resource_size(res)); + + /* map PHY registers space */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "netcomplex_phy"); + if (!res) { + pr_err("Can not find PHY registers base address, aborting\n"); + return -1; + } + + mv_net_complex_phy_vbase_addr = (u32)devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mv_net_complex_phy_vbase_addr) { + pr_err("Cannot map netcomplex phy registers, aborting\n"); + return -1; + } + pr_info("PP3 netcomplex PHY registers base: PHYS = 0x%x, VIRT = 0x%0x, size = %d Bytes\n", + res->start, mv_net_complex_phy_vbase_addr, resource_size(res)); + + /* map PHY registers space */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "netcomplex_base"); + if (!res) { + pr_err("Can not find PHY registers base address, aborting\n"); + return -1; + } + + mv_net_complex_vbase_addr = (u32)devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mv_net_complex_vbase_addr) { + pr_err("Cannot map netcomplex base registers, aborting\n"); + return -1; + } + pr_info("PP3 netcomplex base registers base: PHYS = 0x%x, VIRT = 0x%0x, size = %d Bytes\n", + res->start, mv_net_complex_vbase_addr, resource_size(res)); + + /* map register physical addr */ + mv_net_reg_virt_base = (u32)ioremap(INTER_REGS_PHYS_BASE, INTER_REGS_SIZE); + if (!mv_net_reg_virt_base) { + pr_err("Cannot map base registers, aborting\n"); + return -1; + } + + return 0; +} + +static int mv_net_complex_probe(struct platform_device *pdev) +{ + int ret; + u32 net_complex; + + ret = mv_net_complex_plat_data_get(pdev); + if (ret) { + pr_err("net complex data get fail\n"); + return -1; + } + + /* TODO -- Static initialize net complex temp, after fdt ready, fix it */ + net_complex = 0x421; + mv_net_complex_init(net_complex, 0); + mv_net_complex_init(net_complex, 1); + + return 0; +} + +static int mv_net_complex_remove(struct platform_device *pdev) +{ + /* free all shared resources */ + return 0; +} + +static struct platform_driver mv_net_complex_driver = { + .probe = mv_net_complex_probe, + .remove = mv_net_complex_remove, + .driver = { + .name = MV_NET_COMPLEX_NAME, + .owner = THIS_MODULE, + }, +}; + +static int mv_net_complex_device_register(void) +{ + /* Register netcomplex device */ + platform_device_register(&mv_net_complex_plat); + + return 0; +} + +static int __init mv_net_complex_init_module(void) +{ + int rc; + + /* register net device for static initialization, remove it when FDT ready */ + rc = mv_net_complex_device_register(); + if (rc < 0) { + pr_err("%s Net device register fail. rc=%d\n", __func__, rc); + return rc; + } + + rc = platform_driver_register(&mv_net_complex_driver); + if (rc) { + pr_err("%s: Can't register %s driver. rc=%d\n", + __func__, mv_net_complex_driver.driver.name, rc); + return rc; + } + pr_info("%s platform driver registered\n\n", mv_net_complex_driver.driver.name); + + return rc; +} +module_init(mv_net_complex_init_module); + +/*---------------------------------------------------------------------------*/ + +static void __exit mv_net_complex_cleanup_module(void) +{ + platform_driver_unregister(&mv_net_complex_driver); +} +module_exit(mv_net_complex_cleanup_module); Index: drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.h =================================================================== --- drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/net_complex/mv_net_complex_a39x.h (working copy) @@ -0,0 +1,218 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or on the worldwide web +at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +*******************************************************************************/ + +#ifndef LINUX_MV_NETCOMPLEX_A39X_H +#define LINUX_MV_NETCOMPLEX_A39X_H + +#define MV_NET_COMPLEX_NAME "mv_net_complex" +#define MV_NET_COMPLEX_OFFSET (mv_net_complex_vbase_addr) +#define MV_MISC_REGS_OFFSET (mv_net_complex_misc_vbase_addr) +#define MV_COMMON_PHY_REGS_OFFSET (mv_net_complex_phy_vbase_addr) +#define MV_IP_CONFIG_REGS_OFFSET (mv_net_complex_phy_vbase_addr + 0x100) + +#define MV_REG_READ(offset) \ + readl((void *)(offset)) + +#define MV_REG_WRITE(offset, val) \ + writel((val), (void *)(offset)) + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +enum mvNetComplexTopology { + MV_NETCOMP_GE_MAC0_2_RXAUI = BIT0, + MV_NETCOMP_GE_MAC0_2_XAUI = BIT1, + MV_NETCOMP_GE_MAC0_2_SGMII_L0 = BIT2, + MV_NETCOMP_GE_MAC0_2_SGMII_L1 = BIT3, + MV_NETCOMP_GE_MAC0_2_QSGMII = BIT4, + MV_NETCOMP_GE_MAC1_2_SGMII_L1 = BIT5, + MV_NETCOMP_GE_MAC1_2_RGMII1 = BIT6, + MV_NETCOMP_GE_MAC1_2_SGMII_L2 = BIT7, + MV_NETCOMP_GE_MAC1_2_SGMII_L4 = BIT8, + MV_NETCOMP_GE_MAC1_2_QSGMII = BIT9, + MV_NETCOMP_GE_MAC2_2_SGMII_L3 = BIT10, + MV_NETCOMP_GE_MAC2_2_SGMII_L5 = BIT11, + MV_NETCOMP_GE_MAC2_2_QSGMII = BIT12, + MV_NETCOMP_GE_MAC3_2_SGMII_L4 = BIT13, + MV_NETCOMP_GE_MAC3_2_SGMII_L6 = BIT14, + MV_NETCOMP_GE_MAC3_2_QSGMII = BIT15 +}; + +enum mvNetComplexPhase { + MV_NETC_FIRST_PHASE, + MV_NETC_SECOND_PHASE, +}; + +/******************************************************************************/ +/* Power managment clock control1 */ +#define MV_NETCOMP_CLOCK_GATING (MV_NET_COMPLEX_OFFSET) + +#define NETC_CLOCK_GATING_SRAM_X2_OFFSET 8 +#define NETC_CLOCK_GATING_SRAM_X2_MASK (0x1 << NETC_CLOCK_GATING_SRAM_X2_OFFSET) + +#define NETC_CLOCK_GATING_SRAM_OFFSET 9 +#define NETC_CLOCK_GATING_SRAM_MASK (0x1 << NETC_CLOCK_GATING_SRAM_OFFSET) + +#define NETC_CLOCK_GATING_PPC_CMAC_OFFSET 10 +#define NETC_CLOCK_GATING_PPC_CMAC_MASK (0x1 << NETC_CLOCK_GATING_PPC_CMAC_OFFSET) + +#define NETC_CLOCK_GATING_PPC_PP_OFFSET 11 +#define NETC_CLOCK_GATING_PPC_PP_MASK (0x1 << NETC_CLOCK_GATING_PPC_PP_OFFSET) + +#define NETC_CLOCK_GATING_PPC_NSS_OFFSET 12 +#define NETC_CLOCK_GATING_PPC_NSS_MASK (0x1 << NETC_CLOCK_GATING_PPC_NSS_OFFSET) + +#define NETC_CLOCK_GATING_CMAC_OFFSET 13 +#define NETC_CLOCK_GATING_CMAC_MASK (0x1 << NETC_CLOCK_GATING_CMAC_OFFSET) + +#define NETC_CLOCK_GATING_NSS_OFFSET 14 +#define NETC_CLOCK_GATING_NSS_MASK (0x1 << NETC_CLOCK_GATING_NSS_OFFSET) + +#define NETC_CLOCK_GATING_QM2_OFFSET 15 +#define NETC_CLOCK_GATING_QM2_MASK (0x1 << NETC_CLOCK_GATING_QM2_OFFSET) + +#define NETC_CLOCK_GATING_QM1_X2_OFFSET 16 +#define NETC_CLOCK_GATING_QM1_X2_MASK (0x1 << NETC_CLOCK_GATING_QM1_X2_OFFSET) + +#define NETC_CLOCK_GATING_QM1_OFFSET 17 +#define NETC_CLOCK_GATING_QM1_MASK (0x1 << NETC_CLOCK_GATING_QM1_OFFSET) + +/* System Soft Reset 1 */ +#define MV_NETCOMP_SYSTEM_SOFT_RESET (MV_NET_COMPLEX_OFFSET + 0x8) + +#define NETC_GOP_SOFT_RESET_OFFSET 6 +#define NETC_GOP_SOFT_RESET_MASK (0x1 << NETC_GOP_SOFT_RESET_OFFSET) + +#define NETC_NSS_SRAM_LOAD_CONF_OFFSET 10 +#define NETC_NSS_SRAM_LOAD_CONF_MASK (0x1 << NETC_NSS_SRAM_LOAD_CONF_OFFSET) + +#define NETC_NSS_PPC_LOAD_CONF_OFFSET 12 +#define NETC_NSS_PPC_LOAD_CONF_MASK (0x1 << NETC_NSS_PPC_LOAD_CONF_OFFSET) + +#define NETC_NSS_MACS_LOAD_CONF_OFFSET 14 +#define NETC_NSS_MACS_LOAD_CONF_MASK (0x1 << NETC_NSS_MACS_LOAD_CONF_OFFSET) + +#define NETC_NSS_QM1_LOAD_CONF_OFFSET 17 +#define NETC_NSS_QM1_LOAD_CONF_MASK (0x1 << NETC_NSS_QM1_LOAD_CONF_OFFSET) + +/* Ports Control 0 */ +#define MV_NETCOMP_PORTS_CONTROL_0 (MV_NET_COMPLEX_OFFSET + 0x10) + +#define NETC_CLK_DIV_PHASE_OFFSET 31 +#define NETC_CLK_DIV_PHASE_MASK (0x1 << NETC_CLK_DIV_PHASE_OFFSET) + +#define NETC_GIG_RX_DATA_SAMPLE_OFFSET 29 +#define NETC_GIG_RX_DATA_SAMPLE_MASK (0x1 << NETC_GIG_RX_DATA_SAMPLE_OFFSET) + +#define NETC_BUS_WIDTH_SELECT_OFFSET 1 +#define NETC_BUS_WIDTH_SELECT_MASK (0x1 << NETC_BUS_WIDTH_SELECT_OFFSET) + +/* Ports Control 1 */ +#define MV_NETCOMP_PORTS_CONTROL_1 (MV_NET_COMPLEX_OFFSET + 0x14) + +#define NETC_PORT_GIG_RF_RESET_OFFSET(port) (28 + port) +#define NETC_PORT_GIG_RF_RESET_MASK(port) (0x1 << NETC_PORT_GIG_RF_RESET_OFFSET(port)) + +#define NETC_PORTS_ACTIVE_OFFSET(port) (0 + port) +#define NETC_PORTS_ACTIVE_MASK(port) (0x1 << NETC_PORTS_ACTIVE_OFFSET(port)) + +/* Networking Complex Control 0 */ +#define MV_NETCOMP_CONTROL_0 (MV_NET_COMPLEX_OFFSET + 0x20) + +#define NETC_CTRL_ENA_XAUI_OFFSET 11 +#define NETC_CTRL_ENA_XAUI_MASK (0x1 << NETC_CTRL_ENA_XAUI_OFFSET) + +#define NETC_CTRL_ENA_RXAUI_OFFSET 10 +#define NETC_CTRL_ENA_RXAUI_MASK (0x1 << NETC_CTRL_ENA_RXAUI_OFFSET) + +#define NETC_GBE_PORT1_MODE_OFFSET 1 +#define NETC_GBE_PORT1_MODE_MASK (0x1 << NETC_GBE_PORT1_MODE_OFFSET) + +/* Networking Complex AMB Access Control 0 */ +#define MV_NETCOMP_AMB_ACCESS_CTRL_0 (MV_NET_COMPLEX_OFFSET + 0xC0) + +#define NETC_AMB_ACCESS_CTRL_OFFSET 24 +#define NETC_AMB_ACCESS_CTRL_MASK (0xff << NETC_AMB_ACCESS_CTRL_OFFSET) + +/* QSGMII Control 1 */ +#define MV_NETCOMP_QSGMII_CTRL_1 (MV_IP_CONFIG_REGS_OFFSET + 0x94) + +#define NETC_QSGMII_CTRL_RSTN_OFFSET 31 +#define NETC_QSGMII_CTRL_RSTN_MASK (0x1 << NETC_QSGMII_CTRL_RSTN_OFFSET) + +#define NETC_QSGMII_CTRL_V3ACTIVE_OFFSET 29 +#define NETC_QSGMII_CTRL_V3ACTIVE_MASK (0x1 << NETC_QSGMII_CTRL_V3ACTIVE_OFFSET) + +#define NETC_QSGMII_CTRL_VERSION_OFFSET 28 +#define NETC_QSGMII_CTRL_VERSION_MASK (0x1 << NETC_QSGMII_CTRL_VERSION_OFFSET) + +/* Function Enable Control 1 */ +#define MV_NETCOMP_FUNCTION_ENABLE_CTRL_1 (MV_MISC_REGS_OFFSET + 0x88) + +#define NETC_PACKET_PROCESS_OFFSET 1 +#define NETC_PACKET_PROCESS_MASK (0x1 << NETC_PACKET_PROCESS_OFFSET) + +/* ComPhy Selector */ +#define COMMON_PHYS_SELECTORS_REG (MV_COMMON_PHY_REGS_OFFSET + 0xFC) + +#define COMMON_PHYS_SELECTOR_LANE_OFFSET(lane) (4 * lane) +#define COMMON_PHYS_SELECTOR_LANE_MASK(lane) (0xF << COMMON_PHYS_SELECTOR_LANE_OFFSET(lane)) + +int mv_net_complex_init(u32 net_comp_config, enum mvNetComplexPhase phase); +void mv_net_complex_nss_select(u32 val); + +#endif /* LINUX_MV_NETCOMPLEX_A39X_H */ Index: drivers/net/ethernet/mvebu_net/neta/bm/bm_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/bm/bm_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/bm/bm_sysfs.c (working copy) @@ -61,9 +61,9 @@ if (!strcmp(name, "help")) return bm_help(buf); else if (!strcmp(name, "regs")) - mvBmRegs(); + mvNetaBmRegs(); else if (!strcmp(name, "stat")) - mvBmStatus(); + mvNetaBmStatus(); else if (!strcmp(name, "config")) mv_eth_bm_config_print(); else { @@ -87,7 +87,7 @@ local_irq_save(flags); if (!strcmp(name, "dump")) { - mvBmPoolDump(pool, val); + mvNetaBmPoolDump(pool, val); } else if (!strcmp(name, "size")) { err = mv_eth_ctrl_pool_size_set(pool, val); } else { Index: drivers/net/ethernet/mvebu_net/neta/hal/bm/mvBm.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/bm/mvBm.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/bm/mvBm.c (working copy) @@ -70,6 +70,7 @@ #include "mvOs.h" #ifdef CONFIG_ARCH_MVEBU +#include "mvebu-soc-id.h" #include "mvNetConfig.h" #else #include "mvSysEthConfig.h" @@ -81,12 +82,11 @@ static MV_BM_POOL mvBmPools[MV_BM_POOLS]; /* Initialize Hardware Buffer management unit */ -MV_STATUS mvBmInit(MV_U8 *virtBase) +MV_STATUS mvNetaBmInit(MV_U8 *virtBase) { - mvBmVirtBase = virtBase; - mvBmRegsInit(); + mvNetaBmRegsInit(); memset(mvBmPools, 0, sizeof(mvBmPools)); @@ -93,7 +93,7 @@ return MV_OK; } -void mvBmRegsInit(void) +void mvNetaBmRegsInit(void) { MV_U32 regVal; @@ -114,7 +114,7 @@ return; } -MV_STATUS mvBmControl(MV_COMMAND cmd) +MV_STATUS mvNetaBmControl(MV_COMMAND cmd) { MV_U32 regVal = 0; @@ -139,7 +139,7 @@ return MV_OK; } -MV_STATE mvBmStateGet(void) +MV_STATE mvNetaBmStateGet(void) { MV_U32 regVal; MV_STATE state; @@ -166,7 +166,7 @@ return state; } -void mvBmConfigSet(MV_U32 mask) +void mvNetaBmConfigSet(MV_U32 mask) { MV_U32 regVal; @@ -175,7 +175,7 @@ MV_REG_WRITE(MV_BM_CONFIG_REG, regVal); } -void mvBmConfigClear(MV_U32 mask) +void mvNetaBmConfigClear(MV_U32 mask) { MV_U32 regVal; @@ -184,7 +184,7 @@ MV_REG_WRITE(MV_BM_CONFIG_REG, regVal); } -void mvBmPoolTargetSet(int pool, MV_U8 targetId, MV_U8 attr) +void mvNetaBmPoolTargetSet(int pool, MV_U8 targetId, MV_U8 attr) { MV_U32 regVal; @@ -204,7 +204,7 @@ MV_REG_WRITE(MV_BM_XBAR_POOL_REG(pool), regVal); } -void mvBmPoolEnable(int pool) +void mvNetaBmPoolEnable(int pool) { MV_U32 regVal; @@ -222,7 +222,7 @@ } -void mvBmPoolDisable(int pool) +void mvNetaBmPoolDisable(int pool) { MV_U32 regVal; @@ -236,7 +236,7 @@ MV_REG_WRITE(MV_BM_POOL_BASE_REG(pool), regVal); } -MV_BOOL mvBmPoolIsEnabled(int pool) +MV_BOOL mvNetaBmPoolIsEnabled(int pool) { MV_U32 regVal; @@ -250,7 +250,7 @@ } /* Configure BM specific pool of "capacity" size. */ -MV_STATUS mvBmPoolInit(int pool, void *virtPoolBase, MV_ULONG physPoolBase, int capacity) +MV_STATUS mvNetaBmPoolInit(int pool, void *virtPoolBase, MV_ULONG physPoolBase, int capacity) { MV_BM_POOL *pBmPool; @@ -303,7 +303,7 @@ return MV_OK; } -MV_STATUS mvBmPoolBufSizeSet(int pool, int buf_size) +MV_STATUS mvNetaBmPoolBufferSizeSet(int pool, int buf_size) { MV_BM_POOL *pBmPool; @@ -319,7 +319,7 @@ return MV_OK; } -MV_STATUS mvBmPoolBufNumUpdate(int pool, int buf_num, int add) +MV_STATUS mvNetaBmPoolBufNumUpdate(int pool, int buf_num, int add) { MV_BM_POOL *pBmPool; @@ -343,7 +343,7 @@ return MV_OK; } -void mvBmPoolPrint(int pool) +void mvNetaBmPoolPrint(int pool) { MV_BM_POOL *pBmPool; @@ -364,7 +364,7 @@ pBmPool->pVirt, (unsigned)pBmPool->physAddr); } -void mvBmStatus(void) +void mvNetaBmStatus(void) { int i; @@ -371,10 +371,10 @@ mvOsPrintf("BM Pools status\n"); mvOsPrintf("pool: capacity bufSize bufNum virtPtr physAddr\n"); for (i = 0; i < MV_BM_POOLS; i++) - mvBmPoolPrint(i); + mvNetaBmPoolPrint(i); } -void mvBmPoolDump(int pool, int mode) +void mvNetaBmPoolDump(int pool, int mode) { MV_U32 regVal; MV_ULONG *pBufAddr; @@ -427,7 +427,7 @@ } } -void mvBmRegs(void) +void mvNetaBmRegs(void) { int pool; Index: drivers/net/ethernet/mvebu_net/neta/hal/bm/mvBm.h =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/bm/mvBm.h (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/bm/mvBm.h (working copy) @@ -114,22 +114,22 @@ } /* prototypes */ -MV_STATUS mvBmInit(MV_U8 *virtBase); -void mvBmRegsInit(void); -void mvBmConfigSet(MV_U32 mask); -void mvBmConfigClear(MV_U32 mask); -MV_STATUS mvBmControl(MV_COMMAND cmd); -MV_STATE mvBmStateGet(void); -void mvBmPoolTargetSet(int pool, MV_U8 targetId, MV_U8 attr); -void mvBmPoolEnable(int pool); -void mvBmPoolDisable(int pool); -MV_BOOL mvBmPoolIsEnabled(int pool); -MV_STATUS mvBmPoolInit(int pool, void *virtPoolBase, MV_ULONG physPoolBase, int capacity); -MV_STATUS mvBmPoolBufNumUpdate(int pool, int buf_num, int add); -MV_STATUS mvBmPoolBufSizeSet(int pool, int buf_size); -void mvBmRegs(void); -void mvBmStatus(void); -void mvBmPoolDump(int pool, int mode); -void mvBmPoolPrint(int pool); +MV_STATUS mvNetaBmInit(MV_U8 *virtBase); +void mvNetaBmRegsInit(void); +void mvNetaBmConfigSet(MV_U32 mask); +void mvNetaBmConfigClear(MV_U32 mask); +MV_STATUS mvNetaBmControl(MV_COMMAND cmd); +MV_STATE mvNetaBmStateGet(void); +void mvNetaBmPoolTargetSet(int pool, MV_U8 targetId, MV_U8 attr); +void mvNetaBmPoolEnable(int pool); +void mvNetaBmPoolDisable(int pool); +MV_BOOL mvNetaBmPoolIsEnabled(int pool); +MV_STATUS mvNetaBmPoolInit(int pool, void *virtPoolBase, MV_ULONG physPoolBase, int capacity); +MV_STATUS mvNetaBmPoolBufNumUpdate(int pool, int buf_num, int add); +MV_STATUS mvNetaBmPoolBufferSizeSet(int pool, int buf_size); +void mvNetaBmRegs(void); +void mvNetaBmStatus(void); +void mvNetaBmPoolDump(int pool, int mode); +void mvNetaBmPoolPrint(int pool); #endif /* __mvBm_h__ */ Index: drivers/net/ethernet/mvebu_net/neta/hal/bm/mvBmRegs.h =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/bm/mvBmRegs.h (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/bm/mvBmRegs.h (working copy) @@ -70,8 +70,13 @@ extern "C" { #endif /* __cplusplus */ +#ifdef CONFIG_OF +extern int bm_reg_vbase; +#define MV_BM_REG_BASE (bm_reg_vbase) +#endif #define MV_BM_POOLS 4 +#define MV_BM_POOLS_MASK (MV_BM_POOLS - 1) #define MV_BM_POOL_CAP_MAX (16*1024 - MV_BM_POOL_PTR_ALIGN/4) #define MV_BM_POOL_CAP_MIN 128 #define MV_BM_POOL_PTR_ALIGN 128 Index: drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNeta.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNeta.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNeta.c (working copy) @@ -76,12 +76,20 @@ MV_NETA_PORT_CTRL **mvNetaPortCtrl = NULL; MV_NETA_HAL_DATA mvNetaHalData; +/* Bitmap of NETA dymamic capabilities, such as PnC, BM, HWF and PME + * Pnc - 0x01 + * BM - 0x02 + * HWF - 0x04 + * PME - 0x08 +*/ +unsigned int neta_cap_bitmap = 0x0; + /* Function prototypes */ -#ifdef CONFIG_MV_ETH_LEGACY_PARSER +/* Legacy parse function start */ static MV_BOOL netaSetUcastAddr(int port, MV_U8 lastNibble, int queue); static MV_BOOL netaSetSpecialMcastAddr(int port, MV_U8 lastByte, int queue); static MV_BOOL netaSetOtherMcastAddr(int port, MV_U8 crc8, int queue); -#endif /* CONFIG_MV_ETH_LEGACY_PARSER */ +/* Legacy parse function end */ static void mvNetaPortSgmiiConfig(int port, MV_BOOL isInband); static MV_U8 *mvNetaDescrMemoryAlloc(MV_NETA_PORT_CTRL * pPortCtrl, int descSize, @@ -253,15 +261,14 @@ { int mode; -#if defined(CONFIG_MV_ETH_BM) && defined(CONFIG_MV_ETH_PNC) + if (MV_NETA_BM_CAP() && MV_NETA_PNC_CAP()) mode = NETA_ACC_MODE_MASK(NETA_ACC_MODE_EXT_PNC_BMU); -#elif defined(CONFIG_MV_ETH_BM) + else if (MV_NETA_BM_CAP()) mode = NETA_ACC_MODE_MASK(NETA_ACC_MODE_EXT_BMU); -#elif defined(CONFIG_MV_ETH_PNC) + else if (MV_NETA_PNC_CAP()) mode = NETA_ACC_MODE_MASK(NETA_ACC_MODE_EXT_PNC); -#else + else mode = NETA_ACC_MODE_MASK(NETA_ACC_MODE_EXT); -#endif return mode; } @@ -357,6 +364,7 @@ #ifdef CONFIG_MV_ETH_BM /* Set address of Buffer Management Unit */ + if (MV_NETA_BM_CAP()) MV_REG_WRITE(NETA_BM_ADDR_REG(port), mvNetaHalData.bmPhysBase); #endif /* CONFIG_MV_ETH_BM */ @@ -460,11 +468,13 @@ mvNetaPortCtrl[port] = NULL; #ifdef CONFIG_MV_ETH_BM - mvBmInit(mvNetaHalData.bmVirtBase); + if (MV_NETA_BM_CAP()) + mvNetaBmInit(mvNetaHalData.bmVirtBase); #endif /* CONFIG_MV_ETH_BM */ #ifdef CONFIG_MV_ETH_PNC - mvPncInit(mvNetaHalData.pncVirtBase); + if (MV_NETA_PNC_CAP()) + mvPncInit(mvNetaHalData.pncVirtBase, mvNetaHalData.pncTcamSize); #endif /* CONFIG_MV_ETH_PNC */ return MV_OK; @@ -1466,8 +1476,156 @@ /* MAC Filtering functions */ /******************************************************************************/ -#ifdef CONFIG_MV_ETH_LEGACY_PARSER +/************************ Legacy parse function start *******************************/ /******************************************************************************* +* netaSetUcastAddr - This function Set the port unicast address table +* +* DESCRIPTION: +* This function locates the proper entry in the Unicast table for the +* specified MAC nibble and sets its properties according to function +* parameters. +* +* INPUT: +* int portNo - Port number. +* MV_U8 lastNibble - Unicast MAC Address last nibble. +* int queue - Rx queue number for this MAC address. +* value "-1" means remove address. +* +* OUTPUT: +* This function add/removes MAC addresses from the port unicast address +* table. +* +* RETURN: +* MV_TRUE is output succeeded. +* MV_FALSE if option parameter is invalid. +* +*******************************************************************************/ +static MV_BOOL netaSetUcastAddr(int portNo, MV_U8 lastNibble, int queue) +{ + unsigned int unicastReg; + unsigned int tblOffset; + unsigned int regOffset; + + /* Locate the Unicast table entry */ + lastNibble = (0xf & lastNibble); + tblOffset = (lastNibble / 4) * 4; /* Register offset from unicast table base */ + regOffset = lastNibble % 4; /* Entry offset within the above register */ + + unicastReg = MV_REG_READ((ETH_DA_FILTER_UCAST_BASE(portNo) + tblOffset)); + + if (queue == -1) { + /* Clear accepts frame bit at specified unicast DA table entry */ + unicastReg &= ~(0xFF << (8 * regOffset)); + } else { + unicastReg &= ~(0xFF << (8 * regOffset)); + unicastReg |= ((0x01 | (queue << 1)) << (8 * regOffset)); + } + MV_REG_WRITE((ETH_DA_FILTER_UCAST_BASE(portNo) + tblOffset), unicastReg); + + return MV_TRUE; +} + +/******************************************************************************* +* netaSetSpecialMcastAddr - Special Multicast address settings. +* +* DESCRIPTION: +* This routine controls the MV device special MAC multicast support. +* The Special Multicast Table for MAC addresses supports MAC of the form +* 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF). +* The MAC DA[7:0] bits are used as a pointer to the Special Multicast +* Table entries in the DA-Filter table. +* This function set the Special Multicast Table appropriate entry +* according to the argument given. +* +* INPUT: +* int port Port number. +* unsigned char mcByte Multicast addr last byte (MAC DA[7:0] bits). +* int queue Rx queue number for this MAC address. +* int option 0 = Add, 1 = remove address. +* +* OUTPUT: +* See description. +* +* RETURN: +* MV_TRUE is output succeeded. +* MV_FALSE if option parameter is invalid. +* +*******************************************************************************/ +static MV_BOOL netaSetSpecialMcastAddr(int port, MV_U8 lastByte, int queue) +{ + unsigned int smcTableReg; + unsigned int tblOffset; + unsigned int regOffset; + + /* Locate the SMC table entry */ + tblOffset = (lastByte / 4); /* Register offset from SMC table base */ + regOffset = lastByte % 4; /* Entry offset within the above register */ + + smcTableReg = MV_REG_READ((ETH_DA_FILTER_SPEC_MCAST_BASE(port) + tblOffset * 4)); + + if (queue == -1) { + /* Clear accepts frame bit at specified Special DA table entry */ + smcTableReg &= ~(0xFF << (8 * regOffset)); + } else { + smcTableReg &= ~(0xFF << (8 * regOffset)); + smcTableReg |= ((0x01 | (queue << 1)) << (8 * regOffset)); + } + MV_REG_WRITE((ETH_DA_FILTER_SPEC_MCAST_BASE(port) + tblOffset * 4), smcTableReg); + + return MV_TRUE; +} + +/******************************************************************************* +* netaSetOtherMcastAddr - Multicast address settings. +* +* DESCRIPTION: +* This routine controls the MV device Other MAC multicast support. +* The Other Multicast Table is used for multicast of another type. +* A CRC-8bit is used as an index to the Other Multicast Table entries +* in the DA-Filter table. +* The function gets the CRC-8bit value from the calling routine and +* set the Other Multicast Table appropriate entry according to the +* CRC-8 argument given. +* +* INPUT: +* int port Port number. +* MV_U8 crc8 A CRC-8bit (Polynomial: x^8+x^2+x^1+1). +* int queue Rx queue number for this MAC address. +* +* OUTPUT: +* See description. +* +* RETURN: +* MV_TRUE is output succeeded. +* MV_FALSE if option parameter is invalid. +* +*******************************************************************************/ +static MV_BOOL netaSetOtherMcastAddr(int port, MV_U8 crc8, int queue) +{ + unsigned int omcTableReg; + unsigned int tblOffset; + unsigned int regOffset; + + /* Locate the OMC table entry */ + tblOffset = (crc8 / 4) * 4; /* Register offset from OMC table base */ + regOffset = crc8 % 4; /* Entry offset within the above register */ + + omcTableReg = MV_REG_READ((ETH_DA_FILTER_OTH_MCAST_BASE(port) + tblOffset)); + + if (queue == -1) { + /* Clear accepts frame bit at specified Other DA table entry */ + omcTableReg &= ~(0xFF << (8 * regOffset)); + } else { + omcTableReg &= ~(0xFF << (8 * regOffset)); + omcTableReg |= ((0x01 | (queue << 1)) << (8 * regOffset)); + } + + MV_REG_WRITE((ETH_DA_FILTER_OTH_MCAST_BASE(port) + tblOffset), omcTableReg); + + return MV_TRUE; +} + +/******************************************************************************* * mvNetaRxUnicastPromiscSet - Configure Fitering mode of Ethernet port * * DESCRIPTION: @@ -1743,7 +1901,7 @@ } return MV_OK; } -#endif /* CONFIG_MV_ETH_LEGACY_PARSER */ +/************************ Legacy parse function end *******************************/ /******************************************************************************* * mvNetaSetUcastTable - Unicast address settings. @@ -1829,156 +1987,8 @@ MV_REG_WRITE((ETH_DA_FILTER_OTH_MCAST_BASE(portNo) + offset), regValue); } -#ifdef CONFIG_MV_ETH_LEGACY_PARSER +/************************ Legacy parse function start *******************************/ /******************************************************************************* -* netaSetUcastAddr - This function Set the port unicast address table -* -* DESCRIPTION: -* This function locates the proper entry in the Unicast table for the -* specified MAC nibble and sets its properties according to function -* parameters. -* -* INPUT: -* int portNo - Port number. -* MV_U8 lastNibble - Unicast MAC Address last nibble. -* int queue - Rx queue number for this MAC address. -* value "-1" means remove address. -* -* OUTPUT: -* This function add/removes MAC addresses from the port unicast address -* table. -* -* RETURN: -* MV_TRUE is output succeeded. -* MV_FALSE if option parameter is invalid. -* -*******************************************************************************/ -static MV_BOOL netaSetUcastAddr(int portNo, MV_U8 lastNibble, int queue) -{ - unsigned int unicastReg; - unsigned int tblOffset; - unsigned int regOffset; - - /* Locate the Unicast table entry */ - lastNibble = (0xf & lastNibble); - tblOffset = (lastNibble / 4) * 4; /* Register offset from unicast table base */ - regOffset = lastNibble % 4; /* Entry offset within the above register */ - - unicastReg = MV_REG_READ((ETH_DA_FILTER_UCAST_BASE(portNo) + tblOffset)); - - if (queue == -1) { - /* Clear accepts frame bit at specified unicast DA table entry */ - unicastReg &= ~(0xFF << (8 * regOffset)); - } else { - unicastReg &= ~(0xFF << (8 * regOffset)); - unicastReg |= ((0x01 | (queue << 1)) << (8 * regOffset)); - } - MV_REG_WRITE((ETH_DA_FILTER_UCAST_BASE(portNo) + tblOffset), unicastReg); - - return MV_TRUE; -} - -/******************************************************************************* -* netaSetSpecialMcastAddr - Special Multicast address settings. -* -* DESCRIPTION: -* This routine controls the MV device special MAC multicast support. -* The Special Multicast Table for MAC addresses supports MAC of the form -* 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF). -* The MAC DA[7:0] bits are used as a pointer to the Special Multicast -* Table entries in the DA-Filter table. -* This function set the Special Multicast Table appropriate entry -* according to the argument given. -* -* INPUT: -* int port Port number. -* unsigned char mcByte Multicast addr last byte (MAC DA[7:0] bits). -* int queue Rx queue number for this MAC address. -* int option 0 = Add, 1 = remove address. -* -* OUTPUT: -* See description. -* -* RETURN: -* MV_TRUE is output succeeded. -* MV_FALSE if option parameter is invalid. -* -*******************************************************************************/ -static MV_BOOL netaSetSpecialMcastAddr(int port, MV_U8 lastByte, int queue) -{ - unsigned int smcTableReg; - unsigned int tblOffset; - unsigned int regOffset; - - /* Locate the SMC table entry */ - tblOffset = (lastByte / 4); /* Register offset from SMC table base */ - regOffset = lastByte % 4; /* Entry offset within the above register */ - - smcTableReg = MV_REG_READ((ETH_DA_FILTER_SPEC_MCAST_BASE(port) + tblOffset * 4)); - - if (queue == -1) { - /* Clear accepts frame bit at specified Special DA table entry */ - smcTableReg &= ~(0xFF << (8 * regOffset)); - } else { - smcTableReg &= ~(0xFF << (8 * regOffset)); - smcTableReg |= ((0x01 | (queue << 1)) << (8 * regOffset)); - } - MV_REG_WRITE((ETH_DA_FILTER_SPEC_MCAST_BASE(port) + tblOffset * 4), smcTableReg); - - return MV_TRUE; -} - -/******************************************************************************* -* netaSetOtherMcastAddr - Multicast address settings. -* -* DESCRIPTION: -* This routine controls the MV device Other MAC multicast support. -* The Other Multicast Table is used for multicast of another type. -* A CRC-8bit is used as an index to the Other Multicast Table entries -* in the DA-Filter table. -* The function gets the CRC-8bit value from the calling routine and -* set the Other Multicast Table appropriate entry according to the -* CRC-8 argument given. -* -* INPUT: -* int port Port number. -* MV_U8 crc8 A CRC-8bit (Polynomial: x^8+x^2+x^1+1). -* int queue Rx queue number for this MAC address. -* -* OUTPUT: -* See description. -* -* RETURN: -* MV_TRUE is output succeeded. -* MV_FALSE if option parameter is invalid. -* -*******************************************************************************/ -static MV_BOOL netaSetOtherMcastAddr(int port, MV_U8 crc8, int queue) -{ - unsigned int omcTableReg; - unsigned int tblOffset; - unsigned int regOffset; - - /* Locate the OMC table entry */ - tblOffset = (crc8 / 4) * 4; /* Register offset from OMC table base */ - regOffset = crc8 % 4; /* Entry offset within the above register */ - - omcTableReg = MV_REG_READ((ETH_DA_FILTER_OTH_MCAST_BASE(port) + tblOffset)); - - if (queue == -1) { - /* Clear accepts frame bit at specified Other DA table entry */ - omcTableReg &= ~(0xFF << (8 * regOffset)); - } else { - omcTableReg &= ~(0xFF << (8 * regOffset)); - omcTableReg |= ((0x01 | (queue << 1)) << (8 * regOffset)); - } - - MV_REG_WRITE((ETH_DA_FILTER_OTH_MCAST_BASE(port) + tblOffset), omcTableReg); - - return MV_TRUE; -} - -/******************************************************************************* * mvNetaTosToRxqSet - Map packets with special TOS value to special RX queue * * DESCRIPTION: @@ -2086,7 +2096,7 @@ return rxq; } -#endif /* CONFIG_MV_ETH_LEGACY_PARSER */ +/************************ Legacy parse function end *******************************/ /******************************************************************************/ /* PHY Control Functions */ @@ -2542,6 +2552,7 @@ case MV_NETA_DSA_EXT: regVal |= ETH_DSA_EXT_MASK; + break; default: mvOsPrintf("port=%d: Unexpected MH = %d value\n", port, mh); @@ -3245,7 +3256,7 @@ return MV_OK; } -#ifdef CONFIG_MV_ETH_LEGACY_PARSER +/************************ Legacy parse function start *******************************/ /******************************************************************************/ /* RX Dispatching configuration routines */ /******************************************************************************/ @@ -3333,9 +3344,8 @@ return MV_OK; } -#endif /* CONFIG_MV_ETH_LEGACY_PARSER */ +/************************ Legacy parse function end *******************************/ - /******************************************************************************/ /* MIB Counters functions */ /******************************************************************************/ Index: drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNeta.h =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNeta.h (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNeta.h (working copy) @@ -133,6 +133,9 @@ #define NETA_RX_GET_IPHDR_HDRLEN(rxd) (((rxd)->status & NETA_RX_IP_HLEN_MASK) >> NETA_RX_IP_HLEN_OFFS) #define NETA_RX_SET_IPHDR_HDRLEN(rxd, hlen) ((rxd)->status |= ((hlen) << NETA_RX_IP_HLEN_OFFS) & NETA_RX_IP_HLEN_MASK) +#define NETA_RX_GET_BPID(rxd) (((rxd)->status & NETA_RX_BM_POOL_ALL_MASK) >> NETA_RX_BM_POOL_ID_OFFS) + + #ifdef CONFIG_MV_ETH_PNC #define NETA_RX_IS_PPPOE(rxd) ((rxd)->pncInfo & NETA_PNC_PPPOE) @@ -303,6 +306,7 @@ #endif /* CONFIG_MV_ETH_BM */ #ifdef CONFIG_MV_ETH_PNC + MV_U32 pncTcamSize; MV_ULONG pncPhysBase; MV_U8 *pncVirtBase; #endif /* CONFIG_MV_ETH_PNC */ @@ -365,6 +369,17 @@ extern MV_NETA_PORT_CTRL **mvNetaPortCtrl; extern MV_NETA_HAL_DATA mvNetaHalData; +extern unsigned int neta_cap_bitmap; +/* NETA dynamic capabilities bitmap define */ +#define MV_ETH_CAP_PNC (0x00000001) +#define MV_ETH_CAP_BM (0x00000002) +#define MV_ETH_CAP_HWF (0x00000004) +#define MV_ETH_CAP_PME (0x00000008) +/* NETA dynamic capabilities macro */ +#define MV_NETA_BM_CAP() (MV_ETH_CAP_BM & neta_cap_bitmap) +#define MV_NETA_PNC_CAP() (MV_ETH_CAP_PNC & neta_cap_bitmap) +#define MV_NETA_HWF_CAP() (MV_ETH_CAP_HWF & neta_cap_bitmap) +#define MV_NETA_PMT_CAP() (MV_ETH_CAP_PME & neta_cap_bitmap) /* Get Giga port handler */ static INLINE MV_NETA_PORT_CTRL *mvNetaPortHndlGet(int port) @@ -737,7 +752,7 @@ void mvNetaSetUcastTable(int port, int queue); void mvNetaSetSpecialMcastTable(int portNo, int queue); -#ifdef CONFIG_MV_ETH_LEGACY_PARSER +/************************ Legacy parse function start *******************************/ MV_STATUS mvNetaRxUnicastPromiscSet(int port, MV_BOOL isPromisc); MV_STATUS mvNetaMcastAddrSet(int port, MV_U8 *pAddr, int queue); @@ -753,7 +768,7 @@ MV_STATUS mvNetaUdpRxq(int port, int rxq); MV_STATUS mvNetaArpRxq(int port, int rxq); MV_STATUS mvNetaBpduRxq(int port, int rxq); -#endif /* CONFIG_MV_ETH_LEGACY_PARSER */ +/************************ Legacy parse function end *******************************/ void mvNetaPhyAddrSet(int port, int phyAddr); int mvNetaPhyAddrGet(int port); Index: drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNetaDebug.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNetaDebug.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNetaDebug.c (working copy) @@ -422,7 +422,6 @@ mvOsPrintf("link down\n"); } #ifndef CONFIG_MV_ETH_PNC - { MV_U32 regValue = MV_REG_READ(ETH_PORT_CONFIG_REG(port)); mvOsPrintf("default queue: rx=%d, arp=%d, bpdu=%d, tcp=%d, udp=%d\n", @@ -431,6 +430,16 @@ (regValue & ETH_DEF_RX_BPDU_QUEUE_ALL_MASK) >> ETH_DEF_RX_BPDU_QUEUE_OFFSET, (regValue & ETH_DEF_RX_TCP_QUEUE_ALL_MASK) >> ETH_DEF_RX_TCP_QUEUE_OFFSET, (regValue & ETH_DEF_RX_UDP_QUEUE_ALL_MASK) >> ETH_DEF_RX_UDP_QUEUE_OFFSET); +#else /* CONFIG_MV_ETH_PNC */ + if (!MV_NETA_PNC_CAP()) { + MV_U32 regValue = MV_REG_READ(ETH_PORT_CONFIG_REG(port)); + + mvOsPrintf("default queue: rx=%d, arp=%d, bpdu=%d, tcp=%d, udp=%d\n", + (regValue & ETH_DEF_RX_QUEUE_ALL_MASK) >> ETH_DEF_RX_QUEUE_OFFSET, + (regValue & ETH_DEF_RX_ARP_QUEUE_ALL_MASK) >> ETH_DEF_RX_ARP_QUEUE_OFFSET, + (regValue & ETH_DEF_RX_BPDU_QUEUE_ALL_MASK) >> ETH_DEF_RX_BPDU_QUEUE_OFFSET, + (regValue & ETH_DEF_RX_TCP_QUEUE_ALL_MASK) >> ETH_DEF_RX_TCP_QUEUE_OFFSET, + (regValue & ETH_DEF_RX_UDP_QUEUE_ALL_MASK) >> ETH_DEF_RX_UDP_QUEUE_OFFSET); } #endif /* CONFIG_MV_ETH_PNC */ } Index: drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNetaRegs.h =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNetaRegs.h (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/gbe/mvNetaRegs.h (working copy) @@ -71,9 +71,11 @@ #endif /* __cplusplus */ #ifdef CONFIG_ARCH_MVEBU +#include "mvebu-soc-id.h" #include "mvNetConfig.h" #else #include "mvSysEthConfig.h" +#include "ctrlEnv/mvCtrlEnvLib.h" #endif #ifdef CONFIG_OF Index: drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPnc.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPnc.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPnc.c (working copy) @@ -65,7 +65,9 @@ #include "mvOs.h" #include "mvCommon.h" #include "mv802_3.h" +#ifndef CONFIG_OF #include "ctrlEnv/mvCtrlEnvLib.h" +#endif #include "gbe/mvNetaRegs.h" #include "gbe/mvEthRegs.h" @@ -98,87 +100,99 @@ static int rxq_ip4_udp = CONFIG_MV_ETH_RXQ_DEF; static int rxq_arp = CONFIG_MV_ETH_RXQ_DEF; +/* xlate gbe port number to port value in pnc entry */ +struct gbe_pnc_port_mapping gbe_pnc_map[PORT_BITS]; +struct gbe_pnc_port_mapping gbe_pnc_map_kw2[] = { + {.gbe_port = 0, .pnc_port = 2}, + {.gbe_port = 1, .pnc_port = 4}, + {.gbe_port = 2, .pnc_port = 0}, +}; +struct gbe_pnc_port_mapping gbe_pnc_map_axp[] = { + {.gbe_port = 0, .pnc_port = 0}, + {.gbe_port = 1, .pnc_port = 4}, + {.gbe_port = 2, .pnc_port = 2}, + {.gbe_port = 3, .pnc_port = 3}, + {.gbe_port = 0, .pnc_port = 1}, +}; +struct gbe_pnc_port_mapping gbe_pnc_map_38x[] = { + {.gbe_port = 0, .pnc_port = 0}, + {.gbe_port = 1, .pnc_port = 2}, + {.gbe_port = 2, .pnc_port = 4}, +}; -#ifdef CONFIG_ARCH_FEROCEON_KW2 -int pnc_port_map(int pnc_port) + +#ifdef CONFIG_OF +int pnc_gbe_port_map_init(unsigned int ctrl_model, unsigned int ctrl_rev) { - switch (pnc_port) { - case 2: - return 0; - case 4: - return 1; + memset(&gbe_pnc_map, 0xff, sizeof(gbe_pnc_map)); - case 0: - return 2; - - default: - mvOsPrintf("%s: pnc_port=%d is out of range\n", __func__, pnc_port); + if (ctrl_model == MV78230_DEV_ID + || ctrl_model == MV78260_DEV_ID + || ctrl_model == MV78460_DEV_ID) { + /* Armada XP ID */ + memcpy(&gbe_pnc_map, &gbe_pnc_map_axp, sizeof(gbe_pnc_map_axp)); + } else if (ctrl_model == MV88F6510_DEV_ID + || ctrl_model == MV88F6530_DEV_ID + || ctrl_model == MV88F6601_DEV_ID + || ctrl_model == MV88F6560_DEV_ID) { + /* Armada KW2 ID */ + memcpy(&gbe_pnc_map, &gbe_pnc_map_kw2, sizeof(gbe_pnc_map_kw2)); + } else if (ctrl_model == MV88F6810_DEV_ID + || ctrl_model == MV88F6811_DEV_ID + || ctrl_model == MV88F6820_DEV_ID + || ctrl_model == MV88F6828_DEV_ID) { + /* Armada A38x ID */ + memcpy(&gbe_pnc_map, &gbe_pnc_map_38x, sizeof(gbe_pnc_map_38x)); + } else { + mvOsPrintf("%s: ctrl_model=%x is not supported\n", __func__, ctrl_model); return -1; } + return 0; } - -int pnc_eth_port_map(int eth_port) +#else +int pnc_gbe_port_map_init(unsigned int ctrl_model, unsigned int ctrl_rev) { - switch (eth_port) { - case 0: - return 2; + memset(&gbe_pnc_map, 0xff, sizeof(gbe_pnc_map)); - case 1: - return 4; +#ifdef CONFIG_ARCH_FEROCEON_KW2 + /* Armada KW2 ID */ + memcpy(&gbe_pnc_map, &gbe_pnc_map_kw2, sizeof(gbe_pnc_map_kw2)); +#elif defined(CONFIG_ARCH_ARMADA38X) + /* Armada A38x ID */ + memcpy(&gbe_pnc_map, &gbe_pnc_map_38x, sizeof(gbe_pnc_map_38x)); +#else + /* Armada XP ID */ + memcpy(&gbe_pnc_map, &gbe_pnc_map_axp, sizeof(gbe_pnc_map_axp)); +#endif - case 2: return 0; +} +#endif - default: - mvOsPrintf("%s: eth_port=%d is out of range\n", __func__, eth_port); - return -1; - } -} -#else /* CONFIG_ARCH_ARMADA_XP */ int pnc_port_map(int pnc_port) { - switch (pnc_port) { - case 0: - case 1: - return 0; + int loop; - case 4: - return 1; + for (loop = 0; loop < PORT_BITS; loop++) + if (gbe_pnc_map[loop].pnc_port == pnc_port) + return gbe_pnc_map[loop].gbe_port; - case 2: - return 2; - - case 3: - return 3; - - default: mvOsPrintf("%s: pnc_port=%d is out of range\n", __func__, pnc_port); return -1; } -} int pnc_eth_port_map(int eth_port) { - switch (eth_port) { - case 0: - return 0; + int loop; - case 1: - return 4; + for (loop = 0; loop < PORT_BITS; loop++) + if (gbe_pnc_map[loop].gbe_port == eth_port) + return gbe_pnc_map[loop].pnc_port; - case 2: - return 2; - - case 3: - return 3; - - default: mvOsPrintf("%s: eth_port=%d is out of range\n", __func__, eth_port); return -1; } -} -#endif /* MV_ETH_PNC_NEW */ int pnc_te_del(unsigned int tid) { @@ -694,6 +708,7 @@ te = tcam_sw_alloc(TCAM_LU_L2); pnc_match_etype(te, MV_IP_TYPE); sram_sw_set_shift_update(te, 0, MV_ETH_TYPE_LEN); + sram_sw_set_rinfo(te, RI_L3_IP4, RI_L3_IP4); sram_sw_set_next_lookup(te, TCAM_LU_IP4); tcam_sw_text(te, "etype_ipv4"); @@ -709,6 +724,7 @@ te = tcam_sw_alloc(TCAM_LU_L2); pnc_match_etype(te, MV_IP6_TYPE); sram_sw_set_shift_update(te, 0, MV_ETH_TYPE_LEN); + sram_sw_set_rinfo(te, RI_L3_IP6, RI_L3_IP6); sram_sw_set_next_lookup(te, TCAM_LU_IP6); tcam_sw_text(te, "etype_ipv6"); @@ -729,7 +745,7 @@ sram_sw_set_shift_update(te, 0, MV_ETH_TYPE_LEN + MV_PPPOE_HDR_SIZE); sram_sw_set_next_lookup(te, TCAM_LU_IP4); - sram_sw_set_rinfo(te, RI_PPPOE, RI_PPPOE); + sram_sw_set_rinfo(te, (RI_PPPOE | RI_L3_IP4), (RI_PPPOE | RI_L3_IP4)); tcam_sw_text(te, "pppoe_ip4"); tcam_hw_write(te, TE_PPPOE_IP4); @@ -744,7 +760,7 @@ sram_sw_set_shift_update(te, 0, MV_ETH_TYPE_LEN + MV_PPPOE_HDR_SIZE); sram_sw_set_next_lookup(te, TCAM_LU_IP6); - sram_sw_set_rinfo(te, RI_PPPOE, RI_PPPOE); + sram_sw_set_rinfo(te, (RI_PPPOE | RI_L3_IP6), (RI_PPPOE | RI_L3_IP6)); tcam_sw_text(te, "pppoe_ip6"); tcam_hw_write(te, TE_PPPOE_IP6); @@ -917,7 +933,7 @@ tcam_sw_set_byte(te, 7, 0x00); tcam_sw_set_mask(te, 7, 0xFF); sram_sw_set_shift_update(te, 1, SHIFT_IP4_HLEN); - sram_sw_set_rinfo(te, (RI_L3_IP4 | RI_L4_TCP), (RI_L3_IP4 | RI_L4_TCP)); + sram_sw_set_rinfo(te, RI_L4_TCP, RI_L4_TCP); sram_sw_set_rxq(te, rxq_ip4_tcp, 0); sram_sw_set_ainfo(te, 0, AI_DONE_MASK); pnc_ip4_flow_next_lookup_set(te); @@ -931,10 +947,11 @@ te = tcam_sw_alloc(TCAM_LU_IP4); tcam_sw_set_byte(te, 9, MV_IP_PROTO_TCP); sram_sw_set_shift_update(te, 1, SHIFT_IP4_HLEN); - sram_sw_set_rinfo(te, (RI_L3_IP4_FRAG | RI_L4_TCP), (RI_L3_IP4_FRAG | RI_L4_TCP)); + sram_sw_set_rinfo(te, (RI_L3_IP4_FRAG | RI_L4_TCP), (RI_L3_IP4 | RI_L3_IP4_FRAG | RI_L4_TCP)); sram_sw_set_rxq(te, rxq_ip4_tcp, 0); sram_sw_set_ainfo(te, 0, AI_DONE_MASK); - pnc_ip4_flow_next_lookup_set(te); + sram_sw_set_lookup_done(te, 1); + sram_sw_set_flowid(te, FLOWID_EOF_LU_L4, FLOWID_CTRL_LOW_HALF_MASK); tcam_sw_text(te, "ipv4_tcp_fr"); tcam_hw_write(te, TE_IP4_TCP_FRAG); @@ -957,7 +974,7 @@ tcam_sw_set_byte(te, 7, 0x00); tcam_sw_set_mask(te, 7, 0xFF); sram_sw_set_shift_update(te, 1, SHIFT_IP4_HLEN); - sram_sw_set_rinfo(te, (RI_L3_IP4 | RI_L4_UDP), (RI_L3_IP4 | RI_L4_UDP)); + sram_sw_set_rinfo(te, RI_L4_UDP, RI_L4_UDP); sram_sw_set_rxq(te, rxq_ip4_udp, 0); sram_sw_set_ainfo(te, 0, AI_DONE_MASK); pnc_ip4_flow_next_lookup_set(te); @@ -970,10 +987,11 @@ te = tcam_sw_alloc(TCAM_LU_IP4); tcam_sw_set_byte(te, 9, MV_IP_PROTO_UDP); sram_sw_set_shift_update(te, 1, SHIFT_IP4_HLEN); - sram_sw_set_rinfo(te, (RI_L3_IP4_FRAG | RI_L4_UDP), (RI_L3_IP4_FRAG | RI_L4_UDP)); + sram_sw_set_rinfo(te, (RI_L3_IP4_FRAG | RI_L4_UDP), (RI_L3_IP4 | RI_L3_IP4_FRAG | RI_L4_UDP)); sram_sw_set_rxq(te, rxq_ip4_udp, 0); sram_sw_set_ainfo(te, 0, AI_DONE_MASK); - pnc_ip4_flow_next_lookup_set(te); + sram_sw_set_lookup_done(te, 1); + sram_sw_set_flowid(te, FLOWID_EOF_LU_L4, FLOWID_CTRL_LOW_HALF_MASK); tcam_sw_text(te, "ipv4_udp_fr"); tcam_hw_write(te, TE_IP4_UDP_FRAG); @@ -988,7 +1006,7 @@ PNC_DBG("%s\n", __func__); te = tcam_sw_alloc(TCAM_LU_IP4); - sram_sw_set_rinfo(te, (RI_L3_IP4 | RI_L4_UN), (RI_L3_IP4 | RI_L4_UN)); + sram_sw_set_rinfo(te, RI_L4_UN, RI_L4_UN); sram_sw_set_rxq(te, rxq_ip4, 0); sram_sw_set_lookup_done(te, 1); sram_sw_set_flowid(te, FLOWID_EOF_LU_IP4, FLOWID_CTRL_LOW_HALF_MASK); @@ -1037,11 +1055,14 @@ /* TCP without extension headers */ te = tcam_sw_alloc(TCAM_LU_IP6); tcam_sw_set_byte(te, 6, MV_IP_PROTO_TCP); - sram_sw_set_shift_update(te, 1, sizeof(MV_IP6_HEADER)); - pnc_ip6_flow_next_lookup_set(te); - sram_sw_set_rinfo(te, (RI_L3_IP6 | RI_L4_TCP), (RI_L3_IP6 | RI_L4_TCP)); + sram_sw_set_shift_update(te, 1, SHIFT_IP6_FIRST_PHASE); + sram_sw_set_next_lookup_shift(te, 1); + sram_sw_set_next_lookup(te, TCAM_LU_IP6); + sram_sw_set_rinfo(te, RI_L4_TCP, RI_L4_TCP); sram_sw_set_rxq(te, rxq_ip6, 0); tcam_sw_text(te, "ipv6_tcp"); + tcam_sw_set_ainfo(te, 0, (AI_IP6_L4_TCP_UDP_MASK | AI_IP6_L4_NOTHING_MASK)); + sram_sw_set_ainfo(te, AI_IP6_L4_TCP_UDP_MASK, AI_IP6_L4_TCP_UDP_MASK); tcam_hw_write(te, TE_IP6_TCP); tcam_sw_free(te); @@ -1057,11 +1078,14 @@ /* UDP without extension headers */ te = tcam_sw_alloc(TCAM_LU_IP6); tcam_sw_set_byte(te, 6, MV_IP_PROTO_UDP); - sram_sw_set_shift_update(te, 1, sizeof(MV_IP6_HEADER)); - pnc_ip6_flow_next_lookup_set(te); + sram_sw_set_shift_update(te, 1, SHIFT_IP6_FIRST_PHASE); + sram_sw_set_next_lookup_shift(te, 1); + sram_sw_set_next_lookup(te, TCAM_LU_IP6); - sram_sw_set_rinfo(te, (RI_L3_IP6 | RI_L4_UDP), (RI_L3_IP6 | RI_L4_UDP)); + sram_sw_set_rinfo(te, RI_L4_UDP, RI_L4_UDP); sram_sw_set_rxq(te, rxq_ip6, 0); + tcam_sw_set_ainfo(te, 0, (AI_IP6_L4_TCP_UDP_MASK | AI_IP6_L4_NOTHING_MASK)); + sram_sw_set_ainfo(te, AI_IP6_L4_TCP_UDP_MASK, AI_IP6_L4_TCP_UDP_MASK); tcam_sw_text(te, "ipv6_udp"); tcam_hw_write(te, TE_IP6_UDP); @@ -1068,7 +1092,7 @@ tcam_sw_free(te); } -/* IPv6 - end of section */ +/* IPv6 - second phase */ static void pnc_ip6_end(void) { struct tcam_entry *te; @@ -1075,15 +1099,46 @@ PNC_DBG("%s\n", __func__); + /* IPv6 - second phase, TCP/UDP part */ te = tcam_sw_alloc(TCAM_LU_IP6); - sram_sw_set_shift_update(te, 1, sizeof(MV_IP6_HEADER)); - sram_sw_set_rinfo(te, (RI_L3_IP6 | RI_L4_UN), (RI_L3_IP6 | RI_L4_UN)); + sram_sw_set_shift_update(te, 1, SHIFT_IP6_SECOND_PHASE); + sram_sw_set_next_lookup_shift(te, 1); + tcam_sw_text(te, "ipv6_2nd_tcp_udp"); + tcam_sw_set_ainfo(te, AI_IP6_L4_TCP_UDP_MASK, AI_IP6_L4_TCP_UDP_MASK); + pnc_ip6_flow_next_lookup_set(te); + + tcam_hw_write(te, TE_IP6_2ND_PHASE_TCP_UDP); + tcam_sw_free(te); + + /* IPv6 - second phase, unknown L4 part */ + te = tcam_sw_alloc(TCAM_LU_IP6); + sram_sw_set_shift_update(te, 1, SHIFT_IP6_SECOND_PHASE); + sram_sw_set_next_lookup_shift(te, 1); + tcam_sw_text(te, "ipv6_2nd_unknown_l4"); + tcam_sw_set_ainfo(te, AI_IP6_L4_NOTHING_MASK, AI_IP6_L4_NOTHING_MASK); + pnc_ip6_flow_next_lookup_set(te); + + tcam_hw_write(te, TE_IP6_2ND_PHASE_UNKNOWN_L4); + tcam_sw_free(te); +} +static void pnc_ip6_unknown_l4(void) +{ + struct tcam_entry *te; + + PNC_DBG("%s\n", __func__); + + te = tcam_sw_alloc(TCAM_LU_IP6); + sram_sw_set_shift_update(te, 1, SHIFT_IP6_FIRST_PHASE); + sram_sw_set_next_lookup_shift(te, 1); + sram_sw_set_rinfo(te, RI_L4_UN, RI_L4_UN); sram_sw_set_rxq(te, rxq_ip6, 0); - sram_sw_set_lookup_done(te, 1); + sram_sw_set_next_lookup(te, TCAM_LU_IP6); sram_sw_set_flowid(te, FLOWID_EOF_LU_IP6, FLOWID_CTRL_LOW_HALF_MASK); - tcam_sw_text(te, "ipv6_eof"); + tcam_sw_text(te, "ipv6_unknown_l4"); + tcam_sw_set_ainfo(te, 0, (AI_IP6_L4_TCP_UDP_MASK | AI_IP6_L4_NOTHING_MASK)); + sram_sw_set_ainfo(te, AI_IP6_L4_NOTHING_MASK, AI_IP6_L4_NOTHING_MASK); - tcam_hw_write(te, TE_IP6_EOF); + tcam_hw_write(te, TE_IP6_UNKNOWN_L4); tcam_sw_free(te); } @@ -1093,6 +1148,7 @@ pnc_ip6_tcp(); pnc_ip6_udp(); + pnc_ip6_unknown_l4(); pnc_ip6_end(); Index: drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPnc.h =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPnc.h (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPnc.h (working copy) @@ -119,6 +119,12 @@ #define AI_DONE_BIT 0 #define AI_DONE_MASK (1 << AI_DONE_BIT) +#define AI_IP6_L4_TCP_UDP_BIT 1 +#define AI_IP6_L4_TCP_UDP_MASK (1 << AI_IP6_L4_TCP_UDP_BIT) + +#define AI_IP6_L4_NOTHING_BIT 2 +#define AI_IP6_L4_NOTHING_MASK (1 << AI_IP6_L4_NOTHING_BIT) + /* PnC result info */ #define NETA_PNC_VLAN (RI_VLAN >> 9) #define NETA_PNC_DA_MC (RI_DA_MC >> 9) @@ -129,7 +135,7 @@ /*---------------------------------------------------------------------------*/ -MV_STATUS mvPncInit(MV_U8 *pncVirtBase); +MV_STATUS mvPncInit(MV_U8 *pncVirtBase, MV_U32 pncTcamSize); #ifdef CONFIG_MV_ETH_PNC /* @@ -182,7 +188,9 @@ /* IP6 Lookup */ TE_IP6_TCP, TE_IP6_UDP, - TE_IP6_EOF, + TE_IP6_UNKNOWN_L4, + TE_IP6_2ND_PHASE_TCP_UDP, + TE_IP6_2ND_PHASE_UNKNOWN_L4, #ifdef CONFIG_MV_ETH_PNC_L3_FLOW /* Session Lookup for IPv4 and IPv6 */ @@ -197,7 +205,7 @@ #ifdef CONFIG_MV_ETH_PNC_WOL TE_WOL, - TE_WOL_EOF = CONFIG_MV_PNC_TCAM_LINES - 1, + /*TE_WOL_EOF is always the last line of the TCAM table, it is dynamic, redefined it to macro */ #endif /* CONFIG_MV_ETH_PNC_WOL */ }; @@ -220,6 +228,11 @@ #endif /* CONFIG_MV_ETH_PNC_WOL */ }; +struct gbe_pnc_port_mapping { + int gbe_port; + int pnc_port; +}; + /* * Pre-defined FlowId assigment */ @@ -260,6 +273,7 @@ int pnc_te_del(unsigned int tid); struct tcam_entry *pnc_tcam_entry_get(int tid); +int pnc_gbe_port_map_init(unsigned int ctrl_model, unsigned int ctrl_rev); int pnc_eth_port_map(int eth_port); int pnc_port_map(int pnc_port); @@ -359,6 +373,7 @@ #ifdef MV_ETH_PNC_LB void mvPncLbDump(void); int mvPncLbRxqSet(int hash, int rxq); +int mvPncLbFirstFragL4(int en); int mvPncLbModeIp4(int mode); int mvPncLbModeIp6(int mode); int mvPncLbModeL4(int mode); Index: drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncAging.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncAging.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncAging.c (working copy) @@ -106,14 +106,11 @@ #define PNC_AGING_LOG_VALID_BIT 31 #define PNC_AGING_LOG_VALID_MASK (1 << PNC_AGING_LOG_VALID_BIT) -extern char tcam_text[CONFIG_MV_PNC_TCAM_LINES][TCAM_TEXT]; -extern MV_U8 *mvPncVirtBase; - void mvPncAgingCntrWrite(int tid, MV_U32 w32) { MV_U32 va; - WARN_ON_OOR(tid >= CONFIG_MV_PNC_TCAM_LINES); + WARN_ON_OOR(tid >= MV_PNC_TCAM_SIZE()); va = (MV_U32)mvPncVirtBase; va |= PNC_AGING_ACCESS_MASK; @@ -131,7 +128,7 @@ { MV_U32 va, w32; - ERR_ON_OOR(tid >= CONFIG_MV_PNC_TCAM_LINES); + ERR_ON_OOR(tid >= MV_PNC_TCAM_SIZE()); va = (MV_U32)mvPncVirtBase; va |= PNC_AGING_ACCESS_MASK; @@ -216,7 +213,7 @@ MV_U32 cntrVal; mvOsPrintf("TCAM entries Aging counters: %s\n", all ? "ALL" : "Non ZERO"); - for (tid = 0; tid < CONFIG_MV_PNC_TCAM_LINES; tid++) { + for (tid = 0; tid < MV_PNC_TCAM_SIZE(); tid++) { cntrVal = mvPncAgingCntrRead(tid); if (all || (cntrVal & PNC_AGING_CNTR_MASK)) @@ -227,8 +224,8 @@ mvOsPrintf("group #%d: %10u\n", gr, mvPncAgingGroupCntrRead(gr)); } -static MV_U32 mvPncScannerLog[CONFIG_MV_PNC_TCAM_LINES]; -static MV_U32 mvPncAgingCntrs[CONFIG_MV_PNC_TCAM_LINES]; +static MV_U32 mvPncScannerLog[MV_PNC_TCAM_LINES]; +static MV_U32 mvPncAgingCntrs[MV_PNC_TCAM_LINES]; void mvPncAgingScannerDump(void) { @@ -239,7 +236,7 @@ for (gr = 0; gr < MV_PNC_AGING_MAX_GROUP; gr++) { i = 0; mvOsPrintf("LU group #%d:\n", gr); - while (i < CONFIG_MV_PNC_TCAM_LINES) { + while (i < MV_PNC_TCAM_SIZE()) { w32 = mvPncAgingLogEntryRead(gr, 0); if ((w32 & PNC_AGING_LOG_VALID_MASK) == 0) break; @@ -262,7 +259,7 @@ for (gr = 0; gr < MV_PNC_AGING_MAX_GROUP; gr++) { i = 0; mvOsPrintf("MU group #%d:\n", gr); - while (i < CONFIG_MV_PNC_TCAM_LINES) { + while (i < MV_PNC_TCAM_SIZE()) { w32 = mvPncAgingLogEntryRead(gr, 1); /*mvOsDelay(1);*/ if ((w32 & PNC_AGING_LOG_VALID_MASK) == 0) @@ -309,7 +306,7 @@ { int tid, gr; - for (tid = 0; tid < CONFIG_MV_PNC_TCAM_LINES; tid++) + for (tid = 0; tid < MV_PNC_TCAM_SIZE(); tid++) mvPncAgingCntrClear(tid); for (gr = 0; gr < MV_PNC_AGING_MAX_GROUP; gr++) Index: drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncLb.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncLb.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncLb.c (working copy) @@ -35,6 +35,12 @@ #include "mvTcam.h" #ifdef MV_ETH_PNC_LB +int pnc_lb_first_frag_l4; +int mvPncLbFirstFragL4(int en) +{ + pnc_lb_first_frag_l4 = en; + return 0; +} void mvPncLbDump(void) { @@ -77,11 +83,17 @@ return 0; } +void mvPncLbModeSet(int pnc_entry_num, int lb) +{ + struct tcam_entry te; + tcam_hw_read(&te, pnc_entry_num); + sram_sw_set_load_balance(&te, lb); + tcam_hw_write(&te, pnc_entry_num); +} int mvPncLbModeIp4(int mode) { int lb; - struct tcam_entry te; switch (mode) { case 0: @@ -95,9 +107,11 @@ mvOsPrintf("%s: %d - unexpected mode value\n", __func__, mode); return 1; } - tcam_hw_read(&te, TE_IP4_EOF); - sram_sw_set_load_balance(&te, lb); - tcam_hw_write(&te, TE_IP4_EOF); + mvPncLbModeSet(TE_IP4_TCP, lb); + mvPncLbModeSet(TE_IP4_UDP, lb); + mvPncLbModeSet(TE_IP4_TCP_FRAG, lb); + mvPncLbModeSet(TE_IP4_UDP_FRAG, lb); + mvPncLbModeSet(TE_IP4_EOF, lb); return 0; } @@ -105,7 +119,6 @@ int mvPncLbModeIp6(int mode) { int lb; - struct tcam_entry te; switch (mode) { case 0: @@ -119,9 +132,11 @@ mvOsPrintf("%s: %d - unexpected mode value\n", __func__, mode); return 1; } - tcam_hw_read(&te, TE_IP6_EOF); - sram_sw_set_load_balance(&te, lb); - tcam_hw_write(&te, TE_IP6_EOF); + mvPncLbModeSet(TE_IP6_TCP, lb); + mvPncLbModeSet(TE_IP6_UDP, lb); + mvPncLbModeSet(TE_IP6_UNKNOWN_L4, lb); + mvPncLbModeSet(TE_IP6_2ND_PHASE_TCP_UDP, lb); + mvPncLbModeSet(TE_IP6_2ND_PHASE_UNKNOWN_L4, lb); return 0; } @@ -129,7 +144,6 @@ int mvPncLbModeL4(int mode) { int lb; - struct tcam_entry te; switch (mode) { case 0: @@ -150,9 +164,18 @@ mvOsPrintf("%s: Not supported\n", __func__); return 1; #else - tcam_hw_read(&te, TE_L4_EOF); - sram_sw_set_load_balance(&te, lb); - tcam_hw_write(&te, TE_L4_EOF); + /* IP4 */ + mvPncLbModeSet(TE_IP4_TCP, lb); + mvPncLbModeSet(TE_IP4_UDP, lb); + /* IP6 */ + mvPncLbModeSet(TE_IP6_TCP, lb); + mvPncLbModeSet(TE_IP6_UDP, lb); + mvPncLbModeSet(TE_IP6_2ND_PHASE_TCP_UDP, lb); + + if (pnc_lb_first_frag_l4) { + mvPncLbModeSet(TE_IP4_TCP_FRAG, lb); + mvPncLbModeSet(TE_IP4_UDP_FRAG, lb); + } return 0; #endif /* CONFIG_MV_ETH_PNC_L3_FLOW */ } Index: drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncRxq.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncRxq.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncRxq.c (working copy) @@ -65,7 +65,9 @@ #include "mvOs.h" #include "mvCommon.h" #include "mv802_3.h" +#ifndef CONFIG_OF #include "ctrlEnv/mvCtrlEnvLib.h" +#endif #include "gbe/mvNetaRegs.h" #include "gbe/mvEthRegs.h" Index: drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncWol.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncWol.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvPncWol.c (working copy) @@ -65,7 +65,9 @@ #include "mvOs.h" #include "mvCommon.h" #include "mv802_3.h" +#ifndef CONFIG_OF #include "ctrlEnv/mvCtrlEnvLib.h" +#endif #include "gbe/mvNeta.h" @@ -289,7 +291,7 @@ break; /* Set free TCAM entry */ - for (; tid < TE_WOL_EOF; tid++) { + for (; tid < (MV_PNC_TCAM_SIZE() - 1); tid++) { te = pnc_tcam_entry_get(tid); if (te != NULL) { @@ -381,7 +383,7 @@ } } /* Set free TCAM entry */ - for (i = TE_WOL; i < TE_WOL_EOF; i++) + for (i = TE_WOL; i < (MV_PNC_TCAM_SIZE() - 1); i++) pnc_te_del(i); return 0; Index: drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvTcam.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvTcam.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvTcam.c (working copy) @@ -64,11 +64,10 @@ #include "mvOs.h" #include "mvCommon.h" -#include "ctrlEnv/mvCtrlEnvLib.h" +#include "gbe/mvNetaRegs.h" #include "mvPnc.h" #include "mvTcam.h" -#include "gbe/mvNetaRegs.h" #define DWORD_LEN 32 @@ -86,15 +85,18 @@ /* * Keep short text per entry */ -char tcam_text[CONFIG_MV_PNC_TCAM_LINES][TCAM_TEXT]; +char tcam_text[MV_PNC_TCAM_LINES][TCAM_TEXT]; MV_U8 *mvPncVirtBase = NULL; -MV_STATUS mvPncInit(MV_U8 *pncVirtBase) +unsigned int tcam_line_num; + +MV_STATUS mvPncInit(MV_U8 *pncVirtBase, MV_U32 pncTcamSize) { mvPncVirtBase = pncVirtBase; + tcam_line_num = pncTcamSize; - mvOsPrintf("mvPncVirtBase = 0x%p\n", pncVirtBase); + mvOsPrintf("mvPncVirtBase = 0x%p, pncTcamSize = %d\n", pncVirtBase, pncTcamSize); return MV_OK; } @@ -642,7 +644,7 @@ { MV_U32 va; - WARN_ON_OOR(tid >= CONFIG_MV_PNC_TCAM_LINES); + WARN_ON_OOR(tid >= MV_PNC_TCAM_SIZE()); va = (MV_U32) mvPncVirtBase; va |= PNC_TCAM_ACCESS_MASK; va |= (tid << TCAM_LINE_INDEX_OFFS); @@ -655,7 +657,7 @@ void tcam_hw_inv_all(void) { MV_U32 va; - int tid = CONFIG_MV_PNC_TCAM_LINES; + int tid = MV_PNC_TCAM_SIZE(); while (tid--) { va = (MV_U32) mvPncVirtBase; @@ -677,7 +679,7 @@ MV_U32 i, va, w32; TCAM_DBG("%s: tid=0x%x\n", __func__, tid); - ERR_ON_OOR(tid >= CONFIG_MV_PNC_TCAM_LINES); + ERR_ON_OOR(tid >= MV_PNC_TCAM_SIZE()); /* sram */ for (i = 0; i < SRAM_LEN; i++) { @@ -753,7 +755,7 @@ MV_U32 i, va, w32; TCAM_DBG("%s: tid=0x%x\n", __func__, tid); - ERR_ON_OOR(tid >= CONFIG_MV_PNC_TCAM_LINES); + ERR_ON_OOR(tid >= MV_PNC_TCAM_SIZE()); te->ctrl.index = tid; @@ -868,7 +870,7 @@ struct tcam_entry te; char buff[1024]; - for (i = 0; i < CONFIG_MV_PNC_TCAM_LINES; i++) { + for (i = 0; i < MV_PNC_TCAM_SIZE(); i++) { tcam_sw_clear(&te); tcam_hw_read(&te, i); if (!all && (te.ctrl.flags & TCAM_F_INV)) @@ -892,14 +894,16 @@ MV_U32 regVal; struct tcam_entry te; -#if (CONFIG_MV_PNC_TCAM_LINES > MV_PNC_TCAM_LINES) -#error "CONFIG_MV_PNC_TCAM_LINES must be less or equal than MV_PNC_TCAM_LINES" -#endif + /* Check TCAM size */ + if (MV_PNC_TCAM_SIZE() > MV_PNC_TCAM_LINES) { + mvOsPrintf("MV_PNC_TCAM_SIZE()-%d must be less or equal than MV_PNC_TCAM_LINES\n", MV_PNC_TCAM_SIZE()); + return -1; + } - /* Power on TCAM arrays accordingly with CONFIG_MV_PNC_TCAM_LINES */ + /* Power on TCAM arrays accordingly with MV_PNC_TCAM_SIZE() */ regVal = MV_REG_READ(MV_PNC_TCAM_CTRL_REG); for (i = 0; i < (MV_PNC_TCAM_LINES / MV_PNC_TCAM_ARRAY_SIZE); i++) { - if ((i * MV_PNC_TCAM_ARRAY_SIZE) < CONFIG_MV_PNC_TCAM_LINES) + if ((i * MV_PNC_TCAM_ARRAY_SIZE) < MV_PNC_TCAM_SIZE()) regVal |= MV_PNC_TCAM_POWER_UP(i); /* Power ON */ else regVal &= ~MV_PNC_TCAM_POWER_UP(i); /* Power OFF */ @@ -912,7 +916,7 @@ /* Perform full write */ tcam_ctl_flags = TCAM_F_WRITE; - for (i = 0; i < CONFIG_MV_PNC_TCAM_LINES; i++) { + for (i = 0; i < MV_PNC_TCAM_SIZE(); i++) { sram_sw_set_flowid(&te, i, FLOW_CTRL_MASK); tcam_sw_text(&te, "empty"); tcam_hw_write(&te, i); Index: drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvTcam.h =================================================================== --- drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvTcam.h (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/hal/pnc/mvTcam.h (working copy) @@ -67,6 +67,17 @@ /************************** NETA PNC Registers ******************************/ +#ifdef CONFIG_OF +extern int pnc_reg_vbase; +#define MV_PNC_REG_BASE (pnc_reg_vbase) +#endif +extern unsigned int tcam_line_num; +#define MV_PNC_TCAM_SIZE() (tcam_line_num) +#ifdef CONFIG_MV_ETH_PNC_WOL +#define TE_WOL_EOF (tcam_line_num - 1) +#endif +/*-------------------------------------------------------------------------------*/ + #define MV_PNC_LOOP_CTRL_REG (MV_PNC_REG_BASE + 0x00) #define MV_PNC_TCAM_CTRL_REG (MV_PNC_REG_BASE + 0x04) @@ -267,6 +278,9 @@ #define SHIFT_IP4_HLEN 126 /* IPv4 dynamic shift index */ #define SHIFT_IP6_HLEN 127 /* IPv6 dynamic shift index */ +#define SHIFT_IP6_FIRST_PHASE 24 /* IPv6 first phase shift, to where DIP starts */ +#define SHIFT_IP6_SECOND_PHASE 16 /* IPv6 second phase shift, to L4 offset */ + /* * TCAM misc/control */ @@ -319,6 +333,12 @@ struct tcam_ctrl ctrl; } __attribute__((packed)); +#ifdef CONFIG_MV_ETH_PNC +/* PnC Global variale */ +extern char tcam_text[MV_PNC_TCAM_LINES][TCAM_TEXT]; +extern MV_U8 *mvPncVirtBase; +#endif + /* * TCAM Low Level API */ Index: drivers/net/ethernet/mvebu_net/neta/Kconfig =================================================================== --- drivers/net/ethernet/mvebu_net/neta/Kconfig (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/Kconfig (working copy) @@ -1,3 +1,14 @@ +config MV_ETH_NETA + tristate "Marvell NETA network interface support" + depends on ARCH_FEROCEON_KW2 || ARCH_ARMADA_XP || ARCH_ARMADA370 || ARCH_ARMADA38X || ARCH_MSYS || MACH_ARMADA_380 + default y + ---help--- + This driver supports the network interface + units in the following Marvell MSYS SoC family + 1. ARMADA-370. + 2. ARMADA-XP. + 3. ARMADA-38x. + config MV_ETH_PORTS_NUM int "Number of Marvell GbE ports" depends on MV_ETH_NETA @@ -6,13 +17,12 @@ Number of Marvell GbE ports supported by NETA driver menu "BM configuration" + depends on MV_ETH_NETA config MV_ETH_BM - depends on MV_ETH_NETA && (ARCH_FEROCEON_KW2 || ARCH_ARMADA_XP || ARCH_ARMADA38X) + depends on MV_ETH_NETA && (ARCH_FEROCEON_KW2 || ARCH_ARMADA_XP || ARCH_ARMADA38X || ARCH_MVEBU) bool "Buffer Management support (BM)" - default y if ARCH_FEROCEON_KW2 - default n if ARCH_ARMADA_XP - default n if ARCH_ARMADA38X + default y ---help--- Enable/Disable hardware buffer management support for NETA driver. if 'y' - BM support is enabled for Hardware Forwarding @@ -224,25 +234,16 @@ endmenu -config MV_ETH_LEGACY_PARSER - depends on !MV_ETH_PNC - bool "Use legacy parser for incoming traffic" - default y - ---help--- - menuconfig MV_ETH_PNC - depends on MV_ETH_NETA && (ARCH_FEROCEON_KW2 || ARCH_ARMADA_XP) + depends on MV_ETH_NETA && (ARCH_FEROCEON_KW2 || ARCH_ARMADA_XP || ARCH_MVEBU) bool "PnC support" default y ---help--- + PnC module is used for parse of incoming traffic. + If PnC is involved, incoming traffic is parsed by PnC, legacy mode is bypass. + On LK-3.10 legacy and pnc co-exist, user can select it in FDT. + On other LK version, it depends on corresponding SoC and config file. -config MV_PNC_TCAM_LINES - depends on MV_ETH_PNC - int "Number of TCAM lines supported by PNC" - default 512 if ARCH_FEROCEON_KW2 - default 1024 if ARCH_ARMADA_XP - ---help--- - config MV_ETH_PNC_MCAST_NUM depends on MV_ETH_PNC int "Use PnC for Multicast MAC addresses filtering" @@ -341,6 +342,7 @@ MV_ETH_PMT_SIZE >= (MV_ETH_PMT_FLOWS * (MV_ETH_PMT_CMD_PER_FLOW + 1)) menu "Network Interface configuration" + depends on MV_ETH_NETA config MV_ETH_0_MTU int "Giga port #0 MTU value" @@ -401,6 +403,7 @@ endmenu menu "Rx/Tx Queue configuration" + depends on MV_ETH_NETA config MV_ETH_RXQ int "Number of RX queues" @@ -441,6 +444,7 @@ endmenu menu "IP/TCP/UDP Offloading" + depends on MV_ETH_NETA config MV_ETH_TX_CSUM_OFFLOAD bool "L3/L4 TX checksum offload support for Marvell network interface" @@ -497,6 +501,7 @@ endmenu menu "Control and Statistics" + depends on MV_ETH_NETA config MV_NETA_DEBUG_CODE depends on MV_ETH_DEBUG_CODE @@ -543,16 +548,17 @@ endmenu menu "Advanced Features" + depends on MV_ETH_NETA config MV_NETA_SKB_RECYCLE depends on NET_SKB_RECYCLE - bool "NETA Skb recycle" + bool "NETA Skb recycle only for forwarding cases" default y ---help--- Work-in-progress and experimental. - This option enables skb's to be returned via a callback at kfree to - the allocator to make a fastpath for very skb consuming network + This option enables skb's to be recycled when packet tx done, which + makes a fastpath for routine and bridge skb consuming network applications. config MV_NETA_SKB_RECYCLE_DEF @@ -561,25 +567,60 @@ default 1 ---help--- -config MV_ETH_TX_DONE_TIMER_PERIOD - int "Periodical Tx Done timer period" - default 10 - ---help--- - Periodical timer period for Tx Done operation in [msec]. +config MV_NETA_TXDONE_PROCESS_METHOD + bool "TX_DONE event process method" + default y + help + It's used for choosing TX_DONE event process method + MV_NETA_TXDONE_ISR means processing TX_DONE event in interrupt mode + MV_NETA_TXDONE_IN_TIMER means using regular timer to process TX_DONE event in polling mode + MV_NETA_TXDONE_IN_HRTIMER means using high-resolution timer to process TX_DONE event in polling mode -config MV_ETH_CLEANUP_TIMER_PERIOD - int "Periodical Cleanup timer period" - default 10 - ---help--- - Periodical timer period for cleanup operation in [msec]. +choice + prompt "TX_DONE event process method" + depends on MV_NETA_TXDONE_PROCESS_METHOD + default MV_NETA_TXDONE_IN_HRTIMER -config MV_ETH_TXDONE_ISR + config MV_NETA_TXDONE_ISR bool "Use interrupt to process TX_DONE event" - default n ---help--- When chosen TX_DONE event will be process in interrupt mode When unchosen TX_DONE event will be processed in polling mode + config MV_NETA_TXDONE_IN_TIMER + bool "Use regular timer to process TX_DONE event" + ---help--- + When chosen TX_DONE event will be process by regular timer in polling mode. + + config MV_NETA_TXDONE_IN_HRTIMER + depends on HIGH_RES_TIMERS + bool "Use high resolution timer to process TX_DONE event" + ---help--- + When chosen TX_DONE event will be process by high resolution timer in polling mode. + High resolution timer can support higher precision in ns level. + If high resolution timer is enabled, TX processing + can free SKB memory much faster. + +endchoice + +config MV_NETA_TX_DONE_HIGH_RES_TIMER_PERIOD + depends on MV_NETA_TXDONE_IN_HRTIMER + int "Periodical Tx Done high resolution timer period" + default 1000 + range 10 10000 + ---help--- + Periodical high resolution timer period for Tx Done operation in [usec] + Its precision is much higher than regular timer whose higest precision is 10 msec + Tx done high resolution timer's higest precison is 10 usec + Must be larger than or equal to 10 and be smaller than or equal to 10000. + +config MV_NETA_TX_DONE_TIMER_PERIOD + depends on MV_NETA_TXDONE_IN_TIMER + int "Periodical Tx Done timer period" + default 10 + ---help--- + Periodical timer period for Tx Done operation in [msec]. + config MV_ETH_TXDONE_COAL_PKTS int "Threshold for TX_DONE event trigger" default 16 @@ -669,47 +710,8 @@ Number of extra buffers allocated for each port endmenu -menu "Network Fast Processing (NFP) support" - -config MV_ETH_NFP +menuconfig MV_ETH_NAPI depends on MV_ETH_NETA - bool "NFP support" - default n - ---help--- - Choosing this option will enable Network Fast Processing support. - Kernel image will be able to use NFP modules. - NFP provided as different package and must be compiled separately. - NFP support include two modules: - NFP core functionality and NFP dynamic learning. - -config MV_ETH_NFP_HOOKS - bool "NFP IP stack Hooks" - depends on MV_ETH_NFP - default y - ---help--- - Choosing this option will enable NFP Dynamic Learning. - Marvell specific code was added to few files - in Linux Network Stack. Without this configration option only - static NFP configuration is enabled. - -config MV_ETH_NFP_EXT - bool "Support NFP for External (non GBE) network interfaces" - depends on MV_ETH_NFP - default n - ---help--- - Enable NFP support for External (non GBE) network interfaces. - It doesn't require special changes in external network drivers, - but NFP can be used only for NAPI capable drivers. - Leave default if unsure. - -config MV_ETH_NFP_EXT_NUM - depends on MV_ETH_NFP_EXT - int "Maximum number of External (non-Gbe) interfaces" - default 1 - range 1 4 -endmenu - -menuconfig MV_ETH_NAPI bool "NAPI configuration" default y ---help--- @@ -799,6 +801,7 @@ endmenu menu "PON support for Network driver" + depends on MV_ETH_NETA config MV_PON bool "PON support" @@ -821,6 +824,7 @@ endmenu menu "ERRATA / WA" + depends on MV_ETH_NETA config MV_ETH_ERRATA_SMI_ACCESS bool "use SMI port 1 instead of SMI port 0" Index: drivers/net/ethernet/mvebu_net/neta/l2fw/mv_eth_l2fw.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/l2fw/mv_eth_l2fw.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/l2fw/mv_eth_l2fw.c (working copy) @@ -55,7 +55,7 @@ static int numHashEntries; -struct eth_pbuf *mv_eth_pool_get(struct bm_pool *pool); +struct sk_buff *mv_eth_pool_get(struct bm_pool *pool); static int mv_eth_ports_l2fw_num; @@ -70,8 +70,8 @@ struct eth_port_l2fw **mv_eth_ports_l2fw; static inline int mv_eth_l2fw_rx(struct eth_port *pp, int rx_todo, int rxq); -static inline MV_STATUS mv_eth_l2fw_tx(struct eth_pbuf *pkt, struct eth_port *pp, - int withXor, struct neta_rx_desc *rx_desc); +static inline MV_STATUS mv_eth_l2fw_tx(struct sk_buff *skb, struct eth_port *pp, int withXor, + struct neta_rx_desc *rx_desc); static L2FW_RULE *l2fw_lookup(MV_U32 srcIP, MV_U32 dstIP) @@ -261,7 +261,7 @@ } causeRxTx |= pp->cpu_config[smp_processor_id()]->causeRxTx; -#ifdef CONFIG_MV_ETH_TXDONE_ISR +#ifdef CONFIG_MV_NETA_TXDONE_ISR if (causeRxTx & MV_ETH_TXDONE_INTR_MASK) { /* TX_DONE process */ @@ -270,7 +270,7 @@ causeRxTx &= ~MV_ETH_TXDONE_INTR_MASK; } -#endif /* CONFIG_MV_ETH_TXDONE_ISR */ +#endif /* CONFIG_MV_NETA_TXDONE_ISR */ #if (CONFIG_MV_ETH_RXQ > 1) while ((causeRxTx != 0) && (budget > 0)) { @@ -378,12 +378,12 @@ } -static inline struct eth_pbuf *l2fw_swap_mac(struct eth_pbuf *pRxPktInfo) +inline unsigned char *l2fw_swap_mac(unsigned char *buff) { MV_U16 *pSrc; int i; MV_U16 swap; - pSrc = (MV_U16 *)(pRxPktInfo->pBuf + pRxPktInfo->offset + MV_ETH_MH_SIZE); + pSrc = (MV_U16 *)(buff + MV_ETH_MH_SIZE); for (i = 0; i < 3; i++) { swap = pSrc[i]; @@ -391,11 +391,10 @@ pSrc[i+3] = swap; } - return pRxPktInfo; + return buff; } -static inline void l2fw_copy_mac(struct eth_pbuf *pRxPktInfo, - struct eth_pbuf *pTxPktInfo) +inline void l2fw_copy_mac(unsigned char *rx_buff, unsigned char *tx_buff) { /* copy 30 bytes (start after MH header) */ /* 12 for SA + DA */ @@ -403,8 +402,8 @@ MV_U16 *pSrc; MV_U16 *pDst; int i; - pSrc = (MV_U16 *)(pRxPktInfo->pBuf + pRxPktInfo->offset + MV_ETH_MH_SIZE); - pDst = (MV_U16 *)(pTxPktInfo->pBuf + pTxPktInfo->offset + MV_ETH_MH_SIZE); + pSrc = (MV_U16 *)(rx_buff); + pDst = (MV_U16 *)(tx_buff); /* swap mac SA and DA */ for (i = 0; i < 3; i++) { @@ -415,14 +414,14 @@ pDst[i] = pSrc[i]; } -static inline void l2fw_copy_and_swap_mac(struct eth_pbuf *pRxPktInfo, struct eth_pbuf *pTxPktInfo) +inline void l2fw_copy_and_swap_mac(unsigned char *rx_buff, unsigned char *tx_buff) { MV_U16 *pSrc; MV_U16 *pDst; int i; - pSrc = (MV_U16 *)(pRxPktInfo->pBuf + pRxPktInfo->offset + MV_ETH_MH_SIZE); - pDst = (MV_U16 *)(pTxPktInfo->pBuf + pTxPktInfo->offset + MV_ETH_MH_SIZE); + pSrc = (MV_U16 *)(rx_buff); + pDst = (MV_U16 *)(tx_buff); for (i = 0; i < 3; i++) { pDst[i] = pSrc[i+3]; pDst[i+3] = pSrc[i]; @@ -430,44 +429,49 @@ } static inline -struct eth_pbuf *eth_l2fw_copy_packet_withoutXor(struct eth_pbuf *pRxPktInfo) +struct sk_buff *eth_l2fw_copy_packet_withoutXor(struct sk_buff *skb, struct neta_rx_desc *rx_desc) { MV_U8 *pSrc; MV_U8 *pDst; struct bm_pool *pool; - struct eth_pbuf *pTxPktInfo; + struct sk_buff *skb_new; + int bytes = rx_desc->dataSize - MV_ETH_MH_SIZE; + int pool_id = NETA_RX_GET_BPID(rx_desc); - mvOsCacheInvalidate(NULL, pRxPktInfo->pBuf + pRxPktInfo->offset, - pRxPktInfo->bytes); + mvOsCacheInvalidate(NULL, skb->data, bytes); - pool = &mv_eth_pool[pRxPktInfo->pool]; - pTxPktInfo = mv_eth_pool_get(pool); - if (pTxPktInfo == NULL) { - mvOsPrintf("pTxPktInfo == NULL in %s\n", __func__); + pool = &mv_eth_pool[pool_id]; + skb_new = mv_eth_pool_get(pool); + if (!skb_new) { + mvOsPrintf("skb == NULL in %s\n", __func__); return NULL; } - pSrc = pRxPktInfo->pBuf + pRxPktInfo->offset + MV_ETH_MH_SIZE; - pDst = pTxPktInfo->pBuf + pTxPktInfo->offset + MV_ETH_MH_SIZE; + pSrc = skb->data + MV_ETH_MH_SIZE; + pDst = skb_new->data + MV_ETH_MH_SIZE; - memcpy(pDst+12, pSrc+12, pRxPktInfo->bytes-12); - l2fw_copy_and_swap_mac(pRxPktInfo, pTxPktInfo); - pTxPktInfo->bytes = pRxPktInfo->bytes; - mvOsCacheFlush(NULL, pTxPktInfo->pBuf + pTxPktInfo->offset, pTxPktInfo->bytes); + memcpy(pDst+12, pSrc+12, bytes - 12); + l2fw_copy_and_swap_mac(pSrc, pDst); + mvOsCacheFlush(NULL, skb->data, bytes); - return pTxPktInfo; + return skb_new; } #ifdef CONFIG_MV_INCLUDE_XOR static inline -struct eth_pbuf *eth_l2fw_copy_packet_withXor(struct eth_pbuf *pRxPktInfo) +struct sk_buff *eth_l2fw_copy_packet_withXor(struct sk_buff *skb, struct neta_rx_desc *rx_desc) { + struct sk_buff *skb_new = NULL; struct bm_pool *pool; - struct eth_pbuf *pTxPktInfo; + unsigned int bufPhysAddr; + MV_U8 *pSrc; + MV_U8 *pDst; + int pool_id = NETA_RX_GET_BPID(rx_desc); + int bytes = rx_desc->dataSize - MV_ETH_MH_SIZE; - pool = &mv_eth_pool[pRxPktInfo->pool]; - pTxPktInfo = mv_eth_pool_get(pool); - if (pTxPktInfo == NULL) { - mvOsPrintf("pTxPktInfo == NULL in %s\n", __func__); + pool = &mv_eth_pool[pool_id]; + skb_new = mv_eth_pool_get(pool); + if (!skb_new) { + mvOsPrintf("skb == NULL in %s\n", __func__); return NULL; } @@ -476,10 +480,12 @@ mvOsCacheIoSync(NULL); - eth_xor_desc->srcAdd0 = pRxPktInfo->physAddr + pRxPktInfo->offset + MV_ETH_MH_SIZE + 30; - eth_xor_desc->phyDestAdd = pTxPktInfo->physAddr + pTxPktInfo->offset + MV_ETH_MH_SIZE + 30; + bufPhysAddr = virt_to_phys(skb->data); + eth_xor_desc->srcAdd0 = bufPhysAddr + skb_headroom(skb) + MV_ETH_MH_SIZE + 30; + bufPhysAddr = virt_to_phys(skb_new->data); + eth_xor_desc->phyDestAdd = bufPhysAddr + skb_headroom(skb_new) + MV_ETH_MH_SIZE + 30; - eth_xor_desc->byteCnt = pRxPktInfo->bytes - 30; + eth_xor_desc->byteCnt = bytes - 30; eth_xor_desc->phyNextDescPtr = 0; eth_xor_desc->status = BIT31; @@ -490,13 +496,13 @@ MV_REG_WRITE(XOR_ACTIVATION_REG(1, XOR_CHAN(0)), XEXACTR_XESTART_MASK); - mvOsCacheLineInv(NULL, pRxPktInfo->pBuf + pRxPktInfo->offset); - l2fw_copy_mac(pRxPktInfo, pTxPktInfo); - mvOsCacheLineFlush(NULL, pTxPktInfo->pBuf + pTxPktInfo->offset); + mvOsCacheLineInv(NULL, skb->data); + pSrc = skb->data + MV_ETH_MH_SIZE; + pDst = skb_new->data + MV_ETH_MH_SIZE; + l2fw_copy_mac(pSrc, pDst); + mvOsCacheLineFlush(NULL, skb->data); - /* Update TxPktInfo */ - pTxPktInfo->bytes = pRxPktInfo->bytes; - return pTxPktInfo; + return skb_new; } void setXorDesc(void) @@ -603,7 +609,7 @@ #endif } -static inline MV_STATUS mv_eth_l2fw_tx(struct eth_pbuf *pkt, struct eth_port *pp, int withXor, +static inline MV_STATUS mv_eth_l2fw_tx(struct sk_buff *skb, struct eth_port *pp, int withXor, struct neta_rx_desc *rx_desc) { struct neta_tx_desc *tx_desc; @@ -610,6 +616,7 @@ u32 tx_cmd = 0; struct tx_queue *txq_ctrl; unsigned long flags = 0; + int pool_id; /* assigning different txq for each rx port , to avoid waiting on the same txq lock when traffic on several rx ports are destined to the same @@ -639,11 +646,18 @@ } txq_ctrl->txq_count++; + /* Get pool_id */ + pool_id = NETA_RX_GET_BPID(rx_desc); + #ifdef CONFIG_MV_ETH_BM_CPU - tx_cmd |= NETA_TX_BM_ENABLE_MASK | NETA_TX_BM_POOL_ID_MASK(pkt->pool); + if (MV_NETA_BM_CAP()) { + tx_cmd |= NETA_TX_BM_ENABLE_MASK | NETA_TX_BM_POOL_ID_MASK(pool_id); txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = (u32) NULL; + } else { + txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = (u32) skb; + } #else - txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = (u32) pkt; + txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = (u32) skb; #endif /* CONFIG_MV_ETH_BM_CPU */ mv_eth_shadow_inc_put(txq_ctrl); @@ -651,10 +665,10 @@ tx_desc->command = tx_cmd | NETA_TX_L4_CSUM_NOT | NETA_TX_FLZ_DESC_MASK | NETA_TX_F_DESC_MASK | NETA_TX_L_DESC_MASK | - NETA_TX_PKT_OFFSET_MASK(pkt->offset + MV_ETH_MH_SIZE); + NETA_TX_PKT_OFFSET_MASK(NET_SKB_PAD + MV_ETH_MH_SIZE); - tx_desc->dataSize = pkt->bytes; - tx_desc->bufPhysAddr = pkt->physAddr; + tx_desc->dataSize = rx_desc->dataSize - MV_ETH_MH_SIZE; + tx_desc->bufPhysAddr = virt_to_phys(skb->head); mv_eth_tx_desc_flush(pp, tx_desc); @@ -687,8 +701,7 @@ int rx_done, rx_filled; struct neta_rx_desc *rx_desc; u32 rx_status = MV_OK; - struct eth_pbuf *pkt; - struct eth_pbuf *newpkt = NULL; + struct sk_buff *skb, *skb_new = NULL; struct bm_pool *pool; MV_STATUS status = MV_OK; struct eth_port_l2fw *ppl2fw = mv_eth_ports_l2fw[pp->port]; @@ -695,6 +708,7 @@ MV_IP_HEADER *pIph = NULL; MV_U8 *pData; int ipOffset; + int pool_id, bytes; rx_done = mvNetaRxqBusyDescNumGet(pp->port, rxq); mvOsCacheIoSync(NULL); @@ -718,13 +732,14 @@ rx_done++; rx_filled++; - pkt = (struct eth_pbuf *)rx_desc->bufCookie; - if (!pkt) { - printk(KERN_INFO "pkt is NULL in ; rx_done=%d %s\n", rx_done, __func__); + skb = (struct sk_buff *)rx_desc->bufCookie; + if (!skb) { + pr_err("%s: skb is NULL, rx_done=%d\n", __func__, rx_done); return rx_done; } - pool = &mv_eth_pool[pkt->pool]; + pool_id = NETA_RX_GET_BPID(rx_desc); + pool = &mv_eth_pool[pool_id]; rx_status = rx_desc->status; if (((rx_status & NETA_RX_FL_DESC_MASK) != NETA_RX_FL_DESC_MASK) || (rx_status & NETA_RX_ES_MASK)) { @@ -733,16 +748,23 @@ if (pp->dev) pp->dev->stats.rx_errors++; - mv_eth_rxq_refill(pp, rxq, pkt, pool, rx_desc); + mv_eth_rxq_refill(pp, rxq, pool, skb, rx_desc); continue; } - pkt->bytes = rx_desc->dataSize - (MV_ETH_CRC_SIZE + MV_ETH_MH_SIZE); + bytes = rx_desc->dataSize - (MV_ETH_CRC_SIZE + MV_ETH_MH_SIZE); - pData = pkt->pBuf + pkt->offset; + pData = skb->head + NET_SKB_PAD; #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { ipOffset = NETA_RX_GET_IPHDR_OFFSET(rx_desc); + } else { + if ((rx_desc->status & ETH_RX_VLAN_TAGGED_FRAME_MASK)) + ipOffset = MV_ETH_MH_SIZE + sizeof(MV_802_3_HEADER) + MV_VLAN_HLEN; + else + ipOffset = MV_ETH_MH_SIZE + sizeof(MV_802_3_HEADER); + } #else if ((rx_desc->status & ETH_RX_VLAN_TAGGED_FRAME_MASK)) ipOffset = MV_ETH_MH_SIZE + sizeof(MV_802_3_HEADER) + MV_VLAN_HLEN; @@ -783,30 +805,27 @@ switch (ppl2fw->cmd) { case CMD_L2FW_AS_IS: - status = mv_eth_l2fw_tx(pkt, new_pp, 0, rx_desc); + status = mv_eth_l2fw_tx(skb, new_pp, 0, rx_desc); break; case CMD_L2FW_SWAP_MAC: - mvOsCacheLineInv(NULL, pkt->pBuf + pkt->offset); - l2fw_swap_mac(pkt); - mvOsCacheLineFlush(NULL, pkt->pBuf+pkt->offset); - status = mv_eth_l2fw_tx(pkt, new_pp, 0, rx_desc); + mvOsCacheLineInv(NULL, skb->head + NET_SKB_PAD); + l2fw_swap_mac(skb->data); + mvOsCacheLineFlush(NULL, skb->head + NET_SKB_PAD); + status = mv_eth_l2fw_tx(skb, new_pp, 0, rx_desc); break; case CMD_L2FW_COPY_SWAP: #ifdef CONFIG_MV_INCLUDE_XOR - if (pkt->bytes >= ppl2fw->xorThreshold) { - newpkt = eth_l2fw_copy_packet_withXor(pkt); - if (newpkt) - status = mv_eth_l2fw_tx(newpkt, new_pp, 1, rx_desc); - else - status = MV_ERROR; + if (bytes >= ppl2fw->xorThreshold) { + skb_new = eth_l2fw_copy_packet_withXor(skb, rx_desc); + pr_err("%s: xor is not supported\n", __func__); } else #endif /* CONFIG_MV_INCLUDE_XOR */ { - newpkt = eth_l2fw_copy_packet_withoutXor(pkt); - if (newpkt) - status = mv_eth_l2fw_tx(newpkt, new_pp, 0, rx_desc); + skb_new = eth_l2fw_copy_packet_withoutXor(skb, rx_desc); + if (skb_new) + status = mv_eth_l2fw_tx(skb, new_pp, 0, rx_desc); else status = MV_ERROR; } @@ -819,7 +838,7 @@ default: pr_err("WARNING:in %s invalid mode %d for rx port %d\n", __func__, ppl2fw->cmd, pp->port); - mv_eth_rxq_refill(pp, rxq, pkt, pool, rx_desc); + mv_eth_rxq_refill(pp, rxq, pool, skb, rx_desc); } /*of switch*/ if (status == MV_OK) { @@ -827,7 +846,7 @@ /* BM - no refill */ mvOsCacheLineInv(NULL, rx_desc); } else { - if (mv_eth_refill(pp, rxq, NULL, pool, rx_desc)) { + if (mv_eth_refill(pp, rxq, pool, rx_desc)) { printk(KERN_ERR "%s: Linux processing - Can't refill\n", __func__); pp->rxq_ctrl[rxq].missed++; } @@ -834,21 +853,21 @@ } /* we do not need the pkt , we do not do anything with it*/ if (ppl2fw->cmd == CMD_L2FW_COPY_SWAP) - mv_eth_pool_put(pool, pkt); + mv_eth_pool_put(pool, skb); continue; } else if (status == MV_DROPPED) { ppl2fw->statDrop++; - mv_eth_rxq_refill(pp, rxq, pkt, pool, rx_desc); + mv_eth_rxq_refill(pp, rxq, pool, skb, rx_desc); if (ppl2fw->cmd == CMD_L2FW_COPY_SWAP) - mv_eth_pool_put(pool, newpkt); + mv_eth_pool_put(pool, skb_new); continue; } else if (status == MV_ERROR) { ppl2fw->statErr++; - mv_eth_rxq_refill(pp, rxq, pkt, pool, rx_desc); + mv_eth_rxq_refill(pp, rxq, pool, skb, rx_desc); } Index: drivers/net/ethernet/mvebu_net/neta/l2fw/mv_eth_l2sec.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/l2fw/mv_eth_l2sec.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/l2fw/mv_eth_l2sec.c (working copy) @@ -121,8 +121,12 @@ txq_ctrl->txq_count++; #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { tx_cmd |= NETA_TX_BM_ENABLE_MASK | NETA_TX_BM_POOL_ID_MASK(pkt->pool); txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = (u32) NULL; + } else { + txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = (u32) pkt; + } #else txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = (u32) pkt; #endif /* CONFIG_MV_ETH_BM_CPU */ Index: drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_qos_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_qos_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_qos_sysfs.c (working copy) @@ -32,6 +32,7 @@ #include #include +#include "gbe/mvNeta.h" #include "mv_eth_sysfs.h" #include "mv_netdev.h" @@ -68,14 +69,14 @@ return off; } -#ifdef CONFIG_MV_ETH_PNC -int run_rxq_type(int port, int q, int t) +int pnc_run_rxq_type(int port, int q, int t) { void *port_hndl = mvNetaPortHndlGet(port); if (port_hndl == NULL) return 1; - +#ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { if (!mv_eth_pnc_ctrl_en) { pr_err("%s: PNC control is not supported\n", __func__); return 1; @@ -95,16 +96,17 @@ pr_err("unsupported packet type: value=%d\n", t); return 1; } + } +#endif /* CONFIG_MV_ETH_PNC */ return 0; } -#else -int run_rxq_type(int port, int q, int t) + +int neta_run_rxq_type(int port, int q, int t) { void *port_hndl = mvNetaPortHndlGet(port); if (port_hndl == NULL) return 1; - switch (t) { case 0: mvNetaBpduRxq(port, q); @@ -122,9 +124,9 @@ pr_err("unknown packet type: value=%d\n", t); return 1; } + return 0; } -#endif /* CONFIG_MV_ETH_PNC */ static ssize_t mv_eth_port_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -176,9 +178,12 @@ local_irq_save(flags); - if (!strcmp(name, "rxq_type")) - err = run_rxq_type(p, i, v); - else { + if (!strcmp(name, "rxq_type")) { + if (MV_NETA_PNC_CAP()) + err = pnc_run_rxq_type(p, i, v); + else + err = neta_run_rxq_type(p, i, v); + } else { err = 1; pr_err("%s: illegal operation <%s>\n", __func__, attr->attr.name); } Index: drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_sysfs.c (working copy) @@ -63,9 +63,10 @@ o += scnprintf(b+o, s-o, "echo p > gmac_regs - show gmac registers for

\n"); #endif /* MV_ETH_GMAC_NEW */ #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) o += scnprintf(b+o, s-o, "echo {0|1} > pnc - enable / disable PNC access\n"); #endif /* CONFIG_MV_ETH_PNC */ - o += scnprintf(b+o, s-o, "echo {0|1} > skb - enable / disable SKB recycle\n"); + o += scnprintf(b+o, s-o, "echo {0|1} > skb - enable / disable SKB recycle, only for SWF\n"); o += scnprintf(b+o, s-o, "echo p v > debug - bit0:rx, bit1:tx, bit2:isr, bit3:poll, bit4:dump\n"); o += scnprintf(b+o, s-o, "echo p l s > buf_num - set number of long and short buffers allocated for port

\n"); o += scnprintf(b+o, s-o, "echo p wol > pm_mode - set port

pm mode. 1 wol, 0 suspend.\n"); @@ -151,7 +152,7 @@ err = mv_eth_ctrl_flag(p, MV_ETH_F_DBG_POLL, v & 0x8); err = mv_eth_ctrl_flag(p, MV_ETH_F_DBG_DUMP, v & 0x10); } else if (!strcmp(name, "skb")) { - mv_eth_ctrl_recycle(p); + mv_eth_ctrl_swf_recycle(p); } else if (!strcmp(name, "port")) { mv_eth_status_print(); mvNetaPortStatus(p); Index: drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_tool.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_tool.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_tool.c (working copy) @@ -55,7 +55,6 @@ #define MV_ETH_TOOL_AN_TIMEOUT 5000 - static int isSwitch(struct eth_port *priv) { return priv->tagged; @@ -689,7 +688,61 @@ } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + +static u32 mv_eth_tool_get_rxfh_indir_size(struct net_device *netdev) +{ +#if defined(MV_ETH_PNC_LB) && defined(CONFIG_MV_ETH_PNC) + struct eth_port *priv = MV_ETH_PRIV(netdev); + return ARRAY_SIZE(priv->rx_indir_table); +#else + return 0; +#endif +} + static int mv_eth_tool_get_rxfh_indir(struct net_device *netdev, + u32 *indir) +{ +#if defined(MV_ETH_PNC_LB) && defined(CONFIG_MV_ETH_PNC) + struct eth_port *priv = MV_ETH_PRIV(netdev); + size_t copy_size = ARRAY_SIZE(priv->rx_indir_table); + + if (!MV_NETA_PNC_CAP()) + return -EOPNOTSUPP; + + memcpy(indir, priv->rx_indir_table, + copy_size * sizeof(u32)); + return 0; +#else + return -EOPNOTSUPP; +#endif +} + + +static int mv_eth_tool_set_rxfh_indir(struct net_device *netdev, + const u32 *indir) +{ +#if defined(MV_ETH_PNC_LB) && defined(CONFIG_MV_ETH_PNC) + int i; + struct eth_port *priv = MV_ETH_PRIV(netdev); + if (MV_NETA_PNC_CAP()) { + for (i = 0; i < ARRAY_SIZE(priv->rx_indir_table); i++) { + priv->rx_indir_table[i] = indir[i]; + mvPncLbRxqSet(i, priv->rx_indir_table[i]); + } + return 0; + } else { + return -EOPNOTSUPP; + } +#else + return -EOPNOTSUPP; +#endif +} + +#else /* KERNEL_VERSION(3, 3, 0) */ + +static int mv_eth_tool_get_rxfh_indir(struct net_device *netdev, struct ethtool_rxfh_indir *indir) { #if defined(MV_ETH_PNC_LB) && defined(CONFIG_MV_ETH_PNC) @@ -697,6 +750,9 @@ size_t copy_size = min_t(size_t, indir->size, ARRAY_SIZE(priv->rx_indir_table)); + if (!MV_NETA_PNC_CAP()) + return -EOPNOTSUPP; + indir->size = ARRAY_SIZE(priv->rx_indir_table); memcpy(indir->ring_index, priv->rx_indir_table, @@ -713,16 +769,21 @@ #if defined(MV_ETH_PNC_LB) && defined(CONFIG_MV_ETH_PNC) int i; struct eth_port *priv = MV_ETH_PRIV(netdev); + if (MV_NETA_PNC_CAP()) { for (i = 0; i < indir->size; i++) { priv->rx_indir_table[i] = indir->ring_index[i]; mvPncLbRxqSet(i, priv->rx_indir_table[i]); } return 0; + } else { + return -EOPNOTSUPP; + } #else return -EOPNOTSUPP; #endif } -#endif +#endif /* KERNEL_VERSION(3, 3, 0) */ +#endif /* KERNEL_VERSION(2, 6, 35) */ static int mv_eth_tool_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rules) @@ -831,6 +892,10 @@ .get_stats_count = mv_eth_tool_get_stats_count, #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + .get_rxfh_indir_size = mv_eth_tool_get_rxfh_indir_size, +#endif + #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35) .get_rxfh_indir = mv_eth_tool_get_rxfh_indir, .set_rxfh_indir = mv_eth_tool_set_rxfh_indir, Index: drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_tx_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_tx_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/net_dev/mv_eth_tx_sysfs.c (working copy) @@ -60,6 +60,11 @@ o += scnprintf(b+o, s-o, "echo p {0|1} > tx_nopad - disable zero padding on transmit\n"); o += scnprintf(b+o, s-o, "echo p v > tx_mh_2B - set 2 bytes of Marvell Header for transmit\n"); o += scnprintf(b+o, s-o, "echo p v > tx_cmd - set 4 bytes of TX descriptor offset 0xc\n"); +#ifdef CONFIG_MV_NETA_TXDONE_IN_HRTIMER + o += scnprintf(b+o, s-o, "echo period > tx_period - set Tx Done high resolution timer period\n"); + o += scnprintf(b+o, s-o, " period: period range is [%u, %u], unit usec\n", + MV_ETH_HRTIMER_PERIOD_MIN, MV_ETH_HRTIMER_PERIOD_MAX); +#endif return o; } @@ -135,6 +140,10 @@ err = mv_eth_txp_reset(p, i); } else if (!strcmp(name, "tx_done")) { mv_eth_ctrl_txdone(p); + } else if (!strcmp(name, "tx_period")) { +#ifdef CONFIG_MV_NETA_TXDONE_IN_HRTIMER + err = mv_eth_tx_done_hrtimer_period_set(p); +#endif } else { err = 1; pr_err("%s: illegal operation <%s>\n", __func__, attr->attr.name); @@ -233,6 +242,9 @@ static DEVICE_ATTR(txq_coal, S_IWUSR, NULL, mv_eth_4_store); static DEVICE_ATTR(mh_en, S_IWUSR, NULL, mv_eth_port_store); static DEVICE_ATTR(tx_done, S_IWUSR, NULL, mv_eth_3_store); +#ifdef CONFIG_MV_NETA_TXDONE_IN_HRTIMER +static DEVICE_ATTR(tx_period, S_IWUSR, NULL, mv_eth_3_store); +#endif static DEVICE_ATTR(txq_mask, S_IWUSR, NULL, mv_eth_3_hex_store); static DEVICE_ATTR(txq_shared, S_IWUSR, NULL, mv_eth_4_store); static DEVICE_ATTR(tx_nopad, S_IWUSR, NULL, mv_eth_port_store); @@ -251,6 +263,9 @@ &dev_attr_txq_coal.attr, &dev_attr_mh_en.attr, &dev_attr_tx_done.attr, +#ifdef CONFIG_MV_NETA_TXDONE_IN_HRTIMER + &dev_attr_tx_period.attr, +#endif &dev_attr_txq_mask.attr, &dev_attr_txq_shared.attr, &dev_attr_tx_nopad.attr, Index: drivers/net/ethernet/mvebu_net/neta/net_dev/mv_ethernet.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/net_dev/mv_ethernet.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/net_dev/mv_ethernet.c (working copy) @@ -80,10 +80,11 @@ } if (priv->flags & MV_ETH_F_CONNECT_LINUX) { /* connect to port interrupt line */ - if (request_irq(dev->irq, mv_eth_isr, (IRQF_DISABLED), "mv_eth", priv)) { + if (request_irq(dev->irq, mv_eth_isr, (IRQF_DISABLED), dev->name, priv)) { printk(KERN_ERR "cannot request irq %d for %s port %d\n", dev->irq, dev->name, priv->port); - if (priv->flags & MV_ETH_F_CONNECT_LINUX) - napi_disable(priv->napiGroup[CPU_GROUP_DEF]); + for (group = 0; group < CONFIG_MV_ETH_NAPI_GROUPS; group++) + napi_disable(priv->napiGroup[group]); + goto error; } @@ -121,8 +122,13 @@ mv_eth_stop_internals(priv); for_each_possible_cpu(cpu) { cpuCtrl = priv->cpu_config[cpu]; +#if defined(CONFIG_MV_NETA_TXDONE_IN_HRTIMER) + hrtimer_cancel(&cpuCtrl->tx_done_timer); + clear_bit(MV_ETH_F_TX_DONE_TIMER_BIT, &(cpuCtrl->flags)); +#elif defined(CONFIG_MV_NETA_TXDONE_IN_TIMER) del_timer(&cpuCtrl->tx_done_timer); clear_bit(MV_ETH_F_TX_DONE_TIMER_BIT, &(cpuCtrl->flags)); +#endif del_timer(&cpuCtrl->cleanup_timer); clear_bit(MV_ETH_F_CLEANUP_TIMER_BIT, &(cpuCtrl->flags)); } @@ -193,29 +199,42 @@ int i; #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { if (mv_eth_pnc_ctrl_en) { if (pnc_mac_me(priv->port, mac, CONFIG_MV_ETH_RXQ_DEF)) { - printk(KERN_ERR "%s: ethSetMacAddr failed\n", dev->name); + pr_err("%s: ethSetMacAddr failed\n", dev->name); return -1; } } else { - printk(KERN_ERR "%s: PNC control is disabled\n", __func__); + pr_err("%s: PNC control is disabled\n", __func__); return -1; } + } else { + /* remove previous address table entry */ + if (mvNetaMacAddrSet(priv->port, dev->dev_addr, -1) != MV_OK) { + pr_err("%s: ethSetMacAddr failed\n", dev->name); + return -1; + } + + /* set new addr in hw */ + if (mvNetaMacAddrSet(priv->port, mac, CONFIG_MV_ETH_RXQ_DEF) != MV_OK) { + pr_err("%s: ethSetMacAddr failed\n", dev->name); + return -1; + } + } #else /* remove previous address table entry */ if (mvNetaMacAddrSet(priv->port, dev->dev_addr, -1) != MV_OK) { - printk(KERN_ERR "%s: ethSetMacAddr failed\n", dev->name); + pr_err("%s: ethSetMacAddr failed\n", dev->name); return -1; } /* set new addr in hw */ if (mvNetaMacAddrSet(priv->port, mac, CONFIG_MV_ETH_RXQ_DEF) != MV_OK) { - printk(KERN_ERR "%s: ethSetMacAddr failed\n", dev->name); + pr_err("%s: ethSetMacAddr failed\n", dev->name); return -1; } -#endif /* CONFIG_MV_ETH_PNC */ - +#endif /* set addr in the device */ for (i = 0; i < 6; i++) dev->dev_addr[i] = mac[i]; @@ -226,7 +245,7 @@ } #ifdef CONFIG_MV_ETH_PNC -void mv_eth_set_multicast_list(struct net_device *dev) +void mv_eth_set_multicast_list_pnc(struct net_device *dev) { struct eth_port *priv = MV_ETH_PRIV(dev); int rxq = CONFIG_MV_ETH_RXQ_DEF; @@ -285,8 +304,9 @@ } } } -#else /* !CONFIG_MV_ETH_PNC - legacy parser */ -void mv_eth_set_multicast_list(struct net_device *dev) +#endif + +void mv_eth_set_multicast_list_legacy(struct net_device *dev) { struct eth_port *priv = MV_ETH_PRIV(dev); int queue = CONFIG_MV_ETH_RXQ_DEF; @@ -335,9 +355,19 @@ } } } + +void mv_eth_set_multicast_list(struct net_device *dev) +{ +#ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) + mv_eth_set_multicast_list_pnc(dev); + else + mv_eth_set_multicast_list_legacy(dev); +#else + mv_eth_set_multicast_list_legacy(dev); #endif /* CONFIG_MV_ETH_PNC */ +} - int mv_eth_set_mac_addr(struct net_device *dev, void *addr) { if (!netif_running(dev)) { @@ -377,22 +407,29 @@ int queue = CONFIG_MV_ETH_RXQ_DEF; #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { if (mv_eth_pnc_ctrl_en) { if (pnc_mac_me(priv->port, dev->dev_addr, queue)) { - printk(KERN_ERR "%s: ethSetMacAddr failed\n", dev->name); + pr_err("%s: ethSetMacAddr failed\n", dev->name); return -1; } } else - printk(KERN_ERR "%s: PNC control is disabled\n", __func__); + pr_err("%s: PNC control is disabled\n", __func__); + } else {/* Legacy parser */ + if (mvNetaMacAddrSet(priv->port, dev->dev_addr, queue) != MV_OK) { + pr_err("%s: ethSetMacAddr failed\n", dev->name); + return -1; + } + } #else /* Legacy parser */ if (mvNetaMacAddrSet(priv->port, dev->dev_addr, queue) != MV_OK) { - printk(KERN_ERR "%s: ethSetMacAddr failed\n", dev->name); + pr_err("%s: ethSetMacAddr failed\n", dev->name); return -1; } #endif /* CONFIG_MV_ETH_PNC */ if (mv_eth_start(dev)) { - printk(KERN_ERR "%s: start interface failed\n", dev->name); + pr_err("%s: start interface failed\n", dev->name); return -1; } return 0; Index: drivers/net/ethernet/mvebu_net/neta/net_dev/mv_netdev.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/net_dev/mv_netdev.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/net_dev/mv_netdev.c (working copy) @@ -63,7 +63,13 @@ #include #endif /* CONFIG_OF */ +#ifdef CONFIG_MV_NETA_TXDONE_IN_HRTIMER +#include +#include +#endif + #ifdef CONFIG_ARCH_MVEBU +#include "mvebu-soc-id.h" #include "mvNetConfig.h" #else #include "mvSysEthConfig.h" @@ -76,6 +82,11 @@ #ifdef CONFIG_OF int port_vbase[MV_ETH_MAX_PORTS]; +int bm_reg_vbase, pnc_reg_vbase; +static u32 pnc_phyaddr_base; +static u32 pnc_win_size; +static u32 bm_phyaddr_base; +static u32 bm_win_size; #endif /* CONFIG_OF */ static struct mv_mux_eth_ops mux_eth_ops; @@ -114,20 +125,22 @@ #endif /* CONFIG_MV_ETH_PNC */ #ifdef CONFIG_MV_NETA_SKB_RECYCLE -int mv_ctrl_recycle = CONFIG_MV_NETA_SKB_RECYCLE_DEF; -EXPORT_SYMBOL(mv_ctrl_recycle); +int mv_ctrl_swf_recycle = CONFIG_MV_NETA_SKB_RECYCLE_DEF; +EXPORT_SYMBOL(mv_ctrl_swf_recycle); -int mv_eth_ctrl_recycle(int en) +int mv_eth_ctrl_swf_recycle(int en) { - mv_ctrl_recycle = en; + mv_ctrl_swf_recycle = en; return 0; } #else -int mv_eth_ctrl_recycle(int en) + +int mv_eth_ctrl_swf_recycle(int en) { - printk(KERN_ERR "SKB recycle is not supported\n"); + pr_info("SWF SKB recycle is not supported\n"); return 1; } + #endif /* CONFIG_MV_NETA_SKB_RECYCLE */ extern u8 mvMacAddr[CONFIG_MV_ETH_PORTS_NUM][MV_MAC_ADDR_SIZE]; @@ -139,6 +152,9 @@ struct bm_pool mv_eth_pool[MV_ETH_BM_POOLS]; struct eth_port **mv_eth_ports; +/* Global device used for global cache operation */ +static struct device *neta_global_dev; + int mv_ctrl_txdone = CONFIG_MV_ETH_TXDONE_COAL_PKTS; EXPORT_SYMBOL(mv_ctrl_txdone); @@ -149,6 +165,10 @@ static int mv_eth_initialized = 0; +#ifdef CONFIG_MV_NETA_TXDONE_IN_HRTIMER +static unsigned int mv_eth_tx_done_hrtimer_period_us = CONFIG_MV_NETA_TX_DONE_HIGH_RES_TIMER_PERIOD; +#endif + /* * Local functions */ @@ -186,7 +206,24 @@ int mv_eth_cmdline_port3_config(char *s); __setup("mv_port3_config=", mv_eth_cmdline_port3_config); +#ifdef CONFIG_MV_NETA_TXDONE_IN_HRTIMER +unsigned int mv_eth_tx_done_hrtimer_period_get(void) +{ + return mv_eth_tx_done_hrtimer_period_us; +} +int mv_eth_tx_done_hrtimer_period_set(unsigned int period) +{ + if ((period < MV_ETH_HRTIMER_PERIOD_MIN) || (period > MV_ETH_HRTIMER_PERIOD_MAX)) { + pr_info("period should be in [%u, %u]\n", MV_ETH_HRTIMER_PERIOD_MIN, MV_ETH_HRTIMER_PERIOD_MAX); + return -EINVAL; + } + + mv_eth_tx_done_hrtimer_period_us = period; + return 0; +} +#endif + int mv_eth_cmdline_port0_config(char *s) { port0_config_str = s; @@ -212,31 +249,36 @@ } void mv_eth_stack_print(int port, MV_BOOL isPrintElements) { - struct eth_port *pp = mv_eth_port_by_id(port); + struct eth_port *pp; + if (mvNetaPortCheck(port)) + return; + + pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_INFO "%s: Invalid port [%d]\n", __func__, port); + pr_err("Port %d does not exist\n", port); return; } if (pp->pool_long == NULL) { - printk(KERN_ERR "%s: Error - long pool is null\n", __func__); + pr_err("%s: Error - long pool is null\n", __func__); return; } - printk(KERN_INFO "Long pool (%d) stack\n", pp->pool_long->pool); + pr_info("Long pool (%d) stack\n", pp->pool_long->pool); mvStackStatus(pp->pool_long->stack, isPrintElements); -#ifdef CONFIG_MV_ETH_BM +#ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { if (pp->pool_short == NULL) { - printk(KERN_ERR "%s: Error - short pool is null\n", __func__); + pr_err("%s: Error - short pool is null\n", __func__); return; } - - printk(KERN_INFO "Short pool (%d) stack\n", pp->pool_short->pool); + pr_info("Short pool (%d) stack\n", pp->pool_short->pool); mvStackStatus(pp->pool_short->stack, isPrintElements); -#endif /* CONFIG_MV_ETH_BM */ } +#endif /* CONFIG_MV_ETH_BM_CPU */ +} /***************************************** @@ -261,10 +303,11 @@ } else if (rate > pp->pkt_rate_high_cfg) { if (pp->rate_current != 3) { pp->rate_current = 3; - for (i = 0; i < CONFIG_MV_ETH_RXQ; i++) + for (i = 0; i < CONFIG_MV_ETH_RXQ; i++) { mv_eth_rx_time_coal_set(pp->port, i, pp->rx_time_high_coal_cfg); mv_eth_rx_pkts_coal_set(pp->port, i, pp->rx_pkts_high_coal_cfg); } + } } else { if (pp->rate_current != 2) { pp->rate_current = 2; @@ -274,7 +317,6 @@ } } } - pp->rx_rate_pkts = 0; pp->rx_timestamp = jiffies; } @@ -286,8 +328,17 @@ static int mv_eth_tag_type_set(int port, int type) { - struct eth_port *pp = mv_eth_port_by_id(port); + struct eth_port *pp; + if (mvNetaPortCheck(port)) + return -EINVAL; + + pp = mv_eth_port_by_id(port); + if (pp == NULL) { + pr_err("Port %d does not exist\n", port); + return -EINVAL; + } + if ((type == MV_TAG_TYPE_MH) || (type == MV_TAG_TYPE_DSA) || (type == MV_TAG_TYPE_EDSA)) mvNetaMhSet(port, type); @@ -406,7 +457,7 @@ printk(KERN_ERR "\n"); if (pp == NULL) { - printk(KERN_ERR " o mv_eth_port_config_parse: got NULL pp\n"); + pr_err("Port %d does not exist\n", pp->port); return -1; } @@ -560,7 +611,7 @@ struct eth_port *pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_INFO "port doens not exist (%d) in %s\n" , port, __func__); + pr_err("Port %d does not exist\n", port); return -EINVAL; } @@ -578,6 +629,7 @@ pp->pool_long_num = long_num; #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { if (pp->pool_short != NULL) { /* Update number of buffers in existing pool (allocate or free) */ if (pp->pool_short_num > short_num) @@ -586,6 +638,7 @@ mv_eth_pool_add(pp, pp->pool_short->pool, short_num - pp->pool_short_num); } pp->pool_short_num = short_num; + } #endif /* CONFIG_MV_ETH_BM_CPU */ return 0; @@ -602,6 +655,7 @@ struct bm_pool *ppool; struct eth_port *pp; + if (MV_NETA_BM_CAP()) { if (mvNetaMaxCheck(pool, MV_ETH_BM_POOLS, "bm_pool")) return -EINVAL; @@ -613,7 +667,7 @@ pp = mv_eth_port_by_id(port); if (pp->flags & MV_ETH_F_STARTED) { - printk(KERN_ERR "Port %d use pool #%d and must be stopped before change pkt_size\n", + pr_err("Port %d use pool #%d and must be stopped before change pkt_size\n", port, pool); return -EINVAL; } @@ -637,13 +691,14 @@ } } ppool->pkt_size = pkt_size; + } #endif /* CONFIG_MV_ETH_BM_CPU */ mv_eth_bm_config_pkt_size_set(pool, pkt_size); if (pkt_size == 0) - mvBmPoolBufSizeSet(pool, 0); + mvNetaBmPoolBufferSizeSet(pool, 0); else - mvBmPoolBufSizeSet(pool, RX_BUF_SIZE(pkt_size)); + mvNetaBmPoolBufferSizeSet(pool, RX_BUF_SIZE(pkt_size)); return 0; } @@ -656,7 +711,7 @@ int cpu; if (pp == NULL) { - printk(KERN_INFO "port doens not exist (%d) in %s\n" , port, __func__); + pr_err("Port %d does not exist\n", port); return -EINVAL; } @@ -855,13 +910,14 @@ { struct tx_queue *txq_ctrl; struct eth_port *pp = mv_eth_port_by_id(port); + if ((value < 0) || (value > 1)) { - printk(KERN_ERR "%s:Invalid value %d , should be 0 or 1.\n \n", __func__, value); + pr_err("%s: Invalid value %d, should be 0 or 1\n", __func__, value); return -EINVAL; } if (pp == NULL) { - printk(KERN_ERR "%s: pp is null \n", __func__); + pr_err("Port %d does not exist\n", port); return -EINVAL; } @@ -886,10 +942,10 @@ int mv_eth_ctrl_txq_cpu_def(int port, int txp, int txq, int cpu) { struct cpu_ctrl *cpuCtrl; - struct eth_port *pp = mv_eth_port_by_id(port); + struct eth_port *pp; if ((cpu >= nr_cpu_ids) || (cpu < 0)) { - printk(KERN_ERR "cpu #%d is out of range: from 0 to %d\n", + pr_err("cpu #%d is out of range: from 0 to %d\n", cpu, nr_cpu_ids - 1); return -EINVAL; } @@ -897,9 +953,11 @@ if (mvNetaTxpCheck(port, txp)) return -EINVAL; - if ((pp == NULL) || (pp->txq_ctrl == NULL)) + pp = mv_eth_port_by_id(port); + if ((pp == NULL) || (pp->txq_ctrl == NULL)) { + pr_err("Port %d does not exist\n", port); return -ENODEV; - + } cpuCtrl = pp->cpu_config[cpu]; /* Check that new txq can be allocated for CPU */ @@ -908,6 +966,7 @@ return -EINVAL; } + if (test_bit(MV_ETH_F_STARTED_BIT, &(pp->flags))) { /* Decrement CPU ownership for old txq */ mv_eth_ctrl_txq_cpu_own(port, pp->txp, cpuCtrl->txq, 0, cpu); @@ -919,6 +978,7 @@ if (mv_eth_ctrl_txq_cpu_own(port, txp, txq, 1, cpu)) return -EINVAL; } + } pp->txp = txp; cpuCtrl->txq = txq; @@ -944,7 +1004,7 @@ return -EINVAL; } if (pp == NULL) { - printk(KERN_ERR "%s: pp is null \n", __func__); + pr_err("Port %d does not exist\n", port); return MV_FAIL; } @@ -1082,7 +1142,7 @@ return 0; #if defined(MV_ETH_PNC_LB) && defined(CONFIG_MV_ETH_PNC) - if (changed & NETIF_F_RXHASH) { + if (MV_NETA_PNC_CAP() && (changed & NETIF_F_RXHASH)) { if (features & NETIF_F_RXHASH) { dev->features |= NETIF_F_RXHASH; mvPncLbModeIp4(LB_2_TUPLE_VALUE); @@ -1194,9 +1254,6 @@ atomic_read(&skb->users), atomic_read(&skb_shinfo(skb)->dataref), skb_shinfo(skb)->nr_frags, skb_shinfo(skb)->gso_size, skb_shinfo(skb)->gso_segs); printk(KERN_ERR "\t proto=%d, ip_summed=%d, priority=%d\n", ntohs(skb->protocol), skb->ip_summed, skb->priority); -#ifdef CONFIG_MV_NETA_SKB_RECYCLE - printk(KERN_ERR "\t skb_recycle=%p, hw_cookie=0x%x\n", skb->skb_recycle, skb->hw_cookie); -#endif /* CONFIG_MV_NETA_SKB_RECYCLE */ } void mv_eth_rx_desc_print(struct neta_rx_desc *desc) @@ -1237,19 +1294,21 @@ printk(KERN_CONT "\n"); #ifdef CONFIG_MV_ETH_PNC - printk(KERN_ERR "RINFO: "); + if (MV_NETA_PNC_CAP()) { + pr_err("RINFO: "); if (desc->pncInfo & NETA_PNC_DA_MC) - printk(KERN_CONT "DA_MC, "); + pr_cont("DA_MC, "); if (desc->pncInfo & NETA_PNC_DA_BC) - printk(KERN_CONT "DA_BC, "); + pr_cont("DA_BC, "); if (desc->pncInfo & NETA_PNC_DA_UC) - printk(KERN_CONT "DA_UC, "); + pr_cont("DA_UC, "); if (desc->pncInfo & NETA_PNC_VLAN) - printk(KERN_CONT "VLAN, "); + pr_cont("VLAN, "); if (desc->pncInfo & NETA_PNC_PPPOE) - printk(KERN_CONT "PPPOE, "); + pr_cont("PPPOE, "); if (desc->pncInfo & NETA_PNC_RX_SPECIAL) - printk(KERN_CONT "RX_SPEC, "); + pr_cont("RX_SPEC, "); + } #endif /* CONFIG_MV_ETH_PNC */ printk(KERN_CONT "\n"); @@ -1333,95 +1392,12 @@ return txq; } -#ifdef CONFIG_MV_NETA_SKB_RECYCLE -int mv_eth_skb_recycle(struct sk_buff *skb) -{ - struct eth_pbuf *pkt = (struct eth_pbuf *)(skb->hw_cookie & ~BIT(0)); - struct bm_pool *pool; - int status = 0; - - if (mvNetaMaxCheck(pkt->pool, MV_ETH_BM_POOLS, "bm_pool")) - goto err; - - pool = &mv_eth_pool[pkt->pool]; - if (skb->hw_cookie & BIT(0)) { - /* hw_cookie is not valid for recycle */ - STAT_DBG(pool->stats.skb_hw_cookie_err++); - goto err; - } - -#if defined(CONFIG_MV_ETH_BM_CPU) - /* Check that first 4 bytes of the buffer contain hw_cookie */ - if (*((MV_U32 *) skb->head) != (MV_U32)pkt) { - /* - pr_err("%s: Wrong skb->head=%p (0x%x) != hw_cookie=%p\n", - __func__, skb->head, *((MV_U32 *) skb->head), pkt); - */ - STAT_DBG(pool->stats.skb_hw_cookie_err++); - goto err; - } -#endif /* CONFIG_MV_ETH_BM_CPU */ - - /* Check validity of skb->head - some Linux functions (skb_expand_head) reallocate it */ - if (skb->head != pkt->pBuf) { - /* - pr_err("%s: skb=%p, pkt=%p, Wrong skb->head=%p != pkt->pBuf=%p\n", - __func__, skb, pkt, skb->head, pkt->pBuf); - */ - STAT_DBG(pool->stats.skb_hw_cookie_err++); - goto err; - } - /* - WA for Linux network stack issue that prevent skb recycle. - If dev_kfree_skb_any called from interrupt context or interrupts disabled context - skb->users will be zero when skb_recycle callback function is called. - In such case skb_recycle_check function returns error because skb->users != 1. - */ - if (atomic_read(&skb->users) == 0) - atomic_set(&skb->users, 1); - - if (skb_recycle_check(skb, pool->pkt_size)) { -#ifdef CONFIG_MV_NETA_DEBUG_CODE - /* Sanity check */ - if (SKB_TRUESIZE(skb->end - skb->head) != skb->truesize) { - printk(KERN_ERR "%s: skb=%p, Wrong SKB_TRUESIZE(end - head)=%d\n", - __func__, skb, SKB_TRUESIZE(skb->end - skb->head)); - mv_eth_skb_print(skb); - } -#endif /* CONFIG_MV_NETA_DEBUG_CODE */ - - STAT_DBG(pool->stats.skb_recycled_ok++); - mvOsCacheInvalidate(pp->dev->dev.parent, skb->head, RX_BUF_SIZE(pool->pkt_size)); - - status = mv_eth_pool_put(pool, pkt); - -#ifdef ETH_SKB_DEBUG - if (status == 0) - mv_eth_skb_save(skb, "recycle"); -#endif /* ETH_SKB_DEBUG */ - - return 0; - } - STAT_DBG(pool->stats.skb_recycled_err++); - - /* printk(KERN_ERR "mv_eth_skb_recycle failed: pool=%d, pkt=%p, skb=%p\n", pkt->pool, pkt, skb); */ -err: - mvOsFree(pkt); - skb->hw_cookie = 0; - skb->skb_recycle = NULL; - - return 1; -} -EXPORT_SYMBOL(mv_eth_skb_recycle); - -#endif /* CONFIG_MV_NETA_SKB_RECYCLE */ - static struct sk_buff *mv_eth_skb_alloc(struct eth_port *pp, struct bm_pool *pool, - struct eth_pbuf *pkt, gfp_t gfp_mask) + phys_addr_t *phys_addr, gfp_t gfp_mask) { struct sk_buff *skb; - skb = __dev_alloc_skb(pool->pkt_size, gfp_mask); + skb = __dev_alloc_skb(pool->pkt_size, GFP_DMA | gfp_mask); if (!skb) { STAT_ERR(pool->stats.skb_alloc_oom++); return NULL; @@ -1433,25 +1409,31 @@ #endif /* ETH_SKB_DEBUG */ #ifdef CONFIG_MV_ETH_BM_CPU - /* Save pkt as first 4 bytes in the buffer */ + /* Save skb as first 4 bytes in the buffer, then skb can be get through rx_desc->bufCookie */ #if !defined(CONFIG_MV_ETH_BE_WA) - *((MV_U32 *) skb->head) = MV_32BIT_LE((MV_U32)pkt); + *((MV_U32 *) skb->head) = MV_32BIT_LE((MV_U32)skb); #else - *((MV_U32 *) skb->head) = (MV_U32)pkt; + *((MV_U32 *) skb->head) = (MV_U32)skb; #endif /* !CONFIG_MV_ETH_BE_WA */ mvOsCacheLineFlush(pp->dev->dev.parent, skb->head); #endif /* CONFIG_MV_ETH_BM_CPU */ - pkt->osInfo = (void *)skb; - pkt->pBuf = skb->head; - pkt->bytes = 0; - pkt->physAddr = mvOsCacheInvalidate(pp->dev->dev.parent, skb->head, RX_BUF_SIZE(pool->pkt_size)); - pkt->offset = NET_SKB_PAD; - pkt->pool = pool->pool; + if (phys_addr) + *phys_addr = mvOsCacheInvalidate(pp->dev->dev.parent, skb->head, RX_BUF_SIZE(pool->pkt_size)); return skb; } +#ifdef CONFIG_MV_NETA_SKB_RECYCLE +static inline struct bm_pool *mv_eth_skb_recycle_get_pool(struct sk_buff *skb) +{ + if (mv_eth_is_swf_recycle() && MV_NETA_SKB_RECYCLE_MAGIC_IS_OK(skb)) + return &mv_eth_pool[MV_NETA_SKB_RECYCLE_BPID_GET(skb)]; + else + return NULL; +} +#endif + static inline void mv_eth_txq_buf_free(struct eth_port *pp, u32 shadow) { if (!shadow) @@ -1459,27 +1441,34 @@ if (shadow & MV_ETH_SHADOW_SKB) { shadow &= ~MV_ETH_SHADOW_SKB; +#ifndef CONFIG_MV_ETH_BM_CPU + struct sk_buff *skb = (struct sk_buff *)shadow; + struct bm_pool *pool = mv_eth_skb_recycle_get_pool(skb); + /* check recycle enable flag and magic number */ + if (pool && skb_recycle_check(skb, pool->pkt_size)) { + /* push skb to stack */ + if (mv_eth_pool_put(pool, skb)) + STAT_DBG(pp->stats.tx_skb_free++); + else + STAT_DBG(pool->stats.skb_recycled_ok++); + } else { + dev_kfree_skb_any(skb); + STAT_DBG(pp->stats.tx_skb_free++); + if (pool) + STAT_DBG(pool->stats.skb_recycled_err++); + } +#else dev_kfree_skb_any((struct sk_buff *)shadow); STAT_DBG(pp->stats.tx_skb_free++); - } else { - if (shadow & MV_ETH_SHADOW_EXT) { +#endif /* CONFIG_MV_ETH_BM_CPU */ + } else if (shadow & MV_ETH_SHADOW_EXT) { shadow &= ~MV_ETH_SHADOW_EXT; mv_eth_extra_pool_put(pp, (void *)shadow); } else { - /* packet from NFP without BM */ - struct eth_pbuf *pkt = (struct eth_pbuf *)shadow; - struct bm_pool *pool = &mv_eth_pool[pkt->pool]; - - if (mv_eth_pool_bm(pool)) { - /* Refill BM pool */ - STAT_DBG(pool->stats.bm_put++); - mvBmPoolPut(pkt->pool, (MV_ULONG) pkt->physAddr); - } else { - mv_eth_pool_put(pool, pkt); + /* TBD - return buffer back to BM */ + pr_err("%s: unexpected buffer - not skb and not ext\n", __func__); } } - } -} static inline void mv_eth_txq_cpu_clean(struct eth_port *pp, struct tx_queue *txq_ctrl) { @@ -1565,7 +1554,7 @@ if (mode == MV_ETH_TXQ_CPU) mv_eth_txq_cpu_clean(pp, txq_ctrl); #ifdef CONFIG_MV_ETH_HWF - else if (mode == MV_ETH_TXQ_HWF) + else if (mode == MV_ETH_TXQ_HWF && MV_NETA_HWF_CAP()) mv_eth_txq_hwf_clean(pp, txq_ctrl, rx_port); #endif /* CONFIG_MV_ETH_HWF */ @@ -1606,10 +1595,10 @@ } EXPORT_SYMBOL(mv_eth_txq_done); -inline struct eth_pbuf *mv_eth_pool_get(struct eth_port *pp, struct bm_pool *pool) +inline struct sk_buff *mv_eth_pool_get(struct eth_port *pp, struct bm_pool *pool) { - struct eth_pbuf *pkt = NULL; - struct sk_buff *skb; + struct sk_buff *skb = NULL; + phys_addr_t pa; unsigned long flags = 0; MV_ETH_LOCK(&pool->lock, flags); @@ -1616,48 +1605,56 @@ if (mvStackIndex(pool->stack) > 0) { STAT_DBG(pool->stats.stack_get++); - pkt = (struct eth_pbuf *)mvStackPop(pool->stack); + skb = (struct sk_buff *)mvStackPop(pool->stack); } else STAT_ERR(pool->stats.stack_empty++); MV_ETH_UNLOCK(&pool->lock, flags); - if (pkt) - return pkt; + if (skb) + return skb; - /* Try to allocate new pkt + skb */ - pkt = mvOsMalloc(sizeof(struct eth_pbuf)); - if (pkt) { - skb = mv_eth_skb_alloc(pp, pool, pkt, GFP_ATOMIC); - if (!skb) { - mvOsFree(pkt); - pkt = NULL; + /* Try to allocate new skb */ + skb = mv_eth_skb_alloc(pp, pool, &pa, GFP_ATOMIC); + if (!skb) + return NULL; + + return skb; } - } - return pkt; -} /* Reuse pkt if possible, allocate new skb and move BM pool or RXQ ring */ inline int mv_eth_refill(struct eth_port *pp, int rxq, - struct eth_pbuf *pkt, struct bm_pool *pool, struct neta_rx_desc *rx_desc) + struct bm_pool *pool, struct neta_rx_desc *rx_desc) { - if (pkt == NULL) { - pkt = mv_eth_pool_get(pp, pool); - if (pkt == NULL) + struct sk_buff *skb = NULL; + phys_addr_t phys_addr; + int pool_in_use = atomic_read(&pool->in_use); + + if (pool_in_use <= 0) + return 0; + + if (mv_eth_is_swf_recycle()) { + if (mv_eth_pool_bm(pool) && (pool_in_use < pool->in_use_thresh)) { + mvOsCacheLineInv(pp->dev->dev.parent, rx_desc); + return 0; + } + skb = mv_eth_pool_get(pp, pool); + if (!skb) return 1; - } else { - struct sk_buff *skb; + } /* No recycle - alloc new skb */ - skb = mv_eth_skb_alloc(pp, pool, pkt, GFP_ATOMIC); if (!skb) { - mvOsFree(pkt); + skb = mv_eth_skb_alloc(pp, pool, &phys_addr, GFP_ATOMIC); + if (!skb) { pool->missed++; mv_eth_add_cleanup_timer(pp->cpu_config[smp_processor_id()]); return 1; } } - mv_eth_rxq_refill(pp, rxq, pkt, pool, rx_desc); + mv_eth_rxq_refill(pp, rxq, pool, skb, rx_desc); + atomic_dec(&pool->in_use); + return 0; } EXPORT_SYMBOL(mv_eth_refill); @@ -1730,9 +1727,9 @@ struct neta_rx_desc *rx_desc; u32 rx_status; int rx_bytes; - struct eth_pbuf *pkt; struct sk_buff *skb; struct bm_pool *pool; + int pool_id; #ifdef CONFIG_NETMAP if (pp->flags & MV_ETH_F_IFCAP_NETMAP) { int netmap_done; @@ -1771,14 +1768,19 @@ #ifdef CONFIG_MV_NETA_DEBUG_CODE if (pp->flags & MV_ETH_F_DBG_RX) { - printk(KERN_ERR "\n%s: port=%d, cpu=%d\n", __func__, pp->port, smp_processor_id()); + pr_info("\n%s: port=%d, cpu=%d\n", __func__, pp->port, smp_processor_id()); mv_eth_rx_desc_print(rx_desc); } #endif /* CONFIG_MV_NETA_DEBUG_CODE */ rx_status = rx_desc->status; - pkt = (struct eth_pbuf *)rx_desc->bufCookie; - pool = &mv_eth_pool[pkt->pool]; + skb = (struct sk_buff *)rx_desc->bufCookie; +#if !defined(CONFIG_MV_ETH_BM_CPU) && (defined(CONFIG_MV_NETA_SKB_RECYCLE)) + pool_id = MV_NETA_SKB_RECYCLE_BPID_GET(skb); +#else + pool_id = NETA_RX_GET_BPID(rx_desc); +#endif + pool = &mv_eth_pool[pool_id]; if (((rx_status & NETA_RX_FL_DESC_MASK) != NETA_RX_FL_DESC_MASK) || (rx_status & NETA_RX_ES_MASK)) { @@ -1785,20 +1787,21 @@ mv_eth_rx_error(pp, rx_desc); - mv_eth_rxq_refill(pp, rxq, pkt, pool, rx_desc); + mv_eth_rxq_refill(pp, rxq, pool, skb, rx_desc); continue; } /* Speculative ICache prefetch WA: should be replaced with dma_unmap_single (invalidate l2) */ - mvOsCacheMultiLineInv(pp->dev->dev.parent, pkt->pBuf + pkt->offset, rx_desc->dataSize); + mvOsCacheMultiLineInv(pp->dev->dev.parent, skb->head + NET_SKB_PAD, rx_desc->dataSize); #ifdef CONFIG_MV_ETH_RX_PKT_PREFETCH - prefetch(pkt->pBuf + pkt->offset); - prefetch(pkt->pBuf + pkt->offset + CPU_D_CACHE_LINE_SIZE); + prefetch(skb->head + NET_SKB_PAD); + prefetch(skb->head + NET_SKB_PAD + CPU_D_CACHE_LINE_SIZE); #endif /* CONFIG_MV_ETH_RX_PKT_PREFETCH */ dev = pp->dev; + atomic_inc(&pool->in_use); STAT_DBG(pp->stats.rxq[rxq]++); dev->stats.rx_packets++; @@ -1807,7 +1810,7 @@ #ifndef CONFIG_MV_ETH_PNC /* Update IP offset and IP header len in RX descriptor */ - if (NETA_RX_L3_IS_IP4(rx_desc->status)) { + if (MV_NETA_PNC_CAP() && NETA_RX_L3_IS_IP4(rx_desc->status)) { int ip_offset; if ((rx_desc->status & ETH_RX_VLAN_TAGGED_FRAME_MASK)) @@ -1822,22 +1825,26 @@ #ifdef CONFIG_MV_NETA_DEBUG_CODE if (pp->flags & MV_ETH_F_DBG_RX) { - printk(KERN_ERR "pkt=%p, pBuf=%p, ksize=%d\n", pkt, pkt->pBuf, ksize(pkt->pBuf)); - mvDebugMemDump(pkt->pBuf + pkt->offset, 64, 1); + pr_info("skb=%p, buf=%p, ksize=%d\n", skb, skb->head, ksize(skb->head)); + mvDebugMemDump(skb->head + NET_SKB_PAD, 64, 1); } #endif /* CONFIG_MV_NETA_DEBUG_CODE */ + /* Set skb recycle magic(bit 31~2) and pool(bit 1~0) id if recycle is enabled */ + if (mv_eth_is_swf_recycle()) + MV_NETA_SKB_RECYCLE_MAGIC_BPID_SET(skb, (MV_NETA_SKB_RECYCLE_MAGIC(skb) | pool->pool)); + #if defined(CONFIG_MV_ETH_PNC) && defined(CONFIG_MV_ETH_RX_SPECIAL) /* Special RX processing */ - if (rx_desc->pncInfo & NETA_PNC_RX_SPECIAL) { + if (MV_NETA_PNC_CAP() && (rx_desc->pncInfo & NETA_PNC_RX_SPECIAL)) { if (pp->rx_special_proc) { - pp->rx_special_proc(pp->port, rxq, dev, (struct sk_buff *)(pkt->osInfo), rx_desc); + pp->rx_special_proc(pp->port, rxq, dev, skb, rx_desc); STAT_INFO(pp->stats.rx_special++); /* Refill processing */ - err = mv_eth_refill(pp, rxq, pkt, pool, rx_desc); + err = mv_eth_refill(pp, rxq, pool, rx_desc); if (err) { - printk(KERN_ERR "Linux processing - Can't refill\n"); + pr_err("Linux processing - Can't refill\n"); pp->rxq_ctrl[rxq].missed++; rx_filled--; } @@ -1846,28 +1853,7 @@ } #endif /* CONFIG_MV_ETH_PNC && CONFIG_MV_ETH_RX_SPECIAL */ -#if defined(CONFIG_MV_ETH_NFP) - if (pp->flags & MV_ETH_F_NFP_EN) { - MV_STATUS status; - - pkt->bytes = rx_bytes; - pkt->offset = NET_SKB_PAD; - - status = mv_eth_nfp(pp, rxq, rx_desc, pkt, pool); - if (status == MV_OK) - continue; - if (status == MV_FAIL) { - rx_filled--; - continue; - } - /* MV_TERMINATE - packet returned to slow path */ - } -#endif /* CONFIG_MV_ETH_NFP */ - /* Linux processing */ - skb = (struct sk_buff *)(pkt->osInfo); - - /* Linux processing */ __skb_put(skb, rx_bytes); #ifdef ETH_SKB_DEBUG @@ -1874,14 +1860,6 @@ mv_eth_skb_check(skb); #endif /* ETH_SKB_DEBUG */ -#ifdef CONFIG_MV_NETA_SKB_RECYCLE - if (mv_eth_is_recycle()) { - skb->skb_recycle = mv_eth_skb_recycle; - skb->hw_cookie = (__u32)pkt; - pkt = NULL; - } -#endif /* CONFIG_MV_NETA_SKB_RECYCLE */ - mv_eth_rx_csum(pp, rx_desc, skb); if (pp->tagged) { @@ -1911,7 +1889,7 @@ } /* Refill processing: */ - err = mv_eth_refill(pp, rxq, pkt, pool, rx_desc); + err = mv_eth_refill(pp, rxq, pool, rx_desc); if (err) { printk(KERN_ERR "Linux processing - Can't refill\n"); pp->rxq_ctrl[rxq].missed++; @@ -1933,7 +1911,7 @@ int frags = 0; bool tx_spec_ready = false; struct mv_eth_tx_spec tx_spec; - u32 tx_cmd; + u32 tx_cmd, skb_len = 0; struct tx_queue *txq_ctrl = NULL; struct neta_tx_desc *tx_desc; @@ -2032,10 +2010,33 @@ tx_desc->bufPhysAddr = mvOsCacheFlush(pp->dev->dev.parent, skb->data, tx_desc->dataSize); + /* Record skb len in case skb is reset when recycle */ + skb_len = skb->len; + if (frags == 1) { /* * First and Last descriptor */ +#if defined(CONFIG_MV_ETH_BM_CPU) && defined(CONFIG_MV_NETA_SKB_RECYCLE) + struct bm_pool *pool = mv_eth_skb_recycle_get_pool(skb); + if (pool && (atomic_read(&pool->in_use) > 0) && skb_recycle_check(skb, pool->pkt_size)) { + /* HW release buffer after tx finished */ + tx_cmd |= NETA_TX_BM_ENABLE_MASK | + NETA_TX_BM_POOL_ID_MASK(pool->pool) | + NETA_TX_PKT_OFFSET_MASK(NET_SKB_PAD + MV_ETH_MH_SIZE); + txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = NULL; + tx_desc->bufPhysAddr = virt_to_phys(skb->head); + atomic_dec(&pool->in_use); + STAT_DBG(pool->stats.skb_recycled_ok++); + } else { + txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = ((MV_ULONG) skb | MV_ETH_SHADOW_SKB); + if (pool && (atomic_read(&pool->in_use) > 0)) + STAT_DBG(pool->stats.skb_recycled_err++); + } +#else + txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = ((MV_ULONG) skb | MV_ETH_SHADOW_SKB); +#endif /* CONFIG_MV_ETH_BM_CPU && CONFIG_MV_NETA_SKB_RECYCLE */ + if (tx_spec.flags & MV_ETH_F_NO_PAD) tx_cmd |= NETA_TX_F_DESC_MASK | NETA_TX_L_DESC_MASK; else @@ -2044,7 +2045,6 @@ tx_desc->command = tx_cmd; mv_eth_tx_desc_flush(pp, tx_desc); - txq_ctrl->shadow_txq[txq_ctrl->shadow_txq_put_i] = ((MV_ULONG) skb | MV_ETH_SHADOW_SKB); mv_eth_shadow_inc_put(txq_ctrl); } else { @@ -2074,7 +2074,7 @@ printk(KERN_ERR "%s - eth_tx_%lu: cpu=%d, in_intr=0x%lx, port=%d, txp=%d, txq=%d\n", dev->name, dev->stats.tx_packets, smp_processor_id(), in_interrupt(), pp->port, tx_spec.txp, tx_spec.txq); - printk(KERN_ERR "\t skb=%p, head=%p, data=%p, size=%d\n", skb, skb->head, skb->data, skb->len); + pr_info("\t skb=%p, head=%p, data=%p, size=%d\n", skb, skb->head, skb->data, skb_len); mv_eth_tx_desc_print(tx_desc); /*mv_eth_skb_print(skb);*/ mvDebugMemDump(skb->data, 64, 1); @@ -2083,7 +2083,7 @@ #ifdef CONFIG_MV_PON if (MV_PON_PORT(pp->port)) - mvNetaPonTxqBytesAdd(pp->port, tx_spec.txp, tx_spec.txq, skb->len); + mvNetaPonTxqBytesAdd(pp->port, tx_spec.txp, tx_spec.txq, skb_len); #endif /* CONFIG_MV_PON */ /* Enable transmit */ @@ -2101,7 +2101,7 @@ dev_kfree_skb_any(skb); } -#ifndef CONFIG_MV_ETH_TXDONE_ISR +#ifndef CONFIG_MV_NETA_TXDONE_ISR if (txq_ctrl) { if (txq_ctrl->txq_count >= mv_ctrl_txdone) { u32 tx_done = mv_eth_txq_done(pp, txq_ctrl); @@ -2110,13 +2110,13 @@ } /* If after calling mv_eth_txq_done, txq_ctrl->txq_count equals frags, we need to set the timer */ - if ((txq_ctrl->txq_count == frags) && (frags > 0)) { + if ((txq_ctrl->txq_count > 0) && (txq_ctrl->txq_count <= frags) && (frags > 0)) { struct cpu_ctrl *cpuCtrl = pp->cpu_config[smp_processor_id()]; mv_eth_add_tx_done_timer(cpuCtrl); } } -#endif /* CONFIG_MV_ETH_TXDONE_ISR */ +#endif /* CONFIG_MV_NETA_TXDONE_ISR */ if (txq_ctrl) mv_eth_unlock(txq_ctrl, flags); @@ -2409,9 +2409,9 @@ static void mv_eth_rxq_drop_pkts(struct eth_port *pp, int rxq) { struct neta_rx_desc *rx_desc; - struct eth_pbuf *pkt; + struct sk_buff *skb; struct bm_pool *pool; - int rx_done, i; + int rx_done, i, pool_id; MV_NETA_RXQ_CTRL *rx_ctrl = pp->rxq_ctrl[rxq].q; if (rx_ctrl == NULL) @@ -2428,9 +2428,10 @@ mvNetaRxqDescSwap(rx_desc); #endif /* MV_CPU_BE */ - pkt = (struct eth_pbuf *)rx_desc->bufCookie; - pool = &mv_eth_pool[pkt->pool]; - mv_eth_rxq_refill(pp, rxq, pkt, pool, rx_desc); + skb = (struct sk_buff *)rx_desc->bufCookie; + pool_id = NETA_RX_GET_BPID(rx_desc); + pool = &mv_eth_pool[pool_id]; + mv_eth_rxq_refill(pp, rxq, pool, skb, rx_desc); } if (rx_done) { mv_neta_wmb(); @@ -2585,16 +2586,18 @@ #ifndef CONFIG_MV_ETH_BM_CPU } #else + if (MV_NETA_BM_CAP()) { if (pp->pool_long->buf_num == 0) - mvBmPoolDisable(pp->pool_long->pool); + mvNetaBmPoolDisable(pp->pool_long->pool); /*empty pools*/ if (pp->pool_short && (pp->pool_long->pool != pp->pool_short->pool)) { mv_eth_pool_free(pp->pool_short->pool, pp->pool_short_num); if (pp->pool_short->buf_num == 0) - mvBmPoolDisable(pp->pool_short->pool); + mvNetaBmPoolDisable(pp->pool_short->pool); } } + } #endif /*CONFIG_MV_ETH_BM_CPU*/ return MV_OK; } @@ -2602,7 +2605,7 @@ /* Free "num" buffers from the pool */ static int mv_eth_pool_free(int pool, int num) { - struct eth_pbuf *pkt; + struct sk_buff *skb; int i = 0; struct bm_pool *ppool = &mv_eth_pool[pool]; unsigned long flags = 0; @@ -2620,7 +2623,7 @@ if (mv_eth_pool_bm(ppool)) { if (free_all) - mvBmConfigSet(MV_BM_EMPTY_LIMIT_MASK); + mvNetaBmConfigSet(MV_BM_EMPTY_LIMIT_MASK); while (i < num) { MV_U32 *va; @@ -2630,15 +2633,15 @@ break; va = phys_to_virt(pa); - pkt = (struct eth_pbuf *)*va; + skb = (struct sk_buff *)(*va); #if !defined(CONFIG_MV_ETH_BE_WA) - pkt = (struct eth_pbuf *)MV_32BIT_LE((MV_U32)pkt); + skb = (struct sk_buff *)MV_32BIT_LE((MV_U32)skb); #endif /* !CONFIG_MV_ETH_BE_WA */ - if (pkt) { - mv_eth_pkt_free(pkt); + if (skb) { + dev_kfree_skb_any(skb); #ifdef ETH_SKB_DEBUG - mv_eth_skb_check((struct sk_buff *)pkt->osInfo); + mv_eth_skb_check(skb); #endif /* ETH_SKB_DEBUG */ } i++; @@ -2647,13 +2650,13 @@ pool, ppool->pkt_size, RX_BUF_SIZE(ppool->pkt_size), i, num); if (free_all) - mvBmConfigClear(MV_BM_EMPTY_LIMIT_MASK); + mvNetaBmConfigClear(MV_BM_EMPTY_LIMIT_MASK); } #endif /* CONFIG_MV_ETH_BM_CPU */ ppool->buf_num -= num; #ifdef CONFIG_MV_ETH_BM - mvBmPoolBufNumUpdate(pool, num, 0); + mvNetaBmPoolBufNumUpdate(pool, num, 0); #endif /* Free buffers from the pool stack too */ if (free_all) @@ -2668,11 +2671,12 @@ printk(KERN_ERR "%s: No more buffers in the stack\n", __func__); break; } - pkt = (struct eth_pbuf *)mvStackPop(ppool->stack); - if (pkt) { - mv_eth_pkt_free(pkt); + skb = (struct sk_buff *)mvStackPop(ppool->stack); + if (skb) { + dev_kfree_skb_any(skb); + #ifdef ETH_SKB_DEBUG - mv_eth_skb_check((struct sk_buff *)pkt->osInfo); + mv_eth_skb_check(skb); #endif /* ETH_SKB_DEBUG */ } i++; @@ -2701,11 +2705,13 @@ status = mvStackDelete(ppool->stack); #ifdef CONFIG_MV_ETH_BM_CPU - mvBmPoolDisable(pool); + if (MV_NETA_BM_CAP()) { + mvNetaBmPoolDisable(pool); /* Note: we don't free the bm_pool here ! */ if (ppool->bm_pool) mvOsFree(ppool->bm_pool); + } #endif /* CONFIG_MV_ETH_BM_CPU */ memset(ppool, 0, sizeof(struct bm_pool)); @@ -2718,7 +2724,7 @@ { struct bm_pool *bm_pool; struct sk_buff *skb; - struct eth_pbuf *pkt; + phys_addr_t pa; int i; unsigned long flags = 0; @@ -2747,33 +2753,32 @@ MV_ETH_LOCK(&bm_pool->lock, flags); for (i = 0; i < buf_num; i++) { - pkt = mvOsMalloc(sizeof(struct eth_pbuf)); - if (!pkt) { - printk(KERN_ERR "%s: can't allocate %d bytes\n", __func__, sizeof(struct eth_pbuf)); + skb = mv_eth_skb_alloc(pp, bm_pool, &pa, GFP_KERNEL); + if (!skb) break; - } - - skb = mv_eth_skb_alloc(pp, bm_pool, pkt, GFP_KERNEL); - if (!skb) { - kfree(pkt); - break; - } /* - printk(KERN_ERR "skb_alloc_%d: pool=%d, skb=%p, pkt=%p, head=%p (%lx), skb->truesize=%d\n", - i, bm_pool->pool, skb, pkt, pkt->pBuf, pkt->physAddr, skb->truesize); + printk(KERN_ERR "skb_alloc_%d: pool=%d, skb=%p, head=%p (%lx), skb->truesize=%d\n", + i, bm_pool->pool, skb, skb->head, pa, skb->truesize); */ #ifdef CONFIG_MV_ETH_BM_CPU - mvBmPoolPut(pool, (MV_ULONG) pkt->physAddr); + if (MV_NETA_BM_CAP()) { + /* pa is the physical addr of skb->head */ + mvBmPoolPut(pool, (MV_ULONG)pa); STAT_DBG(bm_pool->stats.bm_put++); + } else { + mvStackPush(bm_pool->stack, (MV_U32)skb); + STAT_DBG(bm_pool->stats.stack_put++); + } #else - mvStackPush(bm_pool->stack, (MV_U32) pkt); + mvStackPush(bm_pool->stack, (MV_U32)skb); STAT_DBG(bm_pool->stats.stack_put++); #endif /* CONFIG_MV_ETH_BM_CPU */ } bm_pool->buf_num += i; + bm_pool->in_use_thresh = bm_pool->buf_num / 4; #ifdef CONFIG_MV_ETH_BM - mvBmPoolBufNumUpdate(pool, i, 1); + mvNetaBmPoolBufNumUpdate(pool, i, 1); #endif printk(KERN_ERR "pool #%d: pkt_size=%d, buf_size=%d - %d of %d buffers added\n", pool, bm_pool->pkt_size, RX_BUF_SIZE(bm_pool->pkt_size), i, buf_num); @@ -2805,13 +2810,17 @@ mvOsIoCachedFree(NULL, sizeof(MV_U32) * capacity, physAddr, pVirt, 0); return NULL; } - status = mvBmPoolInit(pool, pVirt, physAddr, capacity); + status = mvNetaBmPoolInit(pool, pVirt, physAddr, capacity); if (status != MV_OK) { mvOsPrintf("%s: Can't init #%d BM pool. status=%d\n", __func__, pool, status); mvOsIoCachedFree(NULL, sizeof(MV_U32) * capacity, physAddr, pVirt, 0); return NULL; } +#ifdef CONFIG_ARCH_MVEBU + status = mvebu_mbus_get_addr_win_info(physAddr, &winInfo.targetId, &winInfo.attrib); +#else status = mvCtrlAddrWinInfoGet(&winInfo, physAddr); +#endif if (status != MV_OK) { printk(KERN_ERR "%s: Can't map BM pool #%d. phys_addr=0x%x, status=%d\n", __func__, pool, (unsigned)physAddr, status); @@ -2818,8 +2827,8 @@ mvOsIoCachedFree(NULL, sizeof(MV_U32) * capacity, physAddr, pVirt, 0); return NULL; } - mvBmPoolTargetSet(pool, winInfo.targetId, winInfo.attrib); - mvBmPoolEnable(pool); + mvNetaBmPoolTargetSet(pool, winInfo.targetId, winInfo.attrib); + mvNetaBmPoolEnable(pool); if (pPhysAddr != NULL) *pPhysAddr = physAddr; @@ -2841,9 +2850,11 @@ memset(bm_pool, 0, sizeof(struct bm_pool)); #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { bm_pool->bm_pool = mv_eth_bm_pool_create(pool, capacity, &bm_pool->physAddr); if (bm_pool->bm_pool == NULL) return MV_FAIL; + } #endif /* CONFIG_MV_ETH_BM_CPU */ /* Create Stack as container of alloacted skbs for SKB_RECYCLE and for RXQs working without BM support */ @@ -2858,6 +2869,7 @@ bm_pool->capacity = capacity; bm_pool->pkt_size = 0; bm_pool->buf_num = 0; + atomic_set(&bm_pool->in_use, 0); spin_lock_init(&bm_pool->lock); return MV_OK; @@ -2905,8 +2917,11 @@ * Need for Aramada38x. Dosn't need for AXP and A370 */ if ((pp->plat_data->ctrl_model == MV_6810_DEV_ID) || - (pp->plat_data->ctrl_model == MV_6820_DEV_ID)) + (pp->plat_data->ctrl_model == MV_6811_DEV_ID) || + (pp->plat_data->ctrl_model == MV_6820_DEV_ID) || + (pp->plat_data->ctrl_model == MV_6828_DEV_ID)) { regVal = MV_REG_READ(NETA_INTR_NEW_MASK_REG(pp->port)); + } return IRQ_HANDLED; } @@ -2918,12 +2933,17 @@ int txp, txq, rx_port, mode; struct eth_port *pp; + if (!MV_NETA_HWF_CAP()) + return 0; + if (mvNetaPortCheck(port)) return -EINVAL; pp = mv_eth_port_by_id(port); - if (pp == NULL) + if (pp == NULL) { + pr_err("Port %d does not exist\n", port); return -ENODEV; + } for (txp = 0; txp < pp->txp_num; txp++) { for (txq = 0; txq < CONFIG_MV_ETH_TXQ; txq++) { @@ -3032,7 +3052,7 @@ } causeRxTx |= cpuCtrl->causeRxTx; -#ifdef CONFIG_MV_ETH_TXDONE_ISR +#ifdef CONFIG_MV_NETA_TXDONE_ISR if (causeRxTx & MV_ETH_TXDONE_INTR_MASK) { int tx_todo = 0; /* TX_DONE process */ @@ -3044,7 +3064,7 @@ causeRxTx &= ~MV_ETH_TXDONE_INTR_MASK; } -#endif /* CONFIG_MV_ETH_TXDONE_ISR */ +#endif /* CONFIG_MV_NETA_TXDONE_ISR */ #if (CONFIG_MV_ETH_RXQ > 1) while ((causeRxTx != 0) && (budget > 0)) { @@ -3163,13 +3183,20 @@ void mv_eth_port_promisc_set(int port) { #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { /* Accept all */ if (mv_eth_pnc_ctrl_en) { pnc_mac_me(port, NULL, CONFIG_MV_ETH_RXQ_DEF); pnc_mcast_all(port, 1); } else { - printk(KERN_ERR "%s: PNC control is disabled\n", __func__); + pr_err("%s: PNC control is disabled\n", __func__); } + } else { /* Legacy parser */ + mvNetaRxUnicastPromiscSet(port, MV_TRUE); + mvNetaSetUcastTable(port, CONFIG_MV_ETH_RXQ_DEF); + mvNetaSetSpecialMcastTable(port, CONFIG_MV_ETH_RXQ_DEF); + mvNetaSetOtherMcastTable(port, CONFIG_MV_ETH_RXQ_DEF); + } #else /* Legacy parser */ mvNetaRxUnicastPromiscSet(port, MV_TRUE); mvNetaSetUcastTable(port, CONFIG_MV_ETH_RXQ_DEF); @@ -3183,11 +3210,18 @@ #ifdef CONFIG_MV_ETH_PNC static bool is_first = true; + if (MV_NETA_PNC_CAP()) { /* clean TCAM only one, no need to do this per port. */ if (is_first) { tcam_hw_init(); is_first = false; } + } else { + mvNetaRxUnicastPromiscSet(port, MV_FALSE); + mvNetaSetUcastTable(port, -1); + mvNetaSetSpecialMcastTable(port, -1); + mvNetaSetOtherMcastTable(port, -1); + } #else mvNetaRxUnicastPromiscSet(port, MV_FALSE); mvNetaSetUcastTable(port, -1); @@ -3204,8 +3238,10 @@ /* Get compile time configuration */ #ifdef CONFIG_MV_ETH_BM - mvBmControl(MV_START); + if (MV_NETA_BM_CAP()) { + mvNetaBmControl(MV_START); mv_eth_bm_config_get(); + } #endif /* CONFIG_MV_ETH_BM */ /* Create all pools with maximum capacity */ @@ -3218,11 +3254,15 @@ return status; } #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { mv_eth_pool[i].pkt_size = mv_eth_bm_config_pkt_size_get(i); if (mv_eth_pool[i].pkt_size == 0) - mvBmPoolBufSizeSet(i, 0); + mvNetaBmPoolBufferSizeSet(i, 0); else - mvBmPoolBufSizeSet(i, RX_BUF_SIZE(mv_eth_pool[i].pkt_size)); + mvNetaBmPoolBufferSizeSet(i, RX_BUF_SIZE(mv_eth_pool[i].pkt_size)); + } else { + mv_eth_pool[i].pkt_size = 0; + } #else mv_eth_pool[i].pkt_size = 0; #endif /* CONFIG_MV_ETH_BM */ @@ -3342,14 +3382,17 @@ return -EIO; } #ifdef CONFIG_MV_ETH_HWF + if (MV_NETA_HWF_CAP()) mvNetaHwfInit(port); #endif /* CONFIG_MV_ETH_HWF */ #ifdef CONFIG_MV_ETH_PMT + if (MV_NETA_PMT_CAP()) { if (MV_PON_PORT(port)) mvNetaPmtInit(port, (MV_NETA_PMT *)ioremap(PMT_PON_PHYS_BASE, PMT_MEM_SIZE)); else mvNetaPmtInit(port, (MV_NETA_PMT *)ioremap(PMT_GIGA_PHYS_BASE + port * 0x40000, PMT_MEM_SIZE)); + } #endif /* CONFIG_MV_ETH_PMT */ pr_info("\t%s p=%d: mtu=%d, mac=" MV_MACQUAD_FMT " (%s)\n", @@ -3434,9 +3477,12 @@ MV_STATUS status; int pool = bm_pool->pool; - mvBmPoolInit(bm_pool->pool, bm_pool->bm_pool, bm_pool->physAddr, bm_pool->capacity); + mvNetaBmPoolInit(bm_pool->pool, bm_pool->bm_pool, bm_pool->physAddr, bm_pool->capacity); +#ifdef CONFIG_ARCH_MVEBU + status = mvebu_mbus_get_addr_win_info(bm_pool->physAddr, &winInfo.targetId, &winInfo.attrib); +#else status = mvCtrlAddrWinInfoGet(&winInfo, bm_pool->physAddr); - +#endif if (status != MV_OK) { printk(KERN_ERR "%s: Can't map BM pool #%d. phys_addr=0x%x, status=%d\n", __func__, bm_pool->pool, (unsigned)bm_pool->physAddr, status); @@ -3443,8 +3489,8 @@ mvOsIoCachedFree(NULL, sizeof(MV_U32) * bm_pool->capacity, bm_pool->physAddr, bm_pool->bm_pool, 0); return MV_ERROR; } - mvBmPoolTargetSet(pool, winInfo.targetId, winInfo.attrib); - mvBmPoolEnable(pool); + mvNetaBmPoolTargetSet(pool, winInfo.targetId, winInfo.attrib); + mvNetaBmPoolEnable(pool); return MV_OK; } @@ -3473,10 +3519,11 @@ #ifndef CONFIG_MV_ETH_BM_CPU } /*fill long pool */ #else + if (MV_NETA_BM_CAP()) mvNetaBmPoolBufSizeSet(pp->port, pp->pool_long->pool, RX_BUF_SIZE(pp->pool_long->pkt_size)); } - if (pp->pool_short) { + if (MV_NETA_BM_CAP() && pp->pool_short) { if (pp->pool_short->pool != pp->pool_long->pool) { /* fill short pool */ num = mv_eth_pool_add(pp, pp->pool_short->pool, pp->pool_short_num); @@ -3529,6 +3576,7 @@ mv_eth_rx_time_coal_set(pp->port, rxq, pp->rxq_ctrl[rxq].rxq_time_coal); #if defined(CONFIG_MV_ETH_BM_CPU) + if (MV_NETA_BM_CAP()) { /* Enable / Disable - BM support */ if (pp->pool_long && pp->pool_short) { @@ -3538,8 +3586,14 @@ /* To disable short pool we choose unused pool and set pkt size to 0 (buffer size = pkt offset) */ mvNetaRxqBmEnable(pp->port, rxq, dummy_short_pool, pp->pool_long->pool); } else - mvNetaRxqBmEnable(pp->port, rxq, pp->pool_short->pool, pp->pool_long->pool); + mvNetaRxqBmEnable(pp->port, rxq, pp->pool_short->pool, + pp->pool_long->pool); } + } else { + /* Fill RXQ with buffers from RX pool */ + mvNetaRxqBufSizeSet(pp->port, rxq, RX_BUF_SIZE(pp->pool_long->pkt_size)); + mvNetaRxqBmDisable(pp->port, rxq); + } #else /* Fill RXQ with buffers from RX pool */ mvNetaRxqBufSizeSet(pp->port, rxq, RX_BUF_SIZE(pp->pool_long->pkt_size)); @@ -3600,7 +3654,7 @@ pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_ERR "%s: pp == NULL, port=%d\n", __func__, port); + pr_err("Port %d does not exist\n", port); return MV_ERROR; } @@ -3620,20 +3674,23 @@ struct bm_pool *ppool; int pool; - mvBmControl(MV_START); + if (MV_NETA_BM_CAP()) { + mvNetaBmControl(MV_START); - mvBmRegsInit(); + mvNetaBmRegsInit(); for (pool = 0; pool < MV_ETH_BM_POOLS; pool++) { ppool = &mv_eth_pool[pool]; if (mv_eth_bm_pool_restore(ppool)) { - printk(KERN_ERR "%s: port #%d pool #%d resrote failed.\n", __func__, port, pool); + pr_err("%s: port #%d pool #%d resrote failed.\n", __func__, port, pool); return MV_ERROR; } } + } #endif /*CONFIG_MV_ETH_BM*/ #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) mv_eth_pnc_resume(); #endif /* CONFIG_MV_ETH_PNC */ @@ -3672,7 +3729,15 @@ void mv_eth_hal_shared_init(struct mv_neta_pdata *plat_data) { MV_NETA_HAL_DATA halData; + MV_U32 bm_phy_base, bm_size; + MV_U32 pnc_phy_base, pnc_size; + int ret; + unsigned int dev = 0, rev = 0; + bm_phy_base = 0; + pnc_phy_base = 0; + bm_size = 0; + pnc_size = 0; memset(&halData, 0, sizeof(halData)); halData.maxPort = plat_data->max_port; @@ -3683,6 +3748,46 @@ halData.iocc = arch_is_coherent(); halData.ctrlModel = plat_data->ctrl_model; halData.ctrlRev = plat_data->ctrl_rev; + +#ifdef CONFIG_ARCH_MVEBU + dev = plat_data->ctrl_model; + rev = plat_data->ctrl_rev; + + if (MV_NETA_BM_CAP()) { + ret = mvebu_mbus_win_addr_get(MV_BM_WIN_ID, MV_BM_WIN_ATTR, &bm_phy_base, &bm_size); + if (ret) { + pr_err("%s: get BM mbus window failed, error: %d.\n", __func__, ret); + return; + } + halData.bmPhysBase = bm_phy_base; + halData.bmVirtBase = (MV_U8 *)ioremap(bm_phy_base, bm_size); + } + if (MV_NETA_PNC_CAP()) { + /* on AXP, PNC and BM share the same window */ + if ((dev == MV78230_DEV_ID) + || (dev == MV78260_DEV_ID) + || (dev == MV78460_DEV_ID)) { + if (!MV_NETA_BM_CAP()) { + ret = mvebu_mbus_win_addr_get(MV_BM_WIN_ID, MV_BM_WIN_ATTR, &bm_phy_base, &bm_size); + if (ret) { + pr_err("%s: get BM mbus window failed, error: %d.\n", __func__, ret); + return; + } + } + halData.pncPhysBase = bm_phy_base; + halData.pncVirtBase = (MV_U8 *)ioremap(bm_phy_base, bm_size); + } else { + ret = mvebu_mbus_win_addr_get(MV_PNC_WIN_ID, MV_PNC_WIN_ATTR, &pnc_phy_base, &pnc_size); + if (ret) { + pr_err("%s: get PNC mbus window failed, error: %d.\n", __func__, ret); + return; + } + halData.pncPhysBase = pnc_phy_base; + halData.pncVirtBase = (MV_U8 *)ioremap(pnc_phy_base, pnc_size); + } + halData.pncTcamSize = plat_data->pnc_tcam_size; + } +#else #ifdef CONFIG_MV_ETH_BM halData.bmPhysBase = PNC_BM_PHYS_BASE; halData.bmVirtBase = (MV_U8 *)ioremap(PNC_BM_PHYS_BASE, PNC_BM_SIZE); @@ -3689,10 +3794,13 @@ #endif /* CONFIG_MV_ETH_BM */ #ifdef CONFIG_MV_ETH_PNC + halData.pncTcamSize = plat_data->pnc_tcam_size; halData.pncPhysBase = PNC_BM_PHYS_BASE; halData.pncVirtBase = (MV_U8 *)ioremap(PNC_BM_PHYS_BASE, PNC_BM_SIZE); #endif /* CONFIG_MV_ETH_PNC */ +#endif /* CONFIG_ARCH_MVEBU */ + mvNetaHalInit(&halData); return; @@ -3704,58 +3812,46 @@ * Win initilization * ***********************************************************/ #ifdef CONFIG_ARCH_MVEBU -void mv_eth_win_init(int port) +int mv_eth_win_set(int port, u32 base, u32 size, u8 trg_id, u8 win_attr, u32 *enable, u32 *protection) { - const struct mbus_dram_target_info *dram; - int i; - u32 enable = 0, protection = 0; + u32 baseReg, sizeReg; + u32 alignment; + u8 i, attr; - /* First disable all address decode windows */ - enable = (1 << ETH_MAX_DECODE_WIN) - 1; - MV_REG_WRITE(ETH_BASE_ADDR_ENABLE_REG(port), enable); - - /* Clear Base/Size/Remap registers for all windows */ + /* Find the windown not enabled */ for (i = 0; i < ETH_MAX_DECODE_WIN; i++) { - MV_REG_WRITE(ETH_WIN_BASE_REG(port, i), 0); - MV_REG_WRITE(ETH_WIN_SIZE_REG(port, i), 0); - - if (i < ETH_MAX_HIGH_ADDR_REMAP_WIN) - MV_REG_WRITE(ETH_WIN_REMAP_REG(port, i), 0); + if (*enable & (1 << i)) + break; } - - dram = mv_mbus_dram_info(); - if (!dram) { - pr_err("%s: No DRAM information\n", __func__); - return; + if (i == ETH_MAX_DECODE_WIN) { + pr_err("%s: No window is available\n", __func__); + return MV_ERROR; } - for (i = 0; i < dram->num_cs; i++) { - const struct mbus_dram_window *cs = dram->cs + i; - u32 baseReg, base = cs->base; - u32 sizeReg, size = cs->size; - u32 alignment; - u8 attr = cs->mbus_attr; - u8 target = dram->mbus_dram_target_id; /* check if address is aligned to the size */ if (MV_IS_NOT_ALIGN(base, size)) { - pr_err("%s: Error setting window for cs #%d.\n" + pr_err("%s: Error setting eth port%d window%d.\n" "Address 0x%08x is not aligned to size 0x%x.\n", - __func__, i, base, size); - return; + __func__, port, i, base, size); + return MV_ERROR; } if (!MV_IS_POWER_OF_2(size)) { - pr_err("%s: Error setting window for cs #%d.\n" + pr_err("%s:Error setting eth port%d window%d.\n" "Window size %u is not a power to 2.\n", - __func__, i, size); - return; + __func__, port, i, size); + return MV_ERROR; } + attr = win_attr; + #ifdef CONFIG_MV_SUPPORT_L2_DEPOSIT + if (trg_id == TARGET_DDR) { /* Setting DRAM windows attribute to : 0x3 - Shared transaction + L2 write allocate (L2 Deposit) */ attr &= ~(0x30); attr |= 0x30; + } #endif baseReg = (base & ETH_WIN_BASE_MASK); @@ -3772,16 +3868,144 @@ /* set target ID */ baseReg &= ~ETH_WIN_TARGET_MASK; - baseReg |= target << ETH_WIN_TARGET_OFFS; + baseReg |= trg_id << ETH_WIN_TARGET_OFFS; MV_REG_WRITE(ETH_WIN_BASE_REG(port, i), baseReg); MV_REG_WRITE(ETH_WIN_SIZE_REG(port, i), sizeReg); - enable &= ~(1 << i); - protection |= (FULL_ACCESS << (i * 2)); + *enable &= ~(1 << i); + *protection |= (FULL_ACCESS << (i * 2)); + + return MV_OK; } + +int mv_eth_pnc_win_get(u32 *phyaddr_base, u32 *win_size) +{ + static bool first_time = true; + int ret = 0; + + /* get addr from fdt only once, since during eth resume (S2RAM feature), it might crash system */ + if (first_time) { + ret = mvebu_mbus_win_addr_get(MV_PNC_WIN_ID, MV_PNC_WIN_ATTR, phyaddr_base, win_size); + pnc_phyaddr_base = *phyaddr_base; + pnc_win_size = *win_size; + first_time = false; + } else { + *phyaddr_base = pnc_phyaddr_base; + *win_size = pnc_win_size; + } + return ret; +} + +int mv_eth_bm_win_get(u32 *phyaddr_base, u32 *win_size) +{ + static bool first_time = true; + int ret = 0; + + /* get addr from fdt only once, since during eth resume (S2RAM feature), it might crash system */ + if (first_time) { + ret = mvebu_mbus_win_addr_get(MV_BM_WIN_ID, MV_BM_WIN_ATTR, phyaddr_base, win_size); + bm_phyaddr_base = *phyaddr_base; + bm_win_size = *win_size; + first_time = false; + } else { + *phyaddr_base = bm_phyaddr_base; + *win_size = bm_win_size; + } + return ret; +} + +void mv_eth_win_init(int port) +{ + const struct mbus_dram_target_info *dram; + u32 phyaddr_base, win_size; + int i, ret; + u32 enable = 0, protect = 0; + unsigned int dev, rev; + + /* Get SoC ID */ + if (mvebu_get_soc_id(&dev, &rev)) + return; + + /* First disable all address decode windows */ + enable = (1 << ETH_MAX_DECODE_WIN) - 1; + MV_REG_WRITE(ETH_BASE_ADDR_ENABLE_REG(port), enable); + + /* Clear Base/Size/Remap registers for all windows */ + for (i = 0; i < ETH_MAX_DECODE_WIN; i++) { + MV_REG_WRITE(ETH_WIN_BASE_REG(port, i), 0); + MV_REG_WRITE(ETH_WIN_SIZE_REG(port, i), 0); + + if (i < ETH_MAX_HIGH_ADDR_REMAP_WIN) + MV_REG_WRITE(ETH_WIN_REMAP_REG(port, i), 0); + } + + /* set dram window */ + dram = mv_mbus_dram_info(); + if (!dram) { + pr_err("%s: No DRAM information\n", __func__); + return; + } + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; + ret = mv_eth_win_set(port, cs->base, cs->size, dram->mbus_dram_target_id, cs->mbus_attr, + &enable, &protect); + if (ret) { + pr_err("%s: eth window set fail\n", __func__); + return; + } + } + + /* set BM and PnC window */ + if (MV_NETA_BM_CAP()) { + ret = mv_eth_bm_win_get(&phyaddr_base, &win_size); + if (ret) { + pr_err("%s: BM window addr info get fail\n", __func__); + return; + } + ret = mv_eth_win_set(port, phyaddr_base, win_size, MV_BM_WIN_ID, MV_BM_WIN_ATTR, + &enable, &protect); + if (ret) { + pr_err("%s: BM window set fail\n", __func__); + return; + } + } + + if (MV_NETA_PNC_CAP()) { + /* on AXP, PNC and BM share the same window */ + if ((dev == MV78230_DEV_ID) + || (dev == MV78260_DEV_ID) + || (dev == MV78460_DEV_ID)) { + if (!MV_NETA_BM_CAP()) { + ret = mv_eth_bm_win_get(&phyaddr_base, &win_size); + if (ret) { + pr_err("%s: BM window addr info get fail\n", __func__); + return; + } + ret = mv_eth_win_set(port, phyaddr_base, win_size, MV_BM_WIN_ID, MV_BM_WIN_ATTR, + &enable, &protect); + if (ret) { + pr_err("%s: BM window set fail\n", __func__); + return; + } + } + } else { + ret = mv_eth_pnc_win_get(&phyaddr_base, &win_size); + if (ret) { + pr_err("%s: PNC window addr info get fail\n", __func__); + return; + } + ret = mv_eth_win_set(port, phyaddr_base, win_size, MV_PNC_WIN_ID, MV_PNC_WIN_ATTR, + &enable, &protect); + if (ret) { + pr_err("%s: PNC window set fail\n", __func__); + return; + } + } + } + /* Set window protection */ - MV_REG_WRITE(ETH_ACCESS_PROTECT_REG(port), protection); + MV_REG_WRITE(ETH_ACCESS_PROTECT_REG(port), protect); /* Enable window */ MV_REG_WRITE(ETH_BASE_ADDR_ENABLE_REG(port), enable); } @@ -3823,6 +4047,7 @@ int mv_eth_port_suspend(int port) { struct eth_port *pp; + int txp; pp = mv_eth_port_by_id(port); @@ -3846,14 +4071,17 @@ #ifdef CONFIG_MV_ETH_HWF + if (MV_NETA_HWF_CAP()) { mvNetaHwfEnable(pp->port, 0); + } else { + /* Reset TX port, transmit all pending packets */ + for (txp = 0; txp < pp->txp_num; txp++) + mv_eth_txp_reset(pp->port, txp); + } #else - { - int txp; /* Reset TX port, transmit all pending packets */ for (txp = 0; txp < pp->txp_num; txp++) mv_eth_txp_reset(pp->port, txp); - } #endif /* !CONFIG_MV_ETH_HWF */ /* Reset RX port, free the empty buffers form queue */ @@ -3876,7 +4104,7 @@ struct eth_port *pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_ERR "%s: pp == NULL, port=%d\n", __func__, port); + pr_err("Port %d does not exist\n", port); return -EINVAL; } @@ -3917,18 +4145,22 @@ #endif #ifdef CONFIG_MV_ETH_PNC_WOL + if (MV_NETA_PNC_CAP()) mv_neta_wol_sysfs_exit(&pd->kobj); #endif #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) mv_neta_pnc_sysfs_exit(&pd->kobj); #endif #ifdef CONFIG_MV_ETH_BM + if (MV_NETA_BM_CAP()) mv_neta_bm_sysfs_exit(&pd->kobj); #endif #ifdef CONFIG_MV_ETH_HWF + if (MV_NETA_HWF_CAP()) mv_neta_hwf_sysfs_exit(&pd->kobj); #endif @@ -3960,18 +4192,22 @@ #endif #ifdef CONFIG_MV_ETH_HWF + if (MV_NETA_HWF_CAP()) mv_neta_hwf_sysfs_init(&pd->kobj); #endif #ifdef CONFIG_MV_ETH_BM + if (MV_NETA_BM_CAP()) mv_neta_bm_sysfs_init(&pd->kobj); #endif #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) mv_neta_pnc_sysfs_init(&pd->kobj); #endif #ifdef CONFIG_MV_ETH_PNC_WOL + if (MV_NETA_PNC_CAP()) mv_neta_wol_sysfs_init(&pd->kobj); #endif @@ -3997,6 +4233,9 @@ mv_eth_sysfs_init(); + pr_info("SoC: model = 0x%x, revision = 0x%x\n", + plat_data->ctrl_model, plat_data->ctrl_rev); + /* init MAC Unit */ mv_eth_hal_shared_init(plat_data); @@ -4017,11 +4256,19 @@ goto oom; #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { + /* init gbe pnc port mapping */ + if (pnc_gbe_port_map_init(plat_data->ctrl_model, plat_data->ctrl_rev)) { + pr_err("%s: PNC GBE port mapping init failed\n", __func__); + goto oom; + } + if (mv_eth_pnc_ctrl_en) { if (pnc_default_init()) - printk(KERN_ERR "%s: Warning PNC init failed\n", __func__); + pr_err("%s: Warning PNC init failed\n", __func__); } else - printk(KERN_ERR "%s: PNC control is disabled\n", __func__); + pr_err("%s: PNC control is disabled\n", __func__); + } #endif /* CONFIG_MV_ETH_PNC */ #ifdef CONFIG_MV_ETH_L2FW @@ -4043,8 +4290,158 @@ } #ifdef CONFIG_OF +static int pnc_bm_initialize; +static unsigned int pnc_tcam_line_num; static int mv_eth_port_num_get(struct platform_device *pdev); +static int mv_eth_neta_cap_verify(unsigned int neta_cap_bm) +{ + unsigned int dev, rev; + + /* Get SoC ID */ + if (mvebu_get_soc_id(&dev, &rev)) + return MV_FAIL; + + /* According to SoC type to check dynamic neta capabilities */ + switch (dev) { + /* Armada 370 ID */ + case MV6710_DEV_ID: + case MV6707_DEV_ID: + if (neta_cap_bm == 0) + return 0; + else + goto err; + break; + + /* Armada XP ID */ + case MV78230_DEV_ID: + case MV78260_DEV_ID: + case MV78460_DEV_ID: + /* KW2 ID */ + case MV88F6510_DEV_ID: + case MV88F6530_DEV_ID: + case MV88F6560_DEV_ID: + case MV88F6601_DEV_ID: + if (neta_cap_bm == (MV_ETH_CAP_PNC | MV_ETH_CAP_BM | MV_ETH_CAP_HWF | MV_ETH_CAP_PME) || + neta_cap_bm == (MV_ETH_CAP_PNC | MV_ETH_CAP_BM | MV_ETH_CAP_HWF) || + neta_cap_bm == (MV_ETH_CAP_PNC | MV_ETH_CAP_BM) || + neta_cap_bm == MV_ETH_CAP_PNC || + neta_cap_bm == MV_ETH_CAP_BM || + neta_cap_bm == 0) + return 0; + else + goto err; + break; + + /* Armada A38x ID */ + case MV88F6810_DEV_ID: + case MV88F6811_DEV_ID: + case MV88F6820_DEV_ID: + case MV88F6828_DEV_ID: + if (((rev == MV88F68xx_Z1_REV) && (neta_cap_bm == MV_ETH_CAP_BM || neta_cap_bm == 0)) || + ((rev == MV88F68xx_A0_REV) && (neta_cap_bm == (MV_ETH_CAP_PNC | MV_ETH_CAP_BM) || + neta_cap_bm == MV_ETH_CAP_PNC || + neta_cap_bm == MV_ETH_CAP_BM || + neta_cap_bm == 0))) + return 0; + else + goto err; + break; + + default: + goto err; + break; + } + + return 0; + +err: + pr_err("Error: invalid NETA capability 0x%x for SoC with dev_id-0x%x, rev_id-%d\n", neta_cap_bm, dev, rev); + return -1; +} + +static struct of_device_id of_bm_pnc_table[] = { + { .compatible = "marvell,neta_bm_pnc" }, +}; + +static int mv_eth_pnc_bm_init(struct mv_neta_pdata *plat_data) +{ + struct device_node *bm_pnc_np; + struct clk *clk; + unsigned int dev, rev; + + if (pnc_bm_initialize > 0) { + if (plat_data) + plat_data->pnc_tcam_size = pnc_tcam_line_num; + return MV_OK; + } + + /* Get SoC ID */ + if (mvebu_get_soc_id(&dev, &rev)) + return MV_FAIL; + + /* BM&PNC memory iomap */ + bm_pnc_np = of_find_matching_node(NULL, of_bm_pnc_table); + if (bm_pnc_np) { + /* Get NETA dynamic capabilities supported */ + if (of_property_read_u32(bm_pnc_np, "neta_cap_bm", &neta_cap_bitmap)) { + pr_err("could not get bitmap of neta capability\n"); + return -1; + } + /* NETA dynamic capability verify */ + if (mv_eth_neta_cap_verify(neta_cap_bitmap)) { + pr_err("NETA capability verify not pass\n"); + return -1; + } + + /* Get PnC TCAM line number */ + if (MV_NETA_PNC_CAP() && plat_data != NULL) { + if (of_property_read_u32(bm_pnc_np, "pnc_tcam_size", &pnc_tcam_line_num)) { + pr_err("could not get pnc tcam size\n"); + return -1; + } + plat_data->pnc_tcam_size = pnc_tcam_line_num; + } + + if (MV_NETA_BM_CAP()) { + /* IO map */ + bm_reg_vbase = (int)of_iomap(bm_pnc_np, 0); + + /* Enable BM gate clock */ + clk = of_clk_get(bm_pnc_np, 0); + + clk_prepare_enable(clk); + } + + if (MV_NETA_PNC_CAP()) { + /* IO map */ + pnc_reg_vbase = (int)of_iomap(bm_pnc_np, 1); + /* on AXP, PNC and BM share the same clock */ + if ((dev == MV78230_DEV_ID) + || (dev == MV78260_DEV_ID) + || (dev == MV78460_DEV_ID)) { + /* Enable BM gate clock */ + if (!MV_NETA_BM_CAP()) { + clk = of_clk_get(bm_pnc_np, 0); + clk_prepare_enable(clk); + } + } else { + /* Enable PNC gate clock */ + clk = of_clk_get(bm_pnc_np, 1); + clk_prepare_enable(clk); + } + } + } else { + bm_reg_vbase = 0; + pnc_reg_vbase = 0; + neta_cap_bitmap = 0; + } + + pnc_bm_initialize++; + + return MV_OK; +} + static struct mv_neta_pdata *mv_plat_data_get(struct platform_device *pdev) { struct mv_neta_pdata *plat_data; @@ -4055,6 +4452,7 @@ struct clk *clk; phy_interface_t phy_mode; const char *mac_addr = NULL; + u32 ctrl_model, ctrl_rev; /* Get port number */ if (of_property_read_u32(np, "eth,port-num", &pdev->id)) { @@ -4129,6 +4527,12 @@ if (of_property_read_u32(np, "tx-csum-limit", &plat_data->tx_csum_limit)) plat_data->tx_csum_limit = MV_ETH_TX_CSUM_MAX_SIZE; + /* Initialize PnC and BM module */ + if (mv_eth_pnc_bm_init(plat_data)) { + pr_err("pnc and bm init fail\n"); + return NULL; + } + /* Get port PHY mode */ phy_mode = of_get_phy_mode(np); if (phy_mode < 0) { @@ -4155,10 +4559,23 @@ plat_data->max_port = mv_eth_port_num_get(pdev); /* Per port parameters */ - plat_data->cpu_mask = 0x3; + plat_data->cpu_mask = (1 << nr_cpu_ids) - 1; plat_data->duplex = DUPLEX_FULL; - plat_data->speed = MV_ETH_SPEED_AN; + /*if eth port is connect to switch, then we should force its speed to 1gps and force it linked up*/ + if (plat_data->phy_addr == -1) + plat_data->speed = SPEED_1000; + else + plat_data->speed = 0; + + /* Get SoC ID */ + if (mvebu_get_soc_id(&ctrl_model, &ctrl_rev)) { + mvOsPrintf("%s: get soc_id failed\n", __func__); + return NULL; + } + plat_data->ctrl_model = ctrl_model; + plat_data->ctrl_rev = ctrl_rev; + pdev->dev.platform_data = plat_data; clk = devm_clk_get(&pdev->dev, 0); @@ -4189,6 +4606,26 @@ return -ENODEV; } plat_data->irq = res->start; + + /* Initialize NETA capability bitmap */ + neta_cap_bitmap = 0x0; + +#ifdef CONFIG_MV_ETH_PNC + neta_cap_bitmap |= MV_ETH_CAP_PNC; +#endif + +#ifdef CONFIG_MV_ETH_BM + neta_cap_bitmap |= MV_ETH_CAP_BM; +#endif + +#ifdef CONFIG_MV_ETH_HWF + neta_cap_bitmap |= MV_ETH_CAP_HWF; +#endif + +#ifdef CONFIG_MV_ETH_PME + neta_cap_bitmap |= MV_ETH_CAP_PME; +#endif + #endif /* CONFIG_OF */ if (plat_data == NULL) @@ -4196,6 +4633,7 @@ port = pdev->id; if (!mv_eth_initialized) { + neta_global_dev = &pdev->dev; if (mv_eth_shared_probe(plat_data)) return -ENODEV; } @@ -4469,7 +4907,7 @@ #endif #ifdef CONFIG_MV_NETA_SKB_RECYCLE - pr_info(" o SKB recycle supported (%s)\n", mv_ctrl_recycle ? "Enabled" : "Disabled"); + pr_info(" o SKB recycle supported for SWF (%s)\n", mv_ctrl_swf_recycle ? "Enabled" : "Disabled"); #endif #ifdef CONFIG_MV_ETH_NETA @@ -4477,18 +4915,22 @@ #endif #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) pr_info(" o BM supported for CPU: %d BM pools\n", MV_ETH_BM_POOLS); #endif /* CONFIG_MV_ETH_BM_CPU */ #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) pr_info(" o PnC supported (%s)\n", mv_eth_pnc_ctrl_en ? "Enabled" : "Disabled"); #endif #ifdef CONFIG_MV_ETH_HWF + if (MV_NETA_HWF_CAP()) pr_info(" o HWF supported\n"); #endif #ifdef CONFIG_MV_ETH_PMT + if (MV_NETA_PMT_CAP()) pr_info(" o PMT supported\n"); #endif @@ -4511,10 +4953,6 @@ pr_info(" o Transmit checksum offload supported\n"); #endif -#if defined(CONFIG_MV_ETH_NFP) - pr_info(" o NFP is supported\n"); -#endif /* CONFIG_MV_ETH_NFP */ - #if defined(CONFIG_MV_ETH_NFP_HOOKS) pr_info(" o NFP Hooks are supported\n"); #endif /* CONFIG_MV_ETH_NFP_HOOKS */ @@ -4564,6 +5002,7 @@ #if defined(MV_ETH_PNC_LB) && defined(CONFIG_MV_ETH_PNC) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (MV_NETA_PNC_CAP()) dev->hw_features |= NETIF_F_RXHASH; #endif #endif @@ -4602,7 +5041,7 @@ { struct eth_port *pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_ERR "%s: pp == NULL, port=%d\n", __func__, port); + pr_err("Port %d does not exist\n", port); return -1; } @@ -4682,7 +5121,7 @@ struct eth_port *pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_ERR "%s: pp == NULL\n", __func__); + pr_err("Port %d does not exist\n", port); return; } for (group = 0; group < CONFIG_MV_ETH_NAPI_GROUPS; group++) { @@ -4713,6 +5152,7 @@ struct bm_pool *bm_pool, *temp_pool = NULL; unsigned long flags = 0; + if (MV_NETA_BM_CAP()) { pool = mv_eth_bm_config_long_pool_get(pp->port); if (pool != -1) /* constant long pool for the port */ return &mv_eth_pool[pool]; @@ -4740,7 +5180,10 @@ MV_ETH_UNLOCK(&bm_pool->lock, flags); } return temp_pool; + } else { + return &mv_eth_pool[pp->port]; } +} #else static struct bm_pool *mv_eth_long_pool_get(struct eth_port *pp, int pkt_size) { @@ -4748,15 +5191,14 @@ } #endif /* CONFIG_MV_ETH_BM_CPU */ -static int mv_eth_rxq_fill(struct eth_port *pp, int rxq, int num) +static int mv_eth_no_bm_cpu_rxq_fill(struct eth_port *pp, int rxq, int num) { int i; - -#ifndef CONFIG_MV_ETH_BM_CPU - struct eth_pbuf *pkt; + struct sk_buff *skb; struct bm_pool *bm_pool; MV_NETA_RXQ_CTRL *rx_ctrl; struct neta_rx_desc *rx_desc; + phys_addr_t pa; bm_pool = pp->pool_long; @@ -4767,12 +5209,12 @@ } for (i = 0; i < num; i++) { - pkt = mv_eth_pool_get(pp, bm_pool); - if (pkt) { + skb = mv_eth_pool_get(pp, bm_pool); + if (skb) { rx_desc = (struct neta_rx_desc *)MV_NETA_QUEUE_DESC_PTR(&rx_ctrl->queueCtrl, i); memset(rx_desc, 0, sizeof(struct neta_rx_desc)); - - mvNetaRxDescFill(rx_desc, pkt->physAddr, (MV_U32)pkt); + pa = virt_to_phys(skb->head); + mvNetaRxDescFill(rx_desc, (MV_U32)pa, (MV_U32)skb); mvOsCacheLineFlush(pp->dev->dev.parent, rx_desc); } else { printk(KERN_ERR "%s: rxq %d, %d of %d buffers are filled\n", __func__, rxq, i, num); @@ -4779,8 +5221,21 @@ break; } } + + return i; +} + +static int mv_eth_rxq_fill(struct eth_port *pp, int rxq, int num) +{ + int i; + +#ifndef CONFIG_MV_ETH_BM_CPU + i = mv_eth_no_bm_cpu_rxq_fill(pp, rxq, num); #else + if (MV_NETA_BM_CAP()) i = num; + else + i = mv_eth_no_bm_cpu_rxq_fill(pp, rxq, num); #endif /* CONFIG_MV_ETH_BM_CPU */ mvNetaRxqNonOccupDescAdd(pp->port, rxq, i); @@ -4807,6 +5262,7 @@ txq_ctrl->shadow_txq_get_i = 0; #ifdef CONFIG_MV_ETH_HWF + if (MV_NETA_HWF_CAP()) mvNetaHwfTxqInit(pp->port, txq_ctrl->txp, txq_ctrl->txq); #endif /* CONFIG_MV_ETH_HWF */ @@ -4874,9 +5330,18 @@ /* Free all packets pending transmit from all TXQs and reset TX port */ int mv_eth_txp_reset(int port, int txp) { - struct eth_port *pp = mv_eth_port_by_id(port); + struct eth_port *pp; int queue; + if (mvNetaTxpCheck(port, txp)) + return -EINVAL; + + pp = mv_eth_port_by_id(port); + if (pp == NULL) { + pr_err("Port %d does not exist\n", port); + return -ENODEV; + } + if (pp->flags & MV_ETH_F_STARTED) { printk(KERN_ERR "Port %d must be stopped before\n", port); return -EINVAL; @@ -4898,7 +5363,7 @@ txq_ctrl->shadow_txq_get_i = 0; } #ifdef CONFIG_MV_ETH_HWF - else if (mode == MV_ETH_TXQ_HWF) + else if (mode == MV_ETH_TXQ_HWF && MV_NETA_HWF_CAP()) mv_eth_txq_hwf_clean(pp, txq_ctrl, rx_port); #endif /* CONFIG_MV_ETH_HWF */ else @@ -4915,6 +5380,7 @@ int mv_eth_rx_reset(int port) { struct eth_port *pp = mv_eth_port_by_id(port); + int rxq = 0; if (pp->flags & MV_ETH_F_STARTED) { printk(KERN_ERR "Port %d must be stopped before\n", port); @@ -4923,8 +5389,34 @@ #ifndef CONFIG_MV_ETH_BM_CPU { - int rxq = 0; + for (rxq = 0; rxq < CONFIG_MV_ETH_RXQ; rxq++) { + struct eth_pbuf *pkt; + struct neta_rx_desc *rx_desc; + struct bm_pool *pool; + int i, rx_done; + MV_NETA_RXQ_CTRL *rx_ctrl = pp->rxq_ctrl[rxq].q; + if (rx_ctrl == NULL) + continue; + + rx_done = mvNetaRxqFreeDescNumGet(pp->port, rxq); + mvOsCacheIoSync(pp->dev->dev.parent); + for (i = 0; i < rx_done; i++) { + rx_desc = mvNetaRxqNextDescGet(rx_ctrl); + mvOsCacheLineInv(pp->dev->dev.parent, rx_desc); + +#if defined(MV_CPU_BE) + mvNetaRxqDescSwap(rx_desc); +#endif /* MV_CPU_BE */ + + pkt = (struct eth_pbuf *)rx_desc->bufCookie; + pool = &mv_eth_pool[pkt->pool]; + mv_eth_pool_put(pool, pkt); + } + } + } +#else + if (!MV_NETA_BM_CAP()) { for (rxq = 0; rxq < CONFIG_MV_ETH_RXQ; rxq++) { struct eth_pbuf *pkt; struct neta_rx_desc *rx_desc; @@ -5055,7 +5547,8 @@ if (new_pool->pkt_size == 0) { new_pool->pkt_size = pkt_size; #ifdef CONFIG_MV_ETH_BM_CPU - mvBmPoolBufSizeSet(new_pool->pool, RX_BUF_SIZE(pkt_size)); + if (MV_NETA_BM_CAP()) + mvNetaBmPoolBufferSizeSet(new_pool->pool, RX_BUF_SIZE(pkt_size)); #endif /* CONFIG_MV_ETH_BM_CPU */ } if (new_pool->pkt_size < pkt_size) { @@ -5077,6 +5570,7 @@ } #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { mvNetaBmPoolBufSizeSet(pp->port, pp->pool_long->pool, RX_BUF_SIZE(pp->pool_long->pkt_size)); if (pp->pool_short == NULL) { @@ -5092,12 +5586,14 @@ if (pp->pool_short->pool != pp->pool_long->pool) { num = mv_eth_pool_add(pp, pp->pool_short->pool, pp->pool_short_num); if (num != pp->pool_short_num) { - printk(KERN_ERR "%s FAILED: pool=%d, pkt_size=%d - %d of %d buffers added\n", - __func__, short_pool, pp->pool_short->pkt_size, num, pp->pool_short_num); + pr_err("%s FAILED: pool=%d, pkt_size=%d - %d of %d buffers added\n", + __func__, short_pool, pp->pool_short->pkt_size, num, + pp->pool_short_num); err = -ENOMEM; goto out; } - mvNetaBmPoolBufSizeSet(pp->port, pp->pool_short->pool, RX_BUF_SIZE(pp->pool_short->pkt_size)); + mvNetaBmPoolBufSizeSet(pp->port, pp->pool_short->pool, + RX_BUF_SIZE(pp->pool_short->pkt_size)); } else { int dummy_short_pool = (pp->pool_short->pool + 1) % MV_BM_POOLS; @@ -5105,6 +5601,7 @@ mvNetaBmPoolBufSizeSet(pp->port, dummy_short_pool, NET_SKB_PAD); } } + } #endif /* CONFIG_MV_ETH_BM_CPU */ for (rxq = 0; rxq < CONFIG_MV_ETH_RXQ; rxq++) { @@ -5125,7 +5622,8 @@ mv_eth_rx_pkts_coal_set(pp->port, rxq, pp->rxq_ctrl[rxq].rxq_pkts_coal); mv_eth_rx_time_coal_set(pp->port, rxq, pp->rxq_ctrl[rxq].rxq_time_coal); -#if defined(CONFIG_MV_ETH_BM_CPU) +#ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { /* Enable / Disable - BM support */ if (pp->pool_short->pool == pp->pool_long->pool) { int dummy_short_pool = (pp->pool_short->pool + 1) % MV_BM_POOLS; @@ -5134,6 +5632,11 @@ mvNetaRxqBmEnable(pp->port, rxq, dummy_short_pool, pp->pool_long->pool); } else mvNetaRxqBmEnable(pp->port, rxq, pp->pool_short->pool, pp->pool_long->pool); + } else { + /* Fill RXQ with buffers from RX pool */ + mvNetaRxqBufSizeSet(pp->port, rxq, RX_BUF_SIZE(pkt_size)); + mvNetaRxqBmDisable(pp->port, rxq); + } #else /* Fill RXQ with buffers from RX pool */ mvNetaRxqBufSizeSet(pp->port, rxq, RX_BUF_SIZE(pkt_size)); @@ -5174,13 +5677,18 @@ } #ifdef CONFIG_MV_ETH_HWF + if (MV_NETA_HWF_CAP()) { #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) mvNetaHwfBmPoolsSet(pp->port, pp->pool_short->pool, pp->pool_long->pool); + else + mv_eth_hwf_bm_create(pp->port, RX_PKT_SIZE(mtu)); #else mv_eth_hwf_bm_create(pp->port, RX_PKT_SIZE(mtu)); #endif /* CONFIG_MV_ETH_BM_CPU */ mvNetaHwfEnable(pp->port, 1); + } #endif /* CONFIG_MV_ETH_HWF */ /* start the hal - rx/tx activity */ @@ -5210,16 +5718,21 @@ mvNetaMaxRxSizeSet(pp->port, RX_PKT_SIZE(mtu)); #ifdef CONFIG_MV_ETH_HWF + if (MV_NETA_HWF_CAP()) { #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { if (pp->pool_long && pp->pool_short) mvNetaHwfBmPoolsSet(pp->port, pp->pool_short->pool, pp->pool_long->pool); + } else { + /* TODO - update func if we want to support HWF */ + mv_eth_hwf_bm_create(pp->port, RX_PKT_SIZE(mtu)); + } #else /* TODO - update func if we want to support HWF */ mv_eth_hwf_bm_create(pp->port, RX_PKT_SIZE(mtu)); #endif /* CONFIG_MV_ETH_BM_CPU */ - - mvNetaHwfEnable(pp->port, 1); + } #endif /* CONFIG_MV_ETH_HWF */ @@ -5298,6 +5811,7 @@ mdelay(10); #ifdef CONFIG_MV_ETH_HWF + if (MV_NETA_HWF_CAP()) mvNetaHwfEnable(pp->port, 0); #endif /* CONFIG_MV_ETH_HWF */ @@ -5330,20 +5844,14 @@ int mv_eth_check_mtu_valid(struct net_device *dev, int mtu) { if (mtu < 68) { - printk(KERN_INFO "MTU must be at least 68, change mtu failed\n"); + pr_err("MTU must be at least 68, change mtu failed\n"); return -EINVAL; } if (mtu > 9676 /* 9700 - 20 and rounding to 8 */) { - printk(KERN_ERR "%s: Illegal MTU value %d, ", dev->name, mtu); + pr_err("%s: Illegal MTU value %d, ", dev->name, mtu); mtu = 9676; - printk(KERN_CONT " rounding MTU to: %d \n", mtu); + pr_cont(" rounding MTU to: %d\n", mtu); } - - if (MV_IS_NOT_ALIGN(RX_PKT_SIZE(mtu), 8)) { - printk(KERN_ERR "%s: Illegal MTU value %d, ", dev->name, mtu); - mtu = MV_ALIGN_UP(RX_PKT_SIZE(mtu), 8); - printk(KERN_CONT " rounding MTU to: %d \n", mtu); - } return mtu; } @@ -5361,7 +5869,7 @@ return -EPERM; } #ifdef CONFIG_MV_ETH_BM_CPU - if (new_pool->pkt_size < RX_PKT_SIZE(mtu)) { + if (MV_NETA_BM_CAP() && new_pool->pkt_size < RX_PKT_SIZE(mtu)) { if (mv_eth_bm_config_pkt_size_get(new_pool->pool) != 0) { printk(KERN_ERR "%s: BM pool #%d - pkt_size = %d less than required for MTU=%d and can't be changed\n", __func__, new_pool->pool, new_pool->pkt_size, mtu); @@ -5403,9 +5911,14 @@ config_pkt_size = 0; #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { new_pool = mv_eth_long_pool_get(pp, RX_PKT_SIZE(mtu)); if (new_pool != NULL) config_pkt_size = mv_eth_bm_config_pkt_size_get(new_pool->pool); + } else { + /* If BM is not used always free buffers */ + new_pool = NULL; + } #else /* If BM is not used always free buffers */ new_pool = NULL; @@ -5418,15 +5931,20 @@ mv_eth_pool_free(pp->pool_long->pool, pp->pool_long_num); #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { /* redefine pool pkt_size */ if (pp->pool_long->buf_num == 0) { pp->pool_long->pkt_size = config_pkt_size; if (pp->pool_long->pkt_size == 0) - mvBmPoolBufSizeSet(pp->pool_long->pool, 0); + mvNetaBmPoolBufferSizeSet(pp->pool_long->pool, 0); else - mvBmPoolBufSizeSet(pp->pool_long->pool, RX_BUF_SIZE(pp->pool_long->pkt_size)); + mvNetaBmPoolBufferSizeSet(pp->pool_long->pool, + RX_BUF_SIZE(pp->pool_long->pkt_size)); } + } else { + pp->pool_long->pkt_size = config_pkt_size; + } #else pp->pool_long->pkt_size = config_pkt_size; #endif /* CONFIG_MV_ETH_BM_CPU */ @@ -5454,7 +5972,23 @@ return 0; } +#ifdef CONFIG_MV_NETA_TXDONE_IN_HRTIMER /*********************************************************** + * mv_eth_tx_done_hr_timer_callback -- * + * callback for tx_done hrtimer * + ***********************************************************/ +enum hrtimer_restart mv_eth_tx_done_hr_timer_callback(struct hrtimer *timer) +{ + struct cpu_ctrl *cpuCtrl = container_of(timer, struct cpu_ctrl, tx_done_timer); + + tasklet_schedule(&cpuCtrl->tx_done_tasklet); + + return HRTIMER_NORESTART; +} +#endif + +#ifndef CONFIG_MV_NETA_TXDONE_ISR +/*********************************************************** * mv_eth_tx_done_timer_callback -- * * N msec periodic callback for tx_done * ***********************************************************/ @@ -5495,6 +6029,7 @@ if (tx_todo > 0) mv_eth_add_tx_done_timer(cpuCtrl); } +#endif /* !CONFIG_MV_NETA_TXDONE_ISR */ /*********************************************************** * mv_eth_cleanup_timer_callback -- * @@ -5502,18 +6037,22 @@ ***********************************************************/ static void mv_eth_cleanup_timer_callback(unsigned long data) { - struct cpu_ctrl *cpuCtrl; - struct net_device *dev = (struct net_device *)data; - struct eth_port *pp = MV_ETH_PRIV(dev); + struct cpu_ctrl *cpuCtrl = (struct cpu_ctrl *)data; + struct eth_port *pp = cpuCtrl->pp; + struct net_device *dev = pp->dev; STAT_INFO(pp->stats.cleanup_timer++); - cpuCtrl = pp->cpu_config[smp_processor_id()]; clear_bit(MV_ETH_F_CLEANUP_TIMER_BIT, &(cpuCtrl->flags)); if (!test_bit(MV_ETH_F_STARTED_BIT, &(pp->flags))) return; + if (cpuCtrl->cpu != smp_processor_id()) { + pr_warn("%s: Called on other CPU - %d != %d\n", __func__, cpuCtrl->cpu, smp_processor_id()); + cpuCtrl = pp->cpu_config[smp_processor_id()]; + } + /* FIXME: check bm_pool->missed and pp->rxq_ctrl[rxq].missed counters and allocate */ /* re-add timer if necessary (check bm_pool->missed and pp->rxq_ctrl[rxq].missed */ } @@ -5520,19 +6059,28 @@ void mv_eth_mac_show(int port) { - struct eth_port *pp = mv_eth_port_by_id(port); + struct eth_port *pp; + if (mvNetaPortCheck(port)) + return; + + pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_ERR "%s: port %d entry is null \n", __func__, port); + pr_err("Port %d does not exist\n", port); return; } #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { if (mv_eth_pnc_ctrl_en) { mvOsPrintf("PnC MAC Rules - port #%d:\n", port); pnc_mac_show(); } else mvOsPrintf("%s: PNC control is disabled\n", __func__); + } else {/* Legacy parser */ + mvEthPortUcastShow(port); + mvEthPortMcastShow(port); + } #else /* Legacy parser */ mvEthPortUcastShow(port); mvEthPortMcastShow(port); @@ -5541,19 +6089,35 @@ void mv_eth_vlan_prio_show(int port) { - struct eth_port *pp = mv_eth_port_by_id(port); + struct eth_port *pp; + if (mvNetaPortCheck(port)) + return; + + pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_ERR "%s: port %d entry is null \n", __func__, port); + pr_err("Port %d does not exist\n", port); return; } #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { if (mv_eth_pnc_ctrl_en) { mvOsPrintf("PnC VLAN Priority Rules - port #%d:\n", port); pnc_vlan_prio_show(port); } else mvOsPrintf("%s: PNC control is disabled\n", __func__); + } else { /* Legacy parser */ + int prio, rxq; + + mvOsPrintf("Legacy VLAN Priority Rules - port #%d:\n", port); + for (prio = 0; prio <= 0x7; prio++) { + + rxq = mvNetaVprioToRxqGet(port, prio); + if (rxq > 0) + pr_info("prio=%d: rxq=%d\n", prio, rxq); + } + } #else /* Legacy parser */ { int prio, rxq; @@ -5573,19 +6137,35 @@ { int tos, txq, cpu; struct cpu_ctrl *cpuCtrl; - struct eth_port *pp = mv_eth_port_by_id(port); + struct eth_port *pp; + if (mvNetaPortCheck(port)) + return; + + pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_ERR "%s: port %d entry is null \n", __func__, port); + pr_err("Port %d does not exist\n", port); return; } #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { if (mv_eth_pnc_ctrl_en) { mvOsPrintf("PnC TOS (DSCP) => RXQ Mapping Rules - port #%d:\n", port); pnc_ipv4_dscp_show(port); } else mvOsPrintf("%s: PNC control is disabled\n", __func__); + } else { + mvOsPrintf("Legacy TOS (DSCP) => RXQ Mapping Rules - port #%d:\n", port); + for (tos = 0; tos < 0xFF; tos += 0x4) { + int rxq; + + rxq = mvNetaTosToRxqGet(port, tos); + if (rxq > 0) + pr_err(" 0x%02x (0x%02x) => %d\n", + tos, tos >> 2, rxq); + } + } #else mvOsPrintf("Legacy TOS (DSCP) => RXQ Mapping Rules - port #%d:\n", port); for (tos = 0; tos < 0xFF; tos += 0x4) { @@ -5612,18 +6192,26 @@ int mv_eth_rxq_tos_map_set(int port, int rxq, unsigned char tos) { int status = -1; - struct eth_port *pp = mv_eth_port_by_id(port); + struct eth_port *pp; + if (mvNetaPortCheck(port)) + return -EINVAL; + + pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_ERR "%s: port %d entry is null \n", __func__, port); + pr_err("Port %d does not exist\n", port); return 1; } #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { if (mv_eth_pnc_ctrl_en) status = pnc_ip4_dscp(port, tos, 0xFF, rxq); else mvOsPrintf("%s: PNC control is disabled\n", __func__); + } else {/* Legacy parser */ + status = mvNetaTosToRxqSet(port, tos, rxq); + } #else /* Legacy parser */ status = mvNetaTosToRxqSet(port, tos, rxq); #endif /* CONFIG_MV_ETH_PNC */ @@ -5643,10 +6231,14 @@ int status = -1; #ifdef CONFIG_MV_ETH_PNC + if (MV_NETA_PNC_CAP()) { if (mv_eth_pnc_ctrl_en) status = pnc_vlan_prio_set(port, prio, rxq); else mvOsPrintf("%s: PNC control is disabled\n", __func__); + } else {/* Legacy parser */ + status = mvNetaVprioToRxqSet(port, prio, rxq); + } #else /* Legacy parser */ status = mvNetaVprioToRxqSet(port, prio, rxq); #endif /* CONFIG_MV_ETH_PNC */ @@ -5671,9 +6263,10 @@ if (mvNetaPortCheck(port)) return -EINVAL; - if ((pp == NULL) || (pp->txq_ctrl == NULL)) + if ((pp == NULL) || (pp->txq_ctrl == NULL)) { + pr_err("Port %d does not exist\n", port); return -ENODEV; - + } if ((cpu >= nr_cpu_ids) || (cpu < 0)) { printk(KERN_ERR "cpu #%d is out of range: from 0 to %d\n", cpu, nr_cpu_ids - 1); @@ -5751,6 +6344,7 @@ pp->flags = 0; #ifdef CONFIG_MV_ETH_BM_CPU + if (MV_NETA_BM_CAP()) { pp->pool_long_num = mv_eth_bm_config_long_buf_num_get(port); if (pp->pool_long_num > MV_BM_POOL_CAP_MAX) pp->pool_long_num = MV_BM_POOL_CAP_MAX; @@ -5758,6 +6352,9 @@ pp->pool_short_num = mv_eth_bm_config_short_buf_num_get(port); if (pp->pool_short_num > MV_BM_POOL_CAP_MAX) pp->pool_short_num = MV_BM_POOL_CAP_MAX; + } else { + pp->pool_long_num = CONFIG_MV_ETH_RXQ * CONFIG_MV_ETH_RXQ_DESC * 2; + } #else pp->pool_long_num = CONFIG_MV_ETH_RXQ * CONFIG_MV_ETH_RXQ_DESC * 2; #endif /* CONFIG_MV_ETH_BM_CPU */ @@ -5785,15 +6382,23 @@ for_each_possible_cpu(cpu) { cpuCtrl = pp->cpu_config[cpu]; +#if defined(CONFIG_MV_NETA_TXDONE_IN_HRTIMER) + memset(&cpuCtrl->tx_done_timer, 0, sizeof(struct hrtimer)); + hrtimer_init(&cpuCtrl->tx_done_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + cpuCtrl->tx_done_timer.function = mv_eth_tx_done_hr_timer_callback; + tasklet_init(&cpuCtrl->tx_done_tasklet, mv_eth_tx_done_timer_callback, + (unsigned long) cpuCtrl); +#elif defined(CONFIG_MV_NETA_TXDONE_IN_TIMER) memset(&cpuCtrl->tx_done_timer, 0, sizeof(struct timer_list)); cpuCtrl->tx_done_timer.function = mv_eth_tx_done_timer_callback; cpuCtrl->tx_done_timer.data = (unsigned long)cpuCtrl; init_timer(&cpuCtrl->tx_done_timer); - clear_bit(MV_ETH_F_TX_DONE_TIMER_BIT, &(cpuCtrl->flags)); +#endif memset(&cpuCtrl->cleanup_timer, 0, sizeof(struct timer_list)); cpuCtrl->cleanup_timer.function = mv_eth_cleanup_timer_callback; cpuCtrl->cleanup_timer.data = (unsigned long)cpuCtrl; init_timer(&cpuCtrl->cleanup_timer); + clear_bit(MV_ETH_F_TX_DONE_TIMER_BIT, &(cpuCtrl->flags)); clear_bit(MV_ETH_F_CLEANUP_TIMER_BIT, &(cpuCtrl->flags)); } @@ -5872,9 +6477,9 @@ printk(KERN_ERR "\nRX Pool #%d: pkt_size=%d, BM-HW support - %s\n", pool, bm_pool->pkt_size, mv_eth_pool_bm(bm_pool) ? "Yes" : "No"); - printk(KERN_ERR "bm_pool=%p, stack=%p, capacity=%d, buf_num=%d, port_map=0x%x missed=%d\n", + pr_info("bm_pool=%p, stack=%p, capacity=%d, buf_num=%d, port_map=0x%x missed=%d, in_use=%u, in_use_thresh=%u\n", bm_pool->bm_pool, bm_pool->stack, bm_pool->capacity, bm_pool->buf_num, - bm_pool->port_map, bm_pool->missed); + bm_pool->port_map, bm_pool->missed, bm_pool->in_use, bm_pool->in_use_thresh); #ifdef CONFIG_MV_ETH_STAT_ERR printk(KERN_ERR "Errors: skb_alloc_oom=%u, stack_empty=%u, stack_full=%u\n", @@ -5885,8 +6490,8 @@ pr_info(" skb_alloc_ok=%u, bm_put=%u, stack_put=%u, stack_get=%u\n", bm_pool->stats.skb_alloc_ok, bm_pool->stats.bm_put, bm_pool->stats.stack_put, bm_pool->stats.stack_get); - pr_info(" skb_recycled_ok=%u, skb_recycled_err=%u, skb_hw_cookie_err=%u\n", - bm_pool->stats.skb_recycled_ok, bm_pool->stats.skb_recycled_err, bm_pool->stats.skb_hw_cookie_err); + pr_info(" skb_recycled_ok=%u, skb_recycled_err=%u\n", + bm_pool->stats.skb_recycled_ok, bm_pool->stats.skb_recycled_err); #endif /* CONFIG_MV_ETH_STAT_DBG */ if (bm_pool->stack) @@ -5942,14 +6547,15 @@ void mv_eth_status_print(void) { - printk(KERN_ERR "totals: ports=%d\n", mv_eth_ports_num); + pr_info("totals: ports=%d\n", mv_eth_ports_num); #ifdef CONFIG_MV_NETA_SKB_RECYCLE - printk(KERN_ERR "SKB recycle = %s\n", mv_ctrl_recycle ? "Enabled" : "Disabled"); + pr_info("SKB recycle = %s\n", mv_ctrl_swf_recycle ? "Enabled" : "Disabled"); #endif /* CONFIG_MV_NETA_SKB_RECYCLE */ #ifdef CONFIG_MV_ETH_PNC - printk(KERN_ERR "PnC control = %s\n", mv_eth_pnc_ctrl_en ? "Enabled" : "Disabled"); + if (MV_NETA_PNC_CAP()) + pr_info("PnC control = %s\n", mv_eth_pnc_ctrl_en ? "Enabled" : "Disabled"); #endif /* CONFIG_MV_ETH_PNC */ } @@ -5976,13 +6582,6 @@ mv_eth_link_status_print(port); -#ifdef CONFIG_MV_ETH_NFP - printk(KERN_ERR "NFP = "); - if (pp->flags & MV_ETH_F_NFP_EN) - printk(KERN_CONT "Enabled\n"); - else - printk(KERN_CONT "Disabled\n"); -#endif /* CONFIG_MV_ETH_NFP */ if (pp->pm_mode == 1) printk(KERN_CONT "pm - wol\n"); else @@ -6032,7 +6631,7 @@ } printk(KERN_ERR "\n"); -#ifdef CONFIG_MV_ETH_TXDONE_ISR +#if defined(CONFIG_MV_NETA_TXDONE_ISR) printk(KERN_ERR "Do tx_done in NAPI context triggered by ISR\n"); for (txp = 0; txp < pp->txp_num; txp++) { printk(KERN_ERR "txcoal(pkts)[%2d.q] = ", txp); @@ -6040,10 +6639,14 @@ printk(KERN_CONT "%3d ", mvNetaTxDonePktsCoalGet(port, txp, q)); printk(KERN_CONT "\n"); } - printk(KERN_ERR "\n"); -#else - printk(KERN_ERR "Do tx_done in TX or Timer context: tx_done_threshold=%d\n", mv_ctrl_txdone); -#endif /* CONFIG_MV_ETH_TXDONE_ISR */ + pr_err("\n"); +#elif defined(CONFIG_MV_NETA_TXDONE_IN_HRTIMER) + pr_err("Do tx_done in TX or high-resolution Timer's tasklet: tx_done_threshold=%d timer_interval=%d usec\n", + mv_ctrl_txdone, mv_eth_tx_done_hrtimer_period_get()); +#elif defined(CONFIG_MV_NETA_TXDONE_IN_TIMER) + pr_err("Do tx_done in TX or regular Timer context: tx_done_threshold=%d timer_interval=%d msec\n", + mv_ctrl_txdone, CONFIG_MV_NETA_TX_DONE_TIMER_PERIOD); +#endif /* CONFIG_MV_NETA_TXDONE_ISR */ printk(KERN_ERR "txp=%d, zero_pad=%s, mh_en=%s (0x%04x), tx_cmd=0x%08x\n", pp->txp, (pp->flags & MV_ETH_F_NO_PAD) ? "Disabled" : "Enabled", @@ -6050,16 +6653,35 @@ (pp->flags & MV_ETH_F_MH) ? "Enabled" : "Disabled", pp->tx_mh, pp->hw_cmd); printk(KERN_CONT "\n"); - printk(KERN_CONT "CPU: txq causeRxTx napi txqMask txqOwner flags timer\n"); +#ifdef CONFIG_MV_NETA_TXDONE_ISR + pr_cont("CPU: txq causeRxTx napi txqMask txqOwner flags\n"); +#else + pr_cont("CPU: txq causeRxTx napi txqMask txqOwner flags timer\n"); +#endif { int cpu; for_each_possible_cpu(cpu) { cpuCtrl = pp->cpu_config[cpu]; if (cpuCtrl != NULL) - printk(KERN_ERR " %d: %d 0x%08x %d 0x%02x 0x%02x 0x%02x %d\n", - cpu, cpuCtrl->txq, cpuCtrl->causeRxTx, test_bit(NAPI_STATE_SCHED, &cpuCtrl->napi->state), +#if defined(CONFIG_MV_NETA_TXDONE_ISR) + pr_err(" %d: %d 0x%08x %d 0x%02x 0x%02x 0x%02x\n", + cpu, cpuCtrl->txq, cpuCtrl->causeRxTx, + test_bit(NAPI_STATE_SCHED, &cpuCtrl->napi->state), cpuCtrl->cpuTxqMask, cpuCtrl->cpuTxqOwner, + (unsigned)cpuCtrl->flags); +#elif defined(CONFIG_MV_NETA_TXDONE_IN_HRTIMER) + pr_err(" %d: %d 0x%08x %d 0x%02x 0x%02x 0x%02x %d\n", + cpu, cpuCtrl->txq, cpuCtrl->causeRxTx, + test_bit(NAPI_STATE_SCHED, &cpuCtrl->napi->state), + cpuCtrl->cpuTxqMask, cpuCtrl->cpuTxqOwner, + (unsigned)cpuCtrl->flags, !(hrtimer_active(&cpuCtrl->tx_done_timer))); +#elif defined(CONFIG_MV_NETA_TXDONE_IN_TIMER) + pr_err(" %d: %d 0x%08x %d 0x%02x 0x%02x 0x%02x %d\n", + cpu, cpuCtrl->txq, cpuCtrl->causeRxTx, + test_bit(NAPI_STATE_SCHED, &cpuCtrl->napi->state), + cpuCtrl->cpuTxqMask, cpuCtrl->cpuTxqOwner, (unsigned)cpuCtrl->flags, timer_pending(&cpuCtrl->tx_done_timer)); +#endif } } @@ -6092,7 +6714,6 @@ /*********************************************************************************** *** print port statistics ***********************************************************************************/ - void mv_eth_port_stats_print(unsigned int port) { struct eth_port *pp = mv_eth_port_by_id(port); @@ -6106,7 +6727,7 @@ pr_info("----------------------------------------------------\n\n"); if (pp == NULL) { - printk(KERN_ERR "eth_stats_print: wrong port number %d\n", port); + pr_err("Port %d does not exist\n", port); return; } stat = &(pp->stats); @@ -6150,7 +6771,6 @@ printk(KERN_CONT "%8d ", stat->tx_done_timer_add[cpu]); pr_info("\n"); - printk(KERN_ERR "tx_fragmentation..............%10u\n", stat->tx_fragment); printk(KERN_ERR "tx_done_event.................%10u\n", stat->tx_done); printk(KERN_ERR "cleanup_timer_event...........%10u\n", stat->cleanup_timer); printk(KERN_ERR "link..........................%10u\n", stat->link); @@ -6246,7 +6866,7 @@ /* RX pool statistics */ #ifdef CONFIG_MV_ETH_BM_CPU - if (pp->pool_short) + if (MV_NETA_BM_CAP() && pp->pool_short) mv_eth_pool_status_print(pp->pool_short->pool); #endif /* CONFIG_MV_ETH_BM_CPU */ @@ -6303,9 +6923,10 @@ struct rx_queue *rxq_ctrl; pp = mv_eth_port_by_id(port); - - if (pp == NULL) + if (pp == NULL) { + pr_err("Port %d does not exist\n", port); return -1; + } if (pp->flags & MV_ETH_F_STARTED) { printk(KERN_ERR "%s: port %d is started, cannot cleanup\n", __func__, port); @@ -6360,7 +6981,7 @@ pp->pool_long = NULL; } #ifdef CONFIG_MV_ETH_BM_CPU - if (pp->pool_short) { + if (MV_NETA_BM_CAP() && pp->pool_short) { mv_eth_pool_free(pp->pool_short->pool, pp->pool_short_num); pp->pool_short->port_map &= ~(1 << pp->port); pp->pool_short = NULL; @@ -6430,9 +7051,9 @@ { struct eth_port *pp = mv_eth_port_by_id(port); struct neta_rx_desc *rx_desc; - struct eth_pbuf *pkt; + struct sk_buff *skb; struct bm_pool *pool; - int rxq, rx_done, i, wakeup, ruleId; + int rxq, rx_done, i, wakeup, ruleId, pool_id; MV_NETA_RXQ_CTRL *rx_ctrl; wakeup = 0; @@ -6452,15 +7073,15 @@ mvNetaRxqDescSwap(rx_desc); #endif /* MV_CPU_BE */ - pkt = (struct eth_pbuf *)rx_desc->bufCookie; - mvOsCacheInvalidate(pp->dev->dev.parent, pkt->pBuf + pkt->offset, rx_desc->dataSize); + skb = (struct sk_buff *)rx_desc->bufCookie; + mvOsCacheInvalidate(pp->dev->dev.parent, skb->head + NET_SKB_PAD, rx_desc->dataSize); - if (mv_pnc_wol_pkt_match(pp->port, pkt->pBuf + pkt->offset, rx_desc->dataSize, &ruleId)) + if (mv_pnc_wol_pkt_match(pp->port, skb->head + NET_SKB_PAD, rx_desc->dataSize, &ruleId)) wakeup = 1; + pool_id = NETA_RX_GET_BPID(rx_desc); + pool = &mv_eth_pool[pool_id]; + mv_eth_rxq_refill(pp, rxq, pool, skb, rx_desc); - pool = &mv_eth_pool[pkt->pool]; - mv_eth_rxq_refill(pp, rxq, pkt, pool, rx_desc); - if (wakeup) { printk(KERN_INFO "packet match WoL rule=%d found on port=%d, rxq=%d\n", ruleId, port, rxq); @@ -6507,7 +7128,7 @@ pp = mv_eth_port_by_id(port); if (pp == NULL) { - printk(KERN_INFO "Failed to fined pp struct on port #%d\n", port); + pr_err("Port %d does not exist\n", port); return 1; } @@ -6682,7 +7303,7 @@ printk(KERN_INFO "Removing Marvell Ethernet Driver - port #%d\n", port); if (pp == NULL) - printk(KERN_ERR "Not Found\n"); + pr_err("Port %d does not exist\n", port); mv_eth_priv_cleanup(pp); Index: drivers/net/ethernet/mvebu_net/neta/net_dev/mv_netdev.h =================================================================== --- drivers/net/ethernet/mvebu_net/neta/net_dev/mv_netdev.h (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/net_dev/mv_netdev.h (working copy) @@ -33,6 +33,7 @@ #include #include #include +#include #include "mvCommon.h" #include "mvOs.h" @@ -87,20 +88,33 @@ #define RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD) - #ifdef CONFIG_MV_NETA_SKB_RECYCLE -extern int mv_ctrl_recycle; +/* SKB recycle magic, indicate the skb can be recycled, here it is the address of skb */ +#define MV_NETA_SKB_RECYCLE_MAGIC(skb) ((unsigned int)skb) +/* Cb to store magic and bpid, IPv6 TCP will consume the most cb[] with 44 bytes, so the last 4 bytes is safe to use */ +#define MV_NETA_SKB_RECYCLE_CB(skb) (*((unsigned int *)(&(skb->cb[sizeof(skb->cb) - 4])))) +/* Get recycle magic */ +#define MV_NETA_SKB_RECYCLE_MAGIC_GET(skb) (MV_NETA_SKB_RECYCLE_CB(skb) & (~MV_BM_POOLS_MASK)) +/* Set recycle magic and bpid */ +#define MV_NETA_SKB_RECYCLE_MAGIC_BPID_SET(skb, magic_bpid) (MV_NETA_SKB_RECYCLE_CB(skb) = magic_bpid) +/* Recycle magic check */ +#define MV_NETA_SKB_RECYCLE_MAGIC_IS_OK(skb) (MV_NETA_SKB_RECYCLE_MAGIC(skb) == \ + MV_NETA_SKB_RECYCLE_MAGIC_GET(skb)) +/* Get bpid */ +#define MV_NETA_SKB_RECYCLE_BPID_GET(skb) (MV_NETA_SKB_RECYCLE_CB(skb) & MV_BM_POOLS_MASK) -#define mv_eth_is_recycle() (mv_ctrl_recycle) -int mv_eth_skb_recycle(struct sk_buff *skb); +extern int mv_ctrl_swf_recycle; +#define mv_eth_is_swf_recycle() (mv_ctrl_swf_recycle) #else -#define mv_eth_is_recycle() 0 +#define mv_eth_is_swf_recycle() 0 +#define MV_NETA_SKB_RECYCLE_MAGIC_BPID_SET(skb, magic_bpid) {; } +#define MV_NETA_SKB_RECYCLE_MAGIC(skb) 0 #endif /* CONFIG_MV_NETA_SKB_RECYCLE */ /****************************************************** * interrupt control -- * ******************************************************/ -#ifdef CONFIG_MV_ETH_TXDONE_ISR +#ifdef CONFIG_MV_NETA_TXDONE_ISR #define MV_ETH_TXDONE_INTR_MASK (((1 << CONFIG_MV_ETH_TXQ) - 1) << NETA_CAUSE_TXQ_SENT_DESC_OFFS) #else #define MV_ETH_TXDONE_INTR_MASK 0 @@ -200,7 +214,6 @@ u32 poll_exit[CONFIG_NR_CPUS]; u32 tx_done_timer_event[CONFIG_NR_CPUS]; u32 tx_done_timer_add[CONFIG_NR_CPUS]; - u32 tx_fragment; u32 tx_done; u32 cleanup_timer; u32 link; @@ -357,7 +370,12 @@ int napiCpuGroup; int txq; int cpu; +#if defined(CONFIG_MV_NETA_TXDONE_IN_HRTIMER) + struct hrtimer tx_done_timer; + struct tasklet_struct tx_done_tasklet; +#elif defined(CONFIG_MV_NETA_TXDONE_IN_TIMER) struct timer_list tx_done_timer; +#endif struct timer_list cleanup_timer; unsigned long flags; @@ -464,7 +482,6 @@ u32 skb_alloc_ok; u32 skb_recycled_ok; u32 skb_recycled_err; - u32 skb_hw_cookie_err; #endif /* CONFIG_MV_ETH_STAT_DBG */ }; @@ -479,13 +496,15 @@ spinlock_t lock; u32 port_map; int missed; /* FIXME: move to stats */ + atomic_t in_use; + int in_use_thresh; struct pool_stats stats; }; #ifdef CONFIG_MV_ETH_BM_CPU #define MV_ETH_BM_POOLS MV_BM_POOLS -#define mv_eth_pool_bm(p) (p->bm_pool) -#define mv_eth_txq_bm(q) (q->bm_only) +#define mv_eth_pool_bm(p) (MV_NETA_BM_CAP() ? (p->bm_pool) : 0) +#define mv_eth_txq_bm(q) (MV_NETA_BM_CAP() ? (q->bm_only) : 0) #else #define MV_ETH_BM_POOLS CONFIG_MV_ETH_PORTS_NUM #define mv_eth_pool_bm(p) 0 @@ -492,6 +511,13 @@ #define mv_eth_txq_bm(q) 0 #endif /* CONFIG_MV_ETH_BM_CPU */ +#ifdef CONFIG_MV_NETA_TXDONE_IN_HRTIMER +#define MV_ETH_HRTIMER_PERIOD_MIN (10) +#define MV_ETH_HRTIMER_PERIOD_MAX (10000) +unsigned int mv_eth_tx_done_hrtimer_period_get(void); +int mv_eth_tx_done_hrtimer_period_set(unsigned int period); +#endif + #ifdef CONFIG_MV_ETH_BM MV_STATUS mv_eth_bm_config_get(void); int mv_eth_bm_config_pkt_size_get(int pool); @@ -630,20 +656,34 @@ static inline void mv_eth_add_cleanup_timer(struct cpu_ctrl *cpuCtrl) { if (test_and_set_bit(MV_ETH_F_CLEANUP_TIMER_BIT, &(cpuCtrl->flags)) == 0) { - cpuCtrl->cleanup_timer.expires = jiffies + ((HZ * CONFIG_MV_ETH_CLEANUP_TIMER_PERIOD) / 1000); /* ms */ + cpuCtrl->cleanup_timer.expires = jiffies + ((HZ * 10) / 1000); /* ms */ add_timer_on(&cpuCtrl->cleanup_timer, smp_processor_id()); } } +#if defined(CONFIG_MV_NETA_TXDONE_IN_HRTIMER) static inline void mv_eth_add_tx_done_timer(struct cpu_ctrl *cpuCtrl) { + ktime_t interval; + unsigned long delay_in_ns = mv_eth_tx_done_hrtimer_period_get() * 1000; /*the func return value is in us unit*/ + if (test_and_set_bit(MV_ETH_F_TX_DONE_TIMER_BIT, &(cpuCtrl->flags)) == 0) { + STAT_INFO(cpuCtrl->pp->stats.tx_done_timer_add[smp_processor_id()]++); + interval = ktime_set(0, delay_in_ns); + hrtimer_start(&cpuCtrl->tx_done_timer, interval, HRTIMER_MODE_REL_PINNED); + } +} +#elif defined(CONFIG_MV_NETA_TXDONE_IN_TIMER) +static inline void mv_eth_add_tx_done_timer(struct cpu_ctrl *cpuCtrl) +{ + if (test_and_set_bit(MV_ETH_F_TX_DONE_TIMER_BIT, &(cpuCtrl->flags)) == 0) { - cpuCtrl->tx_done_timer.expires = jiffies + ((HZ * CONFIG_MV_ETH_TX_DONE_TIMER_PERIOD) / 1000); /* ms */ + cpuCtrl->tx_done_timer.expires = jiffies + ((HZ * CONFIG_MV_NETA_TX_DONE_TIMER_PERIOD) / 1000); /* ms */ STAT_INFO(cpuCtrl->pp->stats.tx_done_timer_add[smp_processor_id()]++); add_timer_on(&cpuCtrl->tx_done_timer, smp_processor_id()); } } +#endif static inline void mv_eth_shadow_inc_get(struct tx_queue *txq) { @@ -672,16 +712,11 @@ { struct sk_buff *skb = (struct sk_buff *)pkt->osInfo; -#ifdef CONFIG_MV_NETA_SKB_RECYCLE - skb->skb_recycle = NULL; - skb->hw_cookie = 0; -#endif /* CONFIG_MV_NETA_SKB_RECYCLE */ - dev_kfree_skb_any(skb); mvOsFree(pkt); } -static inline int mv_eth_pool_put(struct bm_pool *pool, struct eth_pbuf *pkt) +static inline int mv_eth_pool_put(struct bm_pool *pool, struct sk_buff *skb) { unsigned long flags = 0; @@ -690,30 +725,31 @@ STAT_ERR(pool->stats.stack_full++); MV_ETH_UNLOCK(&pool->lock, flags); - /* free pkt+skb */ - mv_eth_pkt_free(pkt); + /* free skb */ + dev_kfree_skb_any(skb); return 1; } - mvStackPush(pool->stack, (MV_U32) pkt); + mvStackPush(pool->stack, (MV_U32)skb); STAT_DBG(pool->stats.stack_put++); MV_ETH_UNLOCK(&pool->lock, flags); return 0; } - /* Pass pkt to BM Pool or RXQ ring */ static inline void mv_eth_rxq_refill(struct eth_port *pp, int rxq, - struct eth_pbuf *pkt, struct bm_pool *pool, struct neta_rx_desc *rx_desc) + struct bm_pool *pool, struct sk_buff *skb, struct neta_rx_desc *rx_desc) { + phys_addr_t pa = virt_to_phys(skb->head); + if (mv_eth_pool_bm(pool)) { /* Refill BM pool */ STAT_DBG(pool->stats.bm_put++); - mvBmPoolPut(pkt->pool, (MV_ULONG) pkt->physAddr); + mvBmPoolPut(pool->pool, (MV_ULONG)pa); mvOsCacheLineInv(pp->dev->dev.parent, rx_desc); } else { /* Refill Rx descriptor */ STAT_DBG(pp->stats.rxq_fill[rxq]++); - mvNetaRxDescFill(rx_desc, pkt->physAddr, (MV_U32)pkt); + mvNetaRxDescFill(rx_desc, (MV_U32)pa, (MV_U32)skb); mvOsCacheLineFlush(pp->dev->dev.parent, rx_desc); } } @@ -781,7 +817,7 @@ void mv_eth_set_noqueue(struct net_device *dev, int enable); void mv_eth_ctrl_hwf(int en); -int mv_eth_ctrl_recycle(int en); +int mv_eth_ctrl_swf_recycle(int en); void mv_eth_ctrl_txdone(int num); int mv_eth_ctrl_tx_mh(int port, u16 mh); int mv_eth_ctrl_tx_cmd(int port, u32 cmd); @@ -829,7 +865,7 @@ int mv_eth_rx_policy(u32 cause); int mv_eth_refill(struct eth_port *pp, int rxq, - struct eth_pbuf *pkt, struct bm_pool *pool, struct neta_rx_desc *rx_desc); + struct bm_pool *pool, struct neta_rx_desc *rx_desc); u32 mv_eth_txq_done(struct eth_port *pp, struct tx_queue *txq_ctrl); u32 mv_eth_tx_done_gbe(struct eth_port *pp, u32 cause_tx_done, int *tx_todo); u32 mv_eth_tx_done_pon(struct eth_port *pp, int *tx_todo); @@ -843,7 +879,7 @@ void *mv_eth_bm_pool_create(int pool, int capacity, MV_ULONG *physAddr); #endif /* CONFIG_MV_ETH_BM */ -#if defined(CONFIG_MV_ETH_HWF) && !defined(CONFIG_MV_ETH_BM_CPU) +#ifdef CONFIG_MV_ETH_HWF MV_STATUS mv_eth_hwf_bm_create(int port, int mtuPktSize); void mv_hwf_bm_dump(void); #endif /* CONFIG_MV_ETH_HWF && !CONFIG_MV_ETH_BM_CPU */ @@ -852,13 +888,4 @@ int mv_l2fw_init(void); #endif -#ifdef CONFIG_MV_ETH_NFP -int mv_eth_nfp_ctrl(struct net_device *dev, int en); -int mv_eth_nfp_ext_ctrl(struct net_device *dev, int en); -int mv_eth_nfp_ext_add(struct net_device *dev, int port); -int mv_eth_nfp_ext_del(struct net_device *dev); -MV_STATUS mv_eth_nfp(struct eth_port *pp, int rxq, struct neta_rx_desc *rx_desc, - struct eth_pbuf *pkt, struct bm_pool *pool); -#endif /* CONFIG_MV_ETH_NFP */ - #endif /* __mv_netdev_h__ */ Index: drivers/net/ethernet/mvebu_net/neta/pnc/pnc_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/pnc/pnc_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/pnc/pnc_sysfs.c (working copy) @@ -31,10 +31,13 @@ #include #include #include +#include #include "mvOs.h" #include "mvCommon.h" +#ifndef CONFIG_ARCH_MVEBU #include "ctrlEnv/mvCtrlEnvLib.h" +#endif #include "gbe/mvNeta.h" @@ -42,7 +45,7 @@ #include "pnc/mvTcam.h" #ifdef CONFIG_MV_ETH_PNC_L3_FLOW -extern int __devinit rxq_map_sysfs_init(struct kobject *kobj); +#include "pnc_sysfs.h" #endif /* CONFIG_MV_ETH_PNC_L3_FLOW */ static struct tcam_entry te; @@ -75,9 +78,10 @@ off += mvOsSPrintf(buf+off, " hw_hits - start recording for port \n"); #ifdef MV_ETH_PNC_LB + off += mvOsSPrintf(buf+off, " lb_frag_l4 - enable/disable 4-tuple LB mode for first fragment\n"); off += mvOsSPrintf(buf+off, " lb_ip4 - set LB mode for ipv4 traffic: 0-disable, 1-2tuple\n"); off += mvOsSPrintf(buf+off, " lb_ip6 - set LB mode for ipv6 traffic: 0-disable, 1-2tuple\n"); - off += mvOsSPrintf(buf+off, " lb_l4 - set LB mode for TCP/UDP traffic: : 0-disable, 1-2tuple, 2-4tuple\n"); + off += mvOsSPrintf(buf+off, " lb_l4 - set LB mode for TCP/UDP traffic: 0-disable, 1-2tuple, 2-4tuple\n"); #endif /* MV_ETH_PNC_LB */ #ifdef MV_ETH_PNC_AGING @@ -182,6 +186,8 @@ else if (!strcmp(name, "hw_hits")) tcam_hw_record(a); #ifdef MV_ETH_PNC_LB + else if (!strcmp(name, "lb_frag_l4")) + mvPncLbFirstFragL4(a); else if (!strcmp(name, "lb_ip4")) mvPncLbModeIp4(a); else if (!strcmp(name, "lb_ip6")) @@ -294,6 +300,7 @@ static DEVICE_ATTR(lb_ip4, S_IWUSR, tcam_show, tcam_store); static DEVICE_ATTR(lb_ip6, S_IWUSR, tcam_show, tcam_store); static DEVICE_ATTR(lb_l4, S_IWUSR, tcam_show, tcam_store); +static DEVICE_ATTR(lb_frag_l4, S_IWUSR, tcam_show, tcam_store); #endif /* MV_ETH_PNC_LB */ static DEVICE_ATTR(sw_dump, S_IRUSR, tcam_show, tcam_store); @@ -348,6 +355,7 @@ &dev_attr_lb_ip4.attr, &dev_attr_lb_ip6.attr, &dev_attr_lb_l4.attr, + &dev_attr_lb_frag_l4.attr, #endif /* MV_ETH_PNC_LB */ &dev_attr_sw_dump.attr, Index: drivers/net/ethernet/mvebu_net/neta/pnc/pnc_sysfs.h =================================================================== --- drivers/net/ethernet/mvebu_net/neta/pnc/pnc_sysfs.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/neta/pnc/pnc_sysfs.h (working copy) @@ -0,0 +1,33 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +*******************************************************************************/ +#ifndef __PNC_SYSFS_H__ +#define __PNC_SYSFS_H__ + +int rxq_map_sysfs_init(struct kobject *kobj); + +#endif /* __PNC_SYSFS_H__ */ Index: drivers/net/ethernet/mvebu_net/neta/pnc/rxq_map_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/pnc/rxq_map_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/pnc/rxq_map_sysfs.c (working copy) @@ -31,10 +31,13 @@ #include #include #include +#include #include "mvOs.h" #include "mvCommon.h" +#ifndef CONFIG_OF #include "ctrlEnv/mvCtrlEnvLib.h" +#endif #include "gbe/mvNeta.h" #include "pnc/mvPnc.h" @@ -173,7 +176,7 @@ .attrs = rxq_map_attrs, }; -int __devinit rxq_map_sysfs_init(struct kobject *kobj) +int rxq_map_sysfs_init(struct kobject *kobj) { int err; Index: drivers/net/ethernet/mvebu_net/neta/pnc/wol_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/neta/pnc/wol_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/neta/pnc/wol_sysfs.c (working copy) @@ -34,7 +34,9 @@ #include "mvOs.h" #include "mvCommon.h" +#ifndef CONFIG_ARCH_MVEBU #include "ctrlEnv/mvCtrlEnvLib.h" +#endif #include "gbe/mvNeta.h" #include "pnc/mvPnc.h" Index: drivers/net/ethernet/mvebu_net/netmux/mv_mux_netdev.c =================================================================== --- drivers/net/ethernet/mvebu_net/netmux/mv_mux_netdev.c (revision 1) +++ drivers/net/ethernet/mvebu_net/netmux/mv_mux_netdev.c (working copy) @@ -293,11 +293,255 @@ return size_arr[type]; } /*-----------------------------------------------------------------------------------------*/ +/* Restore VLAN with DSA, including EDSA */ +static inline int mv_mux_dsa2vlan(struct net_device *mux_dev, struct sk_buff *skb, bool is_edsa) +{ + u8 *dsa_header; + int source_device; + int source_port; + int len; + struct mux_netdev *pdev = MV_MUX_PRIV(mux_dev); + /* The ethertype field is part of the DSA header. */ + dsa_header = skb->data + (2 * MV_MAC_ADDR_SIZE) + MV_ETH_MH_SIZE; +#ifdef CONFIG_MV_ETH_DEBUG_CODE + if (mux_eth_shadow[pdev->port].flags & MV_MUX_F_DBG_RX) { + pr_info("dsa_header WL = 0x%.8x", ntohl(*((u32 *)dsa_header))); + if (is_edsa) + pr_info(" WH = 0x%.8x\n", ntohl(*(((u32 *)dsa_header) + 1))); + else + pr_info("\n"); + } +#endif + + /* Check that frame type is either TO_CPU or FORWARD. */ + if (MV_DSA_HDR_TAG_CMD_GET(dsa_header) != MV_DSA_HDR_TAG_CMD_TO_CPU && + MV_DSA_HDR_TAG_CMD_GET(dsa_header) != MV_DSA_HDR_TAG_CMD_FORWARD) { + pr_err("Invalid (E)DSA type\n"); + return -1; + } + + /* Determine source device and port. */ + source_device = MV_DSA_HDR_SRC_DEV_GET(dsa_header); + source_port = MV_DSA_HDR_SRC_PORT_GET(dsa_header); + if (is_edsa) { + /* WH bit 10 */ + if (MV_EDSA_HDR_SRC_PORT_BIT5_GET(dsa_header)) + source_port |= 0x20; + } + +#ifdef CONFIG_MV_ETH_DEBUG_CODE + if (mux_eth_shadow[pdev->port].flags & MV_MUX_F_DBG_RX) + pr_info("source_device = 0x%x, source_port=0x%x", source_device, source_port); +#endif + + /* Convert the DSA header to an 802.1q header if the 'tagged' + * bit in the DSA header is set. If the 'tagged' bit is clear, + * delete the DSA header entirely. + */ + if (MV_DSA_HDR_TAGGED(dsa_header)) { + u8 new_header[4]; + u16 tpid = ETH_P_8021Q; + + /* + * Insert 802.1q ethertype and copy the VLAN-related + * fields, but clear the bit that will hold CFI (since + * DSA uses that bit location for another purpose). + */ + new_header[0] = (tpid >> 8) & 0xff; + new_header[1] = tpid & 0xff; + new_header[2] = dsa_header[2] & ~0x10; + new_header[3] = dsa_header[3]; + + /* + * Move CFI bit from its place in the DSA header to + * its 802.1q-designated place. + */ + if (is_edsa) { + if (dsa_header[8] & 0x40) + new_header[2] |= 0x10; + } else { + if (dsa_header[1] & 0x01) + new_header[2] |= 0x10; + } + + /* + * Update packet checksum if skb is CHECKSUM_COMPLETE. + */ + if (skb->ip_summed == CHECKSUM_COMPLETE) { + __wsum c = skb->csum; + c = csum_add(c, csum_partial(new_header + 2, 2, 0)); + c = csum_sub(c, csum_partial(dsa_header + 2, 2, 0)); + skb->csum = c; + } + + memcpy(dsa_header, new_header, MV_ETH_DSA_SIZE); + len = 0; + if (is_edsa) { + /* memmove the extend 4 bytes in EDSA */ + memmove(skb->data + 4, skb->data, (2 * MV_MAC_ADDR_SIZE) + MV_ETH_MH_SIZE + 4); + __skb_pull(skb, 4); + len = 4; + } + + /* MH exist in packet anycase - Skip it */ + __skb_pull(skb, MV_ETH_MH_SIZE); + len += MV_ETH_MH_SIZE; + } else { + /* remove tag*/ + len = mv_mux_rx_tag_remove(mux_dev, skb); + } + + return len; +} + +/* Build DSA with VLAN, the second parameter dsa is used to get trg port and trg dev id */ +static inline int mv_mux_vlan2dsa(struct sk_buff *skb, u32 dsa) +{ + u8 *dsa_header; + /* target devcie ID and port from tx DSA, TODO - what is the better way to get trg_dev and trg_port? */ + u8 trg_dev = (dsa >> MV_DSA_HDR_TRG_DEV_WORD_OFF) & MV_DSA_HDR_TRG_DEV_MASK; + u8 trg_port = (dsa >> MV_DSA_HDR_TRG_PORT_WORD_OFF) & MV_DSA_HDR_TRG_PORT_MASK; + + /* + * Convert the outermost 802.1q tag to a DSA tag for tagged + * packets, or insert a DSA tag between the addresses and + * the ethertype field for untagged packets. + */ + if (skb->protocol == htons(ETH_P_8021Q)) { + if (skb_cow_head(skb, 0) < 0) + return -1; + + /* + * Construct tagged FROM_CPU DSA tag from 802.1q tag. + */ + dsa_header = skb->data + 2 * MV_MAC_ADDR_SIZE; + dsa_header[0] = 0x60 | trg_dev; + dsa_header[1] = trg_port << 3; + + /* + * Move CFI field from byte 2 to byte 1. + */ + if (dsa_header[2] & 0x10) { + dsa_header[1] |= 0x01; + dsa_header[2] &= ~0x10; + } + } else { + if (skb_cow_head(skb, MV_ETH_DSA_SIZE) < 0) + return -1; + skb_push(skb, MV_ETH_DSA_SIZE); + + memmove(skb->data, skb->data + MV_ETH_DSA_SIZE, 2 * MV_MAC_ADDR_SIZE); + + /* + * Construct untagged FROM_CPU DSA tag. + */ + dsa_header = skb->data + (2 * MV_MAC_ADDR_SIZE); + dsa_header[0] = 0x40; + dsa_header[1] = trg_port << 3; + dsa_header[2] = 0x00; + dsa_header[3] = 0x00; + } + +#ifdef CONFIG_MV_ETH_DEBUG_CODE + pr_info("trg_dev = 0x%x, trg_port = 0x%x, dsa header = 0x%.8x\n", + trg_dev, trg_port, ntohl(*(u32 *)dsa_header)); +#endif + + return 0; +} + +static inline int mv_mux_vlan2edsa(struct sk_buff *skb, unsigned int edsaL, unsigned int edsaH) +{ + u8 *dsa_header; + /* target devcie ID and port from tx EDSA, TODO - what is the better way to get trg_dev and trg_port? */ + u8 trg_dev = (edsaL >> MV_DSA_HDR_TRG_DEV_WORD_OFF) & MV_DSA_HDR_TRG_DEV_MASK; + u8 trg_port = (edsaL >> MV_DSA_HDR_TRG_PORT_WORD_OFF) & MV_DSA_HDR_TRG_PORT_MASK; + + /* + * Convert the outermost 802.1q tag to a DSA tag for tagged + * packets, or insert a DSA tag between the addresses and + * the ethertype field for untagged packets. + */ + if (skb->protocol == htons(ETH_P_8021Q)) { + if (skb_cow_head(skb, 0) < 0) + return -1; + + /* add extra 4 bytes; edsa: 8 bytes, vlan: 4 bytes */ + skb_push(skb, MV_ETH_DSA_SIZE); + + /* Data move */ + memmove(skb->data, skb->data + MV_ETH_DSA_SIZE, 2 * MV_MAC_ADDR_SIZE + MV_ETH_DSA_SIZE); + + /* + * Construct tagged FROM_CPU DSA tag from 802.1q tag. + */ + dsa_header = skb->data + 2 * MV_MAC_ADDR_SIZE; + dsa_header[0] = 0x60 | trg_dev; + dsa_header[1] = trg_port << 3; + + /* + * Move CFI field from byte 2 to byte 4. + */ + if (dsa_header[2] & 0x10) { + dsa_header[4] |= 0x40; + dsa_header[2] &= ~0x10; + } + + /* Set extend bit */ + dsa_header[2] |= 0x10; + dsa_header[4] &= 0x7f; + /* Set trg port bit[5] in WH bit 10 */ + if (edsaH & 0x400) + dsa_header[6] |= 0x04; + } else { + if (skb_cow_head(skb, MV_ETH_EDSA_SIZE) < 0) + return -1; + + /* Add 8 bytes of EDSA size */ + skb_push(skb, MV_ETH_EDSA_SIZE); + + /* Data move */ + memmove(skb->data, skb->data + MV_ETH_EDSA_SIZE, 2 * MV_MAC_ADDR_SIZE); + + /* + * Construct untagged FROM_CPU DSA tag. + */ + dsa_header = skb->data + 2 * MV_MAC_ADDR_SIZE; + dsa_header[0] = 0x40 | trg_dev; + dsa_header[1] = trg_port << 3; + dsa_header[2] = 0x00; + dsa_header[3] = 0x00; + + /* Set extend */ + dsa_header[2] |= 0x10; + /* Clear WH bit 31 and 30*/ + dsa_header[4] = 0x00; + /* Clear other fields */ + dsa_header[5] = 0x00; + dsa_header[6] = 0x00; + dsa_header[7] = 0x00; + /* Set trg port bit[5] in WH bit 10 */ + if (edsaH & 0x400) + dsa_header[6] |= 0x04; + } + +#ifdef CONFIG_MV_ETH_DEBUG_CODE + /*pr_info("trg_dev = 0x%x, trg_port = 0x%x, EDSA header WL=0x%.8x, WH=0x%.8x\n", + trg_dev, trg_port, ntohl(*(u32 *)dsa_header), ntohl(*(((u32 *)dsa_header) + 1)));*/ +#endif + + return MV_OK; +} + +/*-----------------------------------------------------------------------------------------*/ + int mv_mux_rx(struct sk_buff *skb, int port, struct napi_struct *napi) { struct net_device *mux_dev; int len; + struct mux_netdev *pdev; + bool is_edsa = false; mux_dev = mv_mux_rx_netdev_get(port, skb); @@ -308,8 +552,29 @@ if (!(mux_dev->flags & IFF_UP)) goto out1; + pdev = MV_MUX_PRIV(mux_dev); + + if (pdev->leave_tag == false) { + /* restore VLAN from DSA */ + if (mux_eth_shadow[pdev->port].tag_type == MV_TAG_TYPE_DSA || + mux_eth_shadow[pdev->port].tag_type == MV_TAG_TYPE_EDSA) { + if (mux_eth_shadow[pdev->port].tag_type == MV_TAG_TYPE_EDSA) + is_edsa = true; + len = mv_mux_dsa2vlan(mux_dev, skb, is_edsa); + /* Not valid DSA mode */ + if (len < 0) { + pr_err("Invalid (E)DSA mode\n"); + goto out1; + } + } else { /* remove tag*/ len = mv_mux_rx_tag_remove(mux_dev, skb); + } + } else { + /* Transparent to packet, however, MH exist in packet anycase - Skip it */ + __skb_pull(skb, MV_ETH_MH_SIZE); + len = MV_ETH_MH_SIZE; + } mux_dev->stats.rx_packets++; mux_dev->stats.rx_bytes += skb->len; @@ -316,8 +581,8 @@ #ifdef CONFIG_MV_ETH_DEBUG_CODE if (mux_eth_shadow[port].flags & MV_MUX_F_DBG_RX) { struct mux_netdev *pmux_priv = MV_MUX_PRIV(mux_dev); - pr_err("\n%s - %s: port=%d, cpu=%d, pkt_size=%d, shift=%d\n", - mux_dev->name, __func__, pmux_priv->port, smp_processor_id(), skb->len, len); + pr_err("\n%s - %s: port=%d, cpu=%d, pkt_size=%d, shift=%d, leave_tag=%d\n", + mux_dev->name, __func__, pmux_priv->port, smp_processor_id(), skb->len, len, pdev->leave_tag); /* mv_eth_skb_print(skb); */ mvDebugMemDump(skb->data, 64, 1); } @@ -330,6 +595,11 @@ */ skb->protocol = eth_type_trans(skb, mux_dev); + /* Replace protocol with ansparent proto for raw socket with app */ + if (pdev->leave_tag == true && + (mux_eth_shadow[port].tag_type == MV_TAG_TYPE_DSA || mux_eth_shadow[port].tag_type == MV_TAG_TYPE_EDSA)) + skb->protocol = htons(pdev->proto_type); + if (mux_dev->features & NETIF_F_GRO) { /* TODO update mux priv gro counters @@ -368,9 +638,9 @@ #ifdef CONFIG_MV_ETH_DEBUG_CODE if (mux_eth_shadow[pmux_priv->port].flags & MV_MUX_F_DBG_TX) { - pr_err("\n%s - %s_%lu: port=%d, cpu=%d, in_intr=0x%lx\n", + pr_err("\n%s - %s_%lu: port=%d, cpu=%d, in_intr=0x%lx, leave_tag=%d\n", dev->name, __func__, dev->stats.tx_packets, pmux_priv->port, - smp_processor_id(), in_interrupt()); + smp_processor_id(), in_interrupt(), pmux_priv->leave_tag); /* mv_eth_skb_print(skb); */ mvDebugMemDump(skb->data, 64, 1); } @@ -417,7 +687,8 @@ return; if (switch_ops->promisc_set) - if (switch_ops->promisc_set(pmux_priv->idx, (mux_dev->flags & IFF_PROMISC) ? 1 : 0)) + if (switch_ops->promisc_set(MV_MUX_GROUP_IDX_2_DB(pmux_priv->idx), + (mux_dev->flags & IFF_PROMISC) ? 1 : 0)) pr_err("%s: Set promiscuous mode failed\n", mux_dev->name); /* IFF_ALLMULTI is not supported by switch */ @@ -424,7 +695,7 @@ /* remove all mcast enries */ if (switch_ops->all_mcast_del) - if (switch_ops->all_mcast_del(pmux_priv->idx)) + if (switch_ops->all_mcast_del(MV_MUX_GROUP_IDX_2_DB(pmux_priv->idx))) pr_err("%s: Delete all Mcast failed\n", mux_dev->name); if (mux_dev->flags & IFF_MULTICAST) { @@ -434,7 +705,8 @@ netdev_for_each_mc_addr(ha, mux_dev) { if (switch_ops->mac_addr_set) { - if (switch_ops->mac_addr_set(pmux_priv->idx, ha->addr, 1)) { + if (switch_ops->mac_addr_set(MV_MUX_GROUP_IDX_2_DB(pmux_priv->idx), + ha->addr, 1)) { pr_err("%s: Mcast init failed\n", mux_dev->name); break; } @@ -448,7 +720,8 @@ if (!curr_addr) break; if (switch_ops->mac_addr_set) { - if (switch_ops->mac_addr_set(pmux_priv->idx, curr_addr->dmi_addr, 1)) { + if (switch_ops->mac_addr_set(MV_MUX_GROUP_IDX_2_DB(pmux_priv->idx), + curr_addr->dmi_addr, 1)) { pr_err("%s: Mcast init failed\n", mux_dev->name); break; } @@ -495,7 +768,7 @@ if (mv_mux_internal_switch(pmux_priv->port)) if (switch_ops && switch_ops->group_disable) - switch_ops->group_disable(pmux_priv->idx); + switch_ops->group_disable(MV_MUX_GROUP_IDX_2_DB(pmux_priv->idx)); printk(KERN_NOTICE "%s: stopped\n", dev->name); @@ -527,7 +800,7 @@ if (mv_mux_internal_switch(pmux_priv->port)) if (switch_ops && switch_ops->group_enable) - switch_ops->group_enable(pmux_priv->idx); + switch_ops->group_enable(MV_MUX_GROUP_IDX_2_DB(pmux_priv->idx)); printk(KERN_NOTICE "%s: started\n", dev->name); @@ -550,11 +823,11 @@ if (switch_ops && switch_ops->mac_addr_set) { /* delete old mac */ - if (switch_ops->mac_addr_set(pmux_priv->idx, mux_dev->dev_addr, 0)) + if (switch_ops->mac_addr_set(MV_MUX_GROUP_IDX_2_DB(pmux_priv->idx), mux_dev->dev_addr, 0)) return MV_ERROR; /* set new mac */ - if (switch_ops->mac_addr_set(pmux_priv->idx, mac, 1)) + if (switch_ops->mac_addr_set(MV_MUX_GROUP_IDX_2_DB(pmux_priv->idx), mac, 1)) return MV_ERROR; } @@ -634,6 +907,8 @@ pmux_priv->tx_tag = tag_cfg->tx_tag; pmux_priv->rx_tag_ptrn = tag_cfg->rx_tag_ptrn; pmux_priv->rx_tag_mask = tag_cfg->rx_tag_mask; + pmux_priv->leave_tag = tag_cfg->leave_tag; + pmux_priv->proto_type = tag_cfg->proto_type; } pmux_priv->idx = idx; return mux_dev; @@ -956,8 +1231,11 @@ pdev_priv = MV_MUX_PRIV(mux_dev); if (mv_mux_internal_switch(port)) { /* In case of internal switch, link is determined by switch */ - if (switch_ops && switch_ops->link_status_get) { - int link_up = switch_ops->link_status_get(pdev_priv->idx); + /*In HGU mode, mux may be created by sysfs cmd and then pdev_priv->idx will be -1*/ + if (switch_ops && switch_ops->link_status_get + && (pdev_priv->idx != MV_MUX_UNKNOWN_GROUP)) { + int link_up; + link_up = switch_ops->link_status_get(MV_MUX_GROUP_IDX_2_DB(pdev_priv->idx)); mv_mux_update_link(mux_dev, link_up); } } else { @@ -1075,6 +1353,8 @@ mux_cfg->tx_tag = pmux_priv->tx_tag; mux_cfg->rx_tag_ptrn = pmux_priv->rx_tag_ptrn; mux_cfg->rx_tag_mask = pmux_priv->rx_tag_mask; + mux_cfg->leave_tag = pmux_priv->leave_tag; + mux_cfg->proto_type = pmux_priv->proto_type; } else memset(mux_cfg, 0, sizeof(MV_MUX_TAG)); } @@ -1161,7 +1441,8 @@ dev = pdev->next; } #ifdef CONFIG_MV_ETH_DEBUG_CODE - printk(KERN_ERR "%s:Error TAG=0x%08x match no interfaces\n", __func__, tag->vlan); + if (mux_eth_shadow[port].flags & MV_MUX_F_DBG_RX) + pr_err("%s:Error TAG=0x%08x, 0x%08x match no interfaces\n", __func__, tag->edsa[0], tag->edsa[1]); #endif return NULL; @@ -1181,23 +1462,23 @@ switch (tag_type) { case MV_TAG_TYPE_MH: - tag.mh = *(MV_U16 *)data; + tag.mh = ntohs(*(MV_U16 *)data); dev = mv_mux_mh_netdev_get(port, &tag); break; case MV_TAG_TYPE_VLAN: - tag.vlan = *(MV_U32 *)(data + MV_ETH_MH_SIZE + (2 * MV_MAC_ADDR_SIZE)); + tag.vlan = ntohl(*(MV_U32 *)(data + MV_ETH_MH_SIZE + (2 * MV_MAC_ADDR_SIZE))); dev = mv_mux_vlan_netdev_get(port, &tag); break; case MV_TAG_TYPE_DSA: - tag.dsa = *(MV_U32 *)(data + MV_ETH_MH_SIZE + (2 * MV_MAC_ADDR_SIZE)); + tag.dsa = ntohl(*(MV_U32 *)(data + MV_ETH_MH_SIZE + (2 * MV_MAC_ADDR_SIZE))); dev = mv_mux_dsa_netdev_get(port, &tag); break; case MV_TAG_TYPE_EDSA: - tag.edsa[0] = *(MV_U32 *)(data + MV_ETH_MH_SIZE + (2 * MV_MAC_ADDR_SIZE)); - tag.edsa[1] = *(MV_U32 *)(data + MV_ETH_MH_SIZE + (2 * MV_MAC_ADDR_SIZE) + 4); + tag.edsa[0] = ntohl(*(MV_U32 *)(data + MV_ETH_MH_SIZE + (2 * MV_MAC_ADDR_SIZE))); + tag.edsa[1] = ntohl(*(MV_U32 *)(data + MV_ETH_MH_SIZE + (2 * MV_MAC_ADDR_SIZE) + 4)); dev = mv_mux_edsa_netdev_get(port, &tag); break; @@ -1350,7 +1631,7 @@ { struct mux_netdev *pdev = MV_MUX_PRIV(dev); - return mv_mux_skb_vlan_add(skb, pdev->tx_tag.vlan); + return mv_mux_skb_vlan_add(skb, htonl(pdev->tx_tag.vlan)); } @@ -1358,8 +1639,12 @@ static inline int mv_mux_tx_skb_dsa_add(struct net_device *dev, struct sk_buff *skb) { - /* both DSA and VLAN are 4 bytes tags, placed in the same offset in the packet */ - return mv_mux_tx_skb_vlan_add(dev, skb); + struct mux_netdev *pdev = MV_MUX_PRIV(dev); + /* build DSA tag with VLAN info */ + if (!pdev->leave_tag) + return mv_mux_vlan2dsa(skb, pdev->tx_tag.dsa); + else + return MV_OK; } /*-----------------------------------------------------------------------------------------*/ @@ -1388,8 +1673,11 @@ static inline int mv_mux_tx_skb_edsa_add(struct net_device *dev, struct sk_buff *skb) { struct mux_netdev *pdev = MV_MUX_PRIV(dev); - - return mv_mux_skb_edsa_add(skb, pdev->tx_tag.edsa[0], pdev->tx_tag.edsa[1]); + /* build EDSA tag with VLAN info */ + if (!pdev->leave_tag) + return mv_mux_vlan2edsa(skb, pdev->tx_tag.edsa[0], pdev->tx_tag.edsa[1]); + else + return MV_OK; } /*-----------------------------------------------------------------------------------------*/ @@ -1400,6 +1688,10 @@ int tag_type = mux_eth_shadow[pdev->port].tag_type; int err = 0; + /* If transparent, leave_tag is true, return */ + if (pdev->leave_tag == true) + return err; + switch (tag_type) { case MV_TAG_TYPE_MH: @@ -1431,12 +1723,12 @@ int tag_type; if (!mux_dev) { - printk(KERN_ERR "%s:device in NULL.\n", __func__); + pr_err("%s:device in NULL.\n", __func__); return; } if (mv_mux_netdev_find(mux_dev->ifindex) != -1) { - printk(KERN_ERR "%s: %s is not mux device.\n", __func__, mux_dev->name); + pr_err("%s: %s is not mux device.\n", __func__, mux_dev->name); return; } @@ -1443,7 +1735,7 @@ pdev = MV_MUX_PRIV(mux_dev); if (!pdev || (pdev->port == -1)) { - printk(KERN_ERR "%s: device must be conncted to physical port\n", __func__); + pr_err("%s: device must be conncted to physical port\n", __func__); return; } tag_type = mux_eth_shadow[pdev->port].tag_type; @@ -1450,24 +1742,24 @@ switch (tag_type) { case MV_TAG_TYPE_VLAN: - printk(KERN_ERR "%s: port=%d, pdev=%p, tx_vlan=0x%08x, rx_vlan=0x%08x, rx_mask=0x%08x\n", + pr_info("%s: port=%d, pdev=%p, tx_vlan=0x%08x, rx_vlan=0x%08x, rx_mask=0x%08x", mux_dev->name, pdev->port, pdev, pdev->tx_tag.vlan, pdev->rx_tag_ptrn.vlan, pdev->rx_tag_mask.vlan); break; case MV_TAG_TYPE_DSA: - printk(KERN_ERR "%s: port=%d, pdev=%p: tx_dsa=0x%08x, rx_dsa=0x%08x, rx_mask=0x%08x\n", + pr_info("%s: port=%d, pdev=%p: tx_dsa=0x%08x, rx_dsa=0x%08x, rx_mask=0x%08x", mux_dev->name, pdev->port, pdev, pdev->tx_tag.dsa, pdev->rx_tag_ptrn.dsa, pdev->rx_tag_mask.dsa); break; case MV_TAG_TYPE_MH: - printk(KERN_ERR "%s: port=%d, pdev=%p: tx_mh=0x%04x, rx_mh=0x%04x, rx_mask=0x%04x\n", + pr_info("%s: port=%d, pdev=%p: tx_mh=0x%04x, rx_mh=0x%04x, rx_mask=0x%04x", mux_dev->name, pdev->port, pdev, pdev->tx_tag.mh, pdev->rx_tag_ptrn.mh, pdev->rx_tag_mask.mh); break; case MV_TAG_TYPE_EDSA: - printk(KERN_ERR "%s: port=%d, pdev=%p: tx_edsa=0x%08x %08x, rx_edsa=0x%08x %08x, rx_mask=0x%08x %08x\n", + pr_info("%s: port=%d, pdev=%p: tx_edsa=0x%08x %08x, rx_edsa=0x%08x %08x, rx_mask=0x%08x %08x", mux_dev->name, pdev->port, pdev, pdev->tx_tag.edsa[1], pdev->tx_tag.edsa[0], pdev->rx_tag_ptrn.edsa[1], pdev->rx_tag_ptrn.edsa[0], pdev->rx_tag_mask.edsa[1], pdev->rx_tag_mask.edsa[0]); @@ -1474,8 +1766,9 @@ break; default: - printk(KERN_ERR "%s: Error, Unknown tag type\n", __func__); + pr_info("%s: Error, Unknown tag type\n", __func__); } + pr_info(", leave_tag=%d\n", pdev->leave_tag); } EXPORT_SYMBOL(mv_mux_netdev_print); Index: drivers/net/ethernet/mvebu_net/netmux/mv_mux_netdev.h =================================================================== --- drivers/net/ethernet/mvebu_net/netmux/mv_mux_netdev.h (revision 1) +++ drivers/net/ethernet/mvebu_net/netmux/mv_mux_netdev.h (working copy) @@ -47,7 +47,35 @@ #define MV_MUX_SKB_TAG_SET(skb) (skb->skb_iif = (MV_MUX_SKB_TAG_VAL)) #define MV_MUX_SKB_IS_TAGGED(skb) (skb->skb_iif == (MV_MUX_SKB_TAG_VAL)) #endif +/*MV_MUX_UNKNOWN_GROUP is usedfor sysfs creating mux device.*/ +/*e.g. when switch is in HGU mode, then mux device is created in -1 group*/ +#define MV_MUX_UNKNOWN_GROUP (-1) +#define MV_MUX_GROUP_IDX_2_DB(idx) ((idx) == MV_MUX_UNKNOWN_GROUP ? 0 : idx) +/* Mux tag related definition */ +/* DSA/EDSA, the unit is Byte */ +#define MV_DSA_HDR_TAG_CMD_OFF (6) +#define MV_DSA_HDR_TAG_CMD_TO_CPU (0x0) +#define MV_DSA_HDR_TAG_CMD_FORWARD (0x3) +#define MV_DSA_HDR_TAG_CMD_MASK (0x3) +#define MV_DSA_HDR_TAG_CMD_GET(hdr) ((hdr[0] >> MV_DSA_HDR_TAG_CMD_OFF) & MV_DSA_HDR_TAG_CMD_MASK) + +#define MV_DSA_HDR_SRC_DEV_MASK (0x1f) +#define MV_DSA_HDR_SRC_DEV_GET(hdr) (hdr[0] & MV_DSA_HDR_SRC_DEV_MASK) + +#define MV_DSA_HDR_SRC_PORT_OFF (3) +#define MV_DSA_HDR_SRC_PORT_MASK (0x1f) +#define MV_DSA_HDR_SRC_PORT_GET(hdr) ((hdr[1] >> MV_DSA_HDR_SRC_PORT_OFF) & MV_DSA_HDR_SRC_PORT_MASK) +#define MV_EDSA_HDR_SRC_PORT_BIT5_GET(hdr) (hdr[6] & 0x4) + +#define MV_DSA_HDR_TRG_DEV_WORD_OFF (24) +#define MV_DSA_HDR_TRG_DEV_MASK (0x1f) +#define MV_DSA_HDR_TRG_PORT_WORD_OFF (19) +#define MV_DSA_HDR_TRG_PORT_MASK (0x1f) + +#define MV_DSA_HDR_TAGGED_MASK (0x20) +#define MV_DSA_HDR_TAGGED(hdr) (hdr[0] & MV_DSA_HDR_TAGGED_MASK) + extern const struct ethtool_ops mv_mux_tool_ops; struct mux_netdev { @@ -54,6 +82,7 @@ int idx; int port; bool leave_tag; + MV_U16 proto_type; MV_TAG tx_tag; MV_TAG rx_tag_ptrn; MV_TAG rx_tag_mask; Index: drivers/net/ethernet/mvebu_net/netmux/mv_mux_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/netmux/mv_mux_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/netmux/mv_mux_sysfs.c (working copy) @@ -48,12 +48,15 @@ o += sprintf(b+o, "echo name wL wH > edsa_tx - Set virt interface EDSA TX tag\n"); o += sprintf(b+o, "echo name wL wH > edsa_rx - Set virt interface EDSA RX tag\n"); o += sprintf(b+o, "echo name wL wH > edsa_rx_mask - Set virt interface EDSA RX mask tag\n"); + o += sprintf(b+o, "echo name en > leave_tag - Don't remove tag on RX and don't add tag on TX\n"); + o += sprintf(b+o, "echo name proto > proto_type - Set 16 bits protocol value to work with raw socket.\n"); #ifdef CONFIG_MV_ETH_DEBUG_CODE o += sprintf(b+o, "echo p hex > debug - bit0:rx, bit1:tx\n"); #endif o += sprintf(b+o, "\n"); - o += sprintf(b+o, "params: name-interface name, mh-2 bytes value(hex), dsa,edsa,vid-4 bytes value(hex)\n"); + o += sprintf(b+o, "params: name - interface name, please follow example: eth0m0 (device mux0 on port eth0)\n"); + o += sprintf(b+o, "params: mh-2 bytes value(hex), dsa,edsa,vid-4 bytes value(hex)\n"); return o; } @@ -95,48 +98,58 @@ } else if (!strcmp(name, "mux_vid")) { mv_mux_vlan_set(&mux_cfg, a); - err = mv_mux_netdev_alloc(dev_name, -1, &mux_cfg) ? 0 : 1; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; } else if (!strcmp(name, "mh_rx")) { mv_mux_cfg_get(mux_dev, &mux_cfg); mux_cfg.rx_tag_ptrn.mh = MV_16BIT_BE((MV_U16)a); mux_cfg.rx_tag_mask.mh = MV_16BIT_BE((MV_U16)b); - err = mv_mux_netdev_alloc(dev_name, -1, &mux_cfg) ? 0 : 1; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; } else if (!strcmp(name, "dsa_rx")) { mv_mux_cfg_get(mux_dev, &mux_cfg); mux_cfg.rx_tag_ptrn.dsa = a; mux_cfg.rx_tag_mask.dsa = b; - err = mv_mux_netdev_alloc(dev_name, -1, &mux_cfg) ? 0 : 1; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; } else if (!strcmp(name, "edsa_rx")) { mv_mux_cfg_get(mux_dev, &mux_cfg); mux_cfg.rx_tag_ptrn.edsa[0] = a; mux_cfg.rx_tag_ptrn.edsa[1] = b; - err = mv_mux_netdev_alloc(dev_name, -1, &mux_cfg) ? 0 : 1; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; } else if (!strcmp(name, "edsa_rx_mask")) { mv_mux_cfg_get(mux_dev, &mux_cfg); mux_cfg.rx_tag_mask.edsa[0] = a; mux_cfg.rx_tag_mask.edsa[1] = b; - err = mv_mux_netdev_alloc(dev_name, -1, &mux_cfg) ? 0 : 1; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; } else if (!strcmp(name, "mh_tx")) { mv_mux_cfg_get(mux_dev, &mux_cfg); mux_cfg.tx_tag.mh = MV_16BIT_BE((MV_U16)a); - err = mv_mux_netdev_alloc(dev_name, -1, &mux_cfg) ? 0 : 1; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; } else if (!strcmp(name, "dsa_tx")) { mv_mux_cfg_get(mux_dev, &mux_cfg); mux_cfg.tx_tag.dsa = a; - err = mv_mux_netdev_alloc(dev_name, -1, &mux_cfg) ? 0 : 1; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; } else if (!strcmp(name, "edsa_tx")) { mv_mux_cfg_get(mux_dev, &mux_cfg); mux_cfg.tx_tag.edsa[0] = a; mux_cfg.tx_tag.edsa[1] = b; - err = mv_mux_netdev_alloc(dev_name, -1, &mux_cfg) ? 0 : 1; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; + } else if (!strcmp(name, "leave_tag")) { + mv_mux_cfg_get(mux_dev, &mux_cfg); + mux_cfg.leave_tag = a; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; + + } else if (!strcmp(name, "proto_type")) { + mv_mux_cfg_get(mux_dev, &mux_cfg); + mux_cfg.proto_type = a; + err = mv_mux_netdev_alloc(dev_name, MV_MUX_UNKNOWN_GROUP, &mux_cfg) ? 0 : 1; + } else if (!strcmp(name, "add")) { err = mv_mux_netdev_add(a, mux_dev) ? 0 : 1; @@ -199,6 +212,8 @@ static DEVICE_ATTR(mh_tx, S_IWUSR, mv_mux_show, mv_mux_netdev_store); static DEVICE_ATTR(dsa_tx, S_IWUSR, mv_mux_show, mv_mux_netdev_store); static DEVICE_ATTR(edsa_tx, S_IWUSR, mv_mux_show, mv_mux_netdev_store); +static DEVICE_ATTR(leave_tag, S_IWUSR, mv_mux_show, mv_mux_netdev_store); +static DEVICE_ATTR(proto_type, S_IWUSR, mv_mux_show, mv_mux_netdev_store); static DEVICE_ATTR(tag_type, S_IWUSR, mv_mux_show, mv_mux_store); static DEVICE_ATTR(dump, S_IWUSR, mv_mux_show, mv_mux_store); static DEVICE_ATTR(debug, S_IWUSR, mv_mux_show, mv_mux_store); @@ -218,6 +233,8 @@ &dev_attr_mh_tx.attr, &dev_attr_dsa_tx.attr, &dev_attr_edsa_tx.attr, + &dev_attr_leave_tag.attr, + &dev_attr_proto_type.attr, &dev_attr_tag_type.attr, &dev_attr_dump.attr, &dev_attr_help.attr, Index: drivers/net/ethernet/mvebu_net/pp2/cls/cls2_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/cls/cls2_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/cls/cls2_sysfs.c (working copy) @@ -91,20 +91,14 @@ off += scnprintf(buf + off, PAGE_SIZE, "echo cmd q from > act_sw_queue - set full queue command and value to action\n"); off += scnprintf(buf + off, PAGE_SIZE, " table software entry. -source for Queue command.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo cmd > act_sw_hwf - set Forwarding command to action table SW entry.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo cmd id bank > act_sw_pol - set PolicerID command bank and number to action table SW entry.\n"); -#else - off += scnprintf(buf + off, PAGE_SIZE, "echo cmd id > act_sw_pol - set PolicerID command and number to action table SW entry.\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "echo en > act_sw_flowid - set FlowID enable/disable <1/0> to action table SW entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo d i cs > act_sw_mdf - set modification parameters to action table SW entry\n"); off += scnprintf(buf + off, PAGE_SIZE, " data pointer , instruction pointrt ,\n"); off += scnprintf(buf + off, PAGE_SIZE, " enable L4 checksum generation.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo idx > act_sw_mtu - set MTU index to action table SW entry\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo miss id > act_sw_sq - set miss bit and instruction ID to action table SW entry\n"); -#endif /*TODO ppv2.1: ADD sysfs command for mvPp2ClsC2SeqSet */ off += scnprintf(buf + off, PAGE_SIZE, "echo id cnt > act_sw_dup - set packet duplication parameters to action table SW entry.\n"); @@ -207,11 +201,7 @@ else if (!strcmp(name, "act_sw_hwf")) mvPp2ClsC2ForwardSet(&act_entry, a); else if (!strcmp(name, "act_sw_pol")) -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2ClsC2PolicerSet(&act_entry, a, b, c); -#else - mvPp2ClsC2PolicerSet(&act_entry, a, b); -#endif else if (!strcmp(name, "act_sw_mdf")) mvPp2ClsC2ModSet(&act_entry, a, b, c); else if (!strcmp(name, "act_sw_mtu"))/*PPv2.1 new feature MAS 3.7*/ Index: drivers/net/ethernet/mvebu_net/pp2/cls/cls3_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/cls/cls3_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/cls/cls3_sysfs.c (working copy) @@ -43,9 +43,7 @@ int off = 0; off += scnprintf(buf + off, PAGE_SIZE, "cat hw_dump - Dump all occupied entries from HW.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat hw_ext_dump - Dump all occupied extension table entries from HW.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "cat hw_ms_dump - Dump all miss table entires from HW.\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "cat sw_dump - Dump SW entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat sc_res_dump - Dump all valid scan results from HW.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat sc_regs - Dump scan registers.\n"); @@ -53,9 +51,7 @@ off += scnprintf(buf + off, PAGE_SIZE, "cat cnt_read_all - Dump all hit counters for all changed indices and miss entries\n"); off += scnprintf(buf + off, PAGE_SIZE, "\n"); off += scnprintf(buf + off, PAGE_SIZE, "\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo lkp_type > hw_ms_add - Write entry from SW into HW miss table \n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "echo depth > hw_query_add - Get query for HEK in the SW entry and Write entry into HW hash entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, " free entry search depth .\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo idx > hw_read - Read entry from HW into SW entry.\n"); @@ -78,16 +74,10 @@ off += scnprintf(buf + off, PAGE_SIZE, "echo cmd ql > act_sw_ql - Set Queue Low command and value to action table SW entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo cmd q > act_sw_queue - Set full Queue command and value to action table SW entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo cmd > act_sw_fwd - Set Forwarding command to action table SW entry.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo cmd id bnk > act_sw_pol - Set PolicerID command bank and number to action table SW entry.\n"); -#else - off += scnprintf(buf + off, PAGE_SIZE, "echo cmd id > act_sw_pol - Set PolicerID command and number to action table SW entry.\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "echo en > act_sw_flowid- Set FlowID enable/disable <1/0> to action table SW entry.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo idx > act_sw_mtu - Set MTU index to action table SW entry\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "echo d i cs > act_sw_mdf - Set modification parameters to action table SW entry data pointer \n"); off += scnprintf(buf + off, PAGE_SIZE, " instruction offset , enable L4 checksum generation\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo id cnt > act_sw_dup - Set packet duplication parameters to action SW entry.\n"); @@ -97,9 +87,7 @@ off += scnprintf(buf + off, PAGE_SIZE, "\n"); off += scnprintf(buf + off, PAGE_SIZE, "\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo idx > cnt_read - Show hit counter for action table entry .\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo lkp_type > cnt_ms_read - Show hit counter for action table miss entry .\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "echo 1 > cnt_clr_all - Clear hit counters for all action table entries.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo t > cnt_clr_lkp - Clear hit counters for all action table entries with lookup type .\n"); off += scnprintf(buf + off, PAGE_SIZE, "\n"); @@ -201,11 +189,7 @@ else if (!strcmp(name, "act_sw_fwd")) mvPp2ClsC3ForwardSet(&c3, a); else if (!strcmp(name, "act_sw_pol")) -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2ClsC3PolicerSet(&c3, a, b, c); -#else - mvPp2ClsC3PolicerSet(&c3, a, b); -#endif else if (!strcmp(name, "act_sw_flowid")) mvPp2ClsC3FlowIdEn(&c3, a); else if (!strcmp(name, "act_sw_mdf")) Index: drivers/net/ethernet/mvebu_net/pp2/cls/cls4_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/cls/cls4_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/cls/cls4_sysfs.c (working copy) @@ -46,9 +46,7 @@ off += scnprintf(buf + off, PAGE_SIZE, "cat sw_dump - Dump software entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat hw_regs - Dump hardware registers.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat hw_dump - Dump all hardware entries.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "cat hw_hits - Dump non zeroed hit counters and the associated HW entries\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo p s r > hw_port_rules - Set physical port number

for rules set .\n"); @@ -81,18 +79,11 @@ off += scnprintf(buf + off, PAGE_SIZE, " table software entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo cmd q > act_sw_ql - Set queue low command and value to action\n"); off += scnprintf(buf + off, PAGE_SIZE, " table software entry.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo cmd > act_sw_fwd - Set Forwarding command to action table software entry\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "echo cmd q > act_sw_queue - Set full queue command and value to action\n"); off += scnprintf(buf + off, PAGE_SIZE, " table software entry.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo cmd id bnk > act_sw_pol - Set PolicerId command bank and numver to action\n"); off += scnprintf(buf + off, PAGE_SIZE, " table software entry.\n"); -#else - off += scnprintf(buf + off, PAGE_SIZE, "echo cmd id > act_sw_pol - Set PolicerId command and numver to action\n"); - off += scnprintf(buf + off, PAGE_SIZE, " table software entry.\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "\n"); return off; @@ -181,11 +172,7 @@ else if (!strcmp(name, "act_sw_queue")) mvPp2ClsC4QueueSet(&C4, a, b); else if (!strcmp(name, "act_sw_pol")) -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2ClsC4PolicerSet(&C4, a, b, c); -#else - mvPp2ClsC4PolicerSet(&C4, a, b); -#endif else { err = 1; printk(KERN_ERR "%s: illegal operation <%s>\n", __func__, attr->attr.name); Index: drivers/net/ethernet/mvebu_net/pp2/cls/cls_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/cls/cls_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/cls/cls_sysfs.c (working copy) @@ -45,10 +45,8 @@ off += scnprintf(buf + off, PAGE_SIZE, "cat lkp_sw_dump - dump lookup ID table sw entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat flow_sw_dump - dump flow table SW entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat lkp_hw_dump - dump lookup ID tabel from hardware.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "cat flow_hw_hits - dump non zeroed hit counters and the associated flow tabel entries from hardware.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat lkp_hw_hits - dump non zeroed hit counters and the associated lookup ID entires from hardware.\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "cat flow_hw_dump - dump flow table from hardware.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat len_change_hw_dump - lkp dump sw entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "cat hw_regs - dump classifier top registers.\n"); @@ -65,20 +63,12 @@ off += scnprintf(buf + off, PAGE_SIZE, "echo virt gpid >hw_virt_gpid - set virtual port number for GemPortId .\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo a b c d >hw_udf - set UDF field as: base , offset bits, size bits.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo p q >hw_over_rxq_low - set oversize rx low queue for ingress port

.\n"); -#else - off += scnprintf(buf + off, PAGE_SIZE, "echo p q >hw_over_rxq - set oversize rxq for ingress port

.\n"); -#endif -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE, "echo p from q >hw_qh - set rx high queue source and queue for ingress port

.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo idx m >hw_mtu - set MTU value for index .\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo p v u mh >hw_mh - set port

enable/disable port Id generation for.\n"); off += scnprintf(buf + off, PAGE_SIZE, " virtual and uni ports, set default Marvell header .\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo idx size >hw_sq_size - set sequence id number size to flow table software entry\n"); -#else - off += scnprintf(buf + off, PAGE_SIZE, "echo p txp m >hw_mtu - set MTU value for egress port .\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo idx way >lkp_hw_write - write lookup ID table SW entry HW .\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo idx way >lkp_hw_read - read lookup ID table entry from HW .\n"); @@ -91,7 +81,6 @@ off += scnprintf(buf + off, PAGE_SIZE, "echo id >flow_hw_write - write flow table SW entry to HW .\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo id >flow_hw_read - read flow table entry from HW.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo t id >flow_sw_port - set port type and id

to flow table SW entry\n"); -#ifdef CONFIG_MV_ETH_PP2_1 /*PPv2.1 new feature MAS 3.18*/ off += scnprintf(buf + off, PAGE_SIZE, "echo from >flow_sw_portid - set cls to recive portid via packet \n"); off += scnprintf(buf + off, PAGE_SIZE, " or via user configurration to flow table SW entry.\n"); @@ -101,7 +90,6 @@ off += scnprintf(buf + off, PAGE_SIZE, "echo mode >flow_sw_udf7 - Set UDF7 lookup skip mode to flow table SW entry.\n"); /*PPv2.1 new feature MAS 3.14*/ off += scnprintf(buf + off, PAGE_SIZE, "echo mode >flow_sw_sq - Set sequence type to flow table SW entry.\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE, "echo e l >flow_sw_engin - set engine nember to flow table SW entry. - last bit.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo l p >flow_sw_extra - set lookup type and priority

to flow table SW entry.\n"); off += scnprintf(buf + off, PAGE_SIZE, "echo idx id >flow_sw_hek - set HEK field flow table SW entry.\n"); @@ -218,18 +206,9 @@ mvPp2ClsHwUdfSet(a, b, c, d); /*PPv2.1 feature changed MAS 3.7*/ else if (!strcmp(name, "hw_mtu")) -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2V1ClsHwMtuSet(a, b); -#else - mvPp2V0ClsHwMtuSet(a, b, c); -#endif -#ifdef CONFIG_MV_ETH_PP2_1 else if (!strcmp(name, "hw_over_rxq_low")) mvPp2ClsHwOversizeRxqLowSet(a, b); -#else - else if (!strcmp(name, "hw_over_rxq")) - mvPp2ClsHwOversizeRxqSet(a, b); -#endif /*PPv2.1 new feature MAS 3.5*/ else if (!strcmp(name, "hw_qh")) mvPp2ClsHwRxQueueHighSet(a, b, c); @@ -319,11 +298,7 @@ static DEVICE_ATTR(hw_virt_gpid, S_IWUSR, mv_cls_show, mv_prs_store_unsigned); static DEVICE_ATTR(hw_udf, S_IWUSR, mv_cls_show, mv_prs_store_unsigned); static DEVICE_ATTR(hw_mtu, S_IWUSR, mv_cls_show, mv_prs_store_unsigned); -#ifdef CONFIG_MV_ETH_PP2_1 static DEVICE_ATTR(hw_over_rxq_low, S_IWUSR, mv_cls_show, mv_prs_store_unsigned); -#else -static DEVICE_ATTR(hw_over_rxq, S_IWUSR, mv_cls_show, mv_prs_store_unsigned); -#endif static DEVICE_ATTR(hw_qh, S_IWUSR, mv_cls_show, mv_prs_store_unsigned); /*PPv2.1 new feature MAS 3.5*/ static DEVICE_ATTR(hw_mh, S_IWUSR, mv_cls_show, mv_prs_store_unsigned); /*PPv2.1 new feature MAS 3.18*/ static DEVICE_ATTR(hw_sq_size, S_IWUSR, mv_cls_show, mv_prs_store_unsigned); /*PPv2.1 new feature MAS 3.14*/ @@ -368,11 +343,7 @@ &dev_attr_hw_virt_gpid.attr, &dev_attr_hw_udf.attr, &dev_attr_hw_mtu.attr,/*PPv2.1 feature changed MAS 3.7*/ -#ifdef CONFIG_MV_ETH_PP2_1 &dev_attr_hw_over_rxq_low.attr,/*PPv2.1 feature changed MAS 3.7*/ -#else - &dev_attr_hw_over_rxq.attr, -#endif &dev_attr_hw_qh.attr,/*PPv2.1 new feature MAS 3.5*/ &dev_attr_hw_mh.attr,/*PPv2.1 new feature MAS 3.18*/ &dev_attr_hw_sq_size.attr,/*PPv2.1 new feature MAS 3.14*/ Index: drivers/net/ethernet/mvebu_net/pp2/cph/mv_cph_flow.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/cph/mv_cph_flow.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/cph/mv_cph_flow.c (working copy) @@ -1722,7 +1722,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_add_vlan(bool mh, unsigned char *p_data, unsigned short tpid, +int cph_flow_add_vlan(bool mh, unsigned char *p_data, unsigned short tpid, unsigned short vid, unsigned char pbits) { unsigned char *p_new = NULL; @@ -1766,7 +1766,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_del_vlan(bool mh, unsigned char *p_data) +int cph_flow_del_vlan(bool mh, unsigned char *p_data) { unsigned char *p_new = NULL; unsigned int len = 0; @@ -1799,7 +1799,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_strip_vlan(bool mh, unsigned char *p_data) +int cph_flow_strip_vlan(bool mh, unsigned char *p_data) { int offset = 0; int total_offset = 0; @@ -1845,7 +1845,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_replace_vlan(bool mh, unsigned char *p_data, unsigned short tpid, +int cph_flow_replace_vlan(bool mh, unsigned char *p_data, unsigned short tpid, unsigned short vid, unsigned char pbits) { unsigned short *p_vlan = NULL; @@ -1884,7 +1884,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_swap_vlan(bool mh, unsigned char *p_data) +int cph_flow_swap_vlan(bool mh, unsigned char *p_data) { unsigned int *p_tci = NULL; unsigned int tci1 = 0; Index: drivers/net/ethernet/mvebu_net/pp2/cph/mv_cph_flow.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/cph/mv_cph_flow.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/cph/mv_cph_flow.h (working copy) @@ -389,7 +389,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_add_vlan(bool mh, unsigned char *p_data, unsigned short tpid, +int cph_flow_add_vlan(bool mh, unsigned char *p_data, unsigned short tpid, unsigned short vid, unsigned char pbits); /****************************************************************************** @@ -408,7 +408,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_del_vlan(bool mh, unsigned char *p_data); +int cph_flow_del_vlan(bool mh, unsigned char *p_data); /****************************************************************************** * cph_flow_replace_vlan() @@ -429,7 +429,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_replace_vlan(bool mh, unsigned char *p_data, unsigned short tpid, +int cph_flow_replace_vlan(bool mh, unsigned char *p_data, unsigned short tpid, unsigned short vid, unsigned char pbits); /****************************************************************************** @@ -448,7 +448,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_swap_vlan(bool mh, unsigned char *p_data); +int cph_flow_swap_vlan(bool mh, unsigned char *p_data); /****************************************************************************** * cph_flow_strip_vlan() @@ -466,7 +466,7 @@ * RETURNS: * The shift of SKB data. *******************************************************************************/ -INLINE int cph_flow_strip_vlan(bool mh, unsigned char *p_data); +int cph_flow_strip_vlan(bool mh, unsigned char *p_data); /****************************************************************************** * cph_flow_compare_rules() Index: drivers/net/ethernet/mvebu_net/pp2/hal/bm/mvBm.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/bm/mvBm.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/bm/mvBm.c (working copy) @@ -93,7 +93,6 @@ memset(mvBmPools, 0, sizeof(mvBmPools)); -#ifdef CONFIG_MV_ETH_PP2_1 /* Enable BM priority */ mvPp2WrReg(MV_BM_PRIO_CTRL_REG, 1); @@ -109,7 +108,6 @@ mvBmTxqToQsetLong[i] = -1; mvBmTxqToQsetShort[i] = -1; } -#endif return MV_OK; } @@ -195,7 +193,6 @@ mvBmPoolControl(pool, MV_STOP); -#ifdef CONFIG_MV_ETH_PP2_1 /* Init Qsets list for this pool */ pBmPool->qsets = mvListCreate(); if (pBmPool->qsets == NULL) { @@ -220,7 +217,6 @@ /* Init default priority counters for this pool */ mvBmPoolBuffNumSet(pool, 0); mvBmPoolBuffCountersSet(pool, 0, 0); -#endif /* Set poolBase address */ mvPp2WrReg(MV_BM_POOL_BASE_REG(pool), physPoolBase); @@ -264,7 +260,6 @@ else pBmPool->bufNum -= buf_num; -#ifdef CONFIG_MV_ETH_PP2_1 /* Update max buffers of default Qset, MC Qset and pool shared */ if (add) { mvBmQsetBuffMaxSet(pBmPool->defQset->id, @@ -279,7 +274,6 @@ pBmPool->mcQset->maxGrntd, pBmPool->mcQset->maxShared - buf_num); mvBmPoolBuffNumSet(pool, pBmPool->maxShared - buf_num); } -#endif return MV_OK; } Index: drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls2Hw.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls2Hw.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls2Hw.c (working copy) @@ -406,10 +406,8 @@ /* write dup_attr 0x1B6C */ mvPp2WrReg(MV_PP2_CLS2_ACT_DUP_ATTR_REG, c2->sram.regs.dup_attr); -#ifdef CONFIG_MV_ETH_PP2_1 /* write seq_attr 0x1B70 */ mvPp2WrReg(MV_PP2_CLS2_ACT_SEQ_ATTR_REG, c2->sram.regs.seq_attr); -#endif return MV_OK; } /*-------------------------------------------------------------------------------*/ @@ -455,10 +453,8 @@ /* read dup_attr 0x1B6C */ c2->sram.regs.dup_attr = mvPp2RdReg(MV_PP2_CLS2_ACT_DUP_ATTR_REG); -#ifdef CONFIG_MV_ETH_PP2_1 /* read seq_attr 0x1B70 */ c2->sram.regs.seq_attr = mvPp2RdReg(MV_PP2_CLS2_ACT_SEQ_ATTR_REG); -#endif return MV_OK; } @@ -595,11 +591,7 @@ /*------------------------------*/ /* hwf_attr 0x1B68 */ /*------------------------------*/ -#ifdef CONFIG_MV_ETH_PP2_1 mvOsPrintf("HWF_ATTR: IPTR DPTR CHKSM MTU_IDX\n"); -#else - mvOsPrintf("HWF_ATTR: IPTR DPTR CHKSM\n"); -#endif mvOsPrintf(" "); /* HWF modification instraction pointer */ @@ -614,17 +606,14 @@ int32bit = ((c2->sram.regs.hwf_attr & ACT_HWF_ATTR_CHKSM_EN_MASK) >> ACT_HWF_ATTR_CHKSM_EN); mvOsPrintf("%s\t", int32bit ? "ENABLE " : "DISABLE"); -#ifdef CONFIG_MV_ETH_PP2_1 /* mtu index */ int32bit = ((c2->sram.regs.hwf_attr & ACT_HWF_ATTR_MTU_INX_MASK) >> ACT_HWF_ATTR_MTU_INX); mvOsPrintf("0x%1.1x\t", int32bit); -#endif mvOsPrintf("\n\n"); /*------------------------------*/ /* dup_attr 0x1B6C */ /*------------------------------*/ -#ifdef CONFIG_MV_ETH_PP2_1 mvOsPrintf("DUP_ATTR: FID COUNT POLICER [id bank]\n"); mvOsPrintf(" 0x%2.2x\t0x%1.1x\t\t[0x%2.2x 0x%1.1x]\n", ((c2->sram.regs.dup_attr & ACT_DUP_FID_MASK) >> ACT_DUP_FID), @@ -643,16 +632,7 @@ mvOsPrintf("\n\n"); -#else - mvOsPrintf("DUP_ATTR: FID COUNT POLICER\n"); - mvOsPrintf(" 0x%2.2x\t0x%1.1x\t0x%2.2x", - ((c2->sram.regs.dup_attr & ACT_DUP_FID_MASK) >> ACT_DUP_FID), - ((c2->sram.regs.dup_attr & ACT_DUP_COUNT_MASK) >> ACT_DUP_COUNT), - ((c2->sram.regs.dup_attr & ACT_DUP_POLICER_MASK) >> ACT_DUP_POLICER_ID)); - mvOsPrintf("\n\n"); -#endif - return MV_OK; } /*-------------------------------------------------------------------------------*/ @@ -960,7 +940,6 @@ return MV_OK; } /*-------------------------------------------------------------------------------*/ -#ifdef CONFIG_MV_ETH_PP2_1 int mvPp2ClsC2PolicerSet(MV_PP2_CLS_C2_ENTRY *c2, int cmd, int policerId, int bank) { PTR_VALIDATE(c2); @@ -983,21 +962,6 @@ } -#else -int mvPp2ClsC2PolicerSet(MV_PP2_CLS_C2_ENTRY *c2, int cmd, int policerId) -{ - PTR_VALIDATE(c2); - POS_RANGE_VALIDATE(cmd, UPDATE_AND_LOCK); - POS_RANGE_VALIDATE(policerId, ACT_DUP_POLICER_MAX); - - c2->sram.regs.actions &= ~ACT_POLICER_SELECT_MASK; - c2->sram.regs.actions |= (cmd << ACT_POLICER_SELECT); - - c2->sram.regs.dup_attr &= ~ACT_DUP_POLICER_MASK; - c2->sram.regs.dup_attr |= (policerId << ACT_DUP_POLICER_ID); - return MV_OK; -} -#endif /*CONFIG_MV_ETH_PP2_1*/ /*-------------------------------------------------------------------------------*/ int mvPp2ClsC2FlowIdEn(MV_PP2_CLS_C2_ENTRY *c2, int flowid_en) @@ -1165,9 +1129,7 @@ mvPp2PrintReg(MV_PP2_CLS2_ACT_QOS_ATTR_REG, "MV_PP2_CLS2_ACT_QOS_ATTR_REG"); mvPp2PrintReg(MV_PP2_CLS2_ACT_HWF_ATTR_REG, "MV_PP2_CLS2_ACT_HWF_ATTR_REG"); mvPp2PrintReg(MV_PP2_CLS2_ACT_DUP_ATTR_REG, "MV_PP2_CLS2_ACT_DUP_ATTR_REG"); -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2PrintReg(MV_PP2_CLS2_ACT_SEQ_ATTR_REG, "MV_PP2_CLS2_ACT_SEQ_ATTR_REG"); -#endif return MV_OK; } Index: drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls2Hw.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls2Hw.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls2Hw.h (working copy) @@ -109,11 +109,7 @@ #define MV_PP2_CLS2_HIT_CTR_REG (MV_PP2_REG_BASE + 0x1B50) #define MV_PP2_CLS2_HIT_CTR_OFF 0 -#ifdef CONFIG_MV_ETH_PP2_1 #define MV_PP2_CLS2_HIT_CTR_BITS 32 -#else -#define MV_PP2_CLS2_HIT_CTR_BITS 24 -#endif #define MV_PP2_CLS2_HIT_CTR_MASK ((1 << MV_PP2_CLS2_HIT_CTR_BITS) - 1) /*-------------------------------------------------------------------------------*/ @@ -278,11 +274,7 @@ int mvPp2ClsC2QueueSet(MV_PP2_CLS_C2_ENTRY *c2, int cmd, int queue, int from); int mvPp2ClsC2ForwardSet(MV_PP2_CLS_C2_ENTRY *c2, int cmd); -#ifdef CONFIG_MV_ETH_PP2_1 int mvPp2ClsC2PolicerSet(MV_PP2_CLS_C2_ENTRY *c2, int cmd, int policerId, int bank); -#else -int mvPp2ClsC2PolicerSet(MV_PP2_CLS_C2_ENTRY *c2, int cmd, int policerId); -#endif int mvPp2ClsC2FlowIdEn(MV_PP2_CLS_C2_ENTRY *c2, int flowid_en); int mvPp2ClsC2ModSet(MV_PP2_CLS_C2_ENTRY *c2, int data_ptr, int instr_offs, int l4_csum); Index: drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls3Hw.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls3Hw.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls3Hw.c (working copy) @@ -211,21 +211,6 @@ } /*------------------------------------------------------------------------------- - bank first 2 entries are reserved for miss actions ---------------------------------------------------------------------------------*/ -static int mvPp2ClsC3IsReservedIndex(int index) -{ -#ifdef CONFIG_MV_ETH_PP2_1 - return MV_FALSE; -#endif - if ((index % MV_PP2_CLS_C3_BANK_SIZE) > 1) - /* not reserved */ - return MV_FALSE; - - return MV_TRUE; -} - -/*------------------------------------------------------------------------------- Add entry to hash table ext_index used only if hek size < 12 -------------------------------------------------------------------------------*/ @@ -266,11 +251,7 @@ regVal |= (1 << MV_PP2_CLS3_HASH_OP_ADD); /* set hit counter init value */ -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2WrReg(MV_PP2_CLS3_INIT_HIT_CNT_REG, SwInitCntSet << MV_PP2_CLS3_INIT_HIT_CNT_OFFS); -#else - regVal |= (SwInitCntSet << MV_PP2_CLS3_HASH_OP_INIT_CTR_VAL); -#endif /*trigger ADD operation*/ mvPp2WrReg(MV_PP2_CLS3_HASH_OP_REG, regVal); @@ -286,10 +267,8 @@ mvPp2WrReg(MV_PP2_CLS3_ACT_QOS_ATTR_REG, c3->sram.regs.qos_attr); mvPp2WrReg(MV_PP2_CLS3_ACT_HWF_ATTR_REG, c3->sram.regs.hwf_attr); mvPp2WrReg(MV_PP2_CLS3_ACT_DUP_ATTR_REG, c3->sram.regs.dup_attr); -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2WrReg(MV_PP2_CLS3_ACT_SEQ_L_ATTR_REG, c3->sram.regs.seq_l_attr); mvPp2WrReg(MV_PP2_CLS3_ACT_SEQ_H_ATTR_REG, c3->sram.regs.seq_h_attr); -#endif /* set entry as valid, extesion pointer in use only if size > 12*/ mvPp2ClsC3ShadowSet(hekSize, index, ext_index); @@ -407,7 +386,7 @@ /* fill in indices for this key */ for (idx = 0; idx < MV_PP2_CLS3_HASH_BANKS_NUM; idx++) { /* if new index is in the bank index, skip it */ - if (new_idx == usedIndex[idx] || mvPp2ClsC3IsReservedIndex(usedIndex[idx])) { + if (new_idx == usedIndex[idx]) { usedIndex[idx] = 0; continue; } @@ -487,7 +466,6 @@ /* Select avaliable entry index */ for (idx = 0; idx < MV_PP2_CLS3_HASH_BANKS_NUM; idx++) { if (!(occupied_bmp & (1 << idx))) - if (!mvPp2ClsC3IsReservedIndex(usedIndex[idx])) break; } @@ -495,16 +473,7 @@ if (idx == MV_PP2_CLS3_HASH_BANKS_NUM) { - /* save all valid bank indices */ for (idx = 0; idx < MV_PP2_CLS3_HASH_BANKS_NUM; idx++) { - if (mvPp2ClsC3IsReservedIndex(usedIndex[idx])) - usedIndex[idx] = 0; - } - - for (idx = 0; idx < MV_PP2_CLS3_HASH_BANKS_NUM; idx++) { - if (mvPp2ClsC3IsReservedIndex(usedIndex[idx])) - continue; - if (mvPp2ClsC3HwQueryAddRelocate(usedIndex[idx], max_search_depth, 0 /*curren depth*/, hash_pair_arr) == MV_OK) break; @@ -525,7 +494,7 @@ /* Get Free Extension Index */ ext_index = mvPp2ClsC3ShadowExtFreeGet(); - if (ext_index == MV_PP2_CLS_C3_HASH_TBL_SIZE) { + if (ext_index == MV_PP2_CLS_C3_EXT_TBL_SIZE) { mvOsPrintf("%s:Error - Extension table is full.\n", __func__); return MV_CLS3_ERR; } @@ -623,10 +592,8 @@ c3->sram.regs.hwf_attr = mvPp2RdReg(MV_PP2_CLS3_ACT_HWF_ATTR_REG); c3->sram.regs.dup_attr = mvPp2RdReg(MV_PP2_CLS3_ACT_DUP_ATTR_REG); -#ifdef CONFIG_MV_ETH_PP2_1 c3->sram.regs.seq_l_attr = mvPp2RdReg(MV_PP2_CLS3_ACT_SEQ_L_ATTR_REG); c3->sram.regs.seq_h_attr = mvPp2RdReg(MV_PP2_CLS3_ACT_SEQ_H_ATTR_REG); -#endif /* read hash data*/ for (i = 0; i < MV_PP2_CLS3_HASH_DATA_REG_NUM; i++) @@ -705,7 +672,6 @@ c3->key.key_ctrl |= (((hashData[3] & KEY_PRT_ID_MASK(isExt)) >> (KEY_PRT_ID(isExt) % DWORD_BITS_LEN)) << KEY_CTRL_PRT_ID); -#ifdef CONFIG_MV_ETH_PP2_1 /* PPv2.1 (feature MAS 3.16) LKP_TYPE size and offset changed */ c3->key.key_ctrl |= (((hashData[3] & KEY_PRT_ID_TYPE_MASK(isExt)) >> @@ -714,15 +680,7 @@ c3->key.key_ctrl |= ((((hashData[2] & 0xf8000000) >> 27) | ((hashData[3] & 0x1) << 5)) << KEY_CTRL_LKP_TYPE); -#else - c3->key.key_ctrl |= ((((hashData[2] & 0x80000000) >> 31) | - ((hashData[3] & 0x1) << 1)) << KEY_CTRL_PRT_ID_TYPE); - c3->key.key_ctrl |= (((hashData[2] & KEY_LKP_TYPE_MASK(isExt)) >> - (KEY_LKP_TYPE(isExt) % DWORD_BITS_LEN)) << KEY_CTRL_LKP_TYPE); - -#endif /* CONFIG_MV_ETH_PP2_1 */ - c3->key.key_ctrl |= (((hashData[2] & KEY_L4_INFO_MASK(isExt)) >> (KEY_L4_INFO(isExt) % DWORD_BITS_LEN)) << KEY_CTRL_L4); } @@ -813,7 +771,6 @@ PTR_VALIDATE(c3); mvOsPrintf("\n"); -#ifdef CONFIG_MV_ETH_PP2_1 /*------------------------------*/ /* actions 0x1D40 */ /*------------------------------*/ @@ -858,50 +815,7 @@ mvOsPrintf("SEQ_ATTR: HIGH[32:37] LOW[0:31]\n"); mvOsPrintf(" 0x%2.2x 0x%8.8x", c3->sram.regs.seq_h_attr, c3->sram.regs.seq_l_attr); -#else - /*------------------------------*/ - /* actions 0x1D40 */ - /*------------------------------*/ - mvOsPrintf("ACT_TBL: COLOR LOW_Q HIGH_Q FWD POLICER FID\n"); - mvOsPrintf("CMD: [%1d] [%1d] [%1d] [%1d] [%1d] [%1d]\n", - ((c3->sram.regs.actions & (ACT_COLOR_MASK)) >> ACT_COLOR), - ((c3->sram.regs.actions & (ACT_LOW_Q_MASK)) >> ACT_LOW_Q), - ((c3->sram.regs.actions & (ACT_HIGH_Q_MASK)) >> ACT_HIGH_Q), - ((c3->sram.regs.actions & ACT_FWD_MASK) >> ACT_FWD), - ((c3->sram.regs.actions & (ACT_POLICER_SELECT_MASK)) >> ACT_POLICER_SELECT), - ((c3->sram.regs.actions & ACT_FLOW_ID_EN_MASK) >> ACT_FLOW_ID_EN)); - - mvOsPrintf("VAL: [%1d] [0x%x] [0x%x]\n", - ((c3->sram.regs.qos_attr & (ACT_QOS_ATTR_MDF_LOW_Q_MASK)) >> ACT_QOS_ATTR_MDF_LOW_Q), - ((c3->sram.regs.qos_attr & (ACT_QOS_ATTR_MDF_HIGH_Q_MASK)) >> ACT_QOS_ATTR_MDF_HIGH_Q), - ((c3->sram.regs.dup_attr & (ACT_DUP_POLICER_MASK)) >> ACT_DUP_POLICER_ID)); - mvOsPrintf("\n"); - - /*------------------------------*/ - /* hwf_attr 0x1D48 */ - /*------------------------------*/ - - mvOsPrintf("HWF_ATTR: IPTR DPTR CHKSM\n"); - mvOsPrintf(" 0x%1.1x 0x%4.4x %s\t", - ((c3->sram.regs.hwf_attr & ACT_HWF_ATTR_IPTR_MASK) >> ACT_HWF_ATTR_IPTR), - ((c3->sram.regs.hwf_attr & ACT_HWF_ATTR_DPTR_MASK) >> ACT_HWF_ATTR_DPTR), - (((c3->sram.regs.hwf_attr & ACT_HWF_ATTR_CHKSM_EN_MASK) >> ACT_HWF_ATTR_CHKSM_EN) ? "ENABLE" : "DISABLE")); - - mvOsPrintf("\n"); - - /*------------------------------*/ - /* dup_attr 0x1D4C */ - /*------------------------------*/ - - mvOsPrintf("DUP_ATTR: FID COUNT\n"); - mvOsPrintf(" 0x%2.2x 0x%1.1x\n", - ((c3->sram.regs.dup_attr & ACT_DUP_FID_MASK) >> ACT_DUP_FID), - ((c3->sram.regs.dup_attr & ACT_DUP_COUNT_MASK) >> ACT_DUP_COUNT)); - - -#endif /* CONFIG_MV_ETH_PP2_1 */ - mvOsPrintf("\n\n"); return MV_OK; @@ -1128,7 +1042,6 @@ return MV_OK; } /*-------------------------------------------------------------------------------*/ -#ifdef CONFIG_MV_ETH_PP2_1 int mvPp2ClsC3PolicerSet(MV_PP2_CLS_C3_ENTRY *c3, int cmd, int policerId, int bank) { PTR_VALIDATE(c3); @@ -1149,21 +1062,6 @@ return MV_OK; } -#else -int mvPp2ClsC3PolicerSet(MV_PP2_CLS_C3_ENTRY *c3, int cmd, int policerId) -{ - PTR_VALIDATE(c3); - POS_RANGE_VALIDATE(cmd, UPDATE_AND_LOCK); - POS_RANGE_VALIDATE(policerId, ACT_DUP_POLICER_MAX); - - c3->sram.regs.actions &= ~ACT_POLICER_SELECT_MASK; - c3->sram.regs.actions |= (cmd << ACT_POLICER_SELECT); - - c3->sram.regs.dup_attr &= ~ACT_DUP_POLICER_MASK; - c3->sram.regs.dup_attr |= (policerId << ACT_DUP_POLICER_ID); - return MV_OK; -} -#endif /*CONFIG_MV_ETH_PP2_1*/ /*-------------------------------------------------------------------------------*/ int mvPp2ClsC3FlowIdEn(MV_PP2_CLS_C3_ENTRY *c3, int flowid_en) { @@ -1304,11 +1202,7 @@ PPv2.1 (feature MAS 3.16) CLEAR_COUNTERS size changed, clear all code changed from 0x1f to 0x3f */ -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2WrReg(MV_PP2_CLS3_CLEAR_COUNTERS_REG, MV_PP2_V1_CLS3_CLEAR_ALL); -#else - mvPp2WrReg(MV_PP2_CLS3_CLEAR_COUNTERS_REG, MV_PP2_V0_CLS3_CLEAR_ALL); -#endif /* wait to clear het counters done bit */ while (!mvPp2ClsC3HitCntrClearDone()) if (++iter >= RETRIES_EXCEEDED) { @@ -1330,11 +1224,7 @@ mvPp2WrReg(MV_PP2_CLS3_DB_INDEX_REG, index); /*counter read*/ -#ifdef CONFIG_MV_ETH_PP2_1 counter = mvPp2RdReg(MV_PP2_CLS3_HIT_COUNTER_REG) & MV_PP2_V1_CLS3_HIT_COUNTER_MASK; -#else - counter = mvPp2RdReg(MV_PP2_CLS3_HIT_COUNTER_REG) & MV_PP2_V0_CLS3_HIT_COUNTER_MASK; -#endif if (!cntr) mvOsPrintf("ADDR:0x%3.3x COUNTER VAL:0x%6.6x\n", index, counter); @@ -1384,7 +1274,6 @@ mvOsPrintf("ADDR:0x%3.3x COUNTER VAL:0x%6.6x\n", index, counter); } -#ifdef CONFIG_MV_ETH_PP2_1 for (index = 0; index < MV_PP2_CLS_C3_MISS_TBL_SIZE; index++) { mvPp2ClsC3HitCntrsMissRead(index, &counter); @@ -1394,7 +1283,6 @@ mvOsPrintf("LKPT:0x%3.3x COUNTER VAL:0x%6.6x\n", index, counter); } -#endif return MV_OK; } @@ -1424,10 +1312,9 @@ int mvPp2ClsC3ScanRegs() { unsigned int prop, propVal; -#ifdef CONFIG_MV_ETH_PP2_1 unsigned int treshHold; treshHold = mvPp2RdReg(MV_PP2_CLS3_SC_TH_REG); -#endif + prop = mvPp2RdReg(MV_PP2_CLS3_SC_PROP_REG); propVal = mvPp2RdReg(MV_PP2_CLS3_SC_PROP_VAL_REG); @@ -1446,7 +1333,6 @@ /* start index */ mvOsPrintf("START = 0x%x\n", (MV_PP2_CLS3_SC_PROP_START_ENTRY_MASK & prop) >> MV_PP2_CLS3_SC_PROP_START_ENTRY); -#ifdef CONFIG_MV_ETH_PP2_1 /* threshold */ mvOsPrintf("THRESHOLD = 0x%x\n", (MV_PP2_CLS3_SC_TH_MASK & treshHold) >> MV_PP2_CLS3_SC_TH); @@ -1454,15 +1340,6 @@ mvOsPrintf("DELAY = 0x%x\n\n", (MV_PP2_V1_CLS3_SC_PROP_VAL_DELAY_MASK & propVal) >> MV_PP2_V1_CLS3_SC_PROP_VAL_DELAY); -#else - /* threshold */ - mvOsPrintf("THRESHOLD = 0x%x\n", - (MV_PP2_V0_CLS3_SC_PROP_VAL_TH_MASK & propVal) >> MV_PP2_V0_CLS3_SC_PROP_VAL_TH); - - /* delay value */ - mvOsPrintf("DELAY = 0x%x\n\n", - (MV_PP2_V0_CLS3_SC_PROP_VAL_DELAY_MASK & propVal) >> MV_PP2_V0_CLS3_SC_PROP_VAL_DELAY); -#endif return MV_OK; } /*-------------------------------------------------------------------------------*/ @@ -1473,11 +1350,7 @@ unsigned int regVal; POS_RANGE_VALIDATE(mode, 1); /* one bit */ -#ifdef CONFIG_MV_ETH_PP2_1 POS_RANGE_VALIDATE(thresh, MV_PP2_CLS3_SC_TH_MAX); -#else - POS_RANGE_VALIDATE(thresh, MV_PP2_V0_CLS3_SC_PROP_VAL_TH_MAX); -#endif regVal = mvPp2RdReg(MV_PP2_CLS3_SC_PROP_REG); regVal &= ~MV_PP2_CLS3_SC_PROP_TH_MODE_MASK; @@ -1484,17 +1357,10 @@ regVal |= (mode << MV_PP2_CLS3_SC_PROP_TH_MODE); mvPp2WrReg(MV_PP2_CLS3_SC_PROP_REG, regVal); -#ifdef CONFIG_MV_ETH_PP2_1 regVal = mvPp2RdReg(MV_PP2_CLS3_SC_TH_REG); regVal &= ~MV_PP2_CLS3_SC_TH_MASK; regVal |= (thresh << MV_PP2_CLS3_SC_TH); mvPp2WrReg(MV_PP2_CLS3_SC_TH_REG, regVal); -#else - regVal = mvPp2RdReg(MV_PP2_CLS3_SC_PROP_VAL_REG); - regVal &= ~MV_PP2_V0_CLS3_SC_PROP_VAL_TH_MASK; - regVal |= (thresh << MV_PP2_V0_CLS3_SC_PROP_VAL_TH); - mvPp2WrReg(MV_PP2_CLS3_SC_PROP_VAL_REG, regVal); -#endif return MV_OK; } @@ -1563,13 +1429,8 @@ POS_RANGE_VALIDATE(time, MV_PP2_CLS3_SC_PROP_VAL_DELAY_MAX); propVal = mvPp2RdReg(MV_PP2_CLS3_SC_PROP_VAL_REG); -#ifdef CONFIG_MV_ETH_PP2_1 propVal &= ~MV_PP2_V1_CLS3_SC_PROP_VAL_DELAY_MASK; propVal |= (time << MV_PP2_V1_CLS3_SC_PROP_VAL_DELAY); -#else - propVal &= ~MV_PP2_V0_CLS3_SC_PROP_VAL_DELAY_MASK; - propVal |= (time << MV_PP2_V0_CLS3_SC_PROP_VAL_DELAY); -#endif mvPp2WrReg(MV_PP2_CLS3_SC_PROP_VAL_REG, propVal); return MV_OK; @@ -1598,11 +1459,7 @@ /*read date*/ regVal = mvPp2RdReg(MV_PP2_CLS3_SC_RES_REG); addres = (regVal & MV_PP2_CLS3_SC_RES_ENTRY_MASK) >> MV_PP2_CLS3_SC_RES_ENTRY; -#ifdef CONFIG_MV_ETH_PP2_1 counter = (regVal & MV_PP2_V1_CLS3_SC_RES_CTR_MASK) >> MV_PP2_V1_CLS3_SC_RES_CTR; -#else - counter = (regVal & MV_PP2_V0_CLS3_SC_RES_CTR_MASK) >> MV_PP2_V0_CLS3_SC_RES_CTR; -#endif /* if one of parameters is null - func call from sysfs*/ if ((!addr) | (!cnt)) mvOsPrintf("INDEX:0x%2.2x ADDR:0x%3.3x COUNTER VAL:0x%6.6x\n", index, addres, counter); Index: drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls3Hw.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls3Hw.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls3Hw.h (working copy) @@ -84,13 +84,8 @@ /* PPv2.1 (feature MAS 3.16) LKP_TYPE size and offset changed */ -#ifdef CONFIG_MV_ETH_PP2_1 #define KEY_CTRL_LKP_TYPE 4 #define KEY_CTRL_LKP_TYPE_BITS 6 -#else -#define KEY_CTRL_LKP_TYPE 8 -#define KEY_CTRL_LKP_TYPE_BITS 4 -#endif #define KEY_CTRL_LKP_TYPE_MAX ((1 << KEY_CTRL_LKP_TYPE_BITS) - 1) #define KEY_CTRL_LKP_TYPE_MASK (((1 << KEY_CTRL_LKP_TYPE_BITS) - 1) << KEY_CTRL_LKP_TYPE) @@ -244,11 +239,7 @@ PPv2.1 (feature MAS 3.16) LKP_TYPE size and offset changed */ -#ifdef CONFIG_MV_ETH_PP2_1 #define MV_PP2_CLS3_SC_PROP_LKP_TYPE_BITS 6 -#else -#define MV_PP2_CLS3_SC_PROP_LKP_TYPE_BITS 4 -#endif #define MV_PP2_CLS3_SC_PROP_LKP_TYPE_MAX ((1 << MV_PP2_CLS3_SC_PROP_LKP_TYPE_BITS) - 1) #define MV_PP2_CLS3_SC_PROP_LKP_TYPE_MASK ((MV_PP2_CLS3_SC_PROP_LKP_TYPE_MAX) << MV_PP2_CLS3_SC_PROP_LKP_TYPE) @@ -335,7 +326,6 @@ /* Classifier C3 offsets in hash table */ /*-------------------------------------------------------------------------------*/ /* PPv2.1 (feature MAS 3.16) LKP_TYPE size and offset changed */ -#ifdef CONFIG_MV_ETH_PP2_1 #define KEY_OCCUPIED 116 #define KEY_FORMAT 115 @@ -347,20 +337,6 @@ #define KEY_PRT_ID_TYPE(ext_mode) ((ext_mode == 1) ? (97) : (105)) #define KEY_PRT_ID_TYPE_MASK(ext_mode) ((KEY_CTRL_PRT_ID_TYPE_MAX) << (KEY_PRT_ID_TYPE(ext_mode) % 32)) -#else - -#define KEY_OCCUPIED 114 -#define KEY_FORMAT 113 -#define KEY_PTR_EXT 105 - -#define KEY_PRT_ID(ext_mode) ((ext_mode == 1) ? (97) : (105)) -#define KEY_PRT_ID_MASK(ext_mode) (((1 << KEY_CTRL_PRT_ID_BITS) - 1) << (KEY_PRT_ID(ext_mode) % 32)) - -#define KEY_PRT_ID_TYPE(ext_mode) ((ext_mode == 1) ? (95) : (103)) -#define KEY_PRT_ID_TYPE_MASK(ext_mode) ((KEY_CTRL_PRT_ID_TYPE_MAX) << (KEY_PRT_ID_TYPE(ext_mode) % 32)) - -#endif /* CONFIG_MV_ETH_PP2_1 */ - #define KEY_LKP_TYPE(ext_mode) ((ext_mode == 1) ? (91) : (99)) #define KEY_LKP_TYPE_MASK(ext_mode) (((1 << KEY_CTRL_LKP_TYPE_BITS) - 1) << (KEY_LKP_TYPE(ext_mode) % 32)) @@ -477,11 +453,7 @@ int mvPp2ClsC3QueueLowSet(MV_PP2_CLS_C3_ENTRY *c3, int cmd, int q); int mvPp2ClsC3QueueSet(MV_PP2_CLS_C3_ENTRY *c3, int cmd, int queue); int mvPp2ClsC3ForwardSet(MV_PP2_CLS_C3_ENTRY *c3, int cmd); -#ifdef CONFIG_MV_ETH_PP2_1 int mvPp2ClsC3PolicerSet(MV_PP2_CLS_C3_ENTRY *c3, int cmd, int policerId, int bank); -#else -int mvPp2ClsC3PolicerSet(MV_PP2_CLS_C3_ENTRY *c3, int cmd, int policerId); -#endif int mvPp2ClsC3FlowIdEn(MV_PP2_CLS_C3_ENTRY *c3, int flowid_en); /* PPv2.1 (feature MAS 3.7) mtu - new field at action table */ Index: drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls4Hw.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls4Hw.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls4Hw.c (working copy) @@ -249,7 +249,6 @@ PPv2.1 (feature MAS 3.9) Add forwarding command to C4 */ -#ifdef CONFIG_MV_ETH_PP2_1 mvOsPrintf("ACT_TBL:COLOR PRIO DSCP GPID LOW_Q HIGH_Q POLICER FWD\n"); mvOsPrintf("CMD: [%1d] [%1d] [%1d] [%1d] [%1d] [%1d] [%1d] [%1d]\n", ((C4->sram.regs.actions & (ACT_COLOR_MASK)) >> ACT_COLOR), @@ -278,41 +277,13 @@ ((C4->sram.regs.dup_attr & (ACT_DUP_POLICER_MASK)) >> ACT_DUP_POLICER_ID), ((C4->sram.regs.dup_attr & ACT_DUP_POLICER_BANK_MASK) >> ACT_DUP_POLICER_BANK_BIT)); -#else - mvOsPrintf("ACT_TBL: COLOR PRIO DSCP GPID LOW_Q HIGH_Q POLICER\n"); - mvOsPrintf("CMD: [%1d] [%1d] [%1d] [%1d] [%1d] [%1d] [%1d]\n", - ((C4->sram.regs.actions & (ACT_COLOR_MASK)) >> ACT_COLOR), - ((C4->sram.regs.actions & (ACT_PRI_MASK)) >> ACT_PRI), - ((C4->sram.regs.actions & (ACT_DSCP_MASK)) >> ACT_DSCP), - ((C4->sram.regs.actions & (ACT_GEM_ID_MASK)) >> ACT_GEM_ID), - ((C4->sram.regs.actions & (ACT_LOW_Q_MASK)) >> ACT_LOW_Q), - ((C4->sram.regs.actions & (ACT_HIGH_Q_MASK)) >> ACT_HIGH_Q), - ((C4->sram.regs.actions & (ACT_POLICER_SELECT_MASK)) >> ACT_POLICER_SELECT)); - - /*------------------------------*/ - /* qos_attr 0x1E84 */ - /*------------------------------*/ - /*mvOsPrintf("VAL: PRIO DSCP GPID LOW_Q HIGH_Q POLICER\n");*/ - - mvOsPrintf("VAL: [%1d] [%1d] [%1d] [%1d] [0x%x] [%1d]\n", - ((C4->sram.regs.qos_attr & (ACT_QOS_ATTR_MDF_PRI_MASK)) >> ACT_QOS_ATTR_MDF_PRI), - ((C4->sram.regs.qos_attr & (ACT_QOS_ATTR_MDF_DSCP_MASK)) >> ACT_QOS_ATTR_MDF_DSCP), - ((C4->sram.regs.qos_attr & (ACT_QOS_ATTR_MDF_GEM_ID_MASK)) >> ACT_QOS_ATTR_MDF_GEM_ID), - ((C4->sram.regs.qos_attr & (ACT_QOS_ATTR_MDF_LOW_Q_MASK)) >> ACT_QOS_ATTR_MDF_LOW_Q), - ((C4->sram.regs.qos_attr & (ACT_QOS_ATTR_MDF_HIGH_Q_MASK)) >> ACT_QOS_ATTR_MDF_HIGH_Q), - ((C4->sram.regs.dup_attr & (ACT_DUP_POLICER_MASK)) >> ACT_DUP_POLICER_ID)); - -#endif - - - return MV_OK; } /*-------------------------------------------------------------------------------*/ /* PPv2.1 MASS 3.20 new feature */ -static int mvPp2V1ClsC4HwCntDump(int rule, int set, unsigned int *cnt) +int mvPp2V1ClsC4HwCntDump(int rule, int set, unsigned int *cnt) { unsigned int regVal; @@ -343,9 +314,7 @@ for (rule = 0; rule < MV_PP2_CLS_C4_GRP_SIZE; rule++) { mvPp2ClsC4HwRead(&C4, rule, set); mvPp2ClsC4SwDump(&C4); -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2V1ClsC4HwCntDump(rule, set, NULL); -#endif mvOsPrintf("--------------------------------------------------------------------\n"); } return MV_OK; @@ -607,7 +576,6 @@ } /*-------------------------------------------------------------------------------*/ -#ifdef CONFIG_MV_ETH_PP2_1 int mvPp2ClsC4PolicerSet(MV_PP2_CLS_C4_ENTRY *C4, int cmd, int policerId, int bank) { PTR_VALIDATE(C4); @@ -628,23 +596,7 @@ return MV_OK; } -#else -int mvPp2ClsC4PolicerSet(MV_PP2_CLS_C4_ENTRY *C4, int cmd, int policerId) -{ - PTR_VALIDATE(C4); - POS_RANGE_VALIDATE(cmd, UPDATE_AND_LOCK); - POS_RANGE_VALIDATE(policerId, ACT_DUP_POLICER_MAX); - C4->sram.regs.actions &= ~ACT_POLICER_SELECT_MASK; - C4->sram.regs.actions |= (cmd << ACT_POLICER_SELECT); - - C4->sram.regs.dup_attr &= ~ACT_DUP_POLICER_MASK; - C4->sram.regs.dup_attr |= (policerId << ACT_DUP_POLICER_ID); - return MV_OK; -} -#endif /*CONFIG_MV_ETH_PP2_1*/ - - /*-------------------------------------------------------------------------------*/ int mvPp2ClsC4QueueHighSet(MV_PP2_CLS_C4_ENTRY *C4, int cmd, int queue) { Index: drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls4Hw.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls4Hw.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2Cls4Hw.h (working copy) @@ -222,6 +222,7 @@ int mvPp2ClsC4RegsDump(void); int mvPp2V1ClsC4HwHitsDump(void); int mvPp2ClsC4HwDumpAll(void); +int mvPp2V1ClsC4HwCntDump(int rule, int set, unsigned int *cnt); /*-------------------------------------------------------------------------------*/ @@ -247,11 +248,7 @@ int mvPp2ClsC4DscpSet(MV_PP2_CLS_C4_ENTRY *C4, int cmd, int dscp); int mvPp2ClsC4GpidSet(MV_PP2_CLS_C4_ENTRY *C4, int cmd, int gpid); int mvPp2ClsC4ForwardSet(MV_PP2_CLS_C4_ENTRY *c4, int cmd); -#ifdef CONFIG_MV_ETH_PP2_1 int mvPp2ClsC4PolicerSet(MV_PP2_CLS_C4_ENTRY *c2, int cmd, int policerId, int bank); -#else -int mvPp2ClsC4PolicerSet(MV_PP2_CLS_C4_ENTRY *c2, int cmd, int policerId); -#endif int mvPp2ClsC4QueueHighSet(MV_PP2_CLS_C4_ENTRY *C4, int cmd, int queue); int mvPp2ClsC4QueueLowSet(MV_PP2_CLS_C4_ENTRY *C4, int cmd, int queue); int mvPp2ClsC4QueueSet(MV_PP2_CLS_C4_ENTRY *C4, int cmd, int queue); Index: drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2ClsActHw.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2ClsActHw.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2ClsActHw.h (working copy) @@ -196,13 +196,8 @@ #define ACT_DUP_COUNT_MASK (((1 << ACT_DUP_COUNT_BITS) - 1) << ACT_DUP_COUNT) #define ACT_DUP_COUNT_MAX 14 -#ifdef CONFIG_MV_ETH_PP2_1 #define ACT_DUP_POLICER_ID 24 #define ACT_DUP_POLICER_ID_BITS 5 -#else -#define ACT_DUP_POLICER_ID 28 -#define ACT_DUP_POLICER_ID_BITS 4 -#endif #define ACT_DUP_POLICER_MASK (((1 << ACT_DUP_POLICER_ID_BITS) - 1) << ACT_DUP_POLICER_ID) #define ACT_DUP_POLICER_MAX ((1 << ACT_DUP_POLICER_ID_BITS) - 1) Index: drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2ClsHw.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2ClsHw.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2ClsHw.c (working copy) @@ -182,12 +182,8 @@ { POS_RANGE_VALIDATE(index, MV_PP2_CLS_GEM_VIRT_REGS_NUM - 1); POS_RANGE_VALIDATE(gem_portid, MV_PP2_CLS_GEM_VIRT_MAX); -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2WrReg(MV_PP2_CLS_GEM_VIRT_INDEX_REG, index); mvPp2WrReg(MV_PP2_CLS_GEM_VIRT_REG, gem_portid); -#else - mvPp2WrReg(MV_PP2_CLS_GEM_VIRT_REG(index), gem_portid); -#endif /* CONFIG_MV_ETH_PP2_1 */ return MV_OK; } /*-------------------------------------------------------------------------------*/ @@ -214,7 +210,6 @@ return MV_OK; } -#ifdef CONFIG_MV_ETH_PP2_1 /*-------------------------------------------------------------------------------*/ /* PPv2.1 (feature MAS 3.7) feature update @@ -229,28 +224,7 @@ return MV_OK; } -#else /*-------------------------------------------------------------------------------*/ -/* -Note: this function set oversize rxq including rxq_high and rxq_low for PPv2 -*/ -int mvPp2ClsHwOversizeRxqSet(int port, int rxq) -{ - - unsigned int regVal; - - POS_RANGE_VALIDATE(rxq, MV_PP2_CLS_OVERSIZE_RXQ_MAX); - - /* set oversize rxq */ - regVal = mvPp2RdReg(MV_PP2_CLS_OVERSIZE_RXQ_REG(port)); - regVal &= ~MV_PP2_CLS_OVERSIZE_RX_MASK; - regVal |= (rxq << MV_PP2_CLS_OVERSIZE_RXQ_OFFS); - mvPp2WrReg(MV_PP2_CLS_OVERSIZE_RXQ_REG(port), regVal); - - return MV_OK; -} -#endif -/*-------------------------------------------------------------------------------*/ /*PPv2.1 feature changed MAS 3.7*/ int mvPp2V0ClsHwMtuSet(int port, int txp, int mtu) { @@ -658,7 +632,6 @@ mvOsPrintf("0x%2.2x 0x%2.2x", int32bit_1, int32bit_2); mvOsPrintf("\n"); -#ifdef CONFIG_MV_ETH_PP2_1 mvOsPrintf("\n"); mvOsPrintf(" PPPEO VLAN MACME UDF7 SELECT SEQ_CTRL\n"); mvOsPrintf(" %1d %1d %1d %1d %1d %1d\n", @@ -670,7 +643,6 @@ (fe->data[1] & FLOW_SEQ_CTRL_MASK) >> FLOW_SEQ_CTRL); mvOsPrintf("\n"); -#endif return MV_OK; } @@ -920,16 +892,10 @@ { POS_RANGE_VALIDATE(index, MV_PP2_CLS_LEN_CHANGE_TBL_SIZE); -#ifdef CONFIG_MV_ETH_PP2_1 /*write index*/ mvPp2WrReg(MV_PP2_V1_CLS_LEN_CHANGE_INDEX_REG, index); mvPp2WrReg(MV_PP2_V1_CLS_LEN_CHANGE_TBL_REG, data); -#else - mvPp2WrReg(MV_PP2_V0_CLS_LEN_CHANGE_INDEX_REG, index); - - mvPp2WrReg(MV_PP2_V0_CLS_LEN_CHANGE_TBL_REG, data); -#endif return MV_OK; } /*-------------------------------------------------------------------------------*/ @@ -939,18 +905,11 @@ PTR_VALIDATE(data); POS_RANGE_VALIDATE(index, MV_PP2_CLS_LEN_CHANGE_TBL_SIZE); -#ifdef CONFIG_MV_ETH_PP2_1 /*write index*/ mvPp2WrReg(MV_PP2_V1_CLS_LEN_CHANGE_INDEX_REG, index); *data = mvPp2RdReg(MV_PP2_V1_CLS_LEN_CHANGE_TBL_REG); -#else - /*write index*/ - mvPp2WrReg(MV_PP2_V0_CLS_LEN_CHANGE_INDEX_REG, index); - *data = mvPp2RdReg(MV_PP2_V0_CLS_LEN_CHANGE_TBL_REG); -#endif - return MV_OK; } /*-------------------------------------------------------------------------------*/ @@ -1036,7 +995,6 @@ mvOsSPrintf(reg_name, "MV_PP2_CLS_SPID_UNI_%d_REG", i); mvPp2PrintReg((MV_PP2_CLS_SPID_UNI_BASE_REG + (4 * i)), reg_name); } -#ifdef CONFIG_MV_ETH_PP2_1 for (i = 0; i < MV_PP2_CLS_GEM_VIRT_REGS_NUM; i++) { /* indirect access */ mvPp2WrReg(MV_PP2_CLS_GEM_VIRT_INDEX_REG, i); @@ -1043,17 +1001,10 @@ mvOsSPrintf(reg_name, "MV_PP2_CLS_GEM_VIRT_%d_REG", i); mvPp2PrintReg(MV_PP2_CLS_GEM_VIRT_REG, reg_name); } -#else - for (i = 0; i < MV_PP2_CLS_GEM_VIRT_REGS_NUM; i++) { - mvOsSPrintf(reg_name, "MV_PP2_CLS_GEM_VIRT_%d_REG", i); - mvPp2PrintReg(MV_PP2_CLS_GEM_VIRT_REG(i), reg_name); - } -#endif for (i = 0; i < MV_PP2_CLS_UDF_BASE_REGS; i++) { mvOsSPrintf(reg_name, "MV_PP2_CLS_UDF_REG_%d_REG", i); mvPp2PrintReg(MV_PP2_CLS_UDF_REG(i), reg_name); } -#ifdef CONFIG_MV_ETH_PP2_1 for (i = 0; i < 16; i++) { mvOsSPrintf(reg_name, "MV_PP2_CLS_MTU_%d_REG", i); mvPp2PrintReg(MV_PP2_CLS_MTU_REG(i), reg_name); @@ -1074,18 +1025,7 @@ mvOsSPrintf(reg_name, "MV_PP2_CLS_PCTRL_%d_REG", i); mvPp2PrintReg(MV_PP2_CLS_PCTRL_REG(i), reg_name); } -#else - for (i = 0; i < (MV_PP2_MAX_TCONT + MV_PP2_MAX_PORTS - 1); i++) { - mvOsSPrintf(reg_name, "MV_PP2_CLS_MTU_%d_REG", i); - mvPp2PrintReg(MV_PP2_CLS_MTU_REG(i), reg_name); - } - for (i = 0; i < MV_PP2_MAX_PORTS; i++) { - mvOsSPrintf(reg_name, "MV_PP2_CLS_OVER_RXQ_%d_REG", i); - mvPp2PrintReg(MV_PP2_CLS_OVERSIZE_RXQ_REG(i), reg_name); - } -#endif - return MV_OK; } /*-------------------------------------------------------------------------------*/ @@ -1162,9 +1102,7 @@ if (mvClsFlowShadowTbl[index] == IN_USE) { mvPp2ClsHwFlowRead(index, &fe); mvPp2ClsSwFlowDump(&fe); -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2V1ClsHwFlowHitGet(index, NULL); -#endif mvOsPrintf("------------------------------------------------------------------\n"); } } @@ -1236,13 +1174,10 @@ int mvPp2ClsHwLkpDump() { int index, way, int32bit, ind; + unsigned int uint32bit; MV_PP2_CLS_LKP_ENTRY fe; -#ifdef CONFIG_MV_ETH_PP2_1 mvOsPrintf("< ID WAY >: RXQ EN FLOW MODE_BASE HITS\n"); -#else - mvOsPrintf("< ID WAY >: RXQ EN FLOW MODE_BASE\n"); -#endif for (index = 0; index < MV_PP2_CLS_LKP_TBL_SIZE ; index++) for (way = 0; way < 2 ; way++) { ind = (way << MV_PP2_CLS_LKP_INDEX_WAY_OFFS) | index; @@ -1257,10 +1192,8 @@ mvOsPrintf("0x%3.3x\t", int32bit); mvPp2ClsSwLkpModGet(&fe, &int32bit); mvOsPrintf(" 0x%2.2x\t", int32bit); -#ifdef CONFIG_MV_ETH_PP2_1 - mvPp2V1ClsHwLkpHitGet(index, way, &int32bit); - mvOsPrintf(" 0x%8.8x\n", int32bit); -#endif + mvPp2V1ClsHwLkpHitGet(index, way, &uint32bit); + mvOsPrintf(" 0x%8.8x\n", uint32bit); mvOsPrintf("\n"); } Index: drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2ClsHw.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2ClsHw.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/cls/mvPp2ClsHw.h (working copy) @@ -141,16 +141,9 @@ #define MV_PP2_CLS_GEM_VIRT_INDEX_BITS (7) #define MV_PP2_CLS_GEM_VIRT_INDEX_MAX (((1 << MV_PP2_CLS_GEM_VIRT_INDEX_BITS) - 1) << 0) /*-------------------------------------------------------------------------------*/ -#ifdef CONFIG_MV_ETH_PP2_1 /* indirect rd/wr via index GEM_VIRT_INDEX */ #define MV_PP2_CLS_GEM_VIRT_REGS_NUM 128 #define MV_PP2_CLS_GEM_VIRT_REG (MV_PP2_REG_BASE + 0x1A04) -#else -/* direct rd/wr */ -#define MV_PP2_CLS_GEM_VIRT_REGS_NUM 64 -#define MV_PP2_CLS_GEM_VIRT_BASE_REG (MV_PP2_REG_BASE + 0x1A00) -#define MV_PP2_CLS_GEM_VIRT_REG(index) (MV_PP2_CLS_GEM_VIRT_BASE_REG + ((index) * 4)) -#endif #define MV_PP2_CLS_GEM_VIRT_BITS 12 #define MV_PP2_CLS_GEM_VIRT_MAX ((1 << MV_PP2_CLS_GEM_VIRT_BITS) - 1) @@ -167,6 +160,13 @@ #define MV_PP2_CLS_UDF_OFFSET_ID_MAX ((1 << MV_PP2_CLS_UDF_OFFSET_ID_BITS) - 1) #define MV_PP2_CLS_UDF_OFFSET_ID_MASK ((MV_PP2_CLS_UDF_OFFSET_ID_MAX) << MV_PP2_CLS_UDF_OFFSET_ID_OFFS) +#define MV_PP2_CLS_UDF_OFFSET_PACKET 0 +#define MV_PP2_CLS_UDF_OFFSET_L3 1 +#define MV_PP2_CLS_UDF_OFFSET_L4 4 +#define MV_PP2_CLS_UDF_OFFSET_OUTVLAN 8 +#define MV_PP2_CLS_UDF_OFFSET_INVLAN 9 +#define MV_PP2_CLS_UDF_OFFSET_ETHTYPE 0xa + #define MV_PP2_CLS_UDF_REL_OFFSET_OFFS 4 #define MV_PP2_CLS_UDF_REL_OFFSET_BITS 11 #define MV_PP2_CLS_UDF_REL_OFFSET_MAX ((1 << MV_PP2_CLS_UDF_REL_OFFSET_BITS) - 1) @@ -420,11 +420,7 @@ int mvPp2ClsHwUdfSet(int udf_no, int offs_id, int offs_bits, int size_bits); int mvPp2V0ClsHwMtuSet(int port, int txp, int mtu);/*PPv2.1 feature changed MAS 3.7*/ int mvPp2V1ClsHwMtuSet(int index, int mtu);/*PPv2.1 feature changed MAS 3.7*/ -#ifdef CONFIG_MV_ETH_PP2_1 int mvPp2ClsHwOversizeRxqLowSet(int port, int rxq);/*PPv2.1 feature changed MAS 3.7*/ -#else -int mvPp2ClsHwOversizeRxqSet(int port, int rxq); -#endif int mvPp2ClsHwRxQueueHighSet(int port, int from, int queue);/*PPv2.1 new feature MAS 3.5*/ int mvPp2ClsHwMhSet(int port, int virtEn, int uniEn, unsigned short mh);/*PPv2.1 new feature MAS 3.18*/ int mvPp2ClsHwSeqInstrSizeSet(int index, int size);/*PPv2.1 new feature MAS 3.14*/ Index: drivers/net/ethernet/mvebu_net/pp2/hal/gbe/mvPp2Gbe.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/gbe/mvPp2Gbe.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/gbe/mvPp2Gbe.c (working copy) @@ -269,14 +269,9 @@ if (mvPp2HalData.iocc) { for (i = 0; i < pPortCtrl->rxqNum; i++) { queue = mvPp2LogicRxqToPhysRxq(port, i); -#ifdef CONFIG_MV_ETH_PP2_1 regVal = mvPp2RdReg(MV_PP2_RXQ_CONFIG_REG(queue)); regVal |= MV_PP2_SNOOP_PKT_SIZE_MASK | MV_PP2_SNOOP_BUF_HDR_MASK; mvPp2WrReg(MV_PP2_RXQ_CONFIG_REG(queue), regVal); -#else - regVal = MV_PP2_V0_SNOOP_PKT_SIZE_MASK | MV_PP2_V0_SNOOP_BUF_HDR_MASK; - mvPp2WrReg(MV_PP2_V0_RXQ_SNOOP_REG(queue), regVal); -#endif } } @@ -1086,7 +1081,6 @@ return MV_OK; } -#ifdef CONFIG_MV_ETH_PP2_1 /******************************************************************************* * mvPp2PortIngressEnable @@ -1220,82 +1214,7 @@ return MV_OK; } -#else -MV_STATUS mvPp2PortIngressEnable(int port, MV_BOOL en) -{ - if (en) - mvPrsMacDropAllSet(port, 0); - else - mvPrsMacDropAllSet(port, 1); - - return MV_OK; -} - -MV_STATUS mvPp2RxqOffsetSet(int port, int rxq, int offset) -{ - MV_U32 regVal; - int prxq = mvPp2LogicRxqToPhysRxq(port, rxq); - - if (offset % 32 != 0) { - mvOsPrintf("%s: offset must be in units of 32\n", __func__); - return MV_BAD_PARAM; - } - - /* convert offset from bytes to units of 32 bytes */ - offset = offset >> 5; - - regVal = mvPp2RdReg(MV_PP2_V0_RXQ_CONFIG_REG(prxq)); - - regVal &= ~MV_PP2_V0_RXQ_PACKET_OFFSET_MASK; - regVal |= ((offset << MV_PP2_V0_RXQ_PACKET_OFFSET_OFFS) & MV_PP2_V0_RXQ_PACKET_OFFSET_MASK); - - mvPp2WrReg(MV_PP2_V0_RXQ_CONFIG_REG(prxq), regVal); - - return MV_OK; -} - -MV_STATUS mvPp2RxqBmLongPoolSet(int port, int rxq, int longPool) -{ - MV_U32 regVal = 0; - int prxq = mvPp2LogicRxqToPhysRxq(port, rxq); - - regVal = mvPp2RdReg(MV_PP2_V0_RXQ_CONFIG_REG(prxq)); - regVal &= ~MV_PP2_V0_RXQ_POOL_LONG_MASK; - regVal |= ((longPool << MV_PP2_V0_RXQ_POOL_LONG_OFFS) & MV_PP2_V0_RXQ_POOL_LONG_MASK); - - mvPp2WrReg(MV_PP2_V0_RXQ_CONFIG_REG(prxq), regVal); - - return MV_OK; -} - -MV_STATUS mvPp2RxqBmShortPoolSet(int port, int rxq, int shortPool) -{ - MV_U32 regVal = 0; - int prxq = mvPp2LogicRxqToPhysRxq(port, rxq); - - regVal = mvPp2RdReg(MV_PP2_V0_RXQ_CONFIG_REG(prxq)); - regVal &= ~MV_PP2_V0_RXQ_POOL_SHORT_MASK; - regVal |= ((shortPool << MV_PP2_V0_RXQ_POOL_SHORT_OFFS) & MV_PP2_V0_RXQ_POOL_SHORT_MASK); - - mvPp2WrReg(MV_PP2_V0_RXQ_CONFIG_REG(prxq), regVal); - - return MV_OK; -} - -MV_STATUS mvPp2PortHwfBmPoolSet(int port, int shortPool, int longPool) -{ - MV_U32 regVal = 0; - - regVal |= ((shortPool << MV_PP2_V0_PORT_HWF_POOL_SHORT_OFFS) & MV_PP2_V0_PORT_HWF_POOL_SHORT_MASK); - regVal |= ((longPool << MV_PP2_V0_PORT_HWF_POOL_LONG_OFFS) & MV_PP2_V0_PORT_HWF_POOL_LONG_MASK); - - mvPp2WrReg(MV_PP2_V0_PORT_HWF_CONFIG_REG(MV_PPV2_PORT_PHYS(port)), regVal); - - return MV_OK; -} -#endif /* CONFIG_MV_ETH_PP2_1 */ - /*-------------------------------------------------------------------------------*/ MV_STATUS mvPp2MhSet(int port, MV_TAG_TYPE mh) @@ -1307,6 +1226,7 @@ regVal &= ~(MV_PP2_DSA_EN_MASK | MV_PP2_MH_EN_MASK); switch (mh) { case MV_TAG_TYPE_NONE: + case MV_TAG_TYPE_VLAN: break; case MV_TAG_TYPE_MH: @@ -1314,11 +1234,12 @@ break; case MV_TAG_TYPE_DSA: - regVal |= MV_PP2_DSA_EN_MASK; + regVal |= MV_PP2_DSA_NON_EXTENDED; break; case MV_TAG_TYPE_EDSA: regVal |= MV_PP2_DSA_EXTENDED; + break; default: mvOsPrintf("port=%d: Unexpected MH = %d value\n", port, mh); @@ -1326,12 +1247,10 @@ } mvPp2WrReg(MV_PP2_MH_REG(MV_PPV2_PORT_PHYS(port)), regVal); -#ifdef CONFIG_MV_ETH_PP2_1 if (mh == MV_TAG_TYPE_MH) mvGmacPortMhSet(port, 1); else mvGmacPortMhSet(port, 0); -#endif /* CONFIG_MV_ETH_PP2_1 */ return MV_OK; } @@ -1850,7 +1769,6 @@ return MV_OK; } -#ifdef CONFIG_MV_ETH_PP2_1 /* Functions implemented only for PPv2.1 version (A0 and later) */ MV_STATUS mvPp2RxqEnable(int port, int rxq, MV_BOOL en) { @@ -1934,19 +1852,7 @@ return MV_OK; } -#else /* Stabs for Z1 */ -MV_STATUS mvPp2TxqDrainSet(int port, int txp, int txq, MV_BOOL en) -{ - return MV_OK; -} - -MV_STATUS mvPp2TxPortFifoFlush(int port, MV_BOOL en) -{ - return MV_OK; -} -#endif /* CONFIG_MV_ETH_PP2_1 */ - /* Function for swithcing SWF to HWF */ /* txq is physical (global) txq in range 0..MV_PP2_TXQ_TOTAL_NUM */ /* txq is physical (global) rxq in range 0..MV_PP2_RXQ_TOTAL_NUM */ Index: drivers/net/ethernet/mvebu_net/pp2/hal/gbe/mvPp2GbeDebug.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/gbe/mvPp2GbeDebug.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/gbe/mvPp2GbeDebug.c (working copy) @@ -81,13 +81,8 @@ mvOsPrintf("\nRXQs [0..%d] registers\n", MV_PP2_RXQ_TOTAL_NUM); for (i = 0; i < MV_PP2_RXQ_TOTAL_NUM; i++) { -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2PrintReg(MV_PP2_RX_STATUS, "MV_PP2_RX_STATUS"); mvPp2PrintReg2(MV_PP2_RXQ_CONFIG_REG(i), "MV_PP2_RXQ_CONFIG_REG", i); -#else - mvPp2PrintReg2(MV_PP2_V0_RXQ_SNOOP_REG(i), "MV_PP2_RXQ_SNOOP_REG", i); - mvPp2PrintReg2(MV_PP2_V0_RXQ_CONFIG_REG(i), "MV_PP2_RXQ_CONFIG_REG", i); -#endif } mvOsPrintf("\nBM pools [0..%d] registers\n", MV_BM_POOLS); for (i = 0; i < MV_BM_POOLS; i++) @@ -95,9 +90,6 @@ mvOsPrintf("\nIngress ports [0..%d] registers\n", MV_PP2_MAX_PORTS); for (i = 0; i < MV_PP2_MAX_PORTS; i++) { -#ifndef CONFIG_MV_ETH_PP2_1 - mvPp2PrintReg2(MV_PP2_V0_PORT_HWF_CONFIG_REG(i), "MV_PP2_PORT_HWF_CONFIG_REG", i); -#endif mvPp2PrintReg2(MV_PP2_RX_CTRL_REG(i), "MV_PP2_RX_CTRL_REG", i); } mvOsPrintf("\n"); @@ -283,15 +275,7 @@ mvPp2PrintReg(MV_PP2_RXQ_STATUS_REG(rxq), "MV_PP2_RXQ_STATUS_REG"); mvPp2PrintReg(MV_PP2_RXQ_THRESH_REG, "MV_PP2_RXQ_THRESH_REG"); mvPp2PrintReg(MV_PP2_RXQ_INDEX_REG, "MV_PP2_RXQ_INDEX_REG"); -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2PrintReg(MV_PP2_RXQ_CONFIG_REG(rxq), "MV_PP2_RXQ_CONFIG_REG"); -#else - mvPp2PrintReg(MV_PP2_V0_RXQ_CONFIG_REG(rxq), "MV_PP2_RXQ_CONFIG_REG"); - mvPp2PrintReg(MV_PP2_V0_RXQ_SNOOP_REG(rxq), "MV_PP2_RXQ_SNOOP_REG"); - mvPp2PrintReg(MV_PP2_V0_RX_EARLY_DROP_REG(rxq), "MV_PP2_V0_RX_EARLY_DROP_REG"); - mvPp2PrintReg(MV_PP2_V0_RX_DESC_DROP_REG(rxq), "MV_PP2_V0_RX_DESC_DROP_REG"); -#endif - } void mvPp2PortRxqRegs(int port, int rxq) Index: drivers/net/ethernet/mvebu_net/pp2/hal/gbe/mvPp2GbeRegs.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/gbe/mvPp2GbeRegs.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/gbe/mvPp2GbeRegs.h (working copy) @@ -105,7 +105,7 @@ #define MV_PP2_MH_EN_OFFS 0 #define MV_PP2_MH_EN_MASK (1 << MV_PP2_MH_EN_OFFS) -#define MV_PP2_DSA_EN_OFFS 0 +#define MV_PP2_DSA_EN_OFFS 4 #define MV_PP2_DSA_EN_MASK (0x3 << MV_PP2_DSA_EN_OFFS) #define MV_PP2_DSA_DISABLE 0 #define MV_PP2_DSA_NON_EXTENDED (0x1 << MV_PP2_DSA_EN_OFFS) @@ -120,7 +120,6 @@ #define MV_PP2_POOL_BUF_SIZE_MASK (0xFFFE) /*-------------------------------------------------------------------------------*/ -#ifdef CONFIG_MV_ETH_PP2_1 /* PPv2.1 - A0 */ #define MV_PP2_RX_STATUS (MV_PP2_REG_BASE + 0x174) @@ -167,43 +166,6 @@ #define MV_PP2_HWF_TXQ_DISABLE_MASK (0x1 << MV_PP2_HWF_TXQ_DISABLE_BIT) /*-------------------------------------------------------------------------------*/ -#else /* PPv2 - Z1 */ - -#define MV_PP2_V0_RXQ_SNOOP_REG(rxq) (MV_PP2_REG_BASE + 0x800 + 4 * (rxq)) - -#define MV_PP2_V0_SNOOP_PKT_SIZE_OFFS 5 -#define MV_PP2_V0_SNOOP_PKT_SIZE_MASK (0x1FF << MV_PP2_V0_SNOOP_PKT_SIZE_OFFS) - -#define MV_PP2_V0_SNOOP_BUF_HDR_OFFS 14 -#define MV_PP2_V0_SNOOP_BUF_HDR_MASK (0x1 << MV_PP2_V0_SNOOP_BUF_HDR_OFFS) - -#define MV_PP2_V0_L2_DEPOSIT_PKT_SIZE_OFFS 21 -#define MV_PP2_V0_L2_DEPOSIT_PKT_SIZE_MASK (0xF << MV_PP2_V0_L2_DEPOSIT_PKT_SIZE_OFFS) - -#define MV_PP2_V0_L2_DEPOSIT_BUF_HDR_OFFS 25 -#define MV_PP2_V0_L2_DEPOSIT_BUF_HDR_MASK (0x1 << MV_PP2_V0_L2_DEPOSIT_BUF_HDR_OFFS) -/*-------------------------------------------------------------------------------*/ - -#define MV_PP2_V0_RXQ_CONFIG_REG(rxq) (MV_PP2_REG_BASE + 0xc00 + 4 * (rxq)) - -#define MV_PP2_V0_RXQ_POOL_SHORT_OFFS 0 -#define MV_PP2_V0_RXQ_POOL_SHORT_MASK (0x7 << MV_PP2_V0_RXQ_POOL_SHORT_OFFS) -#define MV_PP2_V0_RXQ_POOL_LONG_OFFS 8 -#define MV_PP2_V0_RXQ_POOL_LONG_MASK (0x7 << MV_PP2_V0_RXQ_POOL_LONG_OFFS) -#define MV_PP2_V0_RXQ_PACKET_OFFSET_OFFS 17 -#define MV_PP2_V0_RXQ_PACKET_OFFSET_MASK (0xFF << MV_PP2_V0_RXQ_PACKET_OFFSET_OFFS) -/*-------------------------------------------------------------------------------*/ - -#define MV_PP2_V0_PORT_HWF_CONFIG_REG(port) (MV_PP2_REG_BASE + 0x120 + 4 * (port)) - -#define MV_PP2_V0_PORT_HWF_POOL_SHORT_OFFS 0 -#define MV_PP2_V0_PORT_HWF_POOL_SHORT_MASK (0x7 << MV_PP2_V0_PORT_HWF_POOL_SHORT_OFFS) -#define MV_PP2_V0_PORT_HWF_POOL_LONG_OFFS 8 -#define MV_PP2_V0_PORT_HWF_POOL_LONG_MASK (0x7 << MV_PP2_V0_PORT_HWF_POOL_LONG_OFFS) -/*-------------------------------------------------------------------------------*/ - -#endif /* PPv2 - Z1 / PPv2.1 - A0 */ - #define MV_PP2_RX_GEMPID_SRC_OFFS 8 #define MV_PP2_RX_GEMPID_SRC_MASK (0x7 << MV_PP2_RX_GEMPID_SRC_OFFS) Index: drivers/net/ethernet/mvebu_net/pp2/hal/gmac/mvEthGmacApi.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/gmac/mvEthGmacApi.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/gmac/mvEthGmacApi.c (working copy) @@ -692,10 +692,8 @@ mvGmacPrintReg(ETH_PORT_ISR_CAUSE_REG(port), "MV_GMAC_ISR_CAUSE_REG"); mvGmacPrintReg(ETH_PORT_ISR_MASK_REG(port), "MV_GMAC_ISR_MASK_REG"); -#ifdef CONFIG_MV_ETH_PP2_1 mvGmacPrintReg(ETH_PORT_ISR_SUM_CAUSE_REG(port), "MV_GMAC_ISR_SUM_CAUSE_REG"); mvGmacPrintReg(ETH_PORT_ISR_SUM_MASK_REG(port), "MV_GMAC_ISR_SUM_MASK_REG"); -#endif } Index: drivers/net/ethernet/mvebu_net/pp2/hal/gmac/mvEthGmacApi.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/gmac/mvEthGmacApi.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/gmac/mvEthGmacApi.h (working copy) @@ -139,16 +139,12 @@ static INLINE MV_VOID mvGmacPortSumIsrMask(int port) { -#ifdef CONFIG_MV_ETH_PP2_1 MV_REG_WRITE(ETH_PORT_ISR_SUM_MASK_REG(port), 0); -#endif } static INLINE MV_VOID mvGmacPortSumIsrUnmask(int port) { -#ifdef CONFIG_MV_ETH_PP2_1 MV_REG_WRITE(ETH_PORT_ISR_SUM_MASK_REG(port), ETH_PORT_ISR_SUM_INTERN_MASK); -#endif } Index: drivers/net/ethernet/mvebu_net/pp2/hal/plcr/mvPp2PlcrHw.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/plcr/mvPp2PlcrHw.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/plcr/mvPp2PlcrHw.c (working copy) @@ -79,11 +79,7 @@ mvOsPrintf("\n[PLCR registers: %d policers]\n", MV_PP2_PLCR_NUM); -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2PrintReg(MV_PP2_PLCR_MODE_REG, "MV_PP2_PLCR_MODE_REG"); -#else - mvPp2PrintReg(MV_PP2_PLCR_ENABLE_REG, "MV_PP2_PLCR_ENABLE_REG"); -#endif mvPp2PrintReg(MV_PP2_PLCR_BASE_PERIOD_REG, "MV_PP2_PLCR_BASE_PERIOD_REG"); mvPp2PrintReg(MV_PP2_PLCR_MIN_PKT_LEN_REG, "MV_PP2_PLCR_MIN_PKT_LEN_REG"); mvPp2PrintReg(MV_PP2_PLCR_EDROP_EN_REG, "MV_PP2_PLCR_EDROP_EN_REG"); @@ -99,18 +95,10 @@ } mvOsPrintf("\nEarly Drop Thresholds for SW and HW forwarding\n"); -#ifdef CONFIG_MV_ETH_PP2_1 for (i = 0; i < MV_PP2_V1_PLCR_EDROP_THRESH_NUM; i++) { mvPp2PrintReg2(MV_PP2_V1_PLCR_EDROP_CPU_TR_REG(i), "MV_PP2_V1_PLCR_EDROP_CPU_TR_REG", i); mvPp2PrintReg2(MV_PP2_V1_PLCR_EDROP_HWF_TR_REG(i), "MV_PP2_V1_PLCR_EDROP_HWF_TR_REG", i); } -#else - - for (i = 0; i < MV_PP2_V0_PLCR_EDROP_THRESH_NUM; i++) { - mvPp2PrintReg2(MV_PP2_V0_PLCR_EDROP_CPU_TR_REG(i), "MV_PP2_V0_PLCR_EDROP_CPU_TR_REG", i); - mvPp2PrintReg2(MV_PP2_V0_PLCR_EDROP_HWF_TR_REG(i), "MV_PP2_V0_PLCR_EDROP_HWF_TR_REG", i); - } -#endif mvOsPrintf("\nPer RXQ: Non zero early drop thresholds\n"); for (i = 0; i < MV_PP2_RXQ_TOTAL_NUM; i++) { mvPp2WrReg(MV_PP2_PLCR_EDROP_RXQ_REG, i); @@ -153,10 +141,6 @@ mvPp2WrReg(MV_PP2_PLCR_TABLE_INDEX_REG, plcr); mvOsPrintf("%3d: ", plcr); -#ifndef CONFIG_MV_ETH_PP2_1 - enable = mvPp2RdReg(MV_PP2_PLCR_ENABLE_REG); - mvOsPrintf("%4s", MV_BIT_CHECK(enable, plcr) ? "Yes" : "No"); -#endif regVal = mvPp2RdReg(MV_PP2_PLCR_TOKEN_CFG_REG); units = regVal & MV_PP2_PLCR_TOKEN_UNIT_MASK; @@ -163,10 +147,8 @@ color = regVal & MV_PP2_PLCR_COLOR_MODE_MASK; type = (regVal & MV_PP2_PLCR_TOKEN_TYPE_ALL_MASK) >> MV_PP2_PLCR_TOKEN_TYPE_OFFS; tokens = (regVal & MV_PP2_PLCR_TOKEN_VALUE_ALL_MASK) >> MV_PP2_PLCR_TOKEN_VALUE_OFFS; -#ifdef CONFIG_MV_ETH_PP2_1 enable = regVal & MV_PP2_PLCR_ENABLE_MASK; mvOsPrintf("%4s", enable ? "Yes" : "No"); -#endif mvOsPrintf(" %-5s %2d %5d", units ? "pkts" : "bytes", type, tokens); mvOsPrintf(" %-5s", color ? "aware" : "blind"); Index: drivers/net/ethernet/mvebu_net/pp2/hal/plcr/mvPp2PlcrHw.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/hal/plcr/mvPp2PlcrHw.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/hal/plcr/mvPp2PlcrHw.h (working copy) @@ -70,11 +70,7 @@ #endif /* __cplusplus */ -#ifdef CONFIG_MV_ETH_PP2_1 #define MV_PP2_PLCR_NUM 48 -#else -#define MV_PP2_PLCR_NUM 16 -#endif /*********************************** RX Policer Registers *******************/ /* exist only in ppv2.0 */ Index: drivers/net/ethernet/mvebu_net/pp2/Kconfig =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/Kconfig (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/Kconfig (working copy) @@ -1,15 +1,20 @@ -config MV_ETH_PP2_1 - bool "Support PPv2.1 version (A0)" - default n +config MV_ETH_PP2 + tristate "Marvell PP2 network interface driver" + depends on MACH_ARMADA_375 || ARCH_AVANTA_LP + default y + select MV_ETH_PP2_CLS2 + select MV_ETH_PP2_CLS3 + select MV_ETH_PP2_CLS4 + select MV_ETH_PP2_CLS_MC ---help--- - PPv2.1 support (Avanta-LP A0) - PPv2.1 enable various features such as: - * Qsets support - * TX descriptors chunks mechanism - * See PPv2.1 MAS for more info + This driver supports the network interface + units in the following Marvell Soc families: + 1. ARMADA-375. + 2. AVANTA-LP. config MV_PP2_HWF bool "Enable PPv2 Harware Forwarding" + depends on MV_ETH_PP2 default y ---help--- Enable Hardware: @@ -17,25 +22,30 @@ config MV_ETH_PP2_CLS2 bool + depends on MV_ETH_PP2 default y ---help--- config MV_ETH_PP2_CLS3 bool + depends on MV_ETH_PP2 default y ---help--- config MV_ETH_PP2_CLS4 bool + depends on MV_ETH_PP2 default y ---help--- config MV_ETH_PP2_CLS_MC bool + depends on MV_ETH_PP2 default y ---help--- menu "PP2 BM configuration" + depends on MV_ETH_PP2 choice prompt "PP2 BM pool assignment mode" @@ -76,6 +86,7 @@ endmenu menu "PP2 Rx/Tx Queue configuration" + depends on MV_ETH_PP2 config MV_PP2_RXQ int "Number of RX queues per port" @@ -91,7 +102,6 @@ config MV_PP2_RXQ_DESC int "Number of Rx descriptors" - depends on MV_ETH_PP2 default 256 ---help--- The number of Rx descriptors in each Rx queue. @@ -103,7 +113,6 @@ config MV_PP2_TXQ_DESC int "Number of Tx descriptors" - depends on MV_ETH_PP2 default 1024 ---help--- The number of Tx descriptors in each Tx queue. @@ -115,7 +124,6 @@ config MV_PP2_TXQ_CPU_CHUNK int "Number of TX descriptors allocated per CPU" - depends on MV_ETH_PP2_1 default 64 ---help--- The number of TXQ descriptors each CPU will allocate each time when @@ -123,16 +131,8 @@ The total number of TXQ descriptors (MV_PP2_TXQ_DESC) must be at least 3 * nr_cpu_ids * MV_PP2_TXQ_CPU_CHUNK -config MV_PP2_TXQ_HWF_DESC - int "Number of HWF Tx descriptors" - depends on (MV_ETH_PP2 && !MV_ETH_PP2_1) - default 16 - ---help--- - The number of HWF dedicated Tx descriptors in each Tx queue. - config MV_PP2_AGGR_TXQ_SIZE int "Number of aggregated Tx descriptors" - depends on MV_ETH_PP2 default 256 ---help--- The number of Tx descriptors in each aggregated Tx queue. @@ -139,13 +139,13 @@ config MV_PP2_TEMP_TXQ_SIZE int "Number of temporary Txq descriptors (for switching between HWF and SWF)" - depends on (MV_ETH_PP2 && MV_PP2_HWF) + depends on MV_PP2_HWF default 512 ---help--- config MV_PP2_TEMP_TXQ_HWF_SIZE int "Number of temporary Txq HWF descriptors (for switching between HWF and SWF)" - depends on (MV_ETH_PP2 && MV_PP2_HWF) + depends on MV_PP2_HWF default 256 ---help--- @@ -152,6 +152,7 @@ endmenu menu "PP2 IP/TCP/UDP Offloading" + depends on MV_ETH_PP2 config MV_PP2_TSO bool "TSO Support for Marvell network interface" @@ -163,6 +164,7 @@ endmenu menu "PP2 Control and Statistics" + depends on MV_ETH_PP2 config MV_PP2_DEBUG_CODE bool "Add run-time debug code" @@ -204,7 +206,8 @@ endmenu -menu "Advanced Features" +menu "PP2 Advanced Features" + depends on MV_ETH_PP2 config MV_PP2_SKB_RECYCLE depends on NET_SKB_RECYCLE @@ -278,7 +281,6 @@ ---help--- Periodical timer period for Tx Done operation in [msec]. - config MV_PP2_TXDONE_COAL_PKTS int "Threshold for TX_DONE event trigger" default 16 @@ -395,11 +397,12 @@ Number of extra buffers allocated for each port endmenu -menu "PON support for Network driver" +menu "PP2 PON support" + depends on MV_ETH_PP2 config MV_PP2_PON bool "PP2 PON support" - depends on MV_ETH_PP2 && MV_INCLUDE_PON + depends on MV_INCLUDE_PON ---help--- Choose this option to support PON port in Marvell network driver. @@ -420,18 +423,26 @@ endmenu menu "PP2 ERRATA / WA" + depends on MV_ETH_PP2 config MV_PP2_SWF_HWF_CORRUPTION_WA bool "Prevent data corruption in IOCC mode" - depends on (AURORA_IO_CACHE_COHERENCY && MV_PP2_HWF) + depends on MV_PP2_HWF default y ---help--- Enable this feature to avoid data corruption in IOCC mode when HWF and SWF traffic use buffers from the same BM pools. + In addition workaround should be activated using sysfs command: + "echo en > pp2/gbe/c_inv - on/off L1 and L2 cache invalidation" + After activation each buffer that refilled to BM pool in mixed mode + (used for HWF and SWF) will be invalidated by the driver. + Warning: this WA can't be activated when there are any kind + of PCIe activities. endmenu -menu "SoC CPH support" +menu "PP2 CPH support" + depends on MV_ETH_PP2 config MV_CPH tristate "Support for Marvell CPU Packet Handler Driver" Index: drivers/net/ethernet/mvebu_net/pp2/l2fw/mv_eth_l2fw.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/l2fw/mv_eth_l2fw.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/l2fw/mv_eth_l2fw.c (working copy) @@ -654,16 +654,11 @@ txq_ctrl = &pp->txq_ctrl[tx_spec_ptr->txp * CONFIG_MV_PP2_TXQ + tx_spec_ptr->txq]; txq_cpu_ptr = &(txq_ctrl->txq_cpu[cpu]); -#ifdef CONFIG_MV_ETH_PP2_1 if (mv_pp2_reserved_desc_num_proc(pp, tx_spec_ptr->txp, tx_spec_ptr->txq, frags) || mv_pp2_aggr_desc_num_check(aggr_txq_ctrl, frags)) { frags = 0; goto out; } -#else - if (mv_pp2_aggr_desc_num_check(aggr_txq_ctrl, frags)) - goto out; -#endif /*CONFIG_MV_ETH_PP2_1*/ /* Get next descriptor for tx, single buffer, so FIRST & LAST */ tx_desc = mvPp2AggrTxqNextDescGet(aggr_txq_ctrl->q); @@ -690,11 +685,9 @@ tx_desc->command = tx_cmd; -#ifdef CONFIG_MV_ETH_PP2_1 qset = (rx_desc->bmQset & PP2_RX_BUFF_QSET_NUM_MASK) >> PP2_RX_BUFF_QSET_NUM_OFFS; grntd = (rx_desc->bmQset & PP2_RX_BUFF_TYPE_MASK) >> PP2_RX_BUFF_TYPE_OFFS; tx_desc->hwCmd[1] = (qset << PP2_TX_MOD_QSET_OFFS) | (grntd << PP2_TX_MOD_GRNTD_BIT); -#endif tx_desc->physTxq = MV_PPV2_TXQ_PHYS(pp->port, tx_spec_ptr->txp, tx_spec_ptr->txq); @@ -726,9 +719,7 @@ /* TODO - XOR ready check */ -#ifdef CONFIG_MV_ETH_PP2_1 txq_cpu_ptr->reserved_num--; -#endif txq_cpu_ptr->txq_count++; aggr_txq_ctrl->txq_count++; Index: drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_bm_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_bm_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_bm_sysfs.c (working copy) @@ -40,7 +40,6 @@ { int off = 0; -#ifdef CONFIG_MV_ETH_PP2_1 off += sprintf(buf+off, "cat queueMappDump - print BM all rxq/txq to qSet mapp\n"); off += sprintf(buf+off, "cat qsetConfigDump - print BM all qSets configuration\n"); off += sprintf(buf+off, "echo [qset] [pool] > qsetCreate - create qset and attach it to BM [pool]\n"); @@ -53,13 +52,12 @@ off += sprintf(buf+off, "echo [qset] > qsetShow - show info for Qset [qset]\n"); off += sprintf(buf+off, "echo [pool] > poolDropCnt - print BM pool drop counters\n"); -#endif off += sprintf(buf+off, "echo [pool] > poolRegs - print BM pool registers\n"); off += sprintf(buf+off, "echo [pool] > poolStatus - print BM pool status\n"); off += sprintf(buf+off, "echo [pool] [size] > poolSize - set packet size to BM pool\n"); off += sprintf(buf+off, "echo [port] [pool] [buf_num] > poolBufNum - set buffers num for BM pool\n"); - off += sprintf(buf+off, " [port] - any port use this pool"); + off += sprintf(buf+off, " [port] - any port use this pool\n"); off += sprintf(buf+off, "echo [port] [pool] > longPool - set port's long BM pool\n"); off += sprintf(buf+off, "echo [port] [pool] > shortPool - set port's short BM pool\n"); off += sprintf(buf+off, "echo [port] [pool] > hwfLongPool - set port's HWF long BM pool\n"); @@ -111,6 +109,7 @@ mvBmV1PoolDropCntDump(a); } else if (!strcmp(name, "poolStatus")) { mv_pp2_pool_status_print(a); + mv_pp2_pool_stats_print(a); } else if (!strcmp(name, "poolSize")) { err = mv_pp2_ctrl_pool_size_set(a, b); } else if (!strcmp(name, "poolBufNum")) { Index: drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_rx_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_rx_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_rx_sysfs.c (working copy) @@ -45,9 +45,7 @@ o += sprintf(b+o, "echo [p] > rxFifoRegs - show RX FIFO registers for port

\n"); o += sprintf(b+o, "echo [p] v > rxWeight - set weight for poll function, - weight [0..255]\n"); o += sprintf(b+o, "echo [rxq] > gRxqRegs - show RXQ registers for global \n"); -#ifdef CONFIG_MV_ETH_PP2_1 o += sprintf(b+o, "echo [p] [rxq] > rxqCounters - show RXQ counters for

.\n"); -#endif o += sprintf(b+o, "echo [p] [rxq] > pRxqRegs - show RXQ registers for global \n"); o += sprintf(b+o, "echo [p] [rxq] [0|1] > rxqShow - show RXQ descriptors ring for

\n"); o += sprintf(b+o, "echo [p] [rxq] [v] > rxqSize - set number of descriptors for .\n"); Index: drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_sysfs.c (working copy) @@ -127,11 +127,7 @@ mvGmacLmsRegs(); mvGmacPortRegs(p); } else if (!strcmp(name, "dropCntrs")) { -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2V1DropCntrs(p); -#else - mvPp2V0DropCntrs(p); -#endif } else if (!strcmp(name, "stats")) { mv_pp2_port_stats_print(p); } else if (!strcmp(name, "mac")) { Index: drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_tool.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_tool.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_tool.c (working copy) @@ -777,11 +777,7 @@ for (rxq = 0; rxq < priv->rxq_num; rxq++) mv_pp2_ctrl_rxq_size_set(priv->port, rxq, rxq_size); -#ifdef CONFIG_MV_ETH_PP2_1 hwf_size = txq_size - (nr_cpu_ids * priv->txq_ctrl[0].rsvd_chunk); -#else - hwf_size = txq_size/2; -#endif /* relevant only for ppv2.1 */ swf_size = hwf_size - (nr_cpu_ids * priv->txq_ctrl[0].rsvd_chunk); Index: drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_tx_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_tx_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_eth_tx_sysfs.c (working copy) @@ -43,9 +43,7 @@ int s = PAGE_SIZE; /* buffer size */ o += scnprintf(b+o, s-o, "cat txRegs - show global TX registers\n"); -#ifdef CONFIG_MV_ETH_PP2_1 o += scnprintf(b+o, s-o, "echo [p] [txp] [txq] > pTxqCounters - show TXQ Counters for port

where range [0..7]\n"); -#endif o += scnprintf(b+o, s-o, "echo [p] [txp] [txq] > pTxqRegs - show TXQ registers for port

where range [0..7]\n"); o += scnprintf(b+o, s-o, "echo [txq] > gTxqRegs - show TXQ registers for global range [0..255]\n"); o += scnprintf(b+o, s-o, "echo [cpu] > aggrTxqRegs - show Aggregation TXQ registers for range [0..max]\n"); @@ -55,13 +53,8 @@ o += scnprintf(b+o, s-o, "echo [p] [hex] > txMH - set 2 bytes of Marvell Header for transmit\n"); o += scnprintf(b+o, s-o, "echo [p] [txp] [txq] [cpu] > txqDef - set default for packets sent to port

by \n"); o += scnprintf(b+o, s-o, "echo [p] [txp] [txq] [v] > txqSize - set TXQ size for

.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 o += scnprintf(b+o, s-o, "echo [p] [txp] [txq] [hwf] [swf] > txqLimit - set HWF and SWF limits for

.\n"); o += scnprintf(b+o, s-o, "echo [p] [txp] [txq] [v] > txqChunk - set SWF request chunk [v] for

\n"); - -#else - o += scnprintf(b+o, s-o, "echo [p] [txp] [txq] [hwf] > txqLimit - set HWF limit for

.\n"); -#endif #ifdef CONFIG_MV_PP2_TXDONE_IN_HRTIMER o += scnprintf(b+o, s-o, "echo [period] > txPeriod - set Tx Done high resolution timer period\n"); o += scnprintf(b+o, s-o, " [period]: period range is [%lu, %lu], unit usec\n", Index: drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_ethernet.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_ethernet.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_ethernet.c (working copy) @@ -93,7 +93,7 @@ } /* unmask interrupts */ - on_each_cpu(mv_pp2_interrupts_unmask, (void *)priv, 1); + on_each_cpu(((smp_call_func_t)mv_pp2_interrupts_unmask), (void *)priv, 1); /* Enable interrupts for all CPUs */ mvPp2GbeCpuInterruptsEnable(priv->port, priv->cpuMask); @@ -101,7 +101,7 @@ /* Unmask Port link interrupt */ mvGmacPortIsrUnmask(priv->port); - printk(KERN_NOTICE "%s: started\n", dev->name); + pr_info("\n%s: started\n", dev->name); } /* Enable GMAC */ @@ -136,7 +136,7 @@ /* Disable interrupts for all CPUs */ mvPp2GbeCpuInterruptsDisable(priv->port, priv->cpuMask); - on_each_cpu(mv_pp2_interrupts_mask, priv, 1); + on_each_cpu(((smp_call_func_t)mv_pp2_interrupts_mask), priv, 1); /* make sure that the port finished its Rx polling */ for (group = 0; group < MV_PP2_MAX_RXQ; group++) Index: drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_netdev.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_netdev.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_netdev.c (working copy) @@ -90,7 +90,9 @@ void mv_pp2_iocc_l1_l2_cache_inv(unsigned char *v_start, int size) { if (mv_pp2_swf_hwf_wa_en) -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 4, 99) + dma_map_single(NULL, v_start, size, DMA_FROM_DEVICE); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) ___dma_single_dev_to_cpu(v_start, size, DMA_FROM_DEVICE); #else dma_cache_maint(v_start, size, DMA_FROM_DEVICE); @@ -172,7 +174,7 @@ static void mv_pp2_priv_cleanup(struct eth_port *pp); static int mv_pp2_config_get(struct platform_device *pdev, u8 *mac); static int mv_pp2_hal_init(struct eth_port *pp); -struct net_device *mv_pp2_netdev_init(int mtu, u8 *mac, struct platform_device *pdev); +struct net_device *mv_pp2_netdev_init(struct platform_device *pdev); static int mv_pp2_netdev_connect(struct eth_port *pp); static void mv_pp2_netdev_init_features(struct net_device *dev); static struct sk_buff *mv_pp2_skb_alloc(struct eth_port *pp, struct bm_pool *pool, @@ -263,7 +265,6 @@ { struct eth_port *pp = mv_pp2_port_by_id(port); - if ((type == MV_TAG_TYPE_MH) || (type == MV_TAG_TYPE_DSA) || (type == MV_TAG_TYPE_EDSA)) mvPp2MhSet(port, type); pp->tagged = (type == MV_TAG_TYPE_NONE) ? MV_FALSE : MV_TRUE; @@ -434,6 +435,37 @@ return 0; } +const char *mv_pp2_pool_type_str(enum mv_pp2_bm_type type) +{ + const char *type_str; + + switch (type) { + case MV_ETH_BM_FREE: + type_str = "FREE "; + break; + case MV_ETH_BM_SWF_LONG: + type_str = "SWF Long "; + break; + case MV_ETH_BM_SWF_SHORT: + type_str = "SWF Short"; + break; + case MV_ETH_BM_HWF_LONG: + type_str = "HWF Long "; + break; + case MV_ETH_BM_HWF_SHORT: + type_str = "HWF Short"; + break; + case MV_ETH_BM_MIXED_LONG: + type_str = "MIX Long "; + break; + case MV_ETH_BM_MIXED_SHORT: + type_str = "MIX Short"; + break; + default: + type_str = "Unknown "; + } + return type_str; +} /**********************************************************/ struct eth_port *mv_pp2_port_by_id(unsigned int port) @@ -624,7 +656,6 @@ buf_size = RX_BUF_SIZE(pkt_size); } - for (port = 0; port < mv_pp2_ports_num; port++) { if (!((1 << port) & ppool->port_map)) continue; @@ -634,23 +665,23 @@ continue; /* If this pool is used as long pool, then it is expected that MTU will be smaller than buffer size */ - if (MV_ETH_BM_POOL_IS_LONG(ppool->type) && (RX_PKT_SIZE(pp->dev->mtu) > pkt_size)) - pr_warn("%s: port %d MTU (%d) is larger than requested packet size (%d) [total size = %d]\n", - __func__, port, RX_PKT_SIZE(pp->dev->mtu), pkt_size, total_size); + if (MV_ETH_BM_POOL_IS_LONG(ppool->type) && (RX_PKT_SIZE(pp->dev->mtu) > pkt_size)) { + pr_warn("port #%d: Failed. %s pool (%d) size (%d bytes) is too small for MTU=%d, pkt_size=%d\n", + port, mv_pp2_pool_type_str(ppool->type), pool, + total_size, pp->dev->mtu, RX_PKT_SIZE(pp->dev->mtu)); + return -EINVAL; } + } MV_ETH_LOCK(&ppool->lock, flags); pkts_num = ppool->buf_num; mv_pp2_pool_free(pool, pkts_num); + ppool->pkt_size = pkt_size; mv_pp2_pool_add(NULL, pool, pkts_num); mvBmPoolBufSizeSet(pool, buf_size); MV_ETH_UNLOCK(&ppool->lock, flags); - pr_info("%s: BM pool %d:\n", __func__, pool); - pr_info(" packet size = %d, buffer size = %d, total bytes per buffer = %d, true buffer size = %d\n", - pkt_size, buf_size, total_size, (int)RX_TRUE_SIZE(total_size)); - return 0; } @@ -693,7 +724,6 @@ return MV_OK; } -#ifdef CONFIG_MV_ETH_PP2_1 static int mv_pp2_hwf_long_pool_attach(int port, int pool) { int txp, txq; @@ -728,30 +758,6 @@ return MV_OK; } -#else - -/* Init classifer MTU */ -/* the same MTU for all Ports/Queues */ -static int mv_pp2_tx_mtu_set(int port, int mtu) -{ - int txp; - - struct eth_port *pp = mv_pp2_port_by_id(port); - - if (pp == NULL) { - pr_err("%s: port %d does not exist\n" , __func__, port); - return -EINVAL; - } - - for (txp = 0; txp < pp->txp_num; txp++) - mvPp2V0ClsHwMtuSet(MV_PPV2_PORT_PHYS(port), txp, RX_PKT_SIZE(mtu)); - - return MV_OK; - -} - -#endif /* CONFIG_MV_ETH_PP2_1 */ - int mv_pp2_ctrl_long_pool_set(int port, int pool) { unsigned long flags = 0; @@ -866,11 +872,7 @@ pp->hwf_pool_long->port_map |= (1 << port); MV_ETH_UNLOCK(&pp->hwf_pool_long->lock, flags); -#ifdef CONFIG_MV_ETH_PP2_1 mv_pp2_hwf_long_pool_attach(pp->port, pp->hwf_pool_long->pool); -#else - mvPp2PortHwfBmPoolSet(pp->port, pp->hwf_pool_short->pool, pp->hwf_pool_long->pool); -#endif return 0; } @@ -907,11 +909,7 @@ pp->hwf_pool_short->port_map |= (1 << port); MV_ETH_UNLOCK(&pp->hwf_pool_short->lock, flags); -#ifdef CONFIG_MV_ETH_PP2_1 mv_pp2_hwf_short_pool_attach(pp->port, pp->hwf_pool_short->pool); -#else - mvPp2PortHwfBmPoolSet(pp->port, pp->hwf_pool_short->pool, pp->hwf_pool_long->pool); -#endif return 0; } @@ -993,12 +991,7 @@ { int txq_min_size, txq_max_size = MV_PP2_TXQ_DESC_SIZE_MASK; -#ifdef CONFIG_MV_ETH_PP2_1 txq_min_size = 3 * (nr_cpu_ids * txq_ctrl->rsvd_chunk); -#else - /* At least 16 descriptors per CPU */ - txq_min_size = txq_ctrl->hwf_size + 16 * nr_cpu_ids; -#endif /* CONFIG_MV_ETH_PP2_1 */ if ((txq_size < txq_min_size) || (txq_size > txq_max_size)) { pr_err("Invalid TXQ size %d. Valid range: %d .. %d\n", @@ -1022,7 +1015,6 @@ txq_ctrl->txq_size = txq_size; -#ifdef CONFIG_MV_ETH_PP2_1 txq_ctrl->hwf_size = txq_ctrl->txq_size - (nr_cpu_ids * txq_ctrl->rsvd_chunk); txq_ctrl->swf_size = txq_ctrl->txq_size - 2 * (nr_cpu_ids * txq_ctrl->rsvd_chunk); @@ -1031,16 +1023,7 @@ txq_cpu_ptr->txq_size = txq_ctrl->txq_size; } -#else - txq_ctrl->hwf_size = CONFIG_MV_PP2_TXQ_HWF_DESC; - - for_each_possible_cpu(cpu) { - txq_cpu_ptr = &txq_ctrl->txq_cpu[cpu]; - - txq_cpu_ptr->txq_size = (txq_ctrl->txq_size - txq_ctrl->hwf_size) / nr_cpu_ids; } -#endif /* CONFIG_MV_ETH_PP2_1 */ -} /* set SWF request chunk size */ int mv_pp2_ctrl_txq_chunk_set(int port, int txp, int txq, int chunk_size) @@ -1107,13 +1090,11 @@ return -EINVAL; } -#ifdef CONFIG_MV_ETH_PP2_1 if (hwf_size < swf_size) { pr_err("Invalid size params, swf size must be less than hwf size\n"); return -EINVAL; } txq_ctrl->swf_size = swf_size; -#endif /* CONFIG_MV_ETH_PP2_1 */ txq_ctrl->hwf_size = hwf_size; @@ -1639,7 +1620,7 @@ else dev = pp->dev->dev.parent; - skb = __dev_alloc_skb(pool->pkt_size, gfp_mask); + skb = __dev_alloc_skb(pool->pkt_size, GFP_DMA | gfp_mask); if (!skb) { STAT_ERR(pool->stats.skb_alloc_oom++); return NULL; @@ -1822,12 +1803,10 @@ MV_U32 buff_phys_addr, buff_virt_addr, buff_phys_addr_next, buff_virt_addr_next; int count = 0; -#ifdef CONFIG_MV_ETH_PP2_1 int qset, is_grntd; qset = (rx_desc->bmQset & PP2_RX_BUFF_QSET_NUM_MASK) >> PP2_RX_BUFF_QSET_NUM_OFFS; is_grntd = (rx_desc->bmQset & PP2_RX_BUFF_TYPE_MASK) >> PP2_RX_BUFF_TYPE_OFFS; -#endif pool_id = (rx_status & PP2_RX_BM_POOL_ALL_MASK) >> PP2_RX_BM_POOL_ID_OFFS; buff_phys_addr = rx_desc->bufPhysAddr; @@ -1853,15 +1832,11 @@ buff_virt_addr_next = buff_hdr->nextBuffVirtAddr; /* release buffer */ -#ifdef CONFIG_MV_ETH_PP2_1 mvBmPoolQsetMcPut(pool_id, buff_phys_addr, buff_virt_addr, qset, is_grntd, mc_id, 0); /* Qset number and buffer type of next buffer */ qset = (buff_hdr->bmQset & PP2_BUFF_HDR_BM_QSET_NUM_MASK) >> PP2_BUFF_HDR_BM_QSET_NUM_OFFS; is_grntd = (buff_hdr->bmQset & PP2_BUFF_HDR_BM_QSET_TYPE_MASK) >> PP2_BUFF_HDR_BM_QSET_TYPE_OFFS; -#else - mvBmPoolMcPut(pool_id, buff_phys_addr, buff_virt_addr, mc_id, 0); -#endif buff_phys_addr = buff_phys_addr_next; buff_virt_addr = buff_virt_addr_next; @@ -2166,15 +2141,8 @@ } /* is enough descriptors? */ -#ifdef CONFIG_MV_ETH_PP2_1 if (mv_pp2_reserved_desc_num_proc(pp, tx_spec_ptr->txp, tx_spec_ptr->txq, frags) || mv_pp2_aggr_desc_num_check(aggr_txq_ctrl, frags)) { -#else - if (mv_pp2_phys_desc_num_check(txq_cpu_ptr, frags) || - mv_pp2_aggr_desc_num_check(aggr_txq_ctrl, frags)) { - -#endif - frags = 0; goto out; } @@ -2228,10 +2196,8 @@ STAT_DBG(pp->stats.tx_sg++); } -#ifdef CONFIG_MV_ETH_PP2_1 /* PPv2.1 - MAS 3.16, decrease number of reserved descriptors */ txq_cpu_ptr->reserved_num -= frags; -#endif txq_cpu_ptr->txq_count += frags; aggr_txq_ctrl->txq_count += frags; @@ -2450,11 +2416,7 @@ txq_cpu_ptr = &txq_ctrl->txq_cpu[smp_processor_id()]; /* Check if there are enough descriptors in physical TXQ */ -#ifdef CONFIG_MV_ETH_PP2_1 if (mv_pp2_reserved_desc_num_proc(priv, tx_spec->txp, tx_spec->txq, max_desc_num)) { -#else - if (mv_pp2_phys_desc_num_check(txq_cpu_ptr, max_desc_num)) { -#endif STAT_DBG(priv->stats.tx_tso_no_resource++); return 0; } @@ -2577,10 +2539,8 @@ wmb(); mvPp2AggrTxqPendDescAdd(total_desc_num); -#ifdef CONFIG_MV_ETH_PP2_1 /* PPv2.1 - MAS 3.16, decrease number of reserved descriptors */ txq_cpu_ptr->reserved_num -= total_desc_num; -#endif aggr_txq_ctrl->txq_count += total_desc_num; txq_cpu_ptr->txq_count += total_desc_num; @@ -2826,8 +2786,8 @@ } i++; } - pr_info("bm pool #%d: pkt_size=%4d, buf_size=%4d, total buf_size=%4d - %d of %d buffers free\n", - pool, ppool->pkt_size, buf_size, total_size, i, num); + mv_pp2_pool_status_print(pool); + pr_info(" - %d of %d buffers free\n", i, num); ppool->buf_num -= num; @@ -2926,10 +2886,8 @@ /* Update BM driver with number of buffers added to pool */ mvBmPoolBufNumUpdate(pool, i, 1); - pr_info("%s %s pool #%d: pkt_size=%4d, buf_size=%4d, total_size=%4d - %d of %d buffers added\n", - MV_ETH_BM_POOL_IS_HWF(bm_pool->type) ? "HWF" : "SWF", - MV_ETH_BM_POOL_IS_SHORT(bm_pool->type) ? "short" : " long", - pool, bm_pool->pkt_size, buf_size, total_size, i, buf_num); + mv_pp2_pool_status_print(pool); + pr_info(" - %d of %d buffers added\n", i, buf_num); return i; } @@ -3074,7 +3032,8 @@ return NULL; } - } + } else + mv_pp2_pool_status_print(new_pool->pool); if (MV_ETH_BM_POOL_IS_HWF(new_pool->type)) mvPp2BmPoolBufSizeSet(new_pool->pool, RX_HWF_BUF_SIZE(new_pool->pkt_size)); @@ -3453,9 +3412,7 @@ pp->hwf_pool_long->port_map |= (1 << pp->port); MV_ETH_UNLOCK(&pp->hwf_pool_long->lock, flags); -#ifdef CONFIG_MV_ETH_PP2_1 mv_pp2_hwf_long_pool_attach(pp->port, pp->hwf_pool_long->pool); -#endif } if (pp->hwf_pool_short == NULL) { @@ -3468,15 +3425,9 @@ pp->hwf_pool_short->port_map |= (1 << pp->port); MV_ETH_UNLOCK(&pp->hwf_pool_short->lock, flags); -#ifdef CONFIG_MV_ETH_PP2_1 mv_pp2_hwf_short_pool_attach(pp->port, pp->hwf_pool_short->pool); -#endif } -#ifndef CONFIG_MV_ETH_PP2_1 - mvPp2PortHwfBmPoolSet(pp->port, pp->hwf_pool_short->pool, pp->hwf_pool_long->pool); -#endif - return 0; } #endif /* CONFIG_MV_PP2_HWF */ @@ -3529,7 +3480,7 @@ mtu = mv_pp2_config_get(pdev, mac); - dev = mv_pp2_netdev_init(mtu, mac, pdev); + dev = mv_pp2_netdev_init(pdev); if (dev == NULL) { pr_err("\to %s: can't create netdevice\n", __func__); @@ -3617,19 +3568,12 @@ } if (mv_pp2_pnc_ctrl_en) { -#ifndef CONFIG_MV_ETH_PP2_1 - mv_pp2_tx_mtu_set(port, mtu); -#endif /* CONFIG_MV_ETH_PP2_1 */ -#ifndef CONFIG_MV_ETH_PP2_1 - mvPp2ClsHwOversizeRxqSet(MV_PPV2_PORT_PHYS(pp->port), pp->first_rxq); -#else mvPp2ClsHwOversizeRxqLowSet(MV_PPV2_PORT_PHYS(pp->port), (pp->first_rxq) & MV_PP2_CLS_OVERSIZE_RXQ_LOW_MASK); mvPp2ClsHwRxQueueHighSet(MV_PPV2_PORT_PHYS(pp->port), 1, (pp->first_rxq) >> MV_PP2_CLS_OVERSIZE_RXQ_LOW_BITS); -#endif /* classifier port default config */ mvPp2ClsHwPortDefConfig(phys_port, 0, FLOWID_DEF(phys_port), pp->first_rxq); @@ -3868,9 +3812,7 @@ mv_pp2_l2fw_sysfs_exit(&pd->kobj); #endif -#ifdef CONFIG_MV_ETH_PP2_1 mv_pp2_dpi_sysfs_exit(&pd->kobj); -#endif mv_pp2_wol_sysfs_exit(&pd->kobj); mv_pp2_pme_sysfs_exit(&pd->kobj); @@ -3915,9 +3857,7 @@ mv_pp2_dbg_sysfs_init(&pd->kobj); mv_pp2_wol_sysfs_init(&pd->kobj); -#ifdef CONFIG_MV_ETH_PP2_1 mv_pp2_dpi_sysfs_init(&pd->kobj); -#endif #ifdef CONFIG_MV_PP2_L2FW mv_pp2_l2fw_sysfs_init(&pd->kobj); @@ -3979,9 +3919,7 @@ printk(KERN_ERR "%s: Warning Classifier defauld init failed\n", __func__); } -#ifdef CONFIG_MV_ETH_PP2_1 mvPp2DpiInit(); -#endif /* Initialize tasklet for handle link events */ tasklet_init(&link_tasklet, mv_pp2_link_tasklet, 0); @@ -4362,13 +4300,13 @@ * mv_eth_netdev_init -- Allocate and initialize net_device * * structure * ***************************************************************/ -struct net_device *mv_pp2_netdev_init(int mtu, u8 *mac, struct platform_device *pdev) +struct net_device *mv_pp2_netdev_init(struct platform_device *pdev) { struct net_device *dev; struct eth_port *dev_priv; -#ifdef CONFIG_OF + struct mv_pp2_pdata *plat_data = (struct mv_pp2_pdata *)pdev->dev.platform_data; -#else +#ifndef CONFIG_OF struct resource *res; #endif @@ -4388,24 +4326,28 @@ memset(dev_priv, 0, sizeof(struct eth_port)); dev_priv->dev = dev; + dev_priv->port = pdev->id; #ifdef CONFIG_OF dev->irq = plat_data->irq; +#else + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + BUG_ON(!res); + dev->irq = res->start; +#endif if (!is_valid_ether_addr(plat_data->mac_addr)) +#ifdef CONFIG_OF eth_hw_addr_random(dev); +#else + memset(dev->dev_addr, 0, MV_MAC_ADDR_SIZE); +#endif else { memcpy(dev->dev_addr, plat_data->mac_addr, MV_MAC_ADDR_SIZE); memcpy(dev->perm_addr, plat_data->mac_addr, MV_MAC_ADDR_SIZE); } -#else - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - BUG_ON(!res); - dev->irq = res->start; - memcpy(dev->dev_addr, mac, MV_MAC_ADDR_SIZE); - memcpy(dev->perm_addr, mac, MV_MAC_ADDR_SIZE); -#endif - dev->mtu = mtu; + + dev->mtu = plat_data->mtu; dev->tx_queue_len = CONFIG_MV_PP2_TXQ_DESC; dev->watchdog_timeo = 5 * HZ; @@ -4503,11 +4445,7 @@ txq_ctrl->txq_done_pkts_coal = mv_ctrl_pp2_txdone; -#ifdef CONFIG_MV_ETH_PP2_1 txq_ctrl->rsvd_chunk = CONFIG_MV_PP2_TXQ_CPU_CHUNK; -#else - txq_ctrl->hwf_size = CONFIG_MV_PP2_TXQ_HWF_DESC; -#endif txq_size = mv_pp2_txq_size_validate(txq_ctrl, CONFIG_MV_PP2_TXQ_DESC); if (txq_size < 0) return -ENODEV; @@ -4559,11 +4497,7 @@ /* Show network driver configuration */ void mv_pp2_config_show(void) { -#ifdef CONFIG_MV_ETH_PP2_1 pr_info(" o PPv2.1 Giga driver\n"); -#else - pr_info(" o PPv2.0 Giga driver\n"); -#endif pr_info(" o %d Giga ports supported\n", mv_pp2_ports_num); @@ -4662,14 +4596,9 @@ txq_ctrl->q = mvPp2TxqInit(pp->port, txq_ctrl->txp, txq_ctrl->txq, txq_ctrl->txq_size, txq_ctrl->hwf_size); if (txq_ctrl->q == NULL) { -#ifdef CONFIG_MV_ETH_PP2_1 printk(KERN_ERR "%s: can't create TxQ - port=%d, txp=%d, txq=%d, desc=%d, hwf desc=%d swf desc = %d\n", __func__, pp->port, txq_ctrl->txp, txq_ctrl->txp, txq_ctrl->txq_size, txq_ctrl->hwf_size, txq_ctrl->swf_size); -#else - printk(KERN_ERR "%s: can't create TxQ - port=%d, txp=%d, txq=%d, desc=%d, hwf desc=%d\n", - __func__, pp->port, txq_ctrl->txp, txq_ctrl->txp, txq_ctrl->txq_size, txq_ctrl->hwf_size); -#endif return -ENODEV; } @@ -5125,9 +5054,6 @@ mv_pon_mtu_config(pkt_size); #endif -#ifndef CONFIG_MV_ETH_PP2_1 - mv_pp2_tx_mtu_set(pp->port, pkt_size); -#endif mtu_out: dev->mtu = mtu; @@ -5507,10 +5433,19 @@ ***********************************************************************************/ void mv_pp2_pool_status_print(int pool) { - const char *type; - struct bm_pool *bm_pool = &mv_pp2_pool[pool]; + struct bm_pool *bm_pool; int buf_size, total_size, true_size; + if ((pool < 0) || (pool >= MV_ETH_BM_POOLS)) { + pr_err("%s: Invalid pool number (%d)\n", __func__, pool); + return; + } + bm_pool = &mv_pp2_pool[pool]; + if (bm_pool == NULL) { + pr_err("%s: BM pool %d is not initialized\n", __func__, pool); + return; + } + if (MV_ETH_BM_POOL_IS_HWF(bm_pool->type)) { buf_size = RX_HWF_BUF_SIZE(bm_pool->pkt_size); total_size = RX_HWF_TOTAL_SIZE(buf_size); @@ -5520,56 +5455,46 @@ } true_size = RX_TRUE_SIZE(total_size); - switch (bm_pool->type) { - case MV_ETH_BM_FREE: - type = "MV_ETH_BM_FREE"; - break; - case MV_ETH_BM_SWF_LONG: - type = "MV_ETH_BM_SWF_LONG"; - break; - case MV_ETH_BM_SWF_SHORT: - type = "MV_ETH_BM_SWF_SHORT"; - break; - case MV_ETH_BM_HWF_LONG: - type = "MV_ETH_BM_HWF_LONG"; - break; - case MV_ETH_BM_HWF_SHORT: - type = "MV_ETH_BM_HWF_SHORT"; - break; - case MV_ETH_BM_MIXED_LONG: - type = "MV_ETH_BM_MIXED_LONG"; - break; - case MV_ETH_BM_MIXED_SHORT: - type = "MV_ETH_BM_MIXED_SHORT"; - break; - default: - type = "Unknown"; - } - - pr_info("\nBM Pool #%d: pool type = %s, buffers num = %d\n", pool, type, bm_pool->buf_num); - pr_info(" packet size = %d, buffer size = %d, total size = %d, true size = %d\n", - bm_pool->pkt_size, buf_size, total_size, true_size); - pr_info(" capacity=%d, buf_num=%d, port_map=0x%x, in_use=%u, in_use_thresh=%u\n", + pr_info("\n%9s pool #%d: pkt_size=%4d, buf_size=%4d, total_size=%4d, allocated_size=%4d\n", + mv_pp2_pool_type_str(bm_pool->type), pool, + bm_pool->pkt_size, buf_size, total_size, (int)RX_TRUE_SIZE(total_size)); + pr_info("\tcapacity=%d, buf_num=%d, port_map=0x%x, in_use=%u, in_use_thresh=%u\n", bm_pool->capacity, bm_pool->buf_num, bm_pool->port_map, mv_pp2_bm_in_use_read(bm_pool), bm_pool->in_use_thresh); +} +void mv_pp2_pool_stats_print(int pool) +{ + struct bm_pool *bm_pool; + + if ((pool < 0) || (pool >= MV_ETH_BM_POOLS)) { + pr_err("%s: Invalid pool number (%d)\n", __func__, pool); + return; + } + bm_pool = &mv_pp2_pool[pool]; + if (bm_pool == NULL) { + pr_err("%s: BM pool %d is not initialized\n", __func__, pool); + return; + } + #ifdef CONFIG_MV_PP2_STAT_ERR - pr_cont(" skb_alloc_oom=%u", bm_pool->stats.skb_alloc_oom); + pr_info("skb_alloc_oom = %u", bm_pool->stats.skb_alloc_oom); #endif /* #ifdef CONFIG_MV_PP2_STAT_ERR */ #ifdef CONFIG_MV_PP2_STAT_DBG - pr_cont(", skb_alloc_ok=%u, bm_put=%u\n", - bm_pool->stats.skb_alloc_ok, bm_pool->stats.bm_put); + pr_info("skb_alloc_ok = %u\n", bm_pool->stats.skb_alloc_ok); - pr_info(" no_recycle=%u, skb_recycled_ok=%u, skb_recycled_err=%u, bm_cookie_err=%u\n", - bm_pool->stats.no_recycle, bm_pool->stats.skb_recycled_ok, - bm_pool->stats.skb_recycled_err, bm_pool->stats.bm_cookie_err); + pr_info("bm_put = %u\n", bm_pool->stats.bm_put); +#ifdef CONFIG_MV_PP2_SKB_RECYCLE + pr_info("no_recycle = %u\n", bm_pool->stats.no_recycle); + pr_info("skb_recycled_ok = %u\n", bm_pool->stats.bm_put); + pr_info("skb_recycled_err = %u\n", bm_pool->stats.skb_recycled_err); + pr_info("bm_cookie_err = %u\n", bm_pool->stats.bm_cookie_err); +#endif /* CONFIG_MV_PP2_SKB_RECYCLE */ #endif /* CONFIG_MV_PP2_STAT_DBG */ memset(&bm_pool->stats, 0, sizeof(bm_pool->stats)); } - - /*********************************************************************************** *** print ext pool status ***********************************************************************************/ @@ -5693,7 +5618,6 @@ } pr_cont("\n"); -#ifdef CONFIG_MV_ETH_PP2_1 pr_info("txq_swf_desc(num) [%2d.q] = ", txp); for (q = 0; q < CONFIG_MV_PP2_TXQ; q++) { txq_ctrl = &pp->txq_ctrl[txp * CONFIG_MV_PP2_TXQ + q]; @@ -5704,13 +5628,6 @@ txq_ctrl = &pp->txq_ctrl[txp * CONFIG_MV_PP2_TXQ + q]; pr_cont("%4d ", txq_ctrl->rsvd_chunk); } -#else - pr_info("txq_swf_desc(num) [%2d.q] = ", txp); - for (q = 0; q < CONFIG_MV_PP2_TXQ; q++) { - txq_ctrl = &pp->txq_ctrl[txp * CONFIG_MV_PP2_TXQ + q]; - pr_cont("%4d ", txq_ctrl->txq_cpu[0].txq_size); - } -#endif /* CONFIG_MV_ETH_PP2_1 */ pr_cont("\n"); } @@ -5755,7 +5672,7 @@ printk(KERN_CONT "\n"); - mv_pp2_napi_groups_print(port); + /* mv_pp2_napi_groups_print(port); */ /* Print status of all mux_dev for this port */ if (pp->tagged) { @@ -5763,6 +5680,19 @@ mv_mux_netdev_print_all(port); } else printk(KERN_CONT "UNTAGGED PORT\n"); + + pr_info("\nBM pools used by port #%d:\n", port); + if (pp->pool_short) + mv_pp2_pool_status_print(pp->pool_short->pool); + + if (pp->pool_long) + mv_pp2_pool_status_print(pp->pool_long->pool); + + if (pp->hwf_pool_short) + mv_pp2_pool_status_print(pp->hwf_pool_short->pool); + + if (pp->hwf_pool_short) + mv_pp2_pool_status_print(pp->hwf_pool_long->pool); } @@ -5944,12 +5874,22 @@ memset(stat, 0, sizeof(struct port_stats)); /* RX pool statistics */ - if (pp->pool_short) + if (pp->pool_short) { mv_pp2_pool_status_print(pp->pool_short->pool); + mv_pp2_pool_stats_print(pp->pool_short->pool); + } - if (pp->pool_long) + if (pp->pool_long) { mv_pp2_pool_status_print(pp->pool_long->pool); + mv_pp2_pool_stats_print(pp->pool_long->pool); + } + if (pp->hwf_pool_short) + mv_pp2_pool_status_print(pp->hwf_pool_short->pool); + + if (pp->hwf_pool_long) + mv_pp2_pool_status_print(pp->hwf_pool_long->pool); + #ifdef CONFIG_MV_PP2_STAT_DIST { int i; Index: drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_netdev.h =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_netdev.h (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/net_dev/mv_netdev.h (working copy) @@ -953,6 +953,7 @@ void mv_pp2_eth_port_status_print(unsigned int port); void mv_pp2_port_stats_print(unsigned int port); void mv_pp2_pool_status_print(int pool); +void mv_pp2_pool_stats_print(int pool); void mv_pp2_set_noqueue(struct net_device *dev, int enable); void mv_pp2_ctrl_pnc(int en); Index: drivers/net/ethernet/mvebu_net/pp2/plcr/plcr_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/plcr/plcr_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/plcr/plcr_sysfs.c (working copy) @@ -55,9 +55,7 @@ off += scnprintf(buf + off, PAGE_SIZE - off, "echo 0|1 > rate - Enable <1> or Disable <0> addition of tokens to token buckets\n"); off += scnprintf(buf + off, PAGE_SIZE - off, "echo bytes > min_pkt - Set minimal packet length\n"); off += scnprintf(buf + off, PAGE_SIZE - off, "echo 0|1 > edrop - Enable <1> or Disable <0> early packets drop\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE - off, "echo mode > mode - Set policer mode of operation 0-bank01 1-bank10 2-parallal\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE - off, "echo p 0|1 > enable - Enable <1> or Disable <0> policer

\n"); off += scnprintf(buf + off, PAGE_SIZE - off, "echo p 0|1 > color - Set color mode for policer

: 0-blind, 1-aware\n"); off += scnprintf(buf + off, PAGE_SIZE - off, "echo p u t > config - Set token units and update type for policer

\n"); Index: drivers/net/ethernet/mvebu_net/pp2/prs/prs_low_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/pp2/prs/prs_low_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/pp2/prs/prs_low_sysfs.c (working copy) @@ -47,9 +47,7 @@ off += scnprintf(buf + off, PAGE_SIZE - off, "cat sw_dump - dump parser SW entry.\n"); off += scnprintf(buf + off, PAGE_SIZE - off, "cat hw_dump - dump all valid HW entries\n"); off += scnprintf(buf + off, PAGE_SIZE - off, "cat hw_regs - dump parser registers.\n"); -#ifdef CONFIG_MV_ETH_PP2_1 off += scnprintf(buf + off, PAGE_SIZE - off, "cat hw_hits - dump non zeroed hit counters and the associated HW entries\n"); -#endif off += scnprintf(buf + off, PAGE_SIZE - off, "\n"); off += scnprintf(buf + off, PAGE_SIZE - off, "echo id > hw_write - write parser SW entry into HW place .\n"); Index: drivers/net/ethernet/mvebu_net/prestera/.gitignore =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/.gitignore (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/.gitignore (working copy) @@ -0,0 +1,91 @@ + +# +# NOTE! Don't add files that are generated in specific +# subdirectories here. Add them in the ".gitignore" file +# in that subdirectory instead. +# +# NOTE! Please use 'git ls-files -i --exclude-standard' +# command after changing this file, to see if there are +# any tracked files which get ignored after the change. +# +# Normal rules +# +.* +*.o +*.o.* +*.a +*.s +*.ko +*.so +*.so.dbg +*.mod.c +*.i +*.lst +*.symtypes +*.order +modules.builtin +*.elf +*.bin +*.gz +*.bz2 +*.lzma +*.xz +*.lzo +*.patch +*.gcno + +# +# +# + +# +# Top-level generic files +# +/tags +/TAGS +/linux +/vmlinux +/vmlinuz +/System.map +/Module.markers +/Module.symvers + +# +# Debian directory (make deb-pkg) +# +/debian/ + +# +# git files that we don't want to ignore even it they are dot-files +# +!.gitignore +!.mailmap + +# +# Generated include files +# +include/config +include/linux/version.h +include/generated +arch/*/include/generated + +# stgit generated dirs +patches-* + +# quilt's files +patches +series + +# cscope files +cscope.* +ncscope.* + +# gnu global files +GPATH +GRTAGS +GSYMS +GTAGS + +*.orig +*~ +\#*# Index: drivers/net/ethernet/mvebu_net/prestera/Kconfig =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/Kconfig (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/Kconfig (working copy) @@ -0,0 +1,34 @@ + +config MV_INCLUDE_PRESTERA + bool "Prestera Switch Support" + depends on ARCH_MVEBU +# select MV_INCLUDE_PRESTERA_PCI if MACH_ARMADA_XP_AMC + default y + help + Add Prestera mvPP char device driver support, + used by user space to configure and interact with + Prestera Packet Procesors, supporting switches connected + via PCIe to AMC/AXP and MSYS. + This option should be enabled when using CPSS. + + +config MV_INCLUDE_PRESTERA_PCI + bool "Prestera Switch Support for PCI endpoint" + depends on MV_INCLUDE_PRESTERA + default y + help + this option increase virtual memory + allocation in case of ARMADA XP AMC + board for CPSS devices connected to + PCI switch + +config MV_INCLUDE_PRESTERA_KERNELEXT + tristate "Prestera mvKernelExt" + depends on MV_INCLUDE_PRESTERA + default y + help + Add Prestera mvKernelExt char device driver support, + used by user space to configure and interact with + Prestera Packet Processors, supporting switches connected + via PCIe to AMC/AXP and MSYS. + This option should be enabled when using CPSS. Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/2_6/mv_KernelExt.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/2_6/mv_KernelExt.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/2_6/mv_KernelExt.c (working copy) @@ -0,0 +1,968 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************** +* mvKernelExt.c +* +* DESCRIPTION: +* functions in kernel mode special for mainOs. +* +* DEPENDENCIES: +* mvKernelExt.h +* mvKernelExt_Sem.c +* +* $Revision: 9$ +*******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MVKERNELEXT_SYSCALLS +# include +# include +#endif + +#include "mv_KernelExt.h" + +#ifdef ENABLE_REALTIME_SCHEDULING_POLICY +# define MV_PRIO_MIN 1 +# define MV_PRIO_MAX (MAX_USER_RT_PRIO-20) +#else +# define MV_PRIO_MIN MAX_RT_PRIO +# define MV_PRIO_MAX MAX_PRIO +#endif + +#ifdef CONFIG_SMP +/* spinlock_t mv_giantlock = SPIN_LOCK_UNLOCKED; */ +DEFINE_SPINLOCK(mv_giantlock); +#endif + +/* local variables and variables */ +static int mvKernelExt_major = MVKERNELEXT_MAJOR; +static int mvKernelExt_minor = MVKERNELEXT_MINOR; +static int mvKernelExt_initialized = 0; +static int mvKernelExt_opened = 0; +static struct cdev mvKernelExt_cdev; + +module_param(mvKernelExt_major, int, S_IRUGO); +module_param(mvKernelExt_minor, int, S_IRUGO); +MODULE_AUTHOR("Marvell Semi."); +MODULE_LICENSE("GPL"); + +/************************************************************************ + * mvKernelExt_read: this should be the read device function, for now in + * current mvKernelExt driver implemention it does nothing + ************************************************************************/ +static ssize_t mvKernelExt_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) +{ + return -ERESTARTSYS; +} + +/************************************************************************ + * + * mvKernelExt_write: this should be the write device function, for now in + * current mvKernelExt driver implemention it does nothing + * + ************************************************************************/ +static ssize_t mvKernelExt_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) +{ + return -ERESTARTSYS; +} + +/************************************************************************ + * + * mvKernelExt_lseek: this should be the lseek device function, for now in + * current mvKernelExt driver implemention it does nothing + * + ************************************************************************/ +static loff_t mvKernelExt_lseek(struct file *filp, loff_t off, int whence) +{ + return -ERESTARTSYS; +} + + +#include "../common/mv_KernelExt.c" +#include "../common/mv_KernelExtSem.c" +#include "../common/mv_KernelExtMsgQ.c" + + +/************************************************************************ +* +* waitqueue support functions +* +* These functions required to suspend thread till some event occurs +************************************************************************/ + +/******************************************************************************* +* mv_waitqueue_init +* +* DESCRIPTION: +* Initialize wait queue structure +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static void mv_waitqueue_init( + mv_waitqueue_t* queue +) +{ + memset(queue, 0, sizeof(*queue)); +} + +/******************************************************************************* +* mv_waitqueue_cleanup +* +* DESCRIPTION: +* Cleanup wait queue structure +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static void mv_waitqueue_cleanup( + mv_waitqueue_t* queue +) +{ + memset(queue, 0, sizeof(*queue)); +} + +/******************************************************************************* +* mv_waitqueue_add +* +* DESCRIPTION: +* add task to wait queue +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_waitqueue_add( + mv_waitqueue_t* queue, + struct mv_task* tsk +) +{ + tsk->waitqueue = queue; + tsk->wait_next = NULL; + if (queue->first == NULL) + { + queue->first = tsk; + } else { + queue->last->wait_next = tsk; + } + queue->last = tsk; +} + +/******************************************************************************* +* mv_waitqueue_wake_first +* +* DESCRIPTION: +* wakeup first task waiting in queue +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_waitqueue_wake_first( + mv_waitqueue_t* queue +) +{ + struct mv_task *p; + + p = queue->first; + + if (!p) + return; + + if (p->tasklockflag == 1) + p->task->state &= ~TASK_INTERRUPTIBLE; + else if (p->tasklockflag == 2) + wake_up_process(p->task); + p->tasklockflag = 0; + + + p->waitqueue = NULL; + queue->first = p->wait_next; + if (queue->first == NULL) + queue->last = NULL; +} + +/******************************************************************************* +* mv_waitqueue_wake_all +* +* DESCRIPTION: +* wakeup all tasks waiting in queue +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_waitqueue_wake_all( + mv_waitqueue_t* queue +) +{ + struct mv_task *p; + + p = queue->first; + + if (!p) + return; + + while (p) + { + if (p->tasklockflag == 1) + p->task->state &= ~TASK_INTERRUPTIBLE; + else if (p->tasklockflag == 2) + wake_up_process(p->task); + p->tasklockflag = 0; + + p->waitqueue = NULL; + p = p->wait_next; + } + + queue->first = queue->last = NULL; +} + +/******************************************************************************* +* mv_delete_from_waitqueue +* +* DESCRIPTION: +* remove task from wait queue +* +* INPUTS: +* tsk - pointer to task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_delete_from_waitqueue( + struct mv_task* tsk +) +{ + mv_waitqueue_t* queue; + struct mv_task* p; + + queue = tsk->waitqueue; + if (!queue) + return; + + tsk->waitqueue = NULL; + + if (queue->first == tsk) + { + queue->first = tsk->wait_next; + if (queue->first == NULL) + queue->last = NULL; + return; + } + + for (p = queue->first; p; p = p->wait_next) + { + if (p->wait_next == tsk) + { + p->wait_next = tsk->wait_next; + if (p->wait_next == NULL) + queue->last = p; + return; + } + } + + if (p->tasklockflag == 1) + p->task->state &= ~TASK_INTERRUPTIBLE; + else if (p->tasklockflag == 2) + wake_up_process(p->task); + p->tasklockflag = 0; + +} + +/******************************************************************************* +* mv_do_short_wait_on_queue +* +* DESCRIPTION: +* Suspend a task on wait queue for a short period. +* Function has a best performance in a cost of CPU usage +* This is useful for mutual exclusion semaphores +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* owner - resourse owner +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if wait successful +* Non zero if wait interrupted (signal caught) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static int mv_do_short_wait_on_queue( + mv_waitqueue_t* queue, + struct mv_task* tsk, + struct task_struct** owner +) +{ + mv_waitqueue_add(queue, tsk); + + tsk->tasklockflag = 1; +#ifndef ENABLE_REALTIME_SCHEDULING_POLICY + while (tsk->waitqueue) + { + if (unlikely(signal_pending(tsk->task))) + { + tsk->tasklockflag = 0; + mv_delete_from_waitqueue(tsk); + return -1; + } + tsk->task->state |= TASK_INTERRUPTIBLE; + MV_GLOBAL_UNLOCK(); + yield(); + MV_GLOBAL_LOCK(); + } + return 0; +#else + while (tsk->waitqueue) + { + if (rt_task(tsk->task) && *owner) + { + if (tsk->task->prio <= (*owner)->prio) + { + /* spin locks are not acceptable */ + break; + } + } + if (unlikely(signal_pending(tsk->task))) + { + tsk->tasklockflag = 0; + mv_delete_from_waitqueue(tsk); + return -1; + } + tsk->task->state |= TASK_INTERRUPTIBLE; + MV_GLOBAL_UNLOCK(); + yield(); + MV_GLOBAL_LOCK(); + } + if (!tsk->waitqueue) + return 0; + + /* currect task is realtime and has higher prio than resource owner */ + tsk->tasklockflag = 2; + while (tsk->waitqueue) + { + if (unlikely(signal_pending(tsk->task))) + { + tsk->tasklockflag = 0; + mv_delete_from_waitqueue(tsk); + return -1; + } + set_task_state(tsk->task, TASK_INTERRUPTIBLE); + MV_GLOBAL_UNLOCK(); + schedule(); + MV_GLOBAL_LOCK(); + } + return 0; +#endif +} + +/******************************************************************************* +* mv_do_wait_on_queue +* +* DESCRIPTION: +* Suspend a task on wait queue. +* Function has the same performance as mv_do_short_wait_on_queue when +* task suspended for short period. After that task state changed to +* suspended +* This is useful for binary and counting semaphores +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if wait successful +* Non zero if wait interrupted (signal caught) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static int mv_do_wait_on_queue( + mv_waitqueue_t* queue, + struct mv_task* tsk +) +{ + int cnt = 200; + + mv_waitqueue_add(queue, tsk); + + tsk->tasklockflag = 1; + while (tsk->waitqueue && cnt--) + { + if (unlikely(signal_pending(tsk->task))) + { + tsk->tasklockflag = 0; + mv_delete_from_waitqueue(tsk); + return -1; + } + tsk->task->state |= TASK_INTERRUPTIBLE; + MV_GLOBAL_UNLOCK(); + yield(); + MV_GLOBAL_LOCK(); + if (!tsk->waitqueue) + return 0; + } + + tsk->tasklockflag = 2; + while (tsk->waitqueue) + { + if (unlikely(signal_pending(tsk->task))) + { + tsk->tasklockflag = 0; + mv_delete_from_waitqueue(tsk); + return -1; + } + set_task_state(tsk->task, TASK_INTERRUPTIBLE); + MV_GLOBAL_UNLOCK(); + schedule(); + MV_GLOBAL_LOCK(); + } + return 0; +} + +/******************************************************************************* +* mv_do_wait_on_queue_timeout +* +* DESCRIPTION: +* Suspend a task on wait queue. +* Return if timer expited. +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* timeout - timeout in scheduller ticks +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if wait successful +* Zero if timeout occured +* -1 if wait interrupted (signal caught) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static unsigned long mv_do_wait_on_queue_timeout( + mv_waitqueue_t* queue, + struct mv_task* tsk, + unsigned long timeout +) +{ + mv_waitqueue_add(queue, tsk); + + tsk->tasklockflag = 2; + while (tsk->waitqueue && timeout) + { + if (unlikely(signal_pending(tsk->task))) + { + tsk->tasklockflag = 0; + mv_delete_from_waitqueue(tsk); + return -1; + } + set_task_state(tsk->task, TASK_INTERRUPTIBLE); + MV_GLOBAL_UNLOCK(); + timeout = schedule_timeout(timeout); + MV_GLOBAL_LOCK(); + } + if (tsk->waitqueue) /* timeout, delete from waitqueue */ + { + tsk->tasklockflag = 0; + mv_delete_from_waitqueue(tsk); + } + return timeout; +} + + + + + + +/************************************************************************ +* +* Task lookup functions +* +* These functions required to lookup tasks in task array +************************************************************************/ + +/******************************************************************************* +* mv_check_tasks +* +* DESCRIPTION: +* Walk through task array and check if task still alive +* Perform cleanup actions for dead tasks +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mv_check_tasks(void) +{ + int k; + + MV_GLOBAL_LOCK(); + for (k = 0; k < mv_num_tasks; ) + { + /* search task */ + struct task_struct *p, *g; + int found = 0; + do_each_thread(g, p) { + if (p == mv_tasks[k]->task) + found = 1; + } while_each_thread(g, p); + if (!found) + { + /* task not found */ + mv_unregistertask(mv_tasks[k]->task); + continue; + } + if (mv_tasks[k]->task->exit_state) + { + mv_unregistertask(mv_tasks[k]->task); + continue; + } + k++; + } + MV_GLOBAL_UNLOCK(); +} + +/******************************************************************************* +* translate_priority +* +* DESCRIPTION: +* Translates a v2pthread priority into kernel priority +* +* INPUTS: +* policy - scheduler policy +* priority - vxWorks task priority +* +* OUTPUTS: +* None +* +* RETURNS: +* kernel priority +* +* COMMENTS: +* +*******************************************************************************/ +static int translate_priority( + int policy, + int priority +) +{ + if (policy == SCHED_NORMAL) + return 0; + + /* + ** Validate the range of the user's task priority. + */ + if (priority < 0 || priority > 255) + return MV_PRIO_MAX; + +#ifdef CPU_ARM + if (priority <= 10) + return MV_PRIO_MAX-1; +#endif + /* reverse */ + priority = 255 - priority; + + /* translate 0..255 to MAX_RT_PRIO..MAX_PRIO */ + priority *= (MV_PRIO_MAX-MV_PRIO_MIN); + priority >>= 8; + priority += MV_PRIO_MIN; + if (priority >= MV_PRIO_MAX) + priority = MV_PRIO_MAX-1; + + return( priority ); +} + + +/******************************************************************************* +* mv_set_prio +* +* DESCRIPTION: +* Set task priority +* +* INPUTS: +* param->taskid - task ID +* param->vxw_priority - vxWorks task priority +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_set_prio(mv_priority_stc *param) +{ + struct mv_task* p; + struct sched_param prio; + int policy; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(param->taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + p->vxw_priority = param->vxw_priority; + +#ifdef ENABLE_REALTIME_SCHEDULING_POLICY + policy = SCHED_RR; +#else + policy = SCHED_NORMAL; +#endif + prio.sched_priority = translate_priority(policy, param->vxw_priority); + sched_setscheduler(p->task, policy, &prio); + + return 0; +} + +/******************************************************************************* +* mvKernelExt_read_proc_mem +* +* DESCRIPTION: +* proc read data rooutine. +* Use cat /proc/mvKernelExt to show task list and tasklock state +* +* INPUTS: +* +* OUTPUTS: +* None +* +* RETURNS: +* Data length +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_read_proc_mem( + char *page, + char **start, + off_t offset, + int count, + int *eof, + void *data +) +{ + int len; + int k; + int begin = 0; + + len = 0; + len += sprintf(page+len,"mvKernelExt major=%d\n", mvKernelExt_major); +#ifdef MV_TASKLOCK_STAT + len += sprintf(page+len,"tasklock count=%d wait=%d\n", + mv_tasklock_lcount, + mv_tasklock_wcount); +#endif + + len += sprintf(page+len,"lock owner=%d\n", mv_tasklock_owner?mv_tasklock_owner->pid:0); + { + struct mv_task* p; + for (p = mv_tasklock_waitqueue.first; p; p = p->wait_next) + len += sprintf(page+len," wq: %d\n", p->task->pid); + } + /* list registered tasks */ + for (k = 0; k < mv_num_tasks; k++) + { + len += sprintf(page+len, + " task id=%d state=0x%x prio=%d(%d) flag=%d" +#ifdef MV_TASKLOCK_STAT + " tlcount=%d tlwait=%d" +#endif + " name=\"%s\"\n", + mv_tasks[k]->task->pid, + (unsigned int)mv_tasks[k]->task->state, + mv_tasks[k]->task->prio, + mv_tasks[k]->vxw_priority, + mv_tasks[k]->tasklockflag, +#ifdef MV_TASKLOCK_STAT + mv_tasks[k]->tasklock_lcount, + mv_tasks[k]->tasklock_wcount, +#endif + mv_tasks[k]->name); + if (len+begin < offset) + { + begin += len; + len = 0; + } + if (len+begin >= offset+count) + break; + } + + if (len+begin < offset) + *eof = 1; + offset -= begin; + *start = page + offset; + len -= offset; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +/************************************************************************ +* mvKernelExt_cleanup +* +* DESCRIPTION: +* Perform cleanup actions while module unloading +* Unregister /proc entry, remove device entry +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_cleanup(void) +{ + printk("mvKernelExt Says: Bye world from kernel\n"); + + mvKernelExt_cleanup_common(); + + mvKernelExt_initialized = 0; + + remove_proc_entry("mvKernelExt", NULL); + + unregister_chrdev_region(MKDEV(mvKernelExt_major, mvKernelExt_minor), 1); + + cdev_del(&mvKernelExt_cdev); + +} + +/************************************************************************ +* mvKernelExt_init +* +* DESCRIPTION: +* Module initialization +* Register device entry, /proc entry +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* Non zero if failed +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_init(void) +{ + int result = 0; + + printk(KERN_DEBUG "mvKernelExt_init\n"); + + /* first thing register the device at OS */ + + /* Register your major. */ + result = register_chrdev_region( + MKDEV(mvKernelExt_major, mvKernelExt_minor), + 1, "mvKernelExt"); + + if (result < 0) + { + printk("mvKernelExt_init: register_chrdev_region err= %d\n", result); + return result; + } + + cdev_init(&mvKernelExt_cdev, &mvKernelExt_fops); + + mvKernelExt_cdev.owner = THIS_MODULE; + + result = cdev_add(&mvKernelExt_cdev, + MKDEV(mvKernelExt_major, mvKernelExt_minor), 1); + if (result) + { + unregister_chrdev_region(MKDEV(mvKernelExt_major, mvKernelExt_minor), 1); + printk("mvKernelExt_init: cdev_add err= %d\n", result); + return result; + } + + printk(KERN_DEBUG "mvKernelExt_major = %d cdev.dev=%x\n", + mvKernelExt_major, mvKernelExt_cdev.dev); + + result = mvKernelExt_init_common(); + if (result) + { + unregister_chrdev_region(MKDEV(mvKernelExt_major, mvKernelExt_minor), 1); + cdev_del(&mvKernelExt_cdev); + return result; + } + +/* code for kernel 2.6 shouldn't be relevant for 3.10 */ +#ifndef CONFIG_OF + /* create proc entry */ + create_proc_read_entry("mvKernelExt", 0, NULL, mvKernelExt_read_proc_mem, NULL); +#endif + + mvKernelExt_initialized = 1; + + printk(KERN_DEBUG "mvKernelExt_init finished\n"); + + return 0; +} + + +module_init(mvKernelExt_init); +module_exit(mvKernelExt_cleanup); Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/2_6/mv_KernelExt.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/2_6/mv_KernelExt.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/2_6/mv_KernelExt.h (working copy) @@ -0,0 +1,159 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************** +* mvKernelExt.h +* +* DESCRIPTION: +* functions in kernel mode special for mainOs. +* External definitions +* +* DEPENDENCIES: +* +* $Revision:8$ +*******************************************************************************/ +#ifndef __mv_KernelExt_h__ +#define __mv_KernelExt_h__ + +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +#define V306PLUS +#endif + +#if defined(CONFIG_ARCH_KIRKWOOD) && defined(V306PLUS) +#define XCAT340 +#define MVKERNELEXT_MAJOR 244 +#endif + +#if defined(V306PLUS) +#define MVKERNELEXT_MAJOR 244 +#endif + +#if defined(CONFIG_X86_64) && defined(CONFIG_X86) +#define INTEL64 +#define INTEL64_CPU +#define MVKERNELEXT_MAJOR 244 +#endif + +#ifndef MVKERNELEXT_MAJOR +# define MVKERNELEXT_MAJOR 254 +#endif +#ifndef MVKERNELEXT_MINOR +# define MVKERNELEXT_MINOR 1 +#endif + +#ifdef __KERNEL__ +/* + * Typedef: mv_waitqueue_t + * + * Description: + * wait queue + * This type defined here because it is close to kernel internals + * + * Fields: + * first - pointer to first item in queue + * last - pointer to last item in queue + * + */ +typedef struct { + struct mv_task *first; + struct mv_task *last; +} mv_waitqueue_t; + +/******************************************************************************* +* translate_priority +* +* DESCRIPTION: +* Translates a v2pthread priority into kernel priority +* +* INPUTS: +* policy - scheduler policy +* priority - vxWorks task priority +* +* OUTPUTS: +* None +* +* RETURNS: +* kernel priority +* +* COMMENTS: +* +*******************************************************************************/ +static int translate_priority( + int policy, + int priority +); + +#ifndef CONFIG_SMP +# define MV_GLOBAL_LOCK() local_irq_disable() +# define MV_GLOBAL_UNLOCK() local_irq_enable() +#else /* CONFIG_SMP */ +# include +# define MV_GLOBAL_LOCK() spin_lock_irq(&mv_giantlock) +# define MV_GLOBAL_UNLOCK() spin_unlock_irq(&mv_giantlock) +#endif /* CONFIG_SMP */ + +#endif /* __KERNEL__ */ + +#include "../common/mv_KernelExt.h" + +#endif /* __mv_KernelExt_h__ */ Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.c (working copy) @@ -0,0 +1,1241 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************** +* mvKernelExt.c +* +* DESCRIPTION: +* functions in kernel mode special for mainOs. +* +* DEPENDENCIES: +* mvKernelExt.h +* +* $Revision: 11$ +*******************************************************************************/ + +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif + +/* task locking data */ +static struct task_struct* mv_tasklock_owner = NULL; +static mv_waitqueue_t mv_tasklock_waitqueue; +static int mv_tasklock_count = 0; +#ifdef MV_TASKLOCK_STAT +static int mv_tasklock_lcount = 0; +static int mv_tasklock_wcount = 0; +#endif + +/* task array */ +static int mv_max_tasks = MV_MAX_TASKS; +static struct mv_task** mv_tasks = NULL; +static struct mv_task* mv_tasks_alloc = NULL; +static int mv_num_tasks = 0; + +module_param(mv_max_tasks, int, S_IRUGO); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,6) +#define V306PLUS +#endif + +/************************************************************************ + * + * support functions + * + ************************************************************************/ +#ifndef MVKERNELEXT_TASK_STRUCT +/******************************************************************************* +* gettask +* +* DESCRIPTION: +* Search for a mv_task by pointer to kernel's task structure +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* NULL if task is not registered yet +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static struct mv_task* gettask( + struct task_struct *tsk +) +{ + int k; + + for (k = 0; k < mv_num_tasks; k++) + if (mv_tasks[k]->task == tsk) + return mv_tasks[k]; + + return NULL; +} +#endif + +#ifdef MVKERNELEXT_TASK_STRUCT +static void mv_cleanup(struct task_struct *tsk); +#endif + +/******************************************************************************* +* gettask_cr +* +* DESCRIPTION: +* Search for a mv_task by pointer to kernel's task structure +* Register task in array if task was not registered yet +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* NULL if task cannot be registered (task array is full) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static struct mv_task* gettask_cr( + struct task_struct *tsk +) +{ + struct mv_task *p; + + p = gettask(tsk); + if (unlikely(p == NULL)) + { + if (mv_num_tasks >= mv_max_tasks) + return NULL; + p = mv_tasks[mv_num_tasks++]; + memset(p, 0, sizeof(*p)); + p->task = tsk; +#ifdef MVKERNELEXT_TASK_STRUCT + tsk->mv_ptr = p; + tsk->mv_cleanup = mv_cleanup; +#endif + } + return p; +} + +/************************************************************************ + * + * task locking + * + ************************************************************************/ +/******************************************************************************* +* mvKernelExt_TaskLock +* +* DESCRIPTION: +* lock scheduller to current task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_TaskLock(struct task_struct* tsk) +{ + struct mv_task *p; + + MV_GLOBAL_LOCK(); + + p = gettask_cr(tsk); + if (unlikely(p == NULL)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ENOMEM; + } + + if (mv_tasklock_owner && mv_tasklock_owner != tsk) + { +#ifdef MV_TASKLOCK_STAT + mv_tasklock_wcount++; + p->tasklock_wcount++; +#endif + do { + if (mv_do_short_wait_on_queue(&mv_tasklock_waitqueue, p, &mv_tasklock_owner)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + } while (mv_tasklock_owner); + } + +#ifdef MV_TASKLOCK_STAT + mv_tasklock_lcount++; + p->tasklock_lcount++; +#endif + mv_tasklock_owner = tsk; + mv_tasklock_count++; + tsk->state = TASK_RUNNING; + MV_GLOBAL_UNLOCK(); + + return 0; +} + +/** + * sys_mv_taskunlock - unlock scheduller from current task + */ +/******************************************************************************* +* mvKernelExt_TaskUnlock +* +* DESCRIPTION: +* unlock scheduller from current task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* force - force unlock, reset recursion counter +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EPERM - locked by enother task +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_TaskUnlock(struct task_struct* tsk, int force) +{ + MV_GLOBAL_LOCK(); + if (unlikely(mv_tasklock_owner != tsk)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EPERM; + } + + if (force) + mv_tasklock_count = 0; + else + mv_tasklock_count--; + + if (mv_tasklock_count > 0) + { + MV_GLOBAL_UNLOCK(); + return 0; + } + + mv_waitqueue_wake_all(&mv_tasklock_waitqueue); + + mv_tasklock_owner = NULL; + MV_GLOBAL_UNLOCK(); + + return 0; +} + +/******************************************************************************* +* mv_registertask +* +* DESCRIPTION: +* Register current task. Store name for this task +* +* INPUTS: +* taskinfo - pointer to task info: name priority, etc +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_ENOMEM - current task is not yet registered +* and task array is full +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int mv_registertask(mv_registertask_stc* taskinfo) +{ + struct task_struct *curr; + struct mv_task *p; + + curr = current; + + MV_GLOBAL_LOCK(); + + p = gettask_cr(curr); + if (unlikely(p == NULL)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ENOMEM; + } + + memcpy(p->name, taskinfo->name, MV_THREAD_NAME_LEN); + p->name[MV_THREAD_NAME_LEN] = 0; + p->vxw_priority = taskinfo->vxw_priority; + p->pthread_id = taskinfo->pthread_id; + + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* mv_unregistertask +* +* DESCRIPTION: +* Unregister task. Unlock mutexes locked by task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* Non zero if task was not registered +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static int mv_unregistertask(struct task_struct* tsk) +{ + struct mv_task* p = NULL; + int k; + +#ifdef MVKERNELEXT_TASK_STRUCT + tsk->mv_ptr = NULL; + tsk->mv_cleanup = NULL; +#endif + + mvKernelExt_SemUnlockMutexes(tsk); + + if (mv_tasklock_owner == tsk) + { + mv_tasklock_count = 0; + mv_waitqueue_wake_all(&mv_tasklock_waitqueue); + mv_tasklock_owner = NULL; + } + + for (k = 0; k < mv_num_tasks; k++) + if (mv_tasks[k]->task == tsk) + { + p = mv_tasks[k]; + break; + } + + if (p == NULL) + return 1; + + mv_num_tasks--; + if (mv_num_tasks > k) /* task was not the last in array */ + { + mv_tasks[k] = mv_tasks[mv_num_tasks]; + mv_tasks[mv_num_tasks] = p; + } + + mv_delete_from_waitqueue(p); + + return 0; +} + +#ifdef MVKERNELEXT_TASK_STRUCT +/******************************************************************************* +* mv_cleanup +* +* DESCRIPTION: +* Execute cleanup actions when task finished +* Called from kernel's do_exit() if patch applied to kernel +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static void mv_cleanup(struct task_struct *tsk) +{ + MV_GLOBAL_LOCK(); + mv_unregistertask(tsk); + MV_GLOBAL_UNLOCK(); +} +#endif + +/******************************************************************************* +* mv_unregister_current_task +* +* DESCRIPTION: +* Unregister current task +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int mv_unregister_current_task(void) +{ + struct task_struct *curr; + + curr = current; + + MV_GLOBAL_LOCK(); + + mv_unregistertask(curr); + + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* get_task_by_id +* +* DESCRIPTION: +* Search task by task ID +* +* INPUTS: +* tid - Task ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* Null if task not found +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static struct mv_task* get_task_by_id(int tid) +{ + int k; + + if (tid == 0) + return gettask(current); + + for (k = 0; k < mv_num_tasks; k++) + if (mv_tasks[k]->task->pid == tid) + return mv_tasks[k]; + + return NULL; +} + +/******************************************************************************* +* mv_get_pthrid +* +* DESCRIPTION: +* Return pthread ID for task +* Pthread ID associated with task in mv_registertask +* +* INPUTS: +* param->taskid - task ID +* +* OUTPUTS: +* param->pthread_id - associated pthread ID +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_get_pthrid(mv_get_pthrid_stc *param) +{ + struct mv_task* p; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(param->taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + param->pthread_id = p->pthread_id; + + return 0; +} + +/******************************************************************************* +* mv_get_prio +* +* DESCRIPTION: +* Set task priority +* +* INPUTS: +* param->taskid - task ID +* +* OUTPUTS: +* param->vxw_priority - vxWorks task priority +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_get_prio(mv_priority_stc *param) +{ + struct mv_task* p; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(param->taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + param->vxw_priority = p->vxw_priority; + + return 0; +} + +/******************************************************************************* +* mv_suspend +* +* DESCRIPTION: +* Suspend task execution +* +* INPUTS: +* taskid - task ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_suspend(int taskid) +{ + struct mv_task* p; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + if (p->task == current) + mvKernelExt_TaskUnlock(p->task, 1); + + send_sig(SIGSTOP, p->task, 1); + + return 0; +} + +/******************************************************************************* +* mv_resume +* +* DESCRIPTION: +* Resume task execution +* +* INPUTS: +* taskid - task ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_resume(int taskid) +{ + struct mv_task* p; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + send_sig(SIGCONT, p->task, 1); + + return 0; +} + +/******************************************************************************* +* mv_delete +* +* DESCRIPTION: +* Destroy task +* +* INPUTS: +* taskid - task ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_delete(int taskid) +{ + struct mv_task* p; + struct task_struct *tsk; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + tsk = p->task; + + MV_GLOBAL_LOCK(); + mv_unregistertask(tsk); + MV_GLOBAL_UNLOCK(); + + send_sig(SIGRTMIN/*SIGCANCEL*/, tsk, 1); + return 0; +} + +/******************************************************************************* +* mvKernelExt_ioctl +* +* DESCRIPTION: +* The device ioctl() implementation +* +* INPUTS: +* inode - unused +* filp - unused +* cmd - Ioctl number +* arg - parameter +* +* OUTPUTS: +* Depends on cmd +* +* RETURNS: +* -ENOTTY - wrong ioctl magic key +* Depends on cmd +* +* COMMENTS: +* +*******************************************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) +static long mvKernelExt_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg +) +#else +static int mvKernelExt_ioctl( + struct inode *inode, + struct file *filp, + unsigned int cmd, + unsigned long arg +) +#endif +{ + /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */ + if (unlikely(_IOC_TYPE(cmd) != MVKERNELEXT_IOC_MAGIC)) + { + printk("wrong ioctl magic key\n"); + return -ENOTTY; + } + + /* GETTING DATA */ + switch(cmd) + { + case MVKERNELEXT_IOC_NOOP: + return 0; + + case MVKERNELEXT_IOC_TASKLOCK: + return mvKernelExt_TaskLock(current); + + case MVKERNELEXT_IOC_TASKUNLOCK: + return mvKernelExt_TaskUnlock(current, 0); + + case MVKERNELEXT_IOC_TASKUNLOCKFORCE: + return mvKernelExt_TaskUnlock(current, 1); + + case MVKERNELEXT_IOC_REGISTER: + { + mv_registertask_stc lparam; + if (copy_from_user(&lparam, + (mv_registertask_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return mv_registertask(&lparam); + } + case MVKERNELEXT_IOC_UNREGISTER: + return mv_unregister_current_task(); + + case MVKERNELEXT_IOC_SET_PRIO: + { + mv_priority_stc lparam; + + if (copy_from_user(&lparam, + (mv_priority_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return mv_set_prio(&lparam); + } + case MVKERNELEXT_IOC_GET_PRIO: + { + mv_priority_stc lparam; + int retval; + + if (copy_from_user(&lparam, + (mv_priority_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + retval = mv_get_prio(&lparam); + + if (copy_to_user((mv_priority_stc*)arg, + &lparam, sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return retval; + } + + case MVKERNELEXT_IOC_SUSPEND: + return mv_suspend(arg); + case MVKERNELEXT_IOC_RESUME: + return mv_resume(arg); + + case MVKERNELEXT_IOC_GET_PTHRID: + { + mv_get_pthrid_stc lparam; + int retval; + + if (copy_from_user(&lparam, + (mv_get_pthrid_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + retval = mv_get_pthrid(&lparam); + + if (copy_to_user((mv_get_pthrid_stc*)arg, + &lparam, sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return retval; + } + + case MVKERNELEXT_IOC_DELETE: + return mv_delete(arg); + + case MVKERNELEXT_IOC_SEMCREATE: + { + mv_sem_create_stc lparam; + if (copy_from_user(&lparam, + (mv_sem_create_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return mvKernelExt_SemCreate(lparam.flags, lparam.name); + } + case MVKERNELEXT_IOC_SEMDELETE: + return mvKernelExt_SemDelete(arg); + case MVKERNELEXT_IOC_SEMSIGNAL: + return mvKernelExt_SemSignal(arg); + case MVKERNELEXT_IOC_SEMWAIT: + return mvKernelExt_SemWait(arg); + case MVKERNELEXT_IOC_SEMTRYWAIT: + return mvKernelExt_SemTryWait(arg); + case MVKERNELEXT_IOC_SEMWAITTMO: + { + mv_sem_timedwait_stc lparam; + if (copy_from_user(&lparam, + (mv_sem_timedwait_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return mvKernelExt_SemWaitTimeout(lparam.semid, lparam.timeout); + } + + case MVKERNELEXT_IOC_TEST: + printk("mvKernelExt_TEST()\n"); + return 0; + + case MVKERNELEXT_IOC_MSGQCREATE: + { + mv_msgq_create_stc lparam; + if (copy_from_user(&lparam, + (mv_msgq_create_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + return mvKernelExt_MsgQCreate( + lparam.name, + lparam.maxMsgs, + lparam.maxMsgSize); + } + case MVKERNELEXT_IOC_MSGQDELETE: + return mvKernelExt_MsgQDelete(arg); + case MVKERNELEXT_IOC_MSGQSEND: + { + mv_msgq_sr_stc lparam; + if (copy_from_user(&lparam, + (mv_msgq_sr_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + return mvKernelExt_MsgQSend( + lparam.msgqId, + lparam.message, + lparam.messageSize, + lparam.timeOut, + 1/*from userspace*/); + } + case MVKERNELEXT_IOC_MSGQRECV: + { + mv_msgq_sr_stc lparam; + if (copy_from_user(&lparam, + (mv_msgq_sr_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + return mvKernelExt_MsgQRecv( + lparam.msgqId, + lparam.message, + lparam.messageSize, + lparam.timeOut, + 1/*to userspace*/); + } + case MVKERNELEXT_IOC_MSGQNUMMSGS: + return mvKernelExt_MsgQNumMsgs(arg); + + default: + printk (KERN_WARNING "Unknown ioctl (%x).\n", cmd); + break; + } + return 0; +} + + +/******************************************************************************* +* mvKernelExt_open +* +* DESCRIPTION: +* The device open() implementation +* +* INPUTS: +* inode - unused +* filp - unused +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -EIO - module uninitialized +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_open( + struct inode * inode, + struct file * filp +) +{ + if (!mvKernelExt_initialized) + { + return -EIO; + } + + filp->private_data = NULL; + + mv_check_tasks(); + + MV_GLOBAL_LOCK(); + mvKernelExt_opened++; + MV_GLOBAL_UNLOCK(); + + return 0; +} + + +/******************************************************************************* +* mvKernelExt_release +* +* DESCRIPTION: +* The device close() implementation +* +* INPUTS: +* inode - unused +* filp - unused +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_release( + struct inode * inode, + struct file * file +) +{ + printk("mvKernelExt_release\n"); + + /*!!! check task list */ + mv_check_tasks(); + + MV_GLOBAL_LOCK(); + mv_unregistertask(current); + mvKernelExt_opened--; + if (mvKernelExt_opened == 0) + { + mvKernelExt_DeleteAll(); + mvKernelExt_DeleteAllMsgQ(); + mv_tasklock_owner = NULL; + mv_tasklock_count = 0; + /* clean statistics */ +#ifdef MV_TASKLOCK_STAT + mv_tasklock_lcount = 0; + mv_tasklock_wcount = 0; +#endif + } + MV_GLOBAL_UNLOCK(); + + return 0; +} + + + + +static struct file_operations mvKernelExt_fops = +{ + .llseek = mvKernelExt_lseek, + .read = mvKernelExt_read, + .write = mvKernelExt_write, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + .unlocked_ioctl = mvKernelExt_ioctl, +#else + .ioctl = mvKernelExt_ioctl, +#endif + .open = mvKernelExt_open, + .release= mvKernelExt_release /* A.K.A close */ +}; + +#ifdef MVKERNELEXT_SYSCALLS +/************************************************************************ + * + * syscall entries for fast calls + * + ************************************************************************/ +/* fast call to KernelExt ioctl */ +asmlinkage long sys_mv_ctl(unsigned int cmd, unsigned long arg) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + return mvKernelExt_ioctl(NULL, cmd, arg); +#else + return mvKernelExt_ioctl(NULL, NULL, cmd, arg); +#endif +} + +extern long sys_call_table[]; + +#define OWN_SYSCALLS 1 + +#ifdef __NR_SYSCALL_BASE +# define __SYSCALL_TABLE_INDEX(name) (__NR_##name-__NR_SYSCALL_BASE) +#else +# define __SYSCALL_TABLE_INDEX(name) (__NR_##name) +#endif + +#define __TBL_ENTRY(name) { __SYSCALL_TABLE_INDEX(name), (long)sys_##name, 0 } +static struct { + int entry_number; + long own_entry; + long saved_entry; +} override_syscalls[OWN_SYSCALLS] = { + __TBL_ENTRY(mv_ctl) +}; +#undef __TBL_ENTRY + + +/******************************************************************************* +* mv_OverrideSyscalls +* +* DESCRIPTION: +* Override entries in syscall table. +* Store original pointers in array. +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int +mv_OverrideSyscalls(void) +{ + int k; + for (k = 0; k < OWN_SYSCALLS; k++) + { + override_syscalls[k].saved_entry = + sys_call_table[override_syscalls[k].entry_number]; + sys_call_table[override_syscalls[k].entry_number] = + override_syscalls[k].own_entry; + } + return 0; +} + +/******************************************************************************* +* mv_RestoreSyscalls +* +* DESCRIPTION: +* Restore original syscall entries. +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int +mv_RestoreSyscalls(void) +{ + int k; + for (k = 0; k < OWN_SYSCALLS; k++) + { + if (override_syscalls[k].saved_entry) + sys_call_table[override_syscalls[k].entry_number] = + override_syscalls[k].saved_entry; + } + return 0; +} +#endif /* MVKERNELEXT_SYSCALLS */ + + +/************************************************************************ +* mvKernelExt_cleanup_common +* +* DESCRIPTION: +* Perform cleanup actions while module unloading +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_cleanup_common(void) +{ +#ifdef MVKERNELEXT_TASK_STRUCT + int k; +#endif + +#ifdef MVKERNELEXT_SYSCALLS + mv_RestoreSyscalls(); +#endif + +#ifdef MVKERNELEXT_TASK_STRUCT + MV_GLOBAL_LOCK(); + for (k = 0; k < mv_num_tasks; k++) + { + mv_tasks[k]->task->mv_ptr = NULL; + mv_tasks[k]->task->mv_cleanup = NULL; + } + MV_GLOBAL_UNLOCK(); +#endif + + mvKernelExt_SemCleanup(); + mvKernelExt_MsgQCleanup(); + + if (mv_tasks) + kfree(mv_tasks); + mv_tasks = NULL; + + if (mv_tasks_alloc) + kfree(mv_tasks_alloc); + mv_tasks_alloc = NULL; + mv_waitqueue_cleanup(&mv_tasklock_waitqueue); + +} + +/************************************************************************ +* mvKernelExt_init_common +* +* DESCRIPTION: +* Module initialization +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -ENOMEM if failed to allocate memory for tasks/semaphores +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_init_common(void) +{ + int result = 0; + + /* allocate task array for tasklock */ + mv_tasks_alloc = (struct mv_task*) kmalloc(sizeof(struct mv_task) * mv_max_tasks, GFP_KERNEL); + mv_tasks = (struct mv_task**) kmalloc(sizeof(struct mv_task*) * mv_max_tasks, GFP_KERNEL); + if (mv_tasks_alloc == NULL || mv_tasks == NULL) + { + if (mv_tasks) + kfree(mv_tasks); + if (mv_tasks_alloc) + kfree(mv_tasks_alloc); + mv_tasks = NULL; + mv_tasks_alloc = NULL; + result = -ENOMEM; + printk("mvKernelExt_init: unable to allocate task array\n"); + return result; + } + for (result = 0; result < mv_max_tasks; result++) + mv_tasks[result] = &(mv_tasks_alloc[result]); + mv_waitqueue_init(&mv_tasklock_waitqueue); + + if (!mvKernelExt_SemInit()) + { + mvKernelExt_cleanup_common(); + result = -ENOMEM; + return result; + } + + if (!mvKernelExt_MsgQInit()) + { + mvKernelExt_cleanup_common(); + result = -ENOMEM; + return result; + } + +#ifdef MVKERNELEXT_SYSCALLS + mv_OverrideSyscalls(); +#endif + + return 0; +} + +EXPORT_SYMBOL(mvKernelExt_TaskLock); +EXPORT_SYMBOL(mvKernelExt_TaskUnlock); Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.h (working copy) @@ -0,0 +1,1103 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************** +* mvKernelExt.h +* +* DESCRIPTION: +* functions in kernel mode special for mainOs. +* External definitions +* +* DEPENDENCIES: +* It is assumed that mv_waitqueue_t defined before this file included +* +* $Revision: 7.*******************************************************************************/ + + + +#define MV_MAX_TASKS 40 +#define MV_THREAD_NAME_LEN 16 + +#define MV_SEMAPHORES_MIN 32 +#ifndef LINUX_SIM +# define MV_SEMAPHORES_DEF 128 +#else +# define MV_SEMAPHORES_DEF 1024 +#endif + +#define MV_SEMAPTHORE_F_MTX 0x80000000 +#define MV_SEMAPTHORE_F_COUNT 0x40000000 +#define MV_SEMAPTHORE_F_BINARY 0x20000000 +#define MV_SEMAPTHORE_F_TYPE_MASK 0xe0000000 +#define MV_SEMAPTHORE_F_OPENEXIST 0x10000000 +#define MV_SEMAPTHORE_F_FLAGS_MASK 0x10000000 +#define MV_SEMAPTHORE_F_COUNT_MASK 0x0fffffff + + + +#define MV_QUEUES_MIN 32 +#define MV_QUEUES_DEF 32 + + + + +typedef struct { + char name[MV_THREAD_NAME_LEN]; + int vxw_priority; + unsigned long int pthread_id; +} mv_registertask_stc; + +#define MV_SEM_NAME_LEN 16 +typedef struct { + int flags; + char name[MV_SEM_NAME_LEN]; +} mv_sem_create_stc; + +typedef struct { + int semid; + unsigned long timeout; +} mv_sem_timedwait_stc; + +typedef struct { + int flags; + char name[MV_SEM_NAME_LEN]; +} mv_sem_opennamed_stc; + +typedef struct { + int taskid; + int vxw_priority; +} mv_priority_stc; + +typedef struct { + int taskid; + unsigned long int pthread_id; +} mv_get_pthrid_stc; + +#define MV_MSGQ_NAME_LEN 16 +typedef struct { + char name[MV_SEM_NAME_LEN]; + int maxMsgs; + int maxMsgSize; +} mv_msgq_create_stc; + +typedef struct { + int msgqId; + void* message; + int messageSize; + unsigned long timeOut; +} mv_msgq_sr_stc; + + +/******************************************************** + * + * Error codes + * + ********************************************************/ +#define MVKERNELEXT_EINTR 2 +#define MVKERNELEXT_EPERM 3 +#define MVKERNELEXT_EINVAL 4 +#define MVKERNELEXT_ENOMEM 5 +#define MVKERNELEXT_EDELETED 6 +#define MVKERNELEXT_ETIMEOUT 7 +#define MVKERNELEXT_EBUSY 8 +#define MVKERNELEXT_ECONFLICT 9 +#define MVKERNELEXT_EEMPTY 10 +#define MVKERNELEXT_EFULL 11 + + + +/******************************************************** + * + * IOCTL numbers + * + ********************************************************/ +#define MVKERNELEXT_IOC_MAGIC 'k' +#define MVKERNELEXT_IOC_NOOP _IO(MVKERNELEXT_IOC_MAGIC, 0) +#define MVKERNELEXT_IOC_TASKLOCK _IO(MVKERNELEXT_IOC_MAGIC, 1) +#define MVKERNELEXT_IOC_TASKUNLOCK _IO(MVKERNELEXT_IOC_MAGIC, 2) +#define MVKERNELEXT_IOC_TASKUNLOCKFORCE _IO(MVKERNELEXT_IOC_MAGIC, 3) +#define MVKERNELEXT_IOC_REGISTER _IOW(MVKERNELEXT_IOC_MAGIC, 4, mv_registertask_stc) +#define MVKERNELEXT_IOC_UNREGISTER _IO(MVKERNELEXT_IOC_MAGIC, 5) +#define MVKERNELEXT_IOC_SEMCREATE _IOW(MVKERNELEXT_IOC_MAGIC, 6, mv_sem_create_stc) +#define MVKERNELEXT_IOC_SEMDELETE _IOW(MVKERNELEXT_IOC_MAGIC, 7, long) +#define MVKERNELEXT_IOC_SEMSIGNAL _IOW(MVKERNELEXT_IOC_MAGIC, 8, long) +#define MVKERNELEXT_IOC_SEMWAIT _IOW(MVKERNELEXT_IOC_MAGIC, 9, long) +#define MVKERNELEXT_IOC_SEMTRYWAIT _IOW(MVKERNELEXT_IOC_MAGIC, 10, long) +#define MVKERNELEXT_IOC_SEMWAITTMO _IOW(MVKERNELEXT_IOC_MAGIC, 11, mv_sem_timedwait_stc) +#define MVKERNELEXT_IOC_TEST _IO(MVKERNELEXT_IOC_MAGIC, 12) +#define MVKERNELEXT_IOC_SET_PRIO _IOW(MVKERNELEXT_IOC_MAGIC, 13, mv_priority_stc) +#define MVKERNELEXT_IOC_GET_PRIO _IOW(MVKERNELEXT_IOC_MAGIC, 14, mv_priority_stc) +#define MVKERNELEXT_IOC_SUSPEND _IOW(MVKERNELEXT_IOC_MAGIC, 15, long) +#define MVKERNELEXT_IOC_RESUME _IOW(MVKERNELEXT_IOC_MAGIC, 16, long) +#define MVKERNELEXT_IOC_DELETE _IOW(MVKERNELEXT_IOC_MAGIC, 17, long) +#define MVKERNELEXT_IOC_GET_PTHRID _IOW(MVKERNELEXT_IOC_MAGIC, 18, mv_get_pthrid_stc) + +#define MVKERNELEXT_IOC_MSGQCREATE _IOW(MVKERNELEXT_IOC_MAGIC, 19, mv_msgq_create_stc) +#define MVKERNELEXT_IOC_MSGQDELETE _IOW(MVKERNELEXT_IOC_MAGIC, 20, long) +#define MVKERNELEXT_IOC_MSGQSEND _IOW(MVKERNELEXT_IOC_MAGIC, 21, mv_msgq_sr_stc) +#define MVKERNELEXT_IOC_MSGQRECV _IOW(MVKERNELEXT_IOC_MAGIC, 22, mv_msgq_sr_stc) +#define MVKERNELEXT_IOC_MSGQNUMMSGS _IOW(MVKERNELEXT_IOC_MAGIC, 23, long) + + +#ifdef MVKERNELEXT_SYSCALLS +/******************************************************** + * + * Syscall numbers for + * + * long mv_ctl(unsigned int cmd, unsigned long param) + * + ********************************************************/ +#if 0 +# define __NR_mv_tasklock __NR_setxattr +# define __NR_mv_taskunlock __NR_getxattr +# define __NR_mv_taskunlockforce __NR_listxattr +# define __NR_mv_sem_wait __NR_removexattr +# define __NR_mv_sem_trywait __NR_lsetxattr +# define __NR_mv_sem_wait_tmo __NR_lgetxattr +# define __NR_mv_sem_signal __NR_llistxattr +# define __NR_mv_noop __NR_lremovexattr +#endif +# define __NR_mv_ctl __NR_fsetxattr +#endif + + +#ifdef __KERNEL__ + +#define MV_TASKLOCK_STAT + +/* + * Typedef: struct mv_task + * + * Description: per-task structure + * + * Fields: + * task - pointer to kernel's task structure + * tasklockflag - task suspended flag: + * 0 - task running + * 1 - task suspended for a short period + * 2 - task suspended for a long period + * name - task name. Useful for debugging purposes only + * vxw_priority - vxWorks priority value (untranslated priority) + * pthread_id - value for pthread_t pointer (userspace threads) + * waitqueue - queue task suspended in + * wait_next - next task in wait queue + * + */ +struct mv_task { + struct task_struct *task; + int tasklockflag; + char name[MV_THREAD_NAME_LEN+1]; + int vxw_priority; + unsigned long int pthread_id; + mv_waitqueue_t *waitqueue; + struct mv_task *wait_next; +#ifdef MV_TASKLOCK_STAT + int tasklock_lcount; + int tasklock_wcount; +#endif +}; + + +/***** Static function declarations ************************************/ + +/************************************************************************ +* +* waitqueue support functions +* +* These functions required to suspend thread till some event occurs +************************************************************************/ + +/******************************************************************************* +* mv_waitqueue_init +* +* DESCRIPTION: +* Initialize wait queue structure +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static void mv_waitqueue_init( + mv_waitqueue_t* queue +); + +/******************************************************************************* +* mv_waitqueue_cleanup +* +* DESCRIPTION: +* Cleanup wait queue structure +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static void mv_waitqueue_cleanup( + mv_waitqueue_t* queue +); + +/******************************************************************************* +* mv_waitqueue_add +* +* DESCRIPTION: +* add task to wait queue +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_waitqueue_add( + mv_waitqueue_t* queue, + struct mv_task* tsk +); + +/******************************************************************************* +* mv_waitqueue_wake_first +* +* DESCRIPTION: +* wakeup first task waiting in queue +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_waitqueue_wake_first( + mv_waitqueue_t* queue +); + +/******************************************************************************* +* mv_waitqueue_wake_all +* +* DESCRIPTION: +* wakeup all tasks waiting in queue +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_waitqueue_wake_all( + mv_waitqueue_t* queue +); + +/******************************************************************************* +* mv_delete_from_waitqueue +* +* DESCRIPTION: +* remove task from wait queue +* +* INPUTS: +* tsk - pointer to task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_delete_from_waitqueue( + struct mv_task* tsk +); + +/******************************************************************************* +* mv_do_short_wait_on_queue +* +* DESCRIPTION: +* Suspend a task on wait queue for a short period. +* Function has a best performance in a cost of CPU usage +* This is useful for mutual exclusion semaphores +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* owner - resourse owner +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if wait successful +* Non zero if wait interrupted (signal caught) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static int mv_do_short_wait_on_queue( + mv_waitqueue_t* queue, + struct mv_task* tsk, + struct task_struct** owner +); + +/******************************************************************************* +* mv_do_wait_on_queue +* +* DESCRIPTION: +* Suspend a task on wait queue. +* Function has the same performance as mv_do_short_wait_on_queue when +* task suspended for short period. After that task state changed to +* suspended +* This is useful for binary and counting semaphores +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if wait successful +* Non zero if wait interrupted (signal caught) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static int mv_do_wait_on_queue( + mv_waitqueue_t* queue, + struct mv_task* tsk +); + +/******************************************************************************* +* mv_do_wait_on_queue_timeout +* +* DESCRIPTION: +* Suspend a task on wait queue. +* Return if timer expited. +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* timeout - timeout in scheduller ticks +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if wait successful +* Zero if timeout occured +* -1 if wait interrupted (signal caught) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static unsigned long mv_do_wait_on_queue_timeout( + mv_waitqueue_t* queue, + struct mv_task* tsk, + unsigned long timeout +); + + + + + + +/************************************************************************ +* +* Task lookup functions +* +* These functions required to lookup tasks in task array +************************************************************************/ + +/******************************************************************************* +* mv_check_tasks +* +* DESCRIPTION: +* Walk through task array and check if task still alive +* Perform cleanup actions for dead tasks +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mv_check_tasks(void); + +/******************************************************************************* +* gettask +* +* DESCRIPTION: +* Search for a mv_task by pointer to kernel's task structure +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* NULL if task is not registered yet +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +#ifdef MVKERNELEXT_TASK_STRUCT +#define gettask(tsk) ((struct mv_task*)tsk->mv_ptr); +#else +static struct mv_task* gettask( + struct task_struct *tsk +); +#endif + +/******************************************************************************* +* gettask_cr +* +* DESCRIPTION: +* Search for a mv_task by pointer to kernel's task structure +* Register task in array if task was not registered yet +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* NULL if task cannot be registered (task array is full) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static struct mv_task* gettask_cr( + struct task_struct *tsk +); + + + +/************************************************************************ + * + * task locking + * + ************************************************************************/ +/******************************************************************************* +* mvKernelExt_TaskLock +* +* DESCRIPTION: +* lock scheduller to current task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_TaskLock(struct task_struct* tsk); + +/******************************************************************************* +* mvKernelExt_TaskUnlock +* +* DESCRIPTION: +* unlock scheduller from current task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* force - force unlock, reset recursion counter +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EPERM - locked by enother task +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_TaskUnlock(struct task_struct* tsk, int force); + + + + + + +/******************************************************************************* +* mv_set_prio +* +* DESCRIPTION: +* Set task priority +* +* INPUTS: +* param->taskid - task ID +* param->vxw_priority - vxWorks task priority +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_set_prio(mv_priority_stc *param); + + +/************************************************************************ +* +* Semaphore functions +* +************************************************************************/ + +/******************************************************************************* +* mvKernelExt_SemInit +* +* DESCRIPTION: +* Initialize semaphore support, create /proc for semaphores info +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if successful +* Zero if failed +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_SemInit(void); + +/******************************************************************************* +* mvKernelExt_SemCleanup +* +* DESCRIPTION: +* Perform semaphore cleanup actions before module unload +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_SemCleanup(void); + +/******************************************************************************* +* mvKernelExt_SemCreate +* +* DESCRIPTION: +* Create a new semaphore or open existing one +* +* INPUTS: +* arg - pointer to structure with creation flags and semaphore name +* +* OUTPUTS: +* None +* +* RETURNS: +* Positive value - semaphore ID +* -MVKERNELEXT_EINVAL - invalid parameter passed +* -MVKERNELEXT_ENOMEM - semaphore array is full +* -MVKERNELEXT_ECONFLICT - open existing semaphore with different type +* specified +* +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemCreate(int flags, const char *name); + + +/******************************************************************************* +* mvKernelExt_SemDelete +* +* DESCRIPTION: +* Destroys semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL if bad ID passed +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemDelete(int semid); + +/******************************************************************************* +* mvKernelExt_DeleteAll +* +* DESCRIPTION: +* Destroys all semaphores +* This is safety action which is executed when all tasks closed +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_DeleteAll(void); + +/******************************************************************************* +* mvKernelExt_SemSignal +* +* DESCRIPTION: +* Signals to semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_EPERM - Mutex semaphore is locked by another task +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemSignal(int semid); + +/******************************************************************************* +* mvKernelExt_SemWait +* +* DESCRIPTION: +* Wait for semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemWait(int semid); + +/******************************************************************************* +* mvKernelExt_SemTryWait +* +* DESCRIPTION: +* Try to acquire semaphore without waiting +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_EBUSY - semaphore cannot be taken immediatelly +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemTryWait(int semid); + +/******************************************************************************* +* mvKernelExt_SemWaitTimeout +* +* DESCRIPTION: +* Wait for semaphore +* +* INPUTS: +* semid - semaphore ID +* timeout - timeout in milliseconds +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* -MVKERNELEXT_ETIMEOUT - wait timeout +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemWaitTimeout( + int semid, + unsigned long timeout +); + +/******************************************************************************* +* mvKernelExt_SemUnlockMutexes +* +* DESCRIPTION: +* Unlock all mutexes locked by dead task +* +* INPUTS: +* owner - pointer to kernel's structure of dead task +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_SemUnlockMutexes( + struct task_struct *owner +); + + +/******************************************************************************* +* mvKernelExt_MsgQInit +* +* DESCRIPTION: +* Initialize message queues support, create /proc for queues info +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if successful +* Zero if failed +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_MsgQInit(void); + +/******************************************************************************* +* mvKernelExt_DeleteAllMsgQ +* +* DESCRIPTION: +* Destroys all message queues +* This is safety action which is executed when all tasks closed +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_DeleteAllMsgQ(void); + +/******************************************************************************* +* mvKernelExt_MsgQCleanup +* +* DESCRIPTION: +* Perform message queues cleanup actions before module unload +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_MsgQCleanup(void); + +/******************************************************************************* +* mvKernelExt_MsgQCreate +* +* DESCRIPTION: +* Create a new message queue +* +* INPUTS: +* arg - pointer to structure with creation params and queue name +* +* OUTPUTS: +* None +* +* RETURNS: +* Positive value - queue ID +* -MVKERNELEXT_EINVAL - invalid parameter passed +* -MVKERNELEXT_ENOMEM - queue array is full +* +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQCreate( + const char *name, + int maxMsgs, + int maxMsgSize +); + +/******************************************************************************* +* mvKernelExt_MsgQDelete +* +* DESCRIPTION: +* Destroys semaphore +* +* INPUTS: +* msgqId - queue ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL if bad ID passed +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQDelete(int msgqId); + +/******************************************************************************* +* mvKernelExt_MsgQSend +* +* DESCRIPTION: +* Send message to queue +* +* INPUTS: +* msgqId - Message queue Id +* message - message data pointer +* messageSize - message size +* timeOut - time out in miliseconds or +* -1 for WAIT_FOREVER or 0 for NO_WAIT +* userspace - called from userspace +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ETIMEOUT - on timeout +* -MVKERNELEXT_ENOMEM - full and no wait +* -MVKERNELEXT_EDELETED - deleted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQSend( + int msgqId, + void* message, + int messageSize, + int timeOut, + int userspace +); + +/******************************************************************************* +* mvKernelExt_MsgQRecv +* +* DESCRIPTION: +* Receive message from queue +* +* INPUTS: +* msgqId - Message queue Id +* messageSize - size of buffer pointed by message +* timeOut - time out in miliseconds or +* -1 for WAIT_FOREVER or 0 for NO_WAIT +* userspace - called from userspace +* +* OUTPUTS: +* message - message data pointer +* +* RETURNS: +* message size if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ETIMEOUT - on timeout +* -MVKERNELEXT_ENOMEM - empty and no wait +* -MVKERNELEXT_EDELETED - deleted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQRecv( + int msgqId, + void* message, + int messageSize, + int timeOut, + int userspace +); + +/******************************************************************************* +* mvKernelExt_MsgQNumMsgs +* +* DESCRIPTION: +* Return number of messages pending in queue +* +* INPUTS: +* msgqId - Message queue Id +* +* OUTPUTS: +* None +* +* RETURNS: +* numMessages - number of messages pending in queue +* -MVKERNELEXT_EINVAL - bad ID passed +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_MsgQNumMsgs(int msgqId); + +#endif /* __KERNEL */ Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtMsgQ.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtMsgQ.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtMsgQ.c (working copy) @@ -0,0 +1,734 @@ +/******************************************************************************* +* mv_KervelExtMsgQ.c +* +* DESCRIPTION: +* Message queues +* +* DEPENDENCIES: +* +* FILE REVISION NUMBER: +* $Revision: $ +1*******************************************************************************/ + +#ifdef CONFIG_OF +#include +#endif + + +/************* Defines ********************************************************/ + +#define MV_MSGQ_STAT + +/************ Internal Typedefs ***********************************************/ +typedef struct _mvMsgQ +{ + int flags; + char name[MV_MSGQ_NAME_LEN+1]; + mv_waitqueue_t rxWaitQueue; + mv_waitqueue_t txWaitQueue; + int maxMsgs; + int maxMsgSize; + int messages; + char *buffer; + int head; + int tail; + int waitRx; + int waitTx; +} mvMsgQSTC; + +static mvMsgQSTC *mvMsgQs = NULL; +static int mv_num_queues = MV_QUEUES_DEF; + +module_param(mv_num_queues, int, S_IRUGO); + +/******************************************************************************* +* mvKernelExtMsgQ_read_proc_mem +* +* DESCRIPTION: +* proc read data rooutine. +* Use cat /proc/mvKernelExtMsgQ to show message queue list +* +* INPUTS: +* +* OUTPUTS: +* None +* +* RETURNS: +* Data length +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExtMsgQ_read_proc_mem( + char * page, + char **start, + off_t offset, + int count, + int *eof, + void *data) +{ + int len; + int k; + int begin = 0; + + len = 0; + + len += sprintf(page+len,"id msgs waitRx waitTx"); +#ifdef MV_MSGQ_STAT + /* + len += sprintf(page+len," tcount gcount wcount"); + */ +#endif + len += sprintf(page+len," name\n"); + for (k = 1; k < mv_num_queues; k++) + { + mvMsgQSTC *q; + struct mv_task *p; + + if (!mvMsgQs[k].flags) + continue; + q = mvMsgQs + k; + + len += sprintf(page+len,"%d %d %d %d", + k, q->messages, q->waitRx, q->waitTx); +#ifdef MV_MSGQ_STAT + /* + len += sprintf(page+len," %d %d %d", sem->tcount, sem->gcount, sem->wcount); + */ +#endif + if (q->name[0]) + len += sprintf(page+len," %s", q->name); + page[len++] = '\n'; + + for (p = q->rxWaitQueue.first; p; p = p->wait_next) + len += sprintf(page+len, " rq=%d\n", p->task->pid); + for (p = q->txWaitQueue.first; p; p = p->wait_next) + len += sprintf(page+len, " tq=%d\n", p->task->pid); + + if (len+begin < offset) + { + begin += len; + len = 0; + } + if (len+begin >= offset+count) + break; + } + if (len+begin < offset) + *eof = 1; + offset -= begin; + *start = page + offset; + len -= offset; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} +#ifdef CONFIG_OF +static int proc_status_show_msq(struct seq_file *m, void *v) { + + int len; + int k; + + seq_printf(m, "id msgs waitRx waitTx"); +#ifdef MV_MSGQ_STAT + /* + len += sprintf(page+len," tcount gcount wcount"); + */ +#endif + seq_printf(m, " name\n"); + for (k = 1; k < mv_num_queues; k++) + { + mvMsgQSTC *q; + struct mv_task *p; + + if (!mvMsgQs[k].flags) + continue; + q = mvMsgQs + k; + + seq_printf(m, "%d %d %d %d", + k, q->messages, q->waitRx, q->waitTx); +#ifdef MV_MSGQ_STAT + /* + len += sprintf(page+len," %d %d %d", sem->tcount, sem->gcount, sem->wcount); + */ +#endif + if (q->name[0]) + seq_printf(m, " %s", q->name); + seq_putc(m, '\n'); + + for (p = q->rxWaitQueue.first; p; p = p->wait_next) + seq_printf(m, " rq=%d\n", p->task->pid); + for (p = q->txWaitQueue.first; p; p = p->wait_next) + seq_printf(m, " tq=%d\n", p->task->pid); + + } + + return 0; + +} + +static int proc_status_open_msq(struct inode *inode, struct file *file) +{ + return single_open(file, proc_status_show_msq, PDE_DATA(inode)); +} + +static const struct file_operations mv_kext_msq_read_proc_operations = { + .open = proc_status_open_msq, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +/******************************************************************************* +* mvKernelExt_MsgQInit +* +* DESCRIPTION: +* Initialize message queues support, create /proc for queues info +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if successful +* Zero if failed +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_MsgQInit(void) +{ + if (mv_num_queues < MV_QUEUES_MIN) + mv_num_semaphores = MV_QUEUES_MIN; + + mvMsgQs = (mvMsgQSTC*) kmalloc( + mv_num_queues * sizeof(mvMsgQSTC), GFP_KERNEL); + + if (mvMsgQs == NULL) + { + return 0; + } + + memset(mvMsgQs, 0, mv_num_queues * sizeof(mvMsgQSTC)); + + /* create proc entry */ +#ifdef CONFIG_OF + if (!proc_create("mvKernelExtMsgQ", S_IRUGO, NULL, &mv_kext_msq_read_proc_operations)) + return -ENOMEM; +#else + create_proc_read_entry("mvKernelExtMsgQ", 0, NULL, mvKernelExtMsgQ_read_proc_mem, NULL); +#endif + + return 1; +} + +/******************************************************************************* +* mvKernelExt_DeleteAllMsgQ +* +* DESCRIPTION: +* Destroys all message queues +* This is safety action which is executed when all tasks closed +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_DeleteAllMsgQ(void) +{ + int k; + + for (k = 1; k < mv_num_queues; k++) + { + if (mvMsgQs[k].flags) + { + mv_waitqueue_wake_all(&(mvMsgQs[k].rxWaitQueue)); + mv_waitqueue_wake_all(&(mvMsgQs[k].txWaitQueue)); + mv_waitqueue_cleanup(&(mvMsgQs[k].rxWaitQueue)); + mv_waitqueue_cleanup(&(mvMsgQs[k].txWaitQueue)); + } + mvMsgQs[k].flags = 0; + } +} + +/******************************************************************************* +* mvKernelExt_MsgQCleanup +* +* DESCRIPTION: +* Perform message queues cleanup actions before module unload +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_MsgQCleanup(void) +{ + MV_GLOBAL_LOCK(); + + if (mvMsgQs) + { + mvKernelExt_DeleteAllMsgQ(); + kfree(mvMsgQs); + } + + mvMsgQs = NULL; + + MV_GLOBAL_UNLOCK(); + + remove_proc_entry("mvKernelExtMsgQ", NULL); +} + +/******************************************************************************* +* mvKernelExt_MsgQCreate +* +* DESCRIPTION: +* Create a new message queue +* +* INPUTS: +* arg - pointer to structure with creation params and queue name +* +* OUTPUTS: +* None +* +* RETURNS: +* Positive value - queue ID +* -MVKERNELEXT_EINVAL - invalid parameter passed +* -MVKERNELEXT_ENOMEM - queue array is full +* +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQCreate( + const char *name, + int maxMsgs, + int maxMsgSize +) +{ + int k; + mvMsgQSTC *q = NULL; + + MV_GLOBAL_LOCK(); + + /* create queue */ + for (k = 1; k < mv_num_queues; k++) + { + if (mvMsgQs[k].flags == 0) + break; + } + if (k >= mv_num_queues) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ENOMEM; + } + q = mvMsgQs + k; + + memset(q, 0, sizeof(*q)); + q->flags = 3; + MV_GLOBAL_UNLOCK(); + + /* align max message size by 4 bytes */ + maxMsgSize = (maxMsgSize+3) & ~3; + q->maxMsgs = maxMsgs; + q->maxMsgSize = maxMsgSize; + q->buffer = (char*)kmalloc((maxMsgSize + sizeof(int))*maxMsgs, GFP_KERNEL); + if (q->buffer == NULL) + { + q->flags = 0; + return -MVKERNELEXT_ENOMEM; + } + + MV_GLOBAL_LOCK(); + q->flags = 1; + mv_waitqueue_init(&(q->rxWaitQueue)); + mv_waitqueue_init(&(q->txWaitQueue)); + + strncpy(q->name, name, MV_MSGQ_NAME_LEN); + q->name[MV_MSGQ_NAME_LEN] = 0; + + MV_GLOBAL_UNLOCK(); + return k; +} + +#define MSGQ_BY_ID(msgId) \ + MV_GLOBAL_LOCK(); \ + if (unlikely(msgqId == 0 || msgqId >= mv_num_queues)) \ + { \ +ret_einval: \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_EINVAL; \ + } \ + q = mvMsgQs + msgqId; \ + if (unlikely(q->flags != 1)) \ + goto ret_einval; + +#define CHECK_MSGQ() \ + if (unlikely(q->flags != 1)) \ + { \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_EDELETED; \ + } + +/******************************************************************************* +* mvKernelExt_MsgQDelete +* +* DESCRIPTION: +* Destroys semaphore +* +* INPUTS: +* msgqId - queue ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL if bad ID passed +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQDelete(int msgqId) +{ + mvMsgQSTC *q; + int timeOut; + + MSGQ_BY_ID(msgqId); + + q->flags = 2; /* deleting */ + + for (timeOut = HZ; q->waitRx && timeOut; timeOut--) + { + mv_waitqueue_wake_all(&(q->rxWaitQueue)); + if (q->waitRx) + { + MV_GLOBAL_UNLOCK(); + schedule_timeout(1); + MV_GLOBAL_LOCK(); + } + } + for (timeOut = HZ; q->waitTx && timeOut; timeOut--) + { + mv_waitqueue_wake_all(&(q->txWaitQueue)); + if (q->waitTx) + { + MV_GLOBAL_UNLOCK(); + schedule_timeout(1); + MV_GLOBAL_LOCK(); + } + } + + mv_waitqueue_cleanup(&(q->rxWaitQueue)); + mv_waitqueue_cleanup(&(q->txWaitQueue)); + + MV_GLOBAL_UNLOCK(); + kfree(q->buffer); + + q->flags = 0; + + return 0; +} + +/******************************************************************************* +* mvKernelExt_MsgQSend +* +* DESCRIPTION: +* Send message to queue +* +* INPUTS: +* msgqId - Message queue Id +* message - message data pointer +* messageSize - message size +* timeOut - time out in miliseconds or +* -1 for WAIT_FOREVER or 0 for NO_WAIT +* userspace - called from userspace +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ETIMEOUT - on timeout +* -MVKERNELEXT_ENOMEM - full and no wait +* -MVKERNELEXT_EDELETED - deleted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQSend( + int msgqId, + void* message, + int messageSize, + int timeOut, + int userspace +) +{ + char *msg; + mvMsgQSTC *q; + + MSGQ_BY_ID(msgqId); + + while (q->messages == q->maxMsgs) + { + /* queue full */ + if (timeOut == 0) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EFULL; /* ??? -MVKERNELEXT_ETIMEOUT */ + } + else + { + TASK_WILL_WAIT(current); + q->waitTx++; + if (timeOut != -1) + { +#if HZ != 1000 + timeOut += 1000 / HZ - 1; + timeOut /= 1000 / HZ; +#endif + timeOut = mv_do_wait_on_queue_timeout(&(q->txWaitQueue), p, timeOut); + CHECK_MSGQ(); + if (timeOut == 0) + { + q->waitTx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ETIMEOUT; + } + if (timeOut == (unsigned long)(-1)) + { + q->waitTx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + } + else /* timeOut == -1, wait forever */ + { + if (unlikely(mv_do_wait_on_queue(&(q->txWaitQueue), p))) + { + q->waitTx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + CHECK_MSGQ(); + } + q->waitTx--; + } + } + + /* put message */ + msg = q->buffer + q->head * (q->maxMsgSize + sizeof(int)); + if (messageSize > q->maxMsgSize) + messageSize = q->maxMsgSize; + + *((int*)msg) = messageSize; + if (userspace) + { + if (copy_from_user(msg+sizeof(int), message, messageSize)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINVAL; + } + } + else + { + memcpy(msg+sizeof(int), message, messageSize); + + } + q->head++; + if (q->head >= q->maxMsgs) /* round up */ + q->head = 0; + q->messages++; + + /* signal to Recv thread if any */ + if (q->waitRx) + { + mv_waitqueue_wake_first(&(q->rxWaitQueue)); + /* + if (unlikely(!q->rxWaitQueue.first)) + q->waitRx = 0; + */ + } + + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* mvKernelExt_MsgQRecv +* +* DESCRIPTION: +* Receive message from queue +* +* INPUTS: +* msgqId - Message queue Id +* messageSize - size of buffer pointed by message +* timeOut - time out in miliseconds or +* -1 for WAIT_FOREVER or 0 for NO_WAIT +* userspace - called from userspace +* +* OUTPUTS: +* message - message data pointer +* +* RETURNS: +* message size if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ETIMEOUT - on timeout +* -MVKERNELEXT_ENOMEM - empty and no wait +* -MVKERNELEXT_EDELETED - deleted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQRecv( + int msgqId, + void* message, + int messageSize, + int timeOut, + int userspace +) +{ + char *msg; + int msgSize; + mvMsgQSTC *q; + + MSGQ_BY_ID(msgqId); + + while (q->messages == 0) + { + /* queue empty */ + if (timeOut == 0) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EEMPTY; /* ??? -MVKERNELEXT_ETIMEOUT */ + } + else + { + TASK_WILL_WAIT(current); + q->waitRx++; + if (timeOut != -1) + { +#if HZ != 1000 + timeOut += 1000 / HZ - 1; + timeOut /= 1000 / HZ; +#endif + timeOut = mv_do_wait_on_queue_timeout(&(q->rxWaitQueue), p, timeOut); + CHECK_MSGQ(); + if (timeOut == 0) + { + q->waitRx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ETIMEOUT; + } + if (timeOut == (unsigned long)(-1)) + { + q->waitRx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + } + else /* timeOut == -1, wait forever */ + { + if (unlikely(mv_do_wait_on_queue(&(q->rxWaitQueue), p))) + { + q->waitRx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + CHECK_MSGQ(); + } + q->waitRx--; + } + } + /* get message */ + msg = q->buffer + q->tail * (q->maxMsgSize + sizeof(int)); + msgSize = *((int*)msg); + if (msgSize > messageSize) + msgSize = messageSize; + + if (userspace) + { + if (copy_to_user(message, msg+sizeof(int), msgSize)) + { + msgSize = 0; + } + } + else + { + memcpy(message, msg+sizeof(int), msgSize); + } + q->tail++; + if (q->tail >= q->maxMsgs) /* round up */ + q->tail = 0; + q->messages--; + + /* signal to Recv thread if any */ + if (q->waitTx) + { + mv_waitqueue_wake_first(&(q->txWaitQueue)); + /* + if (unlikely(!q->txWaitQueue.first)) + q->waitTx = 0;*/ + } + + MV_GLOBAL_UNLOCK(); + return msgSize; +} + +/******************************************************************************* +* mvKernelExt_MsgQNumMsgs +* +* DESCRIPTION: +* Return number of messages pending in queue +* +* INPUTS: +* msgqId - Message queue Id +* +* OUTPUTS: +* None +* +* RETURNS: +* numMessages - number of messages pending in queue +* -MVKERNELEXT_EINVAL - bad ID passed +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_MsgQNumMsgs(int msgqId) +{ + int numMessages; + mvMsgQSTC *q; + + MSGQ_BY_ID(msgqId); + numMessages = q->messages; + MV_GLOBAL_UNLOCK(); + + return numMessages; +} + +EXPORT_SYMBOL(mvKernelExt_MsgQCreate); +EXPORT_SYMBOL(mvKernelExt_MsgQDelete); +EXPORT_SYMBOL(mvKernelExt_MsgQSend); +EXPORT_SYMBOL(mvKernelExt_MsgQRecv); +EXPORT_SYMBOL(mvKernelExt_MsgQNumMsgs); Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtSem.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtSem.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtSem.c (working copy) @@ -0,0 +1,822 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************** +* mvKernelExtSem.c +* +* DESCRIPTION: +* functions in kernel mode special for mainOs. +* Semaphores implementation +* +* DEPENDENCIES: +* +* $Revision: 6$ +*******************************************************************************/ + +#ifdef CONFIG_OF +#include +#endif + +#define MV_SEM_STAT +typedef struct { + int flags; + int count; + mv_waitqueue_t waitqueue; + struct task_struct *owner; + char name[MV_SEM_NAME_LEN+1]; +#ifdef MV_SEM_STAT + int tcount; + int gcount; + int wcount; +#endif +} mvSemaphoreSTC; + +static mvSemaphoreSTC *mvSemaphores = NULL; +static int mv_num_semaphores = MV_SEMAPHORES_DEF; + +module_param(mv_num_semaphores, int, S_IRUGO); + + +/******************************************************************************* +* mvKernelExtSem_read_proc_mem +* +* DESCRIPTION: +* proc read data rooutine. +* Use cat /proc/mvKernelExtSem to show semaphore list +* +* INPUTS: +* +* OUTPUTS: +* None +* +* RETURNS: +* Data length +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExtSem_read_proc_mem( + char * page, + char **start, + off_t offset, + int count, + int *eof, + void *data) +{ + int len; + int k; + int begin = 0; + + len = 0; + + len += sprintf(page+len,"id type count owner"); +#ifdef MV_SEM_STAT + len += sprintf(page+len," tcount gcount wcount"); +#endif + len += sprintf(page+len," name\n"); + for (k = 1; k < mv_num_semaphores; k++) + { + mvSemaphoreSTC *sem; + struct mv_task *p; + + if (!mvSemaphores[k].flags) + continue; + sem = mvSemaphores + k; + + len += sprintf(page+len,"%d %c %d %d", + k, + (sem->flags & MV_SEMAPTHORE_F_MTX) ? 'M' : + (sem->flags & MV_SEMAPTHORE_F_COUNT) ? 'C' : + (sem->flags & MV_SEMAPTHORE_F_BINARY) ? 'B' : '?', + sem->count, + sem->owner?(sem->owner->pid):0); +#ifdef MV_SEM_STAT + len += sprintf(page+len," %d %d %d", sem->tcount, sem->gcount, sem->wcount); +#endif + if (sem->name[0]) + len += sprintf(page+len," %s", sem->name); + page[len++] = '\n'; + + for (p = sem->waitqueue.first; p; p = p->wait_next) + len += sprintf(page+len, " q=%d\n", p->task->pid); + + if (len+begin < offset) + { + begin += len; + len = 0; + } + if (len+begin >= offset+count) + break; + } + if (len+begin < offset) + *eof = 1; + offset -= begin; + *start = page + offset; + len -= offset; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +#ifdef CONFIG_OF +static int proc_status_show(struct seq_file *m, void *v) { + int k; + + seq_printf(m, "id type count owner"); +#ifdef MV_SEM_STAT + seq_printf(m, " tcount gcount wcount"); +#endif + seq_printf(m, " name\n"); + for (k = 1; k < mv_num_semaphores; k++) + { + mvSemaphoreSTC *sem; + struct mv_task *p; + + if (!mvSemaphores[k].flags) + continue; + sem = mvSemaphores + k; + + seq_printf(m, "%d %c %d %d", + k, + (sem->flags & MV_SEMAPTHORE_F_MTX) ? 'M' : + (sem->flags & MV_SEMAPTHORE_F_COUNT) ? 'C' : + (sem->flags & MV_SEMAPTHORE_F_BINARY) ? 'B' : '?', + sem->count, + sem->owner?(sem->owner->pid):0); +#ifdef MV_SEM_STAT + seq_printf(m," %d %d %d", sem->tcount, sem->gcount, sem->wcount); +#endif + if (sem->name[0]) + seq_printf(m, " %s", sem->name); + seq_putc(m, '\n'); + + for (p = sem->waitqueue.first; p; p = p->wait_next) + seq_printf(m, " q=%d\n", p->task->pid); + + } + + return 0; + +} + +static int proc_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_status_show, PDE_DATA(inode)); +} + +static const struct file_operations mv_kext_sem_read_proc_operations = { + .open = proc_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +/******************************************************************************* +* mvKernelExt_SemInit +* +* DESCRIPTION: +* Initialize semaphore support, create /proc for semaphores info +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if successful +* Zero if failed +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_SemInit(void) +{ + if (mv_num_semaphores < MV_SEMAPHORES_MIN) + mv_num_semaphores = MV_SEMAPHORES_MIN; + + mvSemaphores = (mvSemaphoreSTC*) kmalloc( + mv_num_semaphores * sizeof(mvSemaphoreSTC), GFP_KERNEL); + + if (mvSemaphores == NULL) + { + if (mvSemaphores) + kfree(mvSemaphores); + mvSemaphores = NULL; + return 0; + } + + memset(mvSemaphores, 0, mv_num_semaphores * sizeof(mvSemaphoreSTC)); + +#ifdef CONFIG_OF + if (!proc_create("mvKernelExtSem", S_IRUGO, NULL, &mv_kext_sem_read_proc_operations)) + return -ENOMEM; +#else + /* create proc entry */ + create_proc_read_entry("mvKernelExtSem", 0, NULL, mvKernelExtSem_read_proc_mem, NULL); +#endif + + return 1; +} + + +/******************************************************************************* +* mvKernelExt_DeleteAll +* +* DESCRIPTION: +* Destroys all semaphores +* This is safety action which is executed when all tasks closed +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_DeleteAll(void) +{ + int k; + + for (k = 1; k < mv_num_semaphores; k++) + { + if (mvSemaphores[k].flags) + { + mv_waitqueue_wake_all(&(mvSemaphores[k].waitqueue)); + mv_waitqueue_cleanup(&(mvSemaphores[k].waitqueue)); + } + mvSemaphores[k].flags = 0; + } +} + +/******************************************************************************* +* mvKernelExt_SemCleanup +* +* DESCRIPTION: +* Perform semaphore cleanup actions before module unload +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_SemCleanup(void) +{ + MV_GLOBAL_LOCK(); + + if (mvSemaphores) + { + mvKernelExt_DeleteAll(); + kfree(mvSemaphores); + } + + mvSemaphores = NULL; + + MV_GLOBAL_UNLOCK(); + + remove_proc_entry("mvKernelExtSem", NULL); +} + +/******************************************************************************* +* mvKernelExt_SemCreate +* +* DESCRIPTION: +* Create a new semaphore or open existing one +* +* INPUTS: +* arg - pointer to structure with creation flags and semaphore name +* +* OUTPUTS: +* None +* +* RETURNS: +* Positive value - semaphore ID +* -MVKERNELEXT_EINVAL - invalid parameter passed +* -MVKERNELEXT_ENOMEM - semaphore array is full +* -MVKERNELEXT_ECONFLICT - open existing semaphore with different type +* specified +* +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemCreate(int flags, const char *name) +{ + int k; + mvSemaphoreSTC *sem = NULL; + + if ((flags & MV_SEMAPTHORE_F_TYPE_MASK) == 0) + return -MVKERNELEXT_EINVAL; + + MV_GLOBAL_LOCK(); + + if (flags & MV_SEMAPTHORE_F_OPENEXIST) + { + /* try to find existing semaphore first */ + for (k = 1; k < mv_num_semaphores; k++) + { + sem = mvSemaphores + k; + if (!sem->flags) + continue; + + if (!strncmp(sem->name, name, MV_SEM_NAME_LEN)) + break; + } + + if (k < mv_num_semaphores) + { + /* found */ + if ((sem->flags & MV_SEMAPTHORE_F_TYPE_MASK) != + (flags & MV_SEMAPTHORE_F_TYPE_MASK)) + { + /* semaphore has different type */ + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ECONFLICT; + } + + MV_GLOBAL_UNLOCK(); + return k; + } + } + + /* create semaphore */ + for (k = 1; k < mv_num_semaphores; k++) + { + if (mvSemaphores[k].flags == 0) + break; + } + if (k >= mv_num_semaphores) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ENOMEM; + } + sem = mvSemaphores + k; + + memset(sem, 0, sizeof(*sem)); + mv_waitqueue_init(&(sem->waitqueue)); + + sem->flags = flags & MV_SEMAPTHORE_F_TYPE_MASK; + + if (sem->flags == MV_SEMAPTHORE_F_MTX) + sem->count = 1; + if (sem->flags == MV_SEMAPTHORE_F_BINARY) + sem->count = (flags & MV_SEMAPTHORE_F_COUNT_MASK) ? 1 : 0; + if (sem->flags == MV_SEMAPTHORE_F_COUNT) + sem->count = flags & MV_SEMAPTHORE_F_COUNT_MASK; + + strncpy(sem->name, name, MV_SEM_NAME_LEN); + sem->name[MV_SEM_NAME_LEN] = 0; + + MV_GLOBAL_UNLOCK(); + return k; +} + +#define SEMAPHORE_BY_ID(semid) \ + MV_GLOBAL_LOCK(); \ + if (unlikely(semid == 0 || semid >= mv_num_semaphores)) \ + { \ +ret_einval: \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_EINVAL; \ + } \ + sem = mvSemaphores + semid; \ + if (unlikely(!sem->flags)) \ + goto ret_einval; + +#define TASK_WILL_WAIT(tsk) \ + struct mv_task *p; \ + if (unlikely((p = gettask_cr(tsk)) == NULL)) \ + { \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_ENOMEM; \ + } +#define CHECK_SEM() \ + if (unlikely(!sem->flags)) \ + { \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_EDELETED; \ + } + +/******************************************************************************* +* mvKernelExt_SemDelete +* +* DESCRIPTION: +* Destroys semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL if bad ID passed +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemDelete(int semid) +{ + mvSemaphoreSTC *sem; + + SEMAPHORE_BY_ID(semid); + mv_waitqueue_wake_all(&(sem->waitqueue)); + mv_waitqueue_cleanup(&(sem->waitqueue)); + sem->flags = 0; + + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* mvKernelExt_SemSignal +* +* DESCRIPTION: +* Signals to semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_EPERM - Mutex semaphore is locked by another task +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemSignal(int semid) +{ + mvSemaphoreSTC *sem; + + SEMAPHORE_BY_ID(semid); + + if (sem->flags & MV_SEMAPTHORE_F_MTX) + { + if (unlikely(sem->owner != current)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EPERM; + } + sem->count++; + if (sem->count > 0) + { + sem->owner = NULL; + mv_waitqueue_wake_first(&(sem->waitqueue)); + } + } else { + if (sem->flags & MV_SEMAPTHORE_F_COUNT) + sem->count++; + else + sem->count = 1; /* binary */ + mv_waitqueue_wake_first(&(sem->waitqueue)); + } + +#ifdef MV_SEM_STAT + sem->gcount++; +#endif + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* mvKernelExt_SemUnlockMutexes +* +* DESCRIPTION: +* Unlock all mutexes locked by dead task +* +* INPUTS: +* owner - pointer to kernel's structure of dead task +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_SemUnlockMutexes( + struct task_struct *owner +) +{ + int k; + for (k = 1; k < mv_num_semaphores; k++) + { + mvSemaphoreSTC *sem = mvSemaphores + k; + + if ((sem->flags & MV_SEMAPTHORE_F_MTX) != 0 + && sem->owner == owner) + { + sem->count = 1; + sem->owner = NULL; + mv_waitqueue_wake_first(&(sem->waitqueue)); + } + } +} + +/******************************************************************************* +* mvKernelExt_SemTryWait_common +* +* DESCRIPTION: +* Try to acquire semaphore without waiting +* +* INPUTS: +* sem - pointer to semaphore structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EBUSY - semaphore cannot be taken immediatelly +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_SemTryWait_common( + mvSemaphoreSTC *sem +) +{ + if (sem->flags & MV_SEMAPTHORE_F_MTX) + { + if (sem->count > 0) + { + sem->count--; + sem->owner = current; + } else { + if (sem->owner == current) + sem->count--; + else + return -MVKERNELEXT_EBUSY; + } + } else { + if (sem->count <= 0) + return -MVKERNELEXT_EBUSY; + sem->count--; + } +#ifdef MV_SEM_STAT + sem->tcount++; +#endif + return 0; +} + +/******************************************************************************* +* mvKernelExt_SemTryWait +* +* DESCRIPTION: +* Try to acquire semaphore without waiting +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_EBUSY - semaphore cannot be taken immediatelly +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemTryWait(int semid) +{ + mvSemaphoreSTC *sem; + int ret; + + SEMAPHORE_BY_ID(semid); + + ret = mvKernelExt_SemTryWait_common(sem); + + MV_GLOBAL_UNLOCK(); + return ret; +} + + +/******************************************************************************* +* mvKernelExt_SemWait +* +* DESCRIPTION: +* Wait for semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemWait(int semid) +{ + mvSemaphoreSTC *sem; + + SEMAPHORE_BY_ID(semid); + + if (mvKernelExt_SemTryWait_common(sem) != 0) + { + TASK_WILL_WAIT(current); +#ifdef MV_SEM_STAT + sem->wcount++; +#endif + if (sem->flags & MV_SEMAPTHORE_F_MTX) + { + do { + if (unlikely(mv_do_short_wait_on_queue(&(sem->waitqueue), p, &(sem->owner)))) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + CHECK_SEM(); + } while (sem->count <= 0); + + sem->owner = p->task; + } else { + do { + if (unlikely(mv_do_wait_on_queue(&(sem->waitqueue), p))) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + CHECK_SEM(); + } while (sem->count == 0); + } + sem->count--; + +#ifdef MV_SEM_STAT + sem->tcount++; +#endif + } + + MV_GLOBAL_UNLOCK(); + return 0; +} + + +/******************************************************************************* +* mvKernelExt_SemWaitTimeout +* +* DESCRIPTION: +* Wait for semaphore +* +* INPUTS: +* semid - semaphore ID +* timeout - timeout in milliseconds +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* -MVKERNELEXT_ETIMEOUT - wait timeout +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemWaitTimeout( + int semid, + unsigned long timeout +) +{ + mvSemaphoreSTC *sem; + + SEMAPHORE_BY_ID(semid); + + if (mvKernelExt_SemTryWait_common(sem) != 0) + { + TASK_WILL_WAIT(current); +#ifdef MV_SEM_STAT + sem->wcount++; +#endif +#if HZ != 1000 + timeout += 1000 / HZ - 1; + timeout /= 1000 / HZ; +#endif + do { + timeout = mv_do_wait_on_queue_timeout(&(sem->waitqueue), p, timeout); + CHECK_SEM(); + if (!timeout) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ETIMEOUT; + } + if (timeout == (unsigned long)(-1)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + } while (sem->count <= 0); + + if (sem->flags & MV_SEMAPTHORE_F_MTX) + sem->owner = p->task; + + sem->count--; +#ifdef MV_SEM_STAT + sem->tcount++; +#endif + } + + MV_GLOBAL_UNLOCK(); + return 0; +} + +EXPORT_SYMBOL(mvKernelExt_SemCreate); +EXPORT_SYMBOL(mvKernelExt_SemDelete); +EXPORT_SYMBOL(mvKernelExt_SemSignal); +EXPORT_SYMBOL(mvKernelExt_SemTryWait); +EXPORT_SYMBOL(mvKernelExt_SemWait); +EXPORT_SYMBOL(mvKernelExt_SemWaitTimeout); Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.c (working copy) @@ -0,0 +1,1241 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************** +* mvKernelExt.c +* +* DESCRIPTION: +* functions in kernel mode special for mainOs. +* +* DEPENDENCIES: +* mvKernelExt.h +* +* $Revision: 11$ +*******************************************************************************/ + +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif + +/* task locking data */ +static struct task_struct* mv_tasklock_owner = NULL; +static mv_waitqueue_t mv_tasklock_waitqueue; +static int mv_tasklock_count = 0; +#ifdef MV_TASKLOCK_STAT +static int mv_tasklock_lcount = 0; +static int mv_tasklock_wcount = 0; +#endif + +/* task array */ +static int mv_max_tasks = MV_MAX_TASKS; +static struct mv_task** mv_tasks = NULL; +static struct mv_task* mv_tasks_alloc = NULL; +static int mv_num_tasks = 0; + +module_param(mv_max_tasks, int, S_IRUGO); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,6) +#define V306PLUS +#endif + +/************************************************************************ + * + * support functions + * + ************************************************************************/ +#ifndef MVKERNELEXT_TASK_STRUCT +/******************************************************************************* +* gettask +* +* DESCRIPTION: +* Search for a mv_task by pointer to kernel's task structure +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* NULL if task is not registered yet +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static struct mv_task* gettask( + struct task_struct *tsk +) +{ + int k; + + for (k = 0; k < mv_num_tasks; k++) + if (mv_tasks[k]->task == tsk) + return mv_tasks[k]; + + return NULL; +} +#endif + +#ifdef MVKERNELEXT_TASK_STRUCT +static void mv_cleanup(struct task_struct *tsk); +#endif + +/******************************************************************************* +* gettask_cr +* +* DESCRIPTION: +* Search for a mv_task by pointer to kernel's task structure +* Register task in array if task was not registered yet +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* NULL if task cannot be registered (task array is full) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static struct mv_task* gettask_cr( + struct task_struct *tsk +) +{ + struct mv_task *p; + + p = gettask(tsk); + if (unlikely(p == NULL)) + { + if (mv_num_tasks >= mv_max_tasks) + return NULL; + p = mv_tasks[mv_num_tasks++]; + memset(p, 0, sizeof(*p)); + p->task = tsk; +#ifdef MVKERNELEXT_TASK_STRUCT + tsk->mv_ptr = p; + tsk->mv_cleanup = mv_cleanup; +#endif + } + return p; +} + +/************************************************************************ + * + * task locking + * + ************************************************************************/ +/******************************************************************************* +* mvKernelExt_TaskLock +* +* DESCRIPTION: +* lock scheduller to current task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_TaskLock(struct task_struct* tsk) +{ + struct mv_task *p; + + MV_GLOBAL_LOCK(); + + p = gettask_cr(tsk); + if (unlikely(p == NULL)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ENOMEM; + } + + if (mv_tasklock_owner && mv_tasklock_owner != tsk) + { +#ifdef MV_TASKLOCK_STAT + mv_tasklock_wcount++; + p->tasklock_wcount++; +#endif + do { + if (mv_do_short_wait_on_queue(&mv_tasklock_waitqueue, p, &mv_tasklock_owner)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + } while (mv_tasklock_owner); + } + +#ifdef MV_TASKLOCK_STAT + mv_tasklock_lcount++; + p->tasklock_lcount++; +#endif + mv_tasklock_owner = tsk; + mv_tasklock_count++; + tsk->state = TASK_RUNNING; + MV_GLOBAL_UNLOCK(); + + return 0; +} + +/** + * sys_mv_taskunlock - unlock scheduller from current task + */ +/******************************************************************************* +* mvKernelExt_TaskUnlock +* +* DESCRIPTION: +* unlock scheduller from current task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* force - force unlock, reset recursion counter +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EPERM - locked by enother task +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_TaskUnlock(struct task_struct* tsk, int force) +{ + MV_GLOBAL_LOCK(); + if (unlikely(mv_tasklock_owner != tsk)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EPERM; + } + + if (force) + mv_tasklock_count = 0; + else + mv_tasklock_count--; + + if (mv_tasklock_count > 0) + { + MV_GLOBAL_UNLOCK(); + return 0; + } + + mv_waitqueue_wake_all(&mv_tasklock_waitqueue); + + mv_tasklock_owner = NULL; + MV_GLOBAL_UNLOCK(); + + return 0; +} + +/******************************************************************************* +* mv_registertask +* +* DESCRIPTION: +* Register current task. Store name for this task +* +* INPUTS: +* taskinfo - pointer to task info: name priority, etc +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_ENOMEM - current task is not yet registered +* and task array is full +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int mv_registertask(mv_registertask_stc* taskinfo) +{ + struct task_struct *curr; + struct mv_task *p; + + curr = current; + + MV_GLOBAL_LOCK(); + + p = gettask_cr(curr); + if (unlikely(p == NULL)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ENOMEM; + } + + memcpy(p->name, taskinfo->name, MV_THREAD_NAME_LEN); + p->name[MV_THREAD_NAME_LEN] = 0; + p->vxw_priority = taskinfo->vxw_priority; + p->pthread_id = taskinfo->pthread_id; + + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* mv_unregistertask +* +* DESCRIPTION: +* Unregister task. Unlock mutexes locked by task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* Non zero if task was not registered +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static int mv_unregistertask(struct task_struct* tsk) +{ + struct mv_task* p = NULL; + int k; + +#ifdef MVKERNELEXT_TASK_STRUCT + tsk->mv_ptr = NULL; + tsk->mv_cleanup = NULL; +#endif + + mvKernelExt_SemUnlockMutexes(tsk); + + if (mv_tasklock_owner == tsk) + { + mv_tasklock_count = 0; + mv_waitqueue_wake_all(&mv_tasklock_waitqueue); + mv_tasklock_owner = NULL; + } + + for (k = 0; k < mv_num_tasks; k++) + if (mv_tasks[k]->task == tsk) + { + p = mv_tasks[k]; + break; + } + + if (p == NULL) + return 1; + + mv_num_tasks--; + if (mv_num_tasks > k) /* task was not the last in array */ + { + mv_tasks[k] = mv_tasks[mv_num_tasks]; + mv_tasks[mv_num_tasks] = p; + } + + mv_delete_from_waitqueue(p); + + return 0; +} + +#ifdef MVKERNELEXT_TASK_STRUCT +/******************************************************************************* +* mv_cleanup +* +* DESCRIPTION: +* Execute cleanup actions when task finished +* Called from kernel's do_exit() if patch applied to kernel +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static void mv_cleanup(struct task_struct *tsk) +{ + MV_GLOBAL_LOCK(); + mv_unregistertask(tsk); + MV_GLOBAL_UNLOCK(); +} +#endif + +/******************************************************************************* +* mv_unregister_current_task +* +* DESCRIPTION: +* Unregister current task +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int mv_unregister_current_task(void) +{ + struct task_struct *curr; + + curr = current; + + MV_GLOBAL_LOCK(); + + mv_unregistertask(curr); + + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* get_task_by_id +* +* DESCRIPTION: +* Search task by task ID +* +* INPUTS: +* tid - Task ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* Null if task not found +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static struct mv_task* get_task_by_id(int tid) +{ + int k; + + if (tid == 0) + return gettask(current); + + for (k = 0; k < mv_num_tasks; k++) + if (mv_tasks[k]->task->pid == tid) + return mv_tasks[k]; + + return NULL; +} + +/******************************************************************************* +* mv_get_pthrid +* +* DESCRIPTION: +* Return pthread ID for task +* Pthread ID associated with task in mv_registertask +* +* INPUTS: +* param->taskid - task ID +* +* OUTPUTS: +* param->pthread_id - associated pthread ID +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_get_pthrid(mv_get_pthrid_stc *param) +{ + struct mv_task* p; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(param->taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + param->pthread_id = p->pthread_id; + + return 0; +} + +/******************************************************************************* +* mv_get_prio +* +* DESCRIPTION: +* Set task priority +* +* INPUTS: +* param->taskid - task ID +* +* OUTPUTS: +* param->vxw_priority - vxWorks task priority +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_get_prio(mv_priority_stc *param) +{ + struct mv_task* p; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(param->taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + param->vxw_priority = p->vxw_priority; + + return 0; +} + +/******************************************************************************* +* mv_suspend +* +* DESCRIPTION: +* Suspend task execution +* +* INPUTS: +* taskid - task ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_suspend(int taskid) +{ + struct mv_task* p; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + if (p->task == current) + mvKernelExt_TaskUnlock(p->task, 1); + + send_sig(SIGSTOP, p->task, 1); + + return 0; +} + +/******************************************************************************* +* mv_resume +* +* DESCRIPTION: +* Resume task execution +* +* INPUTS: +* taskid - task ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_resume(int taskid) +{ + struct mv_task* p; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + send_sig(SIGCONT, p->task, 1); + + return 0; +} + +/******************************************************************************* +* mv_delete +* +* DESCRIPTION: +* Destroy task +* +* INPUTS: +* taskid - task ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_delete(int taskid) +{ + struct mv_task* p; + struct task_struct *tsk; + + MV_GLOBAL_LOCK(); + p = get_task_by_id(taskid); + MV_GLOBAL_UNLOCK(); + + if (p == NULL) + return -MVKERNELEXT_EINVAL; + + tsk = p->task; + + MV_GLOBAL_LOCK(); + mv_unregistertask(tsk); + MV_GLOBAL_UNLOCK(); + + send_sig(SIGRTMIN/*SIGCANCEL*/, tsk, 1); + return 0; +} + +/******************************************************************************* +* mvKernelExt_ioctl +* +* DESCRIPTION: +* The device ioctl() implementation +* +* INPUTS: +* inode - unused +* filp - unused +* cmd - Ioctl number +* arg - parameter +* +* OUTPUTS: +* Depends on cmd +* +* RETURNS: +* -ENOTTY - wrong ioctl magic key +* Depends on cmd +* +* COMMENTS: +* +*******************************************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) +static long mvKernelExt_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg +) +#else +static int mvKernelExt_ioctl( + struct inode *inode, + struct file *filp, + unsigned int cmd, + unsigned long arg +) +#endif +{ + /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */ + if (unlikely(_IOC_TYPE(cmd) != MVKERNELEXT_IOC_MAGIC)) + { + printk("wrong ioctl magic key\n"); + return -ENOTTY; + } + + /* GETTING DATA */ + switch(cmd) + { + case MVKERNELEXT_IOC_NOOP: + return 0; + + case MVKERNELEXT_IOC_TASKLOCK: + return mvKernelExt_TaskLock(current); + + case MVKERNELEXT_IOC_TASKUNLOCK: + return mvKernelExt_TaskUnlock(current, 0); + + case MVKERNELEXT_IOC_TASKUNLOCKFORCE: + return mvKernelExt_TaskUnlock(current, 1); + + case MVKERNELEXT_IOC_REGISTER: + { + mv_registertask_stc lparam; + if (copy_from_user(&lparam, + (mv_registertask_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return mv_registertask(&lparam); + } + case MVKERNELEXT_IOC_UNREGISTER: + return mv_unregister_current_task(); + + case MVKERNELEXT_IOC_SET_PRIO: + { + mv_priority_stc lparam; + + if (copy_from_user(&lparam, + (mv_priority_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return mv_set_prio(&lparam); + } + case MVKERNELEXT_IOC_GET_PRIO: + { + mv_priority_stc lparam; + int retval; + + if (copy_from_user(&lparam, + (mv_priority_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + retval = mv_get_prio(&lparam); + + if (copy_to_user((mv_priority_stc*)arg, + &lparam, sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return retval; + } + + case MVKERNELEXT_IOC_SUSPEND: + return mv_suspend(arg); + case MVKERNELEXT_IOC_RESUME: + return mv_resume(arg); + + case MVKERNELEXT_IOC_GET_PTHRID: + { + mv_get_pthrid_stc lparam; + int retval; + + if (copy_from_user(&lparam, + (mv_get_pthrid_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + retval = mv_get_pthrid(&lparam); + + if (copy_to_user((mv_get_pthrid_stc*)arg, + &lparam, sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return retval; + } + + case MVKERNELEXT_IOC_DELETE: + return mv_delete(arg); + + case MVKERNELEXT_IOC_SEMCREATE: + { + mv_sem_create_stc lparam; + if (copy_from_user(&lparam, + (mv_sem_create_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return mvKernelExt_SemCreate(lparam.flags, lparam.name); + } + case MVKERNELEXT_IOC_SEMDELETE: + return mvKernelExt_SemDelete(arg); + case MVKERNELEXT_IOC_SEMSIGNAL: + return mvKernelExt_SemSignal(arg); + case MVKERNELEXT_IOC_SEMWAIT: + return mvKernelExt_SemWait(arg); + case MVKERNELEXT_IOC_SEMTRYWAIT: + return mvKernelExt_SemTryWait(arg); + case MVKERNELEXT_IOC_SEMWAITTMO: + { + mv_sem_timedwait_stc lparam; + if (copy_from_user(&lparam, + (mv_sem_timedwait_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + + return mvKernelExt_SemWaitTimeout(lparam.semid, lparam.timeout); + } + + case MVKERNELEXT_IOC_TEST: + printk("mvKernelExt_TEST()\n"); + return 0; + + case MVKERNELEXT_IOC_MSGQCREATE: + { + mv_msgq_create_stc lparam; + if (copy_from_user(&lparam, + (mv_msgq_create_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + return mvKernelExt_MsgQCreate( + lparam.name, + lparam.maxMsgs, + lparam.maxMsgSize); + } + case MVKERNELEXT_IOC_MSGQDELETE: + return mvKernelExt_MsgQDelete(arg); + case MVKERNELEXT_IOC_MSGQSEND: + { + mv_msgq_sr_stc lparam; + if (copy_from_user(&lparam, + (mv_msgq_sr_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + return mvKernelExt_MsgQSend( + lparam.msgqId, + lparam.message, + lparam.messageSize, + lparam.timeOut, + 1/*from userspace*/); + } + case MVKERNELEXT_IOC_MSGQRECV: + { + mv_msgq_sr_stc lparam; + if (copy_from_user(&lparam, + (mv_msgq_sr_stc*)arg, + sizeof(lparam))) + return -MVKERNELEXT_EINVAL; + return mvKernelExt_MsgQRecv( + lparam.msgqId, + lparam.message, + lparam.messageSize, + lparam.timeOut, + 1/*to userspace*/); + } + case MVKERNELEXT_IOC_MSGQNUMMSGS: + return mvKernelExt_MsgQNumMsgs(arg); + + default: + printk (KERN_WARNING "Unknown ioctl (%x).\n", cmd); + break; + } + return 0; +} + + +/******************************************************************************* +* mvKernelExt_open +* +* DESCRIPTION: +* The device open() implementation +* +* INPUTS: +* inode - unused +* filp - unused +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -EIO - module uninitialized +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_open( + struct inode * inode, + struct file * filp +) +{ + if (!mvKernelExt_initialized) + { + return -EIO; + } + + filp->private_data = NULL; + + mv_check_tasks(); + + MV_GLOBAL_LOCK(); + mvKernelExt_opened++; + MV_GLOBAL_UNLOCK(); + + return 0; +} + + +/******************************************************************************* +* mvKernelExt_release +* +* DESCRIPTION: +* The device close() implementation +* +* INPUTS: +* inode - unused +* filp - unused +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_release( + struct inode * inode, + struct file * file +) +{ + printk("mvKernelExt_release\n"); + + /*!!! check task list */ + mv_check_tasks(); + + MV_GLOBAL_LOCK(); + mv_unregistertask(current); + mvKernelExt_opened--; + if (mvKernelExt_opened == 0) + { + mvKernelExt_DeleteAll(); + mvKernelExt_DeleteAllMsgQ(); + mv_tasklock_owner = NULL; + mv_tasklock_count = 0; + /* clean statistics */ +#ifdef MV_TASKLOCK_STAT + mv_tasklock_lcount = 0; + mv_tasklock_wcount = 0; +#endif + } + MV_GLOBAL_UNLOCK(); + + return 0; +} + + + + +static struct file_operations mvKernelExt_fops = +{ + .llseek = mvKernelExt_lseek, + .read = mvKernelExt_read, + .write = mvKernelExt_write, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + .unlocked_ioctl = mvKernelExt_ioctl, +#else + .ioctl = mvKernelExt_ioctl, +#endif + .open = mvKernelExt_open, + .release= mvKernelExt_release /* A.K.A close */ +}; + +#ifdef MVKERNELEXT_SYSCALLS +/************************************************************************ + * + * syscall entries for fast calls + * + ************************************************************************/ +/* fast call to KernelExt ioctl */ +asmlinkage long sys_mv_ctl(unsigned int cmd, unsigned long arg) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + return mvKernelExt_ioctl(NULL, cmd, arg); +#else + return mvKernelExt_ioctl(NULL, NULL, cmd, arg); +#endif +} + +extern long sys_call_table[]; + +#define OWN_SYSCALLS 1 + +#ifdef __NR_SYSCALL_BASE +# define __SYSCALL_TABLE_INDEX(name) (__NR_##name-__NR_SYSCALL_BASE) +#else +# define __SYSCALL_TABLE_INDEX(name) (__NR_##name) +#endif + +#define __TBL_ENTRY(name) { __SYSCALL_TABLE_INDEX(name), (long)sys_##name, 0 } +static struct { + int entry_number; + long own_entry; + long saved_entry; +} override_syscalls[OWN_SYSCALLS] = { + __TBL_ENTRY(mv_ctl) +}; +#undef __TBL_ENTRY + + +/******************************************************************************* +* mv_OverrideSyscalls +* +* DESCRIPTION: +* Override entries in syscall table. +* Store original pointers in array. +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int +mv_OverrideSyscalls(void) +{ + int k; + for (k = 0; k < OWN_SYSCALLS; k++) + { + override_syscalls[k].saved_entry = + sys_call_table[override_syscalls[k].entry_number]; + sys_call_table[override_syscalls[k].entry_number] = + override_syscalls[k].own_entry; + } + return 0; +} + +/******************************************************************************* +* mv_RestoreSyscalls +* +* DESCRIPTION: +* Restore original syscall entries. +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int +mv_RestoreSyscalls(void) +{ + int k; + for (k = 0; k < OWN_SYSCALLS; k++) + { + if (override_syscalls[k].saved_entry) + sys_call_table[override_syscalls[k].entry_number] = + override_syscalls[k].saved_entry; + } + return 0; +} +#endif /* MVKERNELEXT_SYSCALLS */ + + +/************************************************************************ +* mvKernelExt_cleanup_common +* +* DESCRIPTION: +* Perform cleanup actions while module unloading +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_cleanup_common(void) +{ +#ifdef MVKERNELEXT_TASK_STRUCT + int k; +#endif + +#ifdef MVKERNELEXT_SYSCALLS + mv_RestoreSyscalls(); +#endif + +#ifdef MVKERNELEXT_TASK_STRUCT + MV_GLOBAL_LOCK(); + for (k = 0; k < mv_num_tasks; k++) + { + mv_tasks[k]->task->mv_ptr = NULL; + mv_tasks[k]->task->mv_cleanup = NULL; + } + MV_GLOBAL_UNLOCK(); +#endif + + mvKernelExt_SemCleanup(); + mvKernelExt_MsgQCleanup(); + + if (mv_tasks) + kfree(mv_tasks); + mv_tasks = NULL; + + if (mv_tasks_alloc) + kfree(mv_tasks_alloc); + mv_tasks_alloc = NULL; + mv_waitqueue_cleanup(&mv_tasklock_waitqueue); + +} + +/************************************************************************ +* mvKernelExt_init_common +* +* DESCRIPTION: +* Module initialization +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -ENOMEM if failed to allocate memory for tasks/semaphores +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_init_common(void) +{ + int result = 0; + + /* allocate task array for tasklock */ + mv_tasks_alloc = (struct mv_task*) kmalloc(sizeof(struct mv_task) * mv_max_tasks, GFP_KERNEL); + mv_tasks = (struct mv_task**) kmalloc(sizeof(struct mv_task*) * mv_max_tasks, GFP_KERNEL); + if (mv_tasks_alloc == NULL || mv_tasks == NULL) + { + if (mv_tasks) + kfree(mv_tasks); + if (mv_tasks_alloc) + kfree(mv_tasks_alloc); + mv_tasks = NULL; + mv_tasks_alloc = NULL; + result = -ENOMEM; + printk("mvKernelExt_init: unable to allocate task array\n"); + return result; + } + for (result = 0; result < mv_max_tasks; result++) + mv_tasks[result] = &(mv_tasks_alloc[result]); + mv_waitqueue_init(&mv_tasklock_waitqueue); + + if (!mvKernelExt_SemInit()) + { + mvKernelExt_cleanup_common(); + result = -ENOMEM; + return result; + } + + if (!mvKernelExt_MsgQInit()) + { + mvKernelExt_cleanup_common(); + result = -ENOMEM; + return result; + } + +#ifdef MVKERNELEXT_SYSCALLS + mv_OverrideSyscalls(); +#endif + + return 0; +} + +EXPORT_SYMBOL(mvKernelExt_TaskLock); +EXPORT_SYMBOL(mvKernelExt_TaskUnlock); Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExt.h (working copy) @@ -0,0 +1,1103 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************** +* mvKernelExt.h +* +* DESCRIPTION: +* functions in kernel mode special for mainOs. +* External definitions +* +* DEPENDENCIES: +* It is assumed that mv_waitqueue_t defined before this file included +* +* $Revision: 7.*******************************************************************************/ + + + +#define MV_MAX_TASKS 40 +#define MV_THREAD_NAME_LEN 16 + +#define MV_SEMAPHORES_MIN 32 +#ifndef LINUX_SIM +# define MV_SEMAPHORES_DEF 128 +#else +# define MV_SEMAPHORES_DEF 1024 +#endif + +#define MV_SEMAPTHORE_F_MTX 0x80000000 +#define MV_SEMAPTHORE_F_COUNT 0x40000000 +#define MV_SEMAPTHORE_F_BINARY 0x20000000 +#define MV_SEMAPTHORE_F_TYPE_MASK 0xe0000000 +#define MV_SEMAPTHORE_F_OPENEXIST 0x10000000 +#define MV_SEMAPTHORE_F_FLAGS_MASK 0x10000000 +#define MV_SEMAPTHORE_F_COUNT_MASK 0x0fffffff + + + +#define MV_QUEUES_MIN 32 +#define MV_QUEUES_DEF 32 + + + + +typedef struct { + char name[MV_THREAD_NAME_LEN]; + int vxw_priority; + unsigned long int pthread_id; +} mv_registertask_stc; + +#define MV_SEM_NAME_LEN 16 +typedef struct { + int flags; + char name[MV_SEM_NAME_LEN]; +} mv_sem_create_stc; + +typedef struct { + int semid; + unsigned long timeout; +} mv_sem_timedwait_stc; + +typedef struct { + int flags; + char name[MV_SEM_NAME_LEN]; +} mv_sem_opennamed_stc; + +typedef struct { + int taskid; + int vxw_priority; +} mv_priority_stc; + +typedef struct { + int taskid; + unsigned long int pthread_id; +} mv_get_pthrid_stc; + +#define MV_MSGQ_NAME_LEN 16 +typedef struct { + char name[MV_SEM_NAME_LEN]; + int maxMsgs; + int maxMsgSize; +} mv_msgq_create_stc; + +typedef struct { + int msgqId; + void* message; + int messageSize; + unsigned long timeOut; +} mv_msgq_sr_stc; + + +/******************************************************** + * + * Error codes + * + ********************************************************/ +#define MVKERNELEXT_EINTR 2 +#define MVKERNELEXT_EPERM 3 +#define MVKERNELEXT_EINVAL 4 +#define MVKERNELEXT_ENOMEM 5 +#define MVKERNELEXT_EDELETED 6 +#define MVKERNELEXT_ETIMEOUT 7 +#define MVKERNELEXT_EBUSY 8 +#define MVKERNELEXT_ECONFLICT 9 +#define MVKERNELEXT_EEMPTY 10 +#define MVKERNELEXT_EFULL 11 + + + +/******************************************************** + * + * IOCTL numbers + * + ********************************************************/ +#define MVKERNELEXT_IOC_MAGIC 'k' +#define MVKERNELEXT_IOC_NOOP _IO(MVKERNELEXT_IOC_MAGIC, 0) +#define MVKERNELEXT_IOC_TASKLOCK _IO(MVKERNELEXT_IOC_MAGIC, 1) +#define MVKERNELEXT_IOC_TASKUNLOCK _IO(MVKERNELEXT_IOC_MAGIC, 2) +#define MVKERNELEXT_IOC_TASKUNLOCKFORCE _IO(MVKERNELEXT_IOC_MAGIC, 3) +#define MVKERNELEXT_IOC_REGISTER _IOW(MVKERNELEXT_IOC_MAGIC, 4, mv_registertask_stc) +#define MVKERNELEXT_IOC_UNREGISTER _IO(MVKERNELEXT_IOC_MAGIC, 5) +#define MVKERNELEXT_IOC_SEMCREATE _IOW(MVKERNELEXT_IOC_MAGIC, 6, mv_sem_create_stc) +#define MVKERNELEXT_IOC_SEMDELETE _IOW(MVKERNELEXT_IOC_MAGIC, 7, long) +#define MVKERNELEXT_IOC_SEMSIGNAL _IOW(MVKERNELEXT_IOC_MAGIC, 8, long) +#define MVKERNELEXT_IOC_SEMWAIT _IOW(MVKERNELEXT_IOC_MAGIC, 9, long) +#define MVKERNELEXT_IOC_SEMTRYWAIT _IOW(MVKERNELEXT_IOC_MAGIC, 10, long) +#define MVKERNELEXT_IOC_SEMWAITTMO _IOW(MVKERNELEXT_IOC_MAGIC, 11, mv_sem_timedwait_stc) +#define MVKERNELEXT_IOC_TEST _IO(MVKERNELEXT_IOC_MAGIC, 12) +#define MVKERNELEXT_IOC_SET_PRIO _IOW(MVKERNELEXT_IOC_MAGIC, 13, mv_priority_stc) +#define MVKERNELEXT_IOC_GET_PRIO _IOW(MVKERNELEXT_IOC_MAGIC, 14, mv_priority_stc) +#define MVKERNELEXT_IOC_SUSPEND _IOW(MVKERNELEXT_IOC_MAGIC, 15, long) +#define MVKERNELEXT_IOC_RESUME _IOW(MVKERNELEXT_IOC_MAGIC, 16, long) +#define MVKERNELEXT_IOC_DELETE _IOW(MVKERNELEXT_IOC_MAGIC, 17, long) +#define MVKERNELEXT_IOC_GET_PTHRID _IOW(MVKERNELEXT_IOC_MAGIC, 18, mv_get_pthrid_stc) + +#define MVKERNELEXT_IOC_MSGQCREATE _IOW(MVKERNELEXT_IOC_MAGIC, 19, mv_msgq_create_stc) +#define MVKERNELEXT_IOC_MSGQDELETE _IOW(MVKERNELEXT_IOC_MAGIC, 20, long) +#define MVKERNELEXT_IOC_MSGQSEND _IOW(MVKERNELEXT_IOC_MAGIC, 21, mv_msgq_sr_stc) +#define MVKERNELEXT_IOC_MSGQRECV _IOW(MVKERNELEXT_IOC_MAGIC, 22, mv_msgq_sr_stc) +#define MVKERNELEXT_IOC_MSGQNUMMSGS _IOW(MVKERNELEXT_IOC_MAGIC, 23, long) + + +#ifdef MVKERNELEXT_SYSCALLS +/******************************************************** + * + * Syscall numbers for + * + * long mv_ctl(unsigned int cmd, unsigned long param) + * + ********************************************************/ +#if 0 +# define __NR_mv_tasklock __NR_setxattr +# define __NR_mv_taskunlock __NR_getxattr +# define __NR_mv_taskunlockforce __NR_listxattr +# define __NR_mv_sem_wait __NR_removexattr +# define __NR_mv_sem_trywait __NR_lsetxattr +# define __NR_mv_sem_wait_tmo __NR_lgetxattr +# define __NR_mv_sem_signal __NR_llistxattr +# define __NR_mv_noop __NR_lremovexattr +#endif +# define __NR_mv_ctl __NR_fsetxattr +#endif + + +#ifdef __KERNEL__ + +#define MV_TASKLOCK_STAT + +/* + * Typedef: struct mv_task + * + * Description: per-task structure + * + * Fields: + * task - pointer to kernel's task structure + * tasklockflag - task suspended flag: + * 0 - task running + * 1 - task suspended for a short period + * 2 - task suspended for a long period + * name - task name. Useful for debugging purposes only + * vxw_priority - vxWorks priority value (untranslated priority) + * pthread_id - value for pthread_t pointer (userspace threads) + * waitqueue - queue task suspended in + * wait_next - next task in wait queue + * + */ +struct mv_task { + struct task_struct *task; + int tasklockflag; + char name[MV_THREAD_NAME_LEN+1]; + int vxw_priority; + unsigned long int pthread_id; + mv_waitqueue_t *waitqueue; + struct mv_task *wait_next; +#ifdef MV_TASKLOCK_STAT + int tasklock_lcount; + int tasklock_wcount; +#endif +}; + + +/***** Static function declarations ************************************/ + +/************************************************************************ +* +* waitqueue support functions +* +* These functions required to suspend thread till some event occurs +************************************************************************/ + +/******************************************************************************* +* mv_waitqueue_init +* +* DESCRIPTION: +* Initialize wait queue structure +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static void mv_waitqueue_init( + mv_waitqueue_t* queue +); + +/******************************************************************************* +* mv_waitqueue_cleanup +* +* DESCRIPTION: +* Cleanup wait queue structure +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static void mv_waitqueue_cleanup( + mv_waitqueue_t* queue +); + +/******************************************************************************* +* mv_waitqueue_add +* +* DESCRIPTION: +* add task to wait queue +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_waitqueue_add( + mv_waitqueue_t* queue, + struct mv_task* tsk +); + +/******************************************************************************* +* mv_waitqueue_wake_first +* +* DESCRIPTION: +* wakeup first task waiting in queue +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_waitqueue_wake_first( + mv_waitqueue_t* queue +); + +/******************************************************************************* +* mv_waitqueue_wake_all +* +* DESCRIPTION: +* wakeup all tasks waiting in queue +* +* INPUTS: +* queue - pointer to wait queue structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_waitqueue_wake_all( + mv_waitqueue_t* queue +); + +/******************************************************************************* +* mv_delete_from_waitqueue +* +* DESCRIPTION: +* remove task from wait queue +* +* INPUTS: +* tsk - pointer to task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static void mv_delete_from_waitqueue( + struct mv_task* tsk +); + +/******************************************************************************* +* mv_do_short_wait_on_queue +* +* DESCRIPTION: +* Suspend a task on wait queue for a short period. +* Function has a best performance in a cost of CPU usage +* This is useful for mutual exclusion semaphores +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* owner - resourse owner +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if wait successful +* Non zero if wait interrupted (signal caught) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static int mv_do_short_wait_on_queue( + mv_waitqueue_t* queue, + struct mv_task* tsk, + struct task_struct** owner +); + +/******************************************************************************* +* mv_do_wait_on_queue +* +* DESCRIPTION: +* Suspend a task on wait queue. +* Function has the same performance as mv_do_short_wait_on_queue when +* task suspended for short period. After that task state changed to +* suspended +* This is useful for binary and counting semaphores +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if wait successful +* Non zero if wait interrupted (signal caught) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static int mv_do_wait_on_queue( + mv_waitqueue_t* queue, + struct mv_task* tsk +); + +/******************************************************************************* +* mv_do_wait_on_queue_timeout +* +* DESCRIPTION: +* Suspend a task on wait queue. +* Return if timer expited. +* +* INPUTS: +* queue - pointer to wait queue structure +* tsk - pointer to task structure +* timeout - timeout in scheduller ticks +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if wait successful +* Zero if timeout occured +* -1 if wait interrupted (signal caught) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static unsigned long mv_do_wait_on_queue_timeout( + mv_waitqueue_t* queue, + struct mv_task* tsk, + unsigned long timeout +); + + + + + + +/************************************************************************ +* +* Task lookup functions +* +* These functions required to lookup tasks in task array +************************************************************************/ + +/******************************************************************************* +* mv_check_tasks +* +* DESCRIPTION: +* Walk through task array and check if task still alive +* Perform cleanup actions for dead tasks +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mv_check_tasks(void); + +/******************************************************************************* +* gettask +* +* DESCRIPTION: +* Search for a mv_task by pointer to kernel's task structure +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* NULL if task is not registered yet +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +#ifdef MVKERNELEXT_TASK_STRUCT +#define gettask(tsk) ((struct mv_task*)tsk->mv_ptr); +#else +static struct mv_task* gettask( + struct task_struct *tsk +); +#endif + +/******************************************************************************* +* gettask_cr +* +* DESCRIPTION: +* Search for a mv_task by pointer to kernel's task structure +* Register task in array if task was not registered yet +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Pointer to mv_task +* NULL if task cannot be registered (task array is full) +* +* COMMENTS: +* Interrupts must be disabled when this function called +* +*******************************************************************************/ +static struct mv_task* gettask_cr( + struct task_struct *tsk +); + + + +/************************************************************************ + * + * task locking + * + ************************************************************************/ +/******************************************************************************* +* mvKernelExt_TaskLock +* +* DESCRIPTION: +* lock scheduller to current task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_TaskLock(struct task_struct* tsk); + +/******************************************************************************* +* mvKernelExt_TaskUnlock +* +* DESCRIPTION: +* unlock scheduller from current task +* +* INPUTS: +* tsk - pointer to kernel's task structure +* force - force unlock, reset recursion counter +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EPERM - locked by enother task +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_TaskUnlock(struct task_struct* tsk, int force); + + + + + + +/******************************************************************************* +* mv_set_prio +* +* DESCRIPTION: +* Set task priority +* +* INPUTS: +* param->taskid - task ID +* param->vxw_priority - vxWorks task priority +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - if task is not registered +* +* COMMENTS: +* +*******************************************************************************/ +static int mv_set_prio(mv_priority_stc *param); + + +/************************************************************************ +* +* Semaphore functions +* +************************************************************************/ + +/******************************************************************************* +* mvKernelExt_SemInit +* +* DESCRIPTION: +* Initialize semaphore support, create /proc for semaphores info +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if successful +* Zero if failed +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_SemInit(void); + +/******************************************************************************* +* mvKernelExt_SemCleanup +* +* DESCRIPTION: +* Perform semaphore cleanup actions before module unload +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_SemCleanup(void); + +/******************************************************************************* +* mvKernelExt_SemCreate +* +* DESCRIPTION: +* Create a new semaphore or open existing one +* +* INPUTS: +* arg - pointer to structure with creation flags and semaphore name +* +* OUTPUTS: +* None +* +* RETURNS: +* Positive value - semaphore ID +* -MVKERNELEXT_EINVAL - invalid parameter passed +* -MVKERNELEXT_ENOMEM - semaphore array is full +* -MVKERNELEXT_ECONFLICT - open existing semaphore with different type +* specified +* +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemCreate(int flags, const char *name); + + +/******************************************************************************* +* mvKernelExt_SemDelete +* +* DESCRIPTION: +* Destroys semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL if bad ID passed +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemDelete(int semid); + +/******************************************************************************* +* mvKernelExt_DeleteAll +* +* DESCRIPTION: +* Destroys all semaphores +* This is safety action which is executed when all tasks closed +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_DeleteAll(void); + +/******************************************************************************* +* mvKernelExt_SemSignal +* +* DESCRIPTION: +* Signals to semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_EPERM - Mutex semaphore is locked by another task +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemSignal(int semid); + +/******************************************************************************* +* mvKernelExt_SemWait +* +* DESCRIPTION: +* Wait for semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemWait(int semid); + +/******************************************************************************* +* mvKernelExt_SemTryWait +* +* DESCRIPTION: +* Try to acquire semaphore without waiting +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_EBUSY - semaphore cannot be taken immediatelly +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemTryWait(int semid); + +/******************************************************************************* +* mvKernelExt_SemWaitTimeout +* +* DESCRIPTION: +* Wait for semaphore +* +* INPUTS: +* semid - semaphore ID +* timeout - timeout in milliseconds +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* -MVKERNELEXT_ETIMEOUT - wait timeout +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemWaitTimeout( + int semid, + unsigned long timeout +); + +/******************************************************************************* +* mvKernelExt_SemUnlockMutexes +* +* DESCRIPTION: +* Unlock all mutexes locked by dead task +* +* INPUTS: +* owner - pointer to kernel's structure of dead task +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_SemUnlockMutexes( + struct task_struct *owner +); + + +/******************************************************************************* +* mvKernelExt_MsgQInit +* +* DESCRIPTION: +* Initialize message queues support, create /proc for queues info +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if successful +* Zero if failed +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_MsgQInit(void); + +/******************************************************************************* +* mvKernelExt_DeleteAllMsgQ +* +* DESCRIPTION: +* Destroys all message queues +* This is safety action which is executed when all tasks closed +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_DeleteAllMsgQ(void); + +/******************************************************************************* +* mvKernelExt_MsgQCleanup +* +* DESCRIPTION: +* Perform message queues cleanup actions before module unload +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_MsgQCleanup(void); + +/******************************************************************************* +* mvKernelExt_MsgQCreate +* +* DESCRIPTION: +* Create a new message queue +* +* INPUTS: +* arg - pointer to structure with creation params and queue name +* +* OUTPUTS: +* None +* +* RETURNS: +* Positive value - queue ID +* -MVKERNELEXT_EINVAL - invalid parameter passed +* -MVKERNELEXT_ENOMEM - queue array is full +* +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQCreate( + const char *name, + int maxMsgs, + int maxMsgSize +); + +/******************************************************************************* +* mvKernelExt_MsgQDelete +* +* DESCRIPTION: +* Destroys semaphore +* +* INPUTS: +* msgqId - queue ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL if bad ID passed +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQDelete(int msgqId); + +/******************************************************************************* +* mvKernelExt_MsgQSend +* +* DESCRIPTION: +* Send message to queue +* +* INPUTS: +* msgqId - Message queue Id +* message - message data pointer +* messageSize - message size +* timeOut - time out in miliseconds or +* -1 for WAIT_FOREVER or 0 for NO_WAIT +* userspace - called from userspace +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ETIMEOUT - on timeout +* -MVKERNELEXT_ENOMEM - full and no wait +* -MVKERNELEXT_EDELETED - deleted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQSend( + int msgqId, + void* message, + int messageSize, + int timeOut, + int userspace +); + +/******************************************************************************* +* mvKernelExt_MsgQRecv +* +* DESCRIPTION: +* Receive message from queue +* +* INPUTS: +* msgqId - Message queue Id +* messageSize - size of buffer pointed by message +* timeOut - time out in miliseconds or +* -1 for WAIT_FOREVER or 0 for NO_WAIT +* userspace - called from userspace +* +* OUTPUTS: +* message - message data pointer +* +* RETURNS: +* message size if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ETIMEOUT - on timeout +* -MVKERNELEXT_ENOMEM - empty and no wait +* -MVKERNELEXT_EDELETED - deleted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQRecv( + int msgqId, + void* message, + int messageSize, + int timeOut, + int userspace +); + +/******************************************************************************* +* mvKernelExt_MsgQNumMsgs +* +* DESCRIPTION: +* Return number of messages pending in queue +* +* INPUTS: +* msgqId - Message queue Id +* +* OUTPUTS: +* None +* +* RETURNS: +* numMessages - number of messages pending in queue +* -MVKERNELEXT_EINVAL - bad ID passed +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_MsgQNumMsgs(int msgqId); + +#endif /* __KERNEL */ Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtMsgQ.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtMsgQ.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtMsgQ.c (working copy) @@ -0,0 +1,734 @@ +/******************************************************************************* +* mv_KervelExtMsgQ.c +* +* DESCRIPTION: +* Message queues +* +* DEPENDENCIES: +* +* FILE REVISION NUMBER: +* $Revision: $ +1*******************************************************************************/ + +#ifdef CONFIG_OF +#include +#endif + + +/************* Defines ********************************************************/ + +#define MV_MSGQ_STAT + +/************ Internal Typedefs ***********************************************/ +typedef struct _mvMsgQ +{ + int flags; + char name[MV_MSGQ_NAME_LEN+1]; + mv_waitqueue_t rxWaitQueue; + mv_waitqueue_t txWaitQueue; + int maxMsgs; + int maxMsgSize; + int messages; + char *buffer; + int head; + int tail; + int waitRx; + int waitTx; +} mvMsgQSTC; + +static mvMsgQSTC *mvMsgQs = NULL; +static int mv_num_queues = MV_QUEUES_DEF; + +module_param(mv_num_queues, int, S_IRUGO); + +/******************************************************************************* +* mvKernelExtMsgQ_read_proc_mem +* +* DESCRIPTION: +* proc read data rooutine. +* Use cat /proc/mvKernelExtMsgQ to show message queue list +* +* INPUTS: +* +* OUTPUTS: +* None +* +* RETURNS: +* Data length +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExtMsgQ_read_proc_mem( + char * page, + char **start, + off_t offset, + int count, + int *eof, + void *data) +{ + int len; + int k; + int begin = 0; + + len = 0; + + len += sprintf(page+len,"id msgs waitRx waitTx"); +#ifdef MV_MSGQ_STAT + /* + len += sprintf(page+len," tcount gcount wcount"); + */ +#endif + len += sprintf(page+len," name\n"); + for (k = 1; k < mv_num_queues; k++) + { + mvMsgQSTC *q; + struct mv_task *p; + + if (!mvMsgQs[k].flags) + continue; + q = mvMsgQs + k; + + len += sprintf(page+len,"%d %d %d %d", + k, q->messages, q->waitRx, q->waitTx); +#ifdef MV_MSGQ_STAT + /* + len += sprintf(page+len," %d %d %d", sem->tcount, sem->gcount, sem->wcount); + */ +#endif + if (q->name[0]) + len += sprintf(page+len," %s", q->name); + page[len++] = '\n'; + + for (p = q->rxWaitQueue.first; p; p = p->wait_next) + len += sprintf(page+len, " rq=%d\n", p->task->pid); + for (p = q->txWaitQueue.first; p; p = p->wait_next) + len += sprintf(page+len, " tq=%d\n", p->task->pid); + + if (len+begin < offset) + { + begin += len; + len = 0; + } + if (len+begin >= offset+count) + break; + } + if (len+begin < offset) + *eof = 1; + offset -= begin; + *start = page + offset; + len -= offset; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} +#ifdef CONFIG_OF +static int proc_status_show_msq(struct seq_file *m, void *v) { + + int len; + int k; + + seq_printf(m, "id msgs waitRx waitTx"); +#ifdef MV_MSGQ_STAT + /* + len += sprintf(page+len," tcount gcount wcount"); + */ +#endif + seq_printf(m, " name\n"); + for (k = 1; k < mv_num_queues; k++) + { + mvMsgQSTC *q; + struct mv_task *p; + + if (!mvMsgQs[k].flags) + continue; + q = mvMsgQs + k; + + seq_printf(m, "%d %d %d %d", + k, q->messages, q->waitRx, q->waitTx); +#ifdef MV_MSGQ_STAT + /* + len += sprintf(page+len," %d %d %d", sem->tcount, sem->gcount, sem->wcount); + */ +#endif + if (q->name[0]) + seq_printf(m, " %s", q->name); + seq_putc(m, '\n'); + + for (p = q->rxWaitQueue.first; p; p = p->wait_next) + seq_printf(m, " rq=%d\n", p->task->pid); + for (p = q->txWaitQueue.first; p; p = p->wait_next) + seq_printf(m, " tq=%d\n", p->task->pid); + + } + + return 0; + +} + +static int proc_status_open_msq(struct inode *inode, struct file *file) +{ + return single_open(file, proc_status_show_msq, PDE_DATA(inode)); +} + +static const struct file_operations mv_kext_msq_read_proc_operations = { + .open = proc_status_open_msq, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +/******************************************************************************* +* mvKernelExt_MsgQInit +* +* DESCRIPTION: +* Initialize message queues support, create /proc for queues info +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if successful +* Zero if failed +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_MsgQInit(void) +{ + if (mv_num_queues < MV_QUEUES_MIN) + mv_num_semaphores = MV_QUEUES_MIN; + + mvMsgQs = (mvMsgQSTC*) kmalloc( + mv_num_queues * sizeof(mvMsgQSTC), GFP_KERNEL); + + if (mvMsgQs == NULL) + { + return 0; + } + + memset(mvMsgQs, 0, mv_num_queues * sizeof(mvMsgQSTC)); + + /* create proc entry */ +#ifdef CONFIG_OF + if (!proc_create("mvKernelExtMsgQ", S_IRUGO, NULL, &mv_kext_msq_read_proc_operations)) + return -ENOMEM; +#else + create_proc_read_entry("mvKernelExtMsgQ", 0, NULL, mvKernelExtMsgQ_read_proc_mem, NULL); +#endif + + return 1; +} + +/******************************************************************************* +* mvKernelExt_DeleteAllMsgQ +* +* DESCRIPTION: +* Destroys all message queues +* This is safety action which is executed when all tasks closed +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_DeleteAllMsgQ(void) +{ + int k; + + for (k = 1; k < mv_num_queues; k++) + { + if (mvMsgQs[k].flags) + { + mv_waitqueue_wake_all(&(mvMsgQs[k].rxWaitQueue)); + mv_waitqueue_wake_all(&(mvMsgQs[k].txWaitQueue)); + mv_waitqueue_cleanup(&(mvMsgQs[k].rxWaitQueue)); + mv_waitqueue_cleanup(&(mvMsgQs[k].txWaitQueue)); + } + mvMsgQs[k].flags = 0; + } +} + +/******************************************************************************* +* mvKernelExt_MsgQCleanup +* +* DESCRIPTION: +* Perform message queues cleanup actions before module unload +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_MsgQCleanup(void) +{ + MV_GLOBAL_LOCK(); + + if (mvMsgQs) + { + mvKernelExt_DeleteAllMsgQ(); + kfree(mvMsgQs); + } + + mvMsgQs = NULL; + + MV_GLOBAL_UNLOCK(); + + remove_proc_entry("mvKernelExtMsgQ", NULL); +} + +/******************************************************************************* +* mvKernelExt_MsgQCreate +* +* DESCRIPTION: +* Create a new message queue +* +* INPUTS: +* arg - pointer to structure with creation params and queue name +* +* OUTPUTS: +* None +* +* RETURNS: +* Positive value - queue ID +* -MVKERNELEXT_EINVAL - invalid parameter passed +* -MVKERNELEXT_ENOMEM - queue array is full +* +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQCreate( + const char *name, + int maxMsgs, + int maxMsgSize +) +{ + int k; + mvMsgQSTC *q = NULL; + + MV_GLOBAL_LOCK(); + + /* create queue */ + for (k = 1; k < mv_num_queues; k++) + { + if (mvMsgQs[k].flags == 0) + break; + } + if (k >= mv_num_queues) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ENOMEM; + } + q = mvMsgQs + k; + + memset(q, 0, sizeof(*q)); + q->flags = 3; + MV_GLOBAL_UNLOCK(); + + /* align max message size by 4 bytes */ + maxMsgSize = (maxMsgSize+3) & ~3; + q->maxMsgs = maxMsgs; + q->maxMsgSize = maxMsgSize; + q->buffer = (char*)kmalloc((maxMsgSize + sizeof(int))*maxMsgs, GFP_KERNEL); + if (q->buffer == NULL) + { + q->flags = 0; + return -MVKERNELEXT_ENOMEM; + } + + MV_GLOBAL_LOCK(); + q->flags = 1; + mv_waitqueue_init(&(q->rxWaitQueue)); + mv_waitqueue_init(&(q->txWaitQueue)); + + strncpy(q->name, name, MV_MSGQ_NAME_LEN); + q->name[MV_MSGQ_NAME_LEN] = 0; + + MV_GLOBAL_UNLOCK(); + return k; +} + +#define MSGQ_BY_ID(msgId) \ + MV_GLOBAL_LOCK(); \ + if (unlikely(msgqId == 0 || msgqId >= mv_num_queues)) \ + { \ +ret_einval: \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_EINVAL; \ + } \ + q = mvMsgQs + msgqId; \ + if (unlikely(q->flags != 1)) \ + goto ret_einval; + +#define CHECK_MSGQ() \ + if (unlikely(q->flags != 1)) \ + { \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_EDELETED; \ + } + +/******************************************************************************* +* mvKernelExt_MsgQDelete +* +* DESCRIPTION: +* Destroys semaphore +* +* INPUTS: +* msgqId - queue ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL if bad ID passed +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQDelete(int msgqId) +{ + mvMsgQSTC *q; + int timeOut; + + MSGQ_BY_ID(msgqId); + + q->flags = 2; /* deleting */ + + for (timeOut = HZ; q->waitRx && timeOut; timeOut--) + { + mv_waitqueue_wake_all(&(q->rxWaitQueue)); + if (q->waitRx) + { + MV_GLOBAL_UNLOCK(); + schedule_timeout(1); + MV_GLOBAL_LOCK(); + } + } + for (timeOut = HZ; q->waitTx && timeOut; timeOut--) + { + mv_waitqueue_wake_all(&(q->txWaitQueue)); + if (q->waitTx) + { + MV_GLOBAL_UNLOCK(); + schedule_timeout(1); + MV_GLOBAL_LOCK(); + } + } + + mv_waitqueue_cleanup(&(q->rxWaitQueue)); + mv_waitqueue_cleanup(&(q->txWaitQueue)); + + MV_GLOBAL_UNLOCK(); + kfree(q->buffer); + + q->flags = 0; + + return 0; +} + +/******************************************************************************* +* mvKernelExt_MsgQSend +* +* DESCRIPTION: +* Send message to queue +* +* INPUTS: +* msgqId - Message queue Id +* message - message data pointer +* messageSize - message size +* timeOut - time out in miliseconds or +* -1 for WAIT_FOREVER or 0 for NO_WAIT +* userspace - called from userspace +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ETIMEOUT - on timeout +* -MVKERNELEXT_ENOMEM - full and no wait +* -MVKERNELEXT_EDELETED - deleted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQSend( + int msgqId, + void* message, + int messageSize, + int timeOut, + int userspace +) +{ + char *msg; + mvMsgQSTC *q; + + MSGQ_BY_ID(msgqId); + + while (q->messages == q->maxMsgs) + { + /* queue full */ + if (timeOut == 0) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EFULL; /* ??? -MVKERNELEXT_ETIMEOUT */ + } + else + { + TASK_WILL_WAIT(current); + q->waitTx++; + if (timeOut != -1) + { +#if HZ != 1000 + timeOut += 1000 / HZ - 1; + timeOut /= 1000 / HZ; +#endif + timeOut = mv_do_wait_on_queue_timeout(&(q->txWaitQueue), p, timeOut); + CHECK_MSGQ(); + if (timeOut == 0) + { + q->waitTx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ETIMEOUT; + } + if (timeOut == (unsigned long)(-1)) + { + q->waitTx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + } + else /* timeOut == -1, wait forever */ + { + if (unlikely(mv_do_wait_on_queue(&(q->txWaitQueue), p))) + { + q->waitTx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + CHECK_MSGQ(); + } + q->waitTx--; + } + } + + /* put message */ + msg = q->buffer + q->head * (q->maxMsgSize + sizeof(int)); + if (messageSize > q->maxMsgSize) + messageSize = q->maxMsgSize; + + *((int*)msg) = messageSize; + if (userspace) + { + if (copy_from_user(msg+sizeof(int), message, messageSize)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINVAL; + } + } + else + { + memcpy(msg+sizeof(int), message, messageSize); + + } + q->head++; + if (q->head >= q->maxMsgs) /* round up */ + q->head = 0; + q->messages++; + + /* signal to Recv thread if any */ + if (q->waitRx) + { + mv_waitqueue_wake_first(&(q->rxWaitQueue)); + /* + if (unlikely(!q->rxWaitQueue.first)) + q->waitRx = 0; + */ + } + + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* mvKernelExt_MsgQRecv +* +* DESCRIPTION: +* Receive message from queue +* +* INPUTS: +* msgqId - Message queue Id +* messageSize - size of buffer pointed by message +* timeOut - time out in miliseconds or +* -1 for WAIT_FOREVER or 0 for NO_WAIT +* userspace - called from userspace +* +* OUTPUTS: +* message - message data pointer +* +* RETURNS: +* message size if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ETIMEOUT - on timeout +* -MVKERNELEXT_ENOMEM - empty and no wait +* -MVKERNELEXT_EDELETED - deleted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_MsgQRecv( + int msgqId, + void* message, + int messageSize, + int timeOut, + int userspace +) +{ + char *msg; + int msgSize; + mvMsgQSTC *q; + + MSGQ_BY_ID(msgqId); + + while (q->messages == 0) + { + /* queue empty */ + if (timeOut == 0) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EEMPTY; /* ??? -MVKERNELEXT_ETIMEOUT */ + } + else + { + TASK_WILL_WAIT(current); + q->waitRx++; + if (timeOut != -1) + { +#if HZ != 1000 + timeOut += 1000 / HZ - 1; + timeOut /= 1000 / HZ; +#endif + timeOut = mv_do_wait_on_queue_timeout(&(q->rxWaitQueue), p, timeOut); + CHECK_MSGQ(); + if (timeOut == 0) + { + q->waitRx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ETIMEOUT; + } + if (timeOut == (unsigned long)(-1)) + { + q->waitRx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + } + else /* timeOut == -1, wait forever */ + { + if (unlikely(mv_do_wait_on_queue(&(q->rxWaitQueue), p))) + { + q->waitRx--; + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + CHECK_MSGQ(); + } + q->waitRx--; + } + } + /* get message */ + msg = q->buffer + q->tail * (q->maxMsgSize + sizeof(int)); + msgSize = *((int*)msg); + if (msgSize > messageSize) + msgSize = messageSize; + + if (userspace) + { + if (copy_to_user(message, msg+sizeof(int), msgSize)) + { + msgSize = 0; + } + } + else + { + memcpy(message, msg+sizeof(int), msgSize); + } + q->tail++; + if (q->tail >= q->maxMsgs) /* round up */ + q->tail = 0; + q->messages--; + + /* signal to Recv thread if any */ + if (q->waitTx) + { + mv_waitqueue_wake_first(&(q->txWaitQueue)); + /* + if (unlikely(!q->txWaitQueue.first)) + q->waitTx = 0;*/ + } + + MV_GLOBAL_UNLOCK(); + return msgSize; +} + +/******************************************************************************* +* mvKernelExt_MsgQNumMsgs +* +* DESCRIPTION: +* Return number of messages pending in queue +* +* INPUTS: +* msgqId - Message queue Id +* +* OUTPUTS: +* None +* +* RETURNS: +* numMessages - number of messages pending in queue +* -MVKERNELEXT_EINVAL - bad ID passed +* +* COMMENTS: +* None +* +*******************************************************************************/ +int mvKernelExt_MsgQNumMsgs(int msgqId) +{ + int numMessages; + mvMsgQSTC *q; + + MSGQ_BY_ID(msgqId); + numMessages = q->messages; + MV_GLOBAL_UNLOCK(); + + return numMessages; +} + +EXPORT_SYMBOL(mvKernelExt_MsgQCreate); +EXPORT_SYMBOL(mvKernelExt_MsgQDelete); +EXPORT_SYMBOL(mvKernelExt_MsgQSend); +EXPORT_SYMBOL(mvKernelExt_MsgQRecv); +EXPORT_SYMBOL(mvKernelExt_MsgQNumMsgs); Index: drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtSem.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtSem.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/kerneldrv/common/mv_KernelExtSem.c (working copy) @@ -0,0 +1,822 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************** +* mvKernelExtSem.c +* +* DESCRIPTION: +* functions in kernel mode special for mainOs. +* Semaphores implementation +* +* DEPENDENCIES: +* +* $Revision: 6$ +*******************************************************************************/ + +#ifdef CONFIG_OF +#include +#endif + +#define MV_SEM_STAT +typedef struct { + int flags; + int count; + mv_waitqueue_t waitqueue; + struct task_struct *owner; + char name[MV_SEM_NAME_LEN+1]; +#ifdef MV_SEM_STAT + int tcount; + int gcount; + int wcount; +#endif +} mvSemaphoreSTC; + +static mvSemaphoreSTC *mvSemaphores = NULL; +static int mv_num_semaphores = MV_SEMAPHORES_DEF; + +module_param(mv_num_semaphores, int, S_IRUGO); + + +/******************************************************************************* +* mvKernelExtSem_read_proc_mem +* +* DESCRIPTION: +* proc read data rooutine. +* Use cat /proc/mvKernelExtSem to show semaphore list +* +* INPUTS: +* +* OUTPUTS: +* None +* +* RETURNS: +* Data length +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExtSem_read_proc_mem( + char * page, + char **start, + off_t offset, + int count, + int *eof, + void *data) +{ + int len; + int k; + int begin = 0; + + len = 0; + + len += sprintf(page+len,"id type count owner"); +#ifdef MV_SEM_STAT + len += sprintf(page+len," tcount gcount wcount"); +#endif + len += sprintf(page+len," name\n"); + for (k = 1; k < mv_num_semaphores; k++) + { + mvSemaphoreSTC *sem; + struct mv_task *p; + + if (!mvSemaphores[k].flags) + continue; + sem = mvSemaphores + k; + + len += sprintf(page+len,"%d %c %d %d", + k, + (sem->flags & MV_SEMAPTHORE_F_MTX) ? 'M' : + (sem->flags & MV_SEMAPTHORE_F_COUNT) ? 'C' : + (sem->flags & MV_SEMAPTHORE_F_BINARY) ? 'B' : '?', + sem->count, + sem->owner?(sem->owner->pid):0); +#ifdef MV_SEM_STAT + len += sprintf(page+len," %d %d %d", sem->tcount, sem->gcount, sem->wcount); +#endif + if (sem->name[0]) + len += sprintf(page+len," %s", sem->name); + page[len++] = '\n'; + + for (p = sem->waitqueue.first; p; p = p->wait_next) + len += sprintf(page+len, " q=%d\n", p->task->pid); + + if (len+begin < offset) + { + begin += len; + len = 0; + } + if (len+begin >= offset+count) + break; + } + if (len+begin < offset) + *eof = 1; + offset -= begin; + *start = page + offset; + len -= offset; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +#ifdef CONFIG_OF +static int proc_status_show(struct seq_file *m, void *v) { + int k; + + seq_printf(m, "id type count owner"); +#ifdef MV_SEM_STAT + seq_printf(m, " tcount gcount wcount"); +#endif + seq_printf(m, " name\n"); + for (k = 1; k < mv_num_semaphores; k++) + { + mvSemaphoreSTC *sem; + struct mv_task *p; + + if (!mvSemaphores[k].flags) + continue; + sem = mvSemaphores + k; + + seq_printf(m, "%d %c %d %d", + k, + (sem->flags & MV_SEMAPTHORE_F_MTX) ? 'M' : + (sem->flags & MV_SEMAPTHORE_F_COUNT) ? 'C' : + (sem->flags & MV_SEMAPTHORE_F_BINARY) ? 'B' : '?', + sem->count, + sem->owner?(sem->owner->pid):0); +#ifdef MV_SEM_STAT + seq_printf(m," %d %d %d", sem->tcount, sem->gcount, sem->wcount); +#endif + if (sem->name[0]) + seq_printf(m, " %s", sem->name); + seq_putc(m, '\n'); + + for (p = sem->waitqueue.first; p; p = p->wait_next) + seq_printf(m, " q=%d\n", p->task->pid); + + } + + return 0; + +} + +static int proc_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_status_show, PDE_DATA(inode)); +} + +static const struct file_operations mv_kext_sem_read_proc_operations = { + .open = proc_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +/******************************************************************************* +* mvKernelExt_SemInit +* +* DESCRIPTION: +* Initialize semaphore support, create /proc for semaphores info +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* Non zero if successful +* Zero if failed +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_SemInit(void) +{ + if (mv_num_semaphores < MV_SEMAPHORES_MIN) + mv_num_semaphores = MV_SEMAPHORES_MIN; + + mvSemaphores = (mvSemaphoreSTC*) kmalloc( + mv_num_semaphores * sizeof(mvSemaphoreSTC), GFP_KERNEL); + + if (mvSemaphores == NULL) + { + if (mvSemaphores) + kfree(mvSemaphores); + mvSemaphores = NULL; + return 0; + } + + memset(mvSemaphores, 0, mv_num_semaphores * sizeof(mvSemaphoreSTC)); + +#ifdef CONFIG_OF + if (!proc_create("mvKernelExtSem", S_IRUGO, NULL, &mv_kext_sem_read_proc_operations)) + return -ENOMEM; +#else + /* create proc entry */ + create_proc_read_entry("mvKernelExtSem", 0, NULL, mvKernelExtSem_read_proc_mem, NULL); +#endif + + return 1; +} + + +/******************************************************************************* +* mvKernelExt_DeleteAll +* +* DESCRIPTION: +* Destroys all semaphores +* This is safety action which is executed when all tasks closed +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_DeleteAll(void) +{ + int k; + + for (k = 1; k < mv_num_semaphores; k++) + { + if (mvSemaphores[k].flags) + { + mv_waitqueue_wake_all(&(mvSemaphores[k].waitqueue)); + mv_waitqueue_cleanup(&(mvSemaphores[k].waitqueue)); + } + mvSemaphores[k].flags = 0; + } +} + +/******************************************************************************* +* mvKernelExt_SemCleanup +* +* DESCRIPTION: +* Perform semaphore cleanup actions before module unload +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_SemCleanup(void) +{ + MV_GLOBAL_LOCK(); + + if (mvSemaphores) + { + mvKernelExt_DeleteAll(); + kfree(mvSemaphores); + } + + mvSemaphores = NULL; + + MV_GLOBAL_UNLOCK(); + + remove_proc_entry("mvKernelExtSem", NULL); +} + +/******************************************************************************* +* mvKernelExt_SemCreate +* +* DESCRIPTION: +* Create a new semaphore or open existing one +* +* INPUTS: +* arg - pointer to structure with creation flags and semaphore name +* +* OUTPUTS: +* None +* +* RETURNS: +* Positive value - semaphore ID +* -MVKERNELEXT_EINVAL - invalid parameter passed +* -MVKERNELEXT_ENOMEM - semaphore array is full +* -MVKERNELEXT_ECONFLICT - open existing semaphore with different type +* specified +* +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemCreate(int flags, const char *name) +{ + int k; + mvSemaphoreSTC *sem = NULL; + + if ((flags & MV_SEMAPTHORE_F_TYPE_MASK) == 0) + return -MVKERNELEXT_EINVAL; + + MV_GLOBAL_LOCK(); + + if (flags & MV_SEMAPTHORE_F_OPENEXIST) + { + /* try to find existing semaphore first */ + for (k = 1; k < mv_num_semaphores; k++) + { + sem = mvSemaphores + k; + if (!sem->flags) + continue; + + if (!strncmp(sem->name, name, MV_SEM_NAME_LEN)) + break; + } + + if (k < mv_num_semaphores) + { + /* found */ + if ((sem->flags & MV_SEMAPTHORE_F_TYPE_MASK) != + (flags & MV_SEMAPTHORE_F_TYPE_MASK)) + { + /* semaphore has different type */ + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ECONFLICT; + } + + MV_GLOBAL_UNLOCK(); + return k; + } + } + + /* create semaphore */ + for (k = 1; k < mv_num_semaphores; k++) + { + if (mvSemaphores[k].flags == 0) + break; + } + if (k >= mv_num_semaphores) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ENOMEM; + } + sem = mvSemaphores + k; + + memset(sem, 0, sizeof(*sem)); + mv_waitqueue_init(&(sem->waitqueue)); + + sem->flags = flags & MV_SEMAPTHORE_F_TYPE_MASK; + + if (sem->flags == MV_SEMAPTHORE_F_MTX) + sem->count = 1; + if (sem->flags == MV_SEMAPTHORE_F_BINARY) + sem->count = (flags & MV_SEMAPTHORE_F_COUNT_MASK) ? 1 : 0; + if (sem->flags == MV_SEMAPTHORE_F_COUNT) + sem->count = flags & MV_SEMAPTHORE_F_COUNT_MASK; + + strncpy(sem->name, name, MV_SEM_NAME_LEN); + sem->name[MV_SEM_NAME_LEN] = 0; + + MV_GLOBAL_UNLOCK(); + return k; +} + +#define SEMAPHORE_BY_ID(semid) \ + MV_GLOBAL_LOCK(); \ + if (unlikely(semid == 0 || semid >= mv_num_semaphores)) \ + { \ +ret_einval: \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_EINVAL; \ + } \ + sem = mvSemaphores + semid; \ + if (unlikely(!sem->flags)) \ + goto ret_einval; + +#define TASK_WILL_WAIT(tsk) \ + struct mv_task *p; \ + if (unlikely((p = gettask_cr(tsk)) == NULL)) \ + { \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_ENOMEM; \ + } +#define CHECK_SEM() \ + if (unlikely(!sem->flags)) \ + { \ + MV_GLOBAL_UNLOCK(); \ + return -MVKERNELEXT_EDELETED; \ + } + +/******************************************************************************* +* mvKernelExt_SemDelete +* +* DESCRIPTION: +* Destroys semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL if bad ID passed +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemDelete(int semid) +{ + mvSemaphoreSTC *sem; + + SEMAPHORE_BY_ID(semid); + mv_waitqueue_wake_all(&(sem->waitqueue)); + mv_waitqueue_cleanup(&(sem->waitqueue)); + sem->flags = 0; + + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* mvKernelExt_SemSignal +* +* DESCRIPTION: +* Signals to semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_EPERM - Mutex semaphore is locked by another task +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemSignal(int semid) +{ + mvSemaphoreSTC *sem; + + SEMAPHORE_BY_ID(semid); + + if (sem->flags & MV_SEMAPTHORE_F_MTX) + { + if (unlikely(sem->owner != current)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EPERM; + } + sem->count++; + if (sem->count > 0) + { + sem->owner = NULL; + mv_waitqueue_wake_first(&(sem->waitqueue)); + } + } else { + if (sem->flags & MV_SEMAPTHORE_F_COUNT) + sem->count++; + else + sem->count = 1; /* binary */ + mv_waitqueue_wake_first(&(sem->waitqueue)); + } + +#ifdef MV_SEM_STAT + sem->gcount++; +#endif + MV_GLOBAL_UNLOCK(); + return 0; +} + +/******************************************************************************* +* mvKernelExt_SemUnlockMutexes +* +* DESCRIPTION: +* Unlock all mutexes locked by dead task +* +* INPUTS: +* owner - pointer to kernel's structure of dead task +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +static void mvKernelExt_SemUnlockMutexes( + struct task_struct *owner +) +{ + int k; + for (k = 1; k < mv_num_semaphores; k++) + { + mvSemaphoreSTC *sem = mvSemaphores + k; + + if ((sem->flags & MV_SEMAPTHORE_F_MTX) != 0 + && sem->owner == owner) + { + sem->count = 1; + sem->owner = NULL; + mv_waitqueue_wake_first(&(sem->waitqueue)); + } + } +} + +/******************************************************************************* +* mvKernelExt_SemTryWait_common +* +* DESCRIPTION: +* Try to acquire semaphore without waiting +* +* INPUTS: +* sem - pointer to semaphore structure +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EBUSY - semaphore cannot be taken immediatelly +* +* COMMENTS: +* +*******************************************************************************/ +static int mvKernelExt_SemTryWait_common( + mvSemaphoreSTC *sem +) +{ + if (sem->flags & MV_SEMAPTHORE_F_MTX) + { + if (sem->count > 0) + { + sem->count--; + sem->owner = current; + } else { + if (sem->owner == current) + sem->count--; + else + return -MVKERNELEXT_EBUSY; + } + } else { + if (sem->count <= 0) + return -MVKERNELEXT_EBUSY; + sem->count--; + } +#ifdef MV_SEM_STAT + sem->tcount++; +#endif + return 0; +} + +/******************************************************************************* +* mvKernelExt_SemTryWait +* +* DESCRIPTION: +* Try to acquire semaphore without waiting +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_EBUSY - semaphore cannot be taken immediatelly +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemTryWait(int semid) +{ + mvSemaphoreSTC *sem; + int ret; + + SEMAPHORE_BY_ID(semid); + + ret = mvKernelExt_SemTryWait_common(sem); + + MV_GLOBAL_UNLOCK(); + return ret; +} + + +/******************************************************************************* +* mvKernelExt_SemWait +* +* DESCRIPTION: +* Wait for semaphore +* +* INPUTS: +* semid - semaphore ID +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemWait(int semid) +{ + mvSemaphoreSTC *sem; + + SEMAPHORE_BY_ID(semid); + + if (mvKernelExt_SemTryWait_common(sem) != 0) + { + TASK_WILL_WAIT(current); +#ifdef MV_SEM_STAT + sem->wcount++; +#endif + if (sem->flags & MV_SEMAPTHORE_F_MTX) + { + do { + if (unlikely(mv_do_short_wait_on_queue(&(sem->waitqueue), p, &(sem->owner)))) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + CHECK_SEM(); + } while (sem->count <= 0); + + sem->owner = p->task; + } else { + do { + if (unlikely(mv_do_wait_on_queue(&(sem->waitqueue), p))) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + CHECK_SEM(); + } while (sem->count == 0); + } + sem->count--; + +#ifdef MV_SEM_STAT + sem->tcount++; +#endif + } + + MV_GLOBAL_UNLOCK(); + return 0; +} + + +/******************************************************************************* +* mvKernelExt_SemWaitTimeout +* +* DESCRIPTION: +* Wait for semaphore +* +* INPUTS: +* semid - semaphore ID +* timeout - timeout in milliseconds +* +* OUTPUTS: +* None +* +* RETURNS: +* Zero if successful +* -MVKERNELEXT_EINVAL - bad ID passed +* -MVKERNELEXT_ENOMEM - current task is not registered +* and task array is full +* -MVKERNELEXT_EINTR - wait interrupted +* -MVKERNELEXT_ETIMEOUT - wait timeout +* +* COMMENTS: +* +*******************************************************************************/ +int mvKernelExt_SemWaitTimeout( + int semid, + unsigned long timeout +) +{ + mvSemaphoreSTC *sem; + + SEMAPHORE_BY_ID(semid); + + if (mvKernelExt_SemTryWait_common(sem) != 0) + { + TASK_WILL_WAIT(current); +#ifdef MV_SEM_STAT + sem->wcount++; +#endif +#if HZ != 1000 + timeout += 1000 / HZ - 1; + timeout /= 1000 / HZ; +#endif + do { + timeout = mv_do_wait_on_queue_timeout(&(sem->waitqueue), p, timeout); + CHECK_SEM(); + if (!timeout) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_ETIMEOUT; + } + if (timeout == (unsigned long)(-1)) + { + MV_GLOBAL_UNLOCK(); + return -MVKERNELEXT_EINTR; + } + } while (sem->count <= 0); + + if (sem->flags & MV_SEMAPTHORE_F_MTX) + sem->owner = p->task; + + sem->count--; +#ifdef MV_SEM_STAT + sem->tcount++; +#endif + } + + MV_GLOBAL_UNLOCK(); + return 0; +} + +EXPORT_SYMBOL(mvKernelExt_SemCreate); +EXPORT_SYMBOL(mvKernelExt_SemDelete); +EXPORT_SYMBOL(mvKernelExt_SemSignal); +EXPORT_SYMBOL(mvKernelExt_SemTryWait); +EXPORT_SYMBOL(mvKernelExt_SemWait); +EXPORT_SYMBOL(mvKernelExt_SemWaitTimeout); Index: drivers/net/ethernet/mvebu_net/prestera/Makefile =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/Makefile (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/Makefile (working copy) @@ -0,0 +1,26 @@ +# +# Makefile for the Marvell Prestera driver. +# + +obj-$(CONFIG_MV_INCLUDE_PRESTERA) += mv_prestera.o + +ifeq ($(CONFIG_ARCH_MVEBU),y) +ccflags-y += -Idrivers/net/ethernet/mvebu_net/prestera/pci \ + -Idrivers/net/ethernet/mvebu_net/prestera/platform \ + -Iarch/arm/plat-orion \ + -Idrivers +else +ccflags-y += -Idrivers/net/ethernet/marvell/prestera/pci \ + -Idrivers/net/ethernet/marvell/prestera/platform \ + -Iarch/arm/plat-armada \ + -Iarch/arm/plat-armada/mv_drivers_lsp \ + -Idrivers +endif + +mv_prestera-objs := platform/mv_prestera.o platform/mv_prestera_pltfm.o platform/mv_prestera_irq.o platform/mv_prestera_smi.o platform/mv_pss_api.o + +obj-$(CONFIG_MV_INCLUDE_PRESTERA_KERNELEXT) += kerneldrv/2_6/mv_KernelExt.o +mv_prestera-objs += platform/presteraPpDriverPci.o platform/presteraPpDriverPciHalf.o platform/presteraPpDriverPexMbus.o + +ccflags-y += -DMV_CPU_LE + Index: drivers/net/ethernet/mvebu_net/prestera/pci/Makefile =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/pci/Makefile (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/pci/Makefile (working copy) @@ -0,0 +1,29 @@ +# +# Makefile for the Marvell Prestera Device Driver +# +ifneq ($(MACHINE),) +include $(srctree)/$(MACHINE)/config/mvRules.mk +endif + +CPU_ARCH= ARM +ifeq ($(CONFIG_CPU_BIG_ENDIAN),y) +ENDIAN = BE +else +ENDIAN = LE +endif + +MVEBU_PP_FLAGS := -DMV_LINUX -DMV_CPU_$(ENDIAN) -DMV_$(CPU_ARCH) + +ccflags-y += $(MVEBU_PP_FLAGS) + +INCLUDE_DIRS += -Idrivers/net/ethernet/mvebu_net/prestera/pci \ + -Idrivers +ifeq ($(CONFIG_ARCH_MVEBU),y) +INCLUDE_DIRS += -I$(srctree)/arch/arm/mach-mvebu/include +else +INCLUDE_DIRS += -Iarch/arm/plat-armada +endif + +ccflags-y += $(INCLUDE_DIRS) + +obj-$(CONFIG_MV_INCLUDE_PRESTERA_PCI) += mv_prestera_pci.o Index: drivers/net/ethernet/mvebu_net/prestera/pci/mv_prestera_pci.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/pci/mv_prestera_pci.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/pci/mv_prestera_pci.c (working copy) @@ -0,0 +1,866 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************** +* mv_prestera_pci.c +* +* DESCRIPTION: +* functions in kernel mode special for pci prestera. +* +* DEPENDENCIES: +* +*******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include "mv_prestera_pci.h" +#include "mv_sysmap_pci.h" + +#ifdef CONFIG_OF +#include "mach/mvCommon.h" +#else +#include "common/mvCommon.h" +#endif + +#ifdef CONFIG_MV_INCLUDE_SERVICECPU +#include "mv_servicecpu/servicecpu.h" +#endif +#ifdef CONFIG_MV_INCLUDE_DRAGONITE_XCAT +#include "mv_drivers_lsp/mv_dragonite/dragonite_xcat.h" +#endif + +/* Macro definitions */ +#undef MV_PP_PCI_DBG +#undef MV_PP_IDT_DBG + +#ifdef MV_PP_PCI_DBG +#define dprintk(a...) printk(a) +#else +#define dprintk(a...) +#endif + +#define SWITCH_TARGET_ID 0x3 +#define DFX_TARGET_ID 0x8 +#define RAM_TARGET_ID 0x0 +#define SHARED_RAM_ATTR_ID 0x3E +#define DRAGONITE_TARGET_ID 0xa + +/* DRAGONTIE */ +#define DRAGONITE_CTRL_REG 0x1c +#define DRAGONITE_POE_CAUSE_IRQ_REG 0x64 +#define DRAGONITE_POE_MASK_IRQ_REG 0x68 +#define DRAGONITE_HOST2POE_IRQ_REG 0x6c +#define DRAGONITE_DEBUGGER_REG 0xF8290 + +#ifdef MV_PP_IDT_DBG +#define idtprintk(a...) printk(a) +#else +#define idtprintk(a...) +#endif + +/* Global variables */ +static const char prestera_drv_name[] = "mvPP_PCI"; +static void __iomem *inter_regs; +static struct idtSwitchConfig idtSwCfg[MAX_NUM_OF_IDT_SWITCH]; + +static struct pci_decoding_window ac3_pci_sysmap[] = { + + /*win_num bar_num offset size(n*64KB) remap + * target_id attr status*/ + /* BAR 1*/ + {0, PXWCR_WIN_BAR_MAP_BAR1, 0x0, _64M, 0x0, + SWITCH_TARGET_ID, 0x0, ENABLE}, + + /* BAR 2*/ + {DFXW, PXWCR_WIN_BAR_MAP_BAR2, DFX_BASE, DFX_SIZE, 0x0, + DFX_TARGET_ID, 0x0, ENABLE}, + + {SCPUW, PXWCR_WIN_BAR_MAP_BAR2, SCPU_BASE, SCPU_SIZE, 0xFFF80000, + RAM_TARGET_ID, SHARED_RAM_ATTR_ID, ENABLE}, + + {DITCMW, PXWCR_WIN_BAR_MAP_BAR2, ITCM_BASE, ITCM_SIZE, 0x0, + DRAGONITE_TARGET_ID, 0x0, ENABLE}, + + {DDTCMW, PXWCR_WIN_BAR_MAP_BAR2, DTCM_BASE, DTCM_SIZE, DRAGONITE_DTCM_OFFSET, + DRAGONITE_TARGET_ID, 0x0, ENABLE}, + + {0xff, PXWCR_WIN_BAR_MAP_BAR2, 0x0, 0, 0x0, + 0, 0x0, TBL_TERM}, +}; + +static struct pci_decoding_window bc2_pci_sysmap[] = { + + /*win_num bar_num offset size(n*64KB) remap + * target_id attr status*/ + /* BAR 1*/ + {0, PXWCR_WIN_BAR_MAP_BAR1, 0x0, _64M, 0x0, + SWITCH_TARGET_ID, 0x0, ENABLE}, + + /* BAR 2*/ + {DFXW, PXWCR_WIN_BAR_MAP_BAR2, DFX_BASE, DFX_SIZE, 0x0, + DFX_TARGET_ID, 0x0, ENABLE}, + + {SCPUW, PXWCR_WIN_BAR_MAP_BAR2, SCPU_BASE, SCPU_SIZE, 0xFFF80000, + RAM_TARGET_ID, SHARED_RAM_ATTR_ID, ENABLE}, + + {0xff, PXWCR_WIN_BAR_MAP_BAR2, 0x0, 0, 0x0, + 0, 0x0, TBL_TERM}, +}; + + +static struct pci_decoding_window *get_pci_sysmap(struct pci_dev *pdev) +{ + switch (pdev->device) { + + case MV_BOBCAT2_DEV_ID: + return bc2_pci_sysmap; + case MV_ALLEYCAT3_DEV_ID: + return ac3_pci_sysmap; + default: + return NULL; + } +} + +/******************************************************************************* +* mv_set_pex_bars +* +* +*******************************************************************************/ +static void mv_set_pex_bars(uint8_t pex_nr, uint8_t bar_nr, int enable) +{ + int val; + + dprintk("%s: pex_nr %d, bar_nr %d, enable:%d\n", __func__, pex_nr, + bar_nr, enable); + + val = readl(inter_regs + PEX_BAR_CTRL_REG(pex_nr, bar_nr)); + + if (enable == ENABLE) + writel(val | PXBCR_BAR_EN, + inter_regs + PEX_BAR_CTRL_REG(pex_nr, bar_nr)); + else + writel(val & ~(PXBCR_BAR_EN), + inter_regs + PEX_BAR_CTRL_REG(pex_nr, bar_nr)); +} + +/******************************************************************************* +* mv_resize_bar +* +* +*******************************************************************************/ +static void mv_resize_bar(uint8_t pex_nr, uint8_t bar_nr, uint32_t bar_size) +{ + /* Disable BAR before reconfiguration */ + mv_set_pex_bars(pex_nr, bar_nr, DISABLE); + + /* Resize */ + writel(bar_size, inter_regs + PEX_BAR_CTRL_REG(pex_nr, bar_nr)); + dprintk("PEX_BAR_CTRL_REG(%d, %d) = 0x%x\n", pex_nr, bar_nr, + readl(inter_regs + PEX_BAR_CTRL_REG(pex_nr, bar_nr))); + + /* Enable BAR */ + mv_set_pex_bars(pex_nr, bar_nr, ENABLE); +} + +/******************************************************************************* +* mv_read_and_assign_bars +* +* +*******************************************************************************/ +static int mv_read_and_assign_bars(struct pci_dev *pdev, int resno) +{ + struct resource *res = pdev->resource + resno; + int reg, err; + + dprintk("before reassign: r_start 0x%x, r_end: 0x%x, r_flags 0x%lx\n", + res->start, res->end, res->flags); + + reg = PCI_BASE_ADDRESS_0 + (resno << 2); + __pci_read_base(pdev, pci_bar_unknown, res, reg); + err = pci_assign_resource(pdev, resno); + + dprintk("after reassign: r_start 0x%x, r_end: 0x%x, r_flags 0x%lx\n", + res->start, res->end, res->flags); + + return err; +} + +/******************************************************************************* +* mv_configure_win_bar +* +* +*******************************************************************************/ +static int mv_configure_win_bar(struct pci_decoding_window *win_map, struct pci_dev *pdev) +{ + uint8_t target, bar_nr; + int io_base_bar[2], base_addr, val, win_ctrl_reg, win_base_reg, win_remap_reg; + + io_base_bar[0] = pci_resource_start(pdev, MV_PCI_BAR_1); + io_base_bar[1] = pci_resource_start(pdev, MV_PCI_BAR_2); + + for (target = 0; win_map[target].enable != TBL_TERM; target++) { + if (win_map[target].enable != ENABLE) + continue; + + val = (SIZE_TO_BAR_REG(win_map[target].size) | + (win_map[target].target_id << PXWCR_TARGET_OFFS) | + (win_map[target].attr << PXWCR_ATTRIB_OFFS) | + win_map[target].win_bar_map | PXWCR_WIN_EN); + + dprintk("targ size 0x%x, size reg 0x%x, val 0x%x\n", win_map[target].size, + SIZE_TO_BAR_REG(win_map[target].size), val); + + bar_nr = win_map[target].win_bar_map >> PXWCR_WIN_BAR_MAP_OFFS; + base_addr = io_base_bar[bar_nr] + win_map[target].base_offset; + + switch (win_map[target].win_num) { + case 0: + case 1: + case 2: + case 3: + win_ctrl_reg = PEX_WIN0_3_CTRL_REG(PEX_0, win_map[target].win_num); + win_base_reg = PEX_WIN0_3_BASE_REG(PEX_0, win_map[target].win_num); + win_remap_reg = PEX_WIN0_3_REMAP_REG(PEX_0, win_map[target].win_num); + break; + case 4: + case 5: + win_ctrl_reg = PEX_WIN4_5_CTRL_REG(PEX_0, win_map[target].win_num); + win_base_reg = PEX_WIN4_5_BASE_REG(PEX_0, win_map[target].win_num); + win_remap_reg = PEX_WIN4_5_REMAP_REG(PEX_0, win_map[target].win_num); + break; + default: + dev_err(&pdev->dev, "Not supported decoding window\n"); + return -ENODEV; + } + + dprintk("pex_win %d for bar%d = 0x%x\n", win_map[target].win_num, + bar_nr+1, readl(inter_regs + win_ctrl_reg)); + + writel(base_addr, inter_regs + win_base_reg); + writel(win_map[target].remap | PXWRR_REMAP_EN, inter_regs + win_remap_reg); + writel(val, inter_regs + win_ctrl_reg); + + dprintk("BAR%d: pex_win_ctrl = 0x%x, pex_win_base 0 = 0x%x\n", + bar_nr+1, readl(inter_regs + win_ctrl_reg), readl(inter_regs + win_base_reg)); + } + + return 0; +} + +static void mv_release_pci_resources(struct pci_dev *pdev) +{ + int i; + + /* Release all resources which were assigned */ + for (i = 0; i < PCI_STD_RESOURCE_END; i++) { + struct resource *res = pdev->resource + i; + if (res->parent) + release_resource(res); + } +} + +static int mv_calc_bar_size(struct pci_decoding_window *win_map, uint8_t bar) +{ + uint8_t target, bar_nr; + int size = 0; + + for (target = 0; win_map[target].enable != TBL_TERM; target++) { + if (win_map[target].enable != ENABLE) + continue; + + bar_nr = (win_map[target].win_bar_map >> PXWCR_WIN_BAR_MAP_OFFS) + 1; + if (bar_nr != bar) + continue; + + size += win_map[target].size; + } + + /* Round up to next power of 2 if needed */ + if (!MV_IS_POWER_OF_2(size)) + size = (1 << (mvLog2(size) + 1)); + + dprintk("%s: calculated bar size %d\n", __func__, size); + return size; + +} + +/******************************************************************************* +* mv_reconfig_bars +* +* PCI device BAR Re-configuration +* +*******************************************************************************/ +static int mv_reconfig_bars(struct pci_dev *pdev, struct pci_decoding_window *prestera_sysmap_bar) +{ + int i, err, size; + + /* For some configurations BAR1 or BAR2 occupied whole pci address space + * and BAR0 which is needed to reconfigure other bars is not assigned + * and therefore unreachable. If BAR0 is not assigned, assigned it to + * be able to resize BAR1 and BAR2 + */ + if (!pdev->resource->parent) { + mv_release_pci_resources(pdev); + err = mv_read_and_assign_bars(pdev, MV_PCI_BAR_INTER_REGS); + if (err != 0) + return err; + } + + inter_regs = pci_iomap(pdev, MV_PCI_BAR_INTER_REGS, _1M); + if (inter_regs == NULL) { + dev_err(&pdev->dev, "failed to remap registers\n"); + return -ENODEV; + } + dprintk("%s: inter_regs 0x%p\n", __func__, inter_regs); + + /* Resize BAR1 (64MB for SWITCH) */ + size = mv_calc_bar_size(prestera_sysmap_bar, BAR_1); + mv_resize_bar(PEX_0, BAR_1, SIZE_TO_BAR_REG(size)); + + /* Resize BAR2 (1MB for DFX and Dragonite) */ + size = mv_calc_bar_size(prestera_sysmap_bar, BAR_2); + mv_resize_bar(PEX_0, BAR_2, SIZE_TO_BAR_REG(size)); + + /* Unmap inter_regs - will be mapped again after BAR reassignment */ + iounmap(inter_regs); + + mv_release_pci_resources(pdev); + + /* + * Now when all PCI BARs are reconfigured, read them again and reassign + * resources + */ + err = mv_read_and_assign_bars(pdev, MV_PCI_BAR_1); + if (err != 0) + return err; + + err = mv_read_and_assign_bars(pdev, MV_PCI_BAR_2); + if (err != 0) + return err; + + err = mv_read_and_assign_bars(pdev, MV_PCI_BAR_INTER_REGS); + + inter_regs = pci_iomap(pdev, MV_PCI_BAR_INTER_REGS, _1M); + if (inter_regs == NULL) { + dev_err(&pdev->dev, "failed to remap registers\n"); + return -ENODEV; + } + dprintk("%s: inter_regs 0x%p\n", __func__, inter_regs); + + /* Disable all windows which point to BAR1 and BAR2 */ + for (i = 0; i < 6; i++) { + if (i < 4) { + writel(0, inter_regs + PEX_WIN0_3_CTRL_REG(PEX_0, i)); + writel(0, inter_regs + PEX_WIN0_3_BASE_REG(PEX_0, i)); + writel(0, inter_regs + PEX_WIN0_3_REMAP_REG(PEX_0, i)); + } else { + writel(0, inter_regs + PEX_WIN4_5_CTRL_REG(PEX_0, i)); + writel(0, inter_regs + PEX_WIN4_5_BASE_REG(PEX_0, i)); + writel(0, inter_regs + PEX_WIN4_5_REMAP_REG(PEX_0, i)); + } + } + + err = mv_configure_win_bar(prestera_sysmap_bar, pdev); + if (err) + return err; + + dprintk("%s: decoding win for BAR1 and BAR2 configured\n", __func__); + + return err; +} + +/******************************************************************************* +* mv_calc_device_address_range +* +* Scan device bars and calc address range +* +*******************************************************************************/ +static void mv_calc_device_address_range(struct pci_dev *dev, unsigned int devInstance, int index) +{ + unsigned int i = 0; + unsigned long startAddr, endAddr; + unsigned long tempStartAddr, tempEndAddr; + + startAddr = pci_resource_start(dev, i); + endAddr = pci_resource_end(dev, i); + + for (i = 2; i < PCI_STD_RESOURCE_END; i += 2) { + tempStartAddr = pci_resource_start(dev, i); + tempEndAddr = pci_resource_end(dev, i); + + if ((tempStartAddr != 0) && (tempEndAddr != 0)) { + if (tempStartAddr < startAddr) + startAddr = tempStartAddr; + if (tempEndAddr > endAddr) + endAddr = tempEndAddr; + } + } + + idtSwCfg[index].idtSwPortCfg.startAddr[devInstance] = startAddr; + idtSwCfg[index].idtSwPortCfg.endAddr[devInstance] = endAddr; +} + +/******************************************************************************* +* mv_find_pci_dev_pp_instances +* +* Scan for PCI devices +* Search for first instance if IDT Switch - defined as Uplink Bus +* Search for second instance if IDT Switch - defined as Downlink Bus +* Search for Marvell devices - count and save for further processing +* +*******************************************************************************/ +static void mv_find_pci_dev_pp_instances(void) +{ + struct pci_dev *dev = NULL; + int index; + + memset(&(idtSwCfg[0]), 0, ((sizeof(struct idtSwitchConfig)) * MAX_NUM_OF_IDT_SWITCH)); + for (index = 0; index < MAX_NUM_OF_IDT_SWITCH; index++) { + idtSwCfg[index].ppIdtSwitchUsBusNum = 0xFF; + idtSwCfg[index].ppIdtSwitchDsBusNum = 0xFF; + } + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + if (dev->vendor == PCI_VENDOR_ID_IDT_SWITCH) { + for (index = 0; index < MAX_NUM_OF_IDT_SWITCH; index++) { + if (idtSwCfg[index].ppIdtSwitchUsBusNum == 0xFF) { + idtSwCfg[index].ppIdtSwitchUsBusNum = dev->bus->number; + idtprintk("idt -%d- us bus number %d\n", index, dev->bus->number); + break; + } + + if (idtSwCfg[index].ppIdtSwitchDsBusNum == 0xFF) { + idtSwCfg[index].ppIdtSwitchDsBusNum = dev->bus->number; + idtprintk("idt -%d- ds bus number %d\n", index, dev->bus->number); + break; + } + + if ((idtSwCfg[index].ppIdtSwitchUsBusNum == dev->bus->number) || + (idtSwCfg[index].ppIdtSwitchDsBusNum == dev->bus->number)) + break; + } + } else if (dev->vendor == MARVELL_VEN_ID) { + if (dev->bus->parent != NULL) { + idtprintk("mrvl dev, bus number %d, parent %d\n", + dev->bus->number, dev->bus->parent->number); + + for (index = 0; index < MAX_NUM_OF_IDT_SWITCH; index++) + if (idtSwCfg[index].ppIdtSwitchDsBusNum == dev->bus->parent->number) + break; + + mv_calc_device_address_range(dev, idtSwCfg[index].numOfPpInstances, index); + idtSwCfg[index].idtSwPortCfg.ppBusNumArray[idtSwCfg[index].numOfPpInstances] = + dev->bus->number; + idtSwCfg[index].numOfPpInstances++; + } + } + } +} + +/******************************************************************************* +* mv_discover_active_pp_instances +* +* Locate the active downlink ports connected to IDT switch +* The idt switch downlink ports are 2 - (2 + ppInstance). +* ports 0 - 1 are for the IDT switch, define which idt port are connected to the pps +* +*******************************************************************************/ +static void mv_discover_active_pp_instances(void) +{ + int i, num; + struct pci_dev *dev = NULL; + unsigned short pciLinkStatusRegVal; + int index; + + for (index = 0; index < MAX_NUM_OF_IDT_SWITCH; index++) { + for (num = 0, i = 0; num < idtSwCfg[index].numOfPpInstances; i++) { + dev = pci_get_bus_and_slot(idtSwCfg[index].ppIdtSwitchDsBusNum, PCI_DEVFN(i + 2, 0)); + if (dev != NULL) { + pci_read_config_word(dev, (int)MV_IDT_SWITCH_PCI_LINK_STATUS_REG, &pciLinkStatusRegVal); + if (pciLinkStatusRegVal & MV_IDT_SWITCH_PCI_LINK_STATUS_REG_ACTIVE_LINK) + idtSwCfg[index].idtSwPortCfg.ppIdtSwitchDsPpDevices[num++] = i + 2; + } + } + } +} + +/******************************************************************************* +* mv_configure_idt_switch_addr_range +* +* +*******************************************************************************/ +static void mv_configure_idt_switch_addr_range(void) +{ + int i; + struct pci_dev *dev = NULL; + int index; + + for (index = 0; index < MAX_NUM_OF_IDT_SWITCH; index++) { + for (i = 0; i < idtSwCfg[index].numOfPpInstances; i++) { + dev = pci_get_bus_and_slot(idtSwCfg[index].ppIdtSwitchDsBusNum, + PCI_DEVFN(idtSwCfg[index].idtSwPortCfg.ppIdtSwitchDsPpDevices[i], 0)); + if (dev != NULL) { + pci_write_config_dword(dev, MV_IDT_SWITCH_PCI_MEM_BASE_REG, + ((idtSwCfg[index].idtSwPortCfg.endAddr[i] & + MV_IDT_SWITCH_PCI_MEM_BASE_REG_MASK) | + (((idtSwCfg[index].idtSwPortCfg.startAddr[i]) & + MV_IDT_SWITCH_PCI_MEM_BASE_REG_MASK) >> 16))); + } + } + + if (idtSwCfg[index].numOfPpInstances > 0) { + dev = pci_get_bus_and_slot(idtSwCfg[index].ppIdtSwitchUsBusNum, PCI_DEVFN(0, 0)); + pci_write_config_dword(dev, MV_IDT_SWITCH_PCI_MEM_BASE_REG, + ((idtSwCfg[index].idtSwPortCfg.endAddr[idtSwCfg[index].numOfPpInstances - 1] & + MV_IDT_SWITCH_PCI_MEM_BASE_REG_MASK) | + (((idtSwCfg[index].idtSwPortCfg.startAddr[0]) & + MV_IDT_SWITCH_PCI_MEM_BASE_REG_MASK) >> MV_IDT_SWITCH_PCI_MEM_BASE_REG_SHIFT))); + } + } +} + +/******************************************************************************* +* mv_print_idt_switch_configuration +* +* +*******************************************************************************/ +static void mv_print_idt_switch_configuration(void) +{ + int i; + unsigned int pciBaseAddr; + struct pci_dev *dev = NULL; + int index; + + for (index = 0; index < MAX_NUM_OF_IDT_SWITCH; index++) { + if (idtSwCfg[index].numOfPpInstances > 0) { + idtprintk("\n"); + idtprintk("PEX IDT Switch Configuration\n"); + idtprintk("============================\n"); + idtprintk(" %02x:00.0 (IDT Uplink port)\n", + idtSwCfg[index].ppIdtSwitchUsBusNum); + idtprintk(" |\n"); + idtprintk("---------------------|---------------------\n"); + idtprintk("%02x:%02x.0 %02x:%02x.0 %02x:%02x.0 %02x:%02x.0 (IDT Downlink ports)\n", + idtSwCfg[index].ppIdtSwitchDsBusNum, + idtSwCfg[index].idtSwPortCfg.ppIdtSwitchDsPpDevices[0], + idtSwCfg[index].ppIdtSwitchDsBusNum, + idtSwCfg[index].idtSwPortCfg.ppIdtSwitchDsPpDevices[1], + idtSwCfg[index].ppIdtSwitchDsBusNum, + idtSwCfg[index].idtSwPortCfg.ppIdtSwitchDsPpDevices[2], + idtSwCfg[index].ppIdtSwitchDsBusNum, + idtSwCfg[index].idtSwPortCfg.ppIdtSwitchDsPpDevices[3]); + idtprintk("%02x:00.0 %02x:00.0 %02x:00.0 %02x:00.0 (Marvell Devices)\n", + idtSwCfg[index].idtSwPortCfg.ppBusNumArray[0], + idtSwCfg[index].idtSwPortCfg.ppBusNumArray[1], + idtSwCfg[index].idtSwPortCfg.ppBusNumArray[2], + idtSwCfg[index].idtSwPortCfg.ppBusNumArray[3]); + idtprintk("\n"); + } + } + + for (index = 0; index < MAX_NUM_OF_IDT_SWITCH; index++) { + for (i = 0; i < idtSwCfg[index].numOfPpInstances; i++) { + dev = pci_get_bus_and_slot(idtSwCfg[index].ppIdtSwitchDsBusNum, + PCI_DEVFN(idtSwCfg[index].idtSwPortCfg.ppIdtSwitchDsPpDevices[i], 0)); + if (dev != NULL) { + pci_read_config_dword(dev, (int)MV_IDT_SWITCH_PCI_MEM_BASE_REG, &pciBaseAddr); + idtprintk("PEX IDT Downlink port [bus 0x%x device 0x%x] Base address 0x%08x\n", + idtSwCfg[index].ppIdtSwitchDsBusNum, + idtSwCfg[index].idtSwPortCfg.ppIdtSwitchDsPpDevices[i], pciBaseAddr); + } + } + + if (idtSwCfg[index].numOfPpInstances > 0) { + dev = pci_get_bus_and_slot(idtSwCfg[index].ppIdtSwitchUsBusNum, PCI_DEVFN(0, 0)); + pci_read_config_dword(dev, (int)MV_IDT_SWITCH_PCI_MEM_BASE_REG, &pciBaseAddr); + idtprintk("PEX IDT Uplink port [bus 0x%x device 0x%x] Base address 0x%08x\n\n", + idtSwCfg[index].ppIdtSwitchUsBusNum, 0, pciBaseAddr); + } + } + idtprintk("\n"); +} + +/******************************************************************************* +* mv_reconfig_idt_switch +* +* +*******************************************************************************/ +void mv_reconfig_idt_switch(void) +{ + /* Scan for PCI devices - Marvell & IDT Switch */ + mv_find_pci_dev_pp_instances(); + + /* Discover Marvell devices connected to IDT Switch */ + mv_discover_active_pp_instances(); + + /* Configure IDT Switch address range */ + mv_configure_idt_switch_addr_range(); + + /* Print configuration */ + mv_print_idt_switch_configuration(); +} + +#ifdef CONFIG_MV_INCLUDE_DRAGONITE_XCAT +int __init mv_msys_dragonite_init(struct pci_dev *pdev, struct pci_decoding_window *prestera_sysmap_bar) +{ + void __iomem * const *iomap = NULL; + void __iomem *switch_reg, *dfx_reg; + struct resource *dragonite_resources; + struct dragonite_info *dragonite_pci_data; + struct platform_device *mv_dragonite_dev; + const int msys_interregs_phys_base = + pci_resource_start(pdev, MV_PCI_BAR_INTER_REGS); + const int itcm_phys = pci_resource_start(pdev, MV_PCI_BAR_2) + + prestera_sysmap_bar[DITCMW].base_offset; + const int dtcm_phys = pci_resource_start(pdev, MV_PCI_BAR_2) + + prestera_sysmap_bar[DDTCMW].base_offset; + + dragonite_resources = devm_kzalloc(&pdev->dev, 4 * sizeof(struct resource), GFP_KERNEL); + if (!dragonite_resources) + return -ENOMEM; + + dragonite_pci_data = devm_kzalloc(&pdev->dev, sizeof(struct dragonite_info), GFP_KERNEL); + if (!dragonite_pci_data) + return -ENOMEM; + + mv_dragonite_dev = devm_kzalloc(&pdev->dev, sizeof(struct platform_device), GFP_KERNEL); + if (!mv_dragonite_dev) + return -ENOMEM; + + dev_dbg(&pdev->dev, "Dragonite init...\n"); + + iomap = pcim_iomap_table(pdev); + inter_regs = iomap[MV_PCI_BAR_INTER_REGS]; + switch_reg = iomap[MV_PCI_BAR_1]; + dfx_reg = iomap[MV_PCI_BAR_2]; + + dragonite_resources[0].start = itcm_phys; + dragonite_resources[0].end = itcm_phys + ITCM_SIZE - 1; + dragonite_resources[0].flags = IORESOURCE_MEM; + + dragonite_resources[1].start = dtcm_phys; + dragonite_resources[1].end = dtcm_phys + DTCM_SIZE - 1; + dragonite_resources[1].flags = IORESOURCE_MEM; + + dragonite_resources[2].start = msys_interregs_phys_base; + dragonite_resources[2].end = msys_interregs_phys_base + _1M - 1; + dragonite_resources[2].flags = IORESOURCE_MEM; + + dragonite_resources[3].start = pdev->irq; + dragonite_resources[3].end = pdev->irq; + dragonite_resources[3].flags = IORESOURCE_IRQ; + + dragonite_pci_data->ctrl_reg = (void *)(switch_reg + DRAGONITE_CTRL_REG); + dragonite_pci_data->jtag_reg = (void *)(dfx_reg + DRAGONITE_DEBUGGER_REG); + dragonite_pci_data->poe_cause_irq_reg = (void *)(switch_reg + DRAGONITE_POE_CAUSE_IRQ_REG); + dragonite_pci_data->poe_mask_irq_reg = (void *)(switch_reg + DRAGONITE_POE_MASK_IRQ_REG); + dragonite_pci_data->host2poe_irq_reg = (void *)(switch_reg + DRAGONITE_HOST2POE_IRQ_REG); + + mv_dragonite_dev->name = "dragonite_xcat"; + mv_dragonite_dev->id = -1; + mv_dragonite_dev->dev.platform_data = dragonite_pci_data; + mv_dragonite_dev->num_resources = 4; + mv_dragonite_dev->resource = dragonite_resources; + + platform_device_register(mv_dragonite_dev); + + return 0; +} +#endif + +/******************************************************************************* +* prestera_pci_probe +* +* PCI device probe function +* +*******************************************************************************/ +static int prestera_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err; + static int pexSwitchConfigure; + struct pci_decoding_window *prestera_sysmap_bar; +#ifdef CONFIG_MV_INCLUDE_SERVICECPU + void __iomem * const *iomap; +#endif + + switch (pdev->device) { + + case MV_IDT_SWITCH_DEV_ID_808E: + case MV_IDT_SWITCH_DEV_ID_802B: + if (pexSwitchConfigure == 0) { + mv_reconfig_idt_switch(); + pexSwitchConfigure++; + } + return 0; + + case MV_BOBCAT2_DEV_ID: + case MV_ALLEYCAT3_DEV_ID: + + prestera_sysmap_bar = get_pci_sysmap(pdev); + if (!prestera_sysmap_bar) + return -ENXIO; + + err = pcim_enable_device(pdev); + if (err) + return err; + + /* + * Reconfigure and reassign bars + * BAR0: 1MB for INTER REGS (fixed size, no configuration needed) + * BAR1: 64MB for SWITCH REGS + * BAR2: 1MB for DFX REGS + */ + err = mv_reconfig_bars(pdev, prestera_sysmap_bar); + if (err != 0) + return err; + + err = pcim_iomap_regions(pdev, (1 << MV_PCI_BAR_INTER_REGS) | + (1 << MV_PCI_BAR_1) | + (1 << MV_PCI_BAR_2), prestera_drv_name); + if (err) + return err; +#ifdef CONFIG_MV_INCLUDE_DRAGONITE_XCAT + if (pdev->device == MV_ALLEYCAT3_DEV_ID) + err = mv_msys_dragonite_init(pdev, prestera_sysmap_bar); + if (err) + return err; +#endif + +#ifdef CONFIG_MV_INCLUDE_SERVICECPU + iomap = pcim_iomap_table(pdev); + servicecpu_data.inter_regs_base = iomap[MV_PCI_BAR_INTER_REGS]; + servicecpu_data.pci_win_size = prestera_sysmap_bar[SCPUW].size; + servicecpu_data.pci_win_virt_base = + iomap[MV_PCI_BAR_2] + prestera_sysmap_bar[SCPUW].base_offset; + servicecpu_data.pci_win_phys_base = (void *)(pci_resource_start(pdev, + MV_PCI_BAR_2) + prestera_sysmap_bar[SCPUW].base_offset); + servicecpu_init(); +#endif + break; + + case MV_LION2_DEV_ID: + + err = pcim_enable_device(pdev); + if (err) + return err; + + err = pcim_iomap_regions(pdev, ((1 << MV_PCI_BAR_INTER_REGS) | + (1 << MV_PCI_BAR_1)), + prestera_drv_name); + if (err) + return err; + + break; + + default: + dprintk("%s: unsupported device\n", __func__); + } + + dev_info(&pdev->dev, "%s init completed\n", prestera_drv_name); + return 0; +} + +static void prestera_pci_remove(struct pci_dev *pdev) +{ + dprintk("%s\n", __func__); + +#ifdef CONFIG_MV_INCLUDE_SERVICECPU + servicecpu_deinit(); +#endif +} + +static DEFINE_PCI_DEVICE_TABLE(prestera_pci_tbl) = { + /* Marvell */ + { PCI_DEVICE(PCI_VENDOR_ID_IDT_SWITCH, MV_IDT_SWITCH_DEV_ID_808E)}, + { PCI_DEVICE(PCI_VENDOR_ID_IDT_SWITCH, MV_IDT_SWITCH_DEV_ID_802B)}, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, MV_BOBCAT2_DEV_ID)}, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, MV_LION2_DEV_ID)}, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, MV_ALLEYCAT3_DEV_ID)}, + {} +}; + +static struct pci_driver prestera_pci_driver = { + .name = prestera_drv_name, + .id_table = prestera_pci_tbl, + .probe = prestera_pci_probe, + .remove = prestera_pci_remove, +}; + +static int __init prestera_pci_init(void) +{ + return pci_register_driver(&prestera_pci_driver); +} + +static void __exit prestera_pci_cleanup(void) +{ + pci_unregister_driver(&prestera_pci_driver); +} + +MODULE_AUTHOR("Grzegorz Jaszczyk "); +MODULE_DESCRIPTION("pci device driver for Marvell Prestera family switches"); +MODULE_LICENSE("GPL"); + +module_init(prestera_pci_init); +module_exit(prestera_pci_cleanup); Index: drivers/net/ethernet/mvebu_net/prestera/pci/mv_prestera_pci.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/pci/mv_prestera_pci.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/pci/mv_prestera_pci.h (working copy) @@ -0,0 +1,183 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************** +* mv_prestera_pci.h +* +* DESCRIPTION: +* Includes defines and structures needed by the PCI PP device driver +* +* DEPENDENCIES: +* None. +* +*******************************************************************************/ +#ifndef __MV_PRESTERA_PCI_H +#define __MV_PRESTERA_PCI_H + +/* PCI Devices definition */ +#define MARVELL_VEN_ID (0x11AB) +#define MV_BOBCAT2_DEV_ID (0xFC00) +#define MV_LION2_DEV_ID (0x8000) +#define MV_ALLEYCAT3_DEV_ID (0xF400) +#define PCI_VENDOR_ID_IDT_SWITCH (0x111D) +#define MV_IDT_SWITCH_DEV_ID_808E (0x808E) +#define MV_IDT_SWITCH_DEV_ID_802B (0x802B) + +/* General definition */ +#define ENABLE (1) +#define DISABLE (0) +#define TBL_TERM 0xFF + +/* PCI BAR definition */ +#define PEX_0 (0) +#define BAR_1 (1) +#define BAR_2 (2) + +#define PEX_IRQ_EN BIT(28) +#define PEX_IRQ_EP BIT(31) + +#define PXBCR_BAR_EN (0x00000001) +#define PXBCR_BAR_SIZE_OFFS (16) +#define PXBCR_BAR_SIZE_MASK (0xFFFF << PXBCR_BAR_SIZE_OFFS) +#define PXBCR_BAR_SIZE_ALIGNMENT (0x10000) + +#define SIZE_TO_BAR_REG(size) ((((size) / PXBCR_BAR_SIZE_ALIGNMENT) - 1) << PXBCR_BAR_SIZE_OFFS) + +#define PXWRR_REMAP_EN (BIT(0)) +#define PXWCR_WIN_EN (BIT(0)) /* Window Enable.*/ +#define PXWCR_WIN_BAR_MAP_OFFS (1) /* Mapping to BAR.*/ +#define PXWCR_WIN_BAR_MAP_MASK (BIT(1)) +#define PXWCR_WIN_BAR_MAP_BAR1 (0 << PXWCR_WIN_BAR_MAP_OFFS) +#define PXWCR_WIN_BAR_MAP_BAR2 (1 << PXWCR_WIN_BAR_MAP_OFFS) +#define PXWCR_TARGET_OFFS (4) /*Unit ID */ +#define PXWCR_TARGET_MASK (0xf << PXWCR_TARGET_OFFS) +#define PXWCR_ATTRIB_OFFS (8) /*Target attributes */ + +#define PEX_WIN0_3_CTRL_REG(pexIf, winNum) (0x41820 + (winNum) * 0x10 - (pexIf) * 0x10000) +#define PEX_WIN0_3_BASE_REG(pexIf, winNum) (0x41824 + (winNum) * 0x10 - (pexIf) * 0x10000) +#define PEX_WIN0_3_REMAP_REG(pexIf, winNum) (0x4182C + (winNum) * 0x10 - (pexIf) * 0x10000) +#define PEX_WIN4_5_CTRL_REG(pexIf, winNum) (0x41860 + (winNum - 4) * 0x20 - (pexIf) * 0x10000) +#define PEX_WIN4_5_BASE_REG(pexIf, winNum) (0x41864 + (winNum - 4) * 0x20 - (pexIf) * 0x10000) +#define PEX_WIN4_5_REMAP_REG(pexIf, winNum) (0x4186C + (winNum - 4) * 0x20 - (pexIf) * 0x10000) +#define PEX_WIN4_5_REMAP_HIGH_REG(pexIf, winNum) (0x41870 + (winNum - 4) * 0x20 - (pexIf) * 0x10000) + +/* IDT Switch definition */ +#define MAX_NUM_OF_IDT_SWITCH (2) +#define MAX_NUM_OF_PP (4) +#define MV_IDT_SWITCH_MAX_NUM_OF_PORT (MAX_NUM_OF_PP) + +#define MV_IDT_SWITCH_PCI_MEM_BASE_REG (0x20) +#define MV_IDT_SWITCH_PCI_MEM_BASE_REG_MASK (0xFFFF0000) +#define MV_IDT_SWITCH_PCI_MEM_BASE_REG_SHIFT (16) +#define MV_IDT_SWITCH_PCI_LINK_STATUS_REG (0x52) +#define MV_IDT_SWITCH_PCI_LINK_STATUS_REG_ACTIVE_LINK (0x2000) + +/* IDT Switch structure */ +struct idtSwitchConfig { + unsigned int ppIdtSwitchUsBusNum; + unsigned int ppIdtSwitchDsBusNum; + unsigned int numOfPpInstances; + + struct { + unsigned long startAddr[MV_IDT_SWITCH_MAX_NUM_OF_PORT]; + unsigned long endAddr[MV_IDT_SWITCH_MAX_NUM_OF_PORT]; + unsigned int ppBusNumArray[MV_IDT_SWITCH_MAX_NUM_OF_PORT]; + unsigned int ppIdtSwitchDsPpDevices[MV_IDT_SWITCH_MAX_NUM_OF_PORT]; + } idtSwPortCfg; +}; + +/* PCI BAR enumeration */ +enum { + MV_PCI_BAR_INTER_REGS = 0, + MV_PCI_BAR_1 = 2, + MV_PCI_BAR_2 = 4, +}; + +struct pci_decoding_window { + uint8_t win_num; + uint8_t win_bar_map; + int base_offset; /* offset from BAR base */ + int size; /* have to be 64KB granularity */ + int remap; + uint8_t target_id; + uint8_t attr; + uint8_t enable; +}; + +/* PCI BAR macro */ +#define MV_PEX_IF_REGS_OFFSET(pexIf) (pexIf < 8 ? (0x40000 + ((pexIf) / 4) * 0x40000 + ((pexIf) % 4) * 0x4000) \ + : (0x42000 + ((pexIf) % 8) * 0x40000)) +#define MV_PEX_IF_REGS_BASE(unit) (MV_PEX_IF_REGS_OFFSET(unit)) +#define PEX_BAR_CTRL_REG(pexIf, bar) (MV_PEX_IF_REGS_BASE(pexIf) + 0x1804 + (bar-1)*4) + +/* For msys internal irq */ +#define MV_MBUS_REGS_OFFSET 0x20000 +#define MV_CPUIF_SHARED_REGS_BASE MV_MBUS_REGS_OFFSET +#define CPU_INT_SOURCE_CONTROL_REG(i) (MV_CPUIF_SHARED_REGS_BASE + 0xB00 + (i * 0x4)) + +#define CPU_INT_CLEAR_MASK_OFFS 0xBC +#define MV_CPUIF_LOCAL_REGS_OFFSET 0x21000 +#define CPU_INT_CLEAR_MASK_LOCAL_REG (MV_CPUIF_LOCAL_REGS_OFFSET + CPU_INT_CLEAR_MASK_OFFS) + +#define MSYS_CAUSE_VEC1_REG_OFFS 0x20904 +#define CPU_INT_SOURCE_CONTROL_IRQ_OFFS 28 + +#endif /* __MV_PRESTERA_PCI_H */ Index: drivers/net/ethernet/mvebu_net/prestera/pci/mv_sysmap_pci.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/pci/mv_sysmap_pci.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/pci/mv_sysmap_pci.h (working copy) @@ -0,0 +1,95 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************** +* mv_sysmap_pci.h +* +* DESCRIPTION: +* Includes defines needed by the PCI PP device driver +* +* DEPENDENCIES: +* None. +* +*******************************************************************************/ +#ifndef __MV_SYSMAP_PCI_H +#define __MV_SYSMAP_PCI_H + +/* win nr for BAR2 */ +#define DFXW 1 +#define SCPUW 2 +#define DITCMW 3 +#define DDTCMW 4 + +#define DRAGONITE_DTCM_OFFSET 0x04000000 + +/* BAR 2 sizes */ +#define DFX_SIZE _1M +#define SCPU_SIZE _512K +#define ITCM_SIZE _64K +#define DTCM_SIZE _64K + +/* BAR 2 bases */ +#define DFX_BASE 0x0 +#define SCPU_BASE (DFX_BASE + DFX_SIZE) +#define ITCM_BASE (SCPU_BASE + SCPU_SIZE) +#define DTCM_BASE (ITCM_BASE + ITCM_SIZE) + +#endif /* __MV_SYSMAP_PCI_H */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/Makefile =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/Makefile (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/Makefile (working copy) @@ -0,0 +1,12 @@ +# +# Makefile for the Marvell Prestera Device Driver +# +ifneq ($(MACHINE),) +include $(srctree)/$(MACHINE)/config/mvRules.mk +endif + + +obj-$(CONFIG_MV_INCLUDE_PRESTERA) += mvPrestera.o mv_prestera_pltfm.o +mvPrestera-objs += mv_prestera.o mv_prestera_irq.o mv_prestera_smi.o mv_pss_api.o +mvPrestera-objs += presteraPpDriverPci.o presteraPpDriverPciHalf.o presteraPpDriverPexMbus.o + Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera.c (working copy) @@ -0,0 +1,1854 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************** +* mv_prestera.c +* +* DESCRIPTION: +* functions in kernel mode special for prestera. +* +* DEPENDENCIES: +* +* COMMENTS: +* Please note: this file is shared for: +* axp_lsp_3.4.69 +* msys_lsp_3_4 +* msys_lsp_2_6_32 +* +*******************************************************************************/ +#include "mvOs.h" +#include "mv_prestera_glob.h" +#include "mv_prestera_smi.h" +#include "mv_prestera_smi_glob.h" +#include "mv_prestera_irq.h" +#include "mv_prestera.h" +#include "mv_pss_api.h" +#include "mv_prestera_pci.h" +#ifdef PRESTERA_PP_DRIVER +#include "mv_prestera_pp_driver_glob.h" +#endif + +#if defined(CONFIG_MIPS) && defined(CONFIG_64BIT) +#define MIPS64_CPU +#endif + +#if defined(INTEL64_CPU) || defined(EP3041A) || \ + (defined(CONFIG_MIPS) && defined(CONFIG_64BIT)) +#define EXT_ARCH_64_CPU +#endif + +#if defined(GDA8548) || defined(EP3041A) +#define consistent_sync(x...) +#endif + +#if defined(MIPS64_CPU) || defined(INTEL64_CPU) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#define consistent_sync(x...) +#endif +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 6) +#define consistent_sync(x...) +#endif + +#undef MV_DEBUG + +/* defines */ +#ifdef MV_DEBUG +#define dprintk(a...) printk(a) +#else +#define dprintk(a...) +#endif + +#define MAX_SUPPORTED_INTR (4) + +#define PP_MAPPINGS_MAX 64 + +struct prvPciDeviceQuirks quirks[] = { + {MV_LION2_DEV_ID, PCI_DEV_PEX_EN, PCI_DEV_LION_CONFIG_OFFSET, PCI_DEV_DFX_DIS, {0, 3, 2, 1, 0, 0, 0, 0} }, + {MV_BOBCAT2_DEV_ID, PCI_DEV_PEX_EN, PCI_DEV_BC2_CONFIG_OFFSET, PCI_DEV_DFX_EN, {0, 0, 0, 0, 0, 0, 0, 0} }, + {MV_ALLEYCAT3_DEV_ID, PCI_DEV_PEX_EN, PCI_DEV_AC3_CONFIG_OFFSET, PCI_DEV_DFX_EN, {0, 1, 0, 0, 0, 0, 0, 0} }, + {-1, -1, -1, -1} +}; + +/* local variables and variables */ +static int dev_open_nr; +static int dev_init_done; +static struct prestera_device *prestera_dev; +static int prestera_major = PRESTERA_MAJOR; +static struct cdev prestera_cdev; +static int prestera_ppdev; +static const char *prestera_dev_name = "mvPP"; +static uintptr_t pci_regs_vma_start = CPSS_SWITCH_VIRT_ADDR; +static uintptr_t pci_regs_vma_end = CPSS_SWITCH_VIRT_ADDR; +static uintptr_t pci_dfx_vma_start = CPSS_DFX_VIRT_ADDR; +static uintptr_t pci_dfx_vma_end = CPSS_DFX_VIRT_ADDR; +static uintptr_t pci_conf_vma_start = CPSS_CPU_VIRT_ADDR; +static uintptr_t pci_conf_vma_end = CPSS_CPU_VIRT_ADDR; +static uintptr_t dma_base_vma_start = CPSS_DMA_VIRT_ADDR; +static uintptr_t dma_base_vma_end = CPSS_DMA_VIRT_ADDR + 2 * _1M; +static struct pp_dev *ppdevs[PRV_MAX_PP_DEVICES]; +static int founddevs; +static unsigned int dma_len; +unsigned long dma_base; +static void *dma_area; +static void *dma_tmp_virt; +static dma_addr_t dma_tmp_phys; +/* info for mmap */ +static struct Mmap_Info_stc mmapInfoArr[PP_MAPPINGS_MAX]; +static int mmapInfoArrSize ; +#define M mmapInfoArr[mmapInfoArrSize] + +#ifdef EXT_ARCH_64_CPU +int bspAdv64Malloc32bit; /* in bssBspApis.c */ +#endif + +/************************************************************************ + * + * get_founddev: retrieve number of detected devices + * + */ +unsigned int get_founddev(void) +{ + return founddevs; +} + +/************************************************************************ + * + * prestera_global_init: init global parameters + * + */ +int prestera_global_init(void) +{ + mmapInfoArrSize = 0; + founddevs = 0; + return 0; +} + +/************************************************************************ + * + * get_quirks: retrive quirks instance according to board type + * + */ +static int32_t get_quirks(unsigned short devId) +{ + uint32_t index = 0; + + while (quirks[index].pciId != (-1)) { + if (devId == quirks[index].pciId) + return index; + index++; + } + return -1; +} + +/************************************************************************ + * + * prestera_mapped_virt2phys: convert userspace address to physical + * Only for mmaped areas + * + */ +static mv_phys_addr_t prestera_mapped_virt2phys(unsigned long address) +{ + if (address >= dma_base_vma_start && address < dma_base_vma_end) { + address -= dma_base_vma_start; + address += dma_base; + return (mv_phys_addr_t)address; + } + if (address >= pci_regs_vma_start && address < pci_regs_vma_end) { + struct pp_dev *dev; + int i; + + for (i = 0; i < founddevs; i++) { + dev = ppdevs[i]; + if (address >= dev->ppregs.mmapbase && address < dev->ppregs.mmapbase + dev->ppregs.mmapsize) { + address -= dev->ppregs.mmapbase; + return dev->ppregs.phys + address; + } + } + /* should never happen? */ + return 0; + } + + /* default */ + return 0; +} + + +/************************************************************************ + * + * prestera_DmaRead: bspDmaRead() wrapper + */ +static int prestera_dma_read(unsigned long address, + unsigned long length, + unsigned long burstLimit, + unsigned long buffer) +{ + unsigned long bufferPhys; + unsigned long tmpLength; + mv_phys_addr_t phys; + + dprintk("%s(address=0x%lx, length=0x%lx, burstLimit=0x%lx, buffer=0x%lx)\n", + __func__, + (unsigned long)(address), + (unsigned long)(length), + (unsigned long)burstLimit, + (unsigned long)buffer); + + phys = prestera_mapped_virt2phys(address); + if (!phys) + return -EFAULT; + + bufferPhys = prestera_mapped_virt2phys(buffer); + if (bufferPhys) + return bspDmaRead(phys, length, burstLimit, (unsigned long *)bufferPhys); + + /* use dma_tmp buffer */ + while (length > 0) { + tmpLength = (length > (PAGE_SIZE / 4)) ? PAGE_SIZE / 4 : length; + + if (bspDmaRead(phys, tmpLength, burstLimit, (unsigned long *)dma_tmp_phys)) + return -EFAULT; + + length -= tmpLength; + tmpLength *= 4; + if (copy_to_user((void *)buffer, dma_tmp_virt, tmpLength)) + return -EFAULT; + + phys += tmpLength; + buffer += tmpLength; + } + return 0; +} + +/************************************************************************ + * + * prestera_DmaWrite: bspDmaRead() wrapper + */ +static int prestera_dma_write(unsigned long address, + unsigned long length, + unsigned long burstLimit, + unsigned long buffer) +{ + unsigned long bufferPhys; + unsigned long tmpLength; + mv_phys_addr_t phys; + + dprintk("%s(address=0x%lx, length=0x%lx, burstLimit=0x%lx, buffer=0x%lx)\n", + __func__, + (unsigned long)(address), + (unsigned long)(length), + (unsigned long)burstLimit, + (unsigned long)buffer); + + phys = prestera_mapped_virt2phys(address); + if (!phys) + return -EFAULT; + + bufferPhys = prestera_mapped_virt2phys(buffer); + if (bufferPhys) + return bspDmaWrite(phys, (unsigned long *)bufferPhys, length, burstLimit); + /* use dma_tmp buffer */ + while (length > 0) { + tmpLength = (length > (PAGE_SIZE / 4)) ? PAGE_SIZE / 4 : length; + + if (copy_from_user(dma_tmp_virt, (void *)buffer, tmpLength * 4)) + return -EFAULT; + + if (bspDmaWrite(phys, (unsigned long *)dma_tmp_phys, tmpLength, burstLimit)) + return -EFAULT; + + length -= tmpLength; + tmpLength *= 4; + phys += tmpLength; + buffer += tmpLength; + } + return 0; +} + +static loff_t prestera_lseek(struct file *filp, loff_t off, int whence) +{ + struct prestera_device *dev; + loff_t newpos; + + dev = (struct prestera_device *)filp->private_data; + + dprintk("%s(whence=0x%lx, off=0x%lx)\n", + __func__, + (unsigned long)(whence), + (unsigned long)(off)); + + switch (whence) { + case 0: /* SEEK_SET */ + newpos = off; + break; + + case 1: /* SEEK_CUR */ + newpos = filp->f_pos + off; + break; + + case 2: /* SEEK_END */ + newpos = dev->size + off; + break; + + default: /* can't happend */ + printk(KERN_ERR "%s whence %d ERROR\n", __func__, whence); + return -EINVAL; + } + if (newpos < 0) + return -EINVAL; + + if (newpos >= dev->size) + return -EINVAL; + + filp->f_pos = newpos; + return newpos; +} + +/* + * find device index + * return -1 if not found + */ +static int find_pp_device(uint32_t busNo, uint32_t devSel, uint32_t funcNo) +{ + int i; + struct pp_dev *pp; + for (i = 0; i < founddevs; i++) { + pp = ppdevs[i]; + if (pp->busNo != busNo || + pp->devSel != devSel || + pp->funcNo != funcNo) + continue; + /* found */ + return i; + } + return -1; /* not found */ +} + +/* + * configure virtual addresses + */ +static void ppdev_set_vma(struct pp_dev *dev, int devIdx, unsigned long regsSize) +{ + if (dev->ppregs.mmapbase != 0) + return; /* already configured */ + + /* allocate virtual addresses */ + dev->ppregs.mmapbase = pci_regs_vma_end; + dev->ppregs.mmapsize = regsSize; + pci_regs_vma_end += dev->ppregs.mmapsize; + M.map_type = MMAP_INFO_TYPE_PP_REGS_E; + M.index = devIdx; + M.addr = dev->ppregs.mmapbase; + M.length = dev->ppregs.mmapsize; + M.offset = 0; + mmapInfoArrSize++; + + dev->config.mmapbase = pci_conf_vma_end; + dev->config.mmapsize = dev->config.size; + pci_conf_vma_end += dev->config.mmapsize; + M.map_type = MMAP_INFO_TYPE_PP_CONF_E; + M.index = devIdx; + M.addr = dev->config.mmapbase; + M.length = dev->config.mmapsize; + M.offset = dev->config.mmapoffset; + mmapInfoArrSize++; + + if (dev->dfx.phys != 0) { + dev->dfx.mmapbase = pci_dfx_vma_end; + dev->dfx.mmapsize = dev->dfx.size; + pci_dfx_vma_end += dev->dfx.mmapsize; + M.map_type = MMAP_INFO_TYPE_PP_DFX_E; + M.index = devIdx; + M.addr = dev->dfx.mmapbase; + M.length = dev->dfx.mmapsize; + M.offset = 0; + mmapInfoArrSize++; + } +} + +#ifdef PRESTERA_PP_DRIVER +/* ppDriver */ +static int presteraPpDriver_ioctl(unsigned int cmd, unsigned long arg) +{ + struct pp_dev *dev; + + if (cmd == PRESTERA_PP_DRIVER_IO) { + struct mvPpDrvDriverIo_STC io; + + if (copy_from_user(&io, (struct mvPpDrvDriverIo_STC *)arg, sizeof(io))) { + printk(KERN_ERR "IOCTL: FAULT\n"); + return -EFAULT; + } + if (io.id >= founddevs) + return -EFAULT; + dev = ppdevs[io.id]; + if (dev->ppdriver) + return dev->ppdriver(dev->ppdriverData, &io); + return -EFAULT; + } + if (cmd == PRESTERA_PP_DRIVER_OPEN) { + struct mvPpDrvDriverOpen_STC info; + int i; + if (copy_from_user(&info, (struct mvPpDrvDriverOpen_STC *)arg, sizeof(info))) { + printk(KERN_ERR "IOCTL: FAULT\n"); + return -EFAULT; + } + + i = find_pp_device(info.busNo, info.devSel, info.funcNo); + if (i < 0) { + /* device not found */ + return -EFAULT; + } + dev = ppdevs[i]; + + if (dev->ppdriver != NULL) { + if ((int)info.type != dev->ppdriverType) { + /* driver doesn't match */ + return -EFAULT; + } + /* driver is the same, skip */ + } else { + switch (info.type) { + case mvPpDrvDriverType_Pci_E: + presteraPpDriverPciPexCreate(dev); + break; + case mvPpDrvDriverType_PciHalf_E: + presteraPpDriverPciPexHalfCreate(dev); + break; + case mvPpDrvDriverType_PexMbus_E: + presteraPpDriverPexMbusCreate(dev); + break; + } + if (dev->ppdriver == NULL) { + /* failed to initialize, return error */ + return -EFAULT; + } + } + info.id = i; + + if (copy_to_user((struct mvPpDrvDriverOpen_STC *)arg, &info, sizeof(info))) { + printk(KERN_ERR "IOCTL: FAULT\n"); + return -EFAULT; + } + } + return 0; +} +#endif /* defined(PRESTERA_PP_DRIVER) */ + +#ifdef MV_DEBUG +static void ioctl_cmd_pr(unsigned int cmd) +{ + char *dir; + + static const char * const prestera_ioctls[] = { + [_IOC_NR(PRESTERA_IOC_HWRESET)] = "HW_RESET", + [_IOC_NR(PRESTERA_IOC_INTCONNECT)] = "INT_CONNECT", + [_IOC_NR(PRESTERA_IOC_INTENABLE)] = "INT_ENABLE", + [_IOC_NR(PRESTERA_IOC_INTDISABLE)] = "INT_DISABLE", + [_IOC_NR(PRESTERA_IOC_WAIT)] = "WAIT", + [_IOC_NR(PRESTERA_IOC_FIND_DEV)] = "FIND_DEV", + [_IOC_NR(PRESTERA_IOC_PCICONFIGWRITEREG)] = "PCI_CONFIG_WRITE_REG", + [_IOC_NR(PRESTERA_IOC_PCICONFIGREADREG)] = "PCI_CONFIG_READ_REG", + [_IOC_NR(PRESTERA_IOC_GETINTVEC)] = "GET_INT_VEC", + [_IOC_NR(PRESTERA_IOC_FLUSH)] = "FLUSH", + [_IOC_NR(PRESTERA_IOC_INVALIDATE)] = "INVALIDATE", + [_IOC_NR(PRESTERA_IOC_GETBASEADDR)] = "GET_BASE_ADDR", + [_IOC_NR(PRESTERA_IOC_DMAWRITE)] = "DMA_WRITE", + [_IOC_NR(PRESTERA_IOC_DMAREAD)] = "DMA_READ", + [_IOC_NR(PRESTERA_IOC_GETDMASIZE)] = "GET_DMA_SIZE", + [_IOC_NR(PRESTERA_IOC_TWSIINITDRV)] = "TWSI_INIT_DRV", + [_IOC_NR(PRESTERA_IOC_TWSIWAITNOBUSY)] = "TWSI_WAIT_NO_BUSY", + [_IOC_NR(PRESTERA_IOC_TWSIWRITE)] = "TWSI_WRITE", + [_IOC_NR(PRESTERA_IOC_TWSIREAD)] = "TWSI_READ", + [_IOC_NR(PRESTERA_IOC_GETMAPPING)] = "GET_MAPPING", + }; + #define PRESTERA_IOCTLS ARRAY_SIZE(prestera_ioctls) + + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + dir = "--"; + break; + case _IOC_READ: + dir = "r-"; + break; + case _IOC_WRITE: + dir = "-w"; + break; + case _IOC_READ | _IOC_WRITE: + dir = "rw"; + break; + default: + dir = "*ERR*"; break; + } + printk(KERN_ERR "got ioctl '%c', dir=%s, #%d (0x%08x) ", + _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); + + if (_IOC_NR(cmd) < PRESTERA_IOCTLS) + printk("%s\n", prestera_ioctls[_IOC_NR(cmd)]); +} +#endif + +/************************************************************************ + * + * prestera_ioctl: The device ioctl() implementation + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) +#ifndef HAVE_UNLOCKED_IOCTL +#define HAVE_UNLOCKED_IOCTL 1 +#endif +#endif +#ifdef HAVE_UNLOCKED_IOCTL +static long prestera_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int prestera_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + struct pp_dev *dev = NULL; + struct GT_PCI_Dev_STC gtDev; + struct GT_Intr2Vec int2vec; + struct GT_VectorCookie_STC vector_cookie; + struct PciConfigReg_STC pciConfReg; + struct GT_RANGE_STC range; + struct intData *intData; + int i; + struct GT_DmaReadWrite_STC dmaRWparams; + struct GT_TwsiReadWrite_STC twsiRWparams; + mv_kmod_size_t temp_len; + unsigned long intrline; + struct GT_PCI_Mapping_STC mapping; + mv_phys_addr_t ret; + struct GT_PCI_MMAP_INFO_STC mInfo; + struct GT_PCI_VMA_ADDRESSES_STC vmaInfo; + + +#ifdef PRESTERA_PP_DRIVER + if (_IOC_TYPE(cmd) == PRESTERA_PP_DRIVER_IOC_MAGIC) + return presteraPpDriver_ioctl(cmd, arg); +#endif /* defined(PRESTERA_PP_DRIVER) */ + if (_IOC_TYPE(cmd) == PRESTERA_SMI_IOC_MAGIC) + return prestera_smi_ioctl(cmd, arg); + + /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */ + if (_IOC_TYPE(cmd) != PRESTERA_IOC_MAGIC) { + printk(KERN_ERR "wrong ioctl magic key\n"); + return -ENOTTY; + } + +#ifdef MV_DEBUG + if (cmd != PRESTERA_IOC_WAIT) + ioctl_cmd_pr(cmd); +#endif + + switch (cmd) { + case PRESTERA_IOC_DMAWRITE: + if (copy_from_user(&dmaRWparams, (struct GT_DmaReadWrite_STC *)arg, sizeof(dmaRWparams))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + return prestera_dma_write(dmaRWparams.address, + dmaRWparams.length, + dmaRWparams.burstLimit, + (unsigned long)dmaRWparams.buffer); + + case PRESTERA_IOC_DMAREAD: + if (copy_from_user(&dmaRWparams, (struct GT_DmaReadWrite_STC *)arg, sizeof(dmaRWparams))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + return prestera_dma_read(dmaRWparams.address, + dmaRWparams.length, + dmaRWparams.burstLimit, + (unsigned long)dmaRWparams.buffer); + + case PRESTERA_IOC_HWRESET: + printk(KERN_INFO "got PRESTERA_IOC_HWRESET, do nothing\n"); + break; + + case PRESTERA_IOC_INTCONNECT: + /* read and parse user data structure */ + if (copy_from_user(&vector_cookie, (struct GT_VectorCookie_STC *)arg, + sizeof(vector_cookie))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + + /* Find ppdevs associated with requested irq nr */ + for (i = 0; i < founddevs; i++) { + if (ppdevs[i]->irq_data.intVec == vector_cookie.vector) + break; + } + if (i == founddevs) + return -ENODEV; + + if (prestera_int_connect(ppdevs[i], 0, &intData)) { + printk(KERN_ERR "prestera_int_connect failed\n"); + return -EFAULT; + } + vector_cookie.cookie = (mv_kmod_uintptr_t)((uintptr_t)intData); + + dprintk("PRESTERA_IOC_INTCONNECT interrupt %lx\n", vector_cookie.vector); + + /* USER READS */ + if (copy_to_user((struct GT_VectorCookie_STC *)arg, &vector_cookie, + sizeof(vector_cookie))) { + printk(KERN_ERR "copy_to_user failed\n"); + return -EFAULT; + } + break; + + case PRESTERA_IOC_INTENABLE: + /* clear the mask reg on device 0x10 */ + if (arg > 64) + send_sig_info(SIGSTOP, (struct siginfo *)1, current); + enable_irq(arg); + break; + + case PRESTERA_IOC_INTDISABLE: + disable_irq(arg); + break; + + case PRESTERA_IOC_WAIT: + /* cookie */ + intData = (struct intData *)arg; + + /* enable the interrupt vector */ + enable_irq(intData->intVec); + + if (down_interruptible(&intData->sem)) { + /* to avoid unbalanced irq warning when suspended by gdb */ + disable_irq(intData->intVec); + return -ERESTARTSYS; + } + break; + + case PRESTERA_IOC_FIND_DEV: + /* read and parse user data structure */ + if (copy_from_user(>Dev, (struct GT_PCI_Dev_STC *) arg, sizeof(gtDev))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + + for (i = 0; i < founddevs; i++) { + dev = ppdevs[i]; + if ((gtDev.vendorId == dev->vendorId) && + (gtDev.devId == dev->devId) && + (gtDev.instance == dev->instance)) + break; + } + if (i == founddevs) + return -ENODEV; + + /* Found */ + gtDev.busNo = dev->busNo; + gtDev.devSel = dev->devSel; + gtDev.funcNo = dev->funcNo; + + dprintk("PCI_FIND_DEV: pci? %d bus# %ld devSel %ld func# %ld vendId 0x%X devId 0x%X inst %lx\n", + dev->on_pci_bus, gtDev.busNo, gtDev.devSel, + gtDev.funcNo, gtDev.vendorId, gtDev.devId, gtDev.instance); + + /* READ */ + if (copy_to_user((struct GT_PCI_Dev_STC *)arg, >Dev, sizeof(gtDev))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + break; + + case PRESTERA_IOC_GETMAPPING: + /* read and parse user data structure */ + if (copy_from_user(&mapping, (struct GT_PCI_Mapping_STC *)arg, + sizeof(mapping))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + + dprintk("Search (mapping) bus %x, devSel %x, func%x\n", + mapping.busNo, mapping.devSel, mapping.funcNo); + + i = find_pp_device(mapping.busNo, mapping.devSel, mapping.funcNo); + if (i < 0) { + /* not found */ + return -ENODEV; + } + dev = ppdevs[i]; + /* configure virtual addresses if not configured yet */ + ppdev_set_vma(dev, i, mapping.regsSize); + + mapping.mapConfig.addr = dev->config.mmapbase; + mapping.mapConfig.length = (size_t)(dev->config.mmapsize); + mapping.mapConfig.offset = (size_t)(dev->config.mmapoffset); + mapping.mapRegs.addr = dev->ppregs.mmapbase; + mapping.mapRegs.length = (size_t)(dev->ppregs.mmapsize); + mapping.mapRegs.offset = (size_t)(dev->ppregs.mmapoffset); + mapping.mapDfx.addr = dev->dfx.mmapbase; + mapping.mapDfx.length = (size_t)(dev->dfx.mmapsize); + mapping.mapDfx.offset = (size_t)(dev->dfx.mmapoffset); + + if (copy_to_user((struct GT_PCI_Mapping_STC *)arg, &mapping, sizeof(mapping))) { + printk(KERN_ERR "copy_to_user failed\n"); + return -EFAULT; + } + break; + + case PRESTERA_IOC_PCICONFIGWRITEREG: + /* read and parse user data structure */ + if (copy_from_user(&pciConfReg, (struct PciConfigReg_STC *)arg, sizeof(pciConfReg))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + + for (i = 0; i < founddevs; i++) { + dev = ppdevs[i]; + if ((pciConfReg.busNo == dev->busNo) && + (pciConfReg.devSel == dev->devSel) && + (pciConfReg.funcNo == dev->funcNo)) + break; + } + if (i == founddevs) + return -ENODEV; + + if (dev->on_pci_bus) { + if (bspPciConfigWriteReg(pciConfReg.busNo, pciConfReg.devSel, + pciConfReg.funcNo, pciConfReg.regAddr, + pciConfReg.data) != MV_OK) { + printk(KERN_ERR "bspPciConfigWriteReg failed\n"); + return -EFAULT; + } + } + break; + + case PRESTERA_IOC_PCICONFIGREADREG: + /* read and parse user data structure */ + if (copy_from_user(&pciConfReg, (struct PciConfigReg_STC *) arg, sizeof(pciConfReg))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + + for (i = 0; i < founddevs; i++) { + dev = ppdevs[i]; + if ((pciConfReg.busNo == dev->busNo) && + (pciConfReg.devSel == dev->devSel) && + (pciConfReg.funcNo == dev->funcNo)) + break; + } + + if (i == founddevs) + return -ENODEV; + + pciConfReg.data = 0; + + if (dev->on_pci_bus) { + unsigned long data; + if (bspPciConfigReadReg(pciConfReg.busNo, + pciConfReg.devSel, + pciConfReg.funcNo, + pciConfReg.regAddr, + &data) != MV_OK) { + printk(KERN_ERR "bspPciConfigReadReg failed\n"); + return -EFAULT; + } + pciConfReg.data = (uint32_t)data; + } + + if (copy_to_user((struct PciConfigReg_STC *)arg, &pciConfReg, sizeof(pciConfReg))) { + printk(KERN_ERR "copy_to_user failed\n"); + return -EFAULT; + } + + break; + + case PRESTERA_IOC_GETINTVEC: + if (copy_from_user(&int2vec, (struct GT_Intr2Vec *)arg, sizeof(int2vec))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + + i = get_quirks(prestera_ppdev); + if (i == (-1)) { + printk(KERN_ERR "get_quirks failed for interrupt get\n"); + return -EFAULT; + } + if (int2vec.intrLine > MAX_SUPPORTED_INTR) { + printk(KERN_ERR "intrLine %d is out of range\n", (int)int2vec.intrLine); + return -EFAULT; + } + intrline = quirks[i].interruptMap[int2vec.intrLine]; + + dprintk("PRESTERA_IOC_GETINTVEC input line %ld output line %ld\n", int2vec.intrLine, intrline); + + if (MV_OK != bspPciGetIntVec(intrline, (void *)&int2vec.vector)) { + printk(KERN_ERR "bspPciGetIntVec failed\n"); + return -EFAULT; + } + + if (copy_to_user((struct GT_Intr2Vec *)arg, &int2vec, sizeof(int2vec))) { + printk(KERN_ERR "copy_to_user failed\n"); + return -EFAULT; + } + break; + + case PRESTERA_IOC_FLUSH: + /* read and parse user data structure */ + if (copy_from_user(&range, (struct GT_RANGE_STC *)arg, sizeof(range))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 69) || defined(CONFIG_ARCH_MSYS) + pci_map_single(NULL, (void *)range.address, range.length, PCI_DMA_TODEVICE); +#else + consistent_sync((void *)range.address, range.length, PCI_DMA_TODEVICE); +#endif + break; + + case PRESTERA_IOC_INVALIDATE: + /* read and parse user data structure */ + if (copy_from_user(&range, (struct GT_RANGE_STC *)arg, sizeof(range))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 69) || defined(CONFIG_ARCH_MSYS) + pci_map_single(NULL, (void *)range.address, range.length, PCI_DMA_FROMDEVICE); +#else + consistent_sync((void *)range.address, range.length, PCI_DMA_FROMDEVICE); +#endif + + break; + + case PRESTERA_IOC_GETBASEADDR: + ret = (mv_phys_addr_t)dma_base; + if (copy_to_user((void *)arg, &ret, sizeof(ret))) { + printk(KERN_ERR "copy_to_user failed\n"); + return -EFAULT; + } + break; + + case PRESTERA_IOC_GETDMASIZE: + temp_len = dma_len; + +#ifdef EXT_ARCH_64_CPU + if (bspAdv64Malloc32bit == 1) + temp_len |= 0x80000000; +#endif + if (copy_to_user((void *)arg, &temp_len, sizeof(temp_len))) { + printk(KERN_ERR "copy_to_user failed\n"); + return -EFAULT; + } + break; + + case PRESTERA_IOC_TWSIINITDRV: + if (bspTwsiInitDriver() != MV_OK) { + printk(KERN_ERR "bspTwsiInitDriver failed\n"); + return -EFAULT; + } + break; + + case PRESTERA_IOC_TWSIWAITNOBUSY: + if (bspTwsiWaitNotBusy() != MV_OK) { + printk(KERN_ERR "bspTwsiWaitNotBusy failed\n"); + return -EFAULT; + } + break; + + case PRESTERA_IOC_TWSIWRITE: + /* read and parse user data structure */ + if (copy_from_user(&twsiRWparams, (struct GT_TwsiReadWrite_STC *)arg, + sizeof(twsiRWparams))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + if (bspTwsiMasterWriteTrans(twsiRWparams.devId, twsiRWparams.pData, + twsiRWparams.len, twsiRWparams.stop) != MV_OK) { + printk(KERN_ERR "bspTwsiMasterWriteTrans failed\n"); + return -EFAULT; + } + + break; + + case PRESTERA_IOC_TWSIREAD: + /* read and parse user data structure */ + if (copy_from_user(&twsiRWparams, (struct GT_TwsiReadWrite_STC *)arg, + sizeof(twsiRWparams))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + if (bspTwsiMasterReadTrans(twsiRWparams.devId, twsiRWparams.pData, + twsiRWparams.len, twsiRWparams.stop) != MV_OK) { + printk(KERN_ERR "bspTwsiMasterReadTrans failed\n"); + return -EFAULT; + } + if (copy_to_user((struct GT_TwsiReadWrite_STC *)arg, &twsiRWparams, + sizeof(twsiRWparams))) { + printk(KERN_ERR "copy_to_user failed\n"); + return -EFAULT; + } + + break; + + case PRESTERA_IOC_ISFIRSTCLIENT: + if (dev_open_nr == 1) + return 0; + return 1; + + case PRESTERA_IOC_GETMMAPINFO: + /* read and parse user data structure */ + if (copy_from_user(&mInfo, (struct GT_PCI_MMAP_INFO_STC *) arg, sizeof(mInfo))) { + printk(KERN_ERR "copy_from_user failed\n"); + return -EFAULT; + } + if (mInfo.index < 0 || mInfo.index >= mmapInfoArrSize) { + /* out of range */ + return -EFAULT; + } + mInfo.addr = (mv_kmod_uintptr_t)mmapInfoArr[mInfo.index].addr; + mInfo.length = (mv_kmod_size_t)mmapInfoArr[mInfo.index].length; + mInfo.offset = (mv_kmod_size_t)mmapInfoArr[mInfo.index].offset; + if (copy_to_user((struct GT_PCI_MMAP_INFO_STC *)arg, &mInfo, sizeof(mInfo))) { + printk(KERN_ERR "copy_to_user failed\n"); + return -EFAULT; + } + break; + + case PRESTERA_IOC_GETVMA: + memset(&vmaInfo, 0, sizeof(vmaInfo)); + vmaInfo.dmaBase = (mv_kmod_uintptr_t)dma_base_vma_start; + vmaInfo.ppConfigBase = (mv_kmod_uintptr_t)pci_conf_vma_start; + vmaInfo.ppRegsBase = (mv_kmod_uintptr_t)pci_regs_vma_start; + vmaInfo.ppDfxBase = (mv_kmod_uintptr_t)pci_dfx_vma_start; + if (copy_to_user((struct GT_PCI_VMA_ADDRESSES_STC *)arg, &vmaInfo, sizeof(vmaInfo))) { + printk(KERN_ERR "copy_to_user failed\n"); + return -EFAULT; + } + break; + + default: + printk(KERN_WARNING "Unknown ioctl (%d)\n", _IOC_NR(cmd)); + return -EFAULT; + } + return 0; +} + +/* + * open and close: just keep track of how many times the device is + * mapped, to avoid releasing it. + */ + +void prestera_vma_open(struct vm_area_struct *vma) +{ + dev_open_nr++; +} + +void prestera_vma_close(struct vm_area_struct *vma) +{ + dev_open_nr--; +} + +struct vm_operations_struct prestera_vm_ops = { + .open = prestera_vma_open, + .close = prestera_vma_close, +}; + + + +/************************************************************************ + * + * prestera_do_mmap: Map physical address to userspace + */ +static int prestera_do_mmap(struct vm_area_struct *vma, + unsigned long phys, + unsigned long pageSize, bool no_cache) +{ + int rc; + + /* bind the prestera_vm_ops */ + vma->vm_ops = &prestera_vm_ops; + + /* VM_IO for I/O memory */ + vma->vm_flags |= VM_IO; + + /* disable caching on mapped memory */ + if (no_cache) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + vma->vm_private_data = prestera_dev; + + vma->vm_pgoff = phys >> PAGE_SHIFT; + + /* fix case when pageSize < length_param_of_mmap */ + vma->vm_end = vma->vm_start + pageSize; + + + dprintk(KERN_INFO "%s: remap_pfn_range(0x%lx, 0x%lx, 0x%lx, 0x%lx)\n", + __func__, (unsigned long)(vma->vm_start), + (unsigned long)(phys >> PAGE_SHIFT), + (unsigned long)pageSize, + (unsigned long)vma->vm_page_prot); + + rc = remap_pfn_range(vma, + vma->vm_start, + phys >> PAGE_SHIFT, + pageSize, + vma->vm_page_prot); + if (rc) { + printk(KERN_INFO "remap_pfn_range(0x%lx) failed (rc=%d)\n", vma->vm_start, rc); + return 1; + } + + prestera_vma_open(vma); + + return 0; +} + +/************************************************************************ + * + * prestera_mmap_dyn: map to dynamic address (single process only + * + * Key is vma->vm_pgoff where bit:(31-PAGE_SHIFT) == 1 + */ +static int prestera_mmap_dyn(struct file *file, struct vm_area_struct *vma) +{ + uint32_t busNo, devSel, funcNo, barNo; + struct pp_dev *ppdev; + unsigned long phys; + unsigned long pageSize = 0; + int i; + + busNo = (vma->vm_pgoff >> 10) & 0xff; + devSel = (vma->vm_pgoff >> 5) & 0x1f; + funcNo = (vma->vm_pgoff >> 2) & 0x07; + barNo = vma->vm_pgoff & 0x03; + if (busNo == 0xff && devSel == 0x1f && funcNo == 0x07) { + /* xCat */ + devSel = 0xff; + funcNo = 0xff; + } + i = find_pp_device(busNo, devSel, funcNo); + if (i < 0) { + printk(KERN_ERR "mvPP device 0x%02x:%02x.%x not found\n", + busNo, devSel, funcNo); + return -ENXIO; + } + ppdev = ppdevs[i]; + + pageSize = vma->vm_end - vma->vm_start; + + switch (barNo) { + case 0: /* config */ + if (pageSize < ppdev->config.size + ppdev->config.mmapoffset) { + printk(KERN_ERR "No enough address space for config: 0x%lx, required 0x%lx\n", + (unsigned long)pageSize, + (unsigned long)(ppdev->config.size + ppdev->config.mmapoffset)); + return -ENXIO; + } + phys = ppdev->config.phys; + pageSize = ppdev->config.size; + ppdev->config.mmapbase = vma->vm_start; + vma->vm_start += ppdev->config.mmapoffset; + ppdev->config.mmapsize = pageSize; + break; + case 1: /* regs */ + phys = ppdev->ppregs.phys; + ppdev->ppregs.mmapbase = vma->vm_start; + ppdev->ppregs.mmapsize = pageSize; + break; + case 2: /* dfx */ + if (!ppdev->dfx.phys) + return -ENXIO; /* ignore */ + if (pageSize < ppdev->dfx.size) { + printk(KERN_ERR "No enough address space for dfx: 0x%lx, required 0x%lx\n", + (unsigned long)pageSize, + (unsigned long)ppdev->dfx.size); + return -ENXIO; + } + phys = ppdev->dfx.phys; + pageSize = ppdev->dfx.size; + ppdev->dfx.mmapbase = vma->vm_start; + ppdev->dfx.mmapsize = pageSize; + break; + default: + printk(KERN_ERR "bad bar: %d\n", barNo); + return -ENXIO; + } + + return prestera_do_mmap(vma, phys, pageSize, true); +} + +/************************************************************************ + * + * prestera_mmap: The device mmap() implementation + */ +static int prestera_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long phys = 0; + unsigned long pageSize = 0; + struct pp_dev *ppdev; + int i; + + if ((vma->vm_pgoff & (1<<(31-PAGE_SHIFT))) != 0) + return prestera_mmap_dyn(file, vma); + + if (((vma->vm_pgoff) << PAGE_SHIFT) & (PAGE_SIZE - 1)) { + /* need aligned offsets */ + printk(KERN_ERR "prestera_mmap offset not aligned\n"); + return -ENXIO; + } + +#define D mmapInfoArr[i] + /* search for mapping */ + for (i = 0; i < mmapInfoArrSize; i++) { + if (vma->vm_start == D.addr + D.offset) + break; + } + if (i == mmapInfoArrSize) { + printk(KERN_ERR "unknown range (0%0x)\n", (int)vma->vm_start); + return 1; + } + ppdev = ppdevs[D.index]; + + pageSize = vma->vm_end - vma->vm_start; + + switch (D.map_type) { + case MMAP_INFO_TYPE_DMA_E: + phys = dma_base; + pageSize = dma_len; + return prestera_do_mmap(vma, phys, pageSize, false); + + case MMAP_INFO_TYPE_PP_CONF_E: + phys = ppdev->config.phys; + pageSize = ppdev->config.mmapsize; + + if (pageSize > vma->vm_end - vma->vm_start) + pageSize = vma->vm_end - vma->vm_start; + + break; + + case MMAP_INFO_TYPE_PP_REGS_E: + phys = ppdev->ppregs.phys; + pageSize = ppdev->ppregs.mmapsize; + break; + + case MMAP_INFO_TYPE_PP_DFX_E: + phys = ppdev->dfx.phys; + pageSize = ppdev->dfx.mmapsize; + break; + + default: + printk(KERN_WARNING "Unknown map_type\n"); + return -EFAULT; + } + + return prestera_do_mmap(vma, phys, pageSize, true); +} + +/************************************************************************ + * + * prestera_open: The device open() implementation + */ +static int prestera_open(struct inode *inode, struct file *filp) +{ + if (down_interruptible(&prestera_dev->sem)) + return -ERESTARTSYS; + + + if (!dev_init_done) { + up(&prestera_dev->sem); + return -EIO; + } + +#ifndef SHARED_MEMORY + /* Avoid single-usage restriction for shared memory: + * device should be accessible for multiple clients. */ + if (dev_open_nr) { + up(&prestera_dev->sem); + return -EBUSY; + } +#endif + + filp->private_data = prestera_dev; + + dev_open_nr++; + up(&prestera_dev->sem); + + printk(KERN_INFO "%s opened\n", prestera_dev_name); + + return 0; +} + +/************************************************************************ + * + * prestera_release: The device close() implementation + */ +static int prestera_release(struct inode *inode, struct file *file) +{ + dev_open_nr--; + + if (dev_open_nr == 0) + prestera_int_cleanup(); + + printk(KERN_DEBUG "%s released\n", prestera_dev_name); + + return 0; +} + +/************************************************************************ + * + * proc read data rooutine + */ +static inline u32 dbgread_u32(void *addr) +{ +#if defined EXT_ARCH_64_CPU || defined CONFIG_ARM + return readl((void *)addr); +#else + return *(u32 *)addr; +#endif +} + +struct dumpregs_stc { + const char *nm; + unsigned start; + unsigned end; +}; + +#ifdef CONFIG_OF +static int dumpregs(struct seq_file *m, struct mem_region *mem, struct dumpregs_stc *r) +{ + void *basePtr = (void *)mem->base; + if (basePtr == NULL) + basePtr = ioremap_nocache(mem->phys, PAGE_SIZE); + for (; r->nm; r++) { + int i, n; + uintptr_t address; + uint32_t value; + + address = (uintptr_t)basePtr + (uintptr_t)r->start; + for (i = r->start, n = 0; i <= r->end; i += 4) { + value = dbgread_u32((void *)address); + if (n == 0) + seq_printf(m, "\t%s(0x%04x):", r->nm, i); + seq_printf(m, " %08x", le32_to_cpu(value)); + n++; + if (n == 4) { + n = 0; + if (i+4 <= r->end) + seq_putc(m, '\n'); + } + address += 4; + } + seq_putc(m, '\n'); + } + + if (mem->base == 0) + iounmap(basePtr); + return 0; +} +#else +static int dumpregs(char *page, int len, struct mem_region *mem, struct dumpregs_stc *r) +{ + void *basePtr = (void *)mem->base; + if (basePtr == NULL) + basePtr = ioremap_nocache(mem->phys, PAGE_SIZE); + for (; r->nm; r++) { + int i, n; + uintptr_t address; + uint32_t value; + + address = (uintptr_t)basePtr + (uintptr_t)r->start; + for (i = r->start, n = 0; i <= r->end; i += 4) { + value = dbgread_u32((void *)address); + if (n == 0) + len += sprintf(page+len, "\t%s(0x%04x):", r->nm, i); + len += sprintf(page+len, " %08x", le32_to_cpu(value)); + n++; + if (n == 4) { + n = 0; + if (i+4 <= r->end) + page[len++] = '\n'; + } + address += 4; + } + page[len++] = '\n'; + } + + if (mem->base == 0) + iounmap(basePtr); + return len; +} +#endif + +static struct dumpregs_stc ppConf[] = { + { "\toff", 0, 0x10 }, + { "\toff", 0, 0x10 }, + { "\toff", 0x41804, 0x41804 }, + { "\toff", 0x41808, 0x41808 }, +#if 0 + /* No idea what are these registers, + * not found in Lion2, Bobcat2 specs + */ + { "MASK", 0x118, 0x118 }, + { "MASK", 0x114, 0x114 }, /*cause */ +#endif + { NULL, 0, 0 } +}; + +static struct dumpregs_stc ppRegs[] = { + { "\toff", 0, 0 }, + { "\toff", 0x4c, 0x4c }, + { "\toff", 0x50, 0x50 }, +#if 0 + { "\toff", 0x1000000, 0x1000000 }, +#ifndef EXT_ARCH_64_CPU + { "\toff", 0x2000000, 0x2000000 }, + { "\toff", 0x3000000, 0x3000000 }, +#endif +#endif + { NULL, 0, 0 } +}; + +#ifndef CONFIG_OF +int prestera_read_proc_mem(char *page, + char **start, + off_t offset, + int count, + int *eof, + void *data) +{ + int len; + struct pp_dev *ppdev; + int i; + + len = 0; + + len += sprintf(page + len, "%s major # %d\n", prestera_dev_name, prestera_major); + len += sprintf(page + len, "short_bh_count %d\n", prestera_int_bh_cnt_get()); + len += sprintf(page + len, "rx_DSR %d\n", prestera_smi_eth_port_rx_dsr_cnt()); + len += sprintf(page + len, "tx_DSR %d\n", prestera_smi_eth_port_tx_dsr_cnt()); + + len += sprintf(page + len, "DMA area: 0x%lx(virt), base: 0x%lx(phys), len: 0x%x\n", + (unsigned long)dma_area, dma_base, dma_len); + + for (i = 0; i < founddevs; i++) { + ppdev = ppdevs[i]; + + len += sprintf(page + len, "Device %d\n", i); + len += sprintf(page+len, "\tPCI %02x:%02x.%x vendor:dev=%04x:%04x\n", + (unsigned)ppdev->busNo, (unsigned)ppdev->devSel, (unsigned)ppdev->funcNo, + ppdev->vendorId, ppdev->devId); + + len += sprintf(page + len, "\tconfig 0x%lx(user virt), phys: 0x%lx, len: 0x%lx\n", + ppdev->config.mmapbase, ppdev->config.phys, ppdev->config.size); + + len = dumpregs(page, len, &(ppdev->config), ppConf); + + len += sprintf(page + len, "\tppregs 0x%lx(user virt), phys: 0x%lx, len: 0x%lx\n", + ppdev->ppregs.mmapbase, ppdev->ppregs.phys, ppdev->ppregs.size); + + len = dumpregs(page, len, &(ppdev->ppregs), ppRegs); + } + + *eof = 1; + + return len; +} +#endif + +static const struct file_operations prestera_fops = { + .llseek = prestera_lseek, + .read = prestera_smi_read, + .write = prestera_smi_write, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = prestera_ioctl, +#else + .ioctl = prestera_ioctl, +#endif + .mmap = prestera_mmap, + .open = prestera_open, + .release = prestera_release +}; + +#ifdef PRESTERA_SYSCALLS +/************************************************************************ +* +* syscall entries for fast calls +* +************************************************************************/ +/* fast call to prestera_ioctl() */ +asmlinkage long sys_prestera_ctl(unsigned int cmd, unsigned long arg) +{ +#ifdef HAVE_UNLOCKED_IOCTL + return prestera_ioctl(NULL, cmd, arg); +#else + return prestera_ioctl(NULL, NULL, cmd, arg); +#endif +} + +#define OWN_SYSCALLS 1 + +#ifdef __NR_SYSCALL_BASE +# define __SYSCALL_TABLE_INDEX(name) (__NR_ ## name - __NR_SYSCALL_BASE) +#else +# define __SYSCALL_TABLE_INDEX(name) (__NR_ ## name) +#endif + +#define __TBL_ENTRY(name) { __SYSCALL_TABLE_INDEX(name), (long)sys_ ## name, 0 } +static struct { + int entry_number; + long own_entry; + long saved_entry; +} prestera_syscall[OWN_SYSCALLS] = { + __TBL_ENTRY(prestera_ctl) +}; +#undef __TBL_ENTRY + +/******************************************************************************* +* prestera_syscall_init +* +* DESCRIPTION: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int prestera_syscall_init(void) +{ + int k; + long *syscall_tbl; + + syscall_tbl = (long *)kallsyms_lookup_name("sys_call_table"); + + if (syscall_tbl == NULL) { + printk(KERN_ALERT "%s failed to get address of sys_call_table\n", __func__); + return -EFAULT; + } + + for (k = 0; k < OWN_SYSCALLS; k++) { + prestera_syscall[k].saved_entry = syscall_tbl[prestera_syscall[k].entry_number]; + syscall_tbl[prestera_syscall[k].entry_number] = prestera_syscall[k].own_entry; + } + return 0; +} + +/******************************************************************************* +* prestera_RestoreSyscalls +* +* DESCRIPTION: +* Restore original syscall entries. +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* None +* +*******************************************************************************/ +static int prestera_syscall_restore(void) +{ + int k; + long *syscall_tbl; + + syscall_tbl = (long *)kallsyms_lookup_name("sys_call_table"); + + if (syscall_tbl == NULL) { + printk(KERN_ALERT "%s failed to get address of sys_call_table\n", __func__); + return -EFAULT; + } + + for (k = 0; k < OWN_SYSCALLS; k++) { + if (prestera_syscall[k].saved_entry) + syscall_tbl[prestera_syscall[k].entry_number] = prestera_syscall[k].saved_entry; + } + return 0; +} + +#endif /* PRESTERA_SYSCALLS */ + + + +static int prestera_dma_init(void) +{ + dma_len = _2M; + dma_area = dma_alloc_coherent(NULL, dma_len, (dma_addr_t *)&dma_base, + GFP_DMA | GFP_KERNEL); + + if (!dma_area) { + printk(KERN_ERR "dma_alloc_coherent() dma_area failed\n"); + return -ENOMEM; + } + + dprintk("DMA - dma_area: %p(v), dma_base: 0x%lx(p), dma_len: 0x%x\n", + dma_area, dma_base, dma_len); + + /* allocate temp area for bspDma operations */ + dma_tmp_virt = dma_alloc_coherent(NULL, PAGE_SIZE, &dma_tmp_phys, + GFP_DMA | GFP_KERNEL); + + if (!dma_tmp_virt) { + printk(KERN_ERR "dma_alloc_coherent() failed\n"); + return -ENOMEM; + } + + M.map_type = MMAP_INFO_TYPE_DMA_E; + M.addr = dma_base_vma_start; + dma_base_vma_end = dma_base_vma_start + dma_len; + M.length = dma_len; + M.offset = 0; + mmapInfoArrSize++; + + return 0; +} + +/************************************************************************ + * + * prestera_cleanup: + */ +static void prestera_cleanup(void) +{ + int i; + struct pp_dev *ppdev; + + dev_init_done = 0; + + prestera_int_cleanup(); + + for (i = 0; i < founddevs; i++) { + ppdev = ppdevs[i]; +#ifdef PRESTERA_PP_DRIVER + if (ppdev->ppdriver) /* destroy driver */ + ppdev->ppdriver(ppdev->ppdriverData, NULL); +#endif /* PRESTERA_PP_DRIVER */ + kfree(ppdev); + } + founddevs = 0; + + if (dma_tmp_virt) { + dma_free_coherent(NULL, PAGE_SIZE, dma_tmp_virt, dma_tmp_phys); + dma_tmp_virt = NULL; + } + + if (dma_area) { + dma_free_coherent(NULL, dma_len, (dma_addr_t *)&dma_base, + GFP_DMA | GFP_KERNEL); + dma_area = NULL; + } + +#ifdef PRESTERA_SYSCALLS + prestera_syscall_restore(); +#endif + remove_proc_entry(prestera_dev_name, NULL); + + cdev_del(&prestera_cdev); + + unregister_chrdev_region(MKDEV(prestera_major, 0), 1); +} + +static uint32_t get_instance(unsigned short vendorId, unsigned short devId) +{ + static uint32_t bobcat2_instance; + static uint32_t lion2_instance; + static uint32_t ac3_instance; + uint32_t instance = 0; + + switch (devId) { + case MV_BOBCAT2_DEV_ID: { + instance = bobcat2_instance; + bobcat2_instance++; + break; + } + case MV_LION2_DEV_ID:{ + instance = lion2_instance; + lion2_instance++; + break; + } + case MV_ALLEYCAT3_DEV_ID:{ + instance = ac3_instance; + ac3_instance++; + break; + } + default: + instance = -1; + } + return instance; +} + +/* + * XXX: probably not need to distinguish instance of bobcat, lion etc, maybe + * it will be enough for CPSS to remove instance variable and assign founddevs + * to ppdev->instance - will be check during multi-switch configuration tests + */ +int ppdev_conf_set(struct pci_dev *pdev, struct pp_dev *ppdev) +{ + uint32_t instance; + int32_t quirksInstance; + int start, len; + + dprintk("%s\n", __func__); + + instance = get_instance(ppdev->vendorId, ppdev->devId); + if (instance == (-1)) { + printk(KERN_ERR "%s: instance get failed\n", __func__); + return -ENODEV; + } + + ppdev->instance = instance; + + quirksInstance = get_quirks(ppdev->devId); + if (quirksInstance == (-1)) { + printk(KERN_ERR "%s: quirksInstance get failed\n", __func__); + return -ENODEV; + } + + if (quirks[quirksInstance].hasDfx == PCI_DEV_DFX_EN) { + /* Has DFX */ + ppdev->dfx.phys = ppdev->ppregs.phys + _64M; + ppdev->dfx.size = _1M; + if (ppdev->ppregs.base != 0) + ppdev->dfx.base = ppdev->ppregs.base + _64M; + } + + if (quirks[quirksInstance].configOffset != 0) { + if (ppdev->config.allocsize > quirks[quirksInstance].configOffset) { + start = ppdev->config.phys; + len = ppdev->config.size; + + start += quirks[quirksInstance].configOffset; + len -= quirks[quirksInstance].configOffset; + if (ppdev->config.base) + ppdev->config.base += quirks[quirksInstance].configOffset; + + ppdev->config.phys = start; + ppdev->config.size = len; + + ppdev->config.mmapoffset = quirks[quirksInstance].configOffset; + } + } else { + /* INTER_REGS address space mapping */ + /* ppdev->config.mmapoffset = 0; */ + } + + /* Only for debug purpose */ + dprintk("%s:pdev: devId 0x%x, vendorId 0x%x, instance 0x%lx\n", + __func__, ppdev->devId, ppdev->vendorId, ppdev->instance); + dprintk("pdev: busNo 0x%lx, devSel 0x%lx, funcNo 0x%lx\n", + ppdev->busNo, ppdev->devSel, ppdev->funcNo); + dprintk("ppregs: allocbase 0x%lx, allocsize 0x%lx, size 0x%lx\n", + ppdev->ppregs.allocbase, ppdev->ppregs.allocsize, ppdev->ppregs.size); + dprintk("ppregs: phys 0x%lx, mmapoffset 0x%x\n", + ppdev->ppregs.phys, ppdev->ppregs.mmapoffset); + dprintk("config: phys 0x%lx, mmapoffset 0x%x\n", + ppdev->config.phys, ppdev->config.mmapoffset); + + /* Add device to ppdevs */ + ppdevs[founddevs++] = ppdev; + dprintk("num of devices %d\n", founddevs); + + prestera_ppdev = quirks[quirksInstance].pciId; + + return 0; +} +#ifdef CONFIG_OF +static int proc_status_show(struct seq_file *m, void *v) +{ + int i; + struct pp_dev *ppdev; + + seq_printf(m, "%s major # %d\n", prestera_dev_name, prestera_major); + seq_printf(m, "short_bh_count %d\n", prestera_int_bh_cnt_get()); + seq_printf(m, "rx_DSR %d\n", prestera_smi_eth_port_rx_dsr_cnt()); + seq_printf(m, "tx_DSR %d\n", prestera_smi_eth_port_tx_dsr_cnt()); + + seq_printf(m, "DMA area: 0x%lx(virt), base: 0x%lx(phys), len: 0x%x\n", + (unsigned long)dma_area, dma_base, dma_len); + + for (i = 0; i < founddevs; i++) { + ppdev = ppdevs[i]; + + seq_printf(m, "Device %d\n", i); + seq_printf(m, "\tPCI %02x:%02x.%x vendor:dev=%04x:%04x\n", + (unsigned)ppdev->busNo, (unsigned)ppdev->devSel, (unsigned)ppdev->funcNo, + ppdev->vendorId, ppdev->devId); + + seq_printf(m, "\tconfig 0x%lx(user virt), phys: 0x%lx, len: 0x%lx\n", + ppdev->config.mmapbase, ppdev->config.phys, ppdev->config.size); + + dumpregs(m, &(ppdev->config), ppConf); + + seq_printf(m, "\tppregs 0x%lx(user virt), phys: 0x%lx, len: 0x%lx\n", + ppdev->ppregs.mmapbase, ppdev->ppregs.phys, ppdev->ppregs.size); + + dumpregs(m, &(ppdev->ppregs), ppRegs); + } + + return 0; +} + +static int proc_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_status_show, PDE_DATA(inode)); +} + +static const struct file_operations prestera_read_proc_operations = { + .open = proc_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#endif + +/************************************************************************ + * + * prestera_init: + */ +int prestera_init(void) +{ + int rc = 0; + + dprintk("%s\n", __func__); + + /* If already initialized skip it */ + if (dev_init_done == 1) { + dprintk("%s: already initialized\n", __func__); + return 0; + } + + /* init static vars */ + dev_open_nr = 0; + dma_tmp_virt = NULL; + dma_base = 0; + dma_len = 0; + dma_area = NULL; + + /* first thing register the device at OS */ + + /* Register your major. */ + rc = register_chrdev_region(MKDEV(prestera_major, 0), 1, prestera_dev_name); + if (rc < 0) { + printk(KERN_ERR "%s: register_chrdev_region err= %d\n", __func__, rc); + return rc; + } + + cdev_init(&prestera_cdev, &prestera_fops); + + prestera_cdev.owner = THIS_MODULE; + + rc = cdev_add(&prestera_cdev, MKDEV(prestera_major, 0), 1); + if (rc) { + unregister_chrdev_region(MKDEV(prestera_major, 0), 1); + printk(KERN_ERR "%s: cdev_add err= %d\n", __func__, rc); + return rc; + } + + prestera_dev = kmalloc(sizeof(struct prestera_device), GFP_KERNEL); + if (!prestera_dev) { + printk(KERN_ERR "prestera_dev: Failed allocating memory for device\n"); + rc = -ENOMEM; + goto fail; + } + +#ifdef PRESTERA_SYSCALLS + rc = prestera_syscall_init(); + if (0 != rc) + goto fail; +#endif + + /* create /proc entry */ +#ifdef CONFIG_OF + if (!proc_create(prestera_dev_name, S_IRUGO, NULL, &prestera_read_proc_operations)) + return -ENOMEM; +#else + create_proc_read_entry(prestera_dev_name, 0, NULL, prestera_read_proc_mem, NULL); +#endif + + /* initialize the device main semaphore */ + sema_init(&prestera_dev->sem, 1); + + prestera_int_init(); + + rc = prestera_smi_init(); + if (0 != rc) + goto fail; + + rc = prestera_dma_init(); + if (0 != rc) + goto fail; + + dev_init_done = 1; + + printk(KERN_INFO "%s driver initialized\n", prestera_dev_name); + + return 0; + +fail: + prestera_cleanup(); + + printk(KERN_ERR "%s driver init failed, rc=%d\n", prestera_dev_name, rc); + + return rc; +} + +module_param(prestera_major, int, S_IRUGO); + Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera.h (working copy) @@ -0,0 +1,239 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************** +* mv_prestera.h +* +* DESCRIPTION: +* Includes defines and structures needed by the PP device driver +* +* DEPENDENCIES: +* None. +* +* COMMENTS: +* Please note: this file is shared for: +* axp_lsp_3.4.69 +* msys_lsp_3_4 +* +*******************************************************************************/ +#ifndef __MV_PRESTERA +#define __MV_PRESTERA + +#define PRESTERA_PP_DRIVER + +#include +#include "mv_prestera_glob.h" + +#ifdef __BIG_ENDIAN +#define CPU_BE +#else +#define CPU_LE +#endif + +#ifndef PRESTERA_MAJOR +#define PRESTERA_MAJOR 244 /* major number */ +#endif + +/* CPSS configurations */ +#define CPSS_DMA_VIRT_ADDR 0x19000000 +#define CPSS_SWITCH_VIRT_ADDR 0x20000000 +#define CPSS_DFX_VIRT_ADDR 0x1b000000 +#define CPSS_CPU_VIRT_ADDR 0x19400000 +#define CPSS_VIRT_ADDR_MASK 0xf0000000 + +/* General definition */ +#define ENABLE (1) +#define DISABLE (0) +#define _1M (0x00100000) +#define _2M (0x00200000) +#define _64M (0x04000000) + +#define PCI_DEV_LION_CONFIG_OFFSET 0x70000 +#define PCI_DEV_BC2_CONFIG_OFFSET 0x0 +#define PCI_DEV_AC3_CONFIG_OFFSET 0x0 +#define PCI_DEV_PEX_EN 1 +#define PCI_DEV_DFX_EN 1 +#define PCI_DEV_DFX_DIS 0 + +#define PCI_DEV_INTR_MAX_NUM 8 +#define PCI_DEV_DEF_INTR_NUM 0 +#define PCI_DEV_LION2_PP_1_INTR_NUM 3 +#define PCI_DEV_LION2_PP_2_INTR_NUM 2 +#define PCI_DEV_LION2_PP_3_INTR_NUM 1 +#define PCI_DEV_LION2_PP_4_INTR_NUM 0 + +/* PCI BAR definition */ +#define PEX_0 (0) +#define BAR_1 (1) +#define BAR_2 (2) + +#define PRV_MAX_PP_DEVICES 10 + +/* Switch registers & reg values */ +#define PP_UDID (0x00000204) /* Unit default ID reg */ +#define PP_WIN_BA(n) (0x0000020c + (8*n)) /* base address reg */ +#define PP_WIN_SR(n) (0x00000210 + (8*n)) /* base window size reg */ +#define PP_WIN_CTRL(n) (0x00000254 + (4*n)) /* window control reg */ + +#define PP_ATTR 0x10 +#define PP_UDID_DATTR (PP_ATTR << 4) +#define PP_BA_ATTR (PP_ATTR << 8) +#define PP_WIN_MAX_SIZE 0xFFFF +#define PP_WIN_SIZE_OFF 16 +#define PP_WIN_SIZE_VAL (PP_WIN_MAX_SIZE << PP_WIN_SIZE_OFF) +#define PP_WIN_CTRL_RW 0x3 +#define PP_WIN_CTRL_AP (PP_WIN_CTRL_RW << 1) + +#define IRQ_AURORA_SW_CORE0 33 + +struct intData { + unsigned long intVec; /* The interrupt vector we bind to */ + struct semaphore sem; /* The semaphore on which the user wait for */ + struct tasklet_struct *tasklet; /* The tasklet - need it for cleanup */ +}; + +struct prestera_device { + struct semaphore sem; /* Mutual exclusion semaphore */ + loff_t size; /* prestera mem size */ +}; + +struct mem_region { + mv_phys_addr_t phys; + unsigned long allocbase; + unsigned long size; + unsigned long allocsize; + uintptr_t base; + uintptr_t mmapbase; + uintptr_t mmapsize; + size_t mmapoffset; +}; + +struct Mmap_Info_stc { + enum { + MMAP_INFO_TYPE_DMA_E, + MMAP_INFO_TYPE_PP_CONF_E, + MMAP_INFO_TYPE_PP_REGS_E, + MMAP_INFO_TYPE_PP_DFX_E + } map_type; + int index; + uintptr_t addr; + size_t length; + size_t offset; +}; + +#ifdef PRESTERA_PP_DRIVER +typedef int (*PP_DRIVER_FUNC)(void *drv, void *io); +#endif +struct pp_dev { + unsigned short devId; + unsigned short vendorId; + unsigned short on_pci_bus; + unsigned long instance; + unsigned long busNo; + unsigned long devSel; + unsigned long funcNo; + struct mem_region config; /* Configuration space */ + struct mem_region ppregs; /* PP registers space */ + struct intData irq_data; + struct mem_region dfx; /* DFX space */ + +#ifdef PRESTERA_PP_DRIVER + PP_DRIVER_FUNC ppdriver; + int ppdriverType; + void *ppdriverData; +#endif +}; +#ifdef PRESTERA_PP_DRIVER +int presteraPpDriverPciPexCreate(struct pp_dev *dev); +int presteraPpDriverPciPexHalfCreate(struct pp_dev *dev); +int presteraPpDriverPexMbusCreate(struct pp_dev *dev); +#endif + +/* Register offset definition struct */ +struct prvPciDeviceQuirks { + unsigned int pciId; + unsigned int isPex; + unsigned int configOffset; + unsigned int hasDfx; + unsigned int interruptMap[8]; +}; + +int prestera_init(void); +int prestera_global_init(void); +int ppdev_conf_set(struct pci_dev *pdev, struct pp_dev *ppdev); +unsigned int mvDevIdGet(void); +unsigned int get_founddev(void); +extern unsigned long dma_base; + +static inline uint32_t read_u32(uintptr_t addr) +{ + uint32_t data; + data = __raw_readl(addr); /* also converted from LE */ + return le32_to_cpu(data); +} + +static inline void write_u32(uint32_t data, uintptr_t addr) +{ + data = cpu_to_le32(data); + __raw_writel(data, addr); +} + +#endif /* __MV_PRESTERA */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_glob.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_glob.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_glob.h (working copy) @@ -0,0 +1,230 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************** +* mv_prestera_glob.h +* +* DESCRIPTION: +* This file includes the declaration of the struct we want to send to kernel mode, +* from user mode. +* +* DEPENDENCIES: +* None. +* +* COMMENTS: +* Please note: this file is shared for: +* axp_lsp_3.4.69 +* msys_lsp_3_4 +* msys_lsp_2_6_32 +* +*******************************************************************************/ +#ifndef __MV_PRESTERA_GLOB +#define __MV_PRESTERA_GLOB + +#define PRESTERA_SYSCALLS + +#ifndef __KERNEL__ +#include +/* uint32_t uintptr_t and so on */ +#include +#include +#ifdef PRESTERA_SYSCALLS +#include +#endif +#else /* !defined(__KERNEL__) */ +/* uint32_t uintptr_t and so on */ +#include +#include +#endif /* !defined(__KERNEL__) */ + +#define mv_phys_addr_t uintptr_t +#define mv_kmod_uintptr_t uintptr_t +#define mv_kmod_size_t size_t + + +struct PciConfigReg_STC { + uint32_t busNo; + uint32_t devSel; + uint32_t funcNo; + uint32_t regAddr; + uint32_t data; +}; + +struct GT_PCI_Dev_STC { + uint16_t vendorId; + uint16_t devId; + uint32_t instance; + uint32_t busNo; + uint32_t devSel; + uint32_t funcNo; +}; + +struct GT_PCI_Mapping_STC { + uint32_t busNo; + uint32_t devSel; + uint32_t funcNo; + mv_kmod_size_t regsSize; + struct { + mv_kmod_uintptr_t addr; + mv_kmod_size_t length; + mv_kmod_size_t offset; + } mapConfig, mapRegs, mapDfx; +}; + +/*TD*/ +struct GT_Intr2Vec { + uint32_t intrLine; + uint32_t bus; + uint32_t device; + uint32_t vector; +}; + +/*TD*/ +struct GT_VectorCookie_STC { + uint32_t vector; + mv_kmod_uintptr_t cookie; +}; + +struct GT_RANGE_STC { + mv_kmod_uintptr_t address; + mv_kmod_size_t length; +}; + +struct GT_DmaReadWrite_STC { + mv_kmod_uintptr_t address; + mv_kmod_size_t length; + mv_kmod_size_t burstLimit; + mv_kmod_uintptr_t buffer; +}; + +struct GT_TwsiReadWrite_STC { + unsigned char devId; /* I2c slave ID */ + unsigned char len; /* pData array size (in chars). */ + unsigned char stop; /* Indicates if stop bit is needed in the end */ + mv_kmod_uintptr_t pData; /* Pointer to array of chars (address / data)*/ +}; + +struct GT_PCI_VMA_ADDRESSES_STC { + mv_kmod_uintptr_t dmaBase; + mv_kmod_uintptr_t ppConfigBase; + mv_kmod_uintptr_t ppRegsBase; + mv_kmod_uintptr_t ppDfxBase; + mv_kmod_uintptr_t xCatDraginiteBase; + mv_kmod_uintptr_t hsuBaseAddr; +}; + +struct GT_PCI_MMAP_INFO_STC { + uint32_t index; + mv_kmod_uintptr_t addr; + mv_kmod_size_t length; + mv_kmod_size_t offset; +}; + + +#define PRESTERA_IOC_MAGIC 'p' +#define PRESTERA_IOC_HWRESET _IO(PRESTERA_IOC_MAGIC, 0) +#define PRESTERA_IOC_INTCONNECT _IOWR(PRESTERA_IOC_MAGIC, 3, struct GT_VectorCookie_STC) +#define PRESTERA_IOC_INTENABLE _IOW(PRESTERA_IOC_MAGIC, 4, mv_kmod_uintptr_t) +#define PRESTERA_IOC_INTDISABLE _IOW(PRESTERA_IOC_MAGIC, 5, mv_kmod_uintptr_t) +#define PRESTERA_IOC_WAIT _IOW(PRESTERA_IOC_MAGIC, 6, mv_kmod_uintptr_t) +#define PRESTERA_IOC_FIND_DEV _IOWR(PRESTERA_IOC_MAGIC, 7, struct GT_PCI_Dev_STC) +#define PRESTERA_IOC_PCICONFIGWRITEREG _IOW(PRESTERA_IOC_MAGIC, 8, struct PciConfigReg_STC) +#define PRESTERA_IOC_PCICONFIGREADREG _IOWR(PRESTERA_IOC_MAGIC, 9, struct PciConfigReg_STC) +#define PRESTERA_IOC_GETINTVEC _IOWR(PRESTERA_IOC_MAGIC, 10, struct GT_Intr2Vec) +#define PRESTERA_IOC_FLUSH _IOW(PRESTERA_IOC_MAGIC, 11, struct GT_RANGE_STC) +#define PRESTERA_IOC_INVALIDATE _IOW(PRESTERA_IOC_MAGIC, 12, struct GT_RANGE_STC) +#define PRESTERA_IOC_GETBASEADDR _IOR(PRESTERA_IOC_MAGIC, 13, mv_phys_addr_t*) +#define PRESTERA_IOC_DMAWRITE _IOW(PRESTERA_IOC_MAGIC, 14, struct GT_DmaReadWrite_STC) +#define PRESTERA_IOC_DMAREAD _IOW(PRESTERA_IOC_MAGIC, 15, struct GT_DmaReadWrite_STC) +#define PRESTERA_IOC_GETDMASIZE _IOR(PRESTERA_IOC_MAGIC, 16, mv_kmod_size_t*) +#define PRESTERA_IOC_TWSIINITDRV _IO(PRESTERA_IOC_MAGIC, 17) +#define PRESTERA_IOC_TWSIWAITNOBUSY _IO(PRESTERA_IOC_MAGIC, 18) +#define PRESTERA_IOC_TWSIWRITE _IOW(PRESTERA_IOC_MAGIC, 19, struct GT_TwsiReadWrite_STC) +#define PRESTERA_IOC_TWSIREAD _IOWR(PRESTERA_IOC_MAGIC, 20, struct GT_TwsiReadWrite_STC) +#define PRESTERA_IOC_GETMAPPING _IOWR(PRESTERA_IOC_MAGIC, 29, struct GT_PCI_Mapping_STC) + +#define PRESTERA_IOC_ISFIRSTCLIENT _IO(PRESTERA_IOC_MAGIC, 30) +#define PRESTERA_IOC_GETVMA _IOR(PRESTERA_IOC_MAGIC, 31, struct GT_PCI_VMA_ADDRESSES_STC) +#define PRESTERA_IOC_GETMMAPINFO _IOWR(PRESTERA_IOC_MAGIC, 32, struct GT_PCI_MMAP_INFO_STC) + +#ifdef PRESTERA_SYSCALLS +/******************************************************** +* +* Syscall numbers for +* +* long prestera_ctl(unsigned int cmd, unsigned long param) +* +********************************************************/ +#define __NR_prestera_ctl __NR_setxattr +#endif /* PRESTERA_SYSCALLS */ + +#ifndef __KERNEL__ +extern GT_32 gtPpFd; +#ifdef PRESTERA_SYSCALLS +#include +#include +#define prestera_ctl(cmd, arg) syscall(__NR_prestera_ctl, (cmd), (arg)) +#else +#define prestera_ctl(cmd, arg) ioctl(gtPpFd, cmd, arg) +#endif +#endif + +#endif /* __MV_PRESTERA_GLOB */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_irq.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_irq.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_irq.c (working copy) @@ -0,0 +1,347 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************** +* mv_prestera_irq.c +* +* DESCRIPTION: +* functions in kernel mode special for prestera IRQ. +* +* DEPENDENCIES: +* +*******************************************************************************/ +#include "mvOs.h" +#include "mv_prestera_irq.h" +#include "mv_prestera.h" +#include "mv_prestera_pci.h" + +#ifdef MV_PP_DBG +#define dprintk(a...) printk(a) +#else +#define dprintk(a...) +#endif + +#define PRESTERA_MAX_INTERRUPTS 4 +#define SEMA_DEF_VAL 0 + +#define IRQ_AURORA_SW_CORES (IRQ_AURORA_SW_CORE0 | IRQ_AURORA_SW_CORE1 | IRQ_AURORA_SW_CORE2) +#define IRQ_SWITCH_MASK (0x7 << (IRQ_AURORA_SW_CORE0 - (CPU_INT_SOURCE_CONTROL_IRQ_OFFS + 1))) + +#define SW_CORES 0x3 + +static struct pp_dev *assigned_irq[PRESTERA_MAX_INTERRUPTS]; +static int assinged_irq_nr; +static int short_bh_count; + +int prestera_int_bh_cnt_get(void) +{ + return short_bh_count; +} + +/******************************************************************************* +* mv_ac3_bc2_enable_switch_irq +* +* +*******************************************************************************/ +static void mv_ac3_bc2_enable_switch_irq(uintptr_t i_regs, bool enable) +{ + int reg_val, i; + unsigned int addr; + + /* For all Switching Core Int */ + for (i = 0; i < SW_CORES; i++) { + + /* Set as pci endpoint and enable */ + addr = CPU_INT_SOURCE_CONTROL_REG((IRQ_AURORA_SW_CORE0 + i)); + reg_val = readl(i_regs + addr); + dprintk("irq status and ctrl(0x%x) = 0x%x\n", addr, reg_val); + + if (enable) + reg_val |= (PEX_IRQ_EN) | (PEX_IRQ_EP); + else + reg_val &= ~(PEX_IRQ_EN) & ~(PEX_IRQ_EP); + + writel(reg_val, i_regs + addr); + + dprintk("irq status and ctrl(0x%x) = 0x%x\n", + addr, readl(i_regs + addr)); + + /* Clear irq */ + writel(IRQ_AURORA_SW_CORE0 + i, + i_regs + CPU_INT_CLEAR_MASK_LOCAL_REG); + dprintk("clr reg(0x%x)\n", CPU_INT_CLEAR_MASK_LOCAL_REG); + } +} + +/******************************************************************************* +* prestera_tl_isr +* +* DESCRIPTION: +* This is the Prestera ISR reponsible for only scheduling the BH (Tasklet). +* +* INPUTS: +* irq - the Interrupt ReQuest number +* dev_id - the client data used as argument to the handler +* +* OUTPUTS: +* None. +* +* RETURNS: +* IRQ_HANDLED allways +* +* COMMENTS: +* None. +* +*******************************************************************************/ +static irqreturn_t prestera_tl_isr(int irq, + void *dev_id) +{ + struct pp_dev *ppdev = dev_id; + + /* disable the interrupt vector */ + disable_irq_nosync(irq); + + /* enqueue the PP task BH in the tasklet */ + tasklet_hi_schedule((struct tasklet_struct *)ppdev->irq_data.tasklet); + + short_bh_count++; + + return IRQ_HANDLED; +} + +static irqreturn_t prestera_tl_isr_pci(int irq, void *dev_id) +{ + struct pp_dev *ppdev = dev_id; + int reg; + + reg = readl(ppdev->config.base + MSYS_CAUSE_VEC1_REG_OFFS); + dprintk("msys cause reg 0x%x, switch_mask 0x%x\n", + reg, IRQ_SWITCH_MASK); + + if ((reg & IRQ_SWITCH_MASK) == 0) + return IRQ_NONE; + + /* Disable the interrupt vector */ + disable_irq_nosync(irq); + + /* Enqueue the PP task BH in the tasklet */ + tasklet_hi_schedule((struct tasklet_struct *)ppdev->irq_data.tasklet); + + short_bh_count++; + + return IRQ_HANDLED; +} + +/******************************************************************************* +* prestera_bh +* +* DESCRIPTION: +* This is the Prestera DSR, reponsible for only signaling of the occurence +* of an event, any procecing will be done in the intTask (user space thread) +* it self. +* +* INPUTS: +* data - the interrupt control data +* +* OUTPUTS: +* None. +* +* RETURNS: +* None. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +static void prestera_bh(unsigned long data) +{ + /* awake any reading process */ + up(&((struct intData *)data)->sem); +} + +/******************************************************************************* + * prestera_int_connect + * + * DESCRIPTION: + * connect and interrupt via register it at the kernel. + * + * INPUTS: + * ppdev - pointer to prestera device structure associated with intVect + * routine - the bound routine for this interrupt vector + * + * OUTPUTS: + * cookie - the interrupt control data + * + * RETURNS: + * 0 on success, -1 otherwise. + * + * COMMENTS: + * None. + * + ******************************************************************************/ +int prestera_int_connect(struct pp_dev *ppdev, + void *routine, + struct intData **cookie) +{ + unsigned int status, intVec = ppdev->irq_data.intVec; + struct intData *irq_data = &ppdev->irq_data; + struct tasklet_struct *tasklet; + + tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL); + if (NULL == tasklet) { + printk(KERN_ERR "kmalloc failed\n"); + return -ENOMEM; + } + + *cookie = irq_data; + + /* The user process will wait on it */ + sema_init(&(irq_data->sem), SEMA_DEF_VAL); + + /* For cleanup we will need the tasklet */ + irq_data->tasklet = tasklet; + + tasklet_init(tasklet, prestera_bh, (unsigned long)irq_data); + + if ((ppdev->devId == MV_BOBCAT2_DEV_ID || ppdev->devId == MV_ALLEYCAT3_DEV_ID) && + ppdev->on_pci_bus == 1) + status = request_irq(intVec, prestera_tl_isr_pci, IRQF_SHARED, + "mvPP", (void *)ppdev); + else + status = request_irq(intVec, prestera_tl_isr, IRQF_DISABLED, + "mvPP", (void *)ppdev); + + if (status) { + panic("Can not assign IRQ %d to PresteraDev\n", intVec); + return -1; + } + + + printk(KERN_DEBUG "%s: connected Prestera IRQ - %d\n", __func__, intVec); + disable_irq_nosync(intVec); + local_irq_disable(); + if (assinged_irq_nr < PRESTERA_MAX_INTERRUPTS) { + assigned_irq[assinged_irq_nr++] = ppdev; + local_irq_enable(); + } else { + local_irq_enable(); + printk(KERN_DEBUG "%s: too many irqs assigned\n", __func__); + } + + /* Enable interrupt after registering handler */ + if (ppdev->devId == MV_BOBCAT2_DEV_ID || ppdev->devId == MV_ALLEYCAT3_DEV_ID) + mv_ac3_bc2_enable_switch_irq(ppdev->config.base, true); + + return 0; +} + + +void prestera_int_init(void) +{ + assinged_irq_nr = 0; + short_bh_count = 0; +} + + +/******************************************************************************* +* prestera_int_cleanup +* +* DESCRIPTION: +* unbind all interrupts +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* 0 on success, -1 otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int prestera_int_cleanup(void) +{ + struct intData *irq_data; + struct pp_dev *ppdev; + + while (assinged_irq_nr > 0) { + + ppdev = assigned_irq[--assinged_irq_nr]; + irq_data = &ppdev->irq_data; + + /* Do not disable pcie shared irqs - they can be used by + * other devices connected via the same pcie such as dragonite) + */ + if ((ppdev->devId == MV_BOBCAT2_DEV_ID || ppdev->devId == MV_ALLEYCAT3_DEV_ID) && + ppdev->on_pci_bus == 1) + mv_ac3_bc2_enable_switch_irq(ppdev->config.base, false); + else + disable_irq_nosync(irq_data->intVec); + + free_irq(irq_data->intVec, (void *)ppdev); + tasklet_kill(irq_data->tasklet); + kfree(irq_data->tasklet); + } + return 0; +} Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_irq.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_irq.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_irq.h (working copy) @@ -0,0 +1,127 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************** +* mv_prestera_irq.h +* +* DESCRIPTION: +* Includes defines and structures needed by the PP device driver +* +* DEPENDENCIES: +* None. +* +*******************************************************************************/ +#ifndef __MV_PRESTERA_IRQ +#define __MV_PRESTERA_IRQ + +#include +#include +#include "mv_prestera.h" + + +/******************************************************************************* + * prestera_int_connect + * + * DESCRIPTION: + * connect and interrupt via register it at the kernel. + * + * INPUTS: + * ppdev - pointer to prestera device structure associated with intVect + * routine - the bound routine for this interrupt vector + * + * OUTPUTS: + * cookie - the interrupt control data + * + * RETURNS: + * 0 on success, -1 otherwise. + * + * COMMENTS: + * None. + * + ******************************************************************************/ +int prestera_int_connect(struct pp_dev *ppdev, + void *routine, + struct intData **cookie); + +/******************************************************************************* +* prestera_int_cleanup +* +* DESCRIPTION: +* unbind all interrupts +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* 0 on success, -1 otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int prestera_int_cleanup(void); +void prestera_int_init(void); +int prestera_int_bh_cnt_get(void); + +#endif /* __MV_PRESTERA_IRQ */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_pltfm.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_pltfm.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_pltfm.c (working copy) @@ -0,0 +1,553 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************** +* mv_prestera_pltfm.c +* +* DESCRIPTION: +* functions in kernel mode special for prestera. +* +* DEPENDENCIES: +* +*******************************************************************************/ +#include "mvOs.h" +#include "mv_prestera.h" +#include "mv_prestera_pci.h" +#include "mv_pss_api.h" + +#ifdef CONFIG_OF +#include +#endif + +#undef MV_PP_DBG + +#ifdef MV_PP_DBG +#define dprintk(a...) printk(a) +#else +#define dprintk(a...) +#endif + +#define DRIVER_NAME "prestera_device" + +/* Switch attr and target id is different for PCI */ +#define PP_PCI_ATTR 0xe +#define PP_PCI_TARGETID 0x4 +#define PP_PCI_UDID_DATTR (PP_PCI_ATTR << 4 | PP_PCI_TARGETID) +#define PP_PCI_BA_ATTR (PP_PCI_ATTR << 8 | PP_PCI_TARGETID) + + +#define DFX_JTAG_DEVID_STAT 0xF8244 + +struct presteraPciDev { + unsigned int deviceNum; + unsigned int vendorId; + unsigned int deviceId; +}; + +static struct presteraPciDev presteraPciDevs[] = { + {0, PCI_VENDOR_ID_IDT_SWITCH, MV_IDT_SWITCH_DEV_ID_808E}, + {1, PCI_VENDOR_ID_IDT_SWITCH, MV_IDT_SWITCH_DEV_ID_802B}, + {2, PCI_VENDOR_ID_MARVELL, MV_BOBCAT2_DEV_ID}, + {3, PCI_VENDOR_ID_MARVELL, MV_LION2_DEV_ID}, + {4, PCI_VENDOR_ID_MARVELL, MV_ALLEYCAT3_DEV_ID}, + {-1, -1, -1} +}; + +static const char prestera_drv_name[] = "mvPP"; +static void __iomem *inter_regs; +static int gDevId = -1; + +/******************************************************************************* +******************************************************************************** +******************************************************************************** +*** +*** Internal Device Configuration Section +*** +******************************************************************************** +******************************************************************************** +*******************************************************************************/ + +/******************************************************************************* +* mvInternalDeviceIdSet +* +* +*******************************************************************************/ +void mvInternalDevIdSet(unsigned int devId) +{ + gDevId = devId; +} + +/******************************************************************************* +* mvDevIdGet +* +* +*******************************************************************************/ +unsigned int mvDevIdGet(void) +{ + return gDevId; +} + +#ifndef CONFIG_OF +/******************************************************************************* +* ppdev_conf_set_pltfm +* +* +*******************************************************************************/ +static int ppdev_conf_set_pltfm(void) +{ + struct pp_dev *ppdev; + unsigned long start; + unsigned long len; + int err; + + dprintk("%s\n", __func__); + + ppdev = kmalloc(sizeof(struct pp_dev), GFP_KERNEL); + if (NULL == ppdev) { + printk("kmalloc failed\n"); + return -ENOMEM; + } + memset(ppdev, 0, sizeof(*ppdev)); + + ppdev->devId = mvDevIdGet(); + ppdev->vendorId = MARVELL_VEN_ID; + ppdev->busNo = 0xFF;/* 0xFF represent internal device for CPSS */ + ppdev->devSel = 0xFF; + ppdev->funcNo = 0xFF; + ppdev->on_pci_bus = 0; + ppdev->irq_data.intVec = IRQ_AURORA_SW_CORE0; + + /* configure the SWITCH register address space */ + /* additional 1MB is reserved for DFX registers */ + start = SWITCH_REGS_PHYS_BASE; + len = SWITCH_REGS_SIZE + _1M; + + ppdev->ppregs.allocbase = start; + ppdev->ppregs.allocsize = len; + ppdev->ppregs.size = len; + ppdev->ppregs.phys = start; + ppdev->ppregs.base = (uintptr_t)(SWITCH_REGS_VIRT_BASE); + + /* configure the CPU register address space */ + start = INTER_REGS_PHYS_BASE; + len = _1M; + + ppdev->config.allocbase = start; + ppdev->config.allocsize = len; + ppdev->config.size = len; + ppdev->config.phys = start; + ppdev->config.base = (uintptr_t)(INTER_REGS_VIRT_BASE); + + err = ppdev_conf_set(NULL, ppdev); + if (err) + return err; + + return 0; +} + +/******************************************************************************* +* prestera_dma_switch_init +* +* +*******************************************************************************/ +static void prestera_dma_switch_init(void) +{ + /* open internal switch window for DMA */ + writel(dma_base | PP_BA_ATTR, SWITCH_REGS_VIRT_BASE | PP_WIN_BA(0)); + writel(PP_UDID_DATTR, SWITCH_REGS_VIRT_BASE | PP_UDID); + writel(PP_WIN_SIZE_VAL, SWITCH_REGS_VIRT_BASE | PP_WIN_SR(0)); + writel(PP_WIN_CTRL_AP, SWITCH_REGS_VIRT_BASE | PP_WIN_CTRL(0)); + + dprintk("%s read pp: 0x%x\n", __func__, + readl(SWITCH_REGS_VIRT_BASE | PP_WIN_BA(0))); + dprintk("%s read pp: 0x%x\n", __func__, + readl(SWITCH_REGS_VIRT_BASE | PP_UDID)); + dprintk("%s read pp: 0x%x\n", __func__, + readl(SWITCH_REGS_VIRT_BASE | PP_WIN_SR(0))); + dprintk("%s read pp: 0x%x\n", __func__, + readl(SWITCH_REGS_VIRT_BASE | PP_WIN_CTRL(0))); +} + +/******************************************************************************* +* prestera_Internal_dev_probe +* +* +*******************************************************************************/ +static int prestera_Internal_dev_probe(unsigned int devId) +{ + int err; + + mvInternalDevIdSet(devId); + + switch (devId) { + + case MV_BOBCAT2_DEV_ID: + case MV_ALLEYCAT3_DEV_ID: + + err = ppdev_conf_set_pltfm(); + if (0 != err) + return err; + + err = prestera_init(); + if (err) + return err; + + prestera_dma_switch_init(); + break; + + default: + break; + } + + printk(KERN_INFO "finish internal dev %x probe\n", devId); + + return 0; +} +#endif + +/******************************************************************************* +******************************************************************************** +******************************************************************************** +*** +*** PCI Device Configuration Section +*** +******************************************************************************** +******************************************************************************** +*******************************************************************************/ + +/******************************************************************************* +* mv_ppdev_conf_set_pci +* +* +*******************************************************************************/ +static int mv_ppdev_conf_set_pci(struct pci_dev *pdev) +{ + struct pp_dev *ppdev; + unsigned long start; + unsigned long len; + void __iomem * const *iomap; + int err; + + iomap = pcim_iomap_table(pdev); + + ppdev = kmalloc(sizeof(struct pp_dev), GFP_KERNEL); + if (NULL == ppdev) { + dev_err(&pdev->dev, "kmalloc ppdev failed\n"); + return -ENOMEM; + } + memset(ppdev, 0, sizeof(*ppdev)); + + ppdev->devId = pdev->device; + ppdev->vendorId = MARVELL_VEN_ID; + ppdev->busNo = pdev->bus->number; + ppdev->devSel = PCI_SLOT(pdev->devfn); + ppdev->funcNo = PCI_FUNC(pdev->devfn); + ppdev->on_pci_bus = 1; + ppdev->irq_data.intVec = pdev->irq; + + /* Configure the SWITCH register address space */ + /* Additional 1MB is reserved for DFX registers - Bobcat2 / Alleycat3 */ + /* Lion2 does not have DFX */ + start = pci_resource_start(pdev, MV_PCI_BAR_1); + if (pdev->device != MV_LION2_DEV_ID) /* MV_BOBCAT2_DEV_ID / MV_ALLEYCAT3_DEV_ID */ + len = pci_resource_len(pdev, MV_PCI_BAR_1) + _1M; + else + len = pci_resource_len(pdev, MV_PCI_BAR_1); + + ppdev->ppregs.allocbase = start; + ppdev->ppregs.allocsize = len; + ppdev->ppregs.size = len; + ppdev->ppregs.phys = start; + ppdev->ppregs.base = (unsigned long)iomap[MV_PCI_BAR_1]; + + /* Configure the CPU register address space */ + start = pci_resource_start(pdev, MV_PCI_BAR_INTER_REGS); + len = pci_resource_len(pdev, MV_PCI_BAR_INTER_REGS); + + ppdev->config.allocbase = start; + ppdev->config.allocsize = len; + ppdev->config.size = len; + ppdev->config.phys = start; + ppdev->config.base = (unsigned long)iomap[MV_PCI_BAR_INTER_REGS]; + + err = ppdev_conf_set(pdev, ppdev); + if (err) + return err; + + return 0; +} + +/******************************************************************************* +* mv_pci_dma_switch_init +* +* +*******************************************************************************/ +static void mv_pci_dma_switch_init(unsigned long switch_reg, struct pci_dev *pdev) +{ + dprintk("%s\n", __func__); + + writel(dma_base | PP_PCI_BA_ATTR, switch_reg | PP_WIN_BA(0)); + writel(PP_PCI_UDID_DATTR, switch_reg | PP_UDID); + writel(PP_WIN_SIZE_VAL, switch_reg | PP_WIN_SR(0)); + writel(PP_WIN_CTRL_AP, switch_reg | PP_WIN_CTRL(0)); + + dprintk("read pp: 0x%x\n", readl(switch_reg | PP_WIN_BA(0))); + dprintk("read pp: 0x%x\n", readl(switch_reg | PP_UDID)); + dprintk("read pp: 0x%x\n", readl(switch_reg | PP_WIN_SR(0))); + dprintk("read pp: 0x%x\n", readl(switch_reg | PP_WIN_CTRL(0))); + + /* Debug dma reg - according to old code in + * arch/arm/mach-armadaxp/pss/hwServices.c + */ + writel(0xaaba, switch_reg | 0x2684); + dprintk("%s read pp: 0x%x\n", __func__, readl(switch_reg | 0x2684)); +} + +/******************************************************************************* +* prestera_pci_dev_config +* +* +*******************************************************************************/ +static int prestera_pci_dev_config(struct pci_dev *pdev) +{ + int err; + void __iomem * const *iomap = NULL; + void __iomem *switch_reg = NULL; + + switch (pdev->device) { + + case MV_IDT_SWITCH_DEV_ID_808E: + case MV_IDT_SWITCH_DEV_ID_802B: + bspSmiReadRegLionSpecificSet(); + return 0; + + case MV_BOBCAT2_DEV_ID: + case MV_ALLEYCAT3_DEV_ID: + case MV_LION2_DEV_ID: + + iomap = pcim_iomap_table(pdev); + inter_regs = iomap[MV_PCI_BAR_INTER_REGS]; + switch_reg = iomap[MV_PCI_BAR_1]; + + dprintk("inter_regs: %p, bar1: %p\n", + iomap[MV_PCI_BAR_INTER_REGS], iomap[MV_PCI_BAR_1]); +#ifdef MV_PP_DBG + if (pdev->device != MV_LION2_DEV_ID) + dprintk("bar2: %p\n", iomap[MV_PCI_BAR_2]); +#endif + + break; + + default: + dprintk("%s: unsupported device\n", __func__); + } + + err = mv_ppdev_conf_set_pci(pdev); + if (err) + return err; + + err = prestera_init(); + if (err) + return err; + + /* MV_BOBCAT2_DEV_ID / MV_ALLEYCAT3_DEV_ID */ + if (pdev->device != MV_LION2_DEV_ID) { + mv_pci_dma_switch_init((unsigned long)switch_reg, pdev); + dprintk("DFX(0x%x) test %x and should be ..357\n", iomap[MV_PCI_BAR_2] + DFX_JTAG_DEVID_STAT, + readl(iomap[MV_PCI_BAR_2] + DFX_JTAG_DEVID_STAT)); + } + + dev_info(&pdev->dev, "%s init completed\n", prestera_drv_name); + return 0; +} + +/******************************************************************************* +* prestera_pltfm_probe +* +* Prestera devices probe function +* +*******************************************************************************/ +static int prestera_pci_dev_probe(void) +{ + int err; + unsigned long type = 0; + unsigned long instance = 0; + unsigned long busNo, devSel, funcNo; + unsigned long devId, vendorId; + + /* + ** PCI Configuration Section + ** =============== + */ + prestera_global_init(); + + while (presteraPciDevs[type].deviceNum != (-1)) { + devId = presteraPciDevs[type].deviceId; + vendorId = presteraPciDevs[type].vendorId; + + err = bspPciFindDev(vendorId, devId, instance, &busNo, &devSel, &funcNo); + if (err != 0) { + /* no more devices */ + instance = 0; + type++; + continue; + } + dprintk(KERN_INFO "vendorId 0x%lx, devId 0x%lx, instance 0x%lx\n", vendorId, devId, instance); + dprintk(KERN_INFO "busNo 0x%lx, devSel 0x%lx, funcNo 0x%lx\n", busNo, devSel, funcNo); + instance++; + + prestera_pci_dev_config(pci_get_bus_and_slot(busNo, PCI_DEVFN(devSel, funcNo))); + } + + return 0; +} + +/******************************************************************************* +* prestera_pltfm_probe +* +* Prestera devices probe function +* +*******************************************************************************/ +static int prestera_pltfm_probe(struct platform_device *pdev) +{ + int err; +#ifndef CONFIG_OF + unsigned int *pdata; + unsigned int boardId; +#endif + + /* + ** PCI Devices Configuration Section + ** ===================== + */ + printk(KERN_INFO "\n==Start PCI Devices scan and configure==\n"); + err = prestera_pci_dev_probe(); + if (0 != err) + return err; + +#ifndef CONFIG_OF + /* + ** Internal Device Configuration Section + ** ======================= + */ + printk(KERN_INFO "\n==Start Internal Devices scan and configure==\n"); + pdata = (unsigned int *)dev_get_platdata(&pdev->dev); + if (!pdata) { + printk(KERN_INFO "No internal device detected\n"); + } else { + boardId = *pdata; + printk(KERN_INFO "Internal device 0x%x detected\n", boardId); + err = prestera_Internal_dev_probe(boardId); + if (0 != err) + return err; + } +#endif + + return 0; +} + +/******************************************************************************* +* prestera_pltfm_cleanup +* +* +*******************************************************************************/ +static int prestera_pltfm_cleanup(struct platform_device *pdev) +{ + /* + * do nothing, ppdev is freed during char-dev clean-up + * (prestera_cleanup) + */ + return 0; +} + +static struct of_device_id mv_prestera_dt_ids[] = { + { .compatible = "marvell,armada-prestera", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_prestera_dt_ids); + +static struct platform_driver prestera_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(mv_prestera_dt_ids), +#endif + }, + .probe = prestera_pltfm_probe, + .remove = prestera_pltfm_cleanup, +}; + +static int __init prestera_pltfm_init(void) +{ + return platform_driver_register(&prestera_driver); +} +late_initcall(prestera_pltfm_init); + +static void __exit prestera_pltfm_exit(void) +{ + platform_driver_unregister(&prestera_driver); +} +module_exit(prestera_pltfm_exit); + +MODULE_ALIAS("prestera_device"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("device driver for Marvell Prestera family switches"); Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_pp_driver_glob.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_pp_driver_glob.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_pp_driver_glob.h (working copy) @@ -0,0 +1,152 @@ +/******************************************************************************* +* mv_prestera_pp_driver_glob.h +* +* DESCRIPTION: +* This file includes the declaration of the struct we want to send to kernel mode, +* from user mode. +* +* DEPENDENCIES: +* None. +* +* COMMENTS: +* Please note: this file is shared for: +* axp_lsp_3.4.69 +* msys_lsp_3_4 +* msys_lsp_2_6_32 +* +*******************************************************************************/ +#ifndef __MV_PRESTERA_PP_DRIVER_GLOB__ +#define __MV_PRESTERA_PP_DRIVER_GLOB__ + +#ifndef mv_phys_addr_t +# define mv_phys_addr_t uintptr_t +# define mv_kmod_uintptr_t uintptr_t +# define mv_kmod_size_t size_t +#endif + +/* + * enum mvPpDrvDriverType_ENT + * + * Description: Kernel-mode driver registers select + * + * Enumerations: + * mvPpDrvDriverType_Pci_E - 4 region address completion (ADDRCOMPL==0) + * mvPpDrvDriverType_PciHalf_E - 2 region address completion (ADDRCOMPL==0) + * 32M mapped only + * mvPpDrvDriverType_PexMbus_E - 8 region address completion + * Bobcat2 & Lion3 only now + */ +enum mvPpDrvDriverType_ENT { + mvPpDrvDriverType_Pci_E, + mvPpDrvDriverType_PciHalf_E, + mvPpDrvDriverType_PexMbus_E +}; + +/* + * typedef: struct mvPpDrvDriverOpen_STC + * + * Description: + * + * Kernel-mode driver init data + * + * Fields: + * busNo - PCI bus No + * devSel - PCI device No + * funcno - PCI device function No + * type - driver type (PCI/PexMbus,etc) + * id - Driver Id + * + * Comments: + * + */ +struct mvPpDrvDriverOpen_STC { + uint32_t busNo; + uint32_t devSel; + uint32_t funcNo; + enum mvPpDrvDriverType_ENT type; + uint32_t id; +}; + + + +/* + * enum mvPpDrvDriverIoOps_ENT + * + * Description: Kernel-mode driver operations + * + * Enumerations: + * mvPpDrvDriverIoOps_Reset_E - reset driver + * mvPpDrvDriverIoOps_Destroy_E - destroy + * mvPpDrvDriverIoOps_PpRegRead_E - read PP register + * mvPpDrvDriverIoOps_PpRegWrite_E - write PP register + * mvPpDrvDriverIoOps_PciRegRead_E - read PCI config register + * mvPpDrvDriverIoOps_PciRegWrite_E - write PCI config register + * mvPpDrvDriverIoOps_DfxRegRead_E - read DFX register + * mvPpDrvDriverIoOps_DfxRegWrite_E - write DFX register + * mvPpDrvDriverIoOps_RamRead_E - read PP ram (from pp registers + * address space) + * mvPpDrvDriverIoOps_RamWrite_E - write PP ram (to pp registers + * address space) + * + */ +enum mvPpDrvDriverIoOps_ENT { + mvPpDrvDriverIoOps_Reset_E, + mvPpDrvDriverIoOps_Destroy_E, + mvPpDrvDriverIoOps_PpRegRead_E, + mvPpDrvDriverIoOps_PpRegWrite_E, + mvPpDrvDriverIoOps_PciRegRead_E, + mvPpDrvDriverIoOps_PciRegWrite_E, + mvPpDrvDriverIoOps_DfxRegRead_E, + mvPpDrvDriverIoOps_DfxRegWrite_E, + mvPpDrvDriverIoOps_RamRead_E, + mvPpDrvDriverIoOps_RamWrite_E, +}; + +/* + * typedef: struct mvPpDrvDriverIoStc + * + * Description: + * + * Kernel-mode driver I/O data + * + * Fields: + * id - Driver Id + * op - Operation type + * regAddr - Register address + * length - number of registers to read/write + * byteswap flag for diag mode + * dataPtr - pointer to data for read/write + * + * Comments: + * + */ +struct mvPpDrvDriverIo_STC { + uint32_t id; + enum mvPpDrvDriverIoOps_ENT op; + uint32_t regAddr; + uint32_t length; + mv_kmod_uintptr_t dataPtr; +}; + + +/************************ IOCTLs ****************************/ +#define PRESTERA_PP_DRIVER_IOC_MAGIC 'd' +#define PRESTERA_PP_DRIVER_OPEN _IOWR(PRESTERA_PP_DRIVER_IOC_MAGIC, 0, struct mvPpDrvDriverOpen_STC) +#define PRESTERA_PP_DRIVER_IO _IOWR(PRESTERA_PP_DRIVER_IOC_MAGIC, 1, struct mvPpDrvDriverIo_STC) + +#ifndef __KERNEL__ +# ifndef prestera_ctl +# ifdef PRESTERA_SYSCALLS +# include +# include +# define __NR_prestera_ctl __NR_setxattr +# define prestera_ctl(cmd, arg) syscall(__NR_prestera_ctl, (cmd), (arg)) +# else /* !defined(PRESTERA_SYSCALLS) */ +# include +extern GT_32 gtPpFd; +# define prestera_ctl(cmd, arg) ioctl(gtPpFd, cmd, arg) +# endif /* !defined(PRESTERA_SYSCALLS) */ +# endif /* !defined(prestera_ctl) */ +#endif + +#endif /* __MV_PRESTERA_PP_DRIVER_GLOB__ */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_smi.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_smi.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_smi.c (working copy) @@ -0,0 +1,995 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************* +* mv_prestera_smi.c +* +* DESCRIPTION: +* functions in kernel mode special for prestera_smi. +* +* DEPENDENCIES: +* +*******************************************************************************/ +#include "mvOs.h" +#include "mv_prestera.h" +#include "mv_prestera_smi_glob.h" +#include "mv_pss_api.h" + +#undef MV_DEBUG + +/* defines */ +#ifdef MV_DEBUG +#define dprintk(a...) printk(a) +#else +#define dprintk(a...) +#endif + +/* local variables and variables */ +static int presteraSmi_initialized = -1; + +static int rx_DSR = -1; /* rx DSR invocation counter */ +static int tx_DSR = -1; /* tx DSR invocation counter */ + +struct semaphore *netIfIntTaskSemPtr; /* netIfIntTask Signalling sema */ +struct semaphore netIfIntTaskSem; + + +/******************************************************************************/ +/*********************** ethernet port FIFO section ***************************/ +/******************************************************************************/ +static unsigned long *fifoPtr; /* the FIFO pointer */ +static unsigned long occupied; /* occupied segments counter */ +static unsigned long fifoSize; /* FIFO size */ +static unsigned long *frontPtr; /* FIFO front pointer */ +static unsigned long *rearPtr; /* FIFO rear pointer */ +static unsigned long *firstPtr; /* first element in FIFO */ +static unsigned long *lastPtr; /* last element in FIFO */ + +/******************************************************************************* +* ethPortFifoInit +* +* DESCRIPTION: This routine allocates kernel memory for the FIFO. It is called +* twice: once for the Rx and once for the Tx complete. The second +* invocation allocates the memory and sets the FIFO pointers. +* +* INPUTS: +* numOfElem - the number of elements (buffers and control data) needed +* for Rx/TxEnd +* +* OUTPUTS: +* None. +* +* RETURNS: +* -ENOMEM - if there is no sufficiant memory +* 0 - on success. +* +*******************************************************************************/ +long ethPortFifoInit(unsigned long numOfElem) +{ + if (numOfElem == 0) { + printk(KERN_ERR "ethPortFifoInit:Err numOfElem is 0\n"); + return -EPERM; + } + + if (fifoSize == 0) + fifoSize = numOfElem; + else { + if (fifoPtr != NULL) { + printk(KERN_ERR "ethPortFifoInit: FIFIO allready initialized\n"); + return -EPERM; + } + + fifoSize += numOfElem; + + fifoPtr = kmalloc((1 + fifoSize) * sizeof(unsigned long), GFP_KERNEL); + + if (!fifoPtr) { + printk(KERN_ERR "ethPortFifoInit: Failed allocating memory for FIFO\n"); + return -ENOMEM; + } + + frontPtr = &fifoPtr[0]; + rearPtr = &fifoPtr[0]; + firstPtr = &fifoPtr[0]; + lastPtr = &fifoPtr[1 + fifoSize]; + } + + return 0; +} + +/******************************************************************************* +* ethPortFifoEnQueue +* +* DESCRIPTION: This routine adds the new data in the FIFO front. +* +* INPUTS: +* elem - the element data to insert in the FIFO front +* +* OUTPUTS: +* None. +* +* RETURNS: +* 1 - FIFO is full, insertion failed! +* 0 - on success. +* +*******************************************************************************/ +long ethPortFifoEnQueue(unsigned long elem) +{ + unsigned long *frontTmpPtr; + + frontTmpPtr = frontPtr; + + frontTmpPtr++; + + /* need to wrap ? */ + if (frontTmpPtr >= lastPtr) + frontTmpPtr = firstPtr; + + if (frontTmpPtr == rearPtr) { + /* fifo was full, insertion failed */ + printk(KERN_ERR "ethPortFifoInsert: fifo full err\n"); + return 1; + } else { + /* success, put in the data and update fifo control front ptr */ + *frontPtr = elem; + frontPtr = frontTmpPtr; + occupied++; + } + return 0; +} + +/******************************************************************************* +* ethPortFifoDeQueue +* +* DESCRIPTION: This routine gets the first data element from the FIFO rear. +* +* INPUTS: +* None. +* +* OUTPUTS: +* elemPtr <- pointer to update with the element got from FIFO rear +* +* RETURNS: +* 1 - FIFO is empty. +* 0 - on success. +* +*******************************************************************************/ +long ethPortFifoDeQueue(unsigned long *elemPtr) +{ + /* empty FIFO */ + if (rearPtr == frontPtr) { + printk(KERN_ERR "ethPortFifoDeQueue: fifo is EMPTY\n"); + return 1; + } + + /* get the data */ + *elemPtr = *rearPtr; + + rearPtr++; + + occupied--; + + /* need to wrap ? */ + if (rearPtr == lastPtr) + rearPtr = firstPtr; + + return 0; +} + +/******************************************************************************* +* ethPortFifoEnQueuePossible +* +* DESCRIPTION: This routine returns a status if the number of elements can be +* inserted to FIFO. +* +* INPUTS: +* itemsCnt - the number of elements the user wants to insert +* +* OUTPUTS: +* None. +* +* RETURNS: +* 1 - there in`t enough space for the elements. +* 0 - there is enough space for the elements. +* +*******************************************************************************/ +long ethPortFifoEnQueuePossible(unsigned long elemCnt) +{ + if (fifoSize - occupied > elemCnt) + return 0; + else + return 1; +} + +/******************************************************************************/ +/*********************** ethernet port FIFO section end ***********************/ +/******************************************************************************/ +/******************************************************************************* +* dropThePacket +* +* DESCRIPTION: This routine drops the packet. +* +* +* INPUTS: +* ctrlSeg - packet segment control +* +* OUTPUTS: +* None +* +* RETURNS: +* <0 - the errno to pass to user app. +* +*******************************************************************************/ +static int dropThePacket(unsigned long ctrlSeg) +{ + unsigned long readCnt; + unsigned long sink; + unsigned long *segmentPtr; + unsigned long queueNum; + unsigned long segNumber; + + /* get the number of segments of the packet */ + segNumber = (ETH_PORT_FIFO_ELEM_CNT_MASK & ctrlSeg); + + /* allocate mem to copy the segments to */ + segmentPtr = kmalloc(segNumber * sizeof(unsigned long), GFP_KERNEL); + + if (!segmentPtr) { + printk(KERN_ERR "dropThePacket: Failed allocating memory\n"); + return -ENOMEM; + } + readCnt = 0; + + /* copy the segments from FIFO to allocated memory */ + while (readCnt < segNumber) { + /* read the segment pointer */ + if (ethPortFifoDeQueue(&segmentPtr[readCnt]) != 0) { + panic("dropThePacket: expecting more segments in fifo"); + return -EIO; + } + readCnt++; + + /* read the segment length, not needed by free routine */ + if (ethPortFifoDeQueue(&sink) != 0) { + panic("dropThePacket: expecting more segments in fifo"); + return -EIO; + } + } + /* get the queue number from control segment */ + queueNum = (ctrlSeg & ETH_PORT_FIFO_QUE_NUM_MASK) >> + ETH_PORT_FIFO_QUE_NUM_OFFSET; + + bspEthRxPacketFree((unsigned char **)segmentPtr, readCnt, queueNum); + kfree(segmentPtr); + return -ENOBUFS; +} + +/******************************************************************************* +* prestera_smi_read +* +* DESCRIPTION: This routine reads a packet from the network interface FIFO. The +* first segment in FIFO is the control, which includes some +* additional information regarding the packet. The next segments +* are the packet data and for Rx packets, the segment lengths. +* +* INPUTS: +* filp - device descriptor +* count - the number of segments in the buffer +* f_pos - position in the file (not used) +* +* OUTPUTS: +* buf <- the buffer to put the packet segments in +* +* RETURNS: +* >0 - the number of segments in the read packet. +* 0 - there are no more packets to read. +* +*******************************************************************************/ +ssize_t prestera_smi_read(struct file *filp, + char *buf, + size_t count, + loff_t *f_pos) +{ + ssize_t readCnt; + unsigned long segNumber; + static unsigned long fifoData[MAX_SEG * 2 + 1]; + unsigned long wordNumber; + + readCnt = 0; + + /* read the first segment (control segment) from FIFO */ + if (ethPortFifoDeQueue(&fifoData[readCnt++]) != 0) + /* fifo is empty, no more data */ + return 0; + + /* extract from control segment needed data */ + segNumber = (ETH_PORT_FIFO_ELEM_CNT_MASK & fifoData[0]); + dprintk("presteraSmi_read, segNumber=%ld\n", segNumber); + + /* extract from the fifo the data segments */ + if (ETH_PORT_FIFO_TYPE_RX_MASK & fifoData[0]) { + wordNumber = (segNumber * 2) + 1; + + /* validate the number of words is not too big to copy to user */ + if ((wordNumber * sizeof(unsigned long)) > count) { + /* The control segments indicates too many segments for the packet, */ + /* we can not pass it to user, it is dropped and freed! */ + return dropThePacket(fifoData[0]); + } + /* RX packet segments */ + while (readCnt < wordNumber) { + if (ethPortFifoDeQueue(&fifoData[readCnt]) != 0) { + panic("presteraSmi_read: expecting more segments in fifo"); + return -EIO; + } + readCnt++; + + if (ethPortFifoDeQueue(&fifoData[readCnt]) != 0) { + panic("presteraSmi_read: expecting more segments in fifo"); + return -EIO; + } + readCnt++; + } + } else { /* TX Complete packet segments */ + wordNumber = segNumber + 1; + + /* validate the number of words is not too big to copy to user */ + if ((wordNumber * sizeof(unsigned long)) > count) { + /* return the first segment back to the fifo */ + if (ethPortFifoEnQueue(fifoData[0]) != 0) { + panic("presteraSmi_read: expecting that fifo is not full"); + return -EIO; + } + /* put the rest of the segments back to the fifo*/ + while (readCnt < wordNumber) { + if (ethPortFifoDeQueue(&fifoData[readCnt]) != 0) { + panic("presteraSmi_read: expecting more segments in fifo"); + return -EIO; + } + if (ethPortFifoEnQueue(fifoData[readCnt]) != 0) { + panic("presteraSmi_read: expecting that fifo is not full"); + return -EIO; + } + readCnt++; + } + return -ENOBUFS; + } + + while (readCnt < wordNumber) { + if (ethPortFifoDeQueue(&fifoData[readCnt]) != 0) { + panic("presteraSmi_read: expecting more segments in fifo"); + return -EIO; + } + readCnt++; + } + } + + readCnt *= sizeof(unsigned long); + + if (copy_to_user((char *)buf, (unsigned long *)fifoData, readCnt)) { + printk(KERN_ERR "presteraSmi_read: copy_to_user FAULT\n"); + return -EFAULT; + } + + return readCnt; +} + +/******************************************************************************* +* prestera_smi_write +* +* DESCRIPTION: This routine sends the packet pointed by the segments in the buf +* poiner over the network interface. +* +* INPUTS: +* filp - device descriptor +* buf - the buffer to be written +* count - the number of segments in the buffer +* f_pos - position in the file (not used) +* +* OUTPUTS: +* None. +* +* RETURNS: +* -1 - FIFO is empty. +* <0 - on success. +* +*******************************************************************************/ +ssize_t prestera_smi_write(struct file *filp, + const char *buf, + size_t count, + loff_t *f_pos) +{ + unsigned char *segmentListPtr[MAX_SEG]; + unsigned long segmentLen[MAX_SEG]; + const unsigned long *bufPtr; + unsigned long txQueue; + + bufPtr = (const unsigned long *)buf; + + if (count > MAX_SEG) { + printk(KERN_ERR "%s: count too big\n", __func__); + return -1; + } + + /* the segment list is first in the bufPtr array */ + if (copy_from_user(segmentListPtr, &bufPtr[0], sizeof(unsigned long) * count)) { + printk(KERN_ERR "%s: copy_from_user FAULT\n", __func__); + return -EFAULT; + } + + /* the segment length is second in the bufPtr array */ + if (copy_from_user(segmentLen, &bufPtr[count], sizeof(unsigned long) * count)) { + printk(KERN_ERR "%s: copy_from_user FAULT\n", __func__); + return -EFAULT; + } + + /* The txQueue is in the 8 leftmost bits of segmentLen[0]. + segmentLen[0] is very small and will never get to 2^24 size. gc + Read about it in + cpssEnabler/mainExtDrv/src/gtExtDrv/gtLinuxXcat/gtXcatEthPortControl.c + */ + + txQueue = (segmentLen[0] & 0xff000000) >> 24; + segmentLen[0] &= 0x00ffffff; + +#ifdef MV_DEBUG + { + int i; + printk(KERN_INFO "txQueue = %ld\n", txQueue); + printk(KERN_INFO "in %s, Tx ", __func__); + for (i = 0; i < count; i++) + printk(KERN_INFO "seg[%d]=0x%X (%d) ", i, + (int)segmentListPtr[i], (int)segmentLen[i]); + printk(KERN_INFO "\n"); + } +#endif + + if (bspEthPortTxQueue(segmentListPtr, segmentLen, count, txQueue)) { + printk(KERN_ERR "%s: bspEthPortTxQueue err\n", __func__); + return -1; + } + dprintk("%s: EXIT\n", __func__); + return 1; +} + +int prestera_smi_eth_port_rx_dsr_cnt(void) +{ + return (rx_DSR == -1) ? 0 : rx_DSR; +} + + +int prestera_smi_eth_port_tx_dsr_cnt(void) +{ + return (tx_DSR == -1) ? 0 : tx_DSR; +} + + +/******************************************************************************* +* prestera_smi_eth_port_rx_dsr +* +* DESCRIPTION: +* This is the PresteraSMI ethernet port Rx Deferred-Service-Routine (DSR), +* reponsible for inserting the packet segments and segment lengths to the +* FIFO. The routine wakes the netIfintTask thread. +* +* INPUTS: +* segmentList - A list of pointers to the packets segments. +* segmentLen - A list of segement length. +* numOfSegments - The number of segment in segment list. +* queueNum - the received queue number +* +* OUTPUTS: +* None. +* +* RETURNS:ppTq +* 0 on success, or +* 1 otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +unsigned long prestera_smi_eth_port_rx_dsr(unsigned char *segmentList[], + unsigned long segmentLen[], + unsigned long numOfSegments, + unsigned long queueNum) +{ + unsigned long firstElem; + int i; + + /* validate that there is ample space for the packet in FIFO */ + if (ethPortFifoEnQueuePossible(numOfSegments * 2 + 1) != 0) + return 1; + + /* set the first element (packet control) and insert to FIFO */ + firstElem = (queueNum << ETH_PORT_FIFO_QUE_NUM_OFFSET) | + numOfSegments | + ETH_PORT_FIFO_TYPE_RX_MASK; + + if (ethPortFifoEnQueue(firstElem) != 0) { + panic("presteraSmi_eth_port_rx_DSR: ethPortFifoEnQueue failed\n"); + return 1; + } + + /* insert all packet segments and segment lengths to FIFO */ + for (i = 0; i < numOfSegments; i++) { + if (ethPortFifoEnQueue((unsigned long)segmentList[i]) != 0) { + panic("presteraSmi_eth_port_rx_DSR: ethPortFifoEnQueue failed\n"); + return 1; + } + + if (ethPortFifoEnQueue((unsigned long)segmentLen[i]) != 0) { + panic("presteraSmi_eth_port_rx_DSR: ethPortFifoEnQueue failed\n"); + return 1; + } + } + rx_DSR++; + + /* awake reading process */ + up(netIfIntTaskSemPtr); + + return 0; +} + +/******************************************************************************* +* prestera_smi_eth_port_tx_end_dsr +* +* DESCRIPTION: +* This is the presteraSmi ethernet port Tx Complete Deferred-Service-Routine (DSR), +* reponsible for inserting the packet segments to the FIFO. The routine +* wakes the presteraSmi interrupt thread. +* +* INPUTS: +* segmentList - A list of pointers to the packets segments. +* numOfSegments - The number of segment in segment list. +* +* OUTPUTS: +* None. +* +* RETURNS: +* 0 on success, or +* 1 otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +unsigned long prestera_smi_eth_port_tx_end_dsr(unsigned char *segmentList[], + unsigned long numOfSegments) +{ + int i; + + /* validate that there is ample space for the packet in FIFO */ + if (ethPortFifoEnQueuePossible(numOfSegments + 1) != 0) { + panic("presteraSmi_eth_port_tx_end_DSR: ethPortFifoEnQueue failed\n"); + return 1; + } + + if (ethPortFifoEnQueue(numOfSegments) != 0) { + panic("presteraSmi_eth_port_tx_end_DSR: ethPortFifoEnQueue failed\n"); + return 1; + } + + /* insert all packet segments to FIFO */ + for (i = 0; i < numOfSegments; i++) { + if (ethPortFifoEnQueue((unsigned long)segmentList[i]) != 0) { + panic("presteraSmi_eth_port_tx_end_DSR: ethPortFifoEnQueue failed\n"); + return 1; + } + } + + tx_DSR++; + + /* awake reading process */ + up(netIfIntTaskSemPtr); + + return 0; +} + +/************************************************************************ +* +* presteraSmi_cleanup +* +************************************************************************/ +void prestera_smi_cleanup(void) +{ + presteraSmi_initialized = -1; +} + +#ifdef MV_DEBUG +static void ioctl_cmd_pr(unsigned int cmd) +{ + char *dir; + + static const char const *prestera_ioctls[] = { + [_IOC_NR(PRESTERA_SMI_IOC_WRITEREG)] = "WRITEREG", + [_IOC_NR(PRESTERA_SMI_IOC_READREG)] = "READREG", + [_IOC_NR(PRESTERA_SMI_IOC_WRITEREGDIRECT)] = "WRITEREGDIRECT", + [_IOC_NR(PRESTERA_SMI_IOC_READREGDIRECT)] = "READREGDIRECT", + [_IOC_NR(PRESTERA_SMI_IOC_WRITEREGFIELD)] = "WRITEREGFIELD", + [_IOC_NR(PRESTERA_SMI_IOC_READREGRAM)] = "READREGRAM", + [_IOC_NR(PRESTERA_SMI_IOC_WRITEREGRAM)] = "WRITEREGRAM", + [_IOC_NR(PRESTERA_SMI_IOC_READREGVEC)] = "READREGVEC", + [_IOC_NR(PRESTERA_SMI_IOC_WRITEREGVEC)] = "WRITEREGVEC", + [_IOC_NR(PRESTERA_SMI_IOC_ETHPORTENABLE)] = "ETHPORTENABLE", + [_IOC_NR(PRESTERA_SMI_IOC_ETHPORTDISABLE)] = "ETHPORTDISABLE", + [_IOC_NR(PRESTERA_SMI_IOC_ETHPORTRXINIT)] = "ETHPORTRXINIT", + [_IOC_NR(PRESTERA_SMI_IOC_ETHPORTTXINIT)] = "ETHPORTTXINIT", + [_IOC_NR(PRESTERA_SMI_IOC_ETHPORTFREEBUF)] = "ETHPORTFREEBUF", + [_IOC_NR(PRESTERA_SMI_IOC_ETHPORTRXBIND)] = "ETHPORTRXBIND", + [_IOC_NR(PRESTERA_SMI_IOC_ETHPORTTXBIND)] = "ETHPORTTXBIND", + [_IOC_NR(PRESTERA_SMI_IOC_NETIF_WAIT)] = "NETIF_WAIT", + [_IOC_NR(PRESTERA_SMI_IOC_TXMODE_SET)] = "TXMODE_SET", + [_IOC_NR(PRESTERA_SMI_IOC_CPUCODE_TO_QUEUE)] = "CPUCODE_TO_QUEUE", + [_IOC_NR(PRESTERA_SMI_IOC_MUXSET)] = "MUXSET", + [_IOC_NR(PRESTERA_SMI_IOC_MUXGET)] = "MUXGET", +}; + + #define PRESTERA_IOCTLS ARRAY_SIZE(prestera_ioctls) + + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + dir = "--"; + break; + + case _IOC_READ: + dir = "r-"; + break; + + case _IOC_WRITE: + dir = "-w"; + break; + + case _IOC_READ | _IOC_WRITE: + dir = "rw"; + break; + + default: + dir = "*ERR*"; + break; + } + printk(KERN_INFO "got ioctl '%c', dir=%s, #%d (0x%08x) ", + _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); + + if (_IOC_NR(cmd) < PRESTERA_IOCTLS) + printk("%s\n", prestera_ioctls[_IOC_NR(cmd)]); +} +#endif /* MV_DEBUG */ + + +/************************************************************************ +* +* presteraSmi_ioctl: ioctl() implementation +* +************************************************************************/ +int prestera_smi_ioctl(unsigned int cmd, unsigned long arg) +{ + struct SMI_REG smiReg; + struct SMI_REG_RAM_STC smiRegRam; + unsigned long i; + unsigned long smiRegVal; + unsigned int numOfTxBufs; + unsigned int txMode; + int retStatus; + struct MUX_PARAM muxParam; + struct RX_INIT_PARAM rxParam; + struct RX_FREE_BUF_PARAM bufFreeParam; + struct CPU_CODE_TO_QUEUE_PARAM cpuCodeToQueueParam; + + if (presteraSmi_initialized == -1) + return -ENODEV; + +#ifdef MV_DEBUG + ioctl_cmd_pr(cmd); +#endif + + /* GETTING DATA */ + switch (cmd) { + case PRESTERA_SMI_IOC_ETHPORTRXBIND: + case PRESTERA_SMI_IOC_ETHPORTTXBIND: + case PRESTERA_SMI_IOC_ETHPORTENABLE: + case PRESTERA_SMI_IOC_ETHPORTDISABLE: + break; + + case PRESTERA_SMI_IOC_READREG: + case PRESTERA_SMI_IOC_WRITEREG: + /* read and parse user data structurr */ + if (copy_from_user(&smiReg, (struct SMI_REG *)arg, sizeof(struct SMI_REG))) + goto ioctlFault; + break; + case PRESTERA_SMI_IOC_MUXSET: + /* read and parse user data structurr */ + if (copy_from_user(&muxParam, (struct MUX_PARAM *)arg, sizeof(struct MUX_PARAM))) + goto ioctlFault; + break; + + case PRESTERA_SMI_IOC_READREGRAM: + case PRESTERA_SMI_IOC_WRITEREGRAM: + if (copy_from_user(&smiRegRam, (struct SMI_REG_RAM_STC *)arg, + sizeof(struct SMI_REG_RAM_STC))) + goto ioctlFault; + + case PRESTERA_SMI_IOC_TXMODE_SET: + if (copy_from_user(&txMode, (unsigned int *)arg, sizeof(unsigned int))) + goto ioctlFault; + break; + + case PRESTERA_SMI_IOC_CPUCODE_TO_QUEUE: + /* read and parse user data structure */ + if (copy_from_user(&cpuCodeToQueueParam, + (struct CPU_CODE_TO_QUEUE_PARAM *)arg, + sizeof(struct CPU_CODE_TO_QUEUE_PARAM))) + goto ioctlFault; + break; + + case PRESTERA_SMI_IOC_ETHPORTTXINIT: + /* read and parse user data structure */ + if (copy_from_user(&numOfTxBufs, (unsigned int *)arg, sizeof(unsigned int))) + goto ioctlFault; + break; + case PRESTERA_SMI_IOC_ETHPORTRXINIT: + /* read and parse user data structure */ + if (copy_from_user(&rxParam, (struct RX_INIT_PARAM *)arg, sizeof(struct RX_INIT_PARAM))) + goto ioctlFault; + break; + + case PRESTERA_SMI_IOC_ETHPORTFREEBUF: + /* read and parse user data structure */ + if (copy_from_user(&bufFreeParam, (struct RX_FREE_BUF_PARAM *)arg, 2 * sizeof(long))) + goto ioctlFault; + if (copy_from_user(&bufFreeParam.segmentList, + (struct RX_FREE_BUF_PARAM *)(arg + (2 * sizeof(long))), + bufFreeParam.numOfSegments * sizeof(char *))) + goto ioctlFault; + break; + + case PRESTERA_SMI_IOC_NETIF_WAIT: + break; + + default: + printk(KERN_WARNING "Unknown ioctl (%x).\n", cmd); + break; + } + /* DOING SOMETHING */ + switch (cmd) { + /* Note. Both bspSmiReadReg and bspSmiWriteReg perform indirect operation */ + + case PRESTERA_SMI_IOC_READREG: + /* Read the user params */ + bspSmiReadReg(smiReg.slvId, 0, smiReg.regAddr, &smiReg.value); + break; + + case PRESTERA_SMI_IOC_WRITEREG: + /* Write the user params */ + bspSmiWriteReg(smiReg.slvId, 0, smiReg.regAddr, smiReg.value); + break; + case PRESTERA_SMI_IOC_MUXSET: + break; + + case PRESTERA_SMI_IOC_MUXGET: + break; + + case PRESTERA_SMI_IOC_READREGRAM: + for (i = 0; i < smiRegRam.arrLen; i++, smiRegRam.addr += 4) { + bspSmiReadReg(smiRegRam.devSlvId, 0, smiRegRam.addr, &smiRegVal); + if (copy_to_user((unsigned long *)(smiRegRam.dataArr + i), + &smiRegVal, + sizeof(smiRegVal))) + goto ioctlFault; + } + break; + + case PRESTERA_SMI_IOC_WRITEREGRAM: + for (i = 0; i < smiRegRam.arrLen; i++, smiRegRam.addr += 4) { + if (copy_from_user(&smiRegVal, + (unsigned long *)(smiRegRam.dataArr + i), + sizeof(smiRegVal))) + goto ioctlFault; + + bspSmiWriteReg(smiRegRam.devSlvId, 0, smiRegRam.addr, smiRegVal); + } + break; + + case PRESTERA_SMI_IOC_ETHPORTENABLE: + bspEthPortEnable(); + break; + + case PRESTERA_SMI_IOC_ETHPORTDISABLE: + bspEthPortDisable(); + break; + + case PRESTERA_SMI_IOC_TXMODE_SET: + bspEthPortTxModeSet((void *)txMode); + break; + + case PRESTERA_SMI_IOC_CPUCODE_TO_QUEUE: + bspEthCpuCodeToQueue(cpuCodeToQueueParam.cpuCode, + cpuCodeToQueueParam.queue); + break; + + case PRESTERA_SMI_IOC_ETHPORTTXINIT: + bspEthInit(1); /*?*/ + dprintk("tx bspEthInit - done\n"); + bspEthPortTxInit(numOfTxBufs); + ethPortFifoInit(numOfTxBufs * 2); + break; + + case PRESTERA_SMI_IOC_ETHPORTRXINIT: + bspEthInit(1); /*?*/ + dprintk("rx: bspEthInit - done\n"); + + dprintk("IOCTL_ETHPORTRXINIT:rxParam.rxBufPoolPtr 0x%x\n", + (unsigned int)rxParam.rxBufPoolPtr); + retStatus = bspEthPortRxInit(rxParam.rxBufPoolSize, + rxParam.rxBufPoolPtr, + rxParam.rxBufSize, + rxParam.numOfRxBufsPtr, + rxParam.headerOffset, + rxParam.rxQNum, + rxParam.rxQbufPercentage); + + if (retStatus != MV_OK) { + printk(KERN_ERR "PRESTERA_SMI_IOC_ETHPORTRXINIT,retStatus = %d\n", retStatus); + goto ioctlFault; + } + ethPortFifoInit((*(rxParam.numOfRxBufsPtr)) * 3); + + break; + + case PRESTERA_SMI_IOC_ETHPORTFREEBUF: + bspEthRxPacketFree(bufFreeParam.segmentList, + bufFreeParam.numOfSegments, + bufFreeParam.queueNum); + + break; + + case PRESTERA_SMI_IOC_ETHPORTRXBIND: + bspEthInputHookAdd((BSP_RX_CALLBACK_FUNCPTR)prestera_smi_eth_port_rx_dsr); + + break; + + case PRESTERA_SMI_IOC_ETHPORTTXBIND: + bspEthTxCompleteHookAdd((BSP_TX_COMPLETE_CALLBACK_FUNCPTR)prestera_smi_eth_port_tx_end_dsr); + break; + + case PRESTERA_SMI_IOC_NETIF_WAIT: + dprintk("netIfIntTaskSemPtr:%p\n", netIfIntTaskSemPtr); + + if (down_interruptible(netIfIntTaskSemPtr)) + return -ERESTARTSYS; + break; + + default: + printk(KERN_WARNING "Unknown ioctl (%x).\n", cmd); + break; + } + + /* Write back to user */ + switch (cmd) { + case PRESTERA_SMI_IOC_READREG: + case PRESTERA_SMI_IOC_READREGDIRECT: + if (copy_to_user((struct SMI_REG *)arg, &smiReg, sizeof(struct SMI_REG))) + goto ioctlFault; + break; + + case PRESTERA_SMI_IOC_MUXGET: + if (copy_to_user((struct MUX_PARAM *)arg, &muxParam, sizeof(struct MUX_PARAM))) + goto ioctlFault; + break; + + case PRESTERA_SMI_IOC_NETIF_WAIT: + case PRESTERA_SMI_IOC_WRITEREG: + case PRESTERA_SMI_IOC_MUXSET: + case PRESTERA_SMI_IOC_READREGRAM: + case PRESTERA_SMI_IOC_WRITEREGRAM: + case PRESTERA_SMI_IOC_WRITEREGDIRECT: + case PRESTERA_SMI_IOC_WRITEREGFIELD: + case PRESTERA_SMI_IOC_ETHPORTENABLE: + case PRESTERA_SMI_IOC_ETHPORTDISABLE: + case PRESTERA_SMI_IOC_TXMODE_SET: + case PRESTERA_SMI_IOC_CPUCODE_TO_QUEUE: + case PRESTERA_SMI_IOC_ETHPORTTXINIT: + case PRESTERA_SMI_IOC_ETHPORTFREEBUF: + case PRESTERA_SMI_IOC_ETHPORTRXBIND: + case PRESTERA_SMI_IOC_ETHPORTTXBIND: + case PRESTERA_SMI_IOC_ETHPORTRXINIT: + break; + + default: + printk(KERN_WARNING "Unknown ioctl (%x).\n", cmd); + break; + } + return 0; + +ioctlFault: + printk(KERN_ERR "IOCTL: FAULT\n"); + return -EFAULT; +} + +/************************************************************************ +* +* presteraSmi_init +* +************************************************************************/ +int prestera_smi_init(void) +{ + netIfIntTaskSemPtr = &netIfIntTaskSem; + + /* The netIf user process will wait on it */ + sema_init(netIfIntTaskSemPtr, 1); + + presteraSmi_initialized = 1; + + rx_DSR = tx_DSR = 0; + + fifoPtr = NULL; + occupied = 0; + fifoSize = 0; + frontPtr = NULL; + rearPtr = NULL; + firstPtr = NULL; + lastPtr = NULL; + + dprintk("%s done\n", __func__); + + return 0; +} Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_smi.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_smi.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_smi.h (working copy) @@ -0,0 +1,90 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************* + +* mv_prestera_smi.h +* +* DESCRIPTION: +* Includes defines and structures needed by the PP device driver +* +* DEPENDENCIES: +* None. +* +*******************************************************************************/ +#ifndef __MV_PRESTERA_SMI +#define __MV_PRESTERA_SMI + +#include +#include +#include "mv_prestera.h" + +int prestera_smi_init(void); +int prestera_smi_ioctl(unsigned int, unsigned long); +ssize_t prestera_smi_read(struct file *filp, char *buf, size_t count, loff_t *f_pos); +ssize_t prestera_smi_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos); +int prestera_smi_eth_port_rx_dsr_cnt(void); +int prestera_smi_eth_port_tx_dsr_cnt(void); +unsigned long prestera_smi_eth_port_tx_end_dsr(unsigned char*[], unsigned long); +unsigned long prestera_smi_eth_port_rx_dsr(unsigned char*[], unsigned long[], + unsigned long, unsigned long); +#endif /* __MV_PRESTERA_SMI */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_smi_glob.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_smi_glob.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_prestera_smi_glob.h (working copy) @@ -0,0 +1,201 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************* +* presteraSmiGlob.h +* +* DESCRIPTION: +* This file includes the declaration of the struct we want to send to kernel mode, +* from user mode. +* +* DEPENDENCIES: +* None. +* +*******************************************************************************/ +#ifndef __PRESTERA_SMI_GLOB__ +#define __PRESTERA_SMI_GLOB__ + +/************************ ethrnet port definitions ****************************/ +/* first 32 bit in Ethrnet Port FIFO (Rx and Tx) bits: */ +/* 0..6 - number of elements (values of: 0..127) */ +/* 7..9 - queue number (0..7) */ +/* 10..30 - reserved */ +/* 31 - TxEnd or Rx flag (0..1) */ +#define ETH_PORT_FIFO_ELEM_CNT_MASK (0x0000007F) +#define ETH_PORT_FIFO_QUE_NUM_OFFSET (7) +#define ETH_PORT_FIFO_QUE_NUM_MASK (0x00000380) +#define ETH_PORT_FIFO_TYPE_RX_MASK (1 << 31) +#define MAX_SEG (100) + +#define GT_MAX_RX_QUEUE_CNS 8 /* maximum number of RX queues */ + +struct SMI_REG { + unsigned long slvId; + unsigned long regAddr; + unsigned long value; +}; + +struct SMI_REG_RAM_STC { + unsigned long devSlvId; + unsigned long addr; + unsigned long *dataArr; + unsigned long arrLen; +}; + +struct SMI_REG_VEC_STC { + unsigned long devSlvId; + unsigned long *addrArr; + unsigned long *dataArr; + unsigned long arrLen; +}; + +struct write_smi_reg_field_STC { + unsigned long slvId; + unsigned long regAddr; + unsigned long mask; + unsigned long value; +}; + +struct RX_INIT_PARAM { + unsigned long rxBufPoolSize; + unsigned char *rxBufPoolPtr; + unsigned long rxBufSize; + unsigned long *numOfRxBufsPtr; + unsigned long headerOffset; + unsigned long rxQNum; + unsigned long rxQbufPercentage[GT_MAX_RX_QUEUE_CNS]; +}; + +struct RX_FREE_BUF_PARAM { + unsigned long numOfSegments; + unsigned long queueNum; + unsigned char *segmentList[MAX_SEG]; +}; + +struct CPU_CODE_TO_QUEUE_PARAM { + unsigned long cpuCode; + unsigned char queue; +}; + +/* + * enum bspEthNetPortType_ENT + * + * Description: + * This type defines types of switch ports for BSP ETH driver. + * + * Fields: + * bspEthNetPortType_cpss_E - packets forwarded to CPSS + * bspEthNetPortType_raw_E - packets forwarded to OS (without dsa removal) + * bspEthNetPortType_linux_E - packets forwarded to OS (with dsa removal) + * + * Note: + * The enum has to be compatible with MV_NET_OWN and ap_packet.c + * + */ +enum bspEthNetPortType_ENT { + /* cpss = the packet is sent directly to cpss */ + bspEthNetPortType_cpss_E = 0, + + /* raw = the packet is sent to the network stack WITHOUT removing the dsa */ + bspEthNetPortType_raw_E = 1, + + /* linux = the packet is sent to the network stack AFTER removing the dsa */ + bspEthNetPortType_linux_E = 2, + + bspEthNetPortType_numOfTypes +} ; + +struct MUX_PARAM { + unsigned long portNum; + enum bspEthNetPortType_ENT portType; +}; + +ssize_t presteraSmi_read(struct file *filp, char *buf, size_t count, loff_t *f_pos); +ssize_t presteraSmi_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos); +int presteraSmi_ioctl(unsigned int, unsigned long); +int presteraSmi_init(void); + +/************************ IOCTLs ****************************/ +#define PRESTERA_SMI_IOC_MAGIC 's' +#define PRESTERA_SMI_IOC_WRITEREG _IOWR(PRESTERA_SMI_IOC_MAGIC, 0, struct SMI_REG) +#define PRESTERA_SMI_IOC_READREG _IOWR(PRESTERA_SMI_IOC_MAGIC, 1, struct SMI_REG) +#define PRESTERA_SMI_IOC_WRITEREGDIRECT _IOWR(PRESTERA_SMI_IOC_MAGIC, 2, struct SMI_REG) +#define PRESTERA_SMI_IOC_READREGDIRECT _IOWR(PRESTERA_SMI_IOC_MAGIC, 3, struct SMI_REG) +#define PRESTERA_SMI_IOC_WRITEREGFIELD _IOWR(PRESTERA_SMI_IOC_MAGIC, 7, struct write_smi_reg_field_STC) +#define PRESTERA_SMI_IOC_READREGRAM _IOWR(PRESTERA_SMI_IOC_MAGIC, 8, struct SMI_REG_RAM_STC) +#define PRESTERA_SMI_IOC_WRITEREGRAM _IOWR(PRESTERA_SMI_IOC_MAGIC, 9, struct SMI_REG_RAM_STC) +#define PRESTERA_SMI_IOC_READREGVEC _IOWR(PRESTERA_SMI_IOC_MAGIC, 10, struct SMI_REG_VEC_STC) +#define PRESTERA_SMI_IOC_WRITEREGVEC _IOWR(PRESTERA_SMI_IOC_MAGIC, 11, struct SMI_REG_VEC_STC) +#define PRESTERA_SMI_IOC_ETHPORTENABLE _IO(PRESTERA_SMI_IOC_MAGIC, 13) +#define PRESTERA_SMI_IOC_ETHPORTDISABLE _IO(PRESTERA_SMI_IOC_MAGIC, 14) +#define PRESTERA_SMI_IOC_ETHPORTRXINIT _IOWR(PRESTERA_SMI_IOC_MAGIC, 15, struct RX_INIT_PARAM) +#define PRESTERA_SMI_IOC_ETHPORTTXINIT _IOW(PRESTERA_SMI_IOC_MAGIC, 16, long) +#define PRESTERA_SMI_IOC_ETHPORTFREEBUF _IOW(PRESTERA_SMI_IOC_MAGIC, 17, struct RX_FREE_BUF_PARAM) +#define PRESTERA_SMI_IOC_ETHPORTRXBIND _IO(PRESTERA_SMI_IOC_MAGIC, 18) +#define PRESTERA_SMI_IOC_ETHPORTTXBIND _IO(PRESTERA_SMI_IOC_MAGIC, 19) +#define PRESTERA_SMI_IOC_NETIF_WAIT _IO(PRESTERA_SMI_IOC_MAGIC, 20) +#define PRESTERA_SMI_IOC_TXMODE_SET _IOW(PRESTERA_SMI_IOC_MAGIC, 21, long) +#define PRESTERA_SMI_IOC_CPUCODE_TO_QUEUE _IOW(PRESTERA_SMI_IOC_MAGIC, 22, long) +#define PRESTERA_SMI_IOC_MUXSET _IOW(PRESTERA_SMI_IOC_MAGIC, 23, long) +#define PRESTERA_SMI_IOC_MUXGET _IOW(PRESTERA_SMI_IOC_MAGIC, 24, long) + +#endif /* __PRESTERA_SMI_GLOB__ */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_pss_api.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_pss_api.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_pss_api.c (working copy) @@ -0,0 +1,1661 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates + + This software file (the "File") is owned and distributed by Marvell + International Ltd. and/or its affiliates ("Marvell") under the following + alternative licensing terms. Once you have made an election to distribute the + File under one of the following license alternatives, please (i) delete this + introductory statement regarding license alternatives, (ii) delete the two + license alternatives that you have not elected to use and (iii) preserve the + Marvell copyright notice above. + +******************************************************************************** + Marvell Commercial License Option + + If you received this File from Marvell and you have entered into a commercial + license agreement (a "Commercial License") with Marvell, the File is licensed + to you under the terms of the applicable Commercial License. + +******************************************************************************** + Marvell GPL License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File in accordance with the terms and conditions of the General + Public License Version 2, June 1991 (the "GPL License"), a copy of which is + available along with the File in the license.txt file or by writing to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + DISCLAIMED. The GPL License provides additional details about this warranty + disclaimer. +******************************************************************************** + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +/******************************************************************************* +* pssBspApis.c - bsp APIs +* +* DESCRIPTION: +* API's supported by BSP. +* +* DEPENDENCIES: +* None. +* +*******************************************************************************/ +#include "mvOs.h" +#include "mv_pss_api.h" +#include "mv_prestera.h" + +/* Defines */ +#define SMI_WRITE_ADDRESS_MSB_REGISTER (0x00) +#define SMI_WRITE_ADDRESS_LSB_REGISTER (0x01) +#define SMI_WRITE_DATA_MSB_REGISTER (0x02) +#define SMI_WRITE_DATA_LSB_REGISTER (0x03) + +#define SMI_READ_ADDRESS_MSB_REGISTER (0x04) +#define SMI_READ_ADDRESS_LSB_REGISTER (0x05) +#define SMI_READ_DATA_MSB_REGISTER (0x06) +#define SMI_READ_DATA_LSB_REGISTER (0x07) + + +#define MARVELL_VEN_ID (0x11AB) + +static inline void smiWaitForStatus(unsigned long devSlvId) +{ +#ifdef SMI_WAIT_FOR_STATUS_DONE + unsigned long stat; + unsigned int timeOut; + int rc; + + /* wait for write done */ + timeOut = SMI_TIMEOUT_COUNTER; + do { + rc = smiReadReg(devSlvId, SMI_STATUS_REGISTER, &stat); + if (rc != MV_OK) + return; + if (--timeOut < 1) + return; + } while ((stat & SMI_STATUS_WRITE_DONE) == 0); +#endif +} + +#define SMI_STATUS_REGISTER (0x1f) + +#define SMI_STATUS_WRITE_DONE (0x02) +#define SMI_STATUS_READ_READY (0x01) + +#define SMI_WAIT_FOR_STATUS_DONE +#define SMI_TIMEOUT_COUNTER 10000 + +#define STUB_FAIL do { printk(KERN_INFO "stub function %s returning MV_NOT_SUPPORTED\n", __func__);\ + return MV_NOT_SUPPORTED; } while (1) + +#define STUB_FAIL_NULL do { printk(KERN_INFO "stub function %s returning MV_NOT_SUPPORTED\n", __func__); \ + return NULL; } while (1) + +#define STUB_OK do { printk(KERN_INFO "stub function %s returning MV_OK\n", __func__); \ + return MV_OK; } while (1) + +#define STUB_TBD do { printk(KERN_INFO "stub function TBD %s returning MV_FAIL\n", __func__); \ + return MV_FAIL; } while (1) + +static inline struct pci_dev *find_bdf(u32 bus, u32 device, u32 func) +{ + return pci_get_bus_and_slot(bus, PCI_DEVFN(device, func)); +} + +/* interrupt routine pointer */ +void(*bspIsrRoutine)(void) = NULL; +static unsigned long bspIsrParameter = -1; +static unsigned int lionSpecificRegMode; +static unsigned int ethPhySmiReg; /* Ethernet unit PHY SMI register offset */ + +/*** reset ***/ +/******************************************************************************* +* bspResetInit +* +* DESCRIPTION: +* This routine calls in init to do system init config for reset. +* +* INPUTS: +* none. +* +* OUTPUTS: +* none. +* +* RETURNS: +* MV_OK - on success. +* MV_FAIL - otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspResetInit(void) +{ + return MV_OK; +} + +/******************************************************************************* +* bspReset +* +* DESCRIPTION: +* This routine calls to reset of CPU. +* +* INPUTS: +* none. +* +* OUTPUTS: +* none. +* +* RETURNS: +* MV_OK - on success. +* MV_FAIL - otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspReset(void) +{ + kernel_restart(NULL); + return MV_OK; +} + +/*** cache ***/ +/******************************************************************************* +* bspCacheFlush +* +* DESCRIPTION: +* Flush to RAM content of cache +* +* INPUTS: +* type - type of cache memory data/intraction +* address_PTR - starting address of memory block to flush +* size - size of memory block +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspCacheFlush(enum bspCacheType cacheType, + void *address_PTR, + size_t size) +{ + switch (cacheType) { + case bspCacheType_InstructionCache_E: + return MV_BAD_PARAM; /* only data cache supported */ + + case bspCacheType_DataCache_E: + break; + + default: + return MV_BAD_PARAM; + } + + /* our area doesn't need cache flush/invalidate */ + return MV_OK; +} + +/******************************************************************************* +* bspCacheInvalidate +* +* DESCRIPTION: +* Invalidate current content of cache +* +* INPUTS: +* type - type of cache memory data/intraction +* address_PTR - starting address of memory block to flush +* size - size of memory block +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspCacheInvalidate(enum bspCacheType cacheType, + void *address_PTR, + size_t size) +{ + switch (cacheType) { + case bspCacheType_InstructionCache_E: + return MV_BAD_PARAM; /* only data cache supported */ + + case bspCacheType_DataCache_E: + break; + + default: + return MV_BAD_PARAM; + } + + /* our area doesn't need cache flush/invalidate */ + return MV_OK; +} + +/*** DMA ***/ +/******************************************************************************* +* bspDmaWrite +* +* DESCRIPTION: +* Write a given buffer to the given address using the Dma. +* +* INPUTS: +* address - The destination address to write to. +* buffer - The buffer to be written. +* length - Length of buffer in words. +* burstLimit - Number of words to be written on each burst. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* 1. The given buffer is allways 4 bytes aligned, any further allignment +* requirements should be handled internally by this function. +* 2. The given buffer may be allocated from an uncached memory space, and +* it's to the function to handle the cache flushing. +* 3. The Prestera Driver assumes that the implementation of the DMA is +* blocking, otherwise the Driver functionality might be damaged. +* +*******************************************************************************/ +int bspDmaWrite(unsigned long address, + unsigned long *buffer, + unsigned long length, + unsigned long burstLimit) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspDmaRead +* +* DESCRIPTION: +* Read a memory block from a given address. +* +* INPUTS: +* address - The address to read from. +* length - Length of the memory block to read (in words). +* burstLimit - Number of words to be read on each burst. +* +* OUTPUTS: +* buffer - The read data. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* 1. The given buffer is allways 4 bytes aligned, any further allignment +* requirements should be handled internally by this function. +* 2. The given buffer may be allocated from an uncached memory space, and +* it's to the function to handle the cache flushing. +* 3. The Prestera Driver assumes that the implementation of the DMA is +* blocking, otherwise the Driver functionality might be damaged. +* +*******************************************************************************/ +int bspDmaRead(unsigned long address, + unsigned long length, + unsigned long burstLimit, + unsigned long *buffer) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspCacheDmaMalloc +* +* DESCRIPTION: +* Allocate a cache free area for DMA devices. +* +* INPUTS: +* size_t bytes - number of bytes to allocate +* +* OUTPUTS: +* None. +* +* RETURNS: +* pointer to allocated data per success +* NULL - per failure to allocate space +* +* COMMENTS: +* None +* +*******************************************************************************/ +static unsigned long dma_malloc_base = -1; +static void *dma_area_base = (void *)-1; + +void *bspCacheDmaMalloc(size_t bytes) +{ + unsigned long dma_len = bytes; + void *dma_area; + + if (dma_malloc_base == -1) + dma_malloc_base = __pa(high_memory); + + request_mem_region(dma_malloc_base, dma_len, "prestera-dma"); + dma_area = (unsigned long *)ioremap_nocache(dma_malloc_base, dma_len); + + if (dma_area_base == (void *)-1) + dma_area_base = dma_area; + + return dma_area; +} + +/*** PCI ***/ +/******************************************************************************* +* bspPciConfigWriteReg +* +* DESCRIPTION: +* This routine write register to the PCI configuration space. +* +* INPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* regAddr - Register offset in the configuration space. +* data - data to write. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciConfigWriteReg(unsigned long busNo, + unsigned long devSel, + unsigned long funcNo, + unsigned long regAddr, + unsigned long data) +{ + struct pci_dev *dev; + + dev = find_bdf(busNo, devSel, funcNo); + if (dev) { + pci_write_config_dword(dev, regAddr, data); + pci_dev_put(dev); + return MV_OK; + } else + return MV_FAIL; +} + +/******************************************************************************* +* bspPciConfigReadReg +* +* DESCRIPTION: +* This routine read register from the PCI configuration space. +* +* INPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* regAddr - Register offset in the configuration space. +* +* OUTPUTS: +* data - the read data. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciConfigReadReg(unsigned long busNo, + unsigned long devSel, + unsigned long funcNo, + unsigned long regAddr, + unsigned long *data) +{ + struct pci_dev *dev; + + dev = find_bdf(busNo, devSel, funcNo); + if (dev) { + pci_read_config_dword(dev, (int)regAddr, (unsigned int *)data); + pci_dev_put(dev); + return MV_OK; + } else + return MV_FAIL; +} + +/******************************************************************************* +* bspPciGetResourceStart +* +* DESCRIPTION: +* This routine performs pci_resource_start. +* In INTEL64 this function must be used instead of reading the bar +* directly. +* +* INPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* barNo - Bar Number. +* +* OUTPUTS: +* ResourceStart - the address of the resource. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciGetResourceStart(unsigned long busNo, + unsigned long devSel, + unsigned long funcNo, + unsigned long barNo, + unsigned long long *resourceStart) +{ + struct pci_dev *dev; + + dev = find_bdf(busNo, devSel, funcNo); + if (dev) { + *resourceStart = pci_resource_start(dev, barNo); + pci_dev_put(dev); + return MV_OK; + } + return MV_FAIL; +} + +/******************************************************************************* +* bspPciGetResourceLen +* +* DESCRIPTION: +* This routine performs pci_resource_len. +* In INTEL64 this function must be used instead of reading the bar +* directly. +* +* INPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* barNo - Bar Number. +* +* OUTPUTS: +* ResourceLen - the address of the resource. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciGetResourceLen(unsigned long busNo, + unsigned long devSel, + unsigned long funcNo, + unsigned long barNo, + unsigned long long *resourceLen) +{ + struct pci_dev *dev; + + dev = find_bdf(busNo, devSel, funcNo); + if (dev) { + *resourceLen = pci_resource_len(dev, barNo); + pci_dev_put(dev); + return MV_OK; + } + return MV_FAIL; +} + +/******************************************************************************* +* bspPciFindDev +* +* DESCRIPTION: +* This routine returns the next instance of the given device (defined by +* vendorId & devId). +* +* INPUTS: +* vendorId - The device vendor Id. +* devId - The device Id. +* instance - The requested device instance. +* +* OUTPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciFindDev(unsigned short vendorId, + unsigned short devId, + unsigned long instance, + unsigned long *busNo, + unsigned long *devSel, + unsigned long *funcNo) +{ + struct pci_dev *dev = NULL; + int count = 0; + + *busNo = *devSel = *funcNo = 0; + + for_each_pci_dev(dev) { + if ((vendorId == 0xffff || dev->vendor == vendorId) && + (devId == 0xffff || dev->device == devId) && + /* skip the virtual bridge : 11ab8888 and class 0x06 */ + (!((vendorId == MARVELL_VEN_ID) && ((dev->device == 0x8888)))) && + ((dev->class & 0x00ff0000) != 0x00060000) && (count++ == instance)) { + *busNo = dev->bus->number; + *devSel = PCI_SLOT(dev->devfn); + *funcNo = PCI_FUNC(dev->devfn); + return MV_OK; + } + } + + return MV_FAIL; +} + +/******************************************************************************* +* bspPciGetIntVec +* +* DESCRIPTION: +* This routine return the PCI interrupt vector. +* +* INPUTS: +* pciInt - PCI interrupt number. +* +* OUTPUTS: +* intVec - PCI interrupt vector. +* +* RETURNS: +* MV_OK - on success. +* MV_FAIL - otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspPciGetIntVec(enum bspPciInt_PCI_INT pciInt, void **intVec) +{ + struct pci_dev *dev; + unsigned long b, d, f; + unsigned int devnum; + int rc = MV_FAIL; + + devnum = get_founddev(); + + /* Support MSYS with Internal PP */ + if ((devnum == 1) && (mvDevIdGet() != (-1))) { + *intVec = (void *)(unsigned long)(IRQ_AURORA_SW_CORE0); + printk(KERN_INFO "%s int vector 0x%x, internal Int 0x%x\n", __func__, (int)*intVec, pciInt); + rc = MV_OK; + } else { + /* Support MSYS in B2B Internal PP */ + /* iterate to the instance of pp_core_number */ + if (bspPciFindDev(MARVELL_VEN_ID, 0xffff, pciInt, &b, &d, &f) == MV_OK) { + dev = find_bdf(b, d, f); + if (dev != NULL) { + *intVec = (void *)(unsigned long)(dev->irq); + printk(KERN_INFO "%s int vector 0x%x, pci Int 0x%x\n", __func__, (int)*intVec, pciInt); + pci_dev_put(dev); + rc = MV_OK; + } + } else if (mvDevIdGet() != (-1)) { + *intVec = (void *)(unsigned long)(IRQ_AURORA_SW_CORE0); + printk(KERN_INFO "%s int vector 0x%x, internal Int 0x%x\n", __func__, (int)*intVec, pciInt); + rc = MV_OK; + } + } + + return rc; +} + +/******************************************************************************* +* bspPciGetIntMask +* +* DESCRIPTION: +* This routine return the PCI interrupt vector. +* +* INPUTS: +* pciInt - PCI interrupt number. +* +* OUTPUTS: +* intMask - PCI interrupt mask. +* +* RETURNS: +* MV_OK - on success. +* MV_FAIL - otherwise. +* +* COMMENTS: +* PCI interrupt mask should be used for interrupt disable/enable. +* +*******************************************************************************/ +int bspPciGetIntMask(enum bspPciInt_PCI_INT pciInt, + unsigned long *intMask) +{ + return MV_OK; +} + +/******************************************************************************* +* bspPciEnableCombinedAccess +* +* DESCRIPTION: +* This function enables / disables the Pci writes / reads combining +* feature. +* Some system controllers support combining memory writes / reads. When a +* long burst write / read is required and combining is enabled, the master +* combines consecutive write / read transactions, if possible, and +* performs one burst on the Pci instead of two. (see comments) +* +* INPUTS: +* enWrCombine - MV_TRUE enables write requests combining. +* enRdCombine - MV_TRUE enables read requests combining. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on sucess, +* MV_NOT_SUPPORTED - if the controller does not support this feature, +* MV_FAIL - otherwise. +* +* COMMENTS: +* 1. Example for combined write scenario: +* The controller is required to write a 32-bit data to address 0x8000, +* while this transaction is still in progress, a request for a write +* operation to address 0x8004 arrives, in this case the two writes are +* combined into a single burst of 8-bytes. +* +*******************************************************************************/ +int bspPciEnableCombinedAccess(int enWrCombine, + int enRdCombine) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspEthInit +* +* DESCRIPTION: Init the ethernet HW and HAL +* +* INPUTS: +* port - eth port number +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthInit(unsigned char port) +{ + STUB_OK; +} + +/******************************************************************************* +* bspSmiInitDriver +* +* DESCRIPTION: +* Init the TWSI interface +* +* INPUTS: +* None. +* +* OUTPUTS: +* smiAccessMode - direct/indirect mode +* +* RETURNS: +* MV_OK - on success +* MV_FAIL - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspSmiInitDriver(enum bspSmiAccessMode *smiAccessMode) +{ + ethPhySmiReg = ETH_SMI_REG(0/*MV_ETH_SMI_PORT*/); + STUB_FAIL; +} + +/******************************************************************************* +* _ethPhyRegRead +* +* +*******************************************************************************/ +static inline int _ethPhyRegRead(unsigned long phyAddr, unsigned long regOffs, unsigned short *data) +{ + unsigned int smiReg; + unsigned int timeout; + + /* check parameters */ + if ((phyAddr << ETH_PHY_SMI_DEV_ADDR_OFFS) & ~ETH_PHY_SMI_DEV_ADDR_MASK) { + printk(KERN_ERR "mvEthPhyRegRead: Err. Illegal PHY device address %lx\n", phyAddr); + return MV_FAIL; + } + if ((regOffs << ETH_PHY_SMI_REG_ADDR_OFFS) & ~ETH_PHY_SMI_REG_ADDR_MASK) { + printk(KERN_ERR "mvEthPhyRegRead: Err. Illegal PHY register offset %lx\n", regOffs); + return MV_FAIL; + } + + timeout = ETH_PHY_TIMEOUT; + /* wait till the SMI is not busy*/ + do { + /* read smi register */ + smiReg = MV_REG_READ(ethPhySmiReg); + if (timeout-- == 0) { + printk(KERN_ERR "mvEthPhyRegRead: SMI busy timeout\n"); + return MV_FAIL; + } + } while (smiReg & ETH_PHY_SMI_BUSY_MASK); + + /* fill the phy address and regiser offset and read opcode */ + smiReg = (phyAddr << ETH_PHY_SMI_DEV_ADDR_OFFS) | (regOffs << ETH_PHY_SMI_REG_ADDR_OFFS)| + ETH_PHY_SMI_OPCODE_READ; + + /* write the smi register */ + MV_REG_WRITE(ethPhySmiReg, smiReg); + + timeout = ETH_PHY_TIMEOUT; + + /*wait till readed value is ready */ + do { + /* read smi register */ + smiReg = MV_REG_READ(ethPhySmiReg); + + if (timeout-- == 0) { + printk(KERN_ERR "mvEthPhyRegRead: SMI read-valid timeout\n"); + return MV_FAIL; + } + } while (!(smiReg & ETH_PHY_SMI_READ_VALID_MASK)); + + /* Wait for the data to update in the SMI register */ + for (timeout = 0; timeout < ETH_PHY_TIMEOUT; timeout++) + ; + + *data = (unsigned short)(MV_REG_READ(ethPhySmiReg) & ETH_PHY_SMI_DATA_MASK); + + return MV_OK; +} + +/******************************************************************************* +* ethPhyRegWrite +* +* +*******************************************************************************/ +static inline int ethPhyRegWrite(unsigned long phyAddr, unsigned long regOffs, unsigned short data) +{ + unsigned int smiReg; + unsigned int timeout; + + /* check parameters */ + if ((phyAddr << ETH_PHY_SMI_DEV_ADDR_OFFS) & ~ETH_PHY_SMI_DEV_ADDR_MASK) { + printk(KERN_ERR "mvEthPhyRegWrite: Err. Illegal phy address 0x%lx\n", phyAddr); + return MV_BAD_PARAM; + } + if ((regOffs << ETH_PHY_SMI_REG_ADDR_OFFS) & ~ETH_PHY_SMI_REG_ADDR_MASK) { + printk(KERN_ERR "mvEthPhyRegWrite: Err. Illegal register offset 0x%lx\n", regOffs); + return MV_BAD_PARAM; + } + + timeout = ETH_PHY_TIMEOUT; + + /* wait till the SMI is not busy*/ + do { + /* read smi register */ + smiReg = MV_REG_READ(ethPhySmiReg); + if (timeout-- == 0) { + printk(KERN_ERR "mvEthPhyRegWrite: SMI busy timeout\n"); + return MV_TIMEOUT; + } + } while (smiReg & ETH_PHY_SMI_BUSY_MASK); + + /* fill the phy address and regiser offset and write opcode and data*/ + smiReg = (data << ETH_PHY_SMI_DATA_OFFS); + smiReg |= (phyAddr << ETH_PHY_SMI_DEV_ADDR_OFFS) | (regOffs << ETH_PHY_SMI_REG_ADDR_OFFS); + smiReg &= ~ETH_PHY_SMI_OPCODE_READ; + + /* write the smi register */ + MV_REG_WRITE(ethPhySmiReg, smiReg); + + return MV_OK; +} + +/******************************************************************************* +* smiReadReg +* +* +*******************************************************************************/ +int smiReadReg(unsigned long devSlvId, unsigned long regAddr, unsigned long *value) +{ + int ret; + unsigned short temp1 = 0; + + ret = _ethPhyRegRead(devSlvId, regAddr, &temp1); + *value = temp1; + return (MV_OK == ret) ? MV_OK : MV_FAIL; +} + +/******************************************************************************* +* smiWriteReg +* +* +*******************************************************************************/ +int smiWriteReg(unsigned long devSlvId, unsigned long regAddr, unsigned long value) +{ + /* Perform direct smi write reg */ + int ret; + + ret = ethPhyRegWrite(devSlvId, regAddr, value); + return (MV_OK == ret) ? MV_OK : MV_FAIL; +} + + +/******************************************************************************* +* bspSmiReadRegLionSpecificSet +* +* +*******************************************************************************/ +void bspSmiReadRegLionSpecificSet(void) +{ + lionSpecificRegMode = 1; +} + +/******************************************************************************* +* bspSmiReadReg +* +* DESCRIPTION: +* Reads a register from SMI slave. +* +* INPUTS: +* devSlvId - Slave Device ID +* actSmiAddr - actual smi addr to use (relevant for SX PPs) +* regAddr - Register address to read from. +* +* OUTPUTS: +* valuePtr - Data read from register. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspSmiReadReg(unsigned long devSlvId, + unsigned long actSmiAddr, + unsigned long regAddr, + unsigned long *valuePtr) +{ + /* Perform indirect smi read reg */ + int rc; + unsigned long msb; + unsigned long lsb; + + static int first_time = 1; + + if (first_time) { + enum bspSmiAccessMode smiAccessMode; + first_time = 0; + bspSmiInitDriver(&smiAccessMode); + } + /* fix erroneous devSlvId from cpss */ + if (lionSpecificRegMode) + MV_FIX_DEV_SLAVE_ID_4_LION(lionSpecificRegMode); + + /* write addr to read */ + msb = regAddr >> 16; + lsb = regAddr & 0xFFFF; + rc = smiWriteReg(devSlvId, SMI_READ_ADDRESS_MSB_REGISTER, msb); + if (rc != MV_OK) + return rc; + + rc = smiWriteReg(devSlvId, SMI_READ_ADDRESS_LSB_REGISTER, lsb); + if (rc != MV_OK) + return rc; + + smiWaitForStatus(devSlvId); + + /* read data */ + rc = smiReadReg(devSlvId, SMI_READ_DATA_MSB_REGISTER, &msb); + if (rc != MV_OK) + return rc; + + rc = smiReadReg(devSlvId, SMI_READ_DATA_LSB_REGISTER, &lsb); + if (rc != MV_OK) + return rc; + + *valuePtr = ((msb & 0xFFFF) << 16) | (lsb & 0xFFFF); + return 0; +} + +/******************************************************************************* +* bspSmiWriteReg +* +* DESCRIPTION: +* Writes a register to an SMI slave. +* +* INPUTS: +* devSlvId - Slave Device ID +* actSmiAddr - actual smi addr to use (relevant for SX PPs) +* regAddr - Register address to read from. +* value - data to be written. +* +* OUTPUTS: +* None, +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspSmiWriteReg(unsigned long devSlvId, + unsigned long actSmiAddr, + unsigned long regAddr, + unsigned long value) +{ + /* Perform indirect smi write reg */ + int rc; + unsigned long msb; + unsigned long lsb; + + /* write addr to read */ + msb = regAddr >> 16; + lsb = regAddr & 0xFFFF; + rc = smiWriteReg(devSlvId, SMI_READ_ADDRESS_MSB_REGISTER, msb); + if (rc != 0) + return rc; + + rc = smiWriteReg(devSlvId, SMI_READ_ADDRESS_LSB_REGISTER, lsb); + if (rc != 0) + return rc; + + /* write data to write */ + msb = value >> 16; + lsb = value & 0xFFFF; + rc = smiWriteReg(devSlvId, SMI_WRITE_DATA_MSB_REGISTER, msb); + if (rc != MV_OK) + return rc; + + rc = smiWriteReg(devSlvId, SMI_WRITE_DATA_LSB_REGISTER, lsb); + if (rc != MV_OK) + return rc; + + smiWaitForStatus(devSlvId); + + return MV_OK; +} + + +/*** TWSI ***/ +/******************************************************************************* +* bspTwsiInitDriver +* +* DESCRIPTION: +* Init the TWSI interface +* +* INPUTS: +* None. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspTwsiInitDriver(void) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspTwsiWaitNotBusy +* +* DESCRIPTION: +* Wait for TWSI interface not BUSY +* +* INPUTS: +* None. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspTwsiWaitNotBusy(void) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspTwsiMasterReadTrans +* +* DESCRIPTION: +* do TWSI interface Transaction +* +* INPUTS: +* devId - I2c slave ID +* pData - Pointer to array of chars (address / data) +* len - pData array size (in chars). +* stop - Indicates if stop bit is needed. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspTwsiMasterReadTrans(unsigned char devId, + mv_kmod_uintptr_t pData, + unsigned char len, + int stop) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspTwsiMasterWriteTrans +* +* DESCRIPTION: +* do TWSI interface Transaction +* +* INPUTS: +* devId - I2c slave ID +* pData - Pointer to array of chars (address / data) +* len - pData array size (in chars). +* stop - Indicates if stop bit is needed. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspTwsiMasterWriteTrans(unsigned char devId, + mv_kmod_uintptr_t pData, + unsigned char len, + int stop) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspIsr +* +* DESCRIPTION: +* This is the ISR reponsible for PP. +* +* INPUTS: +* irq - the Interrupt ReQuest number +* dev_id - the client data used as argument to the handler +* regs - holds a snapshot of the CPU context before interrupt +* +* OUTPUTS: +* None. +* +* RETURNS: +* IRQ_HANDLED allways +* +* COMMENTS: +* None. +* +*******************************************************************************/ +static irqreturn_t bspIsr(int irq, + void *dev_id, + struct pt_regs *regs) +{ + if (bspIsrRoutine != NULL) + bspIsrRoutine(); + + return IRQ_HANDLED; +} + +/******************************************************************************* +* bspIntConnect +* +* DESCRIPTION: +* Connect a specified C routine to a specified interrupt vector. +* +* INPUTS: +* vector - interrupt vector number to attach to +* routine - routine to be called +* parameter - parameter to be passed to routine +* +* OUTPUTS: +* None +* +* RETURNS: +* MV_OK - on success +* MV_FAIL - on error +* +* COMMENTS: +* None +* +*******************************************************************************/ +int bspIntConnect(unsigned long vector, + void (*routine) (void) , + unsigned long parameter) +{ + int rc; + + bspIsrParameter = parameter; + bspIsrRoutine = routine; + + rc = request_irq(vector, + (irq_handler_t)bspIsr, + IRQF_DISABLED, "PP_interrupt", (void *)&bspIsrParameter); + + return (0 == rc) ? MV_OK : MV_FAIL; + +} + +/******************************************************************************* +* extDrvIntEnable +* +* DESCRIPTION: +* Enable corresponding interrupt bits +* +* INPUTS: +* intMask - new interrupt bits +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_FAIL - on error +* +* COMMENTS: +* None +* +*******************************************************************************/ +int bspIntEnable(unsigned long intMask) +{ + enable_irq(intMask); + return MV_OK; +} + +/******************************************************************************* +* bspIntDisable +* +* DESCRIPTION: +* Disable corresponding interrupt bits. +* +* INPUTS: +* intMask - new interrupt bits +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_FAIL - on error +* +* COMMENTS: +* None +* +*******************************************************************************/ +int bspIntDisable(unsigned long intMask) +{ + disable_irq(intMask); + return MV_OK; +} + +unsigned long bspPhys2Virt(unsigned long pAddr) +{ + STUB_FAIL; +} + +/*** Ethernet access MII with the Packet Processor ***/ +/******************************************************************************* +* bspEthPortRxInit +* +* DESCRIPTION: Init the ethernet port Rx interface +* +* INPUTS: +* rxBufPoolSize - buffer pool size +* rxBufPool_PTR - the address of the pool +* rxBufSize - the buffer requested size +* numOfRxBufs_PTR - number of requested buffers, and actual buffers created +* headerOffset - packet header offset size +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortRxInit(unsigned long rxBufPoolSize, + unsigned char *rxBufPool_PTR, + unsigned long rxBufSize, + unsigned long *numOfRxBufs_PTR, + unsigned long headerOffset, + unsigned long rxQNum, + unsigned long rxQbufPercentage[]) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspEthPortTxInit +* +* DESCRIPTION: Init the ethernet port Tx interface +* +* INPUTS: +* numOfTxBufs - number of requested buffers +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortTxInit(unsigned long numOfTxBufs) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspEthPortEnable +* +* DESCRIPTION: Enable the ethernet port interface +* +* INPUTS: +* None. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortEnable(void) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspEthPortDisable +* +* DESCRIPTION: Disable the ethernet port interface +* +* INPUTS: +* None. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortDisable(void) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspEthInputHookAdd +* +* DESCRIPTION: +* This bind the user Rx callback +* +* INPUTS: +* userRxFunc - the user Rx callback function +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthInputHookAdd(BSP_RX_CALLBACK_FUNCPTR userRxFunc) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspEthPortTx +* +* DESCRIPTION: +* This function is called after a TxEnd event has been received, it passes +* the needed information to the Tapi part. +* +* INPUTS: +* segmentsList - A list of pointers to the packets segments. +* segmentsLen - A list of segment length. +* numOfSegments - The number of segment in segment list. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortTx(unsigned char *segmentsList[], + unsigned long segmentsLen[], + unsigned long numOfSegments) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspEthPortTxModeSet +* +* DESCRIPTION: Set the ethernet port tx mode +* +* INPUTS: +* if txMode == bspEthTxMode_asynch_E -- don't wait for TX done - free packet when interrupt received +* if txMode == bspEthTxMode_synch_E -- wait to TX done and free packet immediately +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful +* MV_NOT_SUPPORTED if input is wrong +* MV_FAIL if bspTxModeSetOn is zero +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortTxModeSet(void *stub) +{ + STUB_OK; +} + +/******************************************************************************* +* bspEthPortTxQueue +* +* DESCRIPTION: +* This function is called after a TxEnd event has been received, it passes +* the needed information to the Tapi part. +* +* INPUTS: +* segmentList - A list of pointers to the packets segments. +* segmentLen - A list of segment length. +* numOfSegments - The number of segment in segment list. +* txQueue - The TX queue. +* +* OUTPUTS: +* None. +* +* RETURNS: +* GT_OK if successful, or +* GT_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortTxQueue(unsigned char *segmentList[], + unsigned long segmentLen[], + unsigned long numOfSegments, + unsigned long txQueue) +{ + return bspEthPortTx(segmentList, segmentLen, numOfSegments); +} + +/******************************************************************************* +* bspEthTxCompleteHookAdd +* +* DESCRIPTION: +* This bind the user Tx complete callback +* +* INPUTS: +* userTxFunc - the user Tx callback function +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthTxCompleteHookAdd(BSP_TX_COMPLETE_CALLBACK_FUNCPTR userTxFunc) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspEthRxPacketFree +* +* DESCRIPTION: +* This routine frees the received Rx buffer. +* +* INPUTS: +* segmentsList - A list of pointers to the packets segments. +* numOfSegments - The number of segment in segment list. +* queueNum - Receive queue number +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthRxPacketFree(unsigned char *segmentsList[], + unsigned long numOfSegments, + unsigned long queueNum) +{ + STUB_FAIL; +} + +/******************************************************************************* +* bspEthCpuCodeToQueue +* +* DESCRIPTION: +* Binds DSA CPU code to RX queue. +* +* INPUTS: +* dsaCpuCode - DSA CPU code +* rxQueue - rx queue +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthCpuCodeToQueue(unsigned long dsaCpuCode, + unsigned char rxQueue) +{ + STUB_OK; +} + +/******************************************************************************* +* bspPciFindDevReset +* +* DESCRIPTION: +* Reset gPPDevId to make chance to find internal PP again +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* MV_OK - on success, +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciFindDevReset(void) +{ + STUB_OK; +} + + +/******************************************************************************* +* bspWarmRestart +* +* DESCRIPTION: +* This routine performs warm restart. +* +* INPUTS: +* none. +* +* OUTPUTS: +* none. +* +* RETURNS: +* MV_OK - on success. +* MV_FAIL - otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspWarmRestart(void) +{ + STUB_FAIL; +} + + +int bspSmiScan(int instance, int noisy) +{ + int found1 = 0; + int found2 = 0; + int i; + unsigned long data; + + /* scan for SMI devices */ + for (i = 0; i < 32; i++) { + bspSmiReadReg(i, 0, 0x3, &data); + if (data == 0xffffffff || data == 0xffff) + continue; + + bspSmiReadReg(i, 0, 0x50, &data); + if (data != 0x000011ab && data != 0xab110000) + continue; + + if (instance == found1++) { + bspSmiReadReg(i, 0, 0x4c, &data); + printk(KERN_INFO "Smi Scan found Marvell device at smi_addr 0x%x, reg 0x4c=0x%luX\n", + i, data); + found2 = 1; + break; + } + } + + if (!found2) { + if (noisy) + printk(KERN_INFO "Smi scan found no device\n"); + return -1; + } + + return i; +} + Index: drivers/net/ethernet/mvebu_net/prestera/platform/mv_pss_api.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mv_pss_api.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mv_pss_api.h (working copy) @@ -0,0 +1,1216 @@ +/******************************************************************************* + Copyright (C) Marvell International Ltd. and its affiliates +******************************************************************************* + Marvell BSD License Option + + If you received this File from Marvell, you may opt to use, redistribute and/or + modify this File under the following licensing terms. + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************* +* pssBspApis.h - bsp APIs +* +* DESCRIPTION: +* Enable managment of cache memory +* +* DEPENDENCIES: +* None. +* +*******************************************************************************/ +#ifndef __MV_PSS_API +#define __MV_PSS_API + +#include "mvTypes.h" +#include "mv_prestera_glob.h" + +/* Defines */ +#define ETH_PHY_TIMEOUT 10000 + +/* registers offsetes defines */ + +#define ETH_PHY_SMI_DATA_OFFS 0 /* Data */ +#define ETH_PHY_SMI_DATA_MASK (0xffff << ETH_PHY_SMI_DATA_OFFS) + +/* SMI register fields (ETH_PHY_SMI_REG) */ +#define ETH_PHY_SMI_DEV_ADDR_OFFS 16 /* PHY device address */ +#define ETH_PHY_SMI_DEV_ADDR_MASK (0x1f << ETH_PHY_SMI_DEV_ADDR_OFFS) + +#define ETH_PHY_SMI_REG_ADDR_OFFS 21 /* PHY device register address */ +#define ETH_PHY_SMI_REG_ADDR_MASK (0x1f << ETH_PHY_SMI_REG_ADDR_OFFS) + +#define ETH_PHY_SMI_OPCODE_OFFS 26 /* Write/Read opcode */ +#define ETH_PHY_SMI_OPCODE_MASK (3 << ETH_PHY_SMI_OPCODE_OFFS) +#define ETH_PHY_SMI_OPCODE_WRITE (0 << ETH_PHY_SMI_OPCODE_OFFS) +#define ETH_PHY_SMI_OPCODE_READ (1 << ETH_PHY_SMI_OPCODE_OFFS) + +#define ETH_PHY_SMI_READ_VALID_BIT 27 /* Read Valid */ +#define ETH_PHY_SMI_READ_VALID_MASK (1 << ETH_PHY_SMI_READ_VALID_BIT) + +#define ETH_PHY_SMI_BUSY_BIT 28 /* Busy */ +#define ETH_PHY_SMI_BUSY_MASK (1 << ETH_PHY_SMI_BUSY_BIT) + +#define PCIR_BARS 0x10 +#define PCIR_BAR(x) (PCIR_BARS + (x) * 4) + +/* + * dev_id[15:10] bits of DeviceID register of Prestera (0x4C) + * determine the chip type (xCat or xCat2). + * dev_id[15:10] == 0x37 stands for xCat + * dev_id[15:10] == 0x39 stands for xCat2 + */ +#define MV_PP_CHIP_TYPE_MASK 0x000FC00 +#define MV_PP_CHIP_TYPE_OFFSET 10 + +/* + * Typedef: enum bspCacheType + * + * Description: + * This type defines used cache types + * + * Fields: + * bspCacheType_InstructionCache_E - cache of commands + * bspCacheType_DataCache_E- cache of data + * + * Note: + * The enum has to be compatible with MV_MGMT_CACHE_TYPE. + **/ +enum bspCacheType { + bspCacheType_InstructionCache_E, + bspCacheType_DataCache_E +}; + +/* + * Description: Enumeration For PCI interrupt lines. + * + * Enumerations: + * bspPciInt_PCI_INT_A_E - PCI INT# A + * bspPciInt_PCI_INT_B_ - PCI INT# B + * bspPciInt_PCI_INT_C - PCI INT# C + * bspPciInt_PCI_INT_D - PCI INT# D + * + * Assumption: + * This enum should be identical to bspPciInt_PCI_INT. + */ +enum bspPciInt_PCI_INT { + bspPciInt_PCI_INT_A = 1, + bspPciInt_PCI_INT_B, + bspPciInt_PCI_INT_C, + bspPciInt_PCI_INT_D +}; + +/* + * enum bspSmiAccessMode + * + * Description: + * PP SMI access mode. + * + * Fields: + * bspSmiAccessMode_Direct_E - direct access mode (single/parallel) + * bspSmiAccessMode_inDirect_E - indirect access mode + * + * Note: + * The enum has to be compatible with MV_MGMT_CACHE_TYPE. + */ +enum bspSmiAccessMode { + bspSmiAccessMode_Direct_E, + bspSmiAccessMode_inDirect_E +}; + +/* fix erroneous devSlvId from cpss */ +#define MV_FIX_DEV_SLAVE_ID_4_LION(devSlvId) {\ + if (devSlvId > 0x13)\ + devSlvId = 0x14;\ + if (devSlvId >= 0x10)\ + devSlvId -= 0x10;\ +} + +/******************************************************************************* +* BSP_RX_CALLBACK_FUNCPTR +* +* DESCRIPTION: +* The prototype of the routine to be called after a packet was received +* +* INPUTS: +* segmentList - A list of pointers to the packets segments. +* segmentLen - A list of segment length. +* numOfSegments - The number of segment in segment list. +* queueNum - the received queue number +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_TRUE if it has handled the input packet and no further action should +* be taken with it, or +* MV_FALSE if it has not handled the input packet and normal processing. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +typedef int (*BSP_RX_CALLBACK_FUNCPTR)(unsigned char *segmentList[], + unsigned long segmentLen[], + unsigned long numOfSegments, + unsigned long queueNum); + +/******************************************************************************* +* BSP_TX_COMPLETE_CALLBACK_FUNCPTR +* +* DESCRIPTION: +* The prototype of the routine to be called after a packet was received +* +* INPUTS: +* segmentList - A list of pointers to the packets segments. +* numOfSegments - The number of segment in segment list. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_TRUE if it has handled the input packet and no further action should +* be taken with it, or +* MV_FALSE if it has not handled the input packet and normal processing. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +typedef int (*BSP_TX_COMPLETE_CALLBACK_FUNCPTR)(unsigned char *segmentList[], + unsigned long numOfSegments); + +/*** reset ***/ +/******************************************************************************* +* bspResetInit +* +* DESCRIPTION: +* This routine calls in init to do system init config for reset. +* +* INPUTS: +* none. +* +* OUTPUTS: +* none. +* +* RETURNS: +* MV_OK - on success. +* MV_FAIL - otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspResetInit(void); + +/******************************************************************************* +* bspReset +* +* DESCRIPTION: +* This routine calls to reset of CPU. +* +* INPUTS: +* none. +* +* OUTPUTS: +* none. +* +* RETURNS: +* MV_OK - on success. +* MV_FAIL - otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspReset(void); + +/*** PCI ***/ +/******************************************************************************* +* bspPciConfigWriteReg +* +* DESCRIPTION: +* This routine write register to the PCI configuration space. +* +* INPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* regAddr - Register offset in the configuration space. +* data - data to write. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciConfigWriteReg(unsigned long busNo, + unsigned long devSel, + unsigned long funcNo, + unsigned long regAddr, + unsigned long data); + + +/******************************************************************************* +* bspPciConfigReadReg +* +* DESCRIPTION: +* This routine read register from the PCI configuration space. +* +* INPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* regAddr - Register offset in the configuration space. +* +* OUTPUTS: +* data - the read data. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciConfigReadReg(unsigned long busNo, + unsigned long devSel, + unsigned long funcNo, + unsigned long regAddr, + unsigned long *data); + +/******************************************************************************* +* bspPciGetResourceStart +* +* DESCRIPTION: +* This routine performs pci_resource_start. +* In MIPS64 this function must be used instead of reading the bar +* directly. +* +* INPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* barNo - Bar Number. +* +* OUTPUTS: +* ResourceStart - the address of the resource. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciGetResourceStart(unsigned long busNo, + unsigned long devSel, + unsigned long funcNo, + unsigned long barNo, + unsigned long long *resourceStart); + +/******************************************************************************* +* bspPciGetResourceLen +* +* DESCRIPTION: +* This routine performs pci_resource_len. +* In MIPS64 this function must be used instead of reading the bar +* directly. +* +* INPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* barNo - Bar Number. +* +* OUTPUTS: +* ResourceLen - the address of the resource. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciGetResourceLen(unsigned long busNo, + unsigned long devSel, + unsigned long funcNo, + unsigned long barNo, + unsigned long long *resourceLen); + +/******************************************************************************* +* bspPciFindDev +* +* DESCRIPTION: +* This routine returns the next instance of the given device (defined by +* vendorId & devId). +* +* INPUTS: +* vendorId - The device vendor Id. +* devId - The device Id. +* instance - The requested device instance. +* +* OUTPUTS: +* busNo - PCI bus number. +* devSel - the device devSel. +* funcNo - function number. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciFindDev(unsigned short vendorId, + unsigned short devId, + unsigned long instance, + unsigned long *busNo, + unsigned long *devSel, + unsigned long *funcNo); + +/******************************************************************************* +* bspPciGetIntVec +* +* DESCRIPTION: +* This routine return the PCI interrupt vector. +* +* INPUTS: +* pciInt - PCI interrupt number. +* +* OUTPUTS: +* intVec - PCI interrupt vector. +* +* RETURNS: +* MV_OK - on success. +* MV_FAIL - otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspPciGetIntVec(enum bspPciInt_PCI_INT pciInt, + void **intVec); + +/******************************************************************************* +* bspPciGetIntMask +* +* DESCRIPTION: +* This routine return the PCI interrupt vector. +* +* INPUTS: +* pciInt - PCI interrupt number. +* +* OUTPUTS: +* intMask - PCI interrupt mask. +* +* RETURNS: +* MV_OK - on success. +* MV_FAIL - otherwise. +* +* COMMENTS: +* PCI interrupt mask should be used for interrupt disable/enable. +* +*******************************************************************************/ +int bspPciGetIntMask(enum bspPciInt_PCI_INT pciInt, + unsigned long *intMask); + +/******************************************************************************* +* bspPciEnableCombinedAccess +* +* DESCRIPTION: +* This function enables / disables the Pci writes / reads combining +* feature. +* Some system controllers support combining memory writes / reads. When a +* long burst write / read is required and combining is enabled, the master +* combines consecutive write / read transactions, if possible, and +* performs one burst on the Pci instead of two. (see comments) +* +* INPUTS: +* enWrCombine - MV_TRUE enables write requests combining. +* enRdCombine - MV_TRUE enables read requests combining. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on sucess, +* MV_NOT_SUPPORTED - if the controller does not support this feature, +* MV_FAIL - otherwise. +* +* COMMENTS: +* 1. Example for combined write scenario: +* The controller is required to write a 32-bit data to address 0x8000, +* while this transaction is still in progress, a request for a write +* operation to address 0x8004 arrives, in this case the two writes are +* combined into a single burst of 8-bytes. +* +*******************************************************************************/ +int bspPciEnableCombinedAccess(int enWrCombine, + int enRdCombine); + +/*** cache ***/ +/******************************************************************************* +* bspCacheFlush +* +* DESCRIPTION: +* Flush to RAM content of cache +* +* INPUTS: +* type - type of cache memory data/intraction +* address_PTR - starting address of memory block to flush +* size - size of memory block +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspCacheFlush(enum bspCacheType cacheType, + void *address_PTR, + size_t size); + +/******************************************************************************* +* bspCacheInvalidate +* +* DESCRIPTION: +* Invalidate current content of cache +* +* INPUTS: +* type - type of cache memory data/intraction +* address_PTR - starting address of memory block to flush +* size - size of memory block +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* +*******************************************************************************/ +int bspCacheInvalidate(enum bspCacheType cacheType, + void *address_PTR, + size_t size); + +/*** DMA ***/ +/******************************************************************************* +* bspDmaWrite +* +* DESCRIPTION: +* Write a given buffer to the given address using the Dma. +* +* INPUTS: +* address - The destination address to write to. +* buffer - The buffer to be written. +* length - Length of buffer in words. +* burstLimit - Number of words to be written on each burst. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* 1. The given buffer is allways 4 bytes aligned, any further allignment +* requirements should be handled internally by this function. +* 2. The given buffer may be allocated from an uncached memory space, and +* it's to the function to handle the cache flushing. +* 3. The Prestera Driver assumes that the implementation of the DMA is +* blocking, otherwise the Driver functionality might be damaged. +* +*******************************************************************************/ +int bspDmaWrite(unsigned long address, + unsigned long *buffer, + unsigned long length, + unsigned long burstLimit); + +/******************************************************************************* +* bspDmaRead +* +* DESCRIPTION: +* Read a memory block from a given address. +* +* INPUTS: +* address - The address to read from. +* length - Length of the memory block to read (in words). +* burstLimit - Number of words to be read on each burst. +* +* OUTPUTS: +* buffer - The read data. +* +* RETURNS: +* MV_OK - on success, +* MV_FAIL - othersise. +* +* COMMENTS: +* 1. The given buffer is allways 4 bytes aligned, any further allignment +* requirements should be handled internally by this function. +* 2. The given buffer may be allocated from an uncached memory space, and +* it's to the function to handle the cache flushing. +* 3. The Prestera Driver assumes that the implementation of the DMA is +* blocking, otherwise the Driver functionality might be damaged. +* +*******************************************************************************/ +int bspDmaRead(unsigned long address, + unsigned long length, + unsigned long burstLimit, + unsigned long *buffer); + +/******************************************************************************* +* bspCacheDmaMalloc +* +* DESCRIPTION: +* Allocate a cache free area for DMA devices. +* +* INPUTS: +* size_t bytes - number of bytes to allocate +* +* OUTPUTS: +* None. +* +* RETURNS: +* pointer to allocated data per success +* NULL - per failure to allocate space +* +* COMMENTS: +* None +* +*******************************************************************************/ +void *bspCacheDmaMalloc(size_t bytes); + + /*** SMI ***/ +/******************************************************************************* +* bspSmiInitDriver +* +* DESCRIPTION: +* Init the TWSI interface +* +* INPUTS: +* None. +* +* OUTPUTS: +* smiAccessMode - direct/indirect mode +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspSmiInitDriver(enum bspSmiAccessMode *smiAccessMode); + +/******************************************************************************* +* bspSmiReadReg +* +* DESCRIPTION: +* Reads a register from SMI slave. +* +* INPUTS: +* devSlvId - Slave Device ID +* actSmiAddr - actual smi addr to use (relevant for SX PPs) +* regAddr - Register address to read from. +* +* OUTPUTS: +* valuePtr - Data read from register. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspSmiReadReg(unsigned long devSlvId, + unsigned long actSmiAddr, + unsigned long regAddr, + unsigned long *valuePtr); + + +/******************************************************************************* +* bspSmiReadRegLionSpecificSet +* +* DESCRIPTION: +* Set lios specific register mode in bspSmiReadReg +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* None +* +* COMMENTS: +* +*******************************************************************************/ +void bspSmiReadRegLionSpecificSet(void); + +/******************************************************************************* +* bspSmiWriteReg +* +* DESCRIPTION: +* Writes a register to an SMI slave. +* +* INPUTS: +* devSlvId - Slave Device ID +* actSmiAddr - actual smi addr to use (relevant for SX PPs) +* regAddr - Register address to read from. +* value - data to be written. +* +* OUTPUTS: +* None, +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspSmiWriteReg(unsigned long devSlvId, + unsigned long actSmiAddr, + unsigned long regAddr, + unsigned long value); + +/*** TWSI ***/ +/******************************************************************************* +* bspTwsiInitDriver +* +* DESCRIPTION: +* Init the TWSI interface +* +* INPUTS: +* None. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspTwsiInitDriver(void); + +/******************************************************************************* +* bspTwsiWaitNotBusy +* +* DESCRIPTION: +* Wait for TWSI interface not BUSY +* +* INPUTS: +* None. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspTwsiWaitNotBusy(void); + +/******************************************************************************* +* bspTwsiMasterReadTrans +* +* DESCRIPTION: +* do TWSI interface Transaction +* +* INPUTS: +* devId - I2c slave ID +* pData - Pointer to array of chars (address / data) +* len - pData array size (in chars). +* stop - Indicates if stop bit is needed. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspTwsiMasterReadTrans(unsigned char devId, + mv_kmod_uintptr_t pData, + unsigned char len, + int stop); + +/******************************************************************************* +* bspTwsiMasterWriteTrans +* +* DESCRIPTION: +* do TWSI interface Transaction +* +* INPUTS: +* devId - I2c slave ID +* pData - Pointer to array of chars (address / data) +* len - pData array size (in chars). +* stop - Indicates if stop bit is needed. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_ERROR - on hardware error +* +* COMMENTS: +* +*******************************************************************************/ +int bspTwsiMasterWriteTrans(unsigned char devId, + mv_kmod_uintptr_t pData, + unsigned char len, + int stop); + +/*** Ethernet Driver ***/ +/******************************************************************************* +* bspEthPortRxInit +* +* DESCRIPTION: Init the ethernet port Rx interface +* +* INPUTS: +* rxBufPoolSize - buffer pool size +* rxBufPool_PTR - the address of the pool +* rxBufSize - the buffer requested size +* numOfRxBufs_PTR - number of requested buffers, and actual buffers created +* headerOffset - packet header offset size +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortRxInit(unsigned long rxBufPoolSize, + unsigned char *rxBufPool_PTR, + unsigned long rxBufSize, + unsigned long *numOfRxBufs_PTR, + unsigned long headerOffset, + unsigned long rxQNum, + unsigned long rxQbufPercentage[]); + +/******************************************************************************* +* bspEthPortTxInit +* +* DESCRIPTION: Init the ethernet port Tx interface +* +* INPUTS: +* numOfTxBufs - number of requested buffers +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortTxInit(unsigned long numOfTxBufs); + +/******************************************************************************* +* bspEthPortEnable +* +* DESCRIPTION: Enable the ethernet port interface +* +* INPUTS: +* None. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortEnable(void); + +/******************************************************************************* +* bspEthPortDisable +* +* DESCRIPTION: Disable the ethernet port interface +* +* INPUTS: +* None. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortDisable(void); + +/******************************************************************************* +* bspEthPortTx +* +* DESCRIPTION: +* This function transmits a packet. +* +* INPUTS: +* segmentList - A list of pointers to the packets segments. +* segmentLen - A list of segment length. +* numOfSegments - The number of segment in segment list. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortTx(unsigned char *segmentList[], + unsigned long segmentLen[], + unsigned long numOfSegments); + +/******************************************************************************* +* bspEthInputHookAdd +* +* DESCRIPTION: +* This bind the user Rx callback +* +* INPUTS: +* userRxFunc - the user Rx callback function +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthInputHookAdd(BSP_RX_CALLBACK_FUNCPTR userRxFunc); + +/******************************************************************************* +* bspEthTxCompleteHookAdd +* +* DESCRIPTION: +* This bind the user Tx complete callback +* +* INPUTS: +* userTxFunc - the user Tx callback function +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthTxCompleteHookAdd(BSP_TX_COMPLETE_CALLBACK_FUNCPTR userTxFunc); + +/******************************************************************************* +* bspEthRxPacketFree +* +* DESCRIPTION: +* This routine frees the received Rx buffer. +* +* INPUTS: +* segmentList - A list of pointers to the packets segments. +* numOfSegments - The number of segment in segment list. +* queueNum - Receive queue number +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthRxPacketFree(unsigned char *segmentList[], + unsigned long numOfSegments, + unsigned long queueNum); + +/******************************************************************************* +* bspIntConnect +* +* DESCRIPTION: +* Connect a specified C routine to a specified interrupt vector. +* +* INPUTS: +* vector - interrupt vector number to attach to +* routine - routine to be called +* parameter - parameter to be passed to routine +* +* OUTPUTS: +* None +* +* RETURNS: +* MV_OK - on success +* MV_FAIL - on error +* +* COMMENTS: +* None +* +*******************************************************************************/ +int bspIntConnect(unsigned long vector, + void (*routine) (void) , + unsigned long parameter); + +/******************************************************************************* +* extDrvIntEnable +* +* DESCRIPTION: +* Enable corresponding interrupt bits +* +* INPUTS: +* intMask - new interrupt bits +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_FAIL - on error +* +* COMMENTS: +* None +* +*******************************************************************************/ +int bspIntEnable(unsigned long intMask); + +/******************************************************************************* +* extDrvIntDisable +* +* DESCRIPTION: +* Disable corresponding interrupt bits. +* +* INPUTS: +* intMask - new interrupt bits +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK - on success +* MV_FAIL - on error +* +* COMMENTS: +* None +* +*******************************************************************************/ +int bspIntDisable(unsigned long intMask); + +/******************************************************************************* +* bspEthPortTx +* +* DESCRIPTION: +* This function transmits a packet. +* +* INPUTS: +* segmentList - A list of pointers to the packets segments. +* segmentLen - A list of segment length. +* numOfSegments - The number of segment in segment list. +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortTx(unsigned char *segmentList[], + unsigned long segmentLen[], + unsigned long numOfSegments); + +/******************************************************************************* +* bspEthPortTxQueue +* +* DESCRIPTION: +* This function is called after a TxEnd event has been received, it passes +* the needed information to the Tapi part. +* +* INPUTS: +* segmentList - A list of pointers to the packets segments. +* segmentLen - A list of segment length. +* numOfSegments - The number of segment in segment list. +* txQueue - The TX queue. +* +* OUTPUTS: +* None. +* +* RETURNS: +* GT_OK if successful, or +* GT_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortTxQueue(unsigned char *segmentList[], + unsigned long segmentLen[], + unsigned long numOfSegments, + unsigned long txQueue); + +/******************************************************************************* +* bspEthCpuCodeToQueue +* +* DESCRIPTION: +* Binds DSA CPU code to RX queue. +* +* INPUTS: +* dsaCpuCode - DSA CPU code +* rxQueue - rx queue +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthCpuCodeToQueue(unsigned long dsaCpuCode, unsigned char rxQueue); + +/******************************************************************************* +* bspPciFindDevReset +* +* DESCRIPTION: +* Reset gPPDevId to make chance to find internal PP again +* +* INPUTS: +* None +* +* OUTPUTS: +* None +* +* RETURNS: +* MV_OK - on success, +* +* COMMENTS: +* +*******************************************************************************/ +int bspPciFindDevReset(void); + +/******************************************************************************* +* bspEthInit +* +* DESCRIPTION: Init the ethernet HW and HAL +* +* INPUTS: +* port - eth port number +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful, or +* MV_FAIL otherwise. +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthInit(unsigned char port); + +/******************************************************************************* +* bspEthPortTxModeSet +* +* DESCRIPTION: Set the ethernet port tx mode +* +* INPUTS: +* if txMode == bspEthTxMode_asynch_E -- don't wait for TX done - free packet when interrupt received +* if txMode == bspEthTxMode_synch_E -- wait to TX done and free packet immediately +* +* OUTPUTS: +* None. +* +* RETURNS: +* MV_OK if successful +* MV_NOT_SUPPORTED if input is wrong +* MV_FAIL if bspTxModeSetOn is zero +* +* COMMENTS: +* None. +* +*******************************************************************************/ +int bspEthPortTxModeSet(void *stub); + +unsigned long bspPhys2Virt(unsigned long pAddr); + +#endif /* __MV_PSS_API */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/mvOs.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mvOs.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mvOs.h (working copy) @@ -0,0 +1,127 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +*******************************************************************************/ +#ifndef _MV_OS_LNX_H_ +#define _MV_OS_LNX_H_ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include "mvTypes.h" + +/* Endianess macros. */ +#if defined(MV_CPU_LE) +#define MV_16BIT_LE(X) (X) +#define MV_32BIT_LE(X) (X) +#define MV_64BIT_LE(X) (X) +#define MV_16BIT_BE(X) MV_BYTE_SWAP_16BIT(X) +#define MV_32BIT_BE(X) MV_BYTE_SWAP_32BIT(X) +#define MV_64BIT_BE(X) MV_BYTE_SWAP_64BIT(X) +#elif defined(MV_CPU_BE) +#define MV_16BIT_LE(X) MV_BYTE_SWAP_16BIT(X) +#define MV_32BIT_LE(X) MV_BYTE_SWAP_32BIT(X) +#define MV_64BIT_LE(X) MV_BYTE_SWAP_64BIT(X) +#define MV_16BIT_BE(X) (X) +#define MV_32BIT_BE(X) (X) +#define MV_64BIT_BE(X) (X) +#else +#error "CPU endianess isn't defined!\n" +#endif + +#define INTER_REGS_VIRT_BASE 0 + +/*XXX: looks like error - should be 0x72000 - check + * neta + documentation for AC3 0x00072004 and bits e.g. + * ETH_PHY_SMI_BUSY_BIT + */ +#define MV_ETH_BASE_ADDR (0x70000) +#define MV_ETH_REGS_OFFSET(port) (MV_ETH_BASE_ADDR + (port) * 0x4000) +#define MV_ETH_REGS_BASE(p) MV_ETH_REGS_OFFSET(p) +#define ETH_SMI_REG(port) (MV_ETH_REGS_BASE(port) + 0x004) + +#define MV_32BIT_LE_FAST(val) \ + MV_32BIT_LE(val) + +#define MV_MEMIO32_READ(addr) \ + ((*((unsigned int *)(addr)))) + +#define MV_MEMIO32_WRITE(addr, data) \ + ((*((unsigned int *)(addr))) = ((unsigned int)(data))) + +#define MV_MEMIO_LE32_WRITE(addr, data) \ + MV_MEMIO32_WRITE(addr, MV_32BIT_LE_FAST(data)) + +static inline unsigned int MV_MEMIO_LE32_READ(unsigned int addr) +{ + unsigned int data; + + data = (unsigned int)MV_MEMIO32_READ(addr); + + return (unsigned int)MV_32BIT_LE_FAST(data); +} + +#define MV_REG_READ(offset) \ + (MV_MEMIO_LE32_READ(INTER_REGS_VIRT_BASE | (offset))) + +#define MV_REG_WRITE(offset, val) \ + MV_MEMIO_LE32_WRITE((INTER_REGS_VIRT_BASE | (offset)), (val)) + +#endif /* _MV_OS_LNX_H_ */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/mvTypes.h =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/mvTypes.h (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/mvTypes.h (working copy) @@ -0,0 +1,121 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#ifndef __INCmvTypesh +#define __INCmvTypesh + +/* Defines */ + +/* The following is a list of Marvell status */ +#define MV_ERROR (-1) +#define MV_OK (0) /* Operation succeeded */ +#define MV_FAIL (1) /* Operation failed */ +#define MV_BAD_VALUE (2) /* Illegal value (general) */ +#define MV_OUT_OF_RANGE (3) /* The value is out of range */ +#define MV_BAD_PARAM (4) /* Illegal parameter in function called */ +#define MV_BAD_PTR (5) /* Illegal pointer value */ +#define MV_BAD_SIZE (6) /* Illegal size */ +#define MV_BAD_STATE (7) /* Illegal state of state machine */ +#define MV_SET_ERROR (8) /* Set operation failed */ +#define MV_GET_ERROR (9) /* Get operation failed */ +#define MV_CREATE_ERROR (10) /* Fail while creating an item */ +#define MV_NOT_FOUND (11) /* Item not found */ +#define MV_NO_MORE (12) /* No more items found */ +#define MV_NO_SUCH (13) /* No such item */ +#define MV_TIMEOUT (14) /* Time Out */ +#define MV_NO_CHANGE (15) /* Parameter(s) is already in this value */ +#define MV_NOT_SUPPORTED (16) /* This request is not support */ +#define MV_NOT_IMPLEMENTED (17) /* Request supported but not implemented */ +#define MV_NOT_INITIALIZED (18) /* The item is not initialized */ +#define MV_NO_RESOURCE (19) /* Resource not available (memory ...) */ +#define MV_FULL (20) /* Item is full (Queue or table etc...) */ +#define MV_EMPTY (21) /* Item is empty (Queue or table etc...) */ +#define MV_INIT_ERROR (22) /* Error occured while INIT process */ +#define MV_HW_ERROR (23) /* Hardware error */ +#define MV_TX_ERROR (24) /* Transmit operation not succeeded */ +#define MV_RX_ERROR (25) /* Recieve operation not succeeded */ +#define MV_NOT_READY (26) /* The other side is not ready yet */ +#define MV_ALREADY_EXIST (27) /* Tried to create existing item */ +#define MV_OUT_OF_CPU_MEM (28) /* Cpu memory allocation failed. */ +#define MV_NOT_STARTED (29) /* Not started yet */ +#define MV_BUSY (30) /* Item is busy. */ +#define MV_TERMINATE (31) /* Item terminates it's work. */ +#define MV_NOT_ALIGNED (32) /* Wrong alignment */ +#define MV_NOT_ALLOWED (33) /* Operation NOT allowed */ +#define MV_WRITE_PROTECT (34) /* Write protected */ +#define MV_DROPPED (35) /* Packet dropped */ +#define MV_STOLEN (36) /* Packet stolen */ +#define MV_CONTINUE (37) /* Continue */ +#define MV_RETRY (38) /* Operation failed need retry */ + +#define MV_INVALID (int)(-1) + +#define MV_FALSE 0 +#define MV_TRUE (!(MV_FALSE)) + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#endif /* __INCmvTypesh */ Index: drivers/net/ethernet/mvebu_net/prestera/platform/presteraPpDriverPci.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/presteraPpDriverPci.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/presteraPpDriverPci.c (working copy) @@ -0,0 +1,318 @@ +/******************************************************************************* +* presteraPpDriverPci.c +* +* DESCRIPTION: +* PCI/PEX driver, 4 regions +* +* DEPENDENCIES: +* +* COMMENTS: +* Please note: this file is shared for: +* axp_lsp_3.4.69 +* msys_lsp_3_4 +* msys_lsp_2_6_32 +* +*******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#endif + +#include "mv_prestera.h" +#include "mv_prestera_pp_driver_glob.h" + +#define ADDR_COMP_REG 0 +#define REG_ACCESS_ADDRESS(_regAddr, _compIdx) \ + (((_compIdx) << 24) | ((_regAddr) & 0x00ffffff)) + +struct ppDriverPciData_STC { + struct pp_dev *dev; + uintptr_t ppRegsBase; + uintptr_t pciRegsBase; + uintptr_t dfxRegsBase; + uint8_t addrCompletShadow[4]; + spinlock_t hwComplSem; + uint8_t compIdx; +} ppDriverPciData_STC; + + + +static void hwCompletion( + struct ppDriverPciData_STC *drv, + uint32_t regAddr, + uint8_t *compIdxPtr, + uintptr_t *addressPtr +) +{ + uint8_t addrRegion; /* 8 bit MST value of PP internal address*/ + uintptr_t address; /*physical access address for PCI access */ + uintptr_t compIdx; /* address completion register field index 0-3*/ + uint8_t i; /* count iterator for the completion index compare loop*/ + uint32_t data; /* data to be write to memory */ + + /* check if addrRegion is 0 */ + if ((regAddr & 0xFF000000) == 0) { + compIdx = 0; + } else { + spin_lock(&(drv->hwComplSem)); + + addrRegion = (uint8_t)(regAddr >> 24); + /* compare addr region to existing Address regions*/ + for (i = 3; (i > 0) && (addrRegion != drv->addrCompletShadow[i]); i--) + ;/* */ + if (i == 0) { /* Set addrRegion in AddrCompletion register */ + + /*round robin on Region index : 1,2,3*/ + drv->compIdx++; + if (drv->compIdx > 3) + drv->compIdx = 1; + compIdx = drv->compIdx; + + /*update Address Completion shadow*/ + drv->addrCompletShadow[compIdx] = addrRegion; + + /* update Hw Address Completion - using completion region 0 */ + address = drv->ppRegsBase + ADDR_COMP_REG; + data = (drv->addrCompletShadow[1]<<8) | + (drv->addrCompletShadow[2]<<16) | + (drv->addrCompletShadow[3]<<24); + + /*write the address completion 3 times. + because the PP have a 2 entry write buffer + so, the 3 writes will make sure we do get + to the hardware register itself */ + write_u32(data, address); + write_u32(data, address); + write_u32(data, address); + } else { + compIdx = i; + } + } + + address = drv->ppRegsBase + (uintptr_t)REG_ACCESS_ADDRESS(regAddr, compIdx); + *compIdxPtr = compIdx; + *addressPtr = address; +} + +static int hwReadWrite( + struct ppDriverPciData_STC *drv, + int isReadOp, + uint32_t regAddr, + uint32_t length, + uint32_t *dataPtr +) +{ + uintptr_t address; /*physical address for PCI access */ + uint8_t compIdx; /* address completion register field index 0-3*/ + uint32_t j = 0; /* count iterator for the write loop*/ + uint32_t nextRegionAddr; /* address of the next region after the one + currently used */ + uint32_t loopLength = 0; /* when length exceeds region addr, Set to end of + region range */ + uint32_t data; + + hwCompletion(drv, regAddr, &compIdx, &address); + + /* check whether completion region boundaries exceeded*/ + nextRegionAddr = (uint32_t)(drv->addrCompletShadow[compIdx] + 1)<<24; + loopLength = length; + if ((uintptr_t)(regAddr + length * 4) > nextRegionAddr) + loopLength = (nextRegionAddr - regAddr) / 4; + + for (j = 0; j < loopLength; j++) { + if (isReadOp) { + data = read_u32(address); + if (put_user(data, dataPtr+j)) { + if (compIdx != 0) + spin_unlock(&(drv->hwComplSem)); + return -EFAULT; + } + } else { + if (get_user(data, dataPtr+j)) { + if (compIdx != 0) + spin_unlock(&(drv->hwComplSem)); + return -EFAULT; + } + + write_u32(data, address); + } + + address += 4; + } + if (compIdx != 0) + spin_unlock(&(drv->hwComplSem)); + + if (loopLength < length) { + /* Recursive call for rest of data in next region. */ + return hwReadWrite(drv, isReadOp, nextRegionAddr, length-loopLength, + dataPtr+loopLength); + } + return 0; +} + +static int presteraPpDriverPciPexRead( + uintptr_t base, + uint32_t size, + uint32_t regAddr, + uint32_t *dataPtr +) +{ + uint32_t data; + if (base == 0 || regAddr >= size) + return -EFAULT; + + base += regAddr; + + data = read_u32(base); + + if (put_user(data, dataPtr)) + return -EFAULT; + + return 0; +} + +static int presteraPpDriverPciPexWrite( + uintptr_t base, + uint32_t size, + uint32_t regAddr, + uint32_t *dataPtr +) +{ + uint32_t data; + if (base == 0 || regAddr >= size) + return -EFAULT; + + if (get_user(data, dataPtr)) + return -EFAULT; + + base += regAddr; + write_u32(data, base); + + return 0; +} + +static int presteraPpDriverPciPexReset(struct ppDriverPciData_STC *drv) +{ + uintptr_t address; + int i; + + spin_lock(&(drv->hwComplSem)); + + /* Update Address Completion shadow */ + for (i = 0; i < 4; i++) + drv->addrCompletShadow[i] = 0; + + drv->compIdx = 1; + + /* Reset Hw Address Completion */ + address = drv->ppRegsBase + ADDR_COMP_REG; + write_u32(0, address); + write_u32(0, address); + write_u32(0, address); + + spin_unlock(&(drv->hwComplSem)); + + return 0; +} + +static int presteraPpDriverPciPexDestroy(struct ppDriverPciData_STC *drv) +{ + struct pp_dev *dev = drv->dev; + + if (dev->ppregs.base == 0) + iounmap((void *)drv->ppRegsBase); + if (dev->config.base == 0) + iounmap((void *)drv->pciRegsBase); + if (drv->dfxRegsBase && dev->dfx.base == 0) + iounmap((void *)drv->dfxRegsBase); + + dev->ppdriver = (PP_DRIVER_FUNC)NULL; + dev->ppdriverType = 0; + dev->ppdriverData = NULL; + + kfree(drv); + + return 0; +} + +static int presteraPpDriverPciPexIo(struct ppDriverPciData_STC *drv, struct mvPpDrvDriverIo_STC *io) +{ + if (io == NULL) { + /* destroy */ + return presteraPpDriverPciPexDestroy(drv); + } + switch (io->op) { + case mvPpDrvDriverIoOps_PpRegRead_E: + return hwReadWrite(drv, 1, io->regAddr, 1, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_PpRegWrite_E: + return hwReadWrite(drv, 0, io->regAddr, 1, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_RamRead_E: + return hwReadWrite(drv, 1, io->regAddr, io->length, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_RamWrite_E: + return hwReadWrite(drv, 0, io->regAddr, io->length, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_Reset_E: + return presteraPpDriverPciPexReset(drv); + case mvPpDrvDriverIoOps_Destroy_E: + return presteraPpDriverPciPexDestroy(drv); + case mvPpDrvDriverIoOps_PciRegRead_E: + return presteraPpDriverPciPexRead( + drv->pciRegsBase, drv->dev->config.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_PciRegWrite_E: + return presteraPpDriverPciPexWrite( + drv->pciRegsBase, drv->dev->config.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_DfxRegRead_E: + return presteraPpDriverPciPexRead( + drv->dfxRegsBase, drv->dev->dfx.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_DfxRegWrite_E: + return presteraPpDriverPciPexWrite( + drv->dfxRegsBase, drv->dev->dfx.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + } + return -EFAULT; +} + +int presteraPpDriverPciPexCreate(struct pp_dev *dev) +{ + struct ppDriverPciData_STC *drv; + + drv = kmalloc(sizeof(struct ppDriverPciData_STC), GFP_KERNEL); + memset(drv, 0, sizeof(*drv)); + + drv->dev = dev; + + if (dev->ppregs.base == 0) + drv->ppRegsBase = (uintptr_t)ioremap_nocache(dev->ppregs.phys, 64*1024*1024); + else + drv->ppRegsBase = dev->ppregs.base; + if (dev->config.base == 0) + drv->pciRegsBase = (uintptr_t)ioremap_nocache(dev->config.phys, dev->config.size); + else + drv->pciRegsBase = dev->config.base; + if (dev->dfx.phys) { + if (dev->dfx.base == 0) + drv->dfxRegsBase = (uintptr_t)ioremap_nocache(dev->dfx.phys, dev->dfx.size); + else + drv->dfxRegsBase = dev->dfx.base; + } + + spin_lock_init(&(drv->hwComplSem)); + drv->compIdx = 1; + + dev->ppdriver = (PP_DRIVER_FUNC)presteraPpDriverPciPexIo; + dev->ppdriverType = (int)mvPpDrvDriverType_Pci_E; + dev->ppdriverData = drv; + + return 0; +} Index: drivers/net/ethernet/mvebu_net/prestera/platform/presteraPpDriverPciHalf.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/presteraPpDriverPciHalf.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/presteraPpDriverPciHalf.c (working copy) @@ -0,0 +1,299 @@ +/******************************************************************************* +* presteraPpDriverPciHalf.c +* +* DESCRIPTION: +* PCI/PEX driver, 2 of 4 regions +* +* DEPENDENCIES: +* +* COMMENTS: +* Please note: this file is shared for: +* axp_lsp_3.4.69 +* msys_lsp_3_4 +* msys_lsp_2_6_32 +* +*******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#endif + +#include "mv_prestera.h" +#include "mv_prestera_pp_driver_glob.h" + +#define ADDR_COMP_REG 0 +#define REG_ACCESS_ADDRESS(_regAddr, _compIdx) \ + (((_compIdx) << 24) | ((_regAddr) & 0x00ffffff)) + +struct ppDriverPciData_STC { + struct pp_dev *dev; + uintptr_t ppRegsBase; + uintptr_t pciRegsBase; + uintptr_t dfxRegsBase; + uint8_t addrCompletShadow; + spinlock_t hwComplSem; +}; + + + +static void hwCompletion( + struct ppDriverPciData_STC *drv, + uint32_t regAddr, + uint8_t *compIdxPtr, + uintptr_t *addressPtr +) +{ + uint8_t addrRegion; /* 8 bit MST value of PP internal address*/ + uintptr_t address; /*physical access address for PCI access */ + uintptr_t compIdx; /* address completion register field index 0-3*/ + uint32_t data; /* data to be write to memory */ + + /* check if addrRegion is 0 */ + if ((regAddr & 0xFF000000) == 0) + compIdx = 0; + else { + spin_lock(&(drv->hwComplSem)); + + compIdx = 1; + addrRegion = (uint8_t)(regAddr >> 24); + if (drv->addrCompletShadow != addrRegion) { + /* Set addrRegion in AddrCompletion register */ + + /*update Address Completion shadow*/ + drv->addrCompletShadow = addrRegion; + + /* update Hw Address Completion - using completion region 0 */ + address = drv->ppRegsBase + ADDR_COMP_REG; + data = (drv->addrCompletShadow<<8); + + /*write the address completion 3 times. + because the PP have a 2 entry write buffer + so, the 3 writes will make sure we do get + to the hardware register itself */ + write_u32(data, address); + write_u32(data, address); + write_u32(data, address); + } + } + + address = drv->ppRegsBase + (uintptr_t)REG_ACCESS_ADDRESS(regAddr, compIdx); + *compIdxPtr = compIdx; + *addressPtr = address; +} + +static int hwReadWrite( + struct ppDriverPciData_STC *drv, + int isReadOp, + uint32_t regAddr, + uint32_t length, + uint32_t *dataPtr +) +{ + uintptr_t address; /*physical address for PCI access */ + uint8_t compIdx; /* address completion register field index 0-3*/ + uint32_t j = 0; /* count iterator for the write loop*/ + uint32_t nextRegionAddr; /* address of the next region after the one + currently used */ + uint32_t loopLength = 0; /* when length exceeds region addr, Set to end of + region range */ + uint32_t data; + + hwCompletion(drv, regAddr, &compIdx, &address); + + /* check whether completion region boundaries exceeded*/ + nextRegionAddr = (uint32_t)(drv->addrCompletShadow + 1)<<24; + loopLength = length; + if ((uintptr_t)(regAddr + length * 4) > nextRegionAddr) + loopLength = (nextRegionAddr - regAddr) / 4; + + for (j = 0; j < loopLength; j++) { + if (isReadOp) { + data = read_u32(address); + if (put_user(data, dataPtr+j)) { + if (compIdx != 0) + spin_unlock(&(drv->hwComplSem)); + return -EFAULT; + } + } else { + if (get_user(data, dataPtr+j)) { + if (compIdx != 0) + spin_unlock(&(drv->hwComplSem)); + return -EFAULT; + } + + write_u32(data, address); + } + + address += 4; + } + if (compIdx != 0) + spin_unlock(&(drv->hwComplSem)); + + if (loopLength < length) { + /* Recursive call for rest of data in next region. */ + return hwReadWrite(drv, isReadOp, nextRegionAddr, length-loopLength, + dataPtr+loopLength); + } + return 0; +} + +static int presteraPpDriverPciPexRead( + uintptr_t base, + uint32_t size, + uint32_t regAddr, + uint32_t *dataPtr +) +{ + uint32_t data; + if (base == 0 || regAddr >= size) + return -EFAULT; + + base += regAddr; + + data = read_u32(base); + + if (put_user(data, dataPtr)) + return -EFAULT; + + return 0; +} + +static int presteraPpDriverPciPexWrite( + uintptr_t base, + uint32_t size, + uint32_t regAddr, + uint32_t *dataPtr +) +{ + uint32_t data; + if (base == 0 || regAddr >= size) + return -EFAULT; + + if (get_user(data, dataPtr)) + return -EFAULT; + + base += regAddr; + write_u32(data, base); + + return 0; +} + +static int presteraPpDriverPciPexReset(struct ppDriverPciData_STC *drv) +{ + uintptr_t address; + + spin_lock(&(drv->hwComplSem)); + + /* Update Address Completion shadow */ + drv->addrCompletShadow = 0; + + /* Reset Hw Address Completion */ + address = drv->ppRegsBase + ADDR_COMP_REG; + write_u32(0, address); + write_u32(0, address); + write_u32(0, address); + + spin_unlock(&(drv->hwComplSem)); + + return 0; +} + +static int presteraPpDriverPciPexDestroy(struct ppDriverPciData_STC *drv) +{ + struct pp_dev *dev = drv->dev; + + if (dev->ppregs.base == 0) + iounmap((void *)drv->ppRegsBase); + if (dev->config.base == 0) + iounmap((void *)drv->pciRegsBase); + if (drv->dfxRegsBase && dev->dfx.base == 0) + iounmap((void *)drv->dfxRegsBase); + + dev->ppdriver = (PP_DRIVER_FUNC)NULL; + dev->ppdriverType = 0; + dev->ppdriverData = NULL; + + kfree(drv); + + return 0; +} + +static int presteraPpDriverPciPexIo(struct ppDriverPciData_STC *drv, struct mvPpDrvDriverIo_STC *io) +{ + if (io == NULL) + return presteraPpDriverPciPexDestroy(drv); + + switch (io->op) { + case mvPpDrvDriverIoOps_PpRegRead_E: + return hwReadWrite(drv, 1, io->regAddr, 1, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_PpRegWrite_E: + return hwReadWrite(drv, 0, io->regAddr, 1, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_RamRead_E: + return hwReadWrite(drv, 1, io->regAddr, io->length, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_RamWrite_E: + return hwReadWrite(drv, 0, io->regAddr, io->length, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_Reset_E: + return presteraPpDriverPciPexReset(drv); + case mvPpDrvDriverIoOps_Destroy_E: + return presteraPpDriverPciPexDestroy(drv); + case mvPpDrvDriverIoOps_PciRegRead_E: + return presteraPpDriverPciPexRead( + drv->pciRegsBase, drv->dev->config.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_PciRegWrite_E: + return presteraPpDriverPciPexWrite( + drv->pciRegsBase, drv->dev->config.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_DfxRegRead_E: + return presteraPpDriverPciPexRead( + drv->dfxRegsBase, drv->dev->dfx.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_DfxRegWrite_E: + return presteraPpDriverPciPexWrite( + drv->dfxRegsBase, drv->dev->dfx.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + } + return -EFAULT; +} + +int presteraPpDriverPciPexHalfCreate(struct pp_dev *dev) +{ + struct ppDriverPciData_STC *drv; + + drv = kmalloc(sizeof(struct ppDriverPciData_STC), GFP_KERNEL); + memset(drv, 0, sizeof(*drv)); + + drv->dev = dev; + + if (dev->ppregs.base == 0) + drv->ppRegsBase = (uintptr_t)ioremap_nocache(dev->ppregs.phys, 64*1024*1024); + else + drv->ppRegsBase = dev->ppregs.base; + if (dev->config.base == 0) + drv->pciRegsBase = (uintptr_t)ioremap_nocache(dev->config.phys, dev->config.size); + else + drv->pciRegsBase = dev->config.base; + if (dev->dfx.phys) { + if (dev->dfx.base == 0) + drv->dfxRegsBase = (uintptr_t)ioremap_nocache(dev->dfx.phys, dev->dfx.size); + else + drv->dfxRegsBase = dev->dfx.base; + } + + spin_lock_init(&(drv->hwComplSem)); + + dev->ppdriver = (PP_DRIVER_FUNC)presteraPpDriverPciPexIo; + dev->ppdriverType = (int)mvPpDrvDriverType_PciHalf_E; + dev->ppdriverData = drv; + + return 0; +} Index: drivers/net/ethernet/mvebu_net/prestera/platform/presteraPpDriverPexMbus.c =================================================================== --- drivers/net/ethernet/mvebu_net/prestera/platform/presteraPpDriverPexMbus.c (nonexistent) +++ drivers/net/ethernet/mvebu_net/prestera/platform/presteraPpDriverPexMbus.c (working copy) @@ -0,0 +1,319 @@ +/******************************************************************************* +* presteraPpDriverPexMbus.c +* +* DESCRIPTION: +* PexMbus driver, 8 regions +* +* DEPENDENCIES: +* +* COMMENTS: +* Please note: this file is shared for: +* axp_lsp_3.4.69 +* msys_lsp_3_4 +* msys_lsp_2_6_32 +* +*******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#endif + +#include "mv_prestera.h" +#include "mv_prestera_pp_driver_glob.h" + +/* offset of the address completion for PEX 8 completion regions */ +#define PEX_MBUS_ADDR_COMP_REG_MAC(_index) (0x120 + (4 * _index)) +/* bits of address passes as is throw PCI window */ +#define NOT_ADDRESS_COMPLETION_BITS_NUM_CNS 19 +/* bits of address extracted from address completion registers */ +#define ADDRESS_COMPLETION_BITS_MASK_CNS (0xFFFFFFFF << NOT_ADDRESS_COMPLETION_BITS_NUM_CNS) + +#define REG_ACCESS_ADDRESS(_regAddr, _compIdx) \ + (((_compIdx) << NOT_ADDRESS_COMPLETION_BITS_NUM_CNS) | \ + ((_regAddr) & (~ADDRESS_COMPLETION_BITS_MASK_CNS))) + +struct ppDriverPexMbusData_STC { + struct pp_dev *dev; + uintptr_t ppRegsBase; + uintptr_t pciRegsBase; + uintptr_t dfxRegsBase; + uint32_t addrCompletShadow[8]; + spinlock_t hwComplSem; + uint8_t compIdx; +}; + + + +static void hwCompletion( + struct ppDriverPexMbusData_STC *drv, + uint32_t regAddr, + uint8_t *compIdxPtr, + uintptr_t *addressPtr +) +{ + uint32_t addrRegion; /* 13 bit MSB value of PP internal address */ + uintptr_t address; /*physical access address for PCI access */ + uint32_t compIdx; /* address completion register field index 0-7*/ + uint8_t i; /* count iterator for the completion index compare loop*/ + + /* check if addrRegion is 0 */ + if ((regAddr & ADDRESS_COMPLETION_BITS_MASK_CNS) == 0) { + compIdx = 0; + } else { + spin_lock(&(drv->hwComplSem)); + + addrRegion = (regAddr >> NOT_ADDRESS_COMPLETION_BITS_NUM_CNS); + /* compare addr region to existing Address regions*/ + for (i = 1; i < 8; i++) { + if (addrRegion == drv->addrCompletShadow[i]) + break; + } + if (i == 8) { + /* Set addrRegion in AddrCompletion register */ + + /*round robin on Region index : 1,2,3*/ + drv->compIdx++; + if (drv->compIdx > 7) + drv->compIdx = 1; + compIdx = drv->compIdx; + + /*update Address Completion shadow*/ + drv->addrCompletShadow[compIdx] = addrRegion; + + /* update Hw Address Completion - using completion region 0 */ + address = drv->ppRegsBase + PEX_MBUS_ADDR_COMP_REG_MAC(compIdx); + write_u32(addrRegion, address); + } else + compIdx = i; + } + + address = drv->ppRegsBase + (uintptr_t)REG_ACCESS_ADDRESS(regAddr, compIdx); + *compIdxPtr = compIdx; + *addressPtr = address; +} + +static int hwReadWrite( + struct ppDriverPexMbusData_STC *drv, + int isReadOp, + uint32_t regAddr, + uint32_t length, + uint32_t *dataPtr +) +{ + uintptr_t address; /*physical address for PCI access */ + uint8_t compIdx; /* address completion register field index 0-3*/ + uint32_t j = 0; /* count iterator for the write loop*/ + uint32_t nextRegionAddr; /* address of the next region after the one + currently used */ + uint32_t loopLength = 0; /* when length exceeds region addr, Set to end of + region range */ + uint32_t data; + + hwCompletion(drv, regAddr, &compIdx, &address); + + /* check whether completion region boundaries exceeded*/ + nextRegionAddr = (uint32_t)(drv->addrCompletShadow[compIdx] + 1)< nextRegionAddr) + loopLength = (nextRegionAddr - regAddr) / 4; + for (j = 0; j < loopLength; j++) { + if (isReadOp) { + data = read_u32(address); + if (put_user(data, dataPtr+j)) { + if (compIdx != 0) + spin_unlock(&(drv->hwComplSem)); + return -EFAULT; + } + } else { + if (get_user(data, dataPtr+j)) { + if (compIdx != 0) + spin_unlock(&(drv->hwComplSem)); + return -EFAULT; + } + + write_u32(data, address); + } + + address += 4; + } + if (compIdx != 0) + spin_unlock(&(drv->hwComplSem)); + + if (loopLength < length) { + /* Recursive call for rest of data in next region. */ + return hwReadWrite(drv, isReadOp, nextRegionAddr, length-loopLength, + dataPtr+loopLength); + } + return 0; +} + +static int presteraPpDriverPciPexRead( + uintptr_t base, + uint32_t size, + uint32_t regAddr, + uint32_t *dataPtr +) +{ + uint32_t data; + if (base == 0 || regAddr >= size) + return -EFAULT; + + base += regAddr; + + data = read_u32(base); + + if (put_user(data, dataPtr)) + return -EFAULT; + + return 0; +} + +static int presteraPpDriverPciPexWrite( + uintptr_t base, + uint32_t size, + uint32_t regAddr, + uint32_t *dataPtr +) +{ + uint32_t data; + if (base == 0 || regAddr >= size) + return -EFAULT; + + if (get_user(data, dataPtr)) + return -EFAULT; + + base += regAddr; + write_u32(data, base); + + return 0; +} + +static int presteraPpDriverPciPexReset(struct ppDriverPexMbusData_STC *drv) +{ + uintptr_t address; + uint32_t data; + int i; + + spin_lock(&(drv->hwComplSem)); + + /* set 8-region mode: regAddr = 0x140, set bit16 to 0 */ + address = drv->ppRegsBase + 0x140; + data = read_u32(address); + data &= (~(1 << 16)); + write_u32(data, address); + + /* Update Address Completion shadow */ + for (i = 0; i < 8; i++) { + drv->addrCompletShadow[i] = 0; + /* Reset Hw Address Completion */ + address = drv->ppRegsBase+PEX_MBUS_ADDR_COMP_REG_MAC(i); + write_u32(0, address); + } + drv->compIdx = 1; + + spin_unlock(&(drv->hwComplSem)); + + return 0; +} + +static int presteraPpDriverPciPexDestroy(struct ppDriverPexMbusData_STC *drv) +{ + struct pp_dev *dev = drv->dev; + + if (dev->ppregs.base == 0) + iounmap((void *)drv->ppRegsBase); + if (dev->config.base == 0) + iounmap((void *)drv->pciRegsBase); + if (drv->dfxRegsBase && dev->dfx.base == 0) + iounmap((void *)drv->dfxRegsBase); + + dev->ppdriver = (PP_DRIVER_FUNC)NULL; + dev->ppdriverType = 0; + dev->ppdriverData = NULL; + + kfree(drv); + + return 0; +} + +static int presteraPpDriverPexMbusIo(struct ppDriverPexMbusData_STC *drv, struct mvPpDrvDriverIo_STC *io) +{ + if (io == NULL) { + /* destroy */ + return presteraPpDriverPciPexDestroy(drv); + } + switch (io->op) { + case mvPpDrvDriverIoOps_PpRegRead_E: + return hwReadWrite(drv, 1, io->regAddr, 1, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_PpRegWrite_E: + return hwReadWrite(drv, 0, io->regAddr, 1, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_RamRead_E: + return hwReadWrite(drv, 1, io->regAddr, io->length, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_RamWrite_E: + return hwReadWrite(drv, 0, io->regAddr, io->length, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_Reset_E: + return presteraPpDriverPciPexReset(drv); + case mvPpDrvDriverIoOps_Destroy_E: + return presteraPpDriverPciPexDestroy(drv); + case mvPpDrvDriverIoOps_PciRegRead_E: + return presteraPpDriverPciPexRead( + drv->pciRegsBase, drv->dev->config.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_PciRegWrite_E: + return presteraPpDriverPciPexWrite( + drv->pciRegsBase, drv->dev->config.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_DfxRegRead_E: + return presteraPpDriverPciPexRead( + drv->dfxRegsBase, drv->dev->dfx.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + case mvPpDrvDriverIoOps_DfxRegWrite_E: + return presteraPpDriverPciPexWrite( + drv->dfxRegsBase, drv->dev->dfx.size, + io->regAddr, (uint32_t *)(io->dataPtr)); + } + return -EFAULT; +} + +int presteraPpDriverPexMbusCreate(struct pp_dev *dev) +{ + struct ppDriverPexMbusData_STC *drv; + + drv = kmalloc(sizeof(struct ppDriverPexMbusData_STC), GFP_KERNEL); + memset(drv, 0, sizeof(*drv)); + + drv->dev = dev; + + if (dev->ppregs.base == 0) + drv->ppRegsBase = (uintptr_t)ioremap_nocache(dev->ppregs.phys, 64*1024*1024); + else + drv->ppRegsBase = dev->ppregs.base; + if (dev->config.base == 0) + drv->pciRegsBase = (uintptr_t)ioremap_nocache(dev->config.phys, dev->config.size); + else + drv->pciRegsBase = dev->config.base; + if (dev->dfx.phys) { + if (dev->dfx.base == 0) + drv->dfxRegsBase = (uintptr_t)ioremap_nocache(dev->dfx.phys, dev->dfx.size); + else + drv->dfxRegsBase = dev->dfx.base; + } + + spin_lock_init(&(drv->hwComplSem)); + drv->compIdx = 1; + + dev->ppdriver = (PP_DRIVER_FUNC)presteraPpDriverPexMbusIo; + dev->ppdriverType = (int)mvPpDrvDriverType_PexMbus_E; + dev->ppdriverData = drv; + + return 0; +} Index: drivers/net/ethernet/mvebu_net/switch/mv_phy.c =================================================================== --- drivers/net/ethernet/mvebu_net/switch/mv_phy.c (revision 1) +++ drivers/net/ethernet/mvebu_net/switch/mv_phy.c (working copy) @@ -32,16 +32,21 @@ #include #include "mvOs.h" +#ifndef CONFIG_OF #include "mvSysHwConfig.h" #include "eth-phy/mvEthPhy.h" #ifdef MV_INCLUDE_ETH_COMPLEX #include "ctrlEnv/mvCtrlEthCompLib.h" #endif /* MV_INCLUDE_ETH_COMPLEX */ - +#else +#include "phy/mvEthPhy.h" +#endif #include "msApi.h" #include "mv_switch.h" #include "mv_phy.h" +#ifndef CONFIG_OF #include "mv_mux/mv_mux_netdev.h" +#endif /******************************************************************************* * mv_phy_port_power_state_set @@ -370,9 +375,21 @@ GT_STATUS rc = GT_OK; GT_BOOL link_forced; + /*clear the PHY detect bit before enable loopback to preven the port from getting locked up due the PPU bug*/ + if (enable == GT_TRUE) { + rc = gprtSetPHYDetect(mv_switch_qd_dev_get(), lport, GT_FALSE); + SW_IF_ERROR_STR(rc, "failed to call gprtSetPHYDetect()\n"); + } + rc = gprtSetPortLoopback(mv_switch_qd_dev_get(), lport, enable); SW_IF_ERROR_STR(rc, "failed to call gprtSetPortLoopback()\n"); + /*restore the PHY detect bit after disable loopback*/ + if (enable == GT_FALSE) { + rc = gprtSetPHYDetect(mv_switch_qd_dev_get(), lport, GT_TRUE); + SW_IF_ERROR_STR(rc, "failed to call gprtSetPHYDetect()\n"); + } + /* Get port force link statue */ rc = gpcsGetForcedLink(mv_switch_qd_dev_get(), lport, &link_forced); SW_IF_ERROR_STR(rc, "failed to call gpcsGetForcedLink()\n"); Index: drivers/net/ethernet/mvebu_net/switch/mv_switch.c =================================================================== --- drivers/net/ethernet/mvebu_net/switch/mv_switch.c (revision 1) +++ drivers/net/ethernet/mvebu_net/switch/mv_switch.c (working copy) @@ -30,10 +30,16 @@ #include #include #include +#include +#include +#include #include "mvOs.h" -#include "mvSysHwConfig.h" +#ifdef CONFIG_OF +#include "phy/mvEthPhy.h" +#else #include "eth-phy/mvEthPhy.h" +#endif #ifdef MV_INCLUDE_ETH_COMPLEX #include "ctrlEnv/mvCtrlEthCompLib.h" #endif /* MV_INCLUDE_ETH_COMPLEX */ @@ -75,12 +81,13 @@ #define SWITCH_DBG(FLG, X) #endif /* SWITCH_DEBUG */ -static GT_QD_DEV qddev, *qd_dev = NULL; +GT_QD_DEV qddev, *qd_dev = NULL; static GT_SYS_CONFIG qd_cfg; static int qd_cpu_port = -1; static int enabled_ports_mask; static int switch_ports_mask; +static int switch_link_ports_mask; static MV_TAG_TYPE tag_mode; static MV_SWITCH_PRESET_TYPE preset; static int default_vid; @@ -91,14 +98,14 @@ static struct tasklet_struct link_tasklet; static int switch_irq = -1; -int switch_link_poll = 0; +int switch_link_poll; /* polling mode */ static struct timer_list switch_link_timer; +static struct task_struct *switch_link_detect_thrd; static spinlock_t switch_lock; static unsigned int mv_switch_link_detection_init(struct mv_switch_pdata *plat_data); - #ifdef CONFIG_AVANTA_LP static GT_BOOL mv_switch_mii_read(GT_QD_DEV *dev, unsigned int phy, unsigned int reg, unsigned int *data) { @@ -621,6 +628,30 @@ } } +static int mv_switch_link_detect_thread(void *data) +{ + int timeout; + wait_queue_head_t timeout_wq; + + pr_info("switch link detect thread starts up\n"); + + init_waitqueue_head(&timeout_wq); + + set_cpus_allowed_ptr(current, &cpumask_of_cpu(1)); + + while (switch_link_poll) { + mv_switch_link_update_event(switch_link_ports_mask, 0); + sleep_on_timeout(&timeout_wq, HZ); + + if (kthread_should_stop()) { + pr_err("receive stop event!\n"); + break; + } + } + + return 0; +} + static irqreturn_t mv_switch_isr(int irq, void *dev_id) { GT_DEV_INT_STATUS devIntStatus; @@ -697,6 +728,44 @@ return 0; } +static GT_SEM mv_switch_sem_create(GT_SEM_BEGIN_STATE state) +{ + struct semaphore *sem; + + sem = mvOsMalloc(sizeof(struct semaphore)); + + if (GT_SEM_EMPTY == state) + sema_init(sem, 0); + else + sema_init(sem, 1); + + return (GT_SEM)sem; +} + +static GT_STATUS mv_switch_sem_delete(GT_SEM smid) +{ + mvOsFree((struct semaphore *)smid); + + return GT_OK; +} + +static GT_STATUS mv_switch_sem_wait(GT_SEM smid, GT_U32 timeOut) +{ + if (!timeOut) + down((struct semaphore *)(smid)); + else + return down_timeout((struct semaphore *)(smid), timeOut); + + return GT_OK; +} + +static GT_STATUS mv_switch_sem_give(GT_SEM smid) +{ + up((struct semaphore *)(smid)); + + return GT_OK; +} + int mv_switch_load(struct mv_switch_pdata *plat_data) { int p; @@ -727,6 +796,10 @@ } else if (plat_data->smi_scan_mode == 2) { qd_cfg.mode.scanMode = SMI_MULTI_ADDR_MODE; qd_cfg.mode.baseAddr = plat_data->phy_addr; + qd_cfg.BSPFunctions.semCreate = mv_switch_sem_create; + qd_cfg.BSPFunctions.semDelete = mv_switch_sem_delete; + qd_cfg.BSPFunctions.semTake = mv_switch_sem_wait; + qd_cfg.BSPFunctions.semGive = mv_switch_sem_give; } /* load switch sw package */ @@ -756,6 +829,16 @@ if (MV_BIT_CHECK(plat_data->forced_link_port_mask, p)) { /* Switch port connected to GMAC - force link UP - 1000 Full with FC */ printk(KERN_ERR " o Setting Switch Port #%d connected to GMAC port for 1000 Full with FC\n", p); +#ifdef CONFIG_OF + if (gpcsSetRGMIITimingDelay(qd_dev, + p, + plat_data->rgmii_rx_timing_delay, + plat_data->rgmii_tx_timing_delay) != GT_OK) { + pr_err("set rgmii timeing delay - Failed\n"); + return -1; + } +#endif + if (gpcsSetForceSpeed(qd_dev, p, PORT_FORCE_SPEED_1000_MBPS) != GT_OK) { printk(KERN_ERR "Force speed 1000mbps - Failed\n"); return -1; @@ -871,6 +954,12 @@ switch_irq = -1; switch_link_poll = 0; + if ((switch_irq == -1) && (qd_dev->accessMode == SMI_MULTI_ADDR_MODE)) { + if (switch_link_detect_thrd) { + kthread_stop(switch_link_detect_thrd); + switch_link_detect_thrd = NULL; + } + } else del_timer(&switch_link_timer); return 0; @@ -919,6 +1008,8 @@ return -1; } + switch_ports_mask = plat_data->connected_port_mask; + /* set priorities rules */ for (p = 0; p < qd_dev->numOfPorts; p++) { if (MV_BIT_CHECK(plat_data->connected_port_mask, p)) { @@ -943,7 +1034,7 @@ if (gfdbFlush(qd_dev, GT_FLUSH_ALL) != GT_OK) printk(KERN_ERR "gfdbFlush failed\n"); - mv_switch_link_detection_init(plat_data); + switch_link_ports_mask = mv_switch_link_detection_init(plat_data); /* Enable Jumbo support by default */ mv_switch_jumbo_mode_set(9180); @@ -1217,16 +1308,20 @@ if (!link_init_done) { /* we want to use a timer for polling link status if no interrupt is available for all or some of the PHYs */ if (switch_irq == -1) { + switch_link_poll = 1; + if (qd_dev->accessMode == SMI_MULTI_ADDR_MODE) { + /* Use thread for polling, thread can call semphore*/ + pr_err("switch access mode is smi_multi_addr, there are semphore preventation!\n"); + pr_err("So please note that switch APIs can not be called in interrupts!\n"); + pr_err("Switch link detection will be implemented by thread later but not timer!\n"); + } else { /* Use timer for polling */ - switch_link_poll = 1; init_timer(&switch_link_timer); switch_link_timer.function = mv_switch_link_timer_function; - - if (switch_irq == -1) - switch_link_timer.data = connected_phys_mask; - + switch_link_timer.data = connected_phys_mask & (~plat_data->forced_link_port_mask); switch_link_timer.expires = jiffies + (HZ); /* 1 second */ add_timer(&switch_link_timer); + } } else { /* create tasklet for interrupt handling */ tasklet_init(&link_tasklet, mv_switch_tasklet, 0); @@ -1282,12 +1377,15 @@ } #define QD_FMT "%10lu %10lu %10lu %10lu %10lu %10lu %10lu\n" -#define QD_CNT(c, f) (GT_U32)(c[0]->f), (GT_U32)(c[1]->f), (GT_U32)(c[2]->f), (GT_U32)(c[3]->f),\ - (GT_U32)(c[4]->f), (GT_U32)(c[5]->f), (GT_U32)(c[6]->f) +#define QD_CNT_CORRECT(c, f, p) (GT_U32)(c[p]->f - history_counters[p].f) +#define QD_STAT_FMT "%10u %10u %10u %10u %10u %10u %10u\n" +#define QD_STAT_CORRECT(c, f, p) (GT_U16)(c[p]->f - history_stats[p].f) #define QD_MAX 7 void mv_switch_stats_print(void) { + static GT_STATS_COUNTER_SET3 history_counters[QD_MAX] = {0}; + static GT_PORT_STAT2 history_stats[QD_MAX] = {0}; GT_STATS_COUNTER_SET3 * counters[QD_MAX]; GT_PORT_STAT2 * port_stats[QD_MAX]; @@ -1294,7 +1392,7 @@ int p; if (qd_dev == NULL) { - printk(KERN_ERR "Switch is not initialized\n"); + pr_err("Switch is not initialized\n"); return; } @@ -1305,63 +1403,206 @@ mvOsMemset(port_stats[p], 0, sizeof(GT_PORT_STAT2)); } - printk(KERN_ERR "Total free buffers: %u\n\n", mv_switch_get_free_buffers_num()); + pr_err("Total free buffers: %u\n\n", mv_switch_get_free_buffers_num()); for (p = 0; p < QD_MAX; p++) { if (gstatsGetPortAllCounters3(qd_dev, p, counters[p]) != GT_OK) - printk(KERN_ERR "gstatsGetPortAllCounters3 for port #%d - FAILED\n", p); + pr_err("gstatsGetPortAllCounters3 for port #%d - FAILED\n", p); if (gprtGetPortCtr2(qd_dev, p, port_stats[p]) != GT_OK) - printk(KERN_ERR "gprtGetPortCtr2 for port #%d - FAILED\n", p); + pr_err("gprtGetPortCtr2 for port #%d - FAILED\n", p); } - printk(KERN_ERR "PortNum " QD_FMT, (GT_U32) 0, (GT_U32) 1, (GT_U32) 2, (GT_U32) 3, (GT_U32) 4, (GT_U32) 5, + pr_err("PortNum " QD_FMT, (GT_U32) 0, (GT_U32) 1, (GT_U32) 2, (GT_U32) 3, (GT_U32) 4, (GT_U32) 5, (GT_U32) 6); - printk(KERN_ERR "-----------------------------------------------------------------------------------------------\n"); - printk(KERN_ERR "InGoodOctetsLo " QD_FMT, QD_CNT(counters, InGoodOctetsLo)); - printk(KERN_ERR "InGoodOctetsHi " QD_FMT, QD_CNT(counters, InGoodOctetsHi)); - printk(KERN_ERR "InBadOctets " QD_FMT, QD_CNT(counters, InBadOctets)); - printk(KERN_ERR "InUnicasts " QD_FMT, QD_CNT(counters, InUnicasts)); - printk(KERN_ERR "InBroadcasts " QD_FMT, QD_CNT(counters, InBroadcasts)); - printk(KERN_ERR "InMulticasts " QD_FMT, QD_CNT(counters, InMulticasts)); - printk(KERN_ERR "inDiscardLo " QD_FMT, QD_CNT(port_stats, inDiscardLo)); - printk(KERN_ERR "inDiscardHi " QD_FMT, QD_CNT(port_stats, inDiscardHi)); - printk(KERN_ERR "InFiltered " QD_FMT, QD_CNT(port_stats, inFiltered)); + pr_err("-----------------------------------------------------------------------------------------------\n"); + pr_err("InGoodOctetsLo " QD_FMT, + QD_CNT_CORRECT(counters, InGoodOctetsLo, 0), QD_CNT_CORRECT(counters, InGoodOctetsLo, 1), + QD_CNT_CORRECT(counters, InGoodOctetsLo, 2), QD_CNT_CORRECT(counters, InGoodOctetsLo, 3), + QD_CNT_CORRECT(counters, InGoodOctetsLo, 4), QD_CNT_CORRECT(counters, InGoodOctetsLo, 5), + QD_CNT_CORRECT(counters, InGoodOctetsLo, 6)); + pr_err("InGoodOctetsHi " QD_FMT, + QD_CNT_CORRECT(counters, InGoodOctetsHi, 0), QD_CNT_CORRECT(counters, InGoodOctetsHi, 1), + QD_CNT_CORRECT(counters, InGoodOctetsHi, 2), QD_CNT_CORRECT(counters, InGoodOctetsHi, 3), + QD_CNT_CORRECT(counters, InGoodOctetsHi, 4), QD_CNT_CORRECT(counters, InGoodOctetsHi, 5), + QD_CNT_CORRECT(counters, InGoodOctetsHi, 6)); + pr_err("InBadOctets " QD_FMT, + QD_CNT_CORRECT(counters, InBadOctets, 0), QD_CNT_CORRECT(counters, InBadOctets, 1), + QD_CNT_CORRECT(counters, InBadOctets, 2), QD_CNT_CORRECT(counters, InBadOctets, 3), + QD_CNT_CORRECT(counters, InBadOctets, 4), QD_CNT_CORRECT(counters, InBadOctets, 5), + QD_CNT_CORRECT(counters, InBadOctets, 6)); + pr_err("InUnicasts " QD_FMT, + QD_CNT_CORRECT(counters, InUnicasts, 0), QD_CNT_CORRECT(counters, InUnicasts, 1), + QD_CNT_CORRECT(counters, InUnicasts, 2), QD_CNT_CORRECT(counters, InUnicasts, 3), + QD_CNT_CORRECT(counters, InUnicasts, 4), QD_CNT_CORRECT(counters, InUnicasts, 5), + QD_CNT_CORRECT(counters, InUnicasts, 6)); + pr_err("InBroadcasts " QD_FMT, + QD_CNT_CORRECT(counters, InBroadcasts, 0), QD_CNT_CORRECT(counters, InBroadcasts, 1), + QD_CNT_CORRECT(counters, InBroadcasts, 2), QD_CNT_CORRECT(counters, InBroadcasts, 3), + QD_CNT_CORRECT(counters, InBroadcasts, 4), QD_CNT_CORRECT(counters, InBroadcasts, 5), + QD_CNT_CORRECT(counters, InBroadcasts, 6)); + pr_err("InMulticasts " QD_FMT, + QD_CNT_CORRECT(counters, InMulticasts, 0), QD_CNT_CORRECT(counters, InMulticasts, 1), + QD_CNT_CORRECT(counters, InMulticasts, 2), QD_CNT_CORRECT(counters, InMulticasts, 3), + QD_CNT_CORRECT(counters, InMulticasts, 4), QD_CNT_CORRECT(counters, InMulticasts, 5), + QD_CNT_CORRECT(counters, InMulticasts, 6)); + pr_err("inDiscardLo " QD_FMT, + QD_STAT_CORRECT(port_stats, inDiscardLo, 0), QD_STAT_CORRECT(port_stats, inDiscardLo, 1), + QD_STAT_CORRECT(port_stats, inDiscardLo, 2), QD_STAT_CORRECT(port_stats, inDiscardLo, 3), + QD_STAT_CORRECT(port_stats, inDiscardLo, 4), QD_STAT_CORRECT(port_stats, inDiscardLo, 5), + QD_STAT_CORRECT(port_stats, inDiscardLo, 6)); + pr_err("inDiscardHi " QD_FMT, + QD_STAT_CORRECT(port_stats, inDiscardHi, 0), QD_STAT_CORRECT(port_stats, inDiscardHi, 1), + QD_STAT_CORRECT(port_stats, inDiscardHi, 2), QD_STAT_CORRECT(port_stats, inDiscardHi, 3), + QD_STAT_CORRECT(port_stats, inDiscardHi, 4), QD_STAT_CORRECT(port_stats, inDiscardHi, 5), + QD_STAT_CORRECT(port_stats, inDiscardHi, 6)); + pr_err("inFiltered " QD_FMT, + QD_STAT_CORRECT(port_stats, inFiltered, 0), QD_STAT_CORRECT(port_stats, inFiltered, 1), + QD_STAT_CORRECT(port_stats, inFiltered, 2), QD_STAT_CORRECT(port_stats, inFiltered, 3), + QD_STAT_CORRECT(port_stats, inFiltered, 4), QD_STAT_CORRECT(port_stats, inFiltered, 5), + QD_STAT_CORRECT(port_stats, inFiltered, 6)); + pr_err("OutOctetsLo " QD_FMT, + QD_CNT_CORRECT(counters, OutOctetsLo, 0), QD_CNT_CORRECT(counters, OutOctetsLo, 1), + QD_CNT_CORRECT(counters, OutOctetsLo, 2), QD_CNT_CORRECT(counters, OutOctetsLo, 3), + QD_CNT_CORRECT(counters, OutOctetsLo, 4), QD_CNT_CORRECT(counters, OutOctetsLo, 5), + QD_CNT_CORRECT(counters, OutOctetsLo, 6)); + pr_err("OutOctetsHi " QD_FMT, + QD_CNT_CORRECT(counters, OutOctetsHi, 0), QD_CNT_CORRECT(counters, OutOctetsHi, 1), + QD_CNT_CORRECT(counters, OutOctetsHi, 2), QD_CNT_CORRECT(counters, OutOctetsHi, 3), + QD_CNT_CORRECT(counters, OutOctetsHi, 4), QD_CNT_CORRECT(counters, OutOctetsHi, 5), + QD_CNT_CORRECT(counters, OutOctetsHi, 6)); + pr_err("OutUnicasts " QD_FMT, + QD_CNT_CORRECT(counters, OutUnicasts, 0), QD_CNT_CORRECT(counters, OutUnicasts, 1), + QD_CNT_CORRECT(counters, OutUnicasts, 2), QD_CNT_CORRECT(counters, OutUnicasts, 3), + QD_CNT_CORRECT(counters, OutUnicasts, 4), QD_CNT_CORRECT(counters, OutUnicasts, 5), + QD_CNT_CORRECT(counters, OutUnicasts, 6)); + pr_err("OutMulticasts " QD_FMT, + QD_CNT_CORRECT(counters, OutMulticasts, 0), QD_CNT_CORRECT(counters, OutMulticasts, 1), + QD_CNT_CORRECT(counters, OutMulticasts, 2), QD_CNT_CORRECT(counters, OutMulticasts, 3), + QD_CNT_CORRECT(counters, OutMulticasts, 4), QD_CNT_CORRECT(counters, OutMulticasts, 5), + QD_CNT_CORRECT(counters, OutMulticasts, 6)); + pr_err("OutBroadcasts " QD_FMT, + QD_CNT_CORRECT(counters, OutBroadcasts, 0), QD_CNT_CORRECT(counters, OutBroadcasts, 1), + QD_CNT_CORRECT(counters, OutBroadcasts, 2), QD_CNT_CORRECT(counters, OutBroadcasts, 3), + QD_CNT_CORRECT(counters, OutBroadcasts, 4), QD_CNT_CORRECT(counters, OutBroadcasts, 5), + QD_CNT_CORRECT(counters, OutBroadcasts, 6)); + pr_err("outFiltered " QD_FMT, + QD_STAT_CORRECT(port_stats, outFiltered, 0), QD_STAT_CORRECT(port_stats, outFiltered, 1), + QD_STAT_CORRECT(port_stats, outFiltered, 2), QD_STAT_CORRECT(port_stats, outFiltered, 3), + QD_STAT_CORRECT(port_stats, outFiltered, 4), QD_STAT_CORRECT(port_stats, outFiltered, 5), + QD_STAT_CORRECT(port_stats, outFiltered, 6)); - printk(KERN_ERR "OutOctetsLo " QD_FMT, QD_CNT(counters, OutOctetsLo)); - printk(KERN_ERR "OutOctetsHi " QD_FMT, QD_CNT(counters, OutOctetsHi)); - printk(KERN_ERR "OutUnicasts " QD_FMT, QD_CNT(counters, OutUnicasts)); - printk(KERN_ERR "OutMulticasts " QD_FMT, QD_CNT(counters, OutMulticasts)); - printk(KERN_ERR "OutBroadcasts " QD_FMT, QD_CNT(counters, OutBroadcasts)); - printk(KERN_ERR "OutFiltered " QD_FMT, QD_CNT(port_stats, outFiltered)); + pr_err("OutPause " QD_FMT, + QD_CNT_CORRECT(counters, OutPause, 0), QD_CNT_CORRECT(counters, OutPause, 1), + QD_CNT_CORRECT(counters, OutPause, 2), QD_CNT_CORRECT(counters, OutPause, 3), + QD_CNT_CORRECT(counters, OutPause, 4), QD_CNT_CORRECT(counters, OutPause, 5), + QD_CNT_CORRECT(counters, OutPause, 6)); + pr_err("InPause " QD_FMT, + QD_CNT_CORRECT(counters, InPause, 0), QD_CNT_CORRECT(counters, InPause, 1), + QD_CNT_CORRECT(counters, InPause, 2), QD_CNT_CORRECT(counters, InPause, 3), + QD_CNT_CORRECT(counters, InPause, 4), QD_CNT_CORRECT(counters, InPause, 5), + QD_CNT_CORRECT(counters, InPause, 6)); - printk(KERN_ERR "OutPause " QD_FMT, QD_CNT(counters, OutPause)); - printk(KERN_ERR "InPause " QD_FMT, QD_CNT(counters, InPause)); + pr_err("Octets64 " QD_FMT, + QD_CNT_CORRECT(counters, Octets64, 0), QD_CNT_CORRECT(counters, Octets64, 1), + QD_CNT_CORRECT(counters, Octets64, 2), QD_CNT_CORRECT(counters, Octets64, 3), + QD_CNT_CORRECT(counters, Octets64, 4), QD_CNT_CORRECT(counters, Octets64, 5), + QD_CNT_CORRECT(counters, Octets64, 6)); + pr_err("Octets127 " QD_FMT, + QD_CNT_CORRECT(counters, Octets127, 0), QD_CNT_CORRECT(counters, Octets127, 1), + QD_CNT_CORRECT(counters, Octets127, 2), QD_CNT_CORRECT(counters, Octets127, 3), + QD_CNT_CORRECT(counters, Octets127, 4), QD_CNT_CORRECT(counters, Octets127, 5), + QD_CNT_CORRECT(counters, Octets127, 6)); + pr_err("Octets255 " QD_FMT, + QD_CNT_CORRECT(counters, Octets255, 0), QD_CNT_CORRECT(counters, Octets255, 1), + QD_CNT_CORRECT(counters, Octets255, 2), QD_CNT_CORRECT(counters, Octets255, 3), + QD_CNT_CORRECT(counters, Octets255, 4), QD_CNT_CORRECT(counters, Octets255, 5), + QD_CNT_CORRECT(counters, Octets255, 6)); + pr_err("Octets511 " QD_FMT, + QD_CNT_CORRECT(counters, Octets511, 0), QD_CNT_CORRECT(counters, Octets511, 1), + QD_CNT_CORRECT(counters, Octets511, 2), QD_CNT_CORRECT(counters, Octets511, 3), + QD_CNT_CORRECT(counters, Octets511, 4), QD_CNT_CORRECT(counters, Octets511, 5), + QD_CNT_CORRECT(counters, Octets511, 6)); + pr_err("Octets1023 " QD_FMT, + QD_CNT_CORRECT(counters, Octets1023, 0), QD_CNT_CORRECT(counters, Octets1023, 1), + QD_CNT_CORRECT(counters, Octets1023, 2), QD_CNT_CORRECT(counters, Octets1023, 3), + QD_CNT_CORRECT(counters, Octets1023, 4), QD_CNT_CORRECT(counters, Octets1023, 5), + QD_CNT_CORRECT(counters, Octets1023, 6)); + pr_err("OctetsMax " QD_FMT, + QD_CNT_CORRECT(counters, OctetsMax, 0), QD_CNT_CORRECT(counters, OctetsMax, 1), + QD_CNT_CORRECT(counters, OctetsMax, 2), QD_CNT_CORRECT(counters, OctetsMax, 3), + QD_CNT_CORRECT(counters, OctetsMax, 4), QD_CNT_CORRECT(counters, OctetsMax, 5), + QD_CNT_CORRECT(counters, OctetsMax, 6)); + pr_err("Excessive " QD_FMT, + QD_CNT_CORRECT(counters, Excessive, 0), QD_CNT_CORRECT(counters, Excessive, 1), + QD_CNT_CORRECT(counters, Excessive, 2), QD_CNT_CORRECT(counters, Excessive, 3), + QD_CNT_CORRECT(counters, Excessive, 4), QD_CNT_CORRECT(counters, Excessive, 5), + QD_CNT_CORRECT(counters, Excessive, 6)); + pr_err("Single " QD_FMT, + QD_CNT_CORRECT(counters, Single, 0), QD_CNT_CORRECT(counters, Single, 1), + QD_CNT_CORRECT(counters, Single, 2), QD_CNT_CORRECT(counters, Single, 3), + QD_CNT_CORRECT(counters, Single, 4), QD_CNT_CORRECT(counters, Single, 5), + QD_CNT_CORRECT(counters, Single, 6)); + pr_err("Multiple " QD_FMT, + QD_CNT_CORRECT(counters, Multiple, 0), QD_CNT_CORRECT(counters, Multiple, 1), + QD_CNT_CORRECT(counters, Multiple, 2), QD_CNT_CORRECT(counters, Multiple, 3), + QD_CNT_CORRECT(counters, Multiple, 4), QD_CNT_CORRECT(counters, Multiple, 5), + QD_CNT_CORRECT(counters, Multiple, 6)); + pr_err("Undersize " QD_FMT, + QD_CNT_CORRECT(counters, Undersize, 0), QD_CNT_CORRECT(counters, Undersize, 1), + QD_CNT_CORRECT(counters, Undersize, 2), QD_CNT_CORRECT(counters, Undersize, 3), + QD_CNT_CORRECT(counters, Undersize, 4), QD_CNT_CORRECT(counters, Undersize, 5), + QD_CNT_CORRECT(counters, Undersize, 6)); + pr_err("Fragments " QD_FMT, + QD_CNT_CORRECT(counters, Fragments, 0), QD_CNT_CORRECT(counters, Fragments, 1), + QD_CNT_CORRECT(counters, Fragments, 2), QD_CNT_CORRECT(counters, Fragments, 3), + QD_CNT_CORRECT(counters, Fragments, 4), QD_CNT_CORRECT(counters, Fragments, 5), + QD_CNT_CORRECT(counters, Fragments, 6)); + pr_err("Oversize " QD_FMT, + QD_CNT_CORRECT(counters, Oversize, 0), QD_CNT_CORRECT(counters, Oversize, 1), + QD_CNT_CORRECT(counters, Oversize, 2), QD_CNT_CORRECT(counters, Oversize, 3), + QD_CNT_CORRECT(counters, Oversize, 4), QD_CNT_CORRECT(counters, Oversize, 5), + QD_CNT_CORRECT(counters, Oversize, 6)); + pr_err("Jabber " QD_FMT, + QD_CNT_CORRECT(counters, Jabber, 0), QD_CNT_CORRECT(counters, Jabber, 1), + QD_CNT_CORRECT(counters, Jabber, 2), QD_CNT_CORRECT(counters, Jabber, 3), + QD_CNT_CORRECT(counters, Jabber, 4), QD_CNT_CORRECT(counters, Jabber, 5), + QD_CNT_CORRECT(counters, Jabber, 6)); + pr_err("InMACRcvErr " QD_FMT, + QD_CNT_CORRECT(counters, InMACRcvErr, 0), QD_CNT_CORRECT(counters, InMACRcvErr, 1), + QD_CNT_CORRECT(counters, InMACRcvErr, 2), QD_CNT_CORRECT(counters, InMACRcvErr, 3), + QD_CNT_CORRECT(counters, InMACRcvErr, 4), QD_CNT_CORRECT(counters, InMACRcvErr, 5), + QD_CNT_CORRECT(counters, InMACRcvErr, 6)); + pr_err("InFCSErr " QD_FMT, + QD_CNT_CORRECT(counters, InFCSErr, 0), QD_CNT_CORRECT(counters, InFCSErr, 1), + QD_CNT_CORRECT(counters, InFCSErr, 2), QD_CNT_CORRECT(counters, InFCSErr, 3), + QD_CNT_CORRECT(counters, InFCSErr, 4), QD_CNT_CORRECT(counters, InFCSErr, 5), + QD_CNT_CORRECT(counters, InFCSErr, 6)); + pr_err("Collisions " QD_FMT, + QD_CNT_CORRECT(counters, Collisions, 0), QD_CNT_CORRECT(counters, Collisions, 1), + QD_CNT_CORRECT(counters, Collisions, 2), QD_CNT_CORRECT(counters, Collisions, 3), + QD_CNT_CORRECT(counters, Collisions, 4), QD_CNT_CORRECT(counters, Collisions, 5), + QD_CNT_CORRECT(counters, Collisions, 6)); + pr_err("Late " QD_FMT, + QD_CNT_CORRECT(counters, Late, 0), QD_CNT_CORRECT(counters, Late, 1), + QD_CNT_CORRECT(counters, Late, 2), QD_CNT_CORRECT(counters, Late, 3), + QD_CNT_CORRECT(counters, Late, 4), QD_CNT_CORRECT(counters, Late, 5), + QD_CNT_CORRECT(counters, Late, 6)); + pr_err("OutFCSErr " QD_FMT, + QD_CNT_CORRECT(counters, OutFCSErr, 0), QD_CNT_CORRECT(counters, OutFCSErr, 1), + QD_CNT_CORRECT(counters, OutFCSErr, 2), QD_CNT_CORRECT(counters, OutFCSErr, 3), + QD_CNT_CORRECT(counters, OutFCSErr, 4), QD_CNT_CORRECT(counters, OutFCSErr, 5), + QD_CNT_CORRECT(counters, OutFCSErr, 6)); + pr_err("Deferred " QD_FMT, + QD_CNT_CORRECT(counters, Deferred, 0), QD_CNT_CORRECT(counters, Deferred, 1), + QD_CNT_CORRECT(counters, Deferred, 2), QD_CNT_CORRECT(counters, Deferred, 3), + QD_CNT_CORRECT(counters, Deferred, 4), QD_CNT_CORRECT(counters, Deferred, 5), + QD_CNT_CORRECT(counters, Deferred, 6)); - printk(KERN_ERR "Octets64 " QD_FMT, QD_CNT(counters, Octets64)); - printk(KERN_ERR "Octets127 " QD_FMT, QD_CNT(counters, Octets127)); - printk(KERN_ERR "Octets255 " QD_FMT, QD_CNT(counters, Octets255)); - printk(KERN_ERR "Octets511 " QD_FMT, QD_CNT(counters, Octets511)); - printk(KERN_ERR "Octets1023 " QD_FMT, QD_CNT(counters, Octets1023)); - printk(KERN_ERR "OctetsMax " QD_FMT, QD_CNT(counters, OctetsMax)); - - printk(KERN_ERR "Excessive " QD_FMT, QD_CNT(counters, Excessive)); - printk(KERN_ERR "Single " QD_FMT, QD_CNT(counters, Single)); - printk(KERN_ERR "Multiple " QD_FMT, QD_CNT(counters, InPause)); - printk(KERN_ERR "Undersize " QD_FMT, QD_CNT(counters, Undersize)); - printk(KERN_ERR "Fragments " QD_FMT, QD_CNT(counters, Fragments)); - printk(KERN_ERR "Oversize " QD_FMT, QD_CNT(counters, Oversize)); - printk(KERN_ERR "Jabber " QD_FMT, QD_CNT(counters, Jabber)); - printk(KERN_ERR "InMACRcvErr " QD_FMT, QD_CNT(counters, InMACRcvErr)); - printk(KERN_ERR "InFCSErr " QD_FMT, QD_CNT(counters, InFCSErr)); - printk(KERN_ERR "Collisions " QD_FMT, QD_CNT(counters, Collisions)); - printk(KERN_ERR "Late " QD_FMT, QD_CNT(counters, Late)); - printk(KERN_ERR "OutFCSErr " QD_FMT, QD_CNT(counters, OutFCSErr)); - printk(KERN_ERR "Deferred " QD_FMT, QD_CNT(counters, Deferred)); - - gstatsFlushAll(qd_dev); - + /*gstatsFlushAll(qd_dev);*/ /*remove this line for FlushAll operation may cause StatusBusy bit stuck*/ for (p = 0; p < QD_MAX; p++) { + memmove(&history_counters[p], counters[p], sizeof(GT_STATS_COUNTER_SET3)); + memmove(&history_stats[p], port_stats[p], sizeof(GT_PORT_STAT2)); mvOsFree(counters[p]); mvOsFree(port_stats[p]); } @@ -1383,7 +1624,7 @@ } } -static char *mv_str_speed_state(int port) +char *mv_str_speed_state(int port) { GT_PORT_SPEED_MODE speed; char *speed_str; @@ -1406,7 +1647,7 @@ return speed_str; } -static char *mv_str_duplex_state(int port) +char *mv_str_duplex_state(int port) { GT_BOOL duplex; @@ -1421,7 +1662,7 @@ return (duplex) ? "Full" : "Half"; } -static char *mv_str_link_state(int port) +char *mv_str_link_state(int port) { GT_BOOL link; @@ -1506,6 +1747,36 @@ } } +/* paul.chen for ATU table : David Wang */ +void mv_switch_atu_print(void) +{ + GT_STATUS status; + GT_ATU_ENTRY atu_entry; + + if (qd_dev == NULL) { + pr_err("Switch is not initialized\n"); + return; + } + memset(&atu_entry, 0, sizeof(atu_entry)); + + pr_err("Printing Switch ATU Table:\n"); + if (gfdbGetAtuEntryFirst(qd_dev, &atu_entry) != GT_OK) + return; + pr_err("ATU Entry: db = %d, MAC = %02X:%02X:%02X:%02X:%02X:%02X, port vector = 0x%x\n", + atu_entry.DBNum, atu_entry.macAddr.arEther[0], + atu_entry.macAddr.arEther[1], atu_entry.macAddr.arEther[2], + atu_entry.macAddr.arEther[3], atu_entry.macAddr.arEther[4], + atu_entry.macAddr.arEther[5], atu_entry.portVec); + + while ((status = gfdbGetAtuEntryNext(qd_dev, &atu_entry)) == GT_OK) { + pr_err("ATU Entry: db = %d, MAC = %02X:%02X:%02X:%02X:%02X:%02X, port vector = 0x%x\n", + atu_entry.DBNum, atu_entry.macAddr.arEther[0], + atu_entry.macAddr.arEther[1], atu_entry.macAddr.arEther[2], + atu_entry.macAddr.arEther[3], atu_entry.macAddr.arEther[4], + atu_entry.macAddr.arEther[5], atu_entry.portVec); + } +} + void mv_switch_status_print(void) { int p, i; @@ -1668,6 +1939,36 @@ return 0; } +size_t mv_switch_get_peer_count(void) +{ + GT_U32 count = 0; + + MV_IF_NULL_RET_STR(qd_dev, MV_FAIL, "switch dev qd_dev has not been init!\n"); + + if (gfdbGetAtuAllCount(qd_dev, &count) != GT_OK) + return 0; + return count; +} + +size_t mv_switch_get_peer_mac_addresses(uint8_t mac_addresses[][6], size_t count, int port) +{ + GT_ATU_ENTRY mac_entry; + size_t i = 0; + + MV_IF_NULL_RET_STR(qd_dev, MV_FAIL, "switch dev qd_dev has not been init!\n"); + + memset(&mac_entry, 0, sizeof(mac_entry)); + if (gfdbGetAtuEntryFirst(qd_dev, &mac_entry) != GT_OK) + return 0; + do { + if (mac_entry.portVec & (1 << port)) { + memcpy(mac_addresses[i], mac_entry.macAddr.arEther, 6); + ++i; + } + } while (i <= count && gfdbGetAtuEntryNext(qd_dev, &mac_entry) == GT_OK); + return i; +} + int mv_switch_all_multicasts_del(int db_num) { GT_STATUS status = GT_OK; @@ -5307,6 +5608,66 @@ } /******************************************************************************* +* mv_switch_port_rgmii_timing_delay_set +* +* DESCRIPTION: +* This routine will set RGMII receive/transmit Timing Control. +* +* INPUTS: +* lport - logical switch PHY port ID, valid on port 5 and port 6 only. +* rxmode - GT_FALSE for default setup, GT_TRUE for adding delay to rxclk +* txmode - GT_FALSE for default setup, GT_TRUE for adding delay to txclk +* +* OUTPUTS: +* None. +* +* RETURNS: +* On success return MV_OK. +* On error different types are returned according to the case. +*******************************************************************************/ +int mv_switch_port_rgmii_timing_delay_set(unsigned int lport, GT_BOOL rxmode, GT_BOOL txmode) +{ + GT_STATUS rc = GT_OK; + + MV_IF_NULL_RET_STR(qd_dev, MV_FAIL, "switch dev qd_dev has not been init!\n"); + + rc = gpcsSetRGMIITimingDelay(qd_dev, lport, rxmode, txmode); + SW_IF_ERROR_STR(rc, "failed to call gpcsSetForcedFC()\n"); + + return MV_OK; +} + +/******************************************************************************* +* mv_switch_port_rgmii_timing_delay_set +* +* DESCRIPTION: +* This routine will set RGMII receive/transmit Timing Control. +* +* INPUTS: +* lport - logical switch PHY port ID, valid on port 5 and port 6 only. +* rxmode - GT_FALSE for default setup, GT_TRUE for adding delay to rxclk +* txmode - GT_FALSE for default setup, GT_TRUE for adding delay to txclk +* +* OUTPUTS: +* None. +* +* RETURNS: +* On success return MV_OK. +* On error different types are returned according to the case. +*******************************************************************************/ +int mv_switch_port_rgmii_timing_delay_get(unsigned int lport, GT_BOOL *rxmode, GT_BOOL *txmode) +{ + GT_STATUS rc = GT_OK; + + MV_IF_NULL_RET_STR(qd_dev, MV_FAIL, "switch dev qd_dev has not been init!\n"); + + rc = gpcsGetRGMIITimingDelay(qd_dev, lport, rxmode, txmode); + SW_IF_ERROR_STR(rc, "failed to call gpcsSetForcedFC()\n"); + + return MV_OK; +} + +/******************************************************************************* * mv_switch_cpu_port_get * * DESCRIPTION: @@ -5339,9 +5700,91 @@ return MV_OK; } +#ifdef CONFIG_OF +char *mv_switch_str; + +static int mv_switch_cmdline_config(char *s) +{ + mv_switch_str = s; + return 1; +} +__setup("switch_config=", mv_switch_cmdline_config); + +static void mv_switch_parse_cmd_line(char *str, MV_SWITCH_PRESET_TYPE *preset, MV_TAG_TYPE *tag_mode) +{ + int len, curr = 0; + + /* default values */ + *preset = MV_PRESET_TRANSPARENT; + *tag_mode = MV_TAG_TYPE_NONE; + + if (!str || !strcmp(str, "none")) + return; + + len = strlen(str); + + /* Parse tag mode */ + if ((len >= 2) && (((str[curr] == 'm') && (str[curr + 1] == 'h')) || + ((str[curr] == 'M') && (str[curr + 1] == 'H')))) { + *tag_mode = MV_TAG_TYPE_MH; + curr += 2; + } else if ((len >= 3) && (((str[curr] == 'd') && (str[curr + 1] == 's') && (str[curr + 2] == 'a')) + || ((str[curr] == 'D') && (str[curr + 1] == 'S') && (str[curr + 2] == 'A')))) { + *tag_mode = MV_TAG_TYPE_DSA; + curr += 3; + } else + return; + + if (str[curr++] != ',') + return; + + /* Parse preset mode */ + if (!strcmp(str + curr, "per_port")) + *preset = MV_PRESET_PER_PORT_VLAN; + else if (!strcmp(str + curr, "single")) + *preset = MV_PRESET_SINGLE_VLAN; +} + +#endif static int mv_switch_probe(struct platform_device *pdev) { + +#ifdef CONFIG_OF + struct mv_switch_pdata *plat_data = kzalloc(sizeof(struct mv_switch_pdata), GFP_KERNEL); + platform_set_drvdata(pdev, plat_data); + struct device_node *np = pdev->dev.of_node; + int ret; + + ret = 0; + ret |= of_property_read_u32(np, "index", &plat_data->index); + ret |= of_property_read_u32(np, "phy_addr", &plat_data->phy_addr); + ret |= of_property_read_u32(np, "gbe_port", &plat_data->gbe_port); + ret |= of_property_read_u32(np, "cpuPort", &plat_data->switch_cpu_port); + ret |= of_property_read_u32(np, "vid", &plat_data->vid); + ret |= of_property_read_u32(np, "port_mask", &plat_data->port_mask); + ret |= of_property_read_u32(np, "connected_port_mask", &plat_data->connected_port_mask); + ret |= of_property_read_u32(np, "forced_link_port_mask", &plat_data->forced_link_port_mask); + ret |= of_property_read_u32(np, "mtu", &plat_data->mtu); + ret |= of_property_read_u32(np, "smi_scan_mode", &plat_data->smi_scan_mode); + ret |= of_property_read_u32(np, "qsgmii_module", &plat_data->qsgmii_module); + ret |= of_property_read_u32(np, "gephy_on_port", &plat_data->gephy_on_port); + ret |= of_property_read_u32(np, "rgmiia_on_port", &plat_data->rgmiia_on_port); + ret |= of_property_read_u32(np, "switch_irq", &plat_data->switch_irq); + ret |= of_property_read_u32(np, "is_speed_2000", &plat_data->is_speed_2000); + ret |= of_property_read_u32(np, "rgmii_rx_timing_delay", &plat_data->rgmii_rx_timing_delay); + ret |= of_property_read_u32(np, "rgmii_tx_timing_delay", &plat_data->rgmii_tx_timing_delay); + + SW_IF_ERROR_STR(ret, "read switch fdt file failed\n"); + + mv_switch_parse_cmd_line(mv_switch_str, &plat_data->preset, &plat_data->tag_mode); + +/* paul.chen known issue on interrupt the link detect need to fix. */ + +#else struct mv_switch_pdata *plat_data = (struct mv_switch_pdata *)pdev->dev.platform_data; +#endif /* CONFIG_OF */ + + /* load switch driver, force link on cpu port */ mv_switch_load(plat_data); @@ -5379,6 +5822,14 @@ .interrupt_unmask = mv_switch_interrupt_unmask, }; +#ifdef CONFIG_OF +static const struct of_device_id mv_switch_match[] = { + { .compatible = "marvell,mv_switch" }, + { } +}; +MODULE_DEVICE_TABLE(of, pp2_match); +#endif + static struct platform_driver mv_switch_driver = { .probe = mv_switch_probe, .remove = mv_switch_remove, @@ -5388,9 +5839,35 @@ #endif /* CONFIG_CPU_IDLE */ .driver = { .name = MV_SWITCH_SOHO_NAME, +#ifdef CONFIG_OF + .of_match_table = mv_switch_match, +#endif }, }; +static __init int mv_switch_start_link_detect_thread(void) +{ + struct task_struct *thrd; + struct sched_param param; + + /* Start link status polling Thread if switch is external and need smi multi addr access*/ + if ((switch_irq == -1) && (qd_dev->accessMode == SMI_MULTI_ADDR_MODE)) { + + thrd = kthread_run(mv_switch_link_detect_thread, NULL, "link detect"); + if (IS_ERR(thrd)) + pr_err("failed to start config thread\n"); + /* Set scheduler and priority */ + param.sched_priority = 99; + sched_setscheduler(thrd, SCHED_FIFO, ¶m); + pr_info("create config thread (pid=%d)\n", thrd->pid); + switch_link_detect_thrd = thrd; + } + + return 0; +} + +late_initcall(mv_switch_start_link_detect_thread); + static int __init mv_switch_init_module(void) { return platform_driver_register(&mv_switch_driver); Index: drivers/net/ethernet/mvebu_net/switch/mv_switch.h =================================================================== --- drivers/net/ethernet/mvebu_net/switch/mv_switch.h (revision 1) +++ drivers/net/ethernet/mvebu_net/switch/mv_switch.h (working copy) @@ -124,6 +124,7 @@ /*TPM end*/ #endif + /*unsigned int mv_switch_link_detection_init(struct mv_switch_pdata *plat_data);*/ void mv_switch_interrupt_mask(void); void mv_switch_interrupt_unmask(void); @@ -148,9 +149,16 @@ int mv_switch_ptp_reg_write(int port, int reg, MV_U16 value); #endif +char *mv_str_speed_state(int port); +char *mv_str_duplex_state(int port); +char *mv_str_link_state(int port); +void mv_switch_atu_print(void); void mv_switch_stats_print(void); void mv_switch_status_print(void); +size_t mv_switch_get_peer_count(void); +size_t mv_switch_get_peer_mac_addresses(uint8_t mac_addresses[][6], size_t count, int port); + int mv_switch_all_multicasts_del(int db_num); int mv_switch_port_add(int switch_port, u16 vlan_grp_id); @@ -266,6 +274,8 @@ int mv_switch_port_force_link_get(unsigned int lport, GT_BOOL *enable, GT_BOOL *value); int mv_switch_port_state_set(unsigned int lport, enum sw_port_state_t state); int mv_switch_port_state_get(unsigned int lport, enum sw_port_state_t *state); +int mv_switch_port_rgmii_timing_delay_set(unsigned int lport, GT_BOOL rxmode, GT_BOOL txmode); +int mv_switch_port_rgmii_timing_delay_get(unsigned int lport, GT_BOOL *rxmode, GT_BOOL *txmode); int mv_switch_cpu_port_get(unsigned int *cpu_port); /*TPM end*/ #endif Index: drivers/net/ethernet/mvebu_net/switch/mv_switch_sysfs.c =================================================================== --- drivers/net/ethernet/mvebu_net/switch/mv_switch_sysfs.c (revision 1) +++ drivers/net/ethernet/mvebu_net/switch/mv_switch_sysfs.c (working copy) @@ -33,8 +33,10 @@ #include #include +#include #include "mv802_3.h" #include "mv_switch.h" +#include "mv_phy.h" static ssize_t mv_switch_help(char *buf) @@ -41,17 +43,22 @@ { int off = 0; - off += sprintf(buf+off, "cat help - show this help\n"); - off += sprintf(buf+off, "cat stats - show statistics for switch all ports info\n"); - off += sprintf(buf+off, "cat status - show switch status\n"); - off += sprintf(buf+off, "echo p grp > port_add - map switch port to a network device\n"); - off += sprintf(buf+off, "echo p > port_del - unmap switch port from a network device\n"); - off += sprintf(buf+off, "echo p r t > reg_r - read switch register. t: 1-phy, 2-port, 3-global, 4-global2, 5-smi\n"); - off += sprintf(buf+off, "echo p r t v > reg_w - write switch register. t: 1-phy, 2-port, 3-global, 4-global2, 5-smi\n"); + off += scnprintf(buf + off, PAGE_SIZE, "cat help - show this help\n"); + off += scnprintf(buf + off, PAGE_SIZE, "cat stats - show statistics for switch all ports info\n"); + off += scnprintf(buf + off, PAGE_SIZE, "cat status - show switch status\n"); + off += scnprintf(buf + off, PAGE_SIZE, "cat atu_show - show switch MAC Table\n"); + off += scnprintf(buf + off, PAGE_SIZE, "echo p grp > port_add - map switch port to a network device\n"); + off += scnprintf(buf + off, PAGE_SIZE, "echo p > port_del - unmap switch port from a network device\n"); + off += scnprintf(buf + off, PAGE_SIZE, "echo p r t > reg_r - read switch register. t: 1-phy, 2-port, 3-global, 4-global2, 5-smi\n"); + off += scnprintf(buf + off, PAGE_SIZE, "echo p r t v > reg_w - write switch register. t: 1-phy, 2-port, 3-global, 4-global2, 5-smi\n"); #ifdef CONFIG_MV_SW_PTP - off += sprintf(buf+off, "echo p r t > ptp_reg_r - read ptp register. p: 15-PTP Global, 14-TAI Global, t: not used\n"); - off += sprintf(buf+off, "echo p r t v > ptp_reg_w - write ptp register. p: 15-PTP Global, 14-TAI Global, t: not used\n"); + off += scnprintf(buf + off, PAGE_SIZE, "echo p r t > ptp_reg_r - read ptp register. p: 15-PTP Global, 14-TAI Global, t: not used\n"); + off += scnprintf(buf + off, PAGE_SIZE, "echo p r t v > ptp_reg_w - write ptp register. p: 15-PTP Global, 14-TAI Global, t: not used\n"); #endif + off += scnprintf(buf + off, PAGE_SIZE, "echo p en > power_set - set port power state.\n"); + off += scnprintf(buf + off, PAGE_SIZE, "echo p > power_get - get port power state\n"); + off += scnprintf(buf + off, PAGE_SIZE, "\ten: 0-down, 1-up\n"); + return off; } @@ -67,6 +74,8 @@ mv_switch_stats_print(); else if (!strcmp(name, "status")) mv_switch_status_print(); + else if (!strcmp(name, "atu_show")) + mv_switch_atu_print(); else off = mv_switch_help(buf); @@ -77,7 +86,7 @@ { const char *name = attr->attr.name; unsigned long flags; - int err, port, reg, type; + int err, port, reg, type, state; unsigned int v; MV_U16 val; @@ -101,6 +110,18 @@ val = (MV_U16)v; err = mv_switch_ptp_reg_write(port, reg, val); #endif + } else if (!strcmp(name, "power_set")) { + state = reg; + err = mv_phy_port_power_state_set(port, state != 0); + mvOsPrintf(" - %s, set port(%d) power %s!\n", + err == 0 ? "SUCCESS" : "FAILED", port, state == 0 ? "off" : "on"); + goto out; + } else if (!strcmp(name, "power_get")) { + GT_BOOL state; + err = mv_phy_port_power_state_get(port, &state); + mvOsPrintf("- %s, port(%d) power is %s!\n", + err == 0 ? "SUCCESS" : "FAILED", port, state == GT_FALSE ? "off" : "on"); + goto out; } printk(KERN_ERR "switch register access: type=%d, port=%d, reg=%d", type, port, reg); @@ -109,6 +130,7 @@ else printk(KERN_ERR " - SUCCESS, val=0x%04x\n", val); +out: local_irq_restore(flags); return err ? -EINVAL : len; @@ -131,7 +153,6 @@ else if (!strcmp(name, "port_del")) err = mv_switch_port_del(port); - if (err) printk(KERN_ERR " - FAILED, err=%d\n", err); else @@ -151,7 +172,11 @@ static DEVICE_ATTR(help, S_IRUSR, mv_switch_show, mv_switch_store); static DEVICE_ATTR(port_add, S_IWUSR, mv_switch_show, mv_switch_netdev_store); static DEVICE_ATTR(port_del, S_IWUSR, mv_switch_show, mv_switch_netdev_store); +static DEVICE_ATTR(atu_show, S_IRUSR, mv_switch_show, mv_switch_store); +static DEVICE_ATTR(power_set, S_IWUSR, mv_switch_show, mv_switch_store); +static DEVICE_ATTR(power_get, S_IWUSR, mv_switch_show, mv_switch_store); + static struct attribute *mv_switch_attrs[] = { &dev_attr_reg_r.attr, &dev_attr_reg_w.attr, @@ -164,6 +189,9 @@ &dev_attr_help.attr, &dev_attr_port_add.attr, &dev_attr_port_del.attr, + &dev_attr_atu_show.attr, + &dev_attr_power_set.attr, + &dev_attr_power_get.attr, NULL }; @@ -172,16 +200,31 @@ .attrs = mv_switch_attrs, }; -int __devinit mv_switch_sysfs_init(void) +int mv_switch_sysfs_init(void) { int err; struct device *pd; + int i; + pd = bus_find_device_by_name(&platform_bus_type, NULL, "neta"); + if (!pd) { + platform_device_register_simple("neta", -1, NULL, 0); + pd = bus_find_device_by_name(&platform_bus_type, NULL, "neta"); + } + + if (!pd) { + pr_err("%s: cannot find neta device\n", __func__); pd = &platform_bus; + } + + pd = &platform_bus; err = sysfs_create_group(&pd->kobj, &mv_switch_group); - if (err) - pr_err("Init sysfs group %s failed %d\n", mv_switch_group.name, err); + if (err) { + pr_info("sysfs group failed %d\n", err); + goto out; + } +out: return err; } Index: drivers/net/ethernet/mvebu_net/switch/qd-dsdt-3.3/src/msapi/gtPIRL2.c =================================================================== --- drivers/net/ethernet/mvebu_net/switch/qd-dsdt-3.3/src/msapi/gtPIRL2.c (revision 1) +++ drivers/net/ethernet/mvebu_net/switch/qd-dsdt-3.3/src/msapi/gtPIRL2.c (working copy) @@ -1213,8 +1213,7 @@ if (pirlData->ingressRate < 1000) { /* less than 1Mbps */ /* it should be divided by 64 */ - if(pirlData->ingressRate % 64) - { + if (pirlData->ingressRate % 64) { DBG_INFO(("GT_BAD_PARAM ingressRate(%i)\n",pirlData->ingressRate)); return GT_BAD_PARAM; } @@ -1225,8 +1224,7 @@ res->ebsLimit = pirl2RateLimitParaTbl[pirlData->ingressRate / 64].EBS; } else if (pirlData->ingressRate <= 20000) { /* greater or equal to 1Mbps, and less than or equal to 20Mbps, it should be divided by 1000 */ - if(pirlData->ingressRate % 1000) - { + if (pirlData->ingressRate % 1000) { DBG_INFO(("GT_BAD_PARAM ingressRate(%i)\n",pirlData->ingressRate)); return GT_BAD_PARAM; } @@ -1237,19 +1235,18 @@ } else {/* greater than 20Mbps */ if (pirlData->ingressRate < 100000) { /* it should be divided by 1000, if less than 100Mbps*/ - if(pirlData->ingressRate % 1000) - { + if (pirlData->ingressRate % 1000) { DBG_INFO(("GT_BAD_PARAM ingressRate(%i)\n",pirlData->ingressRate)); return GT_BAD_PARAM; } } else { /* it should be divided by 10000, if more or equal than 100Mbps */ - if(pirlData->ingressRate % 10000) - { + if (pirlData->ingressRate % 10000) { DBG_INFO(("GT_BAD_PARAM ingressRate(%i)\n",pirlData->ingressRate)); return GT_BAD_PARAM; } } + pirl2_cir = pirlData->ingressRate * 1000; burst_allocation = pirl2_cir; @@ -1256,11 +1253,32 @@ pirl_gcd = pirl2GetGCD(pirl2_cir, PIRL_ALPHA); res->bktRateFactor = pirl2_cir / pirl_gcd; - /* Correct Rate Factor */ - res->bktRateFactor = res->bktRateFactor * 5 / 6; - res->bktIncrement = PIRL_ALPHA / pirl_gcd; + /* Correct Rate Factor because the actuall rate will be 5/4 of the setting rate, + so we should decrease the configuration rate to 4/5, + if we use res->bktRateFactor = res->bktRateFactor * 4 / 5, then bktRateFactor might be a decimal, + it will cause inaccurate, + so here I amplify bktRateFactor by 4 and amplify bktIncrement by 5. + And if we want to hold random size packets, we can plus 1 more to bktRateFactor, here I do not do it. + Since in all cases, res->bktRateFactor * 4 will not be larger than 2^16, + res->bktIncrement *5 will not be larger than 2^12, so it's safe*/ + res->bktRateFactor = res->bktRateFactor * 4; + res->bktIncrement = (PIRL_ALPHA / pirl_gcd) * 5; + res->ebsLimit = RECOMMENDED_ESB_LIMIT(dev, pirlData->ingressRate); + /* cbs = ebs - ba*bi/8, and we should avoid the counting number > ULONG_MAX*/ + if ((burst_allocation / 8) > + (((~0UL) - RECOMMENDED_CBS_LIMIT(dev, pirlData->ingressRate)) / res->bktIncrement)) res->cbsLimit = RECOMMENDED_CBS_LIMIT(dev, pirlData->ingressRate); + else if (res->ebsLimit > (res->bktIncrement * (burst_allocation/8) + + RECOMMENDED_CBS_LIMIT(dev, pirlData->ingressRate))) + res->cbsLimit = res->ebsLimit - res->bktIncrement * (burst_allocation/8); + else + res->cbsLimit = RECOMMENDED_CBS_LIMIT(dev, pirlData->ingressRate); + + DBG_INFO(("ingressRate %u pirl_gcd %u bktIncrement from 0x%x increased to 0x%x ", + pirlData->ingressRate, pirl_gcd, res->bktIncrement / 5, res->bktIncrement)); + DBG_INFO(("bktRateFactor from 0x%x increased to 0x%x cbsLimit 0x%x\r\n", + res->bktRateFactor / 4, res->bktRateFactor, res->cbsLimit)); } } Index: drivers/net/ethernet/mvebu_net/switch/qd-dsdt-3.3/src/msapi/gtSysConfig.c =================================================================== --- drivers/net/ethernet/mvebu_net/switch/qd-dsdt-3.3/src/msapi/gtSysConfig.c (revision 1) +++ drivers/net/ethernet/mvebu_net/switch/qd-dsdt-3.3/src/msapi/gtSysConfig.c (working copy) @@ -762,6 +762,8 @@ break; case GT_88E6172: dev->numOfPorts = 7; + dev->maxPorts = 7; + dev->maxPhyNum = 5; dev->validPortVec = (1 << dev->numOfPorts) - 1; dev->validPhyVec = 0x1F; dev->validSerdesVec = 0x8000; Index: drivers/of/base.c =================================================================== --- drivers/of/base.c (revision 1) +++ drivers/of/base.c (working copy) @@ -1103,7 +1103,8 @@ static int __of_parse_phandle_with_args(const struct device_node *np, const char *list_name, - const char *cells_name, int index, + const char *cells_name, + int cell_count, int index, struct of_phandle_args *out_args) { const __be32 *list, *list_end; @@ -1139,12 +1140,18 @@ np->full_name); goto err; } - if (of_property_read_u32(node, cells_name, &count)) { + + if (cells_name) { + if (of_property_read_u32(node, cells_name, + &count)) { pr_err("%s: could not get %s for %s\n", np->full_name, cells_name, node->full_name); goto err; } + } else { + count = cell_count; + } /* * Make sure that the arguments actually fit in the @@ -1209,11 +1216,53 @@ { if (index < 0) return -EINVAL; - return __of_parse_phandle_with_args(np, list_name, cells_name, index, out_args); + return __of_parse_phandle_with_args(np, list_name, cells_name, 0, + index, out_args); } EXPORT_SYMBOL(of_parse_phandle_with_args); /** + * of_parse_phandle_with_fixed_args() - Find a node pointed by phandle in a list + * @np: pointer to a device tree node containing a list + * @list_name: property name that contains a list + * @cell_count: number of argument cells following the phandle + * @index: index of a phandle to parse out + * @out_args: optional pointer to output arguments structure (will be filled) + * + * This function is useful to parse lists of phandles and their arguments. + * Returns 0 on success and fills out_args, on error returns appropriate + * errno value. + * + * Caller is responsible to call of_node_put() on the returned out_args->node + * pointer. + * + * Example: + * + * phandle1: node1 { + * } + * + * phandle2: node2 { + * } + * + * node3 { + * list = <&phandle1 0 2 &phandle2 2 3>; + * } + * + * To get a device_node of the `node2' node you may call this: + * of_parse_phandle_with_fixed_args(node3, "list", 2, 1, &args); + */ +int of_parse_phandle_with_fixed_args(const struct device_node *np, + const char *list_name, int cell_count, + int index, struct of_phandle_args *out_args) +{ + if (index < 0) + return -EINVAL; + return __of_parse_phandle_with_args(np, list_name, NULL, cell_count, + index, out_args); +} +EXPORT_SYMBOL(of_parse_phandle_with_fixed_args); + +/** * of_count_phandle_with_args() - Find the number of phandles references in a property * @np: pointer to a device tree node containing a list * @list_name: property name that contains a list @@ -1231,7 +1280,8 @@ int of_count_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name) { - return __of_parse_phandle_with_args(np, list_name, cells_name, -1, NULL); + return __of_parse_phandle_with_args(np, list_name, cells_name, 0, -1, + NULL); } EXPORT_SYMBOL(of_count_phandle_with_args); Index: drivers/pci/host/pci-mvebu.c =================================================================== --- drivers/pci/host/pci-mvebu.c (revision 1) +++ drivers/pci/host/pci-mvebu.c (working copy) @@ -111,7 +111,9 @@ struct mvebu_pcie_port *ports; struct msi_chip *msi; struct resource io; + char io_name[30]; struct resource realio; + char mem_name[30]; struct resource mem; struct resource busn; int nports; @@ -140,6 +142,11 @@ size_t iowin_size; }; +static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port) +{ + return port->io_target != -1 && port->io_attr != -1; +} + static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) { return !(readl(port->base + PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); @@ -281,6 +288,58 @@ return ret; } +/* + * Remove windows, starting from the largest ones to the smallest + * ones. + */ +static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port, + phys_addr_t base, size_t size) +{ + while (size) { + size_t sz = 1 << (fls(size) - 1); + + mvebu_mbus_del_window(base, sz); + base += sz; + size -= sz; + } +} + +/* + * MBus windows can only have a power of two size, but PCI BARs do not + * have this constraint. Therefore, we have to split the PCI BAR into + * areas each having a power of two size. We start from the largest + * one (i.e highest order bit set in the size). + */ +static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, + unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size, + phys_addr_t remap) +{ + size_t size_mapped = 0; + + while (size) { + size_t sz = 1 << (fls(size) - 1); + int ret; + + ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base, + sz, remap); + if (ret) { + dev_err(&port->pcie->pdev->dev, + "Could not create MBus window at 0x%x, size 0x%x: %d\n", + base, sz, ret); + mvebu_pcie_del_windows(port, base - size_mapped, + size_mapped); + return; + } + + size -= sz; + size_mapped += sz; + base += sz; + if (remap != MVEBU_MBUS_NO_REMAP) + remap += sz; + } +} + static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) { phys_addr_t iobase; @@ -291,7 +350,7 @@ /* If a window was configured, remove it */ if (port->iowin_base) { - mvebu_mbus_del_window(port->iowin_base, + mvebu_pcie_del_windows(port, port->iowin_base, port->iowin_size); port->iowin_base = 0; port->iowin_size = 0; @@ -300,6 +359,12 @@ return; } + if (!mvebu_has_ioport(port)) { + dev_WARN(&port->pcie->pdev->dev, + "Attempt to set IO when IO is disabled\n"); + return; + } + /* * We read the PCI-to-PCI bridge emulated registers, and * calculate the base address and size of the address decoding @@ -312,9 +377,9 @@ port->iowin_base = port->pcie->io.start + iobase; port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) | (port->bridge.iolimitupper << 16)) - - iobase); + iobase) + 1; - mvebu_mbus_add_window_remap_by_id(port->io_target, port->io_attr, + mvebu_pcie_add_windows(port, port->io_target, port->io_attr, port->iowin_base, port->iowin_size, iobase); @@ -328,7 +393,7 @@ /* If a window was configured, remove it */ if (port->memwin_base) { - mvebu_mbus_del_window(port->memwin_base, + mvebu_pcie_del_windows(port, port->memwin_base, port->memwin_size); port->memwin_base = 0; port->memwin_size = 0; @@ -346,10 +411,11 @@ port->memwin_base = ((port->bridge.membase & 0xFFF0) << 16); port->memwin_size = (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) - - port->memwin_base; + port->memwin_base + 1; - mvebu_mbus_add_window_by_id(port->mem_target, port->mem_attr, - port->memwin_base, port->memwin_size); + mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr, + port->memwin_base, port->memwin_size, + MVEBU_MBUS_NO_REMAP); } /* @@ -413,6 +479,9 @@ break; case PCI_IO_BASE: + if (!mvebu_has_ioport(port)) + *value = bridge->secondary_status << 16; + else *value = (bridge->secondary_status << 16 | bridge->iolimit << 8 | bridge->iobase); @@ -472,6 +541,9 @@ switch (where & ~3) { case PCI_COMMAND: + if (!mvebu_has_ioport(port)) + value &= ~PCI_COMMAND_IO; + bridge->command = value & 0xffff; break; @@ -637,8 +709,30 @@ { struct mvebu_pcie *pcie = sys_to_pcie(sys); int i; + int domain = 0; - pci_add_resource_offset(&sys->resources, &pcie->realio, sys->io_offset); +#ifdef CONFIG_PCI_DOMAINS + domain = sys->domain; +#endif + + snprintf(pcie->mem_name, sizeof(pcie->mem_name), "PCI MEM %04x", + domain); + pcie->mem.name = pcie->mem_name; + + snprintf(pcie->io_name, sizeof(pcie->io_name), "PCI I/O %04x", domain); + pcie->realio.name = pcie->io_name; + + if (request_resource(&iomem_resource, &pcie->mem)) + return 0; + + if (resource_size(&pcie->realio) != 0) { + if (request_resource(&ioport_resource, &pcie->realio)) { + release_resource(&pcie->mem); + return 0; + } + pci_add_resource_offset(&sys->resources, &pcie->realio, + sys->io_offset); + } pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); @@ -696,14 +790,21 @@ /* * On the PCI-to-PCI bridge side, the I/O windows must have at - * least a 64 KB size and be aligned on their size, and the - * memory windows must have at least a 1 MB size and be - * aligned on their size + * least a 64 KB size and the memory windows must have at + * least a 1 MB size. Moreover, MBus windows need to have a + * base address aligned on their size, and their size must be + * a power of two. This means that if the BAR doesn't have a + * power of two size, several MBus windows will actually be + * created. We need to ensure that the biggest MBus window + * (which will be the first one) is aligned on its size, which + * explains the rounddown_pow_of_two() being done here. */ if (res->flags & IORESOURCE_IO) - return round_up(start, max((resource_size_t)SZ_64K, size)); + return round_up(start, max_t(resource_size_t, SZ_64K, + rounddown_pow_of_two(size))); else if (res->flags & IORESOURCE_MEM) - return round_up(start, max((resource_size_t)SZ_1M, size)); + return round_up(start, max_t(resource_size_t, SZ_1M, + rounddown_pow_of_two(size))); else return start; } @@ -768,12 +869,17 @@ #define DT_CPUADDR_TO_ATTR(cpuaddr) (((cpuaddr) >> 48) & 0xFF) static int mvebu_get_tgt_attr(struct device_node *np, int devfn, - unsigned long type, int *tgt, int *attr) + unsigned long type, + unsigned int *tgt, + unsigned int *attr) { const int na = 3, ns = 2; const __be32 *range; int rlen, nranges, rangesz, pna, i; + *tgt = -1; + *attr = -1; + range = of_get_property(np, "ranges", &rlen); if (!range) return -EINVAL; @@ -805,6 +911,28 @@ return -ENOENT; } +static int mvebu_pcie_suspend(struct platform_device *pdev, pm_message_t message) +{ + int i; + + for (i = 0; i < nports; i++) + mbus_pcie_save[i] = (readl(port_bak[i]->base + PCIE_STAT_OFF)); + + return 0; +} + +int mvebu_pcie_resume(void) +{ + int i; + + for (i = 0; i < nports; i++) { + writel_relaxed(mbus_pcie_save[i], port_bak[i]->base + PCIE_STAT_OFF); + mvebu_pcie_setup_hw(port_bak[i]); + } + + return 0; +} + static int __init mvebu_pcie_probe(struct platform_device *pdev) { struct mvebu_pcie *pcie; @@ -827,16 +955,15 @@ } mvebu_mbus_get_pcie_io_aperture(&pcie->io); - if (resource_size(&pcie->io) == 0) { - dev_err(&pdev->dev, "invalid I/O aperture size\n"); - return -EINVAL; - } + if (resource_size(&pcie->io) != 0) { pcie->realio.flags = pcie->io.flags; pcie->realio.start = PCIBIOS_MIN_IO; pcie->realio.end = min_t(resource_size_t, IO_SPACE_LIMIT, resource_size(&pcie->io)); + } else + pcie->realio = pcie->io; /* Get the bus range */ ret = of_pci_parse_bus_range(np, &pcie->busn); @@ -894,12 +1021,12 @@ continue; } - ret = mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO, + if (resource_size(&pcie->io) != 0) + mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO, &port->io_target, &port->io_attr); - if (ret < 0) { - dev_err(&pdev->dev, "PCIe%d.%d: cannot get tgt/attr for io window\n", - port->port, port->lane); - continue; + else { + port->io_target = -1; + port->io_attr = -1; } port->clk = of_clk_get_by_name(child, NULL); @@ -959,6 +1086,11 @@ MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table); static struct platform_driver mvebu_pcie_driver = { +#ifdef CONFIG_PM + .suspend = mvebu_pcie_suspend, + /* Move PCIe resume to ealier stage in the resume sequence to avoid resume failures - TBD */ + /* .resume = mvebu_pcie_resume, */ +#endif .driver = { .owner = THIS_MODULE, .name = "mvebu-pcie", @@ -975,26 +1107,6 @@ subsys_initcall(mvebu_pcie_init); -#ifdef CONFIG_PM -void mvebu_pcie_suspend(void) -{ - int i; - - for (i = 0; i < nports; i++) - mbus_pcie_save[i] = (readl(port_bak[i]->base + PCIE_STAT_OFF)); -} - -void mvebu_pcie_resume(void) -{ - int i; - - for (i = 0; i < nports; i++) { - writel_relaxed(mbus_pcie_save[i], port_bak[i]->base + PCIE_STAT_OFF); - mvebu_pcie_setup_hw(port_bak[i]); - } -} -#endif - MODULE_AUTHOR("Thomas Petazzoni "); MODULE_DESCRIPTION("Marvell EBU PCIe driver"); MODULE_LICENSE("GPLv2"); Index: drivers/rtc/rtc-mvebu.c =================================================================== --- drivers/rtc/rtc-mvebu.c (revision 1) +++ drivers/rtc/rtc-mvebu.c (working copy) @@ -73,13 +73,13 @@ /* Update RTC-MBUS bridge timing parameters */ writel(0xFD4D4CFA, rtc->regbase_soc); - /* Setup nominal register access timing */ - RTC_WRITE_REG(RTC_NOMINAL_TIMING, RTC_CLOCK_CORR_REG_OFFS); - /* Make sure we are not in any test mode */ RTC_WRITE_REG(0, RTC_TEST_CONFIG_REG_OFFS); msleep_interruptible(500); + /* Setup nominal register access timing */ + RTC_WRITE_REG(RTC_NOMINAL_TIMING, RTC_CLOCK_CORR_REG_OFFS); + /* Turn off Int1 sources & clear the Alarm count */ RTC_WRITE_REG(0, RTC_IRQ_1_CONFIG_REG_OFFS); RTC_WRITE_REG(0, RTC_ALARM_1_REG_OFFS); @@ -104,8 +104,6 @@ (0 == alrm1) && (0xC0 == int1) && (0 == alrm2) && (0xC0 == int2) && (0 == tstcfg)) { - /* Setup the loosest register access timing possible */ - RTC_WRITE_REG(~RTC_SZ_TIMING_RESERVED1_MASK, RTC_CLOCK_CORR_REG_OFFS); return true; } else { return false; @@ -203,10 +201,20 @@ static int mvebu_rtc_read_time(struct device *dev, struct rtc_time *tm) { mvebu_rtc_t *rtc = dev_get_drvdata(dev); - unsigned long time; + unsigned long time, time_check; spin_lock_irq(&rtc->lock); - rtc_time_to_tm((time = RTC_READ_REG(RTC_TIME_REG_OFFS)), tm); + + time = RTC_READ_REG(RTC_TIME_REG_OFFS); + + /* WA for failing time read attempts. The HW ERRATA information should be added here */ + /* if detected more than one second between two time reads, read once again */ + time_check = RTC_READ_REG(RTC_TIME_REG_OFFS); + if ((time_check - time) > 1) + time_check = RTC_READ_REG(RTC_TIME_REG_OFFS); + /* End of WA */ + + rtc_time_to_tm(time_check, tm); spin_unlock_irq(&rtc->lock); return 0; @@ -219,8 +227,11 @@ if (rtc_tm_to_time(tm, &time) == 0) { spin_lock_irq(&rtc->lock); + /* WA for failing time set attempts. The HW ERRATA information should be added here */ + RTC_WRITE_REG(0, RTC_STATUS_REG_OFFS); + mdelay(100); + /* End of SW WA */ RTC_WRITE_REG(time, RTC_TIME_REG_OFFS); - RTC_WRITE_REG(time, RTC_TIME_REG_OFFS); spin_unlock_irq(&rtc->lock); } @@ -347,6 +358,9 @@ ret = -ENOENT; goto errExit1; } + + /* No need to re-init the RTC as it was already initialized by the boot loader at start of battery life */ +#if 0 /* Init the state of the RTC, failure indicates there is probably no battery */ { @@ -361,7 +375,7 @@ goto errExit3; } } - +#endif spin_lock_init(&rtc->lock); /* register shared periodic/carry/alarm irq */ @@ -420,6 +434,16 @@ return 0; } +static int mvebu_rtc_resume(struct platform_device *pdev) +{ + mvebu_rtc_t *rtc = platform_get_drvdata(pdev); + + /* Update RTC-MBUS bridge timing parameters */ + writel(0xFD4D4CFA, rtc->regbase_soc); + + return 0; +} + #ifdef CONFIG_OF static struct of_device_id rtc_mvebu_of_match_table[] = { { .compatible = "marvell,mvebu-rtc", }, @@ -430,6 +454,9 @@ static struct platform_driver mvebu_rtc_driver = { .probe = mvebu_rtc_probe, .remove = __exit_p(mvebu_rtc_remove), +#ifdef CONFIG_PM + .resume = mvebu_rtc_resume, +#endif .driver = { .name = "mvebu-rtc", .owner = THIS_MODULE, Index: drivers/spi/spi-orion.c =================================================================== --- drivers/spi/spi-orion.c (revision 1) +++ drivers/spi/spi-orion.c (working copy) @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - #include #include #include @@ -18,12 +17,15 @@ #include #include #include +#include #include #include #define DRIVER_NAME "orion_spi" -#define ORION_NUM_CHIPSELECTS 1 /* only one slave is supported*/ +#define ORION_NUM_CHIPSELECTS 4 +#define ORION_CHIPSELECTS_OFFS 2 +#define ORION_CHIPSELECTS_MASK (0x3 << ORION_CHIPSELECTS_OFFS) #define ORION_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */ #define ORION_SPI_IF_CTRL_REG 0x00 @@ -36,15 +38,28 @@ #define ORION_SPI_MODE_CPHA (1 << 12) #define ORION_SPI_IF_8_16_BIT_MODE (1 << 5) #define ORION_SPI_CLK_PRESCALE_MASK 0x1F +#define ARMADA_SPI_CLK_PRESCALE_MASK 0xDF #define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \ ORION_SPI_MODE_CPHA) +enum orion_spi_type { + ORION_SPI, + ARMADA_SPI, +}; + +struct orion_spi_dev { + enum orion_spi_type typ; + unsigned int min_divisor; + unsigned int max_divisor; + u32 prescale_mask; +}; + struct orion_spi { struct spi_master *master; void __iomem *base; - unsigned int max_speed; - unsigned int min_speed; struct clk *clk; + struct spi_device *cur_spi; + const struct orion_spi_dev *devdata; }; static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) @@ -98,11 +113,46 @@ u32 prescale; u32 reg; struct orion_spi *orion_spi; + const struct orion_spi_dev *devdata; orion_spi = spi_master_get_devdata(spi->master); + devdata = orion_spi->devdata; tclk_hz = clk_get_rate(orion_spi->clk); + if (devdata->typ == ARMADA_SPI) { + unsigned int clk, spr, sppr, sppr2, err; + unsigned int best_spr, best_sppr, best_err; + + best_err = speed; + best_spr = 0; + best_sppr = 0; + + /* Iterate over the valid range looking for best fit */ + for (sppr = 0; sppr < 8; sppr++) { + sppr2 = 0x1 << sppr; + + spr = tclk_hz / sppr2; + spr = DIV_ROUND_UP(spr, speed); + if ((spr == 0) || (spr > 15)) + continue; + + clk = tclk_hz / (spr * sppr2); + err = speed - clk; + + if (err < best_err) { + best_spr = spr; + best_sppr = sppr; + best_err = err; + } + } + + if ((best_sppr == 0) && (best_spr == 0)) + return -EINVAL; + + prescale = ((best_sppr & 0x6) << 5) | + ((best_sppr & 0x1) << 4) | best_spr; + } else { /* * the supported rates are: 4,6,8...30 * round up as we look for equal or less speed @@ -119,9 +169,10 @@ /* Convert the rate to SPI clock divisor value. */ prescale = 0x10 + rate/2; + } reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); - reg = ((reg & ~ORION_SPI_CLK_PRESCALE_MASK) | prescale); + reg = ((reg & ~devdata->prescale_mask) | prescale); writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); return 0; @@ -174,10 +225,11 @@ static void orion_spi_set_cs(struct orion_spi *orion_spi, int enable) { + orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, + 0x1 | ORION_CHIPSELECTS_MASK); if (enable) - orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); - else - orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); + orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, + 0x1 | (orion_spi->cur_spi->chip_select << ORION_CHIPSELECTS_OFFS)); } static inline int orion_spi_wait_till_ready(struct orion_spi *orion_spi) @@ -200,8 +252,15 @@ { void __iomem *tx_reg, *rx_reg, *int_reg; struct orion_spi *orion_spi; + bool cs_single_byte; + cs_single_byte = spi->mode & SPI_1BYTE_CS; + orion_spi = spi_master_get_devdata(spi->master); + + if (cs_single_byte) + orion_spi_set_cs(orion_spi, 1); + tx_reg = spi_reg(orion_spi, ORION_SPI_DATA_OUT_REG); rx_reg = spi_reg(orion_spi, ORION_SPI_DATA_IN_REG); int_reg = spi_reg(orion_spi, ORION_SPI_INT_CAUSE_REG); @@ -215,6 +274,11 @@ writel(0, tx_reg); if (orion_spi_wait_till_ready(orion_spi) < 0) { + if (cs_single_byte) { + orion_spi_set_cs(orion_spi, 0); + /* Satisfy some SLIC devices requirements */ + udelay(4); + } dev_err(&spi->dev, "TXS timed out\n"); return -1; } @@ -222,6 +286,12 @@ if (rx_buf && *rx_buf) *(*rx_buf)++ = readl(rx_reg); + if (cs_single_byte) { + orion_spi_set_cs(orion_spi, 0); + /* Satisfy some SLIC devices requirements */ + udelay(4); + } + return 1; } @@ -298,6 +368,7 @@ struct orion_spi *orion_spi = spi_master_get_devdata(master); struct spi_device *spi = m->spi; struct spi_transfer *t = NULL; + bool cs_single_byte; int par_override = 0; int status = 0; int cs_active = 0; @@ -308,6 +379,10 @@ if (status < 0) goto msg_done; + orion_spi->cur_spi = spi; + + cs_single_byte = spi->mode & SPI_1BYTE_CS; + list_for_each_entry(t, &m->transfers, transfer_list) { /* make sure buffer length is even when working in 16 * bit mode*/ @@ -320,16 +395,6 @@ goto msg_done; } - if (t->speed_hz && t->speed_hz < orion_spi->min_speed) { - dev_err(&spi->dev, - "message rejected : " - "device min speed (%d Hz) exceeds " - "required transfer speed (%d Hz)\n", - orion_spi->min_speed, t->speed_hz); - status = -EIO; - goto msg_done; - } - if (par_override || t->speed_hz || t->bits_per_word) { par_override = 1; status = orion_spi_setup_transfer(spi, t); @@ -339,7 +404,7 @@ par_override = 0; } - if (!cs_active) { + if (!cs_active && !cs_single_byte) { orion_spi_set_cs(orion_spi, 1); cs_active = 1; } @@ -350,7 +415,7 @@ if (t->delay_usecs) udelay(t->delay_usecs); - if (t->cs_change) { + if (t->cs_change && !cs_single_byte) { orion_spi_set_cs(orion_spi, 0); cs_active = 0; } @@ -357,7 +422,7 @@ } msg_done: - if (cs_active) + if (cs_active && !cs_single_byte) orion_spi_set_cs(orion_spi, 0); m->status = status; @@ -374,35 +439,38 @@ return 0; } -static int orion_spi_setup(struct spi_device *spi) -{ - struct orion_spi *orion_spi; +static const struct orion_spi_dev orion_spi_dev_data = { + .typ = ORION_SPI, + .min_divisor = 4, + .max_divisor = 30, + .prescale_mask = ORION_SPI_CLK_PRESCALE_MASK, +}; - orion_spi = spi_master_get_devdata(spi->master); +static const struct orion_spi_dev armada_spi_dev_data = { + .typ = ARMADA_SPI, + .min_divisor = 1, + .max_divisor = 1920, + .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK, +}; - if ((spi->max_speed_hz == 0) - || (spi->max_speed_hz > orion_spi->max_speed)) - spi->max_speed_hz = orion_spi->max_speed; +static const struct of_device_id orion_spi_of_match_table[] = { + { .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, }, + { .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, }, + {} +}; +MODULE_DEVICE_TABLE(of, orion_spi_of_match_table); - if (spi->max_speed_hz < orion_spi->min_speed) { - dev_err(&spi->dev, "setup: requested speed too low %d Hz\n", - spi->max_speed_hz); - return -EINVAL; - } - - /* - * baudrate & width will be set orion_spi_setup_transfer - */ - return 0; -} - static int orion_spi_probe(struct platform_device *pdev) { + const struct of_device_id *of_id; + const struct orion_spi_dev *devdata; struct spi_master *master; struct orion_spi *spi; struct resource *r; unsigned long tclk_hz; int status = 0; + u32 ret; + unsigned int num_cs; master = spi_alloc_master(&pdev->dev, sizeof *spi); if (master == NULL) { @@ -417,14 +485,15 @@ if (!of_property_read_u32(pdev->dev.of_node, "cell-index", &cell_index)) master->bus_num = cell_index; + + ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); + if (ret < 0) + num_cs = ORION_NUM_CHIPSELECTS; } - /* we support only mode 0, and no options */ - master->mode_bits = SPI_CPHA | SPI_CPOL; - - master->setup = orion_spi_setup; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_1BYTE_CS; master->transfer_one_message = orion_spi_transfer_one_message; - master->num_chipselect = ORION_NUM_CHIPSELECTS; + master->num_chipselect = num_cs; dev_set_drvdata(&pdev->dev, master); @@ -431,7 +500,11 @@ spi = spi_master_get_devdata(master); spi->master = master; - spi->clk = clk_get(&pdev->dev, NULL); + of_id = of_match_device(orion_spi_of_match_table, &pdev->dev); + devdata = of_id->data; + spi->devdata = devdata; + + spi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(spi->clk)) { status = PTR_ERR(spi->clk); goto out; @@ -440,37 +513,28 @@ clk_prepare(spi->clk); clk_enable(spi->clk); tclk_hz = clk_get_rate(spi->clk); - spi->max_speed = DIV_ROUND_UP(tclk_hz, 4); - spi->min_speed = DIV_ROUND_UP(tclk_hz, 30); + master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor); + master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - status = -ENODEV; + spi->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(spi->base)) { + status = PTR_ERR(spi->base); goto out_rel_clk; } - if (!request_mem_region(r->start, resource_size(r), - dev_name(&pdev->dev))) { - status = -EBUSY; + if (orion_spi_reset(spi) < 0) goto out_rel_clk; - } - spi->base = ioremap(r->start, SZ_1K); - if (orion_spi_reset(spi) < 0) - goto out_rel_mem; - master->dev.of_node = pdev->dev.of_node; status = spi_register_master(master); if (status < 0) - goto out_rel_mem; + goto out_rel_clk; return status; -out_rel_mem: - release_mem_region(r->start, resource_size(r)); out_rel_clk: clk_disable_unprepare(spi->clk); - clk_put(spi->clk); out: spi_master_put(master); return status; @@ -480,7 +544,6 @@ static int orion_spi_remove(struct platform_device *pdev) { struct spi_master *master; - struct resource *r; struct orion_spi *spi; master = dev_get_drvdata(&pdev->dev); @@ -487,11 +550,7 @@ spi = spi_master_get_devdata(master); clk_disable_unprepare(spi->clk); - clk_put(spi->clk); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); - spi_unregister_master(master); return 0; @@ -499,12 +558,6 @@ MODULE_ALIAS("platform:" DRIVER_NAME); -static const struct of_device_id orion_spi_of_match_table[] = { - { .compatible = "marvell,orion-spi", }, - {} -}; -MODULE_DEVICE_TABLE(of, orion_spi_of_match_table); - static struct platform_driver orion_spi_driver = { .driver = { .name = DRIVER_NAME, Index: drivers/spi/spi.c =================================================================== --- drivers/spi/spi.c (revision 1) +++ drivers/spi/spi.c (working copy) @@ -868,6 +868,8 @@ spi->mode |= SPI_CS_HIGH; if (of_find_property(nc, "spi-3wire", NULL)) spi->mode |= SPI_3WIRE; + if (of_find_property(nc, "spi-1byte-cs", NULL)) + spi->mode |= SPI_1BYTE_CS; /* Device speed */ prop = of_get_property(nc, "spi-max-frequency", &len); @@ -1332,7 +1334,7 @@ if (spi->master->setup) status = spi->master->setup(spi); - dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s" + dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s%s" "%u bits/w, %u Hz max --> %d\n", (int) (spi->mode & (SPI_CPOL | SPI_CPHA)), (spi->mode & SPI_CS_HIGH) ? "cs_high, " : "", @@ -1339,6 +1341,7 @@ (spi->mode & SPI_LSB_FIRST) ? "lsb, " : "", (spi->mode & SPI_3WIRE) ? "3wire, " : "", (spi->mode & SPI_LOOP) ? "loopback, " : "", + (spi->mode & SPI_1BYTE_CS) ? "single_cs_byte, " : "", spi->bits_per_word, spi->max_speed_hz, status); @@ -1387,6 +1390,13 @@ BIT(xfer->bits_per_word - 1))) return -EINVAL; } + + if (xfer->speed_hz && master->min_speed_hz && + xfer->speed_hz < master->min_speed_hz) + return -EINVAL; + if (xfer->speed_hz && master->max_speed_hz && + xfer->speed_hz > master->max_speed_hz) + return -EINVAL; } message->spi = spi; Index: drivers/thermal/armada_thermal.c =================================================================== --- drivers/thermal/armada_thermal.c (revision 1) +++ drivers/thermal/armada_thermal.c (working copy) @@ -324,9 +324,23 @@ return 0; } +static int armada_thermal_resume(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal = + platform_get_drvdata(pdev); + struct armada_thermal_priv *priv = thermal->devdata; + + priv->data->init_sensor(pdev, priv); + + return 0; +} + static struct platform_driver armada_thermal_driver = { .probe = armada_thermal_probe, .remove = armada_thermal_exit, +#ifdef CONFIG_PM + .resume = armada_thermal_resume, +#endif .driver = { .name = "armada_thermal", .owner = THIS_MODULE, Index: drivers/usb/host/ehci-orion.c =================================================================== --- drivers/usb/host/ehci-orion.c (revision 1) +++ drivers/usb/host/ehci-orion.c (working copy) @@ -24,8 +24,8 @@ #include "ehci.h" -#define rdl(off) __raw_readl(hcd->regs + (off)) -#define wrl(off, val) __raw_writel((val), hcd->regs + (off)) +#define rdl(off) readl_relaxed(hcd->regs + (off)) +#define wrl(off, val) writel_relaxed((val), hcd->regs + (off)) #define USB_CMD 0x140 #define USB_MODE 0x1a8 @@ -46,6 +46,8 @@ static struct hc_driver __read_mostly ehci_orion_hc_driver; +static u32 usb_save[(USB_IPG - USB_CAUSE) + (USB_PHY_TST_GRP_CTRL - USB_PHY_PWR_CTRL)]; + /* * Implement Orion USB controller specification guidelines */ @@ -287,9 +289,78 @@ clk_disable_unprepare(clk); clk_put(clk); } + return 0; } +static int ehci_orion_drv_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + int addr, i; + + for (addr = USB_CAUSE, i = 0; addr <= USB_IPG; addr += 0x4, i++) + usb_save[i] = readl_relaxed(hcd->regs + addr); + + for (addr = USB_PHY_PWR_CTRL; addr <= USB_PHY_TST_GRP_CTRL; addr += 0x4, i++) + usb_save[i] = readl_relaxed(hcd->regs + addr); + + return 0; +} + +#define MV_USB_CORE_CMD_RESET_BIT 1 +#define MV_USB_CORE_CMD_RESET_MASK (1 << MV_USB_CORE_CMD_RESET_BIT) +#define MV_USB_CORE_MODE_OFFSET 0 +#define MV_USB_CORE_MODE_MASK (3 << MV_USB_CORE_MODE_OFFSET) +#define MV_USB_CORE_MODE_HOST (3 << MV_USB_CORE_MODE_OFFSET) +#define MV_USB_CORE_MODE_DEVICE (2 << MV_USB_CORE_MODE_OFFSET) +#define MV_USB_CORE_CMD_RUN_BIT 0 +#define MV_USB_CORE_CMD_RUN_MASK (1 << MV_USB_CORE_CMD_RUN_BIT) + +static int ehci_orion_drv_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + int addr, regVal, i; + + for (addr = USB_CAUSE, i = 0; addr <= USB_IPG; addr += 0x4, i++) + writel_relaxed(usb_save[i], hcd->regs + addr); + + for (addr = USB_PHY_PWR_CTRL; addr <= USB_PHY_TST_GRP_CTRL; addr += 0x4, i++) + writel_relaxed(usb_save[i], hcd->regs + addr); + + /* Clear Interrupt Cause and Mask registers */ + writel_relaxed(0, hcd->regs + 0x310); + writel_relaxed(0, hcd->regs + 0x314); + + /* Reset controller */ + regVal = readl_relaxed(hcd->regs + 0x140); + writel_relaxed(regVal | MV_USB_CORE_CMD_RESET_MASK, hcd->regs + 0x140); + while (readl_relaxed(hcd->regs + 0x140) & MV_USB_CORE_CMD_RESET_MASK) + ; + + /* Set Mode register (Stop and Reset USB Core before) */ + /* Stop the controller */ + regVal = readl_relaxed(hcd->regs + 0x140); + regVal &= ~MV_USB_CORE_CMD_RUN_MASK; + writel_relaxed(regVal, hcd->regs + 0x140); + + /* Reset the controller to get default values */ + regVal = readl_relaxed(hcd->regs + 0x140); + regVal |= MV_USB_CORE_CMD_RESET_MASK; + writel_relaxed(regVal, hcd->regs + 0x140); + + /* Wait for the controller reset to complete */ + do { + regVal = readl_relaxed(hcd->regs + 0x140); + } while (regVal & MV_USB_CORE_CMD_RESET_MASK); + + /* Set USB_MODE register */ + regVal = MV_USB_CORE_MODE_HOST; + writel_relaxed(regVal, hcd->regs + 0x1A8); + + return 0; +} + static int ehci_orion_drv_shutdown(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); @@ -320,6 +391,10 @@ static struct platform_driver ehci_orion_driver = { .probe = ehci_orion_drv_probe, .remove = ehci_orion_drv_remove, +#ifdef CONFIG_PM + .suspend = ehci_orion_drv_suspend, + .resume = ehci_orion_drv_resume, +#endif .shutdown = ehci_orion_drv_shutdown, .driver = { .name = "orion-ehci", Index: drivers/usb/host/xhci-mem.c =================================================================== --- drivers/usb/host/xhci-mem.c (revision 1) +++ drivers/usb/host/xhci-mem.c (working copy) @@ -995,6 +995,32 @@ return 0; } +void mrvl_refresh_ring_seg(struct xhci_segment *seg, unsigned int cycle_state) +{ + union xhci_trb *current_trb = seg->trbs; + do { + if (cycle_state != 0) + current_trb->link.control &= ~TRB_CYCLE; + else + current_trb->link.control |= TRB_CYCLE; + if (TRB_TYPE_LINK_LE32(current_trb->link.control)) + break; + } while (current_trb++); +} + +void mrvl_refresh_ring(struct xhci_ring *ring, unsigned int cycle_state) +{ + struct xhci_segment *current_seg = ring->first_seg; + + while (current_seg != ring->last_seg) { + mrvl_refresh_ring_seg(current_seg, cycle_state); + current_seg = current_seg->next; + }; + + mrvl_refresh_ring_seg(current_seg, cycle_state); + xhci_initialize_ring_info(ring, cycle_state); +} + void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci, struct usb_device *udev) { @@ -1012,9 +1038,17 @@ * configured device has reset, so all control transfers should have * been completed or cancelled before the reset. */ +#define MRVL_XHCI_RELEASE_3_0 1 +#if defined(MRVL_XHCI_RELEASE_3_0) && MRVL_XHCI_RELEASE_3_0 + /*walk around for address dev fail*/ + mrvl_refresh_ring(ep_ring, 1); + ep0_ctx->deq = cpu_to_le64(ep_ring->first_seg->dma | + ep_ring->cycle_state); +#else ep0_ctx->deq = cpu_to_le64(xhci_trb_virt_to_dma(ep_ring->enq_seg, ep_ring->enqueue) | ep_ring->cycle_state); +#endif } /* Index: drivers/usb/host/xhci-mvebu.c =================================================================== --- drivers/usb/host/xhci-mvebu.c (revision 1) +++ drivers/usb/host/xhci-mvebu.c (working copy) @@ -19,8 +19,12 @@ #define USB3_WIN_CTRL(w) (0x0 + ((w) * 8)) #define USB3_WIN_BASE(w) (0x4 + ((w) * 8)) +struct xhci_mvebu_priv { + void __iomem *base; + struct clk *clk; +}; -static void __init mv_usb3_conf_mbus_windows(void __iomem *base, +static void mv_usb3_conf_mbus_windows(void __iomem *base, const struct mbus_dram_target_info *dram) { int win; @@ -46,32 +50,32 @@ int xhci_mvebu_probe(struct platform_device *pdev) { struct resource *res; + struct xhci_mvebu_priv *priv; void __iomem *base; const struct mbus_dram_target_info *dram; int ret; struct clk *clk; + priv = devm_kzalloc(&pdev->dev, sizeof(struct xhci_mvebu_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) return -ENODEV; - /* - * We don't use devm_ioremap() because this mapping should - * only exists for the duration of this probe function. - */ - base = ioremap(res->start, resource_size(res)); + base = devm_ioremap_resource(&pdev->dev, res); if (!base) - return -ENODEV; + return -ENOMEM; clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { - iounmap(base); return PTR_ERR(clk); } ret = clk_prepare_enable(clk); if (ret < 0) { - iounmap(base); return ret; } @@ -78,13 +82,10 @@ dram = mv_mbus_dram_info(); mv_usb3_conf_mbus_windows(base, dram); - /* - * This memory area was only needed to configure the MBus - * windows, and is therefore no longer useful. - */ - iounmap(base); + priv->base = base; + priv->clk = clk; - ret = common_xhci_plat_probe(pdev, clk); + ret = common_xhci_plat_probe(pdev, priv); if (ret < 0) { clk_disable_unprepare(clk); return ret; @@ -97,7 +98,8 @@ { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct clk *clk = xhci->priv; + struct xhci_mvebu_priv *priv = (struct xhci_mvebu_priv *)xhci->priv; + struct clk *clk = priv->clk; common_xhci_plat_remove(pdev); clk_disable_unprepare(clk); @@ -104,3 +106,15 @@ return 0; } + +void xhci_mvebu_resume(struct device *dev) +{ + const struct mbus_dram_target_info *dram; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_mvebu_priv *priv = (struct xhci_mvebu_priv *)xhci->priv; + void __iomem *base = priv->base; + + dram = mv_mbus_dram_info(); + mv_usb3_conf_mbus_windows(base, dram); +} Index: drivers/usb/host/xhci-mvebu.h =================================================================== --- drivers/usb/host/xhci-mvebu.h (revision 1) +++ drivers/usb/host/xhci-mvebu.h (working copy) @@ -14,8 +14,10 @@ #ifdef CONFIG_USB_XHCI_MVEBU int xhci_mvebu_probe(struct platform_device *pdev); int xhci_mvebu_remove(struct platform_device *pdev); +void xhci_mvebu_resume(struct device *dev); #else #define xhci_mvebu_probe NULL #define xhci_mvebu_remove NULL +#define xhci_mvebu_resume NULL #endif #endif /* __LINUX_XHCI_MVEBU_H */ Index: drivers/usb/host/xhci-plat.c =================================================================== --- drivers/usb/host/xhci-plat.c (revision 1) +++ drivers/usb/host/xhci-plat.c (working copy) @@ -139,6 +139,12 @@ goto release_mem_region; } + /* + * Make the host wait until internal buffer is available before issuing + * data request from device - MARVELL proprietary XHCI MAC register + */ + set_bit(7, hcd->regs + 0x380c); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto unmap_registers; @@ -214,6 +220,7 @@ struct xhci_plat_ops { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); + void (*resume)(struct device *); }; static struct xhci_plat_ops xhci_plat_default = { @@ -225,6 +232,7 @@ struct xhci_plat_ops xhci_plat_mvebu = { .probe = xhci_mvebu_probe, .remove = xhci_mvebu_remove, + .resume = xhci_mvebu_resume, }; static const struct of_device_id usb_xhci_of_match[] = { @@ -281,6 +289,46 @@ return plat_of->remove(pdev); } +#ifdef CONFIG_PM +static int xhci_plat_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + return xhci_suspend(xhci); +} + +static int xhci_plat_resume(struct device *dev) +{ + const struct xhci_plat_ops *plat_of = &xhci_plat_default; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if (dev->of_node) { + const struct of_device_id *match = + of_match_device(usb_xhci_of_match, dev); + if (!match) + return -ENODEV; + plat_of = match->data; + } + + if (!plat_of) + return -ENODEV; + + if (plat_of->resume) + plat_of->resume(dev); + + return xhci_resume(xhci, 0); +} + +static const struct dev_pm_ops xhci_plat_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume) +}; +#define DEV_PM_OPS (&xhci_plat_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct platform_driver usb_xhci_driver = { .probe = xhci_plat_probe, .remove = xhci_plat_remove, @@ -287,6 +335,7 @@ .shutdown = xhci_plat_remove, .driver = { .name = "xhci-hcd", + .pm = DEV_PM_OPS, .of_match_table = of_match_ptr(usb_xhci_of_match), }, }; Index: drivers/watchdog/at91rm9200_wdt.c =================================================================== --- drivers/watchdog/at91rm9200_wdt.c (revision 1) +++ drivers/watchdog/at91rm9200_wdt.c (working copy) @@ -269,7 +269,7 @@ .driver = { .name = "at91_wdt", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(at91_wdt_dt_ids), + .of_match_table = at91_wdt_dt_ids, }, }; Index: drivers/watchdog/Kconfig =================================================================== --- drivers/watchdog/Kconfig (revision 1) +++ drivers/watchdog/Kconfig (working copy) @@ -291,7 +291,7 @@ config ORION_WATCHDOG tristate "Orion watchdog" - depends on ARCH_ORION5X || ARCH_KIRKWOOD + depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_MVEBU select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer Index: drivers/watchdog/orion_wdt.c =================================================================== --- drivers/watchdog/orion_wdt.c (revision 1) +++ drivers/watchdog/orion_wdt.c (working copy) @@ -20,102 +20,297 @@ #include #include #include +#include #include -#include #include #include #include -#include +#include +/* RSTOUT mask register physical address for Orion5x, Kirkwood and Dove */ +#define ORION_RSTOUT_MASK_OFFSET 0x20108 + +/* Internal registers can be configured at any 1 MiB aligned address */ +#define INTERNAL_REGS_MASK ~(SZ_1M - 1) + /* * Watchdog timer block registers. */ #define TIMER_CTRL 0x0000 -#define WDT_EN 0x0010 -#define WDT_VAL 0x0024 +#define TIMER_A370_STATUS 0x04 #define WDT_MAX_CYCLE_COUNT 0xffffffff -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 +#define WDT_A370_RATIO_MASK(v) ((v) << 16) +#define WDT_A370_RATIO_SHIFT 5 +#define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT) + +#define WDT_AXP_FIXED_ENABLE_BIT BIT(10) +#define WDT_A370_EXPIRED BIT(31) + static bool nowayout = WATCHDOG_NOWAYOUT; static int heartbeat = -1; /* module parameter (seconds) */ -static unsigned int wdt_max_duration; /* (seconds) */ -static struct clk *clk; -static unsigned int wdt_tclk; -static void __iomem *wdt_reg; -static DEFINE_SPINLOCK(wdt_lock); +struct orion_watchdog; + +struct orion_watchdog_data { + int wdt_counter_offset; + int wdt_enable_bit; + int rstout_enable_bit; + int rstout_mask_bit; + int (*clock_init)(struct platform_device *, + struct orion_watchdog *); + int (*enabled)(struct orion_watchdog *); + int (*start)(struct watchdog_device *); + int (*stop)(struct watchdog_device *); +}; + +struct orion_watchdog { + struct watchdog_device wdt; + void __iomem *reg; + void __iomem *rstout; + void __iomem *rstout_mask; + unsigned long clk_rate; + struct clk *clk; + const struct orion_watchdog_data *data; +}; + +static int orion_wdt_clock_init(struct platform_device *pdev, + struct orion_watchdog *dev) +{ + int ret; + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + ret = clk_prepare_enable(dev->clk); + if (ret) { + clk_put(dev->clk); + return ret; + } + + dev->clk_rate = clk_get_rate(dev->clk); + return 0; +} + +static int armada370_wdt_clock_init(struct platform_device *pdev, + struct orion_watchdog *dev) +{ + int ret; + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + ret = clk_prepare_enable(dev->clk); + if (ret) { + clk_put(dev->clk); + return ret; + } + + /* Setup watchdog input clock */ + atomic_io_modify(dev->reg + TIMER_CTRL, + WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT), + WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT)); + + dev->clk_rate = clk_get_rate(dev->clk) / WDT_A370_RATIO; + return 0; +} + +static int armadaxp_wdt_clock_init(struct platform_device *pdev, + struct orion_watchdog *dev) +{ + int ret; + + dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed"); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + ret = clk_prepare_enable(dev->clk); + if (ret) { + clk_put(dev->clk); + return ret; + } + + /* Enable the fixed watchdog clock input */ + atomic_io_modify(dev->reg + TIMER_CTRL, + WDT_AXP_FIXED_ENABLE_BIT, + WDT_AXP_FIXED_ENABLE_BIT); + + dev->clk_rate = clk_get_rate(dev->clk); + return 0; +} + static int orion_wdt_ping(struct watchdog_device *wdt_dev) { - spin_lock(&wdt_lock); - + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); /* Reload watchdog duration */ - writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL); + writel(dev->clk_rate * wdt_dev->timeout, + dev->reg + dev->data->wdt_counter_offset); + return 0; +} - spin_unlock(&wdt_lock); +static int armada375_start(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + u32 reg; + + /* Set watchdog duration */ + writel(dev->clk_rate * wdt_dev->timeout, + dev->reg + dev->data->wdt_counter_offset); + + /* Clear the watchdog expiration bit */ + atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0); + + /* Enable watchdog timer */ + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, + dev->data->wdt_enable_bit); + + /* Enable reset on watchdog */ + reg = readl(dev->rstout); + reg |= dev->data->rstout_enable_bit; + writel(reg, dev->rstout); + + atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, 0); return 0; } -static int orion_wdt_start(struct watchdog_device *wdt_dev) +static int armada370_start(struct watchdog_device *wdt_dev) { + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); u32 reg; - spin_lock(&wdt_lock); - /* Set watchdog duration */ - writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL); + writel(dev->clk_rate * wdt_dev->timeout, + dev->reg + dev->data->wdt_counter_offset); - /* Clear watchdog timer interrupt */ - reg = readl(BRIDGE_CAUSE); - reg &= ~WDT_INT_REQ; - writel(reg, BRIDGE_CAUSE); + /* Clear the watchdog expiration bit */ + atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0); /* Enable watchdog timer */ - reg = readl(wdt_reg + TIMER_CTRL); - reg |= WDT_EN; - writel(reg, wdt_reg + TIMER_CTRL); + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, + dev->data->wdt_enable_bit); /* Enable reset on watchdog */ - reg = readl(RSTOUTn_MASK); - reg |= WDT_RESET_OUT_EN; - writel(reg, RSTOUTn_MASK); + reg = readl(dev->rstout); + reg |= dev->data->rstout_enable_bit; + writel(reg, dev->rstout); + return 0; +} - spin_unlock(&wdt_lock); +static int orion_start(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + /* Set watchdog duration */ + writel(dev->clk_rate * wdt_dev->timeout, + dev->reg + dev->data->wdt_counter_offset); + + /* Enable watchdog timer */ + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, + dev->data->wdt_enable_bit); + + /* Enable reset on watchdog */ + atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, + dev->data->rstout_enable_bit); + return 0; } -static int orion_wdt_stop(struct watchdog_device *wdt_dev) +static int orion_wdt_start(struct watchdog_device *wdt_dev) { + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + /* There are some per-SoC quirks to handle */ + return dev->data->start(wdt_dev); +} + +static int orion_stop(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + /* Disable reset on watchdog */ + atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0); + + /* Disable watchdog timer */ + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); + + return 0; +} + +static int armada375_stop(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); u32 reg; - spin_lock(&wdt_lock); + /* Disable reset on watchdog */ + atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, + dev->data->rstout_mask_bit); + reg = readl(dev->rstout); + reg &= ~dev->data->rstout_enable_bit; + writel(reg, dev->rstout); + /* Disable watchdog timer */ + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); + + return 0; +} + +static int armada370_stop(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + u32 reg; + /* Disable reset on watchdog */ - reg = readl(RSTOUTn_MASK); - reg &= ~WDT_RESET_OUT_EN; - writel(reg, RSTOUTn_MASK); + reg = readl(dev->rstout); + reg &= ~dev->data->rstout_enable_bit; + writel(reg, dev->rstout); /* Disable watchdog timer */ - reg = readl(wdt_reg + TIMER_CTRL); - reg &= ~WDT_EN; - writel(reg, wdt_reg + TIMER_CTRL); + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); - spin_unlock(&wdt_lock); return 0; } -static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) +static int orion_wdt_stop(struct watchdog_device *wdt_dev) { - unsigned int time_left; + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); - spin_lock(&wdt_lock); - time_left = readl(wdt_reg + WDT_VAL) / wdt_tclk; - spin_unlock(&wdt_lock); + return dev->data->stop(wdt_dev); +} - return time_left; +static int orion_enabled(struct orion_watchdog *dev) +{ + bool enabled, running; + + enabled = readl(dev->rstout) & dev->data->rstout_enable_bit; + running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit; + + return enabled && running; } +static int armada375_enabled(struct orion_watchdog *dev) +{ + bool masked, enabled, running; + + masked = readl(dev->rstout_mask) & dev->data->rstout_mask_bit; + enabled = readl(dev->rstout) & dev->data->rstout_enable_bit; + running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit; + + return !masked && enabled && running; +} + +static int orion_wdt_enabled(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + return dev->data->enabled(dev); +} + +static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate; +} + static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev, unsigned int timeout) { @@ -137,68 +332,264 @@ .get_timeleft = orion_wdt_get_timeleft, }; -static struct watchdog_device orion_wdt = { - .info = &orion_wdt_info, - .ops = &orion_wdt_ops, - .min_timeout = 1, -}; +static irqreturn_t orion_wdt_irq(int irq, void *devid) +{ + panic("Watchdog Timeout"); + return IRQ_HANDLED; +} -static int orion_wdt_probe(struct platform_device *pdev) +/* + * The original devicetree binding for this driver specified only + * one memory resource, so in order to keep DT backwards compatibility + * we try to fallback to a hardcoded register address, if the resource + * is missing from the devicetree. + */ +static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev, + phys_addr_t internal_regs) { struct resource *res; - int ret; + phys_addr_t rstout; - clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "Orion Watchdog missing clock\n"); - return -ENODEV; + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) + return devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET; + + WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout); + return devm_ioremap(&pdev->dev, rstout, 0x4); } - clk_prepare_enable(clk); - wdt_tclk = clk_get_rate(clk); +static const struct orion_watchdog_data orion_data = { + .rstout_enable_bit = BIT(1), + .wdt_enable_bit = BIT(4), + .wdt_counter_offset = 0x24, + .clock_init = orion_wdt_clock_init, + .enabled = orion_enabled, + .start = orion_start, + .stop = orion_stop, +}; + +static const struct orion_watchdog_data armada370_data = { + .rstout_enable_bit = BIT(8), + .wdt_enable_bit = BIT(8), + .wdt_counter_offset = 0x34, + .clock_init = armada370_wdt_clock_init, + .enabled = orion_enabled, + .start = armada370_start, + .stop = armada370_stop, +}; + +static const struct orion_watchdog_data armadaxp_data = { + .rstout_enable_bit = BIT(8), + .wdt_enable_bit = BIT(8), + .wdt_counter_offset = 0x34, + .clock_init = armadaxp_wdt_clock_init, + .enabled = orion_enabled, + .start = armada370_start, + .stop = armada370_stop, +}; + +static const struct orion_watchdog_data armada375_data = { + .rstout_enable_bit = BIT(8), + .rstout_mask_bit = BIT(10), + .wdt_enable_bit = BIT(8), + .wdt_counter_offset = 0x34, + .clock_init = armada370_wdt_clock_init, + .enabled = armada375_enabled, + .start = armada375_start, + .stop = armada375_stop, +}; + +static const struct orion_watchdog_data armada380_data = { + .rstout_enable_bit = BIT(8), + .rstout_mask_bit = BIT(10), + .wdt_enable_bit = BIT(8), + .wdt_counter_offset = 0x34, + .clock_init = armadaxp_wdt_clock_init, + .enabled = armada375_enabled, + .start = armada375_start, + .stop = armada375_stop, +}; + +static const struct of_device_id orion_wdt_of_match_table[] = { + { + .compatible = "marvell,orion-wdt", + .data = &orion_data, + }, + { + .compatible = "marvell,armada-370-wdt", + .data = &armada370_data, + }, + { + .compatible = "marvell,armada-xp-wdt", + .data = &armadaxp_data, + }, + { + .compatible = "marvell,armada-375-wdt", + .data = &armada375_data, + }, + { + .compatible = "marvell,armada-380-wdt", + .data = &armada380_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table); + +static int orion_wdt_get_regs(struct platform_device *pdev, + struct orion_watchdog *dev) +{ + struct device_node *node = pdev->dev.of_node; + struct resource *res; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; - wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!wdt_reg) + dev->reg = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!dev->reg) return -ENOMEM; - wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk; + /* Each supported compatible has some RSTOUT register quirk */ + if (of_device_is_compatible(node, "marvell,orion-wdt")) { - orion_wdt.timeout = wdt_max_duration; - orion_wdt.max_timeout = wdt_max_duration; - watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev); + dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start & + INTERNAL_REGS_MASK); + if (!dev->rstout) + return -ENODEV; - watchdog_set_nowayout(&orion_wdt, nowayout); - ret = watchdog_register_device(&orion_wdt); + } else if (of_device_is_compatible(node, "marvell,armada-370-wdt") || + of_device_is_compatible(node, "marvell,armada-xp-wdt")) { + + /* Dedicated RSTOUT register, can be requested. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dev->rstout = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->rstout)) + return PTR_ERR(dev->rstout); + + } else if (of_device_is_compatible(node, "marvell,armada-375-wdt") || + of_device_is_compatible(node, "marvell,armada-380-wdt")) { + + /* Dedicated RSTOUT register, can be requested. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dev->rstout = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->rstout)) + return PTR_ERR(dev->rstout); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) + return -ENODEV; + dev->rstout_mask = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!dev->rstout_mask) + return -ENOMEM; + + } else { + return -ENODEV; + } + + return 0; +} + +static int orion_wdt_probe(struct platform_device *pdev) +{ + struct orion_watchdog *dev; + const struct of_device_id *match; + unsigned int wdt_max_duration; /* (seconds) */ + int ret, irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog), + GFP_KERNEL); + if (!dev) + return -ENOMEM; + + match = of_match_device(orion_wdt_of_match_table, &pdev->dev); + if (!match) + /* Default legacy match */ + match = &orion_wdt_of_match_table[0]; + + dev->wdt.info = &orion_wdt_info; + dev->wdt.ops = &orion_wdt_ops; + dev->wdt.min_timeout = 1; + dev->data = match->data; + + ret = orion_wdt_get_regs(pdev, dev); + if (ret) + return ret; + + ret = dev->data->clock_init(pdev, dev); if (ret) { - clk_disable_unprepare(clk); + dev_err(&pdev->dev, "cannot initialize clock\n"); return ret; } + wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate; + + dev->wdt.timeout = wdt_max_duration; + dev->wdt.max_timeout = wdt_max_duration; + watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev); + + platform_set_drvdata(pdev, &dev->wdt); + watchdog_set_drvdata(&dev->wdt, dev); + + /* + * Let's make sure the watchdog is fully stopped, unless it's + * explicitly enabled. This may be the case if the module was + * removed and re-insterted, or if the bootloader explicitly + * set a running watchdog before booting the kernel. + */ + if (!orion_wdt_enabled(&dev->wdt)) + orion_wdt_stop(&dev->wdt); + + /* Request the IRQ only after the watchdog is disabled */ + irq = platform_get_irq(pdev, 0); + if (irq > 0) { + /* + * Not all supported platforms specify an interrupt for the + * watchdog, so let's make it optional. + */ + ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0, + pdev->name, dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto disable_clk; + } + } + + watchdog_set_nowayout(&dev->wdt, nowayout); + ret = watchdog_register_device(&dev->wdt); + if (ret) + goto disable_clk; + pr_info("Initial timeout %d sec%s\n", - orion_wdt.timeout, nowayout ? ", nowayout" : ""); + dev->wdt.timeout, nowayout ? ", nowayout" : ""); return 0; + +disable_clk: + clk_disable_unprepare(dev->clk); + clk_put(dev->clk); + return ret; } static int orion_wdt_remove(struct platform_device *pdev) { - watchdog_unregister_device(&orion_wdt); - clk_disable_unprepare(clk); + struct watchdog_device *wdt_dev = platform_get_drvdata(pdev); + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + watchdog_unregister_device(wdt_dev); + clk_disable_unprepare(dev->clk); + clk_put(dev->clk); return 0; } static void orion_wdt_shutdown(struct platform_device *pdev) { - orion_wdt_stop(&orion_wdt); + struct watchdog_device *wdt_dev = platform_get_drvdata(pdev); + orion_wdt_stop(wdt_dev); } -static const struct of_device_id orion_wdt_of_match_table[] = { - { .compatible = "marvell,orion-wdt", }, - {}, -}; -MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table); - static struct platform_driver orion_wdt_driver = { .probe = orion_wdt_probe, .remove = orion_wdt_remove, @@ -206,7 +597,7 @@ .driver = { .owner = THIS_MODULE, .name = "orion_wdt", - .of_match_table = of_match_ptr(orion_wdt_of_match_table), + .of_match_table = orion_wdt_of_match_table, }, }; Index: fs/splice.c =================================================================== --- fs/splice.c (revision 1) +++ fs/splice.c (working copy) @@ -19,6 +19,7 @@ */ #include #include +#include #include #include #include @@ -36,10 +37,6 @@ #include #include "internal.h" -struct common_mempool; -static struct common_mempool/*struct gen_pool*/ * rcv_pool = NULL; -static struct common_mempool/*struct gen_pool*/ * kvec_pool = NULL; - /* * Attempt to steal a page from a pipe buffer. This should perhaps go into * a vm helper function, it's already simplified quite a bit by the @@ -1417,413 +1414,162 @@ return -EINVAL; } -/****************************** POOL MANAGER *************************************/ -/* Forward declarations */ -typedef struct common_mempool common_mempool_t; -void* common_mempool_alloc(common_mempool_t* pool); -void common_mempool_free(common_mempool_t* pool, void* mem); -common_mempool_t* common_mempool_get(void* mem); -common_mempool_t* common_mempool_create(uint32_t number_of_entries, uint32_t entry_size); -void common_mempool_destroy(common_mempool_t* pool); -int32_t common_mempool_get_number_of_free_entries(common_mempool_t* pool); -int32_t common_mempool_get_number_of_entries(common_mempool_t* pool); -int32_t common_mempool_get_entry_size(common_mempool_t* pool); - -/* Implementation */ -#define COMMON_MPOOL_HDR_FLAGS_ALLOCATED 0x00000001 -#define COMMON_MPOOL_HDR_MAGIC 0xa5a5a508 -#define COMMON_MPOOL_FTR_MAGIC 0xa5a5a509 -#define COMMON_MPOOL_ALIGN4(size) ((size)+4) & 0xFFFFFFFC; -#define COMMON_MPOOL_CHECK_ALIGNED4(ptr) ((((uint32_t)(ptr)) & 0x00000003) == 0) -#define MAX_PAGES_PER_RECVFILE 64 - -typedef struct common_mpool_hdr +ssize_t generic_splice_from_socket(struct file *file, struct socket *sock, + loff_t __user *ppos, size_t count_req) { - struct common_mpool_hdr* next; - common_mempool_t* pool; - uint32_t flags; - uint32_t magic; -} common_mpool_hdr_t; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + loff_t pos, pos_tmp; + int ret, i = 0, nr_pages; + struct recvfile_ctl_blk *rv_cb; + struct kvec *iov; + struct msghdr msg = { 0 }; + bool append = true; + int remaining, krecvmsg_sz; + size_t written = 0, verified_sz, + pg_cache_sz, pg_cache_end_sz; -typedef struct -{ - uint32_t magic; - common_mempool_t* pool; -} common_mpool_ftr_t; - -struct common_mempool -{ - common_mpool_hdr_t* head; - common_mpool_hdr_t* tail; - uint32_t number_of_free_entries; - spinlock_t lock; - uint32_t data_size; /* size of data section in pool entry */ - uint32_t pool_entry_size; /* size of pool entry */ - /* parameters passed on init */ - uint32_t number_of_entries; - uint32_t entry_size; - uint8_t* mem; -}; - -bool common_mempool_check_internal(common_mempool_t * pool, - void * ptr, - common_mpool_hdr_t * hdr, - common_mpool_ftr_t * ftr) -{ - if (!ptr) { - printk(KERN_ERR "illegal ptr NULL"); - return false; + if (copy_from_user(&pos, ppos, sizeof(loff_t))) { + ret = -EFAULT; + goto err; } - if (!COMMON_MPOOL_CHECK_ALIGNED4(ptr)) { - printk(KERN_ERR "ptr not aligned %p",ptr); - return false; - } + ret = rw_verify_area(WRITE, file, &file->f_pos, count_req); + if (ret < 0) + goto err; - if (hdr->magic != COMMON_MPOOL_HDR_MAGIC) { - printk(KERN_ERR "illegal hdr magic %x for ptr %p",hdr->magic,ptr); - return false; - } + verified_sz = ret; - if (ftr->magic != COMMON_MPOOL_FTR_MAGIC) { - printk(KERN_ERR "illegal ftr magic %x for ptr %p",ftr->magic,ptr); - return false; + nr_pages = (((pos & ~PAGE_CACHE_MASK) + count_req + ~PAGE_CACHE_MASK) >> PAGE_CACHE_SHIFT); + rv_cb = kzalloc(nr_pages * sizeof(struct recvfile_ctl_blk), GFP_KERNEL); + if (unlikely(!rv_cb)) { + ret = -ENOMEM; + goto err; } - if (hdr->pool != pool || ftr->pool != pool) { - printk(KERN_ERR "inconsistent size hdr->pool: %p ftr->pool: %p for ptr %p",hdr->pool,ftr->pool,ptr); - return false; + iov = kzalloc(nr_pages * sizeof(struct kvec), GFP_KERNEL); + if (unlikely(!iov)) { + kfree(rv_cb); + ret = -ENOMEM; + goto err; } - if (!(hdr->flags & COMMON_MPOOL_HDR_FLAGS_ALLOCATED)) { - printk(KERN_ERR "ptr %p was not allocated",ptr); - return false; - } - return true; -} + sb_start_intwrite(inode->i_sb); -void* common_mempool_alloc(common_mempool_t* pool) -{ - common_mpool_hdr_t* hdr; + /* We can write back this queue in page reclaim. */ + current->backing_dev_info = mapping->backing_dev_info; - if (!pool || !pool->head || pool->number_of_free_entries == 0) { - return NULL; - } - spin_lock_bh(&pool->lock); - hdr = pool->head; - pool->head = pool->head->next; + mutex_lock(&inode->i_mutex); - if (!pool->head) { - pool->tail = NULL; + ret = generic_write_checks(file, &pos, &verified_sz, S_ISBLK(inode->i_mode)); + if (ret < 0) { + pr_info("%s: generic_write_checks err, ret=%d:\n", __func__, ret); + goto cleanup; } - hdr->flags = COMMON_MPOOL_HDR_FLAGS_ALLOCATED; - pool->number_of_free_entries--; - spin_unlock_bh(&pool->lock); - return ((uint8_t*)hdr+sizeof(common_mpool_hdr_t)); + ret = file_remove_suid(file); + if (ret) { + pr_info("%s: file_remove_suid err, ret=%d:\n", __func__, ret); + goto cleanup; } -void common_mempool_free(common_mempool_t* pool, void* ptr) -{ - common_mpool_hdr_t* hdr; - common_mpool_ftr_t* ftr; - - if (!pool || !ptr) { - return; + ret = file_update_time(file); + if (ret) { + pr_info("%s: file_update_time err, ret=%d:\n", __func__, ret); + goto cleanup; } - if (!COMMON_MPOOL_CHECK_ALIGNED4(ptr)) { - printk(KERN_ERR "ptr not aligned %p",ptr); - return; - } - spin_lock_bh(&pool->lock); - hdr = (common_mpool_hdr_t*)((uint8_t*)ptr-sizeof(common_mpool_hdr_t)); - ftr = (common_mpool_ftr_t*)((uint8_t*)ptr+pool->data_size); - if (!common_mempool_check_internal(pool,ptr,hdr,ftr)) { - printk(KERN_ERR "invalid ptr %p",ptr); - spin_unlock_bh(&pool->lock); - return; - } + pg_cache_sz = 0; + remaining = verified_sz; + pos_tmp = pos; + for (i = 0; i < nr_pages; i++) { + pgoff_t offset = pos_tmp & (PAGE_CACHE_SIZE - 1); + unsigned len = min_t(unsigned int, PAGE_CACHE_SIZE - offset, remaining); + struct page *page; + void *fsdata; - hdr->flags ^= COMMON_MPOOL_HDR_FLAGS_ALLOCATED; - hdr->next = NULL; + ret = pagecache_write_begin(file, mapping, pos_tmp, len, + AOP_FLAG_UNINTERRUPTIBLE, + &page, &fsdata); - if (!pool->head) { - pool->head = pool->tail = hdr; - } else { - pool->tail->next = hdr; - pool->tail = hdr; + if (unlikely(ret)) { + pr_info("pagecache_write_begin err. ret %d:\n", ret); + break; } - pool->number_of_free_entries++; - spin_unlock_bh(&pool->lock); -} + rv_cb[i].rv_page = page; + rv_cb[i].rv_pos = pos_tmp; + rv_cb[i].rv_count = len; + rv_cb[i].rv_fsdata = fsdata; + iov[i].iov_base = kmap(page) + offset; + iov[i].iov_len = len; + remaining -= len; + pos_tmp += len; + pg_cache_sz += len; -common_mempool_t* common_mempool_create(uint32_t number_of_entries, - uint32_t entry_size) -{ - uint32_t i; - uint32_t aligned_entry_size; - uint32_t pool_entry_size; - common_mpool_hdr_t* hdr; - common_mpool_hdr_t* next_hdr; - common_mpool_ftr_t* ftr; - common_mempool_t* pool; - - aligned_entry_size = COMMON_MPOOL_ALIGN4(entry_size); - pool_entry_size = COMMON_MPOOL_ALIGN4(sizeof(common_mpool_hdr_t) + - aligned_entry_size + - sizeof(common_mpool_ftr_t)); - - pool = kmalloc((sizeof(common_mempool_t) + pool_entry_size*number_of_entries), GFP_ATOMIC); - - if (!pool) { - return NULL; + if (i_size_read(inode) < pos_tmp) + i_size_write(inode, pos_tmp); } - pool->entry_size = entry_size; - pool->number_of_entries = number_of_entries; - pool->data_size = aligned_entry_size; - pool->pool_entry_size = pool_entry_size; - pool->number_of_free_entries = number_of_entries; - pool->mem = (uint8_t*)(pool+1); - pool->head = (common_mpool_hdr_t*)pool->mem; - spin_lock_init(&pool->lock); + nr_pages = i; - for (i=0;imem[pool_entry_size*i]; - ftr = (common_mpool_ftr_t*)((uint8_t*)hdr+sizeof(common_mpool_hdr_t)+aligned_entry_size); - hdr->magic = COMMON_MPOOL_HDR_MAGIC; - hdr->pool = pool; - hdr->flags = 0; - ftr->magic = COMMON_MPOOL_FTR_MAGIC; - ftr->pool = pool; - - if (i < (number_of_entries-1)) { - next_hdr = (common_mpool_hdr_t*)&pool->mem[pool_entry_size*(i+1)]; - } else { - pool->tail = hdr; - next_hdr = NULL; - } - - hdr->next = next_hdr; - } - return pool; -} - -void common_mempool_destroy(common_mempool_t* pool) -{ - if (!pool) { - return; - } - - kfree(pool); -} - -int32_t common_mempool_get_number_of_free_entries(common_mempool_t* pool) -{ - if (!pool) { - return -1; - } - - return (int32_t)pool->number_of_free_entries; -} - -int32_t common_mempool_get_number_of_entries(common_mempool_t* pool) -{ - if (!pool) { - return -1; - } - return (int32_t)pool->number_of_entries; -} - -int32_t common_mempool_get_entry_size(common_mempool_t* pool) -{ - if (!pool) { - return -1; - } - return (int32_t)pool->entry_size; -} - -common_mempool_t* common_mempool_get(void* ptr) -{ - common_mpool_hdr_t* hdr; - common_mpool_ftr_t* ftr; - - if (!ptr) { - return NULL; - } - if (!COMMON_MPOOL_CHECK_ALIGNED4(ptr)) { - return NULL; - } - hdr = (common_mpool_hdr_t*)((uint8_t*)ptr-sizeof(common_mpool_hdr_t)); - ftr = (common_mpool_ftr_t*)((uint8_t*)ptr + hdr->pool->data_size); - - if (hdr->magic != COMMON_MPOOL_HDR_MAGIC) { - printk(KERN_ERR "illegal hdr magic %x for ptr %p",hdr->magic,ptr); - return NULL; - } - if (ftr->magic != COMMON_MPOOL_FTR_MAGIC) { - printk(KERN_ERR "illegal ftr magic %x for ptr %p",ftr->magic,ptr); - return NULL; - } - if (hdr->pool != ftr->pool || !hdr->pool) { - printk(KERN_ERR "inconsistent size hdr->pool: %p ftr->pool: %p for ptr %p",hdr->pool,ftr->pool,ptr); - return false; - } - return hdr->pool; -} -/****************************** POOL MANAGER *************************************/ - -ssize_t generic_splice_from_socket(struct file *file, struct socket *sock, - loff_t __user *ppos, size_t count) -{ - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; - loff_t pos; - int count_tmp; - int err = 0; - int i = 0; - int nr_pages = 0; - int page_cnt_est= count/PAGE_SIZE + 1; - struct recvfile_ctl_blk *rv_cb = NULL; - struct kvec *iov = NULL; - struct msghdr msg; - long rcvtimeo; - int ret; - - if (copy_from_user(&pos, ppos, sizeof(loff_t))) - return -EFAULT; - - if (count > MAX_PAGES_PER_RECVFILE * PAGE_SIZE) { - printk("%s: count(%u) exceeds maxinum\n", __func__, count); - return -EINVAL; - } - mutex_lock(&inode->i_mutex); - - /* - * TODO: Convert to sb_{start/end}_write et. al. - * - * vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); - */ - sb_start_pagefault(inode->i_sb); - - /* We can write back this queue in page reclaim */ - current->backing_dev_info = mapping->backing_dev_info; - - err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); - if (err != 0 || count == 0) - goto done; - - file_remove_suid(file); - file_update_time(file); - - if (unlikely(!rcv_pool || !kvec_pool)) - goto done; - - rv_cb = (struct recvfile_ctl_blk *)common_mempool_alloc(rcv_pool); - iov = (struct kvec *)common_mempool_alloc(kvec_pool); - - if (!rv_cb || !iov) { - printk(KERN_ERR "Failed to get pool mem for %d pages (rv_cb %p iov %p)\n", page_cnt_est, rv_cb, iov); - goto done; - } - - count_tmp = count; - do { - unsigned long bytes; /* Bytes to write to page */ - unsigned long offset; /* Offset into pagecache page */ - struct page *pageP; - void *fsdata; - - offset = (pos & (PAGE_CACHE_SIZE - 1)); - bytes = PAGE_CACHE_SIZE - offset; - if (bytes > count_tmp) - bytes = count_tmp; - ret = mapping->a_ops->write_begin(file, mapping, pos, bytes, - AOP_FLAG_UNINTERRUPTIBLE, - &pageP, &fsdata); - - if (unlikely(ret)) { - err = ret; + /* failed to get page cache */ + if (unlikely(pg_cache_sz == 0)) goto cleanup; - } - rv_cb[nr_pages].rv_page = pageP; - rv_cb[nr_pages].rv_pos = pos; - rv_cb[nr_pages].rv_count = bytes; - rv_cb[nr_pages].rv_fsdata = fsdata; - iov[nr_pages].iov_base = kmap(pageP) + offset; - iov[nr_pages].iov_len = bytes; - nr_pages++; - count_tmp -= bytes; - pos += bytes; - } while (count_tmp); + if (pos + pg_cache_sz < i_size_read(inode)) + append = false; - /* IOV is ready, receive the date from socket now */ - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = (struct iovec *)&iov[0]; - msg.msg_iovlen = nr_pages ; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = MSG_KERNSPACE; - rcvtimeo = sock->sk->sk_rcvtimeo; - sock->sk->sk_rcvtimeo = 8 * HZ; + /* IOV is ready, receive the data from socket now */ + krecvmsg_sz = kernel_recvmsg(sock, &msg, iov, nr_pages, pg_cache_sz, MSG_WAITALL); - ret = kernel_recvmsg(sock, &msg, &iov[0], nr_pages, count, - MSG_WAITALL | MSG_NOCATCHSIG); + /* socket data is ready, write page cache */ + for (i = 0, pg_cache_end_sz = 0; i < nr_pages; i++) { + unsigned to_copy = min_t(unsigned int, rv_cb[i].rv_count, pg_cache_sz - pg_cache_end_sz); - sock->sk->sk_rcvtimeo = rcvtimeo; - if(ret != count) - err = -EPIPE; - else - err = 0; - - if (unlikely(err < 0)) { - goto cleanup; - } - - for(i=0,count=0;i < nr_pages;i++) { kunmap(rv_cb[i].rv_page); - ret = mapping->a_ops->write_end(file, mapping, + ret = pagecache_write_end(file, mapping, rv_cb[i].rv_pos, rv_cb[i].rv_count, - rv_cb[i].rv_count, + to_copy, rv_cb[i].rv_page, rv_cb[i].rv_fsdata); - if (unlikely(ret < 0)) - printk("%s: write_end fail,ret = %d\n", __func__, ret); - count += rv_cb[i].rv_count; + if (unlikely(ret < 0)) { + pr_info("%s: pagecache_write_end fail,ret = %d\n", __func__, ret); + break; } + BUG_ON(ret != rv_cb[i].rv_count); + pg_cache_end_sz += ret; + } + + /* update the actual bytes written */ + if (likely(krecvmsg_sz > 0)) + /* use the least byte size */ + written = min_t(size_t, pg_cache_end_sz, krecvmsg_sz); + + if (likely(written > 0)) { balance_dirty_pages_ratelimited(mapping); - if (copy_to_user(ppos, &pos, sizeof(loff_t))) - err = -EFAULT; -done: - sb_end_pagefault(inode->i_sb); - current->backing_dev_info = NULL; - common_mempool_free(rcv_pool, (void*)rv_cb); - common_mempool_free(kvec_pool, (void*)iov); + pos += written; + fsnotify_modify(file); + if (unlikely((count_req != written) && append)) + truncate_setsize(inode, pos); + + if (copy_to_user(ppos, &pos, sizeof(loff_t))) { + written = 0; + ret = -EFAULT; + } + } +cleanup: mutex_unlock(&inode->i_mutex); - return err ? err : count; -cleanup: - for(i = 0; i < nr_pages; i++) { - kunmap(rv_cb[i].rv_page); - ret = mapping->a_ops->write_end(file, mapping, - rv_cb[i].rv_pos, - rv_cb[i].rv_count, - 0, - rv_cb[i].rv_page, - rv_cb[i].rv_fsdata); - } - sb_end_pagefault(inode->i_sb); + sb_end_intwrite(inode->i_sb); current->backing_dev_info = NULL; - common_mempool_free(rcv_pool, (void*)rv_cb); - common_mempool_free(kvec_pool, (void*)iov); - mutex_unlock(&inode->i_mutex); - return err ? err : count; + kfree(iov); + kfree(rv_cb); + +err: + return written ? written : ret; } /* @@ -2527,22 +2273,3 @@ return error; } -static int __init init_splice_pools(void) -{ - unsigned int rcv_pool_size= sizeof(struct recvfile_ctl_blk) * MAX_PAGES_PER_RECVFILE; - unsigned int kve_pool_size= sizeof(struct kvec) * MAX_PAGES_PER_RECVFILE; - - rcv_pool = common_mempool_create((8 * num_possible_cpus()), rcv_pool_size); - kvec_pool = common_mempool_create((8 * num_possible_cpus()), kve_pool_size); - if (!rcv_pool || !kvec_pool) - { - return -ENOMEM; - } -/* - printk(KERN_ERR "%s rcv %p (sz:%d) kvec %p (sz:%d) per %d core\n", - __FUNCTION__, rcv_pool, rcv_pool_size, kvec_pool, kve_pool_size, num_possible_cpus()); -*/ - return 0; -} - -fs_initcall(init_splice_pools); Index: include/linux/dma-mapping.h =================================================================== --- include/linux/dma-mapping.h (revision 1) +++ include/linux/dma-mapping.h (working copy) @@ -97,6 +97,30 @@ } #endif +/* + * Set both the DMA mask and the coherent DMA mask to the same thing. + * Note that we don't check the return value from dma_set_coherent_mask() + * as the DMA API guarantees that the coherent DMA mask can be set to + * the same or smaller than the streaming DMA mask. + */ +static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask) +{ + int rc = dma_set_mask(dev, mask); + if (rc == 0) + dma_set_coherent_mask(dev, mask); + return rc; +} + +/* + * Similar to the above, except it deals with the case where the device + * does not have dev->dma_mask appropriately setup. + */ +static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask) +{ + dev->dma_mask = &dev->coherent_dma_mask; + return dma_set_mask_and_coherent(dev, mask); +} + extern u64 dma_get_required_mask(struct device *dev); static inline unsigned int dma_get_max_seg_size(struct device *dev) Index: include/linux/mbus.h =================================================================== --- include/linux/mbus.h (revision 1) +++ include/linux/mbus.h (working copy) @@ -61,6 +61,7 @@ } #endif +int mvebu_mbus_save_cpu_target(u32 *store_addr); void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); void mvebu_mbus_get_pcie_io_aperture(struct resource *res); int mvebu_mbus_add_window_remap_by_id(unsigned int target, @@ -74,5 +75,7 @@ size_t mbus_size, phys_addr_t sdram_phys_base, size_t sdram_size); int mvebu_mbus_dt_init(bool is_coherent); +int mvebu_mbus_get_addr_win_info(phys_addr_t phyaddr, u8 *trg_id, u8 *attr); +int mvebu_mbus_win_addr_get(u8 target_id, u8 attribute, u32 *phy_base, u32 *size); #endif /* __LINUX_MBUS_H */ Index: include/linux/miscdevice.h =================================================================== --- include/linux/miscdevice.h (revision 1) +++ include/linux/miscdevice.h (working copy) @@ -21,6 +21,8 @@ /*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */ #define CRYPTODEV_MINOR 70 /* OCF async crypto */ #define CESADEV_MINOR 71 /* marvell CESA */ +#define SLICDEV_MINOR 73 /* Marvell SLIC control device */ +#define TALDEV_MINOR 74 /* Marvell TAL device */ #define WATCHDOG_MINOR 130 /* Watchdog timer */ #define TEMP_MINOR 131 /* Temperature Sensor */ #define RTC_MINOR 135 Index: include/linux/mmc/sdhci.h =================================================================== --- include/linux/mmc/sdhci.h (revision 1) +++ include/linux/mmc/sdhci.h (working copy) @@ -95,6 +95,8 @@ /* The system physically doesn't support 1.8v, even if the host does */ #define SDHCI_QUIRK2_NO_1_8_V (1<<2) #define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3) +/* Do not disable internal clk on power-off */ +#define SDHCI_QUIRK2_KEEP_INT_CLK_ON (1<<4) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ Index: include/linux/mvebu-pmsu.h =================================================================== --- include/linux/mvebu-pmsu.h (nonexistent) +++ include/linux/mvebu-pmsu.h (working copy) @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2012 Marvell + * + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_PMSU_H__ +#define __MVEBU_PMSU_H__ + +#ifdef CONFIG_MACH_MVEBU_V7 +int mvebu_pmsu_dfs_request(int cpu); +#else +static inline int mvebu_pmsu_dfs_request(int cpu) { return -ENODEV; } +#endif + +#endif /* __MVEBU_PMSU_H__ */ Index: include/linux/of.h =================================================================== --- include/linux/of.h (revision 1) +++ include/linux/of.h (working copy) @@ -290,6 +290,9 @@ extern int of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, int index, struct of_phandle_args *out_args); +extern int of_parse_phandle_with_fixed_args(const struct device_node *np, + const char *list_name, int cells_count, int index, + struct of_phandle_args *out_args); extern int of_count_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name); @@ -497,6 +500,13 @@ return -ENOSYS; } +static inline int of_parse_phandle_with_fixed_args(const struct device_node *np, + const char *list_name, int cells_count, int index, + struct of_phandle_args *out_args) +{ + return -ENOSYS; +} + static inline int of_count_phandle_with_args(struct device_node *np, const char *list_name, const char *cells_name) Index: include/linux/platform_data/pxa_sdhci.h =================================================================== --- include/linux/platform_data/pxa_sdhci.h (revision 1) +++ include/linux/platform_data/pxa_sdhci.h (working copy) @@ -59,5 +59,7 @@ struct sdhci_pxa { u8 clk_enable; u8 power_mode; + void __iomem *sdio3_conf_reg; + void __iomem *mbus_win_regs; }; #endif /* _PXA_SDHCI_H_ */ Index: include/linux/skbuff.h =================================================================== --- include/linux/skbuff.h (revision 1) +++ include/linux/skbuff.h (working copy) @@ -446,6 +446,12 @@ __be16 protocol; void (*destructor)(struct sk_buff *skb); +#ifdef CONFIG_NET_SKB_RECYCLE + int (*skb_recycle) (struct sk_buff *skb); + __u32 hw_cookie; +#endif /* CONFIG_NET_SKB_RECYCLE */ + + #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct nf_conntrack *nfct; #endif @@ -650,6 +656,10 @@ } extern struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src); +#ifdef CONFIG_NET_SKB_RECYCLE +extern void skb_recycle(struct sk_buff *skb); +extern bool skb_recycle_check(struct sk_buff *skb, int skb_size); +#endif extern int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask); extern struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority); @@ -2933,5 +2943,30 @@ skb_network_header(skb); return hdr_len + skb_gso_transport_seglen(skb); } +#ifdef CONFIG_NET_SKB_RECYCLE +static inline bool skb_is_recycleable(const struct sk_buff *skb, int skb_size) +{ + if (irqs_disabled()) + return false; + + if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) + return false; + + if (skb_is_nonlinear(skb) || skb->fclone != SKB_FCLONE_UNAVAILABLE) + return false; + + skb_size = SKB_DATA_ALIGN(skb_size + NET_SKB_PAD); + if (skb_end_pointer(skb) - skb->head < skb_size) + return false; + + if (skb_shared(skb) || skb_cloned(skb) || skb_has_frag_list(skb)) + return false; + + if (skb->head_frag) + return false; + + return true; +} +#endif /* CONFIG_NET_SKB_RECYCLE */ #endif /* __KERNEL__ */ #endif /* _LINUX_SKBUFF_H */ Index: include/linux/socket.h =================================================================== --- include/linux/socket.h (revision 1) +++ include/linux/socket.h (working copy) @@ -251,8 +251,6 @@ #define MSG_MORE 0x8000 /* Sender will send more */ #define MSG_WAITFORONE 0x10000 /* recvmmsg(): block until 1+ packets avail */ #define MSG_SENDPAGE_NOTLAST 0x20000 /* sendpage() internal : not the last page */ -#define MSG_KERNSPACE 0x40000 -#define MSG_NOCATCHSIG 0x80000 #define MSG_EOF MSG_FIN #define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */ Index: include/linux/spi/spi.h =================================================================== --- include/linux/spi/spi.h (revision 1) +++ include/linux/spi/spi.h (working copy) @@ -74,7 +74,7 @@ struct spi_master *master; u32 max_speed_hz; u8 chip_select; - u8 mode; + u16 mode; #define SPI_CPHA 0x01 /* clock phase */ #define SPI_CPOL 0x02 /* clock polarity */ #define SPI_MODE_0 (0|0) /* (original MicroWire) */ @@ -87,6 +87,7 @@ #define SPI_LOOP 0x20 /* loopback mode */ #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */ #define SPI_READY 0x80 /* slave pulls low to pause */ +#define SPI_1BYTE_CS 0x100 /* switch CS every byte */ u8 bits_per_word; int irq; void *controller_state; @@ -233,6 +234,8 @@ * suported. If set, the SPI core will reject any transfer with an * unsupported bits_per_word. If not set, this value is simply ignored, * and it's up to the individual driver to perform any validation. + * @min_speed_hz: Lowest supported transfer speed + * @max_speed_hz: Highest supported transfer speed * @flags: other constraints relevant to this driver * @bus_lock_spinlock: spinlock for SPI bus locking * @bus_lock_mutex: mutex for SPI bus locking @@ -309,6 +312,10 @@ /* bitmask of supported bits_per_word for transfers */ u32 bits_per_word_mask; + /* limits on transfer speed */ + u32 min_speed_hz; + u32 max_speed_hz; + /* other constraints relevant to this driver */ u16 flags; #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */ Index: include/sound/pcm_params.h =================================================================== --- include/sound/pcm_params.h (revision 1) +++ include/sound/pcm_params.h (working copy) @@ -354,4 +354,16 @@ params_channels(p)) / 8; } +static inline int +params_width(const struct snd_pcm_hw_params *p) +{ + return snd_pcm_format_width(params_format(p)); +} + +static inline int +params_physical_width(const struct snd_pcm_hw_params *p) +{ + return snd_pcm_format_physical_width(params_format(p)); +} + #endif /* __SOUND_PCM_PARAMS_H */ Index: include/sound/soc.h =================================================================== --- include/sound/soc.h (revision 1) +++ include/sound/soc.h (working copy) @@ -369,6 +369,7 @@ int snd_soc_register_card(struct snd_soc_card *card); int snd_soc_unregister_card(struct snd_soc_card *card); +int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card); int snd_soc_suspend(struct device *dev); int snd_soc_resume(struct device *dev); int snd_soc_poweroff(struct device *dev); @@ -386,6 +387,9 @@ int snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *cmpnt_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); +int devm_snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_component(struct device *dev); int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, unsigned int reg); @@ -1187,6 +1191,20 @@ return 1; } +/** + * snd_soc_kcontrol_codec() - Returns the CODEC that registered the control + * @kcontrol: The control for which to get the CODEC + * + * Note: This function will only work correctly if the control has been + * registered with snd_soc_add_codec_controls() or via table based setup of + * snd_soc_codec_driver. Otherwise the behavior is undefined. + */ +static inline struct snd_soc_codec *snd_soc_kcontrol_codec( + struct snd_kcontrol *kcontrol) +{ + return snd_kcontrol_chip(kcontrol); +} + int snd_soc_util_init(void); void snd_soc_util_exit(void); Index: include/uapi/linux/netfilter/xt_connmark.h =================================================================== --- include/uapi/linux/netfilter/xt_connmark.h (revision 1) +++ include/uapi/linux/netfilter/xt_connmark.h (working copy) @@ -1,31 +1,6 @@ -#ifndef _XT_CONNMARK_H -#define _XT_CONNMARK_H +#ifndef _XT_CONNMARK_H_target +#define _XT_CONNMARK_H_target -#include +#include -/* Copyright (C) 2002,2004 MARA Systems AB - * by Henrik Nordstrom - * - * 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. - */ - -enum { - XT_CONNMARK_SET = 0, - XT_CONNMARK_SAVE, - XT_CONNMARK_RESTORE -}; - -struct xt_connmark_tginfo1 { - __u32 ctmark, ctmask, nfmask; - __u8 mode; -}; - -struct xt_connmark_mtinfo1 { - __u32 mark, mask; - __u8 invert; -}; - -#endif /*_XT_CONNMARK_H*/ +#endif /*_XT_CONNMARK_H_target*/ Index: include/uapi/linux/netfilter/xt_DSCP.h =================================================================== --- include/uapi/linux/netfilter/xt_DSCP.h (revision 1) +++ include/uapi/linux/netfilter/xt_DSCP.h (working copy) @@ -1,26 +1,31 @@ -/* x_tables module for setting the IPv4/IPv6 DSCP field +/* x_tables module for matching the IPv4/IPv6 DSCP field * * (C) 2002 Harald Welte - * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh * This software is distributed under GNU GPL v2, 1991 * * See RFC2474 for a description of the DSCP field within the IP Header. * - * xt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp + * xt_dscp.h,v 1.3 2002/08/05 19:00:21 laforge Exp */ -#ifndef _XT_DSCP_TARGET_H -#define _XT_DSCP_TARGET_H -#include +#ifndef _XT_DSCP_H +#define _XT_DSCP_H + #include -/* target info */ -struct xt_DSCP_info { +#define XT_DSCP_MASK 0xfc /* 11111100 */ +#define XT_DSCP_SHIFT 2 +#define XT_DSCP_MAX 0x3f /* 00111111 */ + +/* match info */ +struct xt_dscp_info { __u8 dscp; + __u8 invert; }; -struct xt_tos_target_info { +struct xt_tos_match_info { + __u8 tos_mask; __u8 tos_value; - __u8 tos_mask; + __u8 invert; }; -#endif /* _XT_DSCP_TARGET_H */ +#endif /* _XT_DSCP_H */ Index: include/uapi/linux/netfilter/xt_mark.h =================================================================== --- include/uapi/linux/netfilter/xt_mark.h (revision 1) +++ include/uapi/linux/netfilter/xt_mark.h (working copy) @@ -1,15 +1,6 @@ -#ifndef _XT_MARK_H -#define _XT_MARK_H +#ifndef _XT_MARK_H_target +#define _XT_MARK_H_target -#include +#include -struct xt_mark_tginfo2 { - __u32 mark, mask; -}; - -struct xt_mark_mtinfo1 { - __u32 mark, mask; - __u8 invert; -}; - -#endif /*_XT_MARK_H*/ +#endif /*_XT_MARK_H_target */ Index: include/uapi/linux/netfilter/xt_rateest.h =================================================================== --- include/uapi/linux/netfilter/xt_rateest.h (revision 1) +++ include/uapi/linux/netfilter/xt_rateest.h (working copy) @@ -1,37 +1,15 @@ -#ifndef _XT_RATEEST_MATCH_H -#define _XT_RATEEST_MATCH_H +#ifndef _XT_RATEEST_TARGET_H +#define _XT_RATEEST_TARGET_H #include -enum xt_rateest_match_flags { - XT_RATEEST_MATCH_INVERT = 1<<0, - XT_RATEEST_MATCH_ABS = 1<<1, - XT_RATEEST_MATCH_REL = 1<<2, - XT_RATEEST_MATCH_DELTA = 1<<3, - XT_RATEEST_MATCH_BPS = 1<<4, - XT_RATEEST_MATCH_PPS = 1<<5, -}; +struct xt_rateest_target_info { + char name[IFNAMSIZ]; + __s8 interval; + __u8 ewma_log; -enum xt_rateest_match_mode { - XT_RATEEST_MATCH_NONE, - XT_RATEEST_MATCH_EQ, - XT_RATEEST_MATCH_LT, - XT_RATEEST_MATCH_GT, -}; - -struct xt_rateest_match_info { - char name1[IFNAMSIZ]; - char name2[IFNAMSIZ]; - __u16 flags; - __u16 mode; - __u32 bps1; - __u32 pps1; - __u32 bps2; - __u32 pps2; - /* Used internally by the kernel */ - struct xt_rateest *est1 __attribute__((aligned(8))); - struct xt_rateest *est2 __attribute__((aligned(8))); + struct xt_rateest *est __attribute__((aligned(8))); }; -#endif /* _XT_RATEEST_MATCH_H */ +#endif /* _XT_RATEEST_TARGET_H */ Index: include/uapi/linux/netfilter/xt_TCPMSS.h =================================================================== --- include/uapi/linux/netfilter/xt_TCPMSS.h (revision 1) +++ include/uapi/linux/netfilter/xt_TCPMSS.h (working copy) @@ -1,12 +1,11 @@ -#ifndef _XT_TCPMSS_H -#define _XT_TCPMSS_H +#ifndef _XT_TCPMSS_MATCH_H +#define _XT_TCPMSS_MATCH_H #include -struct xt_tcpmss_info { - __u16 mss; +struct xt_tcpmss_match_info { + __u16 mss_min, mss_max; + __u8 invert; }; -#define XT_TCPMSS_CLAMP_PMTU 0xffff - -#endif /* _XT_TCPMSS_H */ +#endif /*_XT_TCPMSS_MATCH_H*/ Index: include/uapi/linux/netfilter_ipv4/ipt_ECN.h =================================================================== --- include/uapi/linux/netfilter_ipv4/ipt_ECN.h (revision 1) +++ include/uapi/linux/netfilter_ipv4/ipt_ECN.h (working copy) @@ -1,33 +1,15 @@ -/* Header file for iptables ipt_ECN target - * - * (C) 2002 by Harald Welte - * - * This software is distributed under GNU GPL v2, 1991 - * - * ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp -*/ -#ifndef _IPT_ECN_TARGET_H -#define _IPT_ECN_TARGET_H +#ifndef _IPT_ECN_H +#define _IPT_ECN_H -#include -#include +#include +#define ipt_ecn_info xt_ecn_info -#define IPT_ECN_IP_MASK (~XT_DSCP_MASK) - -#define IPT_ECN_OP_SET_IP 0x01 /* set ECN bits of IPv4 header */ -#define IPT_ECN_OP_SET_ECE 0x10 /* set ECE bit of TCP header */ -#define IPT_ECN_OP_SET_CWR 0x20 /* set CWR bit of TCP header */ - -#define IPT_ECN_OP_MASK 0xce - -struct ipt_ECN_info { - __u8 operation; /* bitset of operations */ - __u8 ip_ect; /* ECT codepoint of IPv4 header, pre-shifted */ - union { - struct { - __u8 ece:1, cwr:1; /* TCP ECT bits */ - } tcp; - } proto; +enum { + IPT_ECN_IP_MASK = XT_ECN_IP_MASK, + IPT_ECN_OP_MATCH_IP = XT_ECN_OP_MATCH_IP, + IPT_ECN_OP_MATCH_ECE = XT_ECN_OP_MATCH_ECE, + IPT_ECN_OP_MATCH_CWR = XT_ECN_OP_MATCH_CWR, + IPT_ECN_OP_MATCH_MASK = XT_ECN_OP_MATCH_MASK, }; -#endif /* _IPT_ECN_TARGET_H */ +#endif /* IPT_ECN_H */ Index: include/uapi/linux/netfilter_ipv4/ipt_ttl.h =================================================================== --- include/uapi/linux/netfilter_ipv4/ipt_ttl.h (revision 1) +++ include/uapi/linux/netfilter_ipv4/ipt_ttl.h (working copy) @@ -1,5 +1,5 @@ -/* IP tables module for matching the value of the TTL - * (C) 2000 by Harald Welte */ +/* TTL modification module for IP tables + * (C) 2000 by Harald Welte */ #ifndef _IPT_TTL_H #define _IPT_TTL_H @@ -7,14 +7,14 @@ #include enum { - IPT_TTL_EQ = 0, /* equals */ - IPT_TTL_NE, /* not equals */ - IPT_TTL_LT, /* less than */ - IPT_TTL_GT, /* greater than */ + IPT_TTL_SET = 0, + IPT_TTL_INC, + IPT_TTL_DEC }; +#define IPT_TTL_MAXMODE IPT_TTL_DEC -struct ipt_ttl_info { +struct ipt_TTL_info { __u8 mode; __u8 ttl; }; Index: include/uapi/linux/netfilter_ipv6/ip6t_HL.h =================================================================== --- include/uapi/linux/netfilter_ipv6/ip6t_HL.h (revision 1) +++ include/uapi/linux/netfilter_ipv6/ip6t_HL.h (working copy) @@ -1,6 +1,6 @@ -/* Hop Limit modification module for ip6tables +/* ip6tables module for matching the Hop Limit value * Maciej Soltysiak - * Based on HW's TTL module */ + * Based on HW's ttl module */ #ifndef _IP6T_HL_H #define _IP6T_HL_H @@ -8,14 +8,14 @@ #include enum { - IP6T_HL_SET = 0, - IP6T_HL_INC, - IP6T_HL_DEC + IP6T_HL_EQ = 0, /* equals */ + IP6T_HL_NE, /* not equals */ + IP6T_HL_LT, /* less than */ + IP6T_HL_GT, /* greater than */ }; -#define IP6T_HL_MAXMODE IP6T_HL_DEC -struct ip6t_HL_info { +struct ip6t_hl_info { __u8 mode; __u8 hop_limit; }; Index: net/core/dev.c =================================================================== --- net/core/dev.c (revision 1) +++ net/core/dev.c (working copy) @@ -3851,8 +3851,17 @@ break; case GRO_MERGED_FREE: - if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD) + if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD) { +#ifdef CONFIG_NET_SKB_RECYCLE + /* Workaround for the cases when recycle callback was not called */ + if (skb->skb_recycle) { + /* Sign that skb is not available for recycle */ + skb->hw_cookie |= BIT(0); + skb->skb_recycle(skb); + } +#endif /* CONFIG_NET_SKB_RECYCLE */ kmem_cache_free(skbuff_head_cache, skb); + } else __kfree_skb(skb); break; Index: net/core/skbuff.c =================================================================== --- net/core/skbuff.c (revision 1) +++ net/core/skbuff.c (working copy) @@ -507,8 +507,16 @@ skb_drop_fraglist(skb); skb_free_head(skb); +#ifdef CONFIG_NET_SKB_RECYCLE + /* Workaround for the cases when recycle callback was not called */ + if (skb->skb_recycle) { + /* Sign that skb is not available for recycle */ + skb->hw_cookie |= BIT(0); + skb->skb_recycle(skb); } +#endif /* CONFIG_NET_SKB_RECYCLE */ } +} /* * Free an skbuff by memory without cleaning the state. @@ -588,6 +596,10 @@ void __kfree_skb(struct sk_buff *skb) { +#ifdef CONFIG_NET_SKB_RECYCLE + if (skb->skb_recycle && !skb->skb_recycle(skb)) + return; +#endif /* CONFIG_NET_SKB_RECYCLE */ skb_release_all(skb); kfree_skbmem(skb); } @@ -665,6 +677,55 @@ } EXPORT_SYMBOL(consume_skb); +#ifdef CONFIG_NET_SKB_RECYCLE +/** + * skb_recycle - clean up an skb for reuse + * @skb: buffer + * + * Recycles the skb to be reused as a receive buffer. This + * function does any necessary reference count dropping, and + * cleans up the skbuff as if it just came from __alloc_skb(). + */ +void skb_recycle(struct sk_buff *skb) +{ + struct skb_shared_info *shinfo; + + skb_release_head_state(skb); + + shinfo = skb_shinfo(skb); + memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); + atomic_set(&shinfo->dataref, 1); + + memset(skb, 0, offsetof(struct sk_buff, tail)); + skb->data = skb->head + NET_SKB_PAD; + skb_reset_tail_pointer(skb); +} +EXPORT_SYMBOL(skb_recycle); + +/** + * skb_recycle_check - check if skb can be reused for receive + * @skb: buffer + * @skb_size: minimum receive buffer size + * + * Checks that the skb passed in is not shared or cloned, and + * that it is linear and its head portion at least as large as + * skb_size so that it can be recycled as a receive buffer. + * If these conditions are met, this function does any necessary + * reference count dropping and cleans up the skbuff as if it + * just came from __alloc_skb(). + */ +bool skb_recycle_check(struct sk_buff *skb, int skb_size) +{ + if (!skb_is_recycleable(skb, skb_size)) + return false; + + skb_recycle(skb); + + return true; +} +EXPORT_SYMBOL(skb_recycle_check); +#endif /* CONFIG_NET_SKB_RECYCLE */ + static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) { new->tstamp = old->tstamp; @@ -733,6 +794,11 @@ n->cloned = 1; n->nohdr = 0; n->destructor = NULL; +#ifdef CONFIG_NET_SKB_RECYCLE + n->skb_recycle = NULL; + n->hw_cookie = 0; +#endif /* CONFIG_NET_SKB_RECYCLE */ + C(tail); C(end); C(head); @@ -3390,6 +3456,15 @@ void kfree_skb_partial(struct sk_buff *skb, bool head_stolen) { if (head_stolen) { +#ifdef CONFIG_NET_SKB_RECYCLE + /* Workaround for the cases when recycle callback was not called */ + if (skb->skb_recycle) { + /* Sign that skb is not available for recycle */ + skb->hw_cookie |= BIT(0); + skb->skb_recycle(skb); + } +#endif /* CONFIG_NET_SKB_RECYCLE */ + skb_release_head_state(skb); kmem_cache_free(skbuff_head_cache, skb); } else { Index: net/ipv4/tcp.c =================================================================== --- net/ipv4/tcp.c (revision 1) +++ net/ipv4/tcp.c (working copy) @@ -1648,23 +1648,8 @@ do { u32 offset; - if (flags & MSG_NOCATCHSIG) { - if (signal_pending(current)) { - if (sigismember(¤t->pending.signal, SIGQUIT) || - sigismember(¤t->pending.signal, SIGABRT) || - sigismember(¤t->pending.signal, SIGKILL) || - sigismember(¤t->pending.signal, SIGTERM) || - sigismember(¤t->pending.signal, SIGSTOP)) { - - if (copied) - break; - copied = timeo ? sock_intr_errno(timeo) : -EAGAIN; - break; - } - } - } /* Are we at urgent data? Stop if we have read anything or have SIGURG pending. */ - else if (tp->urg_data && tp->urg_seq == *seq) { + if (tp->urg_data && tp->urg_seq == *seq) { if (copied) break; if (signal_pending(current)) { Index: net/netfilter/xt_dscp.c =================================================================== --- net/netfilter/xt_dscp.c (revision 1) +++ net/netfilter/xt_dscp.c (working copy) @@ -1,10 +1,13 @@ -/* IP tables module for matching the value of the IPv4/IPv6 DSCP field +/* x_tables module for setting the IPv4/IPv6 DSCP field, Version 1.8 * * (C) 2002 by Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh * * 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. + * + * See RFC2474 for a description of the DSCP field within the IP Header. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include @@ -14,102 +17,148 @@ #include #include -#include +#include MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Xtables: DSCP/TOS field match"); +MODULE_DESCRIPTION("Xtables: DSCP/TOS field modification"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("ipt_dscp"); -MODULE_ALIAS("ip6t_dscp"); -MODULE_ALIAS("ipt_tos"); -MODULE_ALIAS("ip6t_tos"); +MODULE_ALIAS("ipt_DSCP"); +MODULE_ALIAS("ip6t_DSCP"); +MODULE_ALIAS("ipt_TOS"); +MODULE_ALIAS("ip6t_TOS"); -static bool -dscp_mt(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +dscp_tg(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_dscp_info *info = par->matchinfo; + const struct xt_DSCP_info *dinfo = par->targinfo; u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; - return (dscp == info->dscp) ^ !!info->invert; + if (dscp != dinfo->dscp) { + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + + ipv4_change_dsfield(ip_hdr(skb), (__u8)(~XT_DSCP_MASK), + dinfo->dscp << XT_DSCP_SHIFT); + } + return XT_CONTINUE; +} -static bool -dscp_mt6(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +dscp_tg6(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_dscp_info *info = par->matchinfo; + const struct xt_DSCP_info *dinfo = par->targinfo; u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; - return (dscp == info->dscp) ^ !!info->invert; + if (dscp != dinfo->dscp) { + if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) + return NF_DROP; + + ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~XT_DSCP_MASK), + dinfo->dscp << XT_DSCP_SHIFT); } + return XT_CONTINUE; +} -static int dscp_mt_check(const struct xt_mtchk_param *par) +static int dscp_tg_check(const struct xt_tgchk_param *par) { - const struct xt_dscp_info *info = par->matchinfo; + const struct xt_DSCP_info *info = par->targinfo; if (info->dscp > XT_DSCP_MAX) { pr_info("dscp %x out of range\n", info->dscp); return -EDOM; } - return 0; } -static bool tos_mt(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +tos_tg(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_tos_match_info *info = par->matchinfo; + const struct xt_tos_target_info *info = par->targinfo; + struct iphdr *iph = ip_hdr(skb); + u_int8_t orig, nv; - if (par->family == NFPROTO_IPV4) - return ((ip_hdr(skb)->tos & info->tos_mask) == - info->tos_value) ^ !!info->invert; - else - return ((ipv6_get_dsfield(ipv6_hdr(skb)) & info->tos_mask) == - info->tos_value) ^ !!info->invert; + orig = ipv4_get_dsfield(iph); + nv = (orig & ~info->tos_mask) ^ info->tos_value; + + if (orig != nv) { + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + iph = ip_hdr(skb); + ipv4_change_dsfield(iph, 0, nv); } -static struct xt_match dscp_mt_reg[] __read_mostly = { + return XT_CONTINUE; +} + +static unsigned int +tos_tg6(struct sk_buff *skb, const struct xt_action_param *par) { - .name = "dscp", + const struct xt_tos_target_info *info = par->targinfo; + struct ipv6hdr *iph = ipv6_hdr(skb); + u_int8_t orig, nv; + + orig = ipv6_get_dsfield(iph); + nv = (orig & ~info->tos_mask) ^ info->tos_value; + + if (orig != nv) { + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + iph = ipv6_hdr(skb); + ipv6_change_dsfield(iph, 0, nv); + } + + return XT_CONTINUE; +} + +static struct xt_target dscp_tg_reg[] __read_mostly = { + { + .name = "DSCP", .family = NFPROTO_IPV4, - .checkentry = dscp_mt_check, - .match = dscp_mt, - .matchsize = sizeof(struct xt_dscp_info), + .checkentry = dscp_tg_check, + .target = dscp_tg, + .targetsize = sizeof(struct xt_DSCP_info), + .table = "mangle", .me = THIS_MODULE, }, { - .name = "dscp", + .name = "DSCP", .family = NFPROTO_IPV6, - .checkentry = dscp_mt_check, - .match = dscp_mt6, - .matchsize = sizeof(struct xt_dscp_info), + .checkentry = dscp_tg_check, + .target = dscp_tg6, + .targetsize = sizeof(struct xt_DSCP_info), + .table = "mangle", .me = THIS_MODULE, }, { - .name = "tos", + .name = "TOS", .revision = 1, .family = NFPROTO_IPV4, - .match = tos_mt, - .matchsize = sizeof(struct xt_tos_match_info), + .table = "mangle", + .target = tos_tg, + .targetsize = sizeof(struct xt_tos_target_info), .me = THIS_MODULE, }, { - .name = "tos", + .name = "TOS", .revision = 1, .family = NFPROTO_IPV6, - .match = tos_mt, - .matchsize = sizeof(struct xt_tos_match_info), + .table = "mangle", + .target = tos_tg6, + .targetsize = sizeof(struct xt_tos_target_info), .me = THIS_MODULE, }, }; -static int __init dscp_mt_init(void) +static int __init dscp_tg_init(void) { - return xt_register_matches(dscp_mt_reg, ARRAY_SIZE(dscp_mt_reg)); + return xt_register_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); } -static void __exit dscp_mt_exit(void) +static void __exit dscp_tg_exit(void) { - xt_unregister_matches(dscp_mt_reg, ARRAY_SIZE(dscp_mt_reg)); + xt_unregister_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); } -module_init(dscp_mt_init); -module_exit(dscp_mt_exit); +module_init(dscp_tg_init); +module_exit(dscp_tg_exit); Index: net/netfilter/xt_hl.c =================================================================== --- net/netfilter/xt_hl.c (revision 1) +++ net/netfilter/xt_hl.c (working copy) @@ -1,96 +1,169 @@ /* - * IP tables module for matching the value of the TTL - * (C) 2000,2001 by Harald Welte + * TTL modification target for IP tables + * (C) 2000,2005 by Harald Welte * - * Hop Limit matching module - * (C) 2001-2002 Maciej Soltysiak + * Hop Limit modification target for ip6tables + * Maciej Soltysiak * * 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. */ - +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include #include #include -#include -#include +#include #include -#include -#include +#include +#include +MODULE_AUTHOR("Harald Welte "); MODULE_AUTHOR("Maciej Soltysiak "); -MODULE_DESCRIPTION("Xtables: Hoplimit/TTL field match"); +MODULE_DESCRIPTION("Xtables: Hoplimit/TTL Limit field modification target"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("ipt_ttl"); -MODULE_ALIAS("ip6t_hl"); -static bool ttl_mt(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +ttl_tg(struct sk_buff *skb, const struct xt_action_param *par) { - const struct ipt_ttl_info *info = par->matchinfo; - const u8 ttl = ip_hdr(skb)->ttl; + struct iphdr *iph; + const struct ipt_TTL_info *info = par->targinfo; + int new_ttl; + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + iph = ip_hdr(skb); + switch (info->mode) { - case IPT_TTL_EQ: - return ttl == info->ttl; - case IPT_TTL_NE: - return ttl != info->ttl; - case IPT_TTL_LT: - return ttl < info->ttl; - case IPT_TTL_GT: - return ttl > info->ttl; + case IPT_TTL_SET: + new_ttl = info->ttl; + break; + case IPT_TTL_INC: + new_ttl = iph->ttl + info->ttl; + if (new_ttl > 255) + new_ttl = 255; + break; + case IPT_TTL_DEC: + new_ttl = iph->ttl - info->ttl; + if (new_ttl < 0) + new_ttl = 0; + break; + default: + new_ttl = iph->ttl; + break; } - return false; + if (new_ttl != iph->ttl) { + csum_replace2(&iph->check, htons(iph->ttl << 8), + htons(new_ttl << 8)); + iph->ttl = new_ttl; } -static bool hl_mt6(const struct sk_buff *skb, struct xt_action_param *par) + return XT_CONTINUE; +} + +static unsigned int +hl_tg6(struct sk_buff *skb, const struct xt_action_param *par) { - const struct ip6t_hl_info *info = par->matchinfo; - const struct ipv6hdr *ip6h = ipv6_hdr(skb); + struct ipv6hdr *ip6h; + const struct ip6t_HL_info *info = par->targinfo; + int new_hl; + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + ip6h = ipv6_hdr(skb); + switch (info->mode) { - case IP6T_HL_EQ: - return ip6h->hop_limit == info->hop_limit; - case IP6T_HL_NE: - return ip6h->hop_limit != info->hop_limit; - case IP6T_HL_LT: - return ip6h->hop_limit < info->hop_limit; - case IP6T_HL_GT: - return ip6h->hop_limit > info->hop_limit; + case IP6T_HL_SET: + new_hl = info->hop_limit; + break; + case IP6T_HL_INC: + new_hl = ip6h->hop_limit + info->hop_limit; + if (new_hl > 255) + new_hl = 255; + break; + case IP6T_HL_DEC: + new_hl = ip6h->hop_limit - info->hop_limit; + if (new_hl < 0) + new_hl = 0; + break; + default: + new_hl = ip6h->hop_limit; + break; } - return false; + ip6h->hop_limit = new_hl; + + return XT_CONTINUE; } -static struct xt_match hl_mt_reg[] __read_mostly = { +static int ttl_tg_check(const struct xt_tgchk_param *par) { - .name = "ttl", + const struct ipt_TTL_info *info = par->targinfo; + + if (info->mode > IPT_TTL_MAXMODE) { + pr_info("TTL: invalid or unknown mode %u\n", info->mode); + return -EINVAL; + } + if (info->mode != IPT_TTL_SET && info->ttl == 0) + return -EINVAL; + return 0; +} + +static int hl_tg6_check(const struct xt_tgchk_param *par) +{ + const struct ip6t_HL_info *info = par->targinfo; + + if (info->mode > IP6T_HL_MAXMODE) { + pr_info("invalid or unknown mode %u\n", info->mode); + return -EINVAL; + } + if (info->mode != IP6T_HL_SET && info->hop_limit == 0) { + pr_info("increment/decrement does not " + "make sense with value 0\n"); + return -EINVAL; + } + return 0; +} + +static struct xt_target hl_tg_reg[] __read_mostly = { + { + .name = "TTL", .revision = 0, .family = NFPROTO_IPV4, - .match = ttl_mt, - .matchsize = sizeof(struct ipt_ttl_info), + .target = ttl_tg, + .targetsize = sizeof(struct ipt_TTL_info), + .table = "mangle", + .checkentry = ttl_tg_check, .me = THIS_MODULE, }, { - .name = "hl", + .name = "HL", .revision = 0, .family = NFPROTO_IPV6, - .match = hl_mt6, - .matchsize = sizeof(struct ip6t_hl_info), + .target = hl_tg6, + .targetsize = sizeof(struct ip6t_HL_info), + .table = "mangle", + .checkentry = hl_tg6_check, .me = THIS_MODULE, }, }; -static int __init hl_mt_init(void) +static int __init hl_tg_init(void) { - return xt_register_matches(hl_mt_reg, ARRAY_SIZE(hl_mt_reg)); + return xt_register_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg)); } -static void __exit hl_mt_exit(void) +static void __exit hl_tg_exit(void) { - xt_unregister_matches(hl_mt_reg, ARRAY_SIZE(hl_mt_reg)); + xt_unregister_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg)); } -module_init(hl_mt_init); -module_exit(hl_mt_exit); +module_init(hl_tg_init); +module_exit(hl_tg_exit); +MODULE_ALIAS("ipt_TTL"); +MODULE_ALIAS("ip6t_HL"); Index: net/netfilter/xt_rateest.c =================================================================== --- net/netfilter/xt_rateest.c (revision 1) +++ net/netfilter/xt_rateest.c (working copy) @@ -8,150 +8,187 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include -#include +#include #include +static DEFINE_MUTEX(xt_rateest_mutex); -static bool -xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) +#define RATEEST_HSIZE 16 +static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly; +static unsigned int jhash_rnd __read_mostly; +static bool rnd_inited __read_mostly; + +static unsigned int xt_rateest_hash(const char *name) { - const struct xt_rateest_match_info *info = par->matchinfo; - struct gnet_stats_rate_est *r; - u_int32_t bps1, bps2, pps1, pps2; - bool ret = true; + return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) & + (RATEEST_HSIZE - 1); +} - spin_lock_bh(&info->est1->lock); - r = &info->est1->rstats; - if (info->flags & XT_RATEEST_MATCH_DELTA) { - bps1 = info->bps1 >= r->bps ? info->bps1 - r->bps : 0; - pps1 = info->pps1 >= r->pps ? info->pps1 - r->pps : 0; - } else { - bps1 = r->bps; - pps1 = r->pps; +static void xt_rateest_hash_insert(struct xt_rateest *est) +{ + unsigned int h; + + h = xt_rateest_hash(est->name); + hlist_add_head(&est->list, &rateest_hash[h]); } - spin_unlock_bh(&info->est1->lock); - if (info->flags & XT_RATEEST_MATCH_ABS) { - bps2 = info->bps2; - pps2 = info->pps2; - } else { - spin_lock_bh(&info->est2->lock); - r = &info->est2->rstats; - if (info->flags & XT_RATEEST_MATCH_DELTA) { - bps2 = info->bps2 >= r->bps ? info->bps2 - r->bps : 0; - pps2 = info->pps2 >= r->pps ? info->pps2 - r->pps : 0; - } else { - bps2 = r->bps; - pps2 = r->pps; +struct xt_rateest *xt_rateest_lookup(const char *name) +{ + struct xt_rateest *est; + unsigned int h; + + h = xt_rateest_hash(name); + mutex_lock(&xt_rateest_mutex); + hlist_for_each_entry(est, &rateest_hash[h], list) { + if (strcmp(est->name, name) == 0) { + est->refcnt++; + mutex_unlock(&xt_rateest_mutex); + return est; } - spin_unlock_bh(&info->est2->lock); } - - switch (info->mode) { - case XT_RATEEST_MATCH_LT: - if (info->flags & XT_RATEEST_MATCH_BPS) - ret &= bps1 < bps2; - if (info->flags & XT_RATEEST_MATCH_PPS) - ret &= pps1 < pps2; - break; - case XT_RATEEST_MATCH_GT: - if (info->flags & XT_RATEEST_MATCH_BPS) - ret &= bps1 > bps2; - if (info->flags & XT_RATEEST_MATCH_PPS) - ret &= pps1 > pps2; - break; - case XT_RATEEST_MATCH_EQ: - if (info->flags & XT_RATEEST_MATCH_BPS) - ret &= bps1 == bps2; - if (info->flags & XT_RATEEST_MATCH_PPS) - ret &= pps1 == pps2; - break; + mutex_unlock(&xt_rateest_mutex); + return NULL; } +EXPORT_SYMBOL_GPL(xt_rateest_lookup); - ret ^= info->flags & XT_RATEEST_MATCH_INVERT ? true : false; - return ret; +void xt_rateest_put(struct xt_rateest *est) +{ + mutex_lock(&xt_rateest_mutex); + if (--est->refcnt == 0) { + hlist_del(&est->list); + gen_kill_estimator(&est->bstats, &est->rstats); + /* + * gen_estimator est_timer() might access est->lock or bstats, + * wait a RCU grace period before freeing 'est' + */ + kfree_rcu(est, rcu); } + mutex_unlock(&xt_rateest_mutex); +} +EXPORT_SYMBOL_GPL(xt_rateest_put); -static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) +static unsigned int +xt_rateest_tg(struct sk_buff *skb, const struct xt_action_param *par) { - struct xt_rateest_match_info *info = par->matchinfo; - struct xt_rateest *est1, *est2; - int ret = -EINVAL; + const struct xt_rateest_target_info *info = par->targinfo; + struct gnet_stats_basic_packed *stats = &info->est->bstats; - if (hweight32(info->flags & (XT_RATEEST_MATCH_ABS | - XT_RATEEST_MATCH_REL)) != 1) - goto err1; + spin_lock_bh(&info->est->lock); + stats->bytes += skb->len; + stats->packets++; + spin_unlock_bh(&info->est->lock); - if (!(info->flags & (XT_RATEEST_MATCH_BPS | XT_RATEEST_MATCH_PPS))) - goto err1; + return XT_CONTINUE; +} - switch (info->mode) { - case XT_RATEEST_MATCH_EQ: - case XT_RATEEST_MATCH_LT: - case XT_RATEEST_MATCH_GT: - break; - default: - goto err1; +static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) +{ + struct xt_rateest_target_info *info = par->targinfo; + struct xt_rateest *est; + struct { + struct nlattr opt; + struct gnet_estimator est; + } cfg; + int ret; + + if (unlikely(!rnd_inited)) { + get_random_bytes(&jhash_rnd, sizeof(jhash_rnd)); + rnd_inited = true; } - ret = -ENOENT; - est1 = xt_rateest_lookup(info->name1); - if (!est1) + est = xt_rateest_lookup(info->name); + if (est) { + /* + * If estimator parameters are specified, they must match the + * existing estimator. + */ + if ((!info->interval && !info->ewma_log) || + (info->interval != est->params.interval || + info->ewma_log != est->params.ewma_log)) { + xt_rateest_put(est); + return -EINVAL; + } + info->est = est; + return 0; + } + + ret = -ENOMEM; + est = kzalloc(sizeof(*est), GFP_KERNEL); + if (!est) goto err1; - est2 = NULL; - if (info->flags & XT_RATEEST_MATCH_REL) { - est2 = xt_rateest_lookup(info->name2); - if (!est2) + strlcpy(est->name, info->name, sizeof(est->name)); + spin_lock_init(&est->lock); + est->refcnt = 1; + est->params.interval = info->interval; + est->params.ewma_log = info->ewma_log; + + cfg.opt.nla_len = nla_attr_size(sizeof(cfg.est)); + cfg.opt.nla_type = TCA_STATS_RATE_EST; + cfg.est.interval = info->interval; + cfg.est.ewma_log = info->ewma_log; + + ret = gen_new_estimator(&est->bstats, &est->rstats, + &est->lock, &cfg.opt); + if (ret < 0) goto err2; - } - info->est1 = est1; - info->est2 = est2; + info->est = est; + xt_rateest_hash_insert(est); return 0; err2: - xt_rateest_put(est1); + kfree(est); err1: return ret; } -static void xt_rateest_mt_destroy(const struct xt_mtdtor_param *par) +static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par) { - struct xt_rateest_match_info *info = par->matchinfo; + struct xt_rateest_target_info *info = par->targinfo; - xt_rateest_put(info->est1); - if (info->est2) - xt_rateest_put(info->est2); + xt_rateest_put(info->est); } -static struct xt_match xt_rateest_mt_reg __read_mostly = { - .name = "rateest", +static struct xt_target xt_rateest_tg_reg __read_mostly = { + .name = "RATEEST", .revision = 0, .family = NFPROTO_UNSPEC, - .match = xt_rateest_mt, - .checkentry = xt_rateest_mt_checkentry, - .destroy = xt_rateest_mt_destroy, - .matchsize = sizeof(struct xt_rateest_match_info), + .target = xt_rateest_tg, + .checkentry = xt_rateest_tg_checkentry, + .destroy = xt_rateest_tg_destroy, + .targetsize = sizeof(struct xt_rateest_target_info), .me = THIS_MODULE, }; -static int __init xt_rateest_mt_init(void) +static int __init xt_rateest_tg_init(void) { - return xt_register_match(&xt_rateest_mt_reg); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rateest_hash); i++) + INIT_HLIST_HEAD(&rateest_hash[i]); + + return xt_register_target(&xt_rateest_tg_reg); } -static void __exit xt_rateest_mt_fini(void) +static void __exit xt_rateest_tg_fini(void) { - xt_unregister_match(&xt_rateest_mt_reg); + xt_unregister_target(&xt_rateest_tg_reg); } + MODULE_AUTHOR("Patrick McHardy "); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("xtables rate estimator match"); -MODULE_ALIAS("ipt_rateest"); -MODULE_ALIAS("ip6t_rateest"); -module_init(xt_rateest_mt_init); -module_exit(xt_rateest_mt_fini); +MODULE_DESCRIPTION("Xtables: packet rate estimator"); +MODULE_ALIAS("ipt_RATEEST"); +MODULE_ALIAS("ip6t_RATEEST"); +module_init(xt_rateest_tg_init); +module_exit(xt_rateest_tg_fini); Index: net/netfilter/xt_tcpmss.c =================================================================== --- net/netfilter/xt_tcpmss.c (revision 1) +++ net/netfilter/xt_tcpmss.c (working copy) @@ -1,110 +1,336 @@ -/* Kernel module to match TCP MSS values. */ - -/* Copyright (C) 2000 Marc Boucher - * Portions (C) 2005 by Harald Welte +/* + * This is a module which is used for setting the MSS option in TCP packets. * + * Copyright (C) 2000 Marc Boucher + * Copyright (C) 2007 Patrick McHardy + * * 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. */ - +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include - #include #include +#include +#include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher "); -MODULE_DESCRIPTION("Xtables: TCP MSS match"); -MODULE_ALIAS("ipt_tcpmss"); -MODULE_ALIAS("ip6t_tcpmss"); +MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment"); +MODULE_ALIAS("ipt_TCPMSS"); +MODULE_ALIAS("ip6t_TCPMSS"); -static bool -tcpmss_mt(const struct sk_buff *skb, struct xt_action_param *par) +static inline unsigned int +optlen(const u_int8_t *opt, unsigned int offset) { - const struct xt_tcpmss_match_info *info = par->matchinfo; - const struct tcphdr *th; - struct tcphdr _tcph; - /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ - const u_int8_t *op; - u8 _opt[15 * 4 - sizeof(_tcph)]; - unsigned int i, optlen; + /* Beware zero-length options: make finite progress */ + if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) + return 1; + else + return opt[offset+1]; +} - /* If we don't have the whole header, drop packet. */ - th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); - if (th == NULL) - goto dropit; +static int +tcpmss_mangle_packet(struct sk_buff *skb, + const struct xt_action_param *par, + unsigned int in_mtu, + unsigned int tcphoff, + unsigned int minlen) +{ + const struct xt_tcpmss_info *info = par->targinfo; + struct tcphdr *tcph; + unsigned int tcplen, i; + __be16 oldval; + u16 newmss; + u8 *opt; - /* Malformed. */ - if (th->doff*4 < sizeof(*th)) - goto dropit; + /* This is a fragment, no TCP header is available */ + if (par->fragoff != 0) + return XT_CONTINUE; - optlen = th->doff*4 - sizeof(*th); - if (!optlen) - goto out; + if (!skb_make_writable(skb, skb->len)) + return -1; - /* Truncated options. */ - op = skb_header_pointer(skb, par->thoff + sizeof(*th), optlen, _opt); - if (op == NULL) - goto dropit; + tcplen = skb->len - tcphoff; + tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); - for (i = 0; i < optlen; ) { - if (op[i] == TCPOPT_MSS - && (optlen - i) >= TCPOLEN_MSS - && op[i+1] == TCPOLEN_MSS) { - u_int16_t mssval; + /* Header cannot be larger than the packet */ + if (tcplen < tcph->doff*4) + return -1; - mssval = (op[i+2] << 8) | op[i+3]; + if (info->mss == XT_TCPMSS_CLAMP_PMTU) { + if (dst_mtu(skb_dst(skb)) <= minlen) { + net_err_ratelimited("unknown or invalid path-MTU (%u)\n", + dst_mtu(skb_dst(skb))); + return -1; + } + if (in_mtu <= minlen) { + net_err_ratelimited("unknown or invalid path-MTU (%u)\n", + in_mtu); + return -1; + } + newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen; + } else + newmss = info->mss; - return (mssval >= info->mss_min && - mssval <= info->mss_max) ^ info->invert; + opt = (u_int8_t *)tcph; + for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { + if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && + opt[i+1] == TCPOLEN_MSS) { + u_int16_t oldmss; + + oldmss = (opt[i+2] << 8) | opt[i+3]; + + /* Never increase MSS, even when setting it, as + * doing so results in problems for hosts that rely + * on MSS being set correctly. + */ + if (oldmss <= newmss) + return 0; + + opt[i+2] = (newmss & 0xff00) >> 8; + opt[i+3] = newmss & 0x00ff; + + inet_proto_csum_replace2(&tcph->check, skb, + htons(oldmss), htons(newmss), + 0); + return 0; } - if (op[i] < 2) - i++; + } + + /* There is data after the header so the option can't be added + without moving it, and doing so may make the SYN packet + itself too large. Accept the packet unmodified instead. */ + if (tcplen > tcph->doff*4) + return 0; + + /* + * MSS Option not found ?! add it.. + */ + if (skb_tailroom(skb) < TCPOLEN_MSS) { + if (pskb_expand_head(skb, 0, + TCPOLEN_MSS - skb_tailroom(skb), + GFP_ATOMIC)) + return -1; + tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); + } + + skb_put(skb, TCPOLEN_MSS); + + /* + * IPv4: RFC 1122 states "If an MSS option is not received at + * connection setup, TCP MUST assume a default send MSS of 536". + * IPv6: RFC 2460 states IPv6 has a minimum MTU of 1280 and a minimum + * length IPv6 header of 60, ergo the default MSS value is 1220 + * Since no MSS was provided, we must use the default values + */ + if (par->family == NFPROTO_IPV4) + newmss = min(newmss, (u16)536); else - i += op[i+1] ? : 1; + newmss = min(newmss, (u16)1220); + + opt = (u_int8_t *)tcph + sizeof(struct tcphdr); + memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); + + inet_proto_csum_replace2(&tcph->check, skb, + htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); + opt[0] = TCPOPT_MSS; + opt[1] = TCPOLEN_MSS; + opt[2] = (newmss & 0xff00) >> 8; + opt[3] = newmss & 0x00ff; + + inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0); + + oldval = ((__be16 *)tcph)[6]; + tcph->doff += TCPOLEN_MSS/4; + inet_proto_csum_replace2(&tcph->check, skb, + oldval, ((__be16 *)tcph)[6], 0); + return TCPOLEN_MSS; } -out: - return info->invert; -dropit: - par->hotdrop = true; +static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, + unsigned int family) +{ + struct flowi fl; + const struct nf_afinfo *ai; + struct rtable *rt = NULL; + u_int32_t mtu = ~0U; + + if (family == PF_INET) { + struct flowi4 *fl4 = &fl.u.ip4; + memset(fl4, 0, sizeof(*fl4)); + fl4->daddr = ip_hdr(skb)->saddr; + } else { + struct flowi6 *fl6 = &fl.u.ip6; + + memset(fl6, 0, sizeof(*fl6)); + fl6->daddr = ipv6_hdr(skb)->saddr; + } + rcu_read_lock(); + ai = nf_get_afinfo(family); + if (ai != NULL) + ai->route(&init_net, (struct dst_entry **)&rt, &fl, false); + rcu_read_unlock(); + + if (rt != NULL) { + mtu = dst_mtu(&rt->dst); + dst_release(&rt->dst); + } + return mtu; +} + +static unsigned int +tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) +{ + struct iphdr *iph = ip_hdr(skb); + __be16 newlen; + int ret; + + ret = tcpmss_mangle_packet(skb, par, + tcpmss_reverse_mtu(skb, PF_INET), + iph->ihl * 4, + sizeof(*iph) + sizeof(struct tcphdr)); + if (ret < 0) + return NF_DROP; + if (ret > 0) { + iph = ip_hdr(skb); + newlen = htons(ntohs(iph->tot_len) + ret); + csum_replace2(&iph->check, iph->tot_len, newlen); + iph->tot_len = newlen; + } + return XT_CONTINUE; +} + +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) +static unsigned int +tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par) +{ + struct ipv6hdr *ipv6h = ipv6_hdr(skb); + u8 nexthdr; + __be16 frag_off; + int tcphoff; + int ret; + + nexthdr = ipv6h->nexthdr; + tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off); + if (tcphoff < 0) + return NF_DROP; + ret = tcpmss_mangle_packet(skb, par, + tcpmss_reverse_mtu(skb, PF_INET6), + tcphoff, + sizeof(*ipv6h) + sizeof(struct tcphdr)); + if (ret < 0) + return NF_DROP; + if (ret > 0) { + ipv6h = ipv6_hdr(skb); + ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret); + } + return XT_CONTINUE; +} +#endif + +/* Must specify -p tcp --syn */ +static inline bool find_syn_match(const struct xt_entry_match *m) +{ + const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data; + + if (strcmp(m->u.kernel.match->name, "tcp") == 0 && + tcpinfo->flg_cmp & TCPHDR_SYN && + !(tcpinfo->invflags & XT_TCP_INV_FLAGS)) + return true; + return false; } -static struct xt_match tcpmss_mt_reg[] __read_mostly = { +static int tcpmss_tg4_check(const struct xt_tgchk_param *par) { - .name = "tcpmss", + const struct xt_tcpmss_info *info = par->targinfo; + const struct ipt_entry *e = par->entryinfo; + const struct xt_entry_match *ematch; + + if (info->mss == XT_TCPMSS_CLAMP_PMTU && + (par->hook_mask & ~((1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING))) != 0) { + pr_info("path-MTU clamping only supported in " + "FORWARD, OUTPUT and POSTROUTING hooks\n"); + return -EINVAL; + } + xt_ematch_foreach(ematch, e) + if (find_syn_match(ematch)) + return 0; + pr_info("Only works on TCP SYN packets\n"); + return -EINVAL; +} + +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) +static int tcpmss_tg6_check(const struct xt_tgchk_param *par) +{ + const struct xt_tcpmss_info *info = par->targinfo; + const struct ip6t_entry *e = par->entryinfo; + const struct xt_entry_match *ematch; + + if (info->mss == XT_TCPMSS_CLAMP_PMTU && + (par->hook_mask & ~((1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING))) != 0) { + pr_info("path-MTU clamping only supported in " + "FORWARD, OUTPUT and POSTROUTING hooks\n"); + return -EINVAL; + } + xt_ematch_foreach(ematch, e) + if (find_syn_match(ematch)) + return 0; + pr_info("Only works on TCP SYN packets\n"); + return -EINVAL; +} +#endif + +static struct xt_target tcpmss_tg_reg[] __read_mostly = { + { .family = NFPROTO_IPV4, - .match = tcpmss_mt, - .matchsize = sizeof(struct xt_tcpmss_match_info), + .name = "TCPMSS", + .checkentry = tcpmss_tg4_check, + .target = tcpmss_tg4, + .targetsize = sizeof(struct xt_tcpmss_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) { - .name = "tcpmss", .family = NFPROTO_IPV6, - .match = tcpmss_mt, - .matchsize = sizeof(struct xt_tcpmss_match_info), + .name = "TCPMSS", + .checkentry = tcpmss_tg6_check, + .target = tcpmss_tg6, + .targetsize = sizeof(struct xt_tcpmss_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, +#endif }; -static int __init tcpmss_mt_init(void) +static int __init tcpmss_tg_init(void) { - return xt_register_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); + return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); } -static void __exit tcpmss_mt_exit(void) +static void __exit tcpmss_tg_exit(void) { - xt_unregister_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); + xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); } -module_init(tcpmss_mt_init); -module_exit(tcpmss_mt_exit); +module_init(tcpmss_tg_init); +module_exit(tcpmss_tg_exit); Index: scripts/cr_jffs2_img_arm_mv7sft.sh =================================================================== --- scripts/cr_jffs2_img_arm_mv7sft.sh (nonexistent) +++ scripts/cr_jffs2_img_arm_mv7sft.sh (working copy) @@ -0,0 +1,133 @@ +#!/bin/sh -x + +# +## create flash images: +## jffs2 +## ubifs for flash with erase block size of 256KB and 512KB +# + +# +## parameters: +# +## 1. kernel image +## 2. root_fs +## 3. endianess: big_endian | little_endian +## 3. output directory +## +## example: ./cr_jffs2_img_arm_mv7sft.sh /tftpboot/uImage /tftpboot/rootfs_arm-mv7sft [big_endian | little_endian ] /tftpboot/ + +usage_exit() { + echo + echo "Usage: $1 cr_jffs2_img_arm_mv7sft.sh " + echo "Example: ./cr_jffs2_img_arm_mv7sft.sh /tftpboot/uImage /tftpboot/rootfs_arm-mv7sft-be big_endian /tftpboot" + exit +} + +KERNEL=$1 +ROOT_FS=$2 +ENDIANESS=$3 +OUTPUT_DIR=$4 + +# +### create jffs2 image +# + +if [ ! -f "${KERNEL}" ]; then + echo + echo kernel missing or incorrect + usage_exit +fi + +if [ ! -d "${ROOT_FS}" ]; then + echo + echo rootfs_dir missing or incorrect + usage_exit +fi +if [ "${ENDIANESS}" = "big_endian" ]; then + endianess="-b" +elif [ "${ENDIANESS}" = "little_endian" ]; then + endianess="-l" +else + echo endianess missing or incorrect + usage_exit +fi + +if [ ! -d "${OUTPUT_DIR}" ]; then + echo + echo output_dir missing or incorrect + usage_exit +fi + +echo ">>>>> creating jffs2 image <<<<<" + +IMAGE=${OUTPUT_DIR}/jffs2_arm.image +TEMP_IMAGE=${OUTPUT_DIR}/temp_image + +rm -f ${IMAGE} ${OUTPUT_DIR}/temp_image ${OUTPUT_DIR}/temp_kernel + +mkfs.jffs2 --eraseblock=16KiB ${endianess} -p -n -d ${ROOT_FS} -o ${TEMP_IMAGE} + +bzip2 -c ${KERNEL} > ${IMAGE} +bzip2 -c ${TEMP_IMAGE} >> ${IMAGE} + +rm ${TEMP_IMAGE} +echo +echo "file ${IMAGE} is ready" +echo "Use the mtdburn command to burn it to flash " +echo + + +echo ">>>>> creating 2 ubifs-nand images <<<<<" + +# remove prev build outputs +rm -f ${OUTPUT_DIR}/temp_ubi* + +# use same ubi config for all ubi images +UBI_CFG=${OUTPUT_DIR}/temp_ubinize.cfg +cat << EOF > ${UBI_CFG} +[ubifs] +mode=ubi +image=temp_ubi_rootfs.img +vol_id=0 +vol_type=dynamic +vol_name=rootfs_nand +vol_alignment=1 +vol_flags=autoresize +EOF + +IMAGE=${OUTPUT_DIR}/ubifs_arm_256eb_nand_v2_5.image +rm -f ${IMAGE} + +# create ubi image +mkfs.ubifs -r ${ROOT_FS} -m 4096 -e 253952 -c 4096 -R 24 -o temp_ubi_rootfs.img -v +ubinize -o temp_ubifs_rootfs.img -m 4096 -p 256KiB -s 4096 ${UBI_CFG} -v + +# concat kernel uImage and ubi image +bzip2 -c ${KERNEL} > ${IMAGE} +bzip2 -c temp_ubifs_rootfs.img >> ${IMAGE} + +echo +echo "file ${IMAGE} is ready" + +IMAGE=${OUTPUT_DIR}/ubifs_arm_512eb_nand.image + +# remove prev build outputs +rm -f ${IMAGE} ${OUTPUT_DIR}/temp_ubi_rootfs.img + +# create ubi image +mkfs.ubifs -r ${ROOT_FS} -m 4096 -e 516096 -c 4096 -R 24 -o temp_ubi_rootfs.img -v +ubinize -o temp_ubifs_rootfs.img -m 4096 -p 512KiB -s 4096 ${UBI_CFG} -v + +# concat kernel uImage and ubi image +bzip2 -c ${KERNEL} > ${IMAGE} +bzip2 -c temp_ubifs_rootfs.img >> ${IMAGE} + +# cleanup temporary files +rm -f ${OUTPUT_DIR}/temp_ubi* + +echo +echo "file ${IMAGE} is ready" +echo "Use the mtdburn command to burn it to flash " +echo + + Index: scripts/crfs_arm_mv7sft.sh =================================================================== --- scripts/crfs_arm_mv7sft.sh (nonexistent) +++ scripts/crfs_arm_mv7sft.sh (working copy) @@ -0,0 +1,980 @@ +#!/bin/bash + +############################################################## +# # +# crfs_arm.sh - Create Root File System # +# # +# Written by Aharon Gadot # +# Sep 01, 2006 # +# # +# Building root filesystem for embedded Linux system # +# # +############################################################## +# +## functions +# + +usage_exit() { + echo + echo "Incorrect parameters" + echo "Usage: crfs_arm_mv7sft.sh " + echo + echo "Examples: " + echo + echo "1. Little endian: " + echo + echo " ./crfs_arm_mv7sft.sh /tftpboot/rootfs_arm-mv7sft /swtools/devtools/gnueabi/arm_le/armv7-marvell-linux-gnueabi-softfp /swtools/devsources/root_fs/files/busybox " +# echo "" +# echo "2. Big endian: " +# echo +# echo " ./crfs_arm_mv7sft.sh /tftpboot/rootfs_arm-mv7sft-be /swtools/devtools/gnueabi/arm_be/armeb-linux-gnueabi /swtools/devsources/root_fs/files/busybox " +# echo "" + exit 1 +} + +# +## main +# + +HERE=`dirname $0` +HERE="`cd \"$HERE\" 2>/dev/null && pwd || echo \"$HERE\"`" +echo "" +if [ -n "$1" ] +then + if [ "$1" = "--help" ] + then + echo "Usage: crfs_arm_mv7sft.sh ." + echo " Create root filesystem for ARM CPU in path given by ." + echo " must contain busybox-1.01.tar.bz2 or busybox-1.01.tar.gz file" + echo " Busybox source can be downloaded from http://busybox.net/downloads/." + echo " - path where MV7SFT toolchain or newer package was installed." + echo "" + echo "Example: " + echo " ./crfs_arm_mv7sft.sh /tftpboot/rootfs_arm-mv7sft /swtools/devtools/gnueabi/arm_le/armv7-marvell-linux-gnueabi-softfp /swtools/devsources/root_fs/files/busybox " + echo "" + exit 0 + fi +fi + +if [ $# -ne 3 ] +then + usage_exit +fi + +dname=`dirname "$1"` +bname=`basename "$1"` +start_path="`cd \"$dname\" 2>/dev/null && pwd || echo \"$dname\"`/$bname" +if [ -e $1 ] +then + echo "File system root: $start_path" +else + echo "Creating file system root: $start_path" + /bin/mkdir $start_path +fi + +cd $HERE +runtime_files_bz2_uu=runtime_files.tar.bz2.uu +runtime_files=/tmp/__runtime_files_$$ +rm -rf ${runtime_files} +mkdir -p ${runtime_files} +cp $HERE/runtime_files.tar.bz2.uu ${runtime_files} +cd ${runtime_files} +uudecode ${runtime_files_bz2_uu} +bunzip2 -dc runtime_files.tar.bz2 | tar -xvf - +mv runtime_files/* . +cd .. + +dname=`dirname "$2"` +bname=`basename "$2"` +mv7sft="`cd \"$dname\" 2>/dev/null && pwd || echo \"$dname\"`/$bname" +if [ -e $mv7sft ] +then + echo "MV7SFT path: $mv7sft" +else + echo "MV7SFT not found in $2. Exiting..." + echo "" + exit 1 +fi + +if echo $mv7sft | grep 'arm_be' || echo $mv7sft | grep 'armeb' || echo $mv7sft | grep 'armbe' ; then + eb=eb + endian=be + endianess_string="big endian" +else + eb= + endian=le + endianess_string="little endian" +fi +echo "Endianess $endianess_string" +dname=`dirname "$3"` +bname=`basename "$3"` +busybox_path="`cd \"$dname\" 2>/dev/null && pwd || echo \"$dname\"`/$bname" + +if [ `whoami` = "root" ]; then + sudo= +else + sudo=sudo +fi + +user_path=$(pwd) +cd $start_path + +if [ -e $busybox_path/busybox-1.01.tar.bz2 ] +then + echo "Uncompressing Busybox tar.bz2 archive..." + tar xjf $busybox_path/busybox-1.01.tar.bz2 + echo -e "\bDone.\n" +elif [ -e $busybox_path/busybox-1.01.tar.gz ] +then + echo "Uncompressing Busybox tar.gz archive..." + tar xzf $busybox_path/busybox-1.01.tar.gz + echo -e "\bDone.\n" +else + echo "Busybox 1.01 archive not found at path $busybox_path. Exiting..." + echo "" + exit 1 +fi + +#create file structure +#--------------------- +mkdir bin +mkdir sbin +mkdir root +mkdir home +mkdir home/user +mkdir mnt +mkdir mnt/flash +mkdir mnt/nfs +mkdir etc +mkdir proc +mkdir sys +mkdir tmp +mkdir lib +mkdir lib/modules +mkdir usr +mkdir usr/lib +mkdir usr/bin +mkdir usr/local +mkdir usr/sbin +mkdir usr/share +mkdir var +mkdir var/lib +mkdir var/lock +mkdir var/log +mkdir var/run +mkdir var/tmp + +echo "Creating devices" +#create structure of devices +#--------------------------- +cd $user_path +cd $start_path +mkdir dev +cd dev + +#create dev folders +mkdir pts +mkdir shm +mkdir net + +# create block devices +$sudo mknod loop0 b 7 0 +$sudo mknod loop1 b 7 1 +$sudo mknod ram0 b 1 0 +$sudo mknod ram1 b 1 1 +$sudo mknod mtdblock0 b 31 0 +$sudo mknod mtdblock1 b 31 1 +$sudo mknod mtdblock2 b 31 2 + +# create character devices +$sudo mknod mem c 1 1 +$sudo mknod null c 1 3 +$sudo mknod zero c 1 5 +$sudo mknod random c 1 8 +$sudo mknod ptyp0 c 2 0 +$sudo mknod ptyp1 c 2 1 +$sudo mknod ptyp2 c 2 2 +$sudo mknod ttyp0 c 3 0 +$sudo mknod ttyp1 c 3 1 +$sudo mknod ttyp2 c 3 2 +$sudo mknod tty0 c 4 0 +$sudo mknod ttyS0 c 4 64 +$sudo mknod tty c 5 0 +$sudo mknod console c 5 1 +$sudo mknod ptmx c 5 2 +$sudo mknod sda b 8 0 +$sudo mknod sda1 b 8 1 +$sudo mknod sda2 b 8 2 +$sudo mknod sda3 b 8 3 +$sudo mknod net/tun c 10 200 +$sudo mknod mtd0 c 90 0 +$sudo mknod mtd0ro c 90 1 +$sudo mknod mtd1 c 90 2 +$sudo mknod mtd1ro c 90 3 +$sudo mknod mtd2 c 90 4 +$sudo mknod mtd2ro c 90 5 +$sudo mknod mvROS c 250 0 +$sudo mknod mvPP c 244 0 +$sudo mknod mvKernelExt c 244 1 +$sudo mknod i2c-0 c 89 0 +$sudo mknod i2c-1 c 89 1 + +# create links +ln -s ttyS0 ttys0 +ln -s ttyS0 tty5 +ln -s ttyp1 tty1 +ln -s mtd0 mtd + +echo "Building libraries" +# copy libraries +# -------------- +cd $user_path +cd $start_path + +cd usr/bin +usr_bin_path=`pwd` + +cd ../../lib +lib_path=`pwd` + +mv7sft_lib=$mv7sft/arm-marvell-linux-gnueabi/libc/lib +mv7sft_lib2=$mv7sft_lib +mv7sft_prefix=$mv7sft/bin/arm${eb}-marvell-linux-gnueabi- + +cd $mv7sft_lib +cp -d libc-* libc.* libm* ld-* libcrypt* libpthread* libdl* libSegFault* $lib_path + +cp $runtime_files/usr/bin/* ${lib_path}/../usr/bin +chmod +x ${lib_path}/../usr/bin/* + +cd $mv7sft_lib2 +cp -d libgcc_s* $lib_path + +# strip the libraries except libthread_db +${mv7sft_prefix}strip ${lib_path}/l* > /dev/null 2>&1 + +cp -d $mv7sft_lib/libthread_db* $lib_path + +cd $lib_path +rm -f *orig* + +echo "Creating etc files" +# create init files +# ----------------- +cd $user_path +cd $start_path +cd etc + +# creating passwd +echo -e "root::0:0:root:/root:/bin/sh\n\nuser::500:500:Linux User,,,:/home/user:/bin/sh\n" >./passwd + +# creating group +echo -e "root:x:1:root\nuser:x:500:\n" >./group +# creating inittab +echo -e "\n\t# autoexec\n\t::respawn:/etc/init.sh\n\n\t# Stuff to do when restarting the init process\n\t::restart:/sbin/init\n" >./inittab + +# creating motd +cat << EOF > ./motd + + +Welcome to Embedded Linux + _ _ + | ||_| + | | _ ____ _ _ _ _ + | || | _ \\| | | |\\ \\/ / + | || | | | | |_| |/ \\ + |_||_|_| |_|\\____|\\_/\\_/ + + On Marvell's ARMADAXP board + +For further information on the Marvell products check: +http://www.marvell.com/ + +toolchain=mv7sft ${endianess_string} + +Enjoy! + +EOF + + +# creating welcome file for telnet +cat << EOF > ./welcome + + +Welcome to Embedded Linux Telnet + _ _ + | ||_| + | | _ ____ _ _ _ _ + | || | _ \\| | | |\\ \\/ / + | || | | | | |_| |/ \\ + |_||_|_| |_|\\____|\\_/\\_/ + + On Marvell's ARM board + +toolchain=mv7sft ${endianess_string} + +For further information on the Marvell products check: +http://www.marvell.com/ +EOF + +# creating init.sh +current_date=$(date +%m%d%H%M%Y) +current_date1=$(date) + + +# creating README.txt +cat << EOF > ./README.txt + +File system building information +-------------------------------- + +build_date = ${current_date1} + +sdk_prefix = ${mv7sft} + +lib = \${sdk_prefix}/arm${eb}-mv7sft-linux-gnueabi/libc/lib + +cmd_prefix = \${sdk_prefix}/bin/arm${eb}-mv7sft-linux-gnueabi- + +gdbserver = ${gdbserver_file} + +EOF + +# creating init.sh + +cat << EOF > ./init.sh +#!/bin/sh +if test -e /proc/version +then + echo +else + + hostname MARVELL_LINUX + HOME=/root + + mount -t proc proc /proc + mount -t sysfs none /sys + mount -t tmpfs none /dev/shm -n size=64M + mount -t devpts none /dev/pts mode=0622 + + /usr/sbin/telnetd -l /bin/sh -f /etc/welcome + + if test -e /lib/modules/mvKernelExt.ko + then + insmod /lib/modules/mvKernelExt.ko + fi + +# if test -e /lib/modules/mvPpDrv.ko +# then +# insmod /lib/modules/mvPpDrv.ko +# fi + +date $current_date + + # Start the network interface + /sbin/ifconfig lo 127.0.0.1 + + rm -f /tmp/tasks + +fi + +export LD_PRELOAD=/lib/libSegFault.so + +# print logo +# clear +echo +echo +echo +echo +uname -nrsv +cat /etc/motd + +if test -e /usr/bin/appDemo +then + if /sbin/lsmod |grep -iq mvPpDrv + then + /usr/bin/appDemo + else + echo mvPpDrv not loaded + fi +else + echo /usr/bin/appDemo not found +fi + +exec /bin/sh +EOF + +chmod 777 init.sh + +# creating fstab +cat << EOF > ./fstab +/dev/nfs / nfs defaults 0 0 +none /proc proc defaults 0 0 +none /sys sysfs defaults 0 0 +none /dev/shm tmpfs size=64M 0 0 +none /dev/pts devpts mode=0622 0 0 +EOF + +# creating .config file for busybox +cd $user_path +cd $start_path/busybox-1.01/ + +#patch cmdedit.c - add backspace support +patch -p1 << EOF +--- busybox-1.01/shell/cmdedit_old.c 2008-04-03 19:19:32.000000000 +0300 ++++ busybox-1.01/shell/cmdedit.c 2008-04-03 19:22:23.000000000 +0300 +@@ -1427,8 +1427,8 @@ + input_backward(1); + break; + case '3': +- /* Delete */ +- input_delete(); ++ /* Backspace */ ++ input_backspace(); + break; + case '1': + case 'H': +EOF + +#patch ping6.c - fix compile errors with arm-marvell-linux-gnueabi-gcc +patch -p1 < + #include + /* get page info */ +-#include ++//#include + #include "busybox.h" + + //#define FEATURE_CPU_USAGE_PERCENTAGE /* + 2k */ +EOF + + +#patch insmod.c for kernel 3.0.6 +patch -p1 < 4 && len > 3 && tmp[len - 3] == '.' && + tmp[len - 2] == 'k' && tmp[len - 1] == 'o') { +EOF + +#patch procps.c - fix compile errors with arm-marvell-linux-gnueabi-gcc +patch -p1 < + #include + #include +-#include ++//#include + + #include "libbb.h" + +EOF + +cat << EOF > ./.config +# +# Automatically generated make config: don't edit +# +HAVE_DOT_CONFIG=y + +# +# General Configuration +# +# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set +CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +CONFIG_FEATURE_VERBOSE_USAGE=y +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_LOCALE_SUPPORT is not set +# CONFIG_FEATURE_DEVFS is not set +# CONFIG_FEATURE_DEVPTS is not set +# CONFIG_FEATURE_CLEAN_UP is not set +CONFIG_FEATURE_SUID=y +# CONFIG_FEATURE_SUID_CONFIG is not set +# CONFIG_SELINUX is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +# CONFIG_LFS is not set +USING_CROSS_COMPILER=y +CROSS_COMPILER_PREFIX="arm-marvell-linux-gnueabi-" +EXTRA_CFLAGS_OPTIONS="" + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +PREFIX="$start_path" + +# +# Archival Utilities +# +# CONFIG_AR is not set +CONFIG_BUNZIP2=y +# CONFIG_CPIO is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +CONFIG_GUNZIP=y +# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set +CONFIG_GZIP=y +# CONFIG_RPM2CPIO is not set +# CONFIG_RPM is not set +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +CONFIG_FEATURE_TAR_BZIP2=y +# CONFIG_FEATURE_TAR_FROM is not set +CONFIG_FEATURE_TAR_GZIP=y +# CONFIG_FEATURE_TAR_COMPRESS is not set +CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set +# CONFIG_UNCOMPRESS is not set +CONFIG_UNZIP=y + +# +# Common options for cpio and tar +# +# CONFIG_FEATURE_UNARCHIVE_TAPE is not set + +# +# Coreutils +# +CONFIG_BASENAME=y +# CONFIG_CAL is not set +CONFIG_CAT=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_CHROOT=y +CONFIG_CMP=y +CONFIG_CP=y +CONFIG_CUT=y +CONFIG_DATE=y +CONFIG_FEATURE_DATE_ISOFMT=y +CONFIG_DD=y +CONFIG_DF=y +CONFIG_DIRNAME=y +# CONFIG_DOS2UNIX is not set +CONFIG_DU=y +CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K=y +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +CONFIG_ENV=y +CONFIG_EXPR=y +CONFIG_FALSE=y +# CONFIG_FOLD is not set +CONFIG_HEAD=y +# CONFIG_FEATURE_FANCY_HEAD is not set +# CONFIG_HOSTID is not set +CONFIG_ID=y +CONFIG_INSTALL=y +# CONFIG_LENGTH is not set +CONFIG_LN=y +# CONFIG_LOGNAME is not set +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +CONFIG_FEATURE_LS_COLOR=y +# CONFIG_MD5SUM is not set +CONFIG_MKDIR=y +# CONFIG_MKFIFO is not set +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_OD is not set +# CONFIG_PRINTF is not set +CONFIG_PWD=y +# CONFIG_REALPATH is not set +CONFIG_RM=y +CONFIG_RMDIR=y +# CONFIG_SEQ is not set +# CONFIG_SHA1SUM is not set +CONFIG_SLEEP=y +# CONFIG_FEATURE_FANCY_SLEEP is not set +CONFIG_SORT=y +# CONFIG_STTY is not set +CONFIG_SYNC=y +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TEST=y + +# +# test (forced enabled for use with shell) +# +# CONFIG_FEATURE_TEST_64 is not set +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +CONFIG_UNIQ=y +CONFIG_USLEEP=y +# CONFIG_UUDECODE is not set +# CONFIG_UUENCODE is not set +# CONFIG_WATCH is not set +CONFIG_WC=y +# CONFIG_WHO is not set +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls and more +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Console Utilities +# +CONFIG_CHVT=y +CONFIG_CLEAR=y +CONFIG_DEALLOCVT=y +# CONFIG_DUMPKMAP is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +CONFIG_OPENVT=y +CONFIG_RESET=y +# CONFIG_SETKEYCODES is not set + +# +# Debian Utilities +# +CONFIG_MKTEMP=y +# CONFIG_PIPE_PROGRESS is not set +CONFIG_READLINK=y +# CONFIG_RUN_PARTS is not set +# CONFIG_START_STOP_DAEMON is not set +CONFIG_WHICH=y + +# +# Editors +# +# CONFIG_AWK is not set +# CONFIG_PATCH is not set +CONFIG_SED=y +# CONFIG_VI is not set + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +CONFIG_FEATURE_FIND_XDEV=y +# CONFIG_FEATURE_FIND_NEWER is not set +# CONFIG_FEATURE_FIND_INUM is not set +CONFIG_GREP=y +CONFIG_FEATURE_GREP_EGREP_ALIAS=y +CONFIG_FEATURE_GREP_FGREP_ALIAS=y +CONFIG_FEATURE_GREP_CONTEXT=y +CONFIG_XARGS=y +# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set +CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y +CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y +CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y + +# +# Init Utilities +# +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +CONFIG_FEATURE_INITRD=y +# CONFIG_FEATURE_INIT_COREDUMPS is not set +# CONFIG_FEATURE_INIT_SWAPON is not set +CONFIG_FEATURE_EXTRA_QUIET=y +CONFIG_HALT=y +CONFIG_POWEROFF=y +CONFIG_REBOOT=y +# CONFIG_MESG is not set + +# +# Login/Password Management Utilities +# +CONFIG_USE_BB_PWD_GRP=y +CONFIG_ADDGROUP=y +CONFIG_DELGROUP=y +CONFIG_ADDUSER=y +CONFIG_DELUSER=y +CONFIG_GETTY=y +# CONFIG_FEATURE_UTMP is not set +# CONFIG_FEATURE_WTMP is not set +CONFIG_LOGIN=y +# CONFIG_FEATURE_SECURETTY is not set +# CONFIG_PASSWD is not set +CONFIG_SU=y +CONFIG_SULOGIN=y +CONFIG_VLOCK=y + +# +# Common options for adduser, deluser, login, su +# +CONFIG_FEATURE_SHADOWPASSWDS=y +CONFIG_USE_BB_SHADOW=y + +# +# Miscellaneous Utilities +# +# CONFIG_ADJTIMEX is not set +# CONFIG_CROND is not set +# CONFIG_CRONTAB is not set +# CONFIG_DC is not set +# CONFIG_DEVFSD is not set +# CONFIG_LAST is not set +# CONFIG_HDPARM is not set +# CONFIG_MAKEDEVS is not set +# CONFIG_MT is not set +# CONFIG_RX is not set +CONFIG_STRINGS=y +CONFIG_TIME=y +# CONFIG_WATCHDOG is not set + +# +# Linux Module Utilities +# +CONFIG_INSMOD=y +CONFIG_FEATURE_2_4_MODULES=y +CONFIG_FEATURE_2_6_MODULES=y +CONFIG_FEATURE_INSMOD_VERSION_CHECKING=y +CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS=y +CONFIG_FEATURE_INSMOD_LOADINKMEM=y +CONFIG_FEATURE_INSMOD_LOAD_MAP=y +CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL=y +CONFIG_LSMOD=y +CONFIG_MODPROBE=y +CONFIG_RMMOD=y +CONFIG_FEATURE_CHECK_TAINTED_MODULE=y + +# +# Networking Utilities +# +CONFIG_FEATURE_IPV6=y +CONFIG_ARPING=y +CONFIG_FTPGET=y +CONFIG_FTPPUT=y +CONFIG_HOSTNAME=y +# CONFIG_HTTPD is not set +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set +CONFIG_IFUPDOWN=y +# CONFIG_FEATURE_IFUPDOWN_IP is not set +CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y +CONFIG_FEATURE_IFUPDOWN_IPV4=y +# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set +# CONFIG_FEATURE_IFUPDOWN_IPX is not set +# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set +CONFIG_INETD=y +CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO=y +CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD=y +CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME=y +CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME=y +CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN=y +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y +CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_ROUTE=y +# CONFIG_FEATURE_IP_TUNNEL is not set +# CONFIG_IPCALC is not set +# CONFIG_IPADDR is not set +# CONFIG_IPLINK is not set +# CONFIG_IPROUTE is not set +# CONFIG_IPTUNNEL is not set +# CONFIG_NAMEIF is not set +# CONFIG_NC is not set +CONFIG_NETSTAT=y +CONFIG_NSLOOKUP=y +CONFIG_PING=y +CONFIG_FEATURE_FANCY_PING=y +CONFIG_PING6=y +CONFIG_FEATURE_FANCY_PING6=y +CONFIG_ROUTE=y +CONFIG_TELNET=y +CONFIG_FEATURE_TELNET_TTYPE=y +CONFIG_FEATURE_TELNET_AUTOLOGIN=y +CONFIG_TELNETD=y +# CONFIG_FEATURE_TELNETD_INETD is not set +CONFIG_TFTP=y +CONFIG_FEATURE_TFTP_GET=y +CONFIG_FEATURE_TFTP_PUT=y +# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set +# CONFIG_FEATURE_TFTP_DEBUG is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_IP6_LITERAL=y + +# +# udhcp Server/Client +# +# CONFIG_UDHCPD is not set +# CONFIG_UDHCPC is not set + +# +# Process Utilities +# +CONFIG_FREE=y +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_PIDOF=y +CONFIG_PS=y +# CONFIG_RENICE is not set +CONFIG_TOP=y +FEATURE_CPU_USAGE_PERCENTAGE=y +CONFIG_UPTIME=y +# CONFIG_SYSCTL is not set + +# +# Another Bourne-like Shell +# +CONFIG_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_LASH is not set +# CONFIG_FEATURE_SH_IS_MSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +CONFIG_ASH=y + +# +# Ash Shell Options +# +CONFIG_ASH_JOB_CONTROL=y +CONFIG_ASH_ALIAS=y +CONFIG_ASH_MATH_SUPPORT=y +CONFIG_ASH_MATH_SUPPORT_64=y +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +CONFIG_ASH_OPTIMIZE_FOR_SIZE=y +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_HUSH is not set +# CONFIG_LASH is not set +# CONFIG_MSH is not set + +# +# Bourne Shell Options +# +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set +CONFIG_FEATURE_COMMAND_EDITING=y +CONFIG_FEATURE_COMMAND_HISTORY=15 +CONFIG_FEATURE_COMMAND_SAVEHISTORY=y +CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y +# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set +CONFIG_FEATURE_SH_FANCY_PROMPT=y + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +# CONFIG_FEATURE_REMOTE_LOG is not set +# CONFIG_FEATURE_IPC_SYSLOG is not set +CONFIG_KLOGD=y +CONFIG_LOGGER=y + +# +# Linux System Utilities +# +CONFIG_DMESG=y +# CONFIG_FBSET is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +# CONFIG_FREERAMDISK is not set +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_GETOPT is not set +CONFIG_HEXDUMP=y +# CONFIG_HWCLOCK is not set +# CONFIG_LOSETUP is not set +# CONFIG_MKSWAP is not set +CONFIG_MORE=y +CONFIG_FEATURE_USE_TERMIOS=y +CONFIG_PIVOT_ROOT=y +# CONFIG_RDATE is not set +CONFIG_SWAPONOFF=y +CONFIG_MOUNT=y +CONFIG_NFSMOUNT=y +CONFIG_UMOUNT=y +CONFIG_FEATURE_MOUNT_FORCE=y + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +EOF + +# make and install Busybox +echo -e "Compiling Busybox application. This process may take several minutes.\nPlease wait...\n\n" +#make TARGET_ARCH=arm CROSS=$mv7sft_prefix PREFIX=../. all install >/dev/null 2>/dev/null +make TARGET_ARCH=arm CROSS=$mv7sft_prefix PREFIX=../. all install +ln -s ./bin/busybox $start_path/init + +echo -e "\nCompilation completed.\n" + +# remove Busybox sources +cd .. +rm -rf busybox* +rm -rf ${runtime_files} + +echo "" +echo "Filesystem created successfuly" +echo "" + Index: sound/soc/blackfin/bf5xx-ac97-pcm.c =================================================================== --- sound/soc/blackfin/bf5xx-ac97-pcm.c (revision 1) +++ sound/soc/blackfin/bf5xx-ac97-pcm.c (working copy) @@ -416,19 +416,16 @@ } } -static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); - static int bf5xx_pcm_ac97_new(struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; - int ret = 0; + int ret; pr_debug("%s enter\n", __func__); - if (!card->dev->dma_mask) - card->dev->dma_mask = &bf5xx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { ret = bf5xx_pcm_preallocate_dma_buffer(pcm, Index: tools/nas/nas_init.sh =================================================================== --- tools/nas/nas_init.sh (revision 1) +++ tools/nas/nas_init.sh (working copy) @@ -1,8 +1,20 @@ #!/bin/bash +echo " * Version: 5.5" + # LOG: -echo " * Version: 4.9" - +# 5.5: +# 1. added support for fat32. +# 5.4: +# 1. added support for encrypted RAID arrays (crypt_rd0|crypt_rd1|crypt_rd5|crypt_rd6). +# 5.3: +# 1. enable adaptive coalecing for a338 & a375. +# 5.2: +# 1. fix -j option when not using the -p option. +# 5.1: +# 1. add mkfs.btrfs the -f flag +# 5.0: +# 1. remove btrfs disabling use of strict allocate in smb.conf # 4.9: # 1. set fdisk alignment to better support SSDs and new 4K sectors HDDS. # 2. remove default HDD_NUM set to 4. (each RAID has it's own default) @@ -135,7 +147,7 @@ f) FS=$OPTARG case "$OPTARG" in - ext4|btrfs|xfs) echo "Filesystem: ${OPTARG}" ;; + ext4|btrfs|xfs|fat32) echo "Filesystem: ${OPTARG}" ;; *) do_error "-f: wrong option" ;; esac ;; @@ -155,7 +167,7 @@ ;; t) TOPOLOGY=$OPTARG case "$OPTARG" in - sd|rd0|rd1|rd5|rd6|crypt_sd) ;; + sd|rd0|rd1|rd5|rd6|crypt_sd|crypt_rd0|crypt_rd1|crypt_rd5|crypt_rd6) ;; *) do_error "-t: wrong option" ;; esac ;; @@ -209,6 +221,10 @@ rd5) echo -ne "RAID5\n" ;; rd6) echo -ne "RAID6\n" ;; crypt_sd) echo -ne "Encrypted single drive\n" ;; + crypt_rd0) echo -ne "Encrypted RAID0\n" ;; + crypt_rd1) echo -ne "Encrypted RAID1\n" ;; + crypt_rd5) echo -ne "Encrypted RAID5\n" ;; + crypt_rd6) echo -ne "Encrypted RAID6\n" ;; *) do_error "Invalid drive topology" ;; esac @@ -222,6 +238,11 @@ echo -ne " if you like to change them, please pass different parameters to the nas_init.sh\n" echo -ne "******************************************\n" + +if [[ $TOPOLOGY == "crypt"* ]]; then + [ ! -e "$(which cryptsetup)" ] && do_error "cryptsetup in not installed, can't use encrypted drives" +fi + if [ "$SAMBASTATUS" == "enabled" ]; then [ ! -e "$(which smbd)" ] && do_error "SAMBA in not installed on your filesystem (aptitude install samba)" @@ -261,7 +282,7 @@ echo -ne "[Done]\n" # examine disk topology -if [ "$TOPOLOGY" == "sd" -o "$TOPOLOGY" == "crypt_sd" ]; then +if [[ $TOPOLOGY == *"sd" ]]; then if [ "$SYSDISKEXIST" == "yes" ]; then DRIVES="b" else @@ -268,7 +289,7 @@ DRIVES="a" fi PARTSIZE="55GB" -elif [ "$TOPOLOGY" == "rd0" ]; then +elif [[ $TOPOLOGY == *"rd0" ]]; then if [ "$HDD_NUM" == "5" ]; then if [ "$SYSDISKEXIST" == "yes" ]; then DRIVES="b c d e f" @@ -300,7 +321,7 @@ PARTSIZE="50GB" fi LEVEL=0 -elif [ "$TOPOLOGY" == "rd1" ]; then +elif [[ $TOPOLOGY == *"rd1" ]]; then if [ "$SYSDISKEXIST" == "yes" ]; then DRIVES="b c" else @@ -309,7 +330,7 @@ PARTSIZE="55GB" HDD_NUM=2 LEVEL=1 -elif [ "$TOPOLOGY" == "rd5" ]; then +elif [[ $TOPOLOGY == *"rd5" ]]; then if [ "$HDD_NUM" == "8" ]; then if [ "$SYSDISKEXIST" == "yes" ]; then DRIVES="b c d e f g h i" @@ -341,7 +362,7 @@ HDD_NUM=4 fi LEVEL=5 -elif [ "$TOPOLOGY" == "rd6" ]; then +elif [[ $TOPOLOGY == *"rd6" ]]; then if [ "$HDD_NUM" == "8" ]; then if [ "$SYSDISKEXIST" == "yes" ]; then DRIVES="b c d e f g h i" @@ -384,6 +405,8 @@ && do_error "missing mkfs.xfs in rootfs (aptitude install xfsprogs)" ;; btrfs) [ ! -e "$(which mkfs.btrfs)" ] \ && do_error "missing mkfs.btrfs in rootfs (aptitude install btrfs-tools)" ;; + fat32) [ ! -e "$(which mkfs.vfat)" ] \ + && do_error "missing mkfs.vfat in rootfs" ;; *) do_error "no valid filesystem specified" ;; esac @@ -390,16 +413,16 @@ case "$1" in ext4) case "$2" in - sd) STRIPE=0 + *sd) STRIPE=0 ;; - rd1) STRIPE=$STRIDE + *rd1) STRIPE=$STRIDE ;; - rd0) STRIPE=$((STRIDE * HDD_NUM)) + *rd0) STRIPE=$((STRIDE * HDD_NUM)) ;; - rd5) HDD_NUM=$((HDD_NUM - 1)) + *rd5) HDD_NUM=$((HDD_NUM - 1)) STRIPE=$((STRIDE * HDD_NUM)) ;; - rd6) HDD_NUM=$(($HDD_NUM - 2)) + *rd6) HDD_NUM=$(($HDD_NUM - 2)) STRIPE=$((STRIDE * HDD_NUM)) ;; *) do_error "unsupported topology $2\n" @@ -417,6 +440,8 @@ ;; btrfs) mkfs.btrfs $3 ;; + fat32) mkfs.vfat $3 -s 128 -S 512 -F 32 + ;; *) do_error "unsupported filesystem $1\n" ;; esac @@ -433,6 +458,8 @@ mount -t xfs $2 $3 -o noatime,nodirspread elif [ "$1" == "btrfs" ]; then mount -t btrfs $2 $3 -o noatime + elif [ "$1" == "fat32" ]; then + mount -t vfat $2 $3 -o rw,noatime,umask=0000 else do_error "unsupported filesystem $1\n" fi @@ -489,7 +516,7 @@ sleep 2 echo -ne "[Done]\n" -if [ "$TOPOLOGY" == "sd" ]; then +if [[ $TOPOLOGY == *"sd" ]]; then PARTITIONS="/dev/sd${DRIVES}${PARTNUM}" echo -ne " * Starting single disk: " set -o verbose @@ -496,37 +523,33 @@ echo -e 1024 > /sys/block/sd${DRIVES}/queue/read_ahead_kb + if [ "$MKFS" == "yes" ]; then for partition in `echo $DRIVES`; do mdadm --zero-superblock /dev/sd${partition}${PARTNUM}; done sleep 2 - create_fs $FS $TOPOLOGY $PARTITIONS - fi - mount_fs $FS $PARTITIONS $MNT_DIR - - set +o verbose - echo -ne "[Done]\n" -elif [ "$TOPOLOGY" == "crypt_sd" ]; then - PARTITIONS="/dev/sd${DRIVES}${PARTNUM}" - CRYPTO_PARTITIONS="/dev/mapper/$CRYPTO_NAME" - echo -ne " * Starting encrypted single disk: " - set -o verbose - - echo -e 1024 > /sys/block/sd${DRIVES}/queue/read_ahead_kb - + if [[ $TOPOLOGY == "crypt"* ]]; then + echo -ne "Encrypted: " # create encryption key dd if=/dev/urandom of=key bs=$KEY_SIZE count=1 cryptsetup -c $ALGORITHIM -d key -s $KEY_SIZE create $CRYPTO_NAME $PARTITIONS + PARTITIONS="/dev/mapper/$CRYPTO_NAME" + fi - if [ "$MKFS" == "yes" ]; then - create_fs $FS "sd" $CRYPTO_PARTITIONS + create_fs $FS $TOPOLOGY $PARTITIONS + + elif [[ $TOPOLOGY == "crypt"* ]]; then + echo -ne "Encrypted: " + cryptsetup -c $ALGORITHIM -d key -s $KEY_SIZE create $CRYPTO_NAME $PARTITIONS + PARTITIONS="/dev/mapper/$CRYPTO_NAME" fi - mount_fs $FS $CRYPTO_PARTITIONS $MNT_DIR + mount_fs $FS $PARTITIONS $MNT_DIR set +o verbose echo -ne "[Done]\n" else # RAID TOPOLOGY + TARGET_DRIVE="/dev/md0" [ ! -e "$(which mdadm)" ] && do_error "missing mdadm in rootfs (aptitude install mdadm)" echo -ne " * Starting $TOPOLOGY build: " @@ -540,7 +563,7 @@ for partition in `echo $DRIVES`; do mdadm --zero-superblock /dev/sd${partition}${PARTNUM}; done sleep 2 - echo "y" | mdadm --create -c 128 /dev/md0 --level=$LEVEL -n $HDD_NUM --force $PARTITIONS + echo "y" | mdadm --create -c 128 $TARGET_DRIVE --level=$LEVEL -n $HDD_NUM --force $PARTITIONS sleep 2 if [ `cat /proc/mdstat |grep md0 |wc -l` == 0 ]; then @@ -547,8 +570,16 @@ do_error "Unable to create RAID device" fi - create_fs $FS $TOPOLOGY /dev/md0 $HDD_NUM + if [[ $TOPOLOGY == "crypt"* ]]; then + echo -ne "Encrypted: " + # create encryption key + dd if=/dev/urandom of=key bs=$KEY_SIZE count=1 + cryptsetup -c $ALGORITHIM -d key -s $KEY_SIZE create $CRYPTO_NAME $TARGET_DRIVE + TARGET_DRIVE="/dev/mapper/$CRYPTO_NAME" + fi + create_fs $FS $TOPOLOGY $TARGET_DRIVE $HDD_NUM + else # need to reassemble the raid mdadm --assemble /dev/md0 --force $PARTITIONS @@ -556,11 +587,19 @@ if [ `cat /proc/mdstat |grep md0 |wc -l` == 0 ]; then do_error "Unable to assemble RAID device" fi + + if [[ $TOPOLOGY == "crypt"* ]]; then + echo -ne "Encrypted: " + [ ! -e "key" ] && do_error "no key file available, please locate or use -m to create a new key" + cryptsetup -c $ALGORITHIM -d key -s $KEY_SIZE create $CRYPTO_NAME /dev/md0 + TARGET_DRIVE="/dev/mapper/$CRYPTO_NAME" fi - mount_fs $FS /dev/md0 $MNT_DIR + fi - if [ "$TOPOLOGY" != "rd1" ]; then + mount_fs $FS $TARGET_DRIVE $MNT_DIR + + if [[ $TOPOLOGY != *"rd1" ]]; then # no stripe_cache_size support in rd1 if [ "$LARGE_PAGE" == "65536" ]; then echo 256 > /sys/block/md0/md/stripe_cache_size @@ -653,9 +692,12 @@ elif [ "$PLATFORM" == "a375" ]; then [ ! -e "$(which ethtool)" ] && do_error "missing ethtool in rootfs" set -o verbose - ethtool -C eth0 pkt-rate-low 20000 pkt-rate-high 3000000 rx-frames 32 \ - rx-usecs 1150 rx-usecs-high 1150 rx-usecs-low 100 rx-frames-low 32 \ - rx-frames-high 32 adaptive-rx on + for i in 0 1 ; do + ethtool -C eth$i pkt-rate-low 20000 pkt-rate-high 3000000 \ + rx-usecs-low 60 rx-frames-low 32 \ + rx-usecs 80 rx-frames 32 \ + rx-usecs-high 1150 rx-frames-high 32 adaptive-rx on + done set +o verbose elif [ "$PLATFORM" == "a380" ]; then [ ! -e "$(which ethtool)" ] && do_error "missing ethtool in rootfs" @@ -673,10 +715,14 @@ echo 250000 > /proc/sys/net/ipv4/tcp_limit_output_bytes elif [ "$PLATFORM" == "a388" ]; then [ ! -e "$(which ethtool)" ] && do_error "missing ethtool in rootfs" -# set -o verbose -# ethtool -C eth0 pkt-rate-low 20000 pkt-rate-high 3000000 rx-frames 100 \ -# rx-usecs 1500 rx-usecs-high 1500 adaptive-rx on -# set +o verbose + set -o verbose + for i in 0 1 ; do + ethtool -C eth$i pkt-rate-low 20000 pkt-rate-high 3000000 \ + rx-usecs-low 60 rx-frames-low 32 \ + rx-usecs 80 rx-frames 32 \ + rx-usecs-high 1150 rx-frames-high 32 adaptive-rx on + done + set +o verbose echo 250000 > /proc/sys/net/ipv4/tcp_limit_output_bytes fi @@ -725,13 +771,12 @@ echo ' max xmit = 131072' >> /etc/smb.conf echo ' disable netbios = yes' >> /etc/smb.conf echo ' csc policy = disable' >> /etc/smb.conf - if [ "$FS" == "btrfs" ]; then - # crash identified with btrfs + echo ' strict allocate = yes' >> /etc/smb.conf + if [ "$FS" == "ext4" ]; then + # only ext4 supports splice + echo ' min receivefile size = 16k' >> /etc/smb.conf + else echo '# min receivefile size = 16k' >> /etc/smb.conf - echo '# strict allocate = yes' >> /etc/smb.conf - else - echo ' min receivefile size = 16k' >> /etc/smb.conf - echo ' strict allocate = yes' >> /etc/smb.conf fi echo '' >> /etc/smb.conf echo '[public]' >> /etc/smb.conf