diff options
Diffstat (limited to 'arch/mips/cavium-octeon')
-rw-r--r-- | arch/mips/cavium-octeon/Makefile | 2 | ||||
-rw-r--r-- | arch/mips/cavium-octeon/cpu.c | 52 | ||||
-rw-r--r-- | arch/mips/cavium-octeon/csrc-octeon.c | 32 | ||||
-rw-r--r-- | arch/mips/cavium-octeon/octeon-irq.c | 224 | ||||
-rw-r--r-- | arch/mips/cavium-octeon/octeon-platform.c | 88 | ||||
-rw-r--r-- | arch/mips/cavium-octeon/smp.c | 5 |
6 files changed, 360 insertions, 43 deletions
diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile index 139436280520..3e9876317e61 100644 --- a/arch/mips/cavium-octeon/Makefile +++ b/arch/mips/cavium-octeon/Makefile @@ -9,7 +9,7 @@ # Copyright (C) 2005-2009 Cavium Networks # -obj-y := setup.o serial.o octeon-platform.o octeon-irq.o csrc-octeon.o +obj-y := cpu.o setup.o serial.o octeon-platform.o octeon-irq.o csrc-octeon.o obj-y += dma-octeon.o flash_setup.o obj-y += octeon-memcpy.o diff --git a/arch/mips/cavium-octeon/cpu.c b/arch/mips/cavium-octeon/cpu.c new file mode 100644 index 000000000000..b6df5387e855 --- /dev/null +++ b/arch/mips/cavium-octeon/cpu.c @@ -0,0 +1,52 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2009 Wind River Systems, + * written by Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/init.h> +#include <linux/irqflags.h> +#include <linux/notifier.h> +#include <linux/prefetch.h> +#include <linux/sched.h> + +#include <asm/cop2.h> +#include <asm/current.h> +#include <asm/mipsregs.h> +#include <asm/page.h> +#include <asm/octeon/octeon.h> + +static int cnmips_cu2_call(struct notifier_block *nfb, unsigned long action, + void *data) +{ + unsigned long flags; + unsigned int status; + + switch (action) { + case CU2_EXCEPTION: + prefetch(¤t->thread.cp2); + local_irq_save(flags); + KSTK_STATUS(current) |= ST0_CU2; + status = read_c0_status(); + write_c0_status(status | ST0_CU2); + octeon_cop2_restore(&(current->thread.cp2)); + write_c0_status(status & ~ST0_CU2); + local_irq_restore(flags); + + return NOTIFY_BAD; /* Don't call default notifier */ + } + + return NOTIFY_OK; /* Let default notifier send signals */ +} + +static struct notifier_block cnmips_cu2_notifier = { + .notifier_call = cnmips_cu2_call, +}; + +static int cnmips_cu2_setup(void) +{ + return register_cu2_notifier(&cnmips_cu2_notifier); +} +early_initcall(cnmips_cu2_setup); diff --git a/arch/mips/cavium-octeon/csrc-octeon.c b/arch/mips/cavium-octeon/csrc-octeon.c index 96110f217dcd..0bf4bbe04ae2 100644 --- a/arch/mips/cavium-octeon/csrc-octeon.c +++ b/arch/mips/cavium-octeon/csrc-octeon.c @@ -50,6 +50,38 @@ static struct clocksource clocksource_mips = { .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; +unsigned long long notrace sched_clock(void) +{ + /* 64-bit arithmatic can overflow, so use 128-bit. */ +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 3)) + u64 t1, t2, t3; + unsigned long long rv; + u64 mult = clocksource_mips.mult; + u64 shift = clocksource_mips.shift; + u64 cnt = read_c0_cvmcount(); + + asm ( + "dmultu\t%[cnt],%[mult]\n\t" + "nor\t%[t1],$0,%[shift]\n\t" + "mfhi\t%[t2]\n\t" + "mflo\t%[t3]\n\t" + "dsll\t%[t2],%[t2],1\n\t" + "dsrlv\t%[rv],%[t3],%[shift]\n\t" + "dsllv\t%[t1],%[t2],%[t1]\n\t" + "or\t%[rv],%[t1],%[rv]\n\t" + : [rv] "=&r" (rv), [t1] "=&r" (t1), [t2] "=&r" (t2), [t3] "=&r" (t3) + : [cnt] "r" (cnt), [mult] "r" (mult), [shift] "r" (shift) + : "hi", "lo"); + return rv; +#else + /* GCC > 4.3 do it the easy way. */ + unsigned int __attribute__((mode(TI))) t; + t = read_c0_cvmcount(); + t = t * clocksource_mips.mult; + return (unsigned long long)(t >> clocksource_mips.shift); +#endif +} + void __init plat_time_init(void) { clocksource_mips.rating = 300; diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c index 384f1842bfb1..6f2acf09328d 100644 --- a/arch/mips/cavium-octeon/octeon-irq.c +++ b/arch/mips/cavium-octeon/octeon-irq.c @@ -17,6 +17,15 @@ DEFINE_RWLOCK(octeon_irq_ciu0_rwlock); DEFINE_RWLOCK(octeon_irq_ciu1_rwlock); DEFINE_SPINLOCK(octeon_irq_msi_lock); +static int octeon_coreid_for_cpu(int cpu) +{ +#ifdef CONFIG_SMP + return cpu_logical_map(cpu); +#else + return cvmx_get_core_num(); +#endif +} + static void octeon_irq_core_ack(unsigned int irq) { unsigned int bit = irq - OCTEON_IRQ_SW0; @@ -152,11 +161,10 @@ static void octeon_irq_ciu0_disable(unsigned int irq) int bit = irq - OCTEON_IRQ_WORKQ0; /* Bit 0-63 of EN0 */ unsigned long flags; uint64_t en0; -#ifdef CONFIG_SMP int cpu; write_lock_irqsave(&octeon_irq_ciu0_rwlock, flags); for_each_online_cpu(cpu) { - int coreid = cpu_logical_map(cpu); + int coreid = octeon_coreid_for_cpu(cpu); en0 = cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)); en0 &= ~(1ull << bit); cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0); @@ -167,26 +175,57 @@ static void octeon_irq_ciu0_disable(unsigned int irq) */ cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2)); write_unlock_irqrestore(&octeon_irq_ciu0_rwlock, flags); -#else - int coreid = cvmx_get_core_num(); - local_irq_save(flags); - en0 = cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)); - en0 &= ~(1ull << bit); - cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0); - cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)); - local_irq_restore(flags); -#endif +} + +/* + * Enable the irq on the current core for chips that have the EN*_W1{S,C} + * registers. + */ +static void octeon_irq_ciu0_enable_v2(unsigned int irq) +{ + int index = cvmx_get_core_num() * 2; + u64 mask = 1ull << (irq - OCTEON_IRQ_WORKQ0); + + cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); +} + +/* + * Disable the irq on the current core for chips that have the EN*_W1{S,C} + * registers. + */ +static void octeon_irq_ciu0_disable_v2(unsigned int irq) +{ + int index = cvmx_get_core_num() * 2; + u64 mask = 1ull << (irq - OCTEON_IRQ_WORKQ0); + + cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); +} + +/* + * Disable the irq on the all cores for chips that have the EN*_W1{S,C} + * registers. + */ +static void octeon_irq_ciu0_disable_all_v2(unsigned int irq) +{ + u64 mask = 1ull << (irq - OCTEON_IRQ_WORKQ0); + int index; + int cpu; + for_each_online_cpu(cpu) { + index = octeon_coreid_for_cpu(cpu) * 2; + cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); + } } #ifdef CONFIG_SMP static int octeon_irq_ciu0_set_affinity(unsigned int irq, const struct cpumask *dest) { int cpu; + unsigned long flags; int bit = irq - OCTEON_IRQ_WORKQ0; /* Bit 0-63 of EN0 */ - write_lock(&octeon_irq_ciu0_rwlock); + write_lock_irqsave(&octeon_irq_ciu0_rwlock, flags); for_each_online_cpu(cpu) { - int coreid = cpu_logical_map(cpu); + int coreid = octeon_coreid_for_cpu(cpu); uint64_t en0 = cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)); if (cpumask_test_cpu(cpu, dest)) @@ -200,11 +239,45 @@ static int octeon_irq_ciu0_set_affinity(unsigned int irq, const struct cpumask * * of them are done. */ cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2)); - write_unlock(&octeon_irq_ciu0_rwlock); + write_unlock_irqrestore(&octeon_irq_ciu0_rwlock, flags); return 0; } + +/* + * Set affinity for the irq for chips that have the EN*_W1{S,C} + * registers. + */ +static int octeon_irq_ciu0_set_affinity_v2(unsigned int irq, + const struct cpumask *dest) +{ + int cpu; + int index; + u64 mask = 1ull << (irq - OCTEON_IRQ_WORKQ0); + for_each_online_cpu(cpu) { + index = octeon_coreid_for_cpu(cpu) * 2; + if (cpumask_test_cpu(cpu, dest)) + cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); + else + cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); + } + return 0; +} +#endif + +/* + * Newer octeon chips have support for lockless CIU operation. + */ +static struct irq_chip octeon_irq_chip_ciu0_v2 = { + .name = "CIU0", + .enable = octeon_irq_ciu0_enable_v2, + .disable = octeon_irq_ciu0_disable_all_v2, + .ack = octeon_irq_ciu0_disable_v2, + .eoi = octeon_irq_ciu0_enable_v2, +#ifdef CONFIG_SMP + .set_affinity = octeon_irq_ciu0_set_affinity_v2, #endif +}; static struct irq_chip octeon_irq_chip_ciu0 = { .name = "CIU0", @@ -269,11 +342,10 @@ static void octeon_irq_ciu1_disable(unsigned int irq) int bit = irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */ unsigned long flags; uint64_t en1; -#ifdef CONFIG_SMP int cpu; write_lock_irqsave(&octeon_irq_ciu1_rwlock, flags); for_each_online_cpu(cpu) { - int coreid = cpu_logical_map(cpu); + int coreid = octeon_coreid_for_cpu(cpu); en1 = cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1)); en1 &= ~(1ull << bit); cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1); @@ -284,26 +356,58 @@ static void octeon_irq_ciu1_disable(unsigned int irq) */ cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1)); write_unlock_irqrestore(&octeon_irq_ciu1_rwlock, flags); -#else - int coreid = cvmx_get_core_num(); - local_irq_save(flags); - en1 = cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1)); - en1 &= ~(1ull << bit); - cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1); - cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1)); - local_irq_restore(flags); -#endif +} + +/* + * Enable the irq on the current core for chips that have the EN*_W1{S,C} + * registers. + */ +static void octeon_irq_ciu1_enable_v2(unsigned int irq) +{ + int index = cvmx_get_core_num() * 2 + 1; + u64 mask = 1ull << (irq - OCTEON_IRQ_WDOG0); + + cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); +} + +/* + * Disable the irq on the current core for chips that have the EN*_W1{S,C} + * registers. + */ +static void octeon_irq_ciu1_disable_v2(unsigned int irq) +{ + int index = cvmx_get_core_num() * 2 + 1; + u64 mask = 1ull << (irq - OCTEON_IRQ_WDOG0); + + cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); +} + +/* + * Disable the irq on the all cores for chips that have the EN*_W1{S,C} + * registers. + */ +static void octeon_irq_ciu1_disable_all_v2(unsigned int irq) +{ + u64 mask = 1ull << (irq - OCTEON_IRQ_WDOG0); + int index; + int cpu; + for_each_online_cpu(cpu) { + index = octeon_coreid_for_cpu(cpu) * 2 + 1; + cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); + } } #ifdef CONFIG_SMP -static int octeon_irq_ciu1_set_affinity(unsigned int irq, const struct cpumask *dest) +static int octeon_irq_ciu1_set_affinity(unsigned int irq, + const struct cpumask *dest) { int cpu; + unsigned long flags; int bit = irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */ - write_lock(&octeon_irq_ciu1_rwlock); + write_lock_irqsave(&octeon_irq_ciu1_rwlock, flags); for_each_online_cpu(cpu) { - int coreid = cpu_logical_map(cpu); + int coreid = octeon_coreid_for_cpu(cpu); uint64_t en1 = cvmx_read_csr(CVMX_CIU_INTX_EN1 (coreid * 2 + 1)); @@ -318,12 +422,46 @@ static int octeon_irq_ciu1_set_affinity(unsigned int irq, const struct cpumask * * of them are done. */ cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1)); - write_unlock(&octeon_irq_ciu1_rwlock); + write_unlock_irqrestore(&octeon_irq_ciu1_rwlock, flags); + + return 0; +} +/* + * Set affinity for the irq for chips that have the EN*_W1{S,C} + * registers. + */ +static int octeon_irq_ciu1_set_affinity_v2(unsigned int irq, + const struct cpumask *dest) +{ + int cpu; + int index; + u64 mask = 1ull << (irq - OCTEON_IRQ_WDOG0); + for_each_online_cpu(cpu) { + index = octeon_coreid_for_cpu(cpu) * 2 + 1; + if (cpumask_test_cpu(cpu, dest)) + cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); + else + cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); + } return 0; } #endif +/* + * Newer octeon chips have support for lockless CIU operation. + */ +static struct irq_chip octeon_irq_chip_ciu1_v2 = { + .name = "CIU0", + .enable = octeon_irq_ciu1_enable_v2, + .disable = octeon_irq_ciu1_disable_all_v2, + .ack = octeon_irq_ciu1_disable_v2, + .eoi = octeon_irq_ciu1_enable_v2, +#ifdef CONFIG_SMP + .set_affinity = octeon_irq_ciu1_set_affinity_v2, +#endif +}; + static struct irq_chip octeon_irq_chip_ciu1 = { .name = "CIU1", .enable = octeon_irq_ciu1_enable, @@ -420,6 +558,8 @@ static struct irq_chip octeon_irq_chip_msi = { void __init arch_init_irq(void) { int irq; + struct irq_chip *chip0; + struct irq_chip *chip1; #ifdef CONFIG_SMP /* Set the default affinity to the boot cpu. */ @@ -430,6 +570,16 @@ void __init arch_init_irq(void) if (NR_IRQS < OCTEON_IRQ_LAST) pr_err("octeon_irq_init: NR_IRQS is set too low\n"); + if (OCTEON_IS_MODEL(OCTEON_CN58XX_PASS2_X) || + OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) || + OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X)) { + chip0 = &octeon_irq_chip_ciu0_v2; + chip1 = &octeon_irq_chip_ciu1_v2; + } else { + chip0 = &octeon_irq_chip_ciu0; + chip1 = &octeon_irq_chip_ciu1; + } + /* 0 - 15 reserved for i8259 master and slave controller. */ /* 17 - 23 Mips internal */ @@ -440,14 +590,12 @@ void __init arch_init_irq(void) /* 24 - 87 CIU_INT_SUM0 */ for (irq = OCTEON_IRQ_WORKQ0; irq <= OCTEON_IRQ_BOOTDMA; irq++) { - set_irq_chip_and_handler(irq, &octeon_irq_chip_ciu0, - handle_percpu_irq); + set_irq_chip_and_handler(irq, chip0, handle_percpu_irq); } /* 88 - 151 CIU_INT_SUM1 */ for (irq = OCTEON_IRQ_WDOG0; irq <= OCTEON_IRQ_RESERVED151; irq++) { - set_irq_chip_and_handler(irq, &octeon_irq_chip_ciu1, - handle_percpu_irq); + set_irq_chip_and_handler(irq, chip1, handle_percpu_irq); } #ifdef CONFIG_PCI_MSI @@ -505,14 +653,10 @@ 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 + unsigned int isset; + int coreid = octeon_coreid_for_cpu(cpu); int bit = (irq < OCTEON_IRQ_WDOG0) ? - irq - OCTEON_IRQ_WORKQ0 : 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; diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c index be711dd2d918..cfdb4c2ac5c3 100644 --- a/arch/mips/cavium-octeon/octeon-platform.c +++ b/arch/mips/cavium-octeon/octeon-platform.c @@ -159,6 +159,94 @@ out: } device_initcall(octeon_rng_device_init); +/* Octeon SMI/MDIO interface. */ +static int __init octeon_mdiobus_device_init(void) +{ + struct platform_device *pd; + int ret = 0; + + if (octeon_is_simulation()) + return 0; /* No mdio in the simulator. */ + + /* The bus number is the platform_device id. */ + pd = platform_device_alloc("mdio-octeon", 0); + if (!pd) { + ret = -ENOMEM; + goto out; + } + + ret = platform_device_add(pd); + if (ret) + goto fail; + + return ret; +fail: + platform_device_put(pd); + +out: + return ret; + +} +device_initcall(octeon_mdiobus_device_init); + +/* Octeon mgmt port Ethernet interface. */ +static int __init octeon_mgmt_device_init(void) +{ + struct platform_device *pd; + int ret = 0; + int port, num_ports; + + struct resource mgmt_port_resource = { + .flags = IORESOURCE_IRQ, + .start = -1, + .end = -1 + }; + + if (!OCTEON_IS_MODEL(OCTEON_CN56XX) && !OCTEON_IS_MODEL(OCTEON_CN52XX)) + return 0; + + if (OCTEON_IS_MODEL(OCTEON_CN56XX)) + num_ports = 1; + else + num_ports = 2; + + for (port = 0; port < num_ports; port++) { + pd = platform_device_alloc("octeon_mgmt", port); + if (!pd) { + ret = -ENOMEM; + goto out; + } + switch (port) { + case 0: + mgmt_port_resource.start = OCTEON_IRQ_MII0; + break; + case 1: + mgmt_port_resource.start = OCTEON_IRQ_MII1; + break; + default: + BUG(); + } + mgmt_port_resource.end = mgmt_port_resource.start; + + ret = platform_device_add_resources(pd, &mgmt_port_resource, 1); + + if (ret) + goto fail; + + ret = platform_device_add(pd); + if (ret) + goto fail; + } + return ret; +fail: + platform_device_put(pd); + +out: + return ret; + +} +device_initcall(octeon_mgmt_device_init); + MODULE_AUTHOR("David Daney <ddaney@caviumnetworks.com>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Platform driver for Octeon SOC"); diff --git a/arch/mips/cavium-octeon/smp.c b/arch/mips/cavium-octeon/smp.c index 32d51a31dc48..c198efdf583e 100644 --- a/arch/mips/cavium-octeon/smp.c +++ b/arch/mips/cavium-octeon/smp.c @@ -65,11 +65,12 @@ void octeon_send_ipi_single(int cpu, unsigned int action) cvmx_write_csr(CVMX_CIU_MBOX_SETX(coreid), action); } -static inline void octeon_send_ipi_mask(cpumask_t mask, unsigned int action) +static inline void octeon_send_ipi_mask(const struct cpumask *mask, + unsigned int action) { unsigned int i; - for_each_cpu_mask(i, mask) + for_each_cpu_mask(i, *mask) octeon_send_ipi_single(i, action); } |