1223534Shselasky// SPDX-License-Identifier: GPL-2.0 2223534Shselasky/* 3223534Shselasky * Copyright (C) STMicroelectronics 2018 - All Rights Reserved 4223534Shselasky * Authors: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics. 5223534Shselasky * Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics. 6223534Shselasky */ 7223534Shselasky 8223534Shselasky#include <linux/bitfield.h> 9223534Shselasky#include <linux/clk.h> 10223534Shselasky#include <linux/interrupt.h> 11223534Shselasky#include <linux/io.h> 12223534Shselasky#include <linux/mailbox_controller.h> 13223534Shselasky#include <linux/module.h> 14223534Shselasky#include <linux/of.h> 15223534Shselasky#include <linux/platform_device.h> 16223534Shselasky#include <linux/pm_wakeirq.h> 17223534Shselasky 18223534Shselasky#define IPCC_XCR 0x000 19223534Shselasky#define XCR_RXOIE BIT(0) 20223534Shselasky#define XCR_TXOIE BIT(16) 21223534Shselasky 22223534Shselasky#define IPCC_XMR 0x004 23223534Shselasky#define IPCC_XSCR 0x008 24223534Shselasky#define IPCC_XTOYSR 0x00c 25223534Shselasky 26223534Shselasky#define IPCC_PROC_OFFST 0x010 27223534Shselasky 28223534Shselasky#define IPCC_HWCFGR 0x3f0 29223534Shselasky#define IPCFGR_CHAN_MASK GENMASK(7, 0) 30223534Shselasky 31223534Shselasky#define IPCC_VER 0x3f4 32223534Shselasky#define VER_MINREV_MASK GENMASK(3, 0) 33223534Shselasky#define VER_MAJREV_MASK GENMASK(7, 4) 34223534Shselasky 35223534Shselasky#define RX_BIT_MASK GENMASK(15, 0) 36223534Shselasky#define RX_BIT_CHAN(chan) BIT(chan) 37223534Shselasky#define TX_BIT_SHIFT 16 38223534Shselasky#define TX_BIT_MASK GENMASK(31, 16) 39223534Shselasky#define TX_BIT_CHAN(chan) BIT(TX_BIT_SHIFT + (chan)) 40223534Shselasky 41223534Shselasky#define STM32_MAX_PROCS 2 42223534Shselasky 43223534Shselaskyenum { 44223534Shselasky IPCC_IRQ_RX, 45223534Shselasky IPCC_IRQ_TX, 46223534Shselasky IPCC_IRQ_NUM, 47223534Shselasky}; 48223534Shselasky 49223534Shselaskystruct stm32_ipcc { 50223534Shselasky struct mbox_controller controller; 51223534Shselasky void __iomem *reg_base; 52223534Shselasky void __iomem *reg_proc; 53223534Shselasky struct clk *clk; 54223534Shselasky spinlock_t lock; /* protect access to IPCC registers */ 55223534Shselasky int irqs[IPCC_IRQ_NUM]; 56223534Shselasky u32 proc_id; 57223534Shselasky u32 n_chans; 58223534Shselasky u32 xcr; 59223534Shselasky u32 xmr; 60223534Shselasky}; 61223534Shselasky 62223534Shselaskystatic inline void stm32_ipcc_set_bits(spinlock_t *lock, void __iomem *reg, 63223534Shselasky u32 mask) 64223534Shselasky{ 65223534Shselasky unsigned long flags; 66223534Shselasky 67223534Shselasky spin_lock_irqsave(lock, flags); 68223534Shselasky writel_relaxed(readl_relaxed(reg) | mask, reg); 69223534Shselasky spin_unlock_irqrestore(lock, flags); 70223534Shselasky} 71223534Shselasky 72223534Shselaskystatic inline void stm32_ipcc_clr_bits(spinlock_t *lock, void __iomem *reg, 73223534Shselasky u32 mask) 74223534Shselasky{ 75223534Shselasky unsigned long flags; 76223534Shselasky 77223534Shselasky spin_lock_irqsave(lock, flags); 78223534Shselasky writel_relaxed(readl_relaxed(reg) & ~mask, reg); 79223534Shselasky spin_unlock_irqrestore(lock, flags); 80223534Shselasky} 81223534Shselasky 82223534Shselaskystatic irqreturn_t stm32_ipcc_rx_irq(int irq, void *data) 83223534Shselasky{ 84223534Shselasky struct stm32_ipcc *ipcc = data; 85223534Shselasky struct device *dev = ipcc->controller.dev; 86223534Shselasky u32 status, mr, tosr, chan; 87223534Shselasky irqreturn_t ret = IRQ_NONE; 88223534Shselasky int proc_offset; 89223534Shselasky 90223534Shselasky /* read 'channel occupied' status from other proc */ 91223534Shselasky proc_offset = ipcc->proc_id ? -IPCC_PROC_OFFST : IPCC_PROC_OFFST; 92223534Shselasky tosr = readl_relaxed(ipcc->reg_proc + proc_offset + IPCC_XTOYSR); 93223534Shselasky mr = readl_relaxed(ipcc->reg_proc + IPCC_XMR); 94223534Shselasky 95223534Shselasky /* search for unmasked 'channel occupied' */ 96223534Shselasky status = tosr & FIELD_GET(RX_BIT_MASK, ~mr); 97223534Shselasky 98223534Shselasky for (chan = 0; chan < ipcc->n_chans; chan++) { 99223534Shselasky if (!(status & (1 << chan))) 100223534Shselasky continue; 101223534Shselasky 102223534Shselasky dev_dbg(dev, "%s: chan:%d rx\n", __func__, chan); 103223534Shselasky 104223534Shselasky mbox_chan_received_data(&ipcc->controller.chans[chan], NULL); 105223534Shselasky 106223534Shselasky stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR, 107223534Shselasky RX_BIT_CHAN(chan)); 108223534Shselasky 109223534Shselasky ret = IRQ_HANDLED; 110223534Shselasky } 111223534Shselasky 112223534Shselasky return ret; 113223534Shselasky} 114223534Shselasky 115223534Shselaskystatic irqreturn_t stm32_ipcc_tx_irq(int irq, void *data) 116223534Shselasky{ 117223534Shselasky struct stm32_ipcc *ipcc = data; 118223534Shselasky struct device *dev = ipcc->controller.dev; 119223534Shselasky u32 status, mr, tosr, chan; 120223534Shselasky irqreturn_t ret = IRQ_NONE; 121223534Shselasky 122223534Shselasky tosr = readl_relaxed(ipcc->reg_proc + IPCC_XTOYSR); 123223534Shselasky mr = readl_relaxed(ipcc->reg_proc + IPCC_XMR); 124223534Shselasky 125223534Shselasky /* search for unmasked 'channel free' */ 126223534Shselasky status = ~tosr & FIELD_GET(TX_BIT_MASK, ~mr); 127223534Shselasky 128223534Shselasky for (chan = 0; chan < ipcc->n_chans ; chan++) { 129223534Shselasky if (!(status & (1 << chan))) 130223534Shselasky continue; 131223534Shselasky 132223534Shselasky dev_dbg(dev, "%s: chan:%d tx\n", __func__, chan); 133223534Shselasky 134223534Shselasky /* mask 'tx channel free' interrupt */ 135223534Shselasky stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 136223534Shselasky TX_BIT_CHAN(chan)); 137223534Shselasky 138223534Shselasky mbox_chan_txdone(&ipcc->controller.chans[chan], 0); 139223534Shselasky 140223534Shselasky ret = IRQ_HANDLED; 141223534Shselasky } 142223534Shselasky 143223534Shselasky return ret; 144223534Shselasky} 145223534Shselasky 146223534Shselaskystatic int stm32_ipcc_send_data(struct mbox_chan *link, void *data) 147223534Shselasky{ 148223534Shselasky unsigned long chan = (unsigned long)link->con_priv; 149223534Shselasky struct stm32_ipcc *ipcc = container_of(link->mbox, struct stm32_ipcc, 150223534Shselasky controller); 151223534Shselasky 152223534Shselasky dev_dbg(ipcc->controller.dev, "%s: chan:%lu\n", __func__, chan); 153223534Shselasky 154223534Shselasky /* set channel n occupied */ 155223534Shselasky stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR, 156223534Shselasky TX_BIT_CHAN(chan)); 157223534Shselasky 158223534Shselasky /* unmask 'tx channel free' interrupt */ 159223534Shselasky stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 160223534Shselasky TX_BIT_CHAN(chan)); 161223534Shselasky 162223534Shselasky return 0; 163223534Shselasky} 164223534Shselasky 165223534Shselaskystatic int stm32_ipcc_startup(struct mbox_chan *link) 166223534Shselasky{ 167223534Shselasky unsigned long chan = (unsigned long)link->con_priv; 168223534Shselasky struct stm32_ipcc *ipcc = container_of(link->mbox, struct stm32_ipcc, 169223534Shselasky controller); 170223534Shselasky int ret; 171223534Shselasky 172223534Shselasky ret = clk_prepare_enable(ipcc->clk); 173223534Shselasky if (ret) { 174223534Shselasky dev_err(ipcc->controller.dev, "can not enable the clock\n"); 175223534Shselasky return ret; 176223534Shselasky } 177223534Shselasky 178223534Shselasky /* unmask 'rx channel occupied' interrupt */ 179223534Shselasky stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 180223534Shselasky RX_BIT_CHAN(chan)); 181223534Shselasky 182223534Shselasky return 0; 183223534Shselasky} 184223534Shselasky 185223534Shselaskystatic void stm32_ipcc_shutdown(struct mbox_chan *link) 186223534Shselasky{ 187223534Shselasky unsigned long chan = (unsigned long)link->con_priv; 188223534Shselasky struct stm32_ipcc *ipcc = container_of(link->mbox, struct stm32_ipcc, 189223534Shselasky controller); 190223534Shselasky 191223534Shselasky /* mask rx/tx interrupt */ 192223534Shselasky stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 193223534Shselasky RX_BIT_CHAN(chan) | TX_BIT_CHAN(chan)); 194223534Shselasky 195223534Shselasky clk_disable_unprepare(ipcc->clk); 196223534Shselasky} 197223534Shselasky 198223534Shselaskystatic const struct mbox_chan_ops stm32_ipcc_ops = { 199223534Shselasky .send_data = stm32_ipcc_send_data, 200223534Shselasky .startup = stm32_ipcc_startup, 201223534Shselasky .shutdown = stm32_ipcc_shutdown, 202223534Shselasky}; 203223534Shselasky 204223534Shselaskystatic int stm32_ipcc_probe(struct platform_device *pdev) 205223534Shselasky{ 206223534Shselasky struct device *dev = &pdev->dev; 207223534Shselasky struct device_node *np = dev->of_node; 208223534Shselasky struct stm32_ipcc *ipcc; 209223534Shselasky unsigned long i; 210223534Shselasky int ret; 211223534Shselasky u32 ip_ver; 212223534Shselasky static const char * const irq_name[] = {"rx", "tx"}; 213223534Shselasky irq_handler_t irq_thread[] = {stm32_ipcc_rx_irq, stm32_ipcc_tx_irq}; 214223534Shselasky 215223534Shselasky if (!np) { 216223534Shselasky dev_err(dev, "No DT found\n"); 217223534Shselasky return -ENODEV; 218223534Shselasky } 219223534Shselasky 220223534Shselasky ipcc = devm_kzalloc(dev, sizeof(*ipcc), GFP_KERNEL); 221223534Shselasky if (!ipcc) 222223534Shselasky return -ENOMEM; 223223534Shselasky 224223534Shselasky spin_lock_init(&ipcc->lock); 225223534Shselasky 226223534Shselasky /* proc_id */ 227223534Shselasky if (of_property_read_u32(np, "st,proc-id", &ipcc->proc_id)) { 228223534Shselasky dev_err(dev, "Missing st,proc-id\n"); 229223534Shselasky return -ENODEV; 230223534Shselasky } 231223534Shselasky 232223534Shselasky if (ipcc->proc_id >= STM32_MAX_PROCS) { 233223534Shselasky dev_err(dev, "Invalid proc_id (%d)\n", ipcc->proc_id); 234223534Shselasky return -EINVAL; 235223534Shselasky } 236223534Shselasky 237223534Shselasky /* regs */ 238223534Shselasky ipcc->reg_base = devm_platform_ioremap_resource(pdev, 0); 239223534Shselasky if (IS_ERR(ipcc->reg_base)) 240223534Shselasky return PTR_ERR(ipcc->reg_base); 241223534Shselasky 242223534Shselasky ipcc->reg_proc = ipcc->reg_base + ipcc->proc_id * IPCC_PROC_OFFST; 243223534Shselasky 244223534Shselasky /* clock */ 245223534Shselasky ipcc->clk = devm_clk_get(dev, NULL); 246223534Shselasky if (IS_ERR(ipcc->clk)) 247223534Shselasky return PTR_ERR(ipcc->clk); 248223534Shselasky 249223534Shselasky ret = clk_prepare_enable(ipcc->clk); 250223534Shselasky if (ret) { 251223534Shselasky dev_err(dev, "can not enable the clock\n"); 252223534Shselasky return ret; 253223534Shselasky } 254223534Shselasky 255223534Shselasky /* irq */ 256223534Shselasky for (i = 0; i < IPCC_IRQ_NUM; i++) { 257223534Shselasky ipcc->irqs[i] = platform_get_irq_byname(pdev, irq_name[i]); 258223534Shselasky if (ipcc->irqs[i] < 0) { 259223534Shselasky ret = ipcc->irqs[i]; 260223534Shselasky goto err_clk; 261223534Shselasky } 262223534Shselasky 263223534Shselasky ret = devm_request_threaded_irq(dev, ipcc->irqs[i], NULL, 264223534Shselasky irq_thread[i], IRQF_ONESHOT, 265223534Shselasky dev_name(dev), ipcc); 266223534Shselasky if (ret) { 267223534Shselasky dev_err(dev, "failed to request irq %lu (%d)\n", i, ret); 268223534Shselasky goto err_clk; 269223534Shselasky } 270223534Shselasky } 271223534Shselasky 272223534Shselasky /* mask and enable rx/tx irq */ 273223534Shselasky stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR, 274223534Shselasky RX_BIT_MASK | TX_BIT_MASK); 275223534Shselasky stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XCR, 276223534Shselasky XCR_RXOIE | XCR_TXOIE); 277223534Shselasky 278223534Shselasky /* wakeup */ 279223535Shselasky if (of_property_read_bool(np, "wakeup-source")) { 280223535Shselasky device_set_wakeup_capable(dev, true); 281223535Shselasky 282223535Shselasky ret = dev_pm_set_wake_irq(dev, ipcc->irqs[IPCC_IRQ_RX]); 283223534Shselasky if (ret) { 284223534Shselasky dev_err(dev, "Failed to set wake up irq\n"); 285223534Shselasky goto err_init_wkp; 286223534Shselasky } 287223534Shselasky } 288223534Shselasky 289223534Shselasky /* mailbox controller */ 290223534Shselasky ipcc->n_chans = readl_relaxed(ipcc->reg_base + IPCC_HWCFGR); 291223534Shselasky ipcc->n_chans &= IPCFGR_CHAN_MASK; 292223534Shselasky 293223534Shselasky ipcc->controller.dev = dev; 294223534Shselasky ipcc->controller.txdone_irq = true; 295223534Shselasky ipcc->controller.ops = &stm32_ipcc_ops; 296223534Shselasky ipcc->controller.num_chans = ipcc->n_chans; 297223534Shselasky ipcc->controller.chans = devm_kcalloc(dev, ipcc->controller.num_chans, 298223534Shselasky sizeof(*ipcc->controller.chans), 299223534Shselasky GFP_KERNEL); 300223534Shselasky if (!ipcc->controller.chans) { 301223534Shselasky ret = -ENOMEM; 302223534Shselasky goto err_irq_wkp; 303223534Shselasky } 304223534Shselasky 305223534Shselasky for (i = 0; i < ipcc->controller.num_chans; i++) 306223534Shselasky ipcc->controller.chans[i].con_priv = (void *)i; 307223534Shselasky 308223534Shselasky ret = devm_mbox_controller_register(dev, &ipcc->controller); 309223534Shselasky if (ret) 310223534Shselasky goto err_irq_wkp; 311223534Shselasky 312223534Shselasky platform_set_drvdata(pdev, ipcc); 313223534Shselasky 314223534Shselasky ip_ver = readl_relaxed(ipcc->reg_base + IPCC_VER); 315223534Shselasky 316223534Shselasky dev_info(dev, "ipcc rev:%ld.%ld enabled, %d chans, proc %d\n", 317223534Shselasky FIELD_GET(VER_MAJREV_MASK, ip_ver), 318223534Shselasky FIELD_GET(VER_MINREV_MASK, ip_ver), 319223534Shselasky ipcc->controller.num_chans, ipcc->proc_id); 320233110Shselasky 321223534Shselasky clk_disable_unprepare(ipcc->clk); 322223534Shselasky return 0; 323223534Shselasky 324223534Shselaskyerr_irq_wkp: 325223534Shselasky if (of_property_read_bool(np, "wakeup-source")) 326223534Shselasky dev_pm_clear_wake_irq(dev); 327223534Shselaskyerr_init_wkp: 328223534Shselasky device_set_wakeup_capable(dev, false); 329223534Shselaskyerr_clk: 330223534Shselasky clk_disable_unprepare(ipcc->clk); 331223534Shselasky return ret; 332223534Shselasky} 333223534Shselasky 334223534Shselaskystatic void stm32_ipcc_remove(struct platform_device *pdev) 335223534Shselasky{ 336223534Shselasky struct device *dev = &pdev->dev; 337223534Shselasky 338223534Shselasky if (of_property_read_bool(dev->of_node, "wakeup-source")) 339223534Shselasky dev_pm_clear_wake_irq(&pdev->dev); 340223534Shselasky 341223534Shselasky device_set_wakeup_capable(dev, false); 342223534Shselasky} 343223534Shselasky 344223534Shselasky#ifdef CONFIG_PM_SLEEP 345223534Shselaskystatic int stm32_ipcc_suspend(struct device *dev) 346223534Shselasky{ 347223534Shselasky struct stm32_ipcc *ipcc = dev_get_drvdata(dev); 348223534Shselasky 349223534Shselasky ipcc->xmr = readl_relaxed(ipcc->reg_proc + IPCC_XMR); 350223534Shselasky ipcc->xcr = readl_relaxed(ipcc->reg_proc + IPCC_XCR); 351223534Shselasky 352223534Shselasky return 0; 353223534Shselasky} 354223534Shselasky 355223534Shselaskystatic int stm32_ipcc_resume(struct device *dev) 356223534Shselasky{ 357223534Shselasky struct stm32_ipcc *ipcc = dev_get_drvdata(dev); 358223534Shselasky 359223534Shselasky writel_relaxed(ipcc->xmr, ipcc->reg_proc + IPCC_XMR); 360223534Shselasky writel_relaxed(ipcc->xcr, ipcc->reg_proc + IPCC_XCR); 361223534Shselasky 362223534Shselasky return 0; 363223534Shselasky} 364223534Shselasky#endif 365223534Shselasky 366223534Shselaskystatic SIMPLE_DEV_PM_OPS(stm32_ipcc_pm_ops, 367223534Shselasky stm32_ipcc_suspend, stm32_ipcc_resume); 368223534Shselasky 369223534Shselaskystatic const struct of_device_id stm32_ipcc_of_match[] = { 370223534Shselasky { .compatible = "st,stm32mp1-ipcc" }, 371223534Shselasky {}, 372223534Shselasky}; 373223534ShselaskyMODULE_DEVICE_TABLE(of, stm32_ipcc_of_match); 374223534Shselasky 375223534Shselaskystatic struct platform_driver stm32_ipcc_driver = { 376223534Shselasky .driver = { 377223534Shselasky .name = "stm32-ipcc", 378223534Shselasky .pm = &stm32_ipcc_pm_ops, 379223534Shselasky .of_match_table = stm32_ipcc_of_match, 380223534Shselasky }, 381223534Shselasky .probe = stm32_ipcc_probe, 382223534Shselasky .remove_new = stm32_ipcc_remove, 383223534Shselasky}; 384223534Shselasky 385223534Shselaskymodule_platform_driver(stm32_ipcc_driver); 386223534Shselasky 387MODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>"); 388MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>"); 389MODULE_DESCRIPTION("STM32 IPCC driver"); 390MODULE_LICENSE("GPL v2"); 391