Merge branch 'pci/hotplug'
- Differentiate between pciehp surprise and safe removal (Lukas Wunner)
- Remove unnecessary pciehp includes (Lukas Wunner)
- Drop pciehp hotplug_slot_ops wrappers (Lukas Wunner)
- Tolerate PCIe Slot Presence Detect being hardwired to zero to
workaround broken hardware, e.g., the Wilocity switch/wireless device
(Lukas Wunner)
- Unify pciehp controller & slot structs (Lukas Wunner)
- Constify hotplug_slot_ops (Lukas Wunner)
- Drop hotplug_slot_info (Lukas Wunner)
- Embed hotplug_slot struct into users instead of allocating it
separately (Lukas Wunner)
- Initialize PCIe port service drivers directly instead of relying on
initcall ordering (Keith Busch)
- Restore PCI config state after a slot reset (Keith Busch)
- Save/restore DPC config state along with other PCI config state (Keith
Busch)
- Reference count devices during AER handling to avoid race issue with
concurrent hot removal (Keith Busch)
- If an Upstream Port reports ERR_FATAL, don't try to read the Port's
config space because it is probably unreachable (Keith Busch)
- During error handling, use slot-specific reset instead of secondary
bus reset to avoid link up/down issues on hotplug ports (Keith Busch)
- Restore previous AER/DPC handling that does not remove and re-enumerate
devices on ERR_FATAL (Keith Busch)
- Notify all drivers that may be affected by error recovery resets (Keith
Busch)
- Always generate error recovery uevents, even if a driver doesn't have
error callbacks (Keith Busch)
- Make PCIe link active reporting detection generic (Keith Busch)
- Support D3cold in PCIe hierarchies during system sleep and runtime,
including hotplug and Thunderbolt ports (Mika Westerberg)
- Handle hpmemsize/hpiosize kernel parameters uniformly, whether slots
are empty or occupied (Jon Derrick)
- Remove duplicated include from pci/pcie/err.c and unused variable from
cpqphp (YueHaibing)
- Remove driver pci_cleanup_aer_uncorrect_error_status() calls (Oza
Pawandeep)
- Uninline PCI bus accessors for better ftracing (Keith Busch)
- Remove unused AER Root Port .error_resume method (Keith Busch)
- Use kfifo in AER instead of a local version (Keith Busch)
- Use threaded IRQ in AER bottom half (Keith Busch)
- Use managed resources in AER core (Keith Busch)
- Reuse pcie_port_find_device() for AER injection (Keith Busch)
- Abstract AER interrupt handling to disconnect error injection (Keith
Busch)
- Refactor AER injection callbacks to simplify future improvments (Keith
Busch)
* pci/hotplug:
PCI/AER: Refactor error injection fallbacks
PCI/AER: Abstract AER interrupt handling
PCI/AER: Reuse existing pcie_port_find_device() interface
PCI/AER: Use managed resource allocations
PCI/AER: Use threaded IRQ for bottom half
PCI/AER: Use kfifo_in_spinlocked() to insert locked elements
PCI/AER: Use kfifo for tracking events instead of reimplementing it
PCI/AER: Remove error source from AER struct aer_rpc
PCI/AER: Remove unused aer_error_resume()
PCI: Uninline PCI bus accessors for better ftracing
PCI/AER: Remove pci_cleanup_aer_uncorrect_error_status() calls
PCI: pnv_php: Use kmemdup()
PCI: cpqphp: Remove set but not used variable 'physical_slot'
PCI/ERR: Remove duplicated include from err.c
PCI: Equalize hotplug memory and io for occupied and empty slots
PCI / ACPI: Whitelist D3 for more PCIe hotplug ports
ACPI / property: Allow multiple property compatible _DSD entries
PCI/PME: Implement runtime PM callbacks
PCI: pciehp: Implement runtime PM callbacks
PCI/portdrv: Add runtime PM hooks for port service drivers
PCI/portdrv: Resume upon exit from system suspend if left runtime suspended
PCI: pciehp: Do not handle events if interrupts are masked
PCI: pciehp: Disable hotplug interrupt during suspend
PCI / ACPI: Enable wake automatically for power managed bridges
PCI: Do not skip power-managed bridges in pci_enable_wake()
PCI: Make link active reporting detection generic
PCI: Unify device inaccessible
PCI/ERR: Always report current recovery status for udev
PCI/ERR: Simplify broadcast callouts
PCI/ERR: Run error recovery callbacks for all affected devices
PCI/ERR: Handle fatal error recovery
PCI/ERR: Use slot reset if available
PCI/AER: Don't read upstream ports below fatal errors
PCI/AER: Take reference on error devices
PCI/DPC: Save and restore config state
PCI: portdrv: Restore PCI config state on slot reset
PCI: portdrv: Initialize service drivers directly
PCI: hotplug: Document TODOs
PCI: hotplug: Embed hotplug_slot
PCI: hotplug: Drop hotplug_slot_info
PCI: hotplug: Constify hotplug_slot_ops
PCI: pciehp: Reshuffle controller struct for clarity
PCI: pciehp: Rename controller struct members for clarity
PCI: pciehp: Unify controller and slot structs
PCI: pciehp: Tolerate Presence Detect hardwired to zero
PCI: pciehp: Drop hotplug_slot_ops wrappers
PCI: pciehp: Drop unnecessary includes
PCI: pciehp: Differentiate between surprise and safe removal
PCI: Simplify disconnected marking
This commit is contained in:
@@ -110,7 +110,7 @@ The actual steps taken by a platform to recover from a PCI error
|
|||||||
event will be platform-dependent, but will follow the general
|
event will be platform-dependent, but will follow the general
|
||||||
sequence described below.
|
sequence described below.
|
||||||
|
|
||||||
STEP 0: Error Event: ERR_NONFATAL
|
STEP 0: Error Event
|
||||||
-------------------
|
-------------------
|
||||||
A PCI bus error is detected by the PCI hardware. On powerpc, the slot
|
A PCI bus error is detected by the PCI hardware. On powerpc, the slot
|
||||||
is isolated, in that all I/O is blocked: all reads return 0xffffffff,
|
is isolated, in that all I/O is blocked: all reads return 0xffffffff,
|
||||||
@@ -228,7 +228,13 @@ proceeds to either STEP3 (Link Reset) or to STEP 5 (Resume Operations).
|
|||||||
If any driver returned PCI_ERS_RESULT_NEED_RESET, then the platform
|
If any driver returned PCI_ERS_RESULT_NEED_RESET, then the platform
|
||||||
proceeds to STEP 4 (Slot Reset)
|
proceeds to STEP 4 (Slot Reset)
|
||||||
|
|
||||||
STEP 3: Slot Reset
|
STEP 3: Link Reset
|
||||||
|
------------------
|
||||||
|
The platform resets the link. This is a PCI-Express specific step
|
||||||
|
and is done whenever a fatal error has been detected that can be
|
||||||
|
"solved" by resetting the link.
|
||||||
|
|
||||||
|
STEP 4: Slot Reset
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
In response to a return value of PCI_ERS_RESULT_NEED_RESET, the
|
In response to a return value of PCI_ERS_RESULT_NEED_RESET, the
|
||||||
@@ -314,7 +320,7 @@ Failure).
|
|||||||
>>> However, it probably should.
|
>>> However, it probably should.
|
||||||
|
|
||||||
|
|
||||||
STEP 4: Resume Operations
|
STEP 5: Resume Operations
|
||||||
-------------------------
|
-------------------------
|
||||||
The platform will call the resume() callback on all affected device
|
The platform will call the resume() callback on all affected device
|
||||||
drivers if all drivers on the segment have returned
|
drivers if all drivers on the segment have returned
|
||||||
@@ -326,7 +332,7 @@ a result code.
|
|||||||
At this point, if a new error happens, the platform will restart
|
At this point, if a new error happens, the platform will restart
|
||||||
a new error recovery sequence.
|
a new error recovery sequence.
|
||||||
|
|
||||||
STEP 5: Permanent Failure
|
STEP 6: Permanent Failure
|
||||||
-------------------------
|
-------------------------
|
||||||
A "permanent failure" has occurred, and the platform cannot recover
|
A "permanent failure" has occurred, and the platform cannot recover
|
||||||
the device. The platform will call error_detected() with a
|
the device. The platform will call error_detected() with a
|
||||||
@@ -349,27 +355,6 @@ errors. See the discussion in powerpc/eeh-pci-error-recovery.txt
|
|||||||
for additional detail on real-life experience of the causes of
|
for additional detail on real-life experience of the causes of
|
||||||
software errors.
|
software errors.
|
||||||
|
|
||||||
STEP 0: Error Event: ERR_FATAL
|
|
||||||
-------------------
|
|
||||||
PCI bus error is detected by the PCI hardware. On powerpc, the slot is
|
|
||||||
isolated, in that all I/O is blocked: all reads return 0xffffffff, all
|
|
||||||
writes are ignored.
|
|
||||||
|
|
||||||
STEP 1: Remove devices
|
|
||||||
--------------------
|
|
||||||
Platform removes the devices depending on the error agent, it could be
|
|
||||||
this port for all subordinates or upstream component (likely downstream
|
|
||||||
port)
|
|
||||||
|
|
||||||
STEP 2: Reset link
|
|
||||||
--------------------
|
|
||||||
The platform resets the link. This is a PCI-Express specific step and is
|
|
||||||
done whenever a fatal error has been detected that can be "solved" by
|
|
||||||
resetting the link.
|
|
||||||
|
|
||||||
STEP 3: Re-enumerate the devices
|
|
||||||
--------------------
|
|
||||||
Initiates the re-enumeration.
|
|
||||||
|
|
||||||
Conclusion; General Remarks
|
Conclusion; General Remarks
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs,
|
|||||||
|
|
||||||
struct pnv_php_slot {
|
struct pnv_php_slot {
|
||||||
struct hotplug_slot slot;
|
struct hotplug_slot slot;
|
||||||
struct hotplug_slot_info slot_info;
|
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
char *name;
|
char *name;
|
||||||
int slot_no;
|
int slot_no;
|
||||||
@@ -72,6 +71,7 @@ struct pnv_php_slot {
|
|||||||
struct pci_dev *pdev;
|
struct pci_dev *pdev;
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus;
|
||||||
bool power_state_check;
|
bool power_state_check;
|
||||||
|
u8 attention_state;
|
||||||
void *fdt;
|
void *fdt;
|
||||||
void *dt;
|
void *dt;
|
||||||
struct of_changeset ocs;
|
struct of_changeset ocs;
|
||||||
|
|||||||
@@ -24,11 +24,15 @@ static int acpi_data_get_property_array(const struct acpi_device_data *data,
|
|||||||
acpi_object_type type,
|
acpi_object_type type,
|
||||||
const union acpi_object **obj);
|
const union acpi_object **obj);
|
||||||
|
|
||||||
/* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
|
static const guid_t prp_guids[] = {
|
||||||
static const guid_t prp_guid =
|
/* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
|
||||||
GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
|
GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
|
||||||
0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01);
|
0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01),
|
||||||
/* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
|
/* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */
|
||||||
|
GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3,
|
||||||
|
0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4),
|
||||||
|
};
|
||||||
|
|
||||||
static const guid_t ads_guid =
|
static const guid_t ads_guid =
|
||||||
GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6,
|
GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6,
|
||||||
0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b);
|
0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b);
|
||||||
@@ -56,6 +60,7 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc,
|
|||||||
dn->name = link->package.elements[0].string.pointer;
|
dn->name = link->package.elements[0].string.pointer;
|
||||||
dn->fwnode.ops = &acpi_data_fwnode_ops;
|
dn->fwnode.ops = &acpi_data_fwnode_ops;
|
||||||
dn->parent = parent;
|
dn->parent = parent;
|
||||||
|
INIT_LIST_HEAD(&dn->data.properties);
|
||||||
INIT_LIST_HEAD(&dn->data.subnodes);
|
INIT_LIST_HEAD(&dn->data.subnodes);
|
||||||
|
|
||||||
result = acpi_extract_properties(desc, &dn->data);
|
result = acpi_extract_properties(desc, &dn->data);
|
||||||
@@ -288,6 +293,35 @@ static void acpi_init_of_compatible(struct acpi_device *adev)
|
|||||||
adev->flags.of_compatible_ok = 1;
|
adev->flags.of_compatible_ok = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool acpi_is_property_guid(const guid_t *guid)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(prp_guids); i++) {
|
||||||
|
if (guid_equal(guid, &prp_guids[i]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct acpi_device_properties *
|
||||||
|
acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid,
|
||||||
|
const union acpi_object *properties)
|
||||||
|
{
|
||||||
|
struct acpi_device_properties *props;
|
||||||
|
|
||||||
|
props = kzalloc(sizeof(*props), GFP_KERNEL);
|
||||||
|
if (props) {
|
||||||
|
INIT_LIST_HEAD(&props->list);
|
||||||
|
props->guid = guid;
|
||||||
|
props->properties = properties;
|
||||||
|
list_add_tail(&props->list, &data->properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
static bool acpi_extract_properties(const union acpi_object *desc,
|
static bool acpi_extract_properties(const union acpi_object *desc,
|
||||||
struct acpi_device_data *data)
|
struct acpi_device_data *data)
|
||||||
{
|
{
|
||||||
@@ -312,7 +346,7 @@ static bool acpi_extract_properties(const union acpi_object *desc,
|
|||||||
properties->type != ACPI_TYPE_PACKAGE)
|
properties->type != ACPI_TYPE_PACKAGE)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!guid_equal((guid_t *)guid->buffer.pointer, &prp_guid))
|
if (!acpi_is_property_guid((guid_t *)guid->buffer.pointer))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -320,13 +354,13 @@ static bool acpi_extract_properties(const union acpi_object *desc,
|
|||||||
* package immediately following it.
|
* package immediately following it.
|
||||||
*/
|
*/
|
||||||
if (!acpi_properties_format_valid(properties))
|
if (!acpi_properties_format_valid(properties))
|
||||||
break;
|
continue;
|
||||||
|
|
||||||
data->properties = properties;
|
acpi_data_add_props(data, (const guid_t *)guid->buffer.pointer,
|
||||||
return true;
|
properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return !list_empty(&data->properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
void acpi_init_properties(struct acpi_device *adev)
|
void acpi_init_properties(struct acpi_device *adev)
|
||||||
@@ -336,6 +370,7 @@ void acpi_init_properties(struct acpi_device *adev)
|
|||||||
acpi_status status;
|
acpi_status status;
|
||||||
bool acpi_of = false;
|
bool acpi_of = false;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&adev->data.properties);
|
||||||
INIT_LIST_HEAD(&adev->data.subnodes);
|
INIT_LIST_HEAD(&adev->data.subnodes);
|
||||||
|
|
||||||
if (!adev->handle)
|
if (!adev->handle)
|
||||||
@@ -398,11 +433,16 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list)
|
|||||||
|
|
||||||
void acpi_free_properties(struct acpi_device *adev)
|
void acpi_free_properties(struct acpi_device *adev)
|
||||||
{
|
{
|
||||||
|
struct acpi_device_properties *props, *tmp;
|
||||||
|
|
||||||
acpi_destroy_nondev_subnodes(&adev->data.subnodes);
|
acpi_destroy_nondev_subnodes(&adev->data.subnodes);
|
||||||
ACPI_FREE((void *)adev->data.pointer);
|
ACPI_FREE((void *)adev->data.pointer);
|
||||||
adev->data.of_compatible = NULL;
|
adev->data.of_compatible = NULL;
|
||||||
adev->data.pointer = NULL;
|
adev->data.pointer = NULL;
|
||||||
adev->data.properties = NULL;
|
list_for_each_entry_safe(props, tmp, &adev->data.properties, list) {
|
||||||
|
list_del(&props->list);
|
||||||
|
kfree(props);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -427,32 +467,37 @@ static int acpi_data_get_property(const struct acpi_device_data *data,
|
|||||||
const char *name, acpi_object_type type,
|
const char *name, acpi_object_type type,
|
||||||
const union acpi_object **obj)
|
const union acpi_object **obj)
|
||||||
{
|
{
|
||||||
const union acpi_object *properties;
|
const struct acpi_device_properties *props;
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!data || !name)
|
if (!data || !name)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!data->pointer || !data->properties)
|
if (!data->pointer || list_empty(&data->properties))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
properties = data->properties;
|
list_for_each_entry(props, &data->properties, list) {
|
||||||
for (i = 0; i < properties->package.count; i++) {
|
const union acpi_object *properties;
|
||||||
const union acpi_object *propname, *propvalue;
|
unsigned int i;
|
||||||
const union acpi_object *property;
|
|
||||||
|
|
||||||
property = &properties->package.elements[i];
|
properties = props->properties;
|
||||||
|
for (i = 0; i < properties->package.count; i++) {
|
||||||
|
const union acpi_object *propname, *propvalue;
|
||||||
|
const union acpi_object *property;
|
||||||
|
|
||||||
propname = &property->package.elements[0];
|
property = &properties->package.elements[i];
|
||||||
propvalue = &property->package.elements[1];
|
|
||||||
|
|
||||||
if (!strcmp(name, propname->string.pointer)) {
|
propname = &property->package.elements[0];
|
||||||
if (type != ACPI_TYPE_ANY && propvalue->type != type)
|
propvalue = &property->package.elements[1];
|
||||||
return -EPROTO;
|
|
||||||
if (obj)
|
|
||||||
*obj = propvalue;
|
|
||||||
|
|
||||||
return 0;
|
if (!strcmp(name, propname->string.pointer)) {
|
||||||
|
if (type != ACPI_TYPE_ANY &&
|
||||||
|
propvalue->type != type)
|
||||||
|
return -EPROTO;
|
||||||
|
if (obj)
|
||||||
|
*obj = propvalue;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ void acpi_extract_apple_properties(struct acpi_device *adev)
|
|||||||
}
|
}
|
||||||
WARN_ON(free_space != (void *)newprops + newsize);
|
WARN_ON(free_space != (void *)newprops + newsize);
|
||||||
|
|
||||||
adev->data.properties = newprops;
|
|
||||||
adev->data.pointer = newprops;
|
adev->data.pointer = newprops;
|
||||||
|
acpi_data_add_props(&adev->data, &apple_prp_guid, newprops);
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
ACPI_FREE(props);
|
ACPI_FREE(props);
|
||||||
|
|||||||
@@ -198,7 +198,6 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev)
|
|||||||
pr_err("QAT: Can't find acceleration device\n");
|
pr_err("QAT: Can't find acceleration device\n");
|
||||||
return PCI_ERS_RESULT_DISCONNECT;
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
}
|
}
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (adf_dev_aer_schedule_reset(accel_dev, ADF_DEV_RESET_SYNC))
|
if (adf_dev_aer_schedule_reset(accel_dev, ADF_DEV_RESET_SYNC))
|
||||||
return PCI_ERS_RESULT_DISCONNECT;
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
|
|
||||||
|
|||||||
@@ -1252,7 +1252,6 @@ static pci_ers_result_t ioat_pcie_error_detected(struct pci_dev *pdev,
|
|||||||
static pci_ers_result_t ioat_pcie_error_slot_reset(struct pci_dev *pdev)
|
static pci_ers_result_t ioat_pcie_error_slot_reset(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
pci_ers_result_t result = PCI_ERS_RESULT_RECOVERED;
|
pci_ers_result_t result = PCI_ERS_RESULT_RECOVERED;
|
||||||
int err;
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "%s post reset handling\n", DRV_NAME);
|
dev_dbg(&pdev->dev, "%s post reset handling\n", DRV_NAME);
|
||||||
|
|
||||||
@@ -1267,12 +1266,6 @@ static pci_ers_result_t ioat_pcie_error_slot_reset(struct pci_dev *pdev)
|
|||||||
pci_wake_from_d3(pdev, false);
|
pci_wake_from_d3(pdev, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (err) {
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"AER uncorrect error status clear failed: %#x\n", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1198,7 +1198,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
|
|||||||
bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
|
bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
|
||||||
{
|
{
|
||||||
/* Never allow fallback if the device has properties */
|
/* Never allow fallback if the device has properties */
|
||||||
if (adev->data.properties || adev->driver_gpios)
|
if (acpi_dev_has_props(adev) || adev->driver_gpios)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return con_id == NULL;
|
return con_id == NULL;
|
||||||
|
|||||||
@@ -650,7 +650,6 @@ pci_resume(struct pci_dev *pdev)
|
|||||||
struct hfi1_devdata *dd = pci_get_drvdata(pdev);
|
struct hfi1_devdata *dd = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
dd_dev_info(dd, "HFI1 resume function called\n");
|
dd_dev_info(dd, "HFI1 resume function called\n");
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
/*
|
/*
|
||||||
* Running jobs will fail, since it's asynchronous
|
* Running jobs will fail, since it's asynchronous
|
||||||
* unlike sysfs-requested reset. Better than
|
* unlike sysfs-requested reset. Better than
|
||||||
|
|||||||
@@ -597,7 +597,6 @@ qib_pci_resume(struct pci_dev *pdev)
|
|||||||
struct qib_devdata *dd = pci_get_drvdata(pdev);
|
struct qib_devdata *dd = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
qib_devinfo(pdev, "QIB resume function called\n");
|
qib_devinfo(pdev, "QIB resume function called\n");
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
/*
|
/*
|
||||||
* Running jobs will fail, since it's asynchronous
|
* Running jobs will fail, since it's asynchronous
|
||||||
* unlike sysfs-requested reset. Better than
|
* unlike sysfs-requested reset. Better than
|
||||||
|
|||||||
@@ -1964,8 +1964,6 @@ static pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev)
|
|||||||
if (!alx_reset_mac(hw))
|
if (!alx_reset_mac(hw))
|
||||||
rc = PCI_ERS_RESULT_RECOVERED;
|
rc = PCI_ERS_RESULT_RECOVERED;
|
||||||
out:
|
out:
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
|
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
|||||||
@@ -8793,13 +8793,6 @@ static pci_ers_result_t bnx2_io_slot_reset(struct pci_dev *pdev)
|
|||||||
if (!(bp->flags & BNX2_FLAG_AER_ENABLED))
|
if (!(bp->flags & BNX2_FLAG_AER_ENABLED))
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
err = pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (err) {
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n",
|
|
||||||
err); /* non-fatal, continue */
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14385,14 +14385,6 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev)
|
|||||||
|
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
|
|
||||||
/* If AER, perform cleanup of the PCIe registers */
|
|
||||||
if (bp->flags & AER_ENABLED) {
|
|
||||||
if (pci_cleanup_aer_uncorrect_error_status(pdev))
|
|
||||||
BNX2X_ERR("pci_cleanup_aer_uncorrect_error_status failed\n");
|
|
||||||
else
|
|
||||||
DP(NETIF_MSG_HW, "pci_cleanup_aer_uncorrect_error_status succeeded\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return PCI_ERS_RESULT_RECOVERED;
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9231,13 +9231,6 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)
|
|||||||
|
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
|
|
||||||
err = pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (err) {
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n",
|
|
||||||
err); /* non-fatal, continue */
|
|
||||||
}
|
|
||||||
|
|
||||||
return PCI_ERS_RESULT_RECOVERED;
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4747,7 +4747,6 @@ static pci_ers_result_t eeh_slot_reset(struct pci_dev *pdev)
|
|||||||
pci_set_master(pdev);
|
pci_set_master(pdev);
|
||||||
pci_restore_state(pdev);
|
pci_restore_state(pdev);
|
||||||
pci_save_state(pdev);
|
pci_save_state(pdev);
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
|
|
||||||
if (t4_wait_dev_ready(adap->regs) < 0)
|
if (t4_wait_dev_ready(adap->regs) < 0)
|
||||||
return PCI_ERS_RESULT_DISCONNECT;
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
|
|||||||
@@ -6151,7 +6151,6 @@ static pci_ers_result_t be_eeh_reset(struct pci_dev *pdev)
|
|||||||
if (status)
|
if (status)
|
||||||
return PCI_ERS_RESULT_DISCONNECT;
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
be_clear_error(adapter, BE_CLEAR_ALL);
|
be_clear_error(adapter, BE_CLEAR_ALL);
|
||||||
return PCI_ERS_RESULT_RECOVERED;
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6854,8 +6854,6 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)
|
|||||||
result = PCI_ERS_RESULT_RECOVERED;
|
result = PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2462,8 +2462,6 @@ static pci_ers_result_t fm10k_io_slot_reset(struct pci_dev *pdev)
|
|||||||
result = PCI_ERS_RESULT_RECOVERED;
|
result = PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14227,7 +14227,6 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev)
|
|||||||
{
|
{
|
||||||
struct i40e_pf *pf = pci_get_drvdata(pdev);
|
struct i40e_pf *pf = pci_get_drvdata(pdev);
|
||||||
pci_ers_result_t result;
|
pci_ers_result_t result;
|
||||||
int err;
|
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "%s\n", __func__);
|
dev_dbg(&pdev->dev, "%s\n", __func__);
|
||||||
@@ -14248,14 +14247,6 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev)
|
|||||||
result = PCI_ERS_RESULT_DISCONNECT;
|
result = PCI_ERS_RESULT_DISCONNECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (err) {
|
|
||||||
dev_info(&pdev->dev,
|
|
||||||
"pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n",
|
|
||||||
err);
|
|
||||||
/* non-fatal, continue */
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9116,7 +9116,6 @@ static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev)
|
|||||||
struct igb_adapter *adapter = netdev_priv(netdev);
|
struct igb_adapter *adapter = netdev_priv(netdev);
|
||||||
struct e1000_hw *hw = &adapter->hw;
|
struct e1000_hw *hw = &adapter->hw;
|
||||||
pci_ers_result_t result;
|
pci_ers_result_t result;
|
||||||
int err;
|
|
||||||
|
|
||||||
if (pci_enable_device_mem(pdev)) {
|
if (pci_enable_device_mem(pdev)) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
@@ -9140,14 +9139,6 @@ static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev)
|
|||||||
result = PCI_ERS_RESULT_RECOVERED;
|
result = PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (err) {
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n",
|
|
||||||
err);
|
|
||||||
/* non-fatal, continue */
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11075,8 +11075,6 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
|
|||||||
/* Free device reference count */
|
/* Free device reference count */
|
||||||
pci_dev_put(vfdev);
|
pci_dev_put(vfdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -11126,7 +11124,6 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
|
|||||||
{
|
{
|
||||||
struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
|
struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
|
||||||
pci_ers_result_t result;
|
pci_ers_result_t result;
|
||||||
int err;
|
|
||||||
|
|
||||||
if (pci_enable_device_mem(pdev)) {
|
if (pci_enable_device_mem(pdev)) {
|
||||||
e_err(probe, "Cannot re-enable PCI device after reset.\n");
|
e_err(probe, "Cannot re-enable PCI device after reset.\n");
|
||||||
@@ -11146,13 +11143,6 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
|
|||||||
result = PCI_ERS_RESULT_RECOVERED;
|
result = PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (err) {
|
|
||||||
e_dev_err("pci_cleanup_aer_uncorrect_error_status "
|
|
||||||
"failed 0x%0x\n", err);
|
|
||||||
/* non-fatal, continue */
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1790,11 +1790,6 @@ static pci_ers_result_t netxen_io_slot_reset(struct pci_dev *pdev)
|
|||||||
return err ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
|
return err ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void netxen_io_resume(struct pci_dev *pdev)
|
|
||||||
{
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void netxen_nic_shutdown(struct pci_dev *pdev)
|
static void netxen_nic_shutdown(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
struct netxen_adapter *adapter = pci_get_drvdata(pdev);
|
struct netxen_adapter *adapter = pci_get_drvdata(pdev);
|
||||||
@@ -3488,7 +3483,6 @@ netxen_free_ip_list(struct netxen_adapter *adapter, bool master)
|
|||||||
static const struct pci_error_handlers netxen_err_handler = {
|
static const struct pci_error_handlers netxen_err_handler = {
|
||||||
.error_detected = netxen_io_error_detected,
|
.error_detected = netxen_io_error_detected,
|
||||||
.slot_reset = netxen_io_slot_reset,
|
.slot_reset = netxen_io_slot_reset,
|
||||||
.resume = netxen_io_resume,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct pci_driver netxen_driver = {
|
static struct pci_driver netxen_driver = {
|
||||||
|
|||||||
@@ -4232,7 +4232,6 @@ static void qlcnic_83xx_io_resume(struct pci_dev *pdev)
|
|||||||
{
|
{
|
||||||
struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
|
struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (test_and_clear_bit(__QLCNIC_AER, &adapter->state))
|
if (test_and_clear_bit(__QLCNIC_AER, &adapter->state))
|
||||||
qlcnic_83xx_aer_start_poll_work(adapter);
|
qlcnic_83xx_aer_start_poll_work(adapter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3975,7 +3975,6 @@ static void qlcnic_82xx_io_resume(struct pci_dev *pdev)
|
|||||||
u32 state;
|
u32 state;
|
||||||
struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
|
struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
|
state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
|
||||||
if (state == QLCNIC_DEV_READY && test_and_clear_bit(__QLCNIC_AER,
|
if (state == QLCNIC_DEV_READY && test_and_clear_bit(__QLCNIC_AER,
|
||||||
&adapter->state))
|
&adapter->state))
|
||||||
|
|||||||
@@ -3847,7 +3847,6 @@ static pci_ers_result_t efx_io_slot_reset(struct pci_dev *pdev)
|
|||||||
{
|
{
|
||||||
struct efx_nic *efx = pci_get_drvdata(pdev);
|
struct efx_nic *efx = pci_get_drvdata(pdev);
|
||||||
pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
|
pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (pci_enable_device(pdev)) {
|
if (pci_enable_device(pdev)) {
|
||||||
netif_err(efx, hw, efx->net_dev,
|
netif_err(efx, hw, efx->net_dev,
|
||||||
@@ -3855,13 +3854,6 @@ static pci_ers_result_t efx_io_slot_reset(struct pci_dev *pdev)
|
|||||||
status = PCI_ERS_RESULT_DISCONNECT;
|
status = PCI_ERS_RESULT_DISCONNECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (rc) {
|
|
||||||
netif_err(efx, hw, efx->net_dev,
|
|
||||||
"pci_cleanup_aer_uncorrect_error_status failed (%d)\n", rc);
|
|
||||||
/* Non-fatal error. Continue. */
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3186,7 +3186,6 @@ static pci_ers_result_t ef4_io_slot_reset(struct pci_dev *pdev)
|
|||||||
{
|
{
|
||||||
struct ef4_nic *efx = pci_get_drvdata(pdev);
|
struct ef4_nic *efx = pci_get_drvdata(pdev);
|
||||||
pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
|
pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (pci_enable_device(pdev)) {
|
if (pci_enable_device(pdev)) {
|
||||||
netif_err(efx, hw, efx->net_dev,
|
netif_err(efx, hw, efx->net_dev,
|
||||||
@@ -3194,13 +3193,6 @@ static pci_ers_result_t ef4_io_slot_reset(struct pci_dev *pdev)
|
|||||||
status = PCI_ERS_RESULT_DISCONNECT;
|
status = PCI_ERS_RESULT_DISCONNECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
if (rc) {
|
|
||||||
netif_err(efx, hw, efx->net_dev,
|
|
||||||
"pci_cleanup_aer_uncorrect_error_status failed (%d)\n", rc);
|
|
||||||
/* Non-fatal error. Continue. */
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2649,7 +2649,6 @@ static void nvme_error_resume(struct pci_dev *pdev)
|
|||||||
struct nvme_dev *dev = pci_get_drvdata(pdev);
|
struct nvme_dev *dev = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
flush_work(&dev->ctrl.reset_work);
|
flush_work(&dev->ctrl.reset_work);
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct pci_error_handlers nvme_err_handler = {
|
static const struct pci_error_handlers nvme_err_handler = {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ DEFINE_RAW_SPINLOCK(pci_lock);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PCI_OP_READ(size, type, len) \
|
#define PCI_OP_READ(size, type, len) \
|
||||||
int pci_bus_read_config_##size \
|
int noinline pci_bus_read_config_##size \
|
||||||
(struct pci_bus *bus, unsigned int devfn, int pos, type *value) \
|
(struct pci_bus *bus, unsigned int devfn, int pos, type *value) \
|
||||||
{ \
|
{ \
|
||||||
int res; \
|
int res; \
|
||||||
@@ -48,7 +48,7 @@ int pci_bus_read_config_##size \
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define PCI_OP_WRITE(size, type, len) \
|
#define PCI_OP_WRITE(size, type, len) \
|
||||||
int pci_bus_write_config_##size \
|
int noinline pci_bus_write_config_##size \
|
||||||
(struct pci_bus *bus, unsigned int devfn, int pos, type value) \
|
(struct pci_bus *bus, unsigned int devfn, int pos, type value) \
|
||||||
{ \
|
{ \
|
||||||
int res; \
|
int res; \
|
||||||
|
|||||||
74
drivers/pci/hotplug/TODO
Normal file
74
drivers/pci/hotplug/TODO
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
Contributions are solicited in particular to remedy the following issues:
|
||||||
|
|
||||||
|
cpcihp:
|
||||||
|
|
||||||
|
* There are no implementations of the ->hardware_test, ->get_power and
|
||||||
|
->set_power callbacks in struct cpci_hp_controller_ops. Why were they
|
||||||
|
introduced? Can they be removed from the struct?
|
||||||
|
|
||||||
|
cpqphp:
|
||||||
|
|
||||||
|
* The driver spawns a kthread cpqhp_event_thread() which is woken by the
|
||||||
|
hardirq handler cpqhp_ctrl_intr(). Convert this to threaded IRQ handling.
|
||||||
|
The kthread is also woken from the timer pushbutton_helper_thread(),
|
||||||
|
convert it to call irq_wake_thread(). Use pciehp as a template.
|
||||||
|
|
||||||
|
* A large portion of cpqphp_ctrl.c and cpqphp_pci.c concerns resource
|
||||||
|
management. Doesn't this duplicate functionality in the core?
|
||||||
|
|
||||||
|
ibmphp:
|
||||||
|
|
||||||
|
* Implementations of hotplug_slot_ops callbacks such as get_adapter_present()
|
||||||
|
in ibmphp_core.c create a copy of the struct slot on the stack, then perform
|
||||||
|
the actual operation on that copy. Determine if this overhead is necessary,
|
||||||
|
delete it if not. The functions also perform a NULL pointer check on the
|
||||||
|
struct hotplug_slot, this seems superfluous.
|
||||||
|
|
||||||
|
* Several functions access the pci_slot member in struct hotplug_slot even
|
||||||
|
though pci_hotplug.h declares it private. See get_max_bus_speed() for an
|
||||||
|
example. Either the pci_slot member should no longer be declared private
|
||||||
|
or ibmphp should store a pointer to its bus in struct slot. Probably the
|
||||||
|
former.
|
||||||
|
|
||||||
|
* The functions get_max_adapter_speed() and get_bus_name() are commented out.
|
||||||
|
Can they be deleted? There are also forward declarations at the top of
|
||||||
|
ibmphp_core.c as well as pointers in ibmphp_hotplug_slot_ops, likewise
|
||||||
|
commented out.
|
||||||
|
|
||||||
|
* ibmphp_init_devno() takes a struct slot **, it could instead take a
|
||||||
|
struct slot *.
|
||||||
|
|
||||||
|
* The return value of pci_hp_register() is not checked.
|
||||||
|
|
||||||
|
* iounmap(io_mem) is called in the error path of ebda_rsrc_controller()
|
||||||
|
and once more in the error path of its caller ibmphp_access_ebda().
|
||||||
|
|
||||||
|
* The various slot data structures are difficult to follow and need to be
|
||||||
|
simplified. A lot of functions are too large and too complex, they need
|
||||||
|
to be broken up into smaller, manageable pieces. Negative examples are
|
||||||
|
ebda_rsrc_controller() and configure_bridge().
|
||||||
|
|
||||||
|
* A large portion of ibmphp_res.c and ibmphp_pci.c concerns resource
|
||||||
|
management. Doesn't this duplicate functionality in the core?
|
||||||
|
|
||||||
|
sgi_hotplug:
|
||||||
|
|
||||||
|
* Several functions access the pci_slot member in struct hotplug_slot even
|
||||||
|
though pci_hotplug.h declares it private. See sn_hp_destroy() for an
|
||||||
|
example. Either the pci_slot member should no longer be declared private
|
||||||
|
or sgi_hotplug should store a pointer to it in struct slot. Probably the
|
||||||
|
former.
|
||||||
|
|
||||||
|
shpchp:
|
||||||
|
|
||||||
|
* There is only a single implementation of struct hpc_ops. Can the struct be
|
||||||
|
removed and its functions invoked directly? This has already been done in
|
||||||
|
pciehp with commit 82a9e79ef132 ("PCI: pciehp: remove hpc_ops"). Clarify
|
||||||
|
if there was a specific reason not to apply the same change to shpchp.
|
||||||
|
|
||||||
|
* The ->get_mode1_ECC_cap callback in shpchp_hpc_ops is never invoked.
|
||||||
|
Why was it introduced? Can it be removed?
|
||||||
|
|
||||||
|
* The hardirq handler shpc_isr() queues events on a workqueue. It can be
|
||||||
|
simplified by converting it to threaded IRQ handling. Use pciehp as a
|
||||||
|
template.
|
||||||
@@ -33,15 +33,19 @@ struct acpiphp_slot;
|
|||||||
* struct slot - slot information for each *physical* slot
|
* struct slot - slot information for each *physical* slot
|
||||||
*/
|
*/
|
||||||
struct slot {
|
struct slot {
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
struct acpiphp_slot *acpi_slot;
|
struct acpiphp_slot *acpi_slot;
|
||||||
struct hotplug_slot_info info;
|
|
||||||
unsigned int sun; /* ACPI _SUN (Slot User Number) value */
|
unsigned int sun; /* ACPI _SUN (Slot User Number) value */
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline const char *slot_name(struct slot *slot)
|
static inline const char *slot_name(struct slot *slot)
|
||||||
{
|
{
|
||||||
return hotplug_slot_name(slot->hotplug_slot);
|
return hotplug_slot_name(&slot->hotplug_slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
|
||||||
|
{
|
||||||
|
return container_of(hotplug_slot, struct slot, hotplug_slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ 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_latch_status(struct hotplug_slot *slot, u8 *value);
|
||||||
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
|
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
|
||||||
|
|
||||||
static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
|
static const struct hotplug_slot_ops acpi_hotplug_slot_ops = {
|
||||||
.enable_slot = enable_slot,
|
.enable_slot = enable_slot,
|
||||||
.disable_slot = disable_slot,
|
.disable_slot = disable_slot,
|
||||||
.set_attention_status = set_attention_status,
|
.set_attention_status = set_attention_status,
|
||||||
@@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
|
|||||||
*/
|
*/
|
||||||
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
|
|||||||
*/
|
*/
|
||||||
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
|||||||
*/
|
*/
|
||||||
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
*/
|
*/
|
||||||
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
*/
|
*/
|
||||||
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
|
|
||||||
@@ -266,39 +266,26 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
|
|||||||
if (!slot)
|
if (!slot)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
slot->hotplug_slot = kzalloc(sizeof(*slot->hotplug_slot), GFP_KERNEL);
|
slot->hotplug_slot.ops = &acpi_hotplug_slot_ops;
|
||||||
if (!slot->hotplug_slot)
|
|
||||||
goto error_slot;
|
|
||||||
|
|
||||||
slot->hotplug_slot->info = &slot->info;
|
|
||||||
|
|
||||||
slot->hotplug_slot->private = slot;
|
|
||||||
slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
|
|
||||||
|
|
||||||
slot->acpi_slot = acpiphp_slot;
|
slot->acpi_slot = acpiphp_slot;
|
||||||
slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
|
|
||||||
slot->hotplug_slot->info->attention_status = 0;
|
|
||||||
slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
|
|
||||||
slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
|
|
||||||
|
|
||||||
acpiphp_slot->slot = slot;
|
acpiphp_slot->slot = slot;
|
||||||
slot->sun = sun;
|
slot->sun = sun;
|
||||||
snprintf(name, SLOT_NAME_SIZE, "%u", sun);
|
snprintf(name, SLOT_NAME_SIZE, "%u", sun);
|
||||||
|
|
||||||
retval = pci_hp_register(slot->hotplug_slot, acpiphp_slot->bus,
|
retval = pci_hp_register(&slot->hotplug_slot, acpiphp_slot->bus,
|
||||||
acpiphp_slot->device, name);
|
acpiphp_slot->device, name);
|
||||||
if (retval == -EBUSY)
|
if (retval == -EBUSY)
|
||||||
goto error_hpslot;
|
goto error_slot;
|
||||||
if (retval) {
|
if (retval) {
|
||||||
pr_err("pci_hp_register failed with error %d\n", retval);
|
pr_err("pci_hp_register failed with error %d\n", retval);
|
||||||
goto error_hpslot;
|
goto error_slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("Slot [%s] registered\n", slot_name(slot));
|
pr_info("Slot [%s] registered\n", slot_name(slot));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
error_hpslot:
|
|
||||||
kfree(slot->hotplug_slot);
|
|
||||||
error_slot:
|
error_slot:
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
error:
|
error:
|
||||||
@@ -312,8 +299,7 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
|
|||||||
|
|
||||||
pr_info("Slot [%s] unregistered\n", slot_name(slot));
|
pr_info("Slot [%s] unregistered\n", slot_name(slot));
|
||||||
|
|
||||||
pci_hp_deregister(slot->hotplug_slot);
|
pci_hp_deregister(&slot->hotplug_slot);
|
||||||
kfree(slot->hotplug_slot);
|
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ MODULE_VERSION(DRIVER_VERSION);
|
|||||||
#define IBM_HARDWARE_ID1 "IBM37D0"
|
#define IBM_HARDWARE_ID1 "IBM37D0"
|
||||||
#define IBM_HARDWARE_ID2 "IBM37D4"
|
#define IBM_HARDWARE_ID2 "IBM37D4"
|
||||||
|
|
||||||
#define hpslot_to_sun(A) (((struct slot *)((A)->private))->sun)
|
#define hpslot_to_sun(A) (to_slot(A)->sun)
|
||||||
|
|
||||||
/* union apci_descriptor - allows access to the
|
/* union apci_descriptor - allows access to the
|
||||||
* various device descriptors that are embedded in the
|
* various device descriptors that are embedded in the
|
||||||
|
|||||||
@@ -32,8 +32,10 @@ struct slot {
|
|||||||
unsigned int devfn;
|
unsigned int devfn;
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus;
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
|
unsigned int latch_status:1;
|
||||||
|
unsigned int adapter_status:1;
|
||||||
unsigned int extracting;
|
unsigned int extracting;
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
struct list_head slot_list;
|
struct list_head slot_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -58,7 +60,12 @@ struct cpci_hp_controller {
|
|||||||
|
|
||||||
static inline const char *slot_name(struct slot *slot)
|
static inline const char *slot_name(struct slot *slot)
|
||||||
{
|
{
|
||||||
return hotplug_slot_name(slot->hotplug_slot);
|
return hotplug_slot_name(&slot->hotplug_slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
|
||||||
|
{
|
||||||
|
return container_of(hotplug_slot, struct slot, hotplug_slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cpci_hp_register_controller(struct cpci_hp_controller *controller);
|
int cpci_hp_register_controller(struct cpci_hp_controller *controller);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ static int get_attention_status(struct hotplug_slot *slot, u8 *value);
|
|||||||
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
|
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
|
||||||
static int get_latch_status(struct hotplug_slot *slot, u8 *value);
|
static int get_latch_status(struct hotplug_slot *slot, u8 *value);
|
||||||
|
|
||||||
static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
|
static const struct hotplug_slot_ops cpci_hotplug_slot_ops = {
|
||||||
.enable_slot = enable_slot,
|
.enable_slot = enable_slot,
|
||||||
.disable_slot = disable_slot,
|
.disable_slot = disable_slot,
|
||||||
.set_attention_status = set_attention_status,
|
.set_attention_status = set_attention_status,
|
||||||
@@ -67,30 +67,10 @@ static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
|
|||||||
.get_latch_status = get_latch_status,
|
.get_latch_status = get_latch_status,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
|
||||||
update_latch_status(struct hotplug_slot *hotplug_slot, u8 value)
|
|
||||||
{
|
|
||||||
struct hotplug_slot_info info;
|
|
||||||
|
|
||||||
memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
|
|
||||||
info.latch_status = value;
|
|
||||||
return pci_hp_change_slot_info(hotplug_slot, &info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value)
|
|
||||||
{
|
|
||||||
struct hotplug_slot_info info;
|
|
||||||
|
|
||||||
memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
|
|
||||||
info.adapter_status = value;
|
|
||||||
return pci_hp_change_slot_info(hotplug_slot, &info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
enable_slot(struct hotplug_slot *hotplug_slot)
|
enable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
dbg("%s - physical_slot = %s", __func__, slot_name(slot));
|
dbg("%s - physical_slot = %s", __func__, slot_name(slot));
|
||||||
@@ -103,7 +83,7 @@ enable_slot(struct hotplug_slot *hotplug_slot)
|
|||||||
static int
|
static int
|
||||||
disable_slot(struct hotplug_slot *hotplug_slot)
|
disable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
dbg("%s - physical_slot = %s", __func__, slot_name(slot));
|
dbg("%s - physical_slot = %s", __func__, slot_name(slot));
|
||||||
@@ -135,8 +115,7 @@ disable_slot(struct hotplug_slot *hotplug_slot)
|
|||||||
goto disable_error;
|
goto disable_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update_adapter_status(slot->hotplug_slot, 0))
|
slot->adapter_status = 0;
|
||||||
warn("failure to update adapter file");
|
|
||||||
|
|
||||||
if (slot->extracting) {
|
if (slot->extracting) {
|
||||||
slot->extracting = 0;
|
slot->extracting = 0;
|
||||||
@@ -160,7 +139,7 @@ cpci_get_power_status(struct slot *slot)
|
|||||||
static int
|
static int
|
||||||
get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
*value = cpci_get_power_status(slot);
|
*value = cpci_get_power_status(slot);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -169,7 +148,7 @@ get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
static int
|
static int
|
||||||
get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
*value = cpci_get_attention_status(slot);
|
*value = cpci_get_attention_status(slot);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -178,27 +157,29 @@ get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
static int
|
static int
|
||||||
set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||||
{
|
{
|
||||||
return cpci_set_attention_status(hotplug_slot->private, status);
|
return cpci_set_attention_status(to_slot(hotplug_slot), status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
*value = hotplug_slot->info->adapter_status;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
|
*value = slot->adapter_status;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
*value = hotplug_slot->info->latch_status;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
|
*value = slot->latch_status;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void release_slot(struct slot *slot)
|
static void release_slot(struct slot *slot)
|
||||||
{
|
{
|
||||||
kfree(slot->hotplug_slot->info);
|
|
||||||
kfree(slot->hotplug_slot);
|
|
||||||
pci_dev_put(slot->dev);
|
pci_dev_put(slot->dev);
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
}
|
}
|
||||||
@@ -209,8 +190,6 @@ int
|
|||||||
cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
|
cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
|
||||||
{
|
{
|
||||||
struct slot *slot;
|
struct slot *slot;
|
||||||
struct hotplug_slot *hotplug_slot;
|
|
||||||
struct hotplug_slot_info *info;
|
|
||||||
char name[SLOT_NAME_SIZE];
|
char name[SLOT_NAME_SIZE];
|
||||||
int status;
|
int status;
|
||||||
int i;
|
int i;
|
||||||
@@ -229,43 +208,19 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
hotplug_slot =
|
|
||||||
kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
|
|
||||||
if (!hotplug_slot) {
|
|
||||||
status = -ENOMEM;
|
|
||||||
goto error_slot;
|
|
||||||
}
|
|
||||||
slot->hotplug_slot = hotplug_slot;
|
|
||||||
|
|
||||||
info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
|
|
||||||
if (!info) {
|
|
||||||
status = -ENOMEM;
|
|
||||||
goto error_hpslot;
|
|
||||||
}
|
|
||||||
hotplug_slot->info = info;
|
|
||||||
|
|
||||||
slot->bus = bus;
|
slot->bus = bus;
|
||||||
slot->number = i;
|
slot->number = i;
|
||||||
slot->devfn = PCI_DEVFN(i, 0);
|
slot->devfn = PCI_DEVFN(i, 0);
|
||||||
|
|
||||||
snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
|
snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
|
||||||
|
|
||||||
hotplug_slot->private = slot;
|
slot->hotplug_slot.ops = &cpci_hotplug_slot_ops;
|
||||||
hotplug_slot->ops = &cpci_hotplug_slot_ops;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the slot info structure with some known
|
|
||||||
* good values.
|
|
||||||
*/
|
|
||||||
dbg("initializing slot %s", name);
|
|
||||||
info->power_status = cpci_get_power_status(slot);
|
|
||||||
info->attention_status = cpci_get_attention_status(slot);
|
|
||||||
|
|
||||||
dbg("registering slot %s", name);
|
dbg("registering slot %s", name);
|
||||||
status = pci_hp_register(slot->hotplug_slot, bus, i, name);
|
status = pci_hp_register(&slot->hotplug_slot, bus, i, name);
|
||||||
if (status) {
|
if (status) {
|
||||||
err("pci_hp_register failed with error %d", status);
|
err("pci_hp_register failed with error %d", status);
|
||||||
goto error_info;
|
goto error_slot;
|
||||||
}
|
}
|
||||||
dbg("slot registered with name: %s", slot_name(slot));
|
dbg("slot registered with name: %s", slot_name(slot));
|
||||||
|
|
||||||
@@ -276,10 +231,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
|
|||||||
up_write(&list_rwsem);
|
up_write(&list_rwsem);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
error_info:
|
|
||||||
kfree(info);
|
|
||||||
error_hpslot:
|
|
||||||
kfree(hotplug_slot);
|
|
||||||
error_slot:
|
error_slot:
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
error:
|
error:
|
||||||
@@ -305,7 +256,7 @@ cpci_hp_unregister_bus(struct pci_bus *bus)
|
|||||||
slots--;
|
slots--;
|
||||||
|
|
||||||
dbg("deregistering slot %s", slot_name(slot));
|
dbg("deregistering slot %s", slot_name(slot));
|
||||||
pci_hp_deregister(slot->hotplug_slot);
|
pci_hp_deregister(&slot->hotplug_slot);
|
||||||
release_slot(slot);
|
release_slot(slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,10 +310,8 @@ init_slots(int clear_ins)
|
|||||||
__func__, slot_name(slot));
|
__func__, slot_name(slot));
|
||||||
dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
|
dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
|
||||||
if (dev) {
|
if (dev) {
|
||||||
if (update_adapter_status(slot->hotplug_slot, 1))
|
slot->adapter_status = 1;
|
||||||
warn("failure to update adapter file");
|
slot->latch_status = 1;
|
||||||
if (update_latch_status(slot->hotplug_slot, 1))
|
|
||||||
warn("failure to update latch file");
|
|
||||||
slot->dev = dev;
|
slot->dev = dev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,11 +373,8 @@ check_slots(void)
|
|||||||
dbg("%s - slot %s HS_CSR (2) = %04x",
|
dbg("%s - slot %s HS_CSR (2) = %04x",
|
||||||
__func__, slot_name(slot), hs_csr);
|
__func__, slot_name(slot), hs_csr);
|
||||||
|
|
||||||
if (update_latch_status(slot->hotplug_slot, 1))
|
slot->latch_status = 1;
|
||||||
warn("failure to update latch file");
|
slot->adapter_status = 1;
|
||||||
|
|
||||||
if (update_adapter_status(slot->hotplug_slot, 1))
|
|
||||||
warn("failure to update adapter file");
|
|
||||||
|
|
||||||
cpci_led_off(slot);
|
cpci_led_off(slot);
|
||||||
|
|
||||||
@@ -449,9 +395,7 @@ check_slots(void)
|
|||||||
__func__, slot_name(slot), hs_csr);
|
__func__, slot_name(slot), hs_csr);
|
||||||
|
|
||||||
if (!slot->extracting) {
|
if (!slot->extracting) {
|
||||||
if (update_latch_status(slot->hotplug_slot, 0))
|
slot->latch_status = 0;
|
||||||
warn("failure to update latch file");
|
|
||||||
|
|
||||||
slot->extracting = 1;
|
slot->extracting = 1;
|
||||||
atomic_inc(&extracting);
|
atomic_inc(&extracting);
|
||||||
}
|
}
|
||||||
@@ -465,8 +409,7 @@ check_slots(void)
|
|||||||
*/
|
*/
|
||||||
err("card in slot %s was improperly removed",
|
err("card in slot %s was improperly removed",
|
||||||
slot_name(slot));
|
slot_name(slot));
|
||||||
if (update_adapter_status(slot->hotplug_slot, 0))
|
slot->adapter_status = 0;
|
||||||
warn("failure to update adapter file");
|
|
||||||
slot->extracting = 0;
|
slot->extracting = 0;
|
||||||
atomic_dec(&extracting);
|
atomic_dec(&extracting);
|
||||||
}
|
}
|
||||||
@@ -615,7 +558,7 @@ cleanup_slots(void)
|
|||||||
goto cleanup_null;
|
goto cleanup_null;
|
||||||
list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
|
list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
|
||||||
list_del(&slot->slot_list);
|
list_del(&slot->slot_list);
|
||||||
pci_hp_deregister(slot->hotplug_slot);
|
pci_hp_deregister(&slot->hotplug_slot);
|
||||||
release_slot(slot);
|
release_slot(slot);
|
||||||
}
|
}
|
||||||
cleanup_null:
|
cleanup_null:
|
||||||
|
|||||||
@@ -194,8 +194,7 @@ int cpci_led_on(struct slot *slot)
|
|||||||
slot->devfn,
|
slot->devfn,
|
||||||
hs_cap + 2,
|
hs_cap + 2,
|
||||||
hs_csr)) {
|
hs_csr)) {
|
||||||
err("Could not set LOO for slot %s",
|
err("Could not set LOO for slot %s", slot_name(slot));
|
||||||
hotplug_slot_name(slot->hotplug_slot));
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,8 +222,7 @@ int cpci_led_off(struct slot *slot)
|
|||||||
slot->devfn,
|
slot->devfn,
|
||||||
hs_cap + 2,
|
hs_cap + 2,
|
||||||
hs_csr)) {
|
hs_csr)) {
|
||||||
err("Could not clear LOO for slot %s",
|
err("Could not clear LOO for slot %s", slot_name(slot));
|
||||||
hotplug_slot_name(slot->hotplug_slot));
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ struct slot {
|
|||||||
u8 hp_slot;
|
u8 hp_slot;
|
||||||
struct controller *ctrl;
|
struct controller *ctrl;
|
||||||
void __iomem *p_sm_slot;
|
void __iomem *p_sm_slot;
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pci_resource {
|
struct pci_resource {
|
||||||
@@ -445,7 +445,12 @@ extern u8 cpqhp_disk_irq;
|
|||||||
|
|
||||||
static inline const char *slot_name(struct slot *slot)
|
static inline const char *slot_name(struct slot *slot)
|
||||||
{
|
{
|
||||||
return hotplug_slot_name(slot->hotplug_slot);
|
return hotplug_slot_name(&slot->hotplug_slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
|
||||||
|
{
|
||||||
|
return container_of(hotplug_slot, struct slot, hotplug_slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ static int init_SERR(struct controller *ctrl)
|
|||||||
{
|
{
|
||||||
u32 tempdword;
|
u32 tempdword;
|
||||||
u32 number_of_slots;
|
u32 number_of_slots;
|
||||||
u8 physical_slot;
|
|
||||||
|
|
||||||
if (!ctrl)
|
if (!ctrl)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -131,7 +130,6 @@ static int init_SERR(struct controller *ctrl)
|
|||||||
number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
|
number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
|
||||||
/* Loop through slots */
|
/* Loop through slots */
|
||||||
while (number_of_slots) {
|
while (number_of_slots) {
|
||||||
physical_slot = tempdword;
|
|
||||||
writeb(0, ctrl->hpc_reg + SLOT_SERR);
|
writeb(0, ctrl->hpc_reg + SLOT_SERR);
|
||||||
tempdword++;
|
tempdword++;
|
||||||
number_of_slots--;
|
number_of_slots--;
|
||||||
@@ -275,9 +273,7 @@ static int ctrl_slot_cleanup(struct controller *ctrl)
|
|||||||
|
|
||||||
while (old_slot) {
|
while (old_slot) {
|
||||||
next_slot = old_slot->next;
|
next_slot = old_slot->next;
|
||||||
pci_hp_deregister(old_slot->hotplug_slot);
|
pci_hp_deregister(&old_slot->hotplug_slot);
|
||||||
kfree(old_slot->hotplug_slot->info);
|
|
||||||
kfree(old_slot->hotplug_slot);
|
|
||||||
kfree(old_slot);
|
kfree(old_slot);
|
||||||
old_slot = next_slot;
|
old_slot = next_slot;
|
||||||
}
|
}
|
||||||
@@ -419,7 +415,7 @@ cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,
|
|||||||
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||||
{
|
{
|
||||||
struct pci_func *slot_func;
|
struct pci_func *slot_func;
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
u8 bus;
|
u8 bus;
|
||||||
u8 devfn;
|
u8 devfn;
|
||||||
@@ -446,7 +442,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
|||||||
static int process_SI(struct hotplug_slot *hotplug_slot)
|
static int process_SI(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct pci_func *slot_func;
|
struct pci_func *slot_func;
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
u8 bus;
|
u8 bus;
|
||||||
u8 devfn;
|
u8 devfn;
|
||||||
@@ -478,7 +474,7 @@ static int process_SI(struct hotplug_slot *hotplug_slot)
|
|||||||
static int process_SS(struct hotplug_slot *hotplug_slot)
|
static int process_SS(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct pci_func *slot_func;
|
struct pci_func *slot_func;
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
u8 bus;
|
u8 bus;
|
||||||
u8 devfn;
|
u8 devfn;
|
||||||
@@ -505,7 +501,7 @@ static int process_SS(struct hotplug_slot *hotplug_slot)
|
|||||||
|
|
||||||
static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
|
static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
|
|
||||||
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
@@ -516,7 +512,7 @@ static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
|
|||||||
|
|
||||||
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
|
|
||||||
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
@@ -527,7 +523,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
|
|
||||||
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
|
|
||||||
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
@@ -538,7 +534,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
|
|
||||||
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
|
|
||||||
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
@@ -550,7 +546,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
|
|
||||||
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
|
|
||||||
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
||||||
@@ -560,7 +556,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
|
static const struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
|
||||||
.set_attention_status = set_attention_status,
|
.set_attention_status = set_attention_status,
|
||||||
.enable_slot = process_SI,
|
.enable_slot = process_SI,
|
||||||
.disable_slot = process_SS,
|
.disable_slot = process_SS,
|
||||||
@@ -578,8 +574,6 @@ static int ctrl_slot_setup(struct controller *ctrl,
|
|||||||
void __iomem *smbios_table)
|
void __iomem *smbios_table)
|
||||||
{
|
{
|
||||||
struct slot *slot;
|
struct slot *slot;
|
||||||
struct hotplug_slot *hotplug_slot;
|
|
||||||
struct hotplug_slot_info *hotplug_slot_info;
|
|
||||||
struct pci_bus *bus = ctrl->pci_bus;
|
struct pci_bus *bus = ctrl->pci_bus;
|
||||||
u8 number_of_slots;
|
u8 number_of_slots;
|
||||||
u8 slot_device;
|
u8 slot_device;
|
||||||
@@ -605,22 +599,6 @@ static int ctrl_slot_setup(struct controller *ctrl,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
slot->hotplug_slot = kzalloc(sizeof(*(slot->hotplug_slot)),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!slot->hotplug_slot) {
|
|
||||||
result = -ENOMEM;
|
|
||||||
goto error_slot;
|
|
||||||
}
|
|
||||||
hotplug_slot = slot->hotplug_slot;
|
|
||||||
|
|
||||||
hotplug_slot->info = kzalloc(sizeof(*(hotplug_slot->info)),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!hotplug_slot->info) {
|
|
||||||
result = -ENOMEM;
|
|
||||||
goto error_hpslot;
|
|
||||||
}
|
|
||||||
hotplug_slot_info = hotplug_slot->info;
|
|
||||||
|
|
||||||
slot->ctrl = ctrl;
|
slot->ctrl = ctrl;
|
||||||
slot->bus = ctrl->bus;
|
slot->bus = ctrl->bus;
|
||||||
slot->device = slot_device;
|
slot->device = slot_device;
|
||||||
@@ -669,29 +647,20 @@ static int ctrl_slot_setup(struct controller *ctrl,
|
|||||||
((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04;
|
((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04;
|
||||||
|
|
||||||
/* register this slot with the hotplug pci core */
|
/* register this slot with the hotplug pci core */
|
||||||
hotplug_slot->private = slot;
|
|
||||||
snprintf(name, SLOT_NAME_SIZE, "%u", slot->number);
|
snprintf(name, SLOT_NAME_SIZE, "%u", slot->number);
|
||||||
hotplug_slot->ops = &cpqphp_hotplug_slot_ops;
|
slot->hotplug_slot.ops = &cpqphp_hotplug_slot_ops;
|
||||||
|
|
||||||
hotplug_slot_info->power_status = get_slot_enabled(ctrl, slot);
|
|
||||||
hotplug_slot_info->attention_status =
|
|
||||||
cpq_get_attention_status(ctrl, slot);
|
|
||||||
hotplug_slot_info->latch_status =
|
|
||||||
cpq_get_latch_status(ctrl, slot);
|
|
||||||
hotplug_slot_info->adapter_status =
|
|
||||||
get_presence_status(ctrl, slot);
|
|
||||||
|
|
||||||
dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n",
|
dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n",
|
||||||
slot->bus, slot->device,
|
slot->bus, slot->device,
|
||||||
slot->number, ctrl->slot_device_offset,
|
slot->number, ctrl->slot_device_offset,
|
||||||
slot_number);
|
slot_number);
|
||||||
result = pci_hp_register(hotplug_slot,
|
result = pci_hp_register(&slot->hotplug_slot,
|
||||||
ctrl->pci_dev->bus,
|
ctrl->pci_dev->bus,
|
||||||
slot->device,
|
slot->device,
|
||||||
name);
|
name);
|
||||||
if (result) {
|
if (result) {
|
||||||
err("pci_hp_register failed with error %d\n", result);
|
err("pci_hp_register failed with error %d\n", result);
|
||||||
goto error_info;
|
goto error_slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
slot->next = ctrl->slot;
|
slot->next = ctrl->slot;
|
||||||
@@ -703,10 +672,6 @@ static int ctrl_slot_setup(struct controller *ctrl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
error_info:
|
|
||||||
kfree(hotplug_slot_info);
|
|
||||||
error_hpslot:
|
|
||||||
kfree(hotplug_slot);
|
|
||||||
error_slot:
|
error_slot:
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
error:
|
error:
|
||||||
|
|||||||
@@ -1130,9 +1130,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
|
|||||||
for (slot = ctrl->slot; slot; slot = slot->next) {
|
for (slot = ctrl->slot; slot; slot = slot->next) {
|
||||||
if (slot->device == (hp_slot + ctrl->slot_device_offset))
|
if (slot->device == (hp_slot + ctrl->slot_device_offset))
|
||||||
continue;
|
continue;
|
||||||
if (!slot->hotplug_slot || !slot->hotplug_slot->info)
|
if (get_presence_status(ctrl, slot) == 0)
|
||||||
continue;
|
|
||||||
if (slot->hotplug_slot->info->adapter_status == 0)
|
|
||||||
continue;
|
continue;
|
||||||
/* If another adapter is running on the same segment but at a
|
/* If another adapter is running on the same segment but at a
|
||||||
* lower speed/mode, we allow the new adapter to function at
|
* lower speed/mode, we allow the new adapter to function at
|
||||||
@@ -1767,24 +1765,6 @@ void cpqhp_event_stop_thread(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int update_slot_info(struct controller *ctrl, struct slot *slot)
|
|
||||||
{
|
|
||||||
struct hotplug_slot_info *info;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
|
||||||
if (!info)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
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(slot->hotplug_slot, info);
|
|
||||||
kfree(info);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void interrupt_event_handler(struct controller *ctrl)
|
static void interrupt_event_handler(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
int loop = 0;
|
int loop = 0;
|
||||||
@@ -1884,9 +1864,6 @@ static void interrupt_event_handler(struct controller *ctrl)
|
|||||||
/***********POWER FAULT */
|
/***********POWER FAULT */
|
||||||
else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
|
else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
|
||||||
dbg("power fault\n");
|
dbg("power fault\n");
|
||||||
} else {
|
|
||||||
/* refresh notification */
|
|
||||||
update_slot_info(ctrl, p_slot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl->event_queue[loop].event_type = 0;
|
ctrl->event_queue[loop].event_type = 0;
|
||||||
@@ -2057,9 +2034,6 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
|
|||||||
if (rc)
|
if (rc)
|
||||||
dbg("%s: rc = %d\n", __func__, rc);
|
dbg("%s: rc = %d\n", __func__, rc);
|
||||||
|
|
||||||
if (p_slot)
|
|
||||||
update_slot_info(ctrl, p_slot);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2125,9 +2099,6 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
|
|||||||
rc = 1;
|
rc = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_slot)
|
|
||||||
update_slot_info(ctrl, p_slot);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -698,7 +698,7 @@ struct slot {
|
|||||||
u8 supported_bus_mode;
|
u8 supported_bus_mode;
|
||||||
u8 flag; /* this is for disable slot and polling */
|
u8 flag; /* this is for disable slot and polling */
|
||||||
u8 ctlr_index;
|
u8 ctlr_index;
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
struct controller *ctrl;
|
struct controller *ctrl;
|
||||||
struct pci_func *func;
|
struct pci_func *func;
|
||||||
u8 irq[4];
|
u8 irq[4];
|
||||||
@@ -740,7 +740,12 @@ int ibmphp_do_disable_slot(struct slot *slot_cur);
|
|||||||
int ibmphp_update_slot_info(struct slot *); /* This function is called from HPC, so we need it to not be be static */
|
int ibmphp_update_slot_info(struct slot *); /* This function is called from HPC, so we need it to not be be static */
|
||||||
int ibmphp_configure_card(struct pci_func *, u8);
|
int ibmphp_configure_card(struct pci_func *, u8);
|
||||||
int ibmphp_unconfigure_card(struct slot **, int);
|
int ibmphp_unconfigure_card(struct slot **, int);
|
||||||
extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
|
extern const struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
|
||||||
|
|
||||||
|
static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
|
||||||
|
{
|
||||||
|
return container_of(hotplug_slot, struct slot, hotplug_slot);
|
||||||
|
}
|
||||||
|
|
||||||
#endif //__IBMPHP_H
|
#endif //__IBMPHP_H
|
||||||
|
|
||||||
|
|||||||
@@ -247,11 +247,8 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
pslot = hotplug_slot->private;
|
pslot = to_slot(hotplug_slot);
|
||||||
if (pslot)
|
rc = ibmphp_hpc_writeslot(pslot, cmd);
|
||||||
rc = ibmphp_hpc_writeslot(pslot, cmd);
|
|
||||||
else
|
|
||||||
rc = -ENODEV;
|
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
@@ -273,19 +270,15 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
|
|
||||||
ibmphp_lock_operations();
|
ibmphp_lock_operations();
|
||||||
if (hotplug_slot) {
|
if (hotplug_slot) {
|
||||||
pslot = hotplug_slot->private;
|
pslot = to_slot(hotplug_slot);
|
||||||
if (pslot) {
|
memcpy(&myslot, pslot, sizeof(struct slot));
|
||||||
memcpy(&myslot, pslot, sizeof(struct slot));
|
rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
|
||||||
rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
|
&myslot.status);
|
||||||
&(myslot.status));
|
if (!rc)
|
||||||
if (!rc)
|
rc = ibmphp_hpc_readslot(pslot, READ_EXTSLOTSTATUS,
|
||||||
rc = ibmphp_hpc_readslot(pslot,
|
&myslot.ext_status);
|
||||||
READ_EXTSLOTSTATUS,
|
if (!rc)
|
||||||
&(myslot.ext_status));
|
*value = SLOT_ATTN(myslot.status, myslot.ext_status);
|
||||||
if (!rc)
|
|
||||||
*value = SLOT_ATTN(myslot.status,
|
|
||||||
myslot.ext_status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ibmphp_unlock_operations();
|
ibmphp_unlock_operations();
|
||||||
@@ -303,14 +296,12 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
(ulong) hotplug_slot, (ulong) value);
|
(ulong) hotplug_slot, (ulong) value);
|
||||||
ibmphp_lock_operations();
|
ibmphp_lock_operations();
|
||||||
if (hotplug_slot) {
|
if (hotplug_slot) {
|
||||||
pslot = hotplug_slot->private;
|
pslot = to_slot(hotplug_slot);
|
||||||
if (pslot) {
|
memcpy(&myslot, pslot, sizeof(struct slot));
|
||||||
memcpy(&myslot, pslot, sizeof(struct slot));
|
rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
|
||||||
rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
|
&myslot.status);
|
||||||
&(myslot.status));
|
if (!rc)
|
||||||
if (!rc)
|
*value = SLOT_LATCH(myslot.status);
|
||||||
*value = SLOT_LATCH(myslot.status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ibmphp_unlock_operations();
|
ibmphp_unlock_operations();
|
||||||
@@ -330,14 +321,12 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
(ulong) hotplug_slot, (ulong) value);
|
(ulong) hotplug_slot, (ulong) value);
|
||||||
ibmphp_lock_operations();
|
ibmphp_lock_operations();
|
||||||
if (hotplug_slot) {
|
if (hotplug_slot) {
|
||||||
pslot = hotplug_slot->private;
|
pslot = to_slot(hotplug_slot);
|
||||||
if (pslot) {
|
memcpy(&myslot, pslot, sizeof(struct slot));
|
||||||
memcpy(&myslot, pslot, sizeof(struct slot));
|
rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
|
||||||
rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
|
&myslot.status);
|
||||||
&(myslot.status));
|
if (!rc)
|
||||||
if (!rc)
|
*value = SLOT_PWRGD(myslot.status);
|
||||||
*value = SLOT_PWRGD(myslot.status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ibmphp_unlock_operations();
|
ibmphp_unlock_operations();
|
||||||
@@ -357,18 +346,16 @@ static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
(ulong) hotplug_slot, (ulong) value);
|
(ulong) hotplug_slot, (ulong) value);
|
||||||
ibmphp_lock_operations();
|
ibmphp_lock_operations();
|
||||||
if (hotplug_slot) {
|
if (hotplug_slot) {
|
||||||
pslot = hotplug_slot->private;
|
pslot = to_slot(hotplug_slot);
|
||||||
if (pslot) {
|
memcpy(&myslot, pslot, sizeof(struct slot));
|
||||||
memcpy(&myslot, pslot, sizeof(struct slot));
|
rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
|
||||||
rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
|
&myslot.status);
|
||||||
&(myslot.status));
|
if (!rc) {
|
||||||
if (!rc) {
|
present = SLOT_PRESENT(myslot.status);
|
||||||
present = SLOT_PRESENT(myslot.status);
|
if (present == HPC_SLOT_EMPTY)
|
||||||
if (present == HPC_SLOT_EMPTY)
|
*value = 0;
|
||||||
*value = 0;
|
else
|
||||||
else
|
*value = 1;
|
||||||
*value = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,7 +369,7 @@ static int get_max_bus_speed(struct slot *slot)
|
|||||||
int rc = 0;
|
int rc = 0;
|
||||||
u8 mode = 0;
|
u8 mode = 0;
|
||||||
enum pci_bus_speed speed;
|
enum pci_bus_speed speed;
|
||||||
struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus;
|
struct pci_bus *bus = slot->hotplug_slot.pci_slot->bus;
|
||||||
|
|
||||||
debug("%s - Entry slot[%p]\n", __func__, slot);
|
debug("%s - Entry slot[%p]\n", __func__, slot);
|
||||||
|
|
||||||
@@ -582,29 +569,10 @@ static int validate(struct slot *slot_cur, int opn)
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
int ibmphp_update_slot_info(struct slot *slot_cur)
|
int ibmphp_update_slot_info(struct slot *slot_cur)
|
||||||
{
|
{
|
||||||
struct hotplug_slot_info *info;
|
struct pci_bus *bus = slot_cur->hotplug_slot.pci_slot->bus;
|
||||||
struct pci_bus *bus = slot_cur->hotplug_slot->pci_slot->bus;
|
|
||||||
int rc;
|
|
||||||
u8 bus_speed;
|
u8 bus_speed;
|
||||||
u8 mode;
|
u8 mode;
|
||||||
|
|
||||||
info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
|
|
||||||
if (!info)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
info->power_status = SLOT_PWRGD(slot_cur->status);
|
|
||||||
info->attention_status = SLOT_ATTN(slot_cur->status,
|
|
||||||
slot_cur->ext_status);
|
|
||||||
info->latch_status = SLOT_LATCH(slot_cur->status);
|
|
||||||
if (!SLOT_PRESENT(slot_cur->status)) {
|
|
||||||
info->adapter_status = 0;
|
|
||||||
/* info->max_adapter_speed_status = MAX_ADAPTER_NONE; */
|
|
||||||
} else {
|
|
||||||
info->adapter_status = 1;
|
|
||||||
/* get_max_adapter_speed_1(slot_cur->hotplug_slot,
|
|
||||||
&info->max_adapter_speed_status, 0); */
|
|
||||||
}
|
|
||||||
|
|
||||||
bus_speed = slot_cur->bus_on->current_speed;
|
bus_speed = slot_cur->bus_on->current_speed;
|
||||||
mode = slot_cur->bus_on->current_bus_mode;
|
mode = slot_cur->bus_on->current_bus_mode;
|
||||||
|
|
||||||
@@ -630,9 +598,7 @@ int ibmphp_update_slot_info(struct slot *slot_cur)
|
|||||||
bus->cur_bus_speed = bus_speed;
|
bus->cur_bus_speed = bus_speed;
|
||||||
// To do: bus_names
|
// To do: bus_names
|
||||||
|
|
||||||
rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, info);
|
return 0;
|
||||||
kfree(info);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -673,7 +639,7 @@ static void free_slots(void)
|
|||||||
|
|
||||||
list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head,
|
list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head,
|
||||||
ibm_slot_list) {
|
ibm_slot_list) {
|
||||||
pci_hp_del(slot_cur->hotplug_slot);
|
pci_hp_del(&slot_cur->hotplug_slot);
|
||||||
slot_cur->ctrl = NULL;
|
slot_cur->ctrl = NULL;
|
||||||
slot_cur->bus_on = NULL;
|
slot_cur->bus_on = NULL;
|
||||||
|
|
||||||
@@ -683,9 +649,7 @@ static void free_slots(void)
|
|||||||
*/
|
*/
|
||||||
ibmphp_unconfigure_card(&slot_cur, -1);
|
ibmphp_unconfigure_card(&slot_cur, -1);
|
||||||
|
|
||||||
pci_hp_destroy(slot_cur->hotplug_slot);
|
pci_hp_destroy(&slot_cur->hotplug_slot);
|
||||||
kfree(slot_cur->hotplug_slot->info);
|
|
||||||
kfree(slot_cur->hotplug_slot);
|
|
||||||
kfree(slot_cur);
|
kfree(slot_cur);
|
||||||
}
|
}
|
||||||
debug("%s -- exit\n", __func__);
|
debug("%s -- exit\n", __func__);
|
||||||
@@ -1007,7 +971,7 @@ static int enable_slot(struct hotplug_slot *hs)
|
|||||||
ibmphp_lock_operations();
|
ibmphp_lock_operations();
|
||||||
|
|
||||||
debug("ENABLING SLOT........\n");
|
debug("ENABLING SLOT........\n");
|
||||||
slot_cur = hs->private;
|
slot_cur = to_slot(hs);
|
||||||
|
|
||||||
rc = validate(slot_cur, ENABLE);
|
rc = validate(slot_cur, ENABLE);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@@ -1095,8 +1059,7 @@ static int enable_slot(struct hotplug_slot *hs)
|
|||||||
|
|
||||||
slot_cur->func = kzalloc(sizeof(struct pci_func), GFP_KERNEL);
|
slot_cur->func = kzalloc(sizeof(struct pci_func), GFP_KERNEL);
|
||||||
if (!slot_cur->func) {
|
if (!slot_cur->func) {
|
||||||
/* We cannot do update_slot_info here, since no memory for
|
/* do update_slot_info here? */
|
||||||
* kmalloc n.e.ways, and update_slot_info allocates some */
|
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto error_power;
|
goto error_power;
|
||||||
}
|
}
|
||||||
@@ -1169,7 +1132,7 @@ error_power:
|
|||||||
**************************************************************/
|
**************************************************************/
|
||||||
static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot)
|
static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
ibmphp_lock_operations();
|
ibmphp_lock_operations();
|
||||||
@@ -1259,7 +1222,7 @@ error:
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
|
const struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
|
||||||
.set_attention_status = set_attention_status,
|
.set_attention_status = set_attention_status,
|
||||||
.enable_slot = enable_slot,
|
.enable_slot = enable_slot,
|
||||||
.disable_slot = ibmphp_disable_slot,
|
.disable_slot = ibmphp_disable_slot,
|
||||||
|
|||||||
@@ -666,36 +666,8 @@ static int fillslotinfo(struct hotplug_slot *hotplug_slot)
|
|||||||
struct slot *slot;
|
struct slot *slot;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
if (!hotplug_slot || !hotplug_slot->private)
|
slot = to_slot(hotplug_slot);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
slot = hotplug_slot->private;
|
|
||||||
rc = ibmphp_hpc_readslot(slot, READ_ALLSTAT, NULL);
|
rc = ibmphp_hpc_readslot(slot, READ_ALLSTAT, NULL);
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
// power - enabled:1 not:0
|
|
||||||
hotplug_slot->info->power_status = SLOT_POWER(slot->status);
|
|
||||||
|
|
||||||
// attention - off:0, on:1, blinking:2
|
|
||||||
hotplug_slot->info->attention_status = SLOT_ATTN(slot->status, slot->ext_status);
|
|
||||||
|
|
||||||
// latch - open:1 closed:0
|
|
||||||
hotplug_slot->info->latch_status = SLOT_LATCH(slot->status);
|
|
||||||
|
|
||||||
// pci board - present:1 not:0
|
|
||||||
if (SLOT_PRESENT(slot->status))
|
|
||||||
hotplug_slot->info->adapter_status = 1;
|
|
||||||
else
|
|
||||||
hotplug_slot->info->adapter_status = 0;
|
|
||||||
/*
|
|
||||||
if (slot->bus_on->supported_bus_mode
|
|
||||||
&& (slot->bus_on->supported_speed == BUS_SPEED_66))
|
|
||||||
hotplug_slot->info->max_bus_speed_status = BUS_SPEED_66PCIX;
|
|
||||||
else
|
|
||||||
hotplug_slot->info->max_bus_speed_status = slot->bus_on->supported_speed;
|
|
||||||
*/
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,7 +684,6 @@ static int __init ebda_rsrc_controller(void)
|
|||||||
u8 ctlr_id, temp, bus_index;
|
u8 ctlr_id, temp, bus_index;
|
||||||
u16 ctlr, slot, bus;
|
u16 ctlr, slot, bus;
|
||||||
u16 slot_num, bus_num, index;
|
u16 slot_num, bus_num, index;
|
||||||
struct hotplug_slot *hp_slot_ptr;
|
|
||||||
struct controller *hpc_ptr;
|
struct controller *hpc_ptr;
|
||||||
struct ebda_hpc_bus *bus_ptr;
|
struct ebda_hpc_bus *bus_ptr;
|
||||||
struct ebda_hpc_slot *slot_ptr;
|
struct ebda_hpc_slot *slot_ptr;
|
||||||
@@ -771,7 +742,7 @@ static int __init ebda_rsrc_controller(void)
|
|||||||
bus_info_ptr1 = kzalloc(sizeof(struct bus_info), GFP_KERNEL);
|
bus_info_ptr1 = kzalloc(sizeof(struct bus_info), GFP_KERNEL);
|
||||||
if (!bus_info_ptr1) {
|
if (!bus_info_ptr1) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto error_no_hp_slot;
|
goto error_no_slot;
|
||||||
}
|
}
|
||||||
bus_info_ptr1->slot_min = slot_ptr->slot_num;
|
bus_info_ptr1->slot_min = slot_ptr->slot_num;
|
||||||
bus_info_ptr1->slot_max = slot_ptr->slot_num;
|
bus_info_ptr1->slot_max = slot_ptr->slot_num;
|
||||||
@@ -842,7 +813,7 @@ static int __init ebda_rsrc_controller(void)
|
|||||||
(hpc_ptr->u.isa_ctlr.io_end - hpc_ptr->u.isa_ctlr.io_start + 1),
|
(hpc_ptr->u.isa_ctlr.io_end - hpc_ptr->u.isa_ctlr.io_start + 1),
|
||||||
"ibmphp")) {
|
"ibmphp")) {
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto error_no_hp_slot;
|
goto error_no_slot;
|
||||||
}
|
}
|
||||||
hpc_ptr->irq = readb(io_mem + addr + 4);
|
hpc_ptr->irq = readb(io_mem + addr + 4);
|
||||||
addr += 5;
|
addr += 5;
|
||||||
@@ -857,7 +828,7 @@ static int __init ebda_rsrc_controller(void)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto error_no_hp_slot;
|
goto error_no_slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
//reorganize chassis' linked list
|
//reorganize chassis' linked list
|
||||||
@@ -870,19 +841,6 @@ static int __init ebda_rsrc_controller(void)
|
|||||||
|
|
||||||
// register slots with hpc core as well as create linked list of ibm slot
|
// register slots with hpc core as well as create linked list of ibm slot
|
||||||
for (index = 0; index < hpc_ptr->slot_count; index++) {
|
for (index = 0; index < hpc_ptr->slot_count; index++) {
|
||||||
|
|
||||||
hp_slot_ptr = kzalloc(sizeof(*hp_slot_ptr), GFP_KERNEL);
|
|
||||||
if (!hp_slot_ptr) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto error_no_hp_slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
hp_slot_ptr->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
|
|
||||||
if (!hp_slot_ptr->info) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto error_no_hp_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp_slot = kzalloc(sizeof(*tmp_slot), GFP_KERNEL);
|
tmp_slot = kzalloc(sizeof(*tmp_slot), GFP_KERNEL);
|
||||||
if (!tmp_slot) {
|
if (!tmp_slot) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
@@ -909,7 +867,6 @@ static int __init ebda_rsrc_controller(void)
|
|||||||
|
|
||||||
bus_info_ptr1 = ibmphp_find_same_bus_num(hpc_ptr->slots[index].slot_bus_num);
|
bus_info_ptr1 = ibmphp_find_same_bus_num(hpc_ptr->slots[index].slot_bus_num);
|
||||||
if (!bus_info_ptr1) {
|
if (!bus_info_ptr1) {
|
||||||
kfree(tmp_slot);
|
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -919,22 +876,19 @@ static int __init ebda_rsrc_controller(void)
|
|||||||
|
|
||||||
tmp_slot->ctlr_index = hpc_ptr->slots[index].ctl_index;
|
tmp_slot->ctlr_index = hpc_ptr->slots[index].ctl_index;
|
||||||
tmp_slot->number = hpc_ptr->slots[index].slot_num;
|
tmp_slot->number = hpc_ptr->slots[index].slot_num;
|
||||||
tmp_slot->hotplug_slot = hp_slot_ptr;
|
|
||||||
|
|
||||||
hp_slot_ptr->private = tmp_slot;
|
rc = fillslotinfo(&tmp_slot->hotplug_slot);
|
||||||
|
|
||||||
rc = fillslotinfo(hp_slot_ptr);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
rc = ibmphp_init_devno((struct slot **) &hp_slot_ptr->private);
|
rc = ibmphp_init_devno(&tmp_slot);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto error;
|
goto error;
|
||||||
hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops;
|
tmp_slot->hotplug_slot.ops = &ibmphp_hotplug_slot_ops;
|
||||||
|
|
||||||
// end of registering ibm slot with hotplug core
|
// end of registering ibm slot with hotplug core
|
||||||
|
|
||||||
list_add(&((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head);
|
list_add(&tmp_slot->ibm_slot_list, &ibmphp_slot_head);
|
||||||
}
|
}
|
||||||
|
|
||||||
print_bus_info();
|
print_bus_info();
|
||||||
@@ -944,7 +898,7 @@ static int __init ebda_rsrc_controller(void)
|
|||||||
|
|
||||||
list_for_each_entry(tmp_slot, &ibmphp_slot_head, ibm_slot_list) {
|
list_for_each_entry(tmp_slot, &ibmphp_slot_head, ibm_slot_list) {
|
||||||
snprintf(name, SLOT_NAME_SIZE, "%s", create_file_name(tmp_slot));
|
snprintf(name, SLOT_NAME_SIZE, "%s", create_file_name(tmp_slot));
|
||||||
pci_hp_register(tmp_slot->hotplug_slot,
|
pci_hp_register(&tmp_slot->hotplug_slot,
|
||||||
pci_find_bus(0, tmp_slot->bus), tmp_slot->device, name);
|
pci_find_bus(0, tmp_slot->bus), tmp_slot->device, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -953,12 +907,8 @@ static int __init ebda_rsrc_controller(void)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
kfree(hp_slot_ptr->private);
|
kfree(tmp_slot);
|
||||||
error_no_slot:
|
error_no_slot:
|
||||||
kfree(hp_slot_ptr->info);
|
|
||||||
error_no_hp_info:
|
|
||||||
kfree(hp_slot_ptr);
|
|
||||||
error_no_hp_slot:
|
|
||||||
free_ebda_hpc(hpc_ptr);
|
free_ebda_hpc(hpc_ptr);
|
||||||
error_no_hpc:
|
error_no_hpc:
|
||||||
iounmap(io_mem);
|
iounmap(io_mem);
|
||||||
|
|||||||
@@ -49,15 +49,13 @@ static DEFINE_MUTEX(pci_hp_mutex);
|
|||||||
#define GET_STATUS(name, type) \
|
#define GET_STATUS(name, type) \
|
||||||
static int get_##name(struct hotplug_slot *slot, type *value) \
|
static int get_##name(struct hotplug_slot *slot, type *value) \
|
||||||
{ \
|
{ \
|
||||||
struct hotplug_slot_ops *ops = slot->ops; \
|
const struct hotplug_slot_ops *ops = slot->ops; \
|
||||||
int retval = 0; \
|
int retval = 0; \
|
||||||
if (!try_module_get(ops->owner)) \
|
if (!try_module_get(slot->owner)) \
|
||||||
return -ENODEV; \
|
return -ENODEV; \
|
||||||
if (ops->get_##name) \
|
if (ops->get_##name) \
|
||||||
retval = ops->get_##name(slot, value); \
|
retval = ops->get_##name(slot, value); \
|
||||||
else \
|
module_put(slot->owner); \
|
||||||
*value = slot->info->name; \
|
|
||||||
module_put(ops->owner); \
|
|
||||||
return retval; \
|
return retval; \
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +88,7 @@ static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
|
|||||||
power = (u8)(lpower & 0xff);
|
power = (u8)(lpower & 0xff);
|
||||||
dbg("power = %d\n", power);
|
dbg("power = %d\n", power);
|
||||||
|
|
||||||
if (!try_module_get(slot->ops->owner)) {
|
if (!try_module_get(slot->owner)) {
|
||||||
retval = -ENODEV;
|
retval = -ENODEV;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -109,7 +107,7 @@ static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
|
|||||||
err("Illegal value specified for power\n");
|
err("Illegal value specified for power\n");
|
||||||
retval = -EINVAL;
|
retval = -EINVAL;
|
||||||
}
|
}
|
||||||
module_put(slot->ops->owner);
|
module_put(slot->owner);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (retval)
|
if (retval)
|
||||||
@@ -138,7 +136,8 @@ static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
|
|||||||
static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
|
static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct hotplug_slot_ops *ops = pci_slot->hotplug->ops;
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
||||||
|
const struct hotplug_slot_ops *ops = slot->ops;
|
||||||
unsigned long lattention;
|
unsigned long lattention;
|
||||||
u8 attention;
|
u8 attention;
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
@@ -147,13 +146,13 @@ static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
|
|||||||
attention = (u8)(lattention & 0xff);
|
attention = (u8)(lattention & 0xff);
|
||||||
dbg(" - attention = %d\n", attention);
|
dbg(" - attention = %d\n", attention);
|
||||||
|
|
||||||
if (!try_module_get(ops->owner)) {
|
if (!try_module_get(slot->owner)) {
|
||||||
retval = -ENODEV;
|
retval = -ENODEV;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (ops->set_attention_status)
|
if (ops->set_attention_status)
|
||||||
retval = ops->set_attention_status(pci_slot->hotplug, attention);
|
retval = ops->set_attention_status(slot, attention);
|
||||||
module_put(ops->owner);
|
module_put(slot->owner);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (retval)
|
if (retval)
|
||||||
@@ -213,13 +212,13 @@ static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
|
|||||||
test = (u32)(ltest & 0xffffffff);
|
test = (u32)(ltest & 0xffffffff);
|
||||||
dbg("test = %d\n", test);
|
dbg("test = %d\n", test);
|
||||||
|
|
||||||
if (!try_module_get(slot->ops->owner)) {
|
if (!try_module_get(slot->owner)) {
|
||||||
retval = -ENODEV;
|
retval = -ENODEV;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (slot->ops->hardware_test)
|
if (slot->ops->hardware_test)
|
||||||
retval = slot->ops->hardware_test(slot, test);
|
retval = slot->ops->hardware_test(slot, test);
|
||||||
module_put(slot->ops->owner);
|
module_put(slot->owner);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (retval)
|
if (retval)
|
||||||
@@ -444,11 +443,11 @@ int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,
|
|||||||
|
|
||||||
if (slot == NULL)
|
if (slot == NULL)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
if ((slot->info == NULL) || (slot->ops == NULL))
|
if (slot->ops == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
slot->ops->owner = owner;
|
slot->owner = owner;
|
||||||
slot->ops->mod_name = mod_name;
|
slot->mod_name = mod_name;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No problems if we call this interface from both ACPI_PCI_SLOT
|
* No problems if we call this interface from both ACPI_PCI_SLOT
|
||||||
@@ -559,28 +558,6 @@ void pci_hp_destroy(struct hotplug_slot *slot)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pci_hp_destroy);
|
EXPORT_SYMBOL_GPL(pci_hp_destroy);
|
||||||
|
|
||||||
/**
|
|
||||||
* pci_hp_change_slot_info - changes the slot's information structure in the core
|
|
||||||
* @slot: pointer to the slot whose info has changed
|
|
||||||
* @info: pointer to the info copy into the slot's info structure
|
|
||||||
*
|
|
||||||
* @slot must have been registered with the pci
|
|
||||||
* hotplug subsystem previously with a call to pci_hp_register().
|
|
||||||
*
|
|
||||||
* Returns 0 if successful, anything else for an error.
|
|
||||||
*/
|
|
||||||
int pci_hp_change_slot_info(struct hotplug_slot *slot,
|
|
||||||
struct hotplug_slot_info *info)
|
|
||||||
{
|
|
||||||
if (!slot || !info)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
memcpy(slot->info, info, sizeof(struct hotplug_slot_info));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
|
|
||||||
|
|
||||||
static int __init pci_hotplug_init(void)
|
static int __init pci_hotplug_init(void)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/pci_hotplug.h>
|
#include <linux/pci_hotplug.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/sched/signal.h> /* signal_pending() */
|
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
@@ -59,72 +58,64 @@ do { \
|
|||||||
|
|
||||||
#define SLOT_NAME_SIZE 10
|
#define SLOT_NAME_SIZE 10
|
||||||
|
|
||||||
/**
|
|
||||||
* struct slot - PCIe hotplug slot
|
|
||||||
* @state: current state machine position
|
|
||||||
* @ctrl: pointer to the slot's controller structure
|
|
||||||
* @hotplug_slot: pointer to the structure registered with the PCI hotplug core
|
|
||||||
* @work: work item to turn the slot on or off after 5 seconds in response to
|
|
||||||
* an Attention Button press
|
|
||||||
* @lock: protects reads and writes of @state;
|
|
||||||
* protects scheduling, execution and cancellation of @work
|
|
||||||
*/
|
|
||||||
struct slot {
|
|
||||||
u8 state;
|
|
||||||
struct controller *ctrl;
|
|
||||||
struct hotplug_slot *hotplug_slot;
|
|
||||||
struct delayed_work work;
|
|
||||||
struct mutex lock;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct controller - PCIe hotplug controller
|
* struct controller - PCIe hotplug controller
|
||||||
* @ctrl_lock: serializes writes to the Slot Control register
|
|
||||||
* @pcie: pointer to the controller's PCIe port service device
|
* @pcie: pointer to the controller's PCIe port service device
|
||||||
* @reset_lock: prevents access to the Data Link Layer Link Active bit in the
|
|
||||||
* Link Status register and to the Presence Detect State bit in the Slot
|
|
||||||
* Status register during a slot reset which may cause them to flap
|
|
||||||
* @slot: pointer to the controller's slot structure
|
|
||||||
* @queue: wait queue to wake up on reception of a Command Completed event,
|
|
||||||
* used for synchronous writes to the Slot Control register
|
|
||||||
* @slot_cap: cached copy of the Slot Capabilities register
|
* @slot_cap: cached copy of the Slot Capabilities register
|
||||||
* @slot_ctrl: cached copy of the Slot Control register
|
* @slot_ctrl: cached copy of the Slot Control register
|
||||||
* @poll_thread: thread to poll for slot events if no IRQ is available,
|
* @ctrl_lock: serializes writes to the Slot Control register
|
||||||
* enabled with pciehp_poll_mode module parameter
|
|
||||||
* @cmd_started: jiffies when the Slot Control register was last written;
|
* @cmd_started: jiffies when the Slot Control register was last written;
|
||||||
* the next write is allowed 1 second later, absent a Command Completed
|
* the next write is allowed 1 second later, absent a Command Completed
|
||||||
* interrupt (PCIe r4.0, sec 6.7.3.2)
|
* interrupt (PCIe r4.0, sec 6.7.3.2)
|
||||||
* @cmd_busy: flag set on Slot Control register write, cleared by IRQ handler
|
* @cmd_busy: flag set on Slot Control register write, cleared by IRQ handler
|
||||||
* on reception of a Command Completed event
|
* on reception of a Command Completed event
|
||||||
* @link_active_reporting: cached copy of Data Link Layer Link Active Reporting
|
* @queue: wait queue to wake up on reception of a Command Completed event,
|
||||||
* Capable bit in Link Capabilities register; if this bit is zero, the
|
* used for synchronous writes to the Slot Control register
|
||||||
* Data Link Layer Link Active bit in the Link Status register will never
|
* @pending_events: used by the IRQ handler to save events retrieved from the
|
||||||
* be set and the driver is thus confined to wait 1 second before assuming
|
* Slot Status register for later consumption by the IRQ thread
|
||||||
* the link to a hotplugged device is up and accessing it
|
|
||||||
* @notification_enabled: whether the IRQ was requested successfully
|
* @notification_enabled: whether the IRQ was requested successfully
|
||||||
* @power_fault_detected: whether a power fault was detected by the hardware
|
* @power_fault_detected: whether a power fault was detected by the hardware
|
||||||
* that has not yet been cleared by the user
|
* that has not yet been cleared by the user
|
||||||
* @pending_events: used by the IRQ handler to save events retrieved from the
|
* @poll_thread: thread to poll for slot events if no IRQ is available,
|
||||||
* Slot Status register for later consumption by the IRQ thread
|
* enabled with pciehp_poll_mode module parameter
|
||||||
|
* @state: current state machine position
|
||||||
|
* @state_lock: protects reads and writes of @state;
|
||||||
|
* protects scheduling, execution and cancellation of @button_work
|
||||||
|
* @button_work: work item to turn the slot on or off after 5 seconds
|
||||||
|
* in response to an Attention Button press
|
||||||
|
* @hotplug_slot: structure registered with the PCI hotplug core
|
||||||
|
* @reset_lock: prevents access to the Data Link Layer Link Active bit in the
|
||||||
|
* Link Status register and to the Presence Detect State bit in the Slot
|
||||||
|
* Status register during a slot reset which may cause them to flap
|
||||||
* @request_result: result of last user request submitted to the IRQ thread
|
* @request_result: result of last user request submitted to the IRQ thread
|
||||||
* @requester: wait queue to wake up on completion of user request,
|
* @requester: wait queue to wake up on completion of user request,
|
||||||
* used for synchronous slot enable/disable request via sysfs
|
* used for synchronous slot enable/disable request via sysfs
|
||||||
|
*
|
||||||
|
* PCIe hotplug has a 1:1 relationship between controller and slot, hence
|
||||||
|
* unlike other drivers, the two aren't represented by separate structures.
|
||||||
*/
|
*/
|
||||||
struct controller {
|
struct controller {
|
||||||
struct mutex ctrl_lock;
|
|
||||||
struct pcie_device *pcie;
|
struct pcie_device *pcie;
|
||||||
struct rw_semaphore reset_lock;
|
|
||||||
struct slot *slot;
|
u32 slot_cap; /* capabilities and quirks */
|
||||||
wait_queue_head_t queue;
|
|
||||||
u32 slot_cap;
|
u16 slot_ctrl; /* control register access */
|
||||||
u16 slot_ctrl;
|
struct mutex ctrl_lock;
|
||||||
struct task_struct *poll_thread;
|
unsigned long cmd_started;
|
||||||
unsigned long cmd_started; /* jiffies */
|
|
||||||
unsigned int cmd_busy:1;
|
unsigned int cmd_busy:1;
|
||||||
unsigned int link_active_reporting:1;
|
wait_queue_head_t queue;
|
||||||
|
|
||||||
|
atomic_t pending_events; /* event handling */
|
||||||
unsigned int notification_enabled:1;
|
unsigned int notification_enabled:1;
|
||||||
unsigned int power_fault_detected;
|
unsigned int power_fault_detected;
|
||||||
atomic_t pending_events;
|
struct task_struct *poll_thread;
|
||||||
|
|
||||||
|
u8 state; /* state machine */
|
||||||
|
struct mutex state_lock;
|
||||||
|
struct delayed_work button_work;
|
||||||
|
|
||||||
|
struct hotplug_slot hotplug_slot; /* hotplug core interface */
|
||||||
|
struct rw_semaphore reset_lock;
|
||||||
int request_result;
|
int request_result;
|
||||||
wait_queue_head_t requester;
|
wait_queue_head_t requester;
|
||||||
};
|
};
|
||||||
@@ -174,42 +165,50 @@ struct controller {
|
|||||||
#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS)
|
#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS)
|
||||||
#define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19)
|
#define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19)
|
||||||
|
|
||||||
int pciehp_sysfs_enable_slot(struct slot *slot);
|
|
||||||
int pciehp_sysfs_disable_slot(struct slot *slot);
|
|
||||||
void pciehp_request(struct controller *ctrl, int action);
|
void pciehp_request(struct controller *ctrl, int action);
|
||||||
void pciehp_handle_button_press(struct slot *slot);
|
void pciehp_handle_button_press(struct controller *ctrl);
|
||||||
void pciehp_handle_disable_request(struct slot *slot);
|
void pciehp_handle_disable_request(struct controller *ctrl);
|
||||||
void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events);
|
void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events);
|
||||||
int pciehp_configure_device(struct slot *p_slot);
|
int pciehp_configure_device(struct controller *ctrl);
|
||||||
void pciehp_unconfigure_device(struct slot *p_slot);
|
void pciehp_unconfigure_device(struct controller *ctrl, bool presence);
|
||||||
void pciehp_queue_pushbutton_work(struct work_struct *work);
|
void pciehp_queue_pushbutton_work(struct work_struct *work);
|
||||||
struct controller *pcie_init(struct pcie_device *dev);
|
struct controller *pcie_init(struct pcie_device *dev);
|
||||||
int pcie_init_notification(struct controller *ctrl);
|
int pcie_init_notification(struct controller *ctrl);
|
||||||
void pcie_shutdown_notification(struct controller *ctrl);
|
void pcie_shutdown_notification(struct controller *ctrl);
|
||||||
void pcie_clear_hotplug_events(struct controller *ctrl);
|
void pcie_clear_hotplug_events(struct controller *ctrl);
|
||||||
int pciehp_power_on_slot(struct slot *slot);
|
void pcie_enable_interrupt(struct controller *ctrl);
|
||||||
void pciehp_power_off_slot(struct slot *slot);
|
void pcie_disable_interrupt(struct controller *ctrl);
|
||||||
void pciehp_get_power_status(struct slot *slot, u8 *status);
|
int pciehp_power_on_slot(struct controller *ctrl);
|
||||||
void pciehp_get_attention_status(struct slot *slot, u8 *status);
|
void pciehp_power_off_slot(struct controller *ctrl);
|
||||||
|
void pciehp_get_power_status(struct controller *ctrl, u8 *status);
|
||||||
|
|
||||||
void pciehp_set_attention_status(struct slot *slot, u8 status);
|
void pciehp_set_attention_status(struct controller *ctrl, u8 status);
|
||||||
void pciehp_get_latch_status(struct slot *slot, u8 *status);
|
void pciehp_get_latch_status(struct controller *ctrl, u8 *status);
|
||||||
void pciehp_get_adapter_status(struct slot *slot, u8 *status);
|
int pciehp_query_power_fault(struct controller *ctrl);
|
||||||
int pciehp_query_power_fault(struct slot *slot);
|
void pciehp_green_led_on(struct controller *ctrl);
|
||||||
void pciehp_green_led_on(struct slot *slot);
|
void pciehp_green_led_off(struct controller *ctrl);
|
||||||
void pciehp_green_led_off(struct slot *slot);
|
void pciehp_green_led_blink(struct controller *ctrl);
|
||||||
void pciehp_green_led_blink(struct slot *slot);
|
bool pciehp_card_present(struct controller *ctrl);
|
||||||
|
bool pciehp_card_present_or_link_active(struct controller *ctrl);
|
||||||
int pciehp_check_link_status(struct controller *ctrl);
|
int pciehp_check_link_status(struct controller *ctrl);
|
||||||
bool pciehp_check_link_active(struct controller *ctrl);
|
bool pciehp_check_link_active(struct controller *ctrl);
|
||||||
void pciehp_release_ctrl(struct controller *ctrl);
|
void pciehp_release_ctrl(struct controller *ctrl);
|
||||||
int pciehp_reset_slot(struct slot *slot, int probe);
|
|
||||||
|
|
||||||
|
int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot);
|
||||||
|
int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot);
|
||||||
|
int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe);
|
||||||
|
int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status);
|
||||||
int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status);
|
int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status);
|
||||||
int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status);
|
int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status);
|
||||||
|
|
||||||
static inline const char *slot_name(struct slot *slot)
|
static inline const char *slot_name(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
return hotplug_slot_name(slot->hotplug_slot);
|
return hotplug_slot_name(&ctrl->hotplug_slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct controller *to_ctrl(struct hotplug_slot *hotplug_slot)
|
||||||
|
{
|
||||||
|
return container_of(hotplug_slot, struct controller, hotplug_slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _PCIEHP_H */
|
#endif /* _PCIEHP_H */
|
||||||
|
|||||||
@@ -23,8 +23,6 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include "pciehp.h"
|
#include "pciehp.h"
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/time.h>
|
|
||||||
|
|
||||||
#include "../pci.h"
|
#include "../pci.h"
|
||||||
|
|
||||||
@@ -47,45 +45,30 @@ MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
|
|||||||
#define PCIE_MODULE_NAME "pciehp"
|
#define PCIE_MODULE_NAME "pciehp"
|
||||||
|
|
||||||
static int set_attention_status(struct hotplug_slot *slot, u8 value);
|
static int set_attention_status(struct hotplug_slot *slot, u8 value);
|
||||||
static int enable_slot(struct hotplug_slot *slot);
|
|
||||||
static int disable_slot(struct hotplug_slot *slot);
|
|
||||||
static int get_power_status(struct hotplug_slot *slot, u8 *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_latch_status(struct hotplug_slot *slot, u8 *value);
|
||||||
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
|
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
|
||||||
static int reset_slot(struct hotplug_slot *slot, int probe);
|
|
||||||
|
|
||||||
static int init_slot(struct controller *ctrl)
|
static int init_slot(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct slot *slot = ctrl->slot;
|
struct hotplug_slot_ops *ops;
|
||||||
struct hotplug_slot *hotplug = NULL;
|
|
||||||
struct hotplug_slot_info *info = NULL;
|
|
||||||
struct hotplug_slot_ops *ops = NULL;
|
|
||||||
char name[SLOT_NAME_SIZE];
|
char name[SLOT_NAME_SIZE];
|
||||||
int retval = -ENOMEM;
|
int retval;
|
||||||
|
|
||||||
hotplug = kzalloc(sizeof(*hotplug), GFP_KERNEL);
|
|
||||||
if (!hotplug)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
|
||||||
if (!info)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* Setup hotplug slot ops */
|
/* Setup hotplug slot ops */
|
||||||
ops = kzalloc(sizeof(*ops), GFP_KERNEL);
|
ops = kzalloc(sizeof(*ops), GFP_KERNEL);
|
||||||
if (!ops)
|
if (!ops)
|
||||||
goto out;
|
return -ENOMEM;
|
||||||
|
|
||||||
ops->enable_slot = enable_slot;
|
ops->enable_slot = pciehp_sysfs_enable_slot;
|
||||||
ops->disable_slot = disable_slot;
|
ops->disable_slot = pciehp_sysfs_disable_slot;
|
||||||
ops->get_power_status = get_power_status;
|
ops->get_power_status = get_power_status;
|
||||||
ops->get_adapter_status = get_adapter_status;
|
ops->get_adapter_status = get_adapter_status;
|
||||||
ops->reset_slot = reset_slot;
|
ops->reset_slot = pciehp_reset_slot;
|
||||||
if (MRL_SENS(ctrl))
|
if (MRL_SENS(ctrl))
|
||||||
ops->get_latch_status = get_latch_status;
|
ops->get_latch_status = get_latch_status;
|
||||||
if (ATTN_LED(ctrl)) {
|
if (ATTN_LED(ctrl)) {
|
||||||
ops->get_attention_status = get_attention_status;
|
ops->get_attention_status = pciehp_get_attention_status;
|
||||||
ops->set_attention_status = set_attention_status;
|
ops->set_attention_status = set_attention_status;
|
||||||
} else if (ctrl->pcie->port->hotplug_user_indicators) {
|
} else if (ctrl->pcie->port->hotplug_user_indicators) {
|
||||||
ops->get_attention_status = pciehp_get_raw_indicator_status;
|
ops->get_attention_status = pciehp_get_raw_indicator_status;
|
||||||
@@ -93,33 +76,24 @@ static int init_slot(struct controller *ctrl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* register this slot with the hotplug pci core */
|
/* register this slot with the hotplug pci core */
|
||||||
hotplug->info = info;
|
ctrl->hotplug_slot.ops = ops;
|
||||||
hotplug->private = slot;
|
|
||||||
hotplug->ops = ops;
|
|
||||||
slot->hotplug_slot = hotplug;
|
|
||||||
snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
|
snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
|
||||||
|
|
||||||
retval = pci_hp_initialize(hotplug,
|
retval = pci_hp_initialize(&ctrl->hotplug_slot,
|
||||||
ctrl->pcie->port->subordinate, 0, name);
|
ctrl->pcie->port->subordinate, 0, name);
|
||||||
if (retval)
|
|
||||||
ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval);
|
|
||||||
out:
|
|
||||||
if (retval) {
|
if (retval) {
|
||||||
|
ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval);
|
||||||
kfree(ops);
|
kfree(ops);
|
||||||
kfree(info);
|
|
||||||
kfree(hotplug);
|
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_slot(struct controller *ctrl)
|
static void cleanup_slot(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct hotplug_slot *hotplug_slot = ctrl->slot->hotplug_slot;
|
struct hotplug_slot *hotplug_slot = &ctrl->hotplug_slot;
|
||||||
|
|
||||||
pci_hp_destroy(hotplug_slot);
|
pci_hp_destroy(hotplug_slot);
|
||||||
kfree(hotplug_slot->ops);
|
kfree(hotplug_slot->ops);
|
||||||
kfree(hotplug_slot->info);
|
|
||||||
kfree(hotplug_slot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -127,79 +101,48 @@ static void cleanup_slot(struct controller *ctrl)
|
|||||||
*/
|
*/
|
||||||
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
struct pci_dev *pdev = slot->ctrl->pcie->port;
|
struct pci_dev *pdev = ctrl->pcie->port;
|
||||||
|
|
||||||
pci_config_pm_runtime_get(pdev);
|
pci_config_pm_runtime_get(pdev);
|
||||||
pciehp_set_attention_status(slot, status);
|
pciehp_set_attention_status(ctrl, status);
|
||||||
pci_config_pm_runtime_put(pdev);
|
pci_config_pm_runtime_put(pdev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
|
||||||
{
|
|
||||||
struct slot *slot = hotplug_slot->private;
|
|
||||||
|
|
||||||
return pciehp_sysfs_enable_slot(slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
|
||||||
{
|
|
||||||
struct slot *slot = hotplug_slot->private;
|
|
||||||
|
|
||||||
return pciehp_sysfs_disable_slot(slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
struct pci_dev *pdev = slot->ctrl->pcie->port;
|
struct pci_dev *pdev = ctrl->pcie->port;
|
||||||
|
|
||||||
pci_config_pm_runtime_get(pdev);
|
pci_config_pm_runtime_get(pdev);
|
||||||
pciehp_get_power_status(slot, value);
|
pciehp_get_power_status(ctrl, value);
|
||||||
pci_config_pm_runtime_put(pdev);
|
pci_config_pm_runtime_put(pdev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|
||||||
{
|
|
||||||
struct slot *slot = hotplug_slot->private;
|
|
||||||
|
|
||||||
pciehp_get_attention_status(slot, value);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
struct pci_dev *pdev = slot->ctrl->pcie->port;
|
struct pci_dev *pdev = ctrl->pcie->port;
|
||||||
|
|
||||||
pci_config_pm_runtime_get(pdev);
|
pci_config_pm_runtime_get(pdev);
|
||||||
pciehp_get_latch_status(slot, value);
|
pciehp_get_latch_status(ctrl, value);
|
||||||
pci_config_pm_runtime_put(pdev);
|
pci_config_pm_runtime_put(pdev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
struct pci_dev *pdev = slot->ctrl->pcie->port;
|
struct pci_dev *pdev = ctrl->pcie->port;
|
||||||
|
|
||||||
pci_config_pm_runtime_get(pdev);
|
pci_config_pm_runtime_get(pdev);
|
||||||
pciehp_get_adapter_status(slot, value);
|
*value = pciehp_card_present_or_link_active(ctrl);
|
||||||
pci_config_pm_runtime_put(pdev);
|
pci_config_pm_runtime_put(pdev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
|
|
||||||
{
|
|
||||||
struct slot *slot = hotplug_slot->private;
|
|
||||||
|
|
||||||
return pciehp_reset_slot(slot, probe);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pciehp_check_presence() - synthesize event if presence has changed
|
* pciehp_check_presence() - synthesize event if presence has changed
|
||||||
*
|
*
|
||||||
@@ -212,20 +155,19 @@ static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
|
|||||||
*/
|
*/
|
||||||
static void pciehp_check_presence(struct controller *ctrl)
|
static void pciehp_check_presence(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct slot *slot = ctrl->slot;
|
bool occupied;
|
||||||
u8 occupied;
|
|
||||||
|
|
||||||
down_read(&ctrl->reset_lock);
|
down_read(&ctrl->reset_lock);
|
||||||
mutex_lock(&slot->lock);
|
mutex_lock(&ctrl->state_lock);
|
||||||
|
|
||||||
pciehp_get_adapter_status(slot, &occupied);
|
occupied = pciehp_card_present_or_link_active(ctrl);
|
||||||
if ((occupied && (slot->state == OFF_STATE ||
|
if ((occupied && (ctrl->state == OFF_STATE ||
|
||||||
slot->state == BLINKINGON_STATE)) ||
|
ctrl->state == BLINKINGON_STATE)) ||
|
||||||
(!occupied && (slot->state == ON_STATE ||
|
(!occupied && (ctrl->state == ON_STATE ||
|
||||||
slot->state == BLINKINGOFF_STATE)))
|
ctrl->state == BLINKINGOFF_STATE)))
|
||||||
pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
|
pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
|
||||||
|
|
||||||
mutex_unlock(&slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
up_read(&ctrl->reset_lock);
|
up_read(&ctrl->reset_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +175,6 @@ static int pciehp_probe(struct pcie_device *dev)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct controller *ctrl;
|
struct controller *ctrl;
|
||||||
struct slot *slot;
|
|
||||||
|
|
||||||
/* If this is not a "hotplug" service, we have no business here. */
|
/* If this is not a "hotplug" service, we have no business here. */
|
||||||
if (dev->service != PCIE_PORT_SERVICE_HP)
|
if (dev->service != PCIE_PORT_SERVICE_HP)
|
||||||
@@ -271,8 +212,7 @@ static int pciehp_probe(struct pcie_device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Publish to user space */
|
/* Publish to user space */
|
||||||
slot = ctrl->slot;
|
rc = pci_hp_add(&ctrl->hotplug_slot);
|
||||||
rc = pci_hp_add(slot->hotplug_slot);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc);
|
ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc);
|
||||||
goto err_out_shutdown_notification;
|
goto err_out_shutdown_notification;
|
||||||
@@ -295,29 +235,43 @@ static void pciehp_remove(struct pcie_device *dev)
|
|||||||
{
|
{
|
||||||
struct controller *ctrl = get_service_data(dev);
|
struct controller *ctrl = get_service_data(dev);
|
||||||
|
|
||||||
pci_hp_del(ctrl->slot->hotplug_slot);
|
pci_hp_del(&ctrl->hotplug_slot);
|
||||||
pcie_shutdown_notification(ctrl);
|
pcie_shutdown_notification(ctrl);
|
||||||
cleanup_slot(ctrl);
|
cleanup_slot(ctrl);
|
||||||
pciehp_release_ctrl(ctrl);
|
pciehp_release_ctrl(ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
|
static bool pme_is_native(struct pcie_device *dev)
|
||||||
|
{
|
||||||
|
const struct pci_host_bridge *host;
|
||||||
|
|
||||||
|
host = pci_find_host_bridge(dev->port->bus);
|
||||||
|
return pcie_ports_native || host->native_pme;
|
||||||
|
}
|
||||||
|
|
||||||
static int pciehp_suspend(struct pcie_device *dev)
|
static int pciehp_suspend(struct pcie_device *dev)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Disable hotplug interrupt so that it does not trigger
|
||||||
|
* immediately when the downstream link goes down.
|
||||||
|
*/
|
||||||
|
if (pme_is_native(dev))
|
||||||
|
pcie_disable_interrupt(get_service_data(dev));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pciehp_resume_noirq(struct pcie_device *dev)
|
static int pciehp_resume_noirq(struct pcie_device *dev)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = get_service_data(dev);
|
struct controller *ctrl = get_service_data(dev);
|
||||||
struct slot *slot = ctrl->slot;
|
|
||||||
|
|
||||||
/* pci_restore_state() just wrote to the Slot Control register */
|
/* pci_restore_state() just wrote to the Slot Control register */
|
||||||
ctrl->cmd_started = jiffies;
|
ctrl->cmd_started = jiffies;
|
||||||
ctrl->cmd_busy = true;
|
ctrl->cmd_busy = true;
|
||||||
|
|
||||||
/* clear spurious events from rediscovery of inserted card */
|
/* clear spurious events from rediscovery of inserted card */
|
||||||
if (slot->state == ON_STATE || slot->state == BLINKINGOFF_STATE)
|
if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE)
|
||||||
pcie_clear_hotplug_events(ctrl);
|
pcie_clear_hotplug_events(ctrl);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -327,10 +281,29 @@ static int pciehp_resume(struct pcie_device *dev)
|
|||||||
{
|
{
|
||||||
struct controller *ctrl = get_service_data(dev);
|
struct controller *ctrl = get_service_data(dev);
|
||||||
|
|
||||||
|
if (pme_is_native(dev))
|
||||||
|
pcie_enable_interrupt(ctrl);
|
||||||
|
|
||||||
pciehp_check_presence(ctrl);
|
pciehp_check_presence(ctrl);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pciehp_runtime_resume(struct pcie_device *dev)
|
||||||
|
{
|
||||||
|
struct controller *ctrl = get_service_data(dev);
|
||||||
|
|
||||||
|
/* pci_restore_state() just wrote to the Slot Control register */
|
||||||
|
ctrl->cmd_started = jiffies;
|
||||||
|
ctrl->cmd_busy = true;
|
||||||
|
|
||||||
|
/* clear spurious events from rediscovery of inserted card */
|
||||||
|
if ((ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) &&
|
||||||
|
pme_is_native(dev))
|
||||||
|
pcie_clear_hotplug_events(ctrl);
|
||||||
|
|
||||||
|
return pciehp_resume(dev);
|
||||||
|
}
|
||||||
#endif /* PM */
|
#endif /* PM */
|
||||||
|
|
||||||
static struct pcie_port_service_driver hpdriver_portdrv = {
|
static struct pcie_port_service_driver hpdriver_portdrv = {
|
||||||
@@ -345,10 +318,12 @@ static struct pcie_port_service_driver hpdriver_portdrv = {
|
|||||||
.suspend = pciehp_suspend,
|
.suspend = pciehp_suspend,
|
||||||
.resume_noirq = pciehp_resume_noirq,
|
.resume_noirq = pciehp_resume_noirq,
|
||||||
.resume = pciehp_resume,
|
.resume = pciehp_resume,
|
||||||
|
.runtime_suspend = pciehp_suspend,
|
||||||
|
.runtime_resume = pciehp_runtime_resume,
|
||||||
#endif /* PM */
|
#endif /* PM */
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init pcied_init(void)
|
int __init pcie_hp_init(void)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
@@ -359,4 +334,3 @@ static int __init pcied_init(void)
|
|||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
device_initcall(pcied_init);
|
|
||||||
|
|||||||
@@ -13,24 +13,24 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include "../pci.h"
|
|
||||||
#include "pciehp.h"
|
#include "pciehp.h"
|
||||||
|
|
||||||
/* The following routines constitute the bulk of the
|
/* The following routines constitute the bulk of the
|
||||||
hotplug controller logic
|
hotplug controller logic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void set_slot_off(struct controller *ctrl, struct slot *pslot)
|
#define SAFE_REMOVAL true
|
||||||
|
#define SURPRISE_REMOVAL false
|
||||||
|
|
||||||
|
static void set_slot_off(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
/* turn off slot, turn on Amber LED, turn off Green LED if supported*/
|
/* turn off slot, turn on Amber LED, turn off Green LED if supported*/
|
||||||
if (POWER_CTRL(ctrl)) {
|
if (POWER_CTRL(ctrl)) {
|
||||||
pciehp_power_off_slot(pslot);
|
pciehp_power_off_slot(ctrl);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* After turning power off, we must wait for at least 1 second
|
* After turning power off, we must wait for at least 1 second
|
||||||
@@ -40,31 +40,30 @@ static void set_slot_off(struct controller *ctrl, struct slot *pslot)
|
|||||||
msleep(1000);
|
msleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
pciehp_green_led_off(pslot);
|
pciehp_green_led_off(ctrl);
|
||||||
pciehp_set_attention_status(pslot, 1);
|
pciehp_set_attention_status(ctrl, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* board_added - Called after a board has been added to the system.
|
* board_added - Called after a board has been added to the system.
|
||||||
* @p_slot: &slot where board is added
|
* @ctrl: PCIe hotplug controller where board is added
|
||||||
*
|
*
|
||||||
* Turns power on for the board.
|
* Turns power on for the board.
|
||||||
* Configures board.
|
* Configures board.
|
||||||
*/
|
*/
|
||||||
static int board_added(struct slot *p_slot)
|
static int board_added(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
|
||||||
struct pci_bus *parent = ctrl->pcie->port->subordinate;
|
struct pci_bus *parent = ctrl->pcie->port->subordinate;
|
||||||
|
|
||||||
if (POWER_CTRL(ctrl)) {
|
if (POWER_CTRL(ctrl)) {
|
||||||
/* Power on slot */
|
/* Power on slot */
|
||||||
retval = pciehp_power_on_slot(p_slot);
|
retval = pciehp_power_on_slot(ctrl);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
pciehp_green_led_blink(p_slot);
|
pciehp_green_led_blink(ctrl);
|
||||||
|
|
||||||
/* Check link training status */
|
/* Check link training status */
|
||||||
retval = pciehp_check_link_status(ctrl);
|
retval = pciehp_check_link_status(ctrl);
|
||||||
@@ -74,13 +73,13 @@ static int board_added(struct slot *p_slot)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check for a power fault */
|
/* Check for a power fault */
|
||||||
if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
|
if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) {
|
||||||
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot));
|
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
|
||||||
retval = -EIO;
|
retval = -EIO;
|
||||||
goto err_exit;
|
goto err_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = pciehp_configure_device(p_slot);
|
retval = pciehp_configure_device(ctrl);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
if (retval != -EEXIST) {
|
if (retval != -EEXIST) {
|
||||||
ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
|
ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
|
||||||
@@ -89,27 +88,26 @@ static int board_added(struct slot *p_slot)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pciehp_green_led_on(p_slot);
|
pciehp_green_led_on(ctrl);
|
||||||
pciehp_set_attention_status(p_slot, 0);
|
pciehp_set_attention_status(ctrl, 0);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_exit:
|
err_exit:
|
||||||
set_slot_off(ctrl, p_slot);
|
set_slot_off(ctrl);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove_board - Turns off slot and LEDs
|
* remove_board - Turns off slot and LEDs
|
||||||
* @p_slot: slot where board is being removed
|
* @ctrl: PCIe hotplug controller where board is being removed
|
||||||
|
* @safe_removal: whether the board is safely removed (versus surprise removed)
|
||||||
*/
|
*/
|
||||||
static void remove_board(struct slot *p_slot)
|
static void remove_board(struct controller *ctrl, bool safe_removal)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
pciehp_unconfigure_device(ctrl, safe_removal);
|
||||||
|
|
||||||
pciehp_unconfigure_device(p_slot);
|
|
||||||
|
|
||||||
if (POWER_CTRL(ctrl)) {
|
if (POWER_CTRL(ctrl)) {
|
||||||
pciehp_power_off_slot(p_slot);
|
pciehp_power_off_slot(ctrl);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* After turning power off, we must wait for at least 1 second
|
* After turning power off, we must wait for at least 1 second
|
||||||
@@ -120,11 +118,11 @@ static void remove_board(struct slot *p_slot)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* turn off Green LED */
|
/* turn off Green LED */
|
||||||
pciehp_green_led_off(p_slot);
|
pciehp_green_led_off(ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pciehp_enable_slot(struct slot *slot);
|
static int pciehp_enable_slot(struct controller *ctrl);
|
||||||
static int pciehp_disable_slot(struct slot *slot);
|
static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal);
|
||||||
|
|
||||||
void pciehp_request(struct controller *ctrl, int action)
|
void pciehp_request(struct controller *ctrl, int action)
|
||||||
{
|
{
|
||||||
@@ -135,11 +133,11 @@ void pciehp_request(struct controller *ctrl, int action)
|
|||||||
|
|
||||||
void pciehp_queue_pushbutton_work(struct work_struct *work)
|
void pciehp_queue_pushbutton_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct slot *p_slot = container_of(work, struct slot, work.work);
|
struct controller *ctrl = container_of(work, struct controller,
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
button_work.work);
|
||||||
|
|
||||||
mutex_lock(&p_slot->lock);
|
mutex_lock(&ctrl->state_lock);
|
||||||
switch (p_slot->state) {
|
switch (ctrl->state) {
|
||||||
case BLINKINGOFF_STATE:
|
case BLINKINGOFF_STATE:
|
||||||
pciehp_request(ctrl, DISABLE_SLOT);
|
pciehp_request(ctrl, DISABLE_SLOT);
|
||||||
break;
|
break;
|
||||||
@@ -149,30 +147,28 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mutex_unlock(&p_slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_handle_button_press(struct slot *p_slot)
|
void pciehp_handle_button_press(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
mutex_lock(&ctrl->state_lock);
|
||||||
|
switch (ctrl->state) {
|
||||||
mutex_lock(&p_slot->lock);
|
|
||||||
switch (p_slot->state) {
|
|
||||||
case OFF_STATE:
|
case OFF_STATE:
|
||||||
case ON_STATE:
|
case ON_STATE:
|
||||||
if (p_slot->state == ON_STATE) {
|
if (ctrl->state == ON_STATE) {
|
||||||
p_slot->state = BLINKINGOFF_STATE;
|
ctrl->state = BLINKINGOFF_STATE;
|
||||||
ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
|
ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
} else {
|
} else {
|
||||||
p_slot->state = BLINKINGON_STATE;
|
ctrl->state = BLINKINGON_STATE;
|
||||||
ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
|
ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
}
|
}
|
||||||
/* blink green LED and turn off amber */
|
/* blink green LED and turn off amber */
|
||||||
pciehp_green_led_blink(p_slot);
|
pciehp_green_led_blink(ctrl);
|
||||||
pciehp_set_attention_status(p_slot, 0);
|
pciehp_set_attention_status(ctrl, 0);
|
||||||
schedule_delayed_work(&p_slot->work, 5 * HZ);
|
schedule_delayed_work(&ctrl->button_work, 5 * HZ);
|
||||||
break;
|
break;
|
||||||
case BLINKINGOFF_STATE:
|
case BLINKINGOFF_STATE:
|
||||||
case BLINKINGON_STATE:
|
case BLINKINGON_STATE:
|
||||||
@@ -181,197 +177,184 @@ void pciehp_handle_button_press(struct slot *p_slot)
|
|||||||
* press the attention again before the 5 sec. limit
|
* press the attention again before the 5 sec. limit
|
||||||
* expires to cancel hot-add or hot-remove
|
* expires to cancel hot-add or hot-remove
|
||||||
*/
|
*/
|
||||||
ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot));
|
ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl));
|
||||||
cancel_delayed_work(&p_slot->work);
|
cancel_delayed_work(&ctrl->button_work);
|
||||||
if (p_slot->state == BLINKINGOFF_STATE) {
|
if (ctrl->state == BLINKINGOFF_STATE) {
|
||||||
p_slot->state = ON_STATE;
|
ctrl->state = ON_STATE;
|
||||||
pciehp_green_led_on(p_slot);
|
pciehp_green_led_on(ctrl);
|
||||||
} else {
|
} else {
|
||||||
p_slot->state = OFF_STATE;
|
ctrl->state = OFF_STATE;
|
||||||
pciehp_green_led_off(p_slot);
|
pciehp_green_led_off(ctrl);
|
||||||
}
|
}
|
||||||
pciehp_set_attention_status(p_slot, 0);
|
pciehp_set_attention_status(ctrl, 0);
|
||||||
ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
|
ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
|
ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
|
||||||
slot_name(p_slot), p_slot->state);
|
slot_name(ctrl), ctrl->state);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mutex_unlock(&p_slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_handle_disable_request(struct slot *slot)
|
void pciehp_handle_disable_request(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
mutex_lock(&ctrl->state_lock);
|
||||||
|
switch (ctrl->state) {
|
||||||
mutex_lock(&slot->lock);
|
|
||||||
switch (slot->state) {
|
|
||||||
case BLINKINGON_STATE:
|
case BLINKINGON_STATE:
|
||||||
case BLINKINGOFF_STATE:
|
case BLINKINGOFF_STATE:
|
||||||
cancel_delayed_work(&slot->work);
|
cancel_delayed_work(&ctrl->button_work);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
slot->state = POWEROFF_STATE;
|
ctrl->state = POWEROFF_STATE;
|
||||||
mutex_unlock(&slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
|
|
||||||
ctrl->request_result = pciehp_disable_slot(slot);
|
ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events)
|
void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
bool present, link_active;
|
||||||
bool link_active;
|
|
||||||
u8 present;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the slot is on and presence or link has changed, turn it off.
|
* If the slot is on and presence or link has changed, turn it off.
|
||||||
* Even if it's occupied again, we cannot assume the card is the same.
|
* Even if it's occupied again, we cannot assume the card is the same.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&slot->lock);
|
mutex_lock(&ctrl->state_lock);
|
||||||
switch (slot->state) {
|
switch (ctrl->state) {
|
||||||
case BLINKINGOFF_STATE:
|
case BLINKINGOFF_STATE:
|
||||||
cancel_delayed_work(&slot->work);
|
cancel_delayed_work(&ctrl->button_work);
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case ON_STATE:
|
case ON_STATE:
|
||||||
slot->state = POWEROFF_STATE;
|
ctrl->state = POWEROFF_STATE;
|
||||||
mutex_unlock(&slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
if (events & PCI_EXP_SLTSTA_DLLSC)
|
if (events & PCI_EXP_SLTSTA_DLLSC)
|
||||||
ctrl_info(ctrl, "Slot(%s): Link Down\n",
|
ctrl_info(ctrl, "Slot(%s): Link Down\n",
|
||||||
slot_name(slot));
|
slot_name(ctrl));
|
||||||
if (events & PCI_EXP_SLTSTA_PDC)
|
if (events & PCI_EXP_SLTSTA_PDC)
|
||||||
ctrl_info(ctrl, "Slot(%s): Card not present\n",
|
ctrl_info(ctrl, "Slot(%s): Card not present\n",
|
||||||
slot_name(slot));
|
slot_name(ctrl));
|
||||||
pciehp_disable_slot(slot);
|
pciehp_disable_slot(ctrl, SURPRISE_REMOVAL);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mutex_unlock(&slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turn the slot on if it's occupied or link is up */
|
/* Turn the slot on if it's occupied or link is up */
|
||||||
mutex_lock(&slot->lock);
|
mutex_lock(&ctrl->state_lock);
|
||||||
pciehp_get_adapter_status(slot, &present);
|
present = pciehp_card_present(ctrl);
|
||||||
link_active = pciehp_check_link_active(ctrl);
|
link_active = pciehp_check_link_active(ctrl);
|
||||||
if (!present && !link_active) {
|
if (!present && !link_active) {
|
||||||
mutex_unlock(&slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (slot->state) {
|
switch (ctrl->state) {
|
||||||
case BLINKINGON_STATE:
|
case BLINKINGON_STATE:
|
||||||
cancel_delayed_work(&slot->work);
|
cancel_delayed_work(&ctrl->button_work);
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case OFF_STATE:
|
case OFF_STATE:
|
||||||
slot->state = POWERON_STATE;
|
ctrl->state = POWERON_STATE;
|
||||||
mutex_unlock(&slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
if (present)
|
if (present)
|
||||||
ctrl_info(ctrl, "Slot(%s): Card present\n",
|
ctrl_info(ctrl, "Slot(%s): Card present\n",
|
||||||
slot_name(slot));
|
slot_name(ctrl));
|
||||||
if (link_active)
|
if (link_active)
|
||||||
ctrl_info(ctrl, "Slot(%s): Link Up\n",
|
ctrl_info(ctrl, "Slot(%s): Link Up\n",
|
||||||
slot_name(slot));
|
slot_name(ctrl));
|
||||||
ctrl->request_result = pciehp_enable_slot(slot);
|
ctrl->request_result = pciehp_enable_slot(ctrl);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mutex_unlock(&slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __pciehp_enable_slot(struct slot *p_slot)
|
static int __pciehp_enable_slot(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
u8 getstatus = 0;
|
u8 getstatus = 0;
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
|
||||||
|
|
||||||
pciehp_get_adapter_status(p_slot, &getstatus);
|
if (MRL_SENS(ctrl)) {
|
||||||
if (!getstatus) {
|
pciehp_get_latch_status(ctrl, &getstatus);
|
||||||
ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot));
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
if (MRL_SENS(p_slot->ctrl)) {
|
|
||||||
pciehp_get_latch_status(p_slot, &getstatus);
|
|
||||||
if (getstatus) {
|
if (getstatus) {
|
||||||
ctrl_info(ctrl, "Slot(%s): Latch open\n",
|
ctrl_info(ctrl, "Slot(%s): Latch open\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (POWER_CTRL(p_slot->ctrl)) {
|
if (POWER_CTRL(ctrl)) {
|
||||||
pciehp_get_power_status(p_slot, &getstatus);
|
pciehp_get_power_status(ctrl, &getstatus);
|
||||||
if (getstatus) {
|
if (getstatus) {
|
||||||
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
|
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return board_added(p_slot);
|
return board_added(ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pciehp_enable_slot(struct slot *slot)
|
static int pciehp_enable_slot(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pm_runtime_get_sync(&ctrl->pcie->port->dev);
|
pm_runtime_get_sync(&ctrl->pcie->port->dev);
|
||||||
ret = __pciehp_enable_slot(slot);
|
ret = __pciehp_enable_slot(ctrl);
|
||||||
if (ret && ATTN_BUTTN(ctrl))
|
if (ret && ATTN_BUTTN(ctrl))
|
||||||
pciehp_green_led_off(slot); /* may be blinking */
|
pciehp_green_led_off(ctrl); /* may be blinking */
|
||||||
pm_runtime_put(&ctrl->pcie->port->dev);
|
pm_runtime_put(&ctrl->pcie->port->dev);
|
||||||
|
|
||||||
mutex_lock(&slot->lock);
|
mutex_lock(&ctrl->state_lock);
|
||||||
slot->state = ret ? OFF_STATE : ON_STATE;
|
ctrl->state = ret ? OFF_STATE : ON_STATE;
|
||||||
mutex_unlock(&slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __pciehp_disable_slot(struct slot *p_slot)
|
static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
|
||||||
{
|
{
|
||||||
u8 getstatus = 0;
|
u8 getstatus = 0;
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
|
||||||
|
|
||||||
if (POWER_CTRL(p_slot->ctrl)) {
|
if (POWER_CTRL(ctrl)) {
|
||||||
pciehp_get_power_status(p_slot, &getstatus);
|
pciehp_get_power_status(ctrl, &getstatus);
|
||||||
if (!getstatus) {
|
if (!getstatus) {
|
||||||
ctrl_info(ctrl, "Slot(%s): Already disabled\n",
|
ctrl_info(ctrl, "Slot(%s): Already disabled\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_board(p_slot);
|
remove_board(ctrl, safe_removal);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pciehp_disable_slot(struct slot *slot)
|
static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pm_runtime_get_sync(&ctrl->pcie->port->dev);
|
pm_runtime_get_sync(&ctrl->pcie->port->dev);
|
||||||
ret = __pciehp_disable_slot(slot);
|
ret = __pciehp_disable_slot(ctrl, safe_removal);
|
||||||
pm_runtime_put(&ctrl->pcie->port->dev);
|
pm_runtime_put(&ctrl->pcie->port->dev);
|
||||||
|
|
||||||
mutex_lock(&slot->lock);
|
mutex_lock(&ctrl->state_lock);
|
||||||
slot->state = OFF_STATE;
|
ctrl->state = OFF_STATE;
|
||||||
mutex_unlock(&slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pciehp_sysfs_enable_slot(struct slot *p_slot)
|
int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
|
|
||||||
mutex_lock(&p_slot->lock);
|
mutex_lock(&ctrl->state_lock);
|
||||||
switch (p_slot->state) {
|
switch (ctrl->state) {
|
||||||
case BLINKINGON_STATE:
|
case BLINKINGON_STATE:
|
||||||
case OFF_STATE:
|
case OFF_STATE:
|
||||||
mutex_unlock(&p_slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
/*
|
/*
|
||||||
* The IRQ thread becomes a no-op if the user pulls out the
|
* The IRQ thread becomes a no-op if the user pulls out the
|
||||||
* card before the thread wakes up, so initialize to -ENODEV.
|
* card before the thread wakes up, so initialize to -ENODEV.
|
||||||
@@ -383,53 +366,53 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
|
|||||||
return ctrl->request_result;
|
return ctrl->request_result;
|
||||||
case POWERON_STATE:
|
case POWERON_STATE:
|
||||||
ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
|
ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
break;
|
break;
|
||||||
case BLINKINGOFF_STATE:
|
case BLINKINGOFF_STATE:
|
||||||
case ON_STATE:
|
case ON_STATE:
|
||||||
case POWEROFF_STATE:
|
case POWEROFF_STATE:
|
||||||
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
|
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
|
ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
|
||||||
slot_name(p_slot), p_slot->state);
|
slot_name(ctrl), ctrl->state);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mutex_unlock(&p_slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pciehp_sysfs_disable_slot(struct slot *p_slot)
|
int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
|
|
||||||
mutex_lock(&p_slot->lock);
|
mutex_lock(&ctrl->state_lock);
|
||||||
switch (p_slot->state) {
|
switch (ctrl->state) {
|
||||||
case BLINKINGOFF_STATE:
|
case BLINKINGOFF_STATE:
|
||||||
case ON_STATE:
|
case ON_STATE:
|
||||||
mutex_unlock(&p_slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
pciehp_request(ctrl, DISABLE_SLOT);
|
pciehp_request(ctrl, DISABLE_SLOT);
|
||||||
wait_event(ctrl->requester,
|
wait_event(ctrl->requester,
|
||||||
!atomic_read(&ctrl->pending_events));
|
!atomic_read(&ctrl->pending_events));
|
||||||
return ctrl->request_result;
|
return ctrl->request_result;
|
||||||
case POWEROFF_STATE:
|
case POWEROFF_STATE:
|
||||||
ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
|
ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
break;
|
break;
|
||||||
case BLINKINGON_STATE:
|
case BLINKINGON_STATE:
|
||||||
case OFF_STATE:
|
case OFF_STATE:
|
||||||
case POWERON_STATE:
|
case POWERON_STATE:
|
||||||
ctrl_info(ctrl, "Slot(%s): Already disabled\n",
|
ctrl_info(ctrl, "Slot(%s): Already disabled\n",
|
||||||
slot_name(p_slot));
|
slot_name(ctrl));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
|
ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
|
||||||
slot_name(p_slot), p_slot->state);
|
slot_name(ctrl), ctrl->state);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mutex_unlock(&p_slot->lock);
|
mutex_unlock(&ctrl->state_lock);
|
||||||
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,15 +13,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/signal.h>
|
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/time.h>
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include "../pci.h"
|
#include "../pci.h"
|
||||||
@@ -43,7 +40,7 @@ static inline int pciehp_request_irq(struct controller *ctrl)
|
|||||||
if (pciehp_poll_mode) {
|
if (pciehp_poll_mode) {
|
||||||
ctrl->poll_thread = kthread_run(&pciehp_poll, ctrl,
|
ctrl->poll_thread = kthread_run(&pciehp_poll, ctrl,
|
||||||
"pciehp_poll-%s",
|
"pciehp_poll-%s",
|
||||||
slot_name(ctrl->slot));
|
slot_name(ctrl));
|
||||||
return PTR_ERR_OR_ZERO(ctrl->poll_thread);
|
return PTR_ERR_OR_ZERO(ctrl->poll_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,13 +214,6 @@ bool pciehp_check_link_active(struct controller *ctrl)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pcie_wait_link_active(struct controller *ctrl)
|
|
||||||
{
|
|
||||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
|
||||||
|
|
||||||
pcie_wait_for_link(pdev, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
|
static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
|
||||||
{
|
{
|
||||||
u32 l;
|
u32 l;
|
||||||
@@ -256,18 +246,9 @@ int pciehp_check_link_status(struct controller *ctrl)
|
|||||||
bool found;
|
bool found;
|
||||||
u16 lnk_status;
|
u16 lnk_status;
|
||||||
|
|
||||||
/*
|
if (!pcie_wait_for_link(pdev, true))
|
||||||
* Data Link Layer Link Active Reporting must be capable for
|
return -1;
|
||||||
* hot-plug capable downstream port. But old controller might
|
|
||||||
* not implement it. In this case, we wait for 1000 ms.
|
|
||||||
*/
|
|
||||||
if (ctrl->link_active_reporting)
|
|
||||||
pcie_wait_link_active(ctrl);
|
|
||||||
else
|
|
||||||
msleep(1000);
|
|
||||||
|
|
||||||
/* wait 100ms before read pci conf, and try in 1s */
|
|
||||||
msleep(100);
|
|
||||||
found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
|
found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
|
||||||
PCI_DEVFN(0, 0));
|
PCI_DEVFN(0, 0));
|
||||||
|
|
||||||
@@ -318,8 +299,8 @@ static int pciehp_link_enable(struct controller *ctrl)
|
|||||||
int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
||||||
u8 *status)
|
u8 *status)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
struct pci_dev *pdev = ctrl_dev(slot->ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
u16 slot_ctrl;
|
u16 slot_ctrl;
|
||||||
|
|
||||||
pci_config_pm_runtime_get(pdev);
|
pci_config_pm_runtime_get(pdev);
|
||||||
@@ -329,9 +310,9 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_get_attention_status(struct slot *slot, u8 *status)
|
int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
u16 slot_ctrl;
|
u16 slot_ctrl;
|
||||||
|
|
||||||
@@ -355,11 +336,12 @@ void pciehp_get_attention_status(struct slot *slot, u8 *status)
|
|||||||
*status = 0xFF;
|
*status = 0xFF;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_get_power_status(struct slot *slot, u8 *status)
|
void pciehp_get_power_status(struct controller *ctrl, u8 *status)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
u16 slot_ctrl;
|
u16 slot_ctrl;
|
||||||
|
|
||||||
@@ -380,27 +362,41 @@ void pciehp_get_power_status(struct slot *slot, u8 *status)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_get_latch_status(struct slot *slot, u8 *status)
|
void pciehp_get_latch_status(struct controller *ctrl, u8 *status)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = ctrl_dev(slot->ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
u16 slot_status;
|
u16 slot_status;
|
||||||
|
|
||||||
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
|
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
|
||||||
*status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS);
|
*status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_get_adapter_status(struct slot *slot, u8 *status)
|
bool pciehp_card_present(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = ctrl_dev(slot->ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
u16 slot_status;
|
u16 slot_status;
|
||||||
|
|
||||||
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
|
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
|
||||||
*status = !!(slot_status & PCI_EXP_SLTSTA_PDS);
|
return slot_status & PCI_EXP_SLTSTA_PDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pciehp_query_power_fault(struct slot *slot)
|
/**
|
||||||
|
* pciehp_card_present_or_link_active() - whether given slot is occupied
|
||||||
|
* @ctrl: PCIe hotplug controller
|
||||||
|
*
|
||||||
|
* Unlike pciehp_card_present(), which determines presence solely from the
|
||||||
|
* Presence Detect State bit, this helper also returns true if the Link Active
|
||||||
|
* bit is set. This is a concession to broken hotplug ports which hardwire
|
||||||
|
* Presence Detect State to zero, such as Wilocity's [1ae9:0200].
|
||||||
|
*/
|
||||||
|
bool pciehp_card_present_or_link_active(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = ctrl_dev(slot->ctrl);
|
return pciehp_card_present(ctrl) || pciehp_check_link_active(ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pciehp_query_power_fault(struct controller *ctrl)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
u16 slot_status;
|
u16 slot_status;
|
||||||
|
|
||||||
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
|
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
|
||||||
@@ -410,8 +406,7 @@ int pciehp_query_power_fault(struct slot *slot)
|
|||||||
int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
||||||
u8 status)
|
u8 status)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
|
|
||||||
pci_config_pm_runtime_get(pdev);
|
pci_config_pm_runtime_get(pdev);
|
||||||
@@ -421,9 +416,8 @@ int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_set_attention_status(struct slot *slot, u8 value)
|
void pciehp_set_attention_status(struct controller *ctrl, u8 value)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
u16 slot_cmd;
|
u16 slot_cmd;
|
||||||
|
|
||||||
if (!ATTN_LED(ctrl))
|
if (!ATTN_LED(ctrl))
|
||||||
@@ -447,10 +441,8 @@ void pciehp_set_attention_status(struct slot *slot, u8 value)
|
|||||||
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
|
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_green_led_on(struct slot *slot)
|
void pciehp_green_led_on(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
|
|
||||||
if (!PWR_LED(ctrl))
|
if (!PWR_LED(ctrl))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -461,10 +453,8 @@ void pciehp_green_led_on(struct slot *slot)
|
|||||||
PCI_EXP_SLTCTL_PWR_IND_ON);
|
PCI_EXP_SLTCTL_PWR_IND_ON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_green_led_off(struct slot *slot)
|
void pciehp_green_led_off(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
|
|
||||||
if (!PWR_LED(ctrl))
|
if (!PWR_LED(ctrl))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -475,10 +465,8 @@ void pciehp_green_led_off(struct slot *slot)
|
|||||||
PCI_EXP_SLTCTL_PWR_IND_OFF);
|
PCI_EXP_SLTCTL_PWR_IND_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_green_led_blink(struct slot *slot)
|
void pciehp_green_led_blink(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
|
|
||||||
if (!PWR_LED(ctrl))
|
if (!PWR_LED(ctrl))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -489,9 +477,8 @@ void pciehp_green_led_blink(struct slot *slot)
|
|||||||
PCI_EXP_SLTCTL_PWR_IND_BLINK);
|
PCI_EXP_SLTCTL_PWR_IND_BLINK);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pciehp_power_on_slot(struct slot *slot)
|
int pciehp_power_on_slot(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
u16 slot_status;
|
u16 slot_status;
|
||||||
int retval;
|
int retval;
|
||||||
@@ -515,10 +502,8 @@ int pciehp_power_on_slot(struct slot *slot)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_power_off_slot(struct slot *slot)
|
void pciehp_power_off_slot(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
|
||||||
|
|
||||||
pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC);
|
pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC);
|
||||||
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
||||||
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
|
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
|
||||||
@@ -533,9 +518,11 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
|
|||||||
u16 status, events;
|
u16 status, events;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Interrupts only occur in D3hot or shallower (PCIe r4.0, sec 6.7.3.4).
|
* Interrupts only occur in D3hot or shallower and only if enabled
|
||||||
|
* in the Slot Control register (PCIe r4.0, sec 6.7.3.4).
|
||||||
*/
|
*/
|
||||||
if (pdev->current_state == PCI_D3cold)
|
if (pdev->current_state == PCI_D3cold ||
|
||||||
|
(!(ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE) && !pciehp_poll_mode))
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -616,7 +603,6 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
|
|||||||
{
|
{
|
||||||
struct controller *ctrl = (struct controller *)dev_id;
|
struct controller *ctrl = (struct controller *)dev_id;
|
||||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
struct slot *slot = ctrl->slot;
|
|
||||||
irqreturn_t ret;
|
irqreturn_t ret;
|
||||||
u32 events;
|
u32 events;
|
||||||
|
|
||||||
@@ -642,16 +628,16 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
|
|||||||
/* Check Attention Button Pressed */
|
/* Check Attention Button Pressed */
|
||||||
if (events & PCI_EXP_SLTSTA_ABP) {
|
if (events & PCI_EXP_SLTSTA_ABP) {
|
||||||
ctrl_info(ctrl, "Slot(%s): Attention button pressed\n",
|
ctrl_info(ctrl, "Slot(%s): Attention button pressed\n",
|
||||||
slot_name(slot));
|
slot_name(ctrl));
|
||||||
pciehp_handle_button_press(slot);
|
pciehp_handle_button_press(ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check Power Fault Detected */
|
/* Check Power Fault Detected */
|
||||||
if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
|
if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
|
||||||
ctrl->power_fault_detected = 1;
|
ctrl->power_fault_detected = 1;
|
||||||
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot));
|
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
|
||||||
pciehp_set_attention_status(slot, 1);
|
pciehp_set_attention_status(ctrl, 1);
|
||||||
pciehp_green_led_off(slot);
|
pciehp_green_led_off(ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -660,9 +646,9 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
|
|||||||
*/
|
*/
|
||||||
down_read(&ctrl->reset_lock);
|
down_read(&ctrl->reset_lock);
|
||||||
if (events & DISABLE_SLOT)
|
if (events & DISABLE_SLOT)
|
||||||
pciehp_handle_disable_request(slot);
|
pciehp_handle_disable_request(ctrl);
|
||||||
else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC))
|
else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC))
|
||||||
pciehp_handle_presence_or_link_change(slot, events);
|
pciehp_handle_presence_or_link_change(ctrl, events);
|
||||||
up_read(&ctrl->reset_lock);
|
up_read(&ctrl->reset_lock);
|
||||||
|
|
||||||
pci_config_pm_runtime_put(pdev);
|
pci_config_pm_runtime_put(pdev);
|
||||||
@@ -748,6 +734,16 @@ void pcie_clear_hotplug_events(struct controller *ctrl)
|
|||||||
PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
|
PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pcie_enable_interrupt(struct controller *ctrl)
|
||||||
|
{
|
||||||
|
pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcie_disable_interrupt(struct controller *ctrl)
|
||||||
|
{
|
||||||
|
pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
|
* pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
|
||||||
* bus reset of the bridge, but at the same time we want to ensure that it is
|
* bus reset of the bridge, but at the same time we want to ensure that it is
|
||||||
@@ -756,9 +752,9 @@ void pcie_clear_hotplug_events(struct controller *ctrl)
|
|||||||
* momentarily, if we see that they could interfere. Also, clear any spurious
|
* momentarily, if we see that they could interfere. Also, clear any spurious
|
||||||
* events after.
|
* events after.
|
||||||
*/
|
*/
|
||||||
int pciehp_reset_slot(struct slot *slot, int probe)
|
int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
u16 stat_mask = 0, ctrl_mask = 0;
|
u16 stat_mask = 0, ctrl_mask = 0;
|
||||||
int rc;
|
int rc;
|
||||||
@@ -808,34 +804,6 @@ void pcie_shutdown_notification(struct controller *ctrl)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pcie_init_slot(struct controller *ctrl)
|
|
||||||
{
|
|
||||||
struct pci_bus *subordinate = ctrl_dev(ctrl)->subordinate;
|
|
||||||
struct slot *slot;
|
|
||||||
|
|
||||||
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
|
||||||
if (!slot)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
down_read(&pci_bus_sem);
|
|
||||||
slot->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE;
|
|
||||||
up_read(&pci_bus_sem);
|
|
||||||
|
|
||||||
slot->ctrl = ctrl;
|
|
||||||
mutex_init(&slot->lock);
|
|
||||||
INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
|
|
||||||
ctrl->slot = slot;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pcie_cleanup_slot(struct controller *ctrl)
|
|
||||||
{
|
|
||||||
struct slot *slot = ctrl->slot;
|
|
||||||
|
|
||||||
cancel_delayed_work_sync(&slot->work);
|
|
||||||
kfree(slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void dbg_ctrl(struct controller *ctrl)
|
static inline void dbg_ctrl(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = ctrl->pcie->port;
|
struct pci_dev *pdev = ctrl->pcie->port;
|
||||||
@@ -857,12 +825,13 @@ struct controller *pcie_init(struct pcie_device *dev)
|
|||||||
{
|
{
|
||||||
struct controller *ctrl;
|
struct controller *ctrl;
|
||||||
u32 slot_cap, link_cap;
|
u32 slot_cap, link_cap;
|
||||||
u8 occupied, poweron;
|
u8 poweron;
|
||||||
struct pci_dev *pdev = dev->port;
|
struct pci_dev *pdev = dev->port;
|
||||||
|
struct pci_bus *subordinate = pdev->subordinate;
|
||||||
|
|
||||||
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
|
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
|
||||||
if (!ctrl)
|
if (!ctrl)
|
||||||
goto abort;
|
return NULL;
|
||||||
|
|
||||||
ctrl->pcie = dev;
|
ctrl->pcie = dev;
|
||||||
pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
|
pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
|
||||||
@@ -879,15 +848,19 @@ struct controller *pcie_init(struct pcie_device *dev)
|
|||||||
|
|
||||||
ctrl->slot_cap = slot_cap;
|
ctrl->slot_cap = slot_cap;
|
||||||
mutex_init(&ctrl->ctrl_lock);
|
mutex_init(&ctrl->ctrl_lock);
|
||||||
|
mutex_init(&ctrl->state_lock);
|
||||||
init_rwsem(&ctrl->reset_lock);
|
init_rwsem(&ctrl->reset_lock);
|
||||||
init_waitqueue_head(&ctrl->requester);
|
init_waitqueue_head(&ctrl->requester);
|
||||||
init_waitqueue_head(&ctrl->queue);
|
init_waitqueue_head(&ctrl->queue);
|
||||||
|
INIT_DELAYED_WORK(&ctrl->button_work, pciehp_queue_pushbutton_work);
|
||||||
dbg_ctrl(ctrl);
|
dbg_ctrl(ctrl);
|
||||||
|
|
||||||
|
down_read(&pci_bus_sem);
|
||||||
|
ctrl->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE;
|
||||||
|
up_read(&pci_bus_sem);
|
||||||
|
|
||||||
/* Check if Data Link Layer Link Active Reporting is implemented */
|
/* Check if Data Link Layer Link Active Reporting is implemented */
|
||||||
pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
|
pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
|
||||||
if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
|
|
||||||
ctrl->link_active_reporting = 1;
|
|
||||||
|
|
||||||
/* Clear all remaining event bits in Slot Status register. */
|
/* Clear all remaining event bits in Slot Status register. */
|
||||||
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
|
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
|
||||||
@@ -909,33 +882,24 @@ struct controller *pcie_init(struct pcie_device *dev)
|
|||||||
FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC),
|
FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC),
|
||||||
pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : "");
|
pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : "");
|
||||||
|
|
||||||
if (pcie_init_slot(ctrl))
|
|
||||||
goto abort_ctrl;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If empty slot's power status is on, turn power off. The IRQ isn't
|
* If empty slot's power status is on, turn power off. The IRQ isn't
|
||||||
* requested yet, so avoid triggering a notification with this command.
|
* requested yet, so avoid triggering a notification with this command.
|
||||||
*/
|
*/
|
||||||
if (POWER_CTRL(ctrl)) {
|
if (POWER_CTRL(ctrl)) {
|
||||||
pciehp_get_adapter_status(ctrl->slot, &occupied);
|
pciehp_get_power_status(ctrl, &poweron);
|
||||||
pciehp_get_power_status(ctrl->slot, &poweron);
|
if (!pciehp_card_present_or_link_active(ctrl) && poweron) {
|
||||||
if (!occupied && poweron) {
|
|
||||||
pcie_disable_notification(ctrl);
|
pcie_disable_notification(ctrl);
|
||||||
pciehp_power_off_slot(ctrl->slot);
|
pciehp_power_off_slot(ctrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctrl;
|
return ctrl;
|
||||||
|
|
||||||
abort_ctrl:
|
|
||||||
kfree(ctrl);
|
|
||||||
abort:
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_release_ctrl(struct controller *ctrl)
|
void pciehp_release_ctrl(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
pcie_cleanup_slot(ctrl);
|
cancel_delayed_work_sync(&ctrl->button_work);
|
||||||
kfree(ctrl);
|
kfree(ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,20 +13,26 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include "../pci.h"
|
#include "../pci.h"
|
||||||
#include "pciehp.h"
|
#include "pciehp.h"
|
||||||
|
|
||||||
int pciehp_configure_device(struct slot *p_slot)
|
/**
|
||||||
|
* pciehp_configure_device() - enumerate PCI devices below a hotplug bridge
|
||||||
|
* @ctrl: PCIe hotplug controller
|
||||||
|
*
|
||||||
|
* Enumerate PCI devices below a hotplug bridge and add them to the system.
|
||||||
|
* Return 0 on success, %-EEXIST if the devices are already enumerated or
|
||||||
|
* %-ENODEV if enumeration failed.
|
||||||
|
*/
|
||||||
|
int pciehp_configure_device(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
struct pci_dev *bridge = p_slot->ctrl->pcie->port;
|
struct pci_dev *bridge = ctrl->pcie->port;
|
||||||
struct pci_bus *parent = bridge->subordinate;
|
struct pci_bus *parent = bridge->subordinate;
|
||||||
int num, ret = 0;
|
int num, ret = 0;
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
|
||||||
|
|
||||||
pci_lock_rescan_remove();
|
pci_lock_rescan_remove();
|
||||||
|
|
||||||
@@ -62,17 +68,28 @@ int pciehp_configure_device(struct slot *p_slot)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pciehp_unconfigure_device(struct slot *p_slot)
|
/**
|
||||||
|
* pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge
|
||||||
|
* @ctrl: PCIe hotplug controller
|
||||||
|
* @presence: whether the card is still present in the slot;
|
||||||
|
* true for safe removal via sysfs or an Attention Button press,
|
||||||
|
* false for surprise removal
|
||||||
|
*
|
||||||
|
* Unbind PCI devices below a hotplug bridge from their drivers and remove
|
||||||
|
* them from the system. Safely removed devices are quiesced. Surprise
|
||||||
|
* removed devices are marked as such to prevent further accesses.
|
||||||
|
*/
|
||||||
|
void pciehp_unconfigure_device(struct controller *ctrl, bool presence)
|
||||||
{
|
{
|
||||||
u8 presence = 0;
|
|
||||||
struct pci_dev *dev, *temp;
|
struct pci_dev *dev, *temp;
|
||||||
struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate;
|
struct pci_bus *parent = ctrl->pcie->port->subordinate;
|
||||||
u16 command;
|
u16 command;
|
||||||
struct controller *ctrl = p_slot->ctrl;
|
|
||||||
|
|
||||||
ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n",
|
ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n",
|
||||||
__func__, pci_domain_nr(parent), parent->number);
|
__func__, pci_domain_nr(parent), parent->number);
|
||||||
pciehp_get_adapter_status(p_slot, &presence);
|
|
||||||
|
if (!presence)
|
||||||
|
pci_walk_bus(parent, pci_dev_set_disconnected, NULL);
|
||||||
|
|
||||||
pci_lock_rescan_remove();
|
pci_lock_rescan_remove();
|
||||||
|
|
||||||
@@ -85,12 +102,6 @@ void pciehp_unconfigure_device(struct slot *p_slot)
|
|||||||
list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
|
list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
|
||||||
bus_list) {
|
bus_list) {
|
||||||
pci_dev_get(dev);
|
pci_dev_get(dev);
|
||||||
if (!presence) {
|
|
||||||
pci_dev_set_disconnected(dev, NULL);
|
|
||||||
if (pci_has_subordinate(dev))
|
|
||||||
pci_walk_bus(dev->subordinate,
|
|
||||||
pci_dev_set_disconnected, NULL);
|
|
||||||
}
|
|
||||||
pci_stop_and_remove_bus_device(dev);
|
pci_stop_and_remove_bus_device(dev);
|
||||||
/*
|
/*
|
||||||
* Ensure that no new Requests will be generated from
|
* Ensure that no new Requests will be generated from
|
||||||
|
|||||||
@@ -275,14 +275,13 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot)
|
|||||||
goto free_fdt1;
|
goto free_fdt1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fdt = kzalloc(fdt_totalsize(fdt1), GFP_KERNEL);
|
fdt = kmemdup(fdt1, fdt_totalsize(fdt1), GFP_KERNEL);
|
||||||
if (!fdt) {
|
if (!fdt) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto free_fdt1;
|
goto free_fdt1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unflatten device tree blob */
|
/* Unflatten device tree blob */
|
||||||
memcpy(fdt, fdt1, fdt_totalsize(fdt1));
|
|
||||||
dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL);
|
dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL);
|
||||||
if (!dt) {
|
if (!dt) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
@@ -328,10 +327,15 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct pnv_php_slot *to_pnv_php_slot(struct hotplug_slot *slot)
|
||||||
|
{
|
||||||
|
return container_of(slot, struct pnv_php_slot, slot);
|
||||||
|
}
|
||||||
|
|
||||||
int pnv_php_set_slot_power_state(struct hotplug_slot *slot,
|
int pnv_php_set_slot_power_state(struct hotplug_slot *slot,
|
||||||
uint8_t state)
|
uint8_t state)
|
||||||
{
|
{
|
||||||
struct pnv_php_slot *php_slot = slot->private;
|
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
|
||||||
struct opal_msg msg;
|
struct opal_msg msg;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -363,7 +367,7 @@ EXPORT_SYMBOL_GPL(pnv_php_set_slot_power_state);
|
|||||||
|
|
||||||
static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
|
static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
|
||||||
{
|
{
|
||||||
struct pnv_php_slot *php_slot = slot->private;
|
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
|
||||||
uint8_t power_state = OPAL_PCI_SLOT_POWER_ON;
|
uint8_t power_state = OPAL_PCI_SLOT_POWER_ON;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -378,7 +382,6 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
|
|||||||
ret);
|
ret);
|
||||||
} else {
|
} else {
|
||||||
*state = power_state;
|
*state = power_state;
|
||||||
slot->info->power_status = power_state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -386,7 +389,7 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
|
|||||||
|
|
||||||
static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
|
static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
|
||||||
{
|
{
|
||||||
struct pnv_php_slot *php_slot = slot->private;
|
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
|
||||||
uint8_t presence = OPAL_PCI_SLOT_EMPTY;
|
uint8_t presence = OPAL_PCI_SLOT_EMPTY;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -397,7 +400,6 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
|
|||||||
ret = pnv_pci_get_presence_state(php_slot->id, &presence);
|
ret = pnv_pci_get_presence_state(php_slot->id, &presence);
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
*state = presence;
|
*state = presence;
|
||||||
slot->info->adapter_status = presence;
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else {
|
} else {
|
||||||
pci_warn(php_slot->pdev, "Error %d getting presence\n", ret);
|
pci_warn(php_slot->pdev, "Error %d getting presence\n", ret);
|
||||||
@@ -406,10 +408,20 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pnv_php_get_attention_state(struct hotplug_slot *slot, u8 *state)
|
||||||
|
{
|
||||||
|
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
|
||||||
|
|
||||||
|
*state = php_slot->attention_state;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int pnv_php_set_attention_state(struct hotplug_slot *slot, u8 state)
|
static int pnv_php_set_attention_state(struct hotplug_slot *slot, u8 state)
|
||||||
{
|
{
|
||||||
|
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
|
||||||
|
|
||||||
/* FIXME: Make it real once firmware supports it */
|
/* FIXME: Make it real once firmware supports it */
|
||||||
slot->info->attention_status = state;
|
php_slot->attention_state = state;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -501,15 +513,14 @@ scan:
|
|||||||
|
|
||||||
static int pnv_php_enable_slot(struct hotplug_slot *slot)
|
static int pnv_php_enable_slot(struct hotplug_slot *slot)
|
||||||
{
|
{
|
||||||
struct pnv_php_slot *php_slot = container_of(slot,
|
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
|
||||||
struct pnv_php_slot, slot);
|
|
||||||
|
|
||||||
return pnv_php_enable(php_slot, true);
|
return pnv_php_enable(php_slot, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pnv_php_disable_slot(struct hotplug_slot *slot)
|
static int pnv_php_disable_slot(struct hotplug_slot *slot)
|
||||||
{
|
{
|
||||||
struct pnv_php_slot *php_slot = slot->private;
|
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (php_slot->state != PNV_PHP_STATE_POPULATED)
|
if (php_slot->state != PNV_PHP_STATE_POPULATED)
|
||||||
@@ -530,9 +541,10 @@ static int pnv_php_disable_slot(struct hotplug_slot *slot)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hotplug_slot_ops php_slot_ops = {
|
static const struct hotplug_slot_ops php_slot_ops = {
|
||||||
.get_power_status = pnv_php_get_power_state,
|
.get_power_status = pnv_php_get_power_state,
|
||||||
.get_adapter_status = pnv_php_get_adapter_state,
|
.get_adapter_status = pnv_php_get_adapter_state,
|
||||||
|
.get_attention_status = pnv_php_get_attention_state,
|
||||||
.set_attention_status = pnv_php_set_attention_state,
|
.set_attention_status = pnv_php_set_attention_state,
|
||||||
.enable_slot = pnv_php_enable_slot,
|
.enable_slot = pnv_php_enable_slot,
|
||||||
.disable_slot = pnv_php_disable_slot,
|
.disable_slot = pnv_php_disable_slot,
|
||||||
@@ -594,8 +606,6 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
|
|||||||
php_slot->id = id;
|
php_slot->id = id;
|
||||||
php_slot->power_state_check = false;
|
php_slot->power_state_check = false;
|
||||||
php_slot->slot.ops = &php_slot_ops;
|
php_slot->slot.ops = &php_slot_ops;
|
||||||
php_slot->slot.info = &php_slot->slot_info;
|
|
||||||
php_slot->slot.private = php_slot;
|
|
||||||
|
|
||||||
INIT_LIST_HEAD(&php_slot->children);
|
INIT_LIST_HEAD(&php_slot->children);
|
||||||
INIT_LIST_HEAD(&php_slot->link);
|
INIT_LIST_HEAD(&php_slot->link);
|
||||||
|
|||||||
@@ -63,16 +63,22 @@ struct slot {
|
|||||||
u32 index;
|
u32 index;
|
||||||
u32 type;
|
u32 type;
|
||||||
u32 power_domain;
|
u32 power_domain;
|
||||||
|
u8 attention_status;
|
||||||
char *name;
|
char *name;
|
||||||
struct device_node *dn;
|
struct device_node *dn;
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus;
|
||||||
struct list_head *pci_devs;
|
struct list_head *pci_devs;
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct hotplug_slot_ops rpaphp_hotplug_slot_ops;
|
extern const struct hotplug_slot_ops rpaphp_hotplug_slot_ops;
|
||||||
extern struct list_head rpaphp_slot_head;
|
extern struct list_head rpaphp_slot_head;
|
||||||
|
|
||||||
|
static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
|
||||||
|
{
|
||||||
|
return container_of(hotplug_slot, struct slot, hotplug_slot);
|
||||||
|
}
|
||||||
|
|
||||||
/* function prototypes */
|
/* function prototypes */
|
||||||
|
|
||||||
/* rpaphp_pci.c */
|
/* rpaphp_pci.c */
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ module_param_named(debug, rpaphp_debug, bool, 0644);
|
|||||||
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
|
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -66,7 +66,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
|
|||||||
|
|
||||||
rc = rtas_set_indicator(DR_INDICATOR, slot->index, value);
|
rc = rtas_set_indicator(DR_INDICATOR, slot->index, value);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
hotplug_slot->info->attention_status = value;
|
slot->attention_status = value;
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
|
|||||||
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
int retval, level;
|
int retval, level;
|
||||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
retval = rtas_get_power_level(slot->power_domain, &level);
|
retval = rtas_get_power_level(slot->power_domain, &level);
|
||||||
if (!retval)
|
if (!retval)
|
||||||
@@ -94,14 +94,14 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
*/
|
*/
|
||||||
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
*value = slot->hotplug_slot->info->attention_status;
|
*value = slot->attention_status;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
int rc, state;
|
int rc, state;
|
||||||
|
|
||||||
rc = rpaphp_get_sensor_state(slot, &state);
|
rc = rpaphp_get_sensor_state(slot, &state);
|
||||||
@@ -409,7 +409,7 @@ static void __exit cleanup_slots(void)
|
|||||||
list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
|
list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
|
||||||
rpaphp_slot_list) {
|
rpaphp_slot_list) {
|
||||||
list_del(&slot->rpaphp_slot_list);
|
list_del(&slot->rpaphp_slot_list);
|
||||||
pci_hp_deregister(slot->hotplug_slot);
|
pci_hp_deregister(&slot->hotplug_slot);
|
||||||
dealloc_slot_struct(slot);
|
dealloc_slot_struct(slot);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -434,7 +434,7 @@ static void __exit rpaphp_exit(void)
|
|||||||
|
|
||||||
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
int state;
|
int state;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
@@ -464,7 +464,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
|
|||||||
|
|
||||||
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = (struct slot *)hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
if (slot->state == NOT_CONFIGURED)
|
if (slot->state == NOT_CONFIGURED)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@@ -477,7 +477,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
|
const struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
|
||||||
.enable_slot = enable_slot,
|
.enable_slot = enable_slot,
|
||||||
.disable_slot = disable_slot,
|
.disable_slot = disable_slot,
|
||||||
.set_attention_status = set_attention_status,
|
.set_attention_status = set_attention_status,
|
||||||
|
|||||||
@@ -54,25 +54,21 @@ int rpaphp_get_sensor_state(struct slot *slot, int *state)
|
|||||||
* rpaphp_enable_slot - record slot state, config pci device
|
* rpaphp_enable_slot - record slot state, config pci device
|
||||||
* @slot: target &slot
|
* @slot: target &slot
|
||||||
*
|
*
|
||||||
* Initialize values in the slot, and the hotplug_slot info
|
* Initialize values in the slot structure to indicate if there is a pci card
|
||||||
* structures to indicate if there is a pci card plugged into
|
* plugged into the slot. If the slot is not empty, run the pcibios routine
|
||||||
* the slot. If the slot is not empty, run the pcibios routine
|
|
||||||
* to get pcibios stuff correctly set up.
|
* to get pcibios stuff correctly set up.
|
||||||
*/
|
*/
|
||||||
int rpaphp_enable_slot(struct slot *slot)
|
int rpaphp_enable_slot(struct slot *slot)
|
||||||
{
|
{
|
||||||
int rc, level, state;
|
int rc, level, state;
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus;
|
||||||
struct hotplug_slot_info *info = slot->hotplug_slot->info;
|
|
||||||
|
|
||||||
info->adapter_status = NOT_VALID;
|
|
||||||
slot->state = EMPTY;
|
slot->state = EMPTY;
|
||||||
|
|
||||||
/* Find out if the power is turned on for the slot */
|
/* Find out if the power is turned on for the slot */
|
||||||
rc = rtas_get_power_level(slot->power_domain, &level);
|
rc = rtas_get_power_level(slot->power_domain, &level);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
info->power_status = level;
|
|
||||||
|
|
||||||
/* Figure out if there is an adapter in the slot */
|
/* Figure out if there is an adapter in the slot */
|
||||||
rc = rpaphp_get_sensor_state(slot, &state);
|
rc = rpaphp_get_sensor_state(slot, &state);
|
||||||
@@ -85,13 +81,11 @@ int rpaphp_enable_slot(struct slot *slot)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->adapter_status = EMPTY;
|
|
||||||
slot->bus = bus;
|
slot->bus = bus;
|
||||||
slot->pci_devs = &bus->devices;
|
slot->pci_devs = &bus->devices;
|
||||||
|
|
||||||
/* if there's an adapter in the slot, go add the pci devices */
|
/* if there's an adapter in the slot, go add the pci devices */
|
||||||
if (state == PRESENT) {
|
if (state == PRESENT) {
|
||||||
info->adapter_status = NOT_CONFIGURED;
|
|
||||||
slot->state = NOT_CONFIGURED;
|
slot->state = NOT_CONFIGURED;
|
||||||
|
|
||||||
/* non-empty slot has to have child */
|
/* non-empty slot has to have child */
|
||||||
@@ -105,7 +99,6 @@ int rpaphp_enable_slot(struct slot *slot)
|
|||||||
pci_hp_add_devices(bus);
|
pci_hp_add_devices(bus);
|
||||||
|
|
||||||
if (!list_empty(&bus->devices)) {
|
if (!list_empty(&bus->devices)) {
|
||||||
info->adapter_status = CONFIGURED;
|
|
||||||
slot->state = CONFIGURED;
|
slot->state = CONFIGURED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,7 @@
|
|||||||
/* free up the memory used by a slot */
|
/* free up the memory used by a slot */
|
||||||
void dealloc_slot_struct(struct slot *slot)
|
void dealloc_slot_struct(struct slot *slot)
|
||||||
{
|
{
|
||||||
kfree(slot->hotplug_slot->info);
|
|
||||||
kfree(slot->name);
|
kfree(slot->name);
|
||||||
kfree(slot->hotplug_slot);
|
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,28 +33,16 @@ struct slot *alloc_slot_struct(struct device_node *dn,
|
|||||||
slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
|
slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
|
||||||
if (!slot)
|
if (!slot)
|
||||||
goto error_nomem;
|
goto error_nomem;
|
||||||
slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
|
|
||||||
if (!slot->hotplug_slot)
|
|
||||||
goto error_slot;
|
|
||||||
slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!slot->hotplug_slot->info)
|
|
||||||
goto error_hpslot;
|
|
||||||
slot->name = kstrdup(drc_name, GFP_KERNEL);
|
slot->name = kstrdup(drc_name, GFP_KERNEL);
|
||||||
if (!slot->name)
|
if (!slot->name)
|
||||||
goto error_info;
|
goto error_slot;
|
||||||
slot->dn = dn;
|
slot->dn = dn;
|
||||||
slot->index = drc_index;
|
slot->index = drc_index;
|
||||||
slot->power_domain = power_domain;
|
slot->power_domain = power_domain;
|
||||||
slot->hotplug_slot->private = slot;
|
slot->hotplug_slot.ops = &rpaphp_hotplug_slot_ops;
|
||||||
slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
|
|
||||||
|
|
||||||
return (slot);
|
return (slot);
|
||||||
|
|
||||||
error_info:
|
|
||||||
kfree(slot->hotplug_slot->info);
|
|
||||||
error_hpslot:
|
|
||||||
kfree(slot->hotplug_slot);
|
|
||||||
error_slot:
|
error_slot:
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
error_nomem:
|
error_nomem:
|
||||||
@@ -77,7 +63,7 @@ static int is_registered(struct slot *slot)
|
|||||||
int rpaphp_deregister_slot(struct slot *slot)
|
int rpaphp_deregister_slot(struct slot *slot)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
struct hotplug_slot *php_slot = slot->hotplug_slot;
|
struct hotplug_slot *php_slot = &slot->hotplug_slot;
|
||||||
|
|
||||||
dbg("%s - Entry: deregistering slot=%s\n",
|
dbg("%s - Entry: deregistering slot=%s\n",
|
||||||
__func__, slot->name);
|
__func__, slot->name);
|
||||||
@@ -93,7 +79,7 @@ EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
|
|||||||
|
|
||||||
int rpaphp_register_slot(struct slot *slot)
|
int rpaphp_register_slot(struct slot *slot)
|
||||||
{
|
{
|
||||||
struct hotplug_slot *php_slot = slot->hotplug_slot;
|
struct hotplug_slot *php_slot = &slot->hotplug_slot;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
u32 my_index;
|
u32 my_index;
|
||||||
int retval;
|
int retval;
|
||||||
|
|||||||
@@ -32,10 +32,15 @@ static int zpci_fn_configured(enum zpci_state state)
|
|||||||
*/
|
*/
|
||||||
struct slot {
|
struct slot {
|
||||||
struct list_head slot_list;
|
struct list_head slot_list;
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
struct zpci_dev *zdev;
|
struct zpci_dev *zdev;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
|
||||||
|
{
|
||||||
|
return container_of(hotplug_slot, struct slot, hotplug_slot);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int slot_configure(struct slot *slot)
|
static inline int slot_configure(struct slot *slot)
|
||||||
{
|
{
|
||||||
int ret = sclp_pci_configure(slot->zdev->fid);
|
int ret = sclp_pci_configure(slot->zdev->fid);
|
||||||
@@ -60,7 +65,7 @@ static inline int slot_deconfigure(struct slot *slot)
|
|||||||
|
|
||||||
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (slot->zdev->state != ZPCI_FN_STATE_STANDBY)
|
if (slot->zdev->state != ZPCI_FN_STATE_STANDBY)
|
||||||
@@ -88,7 +93,7 @@ out_deconfigure:
|
|||||||
|
|
||||||
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
static int disable_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
struct pci_dev *pdev;
|
struct pci_dev *pdev;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@@ -110,7 +115,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
|
|||||||
|
|
||||||
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = hotplug_slot->private;
|
struct slot *slot = to_slot(hotplug_slot);
|
||||||
|
|
||||||
switch (slot->zdev->state) {
|
switch (slot->zdev->state) {
|
||||||
case ZPCI_FN_STATE_STANDBY:
|
case ZPCI_FN_STATE_STANDBY:
|
||||||
@@ -130,7 +135,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hotplug_slot_ops s390_hotplug_slot_ops = {
|
static const struct hotplug_slot_ops s390_hotplug_slot_ops = {
|
||||||
.enable_slot = enable_slot,
|
.enable_slot = enable_slot,
|
||||||
.disable_slot = disable_slot,
|
.disable_slot = disable_slot,
|
||||||
.get_power_status = get_power_status,
|
.get_power_status = get_power_status,
|
||||||
@@ -139,8 +144,6 @@ static struct hotplug_slot_ops s390_hotplug_slot_ops = {
|
|||||||
|
|
||||||
int zpci_init_slot(struct zpci_dev *zdev)
|
int zpci_init_slot(struct zpci_dev *zdev)
|
||||||
{
|
{
|
||||||
struct hotplug_slot *hotplug_slot;
|
|
||||||
struct hotplug_slot_info *info;
|
|
||||||
char name[SLOT_NAME_SIZE];
|
char name[SLOT_NAME_SIZE];
|
||||||
struct slot *slot;
|
struct slot *slot;
|
||||||
int rc;
|
int rc;
|
||||||
@@ -152,26 +155,11 @@ int zpci_init_slot(struct zpci_dev *zdev)
|
|||||||
if (!slot)
|
if (!slot)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
|
|
||||||
if (!hotplug_slot)
|
|
||||||
goto error_hp;
|
|
||||||
hotplug_slot->private = slot;
|
|
||||||
|
|
||||||
slot->hotplug_slot = hotplug_slot;
|
|
||||||
slot->zdev = zdev;
|
slot->zdev = zdev;
|
||||||
|
slot->hotplug_slot.ops = &s390_hotplug_slot_ops;
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
|
||||||
if (!info)
|
|
||||||
goto error_info;
|
|
||||||
hotplug_slot->info = info;
|
|
||||||
|
|
||||||
hotplug_slot->ops = &s390_hotplug_slot_ops;
|
|
||||||
|
|
||||||
get_power_status(hotplug_slot, &info->power_status);
|
|
||||||
get_adapter_status(hotplug_slot, &info->adapter_status);
|
|
||||||
|
|
||||||
snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid);
|
snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid);
|
||||||
rc = pci_hp_register(slot->hotplug_slot, zdev->bus,
|
rc = pci_hp_register(&slot->hotplug_slot, zdev->bus,
|
||||||
ZPCI_DEVFN, name);
|
ZPCI_DEVFN, name);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto error_reg;
|
goto error_reg;
|
||||||
@@ -180,10 +168,6 @@ int zpci_init_slot(struct zpci_dev *zdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_reg:
|
error_reg:
|
||||||
kfree(info);
|
|
||||||
error_info:
|
|
||||||
kfree(hotplug_slot);
|
|
||||||
error_hp:
|
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
error:
|
error:
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@@ -198,9 +182,7 @@ void zpci_exit_slot(struct zpci_dev *zdev)
|
|||||||
if (slot->zdev != zdev)
|
if (slot->zdev != zdev)
|
||||||
continue;
|
continue;
|
||||||
list_del(&slot->slot_list);
|
list_del(&slot->slot_list);
|
||||||
pci_hp_deregister(slot->hotplug_slot);
|
pci_hp_deregister(&slot->hotplug_slot);
|
||||||
kfree(slot->hotplug_slot->info);
|
|
||||||
kfree(slot->hotplug_slot);
|
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ struct slot {
|
|||||||
int device_num;
|
int device_num;
|
||||||
struct pci_bus *pci_bus;
|
struct pci_bus *pci_bus;
|
||||||
/* this struct for glue internal only */
|
/* this struct for glue internal only */
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
struct list_head hp_list;
|
struct list_head hp_list;
|
||||||
char physical_path[SN_SLOT_NAME_SIZE];
|
char physical_path[SN_SLOT_NAME_SIZE];
|
||||||
};
|
};
|
||||||
@@ -80,7 +80,7 @@ static int enable_slot(struct hotplug_slot *slot);
|
|||||||
static int disable_slot(struct hotplug_slot *slot);
|
static int disable_slot(struct hotplug_slot *slot);
|
||||||
static inline int get_power_status(struct hotplug_slot *slot, u8 *value);
|
static inline int get_power_status(struct hotplug_slot *slot, u8 *value);
|
||||||
|
|
||||||
static struct hotplug_slot_ops sn_hotplug_slot_ops = {
|
static const struct hotplug_slot_ops sn_hotplug_slot_ops = {
|
||||||
.enable_slot = enable_slot,
|
.enable_slot = enable_slot,
|
||||||
.disable_slot = disable_slot,
|
.disable_slot = disable_slot,
|
||||||
.get_power_status = get_power_status,
|
.get_power_status = get_power_status,
|
||||||
@@ -88,10 +88,15 @@ static struct hotplug_slot_ops sn_hotplug_slot_ops = {
|
|||||||
|
|
||||||
static DEFINE_MUTEX(sn_hotplug_mutex);
|
static DEFINE_MUTEX(sn_hotplug_mutex);
|
||||||
|
|
||||||
|
static struct slot *to_slot(struct hotplug_slot *bss_hotplug_slot)
|
||||||
|
{
|
||||||
|
return container_of(bss_hotplug_slot, struct slot, hotplug_slot);
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t path_show(struct pci_slot *pci_slot, char *buf)
|
static ssize_t path_show(struct pci_slot *pci_slot, char *buf)
|
||||||
{
|
{
|
||||||
int retval = -ENOENT;
|
int retval = -ENOENT;
|
||||||
struct slot *slot = pci_slot->hotplug->private;
|
struct slot *slot = to_slot(pci_slot->hotplug);
|
||||||
|
|
||||||
if (!slot)
|
if (!slot)
|
||||||
return retval;
|
return retval;
|
||||||
@@ -156,7 +161,7 @@ static int sn_pci_bus_valid(struct pci_bus *pci_bus)
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
|
static int sn_hp_slot_private_alloc(struct hotplug_slot **bss_hotplug_slot,
|
||||||
struct pci_bus *pci_bus, int device,
|
struct pci_bus *pci_bus, int device,
|
||||||
char *name)
|
char *name)
|
||||||
{
|
{
|
||||||
@@ -168,7 +173,6 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
|
|||||||
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
||||||
if (!slot)
|
if (!slot)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
bss_hotplug_slot->private = slot;
|
|
||||||
|
|
||||||
slot->device_num = device;
|
slot->device_num = device;
|
||||||
slot->pci_bus = pci_bus;
|
slot->pci_bus = pci_bus;
|
||||||
@@ -179,8 +183,8 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
|
|||||||
|
|
||||||
sn_generate_path(pci_bus, slot->physical_path);
|
sn_generate_path(pci_bus, slot->physical_path);
|
||||||
|
|
||||||
slot->hotplug_slot = bss_hotplug_slot;
|
|
||||||
list_add(&slot->hp_list, &sn_hp_list);
|
list_add(&slot->hp_list, &sn_hp_list);
|
||||||
|
*bss_hotplug_slot = &slot->hotplug_slot;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -192,10 +196,9 @@ static struct hotplug_slot *sn_hp_destroy(void)
|
|||||||
struct hotplug_slot *bss_hotplug_slot = NULL;
|
struct hotplug_slot *bss_hotplug_slot = NULL;
|
||||||
|
|
||||||
list_for_each_entry(slot, &sn_hp_list, hp_list) {
|
list_for_each_entry(slot, &sn_hp_list, hp_list) {
|
||||||
bss_hotplug_slot = slot->hotplug_slot;
|
bss_hotplug_slot = &slot->hotplug_slot;
|
||||||
pci_slot = bss_hotplug_slot->pci_slot;
|
pci_slot = bss_hotplug_slot->pci_slot;
|
||||||
list_del(&((struct slot *)bss_hotplug_slot->private)->
|
list_del(&slot->hp_list);
|
||||||
hp_list);
|
|
||||||
sysfs_remove_file(&pci_slot->kobj,
|
sysfs_remove_file(&pci_slot->kobj,
|
||||||
&sn_slot_path_attr.attr);
|
&sn_slot_path_attr.attr);
|
||||||
break;
|
break;
|
||||||
@@ -227,7 +230,7 @@ static void sn_bus_free_data(struct pci_dev *dev)
|
|||||||
static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
|
static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
|
||||||
int device_num, char **ssdt)
|
int device_num, char **ssdt)
|
||||||
{
|
{
|
||||||
struct slot *slot = bss_hotplug_slot->private;
|
struct slot *slot = to_slot(bss_hotplug_slot);
|
||||||
struct pcibus_info *pcibus_info;
|
struct pcibus_info *pcibus_info;
|
||||||
struct pcibr_slot_enable_resp resp;
|
struct pcibr_slot_enable_resp resp;
|
||||||
int rc;
|
int rc;
|
||||||
@@ -267,7 +270,7 @@ static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
|
|||||||
static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
|
static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
|
||||||
int device_num, int action)
|
int device_num, int action)
|
||||||
{
|
{
|
||||||
struct slot *slot = bss_hotplug_slot->private;
|
struct slot *slot = to_slot(bss_hotplug_slot);
|
||||||
struct pcibus_info *pcibus_info;
|
struct pcibus_info *pcibus_info;
|
||||||
struct pcibr_slot_disable_resp resp;
|
struct pcibr_slot_disable_resp resp;
|
||||||
int rc;
|
int rc;
|
||||||
@@ -323,7 +326,7 @@ static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
|
|||||||
*/
|
*/
|
||||||
static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
|
static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = bss_hotplug_slot->private;
|
struct slot *slot = to_slot(bss_hotplug_slot);
|
||||||
struct pci_bus *new_bus = NULL;
|
struct pci_bus *new_bus = NULL;
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
int num_funcs;
|
int num_funcs;
|
||||||
@@ -469,7 +472,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
|
|||||||
|
|
||||||
static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
|
static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
|
||||||
{
|
{
|
||||||
struct slot *slot = bss_hotplug_slot->private;
|
struct slot *slot = to_slot(bss_hotplug_slot);
|
||||||
struct pci_dev *dev, *temp;
|
struct pci_dev *dev, *temp;
|
||||||
int rc;
|
int rc;
|
||||||
acpi_handle ssdt_hdl = NULL;
|
acpi_handle ssdt_hdl = NULL;
|
||||||
@@ -571,7 +574,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
|
|||||||
static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot,
|
static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot,
|
||||||
u8 *value)
|
u8 *value)
|
||||||
{
|
{
|
||||||
struct slot *slot = bss_hotplug_slot->private;
|
struct slot *slot = to_slot(bss_hotplug_slot);
|
||||||
struct pcibus_info *pcibus_info;
|
struct pcibus_info *pcibus_info;
|
||||||
u32 power;
|
u32 power;
|
||||||
|
|
||||||
@@ -585,9 +588,7 @@ static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot,
|
|||||||
|
|
||||||
static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
|
static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
|
||||||
{
|
{
|
||||||
kfree(bss_hotplug_slot->info);
|
kfree(to_slot(bss_hotplug_slot));
|
||||||
kfree(bss_hotplug_slot->private);
|
|
||||||
kfree(bss_hotplug_slot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
|
static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
|
||||||
@@ -607,22 +608,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
|
|||||||
if (sn_pci_slot_valid(pci_bus, device) != 1)
|
if (sn_pci_slot_valid(pci_bus, device) != 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bss_hotplug_slot = kzalloc(sizeof(*bss_hotplug_slot),
|
if (sn_hp_slot_private_alloc(&bss_hotplug_slot,
|
||||||
GFP_KERNEL);
|
|
||||||
if (!bss_hotplug_slot) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto alloc_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
bss_hotplug_slot->info =
|
|
||||||
kzalloc(sizeof(struct hotplug_slot_info),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!bss_hotplug_slot->info) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto alloc_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sn_hp_slot_private_alloc(bss_hotplug_slot,
|
|
||||||
pci_bus, device, name)) {
|
pci_bus, device, name)) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto alloc_err;
|
goto alloc_err;
|
||||||
@@ -637,7 +623,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
|
|||||||
rc = sysfs_create_file(&pci_slot->kobj,
|
rc = sysfs_create_file(&pci_slot->kobj,
|
||||||
&sn_slot_path_attr.attr);
|
&sn_slot_path_attr.attr);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto register_err;
|
goto alloc_err;
|
||||||
}
|
}
|
||||||
pci_dbg(pci_bus->self, "Registered bus with hotplug\n");
|
pci_dbg(pci_bus->self, "Registered bus with hotplug\n");
|
||||||
return rc;
|
return rc;
|
||||||
@@ -646,14 +632,11 @@ register_err:
|
|||||||
pci_dbg(pci_bus->self, "bus failed to register with err = %d\n",
|
pci_dbg(pci_bus->self, "bus failed to register with err = %d\n",
|
||||||
rc);
|
rc);
|
||||||
|
|
||||||
alloc_err:
|
|
||||||
if (rc == -ENOMEM)
|
|
||||||
pci_dbg(pci_bus->self, "Memory allocation error\n");
|
|
||||||
|
|
||||||
/* destroy THIS element */
|
/* destroy THIS element */
|
||||||
if (bss_hotplug_slot)
|
sn_hp_destroy();
|
||||||
sn_release_slot(bss_hotplug_slot);
|
sn_release_slot(bss_hotplug_slot);
|
||||||
|
|
||||||
|
alloc_err:
|
||||||
/* destroy anything else on the list */
|
/* destroy anything else on the list */
|
||||||
while ((bss_hotplug_slot = sn_hp_destroy())) {
|
while ((bss_hotplug_slot = sn_hp_destroy())) {
|
||||||
pci_hp_deregister(bss_hotplug_slot);
|
pci_hp_deregister(bss_hotplug_slot);
|
||||||
|
|||||||
@@ -67,11 +67,13 @@ struct slot {
|
|||||||
u32 number;
|
u32 number;
|
||||||
u8 is_a_board;
|
u8 is_a_board;
|
||||||
u8 state;
|
u8 state;
|
||||||
|
u8 attention_save;
|
||||||
u8 presence_save;
|
u8 presence_save;
|
||||||
|
u8 latch_save;
|
||||||
u8 pwr_save;
|
u8 pwr_save;
|
||||||
struct controller *ctrl;
|
struct controller *ctrl;
|
||||||
const struct hpc_ops *hpc_ops;
|
const struct hpc_ops *hpc_ops;
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
struct list_head slot_list;
|
struct list_head slot_list;
|
||||||
struct delayed_work work; /* work for button event */
|
struct delayed_work work; /* work for button event */
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
@@ -169,7 +171,7 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev);
|
|||||||
|
|
||||||
static inline const char *slot_name(struct slot *slot)
|
static inline const char *slot_name(struct slot *slot)
|
||||||
{
|
{
|
||||||
return hotplug_slot_name(slot->hotplug_slot);
|
return hotplug_slot_name(&slot->hotplug_slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ctrl_reg {
|
struct ctrl_reg {
|
||||||
@@ -207,7 +209,7 @@ enum ctrl_offsets {
|
|||||||
|
|
||||||
static inline struct slot *get_slot(struct hotplug_slot *hotplug_slot)
|
static inline struct slot *get_slot(struct hotplug_slot *hotplug_slot)
|
||||||
{
|
{
|
||||||
return hotplug_slot->private;
|
return container_of(hotplug_slot, struct slot, hotplug_slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct slot *shpchp_find_slot(struct controller *ctrl, u8 device)
|
static inline struct slot *shpchp_find_slot(struct controller *ctrl, u8 device)
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ 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_latch_status(struct hotplug_slot *slot, u8 *value);
|
||||||
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
|
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
|
||||||
|
|
||||||
static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
|
static const struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
|
||||||
.set_attention_status = set_attention_status,
|
.set_attention_status = set_attention_status,
|
||||||
.enable_slot = enable_slot,
|
.enable_slot = enable_slot,
|
||||||
.disable_slot = disable_slot,
|
.disable_slot = disable_slot,
|
||||||
@@ -65,7 +65,6 @@ static int init_slots(struct controller *ctrl)
|
|||||||
{
|
{
|
||||||
struct slot *slot;
|
struct slot *slot;
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot *hotplug_slot;
|
||||||
struct hotplug_slot_info *info;
|
|
||||||
char name[SLOT_NAME_SIZE];
|
char name[SLOT_NAME_SIZE];
|
||||||
int retval;
|
int retval;
|
||||||
int i;
|
int i;
|
||||||
@@ -77,19 +76,7 @@ static int init_slots(struct controller *ctrl)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
|
hotplug_slot = &slot->hotplug_slot;
|
||||||
if (!hotplug_slot) {
|
|
||||||
retval = -ENOMEM;
|
|
||||||
goto error_slot;
|
|
||||||
}
|
|
||||||
slot->hotplug_slot = hotplug_slot;
|
|
||||||
|
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
|
||||||
if (!info) {
|
|
||||||
retval = -ENOMEM;
|
|
||||||
goto error_hpslot;
|
|
||||||
}
|
|
||||||
hotplug_slot->info = info;
|
|
||||||
|
|
||||||
slot->hp_slot = i;
|
slot->hp_slot = i;
|
||||||
slot->ctrl = ctrl;
|
slot->ctrl = ctrl;
|
||||||
@@ -101,14 +88,13 @@ static int init_slots(struct controller *ctrl)
|
|||||||
slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);
|
slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);
|
||||||
if (!slot->wq) {
|
if (!slot->wq) {
|
||||||
retval = -ENOMEM;
|
retval = -ENOMEM;
|
||||||
goto error_info;
|
goto error_slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_init(&slot->lock);
|
mutex_init(&slot->lock);
|
||||||
INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
|
INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
|
||||||
|
|
||||||
/* register this slot with the hotplug pci core */
|
/* register this slot with the hotplug pci core */
|
||||||
hotplug_slot->private = slot;
|
|
||||||
snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
|
snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
|
||||||
hotplug_slot->ops = &shpchp_hotplug_slot_ops;
|
hotplug_slot->ops = &shpchp_hotplug_slot_ops;
|
||||||
|
|
||||||
@@ -116,7 +102,7 @@ static int init_slots(struct controller *ctrl)
|
|||||||
pci_domain_nr(ctrl->pci_dev->subordinate),
|
pci_domain_nr(ctrl->pci_dev->subordinate),
|
||||||
slot->bus, slot->device, slot->hp_slot, slot->number,
|
slot->bus, slot->device, slot->hp_slot, slot->number,
|
||||||
ctrl->slot_device_offset);
|
ctrl->slot_device_offset);
|
||||||
retval = pci_hp_register(slot->hotplug_slot,
|
retval = pci_hp_register(hotplug_slot,
|
||||||
ctrl->pci_dev->subordinate, slot->device, name);
|
ctrl->pci_dev->subordinate, slot->device, name);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
|
ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
|
||||||
@@ -124,10 +110,10 @@ static int init_slots(struct controller *ctrl)
|
|||||||
goto error_slotwq;
|
goto error_slotwq;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_power_status(hotplug_slot, &info->power_status);
|
get_power_status(hotplug_slot, &slot->pwr_save);
|
||||||
get_attention_status(hotplug_slot, &info->attention_status);
|
get_attention_status(hotplug_slot, &slot->attention_save);
|
||||||
get_latch_status(hotplug_slot, &info->latch_status);
|
get_latch_status(hotplug_slot, &slot->latch_save);
|
||||||
get_adapter_status(hotplug_slot, &info->adapter_status);
|
get_adapter_status(hotplug_slot, &slot->presence_save);
|
||||||
|
|
||||||
list_add(&slot->slot_list, &ctrl->slot_list);
|
list_add(&slot->slot_list, &ctrl->slot_list);
|
||||||
}
|
}
|
||||||
@@ -135,10 +121,6 @@ static int init_slots(struct controller *ctrl)
|
|||||||
return 0;
|
return 0;
|
||||||
error_slotwq:
|
error_slotwq:
|
||||||
destroy_workqueue(slot->wq);
|
destroy_workqueue(slot->wq);
|
||||||
error_info:
|
|
||||||
kfree(info);
|
|
||||||
error_hpslot:
|
|
||||||
kfree(hotplug_slot);
|
|
||||||
error_slot:
|
error_slot:
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
error:
|
error:
|
||||||
@@ -153,9 +135,7 @@ void cleanup_slots(struct controller *ctrl)
|
|||||||
list_del(&slot->slot_list);
|
list_del(&slot->slot_list);
|
||||||
cancel_delayed_work(&slot->work);
|
cancel_delayed_work(&slot->work);
|
||||||
destroy_workqueue(slot->wq);
|
destroy_workqueue(slot->wq);
|
||||||
pci_hp_deregister(slot->hotplug_slot);
|
pci_hp_deregister(&slot->hotplug_slot);
|
||||||
kfree(slot->hotplug_slot->info);
|
|
||||||
kfree(slot->hotplug_slot);
|
|
||||||
kfree(slot);
|
kfree(slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +150,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
|||||||
ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
|
ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
|
||||||
__func__, slot_name(slot));
|
__func__, slot_name(slot));
|
||||||
|
|
||||||
hotplug_slot->info->attention_status = status;
|
slot->attention_save = status;
|
||||||
slot->hpc_ops->set_attention_status(slot, status);
|
slot->hpc_ops->set_attention_status(slot, status);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -206,7 +186,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
|
|
||||||
retval = slot->hpc_ops->get_power_status(slot, value);
|
retval = slot->hpc_ops->get_power_status(slot, value);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
*value = hotplug_slot->info->power_status;
|
*value = slot->pwr_save;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -221,7 +201,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
|
|
||||||
retval = slot->hpc_ops->get_attention_status(slot, value);
|
retval = slot->hpc_ops->get_attention_status(slot, value);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
*value = hotplug_slot->info->attention_status;
|
*value = slot->attention_save;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -236,7 +216,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
|
|
||||||
retval = slot->hpc_ops->get_latch_status(slot, value);
|
retval = slot->hpc_ops->get_latch_status(slot, value);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
*value = hotplug_slot->info->latch_status;
|
*value = slot->latch_save;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -251,7 +231,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
|
|
||||||
retval = slot->hpc_ops->get_adapter_status(slot, value);
|
retval = slot->hpc_ops->get_adapter_status(slot, value);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
*value = hotplug_slot->info->adapter_status;
|
*value = slot->presence_save;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,23 +446,12 @@ void shpchp_queue_pushbutton_work(struct work_struct *work)
|
|||||||
mutex_unlock(&p_slot->lock);
|
mutex_unlock(&p_slot->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int update_slot_info (struct slot *slot)
|
static void update_slot_info(struct slot *slot)
|
||||||
{
|
{
|
||||||
struct hotplug_slot_info *info;
|
slot->hpc_ops->get_power_status(slot, &slot->pwr_save);
|
||||||
int result;
|
slot->hpc_ops->get_attention_status(slot, &slot->attention_save);
|
||||||
|
slot->hpc_ops->get_latch_status(slot, &slot->latch_save);
|
||||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
slot->hpc_ops->get_adapter_status(slot, &slot->presence_save);
|
||||||
if (!info)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
slot->hpc_ops->get_power_status(slot, &(info->power_status));
|
|
||||||
slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
|
|
||||||
slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
|
|
||||||
slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
|
|
||||||
|
|
||||||
result = pci_hp_change_slot_info(slot->hotplug_slot, info);
|
|
||||||
kfree (info);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -519,6 +519,46 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
|
|||||||
return PCI_POWER_ERROR;
|
return PCI_POWER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct acpi_device *acpi_pci_find_companion(struct device *dev);
|
||||||
|
|
||||||
|
static bool acpi_pci_bridge_d3(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
const struct fwnode_handle *fwnode;
|
||||||
|
struct acpi_device *adev;
|
||||||
|
struct pci_dev *root;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
if (!dev->is_hotplug_bridge)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for a special _DSD property for the root port and if it
|
||||||
|
* is set we know the hierarchy behind it supports D3 just fine.
|
||||||
|
*/
|
||||||
|
root = pci_find_pcie_root_port(dev);
|
||||||
|
if (!root)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
adev = ACPI_COMPANION(&root->dev);
|
||||||
|
if (root == dev) {
|
||||||
|
/*
|
||||||
|
* It is possible that the ACPI companion is not yet bound
|
||||||
|
* for the root port so look it up manually here.
|
||||||
|
*/
|
||||||
|
if (!adev && !pci_dev_is_added(root))
|
||||||
|
adev = acpi_pci_find_companion(&root->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!adev)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fwnode = acpi_fwnode_handle(adev);
|
||||||
|
if (fwnode_property_read_u8(fwnode, "HotPlugSupportInD3", &val))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return val == 1;
|
||||||
|
}
|
||||||
|
|
||||||
static bool acpi_pci_power_manageable(struct pci_dev *dev)
|
static bool acpi_pci_power_manageable(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
|
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
|
||||||
@@ -635,6 +675,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
|
static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
|
||||||
|
.bridge_d3 = acpi_pci_bridge_d3,
|
||||||
.is_manageable = acpi_pci_power_manageable,
|
.is_manageable = acpi_pci_power_manageable,
|
||||||
.set_state = acpi_pci_set_power_state,
|
.set_state = acpi_pci_set_power_state,
|
||||||
.get_state = acpi_pci_get_power_state,
|
.get_state = acpi_pci_get_power_state,
|
||||||
@@ -767,19 +808,33 @@ static void pci_acpi_setup(struct device *dev)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
device_set_wakeup_capable(dev, true);
|
device_set_wakeup_capable(dev, true);
|
||||||
|
/*
|
||||||
|
* For bridges that can do D3 we enable wake automatically (as
|
||||||
|
* we do for the power management itself in that case). The
|
||||||
|
* reason is that the bridge may have additional methods such as
|
||||||
|
* _DSW that need to be called.
|
||||||
|
*/
|
||||||
|
if (pci_dev->bridge_d3)
|
||||||
|
device_wakeup_enable(dev);
|
||||||
|
|
||||||
acpi_pci_wakeup(pci_dev, false);
|
acpi_pci_wakeup(pci_dev, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_acpi_cleanup(struct device *dev)
|
static void pci_acpi_cleanup(struct device *dev)
|
||||||
{
|
{
|
||||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||||
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||||
|
|
||||||
if (!adev)
|
if (!adev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pci_acpi_remove_pm_notifier(adev);
|
pci_acpi_remove_pm_notifier(adev);
|
||||||
if (adev->wakeup.flags.valid)
|
if (adev->wakeup.flags.valid) {
|
||||||
|
if (pci_dev->bridge_d3)
|
||||||
|
device_wakeup_disable(dev);
|
||||||
|
|
||||||
device_set_wakeup_capable(dev, false);
|
device_set_wakeup_capable(dev, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool pci_acpi_bus_match(struct device *dev)
|
static bool pci_acpi_bus_match(struct device *dev)
|
||||||
|
|||||||
@@ -35,6 +35,8 @@
|
|||||||
#include <linux/aer.h>
|
#include <linux/aer.h>
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
|
|
||||||
|
DEFINE_MUTEX(pci_slot_mutex);
|
||||||
|
|
||||||
const char *pci_power_names[] = {
|
const char *pci_power_names[] = {
|
||||||
"error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown",
|
"error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown",
|
||||||
};
|
};
|
||||||
@@ -791,6 +793,11 @@ static inline bool platform_pci_need_resume(struct pci_dev *dev)
|
|||||||
return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false;
|
return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool platform_pci_bridge_d3(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
return pci_platform_pm ? pci_platform_pm->bridge_d3(dev) : false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
|
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
|
||||||
* given PCI device
|
* given PCI device
|
||||||
@@ -1284,6 +1291,7 @@ int pci_save_state(struct pci_dev *dev)
|
|||||||
if (i != 0)
|
if (i != 0)
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
|
pci_save_dpc_state(dev);
|
||||||
return pci_save_vc_state(dev);
|
return pci_save_vc_state(dev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pci_save_state);
|
EXPORT_SYMBOL(pci_save_state);
|
||||||
@@ -1378,6 +1386,7 @@ void pci_restore_state(struct pci_dev *dev)
|
|||||||
pci_restore_ats_state(dev);
|
pci_restore_ats_state(dev);
|
||||||
pci_restore_vc_state(dev);
|
pci_restore_vc_state(dev);
|
||||||
pci_restore_rebar_state(dev);
|
pci_restore_rebar_state(dev);
|
||||||
|
pci_restore_dpc_state(dev);
|
||||||
|
|
||||||
pci_cleanup_aer_error_status_regs(dev);
|
pci_cleanup_aer_error_status_regs(dev);
|
||||||
|
|
||||||
@@ -2133,10 +2142,13 @@ static int __pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bridges can only signal wakeup on behalf of subordinate devices,
|
* Bridges that are not power-manageable directly only signal
|
||||||
* but that is set up elsewhere, so skip them.
|
* wakeup on behalf of subordinate devices which is set up
|
||||||
|
* elsewhere, so skip them. However, bridges that are
|
||||||
|
* power-manageable may signal wakeup for themselves (for example,
|
||||||
|
* on a hotplug event) and they need to be covered here.
|
||||||
*/
|
*/
|
||||||
if (pci_has_subordinate(dev))
|
if (!pci_power_manageable(dev))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Don't do the same thing twice in a row for one device. */
|
/* Don't do the same thing twice in a row for one device. */
|
||||||
@@ -2511,6 +2523,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
|
|||||||
if (bridge->is_thunderbolt)
|
if (bridge->is_thunderbolt)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
/* Platform might know better if the bridge supports D3 */
|
||||||
|
if (platform_pci_bridge_d3(bridge))
|
||||||
|
return true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hotplug ports handled natively by the OS were not validated
|
* Hotplug ports handled natively by the OS were not validated
|
||||||
* by vendors for runtime D3 at least until 2018 because there
|
* by vendors for runtime D3 at least until 2018 because there
|
||||||
@@ -4496,21 +4512,42 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active)
|
|||||||
bool ret;
|
bool ret;
|
||||||
u16 lnk_status;
|
u16 lnk_status;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some controllers might not implement link active reporting. In this
|
||||||
|
* case, we wait for 1000 + 100 ms.
|
||||||
|
*/
|
||||||
|
if (!pdev->link_active_reporting) {
|
||||||
|
msleep(1100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PCIe r4.0 sec 6.6.1, a component must enter LTSSM Detect within 20ms,
|
||||||
|
* after which we should expect an link active if the reset was
|
||||||
|
* successful. If so, software must wait a minimum 100ms before sending
|
||||||
|
* configuration requests to devices downstream this port.
|
||||||
|
*
|
||||||
|
* If the link fails to activate, either the device was physically
|
||||||
|
* removed or the link is permanently failed.
|
||||||
|
*/
|
||||||
|
if (active)
|
||||||
|
msleep(20);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
||||||
ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
|
ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
|
||||||
if (ret == active)
|
if (ret == active)
|
||||||
return true;
|
break;
|
||||||
if (timeout <= 0)
|
if (timeout <= 0)
|
||||||
break;
|
break;
|
||||||
msleep(10);
|
msleep(10);
|
||||||
timeout -= 10;
|
timeout -= 10;
|
||||||
}
|
}
|
||||||
|
if (active && ret)
|
||||||
pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n",
|
msleep(100);
|
||||||
active ? "set" : "cleared");
|
else if (ret != active)
|
||||||
|
pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n",
|
||||||
return false;
|
active ? "set" : "cleared");
|
||||||
|
return ret == active;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pci_reset_secondary_bus(struct pci_dev *dev)
|
void pci_reset_secondary_bus(struct pci_dev *dev)
|
||||||
@@ -4582,13 +4619,13 @@ static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, int probe)
|
|||||||
{
|
{
|
||||||
int rc = -ENOTTY;
|
int rc = -ENOTTY;
|
||||||
|
|
||||||
if (!hotplug || !try_module_get(hotplug->ops->owner))
|
if (!hotplug || !try_module_get(hotplug->owner))
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
if (hotplug->ops->reset_slot)
|
if (hotplug->ops->reset_slot)
|
||||||
rc = hotplug->ops->reset_slot(hotplug, probe);
|
rc = hotplug->ops->reset_slot(hotplug, probe);
|
||||||
|
|
||||||
module_put(hotplug->ops->owner);
|
module_put(hotplug->owner);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -5164,6 +5201,41 @@ static int pci_bus_reset(struct pci_bus *bus, int probe)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_bus_error_reset - reset the bridge's subordinate bus
|
||||||
|
* @bridge: The parent device that connects to the bus to reset
|
||||||
|
*
|
||||||
|
* This function will first try to reset the slots on this bus if the method is
|
||||||
|
* available. If slot reset fails or is not available, this will fall back to a
|
||||||
|
* secondary bus reset.
|
||||||
|
*/
|
||||||
|
int pci_bus_error_reset(struct pci_dev *bridge)
|
||||||
|
{
|
||||||
|
struct pci_bus *bus = bridge->subordinate;
|
||||||
|
struct pci_slot *slot;
|
||||||
|
|
||||||
|
if (!bus)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
mutex_lock(&pci_slot_mutex);
|
||||||
|
if (list_empty(&bus->slots))
|
||||||
|
goto bus_reset;
|
||||||
|
|
||||||
|
list_for_each_entry(slot, &bus->slots, list)
|
||||||
|
if (pci_probe_reset_slot(slot))
|
||||||
|
goto bus_reset;
|
||||||
|
|
||||||
|
list_for_each_entry(slot, &bus->slots, list)
|
||||||
|
if (pci_slot_reset(slot, 0))
|
||||||
|
goto bus_reset;
|
||||||
|
|
||||||
|
mutex_unlock(&pci_slot_mutex);
|
||||||
|
return 0;
|
||||||
|
bus_reset:
|
||||||
|
mutex_unlock(&pci_slot_mutex);
|
||||||
|
return pci_bus_reset(bridge->subordinate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pci_probe_reset_bus - probe whether a PCI bus can be reset
|
* pci_probe_reset_bus - probe whether a PCI bus can be reset
|
||||||
* @bus: PCI bus to probe
|
* @bus: PCI bus to probe
|
||||||
|
|||||||
@@ -35,10 +35,13 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
|
|||||||
|
|
||||||
int pci_probe_reset_function(struct pci_dev *dev);
|
int pci_probe_reset_function(struct pci_dev *dev);
|
||||||
int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
|
int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
|
||||||
|
int pci_bus_error_reset(struct pci_dev *dev);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct pci_platform_pm_ops - Firmware PM callbacks
|
* struct pci_platform_pm_ops - Firmware PM callbacks
|
||||||
*
|
*
|
||||||
|
* @bridge_d3: Does the bridge allow entering into D3
|
||||||
|
*
|
||||||
* @is_manageable: returns 'true' if given device is power manageable by the
|
* @is_manageable: returns 'true' if given device is power manageable by the
|
||||||
* platform firmware
|
* platform firmware
|
||||||
*
|
*
|
||||||
@@ -60,6 +63,7 @@ int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
|
|||||||
* these callbacks are mandatory.
|
* these callbacks are mandatory.
|
||||||
*/
|
*/
|
||||||
struct pci_platform_pm_ops {
|
struct pci_platform_pm_ops {
|
||||||
|
bool (*bridge_d3)(struct pci_dev *dev);
|
||||||
bool (*is_manageable)(struct pci_dev *dev);
|
bool (*is_manageable)(struct pci_dev *dev);
|
||||||
int (*set_state)(struct pci_dev *dev, pci_power_t state);
|
int (*set_state)(struct pci_dev *dev, pci_power_t state);
|
||||||
pci_power_t (*get_state)(struct pci_dev *dev);
|
pci_power_t (*get_state)(struct pci_dev *dev);
|
||||||
@@ -136,6 +140,7 @@ static inline void pci_remove_legacy_files(struct pci_bus *bus) { return; }
|
|||||||
|
|
||||||
/* Lock for read/write access to pci device and bus lists */
|
/* Lock for read/write access to pci device and bus lists */
|
||||||
extern struct rw_semaphore pci_bus_sem;
|
extern struct rw_semaphore pci_bus_sem;
|
||||||
|
extern struct mutex pci_slot_mutex;
|
||||||
|
|
||||||
extern raw_spinlock_t pci_lock;
|
extern raw_spinlock_t pci_lock;
|
||||||
|
|
||||||
@@ -293,21 +298,71 @@ struct pci_sriov {
|
|||||||
bool drivers_autoprobe; /* Auto probing of VFs by driver */
|
bool drivers_autoprobe; /* Auto probing of VFs by driver */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* pci_dev priv_flags */
|
/**
|
||||||
#define PCI_DEV_DISCONNECTED 0
|
* pci_dev_set_io_state - Set the new error state if possible.
|
||||||
#define PCI_DEV_ADDED 1
|
*
|
||||||
|
* @dev - pci device to set new error_state
|
||||||
|
* @new - the state we want dev to be in
|
||||||
|
*
|
||||||
|
* Must be called with device_lock held.
|
||||||
|
*
|
||||||
|
* Returns true if state has been changed to the requested state.
|
||||||
|
*/
|
||||||
|
static inline bool pci_dev_set_io_state(struct pci_dev *dev,
|
||||||
|
pci_channel_state_t new)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
device_lock_assert(&dev->dev);
|
||||||
|
switch (new) {
|
||||||
|
case pci_channel_io_perm_failure:
|
||||||
|
switch (dev->error_state) {
|
||||||
|
case pci_channel_io_frozen:
|
||||||
|
case pci_channel_io_normal:
|
||||||
|
case pci_channel_io_perm_failure:
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case pci_channel_io_frozen:
|
||||||
|
switch (dev->error_state) {
|
||||||
|
case pci_channel_io_frozen:
|
||||||
|
case pci_channel_io_normal:
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case pci_channel_io_normal:
|
||||||
|
switch (dev->error_state) {
|
||||||
|
case pci_channel_io_frozen:
|
||||||
|
case pci_channel_io_normal:
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (changed)
|
||||||
|
dev->error_state = new;
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused)
|
static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused)
|
||||||
{
|
{
|
||||||
set_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags);
|
device_lock(&dev->dev);
|
||||||
|
pci_dev_set_io_state(dev, pci_channel_io_perm_failure);
|
||||||
|
device_unlock(&dev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool pci_dev_is_disconnected(const struct pci_dev *dev)
|
static inline bool pci_dev_is_disconnected(const struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
return test_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags);
|
return dev->error_state == pci_channel_io_perm_failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* pci_dev priv_flags */
|
||||||
|
#define PCI_DEV_ADDED 0
|
||||||
|
|
||||||
static inline void pci_dev_assign_added(struct pci_dev *dev, bool added)
|
static inline void pci_dev_assign_added(struct pci_dev *dev, bool added)
|
||||||
{
|
{
|
||||||
assign_bit(PCI_DEV_ADDED, &dev->priv_flags, added);
|
assign_bit(PCI_DEV_ADDED, &dev->priv_flags, added);
|
||||||
@@ -346,6 +401,14 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
|
|||||||
void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
|
void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
|
||||||
#endif /* CONFIG_PCIEAER */
|
#endif /* CONFIG_PCIEAER */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCIE_DPC
|
||||||
|
void pci_save_dpc_state(struct pci_dev *dev);
|
||||||
|
void pci_restore_dpc_state(struct pci_dev *dev);
|
||||||
|
#else
|
||||||
|
static inline void pci_save_dpc_state(struct pci_dev *dev) {}
|
||||||
|
static inline void pci_restore_dpc_state(struct pci_dev *dev) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PCI_ATS
|
#ifdef CONFIG_PCI_ATS
|
||||||
void pci_restore_ats_state(struct pci_dev *dev);
|
void pci_restore_ats_state(struct pci_dev *dev);
|
||||||
#else
|
#else
|
||||||
@@ -423,8 +486,8 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* PCI error reporting and recovery */
|
/* PCI error reporting and recovery */
|
||||||
void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service);
|
void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state,
|
||||||
void pcie_do_nonfatal_recovery(struct pci_dev *dev);
|
u32 service);
|
||||||
|
|
||||||
bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
|
bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
|
||||||
#ifdef CONFIG_PCIEASPM
|
#ifdef CONFIG_PCIEASPM
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
#include "../pci.h"
|
#include "../pci.h"
|
||||||
#include "portdrv.h"
|
#include "portdrv.h"
|
||||||
|
|
||||||
#define AER_ERROR_SOURCES_MAX 100
|
#define AER_ERROR_SOURCES_MAX 128
|
||||||
|
|
||||||
#define AER_MAX_TYPEOF_COR_ERRS 16 /* as per PCI_ERR_COR_STATUS */
|
#define AER_MAX_TYPEOF_COR_ERRS 16 /* as per PCI_ERR_COR_STATUS */
|
||||||
#define AER_MAX_TYPEOF_UNCOR_ERRS 26 /* as per PCI_ERR_UNCOR_STATUS*/
|
#define AER_MAX_TYPEOF_UNCOR_ERRS 26 /* as per PCI_ERR_UNCOR_STATUS*/
|
||||||
@@ -42,21 +42,7 @@ struct aer_err_source {
|
|||||||
|
|
||||||
struct aer_rpc {
|
struct aer_rpc {
|
||||||
struct pci_dev *rpd; /* Root Port device */
|
struct pci_dev *rpd; /* Root Port device */
|
||||||
struct work_struct dpc_handler;
|
DECLARE_KFIFO(aer_fifo, struct aer_err_source, AER_ERROR_SOURCES_MAX);
|
||||||
struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX];
|
|
||||||
struct aer_err_info e_info;
|
|
||||||
unsigned short prod_idx; /* Error Producer Index */
|
|
||||||
unsigned short cons_idx; /* Error Consumer Index */
|
|
||||||
int isr;
|
|
||||||
spinlock_t e_lock; /*
|
|
||||||
* Lock access to Error Status/ID Regs
|
|
||||||
* and error producer/consumer index
|
|
||||||
*/
|
|
||||||
struct mutex rpc_mutex; /*
|
|
||||||
* only one thread could do
|
|
||||||
* recovery on the same
|
|
||||||
* root port hierarchy
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* AER stats for the device */
|
/* AER stats for the device */
|
||||||
@@ -866,7 +852,7 @@ void cper_print_aer(struct pci_dev *dev, int aer_severity,
|
|||||||
static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
|
static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) {
|
if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) {
|
||||||
e_info->dev[e_info->error_dev_num] = dev;
|
e_info->dev[e_info->error_dev_num] = pci_dev_get(dev);
|
||||||
e_info->error_dev_num++;
|
e_info->error_dev_num++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1010,9 +996,12 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
|
|||||||
info->status);
|
info->status);
|
||||||
pci_aer_clear_device_status(dev);
|
pci_aer_clear_device_status(dev);
|
||||||
} else if (info->severity == AER_NONFATAL)
|
} else if (info->severity == AER_NONFATAL)
|
||||||
pcie_do_nonfatal_recovery(dev);
|
pcie_do_recovery(dev, pci_channel_io_normal,
|
||||||
|
PCIE_PORT_SERVICE_AER);
|
||||||
else if (info->severity == AER_FATAL)
|
else if (info->severity == AER_FATAL)
|
||||||
pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER);
|
pcie_do_recovery(dev, pci_channel_io_frozen,
|
||||||
|
PCIE_PORT_SERVICE_AER);
|
||||||
|
pci_dev_put(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI_APEI_PCIEAER
|
#ifdef CONFIG_ACPI_APEI_PCIEAER
|
||||||
@@ -1047,9 +1036,11 @@ static void aer_recover_work_func(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
cper_print_aer(pdev, entry.severity, entry.regs);
|
cper_print_aer(pdev, entry.severity, entry.regs);
|
||||||
if (entry.severity == AER_NONFATAL)
|
if (entry.severity == AER_NONFATAL)
|
||||||
pcie_do_nonfatal_recovery(pdev);
|
pcie_do_recovery(pdev, pci_channel_io_normal,
|
||||||
|
PCIE_PORT_SERVICE_AER);
|
||||||
else if (entry.severity == AER_FATAL)
|
else if (entry.severity == AER_FATAL)
|
||||||
pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_AER);
|
pcie_do_recovery(pdev, pci_channel_io_frozen,
|
||||||
|
PCIE_PORT_SERVICE_AER);
|
||||||
pci_dev_put(pdev);
|
pci_dev_put(pdev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1065,7 +1056,6 @@ static DECLARE_WORK(aer_recover_work, aer_recover_work_func);
|
|||||||
void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
|
void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
|
||||||
int severity, struct aer_capability_regs *aer_regs)
|
int severity, struct aer_capability_regs *aer_regs)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
struct aer_recover_entry entry = {
|
struct aer_recover_entry entry = {
|
||||||
.bus = bus,
|
.bus = bus,
|
||||||
.devfn = devfn,
|
.devfn = devfn,
|
||||||
@@ -1074,13 +1064,12 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
|
|||||||
.regs = aer_regs,
|
.regs = aer_regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
spin_lock_irqsave(&aer_recover_ring_lock, flags);
|
if (kfifo_in_spinlocked(&aer_recover_ring, &entry, sizeof(entry),
|
||||||
if (kfifo_put(&aer_recover_ring, entry))
|
&aer_recover_ring_lock))
|
||||||
schedule_work(&aer_recover_work);
|
schedule_work(&aer_recover_work);
|
||||||
else
|
else
|
||||||
pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n",
|
pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n",
|
||||||
domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||||
spin_unlock_irqrestore(&aer_recover_ring_lock, flags);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(aer_recover_queue);
|
EXPORT_SYMBOL_GPL(aer_recover_queue);
|
||||||
#endif
|
#endif
|
||||||
@@ -1115,8 +1104,9 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
|
|||||||
&info->mask);
|
&info->mask);
|
||||||
if (!(info->status & ~info->mask))
|
if (!(info->status & ~info->mask))
|
||||||
return 0;
|
return 0;
|
||||||
} else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
|
} else if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
|
||||||
info->severity == AER_NONFATAL) {
|
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
|
||||||
|
info->severity == AER_NONFATAL) {
|
||||||
|
|
||||||
/* Link is still healthy for IO reads */
|
/* Link is still healthy for IO reads */
|
||||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
|
||||||
@@ -1170,7 +1160,7 @@ static void aer_isr_one_error(struct aer_rpc *rpc,
|
|||||||
struct aer_err_source *e_src)
|
struct aer_err_source *e_src)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = rpc->rpd;
|
struct pci_dev *pdev = rpc->rpd;
|
||||||
struct aer_err_info *e_info = &rpc->e_info;
|
struct aer_err_info e_info;
|
||||||
|
|
||||||
pci_rootport_aer_stats_incr(pdev, e_src);
|
pci_rootport_aer_stats_incr(pdev, e_src);
|
||||||
|
|
||||||
@@ -1179,83 +1169,57 @@ static void aer_isr_one_error(struct aer_rpc *rpc,
|
|||||||
* uncorrectable error being logged. Report correctable error first.
|
* uncorrectable error being logged. Report correctable error first.
|
||||||
*/
|
*/
|
||||||
if (e_src->status & PCI_ERR_ROOT_COR_RCV) {
|
if (e_src->status & PCI_ERR_ROOT_COR_RCV) {
|
||||||
e_info->id = ERR_COR_ID(e_src->id);
|
e_info.id = ERR_COR_ID(e_src->id);
|
||||||
e_info->severity = AER_CORRECTABLE;
|
e_info.severity = AER_CORRECTABLE;
|
||||||
|
|
||||||
if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV)
|
if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV)
|
||||||
e_info->multi_error_valid = 1;
|
e_info.multi_error_valid = 1;
|
||||||
else
|
else
|
||||||
e_info->multi_error_valid = 0;
|
e_info.multi_error_valid = 0;
|
||||||
aer_print_port_info(pdev, e_info);
|
aer_print_port_info(pdev, &e_info);
|
||||||
|
|
||||||
if (find_source_device(pdev, e_info))
|
if (find_source_device(pdev, &e_info))
|
||||||
aer_process_err_devices(e_info);
|
aer_process_err_devices(&e_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
|
if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
|
||||||
e_info->id = ERR_UNCOR_ID(e_src->id);
|
e_info.id = ERR_UNCOR_ID(e_src->id);
|
||||||
|
|
||||||
if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
|
if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
|
||||||
e_info->severity = AER_FATAL;
|
e_info.severity = AER_FATAL;
|
||||||
else
|
else
|
||||||
e_info->severity = AER_NONFATAL;
|
e_info.severity = AER_NONFATAL;
|
||||||
|
|
||||||
if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)
|
if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)
|
||||||
e_info->multi_error_valid = 1;
|
e_info.multi_error_valid = 1;
|
||||||
else
|
else
|
||||||
e_info->multi_error_valid = 0;
|
e_info.multi_error_valid = 0;
|
||||||
|
|
||||||
aer_print_port_info(pdev, e_info);
|
aer_print_port_info(pdev, &e_info);
|
||||||
|
|
||||||
if (find_source_device(pdev, e_info))
|
if (find_source_device(pdev, &e_info))
|
||||||
aer_process_err_devices(e_info);
|
aer_process_err_devices(&e_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* get_e_source - retrieve an error source
|
|
||||||
* @rpc: pointer to the root port which holds an error
|
|
||||||
* @e_src: pointer to store retrieved error source
|
|
||||||
*
|
|
||||||
* Return 1 if an error source is retrieved, otherwise 0.
|
|
||||||
*
|
|
||||||
* Invoked by DPC handler to consume an error.
|
|
||||||
*/
|
|
||||||
static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
/* Lock access to Root error producer/consumer index */
|
|
||||||
spin_lock_irqsave(&rpc->e_lock, flags);
|
|
||||||
if (rpc->prod_idx == rpc->cons_idx) {
|
|
||||||
spin_unlock_irqrestore(&rpc->e_lock, flags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*e_src = rpc->e_sources[rpc->cons_idx];
|
|
||||||
rpc->cons_idx++;
|
|
||||||
if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
|
|
||||||
rpc->cons_idx = 0;
|
|
||||||
spin_unlock_irqrestore(&rpc->e_lock, flags);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* aer_isr - consume errors detected by root port
|
* aer_isr - consume errors detected by root port
|
||||||
* @work: definition of this work item
|
* @work: definition of this work item
|
||||||
*
|
*
|
||||||
* Invoked, as DPC, when root port records new detected error
|
* Invoked, as DPC, when root port records new detected error
|
||||||
*/
|
*/
|
||||||
static void aer_isr(struct work_struct *work)
|
static irqreturn_t aer_isr(int irq, void *context)
|
||||||
{
|
{
|
||||||
struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
|
struct pcie_device *dev = (struct pcie_device *)context;
|
||||||
|
struct aer_rpc *rpc = get_service_data(dev);
|
||||||
struct aer_err_source uninitialized_var(e_src);
|
struct aer_err_source uninitialized_var(e_src);
|
||||||
|
|
||||||
mutex_lock(&rpc->rpc_mutex);
|
if (kfifo_is_empty(&rpc->aer_fifo))
|
||||||
while (get_e_source(rpc, &e_src))
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
while (kfifo_get(&rpc->aer_fifo, &e_src))
|
||||||
aer_isr_one_error(rpc, &e_src);
|
aer_isr_one_error(rpc, &e_src);
|
||||||
mutex_unlock(&rpc->rpc_mutex);
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1265,56 +1229,26 @@ static void aer_isr(struct work_struct *work)
|
|||||||
*
|
*
|
||||||
* Invoked when Root Port detects AER messages.
|
* Invoked when Root Port detects AER messages.
|
||||||
*/
|
*/
|
||||||
irqreturn_t aer_irq(int irq, void *context)
|
static irqreturn_t aer_irq(int irq, void *context)
|
||||||
{
|
{
|
||||||
unsigned int status, id;
|
|
||||||
struct pcie_device *pdev = (struct pcie_device *)context;
|
struct pcie_device *pdev = (struct pcie_device *)context;
|
||||||
struct aer_rpc *rpc = get_service_data(pdev);
|
struct aer_rpc *rpc = get_service_data(pdev);
|
||||||
int next_prod_idx;
|
struct pci_dev *rp = rpc->rpd;
|
||||||
unsigned long flags;
|
struct aer_err_source e_src = {};
|
||||||
int pos;
|
int pos = rp->aer_cap;
|
||||||
|
|
||||||
pos = pdev->port->aer_cap;
|
pci_read_config_dword(rp, pos + PCI_ERR_ROOT_STATUS, &e_src.status);
|
||||||
/*
|
if (!(e_src.status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV)))
|
||||||
* Must lock access to Root Error Status Reg, Root Error ID Reg,
|
|
||||||
* and Root error producer/consumer index
|
|
||||||
*/
|
|
||||||
spin_lock_irqsave(&rpc->e_lock, flags);
|
|
||||||
|
|
||||||
/* Read error status */
|
|
||||||
pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
|
|
||||||
if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) {
|
|
||||||
spin_unlock_irqrestore(&rpc->e_lock, flags);
|
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
}
|
|
||||||
|
|
||||||
/* Read error source and clear error status */
|
pci_read_config_dword(rp, pos + PCI_ERR_ROOT_ERR_SRC, &e_src.id);
|
||||||
pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id);
|
pci_write_config_dword(rp, pos + PCI_ERR_ROOT_STATUS, e_src.status);
|
||||||
pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
|
|
||||||
|
|
||||||
/* Store error source for later DPC handler */
|
if (!kfifo_put(&rpc->aer_fifo, e_src))
|
||||||
next_prod_idx = rpc->prod_idx + 1;
|
|
||||||
if (next_prod_idx == AER_ERROR_SOURCES_MAX)
|
|
||||||
next_prod_idx = 0;
|
|
||||||
if (next_prod_idx == rpc->cons_idx) {
|
|
||||||
/*
|
|
||||||
* Error Storm Condition - possibly the same error occurred.
|
|
||||||
* Drop the error.
|
|
||||||
*/
|
|
||||||
spin_unlock_irqrestore(&rpc->e_lock, flags);
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
|
||||||
rpc->e_sources[rpc->prod_idx].status = status;
|
|
||||||
rpc->e_sources[rpc->prod_idx].id = id;
|
|
||||||
rpc->prod_idx = next_prod_idx;
|
|
||||||
spin_unlock_irqrestore(&rpc->e_lock, flags);
|
|
||||||
|
|
||||||
/* Invoke DPC handler */
|
return IRQ_WAKE_THREAD;
|
||||||
schedule_work(&rpc->dpc_handler);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(aer_irq);
|
|
||||||
|
|
||||||
static int set_device_error_reporting(struct pci_dev *dev, void *data)
|
static int set_device_error_reporting(struct pci_dev *dev, void *data)
|
||||||
{
|
{
|
||||||
@@ -1422,33 +1356,6 @@ static void aer_disable_rootport(struct aer_rpc *rpc)
|
|||||||
pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
|
pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* aer_alloc_rpc - allocate Root Port data structure
|
|
||||||
* @dev: pointer to the pcie_dev data structure
|
|
||||||
*
|
|
||||||
* Invoked when Root Port's AER service is loaded.
|
|
||||||
*/
|
|
||||||
static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
|
|
||||||
{
|
|
||||||
struct aer_rpc *rpc;
|
|
||||||
|
|
||||||
rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL);
|
|
||||||
if (!rpc)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Initialize Root lock access, e_lock, to Root Error Status Reg */
|
|
||||||
spin_lock_init(&rpc->e_lock);
|
|
||||||
|
|
||||||
rpc->rpd = dev->port;
|
|
||||||
INIT_WORK(&rpc->dpc_handler, aer_isr);
|
|
||||||
mutex_init(&rpc->rpc_mutex);
|
|
||||||
|
|
||||||
/* Use PCIe bus function to store rpc into PCIe device */
|
|
||||||
set_service_data(dev, rpc);
|
|
||||||
|
|
||||||
return rpc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* aer_remove - clean up resources
|
* aer_remove - clean up resources
|
||||||
* @dev: pointer to the pcie_dev data structure
|
* @dev: pointer to the pcie_dev data structure
|
||||||
@@ -1459,16 +1366,7 @@ static void aer_remove(struct pcie_device *dev)
|
|||||||
{
|
{
|
||||||
struct aer_rpc *rpc = get_service_data(dev);
|
struct aer_rpc *rpc = get_service_data(dev);
|
||||||
|
|
||||||
if (rpc) {
|
aer_disable_rootport(rpc);
|
||||||
/* If register interrupt service, it must be free. */
|
|
||||||
if (rpc->isr)
|
|
||||||
free_irq(dev->irq, dev);
|
|
||||||
|
|
||||||
flush_work(&rpc->dpc_handler);
|
|
||||||
aer_disable_rootport(rpc);
|
|
||||||
kfree(rpc);
|
|
||||||
set_service_data(dev, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1481,27 +1379,24 @@ static int aer_probe(struct pcie_device *dev)
|
|||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
struct aer_rpc *rpc;
|
struct aer_rpc *rpc;
|
||||||
struct device *device = &dev->port->dev;
|
struct device *device = &dev->device;
|
||||||
|
|
||||||
/* Alloc rpc data structure */
|
rpc = devm_kzalloc(device, sizeof(struct aer_rpc), GFP_KERNEL);
|
||||||
rpc = aer_alloc_rpc(dev);
|
|
||||||
if (!rpc) {
|
if (!rpc) {
|
||||||
dev_printk(KERN_DEBUG, device, "alloc AER rpc failed\n");
|
dev_printk(KERN_DEBUG, device, "alloc AER rpc failed\n");
|
||||||
aer_remove(dev);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
rpc->rpd = dev->port;
|
||||||
|
set_service_data(dev, rpc);
|
||||||
|
|
||||||
/* Request IRQ ISR */
|
status = devm_request_threaded_irq(device, dev->irq, aer_irq, aer_isr,
|
||||||
status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev);
|
IRQF_SHARED, "aerdrv", dev);
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_printk(KERN_DEBUG, device, "request AER IRQ %d failed\n",
|
dev_printk(KERN_DEBUG, device, "request AER IRQ %d failed\n",
|
||||||
dev->irq);
|
dev->irq);
|
||||||
aer_remove(dev);
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc->isr = 1;
|
|
||||||
|
|
||||||
aer_enable_rootport(rpc);
|
aer_enable_rootport(rpc);
|
||||||
dev_info(device, "AER enabled with IRQ %d\n", dev->irq);
|
dev_info(device, "AER enabled with IRQ %d\n", dev->irq);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1526,7 +1421,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
|||||||
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
||||||
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
|
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
|
||||||
|
|
||||||
rc = pci_bridge_secondary_bus_reset(dev);
|
rc = pci_bus_error_reset(dev);
|
||||||
pci_printk(KERN_DEBUG, dev, "Root Port link has been reset\n");
|
pci_printk(KERN_DEBUG, dev, "Root Port link has been reset\n");
|
||||||
|
|
||||||
/* Clear Root Error Status */
|
/* Clear Root Error Status */
|
||||||
@@ -1541,18 +1436,6 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
|||||||
return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
|
return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* aer_error_resume - clean up corresponding error status bits
|
|
||||||
* @dev: pointer to Root Port's pci_dev data structure
|
|
||||||
*
|
|
||||||
* Invoked by Port Bus driver during nonfatal recovery.
|
|
||||||
*/
|
|
||||||
static void aer_error_resume(struct pci_dev *dev)
|
|
||||||
{
|
|
||||||
pci_aer_clear_device_status(dev);
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct pcie_port_service_driver aerdriver = {
|
static struct pcie_port_service_driver aerdriver = {
|
||||||
.name = "aer",
|
.name = "aer",
|
||||||
.port_type = PCI_EXP_TYPE_ROOT_PORT,
|
.port_type = PCI_EXP_TYPE_ROOT_PORT,
|
||||||
@@ -1560,7 +1443,6 @@ static struct pcie_port_service_driver aerdriver = {
|
|||||||
|
|
||||||
.probe = aer_probe,
|
.probe = aer_probe,
|
||||||
.remove = aer_remove,
|
.remove = aer_remove,
|
||||||
.error_resume = aer_error_resume,
|
|
||||||
.reset_link = aer_root_reset,
|
.reset_link = aer_root_reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1569,10 +1451,9 @@ static struct pcie_port_service_driver aerdriver = {
|
|||||||
*
|
*
|
||||||
* Invoked when AER root service driver is loaded.
|
* Invoked when AER root service driver is loaded.
|
||||||
*/
|
*/
|
||||||
static int __init aer_service_init(void)
|
int __init pcie_aer_init(void)
|
||||||
{
|
{
|
||||||
if (!pci_aer_available() || aer_acpi_firmware_first())
|
if (!pci_aer_available() || aer_acpi_firmware_first())
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
return pcie_port_service_register(&aerdriver);
|
return pcie_port_service_register(&aerdriver);
|
||||||
}
|
}
|
||||||
device_initcall(aer_service_init);
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
#include <linux/miscdevice.h>
|
#include <linux/miscdevice.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@@ -175,14 +176,48 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where,
|
|||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int aer_inj_read(struct pci_bus *bus, unsigned int devfn, int where,
|
||||||
|
int size, u32 *val)
|
||||||
|
{
|
||||||
|
struct pci_ops *ops, *my_ops;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
ops = __find_pci_bus_ops(bus);
|
||||||
|
if (!ops)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
my_ops = bus->ops;
|
||||||
|
bus->ops = ops;
|
||||||
|
rv = ops->read(bus, devfn, where, size, val);
|
||||||
|
bus->ops = my_ops;
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aer_inj_write(struct pci_bus *bus, unsigned int devfn, int where,
|
||||||
|
int size, u32 val)
|
||||||
|
{
|
||||||
|
struct pci_ops *ops, *my_ops;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
ops = __find_pci_bus_ops(bus);
|
||||||
|
if (!ops)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
my_ops = bus->ops;
|
||||||
|
bus->ops = ops;
|
||||||
|
rv = ops->write(bus, devfn, where, size, val);
|
||||||
|
bus->ops = my_ops;
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
|
static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 *val)
|
int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
u32 *sim;
|
u32 *sim;
|
||||||
struct aer_error *err;
|
struct aer_error *err;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct pci_ops *ops;
|
|
||||||
struct pci_ops *my_ops;
|
|
||||||
int domain;
|
int domain;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
@@ -203,18 +238,7 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
ops = __find_pci_bus_ops(bus);
|
rv = aer_inj_read(bus, devfn, where, size, val);
|
||||||
/*
|
|
||||||
* pci_lock must already be held, so we can directly
|
|
||||||
* manipulate bus->ops. Many config access functions,
|
|
||||||
* including pci_generic_config_read() require the original
|
|
||||||
* bus->ops be installed to function, so temporarily put them
|
|
||||||
* back.
|
|
||||||
*/
|
|
||||||
my_ops = bus->ops;
|
|
||||||
bus->ops = ops;
|
|
||||||
rv = ops->read(bus, devfn, where, size, val);
|
|
||||||
bus->ops = my_ops;
|
|
||||||
spin_unlock_irqrestore(&inject_lock, flags);
|
spin_unlock_irqrestore(&inject_lock, flags);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@@ -226,8 +250,6 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
|
|||||||
struct aer_error *err;
|
struct aer_error *err;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int rw1cs;
|
int rw1cs;
|
||||||
struct pci_ops *ops;
|
|
||||||
struct pci_ops *my_ops;
|
|
||||||
int domain;
|
int domain;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
@@ -251,18 +273,7 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
ops = __find_pci_bus_ops(bus);
|
rv = aer_inj_write(bus, devfn, where, size, val);
|
||||||
/*
|
|
||||||
* pci_lock must already be held, so we can directly
|
|
||||||
* manipulate bus->ops. Many config access functions,
|
|
||||||
* including pci_generic_config_write() require the original
|
|
||||||
* bus->ops be installed to function, so temporarily put them
|
|
||||||
* back.
|
|
||||||
*/
|
|
||||||
my_ops = bus->ops;
|
|
||||||
bus->ops = ops;
|
|
||||||
rv = ops->write(bus, devfn, where, size, val);
|
|
||||||
bus->ops = my_ops;
|
|
||||||
spin_unlock_irqrestore(&inject_lock, flags);
|
spin_unlock_irqrestore(&inject_lock, flags);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@@ -303,32 +314,13 @@ out:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_aer_device_iter(struct device *device, void *data)
|
|
||||||
{
|
|
||||||
struct pcie_device **result = data;
|
|
||||||
struct pcie_device *pcie_dev;
|
|
||||||
|
|
||||||
if (device->bus == &pcie_port_bus_type) {
|
|
||||||
pcie_dev = to_pcie_device(device);
|
|
||||||
if (pcie_dev->service & PCIE_PORT_SERVICE_AER) {
|
|
||||||
*result = pcie_dev;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int find_aer_device(struct pci_dev *dev, struct pcie_device **result)
|
|
||||||
{
|
|
||||||
return device_for_each_child(&dev->dev, result, find_aer_device_iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int aer_inject(struct aer_error_inj *einj)
|
static int aer_inject(struct aer_error_inj *einj)
|
||||||
{
|
{
|
||||||
struct aer_error *err, *rperr;
|
struct aer_error *err, *rperr;
|
||||||
struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
|
struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
|
||||||
struct pci_dev *dev, *rpdev;
|
struct pci_dev *dev, *rpdev;
|
||||||
struct pcie_device *edev;
|
struct pcie_device *edev;
|
||||||
|
struct device *device;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn);
|
unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn);
|
||||||
int pos_cap_err, rp_pos_cap_err;
|
int pos_cap_err, rp_pos_cap_err;
|
||||||
@@ -464,7 +456,9 @@ static int aer_inject(struct aer_error_inj *einj)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto out_put;
|
goto out_put;
|
||||||
|
|
||||||
if (find_aer_device(rpdev, &edev)) {
|
device = pcie_port_find_device(rpdev, PCIE_PORT_SERVICE_AER);
|
||||||
|
if (device) {
|
||||||
|
edev = to_pcie_device(device);
|
||||||
if (!get_service_data(edev)) {
|
if (!get_service_data(edev)) {
|
||||||
dev_warn(&edev->device,
|
dev_warn(&edev->device,
|
||||||
"aer_inject: AER service is not initialized\n");
|
"aer_inject: AER service is not initialized\n");
|
||||||
@@ -474,7 +468,9 @@ static int aer_inject(struct aer_error_inj *einj)
|
|||||||
dev_info(&edev->device,
|
dev_info(&edev->device,
|
||||||
"aer_inject: Injecting errors %08x/%08x into device %s\n",
|
"aer_inject: Injecting errors %08x/%08x into device %s\n",
|
||||||
einj->cor_status, einj->uncor_status, pci_name(dev));
|
einj->cor_status, einj->uncor_status, pci_name(dev));
|
||||||
aer_irq(-1, edev);
|
local_irq_disable();
|
||||||
|
generic_handle_irq(edev->irq);
|
||||||
|
local_irq_enable();
|
||||||
} else {
|
} else {
|
||||||
pci_err(rpdev, "aer_inject: AER device not found\n");
|
pci_err(rpdev, "aer_inject: AER device not found\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
|
|||||||
@@ -44,6 +44,58 @@ static const char * const rp_pio_error_string[] = {
|
|||||||
"Memory Request Completion Timeout", /* Bit Position 18 */
|
"Memory Request Completion Timeout", /* Bit Position 18 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct dpc_dev *to_dpc_dev(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
struct device *device;
|
||||||
|
|
||||||
|
device = pcie_port_find_device(dev, PCIE_PORT_SERVICE_DPC);
|
||||||
|
if (!device)
|
||||||
|
return NULL;
|
||||||
|
return get_service_data(to_pcie_device(device));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pci_save_dpc_state(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc;
|
||||||
|
struct pci_cap_saved_state *save_state;
|
||||||
|
u16 *cap;
|
||||||
|
|
||||||
|
if (!pci_is_pcie(dev))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dpc = to_dpc_dev(dev);
|
||||||
|
if (!dpc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
|
||||||
|
if (!save_state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cap = (u16 *)&save_state->cap.data[0];
|
||||||
|
pci_read_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pci_restore_dpc_state(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc;
|
||||||
|
struct pci_cap_saved_state *save_state;
|
||||||
|
u16 *cap;
|
||||||
|
|
||||||
|
if (!pci_is_pcie(dev))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dpc = to_dpc_dev(dev);
|
||||||
|
if (!dpc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
|
||||||
|
if (!save_state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cap = (u16 *)&save_state->cap.data[0];
|
||||||
|
pci_write_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, *cap);
|
||||||
|
}
|
||||||
|
|
||||||
static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
|
static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
|
||||||
{
|
{
|
||||||
unsigned long timeout = jiffies + HZ;
|
unsigned long timeout = jiffies + HZ;
|
||||||
@@ -67,18 +119,13 @@ static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
|
|||||||
static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
|
static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
struct dpc_dev *dpc;
|
struct dpc_dev *dpc;
|
||||||
struct pcie_device *pciedev;
|
|
||||||
struct device *devdpc;
|
|
||||||
|
|
||||||
u16 cap;
|
u16 cap;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DPC disables the Link automatically in hardware, so it has
|
* DPC disables the Link automatically in hardware, so it has
|
||||||
* already been reset by the time we get here.
|
* already been reset by the time we get here.
|
||||||
*/
|
*/
|
||||||
devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
|
dpc = to_dpc_dev(pdev);
|
||||||
pciedev = to_pcie_device(devdpc);
|
|
||||||
dpc = get_service_data(pciedev);
|
|
||||||
cap = dpc->cap_pos;
|
cap = dpc->cap_pos;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -93,10 +140,12 @@ static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
|
|||||||
pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
|
pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
|
||||||
PCI_EXP_DPC_STATUS_TRIGGER);
|
PCI_EXP_DPC_STATUS_TRIGGER);
|
||||||
|
|
||||||
|
if (!pcie_wait_for_link(pdev, true))
|
||||||
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
|
|
||||||
return PCI_ERS_RESULT_RECOVERED;
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
|
static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
|
||||||
{
|
{
|
||||||
struct device *dev = &dpc->dev->device;
|
struct device *dev = &dpc->dev->device;
|
||||||
@@ -169,7 +218,7 @@ static irqreturn_t dpc_handler(int irq, void *context)
|
|||||||
|
|
||||||
reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1;
|
reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1;
|
||||||
ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5;
|
ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5;
|
||||||
dev_warn(dev, "DPC %s detected, remove downstream devices\n",
|
dev_warn(dev, "DPC %s detected\n",
|
||||||
(reason == 0) ? "unmasked uncorrectable error" :
|
(reason == 0) ? "unmasked uncorrectable error" :
|
||||||
(reason == 1) ? "ERR_NONFATAL" :
|
(reason == 1) ? "ERR_NONFATAL" :
|
||||||
(reason == 2) ? "ERR_FATAL" :
|
(reason == 2) ? "ERR_FATAL" :
|
||||||
@@ -186,7 +235,7 @@ static irqreturn_t dpc_handler(int irq, void *context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* We configure DPC so it only triggers on ERR_FATAL */
|
/* We configure DPC so it only triggers on ERR_FATAL */
|
||||||
pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_DPC);
|
pcie_do_recovery(pdev, pci_channel_io_frozen, PCIE_PORT_SERVICE_DPC);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
@@ -259,6 +308,8 @@ static int dpc_probe(struct pcie_device *dev)
|
|||||||
FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
|
FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
|
||||||
FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
|
FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
|
||||||
FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
|
FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
|
||||||
|
|
||||||
|
pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_DPC, sizeof(u16));
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,8 +333,7 @@ static struct pcie_port_service_driver dpcdriver = {
|
|||||||
.reset_link = dpc_reset_link,
|
.reset_link = dpc_reset_link,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init dpc_service_init(void)
|
int __init pcie_dpc_init(void)
|
||||||
{
|
{
|
||||||
return pcie_port_service_register(&dpcdriver);
|
return pcie_port_service_register(&dpcdriver);
|
||||||
}
|
}
|
||||||
device_initcall(dpc_service_init);
|
|
||||||
|
|||||||
@@ -12,18 +12,12 @@
|
|||||||
|
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/aer.h>
|
#include <linux/aer.h>
|
||||||
#include "portdrv.h"
|
#include "portdrv.h"
|
||||||
#include "../pci.h"
|
#include "../pci.h"
|
||||||
|
|
||||||
struct aer_broadcast_data {
|
|
||||||
enum pci_channel_state state;
|
|
||||||
enum pci_ers_result result;
|
|
||||||
};
|
|
||||||
|
|
||||||
static pci_ers_result_t merge_result(enum pci_ers_result orig,
|
static pci_ers_result_t merge_result(enum pci_ers_result orig,
|
||||||
enum pci_ers_result new)
|
enum pci_ers_result new)
|
||||||
{
|
{
|
||||||
@@ -49,66 +43,52 @@ static pci_ers_result_t merge_result(enum pci_ers_result orig,
|
|||||||
return orig;
|
return orig;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int report_error_detected(struct pci_dev *dev, void *data)
|
static int report_error_detected(struct pci_dev *dev,
|
||||||
|
enum pci_channel_state state,
|
||||||
|
enum pci_ers_result *result)
|
||||||
{
|
{
|
||||||
pci_ers_result_t vote;
|
pci_ers_result_t vote;
|
||||||
const struct pci_error_handlers *err_handler;
|
const struct pci_error_handlers *err_handler;
|
||||||
struct aer_broadcast_data *result_data;
|
|
||||||
|
|
||||||
result_data = (struct aer_broadcast_data *) data;
|
|
||||||
|
|
||||||
device_lock(&dev->dev);
|
device_lock(&dev->dev);
|
||||||
dev->error_state = result_data->state;
|
if (!pci_dev_set_io_state(dev, state) ||
|
||||||
|
!dev->driver ||
|
||||||
if (!dev->driver ||
|
|
||||||
!dev->driver->err_handler ||
|
!dev->driver->err_handler ||
|
||||||
!dev->driver->err_handler->error_detected) {
|
!dev->driver->err_handler->error_detected) {
|
||||||
if (result_data->state == pci_channel_io_frozen &&
|
|
||||||
dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
|
|
||||||
/*
|
|
||||||
* In case of fatal recovery, if one of down-
|
|
||||||
* stream device has no driver. We might be
|
|
||||||
* unable to recover because a later insmod
|
|
||||||
* of a driver for this device is unaware of
|
|
||||||
* its hw state.
|
|
||||||
*/
|
|
||||||
pci_printk(KERN_DEBUG, dev, "device has %s\n",
|
|
||||||
dev->driver ?
|
|
||||||
"no AER-aware driver" : "no driver");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there's any device in the subtree that does not
|
* If any device in the subtree does not have an error_detected
|
||||||
* have an error_detected callback, returning
|
* callback, PCI_ERS_RESULT_NO_AER_DRIVER prevents subsequent
|
||||||
* PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of
|
* error callbacks of "any" device in the subtree, and will
|
||||||
* the subsequent mmio_enabled/slot_reset/resume
|
* exit in the disconnected error state.
|
||||||
* callbacks of "any" device in the subtree. All the
|
|
||||||
* devices in the subtree are left in the error state
|
|
||||||
* without recovery.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
|
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
|
||||||
vote = PCI_ERS_RESULT_NO_AER_DRIVER;
|
vote = PCI_ERS_RESULT_NO_AER_DRIVER;
|
||||||
else
|
else
|
||||||
vote = PCI_ERS_RESULT_NONE;
|
vote = PCI_ERS_RESULT_NONE;
|
||||||
} else {
|
} else {
|
||||||
err_handler = dev->driver->err_handler;
|
err_handler = dev->driver->err_handler;
|
||||||
vote = err_handler->error_detected(dev, result_data->state);
|
vote = err_handler->error_detected(dev, state);
|
||||||
pci_uevent_ers(dev, PCI_ERS_RESULT_NONE);
|
|
||||||
}
|
}
|
||||||
|
pci_uevent_ers(dev, vote);
|
||||||
result_data->result = merge_result(result_data->result, vote);
|
*result = merge_result(*result, vote);
|
||||||
device_unlock(&dev->dev);
|
device_unlock(&dev->dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int report_frozen_detected(struct pci_dev *dev, void *data)
|
||||||
|
{
|
||||||
|
return report_error_detected(dev, pci_channel_io_frozen, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int report_normal_detected(struct pci_dev *dev, void *data)
|
||||||
|
{
|
||||||
|
return report_error_detected(dev, pci_channel_io_normal, data);
|
||||||
|
}
|
||||||
|
|
||||||
static int report_mmio_enabled(struct pci_dev *dev, void *data)
|
static int report_mmio_enabled(struct pci_dev *dev, void *data)
|
||||||
{
|
{
|
||||||
pci_ers_result_t vote;
|
pci_ers_result_t vote, *result = data;
|
||||||
const struct pci_error_handlers *err_handler;
|
const struct pci_error_handlers *err_handler;
|
||||||
struct aer_broadcast_data *result_data;
|
|
||||||
|
|
||||||
result_data = (struct aer_broadcast_data *) data;
|
|
||||||
|
|
||||||
device_lock(&dev->dev);
|
device_lock(&dev->dev);
|
||||||
if (!dev->driver ||
|
if (!dev->driver ||
|
||||||
@@ -118,7 +98,7 @@ static int report_mmio_enabled(struct pci_dev *dev, void *data)
|
|||||||
|
|
||||||
err_handler = dev->driver->err_handler;
|
err_handler = dev->driver->err_handler;
|
||||||
vote = err_handler->mmio_enabled(dev);
|
vote = err_handler->mmio_enabled(dev);
|
||||||
result_data->result = merge_result(result_data->result, vote);
|
*result = merge_result(*result, vote);
|
||||||
out:
|
out:
|
||||||
device_unlock(&dev->dev);
|
device_unlock(&dev->dev);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -126,11 +106,8 @@ out:
|
|||||||
|
|
||||||
static int report_slot_reset(struct pci_dev *dev, void *data)
|
static int report_slot_reset(struct pci_dev *dev, void *data)
|
||||||
{
|
{
|
||||||
pci_ers_result_t vote;
|
pci_ers_result_t vote, *result = data;
|
||||||
const struct pci_error_handlers *err_handler;
|
const struct pci_error_handlers *err_handler;
|
||||||
struct aer_broadcast_data *result_data;
|
|
||||||
|
|
||||||
result_data = (struct aer_broadcast_data *) data;
|
|
||||||
|
|
||||||
device_lock(&dev->dev);
|
device_lock(&dev->dev);
|
||||||
if (!dev->driver ||
|
if (!dev->driver ||
|
||||||
@@ -140,7 +117,7 @@ static int report_slot_reset(struct pci_dev *dev, void *data)
|
|||||||
|
|
||||||
err_handler = dev->driver->err_handler;
|
err_handler = dev->driver->err_handler;
|
||||||
vote = err_handler->slot_reset(dev);
|
vote = err_handler->slot_reset(dev);
|
||||||
result_data->result = merge_result(result_data->result, vote);
|
*result = merge_result(*result, vote);
|
||||||
out:
|
out:
|
||||||
device_unlock(&dev->dev);
|
device_unlock(&dev->dev);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -151,17 +128,16 @@ static int report_resume(struct pci_dev *dev, void *data)
|
|||||||
const struct pci_error_handlers *err_handler;
|
const struct pci_error_handlers *err_handler;
|
||||||
|
|
||||||
device_lock(&dev->dev);
|
device_lock(&dev->dev);
|
||||||
dev->error_state = pci_channel_io_normal;
|
if (!pci_dev_set_io_state(dev, pci_channel_io_normal) ||
|
||||||
|
!dev->driver ||
|
||||||
if (!dev->driver ||
|
|
||||||
!dev->driver->err_handler ||
|
!dev->driver->err_handler ||
|
||||||
!dev->driver->err_handler->resume)
|
!dev->driver->err_handler->resume)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
err_handler = dev->driver->err_handler;
|
err_handler = dev->driver->err_handler;
|
||||||
err_handler->resume(dev);
|
err_handler->resume(dev);
|
||||||
pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
|
|
||||||
out:
|
out:
|
||||||
|
pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
|
||||||
device_unlock(&dev->dev);
|
device_unlock(&dev->dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -177,207 +153,86 @@ static pci_ers_result_t default_reset_link(struct pci_dev *dev)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = pci_bridge_secondary_bus_reset(dev);
|
rc = pci_bus_error_reset(dev);
|
||||||
pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
|
pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
|
||||||
return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
|
return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
|
static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
|
||||||
{
|
{
|
||||||
struct pci_dev *udev;
|
|
||||||
pci_ers_result_t status;
|
pci_ers_result_t status;
|
||||||
struct pcie_port_service_driver *driver = NULL;
|
struct pcie_port_service_driver *driver = NULL;
|
||||||
|
|
||||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
driver = pcie_port_find_service(dev, service);
|
||||||
/* Reset this port for all subordinates */
|
|
||||||
udev = dev;
|
|
||||||
} else {
|
|
||||||
/* Reset the upstream component (likely downstream port) */
|
|
||||||
udev = dev->bus->self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use the aer driver of the component firstly */
|
|
||||||
driver = pcie_port_find_service(udev, service);
|
|
||||||
|
|
||||||
if (driver && driver->reset_link) {
|
if (driver && driver->reset_link) {
|
||||||
status = driver->reset_link(udev);
|
status = driver->reset_link(dev);
|
||||||
} else if (udev->has_secondary_link) {
|
} else if (dev->has_secondary_link) {
|
||||||
status = default_reset_link(udev);
|
status = default_reset_link(dev);
|
||||||
} else {
|
} else {
|
||||||
pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
|
pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
|
||||||
pci_name(udev));
|
pci_name(dev));
|
||||||
return PCI_ERS_RESULT_DISCONNECT;
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != PCI_ERS_RESULT_RECOVERED) {
|
if (status != PCI_ERS_RESULT_RECOVERED) {
|
||||||
pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
|
pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
|
||||||
pci_name(udev));
|
pci_name(dev));
|
||||||
return PCI_ERS_RESULT_DISCONNECT;
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state,
|
||||||
* broadcast_error_message - handle message broadcast to downstream drivers
|
u32 service)
|
||||||
* @dev: pointer to from where in a hierarchy message is broadcasted down
|
|
||||||
* @state: error state
|
|
||||||
* @error_mesg: message to print
|
|
||||||
* @cb: callback to be broadcasted
|
|
||||||
*
|
|
||||||
* Invoked during error recovery process. Once being invoked, the content
|
|
||||||
* of error severity will be broadcasted to all downstream drivers in a
|
|
||||||
* hierarchy in question.
|
|
||||||
*/
|
|
||||||
static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
|
|
||||||
enum pci_channel_state state,
|
|
||||||
char *error_mesg,
|
|
||||||
int (*cb)(struct pci_dev *, void *))
|
|
||||||
{
|
{
|
||||||
struct aer_broadcast_data result_data;
|
pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;
|
||||||
|
struct pci_bus *bus;
|
||||||
|
|
||||||
pci_printk(KERN_DEBUG, dev, "broadcast %s message\n", error_mesg);
|
/*
|
||||||
result_data.state = state;
|
* Error recovery runs on all subordinates of the first downstream port.
|
||||||
if (cb == report_error_detected)
|
* If the downstream port detected the error, it is cleared at the end.
|
||||||
result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
|
*/
|
||||||
|
if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
|
||||||
|
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
|
||||||
|
dev = dev->bus->self;
|
||||||
|
bus = dev->subordinate;
|
||||||
|
|
||||||
|
pci_dbg(dev, "broadcast error_detected message\n");
|
||||||
|
if (state == pci_channel_io_frozen)
|
||||||
|
pci_walk_bus(bus, report_frozen_detected, &status);
|
||||||
else
|
else
|
||||||
result_data.result = PCI_ERS_RESULT_RECOVERED;
|
pci_walk_bus(bus, report_normal_detected, &status);
|
||||||
|
|
||||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
if (state == pci_channel_io_frozen &&
|
||||||
/*
|
reset_link(dev, service) != PCI_ERS_RESULT_RECOVERED)
|
||||||
* If the error is reported by a bridge, we think this error
|
goto failed;
|
||||||
* is related to the downstream link of the bridge, so we
|
|
||||||
* do error recovery on all subordinates of the bridge instead
|
if (status == PCI_ERS_RESULT_CAN_RECOVER) {
|
||||||
* of the bridge and clear the error status of the bridge.
|
status = PCI_ERS_RESULT_RECOVERED;
|
||||||
*/
|
pci_dbg(dev, "broadcast mmio_enabled message\n");
|
||||||
if (cb == report_error_detected)
|
pci_walk_bus(bus, report_mmio_enabled, &status);
|
||||||
dev->error_state = state;
|
|
||||||
pci_walk_bus(dev->subordinate, cb, &result_data);
|
|
||||||
if (cb == report_resume) {
|
|
||||||
pci_aer_clear_device_status(dev);
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(dev);
|
|
||||||
dev->error_state = pci_channel_io_normal;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* If the error is reported by an end point, we think this
|
|
||||||
* error is related to the upstream link of the end point.
|
|
||||||
* The error is non fatal so the bus is ok; just invoke
|
|
||||||
* the callback for the function that logged the error.
|
|
||||||
*/
|
|
||||||
cb(dev, &result_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result_data.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pcie_do_fatal_recovery - handle fatal error recovery process
|
|
||||||
* @dev: pointer to a pci_dev data structure of agent detecting an error
|
|
||||||
*
|
|
||||||
* Invoked when an error is fatal. Once being invoked, removes the devices
|
|
||||||
* beneath this AER agent, followed by reset link e.g. secondary bus reset
|
|
||||||
* followed by re-enumeration of devices.
|
|
||||||
*/
|
|
||||||
void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service)
|
|
||||||
{
|
|
||||||
struct pci_dev *udev;
|
|
||||||
struct pci_bus *parent;
|
|
||||||
struct pci_dev *pdev, *temp;
|
|
||||||
pci_ers_result_t result;
|
|
||||||
|
|
||||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
|
|
||||||
udev = dev;
|
|
||||||
else
|
|
||||||
udev = dev->bus->self;
|
|
||||||
|
|
||||||
parent = udev->subordinate;
|
|
||||||
pci_lock_rescan_remove();
|
|
||||||
pci_dev_get(dev);
|
|
||||||
list_for_each_entry_safe_reverse(pdev, temp, &parent->devices,
|
|
||||||
bus_list) {
|
|
||||||
pci_dev_get(pdev);
|
|
||||||
pci_dev_set_disconnected(pdev, NULL);
|
|
||||||
if (pci_has_subordinate(pdev))
|
|
||||||
pci_walk_bus(pdev->subordinate,
|
|
||||||
pci_dev_set_disconnected, NULL);
|
|
||||||
pci_stop_and_remove_bus_device(pdev);
|
|
||||||
pci_dev_put(pdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = reset_link(udev, service);
|
|
||||||
|
|
||||||
if ((service == PCIE_PORT_SERVICE_AER) &&
|
|
||||||
(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
|
|
||||||
/*
|
|
||||||
* If the error is reported by a bridge, we think this error
|
|
||||||
* is related to the downstream link of the bridge, so we
|
|
||||||
* do error recovery on all subordinates of the bridge instead
|
|
||||||
* of the bridge and clear the error status of the bridge.
|
|
||||||
*/
|
|
||||||
pci_aer_clear_fatal_status(dev);
|
|
||||||
pci_aer_clear_device_status(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == PCI_ERS_RESULT_RECOVERED) {
|
|
||||||
if (pcie_wait_for_link(udev, true))
|
|
||||||
pci_rescan_bus(udev->bus);
|
|
||||||
pci_info(dev, "Device recovery from fatal error successful\n");
|
|
||||||
} else {
|
|
||||||
pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
|
|
||||||
pci_info(dev, "Device recovery from fatal error failed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
pci_dev_put(dev);
|
|
||||||
pci_unlock_rescan_remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pcie_do_nonfatal_recovery - handle nonfatal error recovery process
|
|
||||||
* @dev: pointer to a pci_dev data structure of agent detecting an error
|
|
||||||
*
|
|
||||||
* Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
|
|
||||||
* error detected message to all downstream drivers within a hierarchy in
|
|
||||||
* question and return the returned code.
|
|
||||||
*/
|
|
||||||
void pcie_do_nonfatal_recovery(struct pci_dev *dev)
|
|
||||||
{
|
|
||||||
pci_ers_result_t status;
|
|
||||||
enum pci_channel_state state;
|
|
||||||
|
|
||||||
state = pci_channel_io_normal;
|
|
||||||
|
|
||||||
status = broadcast_error_message(dev,
|
|
||||||
state,
|
|
||||||
"error_detected",
|
|
||||||
report_error_detected);
|
|
||||||
|
|
||||||
if (status == PCI_ERS_RESULT_CAN_RECOVER)
|
|
||||||
status = broadcast_error_message(dev,
|
|
||||||
state,
|
|
||||||
"mmio_enabled",
|
|
||||||
report_mmio_enabled);
|
|
||||||
|
|
||||||
if (status == PCI_ERS_RESULT_NEED_RESET) {
|
if (status == PCI_ERS_RESULT_NEED_RESET) {
|
||||||
/*
|
/*
|
||||||
* TODO: Should call platform-specific
|
* TODO: Should call platform-specific
|
||||||
* functions to reset slot before calling
|
* functions to reset slot before calling
|
||||||
* drivers' slot_reset callbacks?
|
* drivers' slot_reset callbacks?
|
||||||
*/
|
*/
|
||||||
status = broadcast_error_message(dev,
|
status = PCI_ERS_RESULT_RECOVERED;
|
||||||
state,
|
pci_dbg(dev, "broadcast slot_reset message\n");
|
||||||
"slot_reset",
|
pci_walk_bus(bus, report_slot_reset, &status);
|
||||||
report_slot_reset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != PCI_ERS_RESULT_RECOVERED)
|
if (status != PCI_ERS_RESULT_RECOVERED)
|
||||||
goto failed;
|
goto failed;
|
||||||
|
|
||||||
broadcast_error_message(dev,
|
pci_dbg(dev, "broadcast resume message\n");
|
||||||
state,
|
pci_walk_bus(bus, report_resume, &status);
|
||||||
"resume",
|
|
||||||
report_resume);
|
|
||||||
|
|
||||||
|
pci_aer_clear_device_status(dev);
|
||||||
|
pci_cleanup_aer_uncorrect_error_status(dev);
|
||||||
pci_info(dev, "AER: Device recovery successful\n");
|
pci_info(dev, "AER: Device recovery successful\n");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -432,6 +432,31 @@ static void pcie_pme_remove(struct pcie_device *srv)
|
|||||||
kfree(get_service_data(srv));
|
kfree(get_service_data(srv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pcie_pme_runtime_suspend(struct pcie_device *srv)
|
||||||
|
{
|
||||||
|
struct pcie_pme_service_data *data = get_service_data(srv);
|
||||||
|
|
||||||
|
spin_lock_irq(&data->lock);
|
||||||
|
pcie_pme_interrupt_enable(srv->port, false);
|
||||||
|
pcie_clear_root_pme_status(srv->port);
|
||||||
|
data->noirq = true;
|
||||||
|
spin_unlock_irq(&data->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcie_pme_runtime_resume(struct pcie_device *srv)
|
||||||
|
{
|
||||||
|
struct pcie_pme_service_data *data = get_service_data(srv);
|
||||||
|
|
||||||
|
spin_lock_irq(&data->lock);
|
||||||
|
pcie_pme_interrupt_enable(srv->port, true);
|
||||||
|
data->noirq = false;
|
||||||
|
spin_unlock_irq(&data->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct pcie_port_service_driver pcie_pme_driver = {
|
static struct pcie_port_service_driver pcie_pme_driver = {
|
||||||
.name = "pcie_pme",
|
.name = "pcie_pme",
|
||||||
.port_type = PCI_EXP_TYPE_ROOT_PORT,
|
.port_type = PCI_EXP_TYPE_ROOT_PORT,
|
||||||
@@ -439,6 +464,8 @@ static struct pcie_port_service_driver pcie_pme_driver = {
|
|||||||
|
|
||||||
.probe = pcie_pme_probe,
|
.probe = pcie_pme_probe,
|
||||||
.suspend = pcie_pme_suspend,
|
.suspend = pcie_pme_suspend,
|
||||||
|
.runtime_suspend = pcie_pme_runtime_suspend,
|
||||||
|
.runtime_resume = pcie_pme_runtime_resume,
|
||||||
.resume = pcie_pme_resume,
|
.resume = pcie_pme_resume,
|
||||||
.remove = pcie_pme_remove,
|
.remove = pcie_pme_remove,
|
||||||
};
|
};
|
||||||
@@ -446,8 +473,7 @@ static struct pcie_port_service_driver pcie_pme_driver = {
|
|||||||
/**
|
/**
|
||||||
* pcie_pme_service_init - Register the PCIe PME service driver.
|
* pcie_pme_service_init - Register the PCIe PME service driver.
|
||||||
*/
|
*/
|
||||||
static int __init pcie_pme_service_init(void)
|
int __init pcie_pme_init(void)
|
||||||
{
|
{
|
||||||
return pcie_port_service_register(&pcie_pme_driver);
|
return pcie_port_service_register(&pcie_pme_driver);
|
||||||
}
|
}
|
||||||
device_initcall(pcie_pme_service_init);
|
|
||||||
|
|||||||
@@ -23,6 +23,30 @@
|
|||||||
|
|
||||||
#define PCIE_PORT_DEVICE_MAXSERVICES 4
|
#define PCIE_PORT_DEVICE_MAXSERVICES 4
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCIEAER
|
||||||
|
int pcie_aer_init(void);
|
||||||
|
#else
|
||||||
|
static inline int pcie_aer_init(void) { return 0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_HOTPLUG_PCI_PCIE
|
||||||
|
int pcie_hp_init(void);
|
||||||
|
#else
|
||||||
|
static inline int pcie_hp_init(void) { return 0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCIE_PME
|
||||||
|
int pcie_pme_init(void);
|
||||||
|
#else
|
||||||
|
static inline int pcie_pme_init(void) { return 0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCIE_DPC
|
||||||
|
int pcie_dpc_init(void);
|
||||||
|
#else
|
||||||
|
static inline int pcie_dpc_init(void) { return 0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Port Type */
|
/* Port Type */
|
||||||
#define PCIE_ANY_PORT (~0)
|
#define PCIE_ANY_PORT (~0)
|
||||||
|
|
||||||
@@ -52,6 +76,8 @@ struct pcie_port_service_driver {
|
|||||||
int (*suspend) (struct pcie_device *dev);
|
int (*suspend) (struct pcie_device *dev);
|
||||||
int (*resume_noirq) (struct pcie_device *dev);
|
int (*resume_noirq) (struct pcie_device *dev);
|
||||||
int (*resume) (struct pcie_device *dev);
|
int (*resume) (struct pcie_device *dev);
|
||||||
|
int (*runtime_suspend) (struct pcie_device *dev);
|
||||||
|
int (*runtime_resume) (struct pcie_device *dev);
|
||||||
|
|
||||||
/* Device driver may resume normal operations */
|
/* Device driver may resume normal operations */
|
||||||
void (*error_resume)(struct pci_dev *dev);
|
void (*error_resume)(struct pci_dev *dev);
|
||||||
@@ -85,6 +111,8 @@ int pcie_port_device_register(struct pci_dev *dev);
|
|||||||
int pcie_port_device_suspend(struct device *dev);
|
int pcie_port_device_suspend(struct device *dev);
|
||||||
int pcie_port_device_resume_noirq(struct device *dev);
|
int pcie_port_device_resume_noirq(struct device *dev);
|
||||||
int pcie_port_device_resume(struct device *dev);
|
int pcie_port_device_resume(struct device *dev);
|
||||||
|
int pcie_port_device_runtime_suspend(struct device *dev);
|
||||||
|
int pcie_port_device_runtime_resume(struct device *dev);
|
||||||
#endif
|
#endif
|
||||||
void pcie_port_device_remove(struct pci_dev *dev);
|
void pcie_port_device_remove(struct pci_dev *dev);
|
||||||
int __must_check pcie_port_bus_register(void);
|
int __must_check pcie_port_bus_register(void);
|
||||||
@@ -123,10 +151,6 @@ static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PCIEAER
|
|
||||||
irqreturn_t aer_irq(int irq, void *context);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev,
|
struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev,
|
||||||
u32 service);
|
u32 service);
|
||||||
struct device *pcie_port_find_device(struct pci_dev *dev, u32 service);
|
struct device *pcie_port_find_device(struct pci_dev *dev, u32 service);
|
||||||
|
|||||||
@@ -395,6 +395,26 @@ int pcie_port_device_resume(struct device *dev)
|
|||||||
size_t off = offsetof(struct pcie_port_service_driver, resume);
|
size_t off = offsetof(struct pcie_port_service_driver, resume);
|
||||||
return device_for_each_child(dev, &off, pm_iter);
|
return device_for_each_child(dev, &off, pm_iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pcie_port_device_runtime_suspend - runtime suspend port services
|
||||||
|
* @dev: PCI Express port to handle
|
||||||
|
*/
|
||||||
|
int pcie_port_device_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
size_t off = offsetof(struct pcie_port_service_driver, runtime_suspend);
|
||||||
|
return device_for_each_child(dev, &off, pm_iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pcie_port_device_runtime_resume - runtime resume port services
|
||||||
|
* @dev: PCI Express port to handle
|
||||||
|
*/
|
||||||
|
int pcie_port_device_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
size_t off = offsetof(struct pcie_port_service_driver, runtime_resume);
|
||||||
|
return device_for_each_child(dev, &off, pm_iter);
|
||||||
|
}
|
||||||
#endif /* PM */
|
#endif /* PM */
|
||||||
|
|
||||||
static int remove_iter(struct device *dev, void *data)
|
static int remove_iter(struct device *dev, void *data)
|
||||||
@@ -466,6 +486,7 @@ struct device *pcie_port_find_device(struct pci_dev *dev,
|
|||||||
device = pdrvs.dev;
|
device = pdrvs.dev;
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pcie_port_find_device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pcie_port_device_remove - unregister PCI Express port service devices
|
* pcie_port_device_remove - unregister PCI Express port service devices
|
||||||
|
|||||||
@@ -45,12 +45,10 @@ __setup("pcie_ports=", pcie_port_setup);
|
|||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int pcie_port_runtime_suspend(struct device *dev)
|
static int pcie_port_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY;
|
if (!to_pci_dev(dev)->bridge_d3)
|
||||||
}
|
return -EBUSY;
|
||||||
|
|
||||||
static int pcie_port_runtime_resume(struct device *dev)
|
return pcie_port_device_runtime_suspend(dev);
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pcie_port_runtime_idle(struct device *dev)
|
static int pcie_port_runtime_idle(struct device *dev)
|
||||||
@@ -73,7 +71,7 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
|
|||||||
.restore_noirq = pcie_port_device_resume_noirq,
|
.restore_noirq = pcie_port_device_resume_noirq,
|
||||||
.restore = pcie_port_device_resume,
|
.restore = pcie_port_device_resume,
|
||||||
.runtime_suspend = pcie_port_runtime_suspend,
|
.runtime_suspend = pcie_port_runtime_suspend,
|
||||||
.runtime_resume = pcie_port_runtime_resume,
|
.runtime_resume = pcie_port_device_runtime_resume,
|
||||||
.runtime_idle = pcie_port_runtime_idle,
|
.runtime_idle = pcie_port_runtime_idle,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,8 +107,8 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
|
|||||||
|
|
||||||
pci_save_state(dev);
|
pci_save_state(dev);
|
||||||
|
|
||||||
dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_SMART_SUSPEND |
|
dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NEVER_SKIP |
|
||||||
DPM_FLAG_LEAVE_SUSPENDED);
|
DPM_FLAG_SMART_SUSPEND);
|
||||||
|
|
||||||
if (pci_bridge_d3_possible(dev)) {
|
if (pci_bridge_d3_possible(dev)) {
|
||||||
/*
|
/*
|
||||||
@@ -146,6 +144,13 @@ static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
|
|||||||
return PCI_ERS_RESULT_CAN_RECOVER;
|
return PCI_ERS_RESULT_CAN_RECOVER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
pci_restore_state(dev);
|
||||||
|
pci_save_state(dev);
|
||||||
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
|
}
|
||||||
|
|
||||||
static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
|
static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
return PCI_ERS_RESULT_RECOVERED;
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
@@ -185,6 +190,7 @@ static const struct pci_device_id port_pci_ids[] = { {
|
|||||||
|
|
||||||
static const struct pci_error_handlers pcie_portdrv_err_handler = {
|
static const struct pci_error_handlers pcie_portdrv_err_handler = {
|
||||||
.error_detected = pcie_portdrv_error_detected,
|
.error_detected = pcie_portdrv_error_detected,
|
||||||
|
.slot_reset = pcie_portdrv_slot_reset,
|
||||||
.mmio_enabled = pcie_portdrv_mmio_enabled,
|
.mmio_enabled = pcie_portdrv_mmio_enabled,
|
||||||
.resume = pcie_portdrv_err_resume,
|
.resume = pcie_portdrv_err_resume,
|
||||||
};
|
};
|
||||||
@@ -226,11 +232,20 @@ static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = {
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void __init pcie_init_services(void)
|
||||||
|
{
|
||||||
|
pcie_aer_init();
|
||||||
|
pcie_pme_init();
|
||||||
|
pcie_dpc_init();
|
||||||
|
pcie_hp_init();
|
||||||
|
}
|
||||||
|
|
||||||
static int __init pcie_portdrv_init(void)
|
static int __init pcie_portdrv_init(void)
|
||||||
{
|
{
|
||||||
if (pcie_ports_disabled)
|
if (pcie_ports_disabled)
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
|
pcie_init_services();
|
||||||
dmi_check_system(pcie_portdrv_dmi_table);
|
dmi_check_system(pcie_portdrv_dmi_table);
|
||||||
|
|
||||||
return pci_register_driver(&pcie_portdriver);
|
return pci_register_driver(&pcie_portdriver);
|
||||||
|
|||||||
@@ -713,6 +713,7 @@ static void pci_set_bus_speed(struct pci_bus *bus)
|
|||||||
|
|
||||||
pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap);
|
pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap);
|
||||||
bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS];
|
bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS];
|
||||||
|
bridge->link_active_reporting = !!(linkcap & PCI_EXP_LNKCAP_DLLLARC);
|
||||||
|
|
||||||
pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta);
|
pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta);
|
||||||
pcie_update_link_speed(bus, linksta);
|
pcie_update_link_speed(bus, linksta);
|
||||||
|
|||||||
@@ -811,6 +811,8 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus,
|
|||||||
static resource_size_t calculate_iosize(resource_size_t size,
|
static resource_size_t calculate_iosize(resource_size_t size,
|
||||||
resource_size_t min_size,
|
resource_size_t min_size,
|
||||||
resource_size_t size1,
|
resource_size_t size1,
|
||||||
|
resource_size_t add_size,
|
||||||
|
resource_size_t children_add_size,
|
||||||
resource_size_t old_size,
|
resource_size_t old_size,
|
||||||
resource_size_t align)
|
resource_size_t align)
|
||||||
{
|
{
|
||||||
@@ -823,15 +825,18 @@ static resource_size_t calculate_iosize(resource_size_t size,
|
|||||||
#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
|
#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
|
||||||
size = (size & 0xff) + ((size & ~0xffUL) << 2);
|
size = (size & 0xff) + ((size & ~0xffUL) << 2);
|
||||||
#endif
|
#endif
|
||||||
size = ALIGN(size + size1, align);
|
size = size + size1;
|
||||||
if (size < old_size)
|
if (size < old_size)
|
||||||
size = old_size;
|
size = old_size;
|
||||||
|
|
||||||
|
size = ALIGN(max(size, add_size) + children_add_size, align);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static resource_size_t calculate_memsize(resource_size_t size,
|
static resource_size_t calculate_memsize(resource_size_t size,
|
||||||
resource_size_t min_size,
|
resource_size_t min_size,
|
||||||
resource_size_t size1,
|
resource_size_t add_size,
|
||||||
|
resource_size_t children_add_size,
|
||||||
resource_size_t old_size,
|
resource_size_t old_size,
|
||||||
resource_size_t align)
|
resource_size_t align)
|
||||||
{
|
{
|
||||||
@@ -841,7 +846,8 @@ static resource_size_t calculate_memsize(resource_size_t size,
|
|||||||
old_size = 0;
|
old_size = 0;
|
||||||
if (size < old_size)
|
if (size < old_size)
|
||||||
size = old_size;
|
size = old_size;
|
||||||
size = ALIGN(size + size1, align);
|
|
||||||
|
size = ALIGN(max(size, add_size) + children_add_size, align);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,12 +936,10 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size0 = calculate_iosize(size, min_size, size1,
|
size0 = calculate_iosize(size, min_size, size1, 0, 0,
|
||||||
resource_size(b_res), min_align);
|
resource_size(b_res), min_align);
|
||||||
if (children_add_size > add_size)
|
size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 :
|
||||||
add_size = children_add_size;
|
calculate_iosize(size, min_size, size1, add_size, children_add_size,
|
||||||
size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
|
|
||||||
calculate_iosize(size, min_size, add_size + size1,
|
|
||||||
resource_size(b_res), min_align);
|
resource_size(b_res), min_align);
|
||||||
if (!size0 && !size1) {
|
if (!size0 && !size1) {
|
||||||
if (b_res->start || b_res->end)
|
if (b_res->start || b_res->end)
|
||||||
@@ -1079,12 +1083,10 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|||||||
|
|
||||||
min_align = calculate_mem_align(aligns, max_order);
|
min_align = calculate_mem_align(aligns, max_order);
|
||||||
min_align = max(min_align, window_alignment(bus, b_res->flags));
|
min_align = max(min_align, window_alignment(bus, b_res->flags));
|
||||||
size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
|
size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align);
|
||||||
add_align = max(min_align, add_align);
|
add_align = max(min_align, add_align);
|
||||||
if (children_add_size > add_size)
|
size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 :
|
||||||
add_size = children_add_size;
|
calculate_memsize(size, min_size, add_size, children_add_size,
|
||||||
size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
|
|
||||||
calculate_memsize(size, min_size, add_size,
|
|
||||||
resource_size(b_res), add_align);
|
resource_size(b_res), add_align);
|
||||||
if (!size0 && !size1) {
|
if (!size0 && !size1) {
|
||||||
if (b_res->start || b_res->end)
|
if (b_res->start || b_res->end)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
struct kset *pci_slots_kset;
|
struct kset *pci_slots_kset;
|
||||||
EXPORT_SYMBOL_GPL(pci_slots_kset);
|
EXPORT_SYMBOL_GPL(pci_slots_kset);
|
||||||
static DEFINE_MUTEX(pci_slot_mutex);
|
|
||||||
|
|
||||||
static ssize_t pci_slot_attr_show(struct kobject *kobj,
|
static ssize_t pci_slot_attr_show(struct kobject *kobj,
|
||||||
struct attribute *attr, char *buf)
|
struct attribute *attr, char *buf)
|
||||||
@@ -371,7 +370,7 @@ void pci_hp_create_module_link(struct pci_slot *pci_slot)
|
|||||||
|
|
||||||
if (!slot || !slot->ops)
|
if (!slot || !slot->ops)
|
||||||
return;
|
return;
|
||||||
kobj = kset_find_obj(module_kset, slot->ops->mod_name);
|
kobj = kset_find_obj(module_kset, slot->mod_name);
|
||||||
if (!kobj)
|
if (!kobj)
|
||||||
return;
|
return;
|
||||||
ret = sysfs_create_link(&pci_slot->kobj, kobj, "module");
|
ret = sysfs_create_link(&pci_slot->kobj, kobj, "module");
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ struct asus_wmi {
|
|||||||
int asus_hwmon_num_fans;
|
int asus_hwmon_num_fans;
|
||||||
int asus_hwmon_pwm;
|
int asus_hwmon_pwm;
|
||||||
|
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
struct mutex hotplug_lock;
|
struct mutex hotplug_lock;
|
||||||
struct mutex wmi_lock;
|
struct mutex wmi_lock;
|
||||||
struct workqueue_struct *hotplug_workqueue;
|
struct workqueue_struct *hotplug_workqueue;
|
||||||
@@ -753,7 +753,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)
|
|||||||
if (asus->wlan.rfkill)
|
if (asus->wlan.rfkill)
|
||||||
rfkill_set_sw_state(asus->wlan.rfkill, blocked);
|
rfkill_set_sw_state(asus->wlan.rfkill, blocked);
|
||||||
|
|
||||||
if (asus->hotplug_slot) {
|
if (asus->hotplug_slot.ops) {
|
||||||
bus = pci_find_bus(0, 1);
|
bus = pci_find_bus(0, 1);
|
||||||
if (!bus) {
|
if (!bus) {
|
||||||
pr_warn("Unable to find PCI bus 1?\n");
|
pr_warn("Unable to find PCI bus 1?\n");
|
||||||
@@ -858,7 +858,8 @@ static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node)
|
|||||||
static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
|
static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
|
||||||
u8 *value)
|
u8 *value)
|
||||||
{
|
{
|
||||||
struct asus_wmi *asus = hotplug_slot->private;
|
struct asus_wmi *asus = container_of(hotplug_slot,
|
||||||
|
struct asus_wmi, hotplug_slot);
|
||||||
int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
|
int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
|
||||||
|
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
@@ -868,8 +869,7 @@ static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hotplug_slot_ops asus_hotplug_slot_ops = {
|
static const struct hotplug_slot_ops asus_hotplug_slot_ops = {
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.get_adapter_status = asus_get_adapter_status,
|
.get_adapter_status = asus_get_adapter_status,
|
||||||
.get_power_status = asus_get_adapter_status,
|
.get_power_status = asus_get_adapter_status,
|
||||||
};
|
};
|
||||||
@@ -899,21 +899,9 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus)
|
|||||||
|
|
||||||
INIT_WORK(&asus->hotplug_work, asus_hotplug_work);
|
INIT_WORK(&asus->hotplug_work, asus_hotplug_work);
|
||||||
|
|
||||||
asus->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
|
asus->hotplug_slot.ops = &asus_hotplug_slot_ops;
|
||||||
if (!asus->hotplug_slot)
|
|
||||||
goto error_slot;
|
|
||||||
|
|
||||||
asus->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
|
ret = pci_hp_register(&asus->hotplug_slot, bus, 0, "asus-wifi");
|
||||||
GFP_KERNEL);
|
|
||||||
if (!asus->hotplug_slot->info)
|
|
||||||
goto error_info;
|
|
||||||
|
|
||||||
asus->hotplug_slot->private = asus;
|
|
||||||
asus->hotplug_slot->ops = &asus_hotplug_slot_ops;
|
|
||||||
asus_get_adapter_status(asus->hotplug_slot,
|
|
||||||
&asus->hotplug_slot->info->adapter_status);
|
|
||||||
|
|
||||||
ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi");
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("Unable to register hotplug slot - %d\n", ret);
|
pr_err("Unable to register hotplug slot - %d\n", ret);
|
||||||
goto error_register;
|
goto error_register;
|
||||||
@@ -922,11 +910,7 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_register:
|
error_register:
|
||||||
kfree(asus->hotplug_slot->info);
|
asus->hotplug_slot.ops = NULL;
|
||||||
error_info:
|
|
||||||
kfree(asus->hotplug_slot);
|
|
||||||
asus->hotplug_slot = NULL;
|
|
||||||
error_slot:
|
|
||||||
destroy_workqueue(asus->hotplug_workqueue);
|
destroy_workqueue(asus->hotplug_workqueue);
|
||||||
error_workqueue:
|
error_workqueue:
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1054,11 +1038,8 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
|
|||||||
* asus_unregister_rfkill_notifier()
|
* asus_unregister_rfkill_notifier()
|
||||||
*/
|
*/
|
||||||
asus_rfkill_hotplug(asus);
|
asus_rfkill_hotplug(asus);
|
||||||
if (asus->hotplug_slot) {
|
if (asus->hotplug_slot.ops)
|
||||||
pci_hp_deregister(asus->hotplug_slot);
|
pci_hp_deregister(&asus->hotplug_slot);
|
||||||
kfree(asus->hotplug_slot->info);
|
|
||||||
kfree(asus->hotplug_slot);
|
|
||||||
}
|
|
||||||
if (asus->hotplug_workqueue)
|
if (asus->hotplug_workqueue)
|
||||||
destroy_workqueue(asus->hotplug_workqueue);
|
destroy_workqueue(asus->hotplug_workqueue);
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ struct eeepc_laptop {
|
|||||||
struct rfkill *wwan3g_rfkill;
|
struct rfkill *wwan3g_rfkill;
|
||||||
struct rfkill *wimax_rfkill;
|
struct rfkill *wimax_rfkill;
|
||||||
|
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot hotplug_slot;
|
||||||
struct mutex hotplug_lock;
|
struct mutex hotplug_lock;
|
||||||
|
|
||||||
struct led_classdev tpd_led;
|
struct led_classdev tpd_led;
|
||||||
@@ -582,7 +582,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
|
|||||||
mutex_lock(&eeepc->hotplug_lock);
|
mutex_lock(&eeepc->hotplug_lock);
|
||||||
pci_lock_rescan_remove();
|
pci_lock_rescan_remove();
|
||||||
|
|
||||||
if (!eeepc->hotplug_slot)
|
if (!eeepc->hotplug_slot.ops)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
port = acpi_get_pci_dev(handle);
|
port = acpi_get_pci_dev(handle);
|
||||||
@@ -715,8 +715,11 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
|
|||||||
static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
|
static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
|
||||||
u8 *value)
|
u8 *value)
|
||||||
{
|
{
|
||||||
struct eeepc_laptop *eeepc = hotplug_slot->private;
|
struct eeepc_laptop *eeepc;
|
||||||
int val = get_acpi(eeepc, CM_ASL_WLAN);
|
int val;
|
||||||
|
|
||||||
|
eeepc = container_of(hotplug_slot, struct eeepc_laptop, hotplug_slot);
|
||||||
|
val = get_acpi(eeepc, CM_ASL_WLAN);
|
||||||
|
|
||||||
if (val == 1 || val == 0)
|
if (val == 1 || val == 0)
|
||||||
*value = val;
|
*value = val;
|
||||||
@@ -726,8 +729,7 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
|
static const struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.get_adapter_status = eeepc_get_adapter_status,
|
.get_adapter_status = eeepc_get_adapter_status,
|
||||||
.get_power_status = eeepc_get_adapter_status,
|
.get_power_status = eeepc_get_adapter_status,
|
||||||
};
|
};
|
||||||
@@ -742,21 +744,9 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
|
eeepc->hotplug_slot.ops = &eeepc_hotplug_slot_ops;
|
||||||
if (!eeepc->hotplug_slot)
|
|
||||||
goto error_slot;
|
|
||||||
|
|
||||||
eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
|
ret = pci_hp_register(&eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
|
||||||
GFP_KERNEL);
|
|
||||||
if (!eeepc->hotplug_slot->info)
|
|
||||||
goto error_info;
|
|
||||||
|
|
||||||
eeepc->hotplug_slot->private = eeepc;
|
|
||||||
eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
|
|
||||||
eeepc_get_adapter_status(eeepc->hotplug_slot,
|
|
||||||
&eeepc->hotplug_slot->info->adapter_status);
|
|
||||||
|
|
||||||
ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("Unable to register hotplug slot - %d\n", ret);
|
pr_err("Unable to register hotplug slot - %d\n", ret);
|
||||||
goto error_register;
|
goto error_register;
|
||||||
@@ -765,11 +755,7 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_register:
|
error_register:
|
||||||
kfree(eeepc->hotplug_slot->info);
|
eeepc->hotplug_slot.ops = NULL;
|
||||||
error_info:
|
|
||||||
kfree(eeepc->hotplug_slot);
|
|
||||||
eeepc->hotplug_slot = NULL;
|
|
||||||
error_slot:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -830,11 +816,8 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
|
|||||||
eeepc->wlan_rfkill = NULL;
|
eeepc->wlan_rfkill = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eeepc->hotplug_slot) {
|
if (eeepc->hotplug_slot.ops)
|
||||||
pci_hp_deregister(eeepc->hotplug_slot);
|
pci_hp_deregister(&eeepc->hotplug_slot);
|
||||||
kfree(eeepc->hotplug_slot->info);
|
|
||||||
kfree(eeepc->hotplug_slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eeepc->bluetooth_rfkill) {
|
if (eeepc->bluetooth_rfkill) {
|
||||||
rfkill_unregister(eeepc->bluetooth_rfkill);
|
rfkill_unregister(eeepc->bluetooth_rfkill);
|
||||||
|
|||||||
@@ -2055,8 +2055,6 @@ static void aac_pci_resume(struct pci_dev *pdev)
|
|||||||
struct scsi_device *sdev = NULL;
|
struct scsi_device *sdev = NULL;
|
||||||
struct aac_dev *aac = (struct aac_dev *)shost_priv(shost);
|
struct aac_dev *aac = (struct aac_dev *)shost_priv(shost);
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
|
|
||||||
if (aac_adapter_ioremap(aac, aac->base_size)) {
|
if (aac_adapter_ioremap(aac, aac->base_size)) {
|
||||||
|
|
||||||
dev_err(&pdev->dev, "aacraid: ioremap failed\n");
|
dev_err(&pdev->dev, "aacraid: ioremap failed\n");
|
||||||
|
|||||||
@@ -5529,7 +5529,6 @@ static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev)
|
|||||||
return PCI_ERS_RESULT_DISCONNECT;
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
return PCI_ERS_RESULT_RECOVERED;
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1569,8 +1569,6 @@ bfad_pci_slot_reset(struct pci_dev *pdev)
|
|||||||
if (pci_set_dma_mask(bfad->pcidev, DMA_BIT_MASK(32)) != 0)
|
if (pci_set_dma_mask(bfad->pcidev, DMA_BIT_MASK(32)) != 0)
|
||||||
goto out_disable_device;
|
goto out_disable_device;
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
|
|
||||||
if (restart_bfa(bfad) == -1)
|
if (restart_bfa(bfad) == -1)
|
||||||
goto out_disable_device;
|
goto out_disable_device;
|
||||||
|
|
||||||
|
|||||||
@@ -1102,7 +1102,6 @@ csio_pci_slot_reset(struct pci_dev *pdev)
|
|||||||
pci_set_master(pdev);
|
pci_set_master(pdev);
|
||||||
pci_restore_state(pdev);
|
pci_restore_state(pdev);
|
||||||
pci_save_state(pdev);
|
pci_save_state(pdev);
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
|
|
||||||
/* Bring HW s/m to ready state.
|
/* Bring HW s/m to ready state.
|
||||||
* but don't resume IOs.
|
* but don't resume IOs.
|
||||||
|
|||||||
@@ -11329,10 +11329,6 @@ lpfc_io_resume_s3(struct pci_dev *pdev)
|
|||||||
|
|
||||||
/* Bring device online, it will be no-op for non-fatal error resume */
|
/* Bring device online, it will be no-op for non-fatal error resume */
|
||||||
lpfc_online(phba);
|
lpfc_online(phba);
|
||||||
|
|
||||||
/* Clean up Advanced Error Reporting (AER) if needed */
|
|
||||||
if (phba->hba_flag & HBA_AER_ENABLED)
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12144,10 +12140,6 @@ lpfc_io_resume_s4(struct pci_dev *pdev)
|
|||||||
/* Bring the device back online */
|
/* Bring the device back online */
|
||||||
lpfc_online(phba);
|
lpfc_online(phba);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clean up Advanced Error Reporting (AER) if needed */
|
|
||||||
if (phba->hba_flag & HBA_AER_ENABLED)
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10828,7 +10828,6 @@ scsih_pci_resume(struct pci_dev *pdev)
|
|||||||
|
|
||||||
pr_info(MPT3SAS_FMT "PCI error: resume callback!!\n", ioc->name);
|
pr_info(MPT3SAS_FMT "PCI error: resume callback!!\n", ioc->name);
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
mpt3sas_base_start_watchdog(ioc);
|
mpt3sas_base_start_watchdog(ioc);
|
||||||
scsi_unblock_requests(ioc->shost);
|
scsi_unblock_requests(ioc->shost);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6839,8 +6839,6 @@ qla2xxx_pci_resume(struct pci_dev *pdev)
|
|||||||
"The device failed to resume I/O from slot/link_reset.\n");
|
"The device failed to resume I/O from slot/link_reset.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
|
|
||||||
ha->flags.eeh_busy = 0;
|
ha->flags.eeh_busy = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9824,7 +9824,6 @@ qla4xxx_pci_resume(struct pci_dev *pdev)
|
|||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
||||||
clear_bit(AF_EEH_BUSY, &ha->flags);
|
clear_bit(AF_EEH_BUSY, &ha->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -346,10 +346,16 @@ struct acpi_device_physical_node {
|
|||||||
bool put_online:1;
|
bool put_online:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct acpi_device_properties {
|
||||||
|
const guid_t *guid;
|
||||||
|
const union acpi_object *properties;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
/* ACPI Device Specific Data (_DSD) */
|
/* ACPI Device Specific Data (_DSD) */
|
||||||
struct acpi_device_data {
|
struct acpi_device_data {
|
||||||
const union acpi_object *pointer;
|
const union acpi_object *pointer;
|
||||||
const union acpi_object *properties;
|
struct list_head properties;
|
||||||
const union acpi_object *of_compatible;
|
const union acpi_object *of_compatible;
|
||||||
struct list_head subnodes;
|
struct list_head subnodes;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1074,6 +1074,15 @@ static inline int acpi_node_get_property_reference(
|
|||||||
NR_FWNODE_REFERENCE_ARGS, args);
|
NR_FWNODE_REFERENCE_ARGS, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool acpi_dev_has_props(const struct acpi_device *adev)
|
||||||
|
{
|
||||||
|
return !list_empty(&adev->data.properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct acpi_device_properties *
|
||||||
|
acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid,
|
||||||
|
const union acpi_object *properties);
|
||||||
|
|
||||||
int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname,
|
int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname,
|
||||||
void **valptr);
|
void **valptr);
|
||||||
int acpi_dev_prop_read_single(struct acpi_device *adev,
|
int acpi_dev_prop_read_single(struct acpi_device *adev,
|
||||||
|
|||||||
@@ -403,6 +403,7 @@ struct pci_dev {
|
|||||||
unsigned int has_secondary_link:1;
|
unsigned int has_secondary_link:1;
|
||||||
unsigned int non_compliant_bars:1; /* Broken BARs; ignore them */
|
unsigned int non_compliant_bars:1; /* Broken BARs; ignore them */
|
||||||
unsigned int is_probed:1; /* Device probing in progress */
|
unsigned int is_probed:1; /* Device probing in progress */
|
||||||
|
unsigned int link_active_reporting:1;/* Device capable of reporting link active */
|
||||||
pci_dev_flags_t dev_flags;
|
pci_dev_flags_t dev_flags;
|
||||||
atomic_t enable_cnt; /* pci_enable_device has been called */
|
atomic_t enable_cnt; /* pci_enable_device has been called */
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* struct hotplug_slot_ops -the callbacks that the hotplug pci core can use
|
* struct hotplug_slot_ops -the callbacks that the hotplug pci core can use
|
||||||
* @owner: The module owner of this structure
|
|
||||||
* @mod_name: The module name (KBUILD_MODNAME) of this structure
|
|
||||||
* @enable_slot: Called when the user wants to enable a specific pci slot
|
* @enable_slot: Called when the user wants to enable a specific pci slot
|
||||||
* @disable_slot: Called when the user wants to disable a specific pci slot
|
* @disable_slot: Called when the user wants to disable a specific pci slot
|
||||||
* @set_attention_status: Called to set the specific slot's attention LED to
|
* @set_attention_status: Called to set the specific slot's attention LED to
|
||||||
@@ -25,17 +23,9 @@
|
|||||||
* @hardware_test: Called to run a specified hardware test on the specified
|
* @hardware_test: Called to run a specified hardware test on the specified
|
||||||
* slot.
|
* slot.
|
||||||
* @get_power_status: Called to get the current power status of a slot.
|
* @get_power_status: Called to get the current power status of a slot.
|
||||||
* If this field is NULL, the value passed in the struct hotplug_slot_info
|
|
||||||
* will be used when this value is requested by a user.
|
|
||||||
* @get_attention_status: Called to get the current attention status of a slot.
|
* @get_attention_status: Called to get the current attention status of a slot.
|
||||||
* If this field is NULL, the value passed in the struct hotplug_slot_info
|
|
||||||
* will be used when this value is requested by a user.
|
|
||||||
* @get_latch_status: Called to get the current latch status of a slot.
|
* @get_latch_status: Called to get the current latch status of a slot.
|
||||||
* If this field is NULL, the value passed in the struct hotplug_slot_info
|
|
||||||
* will be used when this value is requested by a user.
|
|
||||||
* @get_adapter_status: Called to get see if an adapter is present in the slot or not.
|
* @get_adapter_status: Called to get see if an adapter is present in the slot or not.
|
||||||
* If this field is NULL, the value passed in the struct hotplug_slot_info
|
|
||||||
* will be used when this value is requested by a user.
|
|
||||||
* @reset_slot: Optional interface to allow override of a bus reset for the
|
* @reset_slot: Optional interface to allow override of a bus reset for the
|
||||||
* slot for cases where a secondary bus reset can result in spurious
|
* slot for cases where a secondary bus reset can result in spurious
|
||||||
* hotplug events or where a slot can be reset independent of the bus.
|
* hotplug events or where a slot can be reset independent of the bus.
|
||||||
@@ -46,8 +36,6 @@
|
|||||||
* set an LED, enable / disable power, etc.)
|
* set an LED, enable / disable power, etc.)
|
||||||
*/
|
*/
|
||||||
struct hotplug_slot_ops {
|
struct hotplug_slot_ops {
|
||||||
struct module *owner;
|
|
||||||
const char *mod_name;
|
|
||||||
int (*enable_slot) (struct hotplug_slot *slot);
|
int (*enable_slot) (struct hotplug_slot *slot);
|
||||||
int (*disable_slot) (struct hotplug_slot *slot);
|
int (*disable_slot) (struct hotplug_slot *slot);
|
||||||
int (*set_attention_status) (struct hotplug_slot *slot, u8 value);
|
int (*set_attention_status) (struct hotplug_slot *slot, u8 value);
|
||||||
@@ -59,38 +47,20 @@ struct hotplug_slot_ops {
|
|||||||
int (*reset_slot) (struct hotplug_slot *slot, int probe);
|
int (*reset_slot) (struct hotplug_slot *slot, int probe);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot
|
|
||||||
* @power_status: if power is enabled or not (1/0)
|
|
||||||
* @attention_status: if the attention light is enabled or not (1/0)
|
|
||||||
* @latch_status: if the latch (if any) is open or closed (1/0)
|
|
||||||
* @adapter_status: if there is a pci board present in the slot or not (1/0)
|
|
||||||
*
|
|
||||||
* Used to notify the hotplug pci core of the status of a specific slot.
|
|
||||||
*/
|
|
||||||
struct hotplug_slot_info {
|
|
||||||
u8 power_status;
|
|
||||||
u8 attention_status;
|
|
||||||
u8 latch_status;
|
|
||||||
u8 adapter_status;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct hotplug_slot - used to register a physical slot with the hotplug pci core
|
* struct hotplug_slot - used to register a physical slot with the hotplug pci core
|
||||||
* @ops: pointer to the &struct hotplug_slot_ops to be used for this slot
|
* @ops: pointer to the &struct hotplug_slot_ops to be used for this slot
|
||||||
* @info: pointer to the &struct hotplug_slot_info for the initial values for
|
* @owner: The module owner of this structure
|
||||||
* this slot.
|
* @mod_name: The module name (KBUILD_MODNAME) of this structure
|
||||||
* @private: used by the hotplug pci controller driver to store whatever it
|
|
||||||
* needs.
|
|
||||||
*/
|
*/
|
||||||
struct hotplug_slot {
|
struct hotplug_slot {
|
||||||
struct hotplug_slot_ops *ops;
|
const struct hotplug_slot_ops *ops;
|
||||||
struct hotplug_slot_info *info;
|
|
||||||
void *private;
|
|
||||||
|
|
||||||
/* Variables below this are for use only by the hotplug pci core. */
|
/* Variables below this are for use only by the hotplug pci core. */
|
||||||
struct list_head slot_list;
|
struct list_head slot_list;
|
||||||
struct pci_slot *pci_slot;
|
struct pci_slot *pci_slot;
|
||||||
|
struct module *owner;
|
||||||
|
const char *mod_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline const char *hotplug_slot_name(const struct hotplug_slot *slot)
|
static inline const char *hotplug_slot_name(const struct hotplug_slot *slot)
|
||||||
@@ -110,9 +80,6 @@ void pci_hp_del(struct hotplug_slot *slot);
|
|||||||
void pci_hp_destroy(struct hotplug_slot *slot);
|
void pci_hp_destroy(struct hotplug_slot *slot);
|
||||||
void pci_hp_deregister(struct hotplug_slot *slot);
|
void pci_hp_deregister(struct hotplug_slot *slot);
|
||||||
|
|
||||||
int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
|
|
||||||
struct hotplug_slot_info *info);
|
|
||||||
|
|
||||||
/* use a define to avoid include chaining to get THIS_MODULE & friends */
|
/* use a define to avoid include chaining to get THIS_MODULE & friends */
|
||||||
#define pci_hp_register(slot, pbus, devnr, name) \
|
#define pci_hp_register(slot, pbus, devnr, name) \
|
||||||
__pci_hp_register(slot, pbus, devnr, name, THIS_MODULE, KBUILD_MODNAME)
|
__pci_hp_register(slot, pbus, devnr, name, THIS_MODULE, KBUILD_MODNAME)
|
||||||
|
|||||||
Reference in New Issue
Block a user