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((value
>> 32) & 0xffffffff);
61 rc
= fsi_device_write(scom_dev
->fsi_dev
, SCOM_DATA0_REG
, &data
,
66 data
= cpu_to_be32(value
& 0xffffffff);
67 rc
= fsi_device_write(scom_dev
->fsi_dev
, SCOM_DATA1_REG
, &data
,
72 data
= cpu_to_be32(SCOM_WRITE_CMD
| addr
);
73 return fsi_device_write(scom_dev
->fsi_dev
, SCOM_CMD_REG
, &data
,
77 static int get_scom(struct scom_device
*scom_dev
, uint64_t *value
,
80 uint32_t result
, data
;
84 data
= cpu_to_be32(addr
);
85 rc
= fsi_device_write(scom_dev
->fsi_dev
, SCOM_CMD_REG
, &data
,
90 rc
= fsi_device_read(scom_dev
->fsi_dev
, SCOM_DATA0_REG
, &result
,
95 *value
|= (uint64_t)cpu_to_be32(result
) << 32;
96 rc
= fsi_device_read(scom_dev
->fsi_dev
, SCOM_DATA1_REG
, &result
,
101 *value
|= cpu_to_be32(result
);
106 static ssize_t
scom_read(struct file
*filep
, char __user
*buf
, size_t len
,
110 struct miscdevice
*mdev
=
111 (struct miscdevice
*)filep
->private_data
;
112 struct scom_device
*scom
= to_scom_dev(mdev
);
113 struct device
*dev
= &scom
->fsi_dev
->dev
;
116 if (len
!= sizeof(uint64_t))
119 rc
= get_scom(scom
, &val
, *offset
);
121 dev_dbg(dev
, "get_scom fail:%d\n", rc
);
125 rc
= copy_to_user(buf
, &val
, len
);
127 dev_dbg(dev
, "copy to user failed:%d\n", rc
);
129 return rc
? rc
: len
;
132 static ssize_t
scom_write(struct file
*filep
, const char __user
*buf
,
133 size_t len
, loff_t
*offset
)
136 struct miscdevice
*mdev
= filep
->private_data
;
137 struct scom_device
*scom
= to_scom_dev(mdev
);
138 struct device
*dev
= &scom
->fsi_dev
->dev
;
141 if (len
!= sizeof(uint64_t))
144 rc
= copy_from_user(&val
, buf
, len
);
146 dev_dbg(dev
, "copy from user failed:%d\n", rc
);
150 rc
= put_scom(scom
, val
, *offset
);
152 dev_dbg(dev
, "put_scom failed with:%d\n", rc
);
159 static loff_t
scom_llseek(struct file
*file
, loff_t offset
, int whence
)
165 file
->f_pos
= offset
;
174 static const struct file_operations scom_fops
= {
175 .owner
= THIS_MODULE
,
176 .llseek
= scom_llseek
,
181 static int scom_probe(struct device
*dev
)
184 struct fsi_device
*fsi_dev
= to_fsi_dev(dev
);
185 struct scom_device
*scom
;
187 scom
= devm_kzalloc(dev
, sizeof(*scom
), GFP_KERNEL
);
191 scom
->idx
= ida_simple_get(&scom_ida
, 1, INT_MAX
, GFP_KERNEL
);
192 snprintf(scom
->name
, sizeof(scom
->name
), "scom%d", scom
->idx
);
193 scom
->fsi_dev
= fsi_dev
;
194 scom
->mdev
.minor
= MISC_DYNAMIC_MINOR
;
195 scom
->mdev
.fops
= &scom_fops
;
196 scom
->mdev
.name
= scom
->name
;
197 scom
->mdev
.parent
= dev
;
198 list_add(&scom
->link
, &scom_devices
);
200 data
= cpu_to_be32(SCOM_RESET_CMD
);
201 fsi_device_write(fsi_dev
, SCOM_RESET_REG
, &data
, sizeof(uint32_t));
203 return misc_register(&scom
->mdev
);
206 static int scom_remove(struct device
*dev
)
208 struct scom_device
*scom
, *scom_tmp
;
209 struct fsi_device
*fsi_dev
= to_fsi_dev(dev
);
211 list_for_each_entry_safe(scom
, scom_tmp
, &scom_devices
, link
) {
212 if (scom
->fsi_dev
== fsi_dev
) {
213 list_del(&scom
->link
);
214 ida_simple_remove(&scom_ida
, scom
->idx
);
215 misc_deregister(&scom
->mdev
);
222 static struct fsi_device_id scom_ids
[] = {
224 .engine_type
= FSI_ENGID_SCOM
,
225 .version
= FSI_VERSION_ANY
,
230 static struct fsi_driver scom_drv
= {
231 .id_table
= scom_ids
,
234 .bus
= &fsi_bus_type
,
236 .remove
= scom_remove
,
240 static int scom_init(void)
242 INIT_LIST_HEAD(&scom_devices
);
243 return fsi_driver_register(&scom_drv
);
246 static void scom_exit(void)
248 struct list_head
*pos
;
249 struct scom_device
*scom
;
251 list_for_each(pos
, &scom_devices
) {
252 scom
= list_entry(pos
, struct scom_device
, link
);
253 misc_deregister(&scom
->mdev
);
254 devm_kfree(&scom
->fsi_dev
->dev
, scom
);
256 fsi_driver_unregister(&scom_drv
);
259 module_init(scom_init
);
260 module_exit(scom_exit
);
261 MODULE_LICENSE("GPL");