static int cpqhpc_probe()

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