Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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