Commit c82e6d45 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus

* 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus:
  Staging: octeon-ethernet: Fix race freeing transmit buffers.
  Staging: octeon-ethernet: Convert to use net_device_ops.
  MIPS: Cavium: Add CPU hotplugging code.
  MIPS: SMP: Allow suspend and hibernation if CPU hotplug is available
  MIPS: Add arch generic CPU hotplug
  DMA: txx9dmac: use dma_unmap_single if DMA_COMPL_{SRC,DEST}_UNMAP_SINGLE set
  MIPS: Sibyte: Fix build error if CONFIG_SERIAL_SB1250_DUART is undefined.
  MIPS: MIPSsim: Fix build error if MSC01E_INT_BASE is undefined.
  MIPS: Hibernation: Remove SMP TLB and cacheflushing code.
  MIPS: Build fix - include <linux/smp.h> into all smp_processor_id() users.
  MIPS: bug.h Build fix - include <linux/compiler.h>.
parents c3cb5e19 a620c163
...@@ -601,6 +601,7 @@ config CAVIUM_OCTEON_SIMULATOR ...@@ -601,6 +601,7 @@ config CAVIUM_OCTEON_SIMULATOR
select SYS_SUPPORTS_64BIT_KERNEL select SYS_SUPPORTS_64BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_HIGHMEM select SYS_SUPPORTS_HIGHMEM
select SYS_SUPPORTS_HOTPLUG_CPU
select SYS_HAS_CPU_CAVIUM_OCTEON select SYS_HAS_CPU_CAVIUM_OCTEON
help help
The Octeon simulator is software performance model of the Cavium The Octeon simulator is software performance model of the Cavium
...@@ -615,6 +616,7 @@ config CAVIUM_OCTEON_REFERENCE_BOARD ...@@ -615,6 +616,7 @@ config CAVIUM_OCTEON_REFERENCE_BOARD
select SYS_SUPPORTS_64BIT_KERNEL select SYS_SUPPORTS_64BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_HIGHMEM select SYS_SUPPORTS_HIGHMEM
select SYS_SUPPORTS_HOTPLUG_CPU
select SYS_HAS_EARLY_PRINTK select SYS_HAS_EARLY_PRINTK
select SYS_HAS_CPU_CAVIUM_OCTEON select SYS_HAS_CPU_CAVIUM_OCTEON
select SWAP_IO_SPACE select SWAP_IO_SPACE
...@@ -784,8 +786,17 @@ config SYS_HAS_EARLY_PRINTK ...@@ -784,8 +786,17 @@ config SYS_HAS_EARLY_PRINTK
bool bool
config HOTPLUG_CPU config HOTPLUG_CPU
bool "Support for hot-pluggable CPUs"
depends on SMP && HOTPLUG && SYS_SUPPORTS_HOTPLUG_CPU
help
Say Y here to allow turning CPUs off and on. CPUs can be
controlled through /sys/devices/system/cpu.
(Note: power management support will enable this option
automatically on SMP systems. )
Say N if you want to disable CPU hotplug.
config SYS_SUPPORTS_HOTPLUG_CPU
bool bool
default n
config I8259 config I8259
bool bool
...@@ -2136,11 +2147,11 @@ menu "Power management options" ...@@ -2136,11 +2147,11 @@ menu "Power management options"
config ARCH_HIBERNATION_POSSIBLE config ARCH_HIBERNATION_POSSIBLE
def_bool y def_bool y
depends on !SMP depends on SYS_SUPPORTS_HOTPLUG_CPU
config ARCH_SUSPEND_POSSIBLE config ARCH_SUSPEND_POSSIBLE
def_bool y def_bool y
depends on !SMP depends on SYS_SUPPORTS_HOTPLUG_CPU
source "kernel/power/Kconfig" source "kernel/power/Kconfig"
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
*/ */
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/hardirq.h> #include <linux/smp.h>
#include <asm/octeon/octeon.h> #include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-pexp-defs.h> #include <asm/octeon/cvmx-pexp-defs.h>
...@@ -501,3 +501,62 @@ asmlinkage void plat_irq_dispatch(void) ...@@ -501,3 +501,62 @@ asmlinkage void plat_irq_dispatch(void)
} }
} }
} }
#ifdef CONFIG_HOTPLUG_CPU
static int is_irq_enabled_on_cpu(unsigned int irq, unsigned int cpu)
{
unsigned int isset;
#ifdef CONFIG_SMP
int coreid = cpu_logical_map(cpu);
#else
int coreid = cvmx_get_core_num();
#endif
int bit = (irq < OCTEON_IRQ_WDOG0) ?
irq - OCTEON_IRQ_WORKQ0 : irq - OCTEON_IRQ_WDOG0;
if (irq < 64) {
isset = (cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)) &
(1ull << bit)) >> bit;
} else {
isset = (cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1)) &
(1ull << bit)) >> bit;
}
return isset;
}
void fixup_irqs(void)
{
int irq;
for (irq = OCTEON_IRQ_SW0; irq <= OCTEON_IRQ_TIMER; irq++)
octeon_irq_core_disable_local(irq);
for (irq = OCTEON_IRQ_WORKQ0; irq <= OCTEON_IRQ_GPIO15; irq++) {
if (is_irq_enabled_on_cpu(irq, smp_processor_id())) {
/* ciu irq migrates to next cpu */
octeon_irq_chip_ciu0.disable(irq);
octeon_irq_ciu0_set_affinity(irq, &cpu_online_map);
}
}
#if 0
for (irq = OCTEON_IRQ_MBOX0; irq <= OCTEON_IRQ_MBOX1; irq++)
octeon_irq_mailbox_mask(irq);
#endif
for (irq = OCTEON_IRQ_UART0; irq <= OCTEON_IRQ_BOOTDMA; irq++) {
if (is_irq_enabled_on_cpu(irq, smp_processor_id())) {
/* ciu irq migrates to next cpu */
octeon_irq_chip_ciu0.disable(irq);
octeon_irq_ciu0_set_affinity(irq, &cpu_online_map);
}
}
for (irq = OCTEON_IRQ_UART2; irq <= OCTEON_IRQ_RESERVED135; irq++) {
if (is_irq_enabled_on_cpu(irq, smp_processor_id())) {
/* ciu irq migrates to next cpu */
octeon_irq_chip_ciu1.disable(irq);
octeon_irq_ciu1_set_affinity(irq, &cpu_online_map);
}
}
}
#endif /* CONFIG_HOTPLUG_CPU */
/*
* (C) Copyright 2004, 2005 Cavium Networks
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef __OCTEON_BOOT_H__
#define __OCTEON_BOOT_H__
#include <linux/types.h>
struct boot_init_vector {
uint32_t stack_addr;
uint32_t code_addr;
uint32_t app_start_func_addr;
uint32_t k0_val;
uint32_t flags;
uint32_t boot_info_addr;
uint32_t pad;
uint32_t pad2;
};
/* similar to bootloader's linux_app_boot_info but without global data */
struct linux_app_boot_info {
uint32_t labi_signature;
uint32_t start_core0_addr;
uint32_t avail_coremask;
uint32_t pci_console_active;
uint32_t icache_prefetch_disable;
uint32_t InitTLBStart_addr;
uint32_t start_app_addr;
uint32_t cur_exception_base;
uint32_t no_mark_private_data;
uint32_t compact_flash_common_base_addr;
uint32_t compact_flash_attribute_base_addr;
uint32_t led_display_base_addr;
};
/* If not to copy a lot of bootloader's structures
here is only offset of requested member */
#define AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK 0x765c
/* hardcoded in bootloader */
#define LABI_ADDR_IN_BOOTLOADER 0x700
#define LINUX_APP_BOOT_BLOCK_NAME "linux-app-boot"
#define LABI_SIGNATURE 0xAABBCCDD
/* from uboot-headers/octeon_mem_map.h */
#define EXCEPTION_BASE_INCR (4 * 1024)
/* Increment size for exception base addresses (4k minimum) */
#define EXCEPTION_BASE_BASE 0
#define BOOTLOADER_PRIV_DATA_BASE (EXCEPTION_BASE_BASE + 0x800)
#define BOOTLOADER_BOOT_VECTOR (BOOTLOADER_PRIV_DATA_BASE)
#endif /* __OCTEON_BOOT_H__ */
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/smp.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/string.h> /* for memset */ #include <linux/string.h> /* for memset */
#include <linux/tty.h> #include <linux/tty.h>
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* *
* Copyright (C) 2004-2008 Cavium Networks * Copyright (C) 2004-2008 Cavium Networks
*/ */
#include <linux/cpu.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/smp.h> #include <linux/smp.h>
...@@ -19,10 +20,16 @@ ...@@ -19,10 +20,16 @@
#include <asm/octeon/octeon.h> #include <asm/octeon/octeon.h>
#include "octeon_boot.h"
volatile unsigned long octeon_processor_boot = 0xff; volatile unsigned long octeon_processor_boot = 0xff;
volatile unsigned long octeon_processor_sp; volatile unsigned long octeon_processor_sp;
volatile unsigned long octeon_processor_gp; volatile unsigned long octeon_processor_gp;
#ifdef CONFIG_HOTPLUG_CPU
static unsigned int InitTLBStart_addr;
#endif
static irqreturn_t mailbox_interrupt(int irq, void *dev_id) static irqreturn_t mailbox_interrupt(int irq, void *dev_id)
{ {
const int coreid = cvmx_get_core_num(); const int coreid = cvmx_get_core_num();
...@@ -67,8 +74,28 @@ static inline void octeon_send_ipi_mask(cpumask_t mask, unsigned int action) ...@@ -67,8 +74,28 @@ static inline void octeon_send_ipi_mask(cpumask_t mask, unsigned int action)
} }
/** /**
* Detect available CPUs, populate phys_cpu_present_map * Detect available CPUs, populate cpu_possible_map
*/ */
static void octeon_smp_hotplug_setup(void)
{
#ifdef CONFIG_HOTPLUG_CPU
uint32_t labi_signature;
labi_signature =
cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
LABI_ADDR_IN_BOOTLOADER +
offsetof(struct linux_app_boot_info,
labi_signature)));
if (labi_signature != LABI_SIGNATURE)
pr_err("The bootloader version on this board is incorrect\n");
InitTLBStart_addr =
cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
LABI_ADDR_IN_BOOTLOADER +
offsetof(struct linux_app_boot_info,
InitTLBStart_addr)));
#endif
}
static void octeon_smp_setup(void) static void octeon_smp_setup(void)
{ {
const int coreid = cvmx_get_core_num(); const int coreid = cvmx_get_core_num();
...@@ -91,6 +118,9 @@ static void octeon_smp_setup(void) ...@@ -91,6 +118,9 @@ static void octeon_smp_setup(void)
cpus++; cpus++;
} }
} }
cpu_present_map = cpu_possible_map;
octeon_smp_hotplug_setup();
} }
/** /**
...@@ -128,6 +158,17 @@ static void octeon_init_secondary(void) ...@@ -128,6 +158,17 @@ static void octeon_init_secondary(void)
const int coreid = cvmx_get_core_num(); const int coreid = cvmx_get_core_num();
union cvmx_ciu_intx_sum0 interrupt_enable; union cvmx_ciu_intx_sum0 interrupt_enable;
#ifdef CONFIG_HOTPLUG_CPU
unsigned int cur_exception_base;
cur_exception_base = cvmx_read64_uint32(
CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
LABI_ADDR_IN_BOOTLOADER +
offsetof(struct linux_app_boot_info,
cur_exception_base)));
/* cur_exception_base is incremented in bootloader after setting */
write_c0_ebase((unsigned int)(cur_exception_base - EXCEPTION_BASE_INCR));
#endif
octeon_check_cpu_bist(); octeon_check_cpu_bist();
octeon_init_cvmcount(); octeon_init_cvmcount();
/* /*
...@@ -199,6 +240,193 @@ static void octeon_cpus_done(void) ...@@ -199,6 +240,193 @@ static void octeon_cpus_done(void)
#endif #endif
} }
#ifdef CONFIG_HOTPLUG_CPU
/* State of each CPU. */
DEFINE_PER_CPU(int, cpu_state);
extern void fixup_irqs(void);
static DEFINE_SPINLOCK(smp_reserve_lock);
static int octeon_cpu_disable(void)
{
unsigned int cpu = smp_processor_id();
if (cpu == 0)
return -EBUSY;
spin_lock(&smp_reserve_lock);
cpu_clear(cpu, cpu_online_map);
cpu_clear(cpu, cpu_callin_map);
local_irq_disable();
fixup_irqs();
local_irq_enable();
flush_cache_all();
local_flush_tlb_all();
spin_unlock(&smp_reserve_lock);
return 0;
}
static void octeon_cpu_die(unsigned int cpu)
{
int coreid = cpu_logical_map(cpu);
uint32_t avail_coremask;
struct cvmx_bootmem_named_block_desc *block_desc;
#ifdef CONFIG_CAVIUM_OCTEON_WATCHDOG
/* Disable the watchdog */
cvmx_ciu_wdogx_t ciu_wdog;
ciu_wdog.u64 = cvmx_read_csr(CVMX_CIU_WDOGX(cpu));
ciu_wdog.s.mode = 0;
cvmx_write_csr(CVMX_CIU_WDOGX(cpu), ciu_wdog.u64);
#endif
while (per_cpu(cpu_state, cpu) != CPU_DEAD)
cpu_relax();
/*
* This is a bit complicated strategics of getting/settig available
* cores mask, copied from bootloader
*/
/* LINUX_APP_BOOT_BLOCK is initialized in bootoct binary */
block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME);
if (!block_desc) {
avail_coremask =
cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
LABI_ADDR_IN_BOOTLOADER +
offsetof
(struct linux_app_boot_info,
avail_coremask)));
} else { /* alternative, already initialized */
avail_coremask =
cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
block_desc->base_addr +
AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK));
}
avail_coremask |= 1 << coreid;
/* Setting avail_coremask for bootoct binary */
if (!block_desc) {
cvmx_write64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
LABI_ADDR_IN_BOOTLOADER +
offsetof(struct linux_app_boot_info,
avail_coremask)),
avail_coremask);
} else {
cvmx_write64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
block_desc->base_addr +
AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK),
avail_coremask);
}
pr_info("Reset core %d. Available Coremask = %x \n", coreid,
avail_coremask);
cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid);
cvmx_write_csr(CVMX_CIU_PP_RST, 0);
}
void play_dead(void)
{
int coreid = cvmx_get_core_num();
idle_task_exit();
octeon_processor_boot = 0xff;
per_cpu(cpu_state, coreid) = CPU_DEAD;
while (1) /* core will be reset here */
;
}
extern void kernel_entry(unsigned long arg1, ...);
static void start_after_reset(void)
{
kernel_entry(0, 0, 0); /* set a2 = 0 for secondary core */
}
int octeon_update_boot_vector(unsigned int cpu)
{
int coreid = cpu_logical_map(cpu);
unsigned int avail_coremask;
struct cvmx_bootmem_named_block_desc *block_desc;
struct boot_init_vector *boot_vect =
(struct boot_init_vector *) cvmx_phys_to_ptr(0x0 +
BOOTLOADER_BOOT_VECTOR);
block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME);
if (!block_desc) {
avail_coremask =
cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
LABI_ADDR_IN_BOOTLOADER +
offsetof(struct linux_app_boot_info,
avail_coremask)));
} else { /* alternative, already initialized */
avail_coremask =
cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
block_desc->base_addr +
AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK));
}
if (!(avail_coremask & (1 << coreid))) {
/* core not available, assume, that catched by simple-executive */
cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid);
cvmx_write_csr(CVMX_CIU_PP_RST, 0);
}
boot_vect[coreid].app_start_func_addr =
(uint32_t) (unsigned long) start_after_reset;
boot_vect[coreid].code_addr = InitTLBStart_addr;
CVMX_SYNC;
cvmx_write_csr(CVMX_CIU_NMI, (1 << coreid) & avail_coremask);
return 0;
}
static int __cpuinit octeon_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
switch (action) {
case CPU_UP_PREPARE:
octeon_update_boot_vector(cpu);
break;
case CPU_ONLINE:
pr_info("Cpu %d online\n", cpu);
break;
case CPU_DEAD:
break;
}
return NOTIFY_OK;
}
static struct notifier_block __cpuinitdata octeon_cpu_notifier = {
.notifier_call = octeon_cpu_callback,
};
static int __cpuinit register_cavium_notifier(void)
{
register_hotcpu_notifier(&octeon_cpu_notifier);
return 0;
}
late_initcall(register_cavium_notifier);
#endif /* CONFIG_HOTPLUG_CPU */
struct plat_smp_ops octeon_smp_ops = { struct plat_smp_ops octeon_smp_ops = {
.send_ipi_single = octeon_send_ipi_single, .send_ipi_single = octeon_send_ipi_single,
.send_ipi_mask = octeon_send_ipi_mask, .send_ipi_mask = octeon_send_ipi_mask,
...@@ -208,4 +436,8 @@ struct plat_smp_ops octeon_smp_ops = { ...@@ -208,4 +436,8 @@ struct plat_smp_ops octeon_smp_ops = {
.boot_secondary = octeon_boot_secondary, .boot_secondary = octeon_boot_secondary,
.smp_setup = octeon_smp_setup, .smp_setup = octeon_smp_setup,
.prepare_cpus = octeon_prepare_cpus, .prepare_cpus = octeon_prepare_cpus,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = octeon_cpu_disable,
.cpu_die = octeon_cpu_die,
#endif
}; };
#ifndef __ASM_BUG_H #ifndef __ASM_BUG_H
#define __ASM_BUG_H #define __ASM_BUG_H
#include <linux/compiler.h>
#include <asm/sgidefs.h> #include <asm/sgidefs.h>
#ifdef CONFIG_BUG #ifdef CONFIG_BUG
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/smp.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/cpu-info.h> #include <asm/cpu-info.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#define _ASM_IRQ_H #define _ASM_IRQ_H
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/smp.h>
#include <asm/mipsmtregs.h> #include <asm/mipsmtregs.h>
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
......
...@@ -26,6 +26,10 @@ struct plat_smp_ops { ...@@ -26,6 +26,10 @@ struct plat_smp_ops {
void (*boot_secondary)(int cpu, struct task_struct *idle); void (*boot_secondary)(int cpu, struct task_struct *idle);
void (*smp_setup)(void); void (*smp_setup)(void);
void (*prepare_cpus)(unsigned int max_cpus); void (*prepare_cpus)(unsigned int max_cpus);
#ifdef CONFIG_HOTPLUG_CPU
int (*cpu_disable)(void);
void (*cpu_die)(unsigned int cpu);
#endif
}; };
extern void register_smp_ops(struct plat_smp_ops *ops); extern void register_smp_ops(struct plat_smp_ops *ops);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/smp.h>
#include <linux/threads.h> #include <linux/threads.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
...@@ -40,6 +41,7 @@ extern int __cpu_logical_map[NR_CPUS]; ...@@ -40,6 +41,7 @@ extern int __cpu_logical_map[NR_CPUS];
/* Octeon - Tell another core to flush its icache */ /* Octeon - Tell another core to flush its icache */
#define SMP_ICACHE_FLUSH 0x4 #define SMP_ICACHE_FLUSH 0x4
extern volatile cpumask_t cpu_callin_map;
extern void asmlinkage smp_bootstrap(void); extern void asmlinkage smp_bootstrap(void);
...@@ -55,6 +57,24 @@ static inline void smp_send_reschedule(int cpu) ...@@ -55,6 +57,24 @@ static inline void smp_send_reschedule(int cpu)
mp_ops->send_ipi_single(cpu, SMP_RESCHEDULE_YOURSELF); mp_ops->send_ipi_single(cpu, SMP_RESCHEDULE_YOURSELF);
} }
#ifdef CONFIG_HOTPLUG_CPU
static inline int __cpu_disable(void)
{
extern struct plat_smp_ops *mp_ops; /* private */
return mp_ops->cpu_disable();
}
static inline void __cpu_die(unsigned int cpu)
{
extern struct plat_smp_ops *mp_ops; /* private */
mp_ops->cpu_die(cpu);
}
extern void play_dead(void);
#endif
extern asmlinkage void smp_call_function_interrupt(void); extern asmlinkage void smp_call_function_interrupt(void);
extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_single_ipi(int cpu);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/smp.h>
#include <linux/types.h> #include <linux/types.h>
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm/irq_cpu.h> #include <asm/irq_cpu.h>
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/addrspace.h> #include <asm/addrspace.h>
#include <asm/io.h> #include <asm/io.h>
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/smtc_ipi.h> #include <asm/smtc_ipi.h>
#include <asm/time.h> #include <asm/time.h>
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/addrspace.h> #include <asm/addrspace.h>
#include <asm/io.h> #include <asm/io.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/smtc_ipi.h> #include <asm/smtc_ipi.h>
#include <asm/time.h> #include <asm/time.h>
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/smp.h>
#include <linux/stddef.h> #include <linux/stddef.h>
#include <asm/bugs.h> #include <asm/bugs.h>
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/smp.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm/delay.h> #include <asm/delay.h>
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/smp.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/gic.h> #include <asm/gic.h>
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/kgdb.h> #include <linux/kgdb.h>
#include <linux/kdebug.h> #include <linux/kdebug.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <asm/inst.h> #include <asm/inst.h>
#include <asm/fpu.h> #include <asm/fpu.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
......
...@@ -50,10 +50,15 @@ ...@@ -50,10 +50,15 @@
*/ */
void __noreturn cpu_idle(void) void __noreturn cpu_idle(void)
{ {
int cpu;
/* CPU is going idle. */
cpu = smp_processor_id();
/* endless idle loop with no priority at all */ /* endless idle loop with no priority at all */
while (1) { while (1) {
tick_nohz_stop_sched_tick(1); tick_nohz_stop_sched_tick(1);
while (!need_resched()) { while (!need_resched() && cpu_online(cpu)) {
#ifdef CONFIG_MIPS_MT_SMTC #ifdef CONFIG_MIPS_MT_SMTC
extern void smtc_idle_loop_hook(void); extern void smtc_idle_loop_hook(void);
...@@ -62,6 +67,12 @@ void __noreturn cpu_idle(void) ...@@ -62,6 +67,12 @@ void __noreturn cpu_idle(void)
if (cpu_wait) if (cpu_wait)
(*cpu_wait)(); (*cpu_wait)();
} }
#ifdef CONFIG_HOTPLUG_CPU
if (!cpu_online(cpu) && !cpu_isset(cpu, cpu_callin_map) &&
(system_state == SYSTEM_RUNNING ||
system_state == SYSTEM_BOOTING))
play_dead();
#endif
tick_nohz_restart_sched_tick(); tick_nohz_restart_sched_tick();
preempt_enable_no_resched(); preempt_enable_no_resched();
schedule(); schedule();
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/compiler.h> #include <linux/compiler.h>
......
...@@ -55,6 +55,18 @@ static void __init up_prepare_cpus(unsigned int max_cpus) ...@@ -55,6 +55,18 @@ static void __init up_prepare_cpus(unsigned int max_cpus)
{ {
} }
#ifdef CONFIG_HOTPLUG_CPU
static int up_cpu_disable(void)
{
return -ENOSYS;
}
static void up_cpu_die(unsigned int cpu)
{
BUG();
}
#endif
struct plat_smp_ops up_smp_ops = { struct plat_smp_ops up_smp_ops = {
.send_ipi_single = up_send_ipi_single, .send_ipi_single = up_send_ipi_single,
.send_ipi_mask = up_send_ipi_mask, .send_ipi_mask = up_send_ipi_mask,
...@@ -64,4 +76,8 @@ struct plat_smp_ops up_smp_ops = { ...@@ -64,4 +76,8 @@ struct plat_smp_ops up_smp_ops = {
.boot_secondary = up_boot_secondary, .boot_secondary = up_boot_secondary,
.smp_setup = up_smp_setup, .smp_setup = up_smp_setup,
.prepare_cpus = up_prepare_cpus, .prepare_cpus = up_prepare_cpus,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = up_cpu_disable,
.cpu_die = up_cpu_die,
#endif
}; };
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/threads.h> #include <linux/threads.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -44,7 +45,7 @@ ...@@ -44,7 +45,7 @@
#include <asm/mipsmtregs.h> #include <asm/mipsmtregs.h>
#endif /* CONFIG_MIPS_MT_SMTC */ #endif /* CONFIG_MIPS_MT_SMTC */
static volatile cpumask_t cpu_callin_map; /* Bitmask of started secondaries */ volatile cpumask_t cpu_callin_map; /* Bitmask of started secondaries */
int __cpu_number_map[NR_CPUS]; /* Map physical to logical */ int __cpu_number_map[NR_CPUS]; /* Map physical to logical */
int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */ int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */
...@@ -200,6 +201,8 @@ void __devinit smp_prepare_boot_cpu(void) ...@@ -200,6 +201,8 @@ void __devinit smp_prepare_boot_cpu(void)
* and keep control until "cpu_online(cpu)" is set. Note: cpu is * and keep control until "cpu_online(cpu)" is set. Note: cpu is
* physical, not logical. * physical, not logical.
*/ */
static struct task_struct *cpu_idle_thread[NR_CPUS];
int __cpuinit __cpu_up(unsigned int cpu) int __cpuinit __cpu_up(unsigned int cpu)
{ {
struct task_struct *idle; struct task_struct *idle;
...@@ -209,9 +212,16 @@ int __cpuinit __cpu_up(unsigned int cpu) ...@@ -209,9 +212,16 @@ int __cpuinit __cpu_up(unsigned int cpu)
* The following code is purely to make sure * The following code is purely to make sure
* Linux can schedule processes on this slave. * Linux can schedule processes on this slave.
*/ */
if (!cpu_idle_thread[cpu]) {
idle = fork_idle(cpu); idle = fork_idle(cpu);
cpu_idle_thread[cpu] = idle;
if (IS_ERR(idle)) if (IS_ERR(idle))
panic(KERN_ERR "Fork failed for CPU %d", cpu); panic(KERN_ERR "Fork failed for CPU %d", cpu);
} else {
idle = cpu_idle_thread[cpu];
init_idle(idle, cpu);
}
mp_ops->boot_secondary(cpu, idle); mp_ops->boot_secondary(cpu, idle);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
......
...@@ -17,7 +17,10 @@ static int __init topology_init(void) ...@@ -17,7 +17,10 @@ static int __init topology_init(void)
#endif /* CONFIG_NUMA */ #endif /* CONFIG_NUMA */
for_each_present_cpu(i) { for_each_present_cpu(i) {
ret = register_cpu(&per_cpu(cpu_devices, i), i); struct cpu *c = &per_cpu(cpu_devices, i);
c->hotpluggable = 1;
ret = register_cpu(c, i);
if (ret) if (ret)
printk(KERN_WARNING "topology_init: register_cpu %d " printk(KERN_WARNING "topology_init: register_cpu %d "
"failed (%d)\n", i, ret); "failed (%d)\n", i, ret);
......
...@@ -91,6 +91,7 @@ unsigned __cpuinit get_c0_compare_int(void) ...@@ -91,6 +91,7 @@ unsigned __cpuinit get_c0_compare_int(void)
mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR; mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
} else { } else {
#endif #endif
{
if (cpu_has_vint) if (cpu_has_vint)
set_vi_handler(cp0_compare_irq, mips_timer_dispatch); set_vi_handler(cp0_compare_irq, mips_timer_dispatch);
mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq; mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/cpu.h> #include <linux/cpu.h>
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <asm/page.h> #include <asm/page.h>
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/bitops.h> #include <linux/bitops.h>
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <asm/cacheops.h> #include <asm/cacheops.h>
......
#include <linux/module.h> #include <linux/module.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/smp.h>
#include <asm/fixmap.h> #include <asm/fixmap.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/string.h> #include <linux/string.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <asm/page.h> #include <asm/page.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
*/ */
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/hugetlb.h> #include <linux/hugetlb.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
*/ */
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <asm/cpu.h> #include <asm/cpu.h>
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/smp.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/init.h> #include <linux/init.h>
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/smp.h>
#include <asm/sn/arch.h> #include <asm/sn/arch.h>
#include <asm/pci/bridge.h> #include <asm/pci/bridge.h>
#include <asm/paccess.h> #include <asm/paccess.h>
......
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <asm/pmon.h> #include <asm/pmon.h>
#include <asm/titan_dep.h> #include <asm/titan_dep.h>
......
...@@ -43,15 +43,6 @@ LEAF(swsusp_arch_resume) ...@@ -43,15 +43,6 @@ LEAF(swsusp_arch_resume)
bne t1, t3, 1b bne t1, t3, 1b
PTR_L t0, PBE_NEXT(t0) PTR_L t0, PBE_NEXT(t0)
bnez t0, 0b bnez t0, 0b
/* flush caches to make sure context is in memory */
PTR_L t0, __flush_cache_all
jalr t0
/* flush tlb entries */
#ifdef CONFIG_SMP
jal flush_tlb_all
#else
jal local_flush_tlb_all
#endif
PTR_LA t0, saved_regs PTR_LA t0, saved_regs
PTR_L ra, PT_R31(t0) PTR_L ra, PT_R31(t0)
PTR_L sp, PT_R29(t0) PTR_L sp, PT_R29(t0)
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/smp.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/param.h> #include <linux/param.h>
#include <linux/smp.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/mm.h> #include <linux/mm.h>
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/smp.h>
#include <asm/sn/types.h> #include <asm/sn/types.h>
#include <asm/sn/klconfig.h> #include <asm/sn/klconfig.h>
#include <asm/sn/hub.h> #include <asm/sn/hub.h>
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
......
...@@ -51,12 +51,13 @@ static int cfe_console_setup(struct console *cons, char *str) ...@@ -51,12 +51,13 @@ static int cfe_console_setup(struct console *cons, char *str)
setleds("u0cn"); setleds("u0cn");
} else if (!strcmp(consdev, "uart1")) { } else if (!strcmp(consdev, "uart1")) {
setleds("u1cn"); setleds("u1cn");
} else
#endif #endif
#ifdef CONFIG_VGA_CONSOLE #ifdef CONFIG_VGA_CONSOLE
} else if (!strcmp(consdev, "pcconsole0")) { if (!strcmp(consdev, "pcconsole0")) {
setleds("pccn"); setleds("pccn");
#endif
} else } else
#endif
return -ENODEV; return -ENODEV;
} }
return 0; return 0;
......
#include <linux/types.h> #include <linux/types.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/clockchips.h> #include <linux/clockchips.h>
......
...@@ -432,23 +432,27 @@ txx9dmac_descriptor_complete(struct txx9dmac_chan *dc, ...@@ -432,23 +432,27 @@ txx9dmac_descriptor_complete(struct txx9dmac_chan *dc,
list_splice_init(&txd->tx_list, &dc->free_list); list_splice_init(&txd->tx_list, &dc->free_list);
list_move(&desc->desc_node, &dc->free_list); list_move(&desc->desc_node, &dc->free_list);
/*
* We use dma_unmap_page() regardless of how the buffers were
* mapped before they were submitted...
*/
if (!ds) { if (!ds) {
dma_addr_t dmaaddr; dma_addr_t dmaaddr;
if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
dmaaddr = is_dmac64(dc) ? dmaaddr = is_dmac64(dc) ?
desc->hwdesc.DAR : desc->hwdesc32.DAR; desc->hwdesc.DAR : desc->hwdesc32.DAR;
dma_unmap_page(chan2parent(&dc->chan), dmaaddr, if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
desc->len, DMA_FROM_DEVICE); dma_unmap_single(chan2parent(&dc->chan),
dmaaddr, desc->len, DMA_FROM_DEVICE);
else
dma_unmap_page(chan2parent(&dc->chan),
dmaaddr, desc->len, DMA_FROM_DEVICE);
} }
if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
dmaaddr = is_dmac64(dc) ? dmaaddr = is_dmac64(dc) ?
desc->hwdesc.SAR : desc->hwdesc32.SAR; desc->hwdesc.SAR : desc->hwdesc32.SAR;
dma_unmap_page(chan2parent(&dc->chan), dmaaddr, if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
desc->len, DMA_TO_DEVICE); dma_unmap_single(chan2parent(&dc->chan),
dmaaddr, desc->len, DMA_TO_DEVICE);
else
dma_unmap_page(chan2parent(&dc->chan),
dmaaddr, desc->len, DMA_TO_DEVICE);
} }
} }
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
obj-${CONFIG_OCTEON_ETHERNET} := octeon-ethernet.o obj-${CONFIG_OCTEON_ETHERNET} := octeon-ethernet.o
octeon-ethernet-objs := ethernet.o octeon-ethernet-objs := ethernet.o
octeon-ethernet-objs += ethernet-common.o
octeon-ethernet-objs += ethernet-mdio.o octeon-ethernet-objs += ethernet-mdio.o
octeon-ethernet-objs += ethernet-mem.o octeon-ethernet-objs += ethernet-mem.o
octeon-ethernet-objs += ethernet-proc.o octeon-ethernet-objs += ethernet-proc.o
......
/**********************************************************************
* Author: Cavium Networks
*
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
* Copyright (c) 2003-2007 Cavium Networks
*
* This file 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.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* or visit http://www.gnu.org/licenses/.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
**********************************************************************/
#include <linux/kernel.h>
#include <linux/mii.h>
#include <net/dst.h>
#include <asm/atomic.h>
#include <asm/octeon/octeon.h>
#include "ethernet-defines.h"
#include "ethernet-tx.h"
#include "ethernet-mdio.h"
#include "ethernet-util.h"
#include "octeon-ethernet.h"
#include "ethernet-common.h"
#include "cvmx-pip.h"
#include "cvmx-pko.h"
#include "cvmx-fau.h"
#include "cvmx-helper.h"
#include "cvmx-gmxx-defs.h"
/**
* Get the low level ethernet statistics
*
* @dev: Device to get the statistics from
* Returns Pointer to the statistics
*/
static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev)
{
cvmx_pip_port_status_t rx_status;
cvmx_pko_port_status_t tx_status;
struct octeon_ethernet *priv = netdev_priv(dev);
if (priv->port < CVMX_PIP_NUM_INPUT_PORTS) {
if (octeon_is_simulation()) {
/* The simulator doesn't support statistics */
memset(&rx_status, 0, sizeof(rx_status));
memset(&tx_status, 0, sizeof(tx_status));
} else {
cvmx_pip_get_port_status(priv->port, 1, &rx_status);
cvmx_pko_get_port_status(priv->port, 1, &tx_status);
}
priv->stats.rx_packets += rx_status.inb_packets;
priv->stats.tx_packets += tx_status.packets;
priv->stats.rx_bytes += rx_status.inb_octets;
priv->stats.tx_bytes += tx_status.octets;
priv->stats.multicast += rx_status.multicast_packets;
priv->stats.rx_crc_errors += rx_status.inb_errors;
priv->stats.rx_frame_errors += rx_status.fcs_align_err_packets;
/*
* The drop counter must be incremented atomically
* since the RX tasklet also increments it.
*/
#ifdef CONFIG_64BIT
atomic64_add(rx_status.dropped_packets,
(atomic64_t *)&priv->stats.rx_dropped);
#else
atomic_add(rx_status.dropped_packets,
(atomic_t *)&priv->stats.rx_dropped);
#endif
}
return &priv->stats;
}
/**
* Set the multicast list. Currently unimplemented.
*
* @dev: Device to work on
*/
static void cvm_oct_common_set_multicast_list(struct net_device *dev)
{
union cvmx_gmxx_prtx_cfg gmx_cfg;
struct octeon_ethernet *priv = netdev_priv(dev);
int interface = INTERFACE(priv->port);
int index = INDEX(priv->port);
if ((interface < 2)
&& (cvmx_helper_interface_get_mode(interface) !=
CVMX_HELPER_INTERFACE_MODE_SPI)) {
union cvmx_gmxx_rxx_adr_ctl control;
control.u64 = 0;
control.s.bcst = 1; /* Allow broadcast MAC addresses */
if (dev->mc_list || (dev->flags & IFF_ALLMULTI) ||
(dev->flags & IFF_PROMISC))
/* Force accept multicast packets */
control.s.mcst = 2;
else
/* Force reject multicat packets */
control.s.mcst = 1;
if (dev->flags & IFF_PROMISC)
/*
* Reject matches if promisc. Since CAM is
* shut off, should accept everything.
*/
control.s.cam_mode = 0;
else
/* Filter packets based on the CAM */
control.s.cam_mode = 1;
gmx_cfg.u64 =
cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
gmx_cfg.u64 & ~1ull);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CTL(index, interface),
control.u64);
if (dev->flags & IFF_PROMISC)
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN
(index, interface), 0);
else
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN
(index, interface), 1);
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
gmx_cfg.u64);
}
}
/**
* Set the hardware MAC address for a device
*
* @dev: Device to change the MAC address for
* @addr: Address structure to change it too. MAC address is addr + 2.
* Returns Zero on success
*/
static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
{
struct octeon_ethernet *priv = netdev_priv(dev);
union cvmx_gmxx_prtx_cfg gmx_cfg;
int interface = INTERFACE(priv->port);
int index = INDEX(priv->port);
memcpy(dev->dev_addr, addr + 2, 6);
if ((interface < 2)
&& (cvmx_helper_interface_get_mode(interface) !=
CVMX_HELPER_INTERFACE_MODE_SPI)) {
int i;
uint8_t *ptr = addr;
uint64_t mac = 0;
for (i = 0; i < 6; i++)
mac = (mac << 8) | (uint64_t) (ptr[i + 2]);
gmx_cfg.u64 =
cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
gmx_cfg.u64 & ~1ull);
cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface),
ptr[2]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface),
ptr[3]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface),
ptr[4]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface),
ptr[5]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface),
ptr[6]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface),
ptr[7]);
cvm_oct_common_set_multicast_list(dev);
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
gmx_cfg.u64);
}
return 0;
}
/**
* Change the link MTU. Unimplemented
*
* @dev: Device to change
* @new_mtu: The new MTU
*
* Returns Zero on success
*/
static int cvm_oct_common_change_mtu(struct net_device *dev, int new_mtu)
{
struct octeon_ethernet *priv = netdev_priv(dev);
int interface = INTERFACE(priv->port);
int index = INDEX(priv->port);
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
int vlan_bytes = 4;
#else
int vlan_bytes = 0;
#endif
/*
* Limit the MTU to make sure the ethernet packets are between
* 64 bytes and 65535 bytes.
*/
if ((new_mtu + 14 + 4 + vlan_bytes < 64)
|| (new_mtu + 14 + 4 + vlan_bytes > 65392)) {
pr_err("MTU must be between %d and %d.\n",
64 - 14 - 4 - vlan_bytes, 65392 - 14 - 4 - vlan_bytes);
return -EINVAL;
}
dev->mtu = new_mtu;
if ((interface < 2)
&& (cvmx_helper_interface_get_mode(interface) !=
CVMX_HELPER_INTERFACE_MODE_SPI)) {
/* Add ethernet header and FCS, and VLAN if configured. */
int max_packet = new_mtu + 14 + 4 + vlan_bytes;
if (OCTEON_IS_MODEL(OCTEON_CN3XXX)
|| OCTEON_IS_MODEL(OCTEON_CN58XX)) {
/* Signal errors on packets larger than the MTU */
cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(index, interface),
max_packet);
} else {
/*
* Set the hardware to truncate packets larger
* than the MTU and smaller the 64 bytes.
*/
union cvmx_pip_frm_len_chkx frm_len_chk;
frm_len_chk.u64 = 0;
frm_len_chk.s.minlen = 64;
frm_len_chk.s.maxlen = max_packet;
cvmx_write_csr(CVMX_PIP_FRM_LEN_CHKX(interface),
frm_len_chk.u64);
}
/*
* Set the hardware to truncate packets larger than
* the MTU. The jabber register must be set to a
* multiple of 8 bytes, so round up.
*/
cvmx_write_csr(CVMX_GMXX_RXX_JABBER(index, interface),
(max_packet + 7) & ~7u);
}
return 0;
}
/**
* Per network device initialization
*
* @dev: Device to initialize
* Returns Zero on success
*/
int cvm_oct_common_init(struct net_device *dev)
{
static int count;
char mac[8] = { 0x00, 0x00,
octeon_bootinfo->mac_addr_base[0],
octeon_bootinfo->mac_addr_base[1],
octeon_bootinfo->mac_addr_base[2],
octeon_bootinfo->mac_addr_base[3],
octeon_bootinfo->mac_addr_base[4],
octeon_bootinfo->mac_addr_base[5] + count
};
struct octeon_ethernet *priv = netdev_priv(dev);
/*
* Force the interface to use the POW send if always_use_pow
* was specified or it is in the pow send list.
*/
if ((pow_send_group != -1)
&& (always_use_pow || strstr(pow_send_list, dev->name)))
priv->queue = -1;
if (priv->queue != -1) {
dev->hard_start_xmit = cvm_oct_xmit;
if (USE_HW_TCPUDP_CHECKSUM)
dev->features |= NETIF_F_IP_CSUM;
} else
dev->hard_start_xmit = cvm_oct_xmit_pow;
count++;
dev->get_stats = cvm_oct_common_get_stats;
dev->set_mac_address = cvm_oct_common_set_mac_address;
dev->set_multicast_list = cvm_oct_common_set_multicast_list;
dev->change_mtu = cvm_oct_common_change_mtu;
dev->do_ioctl = cvm_oct_ioctl;
/* We do our own locking, Linux doesn't need to */
dev->features |= NETIF_F_LLTX;
SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = cvm_oct_poll_controller;
#endif
cvm_oct_mdio_setup_device(dev);
dev->set_mac_address(dev, mac);
dev->change_mtu(dev, dev->mtu);
/*
* Zero out stats for port so we won't mistakenly show
* counters from the bootloader.
*/
memset(dev->get_stats(dev), 0, sizeof(struct net_device_stats));
return 0;
}
void cvm_oct_common_uninit(struct net_device *dev)
{
/* Currently nothing to do */
}
/*********************************************************************
* Author: Cavium Networks
*
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
* Copyright (c) 2003-2007 Cavium Networks
*
* This file 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.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* or visit http://www.gnu.org/licenses/.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
*********************************************************************/
int cvm_oct_common_init(struct net_device *dev);
void cvm_oct_common_uninit(struct net_device *dev);
...@@ -117,6 +117,8 @@ ...@@ -117,6 +117,8 @@
/* Maximum number of packets to process per interrupt. */ /* Maximum number of packets to process per interrupt. */
#define MAX_RX_PACKETS 120 #define MAX_RX_PACKETS 120
/* Maximum number of SKBs to try to free per xmit packet. */
#define MAX_SKB_TO_FREE 10
#define MAX_OUT_QUEUE_DEPTH 1000 #define MAX_OUT_QUEUE_DEPTH 1000
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
......
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
#include "ethernet-defines.h" #include "ethernet-defines.h"
#include "octeon-ethernet.h" #include "octeon-ethernet.h"
#include "ethernet-common.h"
#include "ethernet-util.h" #include "ethernet-util.h"
#include "cvmx-helper.h" #include "cvmx-helper.h"
...@@ -265,7 +264,7 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id) ...@@ -265,7 +264,7 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
return return_status; return return_status;
} }
static int cvm_oct_rgmii_open(struct net_device *dev) int cvm_oct_rgmii_open(struct net_device *dev)
{ {
union cvmx_gmxx_prtx_cfg gmx_cfg; union cvmx_gmxx_prtx_cfg gmx_cfg;
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
...@@ -286,7 +285,7 @@ static int cvm_oct_rgmii_open(struct net_device *dev) ...@@ -286,7 +285,7 @@ static int cvm_oct_rgmii_open(struct net_device *dev)
return 0; return 0;
} }
static int cvm_oct_rgmii_stop(struct net_device *dev) int cvm_oct_rgmii_stop(struct net_device *dev)
{ {
union cvmx_gmxx_prtx_cfg gmx_cfg; union cvmx_gmxx_prtx_cfg gmx_cfg;
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
...@@ -305,9 +304,7 @@ int cvm_oct_rgmii_init(struct net_device *dev) ...@@ -305,9 +304,7 @@ int cvm_oct_rgmii_init(struct net_device *dev)
int r; int r;
cvm_oct_common_init(dev); cvm_oct_common_init(dev);
dev->open = cvm_oct_rgmii_open; dev->netdev_ops->ndo_stop(dev);
dev->stop = cvm_oct_rgmii_stop;
dev->stop(dev);
/* /*
* Due to GMX errata in CN3XXX series chips, it is necessary * Due to GMX errata in CN3XXX series chips, it is necessary
......
...@@ -34,13 +34,12 @@ ...@@ -34,13 +34,12 @@
#include "ethernet-defines.h" #include "ethernet-defines.h"
#include "octeon-ethernet.h" #include "octeon-ethernet.h"
#include "ethernet-util.h" #include "ethernet-util.h"
#include "ethernet-common.h"
#include "cvmx-helper.h" #include "cvmx-helper.h"
#include "cvmx-gmxx-defs.h" #include "cvmx-gmxx-defs.h"
static int cvm_oct_sgmii_open(struct net_device *dev) int cvm_oct_sgmii_open(struct net_device *dev)
{ {
union cvmx_gmxx_prtx_cfg gmx_cfg; union cvmx_gmxx_prtx_cfg gmx_cfg;
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
...@@ -61,7 +60,7 @@ static int cvm_oct_sgmii_open(struct net_device *dev) ...@@ -61,7 +60,7 @@ static int cvm_oct_sgmii_open(struct net_device *dev)
return 0; return 0;
} }
static int cvm_oct_sgmii_stop(struct net_device *dev) int cvm_oct_sgmii_stop(struct net_device *dev)
{ {
union cvmx_gmxx_prtx_cfg gmx_cfg; union cvmx_gmxx_prtx_cfg gmx_cfg;
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
...@@ -113,9 +112,7 @@ int cvm_oct_sgmii_init(struct net_device *dev) ...@@ -113,9 +112,7 @@ int cvm_oct_sgmii_init(struct net_device *dev)
{ {
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
cvm_oct_common_init(dev); cvm_oct_common_init(dev);
dev->open = cvm_oct_sgmii_open; dev->netdev_ops->ndo_stop(dev);
dev->stop = cvm_oct_sgmii_stop;
dev->stop(dev);
if (!octeon_is_simulation()) if (!octeon_is_simulation())
priv->poll = cvm_oct_sgmii_poll; priv->poll = cvm_oct_sgmii_poll;
......
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
#include "ethernet-defines.h" #include "ethernet-defines.h"
#include "octeon-ethernet.h" #include "octeon-ethernet.h"
#include "ethernet-common.h"
#include "ethernet-util.h" #include "ethernet-util.h"
#include "cvmx-spi.h" #include "cvmx-spi.h"
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include "ethernet-defines.h" #include "ethernet-defines.h"
#include "octeon-ethernet.h" #include "octeon-ethernet.h"
#include "ethernet-tx.h"
#include "ethernet-util.h" #include "ethernet-util.h"
#include "cvmx-wqe.h" #include "cvmx-wqe.h"
...@@ -82,8 +83,10 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -82,8 +83,10 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
uint64_t old_scratch2; uint64_t old_scratch2;
int dropped; int dropped;
int qos; int qos;
int queue_it_up;
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
int32_t in_use; int32_t skb_to_free;
int32_t undo;
int32_t buffers_to_free; int32_t buffers_to_free;
#if REUSE_SKBUFFS_WITHOUT_FREE #if REUSE_SKBUFFS_WITHOUT_FREE
unsigned char *fpa_head; unsigned char *fpa_head;
...@@ -120,15 +123,15 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -120,15 +123,15 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8); old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);
/* /*
* Assume we're going to be able t osend this * Fetch and increment the number of packets to be
* packet. Fetch and increment the number of pending * freed.
* packets for output.
*/ */
cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH + 8, cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH + 8,
FAU_NUM_PACKET_BUFFERS_TO_FREE, FAU_NUM_PACKET_BUFFERS_TO_FREE,
0); 0);
cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH,
priv->fau + qos * 4, 1); priv->fau + qos * 4,
MAX_SKB_TO_FREE);
} }
/* /*
...@@ -253,10 +256,10 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -253,10 +256,10 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
/* /*
* The skbuff will be reused without ever being freed. We must * The skbuff will be reused without ever being freed. We must
* cleanup a bunch of Linux stuff. * cleanup a bunch of core things.
*/ */
dst_release(skb->dst); dst_release(skb_dst(skb));
skb->dst = NULL; skb_dst_set(skb, NULL);
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
secpath_put(skb->sp); secpath_put(skb->sp);
skb->sp = NULL; skb->sp = NULL;
...@@ -286,15 +289,29 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -286,15 +289,29 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
if (USE_ASYNC_IOBDMA) { if (USE_ASYNC_IOBDMA) {
/* Get the number of skbuffs in use by the hardware */ /* Get the number of skbuffs in use by the hardware */
CVMX_SYNCIOBDMA; CVMX_SYNCIOBDMA;
in_use = cvmx_scratch_read64(CVMX_SCR_SCRATCH); skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8); buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);
} else { } else {
/* Get the number of skbuffs in use by the hardware */ /* Get the number of skbuffs in use by the hardware */
in_use = cvmx_fau_fetch_and_add32(priv->fau + qos * 4, 1); skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4,
MAX_SKB_TO_FREE);
buffers_to_free = buffers_to_free =
cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
} }
/*
* We try to claim MAX_SKB_TO_FREE buffers. If there were not
* that many available, we have to un-claim (undo) any that
* were in excess. If skb_to_free is positive we will free
* that many buffers.
*/
undo = skb_to_free > 0 ?
MAX_SKB_TO_FREE : skb_to_free + MAX_SKB_TO_FREE;
if (undo > 0)
cvmx_fau_atomic_add32(priv->fau+qos*4, -undo);
skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ?
MAX_SKB_TO_FREE : -skb_to_free;
/* /*
* If we're sending faster than the receive can free them then * If we're sending faster than the receive can free them then
* don't do the HW free. * don't do the HW free.
...@@ -330,38 +347,31 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -330,38 +347,31 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2); cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2);
} }
queue_it_up = 0;
if (unlikely(dropped)) { if (unlikely(dropped)) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
cvmx_fau_atomic_add32(priv->fau + qos * 4, -1);
priv->stats.tx_dropped++; priv->stats.tx_dropped++;
} else { } else {
if (USE_SKBUFFS_IN_HW) { if (USE_SKBUFFS_IN_HW) {
/* Put this packet on the queue to be freed later */ /* Put this packet on the queue to be freed later */
if (pko_command.s.dontfree) if (pko_command.s.dontfree)
skb_queue_tail(&priv->tx_free_list[qos], skb); queue_it_up = 1;
else { else
cvmx_fau_atomic_add32 cvmx_fau_atomic_add32
(FAU_NUM_PACKET_BUFFERS_TO_FREE, -1); (FAU_NUM_PACKET_BUFFERS_TO_FREE, -1);
cvmx_fau_atomic_add32(priv->fau + qos * 4, -1);
}
} else { } else {
/* Put this packet on the queue to be freed later */ /* Put this packet on the queue to be freed later */
skb_queue_tail(&priv->tx_free_list[qos], skb); queue_it_up = 1;
} }
} }
/* Free skbuffs not in use by the hardware, possibly two at a time */ if (queue_it_up) {
if (skb_queue_len(&priv->tx_free_list[qos]) > in_use) {
spin_lock(&priv->tx_free_list[qos].lock); spin_lock(&priv->tx_free_list[qos].lock);
/* __skb_queue_tail(&priv->tx_free_list[qos], skb);
* Check again now that we have the lock. It might cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 0);
* have changed.
*/
if (skb_queue_len(&priv->tx_free_list[qos]) > in_use)
dev_kfree_skb(__skb_dequeue(&priv->tx_free_list[qos]));
if (skb_queue_len(&priv->tx_free_list[qos]) > in_use)
dev_kfree_skb(__skb_dequeue(&priv->tx_free_list[qos]));
spin_unlock(&priv->tx_free_list[qos].lock); spin_unlock(&priv->tx_free_list[qos].lock);
} else {
cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 1);
} }
return 0; return 0;
......
...@@ -30,3 +30,28 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev); ...@@ -30,3 +30,28 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev);
int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry, int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry,
int do_free, int qos); int do_free, int qos);
void cvm_oct_tx_shutdown(struct net_device *dev); void cvm_oct_tx_shutdown(struct net_device *dev);
/**
* Free dead transmit skbs.
*
* @priv: The driver data
* @skb_to_free: The number of SKBs to free (free none if negative).
* @qos: The queue to free from.
* @take_lock: If true, acquire the skb list lock.
*/
static inline void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv,
int skb_to_free,
int qos, int take_lock)
{
/* Free skbuffs not in use by the hardware. */
if (skb_to_free > 0) {
if (take_lock)
spin_lock(&priv->tx_free_list[qos].lock);
while (skb_to_free > 0) {
dev_kfree_skb(__skb_dequeue(&priv->tx_free_list[qos]));
skb_to_free--;
}
if (take_lock)
spin_unlock(&priv->tx_free_list[qos].lock);
}
}
...@@ -33,14 +33,13 @@ ...@@ -33,14 +33,13 @@
#include "ethernet-defines.h" #include "ethernet-defines.h"
#include "octeon-ethernet.h" #include "octeon-ethernet.h"
#include "ethernet-common.h"
#include "ethernet-util.h" #include "ethernet-util.h"
#include "cvmx-helper.h" #include "cvmx-helper.h"
#include "cvmx-gmxx-defs.h" #include "cvmx-gmxx-defs.h"
static int cvm_oct_xaui_open(struct net_device *dev) int cvm_oct_xaui_open(struct net_device *dev)
{ {
union cvmx_gmxx_prtx_cfg gmx_cfg; union cvmx_gmxx_prtx_cfg gmx_cfg;
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
...@@ -60,7 +59,7 @@ static int cvm_oct_xaui_open(struct net_device *dev) ...@@ -60,7 +59,7 @@ static int cvm_oct_xaui_open(struct net_device *dev)
return 0; return 0;
} }
static int cvm_oct_xaui_stop(struct net_device *dev) int cvm_oct_xaui_stop(struct net_device *dev)
{ {
union cvmx_gmxx_prtx_cfg gmx_cfg; union cvmx_gmxx_prtx_cfg gmx_cfg;
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
...@@ -112,9 +111,7 @@ int cvm_oct_xaui_init(struct net_device *dev) ...@@ -112,9 +111,7 @@ int cvm_oct_xaui_init(struct net_device *dev)
{ {
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
cvm_oct_common_init(dev); cvm_oct_common_init(dev);
dev->open = cvm_oct_xaui_open; dev->netdev_ops->ndo_stop(dev);
dev->stop = cvm_oct_xaui_stop;
dev->stop(dev);
if (!octeon_is_simulation()) if (!octeon_is_simulation())
priv->poll = cvm_oct_xaui_poll; priv->poll = cvm_oct_xaui_poll;
......
...@@ -37,13 +37,14 @@ ...@@ -37,13 +37,14 @@
#include <asm/octeon/octeon.h> #include <asm/octeon/octeon.h>
#include "ethernet-defines.h" #include "ethernet-defines.h"
#include "octeon-ethernet.h"
#include "ethernet-mem.h" #include "ethernet-mem.h"
#include "ethernet-rx.h" #include "ethernet-rx.h"
#include "ethernet-tx.h" #include "ethernet-tx.h"
#include "ethernet-mdio.h"
#include "ethernet-util.h" #include "ethernet-util.h"
#include "ethernet-proc.h" #include "ethernet-proc.h"
#include "ethernet-common.h"
#include "octeon-ethernet.h"
#include "cvmx-pip.h" #include "cvmx-pip.h"
#include "cvmx-pko.h" #include "cvmx-pko.h"
...@@ -51,6 +52,7 @@ ...@@ -51,6 +52,7 @@
#include "cvmx-ipd.h" #include "cvmx-ipd.h"
#include "cvmx-helper.h" #include "cvmx-helper.h"
#include "cvmx-gmxx-defs.h"
#include "cvmx-smix-defs.h" #include "cvmx-smix-defs.h"
#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) \ #if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) \
...@@ -129,13 +131,25 @@ extern struct semaphore mdio_sem; ...@@ -129,13 +131,25 @@ extern struct semaphore mdio_sem;
*/ */
static void cvm_do_timer(unsigned long arg) static void cvm_do_timer(unsigned long arg)
{ {
static int port; int32_t skb_to_free, undo;
if (port < CVMX_PIP_NUM_INPUT_PORTS) {
if (cvm_oct_device[port]) {
int queues_per_port; int queues_per_port;
int qos; int qos;
struct octeon_ethernet *priv = struct octeon_ethernet *priv;
netdev_priv(cvm_oct_device[port]); static int port;
if (port >= CVMX_PIP_NUM_INPUT_PORTS) {
/*
* All ports have been polled. Start the next
* iteration through the ports in one second.
*/
port = 0;
mod_timer(&cvm_oct_poll_timer, jiffies + HZ);
return;
}
if (!cvm_oct_device[port])
goto out;
priv = netdev_priv(cvm_oct_device[port]);
if (priv->poll) { if (priv->poll) {
/* skip polling if we don't get the lock */ /* skip polling if we don't get the lock */
if (!down_trylock(&mdio_sem)) { if (!down_trylock(&mdio_sem)) {
...@@ -147,35 +161,25 @@ static void cvm_do_timer(unsigned long arg) ...@@ -147,35 +161,25 @@ static void cvm_do_timer(unsigned long arg)
queues_per_port = cvmx_pko_get_num_queues(port); queues_per_port = cvmx_pko_get_num_queues(port);
/* Drain any pending packets in the free list */ /* Drain any pending packets in the free list */
for (qos = 0; qos < queues_per_port; qos++) { for (qos = 0; qos < queues_per_port; qos++) {
if (skb_queue_len(&priv->tx_free_list[qos])) { if (skb_queue_len(&priv->tx_free_list[qos]) == 0)
spin_lock(&priv->tx_free_list[qos]. continue;
lock); skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4,
while (skb_queue_len MAX_SKB_TO_FREE);
(&priv->tx_free_list[qos]) > undo = skb_to_free > 0 ?
cvmx_fau_fetch_and_add32(priv-> MAX_SKB_TO_FREE : skb_to_free + MAX_SKB_TO_FREE;
fau + if (undo > 0)
qos * 4, cvmx_fau_atomic_add32(priv->fau+qos*4, -undo);
0)) skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ?
dev_kfree_skb(__skb_dequeue MAX_SKB_TO_FREE : -skb_to_free;
(&priv-> cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 1);
tx_free_list
[qos]));
spin_unlock(&priv->tx_free_list[qos].
lock);
}
}
cvm_oct_device[port]->get_stats(cvm_oct_device[port]);
} }
cvm_oct_device[port]->netdev_ops->ndo_get_stats(cvm_oct_device[port]);
out:
port++; port++;
/* Poll the next port in a 50th of a second. /* Poll the next port in a 50th of a second.
This spreads the polling of ports out a little bit */ This spreads the polling of ports out a little bit */
mod_timer(&cvm_oct_poll_timer, jiffies + HZ / 50); mod_timer(&cvm_oct_poll_timer, jiffies + HZ / 50);
} else {
port = 0;
/* All ports have been polled. Start the next iteration through
the ports in one second */
mod_timer(&cvm_oct_poll_timer, jiffies + HZ);
}
} }
/** /**
...@@ -245,6 +249,362 @@ int cvm_oct_free_work(void *work_queue_entry) ...@@ -245,6 +249,362 @@ int cvm_oct_free_work(void *work_queue_entry)
} }
EXPORT_SYMBOL(cvm_oct_free_work); EXPORT_SYMBOL(cvm_oct_free_work);
/**
* Get the low level ethernet statistics
*
* @dev: Device to get the statistics from
* Returns Pointer to the statistics
*/
static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev)
{
cvmx_pip_port_status_t rx_status;
cvmx_pko_port_status_t tx_status;
struct octeon_ethernet *priv = netdev_priv(dev);
if (priv->port < CVMX_PIP_NUM_INPUT_PORTS) {
if (octeon_is_simulation()) {
/* The simulator doesn't support statistics */
memset(&rx_status, 0, sizeof(rx_status));
memset(&tx_status, 0, sizeof(tx_status));
} else {
cvmx_pip_get_port_status(priv->port, 1, &rx_status);
cvmx_pko_get_port_status(priv->port, 1, &tx_status);
}
priv->stats.rx_packets += rx_status.inb_packets;
priv->stats.tx_packets += tx_status.packets;
priv->stats.rx_bytes += rx_status.inb_octets;
priv->stats.tx_bytes += tx_status.octets;
priv->stats.multicast += rx_status.multicast_packets;
priv->stats.rx_crc_errors += rx_status.inb_errors;
priv->stats.rx_frame_errors += rx_status.fcs_align_err_packets;
/*
* The drop counter must be incremented atomically
* since the RX tasklet also increments it.
*/
#ifdef CONFIG_64BIT
atomic64_add(rx_status.dropped_packets,
(atomic64_t *)&priv->stats.rx_dropped);
#else
atomic_add(rx_status.dropped_packets,
(atomic_t *)&priv->stats.rx_dropped);
#endif
}
return &priv->stats;
}
/**
* Change the link MTU. Unimplemented
*
* @dev: Device to change
* @new_mtu: The new MTU
*
* Returns Zero on success
*/
static int cvm_oct_common_change_mtu(struct net_device *dev, int new_mtu)
{
struct octeon_ethernet *priv = netdev_priv(dev);
int interface = INTERFACE(priv->port);
int index = INDEX(priv->port);
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
int vlan_bytes = 4;
#else
int vlan_bytes = 0;
#endif
/*
* Limit the MTU to make sure the ethernet packets are between
* 64 bytes and 65535 bytes.
*/
if ((new_mtu + 14 + 4 + vlan_bytes < 64)
|| (new_mtu + 14 + 4 + vlan_bytes > 65392)) {
pr_err("MTU must be between %d and %d.\n",
64 - 14 - 4 - vlan_bytes, 65392 - 14 - 4 - vlan_bytes);
return -EINVAL;
}
dev->mtu = new_mtu;
if ((interface < 2)
&& (cvmx_helper_interface_get_mode(interface) !=
CVMX_HELPER_INTERFACE_MODE_SPI)) {
/* Add ethernet header and FCS, and VLAN if configured. */
int max_packet = new_mtu + 14 + 4 + vlan_bytes;
if (OCTEON_IS_MODEL(OCTEON_CN3XXX)
|| OCTEON_IS_MODEL(OCTEON_CN58XX)) {
/* Signal errors on packets larger than the MTU */
cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(index, interface),
max_packet);
} else {
/*
* Set the hardware to truncate packets larger
* than the MTU and smaller the 64 bytes.
*/
union cvmx_pip_frm_len_chkx frm_len_chk;
frm_len_chk.u64 = 0;
frm_len_chk.s.minlen = 64;
frm_len_chk.s.maxlen = max_packet;
cvmx_write_csr(CVMX_PIP_FRM_LEN_CHKX(interface),
frm_len_chk.u64);
}
/*
* Set the hardware to truncate packets larger than
* the MTU. The jabber register must be set to a
* multiple of 8 bytes, so round up.
*/
cvmx_write_csr(CVMX_GMXX_RXX_JABBER(index, interface),
(max_packet + 7) & ~7u);
}
return 0;
}
/**
* Set the multicast list. Currently unimplemented.
*
* @dev: Device to work on
*/
static void cvm_oct_common_set_multicast_list(struct net_device *dev)
{
union cvmx_gmxx_prtx_cfg gmx_cfg;
struct octeon_ethernet *priv = netdev_priv(dev);
int interface = INTERFACE(priv->port);
int index = INDEX(priv->port);
if ((interface < 2)
&& (cvmx_helper_interface_get_mode(interface) !=
CVMX_HELPER_INTERFACE_MODE_SPI)) {
union cvmx_gmxx_rxx_adr_ctl control;
control.u64 = 0;
control.s.bcst = 1; /* Allow broadcast MAC addresses */
if (dev->mc_list || (dev->flags & IFF_ALLMULTI) ||
(dev->flags & IFF_PROMISC))
/* Force accept multicast packets */
control.s.mcst = 2;
else
/* Force reject multicat packets */
control.s.mcst = 1;
if (dev->flags & IFF_PROMISC)
/*
* Reject matches if promisc. Since CAM is
* shut off, should accept everything.
*/
control.s.cam_mode = 0;
else
/* Filter packets based on the CAM */
control.s.cam_mode = 1;
gmx_cfg.u64 =
cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
gmx_cfg.u64 & ~1ull);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CTL(index, interface),
control.u64);
if (dev->flags & IFF_PROMISC)
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN
(index, interface), 0);
else
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN
(index, interface), 1);
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
gmx_cfg.u64);
}
}
/**
* Set the hardware MAC address for a device
*
* @dev: Device to change the MAC address for
* @addr: Address structure to change it too. MAC address is addr + 2.
* Returns Zero on success
*/
static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
{
struct octeon_ethernet *priv = netdev_priv(dev);
union cvmx_gmxx_prtx_cfg gmx_cfg;
int interface = INTERFACE(priv->port);
int index = INDEX(priv->port);
memcpy(dev->dev_addr, addr + 2, 6);
if ((interface < 2)
&& (cvmx_helper_interface_get_mode(interface) !=
CVMX_HELPER_INTERFACE_MODE_SPI)) {
int i;
uint8_t *ptr = addr;
uint64_t mac = 0;
for (i = 0; i < 6; i++)
mac = (mac << 8) | (uint64_t) (ptr[i + 2]);
gmx_cfg.u64 =
cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
gmx_cfg.u64 & ~1ull);
cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface),
ptr[2]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface),
ptr[3]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface),
ptr[4]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface),
ptr[5]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface),
ptr[6]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface),
ptr[7]);
cvm_oct_common_set_multicast_list(dev);
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
gmx_cfg.u64);
}
return 0;
}
/**
* Per network device initialization
*
* @dev: Device to initialize
* Returns Zero on success
*/
int cvm_oct_common_init(struct net_device *dev)
{
static int count;
char mac[8] = { 0x00, 0x00,
octeon_bootinfo->mac_addr_base[0],
octeon_bootinfo->mac_addr_base[1],
octeon_bootinfo->mac_addr_base[2],
octeon_bootinfo->mac_addr_base[3],
octeon_bootinfo->mac_addr_base[4],
octeon_bootinfo->mac_addr_base[5] + count
};
struct octeon_ethernet *priv = netdev_priv(dev);
/*
* Force the interface to use the POW send if always_use_pow
* was specified or it is in the pow send list.
*/
if ((pow_send_group != -1)
&& (always_use_pow || strstr(pow_send_list, dev->name)))
priv->queue = -1;
if (priv->queue != -1 && USE_HW_TCPUDP_CHECKSUM)
dev->features |= NETIF_F_IP_CSUM;
count++;
/* We do our own locking, Linux doesn't need to */
dev->features |= NETIF_F_LLTX;
SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
cvm_oct_mdio_setup_device(dev);
dev->netdev_ops->ndo_set_mac_address(dev, mac);
dev->netdev_ops->ndo_change_mtu(dev, dev->mtu);
/*
* Zero out stats for port so we won't mistakenly show
* counters from the bootloader.
*/
memset(dev->netdev_ops->ndo_get_stats(dev), 0,
sizeof(struct net_device_stats));
return 0;
}
void cvm_oct_common_uninit(struct net_device *dev)
{
/* Currently nothing to do */
}
static const struct net_device_ops cvm_oct_npi_netdev_ops = {
.ndo_init = cvm_oct_common_init,
.ndo_uninit = cvm_oct_common_uninit,
.ndo_start_xmit = cvm_oct_xmit,
.ndo_set_multicast_list = cvm_oct_common_set_multicast_list,
.ndo_set_mac_address = cvm_oct_common_set_mac_address,
.ndo_do_ioctl = cvm_oct_ioctl,
.ndo_change_mtu = cvm_oct_common_change_mtu,
.ndo_get_stats = cvm_oct_common_get_stats,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cvm_oct_poll_controller,
#endif
};
static const struct net_device_ops cvm_oct_xaui_netdev_ops = {
.ndo_init = cvm_oct_xaui_init,
.ndo_uninit = cvm_oct_xaui_uninit,
.ndo_open = cvm_oct_xaui_open,
.ndo_stop = cvm_oct_xaui_stop,
.ndo_start_xmit = cvm_oct_xmit,
.ndo_set_multicast_list = cvm_oct_common_set_multicast_list,
.ndo_set_mac_address = cvm_oct_common_set_mac_address,
.ndo_do_ioctl = cvm_oct_ioctl,
.ndo_change_mtu = cvm_oct_common_change_mtu,
.ndo_get_stats = cvm_oct_common_get_stats,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cvm_oct_poll_controller,
#endif
};
static const struct net_device_ops cvm_oct_sgmii_netdev_ops = {
.ndo_init = cvm_oct_sgmii_init,
.ndo_uninit = cvm_oct_sgmii_uninit,
.ndo_open = cvm_oct_sgmii_open,
.ndo_stop = cvm_oct_sgmii_stop,
.ndo_start_xmit = cvm_oct_xmit,
.ndo_set_multicast_list = cvm_oct_common_set_multicast_list,
.ndo_set_mac_address = cvm_oct_common_set_mac_address,
.ndo_do_ioctl = cvm_oct_ioctl,
.ndo_change_mtu = cvm_oct_common_change_mtu,
.ndo_get_stats = cvm_oct_common_get_stats,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cvm_oct_poll_controller,
#endif
};
static const struct net_device_ops cvm_oct_spi_netdev_ops = {
.ndo_init = cvm_oct_spi_init,
.ndo_uninit = cvm_oct_spi_uninit,
.ndo_start_xmit = cvm_oct_xmit,
.ndo_set_multicast_list = cvm_oct_common_set_multicast_list,
.ndo_set_mac_address = cvm_oct_common_set_mac_address,
.ndo_do_ioctl = cvm_oct_ioctl,
.ndo_change_mtu = cvm_oct_common_change_mtu,
.ndo_get_stats = cvm_oct_common_get_stats,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cvm_oct_poll_controller,
#endif
};
static const struct net_device_ops cvm_oct_rgmii_netdev_ops = {
.ndo_init = cvm_oct_rgmii_init,
.ndo_uninit = cvm_oct_rgmii_uninit,
.ndo_open = cvm_oct_rgmii_open,
.ndo_stop = cvm_oct_rgmii_stop,
.ndo_start_xmit = cvm_oct_xmit,
.ndo_set_multicast_list = cvm_oct_common_set_multicast_list,
.ndo_set_mac_address = cvm_oct_common_set_mac_address,
.ndo_do_ioctl = cvm_oct_ioctl,
.ndo_change_mtu = cvm_oct_common_change_mtu,
.ndo_get_stats = cvm_oct_common_get_stats,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cvm_oct_poll_controller,
#endif
};
static const struct net_device_ops cvm_oct_pow_netdev_ops = {
.ndo_init = cvm_oct_common_init,
.ndo_start_xmit = cvm_oct_xmit_pow,
.ndo_set_multicast_list = cvm_oct_common_set_multicast_list,
.ndo_set_mac_address = cvm_oct_common_set_mac_address,
.ndo_do_ioctl = cvm_oct_ioctl,
.ndo_change_mtu = cvm_oct_common_change_mtu,
.ndo_get_stats = cvm_oct_common_get_stats,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cvm_oct_poll_controller,
#endif
};
/** /**
* Module/ driver initialization. Creates the linux network * Module/ driver initialization. Creates the linux network
* devices. * devices.
...@@ -303,7 +663,7 @@ static int __init cvm_oct_init_module(void) ...@@ -303,7 +663,7 @@ static int __init cvm_oct_init_module(void)
struct octeon_ethernet *priv = netdev_priv(dev); struct octeon_ethernet *priv = netdev_priv(dev);
memset(priv, 0, sizeof(struct octeon_ethernet)); memset(priv, 0, sizeof(struct octeon_ethernet));
dev->init = cvm_oct_common_init; dev->netdev_ops = &cvm_oct_pow_netdev_ops;
priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED; priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
priv->port = CVMX_PIP_NUM_INPUT_PORTS; priv->port = CVMX_PIP_NUM_INPUT_PORTS;
priv->queue = -1; priv->queue = -1;
...@@ -372,44 +732,38 @@ static int __init cvm_oct_init_module(void) ...@@ -372,44 +732,38 @@ static int __init cvm_oct_init_module(void)
break; break;
case CVMX_HELPER_INTERFACE_MODE_NPI: case CVMX_HELPER_INTERFACE_MODE_NPI:
dev->init = cvm_oct_common_init; dev->netdev_ops = &cvm_oct_npi_netdev_ops;
dev->uninit = cvm_oct_common_uninit;
strcpy(dev->name, "npi%d"); strcpy(dev->name, "npi%d");
break; break;
case CVMX_HELPER_INTERFACE_MODE_XAUI: case CVMX_HELPER_INTERFACE_MODE_XAUI:
dev->init = cvm_oct_xaui_init; dev->netdev_ops = &cvm_oct_xaui_netdev_ops;
dev->uninit = cvm_oct_xaui_uninit;
strcpy(dev->name, "xaui%d"); strcpy(dev->name, "xaui%d");
break; break;
case CVMX_HELPER_INTERFACE_MODE_LOOP: case CVMX_HELPER_INTERFACE_MODE_LOOP:
dev->init = cvm_oct_common_init; dev->netdev_ops = &cvm_oct_npi_netdev_ops;
dev->uninit = cvm_oct_common_uninit;
strcpy(dev->name, "loop%d"); strcpy(dev->name, "loop%d");
break; break;
case CVMX_HELPER_INTERFACE_MODE_SGMII: case CVMX_HELPER_INTERFACE_MODE_SGMII:
dev->init = cvm_oct_sgmii_init; dev->netdev_ops = &cvm_oct_sgmii_netdev_ops;
dev->uninit = cvm_oct_sgmii_uninit;
strcpy(dev->name, "eth%d"); strcpy(dev->name, "eth%d");
break; break;
case CVMX_HELPER_INTERFACE_MODE_SPI: case CVMX_HELPER_INTERFACE_MODE_SPI:
dev->init = cvm_oct_spi_init; dev->netdev_ops = &cvm_oct_spi_netdev_ops;
dev->uninit = cvm_oct_spi_uninit;
strcpy(dev->name, "spi%d"); strcpy(dev->name, "spi%d");
break; break;
case CVMX_HELPER_INTERFACE_MODE_RGMII: case CVMX_HELPER_INTERFACE_MODE_RGMII:
case CVMX_HELPER_INTERFACE_MODE_GMII: case CVMX_HELPER_INTERFACE_MODE_GMII:
dev->init = cvm_oct_rgmii_init; dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
dev->uninit = cvm_oct_rgmii_uninit;
strcpy(dev->name, "eth%d"); strcpy(dev->name, "eth%d");
break; break;
} }
if (!dev->init) { if (!dev->netdev_ops) {
kfree(dev); kfree(dev);
} else if (register_netdev(dev) < 0) { } else if (register_netdev(dev) < 0) {
pr_err("Failed to register ethernet device " pr_err("Failed to register ethernet device "
......
...@@ -111,12 +111,23 @@ static inline int cvm_oct_transmit(struct net_device *dev, ...@@ -111,12 +111,23 @@ static inline int cvm_oct_transmit(struct net_device *dev,
extern int cvm_oct_rgmii_init(struct net_device *dev); extern int cvm_oct_rgmii_init(struct net_device *dev);
extern void cvm_oct_rgmii_uninit(struct net_device *dev); extern void cvm_oct_rgmii_uninit(struct net_device *dev);
extern int cvm_oct_rgmii_open(struct net_device *dev);
extern int cvm_oct_rgmii_stop(struct net_device *dev);
extern int cvm_oct_sgmii_init(struct net_device *dev); extern int cvm_oct_sgmii_init(struct net_device *dev);
extern void cvm_oct_sgmii_uninit(struct net_device *dev); extern void cvm_oct_sgmii_uninit(struct net_device *dev);
extern int cvm_oct_sgmii_open(struct net_device *dev);
extern int cvm_oct_sgmii_stop(struct net_device *dev);
extern int cvm_oct_spi_init(struct net_device *dev); extern int cvm_oct_spi_init(struct net_device *dev);
extern void cvm_oct_spi_uninit(struct net_device *dev); extern void cvm_oct_spi_uninit(struct net_device *dev);
extern int cvm_oct_xaui_init(struct net_device *dev); extern int cvm_oct_xaui_init(struct net_device *dev);
extern void cvm_oct_xaui_uninit(struct net_device *dev); extern void cvm_oct_xaui_uninit(struct net_device *dev);
extern int cvm_oct_xaui_open(struct net_device *dev);
extern int cvm_oct_xaui_stop(struct net_device *dev);
extern int cvm_oct_common_init(struct net_device *dev);
extern void cvm_oct_common_uninit(struct net_device *dev);
extern int always_use_pow; extern int always_use_pow;
extern int pow_send_group; extern int pow_send_group;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment