blob: 12cdb4625b980b586ca861e58e3b4b03ddf35f6f [file] [log] [blame]
/*
* OpenBIOS Sparc OBIO driver
*
* (C) 2004 Stefan Reinauer
* (C) 2005 Ed Schouten <ed@fxq.nl>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2
*
*/
#include "config.h"
#include "libopenbios/bindings.h"
#include "kernel/kernel.h"
#include "libc/byteorder.h"
#include "libc/vsprintf.h"
#include "drivers/drivers.h"
#include "arch/common/nvram.h"
#include "libopenbios/ofmem.h"
#include "obio.h"
#include "escc.h"
#define PROMDEV_KBD 0 /* input from keyboard */
#define PROMDEV_SCREEN 0 /* output to screen */
#define PROMDEV_TTYA 1 /* in/out to ttya */
void
ob_new_obio_device(const char *name, const char *type)
{
push_str("/obio");
fword("find-device");
fword("new-device");
push_str(name);
fword("device-name");
if (type) {
push_str(type);
fword("device-type");
}
}
static unsigned long
map_reg(uint64_t base, uint64_t offset, unsigned long size, int map,
int phys_hi)
{
PUSH(phys_hi);
fword("encode-int");
PUSH(offset);
fword("encode-int");
fword("encode+");
PUSH(size);
fword("encode-int");
fword("encode+");
push_str("reg");
fword("property");
if (map) {
unsigned long addr;
addr = (unsigned long)ofmem_map_io(base + offset, size);
PUSH(addr);
fword("encode-int");
push_str("address");
fword("property");
return addr;
}
return 0;
}
unsigned long
ob_reg(uint64_t base, uint64_t offset, unsigned long size, int map)
{
return map_reg(base, offset, size, map, 0);
}
void
ob_intr(int intr)
{
PUSH(intr);
fword("encode-int");
PUSH(0);
fword("encode-int");
fword("encode+");
push_str("intr");
fword("property");
}
void
ob_eccmemctl_init(uint64_t base)
{
uint32_t version, *regs;
const char *mc_type;
push_str("/");
fword("find-device");
fword("new-device");
push_str("eccmemctl");
fword("device-name");
PUSH(0x20);
fword("encode-int");
push_str("width");
fword("property");
regs = (uint32_t *)map_reg(ECC_BASE, 0, ECC_SIZE, 1, ECC_BASE >> 32);
version = regs[0];
switch (version) {
case 0x00000000:
mc_type = "MCC";
break;
case 0x10000000:
mc_type = "EMC";
break;
default:
case 0x20000000:
mc_type = "SMC";
break;
}
push_str(mc_type);
fword("encode-string");
push_str("mc-type");
fword("property");
fword("finish-device");
}
static unsigned char *nvram;
#define NVRAM_OB_START (0)
#define NVRAM_OB_SIZE ((NVRAM_IDPROM - NVRAM_OB_START) & ~15)
void
arch_nvram_get(char *data)
{
memcpy(data, &nvram[NVRAM_OB_START], NVRAM_OB_SIZE);
}
void
arch_nvram_put(char *data)
{
memcpy(&nvram[NVRAM_OB_START], data, NVRAM_OB_SIZE);
}
int
arch_nvram_size(void)
{
return NVRAM_OB_SIZE;
}
void
ss5_init(uint64_t base)
{
ob_new_obio_device("slavioconfig", NULL);
ob_reg(base, SLAVIO_SCONFIG, SCONFIG_REGS, 0);
fword("finish-device");
}
static void
ob_nvram_init(uint64_t base, uint64_t offset)
{
ob_new_obio_device("eeprom", NULL);
nvram = (unsigned char *)ob_reg(base, offset, NVRAM_SIZE, 1);
PUSH((unsigned long)nvram);
fword("encode-int");
push_str("address");
fword("property");
push_str("mk48t08");
fword("model");
fword("finish-device");
// Add /idprom
push_str("/");
fword("find-device");
PUSH((long)&nvram[NVRAM_IDPROM]);
PUSH(32);
fword("encode-bytes");
push_str("idprom");
fword("property");
}
static void
ob_fd_init(uint64_t base, uint64_t offset, int intr)
{
unsigned long addr;
ob_new_obio_device("SUNW,fdtwo", "block");
addr = ob_reg(base, offset, FD_REGS, 1);
ob_intr(intr);
fword("is-deblocker");
ob_floppy_init("/obio", "SUNW,fdtwo", 0, addr);
fword("finish-device");
}
static void
ob_auxio_init(uint64_t base, uint64_t offset)
{
ob_new_obio_device("auxio", NULL);
ob_reg(base, offset, AUXIO_REGS, 1);
fword("finish-device");
}
volatile unsigned char *power_reg;
volatile unsigned int *reset_reg;
static void
sparc32_power_off(void)
{
*power_reg = 1;
}
static void
sparc32_reset_all(void)
{
*reset_reg = 1;
}
// AUX 2 (Software Powerdown Control) and reset
static void
ob_aux2_reset_init(uint64_t base, uint64_t offset, int intr)
{
ob_new_obio_device("power", NULL);
power_reg = (void *)ob_reg(base, offset, AUXIO2_REGS, 1);
bind_func("sparc32-power-off", sparc32_power_off);
push_str("' sparc32-power-off to power-off");
fword("eval");
// Not in device tree
reset_reg = (unsigned int *)ofmem_map_io(base + (uint64_t)SLAVIO_RESET, RESET_REGS);
bind_func("sparc32-reset-all", sparc32_reset_all);
push_str("' sparc32-reset-all to reset-all");
fword("eval");
ob_intr(intr);
fword("finish-device");
}
volatile struct sun4m_timer_regs *counter_regs;
static void
ob_counter_init(uint64_t base, unsigned long offset, int ncpu)
{
int i;
ob_new_obio_device("counter", NULL);
for (i = 0; i < ncpu; i++) {
PUSH(0);
fword("encode-int");
if (i != 0) fword("encode+");
PUSH(offset + (i * PAGE_SIZE));
fword("encode-int");
fword("encode+");
PUSH(COUNTER_REGS);
fword("encode-int");
fword("encode+");
}
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH(offset + 0x10000);
fword("encode-int");
fword("encode+");
PUSH(COUNTER_REGS);
fword("encode-int");
fword("encode+");
push_str("reg");
fword("property");
counter_regs = (struct sun4m_timer_regs *)ofmem_map_io(base + (uint64_t)offset, sizeof(*counter_regs));
counter_regs->cfg = 0xfffffffe;
counter_regs->l10_timer_limit = 0;
counter_regs->cpu_timers[0].l14_timer_limit = 0x9c4000; /* see comment in obio.h */
counter_regs->cpu_timers[0].cntrl = 1;
for (i = 0; i < ncpu; i++) {
PUSH((unsigned long)&counter_regs->cpu_timers[i]);
fword("encode-int");
if (i != 0)
fword("encode+");
}
PUSH((unsigned long)&counter_regs->l10_timer_limit);
fword("encode-int");
fword("encode+");
push_str("address");
fword("property");
fword("finish-device");
}
static volatile struct sun4m_intregs *intregs;
static void
ob_interrupt_init(uint64_t base, unsigned long offset, int ncpu)
{
int i;
ob_new_obio_device("interrupt", NULL);
for (i = 0; i < ncpu; i++) {
PUSH(0);
fword("encode-int");
if (i != 0) fword("encode+");
PUSH(offset + (i * PAGE_SIZE));
fword("encode-int");
fword("encode+");
PUSH(INTERRUPT_REGS);
fword("encode-int");
fword("encode+");
}
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH(offset + 0x10000);
fword("encode-int");
fword("encode+");
PUSH(INTERRUPT_REGS);
fword("encode-int");
fword("encode+");
push_str("reg");
fword("property");
intregs = (struct sun4m_intregs *)ofmem_map_io(base | (uint64_t)offset, sizeof(*intregs));
intregs->clear = ~SUN4M_INT_MASKALL;
intregs->cpu_intregs[0].clear = ~0x17fff;
for (i = 0; i < ncpu; i++) {
PUSH((unsigned long)&intregs->cpu_intregs[i]);
fword("encode-int");
if (i != 0)
fword("encode+");
}
PUSH((unsigned long)&intregs->tbt);
fword("encode-int");
fword("encode+");
push_str("address");
fword("property");
fword("finish-device");
}
/* SMP CPU boot structure */
struct smp_cfg {
uint32_t smp_ctx;
uint32_t smp_ctxtbl;
uint32_t smp_entry;
uint32_t valid;
};
static struct smp_cfg *smp_header;
int
start_cpu(unsigned int pc, unsigned int context_ptr, unsigned int context, int cpu)
{
if (!cpu)
return -1;
cpu &= 7;
smp_header->smp_entry = pc;
smp_header->smp_ctxtbl = context_ptr;
smp_header->smp_ctx = context;
smp_header->valid = cpu;
intregs->cpu_intregs[cpu].set = SUN4M_SOFT_INT(14);
return 0;
}
static void
ob_smp_init(unsigned long mem_size)
{
// See arch/sparc32/entry.S for memory layout
smp_header = (struct smp_cfg *)ofmem_map_io((uint64_t)(mem_size - 0x100),
sizeof(struct smp_cfg));
}
static void
ob_set_obio_ranges(uint64_t base)
{
push_str("/obio");
fword("find-device");
PUSH(0);
fword("encode-int");
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH(base >> 32);
fword("encode-int");
fword("encode+");
PUSH(base & 0xffffffff);
fword("encode-int");
fword("encode+");
PUSH(SLAVIO_SIZE);
fword("encode-int");
fword("encode+");
push_str("ranges");
fword("property");
}
int
ob_obio_init(uint64_t slavio_base, unsigned long fd_offset,
unsigned long counter_offset, unsigned long intr_offset,
int intr_ncpu, unsigned long aux1_offset, unsigned long aux2_offset,
unsigned long mem_size)
{
// All devices were integrated to NCR89C105, see
// http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
//printk("Initializing OBIO devices...\n");
ob_set_obio_ranges(slavio_base);
// Zilog Z8530 serial ports, see http://www.zilog.com
// Must be before zs@0,0 or Linux won't boot
ob_zs_init(slavio_base, SLAVIO_ZS1, ZS_INTR, 0, 0);
ob_zs_init(slavio_base, SLAVIO_ZS, ZS_INTR, 1, 1);
// M48T08 NVRAM, see http://www.st.com
ob_nvram_init(slavio_base, SLAVIO_NVRAM);
// 82078 FDC
if (fd_offset != (unsigned long) -1)
ob_fd_init(slavio_base, fd_offset, FD_INTR);
ob_auxio_init(slavio_base, aux1_offset);
if (aux2_offset != (unsigned long) -1)
ob_aux2_reset_init(slavio_base, aux2_offset, AUXIO2_INTR);
ob_counter_init(slavio_base, counter_offset, intr_ncpu);
ob_interrupt_init(slavio_base, intr_offset, intr_ncpu);
ob_smp_init(mem_size);
return 0;
}