diff options
Diffstat (limited to 'arch/arm/plat-s5p')
-rw-r--r-- | arch/arm/plat-s5p/Kconfig | 23 | ||||
-rw-r--r-- | arch/arm/plat-s5p/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/plat-s5p/cpu.c | 25 | ||||
-rw-r--r-- | arch/arm/plat-s5p/dev-csis0.c | 2 | ||||
-rw-r--r-- | arch/arm/plat-s5p/dev-csis1.c | 2 | ||||
-rw-r--r-- | arch/arm/plat-s5p/dev-fimc3.c | 43 | ||||
-rw-r--r-- | arch/arm/plat-s5p/include/plat/camport.h | 28 | ||||
-rw-r--r-- | arch/arm/plat-s5p/include/plat/csis.h | 28 | ||||
-rw-r--r-- | arch/arm/plat-s5p/include/plat/exynos4.h | 34 | ||||
-rw-r--r-- | arch/arm/plat-s5p/include/plat/mipi_csis.h | 43 | ||||
-rw-r--r-- | arch/arm/plat-s5p/include/plat/s5p-time.h | 40 | ||||
-rw-r--r-- | arch/arm/plat-s5p/include/plat/s5pv310.h | 34 | ||||
-rw-r--r-- | arch/arm/plat-s5p/include/plat/sysmmu.h | 95 | ||||
-rw-r--r-- | arch/arm/plat-s5p/irq-gpioint.c | 170 | ||||
-rw-r--r-- | arch/arm/plat-s5p/s5p-time.c | 448 | ||||
-rw-r--r-- | arch/arm/plat-s5p/setup-mipiphy.c | 63 | ||||
-rw-r--r-- | arch/arm/plat-s5p/sysmmu.c | 370 |
17 files changed, 1112 insertions, 339 deletions
diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 557f8c507f6d..849229716586 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig | |||
@@ -7,10 +7,10 @@ | |||
7 | 7 | ||
8 | config PLAT_S5P | 8 | config PLAT_S5P |
9 | bool | 9 | bool |
10 | depends on (ARCH_S5P64X0 || ARCH_S5P6442 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5PV310) | 10 | depends on (ARCH_S5P64X0 || ARCH_S5P6442 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS4) |
11 | default y | 11 | default y |
12 | select ARM_VIC if !ARCH_S5PV310 | 12 | select ARM_VIC if !ARCH_EXYNOS4 |
13 | select ARM_GIC if ARCH_S5PV310 | 13 | select ARM_GIC if ARCH_EXYNOS4 |
14 | select NO_IOPORT | 14 | select NO_IOPORT |
15 | select ARCH_REQUIRE_GPIOLIB | 15 | select ARCH_REQUIRE_GPIOLIB |
16 | select S3C_GPIO_TRACK | 16 | select S3C_GPIO_TRACK |
@@ -37,11 +37,16 @@ config S5P_GPIO_INT | |||
37 | help | 37 | help |
38 | Common code for the GPIO interrupts (other than external interrupts.) | 38 | Common code for the GPIO interrupts (other than external interrupts.) |
39 | 39 | ||
40 | config S5P_HRT | ||
41 | bool | ||
42 | help | ||
43 | Use the High Resolution timer support | ||
44 | |||
40 | comment "System MMU" | 45 | comment "System MMU" |
41 | 46 | ||
42 | config S5P_SYSTEM_MMU | 47 | config S5P_SYSTEM_MMU |
43 | bool "S5P SYSTEM MMU" | 48 | bool "S5P SYSTEM MMU" |
44 | depends on ARCH_S5PV310 | 49 | depends on ARCH_EXYNOS4 |
45 | help | 50 | help |
46 | Say Y here if you want to enable System MMU | 51 | Say Y here if you want to enable System MMU |
47 | 52 | ||
@@ -60,6 +65,11 @@ config S5P_DEV_FIMC2 | |||
60 | help | 65 | help |
61 | Compile in platform device definitions for FIMC controller 2 | 66 | Compile in platform device definitions for FIMC controller 2 |
62 | 67 | ||
68 | config S5P_DEV_FIMC3 | ||
69 | bool | ||
70 | help | ||
71 | Compile in platform device definitions for FIMC controller 3 | ||
72 | |||
63 | config S5P_DEV_ONENAND | 73 | config S5P_DEV_ONENAND |
64 | bool | 74 | bool |
65 | help | 75 | help |
@@ -74,3 +84,8 @@ config S5P_DEV_CSIS1 | |||
74 | bool | 84 | bool |
75 | help | 85 | help |
76 | Compile in platform device definitions for MIPI-CSIS channel 1 | 86 | Compile in platform device definitions for MIPI-CSIS channel 1 |
87 | |||
88 | config S5P_SETUP_MIPIPHY | ||
89 | bool | ||
90 | help | ||
91 | Compile in common setup code for MIPI-CSIS and MIPI-DSIM devices | ||
diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile index 4bd5cf908977..42afff7f60be 100644 --- a/arch/arm/plat-s5p/Makefile +++ b/arch/arm/plat-s5p/Makefile | |||
@@ -22,12 +22,15 @@ obj-$(CONFIG_S5P_GPIO_INT) += irq-gpioint.o | |||
22 | obj-$(CONFIG_S5P_SYSTEM_MMU) += sysmmu.o | 22 | obj-$(CONFIG_S5P_SYSTEM_MMU) += sysmmu.o |
23 | obj-$(CONFIG_PM) += pm.o | 23 | obj-$(CONFIG_PM) += pm.o |
24 | obj-$(CONFIG_PM) += irq-pm.o | 24 | obj-$(CONFIG_PM) += irq-pm.o |
25 | obj-$(CONFIG_S5P_HRT) += s5p-time.o | ||
25 | 26 | ||
26 | # devices | 27 | # devices |
27 | 28 | ||
28 | obj-$(CONFIG_S5P_DEV_FIMC0) += dev-fimc0.o | 29 | obj-$(CONFIG_S5P_DEV_FIMC0) += dev-fimc0.o |
29 | obj-$(CONFIG_S5P_DEV_FIMC1) += dev-fimc1.o | 30 | obj-$(CONFIG_S5P_DEV_FIMC1) += dev-fimc1.o |
30 | obj-$(CONFIG_S5P_DEV_FIMC2) += dev-fimc2.o | 31 | obj-$(CONFIG_S5P_DEV_FIMC2) += dev-fimc2.o |
32 | obj-$(CONFIG_S5P_DEV_FIMC3) += dev-fimc3.o | ||
31 | obj-$(CONFIG_S5P_DEV_ONENAND) += dev-onenand.o | 33 | obj-$(CONFIG_S5P_DEV_ONENAND) += dev-onenand.o |
32 | obj-$(CONFIG_S5P_DEV_CSIS0) += dev-csis0.o | 34 | obj-$(CONFIG_S5P_DEV_CSIS0) += dev-csis0.o |
33 | obj-$(CONFIG_S5P_DEV_CSIS1) += dev-csis1.o | 35 | obj-$(CONFIG_S5P_DEV_CSIS1) += dev-csis1.o |
36 | obj-$(CONFIG_S5P_SETUP_MIPIPHY) += setup-mipiphy.o | ||
diff --git a/arch/arm/plat-s5p/cpu.c b/arch/arm/plat-s5p/cpu.c index 047d31c1bbd8..c3bfe9b13acf 100644 --- a/arch/arm/plat-s5p/cpu.c +++ b/arch/arm/plat-s5p/cpu.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* linux/arch/arm/plat-s5p/cpu.c | 1 | /* linux/arch/arm/plat-s5p/cpu.c |
2 | * | 2 | * |
3 | * Copyright (c) 2009 Samsung Electronics Co., Ltd. | 3 | * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd. |
4 | * http://www.samsung.com/ | 4 | * http://www.samsung.com |
5 | * | 5 | * |
6 | * S5P CPU Support | 6 | * S5P CPU Support |
7 | * | 7 | * |
@@ -12,17 +12,20 @@ | |||
12 | 12 | ||
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <mach/map.h> | 15 | |
16 | #include <asm/mach/arch.h> | 16 | #include <asm/mach/arch.h> |
17 | #include <asm/mach/map.h> | 17 | #include <asm/mach/map.h> |
18 | |||
19 | #include <mach/map.h> | ||
18 | #include <mach/regs-clock.h> | 20 | #include <mach/regs-clock.h> |
21 | |||
19 | #include <plat/cpu.h> | 22 | #include <plat/cpu.h> |
20 | #include <plat/s5p6440.h> | 23 | #include <plat/s5p6440.h> |
21 | #include <plat/s5p6442.h> | 24 | #include <plat/s5p6442.h> |
22 | #include <plat/s5p6450.h> | 25 | #include <plat/s5p6450.h> |
23 | #include <plat/s5pc100.h> | 26 | #include <plat/s5pc100.h> |
24 | #include <plat/s5pv210.h> | 27 | #include <plat/s5pv210.h> |
25 | #include <plat/s5pv310.h> | 28 | #include <plat/exynos4.h> |
26 | 29 | ||
27 | /* table of supported CPUs */ | 30 | /* table of supported CPUs */ |
28 | 31 | ||
@@ -31,7 +34,7 @@ static const char name_s5p6442[] = "S5P6442"; | |||
31 | static const char name_s5p6450[] = "S5P6450"; | 34 | static const char name_s5p6450[] = "S5P6450"; |
32 | static const char name_s5pc100[] = "S5PC100"; | 35 | static const char name_s5pc100[] = "S5PC100"; |
33 | static const char name_s5pv210[] = "S5PV210/S5PC110"; | 36 | static const char name_s5pv210[] = "S5PV210/S5PC110"; |
34 | static const char name_s5pv310[] = "S5PV310"; | 37 | static const char name_exynos4210[] = "EXYNOS4210"; |
35 | 38 | ||
36 | static struct cpu_table cpu_ids[] __initdata = { | 39 | static struct cpu_table cpu_ids[] __initdata = { |
37 | { | 40 | { |
@@ -75,13 +78,13 @@ static struct cpu_table cpu_ids[] __initdata = { | |||
75 | .init = s5pv210_init, | 78 | .init = s5pv210_init, |
76 | .name = name_s5pv210, | 79 | .name = name_s5pv210, |
77 | }, { | 80 | }, { |
78 | .idcode = 0x43200000, | 81 | .idcode = 0x43210000, |
79 | .idmask = 0xfffff000, | 82 | .idmask = 0xfffff000, |
80 | .map_io = s5pv310_map_io, | 83 | .map_io = exynos4_map_io, |
81 | .init_clocks = s5pv310_init_clocks, | 84 | .init_clocks = exynos4_init_clocks, |
82 | .init_uarts = s5pv310_init_uarts, | 85 | .init_uarts = exynos4_init_uarts, |
83 | .init = s5pv310_init, | 86 | .init = exynos4_init, |
84 | .name = name_s5pv310, | 87 | .name = name_exynos4210, |
85 | }, | 88 | }, |
86 | }; | 89 | }; |
87 | 90 | ||
diff --git a/arch/arm/plat-s5p/dev-csis0.c b/arch/arm/plat-s5p/dev-csis0.c index dfab1c85f54f..e3aabef5e347 100644 --- a/arch/arm/plat-s5p/dev-csis0.c +++ b/arch/arm/plat-s5p/dev-csis0.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2010 Samsung Electronics | 2 | * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. |
3 | * | 3 | * |
4 | * S5P series device definition for MIPI-CSIS channel 0 | 4 | * S5P series device definition for MIPI-CSIS channel 0 |
5 | * | 5 | * |
diff --git a/arch/arm/plat-s5p/dev-csis1.c b/arch/arm/plat-s5p/dev-csis1.c index e3053f27fbbf..08b91b580207 100644 --- a/arch/arm/plat-s5p/dev-csis1.c +++ b/arch/arm/plat-s5p/dev-csis1.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2010 Samsung Electronics | 2 | * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. |
3 | * | 3 | * |
4 | * S5P series device definition for MIPI-CSIS channel 1 | 4 | * S5P series device definition for MIPI-CSIS channel 1 |
5 | * | 5 | * |
diff --git a/arch/arm/plat-s5p/dev-fimc3.c b/arch/arm/plat-s5p/dev-fimc3.c new file mode 100644 index 000000000000..ef31beca386c --- /dev/null +++ b/arch/arm/plat-s5p/dev-fimc3.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* linux/arch/arm/plat-s5p/dev-fimc3.c | ||
2 | * | ||
3 | * Copyright (c) 2010 Samsung Electronics | ||
4 | * | ||
5 | * Base S5P FIMC3 resource and device definitions | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/dma-mapping.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/ioport.h> | ||
17 | #include <mach/map.h> | ||
18 | |||
19 | static struct resource s5p_fimc3_resource[] = { | ||
20 | [0] = { | ||
21 | .start = S5P_PA_FIMC3, | ||
22 | .end = S5P_PA_FIMC3 + SZ_4K - 1, | ||
23 | .flags = IORESOURCE_MEM, | ||
24 | }, | ||
25 | [1] = { | ||
26 | .start = IRQ_FIMC3, | ||
27 | .end = IRQ_FIMC3, | ||
28 | .flags = IORESOURCE_IRQ, | ||
29 | }, | ||
30 | }; | ||
31 | |||
32 | static u64 s5p_fimc3_dma_mask = DMA_BIT_MASK(32); | ||
33 | |||
34 | struct platform_device s5p_device_fimc3 = { | ||
35 | .name = "s5p-fimc", | ||
36 | .id = 3, | ||
37 | .num_resources = ARRAY_SIZE(s5p_fimc3_resource), | ||
38 | .resource = s5p_fimc3_resource, | ||
39 | .dev = { | ||
40 | .dma_mask = &s5p_fimc3_dma_mask, | ||
41 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
42 | }, | ||
43 | }; | ||
diff --git a/arch/arm/plat-s5p/include/plat/camport.h b/arch/arm/plat-s5p/include/plat/camport.h new file mode 100644 index 000000000000..71688c8ba288 --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/camport.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * S5P series camera interface helper functions | ||
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 | #ifndef PLAT_S5P_CAMPORT_H_ | ||
12 | #define PLAT_S5P_CAMPORT_H_ __FILE__ | ||
13 | |||
14 | enum s5p_camport_id { | ||
15 | S5P_CAMPORT_A, | ||
16 | S5P_CAMPORT_B, | ||
17 | }; | ||
18 | |||
19 | /* | ||
20 | * The helper functions to configure GPIO for the camera parallel bus. | ||
21 | * The camera port can be multiplexed with any FIMC entity, even multiple | ||
22 | * FIMC entities are allowed to be attached to a single port simultaneously. | ||
23 | * These functions are to be used in the board setup code. | ||
24 | */ | ||
25 | int s5pv210_fimc_setup_gpio(enum s5p_camport_id id); | ||
26 | int exynos4_fimc_setup_gpio(enum s5p_camport_id id); | ||
27 | |||
28 | #endif | ||
diff --git a/arch/arm/plat-s5p/include/plat/csis.h b/arch/arm/plat-s5p/include/plat/csis.h deleted file mode 100644 index 51e308c7981d..000000000000 --- a/arch/arm/plat-s5p/include/plat/csis.h +++ /dev/null | |||
@@ -1,28 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Samsung Electronics | ||
3 | * | ||
4 | * S5P series MIPI CSI slave device support | ||
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 | #ifndef PLAT_S5P_CSIS_H_ | ||
12 | #define PLAT_S5P_CSIS_H_ __FILE__ | ||
13 | |||
14 | /** | ||
15 | * struct s5p_platform_mipi_csis - platform data for MIPI-CSIS | ||
16 | * @clk_rate: bus clock frequency | ||
17 | * @lanes: number of data lanes used | ||
18 | * @alignment: data alignment in bits | ||
19 | * @hs_settle: HS-RX settle time | ||
20 | */ | ||
21 | struct s5p_platform_mipi_csis { | ||
22 | unsigned long clk_rate; | ||
23 | u8 lanes; | ||
24 | u8 alignment; | ||
25 | u8 hs_settle; | ||
26 | }; | ||
27 | |||
28 | #endif /* PLAT_S5P_CSIS_H_ */ | ||
diff --git a/arch/arm/plat-s5p/include/plat/exynos4.h b/arch/arm/plat-s5p/include/plat/exynos4.h new file mode 100644 index 000000000000..907caab53dcf --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/exynos4.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* linux/arch/arm/plat-s5p/include/plat/exynos4.h | ||
2 | * | ||
3 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com | ||
5 | * | ||
6 | * Header file for exynos4 cpu support | ||
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 | /* Common init code for EXYNOS4 related SoCs */ | ||
14 | |||
15 | extern void exynos4_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); | ||
16 | extern void exynos4_register_clocks(void); | ||
17 | extern void exynos4_setup_clocks(void); | ||
18 | |||
19 | #ifdef CONFIG_CPU_EXYNOS4210 | ||
20 | |||
21 | extern int exynos4_init(void); | ||
22 | extern void exynos4_init_irq(void); | ||
23 | extern void exynos4_map_io(void); | ||
24 | extern void exynos4_init_clocks(int xtal); | ||
25 | extern struct sys_timer exynos4_timer; | ||
26 | |||
27 | #define exynos4_init_uarts exynos4_common_init_uarts | ||
28 | |||
29 | #else | ||
30 | #define exynos4_init_clocks NULL | ||
31 | #define exynos4_init_uarts NULL | ||
32 | #define exynos4_map_io NULL | ||
33 | #define exynos4_init NULL | ||
34 | #endif | ||
diff --git a/arch/arm/plat-s5p/include/plat/mipi_csis.h b/arch/arm/plat-s5p/include/plat/mipi_csis.h new file mode 100644 index 000000000000..9bd254c5ed22 --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/mipi_csis.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * S5P series MIPI CSI slave device support | ||
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 | #ifndef PLAT_S5P_MIPI_CSIS_H_ | ||
12 | #define PLAT_S5P_MIPI_CSIS_H_ __FILE__ | ||
13 | |||
14 | struct platform_device; | ||
15 | |||
16 | /** | ||
17 | * struct s5p_platform_mipi_csis - platform data for S5P MIPI-CSIS driver | ||
18 | * @clk_rate: bus clock frequency | ||
19 | * @lanes: number of data lanes used | ||
20 | * @alignment: data alignment in bits | ||
21 | * @hs_settle: HS-RX settle time | ||
22 | * @fixed_phy_vdd: false to enable external D-PHY regulator management in the | ||
23 | * driver or true in case this regulator has no enable function | ||
24 | * @phy_enable: pointer to a callback controlling D-PHY enable/reset | ||
25 | */ | ||
26 | struct s5p_platform_mipi_csis { | ||
27 | unsigned long clk_rate; | ||
28 | u8 lanes; | ||
29 | u8 alignment; | ||
30 | u8 hs_settle; | ||
31 | bool fixed_phy_vdd; | ||
32 | int (*phy_enable)(struct platform_device *pdev, bool on); | ||
33 | }; | ||
34 | |||
35 | /** | ||
36 | * s5p_csis_phy_enable - global MIPI-CSI receiver D-PHY control | ||
37 | * @pdev: MIPI-CSIS platform device | ||
38 | * @on: true to enable D-PHY and deassert its reset | ||
39 | * false to disable D-PHY | ||
40 | */ | ||
41 | int s5p_csis_phy_enable(struct platform_device *pdev, bool on); | ||
42 | |||
43 | #endif /* PLAT_S5P_MIPI_CSIS_H_ */ | ||
diff --git a/arch/arm/plat-s5p/include/plat/s5p-time.h b/arch/arm/plat-s5p/include/plat/s5p-time.h new file mode 100644 index 000000000000..575e88109db8 --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/s5p-time.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /* linux/arch/arm/plat-s5p/include/plat/s5p-time.h | ||
2 | * | ||
3 | * Copyright 2011 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * | ||
6 | * Header file for s5p time support | ||
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_TIME_H | ||
14 | #define __ASM_PLAT_S5P_TIME_H __FILE__ | ||
15 | |||
16 | /* S5P HR-Timer Clock mode */ | ||
17 | enum s5p_timer_mode { | ||
18 | S5P_PWM0, | ||
19 | S5P_PWM1, | ||
20 | S5P_PWM2, | ||
21 | S5P_PWM3, | ||
22 | S5P_PWM4, | ||
23 | }; | ||
24 | |||
25 | struct s5p_timer_source { | ||
26 | unsigned int event_id; | ||
27 | unsigned int source_id; | ||
28 | }; | ||
29 | |||
30 | /* Be able to sleep for atleast 4 seconds (usually more) */ | ||
31 | #define S5PTIMER_MIN_RANGE 4 | ||
32 | |||
33 | #define TCNT_MAX 0xffffffff | ||
34 | #define NON_PERIODIC 0 | ||
35 | #define PERIODIC 1 | ||
36 | |||
37 | extern void __init s5p_set_timer_source(enum s5p_timer_mode event, | ||
38 | enum s5p_timer_mode source); | ||
39 | extern struct sys_timer s5p_timer; | ||
40 | #endif /* __ASM_PLAT_S5P_TIME_H */ | ||
diff --git a/arch/arm/plat-s5p/include/plat/s5pv310.h b/arch/arm/plat-s5p/include/plat/s5pv310.h deleted file mode 100644 index 769c991ceb37..000000000000 --- a/arch/arm/plat-s5p/include/plat/s5pv310.h +++ /dev/null | |||
@@ -1,34 +0,0 @@ | |||
1 | /* linux/arch/arm/plat-s5p/include/plat/s5pv310.h | ||
2 | * | ||
3 | * Copyright (c) 2010 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * | ||
6 | * Header file for s5pv310 cpu support | ||
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 | /* Common init code for S5PV310 related SoCs */ | ||
14 | |||
15 | extern void s5pv310_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); | ||
16 | extern void s5pv310_register_clocks(void); | ||
17 | extern void s5pv310_setup_clocks(void); | ||
18 | |||
19 | #ifdef CONFIG_CPU_S5PV310 | ||
20 | |||
21 | extern int s5pv310_init(void); | ||
22 | extern void s5pv310_init_irq(void); | ||
23 | extern void s5pv310_map_io(void); | ||
24 | extern void s5pv310_init_clocks(int xtal); | ||
25 | extern struct sys_timer s5pv310_timer; | ||
26 | |||
27 | #define s5pv310_init_uarts s5pv310_common_init_uarts | ||
28 | |||
29 | #else | ||
30 | #define s5pv310_init_clocks NULL | ||
31 | #define s5pv310_init_uarts NULL | ||
32 | #define s5pv310_map_io NULL | ||
33 | #define s5pv310_init NULL | ||
34 | #endif | ||
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..bf5283c2a19d --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/sysmmu.h | |||
@@ -0,0 +1,95 @@ | |||
1 | /* linux/arch/arm/plat-s5p/include/plat/sysmmu.h | ||
2 | * | ||
3 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com | ||
5 | * | ||
6 | * Samsung System MMU driver for S5P platform | ||
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_SYSMMU_H | ||
14 | #define __ASM__PLAT_SYSMMU_H __FILE__ | ||
15 | |||
16 | enum S5P_SYSMMU_INTERRUPT_TYPE { | ||
17 | SYSMMU_PAGEFAULT, | ||
18 | SYSMMU_AR_MULTIHIT, | ||
19 | SYSMMU_AW_MULTIHIT, | ||
20 | SYSMMU_BUSERROR, | ||
21 | SYSMMU_AR_SECURITY, | ||
22 | SYSMMU_AR_ACCESS, | ||
23 | SYSMMU_AW_SECURITY, | ||
24 | SYSMMU_AW_PROTECTION, /* 7 */ | ||
25 | SYSMMU_FAULTS_NUM | ||
26 | }; | ||
27 | |||
28 | #ifdef CONFIG_S5P_SYSTEM_MMU | ||
29 | |||
30 | #include <mach/sysmmu.h> | ||
31 | |||
32 | /** | ||
33 | * s5p_sysmmu_enable() - enable system mmu of ip | ||
34 | * @ips: The ip connected system mmu. | ||
35 | * #pgd: Base physical address of the 1st level page table | ||
36 | * | ||
37 | * This function enable system mmu to transfer address | ||
38 | * from virtual address to physical address | ||
39 | */ | ||
40 | void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd); | ||
41 | |||
42 | /** | ||
43 | * s5p_sysmmu_disable() - disable sysmmu mmu of ip | ||
44 | * @ips: The ip connected system mmu. | ||
45 | * | ||
46 | * This function disable system mmu to transfer address | ||
47 | * from virtual address to physical address | ||
48 | */ | ||
49 | void s5p_sysmmu_disable(sysmmu_ips ips); | ||
50 | |||
51 | /** | ||
52 | * s5p_sysmmu_set_tablebase_pgd() - set page table base address to refer page table | ||
53 | * @ips: The ip connected system mmu. | ||
54 | * @pgd: The page table base address. | ||
55 | * | ||
56 | * This function set page table base address | ||
57 | * When system mmu transfer address from virtaul address to physical address, | ||
58 | * system mmu refer address information from page table | ||
59 | */ | ||
60 | void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd); | ||
61 | |||
62 | /** | ||
63 | * s5p_sysmmu_tlb_invalidate() - flush all TLB entry in system mmu | ||
64 | * @ips: The ip connected system mmu. | ||
65 | * | ||
66 | * This function flush all TLB entry in system mmu | ||
67 | */ | ||
68 | void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips); | ||
69 | |||
70 | /** s5p_sysmmu_set_fault_handler() - Fault handler for System MMUs | ||
71 | * @itype: type of fault. | ||
72 | * @pgtable_base: the physical address of page table base. This is 0 if @ips is | ||
73 | * SYSMMU_BUSERROR. | ||
74 | * @fault_addr: the device (virtual) address that the System MMU tried to | ||
75 | * translated. This is 0 if @ips is SYSMMU_BUSERROR. | ||
76 | * Called when interrupt occurred by the System MMUs | ||
77 | * The device drivers of peripheral devices that has a System MMU can implement | ||
78 | * a fault handler to resolve address translation fault by System MMU. | ||
79 | * The meanings of return value and parameters are described below. | ||
80 | |||
81 | * return value: non-zero if the fault is correctly resolved. | ||
82 | * zero if the fault is not handled. | ||
83 | */ | ||
84 | void s5p_sysmmu_set_fault_handler(sysmmu_ips ips, | ||
85 | int (*handler)(enum S5P_SYSMMU_INTERRUPT_TYPE itype, | ||
86 | unsigned long pgtable_base, | ||
87 | unsigned long fault_addr)); | ||
88 | #else | ||
89 | #define s5p_sysmmu_enable(ips, pgd) do { } while (0) | ||
90 | #define s5p_sysmmu_disable(ips) do { } while (0) | ||
91 | #define s5p_sysmmu_set_tablebase_pgd(ips, pgd) do { } while (0) | ||
92 | #define s5p_sysmmu_tlb_invalidate(ips) do { } while (0) | ||
93 | #define s5p_sysmmu_set_fault_handler(ips, handler) do { } while (0) | ||
94 | #endif | ||
95 | #endif /* __ASM_PLAT_SYSMMU_H */ | ||
diff --git a/arch/arm/plat-s5p/irq-gpioint.c b/arch/arm/plat-s5p/irq-gpioint.c index 3b6bf89d1739..cd87d3256e03 100644 --- a/arch/arm/plat-s5p/irq-gpioint.c +++ b/arch/arm/plat-s5p/irq-gpioint.c | |||
@@ -17,82 +17,79 @@ | |||
17 | #include <linux/irq.h> | 17 | #include <linux/irq.h> |
18 | #include <linux/io.h> | 18 | #include <linux/io.h> |
19 | #include <linux/gpio.h> | 19 | #include <linux/gpio.h> |
20 | #include <linux/slab.h> | ||
20 | 21 | ||
21 | #include <mach/map.h> | 22 | #include <mach/map.h> |
22 | #include <plat/gpio-core.h> | 23 | #include <plat/gpio-core.h> |
23 | #include <plat/gpio-cfg.h> | 24 | #include <plat/gpio-cfg.h> |
24 | 25 | ||
25 | #define S5P_GPIOREG(x) (S5P_VA_GPIO + (x)) | 26 | #define GPIO_BASE(chip) (((unsigned long)(chip)->base) & 0xFFFFF000u) |
26 | 27 | ||
27 | #define GPIOINT_CON_OFFSET 0x700 | 28 | #define CON_OFFSET 0x700 |
28 | #define GPIOINT_MASK_OFFSET 0x900 | 29 | #define MASK_OFFSET 0x900 |
29 | #define GPIOINT_PEND_OFFSET 0xA00 | 30 | #define PEND_OFFSET 0xA00 |
31 | #define REG_OFFSET(x) ((x) << 2) | ||
30 | 32 | ||
31 | static struct s3c_gpio_chip *irq_chips[S5P_GPIOINT_GROUP_MAXNR]; | 33 | struct s5p_gpioint_bank { |
32 | 34 | struct list_head list; | |
33 | static int s5p_gpioint_get_group(struct irq_data *data) | 35 | int start; |
34 | { | 36 | int nr_groups; |
35 | struct gpio_chip *chip = irq_data_get_irq_data(data); | 37 | int irq; |
36 | struct s3c_gpio_chip *s3c_chip = container_of(chip, | 38 | struct s3c_gpio_chip **chips; |
37 | struct s3c_gpio_chip, chip); | 39 | void (*handler)(unsigned int, struct irq_desc *); |
38 | int group; | 40 | }; |
39 | |||
40 | for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++) | ||
41 | if (s3c_chip == irq_chips[group]) | ||
42 | break; | ||
43 | 41 | ||
44 | return group; | 42 | LIST_HEAD(banks); |
45 | } | ||
46 | 43 | ||
47 | static int s5p_gpioint_get_offset(struct irq_data *data) | 44 | static int s5p_gpioint_get_offset(struct irq_data *data) |
48 | { | 45 | { |
49 | struct gpio_chip *chip = irq_data_get_irq_data(data); | 46 | struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); |
50 | struct s3c_gpio_chip *s3c_chip = container_of(chip, | 47 | return data->irq - chip->irq_base; |
51 | struct s3c_gpio_chip, chip); | ||
52 | |||
53 | return data->irq - s3c_chip->irq_base; | ||
54 | } | 48 | } |
55 | 49 | ||
56 | static void s5p_gpioint_ack(struct irq_data *data) | 50 | static void s5p_gpioint_ack(struct irq_data *data) |
57 | { | 51 | { |
52 | struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); | ||
58 | int group, offset, pend_offset; | 53 | int group, offset, pend_offset; |
59 | unsigned int value; | 54 | unsigned int value; |
60 | 55 | ||
61 | group = s5p_gpioint_get_group(data); | 56 | group = chip->group; |
62 | offset = s5p_gpioint_get_offset(data); | 57 | offset = s5p_gpioint_get_offset(data); |
63 | pend_offset = group << 2; | 58 | pend_offset = REG_OFFSET(group); |
64 | 59 | ||
65 | value = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) + pend_offset); | 60 | value = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset); |
66 | value |= 1 << offset; | 61 | value |= BIT(offset); |
67 | __raw_writel(value, S5P_GPIOREG(GPIOINT_PEND_OFFSET) + pend_offset); | 62 | __raw_writel(value, GPIO_BASE(chip) + PEND_OFFSET + pend_offset); |
68 | } | 63 | } |
69 | 64 | ||
70 | static void s5p_gpioint_mask(struct irq_data *data) | 65 | static void s5p_gpioint_mask(struct irq_data *data) |
71 | { | 66 | { |
67 | struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); | ||
72 | int group, offset, mask_offset; | 68 | int group, offset, mask_offset; |
73 | unsigned int value; | 69 | unsigned int value; |
74 | 70 | ||
75 | group = s5p_gpioint_get_group(data); | 71 | group = chip->group; |
76 | offset = s5p_gpioint_get_offset(data); | 72 | offset = s5p_gpioint_get_offset(data); |
77 | mask_offset = group << 2; | 73 | mask_offset = REG_OFFSET(group); |
78 | 74 | ||
79 | value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); | 75 | value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset); |
80 | value |= 1 << offset; | 76 | value |= BIT(offset); |
81 | __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); | 77 | __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + mask_offset); |
82 | } | 78 | } |
83 | 79 | ||
84 | static void s5p_gpioint_unmask(struct irq_data *data) | 80 | static void s5p_gpioint_unmask(struct irq_data *data) |
85 | { | 81 | { |
82 | struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); | ||
86 | int group, offset, mask_offset; | 83 | int group, offset, mask_offset; |
87 | unsigned int value; | 84 | unsigned int value; |
88 | 85 | ||
89 | group = s5p_gpioint_get_group(data); | 86 | group = chip->group; |
90 | offset = s5p_gpioint_get_offset(data); | 87 | offset = s5p_gpioint_get_offset(data); |
91 | mask_offset = group << 2; | 88 | mask_offset = REG_OFFSET(group); |
92 | 89 | ||
93 | value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); | 90 | value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset); |
94 | value &= ~(1 << offset); | 91 | value &= ~BIT(offset); |
95 | __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); | 92 | __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + mask_offset); |
96 | } | 93 | } |
97 | 94 | ||
98 | static void s5p_gpioint_mask_ack(struct irq_data *data) | 95 | static void s5p_gpioint_mask_ack(struct irq_data *data) |
@@ -103,12 +100,13 @@ static void s5p_gpioint_mask_ack(struct irq_data *data) | |||
103 | 100 | ||
104 | static int s5p_gpioint_set_type(struct irq_data *data, unsigned int type) | 101 | static int s5p_gpioint_set_type(struct irq_data *data, unsigned int type) |
105 | { | 102 | { |
103 | struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); | ||
106 | int group, offset, con_offset; | 104 | int group, offset, con_offset; |
107 | unsigned int value; | 105 | unsigned int value; |
108 | 106 | ||
109 | group = s5p_gpioint_get_group(data); | 107 | group = chip->group; |
110 | offset = s5p_gpioint_get_offset(data); | 108 | offset = s5p_gpioint_get_offset(data); |
111 | con_offset = group << 2; | 109 | con_offset = REG_OFFSET(group); |
112 | 110 | ||
113 | switch (type) { | 111 | switch (type) { |
114 | case IRQ_TYPE_EDGE_RISING: | 112 | case IRQ_TYPE_EDGE_RISING: |
@@ -132,15 +130,15 @@ static int s5p_gpioint_set_type(struct irq_data *data, unsigned int type) | |||
132 | return -EINVAL; | 130 | return -EINVAL; |
133 | } | 131 | } |
134 | 132 | ||
135 | value = __raw_readl(S5P_GPIOREG(GPIOINT_CON_OFFSET) + con_offset); | 133 | value = __raw_readl(GPIO_BASE(chip) + CON_OFFSET + con_offset); |
136 | value &= ~(0x7 << (offset * 0x4)); | 134 | value &= ~(0x7 << (offset * 0x4)); |
137 | value |= (type << (offset * 0x4)); | 135 | value |= (type << (offset * 0x4)); |
138 | __raw_writel(value, S5P_GPIOREG(GPIOINT_CON_OFFSET) + con_offset); | 136 | __raw_writel(value, GPIO_BASE(chip) + CON_OFFSET + con_offset); |
139 | 137 | ||
140 | return 0; | 138 | return 0; |
141 | } | 139 | } |
142 | 140 | ||
143 | struct irq_chip s5p_gpioint = { | 141 | static struct irq_chip s5p_gpioint = { |
144 | .name = "s5p_gpioint", | 142 | .name = "s5p_gpioint", |
145 | .irq_ack = s5p_gpioint_ack, | 143 | .irq_ack = s5p_gpioint_ack, |
146 | .irq_mask = s5p_gpioint_mask, | 144 | .irq_mask = s5p_gpioint_mask, |
@@ -151,30 +149,29 @@ struct irq_chip s5p_gpioint = { | |||
151 | 149 | ||
152 | static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) | 150 | static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) |
153 | { | 151 | { |
154 | int group, offset, pend_offset, mask_offset; | 152 | struct s5p_gpioint_bank *bank = get_irq_data(irq); |
155 | int real_irq; | 153 | int group, pend_offset, mask_offset; |
156 | unsigned int pend, mask; | 154 | unsigned int pend, mask; |
157 | 155 | ||
158 | for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++) { | 156 | for (group = 0; group < bank->nr_groups; group++) { |
159 | pend_offset = group << 2; | 157 | struct s3c_gpio_chip *chip = bank->chips[group]; |
160 | pend = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) + | 158 | if (!chip) |
161 | pend_offset); | 159 | continue; |
160 | |||
161 | pend_offset = REG_OFFSET(group); | ||
162 | pend = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset); | ||
162 | if (!pend) | 163 | if (!pend) |
163 | continue; | 164 | continue; |
164 | 165 | ||
165 | mask_offset = group << 2; | 166 | mask_offset = REG_OFFSET(group); |
166 | mask = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + | 167 | mask = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset); |
167 | mask_offset); | ||
168 | pend &= ~mask; | 168 | pend &= ~mask; |
169 | 169 | ||
170 | for (offset = 0; offset < 8; offset++) { | 170 | while (pend) { |
171 | if (pend & (1 << offset)) { | 171 | int offset = fls(pend) - 1; |
172 | struct s3c_gpio_chip *chip = irq_chips[group]; | 172 | int real_irq = chip->irq_base + offset; |
173 | if (chip) { | 173 | generic_handle_irq(real_irq); |
174 | real_irq = chip->irq_base + offset; | 174 | pend &= ~BIT(offset); |
175 | generic_handle_irq(real_irq); | ||
176 | } | ||
177 | } | ||
178 | } | 175 | } |
179 | } | 176 | } |
180 | } | 177 | } |
@@ -182,27 +179,48 @@ static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) | |||
182 | static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip) | 179 | static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip) |
183 | { | 180 | { |
184 | static int used_gpioint_groups = 0; | 181 | static int used_gpioint_groups = 0; |
185 | static bool handler_registered = 0; | ||
186 | int irq, group = chip->group; | 182 | int irq, group = chip->group; |
187 | int i; | 183 | int i; |
184 | struct s5p_gpioint_bank *bank = NULL; | ||
188 | 185 | ||
189 | if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT) | 186 | if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT) |
190 | return -ENOMEM; | 187 | return -ENOMEM; |
191 | 188 | ||
189 | list_for_each_entry(bank, &banks, list) { | ||
190 | if (group >= bank->start && | ||
191 | group < bank->start + bank->nr_groups) | ||
192 | break; | ||
193 | } | ||
194 | if (!bank) | ||
195 | return -EINVAL; | ||
196 | |||
197 | if (!bank->handler) { | ||
198 | bank->chips = kzalloc(sizeof(struct s3c_gpio_chip *) * | ||
199 | bank->nr_groups, GFP_KERNEL); | ||
200 | if (!bank->chips) | ||
201 | return -ENOMEM; | ||
202 | |||
203 | set_irq_chained_handler(bank->irq, s5p_gpioint_handler); | ||
204 | set_irq_data(bank->irq, bank); | ||
205 | bank->handler = s5p_gpioint_handler; | ||
206 | printk(KERN_INFO "Registered chained gpio int handler for interrupt %d.\n", | ||
207 | bank->irq); | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * chained GPIO irq has been sucessfully registered, allocate new gpio | ||
212 | * int group and assign irq nubmers | ||
213 | */ | ||
214 | |||
192 | chip->irq_base = S5P_GPIOINT_BASE + | 215 | chip->irq_base = S5P_GPIOINT_BASE + |
193 | used_gpioint_groups * S5P_GPIOINT_GROUP_SIZE; | 216 | used_gpioint_groups * S5P_GPIOINT_GROUP_SIZE; |
194 | used_gpioint_groups++; | 217 | used_gpioint_groups++; |
195 | 218 | ||
196 | if (!handler_registered) { | 219 | bank->chips[group - bank->start] = chip; |
197 | set_irq_chained_handler(IRQ_GPIOINT, s5p_gpioint_handler); | ||
198 | handler_registered = 1; | ||
199 | } | ||
200 | |||
201 | irq_chips[group] = chip; | ||
202 | for (i = 0; i < chip->chip.ngpio; i++) { | 220 | for (i = 0; i < chip->chip.ngpio; i++) { |
203 | irq = chip->irq_base + i; | 221 | irq = chip->irq_base + i; |
204 | set_irq_chip(irq, &s5p_gpioint); | 222 | set_irq_chip(irq, &s5p_gpioint); |
205 | set_irq_data(irq, &chip->chip); | 223 | set_irq_data(irq, chip); |
206 | set_irq_handler(irq, handle_level_irq); | 224 | set_irq_handler(irq, handle_level_irq); |
207 | set_irq_flags(irq, IRQF_VALID); | 225 | set_irq_flags(irq, IRQF_VALID); |
208 | } | 226 | } |
@@ -235,3 +253,19 @@ int __init s5p_register_gpio_interrupt(int pin) | |||
235 | } | 253 | } |
236 | return ret; | 254 | return ret; |
237 | } | 255 | } |
256 | |||
257 | int __init s5p_register_gpioint_bank(int chain_irq, int start, int nr_groups) | ||
258 | { | ||
259 | struct s5p_gpioint_bank *bank; | ||
260 | |||
261 | bank = kzalloc(sizeof(*bank), GFP_KERNEL); | ||
262 | if (!bank) | ||
263 | return -ENOMEM; | ||
264 | |||
265 | bank->start = start; | ||
266 | bank->nr_groups = nr_groups; | ||
267 | bank->irq = chain_irq; | ||
268 | |||
269 | list_add_tail(&bank->list, &banks); | ||
270 | return 0; | ||
271 | } | ||
diff --git a/arch/arm/plat-s5p/s5p-time.c b/arch/arm/plat-s5p/s5p-time.c new file mode 100644 index 000000000000..8090403eec0f --- /dev/null +++ b/arch/arm/plat-s5p/s5p-time.c | |||
@@ -0,0 +1,448 @@ | |||
1 | /* linux/arch/arm/plat-s5p/s5p-time.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * | ||
6 | * S5P - Common hr-timer support | ||
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 | #include <linux/sched.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/irq.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/clk.h> | ||
18 | #include <linux/clockchips.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | |||
21 | #include <asm/smp_twd.h> | ||
22 | #include <asm/mach/time.h> | ||
23 | #include <asm/mach/arch.h> | ||
24 | #include <asm/mach/map.h> | ||
25 | #include <asm/sched_clock.h> | ||
26 | |||
27 | #include <mach/map.h> | ||
28 | #include <plat/devs.h> | ||
29 | #include <plat/regs-timer.h> | ||
30 | #include <plat/s5p-time.h> | ||
31 | |||
32 | static struct clk *tin_event; | ||
33 | static struct clk *tin_source; | ||
34 | static struct clk *tdiv_event; | ||
35 | static struct clk *tdiv_source; | ||
36 | static struct clk *timerclk; | ||
37 | static struct s5p_timer_source timer_source; | ||
38 | static unsigned long clock_count_per_tick; | ||
39 | static void s5p_timer_resume(void); | ||
40 | |||
41 | static void s5p_time_stop(enum s5p_timer_mode mode) | ||
42 | { | ||
43 | unsigned long tcon; | ||
44 | |||
45 | tcon = __raw_readl(S3C2410_TCON); | ||
46 | |||
47 | switch (mode) { | ||
48 | case S5P_PWM0: | ||
49 | tcon &= ~S3C2410_TCON_T0START; | ||
50 | break; | ||
51 | |||
52 | case S5P_PWM1: | ||
53 | tcon &= ~S3C2410_TCON_T1START; | ||
54 | break; | ||
55 | |||
56 | case S5P_PWM2: | ||
57 | tcon &= ~S3C2410_TCON_T2START; | ||
58 | break; | ||
59 | |||
60 | case S5P_PWM3: | ||
61 | tcon &= ~S3C2410_TCON_T3START; | ||
62 | break; | ||
63 | |||
64 | case S5P_PWM4: | ||
65 | tcon &= ~S3C2410_TCON_T4START; | ||
66 | break; | ||
67 | |||
68 | default: | ||
69 | printk(KERN_ERR "Invalid Timer %d\n", mode); | ||
70 | break; | ||
71 | } | ||
72 | __raw_writel(tcon, S3C2410_TCON); | ||
73 | } | ||
74 | |||
75 | static void s5p_time_setup(enum s5p_timer_mode mode, unsigned long tcnt) | ||
76 | { | ||
77 | unsigned long tcon; | ||
78 | |||
79 | tcon = __raw_readl(S3C2410_TCON); | ||
80 | |||
81 | tcnt--; | ||
82 | |||
83 | switch (mode) { | ||
84 | case S5P_PWM0: | ||
85 | tcon &= ~(0x0f << 0); | ||
86 | tcon |= S3C2410_TCON_T0MANUALUPD; | ||
87 | break; | ||
88 | |||
89 | case S5P_PWM1: | ||
90 | tcon &= ~(0x0f << 8); | ||
91 | tcon |= S3C2410_TCON_T1MANUALUPD; | ||
92 | break; | ||
93 | |||
94 | case S5P_PWM2: | ||
95 | tcon &= ~(0x0f << 12); | ||
96 | tcon |= S3C2410_TCON_T2MANUALUPD; | ||
97 | break; | ||
98 | |||
99 | case S5P_PWM3: | ||
100 | tcon &= ~(0x0f << 16); | ||
101 | tcon |= S3C2410_TCON_T3MANUALUPD; | ||
102 | break; | ||
103 | |||
104 | case S5P_PWM4: | ||
105 | tcon &= ~(0x07 << 20); | ||
106 | tcon |= S3C2410_TCON_T4MANUALUPD; | ||
107 | break; | ||
108 | |||
109 | default: | ||
110 | printk(KERN_ERR "Invalid Timer %d\n", mode); | ||
111 | break; | ||
112 | } | ||
113 | |||
114 | __raw_writel(tcnt, S3C2410_TCNTB(mode)); | ||
115 | __raw_writel(tcnt, S3C2410_TCMPB(mode)); | ||
116 | __raw_writel(tcon, S3C2410_TCON); | ||
117 | } | ||
118 | |||
119 | static void s5p_time_start(enum s5p_timer_mode mode, bool periodic) | ||
120 | { | ||
121 | unsigned long tcon; | ||
122 | |||
123 | tcon = __raw_readl(S3C2410_TCON); | ||
124 | |||
125 | switch (mode) { | ||
126 | case S5P_PWM0: | ||
127 | tcon |= S3C2410_TCON_T0START; | ||
128 | tcon &= ~S3C2410_TCON_T0MANUALUPD; | ||
129 | |||
130 | if (periodic) | ||
131 | tcon |= S3C2410_TCON_T0RELOAD; | ||
132 | else | ||
133 | tcon &= ~S3C2410_TCON_T0RELOAD; | ||
134 | break; | ||
135 | |||
136 | case S5P_PWM1: | ||
137 | tcon |= S3C2410_TCON_T1START; | ||
138 | tcon &= ~S3C2410_TCON_T1MANUALUPD; | ||
139 | |||
140 | if (periodic) | ||
141 | tcon |= S3C2410_TCON_T1RELOAD; | ||
142 | else | ||
143 | tcon &= ~S3C2410_TCON_T1RELOAD; | ||
144 | break; | ||
145 | |||
146 | case S5P_PWM2: | ||
147 | tcon |= S3C2410_TCON_T2START; | ||
148 | tcon &= ~S3C2410_TCON_T2MANUALUPD; | ||
149 | |||
150 | if (periodic) | ||
151 | tcon |= S3C2410_TCON_T2RELOAD; | ||
152 | else | ||
153 | tcon &= ~S3C2410_TCON_T2RELOAD; | ||
154 | break; | ||
155 | |||
156 | case S5P_PWM3: | ||
157 | tcon |= S3C2410_TCON_T3START; | ||
158 | tcon &= ~S3C2410_TCON_T3MANUALUPD; | ||
159 | |||
160 | if (periodic) | ||
161 | tcon |= S3C2410_TCON_T3RELOAD; | ||
162 | else | ||
163 | tcon &= ~S3C2410_TCON_T3RELOAD; | ||
164 | break; | ||
165 | |||
166 | case S5P_PWM4: | ||
167 | tcon |= S3C2410_TCON_T4START; | ||
168 | tcon &= ~S3C2410_TCON_T4MANUALUPD; | ||
169 | |||
170 | if (periodic) | ||
171 | tcon |= S3C2410_TCON_T4RELOAD; | ||
172 | else | ||
173 | tcon &= ~S3C2410_TCON_T4RELOAD; | ||
174 | break; | ||
175 | |||
176 | default: | ||
177 | printk(KERN_ERR "Invalid Timer %d\n", mode); | ||
178 | break; | ||
179 | } | ||
180 | __raw_writel(tcon, S3C2410_TCON); | ||
181 | } | ||
182 | |||
183 | static int s5p_set_next_event(unsigned long cycles, | ||
184 | struct clock_event_device *evt) | ||
185 | { | ||
186 | s5p_time_setup(timer_source.event_id, cycles); | ||
187 | s5p_time_start(timer_source.event_id, NON_PERIODIC); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static void s5p_set_mode(enum clock_event_mode mode, | ||
193 | struct clock_event_device *evt) | ||
194 | { | ||
195 | s5p_time_stop(timer_source.event_id); | ||
196 | |||
197 | switch (mode) { | ||
198 | case CLOCK_EVT_MODE_PERIODIC: | ||
199 | s5p_time_setup(timer_source.event_id, clock_count_per_tick); | ||
200 | s5p_time_start(timer_source.event_id, PERIODIC); | ||
201 | break; | ||
202 | |||
203 | case CLOCK_EVT_MODE_ONESHOT: | ||
204 | break; | ||
205 | |||
206 | case CLOCK_EVT_MODE_UNUSED: | ||
207 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
208 | break; | ||
209 | |||
210 | case CLOCK_EVT_MODE_RESUME: | ||
211 | s5p_timer_resume(); | ||
212 | break; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | static void s5p_timer_resume(void) | ||
217 | { | ||
218 | /* event timer restart */ | ||
219 | s5p_time_setup(timer_source.event_id, clock_count_per_tick); | ||
220 | s5p_time_start(timer_source.event_id, PERIODIC); | ||
221 | |||
222 | /* source timer restart */ | ||
223 | s5p_time_setup(timer_source.source_id, TCNT_MAX); | ||
224 | s5p_time_start(timer_source.source_id, PERIODIC); | ||
225 | } | ||
226 | |||
227 | void __init s5p_set_timer_source(enum s5p_timer_mode event, | ||
228 | enum s5p_timer_mode source) | ||
229 | { | ||
230 | s3c_device_timer[event].dev.bus = &platform_bus_type; | ||
231 | s3c_device_timer[source].dev.bus = &platform_bus_type; | ||
232 | |||
233 | timer_source.event_id = event; | ||
234 | timer_source.source_id = source; | ||
235 | } | ||
236 | |||
237 | static struct clock_event_device time_event_device = { | ||
238 | .name = "s5p_event_timer", | ||
239 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
240 | .rating = 200, | ||
241 | .set_next_event = s5p_set_next_event, | ||
242 | .set_mode = s5p_set_mode, | ||
243 | }; | ||
244 | |||
245 | static irqreturn_t s5p_clock_event_isr(int irq, void *dev_id) | ||
246 | { | ||
247 | struct clock_event_device *evt = dev_id; | ||
248 | |||
249 | evt->event_handler(evt); | ||
250 | |||
251 | return IRQ_HANDLED; | ||
252 | } | ||
253 | |||
254 | static struct irqaction s5p_clock_event_irq = { | ||
255 | .name = "s5p_time_irq", | ||
256 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
257 | .handler = s5p_clock_event_isr, | ||
258 | .dev_id = &time_event_device, | ||
259 | }; | ||
260 | |||
261 | static void __init s5p_clockevent_init(void) | ||
262 | { | ||
263 | unsigned long pclk; | ||
264 | unsigned long clock_rate; | ||
265 | unsigned int irq_number; | ||
266 | struct clk *tscaler; | ||
267 | |||
268 | pclk = clk_get_rate(timerclk); | ||
269 | |||
270 | tscaler = clk_get_parent(tdiv_event); | ||
271 | |||
272 | clk_set_rate(tscaler, pclk / 2); | ||
273 | clk_set_rate(tdiv_event, pclk / 2); | ||
274 | clk_set_parent(tin_event, tdiv_event); | ||
275 | |||
276 | clock_rate = clk_get_rate(tin_event); | ||
277 | clock_count_per_tick = clock_rate / HZ; | ||
278 | |||
279 | clockevents_calc_mult_shift(&time_event_device, | ||
280 | clock_rate, S5PTIMER_MIN_RANGE); | ||
281 | time_event_device.max_delta_ns = | ||
282 | clockevent_delta2ns(-1, &time_event_device); | ||
283 | time_event_device.min_delta_ns = | ||
284 | clockevent_delta2ns(1, &time_event_device); | ||
285 | |||
286 | time_event_device.cpumask = cpumask_of(0); | ||
287 | clockevents_register_device(&time_event_device); | ||
288 | |||
289 | irq_number = timer_source.event_id + IRQ_TIMER0; | ||
290 | setup_irq(irq_number, &s5p_clock_event_irq); | ||
291 | } | ||
292 | |||
293 | static cycle_t s5p_timer_read(struct clocksource *cs) | ||
294 | { | ||
295 | unsigned long offset = 0; | ||
296 | |||
297 | switch (timer_source.source_id) { | ||
298 | case S5P_PWM0: | ||
299 | case S5P_PWM1: | ||
300 | case S5P_PWM2: | ||
301 | case S5P_PWM3: | ||
302 | offset = (timer_source.source_id * 0x0c) + 0x14; | ||
303 | break; | ||
304 | |||
305 | case S5P_PWM4: | ||
306 | offset = 0x40; | ||
307 | break; | ||
308 | |||
309 | default: | ||
310 | printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id); | ||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | return (cycle_t) ~__raw_readl(S3C_TIMERREG(offset)); | ||
315 | } | ||
316 | |||
317 | /* | ||
318 | * Override the global weak sched_clock symbol with this | ||
319 | * local implementation which uses the clocksource to get some | ||
320 | * better resolution when scheduling the kernel. We accept that | ||
321 | * this wraps around for now, since it is just a relative time | ||
322 | * stamp. (Inspired by U300 implementation.) | ||
323 | */ | ||
324 | static DEFINE_CLOCK_DATA(cd); | ||
325 | |||
326 | unsigned long long notrace sched_clock(void) | ||
327 | { | ||
328 | u32 cyc; | ||
329 | unsigned long offset = 0; | ||
330 | |||
331 | switch (timer_source.source_id) { | ||
332 | case S5P_PWM0: | ||
333 | case S5P_PWM1: | ||
334 | case S5P_PWM2: | ||
335 | case S5P_PWM3: | ||
336 | offset = (timer_source.source_id * 0x0c) + 0x14; | ||
337 | break; | ||
338 | |||
339 | case S5P_PWM4: | ||
340 | offset = 0x40; | ||
341 | break; | ||
342 | |||
343 | default: | ||
344 | printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id); | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | cyc = ~__raw_readl(S3C_TIMERREG(offset)); | ||
349 | return cyc_to_sched_clock(&cd, cyc, (u32)~0); | ||
350 | } | ||
351 | |||
352 | static void notrace s5p_update_sched_clock(void) | ||
353 | { | ||
354 | u32 cyc; | ||
355 | unsigned long offset = 0; | ||
356 | |||
357 | switch (timer_source.source_id) { | ||
358 | case S5P_PWM0: | ||
359 | case S5P_PWM1: | ||
360 | case S5P_PWM2: | ||
361 | case S5P_PWM3: | ||
362 | offset = (timer_source.source_id * 0x0c) + 0x14; | ||
363 | break; | ||
364 | |||
365 | case S5P_PWM4: | ||
366 | offset = 0x40; | ||
367 | break; | ||
368 | |||
369 | default: | ||
370 | printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id); | ||
371 | } | ||
372 | |||
373 | cyc = ~__raw_readl(S3C_TIMERREG(offset)); | ||
374 | update_sched_clock(&cd, cyc, (u32)~0); | ||
375 | } | ||
376 | |||
377 | struct clocksource time_clocksource = { | ||
378 | .name = "s5p_clocksource_timer", | ||
379 | .rating = 250, | ||
380 | .read = s5p_timer_read, | ||
381 | .mask = CLOCKSOURCE_MASK(32), | ||
382 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
383 | }; | ||
384 | |||
385 | static void __init s5p_clocksource_init(void) | ||
386 | { | ||
387 | unsigned long pclk; | ||
388 | unsigned long clock_rate; | ||
389 | |||
390 | pclk = clk_get_rate(timerclk); | ||
391 | |||
392 | clk_set_rate(tdiv_source, pclk / 2); | ||
393 | clk_set_parent(tin_source, tdiv_source); | ||
394 | |||
395 | clock_rate = clk_get_rate(tin_source); | ||
396 | |||
397 | init_sched_clock(&cd, s5p_update_sched_clock, 32, clock_rate); | ||
398 | |||
399 | s5p_time_setup(timer_source.source_id, TCNT_MAX); | ||
400 | s5p_time_start(timer_source.source_id, PERIODIC); | ||
401 | |||
402 | if (clocksource_register_hz(&time_clocksource, clock_rate)) | ||
403 | panic("%s: can't register clocksource\n", time_clocksource.name); | ||
404 | } | ||
405 | |||
406 | static void __init s5p_timer_resources(void) | ||
407 | { | ||
408 | |||
409 | unsigned long event_id = timer_source.event_id; | ||
410 | unsigned long source_id = timer_source.source_id; | ||
411 | |||
412 | timerclk = clk_get(NULL, "timers"); | ||
413 | if (IS_ERR(timerclk)) | ||
414 | panic("failed to get timers clock for timer"); | ||
415 | |||
416 | clk_enable(timerclk); | ||
417 | |||
418 | tin_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tin"); | ||
419 | if (IS_ERR(tin_event)) | ||
420 | panic("failed to get pwm-tin clock for event timer"); | ||
421 | |||
422 | tdiv_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tdiv"); | ||
423 | if (IS_ERR(tdiv_event)) | ||
424 | panic("failed to get pwm-tdiv clock for event timer"); | ||
425 | |||
426 | clk_enable(tin_event); | ||
427 | |||
428 | tin_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tin"); | ||
429 | if (IS_ERR(tin_source)) | ||
430 | panic("failed to get pwm-tin clock for source timer"); | ||
431 | |||
432 | tdiv_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tdiv"); | ||
433 | if (IS_ERR(tdiv_source)) | ||
434 | panic("failed to get pwm-tdiv clock for source timer"); | ||
435 | |||
436 | clk_enable(tin_source); | ||
437 | } | ||
438 | |||
439 | static void __init s5p_timer_init(void) | ||
440 | { | ||
441 | s5p_timer_resources(); | ||
442 | s5p_clockevent_init(); | ||
443 | s5p_clocksource_init(); | ||
444 | } | ||
445 | |||
446 | struct sys_timer s5p_timer = { | ||
447 | .init = s5p_timer_init, | ||
448 | }; | ||
diff --git a/arch/arm/plat-s5p/setup-mipiphy.c b/arch/arm/plat-s5p/setup-mipiphy.c new file mode 100644 index 000000000000..683c466c0e6a --- /dev/null +++ b/arch/arm/plat-s5p/setup-mipiphy.c | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * S5P - Helper functions for MIPI-CSIS and MIPI-DSIM D-PHY control | ||
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/kernel.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <mach/regs-clock.h> | ||
16 | |||
17 | static int __s5p_mipi_phy_control(struct platform_device *pdev, | ||
18 | bool on, u32 reset) | ||
19 | { | ||
20 | static DEFINE_SPINLOCK(lock); | ||
21 | void __iomem *addr; | ||
22 | unsigned long flags; | ||
23 | int pid; | ||
24 | u32 cfg; | ||
25 | |||
26 | if (!pdev) | ||
27 | return -EINVAL; | ||
28 | |||
29 | pid = (pdev->id == -1) ? 0 : pdev->id; | ||
30 | |||
31 | if (pid != 0 && pid != 1) | ||
32 | return -EINVAL; | ||
33 | |||
34 | addr = S5P_MIPI_DPHY_CONTROL(pid); | ||
35 | |||
36 | spin_lock_irqsave(&lock, flags); | ||
37 | |||
38 | cfg = __raw_readl(addr); | ||
39 | cfg = on ? (cfg | reset) : (cfg & ~reset); | ||
40 | __raw_writel(cfg, addr); | ||
41 | |||
42 | if (on) { | ||
43 | cfg |= S5P_MIPI_DPHY_ENABLE; | ||
44 | } else if (!(cfg & (S5P_MIPI_DPHY_SRESETN | | ||
45 | S5P_MIPI_DPHY_MRESETN) & ~reset)) { | ||
46 | cfg &= ~S5P_MIPI_DPHY_ENABLE; | ||
47 | } | ||
48 | |||
49 | __raw_writel(cfg, addr); | ||
50 | spin_unlock_irqrestore(&lock, flags); | ||
51 | |||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | int s5p_csis_phy_enable(struct platform_device *pdev, bool on) | ||
56 | { | ||
57 | return __s5p_mipi_phy_control(pdev, on, S5P_MIPI_DPHY_SRESETN); | ||
58 | } | ||
59 | |||
60 | int s5p_dsim_phy_enable(struct platform_device *pdev, bool on) | ||
61 | { | ||
62 | return __s5p_mipi_phy_control(pdev, on, S5P_MIPI_DPHY_MRESETN); | ||
63 | } | ||
diff --git a/arch/arm/plat-s5p/sysmmu.c b/arch/arm/plat-s5p/sysmmu.c index ffe8a48bc3c1..54f5eddc921d 100644 --- a/arch/arm/plat-s5p/sysmmu.c +++ b/arch/arm/plat-s5p/sysmmu.c | |||
@@ -12,280 +12,266 @@ | |||
12 | #include <linux/interrupt.h> | 12 | #include <linux/interrupt.h> |
13 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
14 | 14 | ||
15 | #include <asm/pgtable.h> | ||
16 | |||
15 | #include <mach/map.h> | 17 | #include <mach/map.h> |
16 | #include <mach/regs-sysmmu.h> | 18 | #include <mach/regs-sysmmu.h> |
17 | #include <mach/sysmmu.h> | 19 | #include <plat/sysmmu.h> |
20 | |||
21 | #define CTRL_ENABLE 0x5 | ||
22 | #define CTRL_BLOCK 0x7 | ||
23 | #define CTRL_DISABLE 0x0 | ||
24 | |||
25 | static struct device *dev; | ||
26 | |||
27 | static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { | ||
28 | S5P_PAGE_FAULT_ADDR, | ||
29 | S5P_AR_FAULT_ADDR, | ||
30 | S5P_AW_FAULT_ADDR, | ||
31 | S5P_DEFAULT_SLAVE_ADDR, | ||
32 | S5P_AR_FAULT_ADDR, | ||
33 | S5P_AR_FAULT_ADDR, | ||
34 | S5P_AW_FAULT_ADDR, | ||
35 | S5P_AW_FAULT_ADDR | ||
36 | }; | ||
37 | |||
38 | static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { | ||
39 | "PAGE FAULT", | ||
40 | "AR MULTI-HIT FAULT", | ||
41 | "AW MULTI-HIT FAULT", | ||
42 | "BUS ERROR", | ||
43 | "AR SECURITY PROTECTION FAULT", | ||
44 | "AR ACCESS PROTECTION FAULT", | ||
45 | "AW SECURITY PROTECTION FAULT", | ||
46 | "AW ACCESS PROTECTION FAULT" | ||
47 | }; | ||
18 | 48 | ||
19 | struct sysmmu_controller s5p_sysmmu_cntlrs[S5P_SYSMMU_TOTAL_IPNUM]; | 49 | static int (*fault_handlers[S5P_SYSMMU_TOTAL_IPNUM])( |
50 | enum S5P_SYSMMU_INTERRUPT_TYPE itype, | ||
51 | unsigned long pgtable_base, | ||
52 | unsigned long fault_addr); | ||
20 | 53 | ||
21 | void s5p_sysmmu_register(struct sysmmu_controller *sysmmuconp) | 54 | /* |
55 | * If adjacent 2 bits are true, the system MMU is enabled. | ||
56 | * The system MMU is disabled, otherwise. | ||
57 | */ | ||
58 | static unsigned long sysmmu_states; | ||
59 | |||
60 | static inline void set_sysmmu_active(sysmmu_ips ips) | ||
22 | { | 61 | { |
23 | unsigned int reg_mmu_ctrl; | 62 | sysmmu_states |= 3 << (ips * 2); |
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 | } | 63 | } |
68 | 64 | ||
69 | static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id) | 65 | static inline void set_sysmmu_inactive(sysmmu_ips ips) |
70 | { | 66 | { |
71 | unsigned int i; | 67 | sysmmu_states &= ~(3 << (ips * 2)); |
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 | } | 68 | } |
87 | 69 | ||
88 | int s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd) | 70 | static inline int is_sysmmu_active(sysmmu_ips ips) |
89 | { | 71 | { |
90 | struct sysmmu_controller *sysmmuconp = NULL; | 72 | return sysmmu_states & (3 << (ips * 2)); |
91 | 73 | } | |
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 | 74 | ||
102 | if (s5p_sysmmu_tlb_invalidate(ips) != 0) | 75 | static void __iomem *sysmmusfrs[S5P_SYSMMU_TOTAL_IPNUM]; |
103 | printk(KERN_ERR "failed s5p_sysmmu_tlb_invalidate\n"); | ||
104 | 76 | ||
105 | return 0; | 77 | static inline void sysmmu_block(sysmmu_ips ips) |
78 | { | ||
79 | __raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL); | ||
80 | dev_dbg(dev, "%s is blocked.\n", sysmmu_ips_name[ips]); | ||
106 | } | 81 | } |
107 | 82 | ||
108 | static int s5p_sysmmu_set_tablebase(sysmmu_ips ips) | 83 | static inline void sysmmu_unblock(sysmmu_ips ips) |
109 | { | 84 | { |
110 | unsigned int pg; | 85 | __raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); |
111 | struct sysmmu_controller *sysmmuconp; | 86 | dev_dbg(dev, "%s is unblocked.\n", sysmmu_ips_name[ips]); |
87 | } | ||
112 | 88 | ||
113 | sysmmuconp = &s5p_sysmmu_cntlrs[ips]; | 89 | static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips) |
90 | { | ||
91 | __raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH); | ||
92 | dev_dbg(dev, "TLB of %s is invalidated.\n", sysmmu_ips_name[ips]); | ||
93 | } | ||
114 | 94 | ||
115 | if (sysmmuconp == NULL) { | 95 | static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd) |
116 | printk(KERN_ERR "failed to get ip's sysmmu info\n"); | 96 | { |
117 | return 1; | 97 | if (unlikely(pgd == 0)) { |
98 | pgd = (unsigned long)ZERO_PAGE(0); | ||
99 | __raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB LV1 */ | ||
100 | } else { | ||
101 | __raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB LV1 */ | ||
118 | } | 102 | } |
119 | 103 | ||
120 | __asm__("mrc p15, 0, %0, c2, c0, 0" \ | 104 | __raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR); |
121 | : "=r" (pg) : : "cc"); \ | ||
122 | pg &= ~0x3fff; | ||
123 | 105 | ||
124 | printk(KERN_INFO "%s: CP15 TTBR0 : 0x%x\n", __func__, pg); | 106 | dev_dbg(dev, "Page table base of %s is initialized with 0x%08lX.\n", |
125 | 107 | sysmmu_ips_name[ips], pgd); | |
126 | /* Set sysmmu page table base address */ | 108 | __sysmmu_tlb_invalidate(ips); |
127 | __raw_writel(pg, sysmmuconp->regs + S5P_PT_BASE_ADDR); | 109 | } |
128 | 110 | ||
129 | return 0; | 111 | void sysmmu_set_fault_handler(sysmmu_ips ips, |
112 | int (*handler)(enum S5P_SYSMMU_INTERRUPT_TYPE itype, | ||
113 | unsigned long pgtable_base, | ||
114 | unsigned long fault_addr)) | ||
115 | { | ||
116 | BUG_ON(!((ips >= SYSMMU_MDMA) && (ips < S5P_SYSMMU_TOTAL_IPNUM))); | ||
117 | fault_handlers[ips] = handler; | ||
130 | } | 118 | } |
131 | 119 | ||
132 | int s5p_sysmmu_enable(sysmmu_ips ips) | 120 | static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id) |
133 | { | 121 | { |
134 | unsigned int reg; | 122 | /* SYSMMU is in blocked when interrupt occurred. */ |
123 | unsigned long base = 0; | ||
124 | sysmmu_ips ips = (sysmmu_ips)dev_id; | ||
125 | enum S5P_SYSMMU_INTERRUPT_TYPE itype; | ||
135 | 126 | ||
136 | struct sysmmu_controller *sysmmuconp; | 127 | itype = (enum S5P_SYSMMU_INTERRUPT_TYPE) |
128 | __ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS)); | ||
137 | 129 | ||
138 | sysmmuconp = &s5p_sysmmu_cntlrs[ips]; | 130 | BUG_ON(!((itype >= 0) && (itype < 8))); |
139 | 131 | ||
140 | if (sysmmuconp == NULL) { | 132 | dev_alert(dev, "%s occurred by %s.\n", sysmmu_fault_name[itype], |
141 | printk(KERN_ERR "failed to get ip's sysmmu info\n"); | 133 | sysmmu_ips_name[ips]); |
142 | return 1; | ||
143 | } | ||
144 | 134 | ||
145 | s5p_sysmmu_set_tablebase(ips); | 135 | if (fault_handlers[ips]) { |
136 | unsigned long addr; | ||
146 | 137 | ||
147 | /* replacement policy : LRU */ | 138 | base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR); |
148 | reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CFG); | 139 | addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]); |
149 | reg |= 0x1; | ||
150 | __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CFG); | ||
151 | 140 | ||
152 | /* Enable interrupt, Enable MMU */ | 141 | if (fault_handlers[ips](itype, base, addr)) { |
153 | reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL); | 142 | __raw_writel(1 << itype, |
154 | reg |= (0x1 << 2) | (0x1 << 0); | 143 | sysmmusfrs[ips] + S5P_INT_CLEAR); |
144 | dev_notice(dev, "%s from %s is resolved." | ||
145 | " Retrying translation.\n", | ||
146 | sysmmu_fault_name[itype], sysmmu_ips_name[ips]); | ||
147 | } else { | ||
148 | base = 0; | ||
149 | } | ||
150 | } | ||
155 | 151 | ||
156 | __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL); | 152 | sysmmu_unblock(ips); |
157 | 153 | ||
158 | sysmmuconp->enable = true; | 154 | if (!base) |
155 | dev_notice(dev, "%s from %s is not handled.\n", | ||
156 | sysmmu_fault_name[itype], sysmmu_ips_name[ips]); | ||
159 | 157 | ||
160 | return 0; | 158 | return IRQ_HANDLED; |
161 | } | 159 | } |
162 | 160 | ||
163 | int s5p_sysmmu_disable(sysmmu_ips ips) | 161 | void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd) |
164 | { | 162 | { |
165 | unsigned int reg; | 163 | if (is_sysmmu_active(ips)) { |
166 | 164 | sysmmu_block(ips); | |
167 | struct sysmmu_controller *sysmmuconp = NULL; | 165 | __sysmmu_set_ptbase(ips, pgd); |
168 | 166 | sysmmu_unblock(ips); | |
169 | if (ips > S5P_SYSMMU_TOTAL_IPNUM) | 167 | } else { |
170 | printk(KERN_ERR "failed to get ips parameter\n"); | 168 | dev_dbg(dev, "%s is disabled. " |
171 | 169 | "Skipping initializing page table base.\n", | |
172 | sysmmuconp = &s5p_sysmmu_cntlrs[ips]; | 170 | sysmmu_ips_name[ips]); |
173 | |||
174 | if (sysmmuconp == NULL) { | ||
175 | printk(KERN_ERR "failed to get ip's sysmmu info\n"); | ||
176 | return 1; | ||
177 | } | 171 | } |
172 | } | ||
178 | 173 | ||
179 | reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CFG); | 174 | void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd) |
180 | 175 | { | |
181 | /* replacement policy : LRU */ | 176 | if (!is_sysmmu_active(ips)) { |
182 | reg |= 0x1; | 177 | sysmmu_clk_enable(ips); |
183 | __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CFG); | ||
184 | |||
185 | reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL); | ||
186 | 178 | ||
187 | /* Disable MMU */ | 179 | __sysmmu_set_ptbase(ips, pgd); |
188 | reg &= ~0x1; | ||
189 | __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL); | ||
190 | 180 | ||
191 | sysmmuconp->enable = false; | 181 | __raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); |
192 | 182 | ||
193 | return 0; | 183 | set_sysmmu_active(ips); |
184 | dev_dbg(dev, "%s is enabled.\n", sysmmu_ips_name[ips]); | ||
185 | } else { | ||
186 | dev_dbg(dev, "%s is already enabled.\n", sysmmu_ips_name[ips]); | ||
187 | } | ||
194 | } | 188 | } |
195 | 189 | ||
196 | int s5p_sysmmu_tlb_invalidate(sysmmu_ips ips) | 190 | void s5p_sysmmu_disable(sysmmu_ips ips) |
197 | { | 191 | { |
198 | unsigned int reg; | 192 | if (is_sysmmu_active(ips)) { |
199 | struct sysmmu_controller *sysmmuconp = NULL; | 193 | __raw_writel(CTRL_DISABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); |
200 | 194 | set_sysmmu_inactive(ips); | |
201 | sysmmuconp = &s5p_sysmmu_cntlrs[ips]; | 195 | sysmmu_clk_disable(ips); |
202 | 196 | dev_dbg(dev, "%s is disabled.\n", sysmmu_ips_name[ips]); | |
203 | if (sysmmuconp == NULL) { | 197 | } else { |
204 | printk(KERN_ERR "failed to get ip's sysmmu info\n"); | 198 | dev_dbg(dev, "%s is already disabled.\n", sysmmu_ips_name[ips]); |
205 | return 1; | ||
206 | } | 199 | } |
200 | } | ||
207 | 201 | ||
208 | /* set Block MMU for flush TLB */ | 202 | void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips) |
209 | reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL); | 203 | { |
210 | reg |= 0x1 << 1; | 204 | if (is_sysmmu_active(ips)) { |
211 | __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL); | 205 | sysmmu_block(ips); |
212 | 206 | __sysmmu_tlb_invalidate(ips); | |
213 | /* flush all TLB entry */ | 207 | sysmmu_unblock(ips); |
214 | __raw_writel(0x1, sysmmuconp->regs + S5P_MMU_FLUSH); | 208 | } else { |
215 | 209 | dev_dbg(dev, "%s is disabled. " | |
216 | /* set Un-block MMU after flush TLB */ | 210 | "Skipping invalidating TLB.\n", sysmmu_ips_name[ips]); |
217 | reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL); | 211 | } |
218 | reg &= ~(0x1 << 1); | ||
219 | __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL); | ||
220 | |||
221 | return 0; | ||
222 | } | 212 | } |
223 | 213 | ||
224 | static int s5p_sysmmu_probe(struct platform_device *pdev) | 214 | static int s5p_sysmmu_probe(struct platform_device *pdev) |
225 | { | 215 | { |
226 | int i; | 216 | int i, ret; |
227 | int ret; | 217 | struct resource *res, *mem; |
228 | struct resource *res; | 218 | |
229 | struct sysmmu_controller *sysmmuconp; | 219 | dev = &pdev->dev; |
230 | sysmmu_ips ips; | ||
231 | 220 | ||
232 | for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) { | 221 | for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) { |
233 | sysmmuconp = &s5p_sysmmu_cntlrs[i]; | 222 | int irq; |
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 | 223 | ||
240 | sysmmuconp->name = sysmmu_ips_name[i]; | 224 | sysmmu_clk_init(dev, i); |
225 | sysmmu_clk_disable(i); | ||
241 | 226 | ||
242 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); | 227 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); |
243 | if (!res) { | 228 | if (!res) { |
244 | printk(KERN_ERR "failed to get sysmmu resource\n"); | 229 | dev_err(dev, "Failed to get the resource of %s.\n", |
230 | sysmmu_ips_name[i]); | ||
245 | ret = -ENODEV; | 231 | ret = -ENODEV; |
246 | goto err_res; | 232 | goto err_res; |
247 | } | 233 | } |
248 | 234 | ||
249 | sysmmuconp->mem = request_mem_region(res->start, | 235 | mem = request_mem_region(res->start, |
250 | ((res->end) - (res->start)) + 1, pdev->name); | 236 | ((res->end) - (res->start)) + 1, pdev->name); |
251 | if (!sysmmuconp->mem) { | 237 | if (!mem) { |
252 | pr_err("failed to request sysmmu memory region\n"); | 238 | dev_err(dev, "Failed to request the memory region of %s.\n", |
239 | sysmmu_ips_name[i]); | ||
253 | ret = -EBUSY; | 240 | ret = -EBUSY; |
254 | goto err_res; | 241 | goto err_res; |
255 | } | 242 | } |
256 | 243 | ||
257 | sysmmuconp->regs = ioremap(res->start, res->end - res->start + 1); | 244 | sysmmusfrs[i] = ioremap(res->start, res->end - res->start + 1); |
258 | if (!sysmmuconp->regs) { | 245 | if (!sysmmusfrs[i]) { |
259 | pr_err("failed to sysmmu ioremap\n"); | 246 | dev_err(dev, "Failed to ioremap() for %s.\n", |
247 | sysmmu_ips_name[i]); | ||
260 | ret = -ENXIO; | 248 | ret = -ENXIO; |
261 | goto err_reg; | 249 | goto err_reg; |
262 | } | 250 | } |
263 | 251 | ||
264 | sysmmuconp->irq = platform_get_irq(pdev, i); | 252 | irq = platform_get_irq(pdev, i); |
265 | if (sysmmuconp->irq <= 0) { | 253 | if (irq <= 0) { |
266 | pr_err("failed to get sysmmu irq resource\n"); | 254 | dev_err(dev, "Failed to get the IRQ resource of %s.\n", |
255 | sysmmu_ips_name[i]); | ||
267 | ret = -ENOENT; | 256 | ret = -ENOENT; |
268 | goto err_map; | 257 | goto err_map; |
269 | } | 258 | } |
270 | 259 | ||
271 | ret = request_irq(sysmmuconp->irq, s5p_sysmmu_irq, IRQF_DISABLED, pdev->name, sysmmuconp); | 260 | if (request_irq(irq, s5p_sysmmu_irq, IRQF_DISABLED, |
272 | if (ret) { | 261 | pdev->name, (void *)i)) { |
273 | pr_err("failed to request irq\n"); | 262 | dev_err(dev, "Failed to request IRQ for %s.\n", |
263 | sysmmu_ips_name[i]); | ||
274 | ret = -ENOENT; | 264 | ret = -ENOENT; |
275 | goto err_map; | 265 | goto err_map; |
276 | } | 266 | } |
277 | |||
278 | ips = (sysmmu_ips)i; | ||
279 | |||
280 | sysmmuconp->ips = ips; | ||
281 | } | 267 | } |
282 | 268 | ||
283 | return 0; | 269 | return 0; |
284 | 270 | ||
285 | err_reg: | ||
286 | release_mem_region((resource_size_t)sysmmuconp->mem, (resource_size_t)((res->end) - (res->start) + 1)); | ||
287 | err_map: | 271 | err_map: |
288 | iounmap(sysmmuconp->regs); | 272 | iounmap(sysmmusfrs[i]); |
273 | err_reg: | ||
274 | release_mem_region(mem->start, resource_size(mem)); | ||
289 | err_res: | 275 | err_res: |
290 | return ret; | 276 | return ret; |
291 | } | 277 | } |