261 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * APM X-Gene SLIMpro MailBox Driver4 *5 * Copyright (c) 2015, Applied Micro Circuits Corporation6 * Author: Feng Kan fkan@apm.com7 */8#include <linux/acpi.h>9#include <linux/delay.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/spinlock.h>17 18#define MBOX_CON_NAME "slimpro-mbox"19#define MBOX_REG_SET_OFFSET 0x100020#define MBOX_CNT 821#define MBOX_STATUS_AVAIL_MASK BIT(16)22#define MBOX_STATUS_ACK_MASK BIT(0)23 24/* Configuration and Status Registers */25#define REG_DB_IN 0x0026#define REG_DB_DIN0 0x0427#define REG_DB_DIN1 0x0828#define REG_DB_OUT 0x1029#define REG_DB_DOUT0 0x1430#define REG_DB_DOUT1 0x1831#define REG_DB_STAT 0x2032#define REG_DB_STATMASK 0x2433 34/**35 * X-Gene SlimPRO mailbox channel information36 *37 * @dev: Device to which it is attached38 * @chan: Pointer to mailbox communication channel39 * @reg: Base address to access channel registers40 * @irq: Interrupt number of the channel41 * @rx_msg: Received message storage42 */43struct slimpro_mbox_chan {44 struct device *dev;45 struct mbox_chan *chan;46 void __iomem *reg;47 int irq;48 u32 rx_msg[3];49};50 51/**52 * X-Gene SlimPRO Mailbox controller data53 *54 * X-Gene SlimPRO Mailbox controller has 8 communication channels.55 * Each channel has a separate IRQ number assigned to it.56 *57 * @mb_ctrl: Representation of the communication channel controller58 * @mc: Array of SlimPRO mailbox channels of the controller59 * @chans: Array of mailbox communication channels60 *61 */62struct slimpro_mbox {63 struct mbox_controller mb_ctrl;64 struct slimpro_mbox_chan mc[MBOX_CNT];65 struct mbox_chan chans[MBOX_CNT];66};67 68static void mb_chan_send_msg(struct slimpro_mbox_chan *mb_chan, u32 *msg)69{70 writel(msg[1], mb_chan->reg + REG_DB_DOUT0);71 writel(msg[2], mb_chan->reg + REG_DB_DOUT1);72 writel(msg[0], mb_chan->reg + REG_DB_OUT);73}74 75static void mb_chan_recv_msg(struct slimpro_mbox_chan *mb_chan)76{77 mb_chan->rx_msg[1] = readl(mb_chan->reg + REG_DB_DIN0);78 mb_chan->rx_msg[2] = readl(mb_chan->reg + REG_DB_DIN1);79 mb_chan->rx_msg[0] = readl(mb_chan->reg + REG_DB_IN);80}81 82static int mb_chan_status_ack(struct slimpro_mbox_chan *mb_chan)83{84 u32 val = readl(mb_chan->reg + REG_DB_STAT);85 86 if (val & MBOX_STATUS_ACK_MASK) {87 writel(MBOX_STATUS_ACK_MASK, mb_chan->reg + REG_DB_STAT);88 return 1;89 }90 return 0;91}92 93static int mb_chan_status_avail(struct slimpro_mbox_chan *mb_chan)94{95 u32 val = readl(mb_chan->reg + REG_DB_STAT);96 97 if (val & MBOX_STATUS_AVAIL_MASK) {98 mb_chan_recv_msg(mb_chan);99 writel(MBOX_STATUS_AVAIL_MASK, mb_chan->reg + REG_DB_STAT);100 return 1;101 }102 return 0;103}104 105static irqreturn_t slimpro_mbox_irq(int irq, void *id)106{107 struct slimpro_mbox_chan *mb_chan = id;108 109 if (mb_chan_status_ack(mb_chan))110 mbox_chan_txdone(mb_chan->chan, 0);111 112 if (mb_chan_status_avail(mb_chan))113 mbox_chan_received_data(mb_chan->chan, mb_chan->rx_msg);114 115 return IRQ_HANDLED;116}117 118static int slimpro_mbox_send_data(struct mbox_chan *chan, void *msg)119{120 struct slimpro_mbox_chan *mb_chan = chan->con_priv;121 122 mb_chan_send_msg(mb_chan, msg);123 return 0;124}125 126static int slimpro_mbox_startup(struct mbox_chan *chan)127{128 struct slimpro_mbox_chan *mb_chan = chan->con_priv;129 int rc;130 u32 val;131 132 rc = devm_request_irq(mb_chan->dev, mb_chan->irq, slimpro_mbox_irq, 0,133 MBOX_CON_NAME, mb_chan);134 if (unlikely(rc)) {135 dev_err(mb_chan->dev, "failed to register mailbox interrupt %d\n",136 mb_chan->irq);137 return rc;138 }139 140 /* Enable HW interrupt */141 writel(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK,142 mb_chan->reg + REG_DB_STAT);143 /* Unmask doorbell status interrupt */144 val = readl(mb_chan->reg + REG_DB_STATMASK);145 val &= ~(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK);146 writel(val, mb_chan->reg + REG_DB_STATMASK);147 148 return 0;149}150 151static void slimpro_mbox_shutdown(struct mbox_chan *chan)152{153 struct slimpro_mbox_chan *mb_chan = chan->con_priv;154 u32 val;155 156 /* Mask doorbell status interrupt */157 val = readl(mb_chan->reg + REG_DB_STATMASK);158 val |= (MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK);159 writel(val, mb_chan->reg + REG_DB_STATMASK);160 161 devm_free_irq(mb_chan->dev, mb_chan->irq, mb_chan);162}163 164static const struct mbox_chan_ops slimpro_mbox_ops = {165 .send_data = slimpro_mbox_send_data,166 .startup = slimpro_mbox_startup,167 .shutdown = slimpro_mbox_shutdown,168};169 170static int slimpro_mbox_probe(struct platform_device *pdev)171{172 struct slimpro_mbox *ctx;173 void __iomem *mb_base;174 int rc;175 int i;176 177 ctx = devm_kzalloc(&pdev->dev, sizeof(struct slimpro_mbox), GFP_KERNEL);178 if (!ctx)179 return -ENOMEM;180 181 platform_set_drvdata(pdev, ctx);182 183 mb_base = devm_platform_ioremap_resource(pdev, 0);184 if (IS_ERR(mb_base))185 return PTR_ERR(mb_base);186 187 /* Setup mailbox links */188 for (i = 0; i < MBOX_CNT; i++) {189 ctx->mc[i].irq = platform_get_irq(pdev, i);190 if (ctx->mc[i].irq < 0) {191 if (i == 0) {192 dev_err(&pdev->dev, "no available IRQ\n");193 return -EINVAL;194 }195 dev_info(&pdev->dev, "no IRQ for channel %d\n", i);196 break;197 }198 199 ctx->mc[i].dev = &pdev->dev;200 ctx->mc[i].reg = mb_base + i * MBOX_REG_SET_OFFSET;201 ctx->mc[i].chan = &ctx->chans[i];202 ctx->chans[i].con_priv = &ctx->mc[i];203 }204 205 /* Setup mailbox controller */206 ctx->mb_ctrl.dev = &pdev->dev;207 ctx->mb_ctrl.chans = ctx->chans;208 ctx->mb_ctrl.txdone_irq = true;209 ctx->mb_ctrl.ops = &slimpro_mbox_ops;210 ctx->mb_ctrl.num_chans = i;211 212 rc = devm_mbox_controller_register(&pdev->dev, &ctx->mb_ctrl);213 if (rc) {214 dev_err(&pdev->dev,215 "APM X-Gene SLIMpro MailBox register failed:%d\n", rc);216 return rc;217 }218 219 dev_info(&pdev->dev, "APM X-Gene SLIMpro MailBox registered\n");220 return 0;221}222 223static const struct of_device_id slimpro_of_match[] = {224 {.compatible = "apm,xgene-slimpro-mbox" },225 { },226};227MODULE_DEVICE_TABLE(of, slimpro_of_match);228 229#ifdef CONFIG_ACPI230static const struct acpi_device_id slimpro_acpi_ids[] = {231 {"APMC0D01", 0},232 {}233};234MODULE_DEVICE_TABLE(acpi, slimpro_acpi_ids);235#endif236 237static struct platform_driver slimpro_mbox_driver = {238 .probe = slimpro_mbox_probe,239 .driver = {240 .name = "xgene-slimpro-mbox",241 .of_match_table = of_match_ptr(slimpro_of_match),242 .acpi_match_table = ACPI_PTR(slimpro_acpi_ids)243 },244};245 246static int __init slimpro_mbox_init(void)247{248 return platform_driver_register(&slimpro_mbox_driver);249}250 251static void __exit slimpro_mbox_exit(void)252{253 platform_driver_unregister(&slimpro_mbox_driver);254}255 256subsys_initcall(slimpro_mbox_init);257module_exit(slimpro_mbox_exit);258 259MODULE_DESCRIPTION("APM X-Gene SLIMpro Mailbox Driver");260MODULE_LICENSE("GPL");261