linux/arch/powerpc/kernel/io-workarounds.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Support PCI IO workaround
   4 *
   5 *  Copyright (C) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>
   6 *                     IBM, Corp.
   7 *  (C) Copyright 2007-2008 TOSHIBA CORPORATION
   8 */
   9#undef DEBUG
  10
  11#include <linux/kernel.h>
  12#include <linux/sched/mm.h>     /* for init_mm */
  13#include <linux/pgtable.h>
  14
  15#include <asm/io.h>
  16#include <asm/machdep.h>
  17#include <asm/ppc-pci.h>
  18#include <asm/io-workarounds.h>
  19#include <asm/pte-walk.h>
  20
  21
  22#define IOWA_MAX_BUS    8
  23
  24static struct iowa_bus iowa_busses[IOWA_MAX_BUS];
  25static unsigned int iowa_bus_count;
  26
  27static struct iowa_bus *iowa_pci_find(unsigned long vaddr, unsigned long paddr)
  28{
  29        int i, j;
  30        struct resource *res;
  31        unsigned long vstart, vend;
  32
  33        for (i = 0; i < iowa_bus_count; i++) {
  34                struct iowa_bus *bus = &iowa_busses[i];
  35                struct pci_controller *phb = bus->phb;
  36
  37                if (vaddr) {
  38                        vstart = (unsigned long)phb->io_base_virt;
  39                        vend = vstart + phb->pci_io_size - 1;
  40                        if ((vaddr >= vstart) && (vaddr <= vend))
  41                                return bus;
  42                }
  43
  44                if (paddr)
  45                        for (j = 0; j < 3; j++) {
  46                                res = &phb->mem_resources[j];
  47                                if (paddr >= res->start && paddr <= res->end)
  48                                        return bus;
  49                        }
  50        }
  51
  52        return NULL;
  53}
  54
  55#ifdef CONFIG_PPC_INDIRECT_MMIO
  56struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr)
  57{
  58        unsigned hugepage_shift;
  59        struct iowa_bus *bus;
  60        int token;
  61
  62        token = PCI_GET_ADDR_TOKEN(addr);
  63
  64        if (token && token <= iowa_bus_count)
  65                bus = &iowa_busses[token - 1];
  66        else {
  67                unsigned long vaddr, paddr;
  68                pte_t *ptep;
  69
  70                vaddr = (unsigned long)PCI_FIX_ADDR(addr);
  71                if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END)
  72                        return NULL;
  73                /*
  74                 * We won't find huge pages here (iomem). Also can't hit
  75                 * a page table free due to init_mm
  76                 */
  77                ptep = find_init_mm_pte(vaddr, &hugepage_shift);
  78                if (ptep == NULL)
  79                        paddr = 0;
  80                else {
  81                        WARN_ON(hugepage_shift);
  82                        paddr = pte_pfn(*ptep) << PAGE_SHIFT;
  83                }
  84                bus = iowa_pci_find(vaddr, paddr);
  85
  86                if (bus == NULL)
  87                        return NULL;
  88        }
  89
  90        return bus;
  91}
  92#else /* CONFIG_PPC_INDIRECT_MMIO */
  93struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr)
  94{
  95        return NULL;
  96}
  97#endif /* !CONFIG_PPC_INDIRECT_MMIO */
  98
  99#ifdef CONFIG_PPC_INDIRECT_PIO
 100struct iowa_bus *iowa_pio_find_bus(unsigned long port)
 101{
 102        unsigned long vaddr = (unsigned long)pci_io_base + port;
 103        return iowa_pci_find(vaddr, 0);
 104}
 105#else
 106struct iowa_bus *iowa_pio_find_bus(unsigned long port)
 107{
 108        return NULL;
 109}
 110#endif
 111
 112#define DEF_PCI_AC_RET(name, ret, at, al, space, aa)            \
 113static ret iowa_##name at                                       \
 114{                                                               \
 115        struct iowa_bus *bus;                                   \
 116        bus = iowa_##space##_find_bus(aa);                      \
 117        if (bus && bus->ops && bus->ops->name)                  \
 118                return bus->ops->name al;                       \
 119        return __do_##name al;                                  \
 120}
 121
 122#define DEF_PCI_AC_NORET(name, at, al, space, aa)               \
 123static void iowa_##name at                                      \
 124{                                                               \
 125        struct iowa_bus *bus;                                   \
 126        bus = iowa_##space##_find_bus(aa);                      \
 127        if (bus && bus->ops && bus->ops->name) {                \
 128                bus->ops->name al;                              \
 129                return;                                         \
 130        }                                                       \
 131        __do_##name al;                                         \
 132}
 133
 134#include <asm/io-defs.h>
 135
 136#undef DEF_PCI_AC_RET
 137#undef DEF_PCI_AC_NORET
 138
 139static const struct ppc_pci_io iowa_pci_io = {
 140
 141#define DEF_PCI_AC_RET(name, ret, at, al, space, aa)    .name = iowa_##name,
 142#define DEF_PCI_AC_NORET(name, at, al, space, aa)       .name = iowa_##name,
 143
 144#include <asm/io-defs.h>
 145
 146#undef DEF_PCI_AC_RET
 147#undef DEF_PCI_AC_NORET
 148
 149};
 150
 151#ifdef CONFIG_PPC_INDIRECT_MMIO
 152void __iomem *iowa_ioremap(phys_addr_t addr, unsigned long size,
 153                           pgprot_t prot, void *caller)
 154{
 155        struct iowa_bus *bus;
 156        void __iomem *res = __ioremap_caller(addr, size, prot, caller);
 157        int busno;
 158
 159        bus = iowa_pci_find(0, (unsigned long)addr);
 160        if (bus != NULL) {
 161                busno = bus - iowa_busses;
 162                PCI_SET_ADDR_TOKEN(res, busno + 1);
 163        }
 164        return res;
 165}
 166#endif /* !CONFIG_PPC_INDIRECT_MMIO */
 167
 168bool io_workaround_inited;
 169
 170/* Enable IO workaround */
 171static void io_workaround_init(void)
 172{
 173        if (io_workaround_inited)
 174                return;
 175        ppc_pci_io = iowa_pci_io;
 176        io_workaround_inited = true;
 177}
 178
 179/* Register new bus to support workaround */
 180void iowa_register_bus(struct pci_controller *phb, struct ppc_pci_io *ops,
 181                       int (*initfunc)(struct iowa_bus *, void *), void *data)
 182{
 183        struct iowa_bus *bus;
 184        struct device_node *np = phb->dn;
 185
 186        io_workaround_init();
 187
 188        if (iowa_bus_count >= IOWA_MAX_BUS) {
 189                pr_err("IOWA:Too many pci bridges, "
 190                       "workarounds disabled for %pOF\n", np);
 191                return;
 192        }
 193
 194        bus = &iowa_busses[iowa_bus_count];
 195        bus->phb = phb;
 196        bus->ops = ops;
 197        bus->private = data;
 198
 199        if (initfunc)
 200                if ((*initfunc)(bus, data))
 201                        return;
 202
 203        iowa_bus_count++;
 204
 205        pr_debug("IOWA:[%d]Add bus, %pOF.\n", iowa_bus_count-1, np);
 206}
 207
 208