brintos

brintos / linux-shallow public Read only

0
0
Text · 6.8 KiB · 5a8c26c Raw
305 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright(c) 2019-2022, Intel Corporation. All rights reserved.4 *5 * Intel Management Engine Interface (Intel MEI) Linux driver6 */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#include <linux/kthread.h>17 18#include "mei_dev.h"19#include "hw-me.h"20#include "hw-me-regs.h"21 22#include "mei-trace.h"23 24#define MEI_GSC_RPM_TIMEOUT 50025 26static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val)27{28	struct mei_me_hw *hw = to_me_hw(dev);29 30	*val = ioread32(hw->mem_addr + where + 0xC00);31 32	return 0;33}34 35static void mei_gsc_set_ext_op_mem(const struct mei_me_hw *hw, struct resource *mem)36{37	u32 low = lower_32_bits(mem->start);38	u32 hi  = upper_32_bits(mem->start);39	u32 limit = (resource_size(mem) / SZ_4K) | GSC_EXT_OP_MEM_VALID;40 41	iowrite32(low, hw->mem_addr + H_GSC_EXT_OP_MEM_BASE_ADDR_LO_REG);42	iowrite32(hi, hw->mem_addr + H_GSC_EXT_OP_MEM_BASE_ADDR_HI_REG);43	iowrite32(limit, hw->mem_addr + H_GSC_EXT_OP_MEM_LIMIT_REG);44}45 46static int mei_gsc_probe(struct auxiliary_device *aux_dev,47			 const struct auxiliary_device_id *aux_dev_id)48{49	struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev);50	struct mei_device *dev;51	struct mei_me_hw *hw;52	struct device *device;53	const struct mei_cfg *cfg;54	int ret;55 56	cfg = mei_me_get_cfg(aux_dev_id->driver_data);57	if (!cfg)58		return -ENODEV;59 60	device = &aux_dev->dev;61 62	dev = mei_me_dev_init(device, cfg, adev->slow_firmware);63	if (!dev) {64		ret = -ENOMEM;65		goto err;66	}67 68	hw = to_me_hw(dev);69	hw->mem_addr = devm_ioremap_resource(device, &adev->bar);70	if (IS_ERR(hw->mem_addr)) {71		ret = PTR_ERR(hw->mem_addr);72		goto err;73	}74 75	hw->irq = adev->irq;76	hw->read_fws = mei_gsc_read_hfs;77 78	dev_set_drvdata(device, dev);79 80	if (adev->ext_op_mem.start) {81		mei_gsc_set_ext_op_mem(hw, &adev->ext_op_mem);82		dev->pxp_mode = MEI_DEV_PXP_INIT;83	}84 85	/* use polling */86	if (mei_me_hw_use_polling(hw)) {87		mei_disable_interrupts(dev);88		mei_clear_interrupts(dev);89		init_waitqueue_head(&hw->wait_active);90		hw->is_active = true; /* start in active mode for initialization */91		hw->polling_thread = kthread_run(mei_me_polling_thread, dev,92						 "kmegscirqd/%s", dev_name(device));93		if (IS_ERR(hw->polling_thread)) {94			ret = PTR_ERR(hw->polling_thread);95			dev_err(device, "unable to create kernel thread: %d\n", ret);96			goto err;97		}98	} else {99		ret = devm_request_threaded_irq(device, hw->irq,100						mei_me_irq_quick_handler,101						mei_me_irq_thread_handler,102						IRQF_ONESHOT, KBUILD_MODNAME, dev);103		if (ret) {104			dev_err(device, "irq register failed %d\n", ret);105			goto err;106		}107	}108 109	pm_runtime_get_noresume(device);110	pm_runtime_set_active(device);111	pm_runtime_enable(device);112 113	/* Continue to char device setup in spite of firmware handshake failure.114	 * In order to provide access to the firmware status registers to the user115	 * space via sysfs.116	 */117	if (mei_start(dev))118		dev_warn(device, "init hw failure.\n");119 120	pm_runtime_set_autosuspend_delay(device, MEI_GSC_RPM_TIMEOUT);121	pm_runtime_use_autosuspend(device);122 123	ret = mei_register(dev, device);124	if (ret)125		goto register_err;126 127	pm_runtime_put_noidle(device);128	return 0;129 130register_err:131	mei_stop(dev);132	if (!mei_me_hw_use_polling(hw))133		devm_free_irq(device, hw->irq, dev);134 135err:136	dev_err(device, "probe failed: %d\n", ret);137	dev_set_drvdata(device, NULL);138	return ret;139}140 141static void mei_gsc_remove(struct auxiliary_device *aux_dev)142{143	struct mei_device *dev;144	struct mei_me_hw *hw;145 146	dev = dev_get_drvdata(&aux_dev->dev);147	hw = to_me_hw(dev);148 149	mei_stop(dev);150 151	hw = to_me_hw(dev);152	if (mei_me_hw_use_polling(hw))153		kthread_stop(hw->polling_thread);154 155	mei_deregister(dev);156 157	pm_runtime_disable(&aux_dev->dev);158 159	mei_disable_interrupts(dev);160	if (!mei_me_hw_use_polling(hw))161		devm_free_irq(&aux_dev->dev, hw->irq, dev);162}163 164static int __maybe_unused mei_gsc_pm_suspend(struct device *device)165{166	struct mei_device *dev = dev_get_drvdata(device);167 168	mei_stop(dev);169 170	mei_disable_interrupts(dev);171 172	return 0;173}174 175static int __maybe_unused mei_gsc_pm_resume(struct device *device)176{177	struct mei_device *dev = dev_get_drvdata(device);178	struct auxiliary_device *aux_dev;179	struct mei_aux_device *adev;180	int err;181	struct mei_me_hw *hw;182 183	hw = to_me_hw(dev);184	aux_dev = to_auxiliary_dev(device);185	adev = auxiliary_dev_to_mei_aux_dev(aux_dev);186	if (adev->ext_op_mem.start) {187		mei_gsc_set_ext_op_mem(hw, &adev->ext_op_mem);188		dev->pxp_mode = MEI_DEV_PXP_INIT;189	}190 191	err = mei_restart(dev);192	if (err)193		return err;194 195	/* Start timer if stopped in suspend */196	schedule_delayed_work(&dev->timer_work, HZ);197 198	return 0;199}200 201static int __maybe_unused mei_gsc_pm_runtime_idle(struct device *device)202{203	struct mei_device *dev = dev_get_drvdata(device);204 205	if (mei_write_is_idle(dev))206		pm_runtime_autosuspend(device);207 208	return -EBUSY;209}210 211static int  __maybe_unused mei_gsc_pm_runtime_suspend(struct device *device)212{213	struct mei_device *dev = dev_get_drvdata(device);214	struct mei_me_hw *hw;215	int ret;216 217	mutex_lock(&dev->device_lock);218 219	if (mei_write_is_idle(dev)) {220		hw = to_me_hw(dev);221		hw->pg_state = MEI_PG_ON;222 223		if (mei_me_hw_use_polling(hw))224			hw->is_active = false;225		ret = 0;226	} else {227		ret = -EAGAIN;228	}229 230	mutex_unlock(&dev->device_lock);231 232	return ret;233}234 235static int __maybe_unused mei_gsc_pm_runtime_resume(struct device *device)236{237	struct mei_device *dev = dev_get_drvdata(device);238	struct mei_me_hw *hw;239	irqreturn_t irq_ret;240 241	mutex_lock(&dev->device_lock);242 243	hw = to_me_hw(dev);244	hw->pg_state = MEI_PG_OFF;245 246	if (mei_me_hw_use_polling(hw)) {247		hw->is_active = true;248		wake_up(&hw->wait_active);249	}250 251	mutex_unlock(&dev->device_lock);252 253	irq_ret = mei_me_irq_thread_handler(1, dev);254	if (irq_ret != IRQ_HANDLED)255		dev_err(dev->dev, "thread handler fail %d\n", irq_ret);256 257	return 0;258}259 260static const struct dev_pm_ops mei_gsc_pm_ops = {261	SET_SYSTEM_SLEEP_PM_OPS(mei_gsc_pm_suspend,262				mei_gsc_pm_resume)263	SET_RUNTIME_PM_OPS(mei_gsc_pm_runtime_suspend,264			   mei_gsc_pm_runtime_resume,265			   mei_gsc_pm_runtime_idle)266};267 268static const struct auxiliary_device_id mei_gsc_id_table[] = {269	{270		.name = "i915.mei-gsc",271		.driver_data = MEI_ME_GSC_CFG,272 273	},274	{275		.name = "i915.mei-gscfi",276		.driver_data = MEI_ME_GSCFI_CFG,277	},278	{279		.name = "xe.mei-gscfi",280		.driver_data = MEI_ME_GSCFI_CFG,281	},282	{283		/* sentinel */284	}285};286MODULE_DEVICE_TABLE(auxiliary, mei_gsc_id_table);287 288static struct auxiliary_driver mei_gsc_driver = {289	.probe	= mei_gsc_probe,290	.remove = mei_gsc_remove,291	.driver = {292		/* auxiliary_driver_register() sets .name to be the modname */293		.pm = &mei_gsc_pm_ops,294	},295	.id_table = mei_gsc_id_table296};297module_auxiliary_driver(mei_gsc_driver);298 299MODULE_AUTHOR("Intel Corporation");300MODULE_ALIAS("auxiliary:i915.mei-gsc");301MODULE_ALIAS("auxiliary:i915.mei-gscfi");302MODULE_ALIAS("auxiliary:xe.mei-gscfi");303MODULE_DESCRIPTION("Intel(R) Graphics System Controller");304MODULE_LICENSE("GPL");305