153 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for the Intel SCU IPC mechanism4 *5 * (C) Copyright 2008-2010 Intel Corporation6 * Author: Sreedhara DS (sreedhara.ds@intel.com)7 *8 * This driver provides IOCTL interfaces to call Intel SCU IPC driver API.9 */10 11#include <linux/errno.h>12#include <linux/fcntl.h>13#include <linux/fs.h>14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/sched.h>17#include <linux/slab.h>18#include <linux/types.h>19#include <linux/uaccess.h>20 21#include <linux/platform_data/x86/intel_scu_ipc.h>22 23static int major;24 25static struct intel_scu_ipc_dev *scu;26static DEFINE_MUTEX(scu_lock);27 28/* IOCTL commands */29#define INTE_SCU_IPC_REGISTER_READ 030#define INTE_SCU_IPC_REGISTER_WRITE 131#define INTE_SCU_IPC_REGISTER_UPDATE 232 33struct scu_ipc_data {34 u32 count; /* No. of registers */35 u16 addr[5]; /* Register addresses */36 u8 data[5]; /* Register data */37 u8 mask; /* Valid for read-modify-write */38};39 40/**41 * scu_reg_access - implement register access ioctls42 * @cmd: command we are doing (read/write/update)43 * @data: kernel copy of ioctl data44 *45 * Allow the user to perform register accesses on the SCU via the46 * kernel interface47 */48 49static int scu_reg_access(u32 cmd, struct scu_ipc_data *data)50{51 unsigned int count = data->count;52 53 if (count == 0 || count == 3 || count > 4)54 return -EINVAL;55 56 switch (cmd) {57 case INTE_SCU_IPC_REGISTER_READ:58 return intel_scu_ipc_dev_readv(scu, data->addr, data->data, count);59 case INTE_SCU_IPC_REGISTER_WRITE:60 return intel_scu_ipc_dev_writev(scu, data->addr, data->data, count);61 case INTE_SCU_IPC_REGISTER_UPDATE:62 return intel_scu_ipc_dev_update(scu, data->addr[0], data->data[0],63 data->mask);64 default:65 return -ENOTTY;66 }67}68 69/**70 * scu_ipc_ioctl - control ioctls for the SCU71 * @fp: file handle of the SCU device72 * @cmd: ioctl coce73 * @arg: pointer to user passed structure74 *75 * Support the I/O and firmware flashing interfaces of the SCU76 */77static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,78 unsigned long arg)79{80 int ret;81 struct scu_ipc_data data;82 void __user *argp = (void __user *)arg;83 84 if (!capable(CAP_SYS_RAWIO))85 return -EPERM;86 87 if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data)))88 return -EFAULT;89 ret = scu_reg_access(cmd, &data);90 if (ret < 0)91 return ret;92 if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data)))93 return -EFAULT;94 return 0;95}96 97static int scu_ipc_open(struct inode *inode, struct file *file)98{99 int ret = 0;100 101 /* Only single open at the time */102 mutex_lock(&scu_lock);103 if (scu) {104 ret = -EBUSY;105 goto unlock;106 }107 108 scu = intel_scu_ipc_dev_get();109 if (!scu)110 ret = -ENODEV;111 112unlock:113 mutex_unlock(&scu_lock);114 return ret;115}116 117static int scu_ipc_release(struct inode *inode, struct file *file)118{119 mutex_lock(&scu_lock);120 intel_scu_ipc_dev_put(scu);121 scu = NULL;122 mutex_unlock(&scu_lock);123 124 return 0;125}126 127static const struct file_operations scu_ipc_fops = {128 .unlocked_ioctl = scu_ipc_ioctl,129 .open = scu_ipc_open,130 .release = scu_ipc_release,131};132 133static int __init ipc_module_init(void)134{135 major = register_chrdev(0, "intel_mid_scu", &scu_ipc_fops);136 if (major < 0)137 return major;138 139 return 0;140}141 142static void __exit ipc_module_exit(void)143{144 unregister_chrdev(major, "intel_mid_scu");145}146 147module_init(ipc_module_init);148module_exit(ipc_module_exit);149 150MODULE_LICENSE("GPL v2");151MODULE_DESCRIPTION("Utility driver for intel scu ipc");152MODULE_AUTHOR("Sreedhara <sreedhara.ds@intel.com>");153