From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f49.google.com (mail-wm1-f49.google.com [209.85.128.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 198B519CD0A for ; Sun, 15 Mar 2026 11:17:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.49 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773573441; cv=none; b=E0jBez4n0Vnqy0usHLuOiQ9mYucdTm0MenJamvG4zYIL0C8rx7elu+u04dLeVDbXQwUtzZ7Uwde5vkRMhN4w84e4OezgheFJwjBn1+59Of6SriTmm11UPjFqfkwqRooltw4LE9uU0lKJ0vvveUkRAOZGDSHeOGiIhl3SjHUHjoc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773573441; c=relaxed/simple; bh=GPZzO6NYH3KvM/Y3lePQ+SS19Gz+sc6izPkN5zev33w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Af9nVtphblwCLuA/+xkOPGRHOOScjb+EnbPES8dQa7C7vGhp2TGbEduaSZmrg7DgNYnCxZakoL/s2V+TRLjrKirFAgVRXdN4EAmo+2HlKEt0GB9NkKIoN03Z8iFmU6C9fQBzlfxBNvPeBaesiihxezhlJtjj4Wj8kxUpXm9Lkn4= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=eki6X8mO; arc=none smtp.client-ip=209.85.128.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="eki6X8mO" Received: by mail-wm1-f49.google.com with SMTP id 5b1f17b1804b1-4852e9ca034so32803425e9.2 for ; Sun, 15 Mar 2026 04:17:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773573438; x=1774178238; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=o5wzjtumCJewGyofJMTnovhFCcivfZUBvUWOY7ZjFVQ=; b=eki6X8mOAcVWfgx/7iCdO5MDFzNXzi/oSzc4G9VlAf6RB7JwgH/a8D3JRbPucu/pBy K1BsjhJYHYCZS6is2yfNagfCf4lCc2dENjYmypZ3LTdw5aoxmJ7k7cXasp0SzHCiNgMz VAY+0cxjBEQ1u+jbyJ0ily4DKWZzBK7TcUggKvb2XZbgf8P4FipSOdPEL6U+/pZgSI1R R56zDpCappwas3bJTZ9i14AXjcoXxQd3tuCXNWIk5N8Iy5fQOv7a5bDt0/rSbJQ5llII 98RQl+6VrEOMAJ5ZWzrKDgEko2ZyikqWWxS8JHr9zg2DhJq1io1DlH6IEU3ouHtBoqN7 4ikg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773573438; x=1774178238; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=o5wzjtumCJewGyofJMTnovhFCcivfZUBvUWOY7ZjFVQ=; b=HT76lvzL+ZzMOdnPjeE45rLGncCDbwDZJ9WjYcEGyGX22nt4pFirtfAnfAe17eXDfT +9zP647XCJ1t1hJNCTTfhalssmXFE6MS/ADnFeZonQHagf/y9f685VoPInTVLwMD9g7i LK+3ijUHXnHP4f1Opw/v6F+aMjquNOxLQCG+O6O18VkSTE7DcyiaqFuTNePoFPYJOPJY QpXEY78OS95VSdU2xa37RxzvHrqa/EouPyW4gNi/YbsLXImqYUxrE/U3Ctir2TTLT83w G878VGLRAj96nq7EM5bNqSu27Syl5v80JvmOWHry3qdqVXuNnE9yIwNl2zeF1rCeZ27z un9Q== X-Gm-Message-State: AOJu0Yzky4FGo/0uDQEgmUKJvagncp1Qzup9zPvBYKNoF9Zq6UQJ0Zqk wbDV5WtkANxffsr1gpX5tsW1tkCzwEHgrKgiXdeNHBpUVscHoSNB0d0I8SbZrqvt X-Gm-Gg: ATEYQzw2XXMIcj2l5+L2+ozmG8taYc37qHq4R8pH3XcUgC/sGCX8P6FtfCOL+fF4m57 Bru3RvGCBCW7Vo4I8zUrn33tz7Q+UAkB1AURey48gh36FGrNXJDhz7pGUjawKxlbFGatxp+vHvE VyOn0AUDLOKzthDK+HtcCZt0w+q7lZOTVF+FXbnJLNA8GcIHQoL0QVReerOZ7qfFgg5udP2S1y7 49E5e04xYW4aug342poQ9s54qiKSKVEaWY2CW9gyqqzFUMmNNizGl3G27btCl30d8F59vMGyMWA C54ULPNExnLwMitwRVIV9trX+HNxfCCoRDHylcfvXkk9P6iDzJG6YRwDVhRAurHgexbIIJrOxvm jn1aCXd8LxVQKSTmFWkTPy4ZgzSt2809pDb2fRWdfxavKZIrrLLrPzFoabxSQVyx6JgY8OQj/yZ lpWi8XJe7Pmp4S/bnHs5DpnKP4anL+UXRzEq66YnZ4Jh1HTeIePxN2j3+jatfNWjqLX0GF+84dR ipYlCqmtw== X-Received: by 2002:a05:600c:1395:b0:480:1c85:88bf with SMTP id 5b1f17b1804b1-4855670b72fmr153436645e9.27.1773573438149; Sun, 15 Mar 2026 04:17:18 -0700 (PDT) Received: from j0sh1x-MS-7A34.lan ([2001:9e8:46a7:9a00:6d34:d2d4:e925:2b8d]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48557777105sm110694825e9.4.2026.03.15.04.17.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 15 Mar 2026 04:17:17 -0700 (PDT) From: j0sh1x To: tglx@kernel.org Cc: linux-kernel@vger.kernel.org, subsystem@vger.kernel.org, dominikkobinski314@gmail.com, j0sh1x Subject: [PATCH 2/5] irqchip: Add Qualcomm MSM VIC driver Date: Sun, 15 Mar 2026 12:17:02 +0100 Message-ID: <20260315111705.118544-2-aljoshua.hell@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260315111705.118544-1-aljoshua.hell@gmail.com> References: <20260315111705.118544-1-aljoshua.hell@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Signed-off-by: j0sh1x --- drivers/irqchip/irq-msm-vic.c | 361 ++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 drivers/irqchip/irq-msm-vic.c diff --git a/drivers/irqchip/irq-msm-vic.c b/drivers/irqchip/irq-msm-vic.c new file mode 100644 index 000000000000..7e7f7be3ad6d --- /dev/null +++ b/drivers/irqchip/irq-msm-vic.c @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2024, Htc Leo Revival Project + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static u32 msm_irq_smsm_wake_enable[2]; +struct msm_irq_shadow { + u32 int_en[2]; + u32 int_type; + u32 int_polarity; + u32 int_select; +}; + +static struct msm_irq_shadow *msm_irq_shadow_reg; +static u32 *msm_irq_idle_disable; + +struct msm_irq_map { + u32 irq; + u8 smsm; +}; + +struct vic_device { + void __iomem *base; + struct irq_domain *domain; + u32 msm_nr_irqs; + u32 nr_vic_irqs; + u32 nr_gpio_irqs; + u32 vic_num_regs; + struct msm_irq_map *irq_map; + int irq_map_count; +}; + +static struct vic_device vic_data; + +static int msm_irq_to_smsm(u32 irq) +{ + int i; + + if (!vic_data.irq_map) + return -EINVAL; + + for (i = 0; i < vic_data.irq_map_count; i++) { + if (vic_data.irq_map[i].irq == irq) + return vic_data.irq_map[i].smsm; + } + + return -ENOENT; +} + +void set_irq_flags(unsigned int irq, unsigned int iflags); + +void set_irq_flags(unsigned int irq, unsigned int iflags) +{ + unsigned long clr = 0, set = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + + if (irq >= vic_data.msm_nr_irqs) { + pr_err("Trying to set irq flags for IRQ%d\n", irq); + return; + } + + if (iflags & IRQF_VALID) + clr |= IRQ_NOREQUEST; + if (iflags & IRQF_PROBE) + clr |= IRQ_NOPROBE; + if (!(iflags & IRQF_NOAUTOEN)) + clr |= IRQ_NOAUTOEN; + /* Order is clear bits in "clr" then set bits in "set" */ + irq_modify_status(irq, clr, set & ~clr); +} +EXPORT_SYMBOL_GPL(set_irq_flags); + +static inline void msm_irq_write_all_regs(void __iomem *base, unsigned int val) +{ + for (int i = 0; i < vic_data.vic_num_regs; i++) + writel(val, base + (i * 4)); +} + +static void msm_irq_ack(struct irq_data *d) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(vic_data.base + VIC_INT_CLEAR0, d->irq); + + writel(1 << (d->irq & 31), reg); +} + +static void msm_irq_mask(struct irq_data *d) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(vic_data.base + VIC_INT_ENCLEAR0, d->irq); + unsigned int index = VIC_INT_TO_REG_INDEX(d->irq); + u32 mask = 1UL << (d->irq & 31); + int smsm_irq = msm_irq_to_smsm(d->irq); + + msm_irq_shadow_reg[index].int_en[0] &= ~mask; + writel(mask, reg); + if (smsm_irq == 0) { + msm_irq_idle_disable[index] &= ~mask; + } else { + mask = 1UL << (smsm_irq - 1); + msm_irq_smsm_wake_enable[0] &= ~mask; + } +} + +static void msm_irq_unmask(struct irq_data *d) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(vic_data.base + VIC_INT_ENSET0, d->irq); + unsigned int index = VIC_INT_TO_REG_INDEX(d->irq); + u32 mask = 1UL << (d->irq & 31); + int smsm_irq = msm_irq_to_smsm(d->irq); + + msm_irq_shadow_reg[index].int_en[0] |= mask; + writel(mask, reg); + + if (smsm_irq == 0) { + msm_irq_idle_disable[index] |= mask; + } else { + mask = 1UL << (smsm_irq - 1); + msm_irq_smsm_wake_enable[0] |= mask; + } +} + +static int msm_irq_set_wake(struct irq_data *d, unsigned int on) +{ + unsigned int index = VIC_INT_TO_REG_INDEX(d->irq); + u32 mask = 1UL << (d->irq & 31); + int smsm_irq = msm_irq_to_smsm(d->irq); + + if (smsm_irq == 0) { + pr_err("%s: bad wakeup irq %d\n", __func__, d->irq); + return -EINVAL; + } + if (on) + msm_irq_shadow_reg[index].int_en[1] |= mask; + else + msm_irq_shadow_reg[index].int_en[1] &= ~mask; + + if (smsm_irq == SMSM_FAKE_IRQ) + return 0; + + mask = 1UL << (smsm_irq - 1); + if (on) + msm_irq_smsm_wake_enable[1] |= mask; + else + msm_irq_smsm_wake_enable[1] &= ~mask; + return 0; +} + +static int msm_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + void __iomem *treg = VIC_INT_TO_REG_ADDR(vic_data.base + VIC_INT_TYPE0, d->irq); + void __iomem *preg = VIC_INT_TO_REG_ADDR(vic_data.base + VIC_INT_POLARITY0, d->irq); + unsigned int index = VIC_INT_TO_REG_INDEX(d->irq); + int b = 1 << (d->irq & 31); + u32 polarity; + u32 type; + + polarity = msm_irq_shadow_reg[index].int_polarity; + if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) + polarity |= b; + if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) + polarity &= ~b; + writel(polarity, preg); + msm_irq_shadow_reg[index].int_polarity = polarity; + + type = msm_irq_shadow_reg[index].int_type; + if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + type |= b; + irq_set_handler_locked(d, handle_edge_irq); + } + if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) { + type &= ~b; + irq_set_handler_locked(d, handle_level_irq); + } + writel(type, treg); + msm_irq_shadow_reg[index].int_type = type; + return 0; +} + +static inline void msm_vic_handle_irq(void __iomem *base_addr, struct pt_regs + *regs) +{ + u32 irqnr; + + do { + /* VIC_IRQ_VEC_RD has irq# or old irq# if the irq has been handled + * VIC_IRQ_VEC_PEND_RD has irq# or -1 if none pending *but* if you + * just read VIC_IRQ_VEC_PEND_RD you never get the first irq for some reason + */ + irqnr = readl_relaxed(base_addr + VIC_IRQ_VEC_RD); + irqnr = readl_relaxed(base_addr + VIC_IRQ_VEC_PEND_RD); + if (irqnr == -1) + break; + handle_IRQ(irqnr, regs); + } while (1); +} + +/* enable imprecise aborts */ +static inline void local_cpsie_enable(void) +{ + asm volatile("cpsie a" : : : "memory"); +} + +static void __exception_irq_entry vic_handle_irq(struct pt_regs *regs) +{ + local_cpsie_enable();// local_abt_enable()? + msm_vic_handle_irq(vic_data.base, regs); +} + +static struct irq_chip msm_irq_chip = { + .name = "msm", + .irq_disable = msm_irq_mask, + .irq_ack = msm_irq_ack, + .irq_mask = msm_irq_mask, + .irq_unmask = msm_irq_unmask, + .irq_set_wake = msm_irq_set_wake, + .irq_set_type = msm_irq_set_type, +}; + +static int msm_vic_parse_irq_mapping(struct device_node *np, struct msm_irq_map **map, int *count) +{ + const __be32 *prop; + int len, i; + + prop = of_get_property(np, "irq-mapping", &len); + if (!prop) { + pr_err("%s: No irq-mapping property in device tree\n", __func__); + return -ENODEV; + } + + /* Each entry is 2 u32 values: irq + smsm */ + *count = len / (2 * sizeof(u32)); + + *map = kzalloc((*count) * sizeof(**map), GFP_KERNEL); + if (!*map) { + pr_err("%s: Failed to allocate memory for irq_map\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < *count; i++) { + (*map)[i].irq = be32_to_cpu(prop[i * 2]); + (*map)[i].smsm = be32_to_cpu(prop[i * 2 + 1]); + } + + return 0; +} + +static int __init msm_init_irq(struct device_node *intc, struct device_node *parent) +{ + int ret; + + vic_data.base = of_iomap(intc, 0); + if (WARN_ON(!vic_data.base)) + return -EIO; + ret = of_property_read_u32(intc, "num-irqs", &vic_data.nr_vic_irqs); + if (ret) { + pr_err("%s: failed to read num-irqs ret=%d\n", __func__, ret); + return ret; + } + + ret = of_property_read_u32(intc, "num-gpio-irqs", &vic_data.nr_gpio_irqs); + if (ret) { + pr_err("%s: failed to read num-gpio-irqs ret=%d\n", __func__, ret); + return ret; + } + + ret = msm_vic_parse_irq_mapping(intc, &vic_data.irq_map, &vic_data.irq_map_count); + if (ret) { + pr_err("Failed to parse irq-mapping\n"); + return ret; + } + + ret = of_property_read_u32(intc, "vic-num-regs", &vic_data.vic_num_regs); + if (ret) { + pr_err("Failed to parse vic-num-regs\n"); + return ret; + } + + msm_irq_shadow_reg = kcalloc(vic_data.vic_num_regs, + sizeof(*msm_irq_shadow_reg), + GFP_KERNEL); + + msm_irq_idle_disable = kcalloc(vic_data.vic_num_regs, + sizeof(*msm_irq_idle_disable), + GFP_KERNEL); + + if (!msm_irq_shadow_reg || !msm_irq_idle_disable) + return -ENOMEM; + + vic_data.msm_nr_irqs = vic_data.nr_vic_irqs * 2 + vic_data.nr_gpio_irqs; + + ret = irq_alloc_descs(-1, 0, vic_data.nr_vic_irqs, 0); + if (ret < 0) + pr_warn("Couldn't allocate IRQ numbers\n"); + /* select level interrupts */ + msm_irq_write_all_regs(vic_data.base + VIC_INT_TYPE0, 0); + + /* select highlevel interrupts */ + msm_irq_write_all_regs(vic_data.base + VIC_INT_POLARITY0, 0); + + /* select IRQ for all INTs */ + msm_irq_write_all_regs(vic_data.base + VIC_INT_SELECT0, 0); + + /* disable all INTs */ + msm_irq_write_all_regs(vic_data.base + VIC_INT_EN0, 0); + + /* don't use vic */ + writel(0, vic_data.base + VIC_CONFIG); + + /* enable interrupt controller */ + writel(3, vic_data.base + VIC_INT_MASTEREN); + + for (int n = 0; n < vic_data.msm_nr_irqs; n++) { + irq_set_chip_and_handler(n, &msm_irq_chip, handle_level_irq); + set_irq_flags(n, IRQF_VALID); + } + + /* Ready to receive interrupts */ + set_handle_irq(vic_handle_irq); + + vic_data.domain = irq_domain_create_legacy + (of_fwnode_handle(intc), + vic_data.nr_vic_irqs, + 0, 0, + &irq_domain_simple_ops, + &vic_data); + if (!vic_data.domain) + pr_err("%s: failed to register irq domain\n", __func__); + return 0; +} + +IRQCHIP_DECLARE(qcom_msm_vic, "qcom,msm-vic", msm_init_irq); \ No newline at end of file -- 2.51.0