linux/drivers/misc/mei/pci-txe.c
<<
>>
Prefs
   1/*
   2 *
   3 * Intel Management Engine Interface (Intel MEI) Linux driver
   4 * Copyright (c) 2013-2014, Intel Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2, as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 */
  16
  17#include <linux/module.h>
  18#include <linux/kernel.h>
  19#include <linux/device.h>
  20#include <linux/fs.h>
  21#include <linux/errno.h>
  22#include <linux/types.h>
  23#include <linux/pci.h>
  24#include <linux/init.h>
  25#include <linux/sched.h>
  26#include <linux/uuid.h>
  27#include <linux/jiffies.h>
  28#include <linux/interrupt.h>
  29#include <linux/workqueue.h>
  30#include <linux/pm_domain.h>
  31#include <linux/pm_runtime.h>
  32
  33#include <linux/mei.h>
  34
  35
  36#include "mei_dev.h"
  37#include "hw-txe.h"
  38
  39static const struct pci_device_id mei_txe_pci_tbl[] = {
  40        {PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */
  41        {PCI_VDEVICE(INTEL, 0x2298)}, /* Cherrytrail */
  42
  43        {0, }
  44};
  45MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
  46
  47#ifdef CONFIG_PM
  48static inline void mei_txe_set_pm_domain(struct mei_device *dev);
  49static inline void mei_txe_unset_pm_domain(struct mei_device *dev);
  50#else
  51static inline void mei_txe_set_pm_domain(struct mei_device *dev) {}
  52static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {}
  53#endif /* CONFIG_PM */
  54
  55static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
  56{
  57        int i;
  58
  59        for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) {
  60                if (hw->mem_addr[i]) {
  61                        pci_iounmap(pdev, hw->mem_addr[i]);
  62                        hw->mem_addr[i] = NULL;
  63                }
  64        }
  65}
  66/**
  67 * mei_txe_probe - Device Initialization Routine
  68 *
  69 * @pdev: PCI device structure
  70 * @ent: entry in mei_txe_pci_tbl
  71 *
  72 * Return: 0 on success, <0 on failure.
  73 */
  74static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  75{
  76        struct mei_device *dev;
  77        struct mei_txe_hw *hw;
  78        int err;
  79        int i;
  80
  81        /* enable pci dev */
  82        err = pci_enable_device(pdev);
  83        if (err) {
  84                dev_err(&pdev->dev, "failed to enable pci device.\n");
  85                goto end;
  86        }
  87        /* set PCI host mastering  */
  88        pci_set_master(pdev);
  89        /* pci request regions for mei driver */
  90        err = pci_request_regions(pdev, KBUILD_MODNAME);
  91        if (err) {
  92                dev_err(&pdev->dev, "failed to get pci regions.\n");
  93                goto disable_device;
  94        }
  95
  96        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
  97        if (err) {
  98                err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
  99                if (err) {
 100                        dev_err(&pdev->dev, "No suitable DMA available.\n");
 101                        goto release_regions;
 102                }
 103        }
 104
 105        /* allocates and initializes the mei dev structure */
 106        dev = mei_txe_dev_init(pdev);
 107        if (!dev) {
 108                err = -ENOMEM;
 109                goto release_regions;
 110        }
 111        hw = to_txe_hw(dev);
 112
 113        /* mapping  IO device memory */
 114        for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) {
 115                hw->mem_addr[i] = pci_iomap(pdev, i, 0);
 116                if (!hw->mem_addr[i]) {
 117                        dev_err(&pdev->dev, "mapping I/O device memory failure.\n");
 118                        err = -ENOMEM;
 119                        goto free_device;
 120                }
 121        }
 122
 123
 124        pci_enable_msi(pdev);
 125
 126        /* clear spurious interrupts */
 127        mei_clear_interrupts(dev);
 128
 129        /* request and enable interrupt  */
 130        if (pci_dev_msi_enabled(pdev))
 131                err = request_threaded_irq(pdev->irq,
 132                        NULL,
 133                        mei_txe_irq_thread_handler,
 134                        IRQF_ONESHOT, KBUILD_MODNAME, dev);
 135        else
 136                err = request_threaded_irq(pdev->irq,
 137                        mei_txe_irq_quick_handler,
 138                        mei_txe_irq_thread_handler,
 139                        IRQF_SHARED, KBUILD_MODNAME, dev);
 140        if (err) {
 141                dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n",
 142                        pdev->irq);
 143                goto free_device;
 144        }
 145
 146        if (mei_start(dev)) {
 147                dev_err(&pdev->dev, "init hw failure.\n");
 148                err = -ENODEV;
 149                goto release_irq;
 150        }
 151
 152        pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
 153        pm_runtime_use_autosuspend(&pdev->dev);
 154
 155        err = mei_register(dev, &pdev->dev);
 156        if (err)
 157                goto stop;
 158
 159        pci_set_drvdata(pdev, dev);
 160
 161        /*
 162        * For not wake-able HW runtime pm framework
 163        * can't be used on pci device level.
 164        * Use domain runtime pm callbacks instead.
 165        */
 166        if (!pci_dev_run_wake(pdev))
 167                mei_txe_set_pm_domain(dev);
 168
 169        pm_runtime_put_noidle(&pdev->dev);
 170
 171        return 0;
 172
 173stop:
 174        mei_stop(dev);
 175release_irq:
 176
 177        mei_cancel_work(dev);
 178
 179        /* disable interrupts */
 180        mei_disable_interrupts(dev);
 181
 182        free_irq(pdev->irq, dev);
 183        pci_disable_msi(pdev);
 184
 185free_device:
 186        mei_txe_pci_iounmap(pdev, hw);
 187
 188        kfree(dev);
 189release_regions:
 190        pci_release_regions(pdev);
 191disable_device:
 192        pci_disable_device(pdev);
 193end:
 194        dev_err(&pdev->dev, "initialization failed.\n");
 195        return err;
 196}
 197
 198/**
 199 * mei_txe_remove - Device Removal Routine
 200 *
 201 * @pdev: PCI device structure
 202 *
 203 * mei_remove is called by the PCI subsystem to alert the driver
 204 * that it should release a PCI device.
 205 */
 206static void mei_txe_remove(struct pci_dev *pdev)
 207{
 208        struct mei_device *dev;
 209        struct mei_txe_hw *hw;
 210
 211        dev = pci_get_drvdata(pdev);
 212        if (!dev) {
 213                dev_err(&pdev->dev, "mei: dev =NULL\n");
 214                return;
 215        }
 216
 217        pm_runtime_get_noresume(&pdev->dev);
 218
 219        hw = to_txe_hw(dev);
 220
 221        mei_stop(dev);
 222
 223        if (!pci_dev_run_wake(pdev))
 224                mei_txe_unset_pm_domain(dev);
 225
 226        /* disable interrupts */
 227        mei_disable_interrupts(dev);
 228        free_irq(pdev->irq, dev);
 229        pci_disable_msi(pdev);
 230
 231        pci_set_drvdata(pdev, NULL);
 232
 233        mei_txe_pci_iounmap(pdev, hw);
 234
 235        mei_deregister(dev);
 236
 237        kfree(dev);
 238
 239        pci_release_regions(pdev);
 240        pci_disable_device(pdev);
 241}
 242
 243
 244#ifdef CONFIG_PM_SLEEP
 245static int mei_txe_pci_suspend(struct device *device)
 246{
 247        struct pci_dev *pdev = to_pci_dev(device);
 248        struct mei_device *dev = pci_get_drvdata(pdev);
 249
 250        if (!dev)
 251                return -ENODEV;
 252
 253        dev_dbg(&pdev->dev, "suspend\n");
 254
 255        mei_stop(dev);
 256
 257        mei_disable_interrupts(dev);
 258
 259        free_irq(pdev->irq, dev);
 260        pci_disable_msi(pdev);
 261
 262        return 0;
 263}
 264
 265static int mei_txe_pci_resume(struct device *device)
 266{
 267        struct pci_dev *pdev = to_pci_dev(device);
 268        struct mei_device *dev;
 269        int err;
 270
 271        dev = pci_get_drvdata(pdev);
 272        if (!dev)
 273                return -ENODEV;
 274
 275        pci_enable_msi(pdev);
 276
 277        mei_clear_interrupts(dev);
 278
 279        /* request and enable interrupt */
 280        if (pci_dev_msi_enabled(pdev))
 281                err = request_threaded_irq(pdev->irq,
 282                        NULL,
 283                        mei_txe_irq_thread_handler,
 284                        IRQF_ONESHOT, KBUILD_MODNAME, dev);
 285        else
 286                err = request_threaded_irq(pdev->irq,
 287                        mei_txe_irq_quick_handler,
 288                        mei_txe_irq_thread_handler,
 289                        IRQF_SHARED, KBUILD_MODNAME, dev);
 290        if (err) {
 291                dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n",
 292                                pdev->irq);
 293                return err;
 294        }
 295
 296        err = mei_restart(dev);
 297
 298        return err;
 299}
 300#endif /* CONFIG_PM_SLEEP */
 301
 302#ifdef CONFIG_PM
 303static int mei_txe_pm_runtime_idle(struct device *device)
 304{
 305        struct pci_dev *pdev = to_pci_dev(device);
 306        struct mei_device *dev;
 307
 308        dev_dbg(&pdev->dev, "rpm: txe: runtime_idle\n");
 309
 310        dev = pci_get_drvdata(pdev);
 311        if (!dev)
 312                return -ENODEV;
 313        if (mei_write_is_idle(dev))
 314                pm_runtime_autosuspend(device);
 315
 316        return -EBUSY;
 317}
 318static int mei_txe_pm_runtime_suspend(struct device *device)
 319{
 320        struct pci_dev *pdev = to_pci_dev(device);
 321        struct mei_device *dev;
 322        int ret;
 323
 324        dev_dbg(&pdev->dev, "rpm: txe: runtime suspend\n");
 325
 326        dev = pci_get_drvdata(pdev);
 327        if (!dev)
 328                return -ENODEV;
 329
 330        mutex_lock(&dev->device_lock);
 331
 332        if (mei_write_is_idle(dev))
 333                ret = mei_txe_aliveness_set_sync(dev, 0);
 334        else
 335                ret = -EAGAIN;
 336
 337        /*
 338         * If everything is okay we're about to enter PCI low
 339         * power state (D3) therefor we need to disable the
 340         * interrupts towards host.
 341         * However if device is not wakeable we do not enter
 342         * D-low state and we need to keep the interrupt kicking
 343         */
 344        if (!ret && pci_dev_run_wake(pdev))
 345                mei_disable_interrupts(dev);
 346
 347        dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);
 348
 349        mutex_unlock(&dev->device_lock);
 350
 351        if (ret && ret != -EAGAIN)
 352                schedule_work(&dev->reset_work);
 353
 354        return ret;
 355}
 356
 357static int mei_txe_pm_runtime_resume(struct device *device)
 358{
 359        struct pci_dev *pdev = to_pci_dev(device);
 360        struct mei_device *dev;
 361        int ret;
 362
 363        dev_dbg(&pdev->dev, "rpm: txe: runtime resume\n");
 364
 365        dev = pci_get_drvdata(pdev);
 366        if (!dev)
 367                return -ENODEV;
 368
 369        mutex_lock(&dev->device_lock);
 370
 371        mei_enable_interrupts(dev);
 372
 373        ret = mei_txe_aliveness_set_sync(dev, 1);
 374
 375        mutex_unlock(&dev->device_lock);
 376
 377        dev_dbg(&pdev->dev, "rpm: txe: runtime resume ret = %d\n", ret);
 378
 379        if (ret)
 380                schedule_work(&dev->reset_work);
 381
 382        return ret;
 383}
 384
 385/**
 386 * mei_txe_set_pm_domain - fill and set pm domain structure for device
 387 *
 388 * @dev: mei_device
 389 */
 390static inline void mei_txe_set_pm_domain(struct mei_device *dev)
 391{
 392        struct pci_dev *pdev  = to_pci_dev(dev->dev);
 393
 394        if (pdev->dev.bus && pdev->dev.bus->pm) {
 395                dev->pg_domain.ops = *pdev->dev.bus->pm;
 396
 397                dev->pg_domain.ops.runtime_suspend = mei_txe_pm_runtime_suspend;
 398                dev->pg_domain.ops.runtime_resume = mei_txe_pm_runtime_resume;
 399                dev->pg_domain.ops.runtime_idle = mei_txe_pm_runtime_idle;
 400
 401                dev_pm_domain_set(&pdev->dev, &dev->pg_domain);
 402        }
 403}
 404
 405/**
 406 * mei_txe_unset_pm_domain - clean pm domain structure for device
 407 *
 408 * @dev: mei_device
 409 */
 410static inline void mei_txe_unset_pm_domain(struct mei_device *dev)
 411{
 412        /* stop using pm callbacks if any */
 413        dev_pm_domain_set(dev->dev, NULL);
 414}
 415
 416static const struct dev_pm_ops mei_txe_pm_ops = {
 417        SET_SYSTEM_SLEEP_PM_OPS(mei_txe_pci_suspend,
 418                                mei_txe_pci_resume)
 419        SET_RUNTIME_PM_OPS(
 420                mei_txe_pm_runtime_suspend,
 421                mei_txe_pm_runtime_resume,
 422                mei_txe_pm_runtime_idle)
 423};
 424
 425#define MEI_TXE_PM_OPS  (&mei_txe_pm_ops)
 426#else
 427#define MEI_TXE_PM_OPS  NULL
 428#endif /* CONFIG_PM */
 429
 430/*
 431 *  PCI driver structure
 432 */
 433static struct pci_driver mei_txe_driver = {
 434        .name = KBUILD_MODNAME,
 435        .id_table = mei_txe_pci_tbl,
 436        .probe = mei_txe_probe,
 437        .remove = mei_txe_remove,
 438        .shutdown = mei_txe_remove,
 439        .driver.pm = MEI_TXE_PM_OPS,
 440};
 441
 442module_pci_driver(mei_txe_driver);
 443
 444MODULE_AUTHOR("Intel Corporation");
 445MODULE_DESCRIPTION("Intel(R) Trusted Execution Environment Interface");
 446MODULE_LICENSE("GPL v2");
 447
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.