brintos

brintos / linux-shallow public Read only

0
0
Text · 8.3 KiB · 27a510d Raw
352 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013-2015 Fujitsu Semiconductor Ltd.4 * Copyright (C) 2015 Linaro Ltd.5 * Based on ARM MHU driver by Jassi Brar <jaswinder.singh@linaro.org>6 * Copyright (C) 2020 ARM Ltd.7 */8 9#include <linux/amba/bus.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/interrupt.h>13#include <linux/io.h>14#include <linux/kernel.h>15#include <linux/mailbox_controller.h>16#include <linux/module.h>17#include <linux/of.h>18 19#define INTR_STAT_OFS	0x020#define INTR_SET_OFS	0x821#define INTR_CLR_OFS	0x1022 23#define MHU_LP_OFFSET	0x024#define MHU_HP_OFFSET	0x2025#define MHU_SEC_OFFSET	0x20026#define TX_REG_OFFSET	0x10027 28#define MHU_CHANS	3	/* Secure, Non-Secure High and Low Priority */29#define MHU_CHAN_MAX	20	/* Max channels to save on unused RAM */30#define MHU_NUM_DOORBELLS	3231 32struct mhu_db_link {33	unsigned int irq;34	void __iomem *tx_reg;35	void __iomem *rx_reg;36};37 38struct arm_mhu {39	void __iomem *base;40	struct mhu_db_link mlink[MHU_CHANS];41	struct mbox_controller mbox;42	struct device *dev;43};44 45/**46 * struct mhu_db_channel - ARM MHU Mailbox allocated channel information47 *48 * @mhu: Pointer to parent mailbox device49 * @pchan: Physical channel within which this doorbell resides in50 * @doorbell: doorbell number pertaining to this channel51 */52struct mhu_db_channel {53	struct arm_mhu *mhu;54	unsigned int pchan;55	unsigned int doorbell;56};57 58static inline struct mbox_chan *59mhu_db_mbox_to_channel(struct mbox_controller *mbox, unsigned int pchan,60		       unsigned int doorbell)61{62	int i;63	struct mhu_db_channel *chan_info;64 65	for (i = 0; i < mbox->num_chans; i++) {66		chan_info = mbox->chans[i].con_priv;67		if (chan_info && chan_info->pchan == pchan &&68		    chan_info->doorbell == doorbell)69			return &mbox->chans[i];70	}71 72	return NULL;73}74 75static void mhu_db_mbox_clear_irq(struct mbox_chan *chan)76{77	struct mhu_db_channel *chan_info = chan->con_priv;78	void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].rx_reg;79 80	writel_relaxed(BIT(chan_info->doorbell), base + INTR_CLR_OFS);81}82 83static unsigned int mhu_db_mbox_irq_to_pchan_num(struct arm_mhu *mhu, int irq)84{85	unsigned int pchan;86 87	for (pchan = 0; pchan < MHU_CHANS; pchan++)88		if (mhu->mlink[pchan].irq == irq)89			break;90	return pchan;91}92 93static struct mbox_chan *94mhu_db_mbox_irq_to_channel(struct arm_mhu *mhu, unsigned int pchan)95{96	unsigned long bits;97	unsigned int doorbell;98	struct mbox_chan *chan = NULL;99	struct mbox_controller *mbox = &mhu->mbox;100	void __iomem *base = mhu->mlink[pchan].rx_reg;101 102	bits = readl_relaxed(base + INTR_STAT_OFS);103	if (!bits)104		/* No IRQs fired in specified physical channel */105		return NULL;106 107	/* An IRQ has fired, find the associated channel */108	for (doorbell = 0; bits; doorbell++) {109		if (!test_and_clear_bit(doorbell, &bits))110			continue;111 112		chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell);113		if (chan)114			break;115		dev_err(mbox->dev,116			"Channel not registered: pchan: %d doorbell: %d\n",117			pchan, doorbell);118	}119 120	return chan;121}122 123static irqreturn_t mhu_db_mbox_rx_handler(int irq, void *data)124{125	struct mbox_chan *chan;126	struct arm_mhu *mhu = data;127	unsigned int pchan = mhu_db_mbox_irq_to_pchan_num(mhu, irq);128 129	while (NULL != (chan = mhu_db_mbox_irq_to_channel(mhu, pchan))) {130		mbox_chan_received_data(chan, NULL);131		mhu_db_mbox_clear_irq(chan);132	}133 134	return IRQ_HANDLED;135}136 137static bool mhu_db_last_tx_done(struct mbox_chan *chan)138{139	struct mhu_db_channel *chan_info = chan->con_priv;140	void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg;141 142	if (readl_relaxed(base + INTR_STAT_OFS) & BIT(chan_info->doorbell))143		return false;144 145	return true;146}147 148static int mhu_db_send_data(struct mbox_chan *chan, void *data)149{150	struct mhu_db_channel *chan_info = chan->con_priv;151	void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg;152 153	/* Send event to co-processor */154	writel_relaxed(BIT(chan_info->doorbell), base + INTR_SET_OFS);155 156	return 0;157}158 159static int mhu_db_startup(struct mbox_chan *chan)160{161	mhu_db_mbox_clear_irq(chan);162	return 0;163}164 165static void mhu_db_shutdown(struct mbox_chan *chan)166{167	struct mhu_db_channel *chan_info = chan->con_priv;168	struct mbox_controller *mbox = &chan_info->mhu->mbox;169	int i;170 171	for (i = 0; i < mbox->num_chans; i++)172		if (chan == &mbox->chans[i])173			break;174 175	if (mbox->num_chans == i) {176		dev_warn(mbox->dev, "Request to free non-existent channel\n");177		return;178	}179 180	/* Reset channel */181	mhu_db_mbox_clear_irq(chan);182	devm_kfree(mbox->dev, chan->con_priv);183	chan->con_priv = NULL;184}185 186static struct mbox_chan *mhu_db_mbox_xlate(struct mbox_controller *mbox,187					   const struct of_phandle_args *spec)188{189	struct arm_mhu *mhu = dev_get_drvdata(mbox->dev);190	struct mhu_db_channel *chan_info;191	struct mbox_chan *chan;192	unsigned int pchan = spec->args[0];193	unsigned int doorbell = spec->args[1];194	int i;195 196	/* Bounds checking */197	if (pchan >= MHU_CHANS || doorbell >= MHU_NUM_DOORBELLS) {198		dev_err(mbox->dev,199			"Invalid channel requested pchan: %d doorbell: %d\n",200			pchan, doorbell);201		return ERR_PTR(-EINVAL);202	}203 204	/* Is requested channel free? */205	chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell);206	if (chan) {207		dev_err(mbox->dev, "Channel in use: pchan: %d doorbell: %d\n",208			pchan, doorbell);209		return ERR_PTR(-EBUSY);210	}211 212	/* Find the first free slot */213	for (i = 0; i < mbox->num_chans; i++)214		if (!mbox->chans[i].con_priv)215			break;216 217	if (mbox->num_chans == i) {218		dev_err(mbox->dev, "No free channels left\n");219		return ERR_PTR(-EBUSY);220	}221 222	chan = &mbox->chans[i];223 224	chan_info = devm_kzalloc(mbox->dev, sizeof(*chan_info), GFP_KERNEL);225	if (!chan_info)226		return ERR_PTR(-ENOMEM);227 228	chan_info->mhu = mhu;229	chan_info->pchan = pchan;230	chan_info->doorbell = doorbell;231 232	chan->con_priv = chan_info;233 234	dev_dbg(mbox->dev, "mbox: created channel phys: %d doorbell: %d\n",235		pchan, doorbell);236 237	return chan;238}239 240static const struct mbox_chan_ops mhu_db_ops = {241	.send_data = mhu_db_send_data,242	.startup = mhu_db_startup,243	.shutdown = mhu_db_shutdown,244	.last_tx_done = mhu_db_last_tx_done,245};246 247static int mhu_db_probe(struct amba_device *adev, const struct amba_id *id)248{249	u32 cell_count;250	int i, err, max_chans;251	struct arm_mhu *mhu;252	struct mbox_chan *chans;253	struct device *dev = &adev->dev;254	struct device_node *np = dev->of_node;255	int mhu_reg[MHU_CHANS] = {256		MHU_LP_OFFSET, MHU_HP_OFFSET, MHU_SEC_OFFSET,257	};258 259	if (!of_device_is_compatible(np, "arm,mhu-doorbell"))260		return -ENODEV;261 262	err = of_property_read_u32(np, "#mbox-cells", &cell_count);263	if (err) {264		dev_err(dev, "failed to read #mbox-cells in '%pOF'\n", np);265		return err;266	}267 268	if (cell_count == 2) {269		max_chans = MHU_CHAN_MAX;270	} else {271		dev_err(dev, "incorrect value of #mbox-cells in '%pOF'\n", np);272		return -EINVAL;273	}274 275	mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);276	if (!mhu)277		return -ENOMEM;278 279	mhu->base = devm_ioremap_resource(dev, &adev->res);280	if (IS_ERR(mhu->base))281		return PTR_ERR(mhu->base);282 283	chans = devm_kcalloc(dev, max_chans, sizeof(*chans), GFP_KERNEL);284	if (!chans)285		return -ENOMEM;286 287	mhu->dev = dev;288	mhu->mbox.dev = dev;289	mhu->mbox.chans = chans;290	mhu->mbox.num_chans = max_chans;291	mhu->mbox.txdone_irq = false;292	mhu->mbox.txdone_poll = true;293	mhu->mbox.txpoll_period = 1;294 295	mhu->mbox.of_xlate = mhu_db_mbox_xlate;296	amba_set_drvdata(adev, mhu);297 298	mhu->mbox.ops = &mhu_db_ops;299 300	err = devm_mbox_controller_register(dev, &mhu->mbox);301	if (err) {302		dev_err(dev, "Failed to register mailboxes %d\n", err);303		return err;304	}305 306	for (i = 0; i < MHU_CHANS; i++) {307		int irq = mhu->mlink[i].irq = adev->irq[i];308 309		if (irq <= 0) {310			dev_dbg(dev, "No IRQ found for Channel %d\n", i);311			continue;312		}313 314		mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];315		mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET;316 317		err = devm_request_threaded_irq(dev, irq, NULL,318						mhu_db_mbox_rx_handler,319						IRQF_ONESHOT, "mhu_db_link", mhu);320		if (err) {321			dev_err(dev, "Can't claim IRQ %d\n", irq);322			mbox_controller_unregister(&mhu->mbox);323			return err;324		}325	}326 327	dev_info(dev, "ARM MHU Doorbell mailbox registered\n");328	return 0;329}330 331static struct amba_id mhu_ids[] = {332	{333		.id	= 0x1bb098,334		.mask	= 0xffffff,335	},336	{ 0, 0 },337};338MODULE_DEVICE_TABLE(amba, mhu_ids);339 340static struct amba_driver arm_mhu_db_driver = {341	.drv = {342		.name	= "mhu-doorbell",343	},344	.id_table	= mhu_ids,345	.probe		= mhu_db_probe,346};347module_amba_driver(arm_mhu_db_driver);348 349MODULE_LICENSE("GPL v2");350MODULE_DESCRIPTION("ARM MHU Doorbell Driver");351MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");352