/*1* Open Multi-Processor Interrupt Controller driver2*3* Copyright (C) 2014 Stefan Kristiansson <[email protected]>4* Copyright (C) 2017 Stafford Horne <[email protected]>5*6* This file is licensed under the terms of the GNU General Public License7* version 2. This program is licensed "as is" without any warranty of any8* kind, whether express or implied.9*10* The ompic device handles IPI communication between cores in multi-core11* OpenRISC systems.12*13* Registers14*15* For each CPU the ompic has 2 registers. The control register for sending16* and acking IPIs and the status register for receiving IPIs. The register17* layouts are as follows:18*19* Control register20* +---------+---------+----------+---------+21* | 31 | 30 | 29 .. 16 | 15 .. 0 |22* ----------+---------+----------+----------23* | IRQ ACK | IRQ GEN | DST CORE | DATA |24* +---------+---------+----------+---------+25*26* Status register27* +----------+-------------+----------+---------+28* | 31 | 30 | 29 .. 16 | 15 .. 0 |29* -----------+-------------+----------+---------+30* | Reserved | IRQ Pending | SRC CORE | DATA |31* +----------+-------------+----------+---------+32*33* Architecture34*35* - The ompic generates a level interrupt to the CPU PIC when a message is36* ready. Messages are delivered via the memory bus.37* - The ompic does not have any interrupt input lines.38* - The ompic is wired to the same irq line on each core.39* - Devices are wired to the same irq line on each core.40*41* +---------+ +---------+42* | CPU | | CPU |43* | Core 0 |<==\ (memory access) /==>| Core 1 |44* | [ PIC ]| | | | [ PIC ]|45* +----^-^--+ | | +----^-^--+46* | | v v | |47* <====|=|=================================|=|==> (memory bus)48* | | ^ ^ | |49* (ipi | +------|---------+--------|-------|-+ (device irq)50* irq | | | | |51* core0)| +------|---------|--------|-------+ (ipi irq core1)52* | | | | |53* +----o-o-+ | +--------+ |54* | ompic |<===/ | Device |<===/55* | IPI | +--------+56* +--------+*57*58*/5960#include <linux/io.h>61#include <linux/ioport.h>62#include <linux/interrupt.h>63#include <linux/smp.h>64#include <linux/of.h>65#include <linux/of_irq.h>66#include <linux/of_address.h>6768#include <linux/irqchip.h>6970#define OMPIC_CPUBYTES 871#define OMPIC_CTRL(cpu) (0x0 + (cpu * OMPIC_CPUBYTES))72#define OMPIC_STAT(cpu) (0x4 + (cpu * OMPIC_CPUBYTES))7374#define OMPIC_CTRL_IRQ_ACK (1 << 31)75#define OMPIC_CTRL_IRQ_GEN (1 << 30)76#define OMPIC_CTRL_DST(cpu) (((cpu) & 0x3fff) << 16)7778#define OMPIC_STAT_IRQ_PENDING (1 << 30)7980#define OMPIC_DATA(x) ((x) & 0xffff)8182DEFINE_PER_CPU(unsigned long, ops);8384static void __iomem *ompic_base;8586static DEFINE_PER_CPU_READ_MOSTLY(int, ipi_dummy_dev);8788static inline u32 ompic_readreg(void __iomem *base, loff_t offset)89{90return ioread32be(base + offset);91}9293static void ompic_writereg(void __iomem *base, loff_t offset, u32 data)94{95iowrite32be(data, base + offset);96}9798static void ompic_raise_softirq(const struct cpumask *mask,99unsigned int ipi_msg)100{101unsigned int dst_cpu;102unsigned int src_cpu = smp_processor_id();103104for_each_cpu(dst_cpu, mask) {105set_bit(ipi_msg, &per_cpu(ops, dst_cpu));106107/*108* On OpenRISC the atomic set_bit() call implies a memory109* barrier. Otherwise we would need: smp_wmb(); paired110* with the read in ompic_ipi_handler.111*/112113ompic_writereg(ompic_base, OMPIC_CTRL(src_cpu),114OMPIC_CTRL_IRQ_GEN |115OMPIC_CTRL_DST(dst_cpu) |116OMPIC_DATA(1));117}118}119120static irqreturn_t ompic_ipi_handler(int irq, void *dev_id)121{122unsigned int cpu = smp_processor_id();123unsigned long *pending_ops = &per_cpu(ops, cpu);124unsigned long ops;125126ompic_writereg(ompic_base, OMPIC_CTRL(cpu), OMPIC_CTRL_IRQ_ACK);127while ((ops = xchg(pending_ops, 0)) != 0) {128129/*130* On OpenRISC the atomic xchg() call implies a memory131* barrier. Otherwise we may need an smp_rmb(); paired132* with the write in ompic_raise_softirq.133*/134135do {136unsigned long ipi_msg;137138ipi_msg = __ffs(ops);139ops &= ~(1UL << ipi_msg);140141handle_IPI(ipi_msg);142} while (ops);143}144145return IRQ_HANDLED;146}147148static int __init ompic_of_init(struct device_node *node,149struct device_node *parent)150{151struct resource res;152int irq;153int ret;154155/* Validate the DT */156if (ompic_base) {157pr_err("ompic: duplicate ompic's are not supported");158return -EEXIST;159}160161if (of_address_to_resource(node, 0, &res)) {162pr_err("ompic: reg property requires an address and size");163return -EINVAL;164}165166if (resource_size(&res) < (num_possible_cpus() * OMPIC_CPUBYTES)) {167pr_err("ompic: reg size, currently %d must be at least %d",168resource_size(&res),169(num_possible_cpus() * OMPIC_CPUBYTES));170return -EINVAL;171}172173/* Setup the device */174ompic_base = ioremap(res.start, resource_size(&res));175if (!ompic_base) {176pr_err("ompic: unable to map registers");177return -ENOMEM;178}179180irq = irq_of_parse_and_map(node, 0);181if (irq <= 0) {182pr_err("ompic: unable to parse device irq");183ret = -EINVAL;184goto out_unmap;185}186187irq_set_percpu_devid(irq);188ret = request_percpu_irq(irq, ompic_ipi_handler, "ompic_ipi",189&ipi_dummy_dev);190191if (ret) {192pr_err("ompic: failed to request irq %d, error: %d",193irq, ret);194goto out_irq_disp;195}196197set_smp_cross_call(ompic_raise_softirq, irq);198199return 0;200201out_irq_disp:202irq_dispose_mapping(irq);203out_unmap:204iounmap(ompic_base);205ompic_base = NULL;206return ret;207}208IRQCHIP_DECLARE(ompic, "openrisc,ompic", ompic_of_init);209210211