mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 11:19:37 +00:00
![Linus Torvalds](/assets/img/avatar_default.png)
Nathan Chancellor reports that the recent change to pci_iounmap in commit9caea00076
("parisc: Declare pci_iounmap() parisc version only when CONFIG_PCI enabled") causes build errors on arm64. It took me about two hours to convince myself that I think I know what the logic of that mess of #ifdef's in the <asm-generic/io.h> header file really aim to do, and rewrite it to be easier to follow. Famous last words. Anyway, the code has now been lifted from that grotty header file into lib/pci_iomap.c, and has fairly extensive comments about what the logic is. It also avoids indirecting through another confusing (and badly named) helper function that has other preprocessor config conditionals. Let's see what odd architecture did something else strange in this area to break things. But my arm64 cross build is clean. Fixes:9caea00076
("parisc: Declare pci_iounmap() parisc version only when CONFIG_PCI enabled") Reported-by: Nathan Chancellor <nathan@kernel.org> Cc: Helge Deller <deller@gmx.de> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Guenter Roeck <linux@roeck-us.net> Cc: Ulrich Teichert <krypton@ulrich-teichert.org> Cc: James Bottomley <James.Bottomley@hansenpartnership.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
180 lines
5.7 KiB
C
180 lines
5.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Implement the default iomap interfaces
|
|
*
|
|
* (C) Copyright 2004 Linus Torvalds
|
|
*/
|
|
#include <linux/pci.h>
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/export.h>
|
|
|
|
#ifdef CONFIG_PCI
|
|
/**
|
|
* pci_iomap_range - create a virtual mapping cookie for a PCI BAR
|
|
* @dev: PCI device that owns the BAR
|
|
* @bar: BAR number
|
|
* @offset: map memory at the given offset in BAR
|
|
* @maxlen: max length of the memory to map
|
|
*
|
|
* Using this function you will get a __iomem address to your device BAR.
|
|
* You can access it using ioread*() and iowrite*(). These functions hide
|
|
* the details if this is a MMIO or PIO address space and will just do what
|
|
* you expect from them in the correct way.
|
|
*
|
|
* @maxlen specifies the maximum length to map. If you want to get access to
|
|
* the complete BAR from offset to the end, pass %0 here.
|
|
* */
|
|
void __iomem *pci_iomap_range(struct pci_dev *dev,
|
|
int bar,
|
|
unsigned long offset,
|
|
unsigned long maxlen)
|
|
{
|
|
resource_size_t start = pci_resource_start(dev, bar);
|
|
resource_size_t len = pci_resource_len(dev, bar);
|
|
unsigned long flags = pci_resource_flags(dev, bar);
|
|
|
|
if (len <= offset || !start)
|
|
return NULL;
|
|
len -= offset;
|
|
start += offset;
|
|
if (maxlen && len > maxlen)
|
|
len = maxlen;
|
|
if (flags & IORESOURCE_IO)
|
|
return __pci_ioport_map(dev, start, len);
|
|
if (flags & IORESOURCE_MEM)
|
|
return ioremap(start, len);
|
|
/* What? */
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(pci_iomap_range);
|
|
|
|
/**
|
|
* pci_iomap_wc_range - create a virtual WC mapping cookie for a PCI BAR
|
|
* @dev: PCI device that owns the BAR
|
|
* @bar: BAR number
|
|
* @offset: map memory at the given offset in BAR
|
|
* @maxlen: max length of the memory to map
|
|
*
|
|
* Using this function you will get a __iomem address to your device BAR.
|
|
* You can access it using ioread*() and iowrite*(). These functions hide
|
|
* the details if this is a MMIO or PIO address space and will just do what
|
|
* you expect from them in the correct way. When possible write combining
|
|
* is used.
|
|
*
|
|
* @maxlen specifies the maximum length to map. If you want to get access to
|
|
* the complete BAR from offset to the end, pass %0 here.
|
|
* */
|
|
void __iomem *pci_iomap_wc_range(struct pci_dev *dev,
|
|
int bar,
|
|
unsigned long offset,
|
|
unsigned long maxlen)
|
|
{
|
|
resource_size_t start = pci_resource_start(dev, bar);
|
|
resource_size_t len = pci_resource_len(dev, bar);
|
|
unsigned long flags = pci_resource_flags(dev, bar);
|
|
|
|
|
|
if (flags & IORESOURCE_IO)
|
|
return NULL;
|
|
|
|
if (len <= offset || !start)
|
|
return NULL;
|
|
|
|
len -= offset;
|
|
start += offset;
|
|
if (maxlen && len > maxlen)
|
|
len = maxlen;
|
|
|
|
if (flags & IORESOURCE_MEM)
|
|
return ioremap_wc(start, len);
|
|
|
|
/* What? */
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(pci_iomap_wc_range);
|
|
|
|
/**
|
|
* pci_iomap - create a virtual mapping cookie for a PCI BAR
|
|
* @dev: PCI device that owns the BAR
|
|
* @bar: BAR number
|
|
* @maxlen: length of the memory to map
|
|
*
|
|
* Using this function you will get a __iomem address to your device BAR.
|
|
* You can access it using ioread*() and iowrite*(). These functions hide
|
|
* the details if this is a MMIO or PIO address space and will just do what
|
|
* you expect from them in the correct way.
|
|
*
|
|
* @maxlen specifies the maximum length to map. If you want to get access to
|
|
* the complete BAR without checking for its length first, pass %0 here.
|
|
* */
|
|
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
|
|
{
|
|
return pci_iomap_range(dev, bar, 0, maxlen);
|
|
}
|
|
EXPORT_SYMBOL(pci_iomap);
|
|
|
|
/**
|
|
* pci_iomap_wc - create a virtual WC mapping cookie for a PCI BAR
|
|
* @dev: PCI device that owns the BAR
|
|
* @bar: BAR number
|
|
* @maxlen: length of the memory to map
|
|
*
|
|
* Using this function you will get a __iomem address to your device BAR.
|
|
* You can access it using ioread*() and iowrite*(). These functions hide
|
|
* the details if this is a MMIO or PIO address space and will just do what
|
|
* you expect from them in the correct way. When possible write combining
|
|
* is used.
|
|
*
|
|
* @maxlen specifies the maximum length to map. If you want to get access to
|
|
* the complete BAR without checking for its length first, pass %0 here.
|
|
* */
|
|
void __iomem *pci_iomap_wc(struct pci_dev *dev, int bar, unsigned long maxlen)
|
|
{
|
|
return pci_iomap_wc_range(dev, bar, 0, maxlen);
|
|
}
|
|
EXPORT_SYMBOL_GPL(pci_iomap_wc);
|
|
|
|
/*
|
|
* pci_iounmap() somewhat illogically comes from lib/iomap.c for the
|
|
* CONFIG_GENERIC_IOMAP case, because that's the code that knows about
|
|
* the different IOMAP ranges.
|
|
*
|
|
* But if the architecture does not use the generic iomap code, and if
|
|
* it has _not_ defined it's own private pci_iounmap function, we define
|
|
* it here.
|
|
*
|
|
* NOTE! This default implementation assumes that if the architecture
|
|
* support ioport mapping (HAS_IOPORT_MAP), the ioport mapping will
|
|
* be fixed to the range [ PCI_IOBASE, PCI_IOBASE+IO_SPACE_LIMIT [,
|
|
* and does not need unmapping with 'ioport_unmap()'.
|
|
*
|
|
* If you have different rules for your architecture, you need to
|
|
* implement your own pci_iounmap() that knows the rules for where
|
|
* and how IO vs MEM get mapped.
|
|
*
|
|
* This code is odd, and the ARCH_HAS/ARCH_WANTS #define logic comes
|
|
* from legacy <asm-generic/io.h> header file behavior. In particular,
|
|
* it would seem to make sense to do the iounmap(p) for the non-IO-space
|
|
* case here regardless, but that's not what the old header file code
|
|
* did. Probably incorrectly, but this is meant to be bug-for-bug
|
|
* compatible.
|
|
*/
|
|
#if defined(ARCH_WANTS_GENERIC_PCI_IOUNMAP)
|
|
|
|
void pci_iounmap(struct pci_dev *dev, void __iomem *p)
|
|
{
|
|
#ifdef ARCH_HAS_GENERIC_IOPORT_MAP
|
|
uintptr_t start = (uintptr_t) PCI_IOBASE;
|
|
uintptr_t addr = (uintptr_t) p;
|
|
|
|
if (addr >= start && addr < start + IO_SPACE_LIMIT)
|
|
return;
|
|
iounmap(p);
|
|
#endif
|
|
}
|
|
EXPORT_SYMBOL(pci_iounmap);
|
|
|
|
#endif /* ARCH_WANTS_GENERIC_PCI_IOUNMAP */
|
|
|
|
#endif /* CONFIG_PCI */
|