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:
Bjorn Helgaas
2018-10-20 11:45:29 -05:00
84 changed files with 1382 additions and 1746 deletions

View File

@@ -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
--------------------------- ---------------------------

View File

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

View File

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

View File

@@ -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);

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 = {

View File

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

View File

@@ -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))

View File

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

View File

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

View File

@@ -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 = {

View File

@@ -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
View 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.

View File

@@ -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);
} }
/* /*

View File

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

View File

@@ -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

View File

@@ -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);

View File

@@ -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:

View File

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

View File

@@ -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);
} }
/* /*

View File

@@ -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:

View File

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

View File

@@ -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

View File

@@ -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,

View File

@@ -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);

View File

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

View File

@@ -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 */

View File

@@ -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);

View File

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

View File

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

View File

@@ -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

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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,

View File

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

View File

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

View File

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

View File

@@ -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);

View File

@@ -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)

View File

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

View File

@@ -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;
} }
/* /*

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

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

View File

@@ -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);

View File

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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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)

View File

@@ -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");

View File

@@ -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);

View File

@@ -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);

View File

@@ -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");

View File

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

View File

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

View File

@@ -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.

View File

@@ -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);
} }
/** /**

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,

View File

@@ -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 */

View File

@@ -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)