2005-04-17 02:20:36 +04:00
/*
* IBM Hot Plug Controller Driver
*
* Written By : Irene Zubarev , IBM Corporation
*
* Copyright ( C ) 2001 Greg Kroah - Hartman ( greg @ kroah . com )
* Copyright ( C ) 2001 , 2002 IBM Corp .
*
* All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Send feedback to < gregkh @ us . ibm . com >
*
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/pci.h>
# include <linux/list.h>
# include <linux/init.h>
# include "ibmphp.h"
static int flags = 0 ; /* for testing */
static void update_resources ( struct bus_node * bus_cur , int type , int rangeno ) ;
static int once_over ( void ) ;
static int remove_ranges ( struct bus_node * , struct bus_node * ) ;
static int update_bridge_ranges ( struct bus_node * * ) ;
static int add_range ( int type , struct range_node * , struct bus_node * ) ;
static void fix_resources ( struct bus_node * ) ;
static struct bus_node * find_bus_wprev ( u8 , struct bus_node * * , u8 ) ;
static LIST_HEAD ( gbuses ) ;
static struct bus_node * __init alloc_error_bus ( struct ebda_pci_rsrc * curr , u8 busno , int flag )
{
struct bus_node * newbus ;
if ( ! ( curr ) & & ! ( flag ) ) {
err ( " NULL pointer passed \n " ) ;
return NULL ;
}
2006-02-28 17:34:49 +03:00
newbus = kzalloc ( sizeof ( struct bus_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! newbus ) {
err ( " out of system memory \n " ) ;
return NULL ;
}
if ( flag )
newbus - > busno = busno ;
else
newbus - > busno = curr - > bus_num ;
list_add_tail ( & newbus - > bus_list , & gbuses ) ;
return newbus ;
}
static struct resource_node * __init alloc_resources ( struct ebda_pci_rsrc * curr )
{
struct resource_node * rs ;
if ( ! curr ) {
err ( " NULL passed to allocate \n " ) ;
return NULL ;
}
2006-02-28 17:34:49 +03:00
rs = kzalloc ( sizeof ( struct resource_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! rs ) {
err ( " out of system memory \n " ) ;
return NULL ;
}
rs - > busno = curr - > bus_num ;
rs - > devfunc = curr - > dev_fun ;
rs - > start = curr - > start_addr ;
rs - > end = curr - > end_addr ;
rs - > len = curr - > end_addr - curr - > start_addr + 1 ;
return rs ;
}
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 )
{
struct bus_node * newbus ;
struct range_node * newrange ;
u8 num_ranges = 0 ;
if ( first_bus ) {
2006-02-28 17:34:49 +03:00
newbus = kzalloc ( sizeof ( struct bus_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! newbus ) {
err ( " out of system memory. \n " ) ;
return - ENOMEM ;
}
newbus - > busno = curr - > bus_num ;
} else {
newbus = * new_bus ;
switch ( flag ) {
case MEM :
num_ranges = newbus - > noMemRanges ;
break ;
case PFMEM :
num_ranges = newbus - > noPFMemRanges ;
break ;
case IO :
num_ranges = newbus - > noIORanges ;
break ;
}
}
2006-02-28 17:34:49 +03:00
newrange = kzalloc ( sizeof ( struct range_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! newrange ) {
if ( first_bus )
kfree ( newbus ) ;
err ( " out of system memory \n " ) ;
return - ENOMEM ;
}
newrange - > start = curr - > start_addr ;
newrange - > end = curr - > end_addr ;
if ( first_bus | | ( ! num_ranges ) )
newrange - > rangeno = 1 ;
else {
/* need to insert our range */
add_range ( flag , newrange , newbus ) ;
debug ( " %d resource Primary Bus inserted on bus %x [%x - %x] \n " , flag , newbus - > busno , newrange - > start , newrange - > end ) ;
}
switch ( flag ) {
case MEM :
newbus - > rangeMem = newrange ;
if ( first_bus )
newbus - > noMemRanges = 1 ;
else {
debug ( " First Memory Primary on bus %x, [%x - %x] \n " , newbus - > busno , newrange - > start , newrange - > end ) ;
+ + newbus - > noMemRanges ;
fix_resources ( newbus ) ;
}
break ;
case IO :
newbus - > rangeIO = newrange ;
if ( first_bus )
newbus - > noIORanges = 1 ;
else {
debug ( " First IO Primary on bus %x, [%x - %x] \n " , newbus - > busno , newrange - > start , newrange - > end ) ;
+ + newbus - > noIORanges ;
fix_resources ( newbus ) ;
}
break ;
case PFMEM :
newbus - > rangePFMem = newrange ;
if ( first_bus )
newbus - > noPFMemRanges = 1 ;
else {
debug ( " 1st PFMemory Primary on Bus %x [%x - %x] \n " , newbus - > busno , newrange - > start , newrange - > end ) ;
+ + newbus - > noPFMemRanges ;
fix_resources ( newbus ) ;
}
break ;
}
* new_bus = newbus ;
* new_range = newrange ;
return 0 ;
}
/* Notes:
* 1. The ranges are ordered . The buses are not ordered . ( First come )
*
* 2. If cannot allocate out of PFMem range , allocate from Mem ranges . PFmemFromMem
* are not sorted . ( no need since use mem node ) . To not change the entire code , we
* also add mem node whenever this case happens so as not to change
* ibmphp_check_mem_resource etc ( and since it really is taking Mem resource )
*/
/*****************************************************************************
* This is the Resource Management initialization function . It will go through
* the Resource list taken from EBDA and fill in this module ' s data structures
*
* THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES ,
* SINCE WE ' RE GOING TO ASSUME FOR NOW WE DON ' T HAVE THOSE ON OUR BUSES FOR NOW
*
* Input : ptr to the head of the resource list from EBDA
* Output : 0 , - 1 or error codes
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int __init ibmphp_rsrc_init ( void )
{
struct ebda_pci_rsrc * curr ;
struct range_node * newrange = NULL ;
struct bus_node * newbus = NULL ;
struct bus_node * bus_cur ;
struct bus_node * bus_prev ;
struct list_head * tmp ;
struct resource_node * new_io = NULL ;
struct resource_node * new_mem = NULL ;
struct resource_node * new_pfmem = NULL ;
int rc ;
struct list_head * tmp_ebda ;
list_for_each ( tmp_ebda , & ibmphp_ebda_pci_rsrc_head ) {
curr = list_entry ( tmp_ebda , struct ebda_pci_rsrc , ebda_pci_rsrc_list ) ;
if ( ! ( curr - > rsrc_type & PCIDEVMASK ) ) {
/* EBDA still lists non PCI devices, so ignore... */
debug ( " this is not a PCI DEVICE in rsrc_init, please take care \n " ) ;
// continue;
}
/* this is a primary bus resource */
if ( curr - > rsrc_type & PRIMARYBUSMASK ) {
/* memory */
if ( ( curr - > rsrc_type & RESTYPE ) = = MMASK ) {
/* no bus structure exists in place yet */
if ( list_empty ( & gbuses ) ) {
if ( ( rc = alloc_bus_range ( & newbus , & newrange , curr , MEM , 1 ) ) )
return rc ;
list_add_tail ( & newbus - > bus_list , & gbuses ) ;
debug ( " gbuses = NULL, Memory Primary Bus %x [%x - %x] \n " , newbus - > busno , newrange - > start , newrange - > end ) ;
} else {
bus_cur = find_bus_wprev ( curr - > bus_num , & bus_prev , 1 ) ;
/* found our bus */
if ( bus_cur ) {
rc = alloc_bus_range ( & bus_cur , & newrange , curr , MEM , 0 ) ;
if ( rc )
return rc ;
} else {
/* went through all the buses and didn't find ours, need to create a new bus node */
if ( ( rc = alloc_bus_range ( & newbus , & newrange , curr , MEM , 1 ) ) )
return rc ;
list_add_tail ( & newbus - > bus_list , & gbuses ) ;
debug ( " New Bus, Memory Primary Bus %x [%x - %x] \n " , newbus - > busno , newrange - > start , newrange - > end ) ;
}
}
} else if ( ( curr - > rsrc_type & RESTYPE ) = = PFMASK ) {
/* prefetchable memory */
if ( list_empty ( & gbuses ) ) {
/* no bus structure exists in place yet */
if ( ( rc = alloc_bus_range ( & newbus , & newrange , curr , PFMEM , 1 ) ) )
return rc ;
list_add_tail ( & newbus - > bus_list , & gbuses ) ;
debug ( " gbuses = NULL, PFMemory Primary Bus %x [%x - %x] \n " , newbus - > busno , newrange - > start , newrange - > end ) ;
} else {
bus_cur = find_bus_wprev ( curr - > bus_num , & bus_prev , 1 ) ;
if ( bus_cur ) {
/* found our bus */
rc = alloc_bus_range ( & bus_cur , & newrange , curr , PFMEM , 0 ) ;
if ( rc )
return rc ;
} else {
/* went through all the buses and didn't find ours, need to create a new bus node */
if ( ( rc = alloc_bus_range ( & newbus , & newrange , curr , PFMEM , 1 ) ) )
return rc ;
list_add_tail ( & newbus - > bus_list , & gbuses ) ;
debug ( " 1st Bus, PFMemory Primary Bus %x [%x - %x] \n " , newbus - > busno , newrange - > start , newrange - > end ) ;
}
}
} else if ( ( curr - > rsrc_type & RESTYPE ) = = IOMASK ) {
/* IO */
if ( list_empty ( & gbuses ) ) {
/* no bus structure exists in place yet */
if ( ( rc = alloc_bus_range ( & newbus , & newrange , curr , IO , 1 ) ) )
return rc ;
list_add_tail ( & newbus - > bus_list , & gbuses ) ;
debug ( " gbuses = NULL, IO Primary Bus %x [%x - %x] \n " , newbus - > busno , newrange - > start , newrange - > end ) ;
} else {
bus_cur = find_bus_wprev ( curr - > bus_num , & bus_prev , 1 ) ;
if ( bus_cur ) {
rc = alloc_bus_range ( & bus_cur , & newrange , curr , IO , 0 ) ;
if ( rc )
return rc ;
} else {
/* went through all the buses and didn't find ours, need to create a new bus node */
if ( ( rc = alloc_bus_range ( & newbus , & newrange , curr , IO , 1 ) ) )
return rc ;
list_add_tail ( & newbus - > bus_list , & gbuses ) ;
debug ( " 1st Bus, IO Primary Bus %x [%x - %x] \n " , newbus - > busno , newrange - > start , newrange - > end ) ;
}
}
} else {
; /* type is reserved WHAT TO DO IN THIS CASE???
NOTHING TO DO ? ? ? */
}
} else {
/* regular pci device resource */
if ( ( curr - > rsrc_type & RESTYPE ) = = MMASK ) {
/* Memory resource */
new_mem = alloc_resources ( curr ) ;
if ( ! new_mem )
return - ENOMEM ;
new_mem - > type = MEM ;
/*
* if it didn ' t find the bus , means PCI dev
* came b4 the Primary Bus info , so need to
* create a bus rangeno becomes a problem . . .
* assign a - 1 and then update once the range
* actually appears . . .
*/
if ( ibmphp_add_resource ( new_mem ) < 0 ) {
newbus = alloc_error_bus ( curr , 0 , 0 ) ;
if ( ! newbus )
return - ENOMEM ;
newbus - > firstMem = new_mem ;
+ + newbus - > needMemUpdate ;
new_mem - > rangeno = - 1 ;
}
debug ( " Memory resource for device %x, bus %x, [%x - %x] \n " , new_mem - > devfunc , new_mem - > busno , new_mem - > start , new_mem - > end ) ;
} else if ( ( curr - > rsrc_type & RESTYPE ) = = PFMASK ) {
/* PFMemory resource */
new_pfmem = alloc_resources ( curr ) ;
if ( ! new_pfmem )
return - ENOMEM ;
new_pfmem - > type = PFMEM ;
2006-03-15 03:24:47 +03:00
new_pfmem - > fromMem = 0 ;
2005-04-17 02:20:36 +04:00
if ( ibmphp_add_resource ( new_pfmem ) < 0 ) {
newbus = alloc_error_bus ( curr , 0 , 0 ) ;
if ( ! newbus )
return - ENOMEM ;
newbus - > firstPFMem = new_pfmem ;
+ + newbus - > needPFMemUpdate ;
new_pfmem - > rangeno = - 1 ;
}
debug ( " PFMemory resource for device %x, bus %x, [%x - %x] \n " , new_pfmem - > devfunc , new_pfmem - > busno , new_pfmem - > start , new_pfmem - > end ) ;
} else if ( ( curr - > rsrc_type & RESTYPE ) = = IOMASK ) {
/* IO resource */
new_io = alloc_resources ( curr ) ;
if ( ! new_io )
return - ENOMEM ;
new_io - > type = IO ;
/*
* if it didn ' t find the bus , means PCI dev
* came b4 the Primary Bus info , so need to
* create a bus rangeno becomes a problem . . .
* Can assign a - 1 and then update once the
* range actually appears . . .
*/
if ( ibmphp_add_resource ( new_io ) < 0 ) {
newbus = alloc_error_bus ( curr , 0 , 0 ) ;
if ( ! newbus )
return - ENOMEM ;
newbus - > firstIO = new_io ;
+ + newbus - > needIOUpdate ;
new_io - > rangeno = - 1 ;
}
debug ( " IO resource for device %x, bus %x, [%x - %x] \n " , new_io - > devfunc , new_io - > busno , new_io - > start , new_io - > end ) ;
}
}
}
list_for_each ( tmp , & gbuses ) {
bus_cur = list_entry ( tmp , struct bus_node , bus_list ) ;
/* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */
rc = update_bridge_ranges ( & bus_cur ) ;
if ( rc )
return rc ;
}
rc = once_over ( ) ; /* This is to align ranges (so no -1) */
if ( rc )
return rc ;
return 0 ;
}
/********************************************************************************
* This function adds a range into a sorted list of ranges per bus for a particular
* range type , it then calls another routine to update the range numbers on the
* pci devices ' resources for the appropriate resource
*
* Input : type of the resource , range to add , current bus
* Output : 0 or - 1 , bus and range ptrs
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int add_range ( int type , struct range_node * range , struct bus_node * bus_cur )
{
struct range_node * range_cur = NULL ;
struct range_node * range_prev ;
int count = 0 , i_init ;
int noRanges = 0 ;
switch ( type ) {
case MEM :
range_cur = bus_cur - > rangeMem ;
noRanges = bus_cur - > noMemRanges ;
break ;
case PFMEM :
range_cur = bus_cur - > rangePFMem ;
noRanges = bus_cur - > noPFMemRanges ;
break ;
case IO :
range_cur = bus_cur - > rangeIO ;
noRanges = bus_cur - > noIORanges ;
break ;
}
range_prev = NULL ;
while ( range_cur ) {
if ( range - > start < range_cur - > start )
break ;
range_prev = range_cur ;
range_cur = range_cur - > next ;
count = count + 1 ;
}
if ( ! count ) {
/* our range will go at the beginning of the list */
switch ( type ) {
case MEM :
bus_cur - > rangeMem = range ;
break ;
case PFMEM :
bus_cur - > rangePFMem = range ;
break ;
case IO :
bus_cur - > rangeIO = range ;
break ;
}
range - > next = range_cur ;
range - > rangeno = 1 ;
i_init = 0 ;
} else if ( ! range_cur ) {
/* our range will go at the end of the list */
range - > next = NULL ;
range_prev - > next = range ;
range - > rangeno = range_prev - > rangeno + 1 ;
return 0 ;
} else {
/* the range is in the middle */
range_prev - > next = range ;
range - > next = range_cur ;
range - > rangeno = range_cur - > rangeno ;
i_init = range_prev - > rangeno ;
}
for ( count = i_init ; count < noRanges ; + + count ) {
+ + range_cur - > rangeno ;
range_cur = range_cur - > next ;
}
update_resources ( bus_cur , type , i_init + 1 ) ;
return 0 ;
}
/*******************************************************************************
* This routine goes through the list of resources of type ' type ' and updates
* the range numbers that they correspond to . It was called from add_range fnc
*
* Input : bus , type of the resource , the rangeno starting from which to update
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void update_resources ( struct bus_node * bus_cur , int type , int rangeno )
{
struct resource_node * res = NULL ;
2006-03-15 03:24:47 +03:00
u8 eol = 0 ; /* end of list indicator */
2005-04-17 02:20:36 +04:00
switch ( type ) {
case MEM :
if ( bus_cur - > firstMem )
res = bus_cur - > firstMem ;
break ;
case PFMEM :
if ( bus_cur - > firstPFMem )
res = bus_cur - > firstPFMem ;
break ;
case IO :
if ( bus_cur - > firstIO )
res = bus_cur - > firstIO ;
break ;
}
if ( res ) {
while ( res ) {
if ( res - > rangeno = = rangeno )
break ;
if ( res - > next )
res = res - > next ;
else if ( res - > nextRange )
res = res - > nextRange ;
else {
2006-03-15 03:24:47 +03:00
eol = 1 ;
2005-04-17 02:20:36 +04:00
break ;
}
}
if ( ! eol ) {
/* found the range */
while ( res ) {
+ + res - > rangeno ;
res = res - > next ;
}
}
}
}
static void fix_me ( struct resource_node * res , struct bus_node * bus_cur , struct range_node * range )
{
char * str = " " ;
switch ( res - > type ) {
case IO :
str = " io " ;
break ;
case MEM :
str = " mem " ;
break ;
case PFMEM :
str = " pfmem " ;
break ;
}
while ( res ) {
if ( res - > rangeno = = - 1 ) {
while ( range ) {
if ( ( res - > start > = range - > start ) & & ( res - > end < = range - > end ) ) {
res - > rangeno = range - > rangeno ;
debug ( " %s->rangeno in fix_resources is %d \n " , str , res - > rangeno ) ;
switch ( res - > type ) {
case IO :
- - bus_cur - > needIOUpdate ;
break ;
case MEM :
- - bus_cur - > needMemUpdate ;
break ;
case PFMEM :
- - bus_cur - > needPFMemUpdate ;
break ;
}
break ;
}
range = range - > next ;
}
}
if ( res - > next )
res = res - > next ;
else
res = res - > nextRange ;
}
}
/*****************************************************************************
* This routine reassigns the range numbers to the resources that had a - 1
* This case can happen only if upon initialization , resources taken by pci dev
* appear in EBDA before the resources allocated for that bus , since we don ' t
* know the range , we assign - 1 , and this routine is called after a new range
* is assigned to see the resources with unknown range belong to the added range
*
* Input : current bus
* Output : none , list of resources for that bus are fixed if can be
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void fix_resources ( struct bus_node * bus_cur )
{
struct range_node * range ;
struct resource_node * res ;
debug ( " %s - bus_cur->busno = %d \n " , __FUNCTION__ , bus_cur - > busno ) ;
if ( bus_cur - > needIOUpdate ) {
res = bus_cur - > firstIO ;
range = bus_cur - > rangeIO ;
fix_me ( res , bus_cur , range ) ;
}
if ( bus_cur - > needMemUpdate ) {
res = bus_cur - > firstMem ;
range = bus_cur - > rangeMem ;
fix_me ( res , bus_cur , range ) ;
}
if ( bus_cur - > needPFMemUpdate ) {
res = bus_cur - > firstPFMem ;
range = bus_cur - > rangePFMem ;
fix_me ( res , bus_cur , range ) ;
}
}
/*******************************************************************************
* This routine adds a resource to the list of resources to the appropriate bus
* based on their resource type and sorted by their starting addresses . It assigns
* the ptrs to next and nextRange if needed .
*
* Input : resource ptr
* Output : ptrs assigned ( to the node )
* 0 or - 1
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int ibmphp_add_resource ( struct resource_node * res )
{
struct resource_node * res_cur ;
struct resource_node * res_prev ;
struct bus_node * bus_cur ;
struct range_node * range_cur = NULL ;
struct resource_node * res_start = NULL ;
debug ( " %s - enter \n " , __FUNCTION__ ) ;
if ( ! res ) {
err ( " NULL passed to add \n " ) ;
return - ENODEV ;
}
bus_cur = find_bus_wprev ( res - > busno , NULL , 0 ) ;
if ( ! bus_cur ) {
/* didn't find a bus, smth's wrong!!! */
debug ( " no bus in the system, either pci_dev's wrong or allocation failed \n " ) ;
return - ENODEV ;
}
/* Normal case */
switch ( res - > type ) {
case IO :
range_cur = bus_cur - > rangeIO ;
res_start = bus_cur - > firstIO ;
break ;
case MEM :
range_cur = bus_cur - > rangeMem ;
res_start = bus_cur - > firstMem ;
break ;
case PFMEM :
range_cur = bus_cur - > rangePFMem ;
res_start = bus_cur - > firstPFMem ;
break ;
default :
err ( " cannot read the type of the resource to add... problem \n " ) ;
return - EINVAL ;
}
while ( range_cur ) {
if ( ( res - > start > = range_cur - > start ) & & ( res - > end < = range_cur - > end ) ) {
res - > rangeno = range_cur - > rangeno ;
break ;
}
range_cur = range_cur - > next ;
}
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* this is again the case of rangeno = - 1
* ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
*/
if ( ! range_cur ) {
switch ( res - > type ) {
case IO :
+ + bus_cur - > needIOUpdate ;
break ;
case MEM :
+ + bus_cur - > needMemUpdate ;
break ;
case PFMEM :
+ + bus_cur - > needPFMemUpdate ;
break ;
}
res - > rangeno = - 1 ;
}
debug ( " The range is %d \n " , res - > rangeno ) ;
if ( ! res_start ) {
/* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */
switch ( res - > type ) {
case IO :
bus_cur - > firstIO = res ;
break ;
case MEM :
bus_cur - > firstMem = res ;
break ;
case PFMEM :
bus_cur - > firstPFMem = res ;
break ;
}
res - > next = NULL ;
res - > nextRange = NULL ;
} else {
res_cur = res_start ;
res_prev = NULL ;
debug ( " res_cur->rangeno is %d \n " , res_cur - > rangeno ) ;
while ( res_cur ) {
if ( res_cur - > rangeno > = res - > rangeno )
break ;
res_prev = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
}
if ( ! res_cur ) {
/* at the end of the resource list */
debug ( " i should be here, [%x - %x] \n " , res - > start , res - > end ) ;
res_prev - > nextRange = res ;
res - > next = NULL ;
res - > nextRange = NULL ;
} else if ( res_cur - > rangeno = = res - > rangeno ) {
/* in the same range */
while ( res_cur ) {
if ( res - > start < res_cur - > start )
break ;
res_prev = res_cur ;
res_cur = res_cur - > next ;
}
if ( ! res_cur ) {
/* the last resource in this range */
res_prev - > next = res ;
res - > next = NULL ;
res - > nextRange = res_prev - > nextRange ;
res_prev - > nextRange = NULL ;
} else if ( res - > start < res_cur - > start ) {
/* at the beginning or middle of the range */
if ( ! res_prev ) {
switch ( res - > type ) {
case IO :
bus_cur - > firstIO = res ;
break ;
case MEM :
bus_cur - > firstMem = res ;
break ;
case PFMEM :
bus_cur - > firstPFMem = res ;
break ;
}
} else if ( res_prev - > rangeno = = res_cur - > rangeno )
res_prev - > next = res ;
else
res_prev - > nextRange = res ;
res - > next = res_cur ;
res - > nextRange = NULL ;
}
} else {
/* this is the case where it is 1st occurrence of the range */
if ( ! res_prev ) {
/* at the beginning of the resource list */
res - > next = NULL ;
switch ( res - > type ) {
case IO :
res - > nextRange = bus_cur - > firstIO ;
bus_cur - > firstIO = res ;
break ;
case MEM :
res - > nextRange = bus_cur - > firstMem ;
bus_cur - > firstMem = res ;
break ;
case PFMEM :
res - > nextRange = bus_cur - > firstPFMem ;
bus_cur - > firstPFMem = res ;
break ;
}
} else if ( res_cur - > rangeno > res - > rangeno ) {
/* in the middle of the resource list */
res_prev - > nextRange = res ;
res - > next = NULL ;
res - > nextRange = res_cur ;
}
}
}
debug ( " %s - exit \n " , __FUNCTION__ ) ;
return 0 ;
}
/****************************************************************************
* This routine will remove the resource from the list of resources
*
* Input : io , mem , and / or pfmem resource to be deleted
* Ouput : modified resource list
* 0 or error code
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int ibmphp_remove_resource ( struct resource_node * res )
{
struct bus_node * bus_cur ;
struct resource_node * res_cur = NULL ;
struct resource_node * res_prev ;
struct resource_node * mem_cur ;
char * type = " " ;
if ( ! res ) {
err ( " resource to remove is NULL \n " ) ;
return - ENODEV ;
}
bus_cur = find_bus_wprev ( res - > busno , NULL , 0 ) ;
if ( ! bus_cur ) {
err ( " cannot find corresponding bus of the io resource to remove "
" bailing out... \n " ) ;
return - ENODEV ;
}
switch ( res - > type ) {
case IO :
res_cur = bus_cur - > firstIO ;
type = " io " ;
break ;
case MEM :
res_cur = bus_cur - > firstMem ;
type = " mem " ;
break ;
case PFMEM :
res_cur = bus_cur - > firstPFMem ;
type = " pfmem " ;
break ;
default :
err ( " unknown type for resource to remove \n " ) ;
return - EINVAL ;
}
res_prev = NULL ;
while ( res_cur ) {
if ( ( res_cur - > start = = res - > start ) & & ( res_cur - > end = = res - > end ) )
break ;
res_prev = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
}
if ( ! res_cur ) {
if ( res - > type = = PFMEM ) {
/*
* case where pfmem might be in the PFMemFromMem list
* so will also need to remove the corresponding mem
* entry
*/
res_cur = bus_cur - > firstPFMemFromMem ;
res_prev = NULL ;
while ( res_cur ) {
if ( ( res_cur - > start = = res - > start ) & & ( res_cur - > end = = res - > end ) ) {
mem_cur = bus_cur - > firstMem ;
while ( mem_cur ) {
if ( ( mem_cur - > start = = res_cur - > start )
& & ( mem_cur - > end = = res_cur - > end ) )
break ;
if ( mem_cur - > next )
mem_cur = mem_cur - > next ;
else
mem_cur = mem_cur - > nextRange ;
}
if ( ! mem_cur ) {
err ( " cannot find corresponding mem node for pfmem... \n " ) ;
return - EINVAL ;
}
ibmphp_remove_resource ( mem_cur ) ;
if ( ! res_prev )
bus_cur - > firstPFMemFromMem = res_cur - > next ;
else
res_prev - > next = res_cur - > next ;
kfree ( res_cur ) ;
return 0 ;
}
res_prev = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
}
if ( ! res_cur ) {
err ( " cannot find pfmem to delete... \n " ) ;
return - EINVAL ;
}
} else {
err ( " the %s resource is not in the list to be deleted... \n " , type ) ;
return - EINVAL ;
}
}
if ( ! res_prev ) {
/* first device to be deleted */
if ( res_cur - > next ) {
switch ( res - > type ) {
case IO :
bus_cur - > firstIO = res_cur - > next ;
break ;
case MEM :
bus_cur - > firstMem = res_cur - > next ;
break ;
case PFMEM :
bus_cur - > firstPFMem = res_cur - > next ;
break ;
}
} else if ( res_cur - > nextRange ) {
switch ( res - > type ) {
case IO :
bus_cur - > firstIO = res_cur - > nextRange ;
break ;
case MEM :
bus_cur - > firstMem = res_cur - > nextRange ;
break ;
case PFMEM :
bus_cur - > firstPFMem = res_cur - > nextRange ;
break ;
}
} else {
switch ( res - > type ) {
case IO :
bus_cur - > firstIO = NULL ;
break ;
case MEM :
bus_cur - > firstMem = NULL ;
break ;
case PFMEM :
bus_cur - > firstPFMem = NULL ;
break ;
}
}
kfree ( res_cur ) ;
return 0 ;
} else {
if ( res_cur - > next ) {
if ( res_prev - > rangeno = = res_cur - > rangeno )
res_prev - > next = res_cur - > next ;
else
res_prev - > nextRange = res_cur - > next ;
} else if ( res_cur - > nextRange ) {
res_prev - > next = NULL ;
res_prev - > nextRange = res_cur - > nextRange ;
} else {
res_prev - > next = NULL ;
res_prev - > nextRange = NULL ;
}
kfree ( res_cur ) ;
return 0 ;
}
return 0 ;
}
static struct range_node * find_range ( struct bus_node * bus_cur , struct resource_node * res )
{
struct range_node * range = NULL ;
switch ( res - > type ) {
case IO :
range = bus_cur - > rangeIO ;
break ;
case MEM :
range = bus_cur - > rangeMem ;
break ;
case PFMEM :
range = bus_cur - > rangePFMem ;
break ;
default :
err ( " cannot read resource type in find_range \n " ) ;
}
while ( range ) {
if ( res - > rangeno = = range - > rangeno )
break ;
range = range - > next ;
}
return range ;
}
/*****************************************************************************
* This routine will check to make sure the io / mem / pfmem - > len that the device asked for
* can fit w / i our list of available IO / MEM / PFMEM resources . If cannot , returns - EINVAL ,
* otherwise , returns 0
*
* Input : resource
* Ouput : the correct start and end address are inputted into the resource node ,
* 0 or - EINVAL
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int ibmphp_check_resource ( struct resource_node * res , u8 bridge )
{
struct bus_node * bus_cur ;
struct range_node * range = NULL ;
struct resource_node * res_prev ;
struct resource_node * res_cur = NULL ;
u32 len_cur = 0 , start_cur = 0 , len_tmp = 0 ;
int noranges = 0 ;
u32 tmp_start ; /* this is to make sure start address is divisible by the length needed */
u32 tmp_divide ;
2006-03-15 03:24:47 +03:00
u8 flag = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! res )
return - EINVAL ;
if ( bridge ) {
/* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/
if ( res - > type = = IO )
tmp_divide = IOBRIDGE ;
else
tmp_divide = MEMBRIDGE ;
} else
tmp_divide = res - > len ;
bus_cur = find_bus_wprev ( res - > busno , NULL , 0 ) ;
if ( ! bus_cur ) {
/* didn't find a bus, smth's wrong!!! */
debug ( " no bus in the system, either pci_dev's wrong or allocation failed \n " ) ;
return - EINVAL ;
}
debug ( " %s - enter \n " , __FUNCTION__ ) ;
debug ( " bus_cur->busno is %d \n " , bus_cur - > busno ) ;
/* This is a quick fix to not mess up with the code very much. i.e.,
* 2000 - 2ff f , len = 1000 , but when we compare , we need it to be fff */
res - > len - = 1 ;
switch ( res - > type ) {
case IO :
res_cur = bus_cur - > firstIO ;
noranges = bus_cur - > noIORanges ;
break ;
case MEM :
res_cur = bus_cur - > firstMem ;
noranges = bus_cur - > noMemRanges ;
break ;
case PFMEM :
res_cur = bus_cur - > firstPFMem ;
noranges = bus_cur - > noPFMemRanges ;
break ;
default :
err ( " wrong type of resource to check \n " ) ;
return - EINVAL ;
}
res_prev = NULL ;
while ( res_cur ) {
range = find_range ( bus_cur , res_cur ) ;
debug ( " %s - rangeno = %d \n " , __FUNCTION__ , res_cur - > rangeno ) ;
if ( ! range ) {
err ( " no range for the device exists... bailing out... \n " ) ;
return - EINVAL ;
}
/* found our range */
if ( ! res_prev ) {
/* first time in the loop */
if ( ( res_cur - > start ! = range - > start ) & & ( ( len_tmp = res_cur - > start - 1 - range - > start ) > = res - > len ) ) {
debug ( " len_tmp = %x \n " , len_tmp ) ;
if ( ( len_tmp < len_cur ) | | ( len_cur = = 0 ) ) {
if ( ( range - > start % tmp_divide ) = = 0 ) {
/* just perfect, starting address is divisible by length */
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = range - > start ;
} else {
/* Needs adjusting */
tmp_start = range - > start ;
2006-03-15 03:24:47 +03:00
flag = 0 ;
2005-04-17 02:20:36 +04:00
while ( ( len_tmp = res_cur - > start - 1 - tmp_start ) > = res - > len ) {
if ( ( tmp_start % tmp_divide ) = = 0 ) {
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = tmp_start ;
break ;
}
tmp_start + = tmp_divide - tmp_start % tmp_divide ;
if ( tmp_start > = res_cur - > start - 1 )
break ;
}
}
if ( flag & & len_cur = = res - > len ) {
debug ( " but we are not here, right? \n " ) ;
res - > start = start_cur ;
res - > len + = 1 ; /* To restore the balance */
res - > end = res - > start + res - > len - 1 ;
return 0 ;
}
}
}
}
if ( ! res_cur - > next ) {
/* last device on the range */
if ( ( range - > end ! = res_cur - > end ) & & ( ( len_tmp = range - > end - ( res_cur - > end + 1 ) ) > = res - > len ) ) {
debug ( " len_tmp = %x \n " , len_tmp ) ;
if ( ( len_tmp < len_cur ) | | ( len_cur = = 0 ) ) {
if ( ( ( res_cur - > end + 1 ) % tmp_divide ) = = 0 ) {
/* just perfect, starting address is divisible by length */
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = res_cur - > end + 1 ;
} else {
/* Needs adjusting */
tmp_start = res_cur - > end + 1 ;
2006-03-15 03:24:47 +03:00
flag = 0 ;
2005-04-17 02:20:36 +04:00
while ( ( len_tmp = range - > end - tmp_start ) > = res - > len ) {
if ( ( tmp_start % tmp_divide ) = = 0 ) {
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = tmp_start ;
break ;
}
tmp_start + = tmp_divide - tmp_start % tmp_divide ;
if ( tmp_start > = range - > end )
break ;
}
}
if ( flag & & len_cur = = res - > len ) {
res - > start = start_cur ;
res - > len + = 1 ; /* To restore the balance */
res - > end = res - > start + res - > len - 1 ;
return 0 ;
}
}
}
}
if ( res_prev ) {
if ( res_prev - > rangeno ! = res_cur - > rangeno ) {
/* 1st device on this range */
if ( ( res_cur - > start ! = range - > start ) & &
( ( len_tmp = res_cur - > start - 1 - range - > start ) > = res - > len ) ) {
if ( ( len_tmp < len_cur ) | | ( len_cur = = 0 ) ) {
if ( ( range - > start % tmp_divide ) = = 0 ) {
/* just perfect, starting address is divisible by length */
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = range - > start ;
} else {
/* Needs adjusting */
tmp_start = range - > start ;
2006-03-15 03:24:47 +03:00
flag = 0 ;
2005-04-17 02:20:36 +04:00
while ( ( len_tmp = res_cur - > start - 1 - tmp_start ) > = res - > len ) {
if ( ( tmp_start % tmp_divide ) = = 0 ) {
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = tmp_start ;
break ;
}
tmp_start + = tmp_divide - tmp_start % tmp_divide ;
if ( tmp_start > = res_cur - > start - 1 )
break ;
}
}
if ( flag & & len_cur = = res - > len ) {
res - > start = start_cur ;
res - > len + = 1 ; /* To restore the balance */
res - > end = res - > start + res - > len - 1 ;
return 0 ;
}
}
}
} else {
/* in the same range */
if ( ( len_tmp = res_cur - > start - 1 - res_prev - > end - 1 ) > = res - > len ) {
if ( ( len_tmp < len_cur ) | | ( len_cur = = 0 ) ) {
if ( ( ( res_prev - > end + 1 ) % tmp_divide ) = = 0 ) {
/* just perfect, starting address's divisible by length */
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = res_prev - > end + 1 ;
} else {
/* Needs adjusting */
tmp_start = res_prev - > end + 1 ;
2006-03-15 03:24:47 +03:00
flag = 0 ;
2005-04-17 02:20:36 +04:00
while ( ( len_tmp = res_cur - > start - 1 - tmp_start ) > = res - > len ) {
if ( ( tmp_start % tmp_divide ) = = 0 ) {
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = tmp_start ;
break ;
}
tmp_start + = tmp_divide - tmp_start % tmp_divide ;
if ( tmp_start > = res_cur - > start - 1 )
break ;
}
}
if ( flag & & len_cur = = res - > len ) {
res - > start = start_cur ;
res - > len + = 1 ; /* To restore the balance */
res - > end = res - > start + res - > len - 1 ;
return 0 ;
}
}
}
}
}
/* end if (res_prev) */
res_prev = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
} /* end of while */
if ( ! res_prev ) {
/* 1st device ever */
/* need to find appropriate range */
switch ( res - > type ) {
case IO :
range = bus_cur - > rangeIO ;
break ;
case MEM :
range = bus_cur - > rangeMem ;
break ;
case PFMEM :
range = bus_cur - > rangePFMem ;
break ;
}
while ( range ) {
if ( ( len_tmp = range - > end - range - > start ) > = res - > len ) {
if ( ( len_tmp < len_cur ) | | ( len_cur = = 0 ) ) {
if ( ( range - > start % tmp_divide ) = = 0 ) {
/* just perfect, starting address's divisible by length */
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = range - > start ;
} else {
/* Needs adjusting */
tmp_start = range - > start ;
2006-03-15 03:24:47 +03:00
flag = 0 ;
2005-04-17 02:20:36 +04:00
while ( ( len_tmp = range - > end - tmp_start ) > = res - > len ) {
if ( ( tmp_start % tmp_divide ) = = 0 ) {
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = tmp_start ;
break ;
}
tmp_start + = tmp_divide - tmp_start % tmp_divide ;
if ( tmp_start > = range - > end )
break ;
}
}
if ( flag & & len_cur = = res - > len ) {
res - > start = start_cur ;
res - > len + = 1 ; /* To restore the balance */
res - > end = res - > start + res - > len - 1 ;
return 0 ;
}
}
}
range = range - > next ;
} /* end of while */
if ( ( ! range ) & & ( len_cur = = 0 ) ) {
/* have gone through the list of devices and ranges and haven't found n.e.thing */
err ( " no appropriate range.. bailing out... \n " ) ;
return - EINVAL ;
} else if ( len_cur ) {
res - > start = start_cur ;
res - > len + = 1 ; /* To restore the balance */
res - > end = res - > start + res - > len - 1 ;
return 0 ;
}
}
if ( ! res_cur ) {
debug ( " prev->rangeno = %d, noranges = %d \n " , res_prev - > rangeno , noranges ) ;
if ( res_prev - > rangeno < noranges ) {
/* if there're more ranges out there to check */
switch ( res - > type ) {
case IO :
range = bus_cur - > rangeIO ;
break ;
case MEM :
range = bus_cur - > rangeMem ;
break ;
case PFMEM :
range = bus_cur - > rangePFMem ;
break ;
}
while ( range ) {
if ( ( len_tmp = range - > end - range - > start ) > = res - > len ) {
if ( ( len_tmp < len_cur ) | | ( len_cur = = 0 ) ) {
if ( ( range - > start % tmp_divide ) = = 0 ) {
/* just perfect, starting address's divisible by length */
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = range - > start ;
} else {
/* Needs adjusting */
tmp_start = range - > start ;
2006-03-15 03:24:47 +03:00
flag = 0 ;
2005-04-17 02:20:36 +04:00
while ( ( len_tmp = range - > end - tmp_start ) > = res - > len ) {
if ( ( tmp_start % tmp_divide ) = = 0 ) {
2006-03-15 03:24:47 +03:00
flag = 1 ;
2005-04-17 02:20:36 +04:00
len_cur = len_tmp ;
start_cur = tmp_start ;
break ;
}
tmp_start + = tmp_divide - tmp_start % tmp_divide ;
if ( tmp_start > = range - > end )
break ;
}
}
if ( flag & & len_cur = = res - > len ) {
res - > start = start_cur ;
res - > len + = 1 ; /* To restore the balance */
res - > end = res - > start + res - > len - 1 ;
return 0 ;
}
}
}
range = range - > next ;
} /* end of while */
if ( ( ! range ) & & ( len_cur = = 0 ) ) {
/* have gone through the list of devices and ranges and haven't found n.e.thing */
err ( " no appropriate range.. bailing out... \n " ) ;
return - EINVAL ;
} else if ( len_cur ) {
res - > start = start_cur ;
res - > len + = 1 ; /* To restore the balance */
res - > end = res - > start + res - > len - 1 ;
return 0 ;
}
} else {
/* no more ranges to check on */
if ( len_cur ) {
res - > start = start_cur ;
res - > len + = 1 ; /* To restore the balance */
res - > end = res - > start + res - > len - 1 ;
return 0 ;
} else {
/* have gone through the list of devices and haven't found n.e.thing */
err ( " no appropriate range.. bailing out... \n " ) ;
return - EINVAL ;
}
}
} /* end if(!res_cur) */
return - EINVAL ;
}
/********************************************************************************
* This routine is called from remove_card if the card contained PPB .
* It will remove all the resources on the bus as well as the bus itself
* Input : Bus
* Ouput : 0 , - ENODEV
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int ibmphp_remove_bus ( struct bus_node * bus , u8 parent_busno )
{
struct resource_node * res_cur ;
struct resource_node * res_tmp ;
struct bus_node * prev_bus ;
int rc ;
prev_bus = find_bus_wprev ( parent_busno , NULL , 0 ) ;
if ( ! prev_bus ) {
debug ( " something terribly wrong. Cannot find parent bus to the one to remove \n " ) ;
return - ENODEV ;
}
debug ( " In ibmphp_remove_bus... prev_bus->busno is %x \n " , prev_bus - > busno ) ;
rc = remove_ranges ( bus , prev_bus ) ;
if ( rc )
return rc ;
if ( bus - > firstIO ) {
res_cur = bus - > firstIO ;
while ( res_cur ) {
res_tmp = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
kfree ( res_tmp ) ;
res_tmp = NULL ;
}
bus - > firstIO = NULL ;
}
if ( bus - > firstMem ) {
res_cur = bus - > firstMem ;
while ( res_cur ) {
res_tmp = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
kfree ( res_tmp ) ;
res_tmp = NULL ;
}
bus - > firstMem = NULL ;
}
if ( bus - > firstPFMem ) {
res_cur = bus - > firstPFMem ;
while ( res_cur ) {
res_tmp = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
kfree ( res_tmp ) ;
res_tmp = NULL ;
}
bus - > firstPFMem = NULL ;
}
if ( bus - > firstPFMemFromMem ) {
res_cur = bus - > firstPFMemFromMem ;
while ( res_cur ) {
res_tmp = res_cur ;
res_cur = res_cur - > next ;
kfree ( res_tmp ) ;
res_tmp = NULL ;
}
bus - > firstPFMemFromMem = NULL ;
}
list_del ( & bus - > bus_list ) ;
kfree ( bus ) ;
return 0 ;
}
/******************************************************************************
* This routine deletes the ranges from a given bus , and the entries from the
* parent ' s bus in the resources
* Input : current bus , previous bus
* Output : 0 , - EINVAL
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int remove_ranges ( struct bus_node * bus_cur , struct bus_node * bus_prev )
{
struct range_node * range_cur ;
struct range_node * range_tmp ;
int i ;
struct resource_node * res = NULL ;
if ( bus_cur - > noIORanges ) {
range_cur = bus_cur - > rangeIO ;
for ( i = 0 ; i < bus_cur - > noIORanges ; i + + ) {
if ( ibmphp_find_resource ( bus_prev , range_cur - > start , & res , IO ) < 0 )
return - EINVAL ;
ibmphp_remove_resource ( res ) ;
range_tmp = range_cur ;
range_cur = range_cur - > next ;
kfree ( range_tmp ) ;
range_tmp = NULL ;
}
bus_cur - > rangeIO = NULL ;
}
if ( bus_cur - > noMemRanges ) {
range_cur = bus_cur - > rangeMem ;
for ( i = 0 ; i < bus_cur - > noMemRanges ; i + + ) {
if ( ibmphp_find_resource ( bus_prev , range_cur - > start , & res , MEM ) < 0 )
return - EINVAL ;
ibmphp_remove_resource ( res ) ;
range_tmp = range_cur ;
range_cur = range_cur - > next ;
kfree ( range_tmp ) ;
range_tmp = NULL ;
}
bus_cur - > rangeMem = NULL ;
}
if ( bus_cur - > noPFMemRanges ) {
range_cur = bus_cur - > rangePFMem ;
for ( i = 0 ; i < bus_cur - > noPFMemRanges ; i + + ) {
if ( ibmphp_find_resource ( bus_prev , range_cur - > start , & res , PFMEM ) < 0 )
return - EINVAL ;
ibmphp_remove_resource ( res ) ;
range_tmp = range_cur ;
range_cur = range_cur - > next ;
kfree ( range_tmp ) ;
range_tmp = NULL ;
}
bus_cur - > rangePFMem = NULL ;
}
return 0 ;
}
/*
* find the resource node in the bus
* Input : Resource needed , start address of the resource , type of resource
*/
int ibmphp_find_resource ( struct bus_node * bus , u32 start_address , struct resource_node * * res , int flag )
{
struct resource_node * res_cur = NULL ;
char * type = " " ;
if ( ! bus ) {
err ( " The bus passed in NULL to find resource \n " ) ;
return - ENODEV ;
}
switch ( flag ) {
case IO :
res_cur = bus - > firstIO ;
type = " io " ;
break ;
case MEM :
res_cur = bus - > firstMem ;
type = " mem " ;
break ;
case PFMEM :
res_cur = bus - > firstPFMem ;
type = " pfmem " ;
break ;
default :
err ( " wrong type of flag \n " ) ;
return - EINVAL ;
}
while ( res_cur ) {
if ( res_cur - > start = = start_address ) {
* res = res_cur ;
break ;
}
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
}
if ( ! res_cur ) {
if ( flag = = PFMEM ) {
res_cur = bus - > firstPFMemFromMem ;
while ( res_cur ) {
if ( res_cur - > start = = start_address ) {
* res = res_cur ;
break ;
}
res_cur = res_cur - > next ;
}
if ( ! res_cur ) {
debug ( " SOS...cannot find %s resource in the bus. \n " , type ) ;
return - EINVAL ;
}
} else {
debug ( " SOS... cannot find %s resource in the bus. \n " , type ) ;
return - EINVAL ;
}
}
if ( * res )
debug ( " *res->start = %x \n " , ( * res ) - > start ) ;
return 0 ;
}
/***********************************************************************
* This routine will free the resource structures used by the
* system . It is called from cleanup routine for the module
* Parameters : none
* Returns : none
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void ibmphp_free_resources ( void )
{
struct bus_node * bus_cur = NULL ;
struct bus_node * bus_tmp ;
struct range_node * range_cur ;
struct range_node * range_tmp ;
struct resource_node * res_cur ;
struct resource_node * res_tmp ;
struct list_head * tmp ;
struct list_head * next ;
int i = 0 ;
flags = 1 ;
list_for_each_safe ( tmp , next , & gbuses ) {
bus_cur = list_entry ( tmp , struct bus_node , bus_list ) ;
if ( bus_cur - > noIORanges ) {
range_cur = bus_cur - > rangeIO ;
for ( i = 0 ; i < bus_cur - > noIORanges ; i + + ) {
if ( ! range_cur )
break ;
range_tmp = range_cur ;
range_cur = range_cur - > next ;
kfree ( range_tmp ) ;
range_tmp = NULL ;
}
}
if ( bus_cur - > noMemRanges ) {
range_cur = bus_cur - > rangeMem ;
for ( i = 0 ; i < bus_cur - > noMemRanges ; i + + ) {
if ( ! range_cur )
break ;
range_tmp = range_cur ;
range_cur = range_cur - > next ;
kfree ( range_tmp ) ;
range_tmp = NULL ;
}
}
if ( bus_cur - > noPFMemRanges ) {
range_cur = bus_cur - > rangePFMem ;
for ( i = 0 ; i < bus_cur - > noPFMemRanges ; i + + ) {
if ( ! range_cur )
break ;
range_tmp = range_cur ;
range_cur = range_cur - > next ;
kfree ( range_tmp ) ;
range_tmp = NULL ;
}
}
if ( bus_cur - > firstIO ) {
res_cur = bus_cur - > firstIO ;
while ( res_cur ) {
res_tmp = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
kfree ( res_tmp ) ;
res_tmp = NULL ;
}
bus_cur - > firstIO = NULL ;
}
if ( bus_cur - > firstMem ) {
res_cur = bus_cur - > firstMem ;
while ( res_cur ) {
res_tmp = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
kfree ( res_tmp ) ;
res_tmp = NULL ;
}
bus_cur - > firstMem = NULL ;
}
if ( bus_cur - > firstPFMem ) {
res_cur = bus_cur - > firstPFMem ;
while ( res_cur ) {
res_tmp = res_cur ;
if ( res_cur - > next )
res_cur = res_cur - > next ;
else
res_cur = res_cur - > nextRange ;
kfree ( res_tmp ) ;
res_tmp = NULL ;
}
bus_cur - > firstPFMem = NULL ;
}
if ( bus_cur - > firstPFMemFromMem ) {
res_cur = bus_cur - > firstPFMemFromMem ;
while ( res_cur ) {
res_tmp = res_cur ;
res_cur = res_cur - > next ;
kfree ( res_tmp ) ;
res_tmp = NULL ;
}
bus_cur - > firstPFMemFromMem = NULL ;
}
bus_tmp = bus_cur ;
list_del ( & bus_cur - > bus_list ) ;
kfree ( bus_tmp ) ;
bus_tmp = NULL ;
}
}
/*********************************************************************************
* This function will go over the PFmem resources to check if the EBDA allocated
* pfmem out of memory buckets of the bus . If so , it will change the range numbers
* and a flag to indicate that this resource is out of memory . It will also move the
* Pfmem out of the pfmem resource list to the PFMemFromMem list , and will create
* a new Mem node
* This routine is called right after initialization
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int __init once_over ( void )
{
struct resource_node * pfmem_cur ;
struct resource_node * pfmem_prev ;
struct resource_node * mem ;
struct bus_node * bus_cur ;
struct list_head * tmp ;
list_for_each ( tmp , & gbuses ) {
bus_cur = list_entry ( tmp , struct bus_node , bus_list ) ;
if ( ( ! bus_cur - > rangePFMem ) & & ( bus_cur - > firstPFMem ) ) {
for ( pfmem_cur = bus_cur - > firstPFMem , pfmem_prev = NULL ; pfmem_cur ; pfmem_prev = pfmem_cur , pfmem_cur = pfmem_cur - > next ) {
2006-03-15 03:24:47 +03:00
pfmem_cur - > fromMem = 1 ;
2005-04-17 02:20:36 +04:00
if ( pfmem_prev )
pfmem_prev - > next = pfmem_cur - > next ;
else
bus_cur - > firstPFMem = pfmem_cur - > next ;
if ( ! bus_cur - > firstPFMemFromMem )
pfmem_cur - > next = NULL ;
else
/* we don't need to sort PFMemFromMem since we're using mem node for
all the real work anyways , so just insert at the beginning of the
list
*/
pfmem_cur - > next = bus_cur - > firstPFMemFromMem ;
bus_cur - > firstPFMemFromMem = pfmem_cur ;
2006-02-28 17:34:49 +03:00
mem = kzalloc ( sizeof ( struct resource_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! mem ) {
err ( " out of system memory \n " ) ;
return - ENOMEM ;
}
mem - > type = MEM ;
mem - > busno = pfmem_cur - > busno ;
mem - > devfunc = pfmem_cur - > devfunc ;
mem - > start = pfmem_cur - > start ;
mem - > end = pfmem_cur - > end ;
mem - > len = pfmem_cur - > len ;
if ( ibmphp_add_resource ( mem ) < 0 )
err ( " Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device... \n " ) ;
pfmem_cur - > rangeno = mem - > rangeno ;
} /* end for pfmem */
} /* end if */
} /* end list_for_each bus */
return 0 ;
}
int ibmphp_add_pfmem_from_mem ( struct resource_node * pfmem )
{
struct bus_node * bus_cur = find_bus_wprev ( pfmem - > busno , NULL , 0 ) ;
if ( ! bus_cur ) {
err ( " cannot find bus of pfmem to add... \n " ) ;
return - ENODEV ;
}
if ( bus_cur - > firstPFMemFromMem )
pfmem - > next = bus_cur - > firstPFMemFromMem ;
else
pfmem - > next = NULL ;
bus_cur - > firstPFMemFromMem = pfmem ;
return 0 ;
}
/* This routine just goes through the buses to see if the bus already exists.
* It is called from ibmphp_find_sec_number , to find out a secondary bus number for
* bridged cards
* Parameters : bus_number
* Returns : Bus pointer or NULL
*/
struct bus_node * ibmphp_find_res_bus ( u8 bus_number )
{
return find_bus_wprev ( bus_number , NULL , 0 ) ;
}
static struct bus_node * find_bus_wprev ( u8 bus_number , struct bus_node * * prev , u8 flag )
{
struct bus_node * bus_cur ;
struct list_head * tmp ;
struct list_head * tmp_prev ;
list_for_each ( tmp , & gbuses ) {
tmp_prev = tmp - > prev ;
bus_cur = list_entry ( tmp , struct bus_node , bus_list ) ;
if ( flag )
* prev = list_entry ( tmp_prev , struct bus_node , bus_list ) ;
if ( bus_cur - > busno = = bus_number )
return bus_cur ;
}
return NULL ;
}
void ibmphp_print_test ( void )
{
int i = 0 ;
struct bus_node * bus_cur = NULL ;
struct range_node * range ;
struct resource_node * res ;
struct list_head * tmp ;
debug_pci ( " *****************START********************** \n " ) ;
if ( ( ! list_empty ( & gbuses ) ) & & flags ) {
err ( " The GBUSES is not NULL?!?!?!?!? \n " ) ;
return ;
}
list_for_each ( tmp , & gbuses ) {
bus_cur = list_entry ( tmp , struct bus_node , bus_list ) ;
debug_pci ( " This is bus # %d. There are \n " , bus_cur - > busno ) ;
debug_pci ( " IORanges = %d \t " , bus_cur - > noIORanges ) ;
debug_pci ( " MemRanges = %d \t " , bus_cur - > noMemRanges ) ;
debug_pci ( " PFMemRanges = %d \n " , bus_cur - > noPFMemRanges ) ;
debug_pci ( " The IO Ranges are as follows: \n " ) ;
if ( bus_cur - > rangeIO ) {
range = bus_cur - > rangeIO ;
for ( i = 0 ; i < bus_cur - > noIORanges ; i + + ) {
debug_pci ( " rangeno is %d \n " , range - > rangeno ) ;
debug_pci ( " [%x - %x] \n " , range - > start , range - > end ) ;
range = range - > next ;
}
}
debug_pci ( " The Mem Ranges are as follows: \n " ) ;
if ( bus_cur - > rangeMem ) {
range = bus_cur - > rangeMem ;
for ( i = 0 ; i < bus_cur - > noMemRanges ; i + + ) {
debug_pci ( " rangeno is %d \n " , range - > rangeno ) ;
debug_pci ( " [%x - %x] \n " , range - > start , range - > end ) ;
range = range - > next ;
}
}
debug_pci ( " The PFMem Ranges are as follows: \n " ) ;
if ( bus_cur - > rangePFMem ) {
range = bus_cur - > rangePFMem ;
for ( i = 0 ; i < bus_cur - > noPFMemRanges ; i + + ) {
debug_pci ( " rangeno is %d \n " , range - > rangeno ) ;
debug_pci ( " [%x - %x] \n " , range - > start , range - > end ) ;
range = range - > next ;
}
}
debug_pci ( " The resources on this bus are as follows \n " ) ;
debug_pci ( " IO... \n " ) ;
if ( bus_cur - > firstIO ) {
res = bus_cur - > firstIO ;
while ( res ) {
debug_pci ( " The range # is %d \n " , res - > rangeno ) ;
debug_pci ( " The bus, devfnc is %d, %x \n " , res - > busno , res - > devfunc ) ;
debug_pci ( " [%x - %x], len=%x \n " , res - > start , res - > end , res - > len ) ;
if ( res - > next )
res = res - > next ;
else if ( res - > nextRange )
res = res - > nextRange ;
else
break ;
}
}
debug_pci ( " Mem... \n " ) ;
if ( bus_cur - > firstMem ) {
res = bus_cur - > firstMem ;
while ( res ) {
debug_pci ( " The range # is %d \n " , res - > rangeno ) ;
debug_pci ( " The bus, devfnc is %d, %x \n " , res - > busno , res - > devfunc ) ;
debug_pci ( " [%x - %x], len=%x \n " , res - > start , res - > end , res - > len ) ;
if ( res - > next )
res = res - > next ;
else if ( res - > nextRange )
res = res - > nextRange ;
else
break ;
}
}
debug_pci ( " PFMem... \n " ) ;
if ( bus_cur - > firstPFMem ) {
res = bus_cur - > firstPFMem ;
while ( res ) {
debug_pci ( " The range # is %d \n " , res - > rangeno ) ;
debug_pci ( " The bus, devfnc is %d, %x \n " , res - > busno , res - > devfunc ) ;
debug_pci ( " [%x - %x], len=%x \n " , res - > start , res - > end , res - > len ) ;
if ( res - > next )
res = res - > next ;
else if ( res - > nextRange )
res = res - > nextRange ;
else
break ;
}
}
debug_pci ( " PFMemFromMem... \n " ) ;
if ( bus_cur - > firstPFMemFromMem ) {
res = bus_cur - > firstPFMemFromMem ;
while ( res ) {
debug_pci ( " The range # is %d \n " , res - > rangeno ) ;
debug_pci ( " The bus, devfnc is %d, %x \n " , res - > busno , res - > devfunc ) ;
debug_pci ( " [%x - %x], len=%x \n " , res - > start , res - > end , res - > len ) ;
res = res - > next ;
}
}
}
debug_pci ( " ***********************END*********************** \n " ) ;
}
static int range_exists_already ( struct range_node * range , struct bus_node * bus_cur , u8 type )
{
struct range_node * range_cur = NULL ;
switch ( type ) {
case IO :
range_cur = bus_cur - > rangeIO ;
break ;
case MEM :
range_cur = bus_cur - > rangeMem ;
break ;
case PFMEM :
range_cur = bus_cur - > rangePFMem ;
break ;
default :
err ( " wrong type passed to find out if range already exists \n " ) ;
return - ENODEV ;
}
while ( range_cur ) {
if ( ( range_cur - > start = = range - > start ) & & ( range_cur - > end = = range - > end ) )
return 1 ;
range_cur = range_cur - > next ;
}
return 0 ;
}
/* This routine will read the windows for any PPB we have and update the
* range info for the secondary bus , and will also input this info into
* primary bus , since BIOS doesn ' t . This is for PPB that are in the system
* on bootup . For bridged cards that were added during previous load of the
* driver , only the ranges and the bus structure are added , the devices are
* added from NVRAM
* Input : primary busno
* Returns : none
* Note : this function doesn ' t take into account IO restrictions etc ,
* so will only work for bridges with no video / ISA devices behind them It
* also will not work for onboard PPB ' s that can have more than 1 * bus
* behind them All these are TO DO .
* Also need to add more error checkings . . . ( from fnc returns etc )
*/
static int __init update_bridge_ranges ( struct bus_node * * bus )
{
u8 sec_busno , device , function , hdr_type , start_io_address , end_io_address ;
u16 vendor_id , upper_io_start , upper_io_end , start_mem_address , end_mem_address ;
u32 start_address , end_address , upper_start , upper_end ;
struct bus_node * bus_sec ;
struct bus_node * bus_cur ;
struct resource_node * io ;
struct resource_node * mem ;
struct resource_node * pfmem ;
struct range_node * range ;
unsigned int devfn ;
bus_cur = * bus ;
if ( ! bus_cur )
return - ENODEV ;
ibmphp_pci_bus - > number = bus_cur - > busno ;
debug ( " inside %s \n " , __FUNCTION__ ) ;
debug ( " bus_cur->busno = %x \n " , bus_cur - > busno ) ;
for ( device = 0 ; device < 32 ; device + + ) {
for ( function = 0x00 ; function < 0x08 ; function + + ) {
devfn = PCI_DEVFN ( device , function ) ;
pci_bus_read_config_word ( ibmphp_pci_bus , devfn , PCI_VENDOR_ID , & vendor_id ) ;
if ( vendor_id ! = PCI_VENDOR_ID_NOTVALID ) {
/* found correct device!!! */
pci_bus_read_config_byte ( ibmphp_pci_bus , devfn , PCI_HEADER_TYPE , & hdr_type ) ;
switch ( hdr_type ) {
case PCI_HEADER_TYPE_NORMAL :
function = 0x8 ;
break ;
case PCI_HEADER_TYPE_MULTIDEVICE :
break ;
case PCI_HEADER_TYPE_BRIDGE :
function = 0x8 ;
case PCI_HEADER_TYPE_MULTIBRIDGE :
/* We assume here that only 1 bus behind the bridge
TO DO : add functionality for several :
temp = secondary ;
while ( temp < subordinate ) {
. . .
temp + + ;
}
*/
pci_bus_read_config_byte ( ibmphp_pci_bus , devfn , PCI_SECONDARY_BUS , & sec_busno ) ;
bus_sec = find_bus_wprev ( sec_busno , NULL , 0 ) ;
/* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */
if ( ! bus_sec ) {
bus_sec = alloc_error_bus ( NULL , sec_busno , 1 ) ;
/* the rest will be populated during NVRAM call */
return 0 ;
}
pci_bus_read_config_byte ( ibmphp_pci_bus , devfn , PCI_IO_BASE , & start_io_address ) ;
pci_bus_read_config_byte ( ibmphp_pci_bus , devfn , PCI_IO_LIMIT , & end_io_address ) ;
pci_bus_read_config_word ( ibmphp_pci_bus , devfn , PCI_IO_BASE_UPPER16 , & upper_io_start ) ;
pci_bus_read_config_word ( ibmphp_pci_bus , devfn , PCI_IO_LIMIT_UPPER16 , & upper_io_end ) ;
start_address = ( start_io_address & PCI_IO_RANGE_MASK ) < < 8 ;
start_address | = ( upper_io_start < < 16 ) ;
end_address = ( end_io_address & PCI_IO_RANGE_MASK ) < < 8 ;
end_address | = ( upper_io_end < < 16 ) ;
if ( ( start_address ) & & ( start_address < = end_address ) ) {
2006-02-28 17:34:49 +03:00
range = kzalloc ( sizeof ( struct range_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! range ) {
err ( " out of system memory \n " ) ;
return - ENOMEM ;
}
range - > start = start_address ;
range - > end = end_address + 0xfff ;
if ( bus_sec - > noIORanges > 0 ) {
if ( ! range_exists_already ( range , bus_sec , IO ) ) {
add_range ( IO , range , bus_sec ) ;
+ + bus_sec - > noIORanges ;
} else {
kfree ( range ) ;
range = NULL ;
}
} else {
/* 1st IO Range on the bus */
range - > rangeno = 1 ;
bus_sec - > rangeIO = range ;
+ + bus_sec - > noIORanges ;
}
fix_resources ( bus_sec ) ;
if ( ibmphp_find_resource ( bus_cur , start_address , & io , IO ) ) {
2006-02-28 17:34:49 +03:00
io = kzalloc ( sizeof ( struct resource_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! io ) {
kfree ( range ) ;
err ( " out of system memory \n " ) ;
return - ENOMEM ;
}
io - > type = IO ;
io - > busno = bus_cur - > busno ;
io - > devfunc = ( ( device < < 3 ) | ( function & 0x7 ) ) ;
io - > start = start_address ;
io - > end = end_address + 0xfff ;
io - > len = io - > end - io - > start + 1 ;
ibmphp_add_resource ( io ) ;
}
}
pci_bus_read_config_word ( ibmphp_pci_bus , devfn , PCI_MEMORY_BASE , & start_mem_address ) ;
pci_bus_read_config_word ( ibmphp_pci_bus , devfn , PCI_MEMORY_LIMIT , & end_mem_address ) ;
start_address = 0x00000000 | ( start_mem_address & PCI_MEMORY_RANGE_MASK ) < < 16 ;
end_address = 0x00000000 | ( end_mem_address & PCI_MEMORY_RANGE_MASK ) < < 16 ;
if ( ( start_address ) & & ( start_address < = end_address ) ) {
2006-02-28 17:34:49 +03:00
range = kzalloc ( sizeof ( struct range_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! range ) {
err ( " out of system memory \n " ) ;
return - ENOMEM ;
}
range - > start = start_address ;
range - > end = end_address + 0xfffff ;
if ( bus_sec - > noMemRanges > 0 ) {
if ( ! range_exists_already ( range , bus_sec , MEM ) ) {
add_range ( MEM , range , bus_sec ) ;
+ + bus_sec - > noMemRanges ;
} else {
kfree ( range ) ;
range = NULL ;
}
} else {
/* 1st Mem Range on the bus */
range - > rangeno = 1 ;
bus_sec - > rangeMem = range ;
+ + bus_sec - > noMemRanges ;
}
fix_resources ( bus_sec ) ;
if ( ibmphp_find_resource ( bus_cur , start_address , & mem , MEM ) ) {
2006-02-28 17:34:49 +03:00
mem = kzalloc ( sizeof ( struct resource_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! mem ) {
kfree ( range ) ;
err ( " out of system memory \n " ) ;
return - ENOMEM ;
}
mem - > type = MEM ;
mem - > busno = bus_cur - > busno ;
mem - > devfunc = ( ( device < < 3 ) | ( function & 0x7 ) ) ;
mem - > start = start_address ;
mem - > end = end_address + 0xfffff ;
mem - > len = mem - > end - mem - > start + 1 ;
ibmphp_add_resource ( mem ) ;
}
}
pci_bus_read_config_word ( ibmphp_pci_bus , devfn , PCI_PREF_MEMORY_BASE , & start_mem_address ) ;
pci_bus_read_config_word ( ibmphp_pci_bus , devfn , PCI_PREF_MEMORY_LIMIT , & end_mem_address ) ;
pci_bus_read_config_dword ( ibmphp_pci_bus , devfn , PCI_PREF_BASE_UPPER32 , & upper_start ) ;
pci_bus_read_config_dword ( ibmphp_pci_bus , devfn , PCI_PREF_LIMIT_UPPER32 , & upper_end ) ;
start_address = 0x00000000 | ( start_mem_address & PCI_MEMORY_RANGE_MASK ) < < 16 ;
end_address = 0x00000000 | ( end_mem_address & PCI_MEMORY_RANGE_MASK ) < < 16 ;
# if BITS_PER_LONG == 64
start_address | = ( ( long ) upper_start ) < < 32 ;
end_address | = ( ( long ) upper_end ) < < 32 ;
# endif
if ( ( start_address ) & & ( start_address < = end_address ) ) {
2006-02-28 17:34:49 +03:00
range = kzalloc ( sizeof ( struct range_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! range ) {
err ( " out of system memory \n " ) ;
return - ENOMEM ;
}
range - > start = start_address ;
range - > end = end_address + 0xfffff ;
if ( bus_sec - > noPFMemRanges > 0 ) {
if ( ! range_exists_already ( range , bus_sec , PFMEM ) ) {
add_range ( PFMEM , range , bus_sec ) ;
+ + bus_sec - > noPFMemRanges ;
} else {
kfree ( range ) ;
range = NULL ;
}
} else {
/* 1st PFMem Range on the bus */
range - > rangeno = 1 ;
bus_sec - > rangePFMem = range ;
+ + bus_sec - > noPFMemRanges ;
}
fix_resources ( bus_sec ) ;
if ( ibmphp_find_resource ( bus_cur , start_address , & pfmem , PFMEM ) ) {
2006-02-28 17:34:49 +03:00
pfmem = kzalloc ( sizeof ( struct resource_node ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! pfmem ) {
kfree ( range ) ;
err ( " out of system memory \n " ) ;
return - ENOMEM ;
}
pfmem - > type = PFMEM ;
pfmem - > busno = bus_cur - > busno ;
pfmem - > devfunc = ( ( device < < 3 ) | ( function & 0x7 ) ) ;
pfmem - > start = start_address ;
pfmem - > end = end_address + 0xfffff ;
pfmem - > len = pfmem - > end - pfmem - > start + 1 ;
2006-03-15 03:24:47 +03:00
pfmem - > fromMem = 0 ;
2005-04-17 02:20:36 +04:00
ibmphp_add_resource ( pfmem ) ;
}
}
break ;
} /* end of switch */
} /* end if vendor */
} /* end for function */
} /* end for device */
bus = & bus_cur ;
return 0 ;
}