18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Interrupt handling for IPR-based IRQ.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1999  Niibe Yutaka & Takeshi Yaegashi
68c2ecf20Sopenharmony_ci * Copyright (C) 2000  Kazumoto Kojima
78c2ecf20Sopenharmony_ci * Copyright (C) 2003  Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
88c2ecf20Sopenharmony_ci * Copyright (C) 2006  Paul Mundt
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Supported system:
118c2ecf20Sopenharmony_ci *	On-chip supporting modules (TMU, RTC, etc.).
128c2ecf20Sopenharmony_ci *	On-chip supporting modules for SH7709/SH7709A/SH7729.
138c2ecf20Sopenharmony_ci *	Hitachi SolutionEngine external I/O:
148c2ecf20Sopenharmony_ci *		MS7709SE01, MS7709ASE01, and MS7750SE01
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/io.h>
198c2ecf20Sopenharmony_ci#include <linux/irq.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci#include <linux/topology.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic inline struct ipr_desc *get_ipr_desc(struct irq_data *data)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_data_get_irq_chip(data);
278c2ecf20Sopenharmony_ci	return container_of(chip, struct ipr_desc, chip);
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void disable_ipr_irq(struct irq_data *data)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct ipr_data *p = irq_data_get_irq_chip_data(data);
338c2ecf20Sopenharmony_ci	unsigned long addr = get_ipr_desc(data)->ipr_offsets[p->ipr_idx];
348c2ecf20Sopenharmony_ci	/* Set the priority in IPR to 0 */
358c2ecf20Sopenharmony_ci	__raw_writew(__raw_readw(addr) & (0xffff ^ (0xf << p->shift)), addr);
368c2ecf20Sopenharmony_ci	(void)__raw_readw(addr);	/* Read back to flush write posting */
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void enable_ipr_irq(struct irq_data *data)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct ipr_data *p = irq_data_get_irq_chip_data(data);
428c2ecf20Sopenharmony_ci	unsigned long addr = get_ipr_desc(data)->ipr_offsets[p->ipr_idx];
438c2ecf20Sopenharmony_ci	/* Set priority in IPR back to original value */
448c2ecf20Sopenharmony_ci	__raw_writew(__raw_readw(addr) | (p->priority << p->shift), addr);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * The shift value is now the number of bits to shift, not the number of
498c2ecf20Sopenharmony_ci * bits/4. This is to make it easier to read the value directly from the
508c2ecf20Sopenharmony_ci * datasheets. The IPR address is calculated using the ipr_offset table.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_civoid register_ipr_controller(struct ipr_desc *desc)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int i;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	desc->chip.irq_mask = disable_ipr_irq;
578c2ecf20Sopenharmony_ci	desc->chip.irq_unmask = enable_ipr_irq;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	for (i = 0; i < desc->nr_irqs; i++) {
608c2ecf20Sopenharmony_ci		struct ipr_data *p = desc->ipr_data + i;
618c2ecf20Sopenharmony_ci		int res;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		BUG_ON(p->ipr_idx >= desc->nr_offsets);
648c2ecf20Sopenharmony_ci		BUG_ON(!desc->ipr_offsets[p->ipr_idx]);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		res = irq_alloc_desc_at(p->irq, numa_node_id());
678c2ecf20Sopenharmony_ci		if (unlikely(res != p->irq && res != -EEXIST)) {
688c2ecf20Sopenharmony_ci			printk(KERN_INFO "can not get irq_desc for %d\n",
698c2ecf20Sopenharmony_ci			       p->irq);
708c2ecf20Sopenharmony_ci			continue;
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		disable_irq_nosync(p->irq);
748c2ecf20Sopenharmony_ci		irq_set_chip_and_handler_name(p->irq, &desc->chip,
758c2ecf20Sopenharmony_ci					      handle_level_irq, "level");
768c2ecf20Sopenharmony_ci		irq_set_chip_data(p->irq, p);
778c2ecf20Sopenharmony_ci		disable_ipr_irq(irq_get_irq_data(p->irq));
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_ipr_controller);
81