sysmmu.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /* linux/arch/arm/plat-s5p/sysmmu.c
  2. *
  3. * Copyright (c) 2010 Samsung Electronics Co., Ltd.
  4. * http://www.samsung.com
  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. #include <linux/io.h>
  11. #include <linux/interrupt.h>
  12. #include <linux/platform_device.h>
  13. #include <mach/map.h>
  14. #include <mach/regs-sysmmu.h>
  15. #include <mach/sysmmu.h>
  16. #include <plat/sysmmu.h>
  17. struct sysmmu_controller s5p_sysmmu_cntlrs[S5P_SYSMMU_TOTAL_IPNUM];
  18. void s5p_sysmmu_register(struct sysmmu_controller *sysmmuconp)
  19. {
  20. unsigned int reg_mmu_ctrl;
  21. unsigned int reg_mmu_status;
  22. unsigned int reg_pt_base_addr;
  23. unsigned int reg_int_status;
  24. unsigned int reg_page_ft_addr;
  25. reg_int_status = __raw_readl(sysmmuconp->regs + S5P_INT_STATUS);
  26. reg_mmu_ctrl = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
  27. reg_mmu_status = __raw_readl(sysmmuconp->regs + S5P_MMU_STATUS);
  28. reg_pt_base_addr = __raw_readl(sysmmuconp->regs + S5P_PT_BASE_ADDR);
  29. reg_page_ft_addr = __raw_readl(sysmmuconp->regs + S5P_PAGE_FAULT_ADDR);
  30. printk(KERN_INFO "%s: ips:%s\n", __func__, sysmmuconp->name);
  31. printk(KERN_INFO "%s: MMU_CTRL:0x%X, ", __func__, reg_mmu_ctrl);
  32. printk(KERN_INFO "MMU_STATUS:0x%X, PT_BASE_ADDR:0x%X\n", reg_mmu_status, reg_pt_base_addr);
  33. printk(KERN_INFO "%s: INT_STATUS:0x%X, PAGE_FAULT_ADDR:0x%X\n", __func__, reg_int_status, reg_page_ft_addr);
  34. switch (reg_int_status & 0xFF) {
  35. case 0x1:
  36. printk(KERN_INFO "%s: Page fault\n", __func__);
  37. printk(KERN_INFO "%s: Virtual address causing last page fault or bus error : 0x%x\n", __func__ , reg_page_ft_addr);
  38. break;
  39. case 0x2:
  40. printk(KERN_INFO "%s: AR multi-hit fault\n", __func__);
  41. break;
  42. case 0x4:
  43. printk(KERN_INFO "%s: AW multi-hit fault\n", __func__);
  44. break;
  45. case 0x8:
  46. printk(KERN_INFO "%s: Bus error\n", __func__);
  47. break;
  48. case 0x10:
  49. printk(KERN_INFO "%s: AR Security protection fault\n", __func__);
  50. break;
  51. case 0x20:
  52. printk(KERN_INFO "%s: AR Access protection fault\n", __func__);
  53. break;
  54. case 0x40:
  55. printk(KERN_INFO "%s: AW Security protection fault\n", __func__);
  56. break;
  57. case 0x80:
  58. printk(KERN_INFO "%s: AW Access protection fault\n", __func__);
  59. break;
  60. }
  61. }
  62. static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id)
  63. {
  64. unsigned int i;
  65. unsigned int reg_int_status;
  66. struct sysmmu_controller *sysmmuconp;
  67. for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) {
  68. sysmmuconp = &s5p_sysmmu_cntlrs[i];
  69. if (sysmmuconp->enable == true) {
  70. reg_int_status = __raw_readl(sysmmuconp->regs + S5P_INT_STATUS);
  71. if (reg_int_status & 0xFF)
  72. s5p_sysmmu_register(sysmmuconp);
  73. }
  74. }
  75. return IRQ_HANDLED;
  76. }
  77. int s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd)
  78. {
  79. struct sysmmu_controller *sysmmuconp = NULL;
  80. sysmmuconp = &s5p_sysmmu_cntlrs[ips];
  81. if (sysmmuconp == NULL) {
  82. printk(KERN_ERR "failed to get ip's sysmmu info\n");
  83. return 1;
  84. }
  85. /* Set sysmmu page table base address */
  86. __raw_writel(pgd, sysmmuconp->regs + S5P_PT_BASE_ADDR);
  87. if (s5p_sysmmu_tlb_invalidate(ips) != 0)
  88. printk(KERN_ERR "failed s5p_sysmmu_tlb_invalidate\n");
  89. return 0;
  90. }
  91. static int s5p_sysmmu_set_tablebase(sysmmu_ips ips)
  92. {
  93. unsigned int pg;
  94. struct sysmmu_controller *sysmmuconp;
  95. sysmmuconp = &s5p_sysmmu_cntlrs[ips];
  96. if (sysmmuconp == NULL) {
  97. printk(KERN_ERR "failed to get ip's sysmmu info\n");
  98. return 1;
  99. }
  100. __asm__("mrc p15, 0, %0, c2, c0, 0" \
  101. : "=r" (pg) : : "cc"); \
  102. pg &= ~0x3fff;
  103. sysmmu_debug("CP15 TTBR0 : 0x%x\n", pg);
  104. /* Set sysmmu page table base address */
  105. __raw_writel(pg, sysmmuconp->regs + S5P_PT_BASE_ADDR);
  106. return 0;
  107. }
  108. int s5p_sysmmu_enable(sysmmu_ips ips)
  109. {
  110. unsigned int reg;
  111. struct sysmmu_controller *sysmmuconp;
  112. sysmmuconp = &s5p_sysmmu_cntlrs[ips];
  113. if (sysmmuconp == NULL) {
  114. printk(KERN_ERR "failed to get ip's sysmmu info\n");
  115. return 1;
  116. }
  117. s5p_sysmmu_set_tablebase(ips);
  118. /* replacement policy : LRU */
  119. reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CFG);
  120. reg |= 0x1;
  121. __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CFG);
  122. /* Enable interrupt, Enable MMU */
  123. reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
  124. reg |= (0x1 << 2) | (0x1 << 0);
  125. __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL);
  126. sysmmuconp->enable = true;
  127. return 0;
  128. }
  129. int s5p_sysmmu_disable(sysmmu_ips ips)
  130. {
  131. unsigned int reg;
  132. struct sysmmu_controller *sysmmuconp = NULL;
  133. if (ips > S5P_SYSMMU_TOTAL_IPNUM)
  134. printk(KERN_ERR "failed to get ips parameter\n");
  135. sysmmuconp = &s5p_sysmmu_cntlrs[ips];
  136. if (sysmmuconp == NULL) {
  137. printk(KERN_ERR "failed to get ip's sysmmu info\n");
  138. return 1;
  139. }
  140. reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CFG);
  141. /* replacement policy : LRU */
  142. reg |= 0x1;
  143. __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CFG);
  144. reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
  145. /* Disable MMU */
  146. reg &= ~0x1;
  147. __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL);
  148. sysmmuconp->enable = false;
  149. return 0;
  150. }
  151. int s5p_sysmmu_tlb_invalidate(sysmmu_ips ips)
  152. {
  153. unsigned int reg;
  154. struct sysmmu_controller *sysmmuconp = NULL;
  155. sysmmuconp = &s5p_sysmmu_cntlrs[ips];
  156. if (sysmmuconp == NULL) {
  157. printk(KERN_ERR "failed to get ip's sysmmu info\n");
  158. return 1;
  159. }
  160. /* set Block MMU for flush TLB */
  161. reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
  162. reg |= 0x1 << 1;
  163. __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL);
  164. /* flush all TLB entry */
  165. __raw_writel(0x1, sysmmuconp->regs + S5P_MMU_FLUSH);
  166. /* set Un-block MMU after flush TLB */
  167. reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
  168. reg &= ~(0x1 << 1);
  169. __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL);
  170. return 0;
  171. }
  172. static int s5p_sysmmu_probe(struct platform_device *pdev)
  173. {
  174. int i;
  175. int ret;
  176. struct resource *res;
  177. struct sysmmu_controller *sysmmuconp;
  178. sysmmu_ips ips;
  179. for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) {
  180. sysmmuconp = &s5p_sysmmu_cntlrs[i];
  181. if (sysmmuconp == NULL) {
  182. printk(KERN_ERR "failed to get ip's sysmmu info\n");
  183. ret = -ENOENT;
  184. goto err_res;
  185. }
  186. sysmmuconp->name = sysmmu_ips_name[i];
  187. res = platform_get_resource(pdev, IORESOURCE_MEM, i);
  188. if (!res) {
  189. printk(KERN_ERR "failed to get sysmmu resource\n");
  190. ret = -ENODEV;
  191. goto err_res;
  192. }
  193. sysmmuconp->mem = request_mem_region(res->start,
  194. ((res->end) - (res->start)) + 1, pdev->name);
  195. if (!sysmmuconp->mem) {
  196. pr_err("failed to request sysmmu memory region\n");
  197. ret = -EBUSY;
  198. goto err_res;
  199. }
  200. sysmmuconp->regs = ioremap(res->start, res->end - res->start + 1);
  201. if (!sysmmuconp->regs) {
  202. pr_err("failed to sysmmu ioremap\n");
  203. ret = -ENXIO;
  204. goto err_reg;
  205. }
  206. sysmmuconp->irq = platform_get_irq(pdev, i);
  207. if (sysmmuconp->irq <= 0) {
  208. pr_err("failed to get sysmmu irq resource\n");
  209. ret = -ENOENT;
  210. goto err_map;
  211. }
  212. ret = request_irq(sysmmuconp->irq, s5p_sysmmu_irq, IRQF_DISABLED, pdev->name, sysmmuconp);
  213. if (ret) {
  214. pr_err("failed to request irq\n");
  215. ret = -ENOENT;
  216. goto err_map;
  217. }
  218. ips = (sysmmu_ips)i;
  219. sysmmuconp->ips = ips;
  220. }
  221. return 0;
  222. err_reg:
  223. release_mem_region((resource_size_t)sysmmuconp->mem, (resource_size_t)((res->end) - (res->start) + 1));
  224. err_map:
  225. iounmap(sysmmuconp->regs);
  226. err_res:
  227. return ret;
  228. }
  229. static int s5p_sysmmu_remove(struct platform_device *pdev)
  230. {
  231. return 0;
  232. }
  233. int s5p_sysmmu_runtime_suspend(struct device *dev)
  234. {
  235. return 0;
  236. }
  237. int s5p_sysmmu_runtime_resume(struct device *dev)
  238. {
  239. return 0;
  240. }
  241. const struct dev_pm_ops s5p_sysmmu_pm_ops = {
  242. .runtime_suspend = s5p_sysmmu_runtime_suspend,
  243. .runtime_resume = s5p_sysmmu_runtime_resume,
  244. };
  245. static struct platform_driver s5p_sysmmu_driver = {
  246. .probe = s5p_sysmmu_probe,
  247. .remove = s5p_sysmmu_remove,
  248. .driver = {
  249. .owner = THIS_MODULE,
  250. .name = "s5p-sysmmu",
  251. .pm = &s5p_sysmmu_pm_ops,
  252. }
  253. };
  254. static int __init s5p_sysmmu_init(void)
  255. {
  256. return platform_driver_register(&s5p_sysmmu_driver);
  257. }
  258. arch_initcall(s5p_sysmmu_init);