pci-txe.c 9.5 KB
Newer Older
T
Tomas Winkler 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 *
 * Intel Management Engine Interface (Intel MEI) Linux driver
 * Copyright (c) 2013-2014, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/uuid.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
30
#include <linux/pm_domain.h>
31
#include <linux/pm_runtime.h>
T
Tomas Winkler 已提交
32 33 34 35 36 37 38

#include <linux/mei.h>


#include "mei_dev.h"
#include "hw-txe.h"

39
static const struct pci_device_id mei_txe_pci_tbl[] = {
40
	{PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */
41
	{PCI_VDEVICE(INTEL, 0x2298)}, /* Cherrytrail */
42

T
Tomas Winkler 已提交
43 44 45 46
	{0, }
};
MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);

47
#ifdef CONFIG_PM
48 49 50 51 52
static inline void mei_txe_set_pm_domain(struct mei_device *dev);
static inline void mei_txe_unset_pm_domain(struct mei_device *dev);
#else
static inline void mei_txe_set_pm_domain(struct mei_device *dev) {}
static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {}
53
#endif /* CONFIG_PM */
T
Tomas Winkler 已提交
54 55

/**
56
 * mei_txe_probe - Device Initialization Routine
T
Tomas Winkler 已提交
57 58 59 60
 *
 * @pdev: PCI device structure
 * @ent: entry in mei_txe_pci_tbl
 *
61
 * Return: 0 on success, <0 on failure.
T
Tomas Winkler 已提交
62 63 64 65 66
 */
static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	struct mei_device *dev;
	struct mei_txe_hw *hw;
67
	const int mask = BIT(SEC_BAR) | BIT(BRIDGE_BAR);
T
Tomas Winkler 已提交
68 69 70
	int err;

	/* enable pci dev */
71
	err = pcim_enable_device(pdev);
T
Tomas Winkler 已提交
72 73 74 75 76 77
	if (err) {
		dev_err(&pdev->dev, "failed to enable pci device.\n");
		goto end;
	}
	/* set PCI host mastering  */
	pci_set_master(pdev);
78 79
	/* pci request regions and mapping IO device memory for mei driver */
	err = pcim_iomap_regions(pdev, mask, KBUILD_MODNAME);
T
Tomas Winkler 已提交
80 81
	if (err) {
		dev_err(&pdev->dev, "failed to get pci regions.\n");
82
		goto end;
T
Tomas Winkler 已提交
83 84 85 86 87 88 89
	}

	err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
	if (err) {
		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
		if (err) {
			dev_err(&pdev->dev, "No suitable DMA available.\n");
90
			goto end;
T
Tomas Winkler 已提交
91 92 93 94
		}
	}

	/* allocates and initializes the mei dev structure */
95
	dev = mei_txe_dev_init(pdev);
T
Tomas Winkler 已提交
96 97
	if (!dev) {
		err = -ENOMEM;
98
		goto end;
T
Tomas Winkler 已提交
99 100
	}
	hw = to_txe_hw(dev);
101
	hw->mem_addr = pcim_iomap_table(pdev);
T
Tomas Winkler 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

	pci_enable_msi(pdev);

	/* clear spurious interrupts */
	mei_clear_interrupts(dev);

	/* request and enable interrupt  */
	if (pci_dev_msi_enabled(pdev))
		err = request_threaded_irq(pdev->irq,
			NULL,
			mei_txe_irq_thread_handler,
			IRQF_ONESHOT, KBUILD_MODNAME, dev);
	else
		err = request_threaded_irq(pdev->irq,
			mei_txe_irq_quick_handler,
			mei_txe_irq_thread_handler,
			IRQF_SHARED, KBUILD_MODNAME, dev);
	if (err) {
		dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n",
			pdev->irq);
122
		goto end;
T
Tomas Winkler 已提交
123 124 125 126 127 128 129 130
	}

	if (mei_start(dev)) {
		dev_err(&pdev->dev, "init hw failure.\n");
		err = -ENODEV;
		goto release_irq;
	}

131 132 133
	pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
	pm_runtime_use_autosuspend(&pdev->dev);

134
	err = mei_register(dev, &pdev->dev);
T
Tomas Winkler 已提交
135
	if (err)
136
		goto stop;
T
Tomas Winkler 已提交
137 138 139

	pci_set_drvdata(pdev, dev);

140 141 142 143
	/*
	 * MEI requires to resume from runtime suspend mode
	 * in order to perform link reset flow upon system suspend.
	 */
144
	dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
145

146 147 148 149 150 151 152 153
	/*
	* For not wake-able HW runtime pm framework
	* can't be used on pci device level.
	* Use domain runtime pm callbacks instead.
	*/
	if (!pci_dev_run_wake(pdev))
		mei_txe_set_pm_domain(dev);

154 155
	pm_runtime_put_noidle(&pdev->dev);

T
Tomas Winkler 已提交
156 157
	return 0;

158 159
stop:
	mei_stop(dev);
T
Tomas Winkler 已提交
160 161 162 163 164 165 166 167 168
release_irq:
	mei_cancel_work(dev);
	mei_disable_interrupts(dev);
	free_irq(pdev->irq, dev);
end:
	dev_err(&pdev->dev, "initialization failed.\n");
	return err;
}

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
/**
 * mei_txe_remove - Device Shutdown Routine
 *
 * @pdev: PCI device structure
 *
 *  mei_txe_shutdown is called from the reboot notifier
 *  it's a simplified version of remove so we go down
 *  faster.
 */
static void mei_txe_shutdown(struct pci_dev *pdev)
{
	struct mei_device *dev;

	dev = pci_get_drvdata(pdev);
	if (!dev)
		return;

	dev_dbg(&pdev->dev, "shutdown\n");
	mei_stop(dev);

	if (!pci_dev_run_wake(pdev))
		mei_txe_unset_pm_domain(dev);

	mei_disable_interrupts(dev);
	free_irq(pdev->irq, dev);
}

T
Tomas Winkler 已提交
196
/**
197
 * mei_txe_remove - Device Removal Routine
T
Tomas Winkler 已提交
198 199 200 201 202 203 204 205 206 207 208 209
 *
 * @pdev: PCI device structure
 *
 * mei_remove is called by the PCI subsystem to alert the driver
 * that it should release a PCI device.
 */
static void mei_txe_remove(struct pci_dev *pdev)
{
	struct mei_device *dev;

	dev = pci_get_drvdata(pdev);
	if (!dev) {
210
		dev_err(&pdev->dev, "mei: dev == NULL\n");
T
Tomas Winkler 已提交
211 212 213
		return;
	}

214 215
	pm_runtime_get_noresume(&pdev->dev);

T
Tomas Winkler 已提交
216 217
	mei_stop(dev);

218 219 220
	if (!pci_dev_run_wake(pdev))
		mei_txe_unset_pm_domain(dev);

T
Tomas Winkler 已提交
221 222 223 224 225 226 227
	mei_disable_interrupts(dev);
	free_irq(pdev->irq, dev);

	mei_deregister(dev);
}


228
#ifdef CONFIG_PM_SLEEP
T
Tomas Winkler 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
static int mei_txe_pci_suspend(struct device *device)
{
	struct pci_dev *pdev = to_pci_dev(device);
	struct mei_device *dev = pci_get_drvdata(pdev);

	if (!dev)
		return -ENODEV;

	dev_dbg(&pdev->dev, "suspend\n");

	mei_stop(dev);

	mei_disable_interrupts(dev);

	free_irq(pdev->irq, dev);
	pci_disable_msi(pdev);

	return 0;
}

static int mei_txe_pci_resume(struct device *device)
{
	struct pci_dev *pdev = to_pci_dev(device);
	struct mei_device *dev;
	int err;

	dev = pci_get_drvdata(pdev);
	if (!dev)
		return -ENODEV;

	pci_enable_msi(pdev);

	mei_clear_interrupts(dev);

	/* request and enable interrupt */
	if (pci_dev_msi_enabled(pdev))
		err = request_threaded_irq(pdev->irq,
			NULL,
			mei_txe_irq_thread_handler,
			IRQF_ONESHOT, KBUILD_MODNAME, dev);
	else
		err = request_threaded_irq(pdev->irq,
			mei_txe_irq_quick_handler,
			mei_txe_irq_thread_handler,
			IRQF_SHARED, KBUILD_MODNAME, dev);
	if (err) {
		dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n",
				pdev->irq);
		return err;
	}

	err = mei_restart(dev);

	return err;
}
284 285
#endif /* CONFIG_PM_SLEEP */

286
#ifdef CONFIG_PM
287 288 289 290 291 292 293 294 295 296 297
static int mei_txe_pm_runtime_idle(struct device *device)
{
	struct pci_dev *pdev = to_pci_dev(device);
	struct mei_device *dev;

	dev_dbg(&pdev->dev, "rpm: txe: runtime_idle\n");

	dev = pci_get_drvdata(pdev);
	if (!dev)
		return -ENODEV;
	if (mei_write_is_idle(dev))
298
		pm_runtime_autosuspend(device);
299 300 301 302 303 304 305 306 307 308 309 310 311 312

	return -EBUSY;
}
static int mei_txe_pm_runtime_suspend(struct device *device)
{
	struct pci_dev *pdev = to_pci_dev(device);
	struct mei_device *dev;
	int ret;

	dev_dbg(&pdev->dev, "rpm: txe: runtime suspend\n");

	dev = pci_get_drvdata(pdev);
	if (!dev)
		return -ENODEV;
T
Tomas Winkler 已提交
313

314 315 316 317 318 319 320 321 322 323 324 325 326 327
	mutex_lock(&dev->device_lock);

	if (mei_write_is_idle(dev))
		ret = mei_txe_aliveness_set_sync(dev, 0);
	else
		ret = -EAGAIN;

	/*
	 * If everything is okay we're about to enter PCI low
	 * power state (D3) therefor we need to disable the
	 * interrupts towards host.
	 * However if device is not wakeable we do not enter
	 * D-low state and we need to keep the interrupt kicking
	 */
328
	if (!ret && pci_dev_run_wake(pdev))
329 330 331 332 333
		mei_disable_interrupts(dev);

	dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);

	mutex_unlock(&dev->device_lock);
334 335 336 337

	if (ret && ret != -EAGAIN)
		schedule_work(&dev->reset_work);

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
	return ret;
}

static int mei_txe_pm_runtime_resume(struct device *device)
{
	struct pci_dev *pdev = to_pci_dev(device);
	struct mei_device *dev;
	int ret;

	dev_dbg(&pdev->dev, "rpm: txe: runtime resume\n");

	dev = pci_get_drvdata(pdev);
	if (!dev)
		return -ENODEV;

	mutex_lock(&dev->device_lock);

	mei_enable_interrupts(dev);

	ret = mei_txe_aliveness_set_sync(dev, 1);

	mutex_unlock(&dev->device_lock);

	dev_dbg(&pdev->dev, "rpm: txe: runtime resume ret = %d\n", ret);

363 364 365
	if (ret)
		schedule_work(&dev->reset_work);

366 367
	return ret;
}
368 369

/**
G
Geert Uytterhoeven 已提交
370
 * mei_txe_set_pm_domain - fill and set pm domain structure for device
371 372 373 374 375
 *
 * @dev: mei_device
 */
static inline void mei_txe_set_pm_domain(struct mei_device *dev)
{
376
	struct pci_dev *pdev  = to_pci_dev(dev->dev);
377 378 379 380 381 382 383 384

	if (pdev->dev.bus && pdev->dev.bus->pm) {
		dev->pg_domain.ops = *pdev->dev.bus->pm;

		dev->pg_domain.ops.runtime_suspend = mei_txe_pm_runtime_suspend;
		dev->pg_domain.ops.runtime_resume = mei_txe_pm_runtime_resume;
		dev->pg_domain.ops.runtime_idle = mei_txe_pm_runtime_idle;

385
		dev_pm_domain_set(&pdev->dev, &dev->pg_domain);
386 387 388 389
	}
}

/**
G
Geert Uytterhoeven 已提交
390
 * mei_txe_unset_pm_domain - clean pm domain structure for device
391 392 393 394 395 396
 *
 * @dev: mei_device
 */
static inline void mei_txe_unset_pm_domain(struct mei_device *dev)
{
	/* stop using pm callbacks if any */
397
	dev_pm_domain_set(dev->dev, NULL);
398
}
399 400 401 402 403 404 405 406 407

static const struct dev_pm_ops mei_txe_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(mei_txe_pci_suspend,
				mei_txe_pci_resume)
	SET_RUNTIME_PM_OPS(
		mei_txe_pm_runtime_suspend,
		mei_txe_pm_runtime_resume,
		mei_txe_pm_runtime_idle)
};
T
Tomas Winkler 已提交
408 409 410 411

#define MEI_TXE_PM_OPS	(&mei_txe_pm_ops)
#else
#define MEI_TXE_PM_OPS	NULL
412 413
#endif /* CONFIG_PM */

T
Tomas Winkler 已提交
414 415 416 417 418 419 420 421
/*
 *  PCI driver structure
 */
static struct pci_driver mei_txe_driver = {
	.name = KBUILD_MODNAME,
	.id_table = mei_txe_pci_tbl,
	.probe = mei_txe_probe,
	.remove = mei_txe_remove,
422
	.shutdown = mei_txe_shutdown,
T
Tomas Winkler 已提交
423 424 425 426 427 428 429 430
	.driver.pm = MEI_TXE_PM_OPS,
};

module_pci_driver(mei_txe_driver);

MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Intel(R) Trusted Execution Environment Interface");
MODULE_LICENSE("GPL v2");