linux/arch/powerpc/platforms/celleb/io-workarounds.c
<<
>>
Prefs
   1/*
   2 * Support for Celleb io workarounds
   3 *
   4 * (C) Copyright 2006-2007 TOSHIBA CORPORATION
   5 *
   6 * This file is based to arch/powerpc/platform/cell/io-workarounds.c
   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 as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License along
  19 * with this program; if not, write to the Free Software Foundation, Inc.,
  20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21 */
  22
  23#undef DEBUG
  24
  25#include <linux/of_device.h>
  26#include <linux/irq.h>
  27
  28#include <asm/io.h>
  29#include <asm/prom.h>
  30#include <asm/machdep.h>
  31#include <asm/pci-bridge.h>
  32#include <asm/ppc-pci.h>
  33
  34#include "pci.h"
  35
  36#define MAX_CELLEB_PCI_BUS      4
  37
  38void *celleb_dummy_page_va;
  39
  40static struct celleb_pci_bus {
  41        struct pci_controller *phb;
  42        void (*dummy_read)(struct pci_controller *);
  43} celleb_pci_busses[MAX_CELLEB_PCI_BUS];
  44
  45static int celleb_pci_count = 0;
  46
  47static struct celleb_pci_bus *celleb_pci_find(unsigned long vaddr,
  48                                              unsigned long paddr)
  49{
  50        int i, j;
  51        struct resource *res;
  52
  53        for (i = 0; i < celleb_pci_count; i++) {
  54                struct celleb_pci_bus *bus = &celleb_pci_busses[i];
  55                struct pci_controller *phb = bus->phb;
  56                if (paddr)
  57                        for (j = 0; j < 3; j++) {
  58                                res = &phb->mem_resources[j];
  59                                if (paddr >= res->start && paddr <= res->end)
  60                                        return bus;
  61                        }
  62                res = &phb->io_resource;
  63                if (vaddr && vaddr >= res->start && vaddr <= res->end)
  64                        return bus;
  65        }
  66        return NULL;
  67}
  68
  69static void celleb_io_flush(const PCI_IO_ADDR addr)
  70{
  71        struct celleb_pci_bus *bus;
  72        int token;
  73
  74        token = PCI_GET_ADDR_TOKEN(addr);
  75
  76        if (token && token <= celleb_pci_count)
  77                bus = &celleb_pci_busses[token - 1];
  78        else {
  79                unsigned long vaddr, paddr;
  80                pte_t *ptep;
  81
  82                vaddr = (unsigned long)PCI_FIX_ADDR(addr);
  83                if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END)
  84                        return;
  85
  86                ptep = find_linux_pte(init_mm.pgd, vaddr);
  87                if (ptep == NULL)
  88                        paddr = 0;
  89                else
  90                        paddr = pte_pfn(*ptep) << PAGE_SHIFT;
  91                bus = celleb_pci_find(vaddr, paddr);
  92
  93                if (bus == NULL)
  94                        return;
  95        }
  96
  97        if (bus->dummy_read)
  98                bus->dummy_read(bus->phb);
  99}
 100
 101static u8 celleb_readb(const PCI_IO_ADDR addr)
 102{
 103        u8 val;
 104        val = __do_readb(addr);
 105        celleb_io_flush(addr);
 106        return val;
 107}
 108
 109static u16 celleb_readw(const PCI_IO_ADDR addr)
 110{
 111        u16 val;
 112        val = __do_readw(addr);
 113        celleb_io_flush(addr);
 114        return val;
 115}
 116
 117static u32 celleb_readl(const PCI_IO_ADDR addr)
 118{
 119        u32 val;
 120        val = __do_readl(addr);
 121        celleb_io_flush(addr);
 122        return val;
 123}
 124
 125static u64 celleb_readq(const PCI_IO_ADDR addr)
 126{
 127        u64 val;
 128        val = __do_readq(addr);
 129        celleb_io_flush(addr);
 130        return val;
 131}
 132
 133static u16 celleb_readw_be(const PCI_IO_ADDR addr)
 134{
 135        u16 val;
 136        val = __do_readw_be(addr);
 137        celleb_io_flush(addr);
 138        return val;
 139}
 140
 141static u32 celleb_readl_be(const PCI_IO_ADDR addr)
 142{
 143        u32 val;
 144        val = __do_readl_be(addr);
 145        celleb_io_flush(addr);
 146        return val;
 147}
 148
 149static u64 celleb_readq_be(const PCI_IO_ADDR addr)
 150{
 151        u64 val;
 152        val = __do_readq_be(addr);
 153        celleb_io_flush(addr);
 154        return val;
 155}
 156
 157static void celleb_readsb(const PCI_IO_ADDR addr,
 158                          void *buf, unsigned long count)
 159{
 160        __do_readsb(addr, buf, count);
 161        celleb_io_flush(addr);
 162}
 163
 164static void celleb_readsw(const PCI_IO_ADDR addr,
 165                          void *buf, unsigned long count)
 166{
 167        __do_readsw(addr, buf, count);
 168        celleb_io_flush(addr);
 169}
 170
 171static void celleb_readsl(const PCI_IO_ADDR addr,
 172                          void *buf, unsigned long count)
 173{
 174        __do_readsl(addr, buf, count);
 175        celleb_io_flush(addr);
 176}
 177
 178static void celleb_memcpy_fromio(void *dest,
 179                                 const PCI_IO_ADDR src,
 180                                 unsigned long n)
 181{
 182        __do_memcpy_fromio(dest, src, n);
 183        celleb_io_flush(src);
 184}
 185
 186static void __iomem *celleb_ioremap(unsigned long addr,
 187                                     unsigned long size,
 188                                     unsigned long flags)
 189{
 190        struct celleb_pci_bus *bus;
 191        void __iomem *res = __ioremap(addr, size, flags);
 192        int busno;
 193
 194        bus = celleb_pci_find(0, addr);
 195        if (bus != NULL) {
 196                busno = bus - celleb_pci_busses;
 197                PCI_SET_ADDR_TOKEN(res, busno + 1);
 198        }
 199        return res;
 200}
 201
 202static void celleb_iounmap(volatile void __iomem *addr)
 203{
 204        return __iounmap(PCI_FIX_ADDR(addr));
 205}
 206
 207static struct ppc_pci_io celleb_pci_io __initdata = {
 208        .readb = celleb_readb,
 209        .readw = celleb_readw,
 210        .readl = celleb_readl,
 211        .readq = celleb_readq,
 212        .readw_be = celleb_readw_be,
 213        .readl_be = celleb_readl_be,
 214        .readq_be = celleb_readq_be,
 215        .readsb = celleb_readsb,
 216        .readsw = celleb_readsw,
 217        .readsl = celleb_readsl,
 218        .memcpy_fromio = celleb_memcpy_fromio,
 219};
 220
 221void __init celleb_pci_add_one(struct pci_controller *phb,
 222                               void (*dummy_read)(struct pci_controller *))
 223{
 224        struct celleb_pci_bus *bus = &celleb_pci_busses[celleb_pci_count];
 225        struct device_node *np = phb->arch_data;
 226
 227        if (celleb_pci_count >= MAX_CELLEB_PCI_BUS) {
 228                printk(KERN_ERR "Too many pci bridges, workarounds"
 229                       " disabled for %s\n", np->full_name);
 230                return;
 231        }
 232
 233        celleb_pci_count++;
 234
 235        bus->phb = phb;
 236        bus->dummy_read = dummy_read;
 237}
 238
 239static struct of_device_id celleb_pci_workaround_match[] __initdata = {
 240        {
 241                .name = "pci-pseudo",
 242                .data = fake_pci_workaround_init,
 243        }, {
 244                .name = "epci",
 245                .data = epci_workaround_init,
 246        }, {
 247        },
 248};
 249
 250int __init celleb_pci_workaround_init(void)
 251{
 252        struct pci_controller *phb;
 253        struct device_node *node;
 254        const struct  of_device_id *match;
 255        void (*init_func)(struct pci_controller *);
 256
 257        celleb_dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL);
 258        if (!celleb_dummy_page_va) {
 259                printk(KERN_ERR "Celleb: dummy read disabled."
 260                        "Alloc celleb_dummy_page_va failed\n");
 261                return 1;
 262        }
 263
 264        list_for_each_entry(phb, &hose_list, list_node) {
 265                node = phb->arch_data;
 266                match = of_match_node(celleb_pci_workaround_match, node);
 267
 268                if (match) {
 269                        init_func = match->data;
 270                        (*init_func)(phb);
 271                }
 272        }
 273
 274        ppc_pci_io = celleb_pci_io;
 275        ppc_md.ioremap = celleb_ioremap;
 276        ppc_md.iounmap = celleb_iounmap;
 277
 278        return 0;
 279}
 280