diff options
Diffstat (limited to 'qemu/roms/openbios/drivers')
54 files changed, 17035 insertions, 0 deletions
diff --git a/qemu/roms/openbios/drivers/Kconfig b/qemu/roms/openbios/drivers/Kconfig new file mode 100644 index 000000000..3bebc0293 --- /dev/null +++ b/qemu/roms/openbios/drivers/Kconfig @@ -0,0 +1,59 @@ + + +menu "Drivers" + +config DRIVER_PCI + bool "PCI driver" + default y + help + Builtin PCI driver + +config DEBUG_PCI + bool "Debug PCI driver" + default n + help + Debug PCI driver + +config DRIVER_IDE + depends X86 || AMD64 || PPC + bool "Legacy IDE" + default y + help + If you want to be able to boot from IDE, enable this option. + +config IDE_NUM_CHANNELS + depends DRIVER_IDE + int "Number of IDE channels to be probed" + default 4 + help + Number of IDE channels to be probed. This should be set to + one or two if you build OpenBIOS for the Total Impact BRIQ. + +config DEBUG_IDE + depends DRIVER_IDE + bool "Debug IDE driver" + default n + help + Debug IDE driver + +config DRIVER_USB + bool "USB Support" + default n + help + If you want to be able to use USB devices, enable this option. + +config DEBUG_USB + depends DRIVER_USB + bool "Debug USB driver" + default n + help + Debug USB driver + +config USB_HID + depends DRIVER_USB + bool "USB driver for HID devices" + default n + help + If you want to be able to use USB keyboard, enable this option. + +endmenu diff --git a/qemu/roms/openbios/drivers/adb_bus.c b/qemu/roms/openbios/drivers/adb_bus.c new file mode 100644 index 000000000..d67d1f1a4 --- /dev/null +++ b/qemu/roms/openbios/drivers/adb_bus.c @@ -0,0 +1,257 @@ +/* + * + * Open Hack'Ware BIOS ADB bus support, ported to OpenBIOS + * + * Copyright (c) 2005 Jocelyn Mayer + * Copyright (c) 2005 Stefan Reinauer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "libc/vsprintf.h" + +#include "adb_bus.h" +#include "adb_kbd.h" +#include "adb_mouse.h" + +DECLARE_UNNAMED_NODE( adb, INSTALL_OPEN, sizeof(int)); + +static void +adb_initialize (int *idx) +{ + phandle_t ph=get_cur_dev(); + + push_str("adb"); + fword("device-type"); + + set_property(ph, "compatible", "adb", 4); + set_int_property(ph, "#address-cells", 1); + set_int_property(ph, "#size-cells", 0); +} + +static void +adb_open(int *idx) +{ + RET(-1); +} + +static void +adb_close(int *idx) +{ +} + +NODE_METHODS( adb ) = { + { NULL, adb_initialize }, + { "open", adb_open }, + { "close", adb_close }, +}; + +adb_bus_t *adb_bus_new (void *host, + int (*req)(void *host, const uint8_t *snd_buf, + int len, uint8_t *rcv_buf)) +{ + adb_bus_t *new; + + new = malloc(sizeof(adb_bus_t)); + if (new == NULL) + return NULL; + new->host = host; + new->req = req; + + return new; +} + +/* Check and relocate all ADB devices as suggested in + * ADB_manager Apple documentation + */ + +int adb_bus_init (char *path, adb_bus_t *bus) +{ + char buf[64]; + uint8_t buffer[ADB_BUF_SIZE]; + uint8_t adb_addresses[16] = + { 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, 0, }; + adb_dev_t tmp_device, **cur; + int address; + int reloc = 0, next_free = 7; + int keep; + + snprintf(buf, sizeof(buf), "%s/adb", path); + REGISTER_NAMED_NODE( adb, buf); + /* Reset the bus */ + // ADB_DPRINTF("\n"); + adb_reset(bus); + cur = &bus->devices; + memset(&tmp_device, 0, sizeof(adb_dev_t)); + tmp_device.bus = bus; + for (address = 1; address < 8 && adb_addresses[reloc] > 0;) { + if (address == ADB_RES) { + /* Reserved */ + address++; + continue; + } + //ADB_DPRINTF("Check device on ADB address %d\n", address); + tmp_device.addr = address; + switch (adb_reg_get(&tmp_device, 3, buffer)) { + case 0: + //ADB_DPRINTF("No device on ADB address %d\n", address); + /* Register this address as free */ + if (adb_addresses[next_free] != 0) + adb_addresses[next_free++] = address; + /* Check next ADB address */ + address++; + break; + case 2: + /* One device answered : + * make it available and relocate it to a free address + */ + if (buffer[0] == ADB_CHADDR) { + /* device self test failed */ + ADB_DPRINTF("device on ADB address %d self-test failed " + "%02x %02x %02x\n", address, + buffer[0], buffer[1], buffer[2]); + keep = 0; + } else { + //ADB_DPRINTF("device on ADB address %d self-test OK\n", + // address); + keep = 1; + } + ADB_DPRINTF("Relocate device on ADB address %d to %d (%d)\n", + address, adb_addresses[reloc], reloc); + buffer[0] = ((buffer[0] & 0x40) & ~0x90) | adb_addresses[reloc]; + if (keep == 1) + buffer[0] |= 0x20; + buffer[1] = ADB_CHADDR_NOCOLL; + if (adb_reg_set(&tmp_device, 3, buffer, 2) < 0) { + ADB_DPRINTF("ADB device relocation failed\n"); + return -1; + } + if (keep == 1) { + *cur = malloc(sizeof(adb_dev_t)); + if (*cur == NULL) { + return -1; + } + (*cur)->type = address; + (*cur)->bus = bus; + (*cur)->addr = adb_addresses[reloc++]; + /* Flush buffers */ + adb_flush(*cur); + switch ((*cur)->type) { + case ADB_PROTECT: + ADB_DPRINTF("Found one protected device\n"); + break; + case ADB_KEYBD: + ADB_DPRINTF("Found one keyboard on address %d\n", address); + adb_kbd_new(buf, *cur); + break; + case ADB_MOUSE: + ADB_DPRINTF("Found one mouse on address %d\n", address); + adb_mouse_new(buf, *cur); + break; + case ADB_ABS: + ADB_DPRINTF("Found one absolute positioning device\n"); + break; + case ADB_MODEM: + ADB_DPRINTF("Found one modem\n"); + break; + case ADB_RES: + ADB_DPRINTF("Found one ADB res device\n"); + break; + case ADB_MISC: + ADB_DPRINTF("Found one ADB misc device\n"); + break; + } + cur = &((*cur)->next); + } + break; + case 1: + case 3 ... 7: + /* SHOULD NOT HAPPEN : register 3 is always two bytes long */ + ADB_DPRINTF("Invalid returned len for ADB register 3\n"); + return -1; + case -1: + /* ADB ERROR */ + ADB_DPRINTF("error gettting ADB register 3\n"); + return -1; + } + } + + return 0; +} + +int adb_cmd (adb_dev_t *dev, uint8_t cmd, uint8_t reg, + uint8_t *buf, int len) +{ + uint8_t adb_send[ADB_BUF_SIZE], adb_rcv[ADB_BUF_SIZE]; + + //ADB_DPRINTF("cmd: %d reg: %d len: %d\n", cmd, reg, len); + if (dev->bus == NULL || dev->bus->req == NULL) { + ADB_DPRINTF("ERROR: invalid bus !\n"); + for (;;); + } + /* Sanity checks */ + if (cmd != ADB_LISTEN && len != 0) { + /* No buffer transmitted but for LISTEN command */ + ADB_DPRINTF("in buffer for cmd %d\n", cmd); + return -1; + } + if (cmd == ADB_LISTEN && ((len < 2 || len > 8) || buf == NULL)) { + /* Need a buffer with a regular register size for LISTEN command */ + ADB_DPRINTF("no/invalid buffer for ADB_LISTEN (%d)\n", len); + return -1; + } + if ((cmd == ADB_TALK || cmd == ADB_LISTEN) && reg > 3) { + /* Need a valid register number for LISTEN and TALK commands */ + ADB_DPRINTF("invalid reg for TALK/LISTEN command (%d %d)\n", cmd, reg); + return -1; + } + switch (cmd) { + case ADB_SEND_RESET: + adb_send[0] = ADB_SEND_RESET; + break; + case ADB_FLUSH: + adb_send[0] = (dev->addr << 4) | ADB_FLUSH; + break; + case ADB_LISTEN: + memcpy(adb_send + 1, buf, len); + /* No break here */ + case ADB_TALK: + adb_send[0] = (dev->addr << 4) | cmd | reg; + break; + } + memset(adb_rcv, 0, ADB_BUF_SIZE); + len = (*dev->bus->req)(dev->bus->host, adb_send, len + 1, adb_rcv); +#ifdef DEBUG_ADB + //printk("%x %x %x %x\n", adb_rcv[0], adb_rcv[1], adb_rcv[2], adb_rcv[3]); +#endif + switch (len) { + case 0: + /* No data */ + break; + case 2 ... 8: + /* Register transmitted */ + if (buf != NULL) + memcpy(buf, adb_rcv, len); + break; + default: + /* Should never happen */ + //ADB_DPRINTF("Cmd %d returned %d bytes !\n", cmd, len); + return -1; + } + //ADB_DPRINTF("retlen: %d\n", len); + + return len; +} diff --git a/qemu/roms/openbios/drivers/adb_bus.h b/qemu/roms/openbios/drivers/adb_bus.h new file mode 100644 index 000000000..205b37536 --- /dev/null +++ b/qemu/roms/openbios/drivers/adb_bus.h @@ -0,0 +1,104 @@ +/* + * ADB bus definitions for Open Hack'Ware + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +typedef struct adb_bus_t adb_bus_t; +typedef struct adb_dev_t adb_dev_t; + +#define ADB_BUF_SIZE 8 +struct adb_bus_t { + void *host; + int (*req)(void *host, const uint8_t *snd_buf, int len, uint8_t *rcv_buf); + adb_dev_t *devices; +}; + +struct adb_dev_t { + adb_dev_t *next; + adb_bus_t *bus; + uint8_t addr; + uint8_t type; + void *state; +}; + +#define ADB_BUF_SIZE 8 + +/* ADB commands */ +enum { + ADB_SEND_RESET = 0x00, + ADB_FLUSH = 0x01, + ADB_LISTEN = 0x08, + ADB_TALK = 0x0C, +}; +/* ADB default IDs before relocation */ +enum { + ADB_PROTECT = 0x01, + ADB_KEYBD = 0x02, + ADB_MOUSE = 0x03, + ADB_ABS = 0x04, + ADB_MODEM = 0x05, + ADB_RES = 0x06, + ADB_MISC = 0x07, +}; +/* ADB special device handlers IDs */ +enum { + ADB_CHADDR = 0x00, + ADB_CHADDR_ACTIV = 0xFD, + ADB_CHADDR_NOCOLL = 0xFE, + ADB_SELF_TEST = 0xFF, +}; + +int adb_cmd (adb_dev_t *dev, uint8_t cmd, uint8_t reg, + uint8_t *buf, int len); +void adb_bus_reset (adb_bus_t *bus); +adb_bus_t *adb_bus_new (void *host, + int (*req)(void *host, const uint8_t *snd_buf, + int len, uint8_t *rcv_buf)); +int adb_bus_init (char *path, adb_bus_t *bus); + +static inline int adb_reset (adb_bus_t *bus) +{ + adb_dev_t fake_device; + + memset(&fake_device, 0, sizeof(adb_dev_t)); + fake_device.bus = bus; + + return adb_cmd(&fake_device, ADB_SEND_RESET, 0, NULL, 0); +} + +static inline int adb_flush (adb_dev_t *dev) +{ + return adb_cmd(dev, ADB_FLUSH, 0, NULL, 0); +} + +static inline int adb_reg_get (adb_dev_t *dev, uint8_t reg, uint8_t *buf) +{ + return adb_cmd(dev, ADB_TALK, reg, buf, 0); +} + +static inline int adb_reg_set (adb_dev_t *dev, uint8_t reg, + uint8_t *buf, int len) +{ + return adb_cmd(dev, ADB_LISTEN, reg, buf, len); +} + +#ifdef DEBUG_ADB +#define ADB_DPRINTF(fmt, args...) \ +do { printk("ADB - %s: " fmt, __func__ , ##args); } while (0) +#else +#define ADB_DPRINTF(fmt, args...) do { } while (0) +#endif diff --git a/qemu/roms/openbios/drivers/adb_kbd.c b/qemu/roms/openbios/drivers/adb_kbd.c new file mode 100644 index 000000000..be6099a03 --- /dev/null +++ b/qemu/roms/openbios/drivers/adb_kbd.c @@ -0,0 +1,595 @@ +/* + * + * Open Hack'Ware BIOS ADB keyboard support, ported to OpenBIOS + * + * Copyright (c) 2005 Jocelyn Mayer + * Copyright (c) 2005 Stefan Reinauer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "libc/byteorder.h" +#include "libc/vsprintf.h" +#include "kbd.h" + +#include "adb_bus.h" +#include "adb_kbd.h" + +DECLARE_UNNAMED_NODE( keyboard, INSTALL_OPEN, sizeof(int)); + +static void +keyboard_open(int *idx) +{ + RET(-1); +} + +static void +keyboard_close(int *idx) +{ +} + +static void keyboard_read(void); +static void keyboard_getkeymap(void); + +NODE_METHODS( keyboard ) = { + { "open", keyboard_open }, + { "close", keyboard_close }, + { "read", keyboard_read }, + { "get-key-map", keyboard_getkeymap }, +}; + +/* VT100 escape sequences */ + +enum { + KEY_UP = 0, KEY_DOWN, KEY_RIGHT, KEY_LEFT, KEY_PAGE_UP, KEY_PAGE_DOWN, + KEY_DELETE, KEY_HOME, KEY_END, KEY_HELP, + KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, + KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16 +}; + +#define ADB_MAX_SEQUENCE_LEN 16 + +static const char *ADB_sequences[] = { + [KEY_UP] = "A[", + [KEY_DOWN] = "B[", + [KEY_RIGHT] = "C[", + [KEY_LEFT] = "D[", + [KEY_PAGE_UP] = "~5[", + [KEY_PAGE_DOWN] = "~6[", + [KEY_DELETE] = "~3[", + [KEY_HOME] = "HO", + [KEY_END] = "FO", + [KEY_HELP] = "~2[", + [KEY_F1] = "PO", + [KEY_F2] = "QO", + [KEY_F3] = "RO", + [KEY_F4] = "SO", + [KEY_F5] = "~15[", + [KEY_F6] = "~17[", + [KEY_F7] = "~18[", + [KEY_F8] = "~19[", + [KEY_F9] = "~20[", + [KEY_F10] = "~21[", + [KEY_F11] = "~23[", + [KEY_F12] = "~24[", + [KEY_F13] = "~25[", + [KEY_F14] = "~26[", + [KEY_F15] = "~28[", + [KEY_F15] = "~29[", +}; + +/* ADB US keyboard translation map */ + +static const keymap_t ADB_kbd_us[] = { + /* 0x00 */ + { KBD_SH_CAPS, { 0x61, 0x41, 0x01, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x73, 0x53, 0x13, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x64, 0x44, 0x04, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x66, 0x46, 0x06, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x68, 0x48, 0x08, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x67, 0x47, 0x07, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x7A, 0x5A, 0x1A, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x78, 0x58, 0x18, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + /* 0x08 */ + { KBD_SH_CAPS, { 0x63, 0x43, 0x03, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x76, 0x56, 0x16, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x60, 0x40, 0x00, -1, -1, -1, -1, -1, /* ? */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x62, 0x42, 0x02, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x71, 0x51, 0x11, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x77, 0x57, 0x17, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x65, 0x45, 0x05, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x72, 0x52, 0x12, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + /* 0x10 */ + { KBD_SH_CAPS, { 0x79, 0x59, 0x19, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x74, 0x54, 0x14, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x31, 0x21, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x32, 0x40, 0x00, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x33, 0x23, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x34, 0x24, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x36, 0x5E, 0x1E, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x35, 0x25, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + /* 0x18 */ + { KBD_SH_CAPS, { 0x3D, 0x2B, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x39, 0x28, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x37, 0x26, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x2D, 0x5F, 0x1F, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x38, 0x2A, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x30, 0x29, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x5D, 0x7D, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x6F, 0x4F, 0x0F, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + /* 0x20 */ + { KBD_SH_CAPS, { 0x75, 0x55, 0x15, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x5B, 0x7B, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x69, 0x49, 0x09, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x70, 0x50, 0x10, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_MOD_MAP(0x0D), }, + { KBD_SH_CAPS, { 0x6C, 0x4C, 0x0C, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x6A, 0x4A, 0x0A, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x27, 0x22, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + /* 0x28 */ + { KBD_SH_CAPS, { 0x6B, 0x4B, 0x0B, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x3B, 0x3A, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x5C, 0x7C, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x2C, 0x3C, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x2F, 0x3F, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x6E, 0x4E, 0x0E, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x6D, 0x4D, 0x0D, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_CAPS, { 0x2E, 0x3E, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + /* 0x30 : tab */ + { KBD_MOD_MAP(0x09), }, + /* 0x31 : space */ + { KBD_MOD_MAP(0x20), }, + /* 0x32 : '<' '>' */ + { KBD_SH_CAPS, { 0x3C, 0x3E, -1, -1, -1, -1, -1, -1, /* ? */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + /* 0x33 : backspace */ + { KBD_MOD_MAP(0x08), }, + { KBD_MAP_NONE, }, + /* 0x35 : ESC */ + { KBD_MOD_MAP(0x1B), }, + /* 0x36 : control */ + { KBD_MOD_MAP_LCTRL, }, + /* 0x37 : command */ + { KBD_MOD_MAP_LCMD, }, + /* 0x38 : left shift */ + { KBD_MOD_MAP_LSHIFT, }, + /* 0x39 : caps-lock */ + { KBD_MOD_MAP_CAPS, }, + /* 0x3A : option */ + { KBD_MOD_MAP_LOPT, }, + /* 0x3B : left */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_LEFT)), }, + /* 0x3C : right */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_RIGHT)), }, + /* 0x3D : down */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_DOWN)), }, + /* 0x3E : up */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_UP)), }, + { KBD_MAP_NONE, }, + /* 0x40 */ + { KBD_MAP_NONE, }, + { KBD_SH_NUML, { 0x7F, 0x2E, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_MAP_NONE, }, + { KBD_SH_NONE, { 0x2A, 0x2A, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_MAP_NONE, }, + { KBD_SH_NONE, { 0x2B, 0x2B, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_MAP_NONE, }, + { KBD_MOD_MAP(0x7F), }, + /* 0x48 */ + { KBD_MAP_NONE, }, + { KBD_MAP_NONE, }, + { KBD_MAP_NONE, }, + { KBD_SH_NONE, { 0x2F, 0x2F, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_MOD_MAP(0x0D), }, + { KBD_MAP_NONE, }, + { KBD_SH_NONE, { 0x2D, 0x2D, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_MAP_NONE, }, + /* 0x50 */ + { KBD_MAP_NONE, }, + { KBD_SH_NONE, { 0x3D, 0x3D, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_NUML, { -1, 0x30, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_NUML, { -1, 0x31, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_NUML, { -1, 0x32, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_NUML, { -1, 0x33, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_NUML, { -1, 0x34, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_NUML, { -1, 0x35, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + /* 0x58 */ + { KBD_SH_NUML, { -1, 0x36, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_NUML, { -1, 0x37, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_MAP_NONE, }, + { KBD_SH_NUML, { -1, 0x38, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_SH_NUML, { -1, 0x39, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, }, }, + { KBD_MAP_NONE, }, + { KBD_MOD_MAP(0x2F), }, + { KBD_MAP_NONE, }, + /* 0x60 : F5 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F5)), }, + /* 0x61 : F6 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F6)), }, + /* 0x62 : F7 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F7)), }, + /* 0x63 : F3 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F3)), }, + /* 0x64 : F8 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F8)), }, + /* 0x65 : F9 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F9)), }, + { KBD_MAP_NONE, }, + /* 0x67 : F11 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F11)), }, + /* 0x68 */ + { KBD_MAP_NONE, }, + /* 0x69 : F13 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F13)), }, + { KBD_MAP_NONE, }, + /* 0x6B : F14 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F14)), }, + { KBD_MAP_NONE, }, + /* 0x6D : F10 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F10)), }, + { KBD_MAP_NONE, }, + /* 0x6F : F12 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F12)), }, + /* 0x70 */ + { KBD_MAP_NONE, }, + /* 0x71 : F15 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F15)), }, + /* 0x72 : help */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_HELP)), }, + /* 0x73 : home */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_HOME)), }, + /* 0x74 : page up */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_PAGE_UP)), }, + /* 0x75 : del */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_DELETE)), }, + /* 0x76 : F4 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F4)), }, + /* 0x77 : end */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_END)), }, + /* 0x78 : F2 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F2)), }, + /* 0x79 : page down */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_PAGE_UP)), }, + /* 0x7A : F1 */ + { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F1)), }, + /* 0x7B : right shift */ + { KBD_MOD_MAP_RSHIFT, }, + /* 0x7C : right option */ + { KBD_MOD_MAP_ROPT, }, + /* 0x7D : right control */ + { KBD_MOD_MAP_RCTRL, }, + { KBD_MAP_NONE, }, + /* 0x7F : power */ + { KBD_MAP_NONE, }, +}; + +typedef struct adb_kbd_t adb_kbd_t; +struct adb_kbd_t { + kbd_t kbd; + int next_key; + char sequence[ADB_MAX_SEQUENCE_LEN]; + int len; + char keytable[32]; +}; + +static adb_dev_t *my_adb_dev = NULL; + +static int adb_kbd_read (void *private) +{ + uint8_t buffer[ADB_BUF_SIZE]; + adb_dev_t *dev = private; + adb_kbd_t *kbd; + int key; + int ret; + + kbd = dev->state; + + if (kbd->len > 0) { + ret = kbd->sequence[kbd->len-- - 1]; + ADB_DPRINTF("Buffered %d (%02x)\n", ret, ret); + return ret; + } + + /* Get saved state */ + ret = -1; + for (key = -1; key == -1; ) { + if (kbd->next_key != -1) { + key = kbd->next_key; + kbd->next_key = -1; + } else { + if (adb_reg_get(dev, 0, buffer) != 2) + break; + kbd->next_key = buffer[1] == 0xFF ? -1 : buffer[1]; + key = buffer[0]; + } + ret = kbd_translate_key(&kbd->kbd, key & 0x7F, key >> 7, kbd->sequence); + if (ret > 0) { + kbd->len = ret; + ret = kbd->sequence[kbd->len-- - 1]; + } + + ADB_DPRINTF("Translated %d (%02x) into %d (%02x)\n", + key, key, ret, ret); + } + + return ret; +} + + +void *adb_kbd_new (char *path, void *private) +{ + char buf[64]; + int props[1]; + phandle_t ph, aliases; + adb_kbd_t *kbd; + adb_dev_t *dev = private; + kbd = (adb_kbd_t*)malloc(sizeof(adb_kbd_t)); + if (kbd != NULL) { + memset(kbd, 0, sizeof(adb_kbd_t)); + kbd_set_keymap(&kbd->kbd, sizeof(ADB_kbd_us) / sizeof(keymap_t), + ADB_kbd_us, ADB_sequences); + kbd->next_key = -1; + kbd->len = 0; + + /* Debugging BootX: the lines below force get-key-map to report that + * cmd-V is being held down, which forces BootX to run in verbose mode + * for debugging. + * + * TODO: if we can find a mapping between the get-key-map bitmap and + * ADB scancodes, the keyboard driver should be altered to update this + * accordingly. + */ + + /* + kbd->keytable[3] = 0x40; + kbd->keytable[28] = 0x10; + */ + + dev->state = kbd; + my_adb_dev = dev; + } + + snprintf(buf, sizeof(buf), "%s/keyboard", path); + REGISTER_NAMED_NODE( keyboard, buf); + + ph = find_dev(buf); + + set_property(ph, "device_type", "keyboard", 9); + props[0] = __cpu_to_be32(dev->addr); + set_property(ph, "reg", (char *)&props, sizeof(props)); + + aliases = find_dev("/aliases"); + set_property(aliases, "adb-keyboard", buf, strlen(buf) + 1); + + return kbd; +} + +/* ( addr len -- actual ) */ +static void keyboard_read(void) +{ + char *addr; + int len, key, i; + len=POP(); + addr=(char *)cell2pointer(POP()); + + for (i = 0; i < len; i++) { + key = adb_kbd_read(my_adb_dev); + if (key == -1 || key == -2) + break; + *addr++ = (char)key; + } + PUSH(i); +} + +/* ( -- keymap ) (?) */ +/* should return a pointer to an array with 32 bytes (256 bits) */ +static void keyboard_getkeymap(void) +{ + adb_kbd_t *kbd = my_adb_dev->state; + + PUSH( pointer2cell(kbd->keytable) ); +} diff --git a/qemu/roms/openbios/drivers/adb_kbd.h b/qemu/roms/openbios/drivers/adb_kbd.h new file mode 100644 index 000000000..b219c12e7 --- /dev/null +++ b/qemu/roms/openbios/drivers/adb_kbd.h @@ -0,0 +1,22 @@ +/* + * + * Open Hack'Ware BIOS ADB keyboard support, ported to OpenBIOS + * + * Copyright (c) 2005 Jocelyn Mayer + * Copyright (c) 2005 Stefan Reinauer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +void *adb_kbd_new (char *path, void *private); diff --git a/qemu/roms/openbios/drivers/adb_mouse.c b/qemu/roms/openbios/drivers/adb_mouse.c new file mode 100644 index 000000000..b94a34420 --- /dev/null +++ b/qemu/roms/openbios/drivers/adb_mouse.c @@ -0,0 +1,67 @@ +/* + * + * Open Hack'Ware BIOS ADB mouse support, ported to OpenBIOS + * + * Copyright (c) 2005 Jocelyn Mayer + * Copyright (c) 2005 Stefan Reinauer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "libc/byteorder.h" +#include "libc/vsprintf.h" + +#include "adb_bus.h" +#include "adb_mouse.h" + +DECLARE_UNNAMED_NODE( mouse, INSTALL_OPEN, sizeof(int)); + +static void +mouse_open(int *idx) +{ + RET(-1); +} + +static void +mouse_close(int *idx) +{ +} + +NODE_METHODS( mouse ) = { + { "open", mouse_open }, + { "close", mouse_close }, +}; + +void adb_mouse_new (char *path, void *private) +{ + char buf[64]; + int props[1]; + phandle_t ph, aliases; + adb_dev_t *dev = private; + + snprintf(buf, sizeof(buf), "%s/mouse", path); + REGISTER_NAMED_NODE( mouse, buf); + + ph = find_dev(buf); + + set_property(ph, "device_type", "mouse", 6); + props[0] = __cpu_to_be32(dev->addr); + set_property(ph, "reg", (char *)&props, sizeof(props)); + set_int_property(ph, "#buttons", 3); + + aliases = find_dev("/aliases"); + set_property(aliases, "adb-mouse", buf, strlen(buf) + 1); +} diff --git a/qemu/roms/openbios/drivers/adb_mouse.h b/qemu/roms/openbios/drivers/adb_mouse.h new file mode 100644 index 000000000..1f37dac7c --- /dev/null +++ b/qemu/roms/openbios/drivers/adb_mouse.h @@ -0,0 +1,22 @@ +/* + * + * Open Hack'Ware BIOS ADB mouse support, ported to OpenBIOS + * + * Copyright (c) 2005 Jocelyn Mayer + * Copyright (c) 2005 Stefan Reinauer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +void adb_mouse_new (char *path, void *private); diff --git a/qemu/roms/openbios/drivers/build.xml b/qemu/roms/openbios/drivers/build.xml new file mode 100644 index 000000000..bd1abd358 --- /dev/null +++ b/qemu/roms/openbios/drivers/build.xml @@ -0,0 +1,41 @@ +<build> + + <library name="drivers" type="static" target="target"> + <object source="pci.c" condition="DRIVER_PCI"/> + <object source="pci_database.c" condition="DRIVER_PCI"/> + <object source="ide.c" condition="DRIVER_IDE"/> + <object source="timer.c" condition="DRIVER_IDE"/> + <object source="kbd.c" condition="DRIVER_ADB"/> + <object source="adb_bus.c" condition="DRIVER_ADB"/> + <object source="adb_kbd.c" condition="DRIVER_ADB"/> + <object source="adb_mouse.c" condition="DRIVER_ADB"/> + <object source="cuda.c" condition="DRIVER_ADB"/> + <object source="floppy.c" condition="DRIVER_FLOPPY"/> + <object source="iommu.c" condition="DRIVER_SBUS"/> + <object source="sbus.c" condition="DRIVER_SBUS"/> + <object source="esp.c" condition="DRIVER_ESP"/> + <object source="obio.c" condition="DRIVER_OBIO"/> + <object source="vga_load_regs.c" condition="DRIVER_VGA"/> + <object source="vga_set_mode.c" condition="DRIVER_VGA"/> + <object source="macio.c" condition="DRIVER_MACIO"/> + <object source="pc_kbd.c" condition="DRIVER_PC_KBD"/> + <object source="pc_serial.c" condition="DRIVER_PC_SERIAL"/> + <object source="escc.c" condition="DRIVER_ESCC"/> + <object source="fw_cfg.c" condition="DRIVER_FW_CFG"/> + <object source="usb.c" condition="DRIVER_USB"/> + <object source="usbhid.c" condition="USB_HID"/> + <object source="usbohci.c" condition="DRIVER_USB"/> + <object source="usbohci_rh.c" condition="DRIVER_USB"/> + </library> + + <dictionary name="openbios" target="forth"> + <object source="pci.fs" condition="DRIVER_PCI"/> + <object source="sbus.fs" condition="DRIVER_SBUS"/> + <object source="esp.fs" condition="DRIVER_ESP"/> + </dictionary> + + <fcode source="tcx.fs" name="QEMU,tcx.bin" condition="DRIVER_SBUS" /> + <fcode source="cgthree.fs" name="QEMU,cgthree.bin" condition="DRIVER_SBUS" /> + <fcode source="vga.fs" name="QEMU,VGA.bin" condition="DRIVER_VGA" /> + +</build> diff --git a/qemu/roms/openbios/drivers/cgthree.fs b/qemu/roms/openbios/drivers/cgthree.fs new file mode 100644 index 000000000..aa0c90cdd --- /dev/null +++ b/qemu/roms/openbios/drivers/cgthree.fs @@ -0,0 +1,197 @@ +\ +\ Fcode payload for QEMU CG3 graphics card +\ +\ This is the Forth source for an Fcode payload to initialise +\ the QEMU CG3 graphics card. +\ +\ (C) Copyright 2013 Mark Cave-Ayland +\ + +fcode-version3 + +\ +\ Instead of using fixed values for the framebuffer address and the width +\ and height, grab the ones passed in by QEMU/generated by OpenBIOS +\ + +: (find-xt) \ ( str len -- xt | -1 ) + $find if + exit + else + 2drop + -1 + then +; + +: (is-openbios) \ ( -- true | false ) + " openbios-video-width" (find-xt) -1 <> if + -1 + else + 0 + then +; + +" openbios-video-width" (find-xt) cell+ value openbios-video-width-xt +" openbios-video-height" (find-xt) cell+ value openbios-video-height-xt +" depth-bits" (find-xt) cell+ value depth-bits-xt +" line-bytes" (find-xt) cell+ value line-bytes-xt +" debug-type" (find-xt) value debug-type-xt + +: openbios-video-width + (is-openbios) if + openbios-video-width-xt @ + else + h# 400 + then +; + +: openbios-video-height + (is-openbios) if + openbios-video-height-xt @ + else + h# 300 + then +; + +: depth-bits + (is-openbios) if + depth-bits-xt @ + else + h# 8 + then +; + +: line-bytes + (is-openbios) if + line-bytes-xt @ + else + h# 400 + then +; + +: debug-type debug-type-xt execute ; + +\ +\ Registers +\ + +h# 400000 constant cg3-off-dac +h# 20 constant /cg3-off-dac + +h# 800000 constant cg3-off-fb +h# c0000 constant /cg3-off-fb + +: >cg3-reg-spec ( offset size -- encoded-reg ) + >r 0 my-address d+ my-space encode-phys r> encode-int encode+ +; + +: cg3-reg + \ A real cg3 rom appears to just map the entire region with a + \ single entry + h# 0 h# 1000000 >cg3-reg-spec + " reg" property +; + +: do-map-in ( offset size -- virt ) + >r my-space r> " map-in" $call-parent +; + +: do-map-out ( virt size ) + " map-out" $call-parent +; + +\ +\ DAC +\ + +-1 value cg3-dac +-1 value fb-addr + +: dac! ( data reg# -- ) + cg3-dac + c! +; + +external + +: color! ( r g b c# -- ) + 0 dac! ( r g b ) + swap rot ( b g r ) + 4 dac! ( b g ) + 4 dac! ( b ) + 4 dac! ( ) +; + +headerless + +\ +\ Mapping +\ + +: dac-map + cg3-off-dac /cg3-off-dac do-map-in to cg3-dac +; + +: fb-map + cg3-off-fb h# c0000 do-map-in to fb-addr +; + +: map-regs + dac-map fb-map +; + +\ +\ Installation +\ + +" cgthree" device-name +" display" device-type +" SUNW,501-1415" model + +: qemu-cg3-driver-install ( -- ) + cg3-dac -1 = if + map-regs + + \ Initial pallette taken from Sun's "Writing FCode Programs" + h# ff h# ff h# ff h# 0 color! \ Background white + h# 0 h# 0 h# 0 h# ff color! \ Foreground black + h# 64 h# 41 h# b4 h# 1 color! \ SUN-blue logo + + fb-addr to frame-buffer-adr + default-font set-font + + frame-buffer-adr encode-int " address" property + + openbios-video-width openbios-video-height over char-width / over char-height / + fb8-install + then +; + +: qemu-cg3-driver-init + + cg3-reg + + openbios-video-height encode-int " height" property + openbios-video-width encode-int " width" property + line-bytes encode-int " linebytes" property + + h# 39 encode-int 0 encode-int encode+ " intr" property + + \ Monitor sense. Some searching suggests that this is + \ 5 for 1024x768 and 7 for 1152x900 + openbios-video-width h# 480 = if + h# 7 + else + h# 5 + then + encode-int " monitor-sense" property + + " SUNW" encode-string " manufacturer" property + " ISO8859-1" encode-string " character-set" property + h# c encode-int " cursorshift" property + + ['] qemu-cg3-driver-install is-install +; + +qemu-cg3-driver-init + +end0 diff --git a/qemu/roms/openbios/drivers/cuda.c b/qemu/roms/openbios/drivers/cuda.c new file mode 100644 index 000000000..9555dea49 --- /dev/null +++ b/qemu/roms/openbios/drivers/cuda.c @@ -0,0 +1,424 @@ +#include "config.h" +#include "libopenbios/bindings.h" +#include "drivers/drivers.h" +#include "libc/byteorder.h" +#include "libc/vsprintf.h" + +#include "macio.h" +#include "cuda.h" + +//#define DEBUG_CUDA +#ifdef DEBUG_CUDA +#define CUDA_DPRINTF(fmt, args...) \ + do { printk("CUDA - %s: " fmt, __func__ , ##args); } while (0) +#else +#define CUDA_DPRINTF(fmt, args...) do { } while (0) +#endif + +#define IO_CUDA_OFFSET 0x00016000 +#define IO_CUDA_SIZE 0x00002000 + +/* VIA registers - spaced 0x200 bytes apart */ +#define RS 0x200 /* skip between registers */ +#define B 0 /* B-side data */ +#define A RS /* A-side data */ +#define DIRB (2*RS) /* B-side direction (1=output) */ +#define DIRA (3*RS) /* A-side direction (1=output) */ +#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ +#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ +#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ +#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ +#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */ +#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */ +#define SR (10*RS) /* Shift register */ +#define ACR (11*RS) /* Auxiliary control register */ +#define PCR (12*RS) /* Peripheral control register */ +#define IFR (13*RS) /* Interrupt flag register */ +#define IER (14*RS) /* Interrupt enable register */ +#define ANH (15*RS) /* A-side data, no handshake */ + +/* Bits in B data register: all active low */ +#define TREQ 0x08 /* Transfer request (input) */ +#define TACK 0x10 /* Transfer acknowledge (output) */ +#define TIP 0x20 /* Transfer in progress (output) */ + +/* Bits in ACR */ +#define SR_CTRL 0x1c /* Shift register control bits */ +#define SR_EXT 0x0c /* Shift on external clock */ +#define SR_OUT 0x10 /* Shift out if 1 */ + +/* Bits in IFR and IER */ +#define IER_SET 0x80 /* set bits in IER */ +#define IER_CLR 0 /* clear bits in IER */ +#define SR_INT 0x04 /* Shift register full/empty */ + +#define CUDA_BUF_SIZE 16 + +#define ADB_PACKET 0 +#define CUDA_PACKET 1 + +/* CUDA commands (2nd byte) */ +#define CUDA_GET_TIME 0x03 +#define CUDA_SET_TIME 0x09 +#define CUDA_POWERDOWN 0x0a +#define CUDA_RESET_SYSTEM 0x11 + +static uint8_t cuda_readb (cuda_t *dev, int reg) +{ + return *(volatile uint8_t *)(dev->base + reg); +} + +static void cuda_writeb (cuda_t *dev, int reg, uint8_t val) +{ + *(volatile uint8_t *)(dev->base + reg) = val; +} + +static void cuda_wait_irq (cuda_t *dev) +{ + int val; + +// CUDA_DPRINTF("\n"); + for(;;) { + val = cuda_readb(dev, IFR); + cuda_writeb(dev, IFR, val & 0x7f); + if (val & SR_INT) + break; + } +} + + + +static int cuda_request (cuda_t *dev, uint8_t pkt_type, const uint8_t *buf, + int buf_len, uint8_t *obuf) +{ + int i, obuf_len, val; + + cuda_writeb(dev, ACR, cuda_readb(dev, ACR) | SR_OUT); + cuda_writeb(dev, SR, pkt_type); + cuda_writeb(dev, B, cuda_readb(dev, B) & ~TIP); + if (buf) { + //CUDA_DPRINTF("Send buf len: %d\n", buf_len); + /* send 'buf' */ + for(i = 0; i < buf_len; i++) { + cuda_wait_irq(dev); + cuda_writeb(dev, SR, buf[i]); + cuda_writeb(dev, B, cuda_readb(dev, B) ^ TACK); + } + } + cuda_wait_irq(dev); + cuda_writeb(dev, ACR, cuda_readb(dev, ACR) & ~SR_OUT); + cuda_readb(dev, SR); + cuda_writeb(dev, B, cuda_readb(dev, B) | TIP | TACK); + + obuf_len = 0; + if (obuf) { + cuda_wait_irq(dev); + cuda_readb(dev, SR); + cuda_writeb(dev, B, cuda_readb(dev, B) & ~TIP); + for(;;) { + cuda_wait_irq(dev); + val = cuda_readb(dev, SR); + if (obuf_len < CUDA_BUF_SIZE) + obuf[obuf_len++] = val; + if (cuda_readb(dev, B) & TREQ) + break; + cuda_writeb(dev, B, cuda_readb(dev, B) ^ TACK); + } + cuda_writeb(dev, B, cuda_readb(dev, B) | TIP | TACK); + + cuda_wait_irq(dev); + cuda_readb(dev, SR); + } +// CUDA_DPRINTF("Got len: %d\n", obuf_len); + + return obuf_len; +} + + + +static int cuda_adb_req (void *host, const uint8_t *snd_buf, int len, + uint8_t *rcv_buf) +{ + uint8_t buffer[CUDA_BUF_SIZE], *pos; + + // CUDA_DPRINTF("len: %d %02x\n", len, snd_buf[0]); + len = cuda_request(host, ADB_PACKET, snd_buf, len, buffer); + if (len > 1 && buffer[0] == ADB_PACKET) { + pos = buffer + 2; + len -= 2; + } else { + pos = buffer + 1; + len = -1; + } + memcpy(rcv_buf, pos, len); + + return len; +} + + +DECLARE_UNNAMED_NODE(ob_cuda, INSTALL_OPEN, sizeof(int)); + +static cuda_t *main_cuda; + +static void +ppc32_reset_all(void) +{ + uint8_t cmdbuf[2], obuf[64]; + + cmdbuf[0] = CUDA_RESET_SYSTEM; + cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf); +} + +static void +ppc32_poweroff(void) +{ + uint8_t cmdbuf[2], obuf[64]; + + cmdbuf[0] = CUDA_POWERDOWN; + cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf); +} + +static void +ob_cuda_initialize (int *idx) +{ + phandle_t ph=get_cur_dev(); + int props[2]; + + push_str("via-cuda"); + fword("device-type"); + + set_int_property(ph, "#address-cells", 1); + set_int_property(ph, "#size-cells", 0); + + set_property(ph, "compatible", "cuda", 5); + + props[0] = __cpu_to_be32(IO_CUDA_OFFSET); + props[1] = __cpu_to_be32(IO_CUDA_SIZE); + + set_property(ph, "reg", (char *)&props, sizeof(props)); + + /* on newworld machines the cuda is on interrupt 0x19 */ + + props[0] = 0x19; + props[1] = 0; + NEWWORLD(set_property(ph, "interrupts", (char *)props, sizeof(props))); + NEWWORLD(set_int_property(ph, "#interrupt-cells", 2)); + + /* we emulate an oldworld hardware, so we must use + * non-standard oldworld property (needed by linux 2.6.18) + */ + + OLDWORLD(set_int_property(ph, "AAPL,interrupts", 0x12)); + + bind_func("ppc32-reset-all", ppc32_reset_all); + push_str("' ppc32-reset-all to reset-all"); + fword("eval"); +} + +static void +ob_cuda_open(int *idx) +{ + RET(-1); +} + +static void +ob_cuda_close(int *idx) +{ +} + +NODE_METHODS(ob_cuda) = { + { NULL, ob_cuda_initialize }, + { "open", ob_cuda_open }, + { "close", ob_cuda_close }, +}; + +DECLARE_UNNAMED_NODE(rtc, INSTALL_OPEN, sizeof(int)); + +static void +rtc_open(int *idx) +{ + RET(-1); +} + +/* + * get-time ( -- second minute hour day month year ) + * + */ + +static const int days_month[12] = + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static const int days_month_leap[12] = + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +static inline int is_leap(int year) +{ + return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); +} + +static void +rtc_get_time(int *idx) +{ + uint8_t cmdbuf[2], obuf[64]; + ucell second, minute, hour, day, month, year; + uint32_t now; + int current; + const int *days; + + cmdbuf[0] = CUDA_GET_TIME; + cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf); + + /* seconds since 01/01/1904 */ + + now = (obuf[3] << 24) + (obuf[4] << 16) + (obuf[5] << 8) + obuf[6]; + + second = now % 60; + now /= 60; + + minute = now % 60; + now /= 60; + + hour = now % 24; + now /= 24; + + year = now * 100 / 36525; + now -= year * 36525 / 100; + year += 1904; + + days = is_leap(year) ? days_month_leap : days_month; + + current = 0; + month = 0; + while (month < 12) { + if (now <= current + days[month]) { + break; + } + current += days[month]; + month++; + } + month++; + + day = now - current; + + PUSH(second); + PUSH(minute); + PUSH(hour); + PUSH(day); + PUSH(month); + PUSH(year); +} + +/* + * set-time ( second minute hour day month year -- ) + * + */ + +static void +rtc_set_time(int *idx) +{ + uint8_t cmdbuf[5], obuf[3]; + ucell second, minute, hour, day, month, year; + const int *days; + uint32_t now; + unsigned int nb_days; + int i; + + year = POP(); + month = POP(); + day = POP(); + hour = POP(); + minute = POP(); + second = POP(); + + days = is_leap(year) ? days_month_leap : days_month; + nb_days = (year - 1904) * 36525 / 100 + day; + for (i = 0; i < month - 1; i++) + nb_days += days[i]; + + now = (((nb_days * 24) + hour) * 60 + minute) * 60 + second; + + cmdbuf[0] = CUDA_SET_TIME; + cmdbuf[1] = now >> 24; + cmdbuf[2] = now >> 16; + cmdbuf[3] = now >> 8; + cmdbuf[4] = now; + + cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf); +} + +NODE_METHODS(rtc) = { + { "open", rtc_open }, + { "get-time", rtc_get_time }, + { "set-time", rtc_set_time }, +}; + +static void +rtc_init(char *path) +{ + phandle_t ph, aliases; + char buf[64]; + + snprintf(buf, sizeof(buf), "%s/rtc", path); + REGISTER_NAMED_NODE(rtc, buf); + + ph = find_dev(buf); + set_property(ph, "device_type", "rtc", 4); + set_property(ph, "compatible", "rtc", 4); + + aliases = find_dev("/aliases"); + set_property(aliases, "rtc", buf, strlen(buf) + 1); + +} + +static void +powermgt_init(char *path) +{ + phandle_t ph; + char buf[64]; + + snprintf(buf, sizeof(buf), "%s/power-mgt", path); + REGISTER_NAMED_NODE(rtc, buf); + + ph = find_dev(buf); + set_property(ph, "device_type", "power-mgt", 10); + set_property(ph, "compatible", "power-mgt", 10); +} + +cuda_t *cuda_init (const char *path, phys_addr_t base) +{ + cuda_t *cuda; + char buf[64]; + phandle_t aliases; + + base += IO_CUDA_OFFSET; + CUDA_DPRINTF(" base=" FMT_plx "\n", base); + cuda = malloc(sizeof(cuda_t)); + if (cuda == NULL) + return NULL; + + snprintf(buf, sizeof(buf), "%s/via-cuda", path); + REGISTER_NAMED_NODE(ob_cuda, buf); + + aliases = find_dev("/aliases"); + set_property(aliases, "via-cuda", buf, strlen(buf) + 1); + + cuda->base = base; + cuda_writeb(cuda, B, cuda_readb(cuda, B) | TREQ | TIP); +#ifdef CONFIG_DRIVER_ADB + cuda->adb_bus = adb_bus_new(cuda, &cuda_adb_req); + if (cuda->adb_bus == NULL) { + free(cuda); + return NULL; + } + adb_bus_init(buf, cuda->adb_bus); +#endif + + rtc_init(buf); + powermgt_init(buf); + + main_cuda = cuda; + + device_end(); + bind_func("poweroff", ppc32_poweroff); + + return cuda; +} diff --git a/qemu/roms/openbios/drivers/cuda.h b/qemu/roms/openbios/drivers/cuda.h new file mode 100644 index 000000000..d3818c03c --- /dev/null +++ b/qemu/roms/openbios/drivers/cuda.h @@ -0,0 +1,17 @@ +#include "adb_bus.h" + +struct cuda_t { + phys_addr_t base; + adb_bus_t *adb_bus; +}; +typedef struct cuda_t cuda_t; + +enum { + CHARDEV_KBD = 0, + CHARDEV_MOUSE, + CHARDEV_SERIAL, + CHARDEV_DISPLAY, + CHARDEV_LAST, +}; + +cuda_t *cuda_init (const char *path, phys_addr_t base); diff --git a/qemu/roms/openbios/drivers/escc.c b/qemu/roms/openbios/drivers/escc.c new file mode 100644 index 000000000..240043be3 --- /dev/null +++ b/qemu/roms/openbios/drivers/escc.c @@ -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 diff --git a/qemu/roms/openbios/drivers/escc.h b/qemu/roms/openbios/drivers/escc.h new file mode 100644 index 000000000..caaf00d40 --- /dev/null +++ b/qemu/roms/openbios/drivers/escc.h @@ -0,0 +1,9 @@ + +#define IO_ESCC_SIZE 0x00001000 +#define IO_ESCC_OFFSET 0x00013000 + +#define ZS_REGS 8 + +void escc_init(const char *path, phys_addr_t addr); +void ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, + int keyboard); diff --git a/qemu/roms/openbios/drivers/esp.c b/qemu/roms/openbios/drivers/esp.c new file mode 100644 index 000000000..ad3db280d --- /dev/null +++ b/qemu/roms/openbios/drivers/esp.c @@ -0,0 +1,598 @@ +/* + * OpenBIOS ESP driver + * + * Copyright (C) 2004 Jens Axboe <axboe@suse.de> + * Copyright (C) 2005 Stefan Reinauer <stepan@openbios.org> + * + * Credit goes to Hale Landis for his excellent ata demo software + * OF node handling and some fixes by Stefan Reinauer + * + * 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 "asm/io.h" +#include "scsi.h" +#include "asm/dma.h" +#include "esp.h" +#include "libopenbios/ofmem.h" + +#define BUFSIZE 4096 + +#ifdef CONFIG_DEBUG_ESP +#define DPRINTF(fmt, args...) \ + do { printk(fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +struct esp_dma { + volatile struct sparc_dma_registers *regs; + enum dvma_rev revision; +}; + +typedef struct sd_private { + unsigned int bs; + const char *media_str[2]; + uint32_t sectors; + uint8_t media; + uint8_t id; + uint8_t present; + char model[40]; +} sd_private_t; + +struct esp_regs { + unsigned char regs[ESP_REG_SIZE]; +}; + +typedef struct esp_private { + volatile struct esp_regs *ll; + uint32_t buffer_dvma; + unsigned int irq; /* device IRQ number */ + struct esp_dma espdma; + unsigned char *buffer; + sd_private_t sd[8]; +} esp_private_t; + +static esp_private_t *global_esp; + +/* DECLARE data structures for the nodes. */ +DECLARE_UNNAMED_NODE(ob_sd, INSTALL_OPEN, sizeof(sd_private_t *)); +DECLARE_UNNAMED_NODE(ob_esp, INSTALL_OPEN, sizeof(esp_private_t *)); + +#ifdef CONFIG_DEBUG_ESP +static void dump_drive(sd_private_t *drive) +{ + printk("SCSI DRIVE @%lx:\n", (unsigned long)drive); + printk("id: %d\n", drive->id); + printk("media: %s\n", drive->media_str[0]); + printk("media: %s\n", drive->media_str[1]); + printk("model: %s\n", drive->model); + printk("sectors: %d\n", drive->sectors); + printk("present: %d\n", drive->present); + printk("bs: %d\n", drive->bs); +} +#endif + +static int +do_command(esp_private_t *esp, sd_private_t *sd, int cmdlen, int replylen) +{ + int status; + + // Set SCSI target + esp->ll->regs[ESP_BUSID] = sd->id & 7; + // Set DMA address + esp->espdma.regs->st_addr = esp->buffer_dvma; + // Set DMA length + esp->ll->regs[ESP_TCLOW] = cmdlen & 0xff; + esp->ll->regs[ESP_TCMED] = (cmdlen >> 8) & 0xff; + // Set DMA direction and enable DMA + esp->espdma.regs->cond_reg = DMA_ENABLE; + // Set ATN, issue command + esp->ll->regs[ESP_CMD] = ESP_CMD_SELA | ESP_CMD_DMA; + // Wait for DMA to complete. Can this fail? + while ((esp->espdma.regs->cond_reg & DMA_HNDL_INTR) == 0) /* no-op */; + // Check status + status = esp->ll->regs[ESP_STATUS]; + // Clear interrupts to avoid guests seeing spurious interrupts + (void)esp->ll->regs[ESP_INTRPT]; + + DPRINTF("do_command: id %d, cmd[0] 0x%x, status 0x%x\n", sd->id, esp->buffer[1], status); + + /* Target didn't want all command data? */ + if ((status & ESP_STAT_TCNT) != ESP_STAT_TCNT) { + return status; + } + if (replylen == 0) { + return 0; + } + /* Target went to status phase instead of data phase? */ + if ((status & ESP_STAT_PMASK) == ESP_STATP) { + return status; + } + + // Get reply + // Set DMA address + esp->espdma.regs->st_addr = esp->buffer_dvma; + // Set DMA length + esp->ll->regs[ESP_TCLOW] = replylen & 0xff; + esp->ll->regs[ESP_TCMED] = (replylen >> 8) & 0xff; + // Set DMA direction + esp->espdma.regs->cond_reg = DMA_ST_WRITE | DMA_ENABLE; + // Transfer + esp->ll->regs[ESP_CMD] = ESP_CMD_TI | ESP_CMD_DMA; + // Wait for DMA to complete + while ((esp->espdma.regs->cond_reg & DMA_HNDL_INTR) == 0) /* no-op */; + // Check status + status = esp->ll->regs[ESP_STATUS]; + // Clear interrupts to avoid guests seeing spurious interrupts + (void)esp->ll->regs[ESP_INTRPT]; + + DPRINTF("do_command_reply: status 0x%x\n", status); + + if ((status & ESP_STAT_TCNT) != ESP_STAT_TCNT) + return status; + else + return 0; // OK +} + +// offset is in sectors +static int +ob_sd_read_sector(esp_private_t *esp, sd_private_t *sd, int offset) +{ + DPRINTF("ob_sd_read_sector id %d sector=%d\n", + sd->id, offset); + + // Setup command = Read(10) + memset(esp->buffer, 0, 11); + esp->buffer[0] = 0x80; + esp->buffer[1] = READ_10; + + esp->buffer[3] = (offset >> 24) & 0xff; + esp->buffer[4] = (offset >> 16) & 0xff; + esp->buffer[5] = (offset >> 8) & 0xff; + esp->buffer[6] = offset & 0xff; + + esp->buffer[8] = 0; + esp->buffer[9] = 1; + + if (do_command(esp, sd, 11, sd->bs)) + return 0; + + return 0; +} + +static unsigned int +read_capacity(esp_private_t *esp, sd_private_t *sd) +{ + // Setup command = Read Capacity + memset(esp->buffer, 0, 11); + esp->buffer[0] = 0x80; + esp->buffer[1] = READ_CAPACITY; + + if (do_command(esp, sd, 11, 8)) { + sd->sectors = 0; + sd->bs = 0; + DPRINTF("read_capacity id %d failed\n", sd->id); + return 0; + } + sd->bs = (esp->buffer[4] << 24) | (esp->buffer[5] << 16) | (esp->buffer[6] << 8) | esp->buffer[7]; + sd->sectors = ((esp->buffer[0] << 24) | (esp->buffer[1] << 16) | (esp->buffer[2] << 8) | esp->buffer[3]) * (sd->bs / 512); + + DPRINTF("read_capacity id %d bs %d sectors %d\n", sd->id, sd->bs, + sd->sectors); + return 1; +} + +static unsigned int +test_unit_ready(esp_private_t *esp, sd_private_t *sd) +{ + /* Setup command = Test Unit Ready */ + memset(esp->buffer, 0, 7); + esp->buffer[0] = 0x80; + esp->buffer[1] = TEST_UNIT_READY; + + if (do_command(esp, sd, 7, 0)) { + DPRINTF("test_unit_ready id %d failed\n", sd->id); + return 0; + } + + DPRINTF("test_unit_ready id %d success\n", sd->id); + return 1; +} + +static unsigned int +inquiry(esp_private_t *esp, sd_private_t *sd) +{ + const char *media[2] = { "UNKNOWN", "UNKNOWN"}; + + // Setup command = Inquiry + memset(esp->buffer, 0, 7); + esp->buffer[0] = 0x80; + esp->buffer[1] = INQUIRY; + + esp->buffer[5] = 36; + + if (do_command(esp, sd, 7, 36)) { + sd->present = 0; + sd->media = -1; + return 0; + } + sd->present = 1; + sd->media = esp->buffer[0]; + + switch (sd->media) { + case TYPE_DISK: + media[0] = "disk"; + media[1] = "hd"; + break; + case TYPE_ROM: + media[0] = "cdrom"; + media[1] = "cd"; + break; + } + sd->media_str[0] = media[0]; + sd->media_str[1] = media[1]; + memcpy(sd->model, &esp->buffer[16], 16); + sd->model[17] = '\0'; + + return 1; +} + + +static void +ob_sd_read_blocks(sd_private_t **sd) +{ + cell n = POP(), cnt = n; + ucell blk = POP(); + char *dest = (char*)POP(); + int pos, spb, sect_offset; + + DPRINTF("ob_sd_read_blocks id %d %lx block=%d n=%d\n", (*sd)->id, (unsigned long)dest, blk, n ); + + if ((*sd)->bs == 0) { + PUSH(0); + return; + } + spb = (*sd)->bs / 512; + while (n) { + sect_offset = blk / spb; + pos = (blk - sect_offset * spb) * 512; + + if (ob_sd_read_sector(global_esp, *sd, sect_offset)) { + DPRINTF("ob_sd_read_blocks: error\n"); + RET(0); + } + while (n && pos < spb * 512) { + memcpy(dest, global_esp->buffer + pos, 512); + pos += 512; + dest += 512; + n--; + blk++; + } + } + PUSH(cnt); +} + +static void +ob_sd_block_size(__attribute__((unused))sd_private_t **sd) +{ + PUSH(512); +} + +static void +ob_sd_open(__attribute__((unused))sd_private_t **sd) +{ + int ret = 1, id; + phandle_t ph; + + fword("my-unit"); + id = POP(); + POP(); // unit id is 2 ints but we only need one. + *sd = &global_esp->sd[id]; + +#ifdef CONFIG_DEBUG_ESP + { + char *args; + + fword("my-args"); + args = pop_fstr_copy(); + DPRINTF("opening drive %d args %s\n", id, args); + free(args); + } +#endif + + selfword("open-deblocker"); + + /* interpose disk-label */ + ph = find_dev("/packages/disk-label"); + fword("my-args"); + PUSH_ph( ph ); + fword("interpose"); + + RET ( -ret ); +} + +static void +ob_sd_close(__attribute__((unused)) sd_private_t **sd) +{ + selfword("close-deblocker"); +} + +NODE_METHODS(ob_sd) = { + { "open", ob_sd_open }, + { "close", ob_sd_close }, + { "read-blocks", ob_sd_read_blocks }, + { "block-size", ob_sd_block_size }, +}; + + +static int +espdma_init(unsigned int slot, uint64_t base, unsigned long offset, + struct esp_dma *espdma) +{ + espdma->regs = (void *)ofmem_map_io(base + (uint64_t)offset, 0x10); + + if (espdma->regs == NULL) { + DPRINTF("espdma_init: cannot map registers\n"); + return -1; + } + + DPRINTF("dma1: "); + + switch ((espdma->regs->cond_reg) & DMA_DEVICE_ID) { + case DMA_VERS0: + espdma->revision = dvmarev0; + DPRINTF("Revision 0 "); + break; + case DMA_ESCV1: + espdma->revision = dvmaesc1; + DPRINTF("ESC Revision 1 "); + break; + case DMA_VERS1: + espdma->revision = dvmarev1; + DPRINTF("Revision 1 "); + break; + case DMA_VERS2: + espdma->revision = dvmarev2; + DPRINTF("Revision 2 "); + break; + case DMA_VERHME: + espdma->revision = dvmahme; + DPRINTF("HME DVMA gate array "); + break; + case DMA_VERSPLUS: + espdma->revision = dvmarevplus; + DPRINTF("Revision 1 PLUS "); + break; + default: + DPRINTF("unknown dma version %x", + (espdma->regs->cond_reg) & DMA_DEVICE_ID); + /* espdma->allocated = 1; */ + break; + } + DPRINTF("\n"); + + push_str("/iommu/sbus/espdma"); + fword("find-device"); + + /* set reg */ + PUSH(slot); + fword("encode-int"); + PUSH(offset); + fword("encode-int"); + fword("encode+"); + PUSH(0x00000010); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + return 0; +} + +static void +ob_esp_initialize(__attribute__((unused)) esp_private_t **esp) +{ + phandle_t ph = get_cur_dev(); + + set_int_property(ph, "#address-cells", 2); + set_int_property(ph, "#size-cells", 0); + + /* set device type */ + push_str("scsi"); + fword("device-type"); + + /* QEMU's ESP emulation does not support mixing DMA and FIFO messages. By + setting this attribute, we prevent the Solaris ESP kernel driver from + trying to use this feature when booting a disk image (and failing) */ + PUSH(0x58); + fword("encode-int"); + push_str("scsi-options"); + fword("property"); + + PUSH(0x24); + fword("encode-int"); + PUSH(0); + fword("encode-int"); + fword("encode+"); + push_str("intr"); + fword("property"); +} + +static void +ob_esp_decodeunit(__attribute__((unused)) esp_private_t **esp) +{ + fword("decode-unit-scsi"); +} + + +static void +ob_esp_encodeunit(__attribute__((unused)) esp_private_t **esp) +{ + fword("encode-unit-scsi"); +} + +NODE_METHODS(ob_esp) = { + { NULL, ob_esp_initialize }, + { "decode-unit", ob_esp_decodeunit }, + { "encode-unit", ob_esp_encodeunit }, +}; + +static void +add_alias(const char *device, const char *alias) +{ + DPRINTF("add_alias dev \"%s\" = alias \"%s\"\n", device, alias); + push_str("/aliases"); + fword("find-device"); + push_str(device); + fword("encode-string"); + push_str(alias); + fword("property"); +} + +int +ob_esp_init(unsigned int slot, uint64_t base, unsigned long espoffset, + unsigned long dmaoffset) +{ + int id, diskcount = 0, cdcount = 0, *counter_ptr; + char nodebuff[256], aliasbuff[256]; + esp_private_t *esp; + unsigned int i; + + DPRINTF("Initializing SCSI..."); + + esp = malloc(sizeof(esp_private_t)); + if (!esp) { + DPRINTF("Can't allocate ESP private structure\n"); + return -1; + } + + global_esp = esp; + + if (espdma_init(slot, base, dmaoffset, &esp->espdma) != 0) { + return -1; + } + /* Get the IO region */ + esp->ll = (void *)ofmem_map_io(base + (uint64_t)espoffset, + sizeof(struct esp_regs)); + if (esp->ll == NULL) { + DPRINTF("Can't map ESP registers\n"); + return -1; + } + + esp->buffer = (void *)dvma_alloc(BUFSIZE, &esp->buffer_dvma); + if (!esp->buffer || !esp->buffer_dvma) { + DPRINTF("Can't get a DVMA buffer\n"); + return -1; + } + + // Chip reset + esp->ll->regs[ESP_CMD] = ESP_CMD_RC; + + DPRINTF("ESP at 0x%lx, buffer va 0x%lx dva 0x%lx\n", (unsigned long)esp, + (unsigned long)esp->buffer, (unsigned long)esp->buffer_dvma); + DPRINTF("done\n"); + DPRINTF("Initializing SCSI devices..."); + + for (id = 0; id < 8; id++) { + esp->sd[id].id = id; + if (!inquiry(esp, &esp->sd[id])) { + DPRINTF("Unit %d not present\n", id); + continue; + } + /* Clear Unit Attention condition from reset */ + for (i = 0; i < 5; i++) { + if (test_unit_ready(esp, &esp->sd[id])) { + break; + } + } + if (i == 5) { + DPRINTF("Unit %d present but won't become ready\n", id); + continue; + } + DPRINTF("Unit %d present\n", id); + read_capacity(esp, &esp->sd[id]); + +#ifdef CONFIG_DEBUG_ESP + dump_drive(&esp->sd[id]); +#endif + } + + REGISTER_NAMED_NODE(ob_esp, "/iommu/sbus/espdma/esp"); + device_end(); + /* set reg */ + push_str("/iommu/sbus/espdma/esp"); + fword("find-device"); + PUSH(slot); + fword("encode-int"); + PUSH(espoffset); + fword("encode-int"); + fword("encode+"); + PUSH(0x00000010); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + PUSH(0x02625a00); + fword("encode-int"); + push_str("clock-frequency"); + fword("property"); + + for (id = 0; id < 8; id++) { + if (!esp->sd[id].present) + continue; + push_str("/iommu/sbus/espdma/esp"); + fword("find-device"); + fword("new-device"); + push_str("sd"); + fword("device-name"); + push_str("block"); + fword("device-type"); + fword("is-deblocker"); + PUSH(id); + fword("encode-int"); + PUSH(0); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + fword("finish-device"); + snprintf(nodebuff, sizeof(nodebuff), "/iommu/sbus/espdma/esp/sd@%d,0", + id); + REGISTER_NODE_METHODS(ob_sd, nodebuff); + if (esp->sd[id].media == TYPE_ROM) { + counter_ptr = &cdcount; + } else { + counter_ptr = &diskcount; + } + if (*counter_ptr == 0) { + add_alias(nodebuff, esp->sd[id].media_str[0]); + add_alias(nodebuff, esp->sd[id].media_str[1]); + } + snprintf(aliasbuff, sizeof(aliasbuff), "%s%d", + esp->sd[id].media_str[0], *counter_ptr); + add_alias(nodebuff, aliasbuff); + snprintf(aliasbuff, sizeof(aliasbuff), "%s%d", + esp->sd[id].media_str[1], *counter_ptr); + add_alias(nodebuff, aliasbuff); + snprintf(aliasbuff, sizeof(aliasbuff), "sd(0,%d,0)", id); + add_alias(nodebuff, aliasbuff); + snprintf(aliasbuff, sizeof(aliasbuff), "sd(0,%d,0)@0,0", id); + add_alias(nodebuff, aliasbuff); + (*counter_ptr)++; + } + DPRINTF("done\n"); + + return 0; +} diff --git a/qemu/roms/openbios/drivers/esp.fs b/qemu/roms/openbios/drivers/esp.fs new file mode 100644 index 000000000..9e37c0a0e --- /dev/null +++ b/qemu/roms/openbios/drivers/esp.fs @@ -0,0 +1,18 @@ +\ ------------------------------------------------------------------------- +\ SCSI encode/decode unit +\ ------------------------------------------------------------------------- + +: decode-unit-scsi ( str len -- id lun ) + ascii , left-split + ( addr-R len-R addr-L len-L ) + parse-hex + -rot parse-hex + swap +; + +: encode-unit-scsi ( id lun -- str len) + swap + pocket tohexstr + " ," pocket tmpstrcat >r + rot pocket tohexstr r> tmpstrcat drop +; diff --git a/qemu/roms/openbios/drivers/esp.h b/qemu/roms/openbios/drivers/esp.h new file mode 100644 index 000000000..2b9bd5e01 --- /dev/null +++ b/qemu/roms/openbios/drivers/esp.h @@ -0,0 +1,269 @@ +/* $Id: esp.h,v 1.28 2000/03/30 01:33:17 davem Exp $ + * esp.h: Defines and structures for the Sparc ESP (Enhanced SCSI + * Processor) driver under Linux. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#ifndef _SPARC_ESP_H +#define _SPARC_ESP_H + +/* For dvma controller register definitions. */ +#include "asm/dma.h" + +/* The ESP SCSI controllers have their register sets in three + * "classes": + * + * 1) Registers which are both read and write. + * 2) Registers which are read only. + * 3) Registers which are write only. + * + * Yet, they all live within the same IO space. + */ + +/* All the ESP registers are one byte each and are accessed longwords + * apart with a big-endian ordering to the bytes. + */ + /* Access Description Offset */ +#define ESP_TCLOW 0x00UL /* rw Low bits of the transfer count 0x00 */ +#define ESP_TCMED 0x04UL /* rw Mid bits of the transfer count 0x04 */ +#define ESP_FDATA 0x08UL /* rw FIFO data bits 0x08 */ +#define ESP_CMD 0x0cUL /* rw SCSI command bits 0x0c */ +#define ESP_STATUS 0x10UL /* ro ESP status register 0x10 */ +#define ESP_BUSID ESP_STATUS /* wo Bus ID for select/reselect 0x10 */ +#define ESP_INTRPT 0x14UL /* ro Kind of interrupt 0x14 */ +#define ESP_TIMEO ESP_INTRPT /* wo Timeout value for select/resel 0x14 */ +#define ESP_SSTEP 0x18UL /* ro Sequence step register 0x18 */ +#define ESP_STP ESP_SSTEP /* wo Transfer period per sync 0x18 */ +#define ESP_FFLAGS 0x1cUL /* ro Bits of current FIFO info 0x1c */ +#define ESP_SOFF ESP_FFLAGS /* wo Sync offset 0x1c */ +#define ESP_CFG1 0x20UL /* rw First configuration register 0x20 */ +#define ESP_CFACT 0x24UL /* wo Clock conversion factor 0x24 */ +#define ESP_STATUS2 ESP_CFACT /* ro HME status2 register 0x24 */ +#define ESP_CTEST 0x28UL /* wo Chip test register 0x28 */ +#define ESP_CFG2 0x2cUL /* rw Second configuration register 0x2c */ +#define ESP_CFG3 0x30UL /* rw Third configuration register 0x30 */ +#define ESP_TCHI 0x38UL /* rw High bits of transfer count 0x38 */ +#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */ +#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */ +#define ESP_FGRND 0x3cUL /* rw Data base for fifo 0x3c */ +#define FAS_RHI ESP_FGRND /* rw HME extended counter 0x3c */ +#define ESP_REG_SIZE 0x40UL + +/* Various revisions of the ESP board. */ +enum esp_rev { + esp100 = 0x00, /* NCR53C90 - very broken */ + esp100a = 0x01, /* NCR53C90A */ + esp236 = 0x02, + fas236 = 0x03, + fas100a = 0x04, + fast = 0x05, + fashme = 0x06, + espunknown = 0x07 +}; + +/* Bitfield meanings for the above registers. */ + +/* ESP config reg 1, read-write, found on all ESP chips */ +#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */ +#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */ +#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */ +#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */ +#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */ +#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */ + +/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */ +#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */ +#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */ +#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */ +#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tmode only) */ +#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */ +#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */ +#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */ +#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */ +#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216) */ +#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (esp236) */ +#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */ +#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */ +#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */ + +/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */ +#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */ +#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */ +#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */ +#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */ +#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */ +#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */ +#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */ +#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */ +#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */ +#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */ +#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */ +#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */ +#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */ +#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */ +#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */ +#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */ + +/* ESP command register read-write */ +/* Group 1 commands: These may be sent at any point in time to the ESP + * chip. None of them can generate interrupts 'cept + * the "SCSI bus reset" command if you have not disabled + * SCSI reset interrupts in the config1 ESP register. + */ +#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */ +#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */ +#define ESP_CMD_RC 0x02 /* Chip reset */ +#define ESP_CMD_RS 0x03 /* SCSI bus reset */ + +/* Group 2 commands: ESP must be an initiator and connected to a target + * for these commands to work. + */ +#define ESP_CMD_TI 0x10 /* Transfer Information */ +#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */ +#define ESP_CMD_MOK 0x12 /* Message okie-dokie */ +#define ESP_CMD_TPAD 0x18 /* Transfer Pad */ +#define ESP_CMD_SATN 0x1a /* Set ATN */ +#define ESP_CMD_RATN 0x1b /* De-assert ATN */ + +/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected + * to a target as the initiator for these commands to work. + */ +#define ESP_CMD_SMSG 0x20 /* Send message */ +#define ESP_CMD_SSTAT 0x21 /* Send status */ +#define ESP_CMD_SDATA 0x22 /* Send data */ +#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */ +#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */ +#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */ +#define ESP_CMD_DCNCT 0x27 /* Disconnect */ +#define ESP_CMD_RMSG 0x28 /* Receive Message */ +#define ESP_CMD_RCMD 0x29 /* Receive Command */ +#define ESP_CMD_RDATA 0x2a /* Receive Data */ +#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */ + +/* Group 4 commands: The ESP must be in the disconnected state and must + * not be connected to any targets as initiator for + * these commands to work. + */ +#define ESP_CMD_RSEL 0x40 /* Reselect */ +#define ESP_CMD_SEL 0x41 /* Select w/o ATN */ +#define ESP_CMD_SELA 0x42 /* Select w/ATN */ +#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */ +#define ESP_CMD_ESEL 0x44 /* Enable selection */ +#define ESP_CMD_DSEL 0x45 /* Disable selections */ +#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */ +#define ESP_CMD_RSEL3 0x47 /* Reselect3 */ + +/* This bit enables the ESP's DMA on the SBus */ +#define ESP_CMD_DMA 0x80 /* Do DMA? */ + + +/* ESP status register read-only */ +#define ESP_STAT_PIO 0x01 /* IO phase bit */ +#define ESP_STAT_PCD 0x02 /* CD phase bit */ +#define ESP_STAT_PMSG 0x04 /* MSG phase bit */ +#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */ +#define ESP_STAT_TDONE 0x08 /* Transfer Completed */ +#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */ +#define ESP_STAT_PERR 0x20 /* Parity error */ +#define ESP_STAT_SPAM 0x40 /* Real bad error */ +/* This indicates the 'interrupt pending' condition on esp236, it is a reserved + * bit on other revs of the ESP. + */ +#define ESP_STAT_INTR 0x80 /* Interrupt */ + +/* HME only: status 2 register */ +#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */ +#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */ +#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */ +#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */ +#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */ +#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */ +#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */ +#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */ + +/* The status register can be masked with ESP_STAT_PMASK and compared + * with the following values to determine the current phase the ESP + * (at least thinks it) is in. For our purposes we also add our own + * software 'done' bit for our phase management engine. + */ +#define ESP_DOP (0) /* Data Out */ +#define ESP_DIP (ESP_STAT_PIO) /* Data In */ +#define ESP_CMDP (ESP_STAT_PCD) /* Command */ +#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */ +#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */ +#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */ + +/* ESP interrupt register read-only */ +#define ESP_INTR_S 0x01 /* Select w/o ATN */ +#define ESP_INTR_SATN 0x02 /* Select w/ATN */ +#define ESP_INTR_RSEL 0x04 /* Reselected */ +#define ESP_INTR_FDONE 0x08 /* Function done */ +#define ESP_INTR_BSERV 0x10 /* Bus service */ +#define ESP_INTR_DC 0x20 /* Disconnect */ +#define ESP_INTR_IC 0x40 /* Illegal command given */ +#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */ + +/* Interrupt status macros */ +#define ESP_SRESET_IRQ(esp) ((esp)->intreg & (ESP_INTR_SR)) +#define ESP_ILLCMD_IRQ(esp) ((esp)->intreg & (ESP_INTR_IC)) +#define ESP_SELECT_WITH_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_SATN)) +#define ESP_SELECT_WITHOUT_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_S)) +#define ESP_SELECTION_IRQ(esp) ((ESP_SELECT_WITH_ATN_IRQ(esp)) || \ + (ESP_SELECT_WITHOUT_ATN_IRQ(esp))) +#define ESP_RESELECTION_IRQ(esp) ((esp)->intreg & (ESP_INTR_RSEL)) + +/* ESP sequence step register read-only */ +#define ESP_STEP_VBITS 0x07 /* Valid bits */ +#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */ +#define ESP_STEP_SID 0x01 /* One msg byte sent */ +#define ESP_STEP_NCMD 0x02 /* Was not in command phase */ +#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd + * bytes to be lost + */ +#define ESP_STEP_FINI4 0x04 /* Command was sent ok */ + +/* Ho hum, some ESP's set the step register to this as well... */ +#define ESP_STEP_FINI5 0x05 +#define ESP_STEP_FINI6 0x06 +#define ESP_STEP_FINI7 0x07 + +/* ESP chip-test register read-write */ +#define ESP_TEST_TARG 0x01 /* Target test mode */ +#define ESP_TEST_INI 0x02 /* Initiator test mode */ +#define ESP_TEST_TS 0x04 /* Tristate test mode */ + +/* ESP unique ID register read-only, found on fas236+fas100a only */ +#define ESP_UID_F100A 0x00 /* ESP FAS100A */ +#define ESP_UID_F236 0x02 /* ESP FAS236 */ +#define ESP_UID_REV 0x07 /* ESP revision */ +#define ESP_UID_FAM 0xf8 /* ESP family */ + +/* ESP fifo flags register read-only */ +/* Note that the following implies a 16 byte FIFO on the ESP. */ +#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */ +#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */ +#define ESP_FF_SSTEP 0xe0 /* Sequence step */ + +/* ESP clock conversion factor register write-only */ +#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */ +#define ESP_CCF_NEVER 0x01 /* Set it to this and die */ +#define ESP_CCF_F2 0x02 /* 10MHz */ +#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */ +#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */ +#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */ +#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */ +#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */ + +/* HME only... */ +#define ESP_BUSID_RESELID 0x10 +#define ESP_BUSID_CTR32BIT 0x40 + +#define ESP_BUS_TIMEOUT 275 /* In milli-seconds */ +#define ESP_TIMEO_CONST 8192 +#define ESP_NEG_DEFP(mhz, cfact) \ + ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact))) +#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000)) +#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000)) + +#endif /* !(_SPARC_ESP_H) */ diff --git a/qemu/roms/openbios/drivers/floppy.c b/qemu/roms/openbios/drivers/floppy.c new file mode 100644 index 000000000..329987b17 --- /dev/null +++ b/qemu/roms/openbios/drivers/floppy.c @@ -0,0 +1,1179 @@ +#include "config.h" +#include "libopenbios/bindings.h" +#include "kernel/kernel.h" +#include "libc/byteorder.h" +#include "libc/vsprintf.h" + +#include "drivers/drivers.h" + +#include "timer.h" + +/* DECLARE data structures for the nodes. */ +DECLARE_UNNAMED_NODE( ob_floppy, INSTALL_OPEN, 2*sizeof(int) ); + +#ifdef CONFIG_DEBUG_FLOPPY +#define printk_info printk +#define printk_debug printk +#else +#define printk_info(x ...) +#define printk_debug(x ...) +#endif +#define printk_err printk + +#define FD_DRIVE 0 + + +#define FD_STATUS_A (0) /* Status register A */ +#define FD_STATUS_B (1) /* Status register B */ +#define FD_DOR (2) /* Digital Output Register */ +#define FD_TDR (3) /* Tape Drive Register */ +#define FD_STATUS (4) /* Main Status Register */ +#define FD_DSR (4) /* Data Rate Select Register (old) */ +#define FD_DATA (5) /* Data Transfer (FIFO) register */ +#define FD_DIR (7) /* Digital Input Register (read) */ +#define FD_DCR (7) /* Diskette Control Register (write)*/ + +/* Bit of FD_STATUS_A */ +#define STA_INT_PENDING 0x80 /* Interrupt Pending */ + +/* DOR */ +#define DOR_DRIVE0 0x00 +#define DOR_DRIVE1 0x01 +#define DOR_DRIVE2 0x02 +#define DOR_DRIVE3 0x03 +#define DOR_DRIVE_MASK 0x03 +#define DOR_NO_RESET 0x04 +#define DOR_DMA_EN 0x08 +#define DOR_MOT_EN0 0x10 +#define DOR_MOT_EN1 0x20 +#define DOR_MOT_EN2 0x40 +#define DOR_MOT_EN3 0x80 + +/* Bits of main status register */ +#define STATUS_BUSYMASK 0x0F /* drive busy mask */ +#define STATUS_BUSY 0x10 /* FDC busy */ +#define STATUS_NON_DMA 0x20 /* 0- DMA mode */ +#define STATUS_DIR 0x40 /* 0- cpu->fdc */ +#define STATUS_READY 0x80 /* Data reg ready */ + +/* Bits of FD_ST0 */ +#define ST0_DS 0x03 /* drive select mask */ +#define ST0_HA 0x04 /* Head (Address) */ +#define ST0_NR 0x08 /* Not Ready */ +#define ST0_ECE 0x10 /* Equipment check error */ +#define ST0_SE 0x20 /* Seek end */ +#define ST0_INTR 0xC0 /* Interrupt code mask */ +#define ST0_INTR_OK (0 << 6) +#define ST0_INTR_ERROR (1 << 6) +#define ST0_INTR_INVALID (2 << 6) +#define ST0_INTR_POLL_ERROR (3 << 6) + +/* Bits of FD_ST1 */ +#define ST1_MAM 0x01 /* Missing Address Mark */ +#define ST1_WP 0x02 /* Write Protect */ +#define ST1_ND 0x04 /* No Data - unreadable */ +#define ST1_OR 0x10 /* OverRun */ +#define ST1_CRC 0x20 /* CRC error in data or addr */ +#define ST1_EOC 0x80 /* End Of Cylinder */ + +/* Bits of FD_ST2 */ +#define ST2_MAM 0x01 /* Missing Address Mark (again) */ +#define ST2_BC 0x02 /* Bad Cylinder */ +#define ST2_SNS 0x04 /* Scan Not Satisfied */ +#define ST2_SEH 0x08 /* Scan Equal Hit */ +#define ST2_WC 0x10 /* Wrong Cylinder */ +#define ST2_CRC 0x20 /* CRC error in data field */ +#define ST2_CM 0x40 /* Control Mark = deleted */ + +/* Bits of FD_ST3 */ +#define ST3_HA 0x04 /* Head (Address) */ +#define ST3_DS 0x08 /* drive is double-sided */ +#define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */ +#define ST3_RY 0x20 /* drive is ready */ +#define ST3_WP 0x40 /* Write Protect */ +#define ST3_FT 0x80 /* Drive Fault */ + +/* Values for FD_COMMAND */ +#define FD_RECALIBRATE 0x07 /* move to track 0 */ +#define FD_SEEK 0x0F /* seek track */ +#define FD_READ 0xA6 /* read with MT, SKip deleted */ +#define FD_WRITE 0xC5 /* write with MT, MFM */ +#define FD_SENSEI 0x08 /* Sense Interrupt Status */ +#define FD_SPECIFY 0x03 /* specify HUT etc */ +#define FD_FORMAT 0x4D /* format one track */ +#define FD_VERSION 0x10 /* get version code */ +#define FD_CONFIGURE 0x13 /* configure FIFO operation */ +#define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */ +#define FD_GETSTATUS 0x04 /* read ST3 */ +#define FD_DUMPREGS 0x0E /* dump the contents of the fdc regs */ +#define FD_READID 0xEA /* prints the header of a sector */ +#define FD_UNLOCK 0x14 /* Fifo config unlock */ +#define FD_LOCK 0x94 /* Fifo config lock */ +#define FD_RSEEK_OUT 0x8f /* seek out (i.e. to lower tracks) */ +#define FD_RSEEK_IN 0xcf /* seek in (i.e. to higher tracks) */ + + +/* the following commands are new in the 82078. They are not used in the + * floppy driver, except the first three. These commands may be useful for apps + * which use the FDRAWCMD interface. For doc, get the 82078 spec sheets at + * http://www-techdoc.intel.com/docs/periph/fd_contr/datasheets/ */ + +#define FD_PARTID 0x18 /* part id ("extended" version cmd) */ +#define FD_SAVE 0x2e /* save fdc regs for later restore */ +#define FD_DRIVESPEC 0x8e /* drive specification: Access to the + * 2 Mbps data transfer rate for tape + * drives */ + +#define FD_RESTORE 0x4e /* later restore */ +#define FD_POWERDOWN 0x27 /* configure FDC's powersave features */ +#define FD_FORMAT_N_WRITE 0xef /* format and write in one go. */ +#define FD_OPTION 0x33 /* ISO format (which is a clean way to + * pack more sectors on a track) */ + +/* FDC version return types */ +#define FDC_NONE 0x00 +#define FDC_UNKNOWN 0x10 /* DO NOT USE THIS TYPE EXCEPT IF IDENTIFICATION + FAILS EARLY */ +#define FDC_8272A 0x20 /* Intel 8272a, NEC 765 */ +#define FDC_765ED 0x30 /* Non-Intel 1MB-compatible FDC, can't detect */ +#define FDC_82072 0x40 /* Intel 82072; 8272a + FIFO + DUMPREGS */ +#define FDC_82072A 0x45 /* 82072A (on Sparcs) */ +#define FDC_82077_ORIG 0x51 /* Original version of 82077AA, sans LOCK */ +#define FDC_82077 0x52 /* 82077AA-1 */ +#define FDC_82078_UNKN 0x5f /* Unknown 82078 variant */ +#define FDC_82078 0x60 /* 44pin 82078 or 64pin 82078SL */ +#define FDC_82078_1 0x61 /* 82078-1 (2Mbps fdc) */ +#define FDC_S82078B 0x62 /* S82078B (first seen on Adaptec AVA-2825 VLB + * SCSI/EIDE/Floppy controller) */ +#define FDC_87306 0x63 /* National Semiconductor PC 87306 */ + +/* + * Beware: the fdc type list is roughly sorted by increasing features. + * Presence of features is tested by comparing the FDC version id with the + * "oldest" version that has the needed feature. + * If during FDC detection, an obscure test fails late in the sequence, don't + * assign FDC_UNKNOWN. Else the FDC will be treated as a dumb 8272a, or worse. + * This is especially true if the tests are unneeded. + */ + +/* Parameters for a 1.44 3.5" disk */ +#define DISK_H1440_SIZE 2880 +#define DISK_H1440_SECT 18 +#define DISK_H1440_HEAD 2 +#define DISK_H1440_TRACK 80 +#define DISK_H1440_STRETCH 0 +#define DISK_H1440_GAP 0x1B +#define DISK_H1440_RATE 0x00 +#define DISK_H1440_SPEC1 0xCF +#define DISK_H1440_FMT_GAP 0x6C + +/* Parameters for a 1.44 3.5" drive */ +#define DRIVE_H1440_MAX_DTR 500 +#define DRIVE_H1440_HLT 16 /* ms */ +#define DRIVE_H1440_HUT 16 /* ms */ +#define DRIVE_H1440_SRT 4000 /* us */ +#define DRIVE_H1440_SPINUP 400 /* ms */ +#define DRIVE_H1440_SPINDOWN 3000 /* ms */ +#define DRIVE_H1440_SPINDOWN_OFFSET 10 +#define DRIVE_H1440_SELECT_DELAY 20 /* ms */ +#define DRIVE_H1440_RPS 5 +#define DRIVE_H1440_TRACKS 83 +#define DRIVE_H1440_TIMEOUT 3000 /* ms */ +#define DRIVE_H1440_INTERLEAVE_SECT 20 + +/* Floppy drive configuration */ +#define FIFO_DEPTH 10 +#define USE_IMPLIED_SEEK 0 +#define USE_FIFO 1 +#define FIFO_THRESHOLD 10 +#define TRACK_PRECOMPENSATION 0 + +#define SLOW_FLOPPY 0 + +#define FD_RESET_DELAY 20 /* microseconds */ + +/* + * FDC state + */ +static struct drive_state { + unsigned track; +} drive_state[1]; + +static struct floppy_fdc_state { + int in_sync; + int spec1; /* spec1 value last used */ + int spec2; /* spec2 value last used */ + int dtr; + unsigned char dor; + unsigned char version; /* FDC version code */ + void (*fdc_outb)(unsigned char data, unsigned long port); + unsigned char (*fdc_inb)(unsigned long port); + unsigned long io_base; + unsigned long mmio_base; +} fdc_state; + +/* Synchronization of FDC access. */ +#define FD_COMMAND_NONE -1 +#define FD_COMMAND_ERROR 2 +#define FD_COMMAND_OKAY 3 + +/* + * globals used by 'result()' + */ +#define MAX_REPLIES 16 + +static void show_floppy(void); +static void floppy_reset(void); + +/* + * IO port operations + */ +static unsigned char +ob_fdc_inb(unsigned long port) +{ + return inb(fdc_state.io_base + port); +} + +static void +ob_fdc_outb(unsigned char data, unsigned long port) +{ + outb(data, fdc_state.io_base + port); +} + +/* + * MMIO operations + */ +static unsigned char +ob_fdc_mmio_readb(unsigned long port) +{ + return *(unsigned char *)(fdc_state.mmio_base + port); +} + +static void +ob_fdc_mmio_writeb(unsigned char data, unsigned long port) +{ + *(unsigned char *)(fdc_state.mmio_base + port) = data; +} + +static int set_dor(char mask, char data) +{ + unsigned char newdor,olddor; + + olddor = fdc_state.dor; + newdor = (olddor & mask) | data; + if (newdor != olddor){ + fdc_state.dor = newdor; + fdc_state.fdc_outb(newdor, FD_DOR); + } + return olddor; +} + +/* waits until the fdc becomes ready */ +static int wait_til_ready(void) +{ + int counter, status; + for (counter = 0; counter < 10000; counter++) { + status = fdc_state.fdc_inb(FD_STATUS); + if (status & STATUS_READY) { + return status; + } + } + printk_debug("Getstatus times out (%x)\n", status); + show_floppy(); + return -3; +} + + +/* sends a command byte to the fdc */ +static int output_byte(unsigned char byte) +{ + int status; + + if ((status = wait_til_ready()) < 0) + return status; + if ((status & (STATUS_READY|STATUS_DIR|STATUS_NON_DMA)) == STATUS_READY){ + fdc_state.fdc_outb(byte,FD_DATA); + return 0; + } + printk_debug("Unable to send byte %x to FDC_STATE. Status=%x\n", + byte, status); + + show_floppy(); + return -2; +} + +/* gets the response from the fdc */ +static int result(unsigned char *reply_buffer, int max_replies) +{ + int i, status=0; + + for(i=0; i < max_replies; i++) { + if ((status = wait_til_ready()) < 0) + break; + status &= STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_NON_DMA; + if ((status & ~STATUS_BUSY) == STATUS_READY){ + return i; + } + if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) + reply_buffer[i] = fdc_state.fdc_inb(FD_DATA); + else + break; + } + if (i == max_replies) + return i; + printk_debug("get result error. Last status=%x Read bytes=%d\n", + status, i); + show_floppy(); + return -1; +} +#define MORE_OUTPUT -2 +/* does the fdc need more output? */ +static int need_more_output(void) +{ + unsigned char reply_buffer[MAX_REPLIES]; + int status; + if ((status = wait_til_ready()) < 0) + return -1; + if ((status & (STATUS_READY|STATUS_DIR|STATUS_NON_DMA)) == STATUS_READY) + return MORE_OUTPUT; + return result(reply_buffer, MAX_REPLIES); +} + +static int output_command(unsigned char *cmd, int count) +{ + int i, status; + for(i = 0; i < count; i++) { + if ((status = output_byte(cmd[i])) < 0) { + printk_err("full command not acceppted, status =%x\n", + status); + return -1; + } + } + return 0; +} + +static int output_new_command(unsigned char *cmd, int count) +{ + int i, status; + if ((status = output_byte(cmd[0])) < 0) + return -1; + if (need_more_output() != MORE_OUTPUT) + return -1; + for(i = 1; i < count; i++) { + if ((status = output_byte(cmd[i])) < 0) { + printk_err("full new command not acceppted, status =%d\n", + status); + return -1; + } + } + return 0; +} + + +/* Collect pending interrupt status */ +static unsigned char collect_interrupt(void) +{ + unsigned char pcn = 0xff; + unsigned char reply_buffer[MAX_REPLIES]; + int nr; +#ifdef CONFIG_DEBUG_FLOPPY + int i, status; +#endif + nr = result(reply_buffer, MAX_REPLIES); + if (nr != 0) { + printk_debug("SENSEI\n"); + } + else { + int max_sensei = 4; + do { + if (output_byte(FD_SENSEI) < 0) + break; + nr = result(reply_buffer, MAX_REPLIES); + if (nr == 2) { + pcn = reply_buffer[1]; + printk_debug("SENSEI %02x %02x\n", + reply_buffer[0], reply_buffer[1]); + } + max_sensei--; + }while(((reply_buffer[0] & 0x83) != FD_DRIVE) && (nr == 2) && max_sensei); +#ifdef CONFIG_DEBUG_FLOPPY + status = fdc_state.fdc_inb(FD_STATUS); + printk_debug("status = %x, reply_buffer=", status); + for(i = 0; i < nr; i++) { + printk_debug(" %x", + reply_buffer[i]); + } + printk_debug("\n"); +#else + fdc_state.fdc_inb(FD_STATUS); +#endif + } + + return pcn; +} + + +/* selects the fdc and drive, and enables the fdc's input/dma, and it's motor. */ +static void set_drive(int drive) +{ + int fdc = (drive >> 2) & 1; + int status; + unsigned new_dor; + if (drive > 3) { + printk_err("bad drive value\n"); + return; + } + if (fdc != 0) { + printk_err("bad fdc value\n"); + return; + } + drive &= 3; +#if 0 + new_dor = 8; /* Enable the controller */ +#else + new_dor = 0; /* Don't enable DMA on the controller */ +#endif + new_dor |= (1 << (drive + 4)); /* Spinup the selected drive */ + new_dor |= drive; /* Select the drive for commands as well */ + set_dor(0xc, new_dor); + + mdelay(DRIVE_H1440_SPINUP); + + status = fdc_state.fdc_inb(FD_STATUS); + printk_debug("set_drive status = %02x, new_dor = %02x\n", + status, new_dor); + if (status != STATUS_READY) { + printk_err("set_drive bad status\n"); + } +} + + +/* Disable the motor for a given floppy drive */ +static void floppy_motor_off(int drive) +{ + unsigned mask; + printk_debug("floppy_motor_off\n"); + /* fix the number of drives */ + drive &= 3; + /* Clear the bit for the drive we care about */ + mask = 0xff; + mask &= ~(1 << (drive +4)); + /* Now clear the bit in the Digital Output Register */ + set_dor(mask, 0); +} + +/* Set the FDC's data transfer rate on behalf of the specified drive. + * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue + * of the specify command (i.e. using the fdc_specify function). + */ +static void fdc_dtr(unsigned rate) +{ + rate &= 3; + /* If data rate not already set to desired value, set it. */ + if (fdc_state.in_sync && (rate == fdc_state.dtr)) + return; + + /* Set dtr */ + fdc_state.fdc_outb(rate, FD_DCR); + + /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB) + * need a stabilization period of several milliseconds to be + * enforced after data rate changes before R/W operations. + * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies) + */ + fdc_state.dtr = rate & 3; + mdelay(5); +} /* fdc_dtr */ + +static int fdc_configure(int use_implied_seek, int use_fifo, + unsigned fifo_threshold, unsigned precompensation) +{ + unsigned config_bits; + unsigned char cmd[4]; + /* 0 EIS EFIFO POLL FIFOOTHR[4] */ + + /* santize parameters */ + config_bits = fifo_threshold & 0xf; + config_bits |= (1 << 4); /* Always disable background floppy poll */ + config_bits |= (!use_fifo) << 5; + config_bits |= (!!use_implied_seek) << 6; + + precompensation &= 0xff; /* pre-compensation from track 0 upwards */ + + cmd[0] = FD_CONFIGURE; + cmd[1] = 0; + cmd[2] = config_bits; + cmd[3] = precompensation; + + /* Turn on FIFO */ + if (output_new_command(cmd, 4) < 0) + return 0; + return 1; +} + +#define NOMINAL_DTR 500 +/* Issue a "SPECIFY" command to set the step rate time, head unload time, + * head load time, and DMA disable flag to values needed by floppy. + * + * The value "dtr" is the data transfer rate in Kbps. It is needed + * to account for the data rate-based scaling done by the 82072 and 82077 + * FDC types. This parameter is ignored for other types of FDCs (i.e. + * 8272a). + * + * Note that changing the data transfer rate has a (probably deleterious) + * effect on the parameters subject to scaling for 82072/82077 FDCs, so + * fdc_specify is called again after each data transfer rate + * change. + * + * srt: 1000 to 16000 in microseconds + * hut: 16 to 240 milliseconds + * hlt: 2 to 254 milliseconds + * + * These values are rounded up to the next highest available delay time. + */ +static void fdc_specify( + unsigned head_load_time, unsigned head_unload_time, unsigned step_rate) +{ + unsigned char cmd[3]; + unsigned long srt, hlt, hut; + unsigned long dtr = NOMINAL_DTR; + unsigned long scale_dtr = NOMINAL_DTR; + int hlt_max_code = 0x7f; + int hut_max_code = 0xf; + + printk_debug("fdc_specify\n"); + + switch (DISK_H1440_RATE & 0x03) { + case 3: + dtr = 1000; + break; + case 1: + dtr = 300; + if (fdc_state.version >= FDC_82078) { + /* chose the default rate table, not the one + * where 1 = 2 Mbps */ + cmd[0] = FD_DRIVESPEC; + cmd[1] = FD_DRIVE & 3; + cmd[2] = 0xc0; + output_new_command(cmd,3); + /* FIXME how do I handle errors here? */ + } + break; + case 2: + dtr = 250; + break; + } + + + if (fdc_state.version >= FDC_82072) { + scale_dtr = dtr; + hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */ + hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */ + } + + /* Convert step rate from microseconds to milliseconds and 4 bits */ + srt = 16 - (step_rate*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR; + if (SLOW_FLOPPY) { + srt = srt / 4; + } + if (srt > 0xf) { + srt = 0xf; + } + + hlt = (head_load_time*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR; + if (hlt < 0x01) + hlt = 0x01; + else if (hlt > 0x7f) + hlt = hlt_max_code; + + hut = (head_unload_time*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR; + if (hut < 0x1) + hut = 0x1; + else if (hut > 0xf) + hut = hut_max_code; + + cmd[0] = FD_SPECIFY; + cmd[1] = (srt << 4) | hut; + cmd[2] = (hlt << 1) | 1; /* Always disable DMA */ + + /* If these parameters did not change, just return with success */ + if (!fdc_state.in_sync || fdc_state.spec1 != cmd[1] || fdc_state.spec2 != cmd[2]) { + /* Go ahead and set spec1 and spec2 */ + output_command(cmd, 3); + /* FIXME how do I handle errors here... */ + printk_info("FD_SPECIFY(%02x, %02x)\n", cmd[1], cmd[2]); + } +} /* fdc_specify */ + + +/* + * reset is done by pulling bit 2 of DOR low for a while (old FDCs), + * or by setting the self clearing bit 7 of STATUS (newer FDCs) + */ +static void reset_fdc(void) +{ + unsigned char reply[MAX_REPLIES]; + + fdc_state.in_sync = 0; + + /* Pseudo-DMA may intercept 'reset finished' interrupt. */ + /* Irrelevant for systems with true DMA (i386). */ + + if (fdc_state.version >= FDC_82072A) + fdc_state.fdc_outb(0x80 | (fdc_state.dtr &3), FD_DSR); + else { + fdc_state.fdc_outb(fdc_state.dor & ~DOR_NO_RESET, FD_DOR); + udelay(FD_RESET_DELAY); + fdc_state.fdc_outb(fdc_state.dor, FD_DOR); + } + result(reply, MAX_REPLIES); +} + + + +static void show_floppy(void) +{ + + printk_debug("\n"); + printk_debug("floppy driver state\n"); + printk_debug("-------------------\n"); + + printk_debug("fdc_bytes: %02x %02x xx %02x %02x %02x xx %02x\n", + fdc_state.fdc_inb(FD_STATUS_A), + fdc_state.fdc_inb(FD_STATUS_B), + fdc_state.fdc_inb(FD_TDR), + fdc_state.fdc_inb(FD_STATUS), + fdc_state.fdc_inb(FD_DATA), + fdc_state.fdc_inb(FD_DIR)); + + printk_debug("status=%x\n", fdc_state.fdc_inb(FD_STATUS)); + printk_debug("\n"); +} + +static void floppy_recalibrate(void) +{ + unsigned char cmd[2]; + unsigned char reply[MAX_REPLIES]; + int nr, success; + success = 0; + do { + printk_debug("floppy_recalibrate\n"); + /* Send the recalibrate command to the controller. + * We don't have interrupts or anything we can poll + * so we have to guess when it is done. + */ + cmd[0] = FD_RECALIBRATE; + cmd[1] = 0; + if (output_command(cmd, 2) < 0) + continue; + + /* Sleep for the maximum time the recalibrate command + * can run. + */ + mdelay(80*DRIVE_H1440_SRT/1000); + + /* Now call FD_SENSEI to end the command + * and collect up the reply. + */ + if (output_byte(FD_SENSEI) < 0) + continue; + nr = result(reply, MAX_REPLIES); + + /* Now see if we have succeeded in our seek */ + success = + /* We have the right size result */ + (nr == 2) && + /* The command didn't terminate in error */ + ((reply[0] & ST0_INTR) == ST0_INTR_OK) && + /* We finished a seek */ + (reply[0] & ST0_SE) && + /* We are at cylinder 0 */ + (reply[1] == 0); + } while(!success); + /* Remember we are at track 0 */ + drive_state[FD_DRIVE].track = 0; +} + + +static int floppy_seek(unsigned track) +{ + unsigned char cmd[3]; + unsigned char reply[MAX_REPLIES]; + int nr, success; + unsigned distance, old_track; + + /* Look up the old track and see if we need to + * do anything. + */ + old_track = drive_state[FD_DRIVE].track; + if (old_track == track) { + return 1; + } + + /* Compute the distance we are about to move, + * We need to know this so we know how long to sleep... + */ + distance = (old_track > track)?(old_track - track):(track - old_track); + distance += 1; + + + /* Send the seek command to the controller. + * We don't have interrupts or anything we can poll + * so we have to guess when it is done. + */ + cmd[0] = FD_SEEK; + cmd[1] = FD_DRIVE; + cmd[2] = track; + if (output_command(cmd, 3) < 0) + return 0; + + /* Sleep for the time it takes to step throuhg distance tracks. + */ + mdelay(distance*DRIVE_H1440_SRT/1000); + + /* Now call FD_SENSEI to end the command + * and collect up the reply. + */ + cmd[0] = FD_SENSEI; + if (output_command(cmd, 1) < 0) + return 0; + nr = result(reply, MAX_REPLIES); + + /* Now see if we have succeeded in our seek */ + success = + /* We have the right size result */ + (nr == 2) && + /* The command didn't terminate in error */ + ((reply[0] & ST0_INTR) == ST0_INTR_OK) && + /* We finished a seek */ + (reply[0] & ST0_SE) && + /* We are at cylinder 0 */ + (reply[1] == track); + if (success) + drive_state[FD_DRIVE].track = track; + else { + printk_debug("seek failed\n"); + printk_debug("nr = %d\n", nr); + printk_debug("ST0 = %02x\n", reply[0]); + printk_debug("PCN = %02x\n", reply[1]); + printk_debug("status = %d\n", fdc_state.fdc_inb(FD_STATUS)); + } + return success; +} + +static int read_ok(unsigned head) +{ + unsigned char results[7]; + int result_ok; + int nr; + + /* read back the read results */ + nr = result(results, 7); + + /* Now see if they say we are o.k. */ + result_ok = 0; + /* Are my result bytes o.k.? */ + if (nr == 7) { + /* Are we o.k. */ + if ((results[0] & ST0_INTR) == ST0_INTR_OK) { + result_ok = 1; + } + /* Or did we get just an overflow error */ + else if (((results[0] & ST0_INTR) == ST0_INTR_ERROR) && + (results[1]== ST1_OR) && + (results[2] == 0)) { + result_ok = 1; + } + /* Verify the reply had the correct head */ + if (((results[0] & ST0_HA) >> 2) != head) { + result_ok = 0; + } + /* Verify the reply had the correct drive */ + if (((results[0] & ST0_DS) != FD_DRIVE)) { + result_ok = 0; + } + } + if (!result_ok) { + printk_debug("result_bytes = %d\n", nr); + printk_debug("ST0 = %02x\n", results[0]); + printk_debug("ST1 = %02x\n", results[1]); + printk_debug("ST2 = %02x\n", results[2]); + printk_debug(" C = %02x\n", results[3]); + printk_debug(" H = %02x\n", results[4]); + printk_debug(" R = %02x\n", results[5]); + printk_debug(" N = %02x\n", results[6]); + } + return result_ok; +} + +static int floppy_read_sectors( + char *dest, unsigned byte_offset, unsigned length, + unsigned sector, unsigned head, unsigned track) +{ + /* MT == Multitrack */ + /* MFM == MFM or FM Mode */ + /* SK == Skip deleted data addres Mark */ + /* HDS == Head number select */ + /* DS0 == Disk Drive Select 0 */ + /* DS1 == Disk Drive Select 1 */ + /* C == Cylinder number 0 - 255 */ + /* H == Head number */ + /* R == Record */ + /* N == The number of data bytes written in a sector */ + /* EOT == End of Track */ + /* GPL == Gap Length */ + /* DTL == Data Length */ + /* MT MFM SK 0 1 1 0 0 */ + /* 0 0 0 0 0 HDS DS1 DS0 */ + /* C, H, R, N, EOT, GPL, DTL */ + + int i, status, result_ok; + int max_bytes, bytes_read; + int ret; + unsigned char cmd[9]; + unsigned end_offset; + + end_offset = byte_offset + length; + max_bytes = 512*(DISK_H1440_SECT - sector + 1); + + if (byte_offset >= max_bytes) { + return 0; + } + cmd[0] = FD_READ | (((DISK_H1440_HEAD ==2)?1:0) << 6); + cmd[1] = (head << 2) | FD_DRIVE; + cmd[2] = track; + cmd[3] = head; + cmd[4] = sector; + cmd[5] = 2; /* 2^N *128 == Sector size. Hard coded to 512 bytes */ + cmd[6] = DISK_H1440_SECT; + cmd[7] = DISK_H1440_GAP; + cmd[8] = 0xff; + + /* Output the command bytes */ + if (output_command(cmd, 9) < 0) + return -1; + + /* The execution stage begins when STATUS_READY&STATUS_NON_DMA is set */ + do { + status = fdc_state.fdc_inb(FD_STATUS); + status &= STATUS_READY | STATUS_NON_DMA; + } while(status != (STATUS_READY|STATUS_NON_DMA)); + + for(i = 0; i < max_bytes; i++) { + unsigned char byte; + if ((status = wait_til_ready()) < 0) { + break; + } + status &= STATUS_READY|STATUS_DIR|STATUS_NON_DMA; + if (status != (STATUS_READY|STATUS_DIR|STATUS_NON_DMA)) { + break; + } + byte = fdc_state.fdc_inb(FD_DATA); + if ((i >= byte_offset) && (i < end_offset)) { + dest[i - byte_offset] = byte; + } + } + bytes_read = i; + + /* The result stage begins when STATUS_NON_DMA is cleared */ + while((status = fdc_state.fdc_inb(FD_STATUS)) & STATUS_NON_DMA) { + /* We get extra bytes in the fifo past + * the end of the sector and drop them on the floor. + * Otherwise the fifo is polluted. + */ + fdc_state.fdc_inb(FD_DATA); + } + /* Did I get an error? */ + result_ok = read_ok(head); + /* Did I read enough bytes? */ + ret = -1; + if (result_ok && (bytes_read == max_bytes)) { + ret = bytes_read - byte_offset; + if (ret > length) { + ret = length; + } + } + + if (ret < 0) { + printk_debug("ret = %d\n", ret); + printk_debug("bytes_read = %d\n", bytes_read); + printk_debug("status = %x\n", status); + } + return ret; +} + + +static int __floppy_read(char *dest, unsigned long offset, unsigned long length) +{ + unsigned head, track, sector, byte_offset, sector_offset; + int ret; + + /* break the offset up into sectors and bytes */ + byte_offset = offset % 512; + sector_offset = offset / 512; + + /* Find the disk block we are starting with... */ + sector = (sector_offset % DISK_H1440_SECT) + 1; + head = (sector_offset / DISK_H1440_SECT) % DISK_H1440_HEAD; + track = (sector_offset / (DISK_H1440_SECT *DISK_H1440_HEAD))% DISK_H1440_TRACK; + + /* First seek to our start track */ + if (!floppy_seek(track)) { + return -1; + } + /* Then read the data */ + ret = floppy_read_sectors(dest, byte_offset, length, sector, head, track); + if (ret >= 0) { + return ret; + } + /* If we failed reset the fdc... */ + floppy_reset(); + return -1; +} + +static int floppy_read(char *dest, unsigned long offset, unsigned long length) +{ + int fr_result, bytes_read;; + + printk_debug("floppy_read\n"); + bytes_read = 0; + do { + int max_errors = 3; + do { + fr_result = __floppy_read(dest + bytes_read, offset, + length - bytes_read); + if (max_errors-- == 0) { + return (bytes_read)?bytes_read: -1; + } + } while (fr_result <= 0); + offset += fr_result; + bytes_read += fr_result; + } while(bytes_read < length); + return bytes_read; +} + +/* Determine the floppy disk controller type */ +/* This routine was written by David C. Niemi */ +static char get_fdc_version(void) +{ + int bytes, ret; + unsigned char reply_buffer[MAX_REPLIES]; + + ret = output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */ + if (ret < 0) + return FDC_NONE; + if ((bytes = result(reply_buffer, MAX_REPLIES)) <= 0x00) + return FDC_NONE; /* No FDC present ??? */ + if ((bytes==1) && (reply_buffer[0] == 0x80)){ + printk_info("FDC is an 8272A\n"); + return FDC_8272A; /* 8272a/765 don't know DUMPREGS */ + } + if (bytes != 10) { + printk_debug("init: DUMPREGS: unexpected return of %d bytes.\n", + bytes); + return FDC_UNKNOWN; + } + if (!fdc_configure(USE_IMPLIED_SEEK, USE_FIFO, FIFO_THRESHOLD, + TRACK_PRECOMPENSATION)) { + printk_info("FDC is an 82072\n"); + return FDC_82072; /* 82072 doesn't know CONFIGURE */ + } + + output_byte(FD_PERPENDICULAR); + if (need_more_output() == MORE_OUTPUT) { + output_byte(0); + } else { + printk_info("FDC is an 82072A\n"); + return FDC_82072A; /* 82072A as found on Sparcs. */ + } + + output_byte(FD_UNLOCK); + bytes = result(reply_buffer, MAX_REPLIES); + if ((bytes == 1) && (reply_buffer[0] == 0x80)){ + printk_info("FDC is a pre-1991 82077\n"); + return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know + * LOCK/UNLOCK */ + } + if ((bytes != 1) || (reply_buffer[0] != 0x00)) { + printk_debug("FDC init: UNLOCK: unexpected return of %d bytes.\n", + bytes); + return FDC_UNKNOWN; + } + output_byte(FD_PARTID); + bytes = result(reply_buffer, MAX_REPLIES); + if (bytes != 1) { + printk_debug("FDC init: PARTID: unexpected return of %d bytes.\n", + bytes); + return FDC_UNKNOWN; + } + if (reply_buffer[0] == 0x80) { + printk_info("FDC is a post-1991 82077\n"); + return FDC_82077; /* Revised 82077AA passes all the tests */ + } + switch (reply_buffer[0] >> 5) { + case 0x0: + /* Either a 82078-1 or a 82078SL running at 5Volt */ + printk_info("FDC is an 82078.\n"); + return FDC_82078; + case 0x1: + printk_info("FDC is a 44pin 82078\n"); + return FDC_82078; + case 0x2: + printk_info("FDC is a S82078B\n"); + return FDC_S82078B; + case 0x3: + printk_info("FDC is a National Semiconductor PC87306\n"); + return FDC_87306; + default: + printk_info("FDC init: 82078 variant with unknown PARTID=%d.\n", + reply_buffer[0] >> 5); + return FDC_82078_UNKN; + } +} /* get_fdc_version */ + + +static int floppy_init(unsigned long io_base, unsigned long mmio_base) +{ + printk_debug("floppy_init\n"); + fdc_state.in_sync = 0; + fdc_state.spec1 = -1; + fdc_state.spec2 = -1; + fdc_state.dtr = -1; + fdc_state.dor = DOR_NO_RESET; + fdc_state.version = FDC_UNKNOWN; + if (mmio_base) { + fdc_state.fdc_inb = ob_fdc_mmio_readb; + fdc_state.fdc_outb = ob_fdc_mmio_writeb; + } else { + fdc_state.fdc_inb = ob_fdc_inb; + fdc_state.fdc_outb = ob_fdc_outb; + } + fdc_state.io_base = io_base; + fdc_state.mmio_base = mmio_base; + reset_fdc(); + /* Try to determine the floppy controller type */ + fdc_state.version = get_fdc_version(); + if (fdc_state.version == FDC_NONE) { + return -1; + } + floppy_reset(); + printk_info("fdc_state.version = %04x\n", fdc_state.version); + return 0; +} + +static void floppy_reset(void) +{ + printk_debug("floppy_reset\n"); + floppy_motor_off(FD_DRIVE); + reset_fdc(); + fdc_dtr(DISK_H1440_RATE); + /* program data rate via ccr */ + collect_interrupt(); + fdc_configure(USE_IMPLIED_SEEK, USE_FIFO, FIFO_THRESHOLD, + TRACK_PRECOMPENSATION); + fdc_specify(DRIVE_H1440_HLT, DRIVE_H1440_HUT, DRIVE_H1440_SRT); + set_drive(FD_DRIVE); + floppy_recalibrate(); + fdc_state.in_sync = 1; +} + +static void +ob_floppy_initialize(const char *path) +{ + int props[3]; + phandle_t ph = find_dev(path); + + set_property(ph, "device_type", "block", sizeof("block")); + + // Set dummy reg properties + props[0] = __cpu_to_be32(0); props[1] = __cpu_to_be32(0); props[2] = __cpu_to_be32(0); + set_property(ph, "reg", (char *)&props, 3*sizeof(int)); + + fword("is-deblocker"); +} + + +static void +ob_floppy_open(int *idx) +{ + int ret = 1; + phandle_t ph; + + fword("my-unit"); + idx[0]=POP(); + + fword("my-parent"); + fword("ihandle>phandle"); + ph=(phandle_t)POP(); + + selfword("open-deblocker"); + + /* interpose disk-label */ + ph = find_dev("/packages/disk-label"); + fword("my-args"); + PUSH_ph( ph ); + fword("interpose"); + + RET ( -ret ); +} + +static void +ob_floppy_close(int *idx) +{ + selfword("close-deblocker"); +} + +static void +ob_floppy_read_blocks(int *idx) +{ + cell cnt = POP(); + ucell blk = POP(); + char *dest = (char*)POP(); + floppy_read(dest, blk*512, cnt*512); + PUSH(cnt); +} + + +static void +ob_floppy_block_size(int *idx) +{ + PUSH(512); +} + +static void +ob_floppy_max_transfer(int *idx) +{ + // Fixme + PUSH(18 * 512); +} + +NODE_METHODS(ob_floppy) = { + { "open", ob_floppy_open }, + { "close", ob_floppy_close }, + { "read-blocks", ob_floppy_read_blocks }, + { "block-size", ob_floppy_block_size }, + { "max-transfer", ob_floppy_max_transfer }, +}; + + +int ob_floppy_init(const char *path, const char *dev_name, + unsigned long io_base, unsigned long mmio_base) +{ + char nodebuff[128]; + phandle_t aliases; + + snprintf(nodebuff, sizeof(nodebuff), "%s/%s", path, dev_name); + if (!mmio_base) { + REGISTER_NAMED_NODE(ob_floppy, nodebuff); + ob_floppy_initialize(nodebuff); + } else { + // Already in tree and mapped + REGISTER_NODE_METHODS(ob_floppy, nodebuff); + } + floppy_init(io_base, mmio_base); + + aliases = find_dev("/aliases"); + set_property(aliases, "floppy", nodebuff, strlen(nodebuff) + 1); + + return 0; +} diff --git a/qemu/roms/openbios/drivers/floppy.h b/qemu/roms/openbios/drivers/floppy.h new file mode 100644 index 000000000..b0f30d555 --- /dev/null +++ b/qemu/roms/openbios/drivers/floppy.h @@ -0,0 +1,9 @@ +#ifndef FLOPPY_SUBR_H +#define FLOPPY_SUBR_H + +int floppy_init(void); +int floppy_read(char *dest, unsigned long offset, unsigned long length); +void floppy_fini(void); + + +#endif /* FLOPPY_SUBR_H */ diff --git a/qemu/roms/openbios/drivers/fw_cfg.c b/qemu/roms/openbios/drivers/fw_cfg.c new file mode 100644 index 000000000..402757070 --- /dev/null +++ b/qemu/roms/openbios/drivers/fw_cfg.c @@ -0,0 +1,76 @@ +#include "config.h" +#include "libopenbios/bindings.h" +#include "libc/byteorder.h" +#include "libopenbios/ofmem.h" +#define NO_QEMU_PROTOS +#include "arch/common/fw_cfg.h" + +#if !defined(CONFIG_SPARC64) +static volatile uint16_t *fw_cfg_cmd; +static volatile uint8_t *fw_cfg_data; + +void +fw_cfg_read(uint16_t cmd, char *buf, unsigned int nbytes) +{ + unsigned int i; + + *fw_cfg_cmd = cmd; + for (i = 0; i < nbytes; i++) + buf[i] = *fw_cfg_data; +} +#else +// XXX depends on PCI bus location, should be removed +void +fw_cfg_read(uint16_t cmd, char *buf, unsigned int nbytes) +{ + unsigned int i; + + outw(cmd, CONFIG_FW_CFG_ADDR); + for (i = 0; i < nbytes; i++) + buf[i] = inb(CONFIG_FW_CFG_ADDR + 1); +} +#endif + +uint64_t +fw_cfg_read_i64(uint16_t cmd) +{ + uint64_t buf; + + fw_cfg_read(cmd, (char *)&buf, sizeof(uint64_t)); + + return __le64_to_cpu(buf); +} + +uint32_t +fw_cfg_read_i32(uint16_t cmd) +{ + uint32_t buf; + + fw_cfg_read(cmd, (char *)&buf, sizeof(uint32_t)); + + return __le32_to_cpu(buf); +} + +uint16_t +fw_cfg_read_i16(uint16_t cmd) +{ + uint16_t buf; + + fw_cfg_read(cmd, (char *)&buf, sizeof(uint16_t)); + + return __le16_to_cpu(buf); +} + +void +fw_cfg_init(void) +{ +#if defined(CONFIG_SPARC32) + fw_cfg_cmd = (void *)ofmem_map_io(CONFIG_FW_CFG_ADDR, 2); + fw_cfg_data = (uint8_t *)fw_cfg_cmd + 2; +#elif defined(CONFIG_SPARC64) + // Nothing for the port version +#elif defined(CONFIG_PPC) + fw_cfg_cmd = (void *)CONFIG_FW_CFG_ADDR; + fw_cfg_data = (void *)(CONFIG_FW_CFG_ADDR + 2); +#endif +} diff --git a/qemu/roms/openbios/drivers/hdreg.h b/qemu/roms/openbios/drivers/hdreg.h new file mode 100644 index 000000000..91e4d1ff6 --- /dev/null +++ b/qemu/roms/openbios/drivers/hdreg.h @@ -0,0 +1,289 @@ +/* + * this header holds data structures as dictated by spec + */ +#ifndef HDREG_H +#define HDREG_H + +struct hd_driveid { + unsigned short config; /* lots of obsolete bit flags */ + unsigned short cyls; /* Obsolete, "physical" cyls */ + unsigned short reserved2; /* reserved (word 2) */ + unsigned short heads; /* Obsolete, "physical" heads */ + unsigned short track_bytes; /* unformatted bytes per track */ + unsigned short sector_bytes; /* unformatted bytes per sector */ + unsigned short sectors; /* Obsolete, "physical" sectors per track */ + unsigned short vendor0; /* vendor unique */ + unsigned short vendor1; /* vendor unique */ + unsigned short vendor2; /* Retired vendor unique */ + unsigned char serial_no[20]; /* 0 = not_specified */ + unsigned short buf_type; /* Retired */ + unsigned short buf_size; /* Retired, 512 byte increments + * 0 = not_specified + */ + unsigned short ecc_bytes; /* for r/w long cmds; 0 = not_specified */ + unsigned char fw_rev[8]; /* 0 = not_specified */ + unsigned char model[40]; /* 0 = not_specified */ + unsigned char max_multsect; /* 0=not_implemented */ + unsigned char vendor3; /* vendor unique */ + unsigned short dword_io; /* 0=not_implemented; 1=implemented */ + unsigned char vendor4; /* vendor unique */ + unsigned char capability; /* (upper byte of word 49) + * 3: IORDYsup + * 2: IORDYsw + * 1: LBA + * 0: DMA + */ + unsigned short reserved50; /* reserved (word 50) */ + unsigned char vendor5; /* Obsolete, vendor unique */ + unsigned char tPIO; /* Obsolete, 0=slow, 1=medium, 2=fast */ + unsigned char vendor6; /* Obsolete, vendor unique */ + unsigned char tDMA; /* Obsolete, 0=slow, 1=medium, 2=fast */ + unsigned short field_valid; /* (word 53) + * 2: ultra_ok word 88 + * 1: eide_ok words 64-70 + * 0: cur_ok words 54-58 + */ + unsigned short cur_cyls; /* Obsolete, logical cylinders */ + unsigned short cur_heads; /* Obsolete, l heads */ + unsigned short cur_sectors; /* Obsolete, l sectors per track */ + unsigned short cur_capacity0; /* Obsolete, l total sectors on drive */ + unsigned short cur_capacity1; /* Obsolete, (2 words, misaligned int) */ + unsigned char multsect; /* current multiple sector count */ + unsigned char multsect_valid; /* when (bit0==1) multsect is ok */ + unsigned int lba_capacity; /* Obsolete, total number of sectors */ + unsigned short dma_1word; /* Obsolete, single-word dma info */ + unsigned short dma_mword; /* multiple-word dma info */ + unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */ + unsigned short eide_dma_min; /* min mword dma cycle time (ns) */ + unsigned short eide_dma_time; /* recommended mword dma cycle time (ns) */ + unsigned short eide_pio; /* min cycle time (ns), no IORDY */ + unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */ + unsigned short words69_70[2]; /* reserved words 69-70 + * future command overlap and queuing + */ + /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */ + unsigned short words71_74[4]; /* reserved words 71-74 + * for IDENTIFY PACKET DEVICE command + */ + unsigned short queue_depth; /* (word 75) + * 15:5 reserved + * 4:0 Maximum queue depth -1 + */ + unsigned short words76_79[4]; /* reserved words 76-79 */ + unsigned short major_rev_num; /* (word 80) */ + unsigned short minor_rev_num; /* (word 81) */ + unsigned short command_set_1; /* (word 82) supported + * 15: Obsolete + * 14: NOP command + * 13: READ_BUFFER + * 12: WRITE_BUFFER + * 11: Obsolete + * 10: Host Protected Area + * 9: DEVICE Reset + * 8: SERVICE Interrupt + * 7: Release Interrupt + * 6: look-ahead + * 5: write cache + * 4: PACKET Command + * 3: Power Management Feature Set + * 2: Removable Feature Set + * 1: Security Feature Set + * 0: SMART Feature Set + */ + unsigned short command_set_2; /* (word 83) + * 15: Shall be ZERO + * 14: Shall be ONE + * 13: FLUSH CACHE EXT + * 12: FLUSH CACHE + * 11: Device Configuration Overlay + * 10: 48-bit Address Feature Set + * 9: Automatic Acoustic Management + * 8: SET MAX security + * 7: reserved 1407DT PARTIES + * 6: SetF sub-command Power-Up + * 5: Power-Up in Standby Feature Set + * 4: Removable Media Notification + * 3: APM Feature Set + * 2: CFA Feature Set + * 1: READ/WRITE DMA QUEUED + * 0: Download MicroCode + */ + unsigned short cfsse; /* (word 84) + * cmd set-feature supported extensions + * 15: Shall be ZERO + * 14: Shall be ONE + * 13:6 reserved + * 5: General Purpose Logging + * 4: Streaming Feature Set + * 3: Media Card Pass Through + * 2: Media Serial Number Valid + * 1: SMART selt-test supported + * 0: SMART error logging + */ + unsigned short cfs_enable_1; /* (word 85) + * command set-feature enabled + * 15: Obsolete + * 14: NOP command + * 13: READ_BUFFER + * 12: WRITE_BUFFER + * 11: Obsolete + * 10: Host Protected Area + * 9: DEVICE Reset + * 8: SERVICE Interrupt + * 7: Release Interrupt + * 6: look-ahead + * 5: write cache + * 4: PACKET Command + * 3: Power Management Feature Set + * 2: Removable Feature Set + * 1: Security Feature Set + * 0: SMART Feature Set + */ + unsigned short cfs_enable_2; /* (word 86) + * command set-feature enabled + * 15: Shall be ZERO + * 14: Shall be ONE + * 13: FLUSH CACHE EXT + * 12: FLUSH CACHE + * 11: Device Configuration Overlay + * 10: 48-bit Address Feature Set + * 9: Automatic Acoustic Management + * 8: SET MAX security + * 7: reserved 1407DT PARTIES + * 6: SetF sub-command Power-Up + * 5: Power-Up in Standby Feature Set + * 4: Removable Media Notification + * 3: APM Feature Set + * 2: CFA Feature Set + * 1: READ/WRITE DMA QUEUED + * 0: Download MicroCode + */ + unsigned short csf_default; /* (word 87) + * command set-feature default + * 15: Shall be ZERO + * 14: Shall be ONE + * 13:6 reserved + * 5: General Purpose Logging enabled + * 4: Valid CONFIGURE STREAM executed + * 3: Media Card Pass Through enabled + * 2: Media Serial Number Valid + * 1: SMART selt-test supported + * 0: SMART error logging + */ + unsigned short dma_ultra; /* (word 88) */ + unsigned short trseuc; /* time required for security erase */ + unsigned short trsEuc; /* time required for enhanced erase */ + unsigned short CurAPMvalues; /* current APM values */ + unsigned short mprc; /* master password revision code */ + unsigned short hw_config; /* hardware config (word 93) + * 15: Shall be ZERO + * 14: Shall be ONE + * 13: + * 12: + * 11: + * 10: + * 9: + * 8: + * 7: + * 6: + * 5: + * 4: + * 3: + * 2: + * 1: + * 0: Shall be ONE + */ + unsigned short acoustic; /* (word 94) + * 15:8 Vendor's recommended value + * 7:0 current value + */ + unsigned short msrqs; /* min stream request size */ + unsigned short sxfert; /* stream transfer time */ + unsigned short sal; /* stream access latency */ + unsigned int spg; /* stream performance granularity */ + unsigned long long lba_capacity_2;/* 48-bit total number of sectors */ + unsigned short words104_125[22];/* reserved words 104-125 */ + unsigned short last_lun; /* (word 126) */ + unsigned short word127; /* (word 127) Feature Set + * Removable Media Notification + * 15:2 reserved + * 1:0 00 = not supported + * 01 = supported + * 10 = reserved + * 11 = reserved + */ + unsigned short dlf; /* (word 128) + * device lock function + * 15:9 reserved + * 8 security level 1:max 0:high + * 7:6 reserved + * 5 enhanced erase + * 4 expire + * 3 frozen + * 2 locked + * 1 en/disabled + * 0 capability + */ + unsigned short csfo; /* (word 129) + * current set features options + * 15:4 reserved + * 3: auto reassign + * 2: reverting + * 1: read-look-ahead + * 0: write cache + */ + unsigned short words130_155[26];/* reserved vendor words 130-155 */ + unsigned short word156; /* reserved vendor word 156 */ + unsigned short words157_159[3];/* reserved vendor words 157-159 */ + unsigned short cfa_power; /* (word 160) CFA Power Mode + * 15 word 160 supported + * 14 reserved + * 13 + * 12 + * 11:0 + */ + unsigned short words161_175[15];/* Reserved for CFA */ + unsigned short words176_205[30];/* Current Media Serial Number */ + unsigned short words206_254[49];/* reserved words 206-254 */ + unsigned short integrity_word; /* (word 255) + * 15:8 Checksum + * 7:0 Signature + */ +}; + +struct request_sense { +#if defined(CONFIG_BIG_ENDIAN) + u8 valid : 1; + u8 error_code : 7; +#elif defined(CONFIG_LITTLE_ENDIAN) + u8 error_code : 7; + u8 valid : 1; +#endif + u8 segment_number; +#if defined(CONFIG_BIG_ENDIAN) + u8 reserved1 : 2; + u8 ili : 1; + u8 reserved2 : 1; + u8 sense_key : 4; +#elif defined(CONFIG_LITTLE_ENDIAN) + u8 sense_key : 4; + u8 reserved2 : 1; + u8 ili : 1; + u8 reserved1 : 2; +#endif + u8 information[4]; + u8 add_sense_len; + u8 command_info[4]; + u8 asc; + u8 ascq; + u8 fruc; + u8 sks[3]; + u8 asb[46]; +}; + +struct atapi_capacity { + u32 lba; + u32 block_size; +}; + +#endif diff --git a/qemu/roms/openbios/drivers/ide.c b/qemu/roms/openbios/drivers/ide.c new file mode 100644 index 000000000..327c64a40 --- /dev/null +++ b/qemu/roms/openbios/drivers/ide.c @@ -0,0 +1,1685 @@ +/* + * OpenBIOS polled ide driver + * + * Copyright (C) 2004 Jens Axboe <axboe@suse.de> + * Copyright (C) 2005 Stefan Reinauer <stepan@openbios.org> + * + * Credit goes to Hale Landis for his excellent ata demo software + * OF node handling and some fixes by Stefan Reinauer + * + * 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 "ide.h" +#include "hdreg.h" +#include "timer.h" + +#ifdef CONFIG_DEBUG_IDE +#define IDE_DPRINTF(fmt, args...) \ +do { printk("IDE - %s: " fmt, __func__ , ##args); } while (0) +#else +#define IDE_DPRINTF(fmt, args...) do { } while (0) +#endif + +/* DECLARE data structures for the nodes. */ +DECLARE_UNNAMED_NODE( ob_ide, INSTALL_OPEN, sizeof(struct ide_drive*) ); +DECLARE_UNNAMED_NODE( ob_ide_ctrl, INSTALL_OPEN, sizeof(int)); + +/* + * define to 2 for the standard 2 channels only + */ +#ifndef CONFIG_IDE_NUM_CHANNELS +#define IDE_NUM_CHANNELS 4 +#else +#define IDE_NUM_CHANNELS CONFIG_IDE_NUM_CHANNELS +#endif +#define IDE_MAX_CHANNELS 4 + +#ifndef CONFIG_IDE_FIRST_UNIT +#define FIRST_UNIT 0 +#else +#define FIRST_UNIT CONFIG_IDE_FIRST_UNIT +#endif + +#ifndef CONFIG_IDE_DEV_TYPE +#define DEV_TYPE "ide" +#else +#define DEV_TYPE CONFIG_IDE_DEV_TYPE +#endif + +#ifndef CONFIG_IDE_DEV_NAME +#define DEV_NAME "ide%d" +#else +#define DEV_NAME CONFIG_IDE_DEV_NAME +#endif + +static int current_channel = FIRST_UNIT; + +static struct ide_channel *channels = NULL; + +static inline void ide_add_channel(struct ide_channel *chan) +{ + chan->next = channels; + channels = chan; +} + +static struct ide_channel *ide_seek_channel(const char *name) +{ + struct ide_channel *current; + + current = channels; + while (current) { + if (!strcmp(current->name, name)) + return current; + current = current->next; + } + return NULL; +} + +/* + * don't be pedantic + */ +#undef ATA_PEDANTIC + +static void dump_drive(struct ide_drive *drive) +{ +#ifdef CONFIG_DEBUG_IDE + printk("IDE DRIVE @%lx:\n", (unsigned long)drive); + printk("unit: %d\n",drive->unit); + printk("present: %d\n",drive->present); + printk("type: %d\n",drive->type); + printk("media: %d\n",drive->media); + printk("model: %s\n",drive->model); + printk("nr: %d\n",drive->nr); + printk("cyl: %d\n",drive->cyl); + printk("head: %d\n",drive->head); + printk("sect: %d\n",drive->sect); + printk("bs: %d\n",drive->bs); +#endif +} + +/* + * old style io port operations + */ +static unsigned char +ob_ide_inb(struct ide_channel *chan, unsigned int port) +{ + return inb(chan->io_regs[port]); +} + +static void +ob_ide_outb(struct ide_channel *chan, unsigned char data, unsigned int port) +{ + outb(data, chan->io_regs[port]); +} + +static void +ob_ide_insw(struct ide_channel *chan, + unsigned int port, unsigned char *addr, unsigned int count) +{ + insw(chan->io_regs[port], addr, count); +} + +static void +ob_ide_outsw(struct ide_channel *chan, + unsigned int port, unsigned char *addr, unsigned int count) +{ + outsw(chan->io_regs[port], addr, count); +} + +static inline unsigned char +ob_ide_pio_readb(struct ide_drive *drive, unsigned int offset) +{ + struct ide_channel *chan = drive->channel; + + return chan->obide_inb(chan, offset); +} + +static inline void +ob_ide_pio_writeb(struct ide_drive *drive, unsigned int offset, + unsigned char data) +{ + struct ide_channel *chan = drive->channel; + + chan->obide_outb(chan, data, offset); +} + +static inline void +ob_ide_pio_insw(struct ide_drive *drive, unsigned int offset, + unsigned char *addr, unsigned int len) +{ + struct ide_channel *chan = drive->channel; + + if (len & 1) { + IDE_DPRINTF("%d: command not word aligned\n", drive->nr); + return; + } + + chan->obide_insw(chan, offset, addr, len / 2); +} + +static inline void +ob_ide_pio_outsw(struct ide_drive *drive, unsigned int offset, + unsigned char *addr, unsigned int len) +{ + struct ide_channel *chan = drive->channel; + + if (len & 1) { + IDE_DPRINTF("%d: command not word aligned\n", drive->nr); + return; + } + + chan->obide_outsw(chan, offset, addr, len / 2); +} + +static void +ob_ide_400ns_delay(struct ide_drive *drive) +{ + (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS); + (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS); + (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS); + (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS); + + udelay(1); +} + +static void +ob_ide_error(struct ide_drive *drive, unsigned char stat, const char *msg) +{ +#ifdef CONFIG_DEBUG_IDE + struct ide_channel *chan = drive->channel; + unsigned char err; +#endif + + if (!stat) + stat = ob_ide_pio_readb(drive, IDEREG_STATUS); + + IDE_DPRINTF("ob_ide_error drive<%d>: %s:\n", drive->nr, msg); + IDE_DPRINTF(" cmd=%x, stat=%x", chan->ata_cmd.command, stat); + + if ((stat & (BUSY_STAT | ERR_STAT)) == ERR_STAT) { +#ifdef CONFIG_DEBUG_IDE + err = +#endif + ob_ide_pio_readb(drive, IDEREG_ERROR); + IDE_DPRINTF(", err=%x", err); + } + IDE_DPRINTF("\n"); + +#ifdef CONFIG_DEBUG_IDE + /* + * see if sense is valid and dump that + */ + if (chan->ata_cmd.command == WIN_PACKET) { + struct atapi_command *cmd = &chan->atapi_cmd; + unsigned char old_cdb = cmd->cdb[0]; + + if (cmd->cdb[0] == ATAPI_REQ_SENSE) { + old_cdb = cmd->old_cdb; + + IDE_DPRINTF(" atapi opcode=%02x", old_cdb); + } else { + int i; + + IDE_DPRINTF(" cdb: "); + for (i = 0; i < sizeof(cmd->cdb); i++) + IDE_DPRINTF("%02x ", cmd->cdb[i]); + } + if (cmd->sense_valid) + IDE_DPRINTF(", sense: %02x/%02x/%02x", + cmd->sense.sense_key, cmd->sense.asc, + cmd->sense.ascq); + else + IDE_DPRINTF(", no sense"); + IDE_DPRINTF("\n"); + } +#endif +} + +/* + * wait for 'stat' to be set. returns 1 if failed, 0 if succesful + */ +static int +ob_ide_wait_stat(struct ide_drive *drive, unsigned char ok_stat, + unsigned char bad_stat, unsigned char *ret_stat) +{ + unsigned char stat; + int i; + + ob_ide_400ns_delay(drive); + + for (i = 0; i < 5000; i++) { + stat = ob_ide_pio_readb(drive, IDEREG_STATUS); + if (!(stat & BUSY_STAT)) + break; + + udelay(1000); + } + + if (ret_stat) + *ret_stat = stat; + + if (stat & bad_stat) + return 1; + + if ((stat & ok_stat) || !ok_stat) + return 0; + + return 1; +} + +static int +ob_ide_select_drive(struct ide_drive *drive) +{ + struct ide_channel *chan = drive->channel; + unsigned char control = IDEHEAD_DEV0; + + if (ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL)) { + IDE_DPRINTF("select_drive: timed out\n"); + return 1; + } + + /* + * don't select drive if already active. Note: we always + * wait for BUSY clear + */ + if (drive->unit == chan->selected) + return 0; + + if (drive->unit) + control = IDEHEAD_DEV1; + + ob_ide_pio_writeb(drive, IDEREG_CURRENT, control); + ob_ide_400ns_delay(drive); + + if (ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL)) { + IDE_DPRINTF("select_drive: timed out\n"); + return 1; + } + + chan->selected = drive->unit; + return 0; +} + +static void +ob_ide_write_tasklet(struct ide_drive *drive, struct ata_command *cmd) +{ + ob_ide_pio_writeb(drive, IDEREG_FEATURE, cmd->task[1]); + ob_ide_pio_writeb(drive, IDEREG_NSECTOR, cmd->task[3]); + ob_ide_pio_writeb(drive, IDEREG_SECTOR, cmd->task[7]); + ob_ide_pio_writeb(drive, IDEREG_LCYL, cmd->task[8]); + ob_ide_pio_writeb(drive, IDEREG_HCYL, cmd->task[9]); + + ob_ide_pio_writeb(drive, IDEREG_FEATURE, cmd->task[0]); + ob_ide_pio_writeb(drive, IDEREG_NSECTOR, cmd->task[2]); + ob_ide_pio_writeb(drive, IDEREG_SECTOR, cmd->task[4]); + ob_ide_pio_writeb(drive, IDEREG_LCYL, cmd->task[5]); + ob_ide_pio_writeb(drive, IDEREG_HCYL, cmd->task[6]); + + if (drive->unit) + cmd->device_head |= IDEHEAD_DEV1; + + ob_ide_pio_writeb(drive, IDEREG_CURRENT, cmd->device_head); + + ob_ide_pio_writeb(drive, IDEREG_COMMAND, cmd->command); + ob_ide_400ns_delay(drive); +} + +static void +ob_ide_write_registers(struct ide_drive *drive, struct ata_command *cmd) +{ + /* + * we are _always_ polled + */ + ob_ide_pio_writeb(drive, IDEREG_CONTROL, cmd->control | IDECON_NIEN); + + ob_ide_pio_writeb(drive, IDEREG_FEATURE, cmd->feature); + ob_ide_pio_writeb(drive, IDEREG_NSECTOR, cmd->nsector); + ob_ide_pio_writeb(drive, IDEREG_SECTOR, cmd->sector); + ob_ide_pio_writeb(drive, IDEREG_LCYL, cmd->lcyl); + ob_ide_pio_writeb(drive, IDEREG_HCYL, cmd->hcyl); + + if (drive->unit) + cmd->device_head |= IDEHEAD_DEV1; + + ob_ide_pio_writeb(drive, IDEREG_CURRENT, cmd->device_head); + + ob_ide_pio_writeb(drive, IDEREG_COMMAND, cmd->command); + ob_ide_400ns_delay(drive); +} + +/* + * execute command with "pio non data" protocol + */ +#if 0 +static int +ob_ide_pio_non_data(struct ide_drive *drive, struct ata_command *cmd) +{ + if (ob_ide_select_drive(drive)) + return 1; + + ob_ide_write_registers(drive, cmd); + + if (ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL)) + return 1; + + return 0; +} +#endif + +/* + * execute given command with a pio data-in phase. + */ +static int +ob_ide_pio_data_in(struct ide_drive *drive, struct ata_command *cmd) +{ + unsigned char stat; + unsigned int bytes, timeout; + + if (ob_ide_select_drive(drive)) + return 1; + + /* + * ATA must set ready and seek stat, ATAPI need only clear busy + */ + timeout = 0; + do { + stat = ob_ide_pio_readb(drive, IDEREG_STATUS); + + if (drive->type == ide_type_ata) { + /* + * this is BIOS code, don't be too pedantic + */ +#ifdef ATA_PEDANTIC + if ((stat & (BUSY_STAT | READY_STAT | SEEK_STAT)) == + (READY_STAT | SEEK_STAT)) + break; +#else + if ((stat & (BUSY_STAT | READY_STAT)) == READY_STAT) + break; +#endif + } else { + if (!(stat & BUSY_STAT)) + break; + } + ob_ide_400ns_delay(drive); + } while (timeout++ < 1000); + + if (timeout >= 1000) { + ob_ide_error(drive, stat, "drive timed out"); + cmd->stat = stat; + return 1; + } + + ob_ide_write_registers(drive, cmd); + + /* + * now read the data + */ + bytes = cmd->buflen; + do { + unsigned count = cmd->buflen; + + if (count > drive->bs) + count = drive->bs; + + /* delay 100ms for ATAPI? */ + + /* + * wait for BUSY clear + */ + if (ob_ide_wait_stat(drive, 0, BUSY_STAT | ERR_STAT, &stat)) { + ob_ide_error(drive, stat, "timed out waiting for BUSY clear"); + cmd->stat = stat; + break; + } + + /* + * transfer the data + */ + if ((stat & (BUSY_STAT | DRQ_STAT)) == DRQ_STAT) { + ob_ide_pio_insw(drive, IDEREG_DATA, cmd->buffer, count); + cmd->bytes -= count; + cmd->buffer += count; + bytes -= count; + + ob_ide_400ns_delay(drive); + } + + if (stat & (BUSY_STAT | WRERR_STAT | ERR_STAT)) { + cmd->stat = stat; + break; + } + + if (!(stat & DRQ_STAT)) { + cmd->stat = stat; + break; + } + } while (bytes); + + if (bytes) + IDE_DPRINTF("bytes=%d, stat=%x\n", bytes, stat); + + return bytes ? 1 : 0; +} + +/* + * execute ata command with pio packet protocol + */ +static int +ob_ide_pio_packet(struct ide_drive *drive, struct atapi_command *cmd) +{ + unsigned char stat, reason, lcyl, hcyl; + struct ata_command *acmd = &drive->channel->ata_cmd; + unsigned char *buffer; + unsigned int bytes; + + if (ob_ide_select_drive(drive)) + return 1; + + if (cmd->buflen && cmd->data_direction == atapi_ddir_none) + IDE_DPRINTF("non-zero buflen but no data direction\n"); + + memset(acmd, 0, sizeof(*acmd)); + acmd->lcyl = cmd->buflen & 0xff; + acmd->hcyl = (cmd->buflen >> 8) & 0xff; + acmd->command = WIN_PACKET; + ob_ide_write_registers(drive, acmd); + + /* + * BUSY must be set, _or_ DRQ | ERR + */ + stat = ob_ide_pio_readb(drive, IDEREG_ASTATUS); + if ((stat & BUSY_STAT) == 0) { + if (!(stat & (DRQ_STAT | ERR_STAT))) { + ob_ide_error(drive, stat, "bad stat in atapi cmd"); + cmd->stat = stat; + return 1; + } + } + + if (ob_ide_wait_stat(drive, 0, BUSY_STAT | ERR_STAT, &stat)) { + ob_ide_error(drive, stat, "timeout, ATAPI BUSY clear"); + cmd->stat = stat; + return 1; + } + + if ((stat & (BUSY_STAT | DRQ_STAT | ERR_STAT)) != DRQ_STAT) { + /* + * if command isn't request sense, then we have a problem. if + * we are doing a sense, ERR_STAT == CHECK_CONDITION + */ + if (cmd->cdb[0] != ATAPI_REQ_SENSE) { + IDE_DPRINTF("odd, drive didn't want to transfer %x\n", + stat); + return 1; + } + } + + /* + * transfer cdb + */ + ob_ide_pio_outsw(drive, IDEREG_DATA, cmd->cdb,sizeof(cmd->cdb)); + ob_ide_400ns_delay(drive); + + /* + * ok, cdb was sent to drive, now do data transfer (if any) + */ + bytes = cmd->buflen; + buffer = cmd->buffer; + do { + unsigned int bc; + + if (ob_ide_wait_stat(drive, 0, BUSY_STAT | ERR_STAT, &stat)) { + ob_ide_error(drive, stat, "busy not clear after cdb"); + cmd->stat = stat; + break; + } + + /* + * transfer complete! + */ + if ((stat & (BUSY_STAT | DRQ_STAT)) == 0) + break; + + if ((stat & (BUSY_STAT | DRQ_STAT)) != DRQ_STAT) + break; + + reason = ob_ide_pio_readb(drive, IDEREG_NSECTOR); + lcyl = ob_ide_pio_readb(drive, IDEREG_LCYL); + hcyl = ob_ide_pio_readb(drive, IDEREG_HCYL); + + /* + * check if the drive wants to transfer data in the same + * direction as we do... + */ + if ((reason & IREASON_CD) && cmd->data_direction != atapi_ddir_read) { + ob_ide_error(drive, stat, "atapi, bad transfer ddir"); + break; + } + + bc = (hcyl << 8) | lcyl; + if (!bc) + break; + + if (bc > bytes) + bc = bytes; + + if (cmd->data_direction == atapi_ddir_read) + ob_ide_pio_insw(drive, IDEREG_DATA, buffer, bc); + else + ob_ide_pio_outsw(drive, IDEREG_DATA, buffer, bc); + + bytes -= bc; + buffer += bc; + + ob_ide_400ns_delay(drive); + } while (bytes); + + if (cmd->data_direction != atapi_ddir_none) + (void) ob_ide_wait_stat(drive, 0, BUSY_STAT, &stat); + + if (bytes) + IDE_DPRINTF("cdb failed, bytes=%d, stat=%x\n", bytes, stat); + + return (stat & ERR_STAT) || bytes; +} + +/* + * execute a packet command, with retries if appropriate + */ +static int +ob_ide_atapi_packet(struct ide_drive *drive, struct atapi_command *cmd) +{ + int retries = 5, ret; + + if (drive->type != ide_type_atapi) + return 1; + if (cmd->buflen > 0xffff) + return 1; + + /* + * retry loop + */ + do { + ret = ob_ide_pio_packet(drive, cmd); + if (!ret) + break; + + /* + * request sense failed, bummer + */ + if (cmd->cdb[0] == ATAPI_REQ_SENSE) + break; + + if (ob_ide_atapi_request_sense(drive)) + break; + + /* + * we know sense is valid. retry if the drive isn't ready, + * otherwise don't bother. + */ + if (cmd->sense.sense_key != ATAPI_SENSE_NOT_READY) + break; + /* + * ... except 'medium not present' + */ + if (cmd->sense.asc == 0x3a) + break; + + udelay(1000000); + } while (retries--); + + if (ret) + ob_ide_error(drive, 0, "atapi command"); + + return ret; +} + +static int +ob_ide_atapi_request_sense(struct ide_drive *drive) +{ + struct atapi_command *cmd = &drive->channel->atapi_cmd; + unsigned char old_cdb; + + /* + * save old cdb for debug error + */ + old_cdb = cmd->cdb[0]; + + memset(cmd, 0, sizeof(*cmd)); + cmd->cdb[0] = ATAPI_REQ_SENSE; + cmd->cdb[4] = 18; + cmd->buffer = (unsigned char *) &cmd->sense; + cmd->buflen = 18; + cmd->data_direction = atapi_ddir_read; + cmd->old_cdb = old_cdb; + + if (ob_ide_atapi_packet(drive, cmd)) + return 1; + + cmd->sense_valid = 1; + return 0; +} + +/* + * make sure drive is ready and media loaded + */ +static int +ob_ide_atapi_drive_ready(struct ide_drive *drive) +{ + struct atapi_command *cmd = &drive->channel->atapi_cmd; + struct atapi_capacity cap; + + IDE_DPRINTF("ob_ide_atapi_drive_ready\n"); + + /* + * Test Unit Ready is like a ping + */ + memset(cmd, 0, sizeof(*cmd)); + cmd->cdb[0] = ATAPI_TUR; + + if (ob_ide_atapi_packet(drive, cmd)) { + IDE_DPRINTF("%d: TUR failed\n", drive->nr); + return 1; + } + + /* + * don't force load of tray (bit 2 in byte 4 of cdb), it's + * annoying and we don't want to deal with errors from drives + * that cannot do it + */ + memset(cmd, 0, sizeof(*cmd)); + cmd->cdb[0] = ATAPI_START_STOP_UNIT; + cmd->cdb[4] = 0x01; + + if (ob_ide_atapi_packet(drive, cmd)) { + IDE_DPRINTF("%d: START_STOP unit failed\n", drive->nr); + return 1; + } + + /* + * finally, get capacity and block size + */ + memset(cmd, 0, sizeof(*cmd)); + memset(&cap, 0, sizeof(cap)); + + cmd->cdb[0] = ATAPI_READ_CAPACITY; + cmd->buffer = (unsigned char *) ∩ + cmd->buflen = sizeof(cap); + cmd->data_direction = atapi_ddir_read; + + if (ob_ide_atapi_packet(drive, cmd)) { + drive->sectors = 0x1fffff; + drive->bs = 2048; + return 1; + } + + drive->sectors = __be32_to_cpu(cap.lba) + 1; + drive->bs = __be32_to_cpu(cap.block_size); + return 0; +} + +/* + * read from an atapi device, using READ_10 + */ +static int +ob_ide_read_atapi(struct ide_drive *drive, unsigned long long block, + unsigned char *buf, unsigned int sectors) +{ + struct atapi_command *cmd = &drive->channel->atapi_cmd; + + if (ob_ide_atapi_drive_ready(drive)) + return 1; + + memset(cmd, 0, sizeof(*cmd)); + + /* + * READ_10 should work on generally any atapi device + */ + cmd->cdb[0] = ATAPI_READ_10; + cmd->cdb[2] = (block >> 24) & 0xff; + cmd->cdb[3] = (block >> 16) & 0xff; + cmd->cdb[4] = (block >> 8) & 0xff; + cmd->cdb[5] = block & 0xff; + cmd->cdb[7] = (sectors >> 8) & 0xff; + cmd->cdb[8] = sectors & 0xff; + + cmd->buffer = buf; + cmd->buflen = sectors * 2048; + cmd->data_direction = atapi_ddir_read; + + return ob_ide_atapi_packet(drive, cmd); +} + +static int +ob_ide_read_ata_chs(struct ide_drive *drive, unsigned long long block, + unsigned char *buf, unsigned int sectors) +{ + struct ata_command *cmd = &drive->channel->ata_cmd; + unsigned int track = (block / drive->sect); + unsigned int sect = (block % drive->sect) + 1; + unsigned int head = (track % drive->head); + unsigned int cyl = (track / drive->head); + + /* + * fill in chs command to read from disk at given location + */ + cmd->buffer = buf; + cmd->buflen = sectors * 512; + + cmd->nsector = sectors & 0xff; + cmd->sector = sect; + cmd->lcyl = cyl; + cmd->hcyl = cyl >> 8; + cmd->device_head = head; + + cmd->command = WIN_READ; + + return ob_ide_pio_data_in(drive, cmd); +} + +static int +ob_ide_read_ata_lba28(struct ide_drive *drive, unsigned long long block, + unsigned char *buf, unsigned int sectors) +{ + struct ata_command *cmd = &drive->channel->ata_cmd; + + memset(cmd, 0, sizeof(*cmd)); + + /* + * fill in 28-bit lba command to read from disk at given location + */ + cmd->buffer = buf; + cmd->buflen = sectors * 512; + + cmd->nsector = sectors; + cmd->sector = block; + cmd->lcyl = block >>= 8; + cmd->hcyl = block >>= 8; + cmd->device_head = ((block >> 8) & 0x0f); + cmd->device_head |= (1 << 6); + + cmd->command = WIN_READ; + + return ob_ide_pio_data_in(drive, cmd); +} + +static int +ob_ide_read_ata_lba48(struct ide_drive *drive, unsigned long long block, + unsigned char *buf, unsigned int sectors) +{ + struct ata_command *cmd = &drive->channel->ata_cmd; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->buffer = buf; + cmd->buflen = sectors * 512; + + /* + * we are using tasklet addressing here + */ + cmd->task[2] = sectors; + cmd->task[3] = sectors >> 8; + cmd->task[4] = block; + cmd->task[5] = block >> 8; + cmd->task[6] = block >> 16; + cmd->task[7] = block >> 24; + cmd->task[8] = (u64) block >> 32; + cmd->task[9] = (u64) block >> 40; + + cmd->command = WIN_READ_EXT; + + ob_ide_write_tasklet(drive, cmd); + + return ob_ide_pio_data_in(drive, cmd); +} +/* + * read 'sectors' sectors from ata device + */ +static int +ob_ide_read_ata(struct ide_drive *drive, unsigned long long block, + unsigned char *buf, unsigned int sectors) +{ + unsigned long long end_block = block + sectors; + const int need_lba48 = (end_block > (1ULL << 28)) || (sectors > 255); + + if (end_block > drive->sectors) + return 1; + if (need_lba48 && drive->addressing != ide_lba48) + return 1; + + /* + * use lba48 if we have to, otherwise use the faster lba28 + */ + if (need_lba48) + return ob_ide_read_ata_lba48(drive, block, buf, sectors); + else if (drive->addressing != ide_chs) + return ob_ide_read_ata_lba28(drive, block, buf, sectors); + + return ob_ide_read_ata_chs(drive, block, buf, sectors); +} + +static int +ob_ide_read_sectors(struct ide_drive *drive, unsigned long long block, + unsigned char *buf, unsigned int sectors) +{ + if (!sectors) + return 1; + if (block + sectors > drive->sectors) + return 1; + + IDE_DPRINTF("ob_ide_read_sectors: block=%lu sectors=%u\n", + (unsigned long) block, sectors); + + if (drive->type == ide_type_ata) + return ob_ide_read_ata(drive, block, buf, sectors); + else + return ob_ide_read_atapi(drive, block, buf, sectors); +} + +/* + * byte swap the string if necessay, and strip leading/trailing blanks + */ +static void +ob_ide_fixup_string(unsigned char *s, unsigned int len) +{ + unsigned char *p = s, *end = &s[len & ~1]; + + /* + * if big endian arch, byte swap the string + */ +#ifdef CONFIG_BIG_ENDIAN + for (p = end ; p != s;) { + unsigned short *pp = (unsigned short *) (p -= 2); + *pp = __le16_to_cpu(*pp); + } +#endif + + while (s != end && *s == ' ') + ++s; + while (s != end && *s) + if (*s++ != ' ' || (s != end && *s && *s != ' ')) + *p++ = *(s-1); + while (p != end) + *p++ = '\0'; +} + +/* + * it's big endian, we need to swap (if on little endian) the items we use + */ +static int +ob_ide_fixup_id(struct hd_driveid *id) +{ + ob_ide_fixup_string(id->model, 40); + id->config = __le16_to_cpu(id->config); + id->lba_capacity = __le32_to_cpu(id->lba_capacity); + id->cyls = __le16_to_cpu(id->cyls); + id->heads = __le16_to_cpu(id->heads); + id->sectors = __le16_to_cpu(id->sectors); + id->command_set_2 = __le16_to_cpu(id->command_set_2); + id->cfs_enable_2 = __le16_to_cpu(id->cfs_enable_2); + + return 0; +} + +static int +ob_ide_identify_drive(struct ide_drive *drive) +{ + struct ata_command *cmd = &drive->channel->ata_cmd; + struct hd_driveid id; + + memset(cmd, 0, sizeof(*cmd)); + cmd->buffer = (unsigned char *) &id; + cmd->buflen = 512; + + if (drive->type == ide_type_ata) + cmd->command = WIN_IDENTIFY; + else if (drive->type == ide_type_atapi) + cmd->command = WIN_IDENTIFY_PACKET; + else { + IDE_DPRINTF("%s: called with bad device type %d\n", + __FUNCTION__, drive->type); + return 1; + } + + if (ob_ide_pio_data_in(drive, cmd)) + return 1; + + ob_ide_fixup_id(&id); + + if (drive->type == ide_type_atapi) { + drive->media = (id.config >> 8) & 0x1f; + drive->sectors = 0x7fffffff; + drive->bs = 2048; + drive->max_sectors = 31; + } else { + drive->media = ide_media_disk; + drive->sectors = id.lba_capacity; + drive->bs = 512; + drive->max_sectors = 255; + +#ifdef CONFIG_IDE_LBA48 + if ((id.command_set_2 & 0x0400) && (id.cfs_enable_2 & 0x0400)) { + drive->addressing = ide_lba48; + drive->max_sectors = 65535; + } else +#endif + if (id.capability & 2) + drive->addressing = ide_lba28; + else { + drive->addressing = ide_chs; + } + + /* only set these in chs mode? */ + drive->cyl = id.cyls; + drive->head = id.heads; + drive->sect = id.sectors; + } + + strncpy(drive->model, (char*)id.model, sizeof(id.model)); + drive->model[40] = '\0'; + return 0; +} + +/* + * identify type of devices on channel. must have already been probed. + */ +static void +ob_ide_identify_drives(struct ide_channel *chan) +{ + struct ide_drive *drive; + int i; + + for (i = 0; i < 2; i++) { + drive = &chan->drives[i]; + + if (!drive->present) + continue; + + ob_ide_identify_drive(drive); + } +} + +/* + * software reset (ATA-4, section 8.3) + */ +static void +ob_ide_software_reset(struct ide_drive *drive) +{ + struct ide_channel *chan = drive->channel; + + ob_ide_pio_writeb(drive, IDEREG_CONTROL, IDECON_NIEN | IDECON_SRST); + ob_ide_400ns_delay(drive); + ob_ide_pio_writeb(drive, IDEREG_CONTROL, IDECON_NIEN); + ob_ide_400ns_delay(drive); + + /* + * if master is present, wait for BUSY clear + */ + if (chan->drives[0].present) + ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL); + + /* + * if slave is present, wait until it allows register access + */ + if (chan->drives[1].present) { + unsigned char sectorn, sectorc; + int timeout = 1000; + + do { + /* + * select it + */ + ob_ide_pio_writeb(drive, IDEREG_CURRENT, IDEHEAD_DEV1); + ob_ide_400ns_delay(drive); + + sectorn = ob_ide_pio_readb(drive, IDEREG_SECTOR); + sectorc = ob_ide_pio_readb(drive, IDEREG_NSECTOR); + + if (sectorc == 0x01 && sectorn == 0x01) + break; + + } while (--timeout); + } + + /* + * reset done, reselect original device + */ + drive->channel->selected = -1; + ob_ide_select_drive(drive); +} + +/* + * this serves as both a device check, and also to verify that the drives + * we initially "found" are really there + */ +static void +ob_ide_device_type_check(struct ide_drive *drive) +{ + unsigned char sc, sn, cl, ch, st; + + if (ob_ide_select_drive(drive)) + return; + + sc = ob_ide_pio_readb(drive, IDEREG_NSECTOR); + sn = ob_ide_pio_readb(drive, IDEREG_SECTOR); + + if (sc == 0x01 && sn == 0x01) { + /* + * read device signature + */ + cl = ob_ide_pio_readb(drive, IDEREG_LCYL); + ch = ob_ide_pio_readb(drive, IDEREG_HCYL); + st = ob_ide_pio_readb(drive, IDEREG_STATUS); + if (cl == 0x14 && ch == 0xeb) + drive->type = ide_type_atapi; + else if (cl == 0x00 && ch == 0x00 && st != 0x00) + drive->type = ide_type_ata; + else + drive->present = 0; + } else + drive->present = 0; +} + +/* + * pure magic + */ +static void +ob_ide_device_check(struct ide_drive *drive) +{ + unsigned char sc, sn; + + /* + * non-existing io port should return 0xff, don't probe this + * channel at all then + */ + if (ob_ide_pio_readb(drive, IDEREG_STATUS) == 0xff) { + drive->channel->present = 0; + return; + } + + if (ob_ide_select_drive(drive)) + return; + + ob_ide_pio_writeb(drive, IDEREG_NSECTOR, 0x55); + ob_ide_pio_writeb(drive, IDEREG_SECTOR, 0xaa); + ob_ide_pio_writeb(drive, IDEREG_NSECTOR, 0xaa); + ob_ide_pio_writeb(drive, IDEREG_SECTOR, 0x55); + ob_ide_pio_writeb(drive, IDEREG_NSECTOR, 0x55); + ob_ide_pio_writeb(drive, IDEREG_SECTOR, 0xaa); + + sc = ob_ide_pio_readb(drive, IDEREG_NSECTOR); + sn = ob_ide_pio_readb(drive, IDEREG_SECTOR); + + /* + * we _think_ the device is there, we will make sure later + */ + if (sc == 0x55 && sn == 0xaa) { + drive->present = 1; + drive->type = ide_type_unknown; + } +} + +/* + * probe the legacy ide ports and find attached devices. + */ +static void +ob_ide_probe(struct ide_channel *chan) +{ + struct ide_drive *drive; + int i; + + for (i = 0; i < 2; i++) { + drive = &chan->drives[i]; + + ob_ide_device_check(drive); + + /* + * no point in continuing + */ + if (!chan->present) + break; + + if (!drive->present) + continue; + + /* + * select and reset device + */ + if (ob_ide_select_drive(drive)) + continue; + + ob_ide_software_reset(drive); + + ob_ide_device_type_check(drive); + } +} + +/* + * The following functions are interfacing with OpenBIOS. They + * are device node methods. Thus they have to do proper stack handling. + * + */ + +/* + * 255 sectors for ata lba28, 65535 for lba48, and 31 sectors for atapi + */ +static void +ob_ide_max_transfer(int *idx) +{ + struct ide_drive *drive = *(struct ide_drive **)idx; + + IDE_DPRINTF("max_transfer %x\n", drive->max_sectors * drive->bs); + + PUSH(drive->max_sectors * drive->bs); +} + +static void +ob_ide_read_blocks(int *idx) +{ + cell n = POP(), cnt=n; + ucell blk = POP(); + unsigned char *dest = (unsigned char *)cell2pointer(POP()); + struct ide_drive *drive = *(struct ide_drive **)idx; + + IDE_DPRINTF("ob_ide_read_blocks %lx block=%ld n=%ld\n", + (unsigned long)dest, (unsigned long)blk, (long)n); + + while (n) { + int len = n; + if (len > drive->max_sectors) + len = drive->max_sectors; + + if (ob_ide_read_sectors(drive, blk, dest, len)) { + IDE_DPRINTF("ob_ide_read_blocks: error\n"); + RET(0); + } + + dest += len * drive->bs; + n -= len; + blk += len; + } + + PUSH(cnt); +} + +static void +ob_ide_block_size(int *idx) +{ + struct ide_drive *drive = *(struct ide_drive **)idx; + + IDE_DPRINTF("ob_ide_block_size: block size %x\n", drive->bs); + + PUSH(drive->bs); +} + +static void +ob_ide_initialize(int *idx) +{ + int props[3]; + phandle_t ph=get_cur_dev(); + + push_str("block"); + fword("device-type"); + + // Set dummy reg properties + + set_int_property(ph, "#address-cells", 1); + set_int_property(ph, "#size-cells", 0); + + props[0] = __cpu_to_be32(0); props[1] = __cpu_to_be32(0); props[2] = __cpu_to_be32(0); + set_property(ph, "reg", (char *)&props, 3*sizeof(int)); + + fword("is-deblocker"); +} + +static void +ob_ide_open(int *idx) +{ + int ret=1, len; + phandle_t ph; + struct ide_drive *drive; + struct ide_channel *chan; + char *idename; + int unit; + + fword("my-unit"); + unit = POP(); + + fword("my-parent"); + fword("ihandle>phandle"); + ph=(phandle_t)POP(); + idename=get_property(ph, "name", &len); + + chan = ide_seek_channel(idename); + drive = &chan->drives[unit]; + *(struct ide_drive **)idx = drive; + + IDE_DPRINTF("opening channel %d unit %d\n", idx[1], idx[0]); + dump_drive(drive); + + if (drive->type != ide_type_ata) + ret= !ob_ide_atapi_drive_ready(drive); + + selfword("open-deblocker"); + + /* interpose disk-label */ + ph = find_dev("/packages/disk-label"); + fword("my-args"); + PUSH_ph( ph ); + fword("interpose"); + + RET ( -ret ); +} + +static void +ob_ide_close(struct ide_drive *drive) +{ + selfword("close-deblocker"); +} + +NODE_METHODS(ob_ide) = { + { NULL, ob_ide_initialize }, + { "open", ob_ide_open }, + { "close", ob_ide_close }, + { "read-blocks", ob_ide_read_blocks }, + { "block-size", ob_ide_block_size }, + { "max-transfer", ob_ide_max_transfer }, +}; + +static void +ob_ide_ctrl_initialize(int *idx) +{ + phandle_t ph=get_cur_dev(); + + /* set device type */ + push_str(DEV_TYPE); + fword("device-type"); + + set_int_property(ph, "#address-cells", 1); + set_int_property(ph, "#size-cells", 0); +} + +static void +ob_ide_ctrl_decodeunit(int *idx) +{ + fword("parse-hex"); +} + +NODE_METHODS(ob_ide_ctrl) = { + { NULL, ob_ide_ctrl_initialize }, + { "decode-unit", ob_ide_ctrl_decodeunit }, +}; + +static void set_cd_alias(const char *path) +{ + phandle_t aliases; + + aliases = find_dev("/aliases"); + + if (get_property(aliases, "cd", NULL)) + return; + + set_property(aliases, "cd", path, strlen(path) + 1); + set_property(aliases, "cdrom", path, strlen(path) + 1); +} + +static void set_hd_alias(const char *path) +{ + phandle_t aliases; + + aliases = find_dev("/aliases"); + + if (get_property(aliases, "hd", NULL)) + return; + + set_property(aliases, "hd", path, strlen(path) + 1); + set_property(aliases, "disk", path, strlen(path) + 1); +} + +static void set_ide_alias(const char *path) +{ + phandle_t aliases; + static int ide_counter = 0; + char idestr[8]; + + aliases = find_dev("/aliases"); + + snprintf(idestr, sizeof(idestr), "ide%d", ide_counter++); + set_property(aliases, idestr, path, strlen(path) + 1); +} + +int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0, + uint32_t io_port1, uint32_t ctl_port1) +{ + int i, j; + char nodebuff[128]; + phandle_t dnode; + struct ide_channel *chan; + int io_ports[IDE_MAX_CHANNELS]; + int ctl_ports[IDE_MAX_CHANNELS]; + u32 props[6]; + + io_ports[0] = io_port0; + ctl_ports[0] = ctl_port0; + io_ports[1] = io_port1; + ctl_ports[1] = ctl_port1; + + for (i = 0; i < IDE_NUM_CHANNELS; i++, current_channel++) { + + chan = malloc(sizeof(struct ide_channel)); + + snprintf(chan->name, sizeof(chan->name), + DEV_NAME, current_channel); + + chan->mmio = 0; + + for (j = 0; j < 8; j++) + chan->io_regs[j] = io_ports[i] + j; + + chan->io_regs[8] = ctl_ports[i]; + chan->io_regs[9] = ctl_ports[i] + 1; + + chan->obide_inb = ob_ide_inb; + chan->obide_insw = ob_ide_insw; + chan->obide_outb = ob_ide_outb; + chan->obide_outsw = ob_ide_outsw; + + chan->selected = -1; + + /* + * assume it's there, if not io port dead check will clear + */ + chan->present = 1; + + for (j = 0; j < 2; j++) { + chan->drives[j].present = 0; + chan->drives[j].unit = j; + chan->drives[j].channel = chan; + /* init with a decent value */ + chan->drives[j].bs = 512; + + chan->drives[j].nr = i * 2 + j; + } + + ide_add_channel(chan); + + ob_ide_probe(chan); + + if (!chan->present) + continue; + + ob_ide_identify_drives(chan); + + snprintf(nodebuff, sizeof(nodebuff), "%s/" DEV_NAME, path, + current_channel); + REGISTER_NAMED_NODE(ob_ide_ctrl, nodebuff); + + dnode = find_dev(nodebuff); + +#if !defined(CONFIG_PPC) && !defined(CONFIG_SPARC64) + props[0]=14; props[1]=0; + set_property(dnode, "interrupts", + (char *)&props, 2*sizeof(props[0])); +#endif + + props[0] = __cpu_to_be32(chan->io_regs[0]); + props[1] = __cpu_to_be32(1); props[2] = __cpu_to_be32(8); + props[3] = __cpu_to_be32(chan->io_regs[8]); + props[4] = __cpu_to_be32(1); props[5] = __cpu_to_be32(2); + set_property(dnode, "reg", (char *)&props, 6*sizeof(props[0])); + + IDE_DPRINTF(DEV_NAME": [io ports 0x%x-0x%x,0x%x]\n", + current_channel, chan->io_regs[0], + chan->io_regs[0] + 7, chan->io_regs[8]); + + for (j = 0; j < 2; j++) { + struct ide_drive *drive = &chan->drives[j]; + const char *media = "UNKNOWN"; + + if (!drive->present) + continue; + + IDE_DPRINTF(" drive%d [ATA%s ", j, + drive->type == ide_type_atapi ? "PI" : ""); + switch (drive->media) { + case ide_media_floppy: + media = "floppy"; + break; + case ide_media_cdrom: + media = "cdrom"; + break; + case ide_media_optical: + media = "mo"; + break; + case ide_media_disk: + media = "disk"; + break; + } + IDE_DPRINTF("%s]: %s\n", media, drive->model); + snprintf(nodebuff, sizeof(nodebuff), + "%s/" DEV_NAME "/%s", path, current_channel, + media); + REGISTER_NAMED_NODE(ob_ide, nodebuff); + dnode=find_dev(nodebuff); + set_int_property(dnode, "reg", j); + + /* create aliases */ + + set_ide_alias(nodebuff); + if (drive->media == ide_media_cdrom) + set_cd_alias(nodebuff); + if (drive->media == ide_media_disk) + set_hd_alias(nodebuff); + } + } + + return 0; +} + +#if defined(CONFIG_DRIVER_MACIO) +static unsigned char +macio_ide_inb(struct ide_channel *chan, unsigned int port) +{ + return in_8((unsigned char*)(chan->mmio + (port << 4))); +} + +static void +macio_ide_outb(struct ide_channel *chan, unsigned char data, unsigned int port) +{ + out_8((unsigned char*)(chan->mmio + (port << 4)), data); +} + +static void +macio_ide_insw(struct ide_channel *chan, + unsigned int port, unsigned char *addr, unsigned int count) +{ + _insw((uint16_t*)(chan->mmio + (port << 4)), addr, count); +} + +static void +macio_ide_outsw(struct ide_channel *chan, + unsigned int port, unsigned char *addr, unsigned int count) +{ + _outsw((uint16_t*)(chan->mmio + (port << 4)), addr, count); +} + +#define MACIO_IDE_OFFSET 0x00020000 +#define MACIO_IDE_SIZE 0x00001000 + +int macio_ide_init(const char *path, uint32_t addr, int nb_channels) +{ + int i, j; + char nodebuff[128]; + phandle_t dnode; + u32 props[8]; + struct ide_channel *chan; + + /* IDE ports on Macs are numbered from 3. + * Also see comments in macio.c:openpic_init() */ + current_channel = 3; + + for (i = 0; i < nb_channels; i++, current_channel++) { + + chan = malloc(sizeof(struct ide_channel)); + + snprintf(chan->name, sizeof(chan->name), + DEV_NAME, current_channel); + + chan->mmio = addr + MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE; + + chan->obide_inb = macio_ide_inb; + chan->obide_insw = macio_ide_insw; + chan->obide_outb = macio_ide_outb; + chan->obide_outsw = macio_ide_outsw; + + chan->selected = -1; + + /* + * assume it's there, if not io port dead check will clear + */ + chan->present = 1; + + for (j = 0; j < 2; j++) { + chan->drives[j].present = 0; + chan->drives[j].unit = j; + chan->drives[j].channel = chan; + /* init with a decent value */ + chan->drives[j].bs = 512; + + chan->drives[j].nr = i * 2 + j; + } + + ob_ide_probe(chan); + + if (!chan->present) { + free(chan); + continue; + } + + ide_add_channel(chan); + + ob_ide_identify_drives(chan); + + snprintf(nodebuff, sizeof(nodebuff), "%s/" DEV_NAME, path, + current_channel); + REGISTER_NAMED_NODE(ob_ide_ctrl, nodebuff); + + dnode = find_dev(nodebuff); + + set_property(dnode, "compatible", (is_oldworld() ? + "heathrow-ata" : "keylargo-ata"), 13); + + props[0] = 0x00000526; + props[1] = 0x00000085; + props[2] = 0x00000025; + props[3] = 0x00000025; + props[4] = 0x00000025; + props[5] = 0x00000000; + props[6] = 0x00000000; + props[7] = 0x00000000; + OLDWORLD(set_property(dnode, "AAPL,pio-timing", + (char *)&props, 8*sizeof(props[0]))); + + /* The first interrupt entry is the ide interrupt, the second + the dbdma interrupt */ + switch (i) { + case 0: + props[0] = 0x0000000d; + props[2] = 0x00000002; + break; + case 1: + props[0] = 0x0000000e; + props[2] = 0x00000003; + break; + case 2: + props[0] = 0x0000000f; + props[2] = 0x00000004; + break; + default: + props[0] = 0x00000000; + props[2] = 0x00000000; + break; + } + props[1] = 0x00000000; /* XXX level triggered on real hw */ + props[3] = 0x00000000; + NEWWORLD(set_property(dnode, "interrupts", + (char *)&props, 4*sizeof(props[0]))); + NEWWORLD(set_int_property(dnode, "#interrupt-cells", 2)); + + props[1] = props[2]; + OLDWORLD(set_property(dnode, "AAPL,interrupts", + (char *)&props, 2*sizeof(props[0]))); + + props[0] = MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE; + props[1] = MACIO_IDE_SIZE; + props[2] = 0x00008b00 + i * 0x0200; + props[3] = 0x0200; + set_property(dnode, "reg", (char *)&props, 4*sizeof(props[0])); + + props[0] = addr + MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE; + props[1] = addr + 0x00008b00 + i * 0x0200; + OLDWORLD(set_property(dnode, "AAPL,address", + (char *)&props, 2*sizeof(props[0]))); + + props[0] = 0; + OLDWORLD(set_property(dnode, "AAPL,bus-id", (char*)props, + 1 * sizeof(props[0]))); + IDE_DPRINTF(DEV_NAME": [io ports 0x%lx]\n", + current_channel, chan->mmio); + + for (j = 0; j < 2; j++) { + struct ide_drive *drive = &chan->drives[j]; + const char *media = "UNKNOWN"; + + if (!drive->present) + continue; + + IDE_DPRINTF(" drive%d [ATA%s ", j, + drive->type == ide_type_atapi ? "PI" : ""); + switch (drive->media) { + case ide_media_floppy: + media = "floppy"; + break; + case ide_media_cdrom: + media = "cdrom"; + break; + case ide_media_optical: + media = "mo"; + break; + case ide_media_disk: + media = "disk"; + break; + } + IDE_DPRINTF("%s]: %s\n", media, drive->model); + snprintf(nodebuff, sizeof(nodebuff), + "%s/" DEV_NAME "/%s", path, current_channel, + media); + REGISTER_NAMED_NODE(ob_ide, nodebuff); + dnode = find_dev(nodebuff); + set_int_property(dnode, "reg", j); + + /* create aliases */ + + set_ide_alias(nodebuff); + if (drive->media == ide_media_cdrom) + set_cd_alias(nodebuff); + if (drive->media == ide_media_disk) + set_hd_alias(nodebuff); + } + } + + return 0; +} +#endif /* CONFIG_DRIVER_MACIO */ diff --git a/qemu/roms/openbios/drivers/ide.h b/qemu/roms/openbios/drivers/ide.h new file mode 100644 index 000000000..d6c4b9f5d --- /dev/null +++ b/qemu/roms/openbios/drivers/ide.h @@ -0,0 +1,213 @@ +#ifndef IDE_H +#define IDE_H + +#include "hdreg.h" + +/* + * legacy ide ports + */ +#define IDEREG_DATA 0x00 +#define IDEREG_ERROR 0x01 +#define IDEREG_FEATURE IDEREG_ERROR +#define IDEREG_NSECTOR 0x02 +#define IDEREG_SECTOR 0x03 +#define IDEREG_LCYL 0x04 +#define IDEREG_HCYL 0x05 +#define IDEREG_CURRENT 0x06 +#define IDEREG_STATUS 0x07 +#define IDEREG_COMMAND IDEREG_STATUS +#define IDEREG_CONTROL 0x08 +#define IDEREG_ASTATUS IDEREG_CONTROL + +/* + * device control bits + */ +#define IDECON_NIEN 0x02 +#define IDECON_SRST 0x04 + +/* + * device head bits + */ +#define IDEHEAD_LBA 0x40 +#define IDEHEAD_DEV0 0x00 +#define IDEHEAD_DEV1 0x10 + +/* + * status bytes + */ +#define ERR_STAT 0x01 +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 + +#define IREASON_CD 0x01 +#define IREASON_IO 0x02 + +/* + * ATA opcodes + */ +#define WIN_READ 0x20 +#define WIN_READ_EXT 0x24 +#define WIN_IDENTIFY 0xEC +#define WIN_PACKET 0xA0 +#define WIN_IDENTIFY_PACKET 0xA1 + +/* + * ATAPI opcodes + */ +#define ATAPI_TUR 0x00 +#define ATAPI_READ_10 0x28 +#define ATAPI_REQ_SENSE 0x03 +#define ATAPI_START_STOP_UNIT 0x1b +#define ATAPI_READ_CAPACITY 0x25 + +/* + * atapi sense keys + */ +#define ATAPI_SENSE_NOT_READY 0x02 + +/* + * supported device types + */ +enum { + ide_type_unknown, + ide_type_ata, + ide_type_atapi, +}; + +enum { + ide_media_floppy = 0x00, + ide_media_cdrom = 0x05, + ide_media_optical = 0x07, + ide_media_disk = 0x20, +}; + +/* + * drive addressing + */ +enum { + ide_chs = 1, + ide_lba28, + ide_lba48, +}; + +/* + * simple ata command that works for everything (except 48-bit lba commands) + */ +struct ata_command { + unsigned char *buffer; + unsigned int buflen; + + /* + * data register + */ + unsigned char data; + unsigned char feature; + unsigned char nsector; + unsigned char sector; + unsigned char lcyl; + unsigned char hcyl; + unsigned char device_head; + unsigned char command; + unsigned char control; + + /* + * or tasklet, just for lba48 for now (above could be scrapped) + */ + unsigned char task[10]; + + /* + * output + */ + unsigned char stat; + unsigned int bytes; +}; + +struct atapi_command { + unsigned char cdb[12]; + unsigned char *buffer; + unsigned int buflen; + unsigned char data_direction; + + unsigned char stat; + unsigned char sense_valid; + struct request_sense sense; + unsigned char old_cdb; +}; + +struct ide_channel; + +struct ide_drive { + char unit; /* 0: master, 1: slave */ + char present; /* there or not */ + char type; /* ata or atapi */ + char media; /* disk, cdrom, etc */ + char addressing; /* chs/lba28/lba48 */ + + char model[41]; /* name */ + int nr; + + unsigned long sectors; + + unsigned int max_sectors; + + /* + * for legacy chs crap + */ + unsigned int cyl; + unsigned int head; + unsigned int sect; + + unsigned int bs; /* block size */ + + struct ide_channel *channel; +}; + +struct ide_channel { + + char name[32]; + struct ide_channel *next; + + /* + * either mmio or io_regs is set to indicate mmio or not + */ + unsigned long mmio; + int io_regs[10]; + + /* + * can be set to a mmio hook, default it legacy outb/inb + */ + void (*obide_outb)(struct ide_channel *chan, + unsigned char addr, unsigned int port); + unsigned char (*obide_inb)(struct ide_channel *chan, + unsigned int port); + void (*obide_insw)(struct ide_channel *chan, + unsigned int port, unsigned char *addr, + unsigned int count); + void (*obide_outsw)(struct ide_channel *chan, + unsigned int port, unsigned char *addr, + unsigned int count); + + struct ide_drive drives[2]; + char selected; + char present; + + /* + * only one can be busy per channel + */ + struct ata_command ata_cmd; + struct atapi_command atapi_cmd; + +}; + +enum { + atapi_ddir_none, + atapi_ddir_read, + atapi_ddir_write, +}; + +static int ob_ide_atapi_request_sense(struct ide_drive *drive); + +#endif diff --git a/qemu/roms/openbios/drivers/iommu.c b/qemu/roms/openbios/drivers/iommu.c new file mode 100644 index 000000000..cd9a64bb6 --- /dev/null +++ b/qemu/roms/openbios/drivers/iommu.c @@ -0,0 +1,207 @@ +/** + ** Proll (PROM replacement) + ** iommu.c: Functions for DVMA management. + ** Copyright 1999 Pete Zaitcev + ** This code is licensed under GNU General Public License. + **/ +#include "config.h" +#include "libopenbios/bindings.h" +#include "libopenbios/ofmem.h" +#include "drivers/drivers.h" +#include "iommu.h" +#include "arch/sparc32/ofmem_sparc32.h" + +#ifdef CONFIG_DEBUG_IOMMU +#define DPRINTF(fmt, args...) \ + do { printk(fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +/* + * IOMMU parameters + */ +struct iommu { + struct iommu_regs *regs; + unsigned int *page_table; + unsigned long plow; /* Base bus address */ +}; + +static struct iommu ciommu; + +static void +iommu_invalidate(struct iommu_regs *iregs) +{ + iregs->tlbflush = 0; +} + +/* + * XXX This is a problematic interface. We alloc _memory_ which is uncached. + * So if we ever reuse allocations somebody is going to get uncached pages. + * Returned address is always aligned by page. + * BTW, we were not going to give away anonymous storage, were we not? + */ +void * +dvma_alloc(int size, unsigned int *pphys) +{ + void *va; + unsigned int pa, ba; + unsigned int npages; + unsigned int mva, mpa; + unsigned int i; + unsigned int *iopte; + struct iommu *t = &ciommu; + int ret; + + npages = (size + (PAGE_SIZE-1)) / PAGE_SIZE; + ret = ofmem_posix_memalign(&va, npages * PAGE_SIZE, PAGE_SIZE); + if (ret != 0) + return NULL; + + ba = (unsigned int)mem_alloc(&cdvmem, npages * PAGE_SIZE, PAGE_SIZE); + if (ba == 0) + return NULL; + + pa = (unsigned int)va2pa((unsigned long)va); + + /* + * Change page attributes in MMU to uncached. + */ + mva = (unsigned int) va; + mpa = (unsigned int) pa; + ofmem_arch_map_pages(mpa, mva, npages * PAGE_SIZE, ofmem_arch_io_translation_mode(mpa)); + + /* + * Map into IOMMU page table. + */ + mpa = (unsigned int) pa; + iopte = &t->page_table[(ba - t->plow) / PAGE_SIZE]; + for (i = 0; i < npages; i++) { + *iopte++ = MKIOPTE(mpa); + mpa += PAGE_SIZE; + } + + *pphys = ba; + + return va; +} + +/* + * Initialize IOMMU + * This looks like initialization of CPU MMU but + * the routine is higher in food chain. + */ +static struct iommu_regs * +iommu_init(struct iommu *t, uint64_t base) +{ + unsigned int *ptab; + int ptsize; +#ifdef CONFIG_DEBUG_IOMMU + unsigned int impl, vers; +#endif + unsigned int tmp; + struct iommu_regs *regs; + int ret; + unsigned long vasize; + + regs = (struct iommu_regs *)ofmem_map_io(base, IOMMU_REGS); + if (regs == NULL) { + DPRINTF("Cannot map IOMMU\n"); + for (;;) { } + } + t->regs = regs; +#ifdef CONFIG_DEBUG_IOMMU + impl = (regs->control & IOMMU_CTRL_IMPL) >> 28; + vers = (regs->control & IOMMU_CTRL_VERS) >> 24; +#endif + + tmp = regs->control; + tmp &= ~(IOMMU_CTRL_RNGE); + + tmp |= (IOMMU_RNGE_32MB | IOMMU_CTRL_ENAB); + t->plow = 0xfe000000; /* End - 32 MB */ + /* Size of VA region that we manage */ + vasize = 0x2000000; /* 32 MB */ + + regs->control = tmp; + iommu_invalidate(regs); + + /* Allocate IOMMU page table */ + /* Tremendous alignment causes great waste... */ + ptsize = (vasize / PAGE_SIZE) * sizeof(int); + ret = ofmem_posix_memalign((void *)&ptab, ptsize, ptsize); + if (ret != 0) { + DPRINTF("Cannot allocate IOMMU table [0x%x]\n", ptsize); + for (;;) { } + } + t->page_table = ptab; + + /* flush_cache_all(); */ + /** flush_tlb_all(); **/ + tmp = (unsigned int)va2pa((unsigned long)ptab); + regs->base = tmp >> 4; + iommu_invalidate(regs); + + DPRINTF("IOMMU: impl %d vers %d page table at 0x%p (pa 0x%x) of size %d bytes\n", + impl, vers, t->page_table, tmp, ptsize); + + mem_init(&cdvmem, (char*)t->plow, (char *)0xfffff000); + return regs; +} + +/* ( addr.lo addr.hi size -- virt ) */ + +static void +ob_iommu_map_in(void) +{ + phys_addr_t phys; + ucell size, virt; + + size = POP(); + phys = POP(); + phys = (phys << 32) + POP(); + + virt = ofmem_map_io(phys, size); + + PUSH(virt); +} + +/* ( virt size ) */ + +static void +ob_iommu_map_out(void) +{ + ucell size = POP(); + ucell virt = POP(); + + ofmem_release_io(virt, size); +} + +void +ob_init_iommu(uint64_t base) +{ + struct iommu_regs *regs; + + regs = iommu_init(&ciommu, base); + + push_str("/iommu"); + fword("find-device"); + PUSH((unsigned long)regs); + fword("encode-int"); + push_str("address"); + fword("property"); + + PUSH(base >> 32); + fword("encode-int"); + PUSH(base & 0xffffffff); + fword("encode-int"); + fword("encode+"); + PUSH(IOMMU_REGS); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + bind_func("map-in", ob_iommu_map_in); + bind_func("map-out", ob_iommu_map_out); +} diff --git a/qemu/roms/openbios/drivers/iommu.h b/qemu/roms/openbios/drivers/iommu.h new file mode 100644 index 000000000..59e3387ca --- /dev/null +++ b/qemu/roms/openbios/drivers/iommu.h @@ -0,0 +1,102 @@ +/* iommu.h: Definitions for the sun4m IOMMU. + * + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + * Adapted for Proll by Pete Zaitcev in 1999 (== made worse than original). + */ + +/* #include <asm/page.h> */ + +/* The iommu handles all virtual to physical address translations + * that occur between the SBUS and physical memory. Access by + * the cpu to IO registers and similar go over the mbus so are + * translated by the on chip SRMMU. The iommu and the srmmu do + * not need to have the same translations at all, in fact most + * of the time the translations they handle are a disjunct set. + * Basically the iommu handles all dvma sbus activity. + */ + +/* The IOMMU registers occupy three pages in IO space. */ +struct iommu_regs { + /* First page */ + volatile unsigned long control; /* IOMMU control */ + volatile unsigned long base; /* Physical base of iopte page table */ + volatile unsigned long _unused1[3]; + volatile unsigned long tlbflush; /* write only */ + volatile unsigned long pageflush; /* write only */ + volatile unsigned long _unused2[1017]; + /* Second page */ + volatile unsigned long afsr; /* Async-fault status register */ + volatile unsigned long afar; /* Async-fault physical address */ + volatile unsigned long _unused3[2]; + volatile unsigned long sbuscfg0; /* SBUS configuration registers, per-slot */ + volatile unsigned long sbuscfg1; + volatile unsigned long sbuscfg2; + volatile unsigned long sbuscfg3; + volatile unsigned long mfsr; /* Memory-fault status register */ + volatile unsigned long mfar; /* Memory-fault physical address */ + volatile unsigned long _unused4[1014]; + /* Third page */ + volatile unsigned long mid; /* IOMMU module-id */ +}; + +#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */ +#define IOMMU_CTRL_VERS 0x0f000000 /* Version */ +#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */ +#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */ +#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */ +#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */ +#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */ +#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */ +#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */ +#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */ +#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */ +#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */ + +#define IOMMU_AFSR_ERR 0x80000000 /* LE, TO, or BE asserted */ +#define IOMMU_AFSR_LE 0x40000000 /* SBUS reports error after transaction */ +#define IOMMU_AFSR_TO 0x20000000 /* Write access took more than 12.8 us. */ +#define IOMMU_AFSR_BE 0x10000000 /* Write access received error acknowledge */ +#define IOMMU_AFSR_SIZE 0x0e000000 /* Size of transaction causing error */ +#define IOMMU_AFSR_S 0x01000000 /* Sparc was in supervisor mode */ +#define IOMMU_AFSR_RESV 0x00f00000 /* Reserver, forced to 0x8 by hardware */ +#define IOMMU_AFSR_ME 0x00080000 /* Multiple errors occurred */ +#define IOMMU_AFSR_RD 0x00040000 /* A read operation was in progress */ +#define IOMMU_AFSR_FAV 0x00020000 /* IOMMU afar has valid contents */ + +#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when bypass enabled */ +#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */ +#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */ +#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses + produced by this device as pure + physical. */ + +#define IOMMU_MFSR_ERR 0x80000000 /* One or more of PERR1 or PERR0 */ +#define IOMMU_MFSR_S 0x01000000 /* Sparc was in supervisor mode */ +#define IOMMU_MFSR_CPU 0x00800000 /* CPU transaction caused parity error */ +#define IOMMU_MFSR_ME 0x00080000 /* Multiple parity errors occurred */ +#define IOMMU_MFSR_PERR 0x00006000 /* high bit indicates parity error occurred + on the even word of the access, low bit + indicated odd word caused the parity error */ +#define IOMMU_MFSR_BM 0x00001000 /* Error occurred while in boot mode */ +#define IOMMU_MFSR_C 0x00000800 /* Address causing error was marked cacheable */ +#define IOMMU_MFSR_RTYP 0x000000f0 /* Memory request transaction type */ + +#define IOMMU_MID_SBAE 0x001f0000 /* SBus arbitration enable */ +#define IOMMU_MID_SE 0x00100000 /* Enables SCSI/ETHERNET arbitration */ +#define IOMMU_MID_SB3 0x00080000 /* Enable SBUS device 3 arbitration */ +#define IOMMU_MID_SB2 0x00040000 /* Enable SBUS device 2 arbitration */ +#define IOMMU_MID_SB1 0x00020000 /* Enable SBUS device 1 arbitration */ +#define IOMMU_MID_SB0 0x00010000 /* Enable SBUS device 0 arbitration */ +#define IOMMU_MID_MID 0x0000000f /* Module-id, hardcoded to 0x8 */ + +/* The format of an iopte in the page tables */ +#define IOPTE_PAGE 0x07ffff00 /* Physical page number (PA[30:12]) */ +#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or Viking/MXCC) */ +#define IOPTE_WRITE 0x00000004 /* Writeable */ +#define IOPTE_VALID 0x00000002 /* IOPTE is valid */ +#define IOPTE_WAZ 0x00000001 /* Write as zeros */ + +#define IOPERM (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID) +#define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ) + +#define IOMMU_REGS 0x300 diff --git a/qemu/roms/openbios/drivers/kbd.c b/qemu/roms/openbios/drivers/kbd.c new file mode 100644 index 000000000..43070d877 --- /dev/null +++ b/qemu/roms/openbios/drivers/kbd.c @@ -0,0 +1,116 @@ +/* + * <kbd.c> + * + * Open Hack'Ware BIOS generic keyboard input translation. + * + * Copyright (c) 2005 Jocelyn Mayer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include "config.h" +#include "libc/string.h" +#include "asm/types.h" +#include "kbd.h" + +//#define DEBUG_KBD +#ifdef DEBUG_KBD +#define KBD_DPRINTF(fmt, args...) \ +do { printk("KBD - %s: " fmt, __func__ , ##args); } while (0) +#else +#define KBD_DPRINTF(fmt, args...) do { } while (0) +#endif + +int kbd_set_keymap (kbd_t *kbd, int nb_keys, const keymap_t *keymap, const char **sequences) +{ + kbd->nb_keys = nb_keys; + kbd->keymap = keymap; + kbd->sequences = sequences; + + return 0; +} + +int kbd_translate_key (kbd_t *kbd, int keycode, int up_down, char *sequence) +{ + const keymap_t *keyt; + int mod_state, key, type; + int ret; + + ret = -1; + /* Get key table */ + if (keycode < kbd->nb_keys) { + keyt = &kbd->keymap[keycode]; + /* Get modifier state */ + mod_state = (kbd->mod_state | (kbd->mod_state >> 8)) & 0xFF; + /* Adjust with lock */ + if (keyt->lck_shift >= 0) { + if ((kbd->mod_state >> (16 + keyt->lck_shift)) & 0x01) { + KBD_DPRINTF("adjust with lock %02x => %02x (%d %08x)\n", + mod_state, + mod_state ^ ((kbd->mod_state >> + (16 + keyt->lck_shift)) & + 0x01), + keyt->lck_shift, kbd->mod_state); + } + mod_state ^= (kbd->mod_state >> (16 + keyt->lck_shift)) & 0x01; + } + key = keyt->trans[mod_state]; + type = key & 0xFF000000; + key &= ~0xFF000000; + switch (type) { + case KBD_TYPE_REGULAR: + if (!up_down) { + /* We don't care about up events on "normal" keys */ + *sequence = key; + ret = 1; + } + break; + case KBD_TYPE_SEQUENCE: + if (!up_down) { + /* We don't care about up events on "normal" keys */ + ret = strlen(kbd->sequences[key]); + memcpy(sequence, kbd->sequences[key], ret); + } + break; + case KBD_TYPE_LOCK: + if (!up_down) { + kbd->mod_state ^= key; + ret = -2; + KBD_DPRINTF("Change modifier type %d key %04x %s => %08x\n", + type, key, up_down ? "up" : "down", + kbd->mod_state); + } + break; + case KBD_TYPE_LMOD: + case KBD_TYPE_RMOD: + if (up_down) + kbd->mod_state &= ~key; + else + kbd->mod_state |= key; + KBD_DPRINTF("Change modifier type %d key %04x %s => %08x\n", + type, key, up_down ? "up" : "down", kbd->mod_state); + ret = -2; /* The caller may know the key was a modifier */ + break; + default: + KBD_DPRINTF("Unknown key: keycode=%02x mod_state=%02x (%08x)\n", + keycode, mod_state, kbd->mod_state); + break; + } + } else { + KBD_DPRINTF("Unmanaged key: keycode=%02x mod_state %08x\n", + keycode, kbd->mod_state); + } + + return ret; +} diff --git a/qemu/roms/openbios/drivers/kbd.h b/qemu/roms/openbios/drivers/kbd.h new file mode 100644 index 000000000..8a7d0d476 --- /dev/null +++ b/qemu/roms/openbios/drivers/kbd.h @@ -0,0 +1,108 @@ +/* + * <kbd.h> + * + * Open Hack'Ware BIOS generic keyboard management definitions. + * + * Copyright (c) 2005 Jocelyn Mayer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#if !defined (__OHW_KBD_H__) +#define __OHW_KBD_H__ +typedef struct kbd_t kbd_t; +typedef struct keymap_t keymap_t; +struct kbd_t { + uint32_t mod_state; + /* Modifier state + * 0x00 kk ll rr + * | | | | + * Not used for now -+ | | | + * Locks ---------------+ | | + * Left modifiers ---------+ | + * Right modifiers -----------+ + */ + int nb_keys; + const keymap_t *keymap; + const char **sequences; +}; + +/* Modifiers */ +typedef enum { + KBD_MOD_SHIFT = 0x01, + KBD_MOD_CTRL = 0x02, + KBD_MOD_ALT = 0x04, + KBD_MOD_CMD = 0x08, + KBD_MOD_OPT = 0x10, +} kbd_modifiers; + +/* Locks */ +typedef enum { + KBD_LCK_CAPS = 0x01, + KBD_LCK_NUM = 0x02, + KBD_LCK_SCROLL = 0x04, +} kbd_locks; + +/* Lock shifts */ +typedef enum { + KBD_SH_NONE = -1, + KBD_SH_CAPS = 0, + KBD_SH_NUML = 1, + KBD_SH_SCRL = 2, +} kbd_lck_shifts; + +enum { + KBD_TYPE_REGULAR = 0 << 24, + KBD_TYPE_LMOD = 1 << 24, + KBD_TYPE_RMOD = 2 << 24, + KBD_TYPE_LOCK = 3 << 24, + KBD_TYPE_SEQUENCE = 4 << 24, +}; + +#define KBD_SEQUENCE(sequence) (KBD_TYPE_SEQUENCE | (sequence)) + +#define KBD_MOD_MAP(mod) \ +KBD_SH_NONE, { (mod), (mod), (mod), (mod), (mod), (mod), (mod), (mod), \ + (mod), (mod), (mod), (mod), (mod), (mod), (mod), (mod), \ + (mod), (mod), (mod), (mod), (mod), (mod), (mod), (mod), \ + (mod), (mod), (mod), (mod), (mod), (mod), (mod), (mod), } +#define KBD_MOD_MAP_LSHIFT KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_SHIFT) +#define KBD_MOD_MAP_RSHIFT KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_SHIFT << 8)) +#define KBD_MOD_MAP_LCTRL KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_CTRL) +#define KBD_MOD_MAP_RCTRL KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_CTRL << 8)) +#define KBD_MOD_MAP_LALT KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_ALT) +#define KBD_MOD_MAP_RALT KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_ALT << 8)) +#define KBD_MOD_MAP_LCMD KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_CMD) +#define KBD_MOD_MAP_RCMD KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_CMD << 8)) +#define KBD_MOD_MAP_LOPT KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_OPT) +#define KBD_MOD_MAP_ROPT KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_OPT << 8)) +#define KBD_MOD_MAP_CAPS KBD_MOD_MAP(KBD_TYPE_LOCK | (KBD_LCK_CAPS << 16)) +#define KBD_MOD_MAP_NUML KBD_MOD_MAP(KBD_TYPE_LOCK | (KBD_LCK_NUML << 16)) +#define KBD_MOD_MAP_SCROLL KBD_MOD_MAP(KBD_TYPE_LOCK | (KBD_LCK_SCRL << 16)) +#define KBD_MAP_NONE KBD_MOD_MAP(-1) + +/* Keymap definition */ +struct keymap_t { + /* Set the lock which applies to this key (if any) */ + int lck_shift; + /* Key translations */ + uint32_t trans[32]; +}; + +void *kbd_new (int len); +int kbd_set_keymap (kbd_t *kbd, int nb_keys, const keymap_t *keymap, + const char **sequences); +int kbd_translate_key (kbd_t *kbd, int keycode, int up_down, char *sequence); + +#endif /* !defined (__OHW_KBD_H__) */ diff --git a/qemu/roms/openbios/drivers/macio.c b/qemu/roms/openbios/drivers/macio.c new file mode 100644 index 000000000..f54bc86dc --- /dev/null +++ b/qemu/roms/openbios/drivers/macio.c @@ -0,0 +1,281 @@ +/* + * derived from mol/mol.c, + * Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se) + * + * 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 "arch/common/nvram.h" +#include "packages/nvram.h" +#include "libopenbios/bindings.h" +#include "libc/byteorder.h" +#include "libc/vsprintf.h" + +#include "drivers/drivers.h" +#include "macio.h" +#include "cuda.h" +#include "escc.h" +#include "drivers/pci.h" + +#define OW_IO_NVRAM_SIZE 0x00020000 +#define OW_IO_NVRAM_OFFSET 0x00060000 +#define OW_IO_NVRAM_SHIFT 4 + +#define NW_IO_NVRAM_SIZE 0x00004000 +#define NW_IO_NVRAM_OFFSET 0xfff04000 + +#define IO_OPENPIC_SIZE 0x00040000 +#define IO_OPENPIC_OFFSET 0x00040000 + +static char *nvram; + +static int macio_nvram_shift(void) +{ + int nvram_flat; + + if (is_oldworld()) + return OW_IO_NVRAM_SHIFT; + + nvram_flat = fw_cfg_read_i32(FW_CFG_PPC_NVRAM_FLAT); + return nvram_flat ? 0 : 1; +} + +int +macio_get_nvram_size(void) +{ + int shift = macio_nvram_shift(); + if (is_oldworld()) + return OW_IO_NVRAM_SIZE >> shift; + else + return NW_IO_NVRAM_SIZE >> shift; +} + +static unsigned long macio_nvram_offset(void) +{ + unsigned long r; + + /* Hypervisor tells us where NVRAM lies */ + r = fw_cfg_read_i32(FW_CFG_PPC_NVRAM_ADDR); + if (r) + return r; + + /* Fall back to hardcoded addresses */ + if (is_oldworld()) + return OW_IO_NVRAM_OFFSET; + + return NW_IO_NVRAM_OFFSET; +} + +static unsigned long macio_nvram_size(void) +{ + if (is_oldworld()) + return OW_IO_NVRAM_SIZE; + else + return NW_IO_NVRAM_SIZE; +} + +void macio_nvram_init(const char *path, phys_addr_t addr) +{ + phandle_t chosen, aliases; + phandle_t dnode; + int props[2]; + char buf[64]; + unsigned long nvram_size, nvram_offset; + + nvram_offset = macio_nvram_offset(); + nvram_size = macio_nvram_size(); + + nvram = (char*)addr + nvram_offset; + snprintf(buf, sizeof(buf), "%s/nvram", path); + nvram_init(buf); + dnode = find_dev(buf); + set_int_property(dnode, "#bytes", arch_nvram_size() ); + props[0] = __cpu_to_be32(nvram_offset); + props[1] = __cpu_to_be32(nvram_size); + set_property(dnode, "reg", (char *)&props, sizeof(props)); + set_property(dnode, "device_type", "nvram", 6); + NEWWORLD(set_property(dnode, "compatible", "nvram,flash", 12)); + + chosen = find_dev("/chosen"); + push_str(buf); + fword("open-dev"); + set_int_property(chosen, "nvram", POP()); + + aliases = find_dev("/aliases"); + set_property(aliases, "nvram", buf, strlen(buf) + 1); +} + +#ifdef DUMP_NVRAM +static void +dump_nvram(void) +{ + int i, j; + for (i = 0; i < 10; i++) + { + for (j = 0; j < 16; j++) + printk ("%02x ", nvram[(i*16+j)<<4]); + printk (" "); + for (j = 0; j < 16; j++) + if (isprint(nvram[(i*16+j)<<4])) + printk("%c", nvram[(i*16+j)<<4]); + else + printk("."); + printk ("\n"); + } +} +#endif + + +void +macio_nvram_put(char *buf) +{ + int i; + unsigned int it_shift = macio_nvram_shift(); + + for (i=0; i < arch_nvram_size(); i++) + nvram[i << it_shift] = buf[i]; +#ifdef DUMP_NVRAM + printk("new nvram:\n"); + dump_nvram(); +#endif +} + +void +macio_nvram_get(char *buf) +{ + int i; + unsigned int it_shift = macio_nvram_shift(); + + for (i=0; i< arch_nvram_size(); i++) + buf[i] = nvram[i << it_shift]; + +#ifdef DUMP_NVRAM + printk("current nvram:\n"); + dump_nvram(); +#endif +} + +static void +openpic_init(const char *path, phys_addr_t addr) +{ + phandle_t dnode; + int props[2]; + char buf[128]; + + push_str(path); + fword("find-device"); + fword("new-device"); + push_str("interrupt-controller"); + fword("device-name"); + + snprintf(buf, sizeof(buf), "%s/interrupt-controller", path); + dnode = find_dev(buf); + set_property(dnode, "device_type", "open-pic", 9); + set_property(dnode, "compatible", "chrp,open-pic", 14); + set_property(dnode, "built-in", "", 0); + props[0] = __cpu_to_be32(IO_OPENPIC_OFFSET); + props[1] = __cpu_to_be32(IO_OPENPIC_SIZE); + set_property(dnode, "reg", (char *)&props, sizeof(props)); + set_int_property(dnode, "#interrupt-cells", 2); + set_int_property(dnode, "#address-cells", 0); + set_property(dnode, "interrupt-controller", "", 0); + set_int_property(dnode, "clock-frequency", 4166666); + + fword("finish-device"); +} + +DECLARE_NODE(ob_macio, INSTALL_OPEN, sizeof(int), "Tmac-io"); + +/* ( str len -- addr ) */ + +static void +ob_macio_decode_unit(void *private) +{ + ucell addr; + + const char *arg = pop_fstr_copy(); + + addr = strtol(arg, NULL, 16); + + free((char*)arg); + + PUSH(addr); +} + +/* ( addr -- str len ) */ + +static void +ob_macio_encode_unit(void *private) +{ + char buf[8]; + + ucell addr = POP(); + + snprintf(buf, sizeof(buf), "%x", addr); + + push_str(buf); +} + +NODE_METHODS(ob_macio) = { + { "decode-unit", ob_macio_decode_unit }, + { "encode-unit", ob_macio_encode_unit }, +}; + +static void +ob_unin_init(void) +{ + phandle_t dnode; + int props[2]; + + push_str("/"); + fword("find-device"); + fword("new-device"); + push_str("uni-n"); + fword("device-name"); + + dnode = find_dev("/uni-n"); + set_property(dnode, "device_type", "memory-controller", 18); + set_property(dnode, "compatible", "uni-north", 10); + set_int_property(dnode, "device-rev", 0); + props[0] = __cpu_to_be32(0xf8000000); + props[1] = __cpu_to_be32(0x1000000); + set_property(dnode, "reg", (char *)&props, sizeof(props)); + + fword("finish-device"); +} + +void +ob_macio_heathrow_init(const char *path, phys_addr_t addr) +{ + phandle_t aliases; + + REGISTER_NODE(ob_macio); + aliases = find_dev("/aliases"); + set_property(aliases, "mac-io", path, strlen(path) + 1); + + cuda_init(path, addr); + macio_nvram_init(path, addr); + escc_init(path, addr); + macio_ide_init(path, addr, 2); +} + +void +ob_macio_keylargo_init(const char *path, phys_addr_t addr) +{ + phandle_t aliases; + + aliases = find_dev("/aliases"); + set_property(aliases, "mac-io", path, strlen(path) + 1); + + cuda_init(path, addr); + /* The NewWorld NVRAM is not located in the MacIO device */ + macio_nvram_init("", 0); + escc_init(path, addr); + macio_ide_init(path, addr, 2); + openpic_init(path, addr); + ob_unin_init(); +} diff --git a/qemu/roms/openbios/drivers/macio.h b/qemu/roms/openbios/drivers/macio.h new file mode 100644 index 000000000..925b8812b --- /dev/null +++ b/qemu/roms/openbios/drivers/macio.h @@ -0,0 +1,5 @@ +extern phandle_t pic_handle; + +void ob_macio_heathrow_init(const char *path, phys_addr_t addr); +void ob_macio_keylargo_init(const char *path, phys_addr_t addr); +void macio_nvram_init(const char *path, phys_addr_t addr); diff --git a/qemu/roms/openbios/drivers/obio.c b/qemu/roms/openbios/drivers/obio.c new file mode 100644 index 000000000..7c135a362 --- /dev/null +++ b/qemu/roms/openbios/drivers/obio.c @@ -0,0 +1,525 @@ +/* + * OpenBIOS Sparc OBIO driver + * + * (C) 2004 Stefan Reinauer <stepan@openbios.org> + * (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 */ + +/* DECLARE data structures for the nodes. */ +DECLARE_UNNAMED_NODE( ob_obio, INSTALL_OPEN, sizeof(int) ); + +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_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); + + // 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_obio_open(__attribute__((unused))int *idx) +{ + int ret=1; + RET ( -ret ); +} + +static void +ob_obio_close(__attribute__((unused))int *idx) +{ + selfword("close-deblocker"); +} + +static void +ob_obio_initialize(__attribute__((unused))int *idx) +{ + push_str("/"); + fword("find-device"); + fword("new-device"); + + push_str("obio"); + fword("device-name"); + + push_str("hierarchical"); + fword("device-type"); + + PUSH(2); + fword("encode-int"); + push_str("#address-cells"); + fword("property"); + + PUSH(1); + fword("encode-int"); + push_str("#size-cells"); + fword("property"); + + fword("finish-device"); +} + +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"); +} + +static void +ob_obio_decodeunit(__attribute__((unused)) int *idx) +{ + fword("decode-unit-sbus"); +} + + +static void +ob_obio_encodeunit(__attribute__((unused)) int *idx) +{ + fword("encode-unit-sbus"); +} + +NODE_METHODS(ob_obio) = { + { NULL, ob_obio_initialize }, + { "open", ob_obio_open }, + { "close", ob_obio_close }, + { "encode-unit", ob_obio_encodeunit }, + { "decode-unit", ob_obio_decodeunit }, +}; + + +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"); +#if 0 // XXX + REGISTER_NAMED_NODE(ob_obio, "/obio"); + device_end(); +#endif + 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; +} diff --git a/qemu/roms/openbios/drivers/obio.h b/qemu/roms/openbios/drivers/obio.h new file mode 100644 index 000000000..49c3040c4 --- /dev/null +++ b/qemu/roms/openbios/drivers/obio.h @@ -0,0 +1,165 @@ +/* Addresses, interrupt numbers, register sizes */ + +#define SLAVIO_ZS 0x00000000ULL +#define SLAVIO_ZS1 0x00100000ULL +#define ZS_INTR 0x2c + +#define SLAVIO_NVRAM 0x00200000ULL +#define NVRAM_SIZE 0x2000 +#define NVRAM_IDPROM 0x1fd8 + +#define SLAVIO_FD 0x00400000ULL +#define FD_REGS 15 +#define FD_INTR 0x2b + +#define SLAVIO_SCONFIG 0x00800000ULL +#define SCONFIG_REGS 1 + +#define AUXIO_REGS 1 + +#define AUXIO2_REGS 1 +#define AUXIO2_INTR 0x22 + +#define SLAVIO_COUNTER 0x00d00000ULL +#define COUNTER_REGS 0x10 + +#define SLAVIO_INTERRUPT 0x00e00000ULL +#define INTERRUPT_REGS 0x10 + +#define SLAVIO_RESET 0x00f00000ULL +#define RESET_REGS 1 + +#define ECC_BASE 0xf00000000ULL +#define ECC_SIZE 0x20 + +#define SLAVIO_SIZE 0x01000000 + +#define SUN4M_NCPUS 16 + +#define CFG_ADDR 0xd00000510ULL +#define CFG_SIZE 3 + +/* linux/include/asm-sparc/timer.h */ + +/* A sun4m has two blocks of registers which are probably of the same + * structure. LSI Logic's L64851 is told to _decrement_ from the limit + * value. Aurora behaves similarly but its limit value is compacted in + * other fashion (it's wider). Documented fields are defined here. + */ + +/* As with the interrupt register, we have two classes of timer registers + * which are per-cpu and master. Per-cpu timers only hit that cpu and are + * only level 14 ticks, master timer hits all cpus and is level 10. + */ + +#define SUN4M_PRM_CNT_L 0x80000000 +#define SUN4M_PRM_CNT_LVALUE 0x7FFFFC00 + +struct sun4m_timer_percpu_info { + __volatile__ unsigned int l14_timer_limit; /* Initial value is 0x009c4000 */ + __volatile__ unsigned int l14_cur_count; + + /* This register appears to be write only and/or inaccessible + * on Uni-Processor sun4m machines. + */ + __volatile__ unsigned int l14_limit_noclear; /* Data access error is here */ + + __volatile__ unsigned int cntrl; /* =1 after POST on Aurora */ + __volatile__ unsigned char space[PAGE_SIZE - 16]; +}; + +struct sun4m_timer_regs { + struct sun4m_timer_percpu_info cpu_timers[SUN4M_NCPUS]; + volatile unsigned int l10_timer_limit; + volatile unsigned int l10_cur_count; + + /* Again, this appears to be write only and/or inaccessible + * on uni-processor sun4m machines. + */ + volatile unsigned int l10_limit_noclear; + + /* This register too, it must be magic. */ + volatile unsigned int foobar; + + volatile unsigned int cfg; /* equals zero at boot time... */ +}; + +/* + * Registers of hardware timer in sun4m. + */ +struct sun4m_timer_percpu { + volatile unsigned int l14_timer_limit; /* Initial value is 0x009c4000 = 10ms period*/ + volatile unsigned int l14_cur_count; +}; + +struct sun4m_timer_global { + volatile unsigned int l10_timer_limit; + volatile unsigned int l10_cur_count; +}; + +/* linux/include/asm-sparc/irq.h */ + +/* These registers are used for sending/receiving irqs from/to + * different cpu's. + */ +struct sun4m_intreg_percpu { + unsigned int tbt; /* Interrupts still pending for this cpu. */ + + /* These next two registers are WRITE-ONLY and are only + * "on bit" sensitive, "off bits" written have NO affect. + */ + unsigned int clear; /* Clear this cpus irqs here. */ + unsigned int set; /* Set this cpus irqs here. */ + unsigned char space[PAGE_SIZE - 12]; +}; + +/* + * djhr + * Actually the clear and set fields in this struct are misleading.. + * according to the SLAVIO manual (and the same applies for the SEC) + * the clear field clears bits in the mask which will ENABLE that IRQ + * the set field sets bits in the mask to DISABLE the IRQ. + * + * Also the undirected_xx address in the SLAVIO is defined as + * RESERVED and write only.. + * + * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor + * sun4m machines, for MP the layout makes more sense. + */ +struct sun4m_intregs { + struct sun4m_intreg_percpu cpu_intregs[SUN4M_NCPUS]; + unsigned int tbt; /* IRQ's that are still pending. */ + unsigned int irqs; /* Master IRQ bits. */ + + /* Again, like the above, two these registers are WRITE-ONLY. */ + unsigned int clear; /* Clear master IRQ's by setting bits here. */ + unsigned int set; /* Set master IRQ's by setting bits here. */ + + /* This register is both READ and WRITE. */ + unsigned int undirected_target; /* Which cpu gets undirected irqs. */ +}; + +/* Dave Redman (djhr@tadpole.co.uk) + * The sun4m interrupt registers. + */ +#define SUN4M_INT_ENABLE 0x80000000 +#define SUN4M_INT_E14 0x00000080 +#define SUN4M_INT_E10 0x00080000 + +#define SUN4M_HARD_INT(x) (0x000000001 << (x)) +#define SUN4M_SOFT_INT(x) (0x000010000 << (x)) + +#define SUN4M_INT_MASKALL 0x80000000 /* mask all interrupts */ +#define SUN4M_INT_MODULE_ERR 0x40000000 /* module error */ +#define SUN4M_INT_M2S_WRITE 0x20000000 /* write buffer error */ +#define SUN4M_INT_ECC 0x10000000 /* ecc memory error */ +#define SUN4M_INT_FLOPPY 0x00400000 /* floppy disk */ +#define SUN4M_INT_MODULE 0x00200000 /* module interrupt */ +#define SUN4M_INT_VIDEO 0x00100000 /* onboard video */ +#define SUN4M_INT_REALTIME 0x00080000 /* system timer */ +#define SUN4M_INT_SCSI 0x00040000 /* onboard scsi */ +#define SUN4M_INT_AUDIO 0x00020000 /* audio/isdn */ +#define SUN4M_INT_ETHERNET 0x00010000 /* onboard ethernet */ +#define SUN4M_INT_SERIAL 0x00008000 /* serial ports */ +#define SUN4M_INT_KBDMS 0x00004000 /* keyboard/mouse */ +#define SUN4M_INT_SBUSBITS 0x00003F80 /* sbus int bits */ diff --git a/qemu/roms/openbios/drivers/pc_kbd.c b/qemu/roms/openbios/drivers/pc_kbd.c new file mode 100644 index 000000000..49218f82e --- /dev/null +++ b/qemu/roms/openbios/drivers/pc_kbd.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2003, 2004 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "kernel/kernel.h" +#include "drivers/drivers.h" +#include "libc/vsprintf.h" + +/* ****************************************************************** + * simple polling video/keyboard console functions + * ****************************************************************** */ + +#define SER_SIZE 8 + +/* + * keyboard driver + */ + +static const char normal[] = { + 0x0, 0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', + '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', + 'p', '[', ']', 0xa, 0x0, 'a', 's', 'd', 'f', 'g', 'h', 'j', + 'k', 'l', ';', 0x27, 0x60, 0x0, 0x5c, 'z', 'x', 'c', 'v', 'b', + 'n', 'm', ',', '.', '/', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '0', 0x7f +}; + +static const char shifted[] = { + 0x0, 0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', + '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', + 'P', '{', '}', 0xa, 0x0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', + 'K', 'L', ':', 0x22, '~', 0x0, '|', 'Z', 'X', 'C', 'V', 'B', + 'N', 'M', '<', '>', '?', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '7', '8', + '9', 0x0, '4', '5', '6', 0x0, '1', '2', '3', '0', 0x7f +}; + +static int key_ext; +static int key_lshift = 0, key_rshift = 0, key_caps = 0; + +static char last_key; + +static void pc_kbd_cmd(unsigned char cmd, unsigned char val) +{ + outb(cmd, 0x60); + /* wait until keyboard controller accepts cmds: */ + while (inb(0x64) & 2); + outb(val, 0x60); + while (inb(0x64) & 2); +} + +static void pc_kbd_controller_cmd(unsigned char cmd, unsigned char val) +{ + outb(cmd, 0x64); + /* wait until keyboard controller accepts cmds: */ + while (inb(0x64) & 2); + outb(val, 0x60); + while (inb(0x64) & 2); +} + +static char pc_kbd_poll(void) +{ + unsigned int c; + if (inb(0x64) & 1) { + c = inb(0x60); + switch (c) { + case 0xe0: + key_ext = 1; + return 0; + case 0x2a: + key_lshift = 1; + return 0; + case 0x36: + key_rshift = 1; + return 0; + case 0xaa: + key_lshift = 0; + return 0; + case 0xb6: + key_rshift = 0; + return 0; + case 0x3a: + if (key_caps) { + key_caps = 0; + pc_kbd_cmd(0xed, 0); + } else { + key_caps = 1; + pc_kbd_cmd(0xed, 4); /* set caps led */ + } + return 0; + } + + if (key_ext) { + // void printk(const char *format, ...); + printk("extended keycode: %x\n", c); + + key_ext = 0; + return 0; + } + + if (c & 0x80) /* unhandled key release */ + return 0; + + if (key_lshift || key_rshift) + return key_caps ? normal[c] : shifted[c]; + else + return key_caps ? shifted[c] : normal[c]; + } + return 0; +} + +int pc_kbd_dataready(void) +{ + if (last_key) + return 1; + + last_key = pc_kbd_poll(); + + return (last_key != 0); +} + +unsigned char pc_kbd_readdata(void) +{ + char tmp; + while (!pc_kbd_dataready()); + tmp = last_key; + last_key = 0; + return tmp; +} + +/* ( addr len -- actual ) */ +static void +pc_kbd_read(void) +{ + unsigned char *addr; + int len; + + len = POP(); + addr = (unsigned char *)POP(); + + if (len != 1) + printk("pc_kbd_read: bad len, addr %lx len %x\n", (unsigned long)addr, len); + + if (pc_kbd_dataready()) { + *addr = pc_kbd_readdata(); + PUSH(1); + } else { + PUSH(0); + } +} + +static void +pc_kbd_close(void) +{ +} + +static void +pc_kbd_open(unsigned long *address) +{ + int len; + phandle_t ph; + unsigned long *prop; + + fword("my-self"); + fword("ihandle>phandle"); + ph = (phandle_t)POP(); + prop = (unsigned long *)get_property(ph, "address", &len); + *address = *prop; + + RET ( -1 ); +} + +DECLARE_UNNAMED_NODE(pc_kbd, INSTALL_OPEN, sizeof(unsigned long)); + +NODE_METHODS(pc_kbd) = { + { "open", pc_kbd_open }, + { "close", pc_kbd_close }, + { "read", pc_kbd_read }, +}; + +void +ob_pc_kbd_init(const char *path, const char *dev_name, uint64_t base, + uint64_t offset, int intr) +{ + phandle_t chosen, aliases; + char nodebuff[128]; + + snprintf(nodebuff, sizeof(nodebuff), "%s/%s", path, dev_name); + REGISTER_NAMED_NODE(pc_kbd, nodebuff); + + push_str(nodebuff); + fword("find-device"); + + push_str(dev_name); + fword("device-name"); + + push_str("serial"); + fword("device-type"); + + PUSH(-1); + fword("encode-int"); + push_str("keyboard"); + fword("property"); + + PUSH((base + offset) >> 32); + fword("encode-int"); + PUSH((base + offset) & 0xffffffff); + fword("encode-int"); + fword("encode+"); + PUSH(SER_SIZE); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + PUSH(offset); + fword("encode-int"); + push_str("address"); + fword("property"); + + chosen = find_dev("/chosen"); + push_str(nodebuff); + fword("open-dev"); + set_int_property(chosen, "keyboard", POP()); + + aliases = find_dev("/aliases"); + set_property(aliases, "keyboard", nodebuff, strlen(nodebuff) + 1); + + pc_kbd_controller_cmd(0x60, 0x40); // Write mode command, translated mode +} diff --git a/qemu/roms/openbios/drivers/pc_serial.c b/qemu/roms/openbios/drivers/pc_serial.c new file mode 100644 index 000000000..a638e1f99 --- /dev/null +++ b/qemu/roms/openbios/drivers/pc_serial.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2003, 2004 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "kernel/kernel.h" +#include "drivers/drivers.h" +#include "libc/vsprintf.h" + +/* ****************************************************************** + * serial console functions + * ****************************************************************** */ + +#define SER_SIZE 8 + +#define RBR(x) x==2?0x2f8:0x3f8 +#define THR(x) x==2?0x2f8:0x3f8 +#define IER(x) x==2?0x2f9:0x3f9 +#define IIR(x) x==2?0x2fa:0x3fa +#define LCR(x) x==2?0x2fb:0x3fb +#define MCR(x) x==2?0x2fc:0x3fc +#define LSR(x) x==2?0x2fd:0x3fd +#define MSR(x) x==2?0x2fe:0x3fe +#define SCR(x) x==2?0x2ff:0x3ff +#define DLL(x) x==2?0x2f8:0x3f8 +#define DLM(x) x==2?0x2f9:0x3f9 + +int uart_charav(int port) +{ + return ((inb(LSR(port)) & 1) != 0); +} + +char uart_getchar(int port) +{ + while (!uart_charav(port)); + return ((char) inb(RBR(port)) & 0177); +} + +static void uart_port_putchar(int port, unsigned char c) +{ + if (c == '\n') + uart_port_putchar(port, '\r'); + while (!(inb(LSR(port)) & 0x20)); + outb(c, THR(port)); +} + +static void uart_init_line(int port, unsigned long baud) +{ + int i, baudconst; + + switch (baud) { + case 115200: + baudconst = 1; + break; + case 57600: + baudconst = 2; + break; + case 38400: + baudconst = 3; + break; + case 19200: + baudconst = 6; + break; + case 9600: + default: + baudconst = 12; + break; + } + + outb(0x87, LCR(port)); + outb(0x00, DLM(port)); + outb(baudconst, DLL(port)); + outb(0x07, LCR(port)); + outb(0x0f, MCR(port)); + + for (i = 10; i > 0; i--) { + if (inb(LSR(port)) == (unsigned int) 0) + break; + inb(RBR(port)); + } +} + +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL +int uart_init(int port, unsigned long speed) +{ + uart_init_line(port, speed); + return -1; +} + +void uart_putchar(int c) +{ + uart_port_putchar(CONFIG_SERIAL_PORT, (unsigned char) (c & 0xff)); +} +#endif + +/* ( addr len -- actual ) */ +static void +pc_serial_read(unsigned long *address) +{ + char *addr; + int len; + + len = POP(); + addr = (char *)POP(); + + if (len != 1) + printk("pc_serial_read: bad len, addr %lx len %x\n", (unsigned long)addr, len); + + if (uart_charav(*address)) { + *addr = (char)uart_getchar(*address); + PUSH(1); + } else { + PUSH(0); + } +} + +/* ( addr len -- actual ) */ +static void +pc_serial_write(unsigned long *address) +{ + unsigned char *addr; + int i, len; + + len = POP(); + addr = (unsigned char *)POP(); + + for (i = 0; i < len; i++) { + uart_port_putchar(*address, addr[i]); + } + PUSH(len); +} + +static void +pc_serial_close(void) +{ +} + +static void +pc_serial_open(unsigned long *address) +{ + RET ( -1 ); +} + +static void +pc_serial_init(unsigned long *address) +{ + *address = POP(); +} + +DECLARE_UNNAMED_NODE(pc_serial, INSTALL_OPEN, sizeof(unsigned long)); + +NODE_METHODS(pc_serial) = { + { "init", pc_serial_init }, + { "open", pc_serial_open }, + { "close", pc_serial_close }, + { "read", pc_serial_read }, + { "write", pc_serial_write }, +}; + +void +ob_pc_serial_init(const char *path, const char *dev_name, uint64_t base, + uint64_t offset, int intr) +{ + phandle_t aliases; + char nodebuff[128]; + + snprintf(nodebuff, sizeof(nodebuff), "%s/%s", path, dev_name); + REGISTER_NAMED_NODE(pc_serial, nodebuff); + + push_str(nodebuff); + fword("find-device"); + + PUSH(offset); + PUSH(find_package_method("init", get_cur_dev())); + fword("execute"); + + push_str("serial"); + fword("device-type"); + + PUSH((base + offset) >> 32); + fword("encode-int"); + PUSH((base + offset) & 0xffffffff); + fword("encode-int"); + fword("encode+"); + PUSH(SER_SIZE); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + +#if !defined(CONFIG_SPARC64) + PUSH(offset); + fword("encode-int"); + push_str("address"); + fword("property"); +#endif + +#if defined(CONFIG_SPARC64) + set_int_property(get_cur_dev(), "interrupts", 1); +#endif + + aliases = find_dev("/aliases"); + set_property(aliases, "ttya", nodebuff, strlen(nodebuff) + 1); +} diff --git a/qemu/roms/openbios/drivers/pci.c b/qemu/roms/openbios/drivers/pci.c new file mode 100644 index 000000000..366f4a17f --- /dev/null +++ b/qemu/roms/openbios/drivers/pci.c @@ -0,0 +1,1569 @@ +/* + * OpenBIOS pci driver + * + * This driver is compliant to the + * PCI bus binding to IEEE 1275-1994 Rev 2.1 + * + * (C) 2004 Stefan Reinauer <stepan@openbios.org> + * (C) 2005 Ed Schouten <ed@fxq.nl> + * + * Some parts from OpenHackWare-0.4, Copyright (c) 2004-2005 Jocelyn Mayer + * + * 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 "libopenbios/ofmem.h" +#include "kernel/kernel.h" +#include "drivers/pci.h" +#include "libc/byteorder.h" +#include "libc/vsprintf.h" + +#include "drivers/drivers.h" +#include "drivers/vga.h" +#include "packages/video.h" +#include "libopenbios/video.h" +#include "timer.h" +#include "pci.h" +#include "pci_database.h" +#ifdef CONFIG_DRIVER_MACIO +#include "cuda.h" +#include "macio.h" +#endif +#ifdef CONFIG_DRIVER_USB +#include "drivers/usb.h" +#endif + +#if defined (CONFIG_DEBUG_PCI) +# define PCI_DPRINTF(format, ...) printk(format, ## __VA_ARGS__) +#else +# define PCI_DPRINTF(format, ...) do { } while (0) +#endif + +#define set_bool_property(ph, name) set_property(ph, name, NULL, 0); + +/* DECLARE data structures for the nodes. */ + +DECLARE_UNNAMED_NODE( ob_pci_bus_node, INSTALL_OPEN, 2*sizeof(int) ); +DECLARE_UNNAMED_NODE( ob_pci_simple_node, INSTALL_OPEN, 2*sizeof(int) ); +DECLARE_UNNAMED_NODE( ob_pci_empty_node, 0, 2*sizeof(int) ); + +const pci_arch_t *arch; + +#define IS_NOT_RELOCATABLE 0x80000000 +#define IS_PREFETCHABLE 0x40000000 +#define IS_ALIASED 0x20000000 + +enum { + CONFIGURATION_SPACE = 0, + IO_SPACE = 1, + MEMORY_SPACE_32 = 2, + MEMORY_SPACE_64 = 3, +}; + +static int encode_int32_cells(int num_cells, u32 *prop, ucell val) +{ + int i = 0; + + /* hi ... lo */ + for (i=0; i < num_cells; ++i) { + prop[num_cells - i - 1] = val; + val >>= 16; + val >>= 16; + } + + return num_cells; +} + +static inline int pci_encode_phys_addr(u32 *phys, int flags, int space_code, + pci_addr dev, uint8_t reg, uint64_t addr) +{ + + /* phys.hi */ + + phys[0] = flags | (space_code << 24) | dev | reg; + + /* phys.mid */ + + phys[1] = addr >> 32; + + /* phys.lo */ + + phys[2] = addr; + + return 3; +} + +static inline int pci_encode_size(u32 *prop, uint64_t size) +{ + return encode_int32_cells(2, prop, size); +} + +static int host_address_cells(void) +{ + return get_int_property(find_dev("/"), "#address-cells", NULL); +} + +static int host_encode_phys_addr(u32 *prop, ucell addr) +{ + return encode_int32_cells(host_address_cells(), prop, addr); +} + +static int host_size_cells(void) +{ + return get_int_property(find_dev("/"), "#size-cells", NULL); +} + +/* +static int parent_address_cells(void) +{ + phandle_t parent_ph = ih_to_phandle(my_parent()); + return get_int_property(parent_ph, "#address-cells", NULL); +} + +static int parent_size_cells(void) +{ + phandle_t parent_ph = ih_to_phandle(my_parent()); + return get_int_property(parent_ph, "#size-cells", NULL); +} +*/ + +#if defined(CONFIG_DEBUG_PCI) +static void dump_reg_property(const char* description, int nreg, u32 *reg) +{ + int i; + printk("%s reg", description); + for (i=0; i < nreg; ++i) { + printk(" %08X", reg[i]); + } + printk("\n"); +} +#endif + +static unsigned long pci_bus_addr_to_host_addr(uint32_t ba) +{ + return arch->host_pci_base + (unsigned long)ba; +} + +static void +ob_pci_open(int *idx) +{ + int ret=1; + RET ( -ret ); +} + +static void +ob_pci_close(int *idx) +{ +} + +static void +ob_pci_initialize(int *idx) +{ +} + +/* ( str len -- phys.lo phys.mid phys.hi ) */ + +static void +ob_pci_decode_unit(int *idx) +{ + ucell hi, mid, lo; + const char *arg = pop_fstr_copy(); + int dev, fn, reg, ss, n, p, t; + int bus = 0; /* no information */ + char *ptr; + + PCI_DPRINTF("ob_pci_decode_unit idx=%p\n", idx); + + fn = 0; + reg = 0; + n = 0; + p = 0; + t = 0; + + ptr = (char*)arg; + if (*ptr == 'n') { + n = IS_NOT_RELOCATABLE; + ptr++; + } + if (*ptr == 'i') { + ss = IO_SPACE; + ptr++; + if (*ptr == 't') { + t = IS_ALIASED; + ptr++; + } + + /* DD,F,RR,NNNNNNNN */ + + dev = strtol(ptr, &ptr, 16); + ptr++; + fn = strtol(ptr, &ptr, 16); + ptr++; + reg = strtol(ptr, &ptr, 16); + ptr++; + lo = strtol(ptr, &ptr, 16); + mid = 0; + + } else if (*ptr == 'm') { + ss = MEMORY_SPACE_32; + ptr++; + if (*ptr == 't') { + t = IS_ALIASED; + ptr++; + } + if (*ptr == 'p') { + p = IS_PREFETCHABLE; + ptr++; + } + + /* DD,F,RR,NNNNNNNN */ + + dev = strtol(ptr, &ptr, 16); + ptr++; + fn = strtol(ptr, &ptr, 16); + ptr++; + reg = strtol(ptr, &ptr, 16); + ptr++; + lo = strtol(ptr, &ptr, 16); + mid = 0; + + } else if (*ptr == 'x') { + unsigned long long addr64; + ss = MEMORY_SPACE_64; + ptr++; + if (*ptr == 'p') { + p = IS_PREFETCHABLE; + ptr++; + } + + /* DD,F,RR,NNNNNNNNNNNNNNNN */ + + dev = strtol(ptr, &ptr, 16); + ptr++; + fn = strtol(ptr, &ptr, 16); + ptr++; + reg = strtol(ptr, &ptr, 16); + ptr++; + addr64 = strtoll(ptr, &ptr, 16); + lo = (ucell)addr64; + mid = addr64 >> 32; + + } else { + ss = CONFIGURATION_SPACE; + /* "DD" or "DD,FF" */ + dev = strtol(ptr, &ptr, 16); + if (*ptr == ',') { + ptr++; + fn = strtol(ptr, NULL, 16); + } + lo = 0; + mid = 0; + } + free((char*)arg); + + hi = n | p | t | (ss << 24) | (bus << 16) | (dev << 11) | (fn << 8) | reg; + + PUSH(lo); + PUSH(mid); + PUSH(hi); + + PCI_DPRINTF("ob_pci_decode_unit idx=%p addr=" + FMT_ucellx " " FMT_ucellx " " FMT_ucellx "\n", + idx, lo, mid, hi); +} + +/* ( phys.lo phy.mid phys.hi -- str len ) */ + +static void +ob_pci_encode_unit(int *idx) +{ + char buf[28]; + cell hi = POP(); + cell mid = POP(); + cell lo = POP(); + int n, p, t, ss, dev, fn, reg; + + n = hi & IS_NOT_RELOCATABLE; + p = hi & IS_PREFETCHABLE; + t = hi & IS_ALIASED; + ss = (hi >> 24) & 0x03; + + dev = (hi >> 11) & 0x1F; + fn = (hi >> 8) & 0x07; + reg = hi & 0xFF; + + switch(ss) { + case CONFIGURATION_SPACE: + + if (fn == 0) /* DD */ + snprintf(buf, sizeof(buf), "%x", dev); + else /* DD,F */ + snprintf(buf, sizeof(buf), "%x,%x", dev, fn); + break; + + case IO_SPACE: + + /* [n]i[t]DD,F,RR,NNNNNNNN */ + snprintf(buf, sizeof(buf), "%si%s%x,%x,%x," FMT_ucellx, + n ? "n" : "", /* relocatable */ + t ? "t" : "", /* aliased */ + dev, fn, reg, t ? lo & 0x03FF : lo); + break; + + case MEMORY_SPACE_32: + + /* [n]m[t][p]DD,F,RR,NNNNNNNN */ + snprintf(buf, sizeof(buf), "%sm%s%s%x,%x,%x," FMT_ucellx, + n ? "n" : "", /* relocatable */ + t ? "t" : "", /* aliased */ + p ? "p" : "", /* prefetchable */ + dev, fn, reg, lo ); + break; + + case MEMORY_SPACE_64: + + /* [n]x[p]DD,F,RR,NNNNNNNNNNNNNNNN */ + snprintf(buf, sizeof(buf), "%sx%s%x,%x,%x,%llx", + n ? "n" : "", /* relocatable */ + p ? "p" : "", /* prefetchable */ + dev, fn, reg, ((long long)mid << 32) | (long long)lo); + break; + } + push_str(buf); + + PCI_DPRINTF("ob_pci_encode_unit space=%d dev=%d fn=%d buf=%s\n", + ss, dev, fn, buf); +} + +/* ( pci-addr.lo pci-addr.hi size -- virt ) */ + +static void +ob_pci_map_in(int *idx) +{ + phys_addr_t phys; + uint32_t ba; + ucell size, virt; + + PCI_DPRINTF("ob_pci_bar_map_in idx=%p\n", idx); + + size = POP(); + POP(); + ba = POP(); + + phys = pci_bus_addr_to_host_addr(ba); + +#if defined(CONFIG_OFMEM) + ofmem_claim_phys(phys, size, 0); + +#if defined(CONFIG_PPC) + /* For some reason PPC gets upset when virt != phys for map-in... */ + virt = ofmem_claim_virt(phys, size, 0); +#else + virt = ofmem_claim_virt(-1, size, size); +#endif + + ofmem_map(phys, virt, size, ofmem_arch_io_translation_mode(phys)); + +#else + virt = size; /* Keep compiler quiet */ + virt = phys; +#endif + + PUSH(virt); +} + +NODE_METHODS(ob_pci_bus_node) = { + { NULL, ob_pci_initialize }, + { "open", ob_pci_open }, + { "close", ob_pci_close }, + { "decode-unit", ob_pci_decode_unit }, + { "encode-unit", ob_pci_encode_unit }, + { "pci-map-in", ob_pci_map_in }, +}; + +NODE_METHODS(ob_pci_simple_node) = { + { NULL, ob_pci_initialize }, + { "open", ob_pci_open }, + { "close", ob_pci_close }, +}; + +NODE_METHODS(ob_pci_empty_node) = { + { NULL, ob_pci_initialize } +}; + +static void pci_set_bus_range(const pci_config_t *config) +{ + phandle_t dev = find_dev(config->path); + u32 props[2]; + + props[0] = config->secondary_bus; + props[1] = config->subordinate_bus; + + PCI_DPRINTF("setting bus range for %s PCI device, " + "package handle " FMT_ucellx " " + "bus primary=%d secondary=%d subordinate=%d\n", + config->path, + dev, + config->primary_bus, + config->secondary_bus, + config->subordinate_bus); + + + set_property(dev, "bus-range", (char *)props, 2 * sizeof(props[0])); +} + +static void pci_host_set_reg(phandle_t phandle) +{ + phandle_t dev = phandle; + + /* at most 2 integers for address and size */ + u32 props[4]; + int ncells = 0; + + ncells += encode_int32_cells(host_address_cells(), props + ncells, + arch->cfg_base); + + ncells += encode_int32_cells(host_size_cells(), props + ncells, + arch->cfg_len); + + set_property(dev, "reg", (char *)props, ncells * sizeof(props[0])); + +#if defined(CONFIG_DEBUG_PCI) + dump_reg_property("pci_host_set_reg", 4, props); +#endif +} + +/* child-phys : parent-phys : size */ +/* 3 cells for PCI : 2 cells for 64bit parent : 2 cells for PCI */ + +static void pci_host_set_ranges(const pci_config_t *config) +{ + phandle_t dev = get_cur_dev(); + u32 props[32]; + int ncells; + + ncells = 0; + /* first encode PCI configuration space */ + { + ncells += pci_encode_phys_addr(props + ncells, 0, CONFIGURATION_SPACE, + config->dev, 0, 0); + ncells += host_encode_phys_addr(props + ncells, arch->cfg_addr); + ncells += pci_encode_size(props + ncells, arch->cfg_len); + } + + if (arch->io_base) { + ncells += pci_encode_phys_addr(props + ncells, 0, IO_SPACE, + config->dev, 0, 0); + ncells += host_encode_phys_addr(props + ncells, arch->io_base); + ncells += pci_encode_size(props + ncells, arch->io_len); + } + if (arch->rbase) { + ncells += pci_encode_phys_addr(props + ncells, 0, MEMORY_SPACE_32, + config->dev, 0, 0); + ncells += host_encode_phys_addr(props + ncells, arch->rbase); + ncells += pci_encode_size(props + ncells, arch->rlen); + } + if (arch->pci_mem_base) { + ncells += pci_encode_phys_addr(props + ncells, 0, MEMORY_SPACE_32, + config->dev, 0, arch->pci_mem_base); + ncells += host_encode_phys_addr(props + ncells, arch->host_pci_base + + arch->pci_mem_base); + ncells += pci_encode_size(props + ncells, arch->mem_len); + } + set_property(dev, "ranges", (char *)props, ncells * sizeof(props[0])); +} + +int host_config_cb(const pci_config_t *config) +{ + //XXX this overrides "reg" property + pci_host_set_reg(get_cur_dev()); + pci_host_set_ranges(config); + + return 0; +} + +static int sabre_configure(phandle_t dev) +{ + uint32_t props[28]; + + props[0] = 0xc0000000; + props[1] = 0x20000000; + set_property(dev, "virtual-dma", (char *)props, 2 * sizeof(props[0])); + props[0] = 1; + set_property(dev, "#virtual-dma-size-cells", (char *)props, + sizeof(props[0])); + set_property(dev, "#virtual-dma-addr-cells", (char *)props, + sizeof(props[0])); + + set_property(dev, "no-streaming-cache", (char *)props, 0); + + props[0] = 0x000007f0; + props[1] = 0x000007ee; + props[2] = 0x000007ef; + props[3] = 0x000007e5; + set_property(dev, "interrupts", (char *)props, 4 * sizeof(props[0])); + props[0] = 0x0000001f; + set_property(dev, "upa-portid", (char *)props, 1 * sizeof(props[0])); + return 0; +} + +int sabre_config_cb(const pci_config_t *config) +{ + host_config_cb(config); + + return sabre_configure(get_cur_dev()); +} + +int bridge_config_cb(const pci_config_t *config) +{ + phandle_t aliases; + + aliases = find_dev("/aliases"); + set_property(aliases, "bridge", config->path, strlen(config->path) + 1); + + return 0; +} + +int ide_config_cb2 (const pci_config_t *config) +{ + ob_ide_init(config->path, + config->assigned[0] & ~0x0000000F, + (config->assigned[1] & ~0x0000000F) + 2, + config->assigned[2] & ~0x0000000F, + (config->assigned[3] & ~0x0000000F) + 2); + return 0; +} + +int eth_config_cb (const pci_config_t *config) +{ + phandle_t ph = get_cur_dev(); + + set_property(ph, "network-type", "ethernet", 9); + set_property(ph, "removable", "network", 8); + set_property(ph, "category", "net", 4); + + return 0; +} + +static inline void pci_decode_pci_addr(pci_addr addr, int *flags, + int *space_code, uint32_t *mask) +{ + *flags = 0; + + if (addr & 0x01) { + *space_code = IO_SPACE; + *mask = 0x00000001; + } else { + if (addr & 0x04) { + *space_code = MEMORY_SPACE_64; + *flags |= IS_NOT_RELOCATABLE; /* XXX: why not relocatable? */ + } else { + *space_code = MEMORY_SPACE_32; + } + + if (addr & 0x08) { + *flags |= IS_PREFETCHABLE; + } + + *mask = 0x0000000F; + } +} + +/* + * "Designing PCI Cards and Drivers for Power Macintosh Computers", p. 454 + * + * "AAPL,address" provides an array of 32-bit logical addresses + * Nth entry corresponding to Nth "assigned-address" base address entry. + */ + +static void pci_set_AAPL_address(const pci_config_t *config) +{ + phandle_t dev = get_cur_dev(); + cell props[7]; + int ncells, i; + + ncells = 0; + for (i = 0; i < 6; i++) { + if (!config->assigned[i] || !config->sizes[i]) + continue; + props[ncells++] = config->assigned[i] & ~0x0000000F; + } + if (ncells) + set_property(dev, "AAPL,address", (char *)props, + ncells * sizeof(cell)); +} + +static void pci_set_assigned_addresses(phandle_t phandle, + const pci_config_t *config, int num_bars) +{ + phandle_t dev = phandle; + u32 props[32]; + int ncells; + int i; + uint32_t mask; + int flags, space_code; + + ncells = 0; + for (i = 0; i < num_bars; i++) { + /* consider only bars with non-zero region size */ + if (!config->sizes[i]) + continue; + pci_decode_pci_addr(config->assigned[i], + &flags, &space_code, &mask); + + ncells += pci_encode_phys_addr(props + ncells, + flags, space_code, config->dev, + PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)), + config->assigned[i] & ~mask); + + props[ncells++] = 0x00000000; + props[ncells++] = config->sizes[i]; + } + if (ncells) + set_property(dev, "assigned-addresses", (char *)props, + ncells * sizeof(props[0])); +} + +/* call after writing "reg" property to update config->path */ +static void ob_pci_reload_device_path(phandle_t phandle, pci_config_t *config) +{ + /* since "name" and "reg" are now assigned + we need to reload current node name */ + + PUSH(phandle); + fword("get-package-path"); + char *new_path = pop_fstr_copy(); + if (new_path) { + if (0 != strcmp(config->path, new_path)) { + PCI_DPRINTF("\n=== CHANGED === package path old=%s new=%s\n", + config->path, new_path); + strncpy(config->path, new_path, sizeof(config->path)); + config->path[sizeof(config->path)-1] = '\0'; + } + free(new_path); + } else { + PCI_DPRINTF("\n=== package path old=%s new=NULL\n", config->path); + } +} + +static void pci_set_reg(phandle_t phandle, + pci_config_t *config, int num_bars) +{ + phandle_t dev = phandle; + u32 props[38]; + int ncells; + int i; + uint32_t mask; + int space_code, flags; + + ncells = 0; + + /* first (addr, size) pair is the beginning of configuration address space */ + ncells += pci_encode_phys_addr(props + ncells, 0, CONFIGURATION_SPACE, + config->dev, 0, 0); + + ncells += pci_encode_size(props + ncells, 0); + + for (i = 0; i < num_bars; i++) { + /* consider only bars with non-zero region size */ + if (!config->sizes[i]) + continue; + + pci_decode_pci_addr(config->regions[i], + &flags, &space_code, &mask); + + ncells += pci_encode_phys_addr(props + ncells, + flags, space_code, config->dev, + PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)), + config->regions[i] & ~mask); + + /* set size */ + ncells += pci_encode_size(props + ncells, config->sizes[i]); + } + + set_property(dev, "reg", (char *)props, ncells * sizeof(props[0])); + ob_pci_reload_device_path(dev, config); + +#if defined(CONFIG_DEBUG_PCI) + dump_reg_property("pci_set_reg", ncells, props); +#endif +} + + +static void pci_set_ranges(const pci_config_t *config) +{ + phandle_t dev = get_cur_dev(); + u32 props[32]; + int ncells; + int i; + uint32_t mask; + int flags; + int space_code; + + ncells = 0; + for (i = 0; i < 6; i++) { + if (!config->assigned[i] || !config->sizes[i]) + continue; + + /* child address */ + + props[ncells++] = 0x00000000; + + /* parent address */ + + pci_decode_pci_addr(config->assigned[i], + &flags, &space_code, &mask); + ncells += pci_encode_phys_addr(props + ncells, flags, space_code, + config->dev, 0x10 + i * 4, + config->assigned[i] & ~mask); + + /* size */ + + props[ncells++] = config->sizes[i]; + } + set_property(dev, "ranges", (char *)props, ncells * sizeof(props[0])); +} + +int macio_heathrow_config_cb (const pci_config_t *config) +{ + pci_set_ranges(config); + +#ifdef CONFIG_DRIVER_MACIO + ob_macio_heathrow_init(config->path, config->assigned[0] & ~0x0000000F); +#endif + return 0; +} + +int macio_keylargo_config_cb (const pci_config_t *config) +{ + pci_set_ranges(config); + +#ifdef CONFIG_DRIVER_MACIO + ob_macio_keylargo_init(config->path, config->assigned[0] & ~0x0000000F); +#endif + return 0; +} + +int vga_config_cb (const pci_config_t *config) +{ + unsigned long rom; + uint32_t rom_size, size; + phandle_t ph; + + if (config->assigned[0] != 0x00000000) { + setup_video(); + + rom = pci_bus_addr_to_host_addr(config->assigned[1] & ~0x0000000F); + rom_size = config->sizes[1]; + + ph = get_cur_dev(); + + if (rom_size >= 8) { + const char *p; + + p = (const char *)rom; + if (p[0] == 'N' && p[1] == 'D' && p[2] == 'R' && p[3] == 'V') { + size = *(uint32_t*)(p + 4); + set_property(ph, "driver,AAPL,MacOS,PowerPC", p + 8, size); + } + } + + /* Currently we don't read FCode from the hardware but execute it directly */ + feval("['] vga-driver-fcode 2 cells + 1 byte-load"); + +#ifdef CONFIG_MOL + /* Install special words for Mac On Linux */ + molvideo_init(); +#endif + + } + + return 0; +} + +int ebus_config_cb(const pci_config_t *config) +{ +#ifdef CONFIG_DRIVER_EBUS + phandle_t dev = get_cur_dev(); + uint32_t props[12]; + int ncells; + int i; + uint32_t mask; + int flags, space_code; + + props[0] = 0x14; + props[1] = 0x3f8; + props[2] = 1; + props[3] = find_dev("/"); + props[4] = 0x2b; + set_property(dev, "interrupt-map", (char *)props, 5 * sizeof(props[0])); + + props[0] = 0x000001ff; + props[1] = 0xffffffff; + props[2] = 3; + set_property(dev, "interrupt-map-mask", (char *)props, 3 * sizeof(props[0])); + + /* Build ranges property from the BARs */ + ncells = 0; + for (i = 0; i < 6; i++) { + /* consider only bars with non-zero region size */ + if (!config->sizes[i]) + continue; + + pci_decode_pci_addr(config->assigned[i], + &flags, &space_code, &mask); + + props[ncells++] = PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)); + props[ncells++] = 0x0; + + ncells += pci_encode_phys_addr(props + ncells, + flags, space_code, config->dev, + PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)), + 0); + + props[ncells++] = config->sizes[i]; + } + + set_property(dev, "ranges", (char *)props, ncells * sizeof(props[0])); + + /* Build eeprom node */ + fword("new-device"); + PUSH(0x14); + fword("encode-int"); + PUSH(0x2000); + fword("encode-int"); + fword("encode+"); + PUSH(0x2000); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + push_str("mk48t59"); + fword("model"); + + push_str("eeprom"); + fword("device-name"); + fword("finish-device"); + +#ifdef CONFIG_DRIVER_FLOPPY + ob_floppy_init(config->path, "fdthree", 0x3f0ULL, 0); +#endif +#ifdef CONFIG_DRIVER_PC_SERIAL + ob_pc_serial_init(config->path, "su", (PCI_BASE_ADDR_1 | 0ULL) << 32, 0x3f8ULL, 0); +#endif +#ifdef CONFIG_DRIVER_PC_KBD + ob_pc_kbd_init(config->path, "kb_ps2", (PCI_BASE_ADDR_1 | 0ULL) << 32, 0x60ULL, 0); +#endif +#endif + return 0; +} + +int i82378_config_cb(const pci_config_t *config) +{ +#ifdef CONFIG_DRIVER_PC_SERIAL + ob_pc_serial_init(config->path, "serial", arch->io_base, 0x3f8ULL, 0); +#endif +#ifdef CONFIG_DRIVER_PC_KBD + ob_pc_kbd_init(config->path, "8042", arch->io_base, 0x60ULL, 0); +#endif +#ifdef CONFIG_DRIVER_IDE + ob_ide_init(config->path, 0x1f0, 0x3f6, 0x170, 0x376); +#endif + + return 0; +} + +int usb_ohci_config_cb(const pci_config_t *config) +{ +#ifdef CONFIG_DRIVER_USB + ob_usb_ohci_init(config->path, 0x80000000 | config->dev); +#endif + return 0; +} + +static void ob_pci_add_properties(phandle_t phandle, + pci_addr addr, const pci_dev_t *pci_dev, + const pci_config_t *config, int num_bars) +{ + /* cannot use get_cur_dev() path resolution since "name" and "reg" + properties are being changed */ + phandle_t dev=phandle; + int status,id; + uint16_t vendor_id, device_id; + uint8_t rev; + uint8_t class_prog; + uint32_t class_code; + + vendor_id = pci_config_read16(addr, PCI_VENDOR_ID); + device_id = pci_config_read16(addr, PCI_DEVICE_ID); + rev = pci_config_read8(addr, PCI_REVISION_ID); + class_prog = pci_config_read8(addr, PCI_CLASS_PROG); + class_code = pci_config_read16(addr, PCI_CLASS_DEVICE); + + if (pci_dev) { + /**/ + if (pci_dev->name) { + push_str(pci_dev->name); + fword("encode-string"); + push_str("name"); + fword("property"); + } else { + char path[256]; + snprintf(path, sizeof(path), + "pci%x,%x", vendor_id, device_id); + push_str(path); + fword("encode-string"); + push_str("name"); + fword("property"); + } + } else { + PCI_DPRINTF("*** missing pci_dev\n"); + } + + /* create properties as described in 2.5 */ + + set_int_property(dev, "vendor-id", vendor_id); + set_int_property(dev, "device-id", device_id); + set_int_property(dev, "revision-id", rev); + set_int_property(dev, "class-code", class_code << 8 | class_prog); + + if (config->irq_pin) { + OLDWORLD(set_int_property(dev, "AAPL,interrupts", + config->irq_line)); +#if defined(CONFIG_SPARC64) + set_int_property(dev, "interrupts", config->irq_pin); +#else + NEWWORLD(set_int_property(dev, "interrupts", config->irq_pin)); +#endif + } + + set_int_property(dev, "min-grant", pci_config_read8(addr, PCI_MIN_GNT)); + set_int_property(dev, "max-latency", pci_config_read8(addr, PCI_MAX_LAT)); + + status=pci_config_read16(addr, PCI_STATUS); + + set_int_property(dev, "devsel-speed", + (status&PCI_STATUS_DEVSEL_MASK)>>10); + + if(status&PCI_STATUS_FAST_BACK) + set_bool_property(dev, "fast-back-to-back"); + if(status&PCI_STATUS_66MHZ) + set_bool_property(dev, "66mhz-capable"); + if(status&PCI_STATUS_UDF) + set_bool_property(dev, "udf-supported"); + + id=pci_config_read16(addr, PCI_SUBSYSTEM_VENDOR_ID); + if(id) + set_int_property(dev, "subsystem-vendor-id", id); + id=pci_config_read16(addr, PCI_SUBSYSTEM_ID); + if(id) + set_int_property(dev, "subsystem-id", id); + + set_int_property(dev, "cache-line-size", + pci_config_read16(addr, PCI_CACHE_LINE_SIZE)); + + if (pci_dev) { + if (pci_dev->type) { + push_str(pci_dev->type); + fword("encode-string"); + push_str("device_type"); + fword("property"); + } + if (pci_dev->model) { + push_str(pci_dev->model); + fword("encode-string"); + push_str("model"); + fword("property"); + } + if (pci_dev->compat) + set_property(dev, "compatible", + pci_dev->compat, pci_compat_len(pci_dev)); + + if (pci_dev->acells) + set_int_property(dev, "#address-cells", + pci_dev->acells); + if (pci_dev->scells) + set_int_property(dev, "#size-cells", + pci_dev->scells); + if (pci_dev->icells) + set_int_property(dev, "#interrupt-cells", + pci_dev->icells); + } + + pci_set_assigned_addresses(phandle, config, num_bars); + OLDWORLD(pci_set_AAPL_address(config)); + + PCI_DPRINTF("\n"); +} + +#ifdef CONFIG_XBOX +static char pci_xbox_blacklisted (int bus, int devnum, int fn) +{ + /* + * The Xbox MCPX chipset is a derivative of the nForce 1 + * chipset. It almost has the same bus layout; some devices + * cannot be used, because they have been removed. + */ + + /* + * Devices 00:00.1 and 00:00.2 used to be memory controllers on + * the nForce chipset, but on the Xbox, using them will lockup + * the chipset. + */ + if ((bus == 0) && (devnum == 0) && ((fn == 1) || (fn == 2))) + return 1; + + /* + * Bus 1 only contains a VGA controller at 01:00.0. When you try + * to probe beyond that device, you only get garbage, which + * could cause lockups. + */ + if ((bus == 1) && ((devnum != 0) || (fn != 0))) + return 1; + + /* + * Bus 2 used to contain the AGP controller, but the Xbox MCPX + * doesn't have one. Probing it can cause lockups. + */ + if (bus >= 2) + return 1; + + /* + * The device is not blacklisted. + */ + return 0; +} +#endif + +static void ob_pci_configure_bar(pci_addr addr, pci_config_t *config, + int reg, int config_addr, + uint32_t *p_omask, + unsigned long *mem_base, + unsigned long *io_base) +{ + uint32_t smask, amask, size, reloc, min_align; + unsigned long base; + + config->assigned[reg] = 0x00000000; + config->sizes[reg] = 0x00000000; + + if ((*p_omask & 0x0000000f) == 0x4) { + /* 64 bits memory mapping */ + PCI_DPRINTF("Skipping 64 bit BARs for %s\n", config->path); + return; + } + + config->regions[reg] = pci_config_read32(addr, config_addr); + + /* get region size */ + + pci_config_write32(addr, config_addr, 0xffffffff); + smask = pci_config_read32(addr, config_addr); + if (smask == 0x00000000 || smask == 0xffffffff) + return; + + if (smask & 0x00000001 && reg != 6) { + /* I/O space */ + base = *io_base; + min_align = 1 << 7; + amask = 0x00000001; + } else { + /* Memory Space */ + base = *mem_base; + min_align = 1 << 16; + amask = 0x0000000F; + if (reg == 6) { + smask |= 1; /* ROM */ + } + } + *p_omask = smask & amask; + smask &= ~amask; + size = (~smask) + 1; + config->sizes[reg] = size; + reloc = base; + if (size < min_align) + size = min_align; + reloc = (reloc + size -1) & ~(size - 1); + if (*io_base == base) { + PCI_DPRINTF("changing io_base from 0x%lx to 0x%x\n", + *io_base, reloc + size); + *io_base = reloc + size; + } else { + PCI_DPRINTF("changing mem_base from 0x%lx to 0x%x\n", + *mem_base, reloc + size); + *mem_base = reloc + size; + } + PCI_DPRINTF("Configuring BARs for %s: reloc 0x%x omask 0x%x " + "io_base 0x%lx mem_base 0x%lx size 0x%x\n", + config->path, reloc, *p_omask, *io_base, *mem_base, size); + pci_config_write32(addr, config_addr, reloc | *p_omask); + config->assigned[reg] = reloc | *p_omask; +} + +static void ob_pci_configure_irq(pci_addr addr, pci_config_t *config) +{ + uint8_t irq_pin, irq_line; + + irq_pin = pci_config_read8(addr, PCI_INTERRUPT_PIN); + if (irq_pin) { + config->irq_pin = irq_pin; + irq_pin = (((config->dev >> 11) & 0x1F) + irq_pin - 1) & 3; + irq_line = arch->irqs[irq_pin]; + pci_config_write8(addr, PCI_INTERRUPT_LINE, irq_line); + config->irq_line = irq_line; + } else + config->irq_line = -1; +} + +static void +ob_pci_configure(pci_addr addr, pci_config_t *config, int num_regs, int rom_bar, + unsigned long *mem_base, unsigned long *io_base) + +{ + uint32_t omask; + uint16_t cmd; + int reg; + pci_addr config_addr; + + ob_pci_configure_irq(addr, config); + + omask = 0x00000000; + for (reg = 0; reg < num_regs; ++reg) { + config_addr = PCI_BASE_ADDR_0 + reg * 4; + + ob_pci_configure_bar(addr, config, reg, config_addr, + &omask, mem_base, + io_base); + } + + if (rom_bar) { + config_addr = rom_bar; + ob_pci_configure_bar(addr, config, reg, config_addr, + &omask, mem_base, io_base); + } + cmd = pci_config_read16(addr, PCI_COMMAND); + cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + pci_config_write16(addr, PCI_COMMAND, cmd); +} + +static void ob_configure_pci_device(const char* parent_path, + int *bus_num, unsigned long *mem_base, unsigned long *io_base, + int bus, int devnum, int fn, int *p_is_multi); + +static void ob_scan_pci_bus(int *bus_num, unsigned long *mem_base, + unsigned long *io_base, const char *path, + int bus) +{ + int devnum, fn, is_multi; + + PCI_DPRINTF("\nScanning bus %d at %s...\n", bus, path); + + for (devnum = 0; devnum < 32; devnum++) { + is_multi = 0; + for (fn = 0; fn==0 || (is_multi && fn<8); fn++) { + ob_configure_pci_device(path, bus_num, mem_base, io_base, + bus, devnum, fn, &is_multi); + + } + } +} + +static void ob_configure_pci_bridge(pci_addr addr, + int *bus_num, unsigned long *mem_base, + unsigned long *io_base, + int primary_bus, pci_config_t *config) +{ + config->primary_bus = primary_bus; + pci_config_write8(addr, PCI_PRIMARY_BUS, config->primary_bus); + + config->secondary_bus = *bus_num; + pci_config_write8(addr, PCI_SECONDARY_BUS, config->secondary_bus); + + config->subordinate_bus = 0xff; + pci_config_write8(addr, PCI_SUBORDINATE_BUS, config->subordinate_bus); + + PCI_DPRINTF("scanning new pci bus %u under bridge %s\n", + config->secondary_bus, config->path); + + /* make pci bridge parent device, prepare for recursion */ + + ob_scan_pci_bus(bus_num, mem_base, io_base, + config->path, config->secondary_bus); + + /* bus scan updates *bus_num to last revealed pci bus number */ + config->subordinate_bus = *bus_num; + pci_config_write8(addr, PCI_SUBORDINATE_BUS, config->subordinate_bus); + + PCI_DPRINTF("bridge %s PCI bus primary=%d secondary=%d subordinate=%d\n", + config->path, config->primary_bus, config->secondary_bus, + config->subordinate_bus); + + pci_set_bus_range(config); +} + +static int ob_pci_read_identification(int bus, int devnum, int fn, + int *p_vid, int *p_did, + uint8_t *p_class, uint8_t *p_subclass) +{ + int vid, did; + uint32_t ccode; + pci_addr addr; + +#ifdef CONFIG_XBOX + if (pci_xbox_blacklisted (bus, devnum, fn)) + return; +#endif + addr = PCI_ADDR(bus, devnum, fn); + vid = pci_config_read16(addr, PCI_VENDOR_ID); + did = pci_config_read16(addr, PCI_DEVICE_ID); + + if (vid==0xffff || vid==0) { + return 0; + } + + if (p_vid) { + *p_vid = vid; + } + + if (p_did) { + *p_did = did; + } + + ccode = pci_config_read16(addr, PCI_CLASS_DEVICE); + + if (p_class) { + *p_class = ccode >> 8; + } + + if (p_subclass) { + *p_subclass = ccode; + } + + return 1; +} + +static void ob_configure_pci_device(const char* parent_path, + int *bus_num, unsigned long *mem_base, unsigned long *io_base, + int bus, int devnum, int fn, int *p_is_multi) +{ + int vid, did; + unsigned int htype; + pci_addr addr; + pci_config_t config = {}; + const pci_dev_t *pci_dev; + uint8_t class, subclass, iface; + int num_bars, rom_bar; + + phandle_t phandle = 0; + int is_host_bridge = 0; + + if (!ob_pci_read_identification(bus, devnum, fn, &vid, &did, &class, &subclass)) { + return; + } + + addr = PCI_ADDR(bus, devnum, fn); + iface = pci_config_read8(addr, PCI_CLASS_PROG); + + pci_dev = pci_find_device(class, subclass, iface, + vid, did); + + PCI_DPRINTF("%x:%x.%x - %x:%x - ", bus, devnum, fn, + vid, did); + + htype = pci_config_read8(addr, PCI_HEADER_TYPE); + + if (fn == 0) { + if (p_is_multi) { + *p_is_multi = htype & 0x80; + } + } + + /* stop adding host bridge accessible from it's primary bus + PCI host bridge is to be added by host code + */ + if (class == PCI_BASE_CLASS_BRIDGE && + subclass == PCI_SUBCLASS_BRIDGE_HOST) { + is_host_bridge = 1; + } + + if (is_host_bridge) { + /* reuse device tree node */ + PCI_DPRINTF("host bridge found - "); + snprintf(config.path, sizeof(config.path), + "%s", parent_path); + } else if (pci_dev == NULL || pci_dev->name == NULL) { + snprintf(config.path, sizeof(config.path), + "%s/pci%x,%x", parent_path, vid, did); + } + else { + snprintf(config.path, sizeof(config.path), + "%s/%s", parent_path, pci_dev->name); + } + + PCI_DPRINTF("%s - ", config.path); + + config.dev = addr & 0x00FFFFFF; + + switch (class) { + case PCI_BASE_CLASS_BRIDGE: + if (subclass != PCI_SUBCLASS_BRIDGE_HOST) { + REGISTER_NAMED_NODE_PHANDLE(ob_pci_bus_node, config.path, phandle); + } + break; + case PCI_CLASS_DISPLAY: + REGISTER_NAMED_NODE_PHANDLE(ob_pci_empty_node, config.path, phandle); + break; + default: + REGISTER_NAMED_NODE_PHANDLE(ob_pci_simple_node, config.path, phandle); + break; + } + + if (is_host_bridge) { + phandle = find_dev(config.path); + + if (get_property(phandle, "vendor-id", NULL)) { + PCI_DPRINTF("host bridge already configured\n"); + return; + } + } + + activate_dev(phandle); + + if (htype & PCI_HEADER_TYPE_BRIDGE) { + num_bars = 2; + rom_bar = PCI_ROM_ADDRESS1; + } else { + num_bars = 6; + rom_bar = PCI_ROM_ADDRESS; + } + + ob_pci_configure(addr, &config, num_bars, rom_bar, + mem_base, io_base); + + ob_pci_add_properties(phandle, addr, pci_dev, &config, num_bars); + + if (!is_host_bridge) { + pci_set_reg(phandle, &config, num_bars); + } + + /* call device-specific configuration callback */ + if (pci_dev && pci_dev->config_cb) { + //activate_device(config.path); + pci_dev->config_cb(&config); + } + + /* device is configured so we may move it out of scope */ + device_end(); + + /* scan bus behind bridge device */ + //if (htype & PCI_HEADER_TYPE_BRIDGE && class == PCI_BASE_CLASS_BRIDGE) { + if ( class == PCI_BASE_CLASS_BRIDGE && + ( subclass == PCI_SUBCLASS_BRIDGE_PCI || + subclass == PCI_SUBCLASS_BRIDGE_HOST ) ) { + + if (subclass == PCI_SUBCLASS_BRIDGE_PCI) { + /* reserve next pci bus number for this PCI bridge */ + ++(*bus_num); + } + + ob_configure_pci_bridge(addr, bus_num, mem_base, io_base, bus, &config); + } +} + +static void ob_pci_set_available(phandle_t host, unsigned long mem_base, unsigned long io_base) +{ + /* Create an available property for both memory and IO space */ + uint32_t props[10]; + int ncells; + + ncells = 0; + ncells += pci_encode_phys_addr(props + ncells, 0, MEMORY_SPACE_32, 0, 0, mem_base); + ncells += pci_encode_size(props + ncells, arch->mem_len - mem_base); + ncells += pci_encode_phys_addr(props + ncells, 0, IO_SPACE, 0, 0, io_base); + ncells += pci_encode_size(props + ncells, arch->io_len - io_base); + + set_property(host, "available", (char *)props, ncells * sizeof(props[0])); +} + +/* Convert device/irq pin to interrupt property */ +#define SUN4U_INTERRUPT(dev, irq_pin) \ + ((((dev >> 11) << 2) + irq_pin - 1) & 0x1f) + +static void ob_pci_host_set_interrupt_map(phandle_t host) +{ + phandle_t dnode = 0; + u32 props[128]; + int i; + +#if defined(CONFIG_PPC) + phandle_t target_node; + + /* Oldworld macs do interrupt maps differently */ + if (!is_newworld()) + return; + + dnode = dt_iterate_type(0, "open-pic"); + if (dnode) { + /* patch in openpic interrupt-parent properties */ + target_node = find_dev("/pci/mac-io"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/escc/ch-a"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/escc/ch-b"); + set_int_property(target_node, "interrupt-parent", dnode); + + /* QEMU only emulates 2 of the 3 ata buses currently */ + /* On a new world Mac these are not numbered but named by the + * ATA version they support. Thus we have: ata-3, ata-3, ata-4 + * On g3beige they all called just ide. + * We take ata-3 and ata-4 which seems to work for both + * at least for clients we care about */ + target_node = find_dev("/pci/mac-io/ata-3"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/ata-4"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/via-cuda"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci"); + set_int_property(target_node, "interrupt-parent", dnode); + + /* openpic interrupt mapping */ + for (i = 0; i < (7*8); i += 7) { + props[i + PCI_INT_MAP_PCI0] = 0; + props[i + PCI_INT_MAP_PCI1] = 0; + props[i + PCI_INT_MAP_PCI2] = 0; + props[i + PCI_INT_MAP_PCI_INT] = (i / 7) + 1; // starts at PINA=1 + props[i + PCI_INT_MAP_PIC_HANDLE] = dnode; + props[i + PCI_INT_MAP_PIC_INT] = arch->irqs[i / 7]; + props[i + PCI_INT_MAP_PIC_POL] = 3; + } + set_property(host, "interrupt-map", (char *)props, 7 * 8 * sizeof(props[0])); + + props[PCI_INT_MAP_PCI0] = 0; + props[PCI_INT_MAP_PCI1] = 0; + props[PCI_INT_MAP_PCI2] = 0; + props[PCI_INT_MAP_PCI_INT] = 0x7; + + set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); + } +#elif defined(CONFIG_SPARC64) + int ncells, len; + u32 *val, addr; + char *reg; + + /* Set interrupt-map for PCI devices with an interrupt pin present */ + ncells = 0; + + PUSH(host); + fword("child"); + dnode = POP(); + while (dnode) { + if (get_int_property(dnode, "interrupts", &len)) { + reg = get_property(dnode, "reg", &len); + if (reg) { + val = (u32 *)reg; + + for (i = 0; i < (len / sizeof(u32)); i += 5) { + addr = val[i]; + + /* Device address is in 1st 32-bit word of encoded PCI address for config space */ + if (!(addr & 0x03000000)) { + ncells += pci_encode_phys_addr(props + ncells, 0, 0, addr, 0, 0); + props[ncells++] = 1; /* always interrupt pin 1 for QEMU */ + props[ncells++] = host; + props[ncells++] = SUN4U_INTERRUPT(addr, 1); + } + } + } + } + + PUSH(dnode); + fword("peer"); + dnode = POP(); + } + set_property(host, "interrupt-map", (char *)props, ncells * sizeof(props[0])); + + props[0] = 0x0000f800; + props[1] = 0x0; + props[2] = 0x0; + props[3] = 7; + set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); +#endif +} + +int ob_pci_init(void) +{ + int bus, devnum, fn; + uint8_t class, subclass; + unsigned long mem_base, io_base; + + pci_config_t config = {}; /* host bridge */ + phandle_t phandle_host = 0; + + PCI_DPRINTF("Initializing PCI host bridge...\n"); + + activate_device("/"); + + /* Find all PCI bridges */ + + mem_base = arch->pci_mem_base; + /* I/O ports under 0x400 are used by devices mapped at fixed + location. */ + io_base = 0x400; + + bus = 0; + + for (devnum = 0; devnum < 32; devnum++) { + /* scan only fn 0 */ + fn = 0; + + if (!ob_pci_read_identification(bus, devnum, fn, + 0, 0, &class, &subclass)) { + continue; + } + + if (class != PCI_BASE_CLASS_BRIDGE || subclass != PCI_SUBCLASS_BRIDGE_HOST) { + continue; + } + + /* create root node for host PCI bridge */ + + /* configure */ + snprintf(config.path, sizeof(config.path), "/pci"); + + REGISTER_NAMED_NODE_PHANDLE(ob_pci_bus_node, config.path, phandle_host); + + pci_host_set_reg(phandle_host); + + /* update device path after changing "reg" property */ + ob_pci_reload_device_path(phandle_host, &config); + + ob_configure_pci_device(config.path, &bus, &mem_base, &io_base, + bus, devnum, fn, 0); + + /* we expect single host PCI bridge + but this may be machine-specific */ + break; + } + + /* create available attributes for the PCI bridge */ + ob_pci_set_available(phandle_host, mem_base, io_base); + + /* configure the host bridge interrupt map */ + ob_pci_host_set_interrupt_map(phandle_host); + + device_end(); + + return 0; +} diff --git a/qemu/roms/openbios/drivers/pci.fs b/qemu/roms/openbios/drivers/pci.fs new file mode 100644 index 000000000..563b652a4 --- /dev/null +++ b/qemu/roms/openbios/drivers/pci.fs @@ -0,0 +1,92 @@ +[IFDEF] CONFIG_DRIVER_PCI + +: pci-addr-encode ( addr.lo addr.mi addr.hi ) + rot >r swap >r + encode-int + r> encode-int encode+ + r> encode-int encode+ + ; + +: pci-len-encode ( len.lo len.hi ) + encode-int + rot encode-int encode+ + ; + +\ Get region offset for BAR reg +: pci-bar-offset@ ( bar-reg -- off.lo off.hi -1 | 0 ) + " reg" active-package get-package-property 0= if + begin + decode-phys \ ( reg prop prop-len phys.lo phys.mid phys.hi ) + ff and 5 pick = if + >r >r 3drop r> r> + -1 exit + else + 2drop + then + \ Drop the size as we don't need it + decode-int drop decode-int drop + dup 0= + until + 3drop + 0 exit + else + 0 + then + ; + +\ Get region size for BAR reg +: pci-bar-size@ ( bar-reg -- size ) + " reg" active-package get-package-property 0= if + begin + decode-phys \ ( reg prop prop-len phys.lo phys.mid phys.hi ) + ff and 5 pick = if + 2drop decode-int drop + decode-int + >r 3drop r> + exit + else + 2drop decode-int drop + decode-int drop + then + dup 0= + until + 3drop + 0 \ default size of 0 if BAR not found + then + ; + +\ Get base address for configured BAR reg +: pci-bar-base@ ( bar-reg -- addr.lo addr.hi -1 | 0 ) + " assigned-addresses" active-package get-package-property 0= if + begin + decode-phys \ ( reg prop prop-len phys.lo phys.mid phys.hi ) + ff and 5 pick = if + >r >r 3drop r> r> + -1 exit + else + 2drop + then + \ Drop the size as we don't need it + decode-int drop decode-int drop + dup 0= + until + 3drop + 0 exit + else + 0 + then + ; + +\ Get PCI bus address and size for configured BAR reg +: pci-bar>pci-region ( bar-reg -- addr.lo addr.hi size ) + dup + >r pci-bar-offset@ if + swap r@ pci-bar-base@ if + swap d+ + then + swap r@ pci-bar-size@ + then + r> drop + ; + +[THEN] diff --git a/qemu/roms/openbios/drivers/pci.h b/qemu/roms/openbios/drivers/pci.h new file mode 100644 index 000000000..84a2b2cf6 --- /dev/null +++ b/qemu/roms/openbios/drivers/pci.h @@ -0,0 +1,87 @@ +#ifndef PCI_H +#define PCI_H + +#define PCI_VENDOR_ID 0x00 +#define PCI_DEVICE_ID 0x02 + +#define PCI_COMMAND 0x04 +#define PCI_COMMAND_IO 0x01 +#define PCI_COMMAND_MEMORY 0x02 +#define PCI_COMMAND_BUS_MASTER 0x04 + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features + [obsolete] */ +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + + +#define PCI_REVISION_ID 0x08 /* Revision ID */ +#define PCI_CLASS_DISPLAY 0x03 +#define PCI_CLASS_PROG 0x09 +#define PCI_CLASS_DEVICE 0x0a +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e +#define PCI_HEADER_TYPE_NORMAL 0x00 +#define PCI_HEADER_TYPE_BRIDGE 0x01 +#define PCI_HEADER_TYPE_CARDBUS 0x02 +#define PCI_PRIMARY_BUS 0x18 +#define PCI_SECONDARY_BUS 0x19 +#define PCI_SUBORDINATE_BUS 0x1A +#define PCI_BASE_ADDR_0 0x10 +#define PCI_BASE_ADDR_1 0x14 +#define PCI_BASE_ADDR_2 0x18 +#define PCI_BASE_ADDR_3 0x1c +#define PCI_BASE_ADDR_4 0x20 +#define PCI_BASE_ADDR_5 0x24 + +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e + +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) +#define PCI_ROM_ADDRESS1 0x38 /* ROM_ADDRESS in bridge header */ + +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +typedef struct { + u16 signature; + u8 reserved[0x16]; + u16 dptr; +} rom_header_t; + +typedef struct { + u32 signature; + u16 vendor; + u16 device; + u16 reserved_1; + u16 dlen; + u8 drevision; + u8 class_hi; + u16 class_lo; + u16 ilen; + u16 irevision; + u8 type; + u8 indicator; + u16 reserved_2; +} pci_data_t; + + +#include "asm/pci.h" + +#endif /* PCI_H */ diff --git a/qemu/roms/openbios/drivers/pci_database.c b/qemu/roms/openbios/drivers/pci_database.c new file mode 100644 index 000000000..0070a78ba --- /dev/null +++ b/qemu/roms/openbios/drivers/pci_database.c @@ -0,0 +1,1275 @@ +#include "config.h" +#include "libopenbios/bindings.h" +#include "drivers/pci.h" +#include "libc/vsprintf.h" + +#include "pci_database.h" + +/* PCI devices database */ + +typedef struct pci_class_t pci_class_t; +typedef struct pci_subclass_t pci_subclass_t; +typedef struct pci_iface_t pci_iface_t; + +struct pci_iface_t { + uint8_t iface; + const char *name; + const char *type; + const pci_dev_t *devices; + int (*config_cb)(const pci_config_t *config); + const void *private; +}; + +struct pci_subclass_t { + uint8_t subclass; + const char *name; + const char *type; + const pci_dev_t *devices; + const pci_iface_t *iface; + int (*config_cb)(const pci_config_t *config); + const void *private; +}; + +struct pci_class_t { + const char *name; + const char *type; + const pci_subclass_t *subc; +}; + +/* Current machine description */ + +static const pci_subclass_t undef_subclass[] = { + { + 0xFF, NULL, NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_dev_t scsi_devices[] = { + { + /* Virtio-block controller */ + PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_DEVICE_ID_VIRTIO_BLOCK, + NULL, "virtio-blk", NULL, + "pci1af4,1001\0pci1af4,1001\0pciclass,01018f\0", + 0, 0, 0, + NULL, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const pci_dev_t ide_devices[] = { + { + PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, /* CMD646 IDE controller */ + "pci-ide", "pci-ata", NULL, + "pci1095,646\0pci1095,646\0pciclass,01018f\0", + 0, 0, 0, + ide_config_cb2, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const pci_subclass_t mass_subclass[] = { + { + PCI_SUBCLASS_STORAGE_SCSI, "SCSI bus controller", + "scsi", scsi_devices, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_STORAGE_IDE, "IDE controller", + "ide", ide_devices, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_STORAGE_FLOPPY, "Floppy disk controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_STORAGE_IPI, "IPI bus controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_STORAGE_RAID, "RAID controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_STORAGE_ATA, "ATA controller", + "ata", NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_STORAGE_OTHER, "misc mass-storage controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_dev_t eth_devices[] = { + { + PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_RTL8029, + NULL, "NE2000", "NE2000 PCI", NULL, + 0, 0, 0, + NULL, "ethernet", + }, + { + /* Virtio-network controller */ + PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_DEVICE_ID_VIRTIO_NET, + NULL, "virtio-net", NULL, + "pci1af4,1000\0pci1af4,1000\0pciclass,020000\0", + 0, 0, 0, + NULL, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const pci_subclass_t net_subclass[] = { + { + PCI_SUBCLASS_NETWORK_ETHERNET, "ethernet controller", + NULL, eth_devices, NULL, + eth_config_cb, "ethernet", + }, + { + PCI_SUBCLASS_NETWORK_TOKEN_RING, "token ring controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_NETWORK_FDDI, "FDDI controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_NETWORK_ATM, "ATM controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_NETWORK_ISDN, "ISDN controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_NETWORK_WORDFIP, "WordFip controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_NETWORK_PICMG214, "PICMG 2.14 controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_NETWORK_OTHER, "misc network controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_dev_t vga_devices[] = { + { + PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF, + NULL, "ATY", "ATY Rage128", "VGA\0", + 0, 0, 0, + NULL, NULL, + }, + { + PCI_VENDOR_ID_QEMU, PCI_DEVICE_ID_QEMU_VGA, + NULL, "QEMU,VGA", "Qemu VGA", "VGA\0", + 0, 0, 0, + NULL, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const struct pci_iface_t vga_iface[] = { + { + 0x00, "VGA controller", NULL, + vga_devices, &vga_config_cb, NULL, + }, + { + 0x01, "8514 compatible controller", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_subclass_t displ_subclass[] = { + { + PCI_SUBCLASS_DISPLAY_VGA, "display controller", + NULL, NULL, vga_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_DISPLAY_XGA, "XGA display controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_DISPLAY_3D, "3D display controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_DISPLAY_OTHER, "misc display controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_subclass_t media_subclass[] = { + { + PCI_SUBCLASS_MULTIMEDIA_VIDEO, "video device", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_MULTIMEDIA_AUDIO, "audio device", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_MULTIMEDIA_PHONE, "computer telephony device", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_MULTIMEDIA_OTHER, "misc multimedia device", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_subclass_t mem_subclass[] = { + { + PCI_SUBCLASS_MEMORY_RAM, "RAM controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_MEMORY_FLASH, "flash controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + + +static const pci_dev_t hbrg_devices[] = { + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_U3_AGP, NULL, + "pci", "AAPL,UniNorth", "u3-agp\0", + 3, 2, 1, + host_config_cb, NULL, + }, + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_AGP, NULL, + "pci", "AAPL,UniNorth", "uni-north\0", + 3, 2, 1, + host_config_cb, NULL, + }, + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_PCI, NULL, + "pci", "AAPL,UniNorth", "uni-north\0", + 3, 2, 1, + host_config_cb, NULL, + }, + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_I_PCI, NULL, + "pci", "AAPL,UniNorth", "uni-north\0", + 3, 2, 1, + NULL, NULL + }, + { + PCI_VENDOR_ID_MOTOROLA, PCI_DEVICE_ID_MOTOROLA_MPC106, "pci", + "pci", "MOT,MPC106", "grackle\0", + 3, 2, 1, + host_config_cb, NULL + }, + { + PCI_VENDOR_ID_MOTOROLA, PCI_DEVICE_ID_MOTOROLA_RAVEN, NULL, + "pci", "PREP Host PCI Bridge - Motorola Raven", NULL, + 3, 2, 1, + host_config_cb, NULL, + }, + { + PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SABRE, NULL, + "pci", "SUNW,sabre", "pci108e,a000\0pciclass,0\0", + 3, 2, 1, + sabre_config_cb, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const pci_dev_t PCIbrg_devices[] = { + { + PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21154, NULL, + "pci-bridge", "DEV,21154", "DEV,21154\0pci-bridge\0", + 3, 2, 1, + bridge_config_cb, NULL, + }, + { + PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, NULL, + "pci", "SUNW,simba", "pci108e,5000\0pciclass,060400\0", + 3, 2, 1, + bridge_config_cb, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const pci_dev_t miscbrg_devices[] = { + { + PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, NULL, + "ebus", "ebus", "pci108e,1000\0pciclass,068000\0", + 2, 1, 1, + ebus_config_cb, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const pci_dev_t isabrg_devices[] = { + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82378, NULL, + "isa", "isa", "pci8086,484\0", + 1, 1, 1, + i82378_config_cb, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const pci_subclass_t bridg_subclass[] = { + { + PCI_SUBCLASS_BRIDGE_HOST, "PCI host bridge", + "pci", hbrg_devices, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_ISA, "ISA bridge", + "isa", isabrg_devices, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_EISA, "EISA bridge", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_MC, "MCA bridge", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_PCI, "PCI-to-PCI bridge", + "pci", PCIbrg_devices, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_PCMCIA, "PCMCIA bridge", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_NUBUS, "NUBUS bridge", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_CARDBUS, "cardbus bridge", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_RACEWAY, "raceway bridge", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_PCI_SEMITP, "semi-transparent PCI-to-PCI bridge", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_IB_PCI, "infiniband-to-PCI bridge", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_BRIDGE_OTHER, "misc PCI bridge", + NULL, miscbrg_devices, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_iface_t serial_iface[] = { + { + 0x00, "XT serial controller", NULL, + NULL, NULL, NULL, + }, + { + 0x01, "16450 serial controller", NULL, + NULL, NULL, NULL, + }, + { + 0x02, "16550 serial controller", NULL, + NULL, NULL, NULL, + }, + { + 0x03, "16650 serial controller", NULL, + NULL, NULL, NULL, + }, + { + 0x04, "16750 serial controller", NULL, + NULL, NULL, NULL, + }, + { + 0x05, "16850 serial controller", NULL, + NULL, NULL, NULL, + }, + { + 0x06, "16950 serial controller", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_iface_t par_iface[] = { + { + 0x00, "parallel port", NULL, + NULL, NULL, NULL, + }, + { + 0x01, "bi-directional parallel port", NULL, + NULL, NULL, NULL, + }, + { + 0x02, "ECP 1.x parallel port", NULL, + NULL, NULL, NULL, + }, + { + 0x03, "IEEE 1284 controller", NULL, + NULL, NULL, NULL, + }, + { + 0xFE, "IEEE 1284 device", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_iface_t modem_iface[] = { + { + 0x00, "generic modem", NULL, + NULL, NULL, NULL, + }, + { + 0x01, "Hayes 16450 modem", NULL, + NULL, NULL, NULL, + }, + { + 0x02, "Hayes 16550 modem", NULL, + NULL, NULL, NULL, + }, + { + 0x03, "Hayes 16650 modem", NULL, + NULL, NULL, NULL, + }, + { + 0x04, "Hayes 16750 modem", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_subclass_t comm_subclass[] = { + { + PCI_SUBCLASS_COMMUNICATION_SERIAL, "serial controller", + NULL, NULL, serial_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_COMMUNICATION_PARALLEL, "parallel port", + NULL, NULL, par_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_COMMUNICATION_MULTISERIAL, "multiport serial controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_COMMUNICATION_MODEM, "modem", + NULL, NULL, modem_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_COMMUNICATION_GPIB, "GPIB controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_COMMUNICATION_SC, "smart card", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_COMMUNICATION_OTHER, "misc communication device", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_iface_t pic_iface[] = { + { + 0x00, "8259 PIC", NULL, + NULL, NULL, NULL, + }, + { + 0x01, "ISA PIC", NULL, + NULL, NULL, NULL, + }, + { + 0x02, "EISA PIC", NULL, + NULL, NULL, NULL, + }, + { + 0x10, "I/O APIC", NULL, + NULL, NULL, NULL, + }, + { + 0x20, "I/O APIC", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_iface_t dma_iface[] = { + { + 0x00, "8237 DMA controller", NULL, + NULL, NULL, NULL, + }, + { + 0x01, "ISA DMA controller", NULL, + NULL, NULL, NULL, + }, + { + 0x02, "EISA DMA controller", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_iface_t tmr_iface[] = { + { + 0x00, "8254 system timer", NULL, + NULL, NULL, NULL, + }, + { + 0x01, "ISA system timer", NULL, + NULL, NULL, NULL, + }, + { + 0x02, "EISA system timer", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_iface_t rtc_iface[] = { + { + 0x00, "generic RTC controller", NULL, + NULL, NULL, NULL, + }, + { + 0x01, "ISA RTC controller", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_dev_t sys_devices[] = { + /* IBM MPIC controller */ + { + PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OPENPIC, + "open-pic", "MPIC", NULL, "chrp,open-pic\0", + 0, 0, 2, + NULL, NULL, + }, + /* IBM MPIC2 controller */ + { + PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OPENPIC2, + "open-pic", "MPIC2", NULL, "chrp,open-pic\0", + 0, 0, 2, + NULL, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const pci_subclass_t sys_subclass[] = { + { + PCI_SUBCLASS_SYSTEM_PIC, "PIC", + NULL, NULL, pic_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_SYSTEM_DMA, "DMA controller", + NULL, NULL, dma_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_SYSTEM_TIMER, "system timer", + NULL, NULL, tmr_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_SYSTEM_RTC, "RTC controller", + NULL, NULL, rtc_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_SYSTEM_PCI_HOTPLUG, "PCI hotplug controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SYSTEM_OTHER, "misc system peripheral", + NULL, sys_devices, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_subclass_t inp_subclass[] = { + { + PCI_SUBCLASS_INPUT_KEYBOARD, "keyboard controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_INPUT_PEN, "digitizer", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_INPUT_MOUSE, "mouse controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_INPUT_SCANNER, "scanner controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_INPUT_GAMEPORT, "gameport controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_INPUT_OTHER, "misc input device", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_subclass_t dock_subclass[] = { + { + PCI_SUBCLASS_DOCKING_GENERIC, "generic docking station", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_DOCKING_OTHER, "misc docking station", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_subclass_t cpu_subclass[] = { + { + PCI_SUBCLASS_PROCESSOR_386, "i386 processor", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_PROCESSOR_486, "i486 processor", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_PROCESSOR_PENTIUM, "pentium processor", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_PROCESSOR_ALPHA, "alpha processor", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_PROCESSOR_POWERPC, "PowerPC processor", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_PROCESSOR_MIPS, "MIPS processor", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_PROCESSOR_CO, "co-processor", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_dev_t usb_devices[] = { +#if defined(CONFIG_QEMU) + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_KEYL_USB, + "usb", "usb", NULL, + "pci106b,3f\0pciclass,0c0310\0", + 1, 0, 0, + NULL, NULL, + }, +#endif + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +static const pci_iface_t usb_iface[] = { + { + 0x00, "UHCI USB controller", NULL, + NULL, NULL, NULL, + }, + { + 0x10, "OHCI USB controller", NULL, + usb_devices, &usb_ohci_config_cb, NULL, + }, + { + 0x20, "EHCI USB controller", NULL, + NULL, NULL, NULL, + }, + { + 0x80, "misc USB controller", NULL, + NULL, NULL, NULL, + }, + { + 0xFE, "USB device", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_iface_t ipmi_iface[] = { + { + 0x00, "IPMI SMIC interface", NULL, + NULL, NULL, NULL, + }, + { + 0x01, "IPMI keyboard interface", NULL, + NULL, NULL, NULL, + }, + { + 0x02, "IPMI block transfer interface", NULL, + NULL, NULL, NULL, + }, + { + 0xFF, NULL, NULL, + NULL, NULL, NULL, + }, +}; + +static const pci_subclass_t ser_subclass[] = { + { + PCI_SUBCLASS_SERIAL_FIREWIRE, "Firewire bus controller", + "ieee1394", NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SERIAL_ACCESS, "ACCESS bus controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SERIAL_SSA, "SSA controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SERIAL_USB, "USB controller", + "usb", NULL, usb_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_SERIAL_FIBER, "fibre channel controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SERIAL_SMBUS, "SMBus controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SERIAL_IB, "InfiniBand controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SERIAL_IPMI, "IPMI interface", + NULL, NULL, ipmi_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_SERIAL_SERCOS, "SERCOS controller", + NULL, NULL, ipmi_iface, + NULL, NULL, + }, + { + PCI_SUBCLASS_SERIAL_CANBUS, "CANbus controller", + NULL, NULL, ipmi_iface, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_subclass_t wrl_subclass[] = { + { + PCI_SUBCLASS_WIRELESS_IRDA, "IRDA controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_WIRELESS_CIR, "consumer IR controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_WIRELESS_RF_CONTROLLER, "RF controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_WIRELESS_BLUETOOTH, "bluetooth controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_WIRELESS_BROADBAND, "broadband controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_WIRELESS_OTHER, "misc wireless controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_subclass_t sat_subclass[] = { + { + PCI_SUBCLASS_SATELLITE_TV, "satellite TV controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SATELLITE_AUDIO, "satellite audio controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SATELLITE_VOICE, "satellite voice controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SATELLITE_DATA, "satellite data controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_subclass_t crypt_subclass[] = { + { + PCI_SUBCLASS_CRYPT_NETWORK, "cryptographic network controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_CRYPT_ENTERTAINMENT, + "cryptographic entertainment controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_CRYPT_OTHER, "misc cryptographic controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_subclass_t spc_subclass[] = { + { + PCI_SUBCLASS_SP_DPIO, "DPIO module", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SP_PERF, "performances counters", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SP_SYNCH, "communication synchronisation", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SP_MANAGEMENT, "management card", + NULL, NULL, NULL, + NULL, NULL, + }, + { + PCI_SUBCLASS_SP_OTHER, "misc signal processing controller", + NULL, NULL, NULL, + NULL, NULL, + }, + { + 0xFF, NULL, + NULL, NULL, NULL, + NULL, NULL, + }, +}; + +static const pci_class_t pci_classes[] = { + /* 0x00 */ + { "undefined", NULL, undef_subclass, }, + /* 0x01 */ + { "mass-storage controller", NULL, mass_subclass, }, + /* 0x02 */ + { "network controller", "network", net_subclass, }, + /* 0x03 */ + { "display controller", "display", displ_subclass, }, + /* 0x04 */ + { "multimedia device", NULL, media_subclass, }, + /* 0x05 */ + { "memory controller", "memory-controller", mem_subclass, }, + /* 0x06 */ + { "PCI bridge", NULL, bridg_subclass, }, + /* 0x07 */ + { "communication device", NULL, comm_subclass,}, + /* 0x08 */ + { "system peripheral", NULL, sys_subclass, }, + /* 0x09 */ + { "input device", NULL, inp_subclass, }, + /* 0x0A */ + { "docking station", NULL, dock_subclass, }, + /* 0x0B */ + { "processor", NULL, cpu_subclass, }, + /* 0x0C */ + { "serial bus controller", NULL, ser_subclass, }, + /* 0x0D */ + { "wireless controller", NULL, wrl_subclass, }, + /* 0x0E */ + { "intelligent I/O controller", NULL, NULL, }, + /* 0x0F */ + { "satellite communication controller", NULL, sat_subclass, }, + /* 0x10 */ + { "cryptographic controller", NULL, crypt_subclass, }, + /* 0x11 */ + { "signal processing controller", NULL, spc_subclass, }, +}; + +static const pci_dev_t misc_pci[] = { + /* Heathrow Mac I/O */ + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_343S1201, + "mac-io", "mac-io", "AAPL,343S1201", "heathrow\0", + 1, 1, 1, + &macio_heathrow_config_cb, NULL, + }, + /* Paddington Mac I/O */ + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_343S1211, + "mac-io", "mac-io", "AAPL,343S1211", "paddington\0heathrow\0", + 1, 1, 1, + &macio_heathrow_config_cb, NULL, + }, + /* KeyLargo Mac I/O */ + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_KEYL, + "mac-io", "mac-io", "AAPL,Keylargo", "Keylargo\0", + 1, 1, 1, + &macio_keylargo_config_cb, NULL, + }, + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + +const pci_dev_t *pci_find_device (uint8_t class, uint8_t subclass, + uint8_t iface, uint16_t vendor, + uint16_t product) +{ + int (*config_cb)(const pci_config_t *config); + const pci_class_t *pclass; + const pci_subclass_t *psubclass; + const pci_iface_t *piface; + const pci_dev_t *dev; + const void *private; + pci_dev_t *new; + const char *name, *type; + + name = "unknown"; + type = "unknown"; + config_cb = NULL; + private = NULL; + + if (class == 0x00 && subclass == 0x01) { + /* Special hack for old style VGA devices */ + class = 0x03; + subclass = 0x00; + } else if (class == 0xFF) { + /* Special case for misc devices */ + dev = misc_pci; + goto find_device; + } + if (class > (sizeof(pci_classes) / sizeof(pci_class_t))) { + name = "invalid PCI device"; + type = "invalid"; + goto bad_device; + } + pclass = &pci_classes[class]; + name = pclass->name; + type = pclass->type; + for (psubclass = pclass->subc; ; psubclass++) { + if (psubclass->subclass == 0xFF) + goto bad_device; + if (psubclass->subclass == subclass) { + if (psubclass->name != NULL) + name = psubclass->name; + if (psubclass->type != NULL) + type = psubclass->type; + if (psubclass->config_cb != NULL) { + config_cb = psubclass->config_cb; + } + if (psubclass->private != NULL) + private = psubclass->private; + if (psubclass->iface != NULL) + break; + dev = psubclass->devices; + goto find_device; + } + } + for (piface = psubclass->iface; ; piface++) { + if (piface->iface == 0xFF) { + dev = psubclass->devices; + break; + } + if (piface->iface == iface) { + if (piface->name != NULL) + name = piface->name; + if (piface->type != NULL) + type = piface->type; + if (piface->config_cb != NULL) { + config_cb = piface->config_cb; + } + if (piface->private != NULL) + private = piface->private; + dev = piface->devices; + break; + } + } +find_device: + if (dev == NULL) + goto bad_device; + for (;; dev++) { + if (dev->vendor == 0xFFFF && dev->product == 0xFFFF) { + goto bad_device; + } + if (dev->vendor == vendor && dev->product == product) { + if (dev->name != NULL) + name = dev->name; + if (dev->type != NULL) + type = dev->type; + if (dev->config_cb != NULL) { + config_cb = dev->config_cb; + } + if (dev->private != NULL) + private = dev->private; + new = malloc(sizeof(pci_dev_t)); + if (new == NULL) + return NULL; + new->vendor = vendor; + new->product = product; + new->type = type; + new->name = name; + new->model = dev->model; + new->compat = dev->compat; + new->acells = dev->acells; + new->scells = dev->scells; + new->icells = dev->icells; + new->config_cb = config_cb; + new->private = private; + + return new; + } + } +bad_device: + printk("Cannot manage '%s' PCI device type '%s':\n %x %x (%x %x %x)\n", + name, type, vendor, product, class, subclass, iface); + + return NULL; +} diff --git a/qemu/roms/openbios/drivers/pci_database.h b/qemu/roms/openbios/drivers/pci_database.h new file mode 100644 index 000000000..7f053d808 --- /dev/null +++ b/qemu/roms/openbios/drivers/pci_database.h @@ -0,0 +1,57 @@ +typedef struct pci_config_t pci_config_t; + +struct pci_config_t { + char path[256]; + uint32_t dev; /* bus, dev, fn */ + uint32_t regions[7]; + uint32_t assigned[7]; + uint32_t sizes[7]; + int irq_pin; + int irq_line; + u32 primary_bus; + u32 secondary_bus; + u32 subordinate_bus; +}; + +typedef struct pci_dev_t pci_dev_t; +struct pci_dev_t { + uint16_t vendor; + uint16_t product; + const char *type; + const char *name; + const char *model; + const char *compat; + int acells; + int scells; + int icells; + int (*config_cb)(const pci_config_t *config); + const void *private; +}; + +extern int ide_config_cb2(const pci_config_t *config); +extern int eth_config_cb(const pci_config_t *config); +extern int macio_heathrow_config_cb(const pci_config_t *config); +extern int macio_keylargo_config_cb(const pci_config_t *config); +extern int vga_config_cb(const pci_config_t *config); +extern int host_config_cb(const pci_config_t *config); +extern int sabre_config_cb(const pci_config_t *config); +extern int bridge_config_cb(const pci_config_t *config); +extern int ebus_config_cb(const pci_config_t *config); +extern int i82378_config_cb(const pci_config_t *config); +extern int usb_ohci_config_cb(const pci_config_t *config); + +static inline int pci_compat_len(const pci_dev_t *dev) +{ + int len, ret; + const char *path = dev->compat; + ret = 0; + while ((len = strlen(path)) != 0) { + ret += len + 1; + path += len + 1; + } + return ret; +} + +extern const pci_dev_t *pci_find_device(uint8_t class, uint8_t subclass, + uint8_t iface, uint16_t vendor, + uint16_t product); diff --git a/qemu/roms/openbios/drivers/sbus.c b/qemu/roms/openbios/drivers/sbus.c new file mode 100644 index 000000000..a9b26c0a0 --- /dev/null +++ b/qemu/roms/openbios/drivers/sbus.c @@ -0,0 +1,523 @@ +/* + * OpenBIOS SBus driver + * + * (C) 2004 Stefan Reinauer <stepan@openbios.org> + * (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 "libopenbios/ofmem.h" +#include "libopenbios/video.h" + +#define SBUS_REGS 0x28 +#define SBUS_SLOTS 16 +#define APC_REGS 0x10 +#define APC_OFFSET 0x0a000000ULL +#define CS4231_REGS 0x40 +#define CS4231_OFFSET 0x0c000000ULL +#define MACIO_ESPDMA 0x00400000ULL /* ESP DMA controller */ +#define MACIO_ESP 0x00800000ULL /* ESP SCSI */ +#define SS600MP_ESPDMA 0x00081000ULL +#define SS600MP_ESP 0x00080000ULL +#define SS600MP_LEBUFFER (SS600MP_ESPDMA + 0x10) // XXX should be 0x40000 +#define LEDMA_REGS 0x4 +#define LE_REGS 0x20 + +#ifdef CONFIG_DEBUG_SBUS +#define DPRINTF(fmt, args...) \ + do { printk(fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +typedef struct le_private { + uint32_t *dmaregs; + uint32_t *regs; +} le_private_t; + +static void +ob_sbus_node_init(uint64_t base) +{ + void *regs; + + push_str("/iommu/sbus"); + fword("find-device"); + + PUSH(base >> 32); + fword("encode-int"); + PUSH(base & 0xffffffff); + fword("encode-int"); + fword("encode+"); + PUSH(SBUS_REGS); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + regs = (void *)ofmem_map_io(base, SBUS_REGS); + PUSH((unsigned long)regs); + fword("encode-int"); + push_str("address"); + fword("property"); +} + +static void +ob_le_init(unsigned int slot, uint64_t base, unsigned long leoffset, unsigned long dmaoffset) +{ + le_private_t *le; + + le = malloc(sizeof(le_private_t)); + if (!le) { + DPRINTF("Can't allocate LANCE private structure\n"); + return; + } + + /* Get the IO region for DMA registers */ + le->dmaregs = (void *)ofmem_map_io(base + (uint64_t)dmaoffset, LEDMA_REGS); + if (le->dmaregs == NULL) { + DPRINTF("Can't map LANCE DMA registers\n"); + return; + } + + /* Now it appears that the Solaris kernel forgets to set up the LANCE DMA mapping + and so it must inherit the one from OpenBIOS. The symptom of this is that the + LANCE DMA base addr register is still zero, and so we start sending network + packets containing random areas of memory. + + The correct fix for this should be to use dvma_alloc() to grab a section of + memory and point the LANCE DMA buffers to use that instead; this gets + slightly further but still crashes. Time-consuming investigation on various + hacked versions of QEMU seems to indicate that Solaris always assumes the LANCE + DMA base address is fixed 0xff000000 when setting up the IOMMU for the LANCE + card. Hence we imitate this behaviour here. */ + le->dmaregs[3] = 0xff000000; + + push_str("/iommu/sbus/ledma"); + fword("find-device"); + PUSH(slot); + fword("encode-int"); + PUSH(dmaoffset); + fword("encode-int"); + fword("encode+"); + PUSH(0x00000020); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + /* Get the IO region for Lance registers */ + le->regs = (void *)ofmem_map_io(base + (uint64_t)leoffset, LE_REGS); + if (le->regs == NULL) { + DPRINTF("Can't map LANCE registers\n"); + return; + } + + push_str("/iommu/sbus/ledma/le"); + fword("find-device"); + PUSH(slot); + fword("encode-int"); + PUSH(leoffset); + fword("encode-int"); + fword("encode+"); + PUSH(0x00000004); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); +} + +uint16_t graphic_depth; + +static void +ob_tcx_init(unsigned int slot, const char *path) +{ + char buf[6]; + + printk("No display device located during SBus probe - falling back to internal TCX driver\n"); + + /* Make the sbus node the current instance and active package for probing */ + feval("active-package my-self"); + push_str("/iommu/sbus"); + feval("2dup find-device open-dev to my-self"); + + fword("new-device"); + PUSH(0); + PUSH(0); + snprintf(buf, 6, "%x,0", slot); + push_str(buf); + fword("set-args"); + feval("['] tcx-driver-fcode 2 cells + 1 byte-load"); + fword("finish-device"); + + /* Restore */ + feval("to my-self active-package!"); +} + +static void +ob_apc_init(unsigned int slot, unsigned long base) +{ + push_str("/iommu/sbus"); + fword("find-device"); + fword("new-device"); + + push_str("power-management"); + fword("device-name"); + + PUSH(slot); + fword("encode-int"); + PUSH(base); + fword("encode-int"); + fword("encode+"); + PUSH(APC_REGS); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + fword("finish-device"); +} + +static void +ob_cs4231_init(unsigned int slot) +{ + push_str("/iommu/sbus"); + fword("find-device"); + fword("new-device"); + + push_str("SUNW,CS4231"); + fword("device-name"); + + push_str("serial"); + fword("device-type"); + + PUSH(slot); + fword("encode-int"); + PUSH(CS4231_OFFSET); + fword("encode-int"); + fword("encode+"); + PUSH(CS4231_REGS); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + PUSH(5); + fword("encode-int"); + PUSH(0); + fword("encode-int"); + fword("encode+"); + push_str("intr"); + fword("property"); + + PUSH(5); + fword("encode-int"); + push_str("interrupts"); + fword("property"); + + push_str("audio"); + fword("encode-string"); + push_str("alias"); + fword("property"); + + fword("finish-device"); +} + +static void +ob_macio_init(unsigned int slot, uint64_t base, unsigned long offset) +{ + // All devices were integrated to NCR89C100, see + // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt + + // NCR 53c9x, aka ESP. See + // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt +#ifdef CONFIG_DRIVER_ESP + ob_esp_init(slot, base, offset + MACIO_ESP, offset + MACIO_ESPDMA); +#endif + + // NCR 92C990, Am7990, Lance. See http://www.amd.com + ob_le_init(slot, base, offset + 0x00c00000, offset + 0x00400010); + + // Parallel port + //ob_bpp_init(base); +} + +static void +sbus_probe_self(unsigned int slot, unsigned long offset) +{ + /* Wrapper for calling probe-self in Forth. This is mainly because some + drivers don't handle properties correctly when the sbus node is set + as the current instance during probe. */ + char buf[6]; + + printk("Probing SBus slot %d offset %ld\n", slot, offset); + + /* Make the sbus node the current instance and active package for probing */ + feval("active-package my-self"); + push_str("/iommu/sbus"); + feval("open-dev to my-self"); + + PUSH(0); + PUSH(0); + snprintf(buf, 6, "%x,%lx", slot, offset); + push_str(buf); + fword("2dup"); + fword("probe-self-sbus"); + + /* Restore */ + feval("to my-self active-package!"); +} + +static int +sbus_probe_sucess(void) +{ + /* Return true if the last sbus_probe_self() resulted in + the successful detection and execution of FCode */ + fword("probe-fcode?"); + return POP(); +} + +static void +sbus_probe_slot_ss5(unsigned int slot, uint64_t base) +{ + /* Probe the slot */ + sbus_probe_self(slot, 0); + + /* If the device was successfully created by FCode then do nothing */ + if (sbus_probe_sucess()) { + return; + } + + switch(slot) { + case 3: // SUNW,tcx + ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx"); + break; + case 4: + // SUNW,CS4231 + ob_cs4231_init(slot); + // Power management (APC) + ob_apc_init(slot, APC_OFFSET); + break; + case 5: // MACIO: le, esp, bpp + ob_macio_init(slot, base, 0x08000000); + break; + default: + break; + } +} + +static void +sbus_probe_slot_ss10(unsigned int slot, uint64_t base) +{ + /* Probe the slot */ + sbus_probe_self(slot, 0); + + /* If the device was successfully created by FCode then do nothing */ + if (sbus_probe_sucess()) { + return; + } + + switch(slot) { + case 2: // SUNW,tcx + ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx"); + break; + case 0xf: // le, esp, bpp, power-management + ob_macio_init(slot, base, 0); + // Power management (APC) XXX should not exist + ob_apc_init(slot, APC_OFFSET); + break; + default: + break; + } +} + +static void +sbus_probe_slot_ss600mp(unsigned int slot, uint64_t base) +{ + /* Probe the slot */ + sbus_probe_self(slot, 0); + + /* If the device was successfully created by FCode then do nothing */ + if (sbus_probe_sucess()) { + return; + } + + switch(slot) { + case 2: // SUNW,tcx + ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx"); + break; + case 0xf: // le, esp, bpp, power-management +#ifdef CONFIG_DRIVER_ESP + ob_esp_init(slot, base, SS600MP_ESP, SS600MP_ESPDMA); +#endif + // NCR 92C990, Am7990, Lance. See http://www.amd.com + ob_le_init(slot, base, 0x00060000, SS600MP_LEBUFFER); + // Power management (APC) XXX should not exist + ob_apc_init(slot, APC_OFFSET); + break; + default: + break; + } +} + +static void +ob_sbus_open(void) +{ + int ret=1; + RET ( -ret ); +} + +static void +ob_sbus_close(void) +{ + selfword("close-deblocker"); +} + +static void +ob_sbus_initialize(void) +{ +} + + +NODE_METHODS(ob_sbus_node) = { + { NULL, ob_sbus_initialize }, + { "open", ob_sbus_open }, + { "close", ob_sbus_close }, +}; + +struct sbus_offset { + int slot, type; + uint64_t base; + unsigned long size; +}; + +static const struct sbus_offset sbus_offsets_ss5[SBUS_SLOTS] = { + { 0, 0, 0x20000000, 0x10000000,}, + { 1, 0, 0x30000000, 0x10000000,}, + { 2, 0, 0x40000000, 0x10000000,}, + { 3, 0, 0x50000000, 0x10000000,}, + { 4, 0, 0x60000000, 0x10000000,}, + { 5, 0, 0x70000000, 0x10000000,}, +}; + +/* Shared with ss600mp */ +static const struct sbus_offset sbus_offsets_ss10[SBUS_SLOTS] = { + { 0, 0, 0xe00000000ULL, 0x10000000,}, + { 1, 0, 0xe10000000ULL, 0x10000000,}, + { 2, 0, 0xe20000000ULL, 0x10000000,}, + { 3, 0, 0xe30000000ULL, 0x10000000,}, + [0xf] = { 0xf, 0, 0xef0000000ULL, 0x10000000,}, +}; + +static void +ob_add_sbus_range(const struct sbus_offset *range, int notfirst) +{ + if (!notfirst) { + push_str("/iommu/sbus"); + fword("find-device"); + } + PUSH(range->slot); + fword("encode-int"); + if (notfirst) + fword("encode+"); + PUSH(range->type); + fword("encode-int"); + fword("encode+"); + PUSH(range->base >> 32); + fword("encode-int"); + fword("encode+"); + PUSH(range->base & 0xffffffff); + fword("encode-int"); + fword("encode+"); + PUSH(range->size); + fword("encode-int"); + fword("encode+"); +} + +static int +ob_sbus_init_ss5(void) +{ + unsigned int slot; + int notfirst = 0; + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss5[slot].size > 0) + ob_add_sbus_range(&sbus_offsets_ss5[slot], notfirst++); + } + push_str("ranges"); + fword("property"); + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss5[slot].size > 0) + sbus_probe_slot_ss5(slot, sbus_offsets_ss5[slot].base); + } + + return 0; +} + +static int +ob_sbus_init_ss10(void) +{ + unsigned int slot; + int notfirst = 0; + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss10[slot].size > 0) + ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++); + } + push_str("ranges"); + fword("property"); + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss10[slot].size > 0) + sbus_probe_slot_ss10(slot, sbus_offsets_ss10[slot].base); + } + + return 0; +} + +static int +ob_sbus_init_ss600mp(void) +{ + unsigned int slot; + int notfirst = 0; + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss10[slot].size > 0) + ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++); + } + push_str("ranges"); + fword("property"); + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss10[slot].size > 0) + sbus_probe_slot_ss600mp(slot, sbus_offsets_ss10[slot].base); + } + + return 0; +} + +int ob_sbus_init(uint64_t base, int machine_id) +{ + ob_sbus_node_init(base); + + switch (machine_id) { + case 66: + return ob_sbus_init_ss600mp(); + case 64 ... 65: + return ob_sbus_init_ss10(); + case 32 ... 63: + return ob_sbus_init_ss5(); + default: + return -1; + } +} diff --git a/qemu/roms/openbios/drivers/sbus.fs b/qemu/roms/openbios/drivers/sbus.fs new file mode 100644 index 000000000..b84a3ac72 --- /dev/null +++ b/qemu/roms/openbios/drivers/sbus.fs @@ -0,0 +1,94 @@ +\ ------------------------------------------------------------------------- +\ SBus encode/decode unit +\ ------------------------------------------------------------------------- + +: decode-unit-sbus ( str len -- id lun ) + ascii , left-split + ( addr-R len-R addr-L len-L ) + parse-hex + -rot parse-hex + swap +; + +: encode-unit-sbus ( id lun -- str len) + swap + pocket tohexstr + " ," pocket tmpstrcat >r + rot pocket tohexstr r> tmpstrcat drop +; + +\ Convert sbus unit (from decode-unit) to physical address using +\ sbus node ranges property + +: sbus-unit>addr ( phys.lo phys.hi -- phys.lo phys.hi -1 | 0 ) + " ranges" my-self ihandle>phandle + get-package-property 0= if ( phys.lo phys.hi prop prop-len ) + begin + 2over swap drop 0 swap \ force phys.lo to zero for matching + 2swap ( unit.phys.lo unit.phys.hi 0 phys.hi res prop prop-len ) + 0 -rot ( unit.phys.lo unit.phys.hi res prop prop-len ) + 2 0 do + decode-int -rot >r >r ( unit.phys.lo unit.phys.hi res phys.x -- R: prop-len prop ) + rot ( unit.phys.lo res phys.x phys.hi ) + = if + 1+ + then ( unit.phys.lo res ) + r> r> ( unit.phys.lo res prop prop-len ) + loop + rot ( prop prop-len res ) + 2 = if \ did we match the unit address? if so, return the physical address + decode-phys 2swap 2drop 2swap ( unit.phys.lo unit.phys.hi phys.lo phys.hi ) + drop 0 d+ \ force unit.phys.hi to zero and add address for final offset + -1 exit + else + decode-phys 2drop decode-int drop \ drop the size and carry on + then + dup 0= until + 2drop 2drop 0 + then +; + +: map-in-sbus ( phys.lo phys.hi size ) + >r sbus-unit>addr if + r@ " map-in" $call-parent + then + r> drop +; + +: map-out-sbus ( virt size ) + " map-out" $call-parent +; + +\ ------------------------------------------------------------------------- +\ SBus probe +\ ------------------------------------------------------------------------- + +: probe-self-sbus ( arg-adr arg-len reg-adr reg-len fcode-adr fcode-len -- ) + + 0 to probe-fcode? + + ['] decode-unit-sbus catch if + 2drop 2drop 2drop 2drop + exit + then + + h# 10000 map-in-sbus + + dup cpeek if + dup h# f1 = swap h# fd = or if + new-device + >r set-args r> + dup 1 byte-load + finish-device + + -1 to probe-fcode? + else + nip nip nip nip + ." Invalid FCode start byte" cr + then + else + nip nip nip nip + then + + h# 10000 map-out-sbus +; diff --git a/qemu/roms/openbios/drivers/scsi.h b/qemu/roms/openbios/drivers/scsi.h new file mode 100644 index 000000000..cb975fc70 --- /dev/null +++ b/qemu/roms/openbios/drivers/scsi.h @@ -0,0 +1,262 @@ +#ifndef _LINUX_SCSI_H +#define _LINUX_SCSI_H + +/* + * This header file contains public constants and structures used by + * the scsi code for linux. + */ + +/* + $Header: /usr/src/linux/include/linux/RCS/scsi.h,v 1.3 1993/09/24 12:20:33 drew Exp $ + + For documentation on the OPCODES, MESSAGES, and SENSE values, + please consult the SCSI standard. + +*/ + +/* + * SCSI opcodes + */ + +#define TEST_UNIT_READY 0x00 +#define REZERO_UNIT 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define READ_BLOCK_LIMITS 0x05 +#define REASSIGN_BLOCKS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SEEK_6 0x0b +#define READ_REVERSE 0x0f +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define INQUIRY 0x12 +#define RECOVER_BUFFERED_DATA 0x14 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define ERASE 0x19 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RECEIVE_DIAGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e + +#define SET_WINDOW 0x24 +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define PRE_FETCH 0x34 +#define READ_POSITION 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define LOCK_UNLOCK_CACHE 0x36 +#define READ_DEFECT_DATA 0x37 +#define MEDIUM_SCAN 0x38 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a +#define WRITE_BUFFER 0x3b +#define READ_BUFFER 0x3c +#define UPDATE_BLOCK 0x3d +#define READ_LONG 0x3e +#define WRITE_LONG 0x3f +#define CHANGE_DEFINITION 0x40 +#define WRITE_SAME 0x41 +#define READ_TOC 0x43 +#define LOG_SELECT 0x4c +#define LOG_SENSE 0x4d +#define MODE_SELECT_10 0x55 +#define RESERVE_10 0x56 +#define RELEASE_10 0x57 +#define MODE_SENSE_10 0x5a +#define PERSISTENT_RESERVE_IN 0x5e +#define PERSISTENT_RESERVE_OUT 0x5f +#define REPORT_LUNS 0xa0 +#define MOVE_MEDIUM 0xa5 +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define WRITE_VERIFY_12 0xae +#define SEARCH_HIGH_12 0xb0 +#define SEARCH_EQUAL_12 0xb1 +#define SEARCH_LOW_12 0xb2 +#define READ_ELEMENT_STATUS 0xb8 +#define SEND_VOLUME_TAG 0xb6 +#define WRITE_LONG_2 0xea +#define READ_16 0x88 +#define WRITE_16 0x8a +#define VERIFY_16 0x8f +#define SERVICE_ACTION_IN 0x9e +/* values for service action in */ +#define SAI_READ_CAPACITY_16 0x10 + +#define SCSI_RETRY_10(c) ((c) == READ_6 || (c) == WRITE_6 || (c) == SEEK_6) + +/* + * SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft + * T10/1561-D Revision 4 Draft dated 7th November 2002. + */ +#define SAM_STAT_GOOD 0x00 +#define SAM_STAT_CHECK_CONDITION 0x02 +#define SAM_STAT_CONDITION_MET 0x04 +#define SAM_STAT_BUSY 0x08 +#define SAM_STAT_INTERMEDIATE 0x10 +#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 +#define SAM_STAT_RESERVATION_CONFLICT 0x18 +#define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */ +#define SAM_STAT_TASK_SET_FULL 0x28 +#define SAM_STAT_ACA_ACTIVE 0x30 +#define SAM_STAT_TASK_ABORTED 0x40 + +/* + * Status codes + */ + +#define GOOD 0x00 +#define CHECK_CONDITION 0x01 +#define CONDITION_GOOD 0x02 +#define BUSY 0x04 +#define INTERMEDIATE_GOOD 0x08 +#define INTERMEDIATE_C_GOOD 0x0a +#define RESERVATION_CONFLICT 0x0c +#define COMMAND_TERMINATED 0x11 +#define QUEUE_FULL 0x14 + +#define STATUS_MASK 0x3e + +/* + * SENSE KEYS + */ + +#define NO_SENSE 0x00 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define MEDIUM_ERROR 0x03 +#define HARDWARE_ERROR 0x04 +#define ILLEGAL_REQUEST 0x05 +#define UNIT_ATTENTION 0x06 +#define DATA_PROTECT 0x07 +#define BLANK_CHECK 0x08 +#define COPY_ABORTED 0x0a +#define ABORTED_COMMAND 0x0b +#define VOLUME_OVERFLOW 0x0d +#define MISCOMPARE 0x0e + + +/* + * DEVICE TYPES + */ + +#define TYPE_DISK 0x00 +#define TYPE_TAPE 0x01 +#define TYPE_PRINTER 0x02 +#define TYPE_PROCESSOR 0x03 /* HP scanners use this */ +#define TYPE_WORM 0x04 /* Treated as ROM by our system */ +#define TYPE_ROM 0x05 +#define TYPE_SCANNER 0x06 +#define TYPE_MOD 0x07 /* Magneto-optical disk - + * - treated as TYPE_DISK */ +#define TYPE_MEDIUM_CHANGER 0x08 +#define TYPE_COMM 0x09 /* Communications device */ +#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_NO_LUN 0x7f + +/* + * standard mode-select header prepended to all mode-select commands + * + * moved here from cdrom.h -- kraxel + */ + +struct ccs_modesel_head +{ + uint8_t _r1; /* reserved */ + uint8_t medium; /* device-specific medium type */ + uint8_t _r2; /* reserved */ + uint8_t block_desc_length; /* block descriptor length */ + uint8_t density; /* device-specific density code */ + uint8_t number_blocks_hi; /* number of blocks in this block desc */ + uint8_t number_blocks_med; + uint8_t number_blocks_lo; + uint8_t _r3; + uint8_t block_length_hi; /* block length for blocks in this desc */ + uint8_t block_length_med; + uint8_t block_length_lo; +}; + +/* + * MESSAGE CODES + */ + +#define COMMAND_COMPLETE 0x00 +#define EXTENDED_MESSAGE 0x01 +#define EXTENDED_MODIFY_DATA_POINTER 0x00 +#define EXTENDED_SDTR 0x01 +#define EXTENDED_EXTENDED_IDENTIFY 0x02 /* SCSI-I only */ +#define EXTENDED_WDTR 0x03 +#define SAVE_POINTERS 0x02 +#define RESTORE_POINTERS 0x03 +#define DISCONNECT 0x04 +#define INITIATOR_ERROR 0x05 +#define ABORT 0x06 +#define MESSAGE_REJECT 0x07 +#define NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define LINKED_CMD_COMPLETE 0x0a +#define LINKED_FLG_CMD_COMPLETE 0x0b +#define BUS_DEVICE_RESET 0x0c + +#define INITIATE_RECOVERY 0x0f /* SCSI-II only */ +#define RELEASE_RECOVERY 0x10 /* SCSI-II only */ + +#define SIMPLE_QUEUE_TAG 0x20 +#define HEAD_OF_QUEUE_TAG 0x21 +#define ORDERED_QUEUE_TAG 0x22 + +/* + * Here are some scsi specific ioctl commands which are sometimes useful. + */ +/* These are a few other constants only used by scsi devices */ +/* Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395 */ + +#define SCSI_IOCTL_GET_IDLUN 0x5382 /* conflicts with CDROMAUDIOBUFSIZ */ + +/* Used to turn on and off tagged queuing for scsi devices */ + +#define SCSI_IOCTL_TAGGED_ENABLE 0x5383 +#define SCSI_IOCTL_TAGGED_DISABLE 0x5384 + +/* Used to obtain the host number of a device. */ +#define SCSI_IOCTL_PROBE_HOST 0x5385 + +/* Used to get the bus number for a device */ +#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386 + +/* Used to get the PCI location of a device */ +#define SCSI_IOCTL_GET_PCI 0x5387 + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ + +#endif diff --git a/qemu/roms/openbios/drivers/tcx.fs b/qemu/roms/openbios/drivers/tcx.fs new file mode 100644 index 000000000..af8991fd0 --- /dev/null +++ b/qemu/roms/openbios/drivers/tcx.fs @@ -0,0 +1,280 @@ +\ +\ Fcode payload for QEMU TCX graphics card +\ +\ This is the Forth source for an Fcode payload to initialise +\ the QEMU TCX graphics card. +\ +\ (C) Copyright 2013 Mark Cave-Ayland +\ + +fcode-version3 + +\ +\ Instead of using fixed values for the framebuffer address and the width +\ and height, grab the ones passed in by QEMU/generated by OpenBIOS +\ + +: (find-xt) \ ( str len -- xt | -1 ) + $find if + exit + else + 2drop + -1 + then +; + +: (is-openbios) \ ( -- true | false ) + " openbios-video-width" (find-xt) -1 <> if + -1 + else + 0 + then +; + +" openbios-video-width" (find-xt) cell+ value openbios-video-width-xt +" openbios-video-height" (find-xt) cell+ value openbios-video-height-xt +" depth-bits" (find-xt) cell+ value depth-bits-xt +" line-bytes" (find-xt) cell+ value line-bytes-xt + +: openbios-video-width + (is-openbios) if + openbios-video-width-xt @ + else + h# 400 + then +; + +: openbios-video-height + (is-openbios) if + openbios-video-height-xt @ + else + h# 300 + then +; + +: depth-bits + (is-openbios) if + depth-bits-xt @ + else + h# 8 + then +; + +: line-bytes + (is-openbios) if + line-bytes-xt @ + else + h# 400 + then +; + +\ +\ Registers +\ + +h# 0 constant tcx-off-rom +h# 10000 constant /tcx-off-rom + +h# 200000 constant tcx-off-cmap +h# 4000 constant /tcx-off-cmap-24 +h# 4 constant /tcx-off-cmap-8 + +h# 240000 constant tcx-off-dhc +h# 4000 constant /tcx-off-dhc-24 +h# 4 constant /tcx-off-dhc-8 + +h# 280000 constant tcx-off-alt +h# 8000 constant /tcx-off-alt-24 +h# 1 constant /tcx-off-alt-8 + +h# 301000 constant tcx-off-thc-24 +h# 300000 constant tcx-off-thc-8 +h# 1000 constant /tcx-off-thc-24 +h# 81c constant /tcx-off-thc-8 + +h# 701000 constant tcx-off-tec +h# 1000 constant /tcx-off-tec + +h# 800000 constant tcx-off-dfb8 +h# 100000 constant /tcx-off-dfb8 + +h# 2000000 constant tcx-off-dfb24 +h# 400000 constant /tcx-off-dfb24-24 +h# 1 constant /tcx-off-dfb24-8 + +h# 4000000 constant tcx-off-stip +h# 800000 constant /tcx-off-stip + +h# 6000000 constant tcx-off-blit +h# 800000 constant /tcx-off-blit + +h# a000000 constant tcx-off-rdfb32 +h# 400000 constant /tcx-off-rdfb32-24 +h# 1 constant /tcx-off-rdfb32-8 + +h# c000000 constant tcx-off-rstip +h# 800000 constant /tcx-off-rstip-24 +h# 1 constant /tcx-off-rstip-8 + +h# e000000 constant tcx-off-rblit +h# 800000 constant /tcx-off-rblit-24 +h# 1 constant /tcx-off-rblit-8 + +: >tcx-reg-spec ( offset size -- encoded-reg ) + >r 0 my-address d+ my-space encode-phys r> encode-int encode+ +; + +: tcx-8bit-reg + \ WARNING: order is important (at least to Solaris) + tcx-off-dfb8 /tcx-off-dfb8 >tcx-reg-spec + tcx-off-dfb24 /tcx-off-dfb24-8 >tcx-reg-spec encode+ + tcx-off-stip /tcx-off-stip >tcx-reg-spec encode+ + tcx-off-blit /tcx-off-blit >tcx-reg-spec encode+ + tcx-off-rdfb32 /tcx-off-rdfb32-8 >tcx-reg-spec encode+ + tcx-off-rstip /tcx-off-rstip-8 >tcx-reg-spec encode+ + tcx-off-rblit /tcx-off-rblit-8 >tcx-reg-spec encode+ + tcx-off-tec /tcx-off-tec >tcx-reg-spec encode+ + tcx-off-cmap /tcx-off-cmap-8 >tcx-reg-spec encode+ + tcx-off-thc-8 /tcx-off-thc-8 >tcx-reg-spec encode+ + tcx-off-rom /tcx-off-rom >tcx-reg-spec encode+ + tcx-off-dhc /tcx-off-dhc-8 >tcx-reg-spec encode+ + tcx-off-alt /tcx-off-alt-8 >tcx-reg-spec encode+ + " reg" property +; + +: tcx-24bit-reg + \ WARNING: order is important (at least to Solaris) + tcx-off-dfb8 /tcx-off-dfb8 >tcx-reg-spec + tcx-off-dfb24 /tcx-off-dfb24-24 >tcx-reg-spec encode+ + tcx-off-stip /tcx-off-stip >tcx-reg-spec encode+ + tcx-off-blit /tcx-off-blit >tcx-reg-spec encode+ + tcx-off-rdfb32 /tcx-off-rdfb32-24 >tcx-reg-spec encode+ + tcx-off-rstip /tcx-off-rstip-24 >tcx-reg-spec encode+ + tcx-off-rblit /tcx-off-rblit-24 >tcx-reg-spec encode+ + tcx-off-tec /tcx-off-tec >tcx-reg-spec encode+ + tcx-off-cmap /tcx-off-cmap-24 >tcx-reg-spec encode+ + tcx-off-thc-24 /tcx-off-thc-24 >tcx-reg-spec encode+ + tcx-off-rom /tcx-off-rom >tcx-reg-spec encode+ + tcx-off-dhc /tcx-off-dhc-24 >tcx-reg-spec encode+ + tcx-off-alt /tcx-off-alt-24 >tcx-reg-spec encode+ + " reg" property +; + +: do-map-in ( offset size -- virt ) + >r my-space r> " map-in" $call-parent +; + +: do-map-out ( virt size ) + " map-out" $call-parent +; + +\ +\ DAC +\ + +-1 value tcx-dac +-1 value /tcx-dac +-1 value fb-addr + +: dac! ( data reg# -- ) + >r dup 2dup bljoin r> tcx-dac + l! +; + +external + +: color! ( r g b c# -- ) + 0 dac! ( r g b ) + swap rot ( b g r ) + 4 dac! ( b g ) + 4 dac! ( b ) + 4 dac! ( ) +; + +headerless + +\ +\ Mapping +\ + +: dac-map + tcx-off-cmap /tcx-dac do-map-in to tcx-dac +; + +: fb-map + tcx-off-dfb8 h# c0000 do-map-in to fb-addr +; + +: map-regs + dac-map fb-map +; + +\ +\ Installation +\ + +" SUNW,tcx" device-name +" display" device-type + +: qemu-tcx-driver-install ( -- ) + tcx-dac -1 = if + map-regs + + \ Initial pallette taken from Sun's "Writing FCode Programs" + h# ff h# ff h# ff h# 0 color! \ Background white + h# 0 h# 0 h# 0 h# ff color! \ Foreground black + h# 64 h# 41 h# b4 h# 1 color! \ SUN-blue logo + + fb-addr to frame-buffer-adr + default-font set-font + + \ Sun TCX adapters don't have an address property, but it is useful for + \ OpenBIOS developers. Unfortunately NetBSD SPARC32 has a bug that causes + \ it to fail initialising TCX if the address property is present; so work + \ around this by adding an underscore prefix + frame-buffer-adr encode-int " _address" property + + openbios-video-width openbios-video-height over char-width / over char-height / + fb8-install + then +; + +: qemu-tcx-driver-init + + \ Handle differences between 8-bit/24-bit mode + depth-bits 8 = if + tcx-8bit-reg + /tcx-off-cmap-8 to /tcx-dac + " true" encode-string " tcx-8-bit" property + else + tcx-24bit-reg + /tcx-off-cmap-24 to /tcx-dac + + \ Even with a 24-bit enabled TCX card, the control plane is + \ used in 8-bit mode. So force the video subsystem into 8-bit + \ mode before initialisation. + 8 depth-bits-xt ! + openbios-video-width line-bytes-xt ! + then + + h# 1d encode-int " vbporch" property + h# a0 encode-int " hbporch" property + h# 06 encode-int " vsync" property + h# 88 encode-int " hsync" property + h# 03 encode-int " vfporch" property + h# 18 encode-int " hfporch" property + h# 03dfd240 encode-int " pixfreq" property + h# 3c encode-int " vfreq" property + + openbios-video-height encode-int " height" property + openbios-video-width encode-int " width" property + line-bytes encode-int " linebytes" property + + h# 39 encode-int 0 encode-int encode+ " intr" property + 5 encode-int " interrupts" property + + ['] qemu-tcx-driver-install is-install +; + +qemu-tcx-driver-init + +end0 diff --git a/qemu/roms/openbios/drivers/timer.c b/qemu/roms/openbios/drivers/timer.c new file mode 100644 index 000000000..d6794ae9c --- /dev/null +++ b/qemu/roms/openbios/drivers/timer.c @@ -0,0 +1,100 @@ +/* + * OpenBIOS native timer driver + * + * (C) 2004 Stefan Reinauer <stepan@openbios.org> + * + * 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 "drivers/drivers.h" +#include "timer.h" +#include "asm/io.h" + +#if defined(CONFIG_X86) || defined(CONFIG_AMD64) + +void setup_timers(void) +{ + /* nothing to do */ +} + +static void load_timer2(unsigned int ticks) +{ + /* Set up the timer gate, turn off the speaker */ + outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); + outb(TIMER2_SEL | WORD_ACCESS | MODE0 | BINARY_COUNT, + TIMER_MODE_PORT); + outb(ticks & 0xFF, TIMER2_PORT); + outb(ticks >> 8, TIMER2_PORT); +} + +void udelay(unsigned int usecs) +{ + load_timer2((usecs * TICKS_PER_MS) / 1000); + while ((inb(PPC_PORTB) & PPCB_T2OUT) == 0); +} + +unsigned long currticks(void) +{ + static unsigned long totticks = 0UL; /* High resolution */ + unsigned long ticks = 0; + unsigned char portb = inb(PPC_PORTB); + + /* + * Read the timer, and hope it hasn't wrapped around + * (call this again within 54ms), then restart it + */ + outb(TIMER2_SEL | LATCH_COUNT, TIMER_MODE_PORT); + ticks = inb(TIMER2_PORT); + ticks |= inb(TIMER2_PORT) << 8; + outb(TIMER2_SEL | WORD_ACCESS | MODE0 | BINARY_COUNT, + TIMER_MODE_PORT); + outb(0, TIMER2_PORT); + outb(0, TIMER2_PORT); + + /* + * Check if the timer was running. If not, + * result is rubbish and need to start it + */ + if (portb & PPCB_T2GATE) { + totticks += (0x10000 - ticks); + } else { + /* Set up the timer gate, turn off the speaker */ + outb((portb & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); + } + return totticks / TICKS_PER_MS; +} +#endif + +#ifdef CONFIG_PPC + +void setup_timers(void) +{ + /* nothing to do */ +} + +/* + * TODO: pass via lb table + */ +unsigned long timer_freq = 10000000 / 4; + +void udelay(unsigned int usecs) +{ + unsigned long ticksperusec = timer_freq / 1000000; + _wait_ticks(ticksperusec * usecs); +} + +#endif + +void ndelay(unsigned int nsecs) +{ + udelay((nsecs + 999) / 1000); +} + +void mdelay(unsigned int msecs) +{ + udelay(msecs * 1000); +} diff --git a/qemu/roms/openbios/drivers/timer.h b/qemu/roms/openbios/drivers/timer.h new file mode 100644 index 000000000..7e86db3fa --- /dev/null +++ b/qemu/roms/openbios/drivers/timer.h @@ -0,0 +1,62 @@ +/* Taken from Etherboot */ +/* Defines for routines to implement a low-overhead timer for drivers */ + + /* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#ifndef TIMER_H +#define TIMER_H + +/* Ports for the 8254 timer chip */ +#define TIMER2_PORT 0x42 +#define TIMER_MODE_PORT 0x43 + +/* Meaning of the mode bits */ +#define TIMER0_SEL 0x00 +#define TIMER1_SEL 0x40 +#define TIMER2_SEL 0x80 +#define READBACK_SEL 0xC0 + +#define LATCH_COUNT 0x00 +#define LOBYTE_ACCESS 0x10 +#define HIBYTE_ACCESS 0x20 +#define WORD_ACCESS 0x30 + +#define MODE0 0x00 +#define MODE1 0x02 +#define MODE2 0x04 +#define MODE3 0x06 +#define MODE4 0x08 +#define MODE5 0x0A + +#define BINARY_COUNT 0x00 +#define BCD_COUNT 0x01 + +/* Timers tick over at this rate */ +#define CLOCK_TICK_RATE 1193180U +#define TICKS_PER_MS (CLOCK_TICK_RATE/1000) + +/* Parallel Peripheral Controller Port B */ +#define PPC_PORTB 0x61 + +/* Meaning of the port bits */ +#define PPCB_T2OUT 0x20 /* Bit 5 */ +#define PPCB_SPKR 0x02 /* Bit 1 */ +#define PPCB_T2GATE 0x01 /* Bit 0 */ + +extern void ndelay(unsigned int nsecs); +extern void udelay(unsigned int usecs); +extern void mdelay(unsigned int msecs); +extern unsigned long currticks(void); +extern unsigned long get_timer_freq(void); + +/* arch/ppc/timebase.S */ +void _wait_ticks(unsigned long nticks); + +#define TICKS_PER_SEC 1000 + +#endif /* TIMER_H */ diff --git a/qemu/roms/openbios/drivers/usb.c b/qemu/roms/openbios/drivers/usb.c new file mode 100644 index 000000000..c6e37174e --- /dev/null +++ b/qemu/roms/openbios/drivers/usb.c @@ -0,0 +1,587 @@ +/* + * Driver for USB ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2008-2010 coresystems GmbH + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 "config.h" +#include "drivers/usb.h" +#include "usb.h" +#include "timer.h" +#include "libc/byteorder.h" + +hci_t *usb_hcs = 0; + +static void usb_nop_init (usbdev_t *dev); + +static void +usb_nop_destroy (usbdev_t *dev) +{ + if (dev->descriptor != 0) + free (dev->descriptor); + usb_nop_init (dev); + dev->address = -1; + dev->hub = -1; + dev->port = -1; +} + +static void +usb_nop_poll (usbdev_t *dev) +{ + return; +} + +static void +usb_nop_init (usbdev_t *dev) +{ + dev->descriptor = 0; + dev->destroy = usb_nop_destroy; + dev->poll = usb_nop_poll; +} + +hci_t * +new_controller (void) +{ + hci_t *controller = malloc (sizeof (hci_t)); + + if (controller) { + /* atomic */ + controller->next = usb_hcs; + usb_hcs = controller; + /* atomic end */ + } + + return controller; +} + +void +detach_controller (hci_t *controller) +{ + if (controller == NULL) + return; + if (usb_hcs == controller) { + usb_hcs = controller->next; + } else { + hci_t *it = usb_hcs; + while (it != NULL) { + if (it->next == controller) { + it->next = controller->next; + return; + } + it = it->next; + } + } +} + +/** + * Shut down all controllers + */ +int +usb_exit (void) +{ + while (usb_hcs != NULL) { + usb_hcs->shutdown(usb_hcs); + } + return 0; +} + +/** + * Polls all hubs on all USB controllers, to find out about device changes + */ +void +usb_poll (void) +{ + if (usb_hcs == 0) + return; + hci_t *controller = usb_hcs; + while (controller != NULL) { + int i; + for (i = 0; i < 128; i++) { + if (controller->devices[i] != 0) { + controller->devices[i]->poll (controller->devices[i]); + } + } + controller = controller->next; + } +} + +void +init_device_entry (hci_t *controller, int i) +{ + if (controller->devices[i] != 0) + usb_debug("warning: device %d reassigned?\n", i); + controller->devices[i] = malloc(sizeof(usbdev_t)); + controller->devices[i]->controller = controller; + controller->devices[i]->address = -1; + controller->devices[i]->hub = -1; + controller->devices[i]->port = -1; + controller->devices[i]->init = usb_nop_init; + controller->devices[i]->init (controller->devices[i]); +} + +void +set_feature (usbdev_t *dev, int endp, int feature, int rtype) +{ + dev_req_t dr; + + dr.bmRequestType = rtype; + dr.data_dir = host_to_device; + dr.bRequest = SET_FEATURE; + dr.wValue = __cpu_to_le16(feature); + dr.wIndex = __cpu_to_le16(endp); + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); +} + +void +get_status (usbdev_t *dev, int intf, int rtype, int len, void *data) +{ + dev_req_t dr; + + dr.bmRequestType = rtype; + dr.data_dir = device_to_host; + dr.bRequest = GET_STATUS; + dr.wValue = 0; + dr.wIndex = __cpu_to_le16(intf); + dr.wLength = __cpu_to_le16(len); + dev->controller->control (dev, IN, sizeof (dr), &dr, len, data); +} + +u8 * +get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType, + int descIdx, int langID) +{ + u8 buf[8]; + u8 *result; + dev_req_t dr; + int size; + + dr.bmRequestType = bmRequestType; + dr.data_dir = device_to_host; // always like this for descriptors + dr.bRequest = GET_DESCRIPTOR; + dr.wValue = __cpu_to_le16((descType << 8) | descIdx); + dr.wIndex = __cpu_to_le16(langID); + dr.wLength = __cpu_to_le16(8); + if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) { + usb_debug ("getting descriptor size (type %x) failed\n", + descType); + } + + if (descType == 1) { + device_descriptor_t *dd = (device_descriptor_t *) buf; + usb_debug ("maxPacketSize0: %x\n", dd->bMaxPacketSize0); + if (dd->bMaxPacketSize0 != 0) + dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0; + } + + /* special case for configuration descriptors: they carry all their + subsequent descriptors with them, and keep the entire size at a + different location */ + size = buf[0]; + if (buf[1] == 2) { + int realsize = __le16_to_cpu(((unsigned short *) (buf + 2))[0]); + size = realsize; + } + result = malloc (size); + memset (result, 0, size); + dr.wLength = __cpu_to_le16(size); + if (dev->controller-> + control (dev, IN, sizeof (dr), &dr, size, result)) { + usb_debug ("getting descriptor (type %x, size %x) failed\n", + descType, size); + } + + return result; +} + +void +set_configuration (usbdev_t *dev) +{ + dev_req_t dr; + + dr.bmRequestType = 0; + dr.bRequest = SET_CONFIGURATION; + dr.wValue = __cpu_to_le16(dev->configuration[5]); + dr.wIndex = 0; + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); +} + +int +clear_feature (usbdev_t *dev, int endp, int feature, int rtype) +{ + dev_req_t dr; + + dr.bmRequestType = rtype; + dr.data_dir = host_to_device; + dr.bRequest = CLEAR_FEATURE; + dr.wValue = __cpu_to_le16(feature); + dr.wIndex = __cpu_to_le16(endp); + dr.wLength = 0; + return dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); +} + +int +clear_stall (endpoint_t *ep) +{ + usbdev_t *dev = ep->dev; + int endp = ep->endpoint; + int rtype = gen_bmRequestType (host_to_device, standard_type, + endp ? endp_recp : dev_recp); + + int ret = clear_feature (dev, endp, ENDPOINT_HALT, rtype); + ep->toggle = 0; + return ret; +} + +/* returns free address or -1 */ +static int +get_free_address (hci_t *controller) +{ + int i; + for (i = 1; i < 128; i++) { + if (controller->devices[i] == 0) + return i; + } + usb_debug ("no free address found\n"); + return -1; // no free address +} + +int +generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr) +{ + int adr = get_free_address (controller); // address to set + dev_req_t dr; + + memset (&dr, 0, sizeof (dr)); + dr.data_dir = host_to_device; + dr.req_type = standard_type; + dr.req_recp = dev_recp; + dr.bRequest = SET_ADDRESS; + dr.wValue = __cpu_to_le16(adr); + dr.wIndex = 0; + dr.wLength = 0; + + init_device_entry(controller, adr); + usbdev_t *dev = controller->devices[adr]; + // dummy values for registering the address + dev->address = 0; + dev->hub = hubaddr; + dev->port = hubport; + dev->speed = speed; + dev->endpoints[0].dev = dev; + dev->endpoints[0].endpoint = 0; + dev->endpoints[0].maxpacketsize = 8; + dev->endpoints[0].toggle = 0; + dev->endpoints[0].direction = SETUP; + mdelay (50); + if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0)) { + return -1; + } + mdelay (50); + + return adr; +} + +/* Normalize bInterval to log2 of microframes */ +static int +usb_decode_interval(const int speed, const endpoint_type type, const unsigned char bInterval) +{ +#define LOG2(a) ((sizeof(unsigned) << 3) - __builtin_clz(a) - 1) + switch (speed) { + case LOW_SPEED: + switch (type) { + case ISOCHRONOUS: case INTERRUPT: + return LOG2(bInterval) + 3; + default: + return 0; + } + case FULL_SPEED: + switch (type) { + case ISOCHRONOUS: + return (bInterval - 1) + 3; + case INTERRUPT: + return LOG2(bInterval) + 3; + default: + return 0; + } + case HIGH_SPEED: + switch (type) { + case ISOCHRONOUS: case INTERRUPT: + return bInterval - 1; + default: + return LOG2(bInterval); + } + case SUPER_SPEED: + switch (type) { + case ISOCHRONOUS: case INTERRUPT: + return bInterval - 1; + default: + return 0; + } + default: + return 0; + } +#undef LOG2 +} + +static int +set_address (hci_t *controller, int speed, int hubport, int hubaddr) +{ + int adr = controller->set_address(controller, speed, hubport, hubaddr); + if (adr < 0 || !controller->devices[adr]) { + usb_debug ("set_address failed\n"); + return -1; + } + configuration_descriptor_t *cd; + device_descriptor_t *dd; + + usbdev_t *dev = controller->devices[adr]; + dev->address = adr; + dev->hub = hubaddr; + dev->port = hubport; + dev->speed = speed; + dev->descriptor = get_descriptor (dev, gen_bmRequestType + (device_to_host, standard_type, dev_recp), 1, 0, 0); + dd = (device_descriptor_t *) dev->descriptor; + + usb_debug ("* found device (0x%04x:0x%04x, USB %x.%x)", + __le16_to_cpu(dd->idVendor), __le16_to_cpu(dd->idProduct), + __le16_to_cpu(dd->bcdUSB) >> 8, __le16_to_cpu(dd->bcdUSB) & 0xff); + dev->quirks = USB_QUIRK_NONE; + + usb_debug ("\ndevice has %x configurations\n", dd->bNumConfigurations); + if (dd->bNumConfigurations == 0) { + /* device isn't usable */ + usb_debug ("... no usable configuration!\n"); + dev->address = 0; + return -1; + } + + dev->configuration = get_descriptor (dev, gen_bmRequestType + (device_to_host, standard_type, dev_recp), 2, 0, 0); + cd = (configuration_descriptor_t *) dev->configuration; + interface_descriptor_t *interface = + (interface_descriptor_t *) (((char *) cd) + cd->bLength); + { + int i; + int num = cd->bNumInterfaces; + interface_descriptor_t *current = interface; + usb_debug ("device has %x interfaces\n", num); + if (num > 1) { + usb_debug ("\nNOTICE: This driver defaults to using the first interface.\n" + "This might be the wrong choice and lead to limited functionality\n" + "of the device.\n"); + /* we limit to the first interface, as there was no need to + * implement something else for the time being. If you need + * it, see the SetInterface and GetInterface functions in + * the USB specification, and adapt appropriately. + */ + num = (num > 1) ? 1 : num; + } + for (i = 0; i < num; i++) { + int j; + usb_debug (" #%x has %x endpoints, interface %x:%x, protocol %x\n", + current->bInterfaceNumber, current->bNumEndpoints, current->bInterfaceClass, current->bInterfaceSubClass, current->bInterfaceProtocol); + endpoint_descriptor_t *endp = + (endpoint_descriptor_t *) (((char *) current) + + current->bLength); + /* Skip any non-endpoint descriptor */ + if (endp->bDescriptorType != 0x05) + endp = (endpoint_descriptor_t *)(((char *)endp) + ((char *)endp)[0]); + + memset (dev->endpoints, 0, sizeof (dev->endpoints)); + dev->num_endp = 1; // 0 always exists + dev->endpoints[0].dev = dev; + dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0; + dev->endpoints[0].direction = SETUP; + dev->endpoints[0].type = CONTROL; + dev->endpoints[0].interval = usb_decode_interval(dev->speed, CONTROL, endp->bInterval); + for (j = 1; j <= current->bNumEndpoints; j++) { +#ifdef CONFIG_DEBUG_USB + static const char *transfertypes[4] = { + "control", "isochronous", "bulk", "interrupt" + }; + usb_debug (" #%x: Endpoint %x (%s), max packet size %x, type %s\n", j, endp->bEndpointAddress & 0x7f, ((endp->bEndpointAddress & 0x80) != 0) ? "in" : "out", __le16_to_cpu(endp->wMaxPacketSize), transfertypes[endp->bmAttributes]); +#endif + endpoint_t *ep = + &dev->endpoints[dev->num_endp++]; + ep->dev = dev; + ep->endpoint = endp->bEndpointAddress; + ep->toggle = 0; + ep->maxpacketsize = __le16_to_cpu(endp->wMaxPacketSize); + ep->direction = + ((endp->bEndpointAddress & 0x80) == + 0) ? OUT : IN; + ep->type = endp->bmAttributes; + ep->interval = usb_decode_interval(dev->speed, ep->type, endp->bInterval); + endp = (endpoint_descriptor_t + *) (((char *) endp) + endp->bLength); + } + current = (interface_descriptor_t *) endp; + } + } + + if (controller->finish_device_config && + controller->finish_device_config(dev)) + return adr; /* Device isn't configured correctly, + only control transfers may work. */ + + set_configuration(dev); + + int class = dd->bDeviceClass; + if (class == 0) + class = interface->bInterfaceClass; + + usb_debug(", class: "); + switch (class) { + case audio_device: + usb_debug("audio\n"); + break; + case comm_device: + usb_debug("communication\n"); + break; + case hid_device: + usb_debug ("HID\n"); +#ifdef CONFIG_USB_HID + controller->devices[adr]->init = usb_hid_init; + return adr; +#else + usb_debug ("NOTICE: USB HID support not compiled in\n"); +#endif + break; + case physical_device: + usb_debug("physical\n"); + break; + case imaging_device: + usb_debug("camera\n"); + break; + case printer_device: + usb_debug("printer\n"); + break; + case msc_device: + usb_debug ("MSC\n"); +#ifdef CONFIG_USB_MSC + controller->devices[adr]->init = usb_msc_init; + return adr; +#else + usb_debug ("NOTICE: USB MSC support not compiled in\n"); +#endif + break; + case hub_device: + usb_debug ("hub\n"); +#ifdef CONFIG_USB_HUB + controller->devices[adr]->init = usb_hub_init; + return adr; +#else + usb_debug ("NOTICE: USB hub support not compiled in.\n"); +#endif + break; + case cdc_device: + usb_debug("CDC\n"); + break; + case ccid_device: + usb_debug("smartcard / CCID\n"); + break; + case security_device: + usb_debug("content security\n"); + break; + case video_device: + usb_debug("video\n"); + break; + case healthcare_device: + usb_debug("healthcare\n"); + break; + case diagnostic_device: + usb_debug("diagnostic\n"); + break; + case wireless_device: + usb_debug("wireless\n"); + break; + default: + usb_debug("unsupported class %x\n", class); + break; + } + controller->devices[adr]->init = usb_generic_init; + return adr; +} + +/* + * Should be called by the hub drivers whenever a physical detach occurs + * and can be called by usb class drivers if they are unsatisfied with a + * malfunctioning device. + */ +void +usb_detach_device(hci_t *controller, int devno) +{ + /* check if device exists, as we may have + been called yet by the usb class driver */ + if (controller->devices[devno]) { + controller->devices[devno]->destroy (controller->devices[devno]); + free(controller->devices[devno]); + controller->devices[devno] = NULL; + if (controller->destroy_device) + controller->destroy_device(controller, devno); + } +} + +int +usb_attach_device(hci_t *controller, int hubaddress, int port, int speed) +{ +#ifdef CONFIG_USB_DEBUG + static const char* speeds[] = { "full", "low", "high" }; + usb_debug ("%sspeed device\n", (speed <= 2) ? speeds[speed] : "invalid value - no"); +#endif + int newdev = set_address (controller, speed, port, hubaddress); + if (newdev == -1) + return -1; + usbdev_t *newdev_t = controller->devices[newdev]; + // determine responsible driver - current done in set_address + newdev_t->init (newdev_t); + /* init() may have called usb_detach_device() yet, so check */ + return controller->devices[newdev] ? newdev : -1; +} + +static void +usb_generic_destroy (usbdev_t *dev) +{ + if (usb_generic_remove) + usb_generic_remove(dev); +} + +void +usb_generic_init (usbdev_t *dev) +{ + dev->data = NULL; + dev->destroy = usb_generic_destroy; + + if (usb_generic_create) + usb_generic_create(dev); +} diff --git a/qemu/roms/openbios/drivers/usb.h b/qemu/roms/openbios/drivers/usb.h new file mode 100644 index 000000000..2e23a1370 --- /dev/null +++ b/qemu/roms/openbios/drivers/usb.h @@ -0,0 +1,357 @@ +/* + * Driver for USB ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2008 coresystems GmbH + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +#ifndef __USB_H +#define __USB_H +#include <drivers/pci.h> + +typedef enum { host_to_device = 0, device_to_host = 1 } dev_req_dir; +typedef enum { standard_type = 0, class_type = 1, vendor_type = + 2, reserved_type = 3 +} dev_req_type; +typedef enum { dev_recp = 0, iface_recp = 1, endp_recp = 2, other_recp = 3 +} dev_req_recp; + +typedef enum { + GET_STATUS = 0, + CLEAR_FEATURE = 1, + SET_FEATURE = 3, + SET_ADDRESS = 5, + GET_DESCRIPTOR = 6, + SET_DESCRIPTOR = 7, + GET_CONFIGURATION = 8, + SET_CONFIGURATION = 9, + GET_INTERFACE = 10, + SET_INTERFACE = 11, + SYNCH_FRAME = 12 +} bRequest_Codes; + +typedef enum { + ENDPOINT_HALT = 0, + DEVICE_REMOTE_WAKEUP = 1, + TEST_MODE = 2 +} feature_selectors; + +enum { + audio_device = 0x01, + comm_device = 0x02, + hid_device = 0x03, + physical_device = 0x05, + imaging_device = 0x06, + printer_device = 0x07, + msc_device = 0x08, + hub_device = 0x09, + cdc_device = 0x0a, + ccid_device = 0x0b, + security_device = 0x0d, + video_device = 0x0e, + healthcare_device = 0x0f, + diagnostic_device = 0xdc, + wireless_device = 0xe0, + misc_device = 0xef, +}; + +enum { hid_subclass_none = 0, hid_subclass_boot = 1 }; + +enum { + hid_boot_proto_none = 0, + hid_boot_proto_keyboard = 1, + hid_boot_proto_mouse = 2 +}; + +typedef struct { + union { + struct { +#ifdef CONFIG_BIG_ENDIAN + dev_req_dir data_dir:1; + dev_req_type req_type:2; + dev_req_recp req_recp:5; +#else + dev_req_recp req_recp:5; + dev_req_type req_type:2; + dev_req_dir data_dir:1; +#endif + } __attribute__ ((packed)); + unsigned char bmRequestType; + } __attribute__ ((packed)); + unsigned char bRequest; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; +} __attribute__ ((packed)) dev_req_t; + +struct usbdev_hc; +typedef struct usbdev_hc hci_t; + +struct usbdev; +typedef struct usbdev usbdev_t; + +typedef enum { SETUP, IN, OUT } direction_t; +typedef enum { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3 +} endpoint_type; + +typedef struct { + usbdev_t *dev; + int endpoint; + direction_t direction; + int toggle; + int maxpacketsize; + endpoint_type type; + int interval; /* expressed as binary logarithm of the number + of microframes (i.e. t = 125us * 2^interval) */ +} endpoint_t; + +enum { FULL_SPEED = 0, LOW_SPEED = 1, HIGH_SPEED = 2, SUPER_SPEED = 3 }; + +struct usbdev { + hci_t *controller; + endpoint_t endpoints[32]; + int num_endp; + int address; // usb address + int hub; // hub, device is attached to + int port; // port where device is attached + int speed; // 1: lowspeed, 0: fullspeed, 2: highspeed + u32 quirks; // quirks field. got to love usb + void *data; + u8 *descriptor; + u8 *configuration; + void (*init) (usbdev_t *dev); + void (*destroy) (usbdev_t *dev); + void (*poll) (usbdev_t *dev); +}; + +typedef enum { OHCI = 0, UHCI = 1, EHCI = 2, XHCI = 3} hc_type; + +struct usbdev_hc { + hci_t *next; + u32 reg_base; + hc_type type; + usbdev_t *devices[128]; // dev 0 is root hub, 127 is last addressable + + /* start(): Resume operation. */ + void (*start) (hci_t *controller); + /* stop(): Stop operation but keep controller initialized. */ + void (*stop) (hci_t *controller); + /* reset(): Perform a controller reset. The controller needs to + be (re)initialized afterwards to work (again). */ + void (*reset) (hci_t *controller); + /* init(): Initialize a (previously reset) controller + to a working state. */ + void (*init) (hci_t *controller); + /* shutdown(): Stop operation, detach host controller and shutdown + this driver instance. After calling shutdown() any + other usage of this hci_t* is invalid. */ + void (*shutdown) (hci_t *controller); + + int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize); + int (*control) (usbdev_t *dev, direction_t pid, int dr_length, + void *devreq, int data_length, u8 *data); + void* (*create_intr_queue) (endpoint_t *ep, int reqsize, int reqcount, int reqtiming); + void (*destroy_intr_queue) (endpoint_t *ep, void *queue); + u8* (*poll_intr_queue) (void *queue); + void *instance; + + /* set_address(): Tell the usb device its address and + return it. xHCI controllers want to + do this by themself. Also, the usbdev + structure has to be allocated and + initialized. */ + int (*set_address) (hci_t *controller, int speed, int hubport, int hubaddr); + /* finish_device_config(): Another hook for xHCI, + returns 0 on success. */ + int (*finish_device_config) (usbdev_t *dev); + /* destroy_device(): Finally, destroy all structures that + were allocated during set_address() + and finish_device_config(). */ + void (*destroy_device) (hci_t *controller, int devaddr); +}; + +typedef struct { + unsigned char bDescLength; + unsigned char bDescriptorType; + unsigned char bNbrPorts; + union { + struct { +#ifdef CONFIG_BIG_ENDIAN + unsigned long:8; + unsigned long arePortIndicatorsSupported:1; + unsigned long ttThinkTime:2; + unsigned long overcurrentProtectionMode:2; + unsigned long isCompoundDevice:1; + unsigned long logicalPowerSwitchingMode:2; +#else + unsigned long logicalPowerSwitchingMode:2; + unsigned long isCompoundDevice:1; + unsigned long overcurrentProtectionMode:2; + unsigned long ttThinkTime:2; + unsigned long arePortIndicatorsSupported:1; + unsigned long:8; +#endif + } __attribute__ ((packed)); + unsigned short wHubCharacteristics; + } __attribute__ ((packed)); + unsigned char bPowerOn2PwrGood; + unsigned char bHubContrCurrent; + char DeviceRemovable[]; +} __attribute__ ((packed)) hub_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdUSB; + unsigned char bDeviceClass; + unsigned char bDeviceSubClass; + unsigned char bDeviceProtocol; + unsigned char bMaxPacketSize0; + unsigned short idVendor; + unsigned short idProduct; + unsigned short bcdDevice; + unsigned char iManufacturer; + unsigned char iProduct; + unsigned char iSerialNumber; + unsigned char bNumConfigurations; +} __attribute__ ((packed)) device_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char bMaxPower; +} __attribute__ ((packed)) configuration_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; +} __attribute__ ((packed)) interface_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bEndpointAddress; + unsigned char bmAttributes; + unsigned short wMaxPacketSize; + unsigned char bInterval; +} __attribute__ ((packed)) endpoint_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdHID; + unsigned char bCountryCode; + unsigned char bNumDescriptors; + unsigned char bReportDescriptorType; + unsigned short wReportDescriptorLength; +} __attribute__ ((packed)) hid_descriptor_t; + +hci_t *new_controller (void); +void detach_controller (hci_t *controller); +void usb_poll (void); +void init_device_entry (hci_t *controller, int num); + +void set_feature (usbdev_t *dev, int endp, int feature, int rtype); +void get_status (usbdev_t *dev, int endp, int rtype, int len, void *data); +void set_configuration (usbdev_t *dev); +int clear_feature (usbdev_t *dev, int endp, int feature, int rtype); +int clear_stall (endpoint_t *ep); + +void usb_hub_init (usbdev_t *dev); +void usb_hid_init (usbdev_t *dev); +void usb_msc_init (usbdev_t *dev); +void usb_generic_init (usbdev_t *dev); + +u8 *get_descriptor (usbdev_t *dev, unsigned char bmRequestType, + int descType, int descIdx, int langID); + +static inline unsigned char +gen_bmRequestType (dev_req_dir dir, dev_req_type type, dev_req_recp recp) +{ + return (dir << 7) | (type << 5) | recp; +} + +/* default "set address" handler */ +int generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr); + +void usb_detach_device(hci_t *controller, int devno); +int usb_attach_device(hci_t *controller, int hubaddress, int port, int speed); + +u32 usb_quirk_check(u16 vendor, u16 device); +int usb_interface_check(u16 vendor, u16 device); + +#define USB_QUIRK_MSC_FORCE_PROTO_SCSI (1 << 0) +#define USB_QUIRK_MSC_FORCE_PROTO_ATAPI (1 << 1) +#define USB_QUIRK_MSC_FORCE_PROTO_UFI (1 << 2) +#define USB_QUIRK_MSC_FORCE_PROTO_RBC (1 << 3) +#define USB_QUIRK_MSC_FORCE_TRANS_BBB (1 << 4) +#define USB_QUIRK_MSC_FORCE_TRANS_CBI (1 << 5) +#define USB_QUIRK_MSC_FORCE_TRANS_CBI_I (1 << 6) +#define USB_QUIRK_MSC_NO_TEST_UNIT_READY (1 << 7) +#define USB_QUIRK_MSC_SHORT_INQUIRY (1 << 8) +#define USB_QUIRK_TEST (1 << 31) +#define USB_QUIRK_NONE 0 + +#ifdef CONFIG_DEBUG_USB +#define usb_debug(fmt, args...) do { printk(fmt , ##args); } while (0) +#else +#define usb_debug(fmt, args...) +#endif + +/** + * To be implemented by libpayload-client. It's called by the USB stack + * when a new USB device is found which isn't claimed by a built in driver, + * so the client has the chance to know about it. + * + * @param dev descriptor for the USB device + */ +void __attribute__((weak)) usb_generic_create (usbdev_t *dev); + +/** + * To be implemented by libpayload-client. It's called by the USB stack + * when it finds out that a USB device is removed which wasn't claimed by a + * built in driver. + * + * @param dev descriptor for the USB device + */ +void __attribute__((weak)) usb_generic_remove (usbdev_t *dev); + +#endif diff --git a/qemu/roms/openbios/drivers/usbhid.c b/qemu/roms/openbios/drivers/usbhid.c new file mode 100644 index 000000000..a423278a8 --- /dev/null +++ b/qemu/roms/openbios/drivers/usbhid.c @@ -0,0 +1,579 @@ +/* + * Driver for HID devices ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2008-2010 coresystems GmbH + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 "config.h" +#include "libopenbios/bindings.h" +#include <libc/string.h> +#include "libc/byteorder.h" +#include "libc/vsprintf.h" +#include "drivers/usb.h" +#include "usb.h" + +DECLARE_UNNAMED_NODE(usb_kbd, INSTALL_OPEN, sizeof(int)); + +static void +keyboard_open(int *idx) +{ + RET(-1); +} + +static void +keyboard_close(int *idx) +{ +} + +static void keyboard_read(void); + +NODE_METHODS( usb_kbd ) = { + { "open", keyboard_open }, + { "close", keyboard_close }, + { "read", keyboard_read }, +}; + +#ifdef CONFIG_USB_DEBUG +static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" }; +#endif +typedef enum { hid_proto_boot = 0, hid_proto_report = 1 } hid_proto; +enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT = + 0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb +}; + +typedef union { + struct { + u8 modifiers; + u8 repeats; + u8 keys[6]; + }; + u8 buffer[8]; +} usb_hid_keyboard_event_t; + +typedef struct { + void* queue; + hid_descriptor_t *descriptor; + + usb_hid_keyboard_event_t previous; + int lastkeypress; + int repeat_delay; +} usbhid_inst_t; + +#define HID_INST(dev) ((usbhid_inst_t*)(dev)->data) + +static void +usb_hid_destroy (usbdev_t *dev) +{ + if (HID_INST(dev)->queue) { + int i; + for (i = 0; i <= dev->num_endp; i++) { + if (dev->endpoints[i].endpoint == 0) + continue; + if (dev->endpoints[i].type != INTERRUPT) + continue; + if (dev->endpoints[i].direction != IN) + continue; + break; + } + dev->controller->destroy_intr_queue( + &dev->endpoints[i], HID_INST(dev)->queue); + HID_INST(dev)->queue = NULL; + } + free (dev->data); +} + +/* keybuffer is global to all USB keyboards */ +static int keycount; +#define KEYBOARD_BUFFER_SIZE 16 +static short keybuffer[KEYBOARD_BUFFER_SIZE]; + +const char *countries[36][2] = { + { "unknown", "us" }, + { "Arabic", "ae" }, + { "Belgian", "be" }, + { "Canadian-Bilingual", "ca" }, + { "Canadian-French", "ca" }, + { "Czech Republic", "cz" }, + { "Danish", "dk" }, + { "Finnish", "fi" }, + { "French", "fr" }, + { "German", "de" }, + { "Greek", "gr" }, + { "Hebrew", "il" }, + { "Hungary", "hu" }, + { "International (ISO)", "iso" }, + { "Italian", "it" }, + { "Japan (Katakana)", "jp" }, + { "Korean", "us" }, + { "Latin American", "us" }, + { "Netherlands/Dutch", "nl" }, + { "Norwegian", "no" }, + { "Persian (Farsi)", "ir" }, + { "Poland", "pl" }, + { "Portuguese", "pt" }, + { "Russia", "ru" }, + { "Slovakia", "sl" }, + { "Spanish", "es" }, + { "Swedish", "se" }, + { "Swiss/French", "ch" }, + { "Swiss/German", "ch" }, + { "Switzerland", "ch" }, + { "Taiwan", "tw" }, + { "Turkish-Q", "tr" }, + { "UK", "uk" }, + { "US", "us" }, + { "Yugoslavia", "yu" }, + { "Turkish-F", "tr" }, + /* 36 - 255: Reserved */ +}; + + + +struct layout_maps { + const char *country; + const short map[4][0x80]; +}; + +static const struct layout_maps *map; + +#define KEY_BREAK 0x101 /* Not on PC KBD */ +#define KEY_DOWN 0x102 /* Down arrow key */ +#define KEY_UP 0x103 /* Up arrow key */ +#define KEY_LEFT 0x104 /* Left arrow key */ +#define KEY_RIGHT 0x105 /* Right arrow key */ +#define KEY_HOME 0x106 /* home key */ +#define KEY_BACKSPACE 0x107 /* not on pc */ +#define KEY_F0 0x108 /* function keys; 64 reserved */ +#define KEY_F(n) (KEY_F0 + (n)) + +#define KEY_DC 0x14a /* delete character */ +#define KEY_IC 0x14b /* insert char or enter ins mode */ + +#define KEY_NPAGE 0x152 /* next page */ +#define KEY_PPAGE 0x153 /* previous page */ + +#define KEY_ENTER 0x157 /* enter or send (unreliable) */ + +#define KEY_PRINT 0x15a /* print/copy */ + +#define KEY_END 0x166 /* end key */ + +static const struct layout_maps keyboard_layouts[] = { +// #ifdef CONFIG_PC_KEYBOARD_LAYOUT_US +{ .country = "us", .map = { + { /* No modifier */ + -1, -1, -1, -1, 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + /* 0x10 */ + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', + /* 0x20 */ + '3', '4', '5', '6', '7', '8', '9', '0', + '\n', '\e', '\b', '\t', ' ', '-', '=', '[', + /* 0x30 */ + ']', '\\', -1, ';', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }, + { /* Shift modifier */ + -1, -1, -1, -1, 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + /* 0x10 */ + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', + /* 0x20 */ + '#', '$', '%', '^', '&', '*', '(', ')', + '\n', '\e', '\b', '\t', ' ', '_', '+', '{', + /* 0x30 */ + '}', '|', -1, ':', '"', '~', '<', '>', + '?', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }, + { /* Alt */ + -1, -1, -1, -1, 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + /* 0x10 */ + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', + /* 0x20 */ + '3', '4', '5', '6', '7', '8', '9', '0', + '\n', '\e', '\b', '\t', ' ', '-', '=', '[', + /* 0x30 */ + ']', '\\', -1, ';', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }, + { /* Shift+Alt modifier */ + -1, -1, -1, -1, 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + /* 0x10 */ + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', + /* 0x20 */ + '#', '$', '%', '^', '&', '*', '(', ')', + '\n', '\e', '\b', '\t', ' ', '-', '=', '[', + /* 0x30 */ + ']', '\\', -1, ':', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + } +}}, +//#endif +}; + +#define MOD_SHIFT (1 << 0) +#define MOD_ALT (1 << 1) +#define MOD_CTRL (1 << 2) + +static void usb_hid_keyboard_queue(int ch) { + /* ignore key presses if buffer full */ + if (keycount < KEYBOARD_BUFFER_SIZE) + keybuffer[keycount++] = ch; +} + +#define KEYBOARD_REPEAT_MS 30 +#define INITIAL_REPEAT_DELAY 10 +#define REPEAT_DELAY 2 + +static void +usb_hid_process_keyboard_event(usbhid_inst_t *const inst, + const usb_hid_keyboard_event_t *const current) +{ + const usb_hid_keyboard_event_t *const previous = &inst->previous; + + int i, keypress = 0, modifiers = 0; + + if (current->modifiers & 0x01) /* Left-Ctrl */ modifiers |= MOD_CTRL; + if (current->modifiers & 0x02) /* Left-Shift */ modifiers |= MOD_SHIFT; + if (current->modifiers & 0x04) /* Left-Alt */ modifiers |= MOD_ALT; + if (current->modifiers & 0x08) /* Left-GUI */ ; + if (current->modifiers & 0x10) /* Right-Ctrl */ modifiers |= MOD_CTRL; + if (current->modifiers & 0x20) /* Right-Shift */ modifiers |= MOD_SHIFT; + if (current->modifiers & 0x40) /* Right-AltGr */ modifiers |= MOD_ALT; + if (current->modifiers & 0x80) /* Right-GUI */ ; + + /* Did the event change at all? */ + if (inst->lastkeypress && + !memcmp(current, previous, sizeof(*current))) { + /* No. Then it's a key repeat event. */ + if (inst->repeat_delay) { + inst->repeat_delay--; + } else { + usb_hid_keyboard_queue(inst->lastkeypress); + inst->repeat_delay = REPEAT_DELAY; + } + + return; + } + + inst->lastkeypress = 0; + + for (i=0; i<6; i++) { + int j; + int skip = 0; + // No more keys? skip + if (current->keys[i] == 0) + return; + + for (j=0; j<6; j++) { + if (current->keys[i] == previous->keys[j]) { + skip = 1; + break; + } + } + if (skip) + continue; + + + /* Mask off MOD_CTRL */ + keypress = map->map[modifiers & 0x03][current->keys[i]]; + + if (modifiers & MOD_CTRL) { + switch (keypress) { + case 'a' ... 'z': + keypress &= 0x1f; + break; + default: + continue; + } + } + + if (keypress == -1) { + /* Debug: Print unknown keys */ + usb_debug ("usbhid: <%x> %x [ %x %x %x %x %x %x ] %d\n", + current->modifiers, current->repeats, + current->keys[0], current->keys[1], + current->keys[2], current->keys[3], + current->keys[4], current->keys[5], i); + + /* Unknown key? Try next one in the queue */ + continue; + } + + usb_hid_keyboard_queue(keypress); + + /* Remember for authentic key repeat */ + inst->lastkeypress = keypress; + inst->repeat_delay = INITIAL_REPEAT_DELAY; + } +} + +static void +usb_hid_poll (usbdev_t *dev) +{ + usb_hid_keyboard_event_t current; + const u8 *buf; + + while ((buf=dev->controller->poll_intr_queue (HID_INST(dev)->queue))) { + memcpy(¤t.buffer, buf, 8); + usb_hid_process_keyboard_event(HID_INST(dev), ¤t); + HID_INST(dev)->previous = current; + } +} + +static void +usb_hid_set_idle (usbdev_t *dev, interface_descriptor_t *interface, u16 duration) +{ + dev_req_t dr; + dr.data_dir = host_to_device; + dr.req_type = class_type; + dr.req_recp = iface_recp; + dr.bRequest = SET_IDLE; + dr.wValue = __cpu_to_le16((duration >> 2) << 8); + dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber); + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0); +} + +static void +usb_hid_set_protocol (usbdev_t *dev, interface_descriptor_t *interface, hid_proto proto) +{ + dev_req_t dr; + dr.data_dir = host_to_device; + dr.req_type = class_type; + dr.req_recp = iface_recp; + dr.bRequest = SET_PROTOCOL; + dr.wValue = __cpu_to_le16(proto); + dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber); + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0); +} + +static int usb_hid_set_layout (const char *country) +{ + /* FIXME should be per keyboard */ + int i; + + for (i=0; i<sizeof(keyboard_layouts)/sizeof(keyboard_layouts[0]); i++) { + if (strncmp(keyboard_layouts[i].country, country, + strlen(keyboard_layouts[i].country))) + continue; + + /* Found, changing keyboard layout */ + map = &keyboard_layouts[i]; + usb_debug(" Keyboard layout '%s'\n", map->country); + return 0; + } + + usb_debug(" Keyboard layout '%s' not found, using '%s'\n", + country, map->country); + + /* Nothing found, not changed */ + return -1; +} + +void +usb_hid_init (usbdev_t *dev) +{ + configuration_descriptor_t *cd = (configuration_descriptor_t*)dev->configuration; + interface_descriptor_t *interface = (interface_descriptor_t*)(((char *) cd) + cd->bLength); + + if (interface->bInterfaceSubClass == hid_subclass_boot) { + u8 countrycode = 0; + usb_debug (" supports boot interface..\n"); + usb_debug (" it's a %s\n", + boot_protos[interface->bInterfaceProtocol]); + switch (interface->bInterfaceProtocol) { + case hid_boot_proto_keyboard: + dev->data = malloc (sizeof (usbhid_inst_t)); + if (!dev->data) { + printk("Not enough memory for USB HID device.\n"); + return; + } + memset(&HID_INST(dev)->previous, 0x00, + sizeof(HID_INST(dev)->previous)); + usb_debug (" configuring...\n"); + usb_hid_set_protocol(dev, interface, hid_proto_boot); + usb_hid_set_idle(dev, interface, KEYBOARD_REPEAT_MS); + usb_debug (" activating...\n"); +#if 0 + HID_INST (dev)->descriptor = + (hid_descriptor_t *) + get_descriptor(dev, gen_bmRequestType + (device_to_host, standard_type, iface_recp), + 0x21, 0, 0); + countrycode = HID_INST(dev)->descriptor->bCountryCode; +#endif + /* 35 countries defined: */ + if (countrycode > 35) + countrycode = 0; + usb_debug (" Keyboard has %s layout (country code %02x)\n", + countries[countrycode][0], countrycode); + + /* Set keyboard layout accordingly */ + usb_hid_set_layout(countries[countrycode][1]); + + // only add here, because we only support boot-keyboard HID devices + dev->destroy = usb_hid_destroy; + dev->poll = usb_hid_poll; + int i; + for (i = 0; i <= dev->num_endp; i++) { + if (dev->endpoints[i].endpoint == 0) + continue; + if (dev->endpoints[i].type != INTERRUPT) + continue; + if (dev->endpoints[i].direction != IN) + continue; + break; + } + usb_debug (" found endpoint %x for interrupt-in\n", i); + /* 20 buffers of 8 bytes, for every 10 msecs */ + HID_INST(dev)->queue = dev->controller->create_intr_queue (&dev->endpoints[i], 8, 20, 10); + keycount = 0; + usb_debug (" configuration done.\n"); + break; + default: + usb_debug("NOTICE: HID interface protocol %d%s not supported.\n", + interface->bInterfaceProtocol, + (interface->bInterfaceProtocol == hid_boot_proto_mouse ? + " (USB mouse)" : "")); + break; + } + } +} + +static int usbhid_havechar (void) +{ + return (keycount != 0); +} + +static int usbhid_getchar (void) +{ + short ret; + + if (keycount == 0) + return 0; + ret = keybuffer[0]; + memmove(keybuffer, keybuffer + 1, --keycount); + + return (int)ret; +} + +/* ( addr len -- actual ) */ +static void keyboard_read(void) +{ + char *addr; + int len, key, i; + + usb_poll(); + len=POP(); + addr=(char *)cell2pointer(POP()); + + for (i = 0; i < len; i++) { + if (!usbhid_havechar()) + break; + key = usbhid_getchar(); + *addr++ = (char)key; + } + PUSH(i); +} + +void ob_usb_hid_add_keyboard(const char *path) +{ + char name[128]; + phandle_t aliases; + + snprintf(name, sizeof(name), "%s/keyboard", path); + usb_debug("Found keyboard at %s\n", name); + REGISTER_NAMED_NODE(usb_kbd, name); + + push_str(name); + fword("find-device"); + + push_str("keyboard"); + fword("device-type"); + + aliases = find_dev("/aliases"); + set_property(aliases, "adb-keyboard", name, strlen(name) + 1); +} diff --git a/qemu/roms/openbios/drivers/usbohci.c b/qemu/roms/openbios/drivers/usbohci.c new file mode 100644 index 000000000..774164b0b --- /dev/null +++ b/qemu/roms/openbios/drivers/usbohci.c @@ -0,0 +1,926 @@ +/* + * Driver for USB OHCI ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2010 Patrick Georgi + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +//#define USB_DEBUG_ED + +#include "config.h" +#include <asm/io.h> +#include <libopenbios/ofmem.h> +#include "timer.h" +#include "drivers/pci.h" +#include "pci.h" +#include <drivers/usb.h> +#include "usbohci_private.h" +#include "usbohci.h" + +static void ohci_start (hci_t *controller); +static void ohci_stop (hci_t *controller); +static void ohci_reset (hci_t *controller); +static void ohci_shutdown (hci_t *controller); +static int ohci_bulk (endpoint_t *ep, int size, u8 *data, int finalize); +static int ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, + int dalen, u8 *data); +static void* ohci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming); +static void ohci_destroy_intr_queue (endpoint_t *ep, void *queue); +static u8* ohci_poll_intr_queue (void *queue); +static void ohci_process_done_queue(ohci_t *ohci, int spew_debug); + +#ifdef USB_DEBUG_ED +static void +dump_td (td_t *cur) +{ + usb_debug("+---------------------------------------------------+\n"); + if (((__le32_to_cpu(cur->config) & (3UL << 19)) >> 19) == 0) + usb_debug("|..[SETUP]..........................................|\n"); + else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 2) + usb_debug("|..[IN].............................................|\n"); + else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 1) + usb_debug("|..[OUT]............................................|\n"); + else + usb_debug("|..[]...............................................|\n"); + usb_debug("|:|============ OHCI TD at [0x%08lx] ==========|:|\n", virt_to_phys(cur)); + usb_debug("|:| ERRORS = [%ld] | CONFIG = [0x%08x] | |:|\n", + 3 - ((__le32_to_cpu(cur->config) & (3UL << 26)) >> 26), __le32_to_cpu(cur->config)); + usb_debug("|:+-----------------------------------------------+:|\n"); + usb_debug("|:| C | Condition Code | [%02ld] |:|\n", + (__le32_to_cpu(cur->config) & (0xFUL << 28)) >> 28); + usb_debug("|:| O | Direction/PID | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (3UL << 19)) >> 19); + usb_debug("|:| N | Buffer Rounding | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (1UL << 18)) >> 18); + usb_debug("|:| F | Delay Intterrupt | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (7UL << 21)) >> 21); + usb_debug("|:| I | Data Toggle | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (3UL << 24)) >> 24); + usb_debug("|:| G | Error Count | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (3UL << 26)) >> 26); + usb_debug("|:+-----------------------------------------------+:|\n"); + usb_debug("|:| Current Buffer Pointer [0x%08x] |:|\n", __le32_to_cpu(cur->current_buffer_pointer)); + usb_debug("|:+-----------------------------------------------+:|\n"); + usb_debug("|:| Next TD [0x%08x] |:|\n", __le32_to_cpu(cur->next_td)); + usb_debug("|:+-----------------------------------------------+:|\n"); + usb_debug("|:| Current Buffer End [0x%08x] |:|\n", __le32_to_cpu(cur->buffer_end)); + usb_debug("|:|-----------------------------------------------|:|\n"); + usb_debug("|...................................................|\n"); + usb_debug("+---------------------------------------------------+\n"); +} + +static void +dump_ed (ed_t *cur) +{ + td_t *tmp_td = NULL; + usb_debug("+===================================================+\n"); + usb_debug("| ############# OHCI ED at [0x%08lx] ########### |\n", virt_to_phys(cur)); + usb_debug("+---------------------------------------------------+\n"); + usb_debug("| Next Endpoint Descriptor [0x%08lx] |\n", __le32_to_cpu(cur->next_ed) & ~0xFUL); + usb_debug("+---------------------------------------------------+\n"); + usb_debug("| | @ 0x%08x : |\n", __le32_to_cpu(cur->config)); + usb_debug("| C | Maximum Packet Length | [%04ld] |\n", + ((__le32_to_cpu(cur->config) & (0x3fffUL << 16)) >> 16)); + usb_debug("| O | Function Address | [%04d] |\n", + __le32_to_cpu(cur->config) & 0x7F); + usb_debug("| N | Endpoint Number | [%02ld] |\n", + (__le32_to_cpu(cur->config) & (0xFUL << 7)) >> 7); + usb_debug("| F | Endpoint Direction | [%ld] |\n", + ((__le32_to_cpu(cur->config) & (3UL << 11)) >> 11)); + usb_debug("| I | Endpoint Speed | [%ld] |\n", + ((__le32_to_cpu(cur->config) & (1UL << 13)) >> 13)); + usb_debug("| G | Skip | [%ld] |\n", + ((__le32_to_cpu(cur->config) & (1UL << 14)) >> 14)); + usb_debug("| | Format | [%ld] |\n", + ((__le32_to_cpu(cur->config) & (1UL << 15)) >> 15)); + usb_debug("+---------------------------------------------------+\n"); + usb_debug("| TD Queue Tail Pointer [0x%08lx] |\n", + __le32_to_cpu(cur->tail_pointer) & ~0xFUL); + usb_debug("+---------------------------------------------------+\n"); + usb_debug("| TD Queue Head Pointer [0x%08lx] |\n", + __le32_to_cpu(cur->head_pointer) & ~0xFUL); + usb_debug("| CarryToggleBit [%d] Halted [%d] |\n", + (u16)(__le32_to_cpu(cur->head_pointer) & 0x2UL)>>1, (u16)(__le32_to_cpu(cur->head_pointer) & 0x1UL)); + + tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(cur->head_pointer) & ~0xFUL)); + if ((__le32_to_cpu(cur->head_pointer) & ~0xFUL) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL)) { + usb_debug("|:::::::::::::::::: OHCI TD CHAIN ::::::::::::::::::|\n"); + while (virt_to_phys(tmp_td) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL)) + { + dump_td(tmp_td); + tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(tmp_td->next_td) & ~0xFUL)); + } + usb_debug("|:::::::::::::::: EOF OHCI TD CHAIN ::::::::::::::::|\n"); + usb_debug("+---------------------------------------------------+\n"); + } else { + usb_debug("+---------------------------------------------------+\n"); + } +} +#endif + +static void +ohci_reset (hci_t *controller) +{ + if (controller == NULL) + return; + + OHCI_INST(controller)->opreg->HcCommandStatus = __cpu_to_le32(HostControllerReset); + mdelay(2); /* wait 2ms */ + OHCI_INST(controller)->opreg->HcControl = 0; + mdelay(10); /* wait 10ms */ +} + +static void +ohci_reinit (hci_t *controller) +{ +} + +hci_t * +ohci_init (void *bar) +{ + int i; + + hci_t *controller = new_controller (); + + if (!controller) { + printk("Could not create USB controller instance.\n"); + return NULL; + } + + controller->instance = malloc (sizeof (ohci_t)); + if(!controller->instance) { + printk("Not enough memory creating USB controller instance.\n"); + return NULL; + } + + controller->type = OHCI; + + controller->start = ohci_start; + controller->stop = ohci_stop; + controller->reset = ohci_reset; + controller->init = ohci_reinit; + controller->shutdown = ohci_shutdown; + controller->bulk = ohci_bulk; + controller->control = ohci_control; + controller->set_address = generic_set_address; + controller->finish_device_config = NULL; + controller->destroy_device = NULL; + controller->create_intr_queue = ohci_create_intr_queue; + controller->destroy_intr_queue = ohci_destroy_intr_queue; + controller->poll_intr_queue = ohci_poll_intr_queue; + for (i = 0; i < 128; i++) { + controller->devices[i] = 0; + } + init_device_entry (controller, 0); + OHCI_INST (controller)->roothub = controller->devices[0]; + + controller->reg_base = (u32)(unsigned long)bar; + OHCI_INST (controller)->opreg = (opreg_t*)phys_to_virt(controller->reg_base); + usb_debug("OHCI Version %x.%x\n", + (READ_OPREG(OHCI_INST(controller), HcRevision) >> 4) & 0xf, + READ_OPREG(OHCI_INST(controller), HcRevision) & 0xf); + + if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) == USBReset) { + /* cold boot */ + OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~RemoteWakeupConnected); + OHCI_INST (controller)->opreg->HcFmInterval = + __cpu_to_le32((11999 * FrameInterval) | ((((11999 - 210)*6)/7) * FSLargestDataPacket)); + /* TODO: right value for PowerOnToPowerGoodTime ? */ + OHCI_INST (controller)->opreg->HcRhDescriptorA = + __cpu_to_le32(NoPowerSwitching | NoOverCurrentProtection | (10 * PowerOnToPowerGoodTime)); + OHCI_INST (controller)->opreg->HcRhDescriptorB = __cpu_to_le32(0 * DeviceRemovable); + udelay(100); /* TODO: reset asserting according to USB spec */ + } else if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) != USBOperational) { + OHCI_INST (controller)->opreg->HcControl = + __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl) & ~HostControllerFunctionalStateMask) + | USBResume); + udelay(100); /* TODO: resume time according to USB spec */ + } + int interval = OHCI_INST (controller)->opreg->HcFmInterval; + + OHCI_INST (controller)->opreg->HcCommandStatus = __cpu_to_le32(HostControllerReset); + udelay (10); /* at most 10us for reset to complete. State must be set to Operational within 2ms (5.1.1.4) */ + OHCI_INST (controller)->opreg->HcFmInterval = interval; + ofmem_posix_memalign((void **)&(OHCI_INST (controller)->hcca), 256, 256); + memset((void*)OHCI_INST (controller)->hcca, 0, 256); + + usb_debug("HCCA addr %p\n", OHCI_INST(controller)->hcca); + /* Initialize interrupt table. */ + u32 *const intr_table = OHCI_INST(controller)->hcca->HccaInterruptTable; + ed_t *const periodic_ed; + ofmem_posix_memalign((void **)&periodic_ed, sizeof(ed_t), sizeof(ed_t)); + memset((void *)periodic_ed, 0, sizeof(*periodic_ed)); + for (i = 0; i < 32; ++i) + intr_table[i] = __cpu_to_le32(virt_to_phys(periodic_ed)); + OHCI_INST (controller)->periodic_ed = periodic_ed; + + OHCI_INST (controller)->opreg->HcHCCA = __cpu_to_le32(virt_to_phys(OHCI_INST(controller)->hcca)); + /* Make sure periodic schedule is enabled. */ + OHCI_INST (controller)->opreg->HcControl |= __cpu_to_le32(PeriodicListEnable); + OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~IsochronousEnable); // unused by this driver + // disable everything, contrary to what OHCI spec says in 5.1.1.4, as we don't need IRQs + OHCI_INST (controller)->opreg->HcInterruptEnable = __cpu_to_le32(1<<31); + OHCI_INST (controller)->opreg->HcInterruptDisable = __cpu_to_le32(~(1<<31)); + OHCI_INST (controller)->opreg->HcInterruptStatus = __cpu_to_le32(~0); + OHCI_INST (controller)->opreg->HcPeriodicStart = + __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcFmInterval) & FrameIntervalMask) / 10 * 9); + OHCI_INST (controller)->opreg->HcControl = __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl) + & ~HostControllerFunctionalStateMask) | USBOperational); + + mdelay(100); + + controller->devices[0]->controller = controller; + controller->devices[0]->init = ohci_rh_init; + controller->devices[0]->init (controller->devices[0]); + return controller; +} + +hci_t * +ohci_pci_init (pci_addr addr) +{ + u32 reg_base; + uint16_t cmd; + + cmd = pci_config_read16(addr, PCI_COMMAND); + cmd |= PCI_COMMAND_BUS_MASTER; + pci_config_write16(addr, PCI_COMMAND, cmd); + + /* regarding OHCI spec, Appendix A, BAR_OHCI register description, Table A-4 + * BASE ADDRESS only [31-12] bits. All other usually 0, but not all. + * OHCI mandates MMIO, so bit 0 is clear */ + reg_base = pci_config_read32 (addr, PCI_BASE_ADDR_0) & 0xfffff000; + + return ohci_init((void *)(unsigned long)reg_base); +} + +static void +ohci_shutdown (hci_t *controller) +{ + if (controller == 0) + return; + detach_controller (controller); + ohci_stop(controller); + OHCI_INST (controller)->roothub->destroy (OHCI_INST (controller)-> + roothub); + controller->reset (controller); + free ((void *)OHCI_INST (controller)->periodic_ed); + free (OHCI_INST (controller)); + free (controller); +} + +static void +ohci_start (hci_t *controller) +{ +// TODO: turn on all operation of OHCI, but assume that it's initialized. +} + +static void +ohci_stop (hci_t *controller) +{ +// TODO: turn off all operation of OHCI +} + +static int +wait_for_ed(usbdev_t *dev, ed_t *head, int pages) +{ + usb_debug("Waiting for %d pages on dev %p with head %p\n", pages, dev, head); + /* wait for results */ + /* TOTEST: how long to wait? + * give 2s per TD (2 pages) plus another 2s for now + */ + int timeout = pages*1000 + 2000; + while (((__le32_to_cpu(head->head_pointer) & ~3) != __le32_to_cpu(head->tail_pointer)) && + !(__le32_to_cpu(head->head_pointer) & 1) && + ((__le32_to_cpu((((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3)))->config) + & TD_CC_MASK) >= TD_CC_NOACCESS) && timeout--) { + /* don't log every ms */ + if (!(timeout % 100)) + usb_debug("intst: %x; ctrl: %x; cmdst: %x; head: %x -> %x, tail: %x, condition: %x\n", + READ_OPREG(OHCI_INST(dev->controller), HcInterruptStatus), + READ_OPREG(OHCI_INST(dev->controller), HcControl), + READ_OPREG(OHCI_INST(dev->controller), HcCommandStatus), + __le32_to_cpu(head->head_pointer), + __le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->next_td), + __le32_to_cpu(head->tail_pointer), + (__le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->config) & TD_CC_MASK) >> TD_CC_SHIFT); + mdelay(1); + } + if (timeout < 0) + usb_debug("Error: ohci: endpoint " + "descriptor processing timed out.\n"); + /* Clear the done queue. */ + ohci_process_done_queue(OHCI_INST(dev->controller), 1); + + if (__le32_to_cpu(head->head_pointer) & 1) { + usb_debug("HALTED!\n"); + return 1; + } + return 0; +} + +static void +ohci_free_ed (ed_t *const head) +{ + /* In case the transfer canceled, we have to free unprocessed TDs. */ + while ((__le32_to_cpu(head->head_pointer) & ~0x3) != __le32_to_cpu(head->tail_pointer)) { + /* Save current TD pointer. */ + td_t *const cur_td = + (td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3); + /* Advance head pointer. */ + head->head_pointer = cur_td->next_td; + /* Free current TD. */ + free((void *)cur_td); + } + + /* Always free the dummy TD */ + if ((__le32_to_cpu(head->head_pointer) & ~0x3) == __le32_to_cpu(head->tail_pointer)) + free(phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3)); + /* and the ED. */ + free((void *)head); +} + +static int +ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen, + unsigned char *data) +{ + td_t *cur; + + // pages are specified as 4K in OHCI, so don't use getpagesize() + int first_page = (unsigned long)data / 4096; + int last_page = (unsigned long)(data+dalen-1)/4096; + if (last_page < first_page) last_page = first_page; + int pages = (dalen==0)?0:(last_page - first_page + 1); + + /* First TD. */ + td_t *const first_td; + ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t)); + memset((void *)first_td, 0, sizeof(*first_td)); + cur = first_td; + + cur->config = __cpu_to_le32(TD_DIRECTION_SETUP | + TD_DELAY_INTERRUPT_NOINTR | + TD_TOGGLE_FROM_TD | + TD_TOGGLE_DATA0 | + TD_CC_NOACCESS); + cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(devreq)); + cur->buffer_end = __cpu_to_le32(virt_to_phys((char *)devreq + drlen - 1)); + + while (pages > 0) { + /* One more TD. */ + td_t *const next; + ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t)); + memset((void *)next, 0, sizeof(*next)); + /* Linked to the previous. */ + cur->next_td = __cpu_to_le32(virt_to_phys(next)); + /* Advance to the new TD. */ + cur = next; + + cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) | + TD_DELAY_INTERRUPT_NOINTR | + TD_TOGGLE_FROM_ED | + TD_CC_NOACCESS); + cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(data)); + pages--; + int consumed = (4096 - ((unsigned long)data % 4096)); + if (consumed >= dalen) { + // end of data is within same page + cur->buffer_end = __cpu_to_le32(virt_to_phys(data + dalen - 1)); + dalen = 0; + /* assert(pages == 0); */ + } else { + dalen -= consumed; + data += consumed; + pages--; + int second_page_size = dalen; + if (dalen > 4096) { + second_page_size = 4096; + } + cur->buffer_end = __cpu_to_le32(virt_to_phys(data + second_page_size - 1)); + dalen -= second_page_size; + data += second_page_size; + } + } + + /* One more TD. */ + td_t *const next_td; + ofmem_posix_memalign((void **)&next_td, sizeof(td_t), sizeof(td_t)); + memset((void *)next_td, 0, sizeof(*next_td)); + /* Linked to the previous. */ + cur->next_td = __cpu_to_le32(virt_to_phys(next_td)); + /* Advance to the new TD. */ + cur = next_td; + cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_OUT : TD_DIRECTION_IN) | + TD_DELAY_INTERRUPT_ZERO | /* Write done head after this TD. */ + TD_TOGGLE_FROM_TD | + TD_TOGGLE_DATA1 | + TD_CC_NOACCESS); + cur->current_buffer_pointer = 0; + cur->buffer_end = 0; + + /* Final dummy TD. */ + td_t *const final_td; + ofmem_posix_memalign((void **)&final_td, sizeof(td_t), sizeof(td_t)); + memset((void *)final_td, 0, sizeof(*final_td)); + /* Linked to the previous. */ + cur->next_td = __cpu_to_le32(virt_to_phys(final_td)); + + /* Data structures */ + ed_t *head; + ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t)); + memset((void*)head, 0, sizeof(*head)); + head->config = __cpu_to_le32((dev->address << ED_FUNC_SHIFT) | + (0 << ED_EP_SHIFT) | + (OHCI_FROM_TD << ED_DIR_SHIFT) | + (dev->speed?ED_LOWSPEED:0) | + (dev->endpoints[0].maxpacketsize << ED_MPS_SHIFT)); + head->tail_pointer = __cpu_to_le32(virt_to_phys(final_td)); + head->head_pointer = __cpu_to_le32(virt_to_phys(first_td)); + + usb_debug("ohci_control(): doing transfer with %x. first_td at %x\n", + __le32_to_cpu(head->config) & ED_FUNC_MASK, __le32_to_cpu(head->head_pointer)); +#ifdef USB_DEBUG_ED + dump_ed(head); +#endif + + /* activate schedule */ + OHCI_INST(dev->controller)->opreg->HcControlHeadED = __cpu_to_le32(virt_to_phys(head)); + OHCI_INST(dev->controller)->opreg->HcControl |= __cpu_to_le32(ControlListEnable); + OHCI_INST(dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(ControlListFilled); + + int failure = wait_for_ed(dev, head, + (dalen==0)?0:(last_page - first_page + 1)); + /* Wait some frames before and one after disabling list access. */ + mdelay(4); + OHCI_INST(dev->controller)->opreg->HcControl &= __cpu_to_le32(~ControlListEnable); + mdelay(1); + + /* free memory */ + ohci_free_ed(head); + + return failure; +} + +/* finalize == 1: if data is of packet aligned size, add a zero length packet */ +static int +ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize) +{ + int i; + usb_debug("bulk: %x bytes from %p, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize); + + td_t *cur, *next; + + // pages are specified as 4K in OHCI, so don't use getpagesize() + int first_page = (unsigned long)data / 4096; + int last_page = (unsigned long)(data+dalen-1)/4096; + if (last_page < first_page) last_page = first_page; + int pages = (dalen==0)?0:(last_page - first_page + 1); + int td_count = (pages+1)/2; + + if (finalize && ((dalen % ep->maxpacketsize) == 0)) { + td_count++; + } + + /* First TD. */ + td_t *const first_td; + ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t)); + memset((void *)first_td, 0, sizeof(*first_td)); + cur = next = first_td; + + for (i = 0; i < td_count; ++i) { + /* Advance to next TD. */ + cur = next; + cur->config = __cpu_to_le32((ep->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) | + TD_DELAY_INTERRUPT_NOINTR | + TD_TOGGLE_FROM_ED | + TD_CC_NOACCESS); + cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(data)); + pages--; + if (dalen == 0) { + /* magic TD for empty packet transfer */ + cur->current_buffer_pointer = 0; + cur->buffer_end = 0; + /* assert((pages == 0) && finalize); */ + } + int consumed = (4096 - ((unsigned long)data % 4096)); + if (consumed >= dalen) { + // end of data is within same page + cur->buffer_end = __cpu_to_le32(virt_to_phys(data + dalen - 1)); + dalen = 0; + /* assert(pages == finalize); */ + } else { + dalen -= consumed; + data += consumed; + pages--; + int second_page_size = dalen; + if (dalen > 4096) { + second_page_size = 4096; + } + cur->buffer_end = __cpu_to_le32(virt_to_phys(data + second_page_size - 1)); + dalen -= second_page_size; + data += second_page_size; + } + /* One more TD. */ + ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t)); + memset((void *)next, 0, sizeof(*next)); + /* Linked to the previous. */ + cur->next_td = __cpu_to_le32(virt_to_phys(next)); + } + + /* Write done head after last TD. */ + cur->config &= __cpu_to_le32(~TD_DELAY_INTERRUPT_MASK); + /* Advance to final, dummy TD. */ + cur = next; + + /* Data structures */ + ed_t *head; + ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t)); + memset((void*)head, 0, sizeof(*head)); + head->config = __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) | + ((ep->endpoint & 0xf) << ED_EP_SHIFT) | + (((ep->direction==IN)?OHCI_IN:OHCI_OUT) << ED_DIR_SHIFT) | + (ep->dev->speed?ED_LOWSPEED:0) | + (ep->maxpacketsize << ED_MPS_SHIFT)); + head->tail_pointer = __cpu_to_le32(virt_to_phys(cur)); + head->head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle?ED_TOGGLE:0)); + + usb_debug("doing bulk transfer with %x(%x). first_td at %lx, last %lx\n", + __le32_to_cpu(head->config) & ED_FUNC_MASK, + (__le32_to_cpu(head->config) & ED_EP_MASK) >> ED_EP_SHIFT, + virt_to_phys(first_td), virt_to_phys(cur)); + + /* activate schedule */ + OHCI_INST(ep->dev->controller)->opreg->HcBulkHeadED = __cpu_to_le32(virt_to_phys(head)); + OHCI_INST(ep->dev->controller)->opreg->HcControl |= __cpu_to_le32(BulkListEnable); + OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(BulkListFilled); + + int failure = wait_for_ed(ep->dev, head, + (dalen==0)?0:(last_page - first_page + 1)); + /* Wait some frames before and one after disabling list access. */ + mdelay(4); + OHCI_INST(ep->dev->controller)->opreg->HcControl &= __cpu_to_le32(~BulkListEnable); + mdelay(1); + + ep->toggle = __le32_to_cpu(head->head_pointer) & ED_TOGGLE; + + /* free memory */ + ohci_free_ed(head); + + if (failure) { + /* try cleanup */ + clear_stall(ep); + } + + return failure; +} + + +struct _intr_queue; + +struct _intrq_td { + volatile td_t td; + u8 *data; + struct _intrq_td *next; + struct _intr_queue *intrq; +}; + +struct _intr_queue { + volatile ed_t ed; + struct _intrq_td *head; + struct _intrq_td *tail; + u8 *data; + int reqsize; + endpoint_t *endp; + unsigned int remaining_tds; + int destroy; +}; + +typedef struct _intrq_td intrq_td_t; +typedef struct _intr_queue intr_queue_t; + +#define INTRQ_TD_FROM_TD(x) ((intrq_td_t *)x) + +static void +ohci_fill_intrq_td(intrq_td_t *const td, intr_queue_t *const intrq, + u8 *const data) +{ + memset(td, 0, sizeof(*td)); + td->td.config = __cpu_to_le32(TD_QUEUETYPE_INTR | + (intrq->endp->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) | + TD_DELAY_INTERRUPT_ZERO | + TD_TOGGLE_FROM_ED | + TD_CC_NOACCESS); + td->td.current_buffer_pointer = __cpu_to_le32(virt_to_phys(data)); + td->td.buffer_end = __cpu_to_le32(virt_to_phys(data) + intrq->reqsize - 1); + td->intrq = intrq; + td->data = data; +} + +/* create and hook-up an intr queue into device schedule */ +static void * +ohci_create_intr_queue(endpoint_t *const ep, const int reqsize, + const int reqcount, const int reqtiming) +{ + int i; + intrq_td_t *first_td = NULL, *last_td = NULL; + + if (reqsize > 4096) + return NULL; + + intr_queue_t *const intrq; + ofmem_posix_memalign((void **)&intrq, sizeof(intrq->ed), sizeof(*intrq)); + memset(intrq, 0, sizeof(*intrq)); + intrq->data = (u8 *)malloc(reqcount * reqsize); + intrq->reqsize = reqsize; + intrq->endp = ep; + + /* Create #reqcount TDs. */ + u8 *cur_data = intrq->data; + for (i = 0; i < reqcount; ++i) { + intrq_td_t *const td; + ofmem_posix_memalign((void **)&td, sizeof(td->td), sizeof(*td)); + ++intrq->remaining_tds; + ohci_fill_intrq_td(td, intrq, cur_data); + cur_data += reqsize; + if (!first_td) + first_td = td; + else + last_td->td.next_td = __cpu_to_le32(virt_to_phys(&td->td)); + last_td = td; + } + + /* Create last, dummy TD. */ + intrq_td_t *dummy_td; + ofmem_posix_memalign((void **)&dummy_td, sizeof(dummy_td->td), sizeof(*dummy_td)); + memset(dummy_td, 0, sizeof(*dummy_td)); + dummy_td->intrq = intrq; + if (last_td) + last_td->td.next_td = __cpu_to_le32(virt_to_phys(&dummy_td->td)); + last_td = dummy_td; + + /* Initialize ED. */ + intrq->ed.config = __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) | + ((ep->endpoint & 0xf) << ED_EP_SHIFT) | + (((ep->direction == IN) ? OHCI_IN : OHCI_OUT) << ED_DIR_SHIFT) | + (ep->dev->speed ? ED_LOWSPEED : 0) | + (ep->maxpacketsize << ED_MPS_SHIFT)); + intrq->ed.tail_pointer = __cpu_to_le32(virt_to_phys(last_td)); + intrq->ed.head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle ? ED_TOGGLE : 0)); + +#ifdef USB_DEBUG_ED + dump_ed(&intrq->ed); +#endif + /* Insert ED into periodic table. */ + int nothing_placed = 1; + ohci_t *const ohci = OHCI_INST(ep->dev->controller); + u32 *const intr_table = ohci->hcca->HccaInterruptTable; + const u32 dummy_ptr = __cpu_to_le32(virt_to_phys(ohci->periodic_ed)); + for (i = 0; i < 32; i += reqtiming) { + /* Advance to the next free position. */ + while ((i < 32) && (intr_table[i] != dummy_ptr)) ++i; + if (i < 32) { + usb_debug("Placed endpoint %lx to %d\n", virt_to_phys(&intrq->ed), i); + intr_table[i] = __cpu_to_le32(virt_to_phys(&intrq->ed)); + nothing_placed = 0; + } + } + if (nothing_placed) { + usb_debug("Error: Failed to place ohci interrupt endpoint " + "descriptor into periodic table: no space left\n"); + ohci_destroy_intr_queue(ep, intrq); + return NULL; + } + + return intrq; +} + +/* remove queue from device schedule, dropping all data that came in */ +static void +ohci_destroy_intr_queue(endpoint_t *const ep, void *const q_) +{ + intr_queue_t *const intrq = (intr_queue_t *)q_; + + int i; + + /* Remove interrupt queue from periodic table. */ + ohci_t *const ohci = OHCI_INST(ep->dev->controller); + u32 *const intr_table = ohci->hcca->HccaInterruptTable; + for (i=0; i < 32; ++i) { + if (intr_table[i] == __cpu_to_le32(virt_to_phys(intrq))) + intr_table[i] = __cpu_to_le32(virt_to_phys(ohci->periodic_ed)); + } + /* Wait for frame to finish. */ + mdelay(1); + + /* Free unprocessed TDs. */ + while ((__le32_to_cpu(intrq->ed.head_pointer) & ~0x3) != __le32_to_cpu(intrq->ed.tail_pointer)) { + td_t *const cur_td = (td_t *)phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3); + intrq->ed.head_pointer = cur_td->next_td; + free(INTRQ_TD_FROM_TD(cur_td)); + --intrq->remaining_tds; + } + /* Free final, dummy TD. */ + free(phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3)); + /* Free data buffer. */ + free(intrq->data); + + /* Free TDs already fetched from the done queue. */ + ohci_process_done_queue(ohci, 1); + while (intrq->head) { + intrq_td_t *const cur_td = (intrq_td_t *const )__le32_to_cpu(intrq->head); + intrq->head = intrq->head->next; + free(cur_td); + --intrq->remaining_tds; + } + + /* Mark interrupt queue to be destroyed. + ohci_process_done_queue() will free the remaining TDs + and finish the interrupt queue off once all TDs are gone. */ + intrq->destroy = 1; + + /* Save data toggle. */ + ep->toggle = __le32_to_cpu(intrq->ed.head_pointer) & ED_TOGGLE; +} + +/* read one intr-packet from queue, if available. extend the queue for new input. + return NULL if nothing new available. + Recommended use: while (data=poll_intr_queue(q)) process(data); + */ +static u8 * +ohci_poll_intr_queue(void *const q_) +{ + intr_queue_t *const intrq = (intr_queue_t *)q_; + + u8 *data = NULL; + + /* Process done queue first, then check if we have work to do. */ + ohci_process_done_queue(OHCI_INST(intrq->endp->dev->controller), 0); + + if (intrq->head) { + /* Save pointer to processed TD and advance. */ + intrq_td_t *const cur_td = intrq->head; + intrq->head = cur_td->next; + + /* Return data buffer of this TD. */ + data = cur_td->data; + + /* Requeue this TD (i.e. copy to dummy and requeue as dummy). */ + intrq_td_t *const dummy_td = + INTRQ_TD_FROM_TD(phys_to_virt(__le32_to_cpu(intrq->ed.tail_pointer))); + ohci_fill_intrq_td(dummy_td, intrq, data); + /* Reset all but intrq pointer (i.e. init as dummy). */ + memset(cur_td, 0, sizeof(*cur_td)); + cur_td->intrq = intrq; + /* Insert into interrupt queue as dummy. */ + dummy_td->td.next_td = __le32_to_cpu(virt_to_phys(&cur_td->td)); + intrq->ed.tail_pointer = __le32_to_cpu(virt_to_phys(&cur_td->td)); + } + + return data; +} + +static void +ohci_process_done_queue(ohci_t *const ohci, const int spew_debug) +{ + int i, j; + + /* Temporary queue of interrupt queue TDs (to reverse order). */ + intrq_td_t *temp_tdq = NULL; + + /* Check if done head has been written. */ + if (!(READ_OPREG(ohci, HcInterruptStatus) & WritebackDoneHead)) + return; + /* Fetch current done head. + Lsb is only interesting for hw interrupts. */ + u32 phys_done_queue = __le32_to_cpu(ohci->hcca->HccaDoneHead) & ~1; + /* Tell host controller, he may overwrite the done head pointer. */ + ohci->opreg->HcInterruptStatus = __cpu_to_le32(WritebackDoneHead); + + i = 0; + /* Process done queue (it's in reversed order). */ + while (phys_done_queue) { + td_t *const done_td = (td_t *)phys_to_virt(phys_done_queue); + + /* Advance pointer to next TD. */ + phys_done_queue = __le32_to_cpu(done_td->next_td); + + switch (__le32_to_cpu(done_td->config) & TD_QUEUETYPE_MASK) { + case TD_QUEUETYPE_ASYNC: + /* Free processed async TDs. */ + free((void *)done_td); + break; + case TD_QUEUETYPE_INTR: { + intrq_td_t *const td = INTRQ_TD_FROM_TD(done_td); + intr_queue_t *const intrq = td->intrq; + /* Check if the corresponding interrupt + queue is still beeing processed. */ + if (intrq->destroy) { + /* Free this TD, and */ + free(td); + --intrq->remaining_tds; + /* the interrupt queue if it has no more TDs. */ + if (!intrq->remaining_tds) + free(intrq); + usb_debug("Freed TD from orphaned interrupt " + "queue, %d TDs remain.\n", + intrq->remaining_tds); + } else { + /* Save done TD to be processed. */ + td->next = temp_tdq; + temp_tdq = td; + } + break; + } + default: + break; + } + ++i; + } + if (spew_debug) + usb_debug("Processed %d done TDs.\n", i); + + j = 0; + /* Process interrupt queue TDs in right order. */ + while (temp_tdq) { + /* Save pointer of current TD and advance. */ + intrq_td_t *const cur_td = temp_tdq; + temp_tdq = temp_tdq->next; + + /* The interrupt queue for the current TD. */ + intr_queue_t *const intrq = cur_td->intrq; + /* Append to interrupt queue. */ + if (!intrq->head) { + /* First element. */ + intrq->head = intrq->tail = cur_td; + } else { + /* Insert at tail. */ + intrq->tail->next = cur_td; + intrq->tail = cur_td; + } + /* It's always the last element. */ + cur_td->next = NULL; + ++j; + } + if (spew_debug) + usb_debug("processed %d done tds, %d intr tds thereof.\n", i, j); +} + +int ob_usb_ohci_init (const char *path, uint32_t addr) +{ + hci_t *ctrl; + int i; + + usb_debug("ohci_init: %s addr = %x\n", path, addr); + ctrl = ohci_pci_init(addr); + if (!ctrl) + return 0; + + /* Init ports */ + usb_poll(); + + /* Look for a keyboard */ + for (i = 0; i < 128; i++) { + if (ctrl->devices[i] && ctrl->devices[i]->configuration) { + configuration_descriptor_t *cd; + interface_descriptor_t *intf; + + cd = (configuration_descriptor_t *)ctrl->devices[i]->configuration; + intf = (interface_descriptor_t *)(ctrl->devices[i]->configuration + cd->bLength); + usb_debug("Device at port %d is class %d\n", i, intf->bInterfaceClass); + if (intf->bInterfaceClass == hid_device && + intf->bInterfaceSubClass == hid_subclass_boot && + intf->bInterfaceProtocol == hid_boot_proto_keyboard ) { + break; + } + } + } + if ( i < 128 ) + ob_usb_hid_add_keyboard(path); + + return 1; +} diff --git a/qemu/roms/openbios/drivers/usbohci.h b/qemu/roms/openbios/drivers/usbohci.h new file mode 100644 index 000000000..690332871 --- /dev/null +++ b/qemu/roms/openbios/drivers/usbohci.h @@ -0,0 +1,45 @@ +/* + * Driver for USB OHCI ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2010 Patrick Georgi + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +#ifndef __OHCI_H +#define __OHCI_H + +#include "config.h" +#include "usbohci_private.h" + +hci_t *ohci_pci_init (u32 addr); +hci_t *ohci_init (void *bar); + +void ohci_rh_init (usbdev_t *dev); + +#endif diff --git a/qemu/roms/openbios/drivers/usbohci_private.h b/qemu/roms/openbios/drivers/usbohci_private.h new file mode 100644 index 000000000..b3a723e21 --- /dev/null +++ b/qemu/roms/openbios/drivers/usbohci_private.h @@ -0,0 +1,270 @@ +/* + * Driver for USB OHCI ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2010 Patrick Georgi + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +#ifndef __OHCI_PRIVATE_H +#define __OHCI_PRIVATE_H + +#include "libc/byteorder.h" +#include "usb.h" + +#define READ_OPREG(ohci, field) (__le32_to_cpu((ohci)->opreg->field)) +#define MASK(startbit, lenbit) (((1<<(lenbit))-1)<<(startbit)) + + // FIXME: fake + typedef enum { CMD} reg; + + enum { + NumberDownstreamPorts = 1<<0, + PowerSwitchingMode = 1<<8, + NoPowerSwitching = 1<<9, + DeviceType = 1<<10, + OverCurrentProtectionMode = 1<<11, + NoOverCurrentProtection = 1<<12, + PowerOnToPowerGoodTime = 1<<24 + } HcRhDescriptorAReg; + + enum { + NumberDownstreamPortsMask = MASK(0, 8), + PowerOnToPowerGoodTimeMask = MASK(24, 8) + } HcRhDescriptorAMask; + + enum { + DeviceRemovable = 1<<0, + PortPowerControlMask = 1<<16 + } HcRhDescriptorBReg; + + enum { + CurrentConnectStatus = 1<<0, + PortEnableStatus = 1<<1, + PortSuspendStatus = 1<<2, + PortOverCurrentIndicator = 1<<3, + PortResetStatus = 1<<4, + PortPowerStatus = 1<<8, + LowSpeedDeviceAttached = 1<<9, + ConnectStatusChange = 1<<16, + PortEnableStatusChange = 1<<17, + PortSuspendStatusChange = 1<<18, + PortOverCurrentIndicatorChange = 1<<19, + PortResetStatusChange = 1<<20 + } HcRhPortStatusRead; + enum { + ClearPortEnable = 1<<0, + SetPortEnable = 1<<1, + SetPortSuspend = 1<<2, + ClearSuspendStatus = 1<<3, + SetPortReset = 1<<4, + SetPortPower = 1<<8, + ClearPortPower = 1<<9, + } HcRhPortStatusSet; + + enum { + LocalPowerStatus = 1<<0, + OverCurrentIndicator = 1<<1, + DeviceRemoteWakeupEnable = 1<<15, + LocalPowerStatusChange = 1<<16, + OverCurrentIndicatorChange = 1<<17, + ClearRemoteWakeupEnable = 1<<31 + } HcRhStatusReg; + + enum { + FrameInterval = 1<<0, + FSLargestDataPacket = 1<<16, + FrameIntervalToggle = 1<<31 + } HcFmIntervalOffset; + enum { + FrameIntervalMask = MASK(0, 14), + FSLargestDataPacketMask = MASK(16, 15), + FrameIntervalToggleMask = MASK(31, 1) + } HcFmIntervalMask; + + enum { + ControlBulkServiceRatio = 1<<0, + PeriodicListEnable = 1<<2, + IsochronousEnable = 1<<3, + ControlListEnable = 1<<4, + BulkListEnable = 1<<5, + HostControllerFunctionalState = 1<<6, + InterruptRouting = 1<<8, + RemoteWakeupConnected = 1<<9, + RemoteWakeupEnable = 1<<10 + } HcControlReg; + + enum { + ControlBulkServiceRatioMask = MASK(0, 2), + HostControllerFunctionalStateMask = MASK(6, 2) + } HcControlMask; + + enum { + USBReset = 0*HostControllerFunctionalState, + USBResume = 1*HostControllerFunctionalState, + USBOperational = 2*HostControllerFunctionalState, + USBSuspend = 3*HostControllerFunctionalState + }; + + enum { + HostControllerReset = 1<<0, + ControlListFilled = 1<<1, + BulkListFilled = 1<<2, + OwnershipChangeRequest = 1<<3, + SchedulingOverrunCount = 1<<16 + } HcCommandStatusReg; + + enum { + SchedulingOverrunCountMask = MASK(16, 2) + } HcCommandStatusMask; + + enum { + FrameRemaining = 1<<0, + FrameRemainingToggle = 1<<31 + } HcFmRemainingReg; + + enum { + SchedulingOverrung = 1<<0, + WritebackDoneHead = 1<<1, + StartofFrame = 1<<2, + ResumeDetected = 1<<3, + UnrecoverableError = 1<<4, + FrameNumberOverflow = 1<<5, + RootHubStatusChange = 1<<6, + OwnershipChange = 1<<30 + } HcInterruptStatusReg; + + typedef struct { + // Control and Status Partition + volatile u32 HcRevision; + volatile u32 HcControl; + volatile u32 HcCommandStatus; + volatile u32 HcInterruptStatus; + volatile u32 HcInterruptEnable; + volatile u32 HcInterruptDisable; + + // Memory Pointer Partition + volatile u32 HcHCCA; + volatile u32 HcPeriodCurrentED; + volatile u32 HcControlHeadED; + volatile u32 HcControlCurrentED; + volatile u32 HcBulkHeadED; + volatile u32 HcBulkCurrentED; + volatile u32 HcDoneHead; + + // Frame Counter Partition + volatile u32 HcFmInterval; + volatile u32 HcFmRemaining; + volatile u32 HcFmNumber; + volatile u32 HcPeriodicStart; + volatile u32 HcLSThreshold; + + // Root Hub Partition + volatile u32 HcRhDescriptorA; + volatile u32 HcRhDescriptorB; + volatile u32 HcRhStatus; + /* all bits in HcRhPortStatus registers are R/WC, so + _DO NOT_ use |= to set the bits, + this clears the entire state */ + volatile u32 HcRhPortStatus[]; + } __attribute__ ((packed)) opreg_t; + + typedef struct { /* should be 256 bytes according to spec */ + u32 HccaInterruptTable[32]; + volatile u16 HccaFrameNumber; + volatile u16 HccaPad1; + volatile u32 HccaDoneHead; + u8 reserved[116]; /* pad according to spec */ + u8 what[4]; /* really pad to 256 as spec only covers 252 */ + } __attribute__ ((packed)) hcca_t; + + typedef volatile struct { + u32 config; + u32 tail_pointer; + u32 head_pointer; + u32 next_ed; + } __attribute__ ((packed)) ed_t; +#define ED_HALTED 1 +#define ED_TOGGLE 2 + +#define ED_FUNC_SHIFT 0 +#define ED_FUNC_MASK MASK(0, 7) +#define ED_EP_SHIFT 7 +#define ED_EP_MASK MASK(7, 4) +#define ED_DIR_SHIFT 11 +#define ED_DIR_MASK MASK(11, 2) +#define ED_LOWSPEED (1 << 13) +#define ED_MPS_SHIFT 16 + + typedef volatile struct { + u32 config; + u32 current_buffer_pointer; + u32 next_td; + u32 buffer_end; + } __attribute__ ((packed)) td_t; +/* + * Bits 0 through 17 of .config won't be interpreted by the host controller + * (HC) and, after processing the TD, the HC has to ensure those bits have + * the same state as before. So we are free to use those bits for our own + * purpose. + */ +#define TD_QUEUETYPE_SHIFT 0 +#define TD_QUEUETYPE_MASK MASK(TD_QUEUETYPE_SHIFT, 2) +#define TD_QUEUETYPE_ASYNC (0 << TD_QUEUETYPE_SHIFT) +#define TD_QUEUETYPE_INTR (1 << TD_QUEUETYPE_SHIFT) + +#define TD_DIRECTION_SHIFT 19 +#define TD_DIRECTION_MASK MASK(TD_DIRECTION_SHIFT, 2) +#define TD_DIRECTION_SETUP OHCI_SETUP << TD_DIRECTION_SHIFT +#define TD_DIRECTION_IN OHCI_IN << TD_DIRECTION_SHIFT +#define TD_DIRECTION_OUT OHCI_OUT << TD_DIRECTION_SHIFT +#define TD_DELAY_INTERRUPT_SHIFT 21 +#define TD_DELAY_INTERRUPT_MASK MASK(TD_DELAY_INTERRUPT_SHIFT, 3) +#define TD_DELAY_INTERRUPT_ZERO 0 +#define TD_DELAY_INTERRUPT_NOINTR (7 << TD_DELAY_INTERRUPT_SHIFT) +#define TD_TOGGLE_DATA0 0 +#define TD_TOGGLE_DATA1 (1 << 24) +#define TD_TOGGLE_FROM_ED 0 +#define TD_TOGGLE_FROM_TD (1 << 25) +#define TD_CC_SHIFT 28 +#define TD_CC_MASK MASK(TD_CC_SHIFT, 4) +#define TD_CC_NOERR 0 +#define TD_CC_NOACCESS (14 << TD_CC_SHIFT) /* the lower of the two values, so "no access" can be tested with >= */ + +#define OHCI_INST(controller) ((ohci_t*)((controller)->instance)) + + typedef struct ohci { + opreg_t *opreg; + hcca_t *hcca; + usbdev_t *roothub; + ed_t *periodic_ed; + } ohci_t; + + typedef enum { OHCI_SETUP=0, OHCI_OUT=1, OHCI_IN=2, OHCI_FROM_TD=3 } ohci_pid_t; + +#endif diff --git a/qemu/roms/openbios/drivers/usbohci_rh.c b/qemu/roms/openbios/drivers/usbohci_rh.c new file mode 100644 index 000000000..55503be61 --- /dev/null +++ b/qemu/roms/openbios/drivers/usbohci_rh.c @@ -0,0 +1,212 @@ +/* + * Driver for USB OHCI Root Hubs ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2010 Patrick Georgi + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 "config.h" +#include "timer.h" +#include "usbohci_private.h" +#include "usbohci.h" + +typedef struct { + int numports; + int *port; +} rh_inst_t; + +#define RH_INST(dev) ((rh_inst_t*)(dev)->data) + +static void +ohci_rh_enable_port (usbdev_t *dev, int port) +{ + /* Reset RH port should hold 50ms with pulses of at least 10ms and + * gaps of at most 3ms (usb20 spec 7.1.7.5). + * After reset, the port will be enabled automatically (ohci spec + * 7.4.4). + */ + int total_delay = 100; /* 100 * 500us == 50ms */ + while (total_delay > 0) { + if (!(READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) + & CurrentConnectStatus)) + return; + + /* start reset */ + OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = + __cpu_to_le32(SetPortReset); + int timeout = 200; /* timeout after 200 * 500us == 100ms */ + while ((READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) + & PortResetStatus) + && timeout--) { + udelay(500); total_delay--; + } + if (READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) + & PortResetStatus) { + usb_debug("Warning: root-hub port reset timed out.\n"); + break; + } + if ((200-timeout) < 20) { + usb_debug("Warning: port reset too short: %dms; " + "should be at least 10ms.\n", + (200-timeout)/2); + total_delay = 0; /* can happen on QEMU */ + } + /* clear reset status change */ + OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] = + __cpu_to_le32(PortResetStatusChange); + usb_debug ("rh port reset finished after %dms.\n", (200-timeout)/2); + } +} + +/* disable root hub */ +static void +ohci_rh_disable_port (usbdev_t *dev, int port) +{ + OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = + __cpu_to_le32(ClearPortEnable); // disable port + int timeout = 50; /* timeout after 50 * 100us == 5ms */ + while ((READ_OPREG(OHCI_INST (dev->controller), HcRhPortStatus[port]) + & PortEnableStatus) + && timeout--) { + udelay(100); + } +} + +static void +ohci_rh_scanport (usbdev_t *dev, int port) +{ + if (port >= RH_INST(dev)->numports) { + usb_debug("Invalid port %d\n", port); + return; + } + + /* device registered, and device change logged, so something must have happened */ + if (RH_INST (dev)->port[port] != -1) { + usb_detach_device(dev->controller, RH_INST (dev)->port[port]); + RH_INST (dev)->port[port] = -1; + } + + /* no device attached + previously registered devices are detached, nothing left to do */ + if (!(READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) & CurrentConnectStatus)) + return; + + // clear port state change + OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] = __cpu_to_le32(ConnectStatusChange); + ohci_rh_enable_port (dev, port); + + mdelay(100); // wait for signal to stabilize + + if (!(READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) & PortEnableStatus)) { + usb_debug ("port enable failed\n"); + return; + } + + int speed = (READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) & LowSpeedDeviceAttached) != 0; + RH_INST (dev)->port[port] = usb_attach_device(dev->controller, dev->address, port, speed); +} + +static int +ohci_rh_report_port_changes (usbdev_t *dev) +{ + ohci_t *const ohcic = OHCI_INST (dev->controller); + + int i; + + for (i = 0; i < RH_INST(dev)->numports; i++) { + // maybe detach+attach happened between two scans? + if (READ_OPREG(ohcic, HcRhPortStatus[i]) & ConnectStatusChange) { + ohcic->opreg->HcRhPortStatus[i] = __cpu_to_le32(ConnectStatusChange); + usb_debug("attachment change on port %d\n", i); + return i; + } + } + + // no change + return -1; +} + +static void +ohci_rh_destroy (usbdev_t *dev) +{ + int i; + for (i = 0; i < RH_INST (dev)->numports; i++) + ohci_rh_disable_port (dev, i); + free (RH_INST (dev)); +} + +static void +ohci_rh_poll (usbdev_t *dev) +{ + ohci_t *const ohcic = OHCI_INST (dev->controller); + + int port; + + /* Check if anything changed. */ + if (!(READ_OPREG(ohcic, HcInterruptStatus) & RootHubStatusChange)) + return; + ohcic->opreg->HcInterruptStatus = __cpu_to_le32(RootHubStatusChange); + usb_debug("root hub status change\n"); + + /* Scan ports with changed connection status. */ + while ((port = ohci_rh_report_port_changes (dev)) != -1) + ohci_rh_scanport (dev, port); +} + +void +ohci_rh_init (usbdev_t *dev) +{ + int i; + + dev->destroy = ohci_rh_destroy; + dev->poll = ohci_rh_poll; + + dev->data = malloc (sizeof (rh_inst_t)); + if (!dev->data) { + printk("Not enough memory for OHCI RH.\n"); + return; + } + + RH_INST (dev)->numports = READ_OPREG(OHCI_INST(dev->controller), HcRhDescriptorA) & NumberDownstreamPortsMask; + RH_INST (dev)->port = malloc(sizeof(int) * RH_INST (dev)->numports); + usb_debug("%d ports registered\n", RH_INST (dev)->numports); + + for (i = 0; i < RH_INST (dev)->numports; i++) { + ohci_rh_enable_port (dev, i); + RH_INST (dev)->port[i] = -1; + } + + /* we can set them here because a root hub _really_ shouldn't + appear elsewhere */ + dev->address = 0; + dev->hub = -1; + dev->port = -1; + + usb_debug("rh init done\n"); +} diff --git a/qemu/roms/openbios/drivers/vga.fs b/qemu/roms/openbios/drivers/vga.fs new file mode 100644 index 000000000..ec4c6c5f1 --- /dev/null +++ b/qemu/roms/openbios/drivers/vga.fs @@ -0,0 +1,204 @@ +\ +\ Fcode payload for QEMU VGA graphics card +\ +\ This is the Forth source for an Fcode payload to initialise +\ the QEMU VGA graphics card. +\ +\ (C) Copyright 2013 Mark Cave-Ayland +\ + +fcode-version3 + +\ +\ Dictionary lookups for words that don't have an FCode +\ + +: (find-xt) \ ( str len -- xt | -1 ) + $find if + exit + else + -1 + then +; + +" openbios-video-width" (find-xt) cell+ value openbios-video-width-xt +" openbios-video-height" (find-xt) cell+ value openbios-video-height-xt +" depth-bits" (find-xt) cell+ value depth-bits-xt +" line-bytes" (find-xt) cell+ value line-bytes-xt + +: openbios-video-width openbios-video-width-xt @ ; +: openbios-video-height openbios-video-height-xt @ ; +: depth-bits depth-bits-xt @ ; +: line-bytes line-bytes-xt @ ; + +" fb8-fillrect" (find-xt) value fb8-fillrect-xt +: fb8-fillrect fb8-fillrect-xt execute ; + +\ +\ IO port words +\ + +" ioc!" (find-xt) value ioc!-xt +" iow!" (find-xt) value iow!-xt + +: ioc! ioc!-xt execute ; +: iow! iow!-xt execute ; + +\ +\ VGA registers +\ + +h# 3c0 constant vga-addr +h# 3c8 constant dac-write-addr +h# 3c9 constant dac-data-addr + +: vga-color! ( r g b index -- ) + \ Set the VGA colour registers + dac-write-addr ioc! rot + 2 >> dac-data-addr ioc! swap + 2 >> dac-data-addr ioc! + 2 >> dac-data-addr ioc! +; + +\ +\ VBE registers +\ + +h# 0 constant VBE_DISPI_INDEX_ID +h# 1 constant VBE_DISPI_INDEX_XRES +h# 2 constant VBE_DISPI_INDEX_YRES +h# 3 constant VBE_DISPI_INDEX_BPP +h# 4 constant VBE_DISPI_INDEX_ENABLE +h# 5 constant VBE_DISPI_INDEX_BANK +h# 6 constant VBE_DISPI_INDEX_VIRT_WIDTH +h# 7 constant VBE_DISPI_INDEX_VIRT_HEIGHT +h# 8 constant VBE_DISPI_INDEX_X_OFFSET +h# 9 constant VBE_DISPI_INDEX_Y_OFFSET +h# a constant VBE_DISPI_INDEX_NB + +h# 0 constant VBE_DISPI_DISABLED +h# 1 constant VBE_DISPI_ENABLED + +\ +\ Bochs VBE register writes +\ + +: vbe-iow! ( val addr -- ) + h# 1ce iow! + h# 1d0 iow! +; + +\ +\ Initialise Bochs VBE mode +\ + +: vbe-init ( -- ) + h# 0 vga-addr ioc! \ Enable blanking + VBE_DISPI_DISABLED VBE_DISPI_INDEX_ENABLE vbe-iow! + h# 0 VBE_DISPI_INDEX_X_OFFSET vbe-iow! + h# 0 VBE_DISPI_INDEX_Y_OFFSET vbe-iow! + openbios-video-width VBE_DISPI_INDEX_XRES vbe-iow! + openbios-video-height VBE_DISPI_INDEX_YRES vbe-iow! + depth-bits VBE_DISPI_INDEX_BPP vbe-iow! + VBE_DISPI_ENABLED VBE_DISPI_INDEX_ENABLE vbe-iow! + h# 0 vga-addr ioc! + h# 20 vga-addr ioc! \ Disable blanking +; + +\ +\ PCI +\ + +" pci-bar>pci-region" (find-xt) value pci-bar>pci-region-xt +: pci-bar>pci-region pci-bar>pci-region-xt execute ; + +h# 10 constant cfg-bar0 \ Framebuffer BAR +-1 value fb-addr + +: map-fb ( -- ) + cfg-bar0 pci-bar>pci-region \ ( pci-addr.lo pci-addr.hi size ) + " pci-map-in" $call-parent + to fb-addr +; + +\ +\ Publically visible words +\ + +external + +[IFDEF] CONFIG_MOL +defer mol-color! + +\ Hook for MOL (see packages/molvideo.c) +\ +\ Perhaps for neatness this there should be a separate molvga.fs +\ but let's leave it here for now. + +: color! ( r g b index -- ) + mol-color! +; + +[ELSE] + +\ Standard VGA + +: color! ( r g b index -- ) + vga-color! +; + +[THEN] + +: fill-rectangle ( color_ind x y width height -- ) + fb8-fillrect +; + +: dimensions ( -- width height ) + openbios-video-width + openbios-video-height +; + +: set-colors ( table start count -- ) + 0 do + over dup \ ( table start table table ) + c@ swap 1+ \ ( table start r table-g ) + dup c@ swap 1+ \ ( table start r g table-b ) + c@ 3 pick \ ( table start r g b index ) + color! \ ( table start ) + 1+ + swap 3 + swap \ ( table+3 start+1 ) + loop +; + +headerless + +\ +\ Installation +\ + +: qemu-vga-driver-install ( -- ) + fb-addr -1 = if + map-fb fb-addr to frame-buffer-adr + default-font set-font + + frame-buffer-adr encode-int " address" property + + openbios-video-width openbios-video-height over char-width / over char-height / + fb8-install + then +; + +: qemu-vga-driver-init + + vbe-init + openbios-video-width encode-int " width" property + openbios-video-height encode-int " height" property + depth-bits encode-int " depth" property + line-bytes encode-int " linebytes" property + + ['] qemu-vga-driver-install is-install +; + +qemu-vga-driver-init + +end0 diff --git a/qemu/roms/openbios/drivers/vga.h b/qemu/roms/openbios/drivers/vga.h new file mode 100644 index 000000000..a37e66e62 --- /dev/null +++ b/qemu/roms/openbios/drivers/vga.h @@ -0,0 +1,231 @@ +/* + * + * modified + * by Steve M. Gehlbach <steve@kesa.com> + * + * Originally from linux/drivers/video/vga16.c by + * Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz> + * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz> + * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm + * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> + * + */ + +#ifndef VGA_H_INCL +#define VGA_H_INCL 1 + +#include "drivers/vga.h" + +//#include <cpu/p5/io.h> + +#define u8 unsigned char +#define u16 unsigned short +#define u32 unsigned int +#define __u32 u32 + +#define VERROR -1 +#define CHAR_HEIGHT 16 +#define LINES 25 +#define COLS 80 + +// macros for writing to vga regs +#define write_crtc(data,addr) outb(addr,CRT_IC); outb(data,CRT_DC) +#define write_att(data,addr) inb(IS1_RC); inb(0x80); outb(addr,ATT_IW); inb(0x80); outb(data,ATT_IW); inb(0x80) +#define write_seq(data,addr) outb(addr,SEQ_I); outb(data,SEQ_D) +#define write_gra(data,addr) outb(addr,GRA_I); outb(data,GRA_D) +u8 read_seq_b(u16 addr); +u8 read_gra_b(u16 addr); +u8 read_crtc_b(u16 addr); +u8 read_att_b(u16 addr); + + +#ifdef VGA_HARDWARE_FIXUP +void vga_hardware_fixup(void); +#else +#define vga_hardware_fixup() do{} while(0) +#endif + +#define SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */ +#define SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */ +#define SYNC_EXT 4 /* external sync */ +#define SYNC_COMP_HIGH_ACT 8 /* composite sync high active */ +#define SYNC_BROADCAST 16 /* broadcast video timings */ + /* vtotal = 144d/288n/576i => PAL */ + /* vtotal = 121d/242n/484i => NTSC */ + +#define SYNC_ON_GREEN 32 /* sync on green */ + +#define VMODE_NONINTERLACED 0 /* non interlaced */ +#define VMODE_INTERLACED 1 /* interlaced */ +#define VMODE_DOUBLE 2 /* double scan */ +#define VMODE_MASK 255 + +#define VMODE_YWRAP 256 /* ywrap instead of panning */ +#define VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */ +#define VMODE_CONUPDATE 512 /* don't update x/yoffset */ + +/* VGA data register ports */ +#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */ +#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */ +#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */ +#define GRA_D 0x3CF /* Graphics Controller Data Register */ +#define SEQ_D 0x3C5 /* Sequencer Data Register */ + +#define MIS_R 0x3CC // Misc Output Read Register +#define MIS_W 0x3C2 // Misc Output Write Register + +#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */ +#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */ +#define PEL_D 0x3C9 /* PEL Data Register */ +#define PEL_MSK 0x3C6 /* PEL mask register */ + +/* EGA-specific registers */ +#define GRA_E0 0x3CC /* Graphics enable processor 0 */ +#define GRA_E1 0x3CA /* Graphics enable processor 1 */ + + +/* VGA index register ports */ +#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */ +#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */ +#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */ +#define GRA_I 0x3CE /* Graphics Controller Index */ +#define SEQ_I 0x3C4 /* Sequencer Index */ +#define PEL_IW 0x3C8 /* PEL Write Index */ +#define PEL_IR 0x3C7 /* PEL Read Index */ +#define DAC_REG 0x3C8 /* DAC register */ +#define DAC_VAL 0x3C9 /* DAC value */ + +/* standard VGA indexes max counts */ +#define CRTC_C 25 /* 25 CRT Controller Registers sequentially set*/ + // the remainder are not in the par array +#define ATT_C 21 /* 21 Attribute Controller Registers */ +#define GRA_C 9 /* 9 Graphics Controller Registers */ +#define SEQ_C 5 /* 5 Sequencer Registers */ +#define MIS_C 1 /* 1 Misc Output Register */ + +#define CRTC_H_TOTAL 0 +#define CRTC_H_DISP 1 +#define CRTC_H_BLANK_START 2 +#define CRTC_H_BLANK_END 3 +#define CRTC_H_SYNC_START 4 +#define CRTC_H_SYNC_END 5 +#define CRTC_V_TOTAL 6 +#define CRTC_OVERFLOW 7 +#define CRTC_PRESET_ROW 8 +#define CRTC_MAX_SCAN 9 +#define CRTC_CURSOR_START 0x0A +#define CRTC_CURSOR_END 0x0B +#define CRTC_START_HI 0x0C +#define CRTC_START_LO 0x0D +#define CRTC_CURSOR_HI 0x0E +#define CRTC_CURSOR_LO 0x0F +#define CRTC_V_SYNC_START 0x10 +#define CRTC_V_SYNC_END 0x11 +#define CRTC_V_DISP_END 0x12 +#define CRTC_OFFSET 0x13 +#define CRTC_UNDERLINE 0x14 +#define CRTC_V_BLANK_START 0x15 +#define CRTC_V_BLANK_END 0x16 +#define CRTC_MODE 0x17 +#define CRTC_LINE_COMPARE 0x18 + +#define ATC_MODE 0x10 +#define ATC_OVERSCAN 0x11 +#define ATC_PLANE_ENABLE 0x12 +#define ATC_PEL 0x13 +#define ATC_COLOR_PAGE 0x14 + +#define SEQ_CLOCK_MODE 0x01 +#define SEQ_PLANE_WRITE 0x02 +#define SEQ_CHARACTER_MAP 0x03 +#define SEQ_MEMORY_MODE 0x04 + +#define GDC_SR_VALUE 0x00 +#define GDC_SR_ENABLE 0x01 +#define GDC_COMPARE_VALUE 0x02 +#define GDC_DATA_ROTATE 0x03 +#define GDC_PLANE_READ 0x04 +#define GDC_MODE 0x05 +#define GDC_MISC 0x06 +#define GDC_COMPARE_MASK 0x07 +#define GDC_BIT_MASK 0x08 + +// text attributes +#define VGA_ATTR_CLR_RED 0x4 +#define VGA_ATTR_CLR_GRN 0x2 +#define VGA_ATTR_CLR_BLU 0x1 +#define VGA_ATTR_CLR_YEL (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN) +#define VGA_ATTR_CLR_CYN (VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU) +#define VGA_ATTR_CLR_MAG (VGA_ATTR_CLR_BLU | VGA_ATTR_CLR_RED) +#define VGA_ATTR_CLR_BLK 0 +#define VGA_ATTR_CLR_WHT (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU) +#define VGA_ATTR_BNK 0x80 +#define VGA_ATTR_ITN 0x08 + +/* + * vga register parameters + * these are copied to the + * registers. + * + */ +struct vga_par { + u8 crtc[CRTC_C]; + u8 atc[ATT_C]; + u8 gdc[GRA_C]; + u8 seq[SEQ_C]; + u8 misc; // the misc register, MIS_W + u8 vss; +}; + + +/* Interpretation of offset for color fields: All offsets are from the right, + * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you + * can use the offset as right argument to <<). A pixel afterwards is a bit + * stream and is written to video memory as that unmodified. This implies + * big-endian byte order if bits_per_pixel is greater than 8. + */ +struct fb_bitfield { + __u32 offset; /* beginning of bitfield */ + __u32 length; /* length of bitfield */ + __u32 msb_right; /* != 0 : Most significant bit is */ + /* right */ +}; + +struct screeninfo { + __u32 xres; /* visible resolution */ + __u32 yres; + __u32 xres_virtual; /* virtual resolution */ + __u32 yres_virtual; + __u32 xoffset; /* offset from virtual to visible */ + __u32 yoffset; /* resolution */ + + __u32 bits_per_pixel; /* guess what */ + __u32 grayscale; /* != 0 Graylevels instead of colors */ + + struct fb_bitfield red; /* bitfield in fb mem if true color, */ + struct fb_bitfield green; /* else only length is significant */ + struct fb_bitfield blue; + struct fb_bitfield transp; /* transparency */ + + __u32 nonstd; /* != 0 Non standard pixel format */ + + __u32 activate; /* see FB_ACTIVATE_* */ + + __u32 height; /* height of picture in mm */ + __u32 width; /* width of picture in mm */ + + __u32 accel_flags; /* acceleration flags (hints) */ + + /* Timing: All values in pixclocks, except pixclock (of course) */ + __u32 pixclock; /* pixel clock in ps (pico seconds) */ + __u32 left_margin; /* time from sync to picture */ + __u32 right_margin; /* time from picture to sync */ + __u32 upper_margin; /* time from sync to picture */ + __u32 lower_margin; + __u32 hsync_len; /* length of horizontal sync */ + __u32 vsync_len; /* length of vertical sync */ + __u32 sync; /* sync polarity */ + __u32 vmode; /* interlaced etc */ + __u32 reserved[6]; /* Reserved for future compatibility */ +}; +#endif diff --git a/qemu/roms/openbios/drivers/vga_load_regs.c b/qemu/roms/openbios/drivers/vga_load_regs.c new file mode 100644 index 000000000..dda6b798a --- /dev/null +++ b/qemu/roms/openbios/drivers/vga_load_regs.c @@ -0,0 +1,496 @@ +#include "asm/io.h" +#include "drivers/vga.h" +#include "vga.h" + +/* + * $Id$ + * $Source$ + * + * from the Linux kernel code base. + * orig by Ben Pfaff and Petr Vandrovec. + * + * modified by + * Steve M. Gehlbach <steve@kesa.com> + * + * NOTE: to change the horiz and vertical pixels, + * change the xres,yres,xres_virt,yres_virt setting + * in the screeninfo structure below. You may also need + * to change the border settings as well. + * + * Convert the screeninfo structure to data for + * writing to the vga registers + * + */ + +// prototypes +static int vga_decode_var(const struct screeninfo *var, struct vga_par *par); +static int vga_set_regs(const struct vga_par *par); + +u8 read_seq_b(u16 addr) { + outb(addr,SEQ_I); + return inb(SEQ_D); +} +u8 read_gra_b(u16 addr) { + outb(addr,GRA_I); + return inb(GRA_D); +} +u8 read_crtc_b(u16 addr) { + outb(addr,CRT_IC); + return inb(CRT_DC); +} +u8 read_att_b(u16 addr) { + inb(IS1_RC); + inb(0x80); + outb(addr,ATT_IW); + return inb(ATT_R); +} + + +/* +From: The Frame Buffer Device +by Geert Uytterhoeven <geert@linux-m68k.org> +in the linux kernel docs. + +The following picture summarizes all timings. The horizontal retrace time is +the sum of the left margin, the right margin and the hsync length, while the +vertical retrace time is the sum of the upper margin, the lower margin and the +vsync length. + + +----------+---------------------------------------------+----------+-------+ + | | ^ | | | + | | |upper_margin | | | + | | | | | | + +----------###############################################----------+-------+ + | # ^ # | | + | # | # | | + | # | # | | + | # | # | | + | left # | # right | hsync | + | margin # | xres # margin | len | + |<-------->#<---------------+--------------------------->#<-------->|<----->| + | # | # | | + | # | # | | + | # | # | | + | # |yres # | | + | # | # | | + | # | # | | + | # | # | | + | # | # | | + | # | # | | + | # | # | | + | # | # | | + | # | # | | + | # | # | | + +----------###############################################----------+-------+ + | | ^ | | | + | | |lower_margin | | | + | | | | | | + +----------+---------------------------------------------+----------+-------+ + | | ^ | | | + | | |vsync_len | | | + | | | | | | + +----------+---------------------------------------------+----------+-------+ + +All horizontal timings are in number of dotclocks +(in picoseconds, 1E-12 s), and vertical timings in number of scanlines. + +The vga uses the following fields: + + - pixclock: pixel clock in ps (pico seconds) + - xres,yres,xres_v,yres_v + - left_margin: time from sync to picture + - right_margin: time from picture to sync + - upper_margin: time from sync to picture + - lower_margin: time from picture to sync + - hsync_len: length of horizontal sync + - vsync_len: length of vertical sync + +*/ + +/* our display parameters per the above */ + +static const struct screeninfo vga_settings = { + 640,400,640,400,/* xres,yres,xres_virt,yres_virt */ + 0,0, /* xoffset,yoffset */ + 4, /* bits_per_pixel NOT USED*/ + 0, /* greyscale ? */ + {0,0,0}, /* R */ + {0,0,0}, /* G */ + {0,0,0}, /* B */ + {0,0,0}, /* transparency */ + 0, /* standard pixel format */ + 0, // activate now + -1,-1, // height and width in mm + 0, // accel flags + 39721, // pixclock: 79442 -> 12.587 Mhz (NOT USED) + // 70616 -> 14.161 + // 39721 -> 25.175 + // 35308 -> 28.322 + + 48, 16, 39, 8, // margins left,right,upper,lower + 96, // hsync length + 2, // vsync length + 0, // sync polarity + 0, // non interlaced, single mode + {0,0,0,0,0,0} // compatibility +}; + +// ALPHA-MODE +// Hard coded to BIOS VGA mode 3 (alpha color text) +// screen size settable in screeninfo structure + +static int vga_decode_var(const struct screeninfo *var, + struct vga_par *par) +{ + u8 VgaAttributeTable[16] = + { 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x014, 0x007, 0x038, 0x039, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x03F}; + + u32 xres, right, hslen, left, xtotal; + u32 yres, lower, vslen, upper, ytotal; + u32 vxres, xoffset, vyres, yoffset; + u32 pos; + u8 r7, rMode; + int i; + + xres = (var->xres + 7) & ~7; + vxres = (var->xres_virtual + 0xF) & ~0xF; + xoffset = (var->xoffset + 7) & ~7; + left = (var->left_margin + 7) & ~7; + right = (var->right_margin + 7) & ~7; + hslen = (var->hsync_len + 7) & ~7; + + if (vxres < xres) + vxres = xres; + if (xres + xoffset > vxres) + xoffset = vxres - xres; + + xres >>= 3; + right >>= 3; + hslen >>= 3; + left >>= 3; + vxres >>= 3; + xtotal = xres + right + hslen + left; + if (xtotal >= 256) + return VERROR; //xtotal too big + if (hslen > 32) + return VERROR; //hslen too big + if (right + hslen + left > 64) + return VERROR; //hblank too big + par->crtc[CRTC_H_TOTAL] = xtotal - 5; + par->crtc[CRTC_H_BLANK_START] = xres - 1; + par->crtc[CRTC_H_DISP] = xres - 1; + pos = xres + right; + par->crtc[CRTC_H_SYNC_START] = pos; + pos += hslen; + par->crtc[CRTC_H_SYNC_END] = (pos & 0x1F) | 0x20 ; //<--- stpc text mode p178 + pos += left - 2; /* blank_end + 2 <= total + 5 */ + par->crtc[CRTC_H_BLANK_END] = (pos & 0x1F) | 0x80; + if (pos & 0x20) + par->crtc[CRTC_H_SYNC_END] |= 0x80; + + yres = var->yres; + lower = var->lower_margin; + vslen = var->vsync_len; + upper = var->upper_margin; + vyres = var->yres_virtual; + yoffset = var->yoffset; + + if (yres > vyres) + vyres = yres; + if (vxres * vyres > 65536) { + vyres = 65536 / vxres; + if (vyres < yres) + return VERROR; // out of memory + } + if (yoffset + yres > vyres) + yoffset = vyres - yres; + + if (var->vmode & VMODE_DOUBLE) { + yres <<= 1; + lower <<= 1; + vslen <<= 1; + upper <<= 1; + } + ytotal = yres + lower + vslen + upper; + if (ytotal > 1024) { + ytotal >>= 1; + yres >>= 1; + lower >>= 1; + vslen >>= 1; + upper >>= 1; + rMode = 0x04; + } else + rMode = 0x00; + if (ytotal > 1024) + return VERROR; //ytotal too big + if (vslen > 16) + return VERROR; //vslen too big + par->crtc[CRTC_V_TOTAL] = ytotal - 2; + r7 = 0x10; /* disable linecompare */ + if (ytotal & 0x100) r7 |= 0x01; + if (ytotal & 0x200) r7 |= 0x20; + par->crtc[CRTC_PRESET_ROW] = 0; + + +// GMODE <--> ALPHA-MODE +// default using alpha mode so we need to set char rows= CHAR_HEIGHT-1 + par->crtc[CRTC_MAX_SCAN] = 0x40 | (CHAR_HEIGHT-1); /* 16 scanlines, linecmp max*/ + + if (var->vmode & VMODE_DOUBLE) + par->crtc[CRTC_MAX_SCAN] |= 0x80; + par->crtc[CRTC_CURSOR_START] = 0x00; // curs enabled, start line = 0 + par->crtc[CRTC_CURSOR_END] = CHAR_HEIGHT-1; // end line = 12 + pos = yoffset * vxres + (xoffset >> 3); + par->crtc[CRTC_START_HI] = pos >> 8; + par->crtc[CRTC_START_LO] = pos & 0xFF; + par->crtc[CRTC_CURSOR_HI] = 0x00; + par->crtc[CRTC_CURSOR_LO] = 0x00; + pos = yres - 1; + par->crtc[CRTC_V_DISP_END] = pos & 0xFF; + par->crtc[CRTC_V_BLANK_START] = pos & 0xFF; + if (pos & 0x100) + r7 |= 0x0A; /* 0x02 -> DISP_END, 0x08 -> BLANK_START */ + if (pos & 0x200) { + r7 |= 0x40; /* 0x40 -> DISP_END */ + par->crtc[CRTC_MAX_SCAN] |= 0x20; /* BLANK_START */ + } + pos += lower; + par->crtc[CRTC_V_SYNC_START] = pos & 0xFF; + if (pos & 0x100) + r7 |= 0x04; + if (pos & 0x200) + r7 |= 0x80; + pos += vslen; + par->crtc[CRTC_V_SYNC_END] = (pos & 0x0F) & ~0x10; /* disabled reg write prot, IRQ */ + pos += upper - 1; /* blank_end + 1 <= ytotal + 2 */ + par->crtc[CRTC_V_BLANK_END] = pos & 0xFF; /* 0x7F for original VGA, + but some SVGA chips requires all 8 bits to set */ + if (vxres >= 512) + return VERROR; //vxres too long + par->crtc[CRTC_OFFSET] = vxres >> 1; + + // put the underline off of the character, necessary in alpha color mode + par->crtc[CRTC_UNDERLINE] = 0x1f; + + par->crtc[CRTC_MODE] = rMode | 0xA3; // word mode + par->crtc[CRTC_LINE_COMPARE] = 0xFF; + par->crtc[CRTC_OVERFLOW] = r7; + + + // not used ?? + par->vss = 0x00; /* 3DA */ + + for (i = 0x00; i < 0x10; i++) { + par->atc[i] = VgaAttributeTable[i]; + } + // GMODE <--> ALPHA-MODE + par->atc[ATC_MODE] = 0x0c; // text mode + + par->atc[ATC_OVERSCAN] = 0x00; // no border + par->atc[ATC_PLANE_ENABLE] = 0x0F; + par->atc[ATC_PEL] = xoffset & 7; + par->atc[ATC_COLOR_PAGE] = 0x00; + + par->misc = 0x67; /* enable CPU, ports 0x3Dx, positive sync*/ + if (var->sync & SYNC_HOR_HIGH_ACT) + par->misc &= ~0x40; + if (var->sync & SYNC_VERT_HIGH_ACT) + par->misc &= ~0x80; + + par->seq[SEQ_CLOCK_MODE] = 0x01; //8-bit char; 0x01=alpha mode + par->seq[SEQ_PLANE_WRITE] = 0x03; // just char/attr plane + par->seq[SEQ_CHARACTER_MAP] = 0x00; + par->seq[SEQ_MEMORY_MODE] = 0x02; // A/G bit not used in stpc; O/E on, C4 off + + par->gdc[GDC_SR_VALUE] = 0x00; + // bits set in the SR_EN regs will enable set/reset action + // based on the bit settings in the SR_VAL register + par->gdc[GDC_SR_ENABLE] = 0x00; + par->gdc[GDC_COMPARE_VALUE] = 0x00; + par->gdc[GDC_DATA_ROTATE] = 0x00; + par->gdc[GDC_PLANE_READ] = 0; + par->gdc[GDC_MODE] = 0x10; //Okay + + // GMODE <--> ALPHA-MMODE + par->gdc[GDC_MISC] = 0x0e; // b0=0 ->alpha mode; memory at 0xb8000 + + par->gdc[GDC_COMPARE_MASK] = 0x00; + par->gdc[GDC_BIT_MASK] = 0xFF; + + return 0; +} + +// +// originally from the stpc web site +// +static const unsigned char VgaLookupTable[3 * 0x3f + 3] = { + // Red Green Blue + 0x000, 0x000, 0x000, // 00h + 0x000, 0x000, 0x02A, // 01h + 0x000, 0x02A, 0x000, // 02h + 0x000, 0x02A, 0x02A, // 03h + 0x02A, 0x000, 0x000, // 04h + 0x02A, 0x000, 0x02A, // 05h + 0x02A, 0x02A, 0x000, // 06h + 0x02A, 0x02A, 0x02A, // 07h + 0x000, 0x000, 0x015, // 08h + 0x000, 0x000, 0x03F, // 09h + 0x000, 0x02A, 0x015, // 0Ah + 0x000, 0x02A, 0x03F, // 0Bh + 0x02A, 0x000, 0x015, // 0Ch + 0x02A, 0x000, 0x03F, // 0Dh + 0x02A, 0x02A, 0x015, // 0Eh + 0x02A, 0x02A, 0x03F, // 0Fh + 0x000, 0x015, 0x000, // 10h + 0x000, 0x015, 0x02A, // 11h + 0x000, 0x03F, 0x000, // 12h + 0x000, 0x03F, 0x02A, // 13h + 0x02A, 0x015, 0x000, // 14h + 0x02A, 0x015, 0x02A, // 15h + 0x02A, 0x03F, 0x000, // 16h + 0x02A, 0x03F, 0x02A, // 17h + 0x000, 0x015, 0x015, // 18h + 0x000, 0x015, 0x03F, // 19h + 0x000, 0x03F, 0x015, // 1Ah + 0x000, 0x03F, 0x03F, // 1Bh + 0x02A, 0x015, 0x015, // 1Ch + 0x02A, 0x015, 0x03F, // 1Dh + 0x02A, 0x03F, 0x015, // 1Eh + 0x02A, 0x03F, 0x03F, // 1Fh + 0x015, 0x000, 0x000, // 20h + 0x015, 0x000, 0x02A, // 21h + 0x015, 0x02A, 0x000, // 22h + 0x015, 0x02A, 0x02A, // 23h + 0x03F, 0x000, 0x000, // 24h + 0x03F, 0x000, 0x02A, // 25h + 0x03F, 0x02A, 0x000, // 26h + 0x03F, 0x02A, 0x02A, // 27h + 0x015, 0x000, 0x015, // 28h + 0x015, 0x000, 0x03F, // 29h + 0x015, 0x02A, 0x015, // 2Ah + 0x015, 0x02A, 0x03F, // 2Bh + 0x03F, 0x000, 0x015, // 2Ch + 0x03F, 0x000, 0x03F, // 2Dh + 0x03F, 0x02A, 0x015, // 2Eh + 0x03F, 0x02A, 0x03F, // 2Fh + 0x015, 0x015, 0x000, // 30h + 0x015, 0x015, 0x02A, // 31h + 0x015, 0x03F, 0x000, // 32h + 0x015, 0x03F, 0x02A, // 33h + 0x03F, 0x015, 0x000, // 34h + 0x03F, 0x015, 0x02A, // 35h + 0x03F, 0x03F, 0x000, // 36h + 0x03F, 0x03F, 0x02A, // 37h + 0x015, 0x015, 0x015, // 38h + 0x015, 0x015, 0x03F, // 39h + 0x015, 0x03F, 0x015, // 3Ah + 0x015, 0x03F, 0x03F, // 3Bh + 0x03F, 0x015, 0x015, // 3Ch + 0x03F, 0x015, 0x03F, // 3Dh + 0x03F, 0x03F, 0x015, // 3Eh + 0x03F, 0x03F, 0x03F, // 3Fh +}; + +/* + * From the Linux kernel. + * orig by Ben Pfaff and Petr Vandrovec. + * see the note in the vga.h for attribution. + * + * modified by + * Steve M. Gehlbach <steve@kesa.com> + * for the linuxbios project + * + * Write the data in the vga parameter structure + * to the vga registers, along with other default + * settings. + * + */ +static int vga_set_regs(const struct vga_par *par) +{ + int i; + + /* update misc output register */ + outb(par->misc, MIS_W); + + /* synchronous reset on */ + outb(0x00, SEQ_I); + outb(0x00, SEQ_D); + + /* write sequencer registers */ + outb(1, SEQ_I); + outb(par->seq[1] | 0x20, SEQ_D); // blank display + for (i = 2; i < SEQ_C; i++) { + outb(i, SEQ_I); + outb(par->seq[i], SEQ_D); + } + + /* synchronous reset off */ + outb(0x00, SEQ_I); + outb(0x03, SEQ_D); + + /* deprotect CRT registers 0-7 */ + outb(0x11, CRT_IC); + outb(par->crtc[0x11], CRT_DC); + + /* write CRT registers */ + for (i = 0; i < CRTC_C; i++) { + outb(i, CRT_IC); + outb(par->crtc[i], CRT_DC); + } + /* write graphics controller registers */ + for (i = 0; i < GRA_C; i++) { + outb(i, GRA_I); + outb(par->gdc[i], GRA_D); + } + + /* write attribute controller registers */ + for (i = 0; i < ATT_C; i++) { + inb(IS1_RC); /* reset flip-flop */ + inb(0x80); //delay + outb(i, ATT_IW); + inb(0x80); //delay + + outb(par->atc[i], ATT_IW); + inb(0x80); //delay + } + + // initialize the color table + outb(0, PEL_IW); + i = 0; + // length is a magic number right now + while ( i < (0x3f*3 + 3) ) { + outb(VgaLookupTable[i++], PEL_D); + outb(VgaLookupTable[i++], PEL_D); + outb(VgaLookupTable[i++], PEL_D); + } + + outb(0x0ff, PEL_MSK); // palette mask + + // very important + // turn on video, disable palette access + inb(IS1_RC); /* reset flip-flop */ + inb(0x80); //delay + outb(0x20, ATT_IW); + + /* Wait for screen to stabilize. */ + //for(i=0;i<1000;i++) { inb(0x80); } + + outb(0x01, SEQ_I); // unblank display + outb(par->seq[1], SEQ_D); + +// turn on display, disable access to attr palette + inb(IS1_RC); + outb(0x20, ATT_IW); + +return 0; +} + +void +vga_load_regs(void) +{ + struct vga_par par; + + if (vga_decode_var(&vga_settings, &par) == 0) { + vga_set_regs(&par); + } +} diff --git a/qemu/roms/openbios/drivers/vga_set_mode.c b/qemu/roms/openbios/drivers/vga_set_mode.c new file mode 100644 index 000000000..339ad2966 --- /dev/null +++ b/qemu/roms/openbios/drivers/vga_set_mode.c @@ -0,0 +1,148 @@ +/* + * $Id$ + * $Source$ + * + * by + * Steve M. Gehlbach <steve@kesa.com> + * + * These routines set graphics mode and alpha mode + * for switching back and forth. + * + * Register settings are + * more or less as follows: + * + * Register Graphics Alpha + * 16 color + * ------------------------------------------------ + * GDC_MODE 0x00 0x10 + * GDC_MISC 0x05 0x0e + * SEQ_MEMORY_MODE 0x06 0x02 + * SEQ_PLANE_WRITE 0x0f 0x03 + * CRTC_CURSOR_START 0x20 0x00 + * CRTC_CURSOR_END 0x00 CHAR_HEIGHT-1 + * CRTC_MODE 0xe3 0xa3 + * CRTC_MAX_SCAN 0x40 0x40 | CHAR_HEIGHT-1 + * ATC_MODE 0x01 0x0c + * + */ + +#include "asm/io.h" +#include "vga.h" + +void vga_set_gmode (void) { + u8 byte; + + byte = read_att_b(ATC_MODE) & ~0x0f; + write_att(byte|0x1, ATC_MODE); +// +// display is off at this point + + byte = read_seq_b(SEQ_PLANE_WRITE) & ~0xf; + write_seq(byte|0xf,SEQ_PLANE_WRITE); // all planes + byte = read_seq_b(SEQ_MEMORY_MODE); + write_seq(byte|4,SEQ_MEMORY_MODE); + + byte = read_gra_b(GDC_MODE) & ~0x10; + write_gra(byte,GDC_MODE); + write_gra(0x05, GDC_MISC); + + write_crtc(0x20, CRTC_CURSOR_START); + write_crtc(0x00, CRTC_CURSOR_END); + byte = read_crtc_b(CRTC_MODE) & ~0xe0; + write_crtc(byte|0xe0, CRTC_MODE); + byte = read_crtc_b(CRTC_MAX_SCAN) & ~0x01f; + write_crtc(byte, CRTC_MAX_SCAN); + + byte = inb(MIS_R); // get 3c2 value by reading 3cc + outb(byte & ~0xc,MIS_W); // clear last bits to set 25Mhz clock and low page + + +// turn on display, disable access to attr palette + inb(IS1_RC); + outb(0x20, ATT_IW); +} + +void vga_set_amode (void) { + u8 byte; + write_att(0x0c, ATC_MODE); + + //reset palette to normal in the case it was changed + write_att(0x0, ATC_COLOR_PAGE); +// +// display is off at this point + + write_seq(0x3,SEQ_PLANE_WRITE); // planes 0 & 1 + byte = read_seq_b(SEQ_MEMORY_MODE) & ~0x04; + write_seq(byte,SEQ_MEMORY_MODE); + + byte = read_gra_b(GDC_MODE) & ~0x60; + write_gra(byte|0x10,GDC_MODE); + + write_gra(0x0e, GDC_MISC); + + write_crtc(0x00, CRTC_CURSOR_START); + write_crtc(CHAR_HEIGHT-1, CRTC_CURSOR_END); + + byte = read_crtc_b(CRTC_MODE) & ~0xe0; + write_crtc(byte|0xa0, CRTC_MODE); + byte = read_crtc_b(CRTC_MAX_SCAN) & ~0x01f; + write_crtc(byte | (CHAR_HEIGHT-1), CRTC_MAX_SCAN); + + +// turn on display, disable access to attr palette + inb(IS1_RC); + outb(0x20, ATT_IW); +} + +/* + * by Steve M. Gehlbach, Ph.D. <steve@kesa.com> + * + * vga_font_load loads a font into font memory. It + * assumes alpha mode has been set. + * + * The font load code follows technique used + * in the tiara project, which came from + * the Universal Talkware Boot Loader, + * http://www.talkware.net. + */ + +void vga_font_load(unsigned char *vidmem, const unsigned char *font, int height, int num_chars) { + +/* Note: the font table is 'height' long but the font storage area + * is 32 bytes long. + */ + + int i,j; + u8 byte; + + // set sequencer map 2, odd/even off + byte = read_seq_b(SEQ_PLANE_WRITE) & ~0xf; + write_seq(byte|4,SEQ_PLANE_WRITE); + byte = read_seq_b(SEQ_MEMORY_MODE); + write_seq(byte|4,SEQ_MEMORY_MODE); + + // select graphics map 2, odd/even off, map starts at 0xa0000 + write_gra(2,GDC_PLANE_READ); + byte = read_gra_b(GDC_MODE) & ~0x10; + write_gra(byte,GDC_MODE); + write_gra(0,GDC_MISC); + + for (i = 0 ; i < num_chars ; i++) { + for (j = 0 ; j < height ; j++) { + vidmem[i*32+j] = font[i*16+j]; + } + } + + // set sequencer back to maps 0,1, odd/even on + byte = read_seq_b(SEQ_PLANE_WRITE) & ~0xf; + write_seq(byte|3,SEQ_PLANE_WRITE); + byte = read_seq_b(SEQ_MEMORY_MODE) & ~0x4; + write_seq(byte,SEQ_MEMORY_MODE); + + // select graphics back to map 0,1, odd/even on + write_gra(0,GDC_PLANE_READ); + byte = read_gra_b(GDC_MODE); + write_gra(byte|0x10,GDC_MODE); + write_gra(0xe,GDC_MISC); + +} |