1a98c30fdSTomas Winkler // SPDX-License-Identifier: GPL-2.0 2a98c30fdSTomas Winkler /* 3a98c30fdSTomas Winkler * Copyright(c) 2019-2022, Intel Corporation. All rights reserved. 4a98c30fdSTomas Winkler * 5a98c30fdSTomas Winkler * Intel Management Engine Interface (Intel MEI) Linux driver 6a98c30fdSTomas Winkler */ 7a98c30fdSTomas Winkler 8a98c30fdSTomas Winkler #include <linux/module.h> 9a98c30fdSTomas Winkler #include <linux/mei_aux.h> 10a98c30fdSTomas Winkler #include <linux/device.h> 11a98c30fdSTomas Winkler #include <linux/irqreturn.h> 12a98c30fdSTomas Winkler #include <linux/jiffies.h> 13a98c30fdSTomas Winkler #include <linux/ktime.h> 14a98c30fdSTomas Winkler #include <linux/delay.h> 15a98c30fdSTomas Winkler #include <linux/pm_runtime.h> 165b063995STomas Winkler #include <linux/kthread.h> 17a98c30fdSTomas Winkler 18a98c30fdSTomas Winkler #include "mei_dev.h" 19a98c30fdSTomas Winkler #include "hw-me.h" 20a98c30fdSTomas Winkler #include "hw-me-regs.h" 21a98c30fdSTomas Winkler 22a98c30fdSTomas Winkler #include "mei-trace.h" 23a98c30fdSTomas Winkler 24a98c30fdSTomas Winkler #define MEI_GSC_RPM_TIMEOUT 500 25a98c30fdSTomas Winkler 26a98c30fdSTomas Winkler static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val) 27a98c30fdSTomas Winkler { 28a98c30fdSTomas Winkler struct mei_me_hw *hw = to_me_hw(dev); 29a98c30fdSTomas Winkler 30a98c30fdSTomas Winkler *val = ioread32(hw->mem_addr + where + 0xC00); 31a98c30fdSTomas Winkler 32a98c30fdSTomas Winkler return 0; 33a98c30fdSTomas Winkler } 34a98c30fdSTomas Winkler 35a98c30fdSTomas Winkler static int mei_gsc_probe(struct auxiliary_device *aux_dev, 36a98c30fdSTomas Winkler const struct auxiliary_device_id *aux_dev_id) 37a98c30fdSTomas Winkler { 38a98c30fdSTomas Winkler struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev); 39a98c30fdSTomas Winkler struct mei_device *dev; 40a98c30fdSTomas Winkler struct mei_me_hw *hw; 41a98c30fdSTomas Winkler struct device *device; 42a98c30fdSTomas Winkler const struct mei_cfg *cfg; 43a98c30fdSTomas Winkler int ret; 44a98c30fdSTomas Winkler 45a98c30fdSTomas Winkler cfg = mei_me_get_cfg(aux_dev_id->driver_data); 46a98c30fdSTomas Winkler if (!cfg) 47a98c30fdSTomas Winkler return -ENODEV; 48a98c30fdSTomas Winkler 49a98c30fdSTomas Winkler device = &aux_dev->dev; 50a98c30fdSTomas Winkler 51*95953618SAlexander Usyskin dev = mei_me_dev_init(device, cfg, adev->slow_firmware); 52a98c30fdSTomas Winkler if (!dev) { 53a98c30fdSTomas Winkler ret = -ENOMEM; 54a98c30fdSTomas Winkler goto err; 55a98c30fdSTomas Winkler } 56a98c30fdSTomas Winkler 57a98c30fdSTomas Winkler hw = to_me_hw(dev); 58a98c30fdSTomas Winkler hw->mem_addr = devm_ioremap_resource(device, &adev->bar); 59a98c30fdSTomas Winkler if (IS_ERR(hw->mem_addr)) { 60a98c30fdSTomas Winkler dev_err(device, "mmio not mapped\n"); 61a98c30fdSTomas Winkler ret = PTR_ERR(hw->mem_addr); 62a98c30fdSTomas Winkler goto err; 63a98c30fdSTomas Winkler } 64a98c30fdSTomas Winkler 65a98c30fdSTomas Winkler hw->irq = adev->irq; 66a98c30fdSTomas Winkler hw->read_fws = mei_gsc_read_hfs; 67a98c30fdSTomas Winkler 68a98c30fdSTomas Winkler dev_set_drvdata(device, dev); 69a98c30fdSTomas Winkler 705b063995STomas Winkler /* use polling */ 715b063995STomas Winkler if (mei_me_hw_use_polling(hw)) { 725b063995STomas Winkler mei_disable_interrupts(dev); 735b063995STomas Winkler mei_clear_interrupts(dev); 745b063995STomas Winkler init_waitqueue_head(&hw->wait_active); 755b063995STomas Winkler hw->is_active = true; /* start in active mode for initialization */ 765b063995STomas Winkler hw->polling_thread = kthread_run(mei_me_polling_thread, dev, 775b063995STomas Winkler "kmegscirqd/%s", dev_name(device)); 785b063995STomas Winkler if (IS_ERR(hw->polling_thread)) { 795b063995STomas Winkler ret = PTR_ERR(hw->polling_thread); 805b063995STomas Winkler dev_err(device, "unable to create kernel thread: %d\n", ret); 815b063995STomas Winkler goto err; 825b063995STomas Winkler } 835b063995STomas Winkler } else { 84a98c30fdSTomas Winkler ret = devm_request_threaded_irq(device, hw->irq, 85a98c30fdSTomas Winkler mei_me_irq_quick_handler, 86a98c30fdSTomas Winkler mei_me_irq_thread_handler, 87a98c30fdSTomas Winkler IRQF_ONESHOT, KBUILD_MODNAME, dev); 88a98c30fdSTomas Winkler if (ret) { 89a98c30fdSTomas Winkler dev_err(device, "irq register failed %d\n", ret); 90a98c30fdSTomas Winkler goto err; 91a98c30fdSTomas Winkler } 925b063995STomas Winkler } 93a98c30fdSTomas Winkler 94a98c30fdSTomas Winkler pm_runtime_get_noresume(device); 95a98c30fdSTomas Winkler pm_runtime_set_active(device); 96a98c30fdSTomas Winkler pm_runtime_enable(device); 97a98c30fdSTomas Winkler 98ce97126dSAlexander Usyskin /* Continue to char device setup in spite of firmware handshake failure. 99ce97126dSAlexander Usyskin * In order to provide access to the firmware status registers to the user 100ce97126dSAlexander Usyskin * space via sysfs. 101ce97126dSAlexander Usyskin */ 102ce97126dSAlexander Usyskin if (mei_start(dev)) 103ce97126dSAlexander Usyskin dev_warn(device, "init hw failure.\n"); 104a98c30fdSTomas Winkler 105a98c30fdSTomas Winkler pm_runtime_set_autosuspend_delay(device, MEI_GSC_RPM_TIMEOUT); 106a98c30fdSTomas Winkler pm_runtime_use_autosuspend(device); 107a98c30fdSTomas Winkler 108a98c30fdSTomas Winkler ret = mei_register(dev, device); 109a98c30fdSTomas Winkler if (ret) 110a98c30fdSTomas Winkler goto register_err; 111a98c30fdSTomas Winkler 112a98c30fdSTomas Winkler pm_runtime_put_noidle(device); 113a98c30fdSTomas Winkler return 0; 114a98c30fdSTomas Winkler 115a98c30fdSTomas Winkler register_err: 116a98c30fdSTomas Winkler mei_stop(dev); 1175b063995STomas Winkler if (!mei_me_hw_use_polling(hw)) 118a98c30fdSTomas Winkler devm_free_irq(device, hw->irq, dev); 119a98c30fdSTomas Winkler 120a98c30fdSTomas Winkler err: 121a98c30fdSTomas Winkler dev_err(device, "probe failed: %d\n", ret); 122a98c30fdSTomas Winkler dev_set_drvdata(device, NULL); 123a98c30fdSTomas Winkler return ret; 124a98c30fdSTomas Winkler } 125a98c30fdSTomas Winkler 126a98c30fdSTomas Winkler static void mei_gsc_remove(struct auxiliary_device *aux_dev) 127a98c30fdSTomas Winkler { 128a98c30fdSTomas Winkler struct mei_device *dev; 129a98c30fdSTomas Winkler struct mei_me_hw *hw; 130a98c30fdSTomas Winkler 131a98c30fdSTomas Winkler dev = dev_get_drvdata(&aux_dev->dev); 132a98c30fdSTomas Winkler if (!dev) 133a98c30fdSTomas Winkler return; 134a98c30fdSTomas Winkler 135a98c30fdSTomas Winkler hw = to_me_hw(dev); 136a98c30fdSTomas Winkler 137a98c30fdSTomas Winkler mei_stop(dev); 138a98c30fdSTomas Winkler 1395b063995STomas Winkler hw = to_me_hw(dev); 1405b063995STomas Winkler if (mei_me_hw_use_polling(hw)) 1415b063995STomas Winkler kthread_stop(hw->polling_thread); 1425b063995STomas Winkler 143a98c30fdSTomas Winkler mei_deregister(dev); 144a98c30fdSTomas Winkler 145a98c30fdSTomas Winkler pm_runtime_disable(&aux_dev->dev); 146a98c30fdSTomas Winkler 147a98c30fdSTomas Winkler mei_disable_interrupts(dev); 1485b063995STomas Winkler if (!mei_me_hw_use_polling(hw)) 149a98c30fdSTomas Winkler devm_free_irq(&aux_dev->dev, hw->irq, dev); 150a98c30fdSTomas Winkler } 151a98c30fdSTomas Winkler 152a98c30fdSTomas Winkler static int __maybe_unused mei_gsc_pm_suspend(struct device *device) 153a98c30fdSTomas Winkler { 154a98c30fdSTomas Winkler struct mei_device *dev = dev_get_drvdata(device); 155a98c30fdSTomas Winkler 156a98c30fdSTomas Winkler if (!dev) 157a98c30fdSTomas Winkler return -ENODEV; 158a98c30fdSTomas Winkler 159a98c30fdSTomas Winkler mei_stop(dev); 160a98c30fdSTomas Winkler 161a98c30fdSTomas Winkler mei_disable_interrupts(dev); 162a98c30fdSTomas Winkler 163a98c30fdSTomas Winkler return 0; 164a98c30fdSTomas Winkler } 165a98c30fdSTomas Winkler 166a98c30fdSTomas Winkler static int __maybe_unused mei_gsc_pm_resume(struct device *device) 167a98c30fdSTomas Winkler { 168a98c30fdSTomas Winkler struct mei_device *dev = dev_get_drvdata(device); 169a98c30fdSTomas Winkler int err; 170a98c30fdSTomas Winkler 171a98c30fdSTomas Winkler if (!dev) 172a98c30fdSTomas Winkler return -ENODEV; 173a98c30fdSTomas Winkler 174a98c30fdSTomas Winkler err = mei_restart(dev); 175a98c30fdSTomas Winkler if (err) 176a98c30fdSTomas Winkler return err; 177a98c30fdSTomas Winkler 178a98c30fdSTomas Winkler /* Start timer if stopped in suspend */ 179a98c30fdSTomas Winkler schedule_delayed_work(&dev->timer_work, HZ); 180a98c30fdSTomas Winkler 181a98c30fdSTomas Winkler return 0; 182a98c30fdSTomas Winkler } 183a98c30fdSTomas Winkler 184ad10a354STomas Winkler static int __maybe_unused mei_gsc_pm_runtime_idle(struct device *device) 185ad10a354STomas Winkler { 186ad10a354STomas Winkler struct mei_device *dev = dev_get_drvdata(device); 187ad10a354STomas Winkler 188ad10a354STomas Winkler if (!dev) 189ad10a354STomas Winkler return -ENODEV; 190ad10a354STomas Winkler if (mei_write_is_idle(dev)) 191ad10a354STomas Winkler pm_runtime_autosuspend(device); 192ad10a354STomas Winkler 193ad10a354STomas Winkler return -EBUSY; 194ad10a354STomas Winkler } 195ad10a354STomas Winkler 196ad10a354STomas Winkler static int __maybe_unused mei_gsc_pm_runtime_suspend(struct device *device) 197ad10a354STomas Winkler { 198ad10a354STomas Winkler struct mei_device *dev = dev_get_drvdata(device); 199ad10a354STomas Winkler struct mei_me_hw *hw; 200ad10a354STomas Winkler int ret; 201ad10a354STomas Winkler 202ad10a354STomas Winkler if (!dev) 203ad10a354STomas Winkler return -ENODEV; 204ad10a354STomas Winkler 205ad10a354STomas Winkler mutex_lock(&dev->device_lock); 206ad10a354STomas Winkler 207ad10a354STomas Winkler if (mei_write_is_idle(dev)) { 208ad10a354STomas Winkler hw = to_me_hw(dev); 209ad10a354STomas Winkler hw->pg_state = MEI_PG_ON; 2105b063995STomas Winkler 2115b063995STomas Winkler if (mei_me_hw_use_polling(hw)) 2125b063995STomas Winkler hw->is_active = false; 213ad10a354STomas Winkler ret = 0; 214ad10a354STomas Winkler } else { 215ad10a354STomas Winkler ret = -EAGAIN; 216ad10a354STomas Winkler } 217ad10a354STomas Winkler 218ad10a354STomas Winkler mutex_unlock(&dev->device_lock); 219ad10a354STomas Winkler 220ad10a354STomas Winkler return ret; 221ad10a354STomas Winkler } 222ad10a354STomas Winkler 223ad10a354STomas Winkler static int __maybe_unused mei_gsc_pm_runtime_resume(struct device *device) 224ad10a354STomas Winkler { 225ad10a354STomas Winkler struct mei_device *dev = dev_get_drvdata(device); 226ad10a354STomas Winkler struct mei_me_hw *hw; 227ad10a354STomas Winkler irqreturn_t irq_ret; 228ad10a354STomas Winkler 229ad10a354STomas Winkler if (!dev) 230ad10a354STomas Winkler return -ENODEV; 231ad10a354STomas Winkler 232ad10a354STomas Winkler mutex_lock(&dev->device_lock); 233ad10a354STomas Winkler 234ad10a354STomas Winkler hw = to_me_hw(dev); 235ad10a354STomas Winkler hw->pg_state = MEI_PG_OFF; 236ad10a354STomas Winkler 2375b063995STomas Winkler if (mei_me_hw_use_polling(hw)) { 2385b063995STomas Winkler hw->is_active = true; 2395b063995STomas Winkler wake_up(&hw->wait_active); 2405b063995STomas Winkler } 2415b063995STomas Winkler 242ad10a354STomas Winkler mutex_unlock(&dev->device_lock); 243ad10a354STomas Winkler 244ad10a354STomas Winkler irq_ret = mei_me_irq_thread_handler(1, dev); 245ad10a354STomas Winkler if (irq_ret != IRQ_HANDLED) 246ad10a354STomas Winkler dev_err(dev->dev, "thread handler fail %d\n", irq_ret); 247ad10a354STomas Winkler 248ad10a354STomas Winkler return 0; 249ad10a354STomas Winkler } 250ad10a354STomas Winkler 251ad10a354STomas Winkler static const struct dev_pm_ops mei_gsc_pm_ops = { 252ad10a354STomas Winkler SET_SYSTEM_SLEEP_PM_OPS(mei_gsc_pm_suspend, 253ad10a354STomas Winkler mei_gsc_pm_resume) 254ad10a354STomas Winkler SET_RUNTIME_PM_OPS(mei_gsc_pm_runtime_suspend, 255ad10a354STomas Winkler mei_gsc_pm_runtime_resume, 256ad10a354STomas Winkler mei_gsc_pm_runtime_idle) 257ad10a354STomas Winkler }; 258a98c30fdSTomas Winkler 259a98c30fdSTomas Winkler static const struct auxiliary_device_id mei_gsc_id_table[] = { 260a98c30fdSTomas Winkler { 261a98c30fdSTomas Winkler .name = "i915.mei-gsc", 262a98c30fdSTomas Winkler .driver_data = MEI_ME_GSC_CFG, 263a98c30fdSTomas Winkler 264a98c30fdSTomas Winkler }, 265a98c30fdSTomas Winkler { 266a98c30fdSTomas Winkler .name = "i915.mei-gscfi", 267a98c30fdSTomas Winkler .driver_data = MEI_ME_GSCFI_CFG, 268a98c30fdSTomas Winkler }, 269a98c30fdSTomas Winkler { 270a98c30fdSTomas Winkler /* sentinel */ 271a98c30fdSTomas Winkler } 272a98c30fdSTomas Winkler }; 273a98c30fdSTomas Winkler MODULE_DEVICE_TABLE(auxiliary, mei_gsc_id_table); 274a98c30fdSTomas Winkler 275a98c30fdSTomas Winkler static struct auxiliary_driver mei_gsc_driver = { 276a98c30fdSTomas Winkler .probe = mei_gsc_probe, 277a98c30fdSTomas Winkler .remove = mei_gsc_remove, 278a98c30fdSTomas Winkler .driver = { 279a98c30fdSTomas Winkler /* auxiliary_driver_register() sets .name to be the modname */ 280a98c30fdSTomas Winkler .pm = &mei_gsc_pm_ops, 281a98c30fdSTomas Winkler }, 282a98c30fdSTomas Winkler .id_table = mei_gsc_id_table 283a98c30fdSTomas Winkler }; 284a98c30fdSTomas Winkler module_auxiliary_driver(mei_gsc_driver); 285a98c30fdSTomas Winkler 286a98c30fdSTomas Winkler MODULE_AUTHOR("Intel Corporation"); 287a98c30fdSTomas Winkler MODULE_ALIAS("auxiliary:i915.mei-gsc"); 288a98c30fdSTomas Winkler MODULE_ALIAS("auxiliary:i915.mei-gscfi"); 289a98c30fdSTomas Winkler MODULE_LICENSE("GPL"); 290