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