Rtwo/kernel/motorola/sm8550/drivers/soc/qcom/qcom_rimps.c
2025-09-30 19:22:48 -05:00

247 lines
6 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/platform_device.h>
#include <linux/mailbox_controller.h>
/* RIMPS Register offsets */
#define RIMPS_IPC_CHAN_SUPPORTED 2
#define RIMPS_SEND_IRQ_OFFSET 0xC
#define RIMPS_SEND_IRQ_VAL BIT(28)
#define RIMPS_CLEAR_IRQ_OFFSET 0x308
#define RIMPS_STATUS_IRQ_OFFSET 0x30C
#define RIMPS_CLEAR_IRQ_VAL BIT(3)
#define RIMPS_STATUS_IRQ_VAL BIT(3)
#define RIMPS_CLOCK_DOMAIN_OFFSET 0x1000
/**
* struct rimps_ipc ipc per channel
* @mbox: mailbox-controller interface
* @chans: The mailbox clients' channel array
* @dev: Device associated with this instance
* @irq: Rimps to HLOS irq
*/
struct qcom_rimps_ipc {
struct mbox_controller mbox;
struct mbox_chan chans[RIMPS_IPC_CHAN_SUPPORTED];
void __iomem *tx_irq_base;
void __iomem *rx_irq_base;
struct device *dev;
int irq;
int num_chan;
};
static irqreturn_t qcom_rimps_rx_interrupt(int irq, void *p)
{
struct qcom_rimps_ipc *rimps_ipc;
u32 val;
int i;
unsigned long flags;
rimps_ipc = p;
for (i = 0; i < rimps_ipc->num_chan; i++) {
val = readl(rimps_ipc->rx_irq_base +
RIMPS_STATUS_IRQ_OFFSET + (i * RIMPS_CLOCK_DOMAIN_OFFSET));
if (val & RIMPS_STATUS_IRQ_VAL) {
val = RIMPS_CLEAR_IRQ_VAL;
writel(val, rimps_ipc->rx_irq_base +
RIMPS_CLEAR_IRQ_OFFSET +
(i * RIMPS_CLOCK_DOMAIN_OFFSET));
/* Make sure register write is complete before proceeding */
mb();
spin_lock_irqsave(&rimps_ipc->chans[i].lock, flags);
if (rimps_ipc->chans[i].con_priv)
mbox_chan_received_data(&rimps_ipc->chans[i]
, NULL);
spin_unlock_irqrestore(&rimps_ipc->chans[i].lock, flags);
}
}
return IRQ_HANDLED;
}
static void qcom_rimps_mbox_shutdown(struct mbox_chan *chan)
{
unsigned long flags;
spin_lock_irqsave(&chan->lock, flags);
chan->con_priv = NULL;
spin_unlock_irqrestore(&chan->lock, flags);
}
static int qcom_rimps_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct qcom_rimps_ipc *rimps_ipc = container_of(chan->mbox,
struct qcom_rimps_ipc, mbox);
writel(RIMPS_SEND_IRQ_VAL,
rimps_ipc->tx_irq_base + RIMPS_SEND_IRQ_OFFSET);
return 0;
}
static struct mbox_chan *qcom_rimps_mbox_xlate(struct mbox_controller *mbox,
const struct of_phandle_args *sp)
{
struct qcom_rimps_ipc *rimps_ipc = container_of(mbox,
struct qcom_rimps_ipc, mbox);
unsigned long ind = sp->args[0];
if (sp->args_count != 1)
return ERR_PTR(-EINVAL);
if (ind >= mbox->num_chans)
return ERR_PTR(-EINVAL);
if (mbox->chans[ind].con_priv)
return ERR_PTR(-EBUSY);
mbox->chans[ind].con_priv = rimps_ipc;
return &mbox->chans[ind];
}
static const struct mbox_chan_ops rimps_mbox_chan_ops = {
.send_data = qcom_rimps_mbox_send_data,
.shutdown = qcom_rimps_mbox_shutdown
};
static int qcom_rimps_ipc_setup_mbox(struct qcom_rimps_ipc *rimps_ipc)
{
struct mbox_controller *mbox;
struct device *dev = rimps_ipc->dev;
unsigned long i;
/* Initialize channel identifiers */
for (i = 0; i < ARRAY_SIZE(rimps_ipc->chans); i++)
rimps_ipc->chans[i].con_priv = NULL;
mbox = &rimps_ipc->mbox;
mbox->dev = dev;
mbox->num_chans = rimps_ipc->num_chan;
mbox->chans = rimps_ipc->chans;
mbox->ops = &rimps_mbox_chan_ops;
mbox->of_xlate = qcom_rimps_mbox_xlate;
mbox->txdone_irq = false;
mbox->txdone_poll = false;
return mbox_controller_register(mbox);
}
static int qcom_rimps_probe(struct platform_device *pdev)
{
struct qcom_rimps_ipc *rimps_ipc;
struct resource *res;
int ret;
rimps_ipc = devm_kzalloc(&pdev->dev, sizeof(*rimps_ipc), GFP_KERNEL);
if (!rimps_ipc)
return -ENOMEM;
rimps_ipc->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Failed to get the device base address\n");
return -ENODEV;
}
rimps_ipc->tx_irq_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!rimps_ipc->tx_irq_base) {
dev_err(&pdev->dev, "Failed to ioremap the rimps tx irq addr\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(&pdev->dev, "Failed to get the device base address\n");
return -ENODEV;
}
rimps_ipc->rx_irq_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!rimps_ipc->rx_irq_base) {
dev_err(&pdev->dev, "Failed to ioremap the rimps rx irq addr\n");
return -ENOMEM;
}
rimps_ipc->irq = platform_get_irq(pdev, 0);
if (rimps_ipc->irq < 0) {
dev_err(&pdev->dev, "Failed to get the IRQ\n");
return rimps_ipc->irq;
}
rimps_ipc->num_chan = RIMPS_IPC_CHAN_SUPPORTED;
ret = qcom_rimps_ipc_setup_mbox(rimps_ipc);
if (ret) {
dev_err(&pdev->dev, "Failed to create mailbox\n");
return ret;
}
ret = devm_request_irq(&pdev->dev, rimps_ipc->irq,
qcom_rimps_rx_interrupt, IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "qcom_rimps", rimps_ipc);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret);
goto err_mbox;
}
platform_set_drvdata(pdev, rimps_ipc);
return 0;
err_mbox:
mbox_controller_unregister(&rimps_ipc->mbox);
return ret;
}
static int qcom_rimps_remove(struct platform_device *pdev)
{
struct qcom_rimps_ipc *rimps_ipc = platform_get_drvdata(pdev);
mbox_controller_unregister(&rimps_ipc->mbox);
return 0;
}
static const struct of_device_id qcom_rimps_of_match[] = {
{ .compatible = "qcom,rimps"},
{}
};
MODULE_DEVICE_TABLE(of, qcom_rimps_of_match);
static struct platform_driver qcom_rimps_driver = {
.probe = qcom_rimps_probe,
.remove = qcom_rimps_remove,
.driver = {
.name = "qcom_rimps",
.of_match_table = qcom_rimps_of_match,
.suppress_bind_attrs = true,
},
};
static int __init qcom_rimps_init(void)
{
int ret;
ret = platform_driver_register(&qcom_rimps_driver);
if (ret)
pr_err("%s: qcom_rimps register failed %d\n", __func__, ret);
return ret;
}
module_init(qcom_rimps_init);
static __exit void qcom_rimps_exit(void)
{
platform_driver_unregister(&qcom_rimps_driver);
}
module_exit(qcom_rimps_exit);
MODULE_DESCRIPTION("QTI RIMPS Driver");
MODULE_LICENSE("GPL v2");