blob: 2a82c734067c820f3632382f1f936d8ca287d4f0 [file] [log] [blame]
/*
* arm64 PCI host controller interface
*
* Heavily based on bios32.c from arch/arm/
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/of_pci.h>
#include <linux/slab.h>
static void pcibios_fixup_device_resources(struct pci_dev *dev)
{
struct resource *bar_r;
int bar;
if (pci_has_flag(PCI_PROBE_ONLY)) {
/*
* If the firmware did not assign the BAR, zero out the
* resource so the kernel doesn't attempt to assign
* it later on in pci_assign_unassigned_resources
*/
for (bar = 0; bar <= PCI_STD_RESOURCE_END; bar++) {
bar_r = &dev->resource[bar];
if (bar_r->start == 0 && bar_r->end != 0) {
bar_r->flags = 0;
bar_r->end = 0;
}
}
}
}
void pcibios_fixup_bus(struct pci_bus *bus)
{
struct pci_dev *dev;
pci_read_bridge_bases(bus);
list_for_each_entry(dev, &bus->devices, bus_list)
pcibios_fixup_device_resources(dev);
}
static void pcibios_init_hw(struct device *parent, struct hw_pci *hw,
struct list_head *head)
{
struct pci_sys_data *sys = NULL;
int ret;
int nr, busnr;
for (nr = busnr = 0; nr < hw->nr_controllers; nr++) {
sys = kzalloc(sizeof(struct pci_sys_data), GFP_KERNEL);
if (!sys)
panic("PCI: unable to allocate sys data!");
sys->domain = hw->domain;
sys->busnr = busnr;
sys->add_bus = hw->add_bus;
sys->remove_bus = hw->remove_bus;
INIT_LIST_HEAD(&sys->resources);
if (hw->private_data)
sys->private_data = hw->private_data[nr];
ret = hw->setup(nr, sys);
if (ret > 0) {
if (list_empty(&sys->resources)) {
pr_err("PCI: no resources found\n");
ret = -ENODEV;
kfree(sys);
break;
}
sys->bus = pci_scan_root_bus(parent, sys->busnr,
hw->ops, sys, &sys->resources);
if (!sys->bus)
panic("PCI: unable to scan bus!");
busnr = sys->bus->busn_res.end + 1;
list_add(&sys->node, head);
} else {
kfree(sys);
if (ret < 0)
break;
}
}
}
void pci_common_init_dev(struct device *parent, struct hw_pci *hw)
{
struct pci_sys_data *sys;
LIST_HEAD(head);
pci_add_flags(PCI_REASSIGN_ALL_RSRC);
pcibios_init_hw(parent, hw, &head);
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
list_for_each_entry(sys, &head, node) {
struct pci_bus *bus = sys->bus;
if (!pci_has_flag(PCI_PROBE_ONLY)) {
/*
* Size the bridge windows.
*/
pci_bus_size_bridges(bus);
/*
* Assign resources.
*/
pci_bus_assign_resources(bus);
}
/*
* Tell drivers about devices found.
*/
pci_bus_add_devices(bus);
}
}
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
{
return ALIGN(res->start, align);
}
int pcibios_enable_device(struct pci_dev *dev, int mask)
{
if (pci_has_flag(PCI_PROBE_ONLY))
return 0;
return pci_enable_resources(dev, mask);
}
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
enum pci_mmap_state mmap_state, int write_combine)
{
struct pci_sys_data *root = dev->sysdata;
unsigned long phys;
if (mmap_state == pci_mmap_io) {
return -EINVAL;
} else {
phys = vma->vm_pgoff + (root->mem_offset >> PAGE_SHIFT);
}
/*
* Mark this as IO
*/
vma->vm_page_prot = write_combine ?
pgprot_writecombine(vma->vm_page_prot) :
pgprot_noncached(vma->vm_page_prot);
if (remap_pfn_range(vma, vma->vm_start, phys,
vma->vm_end - vma->vm_start,
vma->vm_page_prot))
return -EAGAIN;
return 0;
}
void pcibios_add_bus(struct pci_bus *bus)
{
struct pci_sys_data *sys = bus->sysdata;
if (sys->add_bus)
sys->add_bus(bus);
}
void pcibios_remove_bus(struct pci_bus *bus)
{
struct pci_sys_data *sys = bus->sysdata;
if (sys->remove_bus)
sys->remove_bus(bus);
}