diff options
Diffstat (limited to 'drivers/pci/hotplug/shpchprm_acpi.c')
| -rw-r--r-- | drivers/pci/hotplug/shpchprm_acpi.c | 1713 | 
1 files changed, 1713 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/shpchprm_acpi.c b/drivers/pci/hotplug/shpchprm_acpi.c new file mode 100644 index 00000000000..243a51d88b8 --- /dev/null +++ b/drivers/pci/hotplug/shpchprm_acpi.c @@ -0,0 +1,1713 @@ +/* + * SHPCHPRM ACPI: PHP Resource Manager for ACPI platform + * + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT.  See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to <dely.l.sy@intel.com> + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/efi.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#ifdef	CONFIG_IA64 +#include <asm/iosapic.h> +#endif +#include <acpi/acpi.h> +#include <acpi/acpi_bus.h> +#include <acpi/actypes.h> +#include "shpchp.h" +#include "shpchprm.h" + +#define	PCI_MAX_BUS		0x100 +#define	ACPI_STA_DEVICE_PRESENT	0x01 + +#define	METHOD_NAME__SUN	"_SUN" +#define	METHOD_NAME__HPP	"_HPP" +#define	METHOD_NAME_OSHP	"OSHP" + +#define	PHP_RES_BUS		0xA0 +#define	PHP_RES_IO		0xA1 +#define	PHP_RES_MEM		0xA2 +#define	PHP_RES_PMEM		0xA3 + +#define	BRIDGE_TYPE_P2P		0x00 +#define	BRIDGE_TYPE_HOST	0x01 + +/* this should go to drivers/acpi/include/ */ +struct acpi__hpp { +	u8	cache_line_size; +	u8	latency_timer; +	u8	enable_serr; +	u8	enable_perr; +}; + +struct acpi_php_slot { +	struct acpi_php_slot	*next; +	struct acpi_bridge	*bridge; +	acpi_handle		handle; +	int	seg; +	int	bus; +	int	dev; +	int	fun; +	u32	sun; +	struct pci_resource *mem_head; +	struct pci_resource *p_mem_head; +	struct pci_resource *io_head; +	struct pci_resource *bus_head; +	void	*slot_ops;	/* _STA, _EJx, etc */ +	struct slot *slot; +};		/* per func */ + +struct acpi_bridge { +	struct acpi_bridge	*parent; +	struct acpi_bridge	*next; +	struct acpi_bridge	*child; +	acpi_handle	handle; +	int seg; +	int pbus;				/* pdev->bus->number		*/ +	int pdevice;				/* PCI_SLOT(pdev->devfn)	*/ +	int pfunction;				/* PCI_DEVFN(pdev->devfn)	*/ +	int bus;				/* pdev->subordinate->number	*/ +	struct acpi__hpp		*_hpp; +	struct acpi_php_slot	*slots; +	struct pci_resource 	*tmem_head;	/* total from crs	*/ +	struct pci_resource 	*tp_mem_head;	/* total from crs	*/ +	struct pci_resource 	*tio_head;	/* total from crs	*/ +	struct pci_resource 	*tbus_head;	/* total from crs	*/ +	struct pci_resource 	*mem_head;	/* available	*/ +	struct pci_resource 	*p_mem_head;	/* available	*/ +	struct pci_resource 	*io_head;	/* available	*/ +	struct pci_resource 	*bus_head;	/* available	*/ +	int scanned; +	int type; +}; + +static struct acpi_bridge *acpi_bridges_head; + +static u8 * acpi_path_name( acpi_handle	handle) +{ +	acpi_status		status; +	static u8	path_name[ACPI_PATHNAME_MAX]; +	struct acpi_buffer		ret_buf = { ACPI_PATHNAME_MAX, path_name }; + +	memset(path_name, 0, sizeof (path_name)); +	status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf); + +	if (ACPI_FAILURE(status)) +		return NULL; +	else +		return path_name;	 +} + +static void acpi_get__hpp ( struct acpi_bridge	*ab); +static void acpi_run_oshp ( struct acpi_bridge	*ab); + +static int acpi_add_slot_to_php_slots( +	struct acpi_bridge	*ab, +	int				bus_num, +	acpi_handle		handle, +	u32				adr, +	u32				sun +	) +{ +	struct acpi_php_slot	*aps; +	static long	samesun = -1; + +	aps = (struct acpi_php_slot *) kmalloc (sizeof(struct acpi_php_slot), GFP_KERNEL); +	if (!aps) { +		err ("acpi_shpchprm: alloc for aps fail\n"); +		return -1; +	} +	memset(aps, 0, sizeof(struct acpi_php_slot)); + +	aps->handle = handle; +	aps->bus = bus_num; +	aps->dev = (adr >> 16) & 0xffff; +	aps->fun = adr & 0xffff; +	aps->sun = sun; + +	aps->next = ab->slots;	/* cling to the bridge */ +	aps->bridge = ab; +	ab->slots = aps; + +	ab->scanned += 1; +	if (!ab->_hpp) +		acpi_get__hpp(ab); + +	acpi_run_oshp(ab); + +	if (sun != samesun) { +		info("acpi_shpchprm:   Slot sun(%x) at s:b:d:f=0x%02x:%02x:%02x:%02x\n", aps->sun, ab->seg,  +			aps->bus, aps->dev, aps->fun); +		samesun = sun; +	} +	return 0; +} + +static void acpi_get__hpp ( struct acpi_bridge	*ab) +{ +	acpi_status		status; +	u8			nui[4]; +	struct acpi_buffer	ret_buf = { 0, NULL}; +	union acpi_object	*ext_obj, *package; +	u8			*path_name = acpi_path_name(ab->handle); +	int			i, len = 0; + +	/* get _hpp */ +	status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); +	switch (status) { +	case AE_BUFFER_OVERFLOW: +		ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL); +		if (!ret_buf.pointer) { +			err ("acpi_shpchprm:%s alloc for _HPP fail\n", path_name); +			return; +		} +		status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); +		if (ACPI_SUCCESS(status)) +			break; +	default: +		if (ACPI_FAILURE(status)) { +			err("acpi_shpchprm:%s _HPP fail=0x%x\n", path_name, status); +			return; +		} +	} + +	ext_obj = (union acpi_object *) ret_buf.pointer; +	if (ext_obj->type != ACPI_TYPE_PACKAGE) { +		err ("acpi_shpchprm:%s _HPP obj not a package\n", path_name); +		goto free_and_return; +	} + +	len = ext_obj->package.count; +	package = (union acpi_object *) ret_buf.pointer; +	for ( i = 0; (i < len) || (i < 4); i++) { +		ext_obj = (union acpi_object *) &package->package.elements[i]; +		switch (ext_obj->type) { +		case ACPI_TYPE_INTEGER: +			nui[i] = (u8)ext_obj->integer.value; +			break; +		default: +			err ("acpi_shpchprm:%s _HPP obj type incorrect\n", path_name); +			goto free_and_return; +		} +	} + +	ab->_hpp = kmalloc (sizeof (struct acpi__hpp), GFP_KERNEL); +	if (!ab->_hpp) { +		err ("acpi_shpchprm:%s alloc for _HPP failed\n", path_name); +		goto free_and_return; +	} +	memset(ab->_hpp, 0, sizeof(struct acpi__hpp)); + +	ab->_hpp->cache_line_size	= nui[0]; +	ab->_hpp->latency_timer		= nui[1]; +	ab->_hpp->enable_serr		= nui[2]; +	ab->_hpp->enable_perr		= nui[3]; + +	dbg("  _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); +	dbg("  _HPP: latency timer  =0x%x\n", ab->_hpp->latency_timer); +	dbg("  _HPP: enable SERR    =0x%x\n", ab->_hpp->enable_serr); +	dbg("  _HPP: enable PERR    =0x%x\n", ab->_hpp->enable_perr); + +free_and_return: +	kfree(ret_buf.pointer); +} + +static void acpi_run_oshp ( struct acpi_bridge	*ab) +{ +	acpi_status		status; +	u8			*path_name = acpi_path_name(ab->handle); + +	/* run OSHP */ +	status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, NULL); +	if (ACPI_FAILURE(status)) { +		err("acpi_pciehprm:%s OSHP fails=0x%x\n", path_name, status); +	} else +		dbg("acpi_pciehprm:%s OSHP passes =0x%x\n", path_name, status); +	return; +} + +static acpi_status acpi_evaluate_crs( +	acpi_handle		handle, +	struct acpi_resource	**retbuf +	) +{ +	acpi_status		status; +	struct acpi_buffer		crsbuf; +	u8			*path_name = acpi_path_name(handle); + +	crsbuf.length  = 0; +	crsbuf.pointer = NULL; + +	status = acpi_get_current_resources (handle, &crsbuf); + +	switch (status) { +	case AE_BUFFER_OVERFLOW: +		break;		/* found */ +	case AE_NOT_FOUND: +		dbg("acpi_shpchprm:%s _CRS not found\n", path_name); +		return status; +	default: +		err ("acpi_shpchprm:%s _CRS fail=0x%x\n", path_name, status); +		return status; +	} + +	crsbuf.pointer = kmalloc (crsbuf.length, GFP_KERNEL); +	if (!crsbuf.pointer) { +		err ("acpi_shpchprm: alloc %ld bytes for %s _CRS fail\n", (ulong)crsbuf.length, path_name); +		return AE_NO_MEMORY; +	} + +	status = acpi_get_current_resources (handle, &crsbuf); +	if (ACPI_FAILURE(status)) { +		err("acpi_shpchprm: %s _CRS fail=0x%x.\n", path_name, status); +		kfree(crsbuf.pointer); +		return status; +	} + +	*retbuf = crsbuf.pointer; + +	return status; +} + +static void free_pci_resource ( struct pci_resource	*aprh) +{ +	struct pci_resource	*res, *next; + +	for (res = aprh; res; res = next) { +		next = res->next; +		kfree(res); +	} +} + +static void print_pci_resource ( struct pci_resource	*aprh) +{ +	struct pci_resource	*res; + +	for (res = aprh; res; res = res->next) +		dbg("        base= 0x%x length= 0x%x\n", res->base, res->length); +} + +static void print_slot_resources( struct acpi_php_slot	*aps) +{ +	if (aps->bus_head) { +		dbg("    BUS Resources:\n"); +		print_pci_resource (aps->bus_head); +	} + +	if (aps->io_head) { +		dbg("    IO Resources:\n"); +		print_pci_resource (aps->io_head); +	} + +	if (aps->mem_head) { +		dbg("    MEM Resources:\n"); +		print_pci_resource (aps->mem_head); +	} + +	if (aps->p_mem_head) { +		dbg("    PMEM Resources:\n"); +		print_pci_resource (aps->p_mem_head); +	} +} + +static void print_pci_resources( struct acpi_bridge	*ab) +{ +	if (ab->tbus_head) { +		dbg("    Total BUS Resources:\n"); +		print_pci_resource (ab->tbus_head); +	} +	if (ab->bus_head) { +		dbg("    BUS Resources:\n"); +		print_pci_resource (ab->bus_head); +	} + +	if (ab->tio_head) { +		dbg("    Total IO Resources:\n"); +		print_pci_resource (ab->tio_head); +	} +	if (ab->io_head) { +		dbg("    IO Resources:\n"); +		print_pci_resource (ab->io_head); +	} + +	if (ab->tmem_head) { +		dbg("    Total MEM Resources:\n"); +		print_pci_resource (ab->tmem_head); +	} +	if (ab->mem_head) { +		dbg("    MEM Resources:\n"); +		print_pci_resource (ab->mem_head); +	} + +	if (ab->tp_mem_head) { +		dbg("    Total PMEM Resources:\n"); +		print_pci_resource (ab->tp_mem_head); +	} +	if (ab->p_mem_head) { +		dbg("    PMEM Resources:\n"); +		print_pci_resource (ab->p_mem_head); +	} +	if (ab->_hpp) { +		dbg("    _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); +		dbg("    _HPP: latency timer  =0x%x\n", ab->_hpp->latency_timer); +		dbg("    _HPP: enable SERR    =0x%x\n", ab->_hpp->enable_serr); +		dbg("    _HPP: enable PERR    =0x%x\n", ab->_hpp->enable_perr); +	} +} + +static int shpchprm_delete_resource( +	struct pci_resource **aprh, +	ulong base, +	ulong size) +{ +	struct pci_resource *res; +	struct pci_resource *prevnode; +	struct pci_resource *split_node; +	ulong tbase; + +	shpchp_resource_sort_and_combine(aprh); + +	for (res = *aprh; res; res = res->next) { +		if (res->base > base) +			continue; + +		if ((res->base + res->length) < (base + size)) +			continue; + +		if (res->base < base) { +			tbase = base; + +			if ((res->length - (tbase - res->base)) < size) +				continue; + +			split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); +			if (!split_node) +				return -ENOMEM; + +			split_node->base = res->base; +			split_node->length = tbase - res->base; +			res->base = tbase; +			res->length -= split_node->length; + +			split_node->next = res->next; +			res->next = split_node; +		} + +		if (res->length >= size) { +			split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); +			if (!split_node) +				return -ENOMEM; + +			split_node->base = res->base + size; +			split_node->length = res->length - size; +			res->length = size; + +			split_node->next = res->next; +			res->next = split_node; +		} + +		if (*aprh == res) { +			*aprh = res->next; +		} else { +			prevnode = *aprh; +			while (prevnode->next != res) +				prevnode = prevnode->next; + +			prevnode->next = res->next; +		} +		res->next = NULL; +		kfree(res); +		break; +	} + +	return 0; +} + +static int shpchprm_delete_resources( +	struct pci_resource **aprh, +	struct pci_resource *this +	) +{ +	struct pci_resource *res; + +	for (res = this; res; res = res->next) +		shpchprm_delete_resource(aprh, res->base, res->length); + +	return 0; +} + +static int shpchprm_add_resource( +	struct pci_resource **aprh, +	ulong base, +	ulong size) +{ +	struct pci_resource *res; + +	for (res = *aprh; res; res = res->next) { +		if ((res->base + res->length) == base) { +			res->length += size; +			size = 0L; +			break; +		} +		if (res->next == *aprh) +			break; +	} + +	if (size) { +		res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); +		if (!res) { +			err ("acpi_shpchprm: alloc for res fail\n"); +			return -ENOMEM; +		} +		memset(res, 0, sizeof (struct pci_resource)); + +		res->base = base; +		res->length = size; +		res->next = *aprh; +		*aprh = res; +	} + +	return 0; +} + +static int shpchprm_add_resources( +	struct pci_resource **aprh, +	struct pci_resource *this +	) +{ +	struct pci_resource *res; +	int	rc = 0; + +	for (res = this; res && !rc; res = res->next) +		rc = shpchprm_add_resource(aprh, res->base, res->length); + +	return rc; +} + +static void acpi_parse_io ( +	struct acpi_bridge		*ab, +	union acpi_resource_data	*data +	) +{ +	struct acpi_resource_io	*dataio; +	dataio = (struct acpi_resource_io *) data; + +	dbg("Io Resource\n"); +	dbg("  %d bit decode\n", ACPI_DECODE_16 == dataio->io_decode ? 16:10); +	dbg("  Range minimum base: %08X\n", dataio->min_base_address); +	dbg("  Range maximum base: %08X\n", dataio->max_base_address); +	dbg("  Alignment: %08X\n", dataio->alignment); +	dbg("  Range Length: %08X\n", dataio->range_length); +} + +static void acpi_parse_fixed_io ( +	struct acpi_bridge		*ab, +	union acpi_resource_data	*data +	) +{ +	struct acpi_resource_fixed_io  *datafio; +	datafio = (struct acpi_resource_fixed_io *) data; + +	dbg("Fixed Io Resource\n"); +	dbg("  Range base address: %08X", datafio->base_address); +	dbg("  Range length: %08X", datafio->range_length); +} + +static void acpi_parse_address16_32 ( +	struct acpi_bridge		*ab, +	union acpi_resource_data	*data, +	acpi_resource_type		id +	) +{ +	/*  +	 * acpi_resource_address16 == acpi_resource_address32 +	 * acpi_resource_address16 *data16 = (acpi_resource_address16 *) data; +	 */ +	struct acpi_resource_address32 *data32 = (struct acpi_resource_address32 *) data; +	struct pci_resource **aprh, **tprh; + +	if (id == ACPI_RSTYPE_ADDRESS16) +		dbg("acpi_shpchprm:16-Bit Address Space Resource\n"); +	else +		dbg("acpi_shpchprm:32-Bit Address Space Resource\n"); + +	switch (data32->resource_type) { +	case ACPI_MEMORY_RANGE:  +		dbg("  Resource Type: Memory Range\n"); +		aprh = &ab->mem_head; +		tprh = &ab->tmem_head; + +		switch (data32->attribute.memory.cache_attribute) { +		case ACPI_NON_CACHEABLE_MEMORY: +			dbg("  Type Specific: Noncacheable memory\n"); +			break;  +		case ACPI_CACHABLE_MEMORY: +			dbg("  Type Specific: Cacheable memory\n"); +			break;  +		case ACPI_WRITE_COMBINING_MEMORY: +			dbg("  Type Specific: Write-combining memory\n"); +			break;  +		case ACPI_PREFETCHABLE_MEMORY: +			aprh = &ab->p_mem_head; +			dbg("  Type Specific: Prefetchable memory\n"); +			break;  +		default: +			dbg("  Type Specific: Invalid cache attribute\n"); +			break; +		} + +		dbg("  Type Specific: Read%s\n", ACPI_READ_WRITE_MEMORY == data32->attribute.memory.read_write_attribute ? "/Write":" Only"); +		break; + +	case ACPI_IO_RANGE:  +		dbg("  Resource Type: I/O Range\n"); +		aprh = &ab->io_head; +		tprh = &ab->tio_head; + +		switch (data32->attribute.io.range_attribute) { +		case ACPI_NON_ISA_ONLY_RANGES: +			dbg("  Type Specific: Non-ISA Io Addresses\n"); +			break;  +		case ACPI_ISA_ONLY_RANGES: +			dbg("  Type Specific: ISA Io Addresses\n"); +			break;  +		case ACPI_ENTIRE_RANGE: +			dbg("  Type Specific: ISA and non-ISA Io Addresses\n"); +			break;  +		default: +			dbg("  Type Specific: Invalid range attribute\n"); +			break; +		} +		break; + +	case ACPI_BUS_NUMBER_RANGE:  +		dbg("  Resource Type: Bus Number Range(fixed)\n"); +		/* fixup to be compatible with the rest of php driver */ +		data32->min_address_range++; +		data32->address_length--; +		aprh = &ab->bus_head; +		tprh = &ab->tbus_head; +		break;  +	default:  +		dbg("  Resource Type: Invalid resource type. Exiting.\n"); +		return; +	} + +	dbg("  Resource %s\n", ACPI_CONSUMER == data32->producer_consumer ? "Consumer":"Producer"); +	dbg("  %s decode\n", ACPI_SUB_DECODE == data32->decode ? "Subtractive":"Positive"); +	dbg("  Min address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->min_address_fixed ? "":"not"); +	dbg("  Max address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->max_address_fixed ? "":"not"); +	dbg("  Granularity: %08X\n", data32->granularity); +	dbg("  Address range min: %08X\n", data32->min_address_range); +	dbg("  Address range max: %08X\n", data32->max_address_range); +	dbg("  Address translation offset: %08X\n", data32->address_translation_offset); +	dbg("  Address Length: %08X\n", data32->address_length); + +	if (0xFF != data32->resource_source.index) { +		dbg("  Resource Source Index: %X\n", data32->resource_source.index); +		/* dbg("  Resource Source: %s\n", data32->resource_source.string_ptr); */ +	} + +	shpchprm_add_resource(aprh, data32->min_address_range, data32->address_length); +} + +static acpi_status acpi_parse_crs( +	struct acpi_bridge	*ab, +	struct acpi_resource	*crsbuf +	) +{ +	acpi_status		status = AE_OK; +	struct acpi_resource	*resource = crsbuf; +	u8			count = 0; +	u8			done = 0; + +	while (!done) { +		dbg("acpi_shpchprm: PCI bus 0x%x Resource structure %x.\n", ab->bus, count++); +		switch (resource->id) { +		case ACPI_RSTYPE_IRQ: +			dbg("Irq -------- Resource\n"); +			break;  +		case ACPI_RSTYPE_DMA: +			dbg("DMA -------- Resource\n"); +			break;  +		case ACPI_RSTYPE_START_DPF: +			dbg("Start DPF -------- Resource\n"); +			break;  +		case ACPI_RSTYPE_END_DPF: +			dbg("End DPF -------- Resource\n"); +			break;  +		case ACPI_RSTYPE_IO: +			acpi_parse_io (ab, &resource->data); +			break;  +		case ACPI_RSTYPE_FIXED_IO: +			acpi_parse_fixed_io (ab, &resource->data); +			break;  +		case ACPI_RSTYPE_VENDOR: +			dbg("Vendor -------- Resource\n"); +			break;  +		case ACPI_RSTYPE_END_TAG: +			dbg("End_tag -------- Resource\n"); +			done = 1; +			break;  +		case ACPI_RSTYPE_MEM24: +			dbg("Mem24 -------- Resource\n"); +			break;  +		case ACPI_RSTYPE_MEM32: +			dbg("Mem32 -------- Resource\n"); +			break;  +		case ACPI_RSTYPE_FIXED_MEM32: +			dbg("Fixed Mem32 -------- Resource\n"); +			break;  +		case ACPI_RSTYPE_ADDRESS16: +			acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS16); +			break;  +		case ACPI_RSTYPE_ADDRESS32: +			acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS32); +			break;  +		case ACPI_RSTYPE_ADDRESS64: +			info("Address64 -------- Resource unparsed\n"); +			break;  +		case ACPI_RSTYPE_EXT_IRQ: +			dbg("Ext Irq -------- Resource\n"); +			break;  +		default: +			dbg("Invalid -------- resource type 0x%x\n", resource->id); +			break; +		} + +		resource = (struct acpi_resource *) ((char *)resource + resource->length); +	} + +	return status; +} + +static acpi_status acpi_get_crs( struct acpi_bridge	*ab) +{ +	acpi_status		status; +	struct acpi_resource	*crsbuf; + +	status = acpi_evaluate_crs(ab->handle, &crsbuf); +	if (ACPI_SUCCESS(status)) { +		status = acpi_parse_crs(ab, crsbuf); +		kfree(crsbuf); + +		shpchp_resource_sort_and_combine(&ab->bus_head); +		shpchp_resource_sort_and_combine(&ab->io_head); +		shpchp_resource_sort_and_combine(&ab->mem_head); +		shpchp_resource_sort_and_combine(&ab->p_mem_head); + +		shpchprm_add_resources (&ab->tbus_head, ab->bus_head); +		shpchprm_add_resources (&ab->tio_head, ab->io_head); +		shpchprm_add_resources (&ab->tmem_head, ab->mem_head); +		shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); +	} + +	return status; +} + +/* find acpi_bridge downword from ab.  */ +static struct acpi_bridge * +find_acpi_bridge_by_bus( +	struct acpi_bridge *ab, +	int seg, +	int bus		/* pdev->subordinate->number */ +	) +{ +	struct acpi_bridge	*lab = NULL; + +	if (!ab) +		return NULL; + +	if ((ab->bus == bus) && (ab->seg == seg)) +		return ab; + +	if (ab->child) +		lab = find_acpi_bridge_by_bus(ab->child, seg, bus); + +	if (!lab) +	if (ab->next) +		lab = find_acpi_bridge_by_bus(ab->next, seg, bus); + +	return lab; +} + +/* + * Build a device tree of ACPI PCI Bridges + */ +static void shpchprm_acpi_register_a_bridge ( +	struct acpi_bridge	**head, +	struct acpi_bridge	*pab,	/* parent bridge to which child bridge is added */ +	struct acpi_bridge	*cab	/* child bridge to add */ +	) +{ +	struct acpi_bridge	*lpab; +	struct acpi_bridge	*lcab; + +	lpab = find_acpi_bridge_by_bus(*head, pab->seg, pab->bus); +	if (!lpab) { +		if (!(pab->type & BRIDGE_TYPE_HOST)) +			warn("PCI parent bridge s:b(%x:%x) not in list.\n", pab->seg, pab->bus); +		pab->next = *head; +		*head = pab; +		lpab = pab; +	} + +	if ((cab->type & BRIDGE_TYPE_HOST) && (pab == cab)) +		return; + +	lcab = find_acpi_bridge_by_bus(*head, cab->seg, cab->bus); +	if (lcab) { +		if ((pab->bus != lcab->parent->bus) || (lcab->bus != cab->bus)) +			err("PCI child bridge s:b(%x:%x) in list with diff parent.\n", cab->seg, cab->bus); +		return; +	} else +		lcab = cab; + +	lcab->parent = lpab; +	lcab->next = lpab->child; +	lpab->child = lcab; +} + +static acpi_status shpchprm_acpi_build_php_slots_callback( +	acpi_handle	handle, +	u32		Level, +	void		*context, +	void		**retval +	) +{ +	ulong		bus_num; +	ulong		seg_num; +	ulong		sun, adr; +	ulong		padr = 0; +	acpi_handle		phandle = NULL; +	struct acpi_bridge	*pab = (struct acpi_bridge *)context; +	struct acpi_bridge	*lab; +	acpi_status		status; +	u8			*path_name = acpi_path_name(handle); + +	/* get _SUN */ +	status = acpi_evaluate_integer(handle, METHOD_NAME__SUN, NULL, &sun); +	switch(status) { +	case AE_NOT_FOUND: +		return AE_OK; +	default: +		if (ACPI_FAILURE(status)) { +			err("acpi_shpchprm:%s _SUN fail=0x%x\n", path_name, status); +			return status; +		} +	} + +	/* get _ADR. _ADR must exist if _SUN exists */ +	status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); +	if (ACPI_FAILURE(status)) { +		err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status); +		return status; +	} + +	dbg("acpi_shpchprm:%s sun=0x%08x adr=0x%08x\n", path_name, (u32)sun, (u32)adr); + +	status = acpi_get_parent(handle, &phandle); +	if (ACPI_FAILURE(status)) { +		err("acpi_shpchprm:%s get_parent fail=0x%x\n", path_name, status); +		return (status); +	} + +	bus_num = pab->bus; +	seg_num = pab->seg; + +	if (pab->bus == bus_num) { +		lab = pab; +	} else { +		dbg("WARN: pab is not parent\n"); +		lab = find_acpi_bridge_by_bus(pab, seg_num, bus_num); +		if (!lab) { +			dbg("acpi_shpchprm: alloc new P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); +			lab = (struct acpi_bridge *)kmalloc(sizeof(struct acpi_bridge), GFP_KERNEL); +			if (!lab) { +				err("acpi_shpchprm: alloc for ab fail\n"); +				return AE_NO_MEMORY; +			} +			memset(lab, 0, sizeof(struct acpi_bridge)); + +			lab->handle = phandle; +			lab->pbus = pab->bus; +			lab->pdevice = (int)(padr >> 16) & 0xffff; +			lab->pfunction = (int)(padr & 0xffff); +			lab->bus = (int)bus_num; +			lab->scanned = 0; +			lab->type = BRIDGE_TYPE_P2P; + +			shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, lab); +		} else +			dbg("acpi_shpchprm: found P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); +	} + +	acpi_add_slot_to_php_slots(lab, (int)bus_num, handle, (u32)adr, (u32)sun); +	return (status); +} + +static int shpchprm_acpi_build_php_slots( +	struct acpi_bridge	*ab, +	u32			depth +	) +{ +	acpi_status	status; +	u8		*path_name = acpi_path_name(ab->handle); + +	/* Walk down this pci bridge to get _SUNs if any behind P2P */ +	status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, +				ab->handle, +				depth, +				shpchprm_acpi_build_php_slots_callback, +				ab, +				NULL ); +	if (ACPI_FAILURE(status)) { +		dbg("acpi_shpchprm:%s walk for _SUN on pci bridge seg:bus(%x:%x) fail=0x%x\n", path_name, ab->seg, ab->bus, status); +		return -1; +	} + +	return 0; +} + +static void build_a_bridge( +	struct acpi_bridge	*pab, +	struct acpi_bridge	*ab +	) +{ +	u8		*path_name = acpi_path_name(ab->handle); + +	shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, ab); + +	switch (ab->type) { +	case BRIDGE_TYPE_HOST: +		dbg("acpi_shpchprm: Registered PCI HOST Bridge(%02x)    on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", +			ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); +		break; +	case BRIDGE_TYPE_P2P: +		dbg("acpi_shpchprm: Registered PCI  P2P Bridge(%02x-%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", +			ab->pbus, ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); +		break; +	}; + +	/* build any immediate PHP slots under this pci bridge */ +	shpchprm_acpi_build_php_slots(ab, 1); +} + +static struct acpi_bridge * add_p2p_bridge( +	acpi_handle handle, +	struct acpi_bridge	*pab,	/* parent */ +	ulong	adr +	) +{ +	struct acpi_bridge	*ab; +	struct pci_dev	*pdev; +	ulong		devnum, funcnum; +	u8			*path_name = acpi_path_name(handle); + +	ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); +	if (!ab) { +		err("acpi_shpchprm: alloc for ab fail\n"); +		return NULL; +	} +	memset(ab, 0, sizeof(struct acpi_bridge)); + +	devnum = (adr >> 16) & 0xffff; +	funcnum = adr & 0xffff; + +	pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); +	if (!pdev || !pdev->subordinate) { +		err("acpi_shpchprm:%s is not a P2P Bridge\n", path_name); +		kfree(ab); +		return NULL; +	} + +	ab->handle = handle; +	ab->seg = pab->seg; +	ab->pbus = pab->bus;		/* or pdev->bus->number */ +	ab->pdevice = devnum;		/* or PCI_SLOT(pdev->devfn) */ +	ab->pfunction = funcnum;	/* or PCI_FUNC(pdev->devfn) */ +	ab->bus = pdev->subordinate->number; +	ab->scanned = 0; +	ab->type = BRIDGE_TYPE_P2P; + +	dbg("acpi_shpchprm: P2P(%x-%x) on pci=b:d:f(%x:%x:%x) acpi=b:d:f(%x:%x:%x) [%s]\n", +		pab->bus, ab->bus, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), +		pab->bus, (u32)devnum, (u32)funcnum, path_name); + +	build_a_bridge(pab, ab); + +	return ab; +} + +static acpi_status scan_p2p_bridge( +	acpi_handle		handle, +	u32			Level, +	void			*context, +	void			**retval +	) +{ +	struct acpi_bridge	*pab = (struct acpi_bridge *)context; +	struct acpi_bridge	*ab; +	acpi_status		status; +	ulong		adr = 0; +	u8			*path_name = acpi_path_name(handle); +	ulong		devnum, funcnum; +	struct pci_dev	*pdev; + +	/* get device, function */ +	status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); +	if (ACPI_FAILURE(status)) { +		if (status != AE_NOT_FOUND) +			err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status); +		return AE_OK; +	} + +	devnum = (adr >> 16) & 0xffff; +	funcnum = adr & 0xffff; + +	pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); +	if (!pdev) +		return AE_OK; +	if (!pdev->subordinate) +		return AE_OK; + +	ab = add_p2p_bridge(handle, pab, adr); +	if (ab) { +		status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, +					handle, +					(u32)1, +					scan_p2p_bridge, +					ab, +					NULL); +		if (ACPI_FAILURE(status)) +			dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status); +	} + +	return AE_OK; +} + +static struct acpi_bridge * add_host_bridge( +	acpi_handle handle, +	ulong	segnum, +	ulong	busnum +	) +{ +	ulong			adr = 0; +	acpi_status		status; +	struct acpi_bridge	*ab; +	u8			*path_name = acpi_path_name(handle); + +	/* get device, function: host br adr is always 0000 though.  */ +	status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); +	if (ACPI_FAILURE(status)) { +		err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status); +		return NULL; +	} +	dbg("acpi_shpchprm: ROOT PCI seg(0x%x)bus(0x%x)dev(0x%x)func(0x%x) [%s]\n", (u32)segnum, (u32)busnum,  +		(u32)(adr >> 16) & 0xffff, (u32)adr & 0xffff, path_name); + +	ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); +	if (!ab) { +		err("acpi_shpchprm: alloc for ab fail\n"); +		return NULL; +	} +	memset(ab, 0, sizeof(struct acpi_bridge)); + +	ab->handle = handle; +	ab->seg = (int)segnum; +	ab->bus = ab->pbus = (int)busnum; +	ab->pdevice = (int)(adr >> 16) & 0xffff; +	ab->pfunction = (int)(adr & 0xffff); +	ab->scanned = 0; +	ab->type = BRIDGE_TYPE_HOST; + +	/* get root pci bridge's current resources */ +	status = acpi_get_crs(ab); +	if (ACPI_FAILURE(status)) { +		err("acpi_shpchprm:%s evaluate _CRS fail=0x%x\n", path_name, status); +		kfree(ab); +		return NULL; +	} +	build_a_bridge(ab, ab); + +	return ab; +} + +static acpi_status acpi_scan_from_root_pci_callback ( +	acpi_handle	handle, +	u32			Level, +	void		*context, +	void		**retval +	) +{ +	ulong		segnum = 0; +	ulong		busnum = 0; +	acpi_status		status; +	struct acpi_bridge	*ab; +	u8			*path_name = acpi_path_name(handle); + +	/* get bus number of this pci root bridge */ +	status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segnum); +	if (ACPI_FAILURE(status)) { +		if (status != AE_NOT_FOUND) { +			err("acpi_shpchprm:%s evaluate _SEG fail=0x%x\n", path_name, status); +			return status; +		} +		segnum = 0; +	} + +	/* get bus number of this pci root bridge */ +	status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &busnum); +	if (ACPI_FAILURE(status)) { +		err("acpi_shpchprm:%s evaluate _BBN fail=0x%x\n", path_name, status); +		return (status); +	} + +	ab = add_host_bridge(handle, segnum, busnum); +	if (ab) { +		status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, +					handle, +					1, +					scan_p2p_bridge, +					ab, +					NULL); +		if (ACPI_FAILURE(status)) +			dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status); +	} + +	return AE_OK; +} + +static int shpchprm_acpi_scan_pci (void) +{ +	acpi_status	status; + +	/* +	 * TBD: traverse LDM device tree with the help of +	 *  unified ACPI augmented for php device population. +	 */ +	status = acpi_get_devices ( PCI_ROOT_HID_STRING, +				acpi_scan_from_root_pci_callback, +				NULL, +				NULL ); +	if (ACPI_FAILURE(status)) { +		err("acpi_shpchprm:get_device PCI ROOT HID fail=0x%x\n", status); +		return -1; +	} + +	return 0; +} + +int shpchprm_init(enum php_ctlr_type ctlr_type) +{ +	int	rc; + +	if (ctlr_type != PCI) +		return -ENODEV; + +	dbg("shpchprm ACPI init <enter>\n"); +	acpi_bridges_head = NULL; + +	/* construct PCI bus:device tree of acpi_handles */ +	rc = shpchprm_acpi_scan_pci(); +	if (rc) +		return rc; + +	dbg("shpchprm ACPI init %s\n", (rc)?"fail":"success"); +	return rc; +} + +static void free_a_slot(struct acpi_php_slot *aps) +{ +	dbg("        free a php func of slot(0x%02x) on PCI b:d:f=0x%02x:%02x:%02x\n", aps->sun, aps->bus, aps->dev, aps->fun); + +	free_pci_resource (aps->io_head); +	free_pci_resource (aps->bus_head); +	free_pci_resource (aps->mem_head); +	free_pci_resource (aps->p_mem_head); + +	kfree(aps); +} + +static void free_a_bridge( struct acpi_bridge	*ab) +{ +	struct acpi_php_slot	*aps, *next; + +	switch (ab->type) { +	case BRIDGE_TYPE_HOST: +		dbg("Free ACPI PCI HOST Bridge(%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", +			ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); +		break; +	case BRIDGE_TYPE_P2P: +		dbg("Free ACPI PCI P2P Bridge(%x-%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", +			ab->pbus, ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); +		break; +	}; + +	/* free slots first */ +	for (aps = ab->slots; aps; aps = next) { +		next = aps->next; +		free_a_slot(aps); +	} + +	free_pci_resource (ab->io_head); +	free_pci_resource (ab->tio_head); +	free_pci_resource (ab->bus_head); +	free_pci_resource (ab->tbus_head); +	free_pci_resource (ab->mem_head); +	free_pci_resource (ab->tmem_head); +	free_pci_resource (ab->p_mem_head); +	free_pci_resource (ab->tp_mem_head); + +	kfree(ab); +} + +static void shpchprm_free_bridges ( struct acpi_bridge	*ab) +{ +	if (!ab) +		return; + +	if (ab->child) +		shpchprm_free_bridges (ab->child); + +	if (ab->next) +		shpchprm_free_bridges (ab->next); + +	free_a_bridge(ab); +} + +void shpchprm_cleanup(void) +{ +	shpchprm_free_bridges (acpi_bridges_head); +} + +static int get_number_of_slots ( +	struct acpi_bridge	*ab, +	int				selfonly +	) +{ +	struct acpi_php_slot	*aps; +	int	prev_slot = -1; +	int	slot_num = 0; + +	for ( aps = ab->slots; aps; aps = aps->next) +		if (aps->dev != prev_slot) { +			prev_slot = aps->dev; +			slot_num++; +		} + +	if (ab->child) +		slot_num += get_number_of_slots (ab->child, 0); + +	if (selfonly) +		return slot_num; + +	if (ab->next) +		slot_num += get_number_of_slots (ab->next, 0); + +	return slot_num; +} + +static int print_acpi_resources (struct acpi_bridge	*ab) +{ +	struct acpi_php_slot	*aps; +	int	i; + +	switch (ab->type) { +	case BRIDGE_TYPE_HOST: +		dbg("PCI HOST Bridge (%x) [%s]\n", ab->bus, acpi_path_name(ab->handle)); +		break; +	case BRIDGE_TYPE_P2P: +		dbg("PCI P2P Bridge (%x-%x) [%s]\n", ab->pbus, ab->bus, acpi_path_name(ab->handle)); +		break; +	}; + +	print_pci_resources (ab); + +	for ( i = -1, aps = ab->slots; aps; aps = aps->next) { +		if (aps->dev == i) +			continue; +		dbg("  Slot sun(%x) s:b:d:f(%02x:%02x:%02x:%02x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun); +		print_slot_resources(aps); +		i = aps->dev; +	} + +	if (ab->child) +		print_acpi_resources (ab->child); + +	if (ab->next) +		print_acpi_resources (ab->next); + +	return 0; +} + +int shpchprm_print_pirt(void) +{ +	dbg("SHPCHPRM ACPI Slots\n"); +	if (acpi_bridges_head) +		print_acpi_resources (acpi_bridges_head); +	return 0; +} + +static struct acpi_php_slot * get_acpi_slot ( +	struct acpi_bridge *ab, +	u32 sun +	) +{ +	struct acpi_php_slot	*aps = NULL; + +	for ( aps = ab->slots; aps; aps = aps->next) +		if (aps->sun == sun) +			return aps; + +	if (!aps && ab->child) { +		aps = (struct acpi_php_slot *)get_acpi_slot (ab->child, sun); +		if (aps) +			return aps; +	} + +	if (!aps && ab->next) { +		aps = (struct acpi_php_slot *)get_acpi_slot (ab->next, sun); +		if (aps) +			return aps; +	} + +	return aps; + +} + +#if 0 +static void * shpchprm_get_slot(struct slot *slot) +{ +	struct acpi_bridge	*ab = acpi_bridges_head; +	struct acpi_php_slot	*aps = get_acpi_slot (ab, slot->number); + +	aps->slot = slot; + +	dbg("Got acpi slot sun(%x): s:b:d:f(%x:%x:%x:%x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun); + +	return (void *)aps; +} +#endif + +static void shpchprm_dump_func_res( struct pci_func *fun) +{ +	struct pci_func *func = fun; + +	if (func->bus_head) { +		dbg(":    BUS Resources:\n"); +		print_pci_resource (func->bus_head); +	} +	if (func->io_head) { +		dbg(":    IO Resources:\n"); +		print_pci_resource (func->io_head); +	} +	if (func->mem_head) { +		dbg(":    MEM Resources:\n"); +		print_pci_resource (func->mem_head); +	} +	if (func->p_mem_head) { +		dbg(":    PMEM Resources:\n"); +		print_pci_resource (func->p_mem_head); +	} +} + +static void shpchprm_dump_ctrl_res( struct controller *ctlr) +{ +	struct controller *ctrl = ctlr; + +	if (ctrl->bus_head) { +		dbg(":    BUS Resources:\n"); +		print_pci_resource (ctrl->bus_head); +	} +	if (ctrl->io_head) { +		dbg(":    IO Resources:\n"); +		print_pci_resource (ctrl->io_head); +	} +	if (ctrl->mem_head) { +		dbg(":    MEM Resources:\n"); +		print_pci_resource (ctrl->mem_head); +	} +	if (ctrl->p_mem_head) { +		dbg(":    PMEM Resources:\n"); +		print_pci_resource (ctrl->p_mem_head); +	} +} + +static int shpchprm_get_used_resources ( +	struct controller *ctrl, +	struct pci_func *func +	) +{ +	return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD); +} + +static int configure_existing_function( +	struct controller *ctrl, +	struct pci_func *func +	) +{ +	int rc; + +	/* see how much resources the func has used. */ +	rc = shpchprm_get_used_resources (ctrl, func); + +	if (!rc) { +		/* subtract the resources used by the func from ctrl resources */ +		rc  = shpchprm_delete_resources (&ctrl->bus_head, func->bus_head); +		rc |= shpchprm_delete_resources (&ctrl->io_head, func->io_head); +		rc |= shpchprm_delete_resources (&ctrl->mem_head, func->mem_head); +		rc |= shpchprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head); +		if (rc) +			warn("aCEF: cannot del used resources\n"); +	} else +		err("aCEF: cannot get used resources\n"); + +	return rc; +} + +static int bind_pci_resources_to_slots ( struct controller *ctrl) +{ +	struct pci_func *func, new_func; +	int busn = ctrl->slot_bus; +	int devn, funn; +	u32	vid; + +	for (devn = 0; devn < 32; devn++) { +		for (funn = 0; funn < 8; funn++) { +			/* +			if (devn == ctrl->device && funn == ctrl->function) +				continue; +			*/ +			/* find out if this entry is for an occupied slot */ +			vid = 0xFFFFFFFF; +			pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid); + +			if (vid != 0xFFFFFFFF) { +				func = shpchp_slot_find(busn, devn, funn); +				if (!func) { +					memset(&new_func, 0, sizeof(struct pci_func)); +					new_func.bus = busn; +					new_func.device = devn; +					new_func.function = funn; +					new_func.is_a_board = 1; +					configure_existing_function(ctrl, &new_func); +					shpchprm_dump_func_res(&new_func); +				} else { +					configure_existing_function(ctrl, func); +					shpchprm_dump_func_res(func); +				} +				dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus); +			} +		} +	} + +	return 0; +} + +static int bind_pci_resources( +	struct controller 	*ctrl, +	struct acpi_bridge	*ab +	) +{ +	int	status = 0; + +	if (ab->bus_head) { +		dbg("bapr:  BUS Resources add on PCI 0x%x\n", ab->bus); +		status = shpchprm_add_resources (&ctrl->bus_head, ab->bus_head); +		if (shpchprm_delete_resources (&ab->bus_head, ctrl->bus_head)) +			warn("bapr:  cannot sub BUS Resource on PCI 0x%x\n", ab->bus); +		if (status) { +			err("bapr:  BUS Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); +			return status; +		} +	} else +		info("bapr:  No BUS Resource on PCI 0x%x.\n", ab->bus); + +	if (ab->io_head) { +		dbg("bapr:  IO Resources add on PCI 0x%x\n", ab->bus); +		status = shpchprm_add_resources (&ctrl->io_head, ab->io_head); +		if (shpchprm_delete_resources (&ab->io_head, ctrl->io_head)) +			warn("bapr:  cannot sub IO Resource on PCI 0x%x\n", ab->bus); +		if (status) { +			err("bapr:  IO Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); +			return status; +		} +	} else +		info("bapr:  No  IO Resource on PCI 0x%x.\n", ab->bus); + +	if (ab->mem_head) { +		dbg("bapr:  MEM Resources add on PCI 0x%x\n", ab->bus); +		status = shpchprm_add_resources (&ctrl->mem_head, ab->mem_head); +		if (shpchprm_delete_resources (&ab->mem_head, ctrl->mem_head)) +			warn("bapr:  cannot sub MEM Resource on PCI 0x%x\n", ab->bus); +		if (status) { +			err("bapr:  MEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); +			return status; +		} +	} else +		info("bapr:  No MEM Resource on PCI 0x%x.\n", ab->bus); + +	if (ab->p_mem_head) { +		dbg("bapr:  PMEM Resources add on PCI 0x%x\n", ab->bus); +		status = shpchprm_add_resources (&ctrl->p_mem_head, ab->p_mem_head); +		if (shpchprm_delete_resources (&ab->p_mem_head, ctrl->p_mem_head)) +			warn("bapr:  cannot sub PMEM Resource on PCI 0x%x\n", ab->bus); +		if (status) { +			err("bapr:  PMEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); +			return status; +		} +	} else +		info("bapr:  No PMEM Resource on PCI 0x%x.\n", ab->bus); + +	return status; +} + +static int no_pci_resources( struct acpi_bridge *ab) +{ +	return !(ab->p_mem_head || ab->mem_head || ab->io_head || ab->bus_head); +} + +static int find_pci_bridge_resources ( +	struct controller *ctrl, +	struct acpi_bridge *ab +	) +{ +	int	rc = 0; +	struct pci_func func; + +	memset(&func, 0, sizeof(struct pci_func)); + +	func.bus = ab->pbus; +	func.device = ab->pdevice; +	func.function = ab->pfunction; +	func.is_a_board = 1; + +	/* Get used resources for this PCI bridge */ +	rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD); + +	ab->io_head = func.io_head; +	ab->mem_head = func.mem_head; +	ab->p_mem_head = func.p_mem_head; +	ab->bus_head = func.bus_head; +	if (ab->bus_head) +		shpchprm_delete_resource(&ab->bus_head, ctrl->bus, 1); + +	return rc; +} + +static int get_pci_resources_from_bridge( +	struct controller *ctrl, +	struct acpi_bridge *ab +	) +{ +	int	rc = 0; + +	dbg("grfb:  Get Resources for PCI 0x%x from actual PCI bridge 0x%x.\n", ctrl->bus, ab->bus); + +	rc = find_pci_bridge_resources (ctrl, ab); + +	shpchp_resource_sort_and_combine(&ab->bus_head); +	shpchp_resource_sort_and_combine(&ab->io_head); +	shpchp_resource_sort_and_combine(&ab->mem_head); +	shpchp_resource_sort_and_combine(&ab->p_mem_head); + +	shpchprm_add_resources (&ab->tbus_head, ab->bus_head); +	shpchprm_add_resources (&ab->tio_head, ab->io_head); +	shpchprm_add_resources (&ab->tmem_head, ab->mem_head); +	shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); + +	return rc; +} + +static int get_pci_resources( +	struct controller	*ctrl, +	struct acpi_bridge	*ab +	) +{ +	int	rc = 0; + +	if (no_pci_resources(ab)) { +		dbg("spbr:PCI 0x%x has no resources. Get parent resources.\n", ab->bus); +		rc = get_pci_resources_from_bridge(ctrl, ab); +	} + +	return rc; +} + +int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum) +{ +	int offset = devnum - ctrl->slot_device_offset; + +	dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset); +	*sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc *offset); +	return 0; +} + +/* + * Get resources for this ctrl. + *  1. get total resources from ACPI _CRS or bridge (this ctrl) + *  2. find used resources of existing adapters + *	3. subtract used resources from total resources + */ +int shpchprm_find_available_resources( struct controller *ctrl) +{ +	int rc = 0; +	struct acpi_bridge	*ab; + +	ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->pci_dev->subordinate->number); +	if (!ab) { +		err("pfar:cannot locate acpi bridge of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number); +		return -1; +	} +	if (no_pci_resources(ab)) { +		rc = get_pci_resources(ctrl, ab); +		if (rc) { +			err("pfar:cannot get pci resources of PCI 0x%x.\n",ctrl->pci_dev->subordinate->number); +			return -1; +		} +	} + +	rc = bind_pci_resources(ctrl, ab); +	dbg("pfar:pre-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); +	shpchprm_dump_ctrl_res(ctrl); + +	bind_pci_resources_to_slots (ctrl); + +	dbg("pfar:post-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); +	shpchprm_dump_ctrl_res(ctrl); + +	return rc; +} + +int shpchprm_set_hpp( +	struct controller *ctrl, +	struct pci_func *func, +	u8	card_type +	) +{ +	struct acpi_bridge	*ab; +	struct pci_bus lpci_bus, *pci_bus; +	int				rc = 0; +	unsigned int	devfn; +	u8				cls= 0x08;	/* default cache line size	*/ +	u8				lt = 0x40;	/* default latency timer	*/ +	u8				ep = 0; +	u8				es = 0; + +	memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); +	pci_bus = &lpci_bus; +	pci_bus->number = func->bus; +	devfn = PCI_DEVFN(func->device, func->function); + +	ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); + +	if (ab) { +		if (ab->_hpp) { +			lt  = (u8)ab->_hpp->latency_timer; +			cls = (u8)ab->_hpp->cache_line_size; +			ep  = (u8)ab->_hpp->enable_perr; +			es  = (u8)ab->_hpp->enable_serr; +		} else +			dbg("_hpp: no _hpp for B/D/F=%#x/%#x/%#x. use default value\n", func->bus, func->device, func->function); +	} else +		dbg("_hpp: no acpi bridge for B/D/F = %#x/%#x/%#x. use default value\n", func->bus, func->device, func->function); + + +	if (card_type == PCI_HEADER_TYPE_BRIDGE) { +		/* set subordinate Latency Timer */ +		rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, lt); +	} + +	/* set base Latency Timer */ +	rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, lt); +	dbg("  set latency timer  =0x%02x: %x\n", lt, rc); + +	rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, cls); +	dbg("  set cache_line_size=0x%02x: %x\n", cls, rc); + +	return rc; +} + +void shpchprm_enable_card( +	struct controller *ctrl, +	struct pci_func *func, +	u8 card_type) +{ +	u16 command, cmd, bcommand, bcmd; +	struct pci_bus lpci_bus, *pci_bus; +	struct acpi_bridge	*ab; +	unsigned int devfn; +	int rc; + +	memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); +	pci_bus = &lpci_bus; +	pci_bus->number = func->bus; +	devfn = PCI_DEVFN(func->device, func->function); + +	rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); + +	if (card_type == PCI_HEADER_TYPE_BRIDGE) { +		rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); +	} + +	cmd = command  = command | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE +		| PCI_COMMAND_IO | PCI_COMMAND_MEMORY; +	bcmd = bcommand  = bcommand | PCI_BRIDGE_CTL_NO_ISA; + +	ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); +	if (ab) { +		if (ab->_hpp) { +			if (ab->_hpp->enable_perr) { +				command |= PCI_COMMAND_PARITY; +				bcommand |= PCI_BRIDGE_CTL_PARITY; +			} else { +				command &= ~PCI_COMMAND_PARITY; +				bcommand &= ~PCI_BRIDGE_CTL_PARITY; +			} +			if (ab->_hpp->enable_serr) { +				command |= PCI_COMMAND_SERR; +				bcommand |= PCI_BRIDGE_CTL_SERR; +			} else { +				command &= ~PCI_COMMAND_SERR; +				bcommand &= ~PCI_BRIDGE_CTL_SERR; +			} +		} else +			dbg("no _hpp for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); +	} else +		dbg("no acpi bridge for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); + +	if (command != cmd) { +		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); +	} +	if ((card_type == PCI_HEADER_TYPE_BRIDGE) && (bcommand != bcmd)) { +		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); +	} +} +  |