diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
commit | 76cb841cb886eef6b3bee341a2266c76578724ad (patch) | |
tree | f5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /arch/m68k/sun3 | |
parent | Initial commit. (diff) | |
download | linux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip |
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | arch/m68k/sun3/Makefile | 7 | ||||
-rw-r--r-- | arch/m68k/sun3/config.c | 226 | ||||
-rw-r--r-- | arch/m68k/sun3/dvma.c | 69 | ||||
-rw-r--r-- | arch/m68k/sun3/idprom.c | 132 | ||||
-rw-r--r-- | arch/m68k/sun3/intersil.c | 76 | ||||
-rw-r--r-- | arch/m68k/sun3/leds.c | 14 | ||||
-rw-r--r-- | arch/m68k/sun3/mmu_emu.c | 429 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/Makefile | 6 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/console.c | 170 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/init.c | 36 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/misc.c | 95 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/printf.c | 56 | ||||
-rw-r--r-- | arch/m68k/sun3/sun3dvma.c | 377 | ||||
-rw-r--r-- | arch/m68k/sun3/sun3ints.c | 102 | ||||
-rw-r--r-- | arch/m68k/sun3x/Makefile | 5 | ||||
-rw-r--r-- | arch/m68k/sun3x/config.c | 80 | ||||
-rw-r--r-- | arch/m68k/sun3x/dvma.c | 199 | ||||
-rw-r--r-- | arch/m68k/sun3x/prom.c | 166 | ||||
-rw-r--r-- | arch/m68k/sun3x/time.c | 109 | ||||
-rw-r--r-- | arch/m68k/sun3x/time.h | 20 |
20 files changed, 2374 insertions, 0 deletions
diff --git a/arch/m68k/sun3/Makefile b/arch/m68k/sun3/Makefile new file mode 100644 index 000000000..38ba0e0ce --- /dev/null +++ b/arch/m68k/sun3/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for Linux arch/m68k/sun3 source directory +# + +obj-y := sun3ints.o sun3dvma.o idprom.o + +obj-$(CONFIG_SUN3) += config.o mmu_emu.o leds.o dvma.o intersil.o diff --git a/arch/m68k/sun3/config.c b/arch/m68k/sun3/config.c new file mode 100644 index 000000000..79a2bb857 --- /dev/null +++ b/arch/m68k/sun3/config.c @@ -0,0 +1,226 @@ +/* + * linux/arch/m68k/sun3/config.c + * + * Copyright (C) 1996,1997 Pekka Pietik{inen + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/seq_file.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/platform_device.h> + +#include <asm/oplib.h> +#include <asm/setup.h> +#include <asm/contregs.h> +#include <asm/movs.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/sun3-head.h> +#include <asm/sun3mmu.h> +#include <asm/machdep.h> +#include <asm/machines.h> +#include <asm/idprom.h> +#include <asm/intersil.h> +#include <asm/irq.h> +#include <asm/sections.h> +#include <asm/segment.h> +#include <asm/sun3ints.h> + +char sun3_reserved_pmeg[SUN3_PMEGS_NUM]; + +extern u32 sun3_gettimeoffset(void); +static void sun3_sched_init(irq_handler_t handler); +extern void sun3_get_model (char* model); +extern int sun3_hwclk(int set, struct rtc_time *t); + +volatile char* clock_va; +extern unsigned long availmem; +unsigned long num_pages; + +static void sun3_get_hardware_list(struct seq_file *m) +{ + seq_printf(m, "PROM Revision:\t%s\n", romvec->pv_monid); +} + +void __init sun3_init(void) +{ + unsigned char enable_register; + int i; + + m68k_machtype= MACH_SUN3; + m68k_cputype = CPU_68020; + m68k_fputype = FPU_68881; /* mc68881 actually */ + m68k_mmutype = MMU_SUN3; + clock_va = (char *) 0xfe06000; /* dark */ + sun3_intreg = (unsigned char *) 0xfe0a000; /* magic */ + sun3_disable_interrupts(); + + prom_init((void *)LINUX_OPPROM_BEGVM); + + GET_CONTROL_BYTE(AC_SENABLE,enable_register); + enable_register |= 0x50; /* Enable FPU */ + SET_CONTROL_BYTE(AC_SENABLE,enable_register); + GET_CONTROL_BYTE(AC_SENABLE,enable_register); + + /* This code looks suspicious, because it doesn't subtract + memory belonging to the kernel from the available space */ + + + memset(sun3_reserved_pmeg, 0, sizeof(sun3_reserved_pmeg)); + + /* Reserve important PMEGS */ + /* FIXME: These should be probed instead of hardcoded */ + + for (i=0; i<8; i++) /* Kernel PMEGs */ + sun3_reserved_pmeg[i] = 1; + + sun3_reserved_pmeg[247] = 1; /* ROM mapping */ + sun3_reserved_pmeg[248] = 1; /* AMD Ethernet */ + sun3_reserved_pmeg[251] = 1; /* VB area */ + sun3_reserved_pmeg[254] = 1; /* main I/O */ + + sun3_reserved_pmeg[249] = 1; + sun3_reserved_pmeg[252] = 1; + sun3_reserved_pmeg[253] = 1; + set_fs(KERNEL_DS); +} + +/* Without this, Bad Things happen when something calls arch_reset. */ +static void sun3_reboot (void) +{ + prom_reboot ("vmlinux"); +} + +static void sun3_halt (void) +{ + prom_halt (); +} + +/* sun3 bootmem allocation */ + +static void __init sun3_bootmem_alloc(unsigned long memory_start, + unsigned long memory_end) +{ + unsigned long start_page; + + /* align start/end to page boundaries */ + memory_start = ((memory_start + (PAGE_SIZE-1)) & PAGE_MASK); + memory_end = memory_end & PAGE_MASK; + + start_page = __pa(memory_start) >> PAGE_SHIFT; + max_pfn = num_pages = __pa(memory_end) >> PAGE_SHIFT; + + high_memory = (void *)memory_end; + availmem = memory_start; + + m68k_setup_node(0); +} + + +void __init config_sun3(void) +{ + unsigned long memory_start, memory_end; + + pr_info("ARCH: SUN3\n"); + idprom_init(); + + /* Subtract kernel memory from available memory */ + + mach_sched_init = sun3_sched_init; + mach_init_IRQ = sun3_init_IRQ; + mach_reset = sun3_reboot; + arch_gettimeoffset = sun3_gettimeoffset; + mach_get_model = sun3_get_model; + mach_hwclk = sun3_hwclk; + mach_halt = sun3_halt; + mach_get_hardware_list = sun3_get_hardware_list; + + memory_start = ((((unsigned long)_end) + 0x2000) & ~0x1fff); +// PROM seems to want the last couple of physical pages. --m + memory_end = *(romvec->pv_sun3mem) + PAGE_OFFSET - 2*PAGE_SIZE; + + m68k_num_memory=1; + m68k_memory[0].size=*(romvec->pv_sun3mem); + + sun3_bootmem_alloc(memory_start, memory_end); +} + +static void __init sun3_sched_init(irq_handler_t timer_routine) +{ + sun3_disable_interrupts(); + intersil_clock->cmd_reg=(INTERSIL_RUN|INTERSIL_INT_DISABLE|INTERSIL_24H_MODE); + intersil_clock->int_reg=INTERSIL_HZ_100_MASK; + intersil_clear(); + sun3_enable_irq(5); + intersil_clock->cmd_reg=(INTERSIL_RUN|INTERSIL_INT_ENABLE|INTERSIL_24H_MODE); + sun3_enable_interrupts(); + intersil_clear(); +} + +#if IS_ENABLED(CONFIG_SUN3_SCSI) + +static const struct resource sun3_scsi_vme_rsrc[] __initconst = { + { + .flags = IORESOURCE_IRQ, + .start = SUN3_VEC_VMESCSI0, + .end = SUN3_VEC_VMESCSI0, + }, { + .flags = IORESOURCE_MEM, + .start = 0xff200000, + .end = 0xff200021, + }, { + .flags = IORESOURCE_IRQ, + .start = SUN3_VEC_VMESCSI1, + .end = SUN3_VEC_VMESCSI1, + }, { + .flags = IORESOURCE_MEM, + .start = 0xff204000, + .end = 0xff204021, + }, +}; + +/* + * Int: level 2 autovector + * IO: type 1, base 0x00140000, 5 bits phys space: A<4..0> + */ +static const struct resource sun3_scsi_rsrc[] __initconst = { + { + .flags = IORESOURCE_IRQ, + .start = 2, + .end = 2, + }, { + .flags = IORESOURCE_MEM, + .start = 0x00140000, + .end = 0x0014001f, + }, +}; + +int __init sun3_platform_init(void) +{ + switch (idprom->id_machtype) { + case SM_SUN3 | SM_3_160: + case SM_SUN3 | SM_3_260: + platform_device_register_simple("sun3_scsi_vme", -1, + sun3_scsi_vme_rsrc, ARRAY_SIZE(sun3_scsi_vme_rsrc)); + break; + case SM_SUN3 | SM_3_50: + case SM_SUN3 | SM_3_60: + platform_device_register_simple("sun3_scsi", -1, + sun3_scsi_rsrc, ARRAY_SIZE(sun3_scsi_rsrc)); + break; + } + return 0; +} + +arch_initcall(sun3_platform_init); + +#endif diff --git a/arch/m68k/sun3/dvma.c b/arch/m68k/sun3/dvma.c new file mode 100644 index 000000000..5f92c72b0 --- /dev/null +++ b/arch/m68k/sun3/dvma.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/m68k/sun3/dvma.c + * + * Written by Sam Creasey + * + * Sun3 IOMMU routines used for dvma accesses. + * + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/bootmem.h> +#include <linux/list.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/sun3mmu.h> +#include <asm/dvma.h> + + +static unsigned long ptelist[120]; + +static unsigned long dvma_page(unsigned long kaddr, unsigned long vaddr) +{ + unsigned long pte; + unsigned long j; + pte_t ptep; + + j = *(volatile unsigned long *)kaddr; + *(volatile unsigned long *)kaddr = j; + + ptep = pfn_pte(virt_to_pfn(kaddr), PAGE_KERNEL); + pte = pte_val(ptep); +// pr_info("dvma_remap: addr %lx -> %lx pte %08lx\n", kaddr, vaddr, pte); + if(ptelist[(vaddr & 0xff000) >> PAGE_SHIFT] != pte) { + sun3_put_pte(vaddr, pte); + ptelist[(vaddr & 0xff000) >> PAGE_SHIFT] = pte; + } + + return (vaddr + (kaddr & ~PAGE_MASK)); + +} + +int dvma_map_iommu(unsigned long kaddr, unsigned long baddr, + int len) +{ + + unsigned long end; + unsigned long vaddr; + + vaddr = dvma_btov(baddr); + + end = vaddr + len; + + while(vaddr < end) { + dvma_page(kaddr, vaddr); + kaddr += PAGE_SIZE; + vaddr += PAGE_SIZE; + } + + return 0; + +} + +void __init sun3_dvma_init(void) +{ + memset(ptelist, 0, sizeof(ptelist)); +} diff --git a/arch/m68k/sun3/idprom.c b/arch/m68k/sun3/idprom.c new file mode 100644 index 000000000..1ace5353d --- /dev/null +++ b/arch/m68k/sun3/idprom.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * idprom.c: Routines to load the idprom into kernel addresses and + * interpret the data contained within. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Sun3/3x models added by David Monro (davidm@psrg.cs.usyd.edu.au) + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/string.h> + +#include <asm/oplib.h> +#include <asm/idprom.h> +#include <asm/machines.h> /* Fun with Sun released architectures. */ + +struct idprom *idprom; +EXPORT_SYMBOL(idprom); + +static struct idprom idprom_buffer; + +/* Here is the master table of Sun machines which use some implementation + * of the Sparc CPU and have a meaningful IDPROM machtype value that we + * know about. See asm-sparc/machines.h for empirical constants. + */ +static struct Sun_Machine_Models Sun_Machines[NUM_SUN_MACHINES] = { +/* First, Sun3's */ + { .name = "Sun 3/160 Series", .id_machtype = (SM_SUN3 | SM_3_160) }, + { .name = "Sun 3/50", .id_machtype = (SM_SUN3 | SM_3_50) }, + { .name = "Sun 3/260 Series", .id_machtype = (SM_SUN3 | SM_3_260) }, + { .name = "Sun 3/110 Series", .id_machtype = (SM_SUN3 | SM_3_110) }, + { .name = "Sun 3/60", .id_machtype = (SM_SUN3 | SM_3_60) }, + { .name = "Sun 3/E", .id_machtype = (SM_SUN3 | SM_3_E) }, +/* Now, Sun3x's */ + { .name = "Sun 3/460 Series", .id_machtype = (SM_SUN3X | SM_3_460) }, + { .name = "Sun 3/80", .id_machtype = (SM_SUN3X | SM_3_80) }, +/* Then, Sun4's */ +// { .name = "Sun 4/100 Series", .id_machtype = (SM_SUN4 | SM_4_110) }, +// { .name = "Sun 4/200 Series", .id_machtype = (SM_SUN4 | SM_4_260) }, +// { .name = "Sun 4/300 Series", .id_machtype = (SM_SUN4 | SM_4_330) }, +// { .name = "Sun 4/400 Series", .id_machtype = (SM_SUN4 | SM_4_470) }, +/* And now, Sun4c's */ +// { .name = "Sun4c SparcStation 1", .id_machtype = (SM_SUN4C | SM_4C_SS1) }, +// { .name = "Sun4c SparcStation IPC", .id_machtype = (SM_SUN4C | SM_4C_IPC) }, +// { .name = "Sun4c SparcStation 1+", .id_machtype = (SM_SUN4C | SM_4C_SS1PLUS) }, +// { .name = "Sun4c SparcStation SLC", .id_machtype = (SM_SUN4C | SM_4C_SLC) }, +// { .name = "Sun4c SparcStation 2", .id_machtype = (SM_SUN4C | SM_4C_SS2) }, +// { .name = "Sun4c SparcStation ELC", .id_machtype = (SM_SUN4C | SM_4C_ELC) }, +// { .name = "Sun4c SparcStation IPX", .id_machtype = (SM_SUN4C | SM_4C_IPX) }, +/* Finally, early Sun4m's */ +// { .name = "Sun4m SparcSystem600", .id_machtype = (SM_SUN4M | SM_4M_SS60) }, +// { .name = "Sun4m SparcStation10/20", .id_machtype = (SM_SUN4M | SM_4M_SS50) }, +// { .name = "Sun4m SparcStation5", .id_machtype = (SM_SUN4M | SM_4M_SS40) }, +/* One entry for the OBP arch's which are sun4d, sun4e, and newer sun4m's */ +// { .name = "Sun4M OBP based system", .id_machtype = (SM_SUN4M_OBP | 0x0) } +}; + +static void __init display_system_type(unsigned char machtype) +{ + register int i; + + for (i = 0; i < NUM_SUN_MACHINES; i++) { + if(Sun_Machines[i].id_machtype == machtype) { + if (machtype != (SM_SUN4M_OBP | 0x00)) + pr_info("TYPE: %s\n", Sun_Machines[i].name); + else { +#if 0 + char sysname[128]; + + prom_getproperty(prom_root_node, "banner-name", + sysname, sizeof(sysname)); + pr_info("TYPE: %s\n", sysname); +#endif + } + return; + } + } + + prom_printf("IDPROM: Bogus id_machtype value, 0x%x\n", machtype); + prom_halt(); +} + +void sun3_get_model(unsigned char* model) +{ + register int i; + + for (i = 0; i < NUM_SUN_MACHINES; i++) { + if(Sun_Machines[i].id_machtype == idprom->id_machtype) { + strcpy(model, Sun_Machines[i].name); + return; + } + } +} + + + +/* Calculate the IDPROM checksum (xor of the data bytes). */ +static unsigned char __init calc_idprom_cksum(struct idprom *idprom) +{ + unsigned char cksum, i, *ptr = (unsigned char *)idprom; + + for (i = cksum = 0; i <= 0x0E; i++) + cksum ^= *ptr++; + + return cksum; +} + +/* Create a local IDPROM copy, verify integrity, and display information. */ +void __init idprom_init(void) +{ + prom_get_idprom((char *) &idprom_buffer, sizeof(idprom_buffer)); + + idprom = &idprom_buffer; + + if (idprom->id_format != 0x01) { + prom_printf("IDPROM: Unknown format type!\n"); + prom_halt(); + } + + if (idprom->id_cksum != calc_idprom_cksum(idprom)) { + prom_printf("IDPROM: Checksum failure (nvram=%x, calc=%x)!\n", + idprom->id_cksum, calc_idprom_cksum(idprom)); + prom_halt(); + } + + display_system_type(idprom->id_machtype); + + pr_info("Ethernet address: %pM\n", idprom->id_ethaddr); +} diff --git a/arch/m68k/sun3/intersil.c b/arch/m68k/sun3/intersil.c new file mode 100644 index 000000000..d911070af --- /dev/null +++ b/arch/m68k/sun3/intersil.c @@ -0,0 +1,76 @@ +/* + * arch/m68k/sun3/intersil.c + * + * basic routines for accessing the intersil clock within the sun3 machines + * + * started 11/12/1999 Sam Creasey + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/kernel.h> +#include <linux/rtc.h> + +#include <asm/errno.h> +#include <asm/intersil.h> +#include <asm/machdep.h> + + +/* bits to set for start/run of the intersil */ +#define STOP_VAL (INTERSIL_STOP | INTERSIL_INT_ENABLE | INTERSIL_24H_MODE) +#define START_VAL (INTERSIL_RUN | INTERSIL_INT_ENABLE | INTERSIL_24H_MODE) + +/* does this need to be implemented? */ +u32 sun3_gettimeoffset(void) +{ + return 1000; +} + + +/* get/set hwclock */ + +int sun3_hwclk(int set, struct rtc_time *t) +{ + volatile struct intersil_dt *todintersil; + unsigned long flags; + + todintersil = (struct intersil_dt *) &intersil_clock->counter; + + local_irq_save(flags); + + intersil_clock->cmd_reg = STOP_VAL; + + /* set or read the clock */ + if(set) { + todintersil->csec = 0; + todintersil->hour = t->tm_hour; + todintersil->minute = t->tm_min; + todintersil->second = t->tm_sec; + todintersil->month = t->tm_mon + 1; + todintersil->day = t->tm_mday; + todintersil->year = (t->tm_year - 68) % 100; + todintersil->weekday = t->tm_wday; + } else { + /* read clock */ + t->tm_sec = todintersil->csec; + t->tm_hour = todintersil->hour; + t->tm_min = todintersil->minute; + t->tm_sec = todintersil->second; + t->tm_mon = todintersil->month - 1; + t->tm_mday = todintersil->day; + t->tm_year = todintersil->year + 68; + t->tm_wday = todintersil->weekday; + if (t->tm_year < 70) + t->tm_year += 100; + } + + intersil_clock->cmd_reg = START_VAL; + + local_irq_restore(flags); + + return 0; + +} + diff --git a/arch/m68k/sun3/leds.c b/arch/m68k/sun3/leds.c new file mode 100644 index 000000000..7c67b58eb --- /dev/null +++ b/arch/m68k/sun3/leds.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <asm/contregs.h> +#include <asm/sun3mmu.h> +#include <asm/io.h> + +void sun3_leds(unsigned char byte) +{ + unsigned char dfc; + + GET_DFC(dfc); + SET_DFC(FC_CONTROL); + SET_CONTROL_BYTE(AC_LEDS, byte); + SET_DFC(dfc); +} diff --git a/arch/m68k/sun3/mmu_emu.c b/arch/m68k/sun3/mmu_emu.c new file mode 100644 index 000000000..d30da12a1 --- /dev/null +++ b/arch/m68k/sun3/mmu_emu.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +** Tablewalk MMU emulator +** +** by Toshiyasu Morita +** +** Started 1/16/98 @ 2:22 am +*/ + +#include <linux/init.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/delay.h> +#include <linux/bootmem.h> +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/sched/mm.h> + +#include <asm/setup.h> +#include <asm/traps.h> +#include <linux/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/sun3mmu.h> +#include <asm/segment.h> +#include <asm/oplib.h> +#include <asm/mmu_context.h> +#include <asm/dvma.h> + + +#undef DEBUG_MMU_EMU +#define DEBUG_PROM_MAPS + +/* +** Defines +*/ + +#define CONTEXTS_NUM 8 +#define SEGMAPS_PER_CONTEXT_NUM 2048 +#define PAGES_PER_SEGMENT 16 +#define PMEGS_NUM 256 +#define PMEG_MASK 0xFF + +/* +** Globals +*/ + +unsigned long m68k_vmalloc_end; +EXPORT_SYMBOL(m68k_vmalloc_end); + +unsigned long pmeg_vaddr[PMEGS_NUM]; +unsigned char pmeg_alloc[PMEGS_NUM]; +unsigned char pmeg_ctx[PMEGS_NUM]; + +/* pointers to the mm structs for each task in each + context. 0xffffffff is a marker for kernel context */ +static struct mm_struct *ctx_alloc[CONTEXTS_NUM] = { + [0] = (struct mm_struct *)0xffffffff +}; + +/* has this context been mmdrop'd? */ +static unsigned char ctx_avail = CONTEXTS_NUM-1; + +/* array of pages to be marked off for the rom when we do mem_init later */ +/* 256 pages lets the rom take up to 2mb of physical ram.. I really + hope it never wants mote than that. */ +unsigned long rom_pages[256]; + +/* Print a PTE value in symbolic form. For debugging. */ +void print_pte (pte_t pte) +{ +#if 0 + /* Verbose version. */ + unsigned long val = pte_val (pte); + pr_cont(" pte=%lx [addr=%lx", + val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT); + if (val & SUN3_PAGE_VALID) pr_cont(" valid"); + if (val & SUN3_PAGE_WRITEABLE) pr_cont(" write"); + if (val & SUN3_PAGE_SYSTEM) pr_cont(" sys"); + if (val & SUN3_PAGE_NOCACHE) pr_cont(" nocache"); + if (val & SUN3_PAGE_ACCESSED) pr_cont(" accessed"); + if (val & SUN3_PAGE_MODIFIED) pr_cont(" modified"); + switch (val & SUN3_PAGE_TYPE_MASK) { + case SUN3_PAGE_TYPE_MEMORY: pr_cont(" memory"); break; + case SUN3_PAGE_TYPE_IO: pr_cont(" io"); break; + case SUN3_PAGE_TYPE_VME16: pr_cont(" vme16"); break; + case SUN3_PAGE_TYPE_VME32: pr_cont(" vme32"); break; + } + pr_cont("]\n"); +#else + /* Terse version. More likely to fit on a line. */ + unsigned long val = pte_val (pte); + char flags[7], *type; + + flags[0] = (val & SUN3_PAGE_VALID) ? 'v' : '-'; + flags[1] = (val & SUN3_PAGE_WRITEABLE) ? 'w' : '-'; + flags[2] = (val & SUN3_PAGE_SYSTEM) ? 's' : '-'; + flags[3] = (val & SUN3_PAGE_NOCACHE) ? 'x' : '-'; + flags[4] = (val & SUN3_PAGE_ACCESSED) ? 'a' : '-'; + flags[5] = (val & SUN3_PAGE_MODIFIED) ? 'm' : '-'; + flags[6] = '\0'; + + switch (val & SUN3_PAGE_TYPE_MASK) { + case SUN3_PAGE_TYPE_MEMORY: type = "memory"; break; + case SUN3_PAGE_TYPE_IO: type = "io" ; break; + case SUN3_PAGE_TYPE_VME16: type = "vme16" ; break; + case SUN3_PAGE_TYPE_VME32: type = "vme32" ; break; + default: type = "unknown?"; break; + } + + pr_cont(" pte=%08lx [%07lx %s %s]\n", + val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT, flags, type); +#endif +} + +/* Print the PTE value for a given virtual address. For debugging. */ +void print_pte_vaddr (unsigned long vaddr) +{ + pr_cont(" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr)); + print_pte (__pte (sun3_get_pte (vaddr))); +} + +/* + * Initialise the MMU emulator. + */ +void __init mmu_emu_init(unsigned long bootmem_end) +{ + unsigned long seg, num; + int i,j; + + memset(rom_pages, 0, sizeof(rom_pages)); + memset(pmeg_vaddr, 0, sizeof(pmeg_vaddr)); + memset(pmeg_alloc, 0, sizeof(pmeg_alloc)); + memset(pmeg_ctx, 0, sizeof(pmeg_ctx)); + + /* pmeg align the end of bootmem, adding another pmeg, + * later bootmem allocations will likely need it */ + bootmem_end = (bootmem_end + (2 * SUN3_PMEG_SIZE)) & ~SUN3_PMEG_MASK; + + /* mark all of the pmegs used thus far as reserved */ + for (i=0; i < __pa(bootmem_end) / SUN3_PMEG_SIZE ; ++i) + pmeg_alloc[i] = 2; + + + /* I'm thinking that most of the top pmeg's are going to be + used for something, and we probably shouldn't risk it */ + for(num = 0xf0; num <= 0xff; num++) + pmeg_alloc[num] = 2; + + /* liberate all existing mappings in the rest of kernel space */ + for(seg = bootmem_end; seg < 0x0f800000; seg += SUN3_PMEG_SIZE) { + i = sun3_get_segmap(seg); + + if(!pmeg_alloc[i]) { +#ifdef DEBUG_MMU_EMU + pr_info("freed:"); + print_pte_vaddr (seg); +#endif + sun3_put_segmap(seg, SUN3_INVALID_PMEG); + } + } + + j = 0; + for (num=0, seg=0x0F800000; seg<0x10000000; seg+=16*PAGE_SIZE) { + if (sun3_get_segmap (seg) != SUN3_INVALID_PMEG) { +#ifdef DEBUG_PROM_MAPS + for(i = 0; i < 16; i++) { + pr_info("mapped:"); + print_pte_vaddr (seg + (i*PAGE_SIZE)); + break; + } +#endif + // the lowest mapping here is the end of our + // vmalloc region + if (!m68k_vmalloc_end) + m68k_vmalloc_end = seg; + + // mark the segmap alloc'd, and reserve any + // of the first 0xbff pages the hardware is + // already using... does any sun3 support > 24mb? + pmeg_alloc[sun3_get_segmap(seg)] = 2; + } + } + + dvma_init(); + + + /* blank everything below the kernel, and we've got the base + mapping to start all the contexts off with... */ + for(seg = 0; seg < PAGE_OFFSET; seg += SUN3_PMEG_SIZE) + sun3_put_segmap(seg, SUN3_INVALID_PMEG); + + set_fs(MAKE_MM_SEG(3)); + for(seg = 0; seg < 0x10000000; seg += SUN3_PMEG_SIZE) { + i = sun3_get_segmap(seg); + for(j = 1; j < CONTEXTS_NUM; j++) + (*(romvec->pv_setctxt))(j, (void *)seg, i); + } + set_fs(KERNEL_DS); + +} + +/* erase the mappings for a dead context. Uses the pg_dir for hints + as the pmeg tables proved somewhat unreliable, and unmapping all of + TASK_SIZE was much slower and no more stable. */ +/* todo: find a better way to keep track of the pmegs used by a + context for when they're cleared */ +void clear_context(unsigned long context) +{ + unsigned char oldctx; + unsigned long i; + + if(context) { + if(!ctx_alloc[context]) + panic("clear_context: context not allocated\n"); + + ctx_alloc[context]->context = SUN3_INVALID_CONTEXT; + ctx_alloc[context] = (struct mm_struct *)0; + ctx_avail++; + } + + oldctx = sun3_get_context(); + + sun3_put_context(context); + + for(i = 0; i < SUN3_INVALID_PMEG; i++) { + if((pmeg_ctx[i] == context) && (pmeg_alloc[i] == 1)) { + sun3_put_segmap(pmeg_vaddr[i], SUN3_INVALID_PMEG); + pmeg_ctx[i] = 0; + pmeg_alloc[i] = 0; + pmeg_vaddr[i] = 0; + } + } + + sun3_put_context(oldctx); +} + +/* gets an empty context. if full, kills the next context listed to + die first */ +/* This context invalidation scheme is, well, totally arbitrary, I'm + sure it could be much more intelligent... but it gets the job done + for now without much overhead in making it's decision. */ +/* todo: come up with optimized scheme for flushing contexts */ +unsigned long get_free_context(struct mm_struct *mm) +{ + unsigned long new = 1; + static unsigned char next_to_die = 1; + + if(!ctx_avail) { + /* kill someone to get our context */ + new = next_to_die; + clear_context(new); + next_to_die = (next_to_die + 1) & 0x7; + if(!next_to_die) + next_to_die++; + } else { + while(new < CONTEXTS_NUM) { + if(ctx_alloc[new]) + new++; + else + break; + } + // check to make sure one was really free... + if(new == CONTEXTS_NUM) + panic("get_free_context: failed to find free context"); + } + + ctx_alloc[new] = mm; + ctx_avail--; + + return new; +} + +/* + * Dynamically select a `spare' PMEG and use it to map virtual `vaddr' in + * `context'. Maintain internal PMEG management structures. This doesn't + * actually map the physical address, but does clear the old mappings. + */ +//todo: better allocation scheme? but is extra complexity worthwhile? +//todo: only clear old entries if necessary? how to tell? + +inline void mmu_emu_map_pmeg (int context, int vaddr) +{ + static unsigned char curr_pmeg = 128; + int i; + + /* Round address to PMEG boundary. */ + vaddr &= ~SUN3_PMEG_MASK; + + /* Find a spare one. */ + while (pmeg_alloc[curr_pmeg] == 2) + ++curr_pmeg; + + +#ifdef DEBUG_MMU_EMU + pr_info("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n", + curr_pmeg, context, vaddr); +#endif + + /* Invalidate old mapping for the pmeg, if any */ + if (pmeg_alloc[curr_pmeg] == 1) { + sun3_put_context(pmeg_ctx[curr_pmeg]); + sun3_put_segmap (pmeg_vaddr[curr_pmeg], SUN3_INVALID_PMEG); + sun3_put_context(context); + } + + /* Update PMEG management structures. */ + // don't take pmeg's away from the kernel... + if(vaddr >= PAGE_OFFSET) { + /* map kernel pmegs into all contexts */ + unsigned char i; + + for(i = 0; i < CONTEXTS_NUM; i++) { + sun3_put_context(i); + sun3_put_segmap (vaddr, curr_pmeg); + } + sun3_put_context(context); + pmeg_alloc[curr_pmeg] = 2; + pmeg_ctx[curr_pmeg] = 0; + + } + else { + pmeg_alloc[curr_pmeg] = 1; + pmeg_ctx[curr_pmeg] = context; + sun3_put_segmap (vaddr, curr_pmeg); + + } + pmeg_vaddr[curr_pmeg] = vaddr; + + /* Set hardware mapping and clear the old PTE entries. */ + for (i=0; i<SUN3_PMEG_SIZE; i+=SUN3_PTE_SIZE) + sun3_put_pte (vaddr + i, SUN3_PAGE_SYSTEM); + + /* Consider a different one next time. */ + ++curr_pmeg; +} + +/* + * Handle a pagefault at virtual address `vaddr'; check if there should be a + * page there (specifically, whether the software pagetables indicate that + * there is). This is necessary due to the limited size of the second-level + * Sun3 hardware pagetables (256 groups of 16 pages). If there should be a + * mapping present, we select a `spare' PMEG and use it to create a mapping. + * `read_flag' is nonzero for a read fault; zero for a write. Returns nonzero + * if we successfully handled the fault. + */ +//todo: should we bump minor pagefault counter? if so, here or in caller? +//todo: possibly inline this into bus_error030 in <asm/buserror.h> ? + +// kernel_fault is set when a kernel page couldn't be demand mapped, +// and forces another try using the kernel page table. basically a +// hack so that vmalloc would work correctly. + +int mmu_emu_handle_fault (unsigned long vaddr, int read_flag, int kernel_fault) +{ + unsigned long segment, offset; + unsigned char context; + pte_t *pte; + pgd_t * crp; + + if(current->mm == NULL) { + crp = swapper_pg_dir; + context = 0; + } else { + context = current->mm->context; + if(kernel_fault) + crp = swapper_pg_dir; + else + crp = current->mm->pgd; + } + +#ifdef DEBUG_MMU_EMU + pr_info("mmu_emu_handle_fault: vaddr=%lx type=%s crp=%p\n", + vaddr, read_flag ? "read" : "write", crp); +#endif + + segment = (vaddr >> SUN3_PMEG_SIZE_BITS) & 0x7FF; + offset = (vaddr >> SUN3_PTE_SIZE_BITS) & 0xF; + +#ifdef DEBUG_MMU_EMU + pr_info("mmu_emu_handle_fault: segment=%lx offset=%lx\n", segment, + offset); +#endif + + pte = (pte_t *) pgd_val (*(crp + segment)); + +//todo: next line should check for valid pmd properly. + if (!pte) { +// pr_info("mmu_emu_handle_fault: invalid pmd\n"); + return 0; + } + + pte = (pte_t *) __va ((unsigned long)(pte + offset)); + + /* Make sure this is a valid page */ + if (!(pte_val (*pte) & SUN3_PAGE_VALID)) + return 0; + + /* Make sure there's a pmeg allocated for the page */ + if (sun3_get_segmap (vaddr&~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG) + mmu_emu_map_pmeg (context, vaddr); + + /* Write the pte value to hardware MMU */ + sun3_put_pte (vaddr&PAGE_MASK, pte_val (*pte)); + + /* Update software copy of the pte value */ +// I'm not sure this is necessary. If this is required, we ought to simply +// copy this out when we reuse the PMEG or at some other convenient time. +// Doing it here is fairly meaningless, anyway, as we only know about the +// first access to a given page. --m + if (!read_flag) { + if (pte_val (*pte) & SUN3_PAGE_WRITEABLE) + pte_val (*pte) |= (SUN3_PAGE_ACCESSED + | SUN3_PAGE_MODIFIED); + else + return 0; /* Write-protect error. */ + } else + pte_val (*pte) |= SUN3_PAGE_ACCESSED; + +#ifdef DEBUG_MMU_EMU + pr_info("seg:%ld crp:%p ->", get_fs().seg, crp); + print_pte_vaddr (vaddr); + pr_cont("\n"); +#endif + + return 1; +} diff --git a/arch/m68k/sun3/prom/Makefile b/arch/m68k/sun3/prom/Makefile new file mode 100644 index 000000000..da7eac06b --- /dev/null +++ b/arch/m68k/sun3/prom/Makefile @@ -0,0 +1,6 @@ +# Makefile for the Sun Boot PROM interface library under +# Linux. +# + +obj-y := init.o console.o printf.o misc.o +#bootstr.o init.o misc.o segment.o console.o printf.o diff --git a/arch/m68k/sun3/prom/console.c b/arch/m68k/sun3/prom/console.c new file mode 100644 index 000000000..eb4f6c903 --- /dev/null +++ b/arch/m68k/sun3/prom/console.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * console.c: Routines that deal with sending and receiving IO + * to/from the current console device using the PROM. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/openprom.h> +#include <asm/oplib.h> +#include <linux/string.h> + +/* Non blocking get character from console input device, returns -1 + * if no input was taken. This can be used for polling. + */ +int +prom_nbgetchar(void) +{ + int i = -1; + unsigned long flags; + + local_irq_save(flags); + i = (*(romvec->pv_nbgetchar))(); + local_irq_restore(flags); + return i; /* Ugh, we could spin forever on unsupported proms ;( */ +} + +/* Non blocking put character to console device, returns -1 if + * unsuccessful. + */ +int +prom_nbputchar(char c) +{ + unsigned long flags; + int i = -1; + + local_irq_save(flags); + i = (*(romvec->pv_nbputchar))(c); + local_irq_restore(flags); + return i; /* Ugh, we could spin forever on unsupported proms ;( */ +} + +/* Blocking version of get character routine above. */ +char +prom_getchar(void) +{ + int character; + while((character = prom_nbgetchar()) == -1) ; + return (char) character; +} + +/* Blocking version of put character routine above. */ +void +prom_putchar(char c) +{ + while(prom_nbputchar(c) == -1) ; + return; +} + +/* Query for input device type */ +#if 0 +enum prom_input_device +prom_query_input_device() +{ + unsigned long flags; + int st_p; + char propb[64]; + char *p; + + switch(prom_vers) { + case PROM_V0: + case PROM_V2: + default: + switch(*romvec->pv_stdin) { + case PROMDEV_KBD: return PROMDEV_IKBD; + case PROMDEV_TTYA: return PROMDEV_ITTYA; + case PROMDEV_TTYB: return PROMDEV_ITTYB; + default: + return PROMDEV_I_UNK; + }; + case PROM_V3: + case PROM_P1275: + local_irq_save(flags); + st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin); + __asm__ __volatile__("ld [%0], %%g6\n\t" : : + "r" (¤t_set[smp_processor_id()]) : + "memory"); + local_irq_restore(flags); + if(prom_node_has_property(st_p, "keyboard")) + return PROMDEV_IKBD; + prom_getproperty(st_p, "device_type", propb, sizeof(propb)); + if(strncmp(propb, "serial", sizeof("serial"))) + return PROMDEV_I_UNK; + prom_getproperty(prom_root_node, "stdin-path", propb, sizeof(propb)); + p = propb; + while(*p) p++; p -= 2; + if(p[0] == ':') { + if(p[1] == 'a') + return PROMDEV_ITTYA; + else if(p[1] == 'b') + return PROMDEV_ITTYB; + } + return PROMDEV_I_UNK; + }; +} +#endif + +/* Query for output device type */ + +#if 0 +enum prom_output_device +prom_query_output_device() +{ + unsigned long flags; + int st_p; + char propb[64]; + char *p; + int propl; + + switch(prom_vers) { + case PROM_V0: + switch(*romvec->pv_stdin) { + case PROMDEV_SCREEN: return PROMDEV_OSCREEN; + case PROMDEV_TTYA: return PROMDEV_OTTYA; + case PROMDEV_TTYB: return PROMDEV_OTTYB; + }; + break; + case PROM_V2: + case PROM_V3: + case PROM_P1275: + local_irq_save(flags); + st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout); + __asm__ __volatile__("ld [%0], %%g6\n\t" : : + "r" (¤t_set[smp_processor_id()]) : + "memory"); + local_irq_restore(flags); + propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb)); + if (propl >= 0 && propl == sizeof("display") && + strncmp("display", propb, sizeof("display")) == 0) + { + return PROMDEV_OSCREEN; + } + if(prom_vers == PROM_V3) { + if(strncmp("serial", propb, sizeof("serial"))) + return PROMDEV_O_UNK; + prom_getproperty(prom_root_node, "stdout-path", propb, sizeof(propb)); + p = propb; + while(*p) p++; p -= 2; + if(p[0]==':') { + if(p[1] == 'a') + return PROMDEV_OTTYA; + else if(p[1] == 'b') + return PROMDEV_OTTYB; + } + return PROMDEV_O_UNK; + } else { + /* This works on SS-2 (an early OpenFirmware) still. */ + switch(*romvec->pv_stdin) { + case PROMDEV_TTYA: return PROMDEV_OTTYA; + case PROMDEV_TTYB: return PROMDEV_OTTYB; + }; + } + break; + }; + return PROMDEV_O_UNK; +} +#endif diff --git a/arch/m68k/sun3/prom/init.c b/arch/m68k/sun3/prom/init.c new file mode 100644 index 000000000..0c6d99df6 --- /dev/null +++ b/arch/m68k/sun3/prom/init.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * init.c: Initialize internal variables used by the PROM + * library functions. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/openprom.h> +#include <asm/oplib.h> + +struct linux_romvec *romvec; +enum prom_major_version prom_vers; +unsigned int prom_rev, prom_prev; + +/* The root node of the prom device tree. */ +int prom_root_node; + +/* Pointer to the device tree operations structure. */ +struct linux_nodeops *prom_nodeops; + +/* You must call prom_init() before you attempt to use any of the + * routines in the prom library. + * It gets passed the pointer to the PROM vector. + */ + +void __init prom_init(struct linux_romvec *rp) +{ + romvec = rp; + + /* Initialization successful. */ + return; +} diff --git a/arch/m68k/sun3/prom/misc.c b/arch/m68k/sun3/prom/misc.c new file mode 100644 index 000000000..b8c95940d --- /dev/null +++ b/arch/m68k/sun3/prom/misc.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * misc.c: Miscellaneous prom functions that don't belong + * anywhere else. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/sun3-head.h> +#include <asm/idprom.h> +#include <asm/openprom.h> +#include <asm/oplib.h> +#include <asm/movs.h> + +/* Reset and reboot the machine with the command 'bcommand'. */ +void +prom_reboot(char *bcommand) +{ + unsigned long flags; + local_irq_save(flags); + (*(romvec->pv_reboot))(bcommand); + local_irq_restore(flags); +} + +/* Drop into the prom, with the chance to continue with the 'go' + * prom command. + */ +void +prom_cmdline(void) +{ +} + +/* Drop into the prom, but completely terminate the program. + * No chance of continuing. + */ +void +prom_halt(void) +{ + unsigned long flags; +again: + local_irq_save(flags); + (*(romvec->pv_halt))(); + local_irq_restore(flags); + goto again; /* PROM is out to get me -DaveM */ +} + +typedef void (*sfunc_t)(void); + +/* Get the idprom and stuff it into buffer 'idbuf'. Returns the + * format type. 'num_bytes' is the number of bytes that your idbuf + * has space for. Returns 0xff on error. + */ +unsigned char +prom_get_idprom(char *idbuf, int num_bytes) +{ + int i, oldsfc; + GET_SFC(oldsfc); + SET_SFC(FC_CONTROL); + for(i=0;i<num_bytes; i++) + { + /* There is a problem with the GET_CONTROL_BYTE + macro; defining the extra variable + gets around it. + */ + int c; + GET_CONTROL_BYTE(SUN3_IDPROM_BASE + i, c); + idbuf[i] = c; + } + SET_SFC(oldsfc); + return idbuf[0]; +} + +/* Get the major prom version number. */ +int +prom_version(void) +{ + return romvec->pv_romvers; +} + +/* Get the prom plugin-revision. */ +int +prom_getrev(void) +{ + return prom_rev; +} + +/* Get the prom firmware print revision. */ +int +prom_getprev(void) +{ + return prom_prev; +} diff --git a/arch/m68k/sun3/prom/printf.c b/arch/m68k/sun3/prom/printf.c new file mode 100644 index 000000000..b6724cc66 --- /dev/null +++ b/arch/m68k/sun3/prom/printf.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * printf.c: Internal prom library printf facility. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +/* This routine is internal to the prom library, no one else should know + * about or use it! It's simple and smelly anyway.... + */ + +#include <linux/kernel.h> + +#include <asm/openprom.h> +#include <asm/oplib.h> + +#ifdef CONFIG_KGDB +extern int kgdb_initialized; +#endif + +static char ppbuf[1024]; + +void +prom_printf(char *fmt, ...) +{ + va_list args; + char ch, *bptr; + int i; + + va_start(args, fmt); + +#ifdef CONFIG_KGDB + ppbuf[0] = 'O'; + i = vsprintf(ppbuf + 1, fmt, args) + 1; +#else + i = vsprintf(ppbuf, fmt, args); +#endif + + bptr = ppbuf; + +#ifdef CONFIG_KGDB + if (kgdb_initialized) { + pr_info("kgdb_initialized = %d\n", kgdb_initialized); + putpacket(bptr, 1); + } else +#else + while((ch = *(bptr++)) != 0) { + if(ch == '\n') + prom_putchar('\r'); + + prom_putchar(ch); + } +#endif + va_end(args); + return; +} diff --git a/arch/m68k/sun3/sun3dvma.c b/arch/m68k/sun3/sun3dvma.c new file mode 100644 index 000000000..8546922ad --- /dev/null +++ b/arch/m68k/sun3/sun3dvma.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/m68k/sun3/sun3dvma.c + * + * Copyright (C) 2000 Sam Creasey + * + * Contains common routines for sun3/sun3x DVMA management. + */ + +#include <linux/bootmem.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/mm.h> +#include <linux/list.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/dvma.h> + +#undef DVMA_DEBUG + +#ifdef CONFIG_SUN3X +extern void dvma_unmap_iommu(unsigned long baddr, int len); +#else +static inline void dvma_unmap_iommu(unsigned long a, int b) +{ +} +#endif + +#ifdef CONFIG_SUN3 +extern void sun3_dvma_init(void); +#endif + +static unsigned long *iommu_use; + +#define dvma_index(baddr) ((baddr - DVMA_START) >> DVMA_PAGE_SHIFT) + +#define dvma_entry_use(baddr) (iommu_use[dvma_index(baddr)]) + +struct hole { + unsigned long start; + unsigned long end; + unsigned long size; + struct list_head list; +}; + +static struct list_head hole_list; +static struct list_head hole_cache; +static struct hole initholes[64]; + +#ifdef DVMA_DEBUG + +static unsigned long dvma_allocs; +static unsigned long dvma_frees; +static unsigned long long dvma_alloc_bytes; +static unsigned long long dvma_free_bytes; + +static void print_use(void) +{ + + int i; + int j = 0; + + pr_info("dvma entry usage:\n"); + + for(i = 0; i < IOMMU_TOTAL_ENTRIES; i++) { + if(!iommu_use[i]) + continue; + + j++; + + pr_info("dvma entry: %08x len %08lx\n", + (i << DVMA_PAGE_SHIFT) + DVMA_START, iommu_use[i]); + } + + pr_info("%d entries in use total\n", j); + + pr_info("allocation/free calls: %lu/%lu\n", dvma_allocs, dvma_frees); + pr_info("allocation/free bytes: %Lx/%Lx\n", dvma_alloc_bytes, + dvma_free_bytes); +} + +static void print_holes(struct list_head *holes) +{ + + struct list_head *cur; + struct hole *hole; + + pr_info("listing dvma holes\n"); + list_for_each(cur, holes) { + hole = list_entry(cur, struct hole, list); + + if((hole->start == 0) && (hole->end == 0) && (hole->size == 0)) + continue; + + pr_info("hole: start %08lx end %08lx size %08lx\n", + hole->start, hole->end, hole->size); + } + + pr_info("end of hole listing...\n"); +} +#endif /* DVMA_DEBUG */ + +static inline int refill(void) +{ + + struct hole *hole; + struct hole *prev = NULL; + struct list_head *cur; + int ret = 0; + + list_for_each(cur, &hole_list) { + hole = list_entry(cur, struct hole, list); + + if(!prev) { + prev = hole; + continue; + } + + if(hole->end == prev->start) { + hole->size += prev->size; + hole->end = prev->end; + list_move(&(prev->list), &hole_cache); + ret++; + } + + } + + return ret; +} + +static inline struct hole *rmcache(void) +{ + struct hole *ret; + + if(list_empty(&hole_cache)) { + if(!refill()) { + pr_crit("out of dvma hole cache!\n"); + BUG(); + } + } + + ret = list_entry(hole_cache.next, struct hole, list); + list_del(&(ret->list)); + + return ret; + +} + +static inline unsigned long get_baddr(int len, unsigned long align) +{ + + struct list_head *cur; + struct hole *hole; + + if(list_empty(&hole_list)) { +#ifdef DVMA_DEBUG + pr_crit("out of dvma holes! (printing hole cache)\n"); + print_holes(&hole_cache); + print_use(); +#endif + BUG(); + } + + list_for_each(cur, &hole_list) { + unsigned long newlen; + + hole = list_entry(cur, struct hole, list); + + if(align > DVMA_PAGE_SIZE) + newlen = len + ((hole->end - len) & (align-1)); + else + newlen = len; + + if(hole->size > newlen) { + hole->end -= newlen; + hole->size -= newlen; + dvma_entry_use(hole->end) = newlen; +#ifdef DVMA_DEBUG + dvma_allocs++; + dvma_alloc_bytes += newlen; +#endif + return hole->end; + } else if(hole->size == newlen) { + list_move(&(hole->list), &hole_cache); + dvma_entry_use(hole->start) = newlen; +#ifdef DVMA_DEBUG + dvma_allocs++; + dvma_alloc_bytes += newlen; +#endif + return hole->start; + } + + } + + pr_crit("unable to find dvma hole!\n"); + BUG(); + return 0; +} + +static inline int free_baddr(unsigned long baddr) +{ + + unsigned long len; + struct hole *hole; + struct list_head *cur; + unsigned long orig_baddr; + + orig_baddr = baddr; + len = dvma_entry_use(baddr); + dvma_entry_use(baddr) = 0; + baddr &= DVMA_PAGE_MASK; + dvma_unmap_iommu(baddr, len); + +#ifdef DVMA_DEBUG + dvma_frees++; + dvma_free_bytes += len; +#endif + + list_for_each(cur, &hole_list) { + hole = list_entry(cur, struct hole, list); + + if(hole->end == baddr) { + hole->end += len; + hole->size += len; + return 0; + } else if(hole->start == (baddr + len)) { + hole->start = baddr; + hole->size += len; + return 0; + } + + } + + hole = rmcache(); + + hole->start = baddr; + hole->end = baddr + len; + hole->size = len; + +// list_add_tail(&(hole->list), cur); + list_add(&(hole->list), cur); + + return 0; + +} + +void __init dvma_init(void) +{ + + struct hole *hole; + int i; + + INIT_LIST_HEAD(&hole_list); + INIT_LIST_HEAD(&hole_cache); + + /* prepare the hole cache */ + for(i = 0; i < 64; i++) + list_add(&(initholes[i].list), &hole_cache); + + hole = rmcache(); + hole->start = DVMA_START; + hole->end = DVMA_END; + hole->size = DVMA_SIZE; + + list_add(&(hole->list), &hole_list); + + iommu_use = alloc_bootmem(IOMMU_TOTAL_ENTRIES * sizeof(unsigned long)); + + dvma_unmap_iommu(DVMA_START, DVMA_SIZE); + +#ifdef CONFIG_SUN3 + sun3_dvma_init(); +#endif + +} + +unsigned long dvma_map_align(unsigned long kaddr, int len, int align) +{ + + unsigned long baddr; + unsigned long off; + + if(!len) + len = 0x800; + + if(!kaddr || !len) { +// pr_err("error: kaddr %lx len %x\n", kaddr, len); +// *(int *)4 = 0; + return 0; + } + + pr_debug("dvma_map request %08x bytes from %08lx\n", len, kaddr); + off = kaddr & ~DVMA_PAGE_MASK; + kaddr &= PAGE_MASK; + len += off; + len = ((len + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK); + + if(align == 0) + align = DVMA_PAGE_SIZE; + else + align = ((align + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK); + + baddr = get_baddr(len, align); +// pr_info("using baddr %lx\n", baddr); + + if(!dvma_map_iommu(kaddr, baddr, len)) + return (baddr + off); + + pr_crit("dvma_map failed kaddr %lx baddr %lx len %x\n", kaddr, baddr, + len); + BUG(); + return 0; +} +EXPORT_SYMBOL(dvma_map_align); + +void dvma_unmap(void *baddr) +{ + unsigned long addr; + + addr = (unsigned long)baddr; + /* check if this is a vme mapping */ + if(!(addr & 0x00f00000)) + addr |= 0xf00000; + + free_baddr(addr); + + return; + +} +EXPORT_SYMBOL(dvma_unmap); + +void *dvma_malloc_align(unsigned long len, unsigned long align) +{ + unsigned long kaddr; + unsigned long baddr; + unsigned long vaddr; + + if(!len) + return NULL; + + pr_debug("dvma_malloc request %lx bytes\n", len); + len = ((len + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK); + + if((kaddr = __get_free_pages(GFP_ATOMIC, get_order(len))) == 0) + return NULL; + + if((baddr = (unsigned long)dvma_map_align(kaddr, len, align)) == 0) { + free_pages(kaddr, get_order(len)); + return NULL; + } + + vaddr = dvma_btov(baddr); + + if(dvma_map_cpu(kaddr, vaddr, len) < 0) { + dvma_unmap((void *)baddr); + free_pages(kaddr, get_order(len)); + return NULL; + } + + pr_debug("mapped %08lx bytes %08lx kern -> %08lx bus\n", len, kaddr, + baddr); + + return (void *)vaddr; + +} +EXPORT_SYMBOL(dvma_malloc_align); + +void dvma_free(void *vaddr) +{ + + return; + +} +EXPORT_SYMBOL(dvma_free); diff --git a/arch/m68k/sun3/sun3ints.c b/arch/m68k/sun3/sun3ints.c new file mode 100644 index 000000000..a5824abb4 --- /dev/null +++ b/arch/m68k/sun3/sun3ints.c @@ -0,0 +1,102 @@ + /* + * linux/arch/m68k/sun3/sun3ints.c -- Sun-3(x) Linux interrupt handling code + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> +#include <asm/segment.h> +#include <asm/intersil.h> +#include <asm/oplib.h> +#include <asm/sun3ints.h> +#include <asm/irq_regs.h> +#include <linux/seq_file.h> + +extern void sun3_leds (unsigned char); + +void sun3_disable_interrupts(void) +{ + sun3_disable_irq(0); +} + +void sun3_enable_interrupts(void) +{ + sun3_enable_irq(0); +} + +static int led_pattern[8] = { + ~(0x80), ~(0x01), + ~(0x40), ~(0x02), + ~(0x20), ~(0x04), + ~(0x10), ~(0x08) +}; + +volatile unsigned char* sun3_intreg; + +void sun3_enable_irq(unsigned int irq) +{ + *sun3_intreg |= (1 << irq); +} + +void sun3_disable_irq(unsigned int irq) +{ + *sun3_intreg &= ~(1 << irq); +} + +static irqreturn_t sun3_int7(int irq, void *dev_id) +{ + unsigned int cnt; + + cnt = kstat_irqs_cpu(irq, 0); + if (!(cnt % 2000)) + sun3_leds(led_pattern[cnt % 16000 / 2000]); + return IRQ_HANDLED; +} + +static irqreturn_t sun3_int5(int irq, void *dev_id) +{ + unsigned long flags; + unsigned int cnt; + + local_irq_save(flags); +#ifdef CONFIG_SUN3 + intersil_clear(); +#endif + sun3_disable_irq(5); + sun3_enable_irq(5); +#ifdef CONFIG_SUN3 + intersil_clear(); +#endif + xtime_update(1); + update_process_times(user_mode(get_irq_regs())); + cnt = kstat_irqs_cpu(irq, 0); + if (!(cnt % 20)) + sun3_leds(led_pattern[cnt % 160 / 20]); + local_irq_restore(flags); + return IRQ_HANDLED; +} + +static irqreturn_t sun3_vec255(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +void __init sun3_init_IRQ(void) +{ + *sun3_intreg = 1; + + m68k_setup_user_interrupt(VEC_USER, 128); + + if (request_irq(IRQ_AUTO_5, sun3_int5, 0, "clock", NULL)) + pr_err("Couldn't register %s interrupt\n", "int5"); + if (request_irq(IRQ_AUTO_7, sun3_int7, 0, "nmi", NULL)) + pr_err("Couldn't register %s interrupt\n", "int7"); + if (request_irq(IRQ_USER+127, sun3_vec255, 0, "vec255", NULL)) + pr_err("Couldn't register %s interrupt\n", "vec255"); +} diff --git a/arch/m68k/sun3x/Makefile b/arch/m68k/sun3x/Makefile new file mode 100644 index 000000000..be5776d9a --- /dev/null +++ b/arch/m68k/sun3x/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for Linux arch/m68k/sun3x source directory +# + +obj-y := config.o time.o dvma.o prom.o diff --git a/arch/m68k/sun3x/config.c b/arch/m68k/sun3x/config.c new file mode 100644 index 000000000..33d3a1c6f --- /dev/null +++ b/arch/m68k/sun3x/config.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Setup kernel for a Sun3x machine + * + * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + * + * based on code from Oliver Jowett <oliver@jowett.manawatu.gen.nz> + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/seq_file.h> +#include <linux/console.h> +#include <linux/init.h> + +#include <asm/machdep.h> +#include <asm/irq.h> +#include <asm/sun3xprom.h> +#include <asm/sun3ints.h> +#include <asm/setup.h> +#include <asm/oplib.h> + +#include "time.h" + +volatile char *clock_va; + +extern void sun3_get_model(char *model); + +void sun3_leds(unsigned int i) +{ + +} + +static void sun3x_get_hardware_list(struct seq_file *m) +{ + seq_printf(m, "PROM Revision:\t%s\n", romvec->pv_monid); +} + +/* + * Setup the sun3x configuration info + */ +void __init config_sun3x(void) +{ + + sun3x_prom_init(); + + mach_max_dma_address = 0xffffffff; /* we can DMA anywhere, whee */ + + mach_sched_init = sun3x_sched_init; + mach_init_IRQ = sun3_init_IRQ; + + arch_gettimeoffset = sun3x_gettimeoffset; + mach_reset = sun3x_reboot; + + mach_hwclk = sun3x_hwclk; + mach_get_model = sun3_get_model; + mach_get_hardware_list = sun3x_get_hardware_list; + + sun3_intreg = (unsigned char *)SUN3X_INTREG; + + /* only the serial console is known to work anyway... */ +#if 0 + switch (*(unsigned char *)SUN3X_EEPROM_CONS) { + case 0x10: + serial_console = 1; + conswitchp = NULL; + break; + case 0x11: + serial_console = 2; + conswitchp = NULL; + break; + default: + serial_console = 0; + conswitchp = &dummy_con; + break; + } +#endif + +} + diff --git a/arch/m68k/sun3x/dvma.c b/arch/m68k/sun3x/dvma.c new file mode 100644 index 000000000..b2acbc862 --- /dev/null +++ b/arch/m68k/sun3x/dvma.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Virtual DMA allocation + * + * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + * + * 11/26/2000 -- disabled the existing code because it didn't work for + * me in 2.4. Replaced with a significantly more primitive version + * similar to the sun3 code. the old functionality was probably more + * desirable, but.... -- Sam Creasey (sammy@oh.verio.com) + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/mm.h> +#include <linux/bootmem.h> +#include <linux/vmalloc.h> + +#include <asm/sun3x.h> +#include <asm/dvma.h> +#include <asm/io.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> + +/* IOMMU support */ + +#define IOMMU_ADDR_MASK 0x03ffe000 +#define IOMMU_CACHE_INHIBIT 0x00000040 +#define IOMMU_FULL_BLOCK 0x00000020 +#define IOMMU_MODIFIED 0x00000010 +#define IOMMU_USED 0x00000008 +#define IOMMU_WRITE_PROTECT 0x00000004 +#define IOMMU_DT_MASK 0x00000003 +#define IOMMU_DT_INVALID 0x00000000 +#define IOMMU_DT_VALID 0x00000001 +#define IOMMU_DT_BAD 0x00000002 + + +static volatile unsigned long *iommu_pte = (unsigned long *)SUN3X_IOMMU; + + +#define dvma_entry_paddr(index) (iommu_pte[index] & IOMMU_ADDR_MASK) +#define dvma_entry_vaddr(index,paddr) ((index << DVMA_PAGE_SHIFT) | \ + (paddr & (DVMA_PAGE_SIZE-1))) +#if 0 +#define dvma_entry_set(index,addr) (iommu_pte[index] = \ + (addr & IOMMU_ADDR_MASK) | \ + IOMMU_DT_VALID | IOMMU_CACHE_INHIBIT) +#else +#define dvma_entry_set(index,addr) (iommu_pte[index] = \ + (addr & IOMMU_ADDR_MASK) | \ + IOMMU_DT_VALID) +#endif +#define dvma_entry_clr(index) (iommu_pte[index] = IOMMU_DT_INVALID) +#define dvma_entry_hash(addr) ((addr >> DVMA_PAGE_SHIFT) ^ \ + ((addr & 0x03c00000) >> \ + (DVMA_PAGE_SHIFT+4))) + +#ifdef DEBUG +/* code to print out a dvma mapping for debugging purposes */ +void dvma_print (unsigned long dvma_addr) +{ + + unsigned long index; + + index = dvma_addr >> DVMA_PAGE_SHIFT; + + pr_info("idx %lx dvma_addr %08lx paddr %08lx\n", index, dvma_addr, + dvma_entry_paddr(index)); +} +#endif + + +/* create a virtual mapping for a page assigned within the IOMMU + so that the cpu can reach it easily */ +inline int dvma_map_cpu(unsigned long kaddr, + unsigned long vaddr, int len) +{ + pgd_t *pgd; + unsigned long end; + int ret = 0; + + kaddr &= PAGE_MASK; + vaddr &= PAGE_MASK; + + end = PAGE_ALIGN(vaddr + len); + + pr_debug("dvma: mapping kern %08lx to virt %08lx\n", kaddr, vaddr); + pgd = pgd_offset_k(vaddr); + + do { + pmd_t *pmd; + unsigned long end2; + + if((pmd = pmd_alloc(&init_mm, pgd, vaddr)) == NULL) { + ret = -ENOMEM; + goto out; + } + + if((end & PGDIR_MASK) > (vaddr & PGDIR_MASK)) + end2 = (vaddr + (PGDIR_SIZE-1)) & PGDIR_MASK; + else + end2 = end; + + do { + pte_t *pte; + unsigned long end3; + + if((pte = pte_alloc_kernel(pmd, vaddr)) == NULL) { + ret = -ENOMEM; + goto out; + } + + if((end2 & PMD_MASK) > (vaddr & PMD_MASK)) + end3 = (vaddr + (PMD_SIZE-1)) & PMD_MASK; + else + end3 = end2; + + do { + pr_debug("mapping %08lx phys to %08lx\n", + __pa(kaddr), vaddr); + set_pte(pte, pfn_pte(virt_to_pfn(kaddr), + PAGE_KERNEL)); + pte++; + kaddr += PAGE_SIZE; + vaddr += PAGE_SIZE; + } while(vaddr < end3); + + } while(vaddr < end2); + + } while(vaddr < end); + + flush_tlb_all(); + + out: + return ret; +} + + +inline int dvma_map_iommu(unsigned long kaddr, unsigned long baddr, + int len) +{ + unsigned long end, index; + + index = baddr >> DVMA_PAGE_SHIFT; + end = ((baddr+len) >> DVMA_PAGE_SHIFT); + + if(len & ~DVMA_PAGE_MASK) + end++; + + for(; index < end ; index++) { +// if(dvma_entry_use(index)) +// BUG(); +// pr_info("mapping pa %lx to ba %lx\n", __pa(kaddr), +// index << DVMA_PAGE_SHIFT); + + dvma_entry_set(index, __pa(kaddr)); + + iommu_pte[index] |= IOMMU_FULL_BLOCK; +// dvma_entry_inc(index); + + kaddr += DVMA_PAGE_SIZE; + } + +#ifdef DEBUG + for(index = (baddr >> DVMA_PAGE_SHIFT); index < end; index++) + dvma_print(index << DVMA_PAGE_SHIFT); +#endif + return 0; + +} + +void dvma_unmap_iommu(unsigned long baddr, int len) +{ + + int index, end; + + + index = baddr >> DVMA_PAGE_SHIFT; + end = (DVMA_PAGE_ALIGN(baddr+len) >> DVMA_PAGE_SHIFT); + + for(; index < end ; index++) { + pr_debug("freeing bus mapping %08x\n", + index << DVMA_PAGE_SHIFT); +#if 0 + if(!dvma_entry_use(index)) + pr_info("dvma_unmap freeing unused entry %04x\n", + index); + else + dvma_entry_dec(index); +#endif + dvma_entry_clr(index); + } + +} + diff --git a/arch/m68k/sun3x/prom.c b/arch/m68k/sun3x/prom.c new file mode 100644 index 000000000..be14c899a --- /dev/null +++ b/arch/m68k/sun3x/prom.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Prom access routines for the sun3x */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/string.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/setup.h> +#include <asm/traps.h> +#include <asm/sun3xprom.h> +#include <asm/idprom.h> +#include <asm/segment.h> +#include <asm/sun3ints.h> +#include <asm/openprom.h> +#include <asm/machines.h> + +void (*sun3x_putchar)(int); +int (*sun3x_getchar)(void); +int (*sun3x_mayget)(void); +int (*sun3x_mayput)(int); +void (*sun3x_prom_reboot)(void); +e_vector sun3x_prom_abort; +struct linux_romvec *romvec; + +/* prom vector table */ +e_vector *sun3x_prom_vbr; + +/* Handle returning to the prom */ +void sun3x_halt(void) +{ + unsigned long flags; + + /* Disable interrupts while we mess with things */ + local_irq_save(flags); + + /* Restore prom vbr */ + asm volatile ("movec %0,%%vbr" : : "r" ((void*)sun3x_prom_vbr)); + + /* Restore prom NMI clock */ +// sun3x_disable_intreg(5); + sun3_enable_irq(7); + + /* Let 'er rip */ + asm volatile ("trap #14"); + + /* Restore everything */ + sun3_disable_irq(7); + sun3_enable_irq(5); + + asm volatile ("movec %0,%%vbr" : : "r" ((void*)vectors)); + local_irq_restore(flags); +} + +void sun3x_reboot(void) +{ + /* This never returns, don't bother saving things */ + local_irq_disable(); + + /* Restore prom vbr */ + asm volatile ("movec %0,%%vbr" : : "r" ((void*)sun3x_prom_vbr)); + + /* Restore prom NMI clock */ + sun3_disable_irq(5); + sun3_enable_irq(7); + + /* Let 'er rip */ + (*romvec->pv_reboot)("vmlinux"); +} + +static void sun3x_prom_write(struct console *co, const char *s, + unsigned int count) +{ + while (count--) { + if (*s == '\n') + sun3x_putchar('\r'); + sun3x_putchar(*s++); + } +} + +/* debug console - write-only */ + +static struct console sun3x_debug = { + .name = "debug", + .write = sun3x_prom_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +void __init sun3x_prom_init(void) +{ + /* Read the vector table */ + + sun3x_putchar = *(void (**)(int)) (SUN3X_P_PUTCHAR); + sun3x_getchar = *(int (**)(void)) (SUN3X_P_GETCHAR); + sun3x_mayget = *(int (**)(void)) (SUN3X_P_MAYGET); + sun3x_mayput = *(int (**)(int)) (SUN3X_P_MAYPUT); + sun3x_prom_reboot = *(void (**)(void)) (SUN3X_P_REBOOT); + sun3x_prom_abort = *(e_vector *) (SUN3X_P_ABORT); + romvec = (struct linux_romvec *)SUN3X_PROM_BASE; + + idprom_init(); + + if (!((idprom->id_machtype & SM_ARCH_MASK) == SM_SUN3X)) { + pr_warn("Machine reports strange type %02x\n", + idprom->id_machtype); + pr_warn("Pretending it's a 3/80, but very afraid...\n"); + idprom->id_machtype = SM_SUN3X | SM_3_80; + } + + /* point trap #14 at abort. + * XXX this is futile since we restore the vbr first - oops + */ + vectors[VEC_TRAP14] = sun3x_prom_abort; +} + +static int __init sun3x_debug_setup(char *arg) +{ + /* If debug=prom was specified, start the debug console */ + if (MACH_IS_SUN3X && !strcmp(arg, "prom")) + register_console(&sun3x_debug); + return 0; +} + +early_param("debug", sun3x_debug_setup); + +/* some prom functions to export */ +int prom_getintdefault(int node, char *property, int deflt) +{ + return deflt; +} + +int prom_getbool (int node, char *prop) +{ + return 1; +} + +void prom_printf(char *fmt, ...) +{ +} + +void prom_halt (void) +{ + sun3x_halt(); +} + +/* Get the idprom and stuff it into buffer 'idbuf'. Returns the + * format type. 'num_bytes' is the number of bytes that your idbuf + * has space for. Returns 0xff on error. + */ +unsigned char +prom_get_idprom(char *idbuf, int num_bytes) +{ + int i; + + /* make a copy of the idprom structure */ + for (i = 0; i < num_bytes; i++) + idbuf[i] = ((char *)SUN3X_IDPROM)[i]; + + return idbuf[0]; +} diff --git a/arch/m68k/sun3x/time.c b/arch/m68k/sun3x/time.c new file mode 100644 index 000000000..3c8a86d08 --- /dev/null +++ b/arch/m68k/sun3x/time.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/m68k/sun3x/time.c + * + * Sun3x-specific time handling + */ + +#include <linux/types.h> +#include <linux/kd.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> +#include <linux/rtc.h> +#include <linux/bcd.h> + +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/traps.h> +#include <asm/sun3x.h> +#include <asm/sun3ints.h> + +#include "time.h" + +#define M_CONTROL 0xf8 +#define M_SEC 0xf9 +#define M_MIN 0xfa +#define M_HOUR 0xfb +#define M_DAY 0xfc +#define M_DATE 0xfd +#define M_MONTH 0xfe +#define M_YEAR 0xff + +#define C_WRITE 0x80 +#define C_READ 0x40 +#define C_SIGN 0x20 +#define C_CALIB 0x1f + +int sun3x_hwclk(int set, struct rtc_time *t) +{ + volatile struct mostek_dt *h = + (struct mostek_dt *)(SUN3X_EEPROM+M_CONTROL); + unsigned long flags; + + local_irq_save(flags); + + if(set) { + h->csr |= C_WRITE; + h->sec = bin2bcd(t->tm_sec); + h->min = bin2bcd(t->tm_min); + h->hour = bin2bcd(t->tm_hour); + h->wday = bin2bcd(t->tm_wday); + h->mday = bin2bcd(t->tm_mday); + h->month = bin2bcd(t->tm_mon + 1); + h->year = bin2bcd(t->tm_year % 100); + h->csr &= ~C_WRITE; + } else { + h->csr |= C_READ; + t->tm_sec = bcd2bin(h->sec); + t->tm_min = bcd2bin(h->min); + t->tm_hour = bcd2bin(h->hour); + t->tm_wday = bcd2bin(h->wday); + t->tm_mday = bcd2bin(h->mday); + t->tm_mon = bcd2bin(h->month) - 1; + t->tm_year = bcd2bin(h->year); + h->csr &= ~C_READ; + if (t->tm_year < 70) + t->tm_year += 100; + } + + local_irq_restore(flags); + + return 0; +} +/* Not much we can do here */ +u32 sun3x_gettimeoffset(void) +{ + return 0L; +} + +#if 0 +static irqreturn_t sun3x_timer_tick(int irq, void *dev_id) +{ + irq_handler_t timer_routine = dev_id; + unsigned long flags; + + local_irq_save(flags); + /* Clear the pending interrupt - pulse the enable line low */ + disable_irq(5); + enable_irq(5); + timer_routine(0, NULL); + local_irq_restore(flags); + + return IRQ_HANDLED; +} +#endif + +void __init sun3x_sched_init(irq_handler_t vector) +{ + + sun3_disable_interrupts(); + + + /* Pulse enable low to get the clock started */ + sun3_disable_irq(5); + sun3_enable_irq(5); + sun3_enable_interrupts(); +} diff --git a/arch/m68k/sun3x/time.h b/arch/m68k/sun3x/time.h new file mode 100644 index 000000000..496f40641 --- /dev/null +++ b/arch/m68k/sun3x/time.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef SUN3X_TIME_H +#define SUN3X_TIME_H + +extern int sun3x_hwclk(int set, struct rtc_time *t); +u32 sun3x_gettimeoffset(void); +void sun3x_sched_init(irq_handler_t vector); + +struct mostek_dt { + volatile unsigned char csr; + volatile unsigned char sec; + volatile unsigned char min; + volatile unsigned char hour; + volatile unsigned char wday; + volatile unsigned char mday; + volatile unsigned char month; + volatile unsigned char year; +}; + +#endif |