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