pti.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Intel(R) Trace Hub PTI output driver
  4. *
  5. * Copyright (C) 2014-2016 Intel Corporation.
  6. */
  7. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  8. #include <linux/types.h>
  9. #include <linux/module.h>
  10. #include <linux/device.h>
  11. #include <linux/sizes.h>
  12. #include <linux/printk.h>
  13. #include <linux/slab.h>
  14. #include <linux/mm.h>
  15. #include <linux/io.h>
  16. #include "intel_th.h"
  17. #include "pti.h"
  18. struct pti_device {
  19. void __iomem *base;
  20. struct intel_th_device *thdev;
  21. unsigned int mode;
  22. unsigned int freeclk;
  23. unsigned int clkdiv;
  24. unsigned int patgen;
  25. unsigned int lpp_dest_mask;
  26. unsigned int lpp_dest;
  27. };
  28. /* map PTI widths to MODE settings of PTI_CTL register */
  29. static const unsigned int pti_mode[] = {
  30. 0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
  31. };
  32. static int pti_width_mode(unsigned int width)
  33. {
  34. int i;
  35. for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
  36. if (pti_mode[i] == width)
  37. return i;
  38. return -EINVAL;
  39. }
  40. static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
  41. char *buf)
  42. {
  43. struct pti_device *pti = dev_get_drvdata(dev);
  44. return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
  45. }
  46. static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
  47. const char *buf, size_t size)
  48. {
  49. struct pti_device *pti = dev_get_drvdata(dev);
  50. unsigned long val;
  51. int ret;
  52. ret = kstrtoul(buf, 10, &val);
  53. if (ret)
  54. return ret;
  55. ret = pti_width_mode(val);
  56. if (ret < 0)
  57. return ret;
  58. pti->mode = ret;
  59. return size;
  60. }
  61. static DEVICE_ATTR_RW(mode);
  62. static ssize_t
  63. freerunning_clock_show(struct device *dev, struct device_attribute *attr,
  64. char *buf)
  65. {
  66. struct pti_device *pti = dev_get_drvdata(dev);
  67. return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
  68. }
  69. static ssize_t
  70. freerunning_clock_store(struct device *dev, struct device_attribute *attr,
  71. const char *buf, size_t size)
  72. {
  73. struct pti_device *pti = dev_get_drvdata(dev);
  74. unsigned long val;
  75. int ret;
  76. ret = kstrtoul(buf, 10, &val);
  77. if (ret)
  78. return ret;
  79. pti->freeclk = !!val;
  80. return size;
  81. }
  82. static DEVICE_ATTR_RW(freerunning_clock);
  83. static ssize_t
  84. clock_divider_show(struct device *dev, struct device_attribute *attr,
  85. char *buf)
  86. {
  87. struct pti_device *pti = dev_get_drvdata(dev);
  88. return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
  89. }
  90. static ssize_t
  91. clock_divider_store(struct device *dev, struct device_attribute *attr,
  92. const char *buf, size_t size)
  93. {
  94. struct pti_device *pti = dev_get_drvdata(dev);
  95. unsigned long val;
  96. int ret;
  97. ret = kstrtoul(buf, 10, &val);
  98. if (ret)
  99. return ret;
  100. if (!is_power_of_2(val) || val > 8 || !val)
  101. return -EINVAL;
  102. pti->clkdiv = val;
  103. return size;
  104. }
  105. static DEVICE_ATTR_RW(clock_divider);
  106. static struct attribute *pti_output_attrs[] = {
  107. &dev_attr_mode.attr,
  108. &dev_attr_freerunning_clock.attr,
  109. &dev_attr_clock_divider.attr,
  110. NULL,
  111. };
  112. static struct attribute_group pti_output_group = {
  113. .attrs = pti_output_attrs,
  114. };
  115. static int intel_th_pti_activate(struct intel_th_device *thdev)
  116. {
  117. struct pti_device *pti = dev_get_drvdata(&thdev->dev);
  118. u32 ctl = PTI_EN;
  119. if (pti->patgen)
  120. ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
  121. if (pti->freeclk)
  122. ctl |= PTI_FCEN;
  123. ctl |= pti->mode << __ffs(PTI_MODE);
  124. ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
  125. ctl |= pti->lpp_dest << __ffs(LPP_DEST);
  126. iowrite32(ctl, pti->base + REG_PTI_CTL);
  127. intel_th_trace_enable(thdev);
  128. return 0;
  129. }
  130. static void intel_th_pti_deactivate(struct intel_th_device *thdev)
  131. {
  132. struct pti_device *pti = dev_get_drvdata(&thdev->dev);
  133. intel_th_trace_disable(thdev);
  134. iowrite32(0, pti->base + REG_PTI_CTL);
  135. }
  136. static void read_hw_config(struct pti_device *pti)
  137. {
  138. u32 ctl = ioread32(pti->base + REG_PTI_CTL);
  139. pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE);
  140. pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
  141. pti->freeclk = !!(ctl & PTI_FCEN);
  142. if (!pti_mode[pti->mode])
  143. pti->mode = pti_width_mode(4);
  144. if (!pti->clkdiv)
  145. pti->clkdiv = 1;
  146. if (pti->thdev->output.type == GTH_LPP) {
  147. if (ctl & LPP_PTIPRESENT)
  148. pti->lpp_dest_mask |= LPP_DEST_PTI;
  149. if (ctl & LPP_BSSBPRESENT)
  150. pti->lpp_dest_mask |= LPP_DEST_EXI;
  151. if (ctl & LPP_DEST)
  152. pti->lpp_dest = 1;
  153. }
  154. }
  155. static int intel_th_pti_probe(struct intel_th_device *thdev)
  156. {
  157. struct device *dev = &thdev->dev;
  158. struct resource *res;
  159. struct pti_device *pti;
  160. void __iomem *base;
  161. res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
  162. if (!res)
  163. return -ENODEV;
  164. base = devm_ioremap(dev, res->start, resource_size(res));
  165. if (!base)
  166. return -ENOMEM;
  167. pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
  168. if (!pti)
  169. return -ENOMEM;
  170. pti->thdev = thdev;
  171. pti->base = base;
  172. read_hw_config(pti);
  173. dev_set_drvdata(dev, pti);
  174. return 0;
  175. }
  176. static void intel_th_pti_remove(struct intel_th_device *thdev)
  177. {
  178. }
  179. static struct intel_th_driver intel_th_pti_driver = {
  180. .probe = intel_th_pti_probe,
  181. .remove = intel_th_pti_remove,
  182. .activate = intel_th_pti_activate,
  183. .deactivate = intel_th_pti_deactivate,
  184. .attr_group = &pti_output_group,
  185. .driver = {
  186. .name = "pti",
  187. .owner = THIS_MODULE,
  188. },
  189. };
  190. static const char * const lpp_dest_str[] = { "pti", "exi" };
  191. static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr,
  192. char *buf)
  193. {
  194. struct pti_device *pti = dev_get_drvdata(dev);
  195. ssize_t ret = 0;
  196. int i;
  197. for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) {
  198. const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s ";
  199. if (!(pti->lpp_dest_mask & BIT(i)))
  200. continue;
  201. ret += scnprintf(buf + ret, PAGE_SIZE - ret,
  202. fmt, lpp_dest_str[i]);
  203. }
  204. if (ret)
  205. buf[ret - 1] = '\n';
  206. return ret;
  207. }
  208. static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr,
  209. const char *buf, size_t size)
  210. {
  211. struct pti_device *pti = dev_get_drvdata(dev);
  212. ssize_t ret = -EINVAL;
  213. int i;
  214. for (i = 0; i < ARRAY_SIZE(lpp_dest_str); i++)
  215. if (sysfs_streq(buf, lpp_dest_str[i]))
  216. break;
  217. if (i < ARRAY_SIZE(lpp_dest_str) && pti->lpp_dest_mask & BIT(i)) {
  218. pti->lpp_dest = i;
  219. ret = size;
  220. }
  221. return ret;
  222. }
  223. static DEVICE_ATTR_RW(lpp_dest);
  224. static struct attribute *lpp_output_attrs[] = {
  225. &dev_attr_mode.attr,
  226. &dev_attr_freerunning_clock.attr,
  227. &dev_attr_clock_divider.attr,
  228. &dev_attr_lpp_dest.attr,
  229. NULL,
  230. };
  231. static struct attribute_group lpp_output_group = {
  232. .attrs = lpp_output_attrs,
  233. };
  234. static struct intel_th_driver intel_th_lpp_driver = {
  235. .probe = intel_th_pti_probe,
  236. .remove = intel_th_pti_remove,
  237. .activate = intel_th_pti_activate,
  238. .deactivate = intel_th_pti_deactivate,
  239. .attr_group = &lpp_output_group,
  240. .driver = {
  241. .name = "lpp",
  242. .owner = THIS_MODULE,
  243. },
  244. };
  245. static int __init intel_th_pti_lpp_init(void)
  246. {
  247. int err;
  248. err = intel_th_driver_register(&intel_th_pti_driver);
  249. if (err)
  250. return err;
  251. err = intel_th_driver_register(&intel_th_lpp_driver);
  252. if (err) {
  253. intel_th_driver_unregister(&intel_th_pti_driver);
  254. return err;
  255. }
  256. return 0;
  257. }
  258. module_init(intel_th_pti_lpp_init);
  259. static void __exit intel_th_pti_lpp_exit(void)
  260. {
  261. intel_th_driver_unregister(&intel_th_pti_driver);
  262. intel_th_driver_unregister(&intel_th_lpp_driver);
  263. }
  264. module_exit(intel_th_pti_lpp_exit);
  265. MODULE_LICENSE("GPL v2");
  266. MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver");
  267. MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");