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
ipc_pcie_resources_release(struct iosm_pcie * ipc_pcie)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
ipc_pcie_cleanup(struct iosm_pcie * ipc_pcie)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
ipc_pcie_deinit(struct iosm_pcie * ipc_pcie)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
ipc_pcie_remove(struct pci_dev * pci)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
ipc_pcie_resources_request(struct iosm_pcie * ipc_pcie)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
ipc_pcie_check_aspm_enabled(struct iosm_pcie * ipc_pcie,bool parent)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
ipc_pcie_check_data_link_active(struct iosm_pcie * ipc_pcie)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
ipc_pcie_check_aspm_supported(struct iosm_pcie * ipc_pcie,bool parent)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
ipc_pcie_config_aspm(struct iosm_pcie * ipc_pcie)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 */
ipc_pcie_config_init(struct iosm_pcie * ipc_pcie)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 */
ipc_pcie_read_bios_cfg(struct device * dev)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
ipc_pcie_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)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 */
ipc_pcie_suspend_s2idle(struct iosm_pcie * ipc_pcie)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 */
ipc_pcie_resume_s2idle(struct iosm_pcie * ipc_pcie)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
ipc_pcie_suspend(struct iosm_pcie * ipc_pcie)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
ipc_pcie_resume(struct iosm_pcie * ipc_pcie)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
ipc_pcie_suspend_cb(struct device * dev)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
ipc_pcie_resume_cb(struct device * dev)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
ipc_pcie_addr_map(struct iosm_pcie * ipc_pcie,unsigned char * data,size_t size,dma_addr_t * mapping,int direction)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
ipc_pcie_addr_unmap(struct iosm_pcie * ipc_pcie,size_t size,dma_addr_t mapping,int direction)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
ipc_pcie_alloc_local_skb(struct iosm_pcie * ipc_pcie,gfp_t flags,size_t size)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
ipc_pcie_alloc_skb(struct iosm_pcie * ipc_pcie,size_t size,gfp_t flags,dma_addr_t * mapping,int direction,size_t headroom)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
ipc_pcie_kfree_skb(struct iosm_pcie * ipc_pcie,struct sk_buff * skb)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