fsi-scom.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. * SCOM FSI Client device driver
  3. *
  4. * Copyright (C) IBM Corporation 2016
  5. *
  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.
  9. *
  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.
  14. */
  15. #include <linux/fsi.h>
  16. #include <linux/module.h>
  17. #include <linux/cdev.h>
  18. #include <linux/delay.h>
  19. #include <linux/fs.h>
  20. #include <linux/uaccess.h>
  21. #include <linux/slab.h>
  22. #include <linux/miscdevice.h>
  23. #include <linux/list.h>
  24. #include <linux/idr.h>
  25. #define FSI_ENGID_SCOM 0x5
  26. #define SCOM_FSI2PIB_DELAY 50
  27. /* SCOM engine register set */
  28. #define SCOM_DATA0_REG 0x00
  29. #define SCOM_DATA1_REG 0x04
  30. #define SCOM_CMD_REG 0x08
  31. #define SCOM_RESET_REG 0x1C
  32. #define SCOM_RESET_CMD 0x80000000
  33. #define SCOM_WRITE_CMD 0x80000000
  34. struct scom_device {
  35. struct list_head link;
  36. struct fsi_device *fsi_dev;
  37. struct miscdevice mdev;
  38. char name[32];
  39. int idx;
  40. };
  41. #define to_scom_dev(x) container_of((x), struct scom_device, mdev)
  42. static struct list_head scom_devices;
  43. static DEFINE_IDA(scom_ida);
  44. static int put_scom(struct scom_device *scom_dev, uint64_t value,
  45. uint32_t addr)
  46. {
  47. int rc;
  48. uint32_t data;
  49. data = cpu_to_be32((value >> 32) & 0xffffffff);
  50. rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
  51. sizeof(uint32_t));
  52. if (rc)
  53. return rc;
  54. data = cpu_to_be32(value & 0xffffffff);
  55. rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
  56. sizeof(uint32_t));
  57. if (rc)
  58. return rc;
  59. data = cpu_to_be32(SCOM_WRITE_CMD | addr);
  60. return fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
  61. sizeof(uint32_t));
  62. }
  63. static int get_scom(struct scom_device *scom_dev, uint64_t *value,
  64. uint32_t addr)
  65. {
  66. uint32_t result, data;
  67. int rc;
  68. *value = 0ULL;
  69. data = cpu_to_be32(addr);
  70. rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
  71. sizeof(uint32_t));
  72. if (rc)
  73. return rc;
  74. rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &result,
  75. sizeof(uint32_t));
  76. if (rc)
  77. return rc;
  78. *value |= (uint64_t)cpu_to_be32(result) << 32;
  79. rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
  80. sizeof(uint32_t));
  81. if (rc)
  82. return rc;
  83. *value |= cpu_to_be32(result);
  84. return 0;
  85. }
  86. static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
  87. loff_t *offset)
  88. {
  89. int rc;
  90. struct miscdevice *mdev =
  91. (struct miscdevice *)filep->private_data;
  92. struct scom_device *scom = to_scom_dev(mdev);
  93. struct device *dev = &scom->fsi_dev->dev;
  94. uint64_t val;
  95. if (len != sizeof(uint64_t))
  96. return -EINVAL;
  97. rc = get_scom(scom, &val, *offset);
  98. if (rc) {
  99. dev_dbg(dev, "get_scom fail:%d\n", rc);
  100. return rc;
  101. }
  102. rc = copy_to_user(buf, &val, len);
  103. if (rc)
  104. dev_dbg(dev, "copy to user failed:%d\n", rc);
  105. return rc ? rc : len;
  106. }
  107. static ssize_t scom_write(struct file *filep, const char __user *buf,
  108. size_t len, loff_t *offset)
  109. {
  110. int rc;
  111. struct miscdevice *mdev = filep->private_data;
  112. struct scom_device *scom = to_scom_dev(mdev);
  113. struct device *dev = &scom->fsi_dev->dev;
  114. uint64_t val;
  115. if (len != sizeof(uint64_t))
  116. return -EINVAL;
  117. rc = copy_from_user(&val, buf, len);
  118. if (rc) {
  119. dev_dbg(dev, "copy from user failed:%d\n", rc);
  120. return -EINVAL;
  121. }
  122. rc = put_scom(scom, val, *offset);
  123. if (rc) {
  124. dev_dbg(dev, "put_scom failed with:%d\n", rc);
  125. return rc;
  126. }
  127. return len;
  128. }
  129. static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
  130. {
  131. switch (whence) {
  132. case SEEK_CUR:
  133. break;
  134. case SEEK_SET:
  135. file->f_pos = offset;
  136. break;
  137. default:
  138. return -EINVAL;
  139. }
  140. return offset;
  141. }
  142. static const struct file_operations scom_fops = {
  143. .owner = THIS_MODULE,
  144. .llseek = scom_llseek,
  145. .read = scom_read,
  146. .write = scom_write,
  147. };
  148. static int scom_probe(struct device *dev)
  149. {
  150. uint32_t data;
  151. struct fsi_device *fsi_dev = to_fsi_dev(dev);
  152. struct scom_device *scom;
  153. scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
  154. if (!scom)
  155. return -ENOMEM;
  156. scom->idx = ida_simple_get(&scom_ida, 1, INT_MAX, GFP_KERNEL);
  157. snprintf(scom->name, sizeof(scom->name), "scom%d", scom->idx);
  158. scom->fsi_dev = fsi_dev;
  159. scom->mdev.minor = MISC_DYNAMIC_MINOR;
  160. scom->mdev.fops = &scom_fops;
  161. scom->mdev.name = scom->name;
  162. scom->mdev.parent = dev;
  163. list_add(&scom->link, &scom_devices);
  164. data = cpu_to_be32(SCOM_RESET_CMD);
  165. fsi_device_write(fsi_dev, SCOM_RESET_REG, &data, sizeof(uint32_t));
  166. return misc_register(&scom->mdev);
  167. }
  168. static int scom_remove(struct device *dev)
  169. {
  170. struct scom_device *scom, *scom_tmp;
  171. struct fsi_device *fsi_dev = to_fsi_dev(dev);
  172. list_for_each_entry_safe(scom, scom_tmp, &scom_devices, link) {
  173. if (scom->fsi_dev == fsi_dev) {
  174. list_del(&scom->link);
  175. ida_simple_remove(&scom_ida, scom->idx);
  176. misc_deregister(&scom->mdev);
  177. }
  178. }
  179. return 0;
  180. }
  181. static struct fsi_device_id scom_ids[] = {
  182. {
  183. .engine_type = FSI_ENGID_SCOM,
  184. .version = FSI_VERSION_ANY,
  185. },
  186. { 0 }
  187. };
  188. static struct fsi_driver scom_drv = {
  189. .id_table = scom_ids,
  190. .drv = {
  191. .name = "scom",
  192. .bus = &fsi_bus_type,
  193. .probe = scom_probe,
  194. .remove = scom_remove,
  195. }
  196. };
  197. static int scom_init(void)
  198. {
  199. INIT_LIST_HEAD(&scom_devices);
  200. return fsi_driver_register(&scom_drv);
  201. }
  202. static void scom_exit(void)
  203. {
  204. struct list_head *pos;
  205. struct scom_device *scom;
  206. list_for_each(pos, &scom_devices) {
  207. scom = list_entry(pos, struct scom_device, link);
  208. misc_deregister(&scom->mdev);
  209. devm_kfree(&scom->fsi_dev->dev, scom);
  210. }
  211. fsi_driver_unregister(&scom_drv);
  212. }
  213. module_init(scom_init);
  214. module_exit(scom_exit);
  215. MODULE_LICENSE("GPL");