arm_mhu_db.c (8504B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2013-2015 Fujitsu Semiconductor Ltd. 4 * Copyright (C) 2015 Linaro Ltd. 5 * Based on ARM MHU driver by Jassi Brar <jaswinder.singh@linaro.org> 6 * Copyright (C) 2020 ARM Ltd. 7 */ 8 9#include <linux/amba/bus.h> 10#include <linux/device.h> 11#include <linux/err.h> 12#include <linux/interrupt.h> 13#include <linux/io.h> 14#include <linux/kernel.h> 15#include <linux/mailbox_controller.h> 16#include <linux/module.h> 17#include <linux/of.h> 18#include <linux/of_device.h> 19 20#define INTR_STAT_OFS 0x0 21#define INTR_SET_OFS 0x8 22#define INTR_CLR_OFS 0x10 23 24#define MHU_LP_OFFSET 0x0 25#define MHU_HP_OFFSET 0x20 26#define MHU_SEC_OFFSET 0x200 27#define TX_REG_OFFSET 0x100 28 29#define MHU_CHANS 3 /* Secure, Non-Secure High and Low Priority */ 30#define MHU_CHAN_MAX 20 /* Max channels to save on unused RAM */ 31#define MHU_NUM_DOORBELLS 32 32 33struct mhu_db_link { 34 unsigned int irq; 35 void __iomem *tx_reg; 36 void __iomem *rx_reg; 37}; 38 39struct arm_mhu { 40 void __iomem *base; 41 struct mhu_db_link mlink[MHU_CHANS]; 42 struct mbox_controller mbox; 43 struct device *dev; 44}; 45 46/** 47 * struct mhu_db_channel - ARM MHU Mailbox allocated channel information 48 * 49 * @mhu: Pointer to parent mailbox device 50 * @pchan: Physical channel within which this doorbell resides in 51 * @doorbell: doorbell number pertaining to this channel 52 */ 53struct mhu_db_channel { 54 struct arm_mhu *mhu; 55 unsigned int pchan; 56 unsigned int doorbell; 57}; 58 59static inline struct mbox_chan * 60mhu_db_mbox_to_channel(struct mbox_controller *mbox, unsigned int pchan, 61 unsigned int doorbell) 62{ 63 int i; 64 struct mhu_db_channel *chan_info; 65 66 for (i = 0; i < mbox->num_chans; i++) { 67 chan_info = mbox->chans[i].con_priv; 68 if (chan_info && chan_info->pchan == pchan && 69 chan_info->doorbell == doorbell) 70 return &mbox->chans[i]; 71 } 72 73 return NULL; 74} 75 76static void mhu_db_mbox_clear_irq(struct mbox_chan *chan) 77{ 78 struct mhu_db_channel *chan_info = chan->con_priv; 79 void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].rx_reg; 80 81 writel_relaxed(BIT(chan_info->doorbell), base + INTR_CLR_OFS); 82} 83 84static unsigned int mhu_db_mbox_irq_to_pchan_num(struct arm_mhu *mhu, int irq) 85{ 86 unsigned int pchan; 87 88 for (pchan = 0; pchan < MHU_CHANS; pchan++) 89 if (mhu->mlink[pchan].irq == irq) 90 break; 91 return pchan; 92} 93 94static struct mbox_chan * 95mhu_db_mbox_irq_to_channel(struct arm_mhu *mhu, unsigned int pchan) 96{ 97 unsigned long bits; 98 unsigned int doorbell; 99 struct mbox_chan *chan = NULL; 100 struct mbox_controller *mbox = &mhu->mbox; 101 void __iomem *base = mhu->mlink[pchan].rx_reg; 102 103 bits = readl_relaxed(base + INTR_STAT_OFS); 104 if (!bits) 105 /* No IRQs fired in specified physical channel */ 106 return NULL; 107 108 /* An IRQ has fired, find the associated channel */ 109 for (doorbell = 0; bits; doorbell++) { 110 if (!test_and_clear_bit(doorbell, &bits)) 111 continue; 112 113 chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell); 114 if (chan) 115 break; 116 dev_err(mbox->dev, 117 "Channel not registered: pchan: %d doorbell: %d\n", 118 pchan, doorbell); 119 } 120 121 return chan; 122} 123 124static irqreturn_t mhu_db_mbox_rx_handler(int irq, void *data) 125{ 126 struct mbox_chan *chan; 127 struct arm_mhu *mhu = data; 128 unsigned int pchan = mhu_db_mbox_irq_to_pchan_num(mhu, irq); 129 130 while (NULL != (chan = mhu_db_mbox_irq_to_channel(mhu, pchan))) { 131 mbox_chan_received_data(chan, NULL); 132 mhu_db_mbox_clear_irq(chan); 133 } 134 135 return IRQ_HANDLED; 136} 137 138static bool mhu_db_last_tx_done(struct mbox_chan *chan) 139{ 140 struct mhu_db_channel *chan_info = chan->con_priv; 141 void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg; 142 143 if (readl_relaxed(base + INTR_STAT_OFS) & BIT(chan_info->doorbell)) 144 return false; 145 146 return true; 147} 148 149static int mhu_db_send_data(struct mbox_chan *chan, void *data) 150{ 151 struct mhu_db_channel *chan_info = chan->con_priv; 152 void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg; 153 154 /* Send event to co-processor */ 155 writel_relaxed(BIT(chan_info->doorbell), base + INTR_SET_OFS); 156 157 return 0; 158} 159 160static int mhu_db_startup(struct mbox_chan *chan) 161{ 162 mhu_db_mbox_clear_irq(chan); 163 return 0; 164} 165 166static void mhu_db_shutdown(struct mbox_chan *chan) 167{ 168 struct mhu_db_channel *chan_info = chan->con_priv; 169 struct mbox_controller *mbox = &chan_info->mhu->mbox; 170 int i; 171 172 for (i = 0; i < mbox->num_chans; i++) 173 if (chan == &mbox->chans[i]) 174 break; 175 176 if (mbox->num_chans == i) { 177 dev_warn(mbox->dev, "Request to free non-existent channel\n"); 178 return; 179 } 180 181 /* Reset channel */ 182 mhu_db_mbox_clear_irq(chan); 183 devm_kfree(mbox->dev, chan->con_priv); 184 chan->con_priv = NULL; 185} 186 187static struct mbox_chan *mhu_db_mbox_xlate(struct mbox_controller *mbox, 188 const struct of_phandle_args *spec) 189{ 190 struct arm_mhu *mhu = dev_get_drvdata(mbox->dev); 191 struct mhu_db_channel *chan_info; 192 struct mbox_chan *chan; 193 unsigned int pchan = spec->args[0]; 194 unsigned int doorbell = spec->args[1]; 195 int i; 196 197 /* Bounds checking */ 198 if (pchan >= MHU_CHANS || doorbell >= MHU_NUM_DOORBELLS) { 199 dev_err(mbox->dev, 200 "Invalid channel requested pchan: %d doorbell: %d\n", 201 pchan, doorbell); 202 return ERR_PTR(-EINVAL); 203 } 204 205 /* Is requested channel free? */ 206 chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell); 207 if (chan) { 208 dev_err(mbox->dev, "Channel in use: pchan: %d doorbell: %d\n", 209 pchan, doorbell); 210 return ERR_PTR(-EBUSY); 211 } 212 213 /* Find the first free slot */ 214 for (i = 0; i < mbox->num_chans; i++) 215 if (!mbox->chans[i].con_priv) 216 break; 217 218 if (mbox->num_chans == i) { 219 dev_err(mbox->dev, "No free channels left\n"); 220 return ERR_PTR(-EBUSY); 221 } 222 223 chan = &mbox->chans[i]; 224 225 chan_info = devm_kzalloc(mbox->dev, sizeof(*chan_info), GFP_KERNEL); 226 if (!chan_info) 227 return ERR_PTR(-ENOMEM); 228 229 chan_info->mhu = mhu; 230 chan_info->pchan = pchan; 231 chan_info->doorbell = doorbell; 232 233 chan->con_priv = chan_info; 234 235 dev_dbg(mbox->dev, "mbox: created channel phys: %d doorbell: %d\n", 236 pchan, doorbell); 237 238 return chan; 239} 240 241static const struct mbox_chan_ops mhu_db_ops = { 242 .send_data = mhu_db_send_data, 243 .startup = mhu_db_startup, 244 .shutdown = mhu_db_shutdown, 245 .last_tx_done = mhu_db_last_tx_done, 246}; 247 248static int mhu_db_probe(struct amba_device *adev, const struct amba_id *id) 249{ 250 u32 cell_count; 251 int i, err, max_chans; 252 struct arm_mhu *mhu; 253 struct mbox_chan *chans; 254 struct device *dev = &adev->dev; 255 struct device_node *np = dev->of_node; 256 int mhu_reg[MHU_CHANS] = { 257 MHU_LP_OFFSET, MHU_HP_OFFSET, MHU_SEC_OFFSET, 258 }; 259 260 if (!of_device_is_compatible(np, "arm,mhu-doorbell")) 261 return -ENODEV; 262 263 err = of_property_read_u32(np, "#mbox-cells", &cell_count); 264 if (err) { 265 dev_err(dev, "failed to read #mbox-cells in '%pOF'\n", np); 266 return err; 267 } 268 269 if (cell_count == 2) { 270 max_chans = MHU_CHAN_MAX; 271 } else { 272 dev_err(dev, "incorrect value of #mbox-cells in '%pOF'\n", np); 273 return -EINVAL; 274 } 275 276 mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL); 277 if (!mhu) 278 return -ENOMEM; 279 280 mhu->base = devm_ioremap_resource(dev, &adev->res); 281 if (IS_ERR(mhu->base)) 282 return PTR_ERR(mhu->base); 283 284 chans = devm_kcalloc(dev, max_chans, sizeof(*chans), GFP_KERNEL); 285 if (!chans) 286 return -ENOMEM; 287 288 mhu->dev = dev; 289 mhu->mbox.dev = dev; 290 mhu->mbox.chans = chans; 291 mhu->mbox.num_chans = max_chans; 292 mhu->mbox.txdone_irq = false; 293 mhu->mbox.txdone_poll = true; 294 mhu->mbox.txpoll_period = 1; 295 296 mhu->mbox.of_xlate = mhu_db_mbox_xlate; 297 amba_set_drvdata(adev, mhu); 298 299 mhu->mbox.ops = &mhu_db_ops; 300 301 err = devm_mbox_controller_register(dev, &mhu->mbox); 302 if (err) { 303 dev_err(dev, "Failed to register mailboxes %d\n", err); 304 return err; 305 } 306 307 for (i = 0; i < MHU_CHANS; i++) { 308 int irq = mhu->mlink[i].irq = adev->irq[i]; 309 310 if (irq <= 0) { 311 dev_dbg(dev, "No IRQ found for Channel %d\n", i); 312 continue; 313 } 314 315 mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i]; 316 mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET; 317 318 err = devm_request_threaded_irq(dev, irq, NULL, 319 mhu_db_mbox_rx_handler, 320 IRQF_ONESHOT, "mhu_db_link", mhu); 321 if (err) { 322 dev_err(dev, "Can't claim IRQ %d\n", irq); 323 mbox_controller_unregister(&mhu->mbox); 324 return err; 325 } 326 } 327 328 dev_info(dev, "ARM MHU Doorbell mailbox registered\n"); 329 return 0; 330} 331 332static struct amba_id mhu_ids[] = { 333 { 334 .id = 0x1bb098, 335 .mask = 0xffffff, 336 }, 337 { 0, 0 }, 338}; 339MODULE_DEVICE_TABLE(amba, mhu_ids); 340 341static struct amba_driver arm_mhu_db_driver = { 342 .drv = { 343 .name = "mhu-doorbell", 344 }, 345 .id_table = mhu_ids, 346 .probe = mhu_db_probe, 347}; 348module_amba_driver(arm_mhu_db_driver); 349 350MODULE_LICENSE("GPL v2"); 351MODULE_DESCRIPTION("ARM MHU Doorbell Driver"); 352MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");