18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright 2017 NXP 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* INTMUX Block Diagram 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * ________________ 78c2ecf20Sopenharmony_ci * interrupt source # 0 +---->| | 88c2ecf20Sopenharmony_ci * | | | 98c2ecf20Sopenharmony_ci * interrupt source # 1 +++-->| | 108c2ecf20Sopenharmony_ci * ... | | | channel # 0 |--------->interrupt out # 0 118c2ecf20Sopenharmony_ci * ... | | | | 128c2ecf20Sopenharmony_ci * ... | | | | 138c2ecf20Sopenharmony_ci * interrupt source # X-1 +++-->|________________| 148c2ecf20Sopenharmony_ci * | | | 158c2ecf20Sopenharmony_ci * | | | 168c2ecf20Sopenharmony_ci * | | | ________________ 178c2ecf20Sopenharmony_ci * +---->| | 188c2ecf20Sopenharmony_ci * | | | | | 198c2ecf20Sopenharmony_ci * | +-->| | 208c2ecf20Sopenharmony_ci * | | | | channel # 1 |--------->interrupt out # 1 218c2ecf20Sopenharmony_ci * | | +>| | 228c2ecf20Sopenharmony_ci * | | | | | 238c2ecf20Sopenharmony_ci * | | | |________________| 248c2ecf20Sopenharmony_ci * | | | 258c2ecf20Sopenharmony_ci * | | | 268c2ecf20Sopenharmony_ci * | | | ... 278c2ecf20Sopenharmony_ci * | | | ... 288c2ecf20Sopenharmony_ci * | | | 298c2ecf20Sopenharmony_ci * | | | ________________ 308c2ecf20Sopenharmony_ci * +---->| | 318c2ecf20Sopenharmony_ci * | | | | 328c2ecf20Sopenharmony_ci * +-->| | 338c2ecf20Sopenharmony_ci * | | channel # N |--------->interrupt out # N 348c2ecf20Sopenharmony_ci * +>| | 358c2ecf20Sopenharmony_ci * | | 368c2ecf20Sopenharmony_ci * |________________| 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * N: Interrupt Channel Instance Number (N=7) 408c2ecf20Sopenharmony_ci * X: Interrupt Source Number for each channel (X=32) 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * The INTMUX interrupt multiplexer has 8 channels, each channel receives 32 438c2ecf20Sopenharmony_ci * interrupt sources and generates 1 interrupt output. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#include <linux/clk.h> 488c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 498c2ecf20Sopenharmony_ci#include <linux/irq.h> 508c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 518c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 528c2ecf20Sopenharmony_ci#include <linux/kernel.h> 538c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 548c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 558c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 568c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define CHANIER(n) (0x10 + (0x40 * n)) 598c2ecf20Sopenharmony_ci#define CHANIPR(n) (0x20 + (0x40 * n)) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define CHAN_MAX_NUM 0x8 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct intmux_irqchip_data { 648c2ecf20Sopenharmony_ci struct irq_chip chip; 658c2ecf20Sopenharmony_ci u32 saved_reg; 668c2ecf20Sopenharmony_ci int chanidx; 678c2ecf20Sopenharmony_ci int irq; 688c2ecf20Sopenharmony_ci struct irq_domain *domain; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct intmux_data { 728c2ecf20Sopenharmony_ci raw_spinlock_t lock; 738c2ecf20Sopenharmony_ci void __iomem *regs; 748c2ecf20Sopenharmony_ci struct clk *ipg_clk; 758c2ecf20Sopenharmony_ci int channum; 768c2ecf20Sopenharmony_ci struct intmux_irqchip_data irqchip_data[]; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void imx_intmux_irq_mask(struct irq_data *d) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct intmux_irqchip_data *irqchip_data = d->chip_data; 828c2ecf20Sopenharmony_ci int idx = irqchip_data->chanidx; 838c2ecf20Sopenharmony_ci struct intmux_data *data = container_of(irqchip_data, struct intmux_data, 848c2ecf20Sopenharmony_ci irqchip_data[idx]); 858c2ecf20Sopenharmony_ci unsigned long flags; 868c2ecf20Sopenharmony_ci void __iomem *reg; 878c2ecf20Sopenharmony_ci u32 val; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&data->lock, flags); 908c2ecf20Sopenharmony_ci reg = data->regs + CHANIER(idx); 918c2ecf20Sopenharmony_ci val = readl_relaxed(reg); 928c2ecf20Sopenharmony_ci /* disable the interrupt source of this channel */ 938c2ecf20Sopenharmony_ci val &= ~BIT(d->hwirq); 948c2ecf20Sopenharmony_ci writel_relaxed(val, reg); 958c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&data->lock, flags); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void imx_intmux_irq_unmask(struct irq_data *d) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct intmux_irqchip_data *irqchip_data = d->chip_data; 1018c2ecf20Sopenharmony_ci int idx = irqchip_data->chanidx; 1028c2ecf20Sopenharmony_ci struct intmux_data *data = container_of(irqchip_data, struct intmux_data, 1038c2ecf20Sopenharmony_ci irqchip_data[idx]); 1048c2ecf20Sopenharmony_ci unsigned long flags; 1058c2ecf20Sopenharmony_ci void __iomem *reg; 1068c2ecf20Sopenharmony_ci u32 val; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&data->lock, flags); 1098c2ecf20Sopenharmony_ci reg = data->regs + CHANIER(idx); 1108c2ecf20Sopenharmony_ci val = readl_relaxed(reg); 1118c2ecf20Sopenharmony_ci /* enable the interrupt source of this channel */ 1128c2ecf20Sopenharmony_ci val |= BIT(d->hwirq); 1138c2ecf20Sopenharmony_ci writel_relaxed(val, reg); 1148c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&data->lock, flags); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic struct irq_chip imx_intmux_irq_chip = { 1188c2ecf20Sopenharmony_ci .name = "intmux", 1198c2ecf20Sopenharmony_ci .irq_mask = imx_intmux_irq_mask, 1208c2ecf20Sopenharmony_ci .irq_unmask = imx_intmux_irq_unmask, 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int imx_intmux_irq_map(struct irq_domain *h, unsigned int irq, 1248c2ecf20Sopenharmony_ci irq_hw_number_t hwirq) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct intmux_irqchip_data *data = h->host_data; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci irq_set_chip_data(irq, data); 1298c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, &data->chip, handle_level_irq); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int imx_intmux_irq_xlate(struct irq_domain *d, struct device_node *node, 1358c2ecf20Sopenharmony_ci const u32 *intspec, unsigned int intsize, 1368c2ecf20Sopenharmony_ci unsigned long *out_hwirq, unsigned int *out_type) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct intmux_irqchip_data *irqchip_data = d->host_data; 1398c2ecf20Sopenharmony_ci int idx = irqchip_data->chanidx; 1408c2ecf20Sopenharmony_ci struct intmux_data *data = container_of(irqchip_data, struct intmux_data, 1418c2ecf20Sopenharmony_ci irqchip_data[idx]); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * two cells needed in interrupt specifier: 1458c2ecf20Sopenharmony_ci * the 1st cell: hw interrupt number 1468c2ecf20Sopenharmony_ci * the 2nd cell: channel index 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci if (WARN_ON(intsize != 2)) 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (WARN_ON(intspec[1] >= data->channum)) 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci *out_hwirq = intspec[0]; 1558c2ecf20Sopenharmony_ci *out_type = IRQ_TYPE_LEVEL_HIGH; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int imx_intmux_irq_select(struct irq_domain *d, struct irq_fwspec *fwspec, 1618c2ecf20Sopenharmony_ci enum irq_domain_bus_token bus_token) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct intmux_irqchip_data *irqchip_data = d->host_data; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Not for us */ 1668c2ecf20Sopenharmony_ci if (fwspec->fwnode != d->fwnode) 1678c2ecf20Sopenharmony_ci return false; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return irqchip_data->chanidx == fwspec->param[1]; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const struct irq_domain_ops imx_intmux_domain_ops = { 1738c2ecf20Sopenharmony_ci .map = imx_intmux_irq_map, 1748c2ecf20Sopenharmony_ci .xlate = imx_intmux_irq_xlate, 1758c2ecf20Sopenharmony_ci .select = imx_intmux_irq_select, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void imx_intmux_irq_handler(struct irq_desc *desc) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct intmux_irqchip_data *irqchip_data = irq_desc_get_handler_data(desc); 1818c2ecf20Sopenharmony_ci int idx = irqchip_data->chanidx; 1828c2ecf20Sopenharmony_ci struct intmux_data *data = container_of(irqchip_data, struct intmux_data, 1838c2ecf20Sopenharmony_ci irqchip_data[idx]); 1848c2ecf20Sopenharmony_ci unsigned long irqstat; 1858c2ecf20Sopenharmony_ci int pos, virq; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci chained_irq_enter(irq_desc_get_chip(desc), desc); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* read the interrupt source pending status of this channel */ 1908c2ecf20Sopenharmony_ci irqstat = readl_relaxed(data->regs + CHANIPR(idx)); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci for_each_set_bit(pos, &irqstat, 32) { 1938c2ecf20Sopenharmony_ci virq = irq_find_mapping(irqchip_data->domain, pos); 1948c2ecf20Sopenharmony_ci if (virq) 1958c2ecf20Sopenharmony_ci generic_handle_irq(virq); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci chained_irq_exit(irq_desc_get_chip(desc), desc); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int imx_intmux_probe(struct platform_device *pdev) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 2048c2ecf20Sopenharmony_ci struct irq_domain *domain; 2058c2ecf20Sopenharmony_ci struct intmux_data *data; 2068c2ecf20Sopenharmony_ci int channum; 2078c2ecf20Sopenharmony_ci int i, ret; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci channum = platform_irq_count(pdev); 2108c2ecf20Sopenharmony_ci if (channum == -EPROBE_DEFER) { 2118c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 2128c2ecf20Sopenharmony_ci } else if (channum > CHAN_MAX_NUM) { 2138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "supports up to %d multiplex channels\n", 2148c2ecf20Sopenharmony_ci CHAN_MAX_NUM); 2158c2ecf20Sopenharmony_ci return -EINVAL; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, struct_size(data, irqchip_data, channum), GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!data) 2208c2ecf20Sopenharmony_ci return -ENOMEM; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci data->regs = devm_platform_ioremap_resource(pdev, 0); 2238c2ecf20Sopenharmony_ci if (IS_ERR(data->regs)) { 2248c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize reg\n"); 2258c2ecf20Sopenharmony_ci return PTR_ERR(data->regs); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci data->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); 2298c2ecf20Sopenharmony_ci if (IS_ERR(data->ipg_clk)) 2308c2ecf20Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(data->ipg_clk), 2318c2ecf20Sopenharmony_ci "failed to get ipg clk\n"); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci data->channum = channum; 2348c2ecf20Sopenharmony_ci raw_spin_lock_init(&data->lock); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci pm_runtime_get_noresume(&pdev->dev); 2378c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 2388c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = clk_prepare_enable(data->ipg_clk); 2418c2ecf20Sopenharmony_ci if (ret) { 2428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable ipg clk: %d\n", ret); 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci for (i = 0; i < channum; i++) { 2478c2ecf20Sopenharmony_ci data->irqchip_data[i].chip = imx_intmux_irq_chip; 2488c2ecf20Sopenharmony_ci data->irqchip_data[i].chip.parent_device = &pdev->dev; 2498c2ecf20Sopenharmony_ci data->irqchip_data[i].chanidx = i; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci data->irqchip_data[i].irq = irq_of_parse_and_map(np, i); 2528c2ecf20Sopenharmony_ci if (data->irqchip_data[i].irq <= 0) { 2538c2ecf20Sopenharmony_ci ret = -EINVAL; 2548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get irq\n"); 2558c2ecf20Sopenharmony_ci goto out; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci domain = irq_domain_add_linear(np, 32, &imx_intmux_domain_ops, 2598c2ecf20Sopenharmony_ci &data->irqchip_data[i]); 2608c2ecf20Sopenharmony_ci if (!domain) { 2618c2ecf20Sopenharmony_ci ret = -ENOMEM; 2628c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to create IRQ domain\n"); 2638c2ecf20Sopenharmony_ci goto out; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci data->irqchip_data[i].domain = domain; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* disable all interrupt sources of this channel firstly */ 2688c2ecf20Sopenharmony_ci writel_relaxed(0, data->regs + CHANIER(i)); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(data->irqchip_data[i].irq, 2718c2ecf20Sopenharmony_ci imx_intmux_irq_handler, 2728c2ecf20Sopenharmony_ci &data->irqchip_data[i]); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* 2788c2ecf20Sopenharmony_ci * Let pm_runtime_put() disable clock. 2798c2ecf20Sopenharmony_ci * If CONFIG_PM is not enabled, the clock will stay powered. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci pm_runtime_put(&pdev->dev); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ciout: 2858c2ecf20Sopenharmony_ci clk_disable_unprepare(data->ipg_clk); 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int imx_intmux_remove(struct platform_device *pdev) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct intmux_data *data = platform_get_drvdata(pdev); 2928c2ecf20Sopenharmony_ci int i; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci for (i = 0; i < data->channum; i++) { 2958c2ecf20Sopenharmony_ci /* disable all interrupt sources of this channel */ 2968c2ecf20Sopenharmony_ci writel_relaxed(0, data->regs + CHANIER(i)); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(data->irqchip_data[i].irq, 2998c2ecf20Sopenharmony_ci NULL, NULL); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci irq_domain_remove(data->irqchip_data[i].domain); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3108c2ecf20Sopenharmony_cistatic int imx_intmux_runtime_suspend(struct device *dev) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct intmux_data *data = dev_get_drvdata(dev); 3138c2ecf20Sopenharmony_ci struct intmux_irqchip_data *irqchip_data; 3148c2ecf20Sopenharmony_ci int i; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci for (i = 0; i < data->channum; i++) { 3178c2ecf20Sopenharmony_ci irqchip_data = &data->irqchip_data[i]; 3188c2ecf20Sopenharmony_ci irqchip_data->saved_reg = readl_relaxed(data->regs + CHANIER(i)); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci clk_disable_unprepare(data->ipg_clk); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic int imx_intmux_runtime_resume(struct device *dev) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct intmux_data *data = dev_get_drvdata(dev); 3298c2ecf20Sopenharmony_ci struct intmux_irqchip_data *irqchip_data; 3308c2ecf20Sopenharmony_ci int ret, i; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ret = clk_prepare_enable(data->ipg_clk); 3338c2ecf20Sopenharmony_ci if (ret) { 3348c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable ipg clk: %d\n", ret); 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci for (i = 0; i < data->channum; i++) { 3398c2ecf20Sopenharmony_ci irqchip_data = &data->irqchip_data[i]; 3408c2ecf20Sopenharmony_ci writel_relaxed(irqchip_data->saved_reg, data->regs + CHANIER(i)); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci#endif 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic const struct dev_pm_ops imx_intmux_pm_ops = { 3488c2ecf20Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 3498c2ecf20Sopenharmony_ci pm_runtime_force_resume) 3508c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(imx_intmux_runtime_suspend, 3518c2ecf20Sopenharmony_ci imx_intmux_runtime_resume, NULL) 3528c2ecf20Sopenharmony_ci}; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic const struct of_device_id imx_intmux_id[] = { 3558c2ecf20Sopenharmony_ci { .compatible = "fsl,imx-intmux", }, 3568c2ecf20Sopenharmony_ci { /* sentinel */ }, 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic struct platform_driver imx_intmux_driver = { 3608c2ecf20Sopenharmony_ci .driver = { 3618c2ecf20Sopenharmony_ci .name = "imx-intmux", 3628c2ecf20Sopenharmony_ci .of_match_table = imx_intmux_id, 3638c2ecf20Sopenharmony_ci .pm = &imx_intmux_pm_ops, 3648c2ecf20Sopenharmony_ci }, 3658c2ecf20Sopenharmony_ci .probe = imx_intmux_probe, 3668c2ecf20Sopenharmony_ci .remove = imx_intmux_remove, 3678c2ecf20Sopenharmony_ci}; 3688c2ecf20Sopenharmony_cibuiltin_platform_driver(imx_intmux_driver); 369