2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* PCI Express Hot Plug Controller Driver
|
|
|
|
*
|
|
|
|
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
|
|
|
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
|
|
|
* Copyright (C) 2001 IBM Corp.
|
|
|
|
* Copyright (C) 2003-2004 Intel Corporation
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
|
|
* your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
|
|
|
|
* NON INFRINGEMENT. See the GNU General Public License for more
|
|
|
|
* details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*
|
2005-08-17 06:16:10 +08:00
|
|
|
* Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
#ifndef _PCIEHP_H
|
|
|
|
#define _PCIEHP_H
|
|
|
|
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/pci.h>
|
2006-10-14 11:05:19 +08:00
|
|
|
#include <linux/pci_hotplug.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/delay.h>
|
2006-01-08 17:02:05 +08:00
|
|
|
#include <linux/sched.h> /* signal_pending() */
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/pcieport_if.h>
|
2006-01-13 23:02:15 +08:00
|
|
|
#include <linux/mutex.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define MY_NAME "pciehp"
|
|
|
|
|
|
|
|
extern int pciehp_poll_mode;
|
|
|
|
extern int pciehp_poll_time;
|
|
|
|
extern int pciehp_debug;
|
2005-11-01 08:20:12 +08:00
|
|
|
extern int pciehp_force;
|
2007-03-07 07:02:26 +08:00
|
|
|
extern struct workqueue_struct *pciehp_wq;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-22 09:01:07 +08:00
|
|
|
#define dbg(format, arg...) \
|
2009-02-06 17:23:36 +08:00
|
|
|
do { \
|
|
|
|
if (pciehp_debug) \
|
|
|
|
printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); \
|
|
|
|
} while (0)
|
2006-12-22 09:01:07 +08:00
|
|
|
#define err(format, arg...) \
|
|
|
|
printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
|
|
|
|
#define info(format, arg...) \
|
|
|
|
printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
|
|
|
|
#define warn(format, arg...) \
|
|
|
|
printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-09-05 11:11:26 +08:00
|
|
|
#define ctrl_dbg(ctrl, format, arg...) \
|
|
|
|
do { \
|
|
|
|
if (pciehp_debug) \
|
2009-02-06 17:23:36 +08:00
|
|
|
dev_printk(KERN_DEBUG, &ctrl->pcie->device, \
|
2008-09-05 11:11:26 +08:00
|
|
|
format, ## arg); \
|
|
|
|
} while (0)
|
|
|
|
#define ctrl_err(ctrl, format, arg...) \
|
|
|
|
dev_err(&ctrl->pcie->device, format, ## arg)
|
|
|
|
#define ctrl_info(ctrl, format, arg...) \
|
|
|
|
dev_info(&ctrl->pcie->device, format, ## arg)
|
|
|
|
#define ctrl_warn(ctrl, format, arg...) \
|
|
|
|
dev_warn(&ctrl->pcie->device, format, ## arg)
|
|
|
|
|
2006-12-22 09:01:02 +08:00
|
|
|
#define SLOT_NAME_SIZE 10
|
2005-04-17 06:20:36 +08:00
|
|
|
struct slot {
|
|
|
|
u8 state;
|
|
|
|
struct controller *ctrl;
|
|
|
|
struct hotplug_slot *hotplug_slot;
|
2007-03-07 07:02:26 +08:00
|
|
|
struct delayed_work work; /* work for button event */
|
|
|
|
struct mutex lock;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct event_info {
|
|
|
|
u32 event_type;
|
2007-03-07 07:02:26 +08:00
|
|
|
struct slot *p_slot;
|
|
|
|
struct work_struct work;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct controller {
|
2006-09-23 01:17:29 +08:00
|
|
|
struct mutex ctrl_lock; /* controller lock */
|
2008-08-22 16:16:48 +08:00
|
|
|
struct pcie_device *pcie; /* PCI Express port service */
|
2009-09-15 16:24:46 +08:00
|
|
|
struct slot *slot;
|
2005-04-17 06:20:36 +08:00
|
|
|
wait_queue_head_t queue; /* sleep & wake process */
|
2008-04-26 05:39:06 +08:00
|
|
|
u32 slot_cap;
|
2006-12-22 09:01:04 +08:00
|
|
|
struct timer_list poll_timer;
|
2009-02-03 14:06:18 +08:00
|
|
|
unsigned int cmd_busy:1;
|
pciehp: fix slow probing
Fix the "pciehp probing slow" problem reported from Jan C. Nordholz in
http://bugzilla.kernel.org/show_bug.cgi?id=10751.
The command completed bit in Slot Status register applies only to
commands issued to control the attention indicator, power indicator,
power controller, or electromechanical interlock. However, writes to
other parts of the Slot Control register would end up writing to the
control fields. Hence, any write to Slot Control register is
considered as a command. However, if the controller doesn't support
any of attention indicator, power indicator, power controller and
electromechanical interlock, command completed bit would not set in
writing to Slot Control register. In this case, we should not wait for
command completed bit set, otherwise all commands would be considered
not completed in timeout seconds (1 sec.).
The cause of the problem is pciehp driver didn't take this situation
into account. This patch changes pciehp to take it into account. This
patch also add the check for "No Command Completed Support" bit in
Slot Capability register. If it is set, we should not wait for command
completed bit set as well.
This problem seems to be revealed by the commit
c27fb883dffe11aa4cb35ecea1fa1832ba45d4da that fixed the bug that
pciehp did not wait for command completed properly (pciehp just
ignored the command completion event).
Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2008-05-27 18:04:30 +08:00
|
|
|
unsigned int no_cmd_complete:1;
|
2008-10-22 13:31:44 +08:00
|
|
|
unsigned int link_active_reporting:1;
|
2009-01-29 11:31:18 +08:00
|
|
|
unsigned int notification_enabled:1;
|
2009-02-03 14:06:16 +08:00
|
|
|
unsigned int power_fault_detected;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#define INT_BUTTON_IGNORE 0
|
|
|
|
#define INT_PRESENCE_ON 1
|
|
|
|
#define INT_PRESENCE_OFF 2
|
|
|
|
#define INT_SWITCH_CLOSE 3
|
|
|
|
#define INT_SWITCH_OPEN 4
|
|
|
|
#define INT_POWER_FAULT 5
|
|
|
|
#define INT_POWER_FAULT_CLEAR 6
|
|
|
|
#define INT_BUTTON_PRESS 7
|
|
|
|
#define INT_BUTTON_RELEASE 8
|
|
|
|
#define INT_BUTTON_CANCEL 9
|
|
|
|
|
|
|
|
#define STATIC_STATE 0
|
|
|
|
#define BLINKINGON_STATE 1
|
|
|
|
#define BLINKINGOFF_STATE 2
|
|
|
|
#define POWERON_STATE 3
|
|
|
|
#define POWEROFF_STATE 4
|
|
|
|
|
2009-09-15 16:34:05 +08:00
|
|
|
#define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP)
|
|
|
|
#define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP)
|
|
|
|
#define MRL_SENS(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_MRLSP)
|
|
|
|
#define ATTN_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_AIP)
|
|
|
|
#define PWR_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PIP)
|
|
|
|
#define HP_SUPR_RM(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_HPS)
|
|
|
|
#define EMI(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_EIP)
|
|
|
|
#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS)
|
2009-09-15 16:28:53 +08:00
|
|
|
#define PSN(ctrl) ((ctrl)->slot_cap >> 19)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-03-07 07:02:26 +08:00
|
|
|
extern int pciehp_sysfs_enable_slot(struct slot *slot);
|
|
|
|
extern int pciehp_sysfs_disable_slot(struct slot *slot);
|
2008-05-27 18:03:16 +08:00
|
|
|
extern u8 pciehp_handle_attention_button(struct slot *p_slot);
|
2009-09-15 16:30:48 +08:00
|
|
|
extern u8 pciehp_handle_switch_change(struct slot *p_slot);
|
2008-05-27 18:03:16 +08:00
|
|
|
extern u8 pciehp_handle_presence_change(struct slot *p_slot);
|
|
|
|
extern u8 pciehp_handle_power_fault(struct slot *p_slot);
|
2006-12-22 09:01:07 +08:00
|
|
|
extern int pciehp_configure_device(struct slot *p_slot);
|
|
|
|
extern int pciehp_unconfigure_device(struct slot *p_slot);
|
2007-03-22 02:45:31 +08:00
|
|
|
extern void pciehp_queue_pushbutton_work(struct work_struct *work);
|
2008-06-20 11:07:08 +08:00
|
|
|
struct controller *pcie_init(struct pcie_device *dev);
|
2009-01-29 11:31:18 +08:00
|
|
|
int pcie_init_notification(struct controller *ctrl);
|
2007-11-29 07:11:28 +08:00
|
|
|
int pciehp_enable_slot(struct slot *p_slot);
|
2007-11-29 07:12:00 +08:00
|
|
|
int pciehp_disable_slot(struct slot *p_slot);
|
2008-06-20 11:07:08 +08:00
|
|
|
int pcie_enable_notification(struct controller *ctrl);
|
2009-09-15 16:30:48 +08:00
|
|
|
int pciehp_power_on_slot(struct slot *slot);
|
|
|
|
int pciehp_power_off_slot(struct slot *slot);
|
|
|
|
int pciehp_get_power_status(struct slot *slot, u8 *status);
|
|
|
|
int pciehp_get_attention_status(struct slot *slot, u8 *status);
|
|
|
|
|
|
|
|
int pciehp_set_attention_status(struct slot *slot, u8 status);
|
|
|
|
int pciehp_get_latch_status(struct slot *slot, u8 *status);
|
|
|
|
int pciehp_get_adapter_status(struct slot *slot, u8 *status);
|
|
|
|
int pciehp_get_max_link_speed(struct slot *slot, enum pci_bus_speed *speed);
|
|
|
|
int pciehp_get_max_link_width(struct slot *slot, enum pcie_link_width *val);
|
|
|
|
int pciehp_get_cur_link_speed(struct slot *slot, enum pci_bus_speed *speed);
|
|
|
|
int pciehp_get_cur_link_width(struct slot *slot, enum pcie_link_width *val);
|
|
|
|
int pciehp_query_power_fault(struct slot *slot);
|
|
|
|
void pciehp_green_led_on(struct slot *slot);
|
|
|
|
void pciehp_green_led_off(struct slot *slot);
|
|
|
|
void pciehp_green_led_blink(struct slot *slot);
|
|
|
|
int pciehp_check_link_status(struct controller *ctrl);
|
|
|
|
void pciehp_release_ctrl(struct controller *ctrl);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-10-21 07:41:38 +08:00
|
|
|
static inline const char *slot_name(struct slot *slot)
|
|
|
|
{
|
|
|
|
return hotplug_slot_name(slot->hotplug_slot);
|
|
|
|
}
|
|
|
|
|
2006-03-04 02:16:05 +08:00
|
|
|
#ifdef CONFIG_ACPI
|
2006-08-08 21:44:26 +08:00
|
|
|
#include <acpi/acpi.h>
|
|
|
|
#include <acpi/acpi_bus.h>
|
|
|
|
#include <linux/pci-acpi.h>
|
|
|
|
|
2008-12-17 11:07:38 +08:00
|
|
|
extern void __init pciehp_acpi_slot_detection_init(void);
|
|
|
|
extern int pciehp_acpi_slot_detection_check(struct pci_dev *dev);
|
|
|
|
|
|
|
|
static inline void pciehp_firmware_init(void)
|
|
|
|
{
|
|
|
|
pciehp_acpi_slot_detection_init();
|
|
|
|
}
|
|
|
|
|
2008-05-28 14:01:03 +08:00
|
|
|
static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
|
|
|
|
{
|
2008-12-17 11:07:38 +08:00
|
|
|
int retval;
|
2008-05-28 14:01:03 +08:00
|
|
|
u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
|
|
|
|
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
|
2008-12-17 11:07:38 +08:00
|
|
|
retval = acpi_get_hp_hw_control_from_firmware(dev, flags);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
return pciehp_acpi_slot_detection_check(dev);
|
2008-05-28 14:01:03 +08:00
|
|
|
}
|
2006-03-04 02:16:05 +08:00
|
|
|
#else
|
2008-12-17 11:07:38 +08:00
|
|
|
#define pciehp_firmware_init() do {} while (0)
|
2006-03-04 02:16:05 +08:00
|
|
|
#define pciehp_get_hp_hw_control_from_firmware(dev) 0
|
|
|
|
#endif /* CONFIG_ACPI */
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif /* _PCIEHP_H */
|