162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Open Multi-Processor Interrupt Controller driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2014 Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
562306a36Sopenharmony_ci * Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License
862306a36Sopenharmony_ci * version 2.  This program is licensed "as is" without any warranty of any
962306a36Sopenharmony_ci * kind, whether express or implied.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The ompic device handles IPI communication between cores in multi-core
1262306a36Sopenharmony_ci * OpenRISC systems.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Registers
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * For each CPU the ompic has 2 registers. The control register for sending
1762306a36Sopenharmony_ci * and acking IPIs and the status register for receiving IPIs. The register
1862306a36Sopenharmony_ci * layouts are as follows:
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci *  Control register
2162306a36Sopenharmony_ci *  +---------+---------+----------+---------+
2262306a36Sopenharmony_ci *  | 31      | 30      | 29 .. 16 | 15 .. 0 |
2362306a36Sopenharmony_ci *  ----------+---------+----------+----------
2462306a36Sopenharmony_ci *  | IRQ ACK | IRQ GEN | DST CORE | DATA    |
2562306a36Sopenharmony_ci *  +---------+---------+----------+---------+
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci *  Status register
2862306a36Sopenharmony_ci *  +----------+-------------+----------+---------+
2962306a36Sopenharmony_ci *  | 31       | 30          | 29 .. 16 | 15 .. 0 |
3062306a36Sopenharmony_ci *  -----------+-------------+----------+---------+
3162306a36Sopenharmony_ci *  | Reserved | IRQ Pending | SRC CORE | DATA    |
3262306a36Sopenharmony_ci *  +----------+-------------+----------+---------+
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Architecture
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * - The ompic generates a level interrupt to the CPU PIC when a message is
3762306a36Sopenharmony_ci *   ready.  Messages are delivered via the memory bus.
3862306a36Sopenharmony_ci * - The ompic does not have any interrupt input lines.
3962306a36Sopenharmony_ci * - The ompic is wired to the same irq line on each core.
4062306a36Sopenharmony_ci * - Devices are wired to the same irq line on each core.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci *   +---------+                         +---------+
4362306a36Sopenharmony_ci *   | CPU     |                         | CPU     |
4462306a36Sopenharmony_ci *   |  Core 0 |<==\ (memory access) /==>|  Core 1 |
4562306a36Sopenharmony_ci *   |  [ PIC ]|   |                 |   |  [ PIC ]|
4662306a36Sopenharmony_ci *   +----^-^--+   |                 |   +----^-^--+
4762306a36Sopenharmony_ci *        | |      v                 v        | |
4862306a36Sopenharmony_ci *   <====|=|=================================|=|==> (memory bus)
4962306a36Sopenharmony_ci *        | |      ^                  ^       | |
5062306a36Sopenharmony_ci *  (ipi  | +------|---------+--------|-------|-+ (device irq)
5162306a36Sopenharmony_ci *   irq  |        |         |        |       |
5262306a36Sopenharmony_ci *  core0)| +------|---------|--------|-------+ (ipi irq core1)
5362306a36Sopenharmony_ci *        | |      |         |        |
5462306a36Sopenharmony_ci *   +----o-o-+    |    +--------+    |
5562306a36Sopenharmony_ci *   | ompic  |<===/    | Device |<===/
5662306a36Sopenharmony_ci *   |  IPI   |         +--------+
5762306a36Sopenharmony_ci *   +--------+*
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#include <linux/io.h>
6262306a36Sopenharmony_ci#include <linux/ioport.h>
6362306a36Sopenharmony_ci#include <linux/interrupt.h>
6462306a36Sopenharmony_ci#include <linux/smp.h>
6562306a36Sopenharmony_ci#include <linux/of.h>
6662306a36Sopenharmony_ci#include <linux/of_irq.h>
6762306a36Sopenharmony_ci#include <linux/of_address.h>
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#include <linux/irqchip.h>
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define OMPIC_CPUBYTES		8
7262306a36Sopenharmony_ci#define OMPIC_CTRL(cpu)		(0x0 + (cpu * OMPIC_CPUBYTES))
7362306a36Sopenharmony_ci#define OMPIC_STAT(cpu)		(0x4 + (cpu * OMPIC_CPUBYTES))
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define OMPIC_CTRL_IRQ_ACK	(1 << 31)
7662306a36Sopenharmony_ci#define OMPIC_CTRL_IRQ_GEN	(1 << 30)
7762306a36Sopenharmony_ci#define OMPIC_CTRL_DST(cpu)	(((cpu) & 0x3fff) << 16)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define OMPIC_STAT_IRQ_PENDING	(1 << 30)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define OMPIC_DATA(x)		((x) & 0xffff)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciDEFINE_PER_CPU(unsigned long, ops);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void __iomem *ompic_base;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline u32 ompic_readreg(void __iomem *base, loff_t offset)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	return ioread32be(base + offset);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void ompic_writereg(void __iomem *base, loff_t offset, u32 data)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	iowrite32be(data, base + offset);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void ompic_raise_softirq(const struct cpumask *mask,
9862306a36Sopenharmony_ci				unsigned int ipi_msg)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	unsigned int dst_cpu;
10162306a36Sopenharmony_ci	unsigned int src_cpu = smp_processor_id();
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	for_each_cpu(dst_cpu, mask) {
10462306a36Sopenharmony_ci		set_bit(ipi_msg, &per_cpu(ops, dst_cpu));
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		/*
10762306a36Sopenharmony_ci		 * On OpenRISC the atomic set_bit() call implies a memory
10862306a36Sopenharmony_ci		 * barrier.  Otherwise we would need: smp_wmb(); paired
10962306a36Sopenharmony_ci		 * with the read in ompic_ipi_handler.
11062306a36Sopenharmony_ci		 */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		ompic_writereg(ompic_base, OMPIC_CTRL(src_cpu),
11362306a36Sopenharmony_ci			       OMPIC_CTRL_IRQ_GEN |
11462306a36Sopenharmony_ci			       OMPIC_CTRL_DST(dst_cpu) |
11562306a36Sopenharmony_ci			       OMPIC_DATA(1));
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic irqreturn_t ompic_ipi_handler(int irq, void *dev_id)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	unsigned int cpu = smp_processor_id();
12262306a36Sopenharmony_ci	unsigned long *pending_ops = &per_cpu(ops, cpu);
12362306a36Sopenharmony_ci	unsigned long ops;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ompic_writereg(ompic_base, OMPIC_CTRL(cpu), OMPIC_CTRL_IRQ_ACK);
12662306a36Sopenharmony_ci	while ((ops = xchg(pending_ops, 0)) != 0) {
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		/*
12962306a36Sopenharmony_ci		 * On OpenRISC the atomic xchg() call implies a memory
13062306a36Sopenharmony_ci		 * barrier.  Otherwise we may need an smp_rmb(); paired
13162306a36Sopenharmony_ci		 * with the write in ompic_raise_softirq.
13262306a36Sopenharmony_ci		 */
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		do {
13562306a36Sopenharmony_ci			unsigned long ipi_msg;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci			ipi_msg = __ffs(ops);
13862306a36Sopenharmony_ci			ops &= ~(1UL << ipi_msg);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci			handle_IPI(ipi_msg);
14162306a36Sopenharmony_ci		} while (ops);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return IRQ_HANDLED;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int __init ompic_of_init(struct device_node *node,
14862306a36Sopenharmony_ci				struct device_node *parent)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct resource res;
15162306a36Sopenharmony_ci	int irq;
15262306a36Sopenharmony_ci	int ret;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Validate the DT */
15562306a36Sopenharmony_ci	if (ompic_base) {
15662306a36Sopenharmony_ci		pr_err("ompic: duplicate ompic's are not supported");
15762306a36Sopenharmony_ci		return -EEXIST;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (of_address_to_resource(node, 0, &res)) {
16162306a36Sopenharmony_ci		pr_err("ompic: reg property requires an address and size");
16262306a36Sopenharmony_ci		return -EINVAL;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (resource_size(&res) < (num_possible_cpus() * OMPIC_CPUBYTES)) {
16662306a36Sopenharmony_ci		pr_err("ompic: reg size, currently %d must be at least %d",
16762306a36Sopenharmony_ci			resource_size(&res),
16862306a36Sopenharmony_ci			(num_possible_cpus() * OMPIC_CPUBYTES));
16962306a36Sopenharmony_ci		return -EINVAL;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Setup the device */
17362306a36Sopenharmony_ci	ompic_base = ioremap(res.start, resource_size(&res));
17462306a36Sopenharmony_ci	if (!ompic_base) {
17562306a36Sopenharmony_ci		pr_err("ompic: unable to map registers");
17662306a36Sopenharmony_ci		return -ENOMEM;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	irq = irq_of_parse_and_map(node, 0);
18062306a36Sopenharmony_ci	if (irq <= 0) {
18162306a36Sopenharmony_ci		pr_err("ompic: unable to parse device irq");
18262306a36Sopenharmony_ci		ret = -EINVAL;
18362306a36Sopenharmony_ci		goto out_unmap;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	ret = request_irq(irq, ompic_ipi_handler, IRQF_PERCPU,
18762306a36Sopenharmony_ci				"ompic_ipi", NULL);
18862306a36Sopenharmony_ci	if (ret)
18962306a36Sopenharmony_ci		goto out_irq_disp;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	set_smp_cross_call(ompic_raise_softirq);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return 0;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ciout_irq_disp:
19662306a36Sopenharmony_ci	irq_dispose_mapping(irq);
19762306a36Sopenharmony_ciout_unmap:
19862306a36Sopenharmony_ci	iounmap(ompic_base);
19962306a36Sopenharmony_ci	ompic_base = NULL;
20062306a36Sopenharmony_ci	return ret;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ciIRQCHIP_DECLARE(ompic, "openrisc,ompic", ompic_of_init);
203