1 /* 2 * PCI glue for ISHTP provider device (ISH) driver 3 * 4 * Copyright (c) 2014-2016, Intel Corporation. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 */ 15 16 #include <linux/module.h> 17 #include <linux/moduleparam.h> 18 #include <linux/kernel.h> 19 #include <linux/device.h> 20 #include <linux/fs.h> 21 #include <linux/errno.h> 22 #include <linux/types.h> 23 #include <linux/pci.h> 24 #include <linux/sched.h> 25 #include <linux/interrupt.h> 26 #include <linux/workqueue.h> 27 #define CREATE_TRACE_POINTS 28 #include <trace/events/intel_ish.h> 29 #include "ishtp-dev.h" 30 #include "hw-ish.h" 31 32 static const struct pci_device_id ish_pci_tbl[] = { 33 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)}, 34 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)}, 35 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)}, 36 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)}, 37 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)}, 38 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)}, 39 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)}, 40 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)}, 41 {0, } 42 }; 43 MODULE_DEVICE_TABLE(pci, ish_pci_tbl); 44 45 /** 46 * ish_event_tracer() - Callback function to dump trace messages 47 * @dev: ishtp device 48 * @format: printf style format 49 * 50 * Callback to direct log messages to Linux trace buffers 51 */ 52 static __printf(2, 3) 53 void ish_event_tracer(struct ishtp_device *dev, const char *format, ...) 54 { 55 if (trace_ishtp_dump_enabled()) { 56 va_list args; 57 char tmp_buf[100]; 58 59 va_start(args, format); 60 vsnprintf(tmp_buf, sizeof(tmp_buf), format, args); 61 va_end(args); 62 63 trace_ishtp_dump(tmp_buf); 64 } 65 } 66 67 /** 68 * ish_init() - Init function 69 * @dev: ishtp device 70 * 71 * This function initialize wait queues for suspend/resume and call 72 * calls hadware initialization function. This will initiate 73 * startup sequence 74 * 75 * Return: 0 for success or error code for failure 76 */ 77 static int ish_init(struct ishtp_device *dev) 78 { 79 int ret; 80 81 /* Set the state of ISH HW to start */ 82 ret = ish_hw_start(dev); 83 if (ret) { 84 dev_err(dev->devc, "ISH: hw start failed.\n"); 85 return ret; 86 } 87 88 /* Start the inter process communication to ISH processor */ 89 ret = ishtp_start(dev); 90 if (ret) { 91 dev_err(dev->devc, "ISHTP: Protocol init failed.\n"); 92 return ret; 93 } 94 95 return 0; 96 } 97 98 static const struct pci_device_id ish_invalid_pci_ids[] = { 99 /* Mehlow platform special pci ids */ 100 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)}, 101 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)}, 102 {} 103 }; 104 105 /** 106 * ish_probe() - PCI driver probe callback 107 * @pdev: pci device 108 * @ent: pci device id 109 * 110 * Initialize PCI function, setup interrupt and call for ISH initialization 111 * 112 * Return: 0 for success or error code for failure 113 */ 114 static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 115 { 116 struct ishtp_device *dev; 117 struct ish_hw *hw; 118 int ret; 119 120 /* Check for invalid platforms for ISH support */ 121 if (pci_dev_present(ish_invalid_pci_ids)) 122 return -ENODEV; 123 124 /* enable pci dev */ 125 ret = pci_enable_device(pdev); 126 if (ret) { 127 dev_err(&pdev->dev, "ISH: Failed to enable PCI device\n"); 128 return ret; 129 } 130 131 /* set PCI host mastering */ 132 pci_set_master(pdev); 133 134 /* pci request regions for ISH driver */ 135 ret = pci_request_regions(pdev, KBUILD_MODNAME); 136 if (ret) { 137 dev_err(&pdev->dev, "ISH: Failed to get PCI regions\n"); 138 goto disable_device; 139 } 140 141 /* allocates and initializes the ISH dev structure */ 142 dev = ish_dev_init(pdev); 143 if (!dev) { 144 ret = -ENOMEM; 145 goto release_regions; 146 } 147 hw = to_ish_hw(dev); 148 dev->print_log = ish_event_tracer; 149 150 /* mapping IO device memory */ 151 hw->mem_addr = pci_iomap(pdev, 0, 0); 152 if (!hw->mem_addr) { 153 dev_err(&pdev->dev, "ISH: mapping I/O range failure\n"); 154 ret = -ENOMEM; 155 goto free_device; 156 } 157 158 dev->pdev = pdev; 159 160 pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; 161 162 /* request and enable interrupt */ 163 ret = request_irq(pdev->irq, ish_irq_handler, IRQF_SHARED, 164 KBUILD_MODNAME, dev); 165 if (ret) { 166 dev_err(&pdev->dev, "ISH: request IRQ failure (%d)\n", 167 pdev->irq); 168 goto free_device; 169 } 170 171 dev_set_drvdata(dev->devc, dev); 172 173 init_waitqueue_head(&dev->suspend_wait); 174 init_waitqueue_head(&dev->resume_wait); 175 176 ret = ish_init(dev); 177 if (ret) 178 goto free_irq; 179 180 return 0; 181 182 free_irq: 183 free_irq(pdev->irq, dev); 184 free_device: 185 pci_iounmap(pdev, hw->mem_addr); 186 release_regions: 187 pci_release_regions(pdev); 188 disable_device: 189 pci_clear_master(pdev); 190 pci_disable_device(pdev); 191 dev_err(&pdev->dev, "ISH: PCI driver initialization failed.\n"); 192 193 return ret; 194 } 195 196 /** 197 * ish_remove() - PCI driver remove callback 198 * @pdev: pci device 199 * 200 * This function does cleanup of ISH on pci remove callback 201 */ 202 static void ish_remove(struct pci_dev *pdev) 203 { 204 struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev); 205 struct ish_hw *hw = to_ish_hw(ishtp_dev); 206 207 ishtp_bus_remove_all_clients(ishtp_dev, false); 208 ish_device_disable(ishtp_dev); 209 210 free_irq(pdev->irq, ishtp_dev); 211 pci_iounmap(pdev, hw->mem_addr); 212 pci_release_regions(pdev); 213 pci_clear_master(pdev); 214 pci_disable_device(pdev); 215 } 216 217 static struct device __maybe_unused *ish_resume_device; 218 219 /* 50ms to get resume response */ 220 #define WAIT_FOR_RESUME_ACK_MS 50 221 222 /** 223 * ish_resume_handler() - Work function to complete resume 224 * @work: work struct 225 * 226 * The resume work function to complete resume function asynchronously. 227 * There are two resume paths, one where ISH is not powered off, 228 * in that case a simple resume message is enough, others we need 229 * a reset sequence. 230 */ 231 static void __maybe_unused ish_resume_handler(struct work_struct *work) 232 { 233 struct pci_dev *pdev = to_pci_dev(ish_resume_device); 234 struct ishtp_device *dev = pci_get_drvdata(pdev); 235 uint32_t fwsts; 236 int ret; 237 238 /* Get ISH FW status */ 239 fwsts = IPC_GET_ISH_FWSTS(dev->ops->get_fw_status(dev)); 240 241 /* 242 * If currently, in ISH FW, sensor app is loaded or beyond that, 243 * it means ISH isn't powered off, in this case, send a resume message. 244 */ 245 if (fwsts >= FWSTS_SENSOR_APP_LOADED) { 246 ishtp_send_resume(dev); 247 248 /* Waiting to get resume response */ 249 if (dev->resume_flag) 250 ret = wait_event_interruptible_timeout(dev->resume_wait, 251 !dev->resume_flag, 252 msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS)); 253 } 254 255 /* 256 * If in ISH FW, sensor app isn't loaded yet, or no resume response. 257 * That means this platform is not S0ix compatible, or something is 258 * wrong with ISH FW. So on resume, full reboot of ISH processor will 259 * happen, so need to go through init sequence again. 260 */ 261 if (dev->resume_flag) 262 ish_init(dev); 263 } 264 265 /** 266 * ish_suspend() - ISH suspend callback 267 * @device: device pointer 268 * 269 * ISH suspend callback 270 * 271 * Return: 0 to the pm core 272 */ 273 static int __maybe_unused ish_suspend(struct device *device) 274 { 275 struct pci_dev *pdev = to_pci_dev(device); 276 struct ishtp_device *dev = pci_get_drvdata(pdev); 277 278 enable_irq_wake(pdev->irq); 279 /* 280 * If previous suspend hasn't been asnwered then ISH is likely dead, 281 * don't attempt nested notification 282 */ 283 if (dev->suspend_flag) 284 return 0; 285 286 dev->resume_flag = 0; 287 dev->suspend_flag = 1; 288 ishtp_send_suspend(dev); 289 290 /* 25 ms should be enough for live ISH to flush all IPC buf */ 291 if (dev->suspend_flag) 292 wait_event_interruptible_timeout(dev->suspend_wait, 293 !dev->suspend_flag, 294 msecs_to_jiffies(25)); 295 296 return 0; 297 } 298 299 static __maybe_unused DECLARE_WORK(resume_work, ish_resume_handler); 300 /** 301 * ish_resume() - ISH resume callback 302 * @device: device pointer 303 * 304 * ISH resume callback 305 * 306 * Return: 0 to the pm core 307 */ 308 static int __maybe_unused ish_resume(struct device *device) 309 { 310 struct pci_dev *pdev = to_pci_dev(device); 311 struct ishtp_device *dev = pci_get_drvdata(pdev); 312 313 ish_resume_device = device; 314 dev->resume_flag = 1; 315 316 disable_irq_wake(pdev->irq); 317 schedule_work(&resume_work); 318 319 return 0; 320 } 321 322 static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume); 323 324 static struct pci_driver ish_driver = { 325 .name = KBUILD_MODNAME, 326 .id_table = ish_pci_tbl, 327 .probe = ish_probe, 328 .remove = ish_remove, 329 .driver.pm = &ish_pm_ops, 330 }; 331 332 module_pci_driver(ish_driver); 333 334 /* Original author */ 335 MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>"); 336 /* Adoption to upstream Linux kernel */ 337 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 338 339 MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver"); 340 MODULE_LICENSE("GPL"); 341