brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · 44f4ed1 Raw
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