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