18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Aspeed 24XX/25XX I2C Interrupt Controller.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2012-2017 ASPEED Technology Inc.
68c2ecf20Sopenharmony_ci *  Copyright 2017 IBM Corporation
78c2ecf20Sopenharmony_ci *  Copyright 2017 Google, Inc.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/irq.h>
118c2ecf20Sopenharmony_ci#include <linux/irqchip.h>
128c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
138c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
148c2ecf20Sopenharmony_ci#include <linux/of_address.h>
158c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define ASPEED_I2C_IC_NUM_BUS 14
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct aspeed_i2c_ic {
228c2ecf20Sopenharmony_ci	void __iomem		*base;
238c2ecf20Sopenharmony_ci	int			parent_irq;
248c2ecf20Sopenharmony_ci	struct irq_domain	*irq_domain;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * The aspeed chip provides a single hardware interrupt for all of the I2C
298c2ecf20Sopenharmony_ci * busses, so we use a dummy interrupt chip to translate this single interrupt
308c2ecf20Sopenharmony_ci * into multiple interrupts, each associated with a single I2C bus.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_cistatic void aspeed_i2c_ic_irq_handler(struct irq_desc *desc)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct aspeed_i2c_ic *i2c_ic = irq_desc_get_handler_data(desc);
358c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
368c2ecf20Sopenharmony_ci	unsigned long bit, status;
378c2ecf20Sopenharmony_ci	unsigned int bus_irq;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	chained_irq_enter(chip, desc);
408c2ecf20Sopenharmony_ci	status = readl(i2c_ic->base);
418c2ecf20Sopenharmony_ci	for_each_set_bit(bit, &status, ASPEED_I2C_IC_NUM_BUS) {
428c2ecf20Sopenharmony_ci		bus_irq = irq_find_mapping(i2c_ic->irq_domain, bit);
438c2ecf20Sopenharmony_ci		generic_handle_irq(bus_irq);
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci	chained_irq_exit(chip, desc);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Set simple handler and mark IRQ as valid. Nothing interesting to do here
508c2ecf20Sopenharmony_ci * since we are using a dummy interrupt chip.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistatic int aspeed_i2c_ic_map_irq_domain(struct irq_domain *domain,
538c2ecf20Sopenharmony_ci					unsigned int irq, irq_hw_number_t hwirq)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
568c2ecf20Sopenharmony_ci	irq_set_chip_data(irq, domain->host_data);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct irq_domain_ops aspeed_i2c_ic_irq_domain_ops = {
628c2ecf20Sopenharmony_ci	.map = aspeed_i2c_ic_map_irq_domain,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int __init aspeed_i2c_ic_of_init(struct device_node *node,
668c2ecf20Sopenharmony_ci					struct device_node *parent)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct aspeed_i2c_ic *i2c_ic;
698c2ecf20Sopenharmony_ci	int ret = 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	i2c_ic = kzalloc(sizeof(*i2c_ic), GFP_KERNEL);
728c2ecf20Sopenharmony_ci	if (!i2c_ic)
738c2ecf20Sopenharmony_ci		return -ENOMEM;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	i2c_ic->base = of_iomap(node, 0);
768c2ecf20Sopenharmony_ci	if (!i2c_ic->base) {
778c2ecf20Sopenharmony_ci		ret = -ENOMEM;
788c2ecf20Sopenharmony_ci		goto err_free_ic;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	i2c_ic->parent_irq = irq_of_parse_and_map(node, 0);
828c2ecf20Sopenharmony_ci	if (!i2c_ic->parent_irq) {
838c2ecf20Sopenharmony_ci		ret = -EINVAL;
848c2ecf20Sopenharmony_ci		goto err_iounmap;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	i2c_ic->irq_domain = irq_domain_add_linear(node, ASPEED_I2C_IC_NUM_BUS,
888c2ecf20Sopenharmony_ci						   &aspeed_i2c_ic_irq_domain_ops,
898c2ecf20Sopenharmony_ci						   NULL);
908c2ecf20Sopenharmony_ci	if (!i2c_ic->irq_domain) {
918c2ecf20Sopenharmony_ci		ret = -ENOMEM;
928c2ecf20Sopenharmony_ci		goto err_iounmap;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	i2c_ic->irq_domain->name = "aspeed-i2c-domain";
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	irq_set_chained_handler_and_data(i2c_ic->parent_irq,
988c2ecf20Sopenharmony_ci					 aspeed_i2c_ic_irq_handler, i2c_ic);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	pr_info("i2c controller registered, irq %d\n", i2c_ic->parent_irq);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cierr_iounmap:
1058c2ecf20Sopenharmony_ci	iounmap(i2c_ic->base);
1068c2ecf20Sopenharmony_cierr_free_ic:
1078c2ecf20Sopenharmony_ci	kfree(i2c_ic);
1088c2ecf20Sopenharmony_ci	return ret;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(ast2400_i2c_ic, "aspeed,ast2400-i2c-ic", aspeed_i2c_ic_of_init);
1128c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(ast2500_i2c_ic, "aspeed,ast2500-i2c-ic", aspeed_i2c_ic_of_init);
113