From 2d46a7ec8fdf0c22f21e57ecfc46a5df5cd5cfbf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 6 May 2011 18:05:13 -0700 Subject: Initial PCI setup. --- pci.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 pci.c (limited to 'pci.c') diff --git a/pci.c b/pci.c new file mode 100644 index 0000000..45739ff --- /dev/null +++ b/pci.c @@ -0,0 +1,150 @@ +/* Simplistic PCI support. + + Copyright (C) 2011 Richard Henderson + + This file is part of QEMU PALcode. + + 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 of the License or + (at your option) any later version. + + 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 text + of the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not see + . */ + +/* We don't bother supporting PCI bridges, because the device model we're + currently using for QEMU doesn't build any. + + We don't bother to build real datastructures in memory, because it's + fairly quick under emulation simply to access configuration space again. + This helps when running kernels under the emulator that might have + re-organized the BARs out from under us. */ + +#include "protos.h" +#include "pci.h" +#include "pci_regs.h" + + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) +#define PCI_BUS(devfn) ((devfn) >> 8) +#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) +#define PCI_FUNC(devfn) ((devfn) & 0x07) +#define PCI_SLOT_MAX 32 +#define PCI_FUNC_MAX 8 +#define PCI_REGION_ROM 6 +#define PCI_REGIONS_MAX 7 + + +void +pci_config_maskw(int bdf, int addr, uint16_t off, uint16_t on) +{ + uint16_t val = pci_config_readw(bdf, addr); + val = (val & ~off) | on; + pci_config_writew(bdf, addr, val); +} + +int +pci_next(int bdf, int *pmax) +{ + int max; + + if (PCI_FUNC(bdf) == 1) + { + /* If the last device was not a multi-function device, skip to next. */ + if ((pci_config_readb(bdf-1, PCI_HEADER_TYPE) & 0x80) == 0) + bdf += 7; + } + + max = *pmax; + while (1) + { + uint16_t vendor; + + /* ??? Support multiple PCI busses here at some point. */ + if (bdf >= max) + return -1; + + /* Check if there is a device present at the location. */ + vendor = pci_config_readw(bdf, PCI_VENDOR_ID); + if (vendor != 0x0000 && vendor != 0xffff) + return bdf; + + bdf += (PCI_FUNC(bdf) == 0 ? 8 : 1); + } +} + +static void +pci_setup_device(int bdf, uint32_t *p_io_base, uint32_t *p_mem_base) +{ + int vendor_id, device_id, class_id, region; + + vendor_id = pci_config_readw(bdf, PCI_VENDOR_ID); + device_id = pci_config_readw(bdf, PCI_DEVICE_ID); + class_id = pci_config_readw(bdf, PCI_CLASS_DEVICE); + + printf("PCI: %02x:%02x:%x class %04x id %04x:%04x\r\n", + PCI_BUS(bdf), PCI_SLOT(bdf), PCI_FUNC(bdf), + class_id, vendor_id, device_id); + + for (region = 0; region < PCI_REGION_ROM; region++) + { + int ofs = PCI_BASE_ADDRESS_0 + region * 4; + uint32_t old, mask, val, size, align; + uint32_t *p_base; + + old = pci_config_readl(bdf, ofs); + if (old & PCI_BASE_ADDRESS_SPACE_IO) + { + mask = PCI_BASE_ADDRESS_IO_MASK; + p_base = p_io_base; + } + else + { + mask = PCI_BASE_ADDRESS_MEM_MASK; + p_base = p_mem_base; + } + + pci_config_writel(bdf, ofs, -1); + val = pci_config_readl(bdf, ofs); + pci_config_writel(bdf, ofs, old); + + align = size = ~(val & mask) + 1; + if (val != 0) + { + uint32_t addr = *p_base; + addr = (addr + align - 1) & ~(align - 1); + *p_base = addr + size; + pci_config_writel(bdf, ofs, addr); + + printf("PCI: region %d: %08x\r\n", region, addr); + + if ((val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) + == PCI_BASE_ADDRESS_MEM_TYPE_64) + { + pci_config_writel(bdf, ofs + 4, 0); + region++; + } + } + } + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + + /* Map the interrupt. */ +} + +void +pci_setup(void) +{ + uint32_t io_base = 0x8000; + uint32_t mem_base = (128+16) * 1024 * 1024; + int bdf, max; + + foreachpci (bdf, max) + pci_setup_device(bdf, &io_base, &mem_base); +} -- cgit v1.1