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        struct iowa_bus *bus;
  59        int token;
  60
  61        token = PCI_GET_ADDR_TOKEN(addr);
  62
  63        if (token && token <= iowa_bus_count)
  64                bus = &iowa_busses[token - 1];
  65        else {
  66                unsigned long vaddr, paddr;
  67
  68                vaddr = (unsigned long)PCI_FIX_ADDR(addr);
  69                if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END)
  70                        return NULL;
  71
  72                paddr = ppc_find_vmap_phys(vaddr);
  73
  74                bus = iowa_pci_find(vaddr, paddr);
  75
  76                if (bus == NULL)
  77                        return NULL;
  78        }
  79
  80        return bus;
  81}
  82#else /* CONFIG_PPC_INDIRECT_MMIO */
  83struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr)
  84{
  85        return NULL;
  86}
  87#endif /* !CONFIG_PPC_INDIRECT_MMIO */
  88
  89#ifdef CONFIG_PPC_INDIRECT_PIO
  90struct iowa_bus *iowa_pio_find_bus(unsigned long port)
  91{
  92        unsigned long vaddr = (unsigned long)pci_io_base + port;
  93        return iowa_pci_find(vaddr, 0);
  94}
  95#else
  96struct iowa_bus *iowa_pio_find_bus(unsigned long port)
  97{
  98        return NULL;
  99}
 100#endif
 101
 102#define DEF_PCI_AC_RET(name, ret, at, al, space, aa)            \
 103static ret iowa_##name at                                       \
 104{                                                               \
 105        struct iowa_bus *bus;                                   \
 106        bus = iowa_##space##_find_bus(aa);                      \
 107        if (bus && bus->ops && bus->ops->name)                  \
 108                return bus->ops->name al;                       \
 109        return __do_##name al;                                  \
 110}
 111
 112#define DEF_PCI_AC_NORET(name, at, al, space, aa)               \
 113static void 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                bus->ops->name al;                              \
 119                return;                                         \
 120        }                                                       \
 121        __do_##name al;                                         \
 122}
 123
 124#include <asm/io-defs.h>
 125
 126#undef DEF_PCI_AC_RET
 127#undef DEF_PCI_AC_NORET
 128
 129static const struct ppc_pci_io iowa_pci_io = {
 130
 131#define DEF_PCI_AC_RET(name, ret, at, al, space, aa)    .name = iowa_##name,
 132#define DEF_PCI_AC_NORET(name, at, al, space, aa)       .name = iowa_##name,
 133
 134#include <asm/io-defs.h>
 135
 136#undef DEF_PCI_AC_RET
 137#undef DEF_PCI_AC_NORET
 138
 139};
 140
 141#ifdef CONFIG_PPC_INDIRECT_MMIO
 142void __iomem *iowa_ioremap(phys_addr_t addr, unsigned long size,
 143                           pgprot_t prot, void *caller)
 144{
 145        struct iowa_bus *bus;
 146        void __iomem *res = __ioremap_caller(addr, size, prot, caller);
 147        int busno;
 148
 149        bus = iowa_pci_find(0, (unsigned long)addr);
 150        if (bus != NULL) {
 151                busno = bus - iowa_busses;
 152                PCI_SET_ADDR_TOKEN(res, busno + 1);
 153        }
 154        return res;
 155}
 156#endif /* !CONFIG_PPC_INDIRECT_MMIO */
 157
 158bool io_workaround_inited;
 159
 160/* Enable IO workaround */
 161static void io_workaround_init(void)
 162{
 163        if (io_workaround_inited)
 164                return;
 165        ppc_pci_io = iowa_pci_io;
 166        io_workaround_inited = true;
 167}
 168
 169/* Register new bus to support workaround */
 170void iowa_register_bus(struct pci_controller *phb, struct ppc_pci_io *ops,
 171                       int (*initfunc)(struct iowa_bus *, void *), void *data)
 172{
 173        struct iowa_bus *bus;
 174        struct device_node *np = phb->dn;
 175
 176        io_workaround_init();
 177
 178        if (iowa_bus_count >= IOWA_MAX_BUS) {
 179                pr_err("IOWA:Too many pci bridges, "
 180                       "workarounds disabled for %pOF\n", np);
 181                return;
 182        }
 183
 184        bus = &iowa_busses[iowa_bus_count];
 185        bus->phb = phb;
 186        bus->ops = ops;
 187        bus->private = data;
 188
 189        if (initfunc)
 190                if ((*initfunc)(bus, data))
 191                        return;
 192
 193        iowa_bus_count++;
 194
 195        pr_debug("IOWA:[%d]Add bus, %pOF.\n", iowa_bus_count-1, np);
 196}
 197
 198