155 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (C) IBM Corporation 2023 */3 4#include <linux/cdev.h>5#include <linux/device.h>6#include <linux/fs.h>7#include <linux/fsi.h>8#include <linux/module.h>9#include <linux/mod_devicetable.h>10 11#include "fsi-master-i2cr.h"12#include "fsi-slave.h"13 14struct i2cr_scom {15 struct device dev;16 struct cdev cdev;17 struct fsi_master_i2cr *i2cr;18};19 20static loff_t i2cr_scom_llseek(struct file *file, loff_t offset, int whence)21{22 switch (whence) {23 case SEEK_CUR:24 break;25 case SEEK_SET:26 file->f_pos = offset;27 break;28 default:29 return -EINVAL;30 }31 32 return offset;33}34 35static ssize_t i2cr_scom_read(struct file *filep, char __user *buf, size_t len, loff_t *offset)36{37 struct i2cr_scom *scom = filep->private_data;38 u64 data;39 int ret;40 41 if (len != sizeof(data))42 return -EINVAL;43 44 ret = fsi_master_i2cr_read(scom->i2cr, (u32)*offset, &data);45 if (ret)46 return ret;47 48 ret = copy_to_user(buf, &data, len);49 if (ret)50 return ret;51 52 return len;53}54 55static ssize_t i2cr_scom_write(struct file *filep, const char __user *buf, size_t len,56 loff_t *offset)57{58 struct i2cr_scom *scom = filep->private_data;59 u64 data;60 int ret;61 62 if (len != sizeof(data))63 return -EINVAL;64 65 ret = copy_from_user(&data, buf, len);66 if (ret)67 return ret;68 69 ret = fsi_master_i2cr_write(scom->i2cr, (u32)*offset, data);70 if (ret)71 return ret;72 73 return len;74}75 76static const struct file_operations i2cr_scom_fops = {77 .owner = THIS_MODULE,78 .open = simple_open,79 .llseek = i2cr_scom_llseek,80 .read = i2cr_scom_read,81 .write = i2cr_scom_write,82};83 84static int i2cr_scom_probe(struct device *dev)85{86 struct fsi_device *fsi_dev = to_fsi_dev(dev);87 struct i2cr_scom *scom;88 int didx;89 int ret;90 91 if (!is_fsi_master_i2cr(fsi_dev->slave->master))92 return -ENODEV;93 94 scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);95 if (!scom)96 return -ENOMEM;97 98 scom->i2cr = to_fsi_master_i2cr(fsi_dev->slave->master);99 dev_set_drvdata(dev, scom);100 101 scom->dev.type = &fsi_cdev_type;102 scom->dev.parent = dev;103 device_initialize(&scom->dev);104 105 ret = fsi_get_new_minor(fsi_dev, fsi_dev_scom, &scom->dev.devt, &didx);106 if (ret)107 return ret;108 109 dev_set_name(&scom->dev, "scom%d", didx);110 cdev_init(&scom->cdev, &i2cr_scom_fops);111 ret = cdev_device_add(&scom->cdev, &scom->dev);112 if (ret)113 fsi_free_minor(scom->dev.devt);114 115 return ret;116}117 118static int i2cr_scom_remove(struct device *dev)119{120 struct i2cr_scom *scom = dev_get_drvdata(dev);121 122 cdev_device_del(&scom->cdev, &scom->dev);123 fsi_free_minor(scom->dev.devt);124 125 return 0;126}127 128static const struct of_device_id i2cr_scom_of_ids[] = {129 { .compatible = "ibm,i2cr-scom" },130 { }131};132MODULE_DEVICE_TABLE(of, i2cr_scom_of_ids);133 134static const struct fsi_device_id i2cr_scom_ids[] = {135 { 0x5, FSI_VERSION_ANY },136 { }137};138 139static struct fsi_driver i2cr_scom_driver = {140 .id_table = i2cr_scom_ids,141 .drv = {142 .name = "i2cr_scom",143 .bus = &fsi_bus_type,144 .of_match_table = i2cr_scom_of_ids,145 .probe = i2cr_scom_probe,146 .remove = i2cr_scom_remove,147 }148};149 150module_fsi_driver(i2cr_scom_driver);151 152MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");153MODULE_DESCRIPTION("IBM I2C Responder SCOM driver");154MODULE_LICENSE("GPL");155