linux/arch/powerpc/platforms/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_device.h>
  35
  36#include <asm/io.h>
  37#include <asm/irq.h>
  38#include <asm/prom.h>
  39#include <asm/machdep.h>
  40#include <asm/pci-bridge.h>
  41#include <asm/ppc-pci.h>
  42
  43#include "pci.h"
  44#include "interrupt.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        return;
 143}
 144
 145static void celleb_config_write_fake(unsigned char *config, int where,
 146                                     int size, u32 val)
 147{
 148        char *p = config + where;
 149
 150        switch (size) {
 151        case 1:
 152                celleb_fake_config_writeb(val, p);
 153                break;
 154        case 2:
 155                celleb_fake_config_writew(val, p);
 156                break;
 157        case 4:
 158                celleb_fake_config_writel(val, p);
 159                break;
 160        }
 161        return;
 162}
 163
 164static int celleb_fake_pci_read_config(struct pci_bus *bus,
 165                unsigned int devfn, int where, int size, u32 *val)
 166{
 167        char *config;
 168        struct device_node *node;
 169        struct pci_controller *hose;
 170        unsigned int devno = devfn >> 3;
 171        unsigned int fn = devfn & 0x7;
 172
 173        /* allignment check */
 174        BUG_ON(where % size);
 175
 176        pr_debug("    fake read: bus=0x%x, ", bus->number);
 177        node = (struct device_node *)bus->sysdata;
 178        hose = pci_find_hose_for_OF_device(node);
 179        config = get_fake_config_start(hose, devno, fn);
 180
 181        pr_debug("devno=0x%x, where=0x%x, size=0x%x, ", devno, where, size);
 182        if (!config) {
 183                pr_debug("failed\n");
 184                return PCIBIOS_DEVICE_NOT_FOUND;
 185        }
 186
 187        celleb_config_read_fake(config, where, size, val);
 188        pr_debug("val=0x%x\n", *val);
 189
 190        return PCIBIOS_SUCCESSFUL;
 191}
 192
 193
 194static int celleb_fake_pci_write_config(struct pci_bus *bus,
 195                 unsigned int devfn, int where, int size, u32 val)
 196{
 197        char *config;
 198        struct device_node *node;
 199        struct pci_controller *hose;
 200        struct celleb_pci_resource *res;
 201        unsigned int devno = devfn >> 3;
 202        unsigned int fn = devfn & 0x7;
 203
 204        /* allignment check */
 205        BUG_ON(where % size);
 206
 207        node = (struct device_node *)bus->sysdata;
 208        hose = pci_find_hose_for_OF_device(node);
 209        config = get_fake_config_start(hose, devno, fn);
 210
 211        if (!config)
 212                return PCIBIOS_DEVICE_NOT_FOUND;
 213
 214        if (val == ~0) {
 215                int i = (where - PCI_BASE_ADDRESS_0) >> 3;
 216
 217                switch (where) {
 218                case PCI_BASE_ADDRESS_0:
 219                case PCI_BASE_ADDRESS_2:
 220                        if (size != 4)
 221                                return PCIBIOS_DEVICE_NOT_FOUND;
 222                        res = get_resource_start(hose, devno, fn);
 223                        if (!res)
 224                                return PCIBIOS_DEVICE_NOT_FOUND;
 225                        celleb_config_write_fake(config, where, size,
 226                                        (res->r[i].end - res->r[i].start));
 227                        return PCIBIOS_SUCCESSFUL;
 228                case PCI_BASE_ADDRESS_1:
 229                case PCI_BASE_ADDRESS_3:
 230                case PCI_BASE_ADDRESS_4:
 231                case PCI_BASE_ADDRESS_5:
 232                        break;
 233                default:
 234                        break;
 235                }
 236        }
 237
 238        celleb_config_write_fake(config, where, size, val);
 239        pr_debug("    fake write: where=%x, size=%d, val=%x\n",
 240                 where, size, val);
 241
 242        return PCIBIOS_SUCCESSFUL;
 243}
 244
 245static struct pci_ops celleb_fake_pci_ops = {
 246        .read = celleb_fake_pci_read_config,
 247        .write = celleb_fake_pci_write_config,
 248};
 249
 250static inline void celleb_setup_pci_base_addrs(struct pci_controller *hose,
 251                                        unsigned int devno, unsigned int fn,
 252                                        unsigned int num_base_addr)
 253{
 254        u32 val;
 255        unsigned char *config;
 256        struct celleb_pci_resource *res;
 257
 258        config = get_fake_config_start(hose, devno, fn);
 259        res = get_resource_start(hose, devno, fn);
 260
 261        if (!config || !res)
 262                return;
 263
 264        switch (num_base_addr) {
 265        case 3:
 266                val = (res->r[2].start & 0xfffffff0)
 267                    | PCI_BASE_ADDRESS_MEM_TYPE_64;
 268                celleb_config_write_fake(config, PCI_BASE_ADDRESS_4, 4, val);
 269                val = res->r[2].start >> 32;
 270                celleb_config_write_fake(config, PCI_BASE_ADDRESS_5, 4, val);
 271                /* FALLTHROUGH */
 272        case 2:
 273                val = (res->r[1].start & 0xfffffff0)
 274                    | PCI_BASE_ADDRESS_MEM_TYPE_64;
 275                celleb_config_write_fake(config, PCI_BASE_ADDRESS_2, 4, val);
 276                val = res->r[1].start >> 32;
 277                celleb_config_write_fake(config, PCI_BASE_ADDRESS_3, 4, val);
 278                /* FALLTHROUGH */
 279        case 1:
 280                val = (res->r[0].start & 0xfffffff0)
 281                    | PCI_BASE_ADDRESS_MEM_TYPE_64;
 282                celleb_config_write_fake(config, PCI_BASE_ADDRESS_0, 4, val);
 283                val = res->r[0].start >> 32;
 284                celleb_config_write_fake(config, PCI_BASE_ADDRESS_1, 4, val);
 285                break;
 286        }
 287
 288        val = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
 289        celleb_config_write_fake(config, PCI_COMMAND, 2, val);
 290}
 291
 292static int __init celleb_setup_fake_pci_device(struct device_node *node,
 293                                               struct pci_controller *hose)
 294{
 295        unsigned int rlen;
 296        int num_base_addr = 0;
 297        u32 val;
 298        const u32 *wi0, *wi1, *wi2, *wi3, *wi4;
 299        unsigned int devno, fn;
 300        struct celleb_pci_private *private = hose->private_data;
 301        unsigned char **config = NULL;
 302        struct celleb_pci_resource **res = NULL;
 303        const char *name;
 304        const unsigned long *li;
 305        int size, result;
 306
 307        if (private == NULL) {
 308                printk(KERN_ERR "PCI: "
 309                       "memory space for pci controller is not assigned\n");
 310                goto error;
 311        }
 312
 313        name = of_get_property(node, "model", &rlen);
 314        if (!name) {
 315                printk(KERN_ERR "PCI: model property not found.\n");
 316                goto error;
 317        }
 318
 319        wi4 = of_get_property(node, "reg", &rlen);
 320        if (wi4 == NULL)
 321                goto error;
 322
 323        devno = ((wi4[0] >> 8) & 0xff) >> 3;
 324        fn = (wi4[0] >> 8) & 0x7;
 325
 326        pr_debug("PCI: celleb_setup_fake_pci() %s devno=%x fn=%x\n", name,
 327                 devno, fn);
 328
 329        size = 256;
 330        config = &private->fake_config[devno][fn];
 331        *config = alloc_maybe_bootmem(size, GFP_KERNEL);
 332        if (*config == NULL) {
 333                printk(KERN_ERR "PCI: "
 334                       "not enough memory for fake configuration space\n");
 335                goto error;
 336        }
 337        pr_debug("PCI: fake config area assigned 0x%016lx\n",
 338                 (unsigned long)*config);
 339
 340        size = sizeof(struct celleb_pci_resource);
 341        res = &private->res[devno][fn];
 342        *res = alloc_maybe_bootmem(size, GFP_KERNEL);
 343        if (*res == NULL) {
 344                printk(KERN_ERR
 345                       "PCI: not enough memory for resource data space\n");
 346                goto error;
 347        }
 348        pr_debug("PCI: res assigned 0x%016lx\n", (unsigned long)*res);
 349
 350        wi0 = of_get_property(node, "device-id", NULL);
 351        wi1 = of_get_property(node, "vendor-id", NULL);
 352        wi2 = of_get_property(node, "class-code", NULL);
 353        wi3 = of_get_property(node, "revision-id", NULL);
 354
 355        celleb_config_write_fake(*config, PCI_DEVICE_ID, 2, wi0[0] & 0xffff);
 356        celleb_config_write_fake(*config, PCI_VENDOR_ID, 2, wi1[0] & 0xffff);
 357        pr_debug("class-code = 0x%08x\n", wi2[0]);
 358
 359        celleb_config_write_fake(*config, PCI_CLASS_PROG, 1, wi2[0] & 0xff);
 360        celleb_config_write_fake(*config, PCI_CLASS_DEVICE, 2,
 361                                 (wi2[0] >> 8) & 0xffff);
 362        celleb_config_write_fake(*config, PCI_REVISION_ID, 1, wi3[0]);
 363
 364        while (num_base_addr < MAX_PCI_BASE_ADDRS) {
 365                result = of_address_to_resource(node,
 366                                num_base_addr, &(*res)->r[num_base_addr]);
 367                if (result)
 368                        break;
 369                num_base_addr++;
 370        }
 371
 372        celleb_setup_pci_base_addrs(hose, devno, fn, num_base_addr);
 373
 374        li = of_get_property(node, "interrupts", &rlen);
 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
 454void __init fake_pci_workaround_init(struct pci_controller *phb)
 455{
 456        /**
 457         *  We will add fake pci bus to scc_pci_bus for the purpose to improve
 458         *  I/O Macro performance. But device-tree and device drivers
 459         *  are not ready to use address with a token.
 460         */
 461
 462        /* celleb_pci_add_one(phb, NULL); */
 463}
 464
 465static struct of_device_id celleb_phb_match[] __initdata = {
 466        {
 467                .name = "pci-pseudo",
 468                .data = celleb_setup_fake_pci,
 469        }, {
 470                .name = "epci",
 471                .data = celleb_setup_epci,
 472        }, {
 473        },
 474};
 475
 476int __init celleb_setup_phb(struct pci_controller *phb)
 477{
 478        struct device_node *dev = phb->arch_data;
 479        const struct of_device_id *match;
 480        int (*setup_func)(struct device_node *, struct pci_controller *);
 481
 482        match = of_match_node(celleb_phb_match, dev);
 483        if (!match)
 484                return 1;
 485
 486        phb_set_bus_ranges(dev, phb);
 487        phb->buid = 1;
 488
 489        setup_func = match->data;
 490        return (*setup_func)(dev, phb);
 491}
 492
 493int celleb_pci_probe_mode(struct pci_bus *bus)
 494{
 495        return PCI_PROBE_DEVTREE;
 496}
 497
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.