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