|
@@ -66,6 +66,7 @@
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/pci.h>
|
|
|
#include <linux/pci-aspm.h>
|
|
|
+#include <linux/acpi.h>
|
|
|
|
|
|
#include "iwl-trans.h"
|
|
|
#include "iwl-drv.h"
|
|
@@ -395,6 +396,81 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
|
|
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
|
+#define SPL_METHOD "SPLC"
|
|
|
+#define SPL_DOMAINTYPE_MODULE BIT(0)
|
|
|
+#define SPL_DOMAINTYPE_WIFI BIT(1)
|
|
|
+#define SPL_DOMAINTYPE_WIGIG BIT(2)
|
|
|
+#define SPL_DOMAINTYPE_RFEM BIT(3)
|
|
|
+
|
|
|
+static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
|
|
|
+{
|
|
|
+ union acpi_object *limits, *domain_type, *power_limit;
|
|
|
+
|
|
|
+ if (splx->type != ACPI_TYPE_PACKAGE ||
|
|
|
+ splx->package.count != 2 ||
|
|
|
+ splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
|
|
|
+ splx->package.elements[0].integer.value != 0) {
|
|
|
+ IWL_ERR(trans, "Unsupported splx structure");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ limits = &splx->package.elements[1];
|
|
|
+ if (limits->type != ACPI_TYPE_PACKAGE ||
|
|
|
+ limits->package.count < 2 ||
|
|
|
+ limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
|
|
|
+ limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
|
|
|
+ IWL_ERR(trans, "Invalid limits element");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ domain_type = &limits->package.elements[0];
|
|
|
+ power_limit = &limits->package.elements[1];
|
|
|
+ if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
|
|
|
+ IWL_DEBUG_INFO(trans, "WiFi power is not limited");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return power_limit->integer.value;
|
|
|
+}
|
|
|
+
|
|
|
+static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
|
|
|
+{
|
|
|
+ acpi_handle pxsx_handle;
|
|
|
+ acpi_handle handle;
|
|
|
+ struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
|
|
|
+ acpi_status status;
|
|
|
+
|
|
|
+ pxsx_handle = ACPI_HANDLE(&pdev->dev);
|
|
|
+ if (!pxsx_handle) {
|
|
|
+ IWL_ERR(trans, "Could not retrieve root port ACPI handle");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Get the method's handle */
|
|
|
+ status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
+ IWL_DEBUG_INFO(trans, "SPL method not found");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Call SPLC with no arguments */
|
|
|
+ status = acpi_evaluate_object(handle, NULL, NULL, &splx);
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
+ IWL_ERR(trans, "SPLC invocation failed (0x%x)", status);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
|
|
|
+ IWL_DEBUG_INFO(trans, "Default power limit set to %lld",
|
|
|
+ trans->dflt_pwr_limit);
|
|
|
+ kfree(splx.pointer);
|
|
|
+}
|
|
|
+
|
|
|
+#else /* CONFIG_ACPI */
|
|
|
+static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {}
|
|
|
+#endif
|
|
|
+
|
|
|
/* PCI registers */
|
|
|
#define PCI_CFG_RETRY_TIMEOUT 0x041
|
|
|
|
|
@@ -419,6 +495,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
|
goto out_free_trans;
|
|
|
}
|
|
|
|
|
|
+ set_dflt_pwr_limit(iwl_trans, pdev);
|
|
|
+
|
|
|
/* register transport layer debugfs here */
|
|
|
ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir);
|
|
|
if (ret)
|