162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the Intel SCU IPC mechanism 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright 2008-2010 Intel Corporation 662306a36Sopenharmony_ci * Author: Sreedhara DS (sreedhara.ds@intel.com) 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This driver provides IOCTL interfaces to call Intel SCU IPC driver API. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/fcntl.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/sched.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/types.h> 1962306a36Sopenharmony_ci#include <linux/uaccess.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/intel_scu_ipc.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int major; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct intel_scu_ipc_dev *scu; 2662306a36Sopenharmony_cistatic DEFINE_MUTEX(scu_lock); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* IOCTL commands */ 2962306a36Sopenharmony_ci#define INTE_SCU_IPC_REGISTER_READ 0 3062306a36Sopenharmony_ci#define INTE_SCU_IPC_REGISTER_WRITE 1 3162306a36Sopenharmony_ci#define INTE_SCU_IPC_REGISTER_UPDATE 2 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct scu_ipc_data { 3462306a36Sopenharmony_ci u32 count; /* No. of registers */ 3562306a36Sopenharmony_ci u16 addr[5]; /* Register addresses */ 3662306a36Sopenharmony_ci u8 data[5]; /* Register data */ 3762306a36Sopenharmony_ci u8 mask; /* Valid for read-modify-write */ 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/** 4162306a36Sopenharmony_ci * scu_reg_access - implement register access ioctls 4262306a36Sopenharmony_ci * @cmd: command we are doing (read/write/update) 4362306a36Sopenharmony_ci * @data: kernel copy of ioctl data 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * Allow the user to perform register accesses on the SCU via the 4662306a36Sopenharmony_ci * kernel interface 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int scu_reg_access(u32 cmd, struct scu_ipc_data *data) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci unsigned int count = data->count; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (count == 0 || count == 3 || count > 4) 5462306a36Sopenharmony_ci return -EINVAL; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci switch (cmd) { 5762306a36Sopenharmony_ci case INTE_SCU_IPC_REGISTER_READ: 5862306a36Sopenharmony_ci return intel_scu_ipc_dev_readv(scu, data->addr, data->data, count); 5962306a36Sopenharmony_ci case INTE_SCU_IPC_REGISTER_WRITE: 6062306a36Sopenharmony_ci return intel_scu_ipc_dev_writev(scu, data->addr, data->data, count); 6162306a36Sopenharmony_ci case INTE_SCU_IPC_REGISTER_UPDATE: 6262306a36Sopenharmony_ci return intel_scu_ipc_dev_update(scu, data->addr[0], data->data[0], 6362306a36Sopenharmony_ci data->mask); 6462306a36Sopenharmony_ci default: 6562306a36Sopenharmony_ci return -ENOTTY; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/** 7062306a36Sopenharmony_ci * scu_ipc_ioctl - control ioctls for the SCU 7162306a36Sopenharmony_ci * @fp: file handle of the SCU device 7262306a36Sopenharmony_ci * @cmd: ioctl coce 7362306a36Sopenharmony_ci * @arg: pointer to user passed structure 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * Support the I/O and firmware flashing interfaces of the SCU 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic long scu_ipc_ioctl(struct file *fp, unsigned int cmd, 7862306a36Sopenharmony_ci unsigned long arg) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int ret; 8162306a36Sopenharmony_ci struct scu_ipc_data data; 8262306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 8562306a36Sopenharmony_ci return -EPERM; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data))) 8862306a36Sopenharmony_ci return -EFAULT; 8962306a36Sopenharmony_ci ret = scu_reg_access(cmd, &data); 9062306a36Sopenharmony_ci if (ret < 0) 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data))) 9362306a36Sopenharmony_ci return -EFAULT; 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int scu_ipc_open(struct inode *inode, struct file *file) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci int ret = 0; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Only single open at the time */ 10262306a36Sopenharmony_ci mutex_lock(&scu_lock); 10362306a36Sopenharmony_ci if (scu) { 10462306a36Sopenharmony_ci ret = -EBUSY; 10562306a36Sopenharmony_ci goto unlock; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci scu = intel_scu_ipc_dev_get(); 10962306a36Sopenharmony_ci if (!scu) 11062306a36Sopenharmony_ci ret = -ENODEV; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciunlock: 11362306a36Sopenharmony_ci mutex_unlock(&scu_lock); 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int scu_ipc_release(struct inode *inode, struct file *file) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci mutex_lock(&scu_lock); 12062306a36Sopenharmony_ci intel_scu_ipc_dev_put(scu); 12162306a36Sopenharmony_ci scu = NULL; 12262306a36Sopenharmony_ci mutex_unlock(&scu_lock); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct file_operations scu_ipc_fops = { 12862306a36Sopenharmony_ci .unlocked_ioctl = scu_ipc_ioctl, 12962306a36Sopenharmony_ci .open = scu_ipc_open, 13062306a36Sopenharmony_ci .release = scu_ipc_release, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int __init ipc_module_init(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci major = register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); 13662306a36Sopenharmony_ci if (major < 0) 13762306a36Sopenharmony_ci return major; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void __exit ipc_module_exit(void) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci unregister_chrdev(major, "intel_mid_scu"); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cimodule_init(ipc_module_init); 14862306a36Sopenharmony_cimodule_exit(ipc_module_exit); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 15162306a36Sopenharmony_ciMODULE_DESCRIPTION("Utility driver for intel scu ipc"); 15262306a36Sopenharmony_ciMODULE_AUTHOR("Sreedhara <sreedhara.ds@intel.com>"); 153