linux/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Intel Speed Select Interface: Mbox via MSR 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/module.h>
  11#include <linux/cpuhotplug.h>
  12#include <linux/pci.h>
  13#include <linux/sched/signal.h>
  14#include <linux/slab.h>
  15#include <linux/suspend.h>
  16#include <linux/topology.h>
  17#include <linux/uaccess.h>
  18#include <uapi/linux/isst_if.h>
  19#include <asm/cpu_device_id.h>
  20#include <asm/intel-family.h>
  21
  22#include "isst_if_common.h"
  23
  24#define MSR_OS_MAILBOX_INTERFACE        0xB0
  25#define MSR_OS_MAILBOX_DATA             0xB1
  26#define MSR_OS_MAILBOX_BUSY_BIT         31
  27
  28/*
  29 * Based on experiments count is never more than 1, as the MSR overhead
  30 * is enough to finish the command. So here this is the worst case number.
  31 */
  32#define OS_MAILBOX_RETRY_COUNT          3
  33
  34static int isst_if_send_mbox_cmd(u8 command, u8 sub_command, u32 parameter,
  35                                 u32 command_data, u32 *response_data)
  36{
  37        u32 retries;
  38        u64 data;
  39        int ret;
  40
  41        /* Poll for rb bit == 0 */
  42        retries = OS_MAILBOX_RETRY_COUNT;
  43        do {
  44                rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
  45                if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
  46                        ret = -EBUSY;
  47                        continue;
  48                }
  49                ret = 0;
  50                break;
  51        } while (--retries);
  52
  53        if (ret)
  54                return ret;
  55
  56        /* Write DATA register */
  57        wrmsrl(MSR_OS_MAILBOX_DATA, command_data);
  58
  59        /* Write command register */
  60        data = BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT) |
  61                      (parameter & GENMASK_ULL(13, 0)) << 16 |
  62                      (sub_command << 8) |
  63                      command;
  64        wrmsrl(MSR_OS_MAILBOX_INTERFACE, data);
  65
  66        /* Poll for rb bit == 0 */
  67        retries = OS_MAILBOX_RETRY_COUNT;
  68        do {
  69                rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
  70                if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
  71                        ret = -EBUSY;
  72                        continue;
  73                }
  74
  75                if (data & 0xff)
  76                        return -ENXIO;
  77
  78                if (response_data) {
  79                        rdmsrl(MSR_OS_MAILBOX_DATA, data);
  80                        *response_data = data;
  81                }
  82                ret = 0;
  83                break;
  84        } while (--retries);
  85
  86        return ret;
  87}
  88
  89struct msrl_action {
  90        int err;
  91        struct isst_if_mbox_cmd *mbox_cmd;
  92};
  93
  94/* revisit, smp_call_function_single should be enough for atomic mailbox! */
  95static void msrl_update_func(void *info)
  96{
  97        struct msrl_action *act = info;
  98
  99        act->err = isst_if_send_mbox_cmd(act->mbox_cmd->command,
 100                                         act->mbox_cmd->sub_command,
 101                                         act->mbox_cmd->parameter,
 102                                         act->mbox_cmd->req_data,
 103                                         &act->mbox_cmd->resp_data);
 104}
 105
 106static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
 107{
 108        struct msrl_action action;
 109        int ret;
 110
 111        action.mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
 112
 113        if (isst_if_mbox_cmd_invalid(action.mbox_cmd))
 114                return -EINVAL;
 115
 116        if (isst_if_mbox_cmd_set_req(action.mbox_cmd) &&
 117            !capable(CAP_SYS_ADMIN))
 118                return -EPERM;
 119
 120        /*
 121         * To complete mailbox command, we need to access two MSRs.
 122         * So we don't want race to complete a mailbox transcation.
 123         * Here smp_call ensures that msrl_update_func() has no race
 124         * and also with wait flag, wait for completion.
 125         * smp_call_function_single is using get_cpu() and put_cpu().
 126         */
 127        ret = smp_call_function_single(action.mbox_cmd->logical_cpu,
 128                                       msrl_update_func, &action, 1);
 129        if (ret)
 130                return ret;
 131
 132        if (!action.err && !resume && isst_if_mbox_cmd_set_req(action.mbox_cmd))
 133                action.err = isst_store_cmd(action.mbox_cmd->command,
 134                                            action.mbox_cmd->sub_command,
 135                                            action.mbox_cmd->logical_cpu, 1,
 136                                            action.mbox_cmd->parameter,
 137                                            action.mbox_cmd->req_data);
 138        *write_only = 0;
 139
 140        return action.err;
 141}
 142
 143
 144static int isst_pm_notify(struct notifier_block *nb,
 145                               unsigned long mode, void *_unused)
 146{
 147        switch (mode) {
 148        case PM_POST_HIBERNATION:
 149        case PM_POST_RESTORE:
 150        case PM_POST_SUSPEND:
 151                isst_resume_common();
 152                break;
 153        default:
 154                break;
 155        }
 156        return 0;
 157}
 158
 159static struct notifier_block isst_pm_nb = {
 160        .notifier_call = isst_pm_notify,
 161};
 162
 163#define ICPU(model)     { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
 164
 165static const struct x86_cpu_id isst_if_cpu_ids[] = {
 166        ICPU(INTEL_FAM6_SKYLAKE_X),
 167        {}
 168};
 169MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids);
 170
 171static int __init isst_if_mbox_init(void)
 172{
 173        struct isst_if_cmd_cb cb;
 174        const struct x86_cpu_id *id;
 175        u64 data;
 176        int ret;
 177
 178        id = x86_match_cpu(isst_if_cpu_ids);
 179        if (!id)
 180                return -ENODEV;
 181
 182        /* Check presence of mailbox MSRs */
 183        ret = rdmsrl_safe(MSR_OS_MAILBOX_INTERFACE, &data);
 184        if (ret)
 185                return ret;
 186
 187        ret = rdmsrl_safe(MSR_OS_MAILBOX_DATA, &data);
 188        if (ret)
 189                return ret;
 190
 191        memset(&cb, 0, sizeof(cb));
 192        cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
 193        cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
 194        cb.cmd_callback = isst_if_mbox_proc_cmd;
 195        cb.owner = THIS_MODULE;
 196        ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
 197        if (ret)
 198                return ret;
 199
 200        ret = register_pm_notifier(&isst_pm_nb);
 201        if (ret)
 202                isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
 203
 204        return ret;
 205}
 206module_init(isst_if_mbox_init)
 207
 208static void __exit isst_if_mbox_exit(void)
 209{
 210        unregister_pm_notifier(&isst_pm_nb);
 211        isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
 212}
 213module_exit(isst_if_mbox_exit)
 214
 215MODULE_LICENSE("GPL v2");
 216MODULE_DESCRIPTION("Intel speed select interface mailbox driver");
 217