1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * PCI glue for ISHTP provider device (ISH) driver
4 *
5 * Copyright (c) 2014-2016, Intel Corporation.
6 */
7
8#include <linux/acpi.h>
9#include <linux/module.h>
10#include <linux/moduleparam.h>
11#include <linux/kernel.h>
12#include <linux/device.h>
13#include <linux/fs.h>
14#include <linux/errno.h>
15#include <linux/types.h>
16#include <linux/pci.h>
17#include <linux/sched.h>
18#include <linux/suspend.h>
19#include <linux/interrupt.h>
20#include <linux/workqueue.h>
21#define CREATE_TRACE_POINTS
22#include <trace/events/intel_ish.h>
23#include "ishtp-dev.h"
24#include "hw-ish.h"
25
26enum ishtp_driver_data_index {
27	ISHTP_DRIVER_DATA_NONE,
28	ISHTP_DRIVER_DATA_LNL_M,
29};
30
31#define ISH_FW_FILENAME_LNL_M "intel/ish/ish_lnlm.bin"
32
33static struct ishtp_driver_data ishtp_driver_data[] = {
34	[ISHTP_DRIVER_DATA_LNL_M] = {
35		.fw_filename = ISH_FW_FILENAME_LNL_M,
36	},
37};
38
39static const struct pci_device_id ish_pci_tbl[] = {
40	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CHV)},
41	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_BXT_Ax)},
42	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_BXT_Bx)},
43	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_APL_Ax)},
44	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_SPT_Ax)},
45	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CNL_Ax)},
46	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_GLK_Ax)},
47	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CNL_H)},
48	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ICL_MOBILE)},
49	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_SPT_H)},
50	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CML_LP)},
51	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CMP_H)},
52	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)},
53	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_TGL_LP)},
54	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_TGL_H)},
55	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_S)},
56	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_P)},
57	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_N)},
58	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_RPL_S)},
59	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_MTL_P)},
60	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ARL_H)},
61	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ARL_S)},
62	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_LNL_M), .driver_data = ISHTP_DRIVER_DATA_LNL_M},
63	{}
64};
65MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
66
67/**
68 * ish_event_tracer() - Callback function to dump trace messages
69 * @dev:	ishtp device
70 * @format:	printf style format
71 *
72 * Callback to direct log messages to Linux trace buffers
73 */
74static __printf(2, 3)
75void ish_event_tracer(struct ishtp_device *dev, const char *format, ...)
76{
77	if (trace_ishtp_dump_enabled()) {
78		va_list args;
79		char tmp_buf[100];
80
81		va_start(args, format);
82		vsnprintf(tmp_buf, sizeof(tmp_buf), format, args);
83		va_end(args);
84
85		trace_ishtp_dump(tmp_buf);
86	}
87}
88
89/**
90 * ish_init() - Init function
91 * @dev:	ishtp device
92 *
93 * This function initialize wait queues for suspend/resume and call
94 * calls hadware initialization function. This will initiate
95 * startup sequence
96 *
97 * Return: 0 for success or error code for failure
98 */
99static int ish_init(struct ishtp_device *dev)
100{
101	int ret;
102
103	/* Set the state of ISH HW to start */
104	ret = ish_hw_start(dev);
105	if (ret) {
106		dev_err(dev->devc, "ISH: hw start failed.\n");
107		return ret;
108	}
109
110	/* Start the inter process communication to ISH processor */
111	ret = ishtp_start(dev);
112	if (ret) {
113		dev_err(dev->devc, "ISHTP: Protocol init failed.\n");
114		return ret;
115	}
116
117	return 0;
118}
119
120static const struct pci_device_id ish_invalid_pci_ids[] = {
121	/* Mehlow platform special pci ids */
122	{PCI_VDEVICE(INTEL, 0xA309)},
123	{PCI_VDEVICE(INTEL, 0xA30A)},
124	{}
125};
126
127static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
128{
129	return !pm_suspend_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV;
130}
131
132static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
133{
134	return !pm_resume_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV;
135}
136
137/**
138 * ish_probe() - PCI driver probe callback
139 * @pdev:	pci device
140 * @ent:	pci device id
141 *
142 * Initialize PCI function, setup interrupt and call for ISH initialization
143 *
144 * Return: 0 for success or error code for failure
145 */
146static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
147{
148	int ret;
149	struct ish_hw *hw;
150	unsigned long irq_flag = 0;
151	struct ishtp_device *ishtp;
152	struct device *dev = &pdev->dev;
153
154	/* Check for invalid platforms for ISH support */
155	if (pci_dev_present(ish_invalid_pci_ids))
156		return -ENODEV;
157
158	/* enable pci dev */
159	ret = pcim_enable_device(pdev);
160	if (ret) {
161		dev_err(dev, "ISH: Failed to enable PCI device\n");
162		return ret;
163	}
164
165	/* set PCI host mastering */
166	pci_set_master(pdev);
167
168	/* pci request regions for ISH driver */
169	ret = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
170	if (ret) {
171		dev_err(dev, "ISH: Failed to get PCI regions\n");
172		return ret;
173	}
174
175	/* allocates and initializes the ISH dev structure */
176	ishtp = ish_dev_init(pdev);
177	if (!ishtp) {
178		ret = -ENOMEM;
179		return ret;
180	}
181	hw = to_ish_hw(ishtp);
182	ishtp->print_log = ish_event_tracer;
183	ishtp->driver_data = &ishtp_driver_data[ent->driver_data];
184
185	/* mapping IO device memory */
186	hw->mem_addr = pcim_iomap_table(pdev)[0];
187	ishtp->pdev = pdev;
188
189	/* request and enable interrupt */
190	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
191	if (ret < 0) {
192		dev_err(dev, "ISH: Failed to allocate IRQ vectors\n");
193		return ret;
194	}
195
196	if (!pdev->msi_enabled && !pdev->msix_enabled)
197		irq_flag = IRQF_SHARED;
198
199	ret = devm_request_irq(dev, pdev->irq, ish_irq_handler,
200			       irq_flag, KBUILD_MODNAME, ishtp);
201	if (ret) {
202		dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq);
203		return ret;
204	}
205
206	dev_set_drvdata(ishtp->devc, ishtp);
207
208	init_waitqueue_head(&ishtp->suspend_wait);
209	init_waitqueue_head(&ishtp->resume_wait);
210
211	/* Enable PME for EHL */
212	if (pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)
213		device_init_wakeup(dev, true);
214
215	ret = ish_init(ishtp);
216	if (ret)
217		return ret;
218
219	return 0;
220}
221
222/**
223 * ish_remove() - PCI driver remove callback
224 * @pdev:	pci device
225 *
226 * This function does cleanup of ISH on pci remove callback
227 */
228static void ish_remove(struct pci_dev *pdev)
229{
230	struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
231
232	ishtp_bus_remove_all_clients(ishtp_dev, false);
233	ish_device_disable(ishtp_dev);
234}
235
236
237/**
238 * ish_shutdown() - PCI driver shutdown callback
239 * @pdev:	pci device
240 *
241 * This function sets up wakeup for S5
242 */
243static void ish_shutdown(struct pci_dev *pdev)
244{
245	if (pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)
246		pci_prepare_to_sleep(pdev);
247}
248
249static struct device __maybe_unused *ish_resume_device;
250
251/* 50ms to get resume response */
252#define WAIT_FOR_RESUME_ACK_MS		50
253
254/**
255 * ish_resume_handler() - Work function to complete resume
256 * @work:	work struct
257 *
258 * The resume work function to complete resume function asynchronously.
259 * There are two resume paths, one where ISH is not powered off,
260 * in that case a simple resume message is enough, others we need
261 * a reset sequence.
262 */
263static void __maybe_unused ish_resume_handler(struct work_struct *work)
264{
265	struct pci_dev *pdev = to_pci_dev(ish_resume_device);
266	struct ishtp_device *dev = pci_get_drvdata(pdev);
267	uint32_t fwsts = dev->ops->get_fw_status(dev);
268
269	if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag
270			&& IPC_IS_ISH_ILUP(fwsts)) {
271		if (device_may_wakeup(&pdev->dev))
272			disable_irq_wake(pdev->irq);
273
274		ish_set_host_ready(dev);
275
276		ishtp_send_resume(dev);
277
278		/* Waiting to get resume response */
279		if (dev->resume_flag)
280			wait_event_interruptible_timeout(dev->resume_wait,
281				!dev->resume_flag,
282				msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS));
283
284		/*
285		 * If the flag is not cleared, something is wrong with ISH FW.
286		 * So on resume, need to go through init sequence again.
287		 */
288		if (dev->resume_flag)
289			ish_init(dev);
290	} else {
291		/*
292		 * Resume from the D3, full reboot of ISH processor will happen,
293		 * so need to go through init sequence again.
294		 */
295		ish_init(dev);
296	}
297}
298
299/**
300 * ish_suspend() - ISH suspend callback
301 * @device:	device pointer
302 *
303 * ISH suspend callback
304 *
305 * Return: 0 to the pm core
306 */
307static int __maybe_unused ish_suspend(struct device *device)
308{
309	struct pci_dev *pdev = to_pci_dev(device);
310	struct ishtp_device *dev = pci_get_drvdata(pdev);
311
312	if (ish_should_enter_d0i3(pdev)) {
313		/*
314		 * If previous suspend hasn't been asnwered then ISH is likely
315		 * dead, don't attempt nested notification
316		 */
317		if (dev->suspend_flag)
318			return	0;
319
320		dev->resume_flag = 0;
321		dev->suspend_flag = 1;
322		ishtp_send_suspend(dev);
323
324		/* 25 ms should be enough for live ISH to flush all IPC buf */
325		if (dev->suspend_flag)
326			wait_event_interruptible_timeout(dev->suspend_wait,
327					!dev->suspend_flag,
328					msecs_to_jiffies(25));
329
330		if (dev->suspend_flag) {
331			/*
332			 * It looks like FW halt, clear the DMA bit, and put
333			 * ISH into D3, and FW would reset on resume.
334			 */
335			ish_disable_dma(dev);
336		} else {
337			/*
338			 * Save state so PCI core will keep the device at D0,
339			 * the ISH would enter D0i3
340			 */
341			pci_save_state(pdev);
342
343			if (device_may_wakeup(&pdev->dev))
344				enable_irq_wake(pdev->irq);
345		}
346	} else {
347		/*
348		 * Clear the DMA bit before putting ISH into D3,
349		 * or ISH FW would reset automatically.
350		 */
351		ish_disable_dma(dev);
352	}
353
354	return 0;
355}
356
357static __maybe_unused DECLARE_WORK(resume_work, ish_resume_handler);
358/**
359 * ish_resume() - ISH resume callback
360 * @device:	device pointer
361 *
362 * ISH resume callback
363 *
364 * Return: 0 to the pm core
365 */
366static int __maybe_unused ish_resume(struct device *device)
367{
368	struct pci_dev *pdev = to_pci_dev(device);
369	struct ishtp_device *dev = pci_get_drvdata(pdev);
370
371	ish_resume_device = device;
372	dev->resume_flag = 1;
373
374	schedule_work(&resume_work);
375
376	return 0;
377}
378
379static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume);
380
381static struct pci_driver ish_driver = {
382	.name = KBUILD_MODNAME,
383	.id_table = ish_pci_tbl,
384	.probe = ish_probe,
385	.remove = ish_remove,
386	.shutdown = ish_shutdown,
387	.driver.pm = &ish_pm_ops,
388};
389
390module_pci_driver(ish_driver);
391
392/* Original author */
393MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
394/* Adoption to upstream Linux kernel */
395MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
396
397MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
398MODULE_LICENSE("GPL");
399
400MODULE_FIRMWARE(ISH_FW_FILENAME_LNL_M);
401