18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2003-2020, Intel Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci * Intel Management Engine Interface (Intel MEI) Linux driver 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/mei.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "mei_dev.h" 228c2ecf20Sopenharmony_ci#include "client.h" 238c2ecf20Sopenharmony_ci#include "hw-me-regs.h" 248c2ecf20Sopenharmony_ci#include "hw-me.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* mei_pci_tbl - PCI Device ID Table */ 278c2ecf20Sopenharmony_cistatic const struct pci_device_id mei_me_pci_tbl[] = { 288c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, MEI_ME_ICH_CFG)}, 298c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_82G35, MEI_ME_ICH_CFG)}, 308c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, MEI_ME_ICH_CFG)}, 318c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_82G965, MEI_ME_ICH_CFG)}, 328c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, MEI_ME_ICH_CFG)}, 338c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, MEI_ME_ICH_CFG)}, 348c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, MEI_ME_ICH_CFG)}, 358c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, MEI_ME_ICH_CFG)}, 368c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, MEI_ME_ICH_CFG)}, 378c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, MEI_ME_ICH_CFG)}, 388c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, MEI_ME_ICH_CFG)}, 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, MEI_ME_ICH_CFG)}, 418c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, MEI_ME_ICH_CFG)}, 428c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, MEI_ME_ICH_CFG)}, 438c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, MEI_ME_ICH_CFG)}, 448c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, MEI_ME_ICH_CFG)}, 458c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, MEI_ME_ICH_CFG)}, 468c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, MEI_ME_ICH_CFG)}, 478c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, MEI_ME_ICH_CFG)}, 488c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, MEI_ME_ICH_CFG)}, 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, MEI_ME_ICH10_CFG)}, 518c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, MEI_ME_ICH10_CFG)}, 528c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, MEI_ME_ICH10_CFG)}, 538c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, MEI_ME_ICH10_CFG)}, 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, MEI_ME_PCH6_CFG)}, 568c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, MEI_ME_PCH6_CFG)}, 578c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, 588c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, 598c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, MEI_ME_PCH7_CFG)}, 608c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, MEI_ME_PCH7_CFG)}, 618c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, MEI_ME_PCH7_CFG)}, 628c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, MEI_ME_PCH8_SPS_4_CFG)}, 638c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, MEI_ME_PCH8_SPS_4_CFG)}, 648c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, MEI_ME_PCH8_CFG)}, 658c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, MEI_ME_PCH8_SPS_4_CFG)}, 668c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, MEI_ME_PCH8_CFG)}, 678c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, MEI_ME_PCH8_CFG)}, 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, 708c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, 718c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)}, 728c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_4_CFG)}, 738c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)}, 748c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH12_SPS_4_CFG)}, 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, MEI_ME_PCH8_CFG)}, 778c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, MEI_ME_PCH8_CFG)}, 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_DNV_IE, MEI_ME_PCH8_CFG)}, 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_GLK, MEI_ME_PCH8_CFG)}, 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, MEI_ME_PCH8_CFG)}, 848c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, MEI_ME_PCH8_CFG)}, 858c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_3, MEI_ME_PCH8_CFG)}, 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP, MEI_ME_PCH12_CFG)}, 888c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, 898c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH12_SPS_CFG)}, 908c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)}, 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP, MEI_ME_PCH12_CFG)}, 938c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, 948c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_V, MEI_ME_PCH12_CFG)}, 958c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H, MEI_ME_PCH12_CFG)}, 968c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, 998c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)}, 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)}, 1028c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_H, MEI_ME_PCH15_SPS_CFG)}, 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_JSP_N, MEI_ME_PCH15_CFG)}, 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_MCC, MEI_ME_PCH15_CFG)}, 1078c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_MCC_4, MEI_ME_PCH8_CFG)}, 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_CDF, MEI_ME_PCH8_CFG)}, 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_EBG, MEI_ME_PCH15_SPS_CFG)}, 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_S, MEI_ME_PCH15_CFG)}, 1148c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_LP, MEI_ME_PCH15_CFG)}, 1158c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_P, MEI_ME_PCH15_CFG)}, 1168c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_N, MEI_ME_PCH15_CFG)}, 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_RPL_S, MEI_ME_PCH15_CFG)}, 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci {MEI_PCI_DEVICE(MEI_DEV_ID_MTL_M, MEI_ME_PCH15_CFG)}, 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* required last entry */ 1238c2ecf20Sopenharmony_ci {0, } 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mei_me_pci_tbl); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1298c2ecf20Sopenharmony_cistatic inline void mei_me_set_pm_domain(struct mei_device *dev); 1308c2ecf20Sopenharmony_cistatic inline void mei_me_unset_pm_domain(struct mei_device *dev); 1318c2ecf20Sopenharmony_ci#else 1328c2ecf20Sopenharmony_cistatic inline void mei_me_set_pm_domain(struct mei_device *dev) {} 1338c2ecf20Sopenharmony_cistatic inline void mei_me_unset_pm_domain(struct mei_device *dev) {} 1348c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int mei_me_read_fws(const struct mei_device *dev, int where, u32 *val) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev->dev); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return pci_read_config_dword(pdev, where, val); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/** 1448c2ecf20Sopenharmony_ci * mei_me_quirk_probe - probe for devices that doesn't valid ME interface 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * @pdev: PCI device structure 1478c2ecf20Sopenharmony_ci * @cfg: per generation config 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * Return: true if ME Interface is valid, false otherwise 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic bool mei_me_quirk_probe(struct pci_dev *pdev, 1528c2ecf20Sopenharmony_ci const struct mei_cfg *cfg) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci if (cfg->quirk_probe && cfg->quirk_probe(pdev)) { 1558c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); 1568c2ecf20Sopenharmony_ci return false; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return true; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/** 1638c2ecf20Sopenharmony_ci * mei_me_probe - Device Initialization Routine 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * @pdev: PCI device structure 1668c2ecf20Sopenharmony_ci * @ent: entry in kcs_pci_tbl 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * Return: 0 on success, <0 on failure. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci const struct mei_cfg *cfg; 1738c2ecf20Sopenharmony_ci struct mei_device *dev; 1748c2ecf20Sopenharmony_ci struct mei_me_hw *hw; 1758c2ecf20Sopenharmony_ci unsigned int irqflags; 1768c2ecf20Sopenharmony_ci int err; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci cfg = mei_me_get_cfg(ent->driver_data); 1798c2ecf20Sopenharmony_ci if (!cfg) 1808c2ecf20Sopenharmony_ci return -ENODEV; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (!mei_me_quirk_probe(pdev, cfg)) 1838c2ecf20Sopenharmony_ci return -ENODEV; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* enable pci dev */ 1868c2ecf20Sopenharmony_ci err = pcim_enable_device(pdev); 1878c2ecf20Sopenharmony_ci if (err) { 1888c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable pci device.\n"); 1898c2ecf20Sopenharmony_ci goto end; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci /* set PCI host mastering */ 1928c2ecf20Sopenharmony_ci pci_set_master(pdev); 1938c2ecf20Sopenharmony_ci /* pci request regions and mapping IO device memory for mei driver */ 1948c2ecf20Sopenharmony_ci err = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); 1958c2ecf20Sopenharmony_ci if (err) { 1968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get pci regions.\n"); 1978c2ecf20Sopenharmony_ci goto end; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) || 2018c2ecf20Sopenharmony_ci dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) { 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 2048c2ecf20Sopenharmony_ci if (err) 2058c2ecf20Sopenharmony_ci err = dma_set_coherent_mask(&pdev->dev, 2068c2ecf20Sopenharmony_ci DMA_BIT_MASK(32)); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci if (err) { 2098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); 2108c2ecf20Sopenharmony_ci goto end; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* allocates and initializes the mei dev structure */ 2148c2ecf20Sopenharmony_ci dev = mei_me_dev_init(&pdev->dev, cfg); 2158c2ecf20Sopenharmony_ci if (!dev) { 2168c2ecf20Sopenharmony_ci err = -ENOMEM; 2178c2ecf20Sopenharmony_ci goto end; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci hw = to_me_hw(dev); 2208c2ecf20Sopenharmony_ci hw->mem_addr = pcim_iomap_table(pdev)[0]; 2218c2ecf20Sopenharmony_ci hw->read_fws = mei_me_read_fws; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci pci_enable_msi(pdev); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci hw->irq = pdev->irq; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* request and enable interrupt */ 2288c2ecf20Sopenharmony_ci irqflags = pci_dev_msi_enabled(pdev) ? IRQF_ONESHOT : IRQF_SHARED; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci err = request_threaded_irq(pdev->irq, 2318c2ecf20Sopenharmony_ci mei_me_irq_quick_handler, 2328c2ecf20Sopenharmony_ci mei_me_irq_thread_handler, 2338c2ecf20Sopenharmony_ci irqflags, KBUILD_MODNAME, dev); 2348c2ecf20Sopenharmony_ci if (err) { 2358c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n", 2368c2ecf20Sopenharmony_ci pdev->irq); 2378c2ecf20Sopenharmony_ci goto end; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (mei_start(dev)) { 2418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "init hw failure.\n"); 2428c2ecf20Sopenharmony_ci err = -ENODEV; 2438c2ecf20Sopenharmony_ci goto release_irq; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT); 2478c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci err = mei_register(dev, &pdev->dev); 2508c2ecf20Sopenharmony_ci if (err) 2518c2ecf20Sopenharmony_ci goto stop; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * MEI requires to resume from runtime suspend mode 2578c2ecf20Sopenharmony_ci * in order to perform link reset flow upon system suspend. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * ME maps runtime suspend/resume to D0i states, 2638c2ecf20Sopenharmony_ci * hence we need to go around native PCI runtime service which 2648c2ecf20Sopenharmony_ci * eventually brings the device into D3cold/hot state, 2658c2ecf20Sopenharmony_ci * but the mei device cannot wake up from D3 unlike from D0i3. 2668c2ecf20Sopenharmony_ci * To get around the PCI device native runtime pm, 2678c2ecf20Sopenharmony_ci * ME uses runtime pm domain handlers which take precedence 2688c2ecf20Sopenharmony_ci * over the driver's pm handlers. 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci mei_me_set_pm_domain(dev); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (mei_pg_is_enabled(dev)) { 2738c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 2748c2ecf20Sopenharmony_ci if (hw->d0i3_supported) 2758c2ecf20Sopenharmony_ci pm_runtime_allow(&pdev->dev); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "initialization successful.\n"); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistop: 2838c2ecf20Sopenharmony_ci mei_stop(dev); 2848c2ecf20Sopenharmony_cirelease_irq: 2858c2ecf20Sopenharmony_ci mei_cancel_work(dev); 2868c2ecf20Sopenharmony_ci mei_disable_interrupts(dev); 2878c2ecf20Sopenharmony_ci free_irq(pdev->irq, dev); 2888c2ecf20Sopenharmony_ciend: 2898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "initialization failed.\n"); 2908c2ecf20Sopenharmony_ci return err; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/** 2948c2ecf20Sopenharmony_ci * mei_me_shutdown - Device Removal Routine 2958c2ecf20Sopenharmony_ci * 2968c2ecf20Sopenharmony_ci * @pdev: PCI device structure 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * mei_me_shutdown is called from the reboot notifier 2998c2ecf20Sopenharmony_ci * it's a simplified version of remove so we go down 3008c2ecf20Sopenharmony_ci * faster. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic void mei_me_shutdown(struct pci_dev *pdev) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct mei_device *dev; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci dev = pci_get_drvdata(pdev); 3078c2ecf20Sopenharmony_ci if (!dev) 3088c2ecf20Sopenharmony_ci return; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "shutdown\n"); 3118c2ecf20Sopenharmony_ci mei_stop(dev); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci mei_me_unset_pm_domain(dev); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci mei_disable_interrupts(dev); 3168c2ecf20Sopenharmony_ci free_irq(pdev->irq, dev); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/** 3208c2ecf20Sopenharmony_ci * mei_me_remove - Device Removal Routine 3218c2ecf20Sopenharmony_ci * 3228c2ecf20Sopenharmony_ci * @pdev: PCI device structure 3238c2ecf20Sopenharmony_ci * 3248c2ecf20Sopenharmony_ci * mei_me_remove is called by the PCI subsystem to alert the driver 3258c2ecf20Sopenharmony_ci * that it should release a PCI device. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_cistatic void mei_me_remove(struct pci_dev *pdev) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct mei_device *dev; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci dev = pci_get_drvdata(pdev); 3328c2ecf20Sopenharmony_ci if (!dev) 3338c2ecf20Sopenharmony_ci return; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (mei_pg_is_enabled(dev)) 3368c2ecf20Sopenharmony_ci pm_runtime_get_noresume(&pdev->dev); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "stop\n"); 3398c2ecf20Sopenharmony_ci mei_stop(dev); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci mei_me_unset_pm_domain(dev); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci mei_disable_interrupts(dev); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci free_irq(pdev->irq, dev); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mei_deregister(dev); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3518c2ecf20Sopenharmony_cistatic int mei_me_pci_suspend(struct device *device) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 3548c2ecf20Sopenharmony_ci struct mei_device *dev = pci_get_drvdata(pdev); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (!dev) 3578c2ecf20Sopenharmony_ci return -ENODEV; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "suspend\n"); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci mei_stop(dev); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci mei_disable_interrupts(dev); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci free_irq(pdev->irq, dev); 3668c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int mei_me_pci_resume(struct device *device) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 3748c2ecf20Sopenharmony_ci struct mei_device *dev; 3758c2ecf20Sopenharmony_ci unsigned int irqflags; 3768c2ecf20Sopenharmony_ci int err; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci dev = pci_get_drvdata(pdev); 3798c2ecf20Sopenharmony_ci if (!dev) 3808c2ecf20Sopenharmony_ci return -ENODEV; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci pci_enable_msi(pdev); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci irqflags = pci_dev_msi_enabled(pdev) ? IRQF_ONESHOT : IRQF_SHARED; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* request and enable interrupt */ 3878c2ecf20Sopenharmony_ci err = request_threaded_irq(pdev->irq, 3888c2ecf20Sopenharmony_ci mei_me_irq_quick_handler, 3898c2ecf20Sopenharmony_ci mei_me_irq_thread_handler, 3908c2ecf20Sopenharmony_ci irqflags, KBUILD_MODNAME, dev); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (err) { 3938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n", 3948c2ecf20Sopenharmony_ci pdev->irq); 3958c2ecf20Sopenharmony_ci return err; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci err = mei_restart(dev); 3998c2ecf20Sopenharmony_ci if (err) 4008c2ecf20Sopenharmony_ci return err; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Start timer if stopped in suspend */ 4038c2ecf20Sopenharmony_ci schedule_delayed_work(&dev->timer_work, HZ); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4108c2ecf20Sopenharmony_cistatic int mei_me_pm_runtime_idle(struct device *device) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct mei_device *dev; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci dev_dbg(device, "rpm: me: runtime_idle\n"); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci dev = dev_get_drvdata(device); 4178c2ecf20Sopenharmony_ci if (!dev) 4188c2ecf20Sopenharmony_ci return -ENODEV; 4198c2ecf20Sopenharmony_ci if (mei_write_is_idle(dev)) 4208c2ecf20Sopenharmony_ci pm_runtime_autosuspend(device); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return -EBUSY; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int mei_me_pm_runtime_suspend(struct device *device) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct mei_device *dev; 4288c2ecf20Sopenharmony_ci int ret; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci dev_dbg(device, "rpm: me: runtime suspend\n"); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci dev = dev_get_drvdata(device); 4338c2ecf20Sopenharmony_ci if (!dev) 4348c2ecf20Sopenharmony_ci return -ENODEV; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci mutex_lock(&dev->device_lock); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (mei_write_is_idle(dev)) 4398c2ecf20Sopenharmony_ci ret = mei_me_pg_enter_sync(dev); 4408c2ecf20Sopenharmony_ci else 4418c2ecf20Sopenharmony_ci ret = -EAGAIN; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci mutex_unlock(&dev->device_lock); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci dev_dbg(device, "rpm: me: runtime suspend ret=%d\n", ret); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (ret && ret != -EAGAIN) 4488c2ecf20Sopenharmony_ci schedule_work(&dev->reset_work); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci return ret; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic int mei_me_pm_runtime_resume(struct device *device) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct mei_device *dev; 4568c2ecf20Sopenharmony_ci int ret; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci dev_dbg(device, "rpm: me: runtime resume\n"); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci dev = dev_get_drvdata(device); 4618c2ecf20Sopenharmony_ci if (!dev) 4628c2ecf20Sopenharmony_ci return -ENODEV; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci mutex_lock(&dev->device_lock); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ret = mei_me_pg_exit_sync(dev); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci mutex_unlock(&dev->device_lock); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci dev_dbg(device, "rpm: me: runtime resume ret = %d\n", ret); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (ret) 4738c2ecf20Sopenharmony_ci schedule_work(&dev->reset_work); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return ret; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/** 4798c2ecf20Sopenharmony_ci * mei_me_set_pm_domain - fill and set pm domain structure for device 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * @dev: mei_device 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_cistatic inline void mei_me_set_pm_domain(struct mei_device *dev) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev->dev); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (pdev->dev.bus && pdev->dev.bus->pm) { 4888c2ecf20Sopenharmony_ci dev->pg_domain.ops = *pdev->dev.bus->pm; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci dev->pg_domain.ops.runtime_suspend = mei_me_pm_runtime_suspend; 4918c2ecf20Sopenharmony_ci dev->pg_domain.ops.runtime_resume = mei_me_pm_runtime_resume; 4928c2ecf20Sopenharmony_ci dev->pg_domain.ops.runtime_idle = mei_me_pm_runtime_idle; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci dev_pm_domain_set(&pdev->dev, &dev->pg_domain); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/** 4998c2ecf20Sopenharmony_ci * mei_me_unset_pm_domain - clean pm domain structure for device 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * @dev: mei_device 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_cistatic inline void mei_me_unset_pm_domain(struct mei_device *dev) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci /* stop using pm callbacks if any */ 5068c2ecf20Sopenharmony_ci dev_pm_domain_set(dev->dev, NULL); 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mei_me_pm_ops = { 5108c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(mei_me_pci_suspend, 5118c2ecf20Sopenharmony_ci mei_me_pci_resume) 5128c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS( 5138c2ecf20Sopenharmony_ci mei_me_pm_runtime_suspend, 5148c2ecf20Sopenharmony_ci mei_me_pm_runtime_resume, 5158c2ecf20Sopenharmony_ci mei_me_pm_runtime_idle) 5168c2ecf20Sopenharmony_ci}; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci#define MEI_ME_PM_OPS (&mei_me_pm_ops) 5198c2ecf20Sopenharmony_ci#else 5208c2ecf20Sopenharmony_ci#define MEI_ME_PM_OPS NULL 5218c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 5228c2ecf20Sopenharmony_ci/* 5238c2ecf20Sopenharmony_ci * PCI driver structure 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_cistatic struct pci_driver mei_me_driver = { 5268c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 5278c2ecf20Sopenharmony_ci .id_table = mei_me_pci_tbl, 5288c2ecf20Sopenharmony_ci .probe = mei_me_probe, 5298c2ecf20Sopenharmony_ci .remove = mei_me_remove, 5308c2ecf20Sopenharmony_ci .shutdown = mei_me_shutdown, 5318c2ecf20Sopenharmony_ci .driver.pm = MEI_ME_PM_OPS, 5328c2ecf20Sopenharmony_ci .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, 5338c2ecf20Sopenharmony_ci}; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cimodule_pci_driver(mei_me_driver); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 5388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel(R) Management Engine Interface"); 5398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 540