/* $NetBSD: machdep.c,v 1.49 2022/07/22 20:09:47 thorpej Exp $ */
/*-
* Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Raytheon BBN Technologies Corp and Defense Advanced Research Projects
* Agency and which was developed by Matt Thomas of 3am Software Foundry.
*
* This material is based upon work supported by the Defense Advanced Research
* Projects Agency and Space and Naval Warfare Systems Center, Pacific, under
* Contract No. N66001-09-C-2073.
* Approved for Public Release, Distribution Unlimited
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* First we need to find out how much physical memory we have.
* We could let our bootloader tell us, but it's almost as easy
* to ask the DDR memory controller.
*/
mr = physmemr;
for (u_int i = 0; i < 4; i++) {
uint32_t v = cpu_read_4(DDRC1_BASE + CS_CONFIG(i));
if (v & CS_CONFIG_EN) {
v = cpu_read_4(DDRC1_BASE + CS_BNDS(i));
if (v == 0)
continue;
mr->start = BNDS_SA_GET(v);
mr->size = BNDS_SIZE_GET(v);
#ifdef MEMSIZE
if (mr->start >= MEMSIZE)
continue;
if (mr->start + mr->size > MEMSIZE)
mr->size = MEMSIZE - mr->start;
#endif
#if 0
printf(" [%zd]={%#"PRIx64"@%#"PRIx64"}",
mr - physmemr, mr->size, mr->start);
#endif
mr++;
}
}
if (mr == physmemr)
panic("no memory configured!");
/*
* Sort memory regions from low to high and coalesce adjacent regions
*/
u_int cnt = mr - physmemr;
if (cnt > 1) {
for (u_int i = 0; i < cnt - 1; i++) {
for (u_int j = i + 1; j < cnt; j++) {
if (physmemr[j].start < physmemr[i].start) {
phys_ram_seg_t tmp = physmemr[i];
physmemr[i] = physmemr[j];
physmemr[j] = tmp;
}
}
}
mr = physmemr;
for (u_int i = 0; i + 1 < cnt; i++, mr++) {
if (mr->start + mr->size == mr[1].start) {
mr->size += mr[1].size;
for (u_int j = 1; i + j + 1 < cnt; j++)
mr[j] = mr[j+1];
cnt--;
}
}
} else if (cnt == 0) {
panic("%s: no memory found", __func__);
}
/*
* Copy physical memory to available memory.
*/
memcpy(availmemr, physmemr, cnt * sizeof(physmemr[0]));
/*
* Adjust available memory to skip kernel at start of memory.
*/
availmemr[0].size -= endkernel - availmemr[0].start;
availmemr[0].start = endkernel;
mr = availmemr;
for (u_int i = 0; i < cnt; i++, mr++) {
/*
* U-boot reserves a boot-page on multi-core chips.
* We need to make sure that we never disturb it.
*/
const paddr_t mr_end = mr->start + mr->size;
if (mr_end > boot_page && boot_page >= mr->start) {
/*
* Normally u-boot will put in at the end
* of memory. But in case it doesn't, deal
* with all possibilities.
*/
if (boot_page + PAGE_SIZE == mr_end) {
mr->size -= PAGE_SIZE;
} else if (boot_page == mr->start) {
mr->start += PAGE_SIZE;
mr->size -= PAGE_SIZE;
} else {
mr->size = boot_page - mr->start;
mr++;
for (u_int j = cnt; j > i + 1; j--) {
availmemr[j] = availmemr[j-1];
}
cnt++;
mr->start = boot_page + PAGE_SIZE;
mr->size = mr_end - mr->start;
}
break;
}
}
/*
* Steal pages at the end of memory for the kernel message buffer.
*/
mr = availmemr + cnt - 1;
KASSERT(mr->size >= round_page(MSGBUFSIZE));
mr->size -= round_page(MSGBUFSIZE);
msgbuf_paddr = (uintptr_t)(mr->start + mr->size);
/*
* Calculate physmem.
*/
for (u_int i = 0; i < cnt; i++)
physmem += atop(physmemr[i].size);
h->hatch_self = self;
h->hatch_ci = ci;
h->hatch_running = -1;
h->hatch_pir = e[i].entry_pir;
h->hatch_hid0 = mfspr(SPR_HID0);
u_int tlbidx;
e500_tlb_lookup_xtlb(0, &tlbidx);
h->hatch_tlbidx = tlbidx;
KASSERT(h->hatch_sp != 0);
/*
* Get new timebase. We don't want to deal with
* timebase crossing a 32-bit boundary so make sure
* that we have enough headroom to do the timebase
* synchronization.
*/
#define TBSYNC_SLOP 2000
uint32_t tbl;
uint32_t tbu;
do {
tbu = mfspr(SPR_RTBU);
tbl = mfspr(SPR_RTBL) + TBSYNC_SLOP;
} while (tbl < TBSYNC_SLOP);
/*
* And here we go...
*/
e[i].entry_addr_lower =
(uint32_t)e500_spinup_trampoline;
dcache_wbinv((vaddr_t)&e[i], sizeof(e[i]));
__asm __volatile("sync;isync");
__insn_barrier();
for (u_int timo = 0; timo++ < 10000; ) {
dcache_inv((vaddr_t)&e[i], sizeof(e[i]));
if (e[i].entry_addr_lower == 3) {
#if 0
printf(
"%s: cpu%u started in %u spins\n",
__func__, cpu_index(ci), timo);
#endif
break;
}
}
for (u_int timo = 0; timo++ < 10000; ) {
dcache_inv((vaddr_t)h, sizeof(*h));
if (h->hatch_running == 0) {
#if 0
printf(
"%s: cpu%u cracked in %u spins: (running=%d)\n",
__func__, cpu_index(ci),
timo, h->hatch_running);
#endif
break;
}
}
if (h->hatch_running == -1) {
aprint_error_dev(self,
"hatch failed (timeout): running=%d"
", entry=%#x\n",
h->hatch_running, e[i].entry_addr_lower);
goto out;
}
/*
* First then we do is to synchronize timebases.
* TBSYNC_SLOP*16 should be more than enough
* instructions.
*/
while (tbl != mftbl())
continue;
h->hatch_running = 1;
dcache_wbinv((vaddr_t)h, sizeof(*h));
__asm("sync;isync");
__insn_barrier();
printf("%s: cpu%u set to running\n",
__func__, cpu_index(ci));
for (u_int timo = 10000; timo-- > 0; ) {
dcache_inv((vaddr_t)h, sizeof(*h));
if (h->hatch_running > 1)
break;
}
if (h->hatch_running == 1) {
printf(
"%s: tb sync failed: offset from %"PRId64"=%"PRId64" (running=%d)\n",
__func__,
((int64_t)tbu << 32) + tbl,
(int64_t)
(((uint64_t)h->hatch_tbu << 32)
+ (uint64_t)h->hatch_tbl),
h->hatch_running);
goto out;
}
printf(
"%s: tb synced: offset=%"PRId64" (running=%d)\n",
__func__,
(int64_t)
(((uint64_t)h->hatch_tbu << 32)
+ (uint64_t)h->hatch_tbl),
h->hatch_running);
/*
* Now we wait for the hatching to complete. 30ms
* should be long enough.
*/
for (u_int timo = 30000; timo-- > 0; ) {
if (kcpuset_isset(hatchlings, id)) {
aprint_normal_dev(self,
"hatch successful (%u spins, "
"timebase adjusted by %"PRId64")\n",
30000 - timo,
(int64_t)
(((uint64_t)h->hatch_tbu << 32)
+ (uint64_t)h->hatch_tbl));
goto out;
}
DELAY(1);
}
/*
* Make sure we don't enter NAP or SLEEP if PSL_POW (MSR[WE]) is set.
* DOZE is ok.
*/
const register_t hid0 = mfspr(SPR_HID0);
mtspr(SPR_HID0,
(hid0 & ~(HID0_NAP | HID0_SLEEP)) | HID0_TBEN | HID0_EMCP | HID0_DOZE);
#ifdef CADMUS
/*
* Need to cache this from cadmus since we need to unmap cadmus since
* it falls in the middle of kernel address space.
*/
cadmus_pci = ((uint8_t *)0xf8004000)[CM_PCI];
cadmus_csr = ((uint8_t *)0xf8004000)[CM_CSR];
((uint8_t *)0xf8004000)[CM_CSR] |= CM_RST_PHYRST;
printf(" cadmus_pci=%#x", cadmus_pci);
printf(" cadmus_csr=%#x", cadmus_csr);
((uint8_t *)0xf8004000)[CM_CSR] = 0;
if ((cadmus_pci & CM_PCI_PSPEED) == CM_PCI_PSPEED_66) {
e500_sys_clk *= 2;
}
#endif
#ifdef PIXIS
pixis_spd = ((uint8_t *)PX_BASE)[PX_SPD];
printf(" pixis_spd=%#x sysclk=%"PRIuMAX,
pixis_spd, PX_SPD_SYSCLK_GET(pixis_spd));
#ifndef SYS_CLK
e500_sys_clk = pixis_spd_map[PX_SPD_SYSCLK_GET(pixis_spd)];
#else
printf(" pixis_sysclk=%u", pixis_spd_map[PX_SPD_SYSCLK_GET(pixis_spd)]);
#endif
#endif
printf(" porpllsr=0x%08x",
*(uint32_t *)(GUR_BASE + GLOBAL_BASE + PORPLLSR));
printf(" sys_clk=%"PRIu64, e500_sys_clk);
/*
* Make sure arguments are page aligned.
*/
startkernel = trunc_page(startkernel);
endkernel = round_page(endkernel);
/*
* Initialize the bus space tag used to access the 85xx general
* utility registers. It doesn't need to be extent protected.
* We know the GUR is mapped via a TLB1 entry so we add a limited
* mapiodev which allows mappings in GUR space.
*/
CTASSERT(offsetof(struct tlb_md_io_ops, md_tlb_mapiodev) == 0);
cpu_md_ops.md_tlb_io_ops = (const void *)&early_tlb_mapiodev;
bus_space_init(&gur_bst, NULL, NULL, 0);
bus_space_init(&gur_le_bst, NULL, NULL, 0);
cpu->cpu_bst = &gur_bst;
cpu->cpu_le_bst = &gur_le_bst;
cpu->cpu_bsh = gur_bsh;
/*
* Attach the console early, really early.
*/
consinit();
/*
* Reset the PIC to a known state.
*/
cpu_write_4(OPENPIC_BASE + OPENPIC_GCR, GCR_RST);
while (cpu_read_4(OPENPIC_BASE + OPENPIC_GCR) & GCR_RST)
;
#if 0
cpu_write_4(OPENPIC_BASE + OPENPIC_CTPR, 15); /* IPL_HIGH */
#endif
printf(" openpic-reset(ctpr=%u)",
cpu_read_4(OPENPIC_BASE + OPENPIC_CTPR));
/*
* fill in with an absolute branch to a routine that will panic.
*/
*(volatile int *)0 = 0x48000002 | (int) calltozero;
/*
* Get the cache sizes.
*/
cpu_probe_cache();
printf(" cache(DC=%uKB/%u,IC=%uKB/%u)",
ci->ci_ci.dcache_size >> 10,
ci->ci_ci.dcache_line_size,
ci->ci_ci.icache_size >> 10,
ci->ci_ci.icache_line_size);
/*
* Now find out how much memory is attached
*/
pmemsize = memprobe(endkernel);
cpu->cpu_highmem = pmemsize;
printf(" memprobe=%zuMB", (size_t) (pmemsize >> 20));
/*
* Now we need cleanout the TLB of stuff that we don't need.
*/
e500_tlb_init(endkernel, pmemsize);
printf(" e500_tlbinit(%#lx,%zuMB)",
endkernel, (size_t) (pmemsize >> 20));
/*
* Let's take all the indirect calls via our stubs and patch
* them to be direct calls.
*/
cpu_fixup_stubs();
/*
* As a debug measure we can change the TLB entry that maps all of
* memory to one that encompasses the 64KB with the kernel vectors.
* All other pages will be soft faulted into the TLB as needed.
*/
e500_tlb_minimize(endkernel);
/*
* Set some more MD helpers
*/
cpu_md_ops.md_cpunode_locs = mpc8548_cpunode_locs;
cpu_md_ops.md_device_register = e500_device_register;
cpu_md_ops.md_cpu_attach = e500_cpu_attach;
cpu_md_ops.md_cpu_reset = e500_cpu_reset;
#if NGPIO > 0
cpu_md_ops.md_cpunode_attach = pq3gpio_attach;
#endif
printf(" initppc done!\n");
/*
* Look for the Book-E modules in the right place.
*/
module_machine = module_machine_booke;
}