17e98d785SM Chetan Kumar // SPDX-License-Identifier: GPL-2.0-only 27e98d785SM Chetan Kumar /* 37e98d785SM Chetan Kumar * Copyright (C) 2020-21 Intel Corporation. 47e98d785SM Chetan Kumar */ 57e98d785SM Chetan Kumar 67e98d785SM Chetan Kumar #include <linux/acpi.h> 77e98d785SM Chetan Kumar #include <linux/bitfield.h> 87e98d785SM Chetan Kumar #include <linux/module.h> 97e98d785SM Chetan Kumar #include <net/rtnetlink.h> 107e98d785SM Chetan Kumar 117e98d785SM Chetan Kumar #include "iosm_ipc_imem.h" 127e98d785SM Chetan Kumar #include "iosm_ipc_pcie.h" 137e98d785SM Chetan Kumar #include "iosm_ipc_protocol.h" 147e98d785SM Chetan Kumar 157e98d785SM Chetan Kumar MODULE_DESCRIPTION("IOSM Driver"); 167e98d785SM Chetan Kumar MODULE_LICENSE("GPL v2"); 177e98d785SM Chetan Kumar 187e98d785SM Chetan Kumar /* WWAN GUID */ 197e98d785SM Chetan Kumar static guid_t wwan_acpi_guid = GUID_INIT(0xbad01b75, 0x22a8, 0x4f48, 0x87, 0x92, 207e98d785SM Chetan Kumar 0xbd, 0xde, 0x94, 0x67, 0x74, 0x7d); 217e98d785SM Chetan Kumar 227e98d785SM Chetan Kumar static void ipc_pcie_resources_release(struct iosm_pcie *ipc_pcie) 237e98d785SM Chetan Kumar { 247e98d785SM Chetan Kumar /* Free the MSI resources. */ 257e98d785SM Chetan Kumar ipc_release_irq(ipc_pcie); 267e98d785SM Chetan Kumar 277e98d785SM Chetan Kumar /* Free mapped doorbell scratchpad bus memory into CPU space. */ 287e98d785SM Chetan Kumar iounmap(ipc_pcie->scratchpad); 297e98d785SM Chetan Kumar 307e98d785SM Chetan Kumar /* Free mapped IPC_REGS bus memory into CPU space. */ 317e98d785SM Chetan Kumar iounmap(ipc_pcie->ipc_regs); 327e98d785SM Chetan Kumar 337e98d785SM Chetan Kumar /* Releases all PCI I/O and memory resources previously reserved by a 347e98d785SM Chetan Kumar * successful call to pci_request_regions. Call this function only 357e98d785SM Chetan Kumar * after all use of the PCI regions has ceased. 367e98d785SM Chetan Kumar */ 377e98d785SM Chetan Kumar pci_release_regions(ipc_pcie->pci); 387e98d785SM Chetan Kumar } 397e98d785SM Chetan Kumar 407e98d785SM Chetan Kumar static void ipc_pcie_cleanup(struct iosm_pcie *ipc_pcie) 417e98d785SM Chetan Kumar { 427e98d785SM Chetan Kumar /* Free the shared memory resources. */ 437e98d785SM Chetan Kumar ipc_imem_cleanup(ipc_pcie->imem); 447e98d785SM Chetan Kumar 457e98d785SM Chetan Kumar ipc_pcie_resources_release(ipc_pcie); 467e98d785SM Chetan Kumar 477e98d785SM Chetan Kumar /* Signal to the system that the PCI device is not in use. */ 487e98d785SM Chetan Kumar pci_disable_device(ipc_pcie->pci); 497e98d785SM Chetan Kumar } 507e98d785SM Chetan Kumar 517e98d785SM Chetan Kumar static void ipc_pcie_deinit(struct iosm_pcie *ipc_pcie) 527e98d785SM Chetan Kumar { 537e98d785SM Chetan Kumar kfree(ipc_pcie->imem); 547e98d785SM Chetan Kumar kfree(ipc_pcie); 557e98d785SM Chetan Kumar } 567e98d785SM Chetan Kumar 577e98d785SM Chetan Kumar static void ipc_pcie_remove(struct pci_dev *pci) 587e98d785SM Chetan Kumar { 597e98d785SM Chetan Kumar struct iosm_pcie *ipc_pcie = pci_get_drvdata(pci); 607e98d785SM Chetan Kumar 617e98d785SM Chetan Kumar ipc_pcie_cleanup(ipc_pcie); 627e98d785SM Chetan Kumar 637e98d785SM Chetan Kumar ipc_pcie_deinit(ipc_pcie); 647e98d785SM Chetan Kumar } 657e98d785SM Chetan Kumar 667e98d785SM Chetan Kumar static int ipc_pcie_resources_request(struct iosm_pcie *ipc_pcie) 677e98d785SM Chetan Kumar { 687e98d785SM Chetan Kumar struct pci_dev *pci = ipc_pcie->pci; 697e98d785SM Chetan Kumar u32 cap = 0; 707e98d785SM Chetan Kumar u32 ret; 717e98d785SM Chetan Kumar 727e98d785SM Chetan Kumar /* Reserved PCI I/O and memory resources. 737e98d785SM Chetan Kumar * Mark all PCI regions associated with PCI device pci as 747e98d785SM Chetan Kumar * being reserved by owner IOSM_IPC. 757e98d785SM Chetan Kumar */ 767e98d785SM Chetan Kumar ret = pci_request_regions(pci, "IOSM_IPC"); 777e98d785SM Chetan Kumar if (ret) { 787e98d785SM Chetan Kumar dev_err(ipc_pcie->dev, "failed pci request regions"); 797e98d785SM Chetan Kumar goto pci_request_region_fail; 807e98d785SM Chetan Kumar } 817e98d785SM Chetan Kumar 827e98d785SM Chetan Kumar /* Reserve the doorbell IPC REGS memory resources. 837e98d785SM Chetan Kumar * Remap the memory into CPU space. Arrange for the physical address 847e98d785SM Chetan Kumar * (BAR) to be visible from this driver. 857e98d785SM Chetan Kumar * pci_ioremap_bar() ensures that the memory is marked uncachable. 867e98d785SM Chetan Kumar */ 877e98d785SM Chetan Kumar ipc_pcie->ipc_regs = pci_ioremap_bar(pci, ipc_pcie->ipc_regs_bar_nr); 887e98d785SM Chetan Kumar 897e98d785SM Chetan Kumar if (!ipc_pcie->ipc_regs) { 907e98d785SM Chetan Kumar dev_err(ipc_pcie->dev, "IPC REGS ioremap error"); 917e98d785SM Chetan Kumar ret = -EBUSY; 927e98d785SM Chetan Kumar goto ipc_regs_remap_fail; 937e98d785SM Chetan Kumar } 947e98d785SM Chetan Kumar 957e98d785SM Chetan Kumar /* Reserve the MMIO scratchpad memory resources. 967e98d785SM Chetan Kumar * Remap the memory into CPU space. Arrange for the physical address 977e98d785SM Chetan Kumar * (BAR) to be visible from this driver. 987e98d785SM Chetan Kumar * pci_ioremap_bar() ensures that the memory is marked uncachable. 997e98d785SM Chetan Kumar */ 1007e98d785SM Chetan Kumar ipc_pcie->scratchpad = 1017e98d785SM Chetan Kumar pci_ioremap_bar(pci, ipc_pcie->scratchpad_bar_nr); 1027e98d785SM Chetan Kumar 1037e98d785SM Chetan Kumar if (!ipc_pcie->scratchpad) { 1047e98d785SM Chetan Kumar dev_err(ipc_pcie->dev, "doorbell scratchpad ioremap error"); 1057e98d785SM Chetan Kumar ret = -EBUSY; 1067e98d785SM Chetan Kumar goto scratch_remap_fail; 1077e98d785SM Chetan Kumar } 1087e98d785SM Chetan Kumar 1097e98d785SM Chetan Kumar /* Install the irq handler triggered by CP. */ 1107e98d785SM Chetan Kumar ret = ipc_acquire_irq(ipc_pcie); 1117e98d785SM Chetan Kumar if (ret) { 1127e98d785SM Chetan Kumar dev_err(ipc_pcie->dev, "acquiring MSI irq failed!"); 1137e98d785SM Chetan Kumar goto irq_acquire_fail; 1147e98d785SM Chetan Kumar } 1157e98d785SM Chetan Kumar 1167e98d785SM Chetan Kumar /* Enable bus-mastering for the IOSM IPC device. */ 1177e98d785SM Chetan Kumar pci_set_master(pci); 1187e98d785SM Chetan Kumar 1197e98d785SM Chetan Kumar /* Enable LTR if possible 1207e98d785SM Chetan Kumar * This is needed for L1.2! 1217e98d785SM Chetan Kumar */ 1227e98d785SM Chetan Kumar pcie_capability_read_dword(ipc_pcie->pci, PCI_EXP_DEVCAP2, &cap); 1237e98d785SM Chetan Kumar if (cap & PCI_EXP_DEVCAP2_LTR) 1247e98d785SM Chetan Kumar pcie_capability_set_word(ipc_pcie->pci, PCI_EXP_DEVCTL2, 1257e98d785SM Chetan Kumar PCI_EXP_DEVCTL2_LTR_EN); 1267e98d785SM Chetan Kumar 1277e98d785SM Chetan Kumar dev_dbg(ipc_pcie->dev, "link between AP and CP is fully on"); 1287e98d785SM Chetan Kumar 1297e98d785SM Chetan Kumar return ret; 1307e98d785SM Chetan Kumar 1317e98d785SM Chetan Kumar irq_acquire_fail: 1327e98d785SM Chetan Kumar iounmap(ipc_pcie->scratchpad); 1337e98d785SM Chetan Kumar scratch_remap_fail: 1347e98d785SM Chetan Kumar iounmap(ipc_pcie->ipc_regs); 1357e98d785SM Chetan Kumar ipc_regs_remap_fail: 1367e98d785SM Chetan Kumar pci_release_regions(pci); 1377e98d785SM Chetan Kumar pci_request_region_fail: 1387e98d785SM Chetan Kumar return ret; 1397e98d785SM Chetan Kumar } 1407e98d785SM Chetan Kumar 1417e98d785SM Chetan Kumar bool ipc_pcie_check_aspm_enabled(struct iosm_pcie *ipc_pcie, 1427e98d785SM Chetan Kumar bool parent) 1437e98d785SM Chetan Kumar { 1447e98d785SM Chetan Kumar struct pci_dev *pdev; 1457e98d785SM Chetan Kumar u16 value = 0; 1467e98d785SM Chetan Kumar u32 enabled; 1477e98d785SM Chetan Kumar 1487e98d785SM Chetan Kumar if (parent) 1497e98d785SM Chetan Kumar pdev = ipc_pcie->pci->bus->self; 1507e98d785SM Chetan Kumar else 1517e98d785SM Chetan Kumar pdev = ipc_pcie->pci; 1527e98d785SM Chetan Kumar 1537e98d785SM Chetan Kumar pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &value); 1547e98d785SM Chetan Kumar enabled = value & PCI_EXP_LNKCTL_ASPMC; 1557e98d785SM Chetan Kumar dev_dbg(ipc_pcie->dev, "ASPM L1: 0x%04X 0x%03X", pdev->device, value); 1567e98d785SM Chetan Kumar 1577e98d785SM Chetan Kumar return (enabled == PCI_EXP_LNKCTL_ASPM_L1 || 1587e98d785SM Chetan Kumar enabled == PCI_EXP_LNKCTL_ASPMC); 1597e98d785SM Chetan Kumar } 1607e98d785SM Chetan Kumar 1617e98d785SM Chetan Kumar bool ipc_pcie_check_data_link_active(struct iosm_pcie *ipc_pcie) 1627e98d785SM Chetan Kumar { 1637e98d785SM Chetan Kumar struct pci_dev *parent; 1647e98d785SM Chetan Kumar u16 link_status = 0; 1657e98d785SM Chetan Kumar 1667e98d785SM Chetan Kumar if (!ipc_pcie->pci->bus || !ipc_pcie->pci->bus->self) { 1677e98d785SM Chetan Kumar dev_err(ipc_pcie->dev, "root port not found"); 1687e98d785SM Chetan Kumar return false; 1697e98d785SM Chetan Kumar } 1707e98d785SM Chetan Kumar 1717e98d785SM Chetan Kumar parent = ipc_pcie->pci->bus->self; 1727e98d785SM Chetan Kumar 1737e98d785SM Chetan Kumar pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &link_status); 1747e98d785SM Chetan Kumar dev_dbg(ipc_pcie->dev, "Link status: 0x%04X", link_status); 1757e98d785SM Chetan Kumar 1767e98d785SM Chetan Kumar return link_status & PCI_EXP_LNKSTA_DLLLA; 1777e98d785SM Chetan Kumar } 1787e98d785SM Chetan Kumar 1797e98d785SM Chetan Kumar static bool ipc_pcie_check_aspm_supported(struct iosm_pcie *ipc_pcie, 1807e98d785SM Chetan Kumar bool parent) 1817e98d785SM Chetan Kumar { 1827e98d785SM Chetan Kumar struct pci_dev *pdev; 1837e98d785SM Chetan Kumar u32 support; 1847e98d785SM Chetan Kumar u32 cap = 0; 1857e98d785SM Chetan Kumar 1867e98d785SM Chetan Kumar if (parent) 1877e98d785SM Chetan Kumar pdev = ipc_pcie->pci->bus->self; 1887e98d785SM Chetan Kumar else 1897e98d785SM Chetan Kumar pdev = ipc_pcie->pci; 1907e98d785SM Chetan Kumar pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &cap); 1917e98d785SM Chetan Kumar support = u32_get_bits(cap, PCI_EXP_LNKCAP_ASPMS); 1927e98d785SM Chetan Kumar if (support < PCI_EXP_LNKCTL_ASPM_L1) { 1937e98d785SM Chetan Kumar dev_dbg(ipc_pcie->dev, "ASPM L1 not supported: 0x%04X", 1947e98d785SM Chetan Kumar pdev->device); 1957e98d785SM Chetan Kumar return false; 1967e98d785SM Chetan Kumar } 1977e98d785SM Chetan Kumar return true; 1987e98d785SM Chetan Kumar } 1997e98d785SM Chetan Kumar 2007e98d785SM Chetan Kumar void ipc_pcie_config_aspm(struct iosm_pcie *ipc_pcie) 2017e98d785SM Chetan Kumar { 2027e98d785SM Chetan Kumar bool parent_aspm_enabled, dev_aspm_enabled; 2037e98d785SM Chetan Kumar 2047e98d785SM Chetan Kumar /* check if both root port and child supports ASPM L1 */ 2057e98d785SM Chetan Kumar if (!ipc_pcie_check_aspm_supported(ipc_pcie, true) || 2067e98d785SM Chetan Kumar !ipc_pcie_check_aspm_supported(ipc_pcie, false)) 2077e98d785SM Chetan Kumar return; 2087e98d785SM Chetan Kumar 2097e98d785SM Chetan Kumar parent_aspm_enabled = ipc_pcie_check_aspm_enabled(ipc_pcie, true); 2107e98d785SM Chetan Kumar dev_aspm_enabled = ipc_pcie_check_aspm_enabled(ipc_pcie, false); 2117e98d785SM Chetan Kumar 2127e98d785SM Chetan Kumar dev_dbg(ipc_pcie->dev, "ASPM parent: %s device: %s", 2137e98d785SM Chetan Kumar parent_aspm_enabled ? "Enabled" : "Disabled", 2147e98d785SM Chetan Kumar dev_aspm_enabled ? "Enabled" : "Disabled"); 2157e98d785SM Chetan Kumar } 2167e98d785SM Chetan Kumar 2177e98d785SM Chetan Kumar /* Initializes PCIe endpoint configuration */ 2187e98d785SM Chetan Kumar static void ipc_pcie_config_init(struct iosm_pcie *ipc_pcie) 2197e98d785SM Chetan Kumar { 2207e98d785SM Chetan Kumar /* BAR0 is used for doorbell */ 2217e98d785SM Chetan Kumar ipc_pcie->ipc_regs_bar_nr = IPC_DOORBELL_BAR0; 2227e98d785SM Chetan Kumar 2237e98d785SM Chetan Kumar /* update HW configuration */ 2247e98d785SM Chetan Kumar ipc_pcie->scratchpad_bar_nr = IPC_SCRATCHPAD_BAR2; 2257e98d785SM Chetan Kumar ipc_pcie->doorbell_reg_offset = IPC_DOORBELL_CH_OFFSET; 2267e98d785SM Chetan Kumar ipc_pcie->doorbell_write = IPC_WRITE_PTR_REG_0; 2277e98d785SM Chetan Kumar ipc_pcie->doorbell_capture = IPC_CAPTURE_PTR_REG_0; 2287e98d785SM Chetan Kumar } 2297e98d785SM Chetan Kumar 2307e98d785SM Chetan Kumar /* This will read the BIOS WWAN RTD3 settings: 2317e98d785SM Chetan Kumar * D0L1.2/D3L2/Disabled 2327e98d785SM Chetan Kumar */ 2337e98d785SM Chetan Kumar static enum ipc_pcie_sleep_state ipc_pcie_read_bios_cfg(struct device *dev) 2347e98d785SM Chetan Kumar { 235d38a648dSM Chetan Kumar enum ipc_pcie_sleep_state sleep_state = IPC_PCIE_D0L12; 2367e98d785SM Chetan Kumar union acpi_object *object; 2377e98d785SM Chetan Kumar acpi_handle handle_acpi; 2387e98d785SM Chetan Kumar 2397e98d785SM Chetan Kumar handle_acpi = ACPI_HANDLE(dev); 2407e98d785SM Chetan Kumar if (!handle_acpi) { 2417e98d785SM Chetan Kumar pr_debug("pci device is NOT ACPI supporting device\n"); 2427e98d785SM Chetan Kumar goto default_ret; 2437e98d785SM Chetan Kumar } 2447e98d785SM Chetan Kumar 2457e98d785SM Chetan Kumar object = acpi_evaluate_dsm(handle_acpi, &wwan_acpi_guid, 0, 3, NULL); 246d38a648dSM Chetan Kumar if (!object) 247d38a648dSM Chetan Kumar goto default_ret; 2487e98d785SM Chetan Kumar 249d38a648dSM Chetan Kumar if (object->integer.value == 3) 250d38a648dSM Chetan Kumar sleep_state = IPC_PCIE_D3L2; 251d38a648dSM Chetan Kumar 252e541dd77SWang ShaoBo ACPI_FREE(object); 2537e98d785SM Chetan Kumar 2547e98d785SM Chetan Kumar default_ret: 255d38a648dSM Chetan Kumar return sleep_state; 2567e98d785SM Chetan Kumar } 2577e98d785SM Chetan Kumar 2587e98d785SM Chetan Kumar static int ipc_pcie_probe(struct pci_dev *pci, 2597e98d785SM Chetan Kumar const struct pci_device_id *pci_id) 2607e98d785SM Chetan Kumar { 2617e98d785SM Chetan Kumar struct iosm_pcie *ipc_pcie = kzalloc(sizeof(*ipc_pcie), GFP_KERNEL); 262035e3befSM Chetan Kumar int ret; 2637e98d785SM Chetan Kumar 2647e98d785SM Chetan Kumar pr_debug("Probing device 0x%X from the vendor 0x%X", pci_id->device, 2657e98d785SM Chetan Kumar pci_id->vendor); 2667e98d785SM Chetan Kumar 2677e98d785SM Chetan Kumar if (!ipc_pcie) 2687e98d785SM Chetan Kumar goto ret_fail; 2697e98d785SM Chetan Kumar 2707e98d785SM Chetan Kumar /* Initialize ipc dbg component for the PCIe device */ 2717e98d785SM Chetan Kumar ipc_pcie->dev = &pci->dev; 2727e98d785SM Chetan Kumar 2737e98d785SM Chetan Kumar /* Set the driver specific data. */ 2747e98d785SM Chetan Kumar pci_set_drvdata(pci, ipc_pcie); 2757e98d785SM Chetan Kumar 2767e98d785SM Chetan Kumar /* Save the address of the PCI device configuration. */ 2777e98d785SM Chetan Kumar ipc_pcie->pci = pci; 2787e98d785SM Chetan Kumar 2797e98d785SM Chetan Kumar /* Update platform configuration */ 2807e98d785SM Chetan Kumar ipc_pcie_config_init(ipc_pcie); 2817e98d785SM Chetan Kumar 2827e98d785SM Chetan Kumar /* Initialize the device before it is used. Ask low-level code 2837e98d785SM Chetan Kumar * to enable I/O and memory. Wake up the device if it was suspended. 2847e98d785SM Chetan Kumar */ 2857e98d785SM Chetan Kumar if (pci_enable_device(pci)) { 2867e98d785SM Chetan Kumar dev_err(ipc_pcie->dev, "failed to enable the AP PCIe device"); 2877e98d785SM Chetan Kumar /* If enable of PCIe device has failed then calling 2887e98d785SM Chetan Kumar * ipc_pcie_cleanup will panic the system. More over 2897e98d785SM Chetan Kumar * ipc_pcie_cleanup() is required to be called after 2907e98d785SM Chetan Kumar * ipc_imem_mount() 2917e98d785SM Chetan Kumar */ 2927e98d785SM Chetan Kumar goto pci_enable_fail; 2937e98d785SM Chetan Kumar } 2947e98d785SM Chetan Kumar 295035e3befSM Chetan Kumar ret = dma_set_mask(ipc_pcie->dev, DMA_BIT_MASK(64)); 296035e3befSM Chetan Kumar if (ret) { 297035e3befSM Chetan Kumar dev_err(ipc_pcie->dev, "Could not set PCI DMA mask: %d", ret); 298a56ef256SHarshit Mogalapalli goto set_mask_fail; 299035e3befSM Chetan Kumar } 300035e3befSM Chetan Kumar 3017e98d785SM Chetan Kumar ipc_pcie_config_aspm(ipc_pcie); 3027e98d785SM Chetan Kumar dev_dbg(ipc_pcie->dev, "PCIe device enabled."); 3037e98d785SM Chetan Kumar 3047e98d785SM Chetan Kumar /* Read WWAN RTD3 BIOS Setting 3057e98d785SM Chetan Kumar */ 3067e98d785SM Chetan Kumar ipc_pcie->d3l2_support = ipc_pcie_read_bios_cfg(&pci->dev); 3077e98d785SM Chetan Kumar 3087e98d785SM Chetan Kumar ipc_pcie->suspend = 0; 3097e98d785SM Chetan Kumar 3107e98d785SM Chetan Kumar if (ipc_pcie_resources_request(ipc_pcie)) 3117e98d785SM Chetan Kumar goto resources_req_fail; 3127e98d785SM Chetan Kumar 3137e98d785SM Chetan Kumar /* Establish the link to the imem layer. */ 3147e98d785SM Chetan Kumar ipc_pcie->imem = ipc_imem_init(ipc_pcie, pci->device, 3157e98d785SM Chetan Kumar ipc_pcie->scratchpad, ipc_pcie->dev); 3167e98d785SM Chetan Kumar if (!ipc_pcie->imem) { 3177e98d785SM Chetan Kumar dev_err(ipc_pcie->dev, "failed to init imem"); 3187e98d785SM Chetan Kumar goto imem_init_fail; 3197e98d785SM Chetan Kumar } 3207e98d785SM Chetan Kumar 3217e98d785SM Chetan Kumar return 0; 3227e98d785SM Chetan Kumar 3237e98d785SM Chetan Kumar imem_init_fail: 3247e98d785SM Chetan Kumar ipc_pcie_resources_release(ipc_pcie); 3257e98d785SM Chetan Kumar resources_req_fail: 326a56ef256SHarshit Mogalapalli set_mask_fail: 3277e98d785SM Chetan Kumar pci_disable_device(pci); 3287e98d785SM Chetan Kumar pci_enable_fail: 3297e98d785SM Chetan Kumar kfree(ipc_pcie); 3307e98d785SM Chetan Kumar ret_fail: 3317e98d785SM Chetan Kumar return -EIO; 3327e98d785SM Chetan Kumar } 3337e98d785SM Chetan Kumar 3347e98d785SM Chetan Kumar static const struct pci_device_id iosm_ipc_ids[] = { 3357e98d785SM Chetan Kumar { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CP_DEVICE_7560_ID) }, 3361f52d7b6SM Chetan Kumar { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CP_DEVICE_7360_ID) }, 3377e98d785SM Chetan Kumar {} 3387e98d785SM Chetan Kumar }; 33995d359edSZou Wei MODULE_DEVICE_TABLE(pci, iosm_ipc_ids); 3407e98d785SM Chetan Kumar 3417e98d785SM Chetan Kumar /* Enter sleep in s2idle case 3427e98d785SM Chetan Kumar */ 3437e98d785SM Chetan Kumar static int __maybe_unused ipc_pcie_suspend_s2idle(struct iosm_pcie *ipc_pcie) 3447e98d785SM Chetan Kumar { 3457e98d785SM Chetan Kumar ipc_cp_irq_sleep_control(ipc_pcie, IPC_MEM_DEV_PM_FORCE_SLEEP); 3467e98d785SM Chetan Kumar 3477e98d785SM Chetan Kumar /* Complete all memory stores before setting bit */ 3487e98d785SM Chetan Kumar smp_mb__before_atomic(); 3497e98d785SM Chetan Kumar 3507e98d785SM Chetan Kumar set_bit(0, &ipc_pcie->suspend); 3517e98d785SM Chetan Kumar 3527e98d785SM Chetan Kumar /* Complete all memory stores after setting bit */ 3537e98d785SM Chetan Kumar smp_mb__after_atomic(); 3547e98d785SM Chetan Kumar 3557e98d785SM Chetan Kumar ipc_imem_pm_s2idle_sleep(ipc_pcie->imem, true); 3567e98d785SM Chetan Kumar 3577e98d785SM Chetan Kumar return 0; 3587e98d785SM Chetan Kumar } 3597e98d785SM Chetan Kumar 3607e98d785SM Chetan Kumar /* Resume from sleep in s2idle case 3617e98d785SM Chetan Kumar */ 3627e98d785SM Chetan Kumar static int __maybe_unused ipc_pcie_resume_s2idle(struct iosm_pcie *ipc_pcie) 3637e98d785SM Chetan Kumar { 3647e98d785SM Chetan Kumar ipc_cp_irq_sleep_control(ipc_pcie, IPC_MEM_DEV_PM_FORCE_ACTIVE); 3657e98d785SM Chetan Kumar 3667e98d785SM Chetan Kumar ipc_imem_pm_s2idle_sleep(ipc_pcie->imem, false); 3677e98d785SM Chetan Kumar 3687e98d785SM Chetan Kumar /* Complete all memory stores before clearing bit. */ 3697e98d785SM Chetan Kumar smp_mb__before_atomic(); 3707e98d785SM Chetan Kumar 3717e98d785SM Chetan Kumar clear_bit(0, &ipc_pcie->suspend); 3727e98d785SM Chetan Kumar 3737e98d785SM Chetan Kumar /* Complete all memory stores after clearing bit. */ 3747e98d785SM Chetan Kumar smp_mb__after_atomic(); 3757e98d785SM Chetan Kumar return 0; 3767e98d785SM Chetan Kumar } 3777e98d785SM Chetan Kumar 3787e98d785SM Chetan Kumar int __maybe_unused ipc_pcie_suspend(struct iosm_pcie *ipc_pcie) 3797e98d785SM Chetan Kumar { 3807e98d785SM Chetan Kumar /* The HAL shall ask the shared memory layer whether D3 is allowed. */ 3817e98d785SM Chetan Kumar ipc_imem_pm_suspend(ipc_pcie->imem); 3827e98d785SM Chetan Kumar 3837e98d785SM Chetan Kumar dev_dbg(ipc_pcie->dev, "SUSPEND done"); 3848f58e29eSKai-Heng Feng return 0; 3857e98d785SM Chetan Kumar } 3867e98d785SM Chetan Kumar 3877e98d785SM Chetan Kumar int __maybe_unused ipc_pcie_resume(struct iosm_pcie *ipc_pcie) 3887e98d785SM Chetan Kumar { 3897e98d785SM Chetan Kumar /* The HAL shall inform the shared memory layer that the device is 3907e98d785SM Chetan Kumar * active. 3917e98d785SM Chetan Kumar */ 3927e98d785SM Chetan Kumar ipc_imem_pm_resume(ipc_pcie->imem); 3937e98d785SM Chetan Kumar 3947e98d785SM Chetan Kumar dev_dbg(ipc_pcie->dev, "RESUME done"); 3958f58e29eSKai-Heng Feng return 0; 3967e98d785SM Chetan Kumar } 3977e98d785SM Chetan Kumar 3987e98d785SM Chetan Kumar static int __maybe_unused ipc_pcie_suspend_cb(struct device *dev) 3997e98d785SM Chetan Kumar { 4007e98d785SM Chetan Kumar struct iosm_pcie *ipc_pcie; 4017e98d785SM Chetan Kumar struct pci_dev *pdev; 4027e98d785SM Chetan Kumar 4037e98d785SM Chetan Kumar pdev = to_pci_dev(dev); 4047e98d785SM Chetan Kumar 4057e98d785SM Chetan Kumar ipc_pcie = pci_get_drvdata(pdev); 4067e98d785SM Chetan Kumar 4077e98d785SM Chetan Kumar switch (ipc_pcie->d3l2_support) { 4087e98d785SM Chetan Kumar case IPC_PCIE_D0L12: 4097e98d785SM Chetan Kumar ipc_pcie_suspend_s2idle(ipc_pcie); 4107e98d785SM Chetan Kumar break; 4117e98d785SM Chetan Kumar case IPC_PCIE_D3L2: 4127e98d785SM Chetan Kumar ipc_pcie_suspend(ipc_pcie); 4137e98d785SM Chetan Kumar break; 4147e98d785SM Chetan Kumar } 4157e98d785SM Chetan Kumar 4167e98d785SM Chetan Kumar return 0; 4177e98d785SM Chetan Kumar } 4187e98d785SM Chetan Kumar 4197e98d785SM Chetan Kumar static int __maybe_unused ipc_pcie_resume_cb(struct device *dev) 4207e98d785SM Chetan Kumar { 4217e98d785SM Chetan Kumar struct iosm_pcie *ipc_pcie; 4227e98d785SM Chetan Kumar struct pci_dev *pdev; 4237e98d785SM Chetan Kumar 4247e98d785SM Chetan Kumar pdev = to_pci_dev(dev); 4257e98d785SM Chetan Kumar 4267e98d785SM Chetan Kumar ipc_pcie = pci_get_drvdata(pdev); 4277e98d785SM Chetan Kumar 4287e98d785SM Chetan Kumar switch (ipc_pcie->d3l2_support) { 4297e98d785SM Chetan Kumar case IPC_PCIE_D0L12: 4307e98d785SM Chetan Kumar ipc_pcie_resume_s2idle(ipc_pcie); 4317e98d785SM Chetan Kumar break; 4327e98d785SM Chetan Kumar case IPC_PCIE_D3L2: 4337e98d785SM Chetan Kumar ipc_pcie_resume(ipc_pcie); 4347e98d785SM Chetan Kumar break; 4357e98d785SM Chetan Kumar } 4367e98d785SM Chetan Kumar 4377e98d785SM Chetan Kumar return 0; 4387e98d785SM Chetan Kumar } 4397e98d785SM Chetan Kumar 440*1db34aa5SBagas Sanjaya static SIMPLE_DEV_PM_OPS(iosm_ipc_pm, ipc_pcie_suspend_cb, ipc_pcie_resume_cb); 4417e98d785SM Chetan Kumar 4427e98d785SM Chetan Kumar static struct pci_driver iosm_ipc_driver = { 4437e98d785SM Chetan Kumar .name = KBUILD_MODNAME, 4447e98d785SM Chetan Kumar .probe = ipc_pcie_probe, 4457e98d785SM Chetan Kumar .remove = ipc_pcie_remove, 4467e98d785SM Chetan Kumar .driver = { 4477e98d785SM Chetan Kumar .pm = &iosm_ipc_pm, 4487e98d785SM Chetan Kumar }, 4497e98d785SM Chetan Kumar .id_table = iosm_ipc_ids, 4507e98d785SM Chetan Kumar }; 4517f8b20d0SAndy Shevchenko module_pci_driver(iosm_ipc_driver); 4527e98d785SM Chetan Kumar 4537e98d785SM Chetan Kumar int ipc_pcie_addr_map(struct iosm_pcie *ipc_pcie, unsigned char *data, 4547e98d785SM Chetan Kumar size_t size, dma_addr_t *mapping, int direction) 4557e98d785SM Chetan Kumar { 4567e98d785SM Chetan Kumar if (ipc_pcie->pci) { 4577e98d785SM Chetan Kumar *mapping = dma_map_single(&ipc_pcie->pci->dev, data, size, 4587e98d785SM Chetan Kumar direction); 4597e98d785SM Chetan Kumar if (dma_mapping_error(&ipc_pcie->pci->dev, *mapping)) { 4607e98d785SM Chetan Kumar dev_err(ipc_pcie->dev, "dma mapping failed"); 4617e98d785SM Chetan Kumar return -EINVAL; 4627e98d785SM Chetan Kumar } 4637e98d785SM Chetan Kumar } 4647e98d785SM Chetan Kumar return 0; 4657e98d785SM Chetan Kumar } 4667e98d785SM Chetan Kumar 4677e98d785SM Chetan Kumar void ipc_pcie_addr_unmap(struct iosm_pcie *ipc_pcie, size_t size, 4687e98d785SM Chetan Kumar dma_addr_t mapping, int direction) 4697e98d785SM Chetan Kumar { 4707e98d785SM Chetan Kumar if (!mapping) 4717e98d785SM Chetan Kumar return; 4727e98d785SM Chetan Kumar if (ipc_pcie->pci) 4737e98d785SM Chetan Kumar dma_unmap_single(&ipc_pcie->pci->dev, mapping, size, direction); 4747e98d785SM Chetan Kumar } 4757e98d785SM Chetan Kumar 4767e98d785SM Chetan Kumar struct sk_buff *ipc_pcie_alloc_local_skb(struct iosm_pcie *ipc_pcie, 4777e98d785SM Chetan Kumar gfp_t flags, size_t size) 4787e98d785SM Chetan Kumar { 4797e98d785SM Chetan Kumar struct sk_buff *skb; 4807e98d785SM Chetan Kumar 4817e98d785SM Chetan Kumar if (!ipc_pcie || !size) { 4827e98d785SM Chetan Kumar pr_err("invalid pcie object or size"); 4837e98d785SM Chetan Kumar return NULL; 4847e98d785SM Chetan Kumar } 4857e98d785SM Chetan Kumar 4867e98d785SM Chetan Kumar skb = __netdev_alloc_skb(NULL, size, flags); 4877e98d785SM Chetan Kumar if (!skb) 4887e98d785SM Chetan Kumar return NULL; 4897e98d785SM Chetan Kumar 4907e98d785SM Chetan Kumar IPC_CB(skb)->op_type = (u8)UL_DEFAULT; 4917e98d785SM Chetan Kumar IPC_CB(skb)->mapping = 0; 4927e98d785SM Chetan Kumar 4937e98d785SM Chetan Kumar return skb; 4947e98d785SM Chetan Kumar } 4957e98d785SM Chetan Kumar 4967e98d785SM Chetan Kumar struct sk_buff *ipc_pcie_alloc_skb(struct iosm_pcie *ipc_pcie, size_t size, 4977e98d785SM Chetan Kumar gfp_t flags, dma_addr_t *mapping, 4987e98d785SM Chetan Kumar int direction, size_t headroom) 4997e98d785SM Chetan Kumar { 5007e98d785SM Chetan Kumar struct sk_buff *skb = ipc_pcie_alloc_local_skb(ipc_pcie, flags, 5017e98d785SM Chetan Kumar size + headroom); 5027e98d785SM Chetan Kumar if (!skb) 5037e98d785SM Chetan Kumar return NULL; 5047e98d785SM Chetan Kumar 5057e98d785SM Chetan Kumar if (headroom) 5067e98d785SM Chetan Kumar skb_reserve(skb, headroom); 5077e98d785SM Chetan Kumar 5087e98d785SM Chetan Kumar if (ipc_pcie_addr_map(ipc_pcie, skb->data, size, mapping, direction)) { 5097e98d785SM Chetan Kumar dev_kfree_skb(skb); 5107e98d785SM Chetan Kumar return NULL; 5117e98d785SM Chetan Kumar } 5127e98d785SM Chetan Kumar 5137e98d785SM Chetan Kumar BUILD_BUG_ON(sizeof(*IPC_CB(skb)) > sizeof(skb->cb)); 5147e98d785SM Chetan Kumar 5157e98d785SM Chetan Kumar /* Store the mapping address in skb scratch pad for later usage */ 5167e98d785SM Chetan Kumar IPC_CB(skb)->mapping = *mapping; 5177e98d785SM Chetan Kumar IPC_CB(skb)->direction = direction; 5187e98d785SM Chetan Kumar IPC_CB(skb)->len = size; 5197e98d785SM Chetan Kumar 5207e98d785SM Chetan Kumar return skb; 5217e98d785SM Chetan Kumar } 5227e98d785SM Chetan Kumar 5237e98d785SM Chetan Kumar void ipc_pcie_kfree_skb(struct iosm_pcie *ipc_pcie, struct sk_buff *skb) 5247e98d785SM Chetan Kumar { 5257e98d785SM Chetan Kumar if (!skb) 5267e98d785SM Chetan Kumar return; 5277e98d785SM Chetan Kumar 5287e98d785SM Chetan Kumar ipc_pcie_addr_unmap(ipc_pcie, IPC_CB(skb)->len, IPC_CB(skb)->mapping, 5297e98d785SM Chetan Kumar IPC_CB(skb)->direction); 5307e98d785SM Chetan Kumar IPC_CB(skb)->mapping = 0; 5317e98d785SM Chetan Kumar dev_kfree_skb(skb); 5327e98d785SM Chetan Kumar } 533