pci-ish.c (10232B)
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 26static const struct pci_device_id ish_pci_tbl[] = { 27 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)}, 28 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)}, 29 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)}, 30 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)}, 31 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)}, 32 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)}, 33 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)}, 34 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)}, 35 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)}, 36 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)}, 37 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)}, 38 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CMP_H_DEVICE_ID)}, 39 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)}, 40 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_LP_DEVICE_ID)}, 41 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_H_DEVICE_ID)}, 42 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_S_DEVICE_ID)}, 43 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_P_DEVICE_ID)}, 44 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_N_DEVICE_ID)}, 45 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_DEVICE_ID)}, 46 {0, } 47}; 48MODULE_DEVICE_TABLE(pci, ish_pci_tbl); 49 50/** 51 * ish_event_tracer() - Callback function to dump trace messages 52 * @dev: ishtp device 53 * @format: printf style format 54 * 55 * Callback to direct log messages to Linux trace buffers 56 */ 57static __printf(2, 3) 58void ish_event_tracer(struct ishtp_device *dev, const char *format, ...) 59{ 60 if (trace_ishtp_dump_enabled()) { 61 va_list args; 62 char tmp_buf[100]; 63 64 va_start(args, format); 65 vsnprintf(tmp_buf, sizeof(tmp_buf), format, args); 66 va_end(args); 67 68 trace_ishtp_dump(tmp_buf); 69 } 70} 71 72/** 73 * ish_init() - Init function 74 * @dev: ishtp device 75 * 76 * This function initialize wait queues for suspend/resume and call 77 * calls hadware initialization function. This will initiate 78 * startup sequence 79 * 80 * Return: 0 for success or error code for failure 81 */ 82static int ish_init(struct ishtp_device *dev) 83{ 84 int ret; 85 86 /* Set the state of ISH HW to start */ 87 ret = ish_hw_start(dev); 88 if (ret) { 89 dev_err(dev->devc, "ISH: hw start failed.\n"); 90 return ret; 91 } 92 93 /* Start the inter process communication to ISH processor */ 94 ret = ishtp_start(dev); 95 if (ret) { 96 dev_err(dev->devc, "ISHTP: Protocol init failed.\n"); 97 return ret; 98 } 99 100 return 0; 101} 102 103static const struct pci_device_id ish_invalid_pci_ids[] = { 104 /* Mehlow platform special pci ids */ 105 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)}, 106 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)}, 107 {} 108}; 109 110static inline bool ish_should_enter_d0i3(struct pci_dev *pdev) 111{ 112 return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID; 113} 114 115static inline bool ish_should_leave_d0i3(struct pci_dev *pdev) 116{ 117 return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID; 118} 119 120static int enable_gpe(struct device *dev) 121{ 122#ifdef CONFIG_ACPI 123 acpi_status acpi_sts; 124 struct acpi_device *adev; 125 struct acpi_device_wakeup *wakeup; 126 127 adev = ACPI_COMPANION(dev); 128 if (!adev) { 129 dev_err(dev, "get acpi handle failed\n"); 130 return -ENODEV; 131 } 132 wakeup = &adev->wakeup; 133 134 acpi_sts = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number); 135 if (ACPI_FAILURE(acpi_sts)) { 136 dev_err(dev, "enable ose_gpe failed\n"); 137 return -EIO; 138 } 139 140 return 0; 141#else 142 return -ENODEV; 143#endif 144} 145 146static void enable_pme_wake(struct pci_dev *pdev) 147{ 148 if ((pci_pme_capable(pdev, PCI_D0) || 149 pci_pme_capable(pdev, PCI_D3hot) || 150 pci_pme_capable(pdev, PCI_D3cold)) && !enable_gpe(&pdev->dev)) { 151 pci_pme_active(pdev, true); 152 dev_dbg(&pdev->dev, "ish ipc driver pme wake enabled\n"); 153 } 154} 155 156/** 157 * ish_probe() - PCI driver probe callback 158 * @pdev: pci device 159 * @ent: pci device id 160 * 161 * Initialize PCI function, setup interrupt and call for ISH initialization 162 * 163 * Return: 0 for success or error code for failure 164 */ 165static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 166{ 167 int ret; 168 struct ish_hw *hw; 169 unsigned long irq_flag = 0; 170 struct ishtp_device *ishtp; 171 struct device *dev = &pdev->dev; 172 173 /* Check for invalid platforms for ISH support */ 174 if (pci_dev_present(ish_invalid_pci_ids)) 175 return -ENODEV; 176 177 /* enable pci dev */ 178 ret = pcim_enable_device(pdev); 179 if (ret) { 180 dev_err(dev, "ISH: Failed to enable PCI device\n"); 181 return ret; 182 } 183 184 /* set PCI host mastering */ 185 pci_set_master(pdev); 186 187 /* pci request regions for ISH driver */ 188 ret = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME); 189 if (ret) { 190 dev_err(dev, "ISH: Failed to get PCI regions\n"); 191 return ret; 192 } 193 194 /* allocates and initializes the ISH dev structure */ 195 ishtp = ish_dev_init(pdev); 196 if (!ishtp) { 197 ret = -ENOMEM; 198 return ret; 199 } 200 hw = to_ish_hw(ishtp); 201 ishtp->print_log = ish_event_tracer; 202 203 /* mapping IO device memory */ 204 hw->mem_addr = pcim_iomap_table(pdev)[0]; 205 ishtp->pdev = pdev; 206 207 /* request and enable interrupt */ 208 ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); 209 if (!pdev->msi_enabled && !pdev->msix_enabled) 210 irq_flag = IRQF_SHARED; 211 212 ret = devm_request_irq(dev, pdev->irq, ish_irq_handler, 213 irq_flag, KBUILD_MODNAME, ishtp); 214 if (ret) { 215 dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq); 216 return ret; 217 } 218 219 dev_set_drvdata(ishtp->devc, ishtp); 220 221 init_waitqueue_head(&ishtp->suspend_wait); 222 init_waitqueue_head(&ishtp->resume_wait); 223 224 /* Enable PME for EHL */ 225 if (pdev->device == EHL_Ax_DEVICE_ID) 226 enable_pme_wake(pdev); 227 228 ret = ish_init(ishtp); 229 if (ret) 230 return ret; 231 232 return 0; 233} 234 235/** 236 * ish_remove() - PCI driver remove callback 237 * @pdev: pci device 238 * 239 * This function does cleanup of ISH on pci remove callback 240 */ 241static void ish_remove(struct pci_dev *pdev) 242{ 243 struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev); 244 245 ishtp_bus_remove_all_clients(ishtp_dev, false); 246 ish_device_disable(ishtp_dev); 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 /* add this to finish power flow for EHL */ 372 if (dev->pdev->device == EHL_Ax_DEVICE_ID) { 373 pci_set_power_state(pdev, PCI_D0); 374 enable_pme_wake(pdev); 375 dev_dbg(dev->devc, "set power state to D0 for ehl\n"); 376 } 377 378 ish_resume_device = device; 379 dev->resume_flag = 1; 380 381 schedule_work(&resume_work); 382 383 return 0; 384} 385 386static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume); 387 388static struct pci_driver ish_driver = { 389 .name = KBUILD_MODNAME, 390 .id_table = ish_pci_tbl, 391 .probe = ish_probe, 392 .remove = ish_remove, 393 .driver.pm = &ish_pm_ops, 394}; 395 396module_pci_driver(ish_driver); 397 398/* Original author */ 399MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>"); 400/* Adoption to upstream Linux kernel */ 401MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 402 403MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver"); 404MODULE_LICENSE("GPL");