cmicx_ccmdma.c (13582B)
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 CCM DMA Driver 8 */ 9 10 #include <shared/bsl.h> 11 #include <shared/alloc.h> 12 #include <soc/drv.h> 13 #include <soc/debug.h> 14 #include <soc/cm.h> 15 #include <soc/ccmdma_internal.h> 16 17 #if defined(BCM_CMICX_SUPPORT) && defined(BCM_CCMDMA_SUPPORT) 18 #include <soc/cmicx.h> 19 #endif 20 21 #if defined(BCM_ESW_SUPPORT) || \ 22 defined(BCM_PETRA_SUPPORT) || defined(BCM_DFE_SUPPORT) 23 #if defined(BCM_CMICX_SUPPORT) && defined(BCM_CCMDMA_SUPPORT) 24 25 typedef struct cmicx_ccmdma_ch_s { 26 sal_spinlock_t lock[SOC_MAX_NUM_DEVICES][CMIC_CMC_NUM_MAX]; 27 int timeout; 28 uint32 ch[SOC_MAX_NUM_DEVICES][CMIC_CMC_NUM_MAX]; 29 }cmicx_ccmdma_ch_t; 30 31 STATIC cmicx_ccmdma_ch_t _cmicx_ccmdma_ch; 32 33 /******************************************* 34 * @function _cmicx_ccmdma_ch_get 35 * purpose get idle DMA channel 36 * 37 * @param unit [in] unit # 38 * @param vchan [out] channel number 39 * 40 * @returns SOC_E_BUSY 41 * @returns SOC_E_NONE 42 * 43 * @end 44 */ 45 static int 46 _cmicx_ccmdma_ch_get(int unit, int *vchan) 47 { 48 int rv = SOC_E_BUSY; 49 int j, ch; 50 soc_timeout_t to; 51 52 soc_timeout_init(&to, _cmicx_ccmdma_ch.timeout, 10); 53 do { 54 for(j = 0; j < CMIC_CMC_NUM_MAX; j++) { 55 sal_spinlock_lock(_cmicx_ccmdma_ch.lock[unit][j]); 56 for(ch = 0; ch < CMICX_N_CCMDMA_CHAN; ch++) { 57 if(_cmicx_ccmdma_ch.ch[unit][j] & (1 << ch)) { 58 *vchan = (j * CMICX_N_CCMDMA_CHAN) + ch; 59 _cmicx_ccmdma_ch.ch[unit][j] &= ~(1 << ch); 60 rv = SOC_E_NONE; 61 break; 62 } 63 } 64 sal_spinlock_unlock(_cmicx_ccmdma_ch.lock[unit][j]); 65 } 66 if (rv == SOC_E_NONE) { 67 break; 68 } 69 } while (!soc_timeout_check(&to)); 70 return rv; 71 } 72 73 /******************************************* 74 * @function _cmicx_ccmdma_ch_put 75 * purpose put back the channel on free list 76 * 77 * @param ch [in] channel number 78 * 79 * @returns SOC_E_PARAM 80 * @returns SOC_E_NONE 81 * 82 * @end 83 */ 84 static int 85 _cmicx_ccmdma_ch_put(int unit, int vchan) 86 { 87 int cmc, ch; 88 cmc = vchan / CMICX_N_CCMDMA_CHAN; 89 ch = vchan % CMICX_N_CCMDMA_CHAN; 90 sal_spinlock_lock(_cmicx_ccmdma_ch.lock[unit][cmc]); 91 _cmicx_ccmdma_ch.ch[unit][cmc] |= 1 << ch; 92 sal_spinlock_unlock(_cmicx_ccmdma_ch.lock[unit][cmc]); 93 return SOC_E_NONE; 94 } 95 96 /******************************************* 97 * @function _cmicx_ccmdma_copy_wait 98 * purpose to initiate DMA from source to 99 * destination memory on vchan 100 * @param vchan [in] channel number 101 * 102 * @returns SOC_E_XXXX 103 * @returns SOC_E_NONE 104 * 105 * @end 106 */ 107 static int 108 _cmicx_ccmdma_copy_wait(int unit, int vchan) 109 { 110 int cmc, ch; 111 int rv = SOC_E_TIMEOUT; 112 soc_control_t *soc = SOC_CONTROL(unit); 113 unsigned int reg; 114 115 cmc = vchan / CMICX_N_CCMDMA_CHAN; 116 ch = vchan % CMICX_N_CCMDMA_CHAN; 117 118 CCM_DMA_LOCK(unit, cmc); 119 120 if (soc->ccmDmaIntrEnb) { 121 if (sal_sem_take(soc->ccmDmaIntr[cmc], soc->ccmDmaTimeout) < 0) { 122 rv = SOC_E_TIMEOUT; 123 } 124 reg = soc_pci_read(unit, CMIC_CMCx_CCMDMA_CHy_STAT_OFFSET(cmc, ch)); 125 if (soc_reg_field_get(unit, CMIC_CMC0_CCMDMA_CH0_STATr, 126 reg, DONEf)) { 127 rv = SOC_E_NONE; 128 if (soc_reg_field_get(unit, CMIC_CMC0_CCMDMA_CH0_STATr, 129 reg, ERRORf)) { 130 rv = SOC_E_FAIL; 131 } 132 } 133 } else { 134 soc_timeout_t to; 135 LOG_DEBUG(BSL_LS_SOC_SOCMEM, 136 (BSL_META_U(unit, 137 "using Polling mode for CCM DMA\n"))); 138 soc_timeout_init(&to, soc->ccmDmaTimeout, 10000); 139 do { 140 reg = soc_pci_read(unit, CMIC_CMCx_CCMDMA_CHy_STAT_OFFSET(cmc, ch)); 141 if (soc_reg_field_get(unit, CMIC_CMC0_CCMDMA_CH0_STATr, 142 reg, DONEf)) { 143 rv = SOC_E_NONE; 144 if (soc_reg_field_get(unit, CMIC_CMC0_CCMDMA_CH0_STATr, 145 reg, ERRORf)) { 146 rv = SOC_E_FAIL; 147 } 148 break; 149 } 150 } while(!(soc_timeout_check(&to))); 151 } 152 153 if (rv == SOC_E_TIMEOUT) { 154 LOG_ERROR(BSL_LS_SOC_SOCMEM, 155 (BSL_META_U(unit, 156 "CcmDmaTimeout: unit %d, ccm_dma timeout\n"), unit)); 157 158 /* Abort CCM DMA */ 159 160 /* Dummy read to allow abort to finish */ 161 162 /* Disable CCM DMA */ 163 164 /* Clear ccm DMA abort bit */ 165 } 166 CCM_DMA_UNLOCK(unit, cmc); 167 168 return rv; 169 170 } 171 172 /******************************************* 173 * @function _cmicx_ccmdma_endian_config 174 * purpose Configure endianess ccmdma engine 175 * 176 * @param unit [in] unit 177 * @param val [in] uint32 pointer 178 * 179 * @returns None 180 * @end 181 */ 182 STATIC void 183 _cmicx_ccmdma_endian_config(int unit, 184 unsigned int src_is_internal, 185 unsigned int dst_is_internal, 186 uint32 *val) 187 { 188 int big_pio, big_packet, big_other; 189 190 soc_endian_get(unit, &big_pio, &big_packet, &big_other); 191 192 if (big_other & !src_is_internal) { 193 soc_reg_field_set(unit, CMIC_CMC0_CCMDMA_CH0_CFGr, val, PROCESSOR0_ENDIANESSf, 1); 194 } else { 195 soc_reg_field_set(unit, CMIC_CMC0_CCMDMA_CH0_CFGr, val, PROCESSOR0_ENDIANESSf, 0); 196 } 197 if (big_other & !dst_is_internal) { 198 soc_reg_field_set(unit, CMIC_CMC0_CCMDMA_CH0_CFGr, val, PROCESSOR1_ENDIANESSf, 1); 199 } else { 200 soc_reg_field_set(unit, CMIC_CMC0_CCMDMA_CH0_CFGr, val, PROCESSOR1_ENDIANESSf, 0); 201 } 202 } 203 204 205 /******************************************* 206 * @function _cmicx_ccmdma_copy 207 * purpose to initiate DMA from source to 208 * destination memory on vchan 209 * @param srcbuf [in] pointer to source memory 210 * @param dstbuf [in] pointer to dest memory 211 * @param count [in] number of bytes to xfer 212 * @param flags [in] 213 * @param vchan [in] channel number 214 * 215 * @returns SOC_E_XXXX 216 * @returns SOC_E_NONE 217 * 218 * @end 219 */ 220 static int 221 _cmicx_ccmdma_copy(int unit, sal_paddr_t *srcbuf, sal_paddr_t *dstbuf, 222 unsigned int src_is_internal, unsigned int dst_is_internal, 223 int count, int flags, int vchan) 224 { 225 int i, rv = SOC_E_NONE; 226 soc_control_t *soc = SOC_CONTROL(unit); 227 unsigned int *srcptr = (unsigned int *)srcbuf; 228 unsigned int *dstptr = (unsigned int *)dstbuf; 229 unsigned int reg, reg2; 230 int cmc, ch; 231 unsigned int offset = 0; 232 sal_paddr_t addr; 233 234 /* If src/dst is internal, then the corresponding srcbuf/dstbuf 235 * should be a Physical Address. If external, then Logical Address. 236 */ 237 238 srcptr = (unsigned int *)srcbuf; 239 dstptr = (unsigned int *)dstbuf; 240 241 assert (srcptr && dstptr); 242 243 cmc = vchan / CMICX_N_CCMDMA_CHAN; 244 ch = vchan % CMICX_N_CCMDMA_CHAN; 245 246 if (soc->ccmDmaMutex[cmc] == 0) { 247 /* If DMA not enabled, or short length use PIO to copy */ 248 for (i = 0; i < count; i ++ ) { 249 if (flags & 2) { /* Read from PCI */ 250 reg = soc_cm_iproc_read(unit, PTR_TO_INT(srcptr)); 251 } else { 252 reg = *srcptr; 253 } 254 255 if (flags & 1) { 256 reg = ((reg & 0xff000000) >> 24) | 257 ((reg & 0x00ff0000) >> 8) | 258 ((reg & 0x0000ff00) << 8) | 259 ((reg & 0x000000ff) << 24); 260 } 261 262 if (flags & 2) { 263 *dstptr = reg; 264 } else { 265 soc_cm_iproc_write(unit, PTR_TO_INT(dstptr), reg); 266 reg2 = soc_cm_iproc_read(unit, PTR_TO_INT(dstptr)); 267 if (reg2 != reg) { 268 LOG_ERROR(BSL_LS_SOC_SOCMEM, 269 (BSL_META_U(unit, 270 "ccm_dma: compare error %x (%x %x)\n"), 271 PTR_TO_INT(dstptr), reg, reg2)); 272 } 273 } 274 275 dstptr++; 276 srcptr++; 277 } 278 return SOC_E_NONE; 279 } 280 281 CCM_DMA_LOCK(unit, cmc); 282 283 if (src_is_internal) { 284 offset = 0; 285 #if !defined(PTRS_ARE_64BITS) && defined(PHYS_ADDRS_ARE_64BITS) 286 addr = (uint32) srcbuf; 287 #else 288 addr = (sal_paddr_t) srcbuf; 289 #endif 290 } else { 291 if (soc_cm_get_bus_type(unit) & SOC_PCI_DEV_TYPE) { 292 offset = 0x10000000; 293 } 294 addr = soc_cm_l2p(unit, srcbuf); 295 } 296 297 soc_pci_write(unit, CMIC_CMCx_CCMDMA_CHy_HOST0_MEM_START_ADDR_LO_OFFSET(cmc, ch), PTR_TO_INT(addr)); 298 soc_pci_write(unit, CMIC_CMCx_CCMDMA_CHy_HOST0_MEM_START_ADDR_HI_OFFSET(cmc, ch), offset | PTR_HI_TO_INT(addr)); 299 300 if (dst_is_internal) { 301 offset = 0; 302 #if !defined(PTRS_ARE_64BITS) && defined(PHYS_ADDRS_ARE_64BITS) 303 addr = (uint32) dstbuf; 304 #else 305 addr = (sal_paddr_t) dstbuf; 306 #endif 307 } else { 308 if (soc_cm_get_bus_type(unit) & SOC_PCI_DEV_TYPE) { 309 offset = 0x10000000; 310 } 311 addr = soc_cm_l2p(unit, dstbuf); 312 } 313 314 soc_pci_write(unit, CMIC_CMCx_CCMDMA_CHy_HOST1_MEM_START_ADDR_LO_OFFSET(cmc, ch), PTR_TO_INT(addr)); 315 soc_pci_write(unit, CMIC_CMCx_CCMDMA_CHy_HOST1_MEM_START_ADDR_HI_OFFSET(cmc, ch), offset | PTR_HI_TO_INT(addr)); 316 317 soc_pci_write(unit, CMIC_CMCx_CCMDMA_CHy_ENTRY_COUNT_OFFSET(cmc, ch), count); 318 319 reg = soc_pci_read(unit, CMIC_CMCx_CCMDMA_CHy_CFG_OFFSET(cmc, ch)); 320 soc_reg_field_set(unit, CMIC_CMC0_CCMDMA_CH0_CFGr, ®, ABORTf, 0); 321 _cmicx_ccmdma_endian_config(unit, src_is_internal, dst_is_internal, ®); 322 soc_reg_field_set(unit, CMIC_CMC0_CCMDMA_CH0_CFGr, ®, ENf, 0); 323 soc_pci_write(unit, CMIC_CMCx_CCMDMA_CHy_CFG_OFFSET(cmc, ch), reg); /* Clearing EN clears stats */ 324 325 /* Start DMA */ 326 soc_reg_field_set(unit, CMIC_CMC0_CCMDMA_CH0_CFGr, ®, ENf, 1); 327 soc_pci_write(unit, CMIC_CMCx_CCMDMA_CHy_CFG_OFFSET(cmc, ch), reg); 328 329 CCM_DMA_UNLOCK(unit, cmc); 330 /* Don't wait for DMA Complete if flag bit 0x100 is set */ 331 if (!(flags & 4)) { 332 rv = _cmicx_ccmdma_copy_wait(unit, vchan); 333 } 334 return rv; 335 336 } 337 338 /******************************************* 339 * @function _cmicx_ccmdma_abort 340 * purpose to abort active DMA operation on 341 * given channel 342 * @param vchan [in] channel number 343 * 344 * @returns SOC_E_XXXX 345 * @returns SOC_E_NONE 346 * 347 * @end 348 */ 349 static int 350 _cmicx_ccmdma_abort(int unit, int vchan) 351 { 352 int rv = SOC_E_UNAVAIL; 353 354 return rv; 355 } 356 357 /******************************************* 358 * @function cmicx_ccmdma_clean 359 * purpose to clean 360 * @param max_cmc [in] number of cmc 361 * 362 * @returns SOC_E_XXXX 363 * @returns SOC_E_NONE 364 * 365 * @end 366 */ 367 static int 368 _cmicx_ccmdma_clean(int unit, int max_cmc) 369 { 370 int rv = 0; 371 uint32 cfg_dw; 372 int cmc, chan; 373 374 for (cmc = 0; cmc < max_cmc; cmc++) { 375 for (chan = 0; chan < CMICX_N_CCMDMA_CHAN; chan++) { 376 cfg_dw = soc_pci_read(unit, CMIC_CMCx_CCMDMA_CHy_CFG_OFFSET(cmc, chan)); 377 soc_reg_field_set(unit, CMIC_CMC0_CCMDMA_CH0_CFGr, &cfg_dw, ABORTf, 0); 378 soc_pci_write(unit, CMIC_CMCx_CCMDMA_CHy_CFG_OFFSET(cmc, chan), cfg_dw); 379 } 380 } 381 382 sal_usleep(1000); 383 384 for (cmc = 0; cmc < max_cmc; cmc++) { 385 for (chan = 0; chan < CMICX_N_CCMDMA_CHAN; chan++) { 386 soc_pci_write(unit, CMIC_CMCx_CCMDMA_CHy_CFG_OFFSET(cmc, chan), 0x00000000); 387 } 388 } 389 390 return rv; 391 } 392 393 /******************************************* 394 * @function cmicx_ccmdma_init 395 * purpose API to Initialize cmicx CCM DMA 396 * 397 * @param unit [in] unit 398 * @param drv [out] soc_ccmdma_drv_t pointer 399 * 400 * @returns SOC_E_NONE 401 * @returns SOC_E_XXX 402 * 403 * @end 404 */ 405 int cmicx_ccmdma_init(int unit, soc_ccmdma_drv_t *drv) 406 { 407 int rv = SOC_E_NONE; 408 int j; 409 410 for(j = 0; j < CMIC_CMC_NUM_MAX; j++) { 411 _cmicx_ccmdma_ch.lock[unit][j] = sal_spinlock_create("ccmdma Lock"); 412 if (_cmicx_ccmdma_ch.lock[unit][j] == NULL) { 413 rv = SOC_E_MEMORY; 414 goto cleanup_exit; 415 } 416 if(j < SOC_PCI_CMCS_NUM(unit)) { 417 _cmicx_ccmdma_ch.ch[unit][j] = 0x3; /* Two CCMDMA channels/CMC */ 418 } 419 } 420 421 _cmicx_ccmdma_ch.timeout = SAL_BOOT_QUICKTURN ? CCMDMA_TIMEOUT_QT : CCMDMA_TIMEOUT; 422 drv->soc_ccmdma_ch_get = _cmicx_ccmdma_ch_get; 423 drv->soc_ccmdma_ch_put = _cmicx_ccmdma_ch_put; 424 drv->soc_ccmdma_copy = _cmicx_ccmdma_copy; 425 drv->soc_ccmdma_copy_wait = _cmicx_ccmdma_copy_wait; 426 drv->soc_ccmdma_abort = _cmicx_ccmdma_abort; 427 drv->soc_ccmdma_clean = _cmicx_ccmdma_clean; 428 return SOC_E_NONE; 429 430 cleanup_exit: 431 for(j = 0; j < CMIC_CMC_NUM_MAX; j++) { 432 if(_cmicx_ccmdma_ch.lock[unit][j] != NULL) { 433 sal_spinlock_destroy(_cmicx_ccmdma_ch.lock[unit][j]); 434 } 435 } 436 return(rv); 437 } 438 439 /******************************************* 440 * @function cmicx_ccmdma_deinit 441 * purpose API to Deinitialize cmicx CCM DMA 442 * 443 * @param unit [in] unit 444 * @param drv [in] soc_ccmdma_drv_t pointer 445 * 446 * @returns SOC_E_NONE 447 448 * 449 * @end 450 */ 451 int cmicx_ccmdma_deinit(int unit, soc_ccmdma_drv_t *drv) 452 { 453 int j; 454 455 drv->soc_ccmdma_ch_get = NULL; 456 drv->soc_ccmdma_ch_put = NULL; 457 drv->soc_ccmdma_copy = NULL; 458 drv->soc_ccmdma_abort = NULL; 459 460 for(j = 0; j < CMIC_CMC_NUM_MAX; j++) { 461 if(_cmicx_ccmdma_ch.lock[unit][j] != NULL) { 462 (void)sal_spinlock_destroy(_cmicx_ccmdma_ch.lock[unit][j]); 463 } 464 } 465 return SOC_E_NONE; 466 } 467 468 #endif /* BCM_CCMDMA_SUPPORT */ 469 #endif /* BCM_ESW_SUPPORT */ 470