| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * MIPS SPRAM support |
| * |
| * Copyright (C) 2007, 2008 MIPS Technologies, Inc. |
| */ |
| #include <linux/kernel.h> |
| #include <linux/ptrace.h> |
| #include <linux/stddef.h> |
| |
| #include <asm/fpu.h> |
| #include <asm/mipsregs.h> |
| #include <asm/r4kcache.h> |
| #include <asm/hazards.h> |
| #include <asm/spram.h> |
| |
| /* |
| * These definitions are correct for the 24K/34K/74K SPRAM sample |
| * implementation. The 4KS interpreted the tags differently... |
| */ |
| #define SPRAM_TAG0_ENABLE 0x00000080 |
| #define SPRAM_TAG0_PA_MASK 0xfffff000 |
| #define SPRAM_TAG1_SIZE_MASK 0xfffff000 |
| |
| #define SPRAM_TAG_STRIDE 8 |
| |
| #define ERRCTL_SPRAM (1 << 28) |
| |
| /* errctl access */ |
| #define read_c0_errctl(x) read_c0_ecc(x) |
| #define write_c0_errctl(x) write_c0_ecc(x) |
| |
| /* |
| * Different semantics to the set_c0_* function built by __BUILD_SET_C0 |
| */ |
| static unsigned int bis_c0_errctl(unsigned int set) |
| { |
| unsigned int res; |
| res = read_c0_errctl(); |
| write_c0_errctl(res | set); |
| return res; |
| } |
| |
| static void ispram_store_tag(unsigned int offset, unsigned int data) |
| { |
| unsigned int errctl; |
| |
| /* enable SPRAM tag access */ |
| errctl = bis_c0_errctl(ERRCTL_SPRAM); |
| ehb(); |
| |
| write_c0_taglo(data); |
| ehb(); |
| |
| cache_op(Index_Store_Tag_I, CKSEG0|offset); |
| ehb(); |
| |
| write_c0_errctl(errctl); |
| ehb(); |
| } |
| |
| |
| static unsigned int ispram_load_tag(unsigned int offset) |
| { |
| unsigned int data; |
| unsigned int errctl; |
| |
| /* enable SPRAM tag access */ |
| errctl = bis_c0_errctl(ERRCTL_SPRAM); |
| ehb(); |
| cache_op(Index_Load_Tag_I, CKSEG0 | offset); |
| ehb(); |
| data = read_c0_taglo(); |
| ehb(); |
| write_c0_errctl(errctl); |
| ehb(); |
| |
| return data; |
| } |
| |
| static void dspram_store_tag(unsigned int offset, unsigned int data) |
| { |
| unsigned int errctl; |
| |
| /* enable SPRAM tag access */ |
| errctl = bis_c0_errctl(ERRCTL_SPRAM); |
| ehb(); |
| write_c0_dtaglo(data); |
| ehb(); |
| cache_op(Index_Store_Tag_D, CKSEG0 | offset); |
| ehb(); |
| write_c0_errctl(errctl); |
| ehb(); |
| } |
| |
| |
| static unsigned int dspram_load_tag(unsigned int offset) |
| { |
| unsigned int data; |
| unsigned int errctl; |
| |
| errctl = bis_c0_errctl(ERRCTL_SPRAM); |
| ehb(); |
| cache_op(Index_Load_Tag_D, CKSEG0 | offset); |
| ehb(); |
| data = read_c0_dtaglo(); |
| ehb(); |
| write_c0_errctl(errctl); |
| ehb(); |
| |
| return data; |
| } |
| |
| static void probe_spram(char *type, |
| unsigned int base, |
| unsigned int (*read)(unsigned int), |
| void (*write)(unsigned int, unsigned int)) |
| { |
| unsigned int firstsize = 0, lastsize = 0; |
| unsigned int firstpa = 0, lastpa = 0, pa = 0; |
| unsigned int offset = 0; |
| unsigned int size, tag0, tag1; |
| unsigned int enabled; |
| int i; |
| |
| /* |
| * The limit is arbitrary but avoids the loop running away if |
| * the SPRAM tags are implemented differently |
| */ |
| |
| for (i = 0; i < 8; i++) { |
| tag0 = read(offset); |
| tag1 = read(offset+SPRAM_TAG_STRIDE); |
| pr_debug("DBG %s%d: tag0=%08x tag1=%08x\n", |
| type, i, tag0, tag1); |
| |
| size = tag1 & SPRAM_TAG1_SIZE_MASK; |
| |
| if (size == 0) |
| break; |
| |
| if (i != 0) { |
| /* tags may repeat... */ |
| if ((pa == firstpa && size == firstsize) || |
| (pa == lastpa && size == lastsize)) |
| break; |
| } |
| |
| /* Align base with size */ |
| base = (base + size - 1) & ~(size-1); |
| |
| /* reprogram the base address base address and enable */ |
| tag0 = (base & SPRAM_TAG0_PA_MASK) | SPRAM_TAG0_ENABLE; |
| write(offset, tag0); |
| |
| base += size; |
| |
| /* reread the tag */ |
| tag0 = read(offset); |
| pa = tag0 & SPRAM_TAG0_PA_MASK; |
| enabled = tag0 & SPRAM_TAG0_ENABLE; |
| |
| if (i == 0) { |
| firstpa = pa; |
| firstsize = size; |
| } |
| |
| lastpa = pa; |
| lastsize = size; |
| |
| if (strcmp(type, "DSPRAM") == 0) { |
| unsigned int *vp = (unsigned int *)(CKSEG1 | pa); |
| unsigned int v; |
| #define TDAT 0x5a5aa5a5 |
| vp[0] = TDAT; |
| vp[1] = ~TDAT; |
| |
| mb(); |
| |
| v = vp[0]; |
| if (v != TDAT) |
| printk(KERN_ERR "vp=%p wrote=%08x got=%08x\n", |
| vp, TDAT, v); |
| v = vp[1]; |
| if (v != ~TDAT) |
| printk(KERN_ERR "vp=%p wrote=%08x got=%08x\n", |
| vp+1, ~TDAT, v); |
| } |
| |
| pr_info("%s%d: PA=%08x,Size=%08x%s\n", |
| type, i, pa, size, enabled ? ",enabled" : ""); |
| offset += 2 * SPRAM_TAG_STRIDE; |
| } |
| } |
| void spram_config(void) |
| { |
| unsigned int config0; |
| |
| switch (current_cpu_type()) { |
| case CPU_24K: |
| case CPU_34K: |
| case CPU_74K: |
| case CPU_1004K: |
| case CPU_1074K: |
| case CPU_INTERAPTIV: |
| case CPU_PROAPTIV: |
| case CPU_P5600: |
| case CPU_QEMU_GENERIC: |
| case CPU_I6400: |
| case CPU_P6600: |
| config0 = read_c0_config(); |
| /* FIXME: addresses are Malta specific */ |
| if (config0 & MIPS_CONF_ISP) { |
| probe_spram("ISPRAM", 0x1c000000, |
| &ispram_load_tag, &ispram_store_tag); |
| } |
| if (config0 & MIPS_CONF_DSP) |
| probe_spram("DSPRAM", 0x1c100000, |
| &dspram_load_tag, &dspram_store_tag); |
| } |
| } |