root/sys/arch/x68k/stand/boot/switch.c
/*      $NetBSD: switch.c,v 1.2 2014/08/10 07:40:50 isaki Exp $ */

/*
 * Copyright (c) 2014 Tetsuya Isaki. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <lib/libsa/stand.h>
#include <lib/libkern/libkern.h>

#include "switch.h"

#define SRAM_MEMSIZE    (*((volatile uint32_t *)0x00ed0008))
#define SRAM_ROMADDR    (*((volatile uint32_t *)0x00ed000c))
#define SRAM_RAMADDR    (*((volatile uint32_t *)0x00ed0010))
#define SRAM_BOOTDEV    (*((volatile uint16_t *)0x00ed0018))

#define SYSPORT_SRAM_WP (*((volatile uint8_t *)0x00e8e00d))

static int hextoi(const char *);
static void cmd_switch_help(void);
static void cmd_switch_show(void);
static void cmd_switch_show_boot(void);
static void cmd_switch_show_rom(void);
static void cmd_switch_show_memory(void);
static const char *romaddr_tostr(uint32_t);
static const char *get_romaddr_name(uint32_t);
static void cmd_switch_boot(const char *);
static void cmd_switch_rom(const char *);
static void cmd_switch_memory(const char *);

static inline void
sram_write_enable(void)
{
        SYSPORT_SRAM_WP = 0x31;
}

static inline void
sram_write_disable(void)
{
        SYSPORT_SRAM_WP = 0;
}

static int
hextoi(const char *in)
{
        char *c;
        int ret;

        ret = 0;
        c = (char *)in;
        for (; isxdigit(*c); c++) {
                ret = (ret * 16) +
                      (*c > '9' ? ((*c | 0x20) - 'a' + 10) : *c - '0');
        }
        return ret;
}

static void
cmd_switch_help(void)
{
        printf(
                "usage: switch <key>=<val>\n"
                "         boot=[std | inscsi<N> | exscsi<N> | fd<N> | rom ]\n"
                "         rom=[ inscsi<N> | exscsi<N> | $<addr> ]\n"
                "         memory=<1..12> (unit:MB)\n"
                "       switch show\n"
        );
}

void
cmd_switch(char *arg)
{
        char *val;

        if (strcmp(arg, "show") == 0) {
                cmd_switch_show();
                return;
        }

        val = strchr(arg, '=');
        if (val == NULL) {
                cmd_switch_help();
                return;
        }
        *val++ = '\0';

        if (strcmp(arg, "boot") == 0) {
                cmd_switch_boot(val);
        } else if (strcmp(arg, "rom") == 0) {
                cmd_switch_rom(val);
        } else if (strcmp(arg, "memory") == 0) {
                cmd_switch_memory(val);
        } else {
                cmd_switch_help();
        }
}

static void
cmd_switch_show(void)
{
        cmd_switch_show_boot();
        cmd_switch_show_rom();
        cmd_switch_show_memory();
}

static void
cmd_switch_show_boot(void)
{
        uint32_t romaddr;
        uint16_t bootdev;
        const char *name;

        bootdev = SRAM_BOOTDEV;
        romaddr = SRAM_ROMADDR;

        /*
         * $0000: std
         * $8n00: sasi<N>
         * $9n70: fd<N>
         * $a000: ROM
         * $b000: RAM
         */
        printf("boot=");
        switch (bootdev >> 12) {
        default:
        case 0x0:
                /*
                 * The real order is fd->sasi->rom->ram
                 * but it is a bit redundant..
                 */
                printf("std (fd -> ");
                name = get_romaddr_name(romaddr);
                if (name)
                        printf("%s)", name);
                else
                        printf("rom$%x)", romaddr);
                break;
        case 0x8:
                printf("sasi%d", (bootdev >> 8) & 15);
                break;
        case 0x9:
                printf("fd%d", (bootdev >> 8) & 3);
                break;
        case 0xa:
                printf("rom%s", romaddr_tostr(romaddr));
                break;
        case 0xb:
                printf("ram$%x", SRAM_RAMADDR);
                break;
        }
        printf("\n");
}

static void
cmd_switch_show_rom(void)
{
        uint32_t romaddr;

        romaddr = SRAM_ROMADDR;
        printf("rom=%s\n", romaddr_tostr(romaddr));
}

static void
cmd_switch_show_memory(void)
{
        printf("memory=%d MB\n", SRAM_MEMSIZE / (1024 * 1024));
}

/* return rom address as string with name if any */
static const char *
romaddr_tostr(uint32_t addr)
{
        static char buf[32];
        const char *name;

        name = get_romaddr_name(addr);
        if (name)
                snprintf(buf, sizeof(buf), "$%x (%s)", addr, name);
        else
                snprintf(buf, sizeof(buf), "$%x", addr);

        return buf;
}

/*
 * return "inscsiN" / "exscsiN" if addr is in range of SCSI boot.
 * Otherwise return NULL.
 */
static const char *
get_romaddr_name(uint32_t addr)
{
        static char buf[8];

        if (0xfc0000 <= addr && addr < 0xfc0020 && addr % 4 == 0) {
                snprintf(buf, sizeof(buf), "inscsi%d", (addr >> 2) & 7);
        } else if (0xea0020 <= addr && addr < 0xea0040 && addr % 4 == 0) {
                snprintf(buf, sizeof(buf), "exscsi%d", (addr >> 2) & 7);
        } else {
                return NULL;
        }
        return buf;
}

static void
cmd_switch_boot(const char *arg)
{
        int id;
        uint32_t romaddr;
        uint16_t bootdev;

        romaddr = 0xffffffff;

        if (strcmp(arg, "std") == 0) {
                bootdev = 0x0000;

        } else if (strcmp(arg, "rom") == 0) {
                bootdev = 0xa000;

        } else if (strncmp(arg, "inscsi", 6) == 0) {
                id = (arg[6] - '0') & 7;
                bootdev = 0xa000;
                romaddr = 0xfc0000 + id * 4;

        } else if (strncmp(arg, "exscsi", 6) == 0) {
                id = (arg[6] - '0') & 7;
                bootdev = 0xa000;
                romaddr = 0xea0020 + id * 4;

        } else if (strncmp(arg, "fd", 2) == 0) {
                id = (arg[2] - '0') & 3;
                bootdev = 0x9070 | (id << 8);

        } else {
                cmd_switch_help();
                return;
        }

        sram_write_enable();
        SRAM_BOOTDEV = bootdev;
        if (romaddr != 0xffffffff)
                SRAM_ROMADDR = romaddr;
        sram_write_disable();

        cmd_switch_show_boot();
}

static void
cmd_switch_rom(const char *arg)
{
        int id;
        uint32_t romaddr;

        if (strncmp(arg, "inscsi", 6) == 0) {
                id = (arg[6] - '0') & 7;
                romaddr = 0xfc0000 + id * 4;

        } else if (strncmp(arg, "exscsi", 6) == 0) {
                id = (arg[6] - '0') & 7;
                romaddr = 0xea0020 + id * 4;

        } else if (*arg == '$') {
                romaddr = hextoi(arg + 1);

        } else {
                cmd_switch_help();
                return;
        }

        sram_write_enable();
        SRAM_ROMADDR = romaddr;
        sram_write_disable();

        cmd_switch_show_rom();
}

static void
cmd_switch_memory(const char *arg)
{
        int num;

        num = atoi(arg);
        if (num < 1 || num > 12) {
                cmd_switch_help();
                return;
        }

        sram_write_enable();
        SRAM_MEMSIZE = num * (1024 * 1024);
        sram_write_disable();

        cmd_switch_show_memory();
}