Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Loongson LPC Interrupt Controller support
0004  *
0005  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
0006  */
0007 
0008 #define pr_fmt(fmt) "lpc: " fmt
0009 
0010 #include <linux/interrupt.h>
0011 #include <linux/irq.h>
0012 #include <linux/irqchip.h>
0013 #include <linux/irqchip/chained_irq.h>
0014 #include <linux/irqdomain.h>
0015 #include <linux/kernel.h>
0016 
0017 /* Registers */
0018 #define LPC_INT_CTL     0x00
0019 #define LPC_INT_ENA     0x04
0020 #define LPC_INT_STS     0x08
0021 #define LPC_INT_CLR     0x0c
0022 #define LPC_INT_POL     0x10
0023 #define LPC_COUNT       16
0024 
0025 /* LPC_INT_CTL */
0026 #define LPC_INT_CTL_EN      BIT(31)
0027 
0028 struct pch_lpc {
0029     void __iomem        *base;
0030     struct irq_domain   *lpc_domain;
0031     raw_spinlock_t      lpc_lock;
0032     u32         saved_reg_ctl;
0033     u32         saved_reg_ena;
0034     u32         saved_reg_pol;
0035 };
0036 
0037 struct fwnode_handle *pch_lpc_handle;
0038 
0039 static void lpc_irq_ack(struct irq_data *d)
0040 {
0041     unsigned long flags;
0042     struct pch_lpc *priv = d->domain->host_data;
0043 
0044     raw_spin_lock_irqsave(&priv->lpc_lock, flags);
0045     writel(0x1 << d->hwirq, priv->base + LPC_INT_CLR);
0046     raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
0047 }
0048 
0049 static void lpc_irq_mask(struct irq_data *d)
0050 {
0051     unsigned long flags;
0052     struct pch_lpc *priv = d->domain->host_data;
0053 
0054     raw_spin_lock_irqsave(&priv->lpc_lock, flags);
0055     writel(readl(priv->base + LPC_INT_ENA) & (~(0x1 << (d->hwirq))),
0056             priv->base + LPC_INT_ENA);
0057     raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
0058 }
0059 
0060 static void lpc_irq_unmask(struct irq_data *d)
0061 {
0062     unsigned long flags;
0063     struct pch_lpc *priv = d->domain->host_data;
0064 
0065     raw_spin_lock_irqsave(&priv->lpc_lock, flags);
0066     writel(readl(priv->base + LPC_INT_ENA) | (0x1 << (d->hwirq)),
0067             priv->base + LPC_INT_ENA);
0068     raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
0069 }
0070 
0071 static int lpc_irq_set_type(struct irq_data *d, unsigned int type)
0072 {
0073     u32 val;
0074     u32 mask = 0x1 << (d->hwirq);
0075     struct pch_lpc *priv = d->domain->host_data;
0076 
0077     if (!(type & IRQ_TYPE_LEVEL_MASK))
0078         return 0;
0079 
0080     val = readl(priv->base + LPC_INT_POL);
0081 
0082     if (type == IRQ_TYPE_LEVEL_HIGH)
0083         val |= mask;
0084     else
0085         val &= ~mask;
0086 
0087     writel(val, priv->base + LPC_INT_POL);
0088 
0089     return 0;
0090 }
0091 
0092 static const struct irq_chip pch_lpc_irq_chip = {
0093     .name           = "PCH LPC",
0094     .irq_mask       = lpc_irq_mask,
0095     .irq_unmask     = lpc_irq_unmask,
0096     .irq_ack        = lpc_irq_ack,
0097     .irq_set_type       = lpc_irq_set_type,
0098     .flags          = IRQCHIP_SKIP_SET_WAKE,
0099 };
0100 
0101 static void lpc_irq_dispatch(struct irq_desc *desc)
0102 {
0103     u32 pending, bit;
0104     struct irq_chip *chip = irq_desc_get_chip(desc);
0105     struct pch_lpc *priv = irq_desc_get_handler_data(desc);
0106 
0107     chained_irq_enter(chip, desc);
0108 
0109     pending = readl(priv->base + LPC_INT_ENA);
0110     pending &= readl(priv->base + LPC_INT_STS);
0111     if (!pending)
0112         spurious_interrupt();
0113 
0114     while (pending) {
0115         bit = __ffs(pending);
0116 
0117         generic_handle_domain_irq(priv->lpc_domain, bit);
0118         pending &= ~BIT(bit);
0119     }
0120     chained_irq_exit(chip, desc);
0121 }
0122 
0123 static int pch_lpc_map(struct irq_domain *d, unsigned int irq,
0124             irq_hw_number_t hw)
0125 {
0126     irq_set_chip_and_handler(irq, &pch_lpc_irq_chip, handle_level_irq);
0127     return 0;
0128 }
0129 
0130 static const struct irq_domain_ops pch_lpc_domain_ops = {
0131     .map        = pch_lpc_map,
0132     .translate  = irq_domain_translate_twocell,
0133 };
0134 
0135 static void pch_lpc_reset(struct pch_lpc *priv)
0136 {
0137     /* Enable the LPC interrupt, bit31: en  bit30: edge */
0138     writel(LPC_INT_CTL_EN, priv->base + LPC_INT_CTL);
0139     writel(0, priv->base + LPC_INT_ENA);
0140     /* Clear all 18-bit interrpt bit */
0141     writel(GENMASK(17, 0), priv->base + LPC_INT_CLR);
0142 }
0143 
0144 static int pch_lpc_disabled(struct pch_lpc *priv)
0145 {
0146     return (readl(priv->base + LPC_INT_ENA) == 0xffffffff) &&
0147             (readl(priv->base + LPC_INT_STS) == 0xffffffff);
0148 }
0149 
0150 int __init pch_lpc_acpi_init(struct irq_domain *parent,
0151                     struct acpi_madt_lpc_pic *acpi_pchlpc)
0152 {
0153     int parent_irq;
0154     struct pch_lpc *priv;
0155     struct irq_fwspec fwspec;
0156     struct fwnode_handle *irq_handle;
0157 
0158     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
0159     if (!priv)
0160         return -ENOMEM;
0161 
0162     raw_spin_lock_init(&priv->lpc_lock);
0163 
0164     priv->base = ioremap(acpi_pchlpc->address, acpi_pchlpc->size);
0165     if (!priv->base)
0166         goto free_priv;
0167 
0168     if (pch_lpc_disabled(priv)) {
0169         pr_err("Failed to get LPC status\n");
0170         goto iounmap_base;
0171     }
0172 
0173     irq_handle = irq_domain_alloc_named_fwnode("lpcintc");
0174     if (!irq_handle) {
0175         pr_err("Unable to allocate domain handle\n");
0176         goto iounmap_base;
0177     }
0178 
0179     priv->lpc_domain = irq_domain_create_linear(irq_handle, LPC_COUNT,
0180                     &pch_lpc_domain_ops, priv);
0181     if (!priv->lpc_domain) {
0182         pr_err("Failed to create IRQ domain\n");
0183         goto free_irq_handle;
0184     }
0185     pch_lpc_reset(priv);
0186 
0187     fwspec.fwnode = parent->fwnode;
0188     fwspec.param[0] = acpi_pchlpc->cascade + GSI_MIN_PCH_IRQ;
0189     fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
0190     fwspec.param_count = 2;
0191     parent_irq = irq_create_fwspec_mapping(&fwspec);
0192     irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv);
0193 
0194     pch_lpc_handle = irq_handle;
0195     return 0;
0196 
0197 free_irq_handle:
0198     irq_domain_free_fwnode(irq_handle);
0199 iounmap_base:
0200     iounmap(priv->base);
0201 free_priv:
0202     kfree(priv);
0203 
0204     return -ENOMEM;
0205 }