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
select SYS_SUPPORTS_64BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_HIGHMEM
select SYS_SUPPORTS_HOTPLUG_CPU
select SYS_HAS_CPU_CAVIUM_OCTEON
help
The Octeon simulator is software performance model of the Cavium
......@@ -615,6 +616,7 @@ config CAVIUM_OCTEON_REFERENCE_BOARD
select SYS_SUPPORTS_64BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_HIGHMEM
select SYS_SUPPORTS_HOTPLUG_CPU
select SYS_HAS_EARLY_PRINTK
select SYS_HAS_CPU_CAVIUM_OCTEON
select SWAP_IO_SPACE
......@@ -784,8 +786,17 @@ config SYS_HAS_EARLY_PRINTK
bool
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
default n
config I8259
bool
......@@ -2136,11 +2147,11 @@ menu "Power management options"
config ARCH_HIBERNATION_POSSIBLE
def_bool y
depends on !SMP
depends on SYS_SUPPORTS_HOTPLUG_CPU
config ARCH_SUSPEND_POSSIBLE
def_bool y
depends on !SMP
depends on SYS_SUPPORTS_HOTPLUG_CPU
source "kernel/power/Kconfig"
......
......@@ -7,7 +7,7 @@
*/
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/hardirq.h>
#include <linux/smp.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-pexp-defs.h>
......@@ -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 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/serial.h>
#include <linux/smp.h>
#include <linux/types.h>
#include <linux/string.h> /* for memset */
#include <linux/tty.h>
......
......@@ -5,6 +5,7 @@
*
* Copyright (C) 2004-2008 Cavium Networks
*/
#include <linux/cpu.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/smp.h>
......@@ -19,10 +20,16 @@
#include <asm/octeon/octeon.h>
#include "octeon_boot.h"
volatile unsigned long octeon_processor_boot = 0xff;
volatile unsigned long octeon_processor_sp;
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)
{
const int coreid = cvmx_get_core_num();
......@@ -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)
{
const int coreid = cvmx_get_core_num();
......@@ -91,6 +118,9 @@ static void octeon_smp_setup(void)
cpus++;
}
}
cpu_present_map = cpu_possible_map;
octeon_smp_hotplug_setup();
}
/**
......@@ -128,6 +158,17 @@ static void octeon_init_secondary(void)
const int coreid = cvmx_get_core_num();
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_init_cvmcount();
/*
......@@ -199,6 +240,193 @@ static void octeon_cpus_done(void)
#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 = {
.send_ipi_single = octeon_send_ipi_single,
.send_ipi_mask = octeon_send_ipi_mask,
......@@ -208,4 +436,8 @@ struct plat_smp_ops octeon_smp_ops = {
.boot_secondary = octeon_boot_secondary,
.smp_setup = octeon_smp_setup,
.prepare_cpus = octeon_prepare_cpus,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = octeon_cpu_disable,
.cpu_die = octeon_cpu_die,
#endif
};
#ifndef __ASM_BUG_H
#define __ASM_BUG_H
#include <linux/compiler.h>
#include <asm/sgidefs.h>
#ifdef CONFIG_BUG
......
......@@ -11,6 +11,7 @@
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/smp.h>
#include <asm/cpu.h>
#include <asm/cpu-info.h>
......
......@@ -10,6 +10,7 @@
#define _ASM_IRQ_H
#include <linux/linkage.h>
#include <linux/smp.h>
#include <asm/mipsmtregs.h>
......
......@@ -13,6 +13,7 @@
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/slab.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
......
......@@ -26,6 +26,10 @@ struct plat_smp_ops {
void (*boot_secondary)(int cpu, struct task_struct *idle);
void (*smp_setup)(void);
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);
......
......@@ -13,6 +13,7 @@
#include <linux/bitops.h>
#include <linux/linkage.h>
#include <linux/smp.h>
#include <linux/threads.h>
#include <linux/cpumask.h>
......@@ -40,6 +41,7 @@ extern int __cpu_logical_map[NR_CPUS];
/* Octeon - Tell another core to flush its icache */
#define SMP_ICACHE_FLUSH 0x4
extern volatile cpumask_t cpu_callin_map;
extern void asmlinkage smp_bootstrap(void);
......@@ -55,6 +57,24 @@ static inline void smp_send_reschedule(int cpu)
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 void arch_send_call_function_single_ipi(int cpu);
......
......@@ -11,6 +11,7 @@
#ifndef __ASSEMBLY__
#include <linux/smp.h>
#include <linux/types.h>
#endif /* !__ASSEMBLY__ */
......
......@@ -10,6 +10,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <asm/irq_cpu.h>
......
......@@ -18,6 +18,7 @@
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/addrspace.h>
#include <asm/io.h>
......
......@@ -9,6 +9,7 @@
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/smtc_ipi.h>
#include <asm/time.h>
......
......@@ -18,6 +18,7 @@
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/addrspace.h>
#include <asm/io.h>
......
......@@ -10,6 +10,7 @@
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/smtc_ipi.h>
#include <asm/time.h>
......
......@@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <asm/bugs.h>
......
......@@ -7,6 +7,7 @@
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <asm/delay.h>
......
......@@ -2,6 +2,7 @@
#include <linux/bitmap.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <asm/io.h>
#include <asm/gic.h>
......
......@@ -26,6 +26,7 @@
#include <linux/kgdb.h>
#include <linux/kdebug.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <asm/inst.h>
#include <asm/fpu.h>
#include <asm/cacheflush.h>
......
......@@ -50,10 +50,15 @@
*/
void __noreturn cpu_idle(void)
{
int cpu;
/* CPU is going idle. */
cpu = smp_processor_id();
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
while (!need_resched()) {
while (!need_resched() && cpu_online(cpu)) {
#ifdef CONFIG_MIPS_MT_SMTC
extern void smtc_idle_loop_hook(void);
......@@ -62,6 +67,12 @@ void __noreturn cpu_idle(void)
if (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();
preempt_enable_no_resched();
schedule();
......
......@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/cpumask.h>
#include <linux/interrupt.h>
#include <linux/compiler.h>
......
......@@ -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 = {
.send_ipi_single = up_send_ipi_single,
.send_ipi_mask = up_send_ipi_mask,
......@@ -64,4 +76,8 @@ struct plat_smp_ops up_smp_ops = {
.boot_secondary = up_boot_secondary,
.smp_setup = up_smp_setup,
.prepare_cpus = up_prepare_cpus,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = up_cpu_disable,
.cpu_die = up_cpu_die,
#endif
};
......@@ -22,6 +22,7 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/threads.h>
#include <linux/module.h>
......@@ -44,7 +45,7 @@
#include <asm/mipsmtregs.h>
#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_logical_map[NR_CPUS]; /* Map logical to physical */
......@@ -200,6 +201,8 @@ void __devinit smp_prepare_boot_cpu(void)
* and keep control until "cpu_online(cpu)" is set. Note: cpu is
* physical, not logical.
*/
static struct task_struct *cpu_idle_thread[NR_CPUS];
int __cpuinit __cpu_up(unsigned int cpu)
{
struct task_struct *idle;
......@@ -209,9 +212,16 @@ int __cpuinit __cpu_up(unsigned int cpu)
* The following code is purely to make sure
* Linux can schedule processes on this slave.
*/
idle = fork_idle(cpu);
if (IS_ERR(idle))
panic(KERN_ERR "Fork failed for CPU %d", cpu);
if (!cpu_idle_thread[cpu]) {
idle = fork_idle(cpu);
cpu_idle_thread[cpu] = idle;
if (IS_ERR(idle))
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);
......
......@@ -20,6 +20,7 @@
#include <linux/clockchips.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/cpumask.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
......
......@@ -17,7 +17,10 @@ static int __init topology_init(void)
#endif /* CONFIG_NUMA */
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)
printk(KERN_WARNING "topology_init: register_cpu %d "
"failed (%d)\n", i, ret);
......
......@@ -91,6 +91,7 @@ unsigned __cpuinit get_c0_compare_int(void)
mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
} else {
#endif
{
if (cpu_has_vint)
set_vi_handler(cp0_compare_irq, mips_timer_dispatch);
mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
......
......@@ -8,6 +8,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <linux/bitops.h>
#include <linux/cpu.h>
......
......@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <asm/page.h>
......
......@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/linkage.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/bitops.h>
......
......@@ -11,6 +11,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <asm/cacheops.h>
......
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/smp.h>
#include <asm/fixmap.h>
#include <asm/tlbflush.h>
......
......@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
......
......@@ -10,6 +10,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
......
......@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <asm/page.h>
......
......@@ -10,6 +10,7 @@
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <linux/hugetlb.h>
......
......@@ -10,6 +10,7 @@
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <asm/cpu.h>
......
......@@ -23,6 +23,7 @@
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/smp.h>
#include <linux/string.h>
#include <linux/init.h>
......
......@@ -24,6 +24,7 @@
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/io.h>
......
......@@ -10,6 +10,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/smp.h>
#include <asm/sn/arch.h>
#include <asm/pci/bridge.h>
#include <asm/paccess.h>
......
#include <linux/linkage.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <asm/pmon.h>
#include <asm/titan_dep.h>
......
......@@ -43,15 +43,6 @@ LEAF(swsusp_arch_resume)
bne t1, t3, 1b
PTR_L t0, PBE_NEXT(t0)
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_L ra, PT_R31(t0)
PTR_L sp, PT_R29(t0)
......
......@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/cpumask.h>
......
......@@ -18,6 +18,7 @@
#include <linux/ioport.h>
#include <linux/timex.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/random.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
......
......@@ -10,6 +10,7 @@
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/param.h>
#include <linux/smp.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/mm.h>
......
......@@ -9,6 +9,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/smp.h>
#include <asm/sn/types.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/hub.h>
......
......@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/linkage.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/slab.h>
......
......@@ -51,12 +51,13 @@ static int cfe_console_setup(struct console *cons, char *str)
setleds("u0cn");
} else if (!strcmp(consdev, "uart1")) {
setleds("u1cn");
} else
#endif
#ifdef CONFIG_VGA_CONSOLE
} else if (!strcmp(consdev, "pcconsole0")) {
setleds("pccn");
#endif
if (!strcmp(consdev, "pcconsole0")) {
setleds("pccn");
} else
#endif
return -ENODEV;
}
return 0;
......
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/time.h>
#include <linux/clockchips.h>
......
......@@ -432,23 +432,27 @@ txx9dmac_descriptor_complete(struct txx9dmac_chan *dc,
list_splice_init(&txd->tx_list, &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) {
dma_addr_t dmaaddr;
if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
dmaaddr = is_dmac64(dc) ?
desc->hwdesc.DAR : desc->hwdesc32.DAR;
dma_unmap_page(chan2parent(&dc->chan), dmaaddr,
desc->len, DMA_FROM_DEVICE);
if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
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)) {
dmaaddr = is_dmac64(dc) ?
desc->hwdesc.SAR : desc->hwdesc32.SAR;
dma_unmap_page(chan2parent(&dc->chan), dmaaddr,
desc->len, DMA_TO_DEVICE);
if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
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 @@
obj-${CONFIG_OCTEON_ETHERNET} := octeon-ethernet.o
octeon-ethernet-objs := ethernet.o
octeon-ethernet-objs += ethernet-common.o
octeon-ethernet-objs += ethernet-mdio.o
octeon-ethernet-objs += ethernet-mem.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 @@
/* Maximum number of packets to process per interrupt. */
#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
#ifndef CONFIG_SMP
......
......@@ -33,7 +33,6 @@
#include "ethernet-defines.h"
#include "octeon-ethernet.h"
#include "ethernet-common.h"
#include "ethernet-util.h"
#include "cvmx-helper.h"
......@@ -265,7 +264,7 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
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;
struct octeon_ethernet *priv = netdev_priv(dev);
......@@ -286,7 +285,7 @@ static int cvm_oct_rgmii_open(struct net_device *dev)
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;
struct octeon_ethernet *priv = netdev_priv(dev);
......@@ -305,9 +304,7 @@ int cvm_oct_rgmii_init(struct net_device *dev)
int r;
cvm_oct_common_init(dev);
dev->open = cvm_oct_rgmii_open;
dev->stop = cvm_oct_rgmii_stop;
dev->stop(dev);
dev->netdev_ops->ndo_stop(dev);
/*
* Due to GMX errata in CN3XXX series chips, it is necessary
......
......@@ -34,13 +34,12 @@
#include "ethernet-defines.h"
#include "octeon-ethernet.h"
#include "ethernet-util.h"
#include "ethernet-common.h"
#include "cvmx-helper.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;
struct octeon_ethernet *priv = netdev_priv(dev);
......@@ -61,7 +60,7 @@ static int cvm_oct_sgmii_open(struct net_device *dev)
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;
struct octeon_ethernet *priv = netdev_priv(dev);
......@@ -113,9 +112,7 @@ int cvm_oct_sgmii_init(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
cvm_oct_common_init(dev);
dev->open = cvm_oct_sgmii_open;
dev->stop = cvm_oct_sgmii_stop;
dev->stop(dev);
dev->netdev_ops->ndo_stop(dev);
if (!octeon_is_simulation())
priv->poll = cvm_oct_sgmii_poll;
......
......@@ -33,7 +33,6 @@
#include "ethernet-defines.h"
#include "octeon-ethernet.h"
#include "ethernet-common.h"
#include "ethernet-util.h"
#include "cvmx-spi.h"
......
......@@ -47,6 +47,7 @@
#include "ethernet-defines.h"
#include "octeon-ethernet.h"
#include "ethernet-tx.h"
#include "ethernet-util.h"
#include "cvmx-wqe.h"
......@@ -82,8 +83,10 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
uint64_t old_scratch2;
int dropped;
int qos;
int queue_it_up;
struct octeon_ethernet *priv = netdev_priv(dev);
int32_t in_use;
int32_t skb_to_free;
int32_t undo;
int32_t buffers_to_free;
#if REUSE_SKBUFFS_WITHOUT_FREE
unsigned char *fpa_head;
......@@ -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);
/*
* Assume we're going to be able t osend this
* packet. Fetch and increment the number of pending
* packets for output.
* Fetch and increment the number of packets to be
* freed.
*/
cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH + 8,
FAU_NUM_PACKET_BUFFERS_TO_FREE,
0);
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)
/*
* 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);
skb->dst = NULL;
dst_release(skb_dst(skb));
skb_dst_set(skb, NULL);
#ifdef CONFIG_XFRM
secpath_put(skb->sp);
skb->sp = NULL;
......@@ -286,15 +289,29 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
if (USE_ASYNC_IOBDMA) {
/* Get the number of skbuffs in use by the hardware */
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);
} else {
/* 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 =
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
* don't do the HW free.
......@@ -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);
}
queue_it_up = 0;
if (unlikely(dropped)) {
dev_kfree_skb_any(skb);
cvmx_fau_atomic_add32(priv->fau + qos * 4, -1);
priv->stats.tx_dropped++;
} else {
if (USE_SKBUFFS_IN_HW) {
/* Put this packet on the queue to be freed later */
if (pko_command.s.dontfree)
skb_queue_tail(&priv->tx_free_list[qos], skb);
else {
queue_it_up = 1;
else
cvmx_fau_atomic_add32
(FAU_NUM_PACKET_BUFFERS_TO_FREE, -1);
cvmx_fau_atomic_add32(priv->fau + qos * 4, -1);
}
} else {
/* 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 (skb_queue_len(&priv->tx_free_list[qos]) > in_use) {
if (queue_it_up) {
spin_lock(&priv->tx_free_list[qos].lock);
/*
* Check again now that we have the lock. It might
* 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]));
__skb_queue_tail(&priv->tx_free_list[qos], skb);
cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 0);
spin_unlock(&priv->tx_free_list[qos].lock);
} else {
cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 1);
}
return 0;
......
......@@ -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 do_free, int qos);
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 @@
#include "ethernet-defines.h"
#include "octeon-ethernet.h"
#include "ethernet-common.h"
#include "ethernet-util.h"
#include "cvmx-helper.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;
struct octeon_ethernet *priv = netdev_priv(dev);
......@@ -60,7 +59,7 @@ static int cvm_oct_xaui_open(struct net_device *dev)
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;
struct octeon_ethernet *priv = netdev_priv(dev);
......@@ -112,9 +111,7 @@ int cvm_oct_xaui_init(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
cvm_oct_common_init(dev);
dev->open = cvm_oct_xaui_open;
dev->stop = cvm_oct_xaui_stop;
dev->stop(dev);
dev->netdev_ops->ndo_stop(dev);
if (!octeon_is_simulation())
priv->poll = cvm_oct_xaui_poll;
......
This diff is collapsed.
......@@ -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 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 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 void cvm_oct_spi_uninit(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 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 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