2118 lines · 56.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * 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. * * 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_bus_range(int type, struct range_node *, struct bus_node *);static void fix_resources(struct bus_node *);static struct bus_node *find_bus_wprev(u8, struct bus_node **, u8); 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;	} 	newbus = kzalloc_obj(struct bus_node);	if (!newbus)		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;	} 	rs = kzalloc_obj(struct resource_node);	if (!rs)		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) {		newbus = kzalloc_obj(struct bus_node);		if (!newbus)			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;		}	} 	newrange = kzalloc_obj(struct range_node);	if (!newrange) {		if (first_bus)			kfree(newbus);		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_bus_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 resource_node *new_io = NULL;	struct resource_node *new_mem = NULL;	struct resource_node *new_pfmem = NULL;	int rc; 	list_for_each_entry(curr, &ibmphp_ebda_pci_rsrc_head,			    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)) {					rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);					if (rc)						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 */						rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);						if (rc)							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 */					rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);					if (rc)						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 */						rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);						if (rc)							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 */					rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);					if (rc)						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 */						rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);						if (rc)							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;				new_pfmem->fromMem = 0;				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_entry(bus_cur, &gbuses, 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;	}	return once_over();	/* This is to align ranges (so no -1) */} /******************************************************************************** * 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_bus_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_bus_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;	u8 eol = 0;	/* end of list indicator */ 	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 {				eol = 1;				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;	}

Lines 1–500 of 2118.