aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s5p
diff options
context:
space:
mode:
authorDonguk Ryu <du.ryu@samsung.com>2011-01-12 23:35:31 -0500
committerKukjin Kim <kgene.kim@samsung.com>2011-01-12 23:35:31 -0500
commitb55f685e973ae952cbff5bd0d57cd8cb91aa8daa (patch)
tree10412ccb7437de12a0026200af854b3dc5b48d0f /arch/arm/plat-s5p
parent2cf0c58ed970b91e620e551d4c8da8b1d0baab49 (diff)
ARM: S5P: Add Support System MMU
This patch adds support System MMU which supports address transition from virtual address to physical address. Basically, each hardware block is connected System MMU block can use directly vitrual address when it accesses physical memory not using physical address. Signed-off-by: Donguk Ryu <du.ryu@samsung.com> Signed-off-by: Sangbeom Kim <sbkim73@samsung.com> [kgene.kim@samsung.com: removed useless codes] Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'arch/arm/plat-s5p')
-rw-r--r--arch/arm/plat-s5p/Kconfig16
-rw-r--r--arch/arm/plat-s5p/Makefile1
-rw-r--r--arch/arm/plat-s5p/include/plat/sysmmu.h23
-rw-r--r--arch/arm/plat-s5p/sysmmu.c328
4 files changed, 368 insertions, 0 deletions
diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig
index 6a161f317a79..deb39951a22e 100644
--- a/arch/arm/plat-s5p/Kconfig
+++ b/arch/arm/plat-s5p/Kconfig
@@ -66,3 +66,19 @@ config S5P_DEV_CSIS1
66 bool 66 bool
67 help 67 help
68 Compile in platform device definitions for MIPI-CSIS channel 1 68 Compile in platform device definitions for MIPI-CSIS channel 1
69
70menuconfig S5P_SYSMMU
71 bool "SYSMMU support"
72 depends on ARCH_S5PV310
73 help
74 This is a System MMU driver for Samsung ARM based Soc.
75
76if S5P_SYSMMU
77
78config S5P_SYSMMU_DEBUG
79 bool "Enables debug messages"
80 depends on S5P_SYSMMU
81 help
82 This enables SYSMMU driver debug massages.
83
84endif
diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile
index 2b7317378103..92efe1adcfd6 100644
--- a/arch/arm/plat-s5p/Makefile
+++ b/arch/arm/plat-s5p/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_S5P_DEV_FIMC2) += dev-fimc2.o
30obj-$(CONFIG_S5P_DEV_ONENAND) += dev-onenand.o 30obj-$(CONFIG_S5P_DEV_ONENAND) += dev-onenand.o
31obj-$(CONFIG_S5P_DEV_CSIS0) += dev-csis0.o 31obj-$(CONFIG_S5P_DEV_CSIS0) += dev-csis0.o
32obj-$(CONFIG_S5P_DEV_CSIS1) += dev-csis1.o 32obj-$(CONFIG_S5P_DEV_CSIS1) += dev-csis1.o
33obj-$(CONFIG_S5P_SYSMMU) += sysmmu.o
diff --git a/arch/arm/plat-s5p/include/plat/sysmmu.h b/arch/arm/plat-s5p/include/plat/sysmmu.h
new file mode 100644
index 000000000000..db298fc5438a
--- /dev/null
+++ b/arch/arm/plat-s5p/include/plat/sysmmu.h
@@ -0,0 +1,23 @@
1/* linux/arch/arm/plat-s5p/include/plat/sysmmu.h
2 *
3 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
5 *
6 * Samsung sysmmu driver
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11*/
12
13#ifndef __ASM_PLAT_S5P_SYSMMU_H
14#define __ASM_PLAT_S5P_SYSMMU_H __FILE__
15
16/* debug macro */
17#ifdef CONFIG_S5P_SYSMMU_DEBUG
18#define sysmmu_debug(fmt, arg...) printk(KERN_INFO "[%s] " fmt, __func__, ## arg)
19#else
20#define sysmmu_debug(fmt, arg...) do { } while (0)
21#endif
22
23#endif /* __ASM_PLAT_S5P_SYSMMU_H */
diff --git a/arch/arm/plat-s5p/sysmmu.c b/arch/arm/plat-s5p/sysmmu.c
new file mode 100644
index 000000000000..d804914dc2e2
--- /dev/null
+++ b/arch/arm/plat-s5p/sysmmu.c
@@ -0,0 +1,328 @@
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#include <plat/sysmmu.h>
20
21struct sysmmu_controller s5p_sysmmu_cntlrs[S5P_SYSMMU_TOTAL_IPNUM];
22
23void s5p_sysmmu_register(struct sysmmu_controller *sysmmuconp)
24{
25 unsigned int reg_mmu_ctrl;
26 unsigned int reg_mmu_status;
27 unsigned int reg_pt_base_addr;
28 unsigned int reg_int_status;
29 unsigned int reg_page_ft_addr;
30
31 reg_int_status = __raw_readl(sysmmuconp->regs + S5P_INT_STATUS);
32 reg_mmu_ctrl = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
33 reg_mmu_status = __raw_readl(sysmmuconp->regs + S5P_MMU_STATUS);
34 reg_pt_base_addr = __raw_readl(sysmmuconp->regs + S5P_PT_BASE_ADDR);
35 reg_page_ft_addr = __raw_readl(sysmmuconp->regs + S5P_PAGE_FAULT_ADDR);
36
37 printk(KERN_INFO "%s: ips:%s\n", __func__, sysmmuconp->name);
38 printk(KERN_INFO "%s: MMU_CTRL:0x%X, ", __func__, reg_mmu_ctrl);
39 printk(KERN_INFO "MMU_STATUS:0x%X, PT_BASE_ADDR:0x%X\n", reg_mmu_status, reg_pt_base_addr);
40 printk(KERN_INFO "%s: INT_STATUS:0x%X, PAGE_FAULT_ADDR:0x%X\n", __func__, reg_int_status, reg_page_ft_addr);
41
42 switch (reg_int_status & 0xFF) {
43 case 0x1:
44 printk(KERN_INFO "%s: Page fault\n", __func__);
45 printk(KERN_INFO "%s: Virtual address causing last page fault or bus error : 0x%x\n", __func__ , reg_page_ft_addr);
46 break;
47 case 0x2:
48 printk(KERN_INFO "%s: AR multi-hit fault\n", __func__);
49 break;
50 case 0x4:
51 printk(KERN_INFO "%s: AW multi-hit fault\n", __func__);
52 break;
53 case 0x8:
54 printk(KERN_INFO "%s: Bus error\n", __func__);
55 break;
56 case 0x10:
57 printk(KERN_INFO "%s: AR Security protection fault\n", __func__);
58 break;
59 case 0x20:
60 printk(KERN_INFO "%s: AR Access protection fault\n", __func__);
61 break;
62 case 0x40:
63 printk(KERN_INFO "%s: AW Security protection fault\n", __func__);
64 break;
65 case 0x80:
66 printk(KERN_INFO "%s: AW Access protection fault\n", __func__);
67 break;
68 }
69}
70
71static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id)
72{
73 unsigned int i;
74 unsigned int reg_int_status;
75 struct sysmmu_controller *sysmmuconp;
76
77 for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) {
78 sysmmuconp = &s5p_sysmmu_cntlrs[i];
79
80 if (sysmmuconp->enable == true) {
81 reg_int_status = __raw_readl(sysmmuconp->regs + S5P_INT_STATUS);
82
83 if (reg_int_status & 0xFF)
84 s5p_sysmmu_register(sysmmuconp);
85 }
86 }
87 return IRQ_HANDLED;
88}
89
90int s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd)
91{
92 struct sysmmu_controller *sysmmuconp = NULL;
93
94 sysmmuconp = &s5p_sysmmu_cntlrs[ips];
95
96 if (sysmmuconp == NULL) {
97 printk(KERN_ERR "failed to get ip's sysmmu info\n");
98 return 1;
99 }
100
101 /* Set sysmmu page table base address */
102 __raw_writel(pgd, sysmmuconp->regs + S5P_PT_BASE_ADDR);
103
104 if (s5p_sysmmu_tlb_invalidate(ips) != 0)
105 printk(KERN_ERR "failed s5p_sysmmu_tlb_invalidate\n");
106
107 return 0;
108}
109
110static int s5p_sysmmu_set_tablebase(sysmmu_ips ips)
111{
112 unsigned int pg;
113 struct sysmmu_controller *sysmmuconp;
114
115 sysmmuconp = &s5p_sysmmu_cntlrs[ips];
116
117 if (sysmmuconp == NULL) {
118 printk(KERN_ERR "failed to get ip's sysmmu info\n");
119 return 1;
120 }
121
122 __asm__("mrc p15, 0, %0, c2, c0, 0" \
123 : "=r" (pg) : : "cc"); \
124 pg &= ~0x3fff;
125
126 sysmmu_debug("CP15 TTBR0 : 0x%x\n", pg);
127
128 /* Set sysmmu page table base address */
129 __raw_writel(pg, sysmmuconp->regs + S5P_PT_BASE_ADDR);
130
131 return 0;
132}
133
134int s5p_sysmmu_enable(sysmmu_ips ips)
135{
136 unsigned int reg;
137
138 struct sysmmu_controller *sysmmuconp;
139
140 sysmmuconp = &s5p_sysmmu_cntlrs[ips];
141
142 if (sysmmuconp == NULL) {
143 printk(KERN_ERR "failed to get ip's sysmmu info\n");
144 return 1;
145 }
146
147 s5p_sysmmu_set_tablebase(ips);
148
149 /* replacement policy : LRU */
150 reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CFG);
151 reg |= 0x1;
152 __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CFG);
153
154 /* Enable interrupt, Enable MMU */
155 reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
156 reg |= (0x1 << 2) | (0x1 << 0);
157
158 __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL);
159
160 sysmmuconp->enable = true;
161
162 return 0;
163}
164
165int s5p_sysmmu_disable(sysmmu_ips ips)
166{
167 unsigned int reg;
168
169 struct sysmmu_controller *sysmmuconp = NULL;
170
171 if (ips > S5P_SYSMMU_TOTAL_IPNUM)
172 printk(KERN_ERR "failed to get ips parameter\n");
173
174 sysmmuconp = &s5p_sysmmu_cntlrs[ips];
175
176 if (sysmmuconp == NULL) {
177 printk(KERN_ERR "failed to get ip's sysmmu info\n");
178 return 1;
179 }
180
181 reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CFG);
182
183 /* replacement policy : LRU */
184 reg |= 0x1;
185 __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CFG);
186
187 reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
188
189 /* Disable MMU */
190 reg &= ~0x1;
191 __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL);
192
193 sysmmuconp->enable = false;
194
195 return 0;
196}
197
198int s5p_sysmmu_tlb_invalidate(sysmmu_ips ips)
199{
200 unsigned int reg;
201 struct sysmmu_controller *sysmmuconp = NULL;
202
203 sysmmuconp = &s5p_sysmmu_cntlrs[ips];
204
205 if (sysmmuconp == NULL) {
206 printk(KERN_ERR "failed to get ip's sysmmu info\n");
207 return 1;
208 }
209
210 /* set Block MMU for flush TLB */
211 reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
212 reg |= 0x1 << 1;
213 __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL);
214
215 /* flush all TLB entry */
216 __raw_writel(0x1, sysmmuconp->regs + S5P_MMU_FLUSH);
217
218 /* set Un-block MMU after flush TLB */
219 reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL);
220 reg &= ~(0x1 << 1);
221 __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL);
222
223 return 0;
224}
225
226static int s5p_sysmmu_probe(struct platform_device *pdev)
227{
228 int i;
229 int ret;
230 struct resource *res;
231 struct sysmmu_controller *sysmmuconp;
232 sysmmu_ips ips;
233
234 for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) {
235 sysmmuconp = &s5p_sysmmu_cntlrs[i];
236 if (sysmmuconp == NULL) {
237 printk(KERN_ERR "failed to get ip's sysmmu info\n");
238 ret = -ENOENT;
239 goto err_res;
240 }
241
242 sysmmuconp->name = sysmmu_ips_name[i];
243
244 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
245 if (!res) {
246 printk(KERN_ERR "failed to get sysmmu resource\n");
247 ret = -ENODEV;
248 goto err_res;
249 }
250
251 sysmmuconp->mem = request_mem_region(res->start,
252 ((res->end) - (res->start)) + 1, pdev->name);
253 if (!sysmmuconp->mem) {
254 pr_err("failed to request sysmmu memory region\n");
255 ret = -EBUSY;
256 goto err_res;
257 }
258
259 sysmmuconp->regs = ioremap(res->start, res->end - res->start + 1);
260 if (!sysmmuconp->regs) {
261 pr_err("failed to sysmmu ioremap\n");
262 ret = -ENXIO;
263 goto err_reg;
264 }
265
266 sysmmuconp->irq = platform_get_irq(pdev, i);
267 if (sysmmuconp->irq <= 0) {
268 pr_err("failed to get sysmmu irq resource\n");
269 ret = -ENOENT;
270 goto err_map;
271 }
272
273 ret = request_irq(sysmmuconp->irq, s5p_sysmmu_irq, IRQF_DISABLED, pdev->name, sysmmuconp);
274 if (ret) {
275 pr_err("failed to request irq\n");
276 ret = -ENOENT;
277 goto err_map;
278 }
279
280 ips = (sysmmu_ips)i;
281
282 sysmmuconp->ips = ips;
283 }
284
285 return 0;
286
287err_reg:
288 release_mem_region((resource_size_t)sysmmuconp->mem, (resource_size_t)((res->end) - (res->start) + 1));
289err_map:
290 iounmap(sysmmuconp->regs);
291err_res:
292 return ret;
293}
294
295static int s5p_sysmmu_remove(struct platform_device *pdev)
296{
297 return 0;
298}
299int s5p_sysmmu_runtime_suspend(struct device *dev)
300{
301 return 0;
302}
303
304int s5p_sysmmu_runtime_resume(struct device *dev)
305{
306 return 0;
307}
308
309const struct dev_pm_ops s5p_sysmmu_pm_ops = {
310 .runtime_suspend = s5p_sysmmu_runtime_suspend,
311 .runtime_resume = s5p_sysmmu_runtime_resume,
312};
313
314static struct platform_driver s5p_sysmmu_driver = {
315 .probe = s5p_sysmmu_probe,
316 .remove = s5p_sysmmu_remove,
317 .driver = {
318 .owner = THIS_MODULE,
319 .name = "s5p-sysmmu",
320 .pm = &s5p_sysmmu_pm_ops,
321 }
322};
323
324static int __init s5p_sysmmu_init(void)
325{
326 return platform_driver_register(&s5p_sysmmu_driver);
327}
328arch_initcall(s5p_sysmmu_init);