1*f62092f6SLey Foon Tan /* 2*f62092f6SLey Foon Tan * Copyright Altera Corporation (C) 2013-2014. All rights reserved 3*f62092f6SLey Foon Tan * 4*f62092f6SLey Foon Tan * This program is free software; you can redistribute it and/or modify it 5*f62092f6SLey Foon Tan * under the terms and conditions of the GNU General Public License, 6*f62092f6SLey Foon Tan * version 2, as published by the Free Software Foundation. 7*f62092f6SLey Foon Tan * 8*f62092f6SLey Foon Tan * This program is distributed in the hope it will be useful, but WITHOUT 9*f62092f6SLey Foon Tan * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10*f62092f6SLey Foon Tan * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11*f62092f6SLey Foon Tan * more details. 12*f62092f6SLey Foon Tan * 13*f62092f6SLey Foon Tan * You should have received a copy of the GNU General Public License along with 14*f62092f6SLey Foon Tan * this program. If not, see <http://www.gnu.org/licenses/>. 15*f62092f6SLey Foon Tan */ 16*f62092f6SLey Foon Tan 17*f62092f6SLey Foon Tan #include <linux/device.h> 18*f62092f6SLey Foon Tan #include <linux/interrupt.h> 19*f62092f6SLey Foon Tan #include <linux/io.h> 20*f62092f6SLey Foon Tan #include <linux/kernel.h> 21*f62092f6SLey Foon Tan #include <linux/mailbox_controller.h> 22*f62092f6SLey Foon Tan #include <linux/module.h> 23*f62092f6SLey Foon Tan #include <linux/of.h> 24*f62092f6SLey Foon Tan #include <linux/platform_device.h> 25*f62092f6SLey Foon Tan 26*f62092f6SLey Foon Tan #define DRIVER_NAME "altera-mailbox" 27*f62092f6SLey Foon Tan 28*f62092f6SLey Foon Tan #define MAILBOX_CMD_REG 0x00 29*f62092f6SLey Foon Tan #define MAILBOX_PTR_REG 0x04 30*f62092f6SLey Foon Tan #define MAILBOX_STS_REG 0x08 31*f62092f6SLey Foon Tan #define MAILBOX_INTMASK_REG 0x0C 32*f62092f6SLey Foon Tan 33*f62092f6SLey Foon Tan #define INT_PENDING_MSK 0x1 34*f62092f6SLey Foon Tan #define INT_SPACE_MSK 0x2 35*f62092f6SLey Foon Tan 36*f62092f6SLey Foon Tan #define STS_PENDING_MSK 0x1 37*f62092f6SLey Foon Tan #define STS_FULL_MSK 0x2 38*f62092f6SLey Foon Tan #define STS_FULL_OFT 0x1 39*f62092f6SLey Foon Tan 40*f62092f6SLey Foon Tan #define MBOX_PENDING(status) (((status) & STS_PENDING_MSK)) 41*f62092f6SLey Foon Tan #define MBOX_FULL(status) (((status) & STS_FULL_MSK) >> STS_FULL_OFT) 42*f62092f6SLey Foon Tan 43*f62092f6SLey Foon Tan enum altera_mbox_msg { 44*f62092f6SLey Foon Tan MBOX_CMD = 0, 45*f62092f6SLey Foon Tan MBOX_PTR, 46*f62092f6SLey Foon Tan }; 47*f62092f6SLey Foon Tan 48*f62092f6SLey Foon Tan #define MBOX_POLLING_MS 5 /* polling interval 5ms */ 49*f62092f6SLey Foon Tan 50*f62092f6SLey Foon Tan struct altera_mbox { 51*f62092f6SLey Foon Tan bool is_sender; /* 1-sender, 0-receiver */ 52*f62092f6SLey Foon Tan bool intr_mode; 53*f62092f6SLey Foon Tan int irq; 54*f62092f6SLey Foon Tan void __iomem *mbox_base; 55*f62092f6SLey Foon Tan struct device *dev; 56*f62092f6SLey Foon Tan struct mbox_controller controller; 57*f62092f6SLey Foon Tan 58*f62092f6SLey Foon Tan /* If the controller supports only RX polling mode */ 59*f62092f6SLey Foon Tan struct timer_list rxpoll_timer; 60*f62092f6SLey Foon Tan }; 61*f62092f6SLey Foon Tan 62*f62092f6SLey Foon Tan static struct altera_mbox *mbox_chan_to_altera_mbox(struct mbox_chan *chan) 63*f62092f6SLey Foon Tan { 64*f62092f6SLey Foon Tan if (!chan || !chan->con_priv) 65*f62092f6SLey Foon Tan return NULL; 66*f62092f6SLey Foon Tan 67*f62092f6SLey Foon Tan return (struct altera_mbox *)chan->con_priv; 68*f62092f6SLey Foon Tan } 69*f62092f6SLey Foon Tan 70*f62092f6SLey Foon Tan static inline int altera_mbox_full(struct altera_mbox *mbox) 71*f62092f6SLey Foon Tan { 72*f62092f6SLey Foon Tan u32 status; 73*f62092f6SLey Foon Tan 74*f62092f6SLey Foon Tan status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG); 75*f62092f6SLey Foon Tan return MBOX_FULL(status); 76*f62092f6SLey Foon Tan } 77*f62092f6SLey Foon Tan 78*f62092f6SLey Foon Tan static inline int altera_mbox_pending(struct altera_mbox *mbox) 79*f62092f6SLey Foon Tan { 80*f62092f6SLey Foon Tan u32 status; 81*f62092f6SLey Foon Tan 82*f62092f6SLey Foon Tan status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG); 83*f62092f6SLey Foon Tan return MBOX_PENDING(status); 84*f62092f6SLey Foon Tan } 85*f62092f6SLey Foon Tan 86*f62092f6SLey Foon Tan static void altera_mbox_rx_intmask(struct altera_mbox *mbox, bool enable) 87*f62092f6SLey Foon Tan { 88*f62092f6SLey Foon Tan u32 mask; 89*f62092f6SLey Foon Tan 90*f62092f6SLey Foon Tan mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG); 91*f62092f6SLey Foon Tan if (enable) 92*f62092f6SLey Foon Tan mask |= INT_PENDING_MSK; 93*f62092f6SLey Foon Tan else 94*f62092f6SLey Foon Tan mask &= ~INT_PENDING_MSK; 95*f62092f6SLey Foon Tan writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG); 96*f62092f6SLey Foon Tan } 97*f62092f6SLey Foon Tan 98*f62092f6SLey Foon Tan static void altera_mbox_tx_intmask(struct altera_mbox *mbox, bool enable) 99*f62092f6SLey Foon Tan { 100*f62092f6SLey Foon Tan u32 mask; 101*f62092f6SLey Foon Tan 102*f62092f6SLey Foon Tan mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG); 103*f62092f6SLey Foon Tan if (enable) 104*f62092f6SLey Foon Tan mask |= INT_SPACE_MSK; 105*f62092f6SLey Foon Tan else 106*f62092f6SLey Foon Tan mask &= ~INT_SPACE_MSK; 107*f62092f6SLey Foon Tan writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG); 108*f62092f6SLey Foon Tan } 109*f62092f6SLey Foon Tan 110*f62092f6SLey Foon Tan static bool altera_mbox_is_sender(struct altera_mbox *mbox) 111*f62092f6SLey Foon Tan { 112*f62092f6SLey Foon Tan u32 reg; 113*f62092f6SLey Foon Tan /* Write a magic number to PTR register and read back this register. 114*f62092f6SLey Foon Tan * This register is read-write if it is a sender. 115*f62092f6SLey Foon Tan */ 116*f62092f6SLey Foon Tan #define MBOX_MAGIC 0xA5A5AA55 117*f62092f6SLey Foon Tan writel_relaxed(MBOX_MAGIC, mbox->mbox_base + MAILBOX_PTR_REG); 118*f62092f6SLey Foon Tan reg = readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG); 119*f62092f6SLey Foon Tan if (reg == MBOX_MAGIC) { 120*f62092f6SLey Foon Tan /* Clear to 0 */ 121*f62092f6SLey Foon Tan writel_relaxed(0, mbox->mbox_base + MAILBOX_PTR_REG); 122*f62092f6SLey Foon Tan return true; 123*f62092f6SLey Foon Tan } 124*f62092f6SLey Foon Tan return false; 125*f62092f6SLey Foon Tan } 126*f62092f6SLey Foon Tan 127*f62092f6SLey Foon Tan static void altera_mbox_rx_data(struct mbox_chan *chan) 128*f62092f6SLey Foon Tan { 129*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 130*f62092f6SLey Foon Tan u32 data[2]; 131*f62092f6SLey Foon Tan 132*f62092f6SLey Foon Tan if (altera_mbox_pending(mbox)) { 133*f62092f6SLey Foon Tan data[MBOX_PTR] = 134*f62092f6SLey Foon Tan readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG); 135*f62092f6SLey Foon Tan data[MBOX_CMD] = 136*f62092f6SLey Foon Tan readl_relaxed(mbox->mbox_base + MAILBOX_CMD_REG); 137*f62092f6SLey Foon Tan mbox_chan_received_data(chan, (void *)data); 138*f62092f6SLey Foon Tan } 139*f62092f6SLey Foon Tan } 140*f62092f6SLey Foon Tan 141*f62092f6SLey Foon Tan static void altera_mbox_poll_rx(unsigned long data) 142*f62092f6SLey Foon Tan { 143*f62092f6SLey Foon Tan struct mbox_chan *chan = (struct mbox_chan *)data; 144*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 145*f62092f6SLey Foon Tan 146*f62092f6SLey Foon Tan altera_mbox_rx_data(chan); 147*f62092f6SLey Foon Tan 148*f62092f6SLey Foon Tan mod_timer(&mbox->rxpoll_timer, 149*f62092f6SLey Foon Tan jiffies + msecs_to_jiffies(MBOX_POLLING_MS)); 150*f62092f6SLey Foon Tan } 151*f62092f6SLey Foon Tan 152*f62092f6SLey Foon Tan static irqreturn_t altera_mbox_tx_interrupt(int irq, void *p) 153*f62092f6SLey Foon Tan { 154*f62092f6SLey Foon Tan struct mbox_chan *chan = (struct mbox_chan *)p; 155*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 156*f62092f6SLey Foon Tan 157*f62092f6SLey Foon Tan altera_mbox_tx_intmask(mbox, false); 158*f62092f6SLey Foon Tan mbox_chan_txdone(chan, 0); 159*f62092f6SLey Foon Tan 160*f62092f6SLey Foon Tan return IRQ_HANDLED; 161*f62092f6SLey Foon Tan } 162*f62092f6SLey Foon Tan 163*f62092f6SLey Foon Tan static irqreturn_t altera_mbox_rx_interrupt(int irq, void *p) 164*f62092f6SLey Foon Tan { 165*f62092f6SLey Foon Tan struct mbox_chan *chan = (struct mbox_chan *)p; 166*f62092f6SLey Foon Tan 167*f62092f6SLey Foon Tan altera_mbox_rx_data(chan); 168*f62092f6SLey Foon Tan return IRQ_HANDLED; 169*f62092f6SLey Foon Tan } 170*f62092f6SLey Foon Tan 171*f62092f6SLey Foon Tan static int altera_mbox_startup_sender(struct mbox_chan *chan) 172*f62092f6SLey Foon Tan { 173*f62092f6SLey Foon Tan int ret; 174*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 175*f62092f6SLey Foon Tan 176*f62092f6SLey Foon Tan if (mbox->intr_mode) { 177*f62092f6SLey Foon Tan ret = request_irq(mbox->irq, altera_mbox_tx_interrupt, 0, 178*f62092f6SLey Foon Tan DRIVER_NAME, chan); 179*f62092f6SLey Foon Tan if (unlikely(ret)) { 180*f62092f6SLey Foon Tan dev_err(mbox->dev, 181*f62092f6SLey Foon Tan "failed to register mailbox interrupt:%d\n", 182*f62092f6SLey Foon Tan ret); 183*f62092f6SLey Foon Tan return ret; 184*f62092f6SLey Foon Tan } 185*f62092f6SLey Foon Tan } 186*f62092f6SLey Foon Tan 187*f62092f6SLey Foon Tan return 0; 188*f62092f6SLey Foon Tan } 189*f62092f6SLey Foon Tan 190*f62092f6SLey Foon Tan static int altera_mbox_startup_receiver(struct mbox_chan *chan) 191*f62092f6SLey Foon Tan { 192*f62092f6SLey Foon Tan int ret; 193*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 194*f62092f6SLey Foon Tan 195*f62092f6SLey Foon Tan if (mbox->intr_mode) { 196*f62092f6SLey Foon Tan ret = request_irq(mbox->irq, altera_mbox_rx_interrupt, 0, 197*f62092f6SLey Foon Tan DRIVER_NAME, chan); 198*f62092f6SLey Foon Tan if (unlikely(ret)) { 199*f62092f6SLey Foon Tan mbox->intr_mode = false; 200*f62092f6SLey Foon Tan goto polling; /* use polling if failed */ 201*f62092f6SLey Foon Tan } 202*f62092f6SLey Foon Tan 203*f62092f6SLey Foon Tan altera_mbox_rx_intmask(mbox, true); 204*f62092f6SLey Foon Tan return 0; 205*f62092f6SLey Foon Tan } 206*f62092f6SLey Foon Tan 207*f62092f6SLey Foon Tan polling: 208*f62092f6SLey Foon Tan /* Setup polling timer */ 209*f62092f6SLey Foon Tan setup_timer(&mbox->rxpoll_timer, altera_mbox_poll_rx, 210*f62092f6SLey Foon Tan (unsigned long)chan); 211*f62092f6SLey Foon Tan mod_timer(&mbox->rxpoll_timer, 212*f62092f6SLey Foon Tan jiffies + msecs_to_jiffies(MBOX_POLLING_MS)); 213*f62092f6SLey Foon Tan 214*f62092f6SLey Foon Tan return 0; 215*f62092f6SLey Foon Tan } 216*f62092f6SLey Foon Tan 217*f62092f6SLey Foon Tan static int altera_mbox_send_data(struct mbox_chan *chan, void *data) 218*f62092f6SLey Foon Tan { 219*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 220*f62092f6SLey Foon Tan u32 *udata = (u32 *)data; 221*f62092f6SLey Foon Tan 222*f62092f6SLey Foon Tan if (!mbox || !data) 223*f62092f6SLey Foon Tan return -EINVAL; 224*f62092f6SLey Foon Tan if (!mbox->is_sender) { 225*f62092f6SLey Foon Tan dev_warn(mbox->dev, 226*f62092f6SLey Foon Tan "failed to send. This is receiver mailbox.\n"); 227*f62092f6SLey Foon Tan return -EINVAL; 228*f62092f6SLey Foon Tan } 229*f62092f6SLey Foon Tan 230*f62092f6SLey Foon Tan if (altera_mbox_full(mbox)) 231*f62092f6SLey Foon Tan return -EBUSY; 232*f62092f6SLey Foon Tan 233*f62092f6SLey Foon Tan /* Enable interrupt before send */ 234*f62092f6SLey Foon Tan if (mbox->intr_mode) 235*f62092f6SLey Foon Tan altera_mbox_tx_intmask(mbox, true); 236*f62092f6SLey Foon Tan 237*f62092f6SLey Foon Tan /* Pointer register must write before command register */ 238*f62092f6SLey Foon Tan writel_relaxed(udata[MBOX_PTR], mbox->mbox_base + MAILBOX_PTR_REG); 239*f62092f6SLey Foon Tan writel_relaxed(udata[MBOX_CMD], mbox->mbox_base + MAILBOX_CMD_REG); 240*f62092f6SLey Foon Tan 241*f62092f6SLey Foon Tan return 0; 242*f62092f6SLey Foon Tan } 243*f62092f6SLey Foon Tan 244*f62092f6SLey Foon Tan static bool altera_mbox_last_tx_done(struct mbox_chan *chan) 245*f62092f6SLey Foon Tan { 246*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 247*f62092f6SLey Foon Tan 248*f62092f6SLey Foon Tan /* Return false if mailbox is full */ 249*f62092f6SLey Foon Tan return altera_mbox_full(mbox) ? false : true; 250*f62092f6SLey Foon Tan } 251*f62092f6SLey Foon Tan 252*f62092f6SLey Foon Tan static bool altera_mbox_peek_data(struct mbox_chan *chan) 253*f62092f6SLey Foon Tan { 254*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 255*f62092f6SLey Foon Tan 256*f62092f6SLey Foon Tan return altera_mbox_pending(mbox) ? true : false; 257*f62092f6SLey Foon Tan } 258*f62092f6SLey Foon Tan 259*f62092f6SLey Foon Tan static int altera_mbox_startup(struct mbox_chan *chan) 260*f62092f6SLey Foon Tan { 261*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 262*f62092f6SLey Foon Tan int ret = 0; 263*f62092f6SLey Foon Tan 264*f62092f6SLey Foon Tan if (!mbox) 265*f62092f6SLey Foon Tan return -EINVAL; 266*f62092f6SLey Foon Tan 267*f62092f6SLey Foon Tan if (mbox->is_sender) 268*f62092f6SLey Foon Tan ret = altera_mbox_startup_sender(chan); 269*f62092f6SLey Foon Tan else 270*f62092f6SLey Foon Tan ret = altera_mbox_startup_receiver(chan); 271*f62092f6SLey Foon Tan 272*f62092f6SLey Foon Tan return ret; 273*f62092f6SLey Foon Tan } 274*f62092f6SLey Foon Tan 275*f62092f6SLey Foon Tan static void altera_mbox_shutdown(struct mbox_chan *chan) 276*f62092f6SLey Foon Tan { 277*f62092f6SLey Foon Tan struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 278*f62092f6SLey Foon Tan 279*f62092f6SLey Foon Tan if (mbox->intr_mode) { 280*f62092f6SLey Foon Tan /* Unmask all interrupt masks */ 281*f62092f6SLey Foon Tan writel_relaxed(~0, mbox->mbox_base + MAILBOX_INTMASK_REG); 282*f62092f6SLey Foon Tan free_irq(mbox->irq, chan); 283*f62092f6SLey Foon Tan } else if (!mbox->is_sender) { 284*f62092f6SLey Foon Tan del_timer_sync(&mbox->rxpoll_timer); 285*f62092f6SLey Foon Tan } 286*f62092f6SLey Foon Tan } 287*f62092f6SLey Foon Tan 288*f62092f6SLey Foon Tan static struct mbox_chan_ops altera_mbox_ops = { 289*f62092f6SLey Foon Tan .send_data = altera_mbox_send_data, 290*f62092f6SLey Foon Tan .startup = altera_mbox_startup, 291*f62092f6SLey Foon Tan .shutdown = altera_mbox_shutdown, 292*f62092f6SLey Foon Tan .last_tx_done = altera_mbox_last_tx_done, 293*f62092f6SLey Foon Tan .peek_data = altera_mbox_peek_data, 294*f62092f6SLey Foon Tan }; 295*f62092f6SLey Foon Tan 296*f62092f6SLey Foon Tan static int altera_mbox_probe(struct platform_device *pdev) 297*f62092f6SLey Foon Tan { 298*f62092f6SLey Foon Tan struct altera_mbox *mbox; 299*f62092f6SLey Foon Tan struct resource *regs; 300*f62092f6SLey Foon Tan struct mbox_chan *chans; 301*f62092f6SLey Foon Tan int ret; 302*f62092f6SLey Foon Tan 303*f62092f6SLey Foon Tan mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), 304*f62092f6SLey Foon Tan GFP_KERNEL); 305*f62092f6SLey Foon Tan if (!mbox) 306*f62092f6SLey Foon Tan return -ENOMEM; 307*f62092f6SLey Foon Tan 308*f62092f6SLey Foon Tan /* Allocated one channel */ 309*f62092f6SLey Foon Tan chans = devm_kzalloc(&pdev->dev, sizeof(*chans), GFP_KERNEL); 310*f62092f6SLey Foon Tan if (!chans) 311*f62092f6SLey Foon Tan return -ENOMEM; 312*f62092f6SLey Foon Tan 313*f62092f6SLey Foon Tan regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 314*f62092f6SLey Foon Tan 315*f62092f6SLey Foon Tan mbox->mbox_base = devm_ioremap_resource(&pdev->dev, regs); 316*f62092f6SLey Foon Tan if (IS_ERR(mbox->mbox_base)) 317*f62092f6SLey Foon Tan return PTR_ERR(mbox->mbox_base); 318*f62092f6SLey Foon Tan 319*f62092f6SLey Foon Tan /* Check is it a sender or receiver? */ 320*f62092f6SLey Foon Tan mbox->is_sender = altera_mbox_is_sender(mbox); 321*f62092f6SLey Foon Tan 322*f62092f6SLey Foon Tan mbox->irq = platform_get_irq(pdev, 0); 323*f62092f6SLey Foon Tan if (mbox->irq >= 0) 324*f62092f6SLey Foon Tan mbox->intr_mode = true; 325*f62092f6SLey Foon Tan 326*f62092f6SLey Foon Tan mbox->dev = &pdev->dev; 327*f62092f6SLey Foon Tan 328*f62092f6SLey Foon Tan /* Hardware supports only one channel. */ 329*f62092f6SLey Foon Tan chans[0].con_priv = mbox; 330*f62092f6SLey Foon Tan mbox->controller.dev = mbox->dev; 331*f62092f6SLey Foon Tan mbox->controller.num_chans = 1; 332*f62092f6SLey Foon Tan mbox->controller.chans = chans; 333*f62092f6SLey Foon Tan mbox->controller.ops = &altera_mbox_ops; 334*f62092f6SLey Foon Tan 335*f62092f6SLey Foon Tan if (mbox->is_sender) { 336*f62092f6SLey Foon Tan if (mbox->intr_mode) { 337*f62092f6SLey Foon Tan mbox->controller.txdone_irq = true; 338*f62092f6SLey Foon Tan } else { 339*f62092f6SLey Foon Tan mbox->controller.txdone_poll = true; 340*f62092f6SLey Foon Tan mbox->controller.txpoll_period = MBOX_POLLING_MS; 341*f62092f6SLey Foon Tan } 342*f62092f6SLey Foon Tan } 343*f62092f6SLey Foon Tan 344*f62092f6SLey Foon Tan ret = mbox_controller_register(&mbox->controller); 345*f62092f6SLey Foon Tan if (ret) { 346*f62092f6SLey Foon Tan dev_err(&pdev->dev, "Register mailbox failed\n"); 347*f62092f6SLey Foon Tan goto err; 348*f62092f6SLey Foon Tan } 349*f62092f6SLey Foon Tan 350*f62092f6SLey Foon Tan platform_set_drvdata(pdev, mbox); 351*f62092f6SLey Foon Tan err: 352*f62092f6SLey Foon Tan return ret; 353*f62092f6SLey Foon Tan } 354*f62092f6SLey Foon Tan 355*f62092f6SLey Foon Tan static int altera_mbox_remove(struct platform_device *pdev) 356*f62092f6SLey Foon Tan { 357*f62092f6SLey Foon Tan struct altera_mbox *mbox = platform_get_drvdata(pdev); 358*f62092f6SLey Foon Tan 359*f62092f6SLey Foon Tan if (!mbox) 360*f62092f6SLey Foon Tan return -EINVAL; 361*f62092f6SLey Foon Tan 362*f62092f6SLey Foon Tan mbox_controller_unregister(&mbox->controller); 363*f62092f6SLey Foon Tan 364*f62092f6SLey Foon Tan return 0; 365*f62092f6SLey Foon Tan } 366*f62092f6SLey Foon Tan 367*f62092f6SLey Foon Tan static const struct of_device_id altera_mbox_match[] = { 368*f62092f6SLey Foon Tan { .compatible = "altr,mailbox-1.0" }, 369*f62092f6SLey Foon Tan { /* Sentinel */ } 370*f62092f6SLey Foon Tan }; 371*f62092f6SLey Foon Tan 372*f62092f6SLey Foon Tan MODULE_DEVICE_TABLE(of, altera_mbox_match); 373*f62092f6SLey Foon Tan 374*f62092f6SLey Foon Tan static struct platform_driver altera_mbox_driver = { 375*f62092f6SLey Foon Tan .probe = altera_mbox_probe, 376*f62092f6SLey Foon Tan .remove = altera_mbox_remove, 377*f62092f6SLey Foon Tan .driver = { 378*f62092f6SLey Foon Tan .name = DRIVER_NAME, 379*f62092f6SLey Foon Tan .of_match_table = altera_mbox_match, 380*f62092f6SLey Foon Tan }, 381*f62092f6SLey Foon Tan }; 382*f62092f6SLey Foon Tan 383*f62092f6SLey Foon Tan module_platform_driver(altera_mbox_driver); 384*f62092f6SLey Foon Tan 385*f62092f6SLey Foon Tan MODULE_LICENSE("GPL v2"); 386*f62092f6SLey Foon Tan MODULE_DESCRIPTION("Altera mailbox specific functions"); 387*f62092f6SLey Foon Tan MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>"); 388*f62092f6SLey Foon Tan MODULE_ALIAS("platform:altera-mailbox"); 389