mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-05-05 09:57:21 +02:00
Merge tag 'mips-fixes_7.0_1' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
Pull MIPS fixes from Thomas Bogendoerfer: - Fix TLB uniquification for systems with TLB not initialised by firmware - Fix allocation in TLB uniquification - Fix SiByte cache initialisation - Check uart parameters from firmware on Loongson64 systems - Fix clock id mismatch for Ralink SoCs - Fix GCC version check for __mutli3 workaround * tag 'mips-fixes_7.0_1' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux: mips: mm: Allocate tlb_vpn array atomically MIPS: mm: Rewrite TLB uniquification for the hidden bit feature MIPS: mm: Suppress TLB uniquification on EHINV hardware MIPS: Always record SEGBITS in cpu_data.vmbits MIPS: Fix the GCC version check for `__multi3' workaround MIPS: SiByte: Bring back cache initialisation mips: ralink: update CPU clock index MIPS: Loongson64: env: Check UARTs passed by LEFI cautiously
This commit is contained in:
@@ -484,7 +484,6 @@
|
||||
# endif
|
||||
# ifndef cpu_vmbits
|
||||
# define cpu_vmbits cpu_data[0].vmbits
|
||||
# define __NEED_VMBITS_PROBE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -80,9 +80,7 @@ struct cpuinfo_mips {
|
||||
int srsets; /* Shadow register sets */
|
||||
int package;/* physical package number */
|
||||
unsigned int globalnumber;
|
||||
#ifdef CONFIG_64BIT
|
||||
int vmbits; /* Virtual memory size in bits */
|
||||
#endif
|
||||
void *data; /* Additional data */
|
||||
unsigned int watch_reg_count; /* Number that exist */
|
||||
unsigned int watch_reg_use_cnt; /* Usable by ptrace */
|
||||
|
||||
@@ -1871,6 +1871,8 @@ do { \
|
||||
|
||||
#define read_c0_entryhi() __read_ulong_c0_register($10, 0)
|
||||
#define write_c0_entryhi(val) __write_ulong_c0_register($10, 0, val)
|
||||
#define read_c0_entryhi_64() __read_64bit_c0_register($10, 0)
|
||||
#define write_c0_entryhi_64(val) __write_64bit_c0_register($10, 0, val)
|
||||
|
||||
#define read_c0_guestctl1() __read_32bit_c0_register($10, 4)
|
||||
#define write_c0_guestctl1(val) __write_32bit_c0_register($10, 4, val)
|
||||
|
||||
@@ -210,11 +210,14 @@ static inline void set_elf_base_platform(const char *plat)
|
||||
|
||||
static inline void cpu_probe_vmbits(struct cpuinfo_mips *c)
|
||||
{
|
||||
#ifdef __NEED_VMBITS_PROBE
|
||||
write_c0_entryhi(0x3fffffffffffe000ULL);
|
||||
back_to_back_c0_hazard();
|
||||
c->vmbits = fls64(read_c0_entryhi() & 0x3fffffffffffe000ULL);
|
||||
#endif
|
||||
int vmbits = 31;
|
||||
|
||||
if (cpu_has_64bits) {
|
||||
write_c0_entryhi_64(0x3fffffffffffe000ULL);
|
||||
back_to_back_c0_hazard();
|
||||
vmbits = fls64(read_c0_entryhi_64() & 0x3fffffffffffe000ULL);
|
||||
}
|
||||
c->vmbits = vmbits;
|
||||
}
|
||||
|
||||
static void set_isa(struct cpuinfo_mips *c, unsigned int isa)
|
||||
|
||||
@@ -137,6 +137,8 @@ void cpu_probe(void)
|
||||
else
|
||||
cpu_set_nofpu_opts(c);
|
||||
|
||||
c->vmbits = 31;
|
||||
|
||||
reserve_exception_space(0, 0x400);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
#include "libgcc.h"
|
||||
|
||||
/*
|
||||
* GCC 7 & older can suboptimally generate __multi3 calls for mips64r6, so for
|
||||
* GCC 9 & older can suboptimally generate __multi3 calls for mips64r6, so for
|
||||
* that specific case only we implement that intrinsic here.
|
||||
*
|
||||
* See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82981
|
||||
*/
|
||||
#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ < 8)
|
||||
#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ < 10)
|
||||
|
||||
/* multiply 64-bit values, low 64-bits returned */
|
||||
static inline long long notrace dmulu(long long a, long long b)
|
||||
@@ -51,4 +51,4 @@ ti_type notrace __multi3(ti_type a, ti_type b)
|
||||
}
|
||||
EXPORT_SYMBOL(__multi3);
|
||||
|
||||
#endif /* 64BIT && CPU_MIPSR6 && GCC7 */
|
||||
#endif /* 64BIT && CPU_MIPSR6 && GCC9 */
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <asm/bootinfo.h>
|
||||
#include <loongson.h>
|
||||
@@ -106,9 +108,23 @@ static void __init lefi_fixup_fdt(struct system_loongson *system)
|
||||
|
||||
is_loongson64g = (read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64G;
|
||||
|
||||
for (i = 0; i < system->nr_uarts; i++) {
|
||||
for (i = 0; i < min(system->nr_uarts, MAX_UARTS); i++) {
|
||||
uartdev = &system->uarts[i];
|
||||
|
||||
/*
|
||||
* Some firmware does not set nr_uarts properly and passes empty
|
||||
* items. Ignore them silently.
|
||||
*/
|
||||
if (uartdev->uart_base == 0)
|
||||
continue;
|
||||
|
||||
/* Our DT only works with UPIO_MEM. */
|
||||
if (uartdev->iotype != UPIO_MEM) {
|
||||
pr_warn("Ignore UART 0x%llx with iotype %u passed by firmware\n",
|
||||
uartdev->uart_base, uartdev->iotype);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = lefi_fixup_fdt_serial(fdt_buf, uartdev->uart_base,
|
||||
uartdev->uartclk);
|
||||
/*
|
||||
|
||||
@@ -207,7 +207,8 @@ void cpu_cache_init(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_CPU_R3000) && cpu_has_3k_cache)
|
||||
r3k_cache_init();
|
||||
if (IS_ENABLED(CONFIG_CPU_R4K_CACHE_TLB) && cpu_has_4k_cache)
|
||||
if ((IS_ENABLED(CONFIG_CPU_R4K_CACHE_TLB) ||
|
||||
IS_ENABLED(CONFIG_CPU_SB1)) && cpu_has_4k_cache)
|
||||
r4k_cache_init();
|
||||
|
||||
if (IS_ENABLED(CONFIG_CPU_CAVIUM_OCTEON) && cpu_has_octeon_cache)
|
||||
|
||||
+231
-56
@@ -13,6 +13,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/export.h>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <asm/hazards.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/tlb.h>
|
||||
#include <asm/tlbdebug.h>
|
||||
#include <asm/tlbex.h>
|
||||
#include <asm/tlbmisc.h>
|
||||
#include <asm/setup.h>
|
||||
@@ -511,12 +513,229 @@ static int __init set_ntlb(char *str)
|
||||
__setup("ntlb=", set_ntlb);
|
||||
|
||||
|
||||
/* Comparison function for EntryHi VPN fields. */
|
||||
static int r4k_vpn_cmp(const void *a, const void *b)
|
||||
/* The start bit position of VPN2 and Mask in EntryHi/PageMask registers. */
|
||||
#define VPN2_SHIFT 13
|
||||
|
||||
/* Read full EntryHi even with CONFIG_32BIT. */
|
||||
static inline unsigned long long read_c0_entryhi_native(void)
|
||||
{
|
||||
long v = *(unsigned long *)a - *(unsigned long *)b;
|
||||
int s = sizeof(long) > sizeof(int) ? sizeof(long) * 8 - 1: 0;
|
||||
return s ? (v != 0) | v >> s : v;
|
||||
return cpu_has_64bits ? read_c0_entryhi_64() : read_c0_entryhi();
|
||||
}
|
||||
|
||||
/* Write full EntryHi even with CONFIG_32BIT. */
|
||||
static inline void write_c0_entryhi_native(unsigned long long v)
|
||||
{
|
||||
if (cpu_has_64bits)
|
||||
write_c0_entryhi_64(v);
|
||||
else
|
||||
write_c0_entryhi(v);
|
||||
}
|
||||
|
||||
/* TLB entry state for uniquification. */
|
||||
struct tlbent {
|
||||
unsigned long long wired:1;
|
||||
unsigned long long global:1;
|
||||
unsigned long long asid:10;
|
||||
unsigned long long vpn:51;
|
||||
unsigned long long pagesz:5;
|
||||
unsigned long long index:14;
|
||||
};
|
||||
|
||||
/*
|
||||
* Comparison function for TLB entry sorting. Place wired entries first,
|
||||
* then global entries, then order by the increasing VPN/ASID and the
|
||||
* decreasing page size. This lets us avoid clashes with wired entries
|
||||
* easily and get entries for larger pages out of the way first.
|
||||
*
|
||||
* We could group bits so as to reduce the number of comparisons, but this
|
||||
* is seldom executed and not performance-critical, so prefer legibility.
|
||||
*/
|
||||
static int r4k_entry_cmp(const void *a, const void *b)
|
||||
{
|
||||
struct tlbent ea = *(struct tlbent *)a, eb = *(struct tlbent *)b;
|
||||
|
||||
if (ea.wired > eb.wired)
|
||||
return -1;
|
||||
else if (ea.wired < eb.wired)
|
||||
return 1;
|
||||
else if (ea.global > eb.global)
|
||||
return -1;
|
||||
else if (ea.global < eb.global)
|
||||
return 1;
|
||||
else if (ea.vpn < eb.vpn)
|
||||
return -1;
|
||||
else if (ea.vpn > eb.vpn)
|
||||
return 1;
|
||||
else if (ea.asid < eb.asid)
|
||||
return -1;
|
||||
else if (ea.asid > eb.asid)
|
||||
return 1;
|
||||
else if (ea.pagesz > eb.pagesz)
|
||||
return -1;
|
||||
else if (ea.pagesz < eb.pagesz)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch all the TLB entries. Mask individual VPN values retrieved with
|
||||
* the corresponding page mask and ignoring any 1KiB extension as we'll
|
||||
* be using 4KiB pages for uniquification.
|
||||
*/
|
||||
static void __ref r4k_tlb_uniquify_read(struct tlbent *tlb_vpns, int tlbsize)
|
||||
{
|
||||
int start = num_wired_entries();
|
||||
unsigned long long vpn_mask;
|
||||
bool global;
|
||||
int i;
|
||||
|
||||
vpn_mask = GENMASK(current_cpu_data.vmbits - 1, VPN2_SHIFT);
|
||||
vpn_mask |= cpu_has_64bits ? 3ULL << 62 : 1 << 31;
|
||||
|
||||
for (i = 0; i < tlbsize; i++) {
|
||||
unsigned long long entryhi, vpn, mask, asid;
|
||||
unsigned int pagesz;
|
||||
|
||||
write_c0_index(i);
|
||||
mtc0_tlbr_hazard();
|
||||
tlb_read();
|
||||
tlb_read_hazard();
|
||||
|
||||
global = !!(read_c0_entrylo0() & ENTRYLO_G);
|
||||
entryhi = read_c0_entryhi_native();
|
||||
mask = read_c0_pagemask();
|
||||
|
||||
asid = entryhi & cpu_asid_mask(¤t_cpu_data);
|
||||
vpn = (entryhi & vpn_mask & ~mask) >> VPN2_SHIFT;
|
||||
pagesz = ilog2((mask >> VPN2_SHIFT) + 1);
|
||||
|
||||
tlb_vpns[i].global = global;
|
||||
tlb_vpns[i].asid = global ? 0 : asid;
|
||||
tlb_vpns[i].vpn = vpn;
|
||||
tlb_vpns[i].pagesz = pagesz;
|
||||
tlb_vpns[i].wired = i < start;
|
||||
tlb_vpns[i].index = i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write unique values to all but the wired TLB entries each, using
|
||||
* the 4KiB page size. This size might not be supported with R6, but
|
||||
* EHINV is mandatory for R6, so we won't ever be called in that case.
|
||||
*
|
||||
* A sorted table is supplied with any wired entries at the beginning,
|
||||
* followed by any global entries, and then finally regular entries.
|
||||
* We start at the VPN and ASID values of zero and only assign user
|
||||
* addresses, therefore guaranteeing no clash with addresses produced
|
||||
* by UNIQUE_ENTRYHI. We avoid any VPN values used by wired or global
|
||||
* entries, by increasing the VPN value beyond the span of such entry.
|
||||
*
|
||||
* When a VPN/ASID clash is found with a regular entry we increment the
|
||||
* ASID instead until no VPN/ASID clash has been found or the ASID space
|
||||
* has been exhausted, in which case we increase the VPN value beyond
|
||||
* the span of the largest clashing entry.
|
||||
*
|
||||
* We do not need to be concerned about FTLB or MMID configurations as
|
||||
* those are required to implement the EHINV feature.
|
||||
*/
|
||||
static void __ref r4k_tlb_uniquify_write(struct tlbent *tlb_vpns, int tlbsize)
|
||||
{
|
||||
unsigned long long asid, vpn, vpn_size, pagesz;
|
||||
int widx, gidx, idx, sidx, lidx, i;
|
||||
|
||||
vpn_size = 1ULL << (current_cpu_data.vmbits - VPN2_SHIFT);
|
||||
pagesz = ilog2((PM_4K >> VPN2_SHIFT) + 1);
|
||||
|
||||
write_c0_pagemask(PM_4K);
|
||||
write_c0_entrylo0(0);
|
||||
write_c0_entrylo1(0);
|
||||
|
||||
asid = 0;
|
||||
vpn = 0;
|
||||
widx = 0;
|
||||
gidx = 0;
|
||||
for (sidx = 0; sidx < tlbsize && tlb_vpns[sidx].wired; sidx++)
|
||||
;
|
||||
for (lidx = sidx; lidx < tlbsize && tlb_vpns[lidx].global; lidx++)
|
||||
;
|
||||
idx = gidx = sidx + 1;
|
||||
for (i = sidx; i < tlbsize; i++) {
|
||||
unsigned long long entryhi, vpn_pagesz = 0;
|
||||
|
||||
while (1) {
|
||||
if (WARN_ON(vpn >= vpn_size)) {
|
||||
dump_tlb_all();
|
||||
/* Pray local_flush_tlb_all() will cope. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* VPN must be below the next wired entry. */
|
||||
if (widx < sidx && vpn >= tlb_vpns[widx].vpn) {
|
||||
vpn = max(vpn,
|
||||
(tlb_vpns[widx].vpn +
|
||||
(1ULL << tlb_vpns[widx].pagesz)));
|
||||
asid = 0;
|
||||
widx++;
|
||||
continue;
|
||||
}
|
||||
/* VPN must be below the next global entry. */
|
||||
if (gidx < lidx && vpn >= tlb_vpns[gidx].vpn) {
|
||||
vpn = max(vpn,
|
||||
(tlb_vpns[gidx].vpn +
|
||||
(1ULL << tlb_vpns[gidx].pagesz)));
|
||||
asid = 0;
|
||||
gidx++;
|
||||
continue;
|
||||
}
|
||||
/* Try to find a free ASID so as to conserve VPNs. */
|
||||
if (idx < tlbsize && vpn == tlb_vpns[idx].vpn &&
|
||||
asid == tlb_vpns[idx].asid) {
|
||||
unsigned long long idx_pagesz;
|
||||
|
||||
idx_pagesz = tlb_vpns[idx].pagesz;
|
||||
vpn_pagesz = max(vpn_pagesz, idx_pagesz);
|
||||
do
|
||||
idx++;
|
||||
while (idx < tlbsize &&
|
||||
vpn == tlb_vpns[idx].vpn &&
|
||||
asid == tlb_vpns[idx].asid);
|
||||
asid++;
|
||||
if (asid > cpu_asid_mask(¤t_cpu_data)) {
|
||||
vpn += vpn_pagesz;
|
||||
asid = 0;
|
||||
vpn_pagesz = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* VPN mustn't be above the next regular entry. */
|
||||
if (idx < tlbsize && vpn > tlb_vpns[idx].vpn) {
|
||||
vpn = max(vpn,
|
||||
(tlb_vpns[idx].vpn +
|
||||
(1ULL << tlb_vpns[idx].pagesz)));
|
||||
asid = 0;
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
entryhi = (vpn << VPN2_SHIFT) | asid;
|
||||
write_c0_entryhi_native(entryhi);
|
||||
write_c0_index(tlb_vpns[i].index);
|
||||
mtc0_tlbw_hazard();
|
||||
tlb_write_indexed();
|
||||
|
||||
tlb_vpns[i].asid = asid;
|
||||
tlb_vpns[i].vpn = vpn;
|
||||
tlb_vpns[i].pagesz = pagesz;
|
||||
|
||||
asid++;
|
||||
if (asid > cpu_asid_mask(¤t_cpu_data)) {
|
||||
vpn += 1ULL << pagesz;
|
||||
asid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -527,70 +746,25 @@ static void __ref r4k_tlb_uniquify(void)
|
||||
{
|
||||
int tlbsize = current_cpu_data.tlbsize;
|
||||
bool use_slab = slab_is_available();
|
||||
int start = num_wired_entries();
|
||||
phys_addr_t tlb_vpn_size;
|
||||
unsigned long *tlb_vpns;
|
||||
unsigned long vpn_mask;
|
||||
int cnt, ent, idx, i;
|
||||
|
||||
vpn_mask = GENMASK(cpu_vmbits - 1, 13);
|
||||
vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31;
|
||||
struct tlbent *tlb_vpns;
|
||||
|
||||
tlb_vpn_size = tlbsize * sizeof(*tlb_vpns);
|
||||
tlb_vpns = (use_slab ?
|
||||
kmalloc(tlb_vpn_size, GFP_KERNEL) :
|
||||
kmalloc(tlb_vpn_size, GFP_ATOMIC) :
|
||||
memblock_alloc_raw(tlb_vpn_size, sizeof(*tlb_vpns)));
|
||||
if (WARN_ON(!tlb_vpns))
|
||||
return; /* Pray local_flush_tlb_all() is good enough. */
|
||||
|
||||
htw_stop();
|
||||
|
||||
for (i = start, cnt = 0; i < tlbsize; i++, cnt++) {
|
||||
unsigned long vpn;
|
||||
r4k_tlb_uniquify_read(tlb_vpns, tlbsize);
|
||||
|
||||
write_c0_index(i);
|
||||
mtc0_tlbr_hazard();
|
||||
tlb_read();
|
||||
tlb_read_hazard();
|
||||
vpn = read_c0_entryhi();
|
||||
vpn &= vpn_mask & PAGE_MASK;
|
||||
tlb_vpns[cnt] = vpn;
|
||||
sort(tlb_vpns, tlbsize, sizeof(*tlb_vpns), r4k_entry_cmp, NULL);
|
||||
|
||||
/* Prevent any large pages from overlapping regular ones. */
|
||||
write_c0_pagemask(read_c0_pagemask() & PM_DEFAULT_MASK);
|
||||
mtc0_tlbw_hazard();
|
||||
tlb_write_indexed();
|
||||
tlbw_use_hazard();
|
||||
}
|
||||
|
||||
sort(tlb_vpns, cnt, sizeof(tlb_vpns[0]), r4k_vpn_cmp, NULL);
|
||||
r4k_tlb_uniquify_write(tlb_vpns, tlbsize);
|
||||
|
||||
write_c0_pagemask(PM_DEFAULT_MASK);
|
||||
write_c0_entrylo0(0);
|
||||
write_c0_entrylo1(0);
|
||||
|
||||
idx = 0;
|
||||
ent = tlbsize;
|
||||
for (i = start; i < tlbsize; i++)
|
||||
while (1) {
|
||||
unsigned long entryhi, vpn;
|
||||
|
||||
entryhi = UNIQUE_ENTRYHI(ent);
|
||||
vpn = entryhi & vpn_mask & PAGE_MASK;
|
||||
|
||||
if (idx >= cnt || vpn < tlb_vpns[idx]) {
|
||||
write_c0_entryhi(entryhi);
|
||||
write_c0_index(i);
|
||||
mtc0_tlbw_hazard();
|
||||
tlb_write_indexed();
|
||||
ent++;
|
||||
break;
|
||||
} else if (vpn == tlb_vpns[idx]) {
|
||||
ent++;
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
tlbw_use_hazard();
|
||||
htw_start();
|
||||
@@ -640,7 +814,8 @@ static void r4k_tlb_configure(void)
|
||||
temp_tlb_entry = current_cpu_data.tlbsize - 1;
|
||||
|
||||
/* From this point on the ARC firmware is dead. */
|
||||
r4k_tlb_uniquify();
|
||||
if (!cpu_has_tlbinv)
|
||||
r4k_tlb_uniquify();
|
||||
local_flush_tlb_all();
|
||||
|
||||
/* Did I tell you that ARC SUCKS? */
|
||||
|
||||
@@ -21,16 +21,16 @@ static const char *clk_cpu(int *idx)
|
||||
{
|
||||
switch (ralink_soc) {
|
||||
case RT2880_SOC:
|
||||
*idx = 0;
|
||||
*idx = 1;
|
||||
return "ralink,rt2880-sysc";
|
||||
case RT3883_SOC:
|
||||
*idx = 0;
|
||||
*idx = 1;
|
||||
return "ralink,rt3883-sysc";
|
||||
case RT305X_SOC_RT3050:
|
||||
*idx = 0;
|
||||
*idx = 1;
|
||||
return "ralink,rt3050-sysc";
|
||||
case RT305X_SOC_RT3052:
|
||||
*idx = 0;
|
||||
*idx = 1;
|
||||
return "ralink,rt3052-sysc";
|
||||
case RT305X_SOC_RT3350:
|
||||
*idx = 1;
|
||||
|
||||
Reference in New Issue
Block a user