1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020, Jianmin Lv <lvjianmin@loongson.cn> 4 * Loongson LPC support 5 */ 6 7#define pr_fmt(fmt) "lpc: " fmt 8 9#include <linux/interrupt.h> 10#include <linux/irq.h> 11#include <linux/irqchip.h> 12#include <linux/irqchip/chained_irq.h> 13#include <linux/irqdomain.h> 14#include <linux/kernel.h> 15#include <linux/syscore_ops.h> 16 17/* Registers */ 18#define LPC_INT_CTL 0x00 19#define LPC_INT_ENA 0x04 20#define LPC_INT_STS 0x08 21#define LPC_INT_CLR 0x0c 22#define LPC_INT_POL 0x10 23#define LPC_COUNT 16 24 25struct pch_lpc { 26 void __iomem *base; 27 struct irq_domain *lpc_domain; 28 struct fwnode_handle *domain_handle; 29 raw_spinlock_t lpc_lock; 30 u32 saved_reg_ctl; 31 u32 saved_reg_ena; 32 u32 saved_reg_pol; 33}; 34 35static struct pch_lpc *pch_lpc_priv; 36 37static void ack_lpc_irq(struct irq_data *d) 38{ 39 unsigned long flags; 40 41 raw_spin_lock_irqsave(&pch_lpc_priv->lpc_lock, flags); 42 writel(0x1 << d->irq, pch_lpc_priv->base + LPC_INT_CLR); 43 raw_spin_unlock_irqrestore(&pch_lpc_priv->lpc_lock, flags); 44} 45static void mask_lpc_irq(struct irq_data *d) 46{ 47 unsigned long flags; 48 49 raw_spin_lock_irqsave(&pch_lpc_priv->lpc_lock, flags); 50 writel(readl(pch_lpc_priv->base + LPC_INT_ENA) & (~(0x1 << (d->irq))), 51 pch_lpc_priv->base + LPC_INT_ENA); 52 raw_spin_unlock_irqrestore(&pch_lpc_priv->lpc_lock, flags); 53} 54 55static void mask_ack_lpc_irq(struct irq_data *d) 56{ 57} 58 59static void unmask_lpc_irq(struct irq_data *d) 60{ 61 unsigned long flags; 62 raw_spin_lock_irqsave(&pch_lpc_priv->lpc_lock, flags); 63 writel(readl(pch_lpc_priv->base + LPC_INT_ENA) | (0x1 << (d->irq)), 64 pch_lpc_priv->base + LPC_INT_ENA); 65 raw_spin_unlock_irqrestore(&pch_lpc_priv->lpc_lock, flags); 66} 67 68static int lpc_set_type(struct irq_data *d, unsigned int type) 69{ 70 u32 val; 71 u32 mask = 0x1 << (d->hwirq); 72 73 if (!(type & IRQ_TYPE_LEVEL_MASK)) 74 return 0; 75 76 val = readl(pch_lpc_priv->base + LPC_INT_POL); 77 78 if (type == IRQ_TYPE_LEVEL_HIGH) 79 val |= mask; 80 else 81 val &= ~mask; 82 83 writel(val, pch_lpc_priv->base + LPC_INT_POL); 84 85 return 0; 86} 87 88static struct irq_chip pch_lpc_irq_chip = { 89 .name = "PCH LPC", 90 .irq_mask = mask_lpc_irq, 91 .irq_unmask = unmask_lpc_irq, 92 .irq_ack = ack_lpc_irq, 93 .irq_mask_ack = mask_ack_lpc_irq, 94 .irq_eoi = unmask_lpc_irq, 95 .irq_set_type = lpc_set_type, 96 .flags = IRQCHIP_SKIP_SET_WAKE, 97}; 98 99static void lpc_irq_dispatch(struct irq_desc *desc) 100{ 101 struct irq_chip *chip = irq_desc_get_chip(desc); 102 u32 pending; 103 104 chained_irq_enter(chip, desc); 105 106 pending = readl(pch_lpc_priv->base + LPC_INT_ENA); 107 pending &= readl(pch_lpc_priv->base + LPC_INT_STS); 108 if (!pending) 109 spurious_interrupt(); 110 111 while (pending) { 112 int bit = __ffs(pending); 113 generic_handle_irq(bit); 114 pending &= ~BIT(bit); 115 } 116 chained_irq_exit(chip, desc); 117} 118 119static int pch_lpc_map(struct irq_domain *d, unsigned int irq, 120 irq_hw_number_t hw) 121{ 122 irq_set_chip_and_handler(irq, &pch_lpc_irq_chip, handle_level_irq); 123 return 0; 124} 125 126static const struct irq_domain_ops pch_lpc_domain_ops = { 127 .map = pch_lpc_map, 128 .translate = irq_domain_translate_twocell, 129}; 130 131static void pch_lpc_reset(struct pch_lpc *priv) 132{ 133 /* Enable the LPC interrupt, bit31: en bit30: edge */ 134 writel(0x80000000, priv->base + LPC_INT_CTL); 135 writel(0, priv->base + LPC_INT_ENA); 136 /* Clear all 18-bit interrpt bit */ 137 writel(0x3ffff, priv->base + LPC_INT_CLR); 138} 139 140static int pch_lpc_disabled(struct pch_lpc *priv) 141{ 142 return (readl(priv->base + LPC_INT_ENA) == 0xffffffff) && 143 (readl(priv->base + LPC_INT_STS) == 0xffffffff); 144} 145 146static int pch_lpc_suspend(void) 147{ 148 pch_lpc_priv->saved_reg_ctl = readl(pch_lpc_priv->base + LPC_INT_CTL); 149 pch_lpc_priv->saved_reg_ena = readl(pch_lpc_priv->base + LPC_INT_ENA); 150 pch_lpc_priv->saved_reg_pol = readl(pch_lpc_priv->base + LPC_INT_POL); 151 return 0; 152} 153 154static void pch_lpc_resume(void) 155{ 156 writel(pch_lpc_priv->saved_reg_ctl, pch_lpc_priv->base + LPC_INT_CTL); 157 writel(pch_lpc_priv->saved_reg_ena, pch_lpc_priv->base + LPC_INT_ENA); 158 writel(pch_lpc_priv->saved_reg_pol, pch_lpc_priv->base + LPC_INT_POL); 159} 160 161static struct syscore_ops pch_lpc_syscore_ops = { 162 .suspend = pch_lpc_suspend, 163 .resume = pch_lpc_resume, 164}; 165 166struct irq_domain *pch_lpc_acpi_init(struct irq_domain *parent, 167 struct acpi_madt_lpc_pic *acpi_pchlpc) 168{ 169 int parent_irq; 170 struct pch_lpc *priv; 171 struct irq_fwspec fwspec; 172 173 if (!acpi_pchlpc) 174 return NULL; 175 176 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 177 if (!priv) 178 return NULL; 179 180 raw_spin_lock_init(&priv->lpc_lock); 181 182 priv->base = ioremap(acpi_pchlpc->address, acpi_pchlpc->size); 183 if (!priv->base) { 184 goto free_priv; 185 } 186 187 if (pch_lpc_disabled(priv)) { 188 pr_err("Failed to get LPC status\n"); 189 goto iounmap_base; 190 } 191 192 priv->domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchlpc); 193 if (!priv->domain_handle) { 194 pr_err("Unable to allocate domain handle\n"); 195 goto iounmap_base; 196 } 197 priv->lpc_domain = irq_domain_add_legacy(NULL, LPC_COUNT, 0, 0, 198 &pch_lpc_domain_ops, priv); 199 if (!priv->lpc_domain) { 200 pr_err("Failed to create IRQ domain\n"); 201 goto iounmap_base; 202 } 203 pch_lpc_reset(priv); 204 205 fwspec.fwnode = parent->fwnode; 206 fwspec.param[0] = acpi_pchlpc->cascade; 207 fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; 208 fwspec.param_count = 2; 209 parent_irq = irq_create_fwspec_mapping(&fwspec); 210 irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv); 211 pch_lpc_priv = priv; 212 213 register_syscore_ops(&pch_lpc_syscore_ops); 214 215 return irq_find_matching_fwnode(priv->domain_handle, DOMAIN_BUS_ANY); 216 217iounmap_base: 218 iounmap(priv->base); 219free_priv: 220 kfree(priv); 221 222 return NULL; 223} 224