1f700e84fSDuc Dang /* 2f700e84fSDuc Dang * APM X-Gene SLIMpro MailBox Driver 3f700e84fSDuc Dang * 4f700e84fSDuc Dang * Copyright (c) 2015, Applied Micro Circuits Corporation 5f700e84fSDuc Dang * Author: Feng Kan fkan@apm.com 6f700e84fSDuc Dang * 7f700e84fSDuc Dang * This program is free software; you can redistribute it and/or 8f700e84fSDuc Dang * modify it under the terms of the GNU General Public License as 9f700e84fSDuc Dang * published by the Free Software Foundation; either version 2 of 10f700e84fSDuc Dang * the License, or (at your option) any later version. 11f700e84fSDuc Dang * 12f700e84fSDuc Dang * This program is distributed in the hope that it will be useful, 13f700e84fSDuc Dang * but WITHOUT ANY WARRANTY; without even the implied warranty of 14f700e84fSDuc Dang * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15f700e84fSDuc Dang * GNU General Public License for more details. 16f700e84fSDuc Dang * 17f700e84fSDuc Dang * You should have received a copy of the GNU General Public License 18f700e84fSDuc Dang * along with this program; if not, see <http://www.gnu.org/licenses/>. 19f700e84fSDuc Dang * 20f700e84fSDuc Dang */ 21f700e84fSDuc Dang #include <linux/acpi.h> 22f700e84fSDuc Dang #include <linux/delay.h> 23f700e84fSDuc Dang #include <linux/interrupt.h> 24f700e84fSDuc Dang #include <linux/io.h> 25f700e84fSDuc Dang #include <linux/mailbox_controller.h> 26f700e84fSDuc Dang #include <linux/module.h> 27f700e84fSDuc Dang #include <linux/of.h> 28f700e84fSDuc Dang #include <linux/platform_device.h> 29f700e84fSDuc Dang #include <linux/spinlock.h> 30f700e84fSDuc Dang 31f700e84fSDuc Dang #define MBOX_CON_NAME "slimpro-mbox" 32f700e84fSDuc Dang #define MBOX_REG_SET_OFFSET 0x1000 33f700e84fSDuc Dang #define MBOX_CNT 8 34f700e84fSDuc Dang #define MBOX_STATUS_AVAIL_MASK BIT(16) 35f700e84fSDuc Dang #define MBOX_STATUS_ACK_MASK BIT(0) 36f700e84fSDuc Dang 37f700e84fSDuc Dang /* Configuration and Status Registers */ 38f700e84fSDuc Dang #define REG_DB_IN 0x00 39f700e84fSDuc Dang #define REG_DB_DIN0 0x04 40f700e84fSDuc Dang #define REG_DB_DIN1 0x08 41f700e84fSDuc Dang #define REG_DB_OUT 0x10 42f700e84fSDuc Dang #define REG_DB_DOUT0 0x14 43f700e84fSDuc Dang #define REG_DB_DOUT1 0x18 44f700e84fSDuc Dang #define REG_DB_STAT 0x20 45f700e84fSDuc Dang #define REG_DB_STATMASK 0x24 46f700e84fSDuc Dang 47f700e84fSDuc Dang /** 48f700e84fSDuc Dang * X-Gene SlimPRO mailbox channel information 49f700e84fSDuc Dang * 50f700e84fSDuc Dang * @dev: Device to which it is attached 51f700e84fSDuc Dang * @chan: Pointer to mailbox communication channel 52f700e84fSDuc Dang * @reg: Base address to access channel registers 53f700e84fSDuc Dang * @irq: Interrupt number of the channel 54f700e84fSDuc Dang * @rx_msg: Received message storage 55f700e84fSDuc Dang */ 56f700e84fSDuc Dang struct slimpro_mbox_chan { 57f700e84fSDuc Dang struct device *dev; 58f700e84fSDuc Dang struct mbox_chan *chan; 59f700e84fSDuc Dang void __iomem *reg; 60f700e84fSDuc Dang int irq; 61f700e84fSDuc Dang u32 rx_msg[3]; 62f700e84fSDuc Dang }; 63f700e84fSDuc Dang 64f700e84fSDuc Dang /** 65f700e84fSDuc Dang * X-Gene SlimPRO Mailbox controller data 66f700e84fSDuc Dang * 67f700e84fSDuc Dang * X-Gene SlimPRO Mailbox controller has 8 commnunication channels. 68f700e84fSDuc Dang * Each channel has a separate IRQ number assgined to it. 69f700e84fSDuc Dang * 70f700e84fSDuc Dang * @mb_ctrl: Representation of the commnunication channel controller 71f700e84fSDuc Dang * @mc: Array of SlimPRO mailbox channels of the controller 72f700e84fSDuc Dang * @chans: Array of mailbox communication channels 73f700e84fSDuc Dang * 74f700e84fSDuc Dang */ 75f700e84fSDuc Dang struct slimpro_mbox { 76f700e84fSDuc Dang struct mbox_controller mb_ctrl; 77f700e84fSDuc Dang struct slimpro_mbox_chan mc[MBOX_CNT]; 78f700e84fSDuc Dang struct mbox_chan chans[MBOX_CNT]; 79f700e84fSDuc Dang }; 80f700e84fSDuc Dang 81f700e84fSDuc Dang static void mb_chan_send_msg(struct slimpro_mbox_chan *mb_chan, u32 *msg) 82f700e84fSDuc Dang { 83f700e84fSDuc Dang writel(msg[1], mb_chan->reg + REG_DB_DOUT0); 84f700e84fSDuc Dang writel(msg[2], mb_chan->reg + REG_DB_DOUT1); 85f700e84fSDuc Dang writel(msg[0], mb_chan->reg + REG_DB_OUT); 86f700e84fSDuc Dang } 87f700e84fSDuc Dang 88f700e84fSDuc Dang static void mb_chan_recv_msg(struct slimpro_mbox_chan *mb_chan) 89f700e84fSDuc Dang { 90f700e84fSDuc Dang mb_chan->rx_msg[1] = readl(mb_chan->reg + REG_DB_DIN0); 91f700e84fSDuc Dang mb_chan->rx_msg[2] = readl(mb_chan->reg + REG_DB_DIN1); 92f700e84fSDuc Dang mb_chan->rx_msg[0] = readl(mb_chan->reg + REG_DB_IN); 93f700e84fSDuc Dang } 94f700e84fSDuc Dang 95f700e84fSDuc Dang static int mb_chan_status_ack(struct slimpro_mbox_chan *mb_chan) 96f700e84fSDuc Dang { 97f700e84fSDuc Dang u32 val = readl(mb_chan->reg + REG_DB_STAT); 98f700e84fSDuc Dang 99f700e84fSDuc Dang if (val & MBOX_STATUS_ACK_MASK) { 100f700e84fSDuc Dang writel(MBOX_STATUS_ACK_MASK, mb_chan->reg + REG_DB_STAT); 101f700e84fSDuc Dang return 1; 102f700e84fSDuc Dang } 103f700e84fSDuc Dang return 0; 104f700e84fSDuc Dang } 105f700e84fSDuc Dang 106f700e84fSDuc Dang static int mb_chan_status_avail(struct slimpro_mbox_chan *mb_chan) 107f700e84fSDuc Dang { 108f700e84fSDuc Dang u32 val = readl(mb_chan->reg + REG_DB_STAT); 109f700e84fSDuc Dang 110f700e84fSDuc Dang if (val & MBOX_STATUS_AVAIL_MASK) { 111f700e84fSDuc Dang mb_chan_recv_msg(mb_chan); 112f700e84fSDuc Dang writel(MBOX_STATUS_AVAIL_MASK, mb_chan->reg + REG_DB_STAT); 113f700e84fSDuc Dang return 1; 114f700e84fSDuc Dang } 115f700e84fSDuc Dang return 0; 116f700e84fSDuc Dang } 117f700e84fSDuc Dang 118f700e84fSDuc Dang static irqreturn_t slimpro_mbox_irq(int irq, void *id) 119f700e84fSDuc Dang { 120f700e84fSDuc Dang struct slimpro_mbox_chan *mb_chan = id; 121f700e84fSDuc Dang 122f700e84fSDuc Dang if (mb_chan_status_ack(mb_chan)) 123f700e84fSDuc Dang mbox_chan_txdone(mb_chan->chan, 0); 124f700e84fSDuc Dang 125f700e84fSDuc Dang if (mb_chan_status_avail(mb_chan)) 126f700e84fSDuc Dang mbox_chan_received_data(mb_chan->chan, mb_chan->rx_msg); 127f700e84fSDuc Dang 128f700e84fSDuc Dang return IRQ_HANDLED; 129f700e84fSDuc Dang } 130f700e84fSDuc Dang 131f700e84fSDuc Dang static int slimpro_mbox_send_data(struct mbox_chan *chan, void *msg) 132f700e84fSDuc Dang { 133f700e84fSDuc Dang struct slimpro_mbox_chan *mb_chan = chan->con_priv; 134f700e84fSDuc Dang 135f700e84fSDuc Dang mb_chan_send_msg(mb_chan, msg); 136f700e84fSDuc Dang return 0; 137f700e84fSDuc Dang } 138f700e84fSDuc Dang 139f700e84fSDuc Dang static int slimpro_mbox_startup(struct mbox_chan *chan) 140f700e84fSDuc Dang { 141f700e84fSDuc Dang struct slimpro_mbox_chan *mb_chan = chan->con_priv; 142f700e84fSDuc Dang int rc; 143f700e84fSDuc Dang u32 val; 144f700e84fSDuc Dang 145f700e84fSDuc Dang rc = devm_request_irq(mb_chan->dev, mb_chan->irq, slimpro_mbox_irq, 0, 146f700e84fSDuc Dang MBOX_CON_NAME, mb_chan); 147f700e84fSDuc Dang if (unlikely(rc)) { 148f700e84fSDuc Dang dev_err(mb_chan->dev, "failed to register mailbox interrupt %d\n", 149f700e84fSDuc Dang mb_chan->irq); 150f700e84fSDuc Dang return rc; 151f700e84fSDuc Dang } 152f700e84fSDuc Dang 153f700e84fSDuc Dang /* Enable HW interrupt */ 154f700e84fSDuc Dang writel(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK, 155f700e84fSDuc Dang mb_chan->reg + REG_DB_STAT); 156f700e84fSDuc Dang /* Unmask doorbell status interrupt */ 157f700e84fSDuc Dang val = readl(mb_chan->reg + REG_DB_STATMASK); 158f700e84fSDuc Dang val &= ~(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK); 159f700e84fSDuc Dang writel(val, mb_chan->reg + REG_DB_STATMASK); 160f700e84fSDuc Dang 161f700e84fSDuc Dang return 0; 162f700e84fSDuc Dang } 163f700e84fSDuc Dang 164f700e84fSDuc Dang static void slimpro_mbox_shutdown(struct mbox_chan *chan) 165f700e84fSDuc Dang { 166f700e84fSDuc Dang struct slimpro_mbox_chan *mb_chan = chan->con_priv; 167f700e84fSDuc Dang u32 val; 168f700e84fSDuc Dang 169f700e84fSDuc Dang /* Mask doorbell status interrupt */ 170f700e84fSDuc Dang val = readl(mb_chan->reg + REG_DB_STATMASK); 171f700e84fSDuc Dang val |= (MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK); 172f700e84fSDuc Dang writel(val, mb_chan->reg + REG_DB_STATMASK); 173f700e84fSDuc Dang 174f700e84fSDuc Dang devm_free_irq(mb_chan->dev, mb_chan->irq, mb_chan); 175f700e84fSDuc Dang } 176f700e84fSDuc Dang 1778ce33c6fSBhumika Goyal static const struct mbox_chan_ops slimpro_mbox_ops = { 178f700e84fSDuc Dang .send_data = slimpro_mbox_send_data, 179f700e84fSDuc Dang .startup = slimpro_mbox_startup, 180f700e84fSDuc Dang .shutdown = slimpro_mbox_shutdown, 181f700e84fSDuc Dang }; 182f700e84fSDuc Dang 183f700e84fSDuc Dang static int slimpro_mbox_probe(struct platform_device *pdev) 184f700e84fSDuc Dang { 185f700e84fSDuc Dang struct slimpro_mbox *ctx; 186f700e84fSDuc Dang struct resource *regs; 187f700e84fSDuc Dang void __iomem *mb_base; 188f700e84fSDuc Dang int rc; 189f700e84fSDuc Dang int i; 190f700e84fSDuc Dang 191f700e84fSDuc Dang ctx = devm_kzalloc(&pdev->dev, sizeof(struct slimpro_mbox), GFP_KERNEL); 192a61b37eaSAxel Lin if (!ctx) 193a61b37eaSAxel Lin return -ENOMEM; 194f700e84fSDuc Dang 195f700e84fSDuc Dang platform_set_drvdata(pdev, ctx); 196f700e84fSDuc Dang 197f700e84fSDuc Dang regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 198f700e84fSDuc Dang mb_base = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); 19914d653afSDan Carpenter if (!mb_base) 20014d653afSDan Carpenter return -ENOMEM; 201f700e84fSDuc Dang 202f700e84fSDuc Dang /* Setup mailbox links */ 203f700e84fSDuc Dang for (i = 0; i < MBOX_CNT; i++) { 204f700e84fSDuc Dang ctx->mc[i].irq = platform_get_irq(pdev, i); 205f700e84fSDuc Dang if (ctx->mc[i].irq < 0) { 206f700e84fSDuc Dang if (i == 0) { 207f700e84fSDuc Dang dev_err(&pdev->dev, "no available IRQ\n"); 208f700e84fSDuc Dang return -EINVAL; 209f700e84fSDuc Dang } 210f700e84fSDuc Dang dev_info(&pdev->dev, "no IRQ for channel %d\n", i); 211f700e84fSDuc Dang break; 212f700e84fSDuc Dang } 213f700e84fSDuc Dang 214f700e84fSDuc Dang ctx->mc[i].dev = &pdev->dev; 215f700e84fSDuc Dang ctx->mc[i].reg = mb_base + i * MBOX_REG_SET_OFFSET; 216f700e84fSDuc Dang ctx->mc[i].chan = &ctx->chans[i]; 217f700e84fSDuc Dang ctx->chans[i].con_priv = &ctx->mc[i]; 218f700e84fSDuc Dang } 219f700e84fSDuc Dang 220f700e84fSDuc Dang /* Setup mailbox controller */ 221f700e84fSDuc Dang ctx->mb_ctrl.dev = &pdev->dev; 222f700e84fSDuc Dang ctx->mb_ctrl.chans = ctx->chans; 223f700e84fSDuc Dang ctx->mb_ctrl.txdone_irq = true; 224f700e84fSDuc Dang ctx->mb_ctrl.ops = &slimpro_mbox_ops; 225f700e84fSDuc Dang ctx->mb_ctrl.num_chans = i; 226f700e84fSDuc Dang 227f700e84fSDuc Dang rc = mbox_controller_register(&ctx->mb_ctrl); 228f700e84fSDuc Dang if (rc) { 229f700e84fSDuc Dang dev_err(&pdev->dev, 230f700e84fSDuc Dang "APM X-Gene SLIMpro MailBox register failed:%d\n", rc); 231f700e84fSDuc Dang return rc; 232f700e84fSDuc Dang } 233f700e84fSDuc Dang 234f700e84fSDuc Dang dev_info(&pdev->dev, "APM X-Gene SLIMpro MailBox registered\n"); 235f700e84fSDuc Dang return 0; 236f700e84fSDuc Dang } 237f700e84fSDuc Dang 238f700e84fSDuc Dang static int slimpro_mbox_remove(struct platform_device *pdev) 239f700e84fSDuc Dang { 240f700e84fSDuc Dang struct slimpro_mbox *smb = platform_get_drvdata(pdev); 241f700e84fSDuc Dang 242f700e84fSDuc Dang mbox_controller_unregister(&smb->mb_ctrl); 243f700e84fSDuc Dang return 0; 244f700e84fSDuc Dang } 245f700e84fSDuc Dang 246f700e84fSDuc Dang static const struct of_device_id slimpro_of_match[] = { 247f700e84fSDuc Dang {.compatible = "apm,xgene-slimpro-mbox" }, 248f700e84fSDuc Dang { }, 249f700e84fSDuc Dang }; 250f700e84fSDuc Dang MODULE_DEVICE_TABLE(of, slimpro_of_match); 251f700e84fSDuc Dang 252f700e84fSDuc Dang #ifdef CONFIG_ACPI 253f700e84fSDuc Dang static const struct acpi_device_id slimpro_acpi_ids[] = { 254f700e84fSDuc Dang {"APMC0D01", 0}, 255f700e84fSDuc Dang {} 256f700e84fSDuc Dang }; 257f700e84fSDuc Dang MODULE_DEVICE_TABLE(acpi, slimpro_acpi_ids); 258f700e84fSDuc Dang #endif 259f700e84fSDuc Dang 260f700e84fSDuc Dang static struct platform_driver slimpro_mbox_driver = { 261f700e84fSDuc Dang .probe = slimpro_mbox_probe, 262f700e84fSDuc Dang .remove = slimpro_mbox_remove, 263f700e84fSDuc Dang .driver = { 264f700e84fSDuc Dang .name = "xgene-slimpro-mbox", 265f700e84fSDuc Dang .of_match_table = of_match_ptr(slimpro_of_match), 266f700e84fSDuc Dang .acpi_match_table = ACPI_PTR(slimpro_acpi_ids) 267f700e84fSDuc Dang }, 268f700e84fSDuc Dang }; 269f700e84fSDuc Dang 270f700e84fSDuc Dang static int __init slimpro_mbox_init(void) 271f700e84fSDuc Dang { 272f700e84fSDuc Dang return platform_driver_register(&slimpro_mbox_driver); 273f700e84fSDuc Dang } 274f700e84fSDuc Dang 275f700e84fSDuc Dang static void __exit slimpro_mbox_exit(void) 276f700e84fSDuc Dang { 277f700e84fSDuc Dang platform_driver_unregister(&slimpro_mbox_driver); 278f700e84fSDuc Dang } 279f700e84fSDuc Dang 280f700e84fSDuc Dang subsys_initcall(slimpro_mbox_init); 281f700e84fSDuc Dang module_exit(slimpro_mbox_exit); 282f700e84fSDuc Dang 283f700e84fSDuc Dang MODULE_DESCRIPTION("APM X-Gene SLIMpro Mailbox Driver"); 284f700e84fSDuc Dang MODULE_LICENSE("GPL"); 285