pciehpc.c revision 269473047d747f7815af570197e4ef7322d3632c
* Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * This file contains Standard PCI Express HotPlug functionality that is * compatible with the PCI Express ver 1.1 specification. * NOTE: This file is compiled and delivered through misc/pcie module. /* Local functions prototype */ * Initialize Hot Plug Controller if present. The arguments are: * dip - Devinfo node pointer to the hot plug bus node * regops - register ops to access HPC registers for non-standard * HPC hw implementations (e.g: HPC in host PCI-E brdiges) * This is NULL for standard HPC in PCIe bridges. * DDI_SUCCESS for successful HPC initialization * DDI_FAILURE for errors or if HPC hw not found PCIE_DBG(
"pciehpc_init() called (dip=%p)\n", (
void *)
dip);
/* Make sure that it is not already initialized */ PCIE_DBG(
"%s%d: pciehpc instance already initialized!\n",
/* Allocate a new hotplug controller and slot structures */ /* setup access handle for HPC regs */ /* HPC access is non-standard; use the supplied reg ops */ * Setup resource maps for this bus node. * Set the platform specific hot plug mode. /* initialize hot plug controller hw */ /* initialize slot information soft state structure */ /* register the hot plug slot with DDI HP framework */ /* create minor node for this slot */ /* HPC initialization is complete now */ /* For debug, dump the HPC registers */ * Uninitialize HPC soft state structure and free up any resources * used for the HPC instance. PCIE_DBG(
"pciehpc_uninit() called (dip=%p)\n", (
void *)
dip);
/* get the soft state structure for this dip */ /* unregister the slot */ /* uninit any slot info data structures */ /* uninitialize hpc, remove interrupt handler, etc. */ * Destroy resource maps for this bus node. /* destroy the soft state structure */ * Interrupt handler for PCI-E Hot plug controller interrupts. * Note: This is only for native mode hot plug. This is called * by the nexus driver at interrupt context. Interrupt Service Routine * registration is done by the nexus driver for both hot plug and * non-hot plug interrupts. This function is called from the ISR * of the nexus driver to handle hot-plug interrupts. /* get the soft state structure for this dip */ /* make sure the controller soft state is initialized */ /* if it is not NATIVE hot plug mode then return */ /* read the current slot status register */ /* check if there are any hot plug interrupts occurred */ /* no hot plug events occurred */ /* clear the interrupt status bits */ /* check for CMD COMPLETE interrupt */ PCIE_DBG(
"pciehpc_intr(): CMD COMPLETED interrupt received\n");
/* wake up any one waiting for Command Completion event */ /* check for ATTN button interrupt */ PCIE_DBG(
"pciehpc_intr(): ATTN BUTTON interrupt received\n");
/* if ATTN button event is still pending then cancel it */ /* wake up the ATTN event handler */ /* check for power fault interrupt */ PCIE_DBG(
"pciehpc_intr(): POWER FAULT interrupt received" /* disable power fault detction interrupt */ * Send the event to DDI Hotplug framework, power off /* check for MRL SENSOR CHANGED interrupt */ /* For now (phase-I), no action is taken on this event */ PCIE_DBG(
"pciehpc_intr(): MRL SENSOR CHANGED interrupt received" /* check for PRESENCE CHANGED interrupt */ PCIE_DBG(
"pciehpc_intr(): PRESENCE CHANGED interrupt received" * card is inserted into the slot, ask DDI Hotplug * framework to change state to Present. }
else {
/* card is removed from the slot */ /* Card is removed when slot is enabled */ /* make sure to disable power fault detction intr */ * Ask DDI Hotplug framework to change state to Empty /* check for DLL state changed interrupt */ PCIE_DBG(
"pciehpc_intr(): DLL STATE CHANGED interrupt received" * Handle hotplug commands * Note: This function is called by DDI HP framework at kernel context only PCIE_DBG(
"pciehpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
/* get the current slot state */ * Get the current state of the slot from the hw. * The slot state should have been initialized before this function gets called. /* read the Slot Control Register */ /* read the current Slot Status Register */ /* get POWER led state */ /* no device present; slot is empty */ * Device is powered on. Set to "ENABLED" state (skip * POWERED state) because there is not a explicit "enable" * action exists for PCIe. * If it is already in "POWERED" state, then keep it until * user explicitly change it to other states. /* platform may not have initialized it */ PCIE_DBG(
"%s#%d: Invalid slot number!\n",
* construct the slot_name: * if "slot-names" property exists then use that name * else if valid slot number exists then it is "pcie<slot-num>". * else it will be "pcie<sec-bus-number>dev0" * Note: for PCI-E slots, the device number is always 0 so the * first (and only) string is the slot name for this slot. /* use device number ie. 0 */ * Read/Write access to HPC registers. If platform nexus has non-standard * HPC access mechanism then regops functions are used to do reads/writes. * ************************************************************************ * *** Local functions (called within this file) * *** PCIe Native Hotplug mode specific functions * ************************************************************************ * Initialize HPC hardware, install interrupt handler, etc. It doesn't * enable hot plug interrupts. * (Note: It is called only from pciehpc_init().) /* read the Slot Control Register */ /* disable all interrupts */ /* clear any interrupt status bits */ * Uninitialize HPC hardware, uninstall interrupt handler, etc. * (Note: It is called only from pciehpc_uninit().) * Setup slot information for use with DDI HP framework. * setup DDI HP framework slot information structure /* read Slot Capabilities Register */ /* check if Attn Button present */ /* check if Manual Retention Latch sensor present */ * PCI-E version 1.1 defines EMI Lock Present bit * in Slot Capabilities register. Check for it. /* setup thread for handling ATTN button events */ PCIE_DBG(
"pciehpc_slotinfo_init: setting up ATTN button event " /* get current slot state from the hw */ "waiting for ATTN thread exit\n");
PCIE_DBG(
"pciehpc_slotinfo_uninit: ATTN thread exit\n");
* Enable hot plug interrupts. * Note: this is only for Native hot plug mode. /* clear any interrupt status bits */ /* read the Slot Control Register */ * enable interrupts: power fault detection interrupt is enabled * only when the slot is powered ON * Disable hot plug interrupts. * Note: this is only for Native hot plug mode. /* read the Slot Control Register */ /* disable all interrupts */ /* clear any interrupt status bits */ * Allocate a new hotplug controller and slot structures for HPC * associated with this dip. /* Allocate a new slot structure. */ /* Initialize the interrupt mutex */ /* Initialize synchronization conditional variable */ * Remove the HPC controller and slot structures /* get the soft state structure for this dip */ * Register the PCI-E hot plug slot with DDI HP framework. /* register the slot with DDI HP framework */ PCIE_DBG(
"pciehpc_register_slot() failed to register slot %d\n",
PCIE_DBG(
"pciehpc_register_slot(): registered slot %d\n",
* Unregister the PCI-E hot plug slot from DDI HP framework. /* unregister the slot with DDI HP framework */ PCIE_DBG(
"pciehpc_unregister_slot(): unregistered slot %d\n",
* Note: This function is called by DDI HP framework at kernel context only /* get the current state of the slot */ /* check if the slot is already in the 'enabled' state */ /* slot is already in the 'enabled' state */ PCIE_DBG(
"pciehpc_slot_poweron() slot %d already enabled\n",
/* read the Slot Status Register */ /* make sure the MRL switch is closed if present */ /* make sure the slot has a device present */ /* get the current state of Slot Control Register */ * Enable power to the slot involves: * 1. Set power LED to blink and ATTN led to OFF. * 2. Set power control ON in Slot Control Reigster and * wait for Command Completed Interrupt or 1 sec timeout. * 3. If Data Link Layer State Changed events are supported * then wait for the event to indicate Data Layer Link * is active. The time out value for this event is 1 second. * This is specified in PCI-E version 1.1. * 4. Set power LED to be ON. /* 1. set power LED to blink & ATTN led to OFF */ /* 2. set power control to ON */ /* 3. wait for DLL State Change event, if it's supported */ /* wait 1 sec for the DLL State Changed event */ /* wait 1 sec for link to come up */ /* check power is really turned ON */ PCIE_DBG(
"slot %d fails to turn on power on connect\n",
/* clear power fault status */ /* enable power fault detection interrupt */ /* 4. Set power LED to be ON */ /* if EMI is present, turn it ON */ /* wait 1 sec after toggling the state of EMI lock */ /* if power is ON, set power control to OFF */ /* set power led to OFF */ /* get the current state of the slot */ /* check if the slot is not in the "enabled' state */ /* slot is in the 'disabled' state */ /* read the Slot Status Register */ /* make sure the slot has a device present */ PCIE_DBG(
"pciehpc_slot_poweroff(): slot %d is empty\n",
* Disable power to the slot involves: * 1. Set power LED to blink. * 2. Set power control OFF in Slot Control Reigster and * wait for Command Completed Interrupt or 1 sec timeout. * 3. Set POWER led and ATTN led to be OFF. /* 1. set power LED to blink */ /* disable power fault detection interrupt */ /* 2. set power control to OFF */ /* check for power control bit to be OFF */ /* 3. Set power LED to be OFF */ /* if EMI is present, turn it OFF */ /* wait 1 sec after toggling the state of EMI lock */ /* get the current state of the slot */ * Note: This function is called by DDI HP framework at kernel context only /* get the current state of the slot */ * Probe a given PCIe Hotplug Connection (CN). PCIE_DBG(
"pciehpc_slot_probe() failed\n");
/* turn the ATTN led ON for configure failure */ /* if power to the slot is still on then set Power led to ON */ /* get the current state of the slot */ * Note: This function is called by DDI HP framework at kernel context only /* get the current state of the slot */ * Unprobe a given PCIe Hotplug Connection (CN). PCIE_DBG(
"pciehpc_slot_unprobe() failed\n");
/* if power to the slot is still on then set Power led to ON */ /* get the current state of the slot */ * From EMPTY to PRESENT, just check the hardware /* should never reach here */ ASSERT(
"unknown devinfo state");
* From PRESENT to EMPTY, just check hardware slot /* should never reach here */ ASSERT(
"unknown devinfo state");
/* Change slot state to a target state */ /* check whether the requested property is "all" or "help" */ * Add all properties into the request list, so that we * will get the values in the following for loop. for (i = 0; i < n; i++) {
* Empty the request list, and add help strings into the * return list. We will pass the following for loop. for (i = 0; i < n; i++) {
/* get the current slot state */ /* for each requested property, get the value and add it to nvlist */ * When getting all properties, just ignore the * one that's not available under certain state. /* unsupported property */ /* pack nvlist and copyout */ /* check whether the requested property is "help" */ /* Validate the request */ /* get the current slot state */ * Send a command to the PCI-E Hot Plug Controller. * NOTES: The PCI-E spec defines the following semantics for issuing hot plug * waits for Command Complete event after issuing a command (i.e writing * to the Slot Control register). The command completion could take as * long as 1 second so software should be prepared to wait for 1 second * before issuing another command. * software could issue multiple Slot Control writes without any delay * PCI-E version 1.1 spec defines No Command Completed * Support bit (bit#18) in Slot Capabilities register. If this * bit is set then slot doesn't support notification of command * If no Command Completion event is supported or it is ACPI * hot plug mode then just issue the command and return. * ************************************** * Command Complete events are supported. * ************************************** * If HPC is not yet initialized then just poll for the Command /* write the command to the HPC */ /* poll for status completion */ /* wait for 10 msec before checking the status */ /* clear the status bits */ /* HPC is already initialized */ * If previous command is still pending then wait for its * completion. i.e cv_wait() * Issue the command and wait for Command Completion or PCIE_DBG(
"pciehpc_issue_hpc_command: Command Complete" " interrupt is not received for slot %d\n",
/* clear the status info in case interrupts are disabled? */ /* clear the status bits */ /* wake up any one waiting for issuing another command to HPC */ * pciehcp_attn_btn_handler() * This handles ATTN button pressed event as per the PCI-E 1.1 spec. PCIE_DBG(
"pciehpc_attn_btn_handler: thread started\n");
"pciehpc_attn_btn_handler");
/* wait for ATTN button event */ /* get the current state of power LED */ /* Blink the Power LED while we wait for 5 seconds */ /* wait for 5 seconds before taking any action */ * It is a time out; make sure the ATTN pending * flag is still ON before sending the event to * We can't call ddihp_cn_gen_sysevent * here since it's not a DDI interface. /* restore the power LED state */ /* wait for another ATTN button event */ PCIE_DBG(
"pciehpc_attn_btn_handler: thread exit\n");
* convert LED state from PCIE HPC definition to pcie_hp_led_state_t * Get the state of an LED. /* get the current state of Slot Control register */ PCIE_DBG(
"pciehpc_get_led_state() invalid LED %d\n",
led);
* Set the state of an LED. It updates both hw and sw state. /* get the current state of Slot Control register */ PCIE_DBG(
"pciehpc_set_led_state() invalid LED %d\n",
led);
PCIE_DBG(
"pciehpc_set_led_state() invalid LED state %d\n",
/* update the Slot Control Register */ /* get the current state of Slot Control register */ PCIE_DBG(
"pciehpc_set_led_state: slot %d power-led %s attn-led %s\n",
* Dump PCI-E Hot Plug registers. PCIE_DBG(
"pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
PCIE_DBG(
"Attention Button Present = %s\n",
PCIE_DBG(
"Power controller Present = %s\n",
PCIE_DBG(
"Attn Indicator Present = %s\n",
PCIE_DBG(
"Power Indicator Present = %s\n",
PCIE_DBG(
"Attn Button interrupt Enabled = %s\n",
PCIE_DBG(
"Power Fault interrupt Enabled = %s\n",
PCIE_DBG(
"MRL Sensor INTR Enabled = %s\n",
PCIE_DBG(
"Presence interrupt Enabled = %s\n",
PCIE_DBG(
"Cmd Complete interrupt Enabled = %s\n",
PCIE_DBG(
"HotPlug interrupt Enabled = %s\n",