2 * SCOM FSI Client device driver
4 * Copyright (C) IBM Corporation 2016
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 #include <linux/fsi.h>
17 #include <linux/module.h>
18 #include <linux/cdev.h>
19 #include <linux/delay.h>
21 #include <linux/uaccess.h>
22 #include <linux/slab.h>
23 #include <linux/miscdevice.h>
24 #include <linux/list.h>
25 #include <linux/idr.h>
27 #define FSI_ENGID_SCOM 0x5
29 /* SCOM engine register set */
30 #define SCOM_DATA0_REG 0x00
31 #define SCOM_DATA1_REG 0x04
32 #define SCOM_CMD_REG 0x08
33 #define SCOM_FSI2PIB_RESET_REG 0x18
34 #define SCOM_STATUS_REG 0x1C /* Read */
35 #define SCOM_PIB_RESET_REG 0x1C /* Write */
37 /* Command register */
38 #define SCOM_WRITE_CMD 0x80000000
39 #define SCOM_READ_CMD 0x00000000
41 /* Status register bits */
42 #define SCOM_STATUS_ERR_SUMMARY 0x80000000
43 #define SCOM_STATUS_PROTECTION 0x01000000
44 #define SCOM_STATUS_PIB_ABORT 0x00100000
45 #define SCOM_STATUS_PIB_RESP_MASK 0x00007000
46 #define SCOM_STATUS_PIB_RESP_SHIFT 12
48 #define SCOM_STATUS_ANY_ERR (SCOM_STATUS_ERR_SUMMARY | \
49 SCOM_STATUS_PROTECTION | \
50 SCOM_STATUS_PIB_ABORT | \
51 SCOM_STATUS_PIB_RESP_MASK)
54 struct list_head link;
55 struct fsi_device *fsi_dev;
56 struct miscdevice mdev;
62 #define to_scom_dev(x) container_of((x), struct scom_device, mdev)
64 static struct list_head scom_devices;
66 static DEFINE_IDA(scom_ida);
68 static int put_scom(struct scom_device *scom_dev, uint64_t value,
74 mutex_lock(&scom_dev->lock);
76 data = cpu_to_be32((value >> 32) & 0xffffffff);
77 rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
82 data = cpu_to_be32(value & 0xffffffff);
83 rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
88 data = cpu_to_be32(SCOM_WRITE_CMD | addr);
89 rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
92 mutex_unlock(&scom_dev->lock);
96 static int get_scom(struct scom_device *scom_dev, uint64_t *value,
103 mutex_lock(&scom_dev->lock);
105 data = cpu_to_be32(SCOM_READ_CMD | addr);
106 rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
111 rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &result,
116 *value |= (uint64_t)be32_to_cpu(result) << 32;
117 rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
122 *value |= be32_to_cpu(result);
124 mutex_unlock(&scom_dev->lock);
128 static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
132 struct miscdevice *mdev =
133 (struct miscdevice *)filep->private_data;
134 struct scom_device *scom = to_scom_dev(mdev);
135 struct device *dev = &scom->fsi_dev->dev;
138 if (len != sizeof(uint64_t))
141 rc = get_scom(scom, &val, *offset);
143 dev_dbg(dev, "get_scom fail:%d\n", rc);
147 rc = copy_to_user(buf, &val, len);
149 dev_dbg(dev, "copy to user failed:%d\n", rc);
151 return rc ? rc : len;
154 static ssize_t scom_write(struct file *filep, const char __user *buf,
155 size_t len, loff_t *offset)
158 struct miscdevice *mdev = filep->private_data;
159 struct scom_device *scom = to_scom_dev(mdev);
160 struct device *dev = &scom->fsi_dev->dev;
163 if (len != sizeof(uint64_t))
166 rc = copy_from_user(&val, buf, len);
168 dev_dbg(dev, "copy from user failed:%d\n", rc);
172 rc = put_scom(scom, val, *offset);
174 dev_dbg(dev, "put_scom failed with:%d\n", rc);
181 static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
187 file->f_pos = offset;
196 static const struct file_operations scom_fops = {
197 .owner = THIS_MODULE,
198 .llseek = scom_llseek,
203 static int scom_probe(struct device *dev)
205 struct fsi_device *fsi_dev = to_fsi_dev(dev);
206 struct scom_device *scom;
208 scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
212 mutex_init(&scom->lock);
213 scom->idx = ida_simple_get(&scom_ida, 1, INT_MAX, GFP_KERNEL);
214 snprintf(scom->name, sizeof(scom->name), "scom%d", scom->idx);
215 scom->fsi_dev = fsi_dev;
216 scom->mdev.minor = MISC_DYNAMIC_MINOR;
217 scom->mdev.fops = &scom_fops;
218 scom->mdev.name = scom->name;
219 scom->mdev.parent = dev;
220 list_add(&scom->link, &scom_devices);
222 return misc_register(&scom->mdev);
225 static int scom_remove(struct device *dev)
227 struct scom_device *scom, *scom_tmp;
228 struct fsi_device *fsi_dev = to_fsi_dev(dev);
230 list_for_each_entry_safe(scom, scom_tmp, &scom_devices, link) {
231 if (scom->fsi_dev == fsi_dev) {
232 list_del(&scom->link);
233 ida_simple_remove(&scom_ida, scom->idx);
234 misc_deregister(&scom->mdev);
241 static struct fsi_device_id scom_ids[] = {
243 .engine_type = FSI_ENGID_SCOM,
244 .version = FSI_VERSION_ANY,
249 static struct fsi_driver scom_drv = {
250 .id_table = scom_ids,
253 .bus = &fsi_bus_type,
255 .remove = scom_remove,
259 static int scom_init(void)
261 INIT_LIST_HEAD(&scom_devices);
262 return fsi_driver_register(&scom_drv);
265 static void scom_exit(void)
267 struct list_head *pos;
268 struct scom_device *scom;
270 list_for_each(pos, &scom_devices) {
271 scom = list_entry(pos, struct scom_device, link);
272 misc_deregister(&scom->mdev);
273 devm_kfree(&scom->fsi_dev->dev, scom);
275 fsi_driver_unregister(&scom_drv);
278 module_init(scom_init);
279 module_exit(scom_exit);
280 MODULE_LICENSE("GPL");