Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
7
arch/m68k/sun3/Makefile
Normal file
7
arch/m68k/sun3/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for Linux arch/m68k/sun3 source directory
|
||||
#
|
||||
|
||||
obj-y := sun3_ksyms.o sun3ints.o sun3dvma.o sbus.o idprom.o
|
||||
|
||||
obj-$(CONFIG_SUN3) += config.o mmu_emu.o leds.o dvma.o intersil.o
|
188
arch/m68k/sun3/config.c
Normal file
188
arch/m68k/sun3/config.c
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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/config.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bootmem.h>
|
||||
|
||||
#include <asm/oplib.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/contregs.h>
|
||||
#include <asm/movs.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/sun3-head.h>
|
||||
#include <asm/sun3mmu.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/intersil.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/segment.h>
|
||||
#include <asm/sun3ints.h>
|
||||
|
||||
extern char _text, _end;
|
||||
|
||||
char sun3_reserved_pmeg[SUN3_PMEGS_NUM];
|
||||
|
||||
extern unsigned long sun3_gettimeoffset(void);
|
||||
extern int show_sun3_interrupts (struct seq_file *, void *);
|
||||
extern void sun3_sched_init(irqreturn_t (*handler)(int, void *, struct pt_regs *));
|
||||
extern void sun3_get_model (char* model);
|
||||
extern void idprom_init (void);
|
||||
extern int sun3_hwclk(int set, struct rtc_time *t);
|
||||
|
||||
volatile char* clock_va;
|
||||
extern volatile unsigned char* sun3_intreg;
|
||||
extern unsigned long availmem;
|
||||
unsigned long num_pages;
|
||||
|
||||
static int sun3_get_hardware_list(char *buffer)
|
||||
{
|
||||
|
||||
int len = 0;
|
||||
|
||||
len += sprintf(buffer + len, "PROM Revision:\t%s\n",
|
||||
romvec->pv_monid);
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
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;
|
||||
num_pages = __pa(memory_end) >> PAGE_SHIFT;
|
||||
|
||||
high_memory = (void *)memory_end;
|
||||
availmem = memory_start;
|
||||
|
||||
availmem += init_bootmem_node(NODE_DATA(0), start_page, 0, num_pages);
|
||||
availmem = (availmem + (PAGE_SIZE-1)) & PAGE_MASK;
|
||||
|
||||
free_bootmem(__pa(availmem), memory_end - (availmem));
|
||||
}
|
||||
|
||||
|
||||
void __init config_sun3(void)
|
||||
{
|
||||
unsigned long memory_start, memory_end;
|
||||
|
||||
printk("ARCH: SUN3\n");
|
||||
idprom_init();
|
||||
|
||||
/* Subtract kernel memory from available memory */
|
||||
|
||||
mach_sched_init = sun3_sched_init;
|
||||
mach_init_IRQ = sun3_init_IRQ;
|
||||
mach_default_handler = &sun3_default_handler;
|
||||
mach_request_irq = sun3_request_irq;
|
||||
mach_free_irq = sun3_free_irq;
|
||||
enable_irq = sun3_enable_irq;
|
||||
disable_irq = sun3_disable_irq;
|
||||
mach_process_int = sun3_process_int;
|
||||
mach_get_irq_list = show_sun3_interrupts;
|
||||
mach_reset = sun3_reboot;
|
||||
mach_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;
|
||||
#if defined(CONFIG_DUMMY_CONSOLE)
|
||||
conswitchp = &dummy_con;
|
||||
#endif
|
||||
|
||||
memory_start = ((((int)&_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);
|
||||
}
|
||||
|
||||
void __init sun3_sched_init(irqreturn_t (*timer_routine)(int, void *, struct pt_regs *))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
71
arch/m68k/sun3/dvma.c
Normal file
71
arch/m68k/sun3/dvma.c
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* linux/arch/m68k/sun3/dvma.c
|
||||
*
|
||||
* Written by Sam Creasey
|
||||
*
|
||||
* Sun3 IOMMU routines used for dvma accesses.
|
||||
*
|
||||
*/
|
||||
|
||||
#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];
|
||||
|
||||
inline 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);
|
||||
// printk("dvma_remap: addr %lx -> %lx pte %08lx len %x\n",
|
||||
// kaddr, vaddr, pte, len);
|
||||
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 sun3_dvma_init(void)
|
||||
{
|
||||
|
||||
memset(ptelist, 0, sizeof(ptelist));
|
||||
|
||||
|
||||
}
|
129
arch/m68k/sun3/idprom.c
Normal file
129
arch/m68k/sun3/idprom.c
Normal file
@@ -0,0 +1,129 @@
|
||||
/* $Id: idprom.c,v 1.22 1996/11/13 05:09:25 davem Exp $
|
||||
* 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/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;
|
||||
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.
|
||||
*/
|
||||
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))
|
||||
printk("TYPE: %s\n", Sun_Machines[i].name);
|
||||
else {
|
||||
#if 0
|
||||
prom_getproperty(prom_root_node, "banner-name",
|
||||
sysname, sizeof(sysname));
|
||||
printk("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);
|
||||
|
||||
printk("Ethernet address: %x:%x:%x:%x:%x:%x\n",
|
||||
idprom->id_ethaddr[0], idprom->id_ethaddr[1],
|
||||
idprom->id_ethaddr[2], idprom->id_ethaddr[3],
|
||||
idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
|
||||
}
|
76
arch/m68k/sun3/intersil.c
Normal file
76
arch/m68k/sun3/intersil.c
Normal file
@@ -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/system.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/intersil.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? */
|
||||
unsigned long sun3_gettimeoffset(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
todintersil->day = t->tm_mday;
|
||||
todintersil->year = t->tm_year - 68;
|
||||
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;
|
||||
t->tm_mday = todintersil->day;
|
||||
t->tm_year = todintersil->year + 68;
|
||||
t->tm_wday = todintersil->weekday;
|
||||
}
|
||||
|
||||
intersil_clock->cmd_reg = START_VAL;
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
13
arch/m68k/sun3/leds.c
Normal file
13
arch/m68k/sun3/leds.c
Normal file
@@ -0,0 +1,13 @@
|
||||
#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);
|
||||
}
|
427
arch/m68k/sun3/mmu_emu.c
Normal file
427
arch/m68k/sun3/mmu_emu.c
Normal file
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
** Tablewalk MMU emulator
|
||||
**
|
||||
** by Toshiyasu Morita
|
||||
**
|
||||
** Started 1/16/98 @ 2:22 am
|
||||
*/
|
||||
|
||||
#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 <asm/setup.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/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>
|
||||
|
||||
extern void prom_reboot (char *) __attribute__ ((__noreturn__));
|
||||
|
||||
#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 vmalloc_end;
|
||||
EXPORT_SYMBOL(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 */
|
||||
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);
|
||||
printk (" pte=%lx [addr=%lx",
|
||||
val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT);
|
||||
if (val & SUN3_PAGE_VALID) printk (" valid");
|
||||
if (val & SUN3_PAGE_WRITEABLE) printk (" write");
|
||||
if (val & SUN3_PAGE_SYSTEM) printk (" sys");
|
||||
if (val & SUN3_PAGE_NOCACHE) printk (" nocache");
|
||||
if (val & SUN3_PAGE_ACCESSED) printk (" accessed");
|
||||
if (val & SUN3_PAGE_MODIFIED) printk (" modified");
|
||||
switch (val & SUN3_PAGE_TYPE_MASK) {
|
||||
case SUN3_PAGE_TYPE_MEMORY: printk (" memory"); break;
|
||||
case SUN3_PAGE_TYPE_IO: printk (" io"); break;
|
||||
case SUN3_PAGE_TYPE_VME16: printk (" vme16"); break;
|
||||
case SUN3_PAGE_TYPE_VME32: printk (" vme32"); break;
|
||||
}
|
||||
printk ("]\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;
|
||||
}
|
||||
|
||||
printk (" 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)
|
||||
{
|
||||
printk (" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr));
|
||||
print_pte (__pte (sun3_get_pte (vaddr)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise the MMU emulator.
|
||||
*/
|
||||
void 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
|
||||
printk("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++) {
|
||||
printk ("mapped:");
|
||||
print_pte_vaddr (seg + (i*PAGE_SIZE));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
// the lowest mapping here is the end of our
|
||||
// vmalloc region
|
||||
if(!vmalloc_end)
|
||||
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 intellegent... 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
|
||||
printk("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
|
||||
printk ("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
|
||||
printk ("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) {
|
||||
// printk ("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
|
||||
printk ("seg:%d crp:%p ->", get_fs().seg, crp);
|
||||
print_pte_vaddr (vaddr);
|
||||
printk ("\n");
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
7
arch/m68k/sun3/prom/Makefile
Normal file
7
arch/m68k/sun3/prom/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
# $Id: Makefile,v 1.5 1995/11/25 00:59:48 davem Exp $
|
||||
# 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
|
174
arch/m68k/sun3/prom/console.c
Normal file
174
arch/m68k/sun3/prom/console.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/* $Id: console.c,v 1.10 1996/12/18 06:46:54 tridge Exp $
|
||||
* 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 <asm/system.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;
|
||||
case PROM_AP1000:
|
||||
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;
|
||||
case PROM_AP1000:
|
||||
return PROMDEV_I_UNK;
|
||||
};
|
||||
return PROMDEV_O_UNK;
|
||||
}
|
||||
#endif
|
89
arch/m68k/sun3/prom/init.c
Normal file
89
arch/m68k/sun3/prom/init.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/* $Id: init.c,v 1.9 1996/12/18 06:46:55 tridge Exp $
|
||||
* init.c: Initialize internal variables used by the PROM
|
||||
* library functions.
|
||||
*
|
||||
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#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 returns 0 on success, 1 on
|
||||
* failure. It gets passed the pointer to the PROM vector.
|
||||
*/
|
||||
|
||||
extern void prom_meminit(void);
|
||||
extern void prom_ranges_init(void);
|
||||
|
||||
void __init prom_init(struct linux_romvec *rp)
|
||||
{
|
||||
#ifdef CONFIG_AP1000
|
||||
extern struct linux_romvec *ap_prom_init(void);
|
||||
rp = ap_prom_init();
|
||||
#endif
|
||||
|
||||
romvec = rp;
|
||||
#ifndef CONFIG_SUN3
|
||||
switch(romvec->pv_romvers) {
|
||||
case 0:
|
||||
prom_vers = PROM_V0;
|
||||
break;
|
||||
case 2:
|
||||
prom_vers = PROM_V2;
|
||||
break;
|
||||
case 3:
|
||||
prom_vers = PROM_V3;
|
||||
break;
|
||||
case 4:
|
||||
prom_vers = PROM_P1275;
|
||||
prom_printf("PROMLIB: Sun IEEE Prom not supported yet\n");
|
||||
prom_halt();
|
||||
break;
|
||||
case 42: /* why not :-) */
|
||||
prom_vers = PROM_AP1000;
|
||||
break;
|
||||
|
||||
default:
|
||||
prom_printf("PROMLIB: Bad PROM version %d\n",
|
||||
romvec->pv_romvers);
|
||||
prom_halt();
|
||||
break;
|
||||
};
|
||||
|
||||
prom_rev = romvec->pv_plugin_revision;
|
||||
prom_prev = romvec->pv_printrev;
|
||||
prom_nodeops = romvec->pv_nodeops;
|
||||
|
||||
prom_root_node = prom_getsibling(0);
|
||||
if((prom_root_node == 0) || (prom_root_node == -1))
|
||||
prom_halt();
|
||||
|
||||
if((((unsigned long) prom_nodeops) == 0) ||
|
||||
(((unsigned long) prom_nodeops) == -1))
|
||||
prom_halt();
|
||||
|
||||
prom_meminit();
|
||||
|
||||
prom_ranges_init();
|
||||
#endif
|
||||
// printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n",
|
||||
// romvec->pv_romvers, prom_rev);
|
||||
|
||||
/* Initialization successful. */
|
||||
return;
|
||||
}
|
94
arch/m68k/sun3/prom/misc.c
Normal file
94
arch/m68k/sun3/prom/misc.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/* $Id: misc.c,v 1.15 1997/05/14 20:45:00 davem Exp $
|
||||
* 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;
|
||||
}
|
61
arch/m68k/sun3/prom/printf.c
Normal file
61
arch/m68k/sun3/prom/printf.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/* $Id: printf.c,v 1.5 1996/04/04 16:31:07 tridge Exp $
|
||||
* 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/config.h>
|
||||
#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_AP1000
|
||||
ap_write(1,bptr,strlen(bptr));
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_KGDB
|
||||
if (kgdb_initialized) {
|
||||
printk("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
|
||||
#endif
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
27
arch/m68k/sun3/sbus.c
Normal file
27
arch/m68k/sun3/sbus.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SBus helper functions
|
||||
*
|
||||
* Sun3 don't have a sbus, but many of the used devices are also
|
||||
* used on Sparc machines with sbus. To avoid having a lot of
|
||||
* duplicate code, we provide necessary glue stuff to make using
|
||||
* of the sbus driver code possible.
|
||||
*
|
||||
* (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
int __init sbus_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *sparc_alloc_io (u32 address, void *virtual, int len, char *name,
|
||||
u32 bus_type, int rdonly)
|
||||
{
|
||||
return (void *)address;
|
||||
}
|
||||
|
||||
subsys_initcall(sbus_init);
|
13
arch/m68k/sun3/sun3_ksyms.c
Normal file
13
arch/m68k/sun3/sun3_ksyms.c
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/dvma.h>
|
||||
#include <asm/idprom.h>
|
||||
|
||||
/*
|
||||
* Add things here when you find the need for it.
|
||||
*/
|
||||
EXPORT_SYMBOL(dvma_map_align);
|
||||
EXPORT_SYMBOL(dvma_unmap);
|
||||
EXPORT_SYMBOL(dvma_malloc_align);
|
||||
EXPORT_SYMBOL(dvma_free);
|
||||
EXPORT_SYMBOL(idprom);
|
379
arch/m68k/sun3/sun3dvma.c
Normal file
379
arch/m68k/sun3/sun3dvma.c
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* linux/arch/m68k/mm/sun3dvma.c
|
||||
*
|
||||
* Copyright (C) 2000 Sam Creasey
|
||||
*
|
||||
* Contains common routines for sun3/sun3x DVMA management.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.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
|
||||
|
||||
unsigned long iommu_use[IOMMU_TOTAL_ENTRIES];
|
||||
|
||||
#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;
|
||||
|
||||
printk("dvma entry usage:\n");
|
||||
|
||||
for(i = 0; i < IOMMU_TOTAL_ENTRIES; i++) {
|
||||
if(!iommu_use[i])
|
||||
continue;
|
||||
|
||||
j++;
|
||||
|
||||
printk("dvma entry: %08lx len %08lx\n",
|
||||
( i << DVMA_PAGE_SHIFT) + DVMA_START,
|
||||
iommu_use[i]);
|
||||
}
|
||||
|
||||
printk("%d entries in use total\n", j);
|
||||
|
||||
printk("allocation/free calls: %lu/%lu\n", dvma_allocs, dvma_frees);
|
||||
printk("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;
|
||||
|
||||
printk("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;
|
||||
|
||||
printk("hole: start %08lx end %08lx size %08lx\n", hole->start, hole->end, hole->size);
|
||||
}
|
||||
|
||||
printk("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_del(&(prev->list));
|
||||
list_add(&(prev->list), &hole_cache);
|
||||
ret++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline struct hole *rmcache(void)
|
||||
{
|
||||
struct hole *ret;
|
||||
|
||||
if(list_empty(&hole_cache)) {
|
||||
if(!refill()) {
|
||||
printk("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
|
||||
printk("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_del(&(hole->list));
|
||||
list_add(&(hole->list), &hole_cache);
|
||||
dvma_entry_use(hole->start) = newlen;
|
||||
#ifdef DVMA_DEBUG
|
||||
dvma_allocs++;
|
||||
dvma_alloc_bytes += newlen;
|
||||
#endif
|
||||
return hole->start;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
printk("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 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);
|
||||
|
||||
memset(iommu_use, 0, sizeof(iommu_use));
|
||||
|
||||
dvma_unmap_iommu(DVMA_START, DVMA_SIZE);
|
||||
|
||||
#ifdef CONFIG_SUN3
|
||||
sun3_dvma_init();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
inline 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) {
|
||||
// printk("error: kaddr %lx len %x\n", kaddr, len);
|
||||
// *(int *)4 = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("dvma_map request %08lx bytes from %08lx\n",
|
||||
len, kaddr);
|
||||
#endif
|
||||
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);
|
||||
// printk("using baddr %lx\n", baddr);
|
||||
|
||||
if(!dvma_map_iommu(kaddr, baddr, len))
|
||||
return (baddr + off);
|
||||
|
||||
printk("dvma_map failed kaddr %lx baddr %lx len %x\n", kaddr, baddr, len);
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void *dvma_malloc_align(unsigned long len, unsigned long align)
|
||||
{
|
||||
unsigned long kaddr;
|
||||
unsigned long baddr;
|
||||
unsigned long vaddr;
|
||||
|
||||
if(!len)
|
||||
return NULL;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("dvma_malloc request %lx bytes\n", len);
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("mapped %08lx bytes %08lx kern -> %08lx bus\n",
|
||||
len, kaddr, baddr);
|
||||
#endif
|
||||
|
||||
return (void *)vaddr;
|
||||
|
||||
}
|
||||
|
||||
void dvma_free(void *vaddr)
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
}
|
265
arch/m68k/sun3/sun3ints.c
Normal file
265
arch/m68k/sun3/sun3ints.c
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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/config.h>
|
||||
#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 <linux/seq_file.h>
|
||||
|
||||
extern void sun3_leds (unsigned char);
|
||||
static irqreturn_t sun3_inthandle(int irq, void *dev_id, struct pt_regs *fp);
|
||||
|
||||
void sun3_disable_interrupts(void)
|
||||
{
|
||||
sun3_disable_irq(0);
|
||||
}
|
||||
|
||||
void sun3_enable_interrupts(void)
|
||||
{
|
||||
sun3_enable_irq(0);
|
||||
}
|
||||
|
||||
int led_pattern[8] = {
|
||||
~(0x80), ~(0x01),
|
||||
~(0x40), ~(0x02),
|
||||
~(0x20), ~(0x04),
|
||||
~(0x10), ~(0x08)
|
||||
};
|
||||
|
||||
volatile unsigned char* sun3_intreg;
|
||||
|
||||
void sun3_insert_irq(irq_node_t **list, irq_node_t *node)
|
||||
{
|
||||
}
|
||||
|
||||
void sun3_delete_irq(irq_node_t **list, void *dev_id)
|
||||
{
|
||||
}
|
||||
|
||||
void sun3_enable_irq(unsigned int irq)
|
||||
{
|
||||
*sun3_intreg |= (1<<irq);
|
||||
}
|
||||
|
||||
void sun3_disable_irq(unsigned int irq)
|
||||
{
|
||||
*sun3_intreg &= ~(1<<irq);
|
||||
}
|
||||
|
||||
inline void sun3_do_irq(int irq, struct pt_regs *fp)
|
||||
{
|
||||
kstat_cpu(0).irqs[SYS_IRQS + irq]++;
|
||||
*sun3_intreg &= ~(1<<irq);
|
||||
*sun3_intreg |= (1<<irq);
|
||||
}
|
||||
|
||||
static irqreturn_t sun3_int7(int irq, void *dev_id, struct pt_regs *fp)
|
||||
{
|
||||
sun3_do_irq(irq,fp);
|
||||
if(!(kstat_cpu(0).irqs[SYS_IRQS + irq] % 2000))
|
||||
sun3_leds(led_pattern[(kstat_cpu(0).irqs[SYS_IRQS+irq]%16000)
|
||||
/2000]);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t sun3_int5(int irq, void *dev_id, struct pt_regs *fp)
|
||||
{
|
||||
kstat_cpu(0).irqs[SYS_IRQS + irq]++;
|
||||
#ifdef CONFIG_SUN3
|
||||
intersil_clear();
|
||||
#endif
|
||||
*sun3_intreg &= ~(1<<irq);
|
||||
*sun3_intreg |= (1<<irq);
|
||||
#ifdef CONFIG_SUN3
|
||||
intersil_clear();
|
||||
#endif
|
||||
do_timer(fp);
|
||||
#ifndef CONFIG_SMP
|
||||
update_process_times(user_mode(fp));
|
||||
#endif
|
||||
if(!(kstat_cpu(0).irqs[SYS_IRQS + irq] % 20))
|
||||
sun3_leds(led_pattern[(kstat_cpu(0).irqs[SYS_IRQS+irq]%160)
|
||||
/20]);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* handle requested ints, excepting 5 and 7, which always do the same
|
||||
thing */
|
||||
irqreturn_t (*sun3_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
|
||||
[0] = sun3_inthandle,
|
||||
[1] = sun3_inthandle,
|
||||
[2] = sun3_inthandle,
|
||||
[3] = sun3_inthandle,
|
||||
[4] = sun3_inthandle,
|
||||
[5] = sun3_int5,
|
||||
[6] = sun3_inthandle,
|
||||
[7] = sun3_int7
|
||||
};
|
||||
|
||||
static const char *dev_names[SYS_IRQS] = {
|
||||
[5] = "timer",
|
||||
[7] = "int7 handler"
|
||||
};
|
||||
static void *dev_ids[SYS_IRQS];
|
||||
static irqreturn_t (*sun3_inthandler[SYS_IRQS])(int, void *, struct pt_regs *) = {
|
||||
[5] = sun3_int5,
|
||||
[7] = sun3_int7
|
||||
};
|
||||
static irqreturn_t (*sun3_vechandler[SUN3_INT_VECS])(int, void *, struct pt_regs *);
|
||||
static void *vec_ids[SUN3_INT_VECS];
|
||||
static const char *vec_names[SUN3_INT_VECS];
|
||||
static int vec_ints[SUN3_INT_VECS];
|
||||
|
||||
|
||||
int show_sun3_interrupts(struct seq_file *p, void *v)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < (SUN3_INT_VECS-1); i++) {
|
||||
if(sun3_vechandler[i] != NULL) {
|
||||
seq_printf(p, "vec %3d: %10u %s\n", i+64,
|
||||
vec_ints[i],
|
||||
(vec_names[i]) ? vec_names[i] :
|
||||
"sun3_vechandler");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t sun3_inthandle(int irq, void *dev_id, struct pt_regs *fp)
|
||||
{
|
||||
if(sun3_inthandler[irq] == NULL)
|
||||
panic ("bad interrupt %d received (id %p)\n",irq, dev_id);
|
||||
|
||||
kstat_cpu(0).irqs[SYS_IRQS + irq]++;
|
||||
*sun3_intreg &= ~(1<<irq);
|
||||
|
||||
sun3_inthandler[irq](irq, dev_ids[irq], fp);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t sun3_vec255(int irq, void *dev_id, struct pt_regs *fp)
|
||||
{
|
||||
// intersil_clear();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void sun3_init_IRQ(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
*sun3_intreg = 1;
|
||||
|
||||
for(i = 0; i < SYS_IRQS; i++)
|
||||
{
|
||||
if(dev_names[i])
|
||||
cpu_request_irq(i, sun3_default_handler[i], 0,
|
||||
dev_names[i], NULL);
|
||||
}
|
||||
|
||||
for(i = 0; i < 192; i++)
|
||||
sun3_vechandler[i] = NULL;
|
||||
|
||||
sun3_vechandler[191] = sun3_vec255;
|
||||
}
|
||||
|
||||
int sun3_request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *),
|
||||
unsigned long flags, const char *devname, void *dev_id)
|
||||
{
|
||||
|
||||
if(irq < SYS_IRQS) {
|
||||
if(sun3_inthandler[irq] != NULL) {
|
||||
printk("sun3_request_irq: request for irq %d -- already taken!\n", irq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sun3_inthandler[irq] = handler;
|
||||
dev_ids[irq] = dev_id;
|
||||
dev_names[irq] = devname;
|
||||
|
||||
/* setting devname would be nice */
|
||||
cpu_request_irq(irq, sun3_default_handler[irq], 0, devname,
|
||||
NULL);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
if((irq >= 64) && (irq <= 255)) {
|
||||
int vec;
|
||||
|
||||
vec = irq - 64;
|
||||
if(sun3_vechandler[vec] != NULL) {
|
||||
printk("sun3_request_irq: request for vec %d -- already taken!\n", irq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sun3_vechandler[vec] = handler;
|
||||
vec_ids[vec] = dev_id;
|
||||
vec_names[vec] = devname;
|
||||
vec_ints[vec] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printk("sun3_request_irq: invalid irq %d\n", irq);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
void sun3_free_irq(unsigned int irq, void *dev_id)
|
||||
{
|
||||
|
||||
if(irq < SYS_IRQS) {
|
||||
if(sun3_inthandler[irq] == NULL)
|
||||
panic("sun3_free_int: attempt to free unused irq %d\n", irq);
|
||||
if(dev_ids[irq] != dev_id)
|
||||
panic("sun3_free_int: incorrect dev_id for irq %d\n", irq);
|
||||
|
||||
sun3_inthandler[irq] = NULL;
|
||||
return;
|
||||
} else if((irq >= 64) && (irq <= 255)) {
|
||||
int vec;
|
||||
|
||||
vec = irq - 64;
|
||||
if(sun3_vechandler[vec] == NULL)
|
||||
panic("sun3_free_int: attempt to free unused vector %d\n", irq);
|
||||
if(vec_ids[irq] != dev_id)
|
||||
panic("sun3_free_int: incorrect dev_id for vec %d\n", irq);
|
||||
|
||||
sun3_vechandler[vec] = NULL;
|
||||
return;
|
||||
} else {
|
||||
panic("sun3_free_irq: invalid irq %d\n", irq);
|
||||
}
|
||||
}
|
||||
|
||||
irqreturn_t sun3_process_int(int irq, struct pt_regs *regs)
|
||||
{
|
||||
|
||||
if((irq >= 64) && (irq <= 255)) {
|
||||
int vec;
|
||||
|
||||
vec = irq - 64;
|
||||
if(sun3_vechandler[vec] == NULL)
|
||||
panic ("bad interrupt vector %d received\n",irq);
|
||||
|
||||
vec_ints[vec]++;
|
||||
return sun3_vechandler[vec](irq, vec_ids[vec], regs);
|
||||
} else {
|
||||
panic("sun3_process_int: unable to handle interrupt vector %d\n",
|
||||
irq);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user