Path: blob/master/arch/mips/cavium-octeon/octeon-irq.c
10818 views
/*1* This file is subject to the terms and conditions of the GNU General Public2* License. See the file "COPYING" in the main directory of this archive3* for more details.4*5* Copyright (C) 2004-2008, 2009, 2010, 2011 Cavium Networks6*/78#include <linux/interrupt.h>9#include <linux/bitops.h>10#include <linux/percpu.h>11#include <linux/irq.h>12#include <linux/smp.h>1314#include <asm/octeon/octeon.h>1516static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock);17static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock);1819static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu0_en_mirror);20static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu1_en_mirror);2122static __read_mostly u8 octeon_irq_ciu_to_irq[8][64];2324union octeon_ciu_chip_data {25void *p;26unsigned long l;27struct {28unsigned int line:6;29unsigned int bit:6;30} s;31};3233struct octeon_core_chip_data {34struct mutex core_irq_mutex;35bool current_en;36bool desired_en;37u8 bit;38};3940#define MIPS_CORE_IRQ_LINES 84142static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES];4344static void __init octeon_irq_set_ciu_mapping(int irq, int line, int bit,45struct irq_chip *chip,46irq_flow_handler_t handler)47{48union octeon_ciu_chip_data cd;4950irq_set_chip_and_handler(irq, chip, handler);5152cd.l = 0;53cd.s.line = line;54cd.s.bit = bit;5556irq_set_chip_data(irq, cd.p);57octeon_irq_ciu_to_irq[line][bit] = irq;58}5960static int octeon_coreid_for_cpu(int cpu)61{62#ifdef CONFIG_SMP63return cpu_logical_map(cpu);64#else65return cvmx_get_core_num();66#endif67}6869static int octeon_cpu_for_coreid(int coreid)70{71#ifdef CONFIG_SMP72return cpu_number_map(coreid);73#else74return smp_processor_id();75#endif76}7778static void octeon_irq_core_ack(struct irq_data *data)79{80struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);81unsigned int bit = cd->bit;8283/*84* We don't need to disable IRQs to make these atomic since85* they are already disabled earlier in the low level86* interrupt code.87*/88clear_c0_status(0x100 << bit);89/* The two user interrupts must be cleared manually. */90if (bit < 2)91clear_c0_cause(0x100 << bit);92}9394static void octeon_irq_core_eoi(struct irq_data *data)95{96struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);9798/*99* We don't need to disable IRQs to make these atomic since100* they are already disabled earlier in the low level101* interrupt code.102*/103set_c0_status(0x100 << cd->bit);104}105106static void octeon_irq_core_set_enable_local(void *arg)107{108struct irq_data *data = arg;109struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);110unsigned int mask = 0x100 << cd->bit;111112/*113* Interrupts are already disabled, so these are atomic.114*/115if (cd->desired_en)116set_c0_status(mask);117else118clear_c0_status(mask);119120}121122static void octeon_irq_core_disable(struct irq_data *data)123{124struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);125cd->desired_en = false;126}127128static void octeon_irq_core_enable(struct irq_data *data)129{130struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);131cd->desired_en = true;132}133134static void octeon_irq_core_bus_lock(struct irq_data *data)135{136struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);137138mutex_lock(&cd->core_irq_mutex);139}140141static void octeon_irq_core_bus_sync_unlock(struct irq_data *data)142{143struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);144145if (cd->desired_en != cd->current_en) {146on_each_cpu(octeon_irq_core_set_enable_local, data, 1);147148cd->current_en = cd->desired_en;149}150151mutex_unlock(&cd->core_irq_mutex);152}153154static struct irq_chip octeon_irq_chip_core = {155.name = "Core",156.irq_enable = octeon_irq_core_enable,157.irq_disable = octeon_irq_core_disable,158.irq_ack = octeon_irq_core_ack,159.irq_eoi = octeon_irq_core_eoi,160.irq_bus_lock = octeon_irq_core_bus_lock,161.irq_bus_sync_unlock = octeon_irq_core_bus_sync_unlock,162163.irq_cpu_online = octeon_irq_core_eoi,164.irq_cpu_offline = octeon_irq_core_ack,165.flags = IRQCHIP_ONOFFLINE_ENABLED,166};167168static void __init octeon_irq_init_core(void)169{170int i;171int irq;172struct octeon_core_chip_data *cd;173174for (i = 0; i < MIPS_CORE_IRQ_LINES; i++) {175cd = &octeon_irq_core_chip_data[i];176cd->current_en = false;177cd->desired_en = false;178cd->bit = i;179mutex_init(&cd->core_irq_mutex);180181irq = OCTEON_IRQ_SW0 + i;182switch (irq) {183case OCTEON_IRQ_TIMER:184case OCTEON_IRQ_SW0:185case OCTEON_IRQ_SW1:186case OCTEON_IRQ_5:187case OCTEON_IRQ_PERF:188irq_set_chip_data(irq, cd);189irq_set_chip_and_handler(irq, &octeon_irq_chip_core,190handle_percpu_irq);191break;192default:193break;194}195}196}197198static int next_cpu_for_irq(struct irq_data *data)199{200201#ifdef CONFIG_SMP202int cpu;203int weight = cpumask_weight(data->affinity);204205if (weight > 1) {206cpu = smp_processor_id();207for (;;) {208cpu = cpumask_next(cpu, data->affinity);209if (cpu >= nr_cpu_ids) {210cpu = -1;211continue;212} else if (cpumask_test_cpu(cpu, cpu_online_mask)) {213break;214}215}216} else if (weight == 1) {217cpu = cpumask_first(data->affinity);218} else {219cpu = smp_processor_id();220}221return cpu;222#else223return smp_processor_id();224#endif225}226227static void octeon_irq_ciu_enable(struct irq_data *data)228{229int cpu = next_cpu_for_irq(data);230int coreid = octeon_coreid_for_cpu(cpu);231unsigned long *pen;232unsigned long flags;233union octeon_ciu_chip_data cd;234235cd.p = irq_data_get_irq_chip_data(data);236237if (cd.s.line == 0) {238raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);239pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);240set_bit(cd.s.bit, pen);241cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);242raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);243} else {244raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);245pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);246set_bit(cd.s.bit, pen);247cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);248raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);249}250}251252static void octeon_irq_ciu_enable_local(struct irq_data *data)253{254unsigned long *pen;255unsigned long flags;256union octeon_ciu_chip_data cd;257258cd.p = irq_data_get_irq_chip_data(data);259260if (cd.s.line == 0) {261raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);262pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror);263set_bit(cd.s.bit, pen);264cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen);265raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);266} else {267raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);268pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror);269set_bit(cd.s.bit, pen);270cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen);271raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);272}273}274275static void octeon_irq_ciu_disable_local(struct irq_data *data)276{277unsigned long *pen;278unsigned long flags;279union octeon_ciu_chip_data cd;280281cd.p = irq_data_get_irq_chip_data(data);282283if (cd.s.line == 0) {284raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);285pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror);286clear_bit(cd.s.bit, pen);287cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen);288raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);289} else {290raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);291pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror);292clear_bit(cd.s.bit, pen);293cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen);294raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);295}296}297298static void octeon_irq_ciu_disable_all(struct irq_data *data)299{300unsigned long flags;301unsigned long *pen;302int cpu;303union octeon_ciu_chip_data cd;304305wmb(); /* Make sure flag changes arrive before register updates. */306307cd.p = irq_data_get_irq_chip_data(data);308309if (cd.s.line == 0) {310raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);311for_each_online_cpu(cpu) {312int coreid = octeon_coreid_for_cpu(cpu);313pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);314clear_bit(cd.s.bit, pen);315cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);316}317raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);318} else {319raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);320for_each_online_cpu(cpu) {321int coreid = octeon_coreid_for_cpu(cpu);322pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);323clear_bit(cd.s.bit, pen);324cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);325}326raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);327}328}329330static void octeon_irq_ciu_enable_all(struct irq_data *data)331{332unsigned long flags;333unsigned long *pen;334int cpu;335union octeon_ciu_chip_data cd;336337cd.p = irq_data_get_irq_chip_data(data);338339if (cd.s.line == 0) {340raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);341for_each_online_cpu(cpu) {342int coreid = octeon_coreid_for_cpu(cpu);343pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);344set_bit(cd.s.bit, pen);345cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);346}347raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);348} else {349raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);350for_each_online_cpu(cpu) {351int coreid = octeon_coreid_for_cpu(cpu);352pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);353set_bit(cd.s.bit, pen);354cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);355}356raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);357}358}359360/*361* Enable the irq on the next core in the affinity set for chips that362* have the EN*_W1{S,C} registers.363*/364static void octeon_irq_ciu_enable_v2(struct irq_data *data)365{366u64 mask;367int cpu = next_cpu_for_irq(data);368union octeon_ciu_chip_data cd;369370cd.p = irq_data_get_irq_chip_data(data);371mask = 1ull << (cd.s.bit);372373/*374* Called under the desc lock, so these should never get out375* of sync.376*/377if (cd.s.line == 0) {378int index = octeon_coreid_for_cpu(cpu) * 2;379set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));380cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);381} else {382int index = octeon_coreid_for_cpu(cpu) * 2 + 1;383set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));384cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);385}386}387388/*389* Enable the irq on the current CPU for chips that390* have the EN*_W1{S,C} registers.391*/392static void octeon_irq_ciu_enable_local_v2(struct irq_data *data)393{394u64 mask;395union octeon_ciu_chip_data cd;396397cd.p = irq_data_get_irq_chip_data(data);398mask = 1ull << (cd.s.bit);399400if (cd.s.line == 0) {401int index = cvmx_get_core_num() * 2;402set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror));403cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);404} else {405int index = cvmx_get_core_num() * 2 + 1;406set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror));407cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);408}409}410411static void octeon_irq_ciu_disable_local_v2(struct irq_data *data)412{413u64 mask;414union octeon_ciu_chip_data cd;415416cd.p = irq_data_get_irq_chip_data(data);417mask = 1ull << (cd.s.bit);418419if (cd.s.line == 0) {420int index = cvmx_get_core_num() * 2;421clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror));422cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);423} else {424int index = cvmx_get_core_num() * 2 + 1;425clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror));426cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);427}428}429430/*431* Write to the W1C bit in CVMX_CIU_INTX_SUM0 to clear the irq.432*/433static void octeon_irq_ciu_ack(struct irq_data *data)434{435u64 mask;436union octeon_ciu_chip_data cd;437438cd.p = data->chip_data;439mask = 1ull << (cd.s.bit);440441if (cd.s.line == 0) {442int index = cvmx_get_core_num() * 2;443cvmx_write_csr(CVMX_CIU_INTX_SUM0(index), mask);444} else {445cvmx_write_csr(CVMX_CIU_INT_SUM1, mask);446}447}448449/*450* Disable the irq on the all cores for chips that have the EN*_W1{S,C}451* registers.452*/453static void octeon_irq_ciu_disable_all_v2(struct irq_data *data)454{455int cpu;456u64 mask;457union octeon_ciu_chip_data cd;458459wmb(); /* Make sure flag changes arrive before register updates. */460461cd.p = data->chip_data;462mask = 1ull << (cd.s.bit);463464if (cd.s.line == 0) {465for_each_online_cpu(cpu) {466int index = octeon_coreid_for_cpu(cpu) * 2;467clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));468cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);469}470} else {471for_each_online_cpu(cpu) {472int index = octeon_coreid_for_cpu(cpu) * 2 + 1;473clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));474cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);475}476}477}478479/*480* Enable the irq on the all cores for chips that have the EN*_W1{S,C}481* registers.482*/483static void octeon_irq_ciu_enable_all_v2(struct irq_data *data)484{485int cpu;486u64 mask;487union octeon_ciu_chip_data cd;488489cd.p = data->chip_data;490mask = 1ull << (cd.s.bit);491492if (cd.s.line == 0) {493for_each_online_cpu(cpu) {494int index = octeon_coreid_for_cpu(cpu) * 2;495set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));496cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);497}498} else {499for_each_online_cpu(cpu) {500int index = octeon_coreid_for_cpu(cpu) * 2 + 1;501set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));502cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);503}504}505}506507#ifdef CONFIG_SMP508509static void octeon_irq_cpu_offline_ciu(struct irq_data *data)510{511int cpu = smp_processor_id();512cpumask_t new_affinity;513514if (!cpumask_test_cpu(cpu, data->affinity))515return;516517if (cpumask_weight(data->affinity) > 1) {518/*519* It has multi CPU affinity, just remove this CPU520* from the affinity set.521*/522cpumask_copy(&new_affinity, data->affinity);523cpumask_clear_cpu(cpu, &new_affinity);524} else {525/* Otherwise, put it on lowest numbered online CPU. */526cpumask_clear(&new_affinity);527cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity);528}529__irq_set_affinity_locked(data, &new_affinity);530}531532static int octeon_irq_ciu_set_affinity(struct irq_data *data,533const struct cpumask *dest, bool force)534{535int cpu;536bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);537unsigned long flags;538union octeon_ciu_chip_data cd;539540cd.p = data->chip_data;541542/*543* For non-v2 CIU, we will allow only single CPU affinity.544* This removes the need to do locking in the .ack/.eoi545* functions.546*/547if (cpumask_weight(dest) != 1)548return -EINVAL;549550if (!enable_one)551return 0;552553if (cd.s.line == 0) {554raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);555for_each_online_cpu(cpu) {556int coreid = octeon_coreid_for_cpu(cpu);557unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);558559if (cpumask_test_cpu(cpu, dest) && enable_one) {560enable_one = false;561set_bit(cd.s.bit, pen);562} else {563clear_bit(cd.s.bit, pen);564}565cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);566}567raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);568} else {569raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);570for_each_online_cpu(cpu) {571int coreid = octeon_coreid_for_cpu(cpu);572unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);573574if (cpumask_test_cpu(cpu, dest) && enable_one) {575enable_one = false;576set_bit(cd.s.bit, pen);577} else {578clear_bit(cd.s.bit, pen);579}580cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);581}582raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);583}584return 0;585}586587/*588* Set affinity for the irq for chips that have the EN*_W1{S,C}589* registers.590*/591static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data,592const struct cpumask *dest,593bool force)594{595int cpu;596bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);597u64 mask;598union octeon_ciu_chip_data cd;599600if (!enable_one)601return 0;602603cd.p = data->chip_data;604mask = 1ull << cd.s.bit;605606if (cd.s.line == 0) {607for_each_online_cpu(cpu) {608unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);609int index = octeon_coreid_for_cpu(cpu) * 2;610if (cpumask_test_cpu(cpu, dest) && enable_one) {611enable_one = false;612set_bit(cd.s.bit, pen);613cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);614} else {615clear_bit(cd.s.bit, pen);616cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);617}618}619} else {620for_each_online_cpu(cpu) {621unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);622int index = octeon_coreid_for_cpu(cpu) * 2 + 1;623if (cpumask_test_cpu(cpu, dest) && enable_one) {624enable_one = false;625set_bit(cd.s.bit, pen);626cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);627} else {628clear_bit(cd.s.bit, pen);629cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);630}631}632}633return 0;634}635#endif636637/*638* The v1 CIU code already masks things, so supply a dummy version to639* the core chip code.640*/641static void octeon_irq_dummy_mask(struct irq_data *data)642{643}644645/*646* Newer octeon chips have support for lockless CIU operation.647*/648static struct irq_chip octeon_irq_chip_ciu_v2 = {649.name = "CIU",650.irq_enable = octeon_irq_ciu_enable_v2,651.irq_disable = octeon_irq_ciu_disable_all_v2,652.irq_mask = octeon_irq_ciu_disable_local_v2,653.irq_unmask = octeon_irq_ciu_enable_v2,654#ifdef CONFIG_SMP655.irq_set_affinity = octeon_irq_ciu_set_affinity_v2,656.irq_cpu_offline = octeon_irq_cpu_offline_ciu,657#endif658};659660static struct irq_chip octeon_irq_chip_ciu_edge_v2 = {661.name = "CIU-E",662.irq_enable = octeon_irq_ciu_enable_v2,663.irq_disable = octeon_irq_ciu_disable_all_v2,664.irq_ack = octeon_irq_ciu_ack,665.irq_mask = octeon_irq_ciu_disable_local_v2,666.irq_unmask = octeon_irq_ciu_enable_v2,667#ifdef CONFIG_SMP668.irq_set_affinity = octeon_irq_ciu_set_affinity_v2,669.irq_cpu_offline = octeon_irq_cpu_offline_ciu,670#endif671};672673static struct irq_chip octeon_irq_chip_ciu = {674.name = "CIU",675.irq_enable = octeon_irq_ciu_enable,676.irq_disable = octeon_irq_ciu_disable_all,677.irq_mask = octeon_irq_dummy_mask,678#ifdef CONFIG_SMP679.irq_set_affinity = octeon_irq_ciu_set_affinity,680.irq_cpu_offline = octeon_irq_cpu_offline_ciu,681#endif682};683684static struct irq_chip octeon_irq_chip_ciu_edge = {685.name = "CIU-E",686.irq_enable = octeon_irq_ciu_enable,687.irq_disable = octeon_irq_ciu_disable_all,688.irq_mask = octeon_irq_dummy_mask,689.irq_ack = octeon_irq_ciu_ack,690#ifdef CONFIG_SMP691.irq_set_affinity = octeon_irq_ciu_set_affinity,692.irq_cpu_offline = octeon_irq_cpu_offline_ciu,693#endif694};695696/* The mbox versions don't do any affinity or round-robin. */697static struct irq_chip octeon_irq_chip_ciu_mbox_v2 = {698.name = "CIU-M",699.irq_enable = octeon_irq_ciu_enable_all_v2,700.irq_disable = octeon_irq_ciu_disable_all_v2,701.irq_ack = octeon_irq_ciu_disable_local_v2,702.irq_eoi = octeon_irq_ciu_enable_local_v2,703704.irq_cpu_online = octeon_irq_ciu_enable_local_v2,705.irq_cpu_offline = octeon_irq_ciu_disable_local_v2,706.flags = IRQCHIP_ONOFFLINE_ENABLED,707};708709static struct irq_chip octeon_irq_chip_ciu_mbox = {710.name = "CIU-M",711.irq_enable = octeon_irq_ciu_enable_all,712.irq_disable = octeon_irq_ciu_disable_all,713714.irq_cpu_online = octeon_irq_ciu_enable_local,715.irq_cpu_offline = octeon_irq_ciu_disable_local,716.flags = IRQCHIP_ONOFFLINE_ENABLED,717};718719/*720* Watchdog interrupts are special. They are associated with a single721* core, so we hardwire the affinity to that core.722*/723static void octeon_irq_ciu_wd_enable(struct irq_data *data)724{725unsigned long flags;726unsigned long *pen;727int coreid = data->irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */728int cpu = octeon_cpu_for_coreid(coreid);729730raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);731pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);732set_bit(coreid, pen);733cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);734raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);735}736737/*738* Watchdog interrupts are special. They are associated with a single739* core, so we hardwire the affinity to that core.740*/741static void octeon_irq_ciu1_wd_enable_v2(struct irq_data *data)742{743int coreid = data->irq - OCTEON_IRQ_WDOG0;744int cpu = octeon_cpu_for_coreid(coreid);745746set_bit(coreid, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));747cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(coreid * 2 + 1), 1ull << coreid);748}749750751static struct irq_chip octeon_irq_chip_ciu_wd_v2 = {752.name = "CIU-W",753.irq_enable = octeon_irq_ciu1_wd_enable_v2,754.irq_disable = octeon_irq_ciu_disable_all_v2,755.irq_mask = octeon_irq_ciu_disable_local_v2,756.irq_unmask = octeon_irq_ciu_enable_local_v2,757};758759static struct irq_chip octeon_irq_chip_ciu_wd = {760.name = "CIU-W",761.irq_enable = octeon_irq_ciu_wd_enable,762.irq_disable = octeon_irq_ciu_disable_all,763.irq_mask = octeon_irq_dummy_mask,764};765766static void octeon_irq_ip2_v1(void)767{768const unsigned long core_id = cvmx_get_core_num();769u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2));770771ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror);772clear_c0_status(STATUSF_IP2);773if (likely(ciu_sum)) {774int bit = fls64(ciu_sum) - 1;775int irq = octeon_irq_ciu_to_irq[0][bit];776if (likely(irq))777do_IRQ(irq);778else779spurious_interrupt();780} else {781spurious_interrupt();782}783set_c0_status(STATUSF_IP2);784}785786static void octeon_irq_ip2_v2(void)787{788const unsigned long core_id = cvmx_get_core_num();789u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2));790791ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror);792if (likely(ciu_sum)) {793int bit = fls64(ciu_sum) - 1;794int irq = octeon_irq_ciu_to_irq[0][bit];795if (likely(irq))796do_IRQ(irq);797else798spurious_interrupt();799} else {800spurious_interrupt();801}802}803static void octeon_irq_ip3_v1(void)804{805u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1);806807ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror);808clear_c0_status(STATUSF_IP3);809if (likely(ciu_sum)) {810int bit = fls64(ciu_sum) - 1;811int irq = octeon_irq_ciu_to_irq[1][bit];812if (likely(irq))813do_IRQ(irq);814else815spurious_interrupt();816} else {817spurious_interrupt();818}819set_c0_status(STATUSF_IP3);820}821822static void octeon_irq_ip3_v2(void)823{824u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1);825826ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror);827if (likely(ciu_sum)) {828int bit = fls64(ciu_sum) - 1;829int irq = octeon_irq_ciu_to_irq[1][bit];830if (likely(irq))831do_IRQ(irq);832else833spurious_interrupt();834} else {835spurious_interrupt();836}837}838839static void octeon_irq_ip4_mask(void)840{841clear_c0_status(STATUSF_IP4);842spurious_interrupt();843}844845static void (*octeon_irq_ip2)(void);846static void (*octeon_irq_ip3)(void);847static void (*octeon_irq_ip4)(void);848849void __cpuinitdata (*octeon_irq_setup_secondary)(void);850851static void __cpuinit octeon_irq_percpu_enable(void)852{853irq_cpu_online();854}855856static void __cpuinit octeon_irq_init_ciu_percpu(void)857{858int coreid = cvmx_get_core_num();859/*860* Disable All CIU Interrupts. The ones we need will be861* enabled later. Read the SUM register so we know the write862* completed.863*/864cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2)), 0);865cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2 + 1)), 0);866cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2)), 0);867cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2 + 1)), 0);868cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2)));869}870871static void __cpuinit octeon_irq_setup_secondary_ciu(void)872{873874__get_cpu_var(octeon_irq_ciu0_en_mirror) = 0;875__get_cpu_var(octeon_irq_ciu1_en_mirror) = 0;876877octeon_irq_init_ciu_percpu();878octeon_irq_percpu_enable();879880/* Enable the CIU lines */881set_c0_status(STATUSF_IP3 | STATUSF_IP2);882clear_c0_status(STATUSF_IP4);883}884885static void __init octeon_irq_init_ciu(void)886{887unsigned int i;888struct irq_chip *chip;889struct irq_chip *chip_edge;890struct irq_chip *chip_mbox;891struct irq_chip *chip_wd;892893octeon_irq_init_ciu_percpu();894octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;895896if (OCTEON_IS_MODEL(OCTEON_CN58XX_PASS2_X) ||897OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) ||898OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X) ||899OCTEON_IS_MODEL(OCTEON_CN6XXX)) {900octeon_irq_ip2 = octeon_irq_ip2_v2;901octeon_irq_ip3 = octeon_irq_ip3_v2;902chip = &octeon_irq_chip_ciu_v2;903chip_edge = &octeon_irq_chip_ciu_edge_v2;904chip_mbox = &octeon_irq_chip_ciu_mbox_v2;905chip_wd = &octeon_irq_chip_ciu_wd_v2;906} else {907octeon_irq_ip2 = octeon_irq_ip2_v1;908octeon_irq_ip3 = octeon_irq_ip3_v1;909chip = &octeon_irq_chip_ciu;910chip_edge = &octeon_irq_chip_ciu_edge;911chip_mbox = &octeon_irq_chip_ciu_mbox;912chip_wd = &octeon_irq_chip_ciu_wd;913}914octeon_irq_ip4 = octeon_irq_ip4_mask;915916/* Mips internal */917octeon_irq_init_core();918919/* CIU_0 */920for (i = 0; i < 16; i++)921octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq);922for (i = 0; i < 16; i++)923octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GPIO0, 0, i + 16, chip, handle_level_irq);924925octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq);926octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq);927928octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART0, 0, 34, chip, handle_level_irq);929octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART1, 0, 35, chip, handle_level_irq);930931for (i = 0; i < 4; i++)932octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_INT0, 0, i + 36, chip, handle_level_irq);933for (i = 0; i < 4; i++)934octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_MSI0, 0, i + 40, chip, handle_level_irq);935936octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI, 0, 45, chip, handle_level_irq);937octeon_irq_set_ciu_mapping(OCTEON_IRQ_RML, 0, 46, chip, handle_level_irq);938octeon_irq_set_ciu_mapping(OCTEON_IRQ_TRACE0, 0, 47, chip, handle_level_irq);939940for (i = 0; i < 2; i++)941octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GMX_DRP0, 0, i + 48, chip_edge, handle_edge_irq);942943octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPD_DRP, 0, 50, chip_edge, handle_edge_irq);944octeon_irq_set_ciu_mapping(OCTEON_IRQ_KEY_ZERO, 0, 51, chip_edge, handle_edge_irq);945946for (i = 0; i < 4; i++)947octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_TIMER0, 0, i + 52, chip_edge, handle_edge_irq);948949octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB0, 0, 56, chip, handle_level_irq);950octeon_irq_set_ciu_mapping(OCTEON_IRQ_PCM, 0, 57, chip, handle_level_irq);951octeon_irq_set_ciu_mapping(OCTEON_IRQ_MPI, 0, 58, chip, handle_level_irq);952octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI2, 0, 59, chip, handle_level_irq);953octeon_irq_set_ciu_mapping(OCTEON_IRQ_POWIQ, 0, 60, chip, handle_level_irq);954octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPDPPTHR, 0, 61, chip, handle_level_irq);955octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII0, 0, 62, chip, handle_level_irq);956octeon_irq_set_ciu_mapping(OCTEON_IRQ_BOOTDMA, 0, 63, chip, handle_level_irq);957958/* CIU_1 */959for (i = 0; i < 16; i++)960octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq);961962octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART2, 1, 16, chip, handle_level_irq);963octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq);964octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII1, 1, 18, chip, handle_level_irq);965octeon_irq_set_ciu_mapping(OCTEON_IRQ_NAND, 1, 19, chip, handle_level_irq);966octeon_irq_set_ciu_mapping(OCTEON_IRQ_MIO, 1, 20, chip, handle_level_irq);967octeon_irq_set_ciu_mapping(OCTEON_IRQ_IOB, 1, 21, chip, handle_level_irq);968octeon_irq_set_ciu_mapping(OCTEON_IRQ_FPA, 1, 22, chip, handle_level_irq);969octeon_irq_set_ciu_mapping(OCTEON_IRQ_POW, 1, 23, chip, handle_level_irq);970octeon_irq_set_ciu_mapping(OCTEON_IRQ_L2C, 1, 24, chip, handle_level_irq);971octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPD, 1, 25, chip, handle_level_irq);972octeon_irq_set_ciu_mapping(OCTEON_IRQ_PIP, 1, 26, chip, handle_level_irq);973octeon_irq_set_ciu_mapping(OCTEON_IRQ_PKO, 1, 27, chip, handle_level_irq);974octeon_irq_set_ciu_mapping(OCTEON_IRQ_ZIP, 1, 28, chip, handle_level_irq);975octeon_irq_set_ciu_mapping(OCTEON_IRQ_TIM, 1, 29, chip, handle_level_irq);976octeon_irq_set_ciu_mapping(OCTEON_IRQ_RAD, 1, 30, chip, handle_level_irq);977octeon_irq_set_ciu_mapping(OCTEON_IRQ_KEY, 1, 31, chip, handle_level_irq);978octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFA, 1, 32, chip, handle_level_irq);979octeon_irq_set_ciu_mapping(OCTEON_IRQ_USBCTL, 1, 33, chip, handle_level_irq);980octeon_irq_set_ciu_mapping(OCTEON_IRQ_SLI, 1, 34, chip, handle_level_irq);981octeon_irq_set_ciu_mapping(OCTEON_IRQ_DPI, 1, 35, chip, handle_level_irq);982983octeon_irq_set_ciu_mapping(OCTEON_IRQ_AGX0, 1, 36, chip, handle_level_irq);984985octeon_irq_set_ciu_mapping(OCTEON_IRQ_AGL, 1, 46, chip, handle_level_irq);986987octeon_irq_set_ciu_mapping(OCTEON_IRQ_PTP, 1, 47, chip_edge, handle_edge_irq);988989octeon_irq_set_ciu_mapping(OCTEON_IRQ_PEM0, 1, 48, chip, handle_level_irq);990octeon_irq_set_ciu_mapping(OCTEON_IRQ_PEM1, 1, 49, chip, handle_level_irq);991octeon_irq_set_ciu_mapping(OCTEON_IRQ_SRIO0, 1, 50, chip, handle_level_irq);992octeon_irq_set_ciu_mapping(OCTEON_IRQ_SRIO1, 1, 51, chip, handle_level_irq);993octeon_irq_set_ciu_mapping(OCTEON_IRQ_LMC0, 1, 52, chip, handle_level_irq);994octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFM, 1, 56, chip, handle_level_irq);995octeon_irq_set_ciu_mapping(OCTEON_IRQ_RST, 1, 63, chip, handle_level_irq);996997/* Enable the CIU lines */998set_c0_status(STATUSF_IP3 | STATUSF_IP2);999clear_c0_status(STATUSF_IP4);1000}10011002void __init arch_init_irq(void)1003{1004#ifdef CONFIG_SMP1005/* Set the default affinity to the boot cpu. */1006cpumask_clear(irq_default_affinity);1007cpumask_set_cpu(smp_processor_id(), irq_default_affinity);1008#endif1009octeon_irq_init_ciu();1010}10111012asmlinkage void plat_irq_dispatch(void)1013{1014unsigned long cop0_cause;1015unsigned long cop0_status;10161017while (1) {1018cop0_cause = read_c0_cause();1019cop0_status = read_c0_status();1020cop0_cause &= cop0_status;1021cop0_cause &= ST0_IM;10221023if (unlikely(cop0_cause & STATUSF_IP2))1024octeon_irq_ip2();1025else if (unlikely(cop0_cause & STATUSF_IP3))1026octeon_irq_ip3();1027else if (unlikely(cop0_cause & STATUSF_IP4))1028octeon_irq_ip4();1029else if (likely(cop0_cause))1030do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE);1031else1032break;1033}1034}10351036#ifdef CONFIG_HOTPLUG_CPU10371038void fixup_irqs(void)1039{1040irq_cpu_offline();1041}10421043#endif /* CONFIG_HOTPLUG_CPU */104410451046