Path: blob/main/sys/powerpc/mpc85xx/platform_mpc85xx.c
39507 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2008-2012 Semihalf.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* 1. Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR17* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES18* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.19* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,20* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT21* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,22* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY23* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF25* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.26*/2728#include "opt_platform.h"2930#include <sys/param.h>31#include <sys/systm.h>32#include <sys/kernel.h>33#include <sys/module.h>34#include <sys/bus.h>35#include <sys/pcpu.h>36#include <sys/proc.h>37#include <sys/smp.h>3839#include <machine/bus.h>40#include <machine/cpu.h>41#include <machine/hid.h>42#include <machine/_inttypes.h>43#include <machine/machdep.h>44#include <machine/md_var.h>45#include <machine/platform.h>46#include <machine/platformvar.h>47#include <machine/smp.h>48#include <machine/spr.h>49#include <machine/vmparam.h>5051#include <dev/fdt/fdt_common.h>52#include <dev/ofw/ofw_bus.h>53#include <dev/ofw/ofw_bus_subr.h>54#include <dev/ofw/openfirm.h>5556#include <vm/vm.h>57#include <vm/pmap.h>58#include <vm/vm_extern.h>5960#include <powerpc/mpc85xx/mpc85xx.h>6162#include "platform_if.h"6364#ifdef SMP65extern void *ap_pcpu;66extern vm_paddr_t kernload; /* Kernel physical load address */67extern uint8_t __boot_page[]; /* Boot page body */68extern vm_paddr_t bp_kernload; /* Boot page copy of kernload */69extern vm_offset_t bp_virtaddr; /* Virtual address of boot page */70extern vm_offset_t __startkernel;7172struct cpu_release {73uint32_t entry_h;74uint32_t entry_l;75uint32_t r3_h;76uint32_t r3_l;77uint32_t reserved;78uint32_t pir;79};80#endif8182extern uint32_t *bootinfo;83vm_paddr_t ccsrbar_pa;84vm_offset_t ccsrbar_va;85vm_size_t ccsrbar_size;8687static int cpu, maxcpu;8889static device_t rcpm_dev;90static void dummy_freeze(device_t, bool);9192static void (*freeze_timebase)(device_t, bool) = dummy_freeze;9394static int mpc85xx_probe(platform_t);95static void mpc85xx_mem_regions(platform_t, struct mem_region *phys,96int *physsz, struct mem_region *avail, int *availsz);97static u_long mpc85xx_timebase_freq(platform_t, struct cpuref *cpuref);98static int mpc85xx_smp_first_cpu(platform_t, struct cpuref *cpuref);99static int mpc85xx_smp_next_cpu(platform_t, struct cpuref *cpuref);100static int mpc85xx_smp_get_bsp(platform_t, struct cpuref *cpuref);101static int mpc85xx_smp_start_cpu(platform_t, struct pcpu *cpu);102static void mpc85xx_smp_timebase_sync(platform_t, u_long tb, int ap);103104static void mpc85xx_reset(platform_t);105106static platform_method_t mpc85xx_methods[] = {107PLATFORMMETHOD(platform_probe, mpc85xx_probe),108PLATFORMMETHOD(platform_attach, mpc85xx_attach),109PLATFORMMETHOD(platform_mem_regions, mpc85xx_mem_regions),110PLATFORMMETHOD(platform_timebase_freq, mpc85xx_timebase_freq),111112PLATFORMMETHOD(platform_smp_first_cpu, mpc85xx_smp_first_cpu),113PLATFORMMETHOD(platform_smp_next_cpu, mpc85xx_smp_next_cpu),114PLATFORMMETHOD(platform_smp_get_bsp, mpc85xx_smp_get_bsp),115PLATFORMMETHOD(platform_smp_start_cpu, mpc85xx_smp_start_cpu),116PLATFORMMETHOD(platform_smp_timebase_sync, mpc85xx_smp_timebase_sync),117118PLATFORMMETHOD(platform_reset, mpc85xx_reset),119120PLATFORMMETHOD_END121};122123DEFINE_CLASS_0(mpc85xx, mpc85xx_platform, mpc85xx_methods, 0);124125PLATFORM_DEF(mpc85xx_platform);126127static int128mpc85xx_probe(platform_t plat)129{130u_int pvr = (mfpvr() >> 16) & 0xFFFF;131132switch (pvr) {133case FSL_E500v1:134case FSL_E500v2:135case FSL_E500mc:136case FSL_E5500:137case FSL_E6500:138return (BUS_PROBE_DEFAULT);139}140return (ENXIO);141}142143int144mpc85xx_attach(platform_t plat)145{146phandle_t cpus, child, ccsr;147const char *soc_name_guesses[] = {"/soc", "soc", NULL};148const char **name;149pcell_t ranges[6], acells, pacells, scells;150uint64_t ccsrbar, ccsrsize;151int i;152153if ((cpus = OF_finddevice("/cpus")) != -1) {154for (maxcpu = 0, child = OF_child(cpus); child != 0;155child = OF_peer(child), maxcpu++)156;157} else158maxcpu = 1;159160/*161* Locate CCSR region. Irritatingly, there is no way to find it162* unless you already know where it is. Try to infer its location163* from the device tree.164*/165166ccsr = -1;167for (name = soc_name_guesses; *name != NULL && ccsr == -1; name++)168ccsr = OF_finddevice(*name);169if (ccsr == -1) {170char type[64];171172/* That didn't work. Search for devices of type "soc" */173child = OF_child(OF_peer(0));174for (OF_child(child); child != 0; child = OF_peer(child)) {175if (OF_getprop(child, "device_type", type, sizeof(type))176<= 0)177continue;178179if (strcmp(type, "soc") == 0) {180ccsr = child;181break;182}183}184}185186if (ccsr == -1)187panic("Could not locate CCSR window!");188189OF_getprop(ccsr, "#size-cells", &scells, sizeof(scells));190OF_getprop(ccsr, "#address-cells", &acells, sizeof(acells));191OF_searchprop(OF_parent(ccsr), "#address-cells", &pacells,192sizeof(pacells));193OF_getprop(ccsr, "ranges", ranges, sizeof(ranges));194ccsrbar = ccsrsize = 0;195for (i = acells; i < acells + pacells; i++) {196ccsrbar <<= 32;197ccsrbar |= ranges[i];198}199for (i = acells + pacells; i < acells + pacells + scells; i++) {200ccsrsize <<= 32;201ccsrsize |= ranges[i];202}203ccsrbar_va = pmap_early_io_map(ccsrbar, ccsrsize);204ccsrbar_pa = ccsrbar;205ccsrbar_size = ccsrsize;206207mpc85xx_enable_l3_cache();208209return (0);210}211212void213mpc85xx_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,214struct mem_region *avail, int *availsz)215{216217ofw_mem_regions(phys, physsz, avail, availsz);218}219220static u_long221mpc85xx_timebase_freq(platform_t plat, struct cpuref *cpuref)222{223u_long ticks;224phandle_t cpus, child;225pcell_t freq;226227if (bootinfo != NULL) {228if (bootinfo[0] == 1) {229/* Backward compatibility. See 8-STABLE. */230ticks = bootinfo[3] >> 3;231} else {232/* Compatibility with Juniper's loader. */233ticks = bootinfo[5] >> 3;234}235} else236ticks = 0;237238if ((cpus = OF_finddevice("/cpus")) == -1)239goto out;240241if ((child = OF_child(cpus)) == 0)242goto out;243244switch (OF_getproplen(child, "timebase-frequency")) {245case 4:246{247uint32_t tbase;248OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase));249ticks = tbase;250return (ticks);251}252case 8:253{254uint64_t tbase;255OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase));256ticks = tbase;257return (ticks);258}259default:260break;261}262263freq = 0;264if (OF_getprop(child, "bus-frequency", (void *)&freq,265sizeof(freq)) <= 0)266goto out;267268if (freq == 0)269goto out;270271/*272* Time Base and Decrementer are updated every 8 CCB bus clocks.273* HID0[SEL_TBCLK] = 0274*/275if (mpc85xx_is_qoriq())276ticks = freq / 32;277else278ticks = freq / 8;279280out:281if (ticks <= 0)282panic("Unable to determine timebase frequency!");283284return (ticks);285}286287static int288mpc85xx_smp_first_cpu(platform_t plat, struct cpuref *cpuref)289{290291cpu = 0;292cpuref->cr_cpuid = cpu;293cpuref->cr_hwref = cpuref->cr_cpuid;294if (bootverbose)295printf("powerpc_smp_first_cpu: cpuid %d\n", cpuref->cr_cpuid);296cpu++;297298return (0);299}300301static int302mpc85xx_smp_next_cpu(platform_t plat, struct cpuref *cpuref)303{304305if (cpu >= maxcpu)306return (ENOENT);307308cpuref->cr_cpuid = cpu++;309cpuref->cr_hwref = cpuref->cr_cpuid;310if (bootverbose)311printf("powerpc_smp_next_cpu: cpuid %d\n", cpuref->cr_cpuid);312313return (0);314}315316static int317mpc85xx_smp_get_bsp(platform_t plat, struct cpuref *cpuref)318{319320cpuref->cr_cpuid = mfspr(SPR_PIR);321cpuref->cr_hwref = cpuref->cr_cpuid;322323return (0);324}325326#ifdef SMP327static int328mpc85xx_smp_start_cpu_epapr(platform_t plat, struct pcpu *pc)329{330vm_paddr_t rel_pa, bptr;331volatile struct cpu_release *rel;332vm_offset_t rel_va, rel_page;333phandle_t node;334int i;335336/* If we're calling this, the node already exists. */337node = OF_finddevice("/cpus");338for (i = 0, node = OF_child(node); i < pc->pc_cpuid;339i++, node = OF_peer(node))340;341if (OF_getencprop(node, "cpu-release-addr", (pcell_t *)&rel_pa,342sizeof(rel_pa)) == -1) {343return (ENOENT);344}345346rel_page = kva_alloc(PAGE_SIZE);347if (rel_page == 0)348return (ENOMEM);349350critical_enter();351rel_va = rel_page + (rel_pa & PAGE_MASK);352pmap_kenter(rel_page, rel_pa & ~PAGE_MASK);353rel = (struct cpu_release *)rel_va;354bptr = pmap_kextract((uintptr_t)__boot_page);355356cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));357rel->pir = pc->pc_cpuid; __asm __volatile("sync" ::: "memory");358rel->entry_h = (bptr >> 32); __asm __volatile("sync" ::: "memory");359cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));360rel->entry_l = bptr & 0xffffffff; __asm __volatile("sync" ::: "memory");361cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));362if (bootverbose)363printf("Waking up CPU %d via CPU release page %p\n",364pc->pc_cpuid, rel);365critical_exit();366pmap_kremove(rel_page);367kva_free(rel_page, PAGE_SIZE);368369return (0);370}371#endif372373static int374mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)375{376#ifdef SMP377vm_paddr_t bptr;378uint32_t reg;379int timeout;380uintptr_t brr;381int cpuid;382int epapr_boot = 0;383uint32_t tgt;384385if (mpc85xx_is_qoriq()) {386reg = ccsr_read4(OCP85XX_COREDISR);387cpuid = pc->pc_cpuid;388389if ((reg & (1 << cpuid)) != 0) {390printf("%s: CPU %d is disabled!\n", __func__, pc->pc_cpuid);391return (-1);392}393394brr = OCP85XX_BRR;395} else {396brr = OCP85XX_EEBPCR;397cpuid = pc->pc_cpuid + 24;398}399bp_kernload = kernload;400bp_virtaddr = (vm_offset_t)&__boot_page;401/*402* bp_kernload and bp_virtaddr are in the boot page. Sync the cache403* because ePAPR booting has the other core(s) already running.404*/405cpu_flush_dcache(&bp_kernload, sizeof(bp_kernload));406cpu_flush_dcache(&bp_virtaddr, sizeof(bp_virtaddr));407408ap_pcpu = pc;409__asm __volatile("msync; isync");410411/* First try the ePAPR way. */412if (mpc85xx_smp_start_cpu_epapr(plat, pc) == 0) {413epapr_boot = 1;414goto spin_wait;415}416417reg = ccsr_read4(brr);418if ((reg & (1 << cpuid)) != 0) {419printf("SMP: CPU %d already out of hold-off state!\n",420pc->pc_cpuid);421return (ENXIO);422}423424/* Flush caches to have our changes hit DRAM. */425cpu_flush_dcache(__boot_page, 4096);426427bptr = pmap_kextract((uintptr_t)__boot_page);428KASSERT((bptr & 0xfff) == 0,429("%s: boot page is not aligned (%#jx)", __func__, (uintmax_t)bptr));430if (mpc85xx_is_qoriq()) {431/*432* Read DDR controller configuration to select proper BPTR target ID.433*434* On P5020 bit 29 of DDR1_CS0_CONFIG enables DDR controllers435* interleaving. If this bit is set, we have to use436* OCP85XX_TGTIF_RAM_INTL as BPTR target ID. On other QorIQ DPAA SoCs,437* this bit is reserved and always 0.438*/439440reg = ccsr_read4(OCP85XX_DDR1_CS0_CONFIG);441if (reg & (1 << 29))442tgt = OCP85XX_TGTIF_RAM_INTL;443else444tgt = OCP85XX_TGTIF_RAM1;445446/*447* Set BSTR to the physical address of the boot page448*/449ccsr_write4(OCP85XX_BSTRH, bptr >> 32);450ccsr_write4(OCP85XX_BSTRL, bptr);451ccsr_write4(OCP85XX_BSTAR, OCP85XX_ENA_MASK |452(tgt << OCP85XX_TRGT_SHIFT_QORIQ) | (ffsl(PAGE_SIZE) - 2));453454/* Read back OCP85XX_BSTAR to synchronize write */455ccsr_read4(OCP85XX_BSTAR);456457/*458* Enable and configure time base on new CPU.459*/460461/* Set TB clock source to platform clock / 32 */462reg = ccsr_read4(CCSR_CTBCKSELR);463ccsr_write4(CCSR_CTBCKSELR, reg & ~(1 << pc->pc_cpuid));464465/* Enable TB */466reg = ccsr_read4(CCSR_CTBENR);467ccsr_write4(CCSR_CTBENR, reg | (1 << pc->pc_cpuid));468} else {469/*470* Set BPTR to the physical address of the boot page471*/472bptr = (bptr >> 12) | 0x80000000u;473ccsr_write4(OCP85XX_BPTR, bptr);474__asm __volatile("isync; msync");475}476477/*478* Release AP from hold-off state479*/480reg = ccsr_read4(brr);481ccsr_write4(brr, reg | (1 << cpuid));482__asm __volatile("isync; msync");483484spin_wait:485timeout = 500;486while (!pc->pc_awake && timeout--)487DELAY(1000); /* wait 1ms */488489/*490* Disable boot page translation so that the 4K page at the default491* address (= 0xfffff000) isn't permanently remapped and thus not492* usable otherwise.493*/494if (!epapr_boot) {495if (mpc85xx_is_qoriq())496ccsr_write4(OCP85XX_BSTAR, 0);497else498ccsr_write4(OCP85XX_BPTR, 0);499__asm __volatile("isync; msync");500}501502if (!pc->pc_awake)503panic("SMP: CPU %d didn't wake up.\n", pc->pc_cpuid);504return ((pc->pc_awake) ? 0 : EBUSY);505#else506/* No SMP support */507return (ENXIO);508#endif509}510511static void512mpc85xx_reset(platform_t plat)513{514515/*516* Try the dedicated reset register first.517* If the SoC doesn't have one, we'll fall518* back to using the debug control register.519*/520ccsr_write4(OCP85XX_RSTCR, 2);521522mtmsr(mfmsr() & ~PSL_DE);523524/* Enable debug interrupts and issue reset. */525mtspr(SPR_DBCR0, DBCR0_IDM | DBCR0_RST_SYSTEM);526__asm __volatile("isync");527528/* Enable Debug Interrupts in MSR. */529mtmsr(mfmsr() | PSL_DE);530531printf("Reset failed...\n");532while (1)533;534}535536static void537mpc85xx_smp_timebase_sync(platform_t plat, u_long tb, int ap)538{539static volatile bool tb_ready;540static volatile int cpu_done;541542if (ap) {543/* APs. Hold off until we get a stable timebase. */544while (!tb_ready)545atomic_thread_fence_seq_cst();546mttb(tb);547atomic_add_int(&cpu_done, 1);548while (cpu_done < mp_ncpus)549atomic_thread_fence_seq_cst();550} else {551/* BSP */552freeze_timebase(rcpm_dev, true);553tb_ready = true;554mttb(tb);555atomic_add_int(&cpu_done, 1);556while (cpu_done < mp_ncpus)557atomic_thread_fence_seq_cst();558freeze_timebase(rcpm_dev, false);559}560}561562/* Fallback freeze. In case no real handler is found in the device tree. */563static void564dummy_freeze(device_t dev, bool freeze)565{566/* Nothing to do here, move along. */567}568569/* QorIQ Run control/power management timebase management. */570571#define RCPM_CTBENR 0x00000084572struct mpc85xx_rcpm_softc {573struct resource *sc_mem;574};575576static void577mpc85xx_rcpm_freeze_timebase(device_t dev, bool freeze)578{579struct mpc85xx_rcpm_softc *sc;580581sc = device_get_softc(dev);582583if (freeze)584bus_write_4(sc->sc_mem, RCPM_CTBENR, 0);585else586bus_write_4(sc->sc_mem, RCPM_CTBENR, (1 << maxcpu) - 1);587}588589static int590mpc85xx_rcpm_probe(device_t dev)591{592if (!ofw_bus_is_compatible(dev, "fsl,qoriq-rcpm-1.0"))593return (ENXIO);594595device_set_desc(dev, "QorIQ Run control and power management");596return (BUS_PROBE_GENERIC);597}598599static int600mpc85xx_rcpm_attach(device_t dev)601{602struct mpc85xx_rcpm_softc *sc;603int rid;604605sc = device_get_softc(dev);606freeze_timebase = mpc85xx_rcpm_freeze_timebase;607rcpm_dev = dev;608609rid = 0;610sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,611RF_ACTIVE | RF_SHAREABLE);612613return (0);614}615616static device_method_t mpc85xx_rcpm_methods[] = {617DEVMETHOD(device_probe, mpc85xx_rcpm_probe),618DEVMETHOD(device_attach, mpc85xx_rcpm_attach),619DEVMETHOD_END620};621622static driver_t mpc85xx_rcpm_driver = {623"rcpm",624mpc85xx_rcpm_methods,625sizeof(struct mpc85xx_rcpm_softc)626};627628EARLY_DRIVER_MODULE(mpc85xx_rcpm, simplebus, mpc85xx_rcpm_driver, 0, 0,629BUS_PASS_BUS);630631/* "Global utilities" power management/Timebase management. */632633#define GUTS_DEVDISR 0x00000070634#define DEVDISR_TB0 0x00004000635#define DEVDISR_TB1 0x00001000636637struct mpc85xx_guts_softc {638struct resource *sc_mem;639};640641static void642mpc85xx_guts_freeze_timebase(device_t dev, bool freeze)643{644struct mpc85xx_guts_softc *sc;645uint32_t devdisr;646647sc = device_get_softc(dev);648649devdisr = bus_read_4(sc->sc_mem, GUTS_DEVDISR);650if (freeze)651bus_write_4(sc->sc_mem, GUTS_DEVDISR,652devdisr | (DEVDISR_TB0 | DEVDISR_TB1));653else654bus_write_4(sc->sc_mem, GUTS_DEVDISR,655devdisr & ~(DEVDISR_TB0 | DEVDISR_TB1));656}657658static int659mpc85xx_guts_probe(device_t dev)660{661if (!ofw_bus_is_compatible(dev, "fsl,mpc8572-guts") &&662!ofw_bus_is_compatible(dev, "fsl,p1020-guts") &&663!ofw_bus_is_compatible(dev, "fsl,p1021-guts") &&664!ofw_bus_is_compatible(dev, "fsl,p1022-guts") &&665!ofw_bus_is_compatible(dev, "fsl,p1023-guts") &&666!ofw_bus_is_compatible(dev, "fsl,p2020-guts"))667return (ENXIO);668669device_set_desc(dev, "MPC85xx Global Utilities");670return (BUS_PROBE_GENERIC);671}672673static int674mpc85xx_guts_attach(device_t dev)675{676struct mpc85xx_rcpm_softc *sc;677int rid;678679sc = device_get_softc(dev);680freeze_timebase = mpc85xx_guts_freeze_timebase;681rcpm_dev = dev;682683rid = 0;684sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,685RF_ACTIVE | RF_SHAREABLE);686687return (0);688}689690static device_method_t mpc85xx_guts_methods[] = {691DEVMETHOD(device_probe, mpc85xx_guts_probe),692DEVMETHOD(device_attach, mpc85xx_guts_attach),693DEVMETHOD_END694};695696static driver_t mpc85xx_guts_driver = {697"guts",698mpc85xx_guts_methods,699sizeof(struct mpc85xx_guts_softc)700};701702EARLY_DRIVER_MODULE(mpc85xx_guts, simplebus, mpc85xx_guts_driver, 0, 0,703BUS_PASS_BUS);704705706