188 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.4 */5 6#include <linux/bitops.h>7#include <linux/interrupt.h>8#include <linux/io.h>9#include <linux/irq.h>10#include <linux/irqdomain.h>11#include <linux/mailbox_controller.h>12#include <linux/module.h>13#include <linux/platform_device.h>14 15#define APSS_CPUCP_IPC_CHAN_SUPPORTED 316#define APSS_CPUCP_MBOX_CMD_OFF 0x417 18/* Tx Registers */19#define APSS_CPUCP_TX_MBOX_CMD(i) (0x100 + ((i) * 8))20 21/* Rx Registers */22#define APSS_CPUCP_RX_MBOX_CMD(i) (0x100 + ((i) * 8))23#define APSS_CPUCP_RX_MBOX_MAP 0x400024#define APSS_CPUCP_RX_MBOX_STAT 0x440025#define APSS_CPUCP_RX_MBOX_CLEAR 0x480026#define APSS_CPUCP_RX_MBOX_EN 0x4c0027#define APSS_CPUCP_RX_MBOX_CMD_MASK GENMASK_ULL(63, 0)28 29/**30 * struct qcom_cpucp_mbox - Holder for the mailbox driver31 * @chans: The mailbox channel32 * @mbox: The mailbox controller33 * @tx_base: Base address of the CPUCP tx registers34 * @rx_base: Base address of the CPUCP rx registers35 */36struct qcom_cpucp_mbox {37 struct mbox_chan chans[APSS_CPUCP_IPC_CHAN_SUPPORTED];38 struct mbox_controller mbox;39 void __iomem *tx_base;40 void __iomem *rx_base;41};42 43static inline int channel_number(struct mbox_chan *chan)44{45 return chan - chan->mbox->chans;46}47 48static irqreturn_t qcom_cpucp_mbox_irq_fn(int irq, void *data)49{50 struct qcom_cpucp_mbox *cpucp = data;51 u64 status;52 int i;53 54 status = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_STAT);55 56 for_each_set_bit(i, (unsigned long *)&status, APSS_CPUCP_IPC_CHAN_SUPPORTED) {57 u32 val = readl(cpucp->rx_base + APSS_CPUCP_RX_MBOX_CMD(i) + APSS_CPUCP_MBOX_CMD_OFF);58 struct mbox_chan *chan = &cpucp->chans[i];59 unsigned long flags;60 61 /* Provide mutual exclusion with changes to chan->cl */62 spin_lock_irqsave(&chan->lock, flags);63 if (chan->cl)64 mbox_chan_received_data(chan, &val);65 writeq(BIT(i), cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR);66 spin_unlock_irqrestore(&chan->lock, flags);67 }68 69 return IRQ_HANDLED;70}71 72static int qcom_cpucp_mbox_startup(struct mbox_chan *chan)73{74 struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);75 unsigned long chan_id = channel_number(chan);76 u64 val;77 78 val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);79 val |= BIT(chan_id);80 writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);81 82 return 0;83}84 85static void qcom_cpucp_mbox_shutdown(struct mbox_chan *chan)86{87 struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);88 unsigned long chan_id = channel_number(chan);89 u64 val;90 91 val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);92 val &= ~BIT(chan_id);93 writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);94}95 96static int qcom_cpucp_mbox_send_data(struct mbox_chan *chan, void *data)97{98 struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);99 unsigned long chan_id = channel_number(chan);100 u32 *val = data;101 102 writel(*val, cpucp->tx_base + APSS_CPUCP_TX_MBOX_CMD(chan_id) + APSS_CPUCP_MBOX_CMD_OFF);103 104 return 0;105}106 107static const struct mbox_chan_ops qcom_cpucp_mbox_chan_ops = {108 .startup = qcom_cpucp_mbox_startup,109 .send_data = qcom_cpucp_mbox_send_data,110 .shutdown = qcom_cpucp_mbox_shutdown111};112 113static int qcom_cpucp_mbox_probe(struct platform_device *pdev)114{115 struct device *dev = &pdev->dev;116 struct qcom_cpucp_mbox *cpucp;117 struct mbox_controller *mbox;118 int irq, ret;119 120 cpucp = devm_kzalloc(dev, sizeof(*cpucp), GFP_KERNEL);121 if (!cpucp)122 return -ENOMEM;123 124 cpucp->rx_base = devm_of_iomap(dev, dev->of_node, 0, NULL);125 if (IS_ERR(cpucp->rx_base))126 return PTR_ERR(cpucp->rx_base);127 128 cpucp->tx_base = devm_of_iomap(dev, dev->of_node, 1, NULL);129 if (IS_ERR(cpucp->tx_base))130 return PTR_ERR(cpucp->tx_base);131 132 writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);133 writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR);134 writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);135 136 irq = platform_get_irq(pdev, 0);137 if (irq < 0)138 return irq;139 140 ret = devm_request_irq(dev, irq, qcom_cpucp_mbox_irq_fn,141 IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "apss_cpucp_mbox", cpucp);142 if (ret < 0)143 return dev_err_probe(dev, ret, "Failed to register irq: %d\n", irq);144 145 writeq(APSS_CPUCP_RX_MBOX_CMD_MASK, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);146 147 mbox = &cpucp->mbox;148 mbox->dev = dev;149 mbox->num_chans = APSS_CPUCP_IPC_CHAN_SUPPORTED;150 mbox->chans = cpucp->chans;151 mbox->ops = &qcom_cpucp_mbox_chan_ops;152 153 ret = devm_mbox_controller_register(dev, mbox);154 if (ret)155 return dev_err_probe(dev, ret, "Failed to create mailbox\n");156 157 return 0;158}159 160static const struct of_device_id qcom_cpucp_mbox_of_match[] = {161 { .compatible = "qcom,x1e80100-cpucp-mbox" },162 {}163};164MODULE_DEVICE_TABLE(of, qcom_cpucp_mbox_of_match);165 166static struct platform_driver qcom_cpucp_mbox_driver = {167 .probe = qcom_cpucp_mbox_probe,168 .driver = {169 .name = "qcom_cpucp_mbox",170 .of_match_table = qcom_cpucp_mbox_of_match,171 },172};173 174static int __init qcom_cpucp_mbox_init(void)175{176 return platform_driver_register(&qcom_cpucp_mbox_driver);177}178core_initcall(qcom_cpucp_mbox_init);179 180static void __exit qcom_cpucp_mbox_exit(void)181{182 platform_driver_unregister(&qcom_cpucp_mbox_driver);183}184module_exit(qcom_cpucp_mbox_exit);185 186MODULE_DESCRIPTION("QTI CPUCP MBOX Driver");187MODULE_LICENSE("GPL");188