#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/systm.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcib_private.h>
int
host_pcib_get_busno(pci_read_config_fn read_config, int bus, int slot, int func,
uint8_t *busnum)
{
uint32_t id;
id = read_config(0, bus, slot, func, PCIR_DEVVENDOR, 4);
if (id == 0xffffffff)
return (0);
switch (id) {
case 0x12258086:
*busnum = bus;
break;
case 0x84c48086:
*busnum = read_config(0, bus, slot, func, 0x4a, 1);
break;
case 0x84ca8086:
return (0);
case 0x84cb8086:
switch (slot) {
case 0x12:
*busnum = read_config(0, bus, 0x10, func, 0xd0, 1);
break;
case 0x13:
*busnum = read_config(0, bus, 0x10, func, 0xd1, 1) + 1;
break;
case 0x14:
*busnum = read_config(0, bus, 0x10, func, 0xd3, 1);
break;
case 0x15:
*busnum = read_config(0, bus, 0x10, func, 0xd4, 1) + 1;
break;
}
break;
case 0x00051166:
case 0x00061166:
case 0x00081166:
case 0x00091166:
case 0x00101166:
case 0x00111166:
case 0x00171166:
case 0x01011166:
case 0x010f1014:
case 0x01101166:
case 0x02011166:
case 0x02251166:
case 0x03021014:
*busnum = read_config(0, bus, slot, func, 0x44, 1);
break;
case 0x60100e11:
*busnum = read_config(0, bus, slot, func, 0xc8, 1);
break;
default:
return 0;
}
return 1;
}
const char *
pcib_child_name(device_t child)
{
static char buf[64];
if (device_get_nameunit(child) != NULL)
return (device_get_nameunit(child));
snprintf(buf, sizeof(buf), "pci%d:%d:%d:%d", pci_get_domain(child),
pci_get_bus(child), pci_get_slot(child), pci_get_function(child));
return (buf);
}
int
pcib_host_res_init(device_t pcib, struct pcib_host_resources *hr)
{
hr->hr_pcib = pcib;
resource_list_init(&hr->hr_rl);
return (0);
}
int
pcib_host_res_free(device_t pcib, struct pcib_host_resources *hr)
{
resource_list_free(&hr->hr_rl);
return (0);
}
int
pcib_host_res_decodes(struct pcib_host_resources *hr, int type, rman_res_t start,
rman_res_t end, u_int flags)
{
struct resource_list_entry *rle;
int rid;
if (bootverbose)
device_printf(hr->hr_pcib, "decoding %d %srange %#jx-%#jx\n",
type, flags & RF_PREFETCHABLE ? "prefetchable ": "", start,
end);
rid = resource_list_add_next(&hr->hr_rl, type, start, end,
end - start + 1);
if (flags & RF_PREFETCHABLE) {
KASSERT(type == SYS_RES_MEMORY,
("only memory is prefetchable"));
rle = resource_list_find(&hr->hr_rl, type, rid);
rle->flags = RLE_PREFETCH;
}
return (0);
}
struct resource *
pcib_host_res_alloc(struct pcib_host_resources *hr, device_t dev, int type,
int rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
{
struct resource_list_entry *rle;
struct resource *r;
rman_res_t new_start, new_end;
if (flags & RF_PREFETCHABLE)
KASSERT(type == SYS_RES_MEMORY,
("only memory is prefetchable"));
rle = resource_list_find(&hr->hr_rl, type, 0);
if (rle == NULL) {
return (bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
start, end, count, flags));
}
restart:
for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
if (rle->type != type)
continue;
if (((flags & RF_PREFETCHABLE) != 0) !=
((rle->flags & RLE_PREFETCH) != 0))
continue;
new_start = ummax(start, rle->start);
new_end = ummin(end, rle->end);
if (new_start > new_end ||
new_start + count - 1 > new_end ||
new_start + count < new_start)
continue;
r = bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
new_start, new_end, count, flags);
if (r != NULL) {
if (bootverbose)
device_printf(hr->hr_pcib,
"allocated type %d (%#jx-%#jx) for rid %x of %s\n",
type, rman_get_start(r), rman_get_end(r),
rid, pcib_child_name(dev));
return (r);
}
}
if (flags & RF_PREFETCHABLE) {
flags &= ~RF_PREFETCHABLE;
rle = resource_list_find(&hr->hr_rl, type, 0);
goto restart;
}
return (NULL);
}
int
pcib_host_res_adjust(struct pcib_host_resources *hr, device_t dev,
struct resource *r, rman_res_t start, rman_res_t end)
{
struct resource_list_entry *rle;
rle = resource_list_find(&hr->hr_rl, rman_get_type(r), 0);
if (rle == NULL) {
return (bus_generic_adjust_resource(hr->hr_pcib, dev, r, start,
end));
}
for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
if (rle->start <= start && rle->end >= end)
return (bus_generic_adjust_resource(hr->hr_pcib, dev,
r, start, end));
}
return (ERANGE);
}
struct pci_domain {
int pd_domain;
struct rman pd_bus_rman;
TAILQ_ENTRY(pci_domain) pd_link;
};
static TAILQ_HEAD(, pci_domain) domains = TAILQ_HEAD_INITIALIZER(domains);
static struct pci_domain *
pci_find_domain(int domain)
{
struct pci_domain *d;
char buf[64];
int error;
TAILQ_FOREACH(d, &domains, pd_link) {
if (d->pd_domain == domain)
return (d);
}
snprintf(buf, sizeof(buf), "PCI domain %d bus numbers", domain);
d = malloc(sizeof(*d) + strlen(buf) + 1, M_DEVBUF, M_WAITOK | M_ZERO);
d->pd_domain = domain;
d->pd_bus_rman.rm_start = 0;
d->pd_bus_rman.rm_end = PCI_BUSMAX;
d->pd_bus_rman.rm_type = RMAN_ARRAY;
strcpy((char *)(d + 1), buf);
d->pd_bus_rman.rm_descr = (char *)(d + 1);
error = rman_init(&d->pd_bus_rman);
if (error == 0)
error = rman_manage_region(&d->pd_bus_rman, 0, PCI_BUSMAX);
if (error)
panic("Failed to initialize PCI domain %d rman", domain);
TAILQ_INSERT_TAIL(&domains, d, pd_link);
return (d);
}
struct resource *
pci_domain_alloc_bus(int domain, device_t dev, int rid, rman_res_t start,
rman_res_t end, rman_res_t count, u_int flags)
{
struct pci_domain *d;
struct resource *res;
if (domain < 0 || domain > PCI_DOMAINMAX)
return (NULL);
d = pci_find_domain(domain);
res = rman_reserve_resource(&d->pd_bus_rman, start, end, count, flags,
dev);
if (res == NULL)
return (NULL);
rman_set_rid(res, rid);
rman_set_type(res, PCI_RES_BUS);
return (res);
}
int
pci_domain_adjust_bus(int domain, device_t dev, struct resource *r,
rman_res_t start, rman_res_t end)
{
#ifdef INVARIANTS
struct pci_domain *d;
#endif
if (domain < 0 || domain > PCI_DOMAINMAX)
return (EINVAL);
#ifdef INVARIANTS
d = pci_find_domain(domain);
KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
#endif
return (rman_adjust_resource(r, start, end));
}
int
pci_domain_release_bus(int domain, device_t dev, struct resource *r)
{
#ifdef INVARIANTS
struct pci_domain *d;
#endif
if (domain < 0 || domain > PCI_DOMAINMAX)
return (EINVAL);
#ifdef INVARIANTS
d = pci_find_domain(domain);
KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
#endif
return (rman_release_resource(r));
}
int
pci_domain_activate_bus(int domain, device_t dev, struct resource *r)
{
#ifdef INVARIANTS
struct pci_domain *d;
#endif
if (domain < 0 || domain > PCI_DOMAINMAX)
return (EINVAL);
#ifdef INVARIANTS
d = pci_find_domain(domain);
KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
#endif
return (rman_activate_resource(r));
}
int
pci_domain_deactivate_bus(int domain, device_t dev, struct resource *r)
{
#ifdef INVARIANTS
struct pci_domain *d;
#endif
if (domain < 0 || domain > PCI_DOMAINMAX)
return (EINVAL);
#ifdef INVARIANTS
d = pci_find_domain(domain);
KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
#endif
return (rman_deactivate_resource(r));
}