| /* |
| * 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; |
| } |