shpchprm_acpi.c 5.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * 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.
 *
23
 * Send feedback to <kristen.c.accardi@intel.com>
L
Linus Torvalds 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/actypes.h>
#include "shpchp.h"

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

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;	
}

55 56
static acpi_status
acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
L
Linus Torvalds 已提交
57 58 59 60 61
{
	acpi_status		status;
	u8			nui[4];
	struct acpi_buffer	ret_buf = { 0, NULL};
	union acpi_object	*ext_obj, *package;
62
	u8			*path_name = acpi_path_name(handle);
L
Linus Torvalds 已提交
63 64 65
	int			i, len = 0;

	/* get _hpp */
66
	status = acpi_evaluate_object(handle, METHOD_NAME__HPP, NULL, &ret_buf);
L
Linus Torvalds 已提交
67 68 69 70
	switch (status) {
	case AE_BUFFER_OVERFLOW:
		ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL);
		if (!ret_buf.pointer) {
71 72 73
			err ("%s:%s alloc for _HPP fail\n", __FUNCTION__,
					path_name);
			return AE_NO_MEMORY;
L
Linus Torvalds 已提交
74
		}
75 76
		status = acpi_evaluate_object(handle, METHOD_NAME__HPP,
				NULL, &ret_buf);
L
Linus Torvalds 已提交
77 78 79 80
		if (ACPI_SUCCESS(status))
			break;
	default:
		if (ACPI_FAILURE(status)) {
81 82 83
			dbg("%s:%s _HPP fail=0x%x\n", __FUNCTION__,
					path_name, status);
			return status;
L
Linus Torvalds 已提交
84 85 86 87 88
		}
	}

	ext_obj = (union acpi_object *) ret_buf.pointer;
	if (ext_obj->type != ACPI_TYPE_PACKAGE) {
89 90 91
		err ("%s:%s _HPP obj not a package\n", __FUNCTION__,
				path_name);
		status = AE_ERROR;
L
Linus Torvalds 已提交
92 93 94 95 96 97 98 99 100 101 102 103
		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:
104 105 106
			err ("%s:%s _HPP obj type incorrect\n", __FUNCTION__,
					path_name);
			status = AE_ERROR;
L
Linus Torvalds 已提交
107 108 109 110
			goto free_and_return;
		}
	}

111 112 113 114
	hpp->cache_line_size = nui[0];
	hpp->latency_timer = nui[1];
	hpp->enable_serr = nui[2];
	hpp->enable_perr = nui[3];
L
Linus Torvalds 已提交
115

116 117 118 119
	dbg("  _HPP: cache_line_size=0x%x\n", hpp->cache_line_size);
	dbg("  _HPP: latency timer  =0x%x\n", hpp->latency_timer);
	dbg("  _HPP: enable SERR    =0x%x\n", hpp->enable_serr);
	dbg("  _HPP: enable PERR    =0x%x\n", hpp->enable_perr);
L
Linus Torvalds 已提交
120 121 122

free_and_return:
	kfree(ret_buf.pointer);
123
	return status;
L
Linus Torvalds 已提交
124 125
}

126
static void acpi_run_oshp(acpi_handle handle)
L
Linus Torvalds 已提交
127 128 129 130
{
	acpi_status		status;
	u8			*path_name = acpi_path_name(handle);

131 132
	/* run OSHP */
	status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL);
L
Linus Torvalds 已提交
133
	if (ACPI_FAILURE(status)) {
134 135
		err("%s:%s OSHP fails=0x%x\n", __FUNCTION__, path_name,
				status);
L
Linus Torvalds 已提交
136
	} else {
137
		dbg("%s:%s OSHP passes\n", __FUNCTION__, path_name);
L
Linus Torvalds 已提交
138 139 140 141 142 143 144 145 146 147 148 149
	}
}

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;
}

150
void get_hp_hw_control_from_firmware(struct pci_dev *dev)
L
Linus Torvalds 已提交
151
{
152 153 154 155 156 157 158 159 160
	/*
	 * OSHP is an optional ACPI firmware control method. If present,
	 * we need to run it to inform BIOS that we will control SHPC
	 * hardware from now on.
	 */
	acpi_handle handle = DEVICE_ACPI_HANDLE(&(dev->dev));
	if (!handle)
		return;
	acpi_run_oshp(handle);
L
Linus Torvalds 已提交
161 162
}

163 164
void get_hp_params_from_firmware(struct pci_dev *dev,
		struct hotplug_params *hpp)
L
Linus Torvalds 已提交
165
{
166 167
	acpi_status status = AE_NOT_FOUND;
	struct pci_dev *pdev = dev;
L
Linus Torvalds 已提交
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	/*
	 * _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 已提交
184 185 186
	}
}