Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / drivers / escc.c
diff --git a/qemu/roms/openbios/drivers/escc.c b/qemu/roms/openbios/drivers/escc.c
new file mode 100644 (file)
index 0000000..240043b
--- /dev/null
@@ -0,0 +1,476 @@
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+#include "drivers/drivers.h"
+#include "libopenbios/ofmem.h"
+
+#include "escc.h"
+
+/* ******************************************************************
+ *                       serial console functions
+ * ****************************************************************** */
+
+static volatile unsigned char *escc_serial_dev;
+
+#define CTRL(addr) (*(volatile unsigned char *)(uintptr_t)(addr))
+#ifdef CONFIG_DRIVER_ESCC_SUN
+#define DATA(addr) (*(volatile unsigned char *)(uintptr_t)(addr + 2))
+#else
+#define DATA(addr) (*(volatile unsigned char *)(uintptr_t)(addr + 16))
+#endif
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+#ifdef CONFIG_DRIVER_ESCC_SUN
+#define ESCC_CLOCK              4915200 /* Zilog input clock rate. */
+#else
+#define ESCC_CLOCK              3686400
+#endif
+#define ESCC_CLOCK_DIVISOR      16      /* Divisor this driver uses. */
+
+/* Write Register 3 */
+#define RxENAB          0x1     /* Rx Enable */
+#define Rx8             0xc0    /* Rx 8 Bits/Character */
+
+/* Write Register 4 */
+#define SB1             0x4     /* 1 stop bit/char */
+#define X16CLK          0x40    /* x16 clock mode */
+
+/* Write Register 5 */
+#define RTS             0x2     /* RTS */
+#define TxENAB          0x8     /* Tx Enable */
+#define Tx8             0x60    /* Tx 8 bits/character */
+#define DTR             0x80    /* DTR */
+
+/* Write Register 14 (Misc control bits) */
+#define BRENAB  1       /* Baud rate generator enable */
+#define BRSRC   2       /* Baud rate generator source */
+
+/* Read Register 0 */
+#define Rx_CH_AV        0x1     /* Rx Character Available */
+#define Tx_BUF_EMP      0x4     /* Tx Buffer empty */
+
+int escc_uart_charav(uintptr_t port)
+{
+    return (CTRL(port) & Rx_CH_AV) != 0;
+}
+
+char escc_uart_getchar(uintptr_t port)
+{
+    while (!escc_uart_charav(port))
+        ;
+    return DATA(port) & 0177;
+}
+
+static void escc_uart_port_putchar(uintptr_t port, unsigned char c)
+{
+    if (!escc_serial_dev)
+        return;
+
+    if (c == '\n')
+        escc_uart_port_putchar(port, '\r');
+    while (!(CTRL(port) & Tx_BUF_EMP))
+        ;
+    DATA(port) = c;
+}
+
+static void uart_init_line(volatile unsigned char *port, unsigned long baud)
+{
+    CTRL(port) = 4; // reg 4
+    CTRL(port) = SB1 | X16CLK; // no parity, async, 1 stop bit, 16x
+                               // clock
+
+    baud = BPS_TO_BRG(baud, ESCC_CLOCK / ESCC_CLOCK_DIVISOR);
+
+    CTRL(port) = 12; // reg 12
+    CTRL(port) = baud & 0xff;
+    CTRL(port) = 13; // reg 13
+    CTRL(port) = (baud >> 8) & 0xff;
+    CTRL(port) = 14; // reg 14
+    CTRL(port) = BRSRC | BRENAB;
+
+    CTRL(port) = 3; // reg 3
+    CTRL(port) = RxENAB | Rx8; // enable rx, 8 bits/char
+
+    CTRL(port) = 5; // reg 5
+    CTRL(port) = RTS | TxENAB | Tx8 | DTR; // enable tx, 8 bits/char,
+                                           // set RTS & DTR
+
+}
+
+int escc_uart_init(phys_addr_t port, unsigned long speed)
+{
+#ifdef CONFIG_DRIVER_ESCC_SUN
+    escc_serial_dev = (unsigned char *)ofmem_map_io(port & ~7ULL, ZS_REGS);
+    escc_serial_dev += port & 7ULL;
+#else
+    escc_serial_dev = (unsigned char *)(uintptr_t)port;
+#endif
+    uart_init_line(escc_serial_dev, speed);
+    return -1;
+}
+
+void escc_uart_putchar(int c)
+{
+    escc_uart_port_putchar((uintptr_t)escc_serial_dev, (unsigned char) (c & 0xff));
+}
+
+void serial_cls(void)
+{
+    escc_uart_putchar(27);
+    escc_uart_putchar('[');
+    escc_uart_putchar('H');
+    escc_uart_putchar(27);
+    escc_uart_putchar('[');
+    escc_uart_putchar('J');
+}
+
+/* ( addr len -- actual ) */
+static void
+escc_read(ucell *address)
+{
+    char *addr;
+    int len;
+
+    len = POP();
+    addr = (char *)cell2pointer(POP());
+
+    if (len < 1)
+        printk("escc_read: bad len, addr %p len %x\n", addr, len);
+
+    if (escc_uart_charav(*address)) {
+        *addr = (char)escc_uart_getchar(*address);
+        PUSH(1);
+    } else {
+        PUSH(0);
+    }
+}
+
+/* ( addr len -- actual ) */
+static void
+escc_write(ucell *address)
+{
+    unsigned char *addr;
+    int i, len;
+
+    len = POP();
+    addr = (unsigned char *)cell2pointer(POP());
+
+    for (i = 0; i < len; i++) {
+        escc_uart_port_putchar(*address, addr[i]);
+    }
+    PUSH(len);
+}
+
+static void
+escc_close(void)
+{
+}
+
+static void
+escc_open(ucell *address)
+{
+#ifdef CONFIG_DRIVER_ESCC_SUN
+    int len;
+    phandle_t ph;
+    unsigned long *prop;
+    char *args;
+
+    fword("my-self");
+    fword("ihandle>phandle");
+    ph = (phandle_t)POP();
+    prop = (unsigned long *)get_property(ph, "address", &len);
+    *address = *prop;
+    fword("my-args");
+    args = pop_fstr_copy();
+    if (args) {
+        if (args[0] == 'a')
+            *address += 4;
+        //printk("escc_open: address %lx, args %s\n", *address, args);
+        free(args);
+    }
+#else
+    *address = (unsigned long)escc_serial_dev; // XXX
+#endif
+    RET ( -1 );
+}
+
+DECLARE_UNNAMED_NODE(escc, INSTALL_OPEN, sizeof(ucell));
+
+NODE_METHODS(escc) = {
+    { "open",               escc_open              },
+    { "close",              escc_close             },
+    { "read",               escc_read              },
+    { "write",              escc_write             },
+};
+
+#ifdef CONFIG_DRIVER_ESCC_SUN
+static volatile unsigned char *kbd_dev;
+
+void kbd_init(phys_addr_t base)
+{
+    kbd_dev = (unsigned char *)ofmem_map_io(base, 2 * 4);
+    kbd_dev += 4;
+}
+
+static const unsigned char sunkbd_keycode[128] = {
+    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0,
+    '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 8,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
+    'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\\', 13,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    ' ',
+};
+
+static const unsigned char sunkbd_keycode_shifted[128] = {
+    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0,
+    '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 8,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
+    'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}',
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '|', 13,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    ' ',
+};
+
+static int shiftstate;
+
+int
+keyboard_dataready(void)
+{
+    return ((kbd_dev[0] & 1) == 1);
+}
+
+unsigned char
+keyboard_readdata(void)
+{
+    unsigned char ch;
+
+    while (!keyboard_dataready()) { }
+
+    do {
+        ch = kbd_dev[2] & 0xff;
+        if (ch == 99)
+            shiftstate |= 1;
+        else if (ch == 110)
+            shiftstate |= 2;
+        else if (ch == 227)
+            shiftstate &= ~1;
+        else if (ch == 238)
+            shiftstate &= ~2;
+        //printk("getch: %d\n", ch);
+    } // If release, wait for key press
+    while ((ch & 0x80) == 0x80 || ch == 238 || ch == 227);
+    //printk("getch rel: %d\n", ch);
+    ch &= 0x7f;
+    if (shiftstate)
+        ch = sunkbd_keycode_shifted[ch];
+    else
+        ch = sunkbd_keycode[ch];
+    //printk("getch xlate: %d\n", ch);
+
+    return ch;
+}
+
+/* ( addr len -- actual ) */
+static void
+escc_read_keyboard(void)
+{
+    unsigned char *addr;
+    int len;
+
+    len = POP();
+    addr = (unsigned char *)POP();
+
+    if (len < 1)
+        printk("escc_read: bad len, addr %p len %x\n", addr, len);
+
+    if (keyboard_dataready()) {
+        *addr = keyboard_readdata();
+        PUSH(1);
+    } else {
+        PUSH(0);
+    }
+}
+
+DECLARE_UNNAMED_NODE(escc_keyboard, INSTALL_OPEN, sizeof(ucell));
+
+NODE_METHODS(escc_keyboard) = {
+    { "open",               escc_open              },
+    { "close",              escc_close             },
+    { "read",               escc_read_keyboard     },
+};
+
+void
+ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, int keyboard)
+{
+    char nodebuff[256];
+    phandle_t aliases;
+
+    ob_new_obio_device("zs", "serial");
+
+    ob_reg(base, offset, ZS_REGS, 1);
+
+    PUSH(slave);
+    fword("encode-int");
+    push_str("slave");
+    fword("property");
+
+    if (keyboard) {
+        PUSH(0);
+        PUSH(0);
+        push_str("keyboard");
+        fword("property");
+
+        PUSH(0);
+        PUSH(0);
+        push_str("mouse");
+        fword("property");
+    }
+
+    ob_intr(intr);
+
+    PUSH(0);
+    PUSH(0);
+    push_str("port-a-ignore-cd");
+    fword("property");
+
+    PUSH(0);
+    PUSH(0);
+    push_str("port-b-ignore-cd");
+    fword("property");
+
+    fword("finish-device");
+
+    snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x",
+             (int)offset & 0xffffffff);
+    if (keyboard) {
+        REGISTER_NODE_METHODS(escc_keyboard, nodebuff);
+
+        aliases = find_dev("/aliases");
+        set_property(aliases, "keyboard", nodebuff, strlen(nodebuff) + 1);
+    } else {
+        REGISTER_NODE_METHODS(escc, nodebuff);
+
+        aliases = find_dev("/aliases");
+        snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:a",
+                 (int)offset & 0xffffffff);
+        set_property(aliases, "ttya", nodebuff, strlen(nodebuff) + 1);
+
+        snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:b",
+                 (int)offset & 0xffffffff);
+        set_property(aliases, "ttyb", nodebuff, strlen(nodebuff) + 1);
+
+    }
+}
+
+#else
+
+static void
+escc_add_channel(const char *path, const char *node, phys_addr_t addr,
+                 uint32_t offset)
+{
+    char buf[64], tty[32];
+    phandle_t dnode, aliases;
+    int len;
+    cell props[2];
+
+    /* add device */
+
+    snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
+
+    REGISTER_NAMED_NODE(escc, buf);
+
+    activate_device(buf);
+
+    /* add aliases */
+
+    aliases = find_dev("/aliases");
+
+    snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
+    OLDWORLD(snprintf(tty, sizeof(tty), "tty%s", node));
+    OLDWORLD(set_property(aliases, tty, buf, strlen(buf) + 1));
+    snprintf(tty, sizeof(tty), "scc%s", node);
+    set_property(aliases, tty, buf, strlen(buf) + 1);
+
+    /* add properties */
+
+    dnode = find_dev(buf);
+    set_property(dnode, "device_type", "serial",
+                 strlen("serial") + 1);
+
+    snprintf(buf, sizeof(buf), "ch-%s", node);
+    len = strlen(buf) + 1;
+    snprintf(buf + len, sizeof(buf) - len, "CHRP,es2");
+    set_property(dnode, "compatible", buf, len + 9);
+
+    props[0] = IO_ESCC_OFFSET + offset * 0x20;
+    props[1] = 0x00000020;
+    set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell));
+
+    props[0] = addr + IO_ESCC_OFFSET + offset * 0x20;
+    OLDWORLD(set_property(dnode, "AAPL,address",
+            (char *)&props, 1 * sizeof(cell)));
+
+    props[0] = 0x00000010 - offset;
+    OLDWORLD(set_property(dnode, "AAPL,interrupts",
+            (char *)&props, 1 * sizeof(cell)));
+
+    props[0] = (0x24) + offset;
+    props[1] = 0;
+    NEWWORLD(set_property(dnode, "interrupts",
+             (char *)&props, 2 * sizeof(cell)));
+
+    device_end();
+
+    uart_init_line((unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20,
+                   CONFIG_SERIAL_SPEED);
+}
+
+void
+escc_init(const char *path, phys_addr_t addr)
+{
+    char buf[64];
+    int props[2];
+    phandle_t dnode;
+
+    push_str(path);
+    fword("find-device");
+    fword("new-device");
+
+    push_str("escc");
+    fword("device-name");
+
+    snprintf(buf, sizeof(buf), "%s/escc", path);
+
+    dnode = find_dev(buf);
+
+    set_int_property(dnode, "#address-cells", 1);
+    props[0] = __cpu_to_be32(IO_ESCC_OFFSET);
+    props[1] = __cpu_to_be32(IO_ESCC_SIZE);
+    set_property(dnode, "reg", (char *)&props, sizeof(props));
+    set_property(dnode, "device_type", "escc",
+                 strlen("escc") + 1);
+    set_property(dnode, "compatible", "escc\0CHRP,es0", 14);
+
+    fword("finish-device");
+
+    escc_add_channel(buf, "a", addr, 1);
+    escc_add_channel(buf, "b", addr, 0);
+
+    escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET +
+                 (CONFIG_SERIAL_PORT ? 0 : 0x20);
+}
+#endif