1 #include <linux/slab.h>
2 #include <linux/module.h>
4 #include <linux/delay.h>
5 #include <linux/platform_device.h>
8 #include <linux/interrupt.h>
9 #include <asm/pgtable.h>
10 #include <asm/bitops.h>
12 #include <plat/s5p-clock.h>
15 #include <plat/sysmmu.h>
16 #include <plat/map-s5p.h>
17 #include <plat/clock.h>
20 #ifdef CONFIG_S5P_SYSMMU_DEBUG
21 static char *sysmmu_fault_name[8] = {
26 "AR SECURITY PROTECTION FAULT",
27 "AR ACCESS PROTECTION FAULT",
28 "AW SECURITY PROTECTION FAULT",
29 "AW ACCESS PROTECTION FAULT"
31 #define LOG(fmt, arg...) printk(KERN_INFO "[SYSMMU] " fmt "\n", ##arg)
32 #define LOG_IF(cond, fmt, arg...) if (cond) LOG(fmt, ##arg)
33 #define LOGE(fmt, arg...)\
34 printk(KERN_ERR "[SYSMMU:%s] " fmt "\n", __func__, ##arg)
35 #define LOGE_IF(cond, fmt, arg...) if (cond) LOGE(fmt, ##arg)
37 #define LOG(fmt, arg...) do { } while (0)
38 #define LOG_IF(cond, fmt, arg...) do { } while (0)
39 #define LOGE(fmt, arg...) do { } while (0)
40 #define LOGE_IF(cond, fmt, arg...) do { } while (0)
44 * 0 : S5P_PA_SYSMMU_MDMA (0x10A40000),
45 * 1 : S5P_PA_SYSMMU_SSS (0x10A50000),
46 * 2 : S5P_PA_SYSMMU_FIMC0 (0x11A20000),
47 * 3 : S5P_PA_SYSMMU_FIMC1 (0x11A30000),
48 * 4 : S5P_PA_SYSMMU_FIMC2 (0x11A40000),
49 * 5 : S5P_PA_SYSMMU_FIMC3 (0x11A50000),
50 * 6 : S5P_PA_SYSMMU_JPEG (0x11A60000),
51 * 7 : S5P_PA_SYSMMU_FIMD0 (0x11E20000),
52 * 8 : S5P_PA_SYSMMU_FIMD1 (0x12220000),
53 * 9 : S5P_PA_SYSMMU_PCIe (0x12620000),
54 * 10 : S5P_PA_SYSMMU_G2D (0x12A20000),
55 * 11 : S5P_PA_SYSMMU_ROTATOR (0x12A30000),
56 * 12 : S5P_PA_SYSMMU_MDMA2 (0x12A40000),
57 * 13 : S5P_PA_SYSMMU_TV (0x12E20000),
58 * 14 : S5P_PA_SYSMMU_MFC_L (0x13620000),
59 * 15 : S5P_PA_SYSMMU_MFC_R (0x13630000)
62 static unsigned long sysmmu_states; /* 1 bits for each system MMUs */
64 static inline int set_sysmmu_active(sysmmu_ips ips)
66 /* return true if it is not set */
67 return !test_and_set_bit(ips, &sysmmu_states);
70 static inline int set_sysmmu_inactive(sysmmu_ips ips)
72 /* return true if it is set */
73 return test_and_clear_bit(ips, &sysmmu_states);
76 static inline int is_sysmmu_active(sysmmu_ips ips)
78 /* BUG_ON(ips >= SYSMMU_TOTAL_IPNUM); */
79 return sysmmu_states & (1 << ips);
82 #if defined(CONFIG_S5P_SYSMMU_DEBUG) || defined(CONFIG_S5P_SYSMMU_PROFILE)
83 /* This character strings must be same with clock names
84 * specified in clock.c */
85 static const char *sysmmu_name[SYSMMU_TOTAL_IPNUM] = {
105 static void __iomem *sysmmusfrs[SYSMMU_TOTAL_IPNUM];
107 #ifdef CONFIG_S5P_SYSMMU_PROFILE
108 #define CTRL_ENABLE 0xD
109 #define CTRL_BLOCK 0xF
110 static inline void sysmmu_start_profile(sysmmu_ips ips)
112 printk(KERN_INFO "[SYSMMU:%s] Initialized profiling constructs.",
114 __raw_writel(0x8000000F, sysmmusfrs[ips] + S5P_PPC_INTENS);
115 __raw_writel(0x8000000F, sysmmusfrs[ips] + S5P_PPC_CNTENS);
116 __raw_writel(0x8000000F, sysmmusfrs[ips] + S5P_PPC_FLAG);
117 __raw_writel(0x0, sysmmusfrs[ips] + S5P_PPC_CCNT);
118 __raw_writel(0x6, sysmmusfrs[ips] + S5P_PPC_PMNC);
120 __raw_writel(0x1, sysmmusfrs[ips] + S5P_PPC_PMNC); /* START PPC */
123 static inline void sysmmu_stop_profile(sysmmu_ips ips)
125 __raw_writel(0x0, sysmmusfrs[ips] + S5P_PPC_PMNC); /* STOP PPC */
128 static inline void sysmmu_show_profile(sysmmu_ips ips)
132 printk(KERN_INFO "[SYSMMU:%s] Profiling result while %d cycles:\n",
133 sysmmu_name[ips], __raw_readl(sysmmusfrs[ips] + S5P_PPC_CCNT));
135 ovf = __raw_readl(sysmmusfrs[ips] + S5P_PPC_FLAG);
137 printk(KERN_INFO "[SYSMMU:%s] %d AW misses in %d tranxs.\n",
139 __raw_readl(sysmmusfrs[ips] + S5P_PPC_PMCNT2),
140 __raw_readl(sysmmusfrs[ips] + S5P_PPC_PMCNT0));
141 printk(KERN_INFO "[SYSMMU:%s] %d AR misses in %d tranxs.\n",
143 __raw_readl(sysmmusfrs[ips] + S5P_PPC_PMCNT3),
144 __raw_readl(sysmmusfrs[ips] + S5P_PPC_PMCNT1));
146 printk(KERN_INFO "[SYSMMU:%s] "
147 "Overflow(%ld) occurred in performance monitor.\n",
148 sysmmu_name[ips], ovf);
152 #define CTRL_ENABLE 0x5
153 #define CTRL_BLOCK 0x7
154 #define sysmmu_start_profile(ips) do { } while (0)
155 #define sysmmu_stop_profile(ips) do { } while (0)
156 #define sysmmu_show_profile(ips) do { } while (0)
158 #define CTRL_DISABLE 0x0
160 static int (*fault_handlers[SYSMMU_TOTAL_IPNUM])(
161 enum SYSMMU_INTERRUPT_TYPE itype,
162 unsigned long pgtable_base,
163 unsigned long fault_addr);
165 static inline void sysmmu_block(sysmmu_ips ips)
167 __raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL);
168 LOG("%s is blocked.", sysmmu_name[ips]);
171 static inline void sysmmu_unblock(sysmmu_ips ips)
173 __raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
174 LOG("%s is unblocked.", sysmmu_name[ips]);
177 static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips)
179 __raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH);
180 LOG("TLB of %s is invalidated.", sysmmu_name[ips]);
183 static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd)
185 if (unlikely(pgd == 0)) {
186 pgd = (unsigned long)ZERO_PAGE(0);
187 __raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB LV1 */
189 __raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB LV1 */
192 __raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
194 LOG("Page table base of %s is initialized with 0x%08lX.",
195 sysmmu_name[ips], pgd);
196 __sysmmu_tlb_invalidate(ips);
199 void sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd)
201 if (is_sysmmu_active(ips)) {
203 __sysmmu_set_ptbase(ips, pgd);
206 LOG("%s is disabled. Skipping initializing page table base.",
211 void sysmmu_set_fault_handler(sysmmu_ips ips,
212 int (*handler)(enum SYSMMU_INTERRUPT_TYPE itype,
213 unsigned long pgtable_base,
214 unsigned long fault_addr))
216 BUG_ON(!((ips >= SYSMMU_MDMA) && (ips < SYSMMU_TOTAL_IPNUM)));
217 fault_handlers[ips] = handler;
220 static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
224 S5P_DEFAULT_SLAVE_ADDR,
232 * sysmmu_irq - [GENERIC] irq service routine
234 * @dev_id: pointer to private data
237 static irqreturn_t sysmmu_irq(int irq, void *dev_id)
239 /* SYSMMU is in blocked when interrupt occurred. */
240 unsigned long base = 0;
241 sysmmu_ips ips = (sysmmu_ips)dev_id;
242 enum SYSMMU_INTERRUPT_TYPE itype;
244 BUG_ON(!is_sysmmu_active(ips));
246 itype = (enum SYSMMU_INTERRUPT_TYPE)
247 __ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS));
248 #ifdef CONFIG_S5P_SYSMMU_PROFILE
249 if (!((itype >= 0) && (itype < 8))) {
250 unsigned long ovflag;
252 ovflag = __raw_readl(sysmmusfrs[ips] + S5P_PPC_FLAG);
255 LOG("Interupt by overflow(flag: 0x%08lX) of PPC counters of %s",
256 ovflag, sysmmu_name[ips]);
259 BUG_ON(!((itype >= 0) && (itype < 8)));
262 /* call fault handler */
263 LOGE("%s occurred by %s.", sysmmu_fault_name[itype], sysmmu_name[ips]);
265 if (fault_handlers[ips]) {
268 base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
269 addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]);
271 if (fault_handlers[ips](itype, base, addr)) {
272 __raw_writel(1 << itype,
273 sysmmusfrs[ips] + S5P_INT_CLEAR);
274 LOGE("%s from %s is resolved. Retrying translation.",
275 sysmmu_fault_name[itype], sysmmu_name[ips]);
284 LOGE("%s from %s is not handled.",
285 sysmmu_fault_name[itype], sysmmu_name[ips]);
290 #ifdef CONFIG_S5P_SYSMMU_CLKGATING
291 static struct clk *sysmmu_clk[SYSMMU_TOTAL_IPNUM];
293 static inline void sysmmu_clk_init(sysmmu_ips ips, struct device *dev)
295 sysmmu_clk[ips] = clk_get(dev, "sysmmu");
296 if (IS_ERR(sysmmu_clk[ips]))
297 sysmmu_clk[ips] = NULL;
300 static inline void sysmmu_clk_enable(sysmmu_ips ips)
303 clk_enable(sysmmu_clk[ips]);
306 static inline void sysmmu_clk_disable(sysmmu_ips ips)
309 clk_disable(sysmmu_clk[ips]);
312 #define sysmmu_clk_init(ips, dev) do { } while (0)
313 #define sysmmu_clk_enable(ips) do { } while (0)
314 #define sysmmu_clk_disable(ips) do { } while (0)
317 void sysmmu_on(sysmmu_ips ips, unsigned long pgd)
319 LOG_IF(is_sysmmu_active(ips),
320 "%s is already enabled.", sysmmu_name[ips]);
322 if (set_sysmmu_active(ips)) {
323 sysmmu_clk_enable(ips);
325 __sysmmu_set_ptbase(ips, pgd);
327 __raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
329 sysmmu_start_profile(ips);
330 LOG("%s is enabled.", sysmmu_name[ips]);
334 void sysmmu_off(sysmmu_ips ips)
336 LOG_IF(!is_sysmmu_active(ips),
337 "%s is already disabled.", sysmmu_name[ips]);
338 if (set_sysmmu_inactive(ips)) {
339 __raw_writel(CTRL_DISABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
341 sysmmu_stop_profile(ips);
342 sysmmu_show_profile(ips);
344 sysmmu_clk_disable(ips);
345 LOG("%s is disabled.", sysmmu_name[ips]);
349 void sysmmu_tlb_invalidate(sysmmu_ips ips)
351 LOG_IF(!is_sysmmu_active(ips),
352 "%s is disabled. Skipping invalidating TLB.", sysmmu_name[ips]);
353 if (is_sysmmu_active(ips)) {
355 __sysmmu_tlb_invalidate(ips);
360 static int sysmmu_probe(struct platform_device *pdev)
363 struct resource *res, *ioarea;
367 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
369 LOGE("Failed probing system MMU: "
370 "failed to get resource.");
374 id = (sysmmu_ips)pdev->id;
375 if (id >= SYSMMU_TOTAL_IPNUM) {
376 LOGE("Unknown System MMU ID %d.", id);
380 ioarea = request_mem_region(res->start, resource_size(res), pdev->name);
381 if (ioarea == NULL) {
382 LOGE("Failed probing system MMU: "
383 "failed to request memory region.");
387 sysmmusfrs[id] = ioremap(res->start, resource_size(res));
388 if (!sysmmusfrs[id]) {
389 LOGE("Failed probing system MMU: "
390 "failed to call ioremap().");
395 irq = platform_get_irq(pdev, 0);
397 LOGE("Failed probing system MMU: "
398 "failed to get irq resource.");
403 if (request_irq(irq, sysmmu_irq, 0, dev_name(&pdev->dev), (void *)id)) {
404 LOGE("Failed probing system MMU: "
405 "failed to request irq.");
410 sysmmu_clk_init(id, &pdev->dev);
412 LOG("Probing system MMU succeeded.");
416 iounmap(sysmmusfrs[id]);
418 release_resource(ioarea);
420 LOG("Probing system MMU failed.");
424 static int sysmmu_remove(struct platform_device *pdev)
429 static int sysmmu_suspend(struct platform_device *pdev, pm_message_t state)
434 static int sysmmu_resume(struct platform_device *pdev)
441 static struct platform_driver s5p_sysmmu_driver = {
442 .probe = sysmmu_probe,
443 .remove = sysmmu_remove,
444 .suspend = sysmmu_suspend,
445 .resume = sysmmu_resume,
447 .owner = THIS_MODULE,
448 .name = "s5p-sysmmu",
452 static int __init sysmmu_register(void)
456 ret = platform_driver_register(&s5p_sysmmu_driver);
457 LOGE_IF(ret, "Failed to register system MMU driver.");
461 static void __exit sysmmu_unregister(void)
463 platform_driver_unregister(&s5p_sysmmu_driver);
466 module_init(sysmmu_register);
467 module_exit(sysmmu_unregister);
469 MODULE_AUTHOR("Donguk Ryu <du.ryu@samsung.com>");
470 MODULE_DESCRIPTION("Samsung System MMU driver");
471 MODULE_LICENSE("GPL");