Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lib: Add generic PIO mapping method

41f8bba7f555 ("of/pci: Add pci_register_io_range() and
pci_pio_to_address()") added support for PCI I/O space mapped into CPU
physical memory space. With that support, the I/O ranges configured for
PCI/PCIe hosts on some architectures can be mapped to logical PIO and
converted easily between CPU address and the corresponding logical PIO.
Based on this, PCI I/O port space can be accessed via in/out accessors that
use memory read/write.

But on some platforms, there are bus hosts that access I/O port space with
host-local I/O port addresses rather than memory addresses.

Add a more generic I/O mapping method to support those devices. With this
patch, both the CPU addresses and the host-local port can be mapped into
the logical PIO space with different logical/fake PIOs. After this, all
the I/O accesses to either PCI MMIO devices or host-local I/O peripherals
can be unified into the existing I/O accessors defined in asm-generic/io.h
and be redirected to the right device-specific hooks based on the input
logical PIO.

Tested-by: dann frazier <dann.frazier@canonical.com>
Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
[bhelgaas: remove -EFAULT return from logic_pio_register_range() per
https://lkml.kernel.org/r/20180403143909.GA21171@ulmo, fix NULL pointer
checking per https://lkml.kernel.org/r/20180403211505.GA29612@embeddedor.com]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>

authored by

Zhichang Yuan and committed by
Bjorn Helgaas
031e3601 7928b2cb

+423
+2
include/asm-generic/io.h
··· 351 351 #define IO_SPACE_LIMIT 0xffff 352 352 #endif 353 353 354 + #include <linux/logic_pio.h> 355 + 354 356 /* 355 357 * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be 356 358 * implemented on hardware that needs an additional delay for I/O accesses to
+123
include/linux/logic_pio.h
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved. 4 + * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com> 5 + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com> 6 + */ 7 + 8 + #ifndef __LINUX_LOGIC_PIO_H 9 + #define __LINUX_LOGIC_PIO_H 10 + 11 + #include <linux/fwnode.h> 12 + 13 + enum { 14 + LOGIC_PIO_INDIRECT, /* Indirect IO flag */ 15 + LOGIC_PIO_CPU_MMIO, /* Memory-mapped IO flag */ 16 + }; 17 + 18 + struct logic_pio_hwaddr { 19 + struct list_head list; 20 + struct fwnode_handle *fwnode; 21 + resource_size_t hw_start; 22 + resource_size_t io_start; 23 + resource_size_t size; /* range size populated */ 24 + unsigned long flags; 25 + 26 + void *hostdata; 27 + const struct logic_pio_host_ops *ops; 28 + }; 29 + 30 + struct logic_pio_host_ops { 31 + u32 (*in)(void *hostdata, unsigned long addr, size_t dwidth); 32 + void (*out)(void *hostdata, unsigned long addr, u32 val, 33 + size_t dwidth); 34 + u32 (*ins)(void *hostdata, unsigned long addr, void *buffer, 35 + size_t dwidth, unsigned int count); 36 + void (*outs)(void *hostdata, unsigned long addr, const void *buffer, 37 + size_t dwidth, unsigned int count); 38 + }; 39 + 40 + #ifdef CONFIG_INDIRECT_PIO 41 + u8 logic_inb(unsigned long addr); 42 + void logic_outb(u8 value, unsigned long addr); 43 + void logic_outw(u16 value, unsigned long addr); 44 + void logic_outl(u32 value, unsigned long addr); 45 + u16 logic_inw(unsigned long addr); 46 + u32 logic_inl(unsigned long addr); 47 + void logic_outb(u8 value, unsigned long addr); 48 + void logic_outw(u16 value, unsigned long addr); 49 + void logic_outl(u32 value, unsigned long addr); 50 + void logic_insb(unsigned long addr, void *buffer, unsigned int count); 51 + void logic_insl(unsigned long addr, void *buffer, unsigned int count); 52 + void logic_insw(unsigned long addr, void *buffer, unsigned int count); 53 + void logic_outsb(unsigned long addr, const void *buffer, unsigned int count); 54 + void logic_outsw(unsigned long addr, const void *buffer, unsigned int count); 55 + void logic_outsl(unsigned long addr, const void *buffer, unsigned int count); 56 + 57 + #ifndef inb 58 + #define inb logic_inb 59 + #endif 60 + 61 + #ifndef inw 62 + #define inw logic_inw 63 + #endif 64 + 65 + #ifndef inl 66 + #define inl logic_inl 67 + #endif 68 + 69 + #ifndef outb 70 + #define outb logic_outb 71 + #endif 72 + 73 + #ifndef outw 74 + #define outw logic_outw 75 + #endif 76 + 77 + #ifndef outl 78 + #define outl logic_outl 79 + #endif 80 + 81 + #ifndef insb 82 + #define insb logic_insb 83 + #endif 84 + 85 + #ifndef insw 86 + #define insw logic_insw 87 + #endif 88 + 89 + #ifndef insl 90 + #define insl logic_insl 91 + #endif 92 + 93 + #ifndef outsb 94 + #define outsb logic_outsb 95 + #endif 96 + 97 + #ifndef outsw 98 + #define outsw logic_outsw 99 + #endif 100 + 101 + #ifndef outsl 102 + #define outsl logic_outsl 103 + #endif 104 + 105 + /* 106 + * We reserve 0x4000 bytes for Indirect IO as so far this library is only 107 + * used by the HiSilicon LPC Host. If needed, we can reserve a wider IO 108 + * area by redefining the macro below. 109 + */ 110 + #define PIO_INDIRECT_SIZE 0x4000 111 + #define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE) 112 + #else 113 + #define MMIO_UPPER_LIMIT IO_SPACE_LIMIT 114 + #endif /* CONFIG_INDIRECT_PIO */ 115 + 116 + struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode); 117 + unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, 118 + resource_size_t hw_addr, resource_size_t size); 119 + int logic_pio_register_range(struct logic_pio_hwaddr *newrange); 120 + resource_size_t logic_pio_to_hwaddr(unsigned long pio); 121 + unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr); 122 + 123 + #endif /* __LINUX_LOGIC_PIO_H */
+16
lib/Kconfig
··· 55 55 config ARCH_HAS_FAST_MULTIPLIER 56 56 bool 57 57 58 + config INDIRECT_PIO 59 + bool "Access I/O in non-MMIO mode" 60 + depends on ARM64 61 + help 62 + On some platforms where no separate I/O space exists, there are I/O 63 + hosts which can not be accessed in MMIO mode. Using the logical PIO 64 + mechanism, the host-local I/O resource can be mapped into system 65 + logic PIO space shared with MMIO hosts, such as PCI/PCIe, then the 66 + system can access the I/O devices with the mapped-logic PIO through 67 + I/O accessors. 68 + 69 + This way has relatively little I/O performance cost. Please make 70 + sure your devices really need this configure item enabled. 71 + 72 + When in doubt, say N. 73 + 58 74 config CRC_CCITT 59 75 tristate "CRC-CCITT functions" 60 76 help
+2
lib/Makefile
··· 81 81 obj-$(CONFIG_CHECK_SIGNATURE) += check_signature.o 82 82 obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o 83 83 84 + obj-y += logic_pio.o 85 + 84 86 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o 85 87 86 88 obj-$(CONFIG_BTREE) += btree.o
+280
lib/logic_pio.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved. 4 + * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com> 5 + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com> 6 + */ 7 + 8 + #define pr_fmt(fmt) "LOGIC PIO: " fmt 9 + 10 + #include <linux/of.h> 11 + #include <linux/io.h> 12 + #include <linux/logic_pio.h> 13 + #include <linux/mm.h> 14 + #include <linux/rculist.h> 15 + #include <linux/sizes.h> 16 + #include <linux/slab.h> 17 + 18 + /* The unique hardware address list */ 19 + static LIST_HEAD(io_range_list); 20 + static DEFINE_MUTEX(io_range_mutex); 21 + 22 + /* Consider a kernel general helper for this */ 23 + #define in_range(b, first, len) ((b) >= (first) && (b) < (first) + (len)) 24 + 25 + /** 26 + * logic_pio_register_range - register logical PIO range for a host 27 + * @new_range: pointer to the IO range to be registered. 28 + * 29 + * Returns 0 on success, the error code in case of failure. 30 + * 31 + * Register a new IO range node in the IO range list. 32 + */ 33 + int logic_pio_register_range(struct logic_pio_hwaddr *new_range) 34 + { 35 + struct logic_pio_hwaddr *range; 36 + resource_size_t start; 37 + resource_size_t end; 38 + resource_size_t mmio_sz = 0; 39 + resource_size_t iio_sz = MMIO_UPPER_LIMIT; 40 + int ret = 0; 41 + 42 + if (!new_range || !new_range->fwnode || !new_range->size) 43 + return -EINVAL; 44 + 45 + start = new_range->hw_start; 46 + end = new_range->hw_start + new_range->size; 47 + 48 + mutex_lock(&io_range_mutex); 49 + list_for_each_entry_rcu(range, &io_range_list, list) { 50 + if (range->fwnode == new_range->fwnode) { 51 + /* range already there */ 52 + goto end_register; 53 + } 54 + if (range->flags == LOGIC_PIO_CPU_MMIO && 55 + new_range->flags == LOGIC_PIO_CPU_MMIO) { 56 + /* for MMIO ranges we need to check for overlap */ 57 + if (start >= range->hw_start + range->size || 58 + end < range->hw_start) { 59 + mmio_sz += range->size; 60 + } else { 61 + ret = -EFAULT; 62 + goto end_register; 63 + } 64 + } else if (range->flags == LOGIC_PIO_INDIRECT && 65 + new_range->flags == LOGIC_PIO_INDIRECT) { 66 + iio_sz += range->size; 67 + } 68 + } 69 + 70 + /* range not registered yet, check for available space */ 71 + if (new_range->flags == LOGIC_PIO_CPU_MMIO) { 72 + if (mmio_sz + new_range->size - 1 > MMIO_UPPER_LIMIT) { 73 + /* if it's too big check if 64K space can be reserved */ 74 + if (mmio_sz + SZ_64K - 1 > MMIO_UPPER_LIMIT) { 75 + ret = -E2BIG; 76 + goto end_register; 77 + } 78 + new_range->size = SZ_64K; 79 + pr_warn("Requested IO range too big, new size set to 64K\n"); 80 + } 81 + new_range->io_start = mmio_sz; 82 + } else if (new_range->flags == LOGIC_PIO_INDIRECT) { 83 + if (iio_sz + new_range->size - 1 > IO_SPACE_LIMIT) { 84 + ret = -E2BIG; 85 + goto end_register; 86 + } 87 + new_range->io_start = iio_sz; 88 + } else { 89 + /* invalid flag */ 90 + ret = -EINVAL; 91 + goto end_register; 92 + } 93 + 94 + list_add_tail_rcu(&new_range->list, &io_range_list); 95 + 96 + end_register: 97 + mutex_unlock(&io_range_mutex); 98 + return ret; 99 + } 100 + 101 + /** 102 + * find_io_range_by_fwnode - find logical PIO range for given FW node 103 + * @fwnode: FW node handle associated with logical PIO range 104 + * 105 + * Returns pointer to node on success, NULL otherwise. 106 + * 107 + * Traverse the io_range_list to find the registered node for @fwnode. 108 + */ 109 + struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode) 110 + { 111 + struct logic_pio_hwaddr *range; 112 + 113 + list_for_each_entry_rcu(range, &io_range_list, list) { 114 + if (range->fwnode == fwnode) 115 + return range; 116 + } 117 + return NULL; 118 + } 119 + 120 + /* Return a registered range given an input PIO token */ 121 + static struct logic_pio_hwaddr *find_io_range(unsigned long pio) 122 + { 123 + struct logic_pio_hwaddr *range; 124 + 125 + list_for_each_entry_rcu(range, &io_range_list, list) { 126 + if (in_range(pio, range->io_start, range->size)) 127 + return range; 128 + } 129 + pr_err("PIO entry token %lx invalid\n", pio); 130 + return NULL; 131 + } 132 + 133 + /** 134 + * logic_pio_to_hwaddr - translate logical PIO to HW address 135 + * @pio: logical PIO value 136 + * 137 + * Returns HW address if valid, ~0 otherwise. 138 + * 139 + * Translate the input logical PIO to the corresponding hardware address. 140 + * The input PIO should be unique in the whole logical PIO space. 141 + */ 142 + resource_size_t logic_pio_to_hwaddr(unsigned long pio) 143 + { 144 + struct logic_pio_hwaddr *range; 145 + 146 + range = find_io_range(pio); 147 + if (range) 148 + return range->hw_start + pio - range->io_start; 149 + 150 + return (resource_size_t)~0; 151 + } 152 + 153 + /** 154 + * logic_pio_trans_hwaddr - translate HW address to logical PIO 155 + * @fwnode: FW node reference for the host 156 + * @addr: Host-relative HW address 157 + * @size: size to translate 158 + * 159 + * Returns Logical PIO value if successful, ~0UL otherwise 160 + */ 161 + unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, 162 + resource_size_t addr, resource_size_t size) 163 + { 164 + struct logic_pio_hwaddr *range; 165 + 166 + range = find_io_range_by_fwnode(fwnode); 167 + if (!range || range->flags == LOGIC_PIO_CPU_MMIO) { 168 + pr_err("IO range not found or invalid\n"); 169 + return ~0UL; 170 + } 171 + if (range->size < size) { 172 + pr_err("resource size %pa cannot fit in IO range size %pa\n", 173 + &size, &range->size); 174 + return ~0UL; 175 + } 176 + return addr - range->hw_start + range->io_start; 177 + } 178 + 179 + unsigned long logic_pio_trans_cpuaddr(resource_size_t addr) 180 + { 181 + struct logic_pio_hwaddr *range; 182 + 183 + list_for_each_entry_rcu(range, &io_range_list, list) { 184 + if (range->flags != LOGIC_PIO_CPU_MMIO) 185 + continue; 186 + if (in_range(addr, range->hw_start, range->size)) 187 + return addr - range->hw_start + range->io_start; 188 + } 189 + pr_err("addr %llx not registered in io_range_list\n", 190 + (unsigned long long) addr); 191 + return ~0UL; 192 + } 193 + 194 + #if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE) 195 + #define BUILD_LOGIC_IO(bw, type) \ 196 + type logic_in##bw(unsigned long addr) \ 197 + { \ 198 + type ret = (type)~0; \ 199 + \ 200 + if (addr < MMIO_UPPER_LIMIT) { \ 201 + ret = read##bw(PCI_IOBASE + addr); \ 202 + } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ 203 + struct logic_pio_hwaddr *entry = find_io_range(addr); \ 204 + \ 205 + if (entry && entry->ops) \ 206 + ret = entry->ops->in(entry->hostdata, \ 207 + addr, sizeof(type)); \ 208 + else \ 209 + WARN_ON_ONCE(1); \ 210 + } \ 211 + return ret; \ 212 + } \ 213 + \ 214 + void logic_out##bw(type value, unsigned long addr) \ 215 + { \ 216 + if (addr < MMIO_UPPER_LIMIT) { \ 217 + write##bw(value, PCI_IOBASE + addr); \ 218 + } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ 219 + struct logic_pio_hwaddr *entry = find_io_range(addr); \ 220 + \ 221 + if (entry && entry->ops) \ 222 + entry->ops->out(entry->hostdata, \ 223 + addr, value, sizeof(type)); \ 224 + else \ 225 + WARN_ON_ONCE(1); \ 226 + } \ 227 + } \ 228 + \ 229 + void logic_ins##bw(unsigned long addr, void *buffer, \ 230 + unsigned int count) \ 231 + { \ 232 + if (addr < MMIO_UPPER_LIMIT) { \ 233 + reads##bw(PCI_IOBASE + addr, buffer, count); \ 234 + } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ 235 + struct logic_pio_hwaddr *entry = find_io_range(addr); \ 236 + \ 237 + if (entry && entry->ops) \ 238 + entry->ops->ins(entry->hostdata, \ 239 + addr, buffer, sizeof(type), count); \ 240 + else \ 241 + WARN_ON_ONCE(1); \ 242 + } \ 243 + \ 244 + } \ 245 + \ 246 + void logic_outs##bw(unsigned long addr, const void *buffer, \ 247 + unsigned int count) \ 248 + { \ 249 + if (addr < MMIO_UPPER_LIMIT) { \ 250 + writes##bw(PCI_IOBASE + addr, buffer, count); \ 251 + } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ 252 + struct logic_pio_hwaddr *entry = find_io_range(addr); \ 253 + \ 254 + if (entry && entry->ops) \ 255 + entry->ops->outs(entry->hostdata, \ 256 + addr, buffer, sizeof(type), count); \ 257 + else \ 258 + WARN_ON_ONCE(1); \ 259 + } \ 260 + } 261 + 262 + BUILD_LOGIC_IO(b, u8) 263 + EXPORT_SYMBOL(logic_inb); 264 + EXPORT_SYMBOL(logic_insb); 265 + EXPORT_SYMBOL(logic_outb); 266 + EXPORT_SYMBOL(logic_outsb); 267 + 268 + BUILD_LOGIC_IO(w, u16) 269 + EXPORT_SYMBOL(logic_inw); 270 + EXPORT_SYMBOL(logic_insw); 271 + EXPORT_SYMBOL(logic_outw); 272 + EXPORT_SYMBOL(logic_outsw); 273 + 274 + BUILD_LOGIC_IO(l, u32) 275 + EXPORT_SYMBOL(logic_inl); 276 + EXPORT_SYMBOL(logic_insl); 277 + EXPORT_SYMBOL(logic_outl); 278 + EXPORT_SYMBOL(logic_outsl); 279 + 280 + #endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */