linux/arch/powerpc/platforms/pasemi/pci.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2006 PA Semi, Inc
   3 *
   4 * Authors: Kip Walker, PA Semi
   5 *          Olof Johansson, PA Semi
   6 *
   7 * Maintained by: Olof Johansson <olof@lixom.net>
   8 *
   9 * Based on arch/powerpc/platforms/maple/pci.c
  10 *
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License version 2 as
  13 * published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  23 */
  24
  25
  26#include <linux/kernel.h>
  27#include <linux/pci.h>
  28
  29#include <asm/pci-bridge.h>
  30#include <asm/machdep.h>
  31
  32#include <asm/ppc-pci.h>
  33
  34#include "pasemi.h"
  35
  36#define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
  37
  38static inline int pa_pxp_offset_valid(u8 bus, u8 devfn, int offset)
  39{
  40        /* Device 0 Function 0 is special: It's config space spans function 1 as
  41         * well, so allow larger offset. It's really a two-function device but the
  42         * second function does not probe.
  43         */
  44        if (bus == 0 && devfn == 0)
  45                return offset < 8192;
  46        else
  47                return offset < 4096;
  48}
  49
  50static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose,
  51                                       u8 bus, u8 devfn, int offset)
  52{
  53        return hose->cfg_data + PA_PXP_CFA(bus, devfn, offset);
  54}
  55
  56static inline int is_root_port(int busno, int devfn)
  57{
  58        return ((busno == 0) && (PCI_FUNC(devfn) < 4) &&
  59                 ((PCI_SLOT(devfn) == 16) || (PCI_SLOT(devfn) == 17)));
  60}
  61
  62static inline int is_5945_reg(int reg)
  63{
  64        return (((reg >= 0x18) && (reg < 0x34)) ||
  65                ((reg >= 0x158) && (reg < 0x178)));
  66}
  67
  68static int workaround_5945(struct pci_bus *bus, unsigned int devfn,
  69                           int offset, int len, u32 *val)
  70{
  71        struct pci_controller *hose;
  72        void volatile __iomem *addr, *dummy;
  73        int byte;
  74        u32 tmp;
  75
  76        if (!is_root_port(bus->number, devfn) || !is_5945_reg(offset))
  77                return 0;
  78
  79        hose = pci_bus_to_host(bus);
  80
  81        addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset & ~0x3);
  82        byte = offset & 0x3;
  83
  84        /* Workaround bug 5945: write 0 to a dummy register before reading,
  85         * and write back what we read. We must read/write the full 32-bit
  86         * contents so we need to shift and mask by hand.
  87         */
  88        dummy = pa_pxp_cfg_addr(hose, bus->number, devfn, 0x10);
  89        out_le32(dummy, 0);
  90        tmp = in_le32(addr);
  91        out_le32(addr, tmp);
  92
  93        switch (len) {
  94        case 1:
  95                *val = (tmp >> (8*byte)) & 0xff;
  96                break;
  97        case 2:
  98                if (byte == 0)
  99                        *val = tmp & 0xffff;
 100                else
 101                        *val = (tmp >> 16) & 0xffff;
 102                break;
 103        default:
 104                *val = tmp;
 105                break;
 106        }
 107
 108        return 1;
 109}
 110
 111static int pa_pxp_read_config(struct pci_bus *bus, unsigned int devfn,
 112                              int offset, int len, u32 *val)
 113{
 114        struct pci_controller *hose;
 115        void volatile __iomem *addr;
 116
 117        hose = pci_bus_to_host(bus);
 118        if (!hose)
 119                return PCIBIOS_DEVICE_NOT_FOUND;
 120
 121        if (!pa_pxp_offset_valid(bus->number, devfn, offset))
 122                return PCIBIOS_BAD_REGISTER_NUMBER;
 123
 124        if (workaround_5945(bus, devfn, offset, len, val))
 125                return PCIBIOS_SUCCESSFUL;
 126
 127        addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
 128
 129        /*
 130         * Note: the caller has already checked that offset is
 131         * suitably aligned and that len is 1, 2 or 4.
 132         */
 133        switch (len) {
 134        case 1:
 135                *val = in_8(addr);
 136                break;
 137        case 2:
 138                *val = in_le16(addr);
 139                break;
 140        default:
 141                *val = in_le32(addr);
 142                break;
 143        }
 144
 145        return PCIBIOS_SUCCESSFUL;
 146}
 147
 148static int pa_pxp_write_config(struct pci_bus *bus, unsigned int devfn,
 149                               int offset, int len, u32 val)
 150{
 151        struct pci_controller *hose;
 152        void volatile __iomem *addr;
 153
 154        hose = pci_bus_to_host(bus);
 155        if (!hose)
 156                return PCIBIOS_DEVICE_NOT_FOUND;
 157
 158        if (!pa_pxp_offset_valid(bus->number, devfn, offset))
 159                return PCIBIOS_BAD_REGISTER_NUMBER;
 160
 161        addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
 162
 163        /*
 164         * Note: the caller has already checked that offset is
 165         * suitably aligned and that len is 1, 2 or 4.
 166         */
 167        switch (len) {
 168        case 1:
 169                out_8(addr, val);
 170                break;
 171        case 2:
 172                out_le16(addr, val);
 173                break;
 174        default:
 175                out_le32(addr, val);
 176                break;
 177        }
 178        return PCIBIOS_SUCCESSFUL;
 179}
 180
 181static struct pci_ops pa_pxp_ops = {
 182        .read = pa_pxp_read_config,
 183        .write = pa_pxp_write_config,
 184};
 185
 186static void __init setup_pa_pxp(struct pci_controller *hose)
 187{
 188        hose->ops = &pa_pxp_ops;
 189        hose->cfg_data = ioremap(0xe0000000, 0x10000000);
 190}
 191
 192static int __init pas_add_bridge(struct device_node *dev)
 193{
 194        struct pci_controller *hose;
 195
 196        pr_debug("Adding PCI host bridge %pOF\n", dev);
 197
 198        hose = pcibios_alloc_controller(dev);
 199        if (!hose)
 200                return -ENOMEM;
 201
 202        hose->first_busno = 0;
 203        hose->last_busno = 0xff;
 204        hose->controller_ops = pasemi_pci_controller_ops;
 205
 206        setup_pa_pxp(hose);
 207
 208        printk(KERN_INFO "Found PA-PXP PCI host bridge.\n");
 209
 210        /* Interpret the "ranges" property */
 211        pci_process_bridge_OF_ranges(hose, dev, 1);
 212
 213        return 0;
 214}
 215
 216void __init pas_pci_init(void)
 217{
 218        struct device_node *np, *root;
 219
 220        root = of_find_node_by_path("/");
 221        if (!root) {
 222                printk(KERN_CRIT "pas_pci_init: can't find root "
 223                        "of device tree\n");
 224                return;
 225        }
 226
 227        pci_set_flags(PCI_SCAN_ALL_PCIE_DEVS);
 228
 229        for (np = NULL; (np = of_get_next_child(root, np)) != NULL;)
 230                if (np->name && !strcmp(np->name, "pxp") && !pas_add_bridge(np))
 231                        of_node_get(np);
 232
 233        of_node_put(root);
 234}
 235
 236void __iomem *pasemi_pci_getcfgaddr(struct pci_dev *dev, int offset)
 237{
 238        struct pci_controller *hose;
 239
 240        hose = pci_bus_to_host(dev->bus);
 241
 242        return (void __iomem *)pa_pxp_cfg_addr(hose, dev->bus->number, dev->devfn, offset);
 243}
 244
 245struct pci_controller_ops pasemi_pci_controller_ops;
 246