diff --git a/Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml similarity index 68% rename from Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100-intc.yaml rename to Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml index ada5788602d65e..d8a0a3862ae2c0 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100-intc.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml @@ -1,13 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/interrupt-controller/starfive,jh8100-intc.yaml# +$id: http://devicetree.org/schemas/interrupt-controller/starfive,jhb100-intc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: StarFive External Interrupt Controller description: - StarFive SoC JH8100 contain a external interrupt controller. It can be used + StarFive SoC JHB100 contain a external interrupt controller. It can be used to handle high-level input interrupt signals. It also send the output interrupt signal to RISC-V PLIC. @@ -16,19 +16,11 @@ maintainers: properties: compatible: - const: starfive,jh8100-intc + const: starfive,jhb100-intc reg: maxItems: 1 - clocks: - description: APB clock for the interrupt controller - maxItems: 1 - - resets: - description: APB reset for the interrupt controller - maxItems: 1 - interrupts: maxItems: 1 @@ -40,8 +32,6 @@ properties: required: - compatible - reg - - clocks - - resets - interrupts - interrupt-controller - "#interrupt-cells" @@ -51,10 +41,8 @@ additionalProperties: false examples: - | interrupt-controller@12260000 { - compatible = "starfive,jh8100-intc"; + compatible = "starfive,jhb100-intc"; reg = <0x12260000 0x10000>; - clocks = <&syscrg_ne 76>; - resets = <&syscrg_ne 13>; interrupts = <45>; interrupt-controller; #interrupt-cells = <1>; diff --git a/MAINTAINERS b/MAINTAINERS index 7d10988cbc62b7..8789220899fecb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25300,11 +25300,11 @@ F: Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml F: drivers/phy/starfive/phy-jh7110-pcie.c F: drivers/phy/starfive/phy-jh7110-usb.c -STARFIVE JH8100 EXTERNAL INTERRUPT CONTROLLER DRIVER +STARFIVE JHB100 EXTERNAL INTERRUPT CONTROLLER DRIVER M: Changhuang Liang S: Supported -F: Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100-intc.yaml -F: drivers/irqchip/irq-starfive-jh8100-intc.c +F: Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml +F: drivers/irqchip/irq-starfive-jhb100-intc.c STATIC BRANCH/CALL M: Peter Zijlstra diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index f07b00d7fef907..697c6b2e006cb7 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -651,13 +651,13 @@ config SIFIVE_PLIC select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP -config STARFIVE_JH8100_INTC - bool "StarFive JH8100 External Interrupt Controller" +config STARFIVE_JHB100_INTC + bool "StarFive JHB100 External Interrupt Controller" depends on ARCH_STARFIVE || COMPILE_TEST default ARCH_STARFIVE select IRQ_DOMAIN_HIERARCHY help - This enables support for the INTC chip found in StarFive JH8100 + This enables support for the INTC chip found in StarFive JHB100 SoC. If you don't know what to do here, say Y. diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 26aa3b6ec99fd4..c686caaa44518d 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -108,7 +108,7 @@ obj-$(CONFIG_RISCV_APLIC_MSI) += irq-riscv-aplic-msi.o obj-$(CONFIG_RISCV_IMSIC) += irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o obj-$(CONFIG_RISCV_RPMI_SYSMSI) += irq-riscv-rpmi-sysmsi.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o -obj-$(CONFIG_STARFIVE_JH8100_INTC) += irq-starfive-jh8100-intc.o +obj-$(CONFIG_STARFIVE_JHB100_INTC) += irq-starfive-jhb100-intc.o obj-$(CONFIG_ACLINT_SSWI) += irq-aclint-sswi.o obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o diff --git a/drivers/irqchip/irq-starfive-jh8100-intc.c b/drivers/irqchip/irq-starfive-jh8100-intc.c deleted file mode 100644 index bb62ef363d0bdd..00000000000000 --- a/drivers/irqchip/irq-starfive-jh8100-intc.c +++ /dev/null @@ -1,207 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * StarFive JH8100 External Interrupt Controller driver - * - * Copyright (C) 2023 StarFive Technology Co., Ltd. - * - * Author: Changhuang Liang - */ - -#define pr_fmt(fmt) "irq-starfive-jh8100: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define STARFIVE_INTC_SRC0_CLEAR 0x10 -#define STARFIVE_INTC_SRC0_MASK 0x14 -#define STARFIVE_INTC_SRC0_INT 0x1c - -#define STARFIVE_INTC_SRC_IRQ_NUM 32 - -struct starfive_irq_chip { - void __iomem *base; - struct irq_domain *domain; - raw_spinlock_t lock; -}; - -static void starfive_intc_bit_set(struct starfive_irq_chip *irqc, - u32 reg, u32 bit_mask) -{ - u32 value; - - value = ioread32(irqc->base + reg); - value |= bit_mask; - iowrite32(value, irqc->base + reg); -} - -static void starfive_intc_bit_clear(struct starfive_irq_chip *irqc, - u32 reg, u32 bit_mask) -{ - u32 value; - - value = ioread32(irqc->base + reg); - value &= ~bit_mask; - iowrite32(value, irqc->base + reg); -} - -static void starfive_intc_unmask(struct irq_data *d) -{ - struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); - - raw_spin_lock(&irqc->lock); - starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq)); - raw_spin_unlock(&irqc->lock); -} - -static void starfive_intc_mask(struct irq_data *d) -{ - struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); - - raw_spin_lock(&irqc->lock); - starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq)); - raw_spin_unlock(&irqc->lock); -} - -static struct irq_chip intc_dev = { - .name = "StarFive JH8100 INTC", - .irq_unmask = starfive_intc_unmask, - .irq_mask = starfive_intc_mask, -}; - -static int starfive_intc_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_domain_set_info(d, irq, hwirq, &intc_dev, d->host_data, - handle_level_irq, NULL, NULL); - - return 0; -} - -static const struct irq_domain_ops starfive_intc_domain_ops = { - .xlate = irq_domain_xlate_onecell, - .map = starfive_intc_map, -}; - -static void starfive_intc_irq_handler(struct irq_desc *desc) -{ - struct starfive_irq_chip *irqc = irq_data_get_irq_handler_data(&desc->irq_data); - struct irq_chip *chip = irq_desc_get_chip(desc); - unsigned long value; - int hwirq; - - chained_irq_enter(chip, desc); - - value = ioread32(irqc->base + STARFIVE_INTC_SRC0_INT); - while (value) { - hwirq = ffs(value) - 1; - - generic_handle_domain_irq(irqc->domain, hwirq); - - starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq)); - starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq)); - - __clear_bit(hwirq, &value); - } - - chained_irq_exit(chip, desc); -} - -static int starfive_intc_probe(struct platform_device *pdev, struct device_node *parent) -{ - struct device_node *intc = pdev->dev.of_node; - struct starfive_irq_chip *irqc; - struct reset_control *rst; - struct clk *clk; - int parent_irq; - int ret; - - irqc = kzalloc_obj(*irqc); - if (!irqc) - return -ENOMEM; - - irqc->base = of_iomap(intc, 0); - if (!irqc->base) { - pr_err("Unable to map registers\n"); - ret = -ENXIO; - goto err_free; - } - - rst = of_reset_control_get_exclusive(intc, NULL); - if (IS_ERR(rst)) { - pr_err("Unable to get reset control %pe\n", rst); - ret = PTR_ERR(rst); - goto err_unmap; - } - - clk = of_clk_get(intc, 0); - if (IS_ERR(clk)) { - pr_err("Unable to get clock %pe\n", clk); - ret = PTR_ERR(clk); - goto err_reset_put; - } - - ret = reset_control_deassert(rst); - if (ret) - goto err_clk_put; - - ret = clk_prepare_enable(clk); - if (ret) - goto err_reset_assert; - - raw_spin_lock_init(&irqc->lock); - - irqc->domain = irq_domain_create_linear(of_fwnode_handle(intc), STARFIVE_INTC_SRC_IRQ_NUM, - &starfive_intc_domain_ops, irqc); - if (!irqc->domain) { - pr_err("Unable to create IRQ domain\n"); - ret = -EINVAL; - goto err_clk_disable; - } - - parent_irq = of_irq_get(intc, 0); - if (parent_irq < 0) { - pr_err("Failed to get main IRQ: %d\n", parent_irq); - ret = parent_irq; - goto err_remove_domain; - } - - irq_set_chained_handler_and_data(parent_irq, starfive_intc_irq_handler, - irqc); - - pr_info("Interrupt controller register, nr_irqs %d\n", - STARFIVE_INTC_SRC_IRQ_NUM); - - return 0; - -err_remove_domain: - irq_domain_remove(irqc->domain); -err_clk_disable: - clk_disable_unprepare(clk); -err_reset_assert: - reset_control_assert(rst); -err_clk_put: - clk_put(clk); -err_reset_put: - reset_control_put(rst); -err_unmap: - iounmap(irqc->base); -err_free: - kfree(irqc); - return ret; -} - -IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc) -IRQCHIP_MATCH("starfive,jh8100-intc", starfive_intc_probe) -IRQCHIP_PLATFORM_DRIVER_END(starfive_intc) - -MODULE_DESCRIPTION("StarFive JH8100 External Interrupt Controller"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Changhuang Liang "); diff --git a/drivers/irqchip/irq-starfive-jhb100-intc.c b/drivers/irqchip/irq-starfive-jhb100-intc.c new file mode 100644 index 00000000000000..0d5914813afd21 --- /dev/null +++ b/drivers/irqchip/irq-starfive-jhb100-intc.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JHB100 External Interrupt Controller driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Changhuang Liang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STARFIVE_INTC_SRC_TYPE(n) (0x04 + ((n) * 0x20)) +#define STARFIVE_INTC_SRC_CLEAR(n) (0x10 + ((n) * 0x20)) +#define STARFIVE_INTC_SRC_MASK(n) (0x14 + ((n) * 0x20)) +#define STARFIVE_INTC_SRC_INT(n) (0x1c + ((n) * 0x20)) + +#define STARFIVE_INTC_TRIGGER_MASK 0x3 +#define STARFIVE_INTC_TRIGGER_HIGH 0 +#define STARFIVE_INTC_TRIGGER_LOW 1 +#define STARFIVE_INTC_TRIGGER_POSEDGE 2 +#define STARFIVE_INTC_TRIGGER_NEGEDGE 3 + +#define STARFIVE_INTC_NUM 2 +#define STARFIVE_INTC_SRC_IRQ_NUM 32 +#define STARFIVE_INTC_TYPE_NUM 16 + +struct starfive_irq_chip { + void __iomem *base; + struct irq_domain *domain; + raw_spinlock_t lock; +}; + +static void starfive_intc_mod(struct starfive_irq_chip *irqc, u32 reg, u32 mask, u32 data) +{ + u32 value; + + value = ioread32(irqc->base + reg) & ~mask; + data &= mask; + data |= value; + iowrite32(data, irqc->base + reg); +} + +static void starfive_intc_bit_set(struct starfive_irq_chip *irqc, + u32 reg, u32 bit_mask) +{ + u32 value; + + value = ioread32(irqc->base + reg); + value |= bit_mask; + iowrite32(value, irqc->base + reg); +} + +static void starfive_intc_bit_clear(struct starfive_irq_chip *irqc, + u32 reg, u32 bit_mask) +{ + u32 value; + + value = ioread32(irqc->base + reg); + value &= ~bit_mask; + iowrite32(value, irqc->base + reg); +} + +static void starfive_intc_unmask(struct irq_data *d) +{ + struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); + int i, bitpos; + + i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM; + bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM; + + guard(raw_spinlock)(&irqc->lock); + starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos)); +} + +static void starfive_intc_mask(struct irq_data *d) +{ + struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); + int i, bitpos; + + i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM; + bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM; + + guard(raw_spinlock)(&irqc->lock); + starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos)); +} + +static void starfive_intc_ack(struct irq_data *d) +{ + /* for handle_edge_irq, nothing to do */ +} + +static int starfive_intc_set_type(struct irq_data *d, unsigned int type) +{ + struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); + u32 i, bitpos, ty_pos, ty_shift, trigger, typeval; + irq_flow_handler_t handler; + + i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM; + bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM; + ty_pos = bitpos / STARFIVE_INTC_TYPE_NUM; + ty_shift = (bitpos % STARFIVE_INTC_TYPE_NUM) * 2; + + switch (type) { + case IRQF_TRIGGER_LOW: + trigger = STARFIVE_INTC_TRIGGER_LOW; + handler = handle_level_irq; + break; + case IRQF_TRIGGER_HIGH: + trigger = STARFIVE_INTC_TRIGGER_HIGH; + handler = handle_level_irq; + break; + case IRQF_TRIGGER_FALLING: + trigger = STARFIVE_INTC_TRIGGER_NEGEDGE; + handler = handle_edge_irq; + break; + case IRQF_TRIGGER_RISING: + trigger = STARFIVE_INTC_TRIGGER_POSEDGE; + handler = handle_edge_irq; + break; + default: + return -EINVAL; + } + + irq_set_handler_locked(d, handler); + typeval = trigger << ty_shift; + + guard(raw_spinlock)(&irqc->lock); + + starfive_intc_mod(irqc, STARFIVE_INTC_SRC_TYPE(i) + 4 * ty_pos, + STARFIVE_INTC_TRIGGER_MASK << ty_shift, typeval); + + /* Once the type is updated, clear interrupt can help to reset the type value */ + starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(bitpos)); + starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(bitpos)); + + return 0; +} + +static struct irq_chip intc_dev = { + .name = "StarFive JHB100 INTC", + .irq_unmask = starfive_intc_unmask, + .irq_mask = starfive_intc_mask, + .irq_ack = starfive_intc_ack, + .irq_set_type = starfive_intc_set_type, +}; + +static int starfive_intc_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_domain_set_info(d, irq, hwirq, &intc_dev, d->host_data, + handle_level_irq, NULL, NULL); + + return 0; +} + +static const struct irq_domain_ops starfive_intc_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = starfive_intc_map, +}; + +static void starfive_intc_irq_handler(struct irq_desc *desc) +{ + struct starfive_irq_chip *irqc = irq_data_get_irq_handler_data(&desc->irq_data); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long value; + int hwirq; + + chained_irq_enter(chip, desc); + + for (int i = 0; i < STARFIVE_INTC_NUM; i++) { + value = ioread32(irqc->base + STARFIVE_INTC_SRC_INT(i)); + while (value) { + hwirq = ffs(value) - 1; + + generic_handle_domain_irq(irqc->domain, + hwirq + i * STARFIVE_INTC_SRC_IRQ_NUM); + + starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(hwirq)); + starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(hwirq)); + + __clear_bit(hwirq, &value); + } + } + + chained_irq_exit(chip, desc); +} + +static int starfive_intc_probe(struct platform_device *pdev, struct device_node *parent) +{ + struct device_node *intc = pdev->dev.of_node; + struct reset_control *rst; + struct clk *clk; + int parent_irq; + + struct starfive_irq_chip *irqc __free(kfree) = kzalloc_obj(*irqc); + if (!irqc) + return -ENOMEM; + + irqc->base = devm_platform_ioremap_resource(pdev, 0); + if (!irqc->base) + return dev_err_probe(&pdev->dev, -ENXIO, "unable to map registers\n"); + + rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Unable to get and deassert reset control\n"); + + clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk), "Unable to get and enable clock\n"); + + + raw_spin_lock_init(&irqc->lock); + + irqc->domain = irq_domain_create_linear(of_fwnode_handle(intc), + STARFIVE_INTC_SRC_IRQ_NUM * STARFIVE_INTC_NUM, + &starfive_intc_domain_ops, irqc); + if (!irqc->domain) + return dev_err_probe(&pdev->dev, -EINVAL, "Unable to create IRQ domain\n"); + + parent_irq = of_irq_get(intc, 0); + if (parent_irq < 0) { + irq_domain_remove(irqc->domain); + return dev_err_probe(&pdev->dev, parent_irq, "Failed to get main IRQ\n"); + } + + irq_set_chained_handler_and_data(parent_irq, starfive_intc_irq_handler, + irqc); + + dev_info(&pdev->dev, "Interrupt controller register, nr_irqs %d\n", + STARFIVE_INTC_SRC_IRQ_NUM * STARFIVE_INTC_NUM); + + retain_and_null_ptr(irqc); + return 0; +} + +IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc) +IRQCHIP_MATCH("starfive,jhb100-intc", starfive_intc_probe) +IRQCHIP_PLATFORM_DRIVER_END(starfive_intc) + +MODULE_DESCRIPTION("StarFive JHB100 External Interrupt Controller"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Changhuang Liang ");