Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
181
drivers/pci/hotplug/Kconfig
Normal file
181
drivers/pci/hotplug/Kconfig
Normal file
@@ -0,0 +1,181 @@
|
||||
#
|
||||
# PCI Hotplug support
|
||||
#
|
||||
|
||||
menu "PCI Hotplug Support"
|
||||
|
||||
config HOTPLUG_PCI
|
||||
tristate "Support for PCI Hotplug (EXPERIMENTAL)"
|
||||
depends on PCI && EXPERIMENTAL && HOTPLUG
|
||||
---help---
|
||||
Say Y here if you have a motherboard with a PCI Hotplug controller.
|
||||
This allows you to add and remove PCI cards while the machine is
|
||||
powered up and running.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pci_hotplug.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_FAKE
|
||||
tristate "Fake PCI Hotplug driver"
|
||||
depends on HOTPLUG_PCI
|
||||
help
|
||||
Say Y here if you want to use the fake PCI hotplug driver. It can
|
||||
be used to simulate PCI hotplug events if even if your system is
|
||||
not PCI hotplug capable.
|
||||
|
||||
This driver will "emulate" removing PCI devices from the system.
|
||||
If the "power" file is written to with "0" then the specified PCI
|
||||
device will be completely removed from the kernel.
|
||||
|
||||
WARNING, this does NOT turn off the power to the PCI device.
|
||||
This is a "logical" removal, not a physical or electrical
|
||||
removal.
|
||||
|
||||
Use this module at your own risk. You have been warned!
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called fakephp.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_COMPAQ
|
||||
tristate "Compaq PCI Hotplug driver"
|
||||
depends on HOTPLUG_PCI && X86 && PCI_BIOS
|
||||
help
|
||||
Say Y here if you have a motherboard with a Compaq PCI Hotplug
|
||||
controller.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cpqphp.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_COMPAQ_NVRAM
|
||||
bool "Save configuration into NVRAM on Compaq servers"
|
||||
depends on HOTPLUG_PCI_COMPAQ
|
||||
help
|
||||
Say Y here if you have a Compaq server that has a PCI Hotplug
|
||||
controller. This will allow the PCI Hotplug driver to store the PCI
|
||||
system configuration options in NVRAM.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_IBM
|
||||
tristate "IBM PCI Hotplug driver"
|
||||
depends on HOTPLUG_PCI && X86_IO_APIC && X86 && PCI_BIOS
|
||||
help
|
||||
Say Y here if you have a motherboard with a IBM PCI Hotplug
|
||||
controller.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ibmphp.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_ACPI
|
||||
tristate "ACPI PCI Hotplug driver"
|
||||
depends on HOTPLUG_PCI
|
||||
depends on (!ACPI_DOCK && ACPI) || (ACPI_DOCK)
|
||||
help
|
||||
Say Y here if you have a system that supports PCI Hotplug using
|
||||
ACPI.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called acpiphp.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_ACPI_IBM
|
||||
tristate "ACPI PCI Hotplug driver IBM extensions"
|
||||
depends on HOTPLUG_PCI_ACPI
|
||||
help
|
||||
Say Y here if you have an IBM system that supports PCI Hotplug using
|
||||
ACPI.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called acpiphp_ibm.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_CPCI
|
||||
bool "CompactPCI Hotplug driver"
|
||||
depends on HOTPLUG_PCI
|
||||
help
|
||||
Say Y here if you have a CompactPCI system card with CompactPCI
|
||||
hotswap support per the PICMG 2.1 specification.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_CPCI_ZT5550
|
||||
tristate "Ziatech ZT5550 CompactPCI Hotplug driver"
|
||||
depends on HOTPLUG_PCI && HOTPLUG_PCI_CPCI && X86
|
||||
help
|
||||
Say Y here if you have an Performance Technologies (formerly Intel,
|
||||
formerly just Ziatech) Ziatech ZT5550 CompactPCI system card.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cpcihp_zt5550.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_CPCI_GENERIC
|
||||
tristate "Generic port I/O CompactPCI Hotplug driver"
|
||||
depends on HOTPLUG_PCI && HOTPLUG_PCI_CPCI && X86
|
||||
help
|
||||
Say Y here if you have a CompactPCI system card that exposes the #ENUM
|
||||
hotswap signal as a bit in a system register that can be read through
|
||||
standard port I/O.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cpcihp_generic.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_SHPC
|
||||
tristate "SHPC PCI Hotplug driver"
|
||||
depends on HOTPLUG_PCI
|
||||
help
|
||||
Say Y here if you have a motherboard with a SHPC PCI Hotplug
|
||||
controller.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called shpchp.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_RPA
|
||||
tristate "RPA PCI Hotplug driver"
|
||||
depends on HOTPLUG_PCI && PPC_PSERIES && PPC64 && !HOTPLUG_PCI_FAKE
|
||||
help
|
||||
Say Y here if you have a RPA system that supports PCI Hotplug.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rpaphp.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_RPA_DLPAR
|
||||
tristate "RPA Dynamic Logical Partitioning for I/O slots"
|
||||
depends on HOTPLUG_PCI_RPA
|
||||
help
|
||||
Say Y here if your system supports Dynamic Logical Partitioning
|
||||
for I/O slots.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rpadlpar_io.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_SGI
|
||||
tristate "SGI PCI Hotplug Support"
|
||||
depends on HOTPLUG_PCI && (IA64_SGI_SN2 || IA64_GENERIC)
|
||||
help
|
||||
Say Y here if you want to use the SGI Altix Hotplug
|
||||
Driver for PCI devices.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
endmenu
|
||||
|
||||
61
drivers/pci/hotplug/Makefile
Normal file
61
drivers/pci/hotplug/Makefile
Normal file
@@ -0,0 +1,61 @@
|
||||
#
|
||||
# Makefile for the Linux kernel pci hotplug controller drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o
|
||||
|
||||
pci_hotplug-objs := pci_hotplug_core.o
|
||||
|
||||
ifdef CONFIG_HOTPLUG_PCI_CPCI
|
||||
pci_hotplug-objs += cpci_hotplug_core.o \
|
||||
cpci_hotplug_pci.o
|
||||
endif
|
||||
ifdef CONFIG_ACPI
|
||||
pci_hotplug-objs += acpi_pcihp.o
|
||||
endif
|
||||
|
||||
cpqphp-objs := cpqphp_core.o \
|
||||
cpqphp_ctrl.o \
|
||||
cpqphp_sysfs.o \
|
||||
cpqphp_pci.o
|
||||
cpqphp-$(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM) += cpqphp_nvram.o
|
||||
cpqphp-objs += $(cpqphp-y)
|
||||
|
||||
ibmphp-objs := ibmphp_core.o \
|
||||
ibmphp_ebda.o \
|
||||
ibmphp_pci.o \
|
||||
ibmphp_res.o \
|
||||
ibmphp_hpc.o
|
||||
|
||||
acpiphp-objs := acpiphp_core.o \
|
||||
acpiphp_glue.o
|
||||
|
||||
rpaphp-objs := rpaphp_core.o \
|
||||
rpaphp_pci.o \
|
||||
rpaphp_slot.o
|
||||
|
||||
rpadlpar_io-objs := rpadlpar_core.o \
|
||||
rpadlpar_sysfs.o
|
||||
|
||||
pciehp-objs := pciehp_core.o \
|
||||
pciehp_ctrl.o \
|
||||
pciehp_pci.o \
|
||||
pciehp_hpc.o
|
||||
|
||||
shpchp-objs := shpchp_core.o \
|
||||
shpchp_ctrl.o \
|
||||
shpchp_pci.o \
|
||||
shpchp_sysfs.o \
|
||||
shpchp_hpc.o
|
||||
414
drivers/pci/hotplug/acpi_pcihp.c
Normal file
414
drivers/pci/hotplug/acpi_pcihp.c
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Common ACPI functions for hot plug platforms
|
||||
*
|
||||
* Copyright (C) 2006 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 <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <acpi/acpi.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include <acpi/actypes.h>
|
||||
|
||||
#define MY_NAME "acpi_pcihp"
|
||||
|
||||
#define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
|
||||
|
||||
#define METHOD_NAME__SUN "_SUN"
|
||||
#define METHOD_NAME__HPP "_HPP"
|
||||
#define METHOD_NAME_OSHP "OSHP"
|
||||
|
||||
static int debug_acpi;
|
||||
|
||||
static acpi_status
|
||||
decode_type0_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
|
||||
{
|
||||
int i;
|
||||
union acpi_object *fields = record->package.elements;
|
||||
u32 revision = fields[1].integer.value;
|
||||
|
||||
switch (revision) {
|
||||
case 1:
|
||||
if (record->package.count != 6)
|
||||
return AE_ERROR;
|
||||
for (i = 2; i < 6; i++)
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER)
|
||||
return AE_ERROR;
|
||||
hpx->t0 = &hpx->type0_data;
|
||||
hpx->t0->revision = revision;
|
||||
hpx->t0->cache_line_size = fields[2].integer.value;
|
||||
hpx->t0->latency_timer = fields[3].integer.value;
|
||||
hpx->t0->enable_serr = fields[4].integer.value;
|
||||
hpx->t0->enable_perr = fields[5].integer.value;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"%s: Type 0 Revision %d record not supported\n",
|
||||
__FUNCTION__, revision);
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
decode_type1_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
|
||||
{
|
||||
int i;
|
||||
union acpi_object *fields = record->package.elements;
|
||||
u32 revision = fields[1].integer.value;
|
||||
|
||||
switch (revision) {
|
||||
case 1:
|
||||
if (record->package.count != 5)
|
||||
return AE_ERROR;
|
||||
for (i = 2; i < 5; i++)
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER)
|
||||
return AE_ERROR;
|
||||
hpx->t1 = &hpx->type1_data;
|
||||
hpx->t1->revision = revision;
|
||||
hpx->t1->max_mem_read = fields[2].integer.value;
|
||||
hpx->t1->avg_max_split = fields[3].integer.value;
|
||||
hpx->t1->tot_max_split = fields[4].integer.value;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"%s: Type 1 Revision %d record not supported\n",
|
||||
__FUNCTION__, revision);
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
decode_type2_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
|
||||
{
|
||||
int i;
|
||||
union acpi_object *fields = record->package.elements;
|
||||
u32 revision = fields[1].integer.value;
|
||||
|
||||
switch (revision) {
|
||||
case 1:
|
||||
if (record->package.count != 18)
|
||||
return AE_ERROR;
|
||||
for (i = 2; i < 18; i++)
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER)
|
||||
return AE_ERROR;
|
||||
hpx->t2 = &hpx->type2_data;
|
||||
hpx->t2->revision = revision;
|
||||
hpx->t2->unc_err_mask_and = fields[2].integer.value;
|
||||
hpx->t2->unc_err_mask_or = fields[3].integer.value;
|
||||
hpx->t2->unc_err_sever_and = fields[4].integer.value;
|
||||
hpx->t2->unc_err_sever_or = fields[5].integer.value;
|
||||
hpx->t2->cor_err_mask_and = fields[6].integer.value;
|
||||
hpx->t2->cor_err_mask_or = fields[7].integer.value;
|
||||
hpx->t2->adv_err_cap_and = fields[8].integer.value;
|
||||
hpx->t2->adv_err_cap_or = fields[9].integer.value;
|
||||
hpx->t2->pci_exp_devctl_and = fields[10].integer.value;
|
||||
hpx->t2->pci_exp_devctl_or = fields[11].integer.value;
|
||||
hpx->t2->pci_exp_lnkctl_and = fields[12].integer.value;
|
||||
hpx->t2->pci_exp_lnkctl_or = fields[13].integer.value;
|
||||
hpx->t2->sec_unc_err_sever_and = fields[14].integer.value;
|
||||
hpx->t2->sec_unc_err_sever_or = fields[15].integer.value;
|
||||
hpx->t2->sec_unc_err_mask_and = fields[16].integer.value;
|
||||
hpx->t2->sec_unc_err_mask_or = fields[17].integer.value;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"%s: Type 2 Revision %d record not supported\n",
|
||||
__FUNCTION__, revision);
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
union acpi_object *package, *record, *fields;
|
||||
u32 type;
|
||||
int i;
|
||||
|
||||
/* Clear the return buffer with zeros */
|
||||
memset(hpx, 0, sizeof(struct hotplug_params));
|
||||
|
||||
status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
package = (union acpi_object *)buffer.pointer;
|
||||
if (package->type != ACPI_TYPE_PACKAGE) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < package->package.count; i++) {
|
||||
record = &package->package.elements[i];
|
||||
if (record->type != ACPI_TYPE_PACKAGE) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fields = record->package.elements;
|
||||
if (fields[0].type != ACPI_TYPE_INTEGER ||
|
||||
fields[1].type != ACPI_TYPE_INTEGER) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
type = fields[0].integer.value;
|
||||
switch (type) {
|
||||
case 0:
|
||||
status = decode_type0_hpx_record(record, hpx);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto exit;
|
||||
break;
|
||||
case 1:
|
||||
status = decode_type1_hpx_record(record, hpx);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto exit;
|
||||
break;
|
||||
case 2:
|
||||
status = decode_type2_hpx_record(record, hpx);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto exit;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: Type %d record not supported\n",
|
||||
__FUNCTION__, type);
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
kfree(buffer.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
|
||||
{
|
||||
acpi_status status;
|
||||
u8 nui[4];
|
||||
struct acpi_buffer ret_buf = { 0, NULL};
|
||||
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *ext_obj, *package;
|
||||
int i, len = 0;
|
||||
|
||||
acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
|
||||
|
||||
/* Clear the return buffer with zeros */
|
||||
memset(hpp, 0, sizeof(struct hotplug_params));
|
||||
|
||||
/* get _hpp */
|
||||
status = acpi_evaluate_object(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) {
|
||||
printk(KERN_ERR "%s:%s alloc for _HPP fail\n",
|
||||
__FUNCTION__, (char *)string.pointer);
|
||||
kfree(string.pointer);
|
||||
return AE_NO_MEMORY;
|
||||
}
|
||||
status = acpi_evaluate_object(handle, METHOD_NAME__HPP,
|
||||
NULL, &ret_buf);
|
||||
if (ACPI_SUCCESS(status))
|
||||
break;
|
||||
default:
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_debug("%s:%s _HPP fail=0x%x\n", __FUNCTION__,
|
||||
(char *)string.pointer, status);
|
||||
kfree(string.pointer);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
ext_obj = (union acpi_object *) ret_buf.pointer;
|
||||
if (ext_obj->type != ACPI_TYPE_PACKAGE) {
|
||||
printk(KERN_ERR "%s:%s _HPP obj not a package\n", __FUNCTION__,
|
||||
(char *)string.pointer);
|
||||
status = AE_ERROR;
|
||||
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:
|
||||
printk(KERN_ERR "%s:%s _HPP obj type incorrect\n",
|
||||
__FUNCTION__, (char *)string.pointer);
|
||||
status = AE_ERROR;
|
||||
goto free_and_return;
|
||||
}
|
||||
}
|
||||
|
||||
hpp->t0 = &hpp->type0_data;
|
||||
hpp->t0->cache_line_size = nui[0];
|
||||
hpp->t0->latency_timer = nui[1];
|
||||
hpp->t0->enable_serr = nui[2];
|
||||
hpp->t0->enable_perr = nui[3];
|
||||
|
||||
pr_debug(" _HPP: cache_line_size=0x%x\n", hpp->t0->cache_line_size);
|
||||
pr_debug(" _HPP: latency timer =0x%x\n", hpp->t0->latency_timer);
|
||||
pr_debug(" _HPP: enable SERR =0x%x\n", hpp->t0->enable_serr);
|
||||
pr_debug(" _HPP: enable PERR =0x%x\n", hpp->t0->enable_perr);
|
||||
|
||||
free_and_return:
|
||||
kfree(string.pointer);
|
||||
kfree(ret_buf.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
|
||||
|
||||
/* run OSHP */
|
||||
status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
if (status != AE_NOT_FOUND)
|
||||
printk(KERN_ERR "%s:%s OSHP fails=0x%x\n",
|
||||
__FUNCTION__, (char *)string.pointer, status);
|
||||
else
|
||||
dbg("%s:%s OSHP not found\n",
|
||||
__FUNCTION__, (char *)string.pointer);
|
||||
else
|
||||
pr_debug("%s:%s OSHP passes\n", __FUNCTION__,
|
||||
(char *)string.pointer);
|
||||
|
||||
kfree(string.pointer);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_run_oshp);
|
||||
|
||||
|
||||
|
||||
/* acpi_get_hp_params_from_firmware
|
||||
*
|
||||
* @bus - the pci_bus of the bus on which the device is newly added
|
||||
* @hpp - allocated by the caller
|
||||
*/
|
||||
acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
|
||||
struct hotplug_params *hpp)
|
||||
{
|
||||
acpi_status status = AE_NOT_FOUND;
|
||||
acpi_handle handle, phandle;
|
||||
struct pci_bus *pbus = bus;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
do {
|
||||
pdev = pbus->self;
|
||||
if (!pdev) {
|
||||
handle = acpi_get_pci_rootbridge_handle(
|
||||
pci_domain_nr(pbus), pbus->number);
|
||||
break;
|
||||
}
|
||||
handle = DEVICE_ACPI_HANDLE(&(pdev->dev));
|
||||
pbus = pbus->parent;
|
||||
} while (!handle);
|
||||
|
||||
/*
|
||||
* _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 (handle) {
|
||||
status = acpi_run_hpx(handle, hpp);
|
||||
if (ACPI_SUCCESS(status))
|
||||
break;
|
||||
status = acpi_run_hpp(handle, hpp);
|
||||
if (ACPI_SUCCESS(status))
|
||||
break;
|
||||
if (acpi_root_bridge(handle))
|
||||
break;
|
||||
status = acpi_get_parent(handle, &phandle);
|
||||
if (ACPI_FAILURE(status))
|
||||
break;
|
||||
handle = phandle;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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)) {
|
||||
kfree(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)) {
|
||||
kfree(buffer.pointer);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
kfree(buffer.pointer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_root_bridge);
|
||||
|
||||
module_param(debug_acpi, bool, 0644);
|
||||
MODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not");
|
||||
223
drivers/pci/hotplug/acpiphp.h
Normal file
223
drivers/pci/hotplug/acpiphp.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* ACPI PCI Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
|
||||
* Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
|
||||
* Copyright (C) 2002,2003 NEC Corporation
|
||||
* Copyright (C) 2003-2005 Matthew Wilcox (matthew.wilcox@hp.com)
|
||||
* Copyright (C) 2003-2005 Hewlett Packard
|
||||
*
|
||||
* 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 <gregkh@us.ibm.com>,
|
||||
* <t-kochi@bq.jp.nec.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ACPIPHP_H
|
||||
#define _ACPIPHP_H
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/kobject.h> /* for KOBJ_NAME_LEN */
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (acpiphp_debug) \
|
||||
printk(KERN_DEBUG "%s: " format, \
|
||||
MY_NAME , ## arg); \
|
||||
} while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
|
||||
|
||||
/* name size which is used for entries in pcihpfs */
|
||||
#define SLOT_NAME_SIZE KOBJ_NAME_LEN /* {_SUN} */
|
||||
|
||||
struct acpiphp_bridge;
|
||||
struct acpiphp_slot;
|
||||
|
||||
/*
|
||||
* struct slot - slot information for each *physical* slot
|
||||
*/
|
||||
struct slot {
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct acpiphp_slot *acpi_slot;
|
||||
struct hotplug_slot_info info;
|
||||
char name[SLOT_NAME_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpiphp_bridge - PCI bridge information
|
||||
*
|
||||
* for each bridge device in ACPI namespace
|
||||
*/
|
||||
struct acpiphp_bridge {
|
||||
struct list_head list;
|
||||
acpi_handle handle;
|
||||
struct acpiphp_slot *slots;
|
||||
|
||||
/* Ejectable PCI-to-PCI bridge (PCI bridge and PCI function) */
|
||||
struct acpiphp_func *func;
|
||||
|
||||
int type;
|
||||
int nr_slots;
|
||||
|
||||
u32 flags;
|
||||
|
||||
/* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */
|
||||
struct pci_bus *pci_bus;
|
||||
|
||||
/* PCI-to-PCI bridge device */
|
||||
struct pci_dev *pci_dev;
|
||||
|
||||
/* ACPI 2.0 _HPP parameters */
|
||||
struct hotplug_params hpp;
|
||||
|
||||
spinlock_t res_lock;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct acpiphp_slot - PCI slot information
|
||||
*
|
||||
* PCI slot information for each *physical* PCI slot
|
||||
*/
|
||||
struct acpiphp_slot {
|
||||
struct acpiphp_slot *next;
|
||||
struct acpiphp_bridge *bridge; /* parent */
|
||||
struct list_head funcs; /* one slot may have different
|
||||
objects (i.e. for each function) */
|
||||
struct slot *slot;
|
||||
struct mutex crit_sect;
|
||||
|
||||
u8 device; /* pci device# */
|
||||
|
||||
u32 sun; /* ACPI _SUN (slot unique number) */
|
||||
u32 slotno; /* slot number relative to bridge */
|
||||
u32 flags; /* see below */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct acpiphp_func - PCI function information
|
||||
*
|
||||
* PCI function information for each object in ACPI namespace
|
||||
* typically 8 objects per slot (i.e. for each PCI function)
|
||||
*/
|
||||
struct acpiphp_func {
|
||||
struct acpiphp_slot *slot; /* parent */
|
||||
struct acpiphp_bridge *bridge; /* Ejectable PCI-to-PCI bridge */
|
||||
|
||||
struct list_head sibling;
|
||||
struct pci_dev *pci_dev;
|
||||
struct notifier_block nb;
|
||||
acpi_handle handle;
|
||||
|
||||
u8 function; /* pci function# */
|
||||
u32 flags; /* see below */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpiphp_attention_info - device specific attention registration
|
||||
*
|
||||
* ACPI has no generic method of setting/getting attention status
|
||||
* this allows for device specific driver registration
|
||||
*/
|
||||
struct acpiphp_attention_info
|
||||
{
|
||||
int (*set_attn)(struct hotplug_slot *slot, u8 status);
|
||||
int (*get_attn)(struct hotplug_slot *slot, u8 *status);
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
struct acpiphp_ioapic {
|
||||
struct pci_dev *dev;
|
||||
u32 gsi_base;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* PCI bus bridge HID */
|
||||
#define ACPI_PCI_HOST_HID "PNP0A03"
|
||||
|
||||
/* PCI BRIDGE type */
|
||||
#define BRIDGE_TYPE_HOST 0
|
||||
#define BRIDGE_TYPE_P2P 1
|
||||
|
||||
/* ACPI _STA method value (ignore bit 4; battery present) */
|
||||
#define ACPI_STA_PRESENT (0x00000001)
|
||||
#define ACPI_STA_ENABLED (0x00000002)
|
||||
#define ACPI_STA_SHOW_IN_UI (0x00000004)
|
||||
#define ACPI_STA_FUNCTIONING (0x00000008)
|
||||
#define ACPI_STA_ALL (0x0000000f)
|
||||
|
||||
/* bridge flags */
|
||||
#define BRIDGE_HAS_STA (0x00000001)
|
||||
#define BRIDGE_HAS_EJ0 (0x00000002)
|
||||
#define BRIDGE_HAS_HPP (0x00000004)
|
||||
#define BRIDGE_HAS_PS0 (0x00000010)
|
||||
#define BRIDGE_HAS_PS1 (0x00000020)
|
||||
#define BRIDGE_HAS_PS2 (0x00000040)
|
||||
#define BRIDGE_HAS_PS3 (0x00000080)
|
||||
|
||||
/* slot flags */
|
||||
|
||||
#define SLOT_POWEREDON (0x00000001)
|
||||
#define SLOT_ENABLED (0x00000002)
|
||||
#define SLOT_MULTIFUNCTION (0x00000004)
|
||||
|
||||
/* function flags */
|
||||
|
||||
#define FUNC_HAS_STA (0x00000001)
|
||||
#define FUNC_HAS_EJ0 (0x00000002)
|
||||
#define FUNC_HAS_PS0 (0x00000010)
|
||||
#define FUNC_HAS_PS1 (0x00000020)
|
||||
#define FUNC_HAS_PS2 (0x00000040)
|
||||
#define FUNC_HAS_PS3 (0x00000080)
|
||||
#define FUNC_HAS_DCK (0x00000100)
|
||||
|
||||
/* function prototypes */
|
||||
|
||||
/* acpiphp_core.c */
|
||||
extern int acpiphp_register_attention(struct acpiphp_attention_info*info);
|
||||
extern int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
|
||||
extern int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot);
|
||||
extern void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
|
||||
|
||||
/* acpiphp_glue.c */
|
||||
extern int acpiphp_glue_init (void);
|
||||
extern void acpiphp_glue_exit (void);
|
||||
extern int acpiphp_get_num_slots (void);
|
||||
typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
|
||||
|
||||
extern int acpiphp_enable_slot (struct acpiphp_slot *slot);
|
||||
extern int acpiphp_disable_slot (struct acpiphp_slot *slot);
|
||||
extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot);
|
||||
extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot);
|
||||
extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot);
|
||||
extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
|
||||
extern u32 acpiphp_get_address (struct acpiphp_slot *slot);
|
||||
|
||||
/* variables */
|
||||
extern int acpiphp_debug;
|
||||
|
||||
#endif /* _ACPIPHP_H */
|
||||
411
drivers/pci/hotplug/acpiphp_core.c
Normal file
411
drivers/pci/hotplug/acpiphp_core.c
Normal file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* ACPI PCI Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
|
||||
* Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
|
||||
* Copyright (C) 2002,2003 NEC Corporation
|
||||
* Copyright (C) 2003-2005 Matthew Wilcox (matthew.wilcox@hp.com)
|
||||
* Copyright (C) 2003-2005 Hewlett Packard
|
||||
*
|
||||
* 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 <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include "acpiphp.h"
|
||||
|
||||
#define MY_NAME "acpiphp"
|
||||
|
||||
static int debug;
|
||||
int acpiphp_debug;
|
||||
|
||||
/* local variables */
|
||||
static int num_slots;
|
||||
static struct acpiphp_attention_info *attention_info;
|
||||
|
||||
#define DRIVER_VERSION "0.5"
|
||||
#define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@hp.com>"
|
||||
#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
||||
module_param(debug, bool, 0644);
|
||||
|
||||
/* export the attention callback registration methods */
|
||||
EXPORT_SYMBOL_GPL(acpiphp_register_attention);
|
||||
EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
|
||||
|
||||
static int enable_slot (struct hotplug_slot *slot);
|
||||
static int disable_slot (struct hotplug_slot *slot);
|
||||
static int set_attention_status (struct hotplug_slot *slot, u8 value);
|
||||
static int get_power_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_address (struct hotplug_slot *slot, u32 *value);
|
||||
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
|
||||
|
||||
static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.set_attention_status = set_attention_status,
|
||||
.get_power_status = get_power_status,
|
||||
.get_attention_status = get_attention_status,
|
||||
.get_latch_status = get_latch_status,
|
||||
.get_adapter_status = get_adapter_status,
|
||||
.get_address = get_address,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* acpiphp_register_attention - set attention LED callback
|
||||
* @info: must be completely filled with LED callbacks
|
||||
*
|
||||
* Description: this is used to register a hardware specific ACPI
|
||||
* driver that manipulates the attention LED. All the fields in
|
||||
* info must be set.
|
||||
**/
|
||||
int acpiphp_register_attention(struct acpiphp_attention_info *info)
|
||||
{
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (info && info->owner && info->set_attn &&
|
||||
info->get_attn && !attention_info) {
|
||||
retval = 0;
|
||||
attention_info = info;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* acpiphp_unregister_attention - unset attention LED callback
|
||||
* @info: must match the pointer used to register
|
||||
*
|
||||
* Description: this is used to un-register a hardware specific acpi
|
||||
* driver that manipulates the attention LED. The pointer to the
|
||||
* info struct must be the same as the one used to set it.
|
||||
**/
|
||||
int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
|
||||
{
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (info && attention_info == info) {
|
||||
attention_info = NULL;
|
||||
retval = 0;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* enable_slot - power on and enable a slot
|
||||
* @hotplug_slot: slot to enable
|
||||
*
|
||||
* Actual tasks are done in acpiphp_enable_slot()
|
||||
*
|
||||
*/
|
||||
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
/* enable the specified slot */
|
||||
return acpiphp_enable_slot(slot->acpi_slot);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* disable_slot - disable and power off a slot
|
||||
* @hotplug_slot: slot to disable
|
||||
*
|
||||
* Actual tasks are done in acpiphp_disable_slot()
|
||||
*
|
||||
*/
|
||||
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
/* disable the specified slot */
|
||||
return acpiphp_disable_slot(slot->acpi_slot);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set_attention_status - set attention LED
|
||||
* @hotplug_slot: slot to set attention LED on
|
||||
* @status: value to set attention LED to (0 or 1)
|
||||
*
|
||||
* attention status LED, so we use a callback that
|
||||
* was registered with us. This allows hardware specific
|
||||
* ACPI implementations to blink the light for us.
|
||||
**/
|
||||
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||
{
|
||||
int retval = -ENODEV;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
if (attention_info && try_module_get(attention_info->owner)) {
|
||||
retval = attention_info->set_attn(hotplug_slot, status);
|
||||
module_put(attention_info->owner);
|
||||
} else
|
||||
attention_info = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get_power_status - get power status of a slot
|
||||
* @hotplug_slot: slot to get status
|
||||
* @value: pointer to store status
|
||||
*
|
||||
* Some platforms may not implement _STA method properly.
|
||||
* In that case, the value returned may not be reliable.
|
||||
*
|
||||
*/
|
||||
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
*value = acpiphp_get_power_status(slot->acpi_slot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get_attention_status - get attention LED status
|
||||
* @hotplug_slot: slot to get status from
|
||||
* @value: returns with value of attention LED
|
||||
*
|
||||
* ACPI doesn't have known method to determine the state
|
||||
* of the attention status LED, so we use a callback that
|
||||
* was registered with us. This allows hardware specific
|
||||
* ACPI implementations to determine its state
|
||||
**/
|
||||
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
int retval = -EINVAL;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
if (attention_info && try_module_get(attention_info->owner)) {
|
||||
retval = attention_info->get_attn(hotplug_slot, value);
|
||||
module_put(attention_info->owner);
|
||||
} else
|
||||
attention_info = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get_latch_status - get latch status of a slot
|
||||
* @hotplug_slot: slot to get status
|
||||
* @value: pointer to store status
|
||||
*
|
||||
* ACPI doesn't provide any formal means to access latch status.
|
||||
* Instead, we fake latch status from _STA
|
||||
*
|
||||
*/
|
||||
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
*value = acpiphp_get_latch_status(slot->acpi_slot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get_adapter_status - get adapter status of a slot
|
||||
* @hotplug_slot: slot to get status
|
||||
* @value: pointer to store status
|
||||
*
|
||||
* ACPI doesn't provide any formal means to access adapter status.
|
||||
* Instead, we fake adapter status from _STA
|
||||
*
|
||||
*/
|
||||
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
*value = acpiphp_get_adapter_status(slot->acpi_slot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get_address - get pci address of a slot
|
||||
* @hotplug_slot: slot to get status
|
||||
* @value: pointer to struct pci_busdev (seg, bus, dev)
|
||||
*/
|
||||
static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
*value = acpiphp_get_address(slot->acpi_slot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init init_acpi(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/* initialize internal data structure etc. */
|
||||
retval = acpiphp_glue_init();
|
||||
|
||||
/* read initial number of slots */
|
||||
if (!retval) {
|
||||
num_slots = acpiphp_get_num_slots();
|
||||
if (num_slots == 0) {
|
||||
acpiphp_glue_exit();
|
||||
retval = -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* release_slot - free up the memory used by a slot
|
||||
* @hotplug_slot: slot to free
|
||||
*/
|
||||
static void release_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
kfree(slot->hotplug_slot);
|
||||
kfree(slot);
|
||||
}
|
||||
|
||||
/* callback routine to initialize 'struct slot' for each slot */
|
||||
int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
|
||||
{
|
||||
struct slot *slot;
|
||||
int retval = -ENOMEM;
|
||||
|
||||
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
||||
if (!slot)
|
||||
goto error;
|
||||
|
||||
slot->hotplug_slot = kzalloc(sizeof(*slot->hotplug_slot), GFP_KERNEL);
|
||||
if (!slot->hotplug_slot)
|
||||
goto error_slot;
|
||||
|
||||
slot->hotplug_slot->info = &slot->info;
|
||||
|
||||
slot->hotplug_slot->name = slot->name;
|
||||
|
||||
slot->hotplug_slot->private = slot;
|
||||
slot->hotplug_slot->release = &release_slot;
|
||||
slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
|
||||
|
||||
slot->acpi_slot = acpiphp_slot;
|
||||
slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
|
||||
slot->hotplug_slot->info->attention_status = 0;
|
||||
slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
|
||||
slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
|
||||
slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
|
||||
slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
|
||||
|
||||
acpiphp_slot->slot = slot;
|
||||
snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun);
|
||||
|
||||
retval = pci_hp_register(slot->hotplug_slot);
|
||||
if (retval) {
|
||||
err("pci_hp_register failed with error %d\n", retval);
|
||||
goto error_hpslot;
|
||||
}
|
||||
|
||||
info("Slot [%s] registered\n", slot->hotplug_slot->name);
|
||||
|
||||
return 0;
|
||||
error_hpslot:
|
||||
kfree(slot->hotplug_slot);
|
||||
error_slot:
|
||||
kfree(slot);
|
||||
error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
|
||||
{
|
||||
struct slot *slot = acpiphp_slot->slot;
|
||||
int retval = 0;
|
||||
|
||||
info ("Slot [%s] unregistered\n", slot->hotplug_slot->name);
|
||||
|
||||
retval = pci_hp_deregister(slot->hotplug_slot);
|
||||
if (retval)
|
||||
err("pci_hp_deregister failed with error %d\n", retval);
|
||||
}
|
||||
|
||||
|
||||
static int __init acpiphp_init(void)
|
||||
{
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
|
||||
acpiphp_debug = debug;
|
||||
|
||||
/* read all the ACPI info from the system */
|
||||
return init_acpi();
|
||||
}
|
||||
|
||||
|
||||
static void __exit acpiphp_exit(void)
|
||||
{
|
||||
/* deallocate internal data structures etc. */
|
||||
acpiphp_glue_exit();
|
||||
}
|
||||
|
||||
module_init(acpiphp_init);
|
||||
module_exit(acpiphp_exit);
|
||||
1846
drivers/pci/hotplug/acpiphp_glue.c
Normal file
1846
drivers/pci/hotplug/acpiphp_glue.c
Normal file
File diff suppressed because it is too large
Load Diff
492
drivers/pci/hotplug/acpiphp_ibm.c
Normal file
492
drivers/pci/hotplug/acpiphp_ibm.c
Normal file
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* ACPI PCI Hot Plug IBM Extension
|
||||
*
|
||||
* Copyright (C) 2004 Vernon Mauery <vernux@us.ibm.com>
|
||||
* Copyright (C) 2004 IBM Corp.
|
||||
*
|
||||
* 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 <vernux@us.ibm.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include "acpiphp.h"
|
||||
|
||||
#define DRIVER_VERSION "1.0.1"
|
||||
#define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
|
||||
#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension"
|
||||
|
||||
static int debug;
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
module_param(debug, bool, 0644);
|
||||
MODULE_PARM_DESC(debug, " Debugging mode enabled or not");
|
||||
#define MY_NAME "acpiphp_ibm"
|
||||
|
||||
#undef dbg
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (debug) \
|
||||
printk(KERN_DEBUG "%s: " format, \
|
||||
MY_NAME , ## arg); \
|
||||
} while (0)
|
||||
|
||||
#define FOUND_APCI 0x61504349
|
||||
/* these are the names for the IBM ACPI pseudo-device */
|
||||
#define IBM_HARDWARE_ID1 "IBM37D0"
|
||||
#define IBM_HARDWARE_ID2 "IBM37D4"
|
||||
|
||||
#define hpslot_to_sun(A) (((struct slot *)((A)->private))->acpi_slot->sun)
|
||||
|
||||
/* union apci_descriptor - allows access to the
|
||||
* various device descriptors that are embedded in the
|
||||
* aPCI table
|
||||
*/
|
||||
union apci_descriptor {
|
||||
struct {
|
||||
char sig[4];
|
||||
u8 len;
|
||||
} header;
|
||||
struct {
|
||||
u8 type;
|
||||
u8 len;
|
||||
u16 slot_id;
|
||||
u8 bus_id;
|
||||
u8 dev_num;
|
||||
u8 slot_num;
|
||||
u8 slot_attr[2];
|
||||
u8 attn;
|
||||
u8 status[2];
|
||||
u8 sun;
|
||||
u8 res[3];
|
||||
} slot;
|
||||
struct {
|
||||
u8 type;
|
||||
u8 len;
|
||||
} generic;
|
||||
};
|
||||
|
||||
/* struct notification - keeps info about the device
|
||||
* that cause the ACPI notification event
|
||||
*/
|
||||
struct notification {
|
||||
struct acpi_device *device;
|
||||
u8 event;
|
||||
};
|
||||
|
||||
static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status);
|
||||
static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status);
|
||||
static void ibm_handle_events(acpi_handle handle, u32 event, void *context);
|
||||
static int ibm_get_table_from_acpi(char **bufp);
|
||||
static ssize_t ibm_read_apci_table(struct kobject *kobj,
|
||||
char *buffer, loff_t pos, size_t size);
|
||||
static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
|
||||
u32 lvl, void *context, void **rv);
|
||||
static int __init ibm_acpiphp_init(void);
|
||||
static void __exit ibm_acpiphp_exit(void);
|
||||
|
||||
static acpi_handle ibm_acpi_handle;
|
||||
static struct notification ibm_note;
|
||||
static struct bin_attribute ibm_apci_table_attr = {
|
||||
.attr = {
|
||||
.name = "apci_table",
|
||||
.owner = THIS_MODULE,
|
||||
.mode = S_IRUGO,
|
||||
},
|
||||
.read = ibm_read_apci_table,
|
||||
.write = NULL,
|
||||
};
|
||||
static struct acpiphp_attention_info ibm_attention_info =
|
||||
{
|
||||
.set_attn = ibm_set_attention_status,
|
||||
.get_attn = ibm_get_attention_status,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/**
|
||||
* ibm_slot_from_id - workaround for bad ibm hardware
|
||||
* @id: the slot number that linux refers to the slot by
|
||||
*
|
||||
* Description: this method returns the aCPI slot descriptor
|
||||
* corresponding to the Linux slot number. This descriptor
|
||||
* has info about the aPCI slot id and attention status.
|
||||
* This descriptor must be freed using kfree when done.
|
||||
**/
|
||||
static union apci_descriptor *ibm_slot_from_id(int id)
|
||||
{
|
||||
int ind = 0, size;
|
||||
union apci_descriptor *ret = NULL, *des;
|
||||
char *table;
|
||||
|
||||
size = ibm_get_table_from_acpi(&table);
|
||||
des = (union apci_descriptor *)table;
|
||||
if (memcmp(des->header.sig, "aPCI", 4) != 0)
|
||||
goto ibm_slot_done;
|
||||
|
||||
des = (union apci_descriptor *)&table[ind += des->header.len];
|
||||
while (ind < size && (des->generic.type != 0x82 ||
|
||||
des->slot.slot_num != id)) {
|
||||
des = (union apci_descriptor *)&table[ind += des->generic.len];
|
||||
}
|
||||
|
||||
if (ind < size && des->slot.slot_num == id)
|
||||
ret = des;
|
||||
|
||||
ibm_slot_done:
|
||||
if (ret) {
|
||||
ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
|
||||
memcpy(ret, des, sizeof(union apci_descriptor));
|
||||
}
|
||||
kfree(table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibm_set_attention_status - callback method to set the attention LED
|
||||
* @slot: the hotplug_slot to work with
|
||||
* @status: what to set the LED to (0 or 1)
|
||||
*
|
||||
* Description: this method is registered with the acpiphp module as a
|
||||
* callback to do the device specific task of setting the LED status
|
||||
**/
|
||||
static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
|
||||
{
|
||||
union acpi_object args[2];
|
||||
struct acpi_object_list params = { .pointer = args, .count = 2 };
|
||||
acpi_status stat;
|
||||
unsigned long rc;
|
||||
union apci_descriptor *ibm_slot;
|
||||
|
||||
ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
|
||||
|
||||
dbg("%s: set slot %d (%d) attention status to %d\n", __FUNCTION__,
|
||||
ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
|
||||
(status ? 1 : 0));
|
||||
|
||||
args[0].type = ACPI_TYPE_INTEGER;
|
||||
args[0].integer.value = ibm_slot->slot.slot_id;
|
||||
args[1].type = ACPI_TYPE_INTEGER;
|
||||
args[1].integer.value = (status) ? 1 : 0;
|
||||
|
||||
kfree(ibm_slot);
|
||||
|
||||
stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", ¶ms, &rc);
|
||||
if (ACPI_FAILURE(stat)) {
|
||||
err("APLS evaluation failed: 0x%08x\n", stat);
|
||||
return -ENODEV;
|
||||
} else if (!rc) {
|
||||
err("APLS method failed: 0x%08lx\n", rc);
|
||||
return -ERANGE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibm_get_attention_status - callback method to get attention LED status
|
||||
* @slot: the hotplug_slot to work with
|
||||
* @status: returns what the LED is set to (0 or 1)
|
||||
*
|
||||
* Description: this method is registered with the acpiphp module as a
|
||||
* callback to do the device specific task of getting the LED status
|
||||
*
|
||||
* Because there is no direct method of getting the LED status directly
|
||||
* from an ACPI call, we read the aPCI table and parse out our
|
||||
* slot descriptor to read the status from that.
|
||||
**/
|
||||
static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
|
||||
{
|
||||
union apci_descriptor *ibm_slot;
|
||||
|
||||
ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
|
||||
|
||||
if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
|
||||
*status = 1;
|
||||
else
|
||||
*status = 0;
|
||||
|
||||
dbg("%s: get slot %d (%d) attention status is %d\n", __FUNCTION__,
|
||||
ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
|
||||
*status);
|
||||
|
||||
kfree(ibm_slot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibm_handle_events - listens for ACPI events for the IBM37D0 device
|
||||
* @handle: an ACPI handle to the device that caused the event
|
||||
* @event: the event info (device specific)
|
||||
* @context: passed context (our notification struct)
|
||||
*
|
||||
* Description: this method is registered as a callback with the ACPI
|
||||
* subsystem it is called when this device has an event to notify the OS of
|
||||
*
|
||||
* The events actually come from the device as two events that get
|
||||
* synthesized into one event with data by this function. The event
|
||||
* ID comes first and then the slot number that caused it. We report
|
||||
* this as one event to the OS.
|
||||
*
|
||||
* From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will
|
||||
* only re-enable the interrupt that causes this event AFTER this method
|
||||
* has returned, thereby enforcing serial access for the notification struct.
|
||||
**/
|
||||
static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
|
||||
{
|
||||
u8 detail = event & 0x0f;
|
||||
u8 subevent = event & 0xf0;
|
||||
struct notification *note = context;
|
||||
|
||||
dbg("%s: Received notification %02x\n", __FUNCTION__, event);
|
||||
|
||||
if (subevent == 0x80) {
|
||||
dbg("%s: generationg bus event\n", __FUNCTION__);
|
||||
acpi_bus_generate_event(note->device, note->event, detail);
|
||||
} else
|
||||
note->event = event;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibm_get_table_from_acpi - reads the APLS buffer from ACPI
|
||||
* @bufp: address to pointer to allocate for the table
|
||||
*
|
||||
* Description: this method reads the APLS buffer in from ACPI and
|
||||
* stores the "stripped" table into a single buffer
|
||||
* it allocates and passes the address back in bufp
|
||||
*
|
||||
* If NULL is passed in as buffer, this method only calculates
|
||||
* the size of the table and returns that without filling
|
||||
* in the buffer
|
||||
*
|
||||
* returns < 0 on error or the size of the table on success
|
||||
**/
|
||||
static int ibm_get_table_from_acpi(char **bufp)
|
||||
{
|
||||
union acpi_object *package;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
acpi_status status;
|
||||
char *lbuf = NULL;
|
||||
int i, size = -EIO;
|
||||
|
||||
status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
err("%s: APCI evaluation failed\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
package = (union acpi_object *) buffer.pointer;
|
||||
if (!(package) ||
|
||||
(package->type != ACPI_TYPE_PACKAGE) ||
|
||||
!(package->package.elements)) {
|
||||
err("%s: Invalid APCI object\n", __FUNCTION__);
|
||||
goto read_table_done;
|
||||
}
|
||||
|
||||
for(size = 0, i = 0; i < package->package.count; i++) {
|
||||
if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
|
||||
err("%s: Invalid APCI element %d\n", __FUNCTION__, i);
|
||||
goto read_table_done;
|
||||
}
|
||||
size += package->package.elements[i].buffer.length;
|
||||
}
|
||||
|
||||
if (bufp == NULL)
|
||||
goto read_table_done;
|
||||
|
||||
lbuf = kzalloc(size, GFP_KERNEL);
|
||||
dbg("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
|
||||
__FUNCTION__, package->package.count, size, lbuf);
|
||||
|
||||
if (lbuf) {
|
||||
*bufp = lbuf;
|
||||
} else {
|
||||
size = -ENOMEM;
|
||||
goto read_table_done;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
for (i=0; i<package->package.count; i++) {
|
||||
memcpy(&lbuf[size],
|
||||
package->package.elements[i].buffer.pointer,
|
||||
package->package.elements[i].buffer.length);
|
||||
size += package->package.elements[i].buffer.length;
|
||||
}
|
||||
|
||||
read_table_done:
|
||||
kfree(buffer.pointer);
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibm_read_apci_table - callback for the sysfs apci_table file
|
||||
* @kobj: the kobject this binary attribute is a part of
|
||||
* @buffer: the kernel space buffer to fill
|
||||
* @pos: the offset into the file
|
||||
* @size: the number of bytes requested
|
||||
*
|
||||
* Description: gets registered with sysfs as the reader callback
|
||||
* to be executed when /sys/bus/pci/slots/apci_table gets read
|
||||
*
|
||||
* Since we don't get notified on open and close for this file,
|
||||
* things get really tricky here...
|
||||
* our solution is to only allow reading the table in all at once
|
||||
**/
|
||||
static ssize_t ibm_read_apci_table(struct kobject *kobj,
|
||||
char *buffer, loff_t pos, size_t size)
|
||||
{
|
||||
int bytes_read = -EINVAL;
|
||||
char *table = NULL;
|
||||
|
||||
dbg("%s: pos = %d, size = %zd\n", __FUNCTION__, (int)pos, size);
|
||||
|
||||
if (pos == 0) {
|
||||
bytes_read = ibm_get_table_from_acpi(&table);
|
||||
if (bytes_read > 0 && bytes_read <= size)
|
||||
memcpy(buffer, table, bytes_read);
|
||||
kfree(table);
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibm_find_acpi_device - callback to find our ACPI device
|
||||
* @handle: the ACPI handle of the device we are inspecting
|
||||
* @lvl: depth into the namespace tree
|
||||
* @context: a pointer to our handle to fill when we find the device
|
||||
* @rv: a return value to fill if desired
|
||||
*
|
||||
* Description: used as a callback when calling acpi_walk_namespace
|
||||
* to find our device. When this method returns non-zero
|
||||
* acpi_walk_namespace quits its search and returns our value
|
||||
**/
|
||||
static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
|
||||
u32 lvl, void *context, void **rv)
|
||||
{
|
||||
acpi_handle *phandle = (acpi_handle *)context;
|
||||
acpi_status status;
|
||||
struct acpi_device_info info;
|
||||
struct acpi_buffer info_buffer = {
|
||||
.length = sizeof(struct acpi_device_info),
|
||||
.pointer = &info,
|
||||
};
|
||||
|
||||
status = acpi_get_object_info(handle, &info_buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
err("%s: Failed to get device information", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
info.hardware_id.value[sizeof(info.hardware_id.value) - 1] = '\0';
|
||||
|
||||
if (info.current_status && (info.valid & ACPI_VALID_HID) &&
|
||||
(!strcmp(info.hardware_id.value, IBM_HARDWARE_ID1) ||
|
||||
!strcmp(info.hardware_id.value, IBM_HARDWARE_ID2))) {
|
||||
dbg("found hardware: %s, handle: %p\n", info.hardware_id.value,
|
||||
handle);
|
||||
*phandle = handle;
|
||||
/* returning non-zero causes the search to stop
|
||||
* and returns this value to the caller of
|
||||
* acpi_walk_namespace, but it also causes some warnings
|
||||
* in the acpi debug code to print...
|
||||
*/
|
||||
return FOUND_APCI;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init ibm_acpiphp_init(void)
|
||||
{
|
||||
int retval = 0;
|
||||
acpi_status status;
|
||||
struct acpi_device *device;
|
||||
struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj;
|
||||
|
||||
dbg("%s\n", __FUNCTION__);
|
||||
|
||||
if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ibm_find_acpi_device,
|
||||
&ibm_acpi_handle, NULL) != FOUND_APCI) {
|
||||
err("%s: acpi_walk_namespace failed\n", __FUNCTION__);
|
||||
retval = -ENODEV;
|
||||
goto init_return;
|
||||
}
|
||||
dbg("%s: found IBM aPCI device\n", __FUNCTION__);
|
||||
if (acpi_bus_get_device(ibm_acpi_handle, &device)) {
|
||||
err("%s: acpi_bus_get_device failed\n", __FUNCTION__);
|
||||
retval = -ENODEV;
|
||||
goto init_return;
|
||||
}
|
||||
if (acpiphp_register_attention(&ibm_attention_info)) {
|
||||
retval = -ENODEV;
|
||||
goto init_return;
|
||||
}
|
||||
|
||||
ibm_note.device = device;
|
||||
status = acpi_install_notify_handler(ibm_acpi_handle,
|
||||
ACPI_DEVICE_NOTIFY, ibm_handle_events,
|
||||
&ibm_note);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
err("%s: Failed to register notification handler\n",
|
||||
__FUNCTION__);
|
||||
retval = -EBUSY;
|
||||
goto init_cleanup;
|
||||
}
|
||||
|
||||
ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL);
|
||||
retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr);
|
||||
|
||||
return retval;
|
||||
|
||||
init_cleanup:
|
||||
acpiphp_unregister_attention(&ibm_attention_info);
|
||||
init_return:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit ibm_acpiphp_exit(void)
|
||||
{
|
||||
acpi_status status;
|
||||
struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj;
|
||||
|
||||
dbg("%s\n", __FUNCTION__);
|
||||
|
||||
if (acpiphp_unregister_attention(&ibm_attention_info))
|
||||
err("%s: attention info deregistration failed", __FUNCTION__);
|
||||
|
||||
status = acpi_remove_notify_handler(
|
||||
ibm_acpi_handle,
|
||||
ACPI_DEVICE_NOTIFY,
|
||||
ibm_handle_events);
|
||||
if (ACPI_FAILURE(status))
|
||||
err("%s: Notification handler removal failed\n", __FUNCTION__);
|
||||
/* remove the /sys entries */
|
||||
sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
|
||||
}
|
||||
|
||||
module_init(ibm_acpiphp_init);
|
||||
module_exit(ibm_acpiphp_exit);
|
||||
96
drivers/pci/hotplug/cpci_hotplug.h
Normal file
96
drivers/pci/hotplug/cpci_hotplug.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* CompactPCI Hot Plug Core Functions
|
||||
*
|
||||
* Copyright (C) 2002 SOMA Networks, Inc.
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
*
|
||||
* 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 <scottm@somanetworks.com>
|
||||
*/
|
||||
|
||||
#ifndef _CPCI_HOTPLUG_H
|
||||
#define _CPCI_HOTPLUG_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
/* PICMG 2.1 R2.0 HS CSR bits: */
|
||||
#define HS_CSR_INS 0x0080
|
||||
#define HS_CSR_EXT 0x0040
|
||||
#define HS_CSR_PI 0x0030
|
||||
#define HS_CSR_LOO 0x0008
|
||||
#define HS_CSR_PIE 0x0004
|
||||
#define HS_CSR_EIM 0x0002
|
||||
#define HS_CSR_DHA 0x0001
|
||||
|
||||
struct slot {
|
||||
u8 number;
|
||||
unsigned int devfn;
|
||||
struct pci_bus *bus;
|
||||
struct pci_dev *dev;
|
||||
unsigned int extracting;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct list_head slot_list;
|
||||
};
|
||||
|
||||
struct cpci_hp_controller_ops {
|
||||
int (*query_enum) (void);
|
||||
int (*enable_irq) (void);
|
||||
int (*disable_irq) (void);
|
||||
int (*check_irq) (void *dev_id);
|
||||
int (*hardware_test) (struct slot* slot, u32 value);
|
||||
u8 (*get_power) (struct slot* slot);
|
||||
int (*set_power) (struct slot* slot, int value);
|
||||
};
|
||||
|
||||
struct cpci_hp_controller {
|
||||
unsigned int irq;
|
||||
unsigned long irq_flags;
|
||||
char *devname;
|
||||
void *dev_id;
|
||||
char *name;
|
||||
struct cpci_hp_controller_ops *ops;
|
||||
};
|
||||
|
||||
extern int cpci_hp_register_controller(struct cpci_hp_controller *controller);
|
||||
extern int cpci_hp_unregister_controller(struct cpci_hp_controller *controller);
|
||||
extern int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last);
|
||||
extern int cpci_hp_unregister_bus(struct pci_bus *bus);
|
||||
extern int cpci_hp_start(void);
|
||||
extern int cpci_hp_stop(void);
|
||||
|
||||
/*
|
||||
* Internal function prototypes, these functions should not be used by
|
||||
* board/chassis drivers.
|
||||
*/
|
||||
extern u8 cpci_get_attention_status(struct slot *slot);
|
||||
extern u8 cpci_get_latch_status(struct slot *slot);
|
||||
extern u8 cpci_get_adapter_status(struct slot *slot);
|
||||
extern u16 cpci_get_hs_csr(struct slot * slot);
|
||||
extern int cpci_set_attention_status(struct slot *slot, int status);
|
||||
extern int cpci_check_and_clear_ins(struct slot * slot);
|
||||
extern int cpci_check_ext(struct slot * slot);
|
||||
extern int cpci_clear_ext(struct slot * slot);
|
||||
extern int cpci_led_on(struct slot * slot);
|
||||
extern int cpci_led_off(struct slot * slot);
|
||||
extern int cpci_configure_slot(struct slot *slot);
|
||||
extern int cpci_unconfigure_slot(struct slot *slot);
|
||||
|
||||
#endif /* _CPCI_HOTPLUG_H */
|
||||
767
drivers/pci/hotplug/cpci_hotplug_core.c
Normal file
767
drivers/pci/hotplug/cpci_hotplug_core.c
Normal file
@@ -0,0 +1,767 @@
|
||||
/*
|
||||
* CompactPCI Hot Plug Driver
|
||||
*
|
||||
* Copyright (C) 2002,2005 SOMA Networks, Inc.
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
*
|
||||
* 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 <scottm@somanetworks.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <linux/delay.h>
|
||||
#include "cpci_hotplug.h"
|
||||
|
||||
#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
|
||||
#define DRIVER_DESC "CompactPCI Hot Plug Core"
|
||||
|
||||
#define MY_NAME "cpci_hotplug"
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (cpci_debug) \
|
||||
printk (KERN_DEBUG "%s: " format "\n", \
|
||||
MY_NAME , ## arg); \
|
||||
} while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
|
||||
|
||||
/* local variables */
|
||||
static DECLARE_RWSEM(list_rwsem);
|
||||
static LIST_HEAD(slot_list);
|
||||
static int slots;
|
||||
static atomic_t extracting;
|
||||
int cpci_debug;
|
||||
static struct cpci_hp_controller *controller;
|
||||
static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
|
||||
static struct semaphore thread_exit; /* guard ensure thread has exited before calling it quits */
|
||||
static int thread_finished = 1;
|
||||
|
||||
static int enable_slot(struct hotplug_slot *slot);
|
||||
static int disable_slot(struct hotplug_slot *slot);
|
||||
static int set_attention_status(struct hotplug_slot *slot, u8 value);
|
||||
static int get_power_status(struct hotplug_slot *slot, u8 * value);
|
||||
static int get_attention_status(struct hotplug_slot *slot, u8 * value);
|
||||
static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
|
||||
static int get_latch_status(struct hotplug_slot *slot, u8 * value);
|
||||
|
||||
static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.set_attention_status = set_attention_status,
|
||||
.get_power_status = get_power_status,
|
||||
.get_attention_status = get_attention_status,
|
||||
.get_adapter_status = get_adapter_status,
|
||||
.get_latch_status = get_latch_status,
|
||||
};
|
||||
|
||||
static int
|
||||
update_latch_status(struct hotplug_slot *hotplug_slot, u8 value)
|
||||
{
|
||||
struct hotplug_slot_info info;
|
||||
|
||||
memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
|
||||
info.latch_status = value;
|
||||
return pci_hp_change_slot_info(hotplug_slot, &info);
|
||||
}
|
||||
|
||||
static int
|
||||
update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value)
|
||||
{
|
||||
struct hotplug_slot_info info;
|
||||
|
||||
memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
|
||||
info.adapter_status = value;
|
||||
return pci_hp_change_slot_info(hotplug_slot, &info);
|
||||
}
|
||||
|
||||
static int
|
||||
enable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
if (controller->ops->set_power)
|
||||
retval = controller->ops->set_power(slot, 1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
disable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
down_write(&list_rwsem);
|
||||
|
||||
/* Unconfigure device */
|
||||
dbg("%s - unconfiguring slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
if ((retval = cpci_unconfigure_slot(slot))) {
|
||||
err("%s - could not unconfigure slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
goto disable_error;
|
||||
}
|
||||
dbg("%s - finished unconfiguring slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
|
||||
/* Clear EXT (by setting it) */
|
||||
if (cpci_clear_ext(slot)) {
|
||||
err("%s - could not clear EXT for slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
retval = -ENODEV;
|
||||
goto disable_error;
|
||||
}
|
||||
cpci_led_on(slot);
|
||||
|
||||
if (controller->ops->set_power)
|
||||
if ((retval = controller->ops->set_power(slot, 0)))
|
||||
goto disable_error;
|
||||
|
||||
if (update_adapter_status(slot->hotplug_slot, 0))
|
||||
warn("failure to update adapter file");
|
||||
|
||||
if (slot->extracting) {
|
||||
slot->extracting = 0;
|
||||
atomic_dec(&extracting);
|
||||
}
|
||||
disable_error:
|
||||
up_write(&list_rwsem);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static u8
|
||||
cpci_get_power_status(struct slot *slot)
|
||||
{
|
||||
u8 power = 1;
|
||||
|
||||
if (controller->ops->get_power)
|
||||
power = controller->ops->get_power(slot);
|
||||
return power;
|
||||
}
|
||||
|
||||
static int
|
||||
get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
*value = cpci_get_power_status(slot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
*value = cpci_get_attention_status(slot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||
{
|
||||
return cpci_set_attention_status(hotplug_slot->private, status);
|
||||
}
|
||||
|
||||
static int
|
||||
get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
|
||||
{
|
||||
*value = hotplug_slot->info->adapter_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
|
||||
{
|
||||
*value = hotplug_slot->info->latch_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
kfree(slot->hotplug_slot->info);
|
||||
kfree(slot->hotplug_slot->name);
|
||||
kfree(slot->hotplug_slot);
|
||||
if (slot->dev)
|
||||
pci_dev_put(slot->dev);
|
||||
kfree(slot);
|
||||
}
|
||||
|
||||
#define SLOT_NAME_SIZE 6
|
||||
static void
|
||||
make_slot_name(struct slot *slot)
|
||||
{
|
||||
snprintf(slot->hotplug_slot->name,
|
||||
SLOT_NAME_SIZE, "%02x:%02x", slot->bus->number, slot->number);
|
||||
}
|
||||
|
||||
int
|
||||
cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
|
||||
{
|
||||
struct slot *slot;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct hotplug_slot_info *info;
|
||||
char *name;
|
||||
int status = -ENOMEM;
|
||||
int i;
|
||||
|
||||
if (!(controller && bus))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Create a structure for each slot, and register that slot
|
||||
* with the pci_hotplug subsystem.
|
||||
*/
|
||||
for (i = first; i <= last; ++i) {
|
||||
slot = kzalloc(sizeof (struct slot), GFP_KERNEL);
|
||||
if (!slot)
|
||||
goto error;
|
||||
|
||||
hotplug_slot =
|
||||
kzalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
|
||||
if (!hotplug_slot)
|
||||
goto error_slot;
|
||||
slot->hotplug_slot = hotplug_slot;
|
||||
|
||||
info = kzalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
goto error_hpslot;
|
||||
hotplug_slot->info = info;
|
||||
|
||||
name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
|
||||
if (!name)
|
||||
goto error_info;
|
||||
hotplug_slot->name = name;
|
||||
|
||||
slot->bus = bus;
|
||||
slot->number = i;
|
||||
slot->devfn = PCI_DEVFN(i, 0);
|
||||
|
||||
hotplug_slot->private = slot;
|
||||
hotplug_slot->release = &release_slot;
|
||||
make_slot_name(slot);
|
||||
hotplug_slot->ops = &cpci_hotplug_slot_ops;
|
||||
|
||||
/*
|
||||
* Initialize the slot info structure with some known
|
||||
* good values.
|
||||
*/
|
||||
dbg("initializing slot %s", slot->hotplug_slot->name);
|
||||
info->power_status = cpci_get_power_status(slot);
|
||||
info->attention_status = cpci_get_attention_status(slot);
|
||||
|
||||
dbg("registering slot %s", slot->hotplug_slot->name);
|
||||
status = pci_hp_register(slot->hotplug_slot);
|
||||
if (status) {
|
||||
err("pci_hp_register failed with error %d", status);
|
||||
goto error_name;
|
||||
}
|
||||
|
||||
/* Add slot to our internal list */
|
||||
down_write(&list_rwsem);
|
||||
list_add(&slot->slot_list, &slot_list);
|
||||
slots++;
|
||||
up_write(&list_rwsem);
|
||||
}
|
||||
return 0;
|
||||
error_name:
|
||||
kfree(name);
|
||||
error_info:
|
||||
kfree(info);
|
||||
error_hpslot:
|
||||
kfree(hotplug_slot);
|
||||
error_slot:
|
||||
kfree(slot);
|
||||
error:
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
cpci_hp_unregister_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct slot *slot;
|
||||
struct slot *tmp;
|
||||
int status = 0;
|
||||
|
||||
down_write(&list_rwsem);
|
||||
if (!slots) {
|
||||
up_write(&list_rwsem);
|
||||
return -1;
|
||||
}
|
||||
list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
|
||||
if (slot->bus == bus) {
|
||||
list_del(&slot->slot_list);
|
||||
slots--;
|
||||
|
||||
dbg("deregistering slot %s", slot->hotplug_slot->name);
|
||||
status = pci_hp_deregister(slot->hotplug_slot);
|
||||
if (status) {
|
||||
err("pci_hp_deregister failed with error %d",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
up_write(&list_rwsem);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* This is the interrupt mode interrupt handler */
|
||||
static irqreturn_t
|
||||
cpci_hp_intr(int irq, void *data)
|
||||
{
|
||||
dbg("entered cpci_hp_intr");
|
||||
|
||||
/* Check to see if it was our interrupt */
|
||||
if ((controller->irq_flags & IRQF_SHARED) &&
|
||||
!controller->ops->check_irq(controller->dev_id)) {
|
||||
dbg("exited cpci_hp_intr, not our interrupt");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Disable ENUM interrupt */
|
||||
controller->ops->disable_irq();
|
||||
|
||||
/* Trigger processing by the event thread */
|
||||
dbg("Signal event_semaphore");
|
||||
up(&event_semaphore);
|
||||
dbg("exited cpci_hp_intr");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to PICMG 2.1 R2.0, section 6.3.2, upon
|
||||
* initialization, the system driver shall clear the
|
||||
* INS bits of the cold-inserted devices.
|
||||
*/
|
||||
static int
|
||||
init_slots(int clear_ins)
|
||||
{
|
||||
struct slot *slot;
|
||||
struct pci_dev* dev;
|
||||
|
||||
dbg("%s - enter", __FUNCTION__);
|
||||
down_read(&list_rwsem);
|
||||
if (!slots) {
|
||||
up_read(&list_rwsem);
|
||||
return -1;
|
||||
}
|
||||
list_for_each_entry(slot, &slot_list, slot_list) {
|
||||
dbg("%s - looking at slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
if (clear_ins && cpci_check_and_clear_ins(slot))
|
||||
dbg("%s - cleared INS for slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
|
||||
if (dev) {
|
||||
if (update_adapter_status(slot->hotplug_slot, 1))
|
||||
warn("failure to update adapter file");
|
||||
if (update_latch_status(slot->hotplug_slot, 1))
|
||||
warn("failure to update latch file");
|
||||
slot->dev = dev;
|
||||
}
|
||||
}
|
||||
up_read(&list_rwsem);
|
||||
dbg("%s - exit", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_slots(void)
|
||||
{
|
||||
struct slot *slot;
|
||||
int extracted;
|
||||
int inserted;
|
||||
u16 hs_csr;
|
||||
|
||||
down_read(&list_rwsem);
|
||||
if (!slots) {
|
||||
up_read(&list_rwsem);
|
||||
err("no slots registered, shutting down");
|
||||
return -1;
|
||||
}
|
||||
extracted = inserted = 0;
|
||||
list_for_each_entry(slot, &slot_list, slot_list) {
|
||||
dbg("%s - looking at slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
if (cpci_check_and_clear_ins(slot)) {
|
||||
/*
|
||||
* Some broken hardware (e.g. PLX 9054AB) asserts
|
||||
* ENUM# twice...
|
||||
*/
|
||||
if (slot->dev) {
|
||||
warn("slot %s already inserted",
|
||||
slot->hotplug_slot->name);
|
||||
inserted++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Process insertion */
|
||||
dbg("%s - slot %s inserted",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
|
||||
/* GSM, debug */
|
||||
hs_csr = cpci_get_hs_csr(slot);
|
||||
dbg("%s - slot %s HS_CSR (1) = %04x",
|
||||
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
|
||||
|
||||
/* Configure device */
|
||||
dbg("%s - configuring slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
if (cpci_configure_slot(slot)) {
|
||||
err("%s - could not configure slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
continue;
|
||||
}
|
||||
dbg("%s - finished configuring slot %s",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
|
||||
/* GSM, debug */
|
||||
hs_csr = cpci_get_hs_csr(slot);
|
||||
dbg("%s - slot %s HS_CSR (2) = %04x",
|
||||
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
|
||||
|
||||
if (update_latch_status(slot->hotplug_slot, 1))
|
||||
warn("failure to update latch file");
|
||||
|
||||
if (update_adapter_status(slot->hotplug_slot, 1))
|
||||
warn("failure to update adapter file");
|
||||
|
||||
cpci_led_off(slot);
|
||||
|
||||
/* GSM, debug */
|
||||
hs_csr = cpci_get_hs_csr(slot);
|
||||
dbg("%s - slot %s HS_CSR (3) = %04x",
|
||||
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
|
||||
|
||||
inserted++;
|
||||
} else if (cpci_check_ext(slot)) {
|
||||
/* Process extraction request */
|
||||
dbg("%s - slot %s extracted",
|
||||
__FUNCTION__, slot->hotplug_slot->name);
|
||||
|
||||
/* GSM, debug */
|
||||
hs_csr = cpci_get_hs_csr(slot);
|
||||
dbg("%s - slot %s HS_CSR = %04x",
|
||||
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
|
||||
|
||||
if (!slot->extracting) {
|
||||
if (update_latch_status(slot->hotplug_slot, 0)) {
|
||||
warn("failure to update latch file");
|
||||
}
|
||||
slot->extracting = 1;
|
||||
atomic_inc(&extracting);
|
||||
}
|
||||
extracted++;
|
||||
} else if (slot->extracting) {
|
||||
hs_csr = cpci_get_hs_csr(slot);
|
||||
if (hs_csr == 0xffff) {
|
||||
/*
|
||||
* Hmmm, we're likely hosed at this point, should we
|
||||
* bother trying to tell the driver or not?
|
||||
*/
|
||||
err("card in slot %s was improperly removed",
|
||||
slot->hotplug_slot->name);
|
||||
if (update_adapter_status(slot->hotplug_slot, 0))
|
||||
warn("failure to update adapter file");
|
||||
slot->extracting = 0;
|
||||
atomic_dec(&extracting);
|
||||
}
|
||||
}
|
||||
}
|
||||
up_read(&list_rwsem);
|
||||
dbg("inserted=%d, extracted=%d, extracting=%d",
|
||||
inserted, extracted, atomic_read(&extracting));
|
||||
if (inserted || extracted)
|
||||
return extracted;
|
||||
else if (!atomic_read(&extracting)) {
|
||||
err("cannot find ENUM# source, shutting down");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the interrupt mode worker thread body */
|
||||
static int
|
||||
event_thread(void *data)
|
||||
{
|
||||
int rc;
|
||||
|
||||
lock_kernel();
|
||||
daemonize("cpci_hp_eventd");
|
||||
unlock_kernel();
|
||||
|
||||
dbg("%s - event thread started", __FUNCTION__);
|
||||
while (1) {
|
||||
dbg("event thread sleeping");
|
||||
down_interruptible(&event_semaphore);
|
||||
dbg("event thread woken, thread_finished = %d",
|
||||
thread_finished);
|
||||
if (thread_finished || signal_pending(current))
|
||||
break;
|
||||
do {
|
||||
rc = check_slots();
|
||||
if (rc > 0) {
|
||||
/* Give userspace a chance to handle extraction */
|
||||
msleep(500);
|
||||
} else if (rc < 0) {
|
||||
dbg("%s - error checking slots", __FUNCTION__);
|
||||
thread_finished = 1;
|
||||
break;
|
||||
}
|
||||
} while (atomic_read(&extracting) && !thread_finished);
|
||||
if (thread_finished)
|
||||
break;
|
||||
|
||||
/* Re-enable ENUM# interrupt */
|
||||
dbg("%s - re-enabling irq", __FUNCTION__);
|
||||
controller->ops->enable_irq();
|
||||
}
|
||||
dbg("%s - event thread signals exit", __FUNCTION__);
|
||||
up(&thread_exit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the polling mode worker thread body */
|
||||
static int
|
||||
poll_thread(void *data)
|
||||
{
|
||||
int rc;
|
||||
|
||||
lock_kernel();
|
||||
daemonize("cpci_hp_polld");
|
||||
unlock_kernel();
|
||||
|
||||
while (1) {
|
||||
if (thread_finished || signal_pending(current))
|
||||
break;
|
||||
if (controller->ops->query_enum()) {
|
||||
do {
|
||||
rc = check_slots();
|
||||
if (rc > 0) {
|
||||
/* Give userspace a chance to handle extraction */
|
||||
msleep(500);
|
||||
} else if (rc < 0) {
|
||||
dbg("%s - error checking slots", __FUNCTION__);
|
||||
thread_finished = 1;
|
||||
break;
|
||||
}
|
||||
} while (atomic_read(&extracting) && !thread_finished);
|
||||
}
|
||||
msleep(100);
|
||||
}
|
||||
dbg("poll thread signals exit");
|
||||
up(&thread_exit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cpci_start_thread(void)
|
||||
{
|
||||
int pid;
|
||||
|
||||
/* initialize our semaphores */
|
||||
init_MUTEX_LOCKED(&event_semaphore);
|
||||
init_MUTEX_LOCKED(&thread_exit);
|
||||
thread_finished = 0;
|
||||
|
||||
if (controller->irq)
|
||||
pid = kernel_thread(event_thread, NULL, 0);
|
||||
else
|
||||
pid = kernel_thread(poll_thread, NULL, 0);
|
||||
if (pid < 0) {
|
||||
err("Can't start up our thread");
|
||||
return -1;
|
||||
}
|
||||
dbg("Our thread pid = %d", pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cpci_stop_thread(void)
|
||||
{
|
||||
thread_finished = 1;
|
||||
dbg("thread finish command given");
|
||||
if (controller->irq)
|
||||
up(&event_semaphore);
|
||||
dbg("wait for thread to exit");
|
||||
down(&thread_exit);
|
||||
}
|
||||
|
||||
int
|
||||
cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (controller)
|
||||
return -1;
|
||||
if (!(new_controller && new_controller->ops))
|
||||
return -EINVAL;
|
||||
if (new_controller->irq) {
|
||||
if (!(new_controller->ops->enable_irq &&
|
||||
new_controller->ops->disable_irq))
|
||||
status = -EINVAL;
|
||||
if (request_irq(new_controller->irq,
|
||||
cpci_hp_intr,
|
||||
new_controller->irq_flags,
|
||||
MY_NAME,
|
||||
new_controller->dev_id)) {
|
||||
err("Can't get irq %d for the hotplug cPCI controller",
|
||||
new_controller->irq);
|
||||
status = -ENODEV;
|
||||
}
|
||||
dbg("%s - acquired controller irq %d",
|
||||
__FUNCTION__, new_controller->irq);
|
||||
}
|
||||
if (!status)
|
||||
controller = new_controller;
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_slots(void)
|
||||
{
|
||||
struct slot *slot;
|
||||
struct slot *tmp;
|
||||
|
||||
/*
|
||||
* Unregister all of our slots with the pci_hotplug subsystem,
|
||||
* and free up all memory that we had allocated.
|
||||
*/
|
||||
down_write(&list_rwsem);
|
||||
if (!slots)
|
||||
goto cleanup_null;
|
||||
list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
|
||||
list_del(&slot->slot_list);
|
||||
pci_hp_deregister(slot->hotplug_slot);
|
||||
}
|
||||
cleanup_null:
|
||||
up_write(&list_rwsem);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (controller) {
|
||||
if (!thread_finished)
|
||||
cpci_stop_thread();
|
||||
if (controller->irq)
|
||||
free_irq(controller->irq, controller->dev_id);
|
||||
controller = NULL;
|
||||
cleanup_slots();
|
||||
} else
|
||||
status = -ENODEV;
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
cpci_hp_start(void)
|
||||
{
|
||||
static int first = 1;
|
||||
int status;
|
||||
|
||||
dbg("%s - enter", __FUNCTION__);
|
||||
if (!controller)
|
||||
return -ENODEV;
|
||||
|
||||
down_read(&list_rwsem);
|
||||
if (list_empty(&slot_list)) {
|
||||
up_read(&list_rwsem);
|
||||
return -ENODEV;
|
||||
}
|
||||
up_read(&list_rwsem);
|
||||
|
||||
status = init_slots(first);
|
||||
if (first)
|
||||
first = 0;
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = cpci_start_thread();
|
||||
if (status)
|
||||
return status;
|
||||
dbg("%s - thread started", __FUNCTION__);
|
||||
|
||||
if (controller->irq) {
|
||||
/* Start enum interrupt processing */
|
||||
dbg("%s - enabling irq", __FUNCTION__);
|
||||
controller->ops->enable_irq();
|
||||
}
|
||||
dbg("%s - exit", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
cpci_hp_stop(void)
|
||||
{
|
||||
if (!controller)
|
||||
return -ENODEV;
|
||||
if (controller->irq) {
|
||||
/* Stop enum interrupt processing */
|
||||
dbg("%s - disabling irq", __FUNCTION__);
|
||||
controller->ops->disable_irq();
|
||||
}
|
||||
cpci_stop_thread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init
|
||||
cpci_hotplug_init(int debug)
|
||||
{
|
||||
cpci_debug = debug;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit
|
||||
cpci_hotplug_exit(void)
|
||||
{
|
||||
/*
|
||||
* Clean everything up.
|
||||
*/
|
||||
cpci_hp_stop();
|
||||
cpci_hp_unregister_controller(controller);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
|
||||
EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
|
||||
EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
|
||||
EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
|
||||
EXPORT_SYMBOL_GPL(cpci_hp_start);
|
||||
EXPORT_SYMBOL_GPL(cpci_hp_stop);
|
||||
355
drivers/pci/hotplug/cpci_hotplug_pci.c
Normal file
355
drivers/pci/hotplug/cpci_hotplug_pci.c
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* CompactPCI Hot Plug Driver PCI functions
|
||||
*
|
||||
* Copyright (C) 2002,2005 by SOMA Networks, Inc.
|
||||
*
|
||||
* 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 <scottm@somanetworks.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include "../pci.h"
|
||||
#include "cpci_hotplug.h"
|
||||
|
||||
#define MY_NAME "cpci_hotplug"
|
||||
|
||||
extern int cpci_debug;
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (cpci_debug) \
|
||||
printk (KERN_DEBUG "%s: " format "\n", \
|
||||
MY_NAME , ## arg); \
|
||||
} while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
|
||||
|
||||
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
|
||||
|
||||
u8 cpci_get_attention_status(struct slot* slot)
|
||||
{
|
||||
int hs_cap;
|
||||
u16 hs_csr;
|
||||
|
||||
hs_cap = pci_bus_find_capability(slot->bus,
|
||||
slot->devfn,
|
||||
PCI_CAP_ID_CHSWP);
|
||||
if (!hs_cap)
|
||||
return 0;
|
||||
|
||||
if (pci_bus_read_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
&hs_csr))
|
||||
return 0;
|
||||
|
||||
return hs_csr & 0x0008 ? 1 : 0;
|
||||
}
|
||||
|
||||
int cpci_set_attention_status(struct slot* slot, int status)
|
||||
{
|
||||
int hs_cap;
|
||||
u16 hs_csr;
|
||||
|
||||
hs_cap = pci_bus_find_capability(slot->bus,
|
||||
slot->devfn,
|
||||
PCI_CAP_ID_CHSWP);
|
||||
if (!hs_cap)
|
||||
return 0;
|
||||
if (pci_bus_read_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
&hs_csr))
|
||||
return 0;
|
||||
if (status)
|
||||
hs_csr |= HS_CSR_LOO;
|
||||
else
|
||||
hs_csr &= ~HS_CSR_LOO;
|
||||
if (pci_bus_write_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
hs_csr))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
u16 cpci_get_hs_csr(struct slot* slot)
|
||||
{
|
||||
int hs_cap;
|
||||
u16 hs_csr;
|
||||
|
||||
hs_cap = pci_bus_find_capability(slot->bus,
|
||||
slot->devfn,
|
||||
PCI_CAP_ID_CHSWP);
|
||||
if (!hs_cap)
|
||||
return 0xFFFF;
|
||||
if (pci_bus_read_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
&hs_csr))
|
||||
return 0xFFFF;
|
||||
return hs_csr;
|
||||
}
|
||||
|
||||
int cpci_check_and_clear_ins(struct slot* slot)
|
||||
{
|
||||
int hs_cap;
|
||||
u16 hs_csr;
|
||||
int ins = 0;
|
||||
|
||||
hs_cap = pci_bus_find_capability(slot->bus,
|
||||
slot->devfn,
|
||||
PCI_CAP_ID_CHSWP);
|
||||
if (!hs_cap)
|
||||
return 0;
|
||||
if (pci_bus_read_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
&hs_csr))
|
||||
return 0;
|
||||
if (hs_csr & HS_CSR_INS) {
|
||||
/* Clear INS (by setting it) */
|
||||
if (pci_bus_write_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
hs_csr))
|
||||
ins = 0;
|
||||
else
|
||||
ins = 1;
|
||||
}
|
||||
return ins;
|
||||
}
|
||||
|
||||
int cpci_check_ext(struct slot* slot)
|
||||
{
|
||||
int hs_cap;
|
||||
u16 hs_csr;
|
||||
int ext = 0;
|
||||
|
||||
hs_cap = pci_bus_find_capability(slot->bus,
|
||||
slot->devfn,
|
||||
PCI_CAP_ID_CHSWP);
|
||||
if (!hs_cap)
|
||||
return 0;
|
||||
if (pci_bus_read_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
&hs_csr))
|
||||
return 0;
|
||||
if (hs_csr & HS_CSR_EXT)
|
||||
ext = 1;
|
||||
return ext;
|
||||
}
|
||||
|
||||
int cpci_clear_ext(struct slot* slot)
|
||||
{
|
||||
int hs_cap;
|
||||
u16 hs_csr;
|
||||
|
||||
hs_cap = pci_bus_find_capability(slot->bus,
|
||||
slot->devfn,
|
||||
PCI_CAP_ID_CHSWP);
|
||||
if (!hs_cap)
|
||||
return -ENODEV;
|
||||
if (pci_bus_read_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
&hs_csr))
|
||||
return -ENODEV;
|
||||
if (hs_csr & HS_CSR_EXT) {
|
||||
/* Clear EXT (by setting it) */
|
||||
if (pci_bus_write_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
hs_csr))
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpci_led_on(struct slot* slot)
|
||||
{
|
||||
int hs_cap;
|
||||
u16 hs_csr;
|
||||
|
||||
hs_cap = pci_bus_find_capability(slot->bus,
|
||||
slot->devfn,
|
||||
PCI_CAP_ID_CHSWP);
|
||||
if (!hs_cap)
|
||||
return -ENODEV;
|
||||
if (pci_bus_read_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
&hs_csr))
|
||||
return -ENODEV;
|
||||
if ((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) {
|
||||
hs_csr |= HS_CSR_LOO;
|
||||
if (pci_bus_write_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
hs_csr)) {
|
||||
err("Could not set LOO for slot %s",
|
||||
slot->hotplug_slot->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpci_led_off(struct slot* slot)
|
||||
{
|
||||
int hs_cap;
|
||||
u16 hs_csr;
|
||||
|
||||
hs_cap = pci_bus_find_capability(slot->bus,
|
||||
slot->devfn,
|
||||
PCI_CAP_ID_CHSWP);
|
||||
if (!hs_cap)
|
||||
return -ENODEV;
|
||||
if (pci_bus_read_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
&hs_csr))
|
||||
return -ENODEV;
|
||||
if (hs_csr & HS_CSR_LOO) {
|
||||
hs_csr &= ~HS_CSR_LOO;
|
||||
if (pci_bus_write_config_word(slot->bus,
|
||||
slot->devfn,
|
||||
hs_cap + 2,
|
||||
hs_csr)) {
|
||||
err("Could not clear LOO for slot %s",
|
||||
slot->hotplug_slot->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Device configuration functions
|
||||
*/
|
||||
|
||||
int cpci_configure_slot(struct slot* slot)
|
||||
{
|
||||
struct pci_bus *parent;
|
||||
int fn;
|
||||
|
||||
dbg("%s - enter", __FUNCTION__);
|
||||
|
||||
if (slot->dev == NULL) {
|
||||
dbg("pci_dev null, finding %02x:%02x:%x",
|
||||
slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn));
|
||||
slot->dev = pci_get_slot(slot->bus, slot->devfn);
|
||||
}
|
||||
|
||||
/* Still NULL? Well then scan for it! */
|
||||
if (slot->dev == NULL) {
|
||||
int n;
|
||||
dbg("pci_dev still null");
|
||||
|
||||
/*
|
||||
* This will generate pci_dev structures for all functions, but
|
||||
* we will only call this case when lookup fails.
|
||||
*/
|
||||
n = pci_scan_slot(slot->bus, slot->devfn);
|
||||
dbg("%s: pci_scan_slot returned %d", __FUNCTION__, n);
|
||||
slot->dev = pci_get_slot(slot->bus, slot->devfn);
|
||||
if (slot->dev == NULL) {
|
||||
err("Could not find PCI device for slot %02x", slot->number);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
parent = slot->dev->bus;
|
||||
|
||||
for (fn = 0; fn < 8; fn++) {
|
||||
struct pci_dev *dev;
|
||||
|
||||
dev = pci_get_slot(parent, PCI_DEVFN(PCI_SLOT(slot->devfn), fn));
|
||||
if (!dev)
|
||||
continue;
|
||||
if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) ||
|
||||
(dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) {
|
||||
/* Find an unused bus number for the new bridge */
|
||||
struct pci_bus *child;
|
||||
unsigned char busnr, start = parent->secondary;
|
||||
unsigned char end = parent->subordinate;
|
||||
|
||||
for (busnr = start; busnr <= end; busnr++) {
|
||||
if (!pci_find_bus(pci_domain_nr(parent),
|
||||
busnr))
|
||||
break;
|
||||
}
|
||||
if (busnr >= end) {
|
||||
err("No free bus for hot-added bridge\n");
|
||||
pci_dev_put(dev);
|
||||
continue;
|
||||
}
|
||||
child = pci_add_new_bus(parent, dev, busnr);
|
||||
if (!child) {
|
||||
err("Cannot add new bus for %s\n",
|
||||
pci_name(dev));
|
||||
pci_dev_put(dev);
|
||||
continue;
|
||||
}
|
||||
child->subordinate = pci_do_scan_bus(child);
|
||||
pci_bus_size_bridges(child);
|
||||
}
|
||||
pci_dev_put(dev);
|
||||
}
|
||||
|
||||
pci_bus_assign_resources(parent);
|
||||
pci_bus_add_devices(parent);
|
||||
pci_enable_bridges(parent);
|
||||
|
||||
dbg("%s - exit", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpci_unconfigure_slot(struct slot* slot)
|
||||
{
|
||||
int i;
|
||||
struct pci_dev *dev;
|
||||
|
||||
dbg("%s - enter", __FUNCTION__);
|
||||
if (!slot->dev) {
|
||||
err("No device for slot %02x\n", slot->number);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
dev = pci_get_slot(slot->bus,
|
||||
PCI_DEVFN(PCI_SLOT(slot->devfn), i));
|
||||
if (dev) {
|
||||
pci_remove_bus_device(dev);
|
||||
pci_dev_put(dev);
|
||||
}
|
||||
}
|
||||
pci_dev_put(slot->dev);
|
||||
slot->dev = NULL;
|
||||
|
||||
dbg("%s - exit", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
223
drivers/pci/hotplug/cpcihp_generic.c
Normal file
223
drivers/pci/hotplug/cpcihp_generic.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* cpcihp_generic.c
|
||||
*
|
||||
* Generic port I/O CompactPCI driver
|
||||
*
|
||||
* Copyright 2002 SOMA Networks, Inc.
|
||||
* Copyright 2001 Intel San Luis Obispo
|
||||
* Copyright 2000,2001 MontaVista Software Inc.
|
||||
*
|
||||
* 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 SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This generic CompactPCI hotplug driver should allow using the PCI hotplug
|
||||
* mechanism on any CompactPCI board that exposes the #ENUM signal as a bit
|
||||
* in a system register that can be read through standard port I/O.
|
||||
*
|
||||
* Send feedback to <scottm@somanetworks.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
#include "cpci_hotplug.h"
|
||||
|
||||
#define DRIVER_VERSION "0.1"
|
||||
#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
|
||||
#define DRIVER_DESC "Generic port I/O CompactPCI Hot Plug Driver"
|
||||
|
||||
#if !defined(MODULE)
|
||||
#define MY_NAME "cpcihp_generic"
|
||||
#else
|
||||
#define MY_NAME THIS_MODULE->name
|
||||
#endif
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if(debug) \
|
||||
printk (KERN_DEBUG "%s: " format "\n", \
|
||||
MY_NAME , ## arg); \
|
||||
} while(0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
|
||||
|
||||
/* local variables */
|
||||
static int debug;
|
||||
static char *bridge;
|
||||
static u8 bridge_busnr;
|
||||
static u8 bridge_slot;
|
||||
static struct pci_bus *bus;
|
||||
static u8 first_slot;
|
||||
static u8 last_slot;
|
||||
static u16 port;
|
||||
static unsigned int enum_bit;
|
||||
static u8 enum_mask;
|
||||
|
||||
static struct cpci_hp_controller_ops generic_hpc_ops;
|
||||
static struct cpci_hp_controller generic_hpc;
|
||||
|
||||
static int __init validate_parameters(void)
|
||||
{
|
||||
char* str;
|
||||
char* p;
|
||||
unsigned long tmp;
|
||||
|
||||
if(!bridge) {
|
||||
info("not configured, disabling.");
|
||||
return -EINVAL;
|
||||
}
|
||||
str = bridge;
|
||||
if(!*str)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = simple_strtoul(str, &p, 16);
|
||||
if(p == str || tmp > 0xff) {
|
||||
err("Invalid hotplug bus bridge device bus number");
|
||||
return -EINVAL;
|
||||
}
|
||||
bridge_busnr = (u8) tmp;
|
||||
dbg("bridge_busnr = 0x%02x", bridge_busnr);
|
||||
if(*p != ':') {
|
||||
err("Invalid hotplug bus bridge device");
|
||||
return -EINVAL;
|
||||
}
|
||||
str = p + 1;
|
||||
tmp = simple_strtoul(str, &p, 16);
|
||||
if(p == str || tmp > 0x1f) {
|
||||
err("Invalid hotplug bus bridge device slot number");
|
||||
return -EINVAL;
|
||||
}
|
||||
bridge_slot = (u8) tmp;
|
||||
dbg("bridge_slot = 0x%02x", bridge_slot);
|
||||
|
||||
dbg("first_slot = 0x%02x", first_slot);
|
||||
dbg("last_slot = 0x%02x", last_slot);
|
||||
if(!(first_slot && last_slot)) {
|
||||
err("Need to specify first_slot and last_slot");
|
||||
return -EINVAL;
|
||||
}
|
||||
if(last_slot < first_slot) {
|
||||
err("first_slot must be less than last_slot");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dbg("port = 0x%04x", port);
|
||||
dbg("enum_bit = 0x%02x", enum_bit);
|
||||
if(enum_bit > 7) {
|
||||
err("Invalid #ENUM bit");
|
||||
return -EINVAL;
|
||||
}
|
||||
enum_mask = 1 << enum_bit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int query_enum(void)
|
||||
{
|
||||
u8 value;
|
||||
|
||||
value = inb_p(port);
|
||||
return ((value & enum_mask) == enum_mask);
|
||||
}
|
||||
|
||||
static int __init cpcihp_generic_init(void)
|
||||
{
|
||||
int status;
|
||||
struct resource* r;
|
||||
struct pci_dev* dev;
|
||||
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION);
|
||||
status = validate_parameters();
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
r = request_region(port, 1, "#ENUM hotswap signal register");
|
||||
if(!r)
|
||||
return -EBUSY;
|
||||
|
||||
dev = pci_find_slot(bridge_busnr, PCI_DEVFN(bridge_slot, 0));
|
||||
if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
|
||||
err("Invalid bridge device %s", bridge);
|
||||
return -EINVAL;
|
||||
}
|
||||
bus = dev->subordinate;
|
||||
|
||||
memset(&generic_hpc, 0, sizeof (struct cpci_hp_controller));
|
||||
generic_hpc_ops.query_enum = query_enum;
|
||||
generic_hpc.ops = &generic_hpc_ops;
|
||||
|
||||
status = cpci_hp_register_controller(&generic_hpc);
|
||||
if(status != 0) {
|
||||
err("Could not register cPCI hotplug controller");
|
||||
return -ENODEV;
|
||||
}
|
||||
dbg("registered controller");
|
||||
|
||||
status = cpci_hp_register_bus(bus, first_slot, last_slot);
|
||||
if(status != 0) {
|
||||
err("Could not register cPCI hotplug bus");
|
||||
goto init_bus_register_error;
|
||||
}
|
||||
dbg("registered bus");
|
||||
|
||||
status = cpci_hp_start();
|
||||
if(status != 0) {
|
||||
err("Could not started cPCI hotplug system");
|
||||
goto init_start_error;
|
||||
}
|
||||
dbg("started cpci hp system");
|
||||
return 0;
|
||||
init_start_error:
|
||||
cpci_hp_unregister_bus(bus);
|
||||
init_bus_register_error:
|
||||
cpci_hp_unregister_controller(&generic_hpc);
|
||||
err("status = %d", status);
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static void __exit cpcihp_generic_exit(void)
|
||||
{
|
||||
cpci_hp_stop();
|
||||
cpci_hp_unregister_bus(bus);
|
||||
cpci_hp_unregister_controller(&generic_hpc);
|
||||
release_region(port, 1);
|
||||
}
|
||||
|
||||
module_init(cpcihp_generic_init);
|
||||
module_exit(cpcihp_generic_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
||||
module_param(bridge, charp, 0);
|
||||
MODULE_PARM_DESC(bridge, "Hotswap bus bridge device, <bus>:<slot> (bus and slot are in hexadecimal)");
|
||||
module_param(first_slot, byte, 0);
|
||||
MODULE_PARM_DESC(first_slot, "Hotswap bus first slot number");
|
||||
module_param(last_slot, byte, 0);
|
||||
MODULE_PARM_DESC(last_slot, "Hotswap bus last slot number");
|
||||
module_param(port, ushort, 0);
|
||||
MODULE_PARM_DESC(port, "#ENUM signal I/O port");
|
||||
module_param(enum_bit, uint, 0);
|
||||
MODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)");
|
||||
324
drivers/pci/hotplug/cpcihp_zt5550.c
Normal file
324
drivers/pci/hotplug/cpcihp_zt5550.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* cpcihp_zt5550.c
|
||||
*
|
||||
* Intel/Ziatech ZT5550 CompactPCI Host Controller driver
|
||||
*
|
||||
* Copyright 2002 SOMA Networks, Inc.
|
||||
* Copyright 2001 Intel San Luis Obispo
|
||||
* Copyright 2000,2001 MontaVista Software Inc.
|
||||
*
|
||||
* 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 SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* 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 <scottm@somanetworks.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/signal.h> /* IRQF_SHARED */
|
||||
#include "cpci_hotplug.h"
|
||||
#include "cpcihp_zt5550.h"
|
||||
|
||||
#define DRIVER_VERSION "0.2"
|
||||
#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
|
||||
#define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver"
|
||||
|
||||
#define MY_NAME "cpcihp_zt5550"
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if(debug) \
|
||||
printk (KERN_DEBUG "%s: " format "\n", \
|
||||
MY_NAME , ## arg); \
|
||||
} while(0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
|
||||
|
||||
/* local variables */
|
||||
static int debug;
|
||||
static int poll;
|
||||
static struct cpci_hp_controller_ops zt5550_hpc_ops;
|
||||
static struct cpci_hp_controller zt5550_hpc;
|
||||
|
||||
/* Primary cPCI bus bridge device */
|
||||
static struct pci_dev *bus0_dev;
|
||||
static struct pci_bus *bus0;
|
||||
|
||||
/* Host controller device */
|
||||
static struct pci_dev *hc_dev;
|
||||
|
||||
/* Host controller register addresses */
|
||||
static void __iomem *hc_registers;
|
||||
static void __iomem *csr_hc_index;
|
||||
static void __iomem *csr_hc_data;
|
||||
static void __iomem *csr_int_status;
|
||||
static void __iomem *csr_int_mask;
|
||||
|
||||
|
||||
static int zt5550_hc_config(struct pci_dev *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Since we know that no boards exist with two HC chips, treat it as an error */
|
||||
if(hc_dev) {
|
||||
err("too many host controller devices?");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
if(ret) {
|
||||
err("cannot enable %s\n", pci_name(pdev));
|
||||
return ret;
|
||||
}
|
||||
|
||||
hc_dev = pdev;
|
||||
dbg("hc_dev = %p", hc_dev);
|
||||
dbg("pci resource start %llx", (unsigned long long)pci_resource_start(hc_dev, 1));
|
||||
dbg("pci resource len %llx", (unsigned long long)pci_resource_len(hc_dev, 1));
|
||||
|
||||
if(!request_mem_region(pci_resource_start(hc_dev, 1),
|
||||
pci_resource_len(hc_dev, 1), MY_NAME)) {
|
||||
err("cannot reserve MMIO region");
|
||||
ret = -ENOMEM;
|
||||
goto exit_disable_device;
|
||||
}
|
||||
|
||||
hc_registers =
|
||||
ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1));
|
||||
if(!hc_registers) {
|
||||
err("cannot remap MMIO region %llx @ %llx",
|
||||
(unsigned long long)pci_resource_len(hc_dev, 1),
|
||||
(unsigned long long)pci_resource_start(hc_dev, 1));
|
||||
ret = -ENODEV;
|
||||
goto exit_release_region;
|
||||
}
|
||||
|
||||
csr_hc_index = hc_registers + CSR_HCINDEX;
|
||||
csr_hc_data = hc_registers + CSR_HCDATA;
|
||||
csr_int_status = hc_registers + CSR_INTSTAT;
|
||||
csr_int_mask = hc_registers + CSR_INTMASK;
|
||||
|
||||
/*
|
||||
* Disable host control, fault and serial interrupts
|
||||
*/
|
||||
dbg("disabling host control, fault and serial interrupts");
|
||||
writeb((u8) HC_INT_MASK_REG, csr_hc_index);
|
||||
writeb((u8) ALL_INDEXED_INTS_MASK, csr_hc_data);
|
||||
dbg("disabled host control, fault and serial interrupts");
|
||||
|
||||
/*
|
||||
* Disable timer0, timer1 and ENUM interrupts
|
||||
*/
|
||||
dbg("disabling timer0, timer1 and ENUM interrupts");
|
||||
writeb((u8) ALL_DIRECT_INTS_MASK, csr_int_mask);
|
||||
dbg("disabled timer0, timer1 and ENUM interrupts");
|
||||
return 0;
|
||||
|
||||
exit_release_region:
|
||||
release_mem_region(pci_resource_start(hc_dev, 1),
|
||||
pci_resource_len(hc_dev, 1));
|
||||
exit_disable_device:
|
||||
pci_disable_device(hc_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zt5550_hc_cleanup(void)
|
||||
{
|
||||
if(!hc_dev)
|
||||
return -ENODEV;
|
||||
|
||||
iounmap(hc_registers);
|
||||
release_mem_region(pci_resource_start(hc_dev, 1),
|
||||
pci_resource_len(hc_dev, 1));
|
||||
pci_disable_device(hc_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zt5550_hc_query_enum(void)
|
||||
{
|
||||
u8 value;
|
||||
|
||||
value = inb_p(ENUM_PORT);
|
||||
return ((value & ENUM_MASK) == ENUM_MASK);
|
||||
}
|
||||
|
||||
static int zt5550_hc_check_irq(void *dev_id)
|
||||
{
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
ret = 0;
|
||||
if(dev_id == zt5550_hpc.dev_id) {
|
||||
reg = readb(csr_int_status);
|
||||
if(reg)
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zt5550_hc_enable_irq(void)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
if(hc_dev == NULL) {
|
||||
return -ENODEV;
|
||||
}
|
||||
reg = readb(csr_int_mask);
|
||||
reg = reg & ~ENUM_INT_MASK;
|
||||
writeb(reg, csr_int_mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zt5550_hc_disable_irq(void)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
if(hc_dev == NULL) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = readb(csr_int_mask);
|
||||
reg = reg | ENUM_INT_MASK;
|
||||
writeb(reg, csr_int_mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = zt5550_hc_config(pdev);
|
||||
if(status != 0) {
|
||||
return status;
|
||||
}
|
||||
dbg("returned from zt5550_hc_config");
|
||||
|
||||
memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller));
|
||||
zt5550_hpc_ops.query_enum = zt5550_hc_query_enum;
|
||||
zt5550_hpc.ops = &zt5550_hpc_ops;
|
||||
if(!poll) {
|
||||
zt5550_hpc.irq = hc_dev->irq;
|
||||
zt5550_hpc.irq_flags = IRQF_SHARED;
|
||||
zt5550_hpc.dev_id = hc_dev;
|
||||
|
||||
zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq;
|
||||
zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq;
|
||||
zt5550_hpc_ops.check_irq = zt5550_hc_check_irq;
|
||||
} else {
|
||||
info("using ENUM# polling mode");
|
||||
}
|
||||
|
||||
status = cpci_hp_register_controller(&zt5550_hpc);
|
||||
if(status != 0) {
|
||||
err("could not register cPCI hotplug controller");
|
||||
goto init_hc_error;
|
||||
}
|
||||
dbg("registered controller");
|
||||
|
||||
/* Look for first device matching cPCI bus's bridge vendor and device IDs */
|
||||
if(!(bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC,
|
||||
PCI_DEVICE_ID_DEC_21154, NULL))) {
|
||||
status = -ENODEV;
|
||||
goto init_register_error;
|
||||
}
|
||||
bus0 = bus0_dev->subordinate;
|
||||
pci_dev_put(bus0_dev);
|
||||
|
||||
status = cpci_hp_register_bus(bus0, 0x0a, 0x0f);
|
||||
if(status != 0) {
|
||||
err("could not register cPCI hotplug bus");
|
||||
goto init_register_error;
|
||||
}
|
||||
dbg("registered bus");
|
||||
|
||||
status = cpci_hp_start();
|
||||
if(status != 0) {
|
||||
err("could not started cPCI hotplug system");
|
||||
cpci_hp_unregister_bus(bus0);
|
||||
goto init_register_error;
|
||||
}
|
||||
dbg("started cpci hp system");
|
||||
|
||||
return 0;
|
||||
init_register_error:
|
||||
cpci_hp_unregister_controller(&zt5550_hpc);
|
||||
init_hc_error:
|
||||
err("status = %d", status);
|
||||
zt5550_hc_cleanup();
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static void __devexit zt5550_hc_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
cpci_hp_stop();
|
||||
cpci_hp_unregister_bus(bus0);
|
||||
cpci_hp_unregister_controller(&zt5550_hpc);
|
||||
zt5550_hc_cleanup();
|
||||
}
|
||||
|
||||
|
||||
static struct pci_device_id zt5550_hc_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl);
|
||||
|
||||
static struct pci_driver zt5550_hc_driver = {
|
||||
.name = "zt5550_hc",
|
||||
.id_table = zt5550_hc_pci_tbl,
|
||||
.probe = zt5550_hc_init_one,
|
||||
.remove = __devexit_p(zt5550_hc_remove_one),
|
||||
};
|
||||
|
||||
static int __init zt5550_init(void)
|
||||
{
|
||||
struct resource* r;
|
||||
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION);
|
||||
r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register");
|
||||
if(!r)
|
||||
return -EBUSY;
|
||||
|
||||
return pci_register_driver(&zt5550_hc_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
zt5550_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&zt5550_hc_driver);
|
||||
release_region(ENUM_PORT, 1);
|
||||
}
|
||||
|
||||
module_init(zt5550_init);
|
||||
module_exit(zt5550_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
module_param(debug, bool, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
||||
module_param(poll, bool, 0644);
|
||||
MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not");
|
||||
79
drivers/pci/hotplug/cpcihp_zt5550.h
Normal file
79
drivers/pci/hotplug/cpcihp_zt5550.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* cpcihp_zt5550.h
|
||||
*
|
||||
* Intel/Ziatech ZT5550 CompactPCI Host Controller driver definitions
|
||||
*
|
||||
* Copyright 2002 SOMA Networks, Inc.
|
||||
* Copyright 2001 Intel San Luis Obispo
|
||||
* Copyright 2000,2001 MontaVista Software Inc.
|
||||
*
|
||||
* 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 SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* 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 <scottm@somanetworks.com>
|
||||
*/
|
||||
|
||||
#ifndef _CPCIHP_ZT5550_H
|
||||
#define _CPCIHP_ZT5550_H
|
||||
|
||||
/* Direct registers */
|
||||
#define CSR_HCINDEX 0x00
|
||||
#define CSR_HCDATA 0x04
|
||||
#define CSR_INTSTAT 0x08
|
||||
#define CSR_INTMASK 0x09
|
||||
#define CSR_CNT0CMD 0x0C
|
||||
#define CSR_CNT1CMD 0x0E
|
||||
#define CSR_CNT0 0x10
|
||||
#define CSR_CNT1 0x14
|
||||
|
||||
/* Masks for interrupt bits in CSR_INTMASK direct register */
|
||||
#define CNT0_INT_MASK 0x01
|
||||
#define CNT1_INT_MASK 0x02
|
||||
#define ENUM_INT_MASK 0x04
|
||||
#define ALL_DIRECT_INTS_MASK 0x07
|
||||
|
||||
/* Indexed registers (through CSR_INDEX, CSR_DATA) */
|
||||
#define HC_INT_MASK_REG 0x04
|
||||
#define HC_STATUS_REG 0x08
|
||||
#define HC_CMD_REG 0x0C
|
||||
#define ARB_CONFIG_GNT_REG 0x10
|
||||
#define ARB_CONFIG_CFG_REG 0x12
|
||||
#define ARB_CONFIG_REG 0x10
|
||||
#define ISOL_CONFIG_REG 0x18
|
||||
#define FAULT_STATUS_REG 0x20
|
||||
#define FAULT_CONFIG_REG 0x24
|
||||
#define WD_CONFIG_REG 0x2C
|
||||
#define HC_DIAG_REG 0x30
|
||||
#define SERIAL_COMM_REG 0x34
|
||||
#define SERIAL_OUT_REG 0x38
|
||||
#define SERIAL_IN_REG 0x3C
|
||||
|
||||
/* Masks for interrupt bits in HC_INT_MASK_REG indexed register */
|
||||
#define SERIAL_INT_MASK 0x01
|
||||
#define FAULT_INT_MASK 0x02
|
||||
#define HCF_INT_MASK 0x04
|
||||
#define ALL_INDEXED_INTS_MASK 0x07
|
||||
|
||||
/* Digital I/O port storing ENUM# */
|
||||
#define ENUM_PORT 0xE1
|
||||
/* Mask to get to the ENUM# bit on the bus */
|
||||
#define ENUM_MASK 0x40
|
||||
|
||||
#endif /* _CPCIHP_ZT5550_H */
|
||||
725
drivers/pci/hotplug/cpqphp.h
Normal file
725
drivers/pci/hotplug/cpqphp.h
Normal file
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* Compaq Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM
|
||||
*
|
||||
* 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 <greg@kroah.com>
|
||||
*
|
||||
*/
|
||||
#ifndef _CPQPHP_H
|
||||
#define _CPQPHP_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/io.h> /* for read? and write? functions */
|
||||
#include <linux/delay.h> /* for delays */
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define MY_NAME "cpqphp"
|
||||
|
||||
#define dbg(fmt, arg...) do { if (cpqhp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
|
||||
|
||||
|
||||
|
||||
struct smbios_system_slot {
|
||||
u8 type;
|
||||
u8 length;
|
||||
u16 handle;
|
||||
u8 name_string_num;
|
||||
u8 slot_type;
|
||||
u8 slot_width;
|
||||
u8 slot_current_usage;
|
||||
u8 slot_length;
|
||||
u16 slot_number;
|
||||
u8 properties1;
|
||||
u8 properties2;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* offsets to the smbios generic type based on the above structure layout */
|
||||
enum smbios_system_slot_offsets {
|
||||
SMBIOS_SLOT_GENERIC_TYPE = offsetof(struct smbios_system_slot, type),
|
||||
SMBIOS_SLOT_GENERIC_LENGTH = offsetof(struct smbios_system_slot, length),
|
||||
SMBIOS_SLOT_GENERIC_HANDLE = offsetof(struct smbios_system_slot, handle),
|
||||
SMBIOS_SLOT_NAME_STRING_NUM = offsetof(struct smbios_system_slot, name_string_num),
|
||||
SMBIOS_SLOT_TYPE = offsetof(struct smbios_system_slot, slot_type),
|
||||
SMBIOS_SLOT_WIDTH = offsetof(struct smbios_system_slot, slot_width),
|
||||
SMBIOS_SLOT_CURRENT_USAGE = offsetof(struct smbios_system_slot, slot_current_usage),
|
||||
SMBIOS_SLOT_LENGTH = offsetof(struct smbios_system_slot, slot_length),
|
||||
SMBIOS_SLOT_NUMBER = offsetof(struct smbios_system_slot, slot_number),
|
||||
SMBIOS_SLOT_PROPERTIES1 = offsetof(struct smbios_system_slot, properties1),
|
||||
SMBIOS_SLOT_PROPERTIES2 = offsetof(struct smbios_system_slot, properties2),
|
||||
};
|
||||
|
||||
struct smbios_generic {
|
||||
u8 type;
|
||||
u8 length;
|
||||
u16 handle;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* offsets to the smbios generic type based on the above structure layout */
|
||||
enum smbios_generic_offsets {
|
||||
SMBIOS_GENERIC_TYPE = offsetof(struct smbios_generic, type),
|
||||
SMBIOS_GENERIC_LENGTH = offsetof(struct smbios_generic, length),
|
||||
SMBIOS_GENERIC_HANDLE = offsetof(struct smbios_generic, handle),
|
||||
};
|
||||
|
||||
struct smbios_entry_point {
|
||||
char anchor[4];
|
||||
u8 ep_checksum;
|
||||
u8 ep_length;
|
||||
u8 major_version;
|
||||
u8 minor_version;
|
||||
u16 max_size_entry;
|
||||
u8 ep_rev;
|
||||
u8 reserved[5];
|
||||
char int_anchor[5];
|
||||
u8 int_checksum;
|
||||
u16 st_length;
|
||||
u32 st_address;
|
||||
u16 number_of_entrys;
|
||||
u8 bcd_rev;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* offsets to the smbios entry point based on the above structure layout */
|
||||
enum smbios_entry_point_offsets {
|
||||
ANCHOR = offsetof(struct smbios_entry_point, anchor[0]),
|
||||
EP_CHECKSUM = offsetof(struct smbios_entry_point, ep_checksum),
|
||||
EP_LENGTH = offsetof(struct smbios_entry_point, ep_length),
|
||||
MAJOR_VERSION = offsetof(struct smbios_entry_point, major_version),
|
||||
MINOR_VERSION = offsetof(struct smbios_entry_point, minor_version),
|
||||
MAX_SIZE_ENTRY = offsetof(struct smbios_entry_point, max_size_entry),
|
||||
EP_REV = offsetof(struct smbios_entry_point, ep_rev),
|
||||
INT_ANCHOR = offsetof(struct smbios_entry_point, int_anchor[0]),
|
||||
INT_CHECKSUM = offsetof(struct smbios_entry_point, int_checksum),
|
||||
ST_LENGTH = offsetof(struct smbios_entry_point, st_length),
|
||||
ST_ADDRESS = offsetof(struct smbios_entry_point, st_address),
|
||||
NUMBER_OF_ENTRYS = offsetof(struct smbios_entry_point, number_of_entrys),
|
||||
BCD_REV = offsetof(struct smbios_entry_point, bcd_rev),
|
||||
};
|
||||
|
||||
struct ctrl_reg { /* offset */
|
||||
u8 slot_RST; /* 0x00 */
|
||||
u8 slot_enable; /* 0x01 */
|
||||
u16 misc; /* 0x02 */
|
||||
u32 led_control; /* 0x04 */
|
||||
u32 int_input_clear; /* 0x08 */
|
||||
u32 int_mask; /* 0x0a */
|
||||
u8 reserved0; /* 0x10 */
|
||||
u8 reserved1; /* 0x11 */
|
||||
u8 reserved2; /* 0x12 */
|
||||
u8 gen_output_AB; /* 0x13 */
|
||||
u32 non_int_input; /* 0x14 */
|
||||
u32 reserved3; /* 0x18 */
|
||||
u32 reserved4; /* 0x1a */
|
||||
u32 reserved5; /* 0x20 */
|
||||
u8 reserved6; /* 0x24 */
|
||||
u8 reserved7; /* 0x25 */
|
||||
u16 reserved8; /* 0x26 */
|
||||
u8 slot_mask; /* 0x28 */
|
||||
u8 reserved9; /* 0x29 */
|
||||
u8 reserved10; /* 0x2a */
|
||||
u8 reserved11; /* 0x2b */
|
||||
u8 slot_SERR; /* 0x2c */
|
||||
u8 slot_power; /* 0x2d */
|
||||
u8 reserved12; /* 0x2e */
|
||||
u8 reserved13; /* 0x2f */
|
||||
u8 next_curr_freq; /* 0x30 */
|
||||
u8 reset_freq_mode; /* 0x31 */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* offsets to the controller registers based on the above structure layout */
|
||||
enum ctrl_offsets {
|
||||
SLOT_RST = offsetof(struct ctrl_reg, slot_RST),
|
||||
SLOT_ENABLE = offsetof(struct ctrl_reg, slot_enable),
|
||||
MISC = offsetof(struct ctrl_reg, misc),
|
||||
LED_CONTROL = offsetof(struct ctrl_reg, led_control),
|
||||
INT_INPUT_CLEAR = offsetof(struct ctrl_reg, int_input_clear),
|
||||
INT_MASK = offsetof(struct ctrl_reg, int_mask),
|
||||
CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0),
|
||||
CTRL_RESERVED1 = offsetof(struct ctrl_reg, reserved1),
|
||||
CTRL_RESERVED2 = offsetof(struct ctrl_reg, reserved1),
|
||||
GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB),
|
||||
NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input),
|
||||
CTRL_RESERVED3 = offsetof(struct ctrl_reg, reserved3),
|
||||
CTRL_RESERVED4 = offsetof(struct ctrl_reg, reserved4),
|
||||
CTRL_RESERVED5 = offsetof(struct ctrl_reg, reserved5),
|
||||
CTRL_RESERVED6 = offsetof(struct ctrl_reg, reserved6),
|
||||
CTRL_RESERVED7 = offsetof(struct ctrl_reg, reserved7),
|
||||
CTRL_RESERVED8 = offsetof(struct ctrl_reg, reserved8),
|
||||
SLOT_MASK = offsetof(struct ctrl_reg, slot_mask),
|
||||
CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9),
|
||||
CTRL_RESERVED10 = offsetof(struct ctrl_reg, reserved10),
|
||||
CTRL_RESERVED11 = offsetof(struct ctrl_reg, reserved11),
|
||||
SLOT_SERR = offsetof(struct ctrl_reg, slot_SERR),
|
||||
SLOT_POWER = offsetof(struct ctrl_reg, slot_power),
|
||||
NEXT_CURR_FREQ = offsetof(struct ctrl_reg, next_curr_freq),
|
||||
RESET_FREQ_MODE = offsetof(struct ctrl_reg, reset_freq_mode),
|
||||
};
|
||||
|
||||
struct hrt {
|
||||
char sig0;
|
||||
char sig1;
|
||||
char sig2;
|
||||
char sig3;
|
||||
u16 unused_IRQ;
|
||||
u16 PCIIRQ;
|
||||
u8 number_of_entries;
|
||||
u8 revision;
|
||||
u16 reserved1;
|
||||
u32 reserved2;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* offsets to the hotplug resource table registers based on the above structure layout */
|
||||
enum hrt_offsets {
|
||||
SIG0 = offsetof(struct hrt, sig0),
|
||||
SIG1 = offsetof(struct hrt, sig1),
|
||||
SIG2 = offsetof(struct hrt, sig2),
|
||||
SIG3 = offsetof(struct hrt, sig3),
|
||||
UNUSED_IRQ = offsetof(struct hrt, unused_IRQ),
|
||||
PCIIRQ = offsetof(struct hrt, PCIIRQ),
|
||||
NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries),
|
||||
REVISION = offsetof(struct hrt, revision),
|
||||
HRT_RESERVED1 = offsetof(struct hrt, reserved1),
|
||||
HRT_RESERVED2 = offsetof(struct hrt, reserved2),
|
||||
};
|
||||
|
||||
struct slot_rt {
|
||||
u8 dev_func;
|
||||
u8 primary_bus;
|
||||
u8 secondary_bus;
|
||||
u8 max_bus;
|
||||
u16 io_base;
|
||||
u16 io_length;
|
||||
u16 mem_base;
|
||||
u16 mem_length;
|
||||
u16 pre_mem_base;
|
||||
u16 pre_mem_length;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* offsets to the hotplug slot resource table registers based on the above structure layout */
|
||||
enum slot_rt_offsets {
|
||||
DEV_FUNC = offsetof(struct slot_rt, dev_func),
|
||||
PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
|
||||
SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
|
||||
MAX_BUS = offsetof(struct slot_rt, max_bus),
|
||||
IO_BASE = offsetof(struct slot_rt, io_base),
|
||||
IO_LENGTH = offsetof(struct slot_rt, io_length),
|
||||
MEM_BASE = offsetof(struct slot_rt, mem_base),
|
||||
MEM_LENGTH = offsetof(struct slot_rt, mem_length),
|
||||
PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
|
||||
PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
|
||||
};
|
||||
|
||||
struct pci_func {
|
||||
struct pci_func *next;
|
||||
u8 bus;
|
||||
u8 device;
|
||||
u8 function;
|
||||
u8 is_a_board;
|
||||
u16 status;
|
||||
u8 configured;
|
||||
u8 switch_save;
|
||||
u8 presence_save;
|
||||
u32 base_length[0x06];
|
||||
u8 base_type[0x06];
|
||||
u16 reserved2;
|
||||
u32 config_space[0x20];
|
||||
struct pci_resource *mem_head;
|
||||
struct pci_resource *p_mem_head;
|
||||
struct pci_resource *io_head;
|
||||
struct pci_resource *bus_head;
|
||||
struct timer_list *p_task_event;
|
||||
struct pci_dev* pci_dev;
|
||||
};
|
||||
|
||||
struct slot {
|
||||
struct slot *next;
|
||||
u8 bus;
|
||||
u8 device;
|
||||
u8 number;
|
||||
u8 is_a_board;
|
||||
u8 configured;
|
||||
u8 state;
|
||||
u8 switch_save;
|
||||
u8 presence_save;
|
||||
u32 capabilities;
|
||||
u16 reserved2;
|
||||
struct timer_list task_event;
|
||||
u8 hp_slot;
|
||||
struct controller *ctrl;
|
||||
void __iomem *p_sm_slot;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
};
|
||||
|
||||
struct pci_resource {
|
||||
struct pci_resource * next;
|
||||
u32 base;
|
||||
u32 length;
|
||||
};
|
||||
|
||||
struct event_info {
|
||||
u32 event_type;
|
||||
u8 hp_slot;
|
||||
};
|
||||
|
||||
struct controller {
|
||||
struct controller *next;
|
||||
u32 ctrl_int_comp;
|
||||
struct mutex crit_sect; /* critical section mutex */
|
||||
void __iomem *hpc_reg; /* cookie for our pci controller location */
|
||||
struct pci_resource *mem_head;
|
||||
struct pci_resource *p_mem_head;
|
||||
struct pci_resource *io_head;
|
||||
struct pci_resource *bus_head;
|
||||
struct pci_dev *pci_dev;
|
||||
struct pci_bus *pci_bus;
|
||||
struct event_info event_queue[10];
|
||||
struct slot *slot;
|
||||
u8 next_event;
|
||||
u8 interrupt;
|
||||
u8 cfgspc_irq;
|
||||
u8 bus; /* bus number for the pci hotplug controller */
|
||||
u8 rev;
|
||||
u8 slot_device_offset;
|
||||
u8 first_slot;
|
||||
u8 add_support;
|
||||
u8 push_flag;
|
||||
enum pci_bus_speed speed;
|
||||
enum pci_bus_speed speed_capability;
|
||||
u8 push_button; /* 0 = no pushbutton, 1 = pushbutton present */
|
||||
u8 slot_switch_type; /* 0 = no switch, 1 = switch present */
|
||||
u8 defeature_PHP; /* 0 = PHP not supported, 1 = PHP supported */
|
||||
u8 alternate_base_address; /* 0 = not supported, 1 = supported */
|
||||
u8 pci_config_space; /* Index/data access to working registers 0 = not supported, 1 = supported */
|
||||
u8 pcix_speed_capability; /* PCI-X */
|
||||
u8 pcix_support; /* PCI-X */
|
||||
u16 vendor_id;
|
||||
struct work_struct int_task_event;
|
||||
wait_queue_head_t queue; /* sleep & wake process */
|
||||
struct dentry *dentry; /* debugfs dentry */
|
||||
};
|
||||
|
||||
struct irq_mapping {
|
||||
u8 barber_pole;
|
||||
u8 valid_INT;
|
||||
u8 interrupt[4];
|
||||
};
|
||||
|
||||
struct resource_lists {
|
||||
struct pci_resource *mem_head;
|
||||
struct pci_resource *p_mem_head;
|
||||
struct pci_resource *io_head;
|
||||
struct pci_resource *bus_head;
|
||||
struct irq_mapping *irqs;
|
||||
};
|
||||
|
||||
#define ROM_PHY_ADDR 0x0F0000
|
||||
#define ROM_PHY_LEN 0x00ffff
|
||||
|
||||
#define PCI_HPC_ID 0xA0F7
|
||||
#define PCI_SUB_HPC_ID 0xA2F7
|
||||
#define PCI_SUB_HPC_ID2 0xA2F8
|
||||
#define PCI_SUB_HPC_ID3 0xA2F9
|
||||
#define PCI_SUB_HPC_ID_INTC 0xA2FA
|
||||
#define PCI_SUB_HPC_ID4 0xA2FD
|
||||
|
||||
#define INT_BUTTON_IGNORE 0
|
||||
#define INT_PRESENCE_ON 1
|
||||
#define INT_PRESENCE_OFF 2
|
||||
#define INT_SWITCH_CLOSE 3
|
||||
#define INT_SWITCH_OPEN 4
|
||||
#define INT_POWER_FAULT 5
|
||||
#define INT_POWER_FAULT_CLEAR 6
|
||||
#define INT_BUTTON_PRESS 7
|
||||
#define INT_BUTTON_RELEASE 8
|
||||
#define INT_BUTTON_CANCEL 9
|
||||
|
||||
#define STATIC_STATE 0
|
||||
#define BLINKINGON_STATE 1
|
||||
#define BLINKINGOFF_STATE 2
|
||||
#define POWERON_STATE 3
|
||||
#define POWEROFF_STATE 4
|
||||
|
||||
#define PCISLOT_INTERLOCK_CLOSED 0x00000001
|
||||
#define PCISLOT_ADAPTER_PRESENT 0x00000002
|
||||
#define PCISLOT_POWERED 0x00000004
|
||||
#define PCISLOT_66_MHZ_OPERATION 0x00000008
|
||||
#define PCISLOT_64_BIT_OPERATION 0x00000010
|
||||
#define PCISLOT_REPLACE_SUPPORTED 0x00000020
|
||||
#define PCISLOT_ADD_SUPPORTED 0x00000040
|
||||
#define PCISLOT_INTERLOCK_SUPPORTED 0x00000080
|
||||
#define PCISLOT_66_MHZ_SUPPORTED 0x00000100
|
||||
#define PCISLOT_64_BIT_SUPPORTED 0x00000200
|
||||
|
||||
#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
|
||||
|
||||
#define INTERLOCK_OPEN 0x00000002
|
||||
#define ADD_NOT_SUPPORTED 0x00000003
|
||||
#define CARD_FUNCTIONING 0x00000005
|
||||
#define ADAPTER_NOT_SAME 0x00000006
|
||||
#define NO_ADAPTER_PRESENT 0x00000009
|
||||
#define NOT_ENOUGH_RESOURCES 0x0000000B
|
||||
#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
|
||||
#define POWER_FAILURE 0x0000000E
|
||||
|
||||
#define REMOVE_NOT_SUPPORTED 0x00000003
|
||||
|
||||
|
||||
/*
|
||||
* error Messages
|
||||
*/
|
||||
#define msg_initialization_err "Initialization failure, error=%d\n"
|
||||
#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
|
||||
#define msg_HPC_non_compaq_or_intel "The PCI hot plug controller is not supported by this driver.\n"
|
||||
#define msg_HPC_not_supported "this system is not supported by this version of cpqphpd. Upgrade to a newer version of cpqphpd\n"
|
||||
#define msg_unable_to_save "unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
|
||||
#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
|
||||
#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
|
||||
#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
|
||||
#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
|
||||
|
||||
|
||||
/* debugfs functions for the hotplug controller info */
|
||||
extern void cpqhp_initialize_debugfs (void);
|
||||
extern void cpqhp_shutdown_debugfs (void);
|
||||
extern void cpqhp_create_debugfs_files (struct controller *ctrl);
|
||||
extern void cpqhp_remove_debugfs_files (struct controller *ctrl);
|
||||
|
||||
/* controller functions */
|
||||
extern void cpqhp_pushbutton_thread (unsigned long event_pointer);
|
||||
extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data);
|
||||
extern int cpqhp_find_available_resources (struct controller *ctrl, void __iomem *rom_start);
|
||||
extern int cpqhp_event_start_thread (void);
|
||||
extern void cpqhp_event_stop_thread (void);
|
||||
extern struct pci_func *cpqhp_slot_create (unsigned char busnumber);
|
||||
extern struct pci_func *cpqhp_slot_find (unsigned char bus, unsigned char device, unsigned char index);
|
||||
extern int cpqhp_process_SI (struct controller *ctrl, struct pci_func *func);
|
||||
extern int cpqhp_process_SS (struct controller *ctrl, struct pci_func *func);
|
||||
extern int cpqhp_hardware_test (struct controller *ctrl, int test_num);
|
||||
|
||||
/* resource functions */
|
||||
extern int cpqhp_resource_sort_and_combine (struct pci_resource **head);
|
||||
|
||||
/* pci functions */
|
||||
extern int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
|
||||
extern int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot);
|
||||
extern int cpqhp_save_config (struct controller *ctrl, int busnumber, int is_hot_plug);
|
||||
extern int cpqhp_save_base_addr_length (struct controller *ctrl, struct pci_func * func);
|
||||
extern int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func);
|
||||
extern int cpqhp_configure_board (struct controller *ctrl, struct pci_func * func);
|
||||
extern int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot);
|
||||
extern int cpqhp_valid_replace (struct controller *ctrl, struct pci_func * func);
|
||||
extern void cpqhp_destroy_board_resources (struct pci_func * func);
|
||||
extern int cpqhp_return_board_resources (struct pci_func * func, struct resource_lists * resources);
|
||||
extern void cpqhp_destroy_resource_list (struct resource_lists * resources);
|
||||
extern int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func);
|
||||
extern int cpqhp_unconfigure_device (struct pci_func* func);
|
||||
|
||||
/* Global variables */
|
||||
extern int cpqhp_debug;
|
||||
extern int cpqhp_legacy_mode;
|
||||
extern struct controller *cpqhp_ctrl_list;
|
||||
extern struct pci_func *cpqhp_slot_list[256];
|
||||
|
||||
/* these can be gotten rid of, but for debugging they are purty */
|
||||
extern u8 cpqhp_nic_irq;
|
||||
extern u8 cpqhp_disk_irq;
|
||||
|
||||
|
||||
/* inline functions */
|
||||
|
||||
/*
|
||||
* return_resource
|
||||
*
|
||||
* Puts node back in the resource list pointed to by head
|
||||
*
|
||||
*/
|
||||
static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
|
||||
{
|
||||
if (!node || !head)
|
||||
return;
|
||||
node->next = *head;
|
||||
*head = node;
|
||||
}
|
||||
|
||||
static inline void set_SOGO(struct controller *ctrl)
|
||||
{
|
||||
u16 misc;
|
||||
|
||||
misc = readw(ctrl->hpc_reg + MISC);
|
||||
misc = (misc | 0x0001) & 0xFFFB;
|
||||
writew(misc, ctrl->hpc_reg + MISC);
|
||||
}
|
||||
|
||||
|
||||
static inline void amber_LED_on(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control |= (0x01010000L << slot);
|
||||
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
|
||||
}
|
||||
|
||||
|
||||
static inline void amber_LED_off(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control &= ~(0x01010000L << slot);
|
||||
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
|
||||
}
|
||||
|
||||
|
||||
static inline int read_amber_LED(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control &= (0x01010000L << slot);
|
||||
|
||||
return led_control ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static inline void green_LED_on(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control |= 0x0101L << slot;
|
||||
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
|
||||
}
|
||||
|
||||
static inline void green_LED_off(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control &= ~(0x0101L << slot);
|
||||
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
|
||||
}
|
||||
|
||||
|
||||
static inline void green_LED_blink(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u32 led_control;
|
||||
|
||||
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
|
||||
led_control &= ~(0x0101L << slot);
|
||||
led_control |= (0x0001L << slot);
|
||||
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
|
||||
}
|
||||
|
||||
|
||||
static inline void slot_disable(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u8 slot_enable;
|
||||
|
||||
slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
|
||||
slot_enable &= ~(0x01 << slot);
|
||||
writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE);
|
||||
}
|
||||
|
||||
|
||||
static inline void slot_enable(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u8 slot_enable;
|
||||
|
||||
slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
|
||||
slot_enable |= (0x01 << slot);
|
||||
writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE);
|
||||
}
|
||||
|
||||
|
||||
static inline u8 is_slot_enabled(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u8 slot_enable;
|
||||
|
||||
slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
|
||||
slot_enable &= (0x01 << slot);
|
||||
return slot_enable ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static inline u8 read_slot_enable(struct controller *ctrl)
|
||||
{
|
||||
return readb(ctrl->hpc_reg + SLOT_ENABLE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* get_controller_speed - find the current frequency/mode of controller.
|
||||
*
|
||||
* @ctrl: controller to get frequency/mode for.
|
||||
*
|
||||
* Returns controller speed.
|
||||
*
|
||||
*/
|
||||
static inline u8 get_controller_speed(struct controller *ctrl)
|
||||
{
|
||||
u8 curr_freq;
|
||||
u16 misc;
|
||||
|
||||
if (ctrl->pcix_support) {
|
||||
curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ);
|
||||
if ((curr_freq & 0xB0) == 0xB0)
|
||||
return PCI_SPEED_133MHz_PCIX;
|
||||
if ((curr_freq & 0xA0) == 0xA0)
|
||||
return PCI_SPEED_100MHz_PCIX;
|
||||
if ((curr_freq & 0x90) == 0x90)
|
||||
return PCI_SPEED_66MHz_PCIX;
|
||||
if (curr_freq & 0x10)
|
||||
return PCI_SPEED_66MHz;
|
||||
|
||||
return PCI_SPEED_33MHz;
|
||||
}
|
||||
|
||||
misc = readw(ctrl->hpc_reg + MISC);
|
||||
return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* get_adapter_speed - find the max supported frequency/mode of adapter.
|
||||
*
|
||||
* @ctrl: hotplug controller.
|
||||
* @hp_slot: hotplug slot where adapter is installed.
|
||||
*
|
||||
* Returns adapter speed.
|
||||
*
|
||||
*/
|
||||
static inline u8 get_adapter_speed(struct controller *ctrl, u8 hp_slot)
|
||||
{
|
||||
u32 temp_dword = readl(ctrl->hpc_reg + NON_INT_INPUT);
|
||||
dbg("slot: %d, PCIXCAP: %8x\n", hp_slot, temp_dword);
|
||||
if (ctrl->pcix_support) {
|
||||
if (temp_dword & (0x10000 << hp_slot))
|
||||
return PCI_SPEED_133MHz_PCIX;
|
||||
if (temp_dword & (0x100 << hp_slot))
|
||||
return PCI_SPEED_66MHz_PCIX;
|
||||
}
|
||||
|
||||
if (temp_dword & (0x01 << hp_slot))
|
||||
return PCI_SPEED_66MHz;
|
||||
|
||||
return PCI_SPEED_33MHz;
|
||||
}
|
||||
|
||||
static inline void enable_slot_power(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u8 slot_power;
|
||||
|
||||
slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
|
||||
slot_power |= (0x01 << slot);
|
||||
writeb(slot_power, ctrl->hpc_reg + SLOT_POWER);
|
||||
}
|
||||
|
||||
static inline void disable_slot_power(struct controller *ctrl, u8 slot)
|
||||
{
|
||||
u8 slot_power;
|
||||
|
||||
slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
|
||||
slot_power &= ~(0x01 << slot);
|
||||
writeb(slot_power, ctrl->hpc_reg + SLOT_POWER);
|
||||
}
|
||||
|
||||
|
||||
static inline int cpq_get_attention_status(struct controller *ctrl, struct slot *slot)
|
||||
{
|
||||
u8 hp_slot;
|
||||
|
||||
hp_slot = slot->device - ctrl->slot_device_offset;
|
||||
|
||||
return read_amber_LED(ctrl, hp_slot);
|
||||
}
|
||||
|
||||
|
||||
static inline int get_slot_enabled(struct controller *ctrl, struct slot *slot)
|
||||
{
|
||||
u8 hp_slot;
|
||||
|
||||
hp_slot = slot->device - ctrl->slot_device_offset;
|
||||
|
||||
return is_slot_enabled(ctrl, hp_slot);
|
||||
}
|
||||
|
||||
|
||||
static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slot)
|
||||
{
|
||||
u32 status;
|
||||
u8 hp_slot;
|
||||
|
||||
hp_slot = slot->device - ctrl->slot_device_offset;
|
||||
dbg("%s: slot->device = %d, ctrl->slot_device_offset = %d \n",
|
||||
__FUNCTION__, slot->device, ctrl->slot_device_offset);
|
||||
|
||||
status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot));
|
||||
|
||||
return(status == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static inline int get_presence_status(struct controller *ctrl, struct slot *slot)
|
||||
{
|
||||
int presence_save = 0;
|
||||
u8 hp_slot;
|
||||
u32 tempdword;
|
||||
|
||||
hp_slot = slot->device - ctrl->slot_device_offset;
|
||||
|
||||
tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
|
||||
presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> hp_slot) & 0x02;
|
||||
|
||||
return presence_save;
|
||||
}
|
||||
|
||||
#define SLOT_NAME_SIZE 10
|
||||
|
||||
static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
|
||||
{
|
||||
snprintf(buffer, buffer_size, "%d", slot->number);
|
||||
}
|
||||
|
||||
|
||||
static inline int wait_for_ctrl_irq(struct controller *ctrl)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - start\n", __FUNCTION__);
|
||||
add_wait_queue(&ctrl->queue, &wait);
|
||||
/* Sleep for up to 1 second to wait for the LED to change. */
|
||||
msleep_interruptible(1000);
|
||||
remove_wait_queue(&ctrl->queue, &wait);
|
||||
if (signal_pending(current))
|
||||
retval = -EINTR;
|
||||
|
||||
dbg("%s - end\n", __FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
1536
drivers/pci/hotplug/cpqphp_core.c
Normal file
1536
drivers/pci/hotplug/cpqphp_core.c
Normal file
File diff suppressed because it is too large
Load Diff
3034
drivers/pci/hotplug/cpqphp_ctrl.c
Normal file
3034
drivers/pci/hotplug/cpqphp_ctrl.c
Normal file
File diff suppressed because it is too large
Load Diff
666
drivers/pci/hotplug/cpqphp_nvram.c
Normal file
666
drivers/pci/hotplug/cpqphp_nvram.c
Normal file
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* Compaq Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
*
|
||||
* 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 <greg@kroah.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "cpqphp.h"
|
||||
#include "cpqphp_nvram.h"
|
||||
|
||||
|
||||
#define ROM_INT15_PHY_ADDR 0x0FF859
|
||||
#define READ_EV 0xD8A4
|
||||
#define WRITE_EV 0xD8A5
|
||||
|
||||
struct register_foo {
|
||||
union {
|
||||
unsigned long lword; /* eax */
|
||||
unsigned short word; /* ax */
|
||||
|
||||
struct {
|
||||
unsigned char low; /* al */
|
||||
unsigned char high; /* ah */
|
||||
} byte;
|
||||
} data;
|
||||
|
||||
unsigned char opcode; /* see below */
|
||||
unsigned long length; /* if the reg. is a pointer, how much data */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct all_reg {
|
||||
struct register_foo eax_reg;
|
||||
struct register_foo ebx_reg;
|
||||
struct register_foo ecx_reg;
|
||||
struct register_foo edx_reg;
|
||||
struct register_foo edi_reg;
|
||||
struct register_foo esi_reg;
|
||||
struct register_foo eflags_reg;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
struct ev_hrt_header {
|
||||
u8 Version;
|
||||
u8 num_of_ctrl;
|
||||
u8 next;
|
||||
};
|
||||
|
||||
struct ev_hrt_ctrl {
|
||||
u8 bus;
|
||||
u8 device;
|
||||
u8 function;
|
||||
u8 mem_avail;
|
||||
u8 p_mem_avail;
|
||||
u8 io_avail;
|
||||
u8 bus_avail;
|
||||
u8 next;
|
||||
};
|
||||
|
||||
|
||||
static u8 evbuffer_init;
|
||||
static u8 evbuffer_length;
|
||||
static u8 evbuffer[1024];
|
||||
|
||||
static void __iomem *compaq_int15_entry_point;
|
||||
|
||||
static spinlock_t int15_lock; /* lock for ordering int15_bios_call() */
|
||||
|
||||
|
||||
/* This is a series of function that deals with
|
||||
setting & getting the hotplug resource table in some environment variable.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We really shouldn't be doing this unless there is a _very_ good reason to!!!
|
||||
* greg k-h
|
||||
*/
|
||||
|
||||
|
||||
static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail)
|
||||
{
|
||||
u8 **tByte;
|
||||
|
||||
if ((*used + 1) > *avail)
|
||||
return(1);
|
||||
|
||||
*((u8*)*p_buffer) = value;
|
||||
tByte = (u8**)p_buffer;
|
||||
(*tByte)++;
|
||||
*used+=1;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static u32 add_dword( u32 **p_buffer, u32 value, u32 *used, u32 *avail)
|
||||
{
|
||||
if ((*used + 4) > *avail)
|
||||
return(1);
|
||||
|
||||
**p_buffer = value;
|
||||
(*p_buffer)++;
|
||||
*used+=4;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* check_for_compaq_ROM
|
||||
*
|
||||
* this routine verifies that the ROM OEM string is 'COMPAQ'
|
||||
*
|
||||
* returns 0 for non-Compaq ROM, 1 for Compaq ROM
|
||||
*/
|
||||
static int check_for_compaq_ROM (void __iomem *rom_start)
|
||||
{
|
||||
u8 temp1, temp2, temp3, temp4, temp5, temp6;
|
||||
int result = 0;
|
||||
|
||||
temp1 = readb(rom_start + 0xffea + 0);
|
||||
temp2 = readb(rom_start + 0xffea + 1);
|
||||
temp3 = readb(rom_start + 0xffea + 2);
|
||||
temp4 = readb(rom_start + 0xffea + 3);
|
||||
temp5 = readb(rom_start + 0xffea + 4);
|
||||
temp6 = readb(rom_start + 0xffea + 5);
|
||||
if ((temp1 == 'C') &&
|
||||
(temp2 == 'O') &&
|
||||
(temp3 == 'M') &&
|
||||
(temp4 == 'P') &&
|
||||
(temp5 == 'A') &&
|
||||
(temp6 == 'Q')) {
|
||||
result = 1;
|
||||
}
|
||||
dbg ("%s - returned %d\n", __FUNCTION__, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
|
||||
{
|
||||
unsigned long flags;
|
||||
int op = operation;
|
||||
int ret_val;
|
||||
|
||||
if (!compaq_int15_entry_point)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&int15_lock, flags);
|
||||
__asm__ (
|
||||
"xorl %%ebx,%%ebx\n" \
|
||||
"xorl %%edx,%%edx\n" \
|
||||
"pushf\n" \
|
||||
"push %%cs\n" \
|
||||
"cli\n" \
|
||||
"call *%6\n"
|
||||
: "=c" (*buf_size), "=a" (ret_val)
|
||||
: "a" (op), "c" (*buf_size), "S" (ev_name),
|
||||
"D" (buffer), "m" (compaq_int15_entry_point)
|
||||
: "%ebx", "%edx");
|
||||
spin_unlock_irqrestore(&int15_lock, flags);
|
||||
|
||||
return((ret_val & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* load_HRT
|
||||
*
|
||||
* Read the hot plug Resource Table from NVRAM
|
||||
*/
|
||||
static int load_HRT (void __iomem *rom_start)
|
||||
{
|
||||
u32 available;
|
||||
u32 temp_dword;
|
||||
u8 temp_byte = 0xFF;
|
||||
u32 rc;
|
||||
|
||||
if (!check_for_compaq_ROM(rom_start)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
available = 1024;
|
||||
|
||||
// Now load the EV
|
||||
temp_dword = available;
|
||||
|
||||
rc = access_EV(READ_EV, "CQTHPS", evbuffer, &temp_dword);
|
||||
|
||||
evbuffer_length = temp_dword;
|
||||
|
||||
// We're maintaining the resource lists so write FF to invalidate old info
|
||||
temp_dword = 1;
|
||||
|
||||
rc = access_EV(WRITE_EV, "CQTHPS", &temp_byte, &temp_dword);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* store_HRT
|
||||
*
|
||||
* Save the hot plug Resource Table in NVRAM
|
||||
*/
|
||||
static u32 store_HRT (void __iomem *rom_start)
|
||||
{
|
||||
u32 *buffer;
|
||||
u32 *pFill;
|
||||
u32 usedbytes;
|
||||
u32 available;
|
||||
u32 temp_dword;
|
||||
u32 rc;
|
||||
u8 loop;
|
||||
u8 numCtrl = 0;
|
||||
struct controller *ctrl;
|
||||
struct pci_resource *resNode;
|
||||
struct ev_hrt_header *p_EV_header;
|
||||
struct ev_hrt_ctrl *p_ev_ctrl;
|
||||
|
||||
available = 1024;
|
||||
|
||||
if (!check_for_compaq_ROM(rom_start)) {
|
||||
return(1);
|
||||
}
|
||||
|
||||
buffer = (u32*) evbuffer;
|
||||
|
||||
if (!buffer)
|
||||
return(1);
|
||||
|
||||
pFill = buffer;
|
||||
usedbytes = 0;
|
||||
|
||||
p_EV_header = (struct ev_hrt_header *) pFill;
|
||||
|
||||
ctrl = cpqhp_ctrl_list;
|
||||
|
||||
// The revision of this structure
|
||||
rc = add_byte( &pFill, 1 + ctrl->push_flag, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// The number of controllers
|
||||
rc = add_byte( &pFill, 1, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
while (ctrl) {
|
||||
p_ev_ctrl = (struct ev_hrt_ctrl *) pFill;
|
||||
|
||||
numCtrl++;
|
||||
|
||||
// The bus number
|
||||
rc = add_byte( &pFill, ctrl->bus, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// The device Number
|
||||
rc = add_byte( &pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// The function Number
|
||||
rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// Skip the number of available entries
|
||||
rc = add_dword( &pFill, 0, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// Figure out memory Available
|
||||
|
||||
resNode = ctrl->mem_head;
|
||||
|
||||
loop = 0;
|
||||
|
||||
while (resNode) {
|
||||
loop ++;
|
||||
|
||||
// base
|
||||
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// length
|
||||
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
resNode = resNode->next;
|
||||
}
|
||||
|
||||
// Fill in the number of entries
|
||||
p_ev_ctrl->mem_avail = loop;
|
||||
|
||||
// Figure out prefetchable memory Available
|
||||
|
||||
resNode = ctrl->p_mem_head;
|
||||
|
||||
loop = 0;
|
||||
|
||||
while (resNode) {
|
||||
loop ++;
|
||||
|
||||
// base
|
||||
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// length
|
||||
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
resNode = resNode->next;
|
||||
}
|
||||
|
||||
// Fill in the number of entries
|
||||
p_ev_ctrl->p_mem_avail = loop;
|
||||
|
||||
// Figure out IO Available
|
||||
|
||||
resNode = ctrl->io_head;
|
||||
|
||||
loop = 0;
|
||||
|
||||
while (resNode) {
|
||||
loop ++;
|
||||
|
||||
// base
|
||||
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// length
|
||||
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
resNode = resNode->next;
|
||||
}
|
||||
|
||||
// Fill in the number of entries
|
||||
p_ev_ctrl->io_avail = loop;
|
||||
|
||||
// Figure out bus Available
|
||||
|
||||
resNode = ctrl->bus_head;
|
||||
|
||||
loop = 0;
|
||||
|
||||
while (resNode) {
|
||||
loop ++;
|
||||
|
||||
// base
|
||||
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
// length
|
||||
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
|
||||
if (rc)
|
||||
return(rc);
|
||||
|
||||
resNode = resNode->next;
|
||||
}
|
||||
|
||||
// Fill in the number of entries
|
||||
p_ev_ctrl->bus_avail = loop;
|
||||
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
||||
p_EV_header->num_of_ctrl = numCtrl;
|
||||
|
||||
// Now store the EV
|
||||
|
||||
temp_dword = usedbytes;
|
||||
|
||||
rc = access_EV(WRITE_EV, "CQTHPS", (u8*) buffer, &temp_dword);
|
||||
|
||||
dbg("usedbytes = 0x%x, length = 0x%x\n", usedbytes, temp_dword);
|
||||
|
||||
evbuffer_length = temp_dword;
|
||||
|
||||
if (rc) {
|
||||
err(msg_unable_to_save);
|
||||
return(1);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
void compaq_nvram_init (void __iomem *rom_start)
|
||||
{
|
||||
if (rom_start) {
|
||||
compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR);
|
||||
}
|
||||
dbg("int15 entry = %p\n", compaq_int15_entry_point);
|
||||
|
||||
/* initialize our int15 lock */
|
||||
spin_lock_init(&int15_lock);
|
||||
}
|
||||
|
||||
|
||||
int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
|
||||
{
|
||||
u8 bus, device, function;
|
||||
u8 nummem, numpmem, numio, numbus;
|
||||
u32 rc;
|
||||
u8 *p_byte;
|
||||
struct pci_resource *mem_node;
|
||||
struct pci_resource *p_mem_node;
|
||||
struct pci_resource *io_node;
|
||||
struct pci_resource *bus_node;
|
||||
struct ev_hrt_ctrl *p_ev_ctrl;
|
||||
struct ev_hrt_header *p_EV_header;
|
||||
|
||||
if (!evbuffer_init) {
|
||||
// Read the resource list information in from NVRAM
|
||||
if (load_HRT(rom_start))
|
||||
memset (evbuffer, 0, 1024);
|
||||
|
||||
evbuffer_init = 1;
|
||||
}
|
||||
|
||||
// If we saved information in NVRAM, use it now
|
||||
p_EV_header = (struct ev_hrt_header *) evbuffer;
|
||||
|
||||
// The following code is for systems where version 1.0 of this
|
||||
// driver has been loaded, but doesn't support the hardware.
|
||||
// In that case, the driver would incorrectly store something
|
||||
// in NVRAM.
|
||||
if ((p_EV_header->Version == 2) ||
|
||||
((p_EV_header->Version == 1) && !ctrl->push_flag)) {
|
||||
p_byte = &(p_EV_header->next);
|
||||
|
||||
p_ev_ctrl = (struct ev_hrt_ctrl *) &(p_EV_header->next);
|
||||
|
||||
p_byte += 3;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
|
||||
return 2;
|
||||
|
||||
bus = p_ev_ctrl->bus;
|
||||
device = p_ev_ctrl->device;
|
||||
function = p_ev_ctrl->function;
|
||||
|
||||
while ((bus != ctrl->bus) ||
|
||||
(device != PCI_SLOT(ctrl->pci_dev->devfn)) ||
|
||||
(function != PCI_FUNC(ctrl->pci_dev->devfn))) {
|
||||
nummem = p_ev_ctrl->mem_avail;
|
||||
numpmem = p_ev_ctrl->p_mem_avail;
|
||||
numio = p_ev_ctrl->io_avail;
|
||||
numbus = p_ev_ctrl->bus_avail;
|
||||
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
|
||||
return 2;
|
||||
|
||||
// Skip forward to the next entry
|
||||
p_byte += (nummem + numpmem + numio + numbus) * 8;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
|
||||
return 2;
|
||||
|
||||
p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte;
|
||||
|
||||
p_byte += 3;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
|
||||
return 2;
|
||||
|
||||
bus = p_ev_ctrl->bus;
|
||||
device = p_ev_ctrl->device;
|
||||
function = p_ev_ctrl->function;
|
||||
}
|
||||
|
||||
nummem = p_ev_ctrl->mem_avail;
|
||||
numpmem = p_ev_ctrl->p_mem_avail;
|
||||
numio = p_ev_ctrl->io_avail;
|
||||
numbus = p_ev_ctrl->bus_avail;
|
||||
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
|
||||
return 2;
|
||||
|
||||
while (nummem--) {
|
||||
mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
|
||||
|
||||
if (!mem_node)
|
||||
break;
|
||||
|
||||
mem_node->base = *(u32*)p_byte;
|
||||
dbg("mem base = %8.8x\n",mem_node->base);
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
|
||||
kfree(mem_node);
|
||||
return 2;
|
||||
}
|
||||
|
||||
mem_node->length = *(u32*)p_byte;
|
||||
dbg("mem length = %8.8x\n",mem_node->length);
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
|
||||
kfree(mem_node);
|
||||
return 2;
|
||||
}
|
||||
|
||||
mem_node->next = ctrl->mem_head;
|
||||
ctrl->mem_head = mem_node;
|
||||
}
|
||||
|
||||
while (numpmem--) {
|
||||
p_mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
|
||||
|
||||
if (!p_mem_node)
|
||||
break;
|
||||
|
||||
p_mem_node->base = *(u32*)p_byte;
|
||||
dbg("pre-mem base = %8.8x\n",p_mem_node->base);
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
|
||||
kfree(p_mem_node);
|
||||
return 2;
|
||||
}
|
||||
|
||||
p_mem_node->length = *(u32*)p_byte;
|
||||
dbg("pre-mem length = %8.8x\n",p_mem_node->length);
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
|
||||
kfree(p_mem_node);
|
||||
return 2;
|
||||
}
|
||||
|
||||
p_mem_node->next = ctrl->p_mem_head;
|
||||
ctrl->p_mem_head = p_mem_node;
|
||||
}
|
||||
|
||||
while (numio--) {
|
||||
io_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
|
||||
|
||||
if (!io_node)
|
||||
break;
|
||||
|
||||
io_node->base = *(u32*)p_byte;
|
||||
dbg("io base = %8.8x\n",io_node->base);
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
|
||||
kfree(io_node);
|
||||
return 2;
|
||||
}
|
||||
|
||||
io_node->length = *(u32*)p_byte;
|
||||
dbg("io length = %8.8x\n",io_node->length);
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
|
||||
kfree(io_node);
|
||||
return 2;
|
||||
}
|
||||
|
||||
io_node->next = ctrl->io_head;
|
||||
ctrl->io_head = io_node;
|
||||
}
|
||||
|
||||
while (numbus--) {
|
||||
bus_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
|
||||
|
||||
if (!bus_node)
|
||||
break;
|
||||
|
||||
bus_node->base = *(u32*)p_byte;
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
|
||||
kfree(bus_node);
|
||||
return 2;
|
||||
}
|
||||
|
||||
bus_node->length = *(u32*)p_byte;
|
||||
p_byte += 4;
|
||||
|
||||
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
|
||||
kfree(bus_node);
|
||||
return 2;
|
||||
}
|
||||
|
||||
bus_node->next = ctrl->bus_head;
|
||||
ctrl->bus_head = bus_node;
|
||||
}
|
||||
|
||||
// If all of the following fail, we don't have any resources for
|
||||
// hot plug add
|
||||
rc = 1;
|
||||
rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
|
||||
rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
|
||||
rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head));
|
||||
rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
|
||||
|
||||
if (rc)
|
||||
return(rc);
|
||||
} else {
|
||||
if ((evbuffer[0] != 0) && (!ctrl->push_flag))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int compaq_nvram_store (void __iomem *rom_start)
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
if (rom_start == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (evbuffer_init) {
|
||||
rc = store_HRT(rom_start);
|
||||
if (rc) {
|
||||
err(msg_unable_to_save);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
57
drivers/pci/hotplug/cpqphp_nvram.h
Normal file
57
drivers/pci/hotplug/cpqphp_nvram.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Compaq Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
*
|
||||
* 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 <greg@kroah.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _CPQPHP_NVRAM_H
|
||||
#define _CPQPHP_NVRAM_H
|
||||
|
||||
#ifndef CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM
|
||||
|
||||
static inline void compaq_nvram_init (void __iomem *rom_start)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int compaq_nvram_store (void __iomem *rom_start)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
extern void compaq_nvram_init (void __iomem *rom_start);
|
||||
extern int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl);
|
||||
extern int compaq_nvram_store (void __iomem *rom_start);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
1564
drivers/pci/hotplug/cpqphp_pci.c
Normal file
1564
drivers/pci/hotplug/cpqphp_pci.c
Normal file
File diff suppressed because it is too large
Load Diff
237
drivers/pci/hotplug/cpqphp_sysfs.c
Normal file
237
drivers/pci/hotplug/cpqphp_sysfs.c
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Compaq Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
*
|
||||
* 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 <greg@kroah.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include "cpqphp.h"
|
||||
|
||||
static int show_ctrl (struct controller *ctrl, char *buf)
|
||||
{
|
||||
char *out = buf;
|
||||
int index;
|
||||
struct pci_resource *res;
|
||||
|
||||
out += sprintf(buf, "Free resources: memory\n");
|
||||
index = 11;
|
||||
res = ctrl->mem_head;
|
||||
while (res && index--) {
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
|
||||
res = res->next;
|
||||
}
|
||||
out += sprintf(out, "Free resources: prefetchable memory\n");
|
||||
index = 11;
|
||||
res = ctrl->p_mem_head;
|
||||
while (res && index--) {
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
|
||||
res = res->next;
|
||||
}
|
||||
out += sprintf(out, "Free resources: IO\n");
|
||||
index = 11;
|
||||
res = ctrl->io_head;
|
||||
while (res && index--) {
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
|
||||
res = res->next;
|
||||
}
|
||||
out += sprintf(out, "Free resources: bus numbers\n");
|
||||
index = 11;
|
||||
res = ctrl->bus_head;
|
||||
while (res && index--) {
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
|
||||
res = res->next;
|
||||
}
|
||||
|
||||
return out - buf;
|
||||
}
|
||||
|
||||
static int show_dev (struct controller *ctrl, char *buf)
|
||||
{
|
||||
char * out = buf;
|
||||
int index;
|
||||
struct pci_resource *res;
|
||||
struct pci_func *new_slot;
|
||||
struct slot *slot;
|
||||
|
||||
slot = ctrl->slot;
|
||||
|
||||
while (slot) {
|
||||
new_slot = cpqhp_slot_find(slot->bus, slot->device, 0);
|
||||
if (!new_slot)
|
||||
break;
|
||||
out += sprintf(out, "assigned resources: memory\n");
|
||||
index = 11;
|
||||
res = new_slot->mem_head;
|
||||
while (res && index--) {
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
|
||||
res = res->next;
|
||||
}
|
||||
out += sprintf(out, "assigned resources: prefetchable memory\n");
|
||||
index = 11;
|
||||
res = new_slot->p_mem_head;
|
||||
while (res && index--) {
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
|
||||
res = res->next;
|
||||
}
|
||||
out += sprintf(out, "assigned resources: IO\n");
|
||||
index = 11;
|
||||
res = new_slot->io_head;
|
||||
while (res && index--) {
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
|
||||
res = res->next;
|
||||
}
|
||||
out += sprintf(out, "assigned resources: bus numbers\n");
|
||||
index = 11;
|
||||
res = new_slot->bus_head;
|
||||
while (res && index--) {
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
|
||||
res = res->next;
|
||||
}
|
||||
slot=slot->next;
|
||||
}
|
||||
|
||||
return out - buf;
|
||||
}
|
||||
|
||||
static int spew_debug_info(struct controller *ctrl, char *data, int size)
|
||||
{
|
||||
int used;
|
||||
|
||||
used = size - show_ctrl(ctrl, data);
|
||||
used = (size - used) - show_dev(ctrl, &data[used]);
|
||||
return used;
|
||||
}
|
||||
|
||||
struct ctrl_dbg {
|
||||
int size;
|
||||
char *data;
|
||||
struct controller *ctrl;
|
||||
};
|
||||
|
||||
#define MAX_OUTPUT (4*PAGE_SIZE)
|
||||
|
||||
static int open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct controller *ctrl = inode->i_private;
|
||||
struct ctrl_dbg *dbg;
|
||||
int retval = -ENOMEM;
|
||||
|
||||
lock_kernel();
|
||||
dbg = kmalloc(sizeof(*dbg), GFP_KERNEL);
|
||||
if (!dbg)
|
||||
goto exit;
|
||||
dbg->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
|
||||
if (!dbg->data) {
|
||||
kfree(dbg);
|
||||
goto exit;
|
||||
}
|
||||
dbg->size = spew_debug_info(ctrl, dbg->data, MAX_OUTPUT);
|
||||
file->private_data = dbg;
|
||||
retval = 0;
|
||||
exit:
|
||||
unlock_kernel();
|
||||
return retval;
|
||||
}
|
||||
|
||||
static loff_t lseek(struct file *file, loff_t off, int whence)
|
||||
{
|
||||
struct ctrl_dbg *dbg;
|
||||
loff_t new = -1;
|
||||
|
||||
lock_kernel();
|
||||
dbg = file->private_data;
|
||||
|
||||
switch (whence) {
|
||||
case 0:
|
||||
new = off;
|
||||
break;
|
||||
case 1:
|
||||
new = file->f_pos + off;
|
||||
break;
|
||||
}
|
||||
if (new < 0 || new > dbg->size) {
|
||||
unlock_kernel();
|
||||
return -EINVAL;
|
||||
}
|
||||
unlock_kernel();
|
||||
return (file->f_pos = new);
|
||||
}
|
||||
|
||||
static ssize_t read(struct file *file, char __user *buf,
|
||||
size_t nbytes, loff_t *ppos)
|
||||
{
|
||||
struct ctrl_dbg *dbg = file->private_data;
|
||||
return simple_read_from_buffer(buf, nbytes, ppos, dbg->data, dbg->size);
|
||||
}
|
||||
|
||||
static int release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ctrl_dbg *dbg = file->private_data;
|
||||
|
||||
kfree(dbg->data);
|
||||
kfree(dbg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations debug_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = open,
|
||||
.llseek = lseek,
|
||||
.read = read,
|
||||
.release = release,
|
||||
};
|
||||
|
||||
static struct dentry *root;
|
||||
|
||||
void cpqhp_initialize_debugfs(void)
|
||||
{
|
||||
if (!root)
|
||||
root = debugfs_create_dir("cpqhp", NULL);
|
||||
}
|
||||
|
||||
void cpqhp_shutdown_debugfs(void)
|
||||
{
|
||||
debugfs_remove(root);
|
||||
}
|
||||
|
||||
void cpqhp_create_debugfs_files(struct controller *ctrl)
|
||||
{
|
||||
ctrl->dentry = debugfs_create_file(ctrl->pci_dev->dev.bus_id, S_IRUGO, root, ctrl, &debug_ops);
|
||||
}
|
||||
|
||||
void cpqhp_remove_debugfs_files(struct controller *ctrl)
|
||||
{
|
||||
if (ctrl->dentry)
|
||||
debugfs_remove(ctrl->dentry);
|
||||
ctrl->dentry = NULL;
|
||||
}
|
||||
|
||||
371
drivers/pci/hotplug/fakephp.c
Normal file
371
drivers/pci/hotplug/fakephp.c
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Fake PCI Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
|
||||
* Copyright (C) 2003 IBM Corp.
|
||||
* Copyright (C) 2003 Rolf Eike Beer <eike-kernel@sf-tec.de>
|
||||
*
|
||||
* Based on ideas and code from:
|
||||
* Vladimir Kondratiev <vladimir.kondratiev@intel.com>
|
||||
* Rolf Eike Beer <eike-kernel@sf-tec.de>
|
||||
*
|
||||
* 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, version 2 of the License.
|
||||
*
|
||||
* Send feedback to <greg@kroah.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* This driver will "emulate" removing PCI devices from the system. If
|
||||
* the "power" file is written to with "0" then the specified PCI device
|
||||
* will be completely removed from the kernel.
|
||||
*
|
||||
* WARNING, this does NOT turn off the power to the PCI device. This is
|
||||
* a "logical" removal, not a physical or electrical removal.
|
||||
*
|
||||
* Use this module at your own risk, you have been warned!
|
||||
*
|
||||
* Enabling PCI devices is left as an exercise for the reader...
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include "../pci.h"
|
||||
|
||||
#if !defined(MODULE)
|
||||
#define MY_NAME "fakephp"
|
||||
#else
|
||||
#define MY_NAME THIS_MODULE->name
|
||||
#endif
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (debug) \
|
||||
printk(KERN_DEBUG "%s: " format, \
|
||||
MY_NAME , ## arg); \
|
||||
} while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
|
||||
|
||||
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
|
||||
#define DRIVER_DESC "Fake PCI Hot Plug Controller Driver"
|
||||
|
||||
struct dummy_slot {
|
||||
struct list_head node;
|
||||
struct hotplug_slot *slot;
|
||||
struct pci_dev *dev;
|
||||
};
|
||||
|
||||
static int debug;
|
||||
static LIST_HEAD(slot_list);
|
||||
|
||||
static int enable_slot (struct hotplug_slot *slot);
|
||||
static int disable_slot (struct hotplug_slot *slot);
|
||||
|
||||
static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
};
|
||||
|
||||
static void dummy_release(struct hotplug_slot *slot)
|
||||
{
|
||||
struct dummy_slot *dslot = slot->private;
|
||||
|
||||
list_del(&dslot->node);
|
||||
kfree(dslot->slot->info);
|
||||
kfree(dslot->slot);
|
||||
pci_dev_put(dslot->dev);
|
||||
kfree(dslot);
|
||||
}
|
||||
|
||||
static int add_slot(struct pci_dev *dev)
|
||||
{
|
||||
struct dummy_slot *dslot;
|
||||
struct hotplug_slot *slot;
|
||||
int retval = -ENOMEM;
|
||||
|
||||
slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
|
||||
if (!slot)
|
||||
goto error;
|
||||
|
||||
slot->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
|
||||
if (!slot->info)
|
||||
goto error_slot;
|
||||
|
||||
slot->info->power_status = 1;
|
||||
slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
|
||||
slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
|
||||
|
||||
slot->name = &dev->dev.bus_id[0];
|
||||
dbg("slot->name = %s\n", slot->name);
|
||||
|
||||
dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
|
||||
if (!dslot)
|
||||
goto error_info;
|
||||
|
||||
slot->ops = &dummy_hotplug_slot_ops;
|
||||
slot->release = &dummy_release;
|
||||
slot->private = dslot;
|
||||
|
||||
retval = pci_hp_register(slot);
|
||||
if (retval) {
|
||||
err("pci_hp_register failed with error %d\n", retval);
|
||||
goto error_dslot;
|
||||
}
|
||||
|
||||
dslot->slot = slot;
|
||||
dslot->dev = pci_dev_get(dev);
|
||||
list_add (&dslot->node, &slot_list);
|
||||
return retval;
|
||||
|
||||
error_dslot:
|
||||
kfree(dslot);
|
||||
error_info:
|
||||
kfree(slot->info);
|
||||
error_slot:
|
||||
kfree(slot);
|
||||
error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __init pci_scan_buses(void)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
int retval = 0;
|
||||
|
||||
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
|
||||
retval = add_slot(dev);
|
||||
if (retval) {
|
||||
pci_dev_put(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void remove_slot(struct dummy_slot *dslot)
|
||||
{
|
||||
int retval;
|
||||
|
||||
dbg("removing slot %s\n", dslot->slot->name);
|
||||
retval = pci_hp_deregister(dslot->slot);
|
||||
if (retval)
|
||||
err("Problem unregistering a slot %s\n", dslot->slot->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rescan slot.
|
||||
* Tries hard not to re-enable already existing devices
|
||||
* also handles scanning of subfunctions
|
||||
*
|
||||
* @param temp Device template. Should be set: bus and devfn.
|
||||
*/
|
||||
static void pci_rescan_slot(struct pci_dev *temp)
|
||||
{
|
||||
struct pci_bus *bus = temp->bus;
|
||||
struct pci_dev *dev;
|
||||
int func;
|
||||
int retval;
|
||||
u8 hdr_type;
|
||||
|
||||
if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
|
||||
temp->hdr_type = hdr_type & 0x7f;
|
||||
if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
|
||||
pci_dev_put(dev);
|
||||
else {
|
||||
dev = pci_scan_single_device(bus, temp->devfn);
|
||||
if (dev) {
|
||||
dbg("New device on %s function %x:%x\n",
|
||||
bus->name, temp->devfn >> 3,
|
||||
temp->devfn & 7);
|
||||
retval = pci_bus_add_device(dev);
|
||||
if (retval)
|
||||
dev_err(&dev->dev, "error adding "
|
||||
"device, continuing.\n");
|
||||
else
|
||||
add_slot(dev);
|
||||
}
|
||||
}
|
||||
/* multifunction device? */
|
||||
if (!(hdr_type & 0x80))
|
||||
return;
|
||||
|
||||
/* continue scanning for other functions */
|
||||
for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) {
|
||||
if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
|
||||
continue;
|
||||
temp->hdr_type = hdr_type & 0x7f;
|
||||
|
||||
if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
|
||||
pci_dev_put(dev);
|
||||
else {
|
||||
dev = pci_scan_single_device(bus, temp->devfn);
|
||||
if (dev) {
|
||||
dbg("New device on %s function %x:%x\n",
|
||||
bus->name, temp->devfn >> 3,
|
||||
temp->devfn & 7);
|
||||
retval = pci_bus_add_device(dev);
|
||||
if (retval)
|
||||
dev_err(&dev->dev, "error adding "
|
||||
"device, continuing.\n");
|
||||
else
|
||||
add_slot(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rescan PCI bus.
|
||||
* call pci_rescan_slot for each possible function of the bus
|
||||
*
|
||||
* @param bus
|
||||
*/
|
||||
static void pci_rescan_bus(const struct pci_bus *bus)
|
||||
{
|
||||
unsigned int devfn;
|
||||
struct pci_dev *dev;
|
||||
dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
dev->bus = (struct pci_bus*)bus;
|
||||
dev->sysdata = bus->sysdata;
|
||||
for (devfn = 0; devfn < 0x100; devfn += 8) {
|
||||
dev->devfn = devfn;
|
||||
pci_rescan_slot(dev);
|
||||
}
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
/* recursively scan all buses */
|
||||
static void pci_rescan_buses(const struct list_head *list)
|
||||
{
|
||||
const struct list_head *l;
|
||||
list_for_each(l,list) {
|
||||
const struct pci_bus *b = pci_bus_b(l);
|
||||
pci_rescan_bus(b);
|
||||
pci_rescan_buses(&b->children);
|
||||
}
|
||||
}
|
||||
|
||||
/* initiate rescan of all pci buses */
|
||||
static inline void pci_rescan(void) {
|
||||
pci_rescan_buses(&pci_root_buses);
|
||||
}
|
||||
|
||||
|
||||
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
/* mis-use enable_slot for rescanning of the pci bus */
|
||||
pci_rescan();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* find the hotplug_slot for the pci_dev */
|
||||
static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
|
||||
{
|
||||
struct dummy_slot *dslot;
|
||||
|
||||
list_for_each_entry(dslot, &slot_list, node) {
|
||||
if (dslot->dev == dev)
|
||||
return dslot->slot;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int disable_slot(struct hotplug_slot *slot)
|
||||
{
|
||||
struct dummy_slot *dslot;
|
||||
struct hotplug_slot *hslot;
|
||||
struct pci_dev *dev;
|
||||
int func;
|
||||
|
||||
if (!slot)
|
||||
return -ENODEV;
|
||||
dslot = slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name);
|
||||
|
||||
/* don't disable bridged devices just yet, we can't handle them easily... */
|
||||
if (dslot->dev->subordinate) {
|
||||
err("Can't remove PCI devices with other PCI devices behind it yet.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* search for subfunctions and disable them first */
|
||||
if (!(dslot->dev->devfn & 7)) {
|
||||
for (func = 1; func < 8; func++) {
|
||||
dev = pci_get_slot(dslot->dev->bus,
|
||||
dslot->dev->devfn + func);
|
||||
if (dev) {
|
||||
hslot = get_slot_from_dev(dev);
|
||||
if (hslot)
|
||||
disable_slot(hslot);
|
||||
else {
|
||||
err("Hotplug slot not found for subfunction of PCI device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
pci_dev_put(dev);
|
||||
} else
|
||||
dbg("No device in slot found\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* remove the device from the pci core */
|
||||
pci_remove_bus_device(dslot->dev);
|
||||
|
||||
/* blow away this sysfs entry and other parts. */
|
||||
remove_slot(dslot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cleanup_slots (void)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct list_head *next;
|
||||
struct dummy_slot *dslot;
|
||||
|
||||
list_for_each_safe (tmp, next, &slot_list) {
|
||||
dslot = list_entry (tmp, struct dummy_slot, node);
|
||||
remove_slot(dslot);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int __init dummyphp_init(void)
|
||||
{
|
||||
info(DRIVER_DESC "\n");
|
||||
|
||||
return pci_scan_buses();
|
||||
}
|
||||
|
||||
|
||||
static void __exit dummyphp_exit(void)
|
||||
{
|
||||
cleanup_slots();
|
||||
}
|
||||
|
||||
module_init(dummyphp_init);
|
||||
module_exit(dummyphp_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
||||
|
||||
761
drivers/pci/hotplug/ibmphp.h
Normal file
761
drivers/pci/hotplug/ibmphp.h
Normal file
@@ -0,0 +1,761 @@
|
||||
#ifndef __IBMPHP_H
|
||||
#define __IBMPHP_H
|
||||
|
||||
/*
|
||||
* IBM Hot Plug Controller Driver
|
||||
*
|
||||
* Written By: Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
|
||||
*
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001-2003 IBM Corp.
|
||||
*
|
||||
* 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 <gregkh@us.ibm.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/pci_hotplug.h>
|
||||
|
||||
extern int ibmphp_debug;
|
||||
|
||||
#if !defined(MODULE)
|
||||
#define MY_NAME "ibmphpd"
|
||||
#else
|
||||
#define MY_NAME THIS_MODULE->name
|
||||
#endif
|
||||
#define debug(fmt, arg...) do { if (ibmphp_debug == 1) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
|
||||
#define debug_pci(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
|
||||
|
||||
|
||||
/* EBDA stuff */
|
||||
|
||||
/***********************************************************
|
||||
* SLOT CAPABILITY *
|
||||
***********************************************************/
|
||||
|
||||
#define EBDA_SLOT_133_MAX 0x20
|
||||
#define EBDA_SLOT_100_MAX 0x10
|
||||
#define EBDA_SLOT_66_MAX 0x02
|
||||
#define EBDA_SLOT_PCIX_CAP 0x08
|
||||
|
||||
|
||||
/************************************************************
|
||||
* RESOURE TYPE *
|
||||
************************************************************/
|
||||
|
||||
#define EBDA_RSRC_TYPE_MASK 0x03
|
||||
#define EBDA_IO_RSRC_TYPE 0x00
|
||||
#define EBDA_MEM_RSRC_TYPE 0x01
|
||||
#define EBDA_PFM_RSRC_TYPE 0x03
|
||||
#define EBDA_RES_RSRC_TYPE 0x02
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* IO RESTRICTION TYPE *
|
||||
*************************************************************/
|
||||
|
||||
#define EBDA_IO_RESTRI_MASK 0x0c
|
||||
#define EBDA_NO_RESTRI 0x00
|
||||
#define EBDA_AVO_VGA_ADDR 0x04
|
||||
#define EBDA_AVO_VGA_ADDR_AND_ALIA 0x08
|
||||
#define EBDA_AVO_ISA_ADDR 0x0c
|
||||
|
||||
|
||||
/**************************************************************
|
||||
* DEVICE TYPE DEF *
|
||||
**************************************************************/
|
||||
|
||||
#define EBDA_DEV_TYPE_MASK 0x10
|
||||
#define EBDA_PCI_DEV 0x10
|
||||
#define EBDA_NON_PCI_DEV 0x00
|
||||
|
||||
|
||||
/***************************************************************
|
||||
* PRIMARY DEF DEFINITION *
|
||||
***************************************************************/
|
||||
|
||||
#define EBDA_PRI_DEF_MASK 0x20
|
||||
#define EBDA_PRI_PCI_BUS_INFO 0x20
|
||||
#define EBDA_NORM_DEV_RSRC_INFO 0x00
|
||||
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// RIO TABLE DATA STRUCTURE
|
||||
//--------------------------------------------------------------
|
||||
|
||||
struct rio_table_hdr {
|
||||
u8 ver_num;
|
||||
u8 scal_count;
|
||||
u8 riodev_count;
|
||||
u16 offset;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// SCALABILITY DETAIL
|
||||
//-------------------------------------------------------------
|
||||
|
||||
struct scal_detail {
|
||||
u8 node_id;
|
||||
u32 cbar;
|
||||
u8 port0_node_connect;
|
||||
u8 port0_port_connect;
|
||||
u8 port1_node_connect;
|
||||
u8 port1_port_connect;
|
||||
u8 port2_node_connect;
|
||||
u8 port2_port_connect;
|
||||
u8 chassis_num;
|
||||
// struct list_head scal_detail_list;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// RIO DETAIL
|
||||
//--------------------------------------------------------------
|
||||
|
||||
struct rio_detail {
|
||||
u8 rio_node_id;
|
||||
u32 bbar;
|
||||
u8 rio_type;
|
||||
u8 owner_id;
|
||||
u8 port0_node_connect;
|
||||
u8 port0_port_connect;
|
||||
u8 port1_node_connect;
|
||||
u8 port1_port_connect;
|
||||
u8 first_slot_num;
|
||||
u8 status;
|
||||
u8 wpindex;
|
||||
u8 chassis_num;
|
||||
struct list_head rio_detail_list;
|
||||
};
|
||||
|
||||
struct opt_rio {
|
||||
u8 rio_type;
|
||||
u8 chassis_num;
|
||||
u8 first_slot_num;
|
||||
u8 middle_num;
|
||||
struct list_head opt_rio_list;
|
||||
};
|
||||
|
||||
struct opt_rio_lo {
|
||||
u8 rio_type;
|
||||
u8 chassis_num;
|
||||
u8 first_slot_num;
|
||||
u8 middle_num;
|
||||
u8 pack_count;
|
||||
struct list_head opt_rio_lo_list;
|
||||
};
|
||||
|
||||
/****************************************************************
|
||||
* HPC DESCRIPTOR NODE *
|
||||
****************************************************************/
|
||||
|
||||
struct ebda_hpc_list {
|
||||
u8 format;
|
||||
u16 num_ctlrs;
|
||||
short phys_addr;
|
||||
// struct list_head ebda_hpc_list;
|
||||
};
|
||||
/*****************************************************************
|
||||
* IN HPC DATA STRUCTURE, THE ASSOCIATED SLOT AND BUS *
|
||||
* STRUCTURE *
|
||||
*****************************************************************/
|
||||
|
||||
struct ebda_hpc_slot {
|
||||
u8 slot_num;
|
||||
u32 slot_bus_num;
|
||||
u8 ctl_index;
|
||||
u8 slot_cap;
|
||||
};
|
||||
|
||||
struct ebda_hpc_bus {
|
||||
u32 bus_num;
|
||||
u8 slots_at_33_conv;
|
||||
u8 slots_at_66_conv;
|
||||
u8 slots_at_66_pcix;
|
||||
u8 slots_at_100_pcix;
|
||||
u8 slots_at_133_pcix;
|
||||
};
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* THREE TYPE OF HOT PLUG CONTROLLER *
|
||||
********************************************************************/
|
||||
|
||||
struct isa_ctlr_access {
|
||||
u16 io_start;
|
||||
u16 io_end;
|
||||
};
|
||||
|
||||
struct pci_ctlr_access {
|
||||
u8 bus;
|
||||
u8 dev_fun;
|
||||
};
|
||||
|
||||
struct wpeg_i2c_ctlr_access {
|
||||
ulong wpegbbar;
|
||||
u8 i2c_addr;
|
||||
};
|
||||
|
||||
#define HPC_DEVICE_ID 0x0246
|
||||
#define HPC_SUBSYSTEM_ID 0x0247
|
||||
#define HPC_PCI_OFFSET 0x40
|
||||
/*************************************************************************
|
||||
* RSTC DESCRIPTOR NODE *
|
||||
*************************************************************************/
|
||||
|
||||
struct ebda_rsrc_list {
|
||||
u8 format;
|
||||
u16 num_entries;
|
||||
u16 phys_addr;
|
||||
struct ebda_rsrc_list *next;
|
||||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* PCI RSRC NODE *
|
||||
***************************************************************************/
|
||||
|
||||
struct ebda_pci_rsrc {
|
||||
u8 rsrc_type;
|
||||
u8 bus_num;
|
||||
u8 dev_fun;
|
||||
u32 start_addr;
|
||||
u32 end_addr;
|
||||
u8 marked; /* for NVRAM */
|
||||
struct list_head ebda_pci_rsrc_list;
|
||||
};
|
||||
|
||||
|
||||
/***********************************************************
|
||||
* BUS_INFO DATE STRUCTURE *
|
||||
***********************************************************/
|
||||
|
||||
struct bus_info {
|
||||
u8 slot_min;
|
||||
u8 slot_max;
|
||||
u8 slot_count;
|
||||
u8 busno;
|
||||
u8 controller_id;
|
||||
u8 current_speed;
|
||||
u8 current_bus_mode;
|
||||
u8 index;
|
||||
u8 slots_at_33_conv;
|
||||
u8 slots_at_66_conv;
|
||||
u8 slots_at_66_pcix;
|
||||
u8 slots_at_100_pcix;
|
||||
u8 slots_at_133_pcix;
|
||||
struct list_head bus_info_list;
|
||||
};
|
||||
|
||||
|
||||
/***********************************************************
|
||||
* GLOBAL VARIABLES *
|
||||
***********************************************************/
|
||||
extern struct list_head ibmphp_ebda_pci_rsrc_head;
|
||||
extern struct list_head ibmphp_slot_head;
|
||||
/***********************************************************
|
||||
* FUNCTION PROTOTYPES *
|
||||
***********************************************************/
|
||||
|
||||
extern void ibmphp_free_ebda_hpc_queue (void);
|
||||
extern int ibmphp_access_ebda (void);
|
||||
extern struct slot *ibmphp_get_slot_from_physical_num (u8);
|
||||
extern int ibmphp_get_total_hp_slots (void);
|
||||
extern void ibmphp_free_ibm_slot (struct slot *);
|
||||
extern void ibmphp_free_bus_info_queue (void);
|
||||
extern void ibmphp_free_ebda_pci_rsrc_queue (void);
|
||||
extern struct bus_info *ibmphp_find_same_bus_num (u32);
|
||||
extern int ibmphp_get_bus_index (u8);
|
||||
extern u16 ibmphp_get_total_controllers (void);
|
||||
extern int ibmphp_register_pci (void);
|
||||
|
||||
/* passed parameters */
|
||||
#define MEM 0
|
||||
#define IO 1
|
||||
#define PFMEM 2
|
||||
|
||||
/* bit masks */
|
||||
#define RESTYPE 0x03
|
||||
#define IOMASK 0x00 /* will need to take its complement */
|
||||
#define MMASK 0x01
|
||||
#define PFMASK 0x03
|
||||
#define PCIDEVMASK 0x10 /* we should always have PCI devices */
|
||||
#define PRIMARYBUSMASK 0x20
|
||||
|
||||
/* pci specific defines */
|
||||
#define PCI_VENDOR_ID_NOTVALID 0xFFFF
|
||||
#define PCI_HEADER_TYPE_MULTIDEVICE 0x80
|
||||
#define PCI_HEADER_TYPE_MULTIBRIDGE 0x81
|
||||
|
||||
#define LATENCY 0x64
|
||||
#define CACHE 64
|
||||
#define DEVICEENABLE 0x015F /* CPQ has 0x0157 */
|
||||
|
||||
#define IOBRIDGE 0x1000 /* 4k */
|
||||
#define MEMBRIDGE 0x100000 /* 1M */
|
||||
|
||||
/* irqs */
|
||||
#define SCSI_IRQ 0x09
|
||||
#define LAN_IRQ 0x0A
|
||||
#define OTHER_IRQ 0x0B
|
||||
|
||||
/* Data Structures */
|
||||
|
||||
/* type is of the form x x xx xx
|
||||
* | | | |_ 00 - I/O, 01 - Memory, 11 - PFMemory
|
||||
* | | - 00 - No Restrictions, 01 - Avoid VGA, 10 - Avoid
|
||||
* | | VGA and their aliases, 11 - Avoid ISA
|
||||
* | - 1 - PCI device, 0 - non pci device
|
||||
* - 1 - Primary PCI Bus Information (0 if Normal device)
|
||||
* the IO restrictions [2:3] are only for primary buses
|
||||
*/
|
||||
|
||||
|
||||
/* we need this struct because there could be several resource blocks
|
||||
* allocated per primary bus in the EBDA
|
||||
*/
|
||||
struct range_node {
|
||||
int rangeno;
|
||||
u32 start;
|
||||
u32 end;
|
||||
struct range_node *next;
|
||||
};
|
||||
|
||||
struct bus_node {
|
||||
u8 busno;
|
||||
int noIORanges;
|
||||
struct range_node *rangeIO;
|
||||
int noMemRanges;
|
||||
struct range_node *rangeMem;
|
||||
int noPFMemRanges;
|
||||
struct range_node *rangePFMem;
|
||||
int needIOUpdate;
|
||||
int needMemUpdate;
|
||||
int needPFMemUpdate;
|
||||
struct resource_node *firstIO; /* first IO resource on the Bus */
|
||||
struct resource_node *firstMem; /* first memory resource on the Bus */
|
||||
struct resource_node *firstPFMem; /* first prefetchable memory resource on the Bus */
|
||||
struct resource_node *firstPFMemFromMem; /* when run out of pfmem available, taking from Mem */
|
||||
struct list_head bus_list;
|
||||
};
|
||||
|
||||
struct resource_node {
|
||||
int rangeno;
|
||||
u8 busno;
|
||||
u8 devfunc;
|
||||
u32 start;
|
||||
u32 end;
|
||||
u32 len;
|
||||
int type; /* MEM, IO, PFMEM */
|
||||
u8 fromMem; /* this is to indicate that the range is from
|
||||
* from the Memory bucket rather than from PFMem */
|
||||
struct resource_node *next;
|
||||
struct resource_node *nextRange; /* for the other mem range on bus */
|
||||
};
|
||||
|
||||
struct res_needed {
|
||||
u32 mem;
|
||||
u32 pfmem;
|
||||
u32 io;
|
||||
u8 not_correct; /* needed for return */
|
||||
int devices[32]; /* for device numbers behind this bridge */
|
||||
};
|
||||
|
||||
/* functions */
|
||||
|
||||
extern int ibmphp_rsrc_init (void);
|
||||
extern int ibmphp_add_resource (struct resource_node *);
|
||||
extern int ibmphp_remove_resource (struct resource_node *);
|
||||
extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int);
|
||||
extern int ibmphp_check_resource (struct resource_node *, u8);
|
||||
extern int ibmphp_remove_bus (struct bus_node *, u8);
|
||||
extern void ibmphp_free_resources (void);
|
||||
extern int ibmphp_add_pfmem_from_mem (struct resource_node *);
|
||||
extern struct bus_node *ibmphp_find_res_bus (u8);
|
||||
extern void ibmphp_print_test (void); /* for debugging purposes */
|
||||
|
||||
extern void ibmphp_hpc_initvars (void);
|
||||
extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *);
|
||||
extern int ibmphp_hpc_writeslot (struct slot *, u8);
|
||||
extern void ibmphp_lock_operations (void);
|
||||
extern void ibmphp_unlock_operations (void);
|
||||
extern int ibmphp_hpc_start_poll_thread (void);
|
||||
extern void ibmphp_hpc_stop_poll_thread (void);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_ERROR 0xFF
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// BUS INFO
|
||||
//-----------------------------------------------------------------------------
|
||||
#define BUS_SPEED 0x30
|
||||
#define BUS_MODE 0x40
|
||||
#define BUS_MODE_PCIX 0x01
|
||||
#define BUS_MODE_PCI 0x00
|
||||
#define BUS_SPEED_2 0x20
|
||||
#define BUS_SPEED_1 0x10
|
||||
#define BUS_SPEED_33 0x00
|
||||
#define BUS_SPEED_66 0x01
|
||||
#define BUS_SPEED_100 0x02
|
||||
#define BUS_SPEED_133 0x03
|
||||
#define BUS_SPEED_66PCIX 0x04
|
||||
#define BUS_SPEED_66UNKNOWN 0x05
|
||||
#define BUS_STATUS_AVAILABLE 0x01
|
||||
#define BUS_CONTROL_AVAILABLE 0x02
|
||||
#define SLOT_LATCH_REGS_SUPPORTED 0x10
|
||||
|
||||
#define PRGM_MODEL_REV_LEVEL 0xF0
|
||||
#define MAX_ADAPTER_NONE 0x09
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC 'write' operations/commands
|
||||
//----------------------------------------------------------------------------
|
||||
// Command Code State Write to reg
|
||||
// Machine at index
|
||||
//------------------------- ---- ------- ------------
|
||||
#define HPC_CTLR_ENABLEIRQ 0x00 // N 15
|
||||
#define HPC_CTLR_DISABLEIRQ 0x01 // N 15
|
||||
#define HPC_SLOT_OFF 0x02 // Y 0-14
|
||||
#define HPC_SLOT_ON 0x03 // Y 0-14
|
||||
#define HPC_SLOT_ATTNOFF 0x04 // N 0-14
|
||||
#define HPC_SLOT_ATTNON 0x05 // N 0-14
|
||||
#define HPC_CTLR_CLEARIRQ 0x06 // N 15
|
||||
#define HPC_CTLR_RESET 0x07 // Y 15
|
||||
#define HPC_CTLR_IRQSTEER 0x08 // N 15
|
||||
#define HPC_BUS_33CONVMODE 0x09 // Y 31-34
|
||||
#define HPC_BUS_66CONVMODE 0x0A // Y 31-34
|
||||
#define HPC_BUS_66PCIXMODE 0x0B // Y 31-34
|
||||
#define HPC_BUS_100PCIXMODE 0x0C // Y 31-34
|
||||
#define HPC_BUS_133PCIXMODE 0x0D // Y 31-34
|
||||
#define HPC_ALLSLOT_OFF 0x11 // Y 15
|
||||
#define HPC_ALLSLOT_ON 0x12 // Y 15
|
||||
#define HPC_SLOT_BLINKLED 0x13 // N 0-14
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// read commands
|
||||
//----------------------------------------------------------------------------
|
||||
#define READ_SLOTSTATUS 0x01
|
||||
#define READ_EXTSLOTSTATUS 0x02
|
||||
#define READ_BUSSTATUS 0x03
|
||||
#define READ_CTLRSTATUS 0x04
|
||||
#define READ_ALLSTAT 0x05
|
||||
#define READ_ALLSLOT 0x06
|
||||
#define READ_SLOTLATCHLOWREG 0x07
|
||||
#define READ_REVLEVEL 0x08
|
||||
#define READ_HPCOPTIONS 0x09
|
||||
//----------------------------------------------------------------------------
|
||||
// slot status
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_POWER 0x01
|
||||
#define HPC_SLOT_CONNECT 0x02
|
||||
#define HPC_SLOT_ATTN 0x04
|
||||
#define HPC_SLOT_PRSNT2 0x08
|
||||
#define HPC_SLOT_PRSNT1 0x10
|
||||
#define HPC_SLOT_PWRGD 0x20
|
||||
#define HPC_SLOT_BUS_SPEED 0x40
|
||||
#define HPC_SLOT_LATCH 0x80
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_SLOT_POWER status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_POWER_OFF 0x00
|
||||
#define HPC_SLOT_POWER_ON 0x01
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_SLOT_CONNECT status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_CONNECTED 0x00
|
||||
#define HPC_SLOT_DISCONNECTED 0x01
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_SLOT_ATTN status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_ATTN_OFF 0x00
|
||||
#define HPC_SLOT_ATTN_ON 0x01
|
||||
#define HPC_SLOT_ATTN_BLINK 0x02
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_SLOT_PRSNT status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_EMPTY 0x00
|
||||
#define HPC_SLOT_PRSNT_7 0x01
|
||||
#define HPC_SLOT_PRSNT_15 0x02
|
||||
#define HPC_SLOT_PRSNT_25 0x03
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_SLOT_PWRGD status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_PWRGD_FAULT_NONE 0x00
|
||||
#define HPC_SLOT_PWRGD_GOOD 0x01
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_SLOT_BUS_SPEED status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_BUS_SPEED_OK 0x00
|
||||
#define HPC_SLOT_BUS_SPEED_MISM 0x01
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_SLOT_LATCH status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_LATCH_OPEN 0x01 // NOTE : in PCI spec bit off = open
|
||||
#define HPC_SLOT_LATCH_CLOSED 0x00 // NOTE : in PCI spec bit on = closed
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// extended slot status
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_PCIX 0x01
|
||||
#define HPC_SLOT_SPEED1 0x02
|
||||
#define HPC_SLOT_SPEED2 0x04
|
||||
#define HPC_SLOT_BLINK_ATTN 0x08
|
||||
#define HPC_SLOT_RSRVD1 0x10
|
||||
#define HPC_SLOT_RSRVD2 0x20
|
||||
#define HPC_SLOT_BUS_MODE 0x40
|
||||
#define HPC_SLOT_RSRVD3 0x80
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_XSLOT_PCIX_CAP status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_PCIX_NO 0x00
|
||||
#define HPC_SLOT_PCIX_YES 0x01
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_XSLOT_SPEED status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_SPEED_33 0x00
|
||||
#define HPC_SLOT_SPEED_66 0x01
|
||||
#define HPC_SLOT_SPEED_133 0x02
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_XSLOT_ATTN_BLINK status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_ATTN_BLINK_OFF 0x00
|
||||
#define HPC_SLOT_ATTN_BLINK_ON 0x01
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_XSLOT_BUS_MODE status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_SLOT_BUS_MODE_OK 0x00
|
||||
#define HPC_SLOT_BUS_MODE_MISM 0x01
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Controller status
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_CTLR_WORKING 0x01
|
||||
#define HPC_CTLR_FINISHED 0x02
|
||||
#define HPC_CTLR_RESULT0 0x04
|
||||
#define HPC_CTLR_RESULT1 0x08
|
||||
#define HPC_CTLR_RESULE2 0x10
|
||||
#define HPC_CTLR_RESULT3 0x20
|
||||
#define HPC_CTLR_IRQ_ROUTG 0x40
|
||||
#define HPC_CTLR_IRQ_PENDG 0x80
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_CTLR_WROKING status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_CTLR_WORKING_NO 0x00
|
||||
#define HPC_CTLR_WORKING_YES 0x01
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_CTLR_FINISHED status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_CTLR_FINISHED_NO 0x00
|
||||
#define HPC_CTLR_FINISHED_YES 0x01
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HPC_CTLR_RESULT status return codes
|
||||
//----------------------------------------------------------------------------
|
||||
#define HPC_CTLR_RESULT_SUCCESS 0x00
|
||||
#define HPC_CTLR_RESULT_FAILED 0x01
|
||||
#define HPC_CTLR_RESULT_RSVD 0x02
|
||||
#define HPC_CTLR_RESULT_NORESP 0x03
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// macro for slot info
|
||||
//----------------------------------------------------------------------------
|
||||
#define SLOT_POWER(s) ((u8) ((s & HPC_SLOT_POWER) \
|
||||
? HPC_SLOT_POWER_ON : HPC_SLOT_POWER_OFF))
|
||||
|
||||
#define SLOT_CONNECT(s) ((u8) ((s & HPC_SLOT_CONNECT) \
|
||||
? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED))
|
||||
|
||||
#define SLOT_ATTN(s,es) ((u8) ((es & HPC_SLOT_BLINK_ATTN) \
|
||||
? HPC_SLOT_ATTN_BLINK \
|
||||
: ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF)))
|
||||
|
||||
#define SLOT_PRESENT(s) ((u8) ((s & HPC_SLOT_PRSNT1) \
|
||||
? ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_EMPTY : HPC_SLOT_PRSNT_15) \
|
||||
: ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_PRSNT_25 : HPC_SLOT_PRSNT_7)))
|
||||
|
||||
#define SLOT_PWRGD(s) ((u8) ((s & HPC_SLOT_PWRGD) \
|
||||
? HPC_SLOT_PWRGD_GOOD : HPC_SLOT_PWRGD_FAULT_NONE))
|
||||
|
||||
#define SLOT_BUS_SPEED(s) ((u8) ((s & HPC_SLOT_BUS_SPEED) \
|
||||
? HPC_SLOT_BUS_SPEED_MISM : HPC_SLOT_BUS_SPEED_OK))
|
||||
|
||||
#define SLOT_LATCH(s) ((u8) ((s & HPC_SLOT_LATCH) \
|
||||
? HPC_SLOT_LATCH_CLOSED : HPC_SLOT_LATCH_OPEN))
|
||||
|
||||
#define SLOT_PCIX(es) ((u8) ((es & HPC_SLOT_PCIX) \
|
||||
? HPC_SLOT_PCIX_YES : HPC_SLOT_PCIX_NO))
|
||||
|
||||
#define SLOT_SPEED(es) ((u8) ((es & HPC_SLOT_SPEED2) \
|
||||
? ((es & HPC_SLOT_SPEED1) ? HPC_SLOT_SPEED_133 \
|
||||
: HPC_SLOT_SPEED_66) \
|
||||
: HPC_SLOT_SPEED_33))
|
||||
|
||||
#define SLOT_BUS_MODE(es) ((u8) ((es & HPC_SLOT_BUS_MODE) \
|
||||
? HPC_SLOT_BUS_MODE_MISM : HPC_SLOT_BUS_MODE_OK))
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// macro for bus info
|
||||
//---------------------------------------------------------------------------
|
||||
#define CURRENT_BUS_SPEED(s) ((u8) (s & BUS_SPEED_2) \
|
||||
? ((s & BUS_SPEED_1) ? BUS_SPEED_133 : BUS_SPEED_100) \
|
||||
: ((s & BUS_SPEED_1) ? BUS_SPEED_66 : BUS_SPEED_33))
|
||||
|
||||
#define CURRENT_BUS_MODE(s) ((u8) (s & BUS_MODE) ? BUS_MODE_PCIX : BUS_MODE_PCI)
|
||||
|
||||
#define READ_BUS_STATUS(s) ((u8) (s->options & BUS_STATUS_AVAILABLE))
|
||||
|
||||
#define READ_BUS_MODE(s) ((s->revision & PRGM_MODEL_REV_LEVEL) >= 0x20)
|
||||
|
||||
#define SET_BUS_STATUS(s) ((u8) (s->options & BUS_CONTROL_AVAILABLE))
|
||||
|
||||
#define READ_SLOT_LATCH(s) ((u8) (s->options & SLOT_LATCH_REGS_SUPPORTED))
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// macro for controller info
|
||||
//----------------------------------------------------------------------------
|
||||
#define CTLR_WORKING(c) ((u8) ((c & HPC_CTLR_WORKING) \
|
||||
? HPC_CTLR_WORKING_YES : HPC_CTLR_WORKING_NO))
|
||||
#define CTLR_FINISHED(c) ((u8) ((c & HPC_CTLR_FINISHED) \
|
||||
? HPC_CTLR_FINISHED_YES : HPC_CTLR_FINISHED_NO))
|
||||
#define CTLR_RESULT(c) ((u8) ((c & HPC_CTLR_RESULT1) \
|
||||
? ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_NORESP \
|
||||
: HPC_CTLR_RESULT_RSVD) \
|
||||
: ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_FAILED \
|
||||
: HPC_CTLR_RESULT_SUCCESS)))
|
||||
|
||||
// command that affect the state machine of HPC
|
||||
#define NEEDTOCHECK_CMDSTATUS(c) ((c == HPC_SLOT_OFF) || \
|
||||
(c == HPC_SLOT_ON) || \
|
||||
(c == HPC_CTLR_RESET) || \
|
||||
(c == HPC_BUS_33CONVMODE) || \
|
||||
(c == HPC_BUS_66CONVMODE) || \
|
||||
(c == HPC_BUS_66PCIXMODE) || \
|
||||
(c == HPC_BUS_100PCIXMODE) || \
|
||||
(c == HPC_BUS_133PCIXMODE) || \
|
||||
(c == HPC_ALLSLOT_OFF) || \
|
||||
(c == HPC_ALLSLOT_ON))
|
||||
|
||||
|
||||
/* Core part of the driver */
|
||||
|
||||
#define ENABLE 1
|
||||
#define DISABLE 0
|
||||
|
||||
#define CARD_INFO 0x07
|
||||
#define PCIX133 0x07
|
||||
#define PCIX66 0x05
|
||||
#define PCI66 0x04
|
||||
|
||||
extern struct pci_bus *ibmphp_pci_bus;
|
||||
|
||||
/* Variables */
|
||||
|
||||
struct pci_func {
|
||||
struct pci_dev *dev; /* from the OS */
|
||||
u8 busno;
|
||||
u8 device;
|
||||
u8 function;
|
||||
struct resource_node *io[6];
|
||||
struct resource_node *mem[6];
|
||||
struct resource_node *pfmem[6];
|
||||
struct pci_func *next;
|
||||
int devices[32]; /* for bridge config */
|
||||
u8 irq[4]; /* for interrupt config */
|
||||
u8 bus; /* flag for unconfiguring, to say if PPB */
|
||||
};
|
||||
|
||||
struct slot {
|
||||
u8 bus;
|
||||
u8 device;
|
||||
u8 number;
|
||||
u8 real_physical_slot_num;
|
||||
char name[100];
|
||||
u32 capabilities;
|
||||
u8 supported_speed;
|
||||
u8 supported_bus_mode;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct controller *ctrl;
|
||||
struct pci_func *func;
|
||||
u8 irq[4];
|
||||
u8 flag; /* this is for disable slot and polling */
|
||||
int bit_mode; /* 0 = 32, 1 = 64 */
|
||||
u8 ctlr_index;
|
||||
struct bus_info *bus_on;
|
||||
struct list_head ibm_slot_list;
|
||||
u8 status;
|
||||
u8 ext_status;
|
||||
u8 busstatus;
|
||||
};
|
||||
|
||||
struct controller {
|
||||
struct ebda_hpc_slot *slots;
|
||||
struct ebda_hpc_bus *buses;
|
||||
struct pci_dev *ctrl_dev; /* in case where controller is PCI */
|
||||
u8 starting_slot_num; /* starting and ending slot #'s this ctrl controls*/
|
||||
u8 ending_slot_num;
|
||||
u8 revision;
|
||||
u8 options; /* which options HPC supports */
|
||||
u8 status;
|
||||
u8 ctlr_id;
|
||||
u8 slot_count;
|
||||
u8 bus_count;
|
||||
u8 ctlr_relative_id;
|
||||
u32 irq;
|
||||
union {
|
||||
struct isa_ctlr_access isa_ctlr;
|
||||
struct pci_ctlr_access pci_ctlr;
|
||||
struct wpeg_i2c_ctlr_access wpeg_ctlr;
|
||||
} u;
|
||||
u8 ctlr_type;
|
||||
struct list_head ebda_hpc_list;
|
||||
};
|
||||
|
||||
/* Functions */
|
||||
|
||||
extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */
|
||||
extern int ibmphp_do_disable_slot (struct slot *slot_cur);
|
||||
extern int ibmphp_update_slot_info (struct slot *); /* This function is called from HPC, so we need it to not be be static */
|
||||
extern int ibmphp_configure_card (struct pci_func *, u8);
|
||||
extern int ibmphp_unconfigure_card (struct slot **, int);
|
||||
extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
|
||||
|
||||
#endif //__IBMPHP_H
|
||||
|
||||
1420
drivers/pci/hotplug/ibmphp_core.c
Normal file
1420
drivers/pci/hotplug/ibmphp_core.c
Normal file
File diff suppressed because it is too large
Load Diff
1245
drivers/pci/hotplug/ibmphp_ebda.c
Normal file
1245
drivers/pci/hotplug/ibmphp_ebda.c
Normal file
File diff suppressed because it is too large
Load Diff
1163
drivers/pci/hotplug/ibmphp_hpc.c
Normal file
1163
drivers/pci/hotplug/ibmphp_hpc.c
Normal file
File diff suppressed because it is too large
Load Diff
1729
drivers/pci/hotplug/ibmphp_pci.c
Normal file
1729
drivers/pci/hotplug/ibmphp_pci.c
Normal file
File diff suppressed because it is too large
Load Diff
2145
drivers/pci/hotplug/ibmphp_res.c
Normal file
2145
drivers/pci/hotplug/ibmphp_res.c
Normal file
File diff suppressed because it is too large
Load Diff
806
drivers/pci/hotplug/pci_hotplug_core.c
Normal file
806
drivers/pci/hotplug/pci_hotplug_core.c
Normal file
@@ -0,0 +1,806 @@
|
||||
/*
|
||||
* PCI HotPlug Controller Core
|
||||
*
|
||||
* Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001-2002 IBM Corp.
|
||||
*
|
||||
* 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 <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#define MY_NAME "pci_hotplug"
|
||||
|
||||
#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
|
||||
|
||||
|
||||
/* local variables */
|
||||
static int debug;
|
||||
|
||||
#define DRIVER_VERSION "0.5"
|
||||
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
|
||||
#define DRIVER_DESC "PCI Hot Plug PCI Core"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
static LIST_HEAD(pci_hotplug_slot_list);
|
||||
|
||||
struct subsystem pci_hotplug_slots_subsys;
|
||||
|
||||
static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
{
|
||||
struct hotplug_slot *slot = to_hotplug_slot(kobj);
|
||||
struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
|
||||
return attribute->show ? attribute->show(slot, buf) : -EIO;
|
||||
}
|
||||
|
||||
static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
|
||||
struct attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct hotplug_slot *slot = to_hotplug_slot(kobj);
|
||||
struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
|
||||
return attribute->store ? attribute->store(slot, buf, len) : -EIO;
|
||||
}
|
||||
|
||||
static struct sysfs_ops hotplug_slot_sysfs_ops = {
|
||||
.show = hotplug_slot_attr_show,
|
||||
.store = hotplug_slot_attr_store,
|
||||
};
|
||||
|
||||
static void hotplug_slot_release(struct kobject *kobj)
|
||||
{
|
||||
struct hotplug_slot *slot = to_hotplug_slot(kobj);
|
||||
if (slot->release)
|
||||
slot->release(slot);
|
||||
}
|
||||
|
||||
static struct kobj_type hotplug_slot_ktype = {
|
||||
.sysfs_ops = &hotplug_slot_sysfs_ops,
|
||||
.release = &hotplug_slot_release,
|
||||
};
|
||||
|
||||
decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
|
||||
|
||||
/* these strings match up with the values in pci_bus_speed */
|
||||
static char *pci_bus_speed_strings[] = {
|
||||
"33 MHz PCI", /* 0x00 */
|
||||
"66 MHz PCI", /* 0x01 */
|
||||
"66 MHz PCIX", /* 0x02 */
|
||||
"100 MHz PCIX", /* 0x03 */
|
||||
"133 MHz PCIX", /* 0x04 */
|
||||
NULL, /* 0x05 */
|
||||
NULL, /* 0x06 */
|
||||
NULL, /* 0x07 */
|
||||
NULL, /* 0x08 */
|
||||
"66 MHz PCIX 266", /* 0x09 */
|
||||
"100 MHz PCIX 266", /* 0x0a */
|
||||
"133 MHz PCIX 266", /* 0x0b */
|
||||
NULL, /* 0x0c */
|
||||
NULL, /* 0x0d */
|
||||
NULL, /* 0x0e */
|
||||
NULL, /* 0x0f */
|
||||
NULL, /* 0x10 */
|
||||
"66 MHz PCIX 533", /* 0x11 */
|
||||
"100 MHz PCIX 533", /* 0x12 */
|
||||
"133 MHz PCIX 533", /* 0x13 */
|
||||
"25 GBps PCI-E", /* 0x14 */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_PCI_CPCI
|
||||
extern int cpci_hotplug_init(int debug);
|
||||
extern void cpci_hotplug_exit(void);
|
||||
#else
|
||||
static inline int cpci_hotplug_init(int debug) { return 0; }
|
||||
static inline void cpci_hotplug_exit(void) { }
|
||||
#endif
|
||||
|
||||
/* Weee, fun with macros... */
|
||||
#define GET_STATUS(name,type) \
|
||||
static int get_##name (struct hotplug_slot *slot, type *value) \
|
||||
{ \
|
||||
struct hotplug_slot_ops *ops = slot->ops; \
|
||||
int retval = 0; \
|
||||
if (try_module_get(ops->owner)) { \
|
||||
if (ops->get_##name) \
|
||||
retval = ops->get_##name (slot, value); \
|
||||
else \
|
||||
*value = slot->info->name; \
|
||||
module_put(ops->owner); \
|
||||
} \
|
||||
return retval; \
|
||||
}
|
||||
|
||||
GET_STATUS(power_status, u8)
|
||||
GET_STATUS(attention_status, u8)
|
||||
GET_STATUS(latch_status, u8)
|
||||
GET_STATUS(adapter_status, u8)
|
||||
GET_STATUS(address, u32)
|
||||
GET_STATUS(max_bus_speed, enum pci_bus_speed)
|
||||
GET_STATUS(cur_bus_speed, enum pci_bus_speed)
|
||||
|
||||
static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
|
||||
{
|
||||
int retval;
|
||||
u8 value;
|
||||
|
||||
retval = get_power_status (slot, &value);
|
||||
if (retval)
|
||||
goto exit;
|
||||
retval = sprintf (buf, "%d\n", value);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long lpower;
|
||||
u8 power;
|
||||
int retval = 0;
|
||||
|
||||
lpower = simple_strtoul (buf, NULL, 10);
|
||||
power = (u8)(lpower & 0xff);
|
||||
dbg ("power = %d\n", power);
|
||||
|
||||
if (!try_module_get(slot->ops->owner)) {
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
switch (power) {
|
||||
case 0:
|
||||
if (slot->ops->disable_slot)
|
||||
retval = slot->ops->disable_slot(slot);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (slot->ops->enable_slot)
|
||||
retval = slot->ops->enable_slot(slot);
|
||||
break;
|
||||
|
||||
default:
|
||||
err ("Illegal value specified for power\n");
|
||||
retval = -EINVAL;
|
||||
}
|
||||
module_put(slot->ops->owner);
|
||||
|
||||
exit:
|
||||
if (retval)
|
||||
return retval;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_power = {
|
||||
.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
||||
.show = power_read_file,
|
||||
.store = power_write_file
|
||||
};
|
||||
|
||||
static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
|
||||
{
|
||||
int retval;
|
||||
u8 value;
|
||||
|
||||
retval = get_attention_status (slot, &value);
|
||||
if (retval)
|
||||
goto exit;
|
||||
retval = sprintf (buf, "%d\n", value);
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long lattention;
|
||||
u8 attention;
|
||||
int retval = 0;
|
||||
|
||||
lattention = simple_strtoul (buf, NULL, 10);
|
||||
attention = (u8)(lattention & 0xff);
|
||||
dbg (" - attention = %d\n", attention);
|
||||
|
||||
if (!try_module_get(slot->ops->owner)) {
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
if (slot->ops->set_attention_status)
|
||||
retval = slot->ops->set_attention_status(slot, attention);
|
||||
module_put(slot->ops->owner);
|
||||
|
||||
exit:
|
||||
if (retval)
|
||||
return retval;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
|
||||
.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
||||
.show = attention_read_file,
|
||||
.store = attention_write_file
|
||||
};
|
||||
|
||||
static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
|
||||
{
|
||||
int retval;
|
||||
u8 value;
|
||||
|
||||
retval = get_latch_status (slot, &value);
|
||||
if (retval)
|
||||
goto exit;
|
||||
retval = sprintf (buf, "%d\n", value);
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
|
||||
.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
|
||||
.show = latch_read_file,
|
||||
};
|
||||
|
||||
static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
|
||||
{
|
||||
int retval;
|
||||
u8 value;
|
||||
|
||||
retval = get_adapter_status (slot, &value);
|
||||
if (retval)
|
||||
goto exit;
|
||||
retval = sprintf (buf, "%d\n", value);
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
|
||||
.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
|
||||
.show = presence_read_file,
|
||||
};
|
||||
|
||||
static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
|
||||
{
|
||||
int retval;
|
||||
u32 address;
|
||||
|
||||
retval = get_address (slot, &address);
|
||||
if (retval)
|
||||
goto exit;
|
||||
retval = sprintf (buf, "%04x:%02x:%02x\n",
|
||||
(address >> 16) & 0xffff,
|
||||
(address >> 8) & 0xff,
|
||||
address & 0xff);
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_address = {
|
||||
.attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
|
||||
.show = address_read_file,
|
||||
};
|
||||
|
||||
static char *unknown_speed = "Unknown bus speed";
|
||||
|
||||
static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
|
||||
{
|
||||
char *speed_string;
|
||||
int retval;
|
||||
enum pci_bus_speed value;
|
||||
|
||||
retval = get_max_bus_speed (slot, &value);
|
||||
if (retval)
|
||||
goto exit;
|
||||
|
||||
if (value == PCI_SPEED_UNKNOWN)
|
||||
speed_string = unknown_speed;
|
||||
else
|
||||
speed_string = pci_bus_speed_strings[value];
|
||||
|
||||
retval = sprintf (buf, "%s\n", speed_string);
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
|
||||
.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
|
||||
.show = max_bus_speed_read_file,
|
||||
};
|
||||
|
||||
static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
|
||||
{
|
||||
char *speed_string;
|
||||
int retval;
|
||||
enum pci_bus_speed value;
|
||||
|
||||
retval = get_cur_bus_speed (slot, &value);
|
||||
if (retval)
|
||||
goto exit;
|
||||
|
||||
if (value == PCI_SPEED_UNKNOWN)
|
||||
speed_string = unknown_speed;
|
||||
else
|
||||
speed_string = pci_bus_speed_strings[value];
|
||||
|
||||
retval = sprintf (buf, "%s\n", speed_string);
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
|
||||
.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
|
||||
.show = cur_bus_speed_read_file,
|
||||
};
|
||||
|
||||
static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long ltest;
|
||||
u32 test;
|
||||
int retval = 0;
|
||||
|
||||
ltest = simple_strtoul (buf, NULL, 10);
|
||||
test = (u32)(ltest & 0xffffffff);
|
||||
dbg ("test = %d\n", test);
|
||||
|
||||
if (!try_module_get(slot->ops->owner)) {
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
if (slot->ops->hardware_test)
|
||||
retval = slot->ops->hardware_test(slot, test);
|
||||
module_put(slot->ops->owner);
|
||||
|
||||
exit:
|
||||
if (retval)
|
||||
return retval;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_test = {
|
||||
.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
||||
.store = test_write_file
|
||||
};
|
||||
|
||||
static int has_power_file (struct hotplug_slot *slot)
|
||||
{
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
if ((slot->ops->enable_slot) ||
|
||||
(slot->ops->disable_slot) ||
|
||||
(slot->ops->get_power_status))
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int has_attention_file (struct hotplug_slot *slot)
|
||||
{
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
if ((slot->ops->set_attention_status) ||
|
||||
(slot->ops->get_attention_status))
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int has_latch_file (struct hotplug_slot *slot)
|
||||
{
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
if (slot->ops->get_latch_status)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int has_adapter_file (struct hotplug_slot *slot)
|
||||
{
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
if (slot->ops->get_adapter_status)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int has_address_file (struct hotplug_slot *slot)
|
||||
{
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
if (slot->ops->get_address)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int has_max_bus_speed_file (struct hotplug_slot *slot)
|
||||
{
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
if (slot->ops->get_max_bus_speed)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int has_cur_bus_speed_file (struct hotplug_slot *slot)
|
||||
{
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
if (slot->ops->get_cur_bus_speed)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int has_test_file (struct hotplug_slot *slot)
|
||||
{
|
||||
if ((!slot) || (!slot->ops))
|
||||
return -ENODEV;
|
||||
if (slot->ops->hardware_test)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int fs_add_slot (struct hotplug_slot *slot)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (has_power_file(slot) == 0) {
|
||||
retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
|
||||
if (retval)
|
||||
goto exit_power;
|
||||
}
|
||||
|
||||
if (has_attention_file(slot) == 0) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_attention.attr);
|
||||
if (retval)
|
||||
goto exit_attention;
|
||||
}
|
||||
|
||||
if (has_latch_file(slot) == 0) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_latch.attr);
|
||||
if (retval)
|
||||
goto exit_latch;
|
||||
}
|
||||
|
||||
if (has_adapter_file(slot) == 0) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_presence.attr);
|
||||
if (retval)
|
||||
goto exit_adapter;
|
||||
}
|
||||
|
||||
if (has_address_file(slot) == 0) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_address.attr);
|
||||
if (retval)
|
||||
goto exit_address;
|
||||
}
|
||||
|
||||
if (has_max_bus_speed_file(slot) == 0) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_max_bus_speed.attr);
|
||||
if (retval)
|
||||
goto exit_max_speed;
|
||||
}
|
||||
|
||||
if (has_cur_bus_speed_file(slot) == 0) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_cur_bus_speed.attr);
|
||||
if (retval)
|
||||
goto exit_cur_speed;
|
||||
}
|
||||
|
||||
if (has_test_file(slot) == 0) {
|
||||
retval = sysfs_create_file(&slot->kobj,
|
||||
&hotplug_slot_attr_test.attr);
|
||||
if (retval)
|
||||
goto exit_test;
|
||||
}
|
||||
|
||||
goto exit;
|
||||
|
||||
exit_test:
|
||||
if (has_cur_bus_speed_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
|
||||
|
||||
exit_cur_speed:
|
||||
if (has_max_bus_speed_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
|
||||
|
||||
exit_max_speed:
|
||||
if (has_address_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
|
||||
|
||||
exit_address:
|
||||
if (has_adapter_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
|
||||
|
||||
exit_adapter:
|
||||
if (has_latch_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
|
||||
|
||||
exit_latch:
|
||||
if (has_attention_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
|
||||
|
||||
exit_attention:
|
||||
if (has_power_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
|
||||
exit_power:
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void fs_remove_slot (struct hotplug_slot *slot)
|
||||
{
|
||||
if (has_power_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
|
||||
|
||||
if (has_attention_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
|
||||
|
||||
if (has_latch_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
|
||||
|
||||
if (has_adapter_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
|
||||
|
||||
if (has_address_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
|
||||
|
||||
if (has_max_bus_speed_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
|
||||
|
||||
if (has_cur_bus_speed_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
|
||||
|
||||
if (has_test_file(slot) == 0)
|
||||
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
|
||||
}
|
||||
|
||||
static struct hotplug_slot *get_slot_from_name (const char *name)
|
||||
{
|
||||
struct hotplug_slot *slot;
|
||||
struct list_head *tmp;
|
||||
|
||||
list_for_each (tmp, &pci_hotplug_slot_list) {
|
||||
slot = list_entry (tmp, struct hotplug_slot, slot_list);
|
||||
if (strcmp(slot->name, name) == 0)
|
||||
return slot;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
|
||||
* @slot: pointer to the &struct hotplug_slot to register
|
||||
*
|
||||
* Registers a hotplug slot with the pci hotplug subsystem, which will allow
|
||||
* userspace interaction to the slot.
|
||||
*
|
||||
* Returns 0 if successful, anything else for an error.
|
||||
*/
|
||||
int pci_hp_register (struct hotplug_slot *slot)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (slot == NULL)
|
||||
return -ENODEV;
|
||||
if ((slot->info == NULL) || (slot->ops == NULL))
|
||||
return -EINVAL;
|
||||
if (slot->release == NULL) {
|
||||
dbg("Why are you trying to register a hotplug slot"
|
||||
"without a proper release function?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kobject_set_name(&slot->kobj, "%s", slot->name);
|
||||
kobj_set_kset_s(slot, pci_hotplug_slots_subsys);
|
||||
|
||||
/* this can fail if we have already registered a slot with the same name */
|
||||
if (kobject_register(&slot->kobj)) {
|
||||
err("Unable to register kobject");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_add (&slot->slot_list, &pci_hotplug_slot_list);
|
||||
|
||||
result = fs_add_slot (slot);
|
||||
dbg ("Added slot %s to the list\n", slot->name);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
|
||||
* @slot: pointer to the &struct hotplug_slot to deregister
|
||||
*
|
||||
* The @slot must have been registered with the pci hotplug subsystem
|
||||
* previously with a call to pci_hp_register().
|
||||
*
|
||||
* Returns 0 if successful, anything else for an error.
|
||||
*/
|
||||
int pci_hp_deregister (struct hotplug_slot *slot)
|
||||
{
|
||||
struct hotplug_slot *temp;
|
||||
|
||||
if (slot == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
temp = get_slot_from_name (slot->name);
|
||||
if (temp != slot) {
|
||||
return -ENODEV;
|
||||
}
|
||||
list_del (&slot->slot_list);
|
||||
|
||||
fs_remove_slot (slot);
|
||||
dbg ("Removed slot %s from the list\n", slot->name);
|
||||
kobject_unregister(&slot->kobj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_hp_change_slot_info - changes the slot's information structure in the core
|
||||
* @slot: pointer to the slot whose info has changed
|
||||
* @info: pointer to the info copy into the slot's info structure
|
||||
*
|
||||
* @slot must have been registered with the pci
|
||||
* hotplug subsystem previously with a call to pci_hp_register().
|
||||
*
|
||||
* Returns 0 if successful, anything else for an error.
|
||||
*/
|
||||
int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
|
||||
struct hotplug_slot_info *info)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if ((slot == NULL) || (info == NULL))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* check all fields in the info structure, and update timestamps
|
||||
* for the files referring to the fields that have now changed.
|
||||
*/
|
||||
if ((has_power_file(slot) == 0) &&
|
||||
(slot->info->power_status != info->power_status)) {
|
||||
retval = sysfs_update_file(&slot->kobj,
|
||||
&hotplug_slot_attr_power.attr);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((has_attention_file(slot) == 0) &&
|
||||
(slot->info->attention_status != info->attention_status)) {
|
||||
retval = sysfs_update_file(&slot->kobj,
|
||||
&hotplug_slot_attr_attention.attr);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((has_latch_file(slot) == 0) &&
|
||||
(slot->info->latch_status != info->latch_status)) {
|
||||
retval = sysfs_update_file(&slot->kobj,
|
||||
&hotplug_slot_attr_latch.attr);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((has_adapter_file(slot) == 0) &&
|
||||
(slot->info->adapter_status != info->adapter_status)) {
|
||||
retval = sysfs_update_file(&slot->kobj,
|
||||
&hotplug_slot_attr_presence.attr);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((has_address_file(slot) == 0) &&
|
||||
(slot->info->address != info->address)) {
|
||||
retval = sysfs_update_file(&slot->kobj,
|
||||
&hotplug_slot_attr_address.attr);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((has_max_bus_speed_file(slot) == 0) &&
|
||||
(slot->info->max_bus_speed != info->max_bus_speed)) {
|
||||
retval = sysfs_update_file(&slot->kobj,
|
||||
&hotplug_slot_attr_max_bus_speed.attr);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((has_cur_bus_speed_file(slot) == 0) &&
|
||||
(slot->info->cur_bus_speed != info->cur_bus_speed)) {
|
||||
retval = sysfs_update_file(&slot->kobj,
|
||||
&hotplug_slot_attr_cur_bus_speed.attr);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init pci_hotplug_init (void)
|
||||
{
|
||||
int result;
|
||||
|
||||
kset_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys);
|
||||
result = subsystem_register(&pci_hotplug_slots_subsys);
|
||||
if (result) {
|
||||
err("Register subsys with error %d\n", result);
|
||||
goto exit;
|
||||
}
|
||||
result = cpci_hotplug_init(debug);
|
||||
if (result) {
|
||||
err ("cpci_hotplug_init with error %d\n", result);
|
||||
goto err_subsys;
|
||||
}
|
||||
|
||||
info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
goto exit;
|
||||
|
||||
err_subsys:
|
||||
subsystem_unregister(&pci_hotplug_slots_subsys);
|
||||
exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit pci_hotplug_exit (void)
|
||||
{
|
||||
cpci_hotplug_exit();
|
||||
subsystem_unregister(&pci_hotplug_slots_subsys);
|
||||
}
|
||||
|
||||
module_init(pci_hotplug_init);
|
||||
module_exit(pci_hotplug_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
module_param(debug, bool, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
||||
|
||||
EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
|
||||
EXPORT_SYMBOL_GPL(pci_hp_register);
|
||||
EXPORT_SYMBOL_GPL(pci_hp_deregister);
|
||||
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
|
||||
221
drivers/pci/hotplug/pciehp.h
Normal file
221
drivers/pci/hotplug/pciehp.h
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* PCI Express Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
#ifndef _PCIEHP_H
|
||||
#define _PCIEHP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h> /* signal_pending() */
|
||||
#include <linux/pcieport_if.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define MY_NAME "pciehp"
|
||||
|
||||
extern int pciehp_poll_mode;
|
||||
extern int pciehp_poll_time;
|
||||
extern int pciehp_debug;
|
||||
extern int pciehp_force;
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (pciehp_debug) \
|
||||
printk("%s: " format, MY_NAME , ## arg); \
|
||||
} while (0)
|
||||
#define err(format, arg...) \
|
||||
printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
|
||||
#define info(format, arg...) \
|
||||
printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
|
||||
#define warn(format, arg...) \
|
||||
printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
|
||||
|
||||
#define SLOT_NAME_SIZE 10
|
||||
struct slot {
|
||||
u8 bus;
|
||||
u8 device;
|
||||
u32 number;
|
||||
u8 state;
|
||||
struct timer_list task_event;
|
||||
u8 hp_slot;
|
||||
struct controller *ctrl;
|
||||
struct hpc_ops *hpc_ops;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct list_head slot_list;
|
||||
char name[SLOT_NAME_SIZE];
|
||||
unsigned long last_emi_toggle;
|
||||
};
|
||||
|
||||
struct event_info {
|
||||
u32 event_type;
|
||||
u8 hp_slot;
|
||||
};
|
||||
|
||||
#define MAX_EVENTS 10
|
||||
struct controller {
|
||||
struct controller *next;
|
||||
struct mutex crit_sect; /* critical section mutex */
|
||||
struct mutex ctrl_lock; /* controller lock */
|
||||
int num_slots; /* Number of slots on ctlr */
|
||||
int slot_num_inc; /* 1 or -1 */
|
||||
struct pci_dev *pci_dev;
|
||||
struct list_head slot_list;
|
||||
struct event_info event_queue[MAX_EVENTS];
|
||||
struct slot *slot;
|
||||
struct hpc_ops *hpc_ops;
|
||||
wait_queue_head_t queue; /* sleep & wake process */
|
||||
u8 next_event;
|
||||
u8 bus;
|
||||
u8 device;
|
||||
u8 function;
|
||||
u8 slot_device_offset;
|
||||
u32 first_slot; /* First physical slot number */ /* PCIE only has 1 slot */
|
||||
u8 slot_bus; /* Bus where the slots handled by this controller sit */
|
||||
u8 ctrlcap;
|
||||
u16 vendor_id;
|
||||
u8 cap_base;
|
||||
struct timer_list poll_timer;
|
||||
volatile int cmd_busy;
|
||||
};
|
||||
|
||||
#define INT_BUTTON_IGNORE 0
|
||||
#define INT_PRESENCE_ON 1
|
||||
#define INT_PRESENCE_OFF 2
|
||||
#define INT_SWITCH_CLOSE 3
|
||||
#define INT_SWITCH_OPEN 4
|
||||
#define INT_POWER_FAULT 5
|
||||
#define INT_POWER_FAULT_CLEAR 6
|
||||
#define INT_BUTTON_PRESS 7
|
||||
#define INT_BUTTON_RELEASE 8
|
||||
#define INT_BUTTON_CANCEL 9
|
||||
|
||||
#define STATIC_STATE 0
|
||||
#define BLINKINGON_STATE 1
|
||||
#define BLINKINGOFF_STATE 2
|
||||
#define POWERON_STATE 3
|
||||
#define POWEROFF_STATE 4
|
||||
|
||||
/* Error messages */
|
||||
#define INTERLOCK_OPEN 0x00000002
|
||||
#define ADD_NOT_SUPPORTED 0x00000003
|
||||
#define CARD_FUNCTIONING 0x00000005
|
||||
#define ADAPTER_NOT_SAME 0x00000006
|
||||
#define NO_ADAPTER_PRESENT 0x00000009
|
||||
#define NOT_ENOUGH_RESOURCES 0x0000000B
|
||||
#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
|
||||
#define WRONG_BUS_FREQUENCY 0x0000000D
|
||||
#define POWER_FAILURE 0x0000000E
|
||||
|
||||
/* Field definitions in Slot Capabilities Register */
|
||||
#define ATTN_BUTTN_PRSN 0x00000001
|
||||
#define PWR_CTRL_PRSN 0x00000002
|
||||
#define MRL_SENS_PRSN 0x00000004
|
||||
#define ATTN_LED_PRSN 0x00000008
|
||||
#define PWR_LED_PRSN 0x00000010
|
||||
#define HP_SUPR_RM_SUP 0x00000020
|
||||
#define EMI_PRSN 0x00020000
|
||||
|
||||
#define ATTN_BUTTN(cap) (cap & ATTN_BUTTN_PRSN)
|
||||
#define POWER_CTRL(cap) (cap & PWR_CTRL_PRSN)
|
||||
#define MRL_SENS(cap) (cap & MRL_SENS_PRSN)
|
||||
#define ATTN_LED(cap) (cap & ATTN_LED_PRSN)
|
||||
#define PWR_LED(cap) (cap & PWR_LED_PRSN)
|
||||
#define HP_SUPR_RM(cap) (cap & HP_SUPR_RM_SUP)
|
||||
#define EMI(cap) (cap & EMI_PRSN)
|
||||
|
||||
extern int pciehp_event_start_thread(void);
|
||||
extern void pciehp_event_stop_thread(void);
|
||||
extern int pciehp_enable_slot(struct slot *slot);
|
||||
extern int pciehp_disable_slot(struct slot *slot);
|
||||
extern u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl);
|
||||
extern int pciehp_configure_device(struct slot *p_slot);
|
||||
extern int pciehp_unconfigure_device(struct slot *p_slot);
|
||||
int pcie_init(struct controller *ctrl, struct pcie_device *dev);
|
||||
|
||||
/* Global variables */
|
||||
extern struct controller *pciehp_ctrl_list;
|
||||
|
||||
static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device)
|
||||
{
|
||||
struct slot *slot;
|
||||
|
||||
list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
|
||||
if (slot->device == device)
|
||||
return slot;
|
||||
}
|
||||
|
||||
err("%s: slot (device=0x%x) not found\n", __FUNCTION__, device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct hpc_ops {
|
||||
int (*power_on_slot)(struct slot *slot);
|
||||
int (*power_off_slot)(struct slot *slot);
|
||||
int (*get_power_status)(struct slot *slot, u8 *status);
|
||||
int (*get_attention_status)(struct slot *slot, u8 *status);
|
||||
int (*set_attention_status)(struct slot *slot, u8 status);
|
||||
int (*get_latch_status)(struct slot *slot, u8 *status);
|
||||
int (*get_adapter_status)(struct slot *slot, u8 *status);
|
||||
int (*get_emi_status)(struct slot *slot, u8 *status);
|
||||
int (*toggle_emi)(struct slot *slot);
|
||||
int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
|
||||
int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
|
||||
int (*get_max_lnk_width)(struct slot *slot, enum pcie_link_width *val);
|
||||
int (*get_cur_lnk_width)(struct slot *slot, enum pcie_link_width *val);
|
||||
int (*query_power_fault)(struct slot *slot);
|
||||
void (*green_led_on)(struct slot *slot);
|
||||
void (*green_led_off)(struct slot *slot);
|
||||
void (*green_led_blink)(struct slot *slot);
|
||||
void (*release_ctlr)(struct controller *ctrl);
|
||||
int (*check_lnk_status)(struct controller *ctrl);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
#include <acpi/acpi.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include <acpi/actypes.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
|
||||
#define pciehp_get_hp_hw_control_from_firmware(dev) \
|
||||
pciehp_acpi_get_hp_hw_control_from_firmware(dev)
|
||||
static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev,
|
||||
struct hotplug_params *hpp)
|
||||
{
|
||||
if (ACPI_FAILURE(acpi_get_hp_params_from_firmware(dev->bus, hpp)))
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define pciehp_get_hp_hw_control_from_firmware(dev) 0
|
||||
#define pciehp_get_hp_params_from_firmware(dev, hpp) (-ENODEV)
|
||||
#endif /* CONFIG_ACPI */
|
||||
#endif /* _PCIEHP_H */
|
||||
617
drivers/pci/hotplug/pciehp_core.c
Normal file
617
drivers/pci/hotplug/pciehp_core.c
Normal file
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* PCI Express Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include "pciehp.h"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
/* Global variables */
|
||||
int pciehp_debug;
|
||||
int pciehp_poll_mode;
|
||||
int pciehp_poll_time;
|
||||
int pciehp_force;
|
||||
struct controller *pciehp_ctrl_list;
|
||||
|
||||
#define DRIVER_VERSION "0.4"
|
||||
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
|
||||
#define DRIVER_DESC "PCI Express Hot Plug Controller Driver"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param(pciehp_debug, bool, 0644);
|
||||
module_param(pciehp_poll_mode, bool, 0644);
|
||||
module_param(pciehp_poll_time, int, 0644);
|
||||
module_param(pciehp_force, bool, 0644);
|
||||
MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
|
||||
MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
|
||||
MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
|
||||
MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
|
||||
|
||||
#define PCIE_MODULE_NAME "pciehp"
|
||||
|
||||
static int pcie_start_thread (void);
|
||||
static int set_attention_status (struct hotplug_slot *slot, u8 value);
|
||||
static int enable_slot (struct hotplug_slot *slot);
|
||||
static int disable_slot (struct hotplug_slot *slot);
|
||||
static int get_power_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_address (struct hotplug_slot *slot, u32 *value);
|
||||
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
|
||||
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
|
||||
|
||||
static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_attention_status = set_attention_status,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.get_power_status = get_power_status,
|
||||
.get_attention_status = get_attention_status,
|
||||
.get_latch_status = get_latch_status,
|
||||
.get_adapter_status = get_adapter_status,
|
||||
.get_address = get_address,
|
||||
.get_max_bus_speed = get_max_bus_speed,
|
||||
.get_cur_bus_speed = get_cur_bus_speed,
|
||||
};
|
||||
|
||||
/*
|
||||
* Check the status of the Electro Mechanical Interlock (EMI)
|
||||
*/
|
||||
static int get_lock_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
return (slot->hpc_ops->get_emi_status(slot, value));
|
||||
}
|
||||
|
||||
/*
|
||||
* sysfs interface for the Electro Mechanical Interlock (EMI)
|
||||
* 1 == locked, 0 == unlocked
|
||||
*/
|
||||
static ssize_t lock_read_file(struct hotplug_slot *slot, char *buf)
|
||||
{
|
||||
int retval;
|
||||
u8 value;
|
||||
|
||||
retval = get_lock_status(slot, &value);
|
||||
if (retval)
|
||||
goto lock_read_exit;
|
||||
retval = sprintf (buf, "%d\n", value);
|
||||
|
||||
lock_read_exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the status of the Electro Mechanical Interlock (EMI)
|
||||
* This is a toggle - in addition there must be at least 1 second
|
||||
* in between toggles.
|
||||
*/
|
||||
static int set_lock_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval;
|
||||
u8 value;
|
||||
|
||||
mutex_lock(&slot->ctrl->crit_sect);
|
||||
|
||||
/* has it been >1 sec since our last toggle? */
|
||||
if ((get_seconds() - slot->last_emi_toggle) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* see what our current state is */
|
||||
retval = get_lock_status(hotplug_slot, &value);
|
||||
if (retval || (value == status))
|
||||
goto set_lock_exit;
|
||||
|
||||
slot->hpc_ops->toggle_emi(slot);
|
||||
set_lock_exit:
|
||||
mutex_unlock(&slot->ctrl->crit_sect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sysfs interface which allows the user to toggle the Electro Mechanical
|
||||
* Interlock. Valid values are either 0 or 1. 0 == unlock, 1 == lock
|
||||
*/
|
||||
static ssize_t lock_write_file(struct hotplug_slot *slot, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long llock;
|
||||
u8 lock;
|
||||
int retval = 0;
|
||||
|
||||
llock = simple_strtoul(buf, NULL, 10);
|
||||
lock = (u8)(llock & 0xff);
|
||||
|
||||
switch (lock) {
|
||||
case 0:
|
||||
case 1:
|
||||
retval = set_lock_status(slot, lock);
|
||||
break;
|
||||
default:
|
||||
err ("%d is an invalid lock value\n", lock);
|
||||
retval = -EINVAL;
|
||||
}
|
||||
if (retval)
|
||||
return retval;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute hotplug_slot_attr_lock = {
|
||||
.attr = {.name = "lock", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
||||
.show = lock_read_file,
|
||||
.store = lock_write_file
|
||||
};
|
||||
|
||||
/**
|
||||
* release_slot - free up the memory used by a slot
|
||||
* @hotplug_slot: slot to free
|
||||
*/
|
||||
static void release_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
kfree(slot->hotplug_slot->info);
|
||||
kfree(slot->hotplug_slot);
|
||||
kfree(slot);
|
||||
}
|
||||
|
||||
static void make_slot_name(struct slot *slot)
|
||||
{
|
||||
snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%04d_%04d",
|
||||
slot->bus, slot->number);
|
||||
}
|
||||
|
||||
static int init_slots(struct controller *ctrl)
|
||||
{
|
||||
struct slot *slot;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct hotplug_slot_info *info;
|
||||
int retval = -ENOMEM;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ctrl->num_slots; i++) {
|
||||
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
||||
if (!slot)
|
||||
goto error;
|
||||
|
||||
hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
|
||||
if (!hotplug_slot)
|
||||
goto error_slot;
|
||||
slot->hotplug_slot = hotplug_slot;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
goto error_hpslot;
|
||||
hotplug_slot->info = info;
|
||||
|
||||
hotplug_slot->name = slot->name;
|
||||
|
||||
slot->hp_slot = i;
|
||||
slot->ctrl = ctrl;
|
||||
slot->bus = ctrl->pci_dev->subordinate->number;
|
||||
slot->device = ctrl->slot_device_offset + i;
|
||||
slot->hpc_ops = ctrl->hpc_ops;
|
||||
slot->number = ctrl->first_slot;
|
||||
|
||||
/* register this slot with the hotplug pci core */
|
||||
hotplug_slot->private = slot;
|
||||
hotplug_slot->release = &release_slot;
|
||||
make_slot_name(slot);
|
||||
hotplug_slot->ops = &pciehp_hotplug_slot_ops;
|
||||
|
||||
get_power_status(hotplug_slot, &info->power_status);
|
||||
get_attention_status(hotplug_slot, &info->attention_status);
|
||||
get_latch_status(hotplug_slot, &info->latch_status);
|
||||
get_adapter_status(hotplug_slot, &info->adapter_status);
|
||||
|
||||
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
|
||||
"slot_device_offset=%x\n", slot->bus, slot->device,
|
||||
slot->hp_slot, slot->number, ctrl->slot_device_offset);
|
||||
retval = pci_hp_register(hotplug_slot);
|
||||
if (retval) {
|
||||
err ("pci_hp_register failed with error %d\n", retval);
|
||||
goto error_info;
|
||||
}
|
||||
/* create additional sysfs entries */
|
||||
if (EMI(ctrl->ctrlcap)) {
|
||||
retval = sysfs_create_file(&hotplug_slot->kobj,
|
||||
&hotplug_slot_attr_lock.attr);
|
||||
if (retval) {
|
||||
pci_hp_deregister(hotplug_slot);
|
||||
err("cannot create additional sysfs entries\n");
|
||||
goto error_info;
|
||||
}
|
||||
}
|
||||
|
||||
list_add(&slot->slot_list, &ctrl->slot_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
error_info:
|
||||
kfree(info);
|
||||
error_hpslot:
|
||||
kfree(hotplug_slot);
|
||||
error_slot:
|
||||
kfree(slot);
|
||||
error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void cleanup_slots(struct controller *ctrl)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct list_head *next;
|
||||
struct slot *slot;
|
||||
|
||||
list_for_each_safe(tmp, next, &ctrl->slot_list) {
|
||||
slot = list_entry(tmp, struct slot, slot_list);
|
||||
list_del(&slot->slot_list);
|
||||
if (EMI(ctrl->ctrlcap))
|
||||
sysfs_remove_file(&slot->hotplug_slot->kobj,
|
||||
&hotplug_slot_attr_lock.attr);
|
||||
pci_hp_deregister(slot->hotplug_slot);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set_attention_status - Turns the Amber LED for a slot on, off or blink
|
||||
*/
|
||||
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
hotplug_slot->info->attention_status = status;
|
||||
|
||||
if (ATTN_LED(slot->ctrl->ctrlcap))
|
||||
slot->hpc_ops->set_attention_status(slot, status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
return pciehp_enable_slot(slot);
|
||||
}
|
||||
|
||||
|
||||
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
return pciehp_disable_slot(slot);
|
||||
}
|
||||
|
||||
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_power_status(slot, value);
|
||||
if (retval < 0)
|
||||
*value = hotplug_slot->info->power_status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_attention_status(slot, value);
|
||||
if (retval < 0)
|
||||
*value = hotplug_slot->info->attention_status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_latch_status(slot, value);
|
||||
if (retval < 0)
|
||||
*value = hotplug_slot->info->latch_status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_adapter_status(slot, value);
|
||||
if (retval < 0)
|
||||
*value = hotplug_slot->info->adapter_status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
*value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_max_bus_speed(slot, value);
|
||||
if (retval < 0)
|
||||
*value = PCI_SPEED_UNKNOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
|
||||
if (retval < 0)
|
||||
*value = PCI_SPEED_UNKNOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_id *id)
|
||||
{
|
||||
int rc;
|
||||
struct controller *ctrl;
|
||||
struct slot *t_slot;
|
||||
u8 value;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
|
||||
if (!ctrl) {
|
||||
err("%s : out of memory\n", __FUNCTION__);
|
||||
goto err_out_none;
|
||||
}
|
||||
INIT_LIST_HEAD(&ctrl->slot_list);
|
||||
|
||||
pdev = dev->port;
|
||||
ctrl->pci_dev = pdev;
|
||||
|
||||
rc = pcie_init(ctrl, dev);
|
||||
if (rc) {
|
||||
dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
|
||||
goto err_out_free_ctrl;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, ctrl);
|
||||
|
||||
ctrl->bus = pdev->bus->number; /* ctrl bus */
|
||||
ctrl->slot_bus = pdev->subordinate->number; /* bus controlled by this HPC */
|
||||
|
||||
ctrl->device = PCI_SLOT(pdev->devfn);
|
||||
ctrl->function = PCI_FUNC(pdev->devfn);
|
||||
dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", __FUNCTION__,
|
||||
ctrl->bus, ctrl->device, ctrl->function, pdev->irq);
|
||||
|
||||
/* Setup the slot information structures */
|
||||
rc = init_slots(ctrl);
|
||||
if (rc) {
|
||||
err("%s: slot initialization failed\n", PCIE_MODULE_NAME);
|
||||
goto err_out_release_ctlr;
|
||||
}
|
||||
|
||||
t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
|
||||
|
||||
/* Finish setting up the hot plug ctrl device */
|
||||
ctrl->next_event = 0;
|
||||
|
||||
if (!pciehp_ctrl_list) {
|
||||
pciehp_ctrl_list = ctrl;
|
||||
ctrl->next = NULL;
|
||||
} else {
|
||||
ctrl->next = pciehp_ctrl_list;
|
||||
pciehp_ctrl_list = ctrl;
|
||||
}
|
||||
|
||||
t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */
|
||||
if ((POWER_CTRL(ctrl->ctrlcap)) && !value) {
|
||||
rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/
|
||||
if (rc)
|
||||
goto err_out_free_ctrl_slot;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out_free_ctrl_slot:
|
||||
cleanup_slots(ctrl);
|
||||
err_out_release_ctlr:
|
||||
ctrl->hpc_ops->release_ctlr(ctrl);
|
||||
err_out_free_ctrl:
|
||||
kfree(ctrl);
|
||||
err_out_none:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static int pcie_start_thread(void)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
dbg("Initialize + Start the notification/polling mechanism \n");
|
||||
|
||||
retval = pciehp_event_start_thread();
|
||||
if (retval) {
|
||||
dbg("pciehp_event_start_thread() failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit unload_pciehpd(void)
|
||||
{
|
||||
struct controller *ctrl;
|
||||
struct controller *tctrl;
|
||||
|
||||
ctrl = pciehp_ctrl_list;
|
||||
|
||||
while (ctrl) {
|
||||
cleanup_slots(ctrl);
|
||||
|
||||
ctrl->hpc_ops->release_ctlr(ctrl);
|
||||
|
||||
tctrl = ctrl;
|
||||
ctrl = ctrl->next;
|
||||
|
||||
kfree(tctrl);
|
||||
}
|
||||
|
||||
/* Stop the notification mechanism */
|
||||
pciehp_event_stop_thread();
|
||||
|
||||
}
|
||||
|
||||
static void pciehp_remove (struct pcie_device *device)
|
||||
{
|
||||
/* XXX - Needs to be adapted to device driver model */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pciehp_suspend (struct pcie_device *dev, pm_message_t state)
|
||||
{
|
||||
printk("%s ENTRY\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pciehp_resume (struct pcie_device *dev)
|
||||
{
|
||||
printk("%s ENTRY\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct pcie_port_service_id port_pci_ids[] = { {
|
||||
.vendor = PCI_ANY_ID,
|
||||
.device = PCI_ANY_ID,
|
||||
.port_type = PCIE_ANY_PORT,
|
||||
.service_type = PCIE_PORT_SERVICE_HP,
|
||||
.driver_data = 0,
|
||||
}, { /* end: all zeroes */ }
|
||||
};
|
||||
static const char device_name[] = "hpdriver";
|
||||
|
||||
static struct pcie_port_service_driver hpdriver_portdrv = {
|
||||
.name = (char *)device_name,
|
||||
.id_table = &port_pci_ids[0],
|
||||
|
||||
.probe = pciehp_probe,
|
||||
.remove = pciehp_remove,
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = pciehp_suspend,
|
||||
.resume = pciehp_resume,
|
||||
#endif /* PM */
|
||||
};
|
||||
|
||||
static int __init pcied_init(void)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
|
||||
pciehp_poll_mode = 1;
|
||||
#endif
|
||||
|
||||
retval = pcie_start_thread();
|
||||
if (retval)
|
||||
goto error_hpc_init;
|
||||
|
||||
retval = pcie_port_service_register(&hpdriver_portdrv);
|
||||
dbg("pcie_port_service_register = %d\n", retval);
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
if (retval)
|
||||
dbg("%s: Failure to register service\n", __FUNCTION__);
|
||||
|
||||
error_hpc_init:
|
||||
if (retval) {
|
||||
pciehp_event_stop_thread();
|
||||
};
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit pcied_cleanup(void)
|
||||
{
|
||||
dbg("unload_pciehpd()\n");
|
||||
unload_pciehpd();
|
||||
|
||||
pcie_port_service_unregister(&hpdriver_portdrv);
|
||||
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
|
||||
}
|
||||
|
||||
module_init(pcied_init);
|
||||
module_exit(pcied_cleanup);
|
||||
741
drivers/pci/hotplug/pciehp_ctrl.c
Normal file
741
drivers/pci/hotplug/pciehp_ctrl.c
Normal file
@@ -0,0 +1,741 @@
|
||||
/*
|
||||
* PCI Express Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/pci.h>
|
||||
#include "../pci.h"
|
||||
#include "pciehp.h"
|
||||
|
||||
static void interrupt_event_handler(struct controller *ctrl);
|
||||
|
||||
static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
|
||||
static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */
|
||||
static int event_finished;
|
||||
static unsigned long pushbutton_pending; /* = 0 */
|
||||
static unsigned long surprise_rm_pending; /* = 0 */
|
||||
|
||||
static inline char *slot_name(struct slot *p_slot)
|
||||
{
|
||||
return p_slot->hotplug_slot->name;
|
||||
}
|
||||
|
||||
u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u8 rc = 0;
|
||||
u8 getstatus;
|
||||
struct event_info *taskInfo;
|
||||
|
||||
/* Attention Button Change */
|
||||
dbg("pciehp: Attention button interrupt received.\n");
|
||||
|
||||
/* This is the structure that tells the worker thread what to do */
|
||||
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
|
||||
ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS;
|
||||
taskInfo->hp_slot = hp_slot;
|
||||
|
||||
rc++;
|
||||
|
||||
/*
|
||||
* Button pressed - See if need to TAKE ACTION!!!
|
||||
*/
|
||||
info("Button pressed on Slot(%s)\n", slot_name(p_slot));
|
||||
taskInfo->event_type = INT_BUTTON_PRESS;
|
||||
|
||||
if ((p_slot->state == BLINKINGON_STATE)
|
||||
|| (p_slot->state == BLINKINGOFF_STATE)) {
|
||||
/* Cancel if we are still blinking; this means that we press the
|
||||
* attention again before the 5 sec. limit expires to cancel hot-add
|
||||
* or hot-remove
|
||||
*/
|
||||
taskInfo->event_type = INT_BUTTON_CANCEL;
|
||||
info("Button cancel on Slot(%s)\n", slot_name(p_slot));
|
||||
} else if ((p_slot->state == POWERON_STATE)
|
||||
|| (p_slot->state == POWEROFF_STATE)) {
|
||||
/* Ignore if the slot is on power-on or power-off state; this
|
||||
* means that the previous attention button action to hot-add or
|
||||
* hot-remove is undergoing
|
||||
*/
|
||||
taskInfo->event_type = INT_BUTTON_IGNORE;
|
||||
info("Button ignore on Slot(%s)\n", slot_name(p_slot));
|
||||
}
|
||||
|
||||
if (rc)
|
||||
up(&event_semaphore); /* signal event thread that new event is posted */
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u8 rc = 0;
|
||||
u8 getstatus;
|
||||
struct event_info *taskInfo;
|
||||
|
||||
/* Switch Change */
|
||||
dbg("pciehp: Switch interrupt received.\n");
|
||||
|
||||
/* This is the structure that tells the worker thread
|
||||
* what to do
|
||||
*/
|
||||
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
|
||||
ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS;
|
||||
taskInfo->hp_slot = hp_slot;
|
||||
|
||||
rc++;
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
|
||||
if (getstatus) {
|
||||
/*
|
||||
* Switch opened
|
||||
*/
|
||||
info("Latch open on Slot(%s)\n", slot_name(p_slot));
|
||||
taskInfo->event_type = INT_SWITCH_OPEN;
|
||||
} else {
|
||||
/*
|
||||
* Switch closed
|
||||
*/
|
||||
info("Latch close on Slot(%s)\n", slot_name(p_slot));
|
||||
taskInfo->event_type = INT_SWITCH_CLOSE;
|
||||
}
|
||||
|
||||
if (rc)
|
||||
up(&event_semaphore); /* signal event thread that new event is posted */
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u8 presence_save, rc = 0;
|
||||
struct event_info *taskInfo;
|
||||
|
||||
/* Presence Change */
|
||||
dbg("pciehp: Presence/Notify input change.\n");
|
||||
|
||||
/* This is the structure that tells the worker thread
|
||||
* what to do
|
||||
*/
|
||||
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
|
||||
ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS;
|
||||
taskInfo->hp_slot = hp_slot;
|
||||
|
||||
rc++;
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
/* Switch is open, assume a presence change
|
||||
* Save the presence state
|
||||
*/
|
||||
p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save);
|
||||
if (presence_save) {
|
||||
/*
|
||||
* Card Present
|
||||
*/
|
||||
info("Card present on Slot(%s)\n", slot_name(p_slot));
|
||||
taskInfo->event_type = INT_PRESENCE_ON;
|
||||
} else {
|
||||
/*
|
||||
* Not Present
|
||||
*/
|
||||
info("Card not present on Slot(%s)\n", slot_name(p_slot));
|
||||
taskInfo->event_type = INT_PRESENCE_OFF;
|
||||
}
|
||||
|
||||
if (rc)
|
||||
up(&event_semaphore); /* signal event thread that new event is posted */
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u8 rc = 0;
|
||||
struct event_info *taskInfo;
|
||||
|
||||
/* power fault */
|
||||
dbg("pciehp: Power fault interrupt received.\n");
|
||||
|
||||
/* this is the structure that tells the worker thread
|
||||
* what to do
|
||||
*/
|
||||
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
|
||||
ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS;
|
||||
taskInfo->hp_slot = hp_slot;
|
||||
|
||||
rc++;
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
|
||||
/*
|
||||
* power fault Cleared
|
||||
*/
|
||||
info("Power fault cleared on Slot(%s)\n", slot_name(p_slot));
|
||||
taskInfo->event_type = INT_POWER_FAULT_CLEAR;
|
||||
} else {
|
||||
/*
|
||||
* power fault
|
||||
*/
|
||||
info("Power fault on Slot(%s)\n", slot_name(p_slot));
|
||||
taskInfo->event_type = INT_POWER_FAULT;
|
||||
info("power fault bit %x set\n", hp_slot);
|
||||
}
|
||||
if (rc)
|
||||
up(&event_semaphore); /* signal event thread that new event is posted */
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The following routines constitute the bulk of the
|
||||
hotplug controller logic
|
||||
*/
|
||||
|
||||
static void set_slot_off(struct controller *ctrl, struct slot * pslot)
|
||||
{
|
||||
/* turn off slot, turn on Amber LED, turn off Green LED if supported*/
|
||||
if (POWER_CTRL(ctrl->ctrlcap)) {
|
||||
if (pslot->hpc_ops->power_off_slot(pslot)) {
|
||||
err("%s: Issue of Slot Power Off command failed\n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (PWR_LED(ctrl->ctrlcap))
|
||||
pslot->hpc_ops->green_led_off(pslot);
|
||||
|
||||
if (ATTN_LED(ctrl->ctrlcap)) {
|
||||
if (pslot->hpc_ops->set_attention_status(pslot, 1)) {
|
||||
err("%s: Issue of Set Attention Led command failed\n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* board_added - Called after a board has been added to the system.
|
||||
*
|
||||
* Turns power on for the board
|
||||
* Configures board
|
||||
*
|
||||
*/
|
||||
static int board_added(struct slot *p_slot)
|
||||
{
|
||||
u8 hp_slot;
|
||||
int retval = 0;
|
||||
struct controller *ctrl = p_slot->ctrl;
|
||||
|
||||
hp_slot = p_slot->device - ctrl->slot_device_offset;
|
||||
|
||||
dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n",
|
||||
__FUNCTION__, p_slot->device,
|
||||
ctrl->slot_device_offset, hp_slot);
|
||||
|
||||
if (POWER_CTRL(ctrl->ctrlcap)) {
|
||||
/* Power on slot */
|
||||
retval = p_slot->hpc_ops->power_on_slot(p_slot);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (PWR_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->green_led_blink(p_slot);
|
||||
|
||||
/* Wait for ~1 second */
|
||||
msleep(1000);
|
||||
|
||||
/* Check link training status */
|
||||
retval = p_slot->hpc_ops->check_lnk_status(ctrl);
|
||||
if (retval) {
|
||||
err("%s: Failed to check link status\n", __FUNCTION__);
|
||||
set_slot_off(ctrl, p_slot);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Check for a power fault */
|
||||
if (p_slot->hpc_ops->query_power_fault(p_slot)) {
|
||||
dbg("%s: power fault detected\n", __FUNCTION__);
|
||||
retval = POWER_FAILURE;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
retval = pciehp_configure_device(p_slot);
|
||||
if (retval) {
|
||||
err("Cannot add device 0x%x:%x\n", p_slot->bus,
|
||||
p_slot->device);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some PCI Express root ports require fixup after hot-plug operation.
|
||||
*/
|
||||
if (pcie_mch_quirk)
|
||||
pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
|
||||
if (PWR_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->green_led_on(p_slot);
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
set_slot_off(ctrl, p_slot);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove_board - Turns off slot and LED's
|
||||
*
|
||||
*/
|
||||
static int remove_board(struct slot *p_slot)
|
||||
{
|
||||
u8 device;
|
||||
u8 hp_slot;
|
||||
int retval = 0;
|
||||
struct controller *ctrl = p_slot->ctrl;
|
||||
|
||||
retval = pciehp_unconfigure_device(p_slot);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
device = p_slot->device;
|
||||
hp_slot = p_slot->device - ctrl->slot_device_offset;
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
|
||||
|
||||
if (POWER_CTRL(ctrl->ctrlcap)) {
|
||||
/* power off slot */
|
||||
retval = p_slot->hpc_ops->power_off_slot(p_slot);
|
||||
if (retval) {
|
||||
err("%s: Issue of Slot Disable command failed\n",
|
||||
__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (PWR_LED(ctrl->ctrlcap))
|
||||
/* turn off Green LED */
|
||||
p_slot->hpc_ops->green_led_off(p_slot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void pushbutton_helper_thread(unsigned long data)
|
||||
{
|
||||
pushbutton_pending = data;
|
||||
|
||||
up(&event_semaphore);
|
||||
}
|
||||
|
||||
/**
|
||||
* pciehp_pushbutton_thread
|
||||
*
|
||||
* Scheduled procedure to handle blocking stuff for the pushbuttons
|
||||
* Handles all pending events and exits.
|
||||
*
|
||||
*/
|
||||
static void pciehp_pushbutton_thread(unsigned long slot)
|
||||
{
|
||||
struct slot *p_slot = (struct slot *) slot;
|
||||
u8 getstatus;
|
||||
|
||||
pushbutton_pending = 0;
|
||||
|
||||
if (!p_slot) {
|
||||
dbg("%s: Error! slot NULL\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
|
||||
if (getstatus) {
|
||||
p_slot->state = POWEROFF_STATE;
|
||||
dbg("%s: disabling bus:device(%x:%x)\n", __FUNCTION__,
|
||||
p_slot->bus, p_slot->device);
|
||||
|
||||
pciehp_disable_slot(p_slot);
|
||||
p_slot->state = STATIC_STATE;
|
||||
} else {
|
||||
p_slot->state = POWERON_STATE;
|
||||
dbg("%s: adding bus:device(%x:%x)\n", __FUNCTION__,
|
||||
p_slot->bus, p_slot->device);
|
||||
|
||||
if (pciehp_enable_slot(p_slot) &&
|
||||
PWR_LED(p_slot->ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->green_led_off(p_slot);
|
||||
|
||||
p_slot->state = STATIC_STATE;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* pciehp_surprise_rm_thread
|
||||
*
|
||||
* Scheduled procedure to handle blocking stuff for the surprise removal
|
||||
* Handles all pending events and exits.
|
||||
*
|
||||
*/
|
||||
static void pciehp_surprise_rm_thread(unsigned long slot)
|
||||
{
|
||||
struct slot *p_slot = (struct slot *) slot;
|
||||
u8 getstatus;
|
||||
|
||||
surprise_rm_pending = 0;
|
||||
|
||||
if (!p_slot) {
|
||||
dbg("%s: Error! slot NULL\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
|
||||
if (!getstatus) {
|
||||
p_slot->state = POWEROFF_STATE;
|
||||
dbg("%s: removing bus:device(%x:%x)\n",
|
||||
__FUNCTION__, p_slot->bus, p_slot->device);
|
||||
|
||||
pciehp_disable_slot(p_slot);
|
||||
p_slot->state = STATIC_STATE;
|
||||
} else {
|
||||
p_slot->state = POWERON_STATE;
|
||||
dbg("%s: adding bus:device(%x:%x)\n",
|
||||
__FUNCTION__, p_slot->bus, p_slot->device);
|
||||
|
||||
if (pciehp_enable_slot(p_slot) &&
|
||||
PWR_LED(p_slot->ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->green_led_off(p_slot);
|
||||
|
||||
p_slot->state = STATIC_STATE;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* this is the main worker thread */
|
||||
static int event_thread(void* data)
|
||||
{
|
||||
struct controller *ctrl;
|
||||
lock_kernel();
|
||||
daemonize("pciehpd_event");
|
||||
|
||||
unlock_kernel();
|
||||
|
||||
while (1) {
|
||||
dbg("!!!!event_thread sleeping\n");
|
||||
down_interruptible (&event_semaphore);
|
||||
dbg("event_thread woken finished = %d\n", event_finished);
|
||||
if (event_finished || signal_pending(current))
|
||||
break;
|
||||
/* Do stuff here */
|
||||
if (pushbutton_pending)
|
||||
pciehp_pushbutton_thread(pushbutton_pending);
|
||||
else if (surprise_rm_pending)
|
||||
pciehp_surprise_rm_thread(surprise_rm_pending);
|
||||
else
|
||||
for (ctrl = pciehp_ctrl_list; ctrl; ctrl=ctrl->next)
|
||||
interrupt_event_handler(ctrl);
|
||||
}
|
||||
dbg("event_thread signals exit\n");
|
||||
up(&event_exit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pciehp_event_start_thread(void)
|
||||
{
|
||||
int pid;
|
||||
|
||||
/* initialize our semaphores */
|
||||
init_MUTEX_LOCKED(&event_exit);
|
||||
event_finished=0;
|
||||
|
||||
init_MUTEX_LOCKED(&event_semaphore);
|
||||
pid = kernel_thread(event_thread, NULL, 0);
|
||||
|
||||
if (pid < 0) {
|
||||
err ("Can't start up our event thread\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void pciehp_event_stop_thread(void)
|
||||
{
|
||||
event_finished = 1;
|
||||
up(&event_semaphore);
|
||||
down(&event_exit);
|
||||
}
|
||||
|
||||
|
||||
static int update_slot_info(struct slot *slot)
|
||||
{
|
||||
struct hotplug_slot_info *info;
|
||||
/* char buffer[SLOT_NAME_SIZE]; */
|
||||
int result;
|
||||
|
||||
info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
/* make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); */
|
||||
|
||||
slot->hpc_ops->get_power_status(slot, &(info->power_status));
|
||||
slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
|
||||
slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
|
||||
slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
|
||||
|
||||
/* result = pci_hp_change_slot_info(buffer, info); */
|
||||
result = pci_hp_change_slot_info(slot->hotplug_slot, info);
|
||||
kfree (info);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void interrupt_event_handler(struct controller *ctrl)
|
||||
{
|
||||
int loop = 0;
|
||||
int change = 1;
|
||||
u8 hp_slot;
|
||||
u8 getstatus;
|
||||
struct slot *p_slot;
|
||||
|
||||
while (change) {
|
||||
change = 0;
|
||||
|
||||
for (loop = 0; loop < MAX_EVENTS; loop++) {
|
||||
if (ctrl->event_queue[loop].event_type != 0) {
|
||||
hp_slot = ctrl->event_queue[loop].hp_slot;
|
||||
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) {
|
||||
dbg("button cancel\n");
|
||||
del_timer(&p_slot->task_event);
|
||||
|
||||
switch (p_slot->state) {
|
||||
case BLINKINGOFF_STATE:
|
||||
if (PWR_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->green_led_on(p_slot);
|
||||
|
||||
if (ATTN_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->set_attention_status(p_slot, 0);
|
||||
break;
|
||||
case BLINKINGON_STATE:
|
||||
if (PWR_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->green_led_off(p_slot);
|
||||
|
||||
if (ATTN_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->set_attention_status(p_slot, 0);
|
||||
break;
|
||||
default:
|
||||
warn("Not a valid state\n");
|
||||
return;
|
||||
}
|
||||
info("PCI slot #%s - action canceled due to button press.\n", slot_name(p_slot));
|
||||
p_slot->state = STATIC_STATE;
|
||||
}
|
||||
/* ***********Button Pressed (No action on 1st press...) */
|
||||
else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
|
||||
|
||||
if (ATTN_BUTTN(ctrl->ctrlcap)) {
|
||||
dbg("Button pressed\n");
|
||||
p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
|
||||
if (getstatus) {
|
||||
/* slot is on */
|
||||
dbg("slot is on\n");
|
||||
p_slot->state = BLINKINGOFF_STATE;
|
||||
info("PCI slot #%s - powering off due to button press.\n", slot_name(p_slot));
|
||||
} else {
|
||||
/* slot is off */
|
||||
dbg("slot is off\n");
|
||||
p_slot->state = BLINKINGON_STATE;
|
||||
info("PCI slot #%s - powering on due to button press.\n", slot_name(p_slot));
|
||||
}
|
||||
|
||||
/* blink green LED and turn off amber */
|
||||
if (PWR_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->green_led_blink(p_slot);
|
||||
|
||||
if (ATTN_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->set_attention_status(p_slot, 0);
|
||||
|
||||
init_timer(&p_slot->task_event);
|
||||
p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
|
||||
p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread;
|
||||
p_slot->task_event.data = (unsigned long) p_slot;
|
||||
|
||||
add_timer(&p_slot->task_event);
|
||||
}
|
||||
}
|
||||
/***********POWER FAULT********************/
|
||||
else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
|
||||
if (POWER_CTRL(ctrl->ctrlcap)) {
|
||||
dbg("power fault\n");
|
||||
if (ATTN_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->set_attention_status(p_slot, 1);
|
||||
|
||||
if (PWR_LED(ctrl->ctrlcap))
|
||||
p_slot->hpc_ops->green_led_off(p_slot);
|
||||
}
|
||||
}
|
||||
/***********SURPRISE REMOVAL********************/
|
||||
else if ((ctrl->event_queue[loop].event_type == INT_PRESENCE_ON) ||
|
||||
(ctrl->event_queue[loop].event_type == INT_PRESENCE_OFF)) {
|
||||
if (HP_SUPR_RM(ctrl->ctrlcap)) {
|
||||
dbg("Surprise Removal\n");
|
||||
if (p_slot) {
|
||||
surprise_rm_pending = (unsigned long) p_slot;
|
||||
up(&event_semaphore);
|
||||
update_slot_info(p_slot);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* refresh notification */
|
||||
if (p_slot)
|
||||
update_slot_info(p_slot);
|
||||
}
|
||||
|
||||
ctrl->event_queue[loop].event_type = 0;
|
||||
|
||||
change = 1;
|
||||
}
|
||||
} /* End of FOR loop */
|
||||
}
|
||||
}
|
||||
|
||||
int pciehp_enable_slot(struct slot *p_slot)
|
||||
{
|
||||
u8 getstatus = 0;
|
||||
int rc;
|
||||
|
||||
/* Check to see if (latch closed, card present, power off) */
|
||||
mutex_lock(&p_slot->ctrl->crit_sect);
|
||||
|
||||
rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
|
||||
if (rc || !getstatus) {
|
||||
info("%s: no adapter on slot(%s)\n", __FUNCTION__,
|
||||
slot_name(p_slot));
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
|
||||
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
if (rc || getstatus) {
|
||||
info("%s: latch open on slot(%s)\n", __FUNCTION__,
|
||||
slot_name(p_slot));
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
|
||||
rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
|
||||
if (rc || getstatus) {
|
||||
info("%s: already enabled on slot(%s)\n", __FUNCTION__,
|
||||
slot_name(p_slot));
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
|
||||
rc = board_added(p_slot);
|
||||
if (rc) {
|
||||
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
}
|
||||
|
||||
update_slot_info(p_slot);
|
||||
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int pciehp_disable_slot(struct slot *p_slot)
|
||||
{
|
||||
u8 getstatus = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!p_slot->ctrl)
|
||||
return 1;
|
||||
|
||||
/* Check to see if (latch closed, card present, power on) */
|
||||
mutex_lock(&p_slot->ctrl->crit_sect);
|
||||
|
||||
if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) {
|
||||
ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
|
||||
if (ret || !getstatus) {
|
||||
info("%s: no adapter on slot(%s)\n", __FUNCTION__,
|
||||
slot_name(p_slot));
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
|
||||
ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
if (ret || getstatus) {
|
||||
info("%s: latch open on slot(%s)\n", __FUNCTION__,
|
||||
slot_name(p_slot));
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
|
||||
ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
|
||||
if (ret || !getstatus) {
|
||||
info("%s: already disabled slot(%s)\n", __FUNCTION__,
|
||||
slot_name(p_slot));
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = remove_board(p_slot);
|
||||
update_slot_info(p_slot);
|
||||
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return ret;
|
||||
}
|
||||
|
||||
1377
drivers/pci/hotplug/pciehp_hpc.c
Normal file
1377
drivers/pci/hotplug/pciehp_hpc.c
Normal file
File diff suppressed because it is too large
Load Diff
285
drivers/pci/hotplug/pciehp_pci.c
Normal file
285
drivers/pci/hotplug/pciehp_pci.c
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* PCI Express Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include "../pci.h"
|
||||
#include "pciehp.h"
|
||||
|
||||
static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
|
||||
{
|
||||
u16 pci_cmd, pci_bctl;
|
||||
|
||||
if (hpp->revision > 1) {
|
||||
printk(KERN_WARNING "%s: Rev.%d type0 record not supported\n",
|
||||
__FUNCTION__, hpp->revision);
|
||||
return;
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
|
||||
pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
|
||||
if (hpp->enable_serr)
|
||||
pci_cmd |= PCI_COMMAND_SERR;
|
||||
else
|
||||
pci_cmd &= ~PCI_COMMAND_SERR;
|
||||
if (hpp->enable_perr)
|
||||
pci_cmd |= PCI_COMMAND_PARITY;
|
||||
else
|
||||
pci_cmd &= ~PCI_COMMAND_PARITY;
|
||||
pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
|
||||
|
||||
/* Program bridge control value */
|
||||
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
|
||||
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
|
||||
hpp->latency_timer);
|
||||
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
|
||||
if (hpp->enable_serr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_SERR;
|
||||
else
|
||||
pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
|
||||
if (hpp->enable_perr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_PARITY;
|
||||
else
|
||||
pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
|
||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
|
||||
}
|
||||
}
|
||||
|
||||
static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
|
||||
{
|
||||
int pos;
|
||||
u16 reg16;
|
||||
u32 reg32;
|
||||
|
||||
if (hpp->revision > 1) {
|
||||
printk(KERN_WARNING "%s: Rev.%d type2 record not supported\n",
|
||||
__FUNCTION__, hpp->revision);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find PCI Express capability */
|
||||
pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
/* Initialize Device Control Register */
|
||||
pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, ®16);
|
||||
reg16 = (reg16 & hpp->pci_exp_devctl_and) | hpp->pci_exp_devctl_or;
|
||||
pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
|
||||
|
||||
/* Initialize Link Control Register */
|
||||
if (dev->subordinate) {
|
||||
pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, ®16);
|
||||
reg16 = (reg16 & hpp->pci_exp_lnkctl_and)
|
||||
| hpp->pci_exp_lnkctl_or;
|
||||
pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, reg16);
|
||||
}
|
||||
|
||||
/* Find Advanced Error Reporting Enhanced Capability */
|
||||
pos = 256;
|
||||
do {
|
||||
pci_read_config_dword(dev, pos, ®32);
|
||||
if (PCI_EXT_CAP_ID(reg32) == PCI_EXT_CAP_ID_ERR)
|
||||
break;
|
||||
} while ((pos = PCI_EXT_CAP_NEXT(reg32)));
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
/* Initialize Uncorrectable Error Mask Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, ®32);
|
||||
reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
|
||||
|
||||
/* Initialize Uncorrectable Error Severity Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, ®32);
|
||||
reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
|
||||
|
||||
/* Initialize Correctable Error Mask Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®32);
|
||||
reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
|
||||
|
||||
/* Initialize Advanced Error Capabilities and Control Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32);
|
||||
reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
|
||||
|
||||
/*
|
||||
* FIXME: The following two registers are not supported yet.
|
||||
*
|
||||
* o Secondary Uncorrectable Error Severity Register
|
||||
* o Secondary Uncorrectable Error Mask Register
|
||||
*/
|
||||
}
|
||||
|
||||
static void program_fw_provided_values(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *cdev;
|
||||
struct hotplug_params hpp;
|
||||
|
||||
/* Program hpp values for this device */
|
||||
if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL ||
|
||||
(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
|
||||
(dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
|
||||
return;
|
||||
|
||||
if (pciehp_get_hp_params_from_firmware(dev, &hpp)) {
|
||||
printk(KERN_WARNING "%s: Could not get hotplug parameters\n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hpp.t2)
|
||||
program_hpp_type2(dev, hpp.t2);
|
||||
if (hpp.t0)
|
||||
program_hpp_type0(dev, hpp.t0);
|
||||
|
||||
/* Program child devices */
|
||||
if (dev->subordinate) {
|
||||
list_for_each_entry(cdev, &dev->subordinate->devices,
|
||||
bus_list)
|
||||
program_fw_provided_values(cdev);
|
||||
}
|
||||
}
|
||||
|
||||
static int pciehp_add_bridge(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_bus *parent = dev->bus;
|
||||
int pass, busnr, start = parent->secondary;
|
||||
int end = parent->subordinate;
|
||||
|
||||
for (busnr = start; busnr <= end; busnr++) {
|
||||
if (!pci_find_bus(pci_domain_nr(parent), busnr))
|
||||
break;
|
||||
}
|
||||
if (busnr-- > end) {
|
||||
err("No bus number available for hot-added bridge %s\n",
|
||||
pci_name(dev));
|
||||
return -1;
|
||||
}
|
||||
for (pass = 0; pass < 2; pass++)
|
||||
busnr = pci_scan_bridge(parent, dev, busnr, pass);
|
||||
if (!dev->subordinate)
|
||||
return -1;
|
||||
pci_bus_size_bridges(dev->subordinate);
|
||||
pci_bus_assign_resources(parent);
|
||||
pci_enable_bridges(parent);
|
||||
pci_bus_add_devices(parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pciehp_configure_device(struct slot *p_slot)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate;
|
||||
int num, fn;
|
||||
|
||||
dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0));
|
||||
if (dev) {
|
||||
err("Device %s already exists at %x:%x, cannot hot-add\n",
|
||||
pci_name(dev), p_slot->bus, p_slot->device);
|
||||
pci_dev_put(dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0));
|
||||
if (num == 0) {
|
||||
err("No new device found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (fn = 0; fn < 8; fn++) {
|
||||
dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, fn));
|
||||
if (!dev)
|
||||
continue;
|
||||
if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
|
||||
err("Cannot hot-add display device %s\n",
|
||||
pci_name(dev));
|
||||
pci_dev_put(dev);
|
||||
continue;
|
||||
}
|
||||
if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) ||
|
||||
(dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) {
|
||||
pciehp_add_bridge(dev);
|
||||
}
|
||||
program_fw_provided_values(dev);
|
||||
pci_dev_put(dev);
|
||||
}
|
||||
|
||||
pci_bus_assign_resources(parent);
|
||||
pci_bus_add_devices(parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pciehp_unconfigure_device(struct slot *p_slot)
|
||||
{
|
||||
int rc = 0;
|
||||
int j;
|
||||
u8 bctl = 0;
|
||||
struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate;
|
||||
|
||||
dbg("%s: bus/dev = %x/%x\n", __FUNCTION__, p_slot->bus,
|
||||
p_slot->device);
|
||||
|
||||
for (j=0; j<8 ; j++) {
|
||||
struct pci_dev* temp = pci_get_slot(parent,
|
||||
(p_slot->device << 3) | j);
|
||||
if (!temp)
|
||||
continue;
|
||||
if ((temp->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
|
||||
err("Cannot remove display device %s\n",
|
||||
pci_name(temp));
|
||||
pci_dev_put(temp);
|
||||
continue;
|
||||
}
|
||||
if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
||||
pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl);
|
||||
if (bctl & PCI_BRIDGE_CTL_VGA) {
|
||||
err("Cannot remove display device %s\n",
|
||||
pci_name(temp));
|
||||
pci_dev_put(temp);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pci_remove_bus_device(temp);
|
||||
pci_dev_put(temp);
|
||||
}
|
||||
/*
|
||||
* Some PCI Express root ports require fixup after hot-plug operation.
|
||||
*/
|
||||
if (pcie_mch_quirk)
|
||||
pci_fixup_device(pci_fixup_final, p_slot->ctrl->pci_dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
360
drivers/pci/hotplug/pcihp_skeleton.c
Normal file
360
drivers/pci/hotplug/pcihp_skeleton.c
Normal file
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* PCI Hot Plug Controller Skeleton Driver - 0.3
|
||||
*
|
||||
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001,2003 IBM Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This driver is to be used as a skeleton driver to show how to interface
|
||||
* with the pci hotplug core easily.
|
||||
*
|
||||
* Send feedback to <greg@kroah.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define SLOT_NAME_SIZE 10
|
||||
struct slot {
|
||||
u8 number;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct list_head slot_list;
|
||||
char name[SLOT_NAME_SIZE];
|
||||
};
|
||||
|
||||
static LIST_HEAD(slot_list);
|
||||
|
||||
#define MY_NAME "pcihp_skeleton"
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (debug) \
|
||||
printk (KERN_DEBUG "%s: " format "\n", \
|
||||
MY_NAME , ## arg); \
|
||||
} while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
|
||||
|
||||
/* local variables */
|
||||
static int debug;
|
||||
static int num_slots;
|
||||
|
||||
#define DRIVER_VERSION "0.3"
|
||||
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
|
||||
#define DRIVER_DESC "Hot Plug PCI Controller Skeleton Driver"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
module_param(debug, bool, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
||||
|
||||
static int enable_slot (struct hotplug_slot *slot);
|
||||
static int disable_slot (struct hotplug_slot *slot);
|
||||
static int set_attention_status (struct hotplug_slot *slot, u8 value);
|
||||
static int hardware_test (struct hotplug_slot *slot, u32 value);
|
||||
static int get_power_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
|
||||
|
||||
static struct hotplug_slot_ops skel_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.set_attention_status = set_attention_status,
|
||||
.hardware_test = hardware_test,
|
||||
.get_power_status = get_power_status,
|
||||
.get_attention_status = get_attention_status,
|
||||
.get_latch_status = get_latch_status,
|
||||
.get_adapter_status = get_adapter_status,
|
||||
};
|
||||
|
||||
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
/*
|
||||
* Fill in code here to enable the specified slot
|
||||
*/
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
/*
|
||||
* Fill in code here to disable the specified slot
|
||||
*/
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
/*
|
||||
* Fill in code here to turn light off
|
||||
*/
|
||||
break;
|
||||
|
||||
case 1:
|
||||
default:
|
||||
/*
|
||||
* Fill in code here to turn light on
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
switch (value) {
|
||||
case 0:
|
||||
/* Specify a test here */
|
||||
break;
|
||||
case 1:
|
||||
/* Specify another test here */
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
/*
|
||||
* Fill in logic to get the current power status of the specific
|
||||
* slot and store it in the *value location.
|
||||
*/
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
/*
|
||||
* Fill in logic to get the current attention status of the specific
|
||||
* slot and store it in the *value location.
|
||||
*/
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
/*
|
||||
* Fill in logic to get the current latch status of the specific
|
||||
* slot and store it in the *value location.
|
||||
*/
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
/*
|
||||
* Fill in logic to get the current adapter status of the specific
|
||||
* slot and store it in the *value location.
|
||||
*/
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void release_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
kfree(slot->hotplug_slot->info);
|
||||
kfree(slot->hotplug_slot);
|
||||
kfree(slot);
|
||||
}
|
||||
|
||||
static void make_slot_name(struct slot *slot)
|
||||
{
|
||||
/*
|
||||
* Stupid way to make a filename out of the slot name.
|
||||
* replace this if your hardware provides a better way to name slots.
|
||||
*/
|
||||
snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_slots - initialize 'struct slot' structures for each slot
|
||||
*
|
||||
*/
|
||||
static int __init init_slots(void)
|
||||
{
|
||||
struct slot *slot;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct hotplug_slot_info *info;
|
||||
int retval = -ENOMEM;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Create a structure for each slot, and register that slot
|
||||
* with the pci_hotplug subsystem.
|
||||
*/
|
||||
for (i = 0; i < num_slots; ++i) {
|
||||
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
||||
if (!slot)
|
||||
goto error;
|
||||
|
||||
hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
|
||||
if (!hotplug_slot)
|
||||
goto error_slot;
|
||||
slot->hotplug_slot = hotplug_slot;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
goto error_hpslot;
|
||||
hotplug_slot->info = info;
|
||||
|
||||
slot->number = i;
|
||||
|
||||
hotplug_slot->name = slot->name;
|
||||
hotplug_slot->private = slot;
|
||||
hotplug_slot->release = &release_slot;
|
||||
make_slot_name(slot);
|
||||
hotplug_slot->ops = &skel_hotplug_slot_ops;
|
||||
|
||||
/*
|
||||
* Initialize the slot info structure with some known
|
||||
* good values.
|
||||
*/
|
||||
get_power_status(hotplug_slot, &info->power_status);
|
||||
get_attention_status(hotplug_slot, &info->attention_status);
|
||||
get_latch_status(hotplug_slot, &info->latch_status);
|
||||
get_adapter_status(hotplug_slot, &info->adapter_status);
|
||||
|
||||
dbg("registering slot %d\n", i);
|
||||
retval = pci_hp_register(slot->hotplug_slot);
|
||||
if (retval) {
|
||||
err("pci_hp_register failed with error %d\n", retval);
|
||||
goto error_info;
|
||||
}
|
||||
|
||||
/* add slot to our internal list */
|
||||
list_add(&slot->slot_list, &slot_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
error_info:
|
||||
kfree(info);
|
||||
error_hpslot:
|
||||
kfree(hotplug_slot);
|
||||
error_slot:
|
||||
kfree(slot);
|
||||
error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit cleanup_slots(void)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct list_head *next;
|
||||
struct slot *slot;
|
||||
|
||||
/*
|
||||
* Unregister all of our slots with the pci_hotplug subsystem.
|
||||
* Memory will be freed in release_slot() callback after slot's
|
||||
* lifespan is finished.
|
||||
*/
|
||||
list_for_each_safe(tmp, next, &slot_list) {
|
||||
slot = list_entry(tmp, struct slot, slot_list);
|
||||
list_del(&slot->slot_list);
|
||||
pci_hp_deregister(slot->hotplug_slot);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init pcihp_skel_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
/*
|
||||
* Do specific initialization stuff for your driver here
|
||||
* like initializing your controller hardware (if any) and
|
||||
* determining the number of slots you have in the system
|
||||
* right now.
|
||||
*/
|
||||
num_slots = 5;
|
||||
|
||||
return init_slots();
|
||||
}
|
||||
|
||||
static void __exit pcihp_skel_exit(void)
|
||||
{
|
||||
/*
|
||||
* Clean everything up.
|
||||
*/
|
||||
cleanup_slots();
|
||||
}
|
||||
|
||||
module_init(pcihp_skel_init);
|
||||
module_exit(pcihp_skel_exit);
|
||||
24
drivers/pci/hotplug/rpadlpar.h
Normal file
24
drivers/pci/hotplug/rpadlpar.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Interface for Dynamic Logical Partitioning of I/O Slots on
|
||||
* RPA-compliant PPC64 platform.
|
||||
*
|
||||
* John Rose <johnrose@austin.ibm.com>
|
||||
* October 2003
|
||||
*
|
||||
* Copyright (C) 2003 IBM.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _RPADLPAR_IO_H_
|
||||
#define _RPADLPAR_IO_H_
|
||||
|
||||
extern int dlpar_sysfs_init(void);
|
||||
extern void dlpar_sysfs_exit(void);
|
||||
|
||||
extern int dlpar_add_slot(char *drc_name);
|
||||
extern int dlpar_remove_slot(char *drc_name);
|
||||
|
||||
#endif
|
||||
475
drivers/pci/hotplug/rpadlpar_core.c
Normal file
475
drivers/pci/hotplug/rpadlpar_core.c
Normal file
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* Interface for Dynamic Logical Partitioning of I/O Slots on
|
||||
* RPA-compliant PPC64 platform.
|
||||
*
|
||||
* John Rose <johnrose@austin.ibm.com>
|
||||
* Linda Xie <lxie@us.ibm.com>
|
||||
*
|
||||
* October 2003
|
||||
*
|
||||
* Copyright (C) 2003 IBM.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/vio.h>
|
||||
|
||||
#include "../pci.h"
|
||||
#include "rpaphp.h"
|
||||
#include "rpadlpar.h"
|
||||
|
||||
static DEFINE_MUTEX(rpadlpar_mutex);
|
||||
|
||||
#define DLPAR_MODULE_NAME "rpadlpar_io"
|
||||
|
||||
#define NODE_TYPE_VIO 1
|
||||
#define NODE_TYPE_SLOT 2
|
||||
#define NODE_TYPE_PHB 3
|
||||
|
||||
static struct device_node *find_vio_slot_node(char *drc_name)
|
||||
{
|
||||
struct device_node *parent = of_find_node_by_name(NULL, "vdevice");
|
||||
struct device_node *dn = NULL;
|
||||
char *name;
|
||||
int rc;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
while ((dn = of_get_next_child(parent, dn))) {
|
||||
rc = rpaphp_get_drc_props(dn, NULL, &name, NULL, NULL);
|
||||
if ((rc == 0) && (!strcmp(drc_name, name)))
|
||||
break;
|
||||
}
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
/* Find dlpar-capable pci node that contains the specified name and type */
|
||||
static struct device_node *find_php_slot_pci_node(char *drc_name,
|
||||
char *drc_type)
|
||||
{
|
||||
struct device_node *np = NULL;
|
||||
char *name;
|
||||
char *type;
|
||||
int rc;
|
||||
|
||||
while ((np = of_find_node_by_name(np, "pci"))) {
|
||||
rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
|
||||
if (rc == 0)
|
||||
if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
|
||||
break;
|
||||
}
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
|
||||
{
|
||||
struct device_node *dn;
|
||||
|
||||
dn = find_php_slot_pci_node(drc_name, "SLOT");
|
||||
if (dn) {
|
||||
*node_type = NODE_TYPE_SLOT;
|
||||
return dn;
|
||||
}
|
||||
|
||||
dn = find_php_slot_pci_node(drc_name, "PHB");
|
||||
if (dn) {
|
||||
*node_type = NODE_TYPE_PHB;
|
||||
return dn;
|
||||
}
|
||||
|
||||
dn = find_vio_slot_node(drc_name);
|
||||
if (dn) {
|
||||
*node_type = NODE_TYPE_VIO;
|
||||
return dn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct slot *find_slot(struct device_node *dn)
|
||||
{
|
||||
struct list_head *tmp, *n;
|
||||
struct slot *slot;
|
||||
|
||||
list_for_each_safe(tmp, n, &rpaphp_slot_head) {
|
||||
slot = list_entry(tmp, struct slot, rpaphp_slot_list);
|
||||
if (slot->dn == dn)
|
||||
return slot;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
|
||||
struct device_node *dev_dn)
|
||||
{
|
||||
struct pci_dev *tmp = NULL;
|
||||
struct device_node *child_dn;
|
||||
|
||||
list_for_each_entry(tmp, &parent->devices, bus_list) {
|
||||
child_dn = pci_device_to_OF_node(tmp);
|
||||
if (child_dn == dev_dn)
|
||||
return tmp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dlpar_pci_add_bus(struct device_node *dn)
|
||||
{
|
||||
struct pci_dn *pdn = PCI_DN(dn);
|
||||
struct pci_controller *phb = pdn->phb;
|
||||
struct pci_dev *dev = NULL;
|
||||
|
||||
eeh_add_device_tree_early(dn);
|
||||
|
||||
/* Add EADS device to PHB bus, adding new entry to bus->devices */
|
||||
dev = of_create_pci_dev(dn, phb->bus, pdn->devfn);
|
||||
if (!dev) {
|
||||
printk(KERN_ERR "%s: failed to create pci dev for %s\n",
|
||||
__FUNCTION__, dn->full_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
|
||||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
|
||||
of_scan_pci_bridge(dn, dev);
|
||||
|
||||
pcibios_fixup_new_pci_devices(dev->subordinate,0);
|
||||
|
||||
/* Claim new bus resources */
|
||||
pcibios_claim_one_bus(dev->bus);
|
||||
|
||||
/* ioremap() for child bus, which may or may not succeed */
|
||||
remap_bus_range(dev->subordinate);
|
||||
|
||||
/* Add new devices to global lists. Register in proc, sysfs. */
|
||||
pci_bus_add_devices(phb->bus);
|
||||
}
|
||||
|
||||
static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
struct pci_controller *phb;
|
||||
|
||||
if (pcibios_find_pci_bus(dn))
|
||||
return -EINVAL;
|
||||
|
||||
/* Add pci bus */
|
||||
dlpar_pci_add_bus(dn);
|
||||
|
||||
/* Confirm new bridge dev was created */
|
||||
phb = PCI_DN(dn)->phb;
|
||||
dev = dlpar_find_new_dev(phb->bus, dn);
|
||||
|
||||
if (!dev) {
|
||||
printk(KERN_ERR "%s: unable to add bus %s\n", __FUNCTION__,
|
||||
drc_name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
|
||||
printk(KERN_ERR "%s: unexpected header type %d, unable to add bus %s\n",
|
||||
__FUNCTION__, dev->hdr_type, drc_name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Add hotplug slot */
|
||||
if (rpaphp_add_slot(dn)) {
|
||||
printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
|
||||
__FUNCTION__, drc_name);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dlpar_remove_root_bus(struct pci_controller *phb)
|
||||
{
|
||||
struct pci_bus *phb_bus;
|
||||
int rc;
|
||||
|
||||
phb_bus = phb->bus;
|
||||
if (!(list_empty(&phb_bus->children) &&
|
||||
list_empty(&phb_bus->devices))) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
rc = pcibios_remove_root_bus(phb);
|
||||
if (rc)
|
||||
return -EIO;
|
||||
|
||||
device_unregister(phb_bus->bridge);
|
||||
pci_remove_bus(phb_bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
|
||||
{
|
||||
struct slot *slot;
|
||||
struct pci_dn *pdn;
|
||||
int rc = 0;
|
||||
|
||||
if (!pcibios_find_pci_bus(dn))
|
||||
return -EINVAL;
|
||||
|
||||
slot = find_slot(dn);
|
||||
if (slot) {
|
||||
/* Remove hotplug slot */
|
||||
if (rpaphp_deregister_slot(slot)) {
|
||||
printk(KERN_ERR
|
||||
"%s: unable to remove hotplug slot %s\n",
|
||||
__FUNCTION__, drc_name);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
pdn = dn->data;
|
||||
BUG_ON(!pdn || !pdn->phb);
|
||||
rc = dlpar_remove_root_bus(pdn->phb);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
pdn->phb = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dlpar_add_phb(char *drc_name, struct device_node *dn)
|
||||
{
|
||||
struct pci_controller *phb;
|
||||
|
||||
if (PCI_DN(dn) && PCI_DN(dn)->phb) {
|
||||
/* PHB already exists */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phb = init_phb_dynamic(dn);
|
||||
if (!phb)
|
||||
return -EIO;
|
||||
|
||||
if (rpaphp_add_slot(dn)) {
|
||||
printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
|
||||
__FUNCTION__, drc_name);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
|
||||
{
|
||||
if (vio_find_node(dn))
|
||||
return -EINVAL;
|
||||
|
||||
if (!vio_register_device_node(dn)) {
|
||||
printk(KERN_ERR
|
||||
"%s: failed to register vio node %s\n",
|
||||
__FUNCTION__, drc_name);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlpar_add_slot - DLPAR add an I/O Slot
|
||||
* @drc_name: drc-name of newly added slot
|
||||
*
|
||||
* Make the hotplug module and the kernel aware
|
||||
* of a newly added I/O Slot.
|
||||
* Return Codes -
|
||||
* 0 Success
|
||||
* -ENODEV Not a valid drc_name
|
||||
* -EINVAL Slot already added
|
||||
* -ERESTARTSYS Signalled before obtaining lock
|
||||
* -EIO Internal PCI Error
|
||||
*/
|
||||
int dlpar_add_slot(char *drc_name)
|
||||
{
|
||||
struct device_node *dn = NULL;
|
||||
int node_type;
|
||||
int rc = -EIO;
|
||||
|
||||
if (mutex_lock_interruptible(&rpadlpar_mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* Find newly added node */
|
||||
dn = find_dlpar_node(drc_name, &node_type);
|
||||
if (!dn) {
|
||||
rc = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch (node_type) {
|
||||
case NODE_TYPE_VIO:
|
||||
rc = dlpar_add_vio_slot(drc_name, dn);
|
||||
break;
|
||||
case NODE_TYPE_SLOT:
|
||||
rc = dlpar_add_pci_slot(drc_name, dn);
|
||||
break;
|
||||
case NODE_TYPE_PHB:
|
||||
rc = dlpar_add_phb(drc_name, dn);
|
||||
break;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name);
|
||||
exit:
|
||||
mutex_unlock(&rpadlpar_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
|
||||
* @drc_name: drc-name of newly added slot
|
||||
*
|
||||
* Remove the kernel and hotplug representations
|
||||
* of an I/O Slot.
|
||||
* Return Codes:
|
||||
* 0 Success
|
||||
* -EINVAL Vio dev doesn't exist
|
||||
*/
|
||||
static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
|
||||
{
|
||||
struct vio_dev *vio_dev;
|
||||
|
||||
vio_dev = vio_find_node(dn);
|
||||
if (!vio_dev)
|
||||
return -EINVAL;
|
||||
|
||||
vio_unregister_device(vio_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlpar_remove_slot - DLPAR remove a PCI I/O Slot
|
||||
* @drc_name: drc-name of newly added slot
|
||||
*
|
||||
* Remove the kernel and hotplug representations
|
||||
* of a PCI I/O Slot.
|
||||
* Return Codes:
|
||||
* 0 Success
|
||||
* -ENODEV Not a valid drc_name
|
||||
* -EIO Internal PCI Error
|
||||
*/
|
||||
int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
|
||||
{
|
||||
struct pci_bus *bus;
|
||||
struct slot *slot;
|
||||
|
||||
bus = pcibios_find_pci_bus(dn);
|
||||
if (!bus)
|
||||
return -EINVAL;
|
||||
|
||||
slot = find_slot(dn);
|
||||
if (slot) {
|
||||
/* Remove hotplug slot */
|
||||
if (rpaphp_deregister_slot(slot)) {
|
||||
printk(KERN_ERR
|
||||
"%s: unable to remove hotplug slot %s\n",
|
||||
__FUNCTION__, drc_name);
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
struct pci_dev *dev, *tmp;
|
||||
list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
|
||||
eeh_remove_bus_device(dev);
|
||||
pci_remove_bus_device(dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (unmap_bus_range(bus)) {
|
||||
printk(KERN_ERR "%s: failed to unmap bus range\n",
|
||||
__FUNCTION__);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
BUG_ON(!bus->self);
|
||||
pci_remove_bus_device(bus->self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlpar_remove_slot - DLPAR remove an I/O Slot
|
||||
* @drc_name: drc-name of newly added slot
|
||||
*
|
||||
* Remove the kernel and hotplug representations
|
||||
* of an I/O Slot.
|
||||
* Return Codes:
|
||||
* 0 Success
|
||||
* -ENODEV Not a valid drc_name
|
||||
* -EINVAL Slot already removed
|
||||
* -ERESTARTSYS Signalled before obtaining lock
|
||||
* -EIO Internal Error
|
||||
*/
|
||||
int dlpar_remove_slot(char *drc_name)
|
||||
{
|
||||
struct device_node *dn;
|
||||
int node_type;
|
||||
int rc = 0;
|
||||
|
||||
if (mutex_lock_interruptible(&rpadlpar_mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
dn = find_dlpar_node(drc_name, &node_type);
|
||||
if (!dn) {
|
||||
rc = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch (node_type) {
|
||||
case NODE_TYPE_VIO:
|
||||
rc = dlpar_remove_vio_slot(drc_name, dn);
|
||||
break;
|
||||
case NODE_TYPE_PHB:
|
||||
rc = dlpar_remove_phb(drc_name, dn);
|
||||
break;
|
||||
case NODE_TYPE_SLOT:
|
||||
rc = dlpar_remove_pci_slot(drc_name, dn);
|
||||
break;
|
||||
}
|
||||
printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name);
|
||||
exit:
|
||||
mutex_unlock(&rpadlpar_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int is_dlpar_capable(void)
|
||||
{
|
||||
int rc = rtas_token("ibm,configure-connector");
|
||||
|
||||
return (int) (rc != RTAS_UNKNOWN_SERVICE);
|
||||
}
|
||||
|
||||
int __init rpadlpar_io_init(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!is_dlpar_capable()) {
|
||||
printk(KERN_WARNING "%s: partition not DLPAR capable\n",
|
||||
__FUNCTION__);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
rc = dlpar_sysfs_init();
|
||||
return rc;
|
||||
}
|
||||
|
||||
void rpadlpar_io_exit(void)
|
||||
{
|
||||
dlpar_sysfs_exit();
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(rpadlpar_io_init);
|
||||
module_exit(rpadlpar_io_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
151
drivers/pci/hotplug/rpadlpar_sysfs.c
Normal file
151
drivers/pci/hotplug/rpadlpar_sysfs.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Interface for Dynamic Logical Partitioning of I/O Slots on
|
||||
* RPA-compliant PPC64 platform.
|
||||
*
|
||||
* John Rose <johnrose@austin.ibm.com>
|
||||
* October 2003
|
||||
*
|
||||
* Copyright (C) 2003 IBM.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include "rpadlpar.h"
|
||||
|
||||
#define DLPAR_KOBJ_NAME "control"
|
||||
#define ADD_SLOT_ATTR_NAME "add_slot"
|
||||
#define REMOVE_SLOT_ATTR_NAME "remove_slot"
|
||||
|
||||
#define MAX_DRC_NAME_LEN 64
|
||||
|
||||
/* Store return code of dlpar operation in attribute struct */
|
||||
struct dlpar_io_attr {
|
||||
int rc;
|
||||
struct attribute attr;
|
||||
ssize_t (*store)(struct dlpar_io_attr *dlpar_attr, const char *buf,
|
||||
size_t nbytes);
|
||||
};
|
||||
|
||||
/* Common show callback for all attrs, display the return code
|
||||
* of the dlpar op */
|
||||
static ssize_t
|
||||
dlpar_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
|
||||
{
|
||||
struct dlpar_io_attr *dlpar_attr = container_of(attr,
|
||||
struct dlpar_io_attr, attr);
|
||||
return sprintf(buf, "%d\n", dlpar_attr->rc);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
dlpar_attr_store(struct kobject * kobj, struct attribute * attr,
|
||||
const char *buf, size_t nbytes)
|
||||
{
|
||||
struct dlpar_io_attr *dlpar_attr = container_of(attr,
|
||||
struct dlpar_io_attr, attr);
|
||||
return dlpar_attr->store ?
|
||||
dlpar_attr->store(dlpar_attr, buf, nbytes) : -EIO;
|
||||
}
|
||||
|
||||
static struct sysfs_ops dlpar_attr_sysfs_ops = {
|
||||
.show = dlpar_attr_show,
|
||||
.store = dlpar_attr_store,
|
||||
};
|
||||
|
||||
static ssize_t add_slot_store(struct dlpar_io_attr *dlpar_attr,
|
||||
const char *buf, size_t nbytes)
|
||||
{
|
||||
char drc_name[MAX_DRC_NAME_LEN];
|
||||
char *end;
|
||||
|
||||
if (nbytes >= MAX_DRC_NAME_LEN)
|
||||
return 0;
|
||||
|
||||
memcpy(drc_name, buf, nbytes);
|
||||
|
||||
end = strchr(drc_name, '\n');
|
||||
if (!end)
|
||||
end = &drc_name[nbytes];
|
||||
*end = '\0';
|
||||
|
||||
dlpar_attr->rc = dlpar_add_slot(drc_name);
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static ssize_t remove_slot_store(struct dlpar_io_attr *dlpar_attr,
|
||||
const char *buf, size_t nbytes)
|
||||
{
|
||||
char drc_name[MAX_DRC_NAME_LEN];
|
||||
char *end;
|
||||
|
||||
if (nbytes >= MAX_DRC_NAME_LEN)
|
||||
return 0;
|
||||
|
||||
memcpy(drc_name, buf, nbytes);
|
||||
|
||||
end = strchr(drc_name, '\n');
|
||||
if (!end)
|
||||
end = &drc_name[nbytes];
|
||||
*end = '\0';
|
||||
|
||||
dlpar_attr->rc = dlpar_remove_slot(drc_name);
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static struct dlpar_io_attr add_slot_attr = {
|
||||
.rc = 0,
|
||||
.attr = { .name = ADD_SLOT_ATTR_NAME, .mode = 0644, },
|
||||
.store = add_slot_store,
|
||||
};
|
||||
|
||||
static struct dlpar_io_attr remove_slot_attr = {
|
||||
.rc = 0,
|
||||
.attr = { .name = REMOVE_SLOT_ATTR_NAME, .mode = 0644},
|
||||
.store = remove_slot_store,
|
||||
};
|
||||
|
||||
static struct attribute *default_attrs[] = {
|
||||
&add_slot_attr.attr,
|
||||
&remove_slot_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void dlpar_io_release(struct kobject *kobj)
|
||||
{
|
||||
/* noop */
|
||||
return;
|
||||
}
|
||||
|
||||
struct kobj_type ktype_dlpar_io = {
|
||||
.release = dlpar_io_release,
|
||||
.sysfs_ops = &dlpar_attr_sysfs_ops,
|
||||
.default_attrs = default_attrs,
|
||||
};
|
||||
|
||||
struct kset dlpar_io_kset = {
|
||||
.subsys = &pci_hotplug_slots_subsys,
|
||||
.kobj = {.name = DLPAR_KOBJ_NAME, .ktype=&ktype_dlpar_io,},
|
||||
.ktype = &ktype_dlpar_io,
|
||||
};
|
||||
|
||||
int dlpar_sysfs_init(void)
|
||||
{
|
||||
if (kset_register(&dlpar_io_kset)) {
|
||||
printk(KERN_ERR "rpadlpar_io: cannot register kset for %s\n",
|
||||
dlpar_io_kset.kobj.name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dlpar_sysfs_exit(void)
|
||||
{
|
||||
kset_unregister(&dlpar_io_kset);
|
||||
}
|
||||
110
drivers/pci/hotplug/rpaphp.h
Normal file
110
drivers/pci/hotplug/rpaphp.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
|
||||
*
|
||||
* Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
|
||||
*
|
||||
* 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 <lxie@us.ibm.com>,
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PPC64PHP_H
|
||||
#define _PPC64PHP_H
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
|
||||
#define DR_INDICATOR 9002
|
||||
#define DR_ENTITY_SENSE 9003
|
||||
|
||||
#define POWER_ON 100
|
||||
#define POWER_OFF 0
|
||||
|
||||
#define LED_OFF 0
|
||||
#define LED_ON 1 /* continuous on */
|
||||
#define LED_ID 2 /* slow blinking */
|
||||
#define LED_ACTION 3 /* fast blinking */
|
||||
|
||||
/* Sensor values from rtas_get-sensor */
|
||||
#define EMPTY 0 /* No card in slot */
|
||||
#define PRESENT 1 /* Card in slot */
|
||||
|
||||
#define MY_NAME "rpaphp"
|
||||
extern int debug;
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (debug) \
|
||||
printk(KERN_DEBUG "%s: " format, \
|
||||
MY_NAME , ## arg); \
|
||||
} while (0)
|
||||
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
|
||||
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
|
||||
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
|
||||
|
||||
/* slot states */
|
||||
|
||||
#define NOT_VALID 3
|
||||
#define NOT_CONFIGURED 2
|
||||
#define CONFIGURED 1
|
||||
#define EMPTY 0
|
||||
|
||||
/*
|
||||
* struct slot - slot information for each *physical* slot
|
||||
*/
|
||||
struct slot {
|
||||
struct list_head rpaphp_slot_list;
|
||||
int state;
|
||||
u32 index;
|
||||
u32 type;
|
||||
u32 power_domain;
|
||||
char *name;
|
||||
char *location;
|
||||
struct device_node *dn;
|
||||
struct pci_bus *bus;
|
||||
struct list_head *pci_devs;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
};
|
||||
|
||||
extern struct hotplug_slot_ops rpaphp_hotplug_slot_ops;
|
||||
extern struct list_head rpaphp_slot_head;
|
||||
extern int num_slots;
|
||||
|
||||
/* function prototypes */
|
||||
|
||||
/* rpaphp_pci.c */
|
||||
extern int rpaphp_enable_pci_slot(struct slot *slot);
|
||||
extern int rpaphp_register_pci_slot(struct slot *slot);
|
||||
extern int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value);
|
||||
extern int rpaphp_get_sensor_state(struct slot *slot, int *state);
|
||||
|
||||
/* rpaphp_core.c */
|
||||
extern int rpaphp_add_slot(struct device_node *dn);
|
||||
extern int rpaphp_remove_slot(struct slot *slot);
|
||||
extern int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
|
||||
char **drc_name, char **drc_type, int *drc_power_domain);
|
||||
|
||||
/* rpaphp_slot.c */
|
||||
extern void dealloc_slot_struct(struct slot *slot);
|
||||
extern struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain);
|
||||
extern int rpaphp_register_slot(struct slot *slot);
|
||||
extern int rpaphp_deregister_slot(struct slot *slot);
|
||||
extern int rpaphp_get_power_status(struct slot *slot, u8 * value);
|
||||
extern int rpaphp_set_attention_status(struct slot *slot, u8 status);
|
||||
|
||||
#endif /* _PPC64PHP_H */
|
||||
451
drivers/pci/hotplug/rpaphp_core.c
Normal file
451
drivers/pci/hotplug/rpaphp_core.c
Normal file
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
|
||||
* Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
|
||||
*
|
||||
* 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 <lxie@us.ibm.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/eeh.h> /* for eeh_add_device() */
|
||||
#include <asm/rtas.h> /* rtas_call */
|
||||
#include <asm/pci-bridge.h> /* for pci_controller */
|
||||
#include "../pci.h" /* for pci_add_new_bus */
|
||||
/* and pci_do_scan_bus */
|
||||
#include "rpaphp.h"
|
||||
|
||||
int debug;
|
||||
static struct semaphore rpaphp_sem;
|
||||
LIST_HEAD(rpaphp_slot_head);
|
||||
int num_slots;
|
||||
|
||||
#define DRIVER_VERSION "0.1"
|
||||
#define DRIVER_AUTHOR "Linda Xie <lxie@us.ibm.com>"
|
||||
#define DRIVER_DESC "RPA HOT Plug PCI Controller Driver"
|
||||
|
||||
#define MAX_LOC_CODE 128
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param(debug, bool, 0644);
|
||||
|
||||
static int rpaphp_get_attention_status(struct slot *slot)
|
||||
{
|
||||
return slot->hotplug_slot->info->attention_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_attention_status - set attention LED
|
||||
* echo 0 > attention -- set LED OFF
|
||||
* echo 1 > attention -- set LED ON
|
||||
* echo 2 > attention -- set LED ID(identify, light is blinking)
|
||||
*
|
||||
*/
|
||||
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
|
||||
{
|
||||
int retval = 0;
|
||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
||||
|
||||
down(&rpaphp_sem);
|
||||
switch (value) {
|
||||
case 0:
|
||||
retval = rpaphp_set_attention_status(slot, LED_OFF);
|
||||
hotplug_slot->info->attention_status = 0;
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
retval = rpaphp_set_attention_status(slot, LED_ON);
|
||||
hotplug_slot->info->attention_status = 1;
|
||||
break;
|
||||
case 2:
|
||||
retval = rpaphp_set_attention_status(slot, LED_ID);
|
||||
hotplug_slot->info->attention_status = 2;
|
||||
break;
|
||||
}
|
||||
up(&rpaphp_sem);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_power_status - get power status of a slot
|
||||
* @hotplug_slot: slot to get status
|
||||
* @value: pointer to store status
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
|
||||
{
|
||||
int retval;
|
||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
||||
|
||||
down(&rpaphp_sem);
|
||||
retval = rpaphp_get_power_status(slot, value);
|
||||
up(&rpaphp_sem);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_attention_status - get attention LED status
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
|
||||
{
|
||||
int retval = 0;
|
||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
||||
|
||||
down(&rpaphp_sem);
|
||||
*value = rpaphp_get_attention_status(slot);
|
||||
up(&rpaphp_sem);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
|
||||
{
|
||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
||||
int retval = 0;
|
||||
|
||||
down(&rpaphp_sem);
|
||||
retval = rpaphp_get_pci_adapter_status(slot, 0, value);
|
||||
up(&rpaphp_sem);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
|
||||
{
|
||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
||||
|
||||
down(&rpaphp_sem);
|
||||
switch (slot->type) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
*value = PCI_SPEED_33MHz; /* speed for case 1-6 */
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
*value = PCI_SPEED_66MHz;
|
||||
break;
|
||||
case 11:
|
||||
case 14:
|
||||
*value = PCI_SPEED_66MHz_PCIX;
|
||||
break;
|
||||
case 12:
|
||||
case 15:
|
||||
*value = PCI_SPEED_100MHz_PCIX;
|
||||
break;
|
||||
case 13:
|
||||
case 16:
|
||||
*value = PCI_SPEED_133MHz_PCIX;
|
||||
break;
|
||||
default:
|
||||
*value = PCI_SPEED_UNKNOWN;
|
||||
break;
|
||||
|
||||
}
|
||||
up(&rpaphp_sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_children_props(struct device_node *dn, const int **drc_indexes,
|
||||
const int **drc_names, const int **drc_types,
|
||||
const int **drc_power_domains)
|
||||
{
|
||||
const int *indexes, *names, *types, *domains;
|
||||
|
||||
indexes = get_property(dn, "ibm,drc-indexes", NULL);
|
||||
names = get_property(dn, "ibm,drc-names", NULL);
|
||||
types = get_property(dn, "ibm,drc-types", NULL);
|
||||
domains = get_property(dn, "ibm,drc-power-domains", NULL);
|
||||
|
||||
if (!indexes || !names || !types || !domains) {
|
||||
/* Slot does not have dynamically-removable children */
|
||||
return -EINVAL;
|
||||
}
|
||||
if (drc_indexes)
|
||||
*drc_indexes = indexes;
|
||||
if (drc_names)
|
||||
/* &drc_names[1] contains NULL terminated slot names */
|
||||
*drc_names = names;
|
||||
if (drc_types)
|
||||
/* &drc_types[1] contains NULL terminated slot types */
|
||||
*drc_types = types;
|
||||
if (drc_power_domains)
|
||||
*drc_power_domains = domains;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* To get the DRC props describing the current node, first obtain it's
|
||||
* my-drc-index property. Next obtain the DRC list from it's parent. Use
|
||||
* the my-drc-index for correlation, and obtain the requested properties.
|
||||
*/
|
||||
int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
|
||||
char **drc_name, char **drc_type, int *drc_power_domain)
|
||||
{
|
||||
const int *indexes, *names;
|
||||
const int *types, *domains;
|
||||
const unsigned int *my_index;
|
||||
char *name_tmp, *type_tmp;
|
||||
int i, rc;
|
||||
|
||||
my_index = get_property(dn, "ibm,my-drc-index", NULL);
|
||||
if (!my_index) {
|
||||
/* Node isn't DLPAR/hotplug capable */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = get_children_props(dn->parent, &indexes, &names, &types, &domains);
|
||||
if (rc < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
name_tmp = (char *) &names[1];
|
||||
type_tmp = (char *) &types[1];
|
||||
|
||||
/* Iterate through parent properties, looking for my-drc-index */
|
||||
for (i = 0; i < indexes[0]; i++) {
|
||||
if ((unsigned int) indexes[i + 1] == *my_index) {
|
||||
if (drc_name)
|
||||
*drc_name = name_tmp;
|
||||
if (drc_type)
|
||||
*drc_type = type_tmp;
|
||||
if (drc_index)
|
||||
*drc_index = *my_index;
|
||||
if (drc_power_domain)
|
||||
*drc_power_domain = domains[i+1];
|
||||
return 0;
|
||||
}
|
||||
name_tmp += (strlen(name_tmp) + 1);
|
||||
type_tmp += (strlen(type_tmp) + 1);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int is_php_type(char *drc_type)
|
||||
{
|
||||
unsigned long value;
|
||||
char *endptr;
|
||||
|
||||
/* PCI Hotplug nodes have an integer for drc_type */
|
||||
value = simple_strtoul(drc_type, &endptr, 10);
|
||||
if (endptr == drc_type)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int is_php_dn(struct device_node *dn, const int **indexes,
|
||||
const int **names, const int **types, const int **power_domains)
|
||||
{
|
||||
const int *drc_types;
|
||||
int rc;
|
||||
|
||||
rc = get_children_props(dn, indexes, names, &drc_types, power_domains);
|
||||
if (rc >= 0) {
|
||||
if (is_php_type((char *) &drc_types[1])) {
|
||||
*types = drc_types;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpaphp_add_slot -- add hotplug or dlpar slot
|
||||
*
|
||||
* rpaphp not only registers PCI hotplug slots(HOTPLUG),
|
||||
* but also logical DR slots(EMBEDDED).
|
||||
* HOTPLUG slot: An adapter can be physically added/removed.
|
||||
* EMBEDDED slot: An adapter can be logically removed/added
|
||||
* from/to a partition with the slot.
|
||||
*/
|
||||
int rpaphp_add_slot(struct device_node *dn)
|
||||
{
|
||||
struct slot *slot;
|
||||
int retval = 0;
|
||||
int i;
|
||||
const int *indexes, *names, *types, *power_domains;
|
||||
char *name, *type;
|
||||
|
||||
dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name);
|
||||
|
||||
/* register PCI devices */
|
||||
if (dn->name != 0 && strcmp(dn->name, "pci") == 0) {
|
||||
if (!is_php_dn(dn, &indexes, &names, &types, &power_domains))
|
||||
goto exit;
|
||||
|
||||
name = (char *) &names[1];
|
||||
type = (char *) &types[1];
|
||||
for (i = 0; i < indexes[0]; i++,
|
||||
name += (strlen(name) + 1), type += (strlen(type) + 1)) {
|
||||
|
||||
if (!(slot = alloc_slot_struct(dn, indexes[i + 1], name,
|
||||
power_domains[i + 1]))) {
|
||||
retval = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
slot->type = simple_strtoul(type, NULL, 10);
|
||||
|
||||
dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",
|
||||
indexes[i + 1], name, type);
|
||||
|
||||
retval = rpaphp_register_pci_slot(slot);
|
||||
}
|
||||
}
|
||||
exit:
|
||||
dbg("%s - Exit: num_slots=%d rc[%d]\n",
|
||||
__FUNCTION__, num_slots, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit cleanup_slots(void)
|
||||
{
|
||||
struct list_head *tmp, *n;
|
||||
struct slot *slot;
|
||||
|
||||
/*
|
||||
* Unregister all of our slots with the pci_hotplug subsystem,
|
||||
* and free up all memory that we had allocated.
|
||||
* memory will be freed in release_slot callback.
|
||||
*/
|
||||
|
||||
list_for_each_safe(tmp, n, &rpaphp_slot_head) {
|
||||
slot = list_entry(tmp, struct slot, rpaphp_slot_list);
|
||||
list_del(&slot->rpaphp_slot_list);
|
||||
pci_hp_deregister(slot->hotplug_slot);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int __init rpaphp_init(void)
|
||||
{
|
||||
struct device_node *dn = NULL;
|
||||
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
init_MUTEX(&rpaphp_sem);
|
||||
|
||||
while ((dn = of_find_node_by_name(dn, "pci")))
|
||||
rpaphp_add_slot(dn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rpaphp_exit(void)
|
||||
{
|
||||
cleanup_slots();
|
||||
}
|
||||
|
||||
static int __enable_slot(struct slot *slot)
|
||||
{
|
||||
int state;
|
||||
int retval;
|
||||
|
||||
if (slot->state == CONFIGURED)
|
||||
return 0;
|
||||
|
||||
retval = rpaphp_get_sensor_state(slot, &state);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (state == PRESENT) {
|
||||
pcibios_add_pci_devices(slot->bus);
|
||||
slot->state = CONFIGURED;
|
||||
} else if (state == EMPTY) {
|
||||
slot->state = EMPTY;
|
||||
} else {
|
||||
err("%s: slot[%s] is in invalid state\n", __FUNCTION__, slot->name);
|
||||
slot->state = NOT_VALID;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
int retval;
|
||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
||||
|
||||
down(&rpaphp_sem);
|
||||
retval = __enable_slot(slot);
|
||||
up(&rpaphp_sem);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __disable_slot(struct slot *slot)
|
||||
{
|
||||
struct pci_dev *dev, *tmp;
|
||||
|
||||
if (slot->state == NOT_CONFIGURED)
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry_safe(dev, tmp, &slot->bus->devices, bus_list) {
|
||||
eeh_remove_bus_device(dev);
|
||||
pci_remove_bus_device(dev);
|
||||
}
|
||||
|
||||
slot->state = NOT_CONFIGURED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
||||
int retval;
|
||||
|
||||
down(&rpaphp_sem);
|
||||
retval = __disable_slot (slot);
|
||||
up(&rpaphp_sem);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.set_attention_status = set_attention_status,
|
||||
.get_power_status = get_power_status,
|
||||
.get_attention_status = get_attention_status,
|
||||
.get_adapter_status = get_adapter_status,
|
||||
.get_max_bus_speed = get_max_bus_speed,
|
||||
};
|
||||
|
||||
module_init(rpaphp_init);
|
||||
module_exit(rpaphp_exit);
|
||||
|
||||
EXPORT_SYMBOL_GPL(rpaphp_add_slot);
|
||||
EXPORT_SYMBOL_GPL(rpaphp_slot_head);
|
||||
EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
|
||||
214
drivers/pci/hotplug/rpaphp_pci.c
Normal file
214
drivers/pci/hotplug/rpaphp_pci.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
|
||||
* Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
|
||||
*
|
||||
* 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 <lxie@us.ibm.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/machdep.h>
|
||||
|
||||
#include "../pci.h" /* for pci_add_new_bus */
|
||||
#include "rpaphp.h"
|
||||
|
||||
int rpaphp_get_sensor_state(struct slot *slot, int *state)
|
||||
{
|
||||
int rc;
|
||||
int setlevel;
|
||||
|
||||
rc = rtas_get_sensor(DR_ENTITY_SENSE, slot->index, state);
|
||||
|
||||
if (rc < 0) {
|
||||
if (rc == -EFAULT || rc == -EEXIST) {
|
||||
dbg("%s: slot must be power up to get sensor-state\n",
|
||||
__FUNCTION__);
|
||||
|
||||
/* some slots have to be powered up
|
||||
* before get-sensor will succeed.
|
||||
*/
|
||||
rc = rtas_set_power_level(slot->power_domain, POWER_ON,
|
||||
&setlevel);
|
||||
if (rc < 0) {
|
||||
dbg("%s: power on slot[%s] failed rc=%d.\n",
|
||||
__FUNCTION__, slot->name, rc);
|
||||
} else {
|
||||
rc = rtas_get_sensor(DR_ENTITY_SENSE,
|
||||
slot->index, state);
|
||||
}
|
||||
} else if (rc == -ENODEV)
|
||||
info("%s: slot is unusable\n", __FUNCTION__);
|
||||
else
|
||||
err("%s failed to get sensor state\n", __FUNCTION__);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_pci_adapter_status - get the status of a slot
|
||||
*
|
||||
* 0-- slot is empty
|
||||
* 1-- adapter is configured
|
||||
* 2-- adapter is not configured
|
||||
* 3-- not valid
|
||||
*/
|
||||
int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value)
|
||||
{
|
||||
struct pci_bus *bus;
|
||||
int state, rc;
|
||||
|
||||
*value = NOT_VALID;
|
||||
rc = rpaphp_get_sensor_state(slot, &state);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
if (state == EMPTY)
|
||||
*value = EMPTY;
|
||||
else if (state == PRESENT) {
|
||||
if (!is_init) {
|
||||
/* at run-time slot->state can be changed by */
|
||||
/* config/unconfig adapter */
|
||||
*value = slot->state;
|
||||
} else {
|
||||
bus = pcibios_find_pci_bus(slot->dn);
|
||||
if (bus && !list_empty(&bus->devices))
|
||||
*value = CONFIGURED;
|
||||
else
|
||||
*value = NOT_CONFIGURED;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void print_slot_pci_funcs(struct pci_bus *bus)
|
||||
{
|
||||
struct device_node *dn;
|
||||
struct pci_dev *dev;
|
||||
|
||||
dn = pci_bus_to_OF_node(bus);
|
||||
if (!dn)
|
||||
return;
|
||||
|
||||
dbg("%s: pci_devs of slot[%s]\n", __FUNCTION__, dn->full_name);
|
||||
list_for_each_entry (dev, &bus->devices, bus_list)
|
||||
dbg("\t%s\n", pci_name(dev));
|
||||
return;
|
||||
}
|
||||
|
||||
static int setup_pci_hotplug_slot_info(struct slot *slot)
|
||||
{
|
||||
struct hotplug_slot_info *hotplug_slot_info = slot->hotplug_slot->info;
|
||||
|
||||
dbg("%s Initilize the PCI slot's hotplug->info structure ...\n",
|
||||
__FUNCTION__);
|
||||
rpaphp_get_power_status(slot, &hotplug_slot_info->power_status);
|
||||
rpaphp_get_pci_adapter_status(slot, 1,
|
||||
&hotplug_slot_info->adapter_status);
|
||||
if (hotplug_slot_info->adapter_status == NOT_VALID) {
|
||||
err("%s: NOT_VALID: skip dn->full_name=%s\n",
|
||||
__FUNCTION__, slot->dn->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_slot_name(struct slot *slot)
|
||||
{
|
||||
struct pci_bus *bus = slot->bus;
|
||||
struct pci_dev *bridge;
|
||||
|
||||
bridge = bus->self;
|
||||
if (bridge)
|
||||
strcpy(slot->name, pci_name(bridge));
|
||||
else
|
||||
sprintf(slot->name, "%04x:%02x:00.0", pci_domain_nr(bus),
|
||||
bus->number);
|
||||
}
|
||||
|
||||
static int setup_pci_slot(struct slot *slot)
|
||||
{
|
||||
struct device_node *dn = slot->dn;
|
||||
struct pci_bus *bus;
|
||||
|
||||
BUG_ON(!dn);
|
||||
bus = pcibios_find_pci_bus(dn);
|
||||
if (!bus) {
|
||||
err("%s: no pci_bus for dn %s\n", __FUNCTION__, dn->full_name);
|
||||
goto exit_rc;
|
||||
}
|
||||
|
||||
slot->bus = bus;
|
||||
slot->pci_devs = &bus->devices;
|
||||
set_slot_name(slot);
|
||||
|
||||
/* find slot's pci_dev if it's not empty */
|
||||
if (slot->hotplug_slot->info->adapter_status == EMPTY) {
|
||||
slot->state = EMPTY; /* slot is empty */
|
||||
} else {
|
||||
/* slot is occupied */
|
||||
if (!dn->child) {
|
||||
/* non-empty slot has to have child */
|
||||
err("%s: slot[%s]'s device_node doesn't have child for adapter\n",
|
||||
__FUNCTION__, slot->name);
|
||||
goto exit_rc;
|
||||
}
|
||||
|
||||
if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) {
|
||||
dbg("%s CONFIGURING pci adapter in slot[%s]\n",
|
||||
__FUNCTION__, slot->name);
|
||||
pcibios_add_pci_devices(slot->bus);
|
||||
|
||||
} else if (slot->hotplug_slot->info->adapter_status != CONFIGURED) {
|
||||
err("%s: slot[%s]'s adapter_status is NOT_VALID.\n",
|
||||
__FUNCTION__, slot->name);
|
||||
goto exit_rc;
|
||||
}
|
||||
print_slot_pci_funcs(slot->bus);
|
||||
if (!list_empty(slot->pci_devs)) {
|
||||
slot->state = CONFIGURED;
|
||||
} else {
|
||||
/* DLPAR add as opposed to
|
||||
* boot time */
|
||||
slot->state = NOT_CONFIGURED;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
exit_rc:
|
||||
dealloc_slot_struct(slot);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int rpaphp_register_pci_slot(struct slot *slot)
|
||||
{
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (setup_pci_hotplug_slot_info(slot))
|
||||
goto exit_rc;
|
||||
if (setup_pci_slot(slot))
|
||||
goto exit_rc;
|
||||
rc = rpaphp_register_slot(slot);
|
||||
exit_rc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
223
drivers/pci/hotplug/rpaphp_slot.c
Normal file
223
drivers/pci/hotplug/rpaphp_slot.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* RPA Virtual I/O device functions
|
||||
* Copyright (C) 2004 Linda Xie <lxie@us.ibm.com>
|
||||
*
|
||||
* 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 <lxie@us.ibm.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/rtas.h>
|
||||
#include "rpaphp.h"
|
||||
|
||||
static ssize_t location_read_file (struct hotplug_slot *php_slot, char *buf)
|
||||
{
|
||||
char *value;
|
||||
int retval = -ENOENT;
|
||||
struct slot *slot = (struct slot *)php_slot->private;
|
||||
|
||||
if (!slot)
|
||||
return retval;
|
||||
|
||||
value = slot->location;
|
||||
retval = sprintf (buf, "%s\n", value);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute php_attr_location = {
|
||||
.attr = {.name = "phy_location", .mode = S_IFREG | S_IRUGO},
|
||||
.show = location_read_file,
|
||||
};
|
||||
|
||||
/* free up the memory used by a slot */
|
||||
static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = (struct slot *) hotplug_slot->private;
|
||||
|
||||
dealloc_slot_struct(slot);
|
||||
}
|
||||
|
||||
void dealloc_slot_struct(struct slot *slot)
|
||||
{
|
||||
kfree(slot->hotplug_slot->info);
|
||||
kfree(slot->hotplug_slot->name);
|
||||
kfree(slot->hotplug_slot);
|
||||
kfree(slot);
|
||||
return;
|
||||
}
|
||||
|
||||
struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name,
|
||||
int power_domain)
|
||||
{
|
||||
struct slot *slot;
|
||||
|
||||
slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
|
||||
if (!slot)
|
||||
goto error_nomem;
|
||||
slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
|
||||
if (!slot->hotplug_slot)
|
||||
goto error_slot;
|
||||
slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
|
||||
GFP_KERNEL);
|
||||
if (!slot->hotplug_slot->info)
|
||||
goto error_hpslot;
|
||||
slot->hotplug_slot->name = kmalloc(BUS_ID_SIZE + 1, GFP_KERNEL);
|
||||
if (!slot->hotplug_slot->name)
|
||||
goto error_info;
|
||||
slot->location = kmalloc(strlen(drc_name) + 1, GFP_KERNEL);
|
||||
if (!slot->location)
|
||||
goto error_name;
|
||||
slot->name = slot->hotplug_slot->name;
|
||||
slot->dn = dn;
|
||||
slot->index = drc_index;
|
||||
strcpy(slot->location, drc_name);
|
||||
slot->power_domain = power_domain;
|
||||
slot->hotplug_slot->private = slot;
|
||||
slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
|
||||
slot->hotplug_slot->release = &rpaphp_release_slot;
|
||||
|
||||
return (slot);
|
||||
|
||||
error_name:
|
||||
kfree(slot->hotplug_slot->name);
|
||||
error_info:
|
||||
kfree(slot->hotplug_slot->info);
|
||||
error_hpslot:
|
||||
kfree(slot->hotplug_slot);
|
||||
error_slot:
|
||||
kfree(slot);
|
||||
error_nomem:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int is_registered(struct slot *slot)
|
||||
{
|
||||
struct slot *tmp_slot;
|
||||
|
||||
list_for_each_entry(tmp_slot, &rpaphp_slot_head, rpaphp_slot_list) {
|
||||
if (!strcmp(tmp_slot->name, slot->name))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rpaphp_deregister_slot(struct slot *slot)
|
||||
{
|
||||
int retval = 0;
|
||||
struct hotplug_slot *php_slot = slot->hotplug_slot;
|
||||
|
||||
dbg("%s - Entry: deregistering slot=%s\n",
|
||||
__FUNCTION__, slot->name);
|
||||
|
||||
list_del(&slot->rpaphp_slot_list);
|
||||
|
||||
/* remove "phy_location" file */
|
||||
sysfs_remove_file(&php_slot->kobj, &php_attr_location.attr);
|
||||
|
||||
retval = pci_hp_deregister(php_slot);
|
||||
if (retval)
|
||||
err("Problem unregistering a slot %s\n", slot->name);
|
||||
else
|
||||
num_slots--;
|
||||
|
||||
dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
|
||||
|
||||
int rpaphp_register_slot(struct slot *slot)
|
||||
{
|
||||
struct hotplug_slot *php_slot = slot->hotplug_slot;
|
||||
int retval;
|
||||
|
||||
dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
|
||||
__FUNCTION__, slot->dn->full_name, slot->index, slot->name,
|
||||
slot->power_domain, slot->type);
|
||||
|
||||
/* should not try to register the same slot twice */
|
||||
if (is_registered(slot)) {
|
||||
err("rpaphp_register_slot: slot[%s] is already registered\n", slot->name);
|
||||
retval = -EAGAIN;
|
||||
goto register_fail;
|
||||
}
|
||||
|
||||
retval = pci_hp_register(php_slot);
|
||||
if (retval) {
|
||||
err("pci_hp_register failed with error %d\n", retval);
|
||||
goto register_fail;
|
||||
}
|
||||
|
||||
/* create "phy_location" file */
|
||||
retval = sysfs_create_file(&php_slot->kobj, &php_attr_location.attr);
|
||||
if (retval) {
|
||||
err("sysfs_create_file failed with error %d\n", retval);
|
||||
goto sysfs_fail;
|
||||
}
|
||||
|
||||
/* add slot to our internal list */
|
||||
list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
|
||||
info("Slot [%s](PCI location=%s) registered\n", slot->name,
|
||||
slot->location);
|
||||
num_slots++;
|
||||
return 0;
|
||||
|
||||
sysfs_fail:
|
||||
pci_hp_deregister(php_slot);
|
||||
register_fail:
|
||||
rpaphp_release_slot(php_slot);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int rpaphp_get_power_status(struct slot *slot, u8 * value)
|
||||
{
|
||||
int rc = 0, level;
|
||||
|
||||
rc = rtas_get_power_level(slot->power_domain, &level);
|
||||
if (rc < 0) {
|
||||
err("failed to get power-level for slot(%s), rc=0x%x\n",
|
||||
slot->location, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
dbg("%s the power level of slot %s(pwd-domain:0x%x) is %d\n",
|
||||
__FUNCTION__, slot->name, slot->power_domain, level);
|
||||
*value = level;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int rpaphp_set_attention_status(struct slot *slot, u8 status)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* status: LED_OFF or LED_ON */
|
||||
rc = rtas_set_indicator(DR_INDICATOR, slot->index, status);
|
||||
if (rc < 0)
|
||||
err("slot(name=%s location=%s index=0x%x) set attention-status(%d) failed! rc=0x%x\n",
|
||||
slot->name, slot->location, slot->index, status, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
733
drivers/pci/hotplug/sgi_hotplug.c
Normal file
733
drivers/pci/hotplug/sgi_hotplug.c
Normal file
@@ -0,0 +1,733 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 2005-2006 Silicon Graphics, Inc. All rights reserved.
|
||||
*
|
||||
* This work was based on the 2.4/2.6 kernel development by Dick Reigner.
|
||||
* Work to add BIOS PROM support was completed by Mike Habeck.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <asm/sn/addrs.h>
|
||||
#include <asm/sn/geo.h>
|
||||
#include <asm/sn/l1.h>
|
||||
#include <asm/sn/module.h>
|
||||
#include <asm/sn/pcibr_provider.h>
|
||||
#include <asm/sn/pcibus_provider_defs.h>
|
||||
#include <asm/sn/pcidev.h>
|
||||
#include <asm/sn/sn_feature_sets.h>
|
||||
#include <asm/sn/sn_sal.h>
|
||||
#include <asm/sn/types.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/sn/acpi.h>
|
||||
|
||||
#include "../pci.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com)");
|
||||
MODULE_DESCRIPTION("SGI Altix Hot Plug PCI Controller Driver");
|
||||
|
||||
|
||||
/* SAL call error codes. Keep in sync with prom header io/include/pcibr.h */
|
||||
#define PCI_SLOT_ALREADY_UP 2 /* slot already up */
|
||||
#define PCI_SLOT_ALREADY_DOWN 3 /* slot already down */
|
||||
#define PCI_L1_ERR 7 /* L1 console command error */
|
||||
#define PCI_EMPTY_33MHZ 15 /* empty 33 MHz bus */
|
||||
|
||||
|
||||
#define PCIIO_ASIC_TYPE_TIOCA 4
|
||||
#define PCI_L1_QSIZE 128 /* our L1 message buffer size */
|
||||
#define SN_MAX_HP_SLOTS 32 /* max hotplug slots */
|
||||
#define SN_SLOT_NAME_SIZE 33 /* size of name string */
|
||||
|
||||
/* internal list head */
|
||||
static struct list_head sn_hp_list;
|
||||
|
||||
/* hotplug_slot struct's private pointer */
|
||||
struct slot {
|
||||
int device_num;
|
||||
struct pci_bus *pci_bus;
|
||||
/* this struct for glue internal only */
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct list_head hp_list;
|
||||
char physical_path[SN_SLOT_NAME_SIZE];
|
||||
};
|
||||
|
||||
struct pcibr_slot_enable_resp {
|
||||
int resp_sub_errno;
|
||||
char resp_l1_msg[PCI_L1_QSIZE + 1];
|
||||
};
|
||||
|
||||
struct pcibr_slot_disable_resp {
|
||||
int resp_sub_errno;
|
||||
char resp_l1_msg[PCI_L1_QSIZE + 1];
|
||||
};
|
||||
|
||||
enum sn_pci_req_e {
|
||||
PCI_REQ_SLOT_ELIGIBLE,
|
||||
PCI_REQ_SLOT_DISABLE
|
||||
};
|
||||
|
||||
static int enable_slot(struct hotplug_slot *slot);
|
||||
static int disable_slot(struct hotplug_slot *slot);
|
||||
static inline int get_power_status(struct hotplug_slot *slot, u8 *value);
|
||||
|
||||
static struct hotplug_slot_ops sn_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.get_power_status = get_power_status,
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(sn_hotplug_mutex);
|
||||
|
||||
static ssize_t path_show (struct hotplug_slot *bss_hotplug_slot,
|
||||
char *buf)
|
||||
{
|
||||
int retval = -ENOENT;
|
||||
struct slot *slot = bss_hotplug_slot->private;
|
||||
|
||||
if (!slot)
|
||||
return retval;
|
||||
|
||||
retval = sprintf (buf, "%s\n", slot->physical_path);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct hotplug_slot_attribute sn_slot_path_attr = __ATTR_RO(path);
|
||||
|
||||
static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device)
|
||||
{
|
||||
struct pcibus_info *pcibus_info;
|
||||
u16 busnum, segment, ioboard_type;
|
||||
|
||||
pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
|
||||
|
||||
/* Check to see if this is a valid slot on 'pci_bus' */
|
||||
if (!(pcibus_info->pbi_valid_devices & (1 << device)))
|
||||
return -EPERM;
|
||||
|
||||
ioboard_type = sn_ioboard_to_pci_bus(pci_bus);
|
||||
busnum = pcibus_info->pbi_buscommon.bs_persist_busnum;
|
||||
segment = pci_domain_nr(pci_bus) & 0xf;
|
||||
|
||||
/* Do not allow hotplug operations on base I/O cards */
|
||||
if ((ioboard_type == L1_BRICKTYPE_IX ||
|
||||
ioboard_type == L1_BRICKTYPE_IA) &&
|
||||
(segment == 1 && busnum == 0 && device != 1))
|
||||
return -EPERM;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sn_pci_bus_valid(struct pci_bus *pci_bus)
|
||||
{
|
||||
struct pcibus_info *pcibus_info;
|
||||
u32 asic_type;
|
||||
u16 ioboard_type;
|
||||
|
||||
/* Don't register slots hanging off the TIOCA bus */
|
||||
pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
|
||||
asic_type = pcibus_info->pbi_buscommon.bs_asic_type;
|
||||
if (asic_type == PCIIO_ASIC_TYPE_TIOCA)
|
||||
return -EPERM;
|
||||
|
||||
/* Only register slots in I/O Bricks that support hotplug */
|
||||
ioboard_type = sn_ioboard_to_pci_bus(pci_bus);
|
||||
switch (ioboard_type) {
|
||||
case L1_BRICKTYPE_IX:
|
||||
case L1_BRICKTYPE_PX:
|
||||
case L1_BRICKTYPE_IA:
|
||||
case L1_BRICKTYPE_PA:
|
||||
case L1_BOARDTYPE_PCIX3SLOT:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
return -EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
|
||||
struct pci_bus *pci_bus, int device)
|
||||
{
|
||||
struct pcibus_info *pcibus_info;
|
||||
struct slot *slot;
|
||||
|
||||
pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
|
||||
|
||||
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
||||
if (!slot)
|
||||
return -ENOMEM;
|
||||
bss_hotplug_slot->private = slot;
|
||||
|
||||
bss_hotplug_slot->name = kmalloc(SN_SLOT_NAME_SIZE, GFP_KERNEL);
|
||||
if (!bss_hotplug_slot->name) {
|
||||
kfree(bss_hotplug_slot->private);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
slot->device_num = device;
|
||||
slot->pci_bus = pci_bus;
|
||||
sprintf(bss_hotplug_slot->name, "%04x:%02x:%02x",
|
||||
pci_domain_nr(pci_bus),
|
||||
((u16)pcibus_info->pbi_buscommon.bs_persist_busnum),
|
||||
device + 1);
|
||||
|
||||
sn_generate_path(pci_bus, slot->physical_path);
|
||||
|
||||
slot->hotplug_slot = bss_hotplug_slot;
|
||||
list_add(&slot->hp_list, &sn_hp_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hotplug_slot * sn_hp_destroy(void)
|
||||
{
|
||||
struct slot *slot;
|
||||
struct hotplug_slot *bss_hotplug_slot = NULL;
|
||||
|
||||
list_for_each_entry(slot, &sn_hp_list, hp_list) {
|
||||
bss_hotplug_slot = slot->hotplug_slot;
|
||||
list_del(&((struct slot *)bss_hotplug_slot->private)->
|
||||
hp_list);
|
||||
sysfs_remove_file(&bss_hotplug_slot->kobj,
|
||||
&sn_slot_path_attr.attr);
|
||||
break;
|
||||
}
|
||||
return bss_hotplug_slot;
|
||||
}
|
||||
|
||||
static void sn_bus_free_data(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_bus *subordinate_bus;
|
||||
struct pci_dev *child;
|
||||
|
||||
/* Recursively clean up sn_irq_info structs */
|
||||
if (dev->subordinate) {
|
||||
subordinate_bus = dev->subordinate;
|
||||
list_for_each_entry(child, &subordinate_bus->devices, bus_list)
|
||||
sn_bus_free_data(child);
|
||||
}
|
||||
/*
|
||||
* Some drivers may use dma accesses during the
|
||||
* driver remove function. We release the sysdata
|
||||
* areas after the driver remove functions have
|
||||
* been called.
|
||||
*/
|
||||
sn_bus_store_sysdata(dev);
|
||||
sn_pci_unfixup_slot(dev);
|
||||
}
|
||||
|
||||
static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
|
||||
int device_num, char **ssdt)
|
||||
{
|
||||
struct slot *slot = bss_hotplug_slot->private;
|
||||
struct pcibus_info *pcibus_info;
|
||||
struct pcibr_slot_enable_resp resp;
|
||||
int rc;
|
||||
|
||||
pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
|
||||
|
||||
/*
|
||||
* Power-on and initialize the slot in the SN
|
||||
* PCI infrastructure.
|
||||
*/
|
||||
rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp, ssdt);
|
||||
|
||||
|
||||
if (rc == PCI_SLOT_ALREADY_UP) {
|
||||
dev_dbg(slot->pci_bus->self, "is already active\n");
|
||||
return 1; /* return 1 to user */
|
||||
}
|
||||
|
||||
if (rc == PCI_L1_ERR) {
|
||||
dev_dbg(slot->pci_bus->self,
|
||||
"L1 failure %d with message: %s",
|
||||
resp.resp_sub_errno, resp.resp_l1_msg);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
dev_dbg(slot->pci_bus->self,
|
||||
"insert failed with error %d sub-error %d\n",
|
||||
rc, resp.resp_sub_errno);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
|
||||
pcibus_info->pbi_enabled_devices |= (1 << device_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
|
||||
int device_num, int action)
|
||||
{
|
||||
struct slot *slot = bss_hotplug_slot->private;
|
||||
struct pcibus_info *pcibus_info;
|
||||
struct pcibr_slot_disable_resp resp;
|
||||
int rc;
|
||||
|
||||
pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
|
||||
|
||||
rc = sal_pcibr_slot_disable(pcibus_info, device_num, action, &resp);
|
||||
|
||||
if ((action == PCI_REQ_SLOT_ELIGIBLE) &&
|
||||
(rc == PCI_SLOT_ALREADY_DOWN)) {
|
||||
dev_dbg(slot->pci_bus->self, "Slot %s already inactive\n");
|
||||
return 1; /* return 1 to user */
|
||||
}
|
||||
|
||||
if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_EMPTY_33MHZ)) {
|
||||
dev_dbg(slot->pci_bus->self,
|
||||
"Cannot remove last 33MHz card\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_L1_ERR)) {
|
||||
dev_dbg(slot->pci_bus->self,
|
||||
"L1 failure %d with message \n%s\n",
|
||||
resp.resp_sub_errno, resp.resp_l1_msg);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if ((action == PCI_REQ_SLOT_ELIGIBLE) && rc) {
|
||||
dev_dbg(slot->pci_bus->self,
|
||||
"remove failed with error %d sub-error %d\n",
|
||||
rc, resp.resp_sub_errno);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if ((action == PCI_REQ_SLOT_ELIGIBLE) && !rc)
|
||||
return 0;
|
||||
|
||||
if ((action == PCI_REQ_SLOT_DISABLE) && !rc) {
|
||||
pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
|
||||
pcibus_info->pbi_enabled_devices &= ~(1 << device_num);
|
||||
dev_dbg(slot->pci_bus->self, "remove successful\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((action == PCI_REQ_SLOT_DISABLE) && rc) {
|
||||
dev_dbg(slot->pci_bus->self,"remove failed rc = %d\n", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power up and configure the slot via a SAL call to PROM.
|
||||
* Scan slot (and any children), do any platform specific fixup,
|
||||
* and find device driver.
|
||||
*/
|
||||
static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
|
||||
{
|
||||
struct slot *slot = bss_hotplug_slot->private;
|
||||
struct pci_bus *new_bus = NULL;
|
||||
struct pci_dev *dev;
|
||||
int func, num_funcs;
|
||||
int new_ppb = 0;
|
||||
int rc;
|
||||
char *ssdt = NULL;
|
||||
void pcibios_fixup_device_resources(struct pci_dev *);
|
||||
|
||||
/* Serialize the Linux PCI infrastructure */
|
||||
mutex_lock(&sn_hotplug_mutex);
|
||||
|
||||
/*
|
||||
* Power-on and initialize the slot in the SN
|
||||
* PCI infrastructure. Also, retrieve the ACPI SSDT
|
||||
* table for the slot (if ACPI capable PROM).
|
||||
*/
|
||||
rc = sn_slot_enable(bss_hotplug_slot, slot->device_num, &ssdt);
|
||||
if (rc) {
|
||||
mutex_unlock(&sn_hotplug_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ssdt)
|
||||
ssdt = __va(ssdt);
|
||||
/* Add the new SSDT for the slot to the ACPI namespace */
|
||||
if (SN_ACPI_BASE_SUPPORT() && ssdt) {
|
||||
acpi_status ret;
|
||||
|
||||
ret = acpi_load_table((struct acpi_table_header *)ssdt);
|
||||
if (ACPI_FAILURE(ret)) {
|
||||
printk(KERN_ERR "%s: acpi_load_table failed (0x%x)\n",
|
||||
__FUNCTION__, ret);
|
||||
/* try to continue on */
|
||||
}
|
||||
}
|
||||
|
||||
num_funcs = pci_scan_slot(slot->pci_bus,
|
||||
PCI_DEVFN(slot->device_num + 1, 0));
|
||||
if (!num_funcs) {
|
||||
dev_dbg(slot->pci_bus->self, "no device in slot\n");
|
||||
mutex_unlock(&sn_hotplug_mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map SN resources for all functions on the card
|
||||
* to the Linux PCI interface and tell the drivers
|
||||
* about them.
|
||||
*/
|
||||
for (func = 0; func < num_funcs; func++) {
|
||||
dev = pci_get_slot(slot->pci_bus,
|
||||
PCI_DEVFN(slot->device_num + 1,
|
||||
PCI_FUNC(func)));
|
||||
if (dev) {
|
||||
/* Need to do slot fixup on PPB before fixup of children
|
||||
* (PPB's pcidev_info needs to be in pcidev_info list
|
||||
* before child's SN_PCIDEV_INFO() call to setup
|
||||
* pdi_host_pcidev_info).
|
||||
*/
|
||||
pcibios_fixup_device_resources(dev);
|
||||
if (SN_ACPI_BASE_SUPPORT())
|
||||
sn_acpi_slot_fixup(dev);
|
||||
else
|
||||
sn_io_slot_fixup(dev);
|
||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
||||
unsigned char sec_bus;
|
||||
pci_read_config_byte(dev, PCI_SECONDARY_BUS,
|
||||
&sec_bus);
|
||||
new_bus = pci_add_new_bus(dev->bus, dev,
|
||||
sec_bus);
|
||||
pci_scan_child_bus(new_bus);
|
||||
new_ppb = 1;
|
||||
}
|
||||
pci_dev_put(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the slot's devices to the ACPI infrastructure */
|
||||
if (SN_ACPI_BASE_SUPPORT() && ssdt) {
|
||||
unsigned long adr;
|
||||
struct acpi_device *pdevice;
|
||||
struct acpi_device *device;
|
||||
acpi_handle phandle;
|
||||
acpi_handle chandle = NULL;
|
||||
acpi_handle rethandle;
|
||||
acpi_status ret;
|
||||
|
||||
phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle;
|
||||
|
||||
if (acpi_bus_get_device(phandle, &pdevice)) {
|
||||
dev_dbg(slot->pci_bus->self,
|
||||
"no parent device, assuming NULL\n");
|
||||
pdevice = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the rootbus node's immediate children looking for
|
||||
* the slot's device node(s). There can be more than
|
||||
* one for multifunction devices.
|
||||
*/
|
||||
for (;;) {
|
||||
rethandle = NULL;
|
||||
ret = acpi_get_next_object(ACPI_TYPE_DEVICE,
|
||||
phandle, chandle,
|
||||
&rethandle);
|
||||
|
||||
if (ret == AE_NOT_FOUND || rethandle == NULL)
|
||||
break;
|
||||
|
||||
chandle = rethandle;
|
||||
|
||||
ret = acpi_evaluate_integer(chandle, METHOD_NAME__ADR,
|
||||
NULL, &adr);
|
||||
|
||||
if (ACPI_SUCCESS(ret) &&
|
||||
(adr>>16) == (slot->device_num + 1)) {
|
||||
|
||||
ret = acpi_bus_add(&device, pdevice, chandle,
|
||||
ACPI_BUS_TYPE_DEVICE);
|
||||
if (ACPI_FAILURE(ret)) {
|
||||
printk(KERN_ERR "%s: acpi_bus_add "
|
||||
"failed (0x%x) for slot %d "
|
||||
"func %d\n", __FUNCTION__,
|
||||
ret, (int)(adr>>16),
|
||||
(int)(adr&0xffff));
|
||||
/* try to continue on */
|
||||
} else {
|
||||
acpi_bus_start(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Call the driver for the new device */
|
||||
pci_bus_add_devices(slot->pci_bus);
|
||||
/* Call the drivers for the new devices subordinate to PPB */
|
||||
if (new_ppb)
|
||||
pci_bus_add_devices(new_bus);
|
||||
|
||||
mutex_unlock(&sn_hotplug_mutex);
|
||||
|
||||
if (rc == 0)
|
||||
dev_dbg(slot->pci_bus->self,
|
||||
"insert operation successful\n");
|
||||
else
|
||||
dev_dbg(slot->pci_bus->self,
|
||||
"insert operation failed rc = %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
|
||||
{
|
||||
struct slot *slot = bss_hotplug_slot->private;
|
||||
struct pci_dev *dev;
|
||||
int func;
|
||||
int rc;
|
||||
acpi_owner_id ssdt_id = 0;
|
||||
|
||||
/* Acquire update access to the bus */
|
||||
mutex_lock(&sn_hotplug_mutex);
|
||||
|
||||
/* is it okay to bring this slot down? */
|
||||
rc = sn_slot_disable(bss_hotplug_slot, slot->device_num,
|
||||
PCI_REQ_SLOT_ELIGIBLE);
|
||||
if (rc)
|
||||
goto leaving;
|
||||
|
||||
/* free the ACPI resources for the slot */
|
||||
if (SN_ACPI_BASE_SUPPORT() &&
|
||||
PCI_CONTROLLER(slot->pci_bus)->acpi_handle) {
|
||||
unsigned long adr;
|
||||
struct acpi_device *device;
|
||||
acpi_handle phandle;
|
||||
acpi_handle chandle = NULL;
|
||||
acpi_handle rethandle;
|
||||
acpi_status ret;
|
||||
|
||||
/* Get the rootbus node pointer */
|
||||
phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle;
|
||||
|
||||
/*
|
||||
* Walk the rootbus node's immediate children looking for
|
||||
* the slot's device node(s). There can be more than
|
||||
* one for multifunction devices.
|
||||
*/
|
||||
for (;;) {
|
||||
rethandle = NULL;
|
||||
ret = acpi_get_next_object(ACPI_TYPE_DEVICE,
|
||||
phandle, chandle,
|
||||
&rethandle);
|
||||
|
||||
if (ret == AE_NOT_FOUND || rethandle == NULL)
|
||||
break;
|
||||
|
||||
chandle = rethandle;
|
||||
|
||||
ret = acpi_evaluate_integer(chandle,
|
||||
METHOD_NAME__ADR,
|
||||
NULL, &adr);
|
||||
if (ACPI_SUCCESS(ret) &&
|
||||
(adr>>16) == (slot->device_num + 1)) {
|
||||
/* retain the owner id */
|
||||
acpi_get_id(chandle, &ssdt_id);
|
||||
|
||||
ret = acpi_bus_get_device(chandle,
|
||||
&device);
|
||||
if (ACPI_SUCCESS(ret))
|
||||
acpi_bus_trim(device, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Free the SN resources assigned to the Linux device.*/
|
||||
for (func = 0; func < 8; func++) {
|
||||
dev = pci_get_slot(slot->pci_bus,
|
||||
PCI_DEVFN(slot->device_num + 1,
|
||||
PCI_FUNC(func)));
|
||||
if (dev) {
|
||||
sn_bus_free_data(dev);
|
||||
pci_remove_bus_device(dev);
|
||||
pci_dev_put(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the SSDT for the slot from the ACPI namespace */
|
||||
if (SN_ACPI_BASE_SUPPORT() && ssdt_id) {
|
||||
acpi_status ret;
|
||||
ret = acpi_unload_table_id(ssdt_id);
|
||||
if (ACPI_FAILURE(ret)) {
|
||||
printk(KERN_ERR "%s: acpi_unload_table_id "
|
||||
"failed (0x%x) for id %d\n",
|
||||
__FUNCTION__, ret, ssdt_id);
|
||||
/* try to continue on */
|
||||
}
|
||||
}
|
||||
|
||||
/* free the collected sysdata pointers */
|
||||
sn_bus_free_sysdata();
|
||||
|
||||
/* Deactivate slot */
|
||||
rc = sn_slot_disable(bss_hotplug_slot, slot->device_num,
|
||||
PCI_REQ_SLOT_DISABLE);
|
||||
leaving:
|
||||
/* Release the bus lock */
|
||||
mutex_unlock(&sn_hotplug_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot,
|
||||
u8 *value)
|
||||
{
|
||||
struct slot *slot = bss_hotplug_slot->private;
|
||||
struct pcibus_info *pcibus_info;
|
||||
u32 power;
|
||||
|
||||
pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
|
||||
mutex_lock(&sn_hotplug_mutex);
|
||||
power = pcibus_info->pbi_enabled_devices & (1 << slot->device_num);
|
||||
*value = power ? 1 : 0;
|
||||
mutex_unlock(&sn_hotplug_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
|
||||
{
|
||||
kfree(bss_hotplug_slot->info);
|
||||
kfree(bss_hotplug_slot->name);
|
||||
kfree(bss_hotplug_slot->private);
|
||||
kfree(bss_hotplug_slot);
|
||||
}
|
||||
|
||||
static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
|
||||
{
|
||||
int device;
|
||||
struct hotplug_slot *bss_hotplug_slot;
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
* Currently only four devices are supported,
|
||||
* in the future there maybe more -- up to 32.
|
||||
*/
|
||||
|
||||
for (device = 0; device < SN_MAX_HP_SLOTS ; device++) {
|
||||
if (sn_pci_slot_valid(pci_bus, device) != 1)
|
||||
continue;
|
||||
|
||||
bss_hotplug_slot = kzalloc(sizeof(*bss_hotplug_slot),
|
||||
GFP_KERNEL);
|
||||
if (!bss_hotplug_slot) {
|
||||
rc = -ENOMEM;
|
||||
goto alloc_err;
|
||||
}
|
||||
|
||||
bss_hotplug_slot->info =
|
||||
kzalloc(sizeof(struct hotplug_slot_info),
|
||||
GFP_KERNEL);
|
||||
if (!bss_hotplug_slot->info) {
|
||||
rc = -ENOMEM;
|
||||
goto alloc_err;
|
||||
}
|
||||
|
||||
if (sn_hp_slot_private_alloc(bss_hotplug_slot,
|
||||
pci_bus, device)) {
|
||||
rc = -ENOMEM;
|
||||
goto alloc_err;
|
||||
}
|
||||
|
||||
bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
|
||||
bss_hotplug_slot->release = &sn_release_slot;
|
||||
|
||||
rc = pci_hp_register(bss_hotplug_slot);
|
||||
if (rc)
|
||||
goto register_err;
|
||||
|
||||
rc = sysfs_create_file(&bss_hotplug_slot->kobj,
|
||||
&sn_slot_path_attr.attr);
|
||||
if (rc)
|
||||
goto register_err;
|
||||
}
|
||||
dev_dbg(pci_bus->self, "Registered bus with hotplug\n");
|
||||
return rc;
|
||||
|
||||
register_err:
|
||||
dev_dbg(pci_bus->self, "bus failed to register with err = %d\n",
|
||||
rc);
|
||||
|
||||
alloc_err:
|
||||
if (rc == -ENOMEM)
|
||||
dev_dbg(pci_bus->self, "Memory allocation error\n");
|
||||
|
||||
/* destroy THIS element */
|
||||
if (bss_hotplug_slot)
|
||||
sn_release_slot(bss_hotplug_slot);
|
||||
|
||||
/* destroy anything else on the list */
|
||||
while ((bss_hotplug_slot = sn_hp_destroy()))
|
||||
pci_hp_deregister(bss_hotplug_slot);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sn_pci_hotplug_init(void)
|
||||
{
|
||||
struct pci_bus *pci_bus = NULL;
|
||||
int rc;
|
||||
int registered = 0;
|
||||
|
||||
if (!sn_prom_feature_available(PRF_HOTPLUG_SUPPORT)) {
|
||||
printk(KERN_ERR "%s: PROM version does not support hotplug.\n",
|
||||
__FUNCTION__);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&sn_hp_list);
|
||||
|
||||
while ((pci_bus = pci_find_next_bus(pci_bus))) {
|
||||
if (!pci_bus->sysdata)
|
||||
continue;
|
||||
|
||||
rc = sn_pci_bus_valid(pci_bus);
|
||||
if (rc != 1) {
|
||||
dev_dbg(pci_bus->self, "not a valid hotplug bus\n");
|
||||
continue;
|
||||
}
|
||||
dev_dbg(pci_bus->self, "valid hotplug bus\n");
|
||||
|
||||
rc = sn_hotplug_slot_register(pci_bus);
|
||||
if (!rc) {
|
||||
registered = 1;
|
||||
} else {
|
||||
registered = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return registered == 1 ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static void sn_pci_hotplug_exit(void)
|
||||
{
|
||||
struct hotplug_slot *bss_hotplug_slot;
|
||||
|
||||
while ((bss_hotplug_slot = sn_hp_destroy()))
|
||||
pci_hp_deregister(bss_hotplug_slot);
|
||||
|
||||
if (!list_empty(&sn_hp_list))
|
||||
printk(KERN_ERR "%s: internal list is not empty\n", __FILE__);
|
||||
}
|
||||
|
||||
module_init(sn_pci_hotplug_init);
|
||||
module_exit(sn_pci_hotplug_exit);
|
||||
337
drivers/pci/hotplug/shpchp.h
Normal file
337
drivers/pci/hotplug/shpchp.h
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Standard Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM
|
||||
* 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 <greg@kroah.com>,<kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
#ifndef _SHPCHP_H
|
||||
#define _SHPCHP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h> /* signal_pending(), struct timer_list */
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#if !defined(MODULE)
|
||||
#define MY_NAME "shpchp"
|
||||
#else
|
||||
#define MY_NAME THIS_MODULE->name
|
||||
#endif
|
||||
|
||||
extern int shpchp_poll_mode;
|
||||
extern int shpchp_poll_time;
|
||||
extern int shpchp_debug;
|
||||
extern struct workqueue_struct *shpchp_wq;
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (shpchp_debug) \
|
||||
printk("%s: " format, MY_NAME , ## arg); \
|
||||
} while (0)
|
||||
#define err(format, arg...) \
|
||||
printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
|
||||
#define info(format, arg...) \
|
||||
printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
|
||||
#define warn(format, arg...) \
|
||||
printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
|
||||
|
||||
#define SLOT_NAME_SIZE 10
|
||||
struct slot {
|
||||
u8 bus;
|
||||
u8 device;
|
||||
u16 status;
|
||||
u32 number;
|
||||
u8 is_a_board;
|
||||
u8 state;
|
||||
u8 presence_save;
|
||||
u8 pwr_save;
|
||||
struct timer_list task_event;
|
||||
u8 hp_slot;
|
||||
struct controller *ctrl;
|
||||
struct hpc_ops *hpc_ops;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct list_head slot_list;
|
||||
char name[SLOT_NAME_SIZE];
|
||||
struct delayed_work work; /* work for button event */
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct event_info {
|
||||
u32 event_type;
|
||||
struct slot *p_slot;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
struct controller {
|
||||
struct mutex crit_sect; /* critical section mutex */
|
||||
struct mutex cmd_lock; /* command lock */
|
||||
int num_slots; /* Number of slots on ctlr */
|
||||
int slot_num_inc; /* 1 or -1 */
|
||||
struct pci_dev *pci_dev;
|
||||
struct list_head slot_list;
|
||||
struct hpc_ops *hpc_ops;
|
||||
wait_queue_head_t queue; /* sleep & wake process */
|
||||
u8 slot_device_offset;
|
||||
u32 pcix_misc2_reg; /* for amd pogo errata */
|
||||
u32 first_slot; /* First physical slot number */
|
||||
u32 cap_offset;
|
||||
unsigned long mmio_base;
|
||||
unsigned long mmio_size;
|
||||
void __iomem *creg;
|
||||
struct timer_list poll_timer;
|
||||
};
|
||||
|
||||
/* Define AMD SHPC ID */
|
||||
#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450
|
||||
#define PCI_DEVICE_ID_AMD_POGO_7458 0x7458
|
||||
|
||||
/* AMD PCIX bridge registers */
|
||||
#define PCIX_MEM_BASE_LIMIT_OFFSET 0x1C
|
||||
#define PCIX_MISCII_OFFSET 0x48
|
||||
#define PCIX_MISC_BRIDGE_ERRORS_OFFSET 0x80
|
||||
|
||||
/* AMD PCIX_MISCII masks and offsets */
|
||||
#define PERRNONFATALENABLE_MASK 0x00040000
|
||||
#define PERRFATALENABLE_MASK 0x00080000
|
||||
#define PERRFLOODENABLE_MASK 0x00100000
|
||||
#define SERRNONFATALENABLE_MASK 0x00200000
|
||||
#define SERRFATALENABLE_MASK 0x00400000
|
||||
|
||||
/* AMD PCIX_MISC_BRIDGE_ERRORS masks and offsets */
|
||||
#define PERR_OBSERVED_MASK 0x00000001
|
||||
|
||||
/* AMD PCIX_MEM_BASE_LIMIT masks */
|
||||
#define RSE_MASK 0x40000000
|
||||
|
||||
#define INT_BUTTON_IGNORE 0
|
||||
#define INT_PRESENCE_ON 1
|
||||
#define INT_PRESENCE_OFF 2
|
||||
#define INT_SWITCH_CLOSE 3
|
||||
#define INT_SWITCH_OPEN 4
|
||||
#define INT_POWER_FAULT 5
|
||||
#define INT_POWER_FAULT_CLEAR 6
|
||||
#define INT_BUTTON_PRESS 7
|
||||
#define INT_BUTTON_RELEASE 8
|
||||
#define INT_BUTTON_CANCEL 9
|
||||
|
||||
#define STATIC_STATE 0
|
||||
#define BLINKINGON_STATE 1
|
||||
#define BLINKINGOFF_STATE 2
|
||||
#define POWERON_STATE 3
|
||||
#define POWEROFF_STATE 4
|
||||
|
||||
/* Error messages */
|
||||
#define INTERLOCK_OPEN 0x00000002
|
||||
#define ADD_NOT_SUPPORTED 0x00000003
|
||||
#define CARD_FUNCTIONING 0x00000005
|
||||
#define ADAPTER_NOT_SAME 0x00000006
|
||||
#define NO_ADAPTER_PRESENT 0x00000009
|
||||
#define NOT_ENOUGH_RESOURCES 0x0000000B
|
||||
#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
|
||||
#define WRONG_BUS_FREQUENCY 0x0000000D
|
||||
#define POWER_FAILURE 0x0000000E
|
||||
|
||||
extern int __must_check shpchp_create_ctrl_files(struct controller *ctrl);
|
||||
extern void shpchp_remove_ctrl_files(struct controller *ctrl);
|
||||
extern int shpchp_sysfs_enable_slot(struct slot *slot);
|
||||
extern int shpchp_sysfs_disable_slot(struct slot *slot);
|
||||
extern u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl);
|
||||
extern int shpchp_configure_device(struct slot *p_slot);
|
||||
extern int shpchp_unconfigure_device(struct slot *p_slot);
|
||||
extern void cleanup_slots(struct controller *ctrl);
|
||||
extern void queue_pushbutton_work(struct work_struct *work);
|
||||
extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static inline int get_hp_params_from_firmware(struct pci_dev *dev,
|
||||
struct hotplug_params *hpp)
|
||||
{
|
||||
if (ACPI_FAILURE(acpi_get_hp_params_from_firmware(dev->bus, hpp)))
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
#define get_hp_hw_control_from_firmware(pdev) \
|
||||
do { \
|
||||
if (DEVICE_ACPI_HANDLE(&(pdev->dev))) \
|
||||
acpi_run_oshp(DEVICE_ACPI_HANDLE(&(pdev->dev)));\
|
||||
} while (0)
|
||||
#else
|
||||
#define get_hp_params_from_firmware(dev, hpp) (-ENODEV)
|
||||
#define get_hp_hw_control_from_firmware(dev) do { } while (0)
|
||||
#endif
|
||||
|
||||
struct ctrl_reg {
|
||||
volatile u32 base_offset;
|
||||
volatile u32 slot_avail1;
|
||||
volatile u32 slot_avail2;
|
||||
volatile u32 slot_config;
|
||||
volatile u16 sec_bus_config;
|
||||
volatile u8 msi_ctrl;
|
||||
volatile u8 prog_interface;
|
||||
volatile u16 cmd;
|
||||
volatile u16 cmd_status;
|
||||
volatile u32 intr_loc;
|
||||
volatile u32 serr_loc;
|
||||
volatile u32 serr_intr_enable;
|
||||
volatile u32 slot1;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* offsets to the controller registers based on the above structure layout */
|
||||
enum ctrl_offsets {
|
||||
BASE_OFFSET = offsetof(struct ctrl_reg, base_offset),
|
||||
SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1),
|
||||
SLOT_AVAIL2 = offsetof(struct ctrl_reg, slot_avail2),
|
||||
SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config),
|
||||
SEC_BUS_CONFIG = offsetof(struct ctrl_reg, sec_bus_config),
|
||||
MSI_CTRL = offsetof(struct ctrl_reg, msi_ctrl),
|
||||
PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface),
|
||||
CMD = offsetof(struct ctrl_reg, cmd),
|
||||
CMD_STATUS = offsetof(struct ctrl_reg, cmd_status),
|
||||
INTR_LOC = offsetof(struct ctrl_reg, intr_loc),
|
||||
SERR_LOC = offsetof(struct ctrl_reg, serr_loc),
|
||||
SERR_INTR_ENABLE = offsetof(struct ctrl_reg, serr_intr_enable),
|
||||
SLOT1 = offsetof(struct ctrl_reg, slot1),
|
||||
};
|
||||
|
||||
static inline struct slot *get_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
return hotplug_slot->private;
|
||||
}
|
||||
|
||||
static inline struct slot *shpchp_find_slot(struct controller *ctrl, u8 device)
|
||||
{
|
||||
struct slot *slot;
|
||||
|
||||
list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
|
||||
if (slot->device == device)
|
||||
return slot;
|
||||
}
|
||||
|
||||
err("%s: slot (device=0x%x) not found\n", __FUNCTION__, device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void amd_pogo_errata_save_misc_reg(struct slot *p_slot)
|
||||
{
|
||||
u32 pcix_misc2_temp;
|
||||
|
||||
/* save MiscII register */
|
||||
pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp);
|
||||
|
||||
p_slot->ctrl->pcix_misc2_reg = pcix_misc2_temp;
|
||||
|
||||
/* clear SERR/PERR enable bits */
|
||||
pcix_misc2_temp &= ~SERRFATALENABLE_MASK;
|
||||
pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK;
|
||||
pcix_misc2_temp &= ~PERRFLOODENABLE_MASK;
|
||||
pcix_misc2_temp &= ~PERRFATALENABLE_MASK;
|
||||
pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK;
|
||||
pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp);
|
||||
}
|
||||
|
||||
static inline void amd_pogo_errata_restore_misc_reg(struct slot *p_slot)
|
||||
{
|
||||
u32 pcix_misc2_temp;
|
||||
u32 pcix_bridge_errors_reg;
|
||||
u32 pcix_mem_base_reg;
|
||||
u8 perr_set;
|
||||
u8 rse_set;
|
||||
|
||||
/* write-one-to-clear Bridge_Errors[ PERR_OBSERVED ] */
|
||||
pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, &pcix_bridge_errors_reg);
|
||||
perr_set = pcix_bridge_errors_reg & PERR_OBSERVED_MASK;
|
||||
if (perr_set) {
|
||||
dbg ("%s W1C: Bridge_Errors[ PERR_OBSERVED = %08X]\n",__FUNCTION__ , perr_set);
|
||||
|
||||
pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, perr_set);
|
||||
}
|
||||
|
||||
/* write-one-to-clear Memory_Base_Limit[ RSE ] */
|
||||
pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, &pcix_mem_base_reg);
|
||||
rse_set = pcix_mem_base_reg & RSE_MASK;
|
||||
if (rse_set) {
|
||||
dbg ("%s W1C: Memory_Base_Limit[ RSE ]\n",__FUNCTION__ );
|
||||
|
||||
pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, rse_set);
|
||||
}
|
||||
/* restore MiscII register */
|
||||
pci_read_config_dword( p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp );
|
||||
|
||||
if (p_slot->ctrl->pcix_misc2_reg & SERRFATALENABLE_MASK)
|
||||
pcix_misc2_temp |= SERRFATALENABLE_MASK;
|
||||
else
|
||||
pcix_misc2_temp &= ~SERRFATALENABLE_MASK;
|
||||
|
||||
if (p_slot->ctrl->pcix_misc2_reg & SERRNONFATALENABLE_MASK)
|
||||
pcix_misc2_temp |= SERRNONFATALENABLE_MASK;
|
||||
else
|
||||
pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK;
|
||||
|
||||
if (p_slot->ctrl->pcix_misc2_reg & PERRFLOODENABLE_MASK)
|
||||
pcix_misc2_temp |= PERRFLOODENABLE_MASK;
|
||||
else
|
||||
pcix_misc2_temp &= ~PERRFLOODENABLE_MASK;
|
||||
|
||||
if (p_slot->ctrl->pcix_misc2_reg & PERRFATALENABLE_MASK)
|
||||
pcix_misc2_temp |= PERRFATALENABLE_MASK;
|
||||
else
|
||||
pcix_misc2_temp &= ~PERRFATALENABLE_MASK;
|
||||
|
||||
if (p_slot->ctrl->pcix_misc2_reg & PERRNONFATALENABLE_MASK)
|
||||
pcix_misc2_temp |= PERRNONFATALENABLE_MASK;
|
||||
else
|
||||
pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK;
|
||||
pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp);
|
||||
}
|
||||
|
||||
struct hpc_ops {
|
||||
int (*power_on_slot)(struct slot *slot);
|
||||
int (*slot_enable)(struct slot *slot);
|
||||
int (*slot_disable)(struct slot *slot);
|
||||
int (*set_bus_speed_mode)(struct slot *slot, enum pci_bus_speed speed);
|
||||
int (*get_power_status)(struct slot *slot, u8 *status);
|
||||
int (*get_attention_status)(struct slot *slot, u8 *status);
|
||||
int (*set_attention_status)(struct slot *slot, u8 status);
|
||||
int (*get_latch_status)(struct slot *slot, u8 *status);
|
||||
int (*get_adapter_status)(struct slot *slot, u8 *status);
|
||||
int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
|
||||
int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
|
||||
int (*get_adapter_speed)(struct slot *slot, enum pci_bus_speed *speed);
|
||||
int (*get_mode1_ECC_cap)(struct slot *slot, u8 *mode);
|
||||
int (*get_prog_int)(struct slot *slot, u8 *prog_int);
|
||||
int (*query_power_fault)(struct slot *slot);
|
||||
void (*green_led_on)(struct slot *slot);
|
||||
void (*green_led_off)(struct slot *slot);
|
||||
void (*green_led_blink)(struct slot *slot);
|
||||
void (*release_ctlr)(struct controller *ctrl);
|
||||
int (*check_cmd_status)(struct controller *ctrl);
|
||||
};
|
||||
|
||||
#endif /* _SHPCHP_H */
|
||||
418
drivers/pci/hotplug/shpchp_core.c
Normal file
418
drivers/pci/hotplug/shpchp_core.c
Normal file
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Standard Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "shpchp.h"
|
||||
|
||||
/* Global variables */
|
||||
int shpchp_debug;
|
||||
int shpchp_poll_mode;
|
||||
int shpchp_poll_time;
|
||||
struct workqueue_struct *shpchp_wq;
|
||||
|
||||
#define DRIVER_VERSION "0.4"
|
||||
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
|
||||
#define DRIVER_DESC "Standard Hot Plug PCI Controller Driver"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param(shpchp_debug, bool, 0644);
|
||||
module_param(shpchp_poll_mode, bool, 0644);
|
||||
module_param(shpchp_poll_time, int, 0644);
|
||||
MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
|
||||
MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
|
||||
MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
|
||||
|
||||
#define SHPC_MODULE_NAME "shpchp"
|
||||
|
||||
static int set_attention_status (struct hotplug_slot *slot, u8 value);
|
||||
static int enable_slot (struct hotplug_slot *slot);
|
||||
static int disable_slot (struct hotplug_slot *slot);
|
||||
static int get_power_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
|
||||
static int get_address (struct hotplug_slot *slot, u32 *value);
|
||||
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
|
||||
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
|
||||
|
||||
static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_attention_status = set_attention_status,
|
||||
.enable_slot = enable_slot,
|
||||
.disable_slot = disable_slot,
|
||||
.get_power_status = get_power_status,
|
||||
.get_attention_status = get_attention_status,
|
||||
.get_latch_status = get_latch_status,
|
||||
.get_adapter_status = get_adapter_status,
|
||||
.get_address = get_address,
|
||||
.get_max_bus_speed = get_max_bus_speed,
|
||||
.get_cur_bus_speed = get_cur_bus_speed,
|
||||
};
|
||||
|
||||
/**
|
||||
* release_slot - free up the memory used by a slot
|
||||
* @hotplug_slot: slot to free
|
||||
*/
|
||||
static void release_slot(struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
kfree(slot->hotplug_slot->info);
|
||||
kfree(slot->hotplug_slot);
|
||||
kfree(slot);
|
||||
}
|
||||
|
||||
static void make_slot_name(struct slot *slot)
|
||||
{
|
||||
snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%04d_%04d",
|
||||
slot->bus, slot->number);
|
||||
}
|
||||
|
||||
static int init_slots(struct controller *ctrl)
|
||||
{
|
||||
struct slot *slot;
|
||||
struct hotplug_slot *hotplug_slot;
|
||||
struct hotplug_slot_info *info;
|
||||
int retval = -ENOMEM;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ctrl->num_slots; i++) {
|
||||
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
||||
if (!slot)
|
||||
goto error;
|
||||
|
||||
hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
|
||||
if (!hotplug_slot)
|
||||
goto error_slot;
|
||||
slot->hotplug_slot = hotplug_slot;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
goto error_hpslot;
|
||||
hotplug_slot->info = info;
|
||||
|
||||
hotplug_slot->name = slot->name;
|
||||
|
||||
slot->hp_slot = i;
|
||||
slot->ctrl = ctrl;
|
||||
slot->bus = ctrl->pci_dev->subordinate->number;
|
||||
slot->device = ctrl->slot_device_offset + i;
|
||||
slot->hpc_ops = ctrl->hpc_ops;
|
||||
slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);
|
||||
mutex_init(&slot->lock);
|
||||
INIT_DELAYED_WORK(&slot->work, queue_pushbutton_work);
|
||||
|
||||
/* register this slot with the hotplug pci core */
|
||||
hotplug_slot->private = slot;
|
||||
hotplug_slot->release = &release_slot;
|
||||
make_slot_name(slot);
|
||||
hotplug_slot->ops = &shpchp_hotplug_slot_ops;
|
||||
|
||||
get_power_status(hotplug_slot, &info->power_status);
|
||||
get_attention_status(hotplug_slot, &info->attention_status);
|
||||
get_latch_status(hotplug_slot, &info->latch_status);
|
||||
get_adapter_status(hotplug_slot, &info->adapter_status);
|
||||
|
||||
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
|
||||
"slot_device_offset=%x\n", slot->bus, slot->device,
|
||||
slot->hp_slot, slot->number, ctrl->slot_device_offset);
|
||||
retval = pci_hp_register(slot->hotplug_slot);
|
||||
if (retval) {
|
||||
err("pci_hp_register failed with error %d\n", retval);
|
||||
goto error_info;
|
||||
}
|
||||
|
||||
list_add(&slot->slot_list, &ctrl->slot_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
error_info:
|
||||
kfree(info);
|
||||
error_hpslot:
|
||||
kfree(hotplug_slot);
|
||||
error_slot:
|
||||
kfree(slot);
|
||||
error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
void cleanup_slots(struct controller *ctrl)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct list_head *next;
|
||||
struct slot *slot;
|
||||
|
||||
list_for_each_safe(tmp, next, &ctrl->slot_list) {
|
||||
slot = list_entry(tmp, struct slot, slot_list);
|
||||
list_del(&slot->slot_list);
|
||||
cancel_delayed_work(&slot->work);
|
||||
flush_scheduled_work();
|
||||
flush_workqueue(shpchp_wq);
|
||||
pci_hp_deregister(slot->hotplug_slot);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set_attention_status - Turns the Amber LED for a slot on, off or blink
|
||||
*/
|
||||
static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
hotplug_slot->info->attention_status = status;
|
||||
slot->hpc_ops->set_attention_status(slot, status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_slot (struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
return shpchp_sysfs_enable_slot(slot);
|
||||
}
|
||||
|
||||
static int disable_slot (struct hotplug_slot *hotplug_slot)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
return shpchp_sysfs_disable_slot(slot);
|
||||
}
|
||||
|
||||
static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_power_status(slot, value);
|
||||
if (retval < 0)
|
||||
*value = hotplug_slot->info->power_status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_attention_status(slot, value);
|
||||
if (retval < 0)
|
||||
*value = hotplug_slot->info->attention_status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_latch_status(slot, value);
|
||||
if (retval < 0)
|
||||
*value = hotplug_slot->info->latch_status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_adapter_status(slot, value);
|
||||
if (retval < 0)
|
||||
*value = hotplug_slot->info->adapter_status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_address (struct hotplug_slot *hotplug_slot, u32 *value)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
*value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_max_bus_speed(slot, value);
|
||||
if (retval < 0)
|
||||
*value = PCI_SPEED_UNKNOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
|
||||
{
|
||||
struct slot *slot = get_slot(hotplug_slot);
|
||||
int retval;
|
||||
|
||||
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
|
||||
|
||||
retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
|
||||
if (retval < 0)
|
||||
*value = PCI_SPEED_UNKNOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_shpc_capable(struct pci_dev *dev)
|
||||
{
|
||||
if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
|
||||
PCI_DEVICE_ID_AMD_GOLAM_7450))
|
||||
return 1;
|
||||
if (pci_find_capability(dev, PCI_CAP_ID_SHPC))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
int rc;
|
||||
struct controller *ctrl;
|
||||
|
||||
if (!is_shpc_capable(pdev))
|
||||
return -ENODEV;
|
||||
|
||||
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
|
||||
if (!ctrl) {
|
||||
err("%s : out of memory\n", __FUNCTION__);
|
||||
goto err_out_none;
|
||||
}
|
||||
INIT_LIST_HEAD(&ctrl->slot_list);
|
||||
|
||||
rc = shpc_init(ctrl, pdev);
|
||||
if (rc) {
|
||||
dbg("%s: controller initialization failed\n",
|
||||
SHPC_MODULE_NAME);
|
||||
goto err_out_free_ctrl;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, ctrl);
|
||||
|
||||
/* Setup the slot information structures */
|
||||
rc = init_slots(ctrl);
|
||||
if (rc) {
|
||||
err("%s: slot initialization failed\n", SHPC_MODULE_NAME);
|
||||
goto err_out_release_ctlr;
|
||||
}
|
||||
|
||||
rc = shpchp_create_ctrl_files(ctrl);
|
||||
if (rc)
|
||||
goto err_cleanup_slots;
|
||||
|
||||
return 0;
|
||||
|
||||
err_cleanup_slots:
|
||||
cleanup_slots(ctrl);
|
||||
err_out_release_ctlr:
|
||||
ctrl->hpc_ops->release_ctlr(ctrl);
|
||||
err_out_free_ctrl:
|
||||
kfree(ctrl);
|
||||
err_out_none:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void shpc_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct controller *ctrl = pci_get_drvdata(dev);
|
||||
|
||||
shpchp_remove_ctrl_files(ctrl);
|
||||
ctrl->hpc_ops->release_ctlr(ctrl);
|
||||
kfree(ctrl);
|
||||
}
|
||||
|
||||
static struct pci_device_id shpcd_pci_tbl[] = {
|
||||
{PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0)},
|
||||
{ /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);
|
||||
|
||||
static struct pci_driver shpc_driver = {
|
||||
.name = SHPC_MODULE_NAME,
|
||||
.id_table = shpcd_pci_tbl,
|
||||
.probe = shpc_probe,
|
||||
.remove = shpc_remove,
|
||||
};
|
||||
|
||||
static int __init shpcd_init(void)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
retval = pci_register_driver(&shpc_driver);
|
||||
dbg("%s: pci_register_driver = %d\n", __FUNCTION__, retval);
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit shpcd_cleanup(void)
|
||||
{
|
||||
dbg("unload_shpchpd()\n");
|
||||
pci_unregister_driver(&shpc_driver);
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
|
||||
}
|
||||
|
||||
module_init(shpcd_init);
|
||||
module_exit(shpcd_cleanup);
|
||||
720
drivers/pci/hotplug/shpchp_ctrl.c
Normal file
720
drivers/pci/hotplug/shpchp_ctrl.c
Normal file
@@ -0,0 +1,720 @@
|
||||
/*
|
||||
* Standard Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "../pci.h"
|
||||
#include "shpchp.h"
|
||||
|
||||
static void interrupt_event_handler(struct work_struct *work);
|
||||
static int shpchp_enable_slot(struct slot *p_slot);
|
||||
static int shpchp_disable_slot(struct slot *p_slot);
|
||||
|
||||
static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
|
||||
{
|
||||
struct event_info *info;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_ATOMIC);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->event_type = event_type;
|
||||
info->p_slot = p_slot;
|
||||
INIT_WORK(&info->work, interrupt_event_handler);
|
||||
|
||||
schedule_work(&info->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u32 event_type;
|
||||
|
||||
/* Attention Button Change */
|
||||
dbg("shpchp: Attention button interrupt received.\n");
|
||||
|
||||
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
|
||||
|
||||
/*
|
||||
* Button pressed - See if need to TAKE ACTION!!!
|
||||
*/
|
||||
info("Button pressed on Slot(%s)\n", p_slot->name);
|
||||
event_type = INT_BUTTON_PRESS;
|
||||
|
||||
queue_interrupt_event(p_slot, event_type);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u8 getstatus;
|
||||
u32 event_type;
|
||||
|
||||
/* Switch Change */
|
||||
dbg("shpchp: Switch interrupt received.\n");
|
||||
|
||||
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
|
||||
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
dbg("%s: Card present %x Power status %x\n", __FUNCTION__,
|
||||
p_slot->presence_save, p_slot->pwr_save);
|
||||
|
||||
if (getstatus) {
|
||||
/*
|
||||
* Switch opened
|
||||
*/
|
||||
info("Latch open on Slot(%s)\n", p_slot->name);
|
||||
event_type = INT_SWITCH_OPEN;
|
||||
if (p_slot->pwr_save && p_slot->presence_save) {
|
||||
event_type = INT_POWER_FAULT;
|
||||
err("Surprise Removal of card\n");
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Switch closed
|
||||
*/
|
||||
info("Latch close on Slot(%s)\n", p_slot->name);
|
||||
event_type = INT_SWITCH_CLOSE;
|
||||
}
|
||||
|
||||
queue_interrupt_event(p_slot, event_type);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u32 event_type;
|
||||
|
||||
/* Presence Change */
|
||||
dbg("shpchp: Presence/Notify input change.\n");
|
||||
|
||||
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
/*
|
||||
* Save the presence state
|
||||
*/
|
||||
p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
|
||||
if (p_slot->presence_save) {
|
||||
/*
|
||||
* Card Present
|
||||
*/
|
||||
info("Card present on Slot(%s)\n", p_slot->name);
|
||||
event_type = INT_PRESENCE_ON;
|
||||
} else {
|
||||
/*
|
||||
* Not Present
|
||||
*/
|
||||
info("Card not present on Slot(%s)\n", p_slot->name);
|
||||
event_type = INT_PRESENCE_OFF;
|
||||
}
|
||||
|
||||
queue_interrupt_event(p_slot, event_type);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u32 event_type;
|
||||
|
||||
/* Power fault */
|
||||
dbg("shpchp: Power fault interrupt received.\n");
|
||||
|
||||
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
|
||||
/*
|
||||
* Power fault Cleared
|
||||
*/
|
||||
info("Power fault cleared on Slot(%s)\n", p_slot->name);
|
||||
p_slot->status = 0x00;
|
||||
event_type = INT_POWER_FAULT_CLEAR;
|
||||
} else {
|
||||
/*
|
||||
* Power fault
|
||||
*/
|
||||
info("Power fault on Slot(%s)\n", p_slot->name);
|
||||
event_type = INT_POWER_FAULT;
|
||||
/* set power fault status for this board */
|
||||
p_slot->status = 0xFF;
|
||||
info("power fault bit %x set\n", hp_slot);
|
||||
}
|
||||
|
||||
queue_interrupt_event(p_slot, event_type);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The following routines constitute the bulk of the
|
||||
hotplug controller logic
|
||||
*/
|
||||
static int change_bus_speed(struct controller *ctrl, struct slot *p_slot,
|
||||
enum pci_bus_speed speed)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
dbg("%s: change to speed %d\n", __FUNCTION__, speed);
|
||||
if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) {
|
||||
err("%s: Issue of set bus speed mode command failed\n",
|
||||
__FUNCTION__);
|
||||
return WRONG_BUS_FREQUENCY;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fix_bus_speed(struct controller *ctrl, struct slot *pslot,
|
||||
u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp,
|
||||
enum pci_bus_speed msp)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
* If other slots on the same bus are occupied, we cannot
|
||||
* change the bus speed.
|
||||
*/
|
||||
if (flag) {
|
||||
if (asp < bsp) {
|
||||
err("%s: speed of bus %x and adapter %x mismatch\n",
|
||||
__FUNCTION__, bsp, asp);
|
||||
rc = WRONG_BUS_FREQUENCY;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (asp < msp) {
|
||||
if (bsp != asp)
|
||||
rc = change_bus_speed(ctrl, pslot, asp);
|
||||
} else {
|
||||
if (bsp != msp)
|
||||
rc = change_bus_speed(ctrl, pslot, msp);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* board_added - Called after a board has been added to the system.
|
||||
*
|
||||
* Turns power on for the board
|
||||
* Configures board
|
||||
*
|
||||
*/
|
||||
static int board_added(struct slot *p_slot)
|
||||
{
|
||||
u8 hp_slot;
|
||||
u8 slots_not_empty = 0;
|
||||
int rc = 0;
|
||||
enum pci_bus_speed asp, bsp, msp;
|
||||
struct controller *ctrl = p_slot->ctrl;
|
||||
|
||||
hp_slot = p_slot->device - ctrl->slot_device_offset;
|
||||
|
||||
dbg("%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n",
|
||||
__FUNCTION__, p_slot->device,
|
||||
ctrl->slot_device_offset, hp_slot);
|
||||
|
||||
/* Power on slot without connecting to bus */
|
||||
rc = p_slot->hpc_ops->power_on_slot(p_slot);
|
||||
if (rc) {
|
||||
err("%s: Failed to power on slot\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
|
||||
if (slots_not_empty)
|
||||
return WRONG_BUS_FREQUENCY;
|
||||
|
||||
if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) {
|
||||
err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
|
||||
return WRONG_BUS_FREQUENCY;
|
||||
}
|
||||
|
||||
/* turn on board, blink green LED, turn off Amber LED */
|
||||
if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
|
||||
err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp);
|
||||
if (rc) {
|
||||
err("%s: Can't get adapter speed or bus mode mismatch\n",
|
||||
__FUNCTION__);
|
||||
return WRONG_BUS_FREQUENCY;
|
||||
}
|
||||
|
||||
rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bsp);
|
||||
if (rc) {
|
||||
err("%s: Can't get bus operation speed\n", __FUNCTION__);
|
||||
return WRONG_BUS_FREQUENCY;
|
||||
}
|
||||
|
||||
rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &msp);
|
||||
if (rc) {
|
||||
err("%s: Can't get max bus operation speed\n", __FUNCTION__);
|
||||
msp = bsp;
|
||||
}
|
||||
|
||||
/* Check if there are other slots or devices on the same bus */
|
||||
if (!list_empty(&ctrl->pci_dev->subordinate->devices))
|
||||
slots_not_empty = 1;
|
||||
|
||||
dbg("%s: slots_not_empty %d, adapter_speed %d, bus_speed %d, "
|
||||
"max_bus_speed %d\n", __FUNCTION__, slots_not_empty, asp,
|
||||
bsp, msp);
|
||||
|
||||
rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* turn on board, blink green LED, turn off Amber LED */
|
||||
if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
|
||||
err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Wait for ~1 second */
|
||||
msleep(1000);
|
||||
|
||||
dbg("%s: slot status = %x\n", __FUNCTION__, p_slot->status);
|
||||
/* Check for a power fault */
|
||||
if (p_slot->status == 0xFF) {
|
||||
/* power fault occurred, but it was benign */
|
||||
dbg("%s: power fault\n", __FUNCTION__);
|
||||
rc = POWER_FAILURE;
|
||||
p_slot->status = 0;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
if (shpchp_configure_device(p_slot)) {
|
||||
err("Cannot add device at 0x%x:0x%x\n", p_slot->bus,
|
||||
p_slot->device);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
p_slot->status = 0;
|
||||
p_slot->is_a_board = 0x01;
|
||||
p_slot->pwr_save = 1;
|
||||
|
||||
p_slot->hpc_ops->green_led_on(p_slot);
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
/* turn off slot, turn on Amber LED, turn off Green LED */
|
||||
rc = p_slot->hpc_ops->slot_disable(p_slot);
|
||||
if (rc) {
|
||||
err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return(rc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* remove_board - Turns off slot and LED's
|
||||
*
|
||||
*/
|
||||
static int remove_board(struct slot *p_slot)
|
||||
{
|
||||
struct controller *ctrl = p_slot->ctrl;
|
||||
u8 hp_slot;
|
||||
int rc;
|
||||
|
||||
if (shpchp_unconfigure_device(p_slot))
|
||||
return(1);
|
||||
|
||||
hp_slot = p_slot->device - ctrl->slot_device_offset;
|
||||
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
|
||||
|
||||
/* Change status to shutdown */
|
||||
if (p_slot->is_a_board)
|
||||
p_slot->status = 0x01;
|
||||
|
||||
/* turn off slot, turn on Amber LED, turn off Green LED */
|
||||
rc = p_slot->hpc_ops->slot_disable(p_slot);
|
||||
if (rc) {
|
||||
err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = p_slot->hpc_ops->set_attention_status(p_slot, 0);
|
||||
if (rc) {
|
||||
err("%s: Issue of Set Attention command failed\n", __FUNCTION__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
p_slot->pwr_save = 0;
|
||||
p_slot->is_a_board = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct pushbutton_work_info {
|
||||
struct slot *p_slot;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
/**
|
||||
* shpchp_pushbutton_thread
|
||||
*
|
||||
* Scheduled procedure to handle blocking stuff for the pushbuttons
|
||||
* Handles all pending events and exits.
|
||||
*
|
||||
*/
|
||||
static void shpchp_pushbutton_thread(struct work_struct *work)
|
||||
{
|
||||
struct pushbutton_work_info *info =
|
||||
container_of(work, struct pushbutton_work_info, work);
|
||||
struct slot *p_slot = info->p_slot;
|
||||
|
||||
mutex_lock(&p_slot->lock);
|
||||
switch (p_slot->state) {
|
||||
case POWEROFF_STATE:
|
||||
mutex_unlock(&p_slot->lock);
|
||||
shpchp_disable_slot(p_slot);
|
||||
mutex_lock(&p_slot->lock);
|
||||
p_slot->state = STATIC_STATE;
|
||||
break;
|
||||
case POWERON_STATE:
|
||||
mutex_unlock(&p_slot->lock);
|
||||
if (shpchp_enable_slot(p_slot))
|
||||
p_slot->hpc_ops->green_led_off(p_slot);
|
||||
mutex_lock(&p_slot->lock);
|
||||
p_slot->state = STATIC_STATE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&p_slot->lock);
|
||||
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
void queue_pushbutton_work(struct work_struct *work)
|
||||
{
|
||||
struct slot *p_slot = container_of(work, struct slot, work.work);
|
||||
struct pushbutton_work_info *info;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
err("%s: Cannot allocate memory\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
info->p_slot = p_slot;
|
||||
INIT_WORK(&info->work, shpchp_pushbutton_thread);
|
||||
|
||||
mutex_lock(&p_slot->lock);
|
||||
switch (p_slot->state) {
|
||||
case BLINKINGOFF_STATE:
|
||||
p_slot->state = POWEROFF_STATE;
|
||||
break;
|
||||
case BLINKINGON_STATE:
|
||||
p_slot->state = POWERON_STATE;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
queue_work(shpchp_wq, &info->work);
|
||||
out:
|
||||
mutex_unlock(&p_slot->lock);
|
||||
}
|
||||
|
||||
static int update_slot_info (struct slot *slot)
|
||||
{
|
||||
struct hotplug_slot_info *info;
|
||||
int result;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
slot->hpc_ops->get_power_status(slot, &(info->power_status));
|
||||
slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
|
||||
slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
|
||||
slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
|
||||
|
||||
result = pci_hp_change_slot_info(slot->hotplug_slot, info);
|
||||
kfree (info);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: This function must be called with slot->lock held
|
||||
*/
|
||||
static void handle_button_press_event(struct slot *p_slot)
|
||||
{
|
||||
u8 getstatus;
|
||||
|
||||
switch (p_slot->state) {
|
||||
case STATIC_STATE:
|
||||
p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
|
||||
if (getstatus) {
|
||||
p_slot->state = BLINKINGOFF_STATE;
|
||||
info("PCI slot #%s - powering off due to button "
|
||||
"press.\n", p_slot->name);
|
||||
} else {
|
||||
p_slot->state = BLINKINGON_STATE;
|
||||
info("PCI slot #%s - powering on due to button "
|
||||
"press.\n", p_slot->name);
|
||||
}
|
||||
/* blink green LED and turn off amber */
|
||||
p_slot->hpc_ops->green_led_blink(p_slot);
|
||||
p_slot->hpc_ops->set_attention_status(p_slot, 0);
|
||||
|
||||
schedule_delayed_work(&p_slot->work, 5*HZ);
|
||||
break;
|
||||
case BLINKINGOFF_STATE:
|
||||
case BLINKINGON_STATE:
|
||||
/*
|
||||
* Cancel if we are still blinking; this means that we
|
||||
* press the attention again before the 5 sec. limit
|
||||
* expires to cancel hot-add or hot-remove
|
||||
*/
|
||||
info("Button cancel on Slot(%s)\n", p_slot->name);
|
||||
dbg("%s: button cancel\n", __FUNCTION__);
|
||||
cancel_delayed_work(&p_slot->work);
|
||||
if (p_slot->state == BLINKINGOFF_STATE)
|
||||
p_slot->hpc_ops->green_led_on(p_slot);
|
||||
else
|
||||
p_slot->hpc_ops->green_led_off(p_slot);
|
||||
p_slot->hpc_ops->set_attention_status(p_slot, 0);
|
||||
info("PCI slot #%s - action canceled due to button press\n",
|
||||
p_slot->name);
|
||||
p_slot->state = STATIC_STATE;
|
||||
break;
|
||||
case POWEROFF_STATE:
|
||||
case POWERON_STATE:
|
||||
/*
|
||||
* Ignore if the slot is on power-on or power-off state;
|
||||
* this means that the previous attention button action
|
||||
* to hot-add or hot-remove is undergoing
|
||||
*/
|
||||
info("Button ignore on Slot(%s)\n", p_slot->name);
|
||||
update_slot_info(p_slot);
|
||||
break;
|
||||
default:
|
||||
warn("Not a valid state\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void interrupt_event_handler(struct work_struct *work)
|
||||
{
|
||||
struct event_info *info = container_of(work, struct event_info, work);
|
||||
struct slot *p_slot = info->p_slot;
|
||||
|
||||
mutex_lock(&p_slot->lock);
|
||||
switch (info->event_type) {
|
||||
case INT_BUTTON_PRESS:
|
||||
handle_button_press_event(p_slot);
|
||||
break;
|
||||
case INT_POWER_FAULT:
|
||||
dbg("%s: power fault\n", __FUNCTION__);
|
||||
p_slot->hpc_ops->set_attention_status(p_slot, 1);
|
||||
p_slot->hpc_ops->green_led_off(p_slot);
|
||||
break;
|
||||
default:
|
||||
update_slot_info(p_slot);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&p_slot->lock);
|
||||
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
|
||||
static int shpchp_enable_slot (struct slot *p_slot)
|
||||
{
|
||||
u8 getstatus = 0;
|
||||
int rc, retval = -ENODEV;
|
||||
|
||||
/* Check to see if (latch closed, card present, power off) */
|
||||
mutex_lock(&p_slot->ctrl->crit_sect);
|
||||
rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
|
||||
if (rc || !getstatus) {
|
||||
info("No adapter on slot(%s)\n", p_slot->name);
|
||||
goto out;
|
||||
}
|
||||
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
if (rc || getstatus) {
|
||||
info("Latch open on slot(%s)\n", p_slot->name);
|
||||
goto out;
|
||||
}
|
||||
rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
|
||||
if (rc || getstatus) {
|
||||
info("Already enabled on slot(%s)\n", p_slot->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
p_slot->is_a_board = 1;
|
||||
|
||||
/* We have to save the presence info for these slots */
|
||||
p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
|
||||
p_slot->hpc_ops->get_power_status(p_slot, &(p_slot->pwr_save));
|
||||
dbg("%s: p_slot->pwr_save %x\n", __FUNCTION__, p_slot->pwr_save);
|
||||
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
|
||||
if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) ||
|
||||
(p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458))
|
||||
&& p_slot->ctrl->num_slots == 1) {
|
||||
/* handle amd pogo errata; this must be done before enable */
|
||||
amd_pogo_errata_save_misc_reg(p_slot);
|
||||
retval = board_added(p_slot);
|
||||
/* handle amd pogo errata; this must be done after enable */
|
||||
amd_pogo_errata_restore_misc_reg(p_slot);
|
||||
} else
|
||||
retval = board_added(p_slot);
|
||||
|
||||
if (retval) {
|
||||
p_slot->hpc_ops->get_adapter_status(p_slot,
|
||||
&(p_slot->presence_save));
|
||||
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
}
|
||||
|
||||
update_slot_info(p_slot);
|
||||
out:
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int shpchp_disable_slot (struct slot *p_slot)
|
||||
{
|
||||
u8 getstatus = 0;
|
||||
int rc, retval = -ENODEV;
|
||||
|
||||
if (!p_slot->ctrl)
|
||||
return -ENODEV;
|
||||
|
||||
/* Check to see if (latch closed, card present, power on) */
|
||||
mutex_lock(&p_slot->ctrl->crit_sect);
|
||||
|
||||
rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
|
||||
if (rc || !getstatus) {
|
||||
info("No adapter on slot(%s)\n", p_slot->name);
|
||||
goto out;
|
||||
}
|
||||
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
if (rc || getstatus) {
|
||||
info("Latch open on slot(%s)\n", p_slot->name);
|
||||
goto out;
|
||||
}
|
||||
rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
|
||||
if (rc || !getstatus) {
|
||||
info("Already disabled slot(%s)\n", p_slot->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = remove_board(p_slot);
|
||||
update_slot_info(p_slot);
|
||||
out:
|
||||
mutex_unlock(&p_slot->ctrl->crit_sect);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int shpchp_sysfs_enable_slot(struct slot *p_slot)
|
||||
{
|
||||
int retval = -ENODEV;
|
||||
|
||||
mutex_lock(&p_slot->lock);
|
||||
switch (p_slot->state) {
|
||||
case BLINKINGON_STATE:
|
||||
cancel_delayed_work(&p_slot->work);
|
||||
case STATIC_STATE:
|
||||
p_slot->state = POWERON_STATE;
|
||||
mutex_unlock(&p_slot->lock);
|
||||
retval = shpchp_enable_slot(p_slot);
|
||||
mutex_lock(&p_slot->lock);
|
||||
p_slot->state = STATIC_STATE;
|
||||
break;
|
||||
case POWERON_STATE:
|
||||
info("Slot %s is already in powering on state\n",
|
||||
p_slot->name);
|
||||
break;
|
||||
case BLINKINGOFF_STATE:
|
||||
case POWEROFF_STATE:
|
||||
info("Already enabled on slot %s\n", p_slot->name);
|
||||
break;
|
||||
default:
|
||||
err("Not a valid state on slot %s\n", p_slot->name);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&p_slot->lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int shpchp_sysfs_disable_slot(struct slot *p_slot)
|
||||
{
|
||||
int retval = -ENODEV;
|
||||
|
||||
mutex_lock(&p_slot->lock);
|
||||
switch (p_slot->state) {
|
||||
case BLINKINGOFF_STATE:
|
||||
cancel_delayed_work(&p_slot->work);
|
||||
case STATIC_STATE:
|
||||
p_slot->state = POWEROFF_STATE;
|
||||
mutex_unlock(&p_slot->lock);
|
||||
retval = shpchp_disable_slot(p_slot);
|
||||
mutex_lock(&p_slot->lock);
|
||||
p_slot->state = STATIC_STATE;
|
||||
break;
|
||||
case POWEROFF_STATE:
|
||||
info("Slot %s is already in powering off state\n",
|
||||
p_slot->name);
|
||||
break;
|
||||
case BLINKINGON_STATE:
|
||||
case POWERON_STATE:
|
||||
info("Already disabled on slot %s\n", p_slot->name);
|
||||
break;
|
||||
default:
|
||||
err("Not a valid state on slot %s\n", p_slot->name);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&p_slot->lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
1130
drivers/pci/hotplug/shpchp_hpc.c
Normal file
1130
drivers/pci/hotplug/shpchp_hpc.c
Normal file
File diff suppressed because it is too large
Load Diff
199
drivers/pci/hotplug/shpchp_pci.c
Normal file
199
drivers/pci/hotplug/shpchp_pci.c
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Standard Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include "../pci.h"
|
||||
#include "shpchp.h"
|
||||
|
||||
static void program_fw_provided_values(struct pci_dev *dev)
|
||||
{
|
||||
u16 pci_cmd, pci_bctl;
|
||||
struct pci_dev *cdev;
|
||||
struct hotplug_params hpp;
|
||||
|
||||
/* Program hpp values for this device */
|
||||
if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL ||
|
||||
(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
|
||||
(dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
|
||||
return;
|
||||
|
||||
/* use default values if we can't get them from firmware */
|
||||
if (get_hp_params_from_firmware(dev, &hpp) ||
|
||||
!hpp.t0 || (hpp.t0->revision > 1)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Could not get hotplug parameters. Use defaults\n",
|
||||
__FUNCTION__);
|
||||
hpp.t0 = &hpp.type0_data;
|
||||
hpp.t0->revision = 0;
|
||||
hpp.t0->cache_line_size = 8;
|
||||
hpp.t0->latency_timer = 0x40;
|
||||
hpp.t0->enable_serr = 0;
|
||||
hpp.t0->enable_perr = 0;
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev,
|
||||
PCI_CACHE_LINE_SIZE, hpp.t0->cache_line_size);
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp.t0->latency_timer);
|
||||
pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
|
||||
if (hpp.t0->enable_serr)
|
||||
pci_cmd |= PCI_COMMAND_SERR;
|
||||
else
|
||||
pci_cmd &= ~PCI_COMMAND_SERR;
|
||||
if (hpp.t0->enable_perr)
|
||||
pci_cmd |= PCI_COMMAND_PARITY;
|
||||
else
|
||||
pci_cmd &= ~PCI_COMMAND_PARITY;
|
||||
pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
|
||||
|
||||
/* Program bridge control value and child devices */
|
||||
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
|
||||
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
|
||||
hpp.t0->latency_timer);
|
||||
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
|
||||
if (hpp.t0->enable_serr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_SERR;
|
||||
else
|
||||
pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
|
||||
if (hpp.t0->enable_perr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_PARITY;
|
||||
else
|
||||
pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
|
||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
|
||||
if (dev->subordinate) {
|
||||
list_for_each_entry(cdev, &dev->subordinate->devices,
|
||||
bus_list)
|
||||
program_fw_provided_values(cdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int shpchp_configure_device(struct slot *p_slot)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate;
|
||||
int num, fn;
|
||||
|
||||
dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0));
|
||||
if (dev) {
|
||||
err("Device %s already exists at %x:%x, cannot hot-add\n",
|
||||
pci_name(dev), p_slot->bus, p_slot->device);
|
||||
pci_dev_put(dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0));
|
||||
if (num == 0) {
|
||||
err("No new device found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (fn = 0; fn < 8; fn++) {
|
||||
dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, fn));
|
||||
if (!dev)
|
||||
continue;
|
||||
if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
|
||||
err("Cannot hot-add display device %s\n",
|
||||
pci_name(dev));
|
||||
pci_dev_put(dev);
|
||||
continue;
|
||||
}
|
||||
if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) ||
|
||||
(dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) {
|
||||
/* Find an unused bus number for the new bridge */
|
||||
struct pci_bus *child;
|
||||
unsigned char busnr, start = parent->secondary;
|
||||
unsigned char end = parent->subordinate;
|
||||
for (busnr = start; busnr <= end; busnr++) {
|
||||
if (!pci_find_bus(pci_domain_nr(parent),
|
||||
busnr))
|
||||
break;
|
||||
}
|
||||
if (busnr >= end) {
|
||||
err("No free bus for hot-added bridge\n");
|
||||
pci_dev_put(dev);
|
||||
continue;
|
||||
}
|
||||
child = pci_add_new_bus(parent, dev, busnr);
|
||||
if (!child) {
|
||||
err("Cannot add new bus for %s\n",
|
||||
pci_name(dev));
|
||||
pci_dev_put(dev);
|
||||
continue;
|
||||
}
|
||||
child->subordinate = pci_do_scan_bus(child);
|
||||
pci_bus_size_bridges(child);
|
||||
}
|
||||
program_fw_provided_values(dev);
|
||||
pci_dev_put(dev);
|
||||
}
|
||||
|
||||
pci_bus_assign_resources(parent);
|
||||
pci_bus_add_devices(parent);
|
||||
pci_enable_bridges(parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int shpchp_unconfigure_device(struct slot *p_slot)
|
||||
{
|
||||
int rc = 0;
|
||||
int j;
|
||||
u8 bctl = 0;
|
||||
struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate;
|
||||
|
||||
dbg("%s: bus/dev = %x/%x\n", __FUNCTION__, p_slot->bus, p_slot->device);
|
||||
|
||||
for (j=0; j<8 ; j++) {
|
||||
struct pci_dev* temp = pci_get_slot(parent,
|
||||
(p_slot->device << 3) | j);
|
||||
if (!temp)
|
||||
continue;
|
||||
if ((temp->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
|
||||
err("Cannot remove display device %s\n",
|
||||
pci_name(temp));
|
||||
pci_dev_put(temp);
|
||||
continue;
|
||||
}
|
||||
if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
||||
pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl);
|
||||
if (bctl & PCI_BRIDGE_CTL_VGA) {
|
||||
err("Cannot remove display device %s\n",
|
||||
pci_name(temp));
|
||||
pci_dev_put(temp);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pci_remove_bus_device(temp);
|
||||
pci_dev_put(temp);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
102
drivers/pci/hotplug/shpchp_sysfs.c
Normal file
102
drivers/pci/hotplug/shpchp_sysfs.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Compaq Hot Plug Controller Driver
|
||||
*
|
||||
* Copyright (c) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (c) 2001 IBM Corp.
|
||||
*
|
||||
* 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 <greg@kroah.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include "shpchp.h"
|
||||
|
||||
|
||||
/* A few routines that create sysfs entries for the hot plug controller */
|
||||
|
||||
static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
char * out = buf;
|
||||
int index, busnr;
|
||||
struct resource *res;
|
||||
struct pci_bus *bus;
|
||||
|
||||
pdev = container_of (dev, struct pci_dev, dev);
|
||||
bus = pdev->subordinate;
|
||||
|
||||
out += sprintf(buf, "Free resources: memory\n");
|
||||
for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
|
||||
res = bus->resource[index];
|
||||
if (res && (res->flags & IORESOURCE_MEM) &&
|
||||
!(res->flags & IORESOURCE_PREFETCH)) {
|
||||
out += sprintf(out, "start = %8.8llx, "
|
||||
"length = %8.8llx\n",
|
||||
(unsigned long long)res->start,
|
||||
(unsigned long long)(res->end - res->start));
|
||||
}
|
||||
}
|
||||
out += sprintf(out, "Free resources: prefetchable memory\n");
|
||||
for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
|
||||
res = bus->resource[index];
|
||||
if (res && (res->flags & IORESOURCE_MEM) &&
|
||||
(res->flags & IORESOURCE_PREFETCH)) {
|
||||
out += sprintf(out, "start = %8.8llx, "
|
||||
"length = %8.8llx\n",
|
||||
(unsigned long long)res->start,
|
||||
(unsigned long long)(res->end - res->start));
|
||||
}
|
||||
}
|
||||
out += sprintf(out, "Free resources: IO\n");
|
||||
for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
|
||||
res = bus->resource[index];
|
||||
if (res && (res->flags & IORESOURCE_IO)) {
|
||||
out += sprintf(out, "start = %8.8llx, "
|
||||
"length = %8.8llx\n",
|
||||
(unsigned long long)res->start,
|
||||
(unsigned long long)(res->end - res->start));
|
||||
}
|
||||
}
|
||||
out += sprintf(out, "Free resources: bus numbers\n");
|
||||
for (busnr = bus->secondary; busnr <= bus->subordinate; busnr++) {
|
||||
if (!pci_find_bus(pci_domain_nr(bus), busnr))
|
||||
break;
|
||||
}
|
||||
if (busnr < bus->subordinate)
|
||||
out += sprintf(out, "start = %8.8x, length = %8.8x\n",
|
||||
busnr, (bus->subordinate - busnr));
|
||||
|
||||
return out - buf;
|
||||
}
|
||||
static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
|
||||
|
||||
int __must_check shpchp_create_ctrl_files (struct controller *ctrl)
|
||||
{
|
||||
return device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
|
||||
}
|
||||
|
||||
void shpchp_remove_ctrl_files(struct controller *ctrl)
|
||||
{
|
||||
device_remove_file(&ctrl->pci_dev->dev, &dev_attr_ctrl);
|
||||
}
|
||||
Reference in New Issue
Block a user