brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · f77741c Raw
372 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Hisilicon's Hi6220 mailbox driver4 *5 * Copyright (c) 2015 HiSilicon Limited.6 * Copyright (c) 2015 Linaro Limited.7 *8 * Author: Leo Yan <leo.yan@linaro.org>9 */10 11#include <linux/device.h>12#include <linux/err.h>13#include <linux/interrupt.h>14#include <linux/io.h>15#include <linux/kfifo.h>16#include <linux/mailbox_controller.h>17#include <linux/module.h>18#include <linux/of.h>19#include <linux/platform_device.h>20#include <linux/slab.h>21 22#define MBOX_CHAN_MAX			3223 24#define MBOX_TX				0x125 26/* Mailbox message length: 8 words */27#define MBOX_MSG_LEN			828 29/* Mailbox Registers */30#define MBOX_OFF(m)			(0x40 * (m))31#define MBOX_MODE_REG(m)		(MBOX_OFF(m) + 0x0)32#define MBOX_DATA_REG(m)		(MBOX_OFF(m) + 0x4)33 34#define MBOX_STATE_MASK			(0xF << 4)35#define MBOX_STATE_IDLE			(0x1 << 4)36#define MBOX_STATE_TX			(0x2 << 4)37#define MBOX_STATE_RX			(0x4 << 4)38#define MBOX_STATE_ACK			(0x8 << 4)39#define MBOX_ACK_CONFIG_MASK		(0x1 << 0)40#define MBOX_ACK_AUTOMATIC		(0x1 << 0)41#define MBOX_ACK_IRQ			(0x0 << 0)42 43/* IPC registers */44#define ACK_INT_RAW_REG(i)		((i) + 0x400)45#define ACK_INT_MSK_REG(i)		((i) + 0x404)46#define ACK_INT_STAT_REG(i)		((i) + 0x408)47#define ACK_INT_CLR_REG(i)		((i) + 0x40c)48#define ACK_INT_ENA_REG(i)		((i) + 0x500)49#define ACK_INT_DIS_REG(i)		((i) + 0x504)50#define DST_INT_RAW_REG(i)		((i) + 0x420)51 52 53struct hi6220_mbox_chan {54 55	/*56	 * Description for channel's hardware info:57	 *  - direction: tx or rx58	 *  - dst irq: peer core's irq number59	 *  - ack irq: local irq number60	 *  - slot number61	 */62	unsigned int dir, dst_irq, ack_irq;63	unsigned int slot;64 65	struct hi6220_mbox *parent;66};67 68struct hi6220_mbox {69	struct device *dev;70 71	int irq;72 73	/* flag of enabling tx's irq mode */74	bool tx_irq_mode;75 76	/* region for ipc event */77	void __iomem *ipc;78 79	/* region for mailbox */80	void __iomem *base;81 82	unsigned int chan_num;83	struct hi6220_mbox_chan *mchan;84 85	void *irq_map_chan[MBOX_CHAN_MAX];86	struct mbox_chan *chan;87	struct mbox_controller controller;88};89 90static void mbox_set_state(struct hi6220_mbox *mbox,91			   unsigned int slot, u32 val)92{93	u32 status;94 95	status = readl(mbox->base + MBOX_MODE_REG(slot));96	status = (status & ~MBOX_STATE_MASK) | val;97	writel(status, mbox->base + MBOX_MODE_REG(slot));98}99 100static void mbox_set_mode(struct hi6220_mbox *mbox,101			  unsigned int slot, u32 val)102{103	u32 mode;104 105	mode = readl(mbox->base + MBOX_MODE_REG(slot));106	mode = (mode & ~MBOX_ACK_CONFIG_MASK) | val;107	writel(mode, mbox->base + MBOX_MODE_REG(slot));108}109 110static bool hi6220_mbox_last_tx_done(struct mbox_chan *chan)111{112	struct hi6220_mbox_chan *mchan = chan->con_priv;113	struct hi6220_mbox *mbox = mchan->parent;114	u32 state;115 116	/* Only set idle state for polling mode */117	BUG_ON(mbox->tx_irq_mode);118 119	state = readl(mbox->base + MBOX_MODE_REG(mchan->slot));120	return ((state & MBOX_STATE_MASK) == MBOX_STATE_IDLE);121}122 123static int hi6220_mbox_send_data(struct mbox_chan *chan, void *msg)124{125	struct hi6220_mbox_chan *mchan = chan->con_priv;126	struct hi6220_mbox *mbox = mchan->parent;127	unsigned int slot = mchan->slot;128	u32 *buf = msg;129	int i;130 131	/* indicate as a TX channel */132	mchan->dir = MBOX_TX;133 134	mbox_set_state(mbox, slot, MBOX_STATE_TX);135 136	if (mbox->tx_irq_mode)137		mbox_set_mode(mbox, slot, MBOX_ACK_IRQ);138	else139		mbox_set_mode(mbox, slot, MBOX_ACK_AUTOMATIC);140 141	for (i = 0; i < MBOX_MSG_LEN; i++)142		writel(buf[i], mbox->base + MBOX_DATA_REG(slot) + i * 4);143 144	/* trigger remote request */145	writel(BIT(mchan->dst_irq), DST_INT_RAW_REG(mbox->ipc));146	return 0;147}148 149static irqreturn_t hi6220_mbox_interrupt(int irq, void *p)150{151	struct hi6220_mbox *mbox = p;152	struct hi6220_mbox_chan *mchan;153	struct mbox_chan *chan;154	unsigned int state, intr_bit, i;155	u32 msg[MBOX_MSG_LEN];156 157	state = readl(ACK_INT_STAT_REG(mbox->ipc));158	if (!state) {159		dev_warn(mbox->dev, "%s: spurious interrupt\n",160			 __func__);161		return IRQ_HANDLED;162	}163 164	while (state) {165		intr_bit = __ffs(state);166		state &= (state - 1);167 168		chan = mbox->irq_map_chan[intr_bit];169		if (!chan) {170			dev_warn(mbox->dev, "%s: unexpected irq vector %d\n",171				 __func__, intr_bit);172			continue;173		}174 175		mchan = chan->con_priv;176		if (mchan->dir == MBOX_TX)177			mbox_chan_txdone(chan, 0);178		else {179			for (i = 0; i < MBOX_MSG_LEN; i++)180				msg[i] = readl(mbox->base +181					MBOX_DATA_REG(mchan->slot) + i * 4);182 183			mbox_chan_received_data(chan, (void *)msg);184		}185 186		/* clear IRQ source */187		writel(BIT(mchan->ack_irq), ACK_INT_CLR_REG(mbox->ipc));188		mbox_set_state(mbox, mchan->slot, MBOX_STATE_IDLE);189	}190 191	return IRQ_HANDLED;192}193 194static int hi6220_mbox_startup(struct mbox_chan *chan)195{196	struct hi6220_mbox_chan *mchan = chan->con_priv;197	struct hi6220_mbox *mbox = mchan->parent;198 199	mchan->dir = 0;200 201	/* enable interrupt */202	writel(BIT(mchan->ack_irq), ACK_INT_ENA_REG(mbox->ipc));203	return 0;204}205 206static void hi6220_mbox_shutdown(struct mbox_chan *chan)207{208	struct hi6220_mbox_chan *mchan = chan->con_priv;209	struct hi6220_mbox *mbox = mchan->parent;210 211	/* disable interrupt */212	writel(BIT(mchan->ack_irq), ACK_INT_DIS_REG(mbox->ipc));213	mbox->irq_map_chan[mchan->ack_irq] = NULL;214}215 216static const struct mbox_chan_ops hi6220_mbox_ops = {217	.send_data    = hi6220_mbox_send_data,218	.startup      = hi6220_mbox_startup,219	.shutdown     = hi6220_mbox_shutdown,220	.last_tx_done = hi6220_mbox_last_tx_done,221};222 223static struct mbox_chan *hi6220_mbox_xlate(struct mbox_controller *controller,224					   const struct of_phandle_args *spec)225{226	struct hi6220_mbox *mbox = dev_get_drvdata(controller->dev);227	struct hi6220_mbox_chan *mchan;228	struct mbox_chan *chan;229	unsigned int i = spec->args[0];230	unsigned int dst_irq = spec->args[1];231	unsigned int ack_irq = spec->args[2];232 233	/* Bounds checking */234	if (i >= mbox->chan_num || dst_irq >= mbox->chan_num ||235	    ack_irq >= mbox->chan_num) {236		dev_err(mbox->dev,237			"Invalid channel idx %d dst_irq %d ack_irq %d\n",238			i, dst_irq, ack_irq);239		return ERR_PTR(-EINVAL);240	}241 242	/* Is requested channel free? */243	chan = &mbox->chan[i];244	if (mbox->irq_map_chan[ack_irq] == (void *)chan) {245		dev_err(mbox->dev, "Channel in use\n");246		return ERR_PTR(-EBUSY);247	}248 249	mchan = chan->con_priv;250	mchan->dst_irq = dst_irq;251	mchan->ack_irq = ack_irq;252 253	mbox->irq_map_chan[ack_irq] = (void *)chan;254	return chan;255}256 257static const struct of_device_id hi6220_mbox_of_match[] = {258	{ .compatible = "hisilicon,hi6220-mbox", },259	{},260};261MODULE_DEVICE_TABLE(of, hi6220_mbox_of_match);262 263static int hi6220_mbox_probe(struct platform_device *pdev)264{265	struct device_node *node = pdev->dev.of_node;266	struct device *dev = &pdev->dev;267	struct hi6220_mbox *mbox;268	int i, err;269 270	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);271	if (!mbox)272		return -ENOMEM;273 274	mbox->dev = dev;275	mbox->chan_num = MBOX_CHAN_MAX;276	mbox->mchan = devm_kcalloc(dev,277		mbox->chan_num, sizeof(*mbox->mchan), GFP_KERNEL);278	if (!mbox->mchan)279		return -ENOMEM;280 281	mbox->chan = devm_kcalloc(dev,282		mbox->chan_num, sizeof(*mbox->chan), GFP_KERNEL);283	if (!mbox->chan)284		return -ENOMEM;285 286	mbox->irq = platform_get_irq(pdev, 0);287	if (mbox->irq < 0)288		return mbox->irq;289 290	mbox->ipc = devm_platform_ioremap_resource(pdev, 0);291	if (IS_ERR(mbox->ipc)) {292		dev_err(dev, "ioremap ipc failed\n");293		return PTR_ERR(mbox->ipc);294	}295 296	mbox->base = devm_platform_ioremap_resource(pdev, 1);297	if (IS_ERR(mbox->base)) {298		dev_err(dev, "ioremap buffer failed\n");299		return PTR_ERR(mbox->base);300	}301 302	err = devm_request_irq(dev, mbox->irq, hi6220_mbox_interrupt, 0,303			dev_name(dev), mbox);304	if (err) {305		dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",306			err);307		return -ENODEV;308	}309 310	mbox->controller.dev = dev;311	mbox->controller.chans = &mbox->chan[0];312	mbox->controller.num_chans = mbox->chan_num;313	mbox->controller.ops = &hi6220_mbox_ops;314	mbox->controller.of_xlate = hi6220_mbox_xlate;315 316	for (i = 0; i < mbox->chan_num; i++) {317		mbox->chan[i].con_priv = &mbox->mchan[i];318		mbox->irq_map_chan[i] = NULL;319 320		mbox->mchan[i].parent = mbox;321		mbox->mchan[i].slot   = i;322	}323 324	/* mask and clear all interrupt vectors */325	writel(0x0,  ACK_INT_MSK_REG(mbox->ipc));326	writel(~0x0, ACK_INT_CLR_REG(mbox->ipc));327 328	/* use interrupt for tx's ack */329	mbox->tx_irq_mode = !of_property_read_bool(node, "hi6220,mbox-tx-noirq");330 331	if (mbox->tx_irq_mode)332		mbox->controller.txdone_irq = true;333	else {334		mbox->controller.txdone_poll = true;335		mbox->controller.txpoll_period = 5;336	}337 338	err = devm_mbox_controller_register(dev, &mbox->controller);339	if (err) {340		dev_err(dev, "Failed to register mailbox %d\n", err);341		return err;342	}343 344	platform_set_drvdata(pdev, mbox);345	dev_info(dev, "Mailbox enabled\n");346	return 0;347}348 349static struct platform_driver hi6220_mbox_driver = {350	.driver = {351		.name = "hi6220-mbox",352		.of_match_table = hi6220_mbox_of_match,353	},354	.probe	= hi6220_mbox_probe,355};356 357static int __init hi6220_mbox_init(void)358{359	return platform_driver_register(&hi6220_mbox_driver);360}361core_initcall(hi6220_mbox_init);362 363static void __exit hi6220_mbox_exit(void)364{365	platform_driver_unregister(&hi6220_mbox_driver);366}367module_exit(hi6220_mbox_exit);368 369MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");370MODULE_DESCRIPTION("Hi6220 mailbox driver");371MODULE_LICENSE("GPL v2");372