1 // SPDX-License-Identifier: GPL-2.0
3 * Intel Speed Select Interface: Common functions
4 * Copyright (c) 2019, Intel Corporation.
7 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
10 #include <linux/cpufeature.h>
11 #include <linux/cpuhotplug.h>
13 #include <linux/hashtable.h>
14 #include <linux/miscdevice.h>
15 #include <linux/module.h>
16 #include <linux/pci.h>
17 #include <linux/sched/signal.h>
18 #include <linux/slab.h>
19 #include <linux/uaccess.h>
20 #include <uapi/linux/isst_if.h>
22 #include "isst_if_common.h"
24 #define MSR_THREAD_ID_INFO 0x53
25 #define MSR_CPU_BUS_NUMBER 0x128
27 static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
29 static int punit_msr_white_list[] = {
30 MSR_TURBO_RATIO_LIMIT,
31 MSR_CONFIG_TDP_CONTROL,
32 MSR_TURBO_RATIO_LIMIT1,
33 MSR_TURBO_RATIO_LIMIT2,
36 struct isst_valid_cmd_ranges {
42 struct isst_cmd_set_req_type {
48 static const struct isst_valid_cmd_ranges isst_valid_cmds[] = {
57 static const struct isst_cmd_set_req_type isst_cmd_set_reqs[] = {
68 struct hlist_node hnode;
76 static DECLARE_HASHTABLE(isst_hash, 8);
77 static DEFINE_MUTEX(isst_hash_lock);
79 static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param,
82 struct isst_cmd *sst_cmd;
84 sst_cmd = kmalloc(sizeof(*sst_cmd), GFP_KERNEL);
90 sst_cmd->mbox_cmd_type = mbox_cmd_type;
91 sst_cmd->param = param;
94 hash_add(isst_hash, &sst_cmd->hnode, sst_cmd->cmd);
99 static void isst_delete_hash(void)
101 struct isst_cmd *sst_cmd;
102 struct hlist_node *tmp;
105 hash_for_each_safe(isst_hash, i, tmp, sst_cmd, hnode) {
106 hash_del(&sst_cmd->hnode);
112 * isst_store_cmd() - Store command to a hash table
113 * @cmd: Mailbox command.
114 * @sub_cmd: Mailbox sub-command or MSR id.
115 * @mbox_cmd_type: Mailbox or MSR command.
116 * @param: Mailbox parameter.
117 * @data: Mailbox request data or MSR data.
119 * Stores the command to a hash table if there is no such command already
120 * stored. If already stored update the latest parameter and data for the
123 * Return: Return result of store to hash table, 0 for success, others for
126 int isst_store_cmd(int cmd, int sub_cmd, u32 cpu, int mbox_cmd_type,
129 struct isst_cmd *sst_cmd;
132 full_cmd = (cmd & GENMASK_ULL(15, 0)) << 16;
133 full_cmd |= (sub_cmd & GENMASK_ULL(15, 0));
134 mutex_lock(&isst_hash_lock);
135 hash_for_each_possible(isst_hash, sst_cmd, hnode, full_cmd) {
136 if (sst_cmd->cmd == full_cmd && sst_cmd->cpu == cpu &&
137 sst_cmd->mbox_cmd_type == mbox_cmd_type) {
138 sst_cmd->param = param;
139 sst_cmd->data = data;
140 mutex_unlock(&isst_hash_lock);
145 ret = isst_store_new_cmd(full_cmd, cpu, mbox_cmd_type, param, data);
146 mutex_unlock(&isst_hash_lock);
150 EXPORT_SYMBOL_GPL(isst_store_cmd);
152 static void isst_mbox_resume_command(struct isst_if_cmd_cb *cb,
153 struct isst_cmd *sst_cmd)
155 struct isst_if_mbox_cmd mbox_cmd;
158 mbox_cmd.command = (sst_cmd->cmd & GENMASK_ULL(31, 16)) >> 16;
159 mbox_cmd.sub_command = sst_cmd->cmd & GENMASK_ULL(15, 0);
160 mbox_cmd.parameter = sst_cmd->param;
161 mbox_cmd.req_data = sst_cmd->data;
162 mbox_cmd.logical_cpu = sst_cmd->cpu;
163 (cb->cmd_callback)((u8 *)&mbox_cmd, &wr_only, 1);
167 * isst_resume_common() - Process Resume request
169 * On resume replay all mailbox commands and MSRs.
173 void isst_resume_common(void)
175 struct isst_cmd *sst_cmd;
178 hash_for_each(isst_hash, i, sst_cmd, hnode) {
179 struct isst_if_cmd_cb *cb;
181 if (sst_cmd->mbox_cmd_type) {
182 cb = &punit_callbacks[ISST_IF_DEV_MBOX];
184 isst_mbox_resume_command(cb, sst_cmd);
186 wrmsrl_safe_on_cpu(sst_cmd->cpu, sst_cmd->cmd,
191 EXPORT_SYMBOL_GPL(isst_resume_common);
193 static void isst_restore_msr_local(int cpu)
195 struct isst_cmd *sst_cmd;
198 mutex_lock(&isst_hash_lock);
199 for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
200 if (!punit_msr_white_list[i])
203 hash_for_each_possible(isst_hash, sst_cmd, hnode,
204 punit_msr_white_list[i]) {
205 if (!sst_cmd->mbox_cmd_type && sst_cmd->cpu == cpu)
206 wrmsrl_safe(sst_cmd->cmd, sst_cmd->data);
209 mutex_unlock(&isst_hash_lock);
213 * isst_if_mbox_cmd_invalid() - Check invalid mailbox commands
214 * @cmd: Pointer to the command structure to verify.
216 * Invalid command to PUNIT to may result in instability of the platform.
217 * This function has a whitelist of commands, which are allowed.
219 * Return: Return true if the command is invalid, else false.
221 bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd)
225 if (cmd->logical_cpu >= nr_cpu_ids)
228 for (i = 0; i < ARRAY_SIZE(isst_valid_cmds); ++i) {
229 if (cmd->command == isst_valid_cmds[i].cmd &&
230 (cmd->sub_command >= isst_valid_cmds[i].sub_cmd_beg &&
231 cmd->sub_command <= isst_valid_cmds[i].sub_cmd_end)) {
238 EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_invalid);
241 * isst_if_mbox_cmd_set_req() - Check mailbox command is a set request
242 * @cmd: Pointer to the command structure to verify.
244 * Check if the given mail box level is set request and not a get request.
246 * Return: Return true if the command is set_req, else false.
248 bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd)
252 for (i = 0; i < ARRAY_SIZE(isst_cmd_set_reqs); ++i) {
253 if (cmd->command == isst_cmd_set_reqs[i].cmd &&
254 cmd->sub_command == isst_cmd_set_reqs[i].sub_cmd &&
255 cmd->parameter == isst_cmd_set_reqs[i].param) {
262 EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req);
264 static int isst_if_get_platform_info(void __user *argp)
266 struct isst_if_platform_info info;
268 info.api_version = ISST_IF_API_VERSION,
269 info.driver_version = ISST_IF_DRIVER_VERSION,
270 info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT,
271 info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered;
272 info.mmio_supported = punit_callbacks[ISST_IF_DEV_MMIO].registered;
274 if (copy_to_user(argp, &info, sizeof(info)))
281 struct isst_if_cpu_info {
282 /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
287 static struct isst_if_cpu_info *isst_cpu_info;
290 * isst_if_get_pci_dev() - Get the PCI device instance for a CPU
291 * @cpu: Logical CPU number.
292 * @bus_number: The bus number assigned by the hardware.
293 * @dev: The device number assigned by the hardware.
294 * @fn: The function number assigned by the hardware.
296 * Using cached bus information, find out the PCI device for a bus number,
297 * device and function.
299 * Return: Return pci_dev pointer or NULL.
301 struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
305 if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
306 cpu >= num_possible_cpus())
309 bus_number = isst_cpu_info[cpu].bus_info[bus_no];
313 return pci_get_domain_bus_and_slot(0, bus_number, PCI_DEVFN(dev, fn));
315 EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
317 static int isst_if_cpu_online(unsigned int cpu)
322 ret = rdmsrl_safe(MSR_CPU_BUS_NUMBER, &data);
324 /* This is not a fatal error on MSR mailbox only I/F */
325 isst_cpu_info[cpu].bus_info[0] = -1;
326 isst_cpu_info[cpu].bus_info[1] = -1;
328 isst_cpu_info[cpu].bus_info[0] = data & 0xff;
329 isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;
332 ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
334 isst_cpu_info[cpu].punit_cpu_id = -1;
337 isst_cpu_info[cpu].punit_cpu_id = data;
339 isst_restore_msr_local(cpu);
344 static int isst_if_online_id;
346 static int isst_if_cpu_info_init(void)
350 isst_cpu_info = kcalloc(num_possible_cpus(),
351 sizeof(*isst_cpu_info),
356 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
357 "platform/x86/isst-if:online",
358 isst_if_cpu_online, NULL);
360 kfree(isst_cpu_info);
364 isst_if_online_id = ret;
369 static void isst_if_cpu_info_exit(void)
371 cpuhp_remove_state(isst_if_online_id);
372 kfree(isst_cpu_info);
375 static long isst_if_proc_phyid_req(u8 *cmd_ptr, int *write_only, int resume)
377 struct isst_if_cpu_map *cpu_map;
379 cpu_map = (struct isst_if_cpu_map *)cmd_ptr;
380 if (cpu_map->logical_cpu >= nr_cpu_ids ||
381 cpu_map->logical_cpu >= num_possible_cpus())
385 cpu_map->physical_cpu = isst_cpu_info[cpu_map->logical_cpu].punit_cpu_id;
390 static bool match_punit_msr_white_list(int msr)
394 for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
395 if (punit_msr_white_list[i] == msr)
402 static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
404 struct isst_if_msr_cmd *msr_cmd;
407 msr_cmd = (struct isst_if_msr_cmd *)cmd_ptr;
409 if (!match_punit_msr_white_list(msr_cmd->msr))
412 if (msr_cmd->logical_cpu >= nr_cpu_ids)
415 if (msr_cmd->read_write) {
416 if (!capable(CAP_SYS_ADMIN))
419 ret = wrmsrl_safe_on_cpu(msr_cmd->logical_cpu,
424 ret = isst_store_cmd(0, msr_cmd->msr,
425 msr_cmd->logical_cpu,
426 0, 0, msr_cmd->data);
430 ret = rdmsrl_safe_on_cpu(msr_cmd->logical_cpu,
431 msr_cmd->msr, &data);
433 msr_cmd->data = data;
442 static long isst_if_exec_multi_cmd(void __user *argp, struct isst_if_cmd_cb *cb)
444 unsigned char __user *ptr;
450 /* Each multi command has u32 command count as the first field */
451 if (copy_from_user(&cmd_count, argp, sizeof(cmd_count)))
454 if (!cmd_count || cmd_count > ISST_IF_CMD_LIMIT)
457 cmd_ptr = kmalloc(cb->cmd_size, GFP_KERNEL);
461 /* cb->offset points to start of the command after the command count */
462 ptr = argp + cb->offset;
464 for (i = 0; i < cmd_count; ++i) {
467 if (signal_pending(current)) {
472 if (copy_from_user(cmd_ptr, ptr, cb->cmd_size)) {
477 ret = cb->cmd_callback(cmd_ptr, &wr_only, 0);
481 if (!wr_only && copy_to_user(ptr, cmd_ptr, cb->cmd_size)) {
494 static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
497 void __user *argp = (void __user *)arg;
498 struct isst_if_cmd_cb cmd_cb;
499 struct isst_if_cmd_cb *cb;
503 case ISST_IF_GET_PLATFORM_INFO:
504 ret = isst_if_get_platform_info(argp);
506 case ISST_IF_GET_PHY_ID:
507 cmd_cb.cmd_size = sizeof(struct isst_if_cpu_map);
508 cmd_cb.offset = offsetof(struct isst_if_cpu_maps, cpu_map);
509 cmd_cb.cmd_callback = isst_if_proc_phyid_req;
510 ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
513 cb = &punit_callbacks[ISST_IF_DEV_MMIO];
515 ret = isst_if_exec_multi_cmd(argp, cb);
517 case ISST_IF_MBOX_COMMAND:
518 cb = &punit_callbacks[ISST_IF_DEV_MBOX];
520 ret = isst_if_exec_multi_cmd(argp, cb);
522 case ISST_IF_MSR_COMMAND:
523 cmd_cb.cmd_size = sizeof(struct isst_if_msr_cmd);
524 cmd_cb.offset = offsetof(struct isst_if_msr_cmds, msr_cmd);
525 cmd_cb.cmd_callback = isst_if_msr_cmd_req;
526 ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
535 static DEFINE_MUTEX(punit_misc_dev_lock);
536 static int misc_usage_count;
537 static int misc_device_ret;
538 static int misc_device_open;
540 static int isst_if_open(struct inode *inode, struct file *file)
544 /* Fail open, if a module is going away */
545 mutex_lock(&punit_misc_dev_lock);
546 for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
547 struct isst_if_cmd_cb *cb = &punit_callbacks[i];
549 if (cb->registered && !try_module_get(cb->owner)) {
557 for (j = 0; j < i; ++j) {
558 struct isst_if_cmd_cb *cb;
560 cb = &punit_callbacks[j];
562 module_put(cb->owner);
567 mutex_unlock(&punit_misc_dev_lock);
572 static int isst_if_relase(struct inode *inode, struct file *f)
576 mutex_lock(&punit_misc_dev_lock);
578 for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
579 struct isst_if_cmd_cb *cb = &punit_callbacks[i];
582 module_put(cb->owner);
584 mutex_unlock(&punit_misc_dev_lock);
589 static const struct file_operations isst_if_char_driver_ops = {
590 .open = isst_if_open,
591 .unlocked_ioctl = isst_if_def_ioctl,
592 .release = isst_if_relase,
595 static struct miscdevice isst_if_char_driver = {
596 .minor = MISC_DYNAMIC_MINOR,
597 .name = "isst_interface",
598 .fops = &isst_if_char_driver_ops,
602 * isst_if_cdev_register() - Register callback for IOCTL
603 * @device_type: The device type this callback handling.
604 * @cb: Callback structure.
606 * This function registers a callback to device type. On very first call
607 * it will register a misc device, which is used for user kernel interface.
608 * Other calls simply increment ref count. Registry will fail, if the user
609 * already opened misc device for operation. Also if the misc device
610 * creation failed, then it will not try again and all callers will get
613 * Return: Return the return value from the misc creation device or -EINVAL
614 * for unsupported device type.
616 int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
619 return misc_device_ret;
621 if (device_type >= ISST_IF_DEV_MAX)
624 mutex_lock(&punit_misc_dev_lock);
625 if (misc_device_open) {
626 mutex_unlock(&punit_misc_dev_lock);
629 if (!misc_usage_count) {
632 misc_device_ret = misc_register(&isst_if_char_driver);
636 ret = isst_if_cpu_info_init();
638 misc_deregister(&isst_if_char_driver);
639 misc_device_ret = ret;
643 memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
644 punit_callbacks[device_type].registered = 1;
647 mutex_unlock(&punit_misc_dev_lock);
649 return misc_device_ret;
651 EXPORT_SYMBOL_GPL(isst_if_cdev_register);
654 * isst_if_cdev_unregister() - Unregister callback for IOCTL
655 * @device_type: The device type to unregister.
657 * This function unregisters the previously registered callback. If this
658 * is the last callback unregistering, then misc device is removed.
662 void isst_if_cdev_unregister(int device_type)
664 mutex_lock(&punit_misc_dev_lock);
666 punit_callbacks[device_type].registered = 0;
667 if (device_type == ISST_IF_DEV_MBOX)
669 if (!misc_usage_count && !misc_device_ret) {
670 misc_deregister(&isst_if_char_driver);
671 isst_if_cpu_info_exit();
673 mutex_unlock(&punit_misc_dev_lock);
675 EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
677 MODULE_LICENSE("GPL v2");