static cycle_t arc_counter_read(struct clocksource *cs) { unsigned long flags; union { #ifdef CONFIG_CPU_BIG_ENDIAN struct { u32 h, l; }; #else struct { u32 l, h; }; #endif cycle_t full; } stamp; local_irq_save(flags); __mcip_cmd(CMD_GRTC_READ_LO, 0); stamp.l = read_aux_reg(ARC_REG_MCIP_READBACK); __mcip_cmd(CMD_GRTC_READ_HI, 0); stamp.h = read_aux_reg(ARC_REG_MCIP_READBACK); local_irq_restore(flags); return stamp.full; }
void tlb_paranoid_check(unsigned int mm_asid, unsigned long addr) { unsigned int mmu_asid; mmu_asid = read_aux_reg(ARC_REG_PID) & 0xff; /* * At the time of a TLB miss/installation * - HW version needs to match SW version * - SW needs to have a valid ASID */ if (addr < 0x70000000 && ((mm_asid == MM_CTXT_NO_ASID) || (mmu_asid != (mm_asid & MM_CTXT_ASID_MASK)))) print_asid_mismatch(mm_asid, mmu_asid, 0); }
int __init get_hw_config_num_irq(void) { uint32_t val = read_aux_reg(ARC_REG_VECBASE_BCR); switch (val & 0x03) { case 0: return 16; case 1: return 32; case 2: return 8; default: return 0; } return 0; }
static unsigned int __before_dc_op(const int op) { unsigned int reg = reg; if (op == OP_FLUSH_N_INV) { /* Dcache provides 2 cmd: FLUSH or INV * INV inturn has sub-modes: DISCARD or FLUSH-BEFORE * flush-n-inv is achieved by INV cmd but with IM=1 * So toggle INV sub-mode depending on op request and default */ reg = read_aux_reg(ARC_REG_DC_CTRL); write_aux_reg(ARC_REG_DC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH) ; } return reg; }
/* Read the Cache Build Confuration Registers, Decode them and save into * the cpuinfo structure for later use. * No Validation is done here, simply read/convert the BCRs */ void read_decode_mmu_bcr(void) { struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; unsigned int tmp; struct bcr_mmu_1_2 { #ifdef CONFIG_CPU_BIG_ENDIAN unsigned int ver:8, ways:4, sets:4, u_itlb:8, u_dtlb:8; #else unsigned int u_dtlb:8, u_itlb:8, sets:4, ways:4, ver:8; #endif } *mmu2; struct bcr_mmu_3 { #ifdef CONFIG_CPU_BIG_ENDIAN unsigned int ver:8, ways:4, sets:4, osm:1, reserv:3, pg_sz:4, u_itlb:4, u_dtlb:4; #else unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, reserv:3, osm:1, sets:4, ways:4, ver:8; #endif } *mmu3; tmp = read_aux_reg(ARC_REG_MMU_BCR); mmu->ver = (tmp >> 24); if (mmu->ver <= 2) { mmu2 = (struct bcr_mmu_1_2 *)&tmp; mmu->pg_sz = PAGE_SIZE; mmu->sets = 1 << mmu2->sets; mmu->ways = 1 << mmu2->ways; mmu->u_dtlb = mmu2->u_dtlb; mmu->u_itlb = mmu2->u_itlb; } else { mmu3 = (struct bcr_mmu_3 *)&tmp; mmu->pg_sz = 512 << mmu3->pg_sz; mmu->sets = 1 << mmu3->sets; mmu->ways = 1 << mmu3->ways; mmu->u_dtlb = mmu3->u_dtlb; mmu->u_itlb = mmu3->u_itlb; } mmu->num_tlb = mmu->sets * mmu->ways; }
static void mcip_ipi_send(int cpu) { unsigned long flags; int ipi_was_pending; /* ARConnect can only send IPI to others */ if (unlikely(cpu == raw_smp_processor_id())) { arc_softirq_trigger(SOFTIRQ_IRQ); return; } /* * NOTE: We must spin here if the other cpu hasn't yet * serviced a previous message. This can burn lots * of time, but we MUST follows this protocol or * ipi messages can be lost!!! * Also, we must release the lock in this loop because * the other side may get to this same loop and not * be able to ack -- thus causing deadlock. */ do { raw_spin_lock_irqsave(&mcip_lock, flags); __mcip_cmd(CMD_INTRPT_READ_STATUS, cpu); ipi_was_pending = read_aux_reg(ARC_REG_MCIP_READBACK); if (ipi_was_pending == 0) break; /* break out but keep lock */ raw_spin_unlock_irqrestore(&mcip_lock, flags); } while (1); __mcip_cmd(CMD_INTRPT_GENERATE_IRQ, cpu); raw_spin_unlock_irqrestore(&mcip_lock, flags); #ifdef CONFIG_ARC_IPI_DBG if (ipi_was_pending) pr_info("IPI ACK delayed from cpu %d\n", cpu); #endif }
static void mcip_ipi_clear(int irq) { unsigned int cpu, c; unsigned long flags; unsigned int __maybe_unused copy; if (unlikely(irq == SOFTIRQ_IRQ)) { arc_softirq_clear(irq); return; } raw_spin_lock_irqsave(&mcip_lock, flags); /* Who sent the IPI */ __mcip_cmd(CMD_INTRPT_CHECK_SOURCE, 0); copy = cpu = read_aux_reg(ARC_REG_MCIP_READBACK); /* 1,2,4,8... */ /* * In rare case, multiple concurrent IPIs sent to same target can * possibly be coalesced by MCIP into 1 asserted IRQ, so @cpus can be * "vectored" (multiple bits sets) as opposed to typical single bit */ do { c = __ffs(cpu); /* 0,1,2,3 */ __mcip_cmd(CMD_INTRPT_GENERATE_ACK, c); cpu &= ~(1U << c); } while (cpu); raw_spin_unlock_irqrestore(&mcip_lock, flags); #ifdef CONFIG_ARC_IPI_DBG if (c != __ffs(copy)) pr_info("IPIs from %x coalesced to %x\n", copy, raw_smp_processor_id()); #endif }
const char *decode_identity(void) { int arcver = read_aux_reg(ARC_AUX_IDENTITY) & 0xff; switch (arcver) { /* ARCompact cores */ case 0x32: return "ARC 700 v4.4-4.5"; case 0x33: return "ARC 700 v4.6-v4.9"; case 0x34: return "ARC 700 v4.10"; case 0x35: return "ARC 700 v4.11"; /* ARCv2 cores */ case 0x41: return "ARC EM v1.1a"; case 0x42: return "ARC EM v3.0"; case 0x43: return "ARC EM v4.0"; case 0x50: return "ARC HS v1.0"; case 0x51: return "ARC EM v2.0"; case 0x52: return "ARC EM v2.1"; case 0x53: return "ARC HS v3.0"; case 0x54: return "ARC HS v4.0"; default: return "Unknown ARC core"; } }
void read_arc_build_cfg_regs(void) { struct bcr_perip uncached_space; struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; FIX_PTR(cpu); READ_BCR(AUX_IDENTITY, cpu->core); cpu->timers = read_aux_reg(ARC_REG_TIMERS_BCR); cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE); if (cpu->vec_base == 0) cpu->vec_base = (unsigned int)_int_vec_base_lds; READ_BCR(ARC_REG_D_UNCACH_BCR, uncached_space); cpu->uncached_base = uncached_space.start << 24; cpu->extn.mul = read_aux_reg(ARC_REG_MUL_BCR); cpu->extn.swap = read_aux_reg(ARC_REG_SWAP_BCR); cpu->extn.norm = read_aux_reg(ARC_REG_NORM_BCR); cpu->extn.minmax = read_aux_reg(ARC_REG_MIXMAX_BCR); cpu->extn.barrel = read_aux_reg(ARC_REG_BARREL_BCR); READ_BCR(ARC_REG_MAC_BCR, cpu->extn_mac_mul); cpu->extn.ext_arith = read_aux_reg(ARC_REG_EXTARITH_BCR); cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR); /* Note that we read the CCM BCRs independent of kernel config * This is to catch the cases where user doesn't know that * CCMs are present in hardware build */ { struct bcr_iccm iccm; struct bcr_dccm dccm; struct bcr_dccm_base dccm_base; unsigned int bcr_32bit_val; bcr_32bit_val = read_aux_reg(ARC_REG_ICCM_BCR); if (bcr_32bit_val) { iccm = *((struct bcr_iccm *)&bcr_32bit_val); cpu->iccm.base_addr = iccm.base << 16; cpu->iccm.sz = 0x2000 << (iccm.sz - 1); } bcr_32bit_val = read_aux_reg(ARC_REG_DCCM_BCR); if (bcr_32bit_val) { dccm = *((struct bcr_dccm *)&bcr_32bit_val); cpu->dccm.sz = 0x800 << (dccm.sz); READ_BCR(ARC_REG_DCCMBASE_BCR, dccm_base); cpu->dccm.base_addr = dccm_base.addr << 8; } } READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem); read_decode_mmu_bcr(); read_decode_cache_bcr(); READ_BCR(ARC_REG_FP_BCR, cpu->fp); READ_BCR(ARC_REG_DPFP_BCR, cpu->dpfp); }
static void read_arc_build_cfg_regs(void) { struct bcr_timer timer; struct bcr_generic bcr; struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; const struct id_to_str *tbl; FIX_PTR(cpu); READ_BCR(AUX_IDENTITY, cpu->core); READ_BCR(ARC_REG_ISA_CFG_BCR, cpu->isa); for (tbl = &arc_cpu_rel[0]; tbl->id != 0; tbl++) { if (cpu->core.family == tbl->id) { cpu->details = tbl->str; break; } } for (tbl = &arc_cpu_nm[0]; tbl->id != 0; tbl++) { if ((cpu->core.family & 0xF0) == tbl->id) break; } cpu->name = tbl->str; READ_BCR(ARC_REG_TIMERS_BCR, timer); cpu->extn.timer0 = timer.t0; cpu->extn.timer1 = timer.t1; cpu->extn.rtc = timer.rtc; cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE); READ_BCR(ARC_REG_MUL_BCR, cpu->extn_mpy); cpu->extn.norm = read_aux_reg(ARC_REG_NORM_BCR) > 1 ? 1 : 0; /* 2,3 */ cpu->extn.barrel = read_aux_reg(ARC_REG_BARREL_BCR) > 1 ? 1 : 0; /* 2,3 */ cpu->extn.swap = read_aux_reg(ARC_REG_SWAP_BCR) ? 1 : 0; /* 1,3 */ cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR) ? 1 : 0; cpu->extn.minmax = read_aux_reg(ARC_REG_MIXMAX_BCR) > 1 ? 1 : 0; /* 2 */ cpu->extn.swape = (cpu->core.family >= 0x34) ? 1 : IS_ENABLED(CONFIG_ARC_HAS_SWAPE); READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem); /* Read CCM BCRs for boot reporting even if not enabled in Kconfig */ read_decode_ccm_bcr(cpu); read_decode_mmu_bcr(); read_decode_cache_bcr(); if (is_isa_arcompact()) { struct bcr_fp_arcompact sp, dp; struct bcr_bpu_arcompact bpu; READ_BCR(ARC_REG_FP_BCR, sp); READ_BCR(ARC_REG_DPFP_BCR, dp); cpu->extn.fpu_sp = sp.ver ? 1 : 0; cpu->extn.fpu_dp = dp.ver ? 1 : 0; READ_BCR(ARC_REG_BPU_BCR, bpu); cpu->bpu.ver = bpu.ver; cpu->bpu.full = bpu.fam ? 1 : 0; if (bpu.ent) { cpu->bpu.num_cache = 256 << (bpu.ent - 1); cpu->bpu.num_pred = 256 << (bpu.ent - 1); } } else { struct bcr_fp_arcv2 spdp; struct bcr_bpu_arcv2 bpu; READ_BCR(ARC_REG_FP_V2_BCR, spdp); cpu->extn.fpu_sp = spdp.sp ? 1 : 0; cpu->extn.fpu_dp = spdp.dp ? 1 : 0; READ_BCR(ARC_REG_BPU_BCR, bpu); cpu->bpu.ver = bpu.ver; cpu->bpu.full = bpu.ft; cpu->bpu.num_cache = 256 << bpu.bce; cpu->bpu.num_pred = 2048 << bpu.pte; } READ_BCR(ARC_REG_AP_BCR, bcr); cpu->extn.ap = bcr.ver ? 1 : 0; READ_BCR(ARC_REG_SMART_BCR, bcr); cpu->extn.smart = bcr.ver ? 1 : 0; READ_BCR(ARC_REG_RTT_BCR, bcr); cpu->extn.rtt = bcr.ver ? 1 : 0; cpu->extn.debug = cpu->extn.ap | cpu->extn.smart | cpu->extn.rtt; /* some hacks for lack of feature BCR info in old ARC700 cores */ if (is_isa_arcompact()) { if (!cpu->isa.ver) /* ISA BCR absent, use Kconfig info */ cpu->isa.atomic = IS_ENABLED(CONFIG_ARC_HAS_LLSC); else cpu->isa.atomic = cpu->isa.atomic1; cpu->isa.be = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN); /* there's no direct way to distinguish 750 vs. 770 */ if (unlikely(cpu->core.family < 0x34 || cpu->mmu.ver < 3)) cpu->name = "ARC750"; } }
/* * 1. Validate the Cache Geomtery (compile time config matches hardware) * 2. If I-cache suffers from aliasing, setup work arounds (difft flush rtn) * (aliasing D-cache configurations are not supported YET) * 3. Enable the Caches, setup default flush mode for D-Cache * 3. Calculate the SHMLBA used by user space */ void arc_cache_init(void) { unsigned int cpu = smp_processor_id(); struct cpuinfo_arc_cache *ic = &cpuinfo_arc700[cpu].icache; struct cpuinfo_arc_cache *dc = &cpuinfo_arc700[cpu].dcache; unsigned int dcache_does_alias, temp; char str[256]; printk(arc_cache_mumbojumbo(0, str, sizeof(str))); if (!ic->ver) goto chk_dc; #ifdef CONFIG_ARC_HAS_ICACHE /* 1. Confirm some of I-cache params which Linux assumes */ if (ic->line_len != ARC_ICACHE_LINE_LEN) panic("Cache H/W doesn't match kernel Config"); if (ic->ver != CONFIG_ARC_MMU_VER) panic("Cache ver doesn't match MMU ver\n"); #endif /* Enable/disable I-Cache */ temp = read_aux_reg(ARC_REG_IC_CTRL); #ifdef CONFIG_ARC_HAS_ICACHE temp &= ~IC_CTRL_CACHE_DISABLE; #else temp |= IC_CTRL_CACHE_DISABLE; #endif write_aux_reg(ARC_REG_IC_CTRL, temp); chk_dc: if (!dc->ver) return; #ifdef CONFIG_ARC_HAS_DCACHE if (dc->line_len != ARC_DCACHE_LINE_LEN) panic("Cache H/W doesn't match kernel Config"); /* check for D-Cache aliasing */ dcache_does_alias = (dc->sz / dc->assoc) > PAGE_SIZE; if (dcache_does_alias && !cache_is_vipt_aliasing()) panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); else if (!dcache_does_alias && cache_is_vipt_aliasing()) panic("Don't need CONFIG_ARC_CACHE_VIPT_ALIASING\n"); #endif /* Set the default Invalidate Mode to "simpy discard dirty lines" * as this is more frequent then flush before invalidate * Ofcourse we toggle this default behviour when desired */ temp = read_aux_reg(ARC_REG_DC_CTRL); temp &= ~DC_CTRL_INV_MODE_FLUSH; #ifdef CONFIG_ARC_HAS_DCACHE /* Enable D-Cache: Clear Bit 0 */ write_aux_reg(ARC_REG_DC_CTRL, temp & ~IC_CTRL_CACHE_DISABLE); #else /* Flush D cache */ write_aux_reg(ARC_REG_DC_FLSH, 0x1); /* Disable D cache */ write_aux_reg(ARC_REG_DC_CTRL, temp | IC_CTRL_CACHE_DISABLE); #endif return; }
static cycle_t arc_counter_read(struct clocksource *cs) { return (cycle_t) read_aux_reg(ARC_REG_TIMER1_CNT); }
/* * Routine to create a TLB entry */ void create_tlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep) { unsigned long flags; unsigned int asid_or_sasid, rwx; unsigned long pd0, pd1; /* * create_tlb() assumes that current->mm == vma->mm, since * -it ASID for TLB entry is fetched from MMU ASID reg (valid for curr) * -completes the lazy write to SASID reg (again valid for curr tsk) * * Removing the assumption involves * -Using vma->mm->context{ASID,SASID}, as opposed to MMU reg. * -Fix the TLB paranoid debug code to not trigger false negatives. * -More importantly it makes this handler inconsistent with fast-path * TLB Refill handler which always deals with "current" * * Lets see the use cases when current->mm != vma->mm and we land here * 1. execve->copy_strings()->__get_user_pages->handle_mm_fault * Here VM wants to pre-install a TLB entry for user stack while * current->mm still points to pre-execve mm (hence the condition). * However the stack vaddr is soon relocated (randomization) and * move_page_tables() tries to undo that TLB entry. * Thus not creating TLB entry is not any worse. * * 2. ptrace(POKETEXT) causes a CoW - debugger(current) inserting a * breakpoint in debugged task. Not creating a TLB now is not * performance critical. * * Both the cases above are not good enough for code churn. */ if (current->active_mm != vma->vm_mm) return; local_irq_save(flags); tlb_paranoid_check(asid_mm(vma->vm_mm, smp_processor_id()), address); address &= PAGE_MASK; /* update this PTE credentials */ pte_val(*ptep) |= (_PAGE_PRESENT | _PAGE_ACCESSED); /* Create HW TLB(PD0,PD1) from PTE */ /* ASID for this task */ asid_or_sasid = read_aux_reg(ARC_REG_PID) & 0xff; pd0 = address | asid_or_sasid | (pte_val(*ptep) & PTE_BITS_IN_PD0); /* * ARC MMU provides fully orthogonal access bits for K/U mode, * however Linux only saves 1 set to save PTE real-estate * Here we convert 3 PTE bits into 6 MMU bits: * -Kernel only entries have Kr Kw Kx 0 0 0 * -User entries have mirrored K and U bits */ rwx = pte_val(*ptep) & PTE_BITS_RWX; if (pte_val(*ptep) & _PAGE_GLOBAL) rwx <<= 3; /* r w x => Kr Kw Kx 0 0 0 */ else rwx |= (rwx << 3); /* r w x => Kr Kw Kx Ur Uw Ux */ pd1 = rwx | (pte_val(*ptep) & PTE_BITS_NON_RWX_IN_PD1); tlb_entry_insert(pd0, pd1); local_irq_restore(flags); }
static void read_arc_build_cfg_regs(void) { struct bcr_perip uncached_space; struct bcr_timer timer; struct bcr_generic bcr; struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; unsigned long perip_space; FIX_PTR(cpu); READ_BCR(AUX_IDENTITY, cpu->core); READ_BCR(ARC_REG_ISA_CFG_BCR, cpu->isa); READ_BCR(ARC_REG_TIMERS_BCR, timer); cpu->extn.timer0 = timer.t0; cpu->extn.timer1 = timer.t1; cpu->extn.rtc = timer.rtc; cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE); READ_BCR(ARC_REG_D_UNCACH_BCR, uncached_space); if (uncached_space.ver < 3) perip_space = uncached_space.start << 24; else perip_space = read_aux_reg(AUX_NON_VOL) & 0xF0000000; BUG_ON(perip_space != ARC_UNCACHED_ADDR_SPACE); READ_BCR(ARC_REG_MUL_BCR, cpu->extn_mpy); cpu->extn.norm = read_aux_reg(ARC_REG_NORM_BCR) > 1 ? 1 : 0; /* 2,3 */ cpu->extn.barrel = read_aux_reg(ARC_REG_BARREL_BCR) > 1 ? 1 : 0; /* 2,3 */ cpu->extn.swap = read_aux_reg(ARC_REG_SWAP_BCR) ? 1 : 0; /* 1,3 */ cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR) ? 1 : 0; cpu->extn.minmax = read_aux_reg(ARC_REG_MIXMAX_BCR) > 1 ? 1 : 0; /* 2 */ READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem); /* Read CCM BCRs for boot reporting even if not enabled in Kconfig */ read_decode_ccm_bcr(cpu); read_decode_mmu_bcr(); read_decode_cache_bcr(); if (is_isa_arcompact()) { struct bcr_fp_arcompact sp, dp; struct bcr_bpu_arcompact bpu; READ_BCR(ARC_REG_FP_BCR, sp); READ_BCR(ARC_REG_DPFP_BCR, dp); cpu->extn.fpu_sp = sp.ver ? 1 : 0; cpu->extn.fpu_dp = dp.ver ? 1 : 0; READ_BCR(ARC_REG_BPU_BCR, bpu); cpu->bpu.ver = bpu.ver; cpu->bpu.full = bpu.fam ? 1 : 0; if (bpu.ent) { cpu->bpu.num_cache = 256 << (bpu.ent - 1); cpu->bpu.num_pred = 256 << (bpu.ent - 1); } } else { struct bcr_fp_arcv2 spdp; struct bcr_bpu_arcv2 bpu; READ_BCR(ARC_REG_FP_V2_BCR, spdp); cpu->extn.fpu_sp = spdp.sp ? 1 : 0; cpu->extn.fpu_dp = spdp.dp ? 1 : 0; READ_BCR(ARC_REG_BPU_BCR, bpu); cpu->bpu.ver = bpu.ver; cpu->bpu.full = bpu.ft; cpu->bpu.num_cache = 256 << bpu.bce; cpu->bpu.num_pred = 2048 << bpu.pte; } READ_BCR(ARC_REG_AP_BCR, bcr); cpu->extn.ap = bcr.ver ? 1 : 0; READ_BCR(ARC_REG_SMART_BCR, bcr); cpu->extn.smart = bcr.ver ? 1 : 0; READ_BCR(ARC_REG_RTT_BCR, bcr); cpu->extn.rtt = bcr.ver ? 1 : 0; cpu->extn.debug = cpu->extn.ap | cpu->extn.smart | cpu->extn.rtt; }
static void read_arc_build_cfg_regs(void) { struct bcr_perip uncached_space; struct bcr_generic bcr; struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; unsigned long perip_space; FIX_PTR(cpu); READ_BCR(AUX_IDENTITY, cpu->core); READ_BCR(ARC_REG_ISA_CFG_BCR, cpu->isa); READ_BCR(ARC_REG_TIMERS_BCR, cpu->timers); cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE); READ_BCR(ARC_REG_D_UNCACH_BCR, uncached_space); if (uncached_space.ver < 3) perip_space = uncached_space.start << 24; else perip_space = read_aux_reg(AUX_NON_VOL) & 0xF0000000; BUG_ON(perip_space != ARC_UNCACHED_ADDR_SPACE); READ_BCR(ARC_REG_MUL_BCR, cpu->extn_mpy); cpu->extn.norm = read_aux_reg(ARC_REG_NORM_BCR) > 1 ? 1 : 0; /* 2,3 */ cpu->extn.barrel = read_aux_reg(ARC_REG_BARREL_BCR) > 1 ? 1 : 0; /* 2,3 */ cpu->extn.swap = read_aux_reg(ARC_REG_SWAP_BCR) ? 1 : 0; /* 1,3 */ cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR) ? 1 : 0; cpu->extn.minmax = read_aux_reg(ARC_REG_MIXMAX_BCR) > 1 ? 1 : 0; /* 2 */ /* Note that we read the CCM BCRs independent of kernel config * This is to catch the cases where user doesn't know that * CCMs are present in hardware build */ { struct bcr_iccm iccm; struct bcr_dccm dccm; struct bcr_dccm_base dccm_base; unsigned int bcr_32bit_val; bcr_32bit_val = read_aux_reg(ARC_REG_ICCM_BCR); if (bcr_32bit_val) { iccm = *((struct bcr_iccm *)&bcr_32bit_val); cpu->iccm.base_addr = iccm.base << 16; cpu->iccm.sz = 0x2000 << (iccm.sz - 1); } bcr_32bit_val = read_aux_reg(ARC_REG_DCCM_BCR); if (bcr_32bit_val) { dccm = *((struct bcr_dccm *)&bcr_32bit_val); cpu->dccm.sz = 0x800 << (dccm.sz); READ_BCR(ARC_REG_DCCMBASE_BCR, dccm_base); cpu->dccm.base_addr = dccm_base.addr << 8; } } READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem); read_decode_mmu_bcr(); read_decode_cache_bcr(); if (is_isa_arcompact()) { struct bcr_fp_arcompact sp, dp; struct bcr_bpu_arcompact bpu; READ_BCR(ARC_REG_FP_BCR, sp); READ_BCR(ARC_REG_DPFP_BCR, dp); cpu->extn.fpu_sp = sp.ver ? 1 : 0; cpu->extn.fpu_dp = dp.ver ? 1 : 0; READ_BCR(ARC_REG_BPU_BCR, bpu); cpu->bpu.ver = bpu.ver; cpu->bpu.full = bpu.fam ? 1 : 0; if (bpu.ent) { cpu->bpu.num_cache = 256 << (bpu.ent - 1); cpu->bpu.num_pred = 256 << (bpu.ent - 1); } } else { struct bcr_fp_arcv2 spdp; struct bcr_bpu_arcv2 bpu; READ_BCR(ARC_REG_FP_V2_BCR, spdp); cpu->extn.fpu_sp = spdp.sp ? 1 : 0; cpu->extn.fpu_dp = spdp.dp ? 1 : 0; READ_BCR(ARC_REG_BPU_BCR, bpu); cpu->bpu.ver = bpu.ver; cpu->bpu.full = bpu.ft; cpu->bpu.num_cache = 256 << bpu.bce; cpu->bpu.num_pred = 2048 << bpu.pte; } READ_BCR(ARC_REG_AP_BCR, bcr); cpu->extn.ap = bcr.ver ? 1 : 0; READ_BCR(ARC_REG_SMART_BCR, bcr); cpu->extn.smart = bcr.ver ? 1 : 0; READ_BCR(ARC_REG_RTT_BCR, bcr); cpu->extn.rtt = bcr.ver ? 1 : 0; cpu->extn.debug = cpu->extn.ap | cpu->extn.smart | cpu->extn.rtt; }
static inline void wait_for_flush(void) { while (read_aux_reg(ARC_REG_DC_CTRL) & DC_CTRL_FLUSH_STATUS) ; }
void do_tlb_overlap_fault(unsigned long cause, unsigned long address, struct pt_regs *regs) { int set, way, n; unsigned long flags, is_valid; struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; unsigned int pd0[mmu->ways], pd1[mmu->ways]; local_irq_save(flags); /* re-enable the MMU */ write_aux_reg(ARC_REG_PID, MMU_ENABLE | read_aux_reg(ARC_REG_PID)); /* loop thru all sets of TLB */ for (set = 0; set < mmu->sets; set++) { /* read out all the ways of current set */ for (way = 0, is_valid = 0; way < mmu->ways; way++) { write_aux_reg(ARC_REG_TLBINDEX, SET_WAY_TO_IDX(mmu, set, way)); write_aux_reg(ARC_REG_TLBCOMMAND, TLBRead); pd0[way] = read_aux_reg(ARC_REG_TLBPD0); pd1[way] = read_aux_reg(ARC_REG_TLBPD1); is_valid |= pd0[way] & _PAGE_PRESENT; } /* If all the WAYS in SET are empty, skip to next SET */ if (!is_valid) continue; /* Scan the set for duplicate ways: needs a nested loop */ for (way = 0; way < mmu->ways - 1; way++) { if (!pd0[way]) continue; for (n = way + 1; n < mmu->ways; n++) { if ((pd0[way] & PAGE_MASK) == (pd0[n] & PAGE_MASK)) { if (dup_pd_verbose) { pr_info("Duplicate PD's @" "[%d:%d]/[%d:%d]\n", set, way, set, n); pr_info("TLBPD0[%u]: %08x\n", way, pd0[way]); } /* * clear entry @way and not @n. This is * critical to our optimised loop */ pd0[way] = pd1[way] = 0; write_aux_reg(ARC_REG_TLBINDEX, SET_WAY_TO_IDX(mmu, set, way)); __tlb_entry_erase(); } } } } local_irq_restore(flags); }
static char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len) { struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id]; struct bcr_identity *core = &cpu->core; int i, n = 0, ua = 0; FIX_PTR(cpu); n += scnprintf(buf + n, len - n, "\nIDENTITY\t: ARCVER [%#02x] ARCNUM [%#02x] CHIPID [%#4x]\n", core->family, core->cpu_id, core->chip_id); n += scnprintf(buf + n, len - n, "processor [%d]\t: %s %s (%s ISA) %s%s%s\n", cpu_id, cpu->name, cpu->details, is_isa_arcompact() ? "ARCompact" : "ARCv2", IS_AVAIL1(cpu->isa.be, "[Big-Endian]"), IS_AVAIL3(cpu->extn.dual, cpu->extn.dual_enb, " Dual-Issue ")); n += scnprintf(buf + n, len - n, "Timers\t\t: %s%s%s%s%s%s\nISA Extn\t: ", IS_AVAIL1(cpu->extn.timer0, "Timer0 "), IS_AVAIL1(cpu->extn.timer1, "Timer1 "), IS_AVAIL2(cpu->extn.rtc, "RTC [UP 64-bit] ", CONFIG_ARC_TIMERS_64BIT), IS_AVAIL2(cpu->extn.gfrc, "GFRC [SMP 64-bit] ", CONFIG_ARC_TIMERS_64BIT)); #ifdef __ARC_UNALIGNED__ ua = 1; #endif n += i = scnprintf(buf + n, len - n, "%s%s%s%s%s%s", IS_AVAIL2(cpu->isa.atomic, "atomic ", CONFIG_ARC_HAS_LLSC), IS_AVAIL2(cpu->isa.ldd, "ll64 ", CONFIG_ARC_HAS_LL64), IS_AVAIL1(cpu->isa.unalign, "unalign "), IS_USED_RUN(ua)); if (i) n += scnprintf(buf + n, len - n, "\n\t\t: "); if (cpu->extn_mpy.ver) { if (cpu->extn_mpy.ver <= 0x2) { /* ARCompact */ n += scnprintf(buf + n, len - n, "mpy "); } else { int opt = 2; /* stock MPY/MPYH */ if (cpu->extn_mpy.dsp) /* OPT 7-9 */ opt = cpu->extn_mpy.dsp + 6; n += scnprintf(buf + n, len - n, "mpy[opt %d] ", opt); } } n += scnprintf(buf + n, len - n, "%s%s%s%s%s%s%s%s\n", IS_AVAIL1(cpu->isa.div_rem, "div_rem "), IS_AVAIL1(cpu->extn.norm, "norm "), IS_AVAIL1(cpu->extn.barrel, "barrel-shift "), IS_AVAIL1(cpu->extn.swap, "swap "), IS_AVAIL1(cpu->extn.minmax, "minmax "), IS_AVAIL1(cpu->extn.crc, "crc "), IS_AVAIL2(cpu->extn.swape, "swape", CONFIG_ARC_HAS_SWAPE)); if (cpu->bpu.ver) n += scnprintf(buf + n, len - n, "BPU\t\t: %s%s match, cache:%d, Predict Table:%d", IS_AVAIL1(cpu->bpu.full, "full"), IS_AVAIL1(!cpu->bpu.full, "partial"), cpu->bpu.num_cache, cpu->bpu.num_pred); if (is_isa_arcv2()) { struct bcr_lpb lpb; READ_BCR(ARC_REG_LPB_BUILD, lpb); if (lpb.ver) { unsigned int ctl; ctl = read_aux_reg(ARC_REG_LPB_CTRL); n += scnprintf(buf + n, len - n, " Loop Buffer:%d %s", lpb.entries, IS_DISABLED_RUN(!ctl)); } } n += scnprintf(buf + n, len - n, "\n"); return buf; }
static inline void __ic_entire_inv(void) { write_aux_reg(ARC_REG_IC_IVIC, 1); read_aux_reg(ARC_REG_IC_CTRL); /* blocks */ }
static void read_arc_build_cfg_regs(void) { struct bcr_timer timer; struct bcr_generic bcr; struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; const struct id_to_str *tbl; struct bcr_isa_arcv2 isa; struct bcr_actionpoint ap; FIX_PTR(cpu); READ_BCR(AUX_IDENTITY, cpu->core); for (tbl = &arc_cpu_rel[0]; tbl->id != 0; tbl++) { if (cpu->core.family == tbl->id) { cpu->details = tbl->str; break; } } for (tbl = &arc_cpu_nm[0]; tbl->id != 0; tbl++) { if ((cpu->core.family & 0xF4) == tbl->id) break; } cpu->name = tbl->str; READ_BCR(ARC_REG_TIMERS_BCR, timer); cpu->extn.timer0 = timer.t0; cpu->extn.timer1 = timer.t1; cpu->extn.rtc = timer.rtc; cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE); READ_BCR(ARC_REG_MUL_BCR, cpu->extn_mpy); cpu->extn.norm = read_aux_reg(ARC_REG_NORM_BCR) > 1 ? 1 : 0; /* 2,3 */ cpu->extn.barrel = read_aux_reg(ARC_REG_BARREL_BCR) > 1 ? 1 : 0; /* 2,3 */ cpu->extn.swap = read_aux_reg(ARC_REG_SWAP_BCR) ? 1 : 0; /* 1,3 */ cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR) ? 1 : 0; cpu->extn.minmax = read_aux_reg(ARC_REG_MIXMAX_BCR) > 1 ? 1 : 0; /* 2 */ cpu->extn.swape = (cpu->core.family >= 0x34) ? 1 : IS_ENABLED(CONFIG_ARC_HAS_SWAPE); READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem); /* Read CCM BCRs for boot reporting even if not enabled in Kconfig */ read_decode_ccm_bcr(cpu); read_decode_mmu_bcr(); read_decode_cache_bcr(); if (is_isa_arcompact()) { struct bcr_fp_arcompact sp, dp; struct bcr_bpu_arcompact bpu; READ_BCR(ARC_REG_FP_BCR, sp); READ_BCR(ARC_REG_DPFP_BCR, dp); cpu->extn.fpu_sp = sp.ver ? 1 : 0; cpu->extn.fpu_dp = dp.ver ? 1 : 0; READ_BCR(ARC_REG_BPU_BCR, bpu); cpu->bpu.ver = bpu.ver; cpu->bpu.full = bpu.fam ? 1 : 0; if (bpu.ent) { cpu->bpu.num_cache = 256 << (bpu.ent - 1); cpu->bpu.num_pred = 256 << (bpu.ent - 1); } } else { struct bcr_fp_arcv2 spdp; struct bcr_bpu_arcv2 bpu; READ_BCR(ARC_REG_FP_V2_BCR, spdp); cpu->extn.fpu_sp = spdp.sp ? 1 : 0; cpu->extn.fpu_dp = spdp.dp ? 1 : 0; READ_BCR(ARC_REG_BPU_BCR, bpu); cpu->bpu.ver = bpu.ver; cpu->bpu.full = bpu.ft; cpu->bpu.num_cache = 256 << bpu.bce; cpu->bpu.num_pred = 2048 << bpu.pte; cpu->bpu.ret_stk = 4 << bpu.rse; if (cpu->core.family >= 0x54) { struct bcr_uarch_build_arcv2 uarch; /* * The first 0x54 core (uarch maj:min 0:1 or 0:2) was * dual issue only (HS4x). But next uarch rev (1:0) * allows it be configured for single issue (HS3x) * Ensure we fiddle with dual issue only on HS4x */ READ_BCR(ARC_REG_MICRO_ARCH_BCR, uarch); if (uarch.prod == 4) { unsigned int exec_ctrl; /* dual issue hardware always present */ cpu->extn.dual = 1; READ_BCR(AUX_EXEC_CTRL, exec_ctrl); /* dual issue hardware enabled ? */ cpu->extn.dual_enb = !(exec_ctrl & 1); } } } READ_BCR(ARC_REG_AP_BCR, ap); if (ap.ver) { cpu->extn.ap_num = 2 << ap.num; cpu->extn.ap_full = !ap.min; } READ_BCR(ARC_REG_SMART_BCR, bcr); cpu->extn.smart = bcr.ver ? 1 : 0; READ_BCR(ARC_REG_RTT_BCR, bcr); cpu->extn.rtt = bcr.ver ? 1 : 0; READ_BCR(ARC_REG_ISA_CFG_BCR, isa); /* some hacks for lack of feature BCR info in old ARC700 cores */ if (is_isa_arcompact()) { if (!isa.ver) /* ISA BCR absent, use Kconfig info */ cpu->isa.atomic = IS_ENABLED(CONFIG_ARC_HAS_LLSC); else { /* ARC700_BUILD only has 2 bits of isa info */ struct bcr_generic bcr = *(struct bcr_generic *)&isa; cpu->isa.atomic = bcr.info & 1; } cpu->isa.be = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN); /* there's no direct way to distinguish 750 vs. 770 */ if (unlikely(cpu->core.family < 0x34 || cpu->mmu.ver < 3)) cpu->name = "ARC750"; } else { cpu->isa = isa; } }