Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Intel Speed Select Interface: Common functions
0004  * Copyright (c) 2019, Intel Corporation.
0005  * All rights reserved.
0006  *
0007  * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
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  * isst_store_cmd() - Store command to a hash table
0113  * @cmd: Mailbox command.
0114  * @sub_cmd: Mailbox sub-command or MSR id.
0115  * @mbox_cmd_type: Mailbox or MSR command.
0116  * @param: Mailbox parameter.
0117  * @data: Mailbox request data or MSR data.
0118  *
0119  * Stores the command to a hash table if there is no such command already
0120  * stored. If already stored update the latest parameter and data for the
0121  * command.
0122  *
0123  * Return: Return result of store to hash table, 0 for success, others for
0124  * failure.
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  * isst_resume_common() - Process Resume request
0168  *
0169  * On resume replay all mailbox commands and MSRs.
0170  *
0171  * Return: None.
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  * isst_if_mbox_cmd_invalid() - Check invalid mailbox commands
0214  * @cmd: Pointer to the command structure to verify.
0215  *
0216  * Invalid command to PUNIT to may result in instability of the platform.
0217  * This function has a whitelist of commands, which are allowed.
0218  *
0219  * Return: Return true if the command is invalid, else false.
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  * isst_if_mbox_cmd_set_req() - Check mailbox command is a set request
0242  * @cmd: Pointer to the command structure to verify.
0243  *
0244  * Check if the given mail box level is set request and not a get request.
0245  *
0246  * Return: Return true if the command is set_req, else false.
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     /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
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      * If there is no numa matched pci_dev, then there can be following cases:
0345      * 1. CONFIG_NUMA is not defined: In this case if there is only single device
0346      *    match, then we don't need numa information. Simply return last match.
0347      *    Othewise return NULL.
0348      * 2. NUMA information is not exposed via _SEG method. In this case it is similar
0349      *    to case 1.
0350      * 3. Numa information doesn't match with CPU numa node and more than one match
0351      *    return NULL.
0352      */
0353     if (!pci_dev && no_matches == 1)
0354         pci_dev = matched_pci_dev;
0355 
0356     /* Return pci_dev pointer for any matched CPU in the package */
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  * isst_if_get_pci_dev() - Get the PCI device instance for a CPU
0365  * @cpu: Logical CPU number.
0366  * @bus_number: The bus number assigned by the hardware.
0367  * @dev: The device number assigned by the hardware.
0368  * @fn: The function number assigned by the hardware.
0369  *
0370  * Using cached bus information, find out the PCI device for a bus number,
0371  * device and function.
0372  *
0373  * Return: Return pci_dev pointer or NULL.
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         /* This is not a fatal error on MSR mailbox only I/F */
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     /* Each multi command has u32 command count as the first field */
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     /* cb->offset points to start of the command after the command count */
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 /* Lock to prevent module registration when already opened by user space */
0625 static DEFINE_MUTEX(punit_misc_dev_open_lock);
0626 /* Lock to allow one share misc device for all ISST interace */
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     /* Fail open, if a module is going away */
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  * isst_if_cdev_register() - Register callback for IOCTL
0732  * @device_type: The device type this callback handling.
0733  * @cb: Callback structure.
0734  *
0735  * This function registers a callback to device type. On very first call
0736  * it will register a misc device, which is used for user kernel interface.
0737  * Other calls simply increment ref count. Registry will fail, if the user
0738  * already opened misc device for operation. Also if the misc device
0739  * creation failed, then it will not try again and all callers will get
0740  * failure code.
0741  *
0742  * Return: Return the return value from the misc creation device or -EINVAL
0743  * for unsupported device type.
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     /* Device is already open, we don't want to add new callbacks */
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          * No need of mutex as the misc device register failed
0766          * as no one can open device yet. Hence no contention.
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  * isst_if_cdev_unregister() - Unregister callback for IOCTL
0777  * @device_type: The device type to unregister.
0778  *
0779  * This function unregisters the previously registered callback. If this
0780  * is the last callback unregistering, then misc device is removed.
0781  *
0782  * Return: None.
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");