--- a/drivers/pci/hotplug/Kconfig Tue Jun 24 13:31:12 2003 +++ b/drivers/pci/hotplug/Kconfig Tue Jun 24 13:31:12 2003 @@ -72,6 +91,10 @@ as a module, say M here and read . When in doubt, say N. + +config HOTPLUG_PCI_AMD + tristate "AMD Standard Hot Plug Controller (SHPC) driver" + depends on HOTPLUG_PCI config HOTPLUG_PCI_CPCI tristate "CompactPCI Hotplug driver" --- a/drivers/pci/hotplug/Makefile Tue Jun 24 13:31:12 2003 +++ b/drivers/pci/hotplug/Makefile Tue Jun 24 13:31:12 2003 @@ -6,8 +6,10 @@ 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_AMD) += amdshpc.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o +obj-$(CONFIG_HOTPLUG_PCI_DUMMY) += dummyphp.o pci_hotplug-objs := pci_hotplug_core.o @@ -31,6 +33,15 @@ acpiphp_glue.o \ acpiphp_pci.o \ acpiphp_res.o + +amdshpc-objs := amdshpc_core.o \ + amdshpc_ctrl.o \ + amdshpc_pci.o \ + amdshpc_ddi.o \ + amdshpc_int.o \ + amdshpc_led.o \ + amdshpc_enb.o \ + amdshpc_dsb.o ifdef CONFIG_HOTPLUG_PCI_ACPI EXTRA_CFLAGS += -D_LINUX -I$(TOPDIR)/drivers/acpi --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc.h Tue Jun 24 13:31:13 2003 @@ -0,0 +1,1265 @@ +/* + * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2003 IBM Corp. + * Copyright (c) 2002-2003 Advanced Micro Devices + * + * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS + * AND CONDITIONS OF THE GNU GENERAL PUBLIC + * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS + * INCLUDED WITH THIS FILE AND POSTED AT + * http://www.gnu.org/licenses/gpl.html + * + * Send feedback to + * + */ + +#ifndef _SHPC_H_ +#define _SHPC_H_ + + +#include +#include +#include +#include +#include +#include +#include +#include "pci_hotplug.h" + +// +// Timeouts +// +#define ONE_TENTH_SEC_TIMEOUT 10 // 0.1 sec +#define ONE_SEC_TIMEOUT HZ * 1 // 1 sec +#define FIVE_SEC_TIMEOUT HZ * 5 // 5 secs +#define TEN_SEC_TIMEOUT HZ * 10 // 10 secs +#define FIFTEEN_SEC_TIMEOUT HZ * 15 // 15 secs +#define QUIESCE_QUIET_TIMEOUT HZ * 30 // 30 secs +#define QUIESCE_TIMEOUT HZ * 60 // 60 secs +#define ONE_SEC_INCREMENT HZ * 1 // 1 sec + +#define SLOT_MAGIC 0x67267322 +struct slot { + u32 magic; + struct slot *next; + struct list_head slot_list; + 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 *p_sm_slot; + struct hotplug_slot *hotplug_slot; + void* private; +}; + +struct controller { + struct controller *next; + void *shpc_context; + u32 ctrl_int_comp; + void *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 slot *slot; + u8 interrupt; + u8 bus; + u8 device; + u8 function; + u8 slot_device_offset; + u8 first_slot; + u8 add_support; + u16 vendor_id; +}; + + +static LIST_HEAD(slot_list); + +#if !defined(CONFIG_HOTPLUG_PCI_AMD_MODULE) + #define MY_NAME "amd_shpc.o" +#else + #define MY_NAME THIS_MODULE->name +#endif + +// +// Debug Facilities +// +#define debug 0 +#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) + +#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_amd "Non-AMD PCI hot plug controller is not supported by this driver.\n" +#define msg_HPC_not_amd_hp "Device is not a hot plug controller.\n" +#define msg_HPC_not_supported "This system is not supported by this version of amdshpc. Upgrade to a newer version of amdshpc\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" + +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; +}; + + +#ifndef FALSE + #define FALSE 0 + #define TRUE 1 +#endif + +#define IN +#define OUT + +enum mutex_action { + ACQUIRE, + RELEASE, +}; + +enum hp_boolean { + HP_FALSE = 0, + HP_TRUE = 1, +}; + +// card power requirements +enum hp_power_requirements { + POWER_LOW, // low power requirements + POWER_MEDIUM, // medium power requirements + POWER_HIGH, // high power requirements +}; + +// +// slot event masks +// +#define ATTN_BUTTON_EVENT 0x00000001 +#define ALERT_EVENT 0x00000002 +#define BUS_REBALANCE_EVENT 0x00000004 +#define QUIESCE_EVENT 0x00000008 +#define ATTN_LED_PROBLEM_EVENT 0x00000010 +#define ATTN_LED_REQUEST_EVENT 0x00000020 +#define SLOT_REQUEST_EVENT 0x00000040 +#define SLOT_TIMER1_EVENT 0x00000080 +#define SLOT_TIMER2_EVENT 0x00000100 +#define SLOT_TIMER3_EVENT 0x00000200 +#define SLOT_TIMER4_EVENT 0x00000400 +#define SLOT_TIMER5_EVENT 0x00000800 +#define SLOT_TIMER6_EVENT 0x00001000 +#define SLOT_TIMER7_EVENT 0x00002000 +#define SLOT_TIMER8_EVENT 0x00004000 +#define SLOT_TIMER9_EVENT 0x00008000 +#define SLOT_TIMER10_EVENT 0x00010000 +#define LED_TIMER1_EVENT 0x00020000 +#define LED_TIMER2_EVENT 0x00040000 +#define LED_TIMER3_EVENT 0x00080000 +#define LED_TIMER4_EVENT 0x00100000 +#define CMD_ACQUIRE_EVENT 0x00200000 +#define CMD_RELEASE_EVENT 0x00400000 +#define LED_CMD_ACQUIRE_EVENT 0x00800000 +#define LED_CMD_RELEASE_EVENT 0x01000000 +#define BUS_RELEASE_EVENT 0x02000000 +#define BUS_ACQUIRE_EVENT 0x04000000 + +// +// controller event masks +// +#define BUS_COMPLETE_EVENT 0x00000001 +#define SUSPEND_EVENT 0x00000002 +#define RESUME_EVENT 0x00000004 +#define REMOVE_EVENT 0x00000008 +#define EXIT_REQUEST_EVENT 0x00000010 +#define CTRL_TIMER_EVENT 0x00000020 +#define CMD_COMPLETION_EVENT 0x00000040 +#define CMD_AVAILABLE_MUTEX_EVENT 0x00000080 +#define BUS_AVAILABLE_MUTEX_EVENT 0x00000100 +#define LED_CMD_AVAILABLE_MUTEX_EVENT 0x00000200 + + +#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400 +#define SLOT_MASK 0x28 + + +#define ADD_NOT_SUPPORTED 0x00000003 +#define ADAPTER_NOT_SAME 0x00000006 +#define NO_ADAPTER_PRESENT 0x00000009 + +#define REMOVE_NOT_SUPPORTED 0x00000003 + + + +// slot states +enum hp_states { + SLOT_DISABLE, // slot disable + SLOT_ENABLE, // slot enable +}; + +// indicator values +enum mode_frequency { + MODE_PCI_33, // PCI 33Mhz + MODE_PCI_66, // PCI 66Mhz + MODE_PCIX_66, // PCI-X 66Mhz + MODE_PCIX_100, // PCI-X 100Mhz + MODE_PCIX_133, // PCI-X 133Mhz +}; + +enum hp_indicators { + INDICATOR_OFF, // Indicator off state + INDICATOR_ON, // Indicator on state + INDICATOR_BLINK, // Indicator blink state + INDICATOR_NORMAL, // Indicator normal state +}; + +struct pci_resource { + struct pci_resource * next; + u32 base; + u32 length; +}; + +struct resource_descriptor { + u32 base; + u32 limit; +}; + +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 NOT_ENOUGH_RESOURCES 0x0000000B +#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C + +// +// Prototypes +// +extern int amdshpc_resource_sort_and_combine (struct pci_resource **head); + +// +// State-Machine Function +// +typedef long ( *SLOT_STATE_FUNCTION )( + void* shpc_context, + void* slot_context); + +// +// SHPC Constants +// +#define SHPC_MAX_NUM_SLOTS 4 + + +#define arraysize(p) (sizeof(p)/sizeof((p)[0])) + + +// +// SHPC Register Offsets +// +enum shpc_register_offset { + SHPC_SLOTS_AVAILABLE1_REG_OFFSET = 0x04, + SHPC_SLOTS_AVAILABLE2_REG_OFFSET = 0x08, + SHPC_SLOT_CONFIG_REG_OFFSET = 0x0C, + SHPC_SEC_BUS_CONFIG_REG_OFFSET = 0x10, + SHPC_COMMAND_REG_OFFSET = 0x14, + SHPC_STATUS_REG_OFFSET = 0x16, + SHPC_INT_LOCATOR_REG_OFFSET = 0x18, + SHPC_SERR_LOCATOR_REG_OFFSET = 0x1C, + SHPC_SERR_INT_REG_OFFSET = 0x20, + SHPC_LOGICAL_SLOT_REG_OFFSET = 0x24, +}; + + +// +// SHPC Slots Available Register I +// +union SHPC_SLOTS_AVAILABLE1_DWREG { + struct { + u32 N_33CONV :5; // 4:0 + u32 reserved1 :3; // 7:5 + u32 N_66PCIX :5; // 12:8 + u32 reserved2 :3; // 15:13 + u32 N_100PCIX :5; // 20:16 + u32 reserved3 :3; // 23:21 + u32 N_133PCIX :5; // 28:24 + u32 reserved4 :3; // 31:29 + }x; + u32 AsDWord; +}; + + +// +// SHPC Slots Available Register II +// +union SHPC_SLOTS_AVAILABLE2_DWREG { + struct { + u32 N_66CONV :5; // 4:0 + u32 reserved4 :27; // 31:5 + }x; + u32 AsDWord; +}; + + +// +// SHPC Slot Configuration Register +// +union SHPC_SLOT_CONFIG_DWREG { + struct { + u32 NSI :5; // 4:0 + u32 reserved1 :3; // 7:5 + u32 FDN :5; // 12:8 + u32 reserved2 :3; // 15:13 + u32 PSN :11; // 26:16 + u32 reserved3 :2; // 28:27 + u32 PSN_UP :1; // 29 + u32 MRLSI :1; // 30 + u32 ABI :1; // 31 + }x; + u32 AsDWord; +}; + + +// +// SHPC Secondary Bus Configuration Register +// +union SHPC_SEC_BUS_CONFIG_DWREG { + struct { + u32 MODE :3; // 2:0 + u32 reserved :21; // 23:3 + u32 format :8; // 31:24 + }x; + u32 AsDWord; +}; + + +// +// SHPC Command Register +// +union SHPC_COMMAND_WREG { + struct { + u16 state : 2; // 1:0 + u16 power_led : 2; // 3:2 + u16 attention_led : 2; // 5:4 + u16 code : 2; // 7:6 + u16 TGT : 5; // 12:8 + u16 reserved : 3; // 15:13 + } Slot; + struct { + u16 speed_mode : 3; // 2:0 + u16 code : 5; // 7:3 + u16 reserved : 8; // 15:8 + } Bus; + struct { + u16 code : 8; // 7:0 + u16 reserved : 8; // 15:8 + }x; + u16 AsWord; +}; + + +// +// SHPC Status Register +// +union SHPC_STATUS_WREG { + struct { + u16 BSY :1; // 0 + u16 MRLO_ERR :1; // 1 + u16 INVCMD_ERR :1; // 2 + u16 INVSM_ERR :1; // 3 + u16 reserved :12; // 15:4 + }x; + u16 AsWord; +}; + + +// +// SHPC Interrupt Locator Register +// +union SHPC_INT_LOCATOR_DWREG { + struct { + u32 CC_IP :1; // 0 + u32 SLOT_IP :4; // 4:1 + u32 reserved :27; // 31:5 + }x; + u32 AsDWord; +}; + + +// +// SHPC SERR Locator Register +// +union SHPC_SERR_LOCATOR_DWREG { + struct { + u32 A_SERRP :1; // 0 + u32 SLOT_SERRP :4; // 4:1 + u32 reserved :27; // 31:5 + }x; + u32 AsDWord; +}; + + +// +// SHPC SERR-INT Register +// +union SHPC_SERR_INT_DWREG { + struct { + u32 GIM :1; // 0 + u32 GSERRM :1; // 1 + u32 CC_IM :1; // 2 + u32 A_SERRM :1; // 3 + u32 reserved1 :12; // 15:4 + u32 CC_STS :1; // 16 + u32 ATOUT_STS :1; // 17 + u32 reserved2 :14; // 31:18 + }x; + u32 AsDWord; +}; + + +// +// SHPC Logical Slot Register +// +union SHPC_LOGICAL_SLOT_DWREG { + struct { + u32 S_STATE :2; // 1:0 + u32 PIS :2; // 3:2 + u32 AIS :2; // 5:4 + u32 PF :1; // 6 + u32 AB :1; // 7 + u32 MRLS :1; // 8 + u32 M66_CAP :1; // 9 + u32 PRSNT1_2 :2; // 11:10 + u32 PCIX_CAP :2; // 13:12 + u32 reserved1 :2; // 15:14 + u32 CPC_STS :1; // 16 + u32 IPF_STS :1; // 17 + u32 ABP_STS :1; // 18 + u32 MRLSC_STS :1; // 19 + u32 CPF_STS :1; // 20 + u32 reserved2 :3; // 23:21 + u32 CP_IM :1; // 24 + u32 IPF_IM :1; // 25 + u32 AB_IM :1; // 26 + u32 MRLS_IM :1; // 27 + u32 CPF_IM :1; // 28 + u32 MRLS_SERRM :1; // 29 + u32 CPF_SERRM :1; // 30 + u32 reserved3 :1; // 31 + }x; + u32 AsDWord; +}; + + +// +// Bus Speed/Mode +// +enum shpc_speed_mode { + SHPC_BUS_CONV_33 = 0, + SHPC_BUS_CONV_66 = 1, + SHPC_BUS_PCIX_66 = 2, + SHPC_BUS_PCIX_100 = 3, + SHPC_BUS_PCIX_133 = 4, +}; + + +// +// Slot PCIX Capability +// +enum shpc_slot_pcix_cap { + SHPC_SLOT_CONV = 0, + SHPC_SLOT_PCIX_66 = 1, + SHPC_SLOT_PCIX_133 = 3, +}; + + +// +// Slot LEDs +// +enum shpc_slot_led { + SHPC_led_NO_CHANGE = 0, + SHPC_LED_ON = 1, + SHPC_LED_BLINK = 2, + SHPC_LED_OFF = 3, +}; + + +// +// Slot State +// +enum shpc_slot_state { + SHPC_SLOT_NO_CHANGE = 0, + SHPC_POWER_ONLY = 1, + SHPC_ENABLE_SLOT = 2, + SHPC_DISABLE_SLOT = 3, +}; + + +// +// Command Code +// +#define SHPC_SLOT_OPERATION 0x00 // 7:6 (00xxxxxxb) +#define SHPC_SET_BUS_SPEED_MODE 0x08 // 7:3 (01000xxxb) +#define SHPC_POWER_ONLY_ALL_SLOTS 0x48 // 7:0 (01001000b) +#define SHPC_ENABLE_ALL_SLOTS 0x49 // 7:0 (01001001b) + + +// +// SHPC Status +// +enum shpc_status { + SHPC_STATUS_CLEARED = 0, + SHPC_STATUS_SET = 1, +}; + + +// +// SHPC Mask +// +enum shpc_mask { + SHPC_UNMASKED = 0, + SHPC_MASKED = 1, +}; + + +// +// Slot MRL Sensor +// +enum shpc_slot_mrl { + SHPC_MRL_CLOSED = 0, + SHPC_MRL_OPEN = 1, +}; + + +// +// Slot Attn Button +// +enum shpc_slot_attn_button { + SHPC_ATTN_BUTTON_RELEASED = 0, + SHPC_ATTN_BUTTON_PRESSED = 1, +}; + + +// +// Card Power Requirements +// +enum shpc_card_power { + SHPC_CARD_PRESENT_7_5W = 0, + SHPC_CARD_PRESENT_15W = 1, + SHPC_CARD_PRESENT_25W = 2, + SHPC_SLOT_EMPTY = 3, +}; + + +// slot config structure +union SLOT_CONFIG_INFO { + struct { + u32 lu_slots_implemented : 5; // [ 4:0 ]Number of slots implemented + u32 lu_reserved1 : 3; // [ 7:5 ]Reserved + u32 lu_base_FDN : 5; // [ 12:8 ]First Device Number + u32 lu_reserved2 : 3; // [ 15:13 ]Reserved + u32 lu_base_PSN : 11; // [ 26:16 ]Physical Slot Number + u32 lu_reserved3 : 2; // [ 28:27 ]Reserved + u32 lu_PSN_up : 1; // [ 29 ]PSN Up (1=TRUE, 0=FALSE) + u32 lu_reserved4 : 2; // [ 31:30 ]Reserved + }x; + u32 AsDWord; +}; + + +// logical slot information +union SLOT_STATUS_INFO { + struct { + u32 lu_slot_state : 1; // [ 0 ]Slot state (1=Enabled, 0=Disabled) + u32 lu_power_fault : 1; // [ 1 ]Power-Fault? (1=TRUE, 0=FALSE) + u32 lu_card_present : 1; // [ 2 ]Card Present? (1=TRUE, 0=FALSE) + u32 lu_card_power : 2; // [ 4:3 ]Card Power Requirements (low/medium/high) + u32 lu_card_mode_freq_cap : 3; // [ 7:5 ]Card Speed/mode capability + u32 lu_mrl_implemented : 1; // [ 8 ]MRL Implemented? (1=TRUE, 0=FALSE) + u32 lu_mrl_opened : 1; // [ 9 ]MRL State (if implemented: 1=TRUE, 0=FALSE) + u32 lu_ai_state : 2; // [ 11:10 ]Attn Indicator State (Blink/On/Off) + u32 lu_pi_state : 2; // [ 13:12 ]Power Indicator State (Blink/On/Off) + u32 lu_reserved1 : 2; // [ 14 ]Reserved + u32 lu_card_pci66_capable : 1; // [ 15 ]Card PCI66 capability (1=TRUE, 0=FALSE) + u32 lu_bus_mode_freq : 3; // [ 18:16 ]Current Bus speed/mode + u32 lu_max_bus_mode_freq : 3; // [ 21:19 ]Maximum Bus speed/mode + u32 lu_reserved2 : 9; // [ 30:22 ]Reserved + u32 lu_request_failed : 1; // [ 31 ]Request Failed? (1=TRUE, 0=FALSE) + }x; + u32 AsDWord; +}; + +enum return_status { + STATUS_UNSUCCESSFUL, + STATUS_SUCCESS +}; + +// +// Async Request +// +enum shpc_async_request { + SHPC_ASYNC_ENABLE_SLOT, + SHPC_ASYNC_DISABLE_SLOT, + SHPC_ASYNC_SURPRISE_REMOVE, + SHPC_ASYNC_QUIESCE_DEVNODE, + SHPC_ASYNC_QUIESCE_DEVNODE_QUIET, + SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY, + SHPC_ASYNC_CANCEL_QUIESCE_DEVNODE, + SHPC_ASYNC_LED_LOCATE, + SHPC_ASYNC_LED_NORMAL +}; + + +// +// Async Request +// +struct async_request { + enum shpc_async_request type; + wait_queue_head_t event; + unsigned long timeout; + void *request_context; +}; + + +// +// Async Completion +// +struct async_completion { + enum shpc_async_request type; + unsigned long timeout; + u8 hw_initiated; + u8 done; + enum hp_boolean failed; + void *request_context; +}; + +// **************************************************************************** +// +// async_callback() @ PASSIVE_LEVEL +// +// Parameters +// driver_context - Pointer provided in hp_AddDevice() +// slot_id - Zero-based slot number (0..n-1). +// Request - Async request completed. For example: Slot Enable/Disable, AttnLED Attn/Normal. +// Status - Slot status at completion +// request_context - Pointer provided in hp_StartAsyncRequest(), NULL for +// completions on hardware-initiated requests. +// +// Return Value +// For QUIESCE_DEVNODE request: #DevNodes associated with a particular slot, else 0. +// +// **************************************************************************** +typedef unsigned long ( *SHPC_ASYNC_CALLBACK )( void* driver_context, + u8 slot_id, + enum shpc_async_request Request, + union SLOT_STATUS_INFO Status, + void* request_context ); + +// +// Slot Context +// +struct slot_context { + + spinlock_t slot_spinlock; + struct semaphore slot_event_bits_semaphore; + struct semaphore cmd_acquire_mutex; + struct semaphore bus_acquire_mutex; + u32 *logical_slot_addr; + u8 slot_number; + u8 slot_psn; + u32 quiesce_requests; + u32 quiesce_replies; + u8 slot_enabled; + enum shpc_speed_mode card_speed_mode; + u8 card_pci66_capable; + u8 in_bus_speed_mode_contention; + u8 problem_detected; + u8 slot_quiesced; + u8 slot_occupied; + struct tasklet_struct attn_button_dpc; + struct tasklet_struct mrl_sensor_dpc; + struct tasklet_struct card_presence_dpc; + struct tasklet_struct isolated_power_fault_dpc; + struct tasklet_struct connected_power_fault_dpc; + wait_queue_head_t slot_event; + wait_queue_head_t led_cmd_acquire_event; + wait_queue_head_t led_cmd_release_event; + wait_queue_head_t cmd_acquire_event; + wait_queue_head_t cmd_release_event; + wait_queue_head_t bus_acquire_event; + wait_queue_head_t bus_release_event; + u32 slot_event_bits; + void *slot_thread; + void *attn_led_thread; + SLOT_STATE_FUNCTION slot_function; + SLOT_STATE_FUNCTION attn_led_function; + struct async_request slot_request; + struct async_completion slot_completion; + struct async_request attn_led_request; + struct async_completion attn_led_completion; + void *shpc_context; + struct timer_list slot_timer1; + struct timer_list slot_timer2; + struct timer_list slot_timer3; + struct timer_list slot_timer4; + struct timer_list slot_timer5; + struct timer_list slot_timer6; + struct timer_list slot_timer7; + struct timer_list slot_timer8; + struct timer_list slot_timer9; + struct timer_list slot_timer10; + struct timer_list led_timer1; + struct timer_list led_timer2; + struct timer_list led_timer3; + struct timer_list led_timer4; +}; + +// +// SHPC Context +// +struct shpc_context { + spinlock_t shpc_spinlock; + struct semaphore shpc_event_bits_semaphore; + void *mmio_base_addr; + struct shpc_context *next; + u8 first_slot; + u8 number_of_slots; + u8 slots_enabled; + u8 at_power_device_d0; + u8 bus_released; + enum shpc_speed_mode max_speed_mode; + enum shpc_speed_mode bus_speed_mode; + struct semaphore cmd_available_mutex; + struct tasklet_struct cmd_completion_dpc; + struct semaphore bus_available_mutex; + wait_queue_head_t *user_event_pointer; + u32 shpc_event_bits; + void *driver_context; + SHPC_ASYNC_CALLBACK async_callback; + u32 shpc_instance; + struct slot_context slot_context[ SHPC_MAX_NUM_SLOTS ]; + void *hpc_reg; // cookie for our pci controller location + struct pci_ops *pci_ops; + 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; + u8 interrupt; + u8 bus; + u8 device; + u8 function; + u16 vendor_id; + u32 ctrl_int_comp; + u8 add_support; +}; + +// +// Function Prototypes +// +int amdshpc_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot); +int amdshpc_process_SI (struct controller *ctrl, struct pci_func *func); +int amdshpc_process_SS (struct controller *ctrl, struct pci_func *func); +int amdshpc_find_available_resources (struct controller *ctrl, void *rom_start); +int amdshpc_save_config(struct controller *ctrl, int busnumber, union SLOT_CONFIG_INFO * is_hot_plug); +struct pci_func *amdshpc_slot_create(u8 busnumber); + + +void hp_clear_shpc_event_bit(struct shpc_context * shpc_context, u32 mask); +void hp_set_shpc_event_bit(struct shpc_context * shpc_context, u32 mask); + +void hp_clear_slot_event_bit(struct slot_context * slot_context, u32 mask); +void hp_set_slot_event_bit(struct slot_context * slot_context, u32 mask); + +void hp_send_event_to_all_slots(struct shpc_context *shpc_context, u32 mask); +void hp_send_slot_event(struct slot_context *slot_context, u32 mask); + +int hp_get_led_cmd_available_mutex_thread(void *slot_context); +int hp_get_cmd_available_mutex_thread (void *slot_context); +int hp_get_bus_available_mutex_thread(void *slot_context); +int hp_cmd_available_mutex_thread(void * slot_context); +int hp_bus_available_mutex_thread(void * slot_context); +int hp_led_cmd_available_mutex_thread(void * slot_context); + +void hp_slot_timer1_func(unsigned long data); +void hp_slot_timer2_func(unsigned long data); +void hp_slot_timer3_func(unsigned long data); +void hp_slot_timer4_func(unsigned long data); +void hp_slot_timer5_func(unsigned long data); +void hp_slot_timer6_func(unsigned long data); +void hp_slot_timer7_func(unsigned long data); +void hp_slot_timer8_func(unsigned long data); +void hp_slot_timer9_func(unsigned long data); +void hp_slot_timer10_func(unsigned long data); +void hp_led_timer1_func(unsigned long data); +void hp_led_timer2_func(unsigned long data); +void hp_led_timer3_func(unsigned long data); +void hp_led_timer4_func(unsigned long data); + +irqreturn_t hp_interrupt_service(int IRQ, void *v, struct pt_regs *regs); + +u32 board_replaced(struct pci_func * func, struct controller * ctrl); +struct pci_func *amdshpc_slot_find(u8 bus, u8 device, u8 index); +int amdshpc_save_base_addr_length(struct controller *ctrl, struct pci_func * func); +int amdshpc_save_used_resources (struct controller *ctrl, struct pci_func * func); +int amdshpc_return_board_resources(struct pci_func * func, struct resource_lists * resources); +int amdshpc_save_slot_config (struct controller *ctrl, struct pci_func * new_slot); +int amdshpc_configure_device (struct controller * ctrl, struct pci_func* func); +int amdshpc_unconfigure_device(struct pci_func* func); + + + +void +hp_attn_button_dpc( + unsigned long deferred_context + ); + +void +hp_mrl_sensor_dpc( + unsigned long deferred_context + ); + +void +hp_card_presence_dpc( + unsigned long deferred_context + ); + +void +hp_isolated_power_fault_dpc( + unsigned long deferred_context + ); + +void +hp_connected_power_fault_dpc( + unsigned long deferred_context + ); + +void +hp_cmd_completion_dpc( + unsigned long deferred_context + ); + +int +hp_slot_thread( + void* slot_context + ); + +long +hp_at_slot_disabled_wait_for_slot_request( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_led_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_led_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_power_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_power_cmd_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_power_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_bus_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_bus_released( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_speed_mode_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_speed_mode_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_enable_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_enable_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_disabled_wait_for_enable_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_to_slot_disabled_wait_for_led_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_to_slot_disabled_wait_for_led_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_to_slot_disabled_wait_for_disable_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_to_slot_disabled_wait_for_disable_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_to_slot_disabled_wait_for_disable_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_to_slot_disabled_wait_for_bus_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_enabled_wait_for_slot_request( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_enabled_wait_for_stop_on_bus_rebalance( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_enabled_wait_for_power_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_enabled_wait_for_power_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_enabled_wait_for_led_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_enabled_wait_for_led_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_enabled_wait_for_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_enabled_wait_for_stop_on_slot_disable( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_to_slot_enabled_wait_for_led_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_to_slot_enabled_wait_for_led_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +void +hp_get_slot_configuration( + struct shpc_context* shpc_context + ); + +void +hp_enable_slot_interrupts( + struct slot_context* slot_context + ); + +void +hp_disable_slot_interrupts( + struct slot_context* slot_context + ); + +void +hp_enable_global_interrupts( + struct shpc_context* shpc_context + ); + +void +hp_disable_global_interrupts( + struct shpc_context* shpc_context + ); + +enum shpc_speed_mode +hp_get_bus_speed_mode( + struct shpc_context* shpc_context + ); + +enum shpc_speed_mode +hp_get_card_speed_mode( + struct slot_context* slot_context + ); + +enum mode_frequency +hp_translate_speed_mode( + enum shpc_speed_mode shpc_speed_mode + ); + +enum hp_power_requirements +hp_translate_card_power( + enum shpc_card_power ShpcCardPower + ); + +enum hp_indicators +hp_translate_indicator( + enum shpc_slot_led ShpcIndicator + ); + +u8 +hp_flag_slot_as_enabled( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +u8 +hp_flag_slot_as_disabled( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +u8 +hp_signal_enabled_slots_to_rebalance_bus( + struct shpc_context* shpc_context + ); + +enum shpc_speed_mode +hp_get_max_speed_mode( + struct shpc_context* shpc_context, + enum shpc_speed_mode From_speed_mode + ); + +void +hp_signal_user_event( + struct shpc_context* shpc_context + ); + +void +hp_signal_user_event_at_dpc_level( + struct shpc_context* shpc_context + ); + +int +hp_attn_led_thread( + void* slot_context + ); + +long +hp_wait_for_attn_led_request( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_wait_for_attn_led_blink_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_wait_for_attn_led_blink_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_wait_for_attn_led_blink_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_wait_for_attn_led_normal_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_wait_for_attn_led_normal_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_wait_for_attn_led_back_to_normal_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + +long +hp_wait_for_attn_led_back_to_normal_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ); + + + +#endif // _SHPC_H_ --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc_core.c Tue Jun 24 13:31:13 2003 @@ -0,0 +1,902 @@ +/* + * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2003 IBM Corp. + * Copyright (c) 2002-2003 Advanced Micro Devices + * + * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS + * AND CONDITIONS OF THE GNU GENERAL PUBLIC + * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS + * INCLUDED WITH THIS FILE AND POSTED AT + * http://www.gnu.org/licenses/gpl.html + * + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "amdshpc.h" +#include "amdshpc_ddi.h" +#include "pci_hotplug.h" +#include "../../../arch/i386/pci/pci.h" + +/* Global variables */ +int amdshpc_debug; +struct shpc_context *amdshpc_ctrl_list; // used for the shpc state machine +struct controller *ctrl_list; // used only for resource management +struct pci_func *amdshpc_slot_list[256]; + +static int num_slots; +static void *amdshpc_rom_start; +static unsigned long shpc_instance; + +#define DRIVER_VERSION "1.03" +#define DRIVER_AUTHOR "Dave Keck " +#define DRIVER_DESC "AMD Standard Hot Plug Controller Driver" +#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450 + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +//MODULE_PARM(debug, "i"); +//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); + +// values to be returned to the PCI Hotplug Core +#define CORE_SLOT_DISABLED 0 +#define CORE_SLOT_ENABLED 1 + +#define CORE_INDICATOR_OFF 0 +#define CORE_INDICATOR_ON 1 +#define CORE_INDICATOR_BLINK 2 + +#define CORE_LATCH_CLOSED 1 +#define CORE_LATCH_OPENED 0 + +static int init_slots ( struct controller *ctrl, int num_slots ); +static void translate_slot_info (struct hotplug_slot_info *info, + union SLOT_STATUS_INFO *query); + +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, +}; + +/* Inline functions to check the sanity of a pointer that is passed to us */ +static inline int slot_paranoia_check (struct slot *slot, const char *function) +{ + if (!slot) { + dbg("-->%s - slot == NULL", function); + return -1; + } + if (slot->magic != SLOT_MAGIC) { + dbg("-->%s - bad magic number for slot", function); + return -1; + } + if (!slot->hotplug_slot) { + dbg("-->%s - slot->hotplug_slot == NULL!", function); + return -1; + } + return 0; +} + +static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function) +{ + struct slot *slot; + + if (!hotplug_slot) { + dbg("-->%s - hotplug_slot == NULL\n", function); + return NULL; + } + + slot = (struct slot *)hotplug_slot->private; + if (slot_paranoia_check (slot, function)) + return NULL; + return slot; +} + +static int enable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + struct shpc_context *shpc_context; + union SLOT_STATUS_INFO query; + long status; + int retval = 0; + + + if (slot == NULL) + return -ENODEV; + + dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + /* + * enable the specified slot + */ + shpc_context = ( struct shpc_context * )slot->private; + status = hp_StartAsyncRequest(shpc_context, slot->number, + SHPC_ASYNC_ENABLE_SLOT, 0, slot ); + + // + // pretend async request was completed (we're not queuing slot requests) + // + hp_QuerySlotStatus( shpc_context, slot->number, &query ); + if( status == STATUS_SUCCESS ) { + query.x.lu_slot_state = SLOT_ENABLE; + query.x.lu_pi_state = INDICATOR_BLINK; + if( query.x.lu_card_present && + ( query.x.lu_mrl_implemented == HP_FALSE || + query.x.lu_mrl_opened == HP_FALSE ) && + query.x.lu_power_fault == HP_FALSE ) { + query.x.lu_request_failed = HP_FALSE; + } + else { + query.x.lu_request_failed = HP_TRUE; + } + } + else { + query.x.lu_request_failed = HP_TRUE; + } + + // + // translate the slot info to PCI HOTPLUG CORE values + // + translate_slot_info (hotplug_slot->info, &query); + + retval = ( query.x.lu_request_failed == HP_TRUE ) ? 0 : -1; + return retval; +} + + +static int disable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + struct shpc_context *shpc_context; + union SLOT_STATUS_INFO query; + long status; + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + /* + * disable the specified slot + */ + shpc_context = ( struct shpc_context * )slot->private; + status = hp_StartAsyncRequest(shpc_context, slot->number, + SHPC_ASYNC_DISABLE_SLOT, 0, slot ); + + // + // pretend async request was completed (we're not queuing slot requests) + // + hp_QuerySlotStatus( shpc_context, slot->number, &query ); + if( status == STATUS_SUCCESS ) { + query.x.lu_slot_state = SLOT_DISABLE; + query.x.lu_pi_state = INDICATOR_BLINK; + query.x.lu_request_failed = HP_FALSE; + } + else { + query.x.lu_request_failed = HP_TRUE; + } + + // + // translate the slot info to CORE values + // + translate_slot_info (hotplug_slot->info, &query); + + retval = ( query.x.lu_request_failed == HP_TRUE ) ? 0 : -1; + return retval; +} + +static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + struct shpc_context *shpc_context; + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg (" %s - physical_slot = %s state = %d",__FUNCTION__, hotplug_slot->name, status); + + /* + * turn light on/off + */ + shpc_context = (struct shpc_context *)slot->private; + + status = hp_StartAsyncRequest(shpc_context, slot->number, + ((status == CORE_INDICATOR_OFF) ? SHPC_ASYNC_LED_NORMAL : SHPC_ASYNC_LED_LOCATE), 10, slot); + hotplug_slot->info->attention_status = status; + + return retval; +} + +static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + struct shpc_context *shpc_context; + union SLOT_STATUS_INFO query; + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name); + + /* + * get the current power status of the specific + * slot and store it in the *value location. + */ + shpc_context = (struct shpc_context *)slot->private; + hp_QuerySlotStatus(shpc_context, slot->number, &query); + translate_slot_info (hotplug_slot->info, &query); + *value = hotplug_slot->info->power_status; + + return retval; +} + +static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + struct shpc_context *shpc_context; + union SLOT_STATUS_INFO query; + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name); + + /* + * get the current attention status of the specific + * slot and store it in the *value location. + */ + shpc_context = (struct shpc_context *)slot->private; + hp_QuerySlotStatus(shpc_context, slot->number, &query); + translate_slot_info (hotplug_slot->info, &query); + *value = hotplug_slot->info->attention_status; + + return retval; +} + +static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + struct shpc_context *shpc_context; + union SLOT_STATUS_INFO query; + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name); + + /* + * get the current latch status of the specific + * slot and store it in the *value location. + */ + shpc_context = (struct shpc_context *)slot->private; + hp_QuerySlotStatus(shpc_context, slot->number, &query); + translate_slot_info (hotplug_slot->info, &query); + *value = hotplug_slot->info->latch_status; + + return retval; +} + +static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + struct shpc_context *shpc_context; + union SLOT_STATUS_INFO query; + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name); + + /* + * get the current adapter status of the specific + * slot and store it in the *value location. + */ + shpc_context = (struct shpc_context *)slot->private; + hp_QuerySlotStatus(shpc_context, slot->number, &query); + translate_slot_info (hotplug_slot->info, &query); + *value = hotplug_slot->info->adapter_status; + + return retval; +} + +static void translate_slot_info (struct hotplug_slot_info *info, + union SLOT_STATUS_INFO *query) +{ + // power indicator + if( query->x.lu_pi_state == INDICATOR_OFF ) { + info->power_status = CORE_INDICATOR_OFF; + } + else if( query->x.lu_pi_state == INDICATOR_ON ) { + info->power_status = CORE_INDICATOR_ON; + } + else { + info->power_status = CORE_INDICATOR_BLINK; + } + + // attention indicator + if( query->x.lu_ai_state == INDICATOR_OFF ) { + info->attention_status = CORE_INDICATOR_OFF; + } + else if( query->x.lu_ai_state == INDICATOR_ON ) { + info->attention_status = CORE_INDICATOR_ON; + } + else { + info->attention_status = CORE_INDICATOR_BLINK; + } + + // retention latch + if( query->x.lu_mrl_implemented == HP_TRUE && + query->x.lu_mrl_opened == HP_TRUE ) { + info->latch_status = CORE_LATCH_OPENED; + } + else { + info->latch_status = CORE_LATCH_CLOSED; + } + + // adapter status + if( query->x.lu_slot_state == SLOT_ENABLE ) { + info->adapter_status = CORE_SLOT_ENABLED; + } + else { + info->adapter_status = CORE_SLOT_DISABLED; + } +} + +static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + err ("No hardware tests are defined for this driver"); + retval = -ENODEV; + + /* Or you can specify a test if you want to */ + /* AMD driver does not have a test */ + return retval; +} + +#define SLOT_NAME_SIZE 10 +static void make_slot_name (struct slot *slot) +{ + unsigned long slot_psn; + struct shpc_context *shpc_context; + + shpc_context = ( struct shpc_context * )slot->private; + + // + // Get physical slot number + // + hp_Queryslot_psn(shpc_context, slot->number, &slot_psn); + + snprintf (slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", (char)slot_psn); +} + +static int init_slots (struct controller *ctrl, int num_slots) +{ + struct slot *slot; + struct hotplug_slot *hotplug_slot; + struct hotplug_slot_info *info; + char *name; + int retval = 0; + int i; + u8 value; + + /* + * Create a structure for each slot, and register that slot + * with the pci_hotplug subsystem. + */ + for (i = 0; i < num_slots; ++i) { + slot = kmalloc (sizeof (struct slot), GFP_KERNEL); + if (!slot) + return -ENOMEM; + memset(slot, 0, sizeof(struct slot)); + + hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); + if (!hotplug_slot) { + kfree (slot); + return -ENOMEM; + } + memset(hotplug_slot, 0, sizeof (struct hotplug_slot)); + slot->hotplug_slot = hotplug_slot; + + info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!info) { + kfree (hotplug_slot); + kfree (slot); + return -ENOMEM; + } + memset(info, 0, sizeof (struct hotplug_slot_info)); + hotplug_slot->info = info; + + name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL); + if (!name) { + kfree (info); + kfree (hotplug_slot); + kfree (slot); + return -ENOMEM; + } + hotplug_slot->name = name; + + slot->magic = SLOT_MAGIC; + slot->number = i; + slot->private = (void*) ctrl->shpc_context; + + hotplug_slot->private = slot; + make_slot_name (slot); + hotplug_slot->ops = &skel_hotplug_slot_ops; + + /* + * Initilize the slot info structure with some known + * good values. + */ + get_power_status(hotplug_slot, &value); + info->power_status = value; + get_attention_status(hotplug_slot, &value); + info->attention_status = value; + get_latch_status(hotplug_slot, &value); + info->latch_status = value; + get_adapter_status(hotplug_slot, &value); + info->adapter_status = value; + + 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); + kfree (info); + kfree (name); + kfree (hotplug_slot); + kfree (slot); + return retval; + } + + /* add slot to our internal list */ + list_add (&slot->slot_list, &slot_list); + } + + return retval; +} + +static int amdshpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int retval; + int loop; + u16 vendor_id; + u16 device_id; + u32 rc; + long status = STATUS_SUCCESS; + struct controller *ctrl; + struct shpc_context *shpc_context; + union SLOT_CONFIG_INFO slot_config; + + rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id); + dbg( "%s-->Vendor ID: %x\n",__FUNCTION__, vendor_id); + if (rc || (vendor_id != PCI_VENDOR_ID_AMD)) { + err(msg_HPC_non_amd); + return -ENODEV; + } + + rc = pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id); + dbg( "%s-->Device ID: %x\n",__FUNCTION__, device_id); + if (rc || (device_id != PCI_DEVICE_ID_AMD_GOLAM_7450)) { + err(msg_HPC_not_amd_hp); + return -ENODEV; + } + + if (vendor_id == PCI_VENDOR_ID_AMD) { + + shpc_context = (struct shpc_context *)kmalloc(sizeof(struct shpc_context), GFP_KERNEL); + if (!shpc_context) { + err("%s : out of memory\n",__FUNCTION__); + return -ENOMEM; + } + memset(shpc_context, 0, sizeof(struct shpc_context)); + + ctrl = (struct controller *)kmalloc(sizeof(struct controller), GFP_KERNEL); + if (!ctrl) { + err("%s : out of memory\n", __FUNCTION__); + rc = -ENOMEM; + goto err_free_shpc_context; + } + memset(ctrl, 0, sizeof(struct controller)); + + /* Set Vendor ID, so it can be accessed later from other functions */ + ctrl->vendor_id = vendor_id; + + } else { + err(msg_HPC_not_supported); + return -ENODEV; + } + + ctrl->shpc_context = shpc_context; + ctrl->pci_dev = pdev; + ctrl->interrupt = pdev->irq; + ctrl->device = PCI_SLOT(pdev->devfn); + ctrl->function = PCI_FUNC(pdev->devfn); + + // + // the AMD hotplug bus is behind a bridge + // +// ctrl->pci_ops = pdev->subordinate->ops; + ctrl->pci_bus = pdev->subordinate; + ctrl->bus = pdev->subordinate->number; + + dbg( "%s-->bus = %d device = %d function = %d\n",__FUNCTION__, ctrl->bus, ctrl->device, ctrl->function); + + info("Found PCI hot plug controller on bus %d\n", pdev->bus->number); + info("Checking if MMIO region available for this HP controller...\n"); + + // + // Get memory mapped I/O region + // + dbg( "%s-->pdev = %p\n",__FUNCTION__, pdev); + dbg("%s -->pci resource start %lx\n",__FUNCTION__, pci_resource_start(pdev, 0)); + dbg("%s -->pci resource len %lx\n",__FUNCTION__, pci_resource_len (pdev, 0)); + if (!request_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0), MY_NAME)) { + err("MMIO region not available, skipping\n"); + rc = -ENOMEM; + goto err_free_ctrl; + } + + // + // Get linear address to put in controller structure + // + shpc_context->mmio_base_addr = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); + if (!shpc_context->mmio_base_addr) { + err("cannot remap MMIO region %lx @ %lx\n", pci_resource_len(pdev, 0), pci_resource_start(pdev, 0)); + rc = -ENODEV; + goto err_free_mem_region; + } + + dbg("%s -->shpc_context->mmio_base_addr = %p",__FUNCTION__, (unsigned long*)shpc_context->mmio_base_addr); + + hp_AddDevice(shpc_context, ctrl, shpc_context->async_callback, shpc_instance++); + + // Initialize controller + shpc_context->interrupt = pdev->irq; + dbg("%s -->shpc_context->interrupt = %d", __FUNCTION__,pdev->irq); + if (!hp_StartDevice(shpc_context)){ + rc = -ENODEV; + goto err_iounmap; + } + + // + // initialize this array only once + // + if (shpc_context->shpc_instance == 0 ) { + dbg("%s Initialize slot lists\n",__FUNCTION__); + for (loop = 0; loop < 256; loop++) { + amdshpc_slot_list[loop] = NULL; + } + } + + if (!amdshpc_ctrl_list) { + amdshpc_ctrl_list = shpc_context; + shpc_context->next = NULL; + } else { + amdshpc_ctrl_list->next = shpc_context; + shpc_context->next = NULL; + } + + if (!ctrl_list) { + ctrl_list = ctrl; + ctrl->next = NULL; + } else { + ctrl_list->next = ctrl; + ctrl->next = NULL; + } + + // Map rom address so we can get the HPRT table + amdshpc_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN); + if (!amdshpc_rom_start) { + err ("Could not ioremap memory region for ROM\n"); + retval = -EIO;; + iounmap(amdshpc_rom_start); + return retval; + } + + //************************************************** + // + // Save configuration headers for this and + // subordinate PCI buses + // + //************************************************** + + // find the physical slot number of the first hot plug slot + status = hp_QuerySlots(shpc_context, &slot_config); + // first slot on a bridged bus is always #1 + ctrl->first_slot = 1; + dbg("%s hp_QuerySlots: first_slot = %d, FDN = %d PSN_UP = %d\n",__FUNCTION__, + ctrl->first_slot, slot_config.x.lu_base_FDN, slot_config.x.lu_PSN_up); + + if (rc) { + err(msg_initialization_err, rc); + goto err_iounmap; + } + + if (!status) { + err(msg_initialization_err, (int)status); + goto err_iounmap; + } + + // Store PCI Config Space for all devices on this bus + rc = amdshpc_save_config(ctrl, ctrl->bus, &slot_config); + if (rc) { + err("%s: unable to save PCI configuration data, error %d",__FUNCTION__, rc); + goto err_iounmap; + } + + // + // Get IO, memory, and IRQ resources for new PCI devices + // + rc = amdshpc_find_available_resources(ctrl, amdshpc_rom_start); + if (rc) { + dbg("%s -->amdshpc_find_available_resources = 0x%x\n",__FUNCTION__, rc); + err("unable to locate PCI configuration resources for hot plug.\n"); + goto err_iounmap; + } + + // + // set global variable num_slots + // + num_slots = shpc_context->number_of_slots; + + dbg("%s about to call init_slots()",__FUNCTION__); + rc = init_slots(ctrl, num_slots); + if (rc){ + goto err_iounmap; + } + + return 0; + +err_iounmap: + iounmap((void *)shpc_context->mmio_base_addr); +err_free_mem_region: + release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); +err_free_shpc_context: + kfree(shpc_context); +err_free_ctrl: + kfree(ctrl); + return rc; +} + +static void cleanup_slots (void) +{ + struct list_head *tmp; + struct slot *slot; + + /* + * Unregister all of our slots with the pci_hotplug subsystem, + * and free up all memory that we had allocated. + */ + list_for_each (tmp, &slot_list) { + slot = list_entry (tmp, struct slot, slot_list); + list_del (&slot->slot_list); + pci_hp_deregister (slot->hotplug_slot); + kfree (slot->hotplug_slot->info); + kfree (slot->hotplug_slot->name); + kfree (slot->hotplug_slot); + kfree (slot); + } + + return; +} +static void unload_amdshpc(void) +{ + struct pci_func *next; + struct pci_func *TempSlot; + int loop; + struct shpc_context *shpc_context; + struct shpc_context *tshpc_context; + struct controller *ctrl; + struct controller *tctrl; + struct pci_resource *res; + struct pci_resource *tres; + + ctrl = ctrl_list; + + while (ctrl) { + //reclaim PCI mem + release_mem_region(pci_resource_start(ctrl->pci_dev, 0), + pci_resource_len(ctrl->pci_dev, 0)); + + res = ctrl->io_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->p_mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->bus_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + tctrl = ctrl; + ctrl = ctrl->next; + kfree(tctrl); + } + + for (loop = 0; loop < 256; loop++) { + next = amdshpc_slot_list[loop]; + while (next != NULL) { + res = next->io_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->p_mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->bus_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + TempSlot = next; + next = next->next; + kfree(TempSlot); + } + } + + shpc_context = amdshpc_ctrl_list; + + while(shpc_context){ + + dbg("%s -->shpc_context = %p",__FUNCTION__ , shpc_context); + dbg("%s -->kill_amdshpc() instance = %d", __FUNCTION__ ,shpc_context->shpc_instance); + hp_StopDevice(shpc_context); + + //Free IRQ associated with hot plug device + free_irq(shpc_context->interrupt, shpc_context); + + //Unmap the memory + iounmap(shpc_context->mmio_base_addr); + + // free the controller memory + tshpc_context = shpc_context; + shpc_context = shpc_context->next; + kfree(tshpc_context); + } + + //unmap the rom address + if (amdshpc_rom_start) + iounmap(amdshpc_rom_start); +} + + +static struct pci_device_id hpcd_pci_tbl[] __devinitdata = { + { + /* handle AMD Standard Hotplug controller */ + +// class: ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00), + class: ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), + class_mask: ~0, + + /* AMD makes it */ + vendor: PCI_VENDOR_ID_AMD, + device: PCI_DEVICE_ID_AMD_GOLAM_7450, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + + }, { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl); + + + +static struct pci_driver amdshpc_driver = { + name: "pci_hotplug", + id_table: hpcd_pci_tbl, + probe: amdshpc_probe, + /* remove: amdshpc_remove_one, */ +}; + + +static int __init amdshpc_init(void) +{ + int result; + + amdshpc_debug = debug; + /* + * Do specific initialization stuff for your driver here + * Like initilizing your controller hardware (if any) and + * determining the number of slots you have in the system + * right now. + */ + + result = pci_module_init(&amdshpc_driver); + dbg("%s -->pci_module_init = %d\n",__FUNCTION__ , result); + if (result) + return result; + + + info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); + return 0; +} + +static void __exit amdshpc_exit(void) +{ + // + // Clean everything up. + // + dbg("%s -->unload_amdshpc()\n",__FUNCTION__ ); + unload_amdshpc(); + + cleanup_slots(); + + dbg("%s -->pci_unregister_driver\n",__FUNCTION__ ); + pci_unregister_driver(&amdshpc_driver); + +} + +module_init(amdshpc_init); +module_exit(amdshpc_exit); --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc_ctrl.c Tue Jun 24 13:31:13 2003 @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2003 IBM Corp. + * Copyright (c) 2002-2003 Advanced Micro Devices + * + * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS + * AND CONDITIONS OF THE GNU GENERAL PUBLIC + * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS + * INCLUDED WITH THIS FILE AND POSTED AT + * http://www.gnu.org/licenses/gpl.html + * + * + * Send feedback to + * + */ + +#include +#include +#include +#include "amdshpc_ddi.h" +#include "amdshpc.h" + + +// **************************************************************************** +// +// hp_slot_thread() @ PASSIVE_LEVEL +// +// **************************************************************************** +int hp_slot_thread(void* ptr) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + struct shpc_context* shpc_context; + struct slot_context* slot_context; + union SLOT_STATUS_INFO slot_status; + + lock_kernel (); + daemonize ("amdshpc_slot"); + reparent_to_init (); + unlock_kernel (); + + slot_context = (struct slot_context*) ptr; + shpc_context = (struct shpc_context*) slot_context->shpc_context; + + // + // Insertion/Removal State Machine (loops until requested to exit) + // + do { + status = slot_context->slot_function( shpc_context, slot_context ); + // + // Suspend? + // + if(!status) { + spin_lock_irqsave(&shpc_context->shpc_spinlock, old_irq_flags); + if(shpc_context->shpc_event_bits & SUSPEND_EVENT ) { + status = STATUS_SUCCESS; + } + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + if(status) { + dbg( "%s-->SUSPEND: slot_id[ %d:%d ]",__FUNCTION__, + (int)shpc_context->shpc_instance, slot_context->slot_number-1 ); + + do { + interruptible_sleep_on(&slot_context->slot_event); + }while(!((shpc_context->shpc_event_bits & RESUME_EVENT) || + (shpc_context->shpc_event_bits & REMOVE_EVENT))); + + if(shpc_context->shpc_event_bits & REMOVE_EVENT ) { + status = STATUS_UNSUCCESSFUL; + } + else { + dbg("%s-->RESUME: slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + } + } + } + } while(status); + + // + // We're exiting, most likely due to an exit_request_event. So, let's cleanup! + // + dbg("%s-->Slot Thread Termination: slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Pending SW-initiated slot request? + // + if(slot_context->slot_event_bits & SLOT_REQUEST_EVENT ) { + // + // Complete it with failure code + // + hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status ); + slot_status.x.lu_request_failed = HP_TRUE; + shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + slot_context->slot_request.type, + slot_status, + slot_context->slot_request.request_context ); + + // + // Signal registered user EVENT + // + hp_signal_user_event( shpc_context ); + } + return(status); +} + + +// **************************************************************************** +// +// hp_attn_led_thread() @ PASSIVE_LEVEL +// +// **************************************************************************** +int +hp_attn_led_thread( + void* ptr +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + struct shpc_context* shpc_context; + struct slot_context* slot_context; + union SLOT_STATUS_INFO slot_status; + + lock_kernel (); + daemonize ("amdshpc_led"); + reparent_to_init (); + unlock_kernel (); + + slot_context = (struct slot_context*) ptr; + shpc_context = (struct shpc_context*) slot_context->shpc_context; + + // + // Attention LED State Machine (loops until requested to exit) + // + do { + status = slot_context->attn_led_function(shpc_context, slot_context); + // + // Suspend? + // + if(!status) { + spin_lock_irqsave(&shpc_context->shpc_spinlock, old_irq_flags); + if(shpc_context->shpc_event_bits & SUSPEND_EVENT ) { + status = STATUS_SUCCESS; + } + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + if(status) { + dbg("%s-->SUSPEND: slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + + do { + interruptible_sleep_on(&slot_context->slot_event); + }while(!((shpc_context->shpc_event_bits & RESUME_EVENT) || + (shpc_context->shpc_event_bits & REMOVE_EVENT))); + + if(shpc_context->shpc_event_bits & REMOVE_EVENT ) { + status = STATUS_UNSUCCESSFUL; + } + else { + dbg("%s-->RESUME: slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + } + } + } + } while(status); + + // + // We're exiting, most likely due to an exit_request_event. So, let's cleanup! + // + dbg("%s-->LED Thread Termination: slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Pending SW-initiated AttnLED request? + // + if(slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT ) { + // + // Complete it with failure code + // + hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status ); + slot_status.x.lu_request_failed = HP_TRUE; + shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + slot_context->attn_led_request.type, + slot_status, + slot_context->attn_led_request.request_context ); + + // + // Signal registered user EVENT + // + hp_signal_user_event( shpc_context ); + } + return(status); +} + + +// **************************************************************************** +// +// hp_get_slot_configuration() @ Any IRQL +// +// **************************************************************************** +void +hp_get_slot_configuration( + struct shpc_context* shpc_context +) +{ + struct slot_context* slot_context; + union SHPC_SLOTS_AVAILABLE1_DWREG SlotAvail1Reg; + union SHPC_SLOTS_AVAILABLE2_DWREG SlotAvail2Reg; + union SHPC_SLOT_CONFIG_DWREG SlotConfigReg; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + enum shpc_speed_mode max_speed_mode; + u8 i; + + // + // Get max number of slots available + // + SlotAvail1Reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOTS_AVAILABLE1_REG_OFFSET); + SlotAvail2Reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOTS_AVAILABLE2_REG_OFFSET); + // + // Get slot configuration + // + SlotConfigReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOT_CONFIG_REG_OFFSET); + + // + // Get number of available slots per speed/mode + // + shpc_context->slots_enabled = 0; + shpc_context->number_of_slots = 0; + if( SlotAvail1Reg.x.N_133PCIX ) { + shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_133PCIX; + shpc_context->max_speed_mode = SHPC_BUS_PCIX_133; + } + else if( SlotAvail1Reg.x.N_100PCIX ) { + shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_100PCIX; + shpc_context->max_speed_mode = SHPC_BUS_PCIX_100; + } + else if( SlotAvail1Reg.x.N_66PCIX ) { + shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_66PCIX; + shpc_context->max_speed_mode = SHPC_BUS_PCIX_66; + } + else if( SlotAvail2Reg.x.N_66CONV ) { + shpc_context->number_of_slots = ( u8 )SlotAvail2Reg.x.N_66CONV; + shpc_context->max_speed_mode = SHPC_BUS_CONV_66; + } + else if( SlotAvail1Reg.x.N_33CONV ) { + shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_33CONV; + shpc_context->max_speed_mode = SHPC_BUS_CONV_33; + } + + if( shpc_context->number_of_slots ) { + // + // Be sure NSI field is not exceeded (this should not happen!) + // + if( shpc_context->number_of_slots > SlotConfigReg.x.NSI ) { + shpc_context->number_of_slots = ( u8 )SlotConfigReg.x.NSI; + } + + // + // Limit slot count to what we're prepared to support + // + if( shpc_context->number_of_slots > SHPC_MAX_NUM_SLOTS ) { + shpc_context->number_of_slots = SHPC_MAX_NUM_SLOTS; + } + + // + // Get current Bus speed/mode + // + shpc_context->bus_speed_mode = hp_get_bus_speed_mode( shpc_context ); + + // + // Initialize slot state based on HW disposition + // + for( i=0; i< shpc_context->number_of_slots; ++i ) { + slot_context = &shpc_context->slot_context[ i ]; + + // + // Get Physical Slot Number (PSN-based) + // + if( SlotConfigReg.x.PSN_UP ) { + slot_context->slot_psn = ( u8 )SlotConfigReg.x.PSN + i; + } + else { + slot_context->slot_psn = ( u8 )SlotConfigReg.x.PSN - i; + } + + // + // Assign Logical Slot Number (1-based) + // + slot_context->slot_number = ( u8 )i+1; + + // + // Get Card's speed/mode capabilities + // + hp_get_card_speed_mode( slot_context ); + + // + // Check current HW state + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + + // + // Already enabled: Card Present, MRL closed, Slot Enabled, No Power-Fault? + // + if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY && + ( logical_slot_reg.x.MRLS_IM == SHPC_MASKED || + logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) && + logical_slot_reg.x.PF == SHPC_STATUS_CLEARED && + logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT ) { + // + // Treat it as a SUCCESSFUL "Slot Enabled" HW-initiated request + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT; + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.request_context = NULL; + slot_context->slot_completion.done = TRUE; + + // + // Flag as "Slot Enabled" + // + ++shpc_context->slots_enabled; + slot_context->slot_enabled = TRUE; + slot_context->in_bus_speed_mode_contention = TRUE; + if( logical_slot_reg.x.PIS == SHPC_LED_ON ) { + slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_at_slot_enabled_wait_for_slot_request; + } + else { + slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_to_slot_enabled_wait_for_led_cmd_available; + } + } + else { + // + // Treat it as a SUCCESSFUL "Slot Disable" HW-initiated request + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_DISABLE_SLOT; + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.request_context = NULL; + slot_context->slot_completion.done = TRUE; + + // + // Flag as "Slot Disabled" + // + slot_context->slot_enabled = FALSE; + slot_context->in_bus_speed_mode_contention = FALSE; + if( logical_slot_reg.x.S_STATE == SHPC_DISABLE_SLOT ) { + slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_at_slot_disabled_wait_for_slot_request; + } + else { + slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_to_slot_disabled_wait_for_disable_cmd_available; + } + } + + // + // Set Attention LED function + // + if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) { + // + // Turn it ON + // + slot_context->problem_detected = TRUE; + slot_context->attn_led_function = (SLOT_STATE_FUNCTION) hp_wait_for_attn_led_back_to_normal_cmd_available; + } + else { + // + // Make sure it is turned OFF + // + slot_context->problem_detected = FALSE; + if( logical_slot_reg.x.AIS == SHPC_LED_OFF ) { + slot_context->attn_led_function = (SLOT_STATE_FUNCTION) hp_wait_for_attn_led_request; + } + else { + slot_context->attn_led_function = (SLOT_STATE_FUNCTION) hp_wait_for_attn_led_back_to_normal_cmd_available; + } + } + } + + // + // Enabled slots running at maximum speed/mode? + // + if( shpc_context->slots_enabled ) { + max_speed_mode = hp_get_max_speed_mode( shpc_context, shpc_context->max_speed_mode ); + + // + // Signal enabled slots to release the bus, then change bus speed/mode + // + if( shpc_context->bus_speed_mode != max_speed_mode ) { + hp_signal_enabled_slots_to_rebalance_bus( shpc_context ); + } + } + } +} + + +// **************************************************************************** +// +// hp_enable_slot_interrupts() @ Any IRQL +// +// **************************************************************************** +void +hp_enable_slot_interrupts( + struct slot_context* slot_context +) +{ + struct shpc_context* shpc_context = ( struct shpc_context* )slot_context->shpc_context; + union SHPC_SLOT_CONFIG_DWREG SlotConfigReg; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + // + // Get HW implementation: Attention Button, MRL Sensor + // + SlotConfigReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOT_CONFIG_REG_OFFSET); + + // + // Attention Button: Enabled only if implemented + // + logical_slot_reg.x.AB_IM = ( SlotConfigReg.x.ABI == SHPC_STATUS_SET ) ? + SHPC_UNMASKED : SHPC_MASKED; + logical_slot_reg.x.ABP_STS = SHPC_STATUS_SET; + + // + // MRL Sensor: Enabled only if implemented (System Error Disabled) + // + logical_slot_reg.x.MRLS_IM = ( SlotConfigReg.x.MRLSI == SHPC_STATUS_SET ) ? + SHPC_UNMASKED : SHPC_MASKED; + logical_slot_reg.x.MRLS_SERRM = SHPC_MASKED; + logical_slot_reg.x.MRLSC_STS = SHPC_STATUS_SET; + + // + // Card Presence: Enabled + // + logical_slot_reg.x.CP_IM = SHPC_UNMASKED; + logical_slot_reg.x.CPC_STS = SHPC_STATUS_SET; + + // + // Isolated Power-Fault: Enabled + // + logical_slot_reg.x.IPF_IM = SHPC_UNMASKED; + logical_slot_reg.x.IPF_STS = SHPC_STATUS_SET; + + // + // Connected Power-Fault: Enabled (System Error Disabled) + // + logical_slot_reg.x.CPF_IM = SHPC_UNMASKED; + logical_slot_reg.x.CPF_SERRM = SHPC_MASKED; + logical_slot_reg.x.CPF_STS = SHPC_STATUS_SET; + + // + // Update Mask and Status bits + // + writel(logical_slot_reg.AsDWord, slot_context->logical_slot_addr); +} + + +// **************************************************************************** +// +// hp_disable_slot_interrupts() @ Any IRQL +// +// **************************************************************************** +void +hp_disable_slot_interrupts( + struct slot_context* slot_context +) +{ + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + // + // Get HW implementation: Attention Button, MRL Sensor + // + logical_slot_reg.AsDWord = readl(slot_context->logical_slot_addr); + + // + // Attention Button: Disabled + // + logical_slot_reg.x.AB_IM = SHPC_MASKED; + logical_slot_reg.x.ABP_STS = SHPC_STATUS_SET; + + // + // MRL Sensor: Disabled + // + logical_slot_reg.x.MRLS_IM = SHPC_MASKED; + logical_slot_reg.x.MRLS_SERRM = SHPC_MASKED; + logical_slot_reg.x.MRLSC_STS = SHPC_STATUS_SET; + + // + // Card Presence: Disabled + // + logical_slot_reg.x.CP_IM = SHPC_MASKED; + logical_slot_reg.x.CPC_STS = SHPC_STATUS_SET; + + // + // Isolated Power-Fault: Disabled + // + logical_slot_reg.x.IPF_IM = SHPC_MASKED; + logical_slot_reg.x.IPF_STS = SHPC_STATUS_SET; + + // + // Connected Power-Fault: Enabled (System Error Disabled) + // + logical_slot_reg.x.CPF_IM = SHPC_MASKED; + logical_slot_reg.x.CPF_SERRM = SHPC_MASKED; + logical_slot_reg.x.CPF_STS = SHPC_STATUS_SET; + + // + // Update Mask and Status bits + // + writel(logical_slot_reg.AsDWord, slot_context->logical_slot_addr); +} + + +// **************************************************************************** +// +// hp_enable_global_interrupts() @ Any IRQL +// +// **************************************************************************** +void +hp_enable_global_interrupts( + struct shpc_context* shpc_context +) +{ + union SHPC_SERR_INT_DWREG SerrIntReg; + + SerrIntReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET); + + // + // Arbiter timeout: System Error Disabled + // + SerrIntReg.x.A_SERRM = SHPC_MASKED; + SerrIntReg.x.ATOUT_STS = SHPC_STATUS_SET; + + // + // Command Completion: Enabled + // + SerrIntReg.x.CC_IM = SHPC_UNMASKED; + SerrIntReg.x.CC_STS = SHPC_STATUS_SET; + + // + // Global: Interrputs Enabled, System Error Disabled + // + SerrIntReg.x.GIM = SHPC_UNMASKED; + SerrIntReg.x.GSERRM = SHPC_MASKED; + + // + // Update Mask and Status bits + // + writel(SerrIntReg.AsDWord, shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET); +} + + +// **************************************************************************** +// +// hp_disable_global_interrupts() @ Any IRQL +// +// **************************************************************************** +void +hp_disable_global_interrupts( + struct shpc_context* shpc_context +) +{ + union SHPC_SERR_INT_DWREG SerrIntReg; + + SerrIntReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET); + // + // Arbiter timeout: System Error Disabled + // + SerrIntReg.x.A_SERRM = SHPC_MASKED; + SerrIntReg.x.ATOUT_STS = SHPC_STATUS_SET; + + // + // Command Completion: Disabled + // + SerrIntReg.x.CC_IM = SHPC_MASKED; + SerrIntReg.x.CC_STS = SHPC_STATUS_SET; + + // + // Global: Interrputs Disabled, System Error Disabled + // + SerrIntReg.x.GIM = SHPC_MASKED; + SerrIntReg.x.GSERRM = SHPC_MASKED; + + // + // Update Mask and Status bits + // + writel(SerrIntReg.AsDWord, shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET); +} + + +// **************************************************************************** +// +// hp_get_card_speed_mode() @ Any IRQL +// +// **************************************************************************** +enum shpc_speed_mode +hp_get_card_speed_mode( + struct slot_context* slot_context +) +{ + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + // + // Slot powered-up? + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + if(( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) && + ( logical_slot_reg.x.S_STATE == SHPC_POWER_ONLY || + logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT )) { + // + // Get Card's maximum speed/mode + // + if( logical_slot_reg.x.PCIX_CAP == SHPC_SLOT_PCIX_133 ) { + slot_context->card_speed_mode = SHPC_BUS_PCIX_133; + } + else if( logical_slot_reg.x.PCIX_CAP == SHPC_SLOT_PCIX_66 ) { + slot_context->card_speed_mode = SHPC_BUS_PCIX_66; + } + else if( logical_slot_reg.x.M66_CAP == SHPC_STATUS_SET ) { + slot_context->card_speed_mode = SHPC_BUS_CONV_66; + } + else { + slot_context->card_speed_mode = SHPC_BUS_CONV_33; + } + + // + // Get Card's PCI-66 capability + // + if( logical_slot_reg.x.M66_CAP == SHPC_STATUS_SET ) { + slot_context->card_pci66_capable = TRUE; + } + } + else { + // + // Slot is not powered-up, use PCI-33 as default + // + slot_context->card_speed_mode = SHPC_BUS_CONV_33; + slot_context->card_pci66_capable = FALSE; + } + + return slot_context->card_speed_mode; +} + +// **************************************************************************** +// +// hp_get_bus_speed_mode() @ Any IRQL +// +// **************************************************************************** +enum shpc_speed_mode +hp_get_bus_speed_mode( + struct shpc_context* shpc_context +) +{ + union SHPC_SEC_BUS_CONFIG_DWREG bus_config_reg; + enum shpc_speed_mode bus_speed_mode; + + bus_config_reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SEC_BUS_CONFIG_REG_OFFSET); + + bus_speed_mode = ( enum shpc_speed_mode )bus_config_reg.x.MODE; + if( bus_speed_mode > SHPC_BUS_PCIX_133 ) { + bus_speed_mode = SHPC_BUS_CONV_33; + } + + return bus_speed_mode; +} + + +// **************************************************************************** +// +// hp_translate_speed_mode() @ Any IRQL +// +// **************************************************************************** +enum mode_frequency +hp_translate_speed_mode( + enum shpc_speed_mode shpc_speed_mode +) +{ + enum mode_frequency translated_speed_mode; + + switch( shpc_speed_mode ) { + case SHPC_BUS_PCIX_133: + translated_speed_mode = MODE_PCIX_133; + break; + + case SHPC_BUS_PCIX_100: + translated_speed_mode = MODE_PCIX_100; + break; + + case SHPC_BUS_PCIX_66: + translated_speed_mode = MODE_PCIX_66; + break; + + case SHPC_BUS_CONV_66: + translated_speed_mode = MODE_PCI_66; + break; + + case SHPC_BUS_CONV_33: + default: + translated_speed_mode = MODE_PCI_33; + break; + } + + return translated_speed_mode; +} + + +// **************************************************************************** +// +// hp_translate_card_power() @ Any IRQL +// +// **************************************************************************** +enum hp_power_requirements +hp_translate_card_power( + enum shpc_card_power ShpcCardPower +) +{ + enum hp_states TranslatedCardPower; + + switch( ShpcCardPower ) { + case SHPC_CARD_PRESENT_25W: + TranslatedCardPower = POWER_HIGH; + break; + + case SHPC_CARD_PRESENT_15W: + TranslatedCardPower = POWER_MEDIUM; + break; + + case SHPC_CARD_PRESENT_7_5W: + default: + TranslatedCardPower = POWER_LOW; + break; + } + + return TranslatedCardPower; +} + + +// **************************************************************************** +// +// hp_translate_indicator() @ Any IRQL +// +// **************************************************************************** +enum hp_indicators +hp_translate_indicator( + enum shpc_slot_led ShpcIndicator +) +{ + enum hp_indicators TranslatedIndicator; + + switch( ShpcIndicator ) { + case SHPC_LED_ON: + TranslatedIndicator = INDICATOR_ON; + break; + + case SHPC_LED_BLINK: + TranslatedIndicator = INDICATOR_BLINK; + break; + + case SHPC_LED_OFF: + default: + TranslatedIndicator =INDICATOR_OFF; + break; + } + + return TranslatedIndicator; +} + + +// **************************************************************************** +// +// hp_flag_slot_as_enabled() @ <= DISPATCH_LEVEL +// +// **************************************************************************** +u8 +hp_flag_slot_as_enabled( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + u8 SlotFlagged = FALSE; + + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + if( !slot_context->slot_enabled ) { + // + // Slot just coming on-line + // + SlotFlagged = TRUE; + ++shpc_context->slots_enabled; + slot_context->slot_enabled = TRUE; + hp_clear_shpc_event_bit(shpc_context, BUS_REBALANCE_EVENT); + } + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + return SlotFlagged; +} + + +// **************************************************************************** +// +// hp_flag_slot_as_disabled() @ <= DISPATCH_LEVEL +// +// **************************************************************************** +u8 +hp_flag_slot_as_disabled( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + u8 SlotFlagged = FALSE; + + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + if( slot_context->slot_enabled ) { + if( --shpc_context->slots_enabled == 0 ) { + // + // This was the last enabled slot, signal waiting thread that bus is released, + // + shpc_context->bus_released = TRUE; + hp_send_event_to_all_slots(shpc_context, BUS_COMPLETE_EVENT); + } + SlotFlagged = TRUE; + slot_context->slot_enabled = FALSE; + } + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + return SlotFlagged; +} + + +// **************************************************************************** +// +// hp_signal_enabled_slots_to_rebalance_bus() @ <= DISPATCH_LEVEL +// +// Comments: +// Assumes shpc_spinlock is already held. +// +// **************************************************************************** +u8 +hp_signal_enabled_slots_to_rebalance_bus( + struct shpc_context* shpc_context +) +{ + struct slot_context* SlotArray[ SHPC_MAX_NUM_SLOTS ]; + struct slot_context* Slot; + u8 i, j, n; + + // + // Initialize array of slot pointers + // + n = shpc_context->number_of_slots; + for( i=0, j=0; islot_context[ i ]; + if( Slot->slot_enabled ) { + SlotArray[ j++ ] = Slot; + } + } + // + // Found slots enabled? + // + if( j ) { + // + // Bubble-sort enabled slots in order of increasing card speed/mode + // + n = j; + for( i=0; icard_speed_mode < SlotArray[ j ]->card_speed_mode ) { + Slot = SlotArray[ j ]; + SlotArray[ j ] = SlotArray[ j+1 ]; + SlotArray[ j+1 ] = Slot; + } + } + } + // + // Signal enabled slots in sorted order as an attempt to re-enable slower cards first + // + hp_set_shpc_event_bit(shpc_context, BUS_REBALANCE_EVENT); + for( i=0; islot_event); + } + return TRUE; + } + + return FALSE; +} + + +// **************************************************************************** +// +// hp_get_max_speed_mode() @ <= DISPATCH_LEVEL +// +// Comments: +// Assumes shpc_spinlock is already held. +// +// **************************************************************************** +enum shpc_speed_mode +hp_get_max_speed_mode( + struct shpc_context* shpc_context, + enum shpc_speed_mode From_speed_mode +) +{ + struct slot_context* slot_context; + enum shpc_speed_mode max_speed_mode; + u8 i; + + max_speed_mode = From_speed_mode; + for( i=0; i< shpc_context->number_of_slots; ++i ) { + slot_context = &shpc_context->slot_context[ i ]; + if( slot_context->in_bus_speed_mode_contention && + slot_context->card_speed_mode < max_speed_mode ) { + // + // Can only go as fast as the slowest card + // + max_speed_mode = slot_context->card_speed_mode; + } + } + + // + // Make sure all cards support conventional PCI-66 speed/mode + // + if( max_speed_mode == SHPC_BUS_CONV_66 ) { + for( i=0; i< shpc_context->number_of_slots; ++i ) { + slot_context = &shpc_context->slot_context[ i ]; + if( slot_context->in_bus_speed_mode_contention && + !slot_context->card_pci66_capable ) { + // + // Fall back to slower common denominator + // + max_speed_mode = SHPC_BUS_CONV_33; + } + } + } + + return max_speed_mode; +} + + +// **************************************************************************** +// +// hp_signal_user_event() @ <= DISPATCH_LEVEL +// +// **************************************************************************** +void hp_signal_user_event(struct shpc_context* shpc_context) +{ + unsigned long old_irq_flags; +return; + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + if( shpc_context->user_event_pointer ) { + wake_up_interruptible( shpc_context->user_event_pointer); + } + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); +} + + +// **************************************************************************** +// +// hp_signal_user_event_at_dpc_level() @ DISPATCH_LEVEL +// +// **************************************************************************** +void hp_signal_user_event_at_dpc_level(struct shpc_context* shpc_context) +{ +return; + spin_lock_bh( &shpc_context->shpc_spinlock ); + if( shpc_context->user_event_pointer ) { + wake_up_interruptible( shpc_context->user_event_pointer); + } + spin_unlock_bh( &shpc_context->shpc_spinlock ); +} --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc_ddi.c Tue Jun 24 13:31:13 2003 @@ -0,0 +1,1666 @@ +/* + * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2003 IBM Corp. + * Copyright (c) 2002-2003 Advanced Micro Devices + * + * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS + * AND CONDITIONS OF THE GNU GENERAL PUBLIC + * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS + * INCLUDED WITH THIS FILE AND POSTED AT + * http://www.gnu.org/licenses/gpl.html + * + * + * Send feedback to + * + */ + +#include +#include +#include +#include "amdshpc_ddi.h" +#include "amdshpc.h" + +static unsigned long async_callback (void* driver_context, + u8 slot_number, + enum shpc_async_request async_request, + union SLOT_STATUS_INFO slot_tatus, + void* request_context ); + +// **************************************************************************** +// +// hp_AddDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data (per hardware-instance). +// driver_context - Caller provided pointer to be returned upon completion. +// Callback - Caller provided function to be called upon completion of async requests. +// shpc_instance - Zero-based hardware instance. +// +// Return Value +// Status returned by any system calls made within hp_AddDevice(). +// +// **************************************************************************** +long +hp_AddDevice( + struct shpc_context* shpc_context, + void* driver_context, + SHPC_ASYNC_CALLBACK Callback, + unsigned long shpc_instance + ) + +{ + struct slot_context* slot_context; + u8 i; + DECLARE_TASKLET(mrl_sensor_dpc0, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[0] ); + DECLARE_TASKLET(attn_button_dpc0, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[0]); + DECLARE_TASKLET(card_presence_dpc0, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[0]); + DECLARE_TASKLET(isolated_power_fault_dpc0, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[0]); + DECLARE_TASKLET(connected_power_fault_dpc0, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[0]); + + DECLARE_TASKLET(mrl_sensor_dpc1, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[1] ); + DECLARE_TASKLET(attn_button_dpc1, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[1]); + DECLARE_TASKLET(card_presence_dpc1, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[1]); + DECLARE_TASKLET(isolated_power_fault_dpc1, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[1]); + DECLARE_TASKLET(connected_power_fault_dpc1, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[1]); + + DECLARE_TASKLET(mrl_sensor_dpc2, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[2] ); + DECLARE_TASKLET(attn_button_dpc2, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[2]); + DECLARE_TASKLET(card_presence_dpc2, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[2]); + DECLARE_TASKLET(isolated_power_fault_dpc2, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[2]); + DECLARE_TASKLET(connected_power_fault_dpc2, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[2]); + + DECLARE_TASKLET(mrl_sensor_dpc3, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[3] ); + DECLARE_TASKLET(attn_button_dpc3, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[3]); + DECLARE_TASKLET(card_presence_dpc3, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[3]); + DECLARE_TASKLET(isolated_power_fault_dpc3, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[3]); + DECLARE_TASKLET(connected_power_fault_dpc3, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[3]); + + + DECLARE_TASKLET(cmd_completion_dpc, hp_cmd_completion_dpc, (unsigned long) shpc_context ); + + // + // Init common resources + // + shpc_context->cmd_completion_dpc = cmd_completion_dpc; + shpc_context->driver_context = driver_context; + shpc_context->async_callback = (SHPC_ASYNC_CALLBACK)async_callback; + shpc_context->shpc_instance = shpc_instance; + shpc_context->slots_enabled = 0; + shpc_context->number_of_slots = 0; + shpc_context->at_power_device_d0 = FALSE; + shpc_context->bus_released = FALSE; + shpc_context->user_event_pointer = NULL; + spin_lock_init( &shpc_context->shpc_spinlock ); + sema_init( &shpc_context->cmd_available_mutex, 1); + sema_init( &shpc_context->bus_available_mutex, 1); + sema_init( &shpc_context->shpc_event_bits_semaphore, 1); + + shpc_context->shpc_event_bits=0; // all shpc events cleared + + dbg("%s -->HwInstance[ %d ]", __FUNCTION__ ,shpc_context->shpc_instance ); + + // + // Init slot resources + // + for( i=0; i< SHPC_MAX_NUM_SLOTS; ++i ) { + slot_context = &shpc_context->slot_context[ i ]; + slot_context->shpc_context = ( void* )shpc_context; + slot_context->slot_number = ( u8 )i+1; + slot_context->slot_enabled = FALSE; + slot_context->in_bus_speed_mode_contention = FALSE; + slot_context->problem_detected = FALSE; + slot_context->slot_quiesced = FALSE; + slot_context->slot_thread = NULL; + slot_context->slot_function = NULL; + slot_context->attn_led_thread = NULL; + slot_context->attn_led_function = NULL; + + // + // Slot SpinLocks and semaphores + // + spin_lock_init( &slot_context->slot_spinlock); + sema_init(&slot_context->slot_event_bits_semaphore, 1); + sema_init(&slot_context->cmd_acquire_mutex, 1); + sema_init(&slot_context->bus_acquire_mutex, 1); + + // + // Slot timers + // + init_timer(&slot_context->slot_timer1); + init_timer(&slot_context->slot_timer2); + init_timer(&slot_context->slot_timer3); + init_timer(&slot_context->slot_timer4); + init_timer(&slot_context->slot_timer5); + init_timer(&slot_context->slot_timer6); + init_timer(&slot_context->slot_timer7); + init_timer(&slot_context->slot_timer8); + init_timer(&slot_context->slot_timer9); + init_timer(&slot_context->slot_timer10); + init_timer(&slot_context->led_timer1); + init_timer(&slot_context->led_timer2); + init_timer(&slot_context->led_timer3); + init_timer(&slot_context->led_timer4); + + // + // Interrupt Service + // + switch (i) { + case 0: + slot_context->attn_button_dpc = attn_button_dpc0; + slot_context->mrl_sensor_dpc = mrl_sensor_dpc0; + slot_context->card_presence_dpc = card_presence_dpc0; + slot_context->isolated_power_fault_dpc = isolated_power_fault_dpc0; + slot_context->connected_power_fault_dpc = connected_power_fault_dpc0; + break; + case 1: + slot_context->attn_button_dpc = attn_button_dpc1; + slot_context->mrl_sensor_dpc = mrl_sensor_dpc1; + slot_context->card_presence_dpc = card_presence_dpc1; + slot_context->isolated_power_fault_dpc = isolated_power_fault_dpc1; + slot_context->connected_power_fault_dpc = connected_power_fault_dpc1; + break; + case 2: + slot_context->attn_button_dpc = attn_button_dpc2; + slot_context->mrl_sensor_dpc = mrl_sensor_dpc2; + slot_context->card_presence_dpc = card_presence_dpc2; + slot_context->isolated_power_fault_dpc = isolated_power_fault_dpc2; + slot_context->connected_power_fault_dpc = connected_power_fault_dpc2; + break; + case 3: + slot_context->attn_button_dpc = attn_button_dpc3; + slot_context->mrl_sensor_dpc = mrl_sensor_dpc3; + slot_context->card_presence_dpc = card_presence_dpc3; + slot_context->isolated_power_fault_dpc = isolated_power_fault_dpc3; + slot_context->connected_power_fault_dpc = connected_power_fault_dpc3; + break; + } + + + // + // Slot Events + // + slot_context->slot_event_bits=0; // all slot events cleared + + dbg("%s -->Init slot wait queues",__FUNCTION__ ); + + init_waitqueue_head(&slot_context->slot_event); + init_waitqueue_head(&slot_context->led_cmd_acquire_event); + init_waitqueue_head(&slot_context->led_cmd_release_event); + init_waitqueue_head(&slot_context->cmd_acquire_event); + init_waitqueue_head(&slot_context->cmd_release_event); + init_waitqueue_head(&slot_context->bus_acquire_event); + init_waitqueue_head(&slot_context->bus_release_event); + } + return STATUS_SUCCESS; +} + + +// **************************************************************************** +// +// hp_StartDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// +// Return Value +// Status returned by any system calls made within hp_StartDevice(). +// +// +// **************************************************************************** +long +hp_StartDevice( + struct shpc_context* shpc_context + ) +{ + struct slot_context* slot_context; + long status = STATUS_SUCCESS; + u32 *logical_slot_addr; + u8 i; + int pid; + + dbg("%s -->From hp_StartDevice: MmioBase[ %p ]",__FUNCTION__ , (unsigned long*)shpc_context->mmio_base_addr); + + // + // Disable Global Interrupts + // + dbg("%s -->hp_disable_global_interrupts( shpc_context=%p );",__FUNCTION__ , shpc_context); + hp_disable_global_interrupts( shpc_context ); + + // + // Reset common resources + // + shpc_context->at_power_device_d0 = TRUE; + shpc_context->bus_released = FALSE; + + // + // Reset slot resources + // + logical_slot_addr = shpc_context->mmio_base_addr + SHPC_LOGICAL_SLOT_REG_OFFSET; + for( i=0; i< SHPC_MAX_NUM_SLOTS; ++i ) { + slot_context = &shpc_context->slot_context[ i ]; + + // + // Assign Logical Slot Register Address + // + slot_context->logical_slot_addr = logical_slot_addr++; + + // + // Disable Slot Interrupts + // + dbg("%s -->hp_disable_slot_interrupts(slot_context)=%p",__FUNCTION__ , slot_context); + hp_disable_slot_interrupts(slot_context); + + // + // Reset slot flags and pointers + // + slot_context->slot_enabled = FALSE; + slot_context->in_bus_speed_mode_contention = FALSE; + slot_context->problem_detected = FALSE; + slot_context->slot_quiesced = FALSE; + slot_context->slot_thread = NULL; + slot_context->slot_function = NULL; + slot_context->attn_led_thread = NULL; + slot_context->attn_led_function = NULL; + slot_context->slot_occupied = 0; + } + + // + // Get initial slot configuration: number_of_slots, slots_enabled, SlotStateFunction + // + shpc_context->slots_enabled = 0; + shpc_context->number_of_slots = 0; + hp_get_slot_configuration( shpc_context ); + dbg("%s -->from hp_StartDevice() number_of_slots = %d", __FUNCTION__ ,shpc_context->number_of_slots); + if( shpc_context->number_of_slots == 0 ) { + status = STATUS_UNSUCCESSFUL; + } + + // + // Hook Interrupt + // + dbg("%s -->HPC interrupt = %d \n", __FUNCTION__ ,shpc_context->interrupt); + + if (request_irq(shpc_context->interrupt, hp_interrupt_service, SA_SHIRQ, MY_NAME, shpc_context)) { + err("Can't get irq %d for the PCI hotplug controller\n", shpc_context->interrupt); + status = STATUS_UNSUCCESSFUL; + return(status); + } + + // + // Set slot operation in motion + // + for( i=0; inumber_of_slots && status; ++i ) { + + slot_context = &shpc_context->slot_context[ i ]; + + // + // Launch slot command and bus completion mutex threads + // + // get led cmd available thread + pid = kernel_thread(hp_get_led_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND); + if (pid < 0) { + err ("Can't start up our get_led_cmd_available_mutex thread\n"); + status = STATUS_UNSUCCESSFUL; + } + dbg("%s -->Our hp_get_led_cmd_available_mutex thread pid = %d",__FUNCTION__ , pid); + + // get cmd available thread + pid = kernel_thread(hp_get_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND); + if (pid < 0) { + err ("Can't start up our get_cmd_available_mutex thread\n"); + status = STATUS_UNSUCCESSFUL; + } + dbg("%s -->Our hp_get_cmd_available_mutex thread pid = %d",__FUNCTION__ , pid); + + // get bus available thread + pid = kernel_thread(hp_get_bus_available_mutex_thread, slot_context, CLONE_SIGHAND); + if (pid < 0) { + err ("Can't start up our get_bus_available_mutex thread\n"); + status = STATUS_UNSUCCESSFUL; + } + dbg("%s \n\n\n-->Our get_bus_available_mutex thread pid = %d",__FUNCTION__ , pid); + + // + // Launch slot thread + // + pid = kernel_thread(hp_slot_thread, slot_context, CLONE_SIGHAND); + if (pid < 0) { + err ("Can't start up our event thread\n"); + status = STATUS_UNSUCCESSFUL; + } + dbg("%s -->Our slot event thread pid = %d\n",__FUNCTION__ , pid); + + // + // Launch Attention LED Thread + // + pid = kernel_thread(hp_attn_led_thread, slot_context, CLONE_SIGHAND); + if (pid < 0) { + err ("Can't start up our event thread\n"); + status = STATUS_UNSUCCESSFUL; + } + dbg("%s -->Our LED event thread pid = %d\n",__FUNCTION__ , pid); + + // + // Enable Slot Interrupts: Attn Button, MRL Sensor, Card Presence, Power-Fault + // + if(status) { + dbg("%s -->hpStartDevice() Enabling slot interrupts...",__FUNCTION__ ); + hp_enable_slot_interrupts( slot_context ); + } + } + + // + // Enable Global Interrupts: Command Completion + // + if(status) { + dbg("%s -->hpStartDevice() Enabling global interrupts...",__FUNCTION__ ); + hp_enable_global_interrupts( shpc_context ); + } else { + // + // Bail out, we're hosed! + // + hp_StopDevice( shpc_context ); + status = STATUS_UNSUCCESSFUL; + } + dbg("%s -->status = %d\n",__FUNCTION__ , (u32)status); + + return status; +} + + +// **************************************************************************** +// +// hp_StopDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// +// Return Value +// Status returned by any system calls made within hp_StopDevice(). +// +// Comments: +// The caller is responsible for unmapping mmio_base_addr, via MmUnmapIoSpace(), +// after calling hp_StopDevice() for resource re-balancing or device removal. +// +// **************************************************************************** +long +hp_StopDevice( + struct shpc_context* shpc_context + ) +{ + struct slot_context* slot_context; + long status = STATUS_SUCCESS; + unsigned long old_irq_flags; + u8 i; + + // + // Already stopped or never started ? + // + if( shpc_context->mmio_base_addr == 0 ) { + return STATUS_UNSUCCESSFUL; + } + // + // Disable Global Interrupts + // + hp_disable_global_interrupts( shpc_context ); + + // + // Signal EXIT request to slot threads + // + + spin_lock_irqsave(&shpc_context->shpc_spinlock, old_irq_flags); + hp_clear_shpc_event_bit(shpc_context, SUSPEND_EVENT); + hp_send_event_to_all_slots(shpc_context, + RESUME_EVENT || REMOVE_EVENT || EXIT_REQUEST_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + for( i=0; islot_context[ i ]; + + // + // Disable Slot Interrupts + // + hp_disable_slot_interrupts( slot_context ); + + // + // Remove scheduled slot DPCs + // + tasklet_kill( &slot_context->attn_button_dpc ); + tasklet_kill( &slot_context->card_presence_dpc ); + tasklet_kill( &slot_context->isolated_power_fault_dpc ); + tasklet_kill( &slot_context->connected_power_fault_dpc ); + + // + // Send events to kill all threads + // + // + // Set event bits to send to running threads + // + hp_set_shpc_event_bit(shpc_context, + (RESUME_EVENT | REMOVE_EVENT | EXIT_REQUEST_EVENT)); + + + + wake_up_interruptible(&slot_context->led_cmd_acquire_event); + wake_up_interruptible(&slot_context->cmd_acquire_event); + wake_up_interruptible(&slot_context->bus_acquire_event); + wake_up_interruptible(&slot_context->led_cmd_release_event); + wake_up_interruptible(&slot_context->cmd_release_event); + wake_up_interruptible(&slot_context->bus_release_event); + // + // Reset slot pointers and flags + // + slot_context->slot_enabled = FALSE; + slot_context->slot_thread = NULL; + slot_context->slot_function = NULL; + slot_context->attn_led_thread = NULL; + slot_context->attn_led_function = NULL; + } + + // + // Remove scheduled common DPC + // + tasklet_kill(&shpc_context->cmd_completion_dpc ); + + // + // Reset common resources + // + shpc_context->number_of_slots = 0; + shpc_context->slots_enabled = 0; + shpc_context->at_power_device_d0 = FALSE; + + return status; +} + + +// **************************************************************************** +// +// hp_SuspendDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// +// Return Value +// Status returned by any system calls made within hp_SuspendDevice(). +// +// Comments: +// hp_SuspendDevice() must be called before transitioning away from PowerDeviceD0. +// +// **************************************************************************** +long +hp_SuspendDevice( + struct shpc_context* shpc_context + ) +{ + long status = STATUS_SUCCESS; + unsigned long old_irq_flags; + + dbg("%s -->HwInstance[ %d ]", __FUNCTION__ ,shpc_context->shpc_instance ); + + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + + if(shpc_context->mmio_base_addr && + (!shpc_context->shpc_event_bits & SUSPEND_EVENT) && + (!shpc_context->shpc_event_bits & REMOVE_EVENT)) { + hp_clear_shpc_event_bit(shpc_context, RESUME_EVENT); + + hp_send_event_to_all_slots(shpc_context, SUSPEND_EVENT); + hp_send_event_to_all_slots(shpc_context, EXIT_REQUEST_EVENT); + + shpc_context->at_power_device_d0 = FALSE; + } + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + return status; +} + + +// **************************************************************************** +// +// hp_ResumeDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// +// Return Value +// Status returned by any system calls made within hp_ResumeDevice(). +// +// Comments: +// hp_SuspendDevice() must be called after transitioning back to PowerDeviceD0. +// +// **************************************************************************** +long +hp_ResumeDevice( + struct shpc_context* shpc_context + ) +{ + long status = STATUS_SUCCESS; + unsigned long old_irq_flags; + + dbg("%s -->HwInstance[ %d ]", __FUNCTION__ ,shpc_context->shpc_instance ); + + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + if(shpc_context->mmio_base_addr && + (shpc_context->shpc_event_bits & SUSPEND_EVENT) && + (!shpc_context->shpc_event_bits & REMOVE_EVENT)) { + hp_clear_shpc_event_bit(shpc_context, SUSPEND_EVENT); + hp_clear_shpc_event_bit(shpc_context, EXIT_REQUEST_EVENT); + hp_send_event_to_all_slots(shpc_context, RESUME_EVENT); + shpc_context->at_power_device_d0 = TRUE; + } + + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + return status; +} + + +// **************************************************************************** +// +// hp_QuerySlots() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// slot_config - Caller provided storage for slots configuration info. +// +// Return Value +// Status returned by any system calls made within hp_QuerySlots(). +// +// **************************************************************************** +long +hp_QuerySlots( + struct shpc_context* shpc_context, + union SLOT_CONFIG_INFO* slot_config + ) +{ + long status = STATUS_SUCCESS; + union SHPC_SLOT_CONFIG_DWREG slot_config_reg; + + dbg("%s -->HwInstance[ %d ] Slots[ %d ]",__FUNCTION__ , + shpc_context->shpc_instance, shpc_context->number_of_slots ); + + // + // Get slot configuration + // + slot_config_reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOT_CONFIG_REG_OFFSET); + + slot_config->AsDWord = 0; + slot_config->x.lu_slots_implemented = slot_config_reg.x.NSI; + slot_config->x.lu_base_PSN = slot_config_reg.x.PSN; + slot_config->x.lu_PSN_up = slot_config_reg.x.PSN_UP; + slot_config->x.lu_base_FDN = slot_config_reg.x.FDN; + + return status; +} + + +// **************************************************************************** +// +// hp_QuerySlotStatus() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// slot_id - Zero-based slot number (0..n-1). +// Query - Pointer to Slot Status Structure +// +// Return Value +// Status returned by any system calls made within hp_QuerySlotStatus(). +// +// **************************************************************************** +long +hp_QuerySlotStatus( + struct shpc_context* shpc_context, + u8 slot_id, + union SLOT_STATUS_INFO* Query + ) +{ + struct slot_context* slot_context; + long status = STATUS_SUCCESS; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_id ); + + // + // Valid slot_id? + // + if( slot_id >= shpc_context->number_of_slots ) { + status = STATUS_UNSUCCESSFUL; + } + else { + // + // Which slot? + // + slot_context = &shpc_context->slot_context[ slot_id ]; + + // + // Get Max Speed/Mode from common context + // + Query->x.lu_max_bus_mode_freq = hp_translate_speed_mode( shpc_context->max_speed_mode ); + + // + // Get Bus Speed/Mode from HW + // + Query->x.lu_bus_mode_freq = hp_translate_speed_mode( hp_get_bus_speed_mode( shpc_context )); + + // + // Get Card Speed/Mode from HW + // + Query->x.lu_card_mode_freq_cap = hp_translate_speed_mode( hp_get_card_speed_mode( slot_context )); + + // + // Get current slot info from HW + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + + // + // Card Present? + // + Query->x.lu_card_present = ( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) ? + HP_TRUE : HP_FALSE; + + // + // Get Card PCI-66 capability + // + Query->x.lu_card_pci66_capable = (( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) && + ( logical_slot_reg.x.S_STATE == SHPC_POWER_ONLY || logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT ) && + ( logical_slot_reg.x.M66_CAP == SHPC_STATUS_SET )) ? + HP_TRUE : HP_FALSE; + + // + // Power-Fault? + // + Query->x.lu_power_fault = ( logical_slot_reg.x.PF == SHPC_STATUS_SET ) ? + HP_TRUE : HP_FALSE; + + // + // Card Power Requirements + // + Query->x.lu_card_power = hp_translate_card_power( logical_slot_reg.x.PRSNT1_2 ); + + // + // Attention Indicator + // + Query->x.lu_ai_state = hp_translate_indicator( logical_slot_reg.x.AIS ); + + // + // Power Indicator + // + Query->x.lu_pi_state = hp_translate_indicator( logical_slot_reg.x.PIS ); + + // + // MRL Implemented? + // + Query->x.lu_mrl_implemented = ( logical_slot_reg.x.MRLS_IM == SHPC_UNMASKED ) ? + HP_TRUE : HP_FALSE; + + // + // MRL Opened? + // + Query->x.lu_mrl_opened = (( logical_slot_reg.x.MRLS == SHPC_MRL_OPEN ) && + ( logical_slot_reg.x.MRLS_IM == SHPC_UNMASKED )) ? HP_TRUE : HP_FALSE; + + // + // Slot State: Card Present, MRL closed, No Power-Fault, Enabled? + // + if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY && + ( logical_slot_reg.x.MRLS_IM == SHPC_MASKED || + logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) && + logical_slot_reg.x.PF == SHPC_STATUS_CLEARED && + logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT ) { + Query->x.lu_slot_state = SLOT_ENABLE; + } + else { + Query->x.lu_slot_state = SLOT_DISABLE; + } + + // + // OK, it's all there! + // + Query->x.lu_reserved1 = 0; + Query->x.lu_reserved2 = 0; + Query->x.lu_request_failed = HP_FALSE; + } + + return status; +} + + +// **************************************************************************** +// +// hp_StartAsyncRequest() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// slot_id - Zero-based slot number (0..n-1). +// Request - Async request: Slot "Enable/Disable", AttnLED "Attn/Normal"). +// timeout - For AttnLED "Attn" requests (in seconds) +// request_context - Caller provided pointer to be returned upon completion. +// +// Return Value +// STATUS_SUCCESS if the request is accepted. The Callback() is later invoked with a completion status. +// STATUS_UNSUCCESSFUL if the request is rejected (invalid parameters, or similar request in progress), +// +// Comment: +// For AttnLED "Attn" requests, the completion Callback() function is invoked as soon as the hardware +// completes (Blink) execution. When the timeout period expires, the AttnLED is brought back to +// its "Normal" (On/Off) state, and the Callback() is invoked once again. +// +// **************************************************************************** +long +hp_StartAsyncRequest( + struct shpc_context* shpc_context, + u8 slot_id, + enum shpc_async_request request, + u32 timeout, + void* request_context + ) +{ + unsigned long old_irq_flags; + struct slot_context* slot_context; + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ] Request[ %d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_id, request ); + + // + // Valid slot_id? + // + if( slot_id >= shpc_context->number_of_slots ) { + status = STATUS_UNSUCCESSFUL; + } + else { + slot_context = &shpc_context->slot_context[ slot_id ]; + + switch( request ) { + case SHPC_ASYNC_ENABLE_SLOT: + dbg("%s SHPC_ASYNC_ENABLE_SLOT",__FUNCTION__); + case SHPC_ASYNC_DISABLE_SLOT: + dbg("%s SHPC_ASYNC_DISABLE_SLOT",__FUNCTION__); + // + // Slot Request Pending? + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + down_interruptible(&slot_context->slot_event_bits_semaphore); + down_interruptible(&shpc_context->shpc_event_bits_semaphore); + if((slot_context->slot_event_bits & SLOT_REQUEST_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)) { + status = STATUS_UNSUCCESSFUL; + up(&slot_context->slot_event_bits_semaphore); + up(&shpc_context->shpc_event_bits_semaphore); + } + else { + up(&slot_context->slot_event_bits_semaphore); + up(&shpc_context->shpc_event_bits_semaphore); + slot_context->slot_request.type = request; + slot_context->slot_request.request_context = request_context; + hp_send_slot_event(slot_context, SLOT_REQUEST_EVENT); + } + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + break; + + case SHPC_ASYNC_LED_LOCATE: + dbg("%s SHPC_ASYNC_LED_LOCATE",__FUNCTION__); + case SHPC_ASYNC_LED_NORMAL: + dbg("%s SHPC_ASYNC_LED_NORMAL",__FUNCTION__); + // + // AttnLED Request Pending? + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + down_interruptible(&slot_context->slot_event_bits_semaphore); + down_interruptible(&shpc_context->shpc_event_bits_semaphore); + if((slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)) { + dbg("%s LED--STATUS_UNSUCCESSFUL slot_event_bits = %08X", __FUNCTION__ ,slot_context->slot_event_bits); + status = STATUS_UNSUCCESSFUL; + up(&slot_context->slot_event_bits_semaphore); + up(&shpc_context->shpc_event_bits_semaphore); + } + else { + up(&slot_context->slot_event_bits_semaphore); + up(&shpc_context->shpc_event_bits_semaphore); + slot_context->attn_led_request.type = request; + slot_context->attn_led_request.timeout = timeout; + slot_context->attn_led_request.request_context = request_context; + hp_send_slot_event(slot_context, ATTN_LED_REQUEST_EVENT); + } + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + break; + + case SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY: + dbg("%s SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY",__FUNCTION__); + // + // HP library notification: DevNode is quiesced + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + ++slot_context->quiesce_replies; + if( slot_context->quiesce_requests && + slot_context->quiesce_replies >= slot_context->quiesce_requests ) { + slot_context->slot_quiesced = TRUE; + hp_send_slot_event(slot_context, QUIESCE_EVENT); + } + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + break; + + case SHPC_ASYNC_CANCEL_QUIESCE_DEVNODE: + dbg("%s SHPC_ASYNC_CANCEL_QUIESCE_DEVNODE",__FUNCTION__); + // + // HP library notification: could not quiesce DevNode + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->slot_quiesced = FALSE; + hp_send_slot_event(slot_context, QUIESCE_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Abort bus-rebalancing + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + shpc_context->bus_released = FALSE; + hp_send_event_to_all_slots(shpc_context, BUS_COMPLETE_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + break; + + default: + status = STATUS_UNSUCCESSFUL; + break; + } + } + + return status; +} + +// **************************************************************************** +// +// hp_Queryslot_psn() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// SlotID - Zero-based slot number (0..n-1). +// slot_psn - Pointer to Physical Slot Number +// +// Return Value +// STATUS_SUCCESS, or STATUS_UNSUCCESSFUL for invalid SlotID. +// +// **************************************************************************** +long hp_Queryslot_psn(struct shpc_context *shpc_context, unsigned char slot_ID, unsigned long *slot_psn) +{ + struct slot_context *slot_context; + long status = STATUS_SUCCESS; + dbg("%s slot_ID[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_ID); + // + // Valid SlotID? + // + if( slot_ID >= shpc_context->number_of_slots || slot_psn == NULL ) { + status = STATUS_UNSUCCESSFUL; + } + else { + // + // Which slot? + // + slot_context = &shpc_context->slot_context[ slot_ID ]; + // + // Get slot PSN + // + *slot_psn = slot_context->slot_psn; + } + return status; +} + +// **************************************************************************** +// +// hp_slot_timers1-10func(): Function passed to timer to send event +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +void hp_slot_timer1_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER1_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_slot_timer2_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER2_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_slot_timer3_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER3_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_slot_timer4_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER4_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_slot_timer5_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER5_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_slot_timer6_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER6_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_slot_timer7_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER7_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_slot_timer8_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER8_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_slot_timer9_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER9_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_slot_timer10_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, SLOT_TIMER10_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +// **************************************************************************** +// +// hp_led_timers1-4_func(): Function passed to timer to send event +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +void hp_led_timer1_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, LED_TIMER1_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_led_timer2_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, LED_TIMER2_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_led_timer3_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, LED_TIMER3_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +void hp_led_timer4_func(unsigned long data){ + + struct slot_context *slot_context; + slot_context = (struct slot_context*) data; + + dbg("%s", __FUNCTION__); + hp_set_slot_event_bit(slot_context, LED_TIMER4_EVENT); + + wake_up_interruptible(&slot_context->slot_event); +} + +// **************************************************************************** +// +// hp_clear_slot_event_bit(): +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +void hp_clear_slot_event_bit(struct slot_context * slot_context, u32 mask) +{ +// dbg("%s -->slot bits %08X MASK=%08X",__FUNCTION__ ,slot_context->slot_event_bits, mask); + + down_interruptible(&slot_context->slot_event_bits_semaphore); + // cleareventbit + slot_context->slot_event_bits &= ~mask; + up(&slot_context->slot_event_bits_semaphore); +} + +// **************************************************************************** +// +// hp_set_slot_event_bit(): +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +void hp_set_slot_event_bit(struct slot_context * slot_context, u32 mask) +{ +// dbg("%s -->slot bits %08X MASK=%08X",__FUNCTION__ ,slot_context->slot_event_bits, mask); + + down_interruptible(&slot_context->slot_event_bits_semaphore); + // cleareventbit + slot_context->slot_event_bits |= mask; + up(&slot_context->slot_event_bits_semaphore); +} + +// **************************************************************************** +// +// hp_clear_shpc_event_bit(): +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +void hp_clear_shpc_event_bit(struct shpc_context * shpc_context, u32 mask) +{ + down_interruptible(&shpc_context->shpc_event_bits_semaphore); + // cleareventbit + shpc_context->shpc_event_bits &= ~mask; + up(&shpc_context->shpc_event_bits_semaphore); +} + +// **************************************************************************** +// +// hp_set_shpc_event_bit(): +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +void hp_set_shpc_event_bit(struct shpc_context * shpc_context, u32 mask) +{ + down_interruptible(&shpc_context->shpc_event_bits_semaphore); + // set event bit + shpc_context->shpc_event_bits |= mask; + up(&shpc_context->shpc_event_bits_semaphore); +} + +// **************************************************************************** +// +// hp_send_event_to_all_slots(): +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +void hp_send_event_to_all_slots(struct shpc_context *shpc_context, u32 mask) +{ + u8 i; + struct slot_context * slot_context; + + down_interruptible(&shpc_context->shpc_event_bits_semaphore); + // set event bit + shpc_context->shpc_event_bits |= mask; + // send event to each slot thread + for( i=0; inumber_of_slots; ++i ) { + slot_context = &shpc_context->slot_context[ i ]; + wake_up_interruptible(&slot_context->slot_event); + } + up(&shpc_context->shpc_event_bits_semaphore); +} + +// **************************************************************************** +// +// hp_send_slot_event(): +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +void hp_send_slot_event(struct slot_context * slot_context, u32 mask) +{ + // set event bit + hp_set_slot_event_bit(slot_context, mask); + wake_up_interruptible( &slot_context->slot_event); +} + + +// **************************************************************************** +// +// hp_get_led_cmd_available_mutex_thread(): run as a thread per each slot +// +// Parameters +// slot_context - Caller provided storage for slot context data. +// +// Return Value +// void +// +// **************************************************************************** +int hp_get_led_cmd_available_mutex_thread(void *ptr) +{ + long status = STATUS_SUCCESS; + struct shpc_context* shpc_context; + struct slot_context* slot_context; + int pid; + + lock_kernel (); + daemonize ("amdshpc_getledcmd_av_mutex"); + reparent_to_init (); + unlock_kernel (); + + slot_context = (struct slot_context* ) ptr; + shpc_context = (struct shpc_context* ) slot_context->shpc_context; + do { + interruptible_sleep_on(&slot_context->led_cmd_acquire_event); + if (slot_context->slot_event_bits & LED_CMD_ACQUIRE_EVENT){ + hp_clear_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT); + pid = kernel_thread(hp_led_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND); + if (pid < 0) { + err ("Can't start up our hp_led_cmd_available_mutex_thread\n"); + status = STATUS_UNSUCCESSFUL; + break; + } + } else { + dbg("%s terminating return 0 slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1); + return 0; + } + } while (1); + return(status); +} + +// **************************************************************************** +// +// hp_led_cmd_available_mutex_thread(): run as a thread per each request for cmd +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +int hp_led_cmd_available_mutex_thread(void *ptr) +{ + struct shpc_context* shpc_context; + struct slot_context* slot_context; + + lock_kernel (); + daemonize ("amdshpc_ledcmd_av_mutex"); + reparent_to_init (); + unlock_kernel (); + + slot_context = (struct slot_context* ) ptr; + shpc_context = (struct shpc_context* ) slot_context->shpc_context; + + // + // acquire the main mutex for all slots exclusion + // + down_interruptible(&shpc_context->cmd_available_mutex); + if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)|| + (shpc_context->shpc_event_bits & RESUME_EVENT)|| + (shpc_context->shpc_event_bits & REMOVE_EVENT)){ + dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1); + up(&shpc_context->cmd_available_mutex); + return 0; + } + + // + // now tell our slot thread that it has the mutex + // + hp_set_shpc_event_bit(shpc_context, LED_CMD_AVAILABLE_MUTEX_EVENT); + wake_up_interruptible(&slot_context->slot_event); + + // + // wait for our slot thread to release the mutex + // + interruptible_sleep_on(&slot_context->led_cmd_release_event); + if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)|| + (shpc_context->shpc_event_bits & RESUME_EVENT)|| + (shpc_context->shpc_event_bits & REMOVE_EVENT)){ + dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1); + up(&shpc_context->cmd_available_mutex); + return 0; + } + hp_clear_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT); + + hp_clear_shpc_event_bit(shpc_context, LED_CMD_AVAILABLE_MUTEX_EVENT); + up(&shpc_context->cmd_available_mutex); + dbg("%s cmd_available_mutex RELEASED",__FUNCTION__); + return(0); +} + +// **************************************************************************** +// +// hp_get_cmd_available_mutex_thread(): run as a thread per each slot +// +// Parameters +// slot_context - Caller provided storage for slot context data. +// +// Return Value +// void +// +// **************************************************************************** +int hp_get_cmd_available_mutex_thread(void *ptr) +{ + long status = STATUS_SUCCESS; + struct shpc_context* shpc_context; + struct slot_context* slot_context; + int pid; + + lock_kernel (); + daemonize ("amdshpc_getcmd_av_mutex"); + reparent_to_init (); + unlock_kernel (); + + slot_context = (struct slot_context* ) ptr; + shpc_context = (struct shpc_context* ) slot_context->shpc_context; + + do { + interruptible_sleep_on(&slot_context->cmd_acquire_event); + if ((slot_context->slot_event_bits & CMD_ACQUIRE_EVENT) || + (slot_context->slot_event_bits & CMD_RELEASE_EVENT)){ + hp_clear_slot_event_bit(slot_context,CMD_ACQUIRE_EVENT); + pid = kernel_thread(hp_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND); + if (pid < 0) { + err ("Can't start up our hp_get_cmd_available_mutex_thread\n"); + status = STATUS_UNSUCCESSFUL; + break; + } + } else { + dbg("%s terminating return 0 slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1); + return 0; + } + } while (1); + return(status); +} + +// **************************************************************************** +// +// hp_cmd_available_mutex_thread(): run as a thread per each request for cmd +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +int hp_cmd_available_mutex_thread(void *ptr) +{ + struct shpc_context* shpc_context; + struct slot_context* slot_context; + + lock_kernel (); + daemonize ("amdshpc_cmd_av_mutex"); + reparent_to_init (); + unlock_kernel (); + + slot_context = (struct slot_context* ) ptr; + shpc_context = (struct shpc_context* ) slot_context->shpc_context; + + // + // acquire the main mutex for all slots exclusion + // + down_interruptible(&shpc_context->cmd_available_mutex); + if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)|| + (shpc_context->shpc_event_bits & RESUME_EVENT)|| + (shpc_context->shpc_event_bits & REMOVE_EVENT)){ + dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1); + up(&shpc_context->cmd_available_mutex); + return 0; + } + + // + // now tell our slot thread that it has the mutex + // + hp_set_shpc_event_bit(shpc_context, CMD_AVAILABLE_MUTEX_EVENT); + wake_up_interruptible(&slot_context->slot_event); + + // + // wait for our slot thread to release the mutex + // + interruptible_sleep_on(&slot_context->cmd_release_event); + if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)|| + (shpc_context->shpc_event_bits & RESUME_EVENT)|| + (shpc_context->shpc_event_bits & REMOVE_EVENT)){ + dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1); + up(&shpc_context->cmd_available_mutex); + return 0; + } + hp_clear_slot_event_bit(slot_context,CMD_RELEASE_EVENT); + hp_clear_shpc_event_bit(shpc_context, CMD_AVAILABLE_MUTEX_EVENT); + up(&shpc_context->cmd_available_mutex); + return(0); +} + +// **************************************************************************** +// +// hp_get_bus_available_mutex_thread(): run as a thread per each slot +// +// Parameters +// slot_context - Caller provided storage for slot context data. +// +// Return Value +// void +// +// **************************************************************************** +int hp_get_bus_available_mutex_thread(void *ptr) +{ + long status = STATUS_SUCCESS; + struct shpc_context* shpc_context; + struct slot_context* slot_context; + int pid; + + lock_kernel (); + daemonize ("amdshpc_getbus_av_mutex"); + reparent_to_init (); + unlock_kernel (); + + slot_context = (struct slot_context* ) ptr; + shpc_context = (struct shpc_context* ) slot_context->shpc_context; + + do { + interruptible_sleep_on(&slot_context->bus_acquire_event); + if (slot_context->slot_event_bits & BUS_ACQUIRE_EVENT) { + hp_clear_slot_event_bit(slot_context, BUS_ACQUIRE_EVENT); + pid = kernel_thread(hp_bus_available_mutex_thread, slot_context, CLONE_SIGHAND); + if (pid < 0) { + err ("Can't start up our hp_get_bus_available_mutex_thread\n"); + status = STATUS_UNSUCCESSFUL; + break; + } + } else { + dbg("%s terminating return 0 slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1); + return 0; + } + } while (1); + return(status); +} + +// **************************************************************************** +// +// hp_bus_available_mutex_thread(): +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +int hp_bus_available_mutex_thread(void *ptr) +{ + struct shpc_context* shpc_context; + struct slot_context* slot_context; + + lock_kernel (); + daemonize ("amdshpc_bus_av_mutex"); + reparent_to_init (); + // New name + unlock_kernel (); + + slot_context = (struct slot_context* ) ptr; + shpc_context = (struct shpc_context* ) slot_context->shpc_context; + + // + // acquire the main mutex for all slots exclusion + // + down_interruptible(&shpc_context->bus_available_mutex); + if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)|| + (shpc_context->shpc_event_bits & RESUME_EVENT)|| + (shpc_context->shpc_event_bits & REMOVE_EVENT)){ + dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1); + up(&shpc_context->bus_available_mutex); + return 0; + } + + // + // now tell our slot thread that it has the mutex + // + hp_set_shpc_event_bit(shpc_context, BUS_AVAILABLE_MUTEX_EVENT); + wake_up_interruptible(&slot_context->slot_event); + + // + // wait for our slot thread to release the mutex + // + interruptible_sleep_on(&slot_context->bus_release_event); + if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)|| + (shpc_context->shpc_event_bits & RESUME_EVENT)|| + (shpc_context->shpc_event_bits & REMOVE_EVENT)){ + dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1); + up(&shpc_context->bus_available_mutex); + return 0; + } + hp_clear_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + + hp_clear_shpc_event_bit(shpc_context, BUS_AVAILABLE_MUTEX_EVENT); + up(&shpc_context->bus_available_mutex); + return(0); +} + +// **************************************************************************** +// +// call_back_routine(): +// +// Parameters +// slot_context - Caller provided storage for SHPC context data. +// +// Return Value +// void +// +// **************************************************************************** +static unsigned long async_callback (void* driver_context, + u8 slot_id, + enum shpc_async_request async_request, + union SLOT_STATUS_INFO slot_status, + void* request_context ) +{ + u8 phys_slot_num; + long rc=0; + struct pci_func *slot_func; + struct controller *ctrl; + struct shpc_context *shpc_context; + u8 bus=0; + u8 device=0; + u8 function=0; + unsigned long devices_still_quiescing = 0; + + dbg("%s slot_id = %d",__FUNCTION__, slot_id); + + ctrl = ((struct controller*) driver_context); + if (ctrl == NULL){ + return -ENODEV; + } + + shpc_context = (struct shpc_context* ) ctrl->shpc_context; + phys_slot_num = shpc_context->slot_context[slot_id].slot_psn; + + bus = ctrl->bus; + device = slot_id + 1; + + dbg("%s - physical_slot = %d instance = %d",__FUNCTION__, phys_slot_num, shpc_context->shpc_instance); + + switch( async_request ) { + case SHPC_ASYNC_ENABLE_SLOT: + dbg("%s SHPC_ASYNC_ENABLE_SLOT",__FUNCTION__); + dbg("%s slot occupied = %d",__FUNCTION__,shpc_context->slot_context[slot_id].slot_occupied); + if (shpc_context->slot_context[slot_id].slot_occupied == 1) { + return 0; + } + // + // Force pci-bus re-enumeration (probe), to load drivers on behalf on enabled device(s) on this slot. + // + dbg("%s In callback routine processing enable slot",__FUNCTION__ ); + + dbg("%s CALLING amdshpc_slot_find bus, dev, fn = %d, %d, %d\n",__FUNCTION__ , + bus, device, function); + slot_func = amdshpc_slot_find(bus, device, function); + dbg("%s slot_func = %p ",__FUNCTION__ , slot_func); + if (!slot_func) { + dbg("%s --> slot_func not found",__FUNCTION__ ); + return -ENODEV; + } + + slot_func->bus = bus; + slot_func->device = device; + slot_func->function = function; + slot_func->configured = 0; + dbg("%s CALLING amdshpc_process_SI(ctrl=%p slot_func=%p)\n",__FUNCTION__ , ctrl, slot_func); + rc = amdshpc_process_SI(ctrl, slot_func); + if (!rc ) { + shpc_context->slot_context[slot_id].slot_occupied = 1; + } + dbg("%s amdshpc_process_SI returned rc=%d",__FUNCTION__ , (int)rc); + break; + + case SHPC_ASYNC_SURPRISE_REMOVE: + dbg("%s SHPC_ASYNC_SURPRISE_REMOVE",__FUNCTION__); + // + // Something went wrong with the slot (eg, power-fault), and loaded drivers must be removed. + // + case SHPC_ASYNC_QUIESCE_DEVNODE: + dbg("%s SHPC_ASYNC_QUIESCE_DEVNODE",__FUNCTION__); + // + // Friendly opportunity to quiesce (remove) drivers, prior to disabling the slot. + // After device drivers are removed, it's OK to show messages to that effect. + // + // If device quiecing will complete at a later time (from a separate thread), + // then set "devices_still_quiescing" accordingly, and upon quiecing-completion, + // call hp_StartAsyncRequest() with a "SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY" request. + // + case SHPC_ASYNC_QUIESCE_DEVNODE_QUIET: + dbg("%s SHPC_ASYNC_QUIESCE_DEVNODE_QUIET",__FUNCTION__); + // + // Friendly opportunity to quiesce (remove) drivers, prior to disabling the slot. + // After device drivers are removed, don't show messages to that effect. + // + // If device quiecing will complete at a later time (from a separate thread), + // then set "devices_still_quiescing" accordingly, and upon quiecing-completion, + // call hp_StartAsyncRequest() with a "SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY" request. + // + dbg("%s Processing disable slot",__FUNCTION__ ); + + dbg("%s CALLING amdshpc_slot_find bus, dev, fn = %d, %d, %d\n",__FUNCTION__ , + bus, device, function); + + slot_func = amdshpc_slot_find(bus, device, function); + dbg("%s slot_func = %p ",__FUNCTION__ , slot_func); + if (!slot_func) { + dbg("%s --> slot_func not found",__FUNCTION__ ); + return -ENODEV; + } + + dbg("%s CALLING amdshpc_process_SS(ctrl=%p slot_func=%p)\n",__FUNCTION__ , ctrl, slot_func); + rc = amdshpc_process_SS(ctrl, slot_func); + if (!rc ) { + shpc_context->slot_context[slot_id].slot_occupied = 0; + } + dbg("%s amdshpc_process_SS returned rc=%d",__FUNCTION__ , (int)rc); + + break; + + case SHPC_ASYNC_DISABLE_SLOT: + dbg("%s SHPC_ASYNC_DISABLE_SLOT",__FUNCTION__); + // + // Just a notification, may be used to update some interested GUI application. + // + break; + + default: + break; + } + return devices_still_quiescing; +} + --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc_ddi.h Tue Jun 24 13:31:13 2003 @@ -0,0 +1,235 @@ +/* +* (c) 2002,2003 Advanced Micro Devices, Inc. +* YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS +* AND CONDITIONS OF THE GNU GENERAL PUBLIC +* LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS +* INCLUDED WITH THIS FILE AND POSTED AT +* http://www.gnu.org/licenses/gpl.html +* +* +* This driver is to be used as a skeleton driver to be show how to interface +* with the pci hotplug core easily. +* +* Send feedback to +* +*/ + + +#ifndef _SHPC_DDI_H_ +#define _SHPC_DDI_H_ + +#include "amdshpc.h" +// +// SHPC Constants +// +#define SHPC_MAX_NUM_SLOTS 4 + + +// **************************************************************************** +// +// hp_AddDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data (per hardware-instance). +// driver_context - Caller provided pointer to be returned upon completion. +// Callback - Caller provided function to be called upon completion of async requests. +// shpc_instance - Zero-based hardware instance. +// +// Return Value +// Status returned by any system calls made within hp_AddDevice(). +// +// **************************************************************************** +long + hp_AddDevice( + struct shpc_context *shpc_context, + void* driver_context, + SHPC_ASYNC_CALLBACK Callback, + unsigned long shpc_instance + ); + + +// **************************************************************************** +// +// hp_StartDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// mmio_base_addr - from u.Memory member of CmResourceTypeMemory +// IntVector - from u.Interrupt.Vector member of CmResourceTypeInterrupt +// IntMode - from Flags member of CmResourceTypeInterrupt +// IntShared - from ShareDisposition member of CmResourceTypeInterrupt +// IntAffinity - from u.Interrupt.Affinity member of CmResourceTypeInterrupt +// +// Return Value +// Status returned by any system calls made within hp_StartDevice(). +// +// Comments: +// The caller is responsible for mapping mmio_base_addr, via MmMapIoSpace(), +// before calling hp_StartDevice(). +// +// **************************************************************************** +long + hp_StartDevice( + struct shpc_context* shpc_context + ); + + +// **************************************************************************** +// +// hp_StopDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// +// Return Value +// Status returned by any system calls made within hp_StopDevice(). +// +// Comments: +// The caller is responsible for unmapping mmio_base_addr, via MmUnmapIoSpace(), +// after calling hp_StopDevice() for resource re-balancing or device removal. +// +// **************************************************************************** +long hp_StopDevice(struct shpc_context *shpc_context); + +// **************************************************************************** +// +// hp_SuspendDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// +// Return Value +// Status returned by any system calls made within hp_SuspendDevice(). +// +// Comments: +// hp_SuspendDevice() must be called before transitioning away from PowerDeviceD0. +// +// **************************************************************************** +long hp_SuspendDevice(struct shpc_context *shpc_context); + +// **************************************************************************** +// +// hp_ResumeDevice() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// +// Return Value +// Status returned by any system calls made within hp_ResumeDevice(). +// +// Comments: +// hp_SuspendDevice() must be called after transitioning back to PowerDeviceD0. +// +// **************************************************************************** +long hp_ResumeDevice(struct shpc_context *shpc_context); + +// **************************************************************************** +// +// hp_QuerySlots() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// SlotConfig - Caller provided storage for slots configuration info. +// +// Return Value +// Status returned by any system calls made within hp_QuerySlots(). +// +// **************************************************************************** +long hp_QuerySlots(struct shpc_context *shpc_context, union SLOT_CONFIG_INFO* SlotConfig); + + +// **************************************************************************** +// +// hp_QuerySlotStatus() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// slot_id - Zero-based slot number (0..n-1). +// Query - Pointer to Slot Status Structure +// +// Return Value +// Status returned by any system calls made within hp_QuerySlotStatus(). +// +// **************************************************************************** +long hp_QuerySlotStatus(struct shpc_context *shpc_context, u8 slot_id, union SLOT_STATUS_INFO* Query); + +// **************************************************************************** +// +// hp_Queryslot_psn() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// SlotID - Zero-based slot number (0..n-1). +// slot_psn - Pointer to Physical Slot Number +// +// Return Value +// STATUS_SUCCESS, or STATUS_UNSUCCESSFUL for invalid SlotID. +// +// **************************************************************************** +long hp_Queryslot_psn(struct shpc_context *shpc_context, unsigned char slot_ID, unsigned long *slot_psn); + +// **************************************************************************** +// +// hp_StartAsyncRequest() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// slot_id - Zero-based slot number (0..n-1). +// Request - Async request: Slot "Enable/Disable", AttnLED "Attn/Normal"). +// timeout - For AttnLED "Attn" requests (in seconds) +// request_context - Caller provided pointer to be returned upon completion. +// +// Return Value +// STATUS_SUCCESS if the request is accepted. The Callback() is later invoked with a completion status. +// STATUS_UNSUCCESSFUL if the request is rejected (invalid parameters, or similar request in progress), +// +// Comment: +// For AttnLED "Attn" requests, the completion Callback() function is invoked as soon as the hardware +// completes (Blink) execution. When the timeout period expires, the AttnLED is brought back to +// its "Normal" (On/Off) state, and the Callback() is invoked once again. +// +// **************************************************************************** +long hp_StartAsyncRequest( + struct shpc_context *shpc_context, + u8 slot_id, + enum shpc_async_request Request, + u32 timeout, + void* request_context + ); + + +// **************************************************************************** +// +// hp_RegisterUserEvent() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// user_event_pointer - Pointer to caller's provided EVENT object. +// +// Return Value +// STATUS_SUCCESS if the request is accepted. +// STATUS_UNSUCCESSFUL if the request is rejected (EVENT already registered). +// +// **************************************************************************** +long hp_RegisterUserEvent( + struct shpc_context *shpc_context, + wait_queue_head_t *user_event_pointer + ); + + +// **************************************************************************** +// +// hp_UnRegisterUserEvent() +// +// Parameters +// shpc_context - Caller provided storage for SHPC context data. +// +// Return Value +// STATUS_SUCCESS if the request is accepted. +// STATUS_UNSUCCESSFUL if the request is rejected (EVENT not previously registered). +// +// **************************************************************************** +long hp_UnRegisterUserEvent(struct shpc_context *shpc_context); + +#endif // _SHPC_DDI_H_ + --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc_dsb.c Tue Jun 24 13:31:13 2003 @@ -0,0 +1,1502 @@ +/* + * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2003 IBM Corp. + * Copyright (c) 2002-2003 Advanced Micro Devices + * + * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS + * AND CONDITIONS OF THE GNU GENERAL PUBLIC + * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS + * INCLUDED WITH THIS FILE AND POSTED AT + * http://www.gnu.org/licenses/gpl.html + * + * Send feedback to + * + */ + +#include +#include +#include "amdshpc_ddi.h" +#include "amdshpc.h" + + +// **************************************************************************** +// +// hp_at_slot_enabled_wait_for_slot_request() +// +// **************************************************************************** +long +hp_at_slot_enabled_wait_for_slot_request( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SLOT_STATUS_INFO slot_status; + unsigned long DevNodes; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Slot Enabled: complete pending slot request + // + if( slot_context->slot_completion.done ) { + dbg("%s -->ENABLE_DONE: slot_id[ %d:%d ] card_speed_mode[ %d+%d ] bus_speed_mode[ %d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + slot_context->card_speed_mode, slot_context->card_pci66_capable, + shpc_context->bus_speed_mode ); + // + // Call Completion Callback() + // + hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status ); + slot_status.x.lu_request_failed = slot_context->slot_completion.failed; + shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + slot_context->slot_completion.type, + slot_status, + slot_context->slot_completion.request_context ); + + // + // Signal registered user EVENT + // + hp_signal_user_event( shpc_context ); + + // + // Clear completion flag + // + slot_context->slot_completion.done = FALSE; + } + + // + // Clear Button EVENT before waiting + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + hp_clear_slot_event_bit(slot_context, ATTN_BUTTON_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Wait for slot request + // + shpc_context->shpc_event_bits = 0;slot_context->slot_event_bits = 0; + + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) || + (slot_context->slot_event_bits & SLOT_REQUEST_EVENT) || + (slot_context->slot_event_bits & BUS_REBALANCE_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Notify unrequested removal + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE; + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.request_context = NULL; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // bus_rebalance_event + // + else if(slot_context->slot_event_bits & BUS_REBALANCE_EVENT) { + // + // Clear Quiesced EVENT before invoking Callback() + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->quiesce_requests = 0; + slot_context->quiesce_replies = 0; + slot_context->slot_quiesced = FALSE; + hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Call Completion Callback() to quiesce DevNode(s) + // + slot_status.AsDWord = 0; + DevNodes = shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + SHPC_ASYNC_QUIESCE_DEVNODE_QUIET, + slot_status, + ( void* )(unsigned long)slot_context->slot_psn ); + + // + // Update request count + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->quiesce_requests = DevNodes; + if( slot_context->quiesce_requests == 0 || + slot_context->quiesce_replies >= slot_context->quiesce_requests ) { + slot_context->slot_quiesced = TRUE; + hp_send_slot_event(slot_context, QUIESCE_EVENT); + } + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Wait for DevNode quiescing + // + dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_bus_rebalance; + } + // + // attn_button_event + // + else if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) { + // + // Set completion info for HW-initiated request + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_DISABLE_SLOT; + slot_context->slot_completion.request_context = NULL; + + // + // Grab Command MUTEX to blink Power LED + // + dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_led_cmd_available; + } + // + // SlotRequestEvent + // + else if(slot_context->slot_event_bits & SLOT_REQUEST_EVENT) { + // + // Set completion info for SW-initiated request + // + slot_context->slot_completion.hw_initiated = FALSE; + slot_context->slot_completion.type = slot_context->slot_request.type; + slot_context->slot_completion.request_context = slot_context->slot_request.request_context; + + // + // Request to disable slot? + // + if( slot_context->slot_request.type == SHPC_ASYNC_DISABLE_SLOT ) { + // + // Grab Command MUTEX to blink Power LED + // + dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_led_cmd_available; + } + else { + // + // Slot already enabled, just complete the request + // + dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.done = TRUE; + } + + // + // Allow next SW-initiated slot request while processing this one + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + hp_clear_slot_event_bit(slot_context, SLOT_REQUEST_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_enabled_wait_for_stop_on_bus_rebalance() +// +// **************************************************************************** +long +hp_at_slot_enabled_wait_for_stop_on_bus_rebalance( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER1_EVENT); + slot_context->slot_timer1.data = (unsigned long)slot_context; + slot_context->slot_timer1.function = hp_slot_timer1_func; + slot_context->slot_timer1.expires = jiffies + QUIESCE_QUIET_TIMEOUT; + add_timer(&slot_context->slot_timer1); + + // + // Wait for Quiescing EVENT + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & QUIESCE_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER1_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer1); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Notify unrequested removal + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE; + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.request_context = NULL; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // quiesce_event, timeout + // + else if((slot_context->slot_event_bits & QUIESCE_EVENT) || (slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) { + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + if((slot_context->slot_event_bits & SLOT_TIMER1_EVENT) || slot_context->slot_quiesced ) { + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + // + // Grab Command MUTEX to set slot at power-only + // + if((slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) { + dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ] Quiesce timeout",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + } + else { + dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ] Slot Quiesced",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + } + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_power_cmd_available; + } + else { + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + // + // Cancel bus re-balancing and treat it as a "Slot Enabled" request + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT; + slot_context->slot_completion.request_context = NULL; + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.done = TRUE; + + dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_slot_request; + } + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_enabled_wait_for_power_cmd_available() +// +// **************************************************************************** +long +hp_at_slot_enabled_wait_for_power_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + struct task_struct; + union SHPC_COMMAND_WREG command_reg; + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + // + // Notify unrequested removal + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE; + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.request_context = NULL; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_available_mutex + // + else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Set slot to "Disable" and blink Power LED + // + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_LED_BLINK; + command_reg.Slot.attention_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_DISABLE_SLOT; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete (while holding MUTEX) + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_power_cmd_completion; + } + // + // exit_request_event + // + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_enabled_wait_for_power_cmd_completion() +// +// **************************************************************************** +long +hp_at_slot_enabled_wait_for_power_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SLOT_STATUS_INFO slot_status; + union SHPC_STATUS_WREG status_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER2_EVENT); + slot_context->slot_timer2.data = (unsigned long)slot_context; + slot_context->slot_timer2.function = hp_slot_timer2_func; + slot_context->slot_timer2.expires = jiffies + FIFTEEN_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer2); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER2_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER2_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer2); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Notify unrequested removal + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE; + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.request_context = NULL; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_completion_event, timeout + // + else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER2_EVENT)) { + // + // Command completed OK? + // + status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET); + + if( status_reg.x.BSY == SHPC_STATUS_CLEARED && + status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED && + status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) { + // + // Flag this slot as DISABLED + // + hp_flag_slot_as_disabled( shpc_context, slot_context ); + + // + // Call Completion Callback(): slot disabled + // + hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status ); + slot_status.x.lu_request_failed = HP_FALSE; + shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + SHPC_ASYNC_DISABLE_SLOT, + slot_status, + NULL ); + + // + // Signal registered user EVENT + // + hp_signal_user_event( shpc_context ); + + // + // Treat it as an on-going ENABLE request + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT; + slot_context->slot_completion.request_context = NULL; + + // + // Grab Command MUTEX to power-on the slot + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_available; + } + else { + // + // Treat it as a HW-initiated DISABLE request + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_DISABLE_SLOT; + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.request_context = NULL; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_enabled_wait_for_led_cmd_available() +// +// **************************************************************************** +long +hp_at_slot_enabled_wait_for_led_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_available_mutex + // + else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Blink Power LED + // + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_LED_BLINK; + command_reg.Slot.attention_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_SLOT_NO_CHANGE; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete (while holding MUTEX) + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_led_cmd_completion; + } + // + // exit_request_event + // + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + status =STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_enabled_wait_for_led_cmd_completion() +// +// **************************************************************************** +long +hp_at_slot_enabled_wait_for_led_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SLOT_STATUS_INFO slot_status; + union SHPC_STATUS_WREG status_reg; + unsigned long DevNodes; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER3_EVENT); + slot_context->slot_timer3.data = (unsigned long)slot_context; + slot_context->slot_timer3.function = hp_slot_timer3_func; + slot_context->slot_timer3.expires = jiffies + ONE_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer3); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER3_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER3_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer3); + } + dbg("%s -->slot bits %08X shpc bits %08X",__FUNCTION__ , + slot_context->slot_event_bits,shpc_context->shpc_event_bits); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_completion_event, timeout + // + else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER3_EVENT)) { + // + // Command completed OK? + // + status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET); + + if( status_reg.x.BSY == SHPC_STATUS_CLEARED && + status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED && + status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) { + // + // Allow cancellation of operation? + // + if( slot_context->slot_completion.hw_initiated ) { + // + // Wait for 5 sec timeout + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_timeout; + } + else { + // + // Clear Quiesced EVENT before invoking Callback() + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->quiesce_requests = 0; + slot_context->quiesce_replies = 0; + slot_context->slot_quiesced = FALSE; + hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Call Completion Callback() to quiesce DevNode(s) + // + slot_status.AsDWord = 0; + DevNodes = shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + SHPC_ASYNC_QUIESCE_DEVNODE, + slot_status, + ( void* )(unsigned long)slot_context->slot_psn ); + + // + // Update request count + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->quiesce_requests = DevNodes; + if( slot_context->quiesce_requests == 0 || + slot_context->quiesce_replies >= slot_context->quiesce_requests ) { + slot_context->slot_quiesced = TRUE; + hp_send_slot_event(slot_context, QUIESCE_EVENT); + } + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Wait for DevNode quiescing + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_slot_disable; + } + } + else { + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_enabled_wait_for_timeout() +// +// **************************************************************************** +long +hp_at_slot_enabled_wait_for_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SLOT_STATUS_INFO slot_status; + unsigned long DevNodes; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Clear Button EVENT before waiting + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + down_interruptible(&slot_context->slot_event_bits_semaphore); + slot_context->slot_event_bits &= ~ATTN_BUTTON_EVENT; + up(&slot_context->slot_event_bits_semaphore); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER7_EVENT); + slot_context->slot_timer7.data = (unsigned long)slot_context; + slot_context->slot_timer7.function = hp_slot_timer7_func; + slot_context->slot_timer7.expires = jiffies + FIVE_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer7); + + // + // Wait for 5 sec timeout + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER7_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER7_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer7); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // attn_button_event + // + else if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) { + // + // Cancel request, grab Command MUTEX to Power LED back ON + // + dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Cancelled: Attn Button",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_available; + } + // + // timeout + // + else if(slot_context->slot_event_bits & SLOT_TIMER7_EVENT) { + // + // Clear Quiesced EVENT before invoking Callback() + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->quiesce_requests = 0; + slot_context->quiesce_replies = 0; + slot_context->slot_quiesced = FALSE; + hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Call Completion Callback() to quiesce DevNode(s) + // + slot_status.AsDWord = 0; + DevNodes = shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + SHPC_ASYNC_QUIESCE_DEVNODE, + slot_status, + ( void* )(unsigned long)slot_context->slot_psn ); + + // + // Update request count + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->quiesce_requests = DevNodes; + if( slot_context->quiesce_requests == 0 || + slot_context->quiesce_replies == slot_context->quiesce_requests ) { + slot_context->slot_quiesced = TRUE; + hp_send_slot_event(slot_context, QUIESCE_EVENT); + } + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Wait for DevNode quiescing + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_slot_disable; + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_enabled_wait_for_stop_on_slot_disable() +// +// **************************************************************************** +long +hp_at_slot_enabled_wait_for_stop_on_slot_disable( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SLOT_STATUS_INFO slot_status; + unsigned long DevNodes; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER4_EVENT); + slot_context->slot_timer4.data = (unsigned long)slot_context; + slot_context->slot_timer4.function = hp_slot_timer4_func; + slot_context->slot_timer4.expires = jiffies + QUIESCE_QUIET_TIMEOUT; + add_timer(&slot_context->slot_timer4); + + // + // Wait for Quiescing EVENT + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & QUIESCE_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER4_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER4_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer4); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // quiesce_event + // + else if(slot_context->slot_event_bits & QUIESCE_EVENT) { + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + if( slot_context->slot_quiesced ) { + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + // + // Complete succesful DISABLE request + // + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + // + dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Slot Quiesced",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + else { + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + // + // Cancel request, grab Command MUTEX to turn Power LED back ON + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT; + slot_context->slot_completion.request_context = NULL; + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.done = TRUE; + + dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_available; + } + } + // + // timeout + // + else if(slot_context->slot_event_bits & SLOT_TIMER4_EVENT) { + // + // Clear Quiesced EVENT before invoking Callback() + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->quiesce_requests = 0; + slot_context->quiesce_replies = 0; + slot_context->slot_quiesced = FALSE; + hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Call Completion Callback() to quiesce DevNode(s) + // + slot_status.AsDWord = 0; + DevNodes = shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + SHPC_ASYNC_QUIESCE_DEVNODE_QUIET, + slot_status, + ( void* )(unsigned long)slot_context->slot_psn ); + + // + // Update request count + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->quiesce_requests = DevNodes; + if( slot_context->quiesce_requests == 0 || + slot_context->quiesce_replies == slot_context->quiesce_requests ) { + slot_context->slot_quiesced = TRUE; + hp_send_slot_event(slot_context, QUIESCE_EVENT); + } + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Wait for DevNode quiescing + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet; + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet() +// +// **************************************************************************** +long +hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER5_EVENT); + slot_context->slot_timer5.data = (unsigned long)slot_context; + slot_context->slot_timer5.function = hp_slot_timer5_func; + slot_context->slot_timer5.expires = jiffies + QUIESCE_QUIET_TIMEOUT; + add_timer(&slot_context->slot_timer5); + + // + // Wait for Quiescing EVENT + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & QUIESCE_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER5_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer5); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // quiesce_event, timeout + // + else if((slot_context->slot_event_bits & QUIESCE_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) { + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + if((slot_context->slot_event_bits & SLOT_TIMER5_EVENT) || slot_context->slot_quiesced ) { + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + // + // Complete succesful DISABLE request + // + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + // + if(slot_context->slot_event_bits & SLOT_TIMER5_EVENT) { + dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Quiesce timeout",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + } + else { + dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Slot Quiesced",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + } + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + else { + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + // + // Cancel request, grab Command MUTEX to turn Power LED back ON + // + dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_available; + } + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_to_slot_enabled_wait_for_led_cmd_available() +// +// **************************************************************************** +long +hp_to_slot_enabled_wait_for_led_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_available_mutex + // + else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Turn Power LED back ON + // + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_LED_ON; + command_reg.Slot.attention_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_SLOT_NO_CHANGE; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete (while holding MUTEX) + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_completion; + } + // + // exit_request_event + // + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_to_slot_enabled_wait_for_led_cmd_completion() +// +// **************************************************************************** +long +hp_to_slot_enabled_wait_for_led_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context +) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_STATUS_WREG status_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , (int)shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER6_EVENT); + slot_context->slot_timer6.data = (unsigned long)slot_context; + slot_context->slot_timer6.function = hp_slot_timer6_func; + slot_context->slot_timer6.expires = jiffies + ONE_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer6); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER6_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer6); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_completion_event, timeout + // + else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) { + // + // Command completed OK? + // + status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET); + + if( status_reg.x.BSY == SHPC_STATUS_CLEARED && + status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED && + status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) { + // + // Wait for next request + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_slot_request; + } + else { + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + return status; +} + --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc_enb.c Tue Jun 24 13:31:13 2003 @@ -0,0 +1,2112 @@ +/* + * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2003 IBM Corp. + * Copyright (c) 2002-2003 Advanced Micro Devices + * + * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS + * AND CONDITIONS OF THE GNU GENERAL PUBLIC + * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS + * INCLUDED WITH THIS FILE AND POSTED AT + * http://www.gnu.org/licenses/gpl.html + * + * Send feedback to + * + */ + +#include +#include +#include "amdshpc_ddi.h" +#include "amdshpc.h" + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_slot_request() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_slot_request( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SLOT_STATUS_INFO slot_status; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Slot Disabled: complete pending slot request + // + if( slot_context->slot_completion.done ) { + dbg("%s -->DISABLE_DONE: slot_id[ %d:%d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + // + // Call Completion Callback() + // + hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status ); + slot_status.x.lu_request_failed = slot_context->slot_completion.failed; + shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + slot_context->slot_completion.type, + slot_status, + slot_context->slot_completion.request_context ); + + // + // Signal registered user EVENT + // + hp_signal_user_event( shpc_context ); + + // + // Clear completion flag + // + slot_context->slot_completion.done = FALSE; + } + + // + // Clear Button EVENT before waiting + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + hp_clear_slot_event_bit(slot_context, ATTN_BUTTON_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Wait for slot request + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) || + (slot_context->slot_event_bits & SLOT_REQUEST_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // attn_button_event + // + if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) { + // + // Set completion info for HW-initiated request + // + slot_context->slot_completion.hw_initiated = TRUE; + slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT; + slot_context->slot_completion.request_context = NULL; + + // + // Get current HW disposition + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + // + // Card present, MRL closed, and no Power-Fault? + // + if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY && + ( logical_slot_reg.x.MRLS_IM == SHPC_MASKED || + logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) && + logical_slot_reg.x.PF == SHPC_STATUS_CLEARED ) { + // + // Clear Alert EVENT and Attention LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + hp_clear_slot_event_bit(slot_context, ALERT_EVENT); + slot_context->problem_detected = FALSE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Grab Command MUTEX to blink Power LED + // + dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_led_cmd_available; + } + // + // Alert: MRL Opened, Power-Fault? + // + else if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) { + // + // Update Attention LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + logical_slot_reg.AsDWord & 0x3F ); + } + } + // + // SlotRequestEvent + // + else if(slot_context->slot_event_bits & SLOT_REQUEST_EVENT) { + // + // Set completion info for SW-initiated request + // + slot_context->slot_completion.hw_initiated = FALSE; + slot_context->slot_completion.type = slot_context->slot_request.type; + slot_context->slot_completion.request_context = slot_context->slot_request.request_context; + + // + // Request to enable slot? + // + if( slot_context->slot_request.type == SHPC_ASYNC_ENABLE_SLOT ) { + // + // Update alert events based on current HW disposition + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + // + // Card present, MRL closed, and no Power-Fault? + // + if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY && + (logical_slot_reg.x.MRLS_IM == SHPC_MASKED || + logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) && + logical_slot_reg.x.PF == SHPC_STATUS_CLEARED ) { + // + // Clear Alert EVENT and Attention LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = FALSE; + hp_clear_slot_event_bit(slot_context, ALERT_EVENT); + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Grab Command MUTEX to blink Power LED + // + dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_led_cmd_available; + } + // + // Alert: MRL Opened, Power-Fault? + // + else if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) { + // + // Update Attention LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + logical_slot_reg.AsDWord & 0x3F ); + } + } + else { + // + // Slot already disabled, just complete the request + // + dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.done = TRUE; + } + + // + // Allow next SW-initiated slot request while processing this one + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + hp_clear_slot_event_bit(slot_context, SLOT_REQUEST_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_led_cmd_available() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_led_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + // + // Wait for Command Available MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_REQUEST_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Wait for next request + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + (readl( slot_context->logical_slot_addr ) & 0x3F )); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request; + } + // + // cmd_available_mutex + // + else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Blink Power LED + // + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_LED_BLINK; + command_reg.Slot.attention_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_SLOT_NO_CHANGE; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord ,shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete (while holding MUTEX) + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_led_cmd_completion; + } + // + // exit_request_event + // + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_led_cmd_completion() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_led_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_STATUS_WREG status_reg; + + dbg( "%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER1_EVENT); + slot_context->slot_timer1.data = (unsigned long)slot_context; + slot_context->slot_timer1.function = hp_slot_timer1_func; + slot_context->slot_timer1.expires = jiffies + ONE_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer1); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER1_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer1); + } + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to make sure Power LED is OFF + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available; + } + // + // cmd_completion_event, timeout + // + else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) { + // + // Command completed OK? + // + status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET); + + if( status_reg.x.BSY == SHPC_STATUS_CLEARED && + status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) { + // + // Allow cancellation of operation? + // + if( slot_context->slot_completion.hw_initiated ) { + // + // Wait for 5 sec timeout + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_timeout; + } + else { + // + // Grab Command MUTEX to power-on the slot + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_available; + } + } + else { + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to make sure Power LED is OFF + // + dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available; + } + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_timeout() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Clear Button EVENT before waiting + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + hp_clear_slot_event_bit(slot_context, ATTN_BUTTON_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER2_EVENT); + slot_context->slot_timer2.data = (unsigned long)slot_context; + slot_context->slot_timer2.function = hp_slot_timer2_func; + slot_context->slot_timer2.expires = jiffies + FIVE_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer2); + + // + // Wait for 5 sec timeout + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER2_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER2_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer2); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to turn OFF Power LED + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available; + } + // + // attn_button_event + // + else if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) { + // + // Cancel request, grab Command MUTEX to turn OFF Power LED + // + dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ] Cancelled: Attn Button",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available; + } + // + // timeout + // + else if(slot_context->slot_event_bits & SLOT_TIMER2_EVENT) { + // + // Grab Command MUTEX to set slot at Power-Only state + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_available; + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_power_cmd_available() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_power_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to turn OFF Power LED + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available; + } + // + // cmd_available_mutex + // + else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Power-on the slot + // + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_led_NO_CHANGE; + command_reg.Slot.attention_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_POWER_ONLY; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for 100ms completion pre-amble on RevB-Errata (while holding MUTEX) + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_timeout; + } + // + // exit_request_event + // + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_power_cmd_timeout() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_power_cmd_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER3_EVENT); + slot_context->slot_timer3.data = (unsigned long)slot_context; + slot_context->slot_timer3.function = hp_slot_timer3_func; + slot_context->slot_timer3.expires = jiffies + ONE_TENTH_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer3); + + // + // Wait for 100ms completion pre-amble on RevB-Errata (while holding MUTEX) + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER3_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER3_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer3); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // timeout + // + else if(slot_context->slot_event_bits & SLOT_TIMER3_EVENT) { + // + // Wait for command to complete (while holding MUTEX) + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_completion; + } + // + // exit_request_event + // + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_power_cmd_completion() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_power_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_STATUS_WREG status_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER4_EVENT); + slot_context->slot_timer4.data = (unsigned long)slot_context; + slot_context->slot_timer4.function = hp_slot_timer4_func; + slot_context->slot_timer4.expires = jiffies + FIFTEEN_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer4); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER4_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER4_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer4); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_completion_event, timeout + // + else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER4_EVENT)) { + // + // Command completed OK? + // + status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET); + + if( status_reg.x.BSY == SHPC_STATUS_CLEARED && + status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED && + status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) { + // + // Grab Bus MUTEX to validate speed/mode + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_bus_available; + } + else { + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_bus_available() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_bus_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + enum shpc_speed_mode max_speed_mode, bus_speed_mode; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Bus Available MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->bus_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // bus_available_mutex + // + else if(shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) { + // + // Get current HW speed/mode + // + bus_speed_mode = hp_get_bus_speed_mode( shpc_context ); + max_speed_mode = hp_get_card_speed_mode( slot_context ); + if( max_speed_mode > shpc_context->max_speed_mode ) { + // + // Can only go as fast as the controller allows + // + max_speed_mode = shpc_context->max_speed_mode; + } + + // + // Grab global spinlock to check current speed/mode settings + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Other slots in contetion for bus speed/mode changes? + // + slot_context->in_bus_speed_mode_contention = FALSE; + max_speed_mode = hp_get_max_speed_mode( shpc_context, max_speed_mode ); + + // + // Make this card can handle PCI-66 speed/mode + // + if( max_speed_mode == SHPC_BUS_CONV_66 && !slot_context->card_pci66_capable ) { + // + // Fall back to slower common denominator + // + max_speed_mode = SHPC_BUS_CONV_33; + } + + // + // Bus running at incompatible speed/mode? + // + if( bus_speed_mode != max_speed_mode ) { + // + // Other slots already enabled? + // + if( hp_signal_enabled_slots_to_rebalance_bus( shpc_context )) { + // + // Wait for enabled slots to release the bus, then change bus speed/mode + // + shpc_context->bus_speed_mode = max_speed_mode; + shpc_context->bus_released = FALSE; + hp_clear_shpc_event_bit(shpc_context, BUS_COMPLETE_EVENT); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_bus_released; + } + else { + // + // Change bus speed/mode to enable this slot + // + shpc_context->bus_speed_mode = max_speed_mode; + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_speed_mode_cmd_available; + } + } + else { + // + // Enable slot at current bus speed/mode + // + shpc_context->bus_speed_mode = bus_speed_mode; + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_cmd_available; + } + + // + // Flag this slot in contention for bus speed/mode validation + // + slot_context->in_bus_speed_mode_contention = TRUE; + + // + // Release global spinlock since we're done checking speed/mode + // + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + dbg("%s -->ENABLE_IN_PROGRESS: slot_id[ %d:%d ] card_speed_mode[ %d+%d ] bus_speed_mode[ %d=>%d ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + slot_context->card_speed_mode, slot_context->card_pci66_capable, + bus_speed_mode, shpc_context->bus_speed_mode ); + } + // + // exit_request_event + // + else { + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_bus_released() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_bus_released( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Bus Release EVENT while holding MUTEX + // + shpc_context->shpc_event_bits = 0;slot_context->slot_event_bits = 0; + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & BUS_RELEASE_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // bus_release_event + // + else if(shpc_context->shpc_event_bits & BUS_RELEASE_EVENT) { + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + if( shpc_context->bus_released ) { + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + // + // Grab Command MUTEX to set Bus speed/mode + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_speed_mode_cmd_available; + } + else { + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1 ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + } + // + // exit_request_event + // + else { + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + status =STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_speed_mode_cmd_available() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_speed_mode_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Bus, Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_available_mutex + // + else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Set Bus speed/mode + // + command_reg.Bus.code = SHPC_SET_BUS_SPEED_MODE; + command_reg.Bus.speed_mode = shpc_context->bus_speed_mode; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete (while holding MUTEX) + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_speed_mode_cmd_completion; + } + // + // exit_request_event + // + else { + // + // Release Bus, Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + status =STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_speed_mode_cmd_completion() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_speed_mode_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_STATUS_WREG status_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER5_EVENT); + slot_context->slot_timer5.data = (unsigned long)slot_context; + slot_context->slot_timer5.function = hp_slot_timer5_func; + slot_context->slot_timer5.expires = jiffies + FIFTEEN_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer5); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER5_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer5); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_completion_event, timeout + // + else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) { + + // + // Command completed OK? + // + status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET); + + if( status_reg.x.BSY == SHPC_STATUS_CLEARED && + status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED && + status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED && + status_reg.x.INVSM_ERR == SHPC_STATUS_CLEARED ) { + // + // Grab Command MUTEX to enable slot + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_cmd_available; + } + else { + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + } + // + // exit_request_event + // + else { + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + status =STATUS_UNSUCCESSFUL; + } + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_enable_cmd_available() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_enable_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Bus, Command MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ , + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_available_mutex + // + else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Enable the slot + // + dbg("%s ENABLING SLOT...",__FUNCTION__ ); + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_LED_ON; + command_reg.Slot.attention_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_ENABLE_SLOT; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete (while holding Bus,Command MUTEX) + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_cmd_completion; + } + // + // exit_request_event + // + else { + // + // Release Bus, Command MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_enable_cmd_completion() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_enable_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_STATUS_WREG status_reg; + + dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER6_EVENT); + slot_context->slot_timer6.data = (unsigned long)slot_context; + slot_context->slot_timer6.function = hp_slot_timer6_func; + slot_context->slot_timer6.expires = jiffies + FIFTEEN_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer6); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER6_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer6); + } + + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // cmd_completion_event, timeout + // + else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) { + // + // Command completed OK? + // + status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET); + + if( status_reg.x.BSY == SHPC_STATUS_CLEARED && + status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED && + status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED && + status_reg.x.INVSM_ERR == SHPC_STATUS_CLEARED ) { + // + // Wait for settling time + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_timeout; + } + else { + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + } + // + // exit_request_event + // + else { + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + status = STATUS_UNSUCCESSFUL; + } + // + // Release Command MUTEX + // + up( &shpc_context->cmd_available_mutex); + + return status; +} + + +// **************************************************************************** +// +// hp_at_slot_disabled_wait_for_enable_timeout() +// +// **************************************************************************** +long +hp_at_slot_disabled_wait_for_enable_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER7_EVENT); + slot_context->slot_timer7.data = (unsigned long)slot_context; + slot_context->slot_timer7.function = hp_slot_timer7_func; + slot_context->slot_timer7.expires = jiffies + ONE_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer7); + + // + // Wait for timeout + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & ALERT_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER7_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER7_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer7); + } + // + // Alert: MRL Opened, Card Removed, Power-Fault? + // + if(slot_context->slot_event_bits & ALERT_EVENT) { + + // + // Update attn_led_problem_event LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + slot_context->problem_detected = TRUE; + hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Fail on-going request + // + slot_context->slot_completion.failed = HP_TRUE; + slot_context->slot_completion.done = TRUE; + + // + // Grab Command MUTEX to disable slot + // + dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1, + readl( slot_context->logical_slot_addr ) & 0x3F ); + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available; + } + // + // timeout + // + else if(slot_context->slot_event_bits & SLOT_TIMER7_EVENT) { + // + // Flag this slot as ENABLED + // + hp_flag_slot_as_enabled( shpc_context, slot_context ); + + // + // Complete succesful ENABLE request + // + slot_context->slot_completion.failed = HP_FALSE; + slot_context->slot_completion.done = TRUE; + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_slot_request; + } + // + // exit_request_event + // + else { + status =STATUS_UNSUCCESSFUL; + } + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + return status; +} + + +// **************************************************************************** +// +// hp_to_slot_disabled_wait_for_led_cmd_available() +// +// **************************************************************************** +long +hp_to_slot_disabled_wait_for_led_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Turn OFF Power LED + // + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_LED_OFF; + command_reg.Slot.attention_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_SLOT_NO_CHANGE; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for Power LED command to complete + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_completion; + } + else { // exit_request_event + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + +// **************************************************************************** +// +// hp_to_slot_disabled_wait_for_led_cmd_completion() +// +// **************************************************************************** +long +hp_to_slot_disabled_wait_for_led_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER8_EVENT); + slot_context->slot_timer8.data = (unsigned long)slot_context; + slot_context->slot_timer8.function = hp_slot_timer8_func; + slot_context->slot_timer8.expires = jiffies + ONE_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer8); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER8_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER8_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer8); + } + + if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER8_EVENT)) { + // + // Wait for next request + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request; + + }else{ // exit_request_event + status = STATUS_UNSUCCESSFUL; + } + + // + // Release command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + return status; +} + + +// **************************************************************************** +// +// hp_to_slot_disabled_wait_for_disable_cmd_available() +// +// **************************************************************************** +long +hp_to_slot_disabled_wait_for_disable_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Disable slot and turn OFF Power LED + // + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_LED_OFF; + command_reg.Slot.attention_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_DISABLE_SLOT; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_completion; + } + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_to_slot_disabled_wait_for_disable_cmd_completion() +// +// **************************************************************************** +long +hp_to_slot_disabled_wait_for_disable_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER9_EVENT); + slot_context->slot_timer9.data = (unsigned long)slot_context; + slot_context->slot_timer9.function = hp_slot_timer9_func; + slot_context->slot_timer9.expires = jiffies + FIFTEEN_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer9); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER9_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER9_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer9); + } + + if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & SLOT_TIMER9_EVENT)) { + // + // Flag this slot as DISABLED (if enabled) + // + if( hp_flag_slot_as_disabled( shpc_context, slot_context )) { + // + // Wait for settling time + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_timeout; + } + else { + // + // Wait for next request + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request; + } + } + else { // exit_request_event + status = STATUS_UNSUCCESSFUL; + } + // + // Release command MUTEX + // + hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->cmd_release_event); + + return status; +} + +// **************************************************************************** +// +// hp_to_slot_disabled_wait_for_DisableTimeout() +// +// **************************************************************************** +long +hp_to_slot_disabled_wait_for_disable_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, SLOT_TIMER10_EVENT); + slot_context->slot_timer10.data = (unsigned long)slot_context; + slot_context->slot_timer10.function = hp_slot_timer10_func; + slot_context->slot_timer10.expires = jiffies + ONE_SEC_TIMEOUT; + add_timer(&slot_context->slot_timer10); + + // + // Wait for timeout + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & SLOT_TIMER10_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & SLOT_TIMER10_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->slot_timer10); + } + + // + // timeout + // + if(slot_context->slot_event_bits & SLOT_TIMER10_EVENT) { + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_bus_available; + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_to_slot_disabled_wait_for_bus_available() +// +// **************************************************************************** +long +hp_to_slot_disabled_wait_for_bus_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + enum shpc_speed_mode max_speed_mode; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Bus Available MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->bus_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if(shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) { + // + // Grab global spinlock to check current speed/mode settings + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Flag this slot out of contetion for bus speed/mode changes + // + slot_context->in_bus_speed_mode_contention = FALSE; + + // + // Enabled slots running at maximum speed/mode? + // + if( shpc_context->slots_enabled ) { + max_speed_mode = hp_get_max_speed_mode( shpc_context, shpc_context->max_speed_mode ); + + // + // Signal enabled slots to release the bus, then change bus speed/mode + // + if( shpc_context->bus_speed_mode != max_speed_mode ) { + hp_signal_enabled_slots_to_rebalance_bus( shpc_context ); + } + } + + // + // Release global spinlock since we're done checking speed/mode + // + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Wait for next request on this slot + // + slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request; + } + else { // exit_request_event + status = STATUS_UNSUCCESSFUL; + } + + // + // Release Bus MUTEX + // + hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT); + wake_up_interruptible(&slot_context->bus_release_event); + + return status; +} + --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc_int.c Tue Jun 24 13:31:13 2003 @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2003 IBM Corp. + * Copyright (c) 2002-2003 Advanced Micro Devices + * + * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS + * AND CONDITIONS OF THE GNU GENERAL PUBLIC + * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS + * INCLUDED WITH THIS FILE AND POSTED AT + * http://www.gnu.org/licenses/gpl.html + * + * Send feedback to + * + */ + +#include +#include +#include "amdshpc_ddi.h" +#include "amdshpc.h" + + +// **************************************************************************** +// +// hp_interrupt_service() +// +// **************************************************************************** +irqreturn_t hp_interrupt_service(int IRQ, void *v, struct pt_regs *regs) +{ + struct shpc_context *shpc_context = v; + struct slot_context *slot_context; + union SHPC_SERR_INT_DWREG SerrIntReg; + union SHPC_INT_LOCATOR_DWREG IntLocatorReg, SlotIndex; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + u8 IsShpcInterrupt = FALSE; + u8 i; + + // + // Device at PowerDeviceD0? + // + if( !shpc_context->at_power_device_d0 ) { + return IRQ_HANDLED; + } + + // + // Read Interrupt Locator Register ( Pending Interrupts ) + // + IntLocatorReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_INT_LOCATOR_REG_OFFSET); + + // + // Read SERR-INT Register ( Global Mask, Command Completion ) + // + SerrIntReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET); + + // + // Global Interrupts Disabled? + // +// if( SerrIntReg.x.GIM == SHPC_MASKED ) { +// return FALSE; +// } + + // + // Command Completion? + // + if( IntLocatorReg.x.CC_IP ) { + if(( SerrIntReg.x.CC_STS == SHPC_STATUS_SET ) && + ( SerrIntReg.x.CC_IM == SHPC_UNMASKED )) { + // + // Schedule Dpc + // + IsShpcInterrupt = TRUE; + tasklet_schedule(&shpc_context->cmd_completion_dpc); + + // + // Clear Interrput (Write-back 1 to STS bits) + // + writel(SerrIntReg.AsDWord, shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET); + } + } + + // + // Slot Interrupts? + // + if( IntLocatorReg.x.SLOT_IP ) { + // + // Walk a "1" thru each bit position (one bit per slot) + // + for( i=0, SlotIndex.x.SLOT_IP = 1; i< SHPC_MAX_NUM_SLOTS; ++i, SlotIndex.x.SLOT_IP <<= 1 ) { + slot_context = &shpc_context->slot_context[ i ]; + + // + // Interrupt from this slot? + // + if( IntLocatorReg.x.SLOT_IP & SlotIndex.x.SLOT_IP ) { + // + // Read Logical Slot Register + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + + // + // Attention Button? + // + if(( logical_slot_reg.x.ABP_STS == SHPC_STATUS_SET ) && + ( logical_slot_reg.x.AB_IM == SHPC_UNMASKED )) { + // + // Schedule Dpc + // + IsShpcInterrupt = TRUE; + tasklet_schedule(&slot_context->attn_button_dpc); + } + + // + // MRL Sensor? + // + if(( logical_slot_reg.x.MRLSC_STS == SHPC_STATUS_SET ) && + ( logical_slot_reg.x.MRLS_IM == SHPC_UNMASKED )) { + // + // Schedule Dpc + // + IsShpcInterrupt = TRUE; + tasklet_schedule(&slot_context->mrl_sensor_dpc); + } + + // + // Card Presence Change? + // + if(( logical_slot_reg.x.CPC_STS == SHPC_STATUS_SET ) && + ( logical_slot_reg.x.CP_IM == SHPC_UNMASKED )) { + // + // Schedule Dpc + // + IsShpcInterrupt = TRUE; + tasklet_schedule(&slot_context->card_presence_dpc); + } + + // + // Isolated Power Fault? + // + if(( logical_slot_reg.x.IPF_STS == SHPC_STATUS_SET ) && + ( logical_slot_reg.x.IPF_IM == SHPC_UNMASKED )) { + // + // Schedule Dpc + // + IsShpcInterrupt = TRUE; + tasklet_schedule(&slot_context->isolated_power_fault_dpc); + } + + // + // Connected Power Fault? + // + if(( logical_slot_reg.x.CPF_STS == SHPC_STATUS_SET ) && + ( logical_slot_reg.x.CPF_IM == SHPC_UNMASKED )) { + // + // Schedule Dpc + // + IsShpcInterrupt = TRUE; + tasklet_schedule(&slot_context->connected_power_fault_dpc); + } + + // + // Clear Interrputs for this slot (Write-back 1 to STS bits) + // + writel(logical_slot_reg.AsDWord, slot_context->logical_slot_addr); + } + } + } + return IRQ_HANDLED; +} + + +// **************************************************************************** +// +// hp_attn_button_dpc() @ DISPATCH_LEVEL +// +// **************************************************************************** +void +hp_attn_button_dpc( + unsigned long deferred_context // struct slot_context* + ) +{ + struct slot_context* slot_context = ( struct slot_context* )deferred_context; + struct shpc_context* shpc_context = slot_context->shpc_context; + + dbg("%s ->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, (slot_context->slot_number-1) ); + // + // Notification Event: Attention Button pressed + // + spin_lock( &slot_context->slot_spinlock ); + hp_send_slot_event(slot_context, ATTN_BUTTON_EVENT); + spin_unlock( &slot_context->slot_spinlock ); +} + + +// **************************************************************************** +// +// hp_card_presence_dpc() @ DISPATCH_LEVEL +// +// **************************************************************************** +void +hp_card_presence_dpc( + unsigned long deferred_context // struct slot_context* + ) +{ + struct slot_context* slot_context = ( struct slot_context* )deferred_context; + struct shpc_context* shpc_context = slot_context->shpc_context; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Signal registered user EVENT + // + hp_signal_user_event_at_dpc_level( shpc_context ); + + // + // Card Removed? + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + if( logical_slot_reg.x.PRSNT1_2 == SHPC_SLOT_EMPTY ) { + // + // Signal Alert EVENT + // + spin_lock( &slot_context->slot_spinlock ); + hp_send_slot_event(slot_context, ALERT_EVENT); + spin_unlock( &slot_context->slot_spinlock ); + } +} + + +// **************************************************************************** +// +// hp_mrl_sensor_dpc() @ DISPATCH_LEVEL +// +// **************************************************************************** +void +hp_mrl_sensor_dpc( + unsigned long deferred_context // struct slot_context* + ) +{ + struct slot_context* slot_context = ( struct slot_context* )deferred_context; + struct shpc_context* shpc_context = slot_context->shpc_context; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Signal registered user EVENT + // + hp_signal_user_event_at_dpc_level( shpc_context ); + + // + // MRL Sensor opened? + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + if( logical_slot_reg.x.MRLS == SHPC_MRL_OPEN ) { + // + // Card Present? + // + if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) { + // + // Signal Alert EVENT + // + spin_lock( &slot_context->slot_spinlock ); + hp_send_slot_event(slot_context, ALERT_EVENT); + spin_unlock( &slot_context->slot_spinlock ); + } + } + else { + // + // Power Fault detected whith MRL closed? + // Note: Golem A0 may not generate power-fault interrupt + if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) { + // + // Signal Alert EVENT + // + spin_lock( &slot_context->slot_spinlock ); + hp_send_slot_event(slot_context, ALERT_EVENT); + spin_unlock( &slot_context->slot_spinlock ); + } + } +} + +// **************************************************************************** +// +// isolated_power_fault_dpc() @ DISPATCH_LEVEL +// +// **************************************************************************** +void +hp_isolated_power_fault_dpc( + unsigned long deferred_context // struct slot_context* + ) +{ + struct slot_context* slot_context = ( struct slot_context* )deferred_context; + struct shpc_context* shpc_context = slot_context->shpc_context; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Signal registered user EVENT + // + hp_signal_user_event_at_dpc_level( shpc_context ); + + // + // Power Fault detected? + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) { + // + // Signal Alert EVENT + // + spin_lock( &slot_context->slot_spinlock ); + hp_send_slot_event(slot_context, ALERT_EVENT); + spin_unlock( &slot_context->slot_spinlock ); + } +} + + +// **************************************************************************** +// +// connected_power_fault_dpc() @ DISPATCH_LEVEL +// +// **************************************************************************** +void +hp_connected_power_fault_dpc( + unsigned long deferred_context // struct slot_context* + ) +{ + struct slot_context* slot_context = ( struct slot_context* )deferred_context; + struct shpc_context* shpc_context = slot_context->shpc_context; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Signal registered user EVENT + // + hp_signal_user_event_at_dpc_level( shpc_context ); + + // + // Power Fault detected? + // + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) { + // + // Signal Alert EVENT + // + spin_lock( &slot_context->slot_spinlock ); + hp_send_slot_event(slot_context, ALERT_EVENT); + spin_unlock( &slot_context->slot_spinlock ); + } +} + + +// **************************************************************************** +// +// hp_cmd_completion_dpc() @ DISPATCH_LEVEL +// +// **************************************************************************** +void +hp_cmd_completion_dpc( + unsigned long deferred_context // struct shpc_context* + ) +{ + struct shpc_context* shpc_context = ( struct shpc_context* )deferred_context; + + dbg("%s -->HwInstance[ %d ]", __FUNCTION__, shpc_context->shpc_instance ); + + // + // Notification Event: Command Completion + // + spin_lock( &shpc_context->shpc_spinlock ); + hp_send_event_to_all_slots(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock( &shpc_context->shpc_spinlock ); +} --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc_led.c Tue Jun 24 13:31:13 2003 @@ -0,0 +1,742 @@ +/* + * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2003 IBM Corp. + * Copyright (c) 2002-2003 Advanced Micro Devices + * + * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS + * AND CONDITIONS OF THE GNU GENERAL PUBLIC + * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS + * INCLUDED WITH THIS FILE AND POSTED AT + * http://www.gnu.org/licenses/gpl.html + * + * Send feedback to + * + */ + +#include +#include +#include "amdshpc_ddi.h" +#include "amdshpc.h" + + +// **************************************************************************** +// +// hp_wait_for_attn_led_request() @ PASSIVE_LEVEL +// +// **************************************************************************** +long +hp_wait_for_attn_led_request( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SLOT_STATUS_INFO slot_status; + union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // LED "Normal": complete pending request + // + if( slot_context->attn_led_completion.done ) { + // + // Call Completion Callback() + // + hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status ); + slot_status.x.lu_request_failed = slot_context->slot_completion.failed; + shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + slot_context->attn_led_completion.type, + slot_status, + slot_context->attn_led_completion.request_context ); + + // + // Signal registered user EVENT + // + hp_signal_user_event( shpc_context ); + + // + // Clear completion flag + // + slot_context->attn_led_completion.done = FALSE; + } + + // + // Wait for slot request + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) || + (slot_context->slot_event_bits & ATTN_LED_PROBLEM_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // AttnLEDRequestEvent + if(slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) { + // + // Set completion info for SW-initiated request + // + slot_context->attn_led_completion.hw_initiated = FALSE; + slot_context->attn_led_completion.type = slot_context->attn_led_request.type; + slot_context->attn_led_completion.timeout = slot_context->attn_led_request.timeout; + slot_context->attn_led_completion.request_context = slot_context->attn_led_request.request_context; + + // + // Request to locate slot? + // + if( slot_context->attn_led_request.type == SHPC_ASYNC_LED_LOCATE ) { + dbg("%s -->LED_LOCATE_REQ: slot_id[ %d:%d ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Grab Command MUTEX to blink Attn LED + // + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_blink_cmd_available; + } + else { + dbg("%s -->LED_NORMAL_REQ: slot_id[ %d:%d ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + + logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr ); + if( logical_slot_reg.x.AIS == SHPC_LED_ON || logical_slot_reg.x.AIS == SHPC_LED_OFF ) { + // + // Already "Normal", just complete the request + // + slot_context->attn_led_completion.failed = HP_FALSE; + slot_context->attn_led_completion.done = TRUE; + } + // + // While waitimg on a request here, the Attn LED should already be On/Off, but... + // + else { + // + // Grab Command MUTEX to set Attn LED to "Normal" (On/Off) state + // + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_available; + } + } + + // + // Allow next SW-initiated request while processing this one + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + hp_clear_slot_event_bit(slot_context, ATTN_LED_REQUEST_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + } + // attn_led_problem_event: Detected, Resolved + else if (slot_context->slot_event_bits & ATTN_LED_PROBLEM_EVENT){ + // + // Set completion info for HW-initiated request + // + slot_context->attn_led_completion.hw_initiated = TRUE; + slot_context->attn_led_completion.type = SHPC_ASYNC_LED_NORMAL; + slot_context->attn_led_completion.timeout = 0; + slot_context->attn_led_completion.request_context = NULL; + + // + // Grab Command MUTEX to update Attention LED (On/Off) + // + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_available; + } + else { // exit_request_event + status = STATUS_UNSUCCESSFUL; + dbg("%s -->EXIT_REQUEST: slot_id[ %d:%d ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + } + return status; +} + + +// **************************************************************************** +// +// hp_wait_for_attn_led_blink_cmd_available() @ PASSIVE_LEVEL +// +// **************************************************************************** +long +hp_wait_for_attn_led_blink_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + //down_interruptible(&slot_context->cmd_acquire_mutex); + hp_set_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->led_cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // cmd_available_mutex + if(shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Blink Attention LED + // + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_led_NO_CHANGE; + command_reg.Slot.attention_led = SHPC_LED_BLINK; + command_reg.Slot.state = SHPC_SLOT_NO_CHANGE; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete (while holding MUTEX) + // + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_blink_cmd_completion; + } + // exit_request_event + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->led_cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_wait_for_attn_led_blink_cmd_completion() @ PASSIVE_LEVEL +// +// **************************************************************************** +long +hp_wait_for_attn_led_blink_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + long status = STATUS_SUCCESS; + union SLOT_STATUS_INFO slot_status; + union SHPC_STATUS_WREG status_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, LED_TIMER1_EVENT); + slot_context->led_timer1.data = (unsigned long)slot_context; + slot_context->led_timer1.function = hp_led_timer1_func; + slot_context->led_timer1.expires = jiffies + ONE_SEC_TIMEOUT; + add_timer(&slot_context->led_timer1); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & LED_TIMER1_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & LED_TIMER1_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->led_timer1); + } + + if(shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) { + // cmd_completion_event, timeout + if ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || (slot_context->slot_event_bits & LED_TIMER1_EVENT)) { + // + // Command completed OK? + // + status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET); + + if( status_reg.x.BSY == SHPC_STATUS_CLEARED && + status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) { + // + // Call Completion Callback() + // + hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status ); + slot_status.x.lu_request_failed = HP_FALSE; + shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + SHPC_ASYNC_LED_LOCATE, + slot_status, + slot_context->attn_led_completion.request_context ); + + // + // Signal registered user EVENT + // + hp_signal_user_event( shpc_context ); + + // + // Wait for specified timeout (in seconds) + // + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_blink_timeout; + } + else { + // + // Fail on-going request + // + slot_context->attn_led_completion.failed = HP_TRUE; + slot_context->attn_led_completion.done = TRUE; + + // + // Grab Command MUTEX to make sure Attn LED gets back to "Normal" (On/Off) + // + dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord ); + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_back_to_normal_cmd_available; + } + + // exit_request_event + } + else { + status = STATUS_UNSUCCESSFUL; + } + } + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->led_cmd_release_event); + + return status; +} + +// **************************************************************************** +// +// hp_wait_for_attn_led_blink_timeout() @ PASSIVE_LEVEL +// +// **************************************************************************** +long +hp_wait_for_attn_led_blink_timeout( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SLOT_STATUS_INFO slot_status; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, LED_TIMER2_EVENT); + slot_context->led_timer2.data = (unsigned long)slot_context; + slot_context->led_timer2.function = hp_led_timer2_func; + slot_context->led_timer2.expires = jiffies + (ONE_SEC_INCREMENT * slot_context->attn_led_completion.timeout); + add_timer(&slot_context->led_timer2); + + // + // Wait for specified timeout ( in seconds ) + // + wait_event_interruptible(slot_context->slot_event, + ((slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) || + (slot_context->slot_event_bits & LED_TIMER2_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & LED_TIMER2_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + hp_clear_slot_event_bit(slot_context, LED_TIMER2_EVENT); + del_timer_sync(&slot_context->led_timer2); + } + + // AttnLEDRequestEvent + if(slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) { + // + // Set completion info for SW-initiated request + // + slot_context->attn_led_completion.hw_initiated = FALSE; + slot_context->attn_led_completion.type = slot_context->attn_led_request.type; + slot_context->attn_led_completion.timeout = slot_context->attn_led_request.timeout; + slot_context->attn_led_completion.request_context = slot_context->attn_led_request.request_context; + + // + // Request to cancel locate? + // + if( slot_context->attn_led_request.type == SHPC_ASYNC_LED_NORMAL ) { + dbg("%s -->LED_NORMAL_REQ: slot_id[ %d:%d ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Grab Command MUTEX to set Attn LED at "Normal" (On/Off) state + // + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)&hp_wait_for_attn_led_normal_cmd_available; + + // + // Allow next SW-initiated request while processing this one + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + hp_clear_slot_event_bit(slot_context, ATTN_LED_REQUEST_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + } + // + // Already located (Attn LED blinking), just re-start timeout + // + else { + dbg("%s -->LED_LOCATE_REQ: slot_id[ %d:%d ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Allow next SW-initiated request before invoking callback, since next + // request may be sent in the context of this thread. + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + hp_clear_slot_event_bit(slot_context, ATTN_LED_REQUEST_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + + // + // Call Completion Callback() + // + hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status ); + slot_status.x.lu_request_failed = HP_FALSE; + shpc_context->async_callback( + shpc_context->driver_context, + slot_context->slot_number - 1, + SHPC_ASYNC_LED_LOCATE, + slot_status, + slot_context->attn_led_completion.request_context ); + + // + // Signal registered user EVENT + // + hp_signal_user_event( shpc_context ); + } + } + // timeout + else if (slot_context->slot_event_bits & LED_TIMER2_EVENT) { + // + // Set completion info for HW-initiated request + // + slot_context->attn_led_completion.hw_initiated = TRUE; + slot_context->attn_led_completion.type = SHPC_ASYNC_LED_NORMAL; + slot_context->attn_led_completion.timeout = 0; + slot_context->attn_led_completion.request_context = NULL; + + // + // Grab Command MUTEX to set Attn LED at "Normal" (On/Off) state + // + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_available; + + } + // exit_request_event + else { + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_wait_for_attn_led_normal_cmd_available() @ PASSIVE_LEVEL +// +// **************************************************************************** +long +hp_wait_for_attn_led_normal_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + //down_interruptible(&slot_context->cmd_acquire_mutex); + hp_set_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->led_cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // cmd_available_mutex + // + if(shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Update Attention LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + command_reg.Slot.attention_led = slot_context->problem_detected ? + SHPC_LED_ON : SHPC_LED_OFF; + hp_clear_slot_event_bit(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_SLOT_NO_CHANGE; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete (while holding MUTEX) + // + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_completion; + } + // + // exit_request_event + // + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->led_cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_wait_for_attn_led_normal_cmd_completion() @ PASSIVE_LEVEL +// +// **************************************************************************** +long +hp_wait_for_attn_led_normal_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + long status = STATUS_SUCCESS; + union SHPC_STATUS_WREG status_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, LED_TIMER3_EVENT); + slot_context->led_timer3.data = (unsigned long)slot_context; + slot_context->led_timer3.function = hp_led_timer3_func; + slot_context->led_timer3.expires = jiffies + ONE_SEC_TIMEOUT; + add_timer(&slot_context->led_timer3); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & LED_TIMER3_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & LED_TIMER3_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->led_timer3); + } + + // + // cmd_completion_event, timeout + // + if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & LED_TIMER3_EVENT)) { + // + // Command completed OK? + // + status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET); + + if( status_reg.x.BSY == SHPC_STATUS_CLEARED && + status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) { + // + // Complete succesful ENABLE request + // + slot_context->attn_led_completion.failed = HP_FALSE; + slot_context->attn_led_completion.done = TRUE; + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_request; + } + else { + // + // Fail on-going request + // + slot_context->attn_led_completion.failed = HP_TRUE; + slot_context->attn_led_completion.done = TRUE; + + // + // Grab Command MUTEX to make sure Attn LED gets back to "Normal" (On/Off) + // + dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]", __FUNCTION__, + shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord ); + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_back_to_normal_cmd_available; + } + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->led_cmd_release_event); + + return status; +} + + +// **************************************************************************** +// +// hp_wait_for_attn_led_back_to_normal_cmd_available() @ PASSIVE_LEVEL +// +// **************************************************************************** +long +hp_wait_for_attn_led_back_to_normal_cmd_available( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + unsigned long old_irq_flags; + long status = STATUS_SUCCESS; + union SHPC_COMMAND_WREG command_reg; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Wait for Command Available MUTEX + // + //down_interruptible(&slot_context->cmd_acquire_mutex); + hp_set_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT); + wake_up_interruptible(&slot_context->led_cmd_acquire_event); + + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + // + // cmd_available_mutex + // + if(shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) { + // + // Clear Completion EVENT before issuing next command + // + spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags ); + hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT); + spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags ); + + // + // Update Attention LED + // + spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags ); + command_reg.Slot.attention_led = slot_context->problem_detected ? + SHPC_LED_ON : SHPC_LED_OFF; + hp_clear_slot_event_bit(slot_context, ATTN_LED_PROBLEM_EVENT); + spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags ); + command_reg.Slot.code = SHPC_SLOT_OPERATION; + command_reg.Slot.power_led = SHPC_led_NO_CHANGE; + command_reg.Slot.state = SHPC_SLOT_NO_CHANGE; + command_reg.Slot.TGT = slot_context->slot_number; + writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET); + + // + // Wait for command to complete (while holding MUTEX) + // + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_back_to_normal_cmd_completion; + } + // + // exit_request_event + // + else { + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->led_cmd_release_event); + status = STATUS_UNSUCCESSFUL; + } + return status; +} + + +// **************************************************************************** +// +// hp_wait_for_attn_led_back_to_normal_cmd_completion() @ PASSIVE_LEVEL +// +// **************************************************************************** +long +hp_wait_for_attn_led_back_to_normal_cmd_completion( + struct shpc_context* shpc_context, + struct slot_context* slot_context + ) +{ + long status = STATUS_SUCCESS; + + dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 ); + + // + // Setup our timer + // + hp_clear_slot_event_bit(slot_context, LED_TIMER4_EVENT); + slot_context->led_timer4.data = (unsigned long)slot_context; + slot_context->led_timer4.function = hp_led_timer4_func; + slot_context->led_timer4.expires = jiffies + ONE_SEC_TIMEOUT; + add_timer(&slot_context->led_timer4); + + // + // Wait for Command Completion EVENT while holding MUTEX + // + wait_event_interruptible(slot_context->slot_event, + ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & LED_TIMER4_EVENT) || + (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT))); + + if (!(slot_context->slot_event_bits & LED_TIMER4_EVENT)) { + // + // delete the timer because we got an event other than the timer + // + del_timer_sync(&slot_context->led_timer4); + } + + // + // cmd_completion_event, timeout + // + if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || + (slot_context->slot_event_bits & LED_TIMER4_EVENT)) { + slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_request; + } + // + // exit_request_event + // + else { + status = STATUS_UNSUCCESSFUL; + } + // + // Release Command MUTEX + // + hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT); + wake_up_interruptible(&slot_context->led_cmd_release_event); + + return status; +} --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/amdshpc_pci.c Tue Jun 24 13:31:13 2003 @@ -0,0 +1,3513 @@ +/* + * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2003 IBM Corp. + * Copyright (c) 2002-2003 Advanced Micro Devices + * + * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS + * AND CONDITIONS OF THE GNU GENERAL PUBLIC + * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS + * INCLUDED WITH THIS FILE AND POSTED AT + * http://www.gnu.org/licenses/gpl.html + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "amdshpc.h" +#include "../pci.h" +#include "../../../arch/i386/pci/pci.h" + +u8 amdshpc_nic_irq; +u8 amdshpc_disk_irq; + +static u16 unused_IRQ; + +extern struct controller *amdshpc_ctrl_list; /* = NULL */ +extern struct pci_func *amdshpc_slot_list[256]; + +static int bridge_slot_remove(struct pci_func *bridge); +static int is_bridge(struct pci_func * func); +static int update_slot_info (struct controller *ctrl, struct slot *slot); +static int slot_remove(struct pci_func * old_slot); +static u32 configure_new_device(struct controller * ctrl, struct pci_func *func,u8 behind_bridge, struct resource_lists *resources); +static int configure_new_function(struct controller * ctrl, struct pci_func *func,u8 behind_bridge, struct resource_lists *resources); +int amdshpc_process_SI (struct controller *ctrl, struct pci_func *func); + +static u16 unused_IRQ; + +/** + * board_added - Called after a board has been added to the system. + * + * Turns power on for the board + * Configures board + * + */ +static u32 board_added(struct pci_func * func, struct controller * ctrl) +{ + int index; + u32 temp_register = 0xFFFFFFFF; + u32 rc = 0; + struct pci_func *new_slot = NULL; + struct resource_lists res_lists; + + dbg("%s: func->device, slot_offset = %d, %d \n",__FUNCTION__, + func->device, ctrl->slot_device_offset); + + // Get vendor/device ID u32 + rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register); + dbg("%s: pci_bus_read_config_dword returns %d\n",__FUNCTION__, rc); + dbg("%s: temp_register is %x\n",__FUNCTION__, temp_register); + + if (rc != 0) { + // Something's wrong here + temp_register = 0xFFFFFFFF; + dbg("%s: temp register set to %x by error\n",__FUNCTION__, temp_register); + } + // Preset return code. It will be changed later if things go okay. + rc = NO_ADAPTER_PRESENT; + + // All F's is an empty slot or an invalid board + if (temp_register != 0xFFFFFFFF) { // Check for a board in the slot + res_lists.io_head = ctrl->io_head; + res_lists.mem_head = ctrl->mem_head; + res_lists.p_mem_head = ctrl->p_mem_head; + res_lists.bus_head = ctrl->bus_head; + res_lists.irqs = NULL; + + rc = configure_new_device(ctrl, func, 0, &res_lists); + + dbg("%s: back from configure_new_device\n",__FUNCTION__); + ctrl->io_head = res_lists.io_head; + ctrl->mem_head = res_lists.mem_head; + ctrl->p_mem_head = res_lists.p_mem_head; + ctrl->bus_head = res_lists.bus_head; + + amdshpc_resource_sort_and_combine(&(ctrl->mem_head)); + amdshpc_resource_sort_and_combine(&(ctrl->p_mem_head)); + amdshpc_resource_sort_and_combine(&(ctrl->io_head)); + amdshpc_resource_sort_and_combine(&(ctrl->bus_head)); + + if (rc) { + // Something went wrong; disable slot +// TO_DO_amd_disable_slot(); + return(rc); + } else { + amdshpc_save_slot_config(ctrl, func); + } + + + func->status = 0; + func->switch_save = 0x10; + func->is_a_board = 0x01; + + //next, we will instantiate the linux pci_dev structures (with appropriate driver notification, if already present) + dbg("%s: configure linux pci_dev structure\n",__FUNCTION__); + index = 0; + do { + new_slot = amdshpc_slot_find(ctrl->bus, func->device, index++); + if (new_slot && !new_slot->pci_dev) { + amdshpc_configure_device(ctrl, new_slot); + } + } while (new_slot); + } else { + // Something went wrong; disable slot +// TO_DO_amd_disable_slot(); + return(rc); + } + return 0; +} + + +/** + * remove_board - Returns resources + */ +static u32 remove_board(struct pci_func * func, u32 replace_flag, struct controller * ctrl) +{ + int index; + u8 skip = 0; + u8 device; + u8 hp_slot; + u32 rc; + struct resource_lists res_lists; + struct pci_func *temp_func; + + if (func == NULL) + return(1); + + if (amdshpc_unconfigure_device(func)) + return(1); + + device = func->device; + + hp_slot = func->device - ctrl->slot_device_offset; + dbg("In %s, hp_slot = %d\n",__FUNCTION__, hp_slot); + + // When we get here, it is safe to change base Address Registers. + // We will attempt to save the base Address Register Lengths + if (replace_flag || !ctrl->add_support) + rc = amdshpc_save_base_addr_length(ctrl, func); + else if (!func->bus_head && !func->mem_head && + !func->p_mem_head && !func->io_head) { + // Here we check to see if we've saved any of the board's + // resources already. If so, we'll skip the attempt to + // determine what's being used. + index = 0; + temp_func = amdshpc_slot_find(func->bus, func->device, index++); + while (temp_func) { + if (temp_func->bus_head || temp_func->mem_head + || temp_func->p_mem_head || temp_func->io_head) { + skip = 1; + break; + } + temp_func = amdshpc_slot_find(temp_func->bus, temp_func->device, index++); + } + + if (!skip) + rc = amdshpc_save_used_resources(ctrl, func); + } + // Change status to shutdown + if (func->is_a_board) + func->status = 0x01; + func->configured = 0; + +// TO_DO_amd_disable_slot(ctrl, hp_slot); + + if (!replace_flag && ctrl->add_support) { + while (func) { + res_lists.io_head = ctrl->io_head; + res_lists.mem_head = ctrl->mem_head; + res_lists.p_mem_head = ctrl->p_mem_head; + res_lists.bus_head = ctrl->bus_head; + + amdshpc_return_board_resources(func, &res_lists); + + ctrl->io_head = res_lists.io_head; + ctrl->mem_head = res_lists.mem_head; + ctrl->p_mem_head = res_lists.p_mem_head; + ctrl->bus_head = res_lists.bus_head; + + amdshpc_resource_sort_and_combine(&(ctrl->mem_head)); + amdshpc_resource_sort_and_combine(&(ctrl->p_mem_head)); + amdshpc_resource_sort_and_combine(&(ctrl->io_head)); + amdshpc_resource_sort_and_combine(&(ctrl->bus_head)); + + if (is_bridge(func)) { + bridge_slot_remove(func); + } else + slot_remove(func); + + func = amdshpc_slot_find(ctrl->bus, device, 0); + } + + // Setup slot structure with entry for empty slot + func = amdshpc_slot_create(ctrl->bus); + + if (func == NULL) { + // Out of memory + return(1); + } + + func->bus = ctrl->bus; + func->device = device; + func->function = 0; + func->configured = 0; + func->switch_save = 0x10; + func->is_a_board = 0; + func->p_task_event = NULL; + } + return 0; +} + + +/* + * find_slot + */ +static inline struct slot* find_slot (struct controller* ctrl, u8 device) +{ + struct slot *slot; + + dbg("%s", __FUNCTION__); + if (!ctrl) + return NULL; + + slot = ctrl->slot; + + while (slot && (slot->device != device)) { + slot = slot->next; + } + + return slot; +} + +// board insertion +int amdshpc_process_SI (struct controller *ctrl, struct pci_func *func) +{ + u8 device, hp_slot; + u16 temp_word; + u32 tempdword; + int rc; + struct slot* p_slot; + int physical_slot = 0; + + dbg("%s 0", __FUNCTION__); + if (!ctrl) + return(1); + + tempdword = 0; + + device = func->device; + hp_slot = device - ctrl->slot_device_offset; + p_slot = find_slot(ctrl, device); + if (p_slot) { + physical_slot = p_slot->number; + } + + if (tempdword & (0x01 << hp_slot)) { + dbg("%s 1", __FUNCTION__); + return(1); + } + + // add board + slot_remove(func); + + func = amdshpc_slot_create(ctrl->bus); + dbg("%s 2",__FUNCTION__); + if (func == NULL) { + dbg("%s 3",__FUNCTION__); + return(1); + } + + func->bus = ctrl->bus; + func->device = device; + func->function = 0; + func->configured = 0; + func->is_a_board = 1; + + // 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; + + dbg("%s 4",__FUNCTION__); + if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { + dbg("%s 5",__FUNCTION__); + func->switch_save = 0; + } else { + dbg("%s 6",__FUNCTION__); + func->switch_save = 0x10; + } + + rc = board_added(func, ctrl); + dbg("%s 7 rc=%d",__FUNCTION__,rc); + if (rc) { + dbg("%s 8",__FUNCTION__); + if (is_bridge(func)) { + dbg("%s 9",__FUNCTION__); + bridge_slot_remove(func); + } else { + dbg("%s 10",__FUNCTION__); + slot_remove(func); + } + + // Setup slot structure with entry for empty slot + func = amdshpc_slot_create(ctrl->bus); + + dbg("%s 11",__FUNCTION__); + if (func == NULL) { + // Out of memory + return(1); + } + + func->bus = ctrl->bus; + func->device = device; + func->function = 0; + func->configured = 0; + func->is_a_board = 0; + + // 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)) { + dbg("%s 12",__FUNCTION__); + func->switch_save = 0; + } else { + dbg("%s 13",__FUNCTION__); + func->switch_save = 0x10; + } + } + + if (rc) { + dbg("%s: rc = %d\n",__FUNCTION__, rc); + } + + if (p_slot){ + dbg("%s 14",__FUNCTION__); + update_slot_info(ctrl, p_slot); + } + + return rc; +} + +// Disable Slot +int amdshpc_process_SS (struct controller *ctrl, struct pci_func *func) +{ + u8 device, class_code, header_type, BCR; + u8 index = 0; + u8 replace_flag; + u32 rc = 0; + struct slot* p_slot; + int physical_slot=0; + + dbg("%s 0",__FUNCTION__); + device = func->device; + func = amdshpc_slot_find(ctrl->bus, device, index++); + p_slot = find_slot(ctrl, device); + if (p_slot) { + physical_slot = p_slot->number; + } + + // Make sure there are no video controllers here + while (func && !rc) { + dbg("%s 1..",__FUNCTION__); + // Check the Class Code + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0B, &class_code); + dbg("%s 1.1 rc = %d class_code = %02x",__FUNCTION__, rc, class_code); + if (rc){ + dbg("%s 2",__FUNCTION__); + return rc; + } + + if (class_code == PCI_BASE_CLASS_DISPLAY) { + /* Display/Video adapter (not supported) */ + dbg("%s 3",__FUNCTION__); + rc = REMOVE_NOT_SUPPORTED; + } else { + dbg("%s 3.5",__FUNCTION__); + // See if it's a bridge + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_HEADER_TYPE, &header_type); + if (rc){ + dbg("%s 4",__FUNCTION__); + return rc; + } + + // If it's a bridge, check the VGA Enable bit + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + dbg("%s 4.5",__FUNCTION__); + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_BRIDGE_CONTROL, &BCR); + if (rc){ + dbg("%s 5",__FUNCTION__); + return rc; + } + + dbg("%s 5.5",__FUNCTION__); + // If the VGA Enable bit is set, remove isn't supported + if (BCR & PCI_BRIDGE_CTL_VGA) { + dbg("%s 6",__FUNCTION__); + rc = REMOVE_NOT_SUPPORTED; + } + } + } + + func = amdshpc_slot_find(ctrl->bus, device, index++); + dbg("%s 7",__FUNCTION__); + } + + func = amdshpc_slot_find(ctrl->bus, device, 0); + dbg("%s 8",__FUNCTION__); + if ((func != NULL) && !rc) { + dbg("%s 9",__FUNCTION__); + //FIXME: Replace flag should be passed into process_SS + replace_flag = !(ctrl->add_support); + rc = remove_board(func, replace_flag, ctrl); + } else if (!rc) { + dbg("%s 10",__FUNCTION__); + rc = 1; + } + + if (p_slot){ + dbg("%s 11",__FUNCTION__); + update_slot_info(ctrl, p_slot); + } + + dbg("%s 12",__FUNCTION__); + return(rc); +} + + +/* + * detect_HRT_floating_pointer + * + * find the Hot Plug Resource Table in the specified region of memory. + * + */ +static void *detect_HRT_floating_pointer(void *begin, void *end) +{ + void *fp; + void *endp; + u8 temp1, temp2, temp3, temp4; + int status = 0; + + endp = (end - sizeof(struct hrt) + 1); + + for (fp = begin; fp <= endp; fp += 16) { + temp1 = readb(fp + SIG0); + temp2 = readb(fp + SIG1); + temp3 = readb(fp + SIG2); + temp4 = readb(fp + SIG3); + if (temp1 == '$' && + temp2 == 'H' && + temp3 == 'R' && + temp4 == 'T') { + status = 1; + dbg("%s -->temp string----> %c%c%c%c at-----> %p\n", __FUNCTION__, temp1,temp2,temp3,temp4,fp); + break; + } + } + + if (!status) { + fp = NULL; + dbg("%s -->Did not discover Hotplug Resource Table between start:%p end:%p\n", __FUNCTION__, begin, end); + return fp; + } + + dbg("%s -->Discovered Hotplug Resource Table at %p\n", __FUNCTION__, fp); + return fp; +} + +/** + * amdshpc_slot_find - Looks for a node by bus, and device, multiple functions accessed + * @bus: bus to find + * @device: device to find + * @index: is 0 for first function found, 1 for the second... + * + * Returns pointer to the node if successful, %NULL otherwise. + */ +struct pci_func *amdshpc_slot_find(u8 bus, u8 device, u8 index) { + int found = -1; + struct pci_func *func; + + func = amdshpc_slot_list[bus]; + dbg("%s amdshpc_slot_list[%02x] = %p", __FUNCTION__, bus, amdshpc_slot_list[bus]); + dbg("%s bus, device, index %x %d %d", __FUNCTION__, bus, device, index); + + if ((func == NULL) || ((func->device == device) && (index == 0))) + return(func); + + if (func->device == device) + found++; + + while (func->next != NULL) { + func = func->next; + + if (func->device == device) + found++; + + if (found == index) + return(func); + } + + return(NULL); +} + + +/* + * amdshpc_resource_sort_and_combine + * + * Sorts all of the nodes in the list in ascending order by + * their base addresses. Also does garbage collection by + * combining adjacent nodes. + * + * returns 0 if success + */ +int amdshpc_resource_sort_and_combine(struct pci_resource **head) +{ + struct pci_resource *node1; + struct pci_resource *node2; + int out_of_order = 1; + + dbg("%s: head = %p, *head = %p\n",__FUNCTION__, head, *head); + + if (!(*head)) + return(1); + + dbg("%s -->*head->next = %p\n", __FUNCTION__,(*head)->next); + + if (!(*head)->next) + return(0); /* only one item on the list, already sorted! */ + + dbg("%s -->*head->base = 0x%x\n", __FUNCTION__,(*head)->base); + dbg("%s -->*head->next->base = 0x%x\n", __FUNCTION__,(*head)->next->base); + while (out_of_order) { + out_of_order = 0; + + // Special case for swapping list head + if (((*head)->next) && + ((*head)->base > (*head)->next->base)) { + node1 = *head; + (*head) = (*head)->next; + node1->next = (*head)->next; + (*head)->next = node1; + out_of_order++; + } + + node1 = (*head); + + while (node1->next && node1->next->next) { + if (node1->next->base > node1->next->next->base) { + out_of_order++; + node2 = node1->next; + node1->next = node1->next->next; + node1 = node1->next; + node2->next = node1->next; + node1->next = node2; + } else + node1 = node1->next; + } + } // End of out_of_order loop + + node1 = *head; + + while (node1 && node1->next) { + if ((node1->base + node1->length) == node1->next->base) { + // Combine + dbg("%s -->8..\n", __FUNCTION__); + node1->length += node1->next->length; + node2 = node1->next; + node1->next = node1->next->next; + kfree(node2); + } else + node1 = node1->next; + } + + return(0); +} + + +/* + * amdshpc_find_available_resources + * + * Finds available memory, IO, and IRQ resources for programming + * devices which may be added to the system + * this function is for hot plug ADD! + * + * returns 0 if success + */ +int amdshpc_find_available_resources (struct controller *ctrl, void *rom_start) +{ + u8 temp; + u8 populated_slot=0; + u8 bridged_slot; + u8 slot_index; + void *one_slot; + struct pci_func *func = NULL; + int i = 10, index; + u32 temp_dword, rc; + struct pci_resource *mem_node; + struct pci_resource *p_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + void *rom_resource_table; + struct shpc_context *shpc_context; + + slot_index=0; + + shpc_context = (struct shpc_context* ) ctrl->shpc_context; + rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff); + dbg("%s -->rom_resource_table = %p\n", __FUNCTION__, rom_resource_table); + + if (rom_resource_table == NULL) { + return -ENODEV; + } + // Sum all resources and setup resource maps + unused_IRQ = readl(rom_resource_table + UNUSED_IRQ); + dbg("%s -->unused_IRQ = %x\n", __FUNCTION__, unused_IRQ); + dbg("%s -->PCI_IRQ = %x\n", __FUNCTION__, readl(rom_resource_table + PCIIRQ)); + + temp = 0; + + while (unused_IRQ) { + if (unused_IRQ & 1) { + amdshpc_disk_irq = temp; + break; + } + unused_IRQ = unused_IRQ >> 1; + temp++; + } + + dbg("%s -->amdshpc_disk_irq= %d\n", __FUNCTION__, amdshpc_disk_irq); + unused_IRQ = unused_IRQ >> 1; + temp++; + + while (unused_IRQ) { + if (unused_IRQ & 1) { + amdshpc_nic_irq = temp; + break; + } + unused_IRQ = unused_IRQ >> 1; + temp++; + } + + dbg("%s -->amdshpc_nic_irq= %d\n", __FUNCTION__, amdshpc_nic_irq); + unused_IRQ = readl(rom_resource_table + PCIIRQ); + + temp = 0; + + if (!amdshpc_nic_irq) { + amdshpc_nic_irq = ctrl->interrupt; + } + + if (!amdshpc_disk_irq) { + amdshpc_disk_irq = ctrl->interrupt; + } + + dbg("%s -->amdshpc_disk_irq, amdshpc_nic_irq= %d, %d\n", __FUNCTION__, amdshpc_disk_irq, amdshpc_nic_irq); + + one_slot = rom_resource_table + sizeof (struct hrt); + + i = readb(rom_resource_table + NUMBER_OF_ENTRIES); + dbg("%s -->number_of_entries = %d\n", __FUNCTION__, i); + + if (!readb(one_slot + SECONDARY_BUS)) { + return(1); + } + + dbg("%s -->dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n", __FUNCTION__); + + while (i && readb(one_slot + SECONDARY_BUS)) { + u8 dev_func = readb(one_slot + DEV_FUNC); + u8 primary_bus = readb(one_slot + PRIMARY_BUS); + u8 secondary_bus = readb(one_slot + SECONDARY_BUS); + u8 max_bus = readb(one_slot + MAX_BUS); + u16 io_base = readw(one_slot + IO_BASE); + u16 io_length = readw(one_slot + IO_LENGTH); + u16 mem_base = readw(one_slot + MEM_BASE); + u16 mem_length = readw(one_slot + MEM_LENGTH); + u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE); + u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH); + + dbg("%s -->%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n", __FUNCTION__, + dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length, + primary_bus, secondary_bus, max_bus); + + // If this entry isn't for our controller's bus, ignore it + if (primary_bus != ctrl->bus) { + i--; + one_slot += sizeof (struct slot_rt); + continue; + } + + // find out if this entry is for an occupied slot + pci_bus_read_config_dword(ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword); + dbg("bus %p, pri-bus %08x, slot %d, function %d, vend ID %d, tempDW %p\n", + ctrl->pci_bus, primary_bus, PCI_SLOT(dev_func), PCI_FUNC(dev_func), PCI_VENDOR_ID, &temp_dword); + + dbg("%s -->temp_D_word = %08X\n", __FUNCTION__, temp_dword); + + if (temp_dword != 0xFFFFFFFF) { + index = 0; + func = amdshpc_slot_find(primary_bus, dev_func >> 3, 0); + dbg("%s -->func = %p",__FUNCTION__, (unsigned long*)func); + while (func && (func->function != PCI_FUNC(dev_func))) { + dbg("%s -->func = %p (bus, dev, fun) = (%d, %d, %d)\n",__FUNCTION__, func, primary_bus, dev_func >> 3, index); + func = amdshpc_slot_find(primary_bus, PCI_SLOT(dev_func), index++); + } + + // If we can't find a match, skip this table entry + if (!func) { + i--; + one_slot += sizeof (struct slot_rt); + continue; + } + // this may not work and shouldn't be used + if (secondary_bus != primary_bus){ + bridged_slot = 1; + } + else{ + bridged_slot = 0; + } + shpc_context->slot_context[slot_index].slot_occupied = 1; + } else { + + populated_slot = 0; + bridged_slot = 0; + } + slot_index++; + + // If we've got a valid IO base, use it + + temp_dword = io_base + io_length; + dbg("%s -->temp_D_word for io base = %08x",__FUNCTION__, temp_dword); + + if ((io_base) && (temp_dword < 0x10000)) { + io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!io_node) + return -ENOMEM; + + io_node->base = io_base; + io_node->length = io_length; + + dbg("%s -->found io_node(base, length) = %x, %x\n",__FUNCTION__, io_node->base, io_node->length); + dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot); + if (!populated_slot) { + io_node->next = ctrl->io_head; + ctrl->io_head = io_node; + } else { + io_node->next = func->io_head; + func->io_head = io_node; + } + } + + // If we've got a valid memory base, use it + temp_dword = mem_base + mem_length; + dbg("%s -->temp_D_word for mem base = %08x",__FUNCTION__, temp_dword); + if ((mem_base) && (temp_dword < 0x10000)) { + mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!mem_node) + return -ENOMEM; + + mem_node->base = mem_base << 16; + + mem_node->length = mem_length << 16; + + dbg("%s -->found mem_node(base, length) = %08x, %08x\n",__FUNCTION__, mem_node->base, mem_node->length); + dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot); + if (!populated_slot) { + mem_node->next = ctrl->mem_head; + ctrl->mem_head = mem_node; + } else { + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } + } + + // If we've got a valid prefetchable memory base, and + // the base + length isn't greater than 0xFFFF + temp_dword = pre_mem_base + pre_mem_length; + dbg("%s -->temp_D_word for pre mem base = %08x",__FUNCTION__, temp_dword); + if ((pre_mem_base) && (temp_dword < 0x10000)) { + p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!p_mem_node) + return -ENOMEM; + + p_mem_node->base = pre_mem_base << 16; + + p_mem_node->length = pre_mem_length << 16; + dbg("%s -->found p_mem_node(base, length) = %08x, %08x\n",__FUNCTION__, p_mem_node->base, p_mem_node->length); + dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot); + + if (!populated_slot) { + p_mem_node->next = ctrl->p_mem_head; + ctrl->p_mem_head = p_mem_node; + } else { + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } + } + + // If we've got a valid bus number, use it + // The second condition is to ignore bus numbers on + // populated slots that don't have PCI-PCI bridges + if (secondary_bus && (secondary_bus != primary_bus)) { + bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!bus_node) + return -ENOMEM; + + bus_node->base = secondary_bus; + bus_node->length = max_bus - secondary_bus + 1; + dbg("%s -->found bus_node(base, length) = %08x, %08x\n",__FUNCTION__, bus_node->base, bus_node->length); + dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot); + if (!populated_slot) { + bus_node->next = ctrl->bus_head; + ctrl->bus_head = bus_node; + } else { + bus_node->next = func->bus_head; + func->bus_head = bus_node; + } + } + + i--; + one_slot += sizeof (struct slot_rt); + } + + // If all of the following fail, we don't have any resources for + // hot plug add + rc = 1; + rc &= amdshpc_resource_sort_and_combine(&(ctrl->mem_head)); + dbg("%s -->rc =%d \n",__FUNCTION__, rc); + rc &= amdshpc_resource_sort_and_combine(&(ctrl->p_mem_head)); + dbg("%s -->rc =%d \n",__FUNCTION__, rc); + rc &= amdshpc_resource_sort_and_combine(&(ctrl->io_head)); + dbg("%s -->rc =%d \n",__FUNCTION__, rc); + rc &= amdshpc_resource_sort_and_combine(&(ctrl->bus_head)); + dbg("%s -->rc =%d \n",__FUNCTION__, rc); + + return(rc); +} + + + +/* + * amdshpc_save_config + * + * Reads configuration for all slots in a PCI bus and saves info. + * + * Note: For non-hot plug busses, the slot # saved is the device # + * + * returns 0 if success + */ +int amdshpc_save_config(struct controller *ctrl, int busnumber, union SLOT_CONFIG_INFO* is_hot_plug) + { + long rc; + u8 class_code; + u8 header_type; + u32 ID; + u8 secondary_bus; + struct pci_func *new_slot; + int sub_bus; + int FirstSupported; + int LastSupported; + int max_functions; + int function; + u8 DevError; + int device = 0; + int cloop = 0; + int stop_it; + int index; + + // Decide which slots are supported + if (is_hot_plug) { + FirstSupported = ctrl->first_slot; + LastSupported = (FirstSupported + is_hot_plug->x.lu_slots_implemented) - 1; + } else { + FirstSupported = 0; + LastSupported = 0x1F; + } + + // Save PCI configuration space for all devices in supported slots + for (device = FirstSupported; device <= LastSupported; device++) { + int devfn = PCI_DEVFN(device, 0); + + ID = 0xFFFFFFFF; + rc = pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_VENDOR_ID, &ID); + if (rc) + return rc; + + if (ID != 0xFFFFFFFF) { // device in slot + rc = pci_bus_read_config_byte(ctrl->pci_bus, devfn, 0x0B, &class_code); + if (rc) + return rc; + + rc = pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + // If multi-function device, set max_functions to 8 + if (header_type & 0x80) + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + DevError = 0; + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // P-P Bridge + // Recurse the subordinate bus + // get the subordinate bus number + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus); + if (rc) { + return rc; + } else { + sub_bus = (int) secondary_bus; + + // Save secondary bus cfg spc + // with this recursive call. + rc = amdshpc_save_config(ctrl, sub_bus, 0); + + if (rc) + return rc; + } + } + + index = 0; + new_slot = amdshpc_slot_find(busnumber, device, index++); + while (new_slot && + (new_slot->function != (u8) function)) + new_slot = amdshpc_slot_find(busnumber, device, index++); + + if (!new_slot) { + // Setup slot structure. + new_slot = amdshpc_slot_create(busnumber); + + if (new_slot == NULL) + return(1); + } + + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = (u8) function; + new_slot->is_a_board = 1; + new_slot->switch_save = 0x10; + // In case of unsupported board + new_slot->status = DevError; + new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function); + dbg("%s EXISTING SLOT", __FUNCTION__); + dbg("%s ns->bus = %d", __FUNCTION__, new_slot->bus); + dbg("%s ns->device = %d", __FUNCTION__, new_slot->device); + dbg("%s ns->function = %d", __FUNCTION__, new_slot->function); + dbg("%s ns->is_a_board = %d", __FUNCTION__, new_slot->is_a_board); + dbg("%s ns->switch_save = %02x", __FUNCTION__, new_slot->switch_save); + dbg("%s ns->pci_dev = %p", __FUNCTION__, new_slot->pci_dev); + + for (cloop = 0; cloop < 0x20; cloop++) { + rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); + if (rc) + return rc; + } + + function++; + stop_it = 0; + + // this loop skips to the next present function + // reading in Class Code and Header type. + while ((function < max_functions)&&(!stop_it)) { + rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID); + if (ID == 0xFFFFFFFF) { // nothing there. + function++; + } else { // Something there + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code); + if (rc) + return rc; + + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + stop_it++; + } + } + + } while (function < max_functions); + } // End of IF (device in slot?) + else if (is_hot_plug) { + // Setup slot structure with entry for empty slot + new_slot = amdshpc_slot_create(busnumber); + + if (new_slot == NULL) { + return(1); + } + + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = 0; + new_slot->is_a_board = 0; + new_slot->presence_save = 0; + new_slot->switch_save = 0; + dbg("%s NEW SLOT", __FUNCTION__); + dbg("%s ns->bus = %d", __FUNCTION__, new_slot->bus); + dbg("%s ns->device = %d", __FUNCTION__, new_slot->function); + dbg("%s ns->function = %d", __FUNCTION__, new_slot->function); + } + }// End of FOR loop + + return 0; +} + + +/* + * amdshpc_set_irq + * + * @bus_num: bus number of PCI device + * @dev_num: device number of PCI device + */ +/* +int amdshpc_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) +{ + int rc; + u16 temp_word; + struct pci_dev fakedev; + struct pci_bus fakebus; + + fakedev.devfn = dev_num << 3; + fakedev.bus = &fakebus; + fakebus.number = bus_num; + dbg("%s : dev %d, bus %d, pin %d, num %d\n",__FUNCTION__, + dev_num, bus_num, int_pin, irq_num); + rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); + dbg("%s:rc %d\n",__FUNCTION__, rc); + if (rc) + return rc; + + // set the Edge Level Control Register (ELCR) + temp_word = inb(0x4d0); + temp_word |= inb(0x4d1) << 8; + + temp_word |= 0x01 << irq_num; + + // This should only be for x86 as it sets the Edge Level Control Register + outb((u8) (temp_word & 0xFF), 0x4d0); + outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); + + return 0; +} +*/ + +/* + * do_pre_bridge_resource_split + * + * Returns zero or one node of resources that aren't in use + * + */ +static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment) { + struct pci_resource *prevnode = NULL; + struct pci_resource *node; + struct pci_resource *split_node; + u32 rc; + u32 temp_dword; + dbg("%s -->do_pre_bridge_resource_split\n",__FUNCTION__); + + if (!(*head) || !(*orig_head)) + return(NULL); + + rc = amdshpc_resource_sort_and_combine(head); + + if (rc) + return(NULL); + + if ((*head)->base != (*orig_head)->base) + return(NULL); + + if ((*head)->length == (*orig_head)->length) + return(NULL); + + + // If we got here, there the bridge requires some of the resource, but + // we may be able to split some off of the front + + node = *head; + + if (node->length & (alignment -1)) { + // this one isn't an aligned length, so we'll make a new entry + // and split it up. + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + temp_dword = (node->length | (alignment-1)) + 1 - alignment; + + split_node->base = node->base; + split_node->length = temp_dword; + + node->length -= temp_dword; + node->base += split_node->length; + + // Put it in the list + *head = split_node; + split_node->next = node; + } + + if (node->length < alignment) { + return(NULL); + } + + // Now unlink it + if (*head == node) { + *head = node->next; + node->next = NULL; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + node->next = NULL; + } + + return(node); +} + + +/* + * do_bridge_resource_split + * + * Returns zero or one node of resources that aren't in use + * + */ +static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment) { + struct pci_resource *prevnode = NULL; + struct pci_resource *node; + u32 rc; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + rc = amdshpc_resource_sort_and_combine(head); + + if (rc) + return(NULL); + + node = *head; + + while (node->next) { + prevnode = node; + node = node->next; + kfree(prevnode); + } + + if (node->length < alignment) { + kfree(node); + return(NULL); + } + + if (node->base & (alignment - 1)) { + // Short circuit if adjusted size is too small + temp_dword = (node->base | (alignment-1)) + 1; + if ((node->length - (temp_dword - node->base)) < alignment) { + kfree(node); + return(NULL); + } + + node->length -= (temp_dword - node->base); + node->base = temp_dword; + } + + if (node->length & (alignment - 1)) { + // There's stuff in use after this node + kfree(node); + return(NULL); + } + + return(node); +} + + +/* + * sort_by_size + * + * Sorts nodes on the list by their length. + * Smallest first. + * + */ +static int sort_by_size(struct pci_resource **head) +{ + struct pci_resource *current_res; + struct pci_resource *next_res; + int out_of_order = 1; + + if (!(*head)) + return(1); + + if (!((*head)->next)) + return(0); + + while (out_of_order) { + out_of_order = 0; + + // Special case for swapping list head + if (((*head)->next) && + ((*head)->length > (*head)->next->length)) { + out_of_order++; + current_res = *head; + *head = (*head)->next; + current_res->next = (*head)->next; + (*head)->next = current_res; + } + + current_res = *head; + + while (current_res->next && current_res->next->next) { + if (current_res->next->length > current_res->next->next->length) { + out_of_order++; + next_res = current_res->next; + current_res->next = current_res->next->next; + current_res = current_res->next; + next_res->next = current_res->next; + current_res->next = next_res; + } else + current_res = current_res->next; + } + } // End of out_of_order loop + + return(0); +} + +/** + * amdshpc_slot_create - Creates a node and adds it to the proper bus. + * @busnumber - bus where new node is to be located + * + * Returns pointer to the new node or NULL if unsuccessful + */ +struct pci_func *amdshpc_slot_create(u8 busnumber) { + struct pci_func *new_slot; + struct pci_func *next; + + dbg("%s busnumber = %02xh",__FUNCTION__, busnumber); + new_slot = (struct pci_func *) kmalloc(sizeof(struct pci_func), GFP_KERNEL); + + if (new_slot == NULL) { + // I'm not dead yet! + // You will be. + return(new_slot); + } + + memset(new_slot, 0, sizeof(struct pci_func)); + + new_slot->next = NULL; + new_slot->configured = 1; + + if (amdshpc_slot_list[busnumber] == NULL) { + amdshpc_slot_list[busnumber] = new_slot; + dbg("%s created new slot in amdshpc_slot_list amdshpc_slot_list[%02X] = %p", __FUNCTION__, + busnumber, amdshpc_slot_list[busnumber]); + } else { + next = amdshpc_slot_list[busnumber]; + while (next->next != NULL) + next = next->next; + next->next = new_slot; + } + return(new_slot); +} + + +/* + * 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) +{ + dbg("%s",__FUNCTION__); + if (!node || !head) + return; + node->next = *head; + *head = node; +} + + +/* + * sort_by_max_size + * + * Sorts nodes on the list by their length. + * Largest first. + * + */ +static int sort_by_max_size(struct pci_resource **head) +{ + struct pci_resource *current_res; + struct pci_resource *next_res; + int out_of_order = 1; + + if (!(*head)) + return(1); + + if (!((*head)->next)) + return(0); + + while (out_of_order) { + out_of_order = 0; + + // Special case for swapping list head + if (((*head)->next) && + ((*head)->length < (*head)->next->length)) { + out_of_order++; + current_res = *head; + *head = (*head)->next; + current_res->next = (*head)->next; + (*head)->next = current_res; + } + + current_res = *head; + + while (current_res->next && current_res->next->next) { + if (current_res->next->length < current_res->next->next->length) { + out_of_order++; + next_res = current_res->next; + current_res->next = current_res->next->next; + current_res = current_res->next; + next_res->next = current_res->next; + current_res->next = next_res; + } else + current_res = current_res->next; + } + } // End of out_of_order loop + + return(0); +} + + +/* + * get_max_resource + * + * Gets the largest node that is at least "size" big from the + * list pointed to by head. It aligns the node on top and bottom + * to "size" alignment before returning it. + */ +static struct pci_resource *get_max_resource (struct pci_resource **head, u32 size) { + struct pci_resource *max; + struct pci_resource *temp; + struct pci_resource *split_node; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + if (amdshpc_resource_sort_and_combine(head)) + return(NULL); + + if (sort_by_max_size(head)) + return(NULL); + + for (max = *head;max; max = max->next) { + + // If not big enough we could probably just bail, + // instead we'll continue to the next. + if (max->length < size) + continue; + + if (max->base & (size - 1)) { + // this one isn't base aligned properly + // so we'll make a new entry and split it up + temp_dword = (max->base | (size-1)) + 1; + + // Short circuit if adjusted size is too small + if ((max->length - (temp_dword - max->base)) < size) + continue; + + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = max->base; + split_node->length = temp_dword - max->base; + max->base = temp_dword; + max->length -= split_node->length; + + // Put it next in the list + split_node->next = max->next; + max->next = split_node; + } + + if ((max->base + max->length) & (size - 1)) { + // this one isn't end aligned properly at the top + // so we'll make a new entry and split it up + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + temp_dword = ((max->base + max->length) & ~(size - 1)); + split_node->base = temp_dword; + split_node->length = max->length + max->base + - split_node->base; + max->length -= split_node->length; + + // Put it in the list + split_node->next = max->next; + max->next = split_node; + } + + // Make sure it didn't shrink too much when we aligned it + if (max->length < size) + continue; + + // Now take it out of the list + temp = (struct pci_resource*) *head; + if (temp == max) { + *head = max->next; + } else { + while (temp && temp->next != max) { + temp = temp->next; + } + + temp->next = max->next; + } + + max->next = NULL; + return(max); + } + + // If we get here, we couldn't find one + return(NULL); +} + + +/* + * get_io_resource + * + * this function sorts the resource list by size and then + * returns the first node of "size" length that is not in the + * ISA aliasing window. If it finds a node larger than "size" + * it will split it up. + * + * size must be a power of two. + */ +static struct pci_resource *get_io_resource (struct pci_resource **head, u32 size) { + struct pci_resource *prevnode; + struct pci_resource *node; + struct pci_resource *split_node; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + if ( amdshpc_resource_sort_and_combine(head) ) + return(NULL); + + if ( sort_by_size(head) ) + return(NULL); + + for (node = *head; node; node = node->next) { + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + // this one isn't base aligned properly + // so we'll make a new entry and split it up + temp_dword = (node->base | (size-1)) + 1; + + // Short circuit if adjusted size is too small + if ((node->length - (temp_dword - node->base)) < size) + continue; + + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base; + split_node->length = temp_dword - node->base; + node->base = temp_dword; + node->length -= split_node->length; + + // Put it in the list + split_node->next = node->next; + node->next = split_node; + } // End of non-aligned base + + // Don't need to check if too small since we already did + if (node->length > size) { + // this one is longer than we need + // so we'll make a new entry and split it up + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base + size; + split_node->length = node->length - size; + node->length = size; + + // Put it in the list + split_node->next = node->next; + node->next = split_node; + } // End of too big on top end + + // For IO make sure it's not in the ISA aliasing space + if (node->base & 0x300L) + continue; + + // If we got here, then it is the right size + // Now take it out of the list + if (*head == node) { + *head = node->next; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + } + node->next = NULL; + // Stop looping + break; + } + + return(node); +} + + +/* + * get_resource + * + * this function sorts the resource list by size and then + * returns the first node of "size" length. If it finds a node + * larger than "size" it will split it up. + * + * size must be a power of two. + */ +static struct pci_resource *get_resource (struct pci_resource **head, u32 size) { + struct pci_resource *prevnode; + struct pci_resource *node; + struct pci_resource *split_node; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + if ( amdshpc_resource_sort_and_combine(head) ) + return(NULL); + + if ( sort_by_size(head) ) + return(NULL); + + for (node = *head; node; node = node->next) { + dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",__FUNCTION__, + size, node, node->base, node->length); + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + dbg("%s: not aligned\n",__FUNCTION__); + // this one isn't base aligned properly + // so we'll make a new entry and split it up + temp_dword = (node->base | (size-1)) + 1; + + // Short circuit if adjusted size is too small + if ((node->length - (temp_dword - node->base)) < size) + continue; + + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base; + split_node->length = temp_dword - node->base; + node->base = temp_dword; + node->length -= split_node->length; + + // Put it in the list + split_node->next = node->next; + node->next = split_node; + } // End of non-aligned base + + // Don't need to check if too small since we already did + if (node->length > size) { + dbg("%s: too big\n",__FUNCTION__); + // this one is longer than we need + // so we'll make a new entry and split it up + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base + size; + split_node->length = node->length - size; + node->length = size; + + // Put it in the list + split_node->next = node->next; + node->next = split_node; + } // End of too big on top end + + dbg("%s: got one!!!\n",__FUNCTION__); + // If we got here, then it is the right size + // Now take it out of the list + if (*head == node) { + *head = node->next; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + } + node->next = NULL; + // Stop looping + break; + } + return(node); +} + +/* + * amdshpc_return_board_resources + * + * this routine returns all resources allocated to a board to + * the available pool. + * + * returns 0 if success + */ +int amdshpc_return_board_resources(struct pci_func * func, struct resource_lists * resources) +{ + int rc = 1; + struct pci_resource *node; + struct pci_resource *t_node; + dbg("%s",__FUNCTION__); + + if (!func) + return(1); + + node = func->io_head; + func->io_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->io_head), node); + node = t_node; + } + + node = func->mem_head; + func->mem_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->mem_head), node); + node = t_node; + } + + node = func->p_mem_head; + func->p_mem_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->p_mem_head), node); + node = t_node; + } + + node = func->bus_head; + func->bus_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->bus_head), node); + node = t_node; + } + + rc |= amdshpc_resource_sort_and_combine(&(resources->mem_head)); + rc |= amdshpc_resource_sort_and_combine(&(resources->p_mem_head)); + rc |= amdshpc_resource_sort_and_combine(&(resources->io_head)); + rc |= amdshpc_resource_sort_and_combine(&(resources->bus_head)); + + return(rc); +} + + +/* + * amdshpc_destroy_resource_list + * + * Puts node back in the resource list pointed to by head + */ +void amdshpc_destroy_resource_list (struct resource_lists * resources) +{ + struct pci_resource *res, *tres; + + res = resources->io_head; + resources->io_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->mem_head; + resources->mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->p_mem_head; + resources->p_mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->bus_head; + resources->bus_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } +} + + +/* + * amdshpc_destroy_board_resources + * + * Puts node back in the resource list pointed to by head + */ +void amdshpc_destroy_board_resources (struct pci_func * func) +{ + struct pci_resource *res, *tres; + + res = func->io_head; + func->io_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->mem_head; + func->mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->p_mem_head; + func->p_mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->bus_head; + func->bus_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } +} + +/** + * configure_new_device - Configures the PCI header information of one board. + * + * @ctrl: pointer to controller structure + * @func: pointer to function structure + * @behind_bridge: 1 if this is a recursive call, 0 if not + * @resources: pointer to set of resource lists + * + * Returns 0 if success + * + */ +static u32 configure_new_device (struct controller * ctrl, struct pci_func * func, + u8 behind_bridge, struct resource_lists * resources) +{ + u8 temp_byte, function, max_functions, stop_it; + int rc; + u32 ID; + struct pci_func *new_slot; + int index; + + new_slot = func; + + dbg("%s",__FUNCTION__); + // Check for Multi-function device + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte); + if (rc) { + dbg("%s: rc = %d\n",__FUNCTION__, rc); + return rc; + } + + if (temp_byte & 0x80) // Multi-function device + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + rc = configure_new_function(ctrl, new_slot, behind_bridge, resources); + + if (rc) { + dbg("%s -->configure_new_function failed %d\n",__FUNCTION__,rc); + index = 0; + + while (new_slot) { + new_slot = amdshpc_slot_find(new_slot->bus, new_slot->device, index++); + + if (new_slot) + amdshpc_return_board_resources(new_slot, resources); + } + + return(rc); + } + + function++; + + stop_it = 0; + + // The following loop skips to the next present function + // and creates a board structure + + while ((function < max_functions) && (!stop_it)) { + pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); + + if (ID == 0xFFFFFFFF) { // There's nothing there. + function++; + } else { // There's something there + // Setup slot structure. + new_slot = amdshpc_slot_create(func->bus); + + if (new_slot == NULL) { + // Out of memory + return(1); + } + + new_slot->bus = func->bus; + new_slot->device = func->device; + new_slot->function = function; + new_slot->is_a_board = 1; + new_slot->status = 0; + + stop_it++; + } + } + + } while (function < max_functions); + dbg("%s -->returning from configure_new_device\n",__FUNCTION__); + + return 0; +} + + +/* + Configuration logic that involves the hotplug data structures and + their bookkeeping + */ + + +/** + * configure_new_function - Configures the PCI header information of one device + * + * @ctrl: pointer to controller structure + * @func: pointer to function structure + * @behind_bridge: 1 if this is a recursive call, 0 if not + * @resources: pointer to set of resource lists + * + * Calls itself recursively for bridged devices. + * Returns 0 if success + * + */ +static int configure_new_function (struct controller * ctrl, struct pci_func * func, + u8 behind_bridge, struct resource_lists * resources) +{ + int cloop; + u8 IRQ; + u8 temp_byte; + u8 device; + u8 class_code; + u16 command; + u16 temp_word; + u32 temp_dword; + u32 rc; + u32 temp_register; + u32 base; + u32 ID; + struct pci_resource *mem_node; + struct pci_resource *p_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + struct pci_resource *hold_mem_node; + struct pci_resource *hold_p_mem_node; + struct pci_resource *hold_IO_node; + struct pci_resource *hold_bus_node; + struct irq_mapping irqs; + struct pci_func *new_slot; + struct resource_lists temp_resources; + int devfn = PCI_DEVFN(func->device, func->function); + + dbg("%s", __FUNCTION__); + // Check for Bridge + rc = pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte); + if (rc) + return rc; + + if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + // set Primary bus + dbg("%s -->set Primary bus = %d\n",__FUNCTION__, func->bus); + rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); + if (rc) + return rc; + + // find range of busses to use + dbg("%s -->find ranges of buses to use\n",__FUNCTION__); + bus_node = get_max_resource(&resources->bus_head, 1); + + // If we don't have any busses to allocate, we can't continue + if (!bus_node) + return -ENOMEM; + + // set Secondary bus + temp_byte = bus_node->base; + dbg("%s -->set Secondary bus = %d\n",__FUNCTION__, bus_node->base); + rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte); + if (rc) + return rc; + + // set subordinate bus + temp_byte = bus_node->base + bus_node->length - 1; + dbg("%s -->set subordinate bus = %d\n",__FUNCTION__, bus_node->base + bus_node->length - 1); + rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); + if (rc) + return rc; + + // set subordinate Latency Timer and base Latency Timer + temp_byte = 0x40; + rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); + if (rc) + return rc; + rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); + if (rc) + return rc; + + // set Cache Line size + temp_byte = 0x08; + rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); + if (rc) + return rc; + + // Setup the IO, memory, and prefetchable windows + + io_node = get_max_resource(&(resources->io_head), 0x1000); + mem_node = get_max_resource(&(resources->mem_head), 0x100000); + p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000); + dbg("%s -->Setup the IO, memory, and prefetchable windows\n",__FUNCTION__); + dbg("%s -->io_node\n",__FUNCTION__); + dbg("%s -->(base, len, next) (%x, %x, %p)\n",__FUNCTION__, io_node->base, io_node->length, io_node->next); + dbg("%s -->mem_node\n",__FUNCTION__); + dbg("%s -->(base, len, next) (%x, %x, %p)\n",__FUNCTION__, mem_node->base, mem_node->length, mem_node->next); + dbg("%s -->p_mem_node\n",__FUNCTION__); + dbg("%s -->(base, len, next) (%x, %x, %p)\n",__FUNCTION__, p_mem_node->base, p_mem_node->length, p_mem_node->next); + + // set up the IRQ info + if (!resources->irqs) { + irqs.barber_pole = 0; + irqs.interrupt[0] = 0; + irqs.interrupt[1] = 0; + irqs.interrupt[2] = 0; + irqs.interrupt[3] = 0; + irqs.valid_INT = 0; + } else { + irqs.barber_pole = resources->irqs->barber_pole; + irqs.interrupt[0] = resources->irqs->interrupt[0]; + irqs.interrupt[1] = resources->irqs->interrupt[1]; + irqs.interrupt[2] = resources->irqs->interrupt[2]; + irqs.interrupt[3] = resources->irqs->interrupt[3]; + irqs.valid_INT = resources->irqs->valid_INT; + } + + // set up resource lists that are now aligned on top and bottom + // for anything behind the bridge. + temp_resources.bus_head = bus_node; + temp_resources.io_head = io_node; + temp_resources.mem_head = mem_node; + temp_resources.p_mem_head = p_mem_node; + temp_resources.irqs = &irqs; + + // Make copies of the nodes we are going to pass down so that + // if there is a problem,we can just use these to free resources + hold_bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + hold_IO_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + hold_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + hold_p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) { + if (hold_bus_node) + kfree(hold_bus_node); + if (hold_IO_node) + kfree(hold_IO_node); + if (hold_mem_node) + kfree(hold_mem_node); + if (hold_p_mem_node) + kfree(hold_p_mem_node); + + return(1); + } + + memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource)); + + bus_node->base += 1; + bus_node->length -= 1; + bus_node->next = NULL; + + // If we have IO resources copy them and fill in the bridge's + // IO range registers + if (io_node) { + memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); + io_node->next = NULL; + + // set IO base and Limit registers + temp_byte = io_node->base >> 8; + rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_IO_BASE, temp_byte); + + temp_byte = (io_node->base + io_node->length - 1) >> 8; + rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + } else { + kfree(hold_IO_node); + hold_IO_node = NULL; + } + + // If we have memory resources copy them and fill in the bridge's + // memory range registers. Otherwise, fill in the range + // registers with values that disable them. + if (mem_node) { + memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); + mem_node->next = NULL; + + // set Mem base and Limit registers + temp_word = mem_node->base >> 16; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + temp_word = (mem_node->base + mem_node->length - 1) >> 16; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + } else { + temp_word = 0xFFFF; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + temp_word = 0x0000; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + kfree(hold_mem_node); + hold_mem_node = NULL; + } + + // If we have prefetchable memory resources copy them and + // fill in the bridge's memory range registers. Otherwise, + // fill in the range registers with values that disable them. + if (p_mem_node) { + memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); + p_mem_node->next = NULL; + + // set Pre Mem base and Limit registers + temp_word = p_mem_node->base >> 16; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + } else { + temp_word = 0xFFFF; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + temp_word = 0x0000; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + kfree(hold_p_mem_node); + hold_p_mem_node = NULL; + } + + // Adjust this to compensate for extra adjustment in first loop + irqs.barber_pole--; + + rc = 0; + + // Here we actually find the devices and configure them + for (device = 0; (device <= 0x1F) && !rc; device++) { + irqs.barber_pole = (irqs.barber_pole + 1) & 0x03; + + ID = 0xFFFFFFFF; + pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, 0), 0x00, &ID); + + if (ID != 0xFFFFFFFF) { // device Present + // Setup slot structure. + new_slot = amdshpc_slot_create(hold_bus_node->base); + + if (new_slot == NULL) { + // Out of memory + rc = -ENOMEM; + continue; + } + + new_slot->bus = hold_bus_node->base; + new_slot->device = device; + new_slot->function = 0; + new_slot->is_a_board = 1; + new_slot->status = 0; + + rc = configure_new_device(ctrl, new_slot, 1, &temp_resources); + dbg("%s -->configure_new_device rc=0x%x\n",__FUNCTION__,rc); + } // End of IF (device in slot?) + } // End of FOR loop + + if (rc) { + amdshpc_destroy_resource_list(&temp_resources); + + return_resource(&(resources->bus_head), hold_bus_node); + return_resource(&(resources->io_head), hold_IO_node); + return_resource(&(resources->mem_head), hold_mem_node); + return_resource(&(resources->p_mem_head), hold_p_mem_node); + return(rc); + } + // save the interrupt routing information + if (resources->irqs) { + resources->irqs->interrupt[0] = irqs.interrupt[0]; + resources->irqs->interrupt[1] = irqs.interrupt[1]; + resources->irqs->interrupt[2] = irqs.interrupt[2]; + resources->irqs->interrupt[3] = irqs.interrupt[3]; + resources->irqs->valid_INT = irqs.valid_INT; + } else if (!behind_bridge) { + // We need to hook up the interrupts here + for (cloop = 0; cloop < 4; cloop++) { + if (irqs.valid_INT & (0x01 << cloop)) { +rc=0; +// rc = amdshpc_set_irq(func->bus, func->device, +// 0x0A + cloop, irqs.interrupt[cloop]); + if (rc) { + amdshpc_destroy_resource_list (&temp_resources); + + return_resource(&(resources-> bus_head), hold_bus_node); + return_resource(&(resources-> io_head), hold_IO_node); + return_resource(&(resources-> mem_head), hold_mem_node); + return_resource(&(resources-> p_mem_head), hold_p_mem_node); + return rc; + } + } + } // end of for loop + } + // Return unused bus resources + // First use the temporary node to store information for the board + if (hold_bus_node && bus_node && temp_resources.bus_head) { + hold_bus_node->length = bus_node->base - hold_bus_node->base; + + hold_bus_node->next = func->bus_head; + func->bus_head = hold_bus_node; + + temp_byte = temp_resources.bus_head->base - 1; + + // set subordinate bus + pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); + + if (temp_resources.bus_head->length == 0) { + kfree(temp_resources.bus_head); + temp_resources.bus_head = NULL; + } else { + return_resource(&(resources->bus_head), temp_resources.bus_head); + } + } + + // If we have IO space available and there is some left, + // return the unused portion + if (hold_IO_node && temp_resources.io_head) { + io_node = do_pre_bridge_resource_split(&(temp_resources.io_head), + &hold_IO_node, 0x1000); + + // Check if we were able to split something off + if (io_node) { + hold_IO_node->base = io_node->base + io_node->length; + + temp_byte = (hold_IO_node->base) >> 8; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_IO_BASE, temp_byte); + + return_resource(&(resources->io_head), io_node); + } + + io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000); + + // Check if we were able to split something off + if (io_node) { + // First use the temporary node to store information for the board + hold_IO_node->length = io_node->base - hold_IO_node->base; + + // If we used any, add it to the board's list + if (hold_IO_node->length) { + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + + temp_byte = (io_node->base - 1) >> 8; + pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + + return_resource(&(resources->io_head), io_node); + } else { + // it doesn't need any IO + temp_word = 0x0000; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_IO_LIMIT, temp_word); + + return_resource(&(resources->io_head), io_node); + kfree(hold_IO_node); + } + } else { + // it used most of the range + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + } + } else if (hold_IO_node) { + // it used the whole range + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + } + // If we have memory space available and there is some left, + // return the unused portion + if (hold_mem_node && temp_resources.mem_head) { + mem_node = do_pre_bridge_resource_split(&(temp_resources. mem_head), + &hold_mem_node, 0x100000); + + // Check if we were able to split something off + if (mem_node) { + hold_mem_node->base = mem_node->base + mem_node->length; + + temp_word = (hold_mem_node->base) >> 16; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + return_resource(&(resources->mem_head), mem_node); + } + + mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000); + + // Check if we were able to split something off + if (mem_node) { + // First use the temporary node to store information for the board + hold_mem_node->length = mem_node->base - hold_mem_node->base; + + if (hold_mem_node->length) { + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + + // configure end address + temp_word = (mem_node->base - 1) >> 16; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + // Return unused resources to the pool + return_resource(&(resources->mem_head), mem_node); + } else { + // it doesn't need any Mem + temp_word = 0x0000; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->mem_head), mem_node); + kfree(hold_mem_node); + } + } else { + // it used most of the range + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + } + } else if (hold_mem_node) { + // it used the whole range + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + } + // If we have prefetchable memory space available and there is some + // left at the end, return the unused portion + if (hold_p_mem_node && temp_resources.p_mem_head) { + p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), + &hold_p_mem_node, 0x100000); + + // Check if we were able to split something off + if (p_mem_node) { + hold_p_mem_node->base = p_mem_node->base + p_mem_node->length; + + temp_word = (hold_p_mem_node->base) >> 16; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + } + + p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000); + + // Check if we were able to split something off + if (p_mem_node) { + // First use the temporary node to store information for the board + hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base; + + // If we used any, add it to the board's list + if (hold_p_mem_node->length) { + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + + temp_word = (p_mem_node->base - 1) >> 16; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + } else { + // it doesn't need any PMem + temp_word = 0x0000; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + kfree(hold_p_mem_node); + } + } else { + // it used the most of the range + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + } + } else if (hold_p_mem_node) { + // it used the whole range + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + } + // We should be configuring an IRQ and the bridge's base address + // registers if it needs them. Although we have never seen such + // a device + + // enable card + command = 0x0157; // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, command); + + // set Bridge Control Register + command = 0x07; // = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_BRIDGE_CONTROL, command); + } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) { + // Standard device + pci_bus_read_config_byte(ctrl->pci_bus, devfn, 0x0B, &class_code); + + if (class_code == PCI_BASE_CLASS_DISPLAY) { + // Display (video) adapter (not supported) + return(DEVICE_TYPE_NOT_SUPPORTED); + } + // Figure out IO and memory needs + for (cloop = 0x10; cloop <= 0x24; cloop += 4) { + temp_register = 0xFFFFFFFF; + + dbg("%s -->CND: devfn=%x, offset=%d\n",__FUNCTION__, devfn, cloop); + pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &temp_register); + dbg("%s -->CND: base = 0x%x\n",__FUNCTION__, temp_register); + + if (temp_register) { // If this register is implemented + if ((temp_register & 0x03L) == 0x01) { + // Map IO + + // set base = amount of IO space + base = temp_register & 0xFFFFFFFC; + base = ~base + 1; + + dbg("%s -->CND: length = 0x%x\n",__FUNCTION__, base); + io_node = get_io_resource(&(resources->io_head), base); + dbg("%s -->Got io_node start = %8.8x, length = %8.8x next (%p)\n",__FUNCTION__, + io_node->base, io_node->length, io_node->next); + dbg("%s -->func (%p) io_head (%p)\n",__FUNCTION__, func, func->io_head); + + // allocate the resource to the board + if (io_node) { + base = io_node->base; + + io_node->next = func->io_head; + func->io_head = io_node; + } else + return -ENOMEM; + } else if ((temp_register & 0x0BL) == 0x08) { + // Map prefetchable memory + base = temp_register & 0xFFFFFFF0; + base = ~base + 1; + + dbg("%s -->CND: length = 0x%x\n",__FUNCTION__, base); + p_mem_node = get_resource(&(resources->p_mem_head), base); + + // allocate the resource to the board + if (p_mem_node) { + base = p_mem_node->base; + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } else + return -ENOMEM; + } else if ((temp_register & 0x0BL) == 0x00) { + // Map memory + base = temp_register & 0xFFFFFFF0; + base = ~base + 1; + + dbg("%s -->CND: length = 0x%x\n",__FUNCTION__, base); + mem_node = get_resource(&(resources->mem_head), base); + + // allocate the resource to the board + if (mem_node) { + base = mem_node->base; + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } else + return -ENOMEM; + } else if ((temp_register & 0x0BL) == 0x04) { + // Map memory + base = temp_register & 0xFFFFFFF0; + base = ~base + 1; + + dbg("%s -->CND: length = 0x%x\n",__FUNCTION__, base); + mem_node = get_resource(&(resources->mem_head), base); + + // allocate the resource to the board + if (mem_node) { + base = mem_node->base; + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } else + return -ENOMEM; + } else if ((temp_register & 0x0BL) == 0x06) { + // Those bits are reserved, we can't handle this + return(1); + } else { + // Requesting space below 1M + return(NOT_ENOUGH_RESOURCES); + } + + pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, base); + + // Check for 64-bit base + if ((temp_register & 0x07L) == 0x04) { + cloop += 4; + + // Upper 32 bits of address always zero on today's systems + // FIXME this is probably not true on Alpha and ia64??? + base = 0; + pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, base); + } + } + } // End of base register loop + + // Figure out which interrupt pin this function uses + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte); + dbg("%s temp_byte for interrupt pin = %x", __FUNCTION__, temp_byte); + // If this function needs an interrupt and we are behind a bridge + // and the pin is tied to something that's already mapped, + // set this one the same + if (temp_byte && resources->irqs && + (resources->irqs->valid_INT & + (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) { + // We have to share with something already set up + IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03]; + dbg("%s We're sharing the IRQ from some other device = %02x", __FUNCTION__, IRQ); + } else { + // Program IRQ based on card type + pci_bus_read_config_byte(ctrl->pci_bus, devfn, 0x0B, &class_code); + if (class_code == PCI_BASE_CLASS_STORAGE) { + dbg("%s We're sharing the disk IRQ (maybe)", __FUNCTION__); + IRQ = amdshpc_disk_irq; + } else { + dbg("%s We're sharing the NIC IRQ (maybe)", __FUNCTION__); + IRQ = amdshpc_nic_irq; + } + } + + // IRQ Line + pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ); + if (!behind_bridge) { +// rc = amdshpc_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ); +// rc = amdshpc_set_irq(func->bus, func->device, temp_byte + 20, IRQ); + rc = 0; + if (rc) + return 1; + } else { + //TBD - this code may also belong in the other clause of this If statement + resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ; + resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03; + } + + // Latency Timer + temp_byte = 0x40; + pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); + + // Cache Line size + temp_byte = 0x08; + pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); + + // disable ROM base Address + temp_dword = 0x00L; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_ROM_ADDRESS, temp_dword); + + // enable card + temp_word = 0x0157; // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, temp_word); + } // End of Not-A-Bridge else + else { + // It's some strange type of PCI adapter (Cardbus?) + return DEVICE_TYPE_NOT_SUPPORTED; + } + + func->configured = 1; + + return 0; +} + +int amdshpc_configure_device (struct controller * ctrl, struct pci_func* func) +{ + unsigned char bus; + struct pci_dev dev0; + struct pci_bus *child; + int num; + + memset(&dev0, 0, sizeof(struct pci_dev)); + dbg("%s", __FUNCTION__); + + if (func->pci_dev == NULL) + func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); + + //Still NULL ? Well then scan for it ! + if (func->pci_dev == NULL) { + num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function)); + if (num) + pci_bus_add_devices(ctrl->pci_dev->bus); + + func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); + if (func->pci_dev == NULL) { + dbg("ERROR: pci_dev still null\n"); + return 0; + } + } + + if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); + child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); + pci_do_scan_bus(child); + + } + + return 0; +} + + +int amdshpc_unconfigure_device(struct pci_func* func) +{ + int j; + + dbg("%s: bus/dev/func = %x/%x/%x\n",__FUNCTION__,func->bus, func->device, func->function); + + for (j=0; j<8 ; j++) { + struct pci_dev* temp = pci_find_slot(func->bus, (func->device << 3) | j); + if (temp) + pci_remove_bus_device(temp); + } + return 0; +} + +/* +static int PCI_RefinedAccessConfig(struct pci_ops *ops, u8 bus, u8 device, u8 function, u8 offset, u32 *value) +{ + u32 vendID = 0; + + dbg("%s", __FUNCTION__); + if (pci_read_config_dword_nodev (ops, bus, device, function, PCI_VENDOR_ID, &vendID) == -1) + return -1; + if (vendID == 0xffffffff) + return -1; + return pci_read_config_dword_nodev (ops, bus, device, function, offset, value); +} + + +// +// WTF??? This function isn't in the code, yet a function calls it, but the +// compiler optimizes it away? strange. Here as a placeholder to keep the +// compiler happy. +// +static int PCI_ScanBusNonBridge (u8 bus, u8 device) +{ + return 0; +} + +static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev_num) +{ + u8 tdevice; + u32 work; + u8 tbus; + + dbg("%s", __FUNCTION__); + for (tdevice = 0; tdevice < 0x100; tdevice++) { + //Scan for access first + if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 0x08, &work) == -1) + continue; + dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice); + //Yep we got one. Not a bridge ? + if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) { + *dev_num = tdevice; + dbg("found it !\n"); + return 0; + } + } + for (tdevice = 0; tdevice < 0x100; tdevice++) { + //Scan for access first + if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 0x08, &work) == -1) + continue; + dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice); + //Yep we got one. bridge ? + if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { + pci_read_config_byte_nodev (ctrl->pci_ops, tbus, tdevice, 0, PCI_SECONDARY_BUS, &tbus); + dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice); + if (PCI_ScanBusNonBridge(tbus, tdevice) == 0) + return 0; + } + } + + return -1; +} + +static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge) +{ + struct irq_routing_table *PCIIRQRoutingInfoLength; + long len; + long loop; + u32 work; + + u8 tbus, tdevice, tslot; + + PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table(); + + len = (PCIIRQRoutingInfoLength->size - + sizeof(struct irq_routing_table)) / sizeof(struct irq_info); + dbg("%s len = %d",__FUNCTION__, (int)len); + // Make sure I got at least one entry + if (len == 0) { + if (PCIIRQRoutingInfoLength != NULL) + kfree(PCIIRQRoutingInfoLength ); + return -1; + } + + for (loop = 0; loop < len; ++loop) { + tbus = PCIIRQRoutingInfoLength->slots[loop].bus; + tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn; + tslot = PCIIRQRoutingInfoLength->slots[loop].slot; + dbg("%s tbus = %02Xh tdevice = %02Xh device = %02Xh function = %d tslot = %d",__FUNCTION__, + tbus, tdevice, tdevice >>3, tdevice & 0x7, tslot); + if (tslot == slot) { + *bus_num = tbus; + *dev_num = tdevice; + pci_read_config_dword_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_VENDOR_ID, &work); + if (!nobridge || (work == 0xffffffff)) { + if (PCIIRQRoutingInfoLength != NULL) + dbg("%s PCIIRQRoutingInfoLength != NULL returning 0",__FUNCTION__); + kfree(PCIIRQRoutingInfoLength ); + return 0; + } + + dbg("bus_num %d dev_num %d func_num %d\n", *bus_num, *dev_num >> 3, *dev_num & 0x7); + pci_read_config_dword_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_CLASS_REVISION, &work); + dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS); + + if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { + pci_read_config_byte_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_SECONDARY_BUS, &tbus); + dbg("Scan bus for Non Bridge: bus %d\n", tbus); + if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) { + *bus_num = tbus; + if (PCIIRQRoutingInfoLength != NULL) + kfree(PCIIRQRoutingInfoLength ); + return 0; + } + } else { + if (PCIIRQRoutingInfoLength != NULL) + kfree(PCIIRQRoutingInfoLength ); + return 0; + } + + } + } + if (PCIIRQRoutingInfoLength != NULL) + kfree(PCIIRQRoutingInfoLength ); + return -1; +} + + +int amdshpc_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot) +{ + dbg("%s", __FUNCTION__); + return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); //plain (bridges allowed) +} +*/ + +/* More PCI configuration routines; this time centered around hotplug controller */ + + +/* + * amdshpc_save_slot_config + * + * Saves configuration info for all PCI devices in a given slot + * including subordinate busses. + * + * returns 0 if success + */ +int amdshpc_save_slot_config (struct controller *ctrl, struct pci_func * new_slot) +{ + long rc; + u8 class_code; + u8 header_type; + u32 ID; + u8 secondary_bus; + int sub_bus; + int max_functions; + int function; + int cloop = 0; + int stop_it; + + ID = 0xFFFFFFFF; + + dbg("%s", __FUNCTION__); + pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID); + + if (ID != 0xFFFFFFFF) { // device in slot + pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code); + pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type); + + if (header_type & 0x80) // Multi-function device + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + // Recurse the subordinate bus + pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus); + + sub_bus = (int) secondary_bus; + + // Save the config headers for the secondary bus. + rc = amdshpc_save_config(ctrl, sub_bus, 0); + + if (rc) + return(rc); + + } // End of IF + + new_slot->status = 0; + + for (cloop = 0; cloop < 0x20; cloop++) { + pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); + } + + function++; + + stop_it = 0; + + // this loop skips to the next present function + // reading in the Class Code and the Header type. + + while ((function < max_functions) && (!stop_it)) { + pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID); + + if (ID == 0xFFFFFFFF) { // nothing there. + function++; + } else { // Something there + pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code); + pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type); + + stop_it++; + } + } + + } while (function < max_functions); + } // End of IF (device in slot?) + else { + return(2); + } + + return(0); +} + + +/* + * amdshpc_save_base_addr_length + * + * Saves the length of all base address registers for the + * specified slot. this is for hot plug REPLACE + * + * returns 0 if success + */ +int amdshpc_save_base_addr_length(struct controller *ctrl, struct pci_func * func) +{ + u8 cloop; + u8 header_type; + u8 secondary_bus; + u8 type; + int sub_bus; + u32 temp_register; + u32 base; + u32 rc; + struct pci_func *next; + int index = 0; + + dbg("%s", __FUNCTION__); + func = amdshpc_slot_find(func->bus, func->device, index++); + + while (func != NULL) { + int devfn = PCI_DEVFN(func->device, func->function); + + // Check for Bridge + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + // PCI-PCI Bridge + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); + + sub_bus = (int) secondary_bus; + + next = amdshpc_slot_list[sub_bus]; + + while (next != NULL) { + rc = amdshpc_save_base_addr_length(ctrl, next); + + if (rc) + return(rc); + + next = next->next; + } + + //FIXME: this loop is duplicated in the non-bridge case. The two could be rolled together + // Figure out IO and memory base lengths + for (cloop = 0x10; cloop <= 0x14; cloop += 4) { + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base); + + if (base) { // If this register is implemented + if (base & 0x01L) { + // IO base + // set base = amount of IO space requested + base = base & 0xFFFFFFFE; + base = (~base) + 1; + + type = 1; + } else { + // memory base + base = base & 0xFFFFFFF0; + base = (~base) + 1; + + type = 0; + } + } else { + base = 0x0L; + type = 0; + } + + // Save information in slot structure + func->base_length[(cloop - 0x10) >> 2] = base; + func->base_type[(cloop - 0x10) >> 2] = type; + + } // End of base register loop + + + } else if ((header_type & 0x7F) == 0x00) { // PCI-PCI Bridge + // Figure out IO and memory base lengths + for (cloop = 0x10; cloop <= 0x24; cloop += 4) { + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base); + + if (base) { // If this register is implemented + if (base & 0x01L) { + // IO base + // base = amount of IO space requested + base = base & 0xFFFFFFFE; + base = (~base) + 1; + + type = 1; + } else { + // memory base + // base = amount of memory space requested + base = base & 0xFFFFFFF0; + base = (~base) + 1; + + type = 0; + } + } else { + base = 0x0L; + type = 0; + } + + // Save information in slot structure + func->base_length[(cloop - 0x10) >> 2] = base; + func->base_type[(cloop - 0x10) >> 2] = type; + + } // End of base register loop + + } else { // Some other unknown header type + } + + // find the next device in this slot + func = amdshpc_slot_find(func->bus, func->device, index++); + } + + return(0); +} + + +/* + * amdshpc_save_used_resources + * + * Stores used resource information for existing boards. this is + * for boards that were in the system when this driver was loaded. + * this function is for hot plug ADD + * + * returns 0 if success + */ +int amdshpc_save_used_resources (struct controller *ctrl, struct pci_func * func) +{ + u8 cloop; + u8 header_type; + u8 secondary_bus; + u8 temp_byte; + u8 b_base; + u8 b_length; + u16 command; + u16 save_command; + u16 w_base; + u16 w_length; + u32 temp_register; + u32 save_base; + u32 base; + int index = 0; + struct pci_resource *mem_node; + struct pci_resource *p_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + + dbg("%s", __FUNCTION__); + func = amdshpc_slot_find(func->bus, func->device, index++); + + while ((func != NULL) && func->is_a_board) { + int devfn = PCI_DEVFN(func->device, func->function); + + // Save the command register + pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, &save_command); + + // disable card + command = 0x00; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, command); + + // Check for Bridge + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + // Clear Bridge Control Register + command = 0x00; + pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_BRIDGE_CONTROL, command); + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); + + bus_node =(struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!bus_node) + return -ENOMEM; + + bus_node->base = secondary_bus; + bus_node->length = temp_byte - secondary_bus + 1; + + bus_node->next = func->bus_head; + func->bus_head = bus_node; + + // Save IO base and Limit registers + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_IO_BASE, &b_base); + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_IO_LIMIT, &b_length); + + if ((b_base <= b_length) && (save_command & 0x01)) { + io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!io_node) + return -ENOMEM; + + io_node->base = (b_base & 0xF0) << 8; + io_node->length = (b_length - b_base + 0x10) << 8; + + io_node->next = func->io_head; + func->io_head = io_node; + } + // Save memory base and Limit registers + pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, &w_base); + pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); + + if ((w_base <= w_length) && (save_command & 0x02)) { + mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!mem_node) + return -ENOMEM; + + mem_node->base = w_base << 16; + mem_node->length = (w_length - w_base + 0x10) << 16; + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } + // Save prefetchable memory base and Limit registers + pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); + pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); + + if ((w_base <= w_length) && (save_command & 0x02)) { + p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!p_mem_node) + return -ENOMEM; + + p_mem_node->base = w_base << 16; + p_mem_node->length = (w_length - w_base + 0x10) << 16; + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } + // Figure out IO and memory base lengths + for (cloop = 0x10; cloop <= 0x14; cloop += 4) { + pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &save_base); + + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base); + + temp_register = base; + + if (base) { // If this register is implemented + if (((base & 0x03L) == 0x01) + && (save_command & 0x01)) { + // IO base + // set temp_register = amount of IO space requested + temp_register = base & 0xFFFFFFFE; + temp_register = (~temp_register) + 1; + + io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!io_node) + return -ENOMEM; + + io_node->base = + save_base & (~0x03L); + io_node->length = temp_register; + + io_node->next = func->io_head; + func->io_head = io_node; + } else + if (((base & 0x0BL) == 0x08) + && (save_command & 0x02)) { + // prefetchable memory base + temp_register = base & 0xFFFFFFF0; + temp_register = (~temp_register) + 1; + + p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!p_mem_node) + return -ENOMEM; + + p_mem_node->base = save_base & (~0x0FL); + p_mem_node->length = temp_register; + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } else + if (((base & 0x0BL) == 0x00) + && (save_command & 0x02)) { + // prefetchable memory base + temp_register = base & 0xFFFFFFF0; + temp_register = (~temp_register) + 1; + + mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!mem_node) + return -ENOMEM; + + mem_node->base = save_base & (~0x0FL); + mem_node->length = temp_register; + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } else + return(1); + } + } // End of base register loop + } else if ((header_type & 0x7F) == 0x00) { // Standard header + // Figure out IO and memory base lengths + for (cloop = 0x10; cloop <= 0x24; cloop += 4) { + pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &save_base); + + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base); + + temp_register = base; + + if (base) { // If this register is implemented + if (((base & 0x03L) == 0x01) + && (save_command & 0x01)) { + // IO base + // set temp_register = amount of IO space requested + temp_register = base & 0xFFFFFFFE; + temp_register = (~temp_register) + 1; + + io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!io_node) + return -ENOMEM; + + io_node->base = save_base & (~0x01L); + io_node->length = temp_register; + + io_node->next = func->io_head; + func->io_head = io_node; + } else + if (((base & 0x0BL) == 0x08) + && (save_command & 0x02)) { + // prefetchable memory base + temp_register = base & 0xFFFFFFF0; + temp_register = (~temp_register) + 1; + + p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!p_mem_node) + return -ENOMEM; + + p_mem_node->base = save_base & (~0x0FL); + p_mem_node->length = temp_register; + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } else + if (((base & 0x0BL) == 0x00) + && (save_command & 0x02)) { + // prefetchable memory base + temp_register = base & 0xFFFFFFF0; + temp_register = (~temp_register) + 1; + + mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!mem_node) + return -ENOMEM; + + mem_node->base = save_base & (~0x0FL); + mem_node->length = temp_register; + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } else + return(1); + } + } // End of base register loop + } else { // Some other unknown header type + } + + // find the next device in this slot + func = amdshpc_slot_find(func->bus, func->device, index++); + } + + return(0); +} + + +/* + * amdshpc_configure_board + * + * Copies saved configuration information to one slot. + * this is called recursively for bridge devices. + * this is for hot plug REPLACE! + * + * returns 0 if success + */ +int amdshpc_configure_board(struct controller *ctrl, struct pci_func * func) +{ + int cloop; + u8 header_type; + u8 secondary_bus; + int sub_bus; + struct pci_func *next; + u32 temp; + u32 rc; + int index = 0; + + dbg("%s", __FUNCTION__); + func = amdshpc_slot_find(func->bus, func->device, index++); + + while (func != NULL) { + int devfn = PCI_DEVFN(func->device, func->function); + + // Start at the top of config space so that the control + // registers are programmed last + for (cloop = 0x3C; cloop > 0; cloop -= 4) { + pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, func->config_space[cloop >> 2]); + } + + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + // If this is a bridge device, restore subordinate devices + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); + + sub_bus = (int) secondary_bus; + + next = amdshpc_slot_list[sub_bus]; + + while (next != NULL) { + rc = amdshpc_configure_board(ctrl, next); + if (rc) + return rc; + + next = next->next; + } + } else { + // Check all the base Address Registers to make sure + // they are the same. If not, the board is different. + for (cloop = 16; cloop < 40; cloop += 4) { + pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &temp); + if (temp != func->config_space[cloop >> 2]) { + dbg("Config space compare failure!!! offset = %x\n", cloop); + dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function); + dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop]); + return 1; + } + } + } + + func->configured = 1; + + func = amdshpc_slot_find(func->bus, func->device, index++); + } + + return 0; +} + + +/* + * amdshpc_valid_replace + * + * this function checks to see if a board is the same as the + * one it is replacing. this check will detect if the device's + * vendor or device id's are the same + * + * returns 0 if the board is the same nonzero otherwise + */ +int amdshpc_valid_replace(struct controller *ctrl, struct pci_func * func) +{ + u8 cloop; + u8 header_type; + u8 secondary_bus; + u8 type; + u32 temp_register = 0; + u32 base; + u32 rc; + struct pci_func *next; + int index = 0; + + dbg("%s", __FUNCTION__); + if (!func->is_a_board) + return(ADD_NOT_SUPPORTED); + + func = amdshpc_slot_find(func->bus, func->device, index++); + + while (func != NULL) { + int devfn = PCI_DEVFN(func->device, func->function); + + pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_VENDOR_ID, &temp_register); + + // No adapter present + if (temp_register == 0xFFFFFFFF) + return(NO_ADAPTER_PRESENT); + + if (temp_register != func->config_space[0]) + return(ADAPTER_NOT_SAME); + + // Check for same revision number and class code + pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_CLASS_REVISION, &temp_register); + + // Adapter not the same + if (temp_register != func->config_space[0x08 >> 2]) + return(ADAPTER_NOT_SAME); + + // Check for Bridge + pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + // In order to continue checking, we must program the + // bus registers in the bridge to respond to accesses + // for it's subordinate bus(es) + + temp_register = func->config_space[0x18 >> 2]; + pci_bus_write_config_dword(ctrl->pci_bus, devfn, PCI_PRIMARY_BUS, temp_register); + + secondary_bus = (temp_register >> 8) & 0xFF; + + next = amdshpc_slot_list[secondary_bus]; + + while (next != NULL) { + rc = amdshpc_valid_replace(ctrl, next); + if (rc) + return(rc); + + next = next->next; + } + + } + // Check to see if it is a standard config header + else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { + // Check subsystem vendor and ID + pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register); + + if (temp_register != func->config_space[0x2C >> 2]) { + // If it's a SMART-2 and the register isn't filled + // in, ignore the difference because + // they just have an old rev of the firmware + + if (!((func->config_space[0] == 0xAE100E11) + && (temp_register == 0x00L))) + return(ADAPTER_NOT_SAME); + } + // Figure out IO and memory base lengths + for (cloop = 0x10; cloop <= 0x24; cloop += 4) { + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base); + + if (base) { // If this register is implemented + if (base & 0x01L) { + // IO base + // set base = amount of IO space requested + base = base & 0xFFFFFFFE; + base = (~base) + 1; + + type = 1; + } else { + // memory base + base = base & 0xFFFFFFF0; + base = (~base) + 1; + + type = 0; + } + } else { + base = 0x0L; + type = 0; + } + + // Check information in slot structure + if (func->base_length[(cloop - 0x10) >> 2] != base) + return(ADAPTER_NOT_SAME); + + if (func->base_type[(cloop - 0x10) >> 2] != type) + return(ADAPTER_NOT_SAME); + + } // End of base register loop + + } // End of (type 0 config space) else + else { + // this is not a type 0 or 1 config space header so + // we don't know how to do it + return(DEVICE_TYPE_NOT_SUPPORTED); + } + + // Get the next function + func = amdshpc_slot_find(func->bus, func->device, index++); + } + + return(0); +} + + + +static int update_slot_info (struct controller *ctrl, struct slot *slot) +{ +// TO_DO_amd_update_slot_info(); + dbg("%s THIS FUNCTION IS STUBBED OUT!!!!!!!!!!!",__FUNCTION__); + return 0; + /* 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); + info->power_status = get_slot_enabled(ctrl, slot); + info->attention_status = cpq_get_attention_status(ctrl, slot); + info->latch_status = cpq_get_latch_status(ctrl, slot); + info->adapter_status = get_presence_status(ctrl, slot); + result = pci_hp_change_slot_info(buffer, info); + kfree (info); + return result; +*/ +} + + +/* + * slot_remove - Removes a node from the linked list of slots. + * @old_slot: slot to remove + * + * Returns 0 if successful, !0 otherwise. + */ +static int slot_remove(struct pci_func * old_slot) +{ + struct pci_func *next; + + dbg("%s", __FUNCTION__); + if (old_slot == NULL) + return(1); + + next = amdshpc_slot_list[old_slot->bus]; + + if (next == NULL) { + return(1); + } + + if (next == old_slot) { + amdshpc_slot_list[old_slot->bus] = old_slot->next; + amdshpc_destroy_board_resources(old_slot); + kfree(old_slot); + return(0); + } + + while ((next->next != old_slot) && (next->next != NULL)) { + next = next->next; + } + + if (next->next == old_slot) { + next->next = old_slot->next; + amdshpc_destroy_board_resources(old_slot); + kfree(old_slot); + return(0); + } else + return(2); +} + +// DJZ: I don't think is_bridge will work as is. +//FIXME +static int is_bridge(struct pci_func * func) +{ + dbg("%s", __FUNCTION__); + // Check the header type + if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01) + return 1; + else + return 0; +} + + +/** + * bridge_slot_remove - Removes a node from the linked list of slots. + * @bridge: bridge to remove + * + * Returns 0 if successful, !0 otherwise. + */ +static int bridge_slot_remove(struct pci_func *bridge) +{ + u8 subordinateBus, secondaryBus; + u8 tempBus; + struct pci_func *next; + + dbg("%s", __FUNCTION__); + if (bridge == NULL) + return(1); + + secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF; + subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF; + + for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) { + next = amdshpc_slot_list[tempBus]; + + while (!slot_remove(next)) { + next = amdshpc_slot_list[tempBus]; + } + } + + next = amdshpc_slot_list[bridge->bus]; + + if (next == NULL) { + return(1); + } + + if (next == bridge) { + amdshpc_slot_list[bridge->bus] = bridge->next; + kfree(bridge); + return(0); + } + + while ((next->next != bridge) && (next->next != NULL)) { + next = next->next; + } + + if (next->next == bridge) { + next->next = bridge->next; + kfree(bridge); + return(0); + } else + return(2); +} + +