1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Intel Speed Select Interface: Common functions
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/cpufeature.h>
11 #include <linux/cpuhotplug.h>
12 #include <linux/fs.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>
21
22 #include <asm/cpu_device_id.h>
23 #include <asm/intel-family.h>
24
25 #include "isst_if_common.h"
26
27 #define MSR_THREAD_ID_INFO 0x53
28 #define MSR_PM_LOGICAL_ID 0x54
29 #define MSR_CPU_BUS_NUMBER 0x128
30
31 static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
32
33 static int punit_msr_white_list[] = {
34 MSR_TURBO_RATIO_LIMIT,
35 MSR_CONFIG_TDP_CONTROL,
36 MSR_TURBO_RATIO_LIMIT1,
37 MSR_TURBO_RATIO_LIMIT2,
38 MSR_PM_LOGICAL_ID,
39 };
40
41 struct isst_valid_cmd_ranges {
42 u16 cmd;
43 u16 sub_cmd_beg;
44 u16 sub_cmd_end;
45 };
46
47 struct isst_cmd_set_req_type {
48 u16 cmd;
49 u16 sub_cmd;
50 u16 param;
51 };
52
53 static const struct isst_valid_cmd_ranges isst_valid_cmds[] = {
54 {0xD0, 0x00, 0x03},
55 {0x7F, 0x00, 0x0C},
56 {0x7F, 0x10, 0x12},
57 {0x7F, 0x20, 0x23},
58 {0x94, 0x03, 0x03},
59 {0x95, 0x03, 0x03},
60 };
61
62 static const struct isst_cmd_set_req_type isst_cmd_set_reqs[] = {
63 {0xD0, 0x00, 0x08},
64 {0xD0, 0x01, 0x08},
65 {0xD0, 0x02, 0x08},
66 {0xD0, 0x03, 0x08},
67 {0x7F, 0x02, 0x00},
68 {0x7F, 0x08, 0x00},
69 {0x95, 0x03, 0x03},
70 };
71
72 struct isst_cmd {
73 struct hlist_node hnode;
74 u64 data;
75 u32 cmd;
76 int cpu;
77 int mbox_cmd_type;
78 u32 param;
79 };
80
81 static bool isst_hpm_support;
82
83 static DECLARE_HASHTABLE(isst_hash, 8);
84 static DEFINE_MUTEX(isst_hash_lock);
85
isst_store_new_cmd(int cmd,u32 cpu,int mbox_cmd_type,u32 param,u32 data)86 static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param,
87 u32 data)
88 {
89 struct isst_cmd *sst_cmd;
90
91 sst_cmd = kmalloc(sizeof(*sst_cmd), GFP_KERNEL);
92 if (!sst_cmd)
93 return -ENOMEM;
94
95 sst_cmd->cpu = cpu;
96 sst_cmd->cmd = cmd;
97 sst_cmd->mbox_cmd_type = mbox_cmd_type;
98 sst_cmd->param = param;
99 sst_cmd->data = data;
100
101 hash_add(isst_hash, &sst_cmd->hnode, sst_cmd->cmd);
102
103 return 0;
104 }
105
isst_delete_hash(void)106 static void isst_delete_hash(void)
107 {
108 struct isst_cmd *sst_cmd;
109 struct hlist_node *tmp;
110 int i;
111
112 hash_for_each_safe(isst_hash, i, tmp, sst_cmd, hnode) {
113 hash_del(&sst_cmd->hnode);
114 kfree(sst_cmd);
115 }
116 }
117
118 /**
119 * isst_store_cmd() - Store command to a hash table
120 * @cmd: Mailbox command.
121 * @sub_cmd: Mailbox sub-command or MSR id.
122 * @cpu: Target CPU for the command
123 * @mbox_cmd_type: Mailbox or MSR command.
124 * @param: Mailbox parameter.
125 * @data: Mailbox request data or MSR data.
126 *
127 * Stores the command to a hash table if there is no such command already
128 * stored. If already stored update the latest parameter and data for the
129 * command.
130 *
131 * Return: Return result of store to hash table, 0 for success, others for
132 * failure.
133 */
isst_store_cmd(int cmd,int sub_cmd,u32 cpu,int mbox_cmd_type,u32 param,u64 data)134 int isst_store_cmd(int cmd, int sub_cmd, u32 cpu, int mbox_cmd_type,
135 u32 param, u64 data)
136 {
137 struct isst_cmd *sst_cmd;
138 int full_cmd, ret;
139
140 full_cmd = (cmd & GENMASK_ULL(15, 0)) << 16;
141 full_cmd |= (sub_cmd & GENMASK_ULL(15, 0));
142 mutex_lock(&isst_hash_lock);
143 hash_for_each_possible(isst_hash, sst_cmd, hnode, full_cmd) {
144 if (sst_cmd->cmd == full_cmd && sst_cmd->cpu == cpu &&
145 sst_cmd->mbox_cmd_type == mbox_cmd_type) {
146 sst_cmd->param = param;
147 sst_cmd->data = data;
148 mutex_unlock(&isst_hash_lock);
149 return 0;
150 }
151 }
152
153 ret = isst_store_new_cmd(full_cmd, cpu, mbox_cmd_type, param, data);
154 mutex_unlock(&isst_hash_lock);
155
156 return ret;
157 }
158 EXPORT_SYMBOL_GPL(isst_store_cmd);
159
isst_mbox_resume_command(struct isst_if_cmd_cb * cb,struct isst_cmd * sst_cmd)160 static void isst_mbox_resume_command(struct isst_if_cmd_cb *cb,
161 struct isst_cmd *sst_cmd)
162 {
163 struct isst_if_mbox_cmd mbox_cmd;
164 int wr_only;
165
166 mbox_cmd.command = (sst_cmd->cmd & GENMASK_ULL(31, 16)) >> 16;
167 mbox_cmd.sub_command = sst_cmd->cmd & GENMASK_ULL(15, 0);
168 mbox_cmd.parameter = sst_cmd->param;
169 mbox_cmd.req_data = sst_cmd->data;
170 mbox_cmd.logical_cpu = sst_cmd->cpu;
171 (cb->cmd_callback)((u8 *)&mbox_cmd, &wr_only, 1);
172 }
173
174 /**
175 * isst_resume_common() - Process Resume request
176 *
177 * On resume replay all mailbox commands and MSRs.
178 *
179 * Return: None.
180 */
isst_resume_common(void)181 void isst_resume_common(void)
182 {
183 struct isst_cmd *sst_cmd;
184 int i;
185
186 hash_for_each(isst_hash, i, sst_cmd, hnode) {
187 struct isst_if_cmd_cb *cb;
188
189 if (sst_cmd->mbox_cmd_type) {
190 cb = &punit_callbacks[ISST_IF_DEV_MBOX];
191 if (cb->registered)
192 isst_mbox_resume_command(cb, sst_cmd);
193 } else {
194 wrmsrl_safe_on_cpu(sst_cmd->cpu, sst_cmd->cmd,
195 sst_cmd->data);
196 }
197 }
198 }
199 EXPORT_SYMBOL_GPL(isst_resume_common);
200
isst_restore_msr_local(int cpu)201 static void isst_restore_msr_local(int cpu)
202 {
203 struct isst_cmd *sst_cmd;
204 int i;
205
206 mutex_lock(&isst_hash_lock);
207 for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
208 if (!punit_msr_white_list[i])
209 break;
210
211 hash_for_each_possible(isst_hash, sst_cmd, hnode,
212 punit_msr_white_list[i]) {
213 if (!sst_cmd->mbox_cmd_type && sst_cmd->cpu == cpu)
214 wrmsrl_safe(sst_cmd->cmd, sst_cmd->data);
215 }
216 }
217 mutex_unlock(&isst_hash_lock);
218 }
219
220 /**
221 * isst_if_mbox_cmd_invalid() - Check invalid mailbox commands
222 * @cmd: Pointer to the command structure to verify.
223 *
224 * Invalid command to PUNIT to may result in instability of the platform.
225 * This function has a whitelist of commands, which are allowed.
226 *
227 * Return: Return true if the command is invalid, else false.
228 */
isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd * cmd)229 bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd)
230 {
231 int i;
232
233 if (cmd->logical_cpu >= nr_cpu_ids)
234 return true;
235
236 for (i = 0; i < ARRAY_SIZE(isst_valid_cmds); ++i) {
237 if (cmd->command == isst_valid_cmds[i].cmd &&
238 (cmd->sub_command >= isst_valid_cmds[i].sub_cmd_beg &&
239 cmd->sub_command <= isst_valid_cmds[i].sub_cmd_end)) {
240 return false;
241 }
242 }
243
244 return true;
245 }
246 EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_invalid);
247
248 /**
249 * isst_if_mbox_cmd_set_req() - Check mailbox command is a set request
250 * @cmd: Pointer to the command structure to verify.
251 *
252 * Check if the given mail box level is set request and not a get request.
253 *
254 * Return: Return true if the command is set_req, else false.
255 */
isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd * cmd)256 bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd)
257 {
258 int i;
259
260 for (i = 0; i < ARRAY_SIZE(isst_cmd_set_reqs); ++i) {
261 if (cmd->command == isst_cmd_set_reqs[i].cmd &&
262 cmd->sub_command == isst_cmd_set_reqs[i].sub_cmd &&
263 cmd->parameter == isst_cmd_set_reqs[i].param) {
264 return true;
265 }
266 }
267
268 return false;
269 }
270 EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req);
271
272 static int isst_if_api_version;
273
isst_if_get_platform_info(void __user * argp)274 static int isst_if_get_platform_info(void __user *argp)
275 {
276 struct isst_if_platform_info info;
277
278 info.api_version = isst_if_api_version;
279 info.driver_version = ISST_IF_DRIVER_VERSION;
280 info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT;
281 info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered;
282 info.mmio_supported = punit_callbacks[ISST_IF_DEV_MMIO].registered;
283
284 if (copy_to_user(argp, &info, sizeof(info)))
285 return -EFAULT;
286
287 return 0;
288 }
289
290 #define ISST_MAX_BUS_NUMBER 2
291
292 struct isst_if_cpu_info {
293 /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
294 int bus_info[ISST_MAX_BUS_NUMBER];
295 struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER];
296 int punit_cpu_id;
297 int numa_node;
298 };
299
300 struct isst_if_pkg_info {
301 struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER];
302 };
303
304 static struct isst_if_cpu_info *isst_cpu_info;
305 static struct isst_if_pkg_info *isst_pkg_info;
306
_isst_if_get_pci_dev(int cpu,int bus_no,int dev,int fn)307 static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
308 {
309 struct pci_dev *matched_pci_dev = NULL;
310 struct pci_dev *pci_dev = NULL;
311 struct pci_dev *_pci_dev = NULL;
312 int no_matches = 0, pkg_id;
313 int bus_number;
314
315 if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 ||
316 cpu >= nr_cpu_ids || cpu >= num_possible_cpus())
317 return NULL;
318
319 pkg_id = topology_physical_package_id(cpu);
320
321 bus_number = isst_cpu_info[cpu].bus_info[bus_no];
322 if (bus_number < 0)
323 return NULL;
324
325 for_each_pci_dev(_pci_dev) {
326 int node;
327
328 if (_pci_dev->bus->number != bus_number ||
329 _pci_dev->devfn != PCI_DEVFN(dev, fn))
330 continue;
331
332 ++no_matches;
333 if (!matched_pci_dev)
334 matched_pci_dev = _pci_dev;
335
336 node = dev_to_node(&_pci_dev->dev);
337 if (node == NUMA_NO_NODE) {
338 pr_info_once("Fail to get numa node for CPU:%d bus:%d dev:%d fn:%d\n",
339 cpu, bus_no, dev, fn);
340 continue;
341 }
342
343 if (node == isst_cpu_info[cpu].numa_node) {
344 isst_pkg_info[pkg_id].pci_dev[bus_no] = _pci_dev;
345
346 pci_dev = _pci_dev;
347 break;
348 }
349 }
350
351 /*
352 * If there is no numa matched pci_dev, then there can be following cases:
353 * 1. CONFIG_NUMA is not defined: In this case if there is only single device
354 * match, then we don't need numa information. Simply return last match.
355 * Othewise return NULL.
356 * 2. NUMA information is not exposed via _SEG method. In this case it is similar
357 * to case 1.
358 * 3. Numa information doesn't match with CPU numa node and more than one match
359 * return NULL.
360 */
361 if (!pci_dev && no_matches == 1)
362 pci_dev = matched_pci_dev;
363
364 /* Return pci_dev pointer for any matched CPU in the package */
365 if (!pci_dev)
366 pci_dev = isst_pkg_info[pkg_id].pci_dev[bus_no];
367
368 return pci_dev;
369 }
370
371 /**
372 * isst_if_get_pci_dev() - Get the PCI device instance for a CPU
373 * @cpu: Logical CPU number.
374 * @bus_no: The bus number assigned by the hardware.
375 * @dev: The device number assigned by the hardware.
376 * @fn: The function number assigned by the hardware.
377 *
378 * Using cached bus information, find out the PCI device for a bus number,
379 * device and function.
380 *
381 * Return: Return pci_dev pointer or NULL.
382 */
isst_if_get_pci_dev(int cpu,int bus_no,int dev,int fn)383 struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
384 {
385 struct pci_dev *pci_dev;
386
387 if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 ||
388 cpu >= nr_cpu_ids || cpu >= num_possible_cpus())
389 return NULL;
390
391 pci_dev = isst_cpu_info[cpu].pci_dev[bus_no];
392
393 if (pci_dev && pci_dev->devfn == PCI_DEVFN(dev, fn))
394 return pci_dev;
395
396 return _isst_if_get_pci_dev(cpu, bus_no, dev, fn);
397 }
398 EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
399
isst_if_cpu_online(unsigned int cpu)400 static int isst_if_cpu_online(unsigned int cpu)
401 {
402 u64 data;
403 int ret;
404
405 isst_cpu_info[cpu].numa_node = cpu_to_node(cpu);
406
407 ret = rdmsrl_safe(MSR_CPU_BUS_NUMBER, &data);
408 if (ret) {
409 /* This is not a fatal error on MSR mailbox only I/F */
410 isst_cpu_info[cpu].bus_info[0] = -1;
411 isst_cpu_info[cpu].bus_info[1] = -1;
412 } else {
413 isst_cpu_info[cpu].bus_info[0] = data & 0xff;
414 isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;
415 isst_cpu_info[cpu].pci_dev[0] = _isst_if_get_pci_dev(cpu, 0, 0, 1);
416 isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1);
417 }
418
419 if (isst_hpm_support) {
420
421 ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data);
422 if (!ret)
423 goto set_punit_id;
424 }
425
426 ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
427 if (ret) {
428 isst_cpu_info[cpu].punit_cpu_id = -1;
429 return ret;
430 }
431
432 set_punit_id:
433 isst_cpu_info[cpu].punit_cpu_id = data;
434
435 isst_restore_msr_local(cpu);
436
437 return 0;
438 }
439
440 static int isst_if_online_id;
441
isst_if_cpu_info_init(void)442 static int isst_if_cpu_info_init(void)
443 {
444 int ret;
445
446 isst_cpu_info = kcalloc(num_possible_cpus(),
447 sizeof(*isst_cpu_info),
448 GFP_KERNEL);
449 if (!isst_cpu_info)
450 return -ENOMEM;
451
452 isst_pkg_info = kcalloc(topology_max_packages(),
453 sizeof(*isst_pkg_info),
454 GFP_KERNEL);
455 if (!isst_pkg_info) {
456 kfree(isst_cpu_info);
457 return -ENOMEM;
458 }
459
460 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
461 "platform/x86/isst-if:online",
462 isst_if_cpu_online, NULL);
463 if (ret < 0) {
464 kfree(isst_pkg_info);
465 kfree(isst_cpu_info);
466 return ret;
467 }
468
469 isst_if_online_id = ret;
470
471 return 0;
472 }
473
isst_if_cpu_info_exit(void)474 static void isst_if_cpu_info_exit(void)
475 {
476 cpuhp_remove_state(isst_if_online_id);
477 kfree(isst_pkg_info);
478 kfree(isst_cpu_info);
479 };
480
isst_if_proc_phyid_req(u8 * cmd_ptr,int * write_only,int resume)481 static long isst_if_proc_phyid_req(u8 *cmd_ptr, int *write_only, int resume)
482 {
483 struct isst_if_cpu_map *cpu_map;
484
485 cpu_map = (struct isst_if_cpu_map *)cmd_ptr;
486 if (cpu_map->logical_cpu >= nr_cpu_ids ||
487 cpu_map->logical_cpu >= num_possible_cpus())
488 return -EINVAL;
489
490 *write_only = 0;
491 cpu_map->physical_cpu = isst_cpu_info[cpu_map->logical_cpu].punit_cpu_id;
492
493 return 0;
494 }
495
match_punit_msr_white_list(int msr)496 static bool match_punit_msr_white_list(int msr)
497 {
498 int i;
499
500 for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
501 if (punit_msr_white_list[i] == msr)
502 return true;
503 }
504
505 return false;
506 }
507
isst_if_msr_cmd_req(u8 * cmd_ptr,int * write_only,int resume)508 static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
509 {
510 struct isst_if_msr_cmd *msr_cmd;
511 int ret;
512
513 msr_cmd = (struct isst_if_msr_cmd *)cmd_ptr;
514
515 if (!match_punit_msr_white_list(msr_cmd->msr))
516 return -EINVAL;
517
518 if (msr_cmd->logical_cpu >= nr_cpu_ids)
519 return -EINVAL;
520
521 if (msr_cmd->read_write) {
522 if (!capable(CAP_SYS_ADMIN))
523 return -EPERM;
524
525 ret = wrmsrl_safe_on_cpu(msr_cmd->logical_cpu,
526 msr_cmd->msr,
527 msr_cmd->data);
528 *write_only = 1;
529 if (!ret && !resume)
530 ret = isst_store_cmd(0, msr_cmd->msr,
531 msr_cmd->logical_cpu,
532 0, 0, msr_cmd->data);
533 } else {
534 u64 data;
535
536 ret = rdmsrl_safe_on_cpu(msr_cmd->logical_cpu,
537 msr_cmd->msr, &data);
538 if (!ret) {
539 msr_cmd->data = data;
540 *write_only = 0;
541 }
542 }
543
544
545 return ret;
546 }
547
isst_if_exec_multi_cmd(void __user * argp,struct isst_if_cmd_cb * cb)548 static long isst_if_exec_multi_cmd(void __user *argp, struct isst_if_cmd_cb *cb)
549 {
550 unsigned char __user *ptr;
551 u32 cmd_count;
552 u8 *cmd_ptr;
553 long ret;
554 int i;
555
556 /* Each multi command has u32 command count as the first field */
557 if (copy_from_user(&cmd_count, argp, sizeof(cmd_count)))
558 return -EFAULT;
559
560 if (!cmd_count || cmd_count > ISST_IF_CMD_LIMIT)
561 return -EINVAL;
562
563 cmd_ptr = kmalloc(cb->cmd_size, GFP_KERNEL);
564 if (!cmd_ptr)
565 return -ENOMEM;
566
567 /* cb->offset points to start of the command after the command count */
568 ptr = argp + cb->offset;
569
570 for (i = 0; i < cmd_count; ++i) {
571 int wr_only;
572
573 if (signal_pending(current)) {
574 ret = -EINTR;
575 break;
576 }
577
578 if (copy_from_user(cmd_ptr, ptr, cb->cmd_size)) {
579 ret = -EFAULT;
580 break;
581 }
582
583 ret = cb->cmd_callback(cmd_ptr, &wr_only, 0);
584 if (ret)
585 break;
586
587 if (!wr_only && copy_to_user(ptr, cmd_ptr, cb->cmd_size)) {
588 ret = -EFAULT;
589 break;
590 }
591
592 ptr += cb->cmd_size;
593 }
594
595 kfree(cmd_ptr);
596
597 return i ? i : ret;
598 }
599
isst_if_def_ioctl(struct file * file,unsigned int cmd,unsigned long arg)600 static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
601 unsigned long arg)
602 {
603 void __user *argp = (void __user *)arg;
604 struct isst_if_cmd_cb cmd_cb;
605 struct isst_if_cmd_cb *cb;
606 long ret = -ENOTTY;
607 int i;
608
609 switch (cmd) {
610 case ISST_IF_GET_PLATFORM_INFO:
611 ret = isst_if_get_platform_info(argp);
612 break;
613 case ISST_IF_GET_PHY_ID:
614 cmd_cb.cmd_size = sizeof(struct isst_if_cpu_map);
615 cmd_cb.offset = offsetof(struct isst_if_cpu_maps, cpu_map);
616 cmd_cb.cmd_callback = isst_if_proc_phyid_req;
617 ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
618 break;
619 case ISST_IF_IO_CMD:
620 cb = &punit_callbacks[ISST_IF_DEV_MMIO];
621 if (cb->registered)
622 ret = isst_if_exec_multi_cmd(argp, cb);
623 break;
624 case ISST_IF_MBOX_COMMAND:
625 cb = &punit_callbacks[ISST_IF_DEV_MBOX];
626 if (cb->registered)
627 ret = isst_if_exec_multi_cmd(argp, cb);
628 break;
629 case ISST_IF_MSR_COMMAND:
630 cmd_cb.cmd_size = sizeof(struct isst_if_msr_cmd);
631 cmd_cb.offset = offsetof(struct isst_if_msr_cmds, msr_cmd);
632 cmd_cb.cmd_callback = isst_if_msr_cmd_req;
633 ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
634 break;
635 default:
636 for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
637 struct isst_if_cmd_cb *cb = &punit_callbacks[i];
638 int ret;
639
640 if (cb->def_ioctl) {
641 ret = cb->def_ioctl(file, cmd, arg);
642 if (!ret)
643 return ret;
644 }
645 }
646 break;
647 }
648
649 return ret;
650 }
651
652 /* Lock to prevent module registration when already opened by user space */
653 static DEFINE_MUTEX(punit_misc_dev_open_lock);
654 /* Lock to allow one shared misc device for all ISST interfaces */
655 static DEFINE_MUTEX(punit_misc_dev_reg_lock);
656 static int misc_usage_count;
657 static int misc_device_ret;
658 static int misc_device_open;
659
isst_if_open(struct inode * inode,struct file * file)660 static int isst_if_open(struct inode *inode, struct file *file)
661 {
662 int i, ret = 0;
663
664 /* Fail open, if a module is going away */
665 mutex_lock(&punit_misc_dev_open_lock);
666 for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
667 struct isst_if_cmd_cb *cb = &punit_callbacks[i];
668
669 if (cb->registered && !try_module_get(cb->owner)) {
670 ret = -ENODEV;
671 break;
672 }
673 }
674 if (ret) {
675 int j;
676
677 for (j = 0; j < i; ++j) {
678 struct isst_if_cmd_cb *cb;
679
680 cb = &punit_callbacks[j];
681 if (cb->registered)
682 module_put(cb->owner);
683 }
684 } else {
685 misc_device_open++;
686 }
687 mutex_unlock(&punit_misc_dev_open_lock);
688
689 return ret;
690 }
691
isst_if_relase(struct inode * inode,struct file * f)692 static int isst_if_relase(struct inode *inode, struct file *f)
693 {
694 int i;
695
696 mutex_lock(&punit_misc_dev_open_lock);
697 misc_device_open--;
698 for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
699 struct isst_if_cmd_cb *cb = &punit_callbacks[i];
700
701 if (cb->registered)
702 module_put(cb->owner);
703 }
704 mutex_unlock(&punit_misc_dev_open_lock);
705
706 return 0;
707 }
708
709 static const struct file_operations isst_if_char_driver_ops = {
710 .open = isst_if_open,
711 .unlocked_ioctl = isst_if_def_ioctl,
712 .release = isst_if_relase,
713 };
714
715 static struct miscdevice isst_if_char_driver = {
716 .minor = MISC_DYNAMIC_MINOR,
717 .name = "isst_interface",
718 .fops = &isst_if_char_driver_ops,
719 };
720
721 static const struct x86_cpu_id hpm_cpu_ids[] = {
722 X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_X, NULL),
723 X86_MATCH_INTEL_FAM6_MODEL(ATOM_CRESTMONT_X, NULL),
724 {}
725 };
726
isst_misc_reg(void)727 static int isst_misc_reg(void)
728 {
729 mutex_lock(&punit_misc_dev_reg_lock);
730 if (misc_device_ret)
731 goto unlock_exit;
732
733 if (!misc_usage_count) {
734 const struct x86_cpu_id *id;
735
736 id = x86_match_cpu(hpm_cpu_ids);
737 if (id)
738 isst_hpm_support = true;
739
740 misc_device_ret = isst_if_cpu_info_init();
741 if (misc_device_ret)
742 goto unlock_exit;
743
744 misc_device_ret = misc_register(&isst_if_char_driver);
745 if (misc_device_ret) {
746 isst_if_cpu_info_exit();
747 goto unlock_exit;
748 }
749 }
750 misc_usage_count++;
751
752 unlock_exit:
753 mutex_unlock(&punit_misc_dev_reg_lock);
754
755 return misc_device_ret;
756 }
757
isst_misc_unreg(void)758 static void isst_misc_unreg(void)
759 {
760 mutex_lock(&punit_misc_dev_reg_lock);
761 if (misc_usage_count)
762 misc_usage_count--;
763 if (!misc_usage_count && !misc_device_ret) {
764 misc_deregister(&isst_if_char_driver);
765 isst_if_cpu_info_exit();
766 }
767 mutex_unlock(&punit_misc_dev_reg_lock);
768 }
769
770 /**
771 * isst_if_cdev_register() - Register callback for IOCTL
772 * @device_type: The device type this callback handling.
773 * @cb: Callback structure.
774 *
775 * This function registers a callback to device type. On very first call
776 * it will register a misc device, which is used for user kernel interface.
777 * Other calls simply increment ref count. Registry will fail, if the user
778 * already opened misc device for operation. Also if the misc device
779 * creation failed, then it will not try again and all callers will get
780 * failure code.
781 *
782 * Return: Return the return value from the misc creation device or -EINVAL
783 * for unsupported device type.
784 */
isst_if_cdev_register(int device_type,struct isst_if_cmd_cb * cb)785 int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
786 {
787 int ret;
788
789 if (device_type >= ISST_IF_DEV_MAX)
790 return -EINVAL;
791
792 mutex_lock(&punit_misc_dev_open_lock);
793 /* Device is already open, we don't want to add new callbacks */
794 if (misc_device_open) {
795 mutex_unlock(&punit_misc_dev_open_lock);
796 return -EAGAIN;
797 }
798 if (!cb->api_version)
799 cb->api_version = ISST_IF_API_VERSION;
800 if (cb->api_version > isst_if_api_version)
801 isst_if_api_version = cb->api_version;
802 memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
803 punit_callbacks[device_type].registered = 1;
804 mutex_unlock(&punit_misc_dev_open_lock);
805
806 ret = isst_misc_reg();
807 if (ret) {
808 /*
809 * No need of mutex as the misc device register failed
810 * as no one can open device yet. Hence no contention.
811 */
812 punit_callbacks[device_type].registered = 0;
813 return ret;
814 }
815 return 0;
816 }
817 EXPORT_SYMBOL_GPL(isst_if_cdev_register);
818
819 /**
820 * isst_if_cdev_unregister() - Unregister callback for IOCTL
821 * @device_type: The device type to unregister.
822 *
823 * This function unregisters the previously registered callback. If this
824 * is the last callback unregistering, then misc device is removed.
825 *
826 * Return: None.
827 */
isst_if_cdev_unregister(int device_type)828 void isst_if_cdev_unregister(int device_type)
829 {
830 isst_misc_unreg();
831 mutex_lock(&punit_misc_dev_open_lock);
832 punit_callbacks[device_type].def_ioctl = NULL;
833 punit_callbacks[device_type].registered = 0;
834 if (device_type == ISST_IF_DEV_MBOX)
835 isst_delete_hash();
836 mutex_unlock(&punit_misc_dev_open_lock);
837 }
838 EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
839
840 MODULE_LICENSE("GPL v2");
841