]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
irqchip/aspeed-scu-ic: Refactor driver to support variant-based initialization
authorRyan Chen <ryan_chen@aspeedtech.com>
Mon, 8 Sep 2025 01:18:09 +0000 (09:18 +0800)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 9 Sep 2025 10:23:28 +0000 (12:23 +0200)
The SCU IC driver handles each AST2600 instance with separate
initialization functions and hardcoded register definitions, which is
inflexible and creates duplicated code.

Consolidate the implementation by introducing a variant-based structure,
selected via compatible string, and use a unified init path and MMIO access
via of_iomap().  This simplifies the code and prepares for upcoming SoCs
like AST2700, which require split register handling.

[ tglx: Cleaned up coding style and massaged change log ]

Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250908011812.1033858-2-ryan_chen@aspeedtech.com
drivers/irqchip/irq-aspeed-scu-ic.c

index 1c7045467c48605cca8856daeea9336bdf5a5d27..9c1fbdd1ce6579d28201403db8def25a35994534 100644 (file)
@@ -7,55 +7,56 @@
  */
 
 #include <linux/bitops.h>
+#include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/irqchip.h>
 #include <linux/irqchip/chained_irq.h>
 #include <linux/irqdomain.h>
-#include <linux/mfd/syscon.h>
+#include <linux/of_address.h>
 #include <linux/of_irq.h>
-#include <linux/regmap.h>
 
-#define ASPEED_SCU_IC_REG              0x018
-#define ASPEED_SCU_IC_SHIFT            0
-#define ASPEED_SCU_IC_ENABLE           GENMASK(15, ASPEED_SCU_IC_SHIFT)
-#define ASPEED_SCU_IC_NUM_IRQS         7
 #define ASPEED_SCU_IC_STATUS           GENMASK(28, 16)
 #define ASPEED_SCU_IC_STATUS_SHIFT     16
 
-#define ASPEED_AST2600_SCU_IC0_REG     0x560
-#define ASPEED_AST2600_SCU_IC0_SHIFT   0
-#define ASPEED_AST2600_SCU_IC0_ENABLE  \
-       GENMASK(5, ASPEED_AST2600_SCU_IC0_SHIFT)
-#define ASPEED_AST2600_SCU_IC0_NUM_IRQS        6
+struct aspeed_scu_ic_variant {
+       const char      *compatible;
+       unsigned long   irq_enable;
+       unsigned long   irq_shift;
+       unsigned int    num_irqs;
+};
+
+#define SCU_VARIANT(_compat, _shift, _enable, _num) {  \
+       .compatible             =       _compat,        \
+       .irq_shift              =       _shift,         \
+       .irq_enable             =       _enable,        \
+       .num_irqs               =       _num,           \
+}
 
-#define ASPEED_AST2600_SCU_IC1_REG     0x570
-#define ASPEED_AST2600_SCU_IC1_SHIFT   4
-#define ASPEED_AST2600_SCU_IC1_ENABLE  \
-       GENMASK(5, ASPEED_AST2600_SCU_IC1_SHIFT)
-#define ASPEED_AST2600_SCU_IC1_NUM_IRQS        2
+static const struct aspeed_scu_ic_variant scu_ic_variants[]    __initconst = {
+       SCU_VARIANT("aspeed,ast2400-scu-ic",    0, GENMASK(15, 0),      7),
+       SCU_VARIANT("aspeed,ast2500-scu-ic",    0, GENMASK(15, 0),      7),
+       SCU_VARIANT("aspeed,ast2600-scu-ic0",   0, GENMASK(5, 0),       6),
+       SCU_VARIANT("aspeed,ast2600-scu-ic1",   4, GENMASK(5, 4),       2),
+};
 
 struct aspeed_scu_ic {
-       unsigned long irq_enable;
-       unsigned long irq_shift;
-       unsigned int num_irqs;
-       unsigned int reg;
-       struct regmap *scu;
-       struct irq_domain *irq_domain;
+       unsigned long           irq_enable;
+       unsigned long           irq_shift;
+       unsigned int            num_irqs;
+       void __iomem            *base;
+       struct irq_domain       *irq_domain;
 };
 
 static void aspeed_scu_ic_irq_handler(struct irq_desc *desc)
 {
-       unsigned int sts;
-       unsigned long bit;
-       unsigned long enabled;
-       unsigned long max;
-       unsigned long status;
        struct aspeed_scu_ic *scu_ic = irq_desc_get_handler_data(desc);
        struct irq_chip *chip = irq_desc_get_chip(desc);
-       unsigned int mask = scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT;
+       unsigned long bit, enabled, max, status;
+       unsigned int sts, mask;
 
        chained_irq_enter(chip, desc);
 
+       mask = scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT;
        /*
         * The SCU IC has just one register to control its operation and read
         * status. The interrupt enable bits occupy the lower 16 bits of the
@@ -66,7 +67,7 @@ static void aspeed_scu_ic_irq_handler(struct irq_desc *desc)
         * shifting the status down to get the mapping and then back up to
         * clear the bit.
         */
-       regmap_read(scu_ic->scu, scu_ic->reg, &sts);
+       sts = readl(scu_ic->base);
        enabled = sts & scu_ic->irq_enable;
        status = (sts >> ASPEED_SCU_IC_STATUS_SHIFT) & enabled;
 
@@ -74,11 +75,9 @@ static void aspeed_scu_ic_irq_handler(struct irq_desc *desc)
        max = scu_ic->num_irqs + bit;
 
        for_each_set_bit_from(bit, &status, max) {
-               generic_handle_domain_irq(scu_ic->irq_domain,
-                                         bit - scu_ic->irq_shift);
-
-               regmap_write_bits(scu_ic->scu, scu_ic->reg, mask,
-                                 BIT(bit + ASPEED_SCU_IC_STATUS_SHIFT));
+               generic_handle_domain_irq(scu_ic->irq_domain, bit - scu_ic->irq_shift);
+               writel((readl(scu_ic->base) & ~mask) | BIT(bit + ASPEED_SCU_IC_STATUS_SHIFT),
+                      scu_ic->base);
        }
 
        chained_irq_exit(chip, desc);
@@ -87,30 +86,29 @@ static void aspeed_scu_ic_irq_handler(struct irq_desc *desc)
 static void aspeed_scu_ic_irq_mask(struct irq_data *data)
 {
        struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(data);
-       unsigned int mask = BIT(data->hwirq + scu_ic->irq_shift) |
-               (scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT);
+       unsigned int bit = BIT(data->hwirq + scu_ic->irq_shift);
+       unsigned int mask = bit | (scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT);
 
        /*
         * Status bits are cleared by writing 1. In order to prevent the mask
         * operation from clearing the status bits, they should be under the
         * mask and written with 0.
         */
-       regmap_update_bits(scu_ic->scu, scu_ic->reg, mask, 0);
+       writel(readl(scu_ic->base) & ~mask, scu_ic->base);
 }
 
 static void aspeed_scu_ic_irq_unmask(struct irq_data *data)
 {
        struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(data);
        unsigned int bit = BIT(data->hwirq + scu_ic->irq_shift);
-       unsigned int mask = bit |
-               (scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT);
+       unsigned int mask = bit | (scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT);
 
        /*
         * Status bits are cleared by writing 1. In order to prevent the unmask
         * operation from clearing the status bits, they should be under the
         * mask and written with 0.
         */
-       regmap_update_bits(scu_ic->scu, scu_ic->reg, mask, bit);
+       writel((readl(scu_ic->base) & ~mask) | bit, scu_ic->base);
 }
 
 static int aspeed_scu_ic_irq_set_affinity(struct irq_data *data,
@@ -143,21 +141,15 @@ static const struct irq_domain_ops aspeed_scu_ic_domain_ops = {
 static int aspeed_scu_ic_of_init_common(struct aspeed_scu_ic *scu_ic,
                                        struct device_node *node)
 {
-       int irq;
-       int rc = 0;
+       int irq, rc = 0;
 
-       if (!node->parent) {
-               rc = -ENODEV;
+       scu_ic->base = of_iomap(node, 0);
+       if (IS_ERR(scu_ic->base)) {
+               rc = PTR_ERR(scu_ic->base);
                goto err;
        }
-
-       scu_ic->scu = syscon_node_to_regmap(node->parent);
-       if (IS_ERR(scu_ic->scu)) {
-               rc = PTR_ERR(scu_ic->scu);
-               goto err;
-       }
-       regmap_write_bits(scu_ic->scu, scu_ic->reg, ASPEED_SCU_IC_STATUS, ASPEED_SCU_IC_STATUS);
-       regmap_write_bits(scu_ic->scu, scu_ic->reg, ASPEED_SCU_IC_ENABLE, 0);
+       writel(ASPEED_SCU_IC_STATUS, scu_ic->base);
+       writel(0, scu_ic->base);
 
        irq = irq_of_parse_and_map(node, 0);
        if (!irq) {
@@ -166,8 +158,7 @@ static int aspeed_scu_ic_of_init_common(struct aspeed_scu_ic *scu_ic,
        }
 
        scu_ic->irq_domain = irq_domain_create_linear(of_fwnode_handle(node), scu_ic->num_irqs,
-                                                  &aspeed_scu_ic_domain_ops,
-                                                  scu_ic);
+                                                     &aspeed_scu_ic_domain_ops, scu_ic);
        if (!scu_ic->irq_domain) {
                rc = -ENOMEM;
                goto err;
@@ -180,61 +171,39 @@ static int aspeed_scu_ic_of_init_common(struct aspeed_scu_ic *scu_ic,
 
 err:
        kfree(scu_ic);
-
        return rc;
 }
 
-static int __init aspeed_scu_ic_of_init(struct device_node *node,
-                                       struct device_node *parent)
+static const struct aspeed_scu_ic_variant *aspeed_scu_ic_find_variant(struct device_node *np)
 {
-       struct aspeed_scu_ic *scu_ic = kzalloc(sizeof(*scu_ic), GFP_KERNEL);
-
-       if (!scu_ic)
-               return -ENOMEM;
-
-       scu_ic->irq_enable = ASPEED_SCU_IC_ENABLE;
-       scu_ic->irq_shift = ASPEED_SCU_IC_SHIFT;
-       scu_ic->num_irqs = ASPEED_SCU_IC_NUM_IRQS;
-       scu_ic->reg = ASPEED_SCU_IC_REG;
-
-       return aspeed_scu_ic_of_init_common(scu_ic, node);
+       for (int i = 0; i < ARRAY_SIZE(scu_ic_variants); i++) {
+               if (of_device_is_compatible(np, scu_ic_variants[i].compatible))
+                       return &scu_ic_variants[i];
+       }
+       return NULL;
 }
 
-static int __init aspeed_ast2600_scu_ic0_of_init(struct device_node *node,
-                                                struct device_node *parent)
+static int __init aspeed_scu_ic_of_init(struct device_node *node, struct device_node *parent)
 {
-       struct aspeed_scu_ic *scu_ic = kzalloc(sizeof(*scu_ic), GFP_KERNEL);
-
-       if (!scu_ic)
-               return -ENOMEM;
-
-       scu_ic->irq_enable = ASPEED_AST2600_SCU_IC0_ENABLE;
-       scu_ic->irq_shift = ASPEED_AST2600_SCU_IC0_SHIFT;
-       scu_ic->num_irqs = ASPEED_AST2600_SCU_IC0_NUM_IRQS;
-       scu_ic->reg = ASPEED_AST2600_SCU_IC0_REG;
-
-       return aspeed_scu_ic_of_init_common(scu_ic, node);
-}
+       const struct aspeed_scu_ic_variant *variant;
+       struct aspeed_scu_ic *scu_ic;
 
-static int __init aspeed_ast2600_scu_ic1_of_init(struct device_node *node,
-                                                struct device_node *parent)
-{
-       struct aspeed_scu_ic *scu_ic = kzalloc(sizeof(*scu_ic), GFP_KERNEL);
+       variant = aspeed_scu_ic_find_variant(node);
+       if (!variant)
+               return -ENODEV;
 
+       scu_ic = kzalloc(sizeof(*scu_ic), GFP_KERNEL);
        if (!scu_ic)
                return -ENOMEM;
 
-       scu_ic->irq_enable = ASPEED_AST2600_SCU_IC1_ENABLE;
-       scu_ic->irq_shift = ASPEED_AST2600_SCU_IC1_SHIFT;
-       scu_ic->num_irqs = ASPEED_AST2600_SCU_IC1_NUM_IRQS;
-       scu_ic->reg = ASPEED_AST2600_SCU_IC1_REG;
+       scu_ic->irq_enable      = variant->irq_enable;
+       scu_ic->irq_shift       = variant->irq_shift;
+       scu_ic->num_irqs        = variant->num_irqs;
 
        return aspeed_scu_ic_of_init_common(scu_ic, node);
 }
 
 IRQCHIP_DECLARE(ast2400_scu_ic, "aspeed,ast2400-scu-ic", aspeed_scu_ic_of_init);
 IRQCHIP_DECLARE(ast2500_scu_ic, "aspeed,ast2500-scu-ic", aspeed_scu_ic_of_init);
-IRQCHIP_DECLARE(ast2600_scu_ic0, "aspeed,ast2600-scu-ic0",
-               aspeed_ast2600_scu_ic0_of_init);
-IRQCHIP_DECLARE(ast2600_scu_ic1, "aspeed,ast2600-scu-ic1",
-               aspeed_ast2600_scu_ic1_of_init);
+IRQCHIP_DECLARE(ast2600_scu_ic0, "aspeed,ast2600-scu-ic0", aspeed_scu_ic_of_init);
+IRQCHIP_DECLARE(ast2600_scu_ic1, "aspeed,ast2600-scu-ic1", aspeed_scu_ic_of_init);