1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright(c) 2019-2022, Intel Corporation. All rights reserved. 4 * 5 * Intel Management Engine Interface (Intel MEI) Linux driver 6 */ 7 8 #include <linux/module.h> 9 #include <linux/mei_aux.h> 10 #include <linux/device.h> 11 #include <linux/irqreturn.h> 12 #include <linux/jiffies.h> 13 #include <linux/ktime.h> 14 #include <linux/delay.h> 15 #include <linux/pm_runtime.h> 16 17 #include "mei_dev.h" 18 #include "hw-me.h" 19 #include "hw-me-regs.h" 20 21 #include "mei-trace.h" 22 23 #define MEI_GSC_RPM_TIMEOUT 500 24 25 static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val) 26 { 27 struct mei_me_hw *hw = to_me_hw(dev); 28 29 *val = ioread32(hw->mem_addr + where + 0xC00); 30 31 return 0; 32 } 33 34 static int mei_gsc_probe(struct auxiliary_device *aux_dev, 35 const struct auxiliary_device_id *aux_dev_id) 36 { 37 struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev); 38 struct mei_device *dev; 39 struct mei_me_hw *hw; 40 struct device *device; 41 const struct mei_cfg *cfg; 42 int ret; 43 44 cfg = mei_me_get_cfg(aux_dev_id->driver_data); 45 if (!cfg) 46 return -ENODEV; 47 48 device = &aux_dev->dev; 49 50 dev = mei_me_dev_init(device, cfg); 51 if (!dev) { 52 ret = -ENOMEM; 53 goto err; 54 } 55 56 hw = to_me_hw(dev); 57 hw->mem_addr = devm_ioremap_resource(device, &adev->bar); 58 if (IS_ERR(hw->mem_addr)) { 59 ret = PTR_ERR(hw->mem_addr); 60 goto err; 61 } 62 63 hw->irq = adev->irq; 64 hw->read_fws = mei_gsc_read_hfs; 65 66 dev_set_drvdata(device, dev); 67 68 ret = devm_request_threaded_irq(device, hw->irq, 69 mei_me_irq_quick_handler, 70 mei_me_irq_thread_handler, 71 IRQF_ONESHOT, KBUILD_MODNAME, dev); 72 if (ret) { 73 dev_err(device, "irq register failed %d\n", ret); 74 goto err; 75 } 76 77 pm_runtime_get_noresume(device); 78 pm_runtime_set_active(device); 79 pm_runtime_enable(device); 80 81 /* Continue to char device setup in spite of firmware handshake failure. 82 * In order to provide access to the firmware status registers to the user 83 * space via sysfs. 84 */ 85 if (mei_start(dev)) 86 dev_warn(device, "init hw failure.\n"); 87 88 pm_runtime_set_autosuspend_delay(device, MEI_GSC_RPM_TIMEOUT); 89 pm_runtime_use_autosuspend(device); 90 91 ret = mei_register(dev, device); 92 if (ret) 93 goto register_err; 94 95 pm_runtime_put_noidle(device); 96 return 0; 97 98 register_err: 99 mei_stop(dev); 100 devm_free_irq(device, hw->irq, dev); 101 102 err: 103 dev_err(device, "probe failed: %d\n", ret); 104 dev_set_drvdata(device, NULL); 105 return ret; 106 } 107 108 static void mei_gsc_remove(struct auxiliary_device *aux_dev) 109 { 110 struct mei_device *dev; 111 struct mei_me_hw *hw; 112 113 dev = dev_get_drvdata(&aux_dev->dev); 114 if (!dev) 115 return; 116 117 hw = to_me_hw(dev); 118 119 mei_stop(dev); 120 121 mei_deregister(dev); 122 123 pm_runtime_disable(&aux_dev->dev); 124 125 mei_disable_interrupts(dev); 126 devm_free_irq(&aux_dev->dev, hw->irq, dev); 127 } 128 129 static int __maybe_unused mei_gsc_pm_suspend(struct device *device) 130 { 131 struct mei_device *dev = dev_get_drvdata(device); 132 133 if (!dev) 134 return -ENODEV; 135 136 mei_stop(dev); 137 138 mei_disable_interrupts(dev); 139 140 return 0; 141 } 142 143 static int __maybe_unused mei_gsc_pm_resume(struct device *device) 144 { 145 struct mei_device *dev = dev_get_drvdata(device); 146 int err; 147 148 if (!dev) 149 return -ENODEV; 150 151 err = mei_restart(dev); 152 if (err) 153 return err; 154 155 /* Start timer if stopped in suspend */ 156 schedule_delayed_work(&dev->timer_work, HZ); 157 158 return 0; 159 } 160 161 static int __maybe_unused mei_gsc_pm_runtime_idle(struct device *device) 162 { 163 struct mei_device *dev = dev_get_drvdata(device); 164 165 if (!dev) 166 return -ENODEV; 167 if (mei_write_is_idle(dev)) 168 pm_runtime_autosuspend(device); 169 170 return -EBUSY; 171 } 172 173 static int __maybe_unused mei_gsc_pm_runtime_suspend(struct device *device) 174 { 175 struct mei_device *dev = dev_get_drvdata(device); 176 struct mei_me_hw *hw; 177 int ret; 178 179 if (!dev) 180 return -ENODEV; 181 182 mutex_lock(&dev->device_lock); 183 184 if (mei_write_is_idle(dev)) { 185 hw = to_me_hw(dev); 186 hw->pg_state = MEI_PG_ON; 187 ret = 0; 188 } else { 189 ret = -EAGAIN; 190 } 191 192 mutex_unlock(&dev->device_lock); 193 194 return ret; 195 } 196 197 static int __maybe_unused mei_gsc_pm_runtime_resume(struct device *device) 198 { 199 struct mei_device *dev = dev_get_drvdata(device); 200 struct mei_me_hw *hw; 201 irqreturn_t irq_ret; 202 203 if (!dev) 204 return -ENODEV; 205 206 mutex_lock(&dev->device_lock); 207 208 hw = to_me_hw(dev); 209 hw->pg_state = MEI_PG_OFF; 210 211 mutex_unlock(&dev->device_lock); 212 213 irq_ret = mei_me_irq_thread_handler(1, dev); 214 if (irq_ret != IRQ_HANDLED) 215 dev_err(dev->dev, "thread handler fail %d\n", irq_ret); 216 217 return 0; 218 } 219 220 static const struct dev_pm_ops mei_gsc_pm_ops = { 221 SET_SYSTEM_SLEEP_PM_OPS(mei_gsc_pm_suspend, 222 mei_gsc_pm_resume) 223 SET_RUNTIME_PM_OPS(mei_gsc_pm_runtime_suspend, 224 mei_gsc_pm_runtime_resume, 225 mei_gsc_pm_runtime_idle) 226 }; 227 228 static const struct auxiliary_device_id mei_gsc_id_table[] = { 229 { 230 .name = "i915.mei-gsc", 231 .driver_data = MEI_ME_GSC_CFG, 232 233 }, 234 { 235 .name = "i915.mei-gscfi", 236 .driver_data = MEI_ME_GSCFI_CFG, 237 }, 238 { 239 /* sentinel */ 240 } 241 }; 242 MODULE_DEVICE_TABLE(auxiliary, mei_gsc_id_table); 243 244 static struct auxiliary_driver mei_gsc_driver = { 245 .probe = mei_gsc_probe, 246 .remove = mei_gsc_remove, 247 .driver = { 248 /* auxiliary_driver_register() sets .name to be the modname */ 249 .pm = &mei_gsc_pm_ops, 250 }, 251 .id_table = mei_gsc_id_table 252 }; 253 module_auxiliary_driver(mei_gsc_driver); 254 255 MODULE_AUTHOR("Intel Corporation"); 256 MODULE_ALIAS("auxiliary:i915.mei-gsc"); 257 MODULE_ALIAS("auxiliary:i915.mei-gscfi"); 258 MODULE_LICENSE("GPL"); 259