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 
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 
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  */
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 
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  */
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 
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  */
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  */
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 
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 
307 #define ISST_MAX_PCI_DOMAINS	8
308 
309 static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
310 {
311 	struct pci_dev *matched_pci_dev = NULL;
312 	struct pci_dev *pci_dev = NULL;
313 	int no_matches = 0, pkg_id;
314 	int i, bus_number;
315 
316 	if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 ||
317 	    cpu >= nr_cpu_ids || cpu >= num_possible_cpus())
318 		return NULL;
319 
320 	pkg_id = topology_physical_package_id(cpu);
321 
322 	bus_number = isst_cpu_info[cpu].bus_info[bus_no];
323 	if (bus_number < 0)
324 		return NULL;
325 
326 	for (i = 0; i < ISST_MAX_PCI_DOMAINS; ++i) {
327 		struct pci_dev *_pci_dev;
328 		int node;
329 
330 		_pci_dev = pci_get_domain_bus_and_slot(i, bus_number, PCI_DEVFN(dev, fn));
331 		if (!_pci_dev)
332 			continue;
333 
334 		++no_matches;
335 		if (!matched_pci_dev)
336 			matched_pci_dev = _pci_dev;
337 
338 		node = dev_to_node(&_pci_dev->dev);
339 		if (node == NUMA_NO_NODE) {
340 			pr_info("Fail to get numa node for CPU:%d bus:%d dev:%d fn:%d\n",
341 				cpu, bus_no, dev, fn);
342 			continue;
343 		}
344 
345 		if (node == isst_cpu_info[cpu].numa_node) {
346 			isst_pkg_info[pkg_id].pci_dev[bus_no] = _pci_dev;
347 
348 			pci_dev = _pci_dev;
349 			break;
350 		}
351 	}
352 
353 	/*
354 	 * If there is no numa matched pci_dev, then there can be following cases:
355 	 * 1. CONFIG_NUMA is not defined: In this case if there is only single device
356 	 *    match, then we don't need numa information. Simply return last match.
357 	 *    Othewise return NULL.
358 	 * 2. NUMA information is not exposed via _SEG method. In this case it is similar
359 	 *    to case 1.
360 	 * 3. Numa information doesn't match with CPU numa node and more than one match
361 	 *    return NULL.
362 	 */
363 	if (!pci_dev && no_matches == 1)
364 		pci_dev = matched_pci_dev;
365 
366 	/* Return pci_dev pointer for any matched CPU in the package */
367 	if (!pci_dev)
368 		pci_dev = isst_pkg_info[pkg_id].pci_dev[bus_no];
369 
370 	return pci_dev;
371 }
372 
373 /**
374  * isst_if_get_pci_dev() - Get the PCI device instance for a CPU
375  * @cpu: Logical CPU number.
376  * @bus_no: The bus number assigned by the hardware.
377  * @dev: The device number assigned by the hardware.
378  * @fn: The function number assigned by the hardware.
379  *
380  * Using cached bus information, find out the PCI device for a bus number,
381  * device and function.
382  *
383  * Return: Return pci_dev pointer or NULL.
384  */
385 struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
386 {
387 	struct pci_dev *pci_dev;
388 
389 	if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER  || cpu < 0 ||
390 	    cpu >= nr_cpu_ids || cpu >= num_possible_cpus())
391 		return NULL;
392 
393 	pci_dev = isst_cpu_info[cpu].pci_dev[bus_no];
394 
395 	if (pci_dev && pci_dev->devfn == PCI_DEVFN(dev, fn))
396 		return pci_dev;
397 
398 	return _isst_if_get_pci_dev(cpu, bus_no, dev, fn);
399 }
400 EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
401 
402 static int isst_if_cpu_online(unsigned int cpu)
403 {
404 	u64 data;
405 	int ret;
406 
407 	isst_cpu_info[cpu].numa_node = cpu_to_node(cpu);
408 
409 	ret = rdmsrl_safe(MSR_CPU_BUS_NUMBER, &data);
410 	if (ret) {
411 		/* This is not a fatal error on MSR mailbox only I/F */
412 		isst_cpu_info[cpu].bus_info[0] = -1;
413 		isst_cpu_info[cpu].bus_info[1] = -1;
414 	} else {
415 		isst_cpu_info[cpu].bus_info[0] = data & 0xff;
416 		isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;
417 		isst_cpu_info[cpu].pci_dev[0] = _isst_if_get_pci_dev(cpu, 0, 0, 1);
418 		isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1);
419 	}
420 
421 	if (isst_hpm_support) {
422 
423 		ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data);
424 		if (!ret)
425 			goto set_punit_id;
426 	}
427 
428 	ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
429 	if (ret) {
430 		isst_cpu_info[cpu].punit_cpu_id = -1;
431 		return ret;
432 	}
433 
434 set_punit_id:
435 	isst_cpu_info[cpu].punit_cpu_id = data;
436 
437 	isst_restore_msr_local(cpu);
438 
439 	return 0;
440 }
441 
442 static int isst_if_online_id;
443 
444 static int isst_if_cpu_info_init(void)
445 {
446 	int ret;
447 
448 	isst_cpu_info = kcalloc(num_possible_cpus(),
449 				sizeof(*isst_cpu_info),
450 				GFP_KERNEL);
451 	if (!isst_cpu_info)
452 		return -ENOMEM;
453 
454 	isst_pkg_info = kcalloc(topology_max_packages(),
455 				sizeof(*isst_pkg_info),
456 				GFP_KERNEL);
457 	if (!isst_pkg_info) {
458 		kfree(isst_cpu_info);
459 		return -ENOMEM;
460 	}
461 
462 	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
463 				"platform/x86/isst-if:online",
464 				isst_if_cpu_online, NULL);
465 	if (ret < 0) {
466 		kfree(isst_pkg_info);
467 		kfree(isst_cpu_info);
468 		return ret;
469 	}
470 
471 	isst_if_online_id = ret;
472 
473 	return 0;
474 }
475 
476 static void isst_if_cpu_info_exit(void)
477 {
478 	cpuhp_remove_state(isst_if_online_id);
479 	kfree(isst_pkg_info);
480 	kfree(isst_cpu_info);
481 };
482 
483 static long isst_if_proc_phyid_req(u8 *cmd_ptr, int *write_only, int resume)
484 {
485 	struct isst_if_cpu_map *cpu_map;
486 
487 	cpu_map = (struct isst_if_cpu_map *)cmd_ptr;
488 	if (cpu_map->logical_cpu >= nr_cpu_ids ||
489 	    cpu_map->logical_cpu >= num_possible_cpus())
490 		return -EINVAL;
491 
492 	*write_only = 0;
493 	cpu_map->physical_cpu = isst_cpu_info[cpu_map->logical_cpu].punit_cpu_id;
494 
495 	return 0;
496 }
497 
498 static bool match_punit_msr_white_list(int msr)
499 {
500 	int i;
501 
502 	for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
503 		if (punit_msr_white_list[i] == msr)
504 			return true;
505 	}
506 
507 	return false;
508 }
509 
510 static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
511 {
512 	struct isst_if_msr_cmd *msr_cmd;
513 	int ret;
514 
515 	msr_cmd = (struct isst_if_msr_cmd *)cmd_ptr;
516 
517 	if (!match_punit_msr_white_list(msr_cmd->msr))
518 		return -EINVAL;
519 
520 	if (msr_cmd->logical_cpu >= nr_cpu_ids)
521 		return -EINVAL;
522 
523 	if (msr_cmd->read_write) {
524 		if (!capable(CAP_SYS_ADMIN))
525 			return -EPERM;
526 
527 		ret = wrmsrl_safe_on_cpu(msr_cmd->logical_cpu,
528 					 msr_cmd->msr,
529 					 msr_cmd->data);
530 		*write_only = 1;
531 		if (!ret && !resume)
532 			ret = isst_store_cmd(0, msr_cmd->msr,
533 					     msr_cmd->logical_cpu,
534 					     0, 0, msr_cmd->data);
535 	} else {
536 		u64 data;
537 
538 		ret = rdmsrl_safe_on_cpu(msr_cmd->logical_cpu,
539 					 msr_cmd->msr, &data);
540 		if (!ret) {
541 			msr_cmd->data = data;
542 			*write_only = 0;
543 		}
544 	}
545 
546 
547 	return ret;
548 }
549 
550 static long isst_if_exec_multi_cmd(void __user *argp, struct isst_if_cmd_cb *cb)
551 {
552 	unsigned char __user *ptr;
553 	u32 cmd_count;
554 	u8 *cmd_ptr;
555 	long ret;
556 	int i;
557 
558 	/* Each multi command has u32 command count as the first field */
559 	if (copy_from_user(&cmd_count, argp, sizeof(cmd_count)))
560 		return -EFAULT;
561 
562 	if (!cmd_count || cmd_count > ISST_IF_CMD_LIMIT)
563 		return -EINVAL;
564 
565 	cmd_ptr = kmalloc(cb->cmd_size, GFP_KERNEL);
566 	if (!cmd_ptr)
567 		return -ENOMEM;
568 
569 	/* cb->offset points to start of the command after the command count */
570 	ptr = argp + cb->offset;
571 
572 	for (i = 0; i < cmd_count; ++i) {
573 		int wr_only;
574 
575 		if (signal_pending(current)) {
576 			ret = -EINTR;
577 			break;
578 		}
579 
580 		if (copy_from_user(cmd_ptr, ptr, cb->cmd_size)) {
581 			ret = -EFAULT;
582 			break;
583 		}
584 
585 		ret = cb->cmd_callback(cmd_ptr, &wr_only, 0);
586 		if (ret)
587 			break;
588 
589 		if (!wr_only && copy_to_user(ptr, cmd_ptr, cb->cmd_size)) {
590 			ret = -EFAULT;
591 			break;
592 		}
593 
594 		ptr += cb->cmd_size;
595 	}
596 
597 	kfree(cmd_ptr);
598 
599 	return i ? i : ret;
600 }
601 
602 static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
603 			      unsigned long arg)
604 {
605 	void __user *argp = (void __user *)arg;
606 	struct isst_if_cmd_cb cmd_cb;
607 	struct isst_if_cmd_cb *cb;
608 	long ret = -ENOTTY;
609 	int i;
610 
611 	switch (cmd) {
612 	case ISST_IF_GET_PLATFORM_INFO:
613 		ret = isst_if_get_platform_info(argp);
614 		break;
615 	case ISST_IF_GET_PHY_ID:
616 		cmd_cb.cmd_size = sizeof(struct isst_if_cpu_map);
617 		cmd_cb.offset = offsetof(struct isst_if_cpu_maps, cpu_map);
618 		cmd_cb.cmd_callback = isst_if_proc_phyid_req;
619 		ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
620 		break;
621 	case ISST_IF_IO_CMD:
622 		cb = &punit_callbacks[ISST_IF_DEV_MMIO];
623 		if (cb->registered)
624 			ret = isst_if_exec_multi_cmd(argp, cb);
625 		break;
626 	case ISST_IF_MBOX_COMMAND:
627 		cb = &punit_callbacks[ISST_IF_DEV_MBOX];
628 		if (cb->registered)
629 			ret = isst_if_exec_multi_cmd(argp, cb);
630 		break;
631 	case ISST_IF_MSR_COMMAND:
632 		cmd_cb.cmd_size = sizeof(struct isst_if_msr_cmd);
633 		cmd_cb.offset = offsetof(struct isst_if_msr_cmds, msr_cmd);
634 		cmd_cb.cmd_callback = isst_if_msr_cmd_req;
635 		ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
636 		break;
637 	default:
638 		for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
639 			struct isst_if_cmd_cb *cb = &punit_callbacks[i];
640 			int ret;
641 
642 			if (cb->def_ioctl) {
643 				ret = cb->def_ioctl(file, cmd, arg);
644 				if (!ret)
645 					return ret;
646 			}
647 		}
648 		break;
649 	}
650 
651 	return ret;
652 }
653 
654 /* Lock to prevent module registration when already opened by user space */
655 static DEFINE_MUTEX(punit_misc_dev_open_lock);
656 /* Lock to allow one shared misc device for all ISST interfaces */
657 static DEFINE_MUTEX(punit_misc_dev_reg_lock);
658 static int misc_usage_count;
659 static int misc_device_ret;
660 static int misc_device_open;
661 
662 static int isst_if_open(struct inode *inode, struct file *file)
663 {
664 	int i, ret = 0;
665 
666 	/* Fail open, if a module is going away */
667 	mutex_lock(&punit_misc_dev_open_lock);
668 	for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
669 		struct isst_if_cmd_cb *cb = &punit_callbacks[i];
670 
671 		if (cb->registered && !try_module_get(cb->owner)) {
672 			ret = -ENODEV;
673 			break;
674 		}
675 	}
676 	if (ret) {
677 		int j;
678 
679 		for (j = 0; j < i; ++j) {
680 			struct isst_if_cmd_cb *cb;
681 
682 			cb = &punit_callbacks[j];
683 			if (cb->registered)
684 				module_put(cb->owner);
685 		}
686 	} else {
687 		misc_device_open++;
688 	}
689 	mutex_unlock(&punit_misc_dev_open_lock);
690 
691 	return ret;
692 }
693 
694 static int isst_if_relase(struct inode *inode, struct file *f)
695 {
696 	int i;
697 
698 	mutex_lock(&punit_misc_dev_open_lock);
699 	misc_device_open--;
700 	for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
701 		struct isst_if_cmd_cb *cb = &punit_callbacks[i];
702 
703 		if (cb->registered)
704 			module_put(cb->owner);
705 	}
706 	mutex_unlock(&punit_misc_dev_open_lock);
707 
708 	return 0;
709 }
710 
711 static const struct file_operations isst_if_char_driver_ops = {
712 	.open = isst_if_open,
713 	.unlocked_ioctl = isst_if_def_ioctl,
714 	.release = isst_if_relase,
715 };
716 
717 static struct miscdevice isst_if_char_driver = {
718 	.minor		= MISC_DYNAMIC_MINOR,
719 	.name		= "isst_interface",
720 	.fops		= &isst_if_char_driver_ops,
721 };
722 
723 static const struct x86_cpu_id hpm_cpu_ids[] = {
724 	X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_X,	NULL),
725 	X86_MATCH_INTEL_FAM6_MODEL(SIERRAFOREST_X,	NULL),
726 	{}
727 };
728 
729 static int isst_misc_reg(void)
730 {
731 	mutex_lock(&punit_misc_dev_reg_lock);
732 	if (misc_device_ret)
733 		goto unlock_exit;
734 
735 	if (!misc_usage_count) {
736 		const struct x86_cpu_id *id;
737 
738 		id = x86_match_cpu(hpm_cpu_ids);
739 		if (id)
740 			isst_hpm_support = true;
741 
742 		misc_device_ret = isst_if_cpu_info_init();
743 		if (misc_device_ret)
744 			goto unlock_exit;
745 
746 		misc_device_ret = misc_register(&isst_if_char_driver);
747 		if (misc_device_ret) {
748 			isst_if_cpu_info_exit();
749 			goto unlock_exit;
750 		}
751 	}
752 	misc_usage_count++;
753 
754 unlock_exit:
755 	mutex_unlock(&punit_misc_dev_reg_lock);
756 
757 	return misc_device_ret;
758 }
759 
760 static void isst_misc_unreg(void)
761 {
762 	mutex_lock(&punit_misc_dev_reg_lock);
763 	if (misc_usage_count)
764 		misc_usage_count--;
765 	if (!misc_usage_count && !misc_device_ret) {
766 		misc_deregister(&isst_if_char_driver);
767 		isst_if_cpu_info_exit();
768 	}
769 	mutex_unlock(&punit_misc_dev_reg_lock);
770 }
771 
772 /**
773  * isst_if_cdev_register() - Register callback for IOCTL
774  * @device_type: The device type this callback handling.
775  * @cb:	Callback structure.
776  *
777  * This function registers a callback to device type. On very first call
778  * it will register a misc device, which is used for user kernel interface.
779  * Other calls simply increment ref count. Registry will fail, if the user
780  * already opened misc device for operation. Also if the misc device
781  * creation failed, then it will not try again and all callers will get
782  * failure code.
783  *
784  * Return: Return the return value from the misc creation device or -EINVAL
785  * for unsupported device type.
786  */
787 int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
788 {
789 	int ret;
790 
791 	if (device_type >= ISST_IF_DEV_MAX)
792 		return -EINVAL;
793 
794 	mutex_lock(&punit_misc_dev_open_lock);
795 	/* Device is already open, we don't want to add new callbacks */
796 	if (misc_device_open) {
797 		mutex_unlock(&punit_misc_dev_open_lock);
798 		return -EAGAIN;
799 	}
800 	if (!cb->api_version)
801 		cb->api_version = ISST_IF_API_VERSION;
802 	if (cb->api_version > isst_if_api_version)
803 		isst_if_api_version = cb->api_version;
804 	memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
805 	punit_callbacks[device_type].registered = 1;
806 	mutex_unlock(&punit_misc_dev_open_lock);
807 
808 	ret = isst_misc_reg();
809 	if (ret) {
810 		/*
811 		 * No need of mutex as the misc device register failed
812 		 * as no one can open device yet. Hence no contention.
813 		 */
814 		punit_callbacks[device_type].registered = 0;
815 		return ret;
816 	}
817 	return 0;
818 }
819 EXPORT_SYMBOL_GPL(isst_if_cdev_register);
820 
821 /**
822  * isst_if_cdev_unregister() - Unregister callback for IOCTL
823  * @device_type: The device type to unregister.
824  *
825  * This function unregisters the previously registered callback. If this
826  * is the last callback unregistering, then misc device is removed.
827  *
828  * Return: None.
829  */
830 void isst_if_cdev_unregister(int device_type)
831 {
832 	isst_misc_unreg();
833 	mutex_lock(&punit_misc_dev_open_lock);
834 	punit_callbacks[device_type].registered = 0;
835 	if (device_type == ISST_IF_DEV_MBOX)
836 		isst_delete_hash();
837 	mutex_unlock(&punit_misc_dev_open_lock);
838 }
839 EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
840 
841 MODULE_LICENSE("GPL v2");
842