in hotplug/cpqphp_core.c [754:1244]
static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
u8 num_of_slots = 0;
u8 hp_slot = 0;
u8 device;
u8 bus_cap;
u16 temp_word;
u16 vendor_id;
u16 subsystem_vid;
u16 subsystem_deviceid;
u32 rc;
struct controller *ctrl;
struct pci_func *func;
struct pci_bus *bus;
int err;
err = pci_enable_device(pdev);
if (err) {
printk(KERN_ERR MY_NAME ": cannot enable PCI device %s (%d)\n",
pci_name(pdev), err);
return err;
}
bus = pdev->subordinate;
if (!bus) {
pci_notice(pdev, "the device is not a bridge, skipping\n");
rc = -ENODEV;
goto err_disable_device;
}
/* Need to read VID early b/c it's used to differentiate CPQ and INTC
* discovery
*/
vendor_id = pdev->vendor;
if ((vendor_id != PCI_VENDOR_ID_COMPAQ) &&
(vendor_id != PCI_VENDOR_ID_INTEL)) {
err(msg_HPC_non_compaq_or_intel);
rc = -ENODEV;
goto err_disable_device;
}
dbg("Vendor ID: %x\n", vendor_id);
dbg("revision: %d\n", pdev->revision);
if ((vendor_id == PCI_VENDOR_ID_COMPAQ) && (!pdev->revision)) {
err(msg_HPC_rev_error);
rc = -ENODEV;
goto err_disable_device;
}
/* Check for the proper subsystem IDs
* Intel uses a different SSID programming model than Compaq.
* For Intel, each SSID bit identifies a PHP capability.
* Also Intel HPCs may have RID=0.
*/
if ((pdev->revision <= 2) && (vendor_id != PCI_VENDOR_ID_INTEL)) {
err(msg_HPC_not_supported);
rc = -ENODEV;
goto err_disable_device;
}
/* TODO: This code can be made to support non-Compaq or Intel
* subsystem IDs
*/
subsystem_vid = pdev->subsystem_vendor;
dbg("Subsystem Vendor ID: %x\n", subsystem_vid);
if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) {
err(msg_HPC_non_compaq_or_intel);
rc = -ENODEV;
goto err_disable_device;
}
ctrl = kzalloc(sizeof(struct controller), GFP_KERNEL);
if (!ctrl) {
rc = -ENOMEM;
goto err_disable_device;
}
subsystem_deviceid = pdev->subsystem_device;
info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid);
/* Set Vendor ID, so it can be accessed later from other
* functions
*/
ctrl->vendor_id = vendor_id;
switch (subsystem_vid) {
case PCI_VENDOR_ID_COMPAQ:
if (pdev->revision >= 0x13) { /* CIOBX */
ctrl->push_flag = 1;
ctrl->slot_switch_type = 1;
ctrl->push_button = 1;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
ctrl->pcix_support = 1;
ctrl->pcix_speed_capability = 1;
pci_read_config_byte(pdev, 0x41, &bus_cap);
if (bus_cap & 0x80) {
dbg("bus max supports 133MHz PCI-X\n");
bus->max_bus_speed = PCI_SPEED_133MHz_PCIX;
break;
}
if (bus_cap & 0x40) {
dbg("bus max supports 100MHz PCI-X\n");
bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
break;
}
if (bus_cap & 0x20) {
dbg("bus max supports 66MHz PCI-X\n");
bus->max_bus_speed = PCI_SPEED_66MHz_PCIX;
break;
}
if (bus_cap & 0x10) {
dbg("bus max supports 66MHz PCI\n");
bus->max_bus_speed = PCI_SPEED_66MHz;
break;
}
break;
}
switch (subsystem_deviceid) {
case PCI_SUB_HPC_ID:
/* Original 6500/7000 implementation */
ctrl->slot_switch_type = 1;
bus->max_bus_speed = PCI_SPEED_33MHz;
ctrl->push_button = 0;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
ctrl->pcix_support = 0;
ctrl->pcix_speed_capability = 0;
break;
case PCI_SUB_HPC_ID2:
/* First Pushbutton implementation */
ctrl->push_flag = 1;
ctrl->slot_switch_type = 1;
bus->max_bus_speed = PCI_SPEED_33MHz;
ctrl->push_button = 1;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
ctrl->pcix_support = 0;
ctrl->pcix_speed_capability = 0;
break;
case PCI_SUB_HPC_ID_INTC:
/* Third party (6500/7000) */
ctrl->slot_switch_type = 1;
bus->max_bus_speed = PCI_SPEED_33MHz;
ctrl->push_button = 0;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
ctrl->pcix_support = 0;
ctrl->pcix_speed_capability = 0;
break;
case PCI_SUB_HPC_ID3:
/* First 66 Mhz implementation */
ctrl->push_flag = 1;
ctrl->slot_switch_type = 1;
bus->max_bus_speed = PCI_SPEED_66MHz;
ctrl->push_button = 1;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
ctrl->pcix_support = 0;
ctrl->pcix_speed_capability = 0;
break;
case PCI_SUB_HPC_ID4:
/* First PCI-X implementation, 100MHz */
ctrl->push_flag = 1;
ctrl->slot_switch_type = 1;
bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
ctrl->push_button = 1;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
ctrl->pcix_support = 1;
ctrl->pcix_speed_capability = 0;
break;
default:
err(msg_HPC_not_supported);
rc = -ENODEV;
goto err_free_ctrl;
}
break;
case PCI_VENDOR_ID_INTEL:
/* Check for speed capability (0=33, 1=66) */
if (subsystem_deviceid & 0x0001)
bus->max_bus_speed = PCI_SPEED_66MHz;
else
bus->max_bus_speed = PCI_SPEED_33MHz;
/* Check for push button */
if (subsystem_deviceid & 0x0002)
ctrl->push_button = 0;
else
ctrl->push_button = 1;
/* Check for slot switch type (0=mechanical, 1=not mechanical) */
if (subsystem_deviceid & 0x0004)
ctrl->slot_switch_type = 0;
else
ctrl->slot_switch_type = 1;
/* PHP Status (0=De-feature PHP, 1=Normal operation) */
if (subsystem_deviceid & 0x0008)
ctrl->defeature_PHP = 1; /* PHP supported */
else
ctrl->defeature_PHP = 0; /* PHP not supported */
/* Alternate Base Address Register Interface
* (0=not supported, 1=supported)
*/
if (subsystem_deviceid & 0x0010)
ctrl->alternate_base_address = 1;
else
ctrl->alternate_base_address = 0;
/* PCI Config Space Index (0=not supported, 1=supported) */
if (subsystem_deviceid & 0x0020)
ctrl->pci_config_space = 1;
else
ctrl->pci_config_space = 0;
/* PCI-X support */
if (subsystem_deviceid & 0x0080) {
ctrl->pcix_support = 1;
if (subsystem_deviceid & 0x0040)
/* 133MHz PCI-X if bit 7 is 1 */
ctrl->pcix_speed_capability = 1;
else
/* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */
/* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */
ctrl->pcix_speed_capability = 0;
} else {
/* Conventional PCI */
ctrl->pcix_support = 0;
ctrl->pcix_speed_capability = 0;
}
break;
default:
err(msg_HPC_not_supported);
rc = -ENODEV;
goto err_free_ctrl;
}
/* Tell the user that we found one. */
info("Initializing the PCI hot plug controller residing on PCI bus %d\n",
pdev->bus->number);
dbg("Hotplug controller capabilities:\n");
dbg(" speed_capability %d\n", bus->max_bus_speed);
dbg(" slot_switch_type %s\n", ctrl->slot_switch_type ?
"switch present" : "no switch");
dbg(" defeature_PHP %s\n", ctrl->defeature_PHP ?
"PHP supported" : "PHP not supported");
dbg(" alternate_base_address %s\n", ctrl->alternate_base_address ?
"supported" : "not supported");
dbg(" pci_config_space %s\n", ctrl->pci_config_space ?
"supported" : "not supported");
dbg(" pcix_speed_capability %s\n", ctrl->pcix_speed_capability ?
"supported" : "not supported");
dbg(" pcix_support %s\n", ctrl->pcix_support ?
"supported" : "not supported");
ctrl->pci_dev = pdev;
pci_set_drvdata(pdev, ctrl);
/* make our own copy of the pci bus structure,
* as we like tweaking it a lot */
ctrl->pci_bus = kmemdup(pdev->bus, sizeof(*ctrl->pci_bus), GFP_KERNEL);
if (!ctrl->pci_bus) {
err("out of memory\n");
rc = -ENOMEM;
goto err_free_ctrl;
}
ctrl->bus = pdev->bus->number;
ctrl->rev = pdev->revision;
dbg("bus device function rev: %d %d %d %d\n", ctrl->bus,
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), ctrl->rev);
mutex_init(&ctrl->crit_sect);
init_waitqueue_head(&ctrl->queue);
/* initialize our threads if they haven't already been started up */
rc = one_time_init();
if (rc)
goto err_free_bus;
dbg("pdev = %p\n", pdev);
dbg("pci resource start %llx\n", (unsigned long long)pci_resource_start(pdev, 0));
dbg("pci resource len %llx\n", (unsigned long long)pci_resource_len(pdev, 0));
if (!request_mem_region(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0), MY_NAME)) {
err("cannot reserve MMIO region\n");
rc = -ENOMEM;
goto err_free_bus;
}
ctrl->hpc_reg = ioremap(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
if (!ctrl->hpc_reg) {
err("cannot remap MMIO region %llx @ %llx\n",
(unsigned long long)pci_resource_len(pdev, 0),
(unsigned long long)pci_resource_start(pdev, 0));
rc = -ENODEV;
goto err_free_mem_region;
}
/* Check for 66Mhz operation */
bus->cur_bus_speed = get_controller_speed(ctrl);
/********************************************************
*
* Save configuration headers for this and
* subordinate PCI buses
*
********************************************************/
/* find the physical slot number of the first hot plug slot */
/* Get slot won't work for devices behind bridges, but
* in this case it will always be called for the "base"
* bus/dev/func of a slot.
* CS: this is leveraging the PCIIRQ routing code from the kernel
* (pci-pc.c: get_irq_routing_table) */
rc = get_slot_mapping(ctrl->pci_bus, pdev->bus->number,
(readb(ctrl->hpc_reg + SLOT_MASK) >> 4),
&(ctrl->first_slot));
dbg("get_slot_mapping: first_slot = %d, returned = %d\n",
ctrl->first_slot, rc);
if (rc) {
err(msg_initialization_err, rc);
goto err_iounmap;
}
/* Store PCI Config Space for all devices on this bus */
rc = cpqhp_save_config(ctrl, ctrl->bus, readb(ctrl->hpc_reg + SLOT_MASK));
if (rc) {
err("%s: unable to save PCI configuration data, error %d\n",
__func__, rc);
goto err_iounmap;
}
/*
* Get IO, memory, and IRQ resources for new devices
*/
/* The next line is required for cpqhp_find_available_resources */
ctrl->interrupt = pdev->irq;
if (ctrl->interrupt < 0x10) {
cpqhp_legacy_mode = 1;
dbg("System seems to be configured for Full Table Mapped MPS mode\n");
}
ctrl->cfgspc_irq = 0;
pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &ctrl->cfgspc_irq);
rc = cpqhp_find_available_resources(ctrl, cpqhp_rom_start);
ctrl->add_support = !rc;
if (rc) {
dbg("cpqhp_find_available_resources = 0x%x\n", rc);
err("unable to locate PCI configuration resources for hot plug add.\n");
goto err_iounmap;
}
/*
* Finish setting up the hot plug ctrl device
*/
ctrl->slot_device_offset = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
dbg("NumSlots %d\n", ctrl->slot_device_offset);
ctrl->next_event = 0;
/* Setup the slot information structures */
rc = ctrl_slot_setup(ctrl, smbios_start, smbios_table);
if (rc) {
err(msg_initialization_err, 6);
err("%s: unable to save PCI configuration data, error %d\n",
__func__, rc);
goto err_iounmap;
}
/* Mask all general input interrupts */
writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK);
/* set up the interrupt */
dbg("HPC interrupt = %d\n", ctrl->interrupt);
if (request_irq(ctrl->interrupt, cpqhp_ctrl_intr,
IRQF_SHARED, MY_NAME, ctrl)) {
err("Can't get irq %d for the hotplug pci controller\n",
ctrl->interrupt);
rc = -ENODEV;
goto err_iounmap;
}
/* Enable Shift Out interrupt and clear it, also enable SERR on power
* fault
*/
temp_word = readw(ctrl->hpc_reg + MISC);
temp_word |= 0x4006;
writew(temp_word, ctrl->hpc_reg + MISC);
/* Changed 05/05/97 to clear all interrupts at start */
writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_INPUT_CLEAR);
ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
writel(0x0L, ctrl->hpc_reg + INT_MASK);
if (!cpqhp_ctrl_list) {
cpqhp_ctrl_list = ctrl;
ctrl->next = NULL;
} else {
ctrl->next = cpqhp_ctrl_list;
cpqhp_ctrl_list = ctrl;
}
/* turn off empty slots here unless command line option "ON" set
* Wait for exclusive access to hardware
*/
mutex_lock(&ctrl->crit_sect);
num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
/* find first device number for the ctrl */
device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
while (num_of_slots) {
dbg("num_of_slots: %d\n", num_of_slots);
func = cpqhp_slot_find(ctrl->bus, device, 0);
if (!func)
break;
hp_slot = func->device - ctrl->slot_device_offset;
dbg("hp_slot: %d\n", hp_slot);
/* We have to save the presence info for these slots */
temp_word = ctrl->ctrl_int_comp >> 16;
func->presence_save = (temp_word >> hp_slot) & 0x01;
func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
if (ctrl->ctrl_int_comp & (0x1L << hp_slot))
func->switch_save = 0;
else
func->switch_save = 0x10;
if (!power_mode)
if (!func->is_a_board) {
green_LED_off(ctrl, hp_slot);
slot_disable(ctrl, hp_slot);
}
device++;
num_of_slots--;
}
if (!power_mode) {
set_SOGO(ctrl);
/* Wait for SOBS to be unset */
wait_for_ctrl_irq(ctrl);
}
rc = init_SERR(ctrl);
if (rc) {
err("init_SERR failed\n");
mutex_unlock(&ctrl->crit_sect);
goto err_free_irq;
}
/* Done with exclusive hardware access */
mutex_unlock(&ctrl->crit_sect);
cpqhp_create_debugfs_files(ctrl);
return 0;
err_free_irq:
free_irq(ctrl->interrupt, ctrl);
err_iounmap:
iounmap(ctrl->hpc_reg);
err_free_mem_region:
release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
err_free_bus:
kfree(ctrl->pci_bus);
err_free_ctrl:
kfree(ctrl);
err_disable_device:
pci_disable_device(pdev);
return rc;
}