162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Generic PCI resource mmap helper
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright © 2017 Amazon.com, Inc. or its affiliates.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: David Woodhouse <dwmw2@infradead.org>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/mm.h>
1262306a36Sopenharmony_ci#include <linux/pci.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#ifdef ARCH_GENERIC_PCI_MMAP_RESOURCE
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic const struct vm_operations_struct pci_phys_vm_ops = {
1762306a36Sopenharmony_ci#ifdef CONFIG_HAVE_IOREMAP_PROT
1862306a36Sopenharmony_ci	.access = generic_access_phys,
1962306a36Sopenharmony_ci#endif
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciint pci_mmap_resource_range(struct pci_dev *pdev, int bar,
2362306a36Sopenharmony_ci			    struct vm_area_struct *vma,
2462306a36Sopenharmony_ci			    enum pci_mmap_state mmap_state, int write_combine)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	unsigned long size;
2762306a36Sopenharmony_ci	int ret;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	size = ((pci_resource_len(pdev, bar) - 1) >> PAGE_SHIFT) + 1;
3062306a36Sopenharmony_ci	if (vma->vm_pgoff + vma_pages(vma) > size)
3162306a36Sopenharmony_ci		return -EINVAL;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (write_combine)
3462306a36Sopenharmony_ci		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
3562306a36Sopenharmony_ci	else
3662306a36Sopenharmony_ci		vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (mmap_state == pci_mmap_io) {
3962306a36Sopenharmony_ci		ret = pci_iobar_pfn(pdev, bar, vma);
4062306a36Sopenharmony_ci		if (ret)
4162306a36Sopenharmony_ci			return ret;
4262306a36Sopenharmony_ci	} else
4362306a36Sopenharmony_ci		vma->vm_pgoff += (pci_resource_start(pdev, bar) >> PAGE_SHIFT);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	vma->vm_ops = &pci_phys_vm_ops;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
4862306a36Sopenharmony_ci				  vma->vm_end - vma->vm_start,
4962306a36Sopenharmony_ci				  vma->vm_page_prot);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#endif
53