linux/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Intel Speed Select Interface: Mbox via PCI Interface
   4 * Copyright (c) 2019, Intel Corporation.
   5 * All rights reserved.
   6 *
   7 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
   8 */
   9
  10#include <linux/cpufeature.h>
  11#include <linux/module.h>
  12#include <linux/pci.h>
  13#include <linux/sched/signal.h>
  14#include <linux/uaccess.h>
  15#include <uapi/linux/isst_if.h>
  16
  17#include "isst_if_common.h"
  18
  19#define PUNIT_MAILBOX_DATA              0xA0
  20#define PUNIT_MAILBOX_INTERFACE         0xA4
  21#define PUNIT_MAILBOX_BUSY_BIT          31
  22
  23/*
  24 * Commands has variable amount of processing time. Most of the commands will
  25 * be done in 0-3 tries, but some takes up to 50.
  26 * The real processing time was observed as 25us for the most of the commands
  27 * at 2GHz. It is possible to optimize this count taking samples on customer
  28 * systems.
  29 */
  30#define OS_MAILBOX_RETRY_COUNT          50
  31
  32struct isst_if_device {
  33        struct mutex mutex;
  34};
  35
  36static int isst_if_mbox_cmd(struct pci_dev *pdev,
  37                            struct isst_if_mbox_cmd *mbox_cmd)
  38{
  39        u32 retries, data;
  40        int ret;
  41
  42        /* Poll for rb bit == 0 */
  43        retries = OS_MAILBOX_RETRY_COUNT;
  44        do {
  45                ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
  46                                            &data);
  47                if (ret)
  48                        return ret;
  49
  50                if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
  51                        ret = -EBUSY;
  52                        continue;
  53                }
  54                ret = 0;
  55                break;
  56        } while (--retries);
  57
  58        if (ret)
  59                return ret;
  60
  61        /* Write DATA register */
  62        ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_DATA,
  63                                     mbox_cmd->req_data);
  64        if (ret)
  65                return ret;
  66
  67        /* Write command register */
  68        data = BIT_ULL(PUNIT_MAILBOX_BUSY_BIT) |
  69                      (mbox_cmd->parameter & GENMASK_ULL(13, 0)) << 16 |
  70                      (mbox_cmd->sub_command << 8) |
  71                      mbox_cmd->command;
  72
  73        ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_INTERFACE, data);
  74        if (ret)
  75                return ret;
  76
  77        /* Poll for rb bit == 0 */
  78        retries = OS_MAILBOX_RETRY_COUNT;
  79        do {
  80                ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
  81                                            &data);
  82                if (ret)
  83                        return ret;
  84
  85                if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
  86                        ret = -EBUSY;
  87                        continue;
  88                }
  89
  90                if (data & 0xff)
  91                        return -ENXIO;
  92
  93                ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_DATA, &data);
  94                if (ret)
  95                        return ret;
  96
  97                mbox_cmd->resp_data = data;
  98                ret = 0;
  99                break;
 100        } while (--retries);
 101
 102        return ret;
 103}
 104
 105static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
 106{
 107        struct isst_if_mbox_cmd *mbox_cmd;
 108        struct isst_if_device *punit_dev;
 109        struct pci_dev *pdev;
 110        int ret;
 111
 112        mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
 113
 114        if (isst_if_mbox_cmd_invalid(mbox_cmd))
 115                return -EINVAL;
 116
 117        if (isst_if_mbox_cmd_set_req(mbox_cmd) && !capable(CAP_SYS_ADMIN))
 118                return -EPERM;
 119
 120        pdev = isst_if_get_pci_dev(mbox_cmd->logical_cpu, 1, 30, 1);
 121        if (!pdev)
 122                return -EINVAL;
 123
 124        punit_dev = pci_get_drvdata(pdev);
 125        if (!punit_dev)
 126                return -EINVAL;
 127
 128        /*
 129         * Basically we are allowing one complete mailbox transaction on
 130         * a mapped PCI device at a time.
 131         */
 132        mutex_lock(&punit_dev->mutex);
 133        ret = isst_if_mbox_cmd(pdev, mbox_cmd);
 134        if (!ret && !resume && isst_if_mbox_cmd_set_req(mbox_cmd))
 135                ret = isst_store_cmd(mbox_cmd->command,
 136                                     mbox_cmd->sub_command,
 137                                     mbox_cmd->logical_cpu, 1,
 138                                     mbox_cmd->parameter,
 139                                     mbox_cmd->req_data);
 140        mutex_unlock(&punit_dev->mutex);
 141        if (ret)
 142                return ret;
 143
 144        *write_only = 0;
 145
 146        return 0;
 147}
 148
 149static const struct pci_device_id isst_if_mbox_ids[] = {
 150        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_0)},
 151        { 0 },
 152};
 153MODULE_DEVICE_TABLE(pci, isst_if_mbox_ids);
 154
 155static int isst_if_mbox_probe(struct pci_dev *pdev,
 156                              const struct pci_device_id *ent)
 157{
 158        struct isst_if_device *punit_dev;
 159        struct isst_if_cmd_cb cb;
 160        int ret;
 161
 162        punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL);
 163        if (!punit_dev)
 164                return -ENOMEM;
 165
 166        ret = pcim_enable_device(pdev);
 167        if (ret)
 168                return ret;
 169
 170        mutex_init(&punit_dev->mutex);
 171        pci_set_drvdata(pdev, punit_dev);
 172
 173        memset(&cb, 0, sizeof(cb));
 174        cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
 175        cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
 176        cb.cmd_callback = isst_if_mbox_proc_cmd;
 177        cb.owner = THIS_MODULE;
 178        ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
 179
 180        if (ret)
 181                mutex_destroy(&punit_dev->mutex);
 182
 183        return ret;
 184}
 185
 186static void isst_if_mbox_remove(struct pci_dev *pdev)
 187{
 188        struct isst_if_device *punit_dev;
 189
 190        punit_dev = pci_get_drvdata(pdev);
 191        isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
 192        mutex_destroy(&punit_dev->mutex);
 193}
 194
 195static int __maybe_unused isst_if_resume(struct device *device)
 196{
 197        isst_resume_common();
 198        return 0;
 199}
 200
 201static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, NULL, isst_if_resume);
 202
 203static struct pci_driver isst_if_pci_driver = {
 204        .name                   = "isst_if_mbox_pci",
 205        .id_table               = isst_if_mbox_ids,
 206        .probe                  = isst_if_mbox_probe,
 207        .remove                 = isst_if_mbox_remove,
 208        .driver.pm              = &isst_if_pm_ops,
 209};
 210
 211module_pci_driver(isst_if_pci_driver);
 212
 213MODULE_LICENSE("GPL v2");
 214MODULE_DESCRIPTION("Intel speed select interface pci mailbox driver");
 215