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