Path: blob/main/sys/powerpc/mpc85xx/platform_mpc85xx.c
106846 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{304phandle_t node;305pcell_t reg;306int i;307308if (cpu >= maxcpu)309return (ENOENT);310311cpuref->cr_cpuid = cpu++;312313node = OF_finddevice("/cpus");314for (i = 0, node = OF_child(node); i < cpuref->cr_cpuid;315i++, node = OF_peer(node))316;317if (OF_getencprop(node, "reg", ®, sizeof(reg)) > 0)318cpuref->cr_hwref = reg;319else320cpuref->cr_hwref = cpuref->cr_cpuid;321if (bootverbose)322printf("powerpc_smp_next_cpu: cpuid %d, hwref %d\n",323cpuref->cr_cpuid, (int)cpuref->cr_hwref);324325return (0);326}327328static int329mpc85xx_smp_get_bsp(platform_t plat, struct cpuref *cpuref)330{331332cpuref->cr_cpuid = mfspr(SPR_PIR);333cpuref->cr_hwref = cpuref->cr_cpuid;334335return (0);336}337338#ifdef SMP339static int340mpc85xx_smp_start_cpu_epapr(platform_t plat, struct pcpu *pc)341{342vm_paddr_t rel_pa, bptr;343volatile struct cpu_release *rel;344vm_offset_t rel_va, rel_page;345phandle_t node;346int i;347348/* If we're calling this, the node already exists. */349node = OF_finddevice("/cpus");350for (i = 0, node = OF_child(node); i < pc->pc_cpuid;351i++, node = OF_peer(node))352;353if (OF_getencprop(node, "cpu-release-addr", (pcell_t *)&rel_pa,354sizeof(rel_pa)) == -1) {355return (ENOENT);356}357358rel_page = kva_alloc(PAGE_SIZE);359if (rel_page == 0)360return (ENOMEM);361362critical_enter();363rel_va = rel_page + (rel_pa & PAGE_MASK);364pmap_kenter(rel_page, rel_pa & ~PAGE_MASK);365rel = (struct cpu_release *)rel_va;366bptr = pmap_kextract((uintptr_t)__boot_page);367368cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));369rel->pir = pc->pc_cpuid; __asm __volatile("sync" ::: "memory");370rel->entry_h = (bptr >> 32); __asm __volatile("sync" ::: "memory");371cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));372rel->entry_l = bptr & 0xffffffff; __asm __volatile("sync" ::: "memory");373cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));374if (bootverbose)375printf("Waking up CPU %d via CPU release page %p\n",376pc->pc_cpuid, rel);377critical_exit();378pmap_kremove(rel_page);379kva_free(rel_page, PAGE_SIZE);380381return (0);382}383#endif384385static int386mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)387{388#ifdef SMP389vm_paddr_t bptr;390uint32_t reg;391int timeout;392uintptr_t brr;393int cpuid;394int epapr_boot = 0;395uint32_t tgt;396397if (mpc85xx_is_qoriq()) {398reg = ccsr_read4(OCP85XX_COREDISR);399cpuid = pc->pc_cpuid;400401if ((reg & (1 << cpuid)) != 0) {402printf("%s: CPU %d is disabled!\n", __func__, pc->pc_cpuid);403return (-1);404}405406brr = OCP85XX_BRR;407} else {408brr = OCP85XX_EEBPCR;409cpuid = pc->pc_cpuid + 24;410}411bp_kernload = kernload;412bp_virtaddr = (vm_offset_t)&__boot_page;413/*414* bp_kernload and bp_virtaddr are in the boot page. Sync the cache415* because ePAPR booting has the other core(s) already running.416*/417cpu_flush_dcache(&bp_kernload, sizeof(bp_kernload));418cpu_flush_dcache(&bp_virtaddr, sizeof(bp_virtaddr));419420ap_pcpu = pc;421__asm __volatile("msync; isync");422423/* First try the ePAPR way. */424if (mpc85xx_smp_start_cpu_epapr(plat, pc) == 0) {425epapr_boot = 1;426goto spin_wait;427}428429reg = ccsr_read4(brr);430if ((reg & (1 << cpuid)) != 0) {431printf("SMP: CPU %d already out of hold-off state!\n",432pc->pc_cpuid);433return (ENXIO);434}435436/* Flush caches to have our changes hit DRAM. */437cpu_flush_dcache(__boot_page, 4096);438439bptr = pmap_kextract((uintptr_t)__boot_page);440KASSERT((bptr & 0xfff) == 0,441("%s: boot page is not aligned (%#jx)", __func__, (uintmax_t)bptr));442if (mpc85xx_is_qoriq()) {443/*444* Read DDR controller configuration to select proper BPTR target ID.445*446* On P5020 bit 29 of DDR1_CS0_CONFIG enables DDR controllers447* interleaving. If this bit is set, we have to use448* OCP85XX_TGTIF_RAM_INTL as BPTR target ID. On other QorIQ DPAA SoCs,449* this bit is reserved and always 0.450*/451452reg = ccsr_read4(OCP85XX_DDR1_CS0_CONFIG);453if (reg & (1 << 29))454tgt = OCP85XX_TGTIF_RAM_INTL;455else456tgt = OCP85XX_TGTIF_RAM1;457458/*459* Set BSTR to the physical address of the boot page460*/461ccsr_write4(OCP85XX_BSTRH, bptr >> 32);462ccsr_write4(OCP85XX_BSTRL, bptr);463ccsr_write4(OCP85XX_BSTAR, OCP85XX_ENA_MASK |464(tgt << OCP85XX_TRGT_SHIFT_QORIQ) | (ffsl(PAGE_SIZE) - 2));465466/* Read back OCP85XX_BSTAR to synchronize write */467ccsr_read4(OCP85XX_BSTAR);468469/*470* Enable and configure time base on new CPU.471*/472473/* Set TB clock source to platform clock / 32 */474reg = ccsr_read4(CCSR_CTBCKSELR);475ccsr_write4(CCSR_CTBCKSELR, reg & ~(1 << pc->pc_cpuid));476477/* Enable TB */478reg = ccsr_read4(CCSR_CTBENR);479ccsr_write4(CCSR_CTBENR, reg | (1 << pc->pc_cpuid));480} else {481/*482* Set BPTR to the physical address of the boot page483*/484bptr = (bptr >> 12) | 0x80000000u;485ccsr_write4(OCP85XX_BPTR, bptr);486__asm __volatile("isync; msync");487}488489/*490* Release AP from hold-off state491*/492reg = ccsr_read4(brr);493ccsr_write4(brr, reg | (1 << cpuid));494__asm __volatile("isync; msync");495496spin_wait:497timeout = 500;498while (!pc->pc_awake && timeout--)499DELAY(1000); /* wait 1ms */500501/*502* Disable boot page translation so that the 4K page at the default503* address (= 0xfffff000) isn't permanently remapped and thus not504* usable otherwise.505*/506if (!epapr_boot) {507if (mpc85xx_is_qoriq())508ccsr_write4(OCP85XX_BSTAR, 0);509else510ccsr_write4(OCP85XX_BPTR, 0);511__asm __volatile("isync; msync");512}513514if (!pc->pc_awake)515panic("SMP: CPU %d didn't wake up.\n", pc->pc_cpuid);516return ((pc->pc_awake) ? 0 : EBUSY);517#else518/* No SMP support */519return (ENXIO);520#endif521}522523static void524mpc85xx_reset(platform_t plat)525{526527/*528* Try the dedicated reset register first.529* If the SoC doesn't have one, we'll fall530* back to using the debug control register.531*/532ccsr_write4(OCP85XX_RSTCR, 2);533534mtmsr(mfmsr() & ~PSL_DE);535536/* Enable debug interrupts and issue reset. */537mtspr(SPR_DBCR0, DBCR0_IDM | DBCR0_RST_SYSTEM);538__asm __volatile("isync");539540/* Enable Debug Interrupts in MSR. */541mtmsr(mfmsr() | PSL_DE);542543printf("Reset failed...\n");544while (1)545;546}547548static void549mpc85xx_smp_timebase_sync(platform_t plat, u_long tb, int ap)550{551static volatile bool tb_ready;552static volatile int cpu_done;553554if (ap) {555/* APs. Hold off until we get a stable timebase. */556while (!tb_ready)557atomic_thread_fence_seq_cst();558mttb(tb);559atomic_add_int(&cpu_done, 1);560while (cpu_done < mp_ncpus)561atomic_thread_fence_seq_cst();562} else {563/* BSP */564freeze_timebase(rcpm_dev, true);565tb_ready = true;566mttb(tb);567atomic_add_int(&cpu_done, 1);568while (cpu_done < mp_ncpus)569atomic_thread_fence_seq_cst();570freeze_timebase(rcpm_dev, false);571}572}573574/* Fallback freeze. In case no real handler is found in the device tree. */575static void576dummy_freeze(device_t dev, bool freeze)577{578/* Nothing to do here, move along. */579}580581/* QorIQ Run control/power management timebase management. */582583#define RCPM_CTBENR 0x00000084584struct mpc85xx_rcpm_softc {585struct resource *sc_mem;586};587588static void589mpc85xx_rcpm_freeze_timebase(device_t dev, bool freeze)590{591struct mpc85xx_rcpm_softc *sc;592593sc = device_get_softc(dev);594595if (freeze)596bus_write_4(sc->sc_mem, RCPM_CTBENR, 0);597else598bus_write_4(sc->sc_mem, RCPM_CTBENR, (1 << maxcpu) - 1);599}600601static int602mpc85xx_rcpm_probe(device_t dev)603{604if (!ofw_bus_is_compatible(dev, "fsl,qoriq-rcpm-1.0"))605return (ENXIO);606607device_set_desc(dev, "QorIQ Run control and power management");608return (BUS_PROBE_GENERIC);609}610611static int612mpc85xx_rcpm_attach(device_t dev)613{614struct mpc85xx_rcpm_softc *sc;615int rid;616617sc = device_get_softc(dev);618freeze_timebase = mpc85xx_rcpm_freeze_timebase;619rcpm_dev = dev;620621rid = 0;622sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,623RF_ACTIVE | RF_SHAREABLE);624625return (0);626}627628static device_method_t mpc85xx_rcpm_methods[] = {629DEVMETHOD(device_probe, mpc85xx_rcpm_probe),630DEVMETHOD(device_attach, mpc85xx_rcpm_attach),631DEVMETHOD_END632};633634static driver_t mpc85xx_rcpm_driver = {635"rcpm",636mpc85xx_rcpm_methods,637sizeof(struct mpc85xx_rcpm_softc)638};639640EARLY_DRIVER_MODULE(mpc85xx_rcpm, simplebus, mpc85xx_rcpm_driver, 0, 0,641BUS_PASS_BUS);642643/* "Global utilities" power management/Timebase management. */644645#define GUTS_DEVDISR 0x00000070646#define DEVDISR_TB0 0x00004000647#define DEVDISR_TB1 0x00001000648649struct mpc85xx_guts_softc {650struct resource *sc_mem;651};652653static void654mpc85xx_guts_freeze_timebase(device_t dev, bool freeze)655{656struct mpc85xx_guts_softc *sc;657uint32_t devdisr;658659sc = device_get_softc(dev);660661devdisr = bus_read_4(sc->sc_mem, GUTS_DEVDISR);662if (freeze)663bus_write_4(sc->sc_mem, GUTS_DEVDISR,664devdisr | (DEVDISR_TB0 | DEVDISR_TB1));665else666bus_write_4(sc->sc_mem, GUTS_DEVDISR,667devdisr & ~(DEVDISR_TB0 | DEVDISR_TB1));668}669670static int671mpc85xx_guts_probe(device_t dev)672{673if (!ofw_bus_is_compatible(dev, "fsl,mpc8572-guts") &&674!ofw_bus_is_compatible(dev, "fsl,p1020-guts") &&675!ofw_bus_is_compatible(dev, "fsl,p1021-guts") &&676!ofw_bus_is_compatible(dev, "fsl,p1022-guts") &&677!ofw_bus_is_compatible(dev, "fsl,p1023-guts") &&678!ofw_bus_is_compatible(dev, "fsl,p2020-guts"))679return (ENXIO);680681device_set_desc(dev, "MPC85xx Global Utilities");682return (BUS_PROBE_GENERIC);683}684685static int686mpc85xx_guts_attach(device_t dev)687{688struct mpc85xx_rcpm_softc *sc;689int rid;690691sc = device_get_softc(dev);692freeze_timebase = mpc85xx_guts_freeze_timebase;693rcpm_dev = dev;694695rid = 0;696sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,697RF_ACTIVE | RF_SHAREABLE);698699return (0);700}701702static device_method_t mpc85xx_guts_methods[] = {703DEVMETHOD(device_probe, mpc85xx_guts_probe),704DEVMETHOD(device_attach, mpc85xx_guts_attach),705DEVMETHOD_END706};707708static driver_t mpc85xx_guts_driver = {709"guts",710mpc85xx_guts_methods,711sizeof(struct mpc85xx_guts_softc)712};713714EARLY_DRIVER_MODULE(mpc85xx_guts, simplebus, mpc85xx_guts_driver, 0, 0,715BUS_PASS_BUS);716717718