xref: /linux/drivers/net/wwan/iosm/iosm_ipc_pcie.c (revision 7e98d785ae6184c7580a33619dae8b651769ff08)
1*7e98d785SM Chetan Kumar // SPDX-License-Identifier: GPL-2.0-only
2*7e98d785SM Chetan Kumar /*
3*7e98d785SM Chetan Kumar  * Copyright (C) 2020-21 Intel Corporation.
4*7e98d785SM Chetan Kumar  */
5*7e98d785SM Chetan Kumar 
6*7e98d785SM Chetan Kumar #include <linux/acpi.h>
7*7e98d785SM Chetan Kumar #include <linux/bitfield.h>
8*7e98d785SM Chetan Kumar #include <linux/module.h>
9*7e98d785SM Chetan Kumar #include <net/rtnetlink.h>
10*7e98d785SM Chetan Kumar 
11*7e98d785SM Chetan Kumar #include "iosm_ipc_imem.h"
12*7e98d785SM Chetan Kumar #include "iosm_ipc_pcie.h"
13*7e98d785SM Chetan Kumar #include "iosm_ipc_protocol.h"
14*7e98d785SM Chetan Kumar 
15*7e98d785SM Chetan Kumar MODULE_DESCRIPTION("IOSM Driver");
16*7e98d785SM Chetan Kumar MODULE_LICENSE("GPL v2");
17*7e98d785SM Chetan Kumar 
18*7e98d785SM Chetan Kumar /* WWAN GUID */
19*7e98d785SM Chetan Kumar static guid_t wwan_acpi_guid = GUID_INIT(0xbad01b75, 0x22a8, 0x4f48, 0x87, 0x92,
20*7e98d785SM Chetan Kumar 				       0xbd, 0xde, 0x94, 0x67, 0x74, 0x7d);
21*7e98d785SM Chetan Kumar 
22*7e98d785SM Chetan Kumar static void ipc_pcie_resources_release(struct iosm_pcie *ipc_pcie)
23*7e98d785SM Chetan Kumar {
24*7e98d785SM Chetan Kumar 	/* Free the MSI resources. */
25*7e98d785SM Chetan Kumar 	ipc_release_irq(ipc_pcie);
26*7e98d785SM Chetan Kumar 
27*7e98d785SM Chetan Kumar 	/* Free mapped doorbell scratchpad bus memory into CPU space. */
28*7e98d785SM Chetan Kumar 	iounmap(ipc_pcie->scratchpad);
29*7e98d785SM Chetan Kumar 
30*7e98d785SM Chetan Kumar 	/* Free mapped IPC_REGS bus memory into CPU space. */
31*7e98d785SM Chetan Kumar 	iounmap(ipc_pcie->ipc_regs);
32*7e98d785SM Chetan Kumar 
33*7e98d785SM Chetan Kumar 	/* Releases all PCI I/O and memory resources previously reserved by a
34*7e98d785SM Chetan Kumar 	 * successful call to pci_request_regions.  Call this function only
35*7e98d785SM Chetan Kumar 	 * after all use of the PCI regions has ceased.
36*7e98d785SM Chetan Kumar 	 */
37*7e98d785SM Chetan Kumar 	pci_release_regions(ipc_pcie->pci);
38*7e98d785SM Chetan Kumar }
39*7e98d785SM Chetan Kumar 
40*7e98d785SM Chetan Kumar static void ipc_pcie_cleanup(struct iosm_pcie *ipc_pcie)
41*7e98d785SM Chetan Kumar {
42*7e98d785SM Chetan Kumar 	/* Free the shared memory resources. */
43*7e98d785SM Chetan Kumar 	ipc_imem_cleanup(ipc_pcie->imem);
44*7e98d785SM Chetan Kumar 
45*7e98d785SM Chetan Kumar 	ipc_pcie_resources_release(ipc_pcie);
46*7e98d785SM Chetan Kumar 
47*7e98d785SM Chetan Kumar 	/* Signal to the system that the PCI device is not in use. */
48*7e98d785SM Chetan Kumar 	pci_disable_device(ipc_pcie->pci);
49*7e98d785SM Chetan Kumar }
50*7e98d785SM Chetan Kumar 
51*7e98d785SM Chetan Kumar static void ipc_pcie_deinit(struct iosm_pcie *ipc_pcie)
52*7e98d785SM Chetan Kumar {
53*7e98d785SM Chetan Kumar 	kfree(ipc_pcie->imem);
54*7e98d785SM Chetan Kumar 	kfree(ipc_pcie);
55*7e98d785SM Chetan Kumar }
56*7e98d785SM Chetan Kumar 
57*7e98d785SM Chetan Kumar static void ipc_pcie_remove(struct pci_dev *pci)
58*7e98d785SM Chetan Kumar {
59*7e98d785SM Chetan Kumar 	struct iosm_pcie *ipc_pcie = pci_get_drvdata(pci);
60*7e98d785SM Chetan Kumar 
61*7e98d785SM Chetan Kumar 	ipc_pcie_cleanup(ipc_pcie);
62*7e98d785SM Chetan Kumar 
63*7e98d785SM Chetan Kumar 	ipc_pcie_deinit(ipc_pcie);
64*7e98d785SM Chetan Kumar }
65*7e98d785SM Chetan Kumar 
66*7e98d785SM Chetan Kumar static int ipc_pcie_resources_request(struct iosm_pcie *ipc_pcie)
67*7e98d785SM Chetan Kumar {
68*7e98d785SM Chetan Kumar 	struct pci_dev *pci = ipc_pcie->pci;
69*7e98d785SM Chetan Kumar 	u32 cap = 0;
70*7e98d785SM Chetan Kumar 	u32 ret;
71*7e98d785SM Chetan Kumar 
72*7e98d785SM Chetan Kumar 	/* Reserved PCI I/O and memory resources.
73*7e98d785SM Chetan Kumar 	 * Mark all PCI regions associated with PCI device pci as
74*7e98d785SM Chetan Kumar 	 * being reserved by owner IOSM_IPC.
75*7e98d785SM Chetan Kumar 	 */
76*7e98d785SM Chetan Kumar 	ret = pci_request_regions(pci, "IOSM_IPC");
77*7e98d785SM Chetan Kumar 	if (ret) {
78*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "failed pci request regions");
79*7e98d785SM Chetan Kumar 		goto pci_request_region_fail;
80*7e98d785SM Chetan Kumar 	}
81*7e98d785SM Chetan Kumar 
82*7e98d785SM Chetan Kumar 	/* Reserve the doorbell IPC REGS memory resources.
83*7e98d785SM Chetan Kumar 	 * Remap the memory into CPU space. Arrange for the physical address
84*7e98d785SM Chetan Kumar 	 * (BAR) to be visible from this driver.
85*7e98d785SM Chetan Kumar 	 * pci_ioremap_bar() ensures that the memory is marked uncachable.
86*7e98d785SM Chetan Kumar 	 */
87*7e98d785SM Chetan Kumar 	ipc_pcie->ipc_regs = pci_ioremap_bar(pci, ipc_pcie->ipc_regs_bar_nr);
88*7e98d785SM Chetan Kumar 
89*7e98d785SM Chetan Kumar 	if (!ipc_pcie->ipc_regs) {
90*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "IPC REGS ioremap error");
91*7e98d785SM Chetan Kumar 		ret = -EBUSY;
92*7e98d785SM Chetan Kumar 		goto ipc_regs_remap_fail;
93*7e98d785SM Chetan Kumar 	}
94*7e98d785SM Chetan Kumar 
95*7e98d785SM Chetan Kumar 	/* Reserve the MMIO scratchpad memory resources.
96*7e98d785SM Chetan Kumar 	 * Remap the memory into CPU space. Arrange for the physical address
97*7e98d785SM Chetan Kumar 	 * (BAR) to be visible from this driver.
98*7e98d785SM Chetan Kumar 	 * pci_ioremap_bar() ensures that the memory is marked uncachable.
99*7e98d785SM Chetan Kumar 	 */
100*7e98d785SM Chetan Kumar 	ipc_pcie->scratchpad =
101*7e98d785SM Chetan Kumar 		pci_ioremap_bar(pci, ipc_pcie->scratchpad_bar_nr);
102*7e98d785SM Chetan Kumar 
103*7e98d785SM Chetan Kumar 	if (!ipc_pcie->scratchpad) {
104*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "doorbell scratchpad ioremap error");
105*7e98d785SM Chetan Kumar 		ret = -EBUSY;
106*7e98d785SM Chetan Kumar 		goto scratch_remap_fail;
107*7e98d785SM Chetan Kumar 	}
108*7e98d785SM Chetan Kumar 
109*7e98d785SM Chetan Kumar 	/* Install the irq handler triggered by CP. */
110*7e98d785SM Chetan Kumar 	ret = ipc_acquire_irq(ipc_pcie);
111*7e98d785SM Chetan Kumar 	if (ret) {
112*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "acquiring MSI irq failed!");
113*7e98d785SM Chetan Kumar 		goto irq_acquire_fail;
114*7e98d785SM Chetan Kumar 	}
115*7e98d785SM Chetan Kumar 
116*7e98d785SM Chetan Kumar 	/* Enable bus-mastering for the IOSM IPC device. */
117*7e98d785SM Chetan Kumar 	pci_set_master(pci);
118*7e98d785SM Chetan Kumar 
119*7e98d785SM Chetan Kumar 	/* Enable LTR if possible
120*7e98d785SM Chetan Kumar 	 * This is needed for L1.2!
121*7e98d785SM Chetan Kumar 	 */
122*7e98d785SM Chetan Kumar 	pcie_capability_read_dword(ipc_pcie->pci, PCI_EXP_DEVCAP2, &cap);
123*7e98d785SM Chetan Kumar 	if (cap & PCI_EXP_DEVCAP2_LTR)
124*7e98d785SM Chetan Kumar 		pcie_capability_set_word(ipc_pcie->pci, PCI_EXP_DEVCTL2,
125*7e98d785SM Chetan Kumar 					 PCI_EXP_DEVCTL2_LTR_EN);
126*7e98d785SM Chetan Kumar 
127*7e98d785SM Chetan Kumar 	dev_dbg(ipc_pcie->dev, "link between AP and CP is fully on");
128*7e98d785SM Chetan Kumar 
129*7e98d785SM Chetan Kumar 	return ret;
130*7e98d785SM Chetan Kumar 
131*7e98d785SM Chetan Kumar irq_acquire_fail:
132*7e98d785SM Chetan Kumar 	iounmap(ipc_pcie->scratchpad);
133*7e98d785SM Chetan Kumar scratch_remap_fail:
134*7e98d785SM Chetan Kumar 	iounmap(ipc_pcie->ipc_regs);
135*7e98d785SM Chetan Kumar ipc_regs_remap_fail:
136*7e98d785SM Chetan Kumar 	pci_release_regions(pci);
137*7e98d785SM Chetan Kumar pci_request_region_fail:
138*7e98d785SM Chetan Kumar 	return ret;
139*7e98d785SM Chetan Kumar }
140*7e98d785SM Chetan Kumar 
141*7e98d785SM Chetan Kumar bool ipc_pcie_check_aspm_enabled(struct iosm_pcie *ipc_pcie,
142*7e98d785SM Chetan Kumar 				 bool parent)
143*7e98d785SM Chetan Kumar {
144*7e98d785SM Chetan Kumar 	struct pci_dev *pdev;
145*7e98d785SM Chetan Kumar 	u16 value = 0;
146*7e98d785SM Chetan Kumar 	u32 enabled;
147*7e98d785SM Chetan Kumar 
148*7e98d785SM Chetan Kumar 	if (parent)
149*7e98d785SM Chetan Kumar 		pdev = ipc_pcie->pci->bus->self;
150*7e98d785SM Chetan Kumar 	else
151*7e98d785SM Chetan Kumar 		pdev = ipc_pcie->pci;
152*7e98d785SM Chetan Kumar 
153*7e98d785SM Chetan Kumar 	pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &value);
154*7e98d785SM Chetan Kumar 	enabled = value & PCI_EXP_LNKCTL_ASPMC;
155*7e98d785SM Chetan Kumar 	dev_dbg(ipc_pcie->dev, "ASPM L1: 0x%04X 0x%03X", pdev->device, value);
156*7e98d785SM Chetan Kumar 
157*7e98d785SM Chetan Kumar 	return (enabled == PCI_EXP_LNKCTL_ASPM_L1 ||
158*7e98d785SM Chetan Kumar 		enabled == PCI_EXP_LNKCTL_ASPMC);
159*7e98d785SM Chetan Kumar }
160*7e98d785SM Chetan Kumar 
161*7e98d785SM Chetan Kumar bool ipc_pcie_check_data_link_active(struct iosm_pcie *ipc_pcie)
162*7e98d785SM Chetan Kumar {
163*7e98d785SM Chetan Kumar 	struct pci_dev *parent;
164*7e98d785SM Chetan Kumar 	u16 link_status = 0;
165*7e98d785SM Chetan Kumar 
166*7e98d785SM Chetan Kumar 	if (!ipc_pcie->pci->bus || !ipc_pcie->pci->bus->self) {
167*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "root port not found");
168*7e98d785SM Chetan Kumar 		return false;
169*7e98d785SM Chetan Kumar 	}
170*7e98d785SM Chetan Kumar 
171*7e98d785SM Chetan Kumar 	parent = ipc_pcie->pci->bus->self;
172*7e98d785SM Chetan Kumar 
173*7e98d785SM Chetan Kumar 	pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &link_status);
174*7e98d785SM Chetan Kumar 	dev_dbg(ipc_pcie->dev, "Link status: 0x%04X", link_status);
175*7e98d785SM Chetan Kumar 
176*7e98d785SM Chetan Kumar 	return link_status & PCI_EXP_LNKSTA_DLLLA;
177*7e98d785SM Chetan Kumar }
178*7e98d785SM Chetan Kumar 
179*7e98d785SM Chetan Kumar static bool ipc_pcie_check_aspm_supported(struct iosm_pcie *ipc_pcie,
180*7e98d785SM Chetan Kumar 					  bool parent)
181*7e98d785SM Chetan Kumar {
182*7e98d785SM Chetan Kumar 	struct pci_dev *pdev;
183*7e98d785SM Chetan Kumar 	u32 support;
184*7e98d785SM Chetan Kumar 	u32 cap = 0;
185*7e98d785SM Chetan Kumar 
186*7e98d785SM Chetan Kumar 	if (parent)
187*7e98d785SM Chetan Kumar 		pdev = ipc_pcie->pci->bus->self;
188*7e98d785SM Chetan Kumar 	else
189*7e98d785SM Chetan Kumar 		pdev = ipc_pcie->pci;
190*7e98d785SM Chetan Kumar 	pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &cap);
191*7e98d785SM Chetan Kumar 	support = u32_get_bits(cap, PCI_EXP_LNKCAP_ASPMS);
192*7e98d785SM Chetan Kumar 	if (support < PCI_EXP_LNKCTL_ASPM_L1) {
193*7e98d785SM Chetan Kumar 		dev_dbg(ipc_pcie->dev, "ASPM L1 not supported: 0x%04X",
194*7e98d785SM Chetan Kumar 			pdev->device);
195*7e98d785SM Chetan Kumar 		return false;
196*7e98d785SM Chetan Kumar 	}
197*7e98d785SM Chetan Kumar 	return true;
198*7e98d785SM Chetan Kumar }
199*7e98d785SM Chetan Kumar 
200*7e98d785SM Chetan Kumar void ipc_pcie_config_aspm(struct iosm_pcie *ipc_pcie)
201*7e98d785SM Chetan Kumar {
202*7e98d785SM Chetan Kumar 	bool parent_aspm_enabled, dev_aspm_enabled;
203*7e98d785SM Chetan Kumar 
204*7e98d785SM Chetan Kumar 	/* check if both root port and child supports ASPM L1 */
205*7e98d785SM Chetan Kumar 	if (!ipc_pcie_check_aspm_supported(ipc_pcie, true) ||
206*7e98d785SM Chetan Kumar 	    !ipc_pcie_check_aspm_supported(ipc_pcie, false))
207*7e98d785SM Chetan Kumar 		return;
208*7e98d785SM Chetan Kumar 
209*7e98d785SM Chetan Kumar 	parent_aspm_enabled = ipc_pcie_check_aspm_enabled(ipc_pcie, true);
210*7e98d785SM Chetan Kumar 	dev_aspm_enabled = ipc_pcie_check_aspm_enabled(ipc_pcie, false);
211*7e98d785SM Chetan Kumar 
212*7e98d785SM Chetan Kumar 	dev_dbg(ipc_pcie->dev, "ASPM parent: %s device: %s",
213*7e98d785SM Chetan Kumar 		parent_aspm_enabled ? "Enabled" : "Disabled",
214*7e98d785SM Chetan Kumar 		dev_aspm_enabled ? "Enabled" : "Disabled");
215*7e98d785SM Chetan Kumar }
216*7e98d785SM Chetan Kumar 
217*7e98d785SM Chetan Kumar /* Initializes PCIe endpoint configuration */
218*7e98d785SM Chetan Kumar static void ipc_pcie_config_init(struct iosm_pcie *ipc_pcie)
219*7e98d785SM Chetan Kumar {
220*7e98d785SM Chetan Kumar 	/* BAR0 is used for doorbell */
221*7e98d785SM Chetan Kumar 	ipc_pcie->ipc_regs_bar_nr = IPC_DOORBELL_BAR0;
222*7e98d785SM Chetan Kumar 
223*7e98d785SM Chetan Kumar 	/* update HW configuration */
224*7e98d785SM Chetan Kumar 	ipc_pcie->scratchpad_bar_nr = IPC_SCRATCHPAD_BAR2;
225*7e98d785SM Chetan Kumar 	ipc_pcie->doorbell_reg_offset = IPC_DOORBELL_CH_OFFSET;
226*7e98d785SM Chetan Kumar 	ipc_pcie->doorbell_write = IPC_WRITE_PTR_REG_0;
227*7e98d785SM Chetan Kumar 	ipc_pcie->doorbell_capture = IPC_CAPTURE_PTR_REG_0;
228*7e98d785SM Chetan Kumar }
229*7e98d785SM Chetan Kumar 
230*7e98d785SM Chetan Kumar /* This will read the BIOS WWAN RTD3 settings:
231*7e98d785SM Chetan Kumar  * D0L1.2/D3L2/Disabled
232*7e98d785SM Chetan Kumar  */
233*7e98d785SM Chetan Kumar static enum ipc_pcie_sleep_state ipc_pcie_read_bios_cfg(struct device *dev)
234*7e98d785SM Chetan Kumar {
235*7e98d785SM Chetan Kumar 	union acpi_object *object;
236*7e98d785SM Chetan Kumar 	acpi_handle handle_acpi;
237*7e98d785SM Chetan Kumar 
238*7e98d785SM Chetan Kumar 	handle_acpi = ACPI_HANDLE(dev);
239*7e98d785SM Chetan Kumar 	if (!handle_acpi) {
240*7e98d785SM Chetan Kumar 		pr_debug("pci device is NOT ACPI supporting device\n");
241*7e98d785SM Chetan Kumar 		goto default_ret;
242*7e98d785SM Chetan Kumar 	}
243*7e98d785SM Chetan Kumar 
244*7e98d785SM Chetan Kumar 	object = acpi_evaluate_dsm(handle_acpi, &wwan_acpi_guid, 0, 3, NULL);
245*7e98d785SM Chetan Kumar 
246*7e98d785SM Chetan Kumar 	if (object && object->integer.value == 3)
247*7e98d785SM Chetan Kumar 		return IPC_PCIE_D3L2;
248*7e98d785SM Chetan Kumar 
249*7e98d785SM Chetan Kumar default_ret:
250*7e98d785SM Chetan Kumar 	return IPC_PCIE_D0L12;
251*7e98d785SM Chetan Kumar }
252*7e98d785SM Chetan Kumar 
253*7e98d785SM Chetan Kumar static int ipc_pcie_probe(struct pci_dev *pci,
254*7e98d785SM Chetan Kumar 			  const struct pci_device_id *pci_id)
255*7e98d785SM Chetan Kumar {
256*7e98d785SM Chetan Kumar 	struct iosm_pcie *ipc_pcie = kzalloc(sizeof(*ipc_pcie), GFP_KERNEL);
257*7e98d785SM Chetan Kumar 
258*7e98d785SM Chetan Kumar 	pr_debug("Probing device 0x%X from the vendor 0x%X", pci_id->device,
259*7e98d785SM Chetan Kumar 		 pci_id->vendor);
260*7e98d785SM Chetan Kumar 
261*7e98d785SM Chetan Kumar 	if (!ipc_pcie)
262*7e98d785SM Chetan Kumar 		goto ret_fail;
263*7e98d785SM Chetan Kumar 
264*7e98d785SM Chetan Kumar 	/* Initialize ipc dbg component for the PCIe device */
265*7e98d785SM Chetan Kumar 	ipc_pcie->dev = &pci->dev;
266*7e98d785SM Chetan Kumar 
267*7e98d785SM Chetan Kumar 	/* Set the driver specific data. */
268*7e98d785SM Chetan Kumar 	pci_set_drvdata(pci, ipc_pcie);
269*7e98d785SM Chetan Kumar 
270*7e98d785SM Chetan Kumar 	/* Save the address of the PCI device configuration. */
271*7e98d785SM Chetan Kumar 	ipc_pcie->pci = pci;
272*7e98d785SM Chetan Kumar 
273*7e98d785SM Chetan Kumar 	/* Update platform configuration */
274*7e98d785SM Chetan Kumar 	ipc_pcie_config_init(ipc_pcie);
275*7e98d785SM Chetan Kumar 
276*7e98d785SM Chetan Kumar 	/* Initialize the device before it is used. Ask low-level code
277*7e98d785SM Chetan Kumar 	 * to enable I/O and memory. Wake up the device if it was suspended.
278*7e98d785SM Chetan Kumar 	 */
279*7e98d785SM Chetan Kumar 	if (pci_enable_device(pci)) {
280*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "failed to enable the AP PCIe device");
281*7e98d785SM Chetan Kumar 		/* If enable of PCIe device has failed then calling
282*7e98d785SM Chetan Kumar 		 * ipc_pcie_cleanup will panic the system. More over
283*7e98d785SM Chetan Kumar 		 * ipc_pcie_cleanup() is required to be called after
284*7e98d785SM Chetan Kumar 		 * ipc_imem_mount()
285*7e98d785SM Chetan Kumar 		 */
286*7e98d785SM Chetan Kumar 		goto pci_enable_fail;
287*7e98d785SM Chetan Kumar 	}
288*7e98d785SM Chetan Kumar 
289*7e98d785SM Chetan Kumar 	ipc_pcie_config_aspm(ipc_pcie);
290*7e98d785SM Chetan Kumar 	dev_dbg(ipc_pcie->dev, "PCIe device enabled.");
291*7e98d785SM Chetan Kumar 
292*7e98d785SM Chetan Kumar 	/* Read WWAN RTD3 BIOS Setting
293*7e98d785SM Chetan Kumar 	 */
294*7e98d785SM Chetan Kumar 	ipc_pcie->d3l2_support = ipc_pcie_read_bios_cfg(&pci->dev);
295*7e98d785SM Chetan Kumar 
296*7e98d785SM Chetan Kumar 	ipc_pcie->suspend = 0;
297*7e98d785SM Chetan Kumar 
298*7e98d785SM Chetan Kumar 	if (ipc_pcie_resources_request(ipc_pcie))
299*7e98d785SM Chetan Kumar 		goto resources_req_fail;
300*7e98d785SM Chetan Kumar 
301*7e98d785SM Chetan Kumar 	/* Establish the link to the imem layer. */
302*7e98d785SM Chetan Kumar 	ipc_pcie->imem = ipc_imem_init(ipc_pcie, pci->device,
303*7e98d785SM Chetan Kumar 				       ipc_pcie->scratchpad, ipc_pcie->dev);
304*7e98d785SM Chetan Kumar 	if (!ipc_pcie->imem) {
305*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "failed to init imem");
306*7e98d785SM Chetan Kumar 		goto imem_init_fail;
307*7e98d785SM Chetan Kumar 	}
308*7e98d785SM Chetan Kumar 
309*7e98d785SM Chetan Kumar 	return 0;
310*7e98d785SM Chetan Kumar 
311*7e98d785SM Chetan Kumar imem_init_fail:
312*7e98d785SM Chetan Kumar 	ipc_pcie_resources_release(ipc_pcie);
313*7e98d785SM Chetan Kumar resources_req_fail:
314*7e98d785SM Chetan Kumar 	pci_disable_device(pci);
315*7e98d785SM Chetan Kumar pci_enable_fail:
316*7e98d785SM Chetan Kumar 	kfree(ipc_pcie);
317*7e98d785SM Chetan Kumar ret_fail:
318*7e98d785SM Chetan Kumar 	return -EIO;
319*7e98d785SM Chetan Kumar }
320*7e98d785SM Chetan Kumar 
321*7e98d785SM Chetan Kumar static const struct pci_device_id iosm_ipc_ids[] = {
322*7e98d785SM Chetan Kumar 	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CP_DEVICE_7560_ID) },
323*7e98d785SM Chetan Kumar 	{}
324*7e98d785SM Chetan Kumar };
325*7e98d785SM Chetan Kumar 
326*7e98d785SM Chetan Kumar /* Enter sleep in s2idle case
327*7e98d785SM Chetan Kumar  */
328*7e98d785SM Chetan Kumar static int __maybe_unused ipc_pcie_suspend_s2idle(struct iosm_pcie *ipc_pcie)
329*7e98d785SM Chetan Kumar {
330*7e98d785SM Chetan Kumar 	ipc_cp_irq_sleep_control(ipc_pcie, IPC_MEM_DEV_PM_FORCE_SLEEP);
331*7e98d785SM Chetan Kumar 
332*7e98d785SM Chetan Kumar 	/* Complete all memory stores before setting bit */
333*7e98d785SM Chetan Kumar 	smp_mb__before_atomic();
334*7e98d785SM Chetan Kumar 
335*7e98d785SM Chetan Kumar 	set_bit(0, &ipc_pcie->suspend);
336*7e98d785SM Chetan Kumar 
337*7e98d785SM Chetan Kumar 	/* Complete all memory stores after setting bit */
338*7e98d785SM Chetan Kumar 	smp_mb__after_atomic();
339*7e98d785SM Chetan Kumar 
340*7e98d785SM Chetan Kumar 	ipc_imem_pm_s2idle_sleep(ipc_pcie->imem, true);
341*7e98d785SM Chetan Kumar 
342*7e98d785SM Chetan Kumar 	return 0;
343*7e98d785SM Chetan Kumar }
344*7e98d785SM Chetan Kumar 
345*7e98d785SM Chetan Kumar /* Resume from sleep in s2idle case
346*7e98d785SM Chetan Kumar  */
347*7e98d785SM Chetan Kumar static int __maybe_unused ipc_pcie_resume_s2idle(struct iosm_pcie *ipc_pcie)
348*7e98d785SM Chetan Kumar {
349*7e98d785SM Chetan Kumar 	ipc_cp_irq_sleep_control(ipc_pcie, IPC_MEM_DEV_PM_FORCE_ACTIVE);
350*7e98d785SM Chetan Kumar 
351*7e98d785SM Chetan Kumar 	ipc_imem_pm_s2idle_sleep(ipc_pcie->imem, false);
352*7e98d785SM Chetan Kumar 
353*7e98d785SM Chetan Kumar 	/* Complete all memory stores before clearing bit. */
354*7e98d785SM Chetan Kumar 	smp_mb__before_atomic();
355*7e98d785SM Chetan Kumar 
356*7e98d785SM Chetan Kumar 	clear_bit(0, &ipc_pcie->suspend);
357*7e98d785SM Chetan Kumar 
358*7e98d785SM Chetan Kumar 	/* Complete all memory stores after clearing bit. */
359*7e98d785SM Chetan Kumar 	smp_mb__after_atomic();
360*7e98d785SM Chetan Kumar 	return 0;
361*7e98d785SM Chetan Kumar }
362*7e98d785SM Chetan Kumar 
363*7e98d785SM Chetan Kumar int __maybe_unused ipc_pcie_suspend(struct iosm_pcie *ipc_pcie)
364*7e98d785SM Chetan Kumar {
365*7e98d785SM Chetan Kumar 	struct pci_dev *pdev;
366*7e98d785SM Chetan Kumar 	int ret;
367*7e98d785SM Chetan Kumar 
368*7e98d785SM Chetan Kumar 	pdev = ipc_pcie->pci;
369*7e98d785SM Chetan Kumar 
370*7e98d785SM Chetan Kumar 	/* Execute D3 one time. */
371*7e98d785SM Chetan Kumar 	if (pdev->current_state != PCI_D0) {
372*7e98d785SM Chetan Kumar 		dev_dbg(ipc_pcie->dev, "done for PM=%d", pdev->current_state);
373*7e98d785SM Chetan Kumar 		return 0;
374*7e98d785SM Chetan Kumar 	}
375*7e98d785SM Chetan Kumar 
376*7e98d785SM Chetan Kumar 	/* The HAL shall ask the shared memory layer whether D3 is allowed. */
377*7e98d785SM Chetan Kumar 	ipc_imem_pm_suspend(ipc_pcie->imem);
378*7e98d785SM Chetan Kumar 
379*7e98d785SM Chetan Kumar 	/* Save the PCI configuration space of a device before suspending. */
380*7e98d785SM Chetan Kumar 	ret = pci_save_state(pdev);
381*7e98d785SM Chetan Kumar 
382*7e98d785SM Chetan Kumar 	if (ret) {
383*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "pci_save_state error=%d", ret);
384*7e98d785SM Chetan Kumar 		return ret;
385*7e98d785SM Chetan Kumar 	}
386*7e98d785SM Chetan Kumar 
387*7e98d785SM Chetan Kumar 	/* Set the power state of a PCI device.
388*7e98d785SM Chetan Kumar 	 * Transition a device to a new power state, using the device's PCI PM
389*7e98d785SM Chetan Kumar 	 * registers.
390*7e98d785SM Chetan Kumar 	 */
391*7e98d785SM Chetan Kumar 	ret = pci_set_power_state(pdev, PCI_D3cold);
392*7e98d785SM Chetan Kumar 
393*7e98d785SM Chetan Kumar 	if (ret) {
394*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "pci_set_power_state error=%d", ret);
395*7e98d785SM Chetan Kumar 		return ret;
396*7e98d785SM Chetan Kumar 	}
397*7e98d785SM Chetan Kumar 
398*7e98d785SM Chetan Kumar 	dev_dbg(ipc_pcie->dev, "SUSPEND done");
399*7e98d785SM Chetan Kumar 	return ret;
400*7e98d785SM Chetan Kumar }
401*7e98d785SM Chetan Kumar 
402*7e98d785SM Chetan Kumar int __maybe_unused ipc_pcie_resume(struct iosm_pcie *ipc_pcie)
403*7e98d785SM Chetan Kumar {
404*7e98d785SM Chetan Kumar 	int ret;
405*7e98d785SM Chetan Kumar 
406*7e98d785SM Chetan Kumar 	/* Set the power state of a PCI device.
407*7e98d785SM Chetan Kumar 	 * Transition a device to a new power state, using the device's PCI PM
408*7e98d785SM Chetan Kumar 	 * registers.
409*7e98d785SM Chetan Kumar 	 */
410*7e98d785SM Chetan Kumar 	ret = pci_set_power_state(ipc_pcie->pci, PCI_D0);
411*7e98d785SM Chetan Kumar 
412*7e98d785SM Chetan Kumar 	if (ret) {
413*7e98d785SM Chetan Kumar 		dev_err(ipc_pcie->dev, "pci_set_power_state error=%d", ret);
414*7e98d785SM Chetan Kumar 		return ret;
415*7e98d785SM Chetan Kumar 	}
416*7e98d785SM Chetan Kumar 
417*7e98d785SM Chetan Kumar 	pci_restore_state(ipc_pcie->pci);
418*7e98d785SM Chetan Kumar 
419*7e98d785SM Chetan Kumar 	/* The HAL shall inform the shared memory layer that the device is
420*7e98d785SM Chetan Kumar 	 * active.
421*7e98d785SM Chetan Kumar 	 */
422*7e98d785SM Chetan Kumar 	ipc_imem_pm_resume(ipc_pcie->imem);
423*7e98d785SM Chetan Kumar 
424*7e98d785SM Chetan Kumar 	dev_dbg(ipc_pcie->dev, "RESUME done");
425*7e98d785SM Chetan Kumar 	return ret;
426*7e98d785SM Chetan Kumar }
427*7e98d785SM Chetan Kumar 
428*7e98d785SM Chetan Kumar static int __maybe_unused ipc_pcie_suspend_cb(struct device *dev)
429*7e98d785SM Chetan Kumar {
430*7e98d785SM Chetan Kumar 	struct iosm_pcie *ipc_pcie;
431*7e98d785SM Chetan Kumar 	struct pci_dev *pdev;
432*7e98d785SM Chetan Kumar 
433*7e98d785SM Chetan Kumar 	pdev = to_pci_dev(dev);
434*7e98d785SM Chetan Kumar 
435*7e98d785SM Chetan Kumar 	ipc_pcie = pci_get_drvdata(pdev);
436*7e98d785SM Chetan Kumar 
437*7e98d785SM Chetan Kumar 	switch (ipc_pcie->d3l2_support) {
438*7e98d785SM Chetan Kumar 	case IPC_PCIE_D0L12:
439*7e98d785SM Chetan Kumar 		ipc_pcie_suspend_s2idle(ipc_pcie);
440*7e98d785SM Chetan Kumar 		break;
441*7e98d785SM Chetan Kumar 	case IPC_PCIE_D3L2:
442*7e98d785SM Chetan Kumar 		ipc_pcie_suspend(ipc_pcie);
443*7e98d785SM Chetan Kumar 		break;
444*7e98d785SM Chetan Kumar 	}
445*7e98d785SM Chetan Kumar 
446*7e98d785SM Chetan Kumar 	return 0;
447*7e98d785SM Chetan Kumar }
448*7e98d785SM Chetan Kumar 
449*7e98d785SM Chetan Kumar static int __maybe_unused ipc_pcie_resume_cb(struct device *dev)
450*7e98d785SM Chetan Kumar {
451*7e98d785SM Chetan Kumar 	struct iosm_pcie *ipc_pcie;
452*7e98d785SM Chetan Kumar 	struct pci_dev *pdev;
453*7e98d785SM Chetan Kumar 
454*7e98d785SM Chetan Kumar 	pdev = to_pci_dev(dev);
455*7e98d785SM Chetan Kumar 
456*7e98d785SM Chetan Kumar 	ipc_pcie = pci_get_drvdata(pdev);
457*7e98d785SM Chetan Kumar 
458*7e98d785SM Chetan Kumar 	switch (ipc_pcie->d3l2_support) {
459*7e98d785SM Chetan Kumar 	case IPC_PCIE_D0L12:
460*7e98d785SM Chetan Kumar 		ipc_pcie_resume_s2idle(ipc_pcie);
461*7e98d785SM Chetan Kumar 		break;
462*7e98d785SM Chetan Kumar 	case IPC_PCIE_D3L2:
463*7e98d785SM Chetan Kumar 		ipc_pcie_resume(ipc_pcie);
464*7e98d785SM Chetan Kumar 		break;
465*7e98d785SM Chetan Kumar 	}
466*7e98d785SM Chetan Kumar 
467*7e98d785SM Chetan Kumar 	return 0;
468*7e98d785SM Chetan Kumar }
469*7e98d785SM Chetan Kumar 
470*7e98d785SM Chetan Kumar static SIMPLE_DEV_PM_OPS(iosm_ipc_pm, ipc_pcie_suspend_cb, ipc_pcie_resume_cb);
471*7e98d785SM Chetan Kumar 
472*7e98d785SM Chetan Kumar static struct pci_driver iosm_ipc_driver = {
473*7e98d785SM Chetan Kumar 	.name = KBUILD_MODNAME,
474*7e98d785SM Chetan Kumar 	.probe = ipc_pcie_probe,
475*7e98d785SM Chetan Kumar 	.remove = ipc_pcie_remove,
476*7e98d785SM Chetan Kumar 	.driver = {
477*7e98d785SM Chetan Kumar 		.pm = &iosm_ipc_pm,
478*7e98d785SM Chetan Kumar 	},
479*7e98d785SM Chetan Kumar 	.id_table = iosm_ipc_ids,
480*7e98d785SM Chetan Kumar };
481*7e98d785SM Chetan Kumar 
482*7e98d785SM Chetan Kumar int ipc_pcie_addr_map(struct iosm_pcie *ipc_pcie, unsigned char *data,
483*7e98d785SM Chetan Kumar 		      size_t size, dma_addr_t *mapping, int direction)
484*7e98d785SM Chetan Kumar {
485*7e98d785SM Chetan Kumar 	if (ipc_pcie->pci) {
486*7e98d785SM Chetan Kumar 		*mapping = dma_map_single(&ipc_pcie->pci->dev, data, size,
487*7e98d785SM Chetan Kumar 					  direction);
488*7e98d785SM Chetan Kumar 		if (dma_mapping_error(&ipc_pcie->pci->dev, *mapping)) {
489*7e98d785SM Chetan Kumar 			dev_err(ipc_pcie->dev, "dma mapping failed");
490*7e98d785SM Chetan Kumar 			return -EINVAL;
491*7e98d785SM Chetan Kumar 		}
492*7e98d785SM Chetan Kumar 	}
493*7e98d785SM Chetan Kumar 	return 0;
494*7e98d785SM Chetan Kumar }
495*7e98d785SM Chetan Kumar 
496*7e98d785SM Chetan Kumar void ipc_pcie_addr_unmap(struct iosm_pcie *ipc_pcie, size_t size,
497*7e98d785SM Chetan Kumar 			 dma_addr_t mapping, int direction)
498*7e98d785SM Chetan Kumar {
499*7e98d785SM Chetan Kumar 	if (!mapping)
500*7e98d785SM Chetan Kumar 		return;
501*7e98d785SM Chetan Kumar 	if (ipc_pcie->pci)
502*7e98d785SM Chetan Kumar 		dma_unmap_single(&ipc_pcie->pci->dev, mapping, size, direction);
503*7e98d785SM Chetan Kumar }
504*7e98d785SM Chetan Kumar 
505*7e98d785SM Chetan Kumar struct sk_buff *ipc_pcie_alloc_local_skb(struct iosm_pcie *ipc_pcie,
506*7e98d785SM Chetan Kumar 					 gfp_t flags, size_t size)
507*7e98d785SM Chetan Kumar {
508*7e98d785SM Chetan Kumar 	struct sk_buff *skb;
509*7e98d785SM Chetan Kumar 
510*7e98d785SM Chetan Kumar 	if (!ipc_pcie || !size) {
511*7e98d785SM Chetan Kumar 		pr_err("invalid pcie object or size");
512*7e98d785SM Chetan Kumar 		return NULL;
513*7e98d785SM Chetan Kumar 	}
514*7e98d785SM Chetan Kumar 
515*7e98d785SM Chetan Kumar 	skb = __netdev_alloc_skb(NULL, size, flags);
516*7e98d785SM Chetan Kumar 	if (!skb)
517*7e98d785SM Chetan Kumar 		return NULL;
518*7e98d785SM Chetan Kumar 
519*7e98d785SM Chetan Kumar 	IPC_CB(skb)->op_type = (u8)UL_DEFAULT;
520*7e98d785SM Chetan Kumar 	IPC_CB(skb)->mapping = 0;
521*7e98d785SM Chetan Kumar 
522*7e98d785SM Chetan Kumar 	return skb;
523*7e98d785SM Chetan Kumar }
524*7e98d785SM Chetan Kumar 
525*7e98d785SM Chetan Kumar struct sk_buff *ipc_pcie_alloc_skb(struct iosm_pcie *ipc_pcie, size_t size,
526*7e98d785SM Chetan Kumar 				   gfp_t flags, dma_addr_t *mapping,
527*7e98d785SM Chetan Kumar 				   int direction, size_t headroom)
528*7e98d785SM Chetan Kumar {
529*7e98d785SM Chetan Kumar 	struct sk_buff *skb = ipc_pcie_alloc_local_skb(ipc_pcie, flags,
530*7e98d785SM Chetan Kumar 						       size + headroom);
531*7e98d785SM Chetan Kumar 	if (!skb)
532*7e98d785SM Chetan Kumar 		return NULL;
533*7e98d785SM Chetan Kumar 
534*7e98d785SM Chetan Kumar 	if (headroom)
535*7e98d785SM Chetan Kumar 		skb_reserve(skb, headroom);
536*7e98d785SM Chetan Kumar 
537*7e98d785SM Chetan Kumar 	if (ipc_pcie_addr_map(ipc_pcie, skb->data, size, mapping, direction)) {
538*7e98d785SM Chetan Kumar 		dev_kfree_skb(skb);
539*7e98d785SM Chetan Kumar 		return NULL;
540*7e98d785SM Chetan Kumar 	}
541*7e98d785SM Chetan Kumar 
542*7e98d785SM Chetan Kumar 	BUILD_BUG_ON(sizeof(*IPC_CB(skb)) > sizeof(skb->cb));
543*7e98d785SM Chetan Kumar 
544*7e98d785SM Chetan Kumar 	/* Store the mapping address in skb scratch pad for later usage */
545*7e98d785SM Chetan Kumar 	IPC_CB(skb)->mapping = *mapping;
546*7e98d785SM Chetan Kumar 	IPC_CB(skb)->direction = direction;
547*7e98d785SM Chetan Kumar 	IPC_CB(skb)->len = size;
548*7e98d785SM Chetan Kumar 
549*7e98d785SM Chetan Kumar 	return skb;
550*7e98d785SM Chetan Kumar }
551*7e98d785SM Chetan Kumar 
552*7e98d785SM Chetan Kumar void ipc_pcie_kfree_skb(struct iosm_pcie *ipc_pcie, struct sk_buff *skb)
553*7e98d785SM Chetan Kumar {
554*7e98d785SM Chetan Kumar 	if (!skb)
555*7e98d785SM Chetan Kumar 		return;
556*7e98d785SM Chetan Kumar 
557*7e98d785SM Chetan Kumar 	ipc_pcie_addr_unmap(ipc_pcie, IPC_CB(skb)->len, IPC_CB(skb)->mapping,
558*7e98d785SM Chetan Kumar 			    IPC_CB(skb)->direction);
559*7e98d785SM Chetan Kumar 	IPC_CB(skb)->mapping = 0;
560*7e98d785SM Chetan Kumar 	dev_kfree_skb(skb);
561*7e98d785SM Chetan Kumar }
562*7e98d785SM Chetan Kumar 
563*7e98d785SM Chetan Kumar static int __init iosm_ipc_driver_init(void)
564*7e98d785SM Chetan Kumar {
565*7e98d785SM Chetan Kumar 	if (pci_register_driver(&iosm_ipc_driver)) {
566*7e98d785SM Chetan Kumar 		pr_err("registering of IOSM PCIe driver failed");
567*7e98d785SM Chetan Kumar 		return -1;
568*7e98d785SM Chetan Kumar 	}
569*7e98d785SM Chetan Kumar 
570*7e98d785SM Chetan Kumar 	return 0;
571*7e98d785SM Chetan Kumar }
572*7e98d785SM Chetan Kumar 
573*7e98d785SM Chetan Kumar static void __exit iosm_ipc_driver_exit(void)
574*7e98d785SM Chetan Kumar {
575*7e98d785SM Chetan Kumar 	pci_unregister_driver(&iosm_ipc_driver);
576*7e98d785SM Chetan Kumar }
577*7e98d785SM Chetan Kumar 
578*7e98d785SM Chetan Kumar module_init(iosm_ipc_driver_init);
579*7e98d785SM Chetan Kumar module_exit(iosm_ipc_driver_exit);
580