1 // SPDX-License-Identifier: GPL-2.0 2 /* Marvell OcteonTx2 RVU Admin Function driver 3 * 4 * Copyright (C) 2018 Marvell International Ltd. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11 #include <linux/module.h> 12 #include <linux/interrupt.h> 13 #include <linux/pci.h> 14 15 #include "rvu_reg.h" 16 #include "mbox.h" 17 18 static const u16 msgs_offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); 19 20 void otx2_mbox_reset(struct otx2_mbox *mbox, int devid) 21 { 22 struct otx2_mbox_dev *mdev = &mbox->dev[devid]; 23 struct mbox_hdr *tx_hdr, *rx_hdr; 24 25 tx_hdr = mdev->mbase + mbox->tx_start; 26 rx_hdr = mdev->mbase + mbox->rx_start; 27 28 spin_lock(&mdev->mbox_lock); 29 mdev->msg_size = 0; 30 mdev->rsp_size = 0; 31 tx_hdr->num_msgs = 0; 32 rx_hdr->num_msgs = 0; 33 spin_unlock(&mdev->mbox_lock); 34 } 35 EXPORT_SYMBOL(otx2_mbox_reset); 36 37 void otx2_mbox_destroy(struct otx2_mbox *mbox) 38 { 39 mbox->reg_base = NULL; 40 mbox->hwbase = NULL; 41 42 kfree(mbox->dev); 43 mbox->dev = NULL; 44 } 45 EXPORT_SYMBOL(otx2_mbox_destroy); 46 47 int otx2_mbox_init(struct otx2_mbox *mbox, void *hwbase, struct pci_dev *pdev, 48 void *reg_base, int direction, int ndevs) 49 { 50 struct otx2_mbox_dev *mdev; 51 int devid; 52 53 switch (direction) { 54 case MBOX_DIR_AFPF: 55 case MBOX_DIR_PFVF: 56 mbox->tx_start = MBOX_DOWN_TX_START; 57 mbox->rx_start = MBOX_DOWN_RX_START; 58 mbox->tx_size = MBOX_DOWN_TX_SIZE; 59 mbox->rx_size = MBOX_DOWN_RX_SIZE; 60 break; 61 case MBOX_DIR_PFAF: 62 case MBOX_DIR_VFPF: 63 mbox->tx_start = MBOX_DOWN_RX_START; 64 mbox->rx_start = MBOX_DOWN_TX_START; 65 mbox->tx_size = MBOX_DOWN_RX_SIZE; 66 mbox->rx_size = MBOX_DOWN_TX_SIZE; 67 break; 68 case MBOX_DIR_AFPF_UP: 69 case MBOX_DIR_PFVF_UP: 70 mbox->tx_start = MBOX_UP_TX_START; 71 mbox->rx_start = MBOX_UP_RX_START; 72 mbox->tx_size = MBOX_UP_TX_SIZE; 73 mbox->rx_size = MBOX_UP_RX_SIZE; 74 break; 75 case MBOX_DIR_PFAF_UP: 76 case MBOX_DIR_VFPF_UP: 77 mbox->tx_start = MBOX_UP_RX_START; 78 mbox->rx_start = MBOX_UP_TX_START; 79 mbox->tx_size = MBOX_UP_RX_SIZE; 80 mbox->rx_size = MBOX_UP_TX_SIZE; 81 break; 82 default: 83 return -ENODEV; 84 } 85 86 switch (direction) { 87 case MBOX_DIR_AFPF: 88 case MBOX_DIR_AFPF_UP: 89 mbox->trigger = RVU_AF_AFPF_MBOX0; 90 mbox->tr_shift = 4; 91 break; 92 case MBOX_DIR_PFAF: 93 case MBOX_DIR_PFAF_UP: 94 mbox->trigger = RVU_PF_PFAF_MBOX1; 95 mbox->tr_shift = 0; 96 break; 97 case MBOX_DIR_PFVF: 98 case MBOX_DIR_PFVF_UP: 99 mbox->trigger = RVU_PF_VFX_PFVF_MBOX0; 100 mbox->tr_shift = 12; 101 break; 102 case MBOX_DIR_VFPF: 103 case MBOX_DIR_VFPF_UP: 104 mbox->trigger = RVU_VF_VFPF_MBOX1; 105 mbox->tr_shift = 0; 106 break; 107 default: 108 return -ENODEV; 109 } 110 111 mbox->reg_base = reg_base; 112 mbox->hwbase = hwbase; 113 mbox->pdev = pdev; 114 115 mbox->dev = kcalloc(ndevs, sizeof(struct otx2_mbox_dev), GFP_KERNEL); 116 if (!mbox->dev) { 117 otx2_mbox_destroy(mbox); 118 return -ENOMEM; 119 } 120 121 mbox->ndevs = ndevs; 122 for (devid = 0; devid < ndevs; devid++) { 123 mdev = &mbox->dev[devid]; 124 mdev->mbase = mbox->hwbase + (devid * MBOX_SIZE); 125 spin_lock_init(&mdev->mbox_lock); 126 /* Init header to reset value */ 127 otx2_mbox_reset(mbox, devid); 128 } 129 130 return 0; 131 } 132 EXPORT_SYMBOL(otx2_mbox_init); 133 134 int otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid) 135 { 136 struct otx2_mbox_dev *mdev = &mbox->dev[devid]; 137 int timeout = 0, sleep = 1; 138 139 while (mdev->num_msgs != mdev->msgs_acked) { 140 msleep(sleep); 141 timeout += sleep; 142 if (timeout >= MBOX_RSP_TIMEOUT) 143 return -EIO; 144 } 145 return 0; 146 } 147 EXPORT_SYMBOL(otx2_mbox_wait_for_rsp); 148 149 int otx2_mbox_busy_poll_for_rsp(struct otx2_mbox *mbox, int devid) 150 { 151 struct otx2_mbox_dev *mdev = &mbox->dev[devid]; 152 unsigned long timeout = jiffies + 1 * HZ; 153 154 while (!time_after(jiffies, timeout)) { 155 if (mdev->num_msgs == mdev->msgs_acked) 156 return 0; 157 cpu_relax(); 158 } 159 return -EIO; 160 } 161 EXPORT_SYMBOL(otx2_mbox_busy_poll_for_rsp); 162 163 void otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid) 164 { 165 struct otx2_mbox_dev *mdev = &mbox->dev[devid]; 166 struct mbox_hdr *tx_hdr, *rx_hdr; 167 168 tx_hdr = mdev->mbase + mbox->tx_start; 169 rx_hdr = mdev->mbase + mbox->rx_start; 170 171 spin_lock(&mdev->mbox_lock); 172 /* Reset header for next messages */ 173 mdev->msg_size = 0; 174 mdev->rsp_size = 0; 175 mdev->msgs_acked = 0; 176 177 /* Sync mbox data into memory */ 178 smp_wmb(); 179 180 /* num_msgs != 0 signals to the peer that the buffer has a number of 181 * messages. So this should be written after writing all the messages 182 * to the shared memory. 183 */ 184 tx_hdr->num_msgs = mdev->num_msgs; 185 rx_hdr->num_msgs = 0; 186 spin_unlock(&mdev->mbox_lock); 187 188 /* The interrupt should be fired after num_msgs is written 189 * to the shared memory 190 */ 191 writeq(1, (void __iomem *)mbox->reg_base + 192 (mbox->trigger | (devid << mbox->tr_shift))); 193 } 194 EXPORT_SYMBOL(otx2_mbox_msg_send); 195 196 struct mbox_msghdr *otx2_mbox_alloc_msg_rsp(struct otx2_mbox *mbox, int devid, 197 int size, int size_rsp) 198 { 199 struct otx2_mbox_dev *mdev = &mbox->dev[devid]; 200 struct mbox_msghdr *msghdr = NULL; 201 202 spin_lock(&mdev->mbox_lock); 203 size = ALIGN(size, MBOX_MSG_ALIGN); 204 size_rsp = ALIGN(size_rsp, MBOX_MSG_ALIGN); 205 /* Check if there is space in mailbox */ 206 if ((mdev->msg_size + size) > mbox->tx_size - msgs_offset) 207 goto exit; 208 if ((mdev->rsp_size + size_rsp) > mbox->rx_size - msgs_offset) 209 goto exit; 210 211 if (mdev->msg_size == 0) 212 mdev->num_msgs = 0; 213 mdev->num_msgs++; 214 215 msghdr = mdev->mbase + mbox->tx_start + msgs_offset + mdev->msg_size; 216 217 /* Clear the whole msg region */ 218 memset(msghdr, 0, sizeof(*msghdr) + size); 219 /* Init message header with reset values */ 220 msghdr->ver = OTX2_MBOX_VERSION; 221 mdev->msg_size += size; 222 mdev->rsp_size += size_rsp; 223 msghdr->next_msgoff = mdev->msg_size + msgs_offset; 224 exit: 225 spin_unlock(&mdev->mbox_lock); 226 227 return msghdr; 228 } 229 EXPORT_SYMBOL(otx2_mbox_alloc_msg_rsp); 230 231 struct mbox_msghdr *otx2_mbox_get_rsp(struct otx2_mbox *mbox, int devid, 232 struct mbox_msghdr *msg) 233 { 234 unsigned long imsg = mbox->tx_start + msgs_offset; 235 unsigned long irsp = mbox->rx_start + msgs_offset; 236 struct otx2_mbox_dev *mdev = &mbox->dev[devid]; 237 u16 msgs; 238 239 if (mdev->num_msgs != mdev->msgs_acked) 240 return ERR_PTR(-ENODEV); 241 242 for (msgs = 0; msgs < mdev->msgs_acked; msgs++) { 243 struct mbox_msghdr *pmsg = mdev->mbase + imsg; 244 struct mbox_msghdr *prsp = mdev->mbase + irsp; 245 246 if (msg == pmsg) { 247 if (pmsg->id != prsp->id) 248 return ERR_PTR(-ENODEV); 249 return prsp; 250 } 251 252 imsg = pmsg->next_msgoff; 253 irsp = prsp->next_msgoff; 254 } 255 256 return ERR_PTR(-ENODEV); 257 } 258 EXPORT_SYMBOL(otx2_mbox_get_rsp); 259 260 int 261 otx2_reply_invalid_msg(struct otx2_mbox *mbox, int devid, u16 pcifunc, u16 id) 262 { 263 struct msg_rsp *rsp; 264 265 rsp = (struct msg_rsp *) 266 otx2_mbox_alloc_msg(mbox, devid, sizeof(*rsp)); 267 if (!rsp) 268 return -ENOMEM; 269 rsp->hdr.id = id; 270 rsp->hdr.sig = OTX2_MBOX_RSP_SIG; 271 rsp->hdr.rc = MBOX_MSG_INVALID; 272 rsp->hdr.pcifunc = pcifunc; 273 return 0; 274 } 275 EXPORT_SYMBOL(otx2_reply_invalid_msg); 276 277 bool otx2_mbox_nonempty(struct otx2_mbox *mbox, int devid) 278 { 279 struct otx2_mbox_dev *mdev = &mbox->dev[devid]; 280 bool ret; 281 282 spin_lock(&mdev->mbox_lock); 283 ret = mdev->num_msgs != 0; 284 spin_unlock(&mdev->mbox_lock); 285 286 return ret; 287 } 288 EXPORT_SYMBOL(otx2_mbox_nonempty); 289 290 const char *otx2_mbox_id2name(u16 id) 291 { 292 switch (id) { 293 #define M(_name, _id, _1, _2, _3) case _id: return # _name; 294 MBOX_MESSAGES 295 #undef M 296 default: 297 return "INVALID ID"; 298 } 299 } 300 EXPORT_SYMBOL(otx2_mbox_id2name); 301 302 MODULE_AUTHOR("Marvell International Ltd."); 303 MODULE_LICENSE("GPL v2"); 304