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