From 7d30e8b3815f804139271dfc31962ab74ce89650 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Mon, 14 Feb 2011 16:33:10 +0900 Subject: ARM: EXYNOS4: Add EXYNOS4 CPU initialization support This patch adds EXYNOS4 CPU support files in mach-exynos4, and basically they are moved from mach-s5pv310 so that it can support Samsung's new CPU name, EXYNOS4. The EXYNOS4 ingegrates a ARM Cortex A9 multi-core. Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/cpu.c | 25 ++++++++++++----------- arch/arm/plat-s5p/include/plat/exynos4.h | 34 ++++++++++++++++++++++++++++++++ arch/arm/plat-s5p/include/plat/s5pv310.h | 34 -------------------------------- 3 files changed, 48 insertions(+), 45 deletions(-) create mode 100644 arch/arm/plat-s5p/include/plat/exynos4.h delete mode 100644 arch/arm/plat-s5p/include/plat/s5pv310.h (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/cpu.c b/arch/arm/plat-s5p/cpu.c index 047d31c..c3bfe9b 100644 --- a/arch/arm/plat-s5p/cpu.c +++ b/arch/arm/plat-s5p/cpu.c @@ -1,7 +1,7 @@ /* linux/arch/arm/plat-s5p/cpu.c * - * Copyright (c) 2009 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ + * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com * * S5P CPU Support * @@ -12,17 +12,20 @@ #include #include -#include + #include #include + +#include #include + #include #include #include #include #include #include -#include +#include /* table of supported CPUs */ @@ -31,7 +34,7 @@ static const char name_s5p6442[] = "S5P6442"; static const char name_s5p6450[] = "S5P6450"; static const char name_s5pc100[] = "S5PC100"; static const char name_s5pv210[] = "S5PV210/S5PC110"; -static const char name_s5pv310[] = "S5PV310"; +static const char name_exynos4210[] = "EXYNOS4210"; static struct cpu_table cpu_ids[] __initdata = { { @@ -75,13 +78,13 @@ static struct cpu_table cpu_ids[] __initdata = { .init = s5pv210_init, .name = name_s5pv210, }, { - .idcode = 0x43200000, + .idcode = 0x43210000, .idmask = 0xfffff000, - .map_io = s5pv310_map_io, - .init_clocks = s5pv310_init_clocks, - .init_uarts = s5pv310_init_uarts, - .init = s5pv310_init, - .name = name_s5pv310, + .map_io = exynos4_map_io, + .init_clocks = exynos4_init_clocks, + .init_uarts = exynos4_init_uarts, + .init = exynos4_init, + .name = name_exynos4210, }, }; 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 0000000..907caab --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/exynos4.h @@ -0,0 +1,34 @@ +/* linux/arch/arm/plat-s5p/include/plat/exynos4.h + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Header file for exynos4 cpu support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +/* Common init code for EXYNOS4 related SoCs */ + +extern void exynos4_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); +extern void exynos4_register_clocks(void); +extern void exynos4_setup_clocks(void); + +#ifdef CONFIG_CPU_EXYNOS4210 + +extern int exynos4_init(void); +extern void exynos4_init_irq(void); +extern void exynos4_map_io(void); +extern void exynos4_init_clocks(int xtal); +extern struct sys_timer exynos4_timer; + +#define exynos4_init_uarts exynos4_common_init_uarts + +#else +#define exynos4_init_clocks NULL +#define exynos4_init_uarts NULL +#define exynos4_map_io NULL +#define exynos4_init NULL +#endif 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 769c991..0000000 --- a/arch/arm/plat-s5p/include/plat/s5pv310.h +++ /dev/null @@ -1,34 +0,0 @@ -/* linux/arch/arm/plat-s5p/include/plat/s5pv310.h - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Header file for s5pv310 cpu support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -/* Common init code for S5PV310 related SoCs */ - -extern void s5pv310_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); -extern void s5pv310_register_clocks(void); -extern void s5pv310_setup_clocks(void); - -#ifdef CONFIG_CPU_S5PV310 - -extern int s5pv310_init(void); -extern void s5pv310_init_irq(void); -extern void s5pv310_map_io(void); -extern void s5pv310_init_clocks(int xtal); -extern struct sys_timer s5pv310_timer; - -#define s5pv310_init_uarts s5pv310_common_init_uarts - -#else -#define s5pv310_init_clocks NULL -#define s5pv310_init_uarts NULL -#define s5pv310_map_io NULL -#define s5pv310_init NULL -#endif -- cgit v1.1 From 10606aadb046d008c88d1a99b309357ca33d590e Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Mon, 14 Feb 2011 15:05:27 +0900 Subject: ARM: EXYNOS4: Update Kconfig and Makefile for the new ARCH_EXYNOS4 This patch changes the Kconfig and Makefile for the new ARCH_EXYNOS4. It also updates arch/arm/Kconfig, Makeifile and arch/arm/mm/Kconfig to include support for the new ARCH_EXYNOS4. Cc: Russell King Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 557f8c5..6390ac7 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig @@ -7,10 +7,10 @@ config PLAT_S5P bool - depends on (ARCH_S5P64X0 || ARCH_S5P6442 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5PV310) + depends on (ARCH_S5P64X0 || ARCH_S5P6442 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS4) default y - select ARM_VIC if !ARCH_S5PV310 - select ARM_GIC if ARCH_S5PV310 + select ARM_VIC if !ARCH_EXYNOS4 + select ARM_GIC if ARCH_EXYNOS4 select NO_IOPORT select ARCH_REQUIRE_GPIOLIB select S3C_GPIO_TRACK @@ -41,7 +41,7 @@ comment "System MMU" config S5P_SYSTEM_MMU bool "S5P SYSTEM MMU" - depends on ARCH_S5PV310 + depends on ARCH_EXYNOS4 help Say Y here if you want to enable System MMU -- cgit v1.1 From 0048a17348645f18cf209483e1f8e46a6dbec971 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 10 Mar 2011 21:49:31 +0900 Subject: ARM: S5P: Add platform helpers for camera GPIO configuration Add functions for configuration of the parallel camera bus pins on S5PV210 and Exynos4 SoC. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/include/plat/camport.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 arch/arm/plat-s5p/include/plat/camport.h (limited to 'arch/arm/plat-s5p') 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 0000000..71688c8 --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/camport.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * + * S5P series camera interface helper functions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PLAT_S5P_CAMPORT_H_ +#define PLAT_S5P_CAMPORT_H_ __FILE__ + +enum s5p_camport_id { + S5P_CAMPORT_A, + S5P_CAMPORT_B, +}; + +/* + * The helper functions to configure GPIO for the camera parallel bus. + * The camera port can be multiplexed with any FIMC entity, even multiple + * FIMC entities are allowed to be attached to a single port simultaneously. + * These functions are to be used in the board setup code. + */ +int s5pv210_fimc_setup_gpio(enum s5p_camport_id id); +int exynos4_fimc_setup_gpio(enum s5p_camport_id id); + +#endif -- cgit v1.1 From 03c4cd397cba38fc621a33c84d9698d39b286036 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 10 Mar 2011 21:52:27 +0900 Subject: ARM: S5P: Rename MIPI-CSIS driver header and update Copyright Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/dev-csis0.c | 2 +- arch/arm/plat-s5p/dev-csis1.c | 2 +- arch/arm/plat-s5p/include/plat/csis.h | 28 ---------------------------- arch/arm/plat-s5p/include/plat/mipi_csis.h | 28 ++++++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 arch/arm/plat-s5p/include/plat/csis.h create mode 100644 arch/arm/plat-s5p/include/plat/mipi_csis.h (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/dev-csis0.c b/arch/arm/plat-s5p/dev-csis0.c index dfab1c8..e3aabef 100644 --- a/arch/arm/plat-s5p/dev-csis0.c +++ b/arch/arm/plat-s5p/dev-csis0.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Samsung Electronics + * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. * * S5P series device definition for MIPI-CSIS channel 0 * diff --git a/arch/arm/plat-s5p/dev-csis1.c b/arch/arm/plat-s5p/dev-csis1.c index e3053f2..08b91b5 100644 --- a/arch/arm/plat-s5p/dev-csis1.c +++ b/arch/arm/plat-s5p/dev-csis1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Samsung Electronics + * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. * * S5P series device definition for MIPI-CSIS channel 1 * 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 51e308c..0000000 --- a/arch/arm/plat-s5p/include/plat/csis.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2010 Samsung Electronics - * - * S5P series MIPI CSI slave device support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef PLAT_S5P_CSIS_H_ -#define PLAT_S5P_CSIS_H_ __FILE__ - -/** - * struct s5p_platform_mipi_csis - platform data for MIPI-CSIS - * @clk_rate: bus clock frequency - * @lanes: number of data lanes used - * @alignment: data alignment in bits - * @hs_settle: HS-RX settle time - */ -struct s5p_platform_mipi_csis { - unsigned long clk_rate; - u8 lanes; - u8 alignment; - u8 hs_settle; -}; - -#endif /* PLAT_S5P_CSIS_H_ */ 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 0000000..8b3ada8 --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/mipi_csis.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. + * + * S5P series MIPI CSI slave device support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PLAT_S5P_MIPI_CSIS_H_ +#define PLAT_S5P_MIPI_CSIS_H_ __FILE__ + +/** + * struct s5p_platform_mipi_csis - platform data for S5P MIPI-CSIS driver + * @clk_rate: bus clock frequency + * @lanes: number of data lanes used + * @alignment: data alignment in bits + * @hs_settle: HS-RX settle time + */ +struct s5p_platform_mipi_csis { + unsigned long clk_rate; + u8 lanes; + u8 alignment; + u8 hs_settle; +}; + +#endif /* PLAT_S5P_MIPI_CSIS_H_ */ -- cgit v1.1 From e24d208de6bc779c6bd97523cde2665a33f2be4d Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 10 Mar 2011 21:53:36 +0900 Subject: ARM: S5P: Extend MIPI-CSIS platform_data with the PHY control callback Extend MIPI-CSIS driver's platform data structure with a callback for D-PHY enable and reset control. Also add a flag indicating whether the external MIPI-CSI (VDD18_MIPI) power supply should be managed in the driver through the "vdd" power supply. On some boards this regulator may be a fixed voltage regulator without an inhibit function. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/include/plat/mipi_csis.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/include/plat/mipi_csis.h b/arch/arm/plat-s5p/include/plat/mipi_csis.h index 8b3ada8..9bd254c 100644 --- a/arch/arm/plat-s5p/include/plat/mipi_csis.h +++ b/arch/arm/plat-s5p/include/plat/mipi_csis.h @@ -11,18 +11,33 @@ #ifndef PLAT_S5P_MIPI_CSIS_H_ #define PLAT_S5P_MIPI_CSIS_H_ __FILE__ +struct platform_device; + /** * struct s5p_platform_mipi_csis - platform data for S5P MIPI-CSIS driver * @clk_rate: bus clock frequency * @lanes: number of data lanes used * @alignment: data alignment in bits * @hs_settle: HS-RX settle time + * @fixed_phy_vdd: false to enable external D-PHY regulator management in the + * driver or true in case this regulator has no enable function + * @phy_enable: pointer to a callback controlling D-PHY enable/reset */ struct s5p_platform_mipi_csis { unsigned long clk_rate; u8 lanes; u8 alignment; u8 hs_settle; + bool fixed_phy_vdd; + int (*phy_enable)(struct platform_device *pdev, bool on); }; +/** + * s5p_csis_phy_enable - global MIPI-CSI receiver D-PHY control + * @pdev: MIPI-CSIS platform device + * @on: true to enable D-PHY and deassert its reset + * false to disable D-PHY + */ +int s5p_csis_phy_enable(struct platform_device *pdev, bool on); + #endif /* PLAT_S5P_MIPI_CSIS_H_ */ -- cgit v1.1 From 1d45ac49daa15fd0a64c58744ac9ea6451e607e6 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 10 Mar 2011 21:53:40 +0900 Subject: ARM: S5P: Add support for common MIPI CSIS/DSIM D-PHY control Add common code for MIPI-CSIS and MIPI-DSIM drivers to support their corresponding D-PHY's enable and reset control. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/Kconfig | 5 ++++ arch/arm/plat-s5p/Makefile | 1 + arch/arm/plat-s5p/setup-mipiphy.c | 63 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 arch/arm/plat-s5p/setup-mipiphy.c (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 6390ac7..463d3aa 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig @@ -74,3 +74,8 @@ config S5P_DEV_CSIS1 bool help Compile in platform device definitions for MIPI-CSIS channel 1 + +config S5P_SETUP_MIPIPHY + bool + help + 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 4bd5cf9..79ee7bb 100644 --- a/arch/arm/plat-s5p/Makefile +++ b/arch/arm/plat-s5p/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_S5P_DEV_FIMC2) += dev-fimc2.o obj-$(CONFIG_S5P_DEV_ONENAND) += dev-onenand.o obj-$(CONFIG_S5P_DEV_CSIS0) += dev-csis0.o obj-$(CONFIG_S5P_DEV_CSIS1) += dev-csis1.o +obj-$(CONFIG_S5P_SETUP_MIPIPHY) += setup-mipiphy.o diff --git a/arch/arm/plat-s5p/setup-mipiphy.c b/arch/arm/plat-s5p/setup-mipiphy.c new file mode 100644 index 0000000..683c466 --- /dev/null +++ b/arch/arm/plat-s5p/setup-mipiphy.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * + * S5P - Helper functions for MIPI-CSIS and MIPI-DSIM D-PHY control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +static int __s5p_mipi_phy_control(struct platform_device *pdev, + bool on, u32 reset) +{ + static DEFINE_SPINLOCK(lock); + void __iomem *addr; + unsigned long flags; + int pid; + u32 cfg; + + if (!pdev) + return -EINVAL; + + pid = (pdev->id == -1) ? 0 : pdev->id; + + if (pid != 0 && pid != 1) + return -EINVAL; + + addr = S5P_MIPI_DPHY_CONTROL(pid); + + spin_lock_irqsave(&lock, flags); + + cfg = __raw_readl(addr); + cfg = on ? (cfg | reset) : (cfg & ~reset); + __raw_writel(cfg, addr); + + if (on) { + cfg |= S5P_MIPI_DPHY_ENABLE; + } else if (!(cfg & (S5P_MIPI_DPHY_SRESETN | + S5P_MIPI_DPHY_MRESETN) & ~reset)) { + cfg &= ~S5P_MIPI_DPHY_ENABLE; + } + + __raw_writel(cfg, addr); + spin_unlock_irqrestore(&lock, flags); + + return 0; +} + +int s5p_csis_phy_enable(struct platform_device *pdev, bool on) +{ + return __s5p_mipi_phy_control(pdev, on, S5P_MIPI_DPHY_SRESETN); +} + +int s5p_dsim_phy_enable(struct platform_device *pdev, bool on) +{ + return __s5p_mipi_phy_control(pdev, on, S5P_MIPI_DPHY_MRESETN); +} -- cgit v1.1 From f8bfff8e4c3c3d447fe12e3c0e3a36f3bdd891b5 Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Sat, 12 Mar 2011 08:02:07 +0900 Subject: ARM: S5P: HRT support This patch adds support HR-Timer(High Resolution Timer) and dynamic tick system for S5P SoCs. There are many clock sources for HR-Timer on S5P SoCs. The PWM timer, RTC, System Timer, and MCT can be used for clock source. This patch can only support PWM timer for clock source of S5P64X0 and S5PV210. Signed-off-by: Sangbeom Kim Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/Makefile | 1 + arch/arm/plat-s5p/include/plat/s5p-time.h | 40 +++ arch/arm/plat-s5p/s5p-time.c | 448 ++++++++++++++++++++++++++++++ 3 files changed, 489 insertions(+) create mode 100644 arch/arm/plat-s5p/include/plat/s5p-time.h create mode 100644 arch/arm/plat-s5p/s5p-time.c (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile index 79ee7bb..a788266 100644 --- a/arch/arm/plat-s5p/Makefile +++ b/arch/arm/plat-s5p/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_S5P_GPIO_INT) += irq-gpioint.o obj-$(CONFIG_S5P_SYSTEM_MMU) += sysmmu.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_PM) += irq-pm.o +obj-$(CONFIG_S5P_HRT) += s5p-time.o # devices 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 0000000..575e881 --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/s5p-time.h @@ -0,0 +1,40 @@ +/* linux/arch/arm/plat-s5p/include/plat/s5p-time.h + * + * Copyright 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Header file for s5p time support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_PLAT_S5P_TIME_H +#define __ASM_PLAT_S5P_TIME_H __FILE__ + +/* S5P HR-Timer Clock mode */ +enum s5p_timer_mode { + S5P_PWM0, + S5P_PWM1, + S5P_PWM2, + S5P_PWM3, + S5P_PWM4, +}; + +struct s5p_timer_source { + unsigned int event_id; + unsigned int source_id; +}; + +/* Be able to sleep for atleast 4 seconds (usually more) */ +#define S5PTIMER_MIN_RANGE 4 + +#define TCNT_MAX 0xffffffff +#define NON_PERIODIC 0 +#define PERIODIC 1 + +extern void __init s5p_set_timer_source(enum s5p_timer_mode event, + enum s5p_timer_mode source); +extern struct sys_timer s5p_timer; +#endif /* __ASM_PLAT_S5P_TIME_H */ diff --git a/arch/arm/plat-s5p/s5p-time.c b/arch/arm/plat-s5p/s5p-time.c new file mode 100644 index 0000000..8090403 --- /dev/null +++ b/arch/arm/plat-s5p/s5p-time.c @@ -0,0 +1,448 @@ +/* linux/arch/arm/plat-s5p/s5p-time.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P - Common hr-timer support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static struct clk *tin_event; +static struct clk *tin_source; +static struct clk *tdiv_event; +static struct clk *tdiv_source; +static struct clk *timerclk; +static struct s5p_timer_source timer_source; +static unsigned long clock_count_per_tick; +static void s5p_timer_resume(void); + +static void s5p_time_stop(enum s5p_timer_mode mode) +{ + unsigned long tcon; + + tcon = __raw_readl(S3C2410_TCON); + + switch (mode) { + case S5P_PWM0: + tcon &= ~S3C2410_TCON_T0START; + break; + + case S5P_PWM1: + tcon &= ~S3C2410_TCON_T1START; + break; + + case S5P_PWM2: + tcon &= ~S3C2410_TCON_T2START; + break; + + case S5P_PWM3: + tcon &= ~S3C2410_TCON_T3START; + break; + + case S5P_PWM4: + tcon &= ~S3C2410_TCON_T4START; + break; + + default: + printk(KERN_ERR "Invalid Timer %d\n", mode); + break; + } + __raw_writel(tcon, S3C2410_TCON); +} + +static void s5p_time_setup(enum s5p_timer_mode mode, unsigned long tcnt) +{ + unsigned long tcon; + + tcon = __raw_readl(S3C2410_TCON); + + tcnt--; + + switch (mode) { + case S5P_PWM0: + tcon &= ~(0x0f << 0); + tcon |= S3C2410_TCON_T0MANUALUPD; + break; + + case S5P_PWM1: + tcon &= ~(0x0f << 8); + tcon |= S3C2410_TCON_T1MANUALUPD; + break; + + case S5P_PWM2: + tcon &= ~(0x0f << 12); + tcon |= S3C2410_TCON_T2MANUALUPD; + break; + + case S5P_PWM3: + tcon &= ~(0x0f << 16); + tcon |= S3C2410_TCON_T3MANUALUPD; + break; + + case S5P_PWM4: + tcon &= ~(0x07 << 20); + tcon |= S3C2410_TCON_T4MANUALUPD; + break; + + default: + printk(KERN_ERR "Invalid Timer %d\n", mode); + break; + } + + __raw_writel(tcnt, S3C2410_TCNTB(mode)); + __raw_writel(tcnt, S3C2410_TCMPB(mode)); + __raw_writel(tcon, S3C2410_TCON); +} + +static void s5p_time_start(enum s5p_timer_mode mode, bool periodic) +{ + unsigned long tcon; + + tcon = __raw_readl(S3C2410_TCON); + + switch (mode) { + case S5P_PWM0: + tcon |= S3C2410_TCON_T0START; + tcon &= ~S3C2410_TCON_T0MANUALUPD; + + if (periodic) + tcon |= S3C2410_TCON_T0RELOAD; + else + tcon &= ~S3C2410_TCON_T0RELOAD; + break; + + case S5P_PWM1: + tcon |= S3C2410_TCON_T1START; + tcon &= ~S3C2410_TCON_T1MANUALUPD; + + if (periodic) + tcon |= S3C2410_TCON_T1RELOAD; + else + tcon &= ~S3C2410_TCON_T1RELOAD; + break; + + case S5P_PWM2: + tcon |= S3C2410_TCON_T2START; + tcon &= ~S3C2410_TCON_T2MANUALUPD; + + if (periodic) + tcon |= S3C2410_TCON_T2RELOAD; + else + tcon &= ~S3C2410_TCON_T2RELOAD; + break; + + case S5P_PWM3: + tcon |= S3C2410_TCON_T3START; + tcon &= ~S3C2410_TCON_T3MANUALUPD; + + if (periodic) + tcon |= S3C2410_TCON_T3RELOAD; + else + tcon &= ~S3C2410_TCON_T3RELOAD; + break; + + case S5P_PWM4: + tcon |= S3C2410_TCON_T4START; + tcon &= ~S3C2410_TCON_T4MANUALUPD; + + if (periodic) + tcon |= S3C2410_TCON_T4RELOAD; + else + tcon &= ~S3C2410_TCON_T4RELOAD; + break; + + default: + printk(KERN_ERR "Invalid Timer %d\n", mode); + break; + } + __raw_writel(tcon, S3C2410_TCON); +} + +static int s5p_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + s5p_time_setup(timer_source.event_id, cycles); + s5p_time_start(timer_source.event_id, NON_PERIODIC); + + return 0; +} + +static void s5p_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + s5p_time_stop(timer_source.event_id); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + s5p_time_setup(timer_source.event_id, clock_count_per_tick); + s5p_time_start(timer_source.event_id, PERIODIC); + break; + + case CLOCK_EVT_MODE_ONESHOT: + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + break; + + case CLOCK_EVT_MODE_RESUME: + s5p_timer_resume(); + break; + } +} + +static void s5p_timer_resume(void) +{ + /* event timer restart */ + s5p_time_setup(timer_source.event_id, clock_count_per_tick); + s5p_time_start(timer_source.event_id, PERIODIC); + + /* source timer restart */ + s5p_time_setup(timer_source.source_id, TCNT_MAX); + s5p_time_start(timer_source.source_id, PERIODIC); +} + +void __init s5p_set_timer_source(enum s5p_timer_mode event, + enum s5p_timer_mode source) +{ + s3c_device_timer[event].dev.bus = &platform_bus_type; + s3c_device_timer[source].dev.bus = &platform_bus_type; + + timer_source.event_id = event; + timer_source.source_id = source; +} + +static struct clock_event_device time_event_device = { + .name = "s5p_event_timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .rating = 200, + .set_next_event = s5p_set_next_event, + .set_mode = s5p_set_mode, +}; + +static irqreturn_t s5p_clock_event_isr(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction s5p_clock_event_irq = { + .name = "s5p_time_irq", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = s5p_clock_event_isr, + .dev_id = &time_event_device, +}; + +static void __init s5p_clockevent_init(void) +{ + unsigned long pclk; + unsigned long clock_rate; + unsigned int irq_number; + struct clk *tscaler; + + pclk = clk_get_rate(timerclk); + + tscaler = clk_get_parent(tdiv_event); + + clk_set_rate(tscaler, pclk / 2); + clk_set_rate(tdiv_event, pclk / 2); + clk_set_parent(tin_event, tdiv_event); + + clock_rate = clk_get_rate(tin_event); + clock_count_per_tick = clock_rate / HZ; + + clockevents_calc_mult_shift(&time_event_device, + clock_rate, S5PTIMER_MIN_RANGE); + time_event_device.max_delta_ns = + clockevent_delta2ns(-1, &time_event_device); + time_event_device.min_delta_ns = + clockevent_delta2ns(1, &time_event_device); + + time_event_device.cpumask = cpumask_of(0); + clockevents_register_device(&time_event_device); + + irq_number = timer_source.event_id + IRQ_TIMER0; + setup_irq(irq_number, &s5p_clock_event_irq); +} + +static cycle_t s5p_timer_read(struct clocksource *cs) +{ + unsigned long offset = 0; + + switch (timer_source.source_id) { + case S5P_PWM0: + case S5P_PWM1: + case S5P_PWM2: + case S5P_PWM3: + offset = (timer_source.source_id * 0x0c) + 0x14; + break; + + case S5P_PWM4: + offset = 0x40; + break; + + default: + printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id); + return 0; + } + + return (cycle_t) ~__raw_readl(S3C_TIMERREG(offset)); +} + +/* + * Override the global weak sched_clock symbol with this + * local implementation which uses the clocksource to get some + * better resolution when scheduling the kernel. We accept that + * this wraps around for now, since it is just a relative time + * stamp. (Inspired by U300 implementation.) + */ +static DEFINE_CLOCK_DATA(cd); + +unsigned long long notrace sched_clock(void) +{ + u32 cyc; + unsigned long offset = 0; + + switch (timer_source.source_id) { + case S5P_PWM0: + case S5P_PWM1: + case S5P_PWM2: + case S5P_PWM3: + offset = (timer_source.source_id * 0x0c) + 0x14; + break; + + case S5P_PWM4: + offset = 0x40; + break; + + default: + printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id); + return 0; + } + + cyc = ~__raw_readl(S3C_TIMERREG(offset)); + return cyc_to_sched_clock(&cd, cyc, (u32)~0); +} + +static void notrace s5p_update_sched_clock(void) +{ + u32 cyc; + unsigned long offset = 0; + + switch (timer_source.source_id) { + case S5P_PWM0: + case S5P_PWM1: + case S5P_PWM2: + case S5P_PWM3: + offset = (timer_source.source_id * 0x0c) + 0x14; + break; + + case S5P_PWM4: + offset = 0x40; + break; + + default: + printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id); + } + + cyc = ~__raw_readl(S3C_TIMERREG(offset)); + update_sched_clock(&cd, cyc, (u32)~0); +} + +struct clocksource time_clocksource = { + .name = "s5p_clocksource_timer", + .rating = 250, + .read = s5p_timer_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init s5p_clocksource_init(void) +{ + unsigned long pclk; + unsigned long clock_rate; + + pclk = clk_get_rate(timerclk); + + clk_set_rate(tdiv_source, pclk / 2); + clk_set_parent(tin_source, tdiv_source); + + clock_rate = clk_get_rate(tin_source); + + init_sched_clock(&cd, s5p_update_sched_clock, 32, clock_rate); + + s5p_time_setup(timer_source.source_id, TCNT_MAX); + s5p_time_start(timer_source.source_id, PERIODIC); + + if (clocksource_register_hz(&time_clocksource, clock_rate)) + panic("%s: can't register clocksource\n", time_clocksource.name); +} + +static void __init s5p_timer_resources(void) +{ + + unsigned long event_id = timer_source.event_id; + unsigned long source_id = timer_source.source_id; + + timerclk = clk_get(NULL, "timers"); + if (IS_ERR(timerclk)) + panic("failed to get timers clock for timer"); + + clk_enable(timerclk); + + tin_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tin"); + if (IS_ERR(tin_event)) + panic("failed to get pwm-tin clock for event timer"); + + tdiv_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tdiv"); + if (IS_ERR(tdiv_event)) + panic("failed to get pwm-tdiv clock for event timer"); + + clk_enable(tin_event); + + tin_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tin"); + if (IS_ERR(tin_source)) + panic("failed to get pwm-tin clock for source timer"); + + tdiv_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tdiv"); + if (IS_ERR(tdiv_source)) + panic("failed to get pwm-tdiv clock for source timer"); + + clk_enable(tin_source); +} + +static void __init s5p_timer_init(void) +{ + s5p_timer_resources(); + s5p_clockevent_init(); + s5p_clocksource_init(); +} + +struct sys_timer s5p_timer = { + .init = s5p_timer_init, +}; -- cgit v1.1 From 9e65bbf21371ea6705acd88229c8a7439f46e25e Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Sat, 12 Mar 2011 08:05:19 +0900 Subject: ARM: S5P: Update defconfig for HRT support This patch updates s5pv210_defconfig and s5p64x0_defconfig for HRT support and CONFIG_S5P_HRT is used for its configuration. Signed-off-by: Sangbeom Kim Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 463d3aa..0e41447 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig @@ -37,6 +37,11 @@ config S5P_GPIO_INT help Common code for the GPIO interrupts (other than external interrupts.) +config S5P_HRT + bool + help + Use the High Resolution timer support + comment "System MMU" config S5P_SYSTEM_MMU -- cgit v1.1 From 604eefeb2308cda72325fd8754aecb55075ae866 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 12 Mar 2011 08:58:01 +0900 Subject: ARM: S5P: Add platform definitions for FIMC3 Add support for fourth FIMC platform device definition and define resources for FIMC modules on EXYNOS4 machines. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/Kconfig | 5 +++++ arch/arm/plat-s5p/Makefile | 1 + arch/arm/plat-s5p/dev-fimc3.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 arch/arm/plat-s5p/dev-fimc3.c (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 0e41447..8492297 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig @@ -65,6 +65,11 @@ config S5P_DEV_FIMC2 help Compile in platform device definitions for FIMC controller 2 +config S5P_DEV_FIMC3 + bool + help + Compile in platform device definitions for FIMC controller 3 + config S5P_DEV_ONENAND bool help diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile index a788266..42afff7 100644 --- a/arch/arm/plat-s5p/Makefile +++ b/arch/arm/plat-s5p/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_S5P_HRT) += s5p-time.o obj-$(CONFIG_S5P_DEV_FIMC0) += dev-fimc0.o obj-$(CONFIG_S5P_DEV_FIMC1) += dev-fimc1.o obj-$(CONFIG_S5P_DEV_FIMC2) += dev-fimc2.o +obj-$(CONFIG_S5P_DEV_FIMC3) += dev-fimc3.o obj-$(CONFIG_S5P_DEV_ONENAND) += dev-onenand.o obj-$(CONFIG_S5P_DEV_CSIS0) += dev-csis0.o obj-$(CONFIG_S5P_DEV_CSIS1) += dev-csis1.o diff --git a/arch/arm/plat-s5p/dev-fimc3.c b/arch/arm/plat-s5p/dev-fimc3.c new file mode 100644 index 0000000..ef31bec --- /dev/null +++ b/arch/arm/plat-s5p/dev-fimc3.c @@ -0,0 +1,43 @@ +/* linux/arch/arm/plat-s5p/dev-fimc3.c + * + * Copyright (c) 2010 Samsung Electronics + * + * Base S5P FIMC3 resource and device definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +static struct resource s5p_fimc3_resource[] = { + [0] = { + .start = S5P_PA_FIMC3, + .end = S5P_PA_FIMC3 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_FIMC3, + .end = IRQ_FIMC3, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 s5p_fimc3_dma_mask = DMA_BIT_MASK(32); + +struct platform_device s5p_device_fimc3 = { + .name = "s5p-fimc", + .id = 3, + .num_resources = ARRAY_SIZE(s5p_fimc3_resource), + .resource = s5p_fimc3_resource, + .dev = { + .dma_mask = &s5p_fimc3_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; -- cgit v1.1 From 2de0926204b100409bcd51465404ae846c7f48fd Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 15 Mar 2011 21:17:43 +0900 Subject: ARM: S5P: Cleanup S5P gpio interrupt code This patch performs a global cleanup in s5p gpio interrupt support code. The code is prepared for upcoming support for gpio interrupts on EXYNOS4 platform, which has 2 gpio banks (regions) instead of one (like on S5PC110 and S5PC100). Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/irq-gpioint.c | 106 +++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 60 deletions(-) (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/irq-gpioint.c b/arch/arm/plat-s5p/irq-gpioint.c index 3b6bf89..79a7b1c 100644 --- a/arch/arm/plat-s5p/irq-gpioint.c +++ b/arch/arm/plat-s5p/irq-gpioint.c @@ -22,77 +22,64 @@ #include #include -#define S5P_GPIOREG(x) (S5P_VA_GPIO + (x)) +#define GPIO_BASE(chip) (((unsigned long)(chip)->base) & 0xFFFFF000u) -#define GPIOINT_CON_OFFSET 0x700 -#define GPIOINT_MASK_OFFSET 0x900 -#define GPIOINT_PEND_OFFSET 0xA00 +#define CON_OFFSET 0x700 +#define MASK_OFFSET 0x900 +#define PEND_OFFSET 0xA00 +#define REG_OFFSET(x) ((x) << 2) static struct s3c_gpio_chip *irq_chips[S5P_GPIOINT_GROUP_MAXNR]; -static int s5p_gpioint_get_group(struct irq_data *data) -{ - struct gpio_chip *chip = irq_data_get_irq_data(data); - struct s3c_gpio_chip *s3c_chip = container_of(chip, - struct s3c_gpio_chip, chip); - int group; - - for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++) - if (s3c_chip == irq_chips[group]) - break; - - return group; -} - static int s5p_gpioint_get_offset(struct irq_data *data) { - struct gpio_chip *chip = irq_data_get_irq_data(data); - struct s3c_gpio_chip *s3c_chip = container_of(chip, - struct s3c_gpio_chip, chip); - - return data->irq - s3c_chip->irq_base; + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); + return data->irq - chip->irq_base; } static void s5p_gpioint_ack(struct irq_data *data) { + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); int group, offset, pend_offset; unsigned int value; - group = s5p_gpioint_get_group(data); + group = chip->group; offset = s5p_gpioint_get_offset(data); - pend_offset = group << 2; + pend_offset = REG_OFFSET(group); - value = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) + pend_offset); - value |= 1 << offset; - __raw_writel(value, S5P_GPIOREG(GPIOINT_PEND_OFFSET) + pend_offset); + value = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset); + value |= BIT(offset); + __raw_writel(value, GPIO_BASE(chip) + PEND_OFFSET + pend_offset); } static void s5p_gpioint_mask(struct irq_data *data) { + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); int group, offset, mask_offset; unsigned int value; - group = s5p_gpioint_get_group(data); + group = chip->group; offset = s5p_gpioint_get_offset(data); - mask_offset = group << 2; + mask_offset = REG_OFFSET(group); - value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); - value |= 1 << offset; - __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); + value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset); + value |= BIT(offset); + __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + mask_offset); } static void s5p_gpioint_unmask(struct irq_data *data) { + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); int group, offset, mask_offset; unsigned int value; - group = s5p_gpioint_get_group(data); + group = chip->group; offset = s5p_gpioint_get_offset(data); - mask_offset = group << 2; + mask_offset = REG_OFFSET(group); - value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); - value &= ~(1 << offset); - __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); + value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset); + value &= ~BIT(offset); + __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + mask_offset); } static void s5p_gpioint_mask_ack(struct irq_data *data) @@ -103,12 +90,13 @@ static void s5p_gpioint_mask_ack(struct irq_data *data) static int s5p_gpioint_set_type(struct irq_data *data, unsigned int type) { + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data); int group, offset, con_offset; unsigned int value; - group = s5p_gpioint_get_group(data); + group = chip->group; offset = s5p_gpioint_get_offset(data); - con_offset = group << 2; + con_offset = REG_OFFSET(group); switch (type) { case IRQ_TYPE_EDGE_RISING: @@ -132,15 +120,15 @@ static int s5p_gpioint_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } - value = __raw_readl(S5P_GPIOREG(GPIOINT_CON_OFFSET) + con_offset); + value = __raw_readl(GPIO_BASE(chip) + CON_OFFSET + con_offset); value &= ~(0x7 << (offset * 0x4)); value |= (type << (offset * 0x4)); - __raw_writel(value, S5P_GPIOREG(GPIOINT_CON_OFFSET) + con_offset); + __raw_writel(value, GPIO_BASE(chip) + CON_OFFSET + con_offset); return 0; } -struct irq_chip s5p_gpioint = { +static struct irq_chip s5p_gpioint = { .name = "s5p_gpioint", .irq_ack = s5p_gpioint_ack, .irq_mask = s5p_gpioint_mask, @@ -151,30 +139,28 @@ struct irq_chip s5p_gpioint = { static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) { - int group, offset, pend_offset, mask_offset; - int real_irq; + int group, pend_offset, mask_offset; unsigned int pend, mask; for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++) { - pend_offset = group << 2; - pend = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) + - pend_offset); + struct s3c_gpio_chip *chip = irq_chips[group]; + if (!chip) + continue; + + pend_offset = REG_OFFSET(group); + pend = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset); if (!pend) continue; - mask_offset = group << 2; - mask = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + - mask_offset); + mask_offset = REG_OFFSET(group); + mask = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset); pend &= ~mask; - for (offset = 0; offset < 8; offset++) { - if (pend & (1 << offset)) { - struct s3c_gpio_chip *chip = irq_chips[group]; - if (chip) { - real_irq = chip->irq_base + offset; - generic_handle_irq(real_irq); - } - } + while (pend) { + int offset = fls(pend) - 1; + int real_irq = chip->irq_base + offset; + generic_handle_irq(real_irq); + pend &= ~BIT(offset); } } } @@ -202,7 +188,7 @@ static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip) for (i = 0; i < chip->chip.ngpio; i++) { irq = chip->irq_base + i; set_irq_chip(irq, &s5p_gpioint); - set_irq_data(irq, &chip->chip); + set_irq_data(irq, chip); set_irq_handler(irq, handle_level_irq); set_irq_flags(irq, IRQF_VALID); } -- cgit v1.1 From a43efddc3bf8b143ff9352763cd39d425db14d27 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 15 Mar 2011 21:17:43 +0900 Subject: ARM: S5P: Add function to register gpio interrupt bank data This patch removes all global data from common s5p gpio interrupt handler code. This enables to reuse this code on EXYNOS4 platform. Instead of global data (IRQ_GPIOINT interrupt number, S5P_GPIOINT_GROUP_MAXNR groups count), a s5p_register_gpioint_bank() function is introduced. It is aimed to be called from gpiolib init. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/irq-gpioint.c | 68 +++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 10 deletions(-) (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/irq-gpioint.c b/arch/arm/plat-s5p/irq-gpioint.c index 79a7b1c..cd87d32 100644 --- a/arch/arm/plat-s5p/irq-gpioint.c +++ b/arch/arm/plat-s5p/irq-gpioint.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,16 @@ #define PEND_OFFSET 0xA00 #define REG_OFFSET(x) ((x) << 2) -static struct s3c_gpio_chip *irq_chips[S5P_GPIOINT_GROUP_MAXNR]; +struct s5p_gpioint_bank { + struct list_head list; + int start; + int nr_groups; + int irq; + struct s3c_gpio_chip **chips; + void (*handler)(unsigned int, struct irq_desc *); +}; + +LIST_HEAD(banks); static int s5p_gpioint_get_offset(struct irq_data *data) { @@ -139,11 +149,12 @@ static struct irq_chip s5p_gpioint = { static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) { + struct s5p_gpioint_bank *bank = get_irq_data(irq); int group, pend_offset, mask_offset; unsigned int pend, mask; - for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++) { - struct s3c_gpio_chip *chip = irq_chips[group]; + for (group = 0; group < bank->nr_groups; group++) { + struct s3c_gpio_chip *chip = bank->chips[group]; if (!chip) continue; @@ -168,23 +179,44 @@ static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip) { static int used_gpioint_groups = 0; - static bool handler_registered = 0; int irq, group = chip->group; int i; + struct s5p_gpioint_bank *bank = NULL; if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT) return -ENOMEM; + list_for_each_entry(bank, &banks, list) { + if (group >= bank->start && + group < bank->start + bank->nr_groups) + break; + } + if (!bank) + return -EINVAL; + + if (!bank->handler) { + bank->chips = kzalloc(sizeof(struct s3c_gpio_chip *) * + bank->nr_groups, GFP_KERNEL); + if (!bank->chips) + return -ENOMEM; + + set_irq_chained_handler(bank->irq, s5p_gpioint_handler); + set_irq_data(bank->irq, bank); + bank->handler = s5p_gpioint_handler; + printk(KERN_INFO "Registered chained gpio int handler for interrupt %d.\n", + bank->irq); + } + + /* + * chained GPIO irq has been sucessfully registered, allocate new gpio + * int group and assign irq nubmers + */ + chip->irq_base = S5P_GPIOINT_BASE + used_gpioint_groups * S5P_GPIOINT_GROUP_SIZE; used_gpioint_groups++; - if (!handler_registered) { - set_irq_chained_handler(IRQ_GPIOINT, s5p_gpioint_handler); - handler_registered = 1; - } - - irq_chips[group] = chip; + bank->chips[group - bank->start] = chip; for (i = 0; i < chip->chip.ngpio; i++) { irq = chip->irq_base + i; set_irq_chip(irq, &s5p_gpioint); @@ -221,3 +253,19 @@ int __init s5p_register_gpio_interrupt(int pin) } return ret; } + +int __init s5p_register_gpioint_bank(int chain_irq, int start, int nr_groups) +{ + struct s5p_gpioint_bank *bank; + + bank = kzalloc(sizeof(*bank), GFP_KERNEL); + if (!bank) + return -ENOMEM; + + bank->start = start; + bank->nr_groups = nr_groups; + bank->irq = chain_irq; + + list_add_tail(&bank->list, &banks); + return 0; +} -- cgit v1.1 From b34f003f271d477f61a980d280afc77929047440 Mon Sep 17 00:00:00 2001 From: KyongHo Cho Date: Mon, 7 Mar 2011 08:56:48 +0900 Subject: ARM: EXYNOS4: Enhancement of System MMU driver This patch includes the following enhancements for System MMU: - Enhanced readability - Removal of unused data structures or their members - Simplified function definitions - Corrections of some logical errors - Full compliance with Linux coding style - Simpler way of registering callback functions of System MMU faults Signed-off-by: KyongHo Cho Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/include/plat/sysmmu.h | 95 +++++++++ arch/arm/plat-s5p/sysmmu.c | 368 +++++++++++++++----------------- 2 files changed, 269 insertions(+), 194 deletions(-) create mode 100644 arch/arm/plat-s5p/include/plat/sysmmu.h (limited to 'arch/arm/plat-s5p') 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 0000000..bf5283c --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/sysmmu.h @@ -0,0 +1,95 @@ +/* linux/arch/arm/plat-s5p/include/plat/sysmmu.h + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung System MMU driver for S5P platform + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM__PLAT_SYSMMU_H +#define __ASM__PLAT_SYSMMU_H __FILE__ + +enum S5P_SYSMMU_INTERRUPT_TYPE { + SYSMMU_PAGEFAULT, + SYSMMU_AR_MULTIHIT, + SYSMMU_AW_MULTIHIT, + SYSMMU_BUSERROR, + SYSMMU_AR_SECURITY, + SYSMMU_AR_ACCESS, + SYSMMU_AW_SECURITY, + SYSMMU_AW_PROTECTION, /* 7 */ + SYSMMU_FAULTS_NUM +}; + +#ifdef CONFIG_S5P_SYSTEM_MMU + +#include + +/** + * s5p_sysmmu_enable() - enable system mmu of ip + * @ips: The ip connected system mmu. + * #pgd: Base physical address of the 1st level page table + * + * This function enable system mmu to transfer address + * from virtual address to physical address + */ +void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd); + +/** + * s5p_sysmmu_disable() - disable sysmmu mmu of ip + * @ips: The ip connected system mmu. + * + * This function disable system mmu to transfer address + * from virtual address to physical address + */ +void s5p_sysmmu_disable(sysmmu_ips ips); + +/** + * s5p_sysmmu_set_tablebase_pgd() - set page table base address to refer page table + * @ips: The ip connected system mmu. + * @pgd: The page table base address. + * + * This function set page table base address + * When system mmu transfer address from virtaul address to physical address, + * system mmu refer address information from page table + */ +void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd); + +/** + * s5p_sysmmu_tlb_invalidate() - flush all TLB entry in system mmu + * @ips: The ip connected system mmu. + * + * This function flush all TLB entry in system mmu + */ +void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips); + +/** s5p_sysmmu_set_fault_handler() - Fault handler for System MMUs + * @itype: type of fault. + * @pgtable_base: the physical address of page table base. This is 0 if @ips is + * SYSMMU_BUSERROR. + * @fault_addr: the device (virtual) address that the System MMU tried to + * translated. This is 0 if @ips is SYSMMU_BUSERROR. + * Called when interrupt occurred by the System MMUs + * The device drivers of peripheral devices that has a System MMU can implement + * a fault handler to resolve address translation fault by System MMU. + * The meanings of return value and parameters are described below. + + * return value: non-zero if the fault is correctly resolved. + * zero if the fault is not handled. + */ +void s5p_sysmmu_set_fault_handler(sysmmu_ips ips, + int (*handler)(enum S5P_SYSMMU_INTERRUPT_TYPE itype, + unsigned long pgtable_base, + unsigned long fault_addr)); +#else +#define s5p_sysmmu_enable(ips, pgd) do { } while (0) +#define s5p_sysmmu_disable(ips) do { } while (0) +#define s5p_sysmmu_set_tablebase_pgd(ips, pgd) do { } while (0) +#define s5p_sysmmu_tlb_invalidate(ips) do { } while (0) +#define s5p_sysmmu_set_fault_handler(ips, handler) do { } while (0) +#endif +#endif /* __ASM_PLAT_SYSMMU_H */ diff --git a/arch/arm/plat-s5p/sysmmu.c b/arch/arm/plat-s5p/sysmmu.c index ffe8a48..89e024f 100644 --- a/arch/arm/plat-s5p/sysmmu.c +++ b/arch/arm/plat-s5p/sysmmu.c @@ -12,280 +12,260 @@ #include #include +#include + #include #include -#include +#include + +#define CTRL_ENABLE 0x5 +#define CTRL_BLOCK 0x7 +#define CTRL_DISABLE 0x0 + +static struct device *dev; + +static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { + S5P_PAGE_FAULT_ADDR, + S5P_AR_FAULT_ADDR, + S5P_AW_FAULT_ADDR, + S5P_DEFAULT_SLAVE_ADDR, + S5P_AR_FAULT_ADDR, + S5P_AR_FAULT_ADDR, + S5P_AW_FAULT_ADDR, + S5P_AW_FAULT_ADDR +}; -struct sysmmu_controller s5p_sysmmu_cntlrs[S5P_SYSMMU_TOTAL_IPNUM]; +static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { + "PAGE FAULT", + "AR MULTI-HIT FAULT", + "AW MULTI-HIT FAULT", + "BUS ERROR", + "AR SECURITY PROTECTION FAULT", + "AR ACCESS PROTECTION FAULT", + "AW SECURITY PROTECTION FAULT", + "AW ACCESS PROTECTION FAULT" +}; -void s5p_sysmmu_register(struct sysmmu_controller *sysmmuconp) +static int (*fault_handlers[S5P_SYSMMU_TOTAL_IPNUM])( + enum S5P_SYSMMU_INTERRUPT_TYPE itype, + unsigned long pgtable_base, + unsigned long fault_addr); + +/* + * If adjacent 2 bits are true, the system MMU is enabled. + * The system MMU is disabled, otherwise. + */ +static unsigned long sysmmu_states; + +static inline void set_sysmmu_active(sysmmu_ips ips) { - unsigned int reg_mmu_ctrl; - unsigned int reg_mmu_status; - unsigned int reg_pt_base_addr; - unsigned int reg_int_status; - unsigned int reg_page_ft_addr; - - reg_int_status = __raw_readl(sysmmuconp->regs + S5P_INT_STATUS); - reg_mmu_ctrl = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL); - reg_mmu_status = __raw_readl(sysmmuconp->regs + S5P_MMU_STATUS); - reg_pt_base_addr = __raw_readl(sysmmuconp->regs + S5P_PT_BASE_ADDR); - reg_page_ft_addr = __raw_readl(sysmmuconp->regs + S5P_PAGE_FAULT_ADDR); - - printk(KERN_INFO "%s: ips:%s\n", __func__, sysmmuconp->name); - printk(KERN_INFO "%s: MMU_CTRL:0x%X, ", __func__, reg_mmu_ctrl); - printk(KERN_INFO "MMU_STATUS:0x%X, PT_BASE_ADDR:0x%X\n", reg_mmu_status, reg_pt_base_addr); - printk(KERN_INFO "%s: INT_STATUS:0x%X, PAGE_FAULT_ADDR:0x%X\n", __func__, reg_int_status, reg_page_ft_addr); - - switch (reg_int_status & 0xFF) { - case 0x1: - printk(KERN_INFO "%s: Page fault\n", __func__); - printk(KERN_INFO "%s: Virtual address causing last page fault or bus error : 0x%x\n", __func__ , reg_page_ft_addr); - break; - case 0x2: - printk(KERN_INFO "%s: AR multi-hit fault\n", __func__); - break; - case 0x4: - printk(KERN_INFO "%s: AW multi-hit fault\n", __func__); - break; - case 0x8: - printk(KERN_INFO "%s: Bus error\n", __func__); - break; - case 0x10: - printk(KERN_INFO "%s: AR Security protection fault\n", __func__); - break; - case 0x20: - printk(KERN_INFO "%s: AR Access protection fault\n", __func__); - break; - case 0x40: - printk(KERN_INFO "%s: AW Security protection fault\n", __func__); - break; - case 0x80: - printk(KERN_INFO "%s: AW Access protection fault\n", __func__); - break; - } + sysmmu_states |= 3 << (ips * 2); } -static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id) +static inline void set_sysmmu_inactive(sysmmu_ips ips) { - unsigned int i; - unsigned int reg_int_status; - struct sysmmu_controller *sysmmuconp; - - for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) { - sysmmuconp = &s5p_sysmmu_cntlrs[i]; - - if (sysmmuconp->enable == true) { - reg_int_status = __raw_readl(sysmmuconp->regs + S5P_INT_STATUS); - - if (reg_int_status & 0xFF) - s5p_sysmmu_register(sysmmuconp); - } - } - return IRQ_HANDLED; + sysmmu_states &= ~(3 << (ips * 2)); } -int s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd) +static inline int is_sysmmu_active(sysmmu_ips ips) { - struct sysmmu_controller *sysmmuconp = NULL; - - sysmmuconp = &s5p_sysmmu_cntlrs[ips]; - - if (sysmmuconp == NULL) { - printk(KERN_ERR "failed to get ip's sysmmu info\n"); - return 1; - } - - /* Set sysmmu page table base address */ - __raw_writel(pgd, sysmmuconp->regs + S5P_PT_BASE_ADDR); + return sysmmu_states & (3 << (ips * 2)); +} - if (s5p_sysmmu_tlb_invalidate(ips) != 0) - printk(KERN_ERR "failed s5p_sysmmu_tlb_invalidate\n"); +static void __iomem *sysmmusfrs[S5P_SYSMMU_TOTAL_IPNUM]; - return 0; +static inline void sysmmu_block(sysmmu_ips ips) +{ + __raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL); + dev_dbg(dev, "%s is blocked.\n", sysmmu_ips_name[ips]); } -static int s5p_sysmmu_set_tablebase(sysmmu_ips ips) +static inline void sysmmu_unblock(sysmmu_ips ips) { - unsigned int pg; - struct sysmmu_controller *sysmmuconp; + __raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); + dev_dbg(dev, "%s is unblocked.\n", sysmmu_ips_name[ips]); +} - sysmmuconp = &s5p_sysmmu_cntlrs[ips]; +static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips) +{ + __raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH); + dev_dbg(dev, "TLB of %s is invalidated.\n", sysmmu_ips_name[ips]); +} - if (sysmmuconp == NULL) { - printk(KERN_ERR "failed to get ip's sysmmu info\n"); - return 1; +static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd) +{ + if (unlikely(pgd == 0)) { + pgd = (unsigned long)ZERO_PAGE(0); + __raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB LV1 */ + } else { + __raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB LV1 */ } - __asm__("mrc p15, 0, %0, c2, c0, 0" \ - : "=r" (pg) : : "cc"); \ - pg &= ~0x3fff; - - printk(KERN_INFO "%s: CP15 TTBR0 : 0x%x\n", __func__, pg); + __raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR); - /* Set sysmmu page table base address */ - __raw_writel(pg, sysmmuconp->regs + S5P_PT_BASE_ADDR); + dev_dbg(dev, "Page table base of %s is initialized with 0x%08lX.\n", + sysmmu_ips_name[ips], pgd); + __sysmmu_tlb_invalidate(ips); +} - return 0; +void sysmmu_set_fault_handler(sysmmu_ips ips, + int (*handler)(enum S5P_SYSMMU_INTERRUPT_TYPE itype, + unsigned long pgtable_base, + unsigned long fault_addr)) +{ + BUG_ON(!((ips >= SYSMMU_MDMA) && (ips < S5P_SYSMMU_TOTAL_IPNUM))); + fault_handlers[ips] = handler; } -int s5p_sysmmu_enable(sysmmu_ips ips) +static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id) { - unsigned int reg; + /* SYSMMU is in blocked when interrupt occurred. */ + unsigned long base = 0; + sysmmu_ips ips = (sysmmu_ips)dev_id; + enum S5P_SYSMMU_INTERRUPT_TYPE itype; - struct sysmmu_controller *sysmmuconp; + itype = (enum S5P_SYSMMU_INTERRUPT_TYPE) + __ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS)); - sysmmuconp = &s5p_sysmmu_cntlrs[ips]; + BUG_ON(!((itype >= 0) && (itype < 8))); - if (sysmmuconp == NULL) { - printk(KERN_ERR "failed to get ip's sysmmu info\n"); - return 1; - } + dev_alert(dev, "%s occurred by %s.\n", sysmmu_fault_name[itype], + sysmmu_ips_name[ips]); - s5p_sysmmu_set_tablebase(ips); + if (fault_handlers[ips]) { + unsigned long addr; - /* replacement policy : LRU */ - reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CFG); - reg |= 0x1; - __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CFG); + base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR); + addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]); - /* Enable interrupt, Enable MMU */ - reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL); - reg |= (0x1 << 2) | (0x1 << 0); + if (fault_handlers[ips](itype, base, addr)) { + __raw_writel(1 << itype, + sysmmusfrs[ips] + S5P_INT_CLEAR); + dev_notice(dev, "%s from %s is resolved." + " Retrying translation.\n", + sysmmu_fault_name[itype], sysmmu_ips_name[ips]); + } else { + base = 0; + } + } - __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL); + sysmmu_unblock(ips); - sysmmuconp->enable = true; + if (!base) + dev_notice(dev, "%s from %s is not handled.\n", + sysmmu_fault_name[itype], sysmmu_ips_name[ips]); - return 0; + return IRQ_HANDLED; } -int s5p_sysmmu_disable(sysmmu_ips ips) +void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd) { - unsigned int reg; - - struct sysmmu_controller *sysmmuconp = NULL; - - if (ips > S5P_SYSMMU_TOTAL_IPNUM) - printk(KERN_ERR "failed to get ips parameter\n"); - - sysmmuconp = &s5p_sysmmu_cntlrs[ips]; - - if (sysmmuconp == NULL) { - printk(KERN_ERR "failed to get ip's sysmmu info\n"); - return 1; + if (is_sysmmu_active(ips)) { + sysmmu_block(ips); + __sysmmu_set_ptbase(ips, pgd); + sysmmu_unblock(ips); + } else { + dev_dbg(dev, "%s is disabled. " + "Skipping initializing page table base.\n", + sysmmu_ips_name[ips]); } - - reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CFG); - - /* replacement policy : LRU */ - reg |= 0x1; - __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CFG); - - reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL); - - /* Disable MMU */ - reg &= ~0x1; - __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL); - - sysmmuconp->enable = false; - - return 0; } -int s5p_sysmmu_tlb_invalidate(sysmmu_ips ips) +void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd) { - unsigned int reg; - struct sysmmu_controller *sysmmuconp = NULL; + if (!is_sysmmu_active(ips)) { + __sysmmu_set_ptbase(ips, pgd); - sysmmuconp = &s5p_sysmmu_cntlrs[ips]; + __raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); - if (sysmmuconp == NULL) { - printk(KERN_ERR "failed to get ip's sysmmu info\n"); - return 1; + set_sysmmu_active(ips); + dev_dbg(dev, "%s is enabled.\n", sysmmu_ips_name[ips]); + } else { + dev_dbg(dev, "%s is already enabled.\n", sysmmu_ips_name[ips]); } +} - /* set Block MMU for flush TLB */ - reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL); - reg |= 0x1 << 1; - __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL); - - /* flush all TLB entry */ - __raw_writel(0x1, sysmmuconp->regs + S5P_MMU_FLUSH); - - /* set Un-block MMU after flush TLB */ - reg = __raw_readl(sysmmuconp->regs + S5P_MMU_CTRL); - reg &= ~(0x1 << 1); - __raw_writel(reg, sysmmuconp->regs + S5P_MMU_CTRL); +void s5p_sysmmu_disable(sysmmu_ips ips) +{ + if (is_sysmmu_active(ips)) { + __raw_writel(CTRL_DISABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); + set_sysmmu_inactive(ips); + dev_dbg(dev, "%s is disabled.\n", sysmmu_ips_name[ips]); + } else { + dev_dbg(dev, "%s is already disabled.\n", sysmmu_ips_name[ips]); + } +} - return 0; +void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips) +{ + if (is_sysmmu_active(ips)) { + sysmmu_block(ips); + __sysmmu_tlb_invalidate(ips); + sysmmu_unblock(ips); + } else { + dev_dbg(dev, "%s is disabled. " + "Skipping invalidating TLB.\n", sysmmu_ips_name[ips]); + } } static int s5p_sysmmu_probe(struct platform_device *pdev) { - int i; - int ret; - struct resource *res; - struct sysmmu_controller *sysmmuconp; - sysmmu_ips ips; + int i, ret; + struct resource *res, *mem; - for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) { - sysmmuconp = &s5p_sysmmu_cntlrs[i]; - if (sysmmuconp == NULL) { - printk(KERN_ERR "failed to get ip's sysmmu info\n"); - ret = -ENOENT; - goto err_res; - } + dev = &pdev->dev; - sysmmuconp->name = sysmmu_ips_name[i]; + for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) { + int irq; res = platform_get_resource(pdev, IORESOURCE_MEM, i); if (!res) { - printk(KERN_ERR "failed to get sysmmu resource\n"); + dev_err(dev, "Failed to get the resource of %s.\n", + sysmmu_ips_name[i]); ret = -ENODEV; goto err_res; } - sysmmuconp->mem = request_mem_region(res->start, + mem = request_mem_region(res->start, ((res->end) - (res->start)) + 1, pdev->name); - if (!sysmmuconp->mem) { - pr_err("failed to request sysmmu memory region\n"); + if (!mem) { + dev_err(dev, "Failed to request the memory region of %s.\n", + sysmmu_ips_name[i]); ret = -EBUSY; goto err_res; } - sysmmuconp->regs = ioremap(res->start, res->end - res->start + 1); - if (!sysmmuconp->regs) { - pr_err("failed to sysmmu ioremap\n"); + sysmmusfrs[i] = ioremap(res->start, res->end - res->start + 1); + if (!sysmmusfrs[i]) { + dev_err(dev, "Failed to ioremap() for %s.\n", + sysmmu_ips_name[i]); ret = -ENXIO; goto err_reg; } - sysmmuconp->irq = platform_get_irq(pdev, i); - if (sysmmuconp->irq <= 0) { - pr_err("failed to get sysmmu irq resource\n"); + irq = platform_get_irq(pdev, i); + if (irq <= 0) { + dev_err(dev, "Failed to get the IRQ resource of %s.\n", + sysmmu_ips_name[i]); ret = -ENOENT; goto err_map; } - ret = request_irq(sysmmuconp->irq, s5p_sysmmu_irq, IRQF_DISABLED, pdev->name, sysmmuconp); - if (ret) { - pr_err("failed to request irq\n"); + if (request_irq(irq, s5p_sysmmu_irq, IRQF_DISABLED, + pdev->name, (void *)i)) { + dev_err(dev, "Failed to request IRQ for %s.\n", + sysmmu_ips_name[i]); ret = -ENOENT; goto err_map; } - - ips = (sysmmu_ips)i; - - sysmmuconp->ips = ips; } return 0; -err_reg: - release_mem_region((resource_size_t)sysmmuconp->mem, (resource_size_t)((res->end) - (res->start) + 1)); err_map: - iounmap(sysmmuconp->regs); + iounmap(sysmmusfrs[i]); +err_reg: + release_mem_region(mem->start, resource_size(mem)); err_res: return ret; } -- cgit v1.1 From b0b6ff0b21057bb8e58b0be8b427a4713fd4b5a5 Mon Sep 17 00:00:00 2001 From: KyongHo Cho Date: Mon, 7 Mar 2011 09:10:24 +0900 Subject: ARM: EXYNOS4: Implement Clock gating for System MMU This patch includes the implementation of the clock gating for System MMU. Initially, all System MMUs are not asserted the system clock. Asserting the system clock to a System MMU is enabled only when s5p_sysmmu_enable() is called. Likewise, it is disabled only when s5p_sysmmu_disable() is called. Therefore, clock gating on System MMUs are still invisible to the outside of the System MMU driver. Signed-off-by: KyongHo Cho Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/sysmmu.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'arch/arm/plat-s5p') diff --git a/arch/arm/plat-s5p/sysmmu.c b/arch/arm/plat-s5p/sysmmu.c index 89e024f..54f5edd 100644 --- a/arch/arm/plat-s5p/sysmmu.c +++ b/arch/arm/plat-s5p/sysmmu.c @@ -174,6 +174,8 @@ void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd) void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd) { if (!is_sysmmu_active(ips)) { + sysmmu_clk_enable(ips); + __sysmmu_set_ptbase(ips, pgd); __raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); @@ -190,6 +192,7 @@ void s5p_sysmmu_disable(sysmmu_ips ips) if (is_sysmmu_active(ips)) { __raw_writel(CTRL_DISABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); set_sysmmu_inactive(ips); + sysmmu_clk_disable(ips); dev_dbg(dev, "%s is disabled.\n", sysmmu_ips_name[ips]); } else { dev_dbg(dev, "%s is already disabled.\n", sysmmu_ips_name[ips]); @@ -218,6 +221,9 @@ static int s5p_sysmmu_probe(struct platform_device *pdev) for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) { int irq; + sysmmu_clk_init(dev, i); + sysmmu_clk_disable(i); + res = platform_get_resource(pdev, IORESOURCE_MEM, i); if (!res) { dev_err(dev, "Failed to get the resource of %s.\n", -- cgit v1.1