Re: [DO NOT MERGE v5 16/37] irqchip: Add SH7751 INTC driver

2023-12-08 Thread Thomas Gleixner
On Tue, Dec 05 2023 at 18:45, Yoshinori Sato wrote:
> +static void endisable_irq(struct irq_data *data, int enable)

bool enable?

> +{
> + struct sh7751_intc_priv *priv;
> + unsigned int irq;
> +
> + priv = irq_data_to_priv(data);
> +
> + irq = irqd_to_hwirq(data);
> + if (!is_valid_irq(irq)) {
> + /* IRQ out of range */
> + pr_warn_once("%s: IRQ %u is out of range\n", __FILE__, irq);
> + return;
> + }
> +
> + if (irq <= MAX_IRL && !priv->irlm)
> + /* IRL encoded external interrupt */
> + /* disable for SR.IMASK */
> + update_sr_imask(irq - IRQ_START, enable);
> + else
> + /* Internal peripheral interrupt */
> + /* mask for IPR priority 0 */
> + update_ipr(priv, irq, enable);

Lacks curly brackets on the if/else

> +static int irq_sh7751_map(struct irq_domain *h, unsigned int virq,
> +   irq_hw_number_t hw_irq_num)
> +{
> + irq_set_chip_and_handler(virq, _irq_chip, handle_level_irq);
> + irq_get_irq_data(virq)->chip_data = h->host_data;
> + irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOPROBE);
> + return 0;
> +}
> +static const struct irq_domain_ops irq_ops = {

Newline before 'static ...'

> + .map= irq_sh7751_map,
> + .xlate  = irq_domain_xlate_onecell,
> +};
> +
> +static int __init load_ipr_map(struct device_node *intc,
> +struct sh7751_intc_priv *priv)
> +{
> + struct property *ipr_map;
> + unsigned int num_ipr, i;
> + struct ipr *ipr;
> + const __be32 *p;
> + u32 irq;
> +
> + ipr_map = of_find_property(intc, "renesas,ipr-map", _ipr);
> + if (IS_ERR(ipr_map))
> + return PTR_ERR(ipr_map);
> + num_ipr /= sizeof(u32);
> + /* 3words per entry. */
> + if (num_ipr % 3)

Three words per ... But you can spare the comment by doing:

if (num_ipr % WORDS_PER_ENTRY)

> + goto error1;
> + num_ipr /= 3;
> +static int __init sh7751_intc_of_init(struct device_node *intc,
> +   struct device_node *parent)
> +{
> + struct sh7751_intc_priv *priv;
> + void __iomem *base, *base2;
> + struct irq_domain *domain;
> + u16 icr;
> + int ret;
> +
> + base = of_iomap(intc, 0);
> + base2 = of_iomap(intc, 1);
> + if (!base || !base2) {
> + pr_err("%pOFP: Invalid register definition\n", intc);

What unmaps 'base' if 'base' is valid and base2 == NULL?

> + return -EINVAL;
> + }
> +
> + priv = kzalloc(sizeof(struct sh7751_intc_priv), GFP_KERNEL);
> + if (priv == NULL)
> + return -ENOMEM;

Leaks base[2] maps, no?

> + ret = load_ipr_map(intc, priv);
> + if (ret < 0) {
> + kfree(priv);
> + return ret;
> + }
> +
> + priv->base = base;
> + priv->intpri00 = base2;
> +
> + if (of_property_read_bool(intc, "renesas,irlm")) {
> + priv->irlm = true;
> + icr = __raw_readw(priv->base + R_ICR);
> + icr |= ICR_IRLM;
> + __raw_writew(icr, priv->base + R_ICR);
> + }
> +
> + domain = irq_domain_add_linear(intc, NR_IRQS, _ops, priv);
> + if (domain == NULL) {
> + pr_err("%pOFP: cannot initialize irq domain\n", intc);
> + kfree(priv);
> + return -ENOMEM;
> + }
> +
> + irq_set_default_host(domain);
> + pr_info("%pOFP: SH7751 Interrupt controller (%s external IRQ)",
> + intc, priv->irlm ? "4 lines" : "15 level");
> + return 0;
> +}
> +
> +IRQCHIP_DECLARE(sh_7751_intc,
> + "renesas,sh7751-intc", sh7751_intc_of_init);

One line please.

Thanks,

tglx


[DO NOT MERGE v5 16/37] irqchip: Add SH7751 INTC driver

2023-12-05 Thread Yoshinori Sato
Renesas SH7751 Internal interrupt controller driver.

Signed-off-by: Yoshinori Sato 
---
 drivers/irqchip/Kconfig  |   8 +
 drivers/irqchip/Makefile |   1 +
 drivers/irqchip/irq-renesas-sh7751.c | 290 +++
 3 files changed, 299 insertions(+)
 create mode 100644 drivers/irqchip/irq-renesas-sh7751.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index f7149d0f3d45..658523f65b1d 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -679,4 +679,12 @@ config SUNPLUS_SP7021_INTC
  chained controller, routing all interrupt source in P-Chip to
  the primary controller on C-Chip.
 
+config RENESAS_SH7751_INTC
+   bool "Renesas SH7751 Interrupt Controller"
+   depends on SH_DEVICE_TREE || COMPILE_TEST
+   select IRQ_DOMAIN_HIERARCHY
+   help
+ Support for the Renesas SH7751 On-chip interrupt controller.
+ And external interrupt encoder for some targets.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ffd945fe71aa..26c91d075e25 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -120,3 +120,4 @@ obj-$(CONFIG_IRQ_IDT3243X)  += irq-idt3243x.o
 obj-$(CONFIG_APPLE_AIC)+= irq-apple-aic.o
 obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o
 obj-$(CONFIG_SUNPLUS_SP7021_INTC)  += irq-sp7021-intc.o
+obj-$(CONFIG_RENESAS_SH7751_INTC)  += irq-renesas-sh7751.o
diff --git a/drivers/irqchip/irq-renesas-sh7751.c 
b/drivers/irqchip/irq-renesas-sh7751.c
new file mode 100644
index ..2a5cb2444d99
--- /dev/null
+++ b/drivers/irqchip/irq-renesas-sh7751.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas SH7751 interrupt controller driver
+ *
+ * Copyright 2023 Yoshinori Sato 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+struct ipr {
+   unsigned int off;
+   unsigned int idx;
+};
+
+struct sh7751_intc_priv {
+   void __iomem *base;
+   void __iomem *intpri00;
+   struct ipr   *iprmap[2];
+   bool irlm;
+};
+
+enum {
+   R_ICR = 0x00,
+   R_IPR = 0x04,
+   R_INTPRI00= 0x00,
+   R_INTREQ00= 0x20,
+   R_INTMSK00= 0x40,
+   R_INTMSKCLR00 = 0x60,
+};
+
+#define ICR_IRLM BIT(7)
+
+/*
+ * SH7751 IRQ mapping
+ *  IRQ16 - 63: Group0 - IPRA to IPRD
+ *   IRQ16 - 31: external IRL input (ICR.IRLM is 0)
+ *  IRQ80 - 92: Group1 - INTPRI00
+ */
+#define IRQ_START  16
+#define MAX_IRL(IRQ_START + NR_IRL)
+#define GRP0_IRQ_END   63
+#define GRP1_IRQ_START 80
+#define IRQ_END92
+
+#define NR_IPRMAP0 (GRP0_IRQ_END - IRQ_START + 1)
+#define NR_IPRMAP1 (IRQ_END - GRP1_IRQ_START)
+#define IPR_PRI_MASK   0x000f
+
+/*
+ * IPR registers have 4bit priority x 4 entry (16bits)
+ * Interrupts can be masked by setting pri to 0.
+ */
+static void update_ipr(struct sh7751_intc_priv *priv, unsigned int irq, u16 
pri)
+{
+   struct ipr *ipr = NULL;
+   void __iomem *ipr_base;
+   unsigned int offset;
+   u16 mask;
+
+   if (irq < GRP1_IRQ_START) {
+   /* Group0 */
+   ipr = priv->iprmap[0];
+   ipr += irq - IRQ_START;
+   ipr_base = priv->base + R_IPR;
+   offset = ipr->off;
+   } else {
+   /* Group1 */
+   ipr = priv->iprmap[1];
+   ipr += irq - GRP1_IRQ_START;
+   ipr_base = priv->intpri00;
+   offset = ipr->off - INTPRI00;
+   }
+   if (ipr->off != ~0) {
+   mask = ~(IPR_PRI_MASK << ipr->idx);
+   pri = (pri & IPR_PRI_MASK) << ipr->idx;
+   mask &= __raw_readw(ipr_base + offset);
+   __raw_writew(mask | pri, ipr_base + offset);
+   } else {
+   pr_warn_once("%s: undefined IPR in irq %u\n", __FILE__, irq);
+   }
+}
+
+static inline bool is_valid_irq(unsigned int irq)
+{
+   /* IRQ16 - 63 */
+   if (irq >= IRQ_START && irq < IRQ_START + NR_IPRMAP0)
+   return true;
+   /* IRQ80 - 92 */
+   if (irq >= GRP1_IRQ_START && irq <= IRQ_END)
+   return true;
+   return false;
+}
+
+static inline struct sh7751_intc_priv *irq_data_to_priv(struct irq_data *data)
+{
+   return data->domain->host_data;
+}
+
+static void endisable_irq(struct irq_data *data, int enable)
+{
+   struct sh7751_intc_priv *priv;
+   unsigned int irq;
+
+   priv = irq_data_to_priv(data);
+
+   irq = irqd_to_hwirq(data);
+   if (!is_valid_irq(irq)) {
+   /* IRQ out of range */
+   pr_warn_once("%s: IRQ %u is out of range\n", __FILE__, irq);
+   return;
+   }
+
+   if (irq <= MAX_IRL && !priv->irlm)
+   /* IRL encoded external interrupt */
+   /* disable for SR.IMASK */
+   update_sr_imask(irq - IRQ_START, enable);
+