#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.61 2026/06/10 21:24:11 andvar Exp $");
#include "opt_ddb.h"
#include "opt_memsize.h"
#include "opt_modular.h"
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/callout.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/ksyms.h>
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/msgbuf.h>
#include <sys/reboot.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <uvm/uvm_extern.h>
#include <sh3/bscreg.h>
#include <sh3/cpgreg.h>
#include <sh3/cache_sh3.h>
#include <sh3/exception.h>
#include <sh3/mmu.h>
#include <machine/bootinfo.h>
#include <machine/mmeye.h>
#include <machine/intr.h>
#include <machine/pcb.h>
#include <dev/cons.h>
#include "ksyms.h"
#if NKSYMS || defined(MODULAR) || defined(DDB)
#include <machine/db_machdep.h>
#include <ddb/db_extern.h>
#include <sys/exec_elf.h>
#endif
char machine[] = MACHINE;
char machine_arch[] = MACHINE_ARCH;
char bootinfo[BOOTINFO_SIZE];
void initSH3(void *, u_int, int32_t);
void LoadAndReset(const char *);
void XLoadAndReset(char *);
void consinit(void);
void sh3_cache_on(void);
void InitializeBsc(void);
void *lookup_bootinfo(unsigned int);
struct mmeye_intrhand {
void *intc_ih;
int irq;
} mmeye_intrhand[_INTR_N];
void
cpu_startup(void)
{
sh_startup();
}
static int
sysctl_machdep_loadandreset(SYSCTLFN_ARGS)
{
const char *osimage;
int error;
error = sysctl_lookup(SYSCTLFN_CALL(__UNCONST(rnode)));
if (error || newp == NULL)
return (error);
osimage = (const char *)(*(const u_long *)newp);
LoadAndReset(osimage);
return (0);
}
SYSCTL_SETUP(sysctl_machdep_setup, "sysctl machdep subtree setup")
{
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_NODE, "machdep", NULL,
NULL, 0, NULL, 0,
CTL_MACHDEP, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRUCT, "console_device", NULL,
sysctl_consdev, 0, NULL, sizeof(dev_t),
CTL_MACHDEP, CPU_CONSDEV, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
CTLTYPE_INT, "load_and_reset", NULL,
sysctl_machdep_loadandreset, 98752, NULL, 0,
CTL_MACHDEP, CPU_LOADANDRESET, CTL_EOL);
}
int waittime = -1;
void
cpu_reboot(int howto, char *bootstr)
{
#if defined(MMEYE_EPC_WDT)
epc_watchdog_timer_reset(NULL);
#endif
if (cold) {
howto |= RB_HALT;
goto haltsys;
}
boothowto = howto;
if ((howto & RB_NOSYNC) == 0 && waittime < 0) {
waittime = 0;
vfs_shutdown();
}
splhigh();
if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP)
dumpsys();
haltsys:
doshutdownhooks();
pmf_system_shutdown(boothowto);
if (howto & RB_HALT) {
printf("\n");
printf("The operating system has halted.\n");
printf("Please press any key to reboot.\n\n");
cngetc();
}
printf("rebooting...\n");
#if !defined(MMEYE_EPC_WDT)
cpu_reset();
#endif
for(;;) ;
}
void
initSH3(void *pc, u_int bim, int32_t bip)
{
extern char edata[], end[];
struct btinfo_howto *bi_howto;
size_t symbolsize;
vaddr_t kernend;
const char *bi_msg;
memset(edata, 0, end - edata);
if (bim == BOOTINFO_MAGIC) {
struct btinfo_magic *bi_magic;
memcpy(bootinfo, (void *)bip, BOOTINFO_SIZE);
bi_magic = lookup_bootinfo(BTINFO_MAGIC);
if (bi_magic == NULL) {
bi_msg = "missing bootinfo structure";
bim = (uintptr_t)bip;
} else if (bi_magic->magic != BOOTINFO_MAGIC) {
bi_msg = "invalid bootinfo structure";
bim = bi_magic->magic;
} else
bi_msg = NULL;
} else {
bi_msg = "invalid bootinfo (standalone boot?)";
}
symbolsize = 0;
#if NKSYMS || defined(MODULAR) || defined(DDB)
if (memcmp(&end, ELFMAG, SELFMAG) == 0) {
Elf_Ehdr *eh = (void *)end;
Elf_Shdr *sh = (void *)(end + eh->e_shoff);
int i;
for (i = 0; i < eh->e_shnum; i++, sh++)
if (sh->sh_offset > 0 &&
(sh->sh_offset + sh->sh_size) > symbolsize)
symbolsize = sh->sh_offset + sh->sh_size;
}
#endif
kernend = (vaddr_t)sh3_round_page(end + symbolsize);
#if defined(SH7708R)
sh_cpu_init(CPU_ARCH_SH3, CPU_PRODUCT_7708R);
#elif defined(SH7708)
sh_cpu_init(CPU_ARCH_SH3, CPU_PRODUCT_7708);
#elif defined(SH7750R)
sh_cpu_init(CPU_ARCH_SH4, CPU_PRODUCT_7750R);
#else
#warning "unknown product"
#endif
consinit();
if (bi_msg != NULL)
printf("%s: magic=%#x\n", bi_msg, bim);
bi_howto = lookup_bootinfo(BTINFO_HOWTO);
if (bi_howto != NULL)
boothowto = bi_howto->bi_howto;
physmem = atop(IOM_RAM_SIZE);
kernend = atop(round_page(SH3_P1SEG_TO_PHYS(kernend)));
uvm_page_physload(
kernend, atop(IOM_RAM_BEGIN + IOM_RAM_SIZE),
kernend, atop(IOM_RAM_BEGIN + IOM_RAM_SIZE),
VM_FREELIST_DEFAULT);
sh_proc0_init();
pmap_bootstrap();
#if NKSYMS || defined(DDB) || defined(MODULAR)
if (symbolsize)
ksyms_addsyms_elf(symbolsize, &end, end + symbolsize);
#endif
#if defined(DDB)
if (boothowto & RB_KDB)
Debugger();
#endif
__asm volatile (
"jmp @%0;"
"mov %1, r15"
:: "r"(pc),"r"(lwp0.l_md.md_pcb->pcb_sf.sf_r7_bank));
}
void
consinit(void)
{
static int initted;
if (initted)
return;
initted = 1;
cninit();
}
void
InitializeBsc(void)
{
#ifdef SH3
#ifdef NOPCMCIA
_reg_write_2(SH3_BCR1, 0x1010);
#else
_reg_write_2(SH3_BCR1, 0x1013);
#endif
#define PCMCIA_16
#ifdef PCMCIA_16
_reg_write_2(SH3_BCR2, 0x2af4);
#else
_reg_write_2(SH3_BCR2, 0x16f4);
#endif
_reg_write_2(SH3_WCR1, 0x3fff);
#if 0
_reg_write_2(SH3_WCR2, 0x4bdd);
#else
_reg_write_2(SH3_WCR2, 0xabfd);
#endif
_reg_write_2(SH3_MCR, 0x6135);
_reg_write_2(SH3_DCR, 0x0000);
_reg_write_2(SH3_PCR, 0x00ff);
#endif
#ifdef SH4
_reg_write_4(SH4_BCR1, 0x0008000d);
_reg_write_2(SH4_BCR2, 0x6af9);
_reg_write_4(SH4_WCR1, 0x11111111);
_reg_write_4(SH4_WCR2, 0xdd7845a7);
_reg_write_4(SH4_WCR3, 0x05555555);
_reg_write_4(SH4_MCR, 0x500921f4);
_reg_write_2(SH4_PCR, 0x4b2d);
#endif
_reg_write_2(SH_(RTCSR), 0xa594);
_reg_write_2(SH_(RTCNT), 0xa500);
#ifdef SH3
_reg_write_2(SH3_RTCOR, 0xa50d);
#elif SH4
_reg_write_2(SH4_RTCOR, 0xa575);
#endif
_reg_write_2(SH_(RFCR), 0xa400);
#if defined(SH7708R)
_reg_write_2(SH3_FRQCR, 0xa100);
#elif defined(SH7708)
_reg_write_2(SH3_FRQCR, 0x0112);
#elif defined(SH7750R)
_reg_write_2(SH4_FRQCR, 0x0e0a);
#endif
#ifndef MMEYE_NO_CACHE
_reg_write_4(SH3_CCR, SH3_CCR_CE);
#endif
}
void
sh3_cache_on(void)
{
#ifndef MMEYE_NO_CACHE
_reg_write_4(SH3_CCR, SH3_CCR_CE);
_reg_write_4(SH3_CCR, SH3_CCR_CF | SH3_CCR_CE);
_reg_write_4(SH3_CCR, SH3_CCR_CE);
#endif
}
#define OSIMAGE_BUF_ADDR (IOM_RAM_BEGIN + 0x00400000)
void
LoadAndReset(const char *osimage)
{
void *buf_addr;
u_long size;
const u_long *src;
u_long *dest;
u_long csum = 0;
u_long csum2 = 0;
u_long size2;
MMTA_IMASK = 0;
printf("LoadAndReset: copy start\n");
buf_addr = (void *)OSIMAGE_BUF_ADDR;
size = *(const u_long *)osimage;
src = (const u_long *)osimage;
dest = buf_addr;
size = (size + sizeof(u_long) * 2 + 3) >> 2;
size2 = size;
while (size--) {
csum += *src;
*dest++ = *src++;
}
dest = buf_addr;
while (size2--)
csum2 += *dest++;
printf("LoadAndReset: copy end[%lx,%lx]\n", csum, csum2);
printf("start XLoadAndReset\n");
XLoadAndReset(buf_addr);
}
#ifdef sh3_tmp
#define UART_BASE 0xa4000008
#define IER 1
#define IER_RBF 0x01
#define IER_TBE 0x02
#define IER_MSI 0x08
#define FCR 2
#define LCR 3
#define LCR_DLAB 0x80
#define DLM 1
#define DLL 0
#define MCR 4
#define MCR_RTS 0x02
#define MCR_DTR 0x01
#define MCR_OUT2 0x08
#define RTS_MODE (MCR_RTS|MCR_DTR)
#define LSR 5
#define LSR_THRE 0x20
#define LSR_ERROR 0x1e
#define THR 0
#define IIR 2
#define IIR_II 0x06
#define IIR_LSI 0x06
#define IIR_MSI 0x00
#define IIR_TBE 0x02
#define IIR_PEND 0x01
#define RBR 0
#define MSR 6
#define OUTP(port, val) *(volatile unsigned char *)(UART_BASE+port) = val
#define INP(port) (*(volatile unsigned char *)(UART_BASE+port))
void
Init16550()
{
int diviser;
int tmp;
diviser = 6;
OUTP(IER, 0);
OUTP(FCR, 0x00);
tmp = INP(LSR);
tmp = INP(MSR);
tmp = INP(IIR);
tmp = INP(RBR);
OUTP(LCR, INP(LCR) | LCR_DLAB);
OUTP(DLM, 0xff & (diviser>>8));
OUTP(DLL, 0xff & diviser);
OUTP(LCR, INP(LCR) & ~LCR_DLAB);
OUTP(MCR, 0);
OUTP(LCR, 0x03);
OUTP(MCR, RTS_MODE | MCR_OUT2);
}
void
Send16550(int c)
{
while (1) {
OUTP(THR, c);
while ((INP(LSR) & LSR_THRE) == 0)
;
if (c == '\n')
c = '\r';
else
return;
}
}
#endif
void
intc_intr(int ssr, int spc, int ssp)
{
struct intc_intrhand *ih;
int evtcode;
curcpu()->ci_data.cpu_nintr++;
evtcode = _reg_read_4(SH_(INTEVT));
ih = EVTCODE_IH(evtcode);
KDASSERT(ih->ih_func);
(void)_cpu_intr_resume(ih->ih_level);
if (evtcode == SH_INTEVT_TMU0_TUNI0) {
struct clockframe cf;
cf.spc = spc;
cf.ssr = ssr;
cf.ssp = ssp;
(*ih->ih_func)(&cf);
} else {
(*ih->ih_func)(ih->ih_arg);
}
}
void *
mmeye_intr_establish(int irq, int trigger, int level, int (*func)(void *),
void *arg)
{
struct mmeye_intrhand *mh = &mmeye_intrhand[irq];
mh->intc_ih = intc_intr_establish(0x200 + (irq << 5), IST_LEVEL, level,
func, arg);
mh->irq = irq;
MMTA_IMASK |= (1 << (15 - irq));
return (mh);
}
void
mmeye_intr_disestablish(void *ih)
{
struct mmeye_intrhand *mh = ih;
MMTA_IMASK &= ~(1 << (15 - mh->irq));
intc_intr_disestablish(mh->intc_ih);
}
int
bus_space_map(bus_space_tag_t t, bus_addr_t addr, bus_size_t size, int flags,
bus_space_handle_t *bshp)
{
#if defined(SH7750R)
if (t == SH3_BUS_SPACE_PCMCIA_IO ||
t == SH3_BUS_SPACE_PCMCIA_IO8 ||
t == SH3_BUS_SPACE_PCMCIA_MEM ||
t == SH3_BUS_SPACE_PCMCIA_MEM8 ||
t == SH3_BUS_SPACE_PCMCIA_ATT ||
t == SH3_BUS_SPACE_PCMCIA_ATT8) {
pt_entry_t *pte;
vaddr_t va;
u_long pa, endpa;
uint32_t sa = 0;
pa = sh3_trunc_page(addr);
endpa = sh3_round_page(addr + size);
#ifdef DIAGNOSTIC
if (endpa <= pa)
panic("bus_space_map: overflow");
#endif
va = uvm_km_alloc(kernel_map, endpa - pa, 0, UVM_KMF_VAONLY);
if (va == 0) {
printf("%s: nomem\n", __func__);
return ENOMEM;
}
*bshp = (bus_space_handle_t)(va + (addr & PGOFSET));
switch (t) {
case SH3_BUS_SPACE_PCMCIA_IO: sa = _PG_PCMCIA_IO16; break;
case SH3_BUS_SPACE_PCMCIA_IO8: sa = _PG_PCMCIA_IO8; break;
case SH3_BUS_SPACE_PCMCIA_MEM: sa = _PG_PCMCIA_MEM16; break;
case SH3_BUS_SPACE_PCMCIA_MEM8: sa = _PG_PCMCIA_MEM8; break;
case SH3_BUS_SPACE_PCMCIA_ATT: sa = _PG_PCMCIA_ATTR16; break;
case SH3_BUS_SPACE_PCMCIA_ATT8: sa = _PG_PCMCIA_ATTR8; break;
}
for ( ; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE) {
pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0);
pte = __pmap_kpte_lookup(va);
KDASSERT(pte);
*pte |= sa;
sh_tlb_update(0, va, *pte);
}
return 0;
}
#endif
*bshp = (bus_space_handle_t)addr;
return 0;
}
void
sh_memio_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
{
#if defined(SH7750R)
if (t == SH3_BUS_SPACE_PCMCIA_IO ||
t == SH3_BUS_SPACE_PCMCIA_IO8 ||
t == SH3_BUS_SPACE_PCMCIA_MEM ||
t == SH3_BUS_SPACE_PCMCIA_MEM8 ||
t == SH3_BUS_SPACE_PCMCIA_ATT ||
t == SH3_BUS_SPACE_PCMCIA_ATT8) {
u_long va, endva;
va = sh3_trunc_page(bsh);
endva = sh3_round_page(bsh + size);
#ifdef DIAGNOSTIC
if (endva <= va)
panic("bus_space_unmap: overflow");
#endif
pmap_kremove(va, endva - va);
uvm_km_free(kernel_map, va, endva - va, UVM_KMF_VAONLY);
return;
}
#endif
}
int
sh_memio_subregion(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
bus_size_t size, bus_space_handle_t *nbshp)
{
*nbshp = bsh + offset;
return 0;
}
void *
lookup_bootinfo(unsigned int type)
{
struct btinfo_common *bt;
char *help = bootinfo;
if (((struct btinfo_common *)help)->next == 0)
return NULL;
do {
bt = (struct btinfo_common *)help;
printf("Type %d @%p\n", bt->type, (void *)(intptr_t)bt);
if (bt->type == type)
return (void *)help;
help += bt->next;
} while (bt->next != 0 &&
(size_t)help < (size_t)bootinfo + BOOTINFO_SIZE);
return NULL;
}
#if defined(MMEYE_EPC_WDT)
callout_t epc_wdtc;
void
epc_watchdog_timer_reset(void *arg)
{
callout_schedule(&epc_wdtc, 15 * hz);
EPC_WDT = WDT_RDYCMD;
EPC_WDT = WDT_CLRCMD;
}
#endif