in hotplug/ibmphp_res.c [952:1332]
int ibmphp_check_resource(struct resource_node *res, u8 bridge)
{
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;
u8 flag = 0;
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;
bus_cur = find_bus_wprev(res->busno, NULL, 0);
if (!bus_cur) {
/* didn't find a bus, something's wrong!!! */
debug("no bus in the system, either pci_dev's wrong or allocation failed\n");
return -EINVAL;
}
debug("%s - enter\n", __func__);
debug("bus_cur->busno is %d\n", bus_cur->busno);
/* 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:
err("wrong type of resource to check\n");
return -EINVAL;
}
res_prev = NULL;
while (res_cur) {
range = find_range(bus_cur, res_cur);
debug("%s - rangeno = %d\n", __func__, res_cur->rangeno);
if (!range) {
err("no range for the device exists... bailing out...\n");
return -EINVAL;
}
/* found our range */
if (!res_prev) {
/* first time in the loop */
len_tmp = res_cur->start - 1 - range->start;
if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
debug("len_tmp = %x\n", len_tmp);
if ((len_tmp < len_cur) || (len_cur == 0)) {
if ((range->start % tmp_divide) == 0) {
/* just perfect, starting address is divisible by length */
flag = 1;
len_cur = len_tmp;
start_cur = range->start;
} else {
/* Needs adjusting */
tmp_start = range->start;
flag = 0;
while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = 1;
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) {
debug("but we are not here, right?\n");
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 */
len_tmp = range->end - (res_cur->end + 1);
if ((range->end != res_cur->end) && (len_tmp >= res->len)) {
debug("len_tmp = %x\n", len_tmp);
if ((len_tmp < len_cur) || (len_cur == 0)) {
if (((res_cur->end + 1) % tmp_divide) == 0) {
/* just perfect, starting address is divisible by length */
flag = 1;
len_cur = len_tmp;
start_cur = res_cur->end + 1;
} else {
/* Needs adjusting */
tmp_start = res_cur->end + 1;
flag = 0;
while ((len_tmp = range->end - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = 1;
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 */
len_tmp = res_cur->start - 1 - range->start;
if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
if ((len_tmp < len_cur) || (len_cur == 0)) {
if ((range->start % tmp_divide) == 0) {
/* just perfect, starting address is divisible by length */
flag = 1;
len_cur = len_tmp;
start_cur = range->start;
} else {
/* Needs adjusting */
tmp_start = range->start;
flag = 0;
while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = 1;
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 */
len_tmp = res_cur->start - 1 - res_prev->end - 1;
if (len_tmp >= res->len) {
if ((len_tmp < len_cur) || (len_cur == 0)) {
if (((res_prev->end + 1) % tmp_divide) == 0) {
/* just perfect, starting address's divisible by length */
flag = 1;
len_cur = len_tmp;
start_cur = res_prev->end + 1;
} else {
/* Needs adjusting */
tmp_start = res_prev->end + 1;
flag = 0;
while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = 1;
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) {
len_tmp = range->end - range->start;
if (len_tmp >= res->len) {
if ((len_tmp < len_cur) || (len_cur == 0)) {
if ((range->start % tmp_divide) == 0) {
/* just perfect, starting address's divisible by length */
flag = 1;
len_cur = len_tmp;
start_cur = range->start;
} else {
/* Needs adjusting */
tmp_start = range->start;
flag = 0;
while ((len_tmp = range->end - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = 1;
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 */
err("no appropriate range.. bailing out...\n");
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) {
debug("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges);
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) {
len_tmp = range->end - range->start;
if (len_tmp >= res->len) {
if ((len_tmp < len_cur) || (len_cur == 0)) {
if ((range->start % tmp_divide) == 0) {
/* just perfect, starting address's divisible by length */
flag = 1;
len_cur = len_tmp;
start_cur = range->start;
} else {
/* Needs adjusting */
tmp_start = range->start;
flag = 0;
while ((len_tmp = range->end - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = 1;
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 */
err("no appropriate range.. bailing out...\n");
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 */
err("no appropriate range.. bailing out...\n");
return -EINVAL;
}
}
} /* end if (!res_cur) */
return -EINVAL;
}