diff options
Diffstat (limited to 'drivers/misc/mei/pci-me.c')
| -rw-r--r-- | drivers/misc/mei/pci-me.c | 283 | 
1 files changed, 216 insertions, 67 deletions
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 1b3844e8237..1b46c64a649 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -13,9 +13,6 @@   * more details.   *   */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -  #include <linux/module.h>  #include <linux/moduleparam.h>  #include <linux/kernel.h> @@ -27,7 +24,6 @@  #include <linux/aio.h>  #include <linux/pci.h>  #include <linux/poll.h> -#include <linux/init.h>  #include <linux/ioctl.h>  #include <linux/cdev.h>  #include <linux/sched.h> @@ -37,47 +33,55 @@  #include <linux/interrupt.h>  #include <linux/miscdevice.h> +#include <linux/pm_runtime.h> +  #include <linux/mei.h>  #include "mei_dev.h" -#include "hw-me.h"  #include "client.h" +#include "hw-me-regs.h" +#include "hw-me.h"  /* mei_pci_tbl - PCI Device ID Table */ -static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = { -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)}, -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)}, +static const struct pci_device_id mei_me_pci_tbl[] = { +	{MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_82G35, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_82G965, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, mei_me_legacy_cfg)}, + +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, mei_me_legacy_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, mei_me_ich_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, mei_me_ich_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, mei_me_ich_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, mei_me_ich_cfg)}, + +	{MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, mei_me_pch_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, mei_me_pch_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, mei_me_pch_cpt_pbg_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, mei_me_pch_cpt_pbg_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, mei_me_pch_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, mei_me_pch_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, mei_me_pch_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, mei_me_lpt_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, mei_me_lpt_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_lpt_cfg)}, +	{MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch_cfg)},  	/* required last entry */  	{0, } @@ -85,28 +89,33 @@ static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = {  MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl); +#ifdef CONFIG_PM_RUNTIME +static inline void mei_me_set_pm_domain(struct mei_device *dev); +static inline void mei_me_unset_pm_domain(struct mei_device *dev); +#else +static inline void mei_me_set_pm_domain(struct mei_device *dev) {} +static inline void mei_me_unset_pm_domain(struct mei_device *dev) {} +#endif /* CONFIG_PM_RUNTIME */ +  /**   * mei_quirk_probe - probe for devices that doesn't valid ME interface   *   * @pdev: PCI device structure - * @ent: entry into pci_device_table + * @cfg: per generation config   *   * returns true if ME Interface is valid, false otherwise   */  static bool mei_me_quirk_probe(struct pci_dev *pdev, -				const struct pci_device_id *ent) +				const struct mei_cfg *cfg)  { -	u32 reg; -	if (ent->device == MEI_DEV_ID_PBG_1) { -		pci_read_config_dword(pdev, 0x48, ®); -		/* make sure that bit 9 is up and bit 10 is down */ -		if ((reg & 0x600) == 0x200) { -			dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); -			return false; -		} +	if (cfg->quirk_probe && cfg->quirk_probe(pdev)) { +		dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); +		return false;  	} +  	return true;  } +  /**   * mei_probe - Device Initialization Routine   * @@ -117,15 +126,14 @@ static bool mei_me_quirk_probe(struct pci_dev *pdev,   */  static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  { +	const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data);  	struct mei_device *dev;  	struct mei_me_hw *hw;  	int err; -	if (!mei_me_quirk_probe(pdev, ent)) { -		err = -ENODEV; -		goto end; -	} +	if (!mei_me_quirk_probe(pdev, cfg)) +		return -ENODEV;  	/* enable pci dev */  	err = pci_enable_device(pdev); @@ -141,8 +149,23 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  		dev_err(&pdev->dev, "failed to get pci regions.\n");  		goto disable_device;  	} + +	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) || +	    dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) { + +		err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); +		if (err) +			err = dma_set_coherent_mask(&pdev->dev, +						    DMA_BIT_MASK(32)); +	} +	if (err) { +		dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); +		goto release_regions; +	} + +  	/* allocates and initializes the mei dev structure */ -	dev = mei_me_dev_init(pdev); +	dev = mei_me_dev_init(pdev, cfg);  	if (!dev) {  		err = -ENOMEM;  		goto release_regions; @@ -181,6 +204,9 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  		goto release_irq;  	} +	pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT); +	pm_runtime_use_autosuspend(&pdev->dev); +  	err = mei_register(dev);  	if (err)  		goto release_irq; @@ -189,13 +215,24 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	schedule_delayed_work(&dev->timer_work, HZ); -	pr_debug("initialization successful.\n"); +	/* +	* For not wake-able HW runtime pm framework +	* can't be used on pci device level. +	* Use domain runtime pm callbacks instead. +	*/ +	if (!pci_dev_run_wake(pdev)) +		mei_me_set_pm_domain(dev); + +	if (mei_pg_is_enabled(dev)) +		pm_runtime_put_noidle(&pdev->dev); + +	dev_dbg(&pdev->dev, "initialization successful.\n");  	return 0;  release_irq: +	mei_cancel_work(dev);  	mei_disable_interrupts(dev); -	flush_scheduled_work();  	free_irq(pdev->irq, dev);  disable_msi:  	pci_disable_msi(pdev); @@ -228,18 +265,23 @@ static void mei_me_remove(struct pci_dev *pdev)  	if (!dev)  		return; +	if (mei_pg_is_enabled(dev)) +		pm_runtime_get_noresume(&pdev->dev); +  	hw = to_me_hw(dev); -	dev_err(&pdev->dev, "stop\n"); +	dev_dbg(&pdev->dev, "stop\n");  	mei_stop(dev); +	if (!pci_dev_run_wake(pdev)) +		mei_me_unset_pm_domain(dev); +  	/* disable interrupts */  	mei_disable_interrupts(dev);  	free_irq(pdev->irq, dev);  	pci_disable_msi(pdev); -	pci_set_drvdata(pdev, NULL);  	if (hw->mem_addr)  		pci_iounmap(pdev, hw->mem_addr); @@ -253,7 +295,7 @@ static void mei_me_remove(struct pci_dev *pdev)  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int mei_me_pci_suspend(struct device *device)  {  	struct pci_dev *pdev = to_pci_dev(device); @@ -262,7 +304,7 @@ static int mei_me_pci_suspend(struct device *device)  	if (!dev)  		return -ENODEV; -	dev_err(&pdev->dev, "suspend\n"); +	dev_dbg(&pdev->dev, "suspend\n");  	mei_stop(dev); @@ -304,18 +346,125 @@ static int mei_me_pci_resume(struct device *device)  		return err;  	} -	mutex_lock(&dev->device_lock); -	dev->dev_state = MEI_DEV_POWER_UP; -	mei_clear_interrupts(dev); -	mei_reset(dev, 1); -	mutex_unlock(&dev->device_lock); +	err = mei_restart(dev); +	if (err) +		return err;  	/* Start timer if stopped in suspend */  	schedule_delayed_work(&dev->timer_work, HZ); -	return err; +	return 0;  } -static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume); +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM_RUNTIME +static int mei_me_pm_runtime_idle(struct device *device) +{ +	struct pci_dev *pdev = to_pci_dev(device); +	struct mei_device *dev; + +	dev_dbg(&pdev->dev, "rpm: me: runtime_idle\n"); + +	dev = pci_get_drvdata(pdev); +	if (!dev) +		return -ENODEV; +	if (mei_write_is_idle(dev)) +		pm_schedule_suspend(device, MEI_ME_RPM_TIMEOUT * 2); + +	return -EBUSY; +} + +static int mei_me_pm_runtime_suspend(struct device *device) +{ +	struct pci_dev *pdev = to_pci_dev(device); +	struct mei_device *dev; +	int ret; + +	dev_dbg(&pdev->dev, "rpm: me: runtime suspend\n"); + +	dev = pci_get_drvdata(pdev); +	if (!dev) +		return -ENODEV; + +	mutex_lock(&dev->device_lock); + +	if (mei_write_is_idle(dev)) +		ret = mei_me_pg_set_sync(dev); +	else +		ret = -EAGAIN; + +	mutex_unlock(&dev->device_lock); + +	dev_dbg(&pdev->dev, "rpm: me: runtime suspend ret=%d\n", ret); + +	return ret; +} + +static int mei_me_pm_runtime_resume(struct device *device) +{ +	struct pci_dev *pdev = to_pci_dev(device); +	struct mei_device *dev; +	int ret; + +	dev_dbg(&pdev->dev, "rpm: me: runtime resume\n"); + +	dev = pci_get_drvdata(pdev); +	if (!dev) +		return -ENODEV; + +	mutex_lock(&dev->device_lock); + +	ret = mei_me_pg_unset_sync(dev); + +	mutex_unlock(&dev->device_lock); + +	dev_dbg(&pdev->dev, "rpm: me: runtime resume ret = %d\n", ret); + +	return ret; +} + +/** + * mei_me_set_pm_domain - fill and set pm domian stucture for device + * + * @dev: mei_device + */ +static inline void mei_me_set_pm_domain(struct mei_device *dev) +{ +	struct pci_dev *pdev  = dev->pdev; + +	if (pdev->dev.bus && pdev->dev.bus->pm) { +		dev->pg_domain.ops = *pdev->dev.bus->pm; + +		dev->pg_domain.ops.runtime_suspend = mei_me_pm_runtime_suspend; +		dev->pg_domain.ops.runtime_resume = mei_me_pm_runtime_resume; +		dev->pg_domain.ops.runtime_idle = mei_me_pm_runtime_idle; + +		pdev->dev.pm_domain = &dev->pg_domain; +	} +} + +/** + * mei_me_unset_pm_domain - clean pm domian stucture for device + * + * @dev: mei_device + */ +static inline void mei_me_unset_pm_domain(struct mei_device *dev) +{ +	/* stop using pm callbacks if any */ +	dev->pdev->dev.pm_domain = NULL; +} +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM +static const struct dev_pm_ops mei_me_pm_ops = { +	SET_SYSTEM_SLEEP_PM_OPS(mei_me_pci_suspend, +				mei_me_pci_resume) +	SET_RUNTIME_PM_OPS( +		mei_me_pm_runtime_suspend, +		mei_me_pm_runtime_resume, +		mei_me_pm_runtime_idle) +}; +  #define MEI_ME_PM_OPS	(&mei_me_pm_ops)  #else  #define MEI_ME_PM_OPS	NULL  | 
