cmicx_sbusdma_desc.c (14837B)
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: CMICX 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_CMICX_SUPPORT) && defined(BCM_SBUSDMA_SUPPORT) 17 18 #include <soc/cmicx.h> 19 #include <soc/intr_cmicx.h> 20 21 #define SOC_SBUSDMA_FP_RETRIES 200 22 23 typedef struct { /* h/w desc structure */ 24 uint32 cntrl; /* DMA control info */ 25 uint32 req; /* DMA request info (refer h/w spec for details) */ 26 uint32 count; /* DMA count */ 27 uint32 opcode; /* Schan opcode (refer h/w spec for details) */ 28 uint32 addr; /* Schan address */ 29 uint32 hostaddrLo; /* h/w mapped host address Lo*/ 30 uint32 hostaddrHi; /* h/w mapped host address Hi*/ 31 uint32 rsrv; 32 } soc_sbusdma_desc_t; 33 34 35 36 /******************************************* 37 * @function _cmicx_sbusdma_desc_endian_config (private) 38 * purpose Configure endianess of sbus dma descriptor 39 * 40 * @param unit [in] unit 41 * @param desc [in] soc_sbusdma_desc_t pointer 42 * 43 * @returns None 44 * @end 45 */ 46 STATIC void 47 _cmicx_sbusdma_desc_endian_config(int unit, soc_sbusdma_desc_t *desc) 48 { 49 int big_pio, big_packet, big_other; 50 51 soc_endian_get(unit, &big_pio, &big_packet, &big_other); 52 53 if (big_other) { 54 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 55 &(desc->req), HOSTMEMWR_ENDIANESSf, 1); 56 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 57 &(desc->req), HOSTMEMRD_ENDIANESSf, 1); 58 } 59 } 60 61 /******************************************* 62 * @function _cmicx_sbusdma_desc_prepare 63 * purpose API to prepare h/w descriptor 64 * 65 * @param unit [in] unit 66 * @param desc [in] soc_sbusdma_desc_t pointer 67 * @param cfg [in] soc_sbusdma_desc_cfg_t pointer 68 * @param count [in] descriptor count 69 * @param ctrl [in] soc_sbusdma_desc_ctrl_t pointer 70 * @param append [in] last of appended 71 * 72 * @returns SOC_E_NONE 73 * @returns SOC_E_XXX 74 * 75 * @end 76 */ 77 STATIC 78 void _cmicx_sbusdma_desc_prepare(int unit, soc_sbusdma_desc_t *desc, 79 soc_sbusdma_desc_cfg_t *cfg, int count, 80 soc_sbusdma_desc_ctrl_t *ctrl, int append) 81 { 82 sal_vaddr_t hptr = (sal_vaddr_t)(ctrl->buff); 83 schan_msg_t msg; 84 int opcode, dst_blk, acc_type, dlen; 85 uint32 flags; 86 uint32 address = 0; 87 uint32 pend_clock_val = 0; 88 soc_mem_t mem; 89 sal_memset(desc, 0, sizeof(soc_sbusdma_desc_t)); 90 schan_msg_clear(&msg); 91 flags = (ctrl->flags & SOC_SBUSDMA_CFG_USE_FLAGS) ? \ 92 cfg->flags : ctrl->flags; 93 94 if (flags & SOC_SBUSDMA_WRITE_CMD_MSG) { 95 opcode = (flags & SOC_SBUSDMA_MEMORY_CMD_MSG) ? 96 WRITE_MEMORY_CMD_MSG : WRITE_REGISTER_CMD_MSG; 97 dlen = (flags & SOC_SBUSDMA_MEMORY_CMD_MSG) ? 98 (sizeof(uint32) * cfg->width) : sizeof(uint32); 99 } else { 100 opcode = (flags & SOC_SBUSDMA_MEMORY_CMD_MSG) ? 101 READ_MEMORY_CMD_MSG : READ_REGISTER_CMD_MSG; 102 dlen = 0; 103 } 104 acc_type = cfg->acc_type; 105 dst_blk = cfg->blk; 106 soc_schan_header_cmd_set(unit, &msg.header, opcode, dst_blk, 0, 107 acc_type, dlen, 0, 0); 108 109 110 desc->count = cfg->count; 111 desc->opcode = msg.dwords[0]; 112 desc->addr = cfg->addr; 113 114 /* Prepare h/w desc */ 115 if ((ctrl->cfg_count == 1) || (count == ctrl->cfg_count - 1)) { 116 desc->cntrl |= SOC_SBUSDMA_CTRL_LAST; 117 } 118 if (append && count) { 119 desc->cntrl |= SOC_SBUSDMA_CTRL_APND; 120 } 121 /* Set the descriptor remaining in prefetch is enabled */ 122 if (flags & SOC_SBUSDMA_CFG_PREFETCH_ENB) { 123 desc->cntrl |= ((ctrl->cfg_count - 1 - count) << 124 SOC_SBUSDMA_CTRL_DSEC_REMAIN_SHIFT); 125 } 126 127 if (ctrl->cfg_count == 1) { 128 desc->hostaddrLo = PTR_TO_INT(soc_cm_l2p(unit, cfg->buff)); 129 desc->hostaddrHi = PTR_HI_TO_INT(soc_cm_l2p(unit, cfg->buff)); 130 if (soc_cm_get_bus_type(unit) & SOC_PCI_DEV_TYPE) { 131 desc->hostaddrHi |= CMIC_PCIE_SO_OFFSET; 132 } 133 } else { 134 if (append) { 135 desc->hostaddrLo = PTR_TO_INT(soc_cm_l2p(unit, ctrl->buff)); 136 desc->hostaddrHi = PTR_HI_TO_INT(soc_cm_l2p(unit, ctrl->buff)); 137 if (soc_cm_get_bus_type(unit) & SOC_PCI_DEV_TYPE) { 138 desc->hostaddrHi |= CMIC_PCIE_SO_OFFSET; 139 } 140 ctrl->buff = (void *)(hptr + (cfg->count * cfg->width)); 141 } else { 142 desc->hostaddrLo = PTR_TO_INT(soc_cm_l2p(unit, cfg->buff)); 143 desc->hostaddrHi = PTR_HI_TO_INT(soc_cm_l2p(unit, cfg->buff)); 144 if (soc_cm_get_bus_type(unit) & SOC_PCI_DEV_TYPE) { 145 desc->hostaddrHi |= CMIC_PCIE_SO_OFFSET; 146 } 147 } 148 } 149 address = desc->addr; 150 151 mem = soc_addr_to_mem_extended(unit, -1, -1, address); 152 153 if ((opcode == WRITE_MEMORY_CMD_MSG) || 154 (opcode == WRITE_REGISTER_CMD_MSG)){ 155 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 156 &(desc->req), REQ_WORDSf, cfg->width); 157 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 158 &(desc->req), REP_WORDSf, 0); 159 if (cfg->mem_clear) { 160 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, &(desc->req), 161 REQ_SINGLEf, 1); 162 } else { 163 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, &(desc->req), 164 REQ_SINGLEf, 0); 165 } 166 } else { 167 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 168 &(desc->req), REQ_WORDSf, 0); 169 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 170 &(desc->req), REP_WORDSf, cfg->width); 171 172 if (flags & SOC_SBUSDMA_CFG_MOR) { 173 if (mem > 0) { 174 if (SOC_MEM_INFO(unit, mem).cmic_mor_clks) { 175 pend_clock_val = SOC_MEM_INFO(unit, mem).cmic_mor_clks; 176 } 177 } 178 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 179 &(desc->req), IGNORE_SBUS_EARLYACKf, 1); 180 if (pend_clock_val != 0) { 181 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 182 &(desc->req), PEND_CLOCKSf, pend_clock_val); 183 } else { 184 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 185 &(desc->req), PEND_CLOCKSf, 30); 186 } 187 } 188 } 189 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_REQUESTr, 190 &(desc->req), INCR_SHIFTf, cfg->addr_shift); 191 192 _cmicx_sbusdma_desc_endian_config(unit, desc); 193 194 return; 195 } 196 197 /******************************************* 198 * @function _cmicx_sbusdma_desc_create 199 * purpose API to prepare h/w descriptor 200 * 201 * @param unit [in] unit 202 * @param desc [in] soc_sbusdma_desc_ctrl_t pointer 203 * @param cfg [in] soc_sbusdma_desc_cfg_t pointer 204 * @param desc [out] descriptor pointer 205 * 206 * @returns SOC_E_NONE 207 * @returns SOC_E_XXX 208 * 209 * @end 210 */ 211 STATIC 212 int _cmicx_sbusdma_desc_create(int unit, soc_sbusdma_desc_ctrl_t *ctrl, 213 soc_sbusdma_desc_cfg_t *cfg, void **desc) 214 { 215 soc_sbusdma_desc_t *dma_desc; 216 uint8 append = 0; 217 sal_vaddr_t hptr = (sal_vaddr_t)(ctrl->buff); 218 uint32 count; 219 220 if (ctrl->cfg_count == 1) { /* single mode */ 221 dma_desc = soc_cm_salloc(unit, sizeof(soc_sbusdma_desc_t), 222 "soc_sbusdma_desc_t"); 223 if (dma_desc == NULL) { 224 return SOC_E_MEMORY; 225 } 226 _cmicx_sbusdma_desc_prepare(unit, dma_desc, cfg, 0, ctrl, 0); 227 } else { 228 if (ctrl->buff) { 229 append = 1; 230 } 231 dma_desc = soc_cm_salloc(unit, sizeof(soc_sbusdma_desc_t) * 232 ctrl->cfg_count, "soc_sbusdma_desc_t"); 233 if (dma_desc == NULL) { 234 return SOC_E_MEMORY; 235 } 236 for (count = 0; count < ctrl->cfg_count; count++) { 237 _cmicx_sbusdma_desc_prepare(unit, &dma_desc[count], &cfg[count], 238 count, ctrl, append); 239 } 240 ctrl->buff = (void *)hptr; 241 } 242 *desc = dma_desc; 243 244 return SOC_E_NONE; 245 } 246 247 /******************************************* 248 * @function _cmicx_sbusdma_wait 249 * purpose API to wait Descriptor DMA complete 250 * 251 * @param unit [in] unit 252 * @param cmc [in] cmc 253 * @param ch [in] channel 254 * @param interval [in] time interval 255 * @param intrEnb [in] interrupt enable 256 * 257 * @returns SOC_E_NONE 258 * @returns SOC_E_XXX 259 * 260 * @end 261 */ 262 STATIC int 263 _cmicx_sbusdma_wait(int unit, int cmc, int ch, int interval, uint8 intrEnb) 264 { 265 int rv = SOC_E_TIMEOUT; 266 int rval; 267 uint32 ctrl; 268 269 if (intrEnb) { 270 rv = cmicx_sbusdma_intr_wait(unit, cmc, ch, interval, 0); 271 if (rv != SOC_E_TIMEOUT) { 272 SOC_CONTROL(unit)->stat.intr_desc++; 273 } 274 } else { 275 rv = cmicx_sbusdma_poll_wait(unit, cmc, ch, interval, 0); 276 } 277 278 if (rv != SOC_E_NONE) { 279 if (rv != SOC_E_TIMEOUT) { 280 LOG_ERROR(BSL_LS_SOC_COMMON, 281 (BSL_META_U(unit, 282 "failed(ERR)\n"))); 283 cmicx_sbusdma_error_details(unit, 284 soc_pci_read(unit, CMIC_CMCx_SBUSDMA_CHy_STATUS(cmc, ch))); 285 } else { /* Timeout cleanup */ 286 soc_timeout_t to; 287 288 LOG_ERROR(BSL_LS_SOC_COMMON, 289 (BSL_META_U(unit, 290 "%s timeout\n"), 291 intrEnb ? 292 "interrupt" : "polling")); 293 /* Abort DMA */ 294 ctrl = soc_pci_read(unit, 295 CMIC_CMCx_SBUSDMA_CHy_CONTROL(cmc, ch)); 296 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_CONTROLr, 297 &ctrl, ABORTf, 1); 298 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_CONTROL(cmc, ch), 299 ctrl); 300 301 /* Check the done bit to confirm */ 302 soc_timeout_init(&to, interval, 0); 303 while (1) { 304 rval = soc_pci_read(unit, 305 CMIC_CMCx_SBUSDMA_CHy_STATUS(cmc, ch)); 306 if (soc_reg_field_get(unit, CMIC_CMC0_SBUSDMA_CH0_STATUSr, 307 rval, DONEf)) { 308 break; 309 } 310 if (soc_timeout_check(&to)) { 311 LOG_ERROR(BSL_LS_SOC_COMMON, 312 (BSL_META_U(unit, 313 "_cmicx_sbusdma_desc: Abort Failed\n"))); 314 break; 315 } 316 } 317 } 318 } /* rv != SOC_E_NONE */ 319 320 /* Stop and free the channel */ 321 cmicx_sbusdma_stop(unit, cmc, ch); 322 323 cmicx_sbusdma_ch_put(unit, cmc, ch); 324 325 return rv; 326 } 327 328 /******************************************* 329 * @function _cmicx_sbusdma_program 330 * purpose API to program Descriptor DMA 331 * 332 * @param unit [in] unit 333 * @param cmc [out] cmc 334 * @param swd [out] _soc_sbusdma_state_t pointer 335 * @param ch [out] channel 336 * 337 * @returns SOC_E_NONE 338 * @returns SOC_E_XXX 339 * 340 * @end 341 */ 342 STATIC int 343 _cmicx_sbusdma_program(int unit, int *cmc, _soc_sbusdma_state_t *swd, int *ch) 344 { 345 uint32 ctrl; 346 int i; 347 soc_sbusdma_desc_t *desc = (soc_sbusdma_desc_t *)swd->desc; 348 349 SOC_IF_ERROR_RETURN(cmicx_sbusdma_ch_get(unit, cmc, ch)); 350 351 LOG_VERBOSE(BSL_LS_SOC_DMA, 352 (BSL_META_U(unit, 353 "cmc = %d, channel = %d\n"), 354 *cmc, *ch)); 355 356 assert((*ch >= 0) && (*ch < CMIC_CMCx_SBUSDMA_CHAN_MAX)); 357 ctrl = soc_pci_read(unit, CMIC_CMCx_SBUSDMA_CHy_CONTROL(*cmc, *ch)); 358 359 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_CONTROLr, &ctrl, 360 MODEf, 1); 361 362 if (swd->ctrl.flags & SOC_SBUSDMA_CFG_PREFETCH_ENB) { 363 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_CONTROLr, &ctrl, 364 DESC_PREFETCH_ENABLEf, 1); 365 } 366 367 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_CONTROL(*cmc, *ch), ctrl); 368 369 /* write desc Lo address */ 370 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_DESCADDR_LO(*cmc, *ch), 371 PTR_TO_INT(soc_cm_l2p(unit, swd->desc))); 372 /* write desc Hi address */ 373 if (soc_cm_get_bus_type(unit) & SOC_PCI_DEV_TYPE) { 374 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_DESCADDR_HI(*cmc, *ch), 375 (PTR_HI_TO_INT(soc_cm_l2p(unit, swd->desc)) | 376 CMIC_PCIE_SO_OFFSET)); 377 } else { 378 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_DESCADDR_HI(*cmc, *ch), 379 PTR_HI_TO_INT(soc_cm_l2p(unit, swd->desc))); 380 } 381 /* Debug stuff */ 382 LOG_VERBOSE(BSL_LS_SOC_DMA, 383 (BSL_META_U(unit, 384 "desc = %x, Count: %d\n"), 385 PTR_TO_INT(soc_cm_l2p(unit, swd->desc)), 386 swd->ctrl.cfg_count)); 387 for (i = 0; i < swd->ctrl.cfg_count; i++) { 388 LOG_VERBOSE(BSL_LS_SOC_DMA, 389 (BSL_META_U(unit, 390 "cntrl: %08x, req: %08x, count: %08x, " 391 "opcode: %08x, saddr: %08x, haddr: %08x\n"), 392 desc[i].cntrl, desc[i].req, 393 desc[i].count, desc[i].opcode, 394 desc[i].addr, desc[i].hostaddrLo)); 395 } 396 397 return SOC_E_NONE; 398 } 399 400 /******************************************* 401 * @function cmicx_sbusdma_desc_init 402 * purpose API to Initialize cmicx Descriptor DMA 403 * 404 * @param unit [in] unit 405 * @param drv [out] soc_sbusdma_desc_drv_t pointer 406 * 407 * @returns SOC_E_NONE 408 * @returns SOC_E_XXX 409 * 410 * @end 411 */ 412 int cmicx_sbusdma_desc_init(int unit, 413 soc_sbusdma_desc_drv_t *drv) 414 { 415 int cmc, ch; 416 uint32 val; 417 int big_pio, big_packet, big_other; 418 419 /* configure descriptor endianess */ 420 soc_endian_get(unit, &big_pio, &big_packet, &big_other); 421 422 if (big_other) { 423 for (cmc = 0; cmc < CMIC_CMC_NUM_MAX; cmc++) { 424 for (ch = 0 ; ch < CMIC_CMCx_SBUSDMA_CHAN_MAX; ch++) { 425 val = soc_pci_read(unit, CMIC_CMCx_SBUSDMA_CHy_CONTROL(cmc, ch)); 426 soc_reg_field_set(unit, CMIC_CMC0_SBUSDMA_CH0_CONTROLr, 427 &val, DESCRIPTOR_ENDIANESSf, 1); 428 soc_pci_write(unit, CMIC_CMCx_SBUSDMA_CHy_CONTROL(cmc, ch), val); 429 } 430 } 431 } 432 drv->soc_sbusdma_desc_detach = NULL; 433 drv->soc_sbusdma_program = _cmicx_sbusdma_program; 434 drv->soc_sbusdma_desc_create = _cmicx_sbusdma_desc_create; 435 drv->soc_sbusdma_wait = _cmicx_sbusdma_wait; 436 437 return SOC_E_NONE; 438 } 439 440 441 #endif