|
@@ -245,6 +245,13 @@ struct i801_priv {
|
|
|
struct platform_device *mux_pdev;
|
|
|
#endif
|
|
|
struct platform_device *tco_pdev;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If set to true the host controller registers are reserved for
|
|
|
+ * ACPI AML use. Protected by acpi_lock.
|
|
|
+ */
|
|
|
+ bool acpi_reserved;
|
|
|
+ struct mutex acpi_lock;
|
|
|
};
|
|
|
|
|
|
#define FEATURE_SMBUS_PEC (1 << 0)
|
|
@@ -718,6 +725,12 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
|
|
int ret = 0, xact = 0;
|
|
|
struct i801_priv *priv = i2c_get_adapdata(adap);
|
|
|
|
|
|
+ mutex_lock(&priv->acpi_lock);
|
|
|
+ if (priv->acpi_reserved) {
|
|
|
+ mutex_unlock(&priv->acpi_lock);
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
pm_runtime_get_sync(&priv->pci_dev->dev);
|
|
|
|
|
|
hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
|
|
@@ -820,6 +833,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
|
|
out:
|
|
|
pm_runtime_mark_last_busy(&priv->pci_dev->dev);
|
|
|
pm_runtime_put_autosuspend(&priv->pci_dev->dev);
|
|
|
+ mutex_unlock(&priv->acpi_lock);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1257,6 +1271,83 @@ static void i801_add_tco(struct i801_priv *priv)
|
|
|
priv->tco_pdev = pdev;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
|
+static acpi_status
|
|
|
+i801_acpi_io_handler(u32 function, acpi_physical_address address, u32 bits,
|
|
|
+ u64 *value, void *handler_context, void *region_context)
|
|
|
+{
|
|
|
+ struct i801_priv *priv = handler_context;
|
|
|
+ struct pci_dev *pdev = priv->pci_dev;
|
|
|
+ acpi_status status;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Once BIOS AML code touches the OpRegion we warn and inhibit any
|
|
|
+ * further access from the driver itself. This device is now owned
|
|
|
+ * by the system firmware.
|
|
|
+ */
|
|
|
+ mutex_lock(&priv->acpi_lock);
|
|
|
+
|
|
|
+ if (!priv->acpi_reserved) {
|
|
|
+ priv->acpi_reserved = true;
|
|
|
+
|
|
|
+ dev_warn(&pdev->dev, "BIOS is accessing SMBus registers\n");
|
|
|
+ dev_warn(&pdev->dev, "Driver SMBus register access inhibited\n");
|
|
|
+
|
|
|
+ /*
|
|
|
+ * BIOS is accessing the host controller so prevent it from
|
|
|
+ * suspending automatically from now on.
|
|
|
+ */
|
|
|
+ pm_runtime_get_sync(&pdev->dev);
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((function & ACPI_IO_MASK) == ACPI_READ)
|
|
|
+ status = acpi_os_read_port(address, (u32 *)value, bits);
|
|
|
+ else
|
|
|
+ status = acpi_os_write_port(address, (u32)*value, bits);
|
|
|
+
|
|
|
+ mutex_unlock(&priv->acpi_lock);
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+static int i801_acpi_probe(struct i801_priv *priv)
|
|
|
+{
|
|
|
+ struct acpi_device *adev;
|
|
|
+ acpi_status status;
|
|
|
+
|
|
|
+ adev = ACPI_COMPANION(&priv->pci_dev->dev);
|
|
|
+ if (adev) {
|
|
|
+ status = acpi_install_address_space_handler(adev->handle,
|
|
|
+ ACPI_ADR_SPACE_SYSTEM_IO, i801_acpi_io_handler,
|
|
|
+ NULL, priv);
|
|
|
+ if (ACPI_SUCCESS(status))
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return acpi_check_resource_conflict(&priv->pci_dev->resource[SMBBAR]);
|
|
|
+}
|
|
|
+
|
|
|
+static void i801_acpi_remove(struct i801_priv *priv)
|
|
|
+{
|
|
|
+ struct acpi_device *adev;
|
|
|
+
|
|
|
+ adev = ACPI_COMPANION(&priv->pci_dev->dev);
|
|
|
+ if (!adev)
|
|
|
+ return;
|
|
|
+
|
|
|
+ acpi_remove_address_space_handler(adev->handle,
|
|
|
+ ACPI_ADR_SPACE_SYSTEM_IO, i801_acpi_io_handler);
|
|
|
+
|
|
|
+ mutex_lock(&priv->acpi_lock);
|
|
|
+ if (priv->acpi_reserved)
|
|
|
+ pm_runtime_put(&priv->pci_dev->dev);
|
|
|
+ mutex_unlock(&priv->acpi_lock);
|
|
|
+}
|
|
|
+#else
|
|
|
+static inline int i801_acpi_probe(struct i801_priv *priv) { return 0; }
|
|
|
+static inline void i801_acpi_remove(struct i801_priv *priv) { }
|
|
|
+#endif
|
|
|
+
|
|
|
static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|
|
{
|
|
|
unsigned char temp;
|
|
@@ -1274,6 +1365,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|
|
priv->adapter.dev.parent = &dev->dev;
|
|
|
ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&dev->dev));
|
|
|
priv->adapter.retries = 3;
|
|
|
+ mutex_init(&priv->acpi_lock);
|
|
|
|
|
|
priv->pci_dev = dev;
|
|
|
switch (dev->device) {
|
|
@@ -1336,10 +1428,8 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
- err = acpi_check_resource_conflict(&dev->resource[SMBBAR]);
|
|
|
- if (err) {
|
|
|
+ if (i801_acpi_probe(priv))
|
|
|
return -ENODEV;
|
|
|
- }
|
|
|
|
|
|
err = pcim_iomap_regions(dev, 1 << SMBBAR,
|
|
|
dev_driver_string(&dev->dev));
|
|
@@ -1348,6 +1438,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|
|
"Failed to request SMBus region 0x%lx-0x%Lx\n",
|
|
|
priv->smba,
|
|
|
(unsigned long long)pci_resource_end(dev, SMBBAR));
|
|
|
+ i801_acpi_remove(priv);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -1412,6 +1503,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|
|
err = i2c_add_adapter(&priv->adapter);
|
|
|
if (err) {
|
|
|
dev_err(&dev->dev, "Failed to add SMBus adapter\n");
|
|
|
+ i801_acpi_remove(priv);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -1438,6 +1530,7 @@ static void i801_remove(struct pci_dev *dev)
|
|
|
|
|
|
i801_del_mux(priv);
|
|
|
i2c_del_adapter(&priv->adapter);
|
|
|
+ i801_acpi_remove(priv);
|
|
|
pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
|
|
|
|
|
|
platform_device_unregister(priv->tco_pdev);
|