Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | // SPDX-License-Identifier: GPL-2.0 /* * SMP support for J2 processor * * Copyright (C) 2015-2016 Smart Energy Instruments, Inc. */ #include <linux/smp.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <asm/cmpxchg.h> DEFINE_PER_CPU(unsigned, j2_ipi_messages); extern u32 *sh2_cpuid_addr; static u32 *j2_ipi_trigger; static int j2_ipi_irq; static irqreturn_t j2_ipi_interrupt_handler(int irq, void *arg) { unsigned cpu = hard_smp_processor_id(); volatile unsigned *pmsg = &per_cpu(j2_ipi_messages, cpu); unsigned messages, i; do messages = *pmsg; while (cmpxchg(pmsg, messages, 0) != messages); if (!messages) return IRQ_NONE; for (i=0; i<SMP_MSG_NR; i++) if (messages & (1U<<i)) smp_message_recv(i); return IRQ_HANDLED; } static void j2_smp_setup(void) { } static void j2_prepare_cpus(unsigned int max_cpus) { struct device_node *np; unsigned i, max = 1; np = of_find_compatible_node(NULL, NULL, "jcore,ipi-controller"); if (!np) goto out; j2_ipi_irq = irq_of_parse_and_map(np, 0); j2_ipi_trigger = of_iomap(np, 0); if (!j2_ipi_irq || !j2_ipi_trigger) goto out; np = of_find_compatible_node(NULL, NULL, "jcore,cpuid-mmio"); if (!np) goto out; sh2_cpuid_addr = of_iomap(np, 0); if (!sh2_cpuid_addr) goto out; if (request_irq(j2_ipi_irq, j2_ipi_interrupt_handler, IRQF_PERCPU, "ipi", (void *)j2_ipi_interrupt_handler) != 0) goto out; max = max_cpus; out: /* Disable any cpus past max_cpus, or all secondaries if we didn't * get the necessary resources to support SMP. */ for (i=max; i<NR_CPUS; i++) { set_cpu_possible(i, false); set_cpu_present(i, false); } } static void j2_start_cpu(unsigned int cpu, unsigned long entry_point) { struct device_node *np; u32 regs[2]; void __iomem *release, *initpc; if (!cpu) return; np = of_get_cpu_node(cpu, NULL); if (!np) return; if (of_property_read_u32_array(np, "cpu-release-addr", regs, 2)) return; release = ioremap(regs[0], sizeof(u32)); initpc = ioremap(regs[1], sizeof(u32)); __raw_writel(entry_point, initpc); __raw_writel(1, release); iounmap(initpc); iounmap(release); pr_info("J2 SMP: requested start of cpu %u\n", cpu); } static unsigned int j2_smp_processor_id(void) { return __raw_readl(sh2_cpuid_addr); } static void j2_send_ipi(unsigned int cpu, unsigned int message) { volatile unsigned *pmsg; unsigned old; unsigned long val; /* There is only one IPI interrupt shared by all messages, so * we keep a separate interrupt flag per message type in sw. */ pmsg = &per_cpu(j2_ipi_messages, cpu); do old = *pmsg; while (cmpxchg(pmsg, old, old|(1U<<message)) != old); /* Generate the actual interrupt by writing to CCRn bit 28. */ val = __raw_readl(j2_ipi_trigger + cpu); __raw_writel(val | (1U<<28), j2_ipi_trigger + cpu); } static struct plat_smp_ops j2_smp_ops = { .smp_setup = j2_smp_setup, .prepare_cpus = j2_prepare_cpus, .start_cpu = j2_start_cpu, .smp_processor_id = j2_smp_processor_id, .send_ipi = j2_send_ipi, .cpu_die = native_cpu_die, .cpu_disable = native_cpu_disable, .play_dead = native_play_dead, }; CPU_METHOD_OF_DECLARE(j2_cpu_method, "jcore,spin-table", &j2_smp_ops); |