xref: /linux/arch/mips/cavium-octeon/executive/cvmx-l2c.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
158f07778SDavid Daney /***********************license start***************
258f07778SDavid Daney  * Author: Cavium Networks
358f07778SDavid Daney  *
458f07778SDavid Daney  * Contact: support@caviumnetworks.com
558f07778SDavid Daney  * This file is part of the OCTEON SDK
658f07778SDavid Daney  *
715f68479SSteven J. Hill  * Copyright (c) 2003-2017 Cavium, Inc.
858f07778SDavid Daney  *
958f07778SDavid Daney  * This file is free software; you can redistribute it and/or modify
1058f07778SDavid Daney  * it under the terms of the GNU General Public License, Version 2, as
1158f07778SDavid Daney  * published by the Free Software Foundation.
1258f07778SDavid Daney  *
1358f07778SDavid Daney  * This file is distributed in the hope that it will be useful, but
1458f07778SDavid Daney  * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
1558f07778SDavid Daney  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
1658f07778SDavid Daney  * NONINFRINGEMENT.  See the GNU General Public License for more
1758f07778SDavid Daney  * details.
1858f07778SDavid Daney  *
1958f07778SDavid Daney  * You should have received a copy of the GNU General Public License
2058f07778SDavid Daney  * along with this file; if not, write to the Free Software
2158f07778SDavid Daney  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2258f07778SDavid Daney  * or visit http://www.gnu.org/licenses/.
2358f07778SDavid Daney  *
2458f07778SDavid Daney  * This file may also be available under a different license from Cavium.
2558f07778SDavid Daney  * Contact Cavium Networks for more information
2658f07778SDavid Daney  ***********************license end**************************************/
2758f07778SDavid Daney 
2858f07778SDavid Daney /*
29b8db85b5SDavid Daney  * Implementation of the Level 2 Cache (L2C) control,
30b8db85b5SDavid Daney  * measurement, and debugging facilities.
3158f07778SDavid Daney  */
3258f07778SDavid Daney 
33757be67fSRalf Baechle #include <linux/compiler.h>
3492d11594SJim Quinlan #include <linux/irqflags.h>
3558f07778SDavid Daney #include <asm/octeon/cvmx.h>
3658f07778SDavid Daney #include <asm/octeon/cvmx-l2c.h>
3758f07778SDavid Daney #include <asm/octeon/cvmx-spinlock.h>
3858f07778SDavid Daney 
3958f07778SDavid Daney /*
4058f07778SDavid Daney  * This spinlock is used internally to ensure that only one core is
4158f07778SDavid Daney  * performing certain L2 operations at a time.
4258f07778SDavid Daney  *
4358f07778SDavid Daney  * NOTE: This only protects calls from within a single application -
4458f07778SDavid Daney  * if multiple applications or operating systems are running, then it
4558f07778SDavid Daney  * is up to the user program to coordinate between them.
4658f07778SDavid Daney  */
476430ba58SAaro Koskinen static cvmx_spinlock_t cvmx_l2c_spinlock;
4858f07778SDavid Daney 
cvmx_l2c_get_core_way_partition(uint32_t core)4958f07778SDavid Daney int cvmx_l2c_get_core_way_partition(uint32_t core)
5058f07778SDavid Daney {
5158f07778SDavid Daney 	uint32_t field;
5258f07778SDavid Daney 
5358f07778SDavid Daney 	/* Validate the core number */
5458f07778SDavid Daney 	if (core >= cvmx_octeon_num_cores())
5558f07778SDavid Daney 		return -1;
5658f07778SDavid Daney 
57b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN63XX))
58b8db85b5SDavid Daney 		return cvmx_read_csr(CVMX_L2C_WPAR_PPX(core)) & 0xffff;
59b8db85b5SDavid Daney 
6058f07778SDavid Daney 	/*
6158f07778SDavid Daney 	 * Use the lower two bits of the coreNumber to determine the
6258f07778SDavid Daney 	 * bit offset of the UMSK[] field in the L2C_SPAR register.
6358f07778SDavid Daney 	 */
6458f07778SDavid Daney 	field = (core & 0x3) * 8;
6558f07778SDavid Daney 
6658f07778SDavid Daney 	/*
6758f07778SDavid Daney 	 * Return the UMSK[] field from the appropriate L2C_SPAR
6858f07778SDavid Daney 	 * register based on the coreNumber.
6958f07778SDavid Daney 	 */
7058f07778SDavid Daney 
7158f07778SDavid Daney 	switch (core & 0xC) {
7258f07778SDavid Daney 	case 0x0:
73b8db85b5SDavid Daney 		return (cvmx_read_csr(CVMX_L2C_SPAR0) & (0xFF << field)) >> field;
7458f07778SDavid Daney 	case 0x4:
75b8db85b5SDavid Daney 		return (cvmx_read_csr(CVMX_L2C_SPAR1) & (0xFF << field)) >> field;
7658f07778SDavid Daney 	case 0x8:
77b8db85b5SDavid Daney 		return (cvmx_read_csr(CVMX_L2C_SPAR2) & (0xFF << field)) >> field;
7858f07778SDavid Daney 	case 0xC:
79b8db85b5SDavid Daney 		return (cvmx_read_csr(CVMX_L2C_SPAR3) & (0xFF << field)) >> field;
8058f07778SDavid Daney 	}
8158f07778SDavid Daney 	return 0;
8258f07778SDavid Daney }
8358f07778SDavid Daney 
cvmx_l2c_set_core_way_partition(uint32_t core,uint32_t mask)8458f07778SDavid Daney int cvmx_l2c_set_core_way_partition(uint32_t core, uint32_t mask)
8558f07778SDavid Daney {
8658f07778SDavid Daney 	uint32_t field;
8758f07778SDavid Daney 	uint32_t valid_mask;
8858f07778SDavid Daney 
8958f07778SDavid Daney 	valid_mask = (0x1 << cvmx_l2c_get_num_assoc()) - 1;
9058f07778SDavid Daney 
9158f07778SDavid Daney 	mask &= valid_mask;
9258f07778SDavid Daney 
93b8db85b5SDavid Daney 	/* A UMSK setting which blocks all L2C Ways is an error on some chips */
94b8db85b5SDavid Daney 	if (mask == valid_mask && !OCTEON_IS_MODEL(OCTEON_CN63XX))
9558f07778SDavid Daney 		return -1;
9658f07778SDavid Daney 
9758f07778SDavid Daney 	/* Validate the core number */
9858f07778SDavid Daney 	if (core >= cvmx_octeon_num_cores())
9958f07778SDavid Daney 		return -1;
10058f07778SDavid Daney 
101b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
102b8db85b5SDavid Daney 		cvmx_write_csr(CVMX_L2C_WPAR_PPX(core), mask);
103b8db85b5SDavid Daney 		return 0;
104b8db85b5SDavid Daney 	}
10558f07778SDavid Daney 
106b8db85b5SDavid Daney 	/*
107b8db85b5SDavid Daney 	 * Use the lower two bits of core to determine the bit offset of the
10858f07778SDavid Daney 	 * UMSK[] field in the L2C_SPAR register.
10958f07778SDavid Daney 	 */
11058f07778SDavid Daney 	field = (core & 0x3) * 8;
11158f07778SDavid Daney 
112b8db85b5SDavid Daney 	/*
113b8db85b5SDavid Daney 	 * Assign the new mask setting to the UMSK[] field in the appropriate
11458f07778SDavid Daney 	 * L2C_SPAR register based on the core_num.
11558f07778SDavid Daney 	 *
11658f07778SDavid Daney 	 */
11758f07778SDavid Daney 	switch (core & 0xC) {
11858f07778SDavid Daney 	case 0x0:
11958f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_SPAR0,
120b8db85b5SDavid Daney 			       (cvmx_read_csr(CVMX_L2C_SPAR0) & ~(0xFF << field)) |
121b8db85b5SDavid Daney 			       mask << field);
12258f07778SDavid Daney 		break;
12358f07778SDavid Daney 	case 0x4:
12458f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_SPAR1,
125b8db85b5SDavid Daney 			       (cvmx_read_csr(CVMX_L2C_SPAR1) & ~(0xFF << field)) |
126b8db85b5SDavid Daney 			       mask << field);
12758f07778SDavid Daney 		break;
12858f07778SDavid Daney 	case 0x8:
12958f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_SPAR2,
130b8db85b5SDavid Daney 			       (cvmx_read_csr(CVMX_L2C_SPAR2) & ~(0xFF << field)) |
131b8db85b5SDavid Daney 			       mask << field);
13258f07778SDavid Daney 		break;
13358f07778SDavid Daney 	case 0xC:
13458f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_SPAR3,
135b8db85b5SDavid Daney 			       (cvmx_read_csr(CVMX_L2C_SPAR3) & ~(0xFF << field)) |
136b8db85b5SDavid Daney 			       mask << field);
13758f07778SDavid Daney 		break;
13858f07778SDavid Daney 	}
13958f07778SDavid Daney 	return 0;
14058f07778SDavid Daney }
14158f07778SDavid Daney 
cvmx_l2c_set_hw_way_partition(uint32_t mask)14258f07778SDavid Daney int cvmx_l2c_set_hw_way_partition(uint32_t mask)
14358f07778SDavid Daney {
14458f07778SDavid Daney 	uint32_t valid_mask;
14558f07778SDavid Daney 
146b8db85b5SDavid Daney 	valid_mask = (0x1 << cvmx_l2c_get_num_assoc()) - 1;
14758f07778SDavid Daney 	mask &= valid_mask;
14858f07778SDavid Daney 
149b8db85b5SDavid Daney 	/* A UMSK setting which blocks all L2C Ways is an error on some chips */
150b8db85b5SDavid Daney 	if (mask == valid_mask	&& !OCTEON_IS_MODEL(OCTEON_CN63XX))
15158f07778SDavid Daney 		return -1;
15258f07778SDavid Daney 
153b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN63XX))
154b8db85b5SDavid Daney 		cvmx_write_csr(CVMX_L2C_WPAR_IOBX(0), mask);
155b8db85b5SDavid Daney 	else
15658f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_SPAR4,
15758f07778SDavid Daney 			       (cvmx_read_csr(CVMX_L2C_SPAR4) & ~0xFF) | mask);
15858f07778SDavid Daney 	return 0;
15958f07778SDavid Daney }
16058f07778SDavid Daney 
cvmx_l2c_get_hw_way_partition(void)16158f07778SDavid Daney int cvmx_l2c_get_hw_way_partition(void)
16258f07778SDavid Daney {
163b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN63XX))
164b8db85b5SDavid Daney 		return cvmx_read_csr(CVMX_L2C_WPAR_IOBX(0)) & 0xffff;
165b8db85b5SDavid Daney 	else
16658f07778SDavid Daney 		return cvmx_read_csr(CVMX_L2C_SPAR4) & (0xFF);
16758f07778SDavid Daney }
16858f07778SDavid Daney 
cvmx_l2c_config_perf(uint32_t counter,enum cvmx_l2c_event event,uint32_t clear_on_read)16958f07778SDavid Daney void cvmx_l2c_config_perf(uint32_t counter, enum cvmx_l2c_event event,
17058f07778SDavid Daney 			  uint32_t clear_on_read)
17158f07778SDavid Daney {
172b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
17358f07778SDavid Daney 		union cvmx_l2c_pfctl pfctl;
17458f07778SDavid Daney 
17558f07778SDavid Daney 		pfctl.u64 = cvmx_read_csr(CVMX_L2C_PFCTL);
17658f07778SDavid Daney 
17758f07778SDavid Daney 		switch (counter) {
17858f07778SDavid Daney 		case 0:
17958f07778SDavid Daney 			pfctl.s.cnt0sel = event;
18058f07778SDavid Daney 			pfctl.s.cnt0ena = 1;
18158f07778SDavid Daney 			pfctl.s.cnt0rdclr = clear_on_read;
18258f07778SDavid Daney 			break;
18358f07778SDavid Daney 		case 1:
18458f07778SDavid Daney 			pfctl.s.cnt1sel = event;
18558f07778SDavid Daney 			pfctl.s.cnt1ena = 1;
18658f07778SDavid Daney 			pfctl.s.cnt1rdclr = clear_on_read;
18758f07778SDavid Daney 			break;
18858f07778SDavid Daney 		case 2:
18958f07778SDavid Daney 			pfctl.s.cnt2sel = event;
19058f07778SDavid Daney 			pfctl.s.cnt2ena = 1;
19158f07778SDavid Daney 			pfctl.s.cnt2rdclr = clear_on_read;
19258f07778SDavid Daney 			break;
19358f07778SDavid Daney 		case 3:
19458f07778SDavid Daney 		default:
19558f07778SDavid Daney 			pfctl.s.cnt3sel = event;
19658f07778SDavid Daney 			pfctl.s.cnt3ena = 1;
19758f07778SDavid Daney 			pfctl.s.cnt3rdclr = clear_on_read;
19858f07778SDavid Daney 			break;
19958f07778SDavid Daney 		}
20058f07778SDavid Daney 
20158f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_PFCTL, pfctl.u64);
202b8db85b5SDavid Daney 	} else {
203b8db85b5SDavid Daney 		union cvmx_l2c_tadx_prf l2c_tadx_prf;
204b8db85b5SDavid Daney 		int tad;
205b8db85b5SDavid Daney 
206b8db85b5SDavid Daney 		cvmx_dprintf("L2C performance counter events are different for this chip, mapping 'event' to cvmx_l2c_tad_event_t\n");
207b8db85b5SDavid Daney 		if (clear_on_read)
208b8db85b5SDavid Daney 			cvmx_dprintf("L2C counters don't support clear on read for this chip\n");
209b8db85b5SDavid Daney 
210b8db85b5SDavid Daney 		l2c_tadx_prf.u64 = cvmx_read_csr(CVMX_L2C_TADX_PRF(0));
211b8db85b5SDavid Daney 
212b8db85b5SDavid Daney 		switch (counter) {
213b8db85b5SDavid Daney 		case 0:
214b8db85b5SDavid Daney 			l2c_tadx_prf.s.cnt0sel = event;
215b8db85b5SDavid Daney 			break;
216b8db85b5SDavid Daney 		case 1:
217b8db85b5SDavid Daney 			l2c_tadx_prf.s.cnt1sel = event;
218b8db85b5SDavid Daney 			break;
219b8db85b5SDavid Daney 		case 2:
220b8db85b5SDavid Daney 			l2c_tadx_prf.s.cnt2sel = event;
221b8db85b5SDavid Daney 			break;
222b8db85b5SDavid Daney 		default:
223b8db85b5SDavid Daney 		case 3:
224b8db85b5SDavid Daney 			l2c_tadx_prf.s.cnt3sel = event;
225b8db85b5SDavid Daney 			break;
226b8db85b5SDavid Daney 		}
227b8db85b5SDavid Daney 		for (tad = 0; tad < CVMX_L2C_TADS; tad++)
228b8db85b5SDavid Daney 			cvmx_write_csr(CVMX_L2C_TADX_PRF(tad),
229b8db85b5SDavid Daney 				       l2c_tadx_prf.u64);
230b8db85b5SDavid Daney 	}
23158f07778SDavid Daney }
23258f07778SDavid Daney 
cvmx_l2c_read_perf(uint32_t counter)23358f07778SDavid Daney uint64_t cvmx_l2c_read_perf(uint32_t counter)
23458f07778SDavid Daney {
23558f07778SDavid Daney 	switch (counter) {
23658f07778SDavid Daney 	case 0:
237b8db85b5SDavid Daney 		if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX))
23858f07778SDavid Daney 			return cvmx_read_csr(CVMX_L2C_PFC0);
239b8db85b5SDavid Daney 		else {
240b8db85b5SDavid Daney 			uint64_t counter = 0;
241b8db85b5SDavid Daney 			int tad;
24215f68479SSteven J. Hill 
243b8db85b5SDavid Daney 			for (tad = 0; tad < CVMX_L2C_TADS; tad++)
244b8db85b5SDavid Daney 				counter += cvmx_read_csr(CVMX_L2C_TADX_PFC0(tad));
245b8db85b5SDavid Daney 			return counter;
246b8db85b5SDavid Daney 		}
24758f07778SDavid Daney 	case 1:
248b8db85b5SDavid Daney 		if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX))
24958f07778SDavid Daney 			return cvmx_read_csr(CVMX_L2C_PFC1);
250b8db85b5SDavid Daney 		else {
251b8db85b5SDavid Daney 			uint64_t counter = 0;
252b8db85b5SDavid Daney 			int tad;
25315f68479SSteven J. Hill 
254b8db85b5SDavid Daney 			for (tad = 0; tad < CVMX_L2C_TADS; tad++)
255b8db85b5SDavid Daney 				counter += cvmx_read_csr(CVMX_L2C_TADX_PFC1(tad));
256b8db85b5SDavid Daney 			return counter;
257b8db85b5SDavid Daney 		}
25858f07778SDavid Daney 	case 2:
259b8db85b5SDavid Daney 		if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX))
26058f07778SDavid Daney 			return cvmx_read_csr(CVMX_L2C_PFC2);
261b8db85b5SDavid Daney 		else {
262b8db85b5SDavid Daney 			uint64_t counter = 0;
263b8db85b5SDavid Daney 			int tad;
26415f68479SSteven J. Hill 
265b8db85b5SDavid Daney 			for (tad = 0; tad < CVMX_L2C_TADS; tad++)
266b8db85b5SDavid Daney 				counter += cvmx_read_csr(CVMX_L2C_TADX_PFC2(tad));
267b8db85b5SDavid Daney 			return counter;
268b8db85b5SDavid Daney 		}
26958f07778SDavid Daney 	case 3:
27058f07778SDavid Daney 	default:
271b8db85b5SDavid Daney 		if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX))
27258f07778SDavid Daney 			return cvmx_read_csr(CVMX_L2C_PFC3);
273b8db85b5SDavid Daney 		else {
274b8db85b5SDavid Daney 			uint64_t counter = 0;
275b8db85b5SDavid Daney 			int tad;
27615f68479SSteven J. Hill 
277b8db85b5SDavid Daney 			for (tad = 0; tad < CVMX_L2C_TADS; tad++)
278b8db85b5SDavid Daney 				counter += cvmx_read_csr(CVMX_L2C_TADX_PFC3(tad));
279b8db85b5SDavid Daney 			return counter;
280b8db85b5SDavid Daney 		}
28158f07778SDavid Daney 	}
28258f07778SDavid Daney }
28358f07778SDavid Daney 
284*16df55ceSRandy Dunlap /*
28558f07778SDavid Daney  * @INTERNAL
28658f07778SDavid Daney  * Helper function use to fault in cache lines for L2 cache locking
28758f07778SDavid Daney  *
28858f07778SDavid Daney  * @addr:   Address of base of memory region to read into L2 cache
28958f07778SDavid Daney  * @len:    Length (in bytes) of region to fault in
29058f07778SDavid Daney  */
fault_in(uint64_t addr,int len)29158f07778SDavid Daney static void fault_in(uint64_t addr, int len)
29258f07778SDavid Daney {
293757be67fSRalf Baechle 	char *ptr;
294757be67fSRalf Baechle 
29558f07778SDavid Daney 	/*
29658f07778SDavid Daney 	 * Adjust addr and length so we get all cache lines even for
297b8db85b5SDavid Daney 	 * small ranges spanning two cache lines.
29858f07778SDavid Daney 	 */
29958f07778SDavid Daney 	len += addr & CVMX_CACHE_LINE_MASK;
30058f07778SDavid Daney 	addr &= ~CVMX_CACHE_LINE_MASK;
301757be67fSRalf Baechle 	ptr = cvmx_phys_to_ptr(addr);
30258f07778SDavid Daney 	/*
30358f07778SDavid Daney 	 * Invalidate L1 cache to make sure all loads result in data
30458f07778SDavid Daney 	 * being in L2.
30558f07778SDavid Daney 	 */
30658f07778SDavid Daney 	CVMX_DCACHE_INVALIDATE;
30758f07778SDavid Daney 	while (len > 0) {
30815f68479SSteven J. Hill 		READ_ONCE(*ptr);
30958f07778SDavid Daney 		len -= CVMX_CACHE_LINE_SIZE;
31058f07778SDavid Daney 		ptr += CVMX_CACHE_LINE_SIZE;
31158f07778SDavid Daney 	}
31258f07778SDavid Daney }
31358f07778SDavid Daney 
cvmx_l2c_lock_line(uint64_t addr)31458f07778SDavid Daney int cvmx_l2c_lock_line(uint64_t addr)
31558f07778SDavid Daney {
316b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
317b8db85b5SDavid Daney 		int shift = CVMX_L2C_TAG_ADDR_ALIAS_SHIFT;
318b8db85b5SDavid Daney 		uint64_t assoc = cvmx_l2c_get_num_assoc();
319b8db85b5SDavid Daney 		uint64_t tag = addr >> shift;
320b8db85b5SDavid Daney 		uint64_t index = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, cvmx_l2c_address_to_index(addr) << CVMX_L2C_IDX_ADDR_SHIFT);
321b8db85b5SDavid Daney 		uint64_t way;
322b8db85b5SDavid Daney 		union cvmx_l2c_tadx_tag l2c_tadx_tag;
323b8db85b5SDavid Daney 
324b8db85b5SDavid Daney 		CVMX_CACHE_LCKL2(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, addr), 0);
325b8db85b5SDavid Daney 
326b8db85b5SDavid Daney 		/* Make sure we were able to lock the line */
327b8db85b5SDavid Daney 		for (way = 0; way < assoc; way++) {
328b8db85b5SDavid Daney 			CVMX_CACHE_LTGL2I(index | (way << shift), 0);
329b8db85b5SDavid Daney 			/* make sure CVMX_L2C_TADX_TAG is updated */
330b8db85b5SDavid Daney 			CVMX_SYNC;
331b8db85b5SDavid Daney 			l2c_tadx_tag.u64 = cvmx_read_csr(CVMX_L2C_TADX_TAG(0));
332b8db85b5SDavid Daney 			if (l2c_tadx_tag.s.valid && l2c_tadx_tag.s.tag == tag)
333b8db85b5SDavid Daney 				break;
334b8db85b5SDavid Daney 		}
335b8db85b5SDavid Daney 
336b8db85b5SDavid Daney 		/* Check if a valid line is found */
337b8db85b5SDavid Daney 		if (way >= assoc) {
338b8db85b5SDavid Daney 			/* cvmx_dprintf("ERROR: cvmx_l2c_lock_line: line not found for locking at 0x%llx address\n", (unsigned long long)addr); */
339b8db85b5SDavid Daney 			return -1;
340b8db85b5SDavid Daney 		}
341b8db85b5SDavid Daney 
342b8db85b5SDavid Daney 		/* Check if lock bit is not set */
343b8db85b5SDavid Daney 		if (!l2c_tadx_tag.s.lock) {
344b8db85b5SDavid Daney 			/* cvmx_dprintf("ERROR: cvmx_l2c_lock_line: Not able to lock at 0x%llx address\n", (unsigned long long)addr); */
345b8db85b5SDavid Daney 			return -1;
346b8db85b5SDavid Daney 		}
347b8db85b5SDavid Daney 		return way;
348b8db85b5SDavid Daney 	} else {
34958f07778SDavid Daney 		int retval = 0;
35058f07778SDavid Daney 		union cvmx_l2c_dbg l2cdbg;
35158f07778SDavid Daney 		union cvmx_l2c_lckbase lckbase;
35258f07778SDavid Daney 		union cvmx_l2c_lckoff lckoff;
35358f07778SDavid Daney 		union cvmx_l2t_err l2t_err;
354b8db85b5SDavid Daney 
355b8db85b5SDavid Daney 		cvmx_spinlock_lock(&cvmx_l2c_spinlock);
356b8db85b5SDavid Daney 
35758f07778SDavid Daney 		l2cdbg.u64 = 0;
35858f07778SDavid Daney 		lckbase.u64 = 0;
35958f07778SDavid Daney 		lckoff.u64 = 0;
36058f07778SDavid Daney 
36158f07778SDavid Daney 		/* Clear l2t error bits if set */
36258f07778SDavid Daney 		l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR);
36358f07778SDavid Daney 		l2t_err.s.lckerr = 1;
36458f07778SDavid Daney 		l2t_err.s.lckerr2 = 1;
36558f07778SDavid Daney 		cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64);
36658f07778SDavid Daney 
36758f07778SDavid Daney 		addr &= ~CVMX_CACHE_LINE_MASK;
36858f07778SDavid Daney 
36958f07778SDavid Daney 		/* Set this core as debug core */
37058f07778SDavid Daney 		l2cdbg.s.ppnum = cvmx_get_core_num();
37158f07778SDavid Daney 		CVMX_SYNC;
37258f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_DBG, l2cdbg.u64);
37358f07778SDavid Daney 		cvmx_read_csr(CVMX_L2C_DBG);
37458f07778SDavid Daney 
37558f07778SDavid Daney 		lckoff.s.lck_offset = 0; /* Only lock 1 line at a time */
37658f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_LCKOFF, lckoff.u64);
37758f07778SDavid Daney 		cvmx_read_csr(CVMX_L2C_LCKOFF);
37858f07778SDavid Daney 
37958f07778SDavid Daney 		if (((union cvmx_l2c_cfg)(cvmx_read_csr(CVMX_L2C_CFG))).s.idxalias) {
380b8db85b5SDavid Daney 			int alias_shift = CVMX_L2C_IDX_ADDR_SHIFT + 2 * CVMX_L2_SET_BITS - 1;
381b8db85b5SDavid Daney 			uint64_t addr_tmp = addr ^ (addr & ((1 << alias_shift) - 1)) >> CVMX_L2_SET_BITS;
38215f68479SSteven J. Hill 
38358f07778SDavid Daney 			lckbase.s.lck_base = addr_tmp >> 7;
38415f68479SSteven J. Hill 
38558f07778SDavid Daney 		} else {
38658f07778SDavid Daney 			lckbase.s.lck_base = addr >> 7;
38758f07778SDavid Daney 		}
38858f07778SDavid Daney 
38958f07778SDavid Daney 		lckbase.s.lck_ena = 1;
39058f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_LCKBASE, lckbase.u64);
391b8db85b5SDavid Daney 		/* Make sure it gets there */
392b8db85b5SDavid Daney 		cvmx_read_csr(CVMX_L2C_LCKBASE);
39358f07778SDavid Daney 
39458f07778SDavid Daney 		fault_in(addr, CVMX_CACHE_LINE_SIZE);
39558f07778SDavid Daney 
39658f07778SDavid Daney 		lckbase.s.lck_ena = 0;
39758f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_LCKBASE, lckbase.u64);
398b8db85b5SDavid Daney 		/* Make sure it gets there */
399b8db85b5SDavid Daney 		cvmx_read_csr(CVMX_L2C_LCKBASE);
40058f07778SDavid Daney 
40158f07778SDavid Daney 		/* Stop being debug core */
40258f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_DBG, 0);
40358f07778SDavid Daney 		cvmx_read_csr(CVMX_L2C_DBG);
40458f07778SDavid Daney 
40558f07778SDavid Daney 		l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR);
40658f07778SDavid Daney 		if (l2t_err.s.lckerr || l2t_err.s.lckerr2)
40758f07778SDavid Daney 			retval = 1;  /* We were unable to lock the line */
40858f07778SDavid Daney 
40958f07778SDavid Daney 		cvmx_spinlock_unlock(&cvmx_l2c_spinlock);
41058f07778SDavid Daney 		return retval;
41158f07778SDavid Daney 	}
412b8db85b5SDavid Daney }
41358f07778SDavid Daney 
cvmx_l2c_lock_mem_region(uint64_t start,uint64_t len)41458f07778SDavid Daney int cvmx_l2c_lock_mem_region(uint64_t start, uint64_t len)
41558f07778SDavid Daney {
41658f07778SDavid Daney 	int retval = 0;
41758f07778SDavid Daney 
41858f07778SDavid Daney 	/* Round start/end to cache line boundaries */
41958f07778SDavid Daney 	len += start & CVMX_CACHE_LINE_MASK;
42058f07778SDavid Daney 	start &= ~CVMX_CACHE_LINE_MASK;
42158f07778SDavid Daney 	len = (len + CVMX_CACHE_LINE_MASK) & ~CVMX_CACHE_LINE_MASK;
42258f07778SDavid Daney 
42358f07778SDavid Daney 	while (len) {
42458f07778SDavid Daney 		retval += cvmx_l2c_lock_line(start);
42558f07778SDavid Daney 		start += CVMX_CACHE_LINE_SIZE;
42658f07778SDavid Daney 		len -= CVMX_CACHE_LINE_SIZE;
42758f07778SDavid Daney 	}
42858f07778SDavid Daney 	return retval;
42958f07778SDavid Daney }
43058f07778SDavid Daney 
cvmx_l2c_flush(void)43158f07778SDavid Daney void cvmx_l2c_flush(void)
43258f07778SDavid Daney {
43358f07778SDavid Daney 	uint64_t assoc, set;
43458f07778SDavid Daney 	uint64_t n_assoc, n_set;
43558f07778SDavid Daney 
436b8db85b5SDavid Daney 	n_set = cvmx_l2c_get_num_sets();
437b8db85b5SDavid Daney 	n_assoc = cvmx_l2c_get_num_assoc();
43858f07778SDavid Daney 
439b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
440b8db85b5SDavid Daney 		uint64_t address;
441b8db85b5SDavid Daney 		/* These may look like constants, but they aren't... */
442b8db85b5SDavid Daney 		int assoc_shift = CVMX_L2C_TAG_ADDR_ALIAS_SHIFT;
443b8db85b5SDavid Daney 		int set_shift = CVMX_L2C_IDX_ADDR_SHIFT;
44415f68479SSteven J. Hill 
44558f07778SDavid Daney 		for (set = 0; set < n_set; set++) {
44658f07778SDavid Daney 			for (assoc = 0; assoc < n_assoc; assoc++) {
447b8db85b5SDavid Daney 				address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
448b8db85b5SDavid Daney 						       (assoc << assoc_shift) | (set << set_shift));
449b8db85b5SDavid Daney 				CVMX_CACHE_WBIL2I(address, 0);
450b8db85b5SDavid Daney 			}
451b8db85b5SDavid Daney 		}
452b8db85b5SDavid Daney 	} else {
453b8db85b5SDavid Daney 		for (set = 0; set < n_set; set++)
454b8db85b5SDavid Daney 			for (assoc = 0; assoc < n_assoc; assoc++)
455b8db85b5SDavid Daney 				cvmx_l2c_flush_line(assoc, set);
45658f07778SDavid Daney 	}
45758f07778SDavid Daney }
45858f07778SDavid Daney 
45958f07778SDavid Daney 
cvmx_l2c_unlock_line(uint64_t address)46058f07778SDavid Daney int cvmx_l2c_unlock_line(uint64_t address)
46158f07778SDavid Daney {
462b8db85b5SDavid Daney 
463b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
46458f07778SDavid Daney 		int assoc;
46558f07778SDavid Daney 		union cvmx_l2c_tag tag;
466b8db85b5SDavid Daney 		uint32_t tag_addr;
467b8db85b5SDavid Daney 		uint32_t index = cvmx_l2c_address_to_index(address);
468b8db85b5SDavid Daney 
469b8db85b5SDavid Daney 		tag_addr = ((address >> CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) & ((1 << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) - 1));
470b8db85b5SDavid Daney 
471b8db85b5SDavid Daney 		/*
472b8db85b5SDavid Daney 		 * For 63XX, we can flush a line by using the physical
473b8db85b5SDavid Daney 		 * address directly, so finding the cache line used by
474b8db85b5SDavid Daney 		 * the address is only required to provide the proper
475b8db85b5SDavid Daney 		 * return value for the function.
476b8db85b5SDavid Daney 		 */
477b8db85b5SDavid Daney 		for (assoc = 0; assoc < CVMX_L2_ASSOC; assoc++) {
478b8db85b5SDavid Daney 			tag = cvmx_l2c_get_tag(assoc, index);
479b8db85b5SDavid Daney 
480b8db85b5SDavid Daney 			if (tag.s.V && (tag.s.addr == tag_addr)) {
481b8db85b5SDavid Daney 				CVMX_CACHE_WBIL2(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, address), 0);
482b8db85b5SDavid Daney 				return tag.s.L;
483b8db85b5SDavid Daney 			}
484b8db85b5SDavid Daney 		}
485b8db85b5SDavid Daney 	} else {
486b8db85b5SDavid Daney 		int assoc;
487b8db85b5SDavid Daney 		union cvmx_l2c_tag tag;
48858f07778SDavid Daney 		uint32_t tag_addr;
48958f07778SDavid Daney 
49058f07778SDavid Daney 		uint32_t index = cvmx_l2c_address_to_index(address);
49158f07778SDavid Daney 
49258f07778SDavid Daney 		/* Compute portion of address that is stored in tag */
493b8db85b5SDavid Daney 		tag_addr = ((address >> CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) & ((1 << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) - 1));
49458f07778SDavid Daney 		for (assoc = 0; assoc < CVMX_L2_ASSOC; assoc++) {
495b8db85b5SDavid Daney 			tag = cvmx_l2c_get_tag(assoc, index);
49658f07778SDavid Daney 
49758f07778SDavid Daney 			if (tag.s.V && (tag.s.addr == tag_addr)) {
498b8db85b5SDavid Daney 				cvmx_l2c_flush_line(assoc, index);
49958f07778SDavid Daney 				return tag.s.L;
50058f07778SDavid Daney 			}
50158f07778SDavid Daney 		}
502b8db85b5SDavid Daney 	}
50358f07778SDavid Daney 	return 0;
50458f07778SDavid Daney }
50558f07778SDavid Daney 
cvmx_l2c_unlock_mem_region(uint64_t start,uint64_t len)50658f07778SDavid Daney int cvmx_l2c_unlock_mem_region(uint64_t start, uint64_t len)
50758f07778SDavid Daney {
50858f07778SDavid Daney 	int num_unlocked = 0;
50958f07778SDavid Daney 	/* Round start/end to cache line boundaries */
51058f07778SDavid Daney 	len += start & CVMX_CACHE_LINE_MASK;
51158f07778SDavid Daney 	start &= ~CVMX_CACHE_LINE_MASK;
51258f07778SDavid Daney 	len = (len + CVMX_CACHE_LINE_MASK) & ~CVMX_CACHE_LINE_MASK;
51358f07778SDavid Daney 	while (len > 0) {
51458f07778SDavid Daney 		num_unlocked += cvmx_l2c_unlock_line(start);
51558f07778SDavid Daney 		start += CVMX_CACHE_LINE_SIZE;
51658f07778SDavid Daney 		len -= CVMX_CACHE_LINE_SIZE;
51758f07778SDavid Daney 	}
51858f07778SDavid Daney 
51958f07778SDavid Daney 	return num_unlocked;
52058f07778SDavid Daney }
52158f07778SDavid Daney 
52258f07778SDavid Daney /*
52358f07778SDavid Daney  * Internal l2c tag types.  These are converted to a generic structure
52458f07778SDavid Daney  * that can be used on all chips.
52558f07778SDavid Daney  */
52658f07778SDavid Daney union __cvmx_l2c_tag {
52758f07778SDavid Daney 	uint64_t u64;
52858f07778SDavid Daney 	struct cvmx_l2c_tag_cn50xx {
52915f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t reserved:40,
53015f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t V:1,		/* Line valid */
53115f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t D:1,		/* Line dirty */
53215f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t L:1,		/* Line locked */
53315f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t U:1,		/* Use, LRU eviction */
53415f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t addr:20,	/* Phys addr (33..14) */
53515f68479SSteven J. Hill 		;))))))
53658f07778SDavid Daney 	} cn50xx;
53758f07778SDavid Daney 	struct cvmx_l2c_tag_cn30xx {
53815f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t reserved:41,
53915f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t V:1,		/* Line valid */
54015f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t D:1,		/* Line dirty */
54115f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t L:1,		/* Line locked */
54215f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t U:1,		/* Use, LRU eviction */
54315f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t addr:19,	/* Phys addr (33..15) */
54415f68479SSteven J. Hill 		;))))))
54558f07778SDavid Daney 	} cn30xx;
54658f07778SDavid Daney 	struct cvmx_l2c_tag_cn31xx {
54715f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t reserved:42,
54815f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t V:1,		/* Line valid */
54915f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t D:1,		/* Line dirty */
55015f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t L:1,		/* Line locked */
55115f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t U:1,		/* Use, LRU eviction */
55215f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t addr:18,	/* Phys addr (33..16) */
55315f68479SSteven J. Hill 		;))))))
55458f07778SDavid Daney 	} cn31xx;
55558f07778SDavid Daney 	struct cvmx_l2c_tag_cn38xx {
55615f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t reserved:43,
55715f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t V:1,		/* Line valid */
55815f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t D:1,		/* Line dirty */
55915f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t L:1,		/* Line locked */
56015f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t U:1,		/* Use, LRU eviction */
56115f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t addr:17,	/* Phys addr (33..17) */
56215f68479SSteven J. Hill 		;))))))
56358f07778SDavid Daney 	} cn38xx;
56458f07778SDavid Daney 	struct cvmx_l2c_tag_cn58xx {
56515f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t reserved:44,
56615f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t V:1,		/* Line valid */
56715f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t D:1,		/* Line dirty */
56815f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t L:1,		/* Line locked */
56915f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t U:1,		/* Use, LRU eviction */
57015f68479SSteven J. Hill 		__BITFIELD_FIELD(uint64_t addr:16,	/* Phys addr (33..18) */
57115f68479SSteven J. Hill 		;))))))
57258f07778SDavid Daney 	} cn58xx;
57358f07778SDavid Daney 	struct cvmx_l2c_tag_cn58xx cn56xx;	/* 2048 sets */
57458f07778SDavid Daney 	struct cvmx_l2c_tag_cn31xx cn52xx;	/* 512 sets */
57558f07778SDavid Daney };
57658f07778SDavid Daney 
577b8db85b5SDavid Daney 
578*16df55ceSRandy Dunlap /*
57958f07778SDavid Daney  * @INTERNAL
58058f07778SDavid Daney  * Function to read a L2C tag.  This code make the current core
58158f07778SDavid Daney  * the 'debug core' for the L2.  This code must only be executed by
58258f07778SDavid Daney  * 1 core at a time.
58358f07778SDavid Daney  *
58458f07778SDavid Daney  * @assoc:  Association (way) of the tag to dump
58558f07778SDavid Daney  * @index:  Index of the cacheline
58658f07778SDavid Daney  *
58758f07778SDavid Daney  * Returns The Octeon model specific tag structure.  This is
58858f07778SDavid Daney  *	   translated by a wrapper function to a generic form that is
58958f07778SDavid Daney  *	   easier for applications to use.
59058f07778SDavid Daney  */
__read_l2_tag(uint64_t assoc,uint64_t index)59158f07778SDavid Daney static union __cvmx_l2c_tag __read_l2_tag(uint64_t assoc, uint64_t index)
59258f07778SDavid Daney {
59358f07778SDavid Daney 
594b8db85b5SDavid Daney 	uint64_t debug_tag_addr = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, (index << 7) + 96);
59558f07778SDavid Daney 	uint64_t core = cvmx_get_core_num();
59658f07778SDavid Daney 	union __cvmx_l2c_tag tag_val;
59758f07778SDavid Daney 	uint64_t dbg_addr = CVMX_L2C_DBG;
59858f07778SDavid Daney 	unsigned long flags;
59958f07778SDavid Daney 	union cvmx_l2c_dbg debug_val;
60015f68479SSteven J. Hill 
60158f07778SDavid Daney 	debug_val.u64 = 0;
60258f07778SDavid Daney 	/*
603b8db85b5SDavid Daney 	 * For low core count parts, the core number is always small
604b8db85b5SDavid Daney 	 * enough to stay in the correct field and not set any
605b8db85b5SDavid Daney 	 * reserved bits.
60658f07778SDavid Daney 	 */
60758f07778SDavid Daney 	debug_val.s.ppnum = core;
60858f07778SDavid Daney 	debug_val.s.l2t = 1;
60958f07778SDavid Daney 	debug_val.s.set = assoc;
610b8db85b5SDavid Daney 
611b8db85b5SDavid Daney 	local_irq_save(flags);
61258f07778SDavid Daney 	/*
61358f07778SDavid Daney 	 * Make sure core is quiet (no prefetches, etc.) before
61458f07778SDavid Daney 	 * entering debug mode.
61558f07778SDavid Daney 	 */
61658f07778SDavid Daney 	CVMX_SYNC;
61758f07778SDavid Daney 	/* Flush L1 to make sure debug load misses L1 */
61858f07778SDavid Daney 	CVMX_DCACHE_INVALIDATE;
61958f07778SDavid Daney 
62058f07778SDavid Daney 	/*
62158f07778SDavid Daney 	 * The following must be done in assembly as when in debug
62258f07778SDavid Daney 	 * mode all data loads from L2 return special debug data, not
623b8db85b5SDavid Daney 	 * normal memory contents.  Also, interrupts must be disabled,
624b8db85b5SDavid Daney 	 * since if an interrupt occurs while in debug mode the ISR
625b8db85b5SDavid Daney 	 * will get debug data from all its memory * reads instead of
626b8db85b5SDavid Daney 	 * the contents of memory.
62758f07778SDavid Daney 	 */
62858f07778SDavid Daney 
629b8db85b5SDavid Daney 	asm volatile (
630b8db85b5SDavid Daney 		".set push\n\t"
631b8db85b5SDavid Daney 		".set mips64\n\t"
632b8db85b5SDavid Daney 		".set noreorder\n\t"
633b8db85b5SDavid Daney 		"sd    %[dbg_val], 0(%[dbg_addr])\n\t"	 /* Enter debug mode, wait for store */
634b8db85b5SDavid Daney 		"ld    $0, 0(%[dbg_addr])\n\t"
635b8db85b5SDavid Daney 		"ld    %[tag_val], 0(%[tag_addr])\n\t"	 /* Read L2C tag data */
636b8db85b5SDavid Daney 		"sd    $0, 0(%[dbg_addr])\n\t"		/* Exit debug mode, wait for store */
637b8db85b5SDavid Daney 		"ld    $0, 0(%[dbg_addr])\n\t"
638b8db85b5SDavid Daney 		"cache 9, 0($0)\n\t"		 /* Invalidate dcache to discard debug data */
639b8db85b5SDavid Daney 		".set pop"
640b8db85b5SDavid Daney 		: [tag_val] "=r" (tag_val)
641b8db85b5SDavid Daney 		: [dbg_addr] "r" (dbg_addr), [dbg_val] "r" (debug_val), [tag_addr] "r" (debug_tag_addr)
642b8db85b5SDavid Daney 		: "memory");
64358f07778SDavid Daney 
64458f07778SDavid Daney 	local_irq_restore(flags);
64558f07778SDavid Daney 
646b8db85b5SDavid Daney 	return tag_val;
64758f07778SDavid Daney }
64858f07778SDavid Daney 
649b8db85b5SDavid Daney 
cvmx_l2c_get_tag(uint32_t association,uint32_t index)65058f07778SDavid Daney union cvmx_l2c_tag cvmx_l2c_get_tag(uint32_t association, uint32_t index)
65158f07778SDavid Daney {
65258f07778SDavid Daney 	union cvmx_l2c_tag tag;
65358f07778SDavid Daney 
65415f68479SSteven J. Hill 	tag.u64 = 0;
65558f07778SDavid Daney 	if ((int)association >= cvmx_l2c_get_num_assoc()) {
656b8db85b5SDavid Daney 		cvmx_dprintf("ERROR: cvmx_l2c_get_tag association out of range\n");
65758f07778SDavid Daney 		return tag;
65858f07778SDavid Daney 	}
65958f07778SDavid Daney 	if ((int)index >= cvmx_l2c_get_num_sets()) {
660b8db85b5SDavid Daney 		cvmx_dprintf("ERROR: cvmx_l2c_get_tag index out of range (arg: %d, max: %d)\n",
661b8db85b5SDavid Daney 			     (int)index, cvmx_l2c_get_num_sets());
66258f07778SDavid Daney 		return tag;
66358f07778SDavid Daney 	}
664b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
665b8db85b5SDavid Daney 		union cvmx_l2c_tadx_tag l2c_tadx_tag;
666b8db85b5SDavid Daney 		uint64_t address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
667b8db85b5SDavid Daney 						(association << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) |
668b8db85b5SDavid Daney 						(index << CVMX_L2C_IDX_ADDR_SHIFT));
669b8db85b5SDavid Daney 		/*
670b8db85b5SDavid Daney 		 * Use L2 cache Index load tag cache instruction, as
671b8db85b5SDavid Daney 		 * hardware loads the virtual tag for the L2 cache
672b8db85b5SDavid Daney 		 * block with the contents of L2C_TAD0_TAG
673b8db85b5SDavid Daney 		 * register.
674b8db85b5SDavid Daney 		 */
675b8db85b5SDavid Daney 		CVMX_CACHE_LTGL2I(address, 0);
676b8db85b5SDavid Daney 		CVMX_SYNC;   /* make sure CVMX_L2C_TADX_TAG is updated */
677b8db85b5SDavid Daney 		l2c_tadx_tag.u64 = cvmx_read_csr(CVMX_L2C_TADX_TAG(0));
678b8db85b5SDavid Daney 
679b8db85b5SDavid Daney 		tag.s.V	    = l2c_tadx_tag.s.valid;
680b8db85b5SDavid Daney 		tag.s.D	    = l2c_tadx_tag.s.dirty;
681b8db85b5SDavid Daney 		tag.s.L	    = l2c_tadx_tag.s.lock;
682b8db85b5SDavid Daney 		tag.s.U	    = l2c_tadx_tag.s.use;
683b8db85b5SDavid Daney 		tag.s.addr  = l2c_tadx_tag.s.tag;
684b8db85b5SDavid Daney 	} else {
685b8db85b5SDavid Daney 		union __cvmx_l2c_tag tmp_tag;
68658f07778SDavid Daney 		/* __read_l2_tag is intended for internal use only */
68758f07778SDavid Daney 		tmp_tag = __read_l2_tag(association, index);
68858f07778SDavid Daney 
68958f07778SDavid Daney 		/*
690b8db85b5SDavid Daney 		 * Convert all tag structure types to generic version,
691b8db85b5SDavid Daney 		 * as it can represent all models.
69258f07778SDavid Daney 		 */
69358f07778SDavid Daney 		if (OCTEON_IS_MODEL(OCTEON_CN58XX) || OCTEON_IS_MODEL(OCTEON_CN56XX)) {
69458f07778SDavid Daney 			tag.s.V	   = tmp_tag.cn58xx.V;
69558f07778SDavid Daney 			tag.s.D	   = tmp_tag.cn58xx.D;
69658f07778SDavid Daney 			tag.s.L	   = tmp_tag.cn58xx.L;
69758f07778SDavid Daney 			tag.s.U	   = tmp_tag.cn58xx.U;
69858f07778SDavid Daney 			tag.s.addr = tmp_tag.cn58xx.addr;
69958f07778SDavid Daney 		} else if (OCTEON_IS_MODEL(OCTEON_CN38XX)) {
70058f07778SDavid Daney 			tag.s.V	   = tmp_tag.cn38xx.V;
70158f07778SDavid Daney 			tag.s.D	   = tmp_tag.cn38xx.D;
70258f07778SDavid Daney 			tag.s.L	   = tmp_tag.cn38xx.L;
70358f07778SDavid Daney 			tag.s.U	   = tmp_tag.cn38xx.U;
70458f07778SDavid Daney 			tag.s.addr = tmp_tag.cn38xx.addr;
705b8db85b5SDavid Daney 		} else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) {
70658f07778SDavid Daney 			tag.s.V	   = tmp_tag.cn31xx.V;
70758f07778SDavid Daney 			tag.s.D	   = tmp_tag.cn31xx.D;
70858f07778SDavid Daney 			tag.s.L	   = tmp_tag.cn31xx.L;
70958f07778SDavid Daney 			tag.s.U	   = tmp_tag.cn31xx.U;
71058f07778SDavid Daney 			tag.s.addr = tmp_tag.cn31xx.addr;
71158f07778SDavid Daney 		} else if (OCTEON_IS_MODEL(OCTEON_CN30XX)) {
71258f07778SDavid Daney 			tag.s.V	   = tmp_tag.cn30xx.V;
71358f07778SDavid Daney 			tag.s.D	   = tmp_tag.cn30xx.D;
71458f07778SDavid Daney 			tag.s.L	   = tmp_tag.cn30xx.L;
71558f07778SDavid Daney 			tag.s.U	   = tmp_tag.cn30xx.U;
71658f07778SDavid Daney 			tag.s.addr = tmp_tag.cn30xx.addr;
71758f07778SDavid Daney 		} else if (OCTEON_IS_MODEL(OCTEON_CN50XX)) {
71858f07778SDavid Daney 			tag.s.V	   = tmp_tag.cn50xx.V;
71958f07778SDavid Daney 			tag.s.D	   = tmp_tag.cn50xx.D;
72058f07778SDavid Daney 			tag.s.L	   = tmp_tag.cn50xx.L;
72158f07778SDavid Daney 			tag.s.U	   = tmp_tag.cn50xx.U;
72258f07778SDavid Daney 			tag.s.addr = tmp_tag.cn50xx.addr;
72358f07778SDavid Daney 		} else {
72458f07778SDavid Daney 			cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__);
72558f07778SDavid Daney 		}
726b8db85b5SDavid Daney 	}
72758f07778SDavid Daney 	return tag;
72858f07778SDavid Daney }
72958f07778SDavid Daney 
cvmx_l2c_address_to_index(uint64_t addr)73058f07778SDavid Daney uint32_t cvmx_l2c_address_to_index(uint64_t addr)
73158f07778SDavid Daney {
73258f07778SDavid Daney 	uint64_t idx = addr >> CVMX_L2C_IDX_ADDR_SHIFT;
733b8db85b5SDavid Daney 	int indxalias = 0;
734b8db85b5SDavid Daney 
735b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
736b8db85b5SDavid Daney 		union cvmx_l2c_ctl l2c_ctl;
73715f68479SSteven J. Hill 
738b8db85b5SDavid Daney 		l2c_ctl.u64 = cvmx_read_csr(CVMX_L2C_CTL);
739b8db85b5SDavid Daney 		indxalias = !l2c_ctl.s.disidxalias;
740b8db85b5SDavid Daney 	} else {
74158f07778SDavid Daney 		union cvmx_l2c_cfg l2c_cfg;
74215f68479SSteven J. Hill 
74358f07778SDavid Daney 		l2c_cfg.u64 = cvmx_read_csr(CVMX_L2C_CFG);
744b8db85b5SDavid Daney 		indxalias = l2c_cfg.s.idxalias;
745b8db85b5SDavid Daney 	}
74658f07778SDavid Daney 
747b8db85b5SDavid Daney 	if (indxalias) {
748b8db85b5SDavid Daney 		if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
749b8db85b5SDavid Daney 			uint32_t a_14_12 = (idx / (CVMX_L2C_MEMBANK_SELECT_SIZE/(1<<CVMX_L2C_IDX_ADDR_SHIFT))) & 0x7;
75015f68479SSteven J. Hill 
751b8db85b5SDavid Daney 			idx ^= idx / cvmx_l2c_get_num_sets();
752b8db85b5SDavid Daney 			idx ^= a_14_12;
753b8db85b5SDavid Daney 		} else {
754b8db85b5SDavid Daney 			idx ^= ((addr & CVMX_L2C_ALIAS_MASK) >> CVMX_L2C_TAG_ADDR_ALIAS_SHIFT);
755b8db85b5SDavid Daney 		}
75658f07778SDavid Daney 	}
75758f07778SDavid Daney 	idx &= CVMX_L2C_IDX_MASK;
75858f07778SDavid Daney 	return idx;
75958f07778SDavid Daney }
76058f07778SDavid Daney 
cvmx_l2c_get_cache_size_bytes(void)76158f07778SDavid Daney int cvmx_l2c_get_cache_size_bytes(void)
76258f07778SDavid Daney {
76358f07778SDavid Daney 	return cvmx_l2c_get_num_sets() * cvmx_l2c_get_num_assoc() *
76458f07778SDavid Daney 		CVMX_CACHE_LINE_SIZE;
76558f07778SDavid Daney }
76658f07778SDavid Daney 
767*16df55ceSRandy Dunlap /*
76858f07778SDavid Daney  * Return log base 2 of the number of sets in the L2 cache
76958f07778SDavid Daney  */
cvmx_l2c_get_set_bits(void)77058f07778SDavid Daney int cvmx_l2c_get_set_bits(void)
77158f07778SDavid Daney {
77258f07778SDavid Daney 	int l2_set_bits;
77315f68479SSteven J. Hill 
77458f07778SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))
77558f07778SDavid Daney 		l2_set_bits = 11;	/* 2048 sets */
776b8db85b5SDavid Daney 	else if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN63XX))
77758f07778SDavid Daney 		l2_set_bits = 10;	/* 1024 sets */
778b8db85b5SDavid Daney 	else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN52XX))
77958f07778SDavid Daney 		l2_set_bits = 9;	/* 512 sets */
78058f07778SDavid Daney 	else if (OCTEON_IS_MODEL(OCTEON_CN30XX))
78158f07778SDavid Daney 		l2_set_bits = 8;	/* 256 sets */
78258f07778SDavid Daney 	else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
78358f07778SDavid Daney 		l2_set_bits = 7;	/* 128 sets */
78458f07778SDavid Daney 	else {
78558f07778SDavid Daney 		cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__);
78658f07778SDavid Daney 		l2_set_bits = 11;	/* 2048 sets */
78758f07778SDavid Daney 	}
78858f07778SDavid Daney 	return l2_set_bits;
78958f07778SDavid Daney }
79058f07778SDavid Daney 
79158f07778SDavid Daney /* Return the number of sets in the L2 Cache */
cvmx_l2c_get_num_sets(void)79258f07778SDavid Daney int cvmx_l2c_get_num_sets(void)
79358f07778SDavid Daney {
79458f07778SDavid Daney 	return 1 << cvmx_l2c_get_set_bits();
79558f07778SDavid Daney }
79658f07778SDavid Daney 
79758f07778SDavid Daney /* Return the number of associations in the L2 Cache */
cvmx_l2c_get_num_assoc(void)79858f07778SDavid Daney int cvmx_l2c_get_num_assoc(void)
79958f07778SDavid Daney {
80058f07778SDavid Daney 	int l2_assoc;
80115f68479SSteven J. Hill 
80258f07778SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN56XX) ||
80358f07778SDavid Daney 	    OCTEON_IS_MODEL(OCTEON_CN52XX) ||
80458f07778SDavid Daney 	    OCTEON_IS_MODEL(OCTEON_CN58XX) ||
805b8db85b5SDavid Daney 	    OCTEON_IS_MODEL(OCTEON_CN50XX) ||
806b8db85b5SDavid Daney 	    OCTEON_IS_MODEL(OCTEON_CN38XX))
80758f07778SDavid Daney 		l2_assoc = 8;
808b8db85b5SDavid Daney 	else if (OCTEON_IS_MODEL(OCTEON_CN63XX))
809b8db85b5SDavid Daney 		l2_assoc = 16;
81058f07778SDavid Daney 	else if (OCTEON_IS_MODEL(OCTEON_CN31XX) ||
81158f07778SDavid Daney 		 OCTEON_IS_MODEL(OCTEON_CN30XX))
81258f07778SDavid Daney 		l2_assoc = 4;
81358f07778SDavid Daney 	else {
81458f07778SDavid Daney 		cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__);
81558f07778SDavid Daney 		l2_assoc = 8;
81658f07778SDavid Daney 	}
81758f07778SDavid Daney 
81858f07778SDavid Daney 	/* Check to see if part of the cache is disabled */
819b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
820b8db85b5SDavid Daney 		union cvmx_mio_fus_dat3 mio_fus_dat3;
82158f07778SDavid Daney 
822b8db85b5SDavid Daney 		mio_fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3);
823b8db85b5SDavid Daney 		/*
824b8db85b5SDavid Daney 		 * cvmx_mio_fus_dat3.s.l2c_crip fuses map as follows
825b8db85b5SDavid Daney 		 * <2> will be not used for 63xx
826b8db85b5SDavid Daney 		 * <1> disables 1/2 ways
827b8db85b5SDavid Daney 		 * <0> disables 1/4 ways
828b8db85b5SDavid Daney 		 * They are cumulative, so for 63xx:
829b8db85b5SDavid Daney 		 * <1> <0>
830b8db85b5SDavid Daney 		 * 0 0 16-way 2MB cache
831b8db85b5SDavid Daney 		 * 0 1 12-way 1.5MB cache
832b8db85b5SDavid Daney 		 * 1 0 8-way 1MB cache
833b8db85b5SDavid Daney 		 * 1 1 4-way 512KB cache
834b8db85b5SDavid Daney 		 */
835b8db85b5SDavid Daney 
836b8db85b5SDavid Daney 		if (mio_fus_dat3.s.l2c_crip == 3)
837b8db85b5SDavid Daney 			l2_assoc = 4;
838b8db85b5SDavid Daney 		else if (mio_fus_dat3.s.l2c_crip == 2)
839b8db85b5SDavid Daney 			l2_assoc = 8;
840b8db85b5SDavid Daney 		else if (mio_fus_dat3.s.l2c_crip == 1)
841b8db85b5SDavid Daney 			l2_assoc = 12;
842b8db85b5SDavid Daney 	} else {
84315f68479SSteven J. Hill 		uint64_t l2d_fus3;
84415f68479SSteven J. Hill 
84515f68479SSteven J. Hill 		l2d_fus3 = cvmx_read_csr(CVMX_L2D_FUS3);
846b8db85b5SDavid Daney 		/*
847b8db85b5SDavid Daney 		 * Using shifts here, as bit position names are
848b8db85b5SDavid Daney 		 * different for each model but they all mean the
849b8db85b5SDavid Daney 		 * same.
850b8db85b5SDavid Daney 		 */
85115f68479SSteven J. Hill 		if ((l2d_fus3 >> 35) & 0x1)
852b8db85b5SDavid Daney 			l2_assoc = l2_assoc >> 2;
85315f68479SSteven J. Hill 		else if ((l2d_fus3 >> 34) & 0x1)
854b8db85b5SDavid Daney 			l2_assoc = l2_assoc >> 1;
855b8db85b5SDavid Daney 	}
85658f07778SDavid Daney 	return l2_assoc;
85758f07778SDavid Daney }
85858f07778SDavid Daney 
859*16df55ceSRandy Dunlap /*
86058f07778SDavid Daney  * Flush a line from the L2 cache
86158f07778SDavid Daney  * This should only be called from one core at a time, as this routine
86258f07778SDavid Daney  * sets the core to the 'debug' core in order to flush the line.
86358f07778SDavid Daney  *
86458f07778SDavid Daney  * @assoc:  Association (or way) to flush
86558f07778SDavid Daney  * @index:  Index to flush
86658f07778SDavid Daney  */
cvmx_l2c_flush_line(uint32_t assoc,uint32_t index)86758f07778SDavid Daney void cvmx_l2c_flush_line(uint32_t assoc, uint32_t index)
86858f07778SDavid Daney {
869b8db85b5SDavid Daney 	/* Check the range of the index. */
870b8db85b5SDavid Daney 	if (index > (uint32_t)cvmx_l2c_get_num_sets()) {
871b8db85b5SDavid Daney 		cvmx_dprintf("ERROR: cvmx_l2c_flush_line index out of range.\n");
872b8db85b5SDavid Daney 		return;
873b8db85b5SDavid Daney 	}
874b8db85b5SDavid Daney 
875b8db85b5SDavid Daney 	/* Check the range of association. */
876b8db85b5SDavid Daney 	if (assoc > (uint32_t)cvmx_l2c_get_num_assoc()) {
877b8db85b5SDavid Daney 		cvmx_dprintf("ERROR: cvmx_l2c_flush_line association out of range.\n");
878b8db85b5SDavid Daney 		return;
879b8db85b5SDavid Daney 	}
880b8db85b5SDavid Daney 
881b8db85b5SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
882b8db85b5SDavid Daney 		uint64_t address;
883b8db85b5SDavid Daney 		/* Create the address based on index and association.
884b8db85b5SDavid Daney 		 * Bits<20:17> select the way of the cache block involved in
885b8db85b5SDavid Daney 		 *	       the operation
886b8db85b5SDavid Daney 		 * Bits<16:7> of the effect address select the index
887b8db85b5SDavid Daney 		 */
888b8db85b5SDavid Daney 		address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
889b8db85b5SDavid Daney 				(assoc << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) |
890b8db85b5SDavid Daney 				(index << CVMX_L2C_IDX_ADDR_SHIFT));
891b8db85b5SDavid Daney 		CVMX_CACHE_WBIL2I(address, 0);
892b8db85b5SDavid Daney 	} else {
89358f07778SDavid Daney 		union cvmx_l2c_dbg l2cdbg;
89458f07778SDavid Daney 
89558f07778SDavid Daney 		l2cdbg.u64 = 0;
896b8db85b5SDavid Daney 		if (!OCTEON_IS_MODEL(OCTEON_CN30XX))
89758f07778SDavid Daney 			l2cdbg.s.ppnum = cvmx_get_core_num();
89858f07778SDavid Daney 		l2cdbg.s.finv = 1;
89958f07778SDavid Daney 
90058f07778SDavid Daney 		l2cdbg.s.set = assoc;
901b8db85b5SDavid Daney 		cvmx_spinlock_lock(&cvmx_l2c_spinlock);
90258f07778SDavid Daney 		/*
903b8db85b5SDavid Daney 		 * Enter debug mode, and make sure all other writes
904b8db85b5SDavid Daney 		 * complete before we enter debug mode
90558f07778SDavid Daney 		 */
906b8db85b5SDavid Daney 		CVMX_SYNC;
90758f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_DBG, l2cdbg.u64);
90858f07778SDavid Daney 		cvmx_read_csr(CVMX_L2C_DBG);
90958f07778SDavid Daney 
910b8db85b5SDavid Daney 		CVMX_PREPARE_FOR_STORE(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
911b8db85b5SDavid Daney 						    index * CVMX_CACHE_LINE_SIZE),
912b8db85b5SDavid Daney 				       0);
91358f07778SDavid Daney 		/* Exit debug mode */
914b8db85b5SDavid Daney 		CVMX_SYNC;
91558f07778SDavid Daney 		cvmx_write_csr(CVMX_L2C_DBG, 0);
91658f07778SDavid Daney 		cvmx_read_csr(CVMX_L2C_DBG);
917b8db85b5SDavid Daney 		cvmx_spinlock_unlock(&cvmx_l2c_spinlock);
918b8db85b5SDavid Daney 	}
91958f07778SDavid Daney }
920