summaryrefslogtreecommitdiff
path: root/src/device/oprom/yabel/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/device/oprom/yabel/io.c')
-rw-r--r--src/device/oprom/yabel/io.c577
1 files changed, 577 insertions, 0 deletions
diff --git a/src/device/oprom/yabel/io.c b/src/device/oprom/yabel/io.c
new file mode 100644
index 0000000000..5c19b5142c
--- /dev/null
+++ b/src/device/oprom/yabel/io.c
@@ -0,0 +1,577 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <types.h>
+#include "compat/rtas.h"
+#include "compat/time.h"
+#include "device.h"
+#include "debug.h"
+#include <x86emu/x86emu.h>
+#include "io.h"
+
+#if CONFIG_PCI_OPTION_ROM_RUN_YABEL
+#include <device/pci.h>
+#include <device/pci_ops.h>
+#include <device/resource.h>
+#endif
+
+#if CONFIG_ARCH_X86
+#include <arch/io.h>
+#else
+// these are not used, only needed for linking, must be overridden using X86emu_setupPioFuncs
+// with the functions and struct below
+void
+outb(u8 val, u16 port)
+{
+ printf("WARNING: outb not implemented!\n");
+ HALT_SYS();
+}
+
+void
+outw(u16 val, u16 port)
+{
+ printf("WARNING: outw not implemented!\n");
+ HALT_SYS();
+}
+
+void
+outl(u32 val, u16 port)
+{
+ printf("WARNING: outl not implemented!\n");
+ HALT_SYS();
+}
+
+u8
+inb(u16 port)
+{
+ printf("WARNING: inb not implemented!\n");
+ HALT_SYS();
+ return 0;
+}
+
+u16
+inw(u16 port)
+{
+ printf("WARNING: inw not implemented!\n");
+ HALT_SYS();
+ return 0;
+}
+
+u32
+inl(u16 port)
+{
+ printf("WARNING: inl not implemented!\n");
+ HALT_SYS();
+ return 0;
+}
+#endif
+
+#if CONFIG_YABEL_DIRECTHW
+u8 my_inb(X86EMU_pioAddr addr)
+{
+ u8 val;
+
+ val = inb(addr);
+ DEBUG_PRINTF_IO("inb(0x%04x) = 0x%02x\n", addr, val);
+
+ return val;
+}
+
+u16 my_inw(X86EMU_pioAddr addr)
+{
+ u16 val;
+
+ val = inw(addr);
+ DEBUG_PRINTF_IO("inw(0x%04x) = 0x%04x\n", addr, val);
+
+ return val;
+}
+
+u32 my_inl(X86EMU_pioAddr addr)
+{
+ u32 val;
+
+ val = inl(addr);
+ DEBUG_PRINTF_IO("inl(0x%04x) = 0x%08x\n", addr, val);
+
+ return val;
+}
+
+void my_outb(X86EMU_pioAddr addr, u8 val)
+{
+ DEBUG_PRINTF_IO("outb(0x%02x, 0x%04x)\n", val, addr);
+ outb(val, addr);
+}
+
+void my_outw(X86EMU_pioAddr addr, u16 val)
+{
+ DEBUG_PRINTF_IO("outw(0x%04x, 0x%04x)\n", val, addr);
+ outw(val, addr);
+}
+
+void my_outl(X86EMU_pioAddr addr, u32 val)
+{
+ DEBUG_PRINTF_IO("outl(0x%08x, 0x%04x)\n", val, addr);
+ outl(val, addr);
+}
+
+#else
+
+static unsigned int
+read_io(void *addr, size_t sz)
+{
+ unsigned int ret;
+ /* since we are using inb instructions, we need the port number as 16bit value */
+ u16 port = (u16)(u32) addr;
+
+ switch (sz) {
+ case 1:
+ asm volatile ("inb %1, %b0" : "=a"(ret) : "d" (port));
+ break;
+ case 2:
+ asm volatile ("inw %1, %w0" : "=a"(ret) : "d" (port));
+ break;
+ case 4:
+ asm volatile ("inl %1, %0" : "=a"(ret) : "d" (port));
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int
+write_io(void *addr, unsigned int value, size_t sz)
+{
+ u16 port = (u16)(u32) addr;
+ switch (sz) {
+ /* since we are using inb instructions, we need the port number as 16bit value */
+ case 1:
+ asm volatile ("outb %b0, %1" : : "a"(value), "d" (port));
+ break;
+ case 2:
+ asm volatile ("outw %w0, %1" : : "a"(value), "d" (port));
+ break;
+ case 4:
+ asm volatile ("outl %0, %1" : : "a"(value), "d" (port));
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+u32 pci_cfg_read(X86EMU_pioAddr addr, u8 size);
+void pci_cfg_write(X86EMU_pioAddr addr, u32 val, u8 size);
+u8 handle_port_61h(void);
+
+u8
+my_inb(X86EMU_pioAddr addr)
+{
+ u8 rval = 0xFF;
+ unsigned long translated_addr = addr;
+ u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
+ if (translated != 0) {
+ //translation successfull, access Device I/O (BAR or Legacy...)
+ DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__,
+ addr);
+ //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
+ rval = read_io((void *)translated_addr, 1);
+ DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %02x\n", __func__,
+ addr, rval);
+ return rval;
+ } else {
+ switch (addr) {
+ case 0x61:
+ //8254 KB Controller / Timer Port
+ // rval = handle_port_61h();
+ rval = inb(0x61);
+ //DEBUG_PRINTF_IO("%s(%04x) KB / Timer Port B --> %02x\n", __func__, addr, rval);
+ return rval;
+ break;
+ case 0xCFC:
+ case 0xCFD:
+ case 0xCFE:
+ case 0xCFF:
+ // PCI Config Mechanism 1 Ports
+ return (u8) pci_cfg_read(addr, 1);
+ break;
+ case 0x0a:
+ CHECK_DBG(DEBUG_INTR) {
+ X86EMU_trace_on();
+ }
+ M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
+ //HALT_SYS();
+ // no break, intentional fall-through to default!!
+ default:
+ DEBUG_PRINTF_IO
+ ("%s(%04x) reading from bios_device.io_buffer\n",
+ __func__, addr);
+ rval = *((u8 *) (bios_device.io_buffer + addr));
+ DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %02x\n",
+ __func__, addr, rval);
+ return rval;
+ break;
+ }
+ }
+}
+
+u16
+my_inw(X86EMU_pioAddr addr)
+{
+ unsigned long translated_addr = addr;
+ u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
+ if (translated != 0) {
+ //translation successfull, access Device I/O (BAR or Legacy...)
+ DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__,
+ addr);
+ //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
+ u16 rval;
+ if ((translated_addr & (u64) 0x1) == 0) {
+ // 16 bit aligned access...
+ u16 tempval = read_io((void *)translated_addr, 2);
+ //little endian conversion
+ rval = in16le((void *) &tempval);
+ } else {
+ // unaligned access, read single bytes, little-endian
+ rval = (read_io((void *)translated_addr, 1) << 8)
+ | (read_io((void *)(translated_addr + 1), 1));
+ }
+ DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %04x\n", __func__,
+ addr, rval);
+ return rval;
+ } else {
+ switch (addr) {
+ case 0xCFC:
+ case 0xCFE:
+ //PCI Config Mechanism 1
+ return (u16) pci_cfg_read(addr, 2);
+ break;
+ default:
+ DEBUG_PRINTF_IO
+ ("%s(%04x) reading from bios_device.io_buffer\n",
+ __func__, addr);
+ u16 rval =
+ in16le((void *) bios_device.io_buffer + addr);
+ DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %04x\n",
+ __func__, addr, rval);
+ return rval;
+ break;
+ }
+ }
+}
+
+u32
+my_inl(X86EMU_pioAddr addr)
+{
+ unsigned long translated_addr = addr;
+ u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
+ if (translated != 0) {
+ //translation successfull, access Device I/O (BAR or Legacy...)
+ DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__,
+ addr);
+ //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
+ u32 rval;
+ if ((translated_addr & (u64) 0x3) == 0) {
+ // 32 bit aligned access...
+ u32 tempval = read_io((void *) translated_addr, 4);
+ //little endian conversion
+ rval = in32le((void *) &tempval);
+ } else {
+ // unaligned access, read single bytes, little-endian
+ rval = (read_io((void *)(translated_addr), 1) << 24)
+ | (read_io((void *)(translated_addr + 1), 1) << 16)
+ | (read_io((void *)(translated_addr + 2), 1) << 8)
+ | (read_io((void *)(translated_addr + 3), 1));
+ }
+ DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %08x\n", __func__,
+ addr, rval);
+ return rval;
+ } else {
+ switch (addr) {
+ case 0xCFC:
+ //PCI Config Mechanism 1
+ return pci_cfg_read(addr, 4);
+ break;
+ default:
+ DEBUG_PRINTF_IO
+ ("%s(%04x) reading from bios_device.io_buffer\n",
+ __func__, addr);
+ u32 rval =
+ in32le((void *) bios_device.io_buffer + addr);
+ DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %08x\n",
+ __func__, addr, rval);
+ return rval;
+ break;
+ }
+ }
+}
+
+void
+my_outb(X86EMU_pioAddr addr, u8 val)
+{
+ unsigned long translated_addr = addr;
+ u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
+ if (translated != 0) {
+ //translation successfull, access Device I/O (BAR or Legacy...)
+ DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n",
+ __func__, addr, val);
+ //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
+ write_io((void *) translated_addr, val, 1);
+ DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %02x\n", __func__,
+ addr, val);
+ } else {
+ switch (addr) {
+ case 0xCFC:
+ case 0xCFD:
+ case 0xCFE:
+ case 0xCFF:
+ // PCI Config Mechanism 1 Ports
+ pci_cfg_write(addr, val, 1);
+ break;
+ default:
+ DEBUG_PRINTF_IO
+ ("%s(%04x,%02x) writing to bios_device.io_buffer\n",
+ __func__, addr, val);
+ *((u8 *) (bios_device.io_buffer + addr)) = val;
+ break;
+ }
+ }
+}
+
+void
+my_outw(X86EMU_pioAddr addr, u16 val)
+{
+ unsigned long translated_addr = addr;
+ u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
+ if (translated != 0) {
+ //translation successfull, access Device I/O (BAR or Legacy...)
+ DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n",
+ __func__, addr, val);
+ //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
+ if ((translated_addr & (u64) 0x1) == 0) {
+ // little-endian conversion
+ u16 tempval = in16le((void *) &val);
+ // 16 bit aligned access...
+ write_io((void *) translated_addr, tempval, 2);
+ } else {
+ // unaligned access, write single bytes, little-endian
+ write_io(((void *) (translated_addr + 1)),
+ (u8) ((val & 0xFF00) >> 8), 1);
+ write_io(((void *) translated_addr),
+ (u8) (val & 0x00FF), 1);
+ }
+ DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %04x\n", __func__,
+ addr, val);
+ } else {
+ switch (addr) {
+ case 0xCFC:
+ case 0xCFE:
+ // PCI Config Mechanism 1 Ports
+ pci_cfg_write(addr, val, 2);
+ break;
+ default:
+ DEBUG_PRINTF_IO
+ ("%s(%04x,%04x) writing to bios_device.io_buffer\n",
+ __func__, addr, val);
+ out16le((void *) bios_device.io_buffer + addr, val);
+ break;
+ }
+ }
+}
+
+void
+my_outl(X86EMU_pioAddr addr, u32 val)
+{
+ unsigned long translated_addr = addr;
+ u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
+ if (translated != 0) {
+ //translation successfull, access Device I/O (BAR or Legacy...)
+ DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n",
+ __func__, addr, val);
+ //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
+ if ((translated_addr & (u64) 0x3) == 0) {
+ // little-endian conversion
+ u32 tempval = in32le((void *) &val);
+ // 32 bit aligned access...
+ write_io((void *) translated_addr, tempval, 4);
+ } else {
+ // unaligned access, write single bytes, little-endian
+ write_io(((void *) translated_addr + 3),
+ (u8) ((val & 0xFF000000) >> 24), 1);
+ write_io(((void *) translated_addr + 2),
+ (u8) ((val & 0x00FF0000) >> 16), 1);
+ write_io(((void *) translated_addr + 1),
+ (u8) ((val & 0x0000FF00) >> 8), 1);
+ write_io(((void *) translated_addr),
+ (u8) (val & 0x000000FF), 1);
+ }
+ DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %08x\n", __func__,
+ addr, val);
+ } else {
+ switch (addr) {
+ case 0xCFC:
+ // PCI Config Mechanism 1 Ports
+ pci_cfg_write(addr, val, 4);
+ break;
+ default:
+ DEBUG_PRINTF_IO
+ ("%s(%04x,%08x) writing to bios_device.io_buffer\n",
+ __func__, addr, val);
+ out32le((void *) bios_device.io_buffer + addr, val);
+ break;
+ }
+ }
+}
+
+u32
+pci_cfg_read(X86EMU_pioAddr addr, u8 size)
+{
+ u32 rval = 0xFFFFFFFF;
+ struct device * dev;
+ if ((addr >= 0xCFC) && ((addr + size) <= 0xD00)) {
+ // PCI Configuration Mechanism 1 step 1
+ // write to 0xCF8, sets bus, device, function and Config Space offset
+ // later read from 0xCFC-0xCFF returns the value...
+ u8 bus, devfn, offs;
+ u32 port_cf8_val = my_inl(0xCF8);
+ if ((port_cf8_val & 0x80000000) != 0) {
+ //highest bit enables config space mapping
+ bus = (port_cf8_val & 0x00FF0000) >> 16;
+ devfn = (port_cf8_val & 0x0000FF00) >> 8;
+ offs = (port_cf8_val & 0x000000FF);
+ offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly
+ DEBUG_PRINTF_INTR("%s(): PCI Config Read from device: bus: %02x, devfn: %02x, offset: %02x\n",
+ __func__, bus, devfn, offs);
+#if CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES
+ dev = dev_find_slot(bus, devfn);
+ DEBUG_PRINTF_INTR("%s(): dev_find_slot() returned: %s\n",
+ __func__, dev_path(dev));
+ if (dev == 0) {
+ // fail accesses to non-existent devices...
+#else
+ dev = bios_device.dev;
+ if ((bus != bios_device.bus)
+ || (devfn != bios_device.devfn)) {
+ // fail accesses to any device but ours...
+#endif
+ printf
+ ("%s(): Config read access invalid device! bus: %02x (%02x), devfn: %02x (%02x), offs: %02x\n",
+ __func__, bus, bios_device.bus, devfn,
+ bios_device.devfn, offs);
+ SET_FLAG(F_CF);
+ HALT_SYS();
+ return 0;
+ } else {
+#if CONFIG_PCI_OPTION_ROM_RUN_YABEL
+ switch (size) {
+ case 1:
+ rval = pci_read_config8(dev, offs);
+ break;
+ case 2:
+ rval = pci_read_config16(dev, offs);
+ break;
+ case 4:
+ rval = pci_read_config32(dev, offs);
+ break;
+ }
+#else
+ rval =
+ (u32) rtas_pci_config_read(bios_device.
+ puid, size,
+ bus, devfn,
+ offs);
+#endif
+ DEBUG_PRINTF_IO
+ ("%s(%04x) PCI Config Read @%02x, size: %d --> 0x%08x\n",
+ __func__, addr, offs, size, rval);
+ }
+ }
+ }
+ return rval;
+}
+
+void
+pci_cfg_write(X86EMU_pioAddr addr, u32 val, u8 size)
+{
+ if ((addr >= 0xCFC) && ((addr + size) <= 0xD00)) {
+ // PCI Configuration Mechanism 1 step 1
+ // write to 0xCF8, sets bus, device, function and Config Space offset
+ // later write to 0xCFC-0xCFF sets the value...
+ u8 bus, devfn, offs;
+ u32 port_cf8_val = my_inl(0xCF8);
+ if ((port_cf8_val & 0x80000000) != 0) {
+ //highest bit enables config space mapping
+ bus = (port_cf8_val & 0x00FF0000) >> 16;
+ devfn = (port_cf8_val & 0x0000FF00) >> 8;
+ offs = (port_cf8_val & 0x000000FF);
+ offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly
+ if ((bus != bios_device.bus)
+ || (devfn != bios_device.devfn)) {
+ // fail accesses to any device but ours...
+ printf
+ ("Config write access invalid! PCI device %x:%x.%x, offs: %x\n",
+ bus, devfn >> 3, devfn & 7, offs);
+#if !CONFIG_YABEL_PCI_FAKE_WRITING_OTHER_DEVICES_CONFIG
+ HALT_SYS();
+#endif
+ } else {
+#if CONFIG_PCI_OPTION_ROM_RUN_YABEL
+ switch (size) {
+ case 1:
+ pci_write_config8(bios_device.dev, offs, val);
+ break;
+ case 2:
+ pci_write_config16(bios_device.dev, offs, val);
+ break;
+ case 4:
+ pci_write_config32(bios_device.dev, offs, val);
+ break;
+ }
+#else
+ rtas_pci_config_write(bios_device.puid,
+ size, bus, devfn, offs,
+ val);
+#endif
+ DEBUG_PRINTF_IO
+ ("%s(%04x) PCI Config Write @%02x, size: %d <-- 0x%08x\n",
+ __func__, addr, offs, size, val);
+ }
+ }
+ }
+}
+
+u8
+handle_port_61h(void)
+{
+ static u64 last_time = 0;
+ u64 curr_time = get_time();
+ u64 time_diff; // time since last call
+ u32 period_ticks; // length of a period in ticks
+ u32 nr_periods; //number of periods passed since last call
+ // bit 4 should toggle with every (DRAM) refresh cycle... (66kHz??)
+ time_diff = curr_time - last_time;
+ // at 66kHz a period is ~ 15 ns long, converted to ticks: (tb_freq is ticks/second)
+ // TODO: as long as the frequency does not change, we should not calculate this every time
+ period_ticks = (15 * tb_freq) / 1000000;
+ nr_periods = time_diff / period_ticks;
+ // if the number if ticks passed since last call is odd, we toggle bit 4
+ if ((nr_periods % 2) != 0) {
+ *((u8 *) (bios_device.io_buffer + 0x61)) ^= 0x10;
+ }
+ //finally read the value from the io_buffer
+ return *((u8 *) (bios_device.io_buffer + 0x61));
+}
+#endif