File Index
Files and directories
Sign in
FILE

isst_if_mbox_msr.c

File preview with syntax highlighting.

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