162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/arch/sh/boards/se/7724/irq.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Renesas Solutions Corp.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Kuninori Morimoto <morimoto.kuninori@renesas.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Based on  linux/arch/sh/boards/se/7722/irq.c
1062306a36Sopenharmony_ci * Copyright (C) 2007  Nobuhiro Iwamatsu
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Hitachi UL SolutionEngine 7724 Support.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/irq.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/export.h>
1862306a36Sopenharmony_ci#include <linux/topology.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/err.h>
2162306a36Sopenharmony_ci#include <mach-se/mach/se7724.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct fpga_irq {
2462306a36Sopenharmony_ci	unsigned long  sraddr;
2562306a36Sopenharmony_ci	unsigned long  mraddr;
2662306a36Sopenharmony_ci	unsigned short mask;
2762306a36Sopenharmony_ci	unsigned int   base;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic unsigned int fpga2irq(unsigned int irq)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	if (irq >= IRQ0_BASE &&
3362306a36Sopenharmony_ci	    irq <= IRQ0_END)
3462306a36Sopenharmony_ci		return IRQ0_IRQ;
3562306a36Sopenharmony_ci	else if (irq >= IRQ1_BASE &&
3662306a36Sopenharmony_ci		 irq <= IRQ1_END)
3762306a36Sopenharmony_ci		return IRQ1_IRQ;
3862306a36Sopenharmony_ci	else
3962306a36Sopenharmony_ci		return IRQ2_IRQ;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic struct fpga_irq get_fpga_irq(unsigned int irq)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct fpga_irq set;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	switch (irq) {
4762306a36Sopenharmony_ci	case IRQ0_IRQ:
4862306a36Sopenharmony_ci		set.sraddr = IRQ0_SR;
4962306a36Sopenharmony_ci		set.mraddr = IRQ0_MR;
5062306a36Sopenharmony_ci		set.mask   = IRQ0_MASK;
5162306a36Sopenharmony_ci		set.base   = IRQ0_BASE;
5262306a36Sopenharmony_ci		break;
5362306a36Sopenharmony_ci	case IRQ1_IRQ:
5462306a36Sopenharmony_ci		set.sraddr = IRQ1_SR;
5562306a36Sopenharmony_ci		set.mraddr = IRQ1_MR;
5662306a36Sopenharmony_ci		set.mask   = IRQ1_MASK;
5762306a36Sopenharmony_ci		set.base   = IRQ1_BASE;
5862306a36Sopenharmony_ci		break;
5962306a36Sopenharmony_ci	default:
6062306a36Sopenharmony_ci		set.sraddr = IRQ2_SR;
6162306a36Sopenharmony_ci		set.mraddr = IRQ2_MR;
6262306a36Sopenharmony_ci		set.mask   = IRQ2_MASK;
6362306a36Sopenharmony_ci		set.base   = IRQ2_BASE;
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return set;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void disable_se7724_irq(struct irq_data *data)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	unsigned int irq = data->irq;
7362306a36Sopenharmony_ci	struct fpga_irq set = get_fpga_irq(fpga2irq(irq));
7462306a36Sopenharmony_ci	unsigned int bit = irq - set.base;
7562306a36Sopenharmony_ci	__raw_writew(__raw_readw(set.mraddr) | 0x0001 << bit, set.mraddr);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void enable_se7724_irq(struct irq_data *data)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	unsigned int irq = data->irq;
8162306a36Sopenharmony_ci	struct fpga_irq set = get_fpga_irq(fpga2irq(irq));
8262306a36Sopenharmony_ci	unsigned int bit = irq - set.base;
8362306a36Sopenharmony_ci	__raw_writew(__raw_readw(set.mraddr) & ~(0x0001 << bit), set.mraddr);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic struct irq_chip se7724_irq_chip __read_mostly = {
8762306a36Sopenharmony_ci	.name		= "SE7724-FPGA",
8862306a36Sopenharmony_ci	.irq_mask	= disable_se7724_irq,
8962306a36Sopenharmony_ci	.irq_unmask	= enable_se7724_irq,
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void se7724_irq_demux(struct irq_desc *desc)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	unsigned int irq = irq_desc_get_irq(desc);
9562306a36Sopenharmony_ci	struct fpga_irq set = get_fpga_irq(irq);
9662306a36Sopenharmony_ci	unsigned short intv = __raw_readw(set.sraddr);
9762306a36Sopenharmony_ci	unsigned int ext_irq = set.base;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	intv &= set.mask;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	for (; intv; intv >>= 1, ext_irq++) {
10262306a36Sopenharmony_ci		if (!(intv & 1))
10362306a36Sopenharmony_ci			continue;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		generic_handle_irq(ext_irq);
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/*
11062306a36Sopenharmony_ci * Initialize IRQ setting
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_civoid __init init_se7724_IRQ(void)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	int irq_base, i;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	__raw_writew(0xffff, IRQ0_MR);  /* mask all */
11762306a36Sopenharmony_ci	__raw_writew(0xffff, IRQ1_MR);  /* mask all */
11862306a36Sopenharmony_ci	__raw_writew(0xffff, IRQ2_MR);  /* mask all */
11962306a36Sopenharmony_ci	__raw_writew(0x0000, IRQ0_SR);  /* clear irq */
12062306a36Sopenharmony_ci	__raw_writew(0x0000, IRQ1_SR);  /* clear irq */
12162306a36Sopenharmony_ci	__raw_writew(0x0000, IRQ2_SR);  /* clear irq */
12262306a36Sopenharmony_ci	__raw_writew(0x002a, IRQ_MODE); /* set irq type */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	irq_base = irq_alloc_descs(SE7724_FPGA_IRQ_BASE, SE7724_FPGA_IRQ_BASE,
12562306a36Sopenharmony_ci				   SE7724_FPGA_IRQ_NR, numa_node_id());
12662306a36Sopenharmony_ci	if (IS_ERR_VALUE(irq_base)) {
12762306a36Sopenharmony_ci		pr_err("%s: failed hooking irqs for FPGA\n", __func__);
12862306a36Sopenharmony_ci		return;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	for (i = 0; i < SE7724_FPGA_IRQ_NR; i++)
13262306a36Sopenharmony_ci		irq_set_chip_and_handler_name(irq_base + i, &se7724_irq_chip,
13362306a36Sopenharmony_ci					      handle_level_irq, "level");
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	irq_set_chained_handler(IRQ0_IRQ, se7724_irq_demux);
13662306a36Sopenharmony_ci	irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	irq_set_chained_handler(IRQ1_IRQ, se7724_irq_demux);
13962306a36Sopenharmony_ci	irq_set_irq_type(IRQ1_IRQ, IRQ_TYPE_LEVEL_LOW);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	irq_set_chained_handler(IRQ2_IRQ, se7724_irq_demux);
14262306a36Sopenharmony_ci	irq_set_irq_type(IRQ2_IRQ, IRQ_TYPE_LEVEL_LOW);
14362306a36Sopenharmony_ci}
144