1 /* 2 * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <stdbool.h> 9 #include <stdint.h> 10 11 #include "mhu_v3_x.h" 12 13 #include "mhu_v3_x_private.h" 14 15 /* 16 * Get the device base from the device struct. Return an error if the dev is 17 * invalid. 18 */ 19 static enum mhu_v3_x_error_t get_dev_base(const struct mhu_v3_x_dev_t *dev, 20 union _mhu_v3_x_frame_t **base) 21 { 22 if (dev == NULL) { 23 return MHU_V_3_X_ERR_INVALID_PARAM; 24 } 25 26 /* Ensure driver has been initialized */ 27 if (dev->is_initialized == false) { 28 return MHU_V_3_X_ERR_NOT_INIT; 29 } 30 31 *base = (union _mhu_v3_x_frame_t *)dev->base; 32 33 return MHU_V_3_X_ERR_NONE; 34 } 35 36 enum mhu_v3_x_error_t mhu_v3_x_driver_init(struct mhu_v3_x_dev_t *dev) 37 { 38 uint32_t aidr = 0; 39 uint8_t mhu_major_rev; 40 union _mhu_v3_x_frame_t *p_mhu; 41 42 if (dev == NULL) { 43 return MHU_V_3_X_ERR_INVALID_PARAM; 44 } 45 46 /* Return if already initialized */ 47 if (dev->is_initialized == true) { 48 return MHU_V_3_X_ERR_NONE; 49 } 50 51 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 52 53 /* Read revision from MHU hardware */ 54 if (dev->frame == MHU_V3_X_PBX_FRAME) { 55 aidr = p_mhu->pbx_frame.pbx_ctrl_page.pbx_aidr; 56 } else if (dev->frame == MHU_V3_X_MBX_FRAME) { 57 aidr = p_mhu->mbx_frame.mbx_ctrl_page.mbx_aidr; 58 } else { 59 /* Only PBX and MBX frames are supported. */ 60 return MHU_V_3_X_ERR_UNSUPPORTED; 61 } 62 63 /* Read the MHU Architecture Major Revision */ 64 mhu_major_rev = 65 ((aidr & MHU_ARCH_MAJOR_REV_MASK) >> MHU_ARCH_MAJOR_REV_OFF); 66 67 /* Return error if the MHU major revision is not 3 */ 68 if (mhu_major_rev != MHU_MAJOR_REV_V3) { 69 /* Unsupported MHU version */ 70 return MHU_V_3_X_ERR_UNSUPPORTED_VERSION; 71 } 72 73 /* Read the MHU Architecture Minor Revision */ 74 dev->subversion = 75 ((aidr & MHU_ARCH_MINOR_REV_MASK) >> MHU_ARCH_MINOR_REV_MASK); 76 77 /* Return error if the MHU minor revision is not 0 */ 78 if (dev->subversion != MHU_MINOR_REV_3_0) { 79 /* Unsupported subversion */ 80 return MHU_V_3_X_ERR_UNSUPPORTED_VERSION; 81 } 82 83 /* Initialize the Postbox/Mailbox to remain in operational state */ 84 if (dev->frame == MHU_V3_X_PBX_FRAME) { 85 p_mhu->pbx_frame.pbx_ctrl_page.pbx_ctrl |= MHU_V3_OP_REQ; 86 } else if (dev->frame == MHU_V3_X_MBX_FRAME) { 87 p_mhu->mbx_frame.mbx_ctrl_page.mbx_ctrl |= MHU_V3_OP_REQ; 88 } else { 89 /* Only PBX and MBX frames are supported. */ 90 return MHU_V_3_X_ERR_UNSUPPORTED; 91 } 92 93 dev->is_initialized = true; 94 95 return MHU_V_3_X_ERR_NONE; 96 } 97 98 enum mhu_v3_x_error_t mhu_v3_x_get_num_channel_implemented( 99 const struct mhu_v3_x_dev_t *dev, 100 enum mhu_v3_x_channel_type_t ch_type, uint8_t *num_ch) 101 { 102 enum mhu_v3_x_error_t status; 103 union _mhu_v3_x_frame_t *p_mhu; 104 105 if (num_ch == NULL) { 106 return MHU_V_3_X_ERR_INVALID_PARAM; 107 } 108 109 /* Get dev->base if it is valid or return an error if dev is not */ 110 status = get_dev_base(dev, &p_mhu); 111 if (status != MHU_V_3_X_ERR_NONE) { 112 return status; 113 } 114 115 /* Only doorbell channel is supported */ 116 if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) { 117 return MHU_V_3_X_ERR_UNSUPPORTED; 118 } 119 120 /* Read the number of channels implemented in the MHU */ 121 if (dev->frame == MHU_V3_X_PBX_FRAME) { 122 *num_ch = (p_mhu->pbx_frame.pbx_ctrl_page.pbx_dbch_cfg0 + 1); 123 } else if (dev->frame == MHU_V3_X_MBX_FRAME) { 124 *num_ch = (p_mhu->mbx_frame.mbx_ctrl_page.mbx_dbch_cfg0 + 1); 125 } else { 126 /* Only PBX and MBX frames are supported. */ 127 return MHU_V_3_X_ERR_UNSUPPORTED; 128 } 129 130 return MHU_V_3_X_ERR_NONE; 131 } 132 133 enum mhu_v3_x_error_t mhu_v3_x_doorbell_clear(const struct mhu_v3_x_dev_t *dev, 134 const uint32_t channel, uint32_t flags) 135 { 136 union _mhu_v3_x_frame_t *p_mhu; 137 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; 138 enum mhu_v3_x_error_t status; 139 140 /* Get dev->base if it is valid or return an error if dev is not */ 141 status = get_dev_base(dev, &p_mhu); 142 if (status != MHU_V_3_X_ERR_NONE) { 143 return status; 144 } 145 146 /* Only MBX can clear the Doorbell channel */ 147 if (dev->frame != MHU_V3_X_MBX_FRAME) { 148 return MHU_V_3_X_ERR_INVALID_PARAM; 149 } 150 151 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 152 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) 153 &(p_mhu->mbx_frame.mdbcw_page); 154 155 /* Clear the bits in the doorbell channel */ 156 mdbcw_reg[channel].mdbcw_clr |= flags; 157 158 return MHU_V_3_X_ERR_NONE; 159 } 160 161 enum mhu_v3_x_error_t mhu_v3_x_doorbell_write(const struct mhu_v3_x_dev_t *dev, 162 const uint32_t channel, uint32_t flags) 163 { 164 union _mhu_v3_x_frame_t *p_mhu; 165 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; 166 enum mhu_v3_x_error_t status; 167 168 /* Get dev->base if it is valid or return an error if dev is not */ 169 status = get_dev_base(dev, &p_mhu); 170 if (status != MHU_V_3_X_ERR_NONE) { 171 return status; 172 } 173 174 /* Only PBX can set the Doorbell channel value */ 175 if (dev->frame != MHU_V3_X_PBX_FRAME) { 176 return MHU_V_3_X_ERR_INVALID_PARAM; 177 } 178 179 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 180 181 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *) 182 &(p_mhu->pbx_frame.pdbcw_page); 183 184 /* Write the value to the doorbell channel */ 185 pdbcw_reg[channel].pdbcw_set |= flags; 186 187 return MHU_V_3_X_ERR_NONE; 188 } 189 190 enum mhu_v3_x_error_t mhu_v3_x_doorbell_read(const struct mhu_v3_x_dev_t *dev, 191 const uint32_t channel, uint32_t *flags) 192 { 193 union _mhu_v3_x_frame_t *p_mhu; 194 enum mhu_v3_x_error_t status; 195 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; 196 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; 197 198 if (flags == NULL) { 199 return MHU_V_3_X_ERR_INVALID_PARAM; 200 } 201 202 /* Get dev->base if it is valid or return an error if dev is not */ 203 status = get_dev_base(dev, &p_mhu); 204 if (status != MHU_V_3_X_ERR_NONE) { 205 return status; 206 } 207 208 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 209 210 if (dev->frame == MHU_V3_X_PBX_FRAME) { 211 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *) 212 &(p_mhu->pbx_frame.pdbcw_page); 213 214 /* Read the value from Postbox Doorbell status register */ 215 *flags = pdbcw_reg[channel].pdbcw_st; 216 } else if (dev->frame == MHU_V3_X_MBX_FRAME) { 217 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) 218 &(p_mhu->mbx_frame.mdbcw_page); 219 220 /* Read the value from Mailbox Doorbell status register */ 221 *flags = mdbcw_reg[channel].mdbcw_st; 222 } else { 223 /* Only PBX and MBX frames are supported. */ 224 return MHU_V_3_X_ERR_UNSUPPORTED; 225 } 226 227 return MHU_V_3_X_ERR_NONE; 228 } 229 230 enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_set( 231 const struct mhu_v3_x_dev_t *dev, const uint32_t channel, 232 uint32_t flags) 233 { 234 union _mhu_v3_x_frame_t *p_mhu; 235 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; 236 enum mhu_v3_x_error_t status; 237 238 /* Get dev->base if it is valid or return an error if dev is not */ 239 status = get_dev_base(dev, &p_mhu); 240 if (status != MHU_V_3_X_ERR_NONE) { 241 return status; 242 } 243 244 /* Doorbell channel mask is not applicable for PBX */ 245 if (dev->frame != MHU_V3_X_MBX_FRAME) { 246 return MHU_V_3_X_ERR_INVALID_PARAM; 247 } 248 249 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 250 251 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) 252 &(p_mhu->mbx_frame.mdbcw_page); 253 254 /* Set the Doorbell channel mask */ 255 mdbcw_reg[channel].mdbcw_msk_set |= flags; 256 257 return MHU_V_3_X_ERR_NONE; 258 } 259 260 enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_clear( 261 const struct mhu_v3_x_dev_t *dev, const uint32_t channel, 262 uint32_t flags) 263 { 264 union _mhu_v3_x_frame_t *p_mhu; 265 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; 266 enum mhu_v3_x_error_t status; 267 268 /* Get dev->base if it is valid or return an error if dev is not */ 269 status = get_dev_base(dev, &p_mhu); 270 if (status != MHU_V_3_X_ERR_NONE) { 271 return status; 272 } 273 274 /* Doorbell channel mask is not applicable for PBX */ 275 if (dev->frame != MHU_V3_X_MBX_FRAME) { 276 return MHU_V_3_X_ERR_INVALID_PARAM; 277 } 278 279 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 280 281 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) 282 &(p_mhu->mbx_frame.mdbcw_page); 283 284 /* Clear the Doorbell channel mask */ 285 mdbcw_reg[channel].mdbcw_msk_clr = flags; 286 287 return MHU_V_3_X_ERR_NONE; 288 } 289 290 enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_get( 291 const struct mhu_v3_x_dev_t *dev, const uint32_t channel, 292 uint32_t *flags) 293 { 294 union _mhu_v3_x_frame_t *p_mhu; 295 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; 296 enum mhu_v3_x_error_t status; 297 298 if (flags == NULL) { 299 return MHU_V_3_X_ERR_INVALID_PARAM; 300 } 301 302 /* Get dev->base if it is valid or return an error if dev is not */ 303 status = get_dev_base(dev, &p_mhu); 304 if (status != MHU_V_3_X_ERR_NONE) { 305 return status; 306 } 307 308 /* Doorbell channel mask is not applicable for PBX */ 309 if (dev->frame != MHU_V3_X_MBX_FRAME) { 310 return MHU_V_3_X_ERR_INVALID_PARAM; 311 } 312 313 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 314 315 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) 316 &(p_mhu->mbx_frame.mdbcw_page); 317 318 /* Save the Doorbell channel mask status */ 319 *flags = mdbcw_reg[channel].mdbcw_msk_st; 320 321 return MHU_V_3_X_ERR_NONE; 322 } 323 324 enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_enable( 325 const struct mhu_v3_x_dev_t *dev, const uint32_t channel, 326 enum mhu_v3_x_channel_type_t ch_type) 327 { 328 enum mhu_v3_x_error_t status; 329 330 union _mhu_v3_x_frame_t *p_mhu; 331 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; 332 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; 333 334 /* Get dev->base if it is valid or return an error if dev is not */ 335 status = get_dev_base(dev, &p_mhu); 336 if (status != MHU_V_3_X_ERR_NONE) { 337 return status; 338 } 339 340 /* Only doorbell channel is supported */ 341 if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) { 342 return MHU_V_3_X_ERR_UNSUPPORTED; 343 } 344 345 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 346 347 if (dev->frame == MHU_V3_X_PBX_FRAME) { 348 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *) 349 &(p_mhu->pbx_frame.pdbcw_page); 350 351 /* 352 * Enable this doorbell channel to generate interrupts for 353 * transfer acknowledge events. 354 */ 355 pdbcw_reg[channel].pdbcw_int_en = MHU_V3_X_PDBCW_INT_X_TFR_ACK; 356 357 /* 358 * Enable this doorbell channel to contribute to the PBX 359 * combined interrupt. 360 */ 361 pdbcw_reg[channel].pdbcw_ctrl = MHU_V3_X_PDBCW_CTRL_PBX_COMB_EN; 362 } else if (dev->frame == MHU_V3_X_MBX_FRAME) { 363 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) 364 &(p_mhu->mbx_frame.mdbcw_page); 365 366 /* 367 * Enable this doorbell channel to contribute to the MBX 368 * combined interrupt. 369 */ 370 mdbcw_reg[channel].mdbcw_ctrl = MHU_V3_X_MDBCW_CTRL_MBX_COMB_EN; 371 } else { 372 /* Only PBX and MBX frames are supported. */ 373 return MHU_V_3_X_ERR_UNSUPPORTED; 374 } 375 376 return MHU_V_3_X_ERR_NONE; 377 } 378 379 enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_disable( 380 const struct mhu_v3_x_dev_t *dev, const uint32_t channel, 381 enum mhu_v3_x_channel_type_t ch_type) 382 { 383 enum mhu_v3_x_error_t status; 384 385 union _mhu_v3_x_frame_t *p_mhu; 386 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; 387 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; 388 389 /* Get dev->base if it is valid or return an error if dev is not */ 390 status = get_dev_base(dev, &p_mhu); 391 if (status != MHU_V_3_X_ERR_NONE) { 392 return status; 393 } 394 395 /* Only doorbell channel is supported */ 396 if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) { 397 return MHU_V_3_X_ERR_UNSUPPORTED; 398 } 399 400 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 401 402 if (dev->frame == MHU_V3_X_PBX_FRAME) { 403 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *) 404 &(p_mhu->pbx_frame.pdbcw_page); 405 406 /* Clear channel transfer acknowledge event interrupt */ 407 pdbcw_reg[channel].pdbcw_int_clr = MHU_V3_X_PDBCW_INT_X_TFR_ACK; 408 409 /* Disable channel transfer acknowledge event interrupt */ 410 pdbcw_reg[channel].pdbcw_int_en &= 411 ~(MHU_V3_X_PDBCW_INT_X_TFR_ACK); 412 413 /* 414 * Disable this doorbell channel from contributing to the PBX 415 * combined interrupt. 416 */ 417 pdbcw_reg[channel].pdbcw_ctrl &= 418 ~(MHU_V3_X_PDBCW_CTRL_PBX_COMB_EN); 419 } else if (dev->frame == MHU_V3_X_MBX_FRAME) { 420 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) 421 &(p_mhu->mbx_frame.mdbcw_page); 422 423 /* 424 * Disable this doorbell channel from contributing to the MBX 425 * combined interrupt. 426 */ 427 mdbcw_reg[channel].mdbcw_ctrl &= 428 ~(MHU_V3_X_MDBCW_CTRL_MBX_COMB_EN); 429 } else { 430 /* Only PBX and MBX frames are supported. */ 431 return MHU_V_3_X_ERR_UNSUPPORTED; 432 } 433 434 return MHU_V_3_X_ERR_NONE; 435 } 436 437 enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_clear( 438 const struct mhu_v3_x_dev_t *dev, const uint32_t channel, 439 enum mhu_v3_x_channel_type_t ch_type) 440 { 441 enum mhu_v3_x_error_t status; 442 union _mhu_v3_x_frame_t *p_mhu; 443 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; 444 445 /* Get dev->base if it is valid or return an error if dev is not */ 446 status = get_dev_base(dev, &p_mhu); 447 if (status != MHU_V_3_X_ERR_NONE) { 448 return status; 449 } 450 451 /* Only doorbell channel is supported */ 452 if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) { 453 return MHU_V_3_X_ERR_UNSUPPORTED; 454 } 455 456 /* 457 * Only postbox doorbell channel transfer acknowledge interrupt can be 458 * cleared manually. 459 * 460 * To clear MBX interrupt the unmasked status must be cleared using 461 * mhu_v3_x_doorbell_clear. 462 */ 463 if (dev->frame != MHU_V3_X_PBX_FRAME) { 464 return MHU_V_3_X_ERR_INVALID_PARAM; 465 } 466 467 p_mhu = (union _mhu_v3_x_frame_t *)dev->base; 468 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)&( 469 p_mhu->pbx_frame.pdbcw_page); 470 471 /* Clear channel transfer acknowledge event interrupt */ 472 pdbcw_reg[channel].pdbcw_int_clr |= 0x1; 473 474 return MHU_V_3_X_ERR_NONE; 475 } 476