linux/arch/powerpc/platforms/cell/celleb_pci.c
<<
>>
Prefs
   1/*
   2 * Support for PCI on Celleb platform.
   3 *
   4 * (C) Copyright 2006-2007 TOSHIBA CORPORATION
   5 *
   6 * This code is based on arch/powerpc/kernel/rtas_pci.c:
   7 *  Copyright (C) 2001 Dave Engebretsen, IBM Corporation
   8 *  Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation; either version 2 of the License, or
  13 * (at your option) any later version.
  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 along
  21 * with this program; if not, write to the Free Software Foundation, Inc.,
  22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  23 */
  24
  25#undef DEBUG
  26
  27#include <linux/kernel.h>
  28#include <linux/threads.h>
  29#include <linux/pci.h>
  30#include <linux/string.h>
  31#include <linux/init.h>
  32#include <linux/bootmem.h>
  33#include <linux/pci_regs.h>
  34#include <linux/of.h>
  35#include <linux/of_device.h>
  36#include <linux/slab.h>
  37
  38#include <asm/io.h>
  39#include <asm/irq.h>
  40#include <asm/prom.h>
  41#include <asm/pci-bridge.h>
  42#include <asm/ppc-pci.h>
  43
  44#include "io-workarounds.h"
  45#include "celleb_pci.h"
  46
  47#define MAX_PCI_DEVICES    32
  48#define MAX_PCI_FUNCTIONS   8
  49#define MAX_PCI_BASE_ADDRS  3 /* use 64 bit address */
  50
  51/* definition for fake pci configuration area for GbE, .... ,and etc. */
  52
  53struct celleb_pci_resource {
  54        struct resource r[MAX_PCI_BASE_ADDRS];
  55};
  56
  57struct celleb_pci_private {
  58        unsigned char *fake_config[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS];
  59        struct celleb_pci_resource *res[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS];
  60};
  61
  62static inline u8 celleb_fake_config_readb(void *addr)
  63{
  64        u8 *p = addr;
  65        return *p;
  66}
  67
  68static inline u16 celleb_fake_config_readw(void *addr)
  69{
  70        __le16 *p = addr;
  71        return le16_to_cpu(*p);
  72}
  73
  74static inline u32 celleb_fake_config_readl(void *addr)
  75{
  76        __le32 *p = addr;
  77        return le32_to_cpu(*p);
  78}
  79
  80static inline void celleb_fake_config_writeb(u32 val, void *addr)
  81{
  82        u8 *p = addr;
  83        *p = val;
  84}
  85
  86static inline void celleb_fake_config_writew(u32 val, void *addr)
  87{
  88        __le16 val16;
  89        __le16 *p = addr;
  90        val16 = cpu_to_le16(val);
  91        *p = val16;
  92}
  93
  94static inline void celleb_fake_config_writel(u32 val, void *addr)
  95{
  96        __le32 val32;
  97        __le32 *p = addr;
  98        val32 = cpu_to_le32(val);
  99        *p = val32;
 100}
 101
 102static unsigned char *get_fake_config_start(struct pci_controller *hose,
 103                                            int devno, int fn)
 104{
 105        struct celleb_pci_private *private = hose->private_data;
 106
 107        if (private == NULL)
 108                return NULL;
 109
 110        return private->fake_config[devno][fn];
 111}
 112
 113static struct celleb_pci_resource *get_resource_start(
 114                                struct pci_controller *hose,
 115                                int devno, int fn)
 116{
 117        struct celleb_pci_private *private = hose->private_data;
 118
 119        if (private == NULL)
 120                return NULL;
 121
 122        return private->res[devno][fn];
 123}
 124
 125
 126static void celleb_config_read_fake(unsigned char *config, int where,
 127                                    int size, u32 *val)
 128{
 129        char *p = config + where;
 130
 131        switch (size) {
 132        case 1:
 133                *val = celleb_fake_config_readb(p);
 134                break;
 135        case 2:
 136                *val = celleb_fake_config_readw(p);
 137                break;
 138        case 4:
 139                *val = celleb_fake_config_readl(p);
 140                break;
 141        }
 142}
 143
 144static void celleb_config_write_fake(unsigned char *config, int where,
 145                                     int size, u32 val)
 146{
 147        char *p = config + where;
 148
 149        switch (size) {
 150        case 1:
 151                celleb_fake_config_writeb(val, p);
 152                break;
 153        case 2:
 154                celleb_fake_config_writew(val, p);
 155                break;
 156        case 4:
 157                celleb_fake_config_writel(val, p);
 158                break;
 159        }
 160}
 161
 162static int celleb_fake_pci_read_config(struct pci_bus *bus,
 163                unsigned int devfn, int where, int size, u32 *val)
 164{
 165        char *config;
 166        struct pci_controller *hose = pci_bus_to_host(bus);
 167        unsigned int devno = devfn >> 3;
 168        unsigned int fn = devfn & 0x7;
 169
 170        /* allignment check */
 171        BUG_ON(where % size);
 172
 173        pr_debug("    fake read: bus=0x%x, ", bus->number);
 174        config = get_fake_config_start(hose, devno, fn);
 175
 176        pr_debug("devno=0x%x, where=0x%x, size=0x%x, ", devno, where, size);
 177        if (!config) {
 178                pr_debug("failed\n");
 179                return PCIBIOS_DEVICE_NOT_FOUND;
 180        }
 181
 182        celleb_config_read_fake(config, where, size, val);
 183        pr_debug("val=0x%x\n", *val);
 184
 185        return PCIBIOS_SUCCESSFUL;
 186}
 187
 188
 189static int celleb_fake_pci_write_config(struct pci_bus *bus,
 190                unsigned int devfn, int where, int size, u32 val)
 191{
 192        char *config;
 193        struct pci_controller *hose = pci_bus_to_host(bus);
 194        struct celleb_pci_resource *res;
 195        unsigned int devno = devfn >> 3;
 196        unsigned int fn = devfn & 0x7;
 197
 198        /* allignment check */
 199        BUG_ON(where % size);
 200
 201        config = get_fake_config_start(hose, devno, fn);
 202
 203        if (!config)
 204                return PCIBIOS_DEVICE_NOT_FOUND;
 205
 206        if (val == ~0) {
 207                int i = (where - PCI_BASE_ADDRESS_0) >> 3;
 208
 209                switch (where) {
 210                case PCI_BASE_ADDRESS_0:
 211                case PCI_BASE_ADDRESS_2:
 212                        if (size != 4)
 213                                return PCIBIOS_DEVICE_NOT_FOUND;
 214                        res = get_resource_start(hose, devno, fn);
 215                        if (!res)
 216                                return PCIBIOS_DEVICE_NOT_FOUND;
 217                        celleb_config_write_fake(config, where, size,
 218                                        (res->r[i].end - res->r[i].start));
 219                        return PCIBIOS_SUCCESSFUL;
 220                case PCI_BASE_ADDRESS_1:
 221                case PCI_BASE_ADDRESS_3:
 222                case PCI_BASE_ADDRESS_4:
 223                case PCI_BASE_ADDRESS_5:
 224                        break;
 225                default:
 226                        break;
 227                }
 228        }
 229
 230        celleb_config_write_fake(config, where, size, val);
 231        pr_debug("    fake write: where=%x, size=%d, val=%x\n",
 232                 where, size, val);
 233
 234        return PCIBIOS_SUCCESSFUL;
 235}
 236
 237static struct pci_ops celleb_fake_pci_ops = {
 238        .read = celleb_fake_pci_read_config,
 239        .write = celleb_fake_pci_write_config,
 240};
 241
 242static inline void celleb_setup_pci_base_addrs(struct pci_controller *hose,
 243                                        unsigned int devno, unsigned int fn,
 244                                        unsigned int num_base_addr)
 245{
 246        u32 val;
 247        unsigned char *config;
 248        struct celleb_pci_resource *res;
 249
 250        config = get_fake_config_start(hose, devno, fn);
 251        res = get_resource_start(hose, devno, fn);
 252
 253        if (!config || !res)
 254                return;
 255
 256        switch (num_base_addr) {
 257        case 3:
 258                val = (res->r[2].start & 0xfffffff0)
 259                    | PCI_BASE_ADDRESS_MEM_TYPE_64;
 260                celleb_config_write_fake(config, PCI_BASE_ADDRESS_4, 4, val);
 261                val = res->r[2].start >> 32;
 262                celleb_config_write_fake(config, PCI_BASE_ADDRESS_5, 4, val);
 263                /* FALLTHROUGH */
 264        case 2:
 265                val = (res->r[1].start & 0xfffffff0)
 266                    | PCI_BASE_ADDRESS_MEM_TYPE_64;
 267                celleb_config_write_fake(config, PCI_BASE_ADDRESS_2, 4, val);
 268                val = res->r[1].start >> 32;
 269                celleb_config_write_fake(config, PCI_BASE_ADDRESS_3, 4, val);
 270                /* FALLTHROUGH */
 271        case 1:
 272                val = (res->r[0].start & 0xfffffff0)
 273                    | PCI_BASE_ADDRESS_MEM_TYPE_64;
 274                celleb_config_write_fake(config, PCI_BASE_ADDRESS_0, 4, val);
 275                val = res->r[0].start >> 32;
 276                celleb_config_write_fake(config, PCI_BASE_ADDRESS_1, 4, val);
 277                break;
 278        }
 279
 280        val = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
 281        celleb_config_write_fake(config, PCI_COMMAND, 2, val);
 282}
 283
 284static int __init celleb_setup_fake_pci_device(struct device_node *node,
 285                                               struct pci_controller *hose)
 286{
 287        unsigned int rlen;
 288        int num_base_addr = 0;
 289        u32 val;
 290        const u32 *wi0, *wi1, *wi2, *wi3, *wi4;
 291        unsigned int devno, fn;
 292        struct celleb_pci_private *private = hose->private_data;
 293        unsigned char **config = NULL;
 294        struct celleb_pci_resource **res = NULL;
 295        const char *name;
 296        const unsigned long *li;
 297        int size, result;
 298
 299        if (private == NULL) {
 300                printk(KERN_ERR "PCI: "
 301                       "memory space for pci controller is not assigned\n");
 302                goto error;
 303        }
 304
 305        name = of_get_property(node, "model", &rlen);
 306        if (!name) {
 307                printk(KERN_ERR "PCI: model property not found.\n");
 308                goto error;
 309        }
 310
 311        wi4 = of_get_property(node, "reg", &rlen);
 312        if (wi4 == NULL)
 313                goto error;
 314
 315        devno = ((wi4[0] >> 8) & 0xff) >> 3;
 316        fn = (wi4[0] >> 8) & 0x7;
 317
 318        pr_debug("PCI: celleb_setup_fake_pci() %s devno=%x fn=%x\n", name,
 319                 devno, fn);
 320
 321        size = 256;
 322        config = &private->fake_config[devno][fn];
 323        *config = alloc_maybe_bootmem(size, GFP_KERNEL);
 324        if (*config == NULL) {
 325                printk(KERN_ERR "PCI: "
 326                       "not enough memory for fake configuration space\n");
 327                goto error;
 328        }
 329        pr_debug("PCI: fake config area assigned 0x%016lx\n",
 330                 (unsigned long)*config);
 331
 332        size = sizeof(struct celleb_pci_resource);
 333        res = &private->res[devno][fn];
 334        *res = alloc_maybe_bootmem(size, GFP_KERNEL);
 335        if (*res == NULL) {
 336                printk(KERN_ERR
 337                       "PCI: not enough memory for resource data space\n");
 338                goto error;
 339        }
 340        pr_debug("PCI: res assigned 0x%016lx\n", (unsigned long)*res);
 341
 342        wi0 = of_get_property(node, "device-id", NULL);
 343        wi1 = of_get_property(node, "vendor-id", NULL);
 344        wi2 = of_get_property(node, "class-code", NULL);
 345        wi3 = of_get_property(node, "revision-id", NULL);
 346        if (!wi0 || !wi1 || !wi2 || !wi3) {
 347                printk(KERN_ERR "PCI: Missing device tree properties.\n");
 348                goto error;
 349        }
 350
 351        celleb_config_write_fake(*config, PCI_DEVICE_ID, 2, wi0[0] & 0xffff);
 352        celleb_config_write_fake(*config, PCI_VENDOR_ID, 2, wi1[0] & 0xffff);
 353        pr_debug("class-code = 0x%08x\n", wi2[0]);
 354
 355        celleb_config_write_fake(*config, PCI_CLASS_PROG, 1, wi2[0] & 0xff);
 356        celleb_config_write_fake(*config, PCI_CLASS_DEVICE, 2,
 357                                 (wi2[0] >> 8) & 0xffff);
 358        celleb_config_write_fake(*config, PCI_REVISION_ID, 1, wi3[0]);
 359
 360        while (num_base_addr < MAX_PCI_BASE_ADDRS) {
 361                result = of_address_to_resource(node,
 362                                num_base_addr, &(*res)->r[num_base_addr]);
 363                if (result)
 364                        break;
 365                num_base_addr++;
 366        }
 367
 368        celleb_setup_pci_base_addrs(hose, devno, fn, num_base_addr);
 369
 370        li = of_get_property(node, "interrupts", &rlen);
 371        if (!li) {
 372                printk(KERN_ERR "PCI: interrupts not found.\n");
 373                goto error;
 374        }
 375        val = li[0];
 376        celleb_config_write_fake(*config, PCI_INTERRUPT_PIN, 1, 1);
 377        celleb_config_write_fake(*config, PCI_INTERRUPT_LINE, 1, val);
 378
 379#ifdef DEBUG
 380        pr_debug("PCI: %s irq=%ld\n", name, li[0]);
 381        for (i = 0; i < 6; i++) {
 382                celleb_config_read_fake(*config,
 383                                        PCI_BASE_ADDRESS_0 + 0x4 * i, 4,
 384                                        &val);
 385                pr_debug("PCI: %s fn=%d base_address_%d=0x%x\n",
 386                         name, fn, i, val);
 387        }
 388#endif
 389
 390        celleb_config_write_fake(*config, PCI_HEADER_TYPE, 1,
 391                                 PCI_HEADER_TYPE_NORMAL);
 392
 393        return 0;
 394
 395error:
 396        if (mem_init_done) {
 397                if (config && *config)
 398                        kfree(*config);
 399                if (res && *res)
 400                        kfree(*res);
 401
 402        } else {
 403                if (config && *config) {
 404                        size = 256;
 405                        free_bootmem((unsigned long)(*config), size);
 406                }
 407                if (res && *res) {
 408                        size = sizeof(struct celleb_pci_resource);
 409                        free_bootmem((unsigned long)(*res), size);
 410                }
 411        }
 412
 413        return 1;
 414}
 415
 416static int __init phb_set_bus_ranges(struct device_node *dev,
 417                                     struct pci_controller *phb)
 418{
 419        const int *bus_range;
 420        unsigned int len;
 421
 422        bus_range = of_get_property(dev, "bus-range", &len);
 423        if (bus_range == NULL || len < 2 * sizeof(int))
 424                return 1;
 425
 426        phb->first_busno = bus_range[0];
 427        phb->last_busno = bus_range[1];
 428
 429        return 0;
 430}
 431
 432static void __init celleb_alloc_private_mem(struct pci_controller *hose)
 433{
 434        hose->private_data =
 435                alloc_maybe_bootmem(sizeof(struct celleb_pci_private),
 436                        GFP_KERNEL);
 437}
 438
 439static int __init celleb_setup_fake_pci(struct device_node *dev,
 440                                        struct pci_controller *phb)
 441{
 442        struct device_node *node;
 443
 444        phb->ops = &celleb_fake_pci_ops;
 445        celleb_alloc_private_mem(phb);
 446
 447        for (node = of_get_next_child(dev, NULL);
 448             node != NULL; node = of_get_next_child(dev, node))
 449                celleb_setup_fake_pci_device(node, phb);
 450
 451        return 0;
 452}
 453
 454static struct celleb_phb_spec celleb_fake_pci_spec __initdata = {
 455        .setup = celleb_setup_fake_pci,
 456};
 457
 458static struct of_device_id celleb_phb_match[] __initdata = {
 459        {
 460                .name = "pci-pseudo",
 461                .data = &celleb_fake_pci_spec,
 462        }, {
 463                .name = "epci",
 464                .data = &celleb_epci_spec,
 465        }, {
 466                .name = "pcie",
 467                .data = &celleb_pciex_spec,
 468        }, {
 469        },
 470};
 471
 472static int __init celleb_io_workaround_init(struct pci_controller *phb,
 473                                            struct celleb_phb_spec *phb_spec)
 474{
 475        if (phb_spec->ops) {
 476                iowa_register_bus(phb, phb_spec->ops, phb_spec->iowa_init,
 477                                  phb_spec->iowa_data);
 478                io_workaround_init();
 479        }
 480
 481        return 0;
 482}
 483
 484int __init celleb_setup_phb(struct pci_controller *phb)
 485{
 486        struct device_node *dev = phb->dn;
 487        const struct of_device_id *match;
 488        struct celleb_phb_spec *phb_spec;
 489        int rc;
 490
 491        match = of_match_node(celleb_phb_match, dev);
 492        if (!match)
 493                return 1;
 494
 495        phb_set_bus_ranges(dev, phb);
 496        phb->buid = 1;
 497
 498        phb_spec = match->data;
 499        rc = (*phb_spec->setup)(dev, phb);
 500        if (rc)
 501                return 1;
 502
 503        return celleb_io_workaround_init(phb, phb_spec);
 504}
 505
 506int celleb_pci_probe_mode(struct pci_bus *bus)
 507{
 508        return PCI_PROBE_DEVTREE;
 509}
 510