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 #define SCOM_FSI2PIB_DELAY 50
31 /* SCOM engine register set */
32 #define SCOM_DATA0_REG 0x00
33 #define SCOM_DATA1_REG 0x04
34 #define SCOM_CMD_REG 0x08
35 #define SCOM_RESET_REG 0x1C
37 #define SCOM_RESET_CMD 0x80000000
38 #define SCOM_WRITE_CMD 0x80000000
41 struct list_head link
;
42 struct fsi_device
*fsi_dev
;
43 struct miscdevice mdev
;
48 #define to_scom_dev(x) container_of((x), struct scom_device, mdev)
50 static struct list_head scom_devices
;
52 static DEFINE_IDA(scom_ida
);
54 static int put_scom(struct scom_device
*scom_dev
, uint64_t value
,
60 data
= cpu_to_be32(SCOM_RESET_CMD
);
61 rc
= fsi_device_write(scom_dev
->fsi_dev
, SCOM_RESET_REG
, &data
,
66 data
= cpu_to_be32((value
>> 32) & 0xffffffff);
67 rc
= fsi_device_write(scom_dev
->fsi_dev
, SCOM_DATA0_REG
, &data
,
72 data
= cpu_to_be32(value
& 0xffffffff);
73 rc
= fsi_device_write(scom_dev
->fsi_dev
, SCOM_DATA1_REG
, &data
,
78 data
= cpu_to_be32(SCOM_WRITE_CMD
| addr
);
79 return fsi_device_write(scom_dev
->fsi_dev
, SCOM_CMD_REG
, &data
,
83 static int get_scom(struct scom_device
*scom_dev
, uint64_t *value
,
86 uint32_t result
, data
;
90 data
= cpu_to_be32(addr
);
91 rc
= fsi_device_write(scom_dev
->fsi_dev
, SCOM_CMD_REG
, &data
,
96 rc
= fsi_device_read(scom_dev
->fsi_dev
, SCOM_DATA0_REG
, &result
,
101 *value
|= (uint64_t)cpu_to_be32(result
) << 32;
102 rc
= fsi_device_read(scom_dev
->fsi_dev
, SCOM_DATA1_REG
, &result
,
107 *value
|= cpu_to_be32(result
);
112 static ssize_t
scom_read(struct file
*filep
, char __user
*buf
, size_t len
,
116 struct miscdevice
*mdev
=
117 (struct miscdevice
*)filep
->private_data
;
118 struct scom_device
*scom
= to_scom_dev(mdev
);
119 struct device
*dev
= &scom
->fsi_dev
->dev
;
122 if (len
!= sizeof(uint64_t))
125 rc
= get_scom(scom
, &val
, *offset
);
127 dev_dbg(dev
, "get_scom fail:%d\n", rc
);
131 rc
= copy_to_user(buf
, &val
, len
);
133 dev_dbg(dev
, "copy to user failed:%d\n", rc
);
135 return rc
? rc
: len
;
138 static ssize_t
scom_write(struct file
*filep
, const char __user
*buf
,
139 size_t len
, loff_t
*offset
)
142 struct miscdevice
*mdev
= filep
->private_data
;
143 struct scom_device
*scom
= to_scom_dev(mdev
);
144 struct device
*dev
= &scom
->fsi_dev
->dev
;
147 if (len
!= sizeof(uint64_t))
150 rc
= copy_from_user(&val
, buf
, len
);
152 dev_dbg(dev
, "copy from user failed:%d\n", rc
);
156 rc
= put_scom(scom
, val
, *offset
);
158 dev_dbg(dev
, "put_scom failed with:%d\n", rc
);
165 static loff_t
scom_llseek(struct file
*file
, loff_t offset
, int whence
)
171 file
->f_pos
= offset
;
180 static const struct file_operations scom_fops
= {
181 .owner
= THIS_MODULE
,
182 .llseek
= scom_llseek
,
187 static int scom_probe(struct device
*dev
)
189 struct fsi_device
*fsi_dev
= to_fsi_dev(dev
);
190 struct scom_device
*scom
;
192 scom
= devm_kzalloc(dev
, sizeof(*scom
), GFP_KERNEL
);
196 scom
->idx
= ida_simple_get(&scom_ida
, 1, INT_MAX
, GFP_KERNEL
);
197 snprintf(scom
->name
, sizeof(scom
->name
), "scom%d", scom
->idx
);
198 scom
->fsi_dev
= fsi_dev
;
199 scom
->mdev
.minor
= MISC_DYNAMIC_MINOR
;
200 scom
->mdev
.fops
= &scom_fops
;
201 scom
->mdev
.name
= scom
->name
;
202 scom
->mdev
.parent
= dev
;
203 list_add(&scom
->link
, &scom_devices
);
205 return misc_register(&scom
->mdev
);
208 static int scom_remove(struct device
*dev
)
210 struct scom_device
*scom
, *scom_tmp
;
211 struct fsi_device
*fsi_dev
= to_fsi_dev(dev
);
213 list_for_each_entry_safe(scom
, scom_tmp
, &scom_devices
, link
) {
214 if (scom
->fsi_dev
== fsi_dev
) {
215 list_del(&scom
->link
);
216 ida_simple_remove(&scom_ida
, scom
->idx
);
217 misc_deregister(&scom
->mdev
);
224 static struct fsi_device_id scom_ids
[] = {
226 .engine_type
= FSI_ENGID_SCOM
,
227 .version
= FSI_VERSION_ANY
,
232 static struct fsi_driver scom_drv
= {
233 .id_table
= scom_ids
,
236 .bus
= &fsi_bus_type
,
238 .remove
= scom_remove
,
242 static int scom_init(void)
244 INIT_LIST_HEAD(&scom_devices
);
245 return fsi_driver_register(&scom_drv
);
248 static void scom_exit(void)
250 struct list_head
*pos
;
251 struct scom_device
*scom
;
253 list_for_each(pos
, &scom_devices
) {
254 scom
= list_entry(pos
, struct scom_device
, link
);
255 misc_deregister(&scom
->mdev
);
256 devm_kfree(&scom
->fsi_dev
->dev
, scom
);
258 fsi_driver_unregister(&scom_drv
);
261 module_init(scom_init
);
262 module_exit(scom_exit
);
263 MODULE_LICENSE("GPL");