PCI: Add pci_mmap_resource_range() and use it for ARM64
Starting to leave behind the legacy of the pci_mmap_page_range() interface which takes "user-visible" BAR addresses. This takes just the resource and offset. For now, both APIs coexist and depending on the platform, one is implemented as a wrapper around the other. Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
parent
f66e225828
commit
f719582435
|
@ -113,9 +113,13 @@ Supporting PCI access on new platforms
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
In order to support PCI resource mapping as described above, Linux platform
|
In order to support PCI resource mapping as described above, Linux platform
|
||||||
code must define HAVE_PCI_MMAP and provide a pci_mmap_page_range function.
|
code should ideally define ARCH_GENERIC_PCI_MMAP_RESOURCE and use the generic
|
||||||
Platforms are free to only support subsets of the mmap functionality, but
|
implementation of that functionality. To support the historical interface of
|
||||||
useful return codes should be provided.
|
mmap() through files in /proc/bus/pci, platforms may also set HAVE_PCI_MMAP.
|
||||||
|
|
||||||
|
Alternatively, platforms which set HAVE_PCI_MMAP may provide their own
|
||||||
|
implementation of pci_mmap_page_range() instead of defining
|
||||||
|
ARCH_GENERIC_PCI_MMAP_RESOURCE.
|
||||||
|
|
||||||
Platforms which support write-combining maps of PCI resources must define
|
Platforms which support write-combining maps of PCI resources must define
|
||||||
arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when
|
arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
*/
|
*/
|
||||||
#define PCI_DMA_BUS_IS_PHYS (0)
|
#define PCI_DMA_BUS_IS_PHYS (0)
|
||||||
|
|
||||||
|
#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1
|
||||||
|
|
||||||
extern int isa_dma_bridge_buggy;
|
extern int isa_dma_bridge_buggy;
|
||||||
|
|
||||||
#ifdef CONFIG_PCI
|
#ifdef CONFIG_PCI
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \
|
obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \
|
||||||
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
|
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
|
||||||
irq.o vpd.o setup-bus.o vc.o
|
irq.o vpd.o setup-bus.o vc.o mmap.o
|
||||||
obj-$(CONFIG_PROC_FS) += proc.o
|
obj-$(CONFIG_PROC_FS) += proc.o
|
||||||
obj-$(CONFIG_SYSFS) += slot.o
|
obj-$(CONFIG_SYSFS) += slot.o
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* mmap.c — generic PCI resource mmap helper
|
||||||
|
*
|
||||||
|
* Copyright © 2017 Amazon.com, Inc. or its affiliates.
|
||||||
|
*
|
||||||
|
* Author: David Woodhouse <dwmw2@infradead.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
|
||||||
|
#ifdef ARCH_GENERIC_PCI_MMAP_RESOURCE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Modern setup: generic pci_mmap_resource_range(), and implement the legacy
|
||||||
|
* pci_mmap_page_range() (if needed) as a wrapper round it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_PCI_MMAP
|
||||||
|
int pci_mmap_page_range(struct pci_dev *pdev, int bar,
|
||||||
|
struct vm_area_struct *vma,
|
||||||
|
enum pci_mmap_state mmap_state, int write_combine)
|
||||||
|
{
|
||||||
|
resource_size_t start, end;
|
||||||
|
|
||||||
|
pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end);
|
||||||
|
|
||||||
|
/* Adjust vm_pgoff to be the offset within the resource */
|
||||||
|
vma->vm_pgoff -= start >> PAGE_SHIFT;
|
||||||
|
return pci_mmap_resource_range(pdev, bar, vma, mmap_state,
|
||||||
|
write_combine);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct vm_operations_struct pci_phys_vm_ops = {
|
||||||
|
#ifdef CONFIG_HAVE_IOREMAP_PROT
|
||||||
|
.access = generic_access_phys,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
int pci_mmap_resource_range(struct pci_dev *pdev, int bar,
|
||||||
|
struct vm_area_struct *vma,
|
||||||
|
enum pci_mmap_state mmap_state, int write_combine)
|
||||||
|
{
|
||||||
|
unsigned long size;
|
||||||
|
|
||||||
|
if (mmap_state == pci_mmap_io)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
size = ((pci_resource_len(pdev, bar) - 1) >> PAGE_SHIFT) + 1;
|
||||||
|
if (vma->vm_pgoff + vma_pages(vma) > size)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (write_combine)
|
||||||
|
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
||||||
|
else
|
||||||
|
vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
|
||||||
|
|
||||||
|
vma->vm_pgoff += (pci_resource_start(pdev, bar) >> PAGE_SHIFT);
|
||||||
|
vma->vm_ops = &pci_phys_vm_ops;
|
||||||
|
|
||||||
|
return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
|
||||||
|
vma->vm_end - vma->vm_start,
|
||||||
|
vma->vm_page_prot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(HAVE_PCI_MMAP) /* && !ARCH_GENERIC_PCI_MMAP_RESOURCE */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Legacy setup: Impement pci_mmap_resource_range() as a wrapper around
|
||||||
|
* the architecture's pci_mmap_page_range(), converting to "user visible"
|
||||||
|
* addresses as necessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int pci_mmap_resource_range(struct pci_dev *pdev, int bar,
|
||||||
|
struct vm_area_struct *vma,
|
||||||
|
enum pci_mmap_state mmap_state, int write_combine)
|
||||||
|
{
|
||||||
|
resource_size_t start, end;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pci_mmap_page_range() expects the same kind of entry as coming
|
||||||
|
* from /proc/bus/pci/ which is a "user visible" value. If this is
|
||||||
|
* different from the resource itself, arch will do necessary fixup.
|
||||||
|
*/
|
||||||
|
pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end);
|
||||||
|
vma->vm_pgoff += start >> PAGE_SHIFT;
|
||||||
|
return pci_mmap_page_range(pdev, bar, vma, mmap_state, write_combine);
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -980,7 +980,7 @@ void pci_remove_legacy_files(struct pci_bus *b)
|
||||||
}
|
}
|
||||||
#endif /* HAVE_PCI_LEGACY */
|
#endif /* HAVE_PCI_LEGACY */
|
||||||
|
|
||||||
#ifdef HAVE_PCI_MMAP
|
#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
|
||||||
|
|
||||||
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
|
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
|
||||||
enum pci_mmap_api mmap_api)
|
enum pci_mmap_api mmap_api)
|
||||||
|
@ -1019,7 +1019,6 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
||||||
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
|
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
|
||||||
int bar = (unsigned long)attr->private;
|
int bar = (unsigned long)attr->private;
|
||||||
enum pci_mmap_state mmap_type;
|
enum pci_mmap_state mmap_type;
|
||||||
resource_size_t start, end;
|
|
||||||
struct resource *res = &pdev->resource[bar];
|
struct resource *res = &pdev->resource[bar];
|
||||||
|
|
||||||
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
||||||
|
@ -1033,15 +1032,9 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
||||||
(u64)pci_resource_len(pdev, bar));
|
(u64)pci_resource_len(pdev, bar));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pci_mmap_page_range() expects the same kind of entry as coming
|
|
||||||
* from /proc/bus/pci/ which is a "user visible" value. If this is
|
|
||||||
* different from the resource itself, arch will do necessary fixup.
|
|
||||||
*/
|
|
||||||
pci_resource_to_user(pdev, bar, res, &start, &end);
|
|
||||||
vma->vm_pgoff += start >> PAGE_SHIFT;
|
|
||||||
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
|
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
|
||||||
return pci_mmap_page_range(pdev, bar, vma, mmap_type, write_combine);
|
|
||||||
|
return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
|
static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
|
||||||
|
|
|
@ -21,14 +21,14 @@ void pci_create_firmware_label_files(struct pci_dev *pdev);
|
||||||
void pci_remove_firmware_label_files(struct pci_dev *pdev);
|
void pci_remove_firmware_label_files(struct pci_dev *pdev);
|
||||||
#endif
|
#endif
|
||||||
void pci_cleanup_rom(struct pci_dev *dev);
|
void pci_cleanup_rom(struct pci_dev *dev);
|
||||||
#ifdef HAVE_PCI_MMAP
|
|
||||||
enum pci_mmap_api {
|
enum pci_mmap_api {
|
||||||
PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */
|
PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */
|
||||||
PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */
|
PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */
|
||||||
};
|
};
|
||||||
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
|
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
|
||||||
enum pci_mmap_api mmap_api);
|
enum pci_mmap_api mmap_api);
|
||||||
#endif
|
|
||||||
int pci_probe_reset_function(struct pci_dev *dev);
|
int pci_probe_reset_function(struct pci_dev *dev);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1626,10 +1626,21 @@ static inline int pci_get_new_domain_nr(void) { return -ENOSYS; }
|
||||||
|
|
||||||
#include <asm/pci.h>
|
#include <asm/pci.h>
|
||||||
|
|
||||||
/* Map a range of PCI memory or I/O space for a device into user space.
|
/* These two functions provide almost identical functionality. Depennding
|
||||||
* Architectures provide this function if they set HAVE_PCI_MMAP, and
|
* on the architecture, one will be implemented as a wrapper around the
|
||||||
* it accepts the 'write_combine' argument when arch_can_pci_mmap_wc()
|
* other (in drivers/pci/mmap.c).
|
||||||
* evaluates to nonzero. */
|
*
|
||||||
|
* pci_mmap_resource_range() maps a specific BAR, and vm->vm_pgoff
|
||||||
|
* is expected to be an offset within that region.
|
||||||
|
*
|
||||||
|
* pci_mmap_page_range() is the legacy architecture-specific interface,
|
||||||
|
* which accepts a "user visible" resource address converted by
|
||||||
|
* pci_resource_to_user(), as used in the legacy mmap() interface in
|
||||||
|
* /proc/bus/pci/.
|
||||||
|
*/
|
||||||
|
int pci_mmap_resource_range(struct pci_dev *dev, int bar,
|
||||||
|
struct vm_area_struct *vma,
|
||||||
|
enum pci_mmap_state mmap_state, int write_combine);
|
||||||
int pci_mmap_page_range(struct pci_dev *pdev, int bar,
|
int pci_mmap_page_range(struct pci_dev *pdev, int bar,
|
||||||
struct vm_area_struct *vma,
|
struct vm_area_struct *vma,
|
||||||
enum pci_mmap_state mmap_state, int write_combine);
|
enum pci_mmap_state mmap_state, int write_combine);
|
||||||
|
|
Loading…
Reference in New Issue