Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7195824 authored by David Woodhouse's avatar David Woodhouse Committed by Bjorn Helgaas
Browse files

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: default avatarDavid Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent f66e2258
Loading
Loading
Loading
Loading
+7 −3
Original line number Original line Diff line number Diff line
@@ -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
+2 −0
Original line number Original line Diff line number Diff line
@@ -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
+1 −1
Original line number Original line Diff line number Diff line
@@ -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


drivers/pci/mmap.c

0 → 100644
+95 −0
Original line number Original line Diff line number Diff line
/*
 * 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
+3 −10
Original line number Original line Diff line number Diff line
@@ -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,
Loading