0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/cpufeature.h>
0011 #include <linux/cpuhotplug.h>
0012 #include <linux/fs.h>
0013 #include <linux/hashtable.h>
0014 #include <linux/miscdevice.h>
0015 #include <linux/module.h>
0016 #include <linux/pci.h>
0017 #include <linux/sched/signal.h>
0018 #include <linux/slab.h>
0019 #include <linux/uaccess.h>
0020 #include <uapi/linux/isst_if.h>
0021
0022 #include "isst_if_common.h"
0023
0024 #define MSR_THREAD_ID_INFO 0x53
0025 #define MSR_CPU_BUS_NUMBER 0x128
0026
0027 static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
0028
0029 static int punit_msr_white_list[] = {
0030 MSR_TURBO_RATIO_LIMIT,
0031 MSR_CONFIG_TDP_CONTROL,
0032 MSR_TURBO_RATIO_LIMIT1,
0033 MSR_TURBO_RATIO_LIMIT2,
0034 };
0035
0036 struct isst_valid_cmd_ranges {
0037 u16 cmd;
0038 u16 sub_cmd_beg;
0039 u16 sub_cmd_end;
0040 };
0041
0042 struct isst_cmd_set_req_type {
0043 u16 cmd;
0044 u16 sub_cmd;
0045 u16 param;
0046 };
0047
0048 static const struct isst_valid_cmd_ranges isst_valid_cmds[] = {
0049 {0xD0, 0x00, 0x03},
0050 {0x7F, 0x00, 0x0B},
0051 {0x7F, 0x10, 0x12},
0052 {0x7F, 0x20, 0x23},
0053 {0x94, 0x03, 0x03},
0054 {0x95, 0x03, 0x03},
0055 };
0056
0057 static const struct isst_cmd_set_req_type isst_cmd_set_reqs[] = {
0058 {0xD0, 0x00, 0x08},
0059 {0xD0, 0x01, 0x08},
0060 {0xD0, 0x02, 0x08},
0061 {0xD0, 0x03, 0x08},
0062 {0x7F, 0x02, 0x00},
0063 {0x7F, 0x08, 0x00},
0064 {0x95, 0x03, 0x03},
0065 };
0066
0067 struct isst_cmd {
0068 struct hlist_node hnode;
0069 u64 data;
0070 u32 cmd;
0071 int cpu;
0072 int mbox_cmd_type;
0073 u32 param;
0074 };
0075
0076 static DECLARE_HASHTABLE(isst_hash, 8);
0077 static DEFINE_MUTEX(isst_hash_lock);
0078
0079 static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param,
0080 u32 data)
0081 {
0082 struct isst_cmd *sst_cmd;
0083
0084 sst_cmd = kmalloc(sizeof(*sst_cmd), GFP_KERNEL);
0085 if (!sst_cmd)
0086 return -ENOMEM;
0087
0088 sst_cmd->cpu = cpu;
0089 sst_cmd->cmd = cmd;
0090 sst_cmd->mbox_cmd_type = mbox_cmd_type;
0091 sst_cmd->param = param;
0092 sst_cmd->data = data;
0093
0094 hash_add(isst_hash, &sst_cmd->hnode, sst_cmd->cmd);
0095
0096 return 0;
0097 }
0098
0099 static void isst_delete_hash(void)
0100 {
0101 struct isst_cmd *sst_cmd;
0102 struct hlist_node *tmp;
0103 int i;
0104
0105 hash_for_each_safe(isst_hash, i, tmp, sst_cmd, hnode) {
0106 hash_del(&sst_cmd->hnode);
0107 kfree(sst_cmd);
0108 }
0109 }
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126 int isst_store_cmd(int cmd, int sub_cmd, u32 cpu, int mbox_cmd_type,
0127 u32 param, u64 data)
0128 {
0129 struct isst_cmd *sst_cmd;
0130 int full_cmd, ret;
0131
0132 full_cmd = (cmd & GENMASK_ULL(15, 0)) << 16;
0133 full_cmd |= (sub_cmd & GENMASK_ULL(15, 0));
0134 mutex_lock(&isst_hash_lock);
0135 hash_for_each_possible(isst_hash, sst_cmd, hnode, full_cmd) {
0136 if (sst_cmd->cmd == full_cmd && sst_cmd->cpu == cpu &&
0137 sst_cmd->mbox_cmd_type == mbox_cmd_type) {
0138 sst_cmd->param = param;
0139 sst_cmd->data = data;
0140 mutex_unlock(&isst_hash_lock);
0141 return 0;
0142 }
0143 }
0144
0145 ret = isst_store_new_cmd(full_cmd, cpu, mbox_cmd_type, param, data);
0146 mutex_unlock(&isst_hash_lock);
0147
0148 return ret;
0149 }
0150 EXPORT_SYMBOL_GPL(isst_store_cmd);
0151
0152 static void isst_mbox_resume_command(struct isst_if_cmd_cb *cb,
0153 struct isst_cmd *sst_cmd)
0154 {
0155 struct isst_if_mbox_cmd mbox_cmd;
0156 int wr_only;
0157
0158 mbox_cmd.command = (sst_cmd->cmd & GENMASK_ULL(31, 16)) >> 16;
0159 mbox_cmd.sub_command = sst_cmd->cmd & GENMASK_ULL(15, 0);
0160 mbox_cmd.parameter = sst_cmd->param;
0161 mbox_cmd.req_data = sst_cmd->data;
0162 mbox_cmd.logical_cpu = sst_cmd->cpu;
0163 (cb->cmd_callback)((u8 *)&mbox_cmd, &wr_only, 1);
0164 }
0165
0166
0167
0168
0169
0170
0171
0172
0173 void isst_resume_common(void)
0174 {
0175 struct isst_cmd *sst_cmd;
0176 int i;
0177
0178 hash_for_each(isst_hash, i, sst_cmd, hnode) {
0179 struct isst_if_cmd_cb *cb;
0180
0181 if (sst_cmd->mbox_cmd_type) {
0182 cb = &punit_callbacks[ISST_IF_DEV_MBOX];
0183 if (cb->registered)
0184 isst_mbox_resume_command(cb, sst_cmd);
0185 } else {
0186 wrmsrl_safe_on_cpu(sst_cmd->cpu, sst_cmd->cmd,
0187 sst_cmd->data);
0188 }
0189 }
0190 }
0191 EXPORT_SYMBOL_GPL(isst_resume_common);
0192
0193 static void isst_restore_msr_local(int cpu)
0194 {
0195 struct isst_cmd *sst_cmd;
0196 int i;
0197
0198 mutex_lock(&isst_hash_lock);
0199 for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
0200 if (!punit_msr_white_list[i])
0201 break;
0202
0203 hash_for_each_possible(isst_hash, sst_cmd, hnode,
0204 punit_msr_white_list[i]) {
0205 if (!sst_cmd->mbox_cmd_type && sst_cmd->cpu == cpu)
0206 wrmsrl_safe(sst_cmd->cmd, sst_cmd->data);
0207 }
0208 }
0209 mutex_unlock(&isst_hash_lock);
0210 }
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221 bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd)
0222 {
0223 int i;
0224
0225 if (cmd->logical_cpu >= nr_cpu_ids)
0226 return true;
0227
0228 for (i = 0; i < ARRAY_SIZE(isst_valid_cmds); ++i) {
0229 if (cmd->command == isst_valid_cmds[i].cmd &&
0230 (cmd->sub_command >= isst_valid_cmds[i].sub_cmd_beg &&
0231 cmd->sub_command <= isst_valid_cmds[i].sub_cmd_end)) {
0232 return false;
0233 }
0234 }
0235
0236 return true;
0237 }
0238 EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_invalid);
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248 bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd)
0249 {
0250 int i;
0251
0252 for (i = 0; i < ARRAY_SIZE(isst_cmd_set_reqs); ++i) {
0253 if (cmd->command == isst_cmd_set_reqs[i].cmd &&
0254 cmd->sub_command == isst_cmd_set_reqs[i].sub_cmd &&
0255 cmd->parameter == isst_cmd_set_reqs[i].param) {
0256 return true;
0257 }
0258 }
0259
0260 return false;
0261 }
0262 EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req);
0263
0264 static int isst_if_get_platform_info(void __user *argp)
0265 {
0266 struct isst_if_platform_info info;
0267
0268 info.api_version = ISST_IF_API_VERSION;
0269 info.driver_version = ISST_IF_DRIVER_VERSION;
0270 info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT;
0271 info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered;
0272 info.mmio_supported = punit_callbacks[ISST_IF_DEV_MMIO].registered;
0273
0274 if (copy_to_user(argp, &info, sizeof(info)))
0275 return -EFAULT;
0276
0277 return 0;
0278 }
0279
0280 #define ISST_MAX_BUS_NUMBER 2
0281
0282 struct isst_if_cpu_info {
0283
0284 int bus_info[ISST_MAX_BUS_NUMBER];
0285 struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER];
0286 int punit_cpu_id;
0287 int numa_node;
0288 };
0289
0290 struct isst_if_pkg_info {
0291 struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER];
0292 };
0293
0294 static struct isst_if_cpu_info *isst_cpu_info;
0295 static struct isst_if_pkg_info *isst_pkg_info;
0296
0297 #define ISST_MAX_PCI_DOMAINS 8
0298
0299 static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
0300 {
0301 struct pci_dev *matched_pci_dev = NULL;
0302 struct pci_dev *pci_dev = NULL;
0303 int no_matches = 0, pkg_id;
0304 int i, bus_number;
0305
0306 if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 ||
0307 cpu >= nr_cpu_ids || cpu >= num_possible_cpus())
0308 return NULL;
0309
0310 pkg_id = topology_physical_package_id(cpu);
0311
0312 bus_number = isst_cpu_info[cpu].bus_info[bus_no];
0313 if (bus_number < 0)
0314 return NULL;
0315
0316 for (i = 0; i < ISST_MAX_PCI_DOMAINS; ++i) {
0317 struct pci_dev *_pci_dev;
0318 int node;
0319
0320 _pci_dev = pci_get_domain_bus_and_slot(i, bus_number, PCI_DEVFN(dev, fn));
0321 if (!_pci_dev)
0322 continue;
0323
0324 ++no_matches;
0325 if (!matched_pci_dev)
0326 matched_pci_dev = _pci_dev;
0327
0328 node = dev_to_node(&_pci_dev->dev);
0329 if (node == NUMA_NO_NODE) {
0330 pr_info("Fail to get numa node for CPU:%d bus:%d dev:%d fn:%d\n",
0331 cpu, bus_no, dev, fn);
0332 continue;
0333 }
0334
0335 if (node == isst_cpu_info[cpu].numa_node) {
0336 isst_pkg_info[pkg_id].pci_dev[bus_no] = _pci_dev;
0337
0338 pci_dev = _pci_dev;
0339 break;
0340 }
0341 }
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353 if (!pci_dev && no_matches == 1)
0354 pci_dev = matched_pci_dev;
0355
0356
0357 if (!pci_dev)
0358 pci_dev = isst_pkg_info[pkg_id].pci_dev[bus_no];
0359
0360 return pci_dev;
0361 }
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375 struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
0376 {
0377 struct pci_dev *pci_dev;
0378
0379 if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 ||
0380 cpu >= nr_cpu_ids || cpu >= num_possible_cpus())
0381 return NULL;
0382
0383 pci_dev = isst_cpu_info[cpu].pci_dev[bus_no];
0384
0385 if (pci_dev && pci_dev->devfn == PCI_DEVFN(dev, fn))
0386 return pci_dev;
0387
0388 return _isst_if_get_pci_dev(cpu, bus_no, dev, fn);
0389 }
0390 EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
0391
0392 static int isst_if_cpu_online(unsigned int cpu)
0393 {
0394 u64 data;
0395 int ret;
0396
0397 isst_cpu_info[cpu].numa_node = cpu_to_node(cpu);
0398
0399 ret = rdmsrl_safe(MSR_CPU_BUS_NUMBER, &data);
0400 if (ret) {
0401
0402 isst_cpu_info[cpu].bus_info[0] = -1;
0403 isst_cpu_info[cpu].bus_info[1] = -1;
0404 } else {
0405 isst_cpu_info[cpu].bus_info[0] = data & 0xff;
0406 isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;
0407 isst_cpu_info[cpu].pci_dev[0] = _isst_if_get_pci_dev(cpu, 0, 0, 1);
0408 isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1);
0409 }
0410
0411 ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
0412 if (ret) {
0413 isst_cpu_info[cpu].punit_cpu_id = -1;
0414 return ret;
0415 }
0416 isst_cpu_info[cpu].punit_cpu_id = data;
0417
0418 isst_restore_msr_local(cpu);
0419
0420 return 0;
0421 }
0422
0423 static int isst_if_online_id;
0424
0425 static int isst_if_cpu_info_init(void)
0426 {
0427 int ret;
0428
0429 isst_cpu_info = kcalloc(num_possible_cpus(),
0430 sizeof(*isst_cpu_info),
0431 GFP_KERNEL);
0432 if (!isst_cpu_info)
0433 return -ENOMEM;
0434
0435 isst_pkg_info = kcalloc(topology_max_packages(),
0436 sizeof(*isst_pkg_info),
0437 GFP_KERNEL);
0438 if (!isst_pkg_info) {
0439 kfree(isst_cpu_info);
0440 return -ENOMEM;
0441 }
0442
0443 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
0444 "platform/x86/isst-if:online",
0445 isst_if_cpu_online, NULL);
0446 if (ret < 0) {
0447 kfree(isst_pkg_info);
0448 kfree(isst_cpu_info);
0449 return ret;
0450 }
0451
0452 isst_if_online_id = ret;
0453
0454 return 0;
0455 }
0456
0457 static void isst_if_cpu_info_exit(void)
0458 {
0459 cpuhp_remove_state(isst_if_online_id);
0460 kfree(isst_pkg_info);
0461 kfree(isst_cpu_info);
0462 };
0463
0464 static long isst_if_proc_phyid_req(u8 *cmd_ptr, int *write_only, int resume)
0465 {
0466 struct isst_if_cpu_map *cpu_map;
0467
0468 cpu_map = (struct isst_if_cpu_map *)cmd_ptr;
0469 if (cpu_map->logical_cpu >= nr_cpu_ids ||
0470 cpu_map->logical_cpu >= num_possible_cpus())
0471 return -EINVAL;
0472
0473 *write_only = 0;
0474 cpu_map->physical_cpu = isst_cpu_info[cpu_map->logical_cpu].punit_cpu_id;
0475
0476 return 0;
0477 }
0478
0479 static bool match_punit_msr_white_list(int msr)
0480 {
0481 int i;
0482
0483 for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
0484 if (punit_msr_white_list[i] == msr)
0485 return true;
0486 }
0487
0488 return false;
0489 }
0490
0491 static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
0492 {
0493 struct isst_if_msr_cmd *msr_cmd;
0494 int ret;
0495
0496 msr_cmd = (struct isst_if_msr_cmd *)cmd_ptr;
0497
0498 if (!match_punit_msr_white_list(msr_cmd->msr))
0499 return -EINVAL;
0500
0501 if (msr_cmd->logical_cpu >= nr_cpu_ids)
0502 return -EINVAL;
0503
0504 if (msr_cmd->read_write) {
0505 if (!capable(CAP_SYS_ADMIN))
0506 return -EPERM;
0507
0508 ret = wrmsrl_safe_on_cpu(msr_cmd->logical_cpu,
0509 msr_cmd->msr,
0510 msr_cmd->data);
0511 *write_only = 1;
0512 if (!ret && !resume)
0513 ret = isst_store_cmd(0, msr_cmd->msr,
0514 msr_cmd->logical_cpu,
0515 0, 0, msr_cmd->data);
0516 } else {
0517 u64 data;
0518
0519 ret = rdmsrl_safe_on_cpu(msr_cmd->logical_cpu,
0520 msr_cmd->msr, &data);
0521 if (!ret) {
0522 msr_cmd->data = data;
0523 *write_only = 0;
0524 }
0525 }
0526
0527
0528 return ret;
0529 }
0530
0531 static long isst_if_exec_multi_cmd(void __user *argp, struct isst_if_cmd_cb *cb)
0532 {
0533 unsigned char __user *ptr;
0534 u32 cmd_count;
0535 u8 *cmd_ptr;
0536 long ret;
0537 int i;
0538
0539
0540 if (copy_from_user(&cmd_count, argp, sizeof(cmd_count)))
0541 return -EFAULT;
0542
0543 if (!cmd_count || cmd_count > ISST_IF_CMD_LIMIT)
0544 return -EINVAL;
0545
0546 cmd_ptr = kmalloc(cb->cmd_size, GFP_KERNEL);
0547 if (!cmd_ptr)
0548 return -ENOMEM;
0549
0550
0551 ptr = argp + cb->offset;
0552
0553 for (i = 0; i < cmd_count; ++i) {
0554 int wr_only;
0555
0556 if (signal_pending(current)) {
0557 ret = -EINTR;
0558 break;
0559 }
0560
0561 if (copy_from_user(cmd_ptr, ptr, cb->cmd_size)) {
0562 ret = -EFAULT;
0563 break;
0564 }
0565
0566 ret = cb->cmd_callback(cmd_ptr, &wr_only, 0);
0567 if (ret)
0568 break;
0569
0570 if (!wr_only && copy_to_user(ptr, cmd_ptr, cb->cmd_size)) {
0571 ret = -EFAULT;
0572 break;
0573 }
0574
0575 ptr += cb->cmd_size;
0576 }
0577
0578 kfree(cmd_ptr);
0579
0580 return i ? i : ret;
0581 }
0582
0583 static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
0584 unsigned long arg)
0585 {
0586 void __user *argp = (void __user *)arg;
0587 struct isst_if_cmd_cb cmd_cb;
0588 struct isst_if_cmd_cb *cb;
0589 long ret = -ENOTTY;
0590
0591 switch (cmd) {
0592 case ISST_IF_GET_PLATFORM_INFO:
0593 ret = isst_if_get_platform_info(argp);
0594 break;
0595 case ISST_IF_GET_PHY_ID:
0596 cmd_cb.cmd_size = sizeof(struct isst_if_cpu_map);
0597 cmd_cb.offset = offsetof(struct isst_if_cpu_maps, cpu_map);
0598 cmd_cb.cmd_callback = isst_if_proc_phyid_req;
0599 ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
0600 break;
0601 case ISST_IF_IO_CMD:
0602 cb = &punit_callbacks[ISST_IF_DEV_MMIO];
0603 if (cb->registered)
0604 ret = isst_if_exec_multi_cmd(argp, cb);
0605 break;
0606 case ISST_IF_MBOX_COMMAND:
0607 cb = &punit_callbacks[ISST_IF_DEV_MBOX];
0608 if (cb->registered)
0609 ret = isst_if_exec_multi_cmd(argp, cb);
0610 break;
0611 case ISST_IF_MSR_COMMAND:
0612 cmd_cb.cmd_size = sizeof(struct isst_if_msr_cmd);
0613 cmd_cb.offset = offsetof(struct isst_if_msr_cmds, msr_cmd);
0614 cmd_cb.cmd_callback = isst_if_msr_cmd_req;
0615 ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
0616 break;
0617 default:
0618 break;
0619 }
0620
0621 return ret;
0622 }
0623
0624
0625 static DEFINE_MUTEX(punit_misc_dev_open_lock);
0626
0627 static DEFINE_MUTEX(punit_misc_dev_reg_lock);
0628 static int misc_usage_count;
0629 static int misc_device_ret;
0630 static int misc_device_open;
0631
0632 static int isst_if_open(struct inode *inode, struct file *file)
0633 {
0634 int i, ret = 0;
0635
0636
0637 mutex_lock(&punit_misc_dev_open_lock);
0638 for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
0639 struct isst_if_cmd_cb *cb = &punit_callbacks[i];
0640
0641 if (cb->registered && !try_module_get(cb->owner)) {
0642 ret = -ENODEV;
0643 break;
0644 }
0645 }
0646 if (ret) {
0647 int j;
0648
0649 for (j = 0; j < i; ++j) {
0650 struct isst_if_cmd_cb *cb;
0651
0652 cb = &punit_callbacks[j];
0653 if (cb->registered)
0654 module_put(cb->owner);
0655 }
0656 } else {
0657 misc_device_open++;
0658 }
0659 mutex_unlock(&punit_misc_dev_open_lock);
0660
0661 return ret;
0662 }
0663
0664 static int isst_if_relase(struct inode *inode, struct file *f)
0665 {
0666 int i;
0667
0668 mutex_lock(&punit_misc_dev_open_lock);
0669 misc_device_open--;
0670 for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
0671 struct isst_if_cmd_cb *cb = &punit_callbacks[i];
0672
0673 if (cb->registered)
0674 module_put(cb->owner);
0675 }
0676 mutex_unlock(&punit_misc_dev_open_lock);
0677
0678 return 0;
0679 }
0680
0681 static const struct file_operations isst_if_char_driver_ops = {
0682 .open = isst_if_open,
0683 .unlocked_ioctl = isst_if_def_ioctl,
0684 .release = isst_if_relase,
0685 };
0686
0687 static struct miscdevice isst_if_char_driver = {
0688 .minor = MISC_DYNAMIC_MINOR,
0689 .name = "isst_interface",
0690 .fops = &isst_if_char_driver_ops,
0691 };
0692
0693 static int isst_misc_reg(void)
0694 {
0695 mutex_lock(&punit_misc_dev_reg_lock);
0696 if (misc_device_ret)
0697 goto unlock_exit;
0698
0699 if (!misc_usage_count) {
0700 misc_device_ret = isst_if_cpu_info_init();
0701 if (misc_device_ret)
0702 goto unlock_exit;
0703
0704 misc_device_ret = misc_register(&isst_if_char_driver);
0705 if (misc_device_ret) {
0706 isst_if_cpu_info_exit();
0707 goto unlock_exit;
0708 }
0709 }
0710 misc_usage_count++;
0711
0712 unlock_exit:
0713 mutex_unlock(&punit_misc_dev_reg_lock);
0714
0715 return misc_device_ret;
0716 }
0717
0718 static void isst_misc_unreg(void)
0719 {
0720 mutex_lock(&punit_misc_dev_reg_lock);
0721 if (misc_usage_count)
0722 misc_usage_count--;
0723 if (!misc_usage_count && !misc_device_ret) {
0724 misc_deregister(&isst_if_char_driver);
0725 isst_if_cpu_info_exit();
0726 }
0727 mutex_unlock(&punit_misc_dev_reg_lock);
0728 }
0729
0730
0731
0732
0733
0734
0735
0736
0737
0738
0739
0740
0741
0742
0743
0744
0745 int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
0746 {
0747 int ret;
0748
0749 if (device_type >= ISST_IF_DEV_MAX)
0750 return -EINVAL;
0751
0752 mutex_lock(&punit_misc_dev_open_lock);
0753
0754 if (misc_device_open) {
0755 mutex_unlock(&punit_misc_dev_open_lock);
0756 return -EAGAIN;
0757 }
0758 memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
0759 punit_callbacks[device_type].registered = 1;
0760 mutex_unlock(&punit_misc_dev_open_lock);
0761
0762 ret = isst_misc_reg();
0763 if (ret) {
0764
0765
0766
0767
0768 punit_callbacks[device_type].registered = 0;
0769 return ret;
0770 }
0771 return 0;
0772 }
0773 EXPORT_SYMBOL_GPL(isst_if_cdev_register);
0774
0775
0776
0777
0778
0779
0780
0781
0782
0783
0784 void isst_if_cdev_unregister(int device_type)
0785 {
0786 isst_misc_unreg();
0787 mutex_lock(&punit_misc_dev_open_lock);
0788 punit_callbacks[device_type].registered = 0;
0789 if (device_type == ISST_IF_DEV_MBOX)
0790 isst_delete_hash();
0791 mutex_unlock(&punit_misc_dev_open_lock);
0792 }
0793 EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
0794
0795 MODULE_LICENSE("GPL v2");