1*a98c30fdSTomas Winkler // SPDX-License-Identifier: GPL-2.0 2*a98c30fdSTomas Winkler /* 3*a98c30fdSTomas Winkler * Copyright(c) 2019-2022, Intel Corporation. All rights reserved. 4*a98c30fdSTomas Winkler * 5*a98c30fdSTomas Winkler * Intel Management Engine Interface (Intel MEI) Linux driver 6*a98c30fdSTomas Winkler */ 7*a98c30fdSTomas Winkler 8*a98c30fdSTomas Winkler #include <linux/module.h> 9*a98c30fdSTomas Winkler #include <linux/mei_aux.h> 10*a98c30fdSTomas Winkler #include <linux/device.h> 11*a98c30fdSTomas Winkler #include <linux/irqreturn.h> 12*a98c30fdSTomas Winkler #include <linux/jiffies.h> 13*a98c30fdSTomas Winkler #include <linux/ktime.h> 14*a98c30fdSTomas Winkler #include <linux/delay.h> 15*a98c30fdSTomas Winkler #include <linux/pm_runtime.h> 16*a98c30fdSTomas Winkler 17*a98c30fdSTomas Winkler #include "mei_dev.h" 18*a98c30fdSTomas Winkler #include "hw-me.h" 19*a98c30fdSTomas Winkler #include "hw-me-regs.h" 20*a98c30fdSTomas Winkler 21*a98c30fdSTomas Winkler #include "mei-trace.h" 22*a98c30fdSTomas Winkler 23*a98c30fdSTomas Winkler #define MEI_GSC_RPM_TIMEOUT 500 24*a98c30fdSTomas Winkler 25*a98c30fdSTomas Winkler static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val) 26*a98c30fdSTomas Winkler { 27*a98c30fdSTomas Winkler struct mei_me_hw *hw = to_me_hw(dev); 28*a98c30fdSTomas Winkler 29*a98c30fdSTomas Winkler *val = ioread32(hw->mem_addr + where + 0xC00); 30*a98c30fdSTomas Winkler 31*a98c30fdSTomas Winkler return 0; 32*a98c30fdSTomas Winkler } 33*a98c30fdSTomas Winkler 34*a98c30fdSTomas Winkler static int mei_gsc_probe(struct auxiliary_device *aux_dev, 35*a98c30fdSTomas Winkler const struct auxiliary_device_id *aux_dev_id) 36*a98c30fdSTomas Winkler { 37*a98c30fdSTomas Winkler struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev); 38*a98c30fdSTomas Winkler struct mei_device *dev; 39*a98c30fdSTomas Winkler struct mei_me_hw *hw; 40*a98c30fdSTomas Winkler struct device *device; 41*a98c30fdSTomas Winkler const struct mei_cfg *cfg; 42*a98c30fdSTomas Winkler int ret; 43*a98c30fdSTomas Winkler 44*a98c30fdSTomas Winkler cfg = mei_me_get_cfg(aux_dev_id->driver_data); 45*a98c30fdSTomas Winkler if (!cfg) 46*a98c30fdSTomas Winkler return -ENODEV; 47*a98c30fdSTomas Winkler 48*a98c30fdSTomas Winkler device = &aux_dev->dev; 49*a98c30fdSTomas Winkler 50*a98c30fdSTomas Winkler dev = mei_me_dev_init(device, cfg); 51*a98c30fdSTomas Winkler if (!dev) { 52*a98c30fdSTomas Winkler ret = -ENOMEM; 53*a98c30fdSTomas Winkler goto err; 54*a98c30fdSTomas Winkler } 55*a98c30fdSTomas Winkler 56*a98c30fdSTomas Winkler hw = to_me_hw(dev); 57*a98c30fdSTomas Winkler hw->mem_addr = devm_ioremap_resource(device, &adev->bar); 58*a98c30fdSTomas Winkler if (IS_ERR(hw->mem_addr)) { 59*a98c30fdSTomas Winkler dev_err(device, "mmio not mapped\n"); 60*a98c30fdSTomas Winkler ret = PTR_ERR(hw->mem_addr); 61*a98c30fdSTomas Winkler goto err; 62*a98c30fdSTomas Winkler } 63*a98c30fdSTomas Winkler 64*a98c30fdSTomas Winkler hw->irq = adev->irq; 65*a98c30fdSTomas Winkler hw->read_fws = mei_gsc_read_hfs; 66*a98c30fdSTomas Winkler 67*a98c30fdSTomas Winkler dev_set_drvdata(device, dev); 68*a98c30fdSTomas Winkler 69*a98c30fdSTomas Winkler ret = devm_request_threaded_irq(device, hw->irq, 70*a98c30fdSTomas Winkler mei_me_irq_quick_handler, 71*a98c30fdSTomas Winkler mei_me_irq_thread_handler, 72*a98c30fdSTomas Winkler IRQF_ONESHOT, KBUILD_MODNAME, dev); 73*a98c30fdSTomas Winkler if (ret) { 74*a98c30fdSTomas Winkler dev_err(device, "irq register failed %d\n", ret); 75*a98c30fdSTomas Winkler goto err; 76*a98c30fdSTomas Winkler } 77*a98c30fdSTomas Winkler 78*a98c30fdSTomas Winkler pm_runtime_get_noresume(device); 79*a98c30fdSTomas Winkler pm_runtime_set_active(device); 80*a98c30fdSTomas Winkler pm_runtime_enable(device); 81*a98c30fdSTomas Winkler 82*a98c30fdSTomas Winkler if (mei_start(dev)) { 83*a98c30fdSTomas Winkler dev_err(device, "init hw failure.\n"); 84*a98c30fdSTomas Winkler ret = -ENODEV; 85*a98c30fdSTomas Winkler goto irq_err; 86*a98c30fdSTomas Winkler } 87*a98c30fdSTomas Winkler 88*a98c30fdSTomas Winkler pm_runtime_set_autosuspend_delay(device, MEI_GSC_RPM_TIMEOUT); 89*a98c30fdSTomas Winkler pm_runtime_use_autosuspend(device); 90*a98c30fdSTomas Winkler 91*a98c30fdSTomas Winkler ret = mei_register(dev, device); 92*a98c30fdSTomas Winkler if (ret) 93*a98c30fdSTomas Winkler goto register_err; 94*a98c30fdSTomas Winkler 95*a98c30fdSTomas Winkler pm_runtime_put_noidle(device); 96*a98c30fdSTomas Winkler return 0; 97*a98c30fdSTomas Winkler 98*a98c30fdSTomas Winkler register_err: 99*a98c30fdSTomas Winkler mei_stop(dev); 100*a98c30fdSTomas Winkler irq_err: 101*a98c30fdSTomas Winkler devm_free_irq(device, hw->irq, dev); 102*a98c30fdSTomas Winkler 103*a98c30fdSTomas Winkler err: 104*a98c30fdSTomas Winkler dev_err(device, "probe failed: %d\n", ret); 105*a98c30fdSTomas Winkler dev_set_drvdata(device, NULL); 106*a98c30fdSTomas Winkler return ret; 107*a98c30fdSTomas Winkler } 108*a98c30fdSTomas Winkler 109*a98c30fdSTomas Winkler static void mei_gsc_remove(struct auxiliary_device *aux_dev) 110*a98c30fdSTomas Winkler { 111*a98c30fdSTomas Winkler struct mei_device *dev; 112*a98c30fdSTomas Winkler struct mei_me_hw *hw; 113*a98c30fdSTomas Winkler 114*a98c30fdSTomas Winkler dev = dev_get_drvdata(&aux_dev->dev); 115*a98c30fdSTomas Winkler if (!dev) 116*a98c30fdSTomas Winkler return; 117*a98c30fdSTomas Winkler 118*a98c30fdSTomas Winkler hw = to_me_hw(dev); 119*a98c30fdSTomas Winkler 120*a98c30fdSTomas Winkler mei_stop(dev); 121*a98c30fdSTomas Winkler 122*a98c30fdSTomas Winkler mei_deregister(dev); 123*a98c30fdSTomas Winkler 124*a98c30fdSTomas Winkler pm_runtime_disable(&aux_dev->dev); 125*a98c30fdSTomas Winkler 126*a98c30fdSTomas Winkler mei_disable_interrupts(dev); 127*a98c30fdSTomas Winkler devm_free_irq(&aux_dev->dev, hw->irq, dev); 128*a98c30fdSTomas Winkler } 129*a98c30fdSTomas Winkler 130*a98c30fdSTomas Winkler static int __maybe_unused mei_gsc_pm_suspend(struct device *device) 131*a98c30fdSTomas Winkler { 132*a98c30fdSTomas Winkler struct mei_device *dev = dev_get_drvdata(device); 133*a98c30fdSTomas Winkler 134*a98c30fdSTomas Winkler if (!dev) 135*a98c30fdSTomas Winkler return -ENODEV; 136*a98c30fdSTomas Winkler 137*a98c30fdSTomas Winkler mei_stop(dev); 138*a98c30fdSTomas Winkler 139*a98c30fdSTomas Winkler mei_disable_interrupts(dev); 140*a98c30fdSTomas Winkler 141*a98c30fdSTomas Winkler return 0; 142*a98c30fdSTomas Winkler } 143*a98c30fdSTomas Winkler 144*a98c30fdSTomas Winkler static int __maybe_unused mei_gsc_pm_resume(struct device *device) 145*a98c30fdSTomas Winkler { 146*a98c30fdSTomas Winkler struct mei_device *dev = dev_get_drvdata(device); 147*a98c30fdSTomas Winkler int err; 148*a98c30fdSTomas Winkler 149*a98c30fdSTomas Winkler if (!dev) 150*a98c30fdSTomas Winkler return -ENODEV; 151*a98c30fdSTomas Winkler 152*a98c30fdSTomas Winkler err = mei_restart(dev); 153*a98c30fdSTomas Winkler if (err) 154*a98c30fdSTomas Winkler return err; 155*a98c30fdSTomas Winkler 156*a98c30fdSTomas Winkler /* Start timer if stopped in suspend */ 157*a98c30fdSTomas Winkler schedule_delayed_work(&dev->timer_work, HZ); 158*a98c30fdSTomas Winkler 159*a98c30fdSTomas Winkler return 0; 160*a98c30fdSTomas Winkler } 161*a98c30fdSTomas Winkler 162*a98c30fdSTomas Winkler static SIMPLE_DEV_PM_OPS(mei_gsc_pm_ops, mei_gsc_pm_suspend, mei_gsc_pm_resume); 163*a98c30fdSTomas Winkler 164*a98c30fdSTomas Winkler static const struct auxiliary_device_id mei_gsc_id_table[] = { 165*a98c30fdSTomas Winkler { 166*a98c30fdSTomas Winkler .name = "i915.mei-gsc", 167*a98c30fdSTomas Winkler .driver_data = MEI_ME_GSC_CFG, 168*a98c30fdSTomas Winkler 169*a98c30fdSTomas Winkler }, 170*a98c30fdSTomas Winkler { 171*a98c30fdSTomas Winkler .name = "i915.mei-gscfi", 172*a98c30fdSTomas Winkler .driver_data = MEI_ME_GSCFI_CFG, 173*a98c30fdSTomas Winkler }, 174*a98c30fdSTomas Winkler { 175*a98c30fdSTomas Winkler /* sentinel */ 176*a98c30fdSTomas Winkler } 177*a98c30fdSTomas Winkler }; 178*a98c30fdSTomas Winkler MODULE_DEVICE_TABLE(auxiliary, mei_gsc_id_table); 179*a98c30fdSTomas Winkler 180*a98c30fdSTomas Winkler static struct auxiliary_driver mei_gsc_driver = { 181*a98c30fdSTomas Winkler .probe = mei_gsc_probe, 182*a98c30fdSTomas Winkler .remove = mei_gsc_remove, 183*a98c30fdSTomas Winkler .driver = { 184*a98c30fdSTomas Winkler /* auxiliary_driver_register() sets .name to be the modname */ 185*a98c30fdSTomas Winkler .pm = &mei_gsc_pm_ops, 186*a98c30fdSTomas Winkler }, 187*a98c30fdSTomas Winkler .id_table = mei_gsc_id_table 188*a98c30fdSTomas Winkler }; 189*a98c30fdSTomas Winkler module_auxiliary_driver(mei_gsc_driver); 190*a98c30fdSTomas Winkler 191*a98c30fdSTomas Winkler MODULE_AUTHOR("Intel Corporation"); 192*a98c30fdSTomas Winkler MODULE_ALIAS("auxiliary:i915.mei-gsc"); 193*a98c30fdSTomas Winkler MODULE_ALIAS("auxiliary:i915.mei-gscfi"); 194*a98c30fdSTomas Winkler MODULE_LICENSE("GPL"); 195