mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-05 10:04:12 +08:00
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:
parent
31a166fe9c
commit
71b21bd7f6
@ -7,3 +7,4 @@
|
||||
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_mbox_pci.o
|
||||
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o
|
||||
|
180
drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
Normal file
180
drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
Normal 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");
|
Loading…
Reference in New Issue
Block a user