1*65f0ecb9SAlexander Usyskin // SPDX-License-Identifier: GPL-2.0 2*65f0ecb9SAlexander Usyskin /* Copyright (C) 2023-2026 Intel Corporation */ 3*65f0ecb9SAlexander Usyskin #include <linux/atomic.h> 4*65f0ecb9SAlexander Usyskin #include <linux/cdev.h> 5*65f0ecb9SAlexander Usyskin #include <linux/device.h> 6*65f0ecb9SAlexander Usyskin #include <linux/err.h> 7*65f0ecb9SAlexander Usyskin #include <linux/export.h> 8*65f0ecb9SAlexander Usyskin #include <linux/fs.h> 9*65f0ecb9SAlexander Usyskin #include <linux/issei.h> 10*65f0ecb9SAlexander Usyskin #include <linux/kobject.h> 11*65f0ecb9SAlexander Usyskin #include <linux/list.h> 12*65f0ecb9SAlexander Usyskin #include <linux/module.h> 13*65f0ecb9SAlexander Usyskin #include <linux/mutex.h> 14*65f0ecb9SAlexander Usyskin #include <linux/types.h> 15*65f0ecb9SAlexander Usyskin #include <linux/wait.h> 16*65f0ecb9SAlexander Usyskin #include <linux/xarray.h> 17*65f0ecb9SAlexander Usyskin 18*65f0ecb9SAlexander Usyskin #include "issei_dev.h" 19*65f0ecb9SAlexander Usyskin #include "cdev.h" 20*65f0ecb9SAlexander Usyskin 21*65f0ecb9SAlexander Usyskin struct class *issei_class; 22*65f0ecb9SAlexander Usyskin static dev_t issei_devt; 23*65f0ecb9SAlexander Usyskin 24*65f0ecb9SAlexander Usyskin #define ISSEI_MAX_DEVS MINORMASK 25*65f0ecb9SAlexander Usyskin 26*65f0ecb9SAlexander Usyskin static DEFINE_XARRAY_ALLOC(issei_minor_xa); 27*65f0ecb9SAlexander Usyskin 28*65f0ecb9SAlexander Usyskin static ssize_t fw_ver_show(struct device *device, 29*65f0ecb9SAlexander Usyskin struct device_attribute *attr, char *buf) 30*65f0ecb9SAlexander Usyskin { 31*65f0ecb9SAlexander Usyskin struct issei_device *idev = dev_get_drvdata(device); 32*65f0ecb9SAlexander Usyskin 33*65f0ecb9SAlexander Usyskin return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_version[1], 34*65f0ecb9SAlexander Usyskin idev->fw_version[2], idev->fw_version[3]); 35*65f0ecb9SAlexander Usyskin } 36*65f0ecb9SAlexander Usyskin static DEVICE_ATTR_RO(fw_ver); 37*65f0ecb9SAlexander Usyskin 38*65f0ecb9SAlexander Usyskin static struct attribute *issei_attrs[] = { 39*65f0ecb9SAlexander Usyskin &dev_attr_fw_ver.attr, 40*65f0ecb9SAlexander Usyskin NULL 41*65f0ecb9SAlexander Usyskin }; 42*65f0ecb9SAlexander Usyskin ATTRIBUTE_GROUPS(issei); 43*65f0ecb9SAlexander Usyskin 44*65f0ecb9SAlexander Usyskin static const struct file_operations issei_fops = { 45*65f0ecb9SAlexander Usyskin .owner = THIS_MODULE, 46*65f0ecb9SAlexander Usyskin }; 47*65f0ecb9SAlexander Usyskin 48*65f0ecb9SAlexander Usyskin static void issei_device_release(struct device *dev) 49*65f0ecb9SAlexander Usyskin { 50*65f0ecb9SAlexander Usyskin kfree(dev_get_drvdata(dev)); 51*65f0ecb9SAlexander Usyskin } 52*65f0ecb9SAlexander Usyskin 53*65f0ecb9SAlexander Usyskin static void issei_device_init(struct issei_device *idev, struct device *parent, 54*65f0ecb9SAlexander Usyskin const struct issei_dma_length *dma_length, 55*65f0ecb9SAlexander Usyskin const struct issei_hw_ops *ops) 56*65f0ecb9SAlexander Usyskin { 57*65f0ecb9SAlexander Usyskin idev->parent = parent; 58*65f0ecb9SAlexander Usyskin idev->power_down = false; 59*65f0ecb9SAlexander Usyskin init_waitqueue_head(&idev->wait_has_data); 60*65f0ecb9SAlexander Usyskin idev->has_data = false; 61*65f0ecb9SAlexander Usyskin init_waitqueue_head(&idev->wait_rst_state); 62*65f0ecb9SAlexander Usyskin idev->rst_state = ISSEI_RST_STATE_INIT; 63*65f0ecb9SAlexander Usyskin 64*65f0ecb9SAlexander Usyskin mutex_init(&idev->client_lock); 65*65f0ecb9SAlexander Usyskin INIT_LIST_HEAD(&idev->host_client_list); 66*65f0ecb9SAlexander Usyskin idev->host_client_last_id = 0; 67*65f0ecb9SAlexander Usyskin idev->host_client_count = 0; 68*65f0ecb9SAlexander Usyskin INIT_LIST_HEAD(&idev->fw_client_list); 69*65f0ecb9SAlexander Usyskin INIT_LIST_HEAD(&idev->write_queue); 70*65f0ecb9SAlexander Usyskin idev->last_write_ts = 0; 71*65f0ecb9SAlexander Usyskin 72*65f0ecb9SAlexander Usyskin idev->dma.length = *dma_length; 73*65f0ecb9SAlexander Usyskin 74*65f0ecb9SAlexander Usyskin idev->ops = ops; 75*65f0ecb9SAlexander Usyskin } 76*65f0ecb9SAlexander Usyskin 77*65f0ecb9SAlexander Usyskin /** 78*65f0ecb9SAlexander Usyskin * issei_register: register issei character device 79*65f0ecb9SAlexander Usyskin * @hw_size: size of the hardware structure to allocate 80*65f0ecb9SAlexander Usyskin * @parent: parent device 81*65f0ecb9SAlexander Usyskin * @dma_length: structure with DMA sizes 82*65f0ecb9SAlexander Usyskin * @ops: hardware-related operations 83*65f0ecb9SAlexander Usyskin * 84*65f0ecb9SAlexander Usyskin * Return: pointer allocated to issei_device structure, error on failure 85*65f0ecb9SAlexander Usyskin */ 86*65f0ecb9SAlexander Usyskin struct issei_device *issei_register(size_t hw_size, struct device *parent, 87*65f0ecb9SAlexander Usyskin const struct issei_dma_length *dma_length, 88*65f0ecb9SAlexander Usyskin const struct issei_hw_ops *ops) 89*65f0ecb9SAlexander Usyskin { 90*65f0ecb9SAlexander Usyskin struct issei_device *idev; 91*65f0ecb9SAlexander Usyskin u32 minor; 92*65f0ecb9SAlexander Usyskin int ret, devno; 93*65f0ecb9SAlexander Usyskin 94*65f0ecb9SAlexander Usyskin idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL); 95*65f0ecb9SAlexander Usyskin if (!idev) 96*65f0ecb9SAlexander Usyskin return ERR_PTR(-ENOMEM); 97*65f0ecb9SAlexander Usyskin 98*65f0ecb9SAlexander Usyskin issei_device_init(idev, parent, dma_length, ops); 99*65f0ecb9SAlexander Usyskin 100*65f0ecb9SAlexander Usyskin ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL); 101*65f0ecb9SAlexander Usyskin if (ret < 0) { 102*65f0ecb9SAlexander Usyskin dev_err(&idev->dev, "Failed to allocate minor. ret = %d\n", ret); 103*65f0ecb9SAlexander Usyskin kfree(idev); 104*65f0ecb9SAlexander Usyskin return ERR_PTR(ret); 105*65f0ecb9SAlexander Usyskin } 106*65f0ecb9SAlexander Usyskin 107*65f0ecb9SAlexander Usyskin idev->minor = minor; 108*65f0ecb9SAlexander Usyskin devno = MKDEV(MAJOR(issei_devt), idev->minor); 109*65f0ecb9SAlexander Usyskin 110*65f0ecb9SAlexander Usyskin device_initialize(&idev->dev); 111*65f0ecb9SAlexander Usyskin idev->dev.devt = devno; 112*65f0ecb9SAlexander Usyskin idev->dev.class = issei_class; 113*65f0ecb9SAlexander Usyskin idev->dev.parent = parent; 114*65f0ecb9SAlexander Usyskin idev->dev.groups = issei_groups; 115*65f0ecb9SAlexander Usyskin idev->dev.release = issei_device_release; 116*65f0ecb9SAlexander Usyskin dev_set_drvdata(&idev->dev, idev); 117*65f0ecb9SAlexander Usyskin 118*65f0ecb9SAlexander Usyskin idev->cdev = cdev_alloc(); 119*65f0ecb9SAlexander Usyskin if (!idev->cdev) { 120*65f0ecb9SAlexander Usyskin ret = -ENOMEM; 121*65f0ecb9SAlexander Usyskin goto err; 122*65f0ecb9SAlexander Usyskin } 123*65f0ecb9SAlexander Usyskin idev->cdev->ops = &issei_fops; 124*65f0ecb9SAlexander Usyskin if (parent->driver) 125*65f0ecb9SAlexander Usyskin idev->cdev->owner = parent->driver->owner; 126*65f0ecb9SAlexander Usyskin cdev_set_parent(idev->cdev, &idev->dev.kobj); 127*65f0ecb9SAlexander Usyskin 128*65f0ecb9SAlexander Usyskin ret = cdev_add(idev->cdev, devno, 1); 129*65f0ecb9SAlexander Usyskin if (ret) { 130*65f0ecb9SAlexander Usyskin dev_err(parent, "unable to add device %d:%u ret = %d\n", 131*65f0ecb9SAlexander Usyskin MAJOR(issei_devt), idev->minor, ret); 132*65f0ecb9SAlexander Usyskin goto err_del_cdev; 133*65f0ecb9SAlexander Usyskin } 134*65f0ecb9SAlexander Usyskin 135*65f0ecb9SAlexander Usyskin ret = dev_set_name(&idev->dev, "issei%u", idev->minor); 136*65f0ecb9SAlexander Usyskin if (ret) { 137*65f0ecb9SAlexander Usyskin dev_err(parent, "unable to set name to device %d:%u ret = %d\n", 138*65f0ecb9SAlexander Usyskin MAJOR(issei_devt), idev->minor, ret); 139*65f0ecb9SAlexander Usyskin goto err_del_cdev; 140*65f0ecb9SAlexander Usyskin } 141*65f0ecb9SAlexander Usyskin 142*65f0ecb9SAlexander Usyskin ret = device_add(&idev->dev); 143*65f0ecb9SAlexander Usyskin if (ret) { 144*65f0ecb9SAlexander Usyskin dev_err(parent, "unable to add device %d:%u ret = %d\n", 145*65f0ecb9SAlexander Usyskin MAJOR(issei_devt), idev->minor, ret); 146*65f0ecb9SAlexander Usyskin goto err_del_cdev; 147*65f0ecb9SAlexander Usyskin } 148*65f0ecb9SAlexander Usyskin 149*65f0ecb9SAlexander Usyskin idev->fw_clients = kset_create_and_add("fw_clients", NULL, &idev->dev.kobj); 150*65f0ecb9SAlexander Usyskin if (!idev->fw_clients) { 151*65f0ecb9SAlexander Usyskin ret = -ENOMEM; 152*65f0ecb9SAlexander Usyskin goto err_del_dev; 153*65f0ecb9SAlexander Usyskin } 154*65f0ecb9SAlexander Usyskin 155*65f0ecb9SAlexander Usyskin return idev; 156*65f0ecb9SAlexander Usyskin 157*65f0ecb9SAlexander Usyskin err_del_dev: 158*65f0ecb9SAlexander Usyskin device_del(&idev->dev); 159*65f0ecb9SAlexander Usyskin err_del_cdev: 160*65f0ecb9SAlexander Usyskin cdev_del(idev->cdev); 161*65f0ecb9SAlexander Usyskin err: 162*65f0ecb9SAlexander Usyskin put_device(&idev->dev); 163*65f0ecb9SAlexander Usyskin xa_erase(&issei_minor_xa, minor); 164*65f0ecb9SAlexander Usyskin 165*65f0ecb9SAlexander Usyskin return ERR_PTR(ret); 166*65f0ecb9SAlexander Usyskin } 167*65f0ecb9SAlexander Usyskin EXPORT_SYMBOL_GPL(issei_register); 168*65f0ecb9SAlexander Usyskin 169*65f0ecb9SAlexander Usyskin /** 170*65f0ecb9SAlexander Usyskin * issei_deregister: remove issei character device 171*65f0ecb9SAlexander Usyskin * @idev: the device structure 172*65f0ecb9SAlexander Usyskin */ 173*65f0ecb9SAlexander Usyskin void issei_deregister(struct issei_device *idev) 174*65f0ecb9SAlexander Usyskin { 175*65f0ecb9SAlexander Usyskin u32 minor = idev->minor; 176*65f0ecb9SAlexander Usyskin 177*65f0ecb9SAlexander Usyskin cdev_del(idev->cdev); 178*65f0ecb9SAlexander Usyskin 179*65f0ecb9SAlexander Usyskin kset_unregister(idev->fw_clients); 180*65f0ecb9SAlexander Usyskin 181*65f0ecb9SAlexander Usyskin device_del(&idev->dev); 182*65f0ecb9SAlexander Usyskin 183*65f0ecb9SAlexander Usyskin put_device(&idev->dev); 184*65f0ecb9SAlexander Usyskin 185*65f0ecb9SAlexander Usyskin xa_erase(&issei_minor_xa, minor); 186*65f0ecb9SAlexander Usyskin } 187*65f0ecb9SAlexander Usyskin EXPORT_SYMBOL_GPL(issei_deregister); 188*65f0ecb9SAlexander Usyskin 189*65f0ecb9SAlexander Usyskin static int __init issei_cdev_init(void) 190*65f0ecb9SAlexander Usyskin { 191*65f0ecb9SAlexander Usyskin int ret; 192*65f0ecb9SAlexander Usyskin 193*65f0ecb9SAlexander Usyskin issei_class = class_create("issei"); 194*65f0ecb9SAlexander Usyskin if (IS_ERR(issei_class)) { 195*65f0ecb9SAlexander Usyskin pr_err("couldn't create class\n"); 196*65f0ecb9SAlexander Usyskin return PTR_ERR(issei_class); 197*65f0ecb9SAlexander Usyskin } 198*65f0ecb9SAlexander Usyskin 199*65f0ecb9SAlexander Usyskin ret = alloc_chrdev_region(&issei_devt, 0, ISSEI_MAX_DEVS, "issei"); 200*65f0ecb9SAlexander Usyskin if (ret < 0) { 201*65f0ecb9SAlexander Usyskin pr_err("unable to allocate char dev region\n"); 202*65f0ecb9SAlexander Usyskin class_destroy(issei_class); 203*65f0ecb9SAlexander Usyskin return ret; 204*65f0ecb9SAlexander Usyskin } 205*65f0ecb9SAlexander Usyskin 206*65f0ecb9SAlexander Usyskin return 0; 207*65f0ecb9SAlexander Usyskin } 208*65f0ecb9SAlexander Usyskin 209*65f0ecb9SAlexander Usyskin static void __exit issei_cdev_exit(void) 210*65f0ecb9SAlexander Usyskin { 211*65f0ecb9SAlexander Usyskin unregister_chrdev_region(issei_devt, ISSEI_MAX_DEVS); 212*65f0ecb9SAlexander Usyskin class_destroy(issei_class); 213*65f0ecb9SAlexander Usyskin } 214*65f0ecb9SAlexander Usyskin 215*65f0ecb9SAlexander Usyskin module_init(issei_cdev_init); 216*65f0ecb9SAlexander Usyskin module_exit(issei_cdev_exit); 217*65f0ecb9SAlexander Usyskin 218*65f0ecb9SAlexander Usyskin MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface"); 219*65f0ecb9SAlexander Usyskin MODULE_LICENSE("GPL"); 220