1736759efSBjorn Helgaas // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * IBM Hot Plug Controller Driver
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Written By: Irene Zubarev, IBM Corporation
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
81da177e4SLinus Torvalds * Copyright (C) 2001,2002 IBM Corp.
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * All rights reserved.
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds * Send feedback to <gregkh@us.ibm.com>
131da177e4SLinus Torvalds *
141da177e4SLinus Torvalds */
151da177e4SLinus Torvalds
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/slab.h>
181da177e4SLinus Torvalds #include <linux/pci.h>
191da177e4SLinus Torvalds #include <linux/list.h>
201da177e4SLinus Torvalds #include <linux/init.h>
211da177e4SLinus Torvalds #include "ibmphp.h"
221da177e4SLinus Torvalds
231da177e4SLinus Torvalds static int flags = 0; /* for testing */
241da177e4SLinus Torvalds
251da177e4SLinus Torvalds static void update_resources(struct bus_node *bus_cur, int type, int rangeno);
261da177e4SLinus Torvalds static int once_over(void);
271da177e4SLinus Torvalds static int remove_ranges(struct bus_node *, struct bus_node *);
281da177e4SLinus Torvalds static int update_bridge_ranges(struct bus_node **);
29c85e4aaeSH. Peter Anvin static int add_bus_range(int type, struct range_node *, struct bus_node *);
301da177e4SLinus Torvalds static void fix_resources(struct bus_node *);
311da177e4SLinus Torvalds static struct bus_node *find_bus_wprev(u8, struct bus_node **, u8);
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds static LIST_HEAD(gbuses);
341da177e4SLinus Torvalds
alloc_error_bus(struct ebda_pci_rsrc * curr,u8 busno,int flag)351da177e4SLinus Torvalds static struct bus_node * __init alloc_error_bus(struct ebda_pci_rsrc *curr, u8 busno, int flag)
361da177e4SLinus Torvalds {
371da177e4SLinus Torvalds struct bus_node *newbus;
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds if (!(curr) && !(flag)) {
401da177e4SLinus Torvalds err("NULL pointer passed\n");
411da177e4SLinus Torvalds return NULL;
421da177e4SLinus Torvalds }
431da177e4SLinus Torvalds
44f5afe806SEric Sesterhenn newbus = kzalloc(sizeof(struct bus_node), GFP_KERNEL);
45c7abb235SMarkus Elfring if (!newbus)
461da177e4SLinus Torvalds return NULL;
471da177e4SLinus Torvalds
481da177e4SLinus Torvalds if (flag)
491da177e4SLinus Torvalds newbus->busno = busno;
501da177e4SLinus Torvalds else
511da177e4SLinus Torvalds newbus->busno = curr->bus_num;
521da177e4SLinus Torvalds list_add_tail(&newbus->bus_list, &gbuses);
531da177e4SLinus Torvalds return newbus;
541da177e4SLinus Torvalds }
551da177e4SLinus Torvalds
alloc_resources(struct ebda_pci_rsrc * curr)561da177e4SLinus Torvalds static struct resource_node * __init alloc_resources(struct ebda_pci_rsrc *curr)
571da177e4SLinus Torvalds {
581da177e4SLinus Torvalds struct resource_node *rs;
591da177e4SLinus Torvalds
601da177e4SLinus Torvalds if (!curr) {
611da177e4SLinus Torvalds err("NULL passed to allocate\n");
621da177e4SLinus Torvalds return NULL;
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds
65f5afe806SEric Sesterhenn rs = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
66c7abb235SMarkus Elfring if (!rs)
671da177e4SLinus Torvalds return NULL;
68c7abb235SMarkus Elfring
691da177e4SLinus Torvalds rs->busno = curr->bus_num;
701da177e4SLinus Torvalds rs->devfunc = curr->dev_fun;
711da177e4SLinus Torvalds rs->start = curr->start_addr;
721da177e4SLinus Torvalds rs->end = curr->end_addr;
731da177e4SLinus Torvalds rs->len = curr->end_addr - curr->start_addr + 1;
741da177e4SLinus Torvalds return rs;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds
alloc_bus_range(struct bus_node ** new_bus,struct range_node ** new_range,struct ebda_pci_rsrc * curr,int flag,u8 first_bus)771da177e4SLinus Torvalds 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)
781da177e4SLinus Torvalds {
791da177e4SLinus Torvalds struct bus_node *newbus;
801da177e4SLinus Torvalds struct range_node *newrange;
811da177e4SLinus Torvalds u8 num_ranges = 0;
821da177e4SLinus Torvalds
831da177e4SLinus Torvalds if (first_bus) {
84f5afe806SEric Sesterhenn newbus = kzalloc(sizeof(struct bus_node), GFP_KERNEL);
85c7abb235SMarkus Elfring if (!newbus)
861da177e4SLinus Torvalds return -ENOMEM;
87c7abb235SMarkus Elfring
881da177e4SLinus Torvalds newbus->busno = curr->bus_num;
891da177e4SLinus Torvalds } else {
901da177e4SLinus Torvalds newbus = *new_bus;
911da177e4SLinus Torvalds switch (flag) {
921da177e4SLinus Torvalds case MEM:
931da177e4SLinus Torvalds num_ranges = newbus->noMemRanges;
941da177e4SLinus Torvalds break;
951da177e4SLinus Torvalds case PFMEM:
961da177e4SLinus Torvalds num_ranges = newbus->noPFMemRanges;
971da177e4SLinus Torvalds break;
981da177e4SLinus Torvalds case IO:
991da177e4SLinus Torvalds num_ranges = newbus->noIORanges;
1001da177e4SLinus Torvalds break;
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds
104f5afe806SEric Sesterhenn newrange = kzalloc(sizeof(struct range_node), GFP_KERNEL);
1051da177e4SLinus Torvalds if (!newrange) {
1061da177e4SLinus Torvalds if (first_bus)
1071da177e4SLinus Torvalds kfree(newbus);
1081da177e4SLinus Torvalds return -ENOMEM;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds newrange->start = curr->start_addr;
1111da177e4SLinus Torvalds newrange->end = curr->end_addr;
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds if (first_bus || (!num_ranges))
1141da177e4SLinus Torvalds newrange->rangeno = 1;
1151da177e4SLinus Torvalds else {
1161da177e4SLinus Torvalds /* need to insert our range */
117c85e4aaeSH. Peter Anvin add_bus_range(flag, newrange, newbus);
1181da177e4SLinus Torvalds debug("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end);
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds
1211da177e4SLinus Torvalds switch (flag) {
1221da177e4SLinus Torvalds case MEM:
1231da177e4SLinus Torvalds newbus->rangeMem = newrange;
1241da177e4SLinus Torvalds if (first_bus)
1251da177e4SLinus Torvalds newbus->noMemRanges = 1;
1261da177e4SLinus Torvalds else {
1271da177e4SLinus Torvalds debug("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
1281da177e4SLinus Torvalds ++newbus->noMemRanges;
1291da177e4SLinus Torvalds fix_resources(newbus);
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds break;
1321da177e4SLinus Torvalds case IO:
1331da177e4SLinus Torvalds newbus->rangeIO = newrange;
1341da177e4SLinus Torvalds if (first_bus)
1351da177e4SLinus Torvalds newbus->noIORanges = 1;
1361da177e4SLinus Torvalds else {
1371da177e4SLinus Torvalds debug("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
1381da177e4SLinus Torvalds ++newbus->noIORanges;
1391da177e4SLinus Torvalds fix_resources(newbus);
1401da177e4SLinus Torvalds }
1411da177e4SLinus Torvalds break;
1421da177e4SLinus Torvalds case PFMEM:
1431da177e4SLinus Torvalds newbus->rangePFMem = newrange;
1441da177e4SLinus Torvalds if (first_bus)
1451da177e4SLinus Torvalds newbus->noPFMemRanges = 1;
1461da177e4SLinus Torvalds else {
1471da177e4SLinus Torvalds debug("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
1481da177e4SLinus Torvalds ++newbus->noPFMemRanges;
1491da177e4SLinus Torvalds fix_resources(newbus);
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds
1521da177e4SLinus Torvalds break;
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds
1551da177e4SLinus Torvalds *new_bus = newbus;
1561da177e4SLinus Torvalds *new_range = newrange;
1571da177e4SLinus Torvalds return 0;
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds
1611da177e4SLinus Torvalds /* Notes:
1621da177e4SLinus Torvalds * 1. The ranges are ordered. The buses are not ordered. (First come)
1631da177e4SLinus Torvalds *
1641da177e4SLinus Torvalds * 2. If cannot allocate out of PFMem range, allocate from Mem ranges. PFmemFromMem
1651da177e4SLinus Torvalds * are not sorted. (no need since use mem node). To not change the entire code, we
1661da177e4SLinus Torvalds * also add mem node whenever this case happens so as not to change
1671da177e4SLinus Torvalds * ibmphp_check_mem_resource etc(and since it really is taking Mem resource)
1681da177e4SLinus Torvalds */
1691da177e4SLinus Torvalds
1701da177e4SLinus Torvalds /*****************************************************************************
1711da177e4SLinus Torvalds * This is the Resource Management initialization function. It will go through
1721da177e4SLinus Torvalds * the Resource list taken from EBDA and fill in this module's data structures
1731da177e4SLinus Torvalds *
1741da177e4SLinus Torvalds * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES,
1751da177e4SLinus Torvalds * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW
1761da177e4SLinus Torvalds *
1771da177e4SLinus Torvalds * Input: ptr to the head of the resource list from EBDA
1781da177e4SLinus Torvalds * Output: 0, -1 or error codes
1791da177e4SLinus Torvalds ***************************************************************************/
ibmphp_rsrc_init(void)1801da177e4SLinus Torvalds int __init ibmphp_rsrc_init(void)
1811da177e4SLinus Torvalds {
1821da177e4SLinus Torvalds struct ebda_pci_rsrc *curr;
1831da177e4SLinus Torvalds struct range_node *newrange = NULL;
1841da177e4SLinus Torvalds struct bus_node *newbus = NULL;
1851da177e4SLinus Torvalds struct bus_node *bus_cur;
1861da177e4SLinus Torvalds struct bus_node *bus_prev;
1871da177e4SLinus Torvalds struct resource_node *new_io = NULL;
1881da177e4SLinus Torvalds struct resource_node *new_mem = NULL;
1891da177e4SLinus Torvalds struct resource_node *new_pfmem = NULL;
1901da177e4SLinus Torvalds int rc;
1911da177e4SLinus Torvalds
1922ac83cccSGeliang Tang list_for_each_entry(curr, &ibmphp_ebda_pci_rsrc_head,
1932ac83cccSGeliang Tang ebda_pci_rsrc_list) {
1941da177e4SLinus Torvalds if (!(curr->rsrc_type & PCIDEVMASK)) {
1951da177e4SLinus Torvalds /* EBDA still lists non PCI devices, so ignore... */
1961da177e4SLinus Torvalds debug("this is not a PCI DEVICE in rsrc_init, please take care\n");
1971da177e4SLinus Torvalds // continue;
1981da177e4SLinus Torvalds }
1991da177e4SLinus Torvalds
2001da177e4SLinus Torvalds /* this is a primary bus resource */
2011da177e4SLinus Torvalds if (curr->rsrc_type & PRIMARYBUSMASK) {
2021da177e4SLinus Torvalds /* memory */
2031da177e4SLinus Torvalds if ((curr->rsrc_type & RESTYPE) == MMASK) {
2041da177e4SLinus Torvalds /* no bus structure exists in place yet */
2051da177e4SLinus Torvalds if (list_empty(&gbuses)) {
20679e50e72SQuentin Lambert rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
20779e50e72SQuentin Lambert if (rc)
2081da177e4SLinus Torvalds return rc;
2091da177e4SLinus Torvalds list_add_tail(&newbus->bus_list, &gbuses);
2101da177e4SLinus Torvalds debug("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
2111da177e4SLinus Torvalds } else {
2121da177e4SLinus Torvalds bus_cur = find_bus_wprev(curr->bus_num, &bus_prev, 1);
2131da177e4SLinus Torvalds /* found our bus */
2141da177e4SLinus Torvalds if (bus_cur) {
2151da177e4SLinus Torvalds rc = alloc_bus_range(&bus_cur, &newrange, curr, MEM, 0);
2161da177e4SLinus Torvalds if (rc)
2171da177e4SLinus Torvalds return rc;
2181da177e4SLinus Torvalds } else {
2191da177e4SLinus Torvalds /* went through all the buses and didn't find ours, need to create a new bus node */
22079e50e72SQuentin Lambert rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
22179e50e72SQuentin Lambert if (rc)
2221da177e4SLinus Torvalds return rc;
2231da177e4SLinus Torvalds
2241da177e4SLinus Torvalds list_add_tail(&newbus->bus_list, &gbuses);
2251da177e4SLinus Torvalds debug("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds } else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
2291da177e4SLinus Torvalds /* prefetchable memory */
2301da177e4SLinus Torvalds if (list_empty(&gbuses)) {
2311da177e4SLinus Torvalds /* no bus structure exists in place yet */
23279e50e72SQuentin Lambert rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
23379e50e72SQuentin Lambert if (rc)
2341da177e4SLinus Torvalds return rc;
2351da177e4SLinus Torvalds list_add_tail(&newbus->bus_list, &gbuses);
2361da177e4SLinus Torvalds debug("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
2371da177e4SLinus Torvalds } else {
2381da177e4SLinus Torvalds bus_cur = find_bus_wprev(curr->bus_num, &bus_prev, 1);
2391da177e4SLinus Torvalds if (bus_cur) {
2401da177e4SLinus Torvalds /* found our bus */
2411da177e4SLinus Torvalds rc = alloc_bus_range(&bus_cur, &newrange, curr, PFMEM, 0);
2421da177e4SLinus Torvalds if (rc)
2431da177e4SLinus Torvalds return rc;
2441da177e4SLinus Torvalds } else {
2451da177e4SLinus Torvalds /* went through all the buses and didn't find ours, need to create a new bus node */
24679e50e72SQuentin Lambert rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
24779e50e72SQuentin Lambert if (rc)
2481da177e4SLinus Torvalds return rc;
2491da177e4SLinus Torvalds list_add_tail(&newbus->bus_list, &gbuses);
2501da177e4SLinus Torvalds debug("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
2511da177e4SLinus Torvalds }
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds } else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
2541da177e4SLinus Torvalds /* IO */
2551da177e4SLinus Torvalds if (list_empty(&gbuses)) {
2561da177e4SLinus Torvalds /* no bus structure exists in place yet */
25779e50e72SQuentin Lambert rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
25879e50e72SQuentin Lambert if (rc)
2591da177e4SLinus Torvalds return rc;
2601da177e4SLinus Torvalds list_add_tail(&newbus->bus_list, &gbuses);
2611da177e4SLinus Torvalds debug("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
2621da177e4SLinus Torvalds } else {
2631da177e4SLinus Torvalds bus_cur = find_bus_wprev(curr->bus_num, &bus_prev, 1);
2641da177e4SLinus Torvalds if (bus_cur) {
2651da177e4SLinus Torvalds rc = alloc_bus_range(&bus_cur, &newrange, curr, IO, 0);
2661da177e4SLinus Torvalds if (rc)
2671da177e4SLinus Torvalds return rc;
2681da177e4SLinus Torvalds } else {
2691da177e4SLinus Torvalds /* went through all the buses and didn't find ours, need to create a new bus node */
27079e50e72SQuentin Lambert rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
27179e50e72SQuentin Lambert if (rc)
2721da177e4SLinus Torvalds return rc;
2731da177e4SLinus Torvalds list_add_tail(&newbus->bus_list, &gbuses);
2741da177e4SLinus Torvalds debug("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
2751da177e4SLinus Torvalds }
2761da177e4SLinus Torvalds }
2771da177e4SLinus Torvalds
2781da177e4SLinus Torvalds } else {
2791da177e4SLinus Torvalds ; /* type is reserved WHAT TO DO IN THIS CASE???
2801da177e4SLinus Torvalds NOTHING TO DO??? */
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds } else {
2831da177e4SLinus Torvalds /* regular pci device resource */
2841da177e4SLinus Torvalds if ((curr->rsrc_type & RESTYPE) == MMASK) {
2851da177e4SLinus Torvalds /* Memory resource */
2861da177e4SLinus Torvalds new_mem = alloc_resources(curr);
2871da177e4SLinus Torvalds if (!new_mem)
2881da177e4SLinus Torvalds return -ENOMEM;
2891da177e4SLinus Torvalds new_mem->type = MEM;
2901da177e4SLinus Torvalds /*
2911da177e4SLinus Torvalds * if it didn't find the bus, means PCI dev
2921da177e4SLinus Torvalds * came b4 the Primary Bus info, so need to
2931da177e4SLinus Torvalds * create a bus rangeno becomes a problem...
2941da177e4SLinus Torvalds * assign a -1 and then update once the range
2951da177e4SLinus Torvalds * actually appears...
2961da177e4SLinus Torvalds */
2971da177e4SLinus Torvalds if (ibmphp_add_resource(new_mem) < 0) {
2981da177e4SLinus Torvalds newbus = alloc_error_bus(curr, 0, 0);
2991da177e4SLinus Torvalds if (!newbus)
3001da177e4SLinus Torvalds return -ENOMEM;
3011da177e4SLinus Torvalds newbus->firstMem = new_mem;
3021da177e4SLinus Torvalds ++newbus->needMemUpdate;
3031da177e4SLinus Torvalds new_mem->rangeno = -1;
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds debug("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end);
3061da177e4SLinus Torvalds
3071da177e4SLinus Torvalds } else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
3081da177e4SLinus Torvalds /* PFMemory resource */
3091da177e4SLinus Torvalds new_pfmem = alloc_resources(curr);
3101da177e4SLinus Torvalds if (!new_pfmem)
3111da177e4SLinus Torvalds return -ENOMEM;
3121da177e4SLinus Torvalds new_pfmem->type = PFMEM;
313dc6712d1SKristen Accardi new_pfmem->fromMem = 0;
3141da177e4SLinus Torvalds if (ibmphp_add_resource(new_pfmem) < 0) {
3151da177e4SLinus Torvalds newbus = alloc_error_bus(curr, 0, 0);
3161da177e4SLinus Torvalds if (!newbus)
3171da177e4SLinus Torvalds return -ENOMEM;
3181da177e4SLinus Torvalds newbus->firstPFMem = new_pfmem;
3191da177e4SLinus Torvalds ++newbus->needPFMemUpdate;
3201da177e4SLinus Torvalds new_pfmem->rangeno = -1;
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds
3231da177e4SLinus Torvalds debug("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end);
3241da177e4SLinus Torvalds } else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
3251da177e4SLinus Torvalds /* IO resource */
3261da177e4SLinus Torvalds new_io = alloc_resources(curr);
3271da177e4SLinus Torvalds if (!new_io)
3281da177e4SLinus Torvalds return -ENOMEM;
3291da177e4SLinus Torvalds new_io->type = IO;
3301da177e4SLinus Torvalds
3311da177e4SLinus Torvalds /*
3321da177e4SLinus Torvalds * if it didn't find the bus, means PCI dev
3331da177e4SLinus Torvalds * came b4 the Primary Bus info, so need to
3341da177e4SLinus Torvalds * create a bus rangeno becomes a problem...
3351da177e4SLinus Torvalds * Can assign a -1 and then update once the
3361da177e4SLinus Torvalds * range actually appears...
3371da177e4SLinus Torvalds */
3381da177e4SLinus Torvalds if (ibmphp_add_resource(new_io) < 0) {
3391da177e4SLinus Torvalds newbus = alloc_error_bus(curr, 0, 0);
3401da177e4SLinus Torvalds if (!newbus)
3411da177e4SLinus Torvalds return -ENOMEM;
3421da177e4SLinus Torvalds newbus->firstIO = new_io;
3431da177e4SLinus Torvalds ++newbus->needIOUpdate;
3441da177e4SLinus Torvalds new_io->rangeno = -1;
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds debug("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end);
3471da177e4SLinus Torvalds }
3481da177e4SLinus Torvalds }
3491da177e4SLinus Torvalds }
3501da177e4SLinus Torvalds
3512ac83cccSGeliang Tang list_for_each_entry(bus_cur, &gbuses, bus_list) {
3521da177e4SLinus Torvalds /* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */
3531da177e4SLinus Torvalds rc = update_bridge_ranges(&bus_cur);
3541da177e4SLinus Torvalds if (rc)
3551da177e4SLinus Torvalds return rc;
3561da177e4SLinus Torvalds }
357754834b9SQuentin Lambert return once_over(); /* This is to align ranges (so no -1) */
3581da177e4SLinus Torvalds }
3591da177e4SLinus Torvalds
3601da177e4SLinus Torvalds /********************************************************************************
3611da177e4SLinus Torvalds * This function adds a range into a sorted list of ranges per bus for a particular
3621da177e4SLinus Torvalds * range type, it then calls another routine to update the range numbers on the
3631da177e4SLinus Torvalds * pci devices' resources for the appropriate resource
3641da177e4SLinus Torvalds *
3651da177e4SLinus Torvalds * Input: type of the resource, range to add, current bus
3661da177e4SLinus Torvalds * Output: 0 or -1, bus and range ptrs
3671da177e4SLinus Torvalds ********************************************************************************/
add_bus_range(int type,struct range_node * range,struct bus_node * bus_cur)368c85e4aaeSH. Peter Anvin static int add_bus_range(int type, struct range_node *range, struct bus_node *bus_cur)
3691da177e4SLinus Torvalds {
3701da177e4SLinus Torvalds struct range_node *range_cur = NULL;
3711da177e4SLinus Torvalds struct range_node *range_prev;
3721da177e4SLinus Torvalds int count = 0, i_init;
3731da177e4SLinus Torvalds int noRanges = 0;
3741da177e4SLinus Torvalds
3751da177e4SLinus Torvalds switch (type) {
3761da177e4SLinus Torvalds case MEM:
3771da177e4SLinus Torvalds range_cur = bus_cur->rangeMem;
3781da177e4SLinus Torvalds noRanges = bus_cur->noMemRanges;
3791da177e4SLinus Torvalds break;
3801da177e4SLinus Torvalds case PFMEM:
3811da177e4SLinus Torvalds range_cur = bus_cur->rangePFMem;
3821da177e4SLinus Torvalds noRanges = bus_cur->noPFMemRanges;
3831da177e4SLinus Torvalds break;
3841da177e4SLinus Torvalds case IO:
3851da177e4SLinus Torvalds range_cur = bus_cur->rangeIO;
3861da177e4SLinus Torvalds noRanges = bus_cur->noIORanges;
3871da177e4SLinus Torvalds break;
3881da177e4SLinus Torvalds }
3891da177e4SLinus Torvalds
3901da177e4SLinus Torvalds range_prev = NULL;
3911da177e4SLinus Torvalds while (range_cur) {
3921da177e4SLinus Torvalds if (range->start < range_cur->start)
3931da177e4SLinus Torvalds break;
3941da177e4SLinus Torvalds range_prev = range_cur;
3951da177e4SLinus Torvalds range_cur = range_cur->next;
3961da177e4SLinus Torvalds count = count + 1;
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds if (!count) {
3991da177e4SLinus Torvalds /* our range will go at the beginning of the list */
4001da177e4SLinus Torvalds switch (type) {
4011da177e4SLinus Torvalds case MEM:
4021da177e4SLinus Torvalds bus_cur->rangeMem = range;
4031da177e4SLinus Torvalds break;
4041da177e4SLinus Torvalds case PFMEM:
4051da177e4SLinus Torvalds bus_cur->rangePFMem = range;
4061da177e4SLinus Torvalds break;
4071da177e4SLinus Torvalds case IO:
4081da177e4SLinus Torvalds bus_cur->rangeIO = range;
4091da177e4SLinus Torvalds break;
4101da177e4SLinus Torvalds }
4111da177e4SLinus Torvalds range->next = range_cur;
4121da177e4SLinus Torvalds range->rangeno = 1;
4131da177e4SLinus Torvalds i_init = 0;
4141da177e4SLinus Torvalds } else if (!range_cur) {
4151da177e4SLinus Torvalds /* our range will go at the end of the list */
4161da177e4SLinus Torvalds range->next = NULL;
4171da177e4SLinus Torvalds range_prev->next = range;
4181da177e4SLinus Torvalds range->rangeno = range_prev->rangeno + 1;
4191da177e4SLinus Torvalds return 0;
4201da177e4SLinus Torvalds } else {
4211da177e4SLinus Torvalds /* the range is in the middle */
4221da177e4SLinus Torvalds range_prev->next = range;
4231da177e4SLinus Torvalds range->next = range_cur;
4241da177e4SLinus Torvalds range->rangeno = range_cur->rangeno;
4251da177e4SLinus Torvalds i_init = range_prev->rangeno;
4261da177e4SLinus Torvalds }
4271da177e4SLinus Torvalds
4281da177e4SLinus Torvalds for (count = i_init; count < noRanges; ++count) {
4291da177e4SLinus Torvalds ++range_cur->rangeno;
4301da177e4SLinus Torvalds range_cur = range_cur->next;
4311da177e4SLinus Torvalds }
4321da177e4SLinus Torvalds
4331da177e4SLinus Torvalds update_resources(bus_cur, type, i_init + 1);
4341da177e4SLinus Torvalds return 0;
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds
4371da177e4SLinus Torvalds /*******************************************************************************
4381da177e4SLinus Torvalds * This routine goes through the list of resources of type 'type' and updates
439c85e4aaeSH. Peter Anvin * the range numbers that they correspond to. It was called from add_bus_range fnc
4401da177e4SLinus Torvalds *
4411da177e4SLinus Torvalds * Input: bus, type of the resource, the rangeno starting from which to update
4421da177e4SLinus Torvalds ******************************************************************************/
update_resources(struct bus_node * bus_cur,int type,int rangeno)4431da177e4SLinus Torvalds static void update_resources(struct bus_node *bus_cur, int type, int rangeno)
4441da177e4SLinus Torvalds {
4451da177e4SLinus Torvalds struct resource_node *res = NULL;
446dc6712d1SKristen Accardi u8 eol = 0; /* end of list indicator */
4471da177e4SLinus Torvalds
4481da177e4SLinus Torvalds switch (type) {
4491da177e4SLinus Torvalds case MEM:
4501da177e4SLinus Torvalds if (bus_cur->firstMem)
4511da177e4SLinus Torvalds res = bus_cur->firstMem;
4521da177e4SLinus Torvalds break;
4531da177e4SLinus Torvalds case PFMEM:
4541da177e4SLinus Torvalds if (bus_cur->firstPFMem)
4551da177e4SLinus Torvalds res = bus_cur->firstPFMem;
4561da177e4SLinus Torvalds break;
4571da177e4SLinus Torvalds case IO:
4581da177e4SLinus Torvalds if (bus_cur->firstIO)
4591da177e4SLinus Torvalds res = bus_cur->firstIO;
4601da177e4SLinus Torvalds break;
4611da177e4SLinus Torvalds }
4621da177e4SLinus Torvalds
4631da177e4SLinus Torvalds if (res) {
4641da177e4SLinus Torvalds while (res) {
4651da177e4SLinus Torvalds if (res->rangeno == rangeno)
4661da177e4SLinus Torvalds break;
4671da177e4SLinus Torvalds if (res->next)
4681da177e4SLinus Torvalds res = res->next;
4691da177e4SLinus Torvalds else if (res->nextRange)
4701da177e4SLinus Torvalds res = res->nextRange;
4711da177e4SLinus Torvalds else {
472dc6712d1SKristen Accardi eol = 1;
4731da177e4SLinus Torvalds break;
4741da177e4SLinus Torvalds }
4751da177e4SLinus Torvalds }
4761da177e4SLinus Torvalds
4771da177e4SLinus Torvalds if (!eol) {
4781da177e4SLinus Torvalds /* found the range */
4791da177e4SLinus Torvalds while (res) {
4801da177e4SLinus Torvalds ++res->rangeno;
4811da177e4SLinus Torvalds res = res->next;
4821da177e4SLinus Torvalds }
4831da177e4SLinus Torvalds }
4841da177e4SLinus Torvalds }
4851da177e4SLinus Torvalds }
4861da177e4SLinus Torvalds
fix_me(struct resource_node * res,struct bus_node * bus_cur,struct range_node * range)4871da177e4SLinus Torvalds static void fix_me(struct resource_node *res, struct bus_node *bus_cur, struct range_node *range)
4881da177e4SLinus Torvalds {
4891da177e4SLinus Torvalds char *str = "";
4901da177e4SLinus Torvalds switch (res->type) {
4911da177e4SLinus Torvalds case IO:
4921da177e4SLinus Torvalds str = "io";
4931da177e4SLinus Torvalds break;
4941da177e4SLinus Torvalds case MEM:
4951da177e4SLinus Torvalds str = "mem";
4961da177e4SLinus Torvalds break;
4971da177e4SLinus Torvalds case PFMEM:
4981da177e4SLinus Torvalds str = "pfmem";
4991da177e4SLinus Torvalds break;
5001da177e4SLinus Torvalds }
5011da177e4SLinus Torvalds
5021da177e4SLinus Torvalds while (res) {
5031da177e4SLinus Torvalds if (res->rangeno == -1) {
5041da177e4SLinus Torvalds while (range) {
5051da177e4SLinus Torvalds if ((res->start >= range->start) && (res->end <= range->end)) {
5061da177e4SLinus Torvalds res->rangeno = range->rangeno;
5071da177e4SLinus Torvalds debug("%s->rangeno in fix_resources is %d\n", str, res->rangeno);
5081da177e4SLinus Torvalds switch (res->type) {
5091da177e4SLinus Torvalds case IO:
5101da177e4SLinus Torvalds --bus_cur->needIOUpdate;
5111da177e4SLinus Torvalds break;
5121da177e4SLinus Torvalds case MEM:
5131da177e4SLinus Torvalds --bus_cur->needMemUpdate;
5141da177e4SLinus Torvalds break;
5151da177e4SLinus Torvalds case PFMEM:
5161da177e4SLinus Torvalds --bus_cur->needPFMemUpdate;
5171da177e4SLinus Torvalds break;
5181da177e4SLinus Torvalds }
5191da177e4SLinus Torvalds break;
5201da177e4SLinus Torvalds }
5211da177e4SLinus Torvalds range = range->next;
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds if (res->next)
5251da177e4SLinus Torvalds res = res->next;
5261da177e4SLinus Torvalds else
5271da177e4SLinus Torvalds res = res->nextRange;
5281da177e4SLinus Torvalds }
5291da177e4SLinus Torvalds
5301da177e4SLinus Torvalds }
5311da177e4SLinus Torvalds
5321da177e4SLinus Torvalds /*****************************************************************************
5331da177e4SLinus Torvalds * This routine reassigns the range numbers to the resources that had a -1
5341da177e4SLinus Torvalds * This case can happen only if upon initialization, resources taken by pci dev
5351da177e4SLinus Torvalds * appear in EBDA before the resources allocated for that bus, since we don't
5361da177e4SLinus Torvalds * know the range, we assign -1, and this routine is called after a new range
5371da177e4SLinus Torvalds * is assigned to see the resources with unknown range belong to the added range
5381da177e4SLinus Torvalds *
5391da177e4SLinus Torvalds * Input: current bus
5401da177e4SLinus Torvalds * Output: none, list of resources for that bus are fixed if can be
5411da177e4SLinus Torvalds *******************************************************************************/
fix_resources(struct bus_node * bus_cur)5421da177e4SLinus Torvalds static void fix_resources(struct bus_node *bus_cur)
5431da177e4SLinus Torvalds {
5441da177e4SLinus Torvalds struct range_node *range;
5451da177e4SLinus Torvalds struct resource_node *res;
5461da177e4SLinus Torvalds
54766bef8c0SHarvey Harrison debug("%s - bus_cur->busno = %d\n", __func__, bus_cur->busno);
5481da177e4SLinus Torvalds
5491da177e4SLinus Torvalds if (bus_cur->needIOUpdate) {
5501da177e4SLinus Torvalds res = bus_cur->firstIO;
5511da177e4SLinus Torvalds range = bus_cur->rangeIO;
5521da177e4SLinus Torvalds fix_me(res, bus_cur, range);
5531da177e4SLinus Torvalds }
5541da177e4SLinus Torvalds if (bus_cur->needMemUpdate) {
5551da177e4SLinus Torvalds res = bus_cur->firstMem;
5561da177e4SLinus Torvalds range = bus_cur->rangeMem;
5571da177e4SLinus Torvalds fix_me(res, bus_cur, range);
5581da177e4SLinus Torvalds }
5591da177e4SLinus Torvalds if (bus_cur->needPFMemUpdate) {
5601da177e4SLinus Torvalds res = bus_cur->firstPFMem;
5611da177e4SLinus Torvalds range = bus_cur->rangePFMem;
5621da177e4SLinus Torvalds fix_me(res, bus_cur, range);
5631da177e4SLinus Torvalds }
5641da177e4SLinus Torvalds }
5651da177e4SLinus Torvalds
5661da177e4SLinus Torvalds /*******************************************************************************
5671da177e4SLinus Torvalds * This routine adds a resource to the list of resources to the appropriate bus
5681da177e4SLinus Torvalds * based on their resource type and sorted by their starting addresses. It assigns
5691da177e4SLinus Torvalds * the ptrs to next and nextRange if needed.
5701da177e4SLinus Torvalds *
5711da177e4SLinus Torvalds * Input: resource ptr
5721da177e4SLinus Torvalds * Output: ptrs assigned (to the node)
5731da177e4SLinus Torvalds * 0 or -1
5741da177e4SLinus Torvalds *******************************************************************************/
ibmphp_add_resource(struct resource_node * res)5751da177e4SLinus Torvalds int ibmphp_add_resource(struct resource_node *res)
5761da177e4SLinus Torvalds {
5771da177e4SLinus Torvalds struct resource_node *res_cur;
5781da177e4SLinus Torvalds struct resource_node *res_prev;
5791da177e4SLinus Torvalds struct bus_node *bus_cur;
5801da177e4SLinus Torvalds struct range_node *range_cur = NULL;
5811da177e4SLinus Torvalds struct resource_node *res_start = NULL;
5821da177e4SLinus Torvalds
58366bef8c0SHarvey Harrison debug("%s - enter\n", __func__);
5841da177e4SLinus Torvalds
5851da177e4SLinus Torvalds if (!res) {
5861da177e4SLinus Torvalds err("NULL passed to add\n");
5871da177e4SLinus Torvalds return -ENODEV;
5881da177e4SLinus Torvalds }
5891da177e4SLinus Torvalds
5901da177e4SLinus Torvalds bus_cur = find_bus_wprev(res->busno, NULL, 0);
5911da177e4SLinus Torvalds
5921da177e4SLinus Torvalds if (!bus_cur) {
593f7625980SBjorn Helgaas /* didn't find a bus, something's wrong!!! */
5941da177e4SLinus Torvalds debug("no bus in the system, either pci_dev's wrong or allocation failed\n");
5951da177e4SLinus Torvalds return -ENODEV;
5961da177e4SLinus Torvalds }
5971da177e4SLinus Torvalds
5981da177e4SLinus Torvalds /* Normal case */
5991da177e4SLinus Torvalds switch (res->type) {
6001da177e4SLinus Torvalds case IO:
6011da177e4SLinus Torvalds range_cur = bus_cur->rangeIO;
6021da177e4SLinus Torvalds res_start = bus_cur->firstIO;
6031da177e4SLinus Torvalds break;
6041da177e4SLinus Torvalds case MEM:
6051da177e4SLinus Torvalds range_cur = bus_cur->rangeMem;
6061da177e4SLinus Torvalds res_start = bus_cur->firstMem;
6071da177e4SLinus Torvalds break;
6081da177e4SLinus Torvalds case PFMEM:
6091da177e4SLinus Torvalds range_cur = bus_cur->rangePFMem;
6101da177e4SLinus Torvalds res_start = bus_cur->firstPFMem;
6111da177e4SLinus Torvalds break;
6121da177e4SLinus Torvalds default:
6131da177e4SLinus Torvalds err("cannot read the type of the resource to add... problem\n");
6141da177e4SLinus Torvalds return -EINVAL;
6151da177e4SLinus Torvalds }
6161da177e4SLinus Torvalds while (range_cur) {
6171da177e4SLinus Torvalds if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) {
6181da177e4SLinus Torvalds res->rangeno = range_cur->rangeno;
6191da177e4SLinus Torvalds break;
6201da177e4SLinus Torvalds }
6211da177e4SLinus Torvalds range_cur = range_cur->next;
6221da177e4SLinus Torvalds }
6231da177e4SLinus Torvalds
6241da177e4SLinus Torvalds /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
6251da177e4SLinus Torvalds * this is again the case of rangeno = -1
6261da177e4SLinus Torvalds * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
6271da177e4SLinus Torvalds */
6281da177e4SLinus Torvalds
6291da177e4SLinus Torvalds if (!range_cur) {
6301da177e4SLinus Torvalds switch (res->type) {
6311da177e4SLinus Torvalds case IO:
6321da177e4SLinus Torvalds ++bus_cur->needIOUpdate;
6331da177e4SLinus Torvalds break;
6341da177e4SLinus Torvalds case MEM:
6351da177e4SLinus Torvalds ++bus_cur->needMemUpdate;
6361da177e4SLinus Torvalds break;
6371da177e4SLinus Torvalds case PFMEM:
6381da177e4SLinus Torvalds ++bus_cur->needPFMemUpdate;
6391da177e4SLinus Torvalds break;
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds res->rangeno = -1;
6421da177e4SLinus Torvalds }
6431da177e4SLinus Torvalds
6441da177e4SLinus Torvalds debug("The range is %d\n", res->rangeno);
6451da177e4SLinus Torvalds if (!res_start) {
6461da177e4SLinus Torvalds /* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */
6471da177e4SLinus Torvalds switch (res->type) {
6481da177e4SLinus Torvalds case IO:
6491da177e4SLinus Torvalds bus_cur->firstIO = res;
6501da177e4SLinus Torvalds break;
6511da177e4SLinus Torvalds case MEM:
6521da177e4SLinus Torvalds bus_cur->firstMem = res;
6531da177e4SLinus Torvalds break;
6541da177e4SLinus Torvalds case PFMEM:
6551da177e4SLinus Torvalds bus_cur->firstPFMem = res;
6561da177e4SLinus Torvalds break;
6571da177e4SLinus Torvalds }
6581da177e4SLinus Torvalds res->next = NULL;
6591da177e4SLinus Torvalds res->nextRange = NULL;
6601da177e4SLinus Torvalds } else {
6611da177e4SLinus Torvalds res_cur = res_start;
6621da177e4SLinus Torvalds res_prev = NULL;
6631da177e4SLinus Torvalds
6641da177e4SLinus Torvalds debug("res_cur->rangeno is %d\n", res_cur->rangeno);
6651da177e4SLinus Torvalds
6661da177e4SLinus Torvalds while (res_cur) {
6671da177e4SLinus Torvalds if (res_cur->rangeno >= res->rangeno)
6681da177e4SLinus Torvalds break;
6691da177e4SLinus Torvalds res_prev = res_cur;
6701da177e4SLinus Torvalds if (res_cur->next)
6711da177e4SLinus Torvalds res_cur = res_cur->next;
6721da177e4SLinus Torvalds else
6731da177e4SLinus Torvalds res_cur = res_cur->nextRange;
6741da177e4SLinus Torvalds }
6751da177e4SLinus Torvalds
6761da177e4SLinus Torvalds if (!res_cur) {
6771da177e4SLinus Torvalds /* at the end of the resource list */
6781da177e4SLinus Torvalds debug("i should be here, [%x - %x]\n", res->start, res->end);
6791da177e4SLinus Torvalds res_prev->nextRange = res;
6801da177e4SLinus Torvalds res->next = NULL;
6811da177e4SLinus Torvalds res->nextRange = NULL;
6821da177e4SLinus Torvalds } else if (res_cur->rangeno == res->rangeno) {
6831da177e4SLinus Torvalds /* in the same range */
6841da177e4SLinus Torvalds while (res_cur) {
6851da177e4SLinus Torvalds if (res->start < res_cur->start)
6861da177e4SLinus Torvalds break;
6871da177e4SLinus Torvalds res_prev = res_cur;
6881da177e4SLinus Torvalds res_cur = res_cur->next;
6891da177e4SLinus Torvalds }
6901da177e4SLinus Torvalds if (!res_cur) {
6911da177e4SLinus Torvalds /* the last resource in this range */
6921da177e4SLinus Torvalds res_prev->next = res;
6931da177e4SLinus Torvalds res->next = NULL;
6941da177e4SLinus Torvalds res->nextRange = res_prev->nextRange;
6951da177e4SLinus Torvalds res_prev->nextRange = NULL;
6961da177e4SLinus Torvalds } else if (res->start < res_cur->start) {
6971da177e4SLinus Torvalds /* at the beginning or middle of the range */
6981da177e4SLinus Torvalds if (!res_prev) {
6991da177e4SLinus Torvalds switch (res->type) {
7001da177e4SLinus Torvalds case IO:
7011da177e4SLinus Torvalds bus_cur->firstIO = res;
7021da177e4SLinus Torvalds break;
7031da177e4SLinus Torvalds case MEM:
7041da177e4SLinus Torvalds bus_cur->firstMem = res;
7051da177e4SLinus Torvalds break;
7061da177e4SLinus Torvalds case PFMEM:
7071da177e4SLinus Torvalds bus_cur->firstPFMem = res;
7081da177e4SLinus Torvalds break;
7091da177e4SLinus Torvalds }
7101da177e4SLinus Torvalds } else if (res_prev->rangeno == res_cur->rangeno)
7111da177e4SLinus Torvalds res_prev->next = res;
7121da177e4SLinus Torvalds else
7131da177e4SLinus Torvalds res_prev->nextRange = res;
7141da177e4SLinus Torvalds
7151da177e4SLinus Torvalds res->next = res_cur;
7161da177e4SLinus Torvalds res->nextRange = NULL;
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds } else {
7191da177e4SLinus Torvalds /* this is the case where it is 1st occurrence of the range */
7201da177e4SLinus Torvalds if (!res_prev) {
7211da177e4SLinus Torvalds /* at the beginning of the resource list */
7221da177e4SLinus Torvalds res->next = NULL;
7231da177e4SLinus Torvalds switch (res->type) {
7241da177e4SLinus Torvalds case IO:
7251da177e4SLinus Torvalds res->nextRange = bus_cur->firstIO;
7261da177e4SLinus Torvalds bus_cur->firstIO = res;
7271da177e4SLinus Torvalds break;
7281da177e4SLinus Torvalds case MEM:
7291da177e4SLinus Torvalds res->nextRange = bus_cur->firstMem;
7301da177e4SLinus Torvalds bus_cur->firstMem = res;
7311da177e4SLinus Torvalds break;
7321da177e4SLinus Torvalds case PFMEM:
7331da177e4SLinus Torvalds res->nextRange = bus_cur->firstPFMem;
7341da177e4SLinus Torvalds bus_cur->firstPFMem = res;
7351da177e4SLinus Torvalds break;
7361da177e4SLinus Torvalds }
7371da177e4SLinus Torvalds } else if (res_cur->rangeno > res->rangeno) {
7381da177e4SLinus Torvalds /* in the middle of the resource list */
7391da177e4SLinus Torvalds res_prev->nextRange = res;
7401da177e4SLinus Torvalds res->next = NULL;
7411da177e4SLinus Torvalds res->nextRange = res_cur;
7421da177e4SLinus Torvalds }
7431da177e4SLinus Torvalds }
7441da177e4SLinus Torvalds }
7451da177e4SLinus Torvalds
74666bef8c0SHarvey Harrison debug("%s - exit\n", __func__);
7471da177e4SLinus Torvalds return 0;
7481da177e4SLinus Torvalds }
7491da177e4SLinus Torvalds
7501da177e4SLinus Torvalds /****************************************************************************
7511da177e4SLinus Torvalds * This routine will remove the resource from the list of resources
7521da177e4SLinus Torvalds *
7531da177e4SLinus Torvalds * Input: io, mem, and/or pfmem resource to be deleted
754f7625980SBjorn Helgaas * Output: modified resource list
7551da177e4SLinus Torvalds * 0 or error code
7561da177e4SLinus Torvalds ****************************************************************************/
ibmphp_remove_resource(struct resource_node * res)7571da177e4SLinus Torvalds int ibmphp_remove_resource(struct resource_node *res)
7581da177e4SLinus Torvalds {
7591da177e4SLinus Torvalds struct bus_node *bus_cur;
7601da177e4SLinus Torvalds struct resource_node *res_cur = NULL;
7611da177e4SLinus Torvalds struct resource_node *res_prev;
7621da177e4SLinus Torvalds struct resource_node *mem_cur;
7631da177e4SLinus Torvalds char *type = "";
7641da177e4SLinus Torvalds
7651da177e4SLinus Torvalds if (!res) {
7661da177e4SLinus Torvalds err("resource to remove is NULL\n");
7671da177e4SLinus Torvalds return -ENODEV;
7681da177e4SLinus Torvalds }
7691da177e4SLinus Torvalds
7701da177e4SLinus Torvalds bus_cur = find_bus_wprev(res->busno, NULL, 0);
7711da177e4SLinus Torvalds
7721da177e4SLinus Torvalds if (!bus_cur) {
773227f0647SRyan Desfosses err("cannot find corresponding bus of the io resource to remove bailing out...\n");
7741da177e4SLinus Torvalds return -ENODEV;
7751da177e4SLinus Torvalds }
7761da177e4SLinus Torvalds
7771da177e4SLinus Torvalds switch (res->type) {
7781da177e4SLinus Torvalds case IO:
7791da177e4SLinus Torvalds res_cur = bus_cur->firstIO;
7801da177e4SLinus Torvalds type = "io";
7811da177e4SLinus Torvalds break;
7821da177e4SLinus Torvalds case MEM:
7831da177e4SLinus Torvalds res_cur = bus_cur->firstMem;
7841da177e4SLinus Torvalds type = "mem";
7851da177e4SLinus Torvalds break;
7861da177e4SLinus Torvalds case PFMEM:
7871da177e4SLinus Torvalds res_cur = bus_cur->firstPFMem;
7881da177e4SLinus Torvalds type = "pfmem";
7891da177e4SLinus Torvalds break;
7901da177e4SLinus Torvalds default:
7911da177e4SLinus Torvalds err("unknown type for resource to remove\n");
7921da177e4SLinus Torvalds return -EINVAL;
7931da177e4SLinus Torvalds }
7941da177e4SLinus Torvalds res_prev = NULL;
7951da177e4SLinus Torvalds
7961da177e4SLinus Torvalds while (res_cur) {
7971da177e4SLinus Torvalds if ((res_cur->start == res->start) && (res_cur->end == res->end))
7981da177e4SLinus Torvalds break;
7991da177e4SLinus Torvalds res_prev = res_cur;
8001da177e4SLinus Torvalds if (res_cur->next)
8011da177e4SLinus Torvalds res_cur = res_cur->next;
8021da177e4SLinus Torvalds else
8031da177e4SLinus Torvalds res_cur = res_cur->nextRange;
8041da177e4SLinus Torvalds }
8051da177e4SLinus Torvalds
8061da177e4SLinus Torvalds if (!res_cur) {
8071da177e4SLinus Torvalds if (res->type == PFMEM) {
8081da177e4SLinus Torvalds /*
8091da177e4SLinus Torvalds * case where pfmem might be in the PFMemFromMem list
8101da177e4SLinus Torvalds * so will also need to remove the corresponding mem
8111da177e4SLinus Torvalds * entry
8121da177e4SLinus Torvalds */
8131da177e4SLinus Torvalds res_cur = bus_cur->firstPFMemFromMem;
8141da177e4SLinus Torvalds res_prev = NULL;
8151da177e4SLinus Torvalds
8161da177e4SLinus Torvalds while (res_cur) {
8171da177e4SLinus Torvalds if ((res_cur->start == res->start) && (res_cur->end == res->end)) {
8181da177e4SLinus Torvalds mem_cur = bus_cur->firstMem;
8191da177e4SLinus Torvalds while (mem_cur) {
8201da177e4SLinus Torvalds if ((mem_cur->start == res_cur->start)
8211da177e4SLinus Torvalds && (mem_cur->end == res_cur->end))
8221da177e4SLinus Torvalds break;
8231da177e4SLinus Torvalds if (mem_cur->next)
8241da177e4SLinus Torvalds mem_cur = mem_cur->next;
8251da177e4SLinus Torvalds else
8261da177e4SLinus Torvalds mem_cur = mem_cur->nextRange;
8271da177e4SLinus Torvalds }
8281da177e4SLinus Torvalds if (!mem_cur) {
8291da177e4SLinus Torvalds err("cannot find corresponding mem node for pfmem...\n");
8301da177e4SLinus Torvalds return -EINVAL;
8311da177e4SLinus Torvalds }
8321da177e4SLinus Torvalds
8331da177e4SLinus Torvalds ibmphp_remove_resource(mem_cur);
8341da177e4SLinus Torvalds if (!res_prev)
8351da177e4SLinus Torvalds bus_cur->firstPFMemFromMem = res_cur->next;
8361da177e4SLinus Torvalds else
8371da177e4SLinus Torvalds res_prev->next = res_cur->next;
8381da177e4SLinus Torvalds kfree(res_cur);
8391da177e4SLinus Torvalds return 0;
8401da177e4SLinus Torvalds }
8411da177e4SLinus Torvalds res_prev = res_cur;
8421da177e4SLinus Torvalds if (res_cur->next)
8431da177e4SLinus Torvalds res_cur = res_cur->next;
8441da177e4SLinus Torvalds else
8451da177e4SLinus Torvalds res_cur = res_cur->nextRange;
8461da177e4SLinus Torvalds }
8471da177e4SLinus Torvalds if (!res_cur) {
8481da177e4SLinus Torvalds err("cannot find pfmem to delete...\n");
8491da177e4SLinus Torvalds return -EINVAL;
8501da177e4SLinus Torvalds }
8511da177e4SLinus Torvalds } else {
8521da177e4SLinus Torvalds err("the %s resource is not in the list to be deleted...\n", type);
8531da177e4SLinus Torvalds return -EINVAL;
8541da177e4SLinus Torvalds }
8551da177e4SLinus Torvalds }
8561da177e4SLinus Torvalds if (!res_prev) {
8571da177e4SLinus Torvalds /* first device to be deleted */
8581da177e4SLinus Torvalds if (res_cur->next) {
8591da177e4SLinus Torvalds switch (res->type) {
8601da177e4SLinus Torvalds case IO:
8611da177e4SLinus Torvalds bus_cur->firstIO = res_cur->next;
8621da177e4SLinus Torvalds break;
8631da177e4SLinus Torvalds case MEM:
8641da177e4SLinus Torvalds bus_cur->firstMem = res_cur->next;
8651da177e4SLinus Torvalds break;
8661da177e4SLinus Torvalds case PFMEM:
8671da177e4SLinus Torvalds bus_cur->firstPFMem = res_cur->next;
8681da177e4SLinus Torvalds break;
8691da177e4SLinus Torvalds }
8701da177e4SLinus Torvalds } else if (res_cur->nextRange) {
8711da177e4SLinus Torvalds switch (res->type) {
8721da177e4SLinus Torvalds case IO:
8731da177e4SLinus Torvalds bus_cur->firstIO = res_cur->nextRange;
8741da177e4SLinus Torvalds break;
8751da177e4SLinus Torvalds case MEM:
8761da177e4SLinus Torvalds bus_cur->firstMem = res_cur->nextRange;
8771da177e4SLinus Torvalds break;
8781da177e4SLinus Torvalds case PFMEM:
8791da177e4SLinus Torvalds bus_cur->firstPFMem = res_cur->nextRange;
8801da177e4SLinus Torvalds break;
8811da177e4SLinus Torvalds }
8821da177e4SLinus Torvalds } else {
8831da177e4SLinus Torvalds switch (res->type) {
8841da177e4SLinus Torvalds case IO:
8851da177e4SLinus Torvalds bus_cur->firstIO = NULL;
8861da177e4SLinus Torvalds break;
8871da177e4SLinus Torvalds case MEM:
8881da177e4SLinus Torvalds bus_cur->firstMem = NULL;
8891da177e4SLinus Torvalds break;
8901da177e4SLinus Torvalds case PFMEM:
8911da177e4SLinus Torvalds bus_cur->firstPFMem = NULL;
8921da177e4SLinus Torvalds break;
8931da177e4SLinus Torvalds }
8941da177e4SLinus Torvalds }
8951da177e4SLinus Torvalds kfree(res_cur);
8961da177e4SLinus Torvalds return 0;
8971da177e4SLinus Torvalds } else {
8981da177e4SLinus Torvalds if (res_cur->next) {
8991da177e4SLinus Torvalds if (res_prev->rangeno == res_cur->rangeno)
9001da177e4SLinus Torvalds res_prev->next = res_cur->next;
9011da177e4SLinus Torvalds else
9021da177e4SLinus Torvalds res_prev->nextRange = res_cur->next;
9031da177e4SLinus Torvalds } else if (res_cur->nextRange) {
9041da177e4SLinus Torvalds res_prev->next = NULL;
9051da177e4SLinus Torvalds res_prev->nextRange = res_cur->nextRange;
9061da177e4SLinus Torvalds } else {
9071da177e4SLinus Torvalds res_prev->next = NULL;
9081da177e4SLinus Torvalds res_prev->nextRange = NULL;
9091da177e4SLinus Torvalds }
9101da177e4SLinus Torvalds kfree(res_cur);
9111da177e4SLinus Torvalds return 0;
9121da177e4SLinus Torvalds }
9131da177e4SLinus Torvalds
9141da177e4SLinus Torvalds return 0;
9151da177e4SLinus Torvalds }
9161da177e4SLinus Torvalds
find_range(struct bus_node * bus_cur,struct resource_node * res)9171da177e4SLinus Torvalds static struct range_node *find_range(struct bus_node *bus_cur, struct resource_node *res)
9181da177e4SLinus Torvalds {
9191da177e4SLinus Torvalds struct range_node *range = NULL;
9201da177e4SLinus Torvalds
9211da177e4SLinus Torvalds switch (res->type) {
9221da177e4SLinus Torvalds case IO:
9231da177e4SLinus Torvalds range = bus_cur->rangeIO;
9241da177e4SLinus Torvalds break;
9251da177e4SLinus Torvalds case MEM:
9261da177e4SLinus Torvalds range = bus_cur->rangeMem;
9271da177e4SLinus Torvalds break;
9281da177e4SLinus Torvalds case PFMEM:
9291da177e4SLinus Torvalds range = bus_cur->rangePFMem;
9301da177e4SLinus Torvalds break;
9311da177e4SLinus Torvalds default:
9321da177e4SLinus Torvalds err("cannot read resource type in find_range\n");
9331da177e4SLinus Torvalds }
9341da177e4SLinus Torvalds
9351da177e4SLinus Torvalds while (range) {
9361da177e4SLinus Torvalds if (res->rangeno == range->rangeno)
9371da177e4SLinus Torvalds break;
9381da177e4SLinus Torvalds range = range->next;
9391da177e4SLinus Torvalds }
9401da177e4SLinus Torvalds return range;
9411da177e4SLinus Torvalds }
9421da177e4SLinus Torvalds
9431da177e4SLinus Torvalds /*****************************************************************************
9441da177e4SLinus Torvalds * This routine will check to make sure the io/mem/pfmem->len that the device asked for
9451da177e4SLinus Torvalds * can fit w/i our list of available IO/MEM/PFMEM resources. If cannot, returns -EINVAL,
9461da177e4SLinus Torvalds * otherwise, returns 0
9471da177e4SLinus Torvalds *
9481da177e4SLinus Torvalds * Input: resource
949f7625980SBjorn Helgaas * Output: the correct start and end address are inputted into the resource node,
9501da177e4SLinus Torvalds * 0 or -EINVAL
9511da177e4SLinus Torvalds *****************************************************************************/
ibmphp_check_resource(struct resource_node * res,u8 bridge)9521da177e4SLinus Torvalds int ibmphp_check_resource(struct resource_node *res, u8 bridge)
9531da177e4SLinus Torvalds {
9541da177e4SLinus Torvalds struct bus_node *bus_cur;
9551da177e4SLinus Torvalds struct range_node *range = NULL;
9561da177e4SLinus Torvalds struct resource_node *res_prev;
9571da177e4SLinus Torvalds struct resource_node *res_cur = NULL;
9581da177e4SLinus Torvalds u32 len_cur = 0, start_cur = 0, len_tmp = 0;
9591da177e4SLinus Torvalds int noranges = 0;
9601da177e4SLinus Torvalds u32 tmp_start; /* this is to make sure start address is divisible by the length needed */
9611da177e4SLinus Torvalds u32 tmp_divide;
962dc6712d1SKristen Accardi u8 flag = 0;
9631da177e4SLinus Torvalds
9641da177e4SLinus Torvalds if (!res)
9651da177e4SLinus Torvalds return -EINVAL;
9661da177e4SLinus Torvalds
9671da177e4SLinus Torvalds if (bridge) {
9681da177e4SLinus Torvalds /* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/
9691da177e4SLinus Torvalds if (res->type == IO)
9701da177e4SLinus Torvalds tmp_divide = IOBRIDGE;
9711da177e4SLinus Torvalds else
9721da177e4SLinus Torvalds tmp_divide = MEMBRIDGE;
9731da177e4SLinus Torvalds } else
9741da177e4SLinus Torvalds tmp_divide = res->len;
9751da177e4SLinus Torvalds
9761da177e4SLinus Torvalds bus_cur = find_bus_wprev(res->busno, NULL, 0);
9771da177e4SLinus Torvalds
9781da177e4SLinus Torvalds if (!bus_cur) {
979f7625980SBjorn Helgaas /* didn't find a bus, something's wrong!!! */
9801da177e4SLinus Torvalds debug("no bus in the system, either pci_dev's wrong or allocation failed\n");
9811da177e4SLinus Torvalds return -EINVAL;
9821da177e4SLinus Torvalds }
9831da177e4SLinus Torvalds
98466bef8c0SHarvey Harrison debug("%s - enter\n", __func__);
9851da177e4SLinus Torvalds debug("bus_cur->busno is %d\n", bus_cur->busno);
9861da177e4SLinus Torvalds
9871da177e4SLinus Torvalds /* This is a quick fix to not mess up with the code very much. i.e.,
9881da177e4SLinus Torvalds * 2000-2fff, len = 1000, but when we compare, we need it to be fff */
9891da177e4SLinus Torvalds res->len -= 1;
9901da177e4SLinus Torvalds
9911da177e4SLinus Torvalds switch (res->type) {
9921da177e4SLinus Torvalds case IO:
9931da177e4SLinus Torvalds res_cur = bus_cur->firstIO;
9941da177e4SLinus Torvalds noranges = bus_cur->noIORanges;
9951da177e4SLinus Torvalds break;
9961da177e4SLinus Torvalds case MEM:
9971da177e4SLinus Torvalds res_cur = bus_cur->firstMem;
9981da177e4SLinus Torvalds noranges = bus_cur->noMemRanges;
9991da177e4SLinus Torvalds break;
10001da177e4SLinus Torvalds case PFMEM:
10011da177e4SLinus Torvalds res_cur = bus_cur->firstPFMem;
10021da177e4SLinus Torvalds noranges = bus_cur->noPFMemRanges;
10031da177e4SLinus Torvalds break;
10041da177e4SLinus Torvalds default:
10051da177e4SLinus Torvalds err("wrong type of resource to check\n");
10061da177e4SLinus Torvalds return -EINVAL;
10071da177e4SLinus Torvalds }
10081da177e4SLinus Torvalds res_prev = NULL;
10091da177e4SLinus Torvalds
10101da177e4SLinus Torvalds while (res_cur) {
10111da177e4SLinus Torvalds range = find_range(bus_cur, res_cur);
101266bef8c0SHarvey Harrison debug("%s - rangeno = %d\n", __func__, res_cur->rangeno);
10131da177e4SLinus Torvalds
10141da177e4SLinus Torvalds if (!range) {
10151da177e4SLinus Torvalds err("no range for the device exists... bailing out...\n");
10161da177e4SLinus Torvalds return -EINVAL;
10171da177e4SLinus Torvalds }
10181da177e4SLinus Torvalds
10191da177e4SLinus Torvalds /* found our range */
10201da177e4SLinus Torvalds if (!res_prev) {
10211da177e4SLinus Torvalds /* first time in the loop */
10222f4096e3SQuentin Lambert len_tmp = res_cur->start - 1 - range->start;
10232f4096e3SQuentin Lambert
10242f4096e3SQuentin Lambert if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
10251da177e4SLinus Torvalds debug("len_tmp = %x\n", len_tmp);
10261da177e4SLinus Torvalds
10271da177e4SLinus Torvalds if ((len_tmp < len_cur) || (len_cur == 0)) {
10281da177e4SLinus Torvalds
10291da177e4SLinus Torvalds if ((range->start % tmp_divide) == 0) {
10301da177e4SLinus Torvalds /* just perfect, starting address is divisible by length */
1031dc6712d1SKristen Accardi flag = 1;
10321da177e4SLinus Torvalds len_cur = len_tmp;
10331da177e4SLinus Torvalds start_cur = range->start;
10341da177e4SLinus Torvalds } else {
10351da177e4SLinus Torvalds /* Needs adjusting */
10361da177e4SLinus Torvalds tmp_start = range->start;
1037dc6712d1SKristen Accardi flag = 0;
10381da177e4SLinus Torvalds
10391da177e4SLinus Torvalds while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
10401da177e4SLinus Torvalds if ((tmp_start % tmp_divide) == 0) {
1041dc6712d1SKristen Accardi flag = 1;
10421da177e4SLinus Torvalds len_cur = len_tmp;
10431da177e4SLinus Torvalds start_cur = tmp_start;
10441da177e4SLinus Torvalds break;
10451da177e4SLinus Torvalds }
10461da177e4SLinus Torvalds tmp_start += tmp_divide - tmp_start % tmp_divide;
10471da177e4SLinus Torvalds if (tmp_start >= res_cur->start - 1)
10481da177e4SLinus Torvalds break;
10491da177e4SLinus Torvalds }
10501da177e4SLinus Torvalds }
10511da177e4SLinus Torvalds
10521da177e4SLinus Torvalds if (flag && len_cur == res->len) {
10531da177e4SLinus Torvalds debug("but we are not here, right?\n");
10541da177e4SLinus Torvalds res->start = start_cur;
10551da177e4SLinus Torvalds res->len += 1; /* To restore the balance */
10561da177e4SLinus Torvalds res->end = res->start + res->len - 1;
10571da177e4SLinus Torvalds return 0;
10581da177e4SLinus Torvalds }
10591da177e4SLinus Torvalds }
10601da177e4SLinus Torvalds }
10611da177e4SLinus Torvalds }
10621da177e4SLinus Torvalds if (!res_cur->next) {
10631da177e4SLinus Torvalds /* last device on the range */
10642f4096e3SQuentin Lambert len_tmp = range->end - (res_cur->end + 1);
10652f4096e3SQuentin Lambert
10662f4096e3SQuentin Lambert if ((range->end != res_cur->end) && (len_tmp >= res->len)) {
10671da177e4SLinus Torvalds debug("len_tmp = %x\n", len_tmp);
10681da177e4SLinus Torvalds if ((len_tmp < len_cur) || (len_cur == 0)) {
10691da177e4SLinus Torvalds
10701da177e4SLinus Torvalds if (((res_cur->end + 1) % tmp_divide) == 0) {
10711da177e4SLinus Torvalds /* just perfect, starting address is divisible by length */
1072dc6712d1SKristen Accardi flag = 1;
10731da177e4SLinus Torvalds len_cur = len_tmp;
10741da177e4SLinus Torvalds start_cur = res_cur->end + 1;
10751da177e4SLinus Torvalds } else {
10761da177e4SLinus Torvalds /* Needs adjusting */
10771da177e4SLinus Torvalds tmp_start = res_cur->end + 1;
1078dc6712d1SKristen Accardi flag = 0;
10791da177e4SLinus Torvalds
10801da177e4SLinus Torvalds while ((len_tmp = range->end - tmp_start) >= res->len) {
10811da177e4SLinus Torvalds if ((tmp_start % tmp_divide) == 0) {
1082dc6712d1SKristen Accardi flag = 1;
10831da177e4SLinus Torvalds len_cur = len_tmp;
10841da177e4SLinus Torvalds start_cur = tmp_start;
10851da177e4SLinus Torvalds break;
10861da177e4SLinus Torvalds }
10871da177e4SLinus Torvalds tmp_start += tmp_divide - tmp_start % tmp_divide;
10881da177e4SLinus Torvalds if (tmp_start >= range->end)
10891da177e4SLinus Torvalds break;
10901da177e4SLinus Torvalds }
10911da177e4SLinus Torvalds }
10921da177e4SLinus Torvalds if (flag && len_cur == res->len) {
10931da177e4SLinus Torvalds res->start = start_cur;
10941da177e4SLinus Torvalds res->len += 1; /* To restore the balance */
10951da177e4SLinus Torvalds res->end = res->start + res->len - 1;
10961da177e4SLinus Torvalds return 0;
10971da177e4SLinus Torvalds }
10981da177e4SLinus Torvalds }
10991da177e4SLinus Torvalds }
11001da177e4SLinus Torvalds }
11011da177e4SLinus Torvalds
11021da177e4SLinus Torvalds if (res_prev) {
11031da177e4SLinus Torvalds if (res_prev->rangeno != res_cur->rangeno) {
11041da177e4SLinus Torvalds /* 1st device on this range */
11052f4096e3SQuentin Lambert len_tmp = res_cur->start - 1 - range->start;
11062f4096e3SQuentin Lambert
11072f4096e3SQuentin Lambert if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
11081da177e4SLinus Torvalds if ((len_tmp < len_cur) || (len_cur == 0)) {
11091da177e4SLinus Torvalds if ((range->start % tmp_divide) == 0) {
11101da177e4SLinus Torvalds /* just perfect, starting address is divisible by length */
1111dc6712d1SKristen Accardi flag = 1;
11121da177e4SLinus Torvalds len_cur = len_tmp;
11131da177e4SLinus Torvalds start_cur = range->start;
11141da177e4SLinus Torvalds } else {
11151da177e4SLinus Torvalds /* Needs adjusting */
11161da177e4SLinus Torvalds tmp_start = range->start;
1117dc6712d1SKristen Accardi flag = 0;
11181da177e4SLinus Torvalds
11191da177e4SLinus Torvalds while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
11201da177e4SLinus Torvalds if ((tmp_start % tmp_divide) == 0) {
1121dc6712d1SKristen Accardi flag = 1;
11221da177e4SLinus Torvalds len_cur = len_tmp;
11231da177e4SLinus Torvalds start_cur = tmp_start;
11241da177e4SLinus Torvalds break;
11251da177e4SLinus Torvalds }
11261da177e4SLinus Torvalds tmp_start += tmp_divide - tmp_start % tmp_divide;
11271da177e4SLinus Torvalds if (tmp_start >= res_cur->start - 1)
11281da177e4SLinus Torvalds break;
11291da177e4SLinus Torvalds }
11301da177e4SLinus Torvalds }
11311da177e4SLinus Torvalds
11321da177e4SLinus Torvalds if (flag && len_cur == res->len) {
11331da177e4SLinus Torvalds res->start = start_cur;
11341da177e4SLinus Torvalds res->len += 1; /* To restore the balance */
11351da177e4SLinus Torvalds res->end = res->start + res->len - 1;
11361da177e4SLinus Torvalds return 0;
11371da177e4SLinus Torvalds }
11381da177e4SLinus Torvalds }
11391da177e4SLinus Torvalds }
11401da177e4SLinus Torvalds } else {
11411da177e4SLinus Torvalds /* in the same range */
114279e50e72SQuentin Lambert len_tmp = res_cur->start - 1 - res_prev->end - 1;
114379e50e72SQuentin Lambert
114479e50e72SQuentin Lambert if (len_tmp >= res->len) {
11451da177e4SLinus Torvalds if ((len_tmp < len_cur) || (len_cur == 0)) {
11461da177e4SLinus Torvalds if (((res_prev->end + 1) % tmp_divide) == 0) {
11471da177e4SLinus Torvalds /* just perfect, starting address's divisible by length */
1148dc6712d1SKristen Accardi flag = 1;
11491da177e4SLinus Torvalds len_cur = len_tmp;
11501da177e4SLinus Torvalds start_cur = res_prev->end + 1;
11511da177e4SLinus Torvalds } else {
11521da177e4SLinus Torvalds /* Needs adjusting */
11531da177e4SLinus Torvalds tmp_start = res_prev->end + 1;
1154dc6712d1SKristen Accardi flag = 0;
11551da177e4SLinus Torvalds
11561da177e4SLinus Torvalds while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
11571da177e4SLinus Torvalds if ((tmp_start % tmp_divide) == 0) {
1158dc6712d1SKristen Accardi flag = 1;
11591da177e4SLinus Torvalds len_cur = len_tmp;
11601da177e4SLinus Torvalds start_cur = tmp_start;
11611da177e4SLinus Torvalds break;
11621da177e4SLinus Torvalds }
11631da177e4SLinus Torvalds tmp_start += tmp_divide - tmp_start % tmp_divide;
11641da177e4SLinus Torvalds if (tmp_start >= res_cur->start - 1)
11651da177e4SLinus Torvalds break;
11661da177e4SLinus Torvalds }
11671da177e4SLinus Torvalds }
11681da177e4SLinus Torvalds
11691da177e4SLinus Torvalds if (flag && len_cur == res->len) {
11701da177e4SLinus Torvalds res->start = start_cur;
11711da177e4SLinus Torvalds res->len += 1; /* To restore the balance */
11721da177e4SLinus Torvalds res->end = res->start + res->len - 1;
11731da177e4SLinus Torvalds return 0;
11741da177e4SLinus Torvalds }
11751da177e4SLinus Torvalds }
11761da177e4SLinus Torvalds }
11771da177e4SLinus Torvalds }
11781da177e4SLinus Torvalds }
11791da177e4SLinus Torvalds /* end if (res_prev) */
11801da177e4SLinus Torvalds res_prev = res_cur;
11811da177e4SLinus Torvalds if (res_cur->next)
11821da177e4SLinus Torvalds res_cur = res_cur->next;
11831da177e4SLinus Torvalds else
11841da177e4SLinus Torvalds res_cur = res_cur->nextRange;
11851da177e4SLinus Torvalds } /* end of while */
11861da177e4SLinus Torvalds
11871da177e4SLinus Torvalds
11881da177e4SLinus Torvalds if (!res_prev) {
11891da177e4SLinus Torvalds /* 1st device ever */
11901da177e4SLinus Torvalds /* need to find appropriate range */
11911da177e4SLinus Torvalds switch (res->type) {
11921da177e4SLinus Torvalds case IO:
11931da177e4SLinus Torvalds range = bus_cur->rangeIO;
11941da177e4SLinus Torvalds break;
11951da177e4SLinus Torvalds case MEM:
11961da177e4SLinus Torvalds range = bus_cur->rangeMem;
11971da177e4SLinus Torvalds break;
11981da177e4SLinus Torvalds case PFMEM:
11991da177e4SLinus Torvalds range = bus_cur->rangePFMem;
12001da177e4SLinus Torvalds break;
12011da177e4SLinus Torvalds }
12021da177e4SLinus Torvalds while (range) {
120379e50e72SQuentin Lambert len_tmp = range->end - range->start;
120479e50e72SQuentin Lambert
120579e50e72SQuentin Lambert if (len_tmp >= res->len) {
12061da177e4SLinus Torvalds if ((len_tmp < len_cur) || (len_cur == 0)) {
12071da177e4SLinus Torvalds if ((range->start % tmp_divide) == 0) {
12081da177e4SLinus Torvalds /* just perfect, starting address's divisible by length */
1209dc6712d1SKristen Accardi flag = 1;
12101da177e4SLinus Torvalds len_cur = len_tmp;
12111da177e4SLinus Torvalds start_cur = range->start;
12121da177e4SLinus Torvalds } else {
12131da177e4SLinus Torvalds /* Needs adjusting */
12141da177e4SLinus Torvalds tmp_start = range->start;
1215dc6712d1SKristen Accardi flag = 0;
12161da177e4SLinus Torvalds
12171da177e4SLinus Torvalds while ((len_tmp = range->end - tmp_start) >= res->len) {
12181da177e4SLinus Torvalds if ((tmp_start % tmp_divide) == 0) {
1219dc6712d1SKristen Accardi flag = 1;
12201da177e4SLinus Torvalds len_cur = len_tmp;
12211da177e4SLinus Torvalds start_cur = tmp_start;
12221da177e4SLinus Torvalds break;
12231da177e4SLinus Torvalds }
12241da177e4SLinus Torvalds tmp_start += tmp_divide - tmp_start % tmp_divide;
12251da177e4SLinus Torvalds if (tmp_start >= range->end)
12261da177e4SLinus Torvalds break;
12271da177e4SLinus Torvalds }
12281da177e4SLinus Torvalds }
12291da177e4SLinus Torvalds
12301da177e4SLinus Torvalds if (flag && len_cur == res->len) {
12311da177e4SLinus Torvalds res->start = start_cur;
12321da177e4SLinus Torvalds res->len += 1; /* To restore the balance */
12331da177e4SLinus Torvalds res->end = res->start + res->len - 1;
12341da177e4SLinus Torvalds return 0;
12351da177e4SLinus Torvalds }
12361da177e4SLinus Torvalds }
12371da177e4SLinus Torvalds }
12381da177e4SLinus Torvalds range = range->next;
12391da177e4SLinus Torvalds } /* end of while */
12401da177e4SLinus Torvalds
12411da177e4SLinus Torvalds if ((!range) && (len_cur == 0)) {
12421da177e4SLinus Torvalds /* have gone through the list of devices and ranges and haven't found n.e.thing */
12431da177e4SLinus Torvalds err("no appropriate range.. bailing out...\n");
12441da177e4SLinus Torvalds return -EINVAL;
12451da177e4SLinus Torvalds } else if (len_cur) {
12461da177e4SLinus Torvalds res->start = start_cur;
12471da177e4SLinus Torvalds res->len += 1; /* To restore the balance */
12481da177e4SLinus Torvalds res->end = res->start + res->len - 1;
12491da177e4SLinus Torvalds return 0;
12501da177e4SLinus Torvalds }
12511da177e4SLinus Torvalds }
12521da177e4SLinus Torvalds
12531da177e4SLinus Torvalds if (!res_cur) {
12541da177e4SLinus Torvalds debug("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges);
12551da177e4SLinus Torvalds if (res_prev->rangeno < noranges) {
12561da177e4SLinus Torvalds /* if there're more ranges out there to check */
12571da177e4SLinus Torvalds switch (res->type) {
12581da177e4SLinus Torvalds case IO:
12591da177e4SLinus Torvalds range = bus_cur->rangeIO;
12601da177e4SLinus Torvalds break;
12611da177e4SLinus Torvalds case MEM:
12621da177e4SLinus Torvalds range = bus_cur->rangeMem;
12631da177e4SLinus Torvalds break;
12641da177e4SLinus Torvalds case PFMEM:
12651da177e4SLinus Torvalds range = bus_cur->rangePFMem;
12661da177e4SLinus Torvalds break;
12671da177e4SLinus Torvalds }
12681da177e4SLinus Torvalds while (range) {
126979e50e72SQuentin Lambert len_tmp = range->end - range->start;
127079e50e72SQuentin Lambert
127179e50e72SQuentin Lambert if (len_tmp >= res->len) {
12721da177e4SLinus Torvalds if ((len_tmp < len_cur) || (len_cur == 0)) {
12731da177e4SLinus Torvalds if ((range->start % tmp_divide) == 0) {
12741da177e4SLinus Torvalds /* just perfect, starting address's divisible by length */
1275dc6712d1SKristen Accardi flag = 1;
12761da177e4SLinus Torvalds len_cur = len_tmp;
12771da177e4SLinus Torvalds start_cur = range->start;
12781da177e4SLinus Torvalds } else {
12791da177e4SLinus Torvalds /* Needs adjusting */
12801da177e4SLinus Torvalds tmp_start = range->start;
1281dc6712d1SKristen Accardi flag = 0;
12821da177e4SLinus Torvalds
12831da177e4SLinus Torvalds while ((len_tmp = range->end - tmp_start) >= res->len) {
12841da177e4SLinus Torvalds if ((tmp_start % tmp_divide) == 0) {
1285dc6712d1SKristen Accardi flag = 1;
12861da177e4SLinus Torvalds len_cur = len_tmp;
12871da177e4SLinus Torvalds start_cur = tmp_start;
12881da177e4SLinus Torvalds break;
12891da177e4SLinus Torvalds }
12901da177e4SLinus Torvalds tmp_start += tmp_divide - tmp_start % tmp_divide;
12911da177e4SLinus Torvalds if (tmp_start >= range->end)
12921da177e4SLinus Torvalds break;
12931da177e4SLinus Torvalds }
12941da177e4SLinus Torvalds }
12951da177e4SLinus Torvalds
12961da177e4SLinus Torvalds if (flag && len_cur == res->len) {
12971da177e4SLinus Torvalds res->start = start_cur;
12981da177e4SLinus Torvalds res->len += 1; /* To restore the balance */
12991da177e4SLinus Torvalds res->end = res->start + res->len - 1;
13001da177e4SLinus Torvalds return 0;
13011da177e4SLinus Torvalds }
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds }
13041da177e4SLinus Torvalds range = range->next;
13051da177e4SLinus Torvalds } /* end of while */
13061da177e4SLinus Torvalds
13071da177e4SLinus Torvalds if ((!range) && (len_cur == 0)) {
13081da177e4SLinus Torvalds /* have gone through the list of devices and ranges and haven't found n.e.thing */
13091da177e4SLinus Torvalds err("no appropriate range.. bailing out...\n");
13101da177e4SLinus Torvalds return -EINVAL;
13111da177e4SLinus Torvalds } else if (len_cur) {
13121da177e4SLinus Torvalds res->start = start_cur;
13131da177e4SLinus Torvalds res->len += 1; /* To restore the balance */
13141da177e4SLinus Torvalds res->end = res->start + res->len - 1;
13151da177e4SLinus Torvalds return 0;
13161da177e4SLinus Torvalds }
13171da177e4SLinus Torvalds } else {
13181da177e4SLinus Torvalds /* no more ranges to check on */
13191da177e4SLinus Torvalds if (len_cur) {
13201da177e4SLinus Torvalds res->start = start_cur;
13211da177e4SLinus Torvalds res->len += 1; /* To restore the balance */
13221da177e4SLinus Torvalds res->end = res->start + res->len - 1;
13231da177e4SLinus Torvalds return 0;
13241da177e4SLinus Torvalds } else {
13251da177e4SLinus Torvalds /* have gone through the list of devices and haven't found n.e.thing */
13261da177e4SLinus Torvalds err("no appropriate range.. bailing out...\n");
13271da177e4SLinus Torvalds return -EINVAL;
13281da177e4SLinus Torvalds }
13291da177e4SLinus Torvalds }
13301da177e4SLinus Torvalds } /* end if (!res_cur) */
13311da177e4SLinus Torvalds return -EINVAL;
13321da177e4SLinus Torvalds }
13331da177e4SLinus Torvalds
13341da177e4SLinus Torvalds /********************************************************************************
13351da177e4SLinus Torvalds * This routine is called from remove_card if the card contained PPB.
13361da177e4SLinus Torvalds * It will remove all the resources on the bus as well as the bus itself
13371da177e4SLinus Torvalds * Input: Bus
1338f7625980SBjorn Helgaas * Output: 0, -ENODEV
13391da177e4SLinus Torvalds ********************************************************************************/
ibmphp_remove_bus(struct bus_node * bus,u8 parent_busno)13401da177e4SLinus Torvalds int ibmphp_remove_bus(struct bus_node *bus, u8 parent_busno)
13411da177e4SLinus Torvalds {
13421da177e4SLinus Torvalds struct resource_node *res_cur;
13431da177e4SLinus Torvalds struct resource_node *res_tmp;
13441da177e4SLinus Torvalds struct bus_node *prev_bus;
13451da177e4SLinus Torvalds int rc;
13461da177e4SLinus Torvalds
13471da177e4SLinus Torvalds prev_bus = find_bus_wprev(parent_busno, NULL, 0);
13481da177e4SLinus Torvalds
13491da177e4SLinus Torvalds if (!prev_bus) {
13501da177e4SLinus Torvalds debug("something terribly wrong. Cannot find parent bus to the one to remove\n");
13511da177e4SLinus Torvalds return -ENODEV;
13521da177e4SLinus Torvalds }
13531da177e4SLinus Torvalds
13541da177e4SLinus Torvalds debug("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno);
13551da177e4SLinus Torvalds
13561da177e4SLinus Torvalds rc = remove_ranges(bus, prev_bus);
13571da177e4SLinus Torvalds if (rc)
13581da177e4SLinus Torvalds return rc;
13591da177e4SLinus Torvalds
13601da177e4SLinus Torvalds if (bus->firstIO) {
13611da177e4SLinus Torvalds res_cur = bus->firstIO;
13621da177e4SLinus Torvalds while (res_cur) {
13631da177e4SLinus Torvalds res_tmp = res_cur;
13641da177e4SLinus Torvalds if (res_cur->next)
13651da177e4SLinus Torvalds res_cur = res_cur->next;
13661da177e4SLinus Torvalds else
13671da177e4SLinus Torvalds res_cur = res_cur->nextRange;
13681da177e4SLinus Torvalds kfree(res_tmp);
13691da177e4SLinus Torvalds res_tmp = NULL;
13701da177e4SLinus Torvalds }
13711da177e4SLinus Torvalds bus->firstIO = NULL;
13721da177e4SLinus Torvalds }
13731da177e4SLinus Torvalds if (bus->firstMem) {
13741da177e4SLinus Torvalds res_cur = bus->firstMem;
13751da177e4SLinus Torvalds while (res_cur) {
13761da177e4SLinus Torvalds res_tmp = res_cur;
13771da177e4SLinus Torvalds if (res_cur->next)
13781da177e4SLinus Torvalds res_cur = res_cur->next;
13791da177e4SLinus Torvalds else
13801da177e4SLinus Torvalds res_cur = res_cur->nextRange;
13811da177e4SLinus Torvalds kfree(res_tmp);
13821da177e4SLinus Torvalds res_tmp = NULL;
13831da177e4SLinus Torvalds }
13841da177e4SLinus Torvalds bus->firstMem = NULL;
13851da177e4SLinus Torvalds }
13861da177e4SLinus Torvalds if (bus->firstPFMem) {
13871da177e4SLinus Torvalds res_cur = bus->firstPFMem;
13881da177e4SLinus Torvalds while (res_cur) {
13891da177e4SLinus Torvalds res_tmp = res_cur;
13901da177e4SLinus Torvalds if (res_cur->next)
13911da177e4SLinus Torvalds res_cur = res_cur->next;
13921da177e4SLinus Torvalds else
13931da177e4SLinus Torvalds res_cur = res_cur->nextRange;
13941da177e4SLinus Torvalds kfree(res_tmp);
13951da177e4SLinus Torvalds res_tmp = NULL;
13961da177e4SLinus Torvalds }
13971da177e4SLinus Torvalds bus->firstPFMem = NULL;
13981da177e4SLinus Torvalds }
13991da177e4SLinus Torvalds
14001da177e4SLinus Torvalds if (bus->firstPFMemFromMem) {
14011da177e4SLinus Torvalds res_cur = bus->firstPFMemFromMem;
14021da177e4SLinus Torvalds while (res_cur) {
14031da177e4SLinus Torvalds res_tmp = res_cur;
14041da177e4SLinus Torvalds res_cur = res_cur->next;
14051da177e4SLinus Torvalds
14061da177e4SLinus Torvalds kfree(res_tmp);
14071da177e4SLinus Torvalds res_tmp = NULL;
14081da177e4SLinus Torvalds }
14091da177e4SLinus Torvalds bus->firstPFMemFromMem = NULL;
14101da177e4SLinus Torvalds }
14111da177e4SLinus Torvalds
14121da177e4SLinus Torvalds list_del(&bus->bus_list);
14131da177e4SLinus Torvalds kfree(bus);
14141da177e4SLinus Torvalds return 0;
14151da177e4SLinus Torvalds }
14161da177e4SLinus Torvalds
14171da177e4SLinus Torvalds /******************************************************************************
14181da177e4SLinus Torvalds * This routine deletes the ranges from a given bus, and the entries from the
14191da177e4SLinus Torvalds * parent's bus in the resources
14201da177e4SLinus Torvalds * Input: current bus, previous bus
14211da177e4SLinus Torvalds * Output: 0, -EINVAL
14221da177e4SLinus Torvalds ******************************************************************************/
remove_ranges(struct bus_node * bus_cur,struct bus_node * bus_prev)14231da177e4SLinus Torvalds static int remove_ranges(struct bus_node *bus_cur, struct bus_node *bus_prev)
14241da177e4SLinus Torvalds {
14251da177e4SLinus Torvalds struct range_node *range_cur;
14261da177e4SLinus Torvalds struct range_node *range_tmp;
14271da177e4SLinus Torvalds int i;
14281da177e4SLinus Torvalds struct resource_node *res = NULL;
14291da177e4SLinus Torvalds
14301da177e4SLinus Torvalds if (bus_cur->noIORanges) {
14311da177e4SLinus Torvalds range_cur = bus_cur->rangeIO;
14321da177e4SLinus Torvalds for (i = 0; i < bus_cur->noIORanges; i++) {
14331da177e4SLinus Torvalds if (ibmphp_find_resource(bus_prev, range_cur->start, &res, IO) < 0)
14341da177e4SLinus Torvalds return -EINVAL;
14351da177e4SLinus Torvalds ibmphp_remove_resource(res);
14361da177e4SLinus Torvalds
14371da177e4SLinus Torvalds range_tmp = range_cur;
14381da177e4SLinus Torvalds range_cur = range_cur->next;
14391da177e4SLinus Torvalds kfree(range_tmp);
14401da177e4SLinus Torvalds range_tmp = NULL;
14411da177e4SLinus Torvalds }
14421da177e4SLinus Torvalds bus_cur->rangeIO = NULL;
14431da177e4SLinus Torvalds }
14441da177e4SLinus Torvalds if (bus_cur->noMemRanges) {
14451da177e4SLinus Torvalds range_cur = bus_cur->rangeMem;
14461da177e4SLinus Torvalds for (i = 0; i < bus_cur->noMemRanges; i++) {
14471da177e4SLinus Torvalds if (ibmphp_find_resource(bus_prev, range_cur->start, &res, MEM) < 0)
14481da177e4SLinus Torvalds return -EINVAL;
14491da177e4SLinus Torvalds
14501da177e4SLinus Torvalds ibmphp_remove_resource(res);
14511da177e4SLinus Torvalds range_tmp = range_cur;
14521da177e4SLinus Torvalds range_cur = range_cur->next;
14531da177e4SLinus Torvalds kfree(range_tmp);
14541da177e4SLinus Torvalds range_tmp = NULL;
14551da177e4SLinus Torvalds }
14561da177e4SLinus Torvalds bus_cur->rangeMem = NULL;
14571da177e4SLinus Torvalds }
14581da177e4SLinus Torvalds if (bus_cur->noPFMemRanges) {
14591da177e4SLinus Torvalds range_cur = bus_cur->rangePFMem;
14601da177e4SLinus Torvalds for (i = 0; i < bus_cur->noPFMemRanges; i++) {
14611da177e4SLinus Torvalds if (ibmphp_find_resource(bus_prev, range_cur->start, &res, PFMEM) < 0)
14621da177e4SLinus Torvalds return -EINVAL;
14631da177e4SLinus Torvalds
14641da177e4SLinus Torvalds ibmphp_remove_resource(res);
14651da177e4SLinus Torvalds range_tmp = range_cur;
14661da177e4SLinus Torvalds range_cur = range_cur->next;
14671da177e4SLinus Torvalds kfree(range_tmp);
14681da177e4SLinus Torvalds range_tmp = NULL;
14691da177e4SLinus Torvalds }
14701da177e4SLinus Torvalds bus_cur->rangePFMem = NULL;
14711da177e4SLinus Torvalds }
14721da177e4SLinus Torvalds return 0;
14731da177e4SLinus Torvalds }
14741da177e4SLinus Torvalds
14751da177e4SLinus Torvalds /*
14761da177e4SLinus Torvalds * find the resource node in the bus
14771da177e4SLinus Torvalds * Input: Resource needed, start address of the resource, type of resource
14781da177e4SLinus Torvalds */
ibmphp_find_resource(struct bus_node * bus,u32 start_address,struct resource_node ** res,int flag)14791da177e4SLinus Torvalds int ibmphp_find_resource(struct bus_node *bus, u32 start_address, struct resource_node **res, int flag)
14801da177e4SLinus Torvalds {
14811da177e4SLinus Torvalds struct resource_node *res_cur = NULL;
14821da177e4SLinus Torvalds char *type = "";
14831da177e4SLinus Torvalds
14841da177e4SLinus Torvalds if (!bus) {
14851da177e4SLinus Torvalds err("The bus passed in NULL to find resource\n");
14861da177e4SLinus Torvalds return -ENODEV;
14871da177e4SLinus Torvalds }
14881da177e4SLinus Torvalds
14891da177e4SLinus Torvalds switch (flag) {
14901da177e4SLinus Torvalds case IO:
14911da177e4SLinus Torvalds res_cur = bus->firstIO;
14921da177e4SLinus Torvalds type = "io";
14931da177e4SLinus Torvalds break;
14941da177e4SLinus Torvalds case MEM:
14951da177e4SLinus Torvalds res_cur = bus->firstMem;
14961da177e4SLinus Torvalds type = "mem";
14971da177e4SLinus Torvalds break;
14981da177e4SLinus Torvalds case PFMEM:
14991da177e4SLinus Torvalds res_cur = bus->firstPFMem;
15001da177e4SLinus Torvalds type = "pfmem";
15011da177e4SLinus Torvalds break;
15021da177e4SLinus Torvalds default:
15031da177e4SLinus Torvalds err("wrong type of flag\n");
15041da177e4SLinus Torvalds return -EINVAL;
15051da177e4SLinus Torvalds }
15061da177e4SLinus Torvalds
15071da177e4SLinus Torvalds while (res_cur) {
15081da177e4SLinus Torvalds if (res_cur->start == start_address) {
15091da177e4SLinus Torvalds *res = res_cur;
15101da177e4SLinus Torvalds break;
15111da177e4SLinus Torvalds }
15121da177e4SLinus Torvalds if (res_cur->next)
15131da177e4SLinus Torvalds res_cur = res_cur->next;
15141da177e4SLinus Torvalds else
15151da177e4SLinus Torvalds res_cur = res_cur->nextRange;
15161da177e4SLinus Torvalds }
15171da177e4SLinus Torvalds
15181da177e4SLinus Torvalds if (!res_cur) {
15191da177e4SLinus Torvalds if (flag == PFMEM) {
15201da177e4SLinus Torvalds res_cur = bus->firstPFMemFromMem;
15211da177e4SLinus Torvalds while (res_cur) {
15221da177e4SLinus Torvalds if (res_cur->start == start_address) {
15231da177e4SLinus Torvalds *res = res_cur;
15241da177e4SLinus Torvalds break;
15251da177e4SLinus Torvalds }
15261da177e4SLinus Torvalds res_cur = res_cur->next;
15271da177e4SLinus Torvalds }
15281da177e4SLinus Torvalds if (!res_cur) {
15291da177e4SLinus Torvalds debug("SOS...cannot find %s resource in the bus.\n", type);
15301da177e4SLinus Torvalds return -EINVAL;
15311da177e4SLinus Torvalds }
15321da177e4SLinus Torvalds } else {
15331da177e4SLinus Torvalds debug("SOS... cannot find %s resource in the bus.\n", type);
15341da177e4SLinus Torvalds return -EINVAL;
15351da177e4SLinus Torvalds }
15361da177e4SLinus Torvalds }
15371da177e4SLinus Torvalds
15381da177e4SLinus Torvalds if (*res)
15391da177e4SLinus Torvalds debug("*res->start = %x\n", (*res)->start);
15401da177e4SLinus Torvalds
15411da177e4SLinus Torvalds return 0;
15421da177e4SLinus Torvalds }
15431da177e4SLinus Torvalds
15441da177e4SLinus Torvalds /***********************************************************************
15451da177e4SLinus Torvalds * This routine will free the resource structures used by the
15461da177e4SLinus Torvalds * system. It is called from cleanup routine for the module
15471da177e4SLinus Torvalds * Parameters: none
15481da177e4SLinus Torvalds * Returns: none
15491da177e4SLinus Torvalds ***********************************************************************/
ibmphp_free_resources(void)15501da177e4SLinus Torvalds void ibmphp_free_resources(void)
15511da177e4SLinus Torvalds {
15522ac83cccSGeliang Tang struct bus_node *bus_cur = NULL, *next;
15531da177e4SLinus Torvalds struct bus_node *bus_tmp;
15541da177e4SLinus Torvalds struct range_node *range_cur;
15551da177e4SLinus Torvalds struct range_node *range_tmp;
15561da177e4SLinus Torvalds struct resource_node *res_cur;
15571da177e4SLinus Torvalds struct resource_node *res_tmp;
15581da177e4SLinus Torvalds int i = 0;
15591da177e4SLinus Torvalds flags = 1;
15601da177e4SLinus Torvalds
15612ac83cccSGeliang Tang list_for_each_entry_safe(bus_cur, next, &gbuses, bus_list) {
15621da177e4SLinus Torvalds if (bus_cur->noIORanges) {
15631da177e4SLinus Torvalds range_cur = bus_cur->rangeIO;
15641da177e4SLinus Torvalds for (i = 0; i < bus_cur->noIORanges; i++) {
15651da177e4SLinus Torvalds if (!range_cur)
15661da177e4SLinus Torvalds break;
15671da177e4SLinus Torvalds range_tmp = range_cur;
15681da177e4SLinus Torvalds range_cur = range_cur->next;
15691da177e4SLinus Torvalds kfree(range_tmp);
15701da177e4SLinus Torvalds range_tmp = NULL;
15711da177e4SLinus Torvalds }
15721da177e4SLinus Torvalds }
15731da177e4SLinus Torvalds if (bus_cur->noMemRanges) {
15741da177e4SLinus Torvalds range_cur = bus_cur->rangeMem;
15751da177e4SLinus Torvalds for (i = 0; i < bus_cur->noMemRanges; i++) {
15761da177e4SLinus Torvalds if (!range_cur)
15771da177e4SLinus Torvalds break;
15781da177e4SLinus Torvalds range_tmp = range_cur;
15791da177e4SLinus Torvalds range_cur = range_cur->next;
15801da177e4SLinus Torvalds kfree(range_tmp);
15811da177e4SLinus Torvalds range_tmp = NULL;
15821da177e4SLinus Torvalds }
15831da177e4SLinus Torvalds }
15841da177e4SLinus Torvalds if (bus_cur->noPFMemRanges) {
15851da177e4SLinus Torvalds range_cur = bus_cur->rangePFMem;
15861da177e4SLinus Torvalds for (i = 0; i < bus_cur->noPFMemRanges; i++) {
15871da177e4SLinus Torvalds if (!range_cur)
15881da177e4SLinus Torvalds break;
15891da177e4SLinus Torvalds range_tmp = range_cur;
15901da177e4SLinus Torvalds range_cur = range_cur->next;
15911da177e4SLinus Torvalds kfree(range_tmp);
15921da177e4SLinus Torvalds range_tmp = NULL;
15931da177e4SLinus Torvalds }
15941da177e4SLinus Torvalds }
15951da177e4SLinus Torvalds
15961da177e4SLinus Torvalds if (bus_cur->firstIO) {
15971da177e4SLinus Torvalds res_cur = bus_cur->firstIO;
15981da177e4SLinus Torvalds while (res_cur) {
15991da177e4SLinus Torvalds res_tmp = res_cur;
16001da177e4SLinus Torvalds if (res_cur->next)
16011da177e4SLinus Torvalds res_cur = res_cur->next;
16021da177e4SLinus Torvalds else
16031da177e4SLinus Torvalds res_cur = res_cur->nextRange;
16041da177e4SLinus Torvalds kfree(res_tmp);
16051da177e4SLinus Torvalds res_tmp = NULL;
16061da177e4SLinus Torvalds }
16071da177e4SLinus Torvalds bus_cur->firstIO = NULL;
16081da177e4SLinus Torvalds }
16091da177e4SLinus Torvalds if (bus_cur->firstMem) {
16101da177e4SLinus Torvalds res_cur = bus_cur->firstMem;
16111da177e4SLinus Torvalds while (res_cur) {
16121da177e4SLinus Torvalds res_tmp = res_cur;
16131da177e4SLinus Torvalds if (res_cur->next)
16141da177e4SLinus Torvalds res_cur = res_cur->next;
16151da177e4SLinus Torvalds else
16161da177e4SLinus Torvalds res_cur = res_cur->nextRange;
16171da177e4SLinus Torvalds kfree(res_tmp);
16181da177e4SLinus Torvalds res_tmp = NULL;
16191da177e4SLinus Torvalds }
16201da177e4SLinus Torvalds bus_cur->firstMem = NULL;
16211da177e4SLinus Torvalds }
16221da177e4SLinus Torvalds if (bus_cur->firstPFMem) {
16231da177e4SLinus Torvalds res_cur = bus_cur->firstPFMem;
16241da177e4SLinus Torvalds while (res_cur) {
16251da177e4SLinus Torvalds res_tmp = res_cur;
16261da177e4SLinus Torvalds if (res_cur->next)
16271da177e4SLinus Torvalds res_cur = res_cur->next;
16281da177e4SLinus Torvalds else
16291da177e4SLinus Torvalds res_cur = res_cur->nextRange;
16301da177e4SLinus Torvalds kfree(res_tmp);
16311da177e4SLinus Torvalds res_tmp = NULL;
16321da177e4SLinus Torvalds }
16331da177e4SLinus Torvalds bus_cur->firstPFMem = NULL;
16341da177e4SLinus Torvalds }
16351da177e4SLinus Torvalds
16361da177e4SLinus Torvalds if (bus_cur->firstPFMemFromMem) {
16371da177e4SLinus Torvalds res_cur = bus_cur->firstPFMemFromMem;
16381da177e4SLinus Torvalds while (res_cur) {
16391da177e4SLinus Torvalds res_tmp = res_cur;
16401da177e4SLinus Torvalds res_cur = res_cur->next;
16411da177e4SLinus Torvalds
16421da177e4SLinus Torvalds kfree(res_tmp);
16431da177e4SLinus Torvalds res_tmp = NULL;
16441da177e4SLinus Torvalds }
16451da177e4SLinus Torvalds bus_cur->firstPFMemFromMem = NULL;
16461da177e4SLinus Torvalds }
16471da177e4SLinus Torvalds
16481da177e4SLinus Torvalds bus_tmp = bus_cur;
16491da177e4SLinus Torvalds list_del(&bus_cur->bus_list);
16501da177e4SLinus Torvalds kfree(bus_tmp);
16511da177e4SLinus Torvalds bus_tmp = NULL;
16521da177e4SLinus Torvalds }
16531da177e4SLinus Torvalds }
16541da177e4SLinus Torvalds
16551da177e4SLinus Torvalds /*********************************************************************************
16561da177e4SLinus Torvalds * This function will go over the PFmem resources to check if the EBDA allocated
16571da177e4SLinus Torvalds * pfmem out of memory buckets of the bus. If so, it will change the range numbers
16581da177e4SLinus Torvalds * and a flag to indicate that this resource is out of memory. It will also move the
16591da177e4SLinus Torvalds * Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create
16601da177e4SLinus Torvalds * a new Mem node
16611da177e4SLinus Torvalds * This routine is called right after initialization
16621da177e4SLinus Torvalds *******************************************************************************/
once_over(void)16631da177e4SLinus Torvalds static int __init once_over(void)
16641da177e4SLinus Torvalds {
16651da177e4SLinus Torvalds struct resource_node *pfmem_cur;
16661da177e4SLinus Torvalds struct resource_node *pfmem_prev;
16671da177e4SLinus Torvalds struct resource_node *mem;
16681da177e4SLinus Torvalds struct bus_node *bus_cur;
16691da177e4SLinus Torvalds
16702ac83cccSGeliang Tang list_for_each_entry(bus_cur, &gbuses, bus_list) {
16711da177e4SLinus Torvalds if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) {
16721da177e4SLinus Torvalds for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) {
1673dc6712d1SKristen Accardi pfmem_cur->fromMem = 1;
16741da177e4SLinus Torvalds if (pfmem_prev)
16751da177e4SLinus Torvalds pfmem_prev->next = pfmem_cur->next;
16761da177e4SLinus Torvalds else
16771da177e4SLinus Torvalds bus_cur->firstPFMem = pfmem_cur->next;
16781da177e4SLinus Torvalds
16791da177e4SLinus Torvalds if (!bus_cur->firstPFMemFromMem)
16801da177e4SLinus Torvalds pfmem_cur->next = NULL;
16811da177e4SLinus Torvalds else
16821da177e4SLinus Torvalds /* we don't need to sort PFMemFromMem since we're using mem node for
16831da177e4SLinus Torvalds all the real work anyways, so just insert at the beginning of the
16841da177e4SLinus Torvalds list
16851da177e4SLinus Torvalds */
16861da177e4SLinus Torvalds pfmem_cur->next = bus_cur->firstPFMemFromMem;
16871da177e4SLinus Torvalds
16881da177e4SLinus Torvalds bus_cur->firstPFMemFromMem = pfmem_cur;
16891da177e4SLinus Torvalds
1690f5afe806SEric Sesterhenn mem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
1691c7abb235SMarkus Elfring if (!mem)
16921da177e4SLinus Torvalds return -ENOMEM;
1693c7abb235SMarkus Elfring
16941da177e4SLinus Torvalds mem->type = MEM;
16951da177e4SLinus Torvalds mem->busno = pfmem_cur->busno;
16961da177e4SLinus Torvalds mem->devfunc = pfmem_cur->devfunc;
16971da177e4SLinus Torvalds mem->start = pfmem_cur->start;
16981da177e4SLinus Torvalds mem->end = pfmem_cur->end;
16991da177e4SLinus Torvalds mem->len = pfmem_cur->len;
17001da177e4SLinus Torvalds if (ibmphp_add_resource(mem) < 0)
17011da177e4SLinus Torvalds err("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n");
17021da177e4SLinus Torvalds pfmem_cur->rangeno = mem->rangeno;
17031da177e4SLinus Torvalds } /* end for pfmem */
17041da177e4SLinus Torvalds } /* end if */
17051da177e4SLinus Torvalds } /* end list_for_each bus */
17061da177e4SLinus Torvalds return 0;
17071da177e4SLinus Torvalds }
17081da177e4SLinus Torvalds
ibmphp_add_pfmem_from_mem(struct resource_node * pfmem)17091da177e4SLinus Torvalds int ibmphp_add_pfmem_from_mem(struct resource_node *pfmem)
17101da177e4SLinus Torvalds {
17111da177e4SLinus Torvalds struct bus_node *bus_cur = find_bus_wprev(pfmem->busno, NULL, 0);
17121da177e4SLinus Torvalds
17131da177e4SLinus Torvalds if (!bus_cur) {
17141da177e4SLinus Torvalds err("cannot find bus of pfmem to add...\n");
17151da177e4SLinus Torvalds return -ENODEV;
17161da177e4SLinus Torvalds }
17171da177e4SLinus Torvalds
17181da177e4SLinus Torvalds if (bus_cur->firstPFMemFromMem)
17191da177e4SLinus Torvalds pfmem->next = bus_cur->firstPFMemFromMem;
17201da177e4SLinus Torvalds else
17211da177e4SLinus Torvalds pfmem->next = NULL;
17221da177e4SLinus Torvalds
17231da177e4SLinus Torvalds bus_cur->firstPFMemFromMem = pfmem;
17241da177e4SLinus Torvalds
17251da177e4SLinus Torvalds return 0;
17261da177e4SLinus Torvalds }
17271da177e4SLinus Torvalds
17281da177e4SLinus Torvalds /* This routine just goes through the buses to see if the bus already exists.
17291da177e4SLinus Torvalds * It is called from ibmphp_find_sec_number, to find out a secondary bus number for
17301da177e4SLinus Torvalds * bridged cards
17311da177e4SLinus Torvalds * Parameters: bus_number
17321da177e4SLinus Torvalds * Returns: Bus pointer or NULL
17331da177e4SLinus Torvalds */
ibmphp_find_res_bus(u8 bus_number)17341da177e4SLinus Torvalds struct bus_node *ibmphp_find_res_bus(u8 bus_number)
17351da177e4SLinus Torvalds {
17361da177e4SLinus Torvalds return find_bus_wprev(bus_number, NULL, 0);
17371da177e4SLinus Torvalds }
17381da177e4SLinus Torvalds
find_bus_wprev(u8 bus_number,struct bus_node ** prev,u8 flag)17391da177e4SLinus Torvalds static struct bus_node *find_bus_wprev(u8 bus_number, struct bus_node **prev, u8 flag)
17401da177e4SLinus Torvalds {
17411da177e4SLinus Torvalds struct bus_node *bus_cur;
17421da177e4SLinus Torvalds
17432ac83cccSGeliang Tang list_for_each_entry(bus_cur, &gbuses, bus_list) {
17441da177e4SLinus Torvalds if (flag)
17452ac83cccSGeliang Tang *prev = list_prev_entry(bus_cur, bus_list);
17461da177e4SLinus Torvalds if (bus_cur->busno == bus_number)
17471da177e4SLinus Torvalds return bus_cur;
17481da177e4SLinus Torvalds }
17491da177e4SLinus Torvalds
17501da177e4SLinus Torvalds return NULL;
17511da177e4SLinus Torvalds }
17521da177e4SLinus Torvalds
ibmphp_print_test(void)17531da177e4SLinus Torvalds void ibmphp_print_test(void)
17541da177e4SLinus Torvalds {
17551da177e4SLinus Torvalds int i = 0;
17561da177e4SLinus Torvalds struct bus_node *bus_cur = NULL;
17571da177e4SLinus Torvalds struct range_node *range;
17581da177e4SLinus Torvalds struct resource_node *res;
17591da177e4SLinus Torvalds
17601da177e4SLinus Torvalds debug_pci("*****************START**********************\n");
17611da177e4SLinus Torvalds
17621da177e4SLinus Torvalds if ((!list_empty(&gbuses)) && flags) {
17631da177e4SLinus Torvalds err("The GBUSES is not NULL?!?!?!?!?\n");
17641da177e4SLinus Torvalds return;
17651da177e4SLinus Torvalds }
17661da177e4SLinus Torvalds
17672ac83cccSGeliang Tang list_for_each_entry(bus_cur, &gbuses, bus_list) {
17681da177e4SLinus Torvalds debug_pci ("This is bus # %d. There are\n", bus_cur->busno);
17691da177e4SLinus Torvalds debug_pci ("IORanges = %d\t", bus_cur->noIORanges);
17701da177e4SLinus Torvalds debug_pci ("MemRanges = %d\t", bus_cur->noMemRanges);
17711da177e4SLinus Torvalds debug_pci ("PFMemRanges = %d\n", bus_cur->noPFMemRanges);
17721da177e4SLinus Torvalds debug_pci ("The IO Ranges are as follows:\n");
17731da177e4SLinus Torvalds if (bus_cur->rangeIO) {
17741da177e4SLinus Torvalds range = bus_cur->rangeIO;
17751da177e4SLinus Torvalds for (i = 0; i < bus_cur->noIORanges; i++) {
17761da177e4SLinus Torvalds debug_pci("rangeno is %d\n", range->rangeno);
17771da177e4SLinus Torvalds debug_pci("[%x - %x]\n", range->start, range->end);
17781da177e4SLinus Torvalds range = range->next;
17791da177e4SLinus Torvalds }
17801da177e4SLinus Torvalds }
17811da177e4SLinus Torvalds
17821da177e4SLinus Torvalds debug_pci("The Mem Ranges are as follows:\n");
17831da177e4SLinus Torvalds if (bus_cur->rangeMem) {
17841da177e4SLinus Torvalds range = bus_cur->rangeMem;
17851da177e4SLinus Torvalds for (i = 0; i < bus_cur->noMemRanges; i++) {
17861da177e4SLinus Torvalds debug_pci("rangeno is %d\n", range->rangeno);
17871da177e4SLinus Torvalds debug_pci("[%x - %x]\n", range->start, range->end);
17881da177e4SLinus Torvalds range = range->next;
17891da177e4SLinus Torvalds }
17901da177e4SLinus Torvalds }
17911da177e4SLinus Torvalds
17921da177e4SLinus Torvalds debug_pci("The PFMem Ranges are as follows:\n");
17931da177e4SLinus Torvalds
17941da177e4SLinus Torvalds if (bus_cur->rangePFMem) {
17951da177e4SLinus Torvalds range = bus_cur->rangePFMem;
17961da177e4SLinus Torvalds for (i = 0; i < bus_cur->noPFMemRanges; i++) {
17971da177e4SLinus Torvalds debug_pci("rangeno is %d\n", range->rangeno);
17981da177e4SLinus Torvalds debug_pci("[%x - %x]\n", range->start, range->end);
17991da177e4SLinus Torvalds range = range->next;
18001da177e4SLinus Torvalds }
18011da177e4SLinus Torvalds }
18021da177e4SLinus Torvalds
18031da177e4SLinus Torvalds debug_pci("The resources on this bus are as follows\n");
18041da177e4SLinus Torvalds
18051da177e4SLinus Torvalds debug_pci("IO...\n");
18061da177e4SLinus Torvalds if (bus_cur->firstIO) {
18071da177e4SLinus Torvalds res = bus_cur->firstIO;
18081da177e4SLinus Torvalds while (res) {
18091da177e4SLinus Torvalds debug_pci("The range # is %d\n", res->rangeno);
18101da177e4SLinus Torvalds debug_pci("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
18111da177e4SLinus Torvalds debug_pci("[%x - %x], len=%x\n", res->start, res->end, res->len);
18121da177e4SLinus Torvalds if (res->next)
18131da177e4SLinus Torvalds res = res->next;
18141da177e4SLinus Torvalds else if (res->nextRange)
18151da177e4SLinus Torvalds res = res->nextRange;
18161da177e4SLinus Torvalds else
18171da177e4SLinus Torvalds break;
18181da177e4SLinus Torvalds }
18191da177e4SLinus Torvalds }
18201da177e4SLinus Torvalds debug_pci("Mem...\n");
18211da177e4SLinus Torvalds if (bus_cur->firstMem) {
18221da177e4SLinus Torvalds res = bus_cur->firstMem;
18231da177e4SLinus Torvalds while (res) {
18241da177e4SLinus Torvalds debug_pci("The range # is %d\n", res->rangeno);
18251da177e4SLinus Torvalds debug_pci("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
18261da177e4SLinus Torvalds debug_pci("[%x - %x], len=%x\n", res->start, res->end, res->len);
18271da177e4SLinus Torvalds if (res->next)
18281da177e4SLinus Torvalds res = res->next;
18291da177e4SLinus Torvalds else if (res->nextRange)
18301da177e4SLinus Torvalds res = res->nextRange;
18311da177e4SLinus Torvalds else
18321da177e4SLinus Torvalds break;
18331da177e4SLinus Torvalds }
18341da177e4SLinus Torvalds }
18351da177e4SLinus Torvalds debug_pci("PFMem...\n");
18361da177e4SLinus Torvalds if (bus_cur->firstPFMem) {
18371da177e4SLinus Torvalds res = bus_cur->firstPFMem;
18381da177e4SLinus Torvalds while (res) {
18391da177e4SLinus Torvalds debug_pci("The range # is %d\n", res->rangeno);
18401da177e4SLinus Torvalds debug_pci("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
18411da177e4SLinus Torvalds debug_pci("[%x - %x], len=%x\n", res->start, res->end, res->len);
18421da177e4SLinus Torvalds if (res->next)
18431da177e4SLinus Torvalds res = res->next;
18441da177e4SLinus Torvalds else if (res->nextRange)
18451da177e4SLinus Torvalds res = res->nextRange;
18461da177e4SLinus Torvalds else
18471da177e4SLinus Torvalds break;
18481da177e4SLinus Torvalds }
18491da177e4SLinus Torvalds }
18501da177e4SLinus Torvalds
18511da177e4SLinus Torvalds debug_pci("PFMemFromMem...\n");
18521da177e4SLinus Torvalds if (bus_cur->firstPFMemFromMem) {
18531da177e4SLinus Torvalds res = bus_cur->firstPFMemFromMem;
18541da177e4SLinus Torvalds while (res) {
18551da177e4SLinus Torvalds debug_pci("The range # is %d\n", res->rangeno);
18561da177e4SLinus Torvalds debug_pci("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
18571da177e4SLinus Torvalds debug_pci("[%x - %x], len=%x\n", res->start, res->end, res->len);
18581da177e4SLinus Torvalds res = res->next;
18591da177e4SLinus Torvalds }
18601da177e4SLinus Torvalds }
18611da177e4SLinus Torvalds }
18621da177e4SLinus Torvalds debug_pci("***********************END***********************\n");
18631da177e4SLinus Torvalds }
18641da177e4SLinus Torvalds
range_exists_already(struct range_node * range,struct bus_node * bus_cur,u8 type)18651da177e4SLinus Torvalds static int range_exists_already(struct range_node *range, struct bus_node *bus_cur, u8 type)
18661da177e4SLinus Torvalds {
18671da177e4SLinus Torvalds struct range_node *range_cur = NULL;
18681da177e4SLinus Torvalds switch (type) {
18691da177e4SLinus Torvalds case IO:
18701da177e4SLinus Torvalds range_cur = bus_cur->rangeIO;
18711da177e4SLinus Torvalds break;
18721da177e4SLinus Torvalds case MEM:
18731da177e4SLinus Torvalds range_cur = bus_cur->rangeMem;
18741da177e4SLinus Torvalds break;
18751da177e4SLinus Torvalds case PFMEM:
18761da177e4SLinus Torvalds range_cur = bus_cur->rangePFMem;
18771da177e4SLinus Torvalds break;
18781da177e4SLinus Torvalds default:
18791da177e4SLinus Torvalds err("wrong type passed to find out if range already exists\n");
18801da177e4SLinus Torvalds return -ENODEV;
18811da177e4SLinus Torvalds }
18821da177e4SLinus Torvalds
18831da177e4SLinus Torvalds while (range_cur) {
18841da177e4SLinus Torvalds if ((range_cur->start == range->start) && (range_cur->end == range->end))
18851da177e4SLinus Torvalds return 1;
18861da177e4SLinus Torvalds range_cur = range_cur->next;
18871da177e4SLinus Torvalds }
18881da177e4SLinus Torvalds
18891da177e4SLinus Torvalds return 0;
18901da177e4SLinus Torvalds }
18911da177e4SLinus Torvalds
18921da177e4SLinus Torvalds /* This routine will read the windows for any PPB we have and update the
18931da177e4SLinus Torvalds * range info for the secondary bus, and will also input this info into
18941da177e4SLinus Torvalds * primary bus, since BIOS doesn't. This is for PPB that are in the system
18951da177e4SLinus Torvalds * on bootup. For bridged cards that were added during previous load of the
18961da177e4SLinus Torvalds * driver, only the ranges and the bus structure are added, the devices are
18971da177e4SLinus Torvalds * added from NVRAM
18981da177e4SLinus Torvalds * Input: primary busno
18991da177e4SLinus Torvalds * Returns: none
19001da177e4SLinus Torvalds * Note: this function doesn't take into account IO restrictions etc,
19011da177e4SLinus Torvalds * so will only work for bridges with no video/ISA devices behind them It
1902f7625980SBjorn Helgaas * also will not work for onboard PPBs that can have more than 1 *bus
19031da177e4SLinus Torvalds * behind them All these are TO DO.
19041da177e4SLinus Torvalds * Also need to add more error checkings... (from fnc returns etc)
19051da177e4SLinus Torvalds */
update_bridge_ranges(struct bus_node ** bus)19061da177e4SLinus Torvalds static int __init update_bridge_ranges(struct bus_node **bus)
19071da177e4SLinus Torvalds {
19081da177e4SLinus Torvalds u8 sec_busno, device, function, hdr_type, start_io_address, end_io_address;
19091da177e4SLinus Torvalds u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address;
19101da177e4SLinus Torvalds u32 start_address, end_address, upper_start, upper_end;
19111da177e4SLinus Torvalds struct bus_node *bus_sec;
19121da177e4SLinus Torvalds struct bus_node *bus_cur;
19131da177e4SLinus Torvalds struct resource_node *io;
19141da177e4SLinus Torvalds struct resource_node *mem;
19151da177e4SLinus Torvalds struct resource_node *pfmem;
19161da177e4SLinus Torvalds struct range_node *range;
19171da177e4SLinus Torvalds unsigned int devfn;
19181da177e4SLinus Torvalds
19191da177e4SLinus Torvalds bus_cur = *bus;
19201da177e4SLinus Torvalds if (!bus_cur)
19211da177e4SLinus Torvalds return -ENODEV;
19221da177e4SLinus Torvalds ibmphp_pci_bus->number = bus_cur->busno;
19231da177e4SLinus Torvalds
192466bef8c0SHarvey Harrison debug("inside %s\n", __func__);
19251da177e4SLinus Torvalds debug("bus_cur->busno = %x\n", bus_cur->busno);
19261da177e4SLinus Torvalds
19271da177e4SLinus Torvalds for (device = 0; device < 32; device++) {
19281da177e4SLinus Torvalds for (function = 0x00; function < 0x08; function++) {
19291da177e4SLinus Torvalds devfn = PCI_DEVFN(device, function);
19301da177e4SLinus Torvalds pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
19311da177e4SLinus Torvalds
19321da177e4SLinus Torvalds if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
19331da177e4SLinus Torvalds /* found correct device!!! */
19341da177e4SLinus Torvalds pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
19351da177e4SLinus Torvalds
19361da177e4SLinus Torvalds switch (hdr_type) {
19371da177e4SLinus Torvalds case PCI_HEADER_TYPE_NORMAL:
19381da177e4SLinus Torvalds function = 0x8;
19391da177e4SLinus Torvalds break;
19401da177e4SLinus Torvalds case PCI_HEADER_TYPE_MULTIDEVICE:
19411da177e4SLinus Torvalds break;
19421da177e4SLinus Torvalds case PCI_HEADER_TYPE_BRIDGE:
19431da177e4SLinus Torvalds function = 0x8;
1944df561f66SGustavo A. R. Silva fallthrough;
19451da177e4SLinus Torvalds case PCI_HEADER_TYPE_MULTIBRIDGE:
19461da177e4SLinus Torvalds /* We assume here that only 1 bus behind the bridge
19471da177e4SLinus Torvalds TO DO: add functionality for several:
19481da177e4SLinus Torvalds temp = secondary;
19491da177e4SLinus Torvalds while (temp < subordinate) {
19501da177e4SLinus Torvalds ...
19511da177e4SLinus Torvalds temp++;
19521da177e4SLinus Torvalds }
19531da177e4SLinus Torvalds */
19541da177e4SLinus Torvalds pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno);
19551da177e4SLinus Torvalds bus_sec = find_bus_wprev(sec_busno, NULL, 0);
19561da177e4SLinus Torvalds /* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */
19571da177e4SLinus Torvalds if (!bus_sec) {
1958*6a8fcf7dSBjorn Helgaas alloc_error_bus(NULL, sec_busno, 1);
19591da177e4SLinus Torvalds /* the rest will be populated during NVRAM call */
19601da177e4SLinus Torvalds return 0;
19611da177e4SLinus Torvalds }
19621da177e4SLinus Torvalds pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_IO_BASE, &start_io_address);
19631da177e4SLinus Torvalds pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &end_io_address);
19641da177e4SLinus Torvalds pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, &upper_io_start);
19651da177e4SLinus Torvalds pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, &upper_io_end);
19661da177e4SLinus Torvalds start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8;
19671da177e4SLinus Torvalds start_address |= (upper_io_start << 16);
19681da177e4SLinus Torvalds end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8;
19691da177e4SLinus Torvalds end_address |= (upper_io_end << 16);
19701da177e4SLinus Torvalds
19711da177e4SLinus Torvalds if ((start_address) && (start_address <= end_address)) {
1972f5afe806SEric Sesterhenn range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
1973c7abb235SMarkus Elfring if (!range)
19741da177e4SLinus Torvalds return -ENOMEM;
1975c7abb235SMarkus Elfring
19761da177e4SLinus Torvalds range->start = start_address;
19771da177e4SLinus Torvalds range->end = end_address + 0xfff;
19781da177e4SLinus Torvalds
19791da177e4SLinus Torvalds if (bus_sec->noIORanges > 0) {
19801da177e4SLinus Torvalds if (!range_exists_already(range, bus_sec, IO)) {
1981c85e4aaeSH. Peter Anvin add_bus_range(IO, range, bus_sec);
19821da177e4SLinus Torvalds ++bus_sec->noIORanges;
19831da177e4SLinus Torvalds } else {
19841da177e4SLinus Torvalds kfree(range);
19851da177e4SLinus Torvalds range = NULL;
19861da177e4SLinus Torvalds }
19871da177e4SLinus Torvalds } else {
19881da177e4SLinus Torvalds /* 1st IO Range on the bus */
19891da177e4SLinus Torvalds range->rangeno = 1;
19901da177e4SLinus Torvalds bus_sec->rangeIO = range;
19911da177e4SLinus Torvalds ++bus_sec->noIORanges;
19921da177e4SLinus Torvalds }
19931da177e4SLinus Torvalds fix_resources(bus_sec);
19941da177e4SLinus Torvalds
19951da177e4SLinus Torvalds if (ibmphp_find_resource(bus_cur, start_address, &io, IO)) {
1996f5afe806SEric Sesterhenn io = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
19971da177e4SLinus Torvalds if (!io) {
19981da177e4SLinus Torvalds kfree(range);
19991da177e4SLinus Torvalds return -ENOMEM;
20001da177e4SLinus Torvalds }
20011da177e4SLinus Torvalds io->type = IO;
20021da177e4SLinus Torvalds io->busno = bus_cur->busno;
20031da177e4SLinus Torvalds io->devfunc = ((device << 3) | (function & 0x7));
20041da177e4SLinus Torvalds io->start = start_address;
20051da177e4SLinus Torvalds io->end = end_address + 0xfff;
20061da177e4SLinus Torvalds io->len = io->end - io->start + 1;
20071da177e4SLinus Torvalds ibmphp_add_resource(io);
20081da177e4SLinus Torvalds }
20091da177e4SLinus Torvalds }
20101da177e4SLinus Torvalds
20111da177e4SLinus Torvalds pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address);
20121da177e4SLinus Torvalds pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address);
20131da177e4SLinus Torvalds
20141da177e4SLinus Torvalds start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
20151da177e4SLinus Torvalds end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
20161da177e4SLinus Torvalds
20171da177e4SLinus Torvalds if ((start_address) && (start_address <= end_address)) {
20181da177e4SLinus Torvalds
2019f5afe806SEric Sesterhenn range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
2020c7abb235SMarkus Elfring if (!range)
20211da177e4SLinus Torvalds return -ENOMEM;
2022c7abb235SMarkus Elfring
20231da177e4SLinus Torvalds range->start = start_address;
20241da177e4SLinus Torvalds range->end = end_address + 0xfffff;
20251da177e4SLinus Torvalds
20261da177e4SLinus Torvalds if (bus_sec->noMemRanges > 0) {
20271da177e4SLinus Torvalds if (!range_exists_already(range, bus_sec, MEM)) {
2028c85e4aaeSH. Peter Anvin add_bus_range(MEM, range, bus_sec);
20291da177e4SLinus Torvalds ++bus_sec->noMemRanges;
20301da177e4SLinus Torvalds } else {
20311da177e4SLinus Torvalds kfree(range);
20321da177e4SLinus Torvalds range = NULL;
20331da177e4SLinus Torvalds }
20341da177e4SLinus Torvalds } else {
20351da177e4SLinus Torvalds /* 1st Mem Range on the bus */
20361da177e4SLinus Torvalds range->rangeno = 1;
20371da177e4SLinus Torvalds bus_sec->rangeMem = range;
20381da177e4SLinus Torvalds ++bus_sec->noMemRanges;
20391da177e4SLinus Torvalds }
20401da177e4SLinus Torvalds
20411da177e4SLinus Torvalds fix_resources(bus_sec);
20421da177e4SLinus Torvalds
20431da177e4SLinus Torvalds if (ibmphp_find_resource(bus_cur, start_address, &mem, MEM)) {
2044f5afe806SEric Sesterhenn mem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
20451da177e4SLinus Torvalds if (!mem) {
20461da177e4SLinus Torvalds kfree(range);
20471da177e4SLinus Torvalds return -ENOMEM;
20481da177e4SLinus Torvalds }
20491da177e4SLinus Torvalds mem->type = MEM;
20501da177e4SLinus Torvalds mem->busno = bus_cur->busno;
20511da177e4SLinus Torvalds mem->devfunc = ((device << 3) | (function & 0x7));
20521da177e4SLinus Torvalds mem->start = start_address;
20531da177e4SLinus Torvalds mem->end = end_address + 0xfffff;
20541da177e4SLinus Torvalds mem->len = mem->end - mem->start + 1;
20551da177e4SLinus Torvalds ibmphp_add_resource(mem);
20561da177e4SLinus Torvalds }
20571da177e4SLinus Torvalds }
20581da177e4SLinus Torvalds pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &start_mem_address);
20591da177e4SLinus Torvalds pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &end_mem_address);
20601da177e4SLinus Torvalds pci_bus_read_config_dword(ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, &upper_start);
20611da177e4SLinus Torvalds pci_bus_read_config_dword(ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, &upper_end);
20621da177e4SLinus Torvalds start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
20631da177e4SLinus Torvalds end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
20641da177e4SLinus Torvalds #if BITS_PER_LONG == 64
20651da177e4SLinus Torvalds start_address |= ((long) upper_start) << 32;
20661da177e4SLinus Torvalds end_address |= ((long) upper_end) << 32;
20671da177e4SLinus Torvalds #endif
20681da177e4SLinus Torvalds
20691da177e4SLinus Torvalds if ((start_address) && (start_address <= end_address)) {
20701da177e4SLinus Torvalds
2071f5afe806SEric Sesterhenn range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
2072c7abb235SMarkus Elfring if (!range)
20731da177e4SLinus Torvalds return -ENOMEM;
2074c7abb235SMarkus Elfring
20751da177e4SLinus Torvalds range->start = start_address;
20761da177e4SLinus Torvalds range->end = end_address + 0xfffff;
20771da177e4SLinus Torvalds
20781da177e4SLinus Torvalds if (bus_sec->noPFMemRanges > 0) {
20791da177e4SLinus Torvalds if (!range_exists_already(range, bus_sec, PFMEM)) {
2080c85e4aaeSH. Peter Anvin add_bus_range(PFMEM, range, bus_sec);
20811da177e4SLinus Torvalds ++bus_sec->noPFMemRanges;
20821da177e4SLinus Torvalds } else {
20831da177e4SLinus Torvalds kfree(range);
20841da177e4SLinus Torvalds range = NULL;
20851da177e4SLinus Torvalds }
20861da177e4SLinus Torvalds } else {
20871da177e4SLinus Torvalds /* 1st PFMem Range on the bus */
20881da177e4SLinus Torvalds range->rangeno = 1;
20891da177e4SLinus Torvalds bus_sec->rangePFMem = range;
20901da177e4SLinus Torvalds ++bus_sec->noPFMemRanges;
20911da177e4SLinus Torvalds }
20921da177e4SLinus Torvalds
20931da177e4SLinus Torvalds fix_resources(bus_sec);
20941da177e4SLinus Torvalds if (ibmphp_find_resource(bus_cur, start_address, &pfmem, PFMEM)) {
2095f5afe806SEric Sesterhenn pfmem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
20961da177e4SLinus Torvalds if (!pfmem) {
20971da177e4SLinus Torvalds kfree(range);
20981da177e4SLinus Torvalds return -ENOMEM;
20991da177e4SLinus Torvalds }
21001da177e4SLinus Torvalds pfmem->type = PFMEM;
21011da177e4SLinus Torvalds pfmem->busno = bus_cur->busno;
21021da177e4SLinus Torvalds pfmem->devfunc = ((device << 3) | (function & 0x7));
21031da177e4SLinus Torvalds pfmem->start = start_address;
21041da177e4SLinus Torvalds pfmem->end = end_address + 0xfffff;
21051da177e4SLinus Torvalds pfmem->len = pfmem->end - pfmem->start + 1;
2106dc6712d1SKristen Accardi pfmem->fromMem = 0;
21071da177e4SLinus Torvalds
21081da177e4SLinus Torvalds ibmphp_add_resource(pfmem);
21091da177e4SLinus Torvalds }
21101da177e4SLinus Torvalds }
21111da177e4SLinus Torvalds break;
21121da177e4SLinus Torvalds } /* end of switch */
21131da177e4SLinus Torvalds } /* end if vendor */
21141da177e4SLinus Torvalds } /* end for function */
21151da177e4SLinus Torvalds } /* end for device */
21161da177e4SLinus Torvalds
21171da177e4SLinus Torvalds return 0;
21181da177e4SLinus Torvalds }
2119