brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · ee8539d Raw
414 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Spreadtrum mailbox driver4 *5 * Copyright (c) 2020 Spreadtrum Communications Inc.6 */7 8#include <linux/delay.h>9#include <linux/err.h>10#include <linux/interrupt.h>11#include <linux/io.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/clk.h>17 18#define SPRD_MBOX_ID		0x019#define SPRD_MBOX_MSG_LOW	0x420#define SPRD_MBOX_MSG_HIGH	0x821#define SPRD_MBOX_TRIGGER	0xc22#define SPRD_MBOX_FIFO_RST	0x1023#define SPRD_MBOX_FIFO_STS	0x1424#define SPRD_MBOX_IRQ_STS	0x1825#define SPRD_MBOX_IRQ_MSK	0x1c26#define SPRD_MBOX_LOCK		0x2027#define SPRD_MBOX_FIFO_DEPTH	0x2428 29/* Bit and mask definition for inbox's SPRD_MBOX_FIFO_STS register */30#define SPRD_INBOX_FIFO_DELIVER_MASK		GENMASK(23, 16)31#define SPRD_INBOX_FIFO_OVERLOW_MASK		GENMASK(15, 8)32#define SPRD_INBOX_FIFO_DELIVER_SHIFT		1633#define SPRD_INBOX_FIFO_BUSY_MASK		GENMASK(7, 0)34 35/* Bit and mask definition for SPRD_MBOX_IRQ_STS register */36#define SPRD_MBOX_IRQ_CLR			BIT(0)37 38/* Bit and mask definition for outbox's SPRD_MBOX_FIFO_STS register */39#define SPRD_OUTBOX_FIFO_FULL			BIT(2)40#define SPRD_OUTBOX_FIFO_WR_SHIFT		1641#define SPRD_OUTBOX_FIFO_RD_SHIFT		2442#define SPRD_OUTBOX_FIFO_POS_MASK		GENMASK(7, 0)43 44/* Bit and mask definition for inbox's SPRD_MBOX_IRQ_MSK register */45#define SPRD_INBOX_FIFO_BLOCK_IRQ		BIT(0)46#define SPRD_INBOX_FIFO_OVERFLOW_IRQ		BIT(1)47#define SPRD_INBOX_FIFO_DELIVER_IRQ		BIT(2)48#define SPRD_INBOX_FIFO_IRQ_MASK		GENMASK(2, 0)49 50/* Bit and mask definition for outbox's SPRD_MBOX_IRQ_MSK register */51#define SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ		BIT(0)52#define SPRD_OUTBOX_FIFO_IRQ_MASK		GENMASK(4, 0)53 54#define SPRD_OUTBOX_BASE_SPAN			0x100055#define SPRD_MBOX_CHAN_MAX			856#define SPRD_SUPP_INBOX_ID_SC9863A		757 58struct sprd_mbox_priv {59	struct mbox_controller	mbox;60	struct device		*dev;61	void __iomem		*inbox_base;62	void __iomem		*outbox_base;63	/*  Base register address for supplementary outbox */64	void __iomem		*supp_base;65	u32			outbox_fifo_depth;66 67	struct mutex		lock;68	u32			refcnt;69	struct mbox_chan	chan[SPRD_MBOX_CHAN_MAX];70};71 72static struct sprd_mbox_priv *to_sprd_mbox_priv(struct mbox_controller *mbox)73{74	return container_of(mbox, struct sprd_mbox_priv, mbox);75}76 77static u32 sprd_mbox_get_fifo_len(struct sprd_mbox_priv *priv, u32 fifo_sts)78{79	u32 wr_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_WR_SHIFT) &80		SPRD_OUTBOX_FIFO_POS_MASK;81	u32 rd_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_RD_SHIFT) &82		SPRD_OUTBOX_FIFO_POS_MASK;83	u32 fifo_len;84 85	/*86	 * If the read pointer is equal with write pointer, which means the fifo87	 * is full or empty.88	 */89	if (wr_pos == rd_pos) {90		if (fifo_sts & SPRD_OUTBOX_FIFO_FULL)91			fifo_len = priv->outbox_fifo_depth;92		else93			fifo_len = 0;94	} else if (wr_pos > rd_pos) {95		fifo_len = wr_pos - rd_pos;96	} else {97		fifo_len = priv->outbox_fifo_depth - rd_pos + wr_pos;98	}99 100	return fifo_len;101}102 103static irqreturn_t do_outbox_isr(void __iomem *base, struct sprd_mbox_priv *priv)104{105	struct mbox_chan *chan;106	u32 fifo_sts, fifo_len, msg[2];107	int i, id;108 109	fifo_sts = readl(base + SPRD_MBOX_FIFO_STS);110 111	fifo_len = sprd_mbox_get_fifo_len(priv, fifo_sts);112	if (!fifo_len) {113		dev_warn_ratelimited(priv->dev, "spurious outbox interrupt\n");114		return IRQ_NONE;115	}116 117	for (i = 0; i < fifo_len; i++) {118		msg[0] = readl(base + SPRD_MBOX_MSG_LOW);119		msg[1] = readl(base + SPRD_MBOX_MSG_HIGH);120		id = readl(base + SPRD_MBOX_ID);121 122		chan = &priv->chan[id];123		if (chan->cl)124			mbox_chan_received_data(chan, (void *)msg);125		else126			dev_warn_ratelimited(priv->dev,127				    "message's been dropped at ch[%d]\n", id);128 129		/* Trigger to update outbox FIFO pointer */130		writel(0x1, base + SPRD_MBOX_TRIGGER);131	}132 133	/* Clear irq status after reading all message. */134	writel(SPRD_MBOX_IRQ_CLR, base + SPRD_MBOX_IRQ_STS);135 136	return IRQ_HANDLED;137}138 139static irqreturn_t sprd_mbox_outbox_isr(int irq, void *data)140{141	struct sprd_mbox_priv *priv = data;142 143	return do_outbox_isr(priv->outbox_base, priv);144}145 146static irqreturn_t sprd_mbox_supp_isr(int irq, void *data)147{148	struct sprd_mbox_priv *priv = data;149 150	return do_outbox_isr(priv->supp_base, priv);151}152 153static irqreturn_t sprd_mbox_inbox_isr(int irq, void *data)154{155	struct sprd_mbox_priv *priv = data;156	struct mbox_chan *chan;157	u32 fifo_sts, send_sts, busy, id;158 159	fifo_sts = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS);160 161	/* Get the inbox data delivery status */162	send_sts = (fifo_sts & SPRD_INBOX_FIFO_DELIVER_MASK) >>163		SPRD_INBOX_FIFO_DELIVER_SHIFT;164	if (!send_sts) {165		dev_warn_ratelimited(priv->dev, "spurious inbox interrupt\n");166		return IRQ_NONE;167	}168 169	while (send_sts) {170		id = __ffs(send_sts);171		send_sts &= (send_sts - 1);172 173		chan = &priv->chan[id];174 175		/*176		 * Check if the message was fetched by remote target, if yes,177		 * that means the transmission has been completed.178		 */179		busy = fifo_sts & SPRD_INBOX_FIFO_BUSY_MASK;180		if (!(busy & BIT(id)))181			mbox_chan_txdone(chan, 0);182	}183 184	/* Clear FIFO delivery and overflow status */185	writel(fifo_sts &186	       (SPRD_INBOX_FIFO_DELIVER_MASK | SPRD_INBOX_FIFO_OVERLOW_MASK),187	       priv->inbox_base + SPRD_MBOX_FIFO_RST);188 189	/* Clear irq status */190	writel(SPRD_MBOX_IRQ_CLR, priv->inbox_base + SPRD_MBOX_IRQ_STS);191 192	return IRQ_HANDLED;193}194 195static int sprd_mbox_send_data(struct mbox_chan *chan, void *msg)196{197	struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);198	unsigned long id = (unsigned long)chan->con_priv;199	u32 *data = msg;200 201	/* Write data into inbox FIFO, and only support 8 bytes every time */202	writel(data[0], priv->inbox_base + SPRD_MBOX_MSG_LOW);203	writel(data[1], priv->inbox_base + SPRD_MBOX_MSG_HIGH);204 205	/* Set target core id */206	writel(id, priv->inbox_base + SPRD_MBOX_ID);207 208	/* Trigger remote request */209	writel(0x1, priv->inbox_base + SPRD_MBOX_TRIGGER);210 211	return 0;212}213 214static int sprd_mbox_flush(struct mbox_chan *chan, unsigned long timeout)215{216	struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);217	unsigned long id = (unsigned long)chan->con_priv;218	u32 busy;219 220	timeout = jiffies + msecs_to_jiffies(timeout);221 222	while (time_before(jiffies, timeout)) {223		busy = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS) &224			SPRD_INBOX_FIFO_BUSY_MASK;225		if (!(busy & BIT(id))) {226			mbox_chan_txdone(chan, 0);227			return 0;228		}229 230		udelay(1);231	}232 233	return -ETIME;234}235 236static int sprd_mbox_startup(struct mbox_chan *chan)237{238	struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);239	u32 val;240 241	mutex_lock(&priv->lock);242	if (priv->refcnt++ == 0) {243		/* Select outbox FIFO mode and reset the outbox FIFO status */244		writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST);245 246		/* Enable inbox FIFO overflow and delivery interrupt */247		val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK);248		val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ);249		writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK);250 251		/* Enable outbox FIFO not empty interrupt */252		val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK);253		val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ;254		writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK);255 256		/* Enable supplementary outbox as the fundamental one */257		if (priv->supp_base) {258			writel(0x0, priv->supp_base + SPRD_MBOX_FIFO_RST);259			val = readl(priv->supp_base + SPRD_MBOX_IRQ_MSK);260			val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ;261			writel(val, priv->supp_base + SPRD_MBOX_IRQ_MSK);262		}263	}264	mutex_unlock(&priv->lock);265 266	return 0;267}268 269static void sprd_mbox_shutdown(struct mbox_chan *chan)270{271	struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);272 273	mutex_lock(&priv->lock);274	if (--priv->refcnt == 0) {275		/* Disable inbox & outbox interrupt */276		writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK);277		writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK);278 279		if (priv->supp_base)280			writel(SPRD_OUTBOX_FIFO_IRQ_MASK,281			       priv->supp_base + SPRD_MBOX_IRQ_MSK);282	}283	mutex_unlock(&priv->lock);284}285 286static const struct mbox_chan_ops sprd_mbox_ops = {287	.send_data	= sprd_mbox_send_data,288	.flush		= sprd_mbox_flush,289	.startup	= sprd_mbox_startup,290	.shutdown	= sprd_mbox_shutdown,291};292 293static int sprd_mbox_probe(struct platform_device *pdev)294{295	struct device *dev = &pdev->dev;296	struct sprd_mbox_priv *priv;297	int ret, inbox_irq, outbox_irq, supp_irq;298	unsigned long id, supp;299	struct clk *clk;300 301	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);302	if (!priv)303		return -ENOMEM;304 305	priv->dev = dev;306	mutex_init(&priv->lock);307 308	/*309	 * Unisoc mailbox uses an inbox to send messages to the target310	 * core, and uses (an) outbox(es) to receive messages from other311	 * cores.312	 *313	 * Thus in general the mailbox controller supplies 2 different314	 * register addresses and IRQ numbers for inbox and outbox.315	 *316	 * If necessary, a supplementary inbox could be enabled optionally317	 * with an independent FIFO and an extra interrupt.318	 */319	priv->inbox_base = devm_platform_ioremap_resource(pdev, 0);320	if (IS_ERR(priv->inbox_base))321		return PTR_ERR(priv->inbox_base);322 323	priv->outbox_base = devm_platform_ioremap_resource(pdev, 1);324	if (IS_ERR(priv->outbox_base))325		return PTR_ERR(priv->outbox_base);326 327	clk = devm_clk_get_enabled(dev, "enable");328	if (IS_ERR(clk)) {329		dev_err(dev, "failed to get mailbox clock\n");330		return PTR_ERR(clk);331	}332 333	inbox_irq = platform_get_irq_byname(pdev, "inbox");334	if (inbox_irq < 0)335		return inbox_irq;336 337	ret = devm_request_irq(dev, inbox_irq, sprd_mbox_inbox_isr,338			       IRQF_NO_SUSPEND, dev_name(dev), priv);339	if (ret) {340		dev_err(dev, "failed to request inbox IRQ: %d\n", ret);341		return ret;342	}343 344	outbox_irq = platform_get_irq_byname(pdev, "outbox");345	if (outbox_irq < 0)346		return outbox_irq;347 348	ret = devm_request_irq(dev, outbox_irq, sprd_mbox_outbox_isr,349			       IRQF_NO_SUSPEND, dev_name(dev), priv);350	if (ret) {351		dev_err(dev, "failed to request outbox IRQ: %d\n", ret);352		return ret;353	}354 355	/* Supplementary outbox IRQ is optional */356	supp_irq = platform_get_irq_byname(pdev, "supp-outbox");357	if (supp_irq > 0) {358		ret = devm_request_irq(dev, supp_irq, sprd_mbox_supp_isr,359				       IRQF_NO_SUSPEND, dev_name(dev), priv);360		if (ret) {361			dev_err(dev, "failed to request outbox IRQ: %d\n", ret);362			return ret;363		}364 365		supp = (unsigned long) of_device_get_match_data(dev);366		if (!supp) {367			dev_err(dev, "no supplementary outbox specified\n");368			return -ENODEV;369		}370		priv->supp_base = priv->outbox_base + (SPRD_OUTBOX_BASE_SPAN * supp);371	}372 373	/* Get the default outbox FIFO depth */374	priv->outbox_fifo_depth =375		readl(priv->outbox_base + SPRD_MBOX_FIFO_DEPTH) + 1;376	priv->mbox.dev = dev;377	priv->mbox.chans = &priv->chan[0];378	priv->mbox.num_chans = SPRD_MBOX_CHAN_MAX;379	priv->mbox.ops = &sprd_mbox_ops;380	priv->mbox.txdone_irq = true;381 382	for (id = 0; id < SPRD_MBOX_CHAN_MAX; id++)383		priv->chan[id].con_priv = (void *)id;384 385	ret = devm_mbox_controller_register(dev, &priv->mbox);386	if (ret) {387		dev_err(dev, "failed to register mailbox: %d\n", ret);388		return ret;389	}390 391	return 0;392}393 394static const struct of_device_id sprd_mbox_of_match[] = {395	{ .compatible = "sprd,sc9860-mailbox" },396	{ .compatible = "sprd,sc9863a-mailbox",397	  .data = (void *)SPRD_SUPP_INBOX_ID_SC9863A },398	{ },399};400MODULE_DEVICE_TABLE(of, sprd_mbox_of_match);401 402static struct platform_driver sprd_mbox_driver = {403	.driver = {404		.name = "sprd-mailbox",405		.of_match_table = sprd_mbox_of_match,406	},407	.probe	= sprd_mbox_probe,408};409module_platform_driver(sprd_mbox_driver);410 411MODULE_AUTHOR("Baolin Wang <baolin.wang@unisoc.com>");412MODULE_DESCRIPTION("Spreadtrum mailbox driver");413MODULE_LICENSE("GPL v2");414