162306a36Sopenharmony_ci/***********************license start*************** 262306a36Sopenharmony_ci * Author: Cavium Networks 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Contact: support@caviumnetworks.com 562306a36Sopenharmony_ci * This file is part of the OCTEON SDK 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2003-2017 Cavium, Inc. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file is free software; you can redistribute it and/or modify 1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as 1162306a36Sopenharmony_ci * published by the Free Software Foundation. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 1462306a36Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty 1562306a36Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or 1662306a36Sopenharmony_ci * NONINFRINGEMENT. See the GNU General Public License for more 1762306a36Sopenharmony_ci * details. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 2062306a36Sopenharmony_ci * along with this file; if not, write to the Free Software 2162306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 2262306a36Sopenharmony_ci * or visit http://www.gnu.org/licenses/. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * This file may also be available under a different license from Cavium. 2562306a36Sopenharmony_ci * Contact Cavium Networks for more information 2662306a36Sopenharmony_ci ***********************license end**************************************/ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Implementation of the Level 2 Cache (L2C) control, 3062306a36Sopenharmony_ci * measurement, and debugging facilities. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/compiler.h> 3462306a36Sopenharmony_ci#include <linux/irqflags.h> 3562306a36Sopenharmony_ci#include <asm/octeon/cvmx.h> 3662306a36Sopenharmony_ci#include <asm/octeon/cvmx-l2c.h> 3762306a36Sopenharmony_ci#include <asm/octeon/cvmx-spinlock.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * This spinlock is used internally to ensure that only one core is 4162306a36Sopenharmony_ci * performing certain L2 operations at a time. 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * NOTE: This only protects calls from within a single application - 4462306a36Sopenharmony_ci * if multiple applications or operating systems are running, then it 4562306a36Sopenharmony_ci * is up to the user program to coordinate between them. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic cvmx_spinlock_t cvmx_l2c_spinlock; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciint cvmx_l2c_get_core_way_partition(uint32_t core) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci uint32_t field; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* Validate the core number */ 5462306a36Sopenharmony_ci if (core >= cvmx_octeon_num_cores()) 5562306a36Sopenharmony_ci return -1; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) 5862306a36Sopenharmony_ci return cvmx_read_csr(CVMX_L2C_WPAR_PPX(core)) & 0xffff; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* 6162306a36Sopenharmony_ci * Use the lower two bits of the coreNumber to determine the 6262306a36Sopenharmony_ci * bit offset of the UMSK[] field in the L2C_SPAR register. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci field = (core & 0x3) * 8; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* 6762306a36Sopenharmony_ci * Return the UMSK[] field from the appropriate L2C_SPAR 6862306a36Sopenharmony_ci * register based on the coreNumber. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci switch (core & 0xC) { 7262306a36Sopenharmony_ci case 0x0: 7362306a36Sopenharmony_ci return (cvmx_read_csr(CVMX_L2C_SPAR0) & (0xFF << field)) >> field; 7462306a36Sopenharmony_ci case 0x4: 7562306a36Sopenharmony_ci return (cvmx_read_csr(CVMX_L2C_SPAR1) & (0xFF << field)) >> field; 7662306a36Sopenharmony_ci case 0x8: 7762306a36Sopenharmony_ci return (cvmx_read_csr(CVMX_L2C_SPAR2) & (0xFF << field)) >> field; 7862306a36Sopenharmony_ci case 0xC: 7962306a36Sopenharmony_ci return (cvmx_read_csr(CVMX_L2C_SPAR3) & (0xFF << field)) >> field; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciint cvmx_l2c_set_core_way_partition(uint32_t core, uint32_t mask) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci uint32_t field; 8762306a36Sopenharmony_ci uint32_t valid_mask; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci valid_mask = (0x1 << cvmx_l2c_get_num_assoc()) - 1; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci mask &= valid_mask; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* A UMSK setting which blocks all L2C Ways is an error on some chips */ 9462306a36Sopenharmony_ci if (mask == valid_mask && !OCTEON_IS_MODEL(OCTEON_CN63XX)) 9562306a36Sopenharmony_ci return -1; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Validate the core number */ 9862306a36Sopenharmony_ci if (core >= cvmx_octeon_num_cores()) 9962306a36Sopenharmony_ci return -1; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { 10262306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_WPAR_PPX(core), mask); 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * Use the lower two bits of core to determine the bit offset of the 10862306a36Sopenharmony_ci * UMSK[] field in the L2C_SPAR register. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci field = (core & 0x3) * 8; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* 11362306a36Sopenharmony_ci * Assign the new mask setting to the UMSK[] field in the appropriate 11462306a36Sopenharmony_ci * L2C_SPAR register based on the core_num. 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci switch (core & 0xC) { 11862306a36Sopenharmony_ci case 0x0: 11962306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_SPAR0, 12062306a36Sopenharmony_ci (cvmx_read_csr(CVMX_L2C_SPAR0) & ~(0xFF << field)) | 12162306a36Sopenharmony_ci mask << field); 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci case 0x4: 12462306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_SPAR1, 12562306a36Sopenharmony_ci (cvmx_read_csr(CVMX_L2C_SPAR1) & ~(0xFF << field)) | 12662306a36Sopenharmony_ci mask << field); 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci case 0x8: 12962306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_SPAR2, 13062306a36Sopenharmony_ci (cvmx_read_csr(CVMX_L2C_SPAR2) & ~(0xFF << field)) | 13162306a36Sopenharmony_ci mask << field); 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case 0xC: 13462306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_SPAR3, 13562306a36Sopenharmony_ci (cvmx_read_csr(CVMX_L2C_SPAR3) & ~(0xFF << field)) | 13662306a36Sopenharmony_ci mask << field); 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciint cvmx_l2c_set_hw_way_partition(uint32_t mask) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci uint32_t valid_mask; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci valid_mask = (0x1 << cvmx_l2c_get_num_assoc()) - 1; 14762306a36Sopenharmony_ci mask &= valid_mask; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* A UMSK setting which blocks all L2C Ways is an error on some chips */ 15062306a36Sopenharmony_ci if (mask == valid_mask && !OCTEON_IS_MODEL(OCTEON_CN63XX)) 15162306a36Sopenharmony_ci return -1; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) 15462306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_WPAR_IOBX(0), mask); 15562306a36Sopenharmony_ci else 15662306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_SPAR4, 15762306a36Sopenharmony_ci (cvmx_read_csr(CVMX_L2C_SPAR4) & ~0xFF) | mask); 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciint cvmx_l2c_get_hw_way_partition(void) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) 16462306a36Sopenharmony_ci return cvmx_read_csr(CVMX_L2C_WPAR_IOBX(0)) & 0xffff; 16562306a36Sopenharmony_ci else 16662306a36Sopenharmony_ci return cvmx_read_csr(CVMX_L2C_SPAR4) & (0xFF); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_civoid cvmx_l2c_config_perf(uint32_t counter, enum cvmx_l2c_event event, 17062306a36Sopenharmony_ci uint32_t clear_on_read) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX)) { 17362306a36Sopenharmony_ci union cvmx_l2c_pfctl pfctl; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci pfctl.u64 = cvmx_read_csr(CVMX_L2C_PFCTL); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci switch (counter) { 17862306a36Sopenharmony_ci case 0: 17962306a36Sopenharmony_ci pfctl.s.cnt0sel = event; 18062306a36Sopenharmony_ci pfctl.s.cnt0ena = 1; 18162306a36Sopenharmony_ci pfctl.s.cnt0rdclr = clear_on_read; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case 1: 18462306a36Sopenharmony_ci pfctl.s.cnt1sel = event; 18562306a36Sopenharmony_ci pfctl.s.cnt1ena = 1; 18662306a36Sopenharmony_ci pfctl.s.cnt1rdclr = clear_on_read; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci case 2: 18962306a36Sopenharmony_ci pfctl.s.cnt2sel = event; 19062306a36Sopenharmony_ci pfctl.s.cnt2ena = 1; 19162306a36Sopenharmony_ci pfctl.s.cnt2rdclr = clear_on_read; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci case 3: 19462306a36Sopenharmony_ci default: 19562306a36Sopenharmony_ci pfctl.s.cnt3sel = event; 19662306a36Sopenharmony_ci pfctl.s.cnt3ena = 1; 19762306a36Sopenharmony_ci pfctl.s.cnt3rdclr = clear_on_read; 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_PFCTL, pfctl.u64); 20262306a36Sopenharmony_ci } else { 20362306a36Sopenharmony_ci union cvmx_l2c_tadx_prf l2c_tadx_prf; 20462306a36Sopenharmony_ci int tad; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci cvmx_dprintf("L2C performance counter events are different for this chip, mapping 'event' to cvmx_l2c_tad_event_t\n"); 20762306a36Sopenharmony_ci if (clear_on_read) 20862306a36Sopenharmony_ci cvmx_dprintf("L2C counters don't support clear on read for this chip\n"); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci l2c_tadx_prf.u64 = cvmx_read_csr(CVMX_L2C_TADX_PRF(0)); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci switch (counter) { 21362306a36Sopenharmony_ci case 0: 21462306a36Sopenharmony_ci l2c_tadx_prf.s.cnt0sel = event; 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci case 1: 21762306a36Sopenharmony_ci l2c_tadx_prf.s.cnt1sel = event; 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci case 2: 22062306a36Sopenharmony_ci l2c_tadx_prf.s.cnt2sel = event; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci default: 22362306a36Sopenharmony_ci case 3: 22462306a36Sopenharmony_ci l2c_tadx_prf.s.cnt3sel = event; 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci for (tad = 0; tad < CVMX_L2C_TADS; tad++) 22862306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_TADX_PRF(tad), 22962306a36Sopenharmony_ci l2c_tadx_prf.u64); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciuint64_t cvmx_l2c_read_perf(uint32_t counter) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci switch (counter) { 23662306a36Sopenharmony_ci case 0: 23762306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX)) 23862306a36Sopenharmony_ci return cvmx_read_csr(CVMX_L2C_PFC0); 23962306a36Sopenharmony_ci else { 24062306a36Sopenharmony_ci uint64_t counter = 0; 24162306a36Sopenharmony_ci int tad; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci for (tad = 0; tad < CVMX_L2C_TADS; tad++) 24462306a36Sopenharmony_ci counter += cvmx_read_csr(CVMX_L2C_TADX_PFC0(tad)); 24562306a36Sopenharmony_ci return counter; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci case 1: 24862306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX)) 24962306a36Sopenharmony_ci return cvmx_read_csr(CVMX_L2C_PFC1); 25062306a36Sopenharmony_ci else { 25162306a36Sopenharmony_ci uint64_t counter = 0; 25262306a36Sopenharmony_ci int tad; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci for (tad = 0; tad < CVMX_L2C_TADS; tad++) 25562306a36Sopenharmony_ci counter += cvmx_read_csr(CVMX_L2C_TADX_PFC1(tad)); 25662306a36Sopenharmony_ci return counter; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci case 2: 25962306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX)) 26062306a36Sopenharmony_ci return cvmx_read_csr(CVMX_L2C_PFC2); 26162306a36Sopenharmony_ci else { 26262306a36Sopenharmony_ci uint64_t counter = 0; 26362306a36Sopenharmony_ci int tad; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci for (tad = 0; tad < CVMX_L2C_TADS; tad++) 26662306a36Sopenharmony_ci counter += cvmx_read_csr(CVMX_L2C_TADX_PFC2(tad)); 26762306a36Sopenharmony_ci return counter; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci case 3: 27062306a36Sopenharmony_ci default: 27162306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX)) 27262306a36Sopenharmony_ci return cvmx_read_csr(CVMX_L2C_PFC3); 27362306a36Sopenharmony_ci else { 27462306a36Sopenharmony_ci uint64_t counter = 0; 27562306a36Sopenharmony_ci int tad; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci for (tad = 0; tad < CVMX_L2C_TADS; tad++) 27862306a36Sopenharmony_ci counter += cvmx_read_csr(CVMX_L2C_TADX_PFC3(tad)); 27962306a36Sopenharmony_ci return counter; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* 28562306a36Sopenharmony_ci * @INTERNAL 28662306a36Sopenharmony_ci * Helper function use to fault in cache lines for L2 cache locking 28762306a36Sopenharmony_ci * 28862306a36Sopenharmony_ci * @addr: Address of base of memory region to read into L2 cache 28962306a36Sopenharmony_ci * @len: Length (in bytes) of region to fault in 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistatic void fault_in(uint64_t addr, int len) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci char *ptr; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* 29662306a36Sopenharmony_ci * Adjust addr and length so we get all cache lines even for 29762306a36Sopenharmony_ci * small ranges spanning two cache lines. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci len += addr & CVMX_CACHE_LINE_MASK; 30062306a36Sopenharmony_ci addr &= ~CVMX_CACHE_LINE_MASK; 30162306a36Sopenharmony_ci ptr = cvmx_phys_to_ptr(addr); 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * Invalidate L1 cache to make sure all loads result in data 30462306a36Sopenharmony_ci * being in L2. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci CVMX_DCACHE_INVALIDATE; 30762306a36Sopenharmony_ci while (len > 0) { 30862306a36Sopenharmony_ci READ_ONCE(*ptr); 30962306a36Sopenharmony_ci len -= CVMX_CACHE_LINE_SIZE; 31062306a36Sopenharmony_ci ptr += CVMX_CACHE_LINE_SIZE; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ciint cvmx_l2c_lock_line(uint64_t addr) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { 31762306a36Sopenharmony_ci int shift = CVMX_L2C_TAG_ADDR_ALIAS_SHIFT; 31862306a36Sopenharmony_ci uint64_t assoc = cvmx_l2c_get_num_assoc(); 31962306a36Sopenharmony_ci uint64_t tag = addr >> shift; 32062306a36Sopenharmony_ci uint64_t index = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, cvmx_l2c_address_to_index(addr) << CVMX_L2C_IDX_ADDR_SHIFT); 32162306a36Sopenharmony_ci uint64_t way; 32262306a36Sopenharmony_ci union cvmx_l2c_tadx_tag l2c_tadx_tag; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci CVMX_CACHE_LCKL2(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, addr), 0); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* Make sure we were able to lock the line */ 32762306a36Sopenharmony_ci for (way = 0; way < assoc; way++) { 32862306a36Sopenharmony_ci CVMX_CACHE_LTGL2I(index | (way << shift), 0); 32962306a36Sopenharmony_ci /* make sure CVMX_L2C_TADX_TAG is updated */ 33062306a36Sopenharmony_ci CVMX_SYNC; 33162306a36Sopenharmony_ci l2c_tadx_tag.u64 = cvmx_read_csr(CVMX_L2C_TADX_TAG(0)); 33262306a36Sopenharmony_ci if (l2c_tadx_tag.s.valid && l2c_tadx_tag.s.tag == tag) 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Check if a valid line is found */ 33762306a36Sopenharmony_ci if (way >= assoc) { 33862306a36Sopenharmony_ci /* cvmx_dprintf("ERROR: cvmx_l2c_lock_line: line not found for locking at 0x%llx address\n", (unsigned long long)addr); */ 33962306a36Sopenharmony_ci return -1; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Check if lock bit is not set */ 34362306a36Sopenharmony_ci if (!l2c_tadx_tag.s.lock) { 34462306a36Sopenharmony_ci /* cvmx_dprintf("ERROR: cvmx_l2c_lock_line: Not able to lock at 0x%llx address\n", (unsigned long long)addr); */ 34562306a36Sopenharmony_ci return -1; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci return way; 34862306a36Sopenharmony_ci } else { 34962306a36Sopenharmony_ci int retval = 0; 35062306a36Sopenharmony_ci union cvmx_l2c_dbg l2cdbg; 35162306a36Sopenharmony_ci union cvmx_l2c_lckbase lckbase; 35262306a36Sopenharmony_ci union cvmx_l2c_lckoff lckoff; 35362306a36Sopenharmony_ci union cvmx_l2t_err l2t_err; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci cvmx_spinlock_lock(&cvmx_l2c_spinlock); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci l2cdbg.u64 = 0; 35862306a36Sopenharmony_ci lckbase.u64 = 0; 35962306a36Sopenharmony_ci lckoff.u64 = 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Clear l2t error bits if set */ 36262306a36Sopenharmony_ci l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); 36362306a36Sopenharmony_ci l2t_err.s.lckerr = 1; 36462306a36Sopenharmony_ci l2t_err.s.lckerr2 = 1; 36562306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci addr &= ~CVMX_CACHE_LINE_MASK; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Set this core as debug core */ 37062306a36Sopenharmony_ci l2cdbg.s.ppnum = cvmx_get_core_num(); 37162306a36Sopenharmony_ci CVMX_SYNC; 37262306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_DBG, l2cdbg.u64); 37362306a36Sopenharmony_ci cvmx_read_csr(CVMX_L2C_DBG); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci lckoff.s.lck_offset = 0; /* Only lock 1 line at a time */ 37662306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_LCKOFF, lckoff.u64); 37762306a36Sopenharmony_ci cvmx_read_csr(CVMX_L2C_LCKOFF); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (((union cvmx_l2c_cfg)(cvmx_read_csr(CVMX_L2C_CFG))).s.idxalias) { 38062306a36Sopenharmony_ci int alias_shift = CVMX_L2C_IDX_ADDR_SHIFT + 2 * CVMX_L2_SET_BITS - 1; 38162306a36Sopenharmony_ci uint64_t addr_tmp = addr ^ (addr & ((1 << alias_shift) - 1)) >> CVMX_L2_SET_BITS; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci lckbase.s.lck_base = addr_tmp >> 7; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci } else { 38662306a36Sopenharmony_ci lckbase.s.lck_base = addr >> 7; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci lckbase.s.lck_ena = 1; 39062306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_LCKBASE, lckbase.u64); 39162306a36Sopenharmony_ci /* Make sure it gets there */ 39262306a36Sopenharmony_ci cvmx_read_csr(CVMX_L2C_LCKBASE); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci fault_in(addr, CVMX_CACHE_LINE_SIZE); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci lckbase.s.lck_ena = 0; 39762306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_LCKBASE, lckbase.u64); 39862306a36Sopenharmony_ci /* Make sure it gets there */ 39962306a36Sopenharmony_ci cvmx_read_csr(CVMX_L2C_LCKBASE); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* Stop being debug core */ 40262306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_DBG, 0); 40362306a36Sopenharmony_ci cvmx_read_csr(CVMX_L2C_DBG); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); 40662306a36Sopenharmony_ci if (l2t_err.s.lckerr || l2t_err.s.lckerr2) 40762306a36Sopenharmony_ci retval = 1; /* We were unable to lock the line */ 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci cvmx_spinlock_unlock(&cvmx_l2c_spinlock); 41062306a36Sopenharmony_ci return retval; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciint cvmx_l2c_lock_mem_region(uint64_t start, uint64_t len) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci int retval = 0; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* Round start/end to cache line boundaries */ 41962306a36Sopenharmony_ci len += start & CVMX_CACHE_LINE_MASK; 42062306a36Sopenharmony_ci start &= ~CVMX_CACHE_LINE_MASK; 42162306a36Sopenharmony_ci len = (len + CVMX_CACHE_LINE_MASK) & ~CVMX_CACHE_LINE_MASK; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci while (len) { 42462306a36Sopenharmony_ci retval += cvmx_l2c_lock_line(start); 42562306a36Sopenharmony_ci start += CVMX_CACHE_LINE_SIZE; 42662306a36Sopenharmony_ci len -= CVMX_CACHE_LINE_SIZE; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci return retval; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_civoid cvmx_l2c_flush(void) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci uint64_t assoc, set; 43462306a36Sopenharmony_ci uint64_t n_assoc, n_set; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci n_set = cvmx_l2c_get_num_sets(); 43762306a36Sopenharmony_ci n_assoc = cvmx_l2c_get_num_assoc(); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { 44062306a36Sopenharmony_ci uint64_t address; 44162306a36Sopenharmony_ci /* These may look like constants, but they aren't... */ 44262306a36Sopenharmony_ci int assoc_shift = CVMX_L2C_TAG_ADDR_ALIAS_SHIFT; 44362306a36Sopenharmony_ci int set_shift = CVMX_L2C_IDX_ADDR_SHIFT; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci for (set = 0; set < n_set; set++) { 44662306a36Sopenharmony_ci for (assoc = 0; assoc < n_assoc; assoc++) { 44762306a36Sopenharmony_ci address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 44862306a36Sopenharmony_ci (assoc << assoc_shift) | (set << set_shift)); 44962306a36Sopenharmony_ci CVMX_CACHE_WBIL2I(address, 0); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } else { 45362306a36Sopenharmony_ci for (set = 0; set < n_set; set++) 45462306a36Sopenharmony_ci for (assoc = 0; assoc < n_assoc; assoc++) 45562306a36Sopenharmony_ci cvmx_l2c_flush_line(assoc, set); 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ciint cvmx_l2c_unlock_line(uint64_t address) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { 46462306a36Sopenharmony_ci int assoc; 46562306a36Sopenharmony_ci union cvmx_l2c_tag tag; 46662306a36Sopenharmony_ci uint32_t tag_addr; 46762306a36Sopenharmony_ci uint32_t index = cvmx_l2c_address_to_index(address); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci tag_addr = ((address >> CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) & ((1 << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) - 1)); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * For 63XX, we can flush a line by using the physical 47362306a36Sopenharmony_ci * address directly, so finding the cache line used by 47462306a36Sopenharmony_ci * the address is only required to provide the proper 47562306a36Sopenharmony_ci * return value for the function. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci for (assoc = 0; assoc < CVMX_L2_ASSOC; assoc++) { 47862306a36Sopenharmony_ci tag = cvmx_l2c_get_tag(assoc, index); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (tag.s.V && (tag.s.addr == tag_addr)) { 48162306a36Sopenharmony_ci CVMX_CACHE_WBIL2(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, address), 0); 48262306a36Sopenharmony_ci return tag.s.L; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci } else { 48662306a36Sopenharmony_ci int assoc; 48762306a36Sopenharmony_ci union cvmx_l2c_tag tag; 48862306a36Sopenharmony_ci uint32_t tag_addr; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci uint32_t index = cvmx_l2c_address_to_index(address); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Compute portion of address that is stored in tag */ 49362306a36Sopenharmony_ci tag_addr = ((address >> CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) & ((1 << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) - 1)); 49462306a36Sopenharmony_ci for (assoc = 0; assoc < CVMX_L2_ASSOC; assoc++) { 49562306a36Sopenharmony_ci tag = cvmx_l2c_get_tag(assoc, index); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (tag.s.V && (tag.s.addr == tag_addr)) { 49862306a36Sopenharmony_ci cvmx_l2c_flush_line(assoc, index); 49962306a36Sopenharmony_ci return tag.s.L; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci return 0; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciint cvmx_l2c_unlock_mem_region(uint64_t start, uint64_t len) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci int num_unlocked = 0; 50962306a36Sopenharmony_ci /* Round start/end to cache line boundaries */ 51062306a36Sopenharmony_ci len += start & CVMX_CACHE_LINE_MASK; 51162306a36Sopenharmony_ci start &= ~CVMX_CACHE_LINE_MASK; 51262306a36Sopenharmony_ci len = (len + CVMX_CACHE_LINE_MASK) & ~CVMX_CACHE_LINE_MASK; 51362306a36Sopenharmony_ci while (len > 0) { 51462306a36Sopenharmony_ci num_unlocked += cvmx_l2c_unlock_line(start); 51562306a36Sopenharmony_ci start += CVMX_CACHE_LINE_SIZE; 51662306a36Sopenharmony_ci len -= CVMX_CACHE_LINE_SIZE; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return num_unlocked; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/* 52362306a36Sopenharmony_ci * Internal l2c tag types. These are converted to a generic structure 52462306a36Sopenharmony_ci * that can be used on all chips. 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ciunion __cvmx_l2c_tag { 52762306a36Sopenharmony_ci uint64_t u64; 52862306a36Sopenharmony_ci struct cvmx_l2c_tag_cn50xx { 52962306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t reserved:40, 53062306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t V:1, /* Line valid */ 53162306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t D:1, /* Line dirty */ 53262306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t L:1, /* Line locked */ 53362306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t U:1, /* Use, LRU eviction */ 53462306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t addr:20, /* Phys addr (33..14) */ 53562306a36Sopenharmony_ci ;)))))) 53662306a36Sopenharmony_ci } cn50xx; 53762306a36Sopenharmony_ci struct cvmx_l2c_tag_cn30xx { 53862306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t reserved:41, 53962306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t V:1, /* Line valid */ 54062306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t D:1, /* Line dirty */ 54162306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t L:1, /* Line locked */ 54262306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t U:1, /* Use, LRU eviction */ 54362306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t addr:19, /* Phys addr (33..15) */ 54462306a36Sopenharmony_ci ;)))))) 54562306a36Sopenharmony_ci } cn30xx; 54662306a36Sopenharmony_ci struct cvmx_l2c_tag_cn31xx { 54762306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t reserved:42, 54862306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t V:1, /* Line valid */ 54962306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t D:1, /* Line dirty */ 55062306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t L:1, /* Line locked */ 55162306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t U:1, /* Use, LRU eviction */ 55262306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t addr:18, /* Phys addr (33..16) */ 55362306a36Sopenharmony_ci ;)))))) 55462306a36Sopenharmony_ci } cn31xx; 55562306a36Sopenharmony_ci struct cvmx_l2c_tag_cn38xx { 55662306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t reserved:43, 55762306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t V:1, /* Line valid */ 55862306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t D:1, /* Line dirty */ 55962306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t L:1, /* Line locked */ 56062306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t U:1, /* Use, LRU eviction */ 56162306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t addr:17, /* Phys addr (33..17) */ 56262306a36Sopenharmony_ci ;)))))) 56362306a36Sopenharmony_ci } cn38xx; 56462306a36Sopenharmony_ci struct cvmx_l2c_tag_cn58xx { 56562306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t reserved:44, 56662306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t V:1, /* Line valid */ 56762306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t D:1, /* Line dirty */ 56862306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t L:1, /* Line locked */ 56962306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t U:1, /* Use, LRU eviction */ 57062306a36Sopenharmony_ci __BITFIELD_FIELD(uint64_t addr:16, /* Phys addr (33..18) */ 57162306a36Sopenharmony_ci ;)))))) 57262306a36Sopenharmony_ci } cn58xx; 57362306a36Sopenharmony_ci struct cvmx_l2c_tag_cn58xx cn56xx; /* 2048 sets */ 57462306a36Sopenharmony_ci struct cvmx_l2c_tag_cn31xx cn52xx; /* 512 sets */ 57562306a36Sopenharmony_ci}; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/* 57962306a36Sopenharmony_ci * @INTERNAL 58062306a36Sopenharmony_ci * Function to read a L2C tag. This code make the current core 58162306a36Sopenharmony_ci * the 'debug core' for the L2. This code must only be executed by 58262306a36Sopenharmony_ci * 1 core at a time. 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * @assoc: Association (way) of the tag to dump 58562306a36Sopenharmony_ci * @index: Index of the cacheline 58662306a36Sopenharmony_ci * 58762306a36Sopenharmony_ci * Returns The Octeon model specific tag structure. This is 58862306a36Sopenharmony_ci * translated by a wrapper function to a generic form that is 58962306a36Sopenharmony_ci * easier for applications to use. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistatic union __cvmx_l2c_tag __read_l2_tag(uint64_t assoc, uint64_t index) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci uint64_t debug_tag_addr = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, (index << 7) + 96); 59562306a36Sopenharmony_ci uint64_t core = cvmx_get_core_num(); 59662306a36Sopenharmony_ci union __cvmx_l2c_tag tag_val; 59762306a36Sopenharmony_ci uint64_t dbg_addr = CVMX_L2C_DBG; 59862306a36Sopenharmony_ci unsigned long flags; 59962306a36Sopenharmony_ci union cvmx_l2c_dbg debug_val; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci debug_val.u64 = 0; 60262306a36Sopenharmony_ci /* 60362306a36Sopenharmony_ci * For low core count parts, the core number is always small 60462306a36Sopenharmony_ci * enough to stay in the correct field and not set any 60562306a36Sopenharmony_ci * reserved bits. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci debug_val.s.ppnum = core; 60862306a36Sopenharmony_ci debug_val.s.l2t = 1; 60962306a36Sopenharmony_ci debug_val.s.set = assoc; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci local_irq_save(flags); 61262306a36Sopenharmony_ci /* 61362306a36Sopenharmony_ci * Make sure core is quiet (no prefetches, etc.) before 61462306a36Sopenharmony_ci * entering debug mode. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci CVMX_SYNC; 61762306a36Sopenharmony_ci /* Flush L1 to make sure debug load misses L1 */ 61862306a36Sopenharmony_ci CVMX_DCACHE_INVALIDATE; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* 62162306a36Sopenharmony_ci * The following must be done in assembly as when in debug 62262306a36Sopenharmony_ci * mode all data loads from L2 return special debug data, not 62362306a36Sopenharmony_ci * normal memory contents. Also, interrupts must be disabled, 62462306a36Sopenharmony_ci * since if an interrupt occurs while in debug mode the ISR 62562306a36Sopenharmony_ci * will get debug data from all its memory * reads instead of 62662306a36Sopenharmony_ci * the contents of memory. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci asm volatile ( 63062306a36Sopenharmony_ci ".set push\n\t" 63162306a36Sopenharmony_ci ".set mips64\n\t" 63262306a36Sopenharmony_ci ".set noreorder\n\t" 63362306a36Sopenharmony_ci "sd %[dbg_val], 0(%[dbg_addr])\n\t" /* Enter debug mode, wait for store */ 63462306a36Sopenharmony_ci "ld $0, 0(%[dbg_addr])\n\t" 63562306a36Sopenharmony_ci "ld %[tag_val], 0(%[tag_addr])\n\t" /* Read L2C tag data */ 63662306a36Sopenharmony_ci "sd $0, 0(%[dbg_addr])\n\t" /* Exit debug mode, wait for store */ 63762306a36Sopenharmony_ci "ld $0, 0(%[dbg_addr])\n\t" 63862306a36Sopenharmony_ci "cache 9, 0($0)\n\t" /* Invalidate dcache to discard debug data */ 63962306a36Sopenharmony_ci ".set pop" 64062306a36Sopenharmony_ci : [tag_val] "=r" (tag_val) 64162306a36Sopenharmony_ci : [dbg_addr] "r" (dbg_addr), [dbg_val] "r" (debug_val), [tag_addr] "r" (debug_tag_addr) 64262306a36Sopenharmony_ci : "memory"); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci local_irq_restore(flags); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci return tag_val; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ciunion cvmx_l2c_tag cvmx_l2c_get_tag(uint32_t association, uint32_t index) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci union cvmx_l2c_tag tag; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci tag.u64 = 0; 65562306a36Sopenharmony_ci if ((int)association >= cvmx_l2c_get_num_assoc()) { 65662306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_l2c_get_tag association out of range\n"); 65762306a36Sopenharmony_ci return tag; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci if ((int)index >= cvmx_l2c_get_num_sets()) { 66062306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_l2c_get_tag index out of range (arg: %d, max: %d)\n", 66162306a36Sopenharmony_ci (int)index, cvmx_l2c_get_num_sets()); 66262306a36Sopenharmony_ci return tag; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { 66562306a36Sopenharmony_ci union cvmx_l2c_tadx_tag l2c_tadx_tag; 66662306a36Sopenharmony_ci uint64_t address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 66762306a36Sopenharmony_ci (association << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) | 66862306a36Sopenharmony_ci (index << CVMX_L2C_IDX_ADDR_SHIFT)); 66962306a36Sopenharmony_ci /* 67062306a36Sopenharmony_ci * Use L2 cache Index load tag cache instruction, as 67162306a36Sopenharmony_ci * hardware loads the virtual tag for the L2 cache 67262306a36Sopenharmony_ci * block with the contents of L2C_TAD0_TAG 67362306a36Sopenharmony_ci * register. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci CVMX_CACHE_LTGL2I(address, 0); 67662306a36Sopenharmony_ci CVMX_SYNC; /* make sure CVMX_L2C_TADX_TAG is updated */ 67762306a36Sopenharmony_ci l2c_tadx_tag.u64 = cvmx_read_csr(CVMX_L2C_TADX_TAG(0)); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci tag.s.V = l2c_tadx_tag.s.valid; 68062306a36Sopenharmony_ci tag.s.D = l2c_tadx_tag.s.dirty; 68162306a36Sopenharmony_ci tag.s.L = l2c_tadx_tag.s.lock; 68262306a36Sopenharmony_ci tag.s.U = l2c_tadx_tag.s.use; 68362306a36Sopenharmony_ci tag.s.addr = l2c_tadx_tag.s.tag; 68462306a36Sopenharmony_ci } else { 68562306a36Sopenharmony_ci union __cvmx_l2c_tag tmp_tag; 68662306a36Sopenharmony_ci /* __read_l2_tag is intended for internal use only */ 68762306a36Sopenharmony_ci tmp_tag = __read_l2_tag(association, index); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* 69062306a36Sopenharmony_ci * Convert all tag structure types to generic version, 69162306a36Sopenharmony_ci * as it can represent all models. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN58XX) || OCTEON_IS_MODEL(OCTEON_CN56XX)) { 69462306a36Sopenharmony_ci tag.s.V = tmp_tag.cn58xx.V; 69562306a36Sopenharmony_ci tag.s.D = tmp_tag.cn58xx.D; 69662306a36Sopenharmony_ci tag.s.L = tmp_tag.cn58xx.L; 69762306a36Sopenharmony_ci tag.s.U = tmp_tag.cn58xx.U; 69862306a36Sopenharmony_ci tag.s.addr = tmp_tag.cn58xx.addr; 69962306a36Sopenharmony_ci } else if (OCTEON_IS_MODEL(OCTEON_CN38XX)) { 70062306a36Sopenharmony_ci tag.s.V = tmp_tag.cn38xx.V; 70162306a36Sopenharmony_ci tag.s.D = tmp_tag.cn38xx.D; 70262306a36Sopenharmony_ci tag.s.L = tmp_tag.cn38xx.L; 70362306a36Sopenharmony_ci tag.s.U = tmp_tag.cn38xx.U; 70462306a36Sopenharmony_ci tag.s.addr = tmp_tag.cn38xx.addr; 70562306a36Sopenharmony_ci } else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) { 70662306a36Sopenharmony_ci tag.s.V = tmp_tag.cn31xx.V; 70762306a36Sopenharmony_ci tag.s.D = tmp_tag.cn31xx.D; 70862306a36Sopenharmony_ci tag.s.L = tmp_tag.cn31xx.L; 70962306a36Sopenharmony_ci tag.s.U = tmp_tag.cn31xx.U; 71062306a36Sopenharmony_ci tag.s.addr = tmp_tag.cn31xx.addr; 71162306a36Sopenharmony_ci } else if (OCTEON_IS_MODEL(OCTEON_CN30XX)) { 71262306a36Sopenharmony_ci tag.s.V = tmp_tag.cn30xx.V; 71362306a36Sopenharmony_ci tag.s.D = tmp_tag.cn30xx.D; 71462306a36Sopenharmony_ci tag.s.L = tmp_tag.cn30xx.L; 71562306a36Sopenharmony_ci tag.s.U = tmp_tag.cn30xx.U; 71662306a36Sopenharmony_ci tag.s.addr = tmp_tag.cn30xx.addr; 71762306a36Sopenharmony_ci } else if (OCTEON_IS_MODEL(OCTEON_CN50XX)) { 71862306a36Sopenharmony_ci tag.s.V = tmp_tag.cn50xx.V; 71962306a36Sopenharmony_ci tag.s.D = tmp_tag.cn50xx.D; 72062306a36Sopenharmony_ci tag.s.L = tmp_tag.cn50xx.L; 72162306a36Sopenharmony_ci tag.s.U = tmp_tag.cn50xx.U; 72262306a36Sopenharmony_ci tag.s.addr = tmp_tag.cn50xx.addr; 72362306a36Sopenharmony_ci } else { 72462306a36Sopenharmony_ci cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci return tag; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ciuint32_t cvmx_l2c_address_to_index(uint64_t addr) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci uint64_t idx = addr >> CVMX_L2C_IDX_ADDR_SHIFT; 73362306a36Sopenharmony_ci int indxalias = 0; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { 73662306a36Sopenharmony_ci union cvmx_l2c_ctl l2c_ctl; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci l2c_ctl.u64 = cvmx_read_csr(CVMX_L2C_CTL); 73962306a36Sopenharmony_ci indxalias = !l2c_ctl.s.disidxalias; 74062306a36Sopenharmony_ci } else { 74162306a36Sopenharmony_ci union cvmx_l2c_cfg l2c_cfg; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci l2c_cfg.u64 = cvmx_read_csr(CVMX_L2C_CFG); 74462306a36Sopenharmony_ci indxalias = l2c_cfg.s.idxalias; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (indxalias) { 74862306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { 74962306a36Sopenharmony_ci uint32_t a_14_12 = (idx / (CVMX_L2C_MEMBANK_SELECT_SIZE/(1<<CVMX_L2C_IDX_ADDR_SHIFT))) & 0x7; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci idx ^= idx / cvmx_l2c_get_num_sets(); 75262306a36Sopenharmony_ci idx ^= a_14_12; 75362306a36Sopenharmony_ci } else { 75462306a36Sopenharmony_ci idx ^= ((addr & CVMX_L2C_ALIAS_MASK) >> CVMX_L2C_TAG_ADDR_ALIAS_SHIFT); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci idx &= CVMX_L2C_IDX_MASK; 75862306a36Sopenharmony_ci return idx; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ciint cvmx_l2c_get_cache_size_bytes(void) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci return cvmx_l2c_get_num_sets() * cvmx_l2c_get_num_assoc() * 76462306a36Sopenharmony_ci CVMX_CACHE_LINE_SIZE; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci/* 76862306a36Sopenharmony_ci * Return log base 2 of the number of sets in the L2 cache 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ciint cvmx_l2c_get_set_bits(void) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci int l2_set_bits; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) 77562306a36Sopenharmony_ci l2_set_bits = 11; /* 2048 sets */ 77662306a36Sopenharmony_ci else if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN63XX)) 77762306a36Sopenharmony_ci l2_set_bits = 10; /* 1024 sets */ 77862306a36Sopenharmony_ci else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) 77962306a36Sopenharmony_ci l2_set_bits = 9; /* 512 sets */ 78062306a36Sopenharmony_ci else if (OCTEON_IS_MODEL(OCTEON_CN30XX)) 78162306a36Sopenharmony_ci l2_set_bits = 8; /* 256 sets */ 78262306a36Sopenharmony_ci else if (OCTEON_IS_MODEL(OCTEON_CN50XX)) 78362306a36Sopenharmony_ci l2_set_bits = 7; /* 128 sets */ 78462306a36Sopenharmony_ci else { 78562306a36Sopenharmony_ci cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__); 78662306a36Sopenharmony_ci l2_set_bits = 11; /* 2048 sets */ 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci return l2_set_bits; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci/* Return the number of sets in the L2 Cache */ 79262306a36Sopenharmony_ciint cvmx_l2c_get_num_sets(void) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci return 1 << cvmx_l2c_get_set_bits(); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci/* Return the number of associations in the L2 Cache */ 79862306a36Sopenharmony_ciint cvmx_l2c_get_num_assoc(void) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci int l2_assoc; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN56XX) || 80362306a36Sopenharmony_ci OCTEON_IS_MODEL(OCTEON_CN52XX) || 80462306a36Sopenharmony_ci OCTEON_IS_MODEL(OCTEON_CN58XX) || 80562306a36Sopenharmony_ci OCTEON_IS_MODEL(OCTEON_CN50XX) || 80662306a36Sopenharmony_ci OCTEON_IS_MODEL(OCTEON_CN38XX)) 80762306a36Sopenharmony_ci l2_assoc = 8; 80862306a36Sopenharmony_ci else if (OCTEON_IS_MODEL(OCTEON_CN63XX)) 80962306a36Sopenharmony_ci l2_assoc = 16; 81062306a36Sopenharmony_ci else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || 81162306a36Sopenharmony_ci OCTEON_IS_MODEL(OCTEON_CN30XX)) 81262306a36Sopenharmony_ci l2_assoc = 4; 81362306a36Sopenharmony_ci else { 81462306a36Sopenharmony_ci cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__); 81562306a36Sopenharmony_ci l2_assoc = 8; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Check to see if part of the cache is disabled */ 81962306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { 82062306a36Sopenharmony_ci union cvmx_mio_fus_dat3 mio_fus_dat3; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci mio_fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3); 82362306a36Sopenharmony_ci /* 82462306a36Sopenharmony_ci * cvmx_mio_fus_dat3.s.l2c_crip fuses map as follows 82562306a36Sopenharmony_ci * <2> will be not used for 63xx 82662306a36Sopenharmony_ci * <1> disables 1/2 ways 82762306a36Sopenharmony_ci * <0> disables 1/4 ways 82862306a36Sopenharmony_ci * They are cumulative, so for 63xx: 82962306a36Sopenharmony_ci * <1> <0> 83062306a36Sopenharmony_ci * 0 0 16-way 2MB cache 83162306a36Sopenharmony_ci * 0 1 12-way 1.5MB cache 83262306a36Sopenharmony_ci * 1 0 8-way 1MB cache 83362306a36Sopenharmony_ci * 1 1 4-way 512KB cache 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (mio_fus_dat3.s.l2c_crip == 3) 83762306a36Sopenharmony_ci l2_assoc = 4; 83862306a36Sopenharmony_ci else if (mio_fus_dat3.s.l2c_crip == 2) 83962306a36Sopenharmony_ci l2_assoc = 8; 84062306a36Sopenharmony_ci else if (mio_fus_dat3.s.l2c_crip == 1) 84162306a36Sopenharmony_ci l2_assoc = 12; 84262306a36Sopenharmony_ci } else { 84362306a36Sopenharmony_ci uint64_t l2d_fus3; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci l2d_fus3 = cvmx_read_csr(CVMX_L2D_FUS3); 84662306a36Sopenharmony_ci /* 84762306a36Sopenharmony_ci * Using shifts here, as bit position names are 84862306a36Sopenharmony_ci * different for each model but they all mean the 84962306a36Sopenharmony_ci * same. 85062306a36Sopenharmony_ci */ 85162306a36Sopenharmony_ci if ((l2d_fus3 >> 35) & 0x1) 85262306a36Sopenharmony_ci l2_assoc = l2_assoc >> 2; 85362306a36Sopenharmony_ci else if ((l2d_fus3 >> 34) & 0x1) 85462306a36Sopenharmony_ci l2_assoc = l2_assoc >> 1; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci return l2_assoc; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci/* 86062306a36Sopenharmony_ci * Flush a line from the L2 cache 86162306a36Sopenharmony_ci * This should only be called from one core at a time, as this routine 86262306a36Sopenharmony_ci * sets the core to the 'debug' core in order to flush the line. 86362306a36Sopenharmony_ci * 86462306a36Sopenharmony_ci * @assoc: Association (or way) to flush 86562306a36Sopenharmony_ci * @index: Index to flush 86662306a36Sopenharmony_ci */ 86762306a36Sopenharmony_civoid cvmx_l2c_flush_line(uint32_t assoc, uint32_t index) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci /* Check the range of the index. */ 87062306a36Sopenharmony_ci if (index > (uint32_t)cvmx_l2c_get_num_sets()) { 87162306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_l2c_flush_line index out of range.\n"); 87262306a36Sopenharmony_ci return; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* Check the range of association. */ 87662306a36Sopenharmony_ci if (assoc > (uint32_t)cvmx_l2c_get_num_assoc()) { 87762306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_l2c_flush_line association out of range.\n"); 87862306a36Sopenharmony_ci return; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { 88262306a36Sopenharmony_ci uint64_t address; 88362306a36Sopenharmony_ci /* Create the address based on index and association. 88462306a36Sopenharmony_ci * Bits<20:17> select the way of the cache block involved in 88562306a36Sopenharmony_ci * the operation 88662306a36Sopenharmony_ci * Bits<16:7> of the effect address select the index 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 88962306a36Sopenharmony_ci (assoc << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) | 89062306a36Sopenharmony_ci (index << CVMX_L2C_IDX_ADDR_SHIFT)); 89162306a36Sopenharmony_ci CVMX_CACHE_WBIL2I(address, 0); 89262306a36Sopenharmony_ci } else { 89362306a36Sopenharmony_ci union cvmx_l2c_dbg l2cdbg; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci l2cdbg.u64 = 0; 89662306a36Sopenharmony_ci if (!OCTEON_IS_MODEL(OCTEON_CN30XX)) 89762306a36Sopenharmony_ci l2cdbg.s.ppnum = cvmx_get_core_num(); 89862306a36Sopenharmony_ci l2cdbg.s.finv = 1; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci l2cdbg.s.set = assoc; 90162306a36Sopenharmony_ci cvmx_spinlock_lock(&cvmx_l2c_spinlock); 90262306a36Sopenharmony_ci /* 90362306a36Sopenharmony_ci * Enter debug mode, and make sure all other writes 90462306a36Sopenharmony_ci * complete before we enter debug mode 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_ci CVMX_SYNC; 90762306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_DBG, l2cdbg.u64); 90862306a36Sopenharmony_ci cvmx_read_csr(CVMX_L2C_DBG); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci CVMX_PREPARE_FOR_STORE(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 91162306a36Sopenharmony_ci index * CVMX_CACHE_LINE_SIZE), 91262306a36Sopenharmony_ci 0); 91362306a36Sopenharmony_ci /* Exit debug mode */ 91462306a36Sopenharmony_ci CVMX_SYNC; 91562306a36Sopenharmony_ci cvmx_write_csr(CVMX_L2C_DBG, 0); 91662306a36Sopenharmony_ci cvmx_read_csr(CVMX_L2C_DBG); 91762306a36Sopenharmony_ci cvmx_spinlock_unlock(&cvmx_l2c_spinlock); 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci} 920