linux/arch/mips/loongson64/cpucfg-emul.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3#include <linux/smp.h>
   4#include <linux/types.h>
   5#include <asm/cpu.h>
   6#include <asm/cpu-info.h>
   7#include <asm/elf.h>
   8
   9#include <loongson_regs.h>
  10#include <cpucfg-emul.h>
  11
  12static bool is_loongson(struct cpuinfo_mips *c)
  13{
  14        switch (c->processor_id & PRID_COMP_MASK) {
  15        case PRID_COMP_LEGACY:
  16                return ((c->processor_id & PRID_IMP_MASK) ==
  17                        PRID_IMP_LOONGSON_64C);
  18
  19        case PRID_COMP_LOONGSON:
  20                return true;
  21
  22        default:
  23                return false;
  24        }
  25}
  26
  27static u32 get_loongson_fprev(struct cpuinfo_mips *c)
  28{
  29        return c->fpu_id & LOONGSON_FPREV_MASK;
  30}
  31
  32static bool cpu_has_uca(void)
  33{
  34        u32 diag = read_c0_diag();
  35        u32 new_diag;
  36
  37        if (diag & LOONGSON_DIAG_UCAC)
  38                /* UCA is already enabled. */
  39                return true;
  40
  41        /* See if UCAC bit can be flipped on. This should be safe. */
  42        new_diag = diag | LOONGSON_DIAG_UCAC;
  43        write_c0_diag(new_diag);
  44        new_diag = read_c0_diag();
  45        write_c0_diag(diag);
  46
  47        return (new_diag & LOONGSON_DIAG_UCAC) != 0;
  48}
  49
  50static void probe_uca(struct cpuinfo_mips *c)
  51{
  52        if (cpu_has_uca())
  53                c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LSUCA;
  54}
  55
  56static void decode_loongson_config6(struct cpuinfo_mips *c)
  57{
  58        u32 config6 = read_c0_config6();
  59
  60        if (config6 & LOONGSON_CONF6_SFBEN)
  61                c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SFBP;
  62        if (config6 & LOONGSON_CONF6_LLEXC)
  63                c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LLEXC;
  64        if (config6 & LOONGSON_CONF6_SCRAND)
  65                c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SCRAND;
  66}
  67
  68static void patch_cpucfg_sel1(struct cpuinfo_mips *c)
  69{
  70        u64 ases = c->ases;
  71        u64 options = c->options;
  72        u32 data = c->loongson3_cpucfg_data[0];
  73
  74        if (options & MIPS_CPU_FPU) {
  75                data |= LOONGSON_CFG1_FP;
  76                data |= get_loongson_fprev(c) << LOONGSON_CFG1_FPREV_OFFSET;
  77        }
  78        if (ases & MIPS_ASE_LOONGSON_MMI)
  79                data |= LOONGSON_CFG1_MMI;
  80        if (ases & MIPS_ASE_MSA)
  81                data |= LOONGSON_CFG1_MSA1;
  82
  83        c->loongson3_cpucfg_data[0] = data;
  84}
  85
  86static void patch_cpucfg_sel2(struct cpuinfo_mips *c)
  87{
  88        u64 ases = c->ases;
  89        u64 options = c->options;
  90        u32 data = c->loongson3_cpucfg_data[1];
  91
  92        if (ases & MIPS_ASE_LOONGSON_EXT)
  93                data |= LOONGSON_CFG2_LEXT1;
  94        if (ases & MIPS_ASE_LOONGSON_EXT2)
  95                data |= LOONGSON_CFG2_LEXT2;
  96        if (options & MIPS_CPU_LDPTE)
  97                data |= LOONGSON_CFG2_LSPW;
  98
  99        if (ases & MIPS_ASE_VZ)
 100                data |= LOONGSON_CFG2_LVZP;
 101        else
 102                data &= ~LOONGSON_CFG2_LVZREV;
 103
 104        c->loongson3_cpucfg_data[1] = data;
 105}
 106
 107static void patch_cpucfg_sel3(struct cpuinfo_mips *c)
 108{
 109        u64 ases = c->ases;
 110        u32 data = c->loongson3_cpucfg_data[2];
 111
 112        if (ases & MIPS_ASE_LOONGSON_CAM) {
 113                data |= LOONGSON_CFG3_LCAMP;
 114        } else {
 115                data &= ~LOONGSON_CFG3_LCAMREV;
 116                data &= ~LOONGSON_CFG3_LCAMNUM;
 117                data &= ~LOONGSON_CFG3_LCAMKW;
 118                data &= ~LOONGSON_CFG3_LCAMVW;
 119        }
 120
 121        c->loongson3_cpucfg_data[2] = data;
 122}
 123
 124void loongson3_cpucfg_synthesize_data(struct cpuinfo_mips *c)
 125{
 126        /* Only engage the logic on Loongson processors. */
 127        if (!is_loongson(c))
 128                return;
 129
 130        /* CPUs with CPUCFG support don't need to synthesize anything. */
 131        if (cpu_has_cfg())
 132                goto have_cpucfg_now;
 133
 134        c->loongson3_cpucfg_data[0] = 0;
 135        c->loongson3_cpucfg_data[1] = 0;
 136        c->loongson3_cpucfg_data[2] = 0;
 137
 138        /* Add CPUCFG features non-discoverable otherwise. */
 139        switch (c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) {
 140        case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_0:
 141        case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_1:
 142        case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_2:
 143        case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_3:
 144                decode_loongson_config6(c);
 145                probe_uca(c);
 146
 147                c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 |
 148                        LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LLSYNC |
 149                        LOONGSON_CFG1_TGTSYNC);
 150                c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
 151                        LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LPMP |
 152                        LOONGSON_CFG2_LPM_REV2);
 153                c->loongson3_cpucfg_data[2] = 0;
 154                break;
 155
 156        case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R1:
 157                c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 |
 158                        LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA |
 159                        LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC);
 160                c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
 161                        LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1);
 162                c->loongson3_cpucfg_data[2] |= (
 163                        LOONGSON_CFG3_LCAM_REV1 |
 164                        LOONGSON_CFG3_LCAMNUM_REV1 |
 165                        LOONGSON_CFG3_LCAMKW_REV1 |
 166                        LOONGSON_CFG3_LCAMVW_REV1);
 167                break;
 168
 169        case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R1:
 170        case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R2:
 171                c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 |
 172                        LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA |
 173                        LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC);
 174                c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
 175                        LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1);
 176                c->loongson3_cpucfg_data[2] |= (
 177                        LOONGSON_CFG3_LCAM_REV1 |
 178                        LOONGSON_CFG3_LCAMNUM_REV1 |
 179                        LOONGSON_CFG3_LCAMKW_REV1 |
 180                        LOONGSON_CFG3_LCAMVW_REV1);
 181                break;
 182
 183        case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_0:
 184        case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_1:
 185        case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_0:
 186        case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_1:
 187                decode_loongson_config6(c);
 188                probe_uca(c);
 189
 190                c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_CNT64 |
 191                        LOONGSON_CFG1_LSLDR0 | LOONGSON_CFG1_LSPREF |
 192                        LOONGSON_CFG1_LSPREFX | LOONGSON_CFG1_LSSYNCI |
 193                        LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC);
 194                c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
 195                        LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LBTMMU |
 196                        LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1 |
 197                        LOONGSON_CFG2_LVZ_REV1);
 198                c->loongson3_cpucfg_data[2] |= (LOONGSON_CFG3_LCAM_REV1 |
 199                        LOONGSON_CFG3_LCAMNUM_REV1 |
 200                        LOONGSON_CFG3_LCAMKW_REV1 |
 201                        LOONGSON_CFG3_LCAMVW_REV1);
 202                break;
 203
 204        default:
 205                /* It is possible that some future Loongson cores still do
 206                 * not have CPUCFG, so do not emulate anything for these
 207                 * cores.
 208                 */
 209                return;
 210        }
 211
 212        /* This feature is set by firmware, but all known Loongson-64 systems
 213         * are configured this way.
 214         */
 215        c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_CDMAP;
 216
 217        /* Patch in dynamically probed bits. */
 218        patch_cpucfg_sel1(c);
 219        patch_cpucfg_sel2(c);
 220        patch_cpucfg_sel3(c);
 221
 222have_cpucfg_now:
 223        /* We have usable CPUCFG now, emulated or not.
 224         * Announce CPUCFG availability to userspace via hwcap.
 225         */
 226        elf_hwcap |= HWCAP_LOONGSON_CPUCFG;
 227}
 228