1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-19 10:53:58 +00:00
freebsd/sys/mips/sibyte/sb_zbpci.c
Neel Natu 9dcae110dc Remove the PCI_IOSPACE_SIZE and PCI_IOSPACE_ADDR hack from nexus.c. Implement
this in the Sibyte PCI hostbridge driver instead.

The nexus driver sees resource allocation requests for memory and irq
resources only. These are legitimate resources on all MIPS platforms.

Suggested by: imp
2010-02-12 02:59:49 +00:00

400 lines
10 KiB
C

/*-
* Copyright (c) 2009 Neelkanth Natu
* All rights reserved.
*
* 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.
*
* 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 <sys/param.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/pcpu.h>
#include <sys/smp.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#include <vm/pmap.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcib_private.h>
#include <machine/pmap.h>
#include <machine/resource.h>
#include <machine/bus.h>
#include "pcib_if.h"
#include "sb_scd.h"
__FBSDID("$FreeBSD$");
static struct {
vm_offset_t vaddr;
vm_paddr_t paddr;
} zbpci_config_space[MAXCPU];
static const vm_paddr_t CFG_PADDR_BASE = 0xFE000000;
static const u_long PCI_IOSPACE_ADDR = 0xFC000000;
static const u_long PCI_IOSPACE_SIZE = 0x02000000;
static struct rman port_rman;
static int
zbpci_probe(device_t dev)
{
device_set_desc(dev, "Broadcom/Sibyte PCI I/O Bridge");
return (0);
}
static int
zbpci_attach(device_t dev)
{
int n, rid, size;
vm_offset_t va;
struct resource *res;
/*
* Reserve the physical memory window used to map PCI I/O space.
*/
rid = 0;
res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
PCI_IOSPACE_ADDR,
PCI_IOSPACE_ADDR + PCI_IOSPACE_SIZE - 1,
PCI_IOSPACE_SIZE, 0);
if (res == NULL)
panic("Cannot allocate resource for PCI I/O space mapping.");
port_rman.rm_start = 0;
port_rman.rm_end = PCI_IOSPACE_SIZE - 1;
port_rman.rm_type = RMAN_ARRAY;
port_rman.rm_descr = "PCI I/O ports";
if (rman_init(&port_rman) != 0 ||
rman_manage_region(&port_rman, 0, PCI_IOSPACE_SIZE - 1) != 0)
panic("%s: port_rman", __func__);
/*
* Reserve the the physical memory that is used to read/write to the
* pci config space but don't activate it. We are using a page worth
* of KVA as a window over this region.
*/
rid = 1;
size = (PCI_BUSMAX + 1) * (PCI_SLOTMAX + 1) * (PCI_FUNCMAX + 1) * 256;
res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, CFG_PADDR_BASE,
CFG_PADDR_BASE + size - 1, size, 0);
if (res == NULL)
panic("Cannot allocate resource for config space accesses.");
/*
* Allocate KVA for accessing PCI config space.
*/
va = kmem_alloc_nofault(kernel_map, PAGE_SIZE * mp_ncpus);
if (va == 0) {
device_printf(dev, "Cannot allocate virtual addresses for "
"config space access.\n");
return (ENOMEM);
}
for (n = 0; n < mp_ncpus; ++n)
zbpci_config_space[n].vaddr = va + n * PAGE_SIZE;
/*
* Sibyte has the PCI bus hierarchy rooted at bus 0 and HT-PCI
* hierarchy rooted at bus 1.
*/
if (device_add_child(dev, "pci", 0) == NULL)
panic("zbpci_attach: could not add pci bus 0.\n");
if (device_add_child(dev, "pci", 1) == NULL)
panic("zbpci_attach: could not add pci bus 1.\n");
if (bootverbose)
device_printf(dev, "attached.\n");
return (bus_generic_attach(dev));
}
static struct resource *
zbpci_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
struct resource *res;
/*
* Handle PCI I/O port resources here and pass everything else to nexus.
*/
if (type != SYS_RES_IOPORT) {
res = bus_generic_alloc_resource(bus, child, type, rid,
start, end, count, flags);
return (res);
}
res = rman_reserve_resource(&port_rman, start, end, count,
flags, child);
if (res == NULL)
return (NULL);
rman_set_rid(res, *rid);
/* Activate the resource is requested */
if (flags & RF_ACTIVE) {
if (bus_activate_resource(child, type, *rid, res) != 0) {
rman_release_resource(res);
return (NULL);
}
}
return (res);
}
static int
zbpci_activate_resource(device_t bus, device_t child, int type, int rid,
struct resource *res)
{
void *vaddr;
u_long paddr, psize;
if (type != SYS_RES_IOPORT) {
return (bus_generic_activate_resource(bus, child, type,
rid, res));
}
/*
* Map the I/O space resource through the memory window starting
* at PCI_IOSPACE_ADDR.
*/
paddr = rman_get_start(res) + PCI_IOSPACE_ADDR;
psize = rman_get_size(res);
vaddr = pmap_mapdev(paddr, psize);
rman_set_virtual(res, vaddr);
rman_set_bustag(res, mips_bus_space_generic);
rman_set_bushandle(res, (bus_space_handle_t)vaddr);
return (rman_activate_resource(res));
}
static int
zbpci_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
int error;
if (type != SYS_RES_IOPORT)
return (bus_generic_release_resource(bus, child, type, rid, r));
if (rman_get_flags(r) & RF_ACTIVE) {
error = bus_deactivate_resource(child, type, rid, r);
if (error)
return (error);
}
return (rman_release_resource(r));
}
static int
zbpci_deactivate_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
vm_offset_t va;
if (type != SYS_RES_IOPORT) {
return (bus_generic_deactivate_resource(bus, child, type,
rid, r));
}
va = (vm_offset_t)rman_get_virtual(r);
pmap_unmapdev(va, rman_get_size(r));
return (rman_deactivate_resource(r));
}
static int
zbpci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
{
switch (which) {
case PCIB_IVAR_DOMAIN:
*result = 0; /* single PCI domain */
return (0);
case PCIB_IVAR_BUS:
*result = device_get_unit(child); /* PCI bus 0 or 1 */
return (0);
default:
return (ENOENT);
}
}
/*
* We rely on the CFE to have configured the intline correctly to point to
* one of PCI-A/PCI-B/PCI-C/PCI-D in the interupt mapper.
*/
static int
zbpci_route_interrupt(device_t pcib, device_t dev, int pin)
{
return (PCI_INVALID_IRQ);
}
/*
* This function is expected to be called in a critical section since it
* changes the per-cpu pci config space va-to-pa mappings.
*/
static vm_offset_t
zbpci_config_space_va(int bus, int slot, int func, int reg, int bytes)
{
int cpu;
vm_offset_t va_page;
vm_paddr_t pa, pa_page;
if (bus <= PCI_BUSMAX && slot <= PCI_SLOTMAX && func <= PCI_FUNCMAX &&
reg <= PCI_REGMAX && (bytes == 1 || bytes == 2 || bytes == 4) &&
((reg & (bytes - 1)) == 0)) {
cpu = PCPU_GET(cpuid);
va_page = zbpci_config_space[cpu].vaddr;
pa = CFG_PADDR_BASE |
(bus << 16) | (slot << 11) | (func << 8) | reg;
pa_page = pa & ~(PAGE_SIZE - 1);
if (zbpci_config_space[cpu].paddr != pa_page) {
pmap_kremove(va_page);
pmap_kenter(va_page, pa_page);
zbpci_config_space[cpu].paddr = pa_page;
}
return (va_page + (pa - pa_page));
} else {
return (0);
}
}
static uint32_t
zbpci_read_config(device_t dev, u_int b, u_int s, u_int f, u_int r, int w)
{
uint32_t data;
vm_offset_t va;
critical_enter();
va = zbpci_config_space_va(b, s, f, r, w);
if (va == 0) {
panic("zbpci_read_config: invalid %d/%d/%d[%d] %d\n",
b, s, f, r, w);
}
switch (w) {
case 4:
data = *(uint32_t *)va;
break;
case 2:
data = *(uint16_t *)va;
break;
case 1:
data = *(uint8_t *)va;
break;
default:
panic("zbpci_read_config: invalid width %d\n", w);
}
critical_exit();
return (data);
}
static void
zbpci_write_config(device_t d, u_int b, u_int s, u_int f, u_int r,
uint32_t data, int w)
{
vm_offset_t va;
critical_enter();
va = zbpci_config_space_va(b, s, f, r, w);
if (va == 0) {
panic("zbpci_write_config: invalid %d/%d/%d[%d] %d/%d\n",
b, s, f, r, data, w);
}
switch (w) {
case 4:
*(uint32_t *)va = data;
break;
case 2:
*(uint16_t *)va = data;
break;
case 1:
*(uint8_t *)va = data;
break;
default:
panic("zbpci_write_config: invalid width %d\n", w);
}
critical_exit();
}
static device_method_t zbpci_methods[] ={
/* Device interface */
DEVMETHOD(device_probe, zbpci_probe),
DEVMETHOD(device_attach, zbpci_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
/* Bus interface */
DEVMETHOD(bus_read_ivar, zbpci_read_ivar),
DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
DEVMETHOD(bus_alloc_resource, zbpci_alloc_resource),
DEVMETHOD(bus_activate_resource, zbpci_activate_resource),
DEVMETHOD(bus_deactivate_resource, zbpci_deactivate_resource),
DEVMETHOD(bus_release_resource, zbpci_release_resource),
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
DEVMETHOD(bus_add_child, bus_generic_add_child),
/* pcib interface */
DEVMETHOD(pcib_maxslots, pcib_maxslots),
DEVMETHOD(pcib_read_config, zbpci_read_config),
DEVMETHOD(pcib_write_config, zbpci_write_config),
DEVMETHOD(pcib_route_interrupt, zbpci_route_interrupt),
{ 0, 0 }
};
/*
* The "zbpci" class inherits from the "pcib" base class. Therefore in
* addition to drivers that belong to the "zbpci" class we will also
* consider drivers belonging to the "pcib" when probing children of
* "zbpci".
*/
DECLARE_CLASS(pcib_driver);
DEFINE_CLASS_1(zbpci, zbpci_driver, zbpci_methods, 0, pcib_driver);
static devclass_t zbpci_devclass;
DRIVER_MODULE(zbpci, zbbus, zbpci_driver, zbpci_devclass, 0, 0);