forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
3.2 KiB
136 lines
3.2 KiB
// 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);
|
|
|