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 * The average time to complete some commands is about 40us. The current
  25 * count is enough to satisfy 40us. But when the firmware is very busy, this
  26 * causes timeout occasionally.  So increase to deal with some worst case
  27 * scenarios. Most of the command still complete in few us.
  28 */
  29#define OS_MAILBOX_RETRY_COUNT          100
  30
  31struct isst_if_device {
  32        struct mutex mutex;
  33};
  34
  35static int isst_if_mbox_cmd(struct pci_dev *pdev,
  36                            struct isst_if_mbox_cmd *mbox_cmd)
  37{
  38        u32 retries, data;
  39        int ret;
  40
  41        /* Poll for rb bit == 0 */
  42        retries = OS_MAILBOX_RETRY_COUNT;
  43        do {
  44                ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
  45                                            &data);
  46                if (ret)
  47                        return ret;
  48
  49                if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
  50                        ret = -EBUSY;
  51                        continue;
  52                }
  53                ret = 0;
  54                break;
  55        } while (--retries);
  56
  57        if (ret)
  58                return ret;
  59
  60        /* Write DATA register */
  61        ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_DATA,
  62                                     mbox_cmd->req_data);
  63        if (ret)
  64                return ret;
  65
  66        /* Write command register */
  67        data = BIT_ULL(PUNIT_MAILBOX_BUSY_BIT) |
  68                      (mbox_cmd->parameter & GENMASK_ULL(13, 0)) << 16 |
  69                      (mbox_cmd->sub_command << 8) |
  70                      mbox_cmd->command;
  71
  72        ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_INTERFACE, data);
  73        if (ret)
  74                return ret;
  75
  76        /* Poll for rb bit == 0 */
  77        retries = OS_MAILBOX_RETRY_COUNT;
  78        do {
  79                ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
  80                                            &data);
  81                if (ret)
  82                        return ret;
  83
  84                if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
  85                        ret = -EBUSY;
  86                        continue;
  87                }
  88
  89                if (data & 0xff)
  90                        return -ENXIO;
  91
  92                ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_DATA, &data);
  93                if (ret)
  94                        return ret;
  95
  96                mbox_cmd->resp_data = data;
  97                ret = 0;
  98                break;
  99        } while (--retries);
 100
 101        return ret;
 102}
 103
 104static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
 105{
 106        struct isst_if_mbox_cmd *mbox_cmd;
 107        struct isst_if_device *punit_dev;
 108        struct pci_dev *pdev;
 109        int ret;
 110
 111        mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
 112
 113        if (isst_if_mbox_cmd_invalid(mbox_cmd))
 114                return -EINVAL;
 115
 116        if (isst_if_mbox_cmd_set_req(mbox_cmd) && !capable(CAP_SYS_ADMIN))
 117                return -EPERM;
 118
 119        pdev = isst_if_get_pci_dev(mbox_cmd->logical_cpu, 1, 30, 1);
 120        if (!pdev)
 121                return -EINVAL;
 122
 123        punit_dev = pci_get_drvdata(pdev);
 124        if (!punit_dev)
 125                return -EINVAL;
 126
 127        /*
 128         * Basically we are allowing one complete mailbox transaction on
 129         * a mapped PCI device at a time.
 130         */
 131        mutex_lock(&punit_dev->mutex);
 132        ret = isst_if_mbox_cmd(pdev, mbox_cmd);
 133        if (!ret && !resume && isst_if_mbox_cmd_set_req(mbox_cmd))
 134                ret = isst_store_cmd(mbox_cmd->command,
 135                                     mbox_cmd->sub_command,
 136                                     mbox_cmd->logical_cpu, 1,
 137                                     mbox_cmd->parameter,
 138                                     mbox_cmd->req_data);
 139        mutex_unlock(&punit_dev->mutex);
 140        if (ret)
 141                return ret;
 142
 143        *write_only = 0;
 144
 145        return 0;
 146}
 147
 148static const struct pci_device_id isst_if_mbox_ids[] = {
 149        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_0)},
 150        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_1)},
 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