📄 shpchprm_acpi.c
字号:
/* * 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); struct acpi_buffer ret_buf = { 0, NULL}; /* run OSHP */ status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, &ret_buf); 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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -