blob: d1172dc7432e4ddcad4458f605413223b2fe0784 [file] [log] [blame]
/******************************************************************************
* 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 <device/oprom/include/io.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;
}
#ifndef CONFIG_PCI
#endif
#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:
ret = inb(port);
break;
case 2:
ret = inw(port);
break;
case 4:
ret = inl(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:
outb(value, port);
break;
case 2:
outw(value, port);
break;
case 4:
outl(value, 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