18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2006 Chris Dearman (chris@mips.com), 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/init.h> 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/sched.h> 88c2ecf20Sopenharmony_ci#include <linux/mm.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <asm/cpu-type.h> 118c2ecf20Sopenharmony_ci#include <asm/mipsregs.h> 128c2ecf20Sopenharmony_ci#include <asm/bcache.h> 138c2ecf20Sopenharmony_ci#include <asm/cacheops.h> 148c2ecf20Sopenharmony_ci#include <asm/page.h> 158c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 168c2ecf20Sopenharmony_ci#include <asm/r4kcache.h> 178c2ecf20Sopenharmony_ci#include <asm/mips-cps.h> 188c2ecf20Sopenharmony_ci#include <asm/bootinfo.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * MIPS32/MIPS64 L2 cache handling 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * Writeback and invalidate the secondary cache before DMA. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_cistatic void mips_sc_wback_inv(unsigned long addr, unsigned long size) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci blast_scache_range(addr, addr + size); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * Invalidate the secondary cache before DMA. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic void mips_sc_inv(unsigned long addr, unsigned long size) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci unsigned long lsize = cpu_scache_line_size(); 388c2ecf20Sopenharmony_ci unsigned long almask = ~(lsize - 1); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci cache_op(Hit_Writeback_Inv_SD, addr & almask); 418c2ecf20Sopenharmony_ci cache_op(Hit_Writeback_Inv_SD, (addr + size - 1) & almask); 428c2ecf20Sopenharmony_ci blast_inv_scache_range(addr, addr + size); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void mips_sc_enable(void) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci /* L2 cache is permanently enabled */ 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void mips_sc_disable(void) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci /* L2 cache is permanently enabled */ 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void mips_sc_prefetch_enable(void) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci unsigned long pftctl; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (mips_cm_revision() < CM_REV_CM2_5) 608c2ecf20Sopenharmony_ci return; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * If there is one or more L2 prefetch unit present then enable 648c2ecf20Sopenharmony_ci * prefetching for both code & data, for all ports. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci pftctl = read_gcr_l2_pft_control(); 678c2ecf20Sopenharmony_ci if (pftctl & CM_GCR_L2_PFT_CONTROL_NPFT) { 688c2ecf20Sopenharmony_ci pftctl &= ~CM_GCR_L2_PFT_CONTROL_PAGEMASK; 698c2ecf20Sopenharmony_ci pftctl |= PAGE_MASK & CM_GCR_L2_PFT_CONTROL_PAGEMASK; 708c2ecf20Sopenharmony_ci pftctl |= CM_GCR_L2_PFT_CONTROL_PFTEN; 718c2ecf20Sopenharmony_ci write_gcr_l2_pft_control(pftctl); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci set_gcr_l2_pft_control_b(CM_GCR_L2_PFT_CONTROL_B_PORTID | 748c2ecf20Sopenharmony_ci CM_GCR_L2_PFT_CONTROL_B_CEN); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void mips_sc_prefetch_disable(void) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci if (mips_cm_revision() < CM_REV_CM2_5) 818c2ecf20Sopenharmony_ci return; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci clear_gcr_l2_pft_control(CM_GCR_L2_PFT_CONTROL_PFTEN); 848c2ecf20Sopenharmony_ci clear_gcr_l2_pft_control_b(CM_GCR_L2_PFT_CONTROL_B_PORTID | 858c2ecf20Sopenharmony_ci CM_GCR_L2_PFT_CONTROL_B_CEN); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic bool mips_sc_prefetch_is_enabled(void) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci unsigned long pftctl; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (mips_cm_revision() < CM_REV_CM2_5) 938c2ecf20Sopenharmony_ci return false; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci pftctl = read_gcr_l2_pft_control(); 968c2ecf20Sopenharmony_ci if (!(pftctl & CM_GCR_L2_PFT_CONTROL_NPFT)) 978c2ecf20Sopenharmony_ci return false; 988c2ecf20Sopenharmony_ci return !!(pftctl & CM_GCR_L2_PFT_CONTROL_PFTEN); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic struct bcache_ops mips_sc_ops = { 1028c2ecf20Sopenharmony_ci .bc_enable = mips_sc_enable, 1038c2ecf20Sopenharmony_ci .bc_disable = mips_sc_disable, 1048c2ecf20Sopenharmony_ci .bc_wback_inv = mips_sc_wback_inv, 1058c2ecf20Sopenharmony_ci .bc_inv = mips_sc_inv, 1068c2ecf20Sopenharmony_ci .bc_prefetch_enable = mips_sc_prefetch_enable, 1078c2ecf20Sopenharmony_ci .bc_prefetch_disable = mips_sc_prefetch_disable, 1088c2ecf20Sopenharmony_ci .bc_prefetch_is_enabled = mips_sc_prefetch_is_enabled, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * Check if the L2 cache controller is activated on a particular platform. 1138c2ecf20Sopenharmony_ci * MTI's L2 controller and the L2 cache controller of Broadcom's BMIPS 1148c2ecf20Sopenharmony_ci * cores both use c0_config2's bit 12 as "L2 Bypass" bit, that is the 1158c2ecf20Sopenharmony_ci * cache being disabled. However there is no guarantee for this to be 1168c2ecf20Sopenharmony_ci * true on all platforms. In an act of stupidity the spec defined bits 1178c2ecf20Sopenharmony_ci * 12..15 as implementation defined so below function will eventually have 1188c2ecf20Sopenharmony_ci * to be replaced by a platform specific probe. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic inline int mips_sc_is_activated(struct cpuinfo_mips *c) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci unsigned int config2 = read_c0_config2(); 1238c2ecf20Sopenharmony_ci unsigned int tmp; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Check the bypass bit (L2B) */ 1268c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 1278c2ecf20Sopenharmony_ci case CPU_34K: 1288c2ecf20Sopenharmony_ci case CPU_74K: 1298c2ecf20Sopenharmony_ci case CPU_1004K: 1308c2ecf20Sopenharmony_ci case CPU_1074K: 1318c2ecf20Sopenharmony_ci case CPU_INTERAPTIV: 1328c2ecf20Sopenharmony_ci case CPU_PROAPTIV: 1338c2ecf20Sopenharmony_ci case CPU_P5600: 1348c2ecf20Sopenharmony_ci case CPU_BMIPS5000: 1358c2ecf20Sopenharmony_ci case CPU_QEMU_GENERIC: 1368c2ecf20Sopenharmony_ci case CPU_P6600: 1378c2ecf20Sopenharmony_ci if (config2 & (1 << 12)) 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci tmp = (config2 >> 4) & 0x0f; 1428c2ecf20Sopenharmony_ci if (0 < tmp && tmp <= 7) 1438c2ecf20Sopenharmony_ci c->scache.linesz = 2 << tmp; 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci return 1; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int mips_sc_probe_cm3(void) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct cpuinfo_mips *c = ¤t_cpu_data; 1528c2ecf20Sopenharmony_ci unsigned long cfg = read_gcr_l2_config(); 1538c2ecf20Sopenharmony_ci unsigned long sets, line_sz, assoc; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (cfg & CM_GCR_L2_CONFIG_BYPASS) 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci sets = cfg & CM_GCR_L2_CONFIG_SET_SIZE; 1598c2ecf20Sopenharmony_ci sets >>= __ffs(CM_GCR_L2_CONFIG_SET_SIZE); 1608c2ecf20Sopenharmony_ci if (sets) 1618c2ecf20Sopenharmony_ci c->scache.sets = 64 << sets; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci line_sz = cfg & CM_GCR_L2_CONFIG_LINE_SIZE; 1648c2ecf20Sopenharmony_ci line_sz >>= __ffs(CM_GCR_L2_CONFIG_LINE_SIZE); 1658c2ecf20Sopenharmony_ci if (line_sz) 1668c2ecf20Sopenharmony_ci c->scache.linesz = 2 << line_sz; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci assoc = cfg & CM_GCR_L2_CONFIG_ASSOC; 1698c2ecf20Sopenharmony_ci assoc >>= __ffs(CM_GCR_L2_CONFIG_ASSOC); 1708c2ecf20Sopenharmony_ci c->scache.ways = assoc + 1; 1718c2ecf20Sopenharmony_ci c->scache.waysize = c->scache.sets * c->scache.linesz; 1728c2ecf20Sopenharmony_ci c->scache.waybit = __ffs(c->scache.waysize); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (c->scache.linesz) { 1758c2ecf20Sopenharmony_ci c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT; 1768c2ecf20Sopenharmony_ci c->options |= MIPS_CPU_INCLUSIVE_CACHES; 1778c2ecf20Sopenharmony_ci return 1; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic inline int mips_sc_probe(void) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct cpuinfo_mips *c = ¤t_cpu_data; 1868c2ecf20Sopenharmony_ci unsigned int config1, config2; 1878c2ecf20Sopenharmony_ci unsigned int tmp; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Mark as not present until probe completed */ 1908c2ecf20Sopenharmony_ci c->scache.flags |= MIPS_CACHE_NOT_PRESENT; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (mips_cm_revision() >= CM_REV_CM3) 1938c2ecf20Sopenharmony_ci return mips_sc_probe_cm3(); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Ignore anything but MIPSxx processors */ 1968c2ecf20Sopenharmony_ci if (!(c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 | 1978c2ecf20Sopenharmony_ci MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | 1988c2ecf20Sopenharmony_ci MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | 1998c2ecf20Sopenharmony_ci MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6))) 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Does this MIPS32/MIPS64 CPU have a config2 register? */ 2038c2ecf20Sopenharmony_ci config1 = read_c0_config1(); 2048c2ecf20Sopenharmony_ci if (!(config1 & MIPS_CONF_M)) 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci config2 = read_c0_config2(); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!mips_sc_is_activated(c)) 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci tmp = (config2 >> 8) & 0x0f; 2138c2ecf20Sopenharmony_ci if (tmp <= 7) 2148c2ecf20Sopenharmony_ci c->scache.sets = 64 << tmp; 2158c2ecf20Sopenharmony_ci else 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci tmp = (config2 >> 0) & 0x0f; 2198c2ecf20Sopenharmony_ci if (tmp <= 7) 2208c2ecf20Sopenharmony_ci c->scache.ways = tmp + 1; 2218c2ecf20Sopenharmony_ci else 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (current_cpu_type() == CPU_XBURST) { 2258c2ecf20Sopenharmony_ci switch (mips_machtype) { 2268c2ecf20Sopenharmony_ci /* 2278c2ecf20Sopenharmony_ci * According to config2 it would be 5-ways, but that is 2288c2ecf20Sopenharmony_ci * contradicted by all documentation. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci case MACH_INGENIC_JZ4770: 2318c2ecf20Sopenharmony_ci case MACH_INGENIC_JZ4775: 2328c2ecf20Sopenharmony_ci c->scache.ways = 4; 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * According to config2 it would be 5-ways and 512-sets, 2378c2ecf20Sopenharmony_ci * but that is contradicted by all documentation. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci case MACH_INGENIC_X1000: 2408c2ecf20Sopenharmony_ci case MACH_INGENIC_X1000E: 2418c2ecf20Sopenharmony_ci c->scache.sets = 256; 2428c2ecf20Sopenharmony_ci c->scache.ways = 4; 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci c->scache.waysize = c->scache.sets * c->scache.linesz; 2488c2ecf20Sopenharmony_ci c->scache.waybit = __ffs(c->scache.waysize); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return 1; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ciint mips_sc_init(void) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci int found = mips_sc_probe(); 2588c2ecf20Sopenharmony_ci if (found) { 2598c2ecf20Sopenharmony_ci mips_sc_enable(); 2608c2ecf20Sopenharmony_ci mips_sc_prefetch_enable(); 2618c2ecf20Sopenharmony_ci bcops = &mips_sc_ops; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci return found; 2648c2ecf20Sopenharmony_ci} 265