ibmphp_res.c 57.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 * IBM Hot Plug Controller Driver
 *
 * Written By: Irene Zubarev, IBM Corporation
 *
 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2001,2002 IBM Corp.
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Send feedback to <gregkh@us.ibm.com>
 *
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/init.h>
#include "ibmphp.h"

static int flags = 0;		/* for testing */

B
Bogicevic Sasa 已提交
39 40 41 42 43 44 45
static void update_resources(struct bus_node *bus_cur, int type, int rangeno);
static int once_over(void);
static int remove_ranges(struct bus_node *, struct bus_node *);
static int update_bridge_ranges(struct bus_node **);
static int add_bus_range(int type, struct range_node *, struct bus_node *);
static void fix_resources(struct bus_node *);
static struct bus_node *find_bus_wprev(u8, struct bus_node **, u8);
L
Linus Torvalds 已提交
46 47 48

static LIST_HEAD(gbuses);

B
Bogicevic Sasa 已提交
49
static struct bus_node * __init alloc_error_bus(struct ebda_pci_rsrc *curr, u8 busno, int flag)
L
Linus Torvalds 已提交
50
{
R
Ryan Desfosses 已提交
51
	struct bus_node *newbus;
L
Linus Torvalds 已提交
52 53

	if (!(curr) && !(flag)) {
B
Bogicevic Sasa 已提交
54
		err("NULL pointer passed\n");
L
Linus Torvalds 已提交
55 56 57
		return NULL;
	}

58
	newbus = kzalloc(sizeof(struct bus_node), GFP_KERNEL);
L
Linus Torvalds 已提交
59
	if (!newbus) {
B
Bogicevic Sasa 已提交
60
		err("out of system memory\n");
L
Linus Torvalds 已提交
61 62 63 64 65 66 67
		return NULL;
	}

	if (flag)
		newbus->busno = busno;
	else
		newbus->busno = curr->bus_num;
B
Bogicevic Sasa 已提交
68
	list_add_tail(&newbus->bus_list, &gbuses);
L
Linus Torvalds 已提交
69 70 71
	return newbus;
}

B
Bogicevic Sasa 已提交
72
static struct resource_node * __init alloc_resources(struct ebda_pci_rsrc *curr)
L
Linus Torvalds 已提交
73 74
{
	struct resource_node *rs;
75

L
Linus Torvalds 已提交
76
	if (!curr) {
B
Bogicevic Sasa 已提交
77
		err("NULL passed to allocate\n");
L
Linus Torvalds 已提交
78 79 80
		return NULL;
	}

81
	rs = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
L
Linus Torvalds 已提交
82
	if (!rs) {
B
Bogicevic Sasa 已提交
83
		err("out of system memory\n");
L
Linus Torvalds 已提交
84 85 86 87 88 89 90 91 92 93
		return NULL;
	}
	rs->busno = curr->bus_num;
	rs->devfunc = curr->dev_fun;
	rs->start = curr->start_addr;
	rs->end = curr->end_addr;
	rs->len = curr->end_addr - curr->start_addr + 1;
	return rs;
}

B
Bogicevic Sasa 已提交
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)
L
Linus Torvalds 已提交
95
{
R
Ryan Desfosses 已提交
96
	struct bus_node *newbus;
L
Linus Torvalds 已提交
97 98 99 100
	struct range_node *newrange;
	u8 num_ranges = 0;

	if (first_bus) {
101
		newbus = kzalloc(sizeof(struct bus_node), GFP_KERNEL);
L
Linus Torvalds 已提交
102
		if (!newbus) {
B
Bogicevic Sasa 已提交
103
			err("out of system memory.\n");
L
Linus Torvalds 已提交
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
			return -ENOMEM;
		}
		newbus->busno = curr->bus_num;
	} else {
		newbus = *new_bus;
		switch (flag) {
			case MEM:
				num_ranges = newbus->noMemRanges;
				break;
			case PFMEM:
				num_ranges = newbus->noPFMemRanges;
				break;
			case IO:
				num_ranges = newbus->noIORanges;
				break;
		}
	}

122
	newrange = kzalloc(sizeof(struct range_node), GFP_KERNEL);
L
Linus Torvalds 已提交
123 124
	if (!newrange) {
		if (first_bus)
B
Bogicevic Sasa 已提交
125 126
			kfree(newbus);
		err("out of system memory\n");
L
Linus Torvalds 已提交
127 128 129 130
		return -ENOMEM;
	}
	newrange->start = curr->start_addr;
	newrange->end = curr->end_addr;
131

L
Linus Torvalds 已提交
132 133 134 135
	if (first_bus || (!num_ranges))
		newrange->rangeno = 1;
	else {
		/* need to insert our range */
B
Bogicevic Sasa 已提交
136 137
		add_bus_range(flag, newrange, newbus);
		debug("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
138 139 140 141 142 143 144 145
	}

	switch (flag) {
		case MEM:
			newbus->rangeMem = newrange;
			if (first_bus)
				newbus->noMemRanges = 1;
			else {
B
Bogicevic Sasa 已提交
146
				debug("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
147
				++newbus->noMemRanges;
B
Bogicevic Sasa 已提交
148
				fix_resources(newbus);
L
Linus Torvalds 已提交
149 150 151 152 153 154 155
			}
			break;
		case IO:
			newbus->rangeIO = newrange;
			if (first_bus)
				newbus->noIORanges = 1;
			else {
B
Bogicevic Sasa 已提交
156
				debug("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
157
				++newbus->noIORanges;
B
Bogicevic Sasa 已提交
158
				fix_resources(newbus);
L
Linus Torvalds 已提交
159 160 161 162 163 164
			}
			break;
		case PFMEM:
			newbus->rangePFMem = newrange;
			if (first_bus)
				newbus->noPFMemRanges = 1;
165
			else {
B
Bogicevic Sasa 已提交
166
				debug("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
167
				++newbus->noPFMemRanges;
B
Bogicevic Sasa 已提交
168
				fix_resources(newbus);
L
Linus Torvalds 已提交
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
			}

			break;
	}

	*new_bus = newbus;
	*new_range = newrange;
	return 0;
}


/* Notes:
 * 1. The ranges are ordered.  The buses are not ordered.  (First come)
 *
 * 2. If cannot allocate out of PFMem range, allocate from Mem ranges.  PFmemFromMem
 * are not sorted. (no need since use mem node). To not change the entire code, we
 * also add mem node whenever this case happens so as not to change
B
Bogicevic Sasa 已提交
186
 * ibmphp_check_mem_resource etc(and since it really is taking Mem resource)
L
Linus Torvalds 已提交
187 188 189 190 191 192
 */

/*****************************************************************************
 * This is the Resource Management initialization function.  It will go through
 * the Resource list taken from EBDA and fill in this module's data structures
 *
193
 * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES,
L
Linus Torvalds 已提交
194 195 196 197 198
 * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW
 *
 * Input: ptr to the head of the resource list from EBDA
 * Output: 0, -1 or error codes
 ***************************************************************************/
B
Bogicevic Sasa 已提交
199
int __init ibmphp_rsrc_init(void)
L
Linus Torvalds 已提交
200 201 202 203 204 205 206 207 208 209 210
{
	struct ebda_pci_rsrc *curr;
	struct range_node *newrange = NULL;
	struct bus_node *newbus = NULL;
	struct bus_node *bus_cur;
	struct bus_node *bus_prev;
	struct resource_node *new_io = NULL;
	struct resource_node *new_mem = NULL;
	struct resource_node *new_pfmem = NULL;
	int rc;

211 212
	list_for_each_entry(curr, &ibmphp_ebda_pci_rsrc_head,
			    ebda_pci_rsrc_list) {
L
Linus Torvalds 已提交
213 214
		if (!(curr->rsrc_type & PCIDEVMASK)) {
			/* EBDA still lists non PCI devices, so ignore... */
B
Bogicevic Sasa 已提交
215
			debug("this is not a PCI DEVICE in rsrc_init, please take care\n");
L
Linus Torvalds 已提交
216 217 218 219 220 221 222 223
			// continue;
		}

		/* this is a primary bus resource */
		if (curr->rsrc_type & PRIMARYBUSMASK) {
			/* memory */
			if ((curr->rsrc_type & RESTYPE) == MMASK) {
				/* no bus structure exists in place yet */
B
Bogicevic Sasa 已提交
224
				if (list_empty(&gbuses)) {
225 226
					rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
					if (rc)
L
Linus Torvalds 已提交
227
						return rc;
B
Bogicevic Sasa 已提交
228 229
					list_add_tail(&newbus->bus_list, &gbuses);
					debug("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
230
				} else {
B
Bogicevic Sasa 已提交
231
					bus_cur = find_bus_wprev(curr->bus_num, &bus_prev, 1);
L
Linus Torvalds 已提交
232 233
					/* found our bus */
					if (bus_cur) {
B
Bogicevic Sasa 已提交
234
						rc = alloc_bus_range(&bus_cur, &newrange, curr, MEM, 0);
L
Linus Torvalds 已提交
235 236 237 238
						if (rc)
							return rc;
					} else {
						/* went through all the buses and didn't find ours, need to create a new bus node */
239 240
						rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
						if (rc)
L
Linus Torvalds 已提交
241 242
							return rc;

B
Bogicevic Sasa 已提交
243 244
						list_add_tail(&newbus->bus_list, &gbuses);
						debug("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
245 246 247 248
					}
				}
			} else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
				/* prefetchable memory */
B
Bogicevic Sasa 已提交
249
				if (list_empty(&gbuses)) {
L
Linus Torvalds 已提交
250
					/* no bus structure exists in place yet */
251 252
					rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
					if (rc)
L
Linus Torvalds 已提交
253
						return rc;
B
Bogicevic Sasa 已提交
254 255
					list_add_tail(&newbus->bus_list, &gbuses);
					debug("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
256
				} else {
B
Bogicevic Sasa 已提交
257
					bus_cur = find_bus_wprev(curr->bus_num, &bus_prev, 1);
L
Linus Torvalds 已提交
258 259
					if (bus_cur) {
						/* found our bus */
B
Bogicevic Sasa 已提交
260
						rc = alloc_bus_range(&bus_cur, &newrange, curr, PFMEM, 0);
L
Linus Torvalds 已提交
261 262 263 264
						if (rc)
							return rc;
					} else {
						/* went through all the buses and didn't find ours, need to create a new bus node */
265 266
						rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
						if (rc)
L
Linus Torvalds 已提交
267
							return rc;
B
Bogicevic Sasa 已提交
268 269
						list_add_tail(&newbus->bus_list, &gbuses);
						debug("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
270 271 272 273
					}
				}
			} else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
				/* IO */
B
Bogicevic Sasa 已提交
274
				if (list_empty(&gbuses)) {
L
Linus Torvalds 已提交
275
					/* no bus structure exists in place yet */
276 277
					rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
					if (rc)
L
Linus Torvalds 已提交
278
						return rc;
B
Bogicevic Sasa 已提交
279 280
					list_add_tail(&newbus->bus_list, &gbuses);
					debug("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
281
				} else {
B
Bogicevic Sasa 已提交
282
					bus_cur = find_bus_wprev(curr->bus_num, &bus_prev, 1);
L
Linus Torvalds 已提交
283
					if (bus_cur) {
B
Bogicevic Sasa 已提交
284
						rc = alloc_bus_range(&bus_cur, &newrange, curr, IO, 0);
L
Linus Torvalds 已提交
285 286 287 288
						if (rc)
							return rc;
					} else {
						/* went through all the buses and didn't find ours, need to create a new bus node */
289 290
						rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
						if (rc)
L
Linus Torvalds 已提交
291
							return rc;
B
Bogicevic Sasa 已提交
292 293
						list_add_tail(&newbus->bus_list, &gbuses);
						debug("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
L
Linus Torvalds 已提交
294 295 296 297 298 299 300 301 302 303 304
					}
				}

			} else {
				;	/* type is reserved  WHAT TO DO IN THIS CASE???
					   NOTHING TO DO??? */
			}
		} else {
			/* regular pci device resource */
			if ((curr->rsrc_type & RESTYPE) == MMASK) {
				/* Memory resource */
B
Bogicevic Sasa 已提交
305
				new_mem = alloc_resources(curr);
L
Linus Torvalds 已提交
306 307 308 309 310 311 312 313 314 315
				if (!new_mem)
					return -ENOMEM;
				new_mem->type = MEM;
				/*
				 * if it didn't find the bus, means PCI dev
				 * came b4 the Primary Bus info, so need to
				 * create a bus rangeno becomes a problem...
				 * assign a -1 and then update once the range
				 * actually appears...
				 */
B
Bogicevic Sasa 已提交
316 317
				if (ibmphp_add_resource(new_mem) < 0) {
					newbus = alloc_error_bus(curr, 0, 0);
L
Linus Torvalds 已提交
318 319 320 321 322 323
					if (!newbus)
						return -ENOMEM;
					newbus->firstMem = new_mem;
					++newbus->needMemUpdate;
					new_mem->rangeno = -1;
				}
B
Bogicevic Sasa 已提交
324
				debug("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end);
L
Linus Torvalds 已提交
325 326 327

			} else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
				/* PFMemory resource */
B
Bogicevic Sasa 已提交
328
				new_pfmem = alloc_resources(curr);
L
Linus Torvalds 已提交
329 330 331
				if (!new_pfmem)
					return -ENOMEM;
				new_pfmem->type = PFMEM;
332
				new_pfmem->fromMem = 0;
B
Bogicevic Sasa 已提交
333 334
				if (ibmphp_add_resource(new_pfmem) < 0) {
					newbus = alloc_error_bus(curr, 0, 0);
L
Linus Torvalds 已提交
335 336 337 338 339 340 341
					if (!newbus)
						return -ENOMEM;
					newbus->firstPFMem = new_pfmem;
					++newbus->needPFMemUpdate;
					new_pfmem->rangeno = -1;
				}

B
Bogicevic Sasa 已提交
342
				debug("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end);
L
Linus Torvalds 已提交
343 344
			} else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
				/* IO resource */
B
Bogicevic Sasa 已提交
345
				new_io = alloc_resources(curr);
L
Linus Torvalds 已提交
346 347 348 349 350 351 352 353 354 355 356
				if (!new_io)
					return -ENOMEM;
				new_io->type = IO;

				/*
				 * if it didn't find the bus, means PCI dev
				 * came b4 the Primary Bus info, so need to
				 * create a bus rangeno becomes a problem...
				 * Can assign a -1 and then update once the
				 * range actually appears...
				 */
B
Bogicevic Sasa 已提交
357 358
				if (ibmphp_add_resource(new_io) < 0) {
					newbus = alloc_error_bus(curr, 0, 0);
L
Linus Torvalds 已提交
359 360 361 362 363 364
					if (!newbus)
						return -ENOMEM;
					newbus->firstIO = new_io;
					++newbus->needIOUpdate;
					new_io->rangeno = -1;
				}
B
Bogicevic Sasa 已提交
365
				debug("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end);
L
Linus Torvalds 已提交
366 367 368 369
			}
		}
	}

370
	list_for_each_entry(bus_cur, &gbuses, bus_list) {
L
Linus Torvalds 已提交
371
		/* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */
B
Bogicevic Sasa 已提交
372
		rc = update_bridge_ranges(&bus_cur);
L
Linus Torvalds 已提交
373 374 375
		if (rc)
			return rc;
	}
B
Bogicevic Sasa 已提交
376
	return once_over();	/* This is to align ranges (so no -1) */
L
Linus Torvalds 已提交
377 378 379 380 381 382 383 384
}

/********************************************************************************
 * This function adds a range into a sorted list of ranges per bus for a particular
 * range type, it then calls another routine to update the range numbers on the
 * pci devices' resources for the appropriate resource
 *
 * Input: type of the resource, range to add, current bus
385
 * Output: 0 or -1, bus and range ptrs
L
Linus Torvalds 已提交
386
 ********************************************************************************/
B
Bogicevic Sasa 已提交
387
static int add_bus_range(int type, struct range_node *range, struct bus_node *bus_cur)
L
Linus Torvalds 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
{
	struct range_node *range_cur = NULL;
	struct range_node *range_prev;
	int count = 0, i_init;
	int noRanges = 0;

	switch (type) {
		case MEM:
			range_cur = bus_cur->rangeMem;
			noRanges = bus_cur->noMemRanges;
			break;
		case PFMEM:
			range_cur = bus_cur->rangePFMem;
			noRanges = bus_cur->noPFMemRanges;
			break;
		case IO:
			range_cur = bus_cur->rangeIO;
			noRanges = bus_cur->noIORanges;
			break;
	}

	range_prev = NULL;
	while (range_cur) {
		if (range->start < range_cur->start)
			break;
		range_prev = range_cur;
		range_cur = range_cur->next;
		count = count + 1;
	}
	if (!count) {
		/* our range will go at the beginning of the list */
		switch (type) {
			case MEM:
				bus_cur->rangeMem = range;
				break;
			case PFMEM:
				bus_cur->rangePFMem = range;
				break;
			case IO:
				bus_cur->rangeIO = range;
				break;
		}
		range->next = range_cur;
		range->rangeno = 1;
		i_init = 0;
	} else if (!range_cur) {
		/* our range will go at the end of the list */
		range->next = NULL;
		range_prev->next = range;
		range->rangeno = range_prev->rangeno + 1;
		return 0;
	} else {
		/* the range is in the middle */
		range_prev->next = range;
		range->next = range_cur;
		range->rangeno = range_cur->rangeno;
		i_init = range_prev->rangeno;
	}

	for (count = i_init; count < noRanges; ++count) {
		++range_cur->rangeno;
		range_cur = range_cur->next;
	}

B
Bogicevic Sasa 已提交
452
	update_resources(bus_cur, type, i_init + 1);
L
Linus Torvalds 已提交
453 454 455 456 457
	return 0;
}

/*******************************************************************************
 * 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_bus_range fnc
L
Linus Torvalds 已提交
459 460 461
 *
 * Input: bus, type of the resource, the rangeno starting from which to update
 ******************************************************************************/
B
Bogicevic Sasa 已提交
462
static void update_resources(struct bus_node *bus_cur, int type, int rangeno)
L
Linus Torvalds 已提交
463 464
{
	struct resource_node *res = NULL;
465
	u8 eol = 0;	/* end of list indicator */
L
Linus Torvalds 已提交
466 467 468

	switch (type) {
		case MEM:
469
			if (bus_cur->firstMem)
L
Linus Torvalds 已提交
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
				res = bus_cur->firstMem;
			break;
		case PFMEM:
			if (bus_cur->firstPFMem)
				res = bus_cur->firstPFMem;
			break;
		case IO:
			if (bus_cur->firstIO)
				res = bus_cur->firstIO;
			break;
	}

	if (res) {
		while (res) {
			if (res->rangeno == rangeno)
				break;
			if (res->next)
				res = res->next;
			else if (res->nextRange)
				res = res->nextRange;
			else {
491
				eol = 1;
L
Linus Torvalds 已提交
492 493 494 495 496 497 498 499 500 501 502 503 504 505
				break;
			}
		}

		if (!eol) {
			/* found the range */
			while (res) {
				++res->rangeno;
				res = res->next;
			}
		}
	}
}

B
Bogicevic Sasa 已提交
506
static void fix_me(struct resource_node *res, struct bus_node *bus_cur, struct range_node *range)
L
Linus Torvalds 已提交
507
{
B
Bogicevic Sasa 已提交
508
	char *str = "";
L
Linus Torvalds 已提交
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
	switch (res->type) {
		case IO:
			str = "io";
			break;
		case MEM:
			str = "mem";
			break;
		case PFMEM:
			str = "pfmem";
			break;
	}

	while (res) {
		if (res->rangeno == -1) {
			while (range) {
				if ((res->start >= range->start) && (res->end <= range->end)) {
					res->rangeno = range->rangeno;
B
Bogicevic Sasa 已提交
526
					debug("%s->rangeno in fix_resources is %d\n", str, res->rangeno);
L
Linus Torvalds 已提交
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
					switch (res->type) {
						case IO:
							--bus_cur->needIOUpdate;
							break;
						case MEM:
							--bus_cur->needMemUpdate;
							break;
						case PFMEM:
							--bus_cur->needPFMemUpdate;
							break;
					}
					break;
				}
				range = range->next;
			}
		}
		if (res->next)
			res = res->next;
		else
			res = res->nextRange;
	}

}

/*****************************************************************************
 * This routine reassigns the range numbers to the resources that had a -1
 * This case can happen only if upon initialization, resources taken by pci dev
 * appear in EBDA before the resources allocated for that bus, since we don't
 * know the range, we assign -1, and this routine is called after a new range
 * is assigned to see the resources with unknown range belong to the added range
 *
 * Input: current bus
 * Output: none, list of resources for that bus are fixed if can be
 *******************************************************************************/
B
Bogicevic Sasa 已提交
561
static void fix_resources(struct bus_node *bus_cur)
L
Linus Torvalds 已提交
562 563 564 565
{
	struct range_node *range;
	struct resource_node *res;

B
Bogicevic Sasa 已提交
566
	debug("%s - bus_cur->busno = %d\n", __func__, bus_cur->busno);
L
Linus Torvalds 已提交
567 568 569 570

	if (bus_cur->needIOUpdate) {
		res = bus_cur->firstIO;
		range = bus_cur->rangeIO;
B
Bogicevic Sasa 已提交
571
		fix_me(res, bus_cur, range);
L
Linus Torvalds 已提交
572 573 574 575
	}
	if (bus_cur->needMemUpdate) {
		res = bus_cur->firstMem;
		range = bus_cur->rangeMem;
B
Bogicevic Sasa 已提交
576
		fix_me(res, bus_cur, range);
L
Linus Torvalds 已提交
577 578 579 580
	}
	if (bus_cur->needPFMemUpdate) {
		res = bus_cur->firstPFMem;
		range = bus_cur->rangePFMem;
B
Bogicevic Sasa 已提交
581
		fix_me(res, bus_cur, range);
L
Linus Torvalds 已提交
582 583 584 585
	}
}

/*******************************************************************************
586
 * This routine adds a resource to the list of resources to the appropriate bus
L
Linus Torvalds 已提交
587 588 589 590 591 592 593
 * based on their resource type and sorted by their starting addresses.  It assigns
 * the ptrs to next and nextRange if needed.
 *
 * Input: resource ptr
 * Output: ptrs assigned (to the node)
 * 0 or -1
 *******************************************************************************/
B
Bogicevic Sasa 已提交
594
int ibmphp_add_resource(struct resource_node *res)
L
Linus Torvalds 已提交
595 596 597 598 599 600 601
{
	struct resource_node *res_cur;
	struct resource_node *res_prev;
	struct bus_node *bus_cur;
	struct range_node *range_cur = NULL;
	struct resource_node *res_start = NULL;

B
Bogicevic Sasa 已提交
602
	debug("%s - enter\n", __func__);
L
Linus Torvalds 已提交
603 604

	if (!res) {
B
Bogicevic Sasa 已提交
605
		err("NULL passed to add\n");
L
Linus Torvalds 已提交
606 607
		return -ENODEV;
	}
608

B
Bogicevic Sasa 已提交
609
	bus_cur = find_bus_wprev(res->busno, NULL, 0);
610

L
Linus Torvalds 已提交
611
	if (!bus_cur) {
612
		/* didn't find a bus, something's wrong!!! */
B
Bogicevic Sasa 已提交
613
		debug("no bus in the system, either pci_dev's wrong or allocation failed\n");
L
Linus Torvalds 已提交
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
		return -ENODEV;
	}

	/* Normal case */
	switch (res->type) {
		case IO:
			range_cur = bus_cur->rangeIO;
			res_start = bus_cur->firstIO;
			break;
		case MEM:
			range_cur = bus_cur->rangeMem;
			res_start = bus_cur->firstMem;
			break;
		case PFMEM:
			range_cur = bus_cur->rangePFMem;
			res_start = bus_cur->firstPFMem;
			break;
		default:
B
Bogicevic Sasa 已提交
632
			err("cannot read the type of the resource to add... problem\n");
L
Linus Torvalds 已提交
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
			return -EINVAL;
	}
	while (range_cur) {
		if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) {
			res->rangeno = range_cur->rangeno;
			break;
		}
		range_cur = range_cur->next;
	}

	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	 * this is again the case of rangeno = -1
	 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	 */

	if (!range_cur) {
		switch (res->type) {
			case IO:
651
				++bus_cur->needIOUpdate;
L
Linus Torvalds 已提交
652 653 654 655 656 657 658 659 660 661
				break;
			case MEM:
				++bus_cur->needMemUpdate;
				break;
			case PFMEM:
				++bus_cur->needPFMemUpdate;
				break;
		}
		res->rangeno = -1;
	}
662

B
Bogicevic Sasa 已提交
663
	debug("The range is %d\n", res->rangeno);
L
Linus Torvalds 已提交
664 665 666 667
	if (!res_start) {
		/* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */
		switch (res->type) {
			case IO:
668
				bus_cur->firstIO = res;
L
Linus Torvalds 已提交
669 670 671 672 673 674 675
				break;
			case MEM:
				bus_cur->firstMem = res;
				break;
			case PFMEM:
				bus_cur->firstPFMem = res;
				break;
676
		}
L
Linus Torvalds 已提交
677 678 679 680 681 682
		res->next = NULL;
		res->nextRange = NULL;
	} else {
		res_cur = res_start;
		res_prev = NULL;

B
Bogicevic Sasa 已提交
683
		debug("res_cur->rangeno is %d\n", res_cur->rangeno);
L
Linus Torvalds 已提交
684 685 686 687 688 689 690 691 692 693 694 695 696

		while (res_cur) {
			if (res_cur->rangeno >= res->rangeno)
				break;
			res_prev = res_cur;
			if (res_cur->next)
				res_cur = res_cur->next;
			else
				res_cur = res_cur->nextRange;
		}

		if (!res_cur) {
			/* at the end of the resource list */
B
Bogicevic Sasa 已提交
697
			debug("i should be here, [%x - %x]\n", res->start, res->end);
L
Linus Torvalds 已提交
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
			res_prev->nextRange = res;
			res->next = NULL;
			res->nextRange = NULL;
		} else if (res_cur->rangeno == res->rangeno) {
			/* in the same range */
			while (res_cur) {
				if (res->start < res_cur->start)
					break;
				res_prev = res_cur;
				res_cur = res_cur->next;
			}
			if (!res_cur) {
				/* the last resource in this range */
				res_prev->next = res;
				res->next = NULL;
				res->nextRange = res_prev->nextRange;
				res_prev->nextRange = NULL;
			} else if (res->start < res_cur->start) {
				/* at the beginning or middle of the range */
				if (!res_prev)	{
					switch (res->type) {
						case IO:
							bus_cur->firstIO = res;
							break;
						case MEM:
							bus_cur->firstMem = res;
							break;
						case PFMEM:
							bus_cur->firstPFMem = res;
							break;
					}
				} else if (res_prev->rangeno == res_cur->rangeno)
					res_prev->next = res;
				else
					res_prev->nextRange = res;

				res->next = res_cur;
				res->nextRange = NULL;
			}
		} else {
			/* this is the case where it is 1st occurrence of the range */
			if (!res_prev) {
				/* at the beginning of the resource list */
				res->next = NULL;
				switch (res->type) {
					case IO:
						res->nextRange = bus_cur->firstIO;
						bus_cur->firstIO = res;
						break;
					case MEM:
						res->nextRange = bus_cur->firstMem;
						bus_cur->firstMem = res;
						break;
					case PFMEM:
						res->nextRange = bus_cur->firstPFMem;
						bus_cur->firstPFMem = res;
						break;
				}
			} else if (res_cur->rangeno > res->rangeno) {
				/* in the middle of the resource list */
				res_prev->nextRange = res;
				res->next = NULL;
				res->nextRange = res_cur;
			}
		}
	}

B
Bogicevic Sasa 已提交
765
	debug("%s - exit\n", __func__);
L
Linus Torvalds 已提交
766 767 768 769 770 771 772
	return 0;
}

/****************************************************************************
 * This routine will remove the resource from the list of resources
 *
 * Input: io, mem, and/or pfmem resource to be deleted
773
 * Output: modified resource list
L
Linus Torvalds 已提交
774 775
 *        0 or error code
 ****************************************************************************/
B
Bogicevic Sasa 已提交
776
int ibmphp_remove_resource(struct resource_node *res)
L
Linus Torvalds 已提交
777 778 779 780 781
{
	struct bus_node *bus_cur;
	struct resource_node *res_cur = NULL;
	struct resource_node *res_prev;
	struct resource_node *mem_cur;
B
Bogicevic Sasa 已提交
782
	char *type = "";
L
Linus Torvalds 已提交
783 784

	if (!res)  {
B
Bogicevic Sasa 已提交
785
		err("resource to remove is NULL\n");
L
Linus Torvalds 已提交
786 787 788
		return -ENODEV;
	}

B
Bogicevic Sasa 已提交
789
	bus_cur = find_bus_wprev(res->busno, NULL, 0);
L
Linus Torvalds 已提交
790 791

	if (!bus_cur) {
B
Bogicevic Sasa 已提交
792
		err("cannot find corresponding bus of the io resource to remove  bailing out...\n");
L
Linus Torvalds 已提交
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
		return -ENODEV;
	}

	switch (res->type) {
		case IO:
			res_cur = bus_cur->firstIO;
			type = "io";
			break;
		case MEM:
			res_cur = bus_cur->firstMem;
			type = "mem";
			break;
		case PFMEM:
			res_cur = bus_cur->firstPFMem;
			type = "pfmem";
			break;
		default:
B
Bogicevic Sasa 已提交
810
			err("unknown type for resource to remove\n");
L
Linus Torvalds 已提交
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
			return -EINVAL;
	}
	res_prev = NULL;

	while (res_cur) {
		if ((res_cur->start == res->start) && (res_cur->end == res->end))
			break;
		res_prev = res_cur;
		if (res_cur->next)
			res_cur = res_cur->next;
		else
			res_cur = res_cur->nextRange;
	}

	if (!res_cur) {
		if (res->type == PFMEM) {
827
			/*
L
Linus Torvalds 已提交
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
			 * case where pfmem might be in the PFMemFromMem list
			 * so will also need to remove the corresponding mem
			 * entry
			 */
			res_cur = bus_cur->firstPFMemFromMem;
			res_prev = NULL;

			while (res_cur) {
				if ((res_cur->start == res->start) && (res_cur->end == res->end)) {
					mem_cur = bus_cur->firstMem;
					while (mem_cur) {
						if ((mem_cur->start == res_cur->start)
						    && (mem_cur->end == res_cur->end))
							break;
						if (mem_cur->next)
							mem_cur = mem_cur->next;
						else
							mem_cur = mem_cur->nextRange;
					}
					if (!mem_cur) {
B
Bogicevic Sasa 已提交
848
						err("cannot find corresponding mem node for pfmem...\n");
L
Linus Torvalds 已提交
849 850 851
						return -EINVAL;
					}

B
Bogicevic Sasa 已提交
852
					ibmphp_remove_resource(mem_cur);
L
Linus Torvalds 已提交
853 854 855 856
					if (!res_prev)
						bus_cur->firstPFMemFromMem = res_cur->next;
					else
						res_prev->next = res_cur->next;
B
Bogicevic Sasa 已提交
857
					kfree(res_cur);
L
Linus Torvalds 已提交
858 859 860 861 862 863 864 865 866
					return 0;
				}
				res_prev = res_cur;
				if (res_cur->next)
					res_cur = res_cur->next;
				else
					res_cur = res_cur->nextRange;
			}
			if (!res_cur) {
B
Bogicevic Sasa 已提交
867
				err("cannot find pfmem to delete...\n");
L
Linus Torvalds 已提交
868 869 870
				return -EINVAL;
			}
		} else {
B
Bogicevic Sasa 已提交
871
			err("the %s resource is not in the list to be deleted...\n", type);
L
Linus Torvalds 已提交
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
			return -EINVAL;
		}
	}
	if (!res_prev) {
		/* first device to be deleted */
		if (res_cur->next) {
			switch (res->type) {
				case IO:
					bus_cur->firstIO = res_cur->next;
					break;
				case MEM:
					bus_cur->firstMem = res_cur->next;
					break;
				case PFMEM:
					bus_cur->firstPFMem = res_cur->next;
					break;
			}
		} else if (res_cur->nextRange) {
			switch (res->type) {
				case IO:
					bus_cur->firstIO = res_cur->nextRange;
					break;
				case MEM:
					bus_cur->firstMem = res_cur->nextRange;
					break;
				case PFMEM:
					bus_cur->firstPFMem = res_cur->nextRange;
					break;
			}
		} else {
			switch (res->type) {
				case IO:
					bus_cur->firstIO = NULL;
					break;
				case MEM:
					bus_cur->firstMem = NULL;
					break;
				case PFMEM:
					bus_cur->firstPFMem = NULL;
					break;
			}
		}
B
Bogicevic Sasa 已提交
914
		kfree(res_cur);
L
Linus Torvalds 已提交
915 916 917 918 919 920 921 922 923 924 925 926 927 928
		return 0;
	} else {
		if (res_cur->next) {
			if (res_prev->rangeno == res_cur->rangeno)
				res_prev->next = res_cur->next;
			else
				res_prev->nextRange = res_cur->next;
		} else if (res_cur->nextRange) {
			res_prev->next = NULL;
			res_prev->nextRange = res_cur->nextRange;
		} else {
			res_prev->next = NULL;
			res_prev->nextRange = NULL;
		}
B
Bogicevic Sasa 已提交
929
		kfree(res_cur);
L
Linus Torvalds 已提交
930 931 932 933 934 935
		return 0;
	}

	return 0;
}

B
Bogicevic Sasa 已提交
936
static struct range_node *find_range(struct bus_node *bus_cur, struct resource_node *res)
L
Linus Torvalds 已提交
937
{
R
Ryan Desfosses 已提交
938
	struct range_node *range = NULL;
L
Linus Torvalds 已提交
939 940 941 942 943 944 945 946 947 948 949 950

	switch (res->type) {
		case IO:
			range = bus_cur->rangeIO;
			break;
		case MEM:
			range = bus_cur->rangeMem;
			break;
		case PFMEM:
			range = bus_cur->rangePFMem;
			break;
		default:
B
Bogicevic Sasa 已提交
951
			err("cannot read resource type in find_range\n");
L
Linus Torvalds 已提交
952 953 954 955 956 957 958 959 960 961 962
	}

	while (range) {
		if (res->rangeno == range->rangeno)
			break;
		range = range->next;
	}
	return range;
}

/*****************************************************************************
963
 * This routine will check to make sure the io/mem/pfmem->len that the device asked for
L
Linus Torvalds 已提交
964 965 966 967
 * can fit w/i our list of available IO/MEM/PFMEM resources.  If cannot, returns -EINVAL,
 * otherwise, returns 0
 *
 * Input: resource
968
 * Output: the correct start and end address are inputted into the resource node,
L
Linus Torvalds 已提交
969 970
 *        0 or -EINVAL
 *****************************************************************************/
B
Bogicevic Sasa 已提交
971
int ibmphp_check_resource(struct resource_node *res, u8 bridge)
L
Linus Torvalds 已提交
972 973 974 975 976 977 978 979 980
{
	struct bus_node *bus_cur;
	struct range_node *range = NULL;
	struct resource_node *res_prev;
	struct resource_node *res_cur = NULL;
	u32 len_cur = 0, start_cur = 0, len_tmp = 0;
	int noranges = 0;
	u32 tmp_start;		/* this is to make sure start address is divisible by the length needed */
	u32 tmp_divide;
981
	u8 flag = 0;
L
Linus Torvalds 已提交
982 983 984 985 986 987 988 989 990 991 992 993 994

	if (!res)
		return -EINVAL;

	if (bridge) {
		/* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/
		if (res->type == IO)
			tmp_divide = IOBRIDGE;
		else
			tmp_divide = MEMBRIDGE;
	} else
		tmp_divide = res->len;

B
Bogicevic Sasa 已提交
995
	bus_cur = find_bus_wprev(res->busno, NULL, 0);
L
Linus Torvalds 已提交
996 997

	if (!bus_cur) {
998
		/* didn't find a bus, something's wrong!!! */
B
Bogicevic Sasa 已提交
999
		debug("no bus in the system, either pci_dev's wrong or allocation failed\n");
L
Linus Torvalds 已提交
1000 1001 1002
		return -EINVAL;
	}

B
Bogicevic Sasa 已提交
1003 1004
	debug("%s - enter\n", __func__);
	debug("bus_cur->busno is %d\n", bus_cur->busno);
L
Linus Torvalds 已提交
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023

	/* This is a quick fix to not mess up with the code very much.  i.e.,
	 * 2000-2fff, len = 1000, but when we compare, we need it to be fff */
	res->len -= 1;

	switch (res->type) {
		case IO:
			res_cur = bus_cur->firstIO;
			noranges = bus_cur->noIORanges;
			break;
		case MEM:
			res_cur = bus_cur->firstMem;
			noranges = bus_cur->noMemRanges;
			break;
		case PFMEM:
			res_cur = bus_cur->firstPFMem;
			noranges = bus_cur->noPFMemRanges;
			break;
		default:
B
Bogicevic Sasa 已提交
1024
			err("wrong type of resource to check\n");
L
Linus Torvalds 已提交
1025 1026 1027 1028 1029
			return -EINVAL;
	}
	res_prev = NULL;

	while (res_cur) {
B
Bogicevic Sasa 已提交
1030 1031
		range = find_range(bus_cur, res_cur);
		debug("%s - rangeno = %d\n", __func__, res_cur->rangeno);
L
Linus Torvalds 已提交
1032 1033

		if (!range) {
B
Bogicevic Sasa 已提交
1034
			err("no range for the device exists... bailing out...\n");
L
Linus Torvalds 已提交
1035 1036 1037 1038 1039 1040
			return -EINVAL;
		}

		/* found our range */
		if (!res_prev) {
			/* first time in the loop */
1041 1042 1043
			len_tmp = res_cur->start - 1 - range->start;

			if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
B
Bogicevic Sasa 已提交
1044
				debug("len_tmp = %x\n", len_tmp);
L
Linus Torvalds 已提交
1045 1046 1047 1048 1049

				if ((len_tmp < len_cur) || (len_cur == 0)) {

					if ((range->start % tmp_divide) == 0) {
						/* just perfect, starting address is divisible by length */
1050
						flag = 1;
L
Linus Torvalds 已提交
1051 1052 1053 1054 1055
						len_cur = len_tmp;
						start_cur = range->start;
					} else {
						/* Needs adjusting */
						tmp_start = range->start;
1056
						flag = 0;
L
Linus Torvalds 已提交
1057 1058 1059

						while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
							if ((tmp_start % tmp_divide) == 0) {
1060
								flag = 1;
L
Linus Torvalds 已提交
1061 1062 1063 1064 1065 1066 1067 1068 1069
								len_cur = len_tmp;
								start_cur = tmp_start;
								break;
							}
							tmp_start += tmp_divide - tmp_start % tmp_divide;
							if (tmp_start >= res_cur->start - 1)
								break;
						}
					}
1070

L
Linus Torvalds 已提交
1071
					if (flag && len_cur == res->len) {
B
Bogicevic Sasa 已提交
1072
						debug("but we are not here, right?\n");
L
Linus Torvalds 已提交
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
						res->start = start_cur;
						res->len += 1; /* To restore the balance */
						res->end = res->start + res->len - 1;
						return 0;
					}
				}
			}
		}
		if (!res_cur->next) {
			/* last device on the range */
1083 1084 1085
			len_tmp = range->end - (res_cur->end + 1);

			if ((range->end != res_cur->end) && (len_tmp >= res->len)) {
B
Bogicevic Sasa 已提交
1086
				debug("len_tmp = %x\n", len_tmp);
L
Linus Torvalds 已提交
1087 1088 1089 1090
				if ((len_tmp < len_cur) || (len_cur == 0)) {

					if (((res_cur->end + 1) % tmp_divide) == 0) {
						/* just perfect, starting address is divisible by length */
1091
						flag = 1;
L
Linus Torvalds 已提交
1092 1093 1094 1095 1096
						len_cur = len_tmp;
						start_cur = res_cur->end + 1;
					} else {
						/* Needs adjusting */
						tmp_start = res_cur->end + 1;
1097
						flag = 0;
L
Linus Torvalds 已提交
1098 1099 1100

						while ((len_tmp = range->end - tmp_start) >= res->len) {
							if ((tmp_start % tmp_divide) == 0) {
1101
								flag = 1;
L
Linus Torvalds 已提交
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
								len_cur = len_tmp;
								start_cur = tmp_start;
								break;
							}
							tmp_start += tmp_divide - tmp_start % tmp_divide;
							if (tmp_start >= range->end)
								break;
						}
					}
					if (flag && len_cur == res->len) {
						res->start = start_cur;
						res->len += 1; /* To restore the balance */
						res->end = res->start + res->len - 1;
						return 0;
					}
				}
			}
		}

		if (res_prev) {
			if (res_prev->rangeno != res_cur->rangeno) {
				/* 1st device on this range */
1124 1125 1126
				len_tmp = res_cur->start - 1 - range->start;

				if ((res_cur->start != range->start) &&	(len_tmp >= res->len)) {
L
Linus Torvalds 已提交
1127
					if ((len_tmp < len_cur) || (len_cur == 0)) {
1128
						if ((range->start % tmp_divide) == 0) {
L
Linus Torvalds 已提交
1129
							/* just perfect, starting address is divisible by length */
1130
							flag = 1;
L
Linus Torvalds 已提交
1131 1132 1133 1134 1135
							len_cur = len_tmp;
							start_cur = range->start;
						} else {
							/* Needs adjusting */
							tmp_start = range->start;
1136
							flag = 0;
L
Linus Torvalds 已提交
1137 1138 1139

							while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
								if ((tmp_start % tmp_divide) == 0) {
1140
									flag = 1;
L
Linus Torvalds 已提交
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
									len_cur = len_tmp;
									start_cur = tmp_start;
									break;
								}
								tmp_start += tmp_divide - tmp_start % tmp_divide;
								if (tmp_start >= res_cur->start - 1)
									break;
							}
						}

						if (flag && len_cur == res->len) {
							res->start = start_cur;
							res->len += 1; /* To restore the balance */
							res->end = res->start + res->len - 1;
							return 0;
						}
					}
				}
			} else {
				/* in the same range */
1161 1162 1163
				len_tmp = res_cur->start - 1 - res_prev->end - 1;

				if (len_tmp >= res->len) {
L
Linus Torvalds 已提交
1164 1165 1166
					if ((len_tmp < len_cur) || (len_cur == 0)) {
						if (((res_prev->end + 1) % tmp_divide) == 0) {
							/* just perfect, starting address's divisible by length */
1167
							flag = 1;
L
Linus Torvalds 已提交
1168 1169 1170 1171 1172
							len_cur = len_tmp;
							start_cur = res_prev->end + 1;
						} else {
							/* Needs adjusting */
							tmp_start = res_prev->end + 1;
1173
							flag = 0;
L
Linus Torvalds 已提交
1174 1175 1176

							while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
								if ((tmp_start % tmp_divide) == 0) {
1177
									flag = 1;
L
Linus Torvalds 已提交
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
									len_cur = len_tmp;
									start_cur = tmp_start;
									break;
								}
								tmp_start += tmp_divide - tmp_start % tmp_divide;
								if (tmp_start >= res_cur->start - 1)
									break;
							}
						}

						if (flag && len_cur == res->len) {
							res->start = start_cur;
							res->len += 1; /* To restore the balance */
							res->end = res->start + res->len - 1;
							return 0;
						}
					}
				}
			}
		}
		/* end if (res_prev) */
		res_prev = res_cur;
		if (res_cur->next)
			res_cur = res_cur->next;
		else
			res_cur = res_cur->nextRange;
	}	/* end of while */


	if (!res_prev) {
		/* 1st device ever */
		/* need to find appropriate range */
		switch (res->type) {
			case IO:
				range = bus_cur->rangeIO;
				break;
			case MEM:
				range = bus_cur->rangeMem;
				break;
			case PFMEM:
				range = bus_cur->rangePFMem;
				break;
		}
		while (range) {
1222 1223 1224
			len_tmp = range->end - range->start;

			if (len_tmp >= res->len) {
L
Linus Torvalds 已提交
1225 1226 1227
				if ((len_tmp < len_cur) || (len_cur == 0)) {
					if ((range->start % tmp_divide) == 0) {
						/* just perfect, starting address's divisible by length */
1228
						flag = 1;
L
Linus Torvalds 已提交
1229 1230 1231 1232 1233
						len_cur = len_tmp;
						start_cur = range->start;
					} else {
						/* Needs adjusting */
						tmp_start = range->start;
1234
						flag = 0;
L
Linus Torvalds 已提交
1235 1236 1237

						while ((len_tmp = range->end - tmp_start) >= res->len) {
							if ((tmp_start % tmp_divide) == 0) {
1238
								flag = 1;
L
Linus Torvalds 已提交
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
								len_cur = len_tmp;
								start_cur = tmp_start;
								break;
							}
							tmp_start += tmp_divide - tmp_start % tmp_divide;
							if (tmp_start >= range->end)
								break;
						}
					}

					if (flag && len_cur == res->len) {
						res->start = start_cur;
						res->len += 1; /* To restore the balance */
						res->end = res->start + res->len - 1;
						return 0;
					}
				}
			}
			range = range->next;
		}		/* end of while */

		if ((!range) && (len_cur == 0)) {
			/* have gone through the list of devices and ranges and haven't found n.e.thing */
B
Bogicevic Sasa 已提交
1262
			err("no appropriate range.. bailing out...\n");
L
Linus Torvalds 已提交
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272
			return -EINVAL;
		} else if (len_cur) {
			res->start = start_cur;
			res->len += 1; /* To restore the balance */
			res->end = res->start + res->len - 1;
			return 0;
		}
	}

	if (!res_cur) {
B
Bogicevic Sasa 已提交
1273
		debug("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges);
L
Linus Torvalds 已提交
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
		if (res_prev->rangeno < noranges) {
			/* if there're more ranges out there to check */
			switch (res->type) {
				case IO:
					range = bus_cur->rangeIO;
					break;
				case MEM:
					range = bus_cur->rangeMem;
					break;
				case PFMEM:
					range = bus_cur->rangePFMem;
					break;
			}
			while (range) {
1288 1289 1290
				len_tmp = range->end - range->start;

				if (len_tmp >= res->len) {
L
Linus Torvalds 已提交
1291 1292 1293
					if ((len_tmp < len_cur) || (len_cur == 0)) {
						if ((range->start % tmp_divide) == 0) {
							/* just perfect, starting address's divisible by length */
1294
							flag = 1;
L
Linus Torvalds 已提交
1295 1296 1297 1298 1299
							len_cur = len_tmp;
							start_cur = range->start;
						} else {
							/* Needs adjusting */
							tmp_start = range->start;
1300
							flag = 0;
L
Linus Torvalds 已提交
1301 1302 1303

							while ((len_tmp = range->end - tmp_start) >= res->len) {
								if ((tmp_start % tmp_divide) == 0) {
1304
									flag = 1;
L
Linus Torvalds 已提交
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327
									len_cur = len_tmp;
									start_cur = tmp_start;
									break;
								}
								tmp_start += tmp_divide - tmp_start % tmp_divide;
								if (tmp_start >= range->end)
									break;
							}
						}

						if (flag && len_cur == res->len) {
							res->start = start_cur;
							res->len += 1; /* To restore the balance */
							res->end = res->start + res->len - 1;
							return 0;
						}
					}
				}
				range = range->next;
			}	/* end of while */

			if ((!range) && (len_cur == 0)) {
				/* have gone through the list of devices and ranges and haven't found n.e.thing */
B
Bogicevic Sasa 已提交
1328
				err("no appropriate range.. bailing out...\n");
L
Linus Torvalds 已提交
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
				return -EINVAL;
			} else if (len_cur) {
				res->start = start_cur;
				res->len += 1; /* To restore the balance */
				res->end = res->start + res->len - 1;
				return 0;
			}
		} else {
			/* no more ranges to check on */
			if (len_cur) {
				res->start = start_cur;
				res->len += 1; /* To restore the balance */
				res->end = res->start + res->len - 1;
				return 0;
			} else {
				/* have gone through the list of devices and haven't found n.e.thing */
B
Bogicevic Sasa 已提交
1345
				err("no appropriate range.. bailing out...\n");
L
Linus Torvalds 已提交
1346 1347 1348
				return -EINVAL;
			}
		}
1349
	}	/* end if (!res_cur) */
L
Linus Torvalds 已提交
1350 1351 1352 1353 1354 1355 1356
	return -EINVAL;
}

/********************************************************************************
 * This routine is called from remove_card if the card contained PPB.
 * It will remove all the resources on the bus as well as the bus itself
 * Input: Bus
1357
 * Output: 0, -ENODEV
L
Linus Torvalds 已提交
1358
 ********************************************************************************/
B
Bogicevic Sasa 已提交
1359
int ibmphp_remove_bus(struct bus_node *bus, u8 parent_busno)
L
Linus Torvalds 已提交
1360 1361 1362 1363 1364 1365
{
	struct resource_node *res_cur;
	struct resource_node *res_tmp;
	struct bus_node *prev_bus;
	int rc;

B
Bogicevic Sasa 已提交
1366
	prev_bus = find_bus_wprev(parent_busno, NULL, 0);
L
Linus Torvalds 已提交
1367 1368

	if (!prev_bus) {
B
Bogicevic Sasa 已提交
1369
		debug("something terribly wrong. Cannot find parent bus to the one to remove\n");
L
Linus Torvalds 已提交
1370 1371 1372
		return -ENODEV;
	}

B
Bogicevic Sasa 已提交
1373
	debug("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno);
L
Linus Torvalds 已提交
1374

B
Bogicevic Sasa 已提交
1375
	rc = remove_ranges(bus, prev_bus);
L
Linus Torvalds 已提交
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386
	if (rc)
		return rc;

	if (bus->firstIO) {
		res_cur = bus->firstIO;
		while (res_cur) {
			res_tmp = res_cur;
			if (res_cur->next)
				res_cur = res_cur->next;
			else
				res_cur = res_cur->nextRange;
B
Bogicevic Sasa 已提交
1387
			kfree(res_tmp);
L
Linus Torvalds 已提交
1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
			res_tmp = NULL;
		}
		bus->firstIO = NULL;
	}
	if (bus->firstMem) {
		res_cur = bus->firstMem;
		while (res_cur) {
			res_tmp = res_cur;
			if (res_cur->next)
				res_cur = res_cur->next;
			else
				res_cur = res_cur->nextRange;
B
Bogicevic Sasa 已提交
1400
			kfree(res_tmp);
L
Linus Torvalds 已提交
1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
			res_tmp = NULL;
		}
		bus->firstMem = NULL;
	}
	if (bus->firstPFMem) {
		res_cur = bus->firstPFMem;
		while (res_cur) {
			res_tmp = res_cur;
			if (res_cur->next)
				res_cur = res_cur->next;
			else
				res_cur = res_cur->nextRange;
B
Bogicevic Sasa 已提交
1413
			kfree(res_tmp);
L
Linus Torvalds 已提交
1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
			res_tmp = NULL;
		}
		bus->firstPFMem = NULL;
	}

	if (bus->firstPFMemFromMem) {
		res_cur = bus->firstPFMemFromMem;
		while (res_cur) {
			res_tmp = res_cur;
			res_cur = res_cur->next;

B
Bogicevic Sasa 已提交
1425
			kfree(res_tmp);
L
Linus Torvalds 已提交
1426 1427 1428 1429 1430
			res_tmp = NULL;
		}
		bus->firstPFMemFromMem = NULL;
	}

B
Bogicevic Sasa 已提交
1431 1432
	list_del(&bus->bus_list);
	kfree(bus);
L
Linus Torvalds 已提交
1433 1434 1435 1436
	return 0;
}

/******************************************************************************
1437
 * This routine deletes the ranges from a given bus, and the entries from the
L
Linus Torvalds 已提交
1438 1439 1440 1441
 * parent's bus in the resources
 * Input: current bus, previous bus
 * Output: 0, -EINVAL
 ******************************************************************************/
B
Bogicevic Sasa 已提交
1442
static int remove_ranges(struct bus_node *bus_cur, struct bus_node *bus_prev)
L
Linus Torvalds 已提交
1443 1444 1445 1446 1447 1448 1449 1450 1451
{
	struct range_node *range_cur;
	struct range_node *range_tmp;
	int i;
	struct resource_node *res = NULL;

	if (bus_cur->noIORanges) {
		range_cur = bus_cur->rangeIO;
		for (i = 0; i < bus_cur->noIORanges; i++) {
B
Bogicevic Sasa 已提交
1452
			if (ibmphp_find_resource(bus_prev, range_cur->start, &res, IO) < 0)
L
Linus Torvalds 已提交
1453
				return -EINVAL;
B
Bogicevic Sasa 已提交
1454
			ibmphp_remove_resource(res);
L
Linus Torvalds 已提交
1455 1456 1457

			range_tmp = range_cur;
			range_cur = range_cur->next;
B
Bogicevic Sasa 已提交
1458
			kfree(range_tmp);
L
Linus Torvalds 已提交
1459 1460 1461 1462 1463 1464 1465
			range_tmp = NULL;
		}
		bus_cur->rangeIO = NULL;
	}
	if (bus_cur->noMemRanges) {
		range_cur = bus_cur->rangeMem;
		for (i = 0; i < bus_cur->noMemRanges; i++) {
B
Bogicevic Sasa 已提交
1466
			if (ibmphp_find_resource(bus_prev, range_cur->start, &res, MEM) < 0)
L
Linus Torvalds 已提交
1467 1468
				return -EINVAL;

B
Bogicevic Sasa 已提交
1469
			ibmphp_remove_resource(res);
L
Linus Torvalds 已提交
1470 1471
			range_tmp = range_cur;
			range_cur = range_cur->next;
B
Bogicevic Sasa 已提交
1472
			kfree(range_tmp);
L
Linus Torvalds 已提交
1473 1474 1475 1476 1477 1478 1479
			range_tmp = NULL;
		}
		bus_cur->rangeMem = NULL;
	}
	if (bus_cur->noPFMemRanges) {
		range_cur = bus_cur->rangePFMem;
		for (i = 0; i < bus_cur->noPFMemRanges; i++) {
B
Bogicevic Sasa 已提交
1480
			if (ibmphp_find_resource(bus_prev, range_cur->start, &res, PFMEM) < 0)
L
Linus Torvalds 已提交
1481 1482
				return -EINVAL;

B
Bogicevic Sasa 已提交
1483
			ibmphp_remove_resource(res);
L
Linus Torvalds 已提交
1484 1485
			range_tmp = range_cur;
			range_cur = range_cur->next;
B
Bogicevic Sasa 已提交
1486
			kfree(range_tmp);
L
Linus Torvalds 已提交
1487 1488 1489 1490 1491 1492 1493 1494
			range_tmp = NULL;
		}
		bus_cur->rangePFMem = NULL;
	}
	return 0;
}

/*
1495
 * find the resource node in the bus
L
Linus Torvalds 已提交
1496 1497
 * Input: Resource needed, start address of the resource, type of resource
 */
B
Bogicevic Sasa 已提交
1498
int ibmphp_find_resource(struct bus_node *bus, u32 start_address, struct resource_node **res, int flag)
L
Linus Torvalds 已提交
1499 1500
{
	struct resource_node *res_cur = NULL;
B
Bogicevic Sasa 已提交
1501
	char *type = "";
L
Linus Torvalds 已提交
1502 1503

	if (!bus) {
B
Bogicevic Sasa 已提交
1504
		err("The bus passed in NULL to find resource\n");
L
Linus Torvalds 已提交
1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521
		return -ENODEV;
	}

	switch (flag) {
		case IO:
			res_cur = bus->firstIO;
			type = "io";
			break;
		case MEM:
			res_cur = bus->firstMem;
			type = "mem";
			break;
		case PFMEM:
			res_cur = bus->firstPFMem;
			type = "pfmem";
			break;
		default:
B
Bogicevic Sasa 已提交
1522
			err("wrong type of flag\n");
L
Linus Torvalds 已提交
1523 1524
			return -EINVAL;
	}
1525

L
Linus Torvalds 已提交
1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547
	while (res_cur) {
		if (res_cur->start == start_address) {
			*res = res_cur;
			break;
		}
		if (res_cur->next)
			res_cur = res_cur->next;
		else
			res_cur = res_cur->nextRange;
	}

	if (!res_cur) {
		if (flag == PFMEM) {
			res_cur = bus->firstPFMemFromMem;
			while (res_cur) {
				if (res_cur->start == start_address) {
					*res = res_cur;
					break;
				}
				res_cur = res_cur->next;
			}
			if (!res_cur) {
B
Bogicevic Sasa 已提交
1548
				debug("SOS...cannot find %s resource in the bus.\n", type);
L
Linus Torvalds 已提交
1549 1550 1551
				return -EINVAL;
			}
		} else {
B
Bogicevic Sasa 已提交
1552
			debug("SOS... cannot find %s resource in the bus.\n", type);
L
Linus Torvalds 已提交
1553 1554 1555 1556 1557
			return -EINVAL;
		}
	}

	if (*res)
B
Bogicevic Sasa 已提交
1558
		debug("*res->start = %x\n", (*res)->start);
L
Linus Torvalds 已提交
1559 1560 1561 1562 1563 1564 1565 1566 1567 1568

	return 0;
}

/***********************************************************************
 * This routine will free the resource structures used by the
 * system.  It is called from cleanup routine for the module
 * Parameters: none
 * Returns: none
 ***********************************************************************/
B
Bogicevic Sasa 已提交
1569
void ibmphp_free_resources(void)
L
Linus Torvalds 已提交
1570
{
1571
	struct bus_node *bus_cur = NULL, *next;
L
Linus Torvalds 已提交
1572 1573 1574 1575 1576 1577 1578 1579
	struct bus_node *bus_tmp;
	struct range_node *range_cur;
	struct range_node *range_tmp;
	struct resource_node *res_cur;
	struct resource_node *res_tmp;
	int i = 0;
	flags = 1;

1580
	list_for_each_entry_safe(bus_cur, next, &gbuses, bus_list) {
L
Linus Torvalds 已提交
1581 1582 1583 1584 1585 1586 1587
		if (bus_cur->noIORanges) {
			range_cur = bus_cur->rangeIO;
			for (i = 0; i < bus_cur->noIORanges; i++) {
				if (!range_cur)
					break;
				range_tmp = range_cur;
				range_cur = range_cur->next;
B
Bogicevic Sasa 已提交
1588
				kfree(range_tmp);
L
Linus Torvalds 已提交
1589 1590 1591 1592 1593 1594 1595 1596 1597 1598
				range_tmp = NULL;
			}
		}
		if (bus_cur->noMemRanges) {
			range_cur = bus_cur->rangeMem;
			for (i = 0; i < bus_cur->noMemRanges; i++) {
				if (!range_cur)
					break;
				range_tmp = range_cur;
				range_cur = range_cur->next;
B
Bogicevic Sasa 已提交
1599
				kfree(range_tmp);
L
Linus Torvalds 已提交
1600 1601 1602 1603 1604 1605 1606 1607 1608 1609
				range_tmp = NULL;
			}
		}
		if (bus_cur->noPFMemRanges) {
			range_cur = bus_cur->rangePFMem;
			for (i = 0; i < bus_cur->noPFMemRanges; i++) {
				if (!range_cur)
					break;
				range_tmp = range_cur;
				range_cur = range_cur->next;
B
Bogicevic Sasa 已提交
1610
				kfree(range_tmp);
L
Linus Torvalds 已提交
1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
				range_tmp = NULL;
			}
		}

		if (bus_cur->firstIO) {
			res_cur = bus_cur->firstIO;
			while (res_cur) {
				res_tmp = res_cur;
				if (res_cur->next)
					res_cur = res_cur->next;
				else
					res_cur = res_cur->nextRange;
B
Bogicevic Sasa 已提交
1623
				kfree(res_tmp);
L
Linus Torvalds 已提交
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
				res_tmp = NULL;
			}
			bus_cur->firstIO = NULL;
		}
		if (bus_cur->firstMem) {
			res_cur = bus_cur->firstMem;
			while (res_cur) {
				res_tmp = res_cur;
				if (res_cur->next)
					res_cur = res_cur->next;
				else
					res_cur = res_cur->nextRange;
B
Bogicevic Sasa 已提交
1636
				kfree(res_tmp);
L
Linus Torvalds 已提交
1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648
				res_tmp = NULL;
			}
			bus_cur->firstMem = NULL;
		}
		if (bus_cur->firstPFMem) {
			res_cur = bus_cur->firstPFMem;
			while (res_cur) {
				res_tmp = res_cur;
				if (res_cur->next)
					res_cur = res_cur->next;
				else
					res_cur = res_cur->nextRange;
B
Bogicevic Sasa 已提交
1649
				kfree(res_tmp);
L
Linus Torvalds 已提交
1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660
				res_tmp = NULL;
			}
			bus_cur->firstPFMem = NULL;
		}

		if (bus_cur->firstPFMemFromMem) {
			res_cur = bus_cur->firstPFMemFromMem;
			while (res_cur) {
				res_tmp = res_cur;
				res_cur = res_cur->next;

B
Bogicevic Sasa 已提交
1661
				kfree(res_tmp);
L
Linus Torvalds 已提交
1662 1663 1664 1665 1666 1667
				res_tmp = NULL;
			}
			bus_cur->firstPFMemFromMem = NULL;
		}

		bus_tmp = bus_cur;
B
Bogicevic Sasa 已提交
1668 1669
		list_del(&bus_cur->bus_list);
		kfree(bus_tmp);
L
Linus Torvalds 已提交
1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681
		bus_tmp = NULL;
	}
}

/*********************************************************************************
 * This function will go over the PFmem resources to check if the EBDA allocated
 * pfmem out of memory buckets of the bus.  If so, it will change the range numbers
 * and a flag to indicate that this resource is out of memory. It will also move the
 * Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create
 * a new Mem node
 * This routine is called right after initialization
 *******************************************************************************/
B
Bogicevic Sasa 已提交
1682
static int __init once_over(void)
L
Linus Torvalds 已提交
1683 1684 1685 1686 1687 1688
{
	struct resource_node *pfmem_cur;
	struct resource_node *pfmem_prev;
	struct resource_node *mem;
	struct bus_node *bus_cur;

1689
	list_for_each_entry(bus_cur, &gbuses, bus_list) {
L
Linus Torvalds 已提交
1690 1691
		if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) {
			for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) {
1692
				pfmem_cur->fromMem = 1;
L
Linus Torvalds 已提交
1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708
				if (pfmem_prev)
					pfmem_prev->next = pfmem_cur->next;
				else
					bus_cur->firstPFMem = pfmem_cur->next;

				if (!bus_cur->firstPFMemFromMem)
					pfmem_cur->next = NULL;
				else
					/* we don't need to sort PFMemFromMem since we're using mem node for
					   all the real work anyways, so just insert at the beginning of the
					   list
					 */
					pfmem_cur->next = bus_cur->firstPFMemFromMem;

				bus_cur->firstPFMemFromMem = pfmem_cur;

1709
				mem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
L
Linus Torvalds 已提交
1710
				if (!mem) {
B
Bogicevic Sasa 已提交
1711
					err("out of system memory\n");
L
Linus Torvalds 已提交
1712 1713 1714 1715 1716 1717 1718 1719
					return -ENOMEM;
				}
				mem->type = MEM;
				mem->busno = pfmem_cur->busno;
				mem->devfunc = pfmem_cur->devfunc;
				mem->start = pfmem_cur->start;
				mem->end = pfmem_cur->end;
				mem->len = pfmem_cur->len;
B
Bogicevic Sasa 已提交
1720 1721
				if (ibmphp_add_resource(mem) < 0)
					err("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n");
L
Linus Torvalds 已提交
1722 1723 1724 1725
				pfmem_cur->rangeno = mem->rangeno;
			}	/* end for pfmem */
		}	/* end if */
	}	/* end list_for_each bus */
1726
	return 0;
L
Linus Torvalds 已提交
1727 1728
}

B
Bogicevic Sasa 已提交
1729
int ibmphp_add_pfmem_from_mem(struct resource_node *pfmem)
L
Linus Torvalds 已提交
1730
{
B
Bogicevic Sasa 已提交
1731
	struct bus_node *bus_cur = find_bus_wprev(pfmem->busno, NULL, 0);
L
Linus Torvalds 已提交
1732 1733

	if (!bus_cur) {
B
Bogicevic Sasa 已提交
1734
		err("cannot find bus of pfmem to add...\n");
L
Linus Torvalds 已提交
1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753
		return -ENODEV;
	}

	if (bus_cur->firstPFMemFromMem)
		pfmem->next = bus_cur->firstPFMemFromMem;
	else
		pfmem->next = NULL;

	bus_cur->firstPFMemFromMem = pfmem;

	return 0;
}

/* This routine just goes through the buses to see if the bus already exists.
 * It is called from ibmphp_find_sec_number, to find out a secondary bus number for
 * bridged cards
 * Parameters: bus_number
 * Returns: Bus pointer or NULL
 */
B
Bogicevic Sasa 已提交
1754
struct bus_node *ibmphp_find_res_bus(u8 bus_number)
L
Linus Torvalds 已提交
1755
{
B
Bogicevic Sasa 已提交
1756
	return find_bus_wprev(bus_number, NULL, 0);
L
Linus Torvalds 已提交
1757 1758
}

B
Bogicevic Sasa 已提交
1759
static struct bus_node *find_bus_wprev(u8 bus_number, struct bus_node **prev, u8 flag)
L
Linus Torvalds 已提交
1760 1761 1762
{
	struct bus_node *bus_cur;

1763
	list_for_each_entry(bus_cur, &gbuses, bus_list) {
1764
		if (flag)
1765
			*prev = list_prev_entry(bus_cur, bus_list);
1766
		if (bus_cur->busno == bus_number)
L
Linus Torvalds 已提交
1767 1768 1769 1770 1771 1772
			return bus_cur;
	}

	return NULL;
}

B
Bogicevic Sasa 已提交
1773
void ibmphp_print_test(void)
L
Linus Torvalds 已提交
1774 1775 1776 1777 1778
{
	int i = 0;
	struct bus_node *bus_cur = NULL;
	struct range_node *range;
	struct resource_node *res;
1779

B
Bogicevic Sasa 已提交
1780
	debug_pci("*****************START**********************\n");
L
Linus Torvalds 已提交
1781 1782

	if ((!list_empty(&gbuses)) && flags) {
B
Bogicevic Sasa 已提交
1783
		err("The GBUSES is not NULL?!?!?!?!?\n");
L
Linus Torvalds 已提交
1784 1785 1786
		return;
	}

1787
	list_for_each_entry(bus_cur, &gbuses, bus_list) {
L
Linus Torvalds 已提交
1788 1789 1790 1791 1792 1793 1794 1795
		debug_pci ("This is bus # %d.  There are\n", bus_cur->busno);
		debug_pci ("IORanges = %d\t", bus_cur->noIORanges);
		debug_pci ("MemRanges = %d\t", bus_cur->noMemRanges);
		debug_pci ("PFMemRanges = %d\n", bus_cur->noPFMemRanges);
		debug_pci ("The IO Ranges are as follows:\n");
		if (bus_cur->rangeIO) {
			range = bus_cur->rangeIO;
			for (i = 0; i < bus_cur->noIORanges; i++) {
B
Bogicevic Sasa 已提交
1796 1797
				debug_pci("rangeno is %d\n", range->rangeno);
				debug_pci("[%x - %x]\n", range->start, range->end);
L
Linus Torvalds 已提交
1798 1799 1800 1801
				range = range->next;
			}
		}

B
Bogicevic Sasa 已提交
1802
		debug_pci("The Mem Ranges are as follows:\n");
L
Linus Torvalds 已提交
1803 1804 1805
		if (bus_cur->rangeMem) {
			range = bus_cur->rangeMem;
			for (i = 0; i < bus_cur->noMemRanges; i++) {
B
Bogicevic Sasa 已提交
1806 1807
				debug_pci("rangeno is %d\n", range->rangeno);
				debug_pci("[%x - %x]\n", range->start, range->end);
L
Linus Torvalds 已提交
1808 1809 1810 1811
				range = range->next;
			}
		}

B
Bogicevic Sasa 已提交
1812
		debug_pci("The PFMem Ranges are as follows:\n");
L
Linus Torvalds 已提交
1813 1814 1815 1816

		if (bus_cur->rangePFMem) {
			range = bus_cur->rangePFMem;
			for (i = 0; i < bus_cur->noPFMemRanges; i++) {
B
Bogicevic Sasa 已提交
1817 1818
				debug_pci("rangeno is %d\n", range->rangeno);
				debug_pci("[%x - %x]\n", range->start, range->end);
L
Linus Torvalds 已提交
1819 1820 1821 1822
				range = range->next;
			}
		}

B
Bogicevic Sasa 已提交
1823
		debug_pci("The resources on this bus are as follows\n");
L
Linus Torvalds 已提交
1824

B
Bogicevic Sasa 已提交
1825
		debug_pci("IO...\n");
L
Linus Torvalds 已提交
1826 1827 1828
		if (bus_cur->firstIO) {
			res = bus_cur->firstIO;
			while (res) {
B
Bogicevic Sasa 已提交
1829 1830 1831
				debug_pci("The range # is %d\n", res->rangeno);
				debug_pci("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
				debug_pci("[%x - %x], len=%x\n", res->start, res->end, res->len);
L
Linus Torvalds 已提交
1832 1833 1834 1835 1836 1837 1838 1839
				if (res->next)
					res = res->next;
				else if (res->nextRange)
					res = res->nextRange;
				else
					break;
			}
		}
B
Bogicevic Sasa 已提交
1840
		debug_pci("Mem...\n");
L
Linus Torvalds 已提交
1841 1842 1843
		if (bus_cur->firstMem) {
			res = bus_cur->firstMem;
			while (res) {
B
Bogicevic Sasa 已提交
1844 1845 1846
				debug_pci("The range # is %d\n", res->rangeno);
				debug_pci("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
				debug_pci("[%x - %x], len=%x\n", res->start, res->end, res->len);
L
Linus Torvalds 已提交
1847 1848 1849 1850 1851 1852 1853 1854
				if (res->next)
					res = res->next;
				else if (res->nextRange)
					res = res->nextRange;
				else
					break;
			}
		}
B
Bogicevic Sasa 已提交
1855
		debug_pci("PFMem...\n");
L
Linus Torvalds 已提交
1856 1857 1858
		if (bus_cur->firstPFMem) {
			res = bus_cur->firstPFMem;
			while (res) {
B
Bogicevic Sasa 已提交
1859 1860 1861
				debug_pci("The range # is %d\n", res->rangeno);
				debug_pci("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
				debug_pci("[%x - %x], len=%x\n", res->start, res->end, res->len);
L
Linus Torvalds 已提交
1862 1863 1864 1865 1866 1867 1868 1869 1870
				if (res->next)
					res = res->next;
				else if (res->nextRange)
					res = res->nextRange;
				else
					break;
			}
		}

B
Bogicevic Sasa 已提交
1871
		debug_pci("PFMemFromMem...\n");
L
Linus Torvalds 已提交
1872 1873 1874
		if (bus_cur->firstPFMemFromMem) {
			res = bus_cur->firstPFMemFromMem;
			while (res) {
B
Bogicevic Sasa 已提交
1875 1876 1877
				debug_pci("The range # is %d\n", res->rangeno);
				debug_pci("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
				debug_pci("[%x - %x], len=%x\n", res->start, res->end, res->len);
L
Linus Torvalds 已提交
1878 1879 1880 1881
				res = res->next;
			}
		}
	}
B
Bogicevic Sasa 已提交
1882
	debug_pci("***********************END***********************\n");
L
Linus Torvalds 已提交
1883 1884
}

B
Bogicevic Sasa 已提交
1885
static int range_exists_already(struct range_node *range, struct bus_node *bus_cur, u8 type)
L
Linus Torvalds 已提交
1886
{
B
Bogicevic Sasa 已提交
1887
	struct range_node *range_cur = NULL;
L
Linus Torvalds 已提交
1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898
	switch (type) {
		case IO:
			range_cur = bus_cur->rangeIO;
			break;
		case MEM:
			range_cur = bus_cur->rangeMem;
			break;
		case PFMEM:
			range_cur = bus_cur->rangePFMem;
			break;
		default:
B
Bogicevic Sasa 已提交
1899
			err("wrong type passed to find out if range already exists\n");
L
Linus Torvalds 已提交
1900 1901 1902 1903 1904 1905 1906 1907
			return -ENODEV;
	}

	while (range_cur) {
		if ((range_cur->start == range->start) && (range_cur->end == range->end))
			return 1;
		range_cur = range_cur->next;
	}
1908

L
Linus Torvalds 已提交
1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921
	return 0;
}

/* This routine will read the windows for any PPB we have and update the
 * range info for the secondary bus, and will also input this info into
 * primary bus, since BIOS doesn't. This is for PPB that are in the system
 * on bootup.  For bridged cards that were added during previous load of the
 * driver, only the ranges and the bus structure are added, the devices are
 * added from NVRAM
 * Input: primary busno
 * Returns: none
 * Note: this function doesn't take into account IO restrictions etc,
 *	 so will only work for bridges with no video/ISA devices behind them It
1922
 *	 also will not work for onboard PPBs that can have more than 1 *bus
L
Linus Torvalds 已提交
1923 1924 1925
 *	 behind them All these are TO DO.
 *	 Also need to add more error checkings... (from fnc returns etc)
 */
B
Bogicevic Sasa 已提交
1926
static int __init update_bridge_ranges(struct bus_node **bus)
L
Linus Torvalds 已提交
1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943
{
	u8 sec_busno, device, function, hdr_type, start_io_address, end_io_address;
	u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address;
	u32 start_address, end_address, upper_start, upper_end;
	struct bus_node *bus_sec;
	struct bus_node *bus_cur;
	struct resource_node *io;
	struct resource_node *mem;
	struct resource_node *pfmem;
	struct range_node *range;
	unsigned int devfn;

	bus_cur = *bus;
	if (!bus_cur)
		return -ENODEV;
	ibmphp_pci_bus->number = bus_cur->busno;

B
Bogicevic Sasa 已提交
1944 1945
	debug("inside %s\n", __func__);
	debug("bus_cur->busno = %x\n", bus_cur->busno);
L
Linus Torvalds 已提交
1946 1947 1948 1949

	for (device = 0; device < 32; device++) {
		for (function = 0x00; function < 0x08; function++) {
			devfn = PCI_DEVFN(device, function);
B
Bogicevic Sasa 已提交
1950
			pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
L
Linus Torvalds 已提交
1951 1952 1953

			if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
				/* found correct device!!! */
B
Bogicevic Sasa 已提交
1954
				pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
L
Linus Torvalds 已提交
1955 1956 1957 1958 1959 1960 1961 1962 1963 1964

				switch (hdr_type) {
					case PCI_HEADER_TYPE_NORMAL:
						function = 0x8;
						break;
					case PCI_HEADER_TYPE_MULTIDEVICE:
						break;
					case PCI_HEADER_TYPE_BRIDGE:
						function = 0x8;
					case PCI_HEADER_TYPE_MULTIBRIDGE:
1965
						/* We assume here that only 1 bus behind the bridge
L
Linus Torvalds 已提交
1966 1967 1968 1969 1970 1971 1972
						   TO DO: add functionality for several:
						   temp = secondary;
						   while (temp < subordinate) {
						   ...
						   temp++;
						   }
						 */
B
Bogicevic Sasa 已提交
1973 1974
						pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno);
						bus_sec = find_bus_wprev(sec_busno, NULL, 0);
L
Linus Torvalds 已提交
1975 1976
						/* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */
						if (!bus_sec) {
B
Bogicevic Sasa 已提交
1977
							bus_sec = alloc_error_bus(NULL, sec_busno, 1);
L
Linus Torvalds 已提交
1978 1979 1980
							/* the rest will be populated during NVRAM call */
							return 0;
						}
B
Bogicevic Sasa 已提交
1981 1982 1983 1984
						pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_IO_BASE, &start_io_address);
						pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &end_io_address);
						pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, &upper_io_start);
						pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, &upper_io_end);
L
Linus Torvalds 已提交
1985 1986 1987 1988 1989 1990
						start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8;
						start_address |= (upper_io_start << 16);
						end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8;
						end_address |= (upper_io_end << 16);

						if ((start_address) && (start_address <= end_address)) {
1991
							range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
L
Linus Torvalds 已提交
1992
							if (!range) {
B
Bogicevic Sasa 已提交
1993
								err("out of system memory\n");
L
Linus Torvalds 已提交
1994 1995 1996 1997 1998 1999
								return -ENOMEM;
							}
							range->start = start_address;
							range->end = end_address + 0xfff;

							if (bus_sec->noIORanges > 0) {
B
Bogicevic Sasa 已提交
2000 2001
								if (!range_exists_already(range, bus_sec, IO)) {
									add_bus_range(IO, range, bus_sec);
L
Linus Torvalds 已提交
2002 2003
									++bus_sec->noIORanges;
								} else {
B
Bogicevic Sasa 已提交
2004
									kfree(range);
L
Linus Torvalds 已提交
2005 2006 2007 2008 2009 2010 2011 2012
									range = NULL;
								}
							} else {
								/* 1st IO Range on the bus */
								range->rangeno = 1;
								bus_sec->rangeIO = range;
								++bus_sec->noIORanges;
							}
B
Bogicevic Sasa 已提交
2013
							fix_resources(bus_sec);
L
Linus Torvalds 已提交
2014

B
Bogicevic Sasa 已提交
2015
							if (ibmphp_find_resource(bus_cur, start_address, &io, IO)) {
2016
								io = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
L
Linus Torvalds 已提交
2017
								if (!io) {
B
Bogicevic Sasa 已提交
2018 2019
									kfree(range);
									err("out of system memory\n");
L
Linus Torvalds 已提交
2020 2021 2022 2023 2024 2025 2026 2027
									return -ENOMEM;
								}
								io->type = IO;
								io->busno = bus_cur->busno;
								io->devfunc = ((device << 3) | (function & 0x7));
								io->start = start_address;
								io->end = end_address + 0xfff;
								io->len = io->end - io->start + 1;
B
Bogicevic Sasa 已提交
2028
								ibmphp_add_resource(io);
L
Linus Torvalds 已提交
2029
							}
2030
						}
L
Linus Torvalds 已提交
2031

B
Bogicevic Sasa 已提交
2032 2033
						pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address);
						pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address);
L
Linus Torvalds 已提交
2034 2035 2036 2037 2038 2039

						start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
						end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;

						if ((start_address) && (start_address <= end_address)) {

2040
							range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
L
Linus Torvalds 已提交
2041
							if (!range) {
B
Bogicevic Sasa 已提交
2042
								err("out of system memory\n");
L
Linus Torvalds 已提交
2043 2044 2045 2046 2047 2048
								return -ENOMEM;
							}
							range->start = start_address;
							range->end = end_address + 0xfffff;

							if (bus_sec->noMemRanges > 0) {
B
Bogicevic Sasa 已提交
2049 2050
								if (!range_exists_already(range, bus_sec, MEM)) {
									add_bus_range(MEM, range, bus_sec);
L
Linus Torvalds 已提交
2051 2052
									++bus_sec->noMemRanges;
								} else {
B
Bogicevic Sasa 已提交
2053
									kfree(range);
L
Linus Torvalds 已提交
2054 2055 2056 2057 2058 2059 2060 2061 2062
									range = NULL;
								}
							} else {
								/* 1st Mem Range on the bus */
								range->rangeno = 1;
								bus_sec->rangeMem = range;
								++bus_sec->noMemRanges;
							}

B
Bogicevic Sasa 已提交
2063
							fix_resources(bus_sec);
L
Linus Torvalds 已提交
2064

B
Bogicevic Sasa 已提交
2065
							if (ibmphp_find_resource(bus_cur, start_address, &mem, MEM)) {
2066
								mem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
L
Linus Torvalds 已提交
2067
								if (!mem) {
B
Bogicevic Sasa 已提交
2068 2069
									kfree(range);
									err("out of system memory\n");
L
Linus Torvalds 已提交
2070 2071 2072 2073 2074 2075 2076 2077
									return -ENOMEM;
								}
								mem->type = MEM;
								mem->busno = bus_cur->busno;
								mem->devfunc = ((device << 3) | (function & 0x7));
								mem->start = start_address;
								mem->end = end_address + 0xfffff;
								mem->len = mem->end - mem->start + 1;
B
Bogicevic Sasa 已提交
2078
								ibmphp_add_resource(mem);
L
Linus Torvalds 已提交
2079 2080
							}
						}
B
Bogicevic Sasa 已提交
2081 2082 2083 2084
						pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &start_mem_address);
						pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &end_mem_address);
						pci_bus_read_config_dword(ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, &upper_start);
						pci_bus_read_config_dword(ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, &upper_end);
L
Linus Torvalds 已提交
2085 2086 2087 2088 2089 2090 2091 2092 2093
						start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
						end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
#if BITS_PER_LONG == 64
						start_address |= ((long) upper_start) << 32;
						end_address |= ((long) upper_end) << 32;
#endif

						if ((start_address) && (start_address <= end_address)) {

2094
							range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
L
Linus Torvalds 已提交
2095
							if (!range) {
B
Bogicevic Sasa 已提交
2096
								err("out of system memory\n");
L
Linus Torvalds 已提交
2097 2098 2099 2100 2101 2102
								return -ENOMEM;
							}
							range->start = start_address;
							range->end = end_address + 0xfffff;

							if (bus_sec->noPFMemRanges > 0) {
B
Bogicevic Sasa 已提交
2103 2104
								if (!range_exists_already(range, bus_sec, PFMEM)) {
									add_bus_range(PFMEM, range, bus_sec);
L
Linus Torvalds 已提交
2105 2106
									++bus_sec->noPFMemRanges;
								} else {
B
Bogicevic Sasa 已提交
2107
									kfree(range);
L
Linus Torvalds 已提交
2108 2109 2110 2111 2112 2113 2114 2115 2116
									range = NULL;
								}
							} else {
								/* 1st PFMem Range on the bus */
								range->rangeno = 1;
								bus_sec->rangePFMem = range;
								++bus_sec->noPFMemRanges;
							}

B
Bogicevic Sasa 已提交
2117 2118
							fix_resources(bus_sec);
							if (ibmphp_find_resource(bus_cur, start_address, &pfmem, PFMEM)) {
2119
								pfmem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
L
Linus Torvalds 已提交
2120
								if (!pfmem) {
B
Bogicevic Sasa 已提交
2121 2122
									kfree(range);
									err("out of system memory\n");
L
Linus Torvalds 已提交
2123 2124 2125 2126 2127 2128 2129 2130
									return -ENOMEM;
								}
								pfmem->type = PFMEM;
								pfmem->busno = bus_cur->busno;
								pfmem->devfunc = ((device << 3) | (function & 0x7));
								pfmem->start = start_address;
								pfmem->end = end_address + 0xfffff;
								pfmem->len = pfmem->end - pfmem->start + 1;
2131
								pfmem->fromMem = 0;
L
Linus Torvalds 已提交
2132

B
Bogicevic Sasa 已提交
2133
								ibmphp_add_resource(pfmem);
L
Linus Torvalds 已提交
2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144
							}
						}
						break;
				}	/* end of switch */
			}	/* end if vendor */
		}	/* end for function */
	}	/* end for device */

	bus = &bus_cur;
	return 0;
}