acpi_pcihp.c 5.9 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * Common ACPI functions for hot plug platforms
L
Linus Torvalds 已提交
3
 *
4
 * Copyright (C) 2006 Intel Corporation
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * 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.
 *
23
 * Send feedback to <kristen.c.accardi@intel.com>
L
Linus Torvalds 已提交
24 25 26 27 28 29 30
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
31
#include <acpi/acpi.h>
L
Linus Torvalds 已提交
32 33
#include <acpi/acpi_bus.h>
#include <acpi/actypes.h>
34
#include "pci_hotplug.h"
L
Linus Torvalds 已提交
35 36 37 38 39

#define	METHOD_NAME__SUN	"_SUN"
#define	METHOD_NAME__HPP	"_HPP"
#define	METHOD_NAME_OSHP	"OSHP"

40 41 42 43 44 45 46
/* acpi_path_name
 *
 * @handle - the acpi_handle of the object who's name you want.
 *
 * Caller must free buffer.
 */
u8 * acpi_path_name(acpi_handle handle)
L
Linus Torvalds 已提交
47
{
48 49 50
	acpi_status status;
	struct acpi_buffer ret_buf = {ACPI_ALLOCATE_BUFFER, NULL};
	union acpi_object *obj;
L
Linus Torvalds 已提交
51 52

	status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf);
53
	if (ACPI_FAILURE(status)) {
L
Linus Torvalds 已提交
54
		return NULL;
55 56 57
	}
	obj = ret_buf.pointer;
	return obj->string.pointer;
L
Linus Torvalds 已提交
58
}
59 60 61
EXPORT_SYMBOL_GPL(acpi_path_name);


L
Linus Torvalds 已提交
62

63 64
static acpi_status
acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
L
Linus Torvalds 已提交
65 66 67 68 69
{
	acpi_status		status;
	u8			nui[4];
	struct acpi_buffer	ret_buf = { 0, NULL};
	union acpi_object	*ext_obj, *package;
70
	u8			*path_name = acpi_path_name(handle);
L
Linus Torvalds 已提交
71 72 73
	int			i, len = 0;

	/* get _hpp */
74
	status = acpi_evaluate_object(handle, METHOD_NAME__HPP, NULL, &ret_buf);
L
Linus Torvalds 已提交
75 76 77 78
	switch (status) {
	case AE_BUFFER_OVERFLOW:
		ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL);
		if (!ret_buf.pointer) {
79 80 81
			printk(KERN_ERR "%s:%s alloc for _HPP fail\n",
				__FUNCTION__, path_name);
			acpi_os_free(path_name);
82
			return AE_NO_MEMORY;
L
Linus Torvalds 已提交
83
		}
84 85
		status = acpi_evaluate_object(handle, METHOD_NAME__HPP,
				NULL, &ret_buf);
L
Linus Torvalds 已提交
86 87 88 89
		if (ACPI_SUCCESS(status))
			break;
	default:
		if (ACPI_FAILURE(status)) {
90
			pr_debug("%s:%s _HPP fail=0x%x\n", __FUNCTION__,
91
					path_name, status);
92
			acpi_os_free(path_name);
93
			return status;
L
Linus Torvalds 已提交
94 95 96 97 98
		}
	}

	ext_obj = (union acpi_object *) ret_buf.pointer;
	if (ext_obj->type != ACPI_TYPE_PACKAGE) {
99
		printk(KERN_ERR "%s:%s _HPP obj not a package\n", __FUNCTION__,
100 101
				path_name);
		status = AE_ERROR;
L
Linus Torvalds 已提交
102 103 104 105 106 107 108 109 110 111 112 113
		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:
114 115
			printk(KERN_ERR "%s:%s _HPP obj type incorrect\n",
				__FUNCTION__, path_name);
116
			status = AE_ERROR;
L
Linus Torvalds 已提交
117 118 119 120
			goto free_and_return;
		}
	}

121 122 123 124
	hpp->cache_line_size = nui[0];
	hpp->latency_timer = nui[1];
	hpp->enable_serr = nui[2];
	hpp->enable_perr = nui[3];
L
Linus Torvalds 已提交
125

126 127 128 129
	pr_debug("  _HPP: cache_line_size=0x%x\n", hpp->cache_line_size);
	pr_debug("  _HPP: latency timer  =0x%x\n", hpp->latency_timer);
	pr_debug("  _HPP: enable SERR    =0x%x\n", hpp->enable_serr);
	pr_debug("  _HPP: enable PERR    =0x%x\n", hpp->enable_perr);
L
Linus Torvalds 已提交
130 131

free_and_return:
132
	acpi_os_free(path_name);
L
Linus Torvalds 已提交
133
	kfree(ret_buf.pointer);
134
	return status;
L
Linus Torvalds 已提交
135 136
}

137 138 139 140 141 142 143


/* acpi_run_oshp - get control of hotplug from the firmware
 *
 * @handle - the handle of the hotplug controller.
 */
acpi_status acpi_run_oshp(acpi_handle handle)
L
Linus Torvalds 已提交
144 145
{
	acpi_status		status;
146
	u8			*path_name = acpi_path_name(handle);
L
Linus Torvalds 已提交
147 148

	/* run OSHP */
149
	status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL);
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
	if (ACPI_FAILURE(status))
		printk(KERN_ERR "%s:%s OSHP fails=0x%x\n", __FUNCTION__,
			path_name, status);
	else
		pr_debug("%s:%s OSHP passes\n", __FUNCTION__, path_name);
	acpi_os_free(path_name);
	return status;
}
EXPORT_SYMBOL_GPL(acpi_run_oshp);



/* acpi_get_hp_params_from_firmware
 *
 * @dev - the pci_dev of the newly added device
 * @hpp - allocated by the caller
 */
acpi_status acpi_get_hp_params_from_firmware(struct pci_dev *dev,
		struct hotplug_params *hpp)
{
	acpi_status status = AE_NOT_FOUND;
	struct pci_dev *pdev = dev;

	/*
	 * _HPP settings apply to all child buses, until another _HPP is
	 * encountered. If we don't find an _HPP for the input pci dev,
	 * look for it in the parent device scope since that would apply to
	 * this pci dev. If we don't find any _HPP, use hardcoded defaults
	 */
	while (pdev && (ACPI_FAILURE(status))) {
		acpi_handle handle = DEVICE_ACPI_HANDLE(&(pdev->dev));
		if (!handle)
			break;
		status = acpi_run_hpp(handle, hpp);
		if (!(pdev->bus->parent))
			break;
		/* Check if a parent object supports _HPP */
		pdev = pdev->bus->parent->self;
L
Linus Torvalds 已提交
188
	}
189
	return status;
L
Linus Torvalds 已提交
190
}
191 192
EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);

L
Linus Torvalds 已提交
193

194 195 196 197 198
/* acpi_root_bridge - check to see if this acpi object is a root bridge
 *
 * @handle - the acpi object in question.
 */
int acpi_root_bridge(acpi_handle handle)
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
{
	acpi_status status;
	struct acpi_device_info *info;
	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
	int i;

	status = acpi_get_object_info(handle, &buffer);
	if (ACPI_SUCCESS(status)) {
		info = buffer.pointer;
		if ((info->valid & ACPI_VALID_HID) &&
			!strcmp(PCI_ROOT_HID_STRING,
					info->hardware_id.value)) {
			acpi_os_free(buffer.pointer);
			return 1;
		}
		if (info->valid & ACPI_VALID_CID) {
			for (i=0; i < info->compatibility_id.count; i++) {
				if (!strcmp(PCI_ROOT_HID_STRING,
					info->compatibility_id.id[i].value)) {
					acpi_os_free(buffer.pointer);
					return 1;
				}
			}
		}
223
		acpi_os_free(buffer.pointer);
224 225 226
	}
	return 0;
}
227
EXPORT_SYMBOL_GPL(acpi_root_bridge);