acpi_pcihp.c 5.7 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 40

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


41 42
static acpi_status
acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
L
Linus Torvalds 已提交
43 44 45 46
{
	acpi_status		status;
	u8			nui[4];
	struct acpi_buffer	ret_buf = { 0, NULL};
47
	struct acpi_buffer	string = { ACPI_ALLOCATE_BUFFER, NULL };
L
Linus Torvalds 已提交
48 49 50
	union acpi_object	*ext_obj, *package;
	int			i, len = 0;

51 52
	acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);

L
Linus Torvalds 已提交
53
	/* get _hpp */
54
	status = acpi_evaluate_object(handle, METHOD_NAME__HPP, NULL, &ret_buf);
L
Linus Torvalds 已提交
55 56 57 58
	switch (status) {
	case AE_BUFFER_OVERFLOW:
		ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL);
		if (!ret_buf.pointer) {
59
			printk(KERN_ERR "%s:%s alloc for _HPP fail\n",
60 61
				__FUNCTION__, (char *)string.pointer);
			acpi_os_free(string.pointer);
62
			return AE_NO_MEMORY;
L
Linus Torvalds 已提交
63
		}
64 65
		status = acpi_evaluate_object(handle, METHOD_NAME__HPP,
				NULL, &ret_buf);
L
Linus Torvalds 已提交
66 67 68 69
		if (ACPI_SUCCESS(status))
			break;
	default:
		if (ACPI_FAILURE(status)) {
70
			pr_debug("%s:%s _HPP fail=0x%x\n", __FUNCTION__,
71 72
				(char *)string.pointer, status);
			acpi_os_free(string.pointer);
73
			return status;
L
Linus Torvalds 已提交
74 75 76 77 78
		}
	}

	ext_obj = (union acpi_object *) ret_buf.pointer;
	if (ext_obj->type != ACPI_TYPE_PACKAGE) {
79
		printk(KERN_ERR "%s:%s _HPP obj not a package\n", __FUNCTION__,
80
				(char *)string.pointer);
81
		status = AE_ERROR;
L
Linus Torvalds 已提交
82 83 84 85 86 87 88 89 90 91 92 93
		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:
94
			printk(KERN_ERR "%s:%s _HPP obj type incorrect\n",
95
				__FUNCTION__, (char *)string.pointer);
96
			status = AE_ERROR;
L
Linus Torvalds 已提交
97 98 99 100
			goto free_and_return;
		}
	}

101 102 103 104
	hpp->cache_line_size = nui[0];
	hpp->latency_timer = nui[1];
	hpp->enable_serr = nui[2];
	hpp->enable_perr = nui[3];
L
Linus Torvalds 已提交
105

106 107 108 109
	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 已提交
110 111

free_and_return:
112 113
	acpi_os_free(string.pointer);
	acpi_os_free(ret_buf.pointer);
114
	return status;
L
Linus Torvalds 已提交
115 116
}

117 118 119 120 121 122 123


/* 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 已提交
124 125
{
	acpi_status		status;
126 127 128
	struct acpi_buffer	string = { ACPI_ALLOCATE_BUFFER, NULL };

	acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
L
Linus Torvalds 已提交
129 130

	/* run OSHP */
131
	status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL);
132 133
	if (ACPI_FAILURE(status))
		printk(KERN_ERR "%s:%s OSHP fails=0x%x\n", __FUNCTION__,
134
			(char *)string.pointer, status);
135
	else
136 137 138 139
		pr_debug("%s:%s OSHP passes\n", __FUNCTION__,
			(char *)string.pointer);

	acpi_os_free(string.pointer);
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
	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 已提交
172
	}
173
	return status;
L
Linus Torvalds 已提交
174
}
175 176
EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);

L
Linus Torvalds 已提交
177

178 179 180 181 182
/* 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)
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
{
	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;
				}
			}
		}
207
		acpi_os_free(buffer.pointer);
208 209 210
	}
	return 0;
}
211
EXPORT_SYMBOL_GPL(acpi_root_bridge);