cmicm_sbusdma_desc.c (14196B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * Purpose: CMICM SBUS Descriptor based DMA Driver. 8 */ 9 10 #include <shared/bsl.h> 11 #include <soc/drv.h> 12 #include <soc/debug.h> 13 #include <soc/cm.h> 14 #include <soc/sbusdma_internal.h> 15 16 #if defined(BCM_CMICM_SUPPORT) && defined(BCM_SBUSDMA_SUPPORT) 17 18 #include <soc/cmicm.h> 19 20 #define SOC_SBUSDMA_FP_RETRIES 200 21 22 typedef struct { /* h/w desc structure */ 23 uint32 cntrl; /* DMA control info */ 24 uint32 req; /* DMA request info (refer h/w spec for details) */ 25 uint32 count; /* DMA count */ 26 uint32 opcode; /* Schan opcode (refer h/w spec for details) */ 27 uint32 addr; /* Schan address */ 28 uint32 hostaddr; /* h/w mapped host address */ 29 } soc_sbusdma_desc_t; 30 31 32 STATIC uint32 _cmicm_irq_sbusdma_ch[] = { 33 IRQ_SBUSDMA_CH0_DONE, 34 IRQ_SBUSDMA_CH1_DONE, 35 IRQ_SBUSDMA_CH2_DONE 36 }; 37 38 STATIC int _soc_desc_multi_cmc = 0; 39 40 41 /******************************************* 42 * @function _cmicm_sbusdma_desc_endian_config (private) 43 * purpose Configure endianess of sbus dma descriptor 44 * 45 * @param unit [in] unit 46 * @param desc [in] soc_sbusdma_desc_t pointer 47 * 48 * @returns None 49 * @end 50 */ 51 STATIC void 52 _cmicm_sbusdma_desc_endian_config(int unit, soc_sbusdma_desc_t *desc) 53 { 54 int big_pio, big_packet, big_other; 55 56 soc_endian_get(unit, &big_pio, &big_packet, &big_other); 57 58 if (big_other) { 59 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 60 &(desc->req), HOSTMEMWR_ENDIANESSf, 1); 61 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 62 &(desc->req), HOSTMEMRD_ENDIANESSf, 1); 63 } 64 } 65 66 /******************************************* 67 * @function _cmicm_sbusdma_desc_prepare 68 * purpose API to prepare h/w descriptor 69 * 70 * @param unit [in] unit 71 * @param desc [in] soc_sbusdma_desc_t pointer 72 * @param cfg [in] soc_sbusdma_desc_cfg_t pointer 73 * @param count [in] descriptor count 74 * @param cfg_count [in] configuration count 75 * @param append [in] last of appended 76 * 77 * @returns SOC_E_NONE 78 * @returns SOC_E_XXX 79 * 80 * @end 81 */ 82 STATIC 83 void _cmicm_sbusdma_desc_prepare(int unit, soc_sbusdma_desc_t *desc, 84 soc_sbusdma_desc_cfg_t *cfg, int count, 85 soc_sbusdma_desc_ctrl_t *ctrl, int append) 86 { 87 sal_vaddr_t hptr = (sal_vaddr_t)(ctrl->buff); 88 uint32 flags; 89 schan_msg_t msg; 90 int opcode, dst_blk, acc_type; 91 int data_byte_len = 0; 92 93 sal_memset(desc, 0, sizeof(soc_sbusdma_desc_t)); 94 schan_msg_clear(&msg); 95 flags = (ctrl->flags & SOC_SBUSDMA_CFG_USE_FLAGS) ? cfg->flags : ctrl->flags; 96 97 98 if (flags & SOC_SBUSDMA_WRITE_CMD_MSG) { 99 opcode = (flags & SOC_SBUSDMA_MEMORY_CMD_MSG) ? 100 WRITE_MEMORY_CMD_MSG : WRITE_REGISTER_CMD_MSG; 101 data_byte_len = cfg->width * sizeof(uint32); 102 } else { 103 opcode = (flags & SOC_SBUSDMA_MEMORY_CMD_MSG) ? 104 READ_MEMORY_CMD_MSG : READ_REGISTER_CMD_MSG; 105 } 106 acc_type = cfg->acc_type; 107 dst_blk = cfg->blk; 108 109 soc_schan_header_cmd_set(unit, &msg.header, opcode, dst_blk, 0, 110 acc_type, data_byte_len, 0, 0); 111 112 113 desc->count = cfg->count; 114 desc->opcode = msg.dwords[0]; 115 desc->addr = cfg->addr; 116 117 /* Prepare h/w desc */ 118 if ((ctrl->cfg_count == 1) || (count == ctrl->cfg_count - 1)) { 119 desc->cntrl |= SOC_SBUSDMA_CTRL_LAST; 120 } 121 if (append && count) { 122 desc->cntrl |= SOC_SBUSDMA_CTRL_APND; 123 } 124 125 if (ctrl->cfg_count == 1) { 126 desc->hostaddr = soc_cm_l2p(unit, cfg->buff); 127 } else { 128 if (append) { 129 desc->hostaddr = soc_cm_l2p(unit, ctrl->buff); 130 ctrl->buff = (void *)(hptr + (cfg->count * cfg->width)); 131 } else { 132 desc->hostaddr = soc_cm_l2p(unit, cfg->buff); 133 } 134 } 135 if ((opcode == WRITE_MEMORY_CMD_MSG) || 136 (opcode == WRITE_REGISTER_CMD_MSG)){ 137 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 138 &(desc->req), REQ_WORDSf, cfg->width); 139 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 140 &(desc->req), REP_WORDSf, 0); 141 } else { 142 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 143 &(desc->req), REP_WORDSf, cfg->width); 144 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 145 &(desc->req), REQ_WORDSf, 0); 146 if (flags & SOC_SBUSDMA_CFG_MOR) { 147 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 148 &(desc->req), IGNORE_SBUS_EARLYACKf, 1); 149 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 150 &(desc->req), PEND_CLOCKSf, 30); 151 } 152 } 153 154 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 155 &(desc->req), INCR_SHIFTf, cfg->addr_shift); 156 157 _cmicm_sbusdma_desc_endian_config(unit, desc); 158 159 return; 160 } 161 162 /******************************************* 163 * @function _cmicm_sbusdma_desc_create 164 * purpose API to prepare h/w descriptor 165 * 166 * @param unit [in] unit 167 * @param desc [in] soc_sbusdma_desc_ctrl_t pointer 168 * @param cfg [in] soc_sbusdma_desc_cfg_t pointer 169 * @param desc [out] descriptor pointer 170 * 171 * @returns SOC_E_NONE 172 * @returns SOC_E_XXX 173 * 174 * @end 175 */ 176 STATIC 177 int _cmicm_sbusdma_desc_create(int unit, soc_sbusdma_desc_ctrl_t *ctrl, 178 soc_sbusdma_desc_cfg_t *cfg, void **desc) 179 { 180 soc_sbusdma_desc_t *dma_desc; 181 uint8 append = 0; 182 sal_vaddr_t hptr = (sal_vaddr_t)(ctrl->buff); 183 uint32 count; 184 185 if (ctrl->cfg_count == 1) { /* single mode */ 186 dma_desc = soc_cm_salloc(unit, sizeof(soc_sbusdma_desc_t), 187 "soc_sbusdma_desc_t"); 188 if (dma_desc == NULL) { 189 return SOC_E_MEMORY; 190 } 191 _cmicm_sbusdma_desc_prepare(unit, dma_desc, cfg, 0, ctrl, 0); 192 } else { 193 if (ctrl->buff) { 194 append = 1; 195 } 196 dma_desc = soc_cm_salloc(unit, sizeof(soc_sbusdma_desc_t) * 197 ctrl->cfg_count, "soc_sbusdma_desc_t"); 198 if (dma_desc == NULL) { 199 return SOC_E_MEMORY; 200 } 201 for (count = 0; count < ctrl->cfg_count; count++) { 202 _cmicm_sbusdma_desc_prepare(unit, &dma_desc[count], &cfg[count], 203 count, ctrl, append); 204 } 205 ctrl->buff = (void *)hptr; 206 } 207 *desc = dma_desc; 208 209 return SOC_E_NONE; 210 } 211 212 213 /******************************************* 214 * @function _cmicm_sbusdma_intr_wait 215 * purpose API to wait Descriptor DMA complete 216 * 217 * @param unit [in] unit 218 * @param cmc [in] cmc 219 * @param ch [in] channel 220 * @param interval [in] timeout 221 * 222 * @returns SOC_E_NONE 223 * @returns SOC_E_XXX 224 * 225 * @end 226 */ 227 STATIC int 228 _cmicm_sbusdma_intr_wait(int unit, int cmc, int ch, int timeout) 229 { 230 int rv = SOC_E_TIMEOUT; 231 int rval; 232 233 soc_cmicm_cmcx_intr0_enable(unit, cmc, _cmicm_irq_sbusdma_ch[ch]); 234 if (sal_sem_take(SOC_CONTROL(unit)->sbusDmaIntrs[cmc][ch], timeout) < 0) { 235 rv = SOC_E_TIMEOUT; 236 } 237 soc_cmicm_cmcx_intr0_disable(unit, cmc, _cmicm_irq_sbusdma_ch[ch]); 238 239 rval = soc_pci_read(unit, CMIC_CMCx_SBUSDMA_CHy_STATUS(cmc, ch)); 240 if (soc_reg_field_get(unit, CMIC_CMC0_SBUSDMA_CH0_STATUSr, 241 rval, DONEf)) { 242 rv = SOC_E_NONE; 243 if (soc_reg_field_get(unit, CMIC_CMC0_SBUSDMA_CH0_STATUSr, 244 rval, ERRORf)) { 245 rv = SOC_E_FAIL; 246 cmicm_sbusdma_curr_op_details(unit, cmc, ch); 247 } 248 } 249 250 return rv; 251 } 252 253 /******************************************* 254 * @function _cmicm_sbusdma_poll_wait 255 * purpose API to wait Descriptor using Polling mode 256 * 257 * @param unit [in] unit 258 * @param cmc [in] cmc 259 * @param ch [in] channel 260 * @param interval [in] timeout 261 * 262 * @returns SOC_E_NONE 263 * @returns SOC_E_XXX 264 * 265 * @end 266 */ 267 STATIC int 268 _cmicm_sbusdma_poll_wait(int unit, int cmc, int ch, int timeout) 269 { 270 soc_timeout_t to; 271 int rv = SOC_E_TIMEOUT; 272 int rval; 273 274 soc_timeout_init(&to, timeout, 0); 275 276 do { 277 rval = soc_pci_read(unit, 278 CMIC_CMCx_SBUSDMA_CHy_STATUS(cmc, ch)); 279 280 if (SAL_BOOT_PLISIM && !SAL_BOOT_BCMSIM) { 281 rv = SOC_E_NONE; 282 break; 283 } 284 if (soc_reg_field_get(unit, CMIC_CMC0_SBUSDMA_CH0_STATUSr, 285 rval, DONEf)) { 286 rv = SOC_E_NONE; 287 if (soc_reg_field_get(unit, CMIC_CMC0_SBUSDMA_CH0_STATUSr, 288 rval, ERRORf)) { 289 rv = SOC_E_FAIL; 290 cmicm_sbusdma_curr_op_details(unit, cmc, ch); 291 } 292 break; 293 } 294 } while(!(soc_timeout_check(&to))); 295 296 return rv; 297 298 } 299 300 /******************************************* 301 * @function _cmicm_sbusdma_wait 302 * purpose API to wait Descriptor DMA complete 303 * 304 * @param unit [in] unit 305 * @param cmc [in] cmc 306 * @param ch [in] channel 307 * @param interval [in] time interval 308 * @param intrEnb [in] interrupt enable 309 * 310 * @returns SOC_E_NONE 311 * @returns SOC_E_XXX 312 * 313 * @end 314 */ 315 STATIC int 316 _cmicm_sbusdma_wait(int unit, int cmc, int ch, int interval, uint8 intrEnb) 317 { 318 int rv = SOC_E_TIMEOUT; 319 int rval; 320 uint32 ctrl; 321 322 if (intrEnb) { 323 rv = _cmicm_sbusdma_intr_wait(unit, cmc, ch, interval); 324 } else { 325 rv = _cmicm_sbusdma_poll_wait(unit, cmc, ch, interval); 326 } 327 328 if (rv != SOC_E_NONE) { 329 if (rv != SOC_E_TIMEOUT) { 330 LOG_ERROR(BSL_LS_SOC_COMMON, 331 (BSL_META_U(unit, 332 "failed(ERR)\n"))); 333 cmicm_sbusdma_error_details(unit, 334 soc_pci_read(unit, CMIC_CMCx_SBUSDMA_CHy_STATUS(cmc, ch))); 335 } else { /* Timeout cleanup */ 336 soc_timeout_t to; 337 338 LOG_ERROR(BSL_LS_SOC_COMMON, 339 (BSL_META_U(unit, 340 "%s timeout\n"), 341 intrEnb ? 342 "interrupt" : "polling")); 343 344 /* Abort DMA */ 345 ctrl = soc_pci_read(unit, 346 CMIC_CMCx_SBUSDMA_CHy_CONTROL(cmc, ch)); 347 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_CONTROLr, 348 &ctrl, ABORTf, 1); 349 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_CONTROL(cmc, ch), 350 ctrl); 351 352 /* Check the done bit to confirm */ 353 soc_timeout_init(&to, interval, 0); 354 while (1) { 355 rval = soc_pci_read(unit, 356 CMIC_CMCx_SBUSDMA_CHy_STATUS(cmc, ch)); 357 if (soc_reg_field_get(unit, CMIC_CMC0_SBUSDMA_CH0_STATUSr, 358 rval, DONEf)) { 359 break; 360 } 361 if (soc_timeout_check(&to)) { 362 LOG_ERROR(BSL_LS_SOC_COMMON, 363 (BSL_META_U(unit, 364 "_cmicm_sbusdma_desc: Abort Failed\n"))); 365 break; 366 } 367 } 368 } 369 } /* rv != SOC_E_NONE */ 370 371 if (_soc_desc_multi_cmc) { 372 cmicm_sbusdma_ch_put(unit, cmc, ch); 373 } 374 375 return rv; 376 } 377 378 /******************************************* 379 * @function _cmicm_sbusdma_program 380 * purpose API to program Descriptor DMA 381 * 382 * @param unit [in] unit 383 * @param cmc [out] cmc 384 * @param swd [out] _soc_sbusdma_state_t pointer 385 * @param ch [out] channel 386 * 387 * @returns SOC_E_NONE 388 * @returns SOC_E_XXX 389 * 390 * @end 391 */ 392 STATIC int 393 _cmicm_sbusdma_program(int unit, int *cmc, _soc_sbusdma_state_t *swd, int *ch) 394 { 395 uint32 ctrl; 396 int i; 397 soc_sbusdma_desc_t *desc = swd->desc; 398 399 /* multicmc */ 400 if (_soc_desc_multi_cmc) { 401 SOC_IF_ERROR_RETURN(cmicm_sbusdma_ch_get(unit, cmc, ch)); 402 assert((*ch >= 0) && (*ch < CMIC_CMCx_SBUSDMA_CHAN_MAX)); 403 } 404 405 ctrl = soc_pci_read(unit, CMIC_CMCx_SBUSDMA_CHy_CONTROL(*cmc, *ch)); 406 407 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_CONTROLr, &ctrl, 408 MODEf, 1); 409 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_CONTROLr, &ctrl, 410 ABORTf, 0); 411 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_CONTROLr, &ctrl, 412 STARTf, 0); 413 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_CONTROL(*cmc, *ch), ctrl); 414 415 /* write desc address */ 416 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_DESCADDR(*cmc, *ch), 417 soc_cm_l2p(unit, desc)); 418 419 /* Debug stuff */ 420 LOG_VERBOSE(BSL_LS_SOC_DMA, 421 (BSL_META_U(unit, 422 "Count: %d, cmc = %d, ch = %d\n"), 423 swd->ctrl.cfg_count, *cmc, *ch)); 424 for (i = 0; i < swd->ctrl.cfg_count; i++) { 425 LOG_VERBOSE(BSL_LS_SOC_DMA, 426 (BSL_META_U(unit, 427 "cntrl: %08x, req: %08x, count: %08x, " 428 "opcode: %08x, saddr: %08x, haddr: %08x\n"), 429 desc[i].cntrl, desc[i].req, 430 desc[i].count, desc[i].opcode, 431 desc[i].addr, desc[i].hostaddr)); 432 } 433 434 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_CONTROLr, &ctrl, 435 STARTf, 1); 436 /* Start DMA */ 437 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_CONTROL(*cmc, *ch), ctrl); 438 439 return SOC_E_NONE; 440 } 441 442 443 /******************************************* 444 * @function cmicm_sbusdma_desc_init 445 * purpose API to Initialize cmicm Descriptor DMA 446 * 447 * @param unit [in] unit 448 * @param drv [out] soc_sbusdma_desc_drv_t pointer 449 * 450 * @returns SOC_E_NONE 451 * @returns SOC_E_XXX 452 * 453 * @end 454 */ 455 int cmicm_sbusdma_desc_init(int unit, 456 soc_sbusdma_desc_drv_t *drv) 457 { 458 /* Cache multi CMC flag */ 459 if (SOC_CHECK_MULTI_CMC(unit)) { 460 _soc_desc_multi_cmc = 1; 461 } 462 drv->soc_sbusdma_desc_detach = NULL; 463 drv->soc_sbusdma_program = _cmicm_sbusdma_program; 464 drv->soc_sbusdma_desc_create = _cmicm_sbusdma_desc_create; 465 drv->soc_sbusdma_wait = _cmicm_sbusdma_wait; 466 467 return SOC_E_NONE; 468 } 469 470 471 #endif