18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) STMicroelectronics 2018 - All Rights Reserved 48c2ecf20Sopenharmony_ci * Authors: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics. 58c2ecf20Sopenharmony_ci * Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/pm_wakeirq.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define IPCC_XCR 0x000 188c2ecf20Sopenharmony_ci#define XCR_RXOIE BIT(0) 198c2ecf20Sopenharmony_ci#define XCR_TXOIE BIT(16) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define IPCC_XMR 0x004 228c2ecf20Sopenharmony_ci#define IPCC_XSCR 0x008 238c2ecf20Sopenharmony_ci#define IPCC_XTOYSR 0x00c 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define IPCC_PROC_OFFST 0x010 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define IPCC_HWCFGR 0x3f0 288c2ecf20Sopenharmony_ci#define IPCFGR_CHAN_MASK GENMASK(7, 0) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define IPCC_VER 0x3f4 318c2ecf20Sopenharmony_ci#define VER_MINREV_MASK GENMASK(3, 0) 328c2ecf20Sopenharmony_ci#define VER_MAJREV_MASK GENMASK(7, 4) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define RX_BIT_MASK GENMASK(15, 0) 358c2ecf20Sopenharmony_ci#define RX_BIT_CHAN(chan) BIT(chan) 368c2ecf20Sopenharmony_ci#define TX_BIT_SHIFT 16 378c2ecf20Sopenharmony_ci#define TX_BIT_MASK GENMASK(31, 16) 388c2ecf20Sopenharmony_ci#define TX_BIT_CHAN(chan) BIT(TX_BIT_SHIFT + (chan)) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define STM32_MAX_PROCS 2 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cienum { 438c2ecf20Sopenharmony_ci IPCC_IRQ_RX, 448c2ecf20Sopenharmony_ci IPCC_IRQ_TX, 458c2ecf20Sopenharmony_ci IPCC_IRQ_NUM, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct stm32_ipcc { 498c2ecf20Sopenharmony_ci struct mbox_controller controller; 508c2ecf20Sopenharmony_ci void __iomem *reg_base; 518c2ecf20Sopenharmony_ci void __iomem *reg_proc; 528c2ecf20Sopenharmony_ci struct clk *clk; 538c2ecf20Sopenharmony_ci spinlock_t lock; /* protect access to IPCC registers */ 548c2ecf20Sopenharmony_ci int irqs[IPCC_IRQ_NUM]; 558c2ecf20Sopenharmony_ci u32 proc_id; 568c2ecf20Sopenharmony_ci u32 n_chans; 578c2ecf20Sopenharmony_ci u32 xcr; 588c2ecf20Sopenharmony_ci u32 xmr; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline void stm32_ipcc_set_bits(spinlock_t *lock, void __iomem *reg, 628c2ecf20Sopenharmony_ci u32 mask) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci unsigned long flags; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci spin_lock_irqsave(lock, flags); 678c2ecf20Sopenharmony_ci writel_relaxed(readl_relaxed(reg) | mask, reg); 688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic inline void stm32_ipcc_clr_bits(spinlock_t *lock, void __iomem *reg, 728c2ecf20Sopenharmony_ci u32 mask) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci unsigned long flags; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci spin_lock_irqsave(lock, flags); 778c2ecf20Sopenharmony_ci writel_relaxed(readl_relaxed(reg) & ~mask, reg); 788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic irqreturn_t stm32_ipcc_rx_irq(int irq, void *data) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct stm32_ipcc *ipcc = data; 848c2ecf20Sopenharmony_ci struct device *dev = ipcc->controller.dev; 858c2ecf20Sopenharmony_ci u32 status, mr, tosr, chan; 868c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 878c2ecf20Sopenharmony_ci int proc_offset; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* read 'channel occupied' status from other proc */ 908c2ecf20Sopenharmony_ci proc_offset = ipcc->proc_id ? -IPCC_PROC_OFFST : IPCC_PROC_OFFST; 918c2ecf20Sopenharmony_ci tosr = readl_relaxed(ipcc->reg_proc + proc_offset + IPCC_XTOYSR); 928c2ecf20Sopenharmony_ci mr = readl_relaxed(ipcc->reg_proc + IPCC_XMR); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* search for unmasked 'channel occupied' */ 958c2ecf20Sopenharmony_ci status = tosr & FIELD_GET(RX_BIT_MASK, ~mr); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci for (chan = 0; chan < ipcc->n_chans; chan++) { 988c2ecf20Sopenharmony_ci if (!(status & (1 << chan))) 998c2ecf20Sopenharmony_ci continue; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: chan:%d rx\n", __func__, chan); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci mbox_chan_received_data(&ipcc->controller.chans[chan], NULL); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR, 1068c2ecf20Sopenharmony_ci RX_BIT_CHAN(chan)); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic irqreturn_t stm32_ipcc_tx_irq(int irq, void *data) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct stm32_ipcc *ipcc = data; 1178c2ecf20Sopenharmony_ci struct device *dev = ipcc->controller.dev; 1188c2ecf20Sopenharmony_ci u32 status, mr, tosr, chan; 1198c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci tosr = readl_relaxed(ipcc->reg_proc + IPCC_XTOYSR); 1228c2ecf20Sopenharmony_ci mr = readl_relaxed(ipcc->reg_proc + IPCC_XMR); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* search for unmasked 'channel free' */ 1258c2ecf20Sopenharmony_ci status = ~tosr & FIELD_GET(TX_BIT_MASK, ~mr); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci for (chan = 0; chan < ipcc->n_chans ; chan++) { 1288c2ecf20Sopenharmony_ci if (!(status & (1 << chan))) 1298c2ecf20Sopenharmony_ci continue; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: chan:%d tx\n", __func__, chan); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* mask 'tx channel free' interrupt */ 1348c2ecf20Sopenharmony_ci stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 1358c2ecf20Sopenharmony_ci TX_BIT_CHAN(chan)); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci mbox_chan_txdone(&ipcc->controller.chans[chan], 0); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int stm32_ipcc_send_data(struct mbox_chan *link, void *data) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci unsigned int chan = (unsigned int)link->con_priv; 1488c2ecf20Sopenharmony_ci struct stm32_ipcc *ipcc = container_of(link->mbox, struct stm32_ipcc, 1498c2ecf20Sopenharmony_ci controller); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci dev_dbg(ipcc->controller.dev, "%s: chan:%d\n", __func__, chan); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* set channel n occupied */ 1548c2ecf20Sopenharmony_ci stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR, 1558c2ecf20Sopenharmony_ci TX_BIT_CHAN(chan)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* unmask 'tx channel free' interrupt */ 1588c2ecf20Sopenharmony_ci stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 1598c2ecf20Sopenharmony_ci TX_BIT_CHAN(chan)); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int stm32_ipcc_startup(struct mbox_chan *link) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci unsigned int chan = (unsigned int)link->con_priv; 1678c2ecf20Sopenharmony_ci struct stm32_ipcc *ipcc = container_of(link->mbox, struct stm32_ipcc, 1688c2ecf20Sopenharmony_ci controller); 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ipcc->clk); 1728c2ecf20Sopenharmony_ci if (ret) { 1738c2ecf20Sopenharmony_ci dev_err(ipcc->controller.dev, "can not enable the clock\n"); 1748c2ecf20Sopenharmony_ci return ret; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* unmask 'rx channel occupied' interrupt */ 1788c2ecf20Sopenharmony_ci stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 1798c2ecf20Sopenharmony_ci RX_BIT_CHAN(chan)); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void stm32_ipcc_shutdown(struct mbox_chan *link) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci unsigned int chan = (unsigned int)link->con_priv; 1878c2ecf20Sopenharmony_ci struct stm32_ipcc *ipcc = container_of(link->mbox, struct stm32_ipcc, 1888c2ecf20Sopenharmony_ci controller); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* mask rx/tx interrupt */ 1918c2ecf20Sopenharmony_ci stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 1928c2ecf20Sopenharmony_ci RX_BIT_CHAN(chan) | TX_BIT_CHAN(chan)); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci clk_disable_unprepare(ipcc->clk); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops stm32_ipcc_ops = { 1988c2ecf20Sopenharmony_ci .send_data = stm32_ipcc_send_data, 1998c2ecf20Sopenharmony_ci .startup = stm32_ipcc_startup, 2008c2ecf20Sopenharmony_ci .shutdown = stm32_ipcc_shutdown, 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int stm32_ipcc_probe(struct platform_device *pdev) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2068c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 2078c2ecf20Sopenharmony_ci struct stm32_ipcc *ipcc; 2088c2ecf20Sopenharmony_ci struct resource *res; 2098c2ecf20Sopenharmony_ci unsigned int i; 2108c2ecf20Sopenharmony_ci int ret; 2118c2ecf20Sopenharmony_ci u32 ip_ver; 2128c2ecf20Sopenharmony_ci static const char * const irq_name[] = {"rx", "tx"}; 2138c2ecf20Sopenharmony_ci irq_handler_t irq_thread[] = {stm32_ipcc_rx_irq, stm32_ipcc_tx_irq}; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (!np) { 2168c2ecf20Sopenharmony_ci dev_err(dev, "No DT found\n"); 2178c2ecf20Sopenharmony_ci return -ENODEV; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ipcc = devm_kzalloc(dev, sizeof(*ipcc), GFP_KERNEL); 2218c2ecf20Sopenharmony_ci if (!ipcc) 2228c2ecf20Sopenharmony_ci return -ENOMEM; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci spin_lock_init(&ipcc->lock); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* proc_id */ 2278c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "st,proc-id", &ipcc->proc_id)) { 2288c2ecf20Sopenharmony_ci dev_err(dev, "Missing st,proc-id\n"); 2298c2ecf20Sopenharmony_ci return -ENODEV; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (ipcc->proc_id >= STM32_MAX_PROCS) { 2338c2ecf20Sopenharmony_ci dev_err(dev, "Invalid proc_id (%d)\n", ipcc->proc_id); 2348c2ecf20Sopenharmony_ci return -EINVAL; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* regs */ 2388c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2398c2ecf20Sopenharmony_ci ipcc->reg_base = devm_ioremap_resource(dev, res); 2408c2ecf20Sopenharmony_ci if (IS_ERR(ipcc->reg_base)) 2418c2ecf20Sopenharmony_ci return PTR_ERR(ipcc->reg_base); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ipcc->reg_proc = ipcc->reg_base + ipcc->proc_id * IPCC_PROC_OFFST; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* clock */ 2468c2ecf20Sopenharmony_ci ipcc->clk = devm_clk_get(dev, NULL); 2478c2ecf20Sopenharmony_ci if (IS_ERR(ipcc->clk)) 2488c2ecf20Sopenharmony_ci return PTR_ERR(ipcc->clk); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ipcc->clk); 2518c2ecf20Sopenharmony_ci if (ret) { 2528c2ecf20Sopenharmony_ci dev_err(dev, "can not enable the clock\n"); 2538c2ecf20Sopenharmony_ci return ret; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* irq */ 2578c2ecf20Sopenharmony_ci for (i = 0; i < IPCC_IRQ_NUM; i++) { 2588c2ecf20Sopenharmony_ci ipcc->irqs[i] = platform_get_irq_byname(pdev, irq_name[i]); 2598c2ecf20Sopenharmony_ci if (ipcc->irqs[i] < 0) { 2608c2ecf20Sopenharmony_ci if (ipcc->irqs[i] != -EPROBE_DEFER) 2618c2ecf20Sopenharmony_ci dev_err(dev, "no IRQ specified %s\n", 2628c2ecf20Sopenharmony_ci irq_name[i]); 2638c2ecf20Sopenharmony_ci ret = ipcc->irqs[i]; 2648c2ecf20Sopenharmony_ci goto err_clk; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, ipcc->irqs[i], NULL, 2688c2ecf20Sopenharmony_ci irq_thread[i], IRQF_ONESHOT, 2698c2ecf20Sopenharmony_ci dev_name(dev), ipcc); 2708c2ecf20Sopenharmony_ci if (ret) { 2718c2ecf20Sopenharmony_ci dev_err(dev, "failed to request irq %d (%d)\n", i, ret); 2728c2ecf20Sopenharmony_ci goto err_clk; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* mask and enable rx/tx irq */ 2778c2ecf20Sopenharmony_ci stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 2788c2ecf20Sopenharmony_ci RX_BIT_MASK | TX_BIT_MASK); 2798c2ecf20Sopenharmony_ci stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XCR, 2808c2ecf20Sopenharmony_ci XCR_RXOIE | XCR_TXOIE); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* wakeup */ 2838c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "wakeup-source")) { 2848c2ecf20Sopenharmony_ci device_set_wakeup_capable(dev, true); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci ret = dev_pm_set_wake_irq(dev, ipcc->irqs[IPCC_IRQ_RX]); 2878c2ecf20Sopenharmony_ci if (ret) { 2888c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set wake up irq\n"); 2898c2ecf20Sopenharmony_ci goto err_init_wkp; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* mailbox controller */ 2948c2ecf20Sopenharmony_ci ipcc->n_chans = readl_relaxed(ipcc->reg_base + IPCC_HWCFGR); 2958c2ecf20Sopenharmony_ci ipcc->n_chans &= IPCFGR_CHAN_MASK; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ipcc->controller.dev = dev; 2988c2ecf20Sopenharmony_ci ipcc->controller.txdone_irq = true; 2998c2ecf20Sopenharmony_ci ipcc->controller.ops = &stm32_ipcc_ops; 3008c2ecf20Sopenharmony_ci ipcc->controller.num_chans = ipcc->n_chans; 3018c2ecf20Sopenharmony_ci ipcc->controller.chans = devm_kcalloc(dev, ipcc->controller.num_chans, 3028c2ecf20Sopenharmony_ci sizeof(*ipcc->controller.chans), 3038c2ecf20Sopenharmony_ci GFP_KERNEL); 3048c2ecf20Sopenharmony_ci if (!ipcc->controller.chans) { 3058c2ecf20Sopenharmony_ci ret = -ENOMEM; 3068c2ecf20Sopenharmony_ci goto err_irq_wkp; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci for (i = 0; i < ipcc->controller.num_chans; i++) 3108c2ecf20Sopenharmony_ci ipcc->controller.chans[i].con_priv = (void *)i; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ret = devm_mbox_controller_register(dev, &ipcc->controller); 3138c2ecf20Sopenharmony_ci if (ret) 3148c2ecf20Sopenharmony_ci goto err_irq_wkp; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ipcc); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci ip_ver = readl_relaxed(ipcc->reg_base + IPCC_VER); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci dev_info(dev, "ipcc rev:%ld.%ld enabled, %d chans, proc %d\n", 3218c2ecf20Sopenharmony_ci FIELD_GET(VER_MAJREV_MASK, ip_ver), 3228c2ecf20Sopenharmony_ci FIELD_GET(VER_MINREV_MASK, ip_ver), 3238c2ecf20Sopenharmony_ci ipcc->controller.num_chans, ipcc->proc_id); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci clk_disable_unprepare(ipcc->clk); 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cierr_irq_wkp: 3298c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "wakeup-source")) 3308c2ecf20Sopenharmony_ci dev_pm_clear_wake_irq(dev); 3318c2ecf20Sopenharmony_cierr_init_wkp: 3328c2ecf20Sopenharmony_ci device_set_wakeup_capable(dev, false); 3338c2ecf20Sopenharmony_cierr_clk: 3348c2ecf20Sopenharmony_ci clk_disable_unprepare(ipcc->clk); 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int stm32_ipcc_remove(struct platform_device *pdev) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (of_property_read_bool(dev->of_node, "wakeup-source")) 3438c2ecf20Sopenharmony_ci dev_pm_clear_wake_irq(&pdev->dev); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci device_set_wakeup_capable(dev, false); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3518c2ecf20Sopenharmony_cistatic int stm32_ipcc_suspend(struct device *dev) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct stm32_ipcc *ipcc = dev_get_drvdata(dev); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci ipcc->xmr = readl_relaxed(ipcc->reg_proc + IPCC_XMR); 3568c2ecf20Sopenharmony_ci ipcc->xcr = readl_relaxed(ipcc->reg_proc + IPCC_XCR); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int stm32_ipcc_resume(struct device *dev) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct stm32_ipcc *ipcc = dev_get_drvdata(dev); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci writel_relaxed(ipcc->xmr, ipcc->reg_proc + IPCC_XMR); 3668c2ecf20Sopenharmony_ci writel_relaxed(ipcc->xcr, ipcc->reg_proc + IPCC_XCR); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci#endif 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(stm32_ipcc_pm_ops, 3738c2ecf20Sopenharmony_ci stm32_ipcc_suspend, stm32_ipcc_resume); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic const struct of_device_id stm32_ipcc_of_match[] = { 3768c2ecf20Sopenharmony_ci { .compatible = "st,stm32mp1-ipcc" }, 3778c2ecf20Sopenharmony_ci {}, 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_ipcc_of_match); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic struct platform_driver stm32_ipcc_driver = { 3828c2ecf20Sopenharmony_ci .driver = { 3838c2ecf20Sopenharmony_ci .name = "stm32-ipcc", 3848c2ecf20Sopenharmony_ci .pm = &stm32_ipcc_pm_ops, 3858c2ecf20Sopenharmony_ci .of_match_table = stm32_ipcc_of_match, 3868c2ecf20Sopenharmony_ci }, 3878c2ecf20Sopenharmony_ci .probe = stm32_ipcc_probe, 3888c2ecf20Sopenharmony_ci .remove = stm32_ipcc_remove, 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cimodule_platform_driver(stm32_ipcc_driver); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>"); 3948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>"); 3958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STM32 IPCC driver"); 3968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 397