brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · afb320e Raw
362 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright Altera Corporation (C) 2013-2014. All rights reserved4 */5 6#include <linux/device.h>7#include <linux/interrupt.h>8#include <linux/io.h>9#include <linux/kernel.h>10#include <linux/mailbox_controller.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/platform_device.h>14 15#define DRIVER_NAME	"altera-mailbox"16 17#define MAILBOX_CMD_REG			0x0018#define MAILBOX_PTR_REG			0x0419#define MAILBOX_STS_REG			0x0820#define MAILBOX_INTMASK_REG		0x0C21 22#define INT_PENDING_MSK			0x123#define INT_SPACE_MSK			0x224 25#define STS_PENDING_MSK			0x126#define STS_FULL_MSK			0x227#define STS_FULL_OFT			0x128 29#define MBOX_PENDING(status)	(((status) & STS_PENDING_MSK))30#define MBOX_FULL(status)	(((status) & STS_FULL_MSK) >> STS_FULL_OFT)31 32enum altera_mbox_msg {33	MBOX_CMD = 0,34	MBOX_PTR,35};36 37#define MBOX_POLLING_MS		5	/* polling interval 5ms */38 39struct altera_mbox {40	bool is_sender;		/* 1-sender, 0-receiver */41	bool intr_mode;42	int irq;43	void __iomem *mbox_base;44	struct device *dev;45	struct mbox_controller controller;46 47	/* If the controller supports only RX polling mode */48	struct timer_list rxpoll_timer;49	struct mbox_chan *chan;50};51 52static struct altera_mbox *mbox_chan_to_altera_mbox(struct mbox_chan *chan)53{54	if (!chan || !chan->con_priv)55		return NULL;56 57	return (struct altera_mbox *)chan->con_priv;58}59 60static inline int altera_mbox_full(struct altera_mbox *mbox)61{62	u32 status;63 64	status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG);65	return MBOX_FULL(status);66}67 68static inline int altera_mbox_pending(struct altera_mbox *mbox)69{70	u32 status;71 72	status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG);73	return MBOX_PENDING(status);74}75 76static void altera_mbox_rx_intmask(struct altera_mbox *mbox, bool enable)77{78	u32 mask;79 80	mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG);81	if (enable)82		mask |= INT_PENDING_MSK;83	else84		mask &= ~INT_PENDING_MSK;85	writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG);86}87 88static void altera_mbox_tx_intmask(struct altera_mbox *mbox, bool enable)89{90	u32 mask;91 92	mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG);93	if (enable)94		mask |= INT_SPACE_MSK;95	else96		mask &= ~INT_SPACE_MSK;97	writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG);98}99 100static bool altera_mbox_is_sender(struct altera_mbox *mbox)101{102	u32 reg;103	/* Write a magic number to PTR register and read back this register.104	 * This register is read-write if it is a sender.105	 */106	#define MBOX_MAGIC	0xA5A5AA55107	writel_relaxed(MBOX_MAGIC, mbox->mbox_base + MAILBOX_PTR_REG);108	reg = readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG);109	if (reg == MBOX_MAGIC) {110		/* Clear to 0 */111		writel_relaxed(0, mbox->mbox_base + MAILBOX_PTR_REG);112		return true;113	}114	return false;115}116 117static void altera_mbox_rx_data(struct mbox_chan *chan)118{119	struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);120	u32 data[2];121 122	if (altera_mbox_pending(mbox)) {123		data[MBOX_PTR] =124			readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG);125		data[MBOX_CMD] =126			readl_relaxed(mbox->mbox_base + MAILBOX_CMD_REG);127		mbox_chan_received_data(chan, (void *)data);128	}129}130 131static void altera_mbox_poll_rx(struct timer_list *t)132{133	struct altera_mbox *mbox = from_timer(mbox, t, rxpoll_timer);134 135	altera_mbox_rx_data(mbox->chan);136 137	mod_timer(&mbox->rxpoll_timer,138		  jiffies + msecs_to_jiffies(MBOX_POLLING_MS));139}140 141static irqreturn_t altera_mbox_tx_interrupt(int irq, void *p)142{143	struct mbox_chan *chan = (struct mbox_chan *)p;144	struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);145 146	altera_mbox_tx_intmask(mbox, false);147	mbox_chan_txdone(chan, 0);148 149	return IRQ_HANDLED;150}151 152static irqreturn_t altera_mbox_rx_interrupt(int irq, void *p)153{154	struct mbox_chan *chan = (struct mbox_chan *)p;155 156	altera_mbox_rx_data(chan);157	return IRQ_HANDLED;158}159 160static int altera_mbox_startup_sender(struct mbox_chan *chan)161{162	int ret;163	struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);164 165	if (mbox->intr_mode) {166		ret = request_irq(mbox->irq, altera_mbox_tx_interrupt, 0,167				  DRIVER_NAME, chan);168		if (unlikely(ret)) {169			dev_err(mbox->dev,170				"failed to register mailbox interrupt:%d\n",171				ret);172			return ret;173		}174	}175 176	return 0;177}178 179static int altera_mbox_startup_receiver(struct mbox_chan *chan)180{181	int ret;182	struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);183 184	if (mbox->intr_mode) {185		ret = request_irq(mbox->irq, altera_mbox_rx_interrupt, 0,186				  DRIVER_NAME, chan);187		if (unlikely(ret)) {188			mbox->intr_mode = false;189			goto polling; /* use polling if failed */190		}191 192		altera_mbox_rx_intmask(mbox, true);193		return 0;194	}195 196polling:197	/* Setup polling timer */198	mbox->chan = chan;199	timer_setup(&mbox->rxpoll_timer, altera_mbox_poll_rx, 0);200	mod_timer(&mbox->rxpoll_timer,201		  jiffies + msecs_to_jiffies(MBOX_POLLING_MS));202 203	return 0;204}205 206static int altera_mbox_send_data(struct mbox_chan *chan, void *data)207{208	struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);209	u32 *udata = (u32 *)data;210 211	if (!mbox || !data)212		return -EINVAL;213	if (!mbox->is_sender) {214		dev_warn(mbox->dev,215			 "failed to send. This is receiver mailbox.\n");216		return -EINVAL;217	}218 219	if (altera_mbox_full(mbox))220		return -EBUSY;221 222	/* Enable interrupt before send */223	if (mbox->intr_mode)224		altera_mbox_tx_intmask(mbox, true);225 226	/* Pointer register must write before command register */227	writel_relaxed(udata[MBOX_PTR], mbox->mbox_base + MAILBOX_PTR_REG);228	writel_relaxed(udata[MBOX_CMD], mbox->mbox_base + MAILBOX_CMD_REG);229 230	return 0;231}232 233static bool altera_mbox_last_tx_done(struct mbox_chan *chan)234{235	struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);236 237	/* Return false if mailbox is full */238	return altera_mbox_full(mbox) ? false : true;239}240 241static bool altera_mbox_peek_data(struct mbox_chan *chan)242{243	struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);244 245	return altera_mbox_pending(mbox) ? true : false;246}247 248static int altera_mbox_startup(struct mbox_chan *chan)249{250	struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);251	int ret = 0;252 253	if (!mbox)254		return -EINVAL;255 256	if (mbox->is_sender)257		ret = altera_mbox_startup_sender(chan);258	else259		ret = altera_mbox_startup_receiver(chan);260 261	return ret;262}263 264static void altera_mbox_shutdown(struct mbox_chan *chan)265{266	struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);267 268	if (mbox->intr_mode) {269		/* Unmask all interrupt masks */270		writel_relaxed(~0, mbox->mbox_base + MAILBOX_INTMASK_REG);271		free_irq(mbox->irq, chan);272	} else if (!mbox->is_sender) {273		del_timer_sync(&mbox->rxpoll_timer);274	}275}276 277static const struct mbox_chan_ops altera_mbox_ops = {278	.send_data = altera_mbox_send_data,279	.startup = altera_mbox_startup,280	.shutdown = altera_mbox_shutdown,281	.last_tx_done = altera_mbox_last_tx_done,282	.peek_data = altera_mbox_peek_data,283};284 285static int altera_mbox_probe(struct platform_device *pdev)286{287	struct altera_mbox *mbox;288	struct mbox_chan *chans;289	int ret;290 291	mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox),292			    GFP_KERNEL);293	if (!mbox)294		return -ENOMEM;295 296	/* Allocated one channel */297	chans = devm_kzalloc(&pdev->dev, sizeof(*chans), GFP_KERNEL);298	if (!chans)299		return -ENOMEM;300 301	mbox->mbox_base = devm_platform_ioremap_resource(pdev, 0);302	if (IS_ERR(mbox->mbox_base))303		return PTR_ERR(mbox->mbox_base);304 305	/* Check is it a sender or receiver? */306	mbox->is_sender = altera_mbox_is_sender(mbox);307 308	mbox->irq = platform_get_irq(pdev, 0);309	if (mbox->irq >= 0)310		mbox->intr_mode = true;311 312	mbox->dev = &pdev->dev;313 314	/* Hardware supports only one channel. */315	chans[0].con_priv = mbox;316	mbox->controller.dev = mbox->dev;317	mbox->controller.num_chans = 1;318	mbox->controller.chans = chans;319	mbox->controller.ops = &altera_mbox_ops;320 321	if (mbox->is_sender) {322		if (mbox->intr_mode) {323			mbox->controller.txdone_irq = true;324		} else {325			mbox->controller.txdone_poll = true;326			mbox->controller.txpoll_period = MBOX_POLLING_MS;327		}328	}329 330	ret = devm_mbox_controller_register(&pdev->dev, &mbox->controller);331	if (ret) {332		dev_err(&pdev->dev, "Register mailbox failed\n");333		goto err;334	}335 336	platform_set_drvdata(pdev, mbox);337err:338	return ret;339}340 341static const struct of_device_id altera_mbox_match[] = {342	{ .compatible = "altr,mailbox-1.0" },343	{ /* Sentinel */ }344};345 346MODULE_DEVICE_TABLE(of, altera_mbox_match);347 348static struct platform_driver altera_mbox_driver = {349	.probe	= altera_mbox_probe,350	.driver	= {351		.name	= DRIVER_NAME,352		.of_match_table	= altera_mbox_match,353	},354};355 356module_platform_driver(altera_mbox_driver);357 358MODULE_LICENSE("GPL v2");359MODULE_DESCRIPTION("Altera mailbox specific functions");360MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");361MODULE_ALIAS("platform:altera-mailbox");362