| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * IBM Hot Plug Controller Driver | 
|  | 3 | * | 
|  | 4 | * Written By: Irene Zubarev, IBM Corporation | 
|  | 5 | * | 
|  | 6 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | 
|  | 7 | * Copyright (C) 2001,2002 IBM Corp. | 
|  | 8 | * | 
|  | 9 | * All rights reserved. | 
|  | 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 as published by | 
|  | 13 | * the Free Software Foundation; either version 2 of the License, or (at | 
|  | 14 | * your option) any later version. | 
|  | 15 | * | 
|  | 16 | * This program is distributed in the hope that it will be useful, but | 
|  | 17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 
|  | 19 | * NON INFRINGEMENT.  See the GNU General Public License for more | 
|  | 20 | * details. | 
|  | 21 | * | 
|  | 22 | * You should have received a copy of the GNU General Public License | 
|  | 23 | * along with this program; if not, write to the Free Software | 
|  | 24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | 25 | * | 
|  | 26 | * Send feedback to <gregkh@us.ibm.com> | 
|  | 27 | * | 
|  | 28 | */ | 
|  | 29 |  | 
|  | 30 | #include <linux/module.h> | 
|  | 31 | #include <linux/slab.h> | 
|  | 32 | #include <linux/pci.h> | 
|  | 33 | #include <linux/list.h> | 
|  | 34 | #include <linux/init.h> | 
|  | 35 | #include "ibmphp.h" | 
|  | 36 |  | 
|  | 37 | static int flags = 0;		/* for testing */ | 
|  | 38 |  | 
|  | 39 | static void update_resources (struct bus_node *bus_cur, int type, int rangeno); | 
|  | 40 | static int once_over (void); | 
|  | 41 | static int remove_ranges (struct bus_node *, struct bus_node *); | 
|  | 42 | static int update_bridge_ranges (struct bus_node **); | 
|  | 43 | static int add_range (int type, struct range_node *, struct bus_node *); | 
|  | 44 | static void fix_resources (struct bus_node *); | 
|  | 45 | static struct bus_node *find_bus_wprev (u8, struct bus_node **, u8); | 
|  | 46 |  | 
|  | 47 | static LIST_HEAD(gbuses); | 
|  | 48 |  | 
|  | 49 | static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8 busno, int flag) | 
|  | 50 | { | 
|  | 51 | struct bus_node * newbus; | 
|  | 52 |  | 
|  | 53 | if (!(curr) && !(flag)) { | 
|  | 54 | err ("NULL pointer passed\n"); | 
|  | 55 | return NULL; | 
|  | 56 | } | 
|  | 57 |  | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 58 | newbus = kzalloc(sizeof(struct bus_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 59 | if (!newbus) { | 
|  | 60 | err ("out of system memory\n"); | 
|  | 61 | return NULL; | 
|  | 62 | } | 
|  | 63 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 64 | if (flag) | 
|  | 65 | newbus->busno = busno; | 
|  | 66 | else | 
|  | 67 | newbus->busno = curr->bus_num; | 
|  | 68 | list_add_tail (&newbus->bus_list, &gbuses); | 
|  | 69 | return newbus; | 
|  | 70 | } | 
|  | 71 |  | 
|  | 72 | static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * curr) | 
|  | 73 | { | 
|  | 74 | struct resource_node *rs; | 
|  | 75 |  | 
|  | 76 | if (!curr) { | 
|  | 77 | err ("NULL passed to allocate\n"); | 
|  | 78 | return NULL; | 
|  | 79 | } | 
|  | 80 |  | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 81 | rs = kzalloc(sizeof(struct resource_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 82 | if (!rs) { | 
|  | 83 | err ("out of system memory\n"); | 
|  | 84 | return NULL; | 
|  | 85 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 86 | rs->busno = curr->bus_num; | 
|  | 87 | rs->devfunc = curr->dev_fun; | 
|  | 88 | rs->start = curr->start_addr; | 
|  | 89 | rs->end = curr->end_addr; | 
|  | 90 | rs->len = curr->end_addr - curr->start_addr + 1; | 
|  | 91 | return rs; | 
|  | 92 | } | 
|  | 93 |  | 
|  | 94 | static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus) | 
|  | 95 | { | 
|  | 96 | struct bus_node * newbus; | 
|  | 97 | struct range_node *newrange; | 
|  | 98 | u8 num_ranges = 0; | 
|  | 99 |  | 
|  | 100 | if (first_bus) { | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 101 | newbus = kzalloc(sizeof(struct bus_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 102 | if (!newbus) { | 
|  | 103 | err ("out of system memory.\n"); | 
|  | 104 | return -ENOMEM; | 
|  | 105 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 | newbus->busno = curr->bus_num; | 
|  | 107 | } else { | 
|  | 108 | newbus = *new_bus; | 
|  | 109 | switch (flag) { | 
|  | 110 | case MEM: | 
|  | 111 | num_ranges = newbus->noMemRanges; | 
|  | 112 | break; | 
|  | 113 | case PFMEM: | 
|  | 114 | num_ranges = newbus->noPFMemRanges; | 
|  | 115 | break; | 
|  | 116 | case IO: | 
|  | 117 | num_ranges = newbus->noIORanges; | 
|  | 118 | break; | 
|  | 119 | } | 
|  | 120 | } | 
|  | 121 |  | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 122 | newrange = kzalloc(sizeof(struct range_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 123 | if (!newrange) { | 
|  | 124 | if (first_bus) | 
|  | 125 | kfree (newbus); | 
|  | 126 | err ("out of system memory\n"); | 
|  | 127 | return -ENOMEM; | 
|  | 128 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 129 | newrange->start = curr->start_addr; | 
|  | 130 | newrange->end = curr->end_addr; | 
|  | 131 |  | 
|  | 132 | if (first_bus || (!num_ranges)) | 
|  | 133 | newrange->rangeno = 1; | 
|  | 134 | else { | 
|  | 135 | /* need to insert our range */ | 
|  | 136 | add_range (flag, newrange, newbus); | 
|  | 137 | debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end); | 
|  | 138 | } | 
|  | 139 |  | 
|  | 140 | switch (flag) { | 
|  | 141 | case MEM: | 
|  | 142 | newbus->rangeMem = newrange; | 
|  | 143 | if (first_bus) | 
|  | 144 | newbus->noMemRanges = 1; | 
|  | 145 | else { | 
|  | 146 | debug ("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | 
|  | 147 | ++newbus->noMemRanges; | 
|  | 148 | fix_resources (newbus); | 
|  | 149 | } | 
|  | 150 | break; | 
|  | 151 | case IO: | 
|  | 152 | newbus->rangeIO = newrange; | 
|  | 153 | if (first_bus) | 
|  | 154 | newbus->noIORanges = 1; | 
|  | 155 | else { | 
|  | 156 | debug ("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | 
|  | 157 | ++newbus->noIORanges; | 
|  | 158 | fix_resources (newbus); | 
|  | 159 | } | 
|  | 160 | break; | 
|  | 161 | case PFMEM: | 
|  | 162 | newbus->rangePFMem = newrange; | 
|  | 163 | if (first_bus) | 
|  | 164 | newbus->noPFMemRanges = 1; | 
|  | 165 | else { | 
|  | 166 | debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | 
|  | 167 | ++newbus->noPFMemRanges; | 
|  | 168 | fix_resources (newbus); | 
|  | 169 | } | 
|  | 170 |  | 
|  | 171 | break; | 
|  | 172 | } | 
|  | 173 |  | 
|  | 174 | *new_bus = newbus; | 
|  | 175 | *new_range = newrange; | 
|  | 176 | return 0; | 
|  | 177 | } | 
|  | 178 |  | 
|  | 179 |  | 
|  | 180 | /* Notes: | 
|  | 181 | * 1. The ranges are ordered.  The buses are not ordered.  (First come) | 
|  | 182 | * | 
|  | 183 | * 2. If cannot allocate out of PFMem range, allocate from Mem ranges.  PFmemFromMem | 
|  | 184 | * are not sorted. (no need since use mem node). To not change the entire code, we | 
|  | 185 | * also add mem node whenever this case happens so as not to change | 
|  | 186 | * ibmphp_check_mem_resource etc (and since it really is taking Mem resource) | 
|  | 187 | */ | 
|  | 188 |  | 
|  | 189 | /***************************************************************************** | 
|  | 190 | * This is the Resource Management initialization function.  It will go through | 
|  | 191 | * the Resource list taken from EBDA and fill in this module's data structures | 
|  | 192 | * | 
|  | 193 | * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES, | 
|  | 194 | * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW | 
|  | 195 | * | 
|  | 196 | * Input: ptr to the head of the resource list from EBDA | 
|  | 197 | * Output: 0, -1 or error codes | 
|  | 198 | ***************************************************************************/ | 
|  | 199 | int __init ibmphp_rsrc_init (void) | 
|  | 200 | { | 
|  | 201 | struct ebda_pci_rsrc *curr; | 
|  | 202 | struct range_node *newrange = NULL; | 
|  | 203 | struct bus_node *newbus = NULL; | 
|  | 204 | struct bus_node *bus_cur; | 
|  | 205 | struct bus_node *bus_prev; | 
|  | 206 | struct list_head *tmp; | 
|  | 207 | struct resource_node *new_io = NULL; | 
|  | 208 | struct resource_node *new_mem = NULL; | 
|  | 209 | struct resource_node *new_pfmem = NULL; | 
|  | 210 | int rc; | 
|  | 211 | struct list_head *tmp_ebda; | 
|  | 212 |  | 
|  | 213 | list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) { | 
|  | 214 | curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list); | 
|  | 215 | if (!(curr->rsrc_type & PCIDEVMASK)) { | 
|  | 216 | /* EBDA still lists non PCI devices, so ignore... */ | 
|  | 217 | debug ("this is not a PCI DEVICE in rsrc_init, please take care\n"); | 
|  | 218 | // continue; | 
|  | 219 | } | 
|  | 220 |  | 
|  | 221 | /* this is a primary bus resource */ | 
|  | 222 | if (curr->rsrc_type & PRIMARYBUSMASK) { | 
|  | 223 | /* memory */ | 
|  | 224 | if ((curr->rsrc_type & RESTYPE) == MMASK) { | 
|  | 225 | /* no bus structure exists in place yet */ | 
|  | 226 | if (list_empty (&gbuses)) { | 
|  | 227 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) | 
|  | 228 | return rc; | 
|  | 229 | list_add_tail (&newbus->bus_list, &gbuses); | 
|  | 230 | debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | 
|  | 231 | } else { | 
|  | 232 | bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); | 
|  | 233 | /* found our bus */ | 
|  | 234 | if (bus_cur) { | 
|  | 235 | rc = alloc_bus_range (&bus_cur, &newrange, curr, MEM, 0); | 
|  | 236 | if (rc) | 
|  | 237 | return rc; | 
|  | 238 | } else { | 
|  | 239 | /* went through all the buses and didn't find ours, need to create a new bus node */ | 
|  | 240 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) | 
|  | 241 | return rc; | 
|  | 242 |  | 
|  | 243 | list_add_tail (&newbus->bus_list, &gbuses); | 
|  | 244 | debug ("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | 
|  | 245 | } | 
|  | 246 | } | 
|  | 247 | } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { | 
|  | 248 | /* prefetchable memory */ | 
|  | 249 | if (list_empty (&gbuses)) { | 
|  | 250 | /* no bus structure exists in place yet */ | 
|  | 251 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) | 
|  | 252 | return rc; | 
|  | 253 | list_add_tail (&newbus->bus_list, &gbuses); | 
|  | 254 | debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | 
|  | 255 | } else { | 
|  | 256 | bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); | 
|  | 257 | if (bus_cur) { | 
|  | 258 | /* found our bus */ | 
|  | 259 | rc = alloc_bus_range (&bus_cur, &newrange, curr, PFMEM, 0); | 
|  | 260 | if (rc) | 
|  | 261 | return rc; | 
|  | 262 | } else { | 
|  | 263 | /* went through all the buses and didn't find ours, need to create a new bus node */ | 
|  | 264 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) | 
|  | 265 | return rc; | 
|  | 266 | list_add_tail (&newbus->bus_list, &gbuses); | 
|  | 267 | debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | 
|  | 268 | } | 
|  | 269 | } | 
|  | 270 | } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { | 
|  | 271 | /* IO */ | 
|  | 272 | if (list_empty (&gbuses)) { | 
|  | 273 | /* no bus structure exists in place yet */ | 
|  | 274 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) | 
|  | 275 | return rc; | 
|  | 276 | list_add_tail (&newbus->bus_list, &gbuses); | 
|  | 277 | debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | 
|  | 278 | } else { | 
|  | 279 | bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); | 
|  | 280 | if (bus_cur) { | 
|  | 281 | rc = alloc_bus_range (&bus_cur, &newrange, curr, IO, 0); | 
|  | 282 | if (rc) | 
|  | 283 | return rc; | 
|  | 284 | } else { | 
|  | 285 | /* went through all the buses and didn't find ours, need to create a new bus node */ | 
|  | 286 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) | 
|  | 287 | return rc; | 
|  | 288 | list_add_tail (&newbus->bus_list, &gbuses); | 
|  | 289 | debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | 
|  | 290 | } | 
|  | 291 | } | 
|  | 292 |  | 
|  | 293 | } else { | 
|  | 294 | ;	/* type is reserved  WHAT TO DO IN THIS CASE??? | 
|  | 295 | NOTHING TO DO??? */ | 
|  | 296 | } | 
|  | 297 | } else { | 
|  | 298 | /* regular pci device resource */ | 
|  | 299 | if ((curr->rsrc_type & RESTYPE) == MMASK) { | 
|  | 300 | /* Memory resource */ | 
|  | 301 | new_mem = alloc_resources (curr); | 
|  | 302 | if (!new_mem) | 
|  | 303 | return -ENOMEM; | 
|  | 304 | new_mem->type = MEM; | 
|  | 305 | /* | 
|  | 306 | * if it didn't find the bus, means PCI dev | 
|  | 307 | * came b4 the Primary Bus info, so need to | 
|  | 308 | * create a bus rangeno becomes a problem... | 
|  | 309 | * assign a -1 and then update once the range | 
|  | 310 | * actually appears... | 
|  | 311 | */ | 
|  | 312 | if (ibmphp_add_resource (new_mem) < 0) { | 
|  | 313 | newbus = alloc_error_bus (curr, 0, 0); | 
|  | 314 | if (!newbus) | 
|  | 315 | return -ENOMEM; | 
|  | 316 | newbus->firstMem = new_mem; | 
|  | 317 | ++newbus->needMemUpdate; | 
|  | 318 | new_mem->rangeno = -1; | 
|  | 319 | } | 
|  | 320 | debug ("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end); | 
|  | 321 |  | 
|  | 322 | } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { | 
|  | 323 | /* PFMemory resource */ | 
|  | 324 | new_pfmem = alloc_resources (curr); | 
|  | 325 | if (!new_pfmem) | 
|  | 326 | return -ENOMEM; | 
|  | 327 | new_pfmem->type = PFMEM; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 328 | new_pfmem->fromMem = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 329 | if (ibmphp_add_resource (new_pfmem) < 0) { | 
|  | 330 | newbus = alloc_error_bus (curr, 0, 0); | 
|  | 331 | if (!newbus) | 
|  | 332 | return -ENOMEM; | 
|  | 333 | newbus->firstPFMem = new_pfmem; | 
|  | 334 | ++newbus->needPFMemUpdate; | 
|  | 335 | new_pfmem->rangeno = -1; | 
|  | 336 | } | 
|  | 337 |  | 
|  | 338 | debug ("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end); | 
|  | 339 | } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { | 
|  | 340 | /* IO resource */ | 
|  | 341 | new_io = alloc_resources (curr); | 
|  | 342 | if (!new_io) | 
|  | 343 | return -ENOMEM; | 
|  | 344 | new_io->type = IO; | 
|  | 345 |  | 
|  | 346 | /* | 
|  | 347 | * if it didn't find the bus, means PCI dev | 
|  | 348 | * came b4 the Primary Bus info, so need to | 
|  | 349 | * create a bus rangeno becomes a problem... | 
|  | 350 | * Can assign a -1 and then update once the | 
|  | 351 | * range actually appears... | 
|  | 352 | */ | 
|  | 353 | if (ibmphp_add_resource (new_io) < 0) { | 
|  | 354 | newbus = alloc_error_bus (curr, 0, 0); | 
|  | 355 | if (!newbus) | 
|  | 356 | return -ENOMEM; | 
|  | 357 | newbus->firstIO = new_io; | 
|  | 358 | ++newbus->needIOUpdate; | 
|  | 359 | new_io->rangeno = -1; | 
|  | 360 | } | 
|  | 361 | debug ("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end); | 
|  | 362 | } | 
|  | 363 | } | 
|  | 364 | } | 
|  | 365 |  | 
|  | 366 | list_for_each (tmp, &gbuses) { | 
|  | 367 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | 
|  | 368 | /* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */ | 
|  | 369 | rc = update_bridge_ranges (&bus_cur); | 
|  | 370 | if (rc) | 
|  | 371 | return rc; | 
|  | 372 | } | 
|  | 373 | rc = once_over ();  /* This is to align ranges (so no -1) */ | 
|  | 374 | if (rc) | 
|  | 375 | return rc; | 
|  | 376 | return 0; | 
|  | 377 | } | 
|  | 378 |  | 
|  | 379 | /******************************************************************************** | 
|  | 380 | * This function adds a range into a sorted list of ranges per bus for a particular | 
|  | 381 | * range type, it then calls another routine to update the range numbers on the | 
|  | 382 | * pci devices' resources for the appropriate resource | 
|  | 383 | * | 
|  | 384 | * Input: type of the resource, range to add, current bus | 
|  | 385 | * Output: 0 or -1, bus and range ptrs | 
|  | 386 | ********************************************************************************/ | 
|  | 387 | static int add_range (int type, struct range_node *range, struct bus_node *bus_cur) | 
|  | 388 | { | 
|  | 389 | struct range_node *range_cur = NULL; | 
|  | 390 | struct range_node *range_prev; | 
|  | 391 | int count = 0, i_init; | 
|  | 392 | int noRanges = 0; | 
|  | 393 |  | 
|  | 394 | switch (type) { | 
|  | 395 | case MEM: | 
|  | 396 | range_cur = bus_cur->rangeMem; | 
|  | 397 | noRanges = bus_cur->noMemRanges; | 
|  | 398 | break; | 
|  | 399 | case PFMEM: | 
|  | 400 | range_cur = bus_cur->rangePFMem; | 
|  | 401 | noRanges = bus_cur->noPFMemRanges; | 
|  | 402 | break; | 
|  | 403 | case IO: | 
|  | 404 | range_cur = bus_cur->rangeIO; | 
|  | 405 | noRanges = bus_cur->noIORanges; | 
|  | 406 | break; | 
|  | 407 | } | 
|  | 408 |  | 
|  | 409 | range_prev = NULL; | 
|  | 410 | while (range_cur) { | 
|  | 411 | if (range->start < range_cur->start) | 
|  | 412 | break; | 
|  | 413 | range_prev = range_cur; | 
|  | 414 | range_cur = range_cur->next; | 
|  | 415 | count = count + 1; | 
|  | 416 | } | 
|  | 417 | if (!count) { | 
|  | 418 | /* our range will go at the beginning of the list */ | 
|  | 419 | switch (type) { | 
|  | 420 | case MEM: | 
|  | 421 | bus_cur->rangeMem = range; | 
|  | 422 | break; | 
|  | 423 | case PFMEM: | 
|  | 424 | bus_cur->rangePFMem = range; | 
|  | 425 | break; | 
|  | 426 | case IO: | 
|  | 427 | bus_cur->rangeIO = range; | 
|  | 428 | break; | 
|  | 429 | } | 
|  | 430 | range->next = range_cur; | 
|  | 431 | range->rangeno = 1; | 
|  | 432 | i_init = 0; | 
|  | 433 | } else if (!range_cur) { | 
|  | 434 | /* our range will go at the end of the list */ | 
|  | 435 | range->next = NULL; | 
|  | 436 | range_prev->next = range; | 
|  | 437 | range->rangeno = range_prev->rangeno + 1; | 
|  | 438 | return 0; | 
|  | 439 | } else { | 
|  | 440 | /* the range is in the middle */ | 
|  | 441 | range_prev->next = range; | 
|  | 442 | range->next = range_cur; | 
|  | 443 | range->rangeno = range_cur->rangeno; | 
|  | 444 | i_init = range_prev->rangeno; | 
|  | 445 | } | 
|  | 446 |  | 
|  | 447 | for (count = i_init; count < noRanges; ++count) { | 
|  | 448 | ++range_cur->rangeno; | 
|  | 449 | range_cur = range_cur->next; | 
|  | 450 | } | 
|  | 451 |  | 
|  | 452 | update_resources (bus_cur, type, i_init + 1); | 
|  | 453 | return 0; | 
|  | 454 | } | 
|  | 455 |  | 
|  | 456 | /******************************************************************************* | 
|  | 457 | * This routine goes through the list of resources of type 'type' and updates | 
|  | 458 | * the range numbers that they correspond to.  It was called from add_range fnc | 
|  | 459 | * | 
|  | 460 | * Input: bus, type of the resource, the rangeno starting from which to update | 
|  | 461 | ******************************************************************************/ | 
|  | 462 | static void update_resources (struct bus_node *bus_cur, int type, int rangeno) | 
|  | 463 | { | 
|  | 464 | struct resource_node *res = NULL; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 465 | u8 eol = 0;	/* end of list indicator */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 466 |  | 
|  | 467 | switch (type) { | 
|  | 468 | case MEM: | 
|  | 469 | if (bus_cur->firstMem) | 
|  | 470 | res = bus_cur->firstMem; | 
|  | 471 | break; | 
|  | 472 | case PFMEM: | 
|  | 473 | if (bus_cur->firstPFMem) | 
|  | 474 | res = bus_cur->firstPFMem; | 
|  | 475 | break; | 
|  | 476 | case IO: | 
|  | 477 | if (bus_cur->firstIO) | 
|  | 478 | res = bus_cur->firstIO; | 
|  | 479 | break; | 
|  | 480 | } | 
|  | 481 |  | 
|  | 482 | if (res) { | 
|  | 483 | while (res) { | 
|  | 484 | if (res->rangeno == rangeno) | 
|  | 485 | break; | 
|  | 486 | if (res->next) | 
|  | 487 | res = res->next; | 
|  | 488 | else if (res->nextRange) | 
|  | 489 | res = res->nextRange; | 
|  | 490 | else { | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 491 | eol = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 492 | break; | 
|  | 493 | } | 
|  | 494 | } | 
|  | 495 |  | 
|  | 496 | if (!eol) { | 
|  | 497 | /* found the range */ | 
|  | 498 | while (res) { | 
|  | 499 | ++res->rangeno; | 
|  | 500 | res = res->next; | 
|  | 501 | } | 
|  | 502 | } | 
|  | 503 | } | 
|  | 504 | } | 
|  | 505 |  | 
|  | 506 | static void fix_me (struct resource_node *res, struct bus_node *bus_cur, struct range_node *range) | 
|  | 507 | { | 
|  | 508 | char * str = ""; | 
|  | 509 | switch (res->type) { | 
|  | 510 | case IO: | 
|  | 511 | str = "io"; | 
|  | 512 | break; | 
|  | 513 | case MEM: | 
|  | 514 | str = "mem"; | 
|  | 515 | break; | 
|  | 516 | case PFMEM: | 
|  | 517 | str = "pfmem"; | 
|  | 518 | break; | 
|  | 519 | } | 
|  | 520 |  | 
|  | 521 | while (res) { | 
|  | 522 | if (res->rangeno == -1) { | 
|  | 523 | while (range) { | 
|  | 524 | if ((res->start >= range->start) && (res->end <= range->end)) { | 
|  | 525 | res->rangeno = range->rangeno; | 
|  | 526 | debug ("%s->rangeno in fix_resources is %d\n", str, res->rangeno); | 
|  | 527 | switch (res->type) { | 
|  | 528 | case IO: | 
|  | 529 | --bus_cur->needIOUpdate; | 
|  | 530 | break; | 
|  | 531 | case MEM: | 
|  | 532 | --bus_cur->needMemUpdate; | 
|  | 533 | break; | 
|  | 534 | case PFMEM: | 
|  | 535 | --bus_cur->needPFMemUpdate; | 
|  | 536 | break; | 
|  | 537 | } | 
|  | 538 | break; | 
|  | 539 | } | 
|  | 540 | range = range->next; | 
|  | 541 | } | 
|  | 542 | } | 
|  | 543 | if (res->next) | 
|  | 544 | res = res->next; | 
|  | 545 | else | 
|  | 546 | res = res->nextRange; | 
|  | 547 | } | 
|  | 548 |  | 
|  | 549 | } | 
|  | 550 |  | 
|  | 551 | /***************************************************************************** | 
|  | 552 | * This routine reassigns the range numbers to the resources that had a -1 | 
|  | 553 | * This case can happen only if upon initialization, resources taken by pci dev | 
|  | 554 | * appear in EBDA before the resources allocated for that bus, since we don't | 
|  | 555 | * know the range, we assign -1, and this routine is called after a new range | 
|  | 556 | * is assigned to see the resources with unknown range belong to the added range | 
|  | 557 | * | 
|  | 558 | * Input: current bus | 
|  | 559 | * Output: none, list of resources for that bus are fixed if can be | 
|  | 560 | *******************************************************************************/ | 
|  | 561 | static void fix_resources (struct bus_node *bus_cur) | 
|  | 562 | { | 
|  | 563 | struct range_node *range; | 
|  | 564 | struct resource_node *res; | 
|  | 565 |  | 
| Harvey Harrison | 66bef8c | 2008-03-03 19:09:46 -0800 | [diff] [blame] | 566 | debug ("%s - bus_cur->busno = %d\n", __func__, bus_cur->busno); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 567 |  | 
|  | 568 | if (bus_cur->needIOUpdate) { | 
|  | 569 | res = bus_cur->firstIO; | 
|  | 570 | range = bus_cur->rangeIO; | 
|  | 571 | fix_me (res, bus_cur, range); | 
|  | 572 | } | 
|  | 573 | if (bus_cur->needMemUpdate) { | 
|  | 574 | res = bus_cur->firstMem; | 
|  | 575 | range = bus_cur->rangeMem; | 
|  | 576 | fix_me (res, bus_cur, range); | 
|  | 577 | } | 
|  | 578 | if (bus_cur->needPFMemUpdate) { | 
|  | 579 | res = bus_cur->firstPFMem; | 
|  | 580 | range = bus_cur->rangePFMem; | 
|  | 581 | fix_me (res, bus_cur, range); | 
|  | 582 | } | 
|  | 583 | } | 
|  | 584 |  | 
|  | 585 | /******************************************************************************* | 
|  | 586 | * This routine adds a resource to the list of resources to the appropriate bus | 
|  | 587 | * based on their resource type and sorted by their starting addresses.  It assigns | 
|  | 588 | * the ptrs to next and nextRange if needed. | 
|  | 589 | * | 
|  | 590 | * Input: resource ptr | 
|  | 591 | * Output: ptrs assigned (to the node) | 
|  | 592 | * 0 or -1 | 
|  | 593 | *******************************************************************************/ | 
|  | 594 | int ibmphp_add_resource (struct resource_node *res) | 
|  | 595 | { | 
|  | 596 | struct resource_node *res_cur; | 
|  | 597 | struct resource_node *res_prev; | 
|  | 598 | struct bus_node *bus_cur; | 
|  | 599 | struct range_node *range_cur = NULL; | 
|  | 600 | struct resource_node *res_start = NULL; | 
|  | 601 |  | 
| Harvey Harrison | 66bef8c | 2008-03-03 19:09:46 -0800 | [diff] [blame] | 602 | debug ("%s - enter\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 603 |  | 
|  | 604 | if (!res) { | 
|  | 605 | err ("NULL passed to add\n"); | 
|  | 606 | return -ENODEV; | 
|  | 607 | } | 
|  | 608 |  | 
|  | 609 | bus_cur = find_bus_wprev (res->busno, NULL, 0); | 
|  | 610 |  | 
|  | 611 | if (!bus_cur) { | 
|  | 612 | /* didn't find a bus, smth's wrong!!! */ | 
|  | 613 | debug ("no bus in the system, either pci_dev's wrong or allocation failed\n"); | 
|  | 614 | return -ENODEV; | 
|  | 615 | } | 
|  | 616 |  | 
|  | 617 | /* Normal case */ | 
|  | 618 | switch (res->type) { | 
|  | 619 | case IO: | 
|  | 620 | range_cur = bus_cur->rangeIO; | 
|  | 621 | res_start = bus_cur->firstIO; | 
|  | 622 | break; | 
|  | 623 | case MEM: | 
|  | 624 | range_cur = bus_cur->rangeMem; | 
|  | 625 | res_start = bus_cur->firstMem; | 
|  | 626 | break; | 
|  | 627 | case PFMEM: | 
|  | 628 | range_cur = bus_cur->rangePFMem; | 
|  | 629 | res_start = bus_cur->firstPFMem; | 
|  | 630 | break; | 
|  | 631 | default: | 
|  | 632 | err ("cannot read the type of the resource to add... problem\n"); | 
|  | 633 | return -EINVAL; | 
|  | 634 | } | 
|  | 635 | while (range_cur) { | 
|  | 636 | if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) { | 
|  | 637 | res->rangeno = range_cur->rangeno; | 
|  | 638 | break; | 
|  | 639 | } | 
|  | 640 | range_cur = range_cur->next; | 
|  | 641 | } | 
|  | 642 |  | 
|  | 643 | /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | 
|  | 644 | * this is again the case of rangeno = -1 | 
|  | 645 | * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | 
|  | 646 | */ | 
|  | 647 |  | 
|  | 648 | if (!range_cur) { | 
|  | 649 | switch (res->type) { | 
|  | 650 | case IO: | 
|  | 651 | ++bus_cur->needIOUpdate; | 
|  | 652 | break; | 
|  | 653 | case MEM: | 
|  | 654 | ++bus_cur->needMemUpdate; | 
|  | 655 | break; | 
|  | 656 | case PFMEM: | 
|  | 657 | ++bus_cur->needPFMemUpdate; | 
|  | 658 | break; | 
|  | 659 | } | 
|  | 660 | res->rangeno = -1; | 
|  | 661 | } | 
|  | 662 |  | 
|  | 663 | debug ("The range is %d\n", res->rangeno); | 
|  | 664 | if (!res_start) { | 
|  | 665 | /* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */ | 
|  | 666 | switch (res->type) { | 
|  | 667 | case IO: | 
|  | 668 | bus_cur->firstIO = res; | 
|  | 669 | break; | 
|  | 670 | case MEM: | 
|  | 671 | bus_cur->firstMem = res; | 
|  | 672 | break; | 
|  | 673 | case PFMEM: | 
|  | 674 | bus_cur->firstPFMem = res; | 
|  | 675 | break; | 
|  | 676 | } | 
|  | 677 | res->next = NULL; | 
|  | 678 | res->nextRange = NULL; | 
|  | 679 | } else { | 
|  | 680 | res_cur = res_start; | 
|  | 681 | res_prev = NULL; | 
|  | 682 |  | 
|  | 683 | debug ("res_cur->rangeno is %d\n", res_cur->rangeno); | 
|  | 684 |  | 
|  | 685 | while (res_cur) { | 
|  | 686 | if (res_cur->rangeno >= res->rangeno) | 
|  | 687 | break; | 
|  | 688 | res_prev = res_cur; | 
|  | 689 | if (res_cur->next) | 
|  | 690 | res_cur = res_cur->next; | 
|  | 691 | else | 
|  | 692 | res_cur = res_cur->nextRange; | 
|  | 693 | } | 
|  | 694 |  | 
|  | 695 | if (!res_cur) { | 
|  | 696 | /* at the end of the resource list */ | 
|  | 697 | debug ("i should be here, [%x - %x]\n", res->start, res->end); | 
|  | 698 | res_prev->nextRange = res; | 
|  | 699 | res->next = NULL; | 
|  | 700 | res->nextRange = NULL; | 
|  | 701 | } else if (res_cur->rangeno == res->rangeno) { | 
|  | 702 | /* in the same range */ | 
|  | 703 | while (res_cur) { | 
|  | 704 | if (res->start < res_cur->start) | 
|  | 705 | break; | 
|  | 706 | res_prev = res_cur; | 
|  | 707 | res_cur = res_cur->next; | 
|  | 708 | } | 
|  | 709 | if (!res_cur) { | 
|  | 710 | /* the last resource in this range */ | 
|  | 711 | res_prev->next = res; | 
|  | 712 | res->next = NULL; | 
|  | 713 | res->nextRange = res_prev->nextRange; | 
|  | 714 | res_prev->nextRange = NULL; | 
|  | 715 | } else if (res->start < res_cur->start) { | 
|  | 716 | /* at the beginning or middle of the range */ | 
|  | 717 | if (!res_prev)	{ | 
|  | 718 | switch (res->type) { | 
|  | 719 | case IO: | 
|  | 720 | bus_cur->firstIO = res; | 
|  | 721 | break; | 
|  | 722 | case MEM: | 
|  | 723 | bus_cur->firstMem = res; | 
|  | 724 | break; | 
|  | 725 | case PFMEM: | 
|  | 726 | bus_cur->firstPFMem = res; | 
|  | 727 | break; | 
|  | 728 | } | 
|  | 729 | } else if (res_prev->rangeno == res_cur->rangeno) | 
|  | 730 | res_prev->next = res; | 
|  | 731 | else | 
|  | 732 | res_prev->nextRange = res; | 
|  | 733 |  | 
|  | 734 | res->next = res_cur; | 
|  | 735 | res->nextRange = NULL; | 
|  | 736 | } | 
|  | 737 | } else { | 
|  | 738 | /* this is the case where it is 1st occurrence of the range */ | 
|  | 739 | if (!res_prev) { | 
|  | 740 | /* at the beginning of the resource list */ | 
|  | 741 | res->next = NULL; | 
|  | 742 | switch (res->type) { | 
|  | 743 | case IO: | 
|  | 744 | res->nextRange = bus_cur->firstIO; | 
|  | 745 | bus_cur->firstIO = res; | 
|  | 746 | break; | 
|  | 747 | case MEM: | 
|  | 748 | res->nextRange = bus_cur->firstMem; | 
|  | 749 | bus_cur->firstMem = res; | 
|  | 750 | break; | 
|  | 751 | case PFMEM: | 
|  | 752 | res->nextRange = bus_cur->firstPFMem; | 
|  | 753 | bus_cur->firstPFMem = res; | 
|  | 754 | break; | 
|  | 755 | } | 
|  | 756 | } else if (res_cur->rangeno > res->rangeno) { | 
|  | 757 | /* in the middle of the resource list */ | 
|  | 758 | res_prev->nextRange = res; | 
|  | 759 | res->next = NULL; | 
|  | 760 | res->nextRange = res_cur; | 
|  | 761 | } | 
|  | 762 | } | 
|  | 763 | } | 
|  | 764 |  | 
| Harvey Harrison | 66bef8c | 2008-03-03 19:09:46 -0800 | [diff] [blame] | 765 | debug ("%s - exit\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 766 | return 0; | 
|  | 767 | } | 
|  | 768 |  | 
|  | 769 | /**************************************************************************** | 
|  | 770 | * This routine will remove the resource from the list of resources | 
|  | 771 | * | 
|  | 772 | * Input: io, mem, and/or pfmem resource to be deleted | 
|  | 773 | * Ouput: modified resource list | 
|  | 774 | *        0 or error code | 
|  | 775 | ****************************************************************************/ | 
|  | 776 | int ibmphp_remove_resource (struct resource_node *res) | 
|  | 777 | { | 
|  | 778 | struct bus_node *bus_cur; | 
|  | 779 | struct resource_node *res_cur = NULL; | 
|  | 780 | struct resource_node *res_prev; | 
|  | 781 | struct resource_node *mem_cur; | 
|  | 782 | char * type = ""; | 
|  | 783 |  | 
|  | 784 | if (!res)  { | 
|  | 785 | err ("resource to remove is NULL\n"); | 
|  | 786 | return -ENODEV; | 
|  | 787 | } | 
|  | 788 |  | 
|  | 789 | bus_cur = find_bus_wprev (res->busno, NULL, 0); | 
|  | 790 |  | 
|  | 791 | if (!bus_cur) { | 
|  | 792 | err ("cannot find corresponding bus of the io resource to remove  " | 
|  | 793 | "bailing out...\n"); | 
|  | 794 | return -ENODEV; | 
|  | 795 | } | 
|  | 796 |  | 
|  | 797 | switch (res->type) { | 
|  | 798 | case IO: | 
|  | 799 | res_cur = bus_cur->firstIO; | 
|  | 800 | type = "io"; | 
|  | 801 | break; | 
|  | 802 | case MEM: | 
|  | 803 | res_cur = bus_cur->firstMem; | 
|  | 804 | type = "mem"; | 
|  | 805 | break; | 
|  | 806 | case PFMEM: | 
|  | 807 | res_cur = bus_cur->firstPFMem; | 
|  | 808 | type = "pfmem"; | 
|  | 809 | break; | 
|  | 810 | default: | 
|  | 811 | err ("unknown type for resource to remove\n"); | 
|  | 812 | return -EINVAL; | 
|  | 813 | } | 
|  | 814 | res_prev = NULL; | 
|  | 815 |  | 
|  | 816 | while (res_cur) { | 
|  | 817 | if ((res_cur->start == res->start) && (res_cur->end == res->end)) | 
|  | 818 | break; | 
|  | 819 | res_prev = res_cur; | 
|  | 820 | if (res_cur->next) | 
|  | 821 | res_cur = res_cur->next; | 
|  | 822 | else | 
|  | 823 | res_cur = res_cur->nextRange; | 
|  | 824 | } | 
|  | 825 |  | 
|  | 826 | if (!res_cur) { | 
|  | 827 | if (res->type == PFMEM) { | 
|  | 828 | /* | 
|  | 829 | * case where pfmem might be in the PFMemFromMem list | 
|  | 830 | * so will also need to remove the corresponding mem | 
|  | 831 | * entry | 
|  | 832 | */ | 
|  | 833 | res_cur = bus_cur->firstPFMemFromMem; | 
|  | 834 | res_prev = NULL; | 
|  | 835 |  | 
|  | 836 | while (res_cur) { | 
|  | 837 | if ((res_cur->start == res->start) && (res_cur->end == res->end)) { | 
|  | 838 | mem_cur = bus_cur->firstMem; | 
|  | 839 | while (mem_cur) { | 
|  | 840 | if ((mem_cur->start == res_cur->start) | 
|  | 841 | && (mem_cur->end == res_cur->end)) | 
|  | 842 | break; | 
|  | 843 | if (mem_cur->next) | 
|  | 844 | mem_cur = mem_cur->next; | 
|  | 845 | else | 
|  | 846 | mem_cur = mem_cur->nextRange; | 
|  | 847 | } | 
|  | 848 | if (!mem_cur) { | 
|  | 849 | err ("cannot find corresponding mem node for pfmem...\n"); | 
|  | 850 | return -EINVAL; | 
|  | 851 | } | 
|  | 852 |  | 
|  | 853 | ibmphp_remove_resource (mem_cur); | 
|  | 854 | if (!res_prev) | 
|  | 855 | bus_cur->firstPFMemFromMem = res_cur->next; | 
|  | 856 | else | 
|  | 857 | res_prev->next = res_cur->next; | 
|  | 858 | kfree (res_cur); | 
|  | 859 | return 0; | 
|  | 860 | } | 
|  | 861 | res_prev = res_cur; | 
|  | 862 | if (res_cur->next) | 
|  | 863 | res_cur = res_cur->next; | 
|  | 864 | else | 
|  | 865 | res_cur = res_cur->nextRange; | 
|  | 866 | } | 
|  | 867 | if (!res_cur) { | 
|  | 868 | err ("cannot find pfmem to delete...\n"); | 
|  | 869 | return -EINVAL; | 
|  | 870 | } | 
|  | 871 | } else { | 
|  | 872 | err ("the %s resource is not in the list to be deleted...\n", type); | 
|  | 873 | return -EINVAL; | 
|  | 874 | } | 
|  | 875 | } | 
|  | 876 | if (!res_prev) { | 
|  | 877 | /* first device to be deleted */ | 
|  | 878 | if (res_cur->next) { | 
|  | 879 | switch (res->type) { | 
|  | 880 | case IO: | 
|  | 881 | bus_cur->firstIO = res_cur->next; | 
|  | 882 | break; | 
|  | 883 | case MEM: | 
|  | 884 | bus_cur->firstMem = res_cur->next; | 
|  | 885 | break; | 
|  | 886 | case PFMEM: | 
|  | 887 | bus_cur->firstPFMem = res_cur->next; | 
|  | 888 | break; | 
|  | 889 | } | 
|  | 890 | } else if (res_cur->nextRange) { | 
|  | 891 | switch (res->type) { | 
|  | 892 | case IO: | 
|  | 893 | bus_cur->firstIO = res_cur->nextRange; | 
|  | 894 | break; | 
|  | 895 | case MEM: | 
|  | 896 | bus_cur->firstMem = res_cur->nextRange; | 
|  | 897 | break; | 
|  | 898 | case PFMEM: | 
|  | 899 | bus_cur->firstPFMem = res_cur->nextRange; | 
|  | 900 | break; | 
|  | 901 | } | 
|  | 902 | } else { | 
|  | 903 | switch (res->type) { | 
|  | 904 | case IO: | 
|  | 905 | bus_cur->firstIO = NULL; | 
|  | 906 | break; | 
|  | 907 | case MEM: | 
|  | 908 | bus_cur->firstMem = NULL; | 
|  | 909 | break; | 
|  | 910 | case PFMEM: | 
|  | 911 | bus_cur->firstPFMem = NULL; | 
|  | 912 | break; | 
|  | 913 | } | 
|  | 914 | } | 
|  | 915 | kfree (res_cur); | 
|  | 916 | return 0; | 
|  | 917 | } else { | 
|  | 918 | if (res_cur->next) { | 
|  | 919 | if (res_prev->rangeno == res_cur->rangeno) | 
|  | 920 | res_prev->next = res_cur->next; | 
|  | 921 | else | 
|  | 922 | res_prev->nextRange = res_cur->next; | 
|  | 923 | } else if (res_cur->nextRange) { | 
|  | 924 | res_prev->next = NULL; | 
|  | 925 | res_prev->nextRange = res_cur->nextRange; | 
|  | 926 | } else { | 
|  | 927 | res_prev->next = NULL; | 
|  | 928 | res_prev->nextRange = NULL; | 
|  | 929 | } | 
|  | 930 | kfree (res_cur); | 
|  | 931 | return 0; | 
|  | 932 | } | 
|  | 933 |  | 
|  | 934 | return 0; | 
|  | 935 | } | 
|  | 936 |  | 
|  | 937 | static struct range_node * find_range (struct bus_node *bus_cur, struct resource_node * res) | 
|  | 938 | { | 
|  | 939 | struct range_node * range = NULL; | 
|  | 940 |  | 
|  | 941 | switch (res->type) { | 
|  | 942 | case IO: | 
|  | 943 | range = bus_cur->rangeIO; | 
|  | 944 | break; | 
|  | 945 | case MEM: | 
|  | 946 | range = bus_cur->rangeMem; | 
|  | 947 | break; | 
|  | 948 | case PFMEM: | 
|  | 949 | range = bus_cur->rangePFMem; | 
|  | 950 | break; | 
|  | 951 | default: | 
|  | 952 | err ("cannot read resource type in find_range\n"); | 
|  | 953 | } | 
|  | 954 |  | 
|  | 955 | while (range) { | 
|  | 956 | if (res->rangeno == range->rangeno) | 
|  | 957 | break; | 
|  | 958 | range = range->next; | 
|  | 959 | } | 
|  | 960 | return range; | 
|  | 961 | } | 
|  | 962 |  | 
|  | 963 | /***************************************************************************** | 
|  | 964 | * This routine will check to make sure the io/mem/pfmem->len that the device asked for | 
|  | 965 | * can fit w/i our list of available IO/MEM/PFMEM resources.  If cannot, returns -EINVAL, | 
|  | 966 | * otherwise, returns 0 | 
|  | 967 | * | 
|  | 968 | * Input: resource | 
|  | 969 | * Ouput: the correct start and end address are inputted into the resource node, | 
|  | 970 | *        0 or -EINVAL | 
|  | 971 | *****************************************************************************/ | 
|  | 972 | int ibmphp_check_resource (struct resource_node *res, u8 bridge) | 
|  | 973 | { | 
|  | 974 | struct bus_node *bus_cur; | 
|  | 975 | struct range_node *range = NULL; | 
|  | 976 | struct resource_node *res_prev; | 
|  | 977 | struct resource_node *res_cur = NULL; | 
|  | 978 | u32 len_cur = 0, start_cur = 0, len_tmp = 0; | 
|  | 979 | int noranges = 0; | 
|  | 980 | u32 tmp_start;		/* this is to make sure start address is divisible by the length needed */ | 
|  | 981 | u32 tmp_divide; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 982 | u8 flag = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 983 |  | 
|  | 984 | if (!res) | 
|  | 985 | return -EINVAL; | 
|  | 986 |  | 
|  | 987 | if (bridge) { | 
|  | 988 | /* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/ | 
|  | 989 | if (res->type == IO) | 
|  | 990 | tmp_divide = IOBRIDGE; | 
|  | 991 | else | 
|  | 992 | tmp_divide = MEMBRIDGE; | 
|  | 993 | } else | 
|  | 994 | tmp_divide = res->len; | 
|  | 995 |  | 
|  | 996 | bus_cur = find_bus_wprev (res->busno, NULL, 0); | 
|  | 997 |  | 
|  | 998 | if (!bus_cur) { | 
|  | 999 | /* didn't find a bus, smth's wrong!!! */ | 
|  | 1000 | debug ("no bus in the system, either pci_dev's wrong or allocation failed\n"); | 
|  | 1001 | return -EINVAL; | 
|  | 1002 | } | 
|  | 1003 |  | 
| Harvey Harrison | 66bef8c | 2008-03-03 19:09:46 -0800 | [diff] [blame] | 1004 | debug ("%s - enter\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1005 | debug ("bus_cur->busno is %d\n", bus_cur->busno); | 
|  | 1006 |  | 
|  | 1007 | /* This is a quick fix to not mess up with the code very much.  i.e., | 
|  | 1008 | * 2000-2fff, len = 1000, but when we compare, we need it to be fff */ | 
|  | 1009 | res->len -= 1; | 
|  | 1010 |  | 
|  | 1011 | switch (res->type) { | 
|  | 1012 | case IO: | 
|  | 1013 | res_cur = bus_cur->firstIO; | 
|  | 1014 | noranges = bus_cur->noIORanges; | 
|  | 1015 | break; | 
|  | 1016 | case MEM: | 
|  | 1017 | res_cur = bus_cur->firstMem; | 
|  | 1018 | noranges = bus_cur->noMemRanges; | 
|  | 1019 | break; | 
|  | 1020 | case PFMEM: | 
|  | 1021 | res_cur = bus_cur->firstPFMem; | 
|  | 1022 | noranges = bus_cur->noPFMemRanges; | 
|  | 1023 | break; | 
|  | 1024 | default: | 
|  | 1025 | err ("wrong type of resource to check\n"); | 
|  | 1026 | return -EINVAL; | 
|  | 1027 | } | 
|  | 1028 | res_prev = NULL; | 
|  | 1029 |  | 
|  | 1030 | while (res_cur) { | 
|  | 1031 | range = find_range (bus_cur, res_cur); | 
| Harvey Harrison | 66bef8c | 2008-03-03 19:09:46 -0800 | [diff] [blame] | 1032 | debug ("%s - rangeno = %d\n", __func__, res_cur->rangeno); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1033 |  | 
|  | 1034 | if (!range) { | 
|  | 1035 | err ("no range for the device exists... bailing out...\n"); | 
|  | 1036 | return -EINVAL; | 
|  | 1037 | } | 
|  | 1038 |  | 
|  | 1039 | /* found our range */ | 
|  | 1040 | if (!res_prev) { | 
|  | 1041 | /* first time in the loop */ | 
|  | 1042 | if ((res_cur->start != range->start) && ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { | 
|  | 1043 | debug ("len_tmp = %x\n", len_tmp); | 
|  | 1044 |  | 
|  | 1045 | if ((len_tmp < len_cur) || (len_cur == 0)) { | 
|  | 1046 |  | 
|  | 1047 | if ((range->start % tmp_divide) == 0) { | 
|  | 1048 | /* just perfect, starting address is divisible by length */ | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1049 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1050 | len_cur = len_tmp; | 
|  | 1051 | start_cur = range->start; | 
|  | 1052 | } else { | 
|  | 1053 | /* Needs adjusting */ | 
|  | 1054 | tmp_start = range->start; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1055 | flag = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1056 |  | 
|  | 1057 | while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { | 
|  | 1058 | if ((tmp_start % tmp_divide) == 0) { | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1059 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1060 | len_cur = len_tmp; | 
|  | 1061 | start_cur = tmp_start; | 
|  | 1062 | break; | 
|  | 1063 | } | 
|  | 1064 | tmp_start += tmp_divide - tmp_start % tmp_divide; | 
|  | 1065 | if (tmp_start >= res_cur->start - 1) | 
|  | 1066 | break; | 
|  | 1067 | } | 
|  | 1068 | } | 
|  | 1069 |  | 
|  | 1070 | if (flag && len_cur == res->len) { | 
|  | 1071 | debug ("but we are not here, right?\n"); | 
|  | 1072 | res->start = start_cur; | 
|  | 1073 | res->len += 1; /* To restore the balance */ | 
|  | 1074 | res->end = res->start + res->len - 1; | 
|  | 1075 | return 0; | 
|  | 1076 | } | 
|  | 1077 | } | 
|  | 1078 | } | 
|  | 1079 | } | 
|  | 1080 | if (!res_cur->next) { | 
|  | 1081 | /* last device on the range */ | 
|  | 1082 | if ((range->end != res_cur->end) && ((len_tmp = range->end - (res_cur->end + 1)) >= res->len)) { | 
|  | 1083 | debug ("len_tmp = %x\n", len_tmp); | 
|  | 1084 | if ((len_tmp < len_cur) || (len_cur == 0)) { | 
|  | 1085 |  | 
|  | 1086 | if (((res_cur->end + 1) % tmp_divide) == 0) { | 
|  | 1087 | /* just perfect, starting address is divisible by length */ | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1088 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1089 | len_cur = len_tmp; | 
|  | 1090 | start_cur = res_cur->end + 1; | 
|  | 1091 | } else { | 
|  | 1092 | /* Needs adjusting */ | 
|  | 1093 | tmp_start = res_cur->end + 1; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1094 | flag = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1095 |  | 
|  | 1096 | while ((len_tmp = range->end - tmp_start) >= res->len) { | 
|  | 1097 | if ((tmp_start % tmp_divide) == 0) { | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1098 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1099 | len_cur = len_tmp; | 
|  | 1100 | start_cur = tmp_start; | 
|  | 1101 | break; | 
|  | 1102 | } | 
|  | 1103 | tmp_start += tmp_divide - tmp_start % tmp_divide; | 
|  | 1104 | if (tmp_start >= range->end) | 
|  | 1105 | break; | 
|  | 1106 | } | 
|  | 1107 | } | 
|  | 1108 | if (flag && len_cur == res->len) { | 
|  | 1109 | res->start = start_cur; | 
|  | 1110 | res->len += 1; /* To restore the balance */ | 
|  | 1111 | res->end = res->start + res->len - 1; | 
|  | 1112 | return 0; | 
|  | 1113 | } | 
|  | 1114 | } | 
|  | 1115 | } | 
|  | 1116 | } | 
|  | 1117 |  | 
|  | 1118 | if (res_prev) { | 
|  | 1119 | if (res_prev->rangeno != res_cur->rangeno) { | 
|  | 1120 | /* 1st device on this range */ | 
|  | 1121 | if ((res_cur->start != range->start) && | 
|  | 1122 | ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { | 
|  | 1123 | if ((len_tmp < len_cur) || (len_cur == 0)) { | 
|  | 1124 | if ((range->start % tmp_divide) == 0) { | 
|  | 1125 | /* just perfect, starting address is divisible by length */ | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1126 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1127 | len_cur = len_tmp; | 
|  | 1128 | start_cur = range->start; | 
|  | 1129 | } else { | 
|  | 1130 | /* Needs adjusting */ | 
|  | 1131 | tmp_start = range->start; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1132 | flag = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1133 |  | 
|  | 1134 | while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { | 
|  | 1135 | if ((tmp_start % tmp_divide) == 0) { | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1136 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1137 | len_cur = len_tmp; | 
|  | 1138 | start_cur = tmp_start; | 
|  | 1139 | break; | 
|  | 1140 | } | 
|  | 1141 | tmp_start += tmp_divide - tmp_start % tmp_divide; | 
|  | 1142 | if (tmp_start >= res_cur->start - 1) | 
|  | 1143 | break; | 
|  | 1144 | } | 
|  | 1145 | } | 
|  | 1146 |  | 
|  | 1147 | if (flag && len_cur == res->len) { | 
|  | 1148 | res->start = start_cur; | 
|  | 1149 | res->len += 1; /* To restore the balance */ | 
|  | 1150 | res->end = res->start + res->len - 1; | 
|  | 1151 | return 0; | 
|  | 1152 | } | 
|  | 1153 | } | 
|  | 1154 | } | 
|  | 1155 | } else { | 
|  | 1156 | /* in the same range */ | 
|  | 1157 | if ((len_tmp = res_cur->start - 1 - res_prev->end - 1) >= res->len) { | 
|  | 1158 | if ((len_tmp < len_cur) || (len_cur == 0)) { | 
|  | 1159 | if (((res_prev->end + 1) % tmp_divide) == 0) { | 
|  | 1160 | /* just perfect, starting address's divisible by length */ | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1161 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1162 | len_cur = len_tmp; | 
|  | 1163 | start_cur = res_prev->end + 1; | 
|  | 1164 | } else { | 
|  | 1165 | /* Needs adjusting */ | 
|  | 1166 | tmp_start = res_prev->end + 1; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1167 | flag = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1168 |  | 
|  | 1169 | while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { | 
|  | 1170 | if ((tmp_start % tmp_divide) == 0) { | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1171 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1172 | len_cur = len_tmp; | 
|  | 1173 | start_cur = tmp_start; | 
|  | 1174 | break; | 
|  | 1175 | } | 
|  | 1176 | tmp_start += tmp_divide - tmp_start % tmp_divide; | 
|  | 1177 | if (tmp_start >= res_cur->start - 1) | 
|  | 1178 | break; | 
|  | 1179 | } | 
|  | 1180 | } | 
|  | 1181 |  | 
|  | 1182 | if (flag && len_cur == res->len) { | 
|  | 1183 | res->start = start_cur; | 
|  | 1184 | res->len += 1; /* To restore the balance */ | 
|  | 1185 | res->end = res->start + res->len - 1; | 
|  | 1186 | return 0; | 
|  | 1187 | } | 
|  | 1188 | } | 
|  | 1189 | } | 
|  | 1190 | } | 
|  | 1191 | } | 
|  | 1192 | /* end if (res_prev) */ | 
|  | 1193 | res_prev = res_cur; | 
|  | 1194 | if (res_cur->next) | 
|  | 1195 | res_cur = res_cur->next; | 
|  | 1196 | else | 
|  | 1197 | res_cur = res_cur->nextRange; | 
|  | 1198 | }	/* end of while */ | 
|  | 1199 |  | 
|  | 1200 |  | 
|  | 1201 | if (!res_prev) { | 
|  | 1202 | /* 1st device ever */ | 
|  | 1203 | /* need to find appropriate range */ | 
|  | 1204 | switch (res->type) { | 
|  | 1205 | case IO: | 
|  | 1206 | range = bus_cur->rangeIO; | 
|  | 1207 | break; | 
|  | 1208 | case MEM: | 
|  | 1209 | range = bus_cur->rangeMem; | 
|  | 1210 | break; | 
|  | 1211 | case PFMEM: | 
|  | 1212 | range = bus_cur->rangePFMem; | 
|  | 1213 | break; | 
|  | 1214 | } | 
|  | 1215 | while (range) { | 
|  | 1216 | if ((len_tmp = range->end - range->start) >= res->len) { | 
|  | 1217 | if ((len_tmp < len_cur) || (len_cur == 0)) { | 
|  | 1218 | if ((range->start % tmp_divide) == 0) { | 
|  | 1219 | /* just perfect, starting address's divisible by length */ | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1220 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1221 | len_cur = len_tmp; | 
|  | 1222 | start_cur = range->start; | 
|  | 1223 | } else { | 
|  | 1224 | /* Needs adjusting */ | 
|  | 1225 | tmp_start = range->start; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1226 | flag = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1227 |  | 
|  | 1228 | while ((len_tmp = range->end - tmp_start) >= res->len) { | 
|  | 1229 | if ((tmp_start % tmp_divide) == 0) { | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1230 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1231 | len_cur = len_tmp; | 
|  | 1232 | start_cur = tmp_start; | 
|  | 1233 | break; | 
|  | 1234 | } | 
|  | 1235 | tmp_start += tmp_divide - tmp_start % tmp_divide; | 
|  | 1236 | if (tmp_start >= range->end) | 
|  | 1237 | break; | 
|  | 1238 | } | 
|  | 1239 | } | 
|  | 1240 |  | 
|  | 1241 | if (flag && len_cur == res->len) { | 
|  | 1242 | res->start = start_cur; | 
|  | 1243 | res->len += 1; /* To restore the balance */ | 
|  | 1244 | res->end = res->start + res->len - 1; | 
|  | 1245 | return 0; | 
|  | 1246 | } | 
|  | 1247 | } | 
|  | 1248 | } | 
|  | 1249 | range = range->next; | 
|  | 1250 | }		/* end of while */ | 
|  | 1251 |  | 
|  | 1252 | if ((!range) && (len_cur == 0)) { | 
|  | 1253 | /* have gone through the list of devices and ranges and haven't found n.e.thing */ | 
|  | 1254 | err ("no appropriate range.. bailing out...\n"); | 
|  | 1255 | return -EINVAL; | 
|  | 1256 | } else if (len_cur) { | 
|  | 1257 | res->start = start_cur; | 
|  | 1258 | res->len += 1; /* To restore the balance */ | 
|  | 1259 | res->end = res->start + res->len - 1; | 
|  | 1260 | return 0; | 
|  | 1261 | } | 
|  | 1262 | } | 
|  | 1263 |  | 
|  | 1264 | if (!res_cur) { | 
|  | 1265 | debug ("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges); | 
|  | 1266 | if (res_prev->rangeno < noranges) { | 
|  | 1267 | /* if there're more ranges out there to check */ | 
|  | 1268 | switch (res->type) { | 
|  | 1269 | case IO: | 
|  | 1270 | range = bus_cur->rangeIO; | 
|  | 1271 | break; | 
|  | 1272 | case MEM: | 
|  | 1273 | range = bus_cur->rangeMem; | 
|  | 1274 | break; | 
|  | 1275 | case PFMEM: | 
|  | 1276 | range = bus_cur->rangePFMem; | 
|  | 1277 | break; | 
|  | 1278 | } | 
|  | 1279 | while (range) { | 
|  | 1280 | if ((len_tmp = range->end - range->start) >= res->len) { | 
|  | 1281 | if ((len_tmp < len_cur) || (len_cur == 0)) { | 
|  | 1282 | if ((range->start % tmp_divide) == 0) { | 
|  | 1283 | /* just perfect, starting address's divisible by length */ | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1284 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1285 | len_cur = len_tmp; | 
|  | 1286 | start_cur = range->start; | 
|  | 1287 | } else { | 
|  | 1288 | /* Needs adjusting */ | 
|  | 1289 | tmp_start = range->start; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1290 | flag = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1291 |  | 
|  | 1292 | while ((len_tmp = range->end - tmp_start) >= res->len) { | 
|  | 1293 | if ((tmp_start % tmp_divide) == 0) { | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1294 | flag = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1295 | len_cur = len_tmp; | 
|  | 1296 | start_cur = tmp_start; | 
|  | 1297 | break; | 
|  | 1298 | } | 
|  | 1299 | tmp_start += tmp_divide - tmp_start % tmp_divide; | 
|  | 1300 | if (tmp_start >= range->end) | 
|  | 1301 | break; | 
|  | 1302 | } | 
|  | 1303 | } | 
|  | 1304 |  | 
|  | 1305 | if (flag && len_cur == res->len) { | 
|  | 1306 | res->start = start_cur; | 
|  | 1307 | res->len += 1; /* To restore the balance */ | 
|  | 1308 | res->end = res->start + res->len - 1; | 
|  | 1309 | return 0; | 
|  | 1310 | } | 
|  | 1311 | } | 
|  | 1312 | } | 
|  | 1313 | range = range->next; | 
|  | 1314 | }	/* end of while */ | 
|  | 1315 |  | 
|  | 1316 | if ((!range) && (len_cur == 0)) { | 
|  | 1317 | /* have gone through the list of devices and ranges and haven't found n.e.thing */ | 
|  | 1318 | err ("no appropriate range.. bailing out...\n"); | 
|  | 1319 | return -EINVAL; | 
|  | 1320 | } else if (len_cur) { | 
|  | 1321 | res->start = start_cur; | 
|  | 1322 | res->len += 1; /* To restore the balance */ | 
|  | 1323 | res->end = res->start + res->len - 1; | 
|  | 1324 | return 0; | 
|  | 1325 | } | 
|  | 1326 | } else { | 
|  | 1327 | /* no more ranges to check on */ | 
|  | 1328 | if (len_cur) { | 
|  | 1329 | res->start = start_cur; | 
|  | 1330 | res->len += 1; /* To restore the balance */ | 
|  | 1331 | res->end = res->start + res->len - 1; | 
|  | 1332 | return 0; | 
|  | 1333 | } else { | 
|  | 1334 | /* have gone through the list of devices and haven't found n.e.thing */ | 
|  | 1335 | err ("no appropriate range.. bailing out...\n"); | 
|  | 1336 | return -EINVAL; | 
|  | 1337 | } | 
|  | 1338 | } | 
|  | 1339 | }	/* end if(!res_cur) */ | 
|  | 1340 | return -EINVAL; | 
|  | 1341 | } | 
|  | 1342 |  | 
|  | 1343 | /******************************************************************************** | 
|  | 1344 | * This routine is called from remove_card if the card contained PPB. | 
|  | 1345 | * It will remove all the resources on the bus as well as the bus itself | 
|  | 1346 | * Input: Bus | 
|  | 1347 | * Ouput: 0, -ENODEV | 
|  | 1348 | ********************************************************************************/ | 
|  | 1349 | int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno) | 
|  | 1350 | { | 
|  | 1351 | struct resource_node *res_cur; | 
|  | 1352 | struct resource_node *res_tmp; | 
|  | 1353 | struct bus_node *prev_bus; | 
|  | 1354 | int rc; | 
|  | 1355 |  | 
|  | 1356 | prev_bus = find_bus_wprev (parent_busno, NULL, 0); | 
|  | 1357 |  | 
|  | 1358 | if (!prev_bus) { | 
|  | 1359 | debug ("something terribly wrong. Cannot find parent bus to the one to remove\n"); | 
|  | 1360 | return -ENODEV; | 
|  | 1361 | } | 
|  | 1362 |  | 
|  | 1363 | debug ("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno); | 
|  | 1364 |  | 
|  | 1365 | rc = remove_ranges (bus, prev_bus); | 
|  | 1366 | if (rc) | 
|  | 1367 | return rc; | 
|  | 1368 |  | 
|  | 1369 | if (bus->firstIO) { | 
|  | 1370 | res_cur = bus->firstIO; | 
|  | 1371 | while (res_cur) { | 
|  | 1372 | res_tmp = res_cur; | 
|  | 1373 | if (res_cur->next) | 
|  | 1374 | res_cur = res_cur->next; | 
|  | 1375 | else | 
|  | 1376 | res_cur = res_cur->nextRange; | 
|  | 1377 | kfree (res_tmp); | 
|  | 1378 | res_tmp = NULL; | 
|  | 1379 | } | 
|  | 1380 | bus->firstIO = NULL; | 
|  | 1381 | } | 
|  | 1382 | if (bus->firstMem) { | 
|  | 1383 | res_cur = bus->firstMem; | 
|  | 1384 | while (res_cur) { | 
|  | 1385 | res_tmp = res_cur; | 
|  | 1386 | if (res_cur->next) | 
|  | 1387 | res_cur = res_cur->next; | 
|  | 1388 | else | 
|  | 1389 | res_cur = res_cur->nextRange; | 
|  | 1390 | kfree (res_tmp); | 
|  | 1391 | res_tmp = NULL; | 
|  | 1392 | } | 
|  | 1393 | bus->firstMem = NULL; | 
|  | 1394 | } | 
|  | 1395 | if (bus->firstPFMem) { | 
|  | 1396 | res_cur = bus->firstPFMem; | 
|  | 1397 | while (res_cur) { | 
|  | 1398 | res_tmp = res_cur; | 
|  | 1399 | if (res_cur->next) | 
|  | 1400 | res_cur = res_cur->next; | 
|  | 1401 | else | 
|  | 1402 | res_cur = res_cur->nextRange; | 
|  | 1403 | kfree (res_tmp); | 
|  | 1404 | res_tmp = NULL; | 
|  | 1405 | } | 
|  | 1406 | bus->firstPFMem = NULL; | 
|  | 1407 | } | 
|  | 1408 |  | 
|  | 1409 | if (bus->firstPFMemFromMem) { | 
|  | 1410 | res_cur = bus->firstPFMemFromMem; | 
|  | 1411 | while (res_cur) { | 
|  | 1412 | res_tmp = res_cur; | 
|  | 1413 | res_cur = res_cur->next; | 
|  | 1414 |  | 
|  | 1415 | kfree (res_tmp); | 
|  | 1416 | res_tmp = NULL; | 
|  | 1417 | } | 
|  | 1418 | bus->firstPFMemFromMem = NULL; | 
|  | 1419 | } | 
|  | 1420 |  | 
|  | 1421 | list_del (&bus->bus_list); | 
|  | 1422 | kfree (bus); | 
|  | 1423 | return 0; | 
|  | 1424 | } | 
|  | 1425 |  | 
|  | 1426 | /****************************************************************************** | 
|  | 1427 | * This routine deletes the ranges from a given bus, and the entries from the | 
|  | 1428 | * parent's bus in the resources | 
|  | 1429 | * Input: current bus, previous bus | 
|  | 1430 | * Output: 0, -EINVAL | 
|  | 1431 | ******************************************************************************/ | 
|  | 1432 | static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev) | 
|  | 1433 | { | 
|  | 1434 | struct range_node *range_cur; | 
|  | 1435 | struct range_node *range_tmp; | 
|  | 1436 | int i; | 
|  | 1437 | struct resource_node *res = NULL; | 
|  | 1438 |  | 
|  | 1439 | if (bus_cur->noIORanges) { | 
|  | 1440 | range_cur = bus_cur->rangeIO; | 
|  | 1441 | for (i = 0; i < bus_cur->noIORanges; i++) { | 
|  | 1442 | if (ibmphp_find_resource (bus_prev, range_cur->start, &res, IO) < 0) | 
|  | 1443 | return -EINVAL; | 
|  | 1444 | ibmphp_remove_resource (res); | 
|  | 1445 |  | 
|  | 1446 | range_tmp = range_cur; | 
|  | 1447 | range_cur = range_cur->next; | 
|  | 1448 | kfree (range_tmp); | 
|  | 1449 | range_tmp = NULL; | 
|  | 1450 | } | 
|  | 1451 | bus_cur->rangeIO = NULL; | 
|  | 1452 | } | 
|  | 1453 | if (bus_cur->noMemRanges) { | 
|  | 1454 | range_cur = bus_cur->rangeMem; | 
|  | 1455 | for (i = 0; i < bus_cur->noMemRanges; i++) { | 
|  | 1456 | if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0) | 
|  | 1457 | return -EINVAL; | 
|  | 1458 |  | 
|  | 1459 | ibmphp_remove_resource (res); | 
|  | 1460 | range_tmp = range_cur; | 
|  | 1461 | range_cur = range_cur->next; | 
|  | 1462 | kfree (range_tmp); | 
|  | 1463 | range_tmp = NULL; | 
|  | 1464 | } | 
|  | 1465 | bus_cur->rangeMem = NULL; | 
|  | 1466 | } | 
|  | 1467 | if (bus_cur->noPFMemRanges) { | 
|  | 1468 | range_cur = bus_cur->rangePFMem; | 
|  | 1469 | for (i = 0; i < bus_cur->noPFMemRanges; i++) { | 
|  | 1470 | if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0) | 
|  | 1471 | return -EINVAL; | 
|  | 1472 |  | 
|  | 1473 | ibmphp_remove_resource (res); | 
|  | 1474 | range_tmp = range_cur; | 
|  | 1475 | range_cur = range_cur->next; | 
|  | 1476 | kfree (range_tmp); | 
|  | 1477 | range_tmp = NULL; | 
|  | 1478 | } | 
|  | 1479 | bus_cur->rangePFMem = NULL; | 
|  | 1480 | } | 
|  | 1481 | return 0; | 
|  | 1482 | } | 
|  | 1483 |  | 
|  | 1484 | /* | 
|  | 1485 | * find the resource node in the bus | 
|  | 1486 | * Input: Resource needed, start address of the resource, type of resource | 
|  | 1487 | */ | 
|  | 1488 | int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag) | 
|  | 1489 | { | 
|  | 1490 | struct resource_node *res_cur = NULL; | 
|  | 1491 | char * type = ""; | 
|  | 1492 |  | 
|  | 1493 | if (!bus) { | 
|  | 1494 | err ("The bus passed in NULL to find resource\n"); | 
|  | 1495 | return -ENODEV; | 
|  | 1496 | } | 
|  | 1497 |  | 
|  | 1498 | switch (flag) { | 
|  | 1499 | case IO: | 
|  | 1500 | res_cur = bus->firstIO; | 
|  | 1501 | type = "io"; | 
|  | 1502 | break; | 
|  | 1503 | case MEM: | 
|  | 1504 | res_cur = bus->firstMem; | 
|  | 1505 | type = "mem"; | 
|  | 1506 | break; | 
|  | 1507 | case PFMEM: | 
|  | 1508 | res_cur = bus->firstPFMem; | 
|  | 1509 | type = "pfmem"; | 
|  | 1510 | break; | 
|  | 1511 | default: | 
|  | 1512 | err ("wrong type of flag\n"); | 
|  | 1513 | return -EINVAL; | 
|  | 1514 | } | 
|  | 1515 |  | 
|  | 1516 | while (res_cur) { | 
|  | 1517 | if (res_cur->start == start_address) { | 
|  | 1518 | *res = res_cur; | 
|  | 1519 | break; | 
|  | 1520 | } | 
|  | 1521 | if (res_cur->next) | 
|  | 1522 | res_cur = res_cur->next; | 
|  | 1523 | else | 
|  | 1524 | res_cur = res_cur->nextRange; | 
|  | 1525 | } | 
|  | 1526 |  | 
|  | 1527 | if (!res_cur) { | 
|  | 1528 | if (flag == PFMEM) { | 
|  | 1529 | res_cur = bus->firstPFMemFromMem; | 
|  | 1530 | while (res_cur) { | 
|  | 1531 | if (res_cur->start == start_address) { | 
|  | 1532 | *res = res_cur; | 
|  | 1533 | break; | 
|  | 1534 | } | 
|  | 1535 | res_cur = res_cur->next; | 
|  | 1536 | } | 
|  | 1537 | if (!res_cur) { | 
|  | 1538 | debug ("SOS...cannot find %s resource in the bus.\n", type); | 
|  | 1539 | return -EINVAL; | 
|  | 1540 | } | 
|  | 1541 | } else { | 
|  | 1542 | debug ("SOS... cannot find %s resource in the bus.\n", type); | 
|  | 1543 | return -EINVAL; | 
|  | 1544 | } | 
|  | 1545 | } | 
|  | 1546 |  | 
|  | 1547 | if (*res) | 
|  | 1548 | debug ("*res->start = %x\n", (*res)->start); | 
|  | 1549 |  | 
|  | 1550 | return 0; | 
|  | 1551 | } | 
|  | 1552 |  | 
|  | 1553 | /*********************************************************************** | 
|  | 1554 | * This routine will free the resource structures used by the | 
|  | 1555 | * system.  It is called from cleanup routine for the module | 
|  | 1556 | * Parameters: none | 
|  | 1557 | * Returns: none | 
|  | 1558 | ***********************************************************************/ | 
|  | 1559 | void ibmphp_free_resources (void) | 
|  | 1560 | { | 
|  | 1561 | struct bus_node *bus_cur = NULL; | 
|  | 1562 | struct bus_node *bus_tmp; | 
|  | 1563 | struct range_node *range_cur; | 
|  | 1564 | struct range_node *range_tmp; | 
|  | 1565 | struct resource_node *res_cur; | 
|  | 1566 | struct resource_node *res_tmp; | 
|  | 1567 | struct list_head *tmp; | 
|  | 1568 | struct list_head *next; | 
|  | 1569 | int i = 0; | 
|  | 1570 | flags = 1; | 
|  | 1571 |  | 
|  | 1572 | list_for_each_safe (tmp, next, &gbuses) { | 
|  | 1573 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | 
|  | 1574 | if (bus_cur->noIORanges) { | 
|  | 1575 | range_cur = bus_cur->rangeIO; | 
|  | 1576 | for (i = 0; i < bus_cur->noIORanges; i++) { | 
|  | 1577 | if (!range_cur) | 
|  | 1578 | break; | 
|  | 1579 | range_tmp = range_cur; | 
|  | 1580 | range_cur = range_cur->next; | 
|  | 1581 | kfree (range_tmp); | 
|  | 1582 | range_tmp = NULL; | 
|  | 1583 | } | 
|  | 1584 | } | 
|  | 1585 | if (bus_cur->noMemRanges) { | 
|  | 1586 | range_cur = bus_cur->rangeMem; | 
|  | 1587 | for (i = 0; i < bus_cur->noMemRanges; i++) { | 
|  | 1588 | if (!range_cur) | 
|  | 1589 | break; | 
|  | 1590 | range_tmp = range_cur; | 
|  | 1591 | range_cur = range_cur->next; | 
|  | 1592 | kfree (range_tmp); | 
|  | 1593 | range_tmp = NULL; | 
|  | 1594 | } | 
|  | 1595 | } | 
|  | 1596 | if (bus_cur->noPFMemRanges) { | 
|  | 1597 | range_cur = bus_cur->rangePFMem; | 
|  | 1598 | for (i = 0; i < bus_cur->noPFMemRanges; i++) { | 
|  | 1599 | if (!range_cur) | 
|  | 1600 | break; | 
|  | 1601 | range_tmp = range_cur; | 
|  | 1602 | range_cur = range_cur->next; | 
|  | 1603 | kfree (range_tmp); | 
|  | 1604 | range_tmp = NULL; | 
|  | 1605 | } | 
|  | 1606 | } | 
|  | 1607 |  | 
|  | 1608 | if (bus_cur->firstIO) { | 
|  | 1609 | res_cur = bus_cur->firstIO; | 
|  | 1610 | while (res_cur) { | 
|  | 1611 | res_tmp = res_cur; | 
|  | 1612 | if (res_cur->next) | 
|  | 1613 | res_cur = res_cur->next; | 
|  | 1614 | else | 
|  | 1615 | res_cur = res_cur->nextRange; | 
|  | 1616 | kfree (res_tmp); | 
|  | 1617 | res_tmp = NULL; | 
|  | 1618 | } | 
|  | 1619 | bus_cur->firstIO = NULL; | 
|  | 1620 | } | 
|  | 1621 | if (bus_cur->firstMem) { | 
|  | 1622 | res_cur = bus_cur->firstMem; | 
|  | 1623 | while (res_cur) { | 
|  | 1624 | res_tmp = res_cur; | 
|  | 1625 | if (res_cur->next) | 
|  | 1626 | res_cur = res_cur->next; | 
|  | 1627 | else | 
|  | 1628 | res_cur = res_cur->nextRange; | 
|  | 1629 | kfree (res_tmp); | 
|  | 1630 | res_tmp = NULL; | 
|  | 1631 | } | 
|  | 1632 | bus_cur->firstMem = NULL; | 
|  | 1633 | } | 
|  | 1634 | if (bus_cur->firstPFMem) { | 
|  | 1635 | res_cur = bus_cur->firstPFMem; | 
|  | 1636 | while (res_cur) { | 
|  | 1637 | res_tmp = res_cur; | 
|  | 1638 | if (res_cur->next) | 
|  | 1639 | res_cur = res_cur->next; | 
|  | 1640 | else | 
|  | 1641 | res_cur = res_cur->nextRange; | 
|  | 1642 | kfree (res_tmp); | 
|  | 1643 | res_tmp = NULL; | 
|  | 1644 | } | 
|  | 1645 | bus_cur->firstPFMem = NULL; | 
|  | 1646 | } | 
|  | 1647 |  | 
|  | 1648 | if (bus_cur->firstPFMemFromMem) { | 
|  | 1649 | res_cur = bus_cur->firstPFMemFromMem; | 
|  | 1650 | while (res_cur) { | 
|  | 1651 | res_tmp = res_cur; | 
|  | 1652 | res_cur = res_cur->next; | 
|  | 1653 |  | 
|  | 1654 | kfree (res_tmp); | 
|  | 1655 | res_tmp = NULL; | 
|  | 1656 | } | 
|  | 1657 | bus_cur->firstPFMemFromMem = NULL; | 
|  | 1658 | } | 
|  | 1659 |  | 
|  | 1660 | bus_tmp = bus_cur; | 
|  | 1661 | list_del (&bus_cur->bus_list); | 
|  | 1662 | kfree (bus_tmp); | 
|  | 1663 | bus_tmp = NULL; | 
|  | 1664 | } | 
|  | 1665 | } | 
|  | 1666 |  | 
|  | 1667 | /********************************************************************************* | 
|  | 1668 | * This function will go over the PFmem resources to check if the EBDA allocated | 
|  | 1669 | * pfmem out of memory buckets of the bus.  If so, it will change the range numbers | 
|  | 1670 | * and a flag to indicate that this resource is out of memory. It will also move the | 
|  | 1671 | * Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create | 
|  | 1672 | * a new Mem node | 
|  | 1673 | * This routine is called right after initialization | 
|  | 1674 | *******************************************************************************/ | 
|  | 1675 | static int __init once_over (void) | 
|  | 1676 | { | 
|  | 1677 | struct resource_node *pfmem_cur; | 
|  | 1678 | struct resource_node *pfmem_prev; | 
|  | 1679 | struct resource_node *mem; | 
|  | 1680 | struct bus_node *bus_cur; | 
|  | 1681 | struct list_head *tmp; | 
|  | 1682 |  | 
|  | 1683 | list_for_each (tmp, &gbuses) { | 
|  | 1684 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | 
|  | 1685 | if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) { | 
|  | 1686 | for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) { | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 1687 | pfmem_cur->fromMem = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1688 | if (pfmem_prev) | 
|  | 1689 | pfmem_prev->next = pfmem_cur->next; | 
|  | 1690 | else | 
|  | 1691 | bus_cur->firstPFMem = pfmem_cur->next; | 
|  | 1692 |  | 
|  | 1693 | if (!bus_cur->firstPFMemFromMem) | 
|  | 1694 | pfmem_cur->next = NULL; | 
|  | 1695 | else | 
|  | 1696 | /* we don't need to sort PFMemFromMem since we're using mem node for | 
|  | 1697 | all the real work anyways, so just insert at the beginning of the | 
|  | 1698 | list | 
|  | 1699 | */ | 
|  | 1700 | pfmem_cur->next = bus_cur->firstPFMemFromMem; | 
|  | 1701 |  | 
|  | 1702 | bus_cur->firstPFMemFromMem = pfmem_cur; | 
|  | 1703 |  | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 1704 | mem = kzalloc(sizeof(struct resource_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1705 | if (!mem) { | 
|  | 1706 | err ("out of system memory\n"); | 
|  | 1707 | return -ENOMEM; | 
|  | 1708 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1709 | mem->type = MEM; | 
|  | 1710 | mem->busno = pfmem_cur->busno; | 
|  | 1711 | mem->devfunc = pfmem_cur->devfunc; | 
|  | 1712 | mem->start = pfmem_cur->start; | 
|  | 1713 | mem->end = pfmem_cur->end; | 
|  | 1714 | mem->len = pfmem_cur->len; | 
|  | 1715 | if (ibmphp_add_resource (mem) < 0) | 
|  | 1716 | err ("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n"); | 
|  | 1717 | pfmem_cur->rangeno = mem->rangeno; | 
|  | 1718 | }	/* end for pfmem */ | 
|  | 1719 | }	/* end if */ | 
|  | 1720 | }	/* end list_for_each bus */ | 
|  | 1721 | return 0; | 
|  | 1722 | } | 
|  | 1723 |  | 
|  | 1724 | int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem) | 
|  | 1725 | { | 
|  | 1726 | struct bus_node *bus_cur = find_bus_wprev (pfmem->busno, NULL, 0); | 
|  | 1727 |  | 
|  | 1728 | if (!bus_cur) { | 
|  | 1729 | err ("cannot find bus of pfmem to add...\n"); | 
|  | 1730 | return -ENODEV; | 
|  | 1731 | } | 
|  | 1732 |  | 
|  | 1733 | if (bus_cur->firstPFMemFromMem) | 
|  | 1734 | pfmem->next = bus_cur->firstPFMemFromMem; | 
|  | 1735 | else | 
|  | 1736 | pfmem->next = NULL; | 
|  | 1737 |  | 
|  | 1738 | bus_cur->firstPFMemFromMem = pfmem; | 
|  | 1739 |  | 
|  | 1740 | return 0; | 
|  | 1741 | } | 
|  | 1742 |  | 
|  | 1743 | /* This routine just goes through the buses to see if the bus already exists. | 
|  | 1744 | * It is called from ibmphp_find_sec_number, to find out a secondary bus number for | 
|  | 1745 | * bridged cards | 
|  | 1746 | * Parameters: bus_number | 
|  | 1747 | * Returns: Bus pointer or NULL | 
|  | 1748 | */ | 
|  | 1749 | struct bus_node *ibmphp_find_res_bus (u8 bus_number) | 
|  | 1750 | { | 
|  | 1751 | return find_bus_wprev (bus_number, NULL, 0); | 
|  | 1752 | } | 
|  | 1753 |  | 
|  | 1754 | static struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u8 flag) | 
|  | 1755 | { | 
|  | 1756 | struct bus_node *bus_cur; | 
|  | 1757 | struct list_head *tmp; | 
|  | 1758 | struct list_head *tmp_prev; | 
|  | 1759 |  | 
|  | 1760 | list_for_each (tmp, &gbuses) { | 
|  | 1761 | tmp_prev = tmp->prev; | 
|  | 1762 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | 
|  | 1763 | if (flag) | 
|  | 1764 | *prev = list_entry (tmp_prev, struct bus_node, bus_list); | 
|  | 1765 | if (bus_cur->busno == bus_number) | 
|  | 1766 | return bus_cur; | 
|  | 1767 | } | 
|  | 1768 |  | 
|  | 1769 | return NULL; | 
|  | 1770 | } | 
|  | 1771 |  | 
|  | 1772 | void ibmphp_print_test (void) | 
|  | 1773 | { | 
|  | 1774 | int i = 0; | 
|  | 1775 | struct bus_node *bus_cur = NULL; | 
|  | 1776 | struct range_node *range; | 
|  | 1777 | struct resource_node *res; | 
|  | 1778 | struct list_head *tmp; | 
|  | 1779 |  | 
|  | 1780 | debug_pci ("*****************START**********************\n"); | 
|  | 1781 |  | 
|  | 1782 | if ((!list_empty(&gbuses)) && flags) { | 
|  | 1783 | err ("The GBUSES is not NULL?!?!?!?!?\n"); | 
|  | 1784 | return; | 
|  | 1785 | } | 
|  | 1786 |  | 
|  | 1787 | list_for_each (tmp, &gbuses) { | 
|  | 1788 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | 
|  | 1789 | debug_pci ("This is bus # %d.  There are\n", bus_cur->busno); | 
|  | 1790 | debug_pci ("IORanges = %d\t", bus_cur->noIORanges); | 
|  | 1791 | debug_pci ("MemRanges = %d\t", bus_cur->noMemRanges); | 
|  | 1792 | debug_pci ("PFMemRanges = %d\n", bus_cur->noPFMemRanges); | 
|  | 1793 | debug_pci ("The IO Ranges are as follows:\n"); | 
|  | 1794 | if (bus_cur->rangeIO) { | 
|  | 1795 | range = bus_cur->rangeIO; | 
|  | 1796 | for (i = 0; i < bus_cur->noIORanges; i++) { | 
|  | 1797 | debug_pci ("rangeno is %d\n", range->rangeno); | 
|  | 1798 | debug_pci ("[%x - %x]\n", range->start, range->end); | 
|  | 1799 | range = range->next; | 
|  | 1800 | } | 
|  | 1801 | } | 
|  | 1802 |  | 
|  | 1803 | debug_pci ("The Mem Ranges are as follows:\n"); | 
|  | 1804 | if (bus_cur->rangeMem) { | 
|  | 1805 | range = bus_cur->rangeMem; | 
|  | 1806 | for (i = 0; i < bus_cur->noMemRanges; i++) { | 
|  | 1807 | debug_pci ("rangeno is %d\n", range->rangeno); | 
|  | 1808 | debug_pci ("[%x - %x]\n", range->start, range->end); | 
|  | 1809 | range = range->next; | 
|  | 1810 | } | 
|  | 1811 | } | 
|  | 1812 |  | 
|  | 1813 | debug_pci ("The PFMem Ranges are as follows:\n"); | 
|  | 1814 |  | 
|  | 1815 | if (bus_cur->rangePFMem) { | 
|  | 1816 | range = bus_cur->rangePFMem; | 
|  | 1817 | for (i = 0; i < bus_cur->noPFMemRanges; i++) { | 
|  | 1818 | debug_pci ("rangeno is %d\n", range->rangeno); | 
|  | 1819 | debug_pci ("[%x - %x]\n", range->start, range->end); | 
|  | 1820 | range = range->next; | 
|  | 1821 | } | 
|  | 1822 | } | 
|  | 1823 |  | 
|  | 1824 | debug_pci ("The resources on this bus are as follows\n"); | 
|  | 1825 |  | 
|  | 1826 | debug_pci ("IO...\n"); | 
|  | 1827 | if (bus_cur->firstIO) { | 
|  | 1828 | res = bus_cur->firstIO; | 
|  | 1829 | while (res) { | 
|  | 1830 | debug_pci ("The range # is %d\n", res->rangeno); | 
|  | 1831 | debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); | 
|  | 1832 | debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); | 
|  | 1833 | if (res->next) | 
|  | 1834 | res = res->next; | 
|  | 1835 | else if (res->nextRange) | 
|  | 1836 | res = res->nextRange; | 
|  | 1837 | else | 
|  | 1838 | break; | 
|  | 1839 | } | 
|  | 1840 | } | 
|  | 1841 | debug_pci ("Mem...\n"); | 
|  | 1842 | if (bus_cur->firstMem) { | 
|  | 1843 | res = bus_cur->firstMem; | 
|  | 1844 | while (res) { | 
|  | 1845 | debug_pci ("The range # is %d\n", res->rangeno); | 
|  | 1846 | debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); | 
|  | 1847 | debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); | 
|  | 1848 | if (res->next) | 
|  | 1849 | res = res->next; | 
|  | 1850 | else if (res->nextRange) | 
|  | 1851 | res = res->nextRange; | 
|  | 1852 | else | 
|  | 1853 | break; | 
|  | 1854 | } | 
|  | 1855 | } | 
|  | 1856 | debug_pci ("PFMem...\n"); | 
|  | 1857 | if (bus_cur->firstPFMem) { | 
|  | 1858 | res = bus_cur->firstPFMem; | 
|  | 1859 | while (res) { | 
|  | 1860 | debug_pci ("The range # is %d\n", res->rangeno); | 
|  | 1861 | debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); | 
|  | 1862 | debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); | 
|  | 1863 | if (res->next) | 
|  | 1864 | res = res->next; | 
|  | 1865 | else if (res->nextRange) | 
|  | 1866 | res = res->nextRange; | 
|  | 1867 | else | 
|  | 1868 | break; | 
|  | 1869 | } | 
|  | 1870 | } | 
|  | 1871 |  | 
|  | 1872 | debug_pci ("PFMemFromMem...\n"); | 
|  | 1873 | if (bus_cur->firstPFMemFromMem) { | 
|  | 1874 | res = bus_cur->firstPFMemFromMem; | 
|  | 1875 | while (res) { | 
|  | 1876 | debug_pci ("The range # is %d\n", res->rangeno); | 
|  | 1877 | debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); | 
|  | 1878 | debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); | 
|  | 1879 | res = res->next; | 
|  | 1880 | } | 
|  | 1881 | } | 
|  | 1882 | } | 
|  | 1883 | debug_pci ("***********************END***********************\n"); | 
|  | 1884 | } | 
|  | 1885 |  | 
|  | 1886 | static int range_exists_already (struct range_node * range, struct bus_node * bus_cur, u8 type) | 
|  | 1887 | { | 
|  | 1888 | struct range_node * range_cur = NULL; | 
|  | 1889 | switch (type) { | 
|  | 1890 | case IO: | 
|  | 1891 | range_cur = bus_cur->rangeIO; | 
|  | 1892 | break; | 
|  | 1893 | case MEM: | 
|  | 1894 | range_cur = bus_cur->rangeMem; | 
|  | 1895 | break; | 
|  | 1896 | case PFMEM: | 
|  | 1897 | range_cur = bus_cur->rangePFMem; | 
|  | 1898 | break; | 
|  | 1899 | default: | 
|  | 1900 | err ("wrong type passed to find out if range already exists\n"); | 
|  | 1901 | return -ENODEV; | 
|  | 1902 | } | 
|  | 1903 |  | 
|  | 1904 | while (range_cur) { | 
|  | 1905 | if ((range_cur->start == range->start) && (range_cur->end == range->end)) | 
|  | 1906 | return 1; | 
|  | 1907 | range_cur = range_cur->next; | 
|  | 1908 | } | 
|  | 1909 |  | 
|  | 1910 | return 0; | 
|  | 1911 | } | 
|  | 1912 |  | 
|  | 1913 | /* This routine will read the windows for any PPB we have and update the | 
|  | 1914 | * range info for the secondary bus, and will also input this info into | 
|  | 1915 | * primary bus, since BIOS doesn't. This is for PPB that are in the system | 
|  | 1916 | * on bootup.  For bridged cards that were added during previous load of the | 
|  | 1917 | * driver, only the ranges and the bus structure are added, the devices are | 
|  | 1918 | * added from NVRAM | 
|  | 1919 | * Input: primary busno | 
|  | 1920 | * Returns: none | 
|  | 1921 | * Note: this function doesn't take into account IO restrictions etc, | 
|  | 1922 | *	 so will only work for bridges with no video/ISA devices behind them It | 
|  | 1923 | *	 also will not work for onboard PPB's that can have more than 1 *bus | 
|  | 1924 | *	 behind them All these are TO DO. | 
|  | 1925 | *	 Also need to add more error checkings... (from fnc returns etc) | 
|  | 1926 | */ | 
|  | 1927 | static int __init update_bridge_ranges (struct bus_node **bus) | 
|  | 1928 | { | 
|  | 1929 | u8 sec_busno, device, function, hdr_type, start_io_address, end_io_address; | 
|  | 1930 | u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address; | 
|  | 1931 | u32 start_address, end_address, upper_start, upper_end; | 
|  | 1932 | struct bus_node *bus_sec; | 
|  | 1933 | struct bus_node *bus_cur; | 
|  | 1934 | struct resource_node *io; | 
|  | 1935 | struct resource_node *mem; | 
|  | 1936 | struct resource_node *pfmem; | 
|  | 1937 | struct range_node *range; | 
|  | 1938 | unsigned int devfn; | 
|  | 1939 |  | 
|  | 1940 | bus_cur = *bus; | 
|  | 1941 | if (!bus_cur) | 
|  | 1942 | return -ENODEV; | 
|  | 1943 | ibmphp_pci_bus->number = bus_cur->busno; | 
|  | 1944 |  | 
| Harvey Harrison | 66bef8c | 2008-03-03 19:09:46 -0800 | [diff] [blame] | 1945 | debug ("inside %s\n", __func__); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1946 | debug ("bus_cur->busno = %x\n", bus_cur->busno); | 
|  | 1947 |  | 
|  | 1948 | for (device = 0; device < 32; device++) { | 
|  | 1949 | for (function = 0x00; function < 0x08; function++) { | 
|  | 1950 | devfn = PCI_DEVFN(device, function); | 
|  | 1951 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); | 
|  | 1952 |  | 
|  | 1953 | if (vendor_id != PCI_VENDOR_ID_NOTVALID) { | 
|  | 1954 | /* found correct device!!! */ | 
|  | 1955 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); | 
|  | 1956 |  | 
|  | 1957 | switch (hdr_type) { | 
|  | 1958 | case PCI_HEADER_TYPE_NORMAL: | 
|  | 1959 | function = 0x8; | 
|  | 1960 | break; | 
|  | 1961 | case PCI_HEADER_TYPE_MULTIDEVICE: | 
|  | 1962 | break; | 
|  | 1963 | case PCI_HEADER_TYPE_BRIDGE: | 
|  | 1964 | function = 0x8; | 
|  | 1965 | case PCI_HEADER_TYPE_MULTIBRIDGE: | 
|  | 1966 | /* We assume here that only 1 bus behind the bridge | 
|  | 1967 | TO DO: add functionality for several: | 
|  | 1968 | temp = secondary; | 
|  | 1969 | while (temp < subordinate) { | 
|  | 1970 | ... | 
|  | 1971 | temp++; | 
|  | 1972 | } | 
|  | 1973 | */ | 
|  | 1974 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno); | 
|  | 1975 | bus_sec = find_bus_wprev (sec_busno, NULL, 0); | 
|  | 1976 | /* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */ | 
|  | 1977 | if (!bus_sec) { | 
|  | 1978 | bus_sec = alloc_error_bus (NULL, sec_busno, 1); | 
|  | 1979 | /* the rest will be populated during NVRAM call */ | 
|  | 1980 | return 0; | 
|  | 1981 | } | 
|  | 1982 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &start_io_address); | 
|  | 1983 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &end_io_address); | 
|  | 1984 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, &upper_io_start); | 
|  | 1985 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, &upper_io_end); | 
|  | 1986 | start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8; | 
|  | 1987 | start_address |= (upper_io_start << 16); | 
|  | 1988 | end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8; | 
|  | 1989 | end_address |= (upper_io_end << 16); | 
|  | 1990 |  | 
|  | 1991 | if ((start_address) && (start_address <= end_address)) { | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 1992 | range = kzalloc(sizeof(struct range_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1993 | if (!range) { | 
|  | 1994 | err ("out of system memory\n"); | 
|  | 1995 | return -ENOMEM; | 
|  | 1996 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1997 | range->start = start_address; | 
|  | 1998 | range->end = end_address + 0xfff; | 
|  | 1999 |  | 
|  | 2000 | if (bus_sec->noIORanges > 0) { | 
|  | 2001 | if (!range_exists_already (range, bus_sec, IO)) { | 
|  | 2002 | add_range (IO, range, bus_sec); | 
|  | 2003 | ++bus_sec->noIORanges; | 
|  | 2004 | } else { | 
|  | 2005 | kfree (range); | 
|  | 2006 | range = NULL; | 
|  | 2007 | } | 
|  | 2008 | } else { | 
|  | 2009 | /* 1st IO Range on the bus */ | 
|  | 2010 | range->rangeno = 1; | 
|  | 2011 | bus_sec->rangeIO = range; | 
|  | 2012 | ++bus_sec->noIORanges; | 
|  | 2013 | } | 
|  | 2014 | fix_resources (bus_sec); | 
|  | 2015 |  | 
|  | 2016 | if (ibmphp_find_resource (bus_cur, start_address, &io, IO)) { | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 2017 | io = kzalloc(sizeof(struct resource_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2018 | if (!io) { | 
|  | 2019 | kfree (range); | 
|  | 2020 | err ("out of system memory\n"); | 
|  | 2021 | return -ENOMEM; | 
|  | 2022 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2023 | io->type = IO; | 
|  | 2024 | io->busno = bus_cur->busno; | 
|  | 2025 | io->devfunc = ((device << 3) | (function & 0x7)); | 
|  | 2026 | io->start = start_address; | 
|  | 2027 | io->end = end_address + 0xfff; | 
|  | 2028 | io->len = io->end - io->start + 1; | 
|  | 2029 | ibmphp_add_resource (io); | 
|  | 2030 | } | 
|  | 2031 | } | 
|  | 2032 |  | 
|  | 2033 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address); | 
|  | 2034 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address); | 
|  | 2035 |  | 
|  | 2036 | start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; | 
|  | 2037 | end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; | 
|  | 2038 |  | 
|  | 2039 | if ((start_address) && (start_address <= end_address)) { | 
|  | 2040 |  | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 2041 | range = kzalloc(sizeof(struct range_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2042 | if (!range) { | 
|  | 2043 | err ("out of system memory\n"); | 
|  | 2044 | return -ENOMEM; | 
|  | 2045 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2046 | range->start = start_address; | 
|  | 2047 | range->end = end_address + 0xfffff; | 
|  | 2048 |  | 
|  | 2049 | if (bus_sec->noMemRanges > 0) { | 
|  | 2050 | if (!range_exists_already (range, bus_sec, MEM)) { | 
|  | 2051 | add_range (MEM, range, bus_sec); | 
|  | 2052 | ++bus_sec->noMemRanges; | 
|  | 2053 | } else { | 
|  | 2054 | kfree (range); | 
|  | 2055 | range = NULL; | 
|  | 2056 | } | 
|  | 2057 | } else { | 
|  | 2058 | /* 1st Mem Range on the bus */ | 
|  | 2059 | range->rangeno = 1; | 
|  | 2060 | bus_sec->rangeMem = range; | 
|  | 2061 | ++bus_sec->noMemRanges; | 
|  | 2062 | } | 
|  | 2063 |  | 
|  | 2064 | fix_resources (bus_sec); | 
|  | 2065 |  | 
|  | 2066 | if (ibmphp_find_resource (bus_cur, start_address, &mem, MEM)) { | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 2067 | mem = kzalloc(sizeof(struct resource_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2068 | if (!mem) { | 
|  | 2069 | kfree (range); | 
|  | 2070 | err ("out of system memory\n"); | 
|  | 2071 | return -ENOMEM; | 
|  | 2072 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2073 | mem->type = MEM; | 
|  | 2074 | mem->busno = bus_cur->busno; | 
|  | 2075 | mem->devfunc = ((device << 3) | (function & 0x7)); | 
|  | 2076 | mem->start = start_address; | 
|  | 2077 | mem->end = end_address + 0xfffff; | 
|  | 2078 | mem->len = mem->end - mem->start + 1; | 
|  | 2079 | ibmphp_add_resource (mem); | 
|  | 2080 | } | 
|  | 2081 | } | 
|  | 2082 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &start_mem_address); | 
|  | 2083 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &end_mem_address); | 
|  | 2084 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, &upper_start); | 
|  | 2085 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, &upper_end); | 
|  | 2086 | start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; | 
|  | 2087 | end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; | 
|  | 2088 | #if BITS_PER_LONG == 64 | 
|  | 2089 | start_address |= ((long) upper_start) << 32; | 
|  | 2090 | end_address |= ((long) upper_end) << 32; | 
|  | 2091 | #endif | 
|  | 2092 |  | 
|  | 2093 | if ((start_address) && (start_address <= end_address)) { | 
|  | 2094 |  | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 2095 | range = kzalloc(sizeof(struct range_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2096 | if (!range) { | 
|  | 2097 | err ("out of system memory\n"); | 
|  | 2098 | return -ENOMEM; | 
|  | 2099 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2100 | range->start = start_address; | 
|  | 2101 | range->end = end_address + 0xfffff; | 
|  | 2102 |  | 
|  | 2103 | if (bus_sec->noPFMemRanges > 0) { | 
|  | 2104 | if (!range_exists_already (range, bus_sec, PFMEM)) { | 
|  | 2105 | add_range (PFMEM, range, bus_sec); | 
|  | 2106 | ++bus_sec->noPFMemRanges; | 
|  | 2107 | } else { | 
|  | 2108 | kfree (range); | 
|  | 2109 | range = NULL; | 
|  | 2110 | } | 
|  | 2111 | } else { | 
|  | 2112 | /* 1st PFMem Range on the bus */ | 
|  | 2113 | range->rangeno = 1; | 
|  | 2114 | bus_sec->rangePFMem = range; | 
|  | 2115 | ++bus_sec->noPFMemRanges; | 
|  | 2116 | } | 
|  | 2117 |  | 
|  | 2118 | fix_resources (bus_sec); | 
|  | 2119 | if (ibmphp_find_resource (bus_cur, start_address, &pfmem, PFMEM)) { | 
| Eric Sesterhenn | f5afe80 | 2006-02-28 15:34:49 +0100 | [diff] [blame] | 2120 | pfmem = kzalloc(sizeof(struct resource_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2121 | if (!pfmem) { | 
|  | 2122 | kfree (range); | 
|  | 2123 | err ("out of system memory\n"); | 
|  | 2124 | return -ENOMEM; | 
|  | 2125 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2126 | pfmem->type = PFMEM; | 
|  | 2127 | pfmem->busno = bus_cur->busno; | 
|  | 2128 | pfmem->devfunc = ((device << 3) | (function & 0x7)); | 
|  | 2129 | pfmem->start = start_address; | 
|  | 2130 | pfmem->end = end_address + 0xfffff; | 
|  | 2131 | pfmem->len = pfmem->end - pfmem->start + 1; | 
| Kristen Accardi | dc6712d | 2006-03-14 16:24:47 -0800 | [diff] [blame] | 2132 | pfmem->fromMem = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2133 |  | 
|  | 2134 | ibmphp_add_resource (pfmem); | 
|  | 2135 | } | 
|  | 2136 | } | 
|  | 2137 | break; | 
|  | 2138 | }	/* end of switch */ | 
|  | 2139 | }	/* end if vendor */ | 
|  | 2140 | }	/* end for function */ | 
|  | 2141 | }	/* end for device */ | 
|  | 2142 |  | 
|  | 2143 | bus = &bus_cur; | 
|  | 2144 | return 0; | 
|  | 2145 | } |