hi3660-mailbox.c (7596B)
1// SPDX-License-Identifier: GPL-2.0 2// Copyright (c) 2017-2018 HiSilicon Limited. 3// Copyright (c) 2017-2018 Linaro Limited. 4 5#include <linux/bitops.h> 6#include <linux/delay.h> 7#include <linux/device.h> 8#include <linux/err.h> 9#include <linux/interrupt.h> 10#include <linux/io.h> 11#include <linux/iopoll.h> 12#include <linux/mailbox_controller.h> 13#include <linux/module.h> 14#include <linux/platform_device.h> 15#include <linux/slab.h> 16 17#include "mailbox.h" 18 19#define MBOX_CHAN_MAX 32 20 21#define MBOX_RX 0x0 22#define MBOX_TX 0x1 23 24#define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x40)) 25#define MBOX_SRC_REG 0x00 26#define MBOX_DST_REG 0x04 27#define MBOX_DCLR_REG 0x08 28#define MBOX_DSTAT_REG 0x0c 29#define MBOX_MODE_REG 0x10 30#define MBOX_IMASK_REG 0x14 31#define MBOX_ICLR_REG 0x18 32#define MBOX_SEND_REG 0x1c 33#define MBOX_DATA_REG 0x20 34 35#define MBOX_IPC_LOCK_REG 0xa00 36#define MBOX_IPC_UNLOCK 0x1acce551 37 38#define MBOX_AUTOMATIC_ACK 1 39 40#define MBOX_STATE_IDLE BIT(4) 41#define MBOX_STATE_READY BIT(5) 42#define MBOX_STATE_ACK BIT(7) 43 44#define MBOX_MSG_LEN 8 45 46/** 47 * struct hi3660_chan_info - Hi3660 mailbox channel information 48 * @dst_irq: Interrupt vector for remote processor 49 * @ack_irq: Interrupt vector for local processor 50 * 51 * A channel can be used for TX or RX, it can trigger remote 52 * processor interrupt to notify remote processor and can receive 53 * interrupt if it has an incoming message. 54 */ 55struct hi3660_chan_info { 56 unsigned int dst_irq; 57 unsigned int ack_irq; 58}; 59 60/** 61 * struct hi3660_mbox - Hi3660 mailbox controller data 62 * @dev: Device to which it is attached 63 * @base: Base address of the register mapping region 64 * @chan: Representation of channels in mailbox controller 65 * @mchan: Representation of channel info 66 * @controller: Representation of a communication channel controller 67 * 68 * Mailbox controller includes 32 channels and can allocate 69 * channel for message transferring. 70 */ 71struct hi3660_mbox { 72 struct device *dev; 73 void __iomem *base; 74 struct mbox_chan chan[MBOX_CHAN_MAX]; 75 struct hi3660_chan_info mchan[MBOX_CHAN_MAX]; 76 struct mbox_controller controller; 77}; 78 79static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox) 80{ 81 return container_of(mbox, struct hi3660_mbox, controller); 82} 83 84static int hi3660_mbox_check_state(struct mbox_chan *chan) 85{ 86 unsigned long ch = (unsigned long)chan->con_priv; 87 struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); 88 struct hi3660_chan_info *mchan = &mbox->mchan[ch]; 89 void __iomem *base = MBOX_BASE(mbox, ch); 90 unsigned long val; 91 unsigned int ret; 92 93 /* Mailbox is ready to use */ 94 if (readl(base + MBOX_MODE_REG) & MBOX_STATE_READY) 95 return 0; 96 97 /* Wait for acknowledge from remote */ 98 ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG, 99 val, (val & MBOX_STATE_ACK), 1000, 300000); 100 if (ret) { 101 dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__); 102 return ret; 103 } 104 105 /* clear ack state, mailbox will get back to ready state */ 106 writel(BIT(mchan->ack_irq), base + MBOX_ICLR_REG); 107 108 return 0; 109} 110 111static int hi3660_mbox_unlock(struct mbox_chan *chan) 112{ 113 struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); 114 unsigned int val, retry = 3; 115 116 do { 117 writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG); 118 119 val = readl(mbox->base + MBOX_IPC_LOCK_REG); 120 if (!val) 121 break; 122 123 udelay(10); 124 } while (retry--); 125 126 if (val) 127 dev_err(mbox->dev, "%s: failed to unlock mailbox\n", __func__); 128 129 return (!val) ? 0 : -ETIMEDOUT; 130} 131 132static int hi3660_mbox_acquire_channel(struct mbox_chan *chan) 133{ 134 unsigned long ch = (unsigned long)chan->con_priv; 135 struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); 136 struct hi3660_chan_info *mchan = &mbox->mchan[ch]; 137 void __iomem *base = MBOX_BASE(mbox, ch); 138 unsigned int val, retry; 139 140 for (retry = 10; retry; retry--) { 141 /* Check if channel is in idle state */ 142 if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) { 143 writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG); 144 145 /* Check ack bit has been set successfully */ 146 val = readl(base + MBOX_SRC_REG); 147 if (val & BIT(mchan->ack_irq)) 148 break; 149 } 150 } 151 152 if (!retry) 153 dev_err(mbox->dev, "%s: failed to acquire channel\n", __func__); 154 155 return retry ? 0 : -ETIMEDOUT; 156} 157 158static int hi3660_mbox_startup(struct mbox_chan *chan) 159{ 160 int ret; 161 162 ret = hi3660_mbox_unlock(chan); 163 if (ret) 164 return ret; 165 166 ret = hi3660_mbox_acquire_channel(chan); 167 if (ret) 168 return ret; 169 170 return 0; 171} 172 173static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg) 174{ 175 unsigned long ch = (unsigned long)chan->con_priv; 176 struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); 177 struct hi3660_chan_info *mchan = &mbox->mchan[ch]; 178 void __iomem *base = MBOX_BASE(mbox, ch); 179 u32 *buf = msg; 180 unsigned int i; 181 int ret; 182 183 ret = hi3660_mbox_check_state(chan); 184 if (ret) 185 return ret; 186 187 /* Clear mask for destination interrupt */ 188 writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG); 189 190 /* Config destination for interrupt vector */ 191 writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG); 192 193 /* Automatic acknowledge mode */ 194 writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG); 195 196 /* Fill message data */ 197 for (i = 0; i < MBOX_MSG_LEN; i++) 198 writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4); 199 200 /* Trigger data transferring */ 201 writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG); 202 return 0; 203} 204 205static const struct mbox_chan_ops hi3660_mbox_ops = { 206 .startup = hi3660_mbox_startup, 207 .send_data = hi3660_mbox_send_data, 208}; 209 210static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller, 211 const struct of_phandle_args *spec) 212{ 213 struct hi3660_mbox *mbox = to_hi3660_mbox(controller); 214 struct hi3660_chan_info *mchan; 215 unsigned int ch = spec->args[0]; 216 217 if (ch >= MBOX_CHAN_MAX) { 218 dev_err(mbox->dev, "Invalid channel idx %d\n", ch); 219 return ERR_PTR(-EINVAL); 220 } 221 222 mchan = &mbox->mchan[ch]; 223 mchan->dst_irq = spec->args[1]; 224 mchan->ack_irq = spec->args[2]; 225 226 return &mbox->chan[ch]; 227} 228 229static const struct of_device_id hi3660_mbox_of_match[] = { 230 { .compatible = "hisilicon,hi3660-mbox", }, 231 {}, 232}; 233 234MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match); 235 236static int hi3660_mbox_probe(struct platform_device *pdev) 237{ 238 struct device *dev = &pdev->dev; 239 struct hi3660_mbox *mbox; 240 struct mbox_chan *chan; 241 unsigned long ch; 242 int err; 243 244 mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); 245 if (!mbox) 246 return -ENOMEM; 247 248 mbox->base = devm_platform_ioremap_resource(pdev, 0); 249 if (IS_ERR(mbox->base)) 250 return PTR_ERR(mbox->base); 251 252 mbox->dev = dev; 253 mbox->controller.dev = dev; 254 mbox->controller.chans = mbox->chan; 255 mbox->controller.num_chans = MBOX_CHAN_MAX; 256 mbox->controller.ops = &hi3660_mbox_ops; 257 mbox->controller.of_xlate = hi3660_mbox_xlate; 258 259 /* Initialize mailbox channel data */ 260 chan = mbox->chan; 261 for (ch = 0; ch < MBOX_CHAN_MAX; ch++) 262 chan[ch].con_priv = (void *)ch; 263 264 err = devm_mbox_controller_register(dev, &mbox->controller); 265 if (err) { 266 dev_err(dev, "Failed to register mailbox %d\n", err); 267 return err; 268 } 269 270 platform_set_drvdata(pdev, mbox); 271 dev_info(dev, "Mailbox enabled\n"); 272 return 0; 273} 274 275static struct platform_driver hi3660_mbox_driver = { 276 .probe = hi3660_mbox_probe, 277 .driver = { 278 .name = "hi3660-mbox", 279 .of_match_table = hi3660_mbox_of_match, 280 }, 281}; 282 283static int __init hi3660_mbox_init(void) 284{ 285 return platform_driver_register(&hi3660_mbox_driver); 286} 287core_initcall(hi3660_mbox_init); 288 289static void __exit hi3660_mbox_exit(void) 290{ 291 platform_driver_unregister(&hi3660_mbox_driver); 292} 293module_exit(hi3660_mbox_exit); 294 295MODULE_LICENSE("GPL"); 296MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller"); 297MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");