brintos

brintos / linux-shallow public Read only

0
0
Text · 7.4 KiB · 17c29e9 Raw
299 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2017-2018 HiSilicon Limited.3// Copyright (c) 2017-2018 Linaro Limited.4 5#include <linux/bitops.h>6#include <linux/delay.h>7#include <linux/device.h>8#include <linux/err.h>9#include <linux/interrupt.h>10#include <linux/io.h>11#include <linux/iopoll.h>12#include <linux/mailbox_controller.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/slab.h>17 18#include "mailbox.h"19 20#define MBOX_CHAN_MAX			3221 22#define MBOX_RX				0x023#define MBOX_TX				0x124 25#define MBOX_BASE(mbox, ch)		((mbox)->base + ((ch) * 0x40))26#define MBOX_SRC_REG			0x0027#define MBOX_DST_REG			0x0428#define MBOX_DCLR_REG			0x0829#define MBOX_DSTAT_REG			0x0c30#define MBOX_MODE_REG			0x1031#define MBOX_IMASK_REG			0x1432#define MBOX_ICLR_REG			0x1833#define MBOX_SEND_REG			0x1c34#define MBOX_DATA_REG			0x2035 36#define MBOX_IPC_LOCK_REG		0xa0037#define MBOX_IPC_UNLOCK			0x1acce55138 39#define MBOX_AUTOMATIC_ACK		140 41#define MBOX_STATE_IDLE			BIT(4)42#define MBOX_STATE_READY		BIT(5)43#define MBOX_STATE_ACK			BIT(7)44 45#define MBOX_MSG_LEN			846 47/**48 * struct hi3660_chan_info - Hi3660 mailbox channel information49 * @dst_irq:	Interrupt vector for remote processor50 * @ack_irq:	Interrupt vector for local processor51 *52 * A channel can be used for TX or RX, it can trigger remote53 * processor interrupt to notify remote processor and can receive54 * interrupt if it has an incoming message.55 */56struct hi3660_chan_info {57	unsigned int dst_irq;58	unsigned int ack_irq;59};60 61/**62 * struct hi3660_mbox - Hi3660 mailbox controller data63 * @dev:	Device to which it is attached64 * @base:	Base address of the register mapping region65 * @chan:	Representation of channels in mailbox controller66 * @mchan:	Representation of channel info67 * @controller:	Representation of a communication channel controller68 *69 * Mailbox controller includes 32 channels and can allocate70 * channel for message transferring.71 */72struct hi3660_mbox {73	struct device *dev;74	void __iomem *base;75	struct mbox_chan chan[MBOX_CHAN_MAX];76	struct hi3660_chan_info mchan[MBOX_CHAN_MAX];77	struct mbox_controller controller;78};79 80static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox)81{82	return container_of(mbox, struct hi3660_mbox, controller);83}84 85static int hi3660_mbox_check_state(struct mbox_chan *chan)86{87	unsigned long ch = (unsigned long)chan->con_priv;88	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);89	struct hi3660_chan_info *mchan = &mbox->mchan[ch];90	void __iomem *base = MBOX_BASE(mbox, ch);91	unsigned long val;92	unsigned int ret;93 94	/* Mailbox is ready to use */95	if (readl(base + MBOX_MODE_REG) & MBOX_STATE_READY)96		return 0;97 98	/* Wait for acknowledge from remote */99	ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG,100			val, (val & MBOX_STATE_ACK), 1000, 300000);101	if (ret) {102		dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__);103		return ret;104	}105 106	/* clear ack state, mailbox will get back to ready state */107	writel(BIT(mchan->ack_irq), base + MBOX_ICLR_REG);108 109	return 0;110}111 112static int hi3660_mbox_unlock(struct mbox_chan *chan)113{114	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);115	unsigned int val, retry = 3;116 117	do {118		writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG);119 120		val = readl(mbox->base + MBOX_IPC_LOCK_REG);121		if (!val)122			break;123 124		udelay(10);125	} while (retry--);126 127	if (val)128		dev_err(mbox->dev, "%s: failed to unlock mailbox\n", __func__);129 130	return (!val) ? 0 : -ETIMEDOUT;131}132 133static int hi3660_mbox_acquire_channel(struct mbox_chan *chan)134{135	unsigned long ch = (unsigned long)chan->con_priv;136	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);137	struct hi3660_chan_info *mchan = &mbox->mchan[ch];138	void __iomem *base = MBOX_BASE(mbox, ch);139	unsigned int val, retry;140 141	for (retry = 10; retry; retry--) {142		/* Check if channel is in idle state */143		if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) {144			writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG);145 146			/* Check ack bit has been set successfully */147			val = readl(base + MBOX_SRC_REG);148			if (val & BIT(mchan->ack_irq))149				break;150		}151	}152 153	if (!retry)154		dev_err(mbox->dev, "%s: failed to acquire channel\n", __func__);155 156	return retry ? 0 : -ETIMEDOUT;157}158 159static int hi3660_mbox_startup(struct mbox_chan *chan)160{161	int ret;162 163	ret = hi3660_mbox_unlock(chan);164	if (ret)165		return ret;166 167	ret = hi3660_mbox_acquire_channel(chan);168	if (ret)169		return ret;170 171	return 0;172}173 174static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg)175{176	unsigned long ch = (unsigned long)chan->con_priv;177	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);178	struct hi3660_chan_info *mchan = &mbox->mchan[ch];179	void __iomem *base = MBOX_BASE(mbox, ch);180	u32 *buf = msg;181	unsigned int i;182	int ret;183 184	ret = hi3660_mbox_check_state(chan);185	if (ret)186		return ret;187 188	/* Clear mask for destination interrupt */189	writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG);190 191	/* Config destination for interrupt vector */192	writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG);193 194	/* Automatic acknowledge mode */195	writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG);196 197	/* Fill message data */198	for (i = 0; i < MBOX_MSG_LEN; i++)199		writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4);200 201	/* Trigger data transferring */202	writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG);203	return 0;204}205 206static const struct mbox_chan_ops hi3660_mbox_ops = {207	.startup	= hi3660_mbox_startup,208	.send_data	= hi3660_mbox_send_data,209};210 211static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller,212					   const struct of_phandle_args *spec)213{214	struct hi3660_mbox *mbox = to_hi3660_mbox(controller);215	struct hi3660_chan_info *mchan;216	unsigned int ch = spec->args[0];217 218	if (ch >= MBOX_CHAN_MAX) {219		dev_err(mbox->dev, "Invalid channel idx %d\n", ch);220		return ERR_PTR(-EINVAL);221	}222 223	mchan = &mbox->mchan[ch];224	mchan->dst_irq = spec->args[1];225	mchan->ack_irq = spec->args[2];226 227	return &mbox->chan[ch];228}229 230static const struct of_device_id hi3660_mbox_of_match[] = {231	{ .compatible = "hisilicon,hi3660-mbox", },232	{},233};234 235MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match);236 237static int hi3660_mbox_probe(struct platform_device *pdev)238{239	struct device *dev = &pdev->dev;240	struct hi3660_mbox *mbox;241	struct mbox_chan *chan;242	unsigned long ch;243	int err;244 245	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);246	if (!mbox)247		return -ENOMEM;248 249	mbox->base = devm_platform_ioremap_resource(pdev, 0);250	if (IS_ERR(mbox->base))251		return PTR_ERR(mbox->base);252 253	mbox->dev = dev;254	mbox->controller.dev = dev;255	mbox->controller.chans = mbox->chan;256	mbox->controller.num_chans = MBOX_CHAN_MAX;257	mbox->controller.ops = &hi3660_mbox_ops;258	mbox->controller.of_xlate = hi3660_mbox_xlate;259 260	/* Initialize mailbox channel data */261	chan = mbox->chan;262	for (ch = 0; ch < MBOX_CHAN_MAX; ch++)263		chan[ch].con_priv = (void *)ch;264 265	err = devm_mbox_controller_register(dev, &mbox->controller);266	if (err) {267		dev_err(dev, "Failed to register mailbox %d\n", err);268		return err;269	}270 271	platform_set_drvdata(pdev, mbox);272	dev_info(dev, "Mailbox enabled\n");273	return 0;274}275 276static struct platform_driver hi3660_mbox_driver = {277	.probe  = hi3660_mbox_probe,278	.driver = {279		.name = "hi3660-mbox",280		.of_match_table = hi3660_mbox_of_match,281	},282};283 284static int __init hi3660_mbox_init(void)285{286	return platform_driver_register(&hi3660_mbox_driver);287}288core_initcall(hi3660_mbox_init);289 290static void __exit hi3660_mbox_exit(void)291{292	platform_driver_unregister(&hi3660_mbox_driver);293}294module_exit(hi3660_mbox_exit);295 296MODULE_LICENSE("GPL");297MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller");298MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");299