platform/x86: ISST: Add Intel Speed Select mailbox interface via MSRs

Add an IOCTL to send mailbox commands to PUNIT using PUNIT MSRs for
mailbox. Some CPU models don't have PCI device, so need to use MSRs.
A limited set of mailbox commands can be sent to PUNIT.

This MMIO interface is used by the intel-speed-select tool under
tools/x86/power to enumerate and control Intel Speed Select features.
The MBOX commands ids and semantics of the message can be checked from
the source code of the tool.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
This commit is contained in:
Srinivas Pandruvada 2019-06-26 15:38:48 -07:00 committed by Andy Shevchenko
parent 31a166fe9c
commit 71b21bd7f6
2 changed files with 181 additions and 0 deletions

View File

@ -7,3 +7,4 @@
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o

View File

@ -0,0 +1,180 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select Interface: Mbox via MSR Interface
* Copyright (c) 2019, Intel Corporation.
* All rights reserved.
*
* Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
*/
#include <linux/module.h>
#include <linux/cpuhotplug.h>
#include <linux/pci.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/topology.h>
#include <linux/uaccess.h>
#include <uapi/linux/isst_if.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include "isst_if_common.h"
#define MSR_OS_MAILBOX_INTERFACE 0xB0
#define MSR_OS_MAILBOX_DATA 0xB1
#define MSR_OS_MAILBOX_BUSY_BIT 31
/*
* Based on experiments count is never more than 1, as the MSR overhead
* is enough to finish the command. So here this is the worst case number.
*/
#define OS_MAILBOX_RETRY_COUNT 3
static int isst_if_send_mbox_cmd(u8 command, u8 sub_command, u32 parameter,
u32 command_data, u32 *response_data)
{
u32 retries;
u64 data;
int ret;
/* Poll for rb bit == 0 */
retries = OS_MAILBOX_RETRY_COUNT;
do {
rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
ret = -EBUSY;
continue;
}
ret = 0;
break;
} while (--retries);
if (ret)
return ret;
/* Write DATA register */
wrmsrl(MSR_OS_MAILBOX_DATA, command_data);
/* Write command register */
data = BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT) |
(parameter & GENMASK_ULL(13, 0)) << 16 |
(sub_command << 8) |
command;
wrmsrl(MSR_OS_MAILBOX_INTERFACE, data);
/* Poll for rb bit == 0 */
retries = OS_MAILBOX_RETRY_COUNT;
do {
rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
ret = -EBUSY;
continue;
}
if (data & 0xff)
return -ENXIO;
if (response_data) {
rdmsrl(MSR_OS_MAILBOX_DATA, data);
*response_data = data;
}
ret = 0;
break;
} while (--retries);
return ret;
}
struct msrl_action {
int err;
struct isst_if_mbox_cmd *mbox_cmd;
};
/* revisit, smp_call_function_single should be enough for atomic mailbox! */
static void msrl_update_func(void *info)
{
struct msrl_action *act = info;
act->err = isst_if_send_mbox_cmd(act->mbox_cmd->command,
act->mbox_cmd->sub_command,
act->mbox_cmd->parameter,
act->mbox_cmd->req_data,
&act->mbox_cmd->resp_data);
}
static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
{
struct msrl_action action;
int ret;
action.mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
if (isst_if_mbox_cmd_invalid(action.mbox_cmd))
return -EINVAL;
if (isst_if_mbox_cmd_set_req(action.mbox_cmd) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
/*
* To complete mailbox command, we need to access two MSRs.
* So we don't want race to complete a mailbox transcation.
* Here smp_call ensures that msrl_update_func() has no race
* and also with wait flag, wait for completion.
* smp_call_function_single is using get_cpu() and put_cpu().
*/
ret = smp_call_function_single(action.mbox_cmd->logical_cpu,
msrl_update_func, &action, 1);
if (ret)
return ret;
*write_only = 0;
return action.err;
}
#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
static const struct x86_cpu_id isst_if_cpu_ids[] = {
ICPU(INTEL_FAM6_SKYLAKE_X),
{}
};
MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids);
static int __init isst_if_mbox_init(void)
{
struct isst_if_cmd_cb cb;
const struct x86_cpu_id *id;
u64 data;
int ret;
id = x86_match_cpu(isst_if_cpu_ids);
if (!id)
return -ENODEV;
/* Check presence of mailbox MSRs */
ret = rdmsrl_safe(MSR_OS_MAILBOX_INTERFACE, &data);
if (ret)
return ret;
ret = rdmsrl_safe(MSR_OS_MAILBOX_DATA, &data);
if (ret)
return ret;
memset(&cb, 0, sizeof(cb));
cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
cb.cmd_callback = isst_if_mbox_proc_cmd;
cb.owner = THIS_MODULE;
return isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
}
module_init(isst_if_mbox_init)
static void __exit isst_if_mbox_exit(void)
{
isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
}
module_exit(isst_if_mbox_exit)
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel speed select interface mailbox driver");