vha_plat_dt.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*!
  2. *****************************************************************************
  3. *
  4. * @File vha_plat_dt.c
  5. * ---------------------------------------------------------------------------
  6. *
  7. * Copyright (c) Imagination Technologies Ltd.
  8. *
  9. * The contents of this file are subject to the MIT license as set out below.
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a
  12. * copy of this software and associated documentation files (the "Software"),
  13. * to deal in the Software without restriction, including without limitation
  14. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  15. * and/or sell copies of the Software, and to permit persons to whom the
  16. * Software is furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * Alternatively, the contents of this file may be used under the terms of the
  30. * GNU General Public License Version 2 ("GPL")in which case the provisions of
  31. * GPL are applicable instead of those above.
  32. *
  33. * If you wish to allow use of your version of this file only under the terms
  34. * of GPL, and not to allow others to use your version of this file under the
  35. * terms of the MIT license, indicate your decision by deleting the provisions
  36. * above and replace them with the notice and other provisions required by GPL
  37. * as set out in the file called "GPLHEADER" included in this distribution. If
  38. * you do not delete the provisions above, a recipient may use your version of
  39. * this file under the terms of either the MIT license or GPL.
  40. *
  41. * This License is also included in this distribution in the file called
  42. * "MIT_COPYING".
  43. *
  44. *****************************************************************************/
  45. #include <linux/interrupt.h>
  46. #include <linux/device.h>
  47. #include <linux/module.h>
  48. #include <linux/platform_device.h>
  49. #include <linux/of_address.h>
  50. #include <linux/of_irq.h>
  51. #include <linux/io.h>
  52. #include <linux/pm.h>
  53. #include <linux/version.h>
  54. #include <img_mem_man.h>
  55. #include "vha_common.h"
  56. #include "uapi/version.h"
  57. #include "vha_plat.h"
  58. #include "vha_plat_dt.h"
  59. #if defined(CFG_SYS_VAGUS)
  60. #include <hwdefs/vagus_system.h>
  61. #elif defined(CFG_SYS_AURA)
  62. #include <hwdefs/aura_system.h>
  63. #elif defined(CFG_SYS_MIRAGE)
  64. #include <hwdefs/mirage_system.h>
  65. #endif
  66. #define DEVICE_NAME "vha"
  67. /* Number of core cycles used to measure the core clock frequency */
  68. #define FREQ_MEASURE_CYCLES 0xfffffff
  69. static bool poll_interrupts; /* Disabled by default */
  70. module_param(poll_interrupts, bool, 0444);
  71. MODULE_PARM_DESC(poll_interrupts, "Poll for interrupts? 0: No, 1: Yes");
  72. static unsigned int irq_poll_interval_ms = 100; /* 100 ms */
  73. module_param(irq_poll_interval_ms, uint, 0444);
  74. MODULE_PARM_DESC(irq_poll_interval_ms, "Time in ms between each interrupt poll");
  75. /* Global timer used when irq poll mode is switched on.
  76. * NOTE: only single core instance is supported in polling mode */
  77. static struct poll_timer {
  78. struct platform_device *pdev;
  79. struct timer_list tmr;
  80. bool enabled;
  81. } irq_poll_timer;
  82. static irqreturn_t dt_plat_thread_irq(int irq, void *dev_id)
  83. {
  84. struct platform_device *ofdev = (struct platform_device *)dev_id;
  85. return vha_handle_thread_irq(&ofdev->dev);
  86. }
  87. static irqreturn_t dt_plat_isrcb(int irq, void *dev_id)
  88. {
  89. struct platform_device *ofdev = (struct platform_device *)dev_id;
  90. if (!ofdev)
  91. return IRQ_NONE;
  92. return vha_handle_irq(&ofdev->dev);
  93. }
  94. /* Interrupt polling function */
  95. #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
  96. static void dt_plat_poll_interrupt(struct timer_list *t)
  97. {
  98. struct poll_timer *poll_timer = from_timer(poll_timer, t, tmr);
  99. #else
  100. static void dt_plat_poll_interrupt(unsigned long ctx)
  101. {
  102. struct poll_timer *poll_timer = (struct poll_timer *)ctx;
  103. #endif
  104. struct platform_device *ofdev = poll_timer->pdev;
  105. int ret;
  106. if (!poll_timer->enabled)
  107. return;
  108. preempt_disable();
  109. ret = vha_handle_irq(&ofdev->dev);
  110. preempt_enable();
  111. if (ret == IRQ_WAKE_THREAD)
  112. vha_handle_thread_irq(&ofdev->dev);
  113. /* retrigger */
  114. mod_timer(&poll_timer->tmr,
  115. jiffies + msecs_to_jiffies(irq_poll_interval_ms));
  116. }
  117. static int vha_plat_probe(struct platform_device *ofdev)
  118. {
  119. int ret, module_irq;
  120. struct resource res;
  121. void __iomem *reg_addr;
  122. uint32_t reg_size, core_size;
  123. ret = of_address_to_resource(ofdev->dev.of_node, 0, &res);
  124. if (ret) {
  125. dev_err(&ofdev->dev, "missing 'reg' property in device tree\n");
  126. return ret;
  127. }
  128. pr_info("%s: registers %#llx-%#llx\n", __func__,
  129. (unsigned long long)res.start, (unsigned long long)res.end);
  130. module_irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
  131. if (module_irq == 0) {
  132. dev_err(&ofdev->dev, "could not map IRQ\n");
  133. return -ENXIO;
  134. }
  135. /* Assuming DT holds a single registers space entry that covers all regions,
  136. * So we need to do the split accordingly */
  137. reg_size = res.end - res.start + 1;
  138. #ifdef CFG_SYS_VAGUS
  139. core_size = _REG_SIZE + _REG_NNSYS_SIZE;
  140. #else
  141. core_size = _REG_SIZE;
  142. #endif
  143. if ((res.start + _REG_START) > res.end) {
  144. dev_err(&ofdev->dev, "wrong system conf for core region!\n");
  145. return -ENXIO;
  146. }
  147. if ((res.start + _REG_START + core_size) > res.end) {
  148. dev_warn(&ofdev->dev, "trimming system conf for core region!\n");
  149. core_size = reg_size - _REG_START;
  150. }
  151. #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
  152. reg_addr = devm_ioremap_nocache(&ofdev->dev, res.start +
  153. _REG_START, core_size);
  154. #else
  155. reg_addr = devm_ioremap(&ofdev->dev, res.start +
  156. _REG_START, core_size);
  157. #endif
  158. if (!reg_addr) {
  159. dev_err(&ofdev->dev, "failed to map core registers\n");
  160. return -ENXIO;
  161. }
  162. ret = vha_plat_dt_hw_init(ofdev);
  163. if (ret) {
  164. dev_err(&ofdev->dev, "failed to init platform-specific hw!\n");
  165. goto out_add_dev;
  166. }
  167. /* no 'per device' memory heaps used */
  168. ret = vha_add_dev(&ofdev->dev, NULL, 0,
  169. NULL /* plat priv data */, reg_addr, core_size);
  170. if (ret) {
  171. dev_err(&ofdev->dev, "failed to intialize driver core!\n");
  172. goto out_add_dev;
  173. }
  174. if (!poll_interrupts) {
  175. ret = devm_request_threaded_irq(&ofdev->dev, module_irq, &dt_plat_isrcb,
  176. &dt_plat_thread_irq, IRQF_SHARED, DEVICE_NAME, ofdev);
  177. if (ret) {
  178. dev_err(&ofdev->dev, "failed to request irq\n");
  179. goto out_irq;
  180. }
  181. } else {
  182. irq_poll_timer.pdev = ofdev;
  183. irq_poll_timer.enabled = true;
  184. /* Setup and start poll timer */
  185. #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
  186. timer_setup(&irq_poll_timer.tmr, dt_plat_poll_interrupt, 0);
  187. #else
  188. setup_timer(&irq_poll_timer.tmr, dt_plat_poll_interrupt,
  189. (uintptr_t)&irq_poll_timer);
  190. #endif
  191. mod_timer(&irq_poll_timer.tmr,
  192. jiffies + msecs_to_jiffies(irq_poll_interval_ms));
  193. }
  194. /* Try to calibrate the core if needed */
  195. ret = vha_dev_calibrate(&ofdev->dev, FREQ_MEASURE_CYCLES);
  196. if (ret) {
  197. dev_err(&ofdev->dev, "%s: Failed to start clock calibration!\n", __func__);
  198. goto out_irq;
  199. }
  200. return ret;
  201. out_irq:
  202. vha_rm_dev(&ofdev->dev);
  203. out_add_dev:
  204. devm_iounmap(&ofdev->dev, reg_addr);
  205. return ret;
  206. }
  207. static int vha_plat_remove(struct platform_device *ofdev)
  208. {
  209. vha_rm_dev(&ofdev->dev);
  210. vha_plat_dt_hw_destroy(ofdev);
  211. return 0;
  212. }
  213. #ifdef CONFIG_PM
  214. static int vha_plat_suspend(struct device *dev)
  215. {
  216. struct platform_device *ofdev =
  217. container_of(dev, struct platform_device, dev);
  218. int ret = 0;
  219. ret = vha_suspend_dev(dev);
  220. if (ret)
  221. dev_err(dev, "failed to suspend the core!\n");
  222. else {
  223. ret = vha_plat_dt_hw_suspend(ofdev);
  224. if (ret)
  225. dev_err(dev, "failed to suspend platform-specific hw!\n");
  226. }
  227. return ret;
  228. }
  229. static int vha_plat_resume(struct device *dev)
  230. {
  231. struct platform_device *ofdev =
  232. container_of(dev, struct platform_device, dev);
  233. int ret = 0;
  234. ret = vha_plat_dt_hw_resume(ofdev);
  235. if (ret)
  236. dev_err(dev, "failed to resume platform-specific hw!\n");
  237. else {
  238. ret = vha_resume_dev(dev);
  239. if (ret)
  240. dev_err(dev, "failed to resume the core!\n");
  241. }
  242. return ret;
  243. }
  244. static int vha_plat_runtime_idle(struct device *dev)
  245. {
  246. /* Eg. turn off external clocks */
  247. return 0;
  248. }
  249. static int vha_plat_runtime_suspend(struct device *dev)
  250. {
  251. /* Nothing to do */
  252. return 0;
  253. }
  254. static int vha_plat_runtime_resume(struct device *dev)
  255. {
  256. /* Eg. turn on external clocks */
  257. return 0;
  258. }
  259. #endif
  260. static struct dev_pm_ops vha_pm_plat_ops = {
  261. SET_RUNTIME_PM_OPS(vha_plat_runtime_suspend,
  262. vha_plat_runtime_resume, vha_plat_runtime_idle)
  263. SET_SYSTEM_SLEEP_PM_OPS(vha_plat_suspend, vha_plat_resume)
  264. };
  265. static ssize_t info_show(struct device_driver *drv, char *buf)
  266. {
  267. return sprintf(buf, "VHA DT driver version : " VERSION_STRING "\n");
  268. }
  269. static DRIVER_ATTR_RO(info);
  270. static struct attribute *drv_attrs[] = {
  271. &driver_attr_info.attr,
  272. NULL
  273. };
  274. ATTRIBUTE_GROUPS(drv);
  275. static struct platform_driver vha_plat_drv = {
  276. .probe = vha_plat_probe,
  277. .remove = vha_plat_remove,
  278. .driver = {
  279. .name = VHA_PLAT_DT_NAME,
  280. .groups = drv_groups,
  281. .owner = THIS_MODULE,
  282. .of_match_table = vha_plat_dt_of_ids,
  283. .pm = &vha_pm_plat_ops,
  284. },
  285. };
  286. int vha_plat_init(void)
  287. {
  288. int ret = 0;
  289. struct heap_config *heap_configs;
  290. int num_heaps;
  291. vha_plat_dt_get_heaps(&heap_configs, &num_heaps);
  292. ret = vha_init_plat_heaps(heap_configs, num_heaps);
  293. if (ret) {
  294. pr_err("failed to initialize global heaps\n");
  295. return -ENOMEM;
  296. }
  297. ret = platform_driver_register(&vha_plat_drv);
  298. if (ret) {
  299. pr_err("failed to register VHA driver!\n");
  300. return ret;
  301. }
  302. return 0;
  303. }
  304. int vha_plat_deinit(void)
  305. {
  306. int ret;
  307. if (poll_interrupts) {
  308. irq_poll_timer.enabled = false;
  309. del_timer_sync(&irq_poll_timer.tmr);
  310. }
  311. /* Unregister the driver from the OS */
  312. platform_driver_unregister(&vha_plat_drv);
  313. ret = vha_deinit();
  314. if (ret)
  315. pr_err("VHA driver deinit failed\n");
  316. return ret;
  317. }
  318. /*
  319. * coding style for emacs
  320. *
  321. * Local variables:
  322. * indent-tabs-mode: t
  323. * tab-width: 8
  324. * c-basic-offset: 8
  325. * End:
  326. */