From 1c8408e3137bcb78d9ab8af832111f455d11e99c Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 12 Feb 2013 10:12:09 -0800 Subject: ARM: S3C24XX: handle s3c2412 eints using new infrastructure The s3c2412 handles the eints 0 to 3 different than all the other SoCs of the 24xx range. These eints must be acked and masked in the regular bits as well as the bits 0 to 3 of the eint registers, which are unused on the other SoCs. This of course can be realized using the new infrastructure with the eint bits in the main register being the parent interrupts of the same bits in the eint register. The s3c2412 therefore gets its own IRQ_EINT0 to 4 constants that reside in the newly created gap before IRQ_EINT4. gpio-samsung, as the only user of these is modified to return the correct values when handling gpio_to_irq requests on s3c2412 based machines. Due to lack of hardware this is compile tested only, but should hopefully work as intended. Signed-off-by: Heiko Stuebner Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index b3643ff007e4..db098cea3a55 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -1123,7 +1123,10 @@ int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset) static int s3c24xx_gpiolib_fbank_to_irq(struct gpio_chip *chip, unsigned offset) { if (offset < 4) - return IRQ_EINT0 + offset; + if (soc_is_s3c2412()) + return IRQ_EINT0_2412 + offset; + else + return IRQ_EINT0 + offset; if (offset < 8) return IRQ_EINT4 + offset - 4; -- cgit v1.2.3 From d97fedef912832611b668fa7ece8e8ff54a6a590 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 12 Feb 2013 21:01:38 -0800 Subject: gpio: samsung: fixes build warning with s3c2410_defconfig commit 7b45ed96 ("ARM: S3C24XX: handle s3c2412 eints using new infrastructure") introduced build warning and this patch fixes that: drivers/gpio/gpio-samsung.c: In function 's3c24xx_gpiolib_fbank_to_irq': drivers/gpio/gpio-samsung.c:1126:5: warning: suggest explicit braces to avoid ambiguous 'else' [-Wparentheses] Reported-by: kbuild test robot Cc: Heiko Stuebner Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index db098cea3a55..58aa28fb5889 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -1122,11 +1122,12 @@ int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset) #ifdef CONFIG_PLAT_S3C24XX static int s3c24xx_gpiolib_fbank_to_irq(struct gpio_chip *chip, unsigned offset) { - if (offset < 4) + if (offset < 4) { if (soc_is_s3c2412()) return IRQ_EINT0_2412 + offset; else return IRQ_EINT0 + offset; + } if (offset < 8) return IRQ_EINT4 + offset - 4; -- cgit v1.2.3 From f67faf487fc182772ffa5521e94aa7f0816a309c Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Fri, 28 Dec 2012 10:37:27 -0800 Subject: pinctrl: exynos: add exynos5250 SoC specific data Add Samsung Exynos5250 SoC specific data to enable pinctrl support for all platforms based on Exynos5250. Signed-off-by: Thomas Abraham Acked-by: Linus Walleij Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- drivers/pinctrl/pinctrl-exynos.c | 108 ++++++++++++++++++++++++++++++++++++++ drivers/pinctrl/pinctrl-samsung.c | 2 + drivers/pinctrl/pinctrl-samsung.h | 1 + 3 files changed, 111 insertions(+) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 538b9ddaadf7..8738933a57d7 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -677,3 +677,111 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = { .label = "exynos4x12-gpio-ctrl3", }, }; + +/* pin banks of exynos5250 pin-controller 0 */ +static struct samsung_pin_bank exynos5250_pin_banks0[] = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00), + EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpa2", 0x08), + EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpb0", 0x0c), + EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpb1", 0x10), + EXYNOS_PIN_BANK_EINTG(4, 0x0A0, "gpb2", 0x14), + EXYNOS_PIN_BANK_EINTG(4, 0x0C0, "gpb3", 0x18), + EXYNOS_PIN_BANK_EINTG(7, 0x0E0, "gpc0", 0x1c), + EXYNOS_PIN_BANK_EINTG(4, 0x100, "gpc1", 0x20), + EXYNOS_PIN_BANK_EINTG(7, 0x120, "gpc2", 0x24), + EXYNOS_PIN_BANK_EINTG(7, 0x140, "gpc3", 0x28), + EXYNOS_PIN_BANK_EINTG(4, 0x160, "gpd0", 0x2c), + EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpd1", 0x30), + EXYNOS_PIN_BANK_EINTG(7, 0x2E0, "gpc4", 0x34), + EXYNOS_PIN_BANK_EINTN(6, 0x1A0, "gpy0"), + EXYNOS_PIN_BANK_EINTN(4, 0x1C0, "gpy1"), + EXYNOS_PIN_BANK_EINTN(6, 0x1E0, "gpy2"), + EXYNOS_PIN_BANK_EINTN(8, 0x200, "gpy3"), + EXYNOS_PIN_BANK_EINTN(8, 0x220, "gpy4"), + EXYNOS_PIN_BANK_EINTN(8, 0x240, "gpy5"), + EXYNOS_PIN_BANK_EINTN(8, 0x260, "gpy6"), + EXYNOS_PIN_BANK_EINTW(8, 0xC00, "gpx0", 0x00), + EXYNOS_PIN_BANK_EINTW(8, 0xC20, "gpx1", 0x04), + EXYNOS_PIN_BANK_EINTW(8, 0xC40, "gpx2", 0x08), + EXYNOS_PIN_BANK_EINTW(8, 0xC60, "gpx3", 0x0c), +}; + +/* pin banks of exynos5250 pin-controller 1 */ +static struct samsung_pin_bank exynos5250_pin_banks1[] = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpe0", 0x00), + EXYNOS_PIN_BANK_EINTG(2, 0x020, "gpe1", 0x04), + EXYNOS_PIN_BANK_EINTG(4, 0x040, "gpf0", 0x08), + EXYNOS_PIN_BANK_EINTG(4, 0x060, "gpf1", 0x0c), + EXYNOS_PIN_BANK_EINTG(8, 0x080, "gpg0", 0x10), + EXYNOS_PIN_BANK_EINTG(8, 0x0A0, "gpg1", 0x14), + EXYNOS_PIN_BANK_EINTG(2, 0x0C0, "gpg2", 0x18), + EXYNOS_PIN_BANK_EINTG(4, 0x0E0, "gph0", 0x1c), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gph1", 0x20), +}; + +/* pin banks of exynos5250 pin-controller 2 */ +static struct samsung_pin_bank exynos5250_pin_banks2[] = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpv0", 0x00), + EXYNOS_PIN_BANK_EINTG(8, 0x020, "gpv1", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x060, "gpv2", 0x08), + EXYNOS_PIN_BANK_EINTG(8, 0x080, "gpv3", 0x0c), + EXYNOS_PIN_BANK_EINTG(2, 0x0C0, "gpv4", 0x10), +}; + +/* pin banks of exynos5250 pin-controller 3 */ +static struct samsung_pin_bank exynos5250_pin_banks3[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpz", 0x00), +}; + +/* + * Samsung pinctrl driver data for Exynos5250 SoC. Exynos5250 SoC includes + * four gpio/pin-mux/pinconfig controllers. + */ +struct samsung_pin_ctrl exynos5250_pin_ctrl[] = { + { + /* pin-controller instance 0 data */ + .pin_banks = exynos5250_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos5250_pin_banks0), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .weint_con = EXYNOS_WKUP_ECON_OFFSET, + .weint_mask = EXYNOS_WKUP_EMASK_OFFSET, + .weint_pend = EXYNOS_WKUP_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .eint_wkup_init = exynos_eint_wkup_init, + .label = "exynos5250-gpio-ctrl0", + }, { + /* pin-controller instance 1 data */ + .pin_banks = exynos5250_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos5250_pin_banks1), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos5250-gpio-ctrl1", + }, { + /* pin-controller instance 2 data */ + .pin_banks = exynos5250_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos5250_pin_banks2), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos5250-gpio-ctrl2", + }, { + /* pin-controller instance 3 data */ + .pin_banks = exynos5250_pin_banks3, + .nr_banks = ARRAY_SIZE(exynos5250_pin_banks3), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos5250-gpio-ctrl3", + }, +}; diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index f206df175656..3d5cf639aa46 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -948,6 +948,8 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = { .data = (void *)exynos4210_pin_ctrl }, { .compatible = "samsung,exynos4x12-pinctrl", .data = (void *)exynos4x12_pin_ctrl }, + { .compatible = "samsung,exynos5250-pinctrl", + .data = (void *)exynos5250_pin_ctrl }, {}, }; MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match); diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index e2d4e67f7e88..ee964aadce0c 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -237,5 +237,6 @@ struct samsung_pmx_func { /* list of all exported SoC specific data */ extern struct samsung_pin_ctrl exynos4210_pin_ctrl[]; extern struct samsung_pin_ctrl exynos4x12_pin_ctrl[]; +extern struct samsung_pin_ctrl exynos5250_pin_ctrl[]; #endif /* __PINCTRL_SAMSUNG_H */ -- cgit v1.2.3 From 70850ad85370b2100ffe0e6a892c1d60569b476a Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Fri, 28 Dec 2012 10:37:27 -0800 Subject: gpio: samsung: skip gpiolib registration if pinctrl support is enabled for exynos5250 Skip exynos5250 gpiolib registration if pinctrl support for exynos5250 is enabled. Signed-off-by: Thomas Abraham Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index b3643ff007e4..49b4292b2d6a 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -3024,6 +3024,7 @@ static __init int samsung_gpiolib_init(void) static const struct of_device_id exynos_pinctrl_ids[] = { { .compatible = "samsung,exynos4210-pinctrl", }, { .compatible = "samsung,exynos4x12-pinctrl", }, + { .compatible = "samsung,exynos5250-pinctrl", }, { .compatible = "samsung,exynos5440-pinctrl", }, }; for_each_matching_node(pctrl_np, exynos_pinctrl_ids) -- cgit v1.2.3 From 6938d75a8c1a1752f9fa7ef14a0c570036c7b73b Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 9 Mar 2013 16:16:13 +0900 Subject: ARM: EXYNOS: move mct driver to drivers/clocksource Move the multi core timer (mct) driver to from mach-exynos to drivers/clocksource and update the Kconfig and makefiles. Cc: Changhwan Youn Signed-off-by: Thomas Abraham Reviewed-by: Stephen Warren Signed-off-by: Kukjin Kim --- drivers/clocksource/Kconfig | 5 + drivers/clocksource/Makefile | 1 + drivers/clocksource/exynos_mct.c | 550 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 556 insertions(+) create mode 100644 drivers/clocksource/exynos_mct.c (limited to 'drivers') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index e507ab7df60b..e8c453285151 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -67,3 +67,8 @@ config CLKSRC_METAG_GENERIC def_bool y if METAG help This option enables support for the Meta per-thread timers. + +config CLKSRC_EXYNOS_MCT + def_bool y if ARCH_EXYNOS + help + Support for Multi Core Timer controller on Exynos SoCs. diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 4d8283aec5b5..1c1b15db7c4d 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o +obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c new file mode 100644 index 000000000000..545c98976e93 --- /dev/null +++ b/drivers/clocksource/exynos_mct.c @@ -0,0 +1,550 @@ +/* linux/arch/arm/mach-exynos4/mct.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS4 MCT(Multi-Core 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 + +#include +#include +#include + +#define EXYNOS4_MCTREG(x) (x) +#define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100) +#define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104) +#define EXYNOS4_MCT_G_CNT_WSTAT EXYNOS4_MCTREG(0x110) +#define EXYNOS4_MCT_G_COMP0_L EXYNOS4_MCTREG(0x200) +#define EXYNOS4_MCT_G_COMP0_U EXYNOS4_MCTREG(0x204) +#define EXYNOS4_MCT_G_COMP0_ADD_INCR EXYNOS4_MCTREG(0x208) +#define EXYNOS4_MCT_G_TCON EXYNOS4_MCTREG(0x240) +#define EXYNOS4_MCT_G_INT_CSTAT EXYNOS4_MCTREG(0x244) +#define EXYNOS4_MCT_G_INT_ENB EXYNOS4_MCTREG(0x248) +#define EXYNOS4_MCT_G_WSTAT EXYNOS4_MCTREG(0x24C) +#define _EXYNOS4_MCT_L_BASE EXYNOS4_MCTREG(0x300) +#define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * x)) +#define EXYNOS4_MCT_L_MASK (0xffffff00) + +#define MCT_L_TCNTB_OFFSET (0x00) +#define MCT_L_ICNTB_OFFSET (0x08) +#define MCT_L_TCON_OFFSET (0x20) +#define MCT_L_INT_CSTAT_OFFSET (0x30) +#define MCT_L_INT_ENB_OFFSET (0x34) +#define MCT_L_WSTAT_OFFSET (0x40) +#define MCT_G_TCON_START (1 << 8) +#define MCT_G_TCON_COMP0_AUTO_INC (1 << 1) +#define MCT_G_TCON_COMP0_ENABLE (1 << 0) +#define MCT_L_TCON_INTERVAL_MODE (1 << 2) +#define MCT_L_TCON_INT_START (1 << 1) +#define MCT_L_TCON_TIMER_START (1 << 0) + +#define TICK_BASE_CNT 1 + +enum { + MCT_INT_SPI, + MCT_INT_PPI +}; + +enum { + MCT_G0_IRQ, + MCT_G1_IRQ, + MCT_G2_IRQ, + MCT_G3_IRQ, + MCT_L0_IRQ, + MCT_L1_IRQ, + MCT_L2_IRQ, + MCT_L3_IRQ, + MCT_NR_IRQS, +}; + +static void __iomem *reg_base; +static unsigned long clk_rate; +static unsigned int mct_int_type; +static int mct_irqs[MCT_NR_IRQS]; + +struct mct_clock_event_device { + struct clock_event_device *evt; + unsigned long base; + char name[10]; +}; + +static void exynos4_mct_write(unsigned int value, unsigned long offset) +{ + unsigned long stat_addr; + u32 mask; + u32 i; + + __raw_writel(value, reg_base + offset); + + if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) { + stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET; + switch (offset & EXYNOS4_MCT_L_MASK) { + case MCT_L_TCON_OFFSET: + mask = 1 << 3; /* L_TCON write status */ + break; + case MCT_L_ICNTB_OFFSET: + mask = 1 << 1; /* L_ICNTB write status */ + break; + case MCT_L_TCNTB_OFFSET: + mask = 1 << 0; /* L_TCNTB write status */ + break; + default: + return; + } + } else { + switch (offset) { + case EXYNOS4_MCT_G_TCON: + stat_addr = EXYNOS4_MCT_G_WSTAT; + mask = 1 << 16; /* G_TCON write status */ + break; + case EXYNOS4_MCT_G_COMP0_L: + stat_addr = EXYNOS4_MCT_G_WSTAT; + mask = 1 << 0; /* G_COMP0_L write status */ + break; + case EXYNOS4_MCT_G_COMP0_U: + stat_addr = EXYNOS4_MCT_G_WSTAT; + mask = 1 << 1; /* G_COMP0_U write status */ + break; + case EXYNOS4_MCT_G_COMP0_ADD_INCR: + stat_addr = EXYNOS4_MCT_G_WSTAT; + mask = 1 << 2; /* G_COMP0_ADD_INCR w status */ + break; + case EXYNOS4_MCT_G_CNT_L: + stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; + mask = 1 << 0; /* G_CNT_L write status */ + break; + case EXYNOS4_MCT_G_CNT_U: + stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; + mask = 1 << 1; /* G_CNT_U write status */ + break; + default: + return; + } + } + + /* Wait maximum 1 ms until written values are applied */ + for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++) + if (__raw_readl(reg_base + stat_addr) & mask) { + __raw_writel(mask, reg_base + stat_addr); + return; + } + + panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset); +} + +/* Clocksource handling */ +static void exynos4_mct_frc_start(u32 hi, u32 lo) +{ + u32 reg; + + exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L); + exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U); + + reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON); + reg |= MCT_G_TCON_START; + exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON); +} + +static cycle_t exynos4_frc_read(struct clocksource *cs) +{ + unsigned int lo, hi; + u32 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U); + + do { + hi = hi2; + lo = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_L); + hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U); + } while (hi != hi2); + + return ((cycle_t)hi << 32) | lo; +} + +static void exynos4_frc_resume(struct clocksource *cs) +{ + exynos4_mct_frc_start(0, 0); +} + +struct clocksource mct_frc = { + .name = "mct-frc", + .rating = 400, + .read = exynos4_frc_read, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .resume = exynos4_frc_resume, +}; + +static void __init exynos4_clocksource_init(void) +{ + exynos4_mct_frc_start(0, 0); + + if (clocksource_register_hz(&mct_frc, clk_rate)) + panic("%s: can't register clocksource\n", mct_frc.name); +} + +static void exynos4_mct_comp0_stop(void) +{ + unsigned int tcon; + + tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON); + tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC); + + exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON); + exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB); +} + +static void exynos4_mct_comp0_start(enum clock_event_mode mode, + unsigned long cycles) +{ + unsigned int tcon; + cycle_t comp_cycle; + + tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON); + + if (mode == CLOCK_EVT_MODE_PERIODIC) { + tcon |= MCT_G_TCON_COMP0_AUTO_INC; + exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR); + } + + comp_cycle = exynos4_frc_read(&mct_frc) + cycles; + exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L); + exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U); + + exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB); + + tcon |= MCT_G_TCON_COMP0_ENABLE; + exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON); +} + +static int exynos4_comp_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + exynos4_mct_comp0_start(evt->mode, cycles); + + return 0; +} + +static void exynos4_comp_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + unsigned long cycles_per_jiffy; + exynos4_mct_comp0_stop(); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + cycles_per_jiffy = + (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift); + exynos4_mct_comp0_start(mode, cycles_per_jiffy); + break; + + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_RESUME: + break; + } +} + +static struct clock_event_device mct_comp_device = { + .name = "mct-comp", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .rating = 250, + .set_next_event = exynos4_comp_set_next_event, + .set_mode = exynos4_comp_set_mode, +}; + +static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction mct_comp_event_irq = { + .name = "mct_comp_irq", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = exynos4_mct_comp_isr, + .dev_id = &mct_comp_device, +}; + +static void exynos4_clockevent_init(void) +{ + mct_comp_device.cpumask = cpumask_of(0); + clockevents_config_and_register(&mct_comp_device, clk_rate, + 0xf, 0xffffffff); + setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq); +} + +#ifdef CONFIG_LOCAL_TIMERS + +static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick); + +/* Clock event handling */ +static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt) +{ + unsigned long tmp; + unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START; + unsigned long offset = mevt->base + MCT_L_TCON_OFFSET; + + tmp = __raw_readl(reg_base + offset); + if (tmp & mask) { + tmp &= ~mask; + exynos4_mct_write(tmp, offset); + } +} + +static void exynos4_mct_tick_start(unsigned long cycles, + struct mct_clock_event_device *mevt) +{ + unsigned long tmp; + + exynos4_mct_tick_stop(mevt); + + tmp = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */ + + /* update interrupt count buffer */ + exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET); + + /* enable MCT tick interrupt */ + exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET); + + tmp = __raw_readl(reg_base + mevt->base + MCT_L_TCON_OFFSET); + tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START | + MCT_L_TCON_INTERVAL_MODE; + exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET); +} + +static int exynos4_tick_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick); + + exynos4_mct_tick_start(cycles, mevt); + + return 0; +} + +static inline void exynos4_tick_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick); + unsigned long cycles_per_jiffy; + + exynos4_mct_tick_stop(mevt); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + cycles_per_jiffy = + (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift); + exynos4_mct_tick_start(cycles_per_jiffy, mevt); + break; + + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_RESUME: + break; + } +} + +static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt) +{ + struct clock_event_device *evt = mevt->evt; + + /* + * This is for supporting oneshot mode. + * Mct would generate interrupt periodically + * without explicit stopping. + */ + if (evt->mode != CLOCK_EVT_MODE_PERIODIC) + exynos4_mct_tick_stop(mevt); + + /* Clear the MCT tick interrupt */ + if (__raw_readl(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) { + exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); + return 1; + } else { + return 0; + } +} + +static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id) +{ + struct mct_clock_event_device *mevt = dev_id; + struct clock_event_device *evt = mevt->evt; + + exynos4_mct_tick_clear(mevt); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction mct_tick0_event_irq = { + .name = "mct_tick0_irq", + .flags = IRQF_TIMER | IRQF_NOBALANCING, + .handler = exynos4_mct_tick_isr, +}; + +static struct irqaction mct_tick1_event_irq = { + .name = "mct_tick1_irq", + .flags = IRQF_TIMER | IRQF_NOBALANCING, + .handler = exynos4_mct_tick_isr, +}; + +static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt) +{ + struct mct_clock_event_device *mevt; + unsigned int cpu = smp_processor_id(); + + mevt = this_cpu_ptr(&percpu_mct_tick); + mevt->evt = evt; + + mevt->base = EXYNOS4_MCT_L_BASE(cpu); + sprintf(mevt->name, "mct_tick%d", cpu); + + evt->name = mevt->name; + evt->cpumask = cpumask_of(cpu); + evt->set_next_event = exynos4_tick_set_next_event; + evt->set_mode = exynos4_tick_set_mode; + evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + evt->rating = 450; + clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1), + 0xf, 0x7fffffff); + + exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET); + + if (mct_int_type == MCT_INT_SPI) { + if (cpu == 0) { + mct_tick0_event_irq.dev_id = mevt; + evt->irq = mct_irqs[MCT_L0_IRQ]; + setup_irq(evt->irq, &mct_tick0_event_irq); + } else { + mct_tick1_event_irq.dev_id = mevt; + evt->irq = mct_irqs[MCT_L1_IRQ]; + setup_irq(evt->irq, &mct_tick1_event_irq); + irq_set_affinity(evt->irq, cpumask_of(1)); + } + } else { + enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0); + } + + return 0; +} + +static void exynos4_local_timer_stop(struct clock_event_device *evt) +{ + unsigned int cpu = smp_processor_id(); + evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); + if (mct_int_type == MCT_INT_SPI) + if (cpu == 0) + remove_irq(evt->irq, &mct_tick0_event_irq); + else + remove_irq(evt->irq, &mct_tick1_event_irq); + else + disable_percpu_irq(mct_irqs[MCT_L0_IRQ]); +} + +static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = { + .setup = exynos4_local_timer_setup, + .stop = exynos4_local_timer_stop, +}; +#endif /* CONFIG_LOCAL_TIMERS */ + +static void __init exynos4_timer_resources(struct device_node *np) +{ + struct clk *mct_clk; + mct_clk = clk_get(NULL, "xtal"); + + clk_rate = clk_get_rate(mct_clk); + + reg_base = np ? of_iomap(np, 0) : S5P_VA_SYSTIMER; + if (!reg_base) + panic("%s: unable to ioremap mct address space\n", __func__); + +#ifdef CONFIG_LOCAL_TIMERS + if (mct_int_type == MCT_INT_PPI) { + int err; + + err = request_percpu_irq(mct_irqs[MCT_L0_IRQ], + exynos4_mct_tick_isr, "MCT", + &percpu_mct_tick); + WARN(err, "MCT: can't request IRQ %d (%d)\n", + mct_irqs[MCT_L0_IRQ], err); + } + + local_timer_register(&exynos4_mct_tick_ops); +#endif /* CONFIG_LOCAL_TIMERS */ +} + +static const struct of_device_id exynos_mct_ids[] = { + { .compatible = "samsung,exynos4210-mct", .data = (void *)MCT_INT_SPI }, + { .compatible = "samsung,exynos4412-mct", .data = (void *)MCT_INT_PPI }, +}; + +void __init mct_init(void) +{ + struct device_node *np = NULL; + const struct of_device_id *match; + u32 nr_irqs, i; + +#ifdef CONFIG_OF + np = of_find_matching_node_and_match(NULL, exynos_mct_ids, &match); +#endif + if (np) { + mct_int_type = (u32)(match->data); + + /* This driver uses only one global timer interrupt */ + mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ); + + /* + * Find out the number of local irqs specified. The local + * timer irqs are specified after the four global timer + * irqs are specified. + */ +#ifdef CONFIG_OF + nr_irqs = of_irq_count(np); +#endif + for (i = MCT_L0_IRQ; i < nr_irqs; i++) + mct_irqs[i] = irq_of_parse_and_map(np, i); + } else if (soc_is_exynos4210()) { + mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0; + mct_irqs[MCT_L0_IRQ] = EXYNOS4_IRQ_MCT_L0; + mct_irqs[MCT_L1_IRQ] = EXYNOS4_IRQ_MCT_L1; + mct_int_type = MCT_INT_SPI; + } else { + panic("unable to determine mct controller type\n"); + } + + exynos4_timer_resources(np); + exynos4_clocksource_init(); + exynos4_clockevent_init(); +} +CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init) +CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init) -- cgit v1.2.3 From 2fd61b32764c82b8410a4374d0ab3ec418ce37c7 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Dec 2012 14:53:32 +0900 Subject: clocksource: sh_cmt: Take care of clk_put() when setup_irq() fails Make sure clk_put() is called in case of failure in sh_cmt_setup(). Signed-off-by: Shinya Kuribayashi Signed-off-by: Magnus Damm Acked-by: John Stultz Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/clocksource/sh_cmt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 488c14cc8dbf..4b8d2962cad7 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -708,17 +708,19 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) cfg->clocksource_rating); if (ret) { dev_err(&p->pdev->dev, "registration failed\n"); - goto err1; + goto err2; } p->cs_enabled = false; ret = setup_irq(irq, &p->irqaction); if (ret) { dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); - goto err1; + goto err2; } return 0; +err2: + clk_put(p->clk); err1: iounmap(p->mapbase); -- cgit v1.2.3 From 44a10f943f59339f1206d599d4269a35995e397e Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Dec 2012 14:53:41 +0900 Subject: clocksource: sh_cmt: Initialize 'max_match_value' and 'lock' in sh_cmt_setup() Move the setup of spinlock and max_match_value to sh_cmt_setup(). There's no need to defer those steps until sh_cmt_register(). Signed-off-by: Shinya Kuribayashi Signed-off-by: Magnus Damm Acked-by: John Stultz Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/clocksource/sh_cmt.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 4b8d2962cad7..2e496841b167 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -625,14 +625,6 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name, unsigned long clockevent_rating, unsigned long clocksource_rating) { - if (p->width == (sizeof(p->max_match_value) * 8)) - p->max_match_value = ~0; - else - p->max_match_value = (1 << p->width) - 1; - - p->match_value = p->max_match_value; - raw_spin_lock_init(&p->lock); - if (clockevent_rating) sh_cmt_register_clockevent(p, name, clockevent_rating); @@ -703,6 +695,14 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) p->clear_bits = ~0xc000; } + if (p->width == (sizeof(p->max_match_value) * 8)) + p->max_match_value = ~0; + else + p->max_match_value = (1 << p->width) - 1; + + p->match_value = p->max_match_value; + raw_spin_lock_init(&p->lock); + ret = sh_cmt_register(p, (char *)dev_name(&p->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); -- cgit v1.2.3 From 1b56b96b663d135305c3c47755fbdde3dc0ef720 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Dec 2012 14:54:00 +0900 Subject: clocksource: sh_cmt: Introduce per-register functions Introduce sh_cmt_read_cmstr/cmcsr/cmcnt() and sh_cmt_write_cmstr/cmcsr/cmcnt/cmcor() to in the future allow us to split counter registers from control registers and reduce code complexity by removing sh_cmt_read() and sh_cmt_write(). Signed-off-by: Magnus Damm Acked-by: John Stultz Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/clocksource/sh_cmt.c | 71 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 2e496841b167..94fd3abd6434 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -86,6 +86,21 @@ static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr) return ioread16(base + offs); } +static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) +{ + return sh_cmt_read(p, CMSTR); +} + +static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) +{ + return sh_cmt_read(p, CMCSR); +} + +static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) +{ + return sh_cmt_read(p, CMCNT); +} + static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr, unsigned long value) { @@ -112,21 +127,45 @@ static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr, iowrite16(value, base + offs); } +static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, + unsigned long value) +{ + sh_cmt_write(p, CMSTR, value); +} + +static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, + unsigned long value) +{ + sh_cmt_write(p, CMCSR, value); +} + +static inline void sh_cmt_write_cmcnt(struct sh_cmt_priv *p, + unsigned long value) +{ + sh_cmt_write(p, CMCNT, value); +} + +static inline void sh_cmt_write_cmcor(struct sh_cmt_priv *p, + unsigned long value) +{ + sh_cmt_write(p, CMCOR, value); +} + static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p, int *has_wrapped) { unsigned long v1, v2, v3; int o1, o2; - o1 = sh_cmt_read(p, CMCSR) & p->overflow_bit; + o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit; /* Make sure the timer value is stable. Stolen from acpi_pm.c */ do { o2 = o1; - v1 = sh_cmt_read(p, CMCNT); - v2 = sh_cmt_read(p, CMCNT); - v3 = sh_cmt_read(p, CMCNT); - o1 = sh_cmt_read(p, CMCSR) & p->overflow_bit; + v1 = sh_cmt_read_cmcnt(p); + v2 = sh_cmt_read_cmcnt(p); + v3 = sh_cmt_read_cmcnt(p); + o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit; } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); @@ -142,14 +181,14 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) /* start stop register shared by multiple timer channels */ raw_spin_lock_irqsave(&sh_cmt_lock, flags); - value = sh_cmt_read(p, CMSTR); + value = sh_cmt_read_cmstr(p); if (start) value |= 1 << cfg->timer_bit; else value &= ~(1 << cfg->timer_bit); - sh_cmt_write(p, CMSTR, value); + sh_cmt_write_cmstr(p, value); raw_spin_unlock_irqrestore(&sh_cmt_lock, flags); } @@ -173,14 +212,14 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) /* configure channel, periodic mode and maximum timeout */ if (p->width == 16) { *rate = clk_get_rate(p->clk) / 512; - sh_cmt_write(p, CMCSR, 0x43); + sh_cmt_write_cmcsr(p, 0x43); } else { *rate = clk_get_rate(p->clk) / 8; - sh_cmt_write(p, CMCSR, 0x01a4); + sh_cmt_write_cmcsr(p, 0x01a4); } - sh_cmt_write(p, CMCOR, 0xffffffff); - sh_cmt_write(p, CMCNT, 0); + sh_cmt_write_cmcor(p, 0xffffffff); + sh_cmt_write_cmcnt(p, 0); /* * According to the sh73a0 user's manual, as CMCNT can be operated @@ -194,12 +233,12 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) * take RCLKx2 at maximum. */ for (k = 0; k < 100; k++) { - if (!sh_cmt_read(p, CMCNT)) + if (!sh_cmt_read_cmcnt(p)) break; udelay(1); } - if (sh_cmt_read(p, CMCNT)) { + if (sh_cmt_read_cmcnt(p)) { dev_err(&p->pdev->dev, "cannot clear CMCNT\n"); ret = -ETIMEDOUT; goto err1; @@ -222,7 +261,7 @@ static void sh_cmt_disable(struct sh_cmt_priv *p) sh_cmt_start_stop_ch(p, 0); /* disable interrupts in CMT block */ - sh_cmt_write(p, CMCSR, 0); + sh_cmt_write_cmcsr(p, 0); /* stop clock */ clk_disable(p->clk); @@ -270,7 +309,7 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p, if (new_match > p->max_match_value) new_match = p->max_match_value; - sh_cmt_write(p, CMCOR, new_match); + sh_cmt_write_cmcor(p, new_match); now = sh_cmt_get_counter(p, &has_wrapped); if (has_wrapped && (new_match > p->match_value)) { @@ -346,7 +385,7 @@ static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id) struct sh_cmt_priv *p = dev_id; /* clear flags */ - sh_cmt_write(p, CMCSR, sh_cmt_read(p, CMCSR) & p->clear_bits); + sh_cmt_write_cmcsr(p, sh_cmt_read_cmcsr(p) & p->clear_bits); /* update clock source counter to begin with if enabled * the wrap flag should be cleared by the timer specific -- cgit v1.2.3 From adccc69e7ad1815ce79b073830b244a803776bbd Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Dec 2012 14:53:51 +0900 Subject: clocksource: sh_cmt: Consolidate platform_set_drvdata() call Cleanup the use of platform_set_drvdata() to reduce code size Signed-off-by: Shinya Kuribayashi Signed-off-by: Magnus Damm Acked-by: John Stultz Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/clocksource/sh_cmt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 94fd3abd6434..a2f8023a846b 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -688,8 +688,6 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) goto err0; } - platform_set_drvdata(pdev, p); - res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&p->pdev->dev, "failed to get I/O memory\n"); @@ -757,6 +755,8 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) goto err2; } + platform_set_drvdata(pdev, p); + return 0; err2: clk_put(p->clk); @@ -792,7 +792,6 @@ static int sh_cmt_probe(struct platform_device *pdev) ret = sh_cmt_setup(p, pdev); if (ret) { kfree(p); - platform_set_drvdata(pdev, NULL); pm_runtime_idle(&pdev->dev); return ret; } -- cgit v1.2.3 From 587acb3dd5cf387de1325309e831fd0f560d1bf6 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Dec 2012 14:54:10 +0900 Subject: clocksource: sh_cmt: CMSTR and CMCSR register access update Update hardware register access code for CMSTR and CMCSR from using sh_cmt_read() and sh_cmt_write() to make use of 16-bit register access functions such as sh_cmt_read16() and sh_cmt_write16(). Also update sh_cmt_read() and sh_cmt_write() now when the special cases are gone. This patch moves us one step closer to the goal of separating counter register access functions from control control register functions. Signed-off-by: Magnus Damm Acked-by: John Stultz Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/clocksource/sh_cmt.c | 66 ++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index a2f8023a846b..eefacc3ac4f2 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -56,44 +56,46 @@ struct sh_cmt_priv { bool cs_enabled; }; -static DEFINE_RAW_SPINLOCK(sh_cmt_lock); +static inline unsigned long sh_cmt_read16(void __iomem *base, + unsigned long offs) +{ + return ioread16(base + (offs << 1)); +} + +static inline void sh_cmt_write16(void __iomem *base, unsigned long offs, + unsigned long value) +{ + iowrite16(value, base + (offs << 1)); +} -#define CMSTR -1 /* shared register */ #define CMCSR 0 /* channel register */ #define CMCNT 1 /* channel register */ #define CMCOR 2 /* channel register */ static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; void __iomem *base = p->mapbase; - unsigned long offs; - - if (reg_nr == CMSTR) { - offs = 0; - base -= cfg->channel_offset; - } else - offs = reg_nr; + unsigned long offs = reg_nr; - if (p->width == 16) + if (p->width == 16) { offs <<= 1; - else { + return ioread16(base + offs); + } else { offs <<= 2; - if ((reg_nr == CMCNT) || (reg_nr == CMCOR)) - return ioread32(base + offs); + return ioread32(base + offs); } - - return ioread16(base + offs); } static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) { - return sh_cmt_read(p, CMSTR); + struct sh_timer_config *cfg = p->pdev->dev.platform_data; + + return sh_cmt_read16(p->mapbase - cfg->channel_offset, 0); } static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) { - return sh_cmt_read(p, CMCSR); + return sh_cmt_read16(p->mapbase, CMCSR); } static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) @@ -104,39 +106,30 @@ static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr, unsigned long value) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; void __iomem *base = p->mapbase; - unsigned long offs; + unsigned long offs = reg_nr; - if (reg_nr == CMSTR) { - offs = 0; - base -= cfg->channel_offset; - } else - offs = reg_nr; - - if (p->width == 16) + if (p->width == 16) { offs <<= 1; - else { + iowrite16(value, base + offs); + } else { offs <<= 2; - if ((reg_nr == CMCNT) || (reg_nr == CMCOR)) { - iowrite32(value, base + offs); - return; - } + iowrite32(value, base + offs); } - - iowrite16(value, base + offs); } static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, unsigned long value) { - sh_cmt_write(p, CMSTR, value); + struct sh_timer_config *cfg = p->pdev->dev.platform_data; + + sh_cmt_write16(p->mapbase - cfg->channel_offset, 0, value); } static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, unsigned long value) { - sh_cmt_write(p, CMCSR, value); + sh_cmt_write16(p->mapbase, CMCSR, value); } static inline void sh_cmt_write_cmcnt(struct sh_cmt_priv *p, @@ -173,6 +166,7 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p, return v2; } +static DEFINE_RAW_SPINLOCK(sh_cmt_lock); static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) { -- cgit v1.2.3 From a6a912ca43843d43590ce5f1cbc85cbc7ac14bba Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Dec 2012 14:54:19 +0900 Subject: clocksource: sh_cmt: CMCNT and CMCOR register access update Break out the CMCNT and CMCOR register access code into separate 16-bit and 32-bit functions that are hooked into callbacks at init time. This reduces the amount of software calculations happening at runtime. Signed-off-by: Magnus Damm Acked-by: John Stultz Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/clocksource/sh_cmt.c | 62 +++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index eefacc3ac4f2..da904d7f7530 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -54,38 +54,39 @@ struct sh_cmt_priv { struct clocksource cs; unsigned long total_cycles; bool cs_enabled; + + /* callbacks for CMCNT and CMCOR access */ + unsigned long (*read_count)(void __iomem *base, unsigned long offs); + void (*write_count)(void __iomem *base, unsigned long offs, + unsigned long value); }; -static inline unsigned long sh_cmt_read16(void __iomem *base, - unsigned long offs) +static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs) { return ioread16(base + (offs << 1)); } -static inline void sh_cmt_write16(void __iomem *base, unsigned long offs, - unsigned long value) +static unsigned long sh_cmt_read32(void __iomem *base, unsigned long offs) +{ + return ioread32(base + (offs << 2)); +} + +static void sh_cmt_write16(void __iomem *base, unsigned long offs, + unsigned long value) { iowrite16(value, base + (offs << 1)); } +static void sh_cmt_write32(void __iomem *base, unsigned long offs, + unsigned long value) +{ + iowrite32(value, base + (offs << 2)); +} + #define CMCSR 0 /* channel register */ #define CMCNT 1 /* channel register */ #define CMCOR 2 /* channel register */ -static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr) -{ - void __iomem *base = p->mapbase; - unsigned long offs = reg_nr; - - if (p->width == 16) { - offs <<= 1; - return ioread16(base + offs); - } else { - offs <<= 2; - return ioread32(base + offs); - } -} - static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) { struct sh_timer_config *cfg = p->pdev->dev.platform_data; @@ -100,22 +101,7 @@ static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) { - return sh_cmt_read(p, CMCNT); -} - -static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr, - unsigned long value) -{ - void __iomem *base = p->mapbase; - unsigned long offs = reg_nr; - - if (p->width == 16) { - offs <<= 1; - iowrite16(value, base + offs); - } else { - offs <<= 2; - iowrite32(value, base + offs); - } + return p->read_count(p->mapbase, CMCNT); } static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, @@ -135,13 +121,13 @@ static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, static inline void sh_cmt_write_cmcnt(struct sh_cmt_priv *p, unsigned long value) { - sh_cmt_write(p, CMCNT, value); + p->write_count(p->mapbase, CMCNT, value); } static inline void sh_cmt_write_cmcor(struct sh_cmt_priv *p, unsigned long value) { - sh_cmt_write(p, CMCOR, value); + p->write_count(p->mapbase, CMCOR, value); } static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p, @@ -718,10 +704,14 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) if (resource_size(res) == 6) { p->width = 16; + p->read_count = sh_cmt_read16; + p->write_count = sh_cmt_write16; p->overflow_bit = 0x80; p->clear_bits = ~0x80; } else { p->width = 32; + p->read_count = sh_cmt_read32; + p->write_count = sh_cmt_write32; p->overflow_bit = 0x8000; p->clear_bits = ~0xc000; } -- cgit v1.2.3 From cccd70455c351604d0a9075d35298ed4ff66dab3 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Dec 2012 14:54:28 +0900 Subject: clocksource: sh_cmt: Add control register callbacks This patch adds control register callbacks for the CMT driver. At this point only 16-bit access is supported but in the future this will be updated to allow 32-bit access as well. Signed-off-by: Magnus Damm Acked-by: John Stultz Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/clocksource/sh_cmt.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index da904d7f7530..7108963a6ab8 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -55,6 +55,11 @@ struct sh_cmt_priv { unsigned long total_cycles; bool cs_enabled; + /* callbacks for CMSTR and CMCSR access */ + unsigned long (*read_control)(void __iomem *base, unsigned long offs); + void (*write_control)(void __iomem *base, unsigned long offs, + unsigned long value); + /* callbacks for CMCNT and CMCOR access */ unsigned long (*read_count)(void __iomem *base, unsigned long offs); void (*write_count)(void __iomem *base, unsigned long offs, @@ -91,12 +96,12 @@ static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) { struct sh_timer_config *cfg = p->pdev->dev.platform_data; - return sh_cmt_read16(p->mapbase - cfg->channel_offset, 0); + return p->read_control(p->mapbase - cfg->channel_offset, 0); } static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) { - return sh_cmt_read16(p->mapbase, CMCSR); + return p->read_control(p->mapbase, CMCSR); } static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) @@ -109,13 +114,13 @@ static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, { struct sh_timer_config *cfg = p->pdev->dev.platform_data; - sh_cmt_write16(p->mapbase - cfg->channel_offset, 0, value); + p->write_control(p->mapbase - cfg->channel_offset, 0, value); } static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, unsigned long value) { - sh_cmt_write16(p->mapbase, CMCSR, value); + p->write_control(p->mapbase, CMCSR, value); } static inline void sh_cmt_write_cmcnt(struct sh_cmt_priv *p, @@ -702,6 +707,9 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) goto err1; } + p->read_control = sh_cmt_read16; + p->write_control = sh_cmt_write16; + if (resource_size(res) == 6) { p->width = 16; p->read_count = sh_cmt_read16; -- cgit v1.2.3 From 118aee4dd91cf3c0b9546788ef66b65d3e9e31b1 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Dec 2012 14:54:37 +0900 Subject: clocksource: sh_cmt: Add CMT register layout comment Add a comment about different register layouts supported by the CMT driver. Signed-off-by: Magnus Damm Acked-by: John Stultz Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/clocksource/sh_cmt.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 7108963a6ab8..b72b7242125e 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -66,6 +66,21 @@ struct sh_cmt_priv { unsigned long value); }; +/* Examples of supported CMT timer register layouts and I/O access widths: + * + * "16-bit counter and 16-bit control" as found on sh7263: + * CMSTR 0xfffec000 16-bit + * CMCSR 0xfffec002 16-bit + * CMCNT 0xfffec004 16-bit + * CMCOR 0xfffec006 16-bit + * + * "32-bit counter and 16-bit control" as found on sh7372, sh73a0, r8a7740: + * CMSTR 0xffca0000 16-bit + * CMCSR 0xffca0060 16-bit + * CMCNT 0xffca0064 32-bit + * CMCOR 0xffca0068 32-bit + */ + static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs) { return ioread16(base + (offs << 1)); -- cgit v1.2.3 From e903a031402c8dccc675b2f0cf8af40ac89163b0 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 5 Mar 2013 15:40:42 +0900 Subject: clocksource: sh_cmt: Set initcall level to subsys The reason for this is to ensure that CMT is probed earlier than with its previous initcall level, module init. This came up as a problem with using kzm9g-reference which does not make use of early timers or devices. In that scenario initialisation of SDHI and MMCIF both stall on msleep() calls due to the absence of a initialised clock source. Boot tested on: armadillo800eva, mackerel and kzm9g Signed-off-by: Simon Horman --- drivers/clocksource/sh_cmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index b72b7242125e..08d0c418c94a 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -838,7 +838,7 @@ static void __exit sh_cmt_exit(void) } early_platform_init("earlytimer", &sh_cmt_device_driver); -module_init(sh_cmt_init); +subsys_initcall(sh_cmt_init); module_exit(sh_cmt_exit); MODULE_AUTHOR("Magnus Damm"); -- cgit v1.2.3 From b9773c3f52540ada159dc135c07653be010deee7 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 5 Mar 2013 15:40:42 +0900 Subject: clocksource: sh_tmu: Set initcall level to subsys The reason for this is to ensure that TMU is probed earlier than with its previous initcall level, module init. This came up as a problem with using CMT as a clock source kzm9g-reference which does not make use of early timers or devices. In that scenario initialisation of SDHI and MMCIF both stall on msleep() calls due to the absence of a initialised clock source. The purpose of this change is to keep the TMU code in sync with the CMT code which has been modified in a similar manner.. Boot tested on: mackerel. Signed-off-by: Simon Horman --- drivers/clocksource/sh_tmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index b4502edce2a1..78b8dae49628 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -549,7 +549,7 @@ static void __exit sh_tmu_exit(void) } early_platform_init("earlytimer", &sh_tmu_device_driver); -module_init(sh_tmu_init); +subsys_initcall(sh_tmu_init); module_exit(sh_tmu_exit); MODULE_AUTHOR("Magnus Damm"); -- cgit v1.2.3 From 09acc3a1e0dee537fcfcd5a6c17a1e9b26586066 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 5 Mar 2013 15:40:42 +0900 Subject: clocksource: em_sti: Set initcall level to subsys The reason for this is to ensure that STI is probed earlier than with its previous initcall level, module init. This came up as a problem with using CMT as a clock source kzm9g-reference which does not make use of early timers or devices. In that scenario initialisation of SDHI and MMCIF both stall on msleep() calls due to the absence of a initialised clock source. The purpose of this change is to keep the STI code in sync with the CMT code which has been modified in a similar manner.. Boot tested on: kzm9d. Signed-off-by: Simon Horman --- drivers/clocksource/em_sti.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c index e6a553cb73e8..4329a29a5310 100644 --- a/drivers/clocksource/em_sti.c +++ b/drivers/clocksource/em_sti.c @@ -399,7 +399,18 @@ static struct platform_driver em_sti_device_driver = { } }; -module_platform_driver(em_sti_device_driver); +static int __init em_sti_init(void) +{ + return platform_driver_register(&em_sti_device_driver); +} + +static void __exit em_sti_exit(void) +{ + platform_driver_unregister(&em_sti_device_driver); +} + +subsys_initcall(em_sti_init); +module_exit(em_sti_exit); MODULE_AUTHOR("Magnus Damm"); MODULE_DESCRIPTION("Renesas Emma Mobile STI Timer Driver"); -- cgit v1.2.3 From 342896a5c565e38dfb87954f362735f03ae1efb0 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 5 Mar 2013 15:40:42 +0900 Subject: clocksource: sh_mtu2: Set initcall level to subsys The reason for this is to ensure that MTU2 is probed earlier than with its previous initcall level, module init. This came up as a problem with using CMT as a clock source kzm9g-reference which does not make use of early timers or devices. In that scenario initialisation of SDHI and MMCIF both stall on msleep() calls due to the absence of a initialised clock source. The purpose of this change is to keep the MTU2 code in sync with the CMT code which has been modified in a similar manner.. Compile tested only using se7206_defconfig. I do not believe I have any boards that support the MTU2. Signed-off-by: Simon Horman --- drivers/clocksource/sh_mtu2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 83943e27cfac..4aac9ee0d0c0 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -386,7 +386,7 @@ static void __exit sh_mtu2_exit(void) } early_platform_init("earlytimer", &sh_mtu2_device_driver); -module_init(sh_mtu2_init); +subsys_initcall(sh_mtu2_init); module_exit(sh_mtu2_exit); MODULE_AUTHOR("Magnus Damm"); -- cgit v1.2.3 From 557b7d5d0787997247a798e6f46541fe9dc977fa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Feb 2013 16:31:56 +0100 Subject: ARM: at91/avr32/atmel_lcdfb: add bus-clock entry Add hclk entry for the atmel_lcdfb bus clock. On at91sam9261, at91sam9g10 and at32ap the bus clock has to be enabled as well as the peripheral clock. Add the appropriate lookup entries to these SOCs and fake clocks to the SOCs that do not use it. This allows us to get rid of the conditional enabling of the clocks in the driver which relied on the cpu_is macros. Tested on at91sam9263 and at91sam9g45, compile-tested for other AT91-SOCs, and untested for AVR32. Signed-off-by: Johan Hovold Signed-off-by: Nicolas Ferre --- drivers/video/atmel_lcdfb.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 025428e04c33..c5883cafa38a 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -821,15 +821,13 @@ static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo) static void atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo) { - if (sinfo->bus_clk) - clk_enable(sinfo->bus_clk); + clk_enable(sinfo->bus_clk); clk_enable(sinfo->lcdc_clk); } static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo) { - if (sinfo->bus_clk) - clk_disable(sinfo->bus_clk); + clk_disable(sinfo->bus_clk); clk_disable(sinfo->lcdc_clk); } @@ -888,13 +886,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) info->fix = atmel_lcdfb_fix; /* Enable LCDC Clocks */ - if (cpu_is_at91sam9261() || cpu_is_at91sam9g10() - || cpu_is_at32ap7000()) { - sinfo->bus_clk = clk_get(dev, "hck1"); - if (IS_ERR(sinfo->bus_clk)) { - ret = PTR_ERR(sinfo->bus_clk); - goto free_info; - } + sinfo->bus_clk = clk_get(dev, "hclk"); + if (IS_ERR(sinfo->bus_clk)) { + ret = PTR_ERR(sinfo->bus_clk); + goto free_info; } sinfo->lcdc_clk = clk_get(dev, "lcdc_clk"); if (IS_ERR(sinfo->lcdc_clk)) { @@ -1055,8 +1050,7 @@ stop_clk: atmel_lcdfb_stop_clock(sinfo); clk_put(sinfo->lcdc_clk); put_bus_clk: - if (sinfo->bus_clk) - clk_put(sinfo->bus_clk); + clk_put(sinfo->bus_clk); free_info: framebuffer_release(info); out: @@ -1081,8 +1075,7 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev) unregister_framebuffer(info); atmel_lcdfb_stop_clock(sinfo); clk_put(sinfo->lcdc_clk); - if (sinfo->bus_clk) - clk_put(sinfo->bus_clk); + clk_put(sinfo->bus_clk); fb_dealloc_cmap(&info->cmap); free_irq(sinfo->irq_base, info); iounmap(sinfo->mmio); -- cgit v1.2.3 From 934a50bd089789d1ed74f0bef4988a97bd590afe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Feb 2013 16:31:57 +0100 Subject: atmel_lcdfb: move lcdcon2 register access to compute_hozval Pass atmel_lcd_info structure to compute_hozval and only do the register access on SOCs that actually use it. This will also simplify the removal of the cpu_is macros. Signed-off-by: Johan Hovold Signed-off-by: Nicolas Ferre --- drivers/video/atmel_lcdfb.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index c5883cafa38a..2effd35da589 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -193,14 +193,17 @@ static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = { .accel = FB_ACCEL_NONE, }; -static unsigned long compute_hozval(unsigned long xres, unsigned long lcdcon2) +static unsigned long compute_hozval(struct atmel_lcdfb_info *sinfo, + unsigned long xres) { + unsigned long lcdcon2; unsigned long value; if (!(cpu_is_at91sam9261() || cpu_is_at91sam9g10() || cpu_is_at32ap7000())) return xres; + lcdcon2 = lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2); value = xres; if ((lcdcon2 & ATMEL_LCDC_DISTYPE) != ATMEL_LCDC_DISTYPE_TFT) { /* STN display */ @@ -591,8 +594,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info) lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value); /* Horizontal value (aka line size) */ - hozval_linesz = compute_hozval(info->var.xres, - lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2)); + hozval_linesz = compute_hozval(sinfo, info->var.xres); /* Display size */ value = (hozval_linesz - 1) << ATMEL_LCDC_HOZVAL_OFFSET; -- cgit v1.2.3 From bbd44f6bd9d1aa735b180b29b5719d63a8e87b55 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Feb 2013 16:31:58 +0100 Subject: ARM: at91/avr32/atmel_lcdfb: add platform device-id table Add platform device-id table in order to identify the controller and determine its configuration. The currently used configuration parameters are: have_alt_pixclock - SOC uses an alternate pixel-clock calculation formula (at91sam9g45 non-ES) have_hozval - SOC has a HOZVAL field in LCDFRMCFG which is used to determine the linesize for STN displays (at91sam9261, at921sam9g10 and at32ap) have_intensity_bit - SOC uses IBGR:555 rather than BGR:565 16-bit pixel layout (at91sam9261, at91sam9263 and at91sam9rl) This allows us to remove all the remaining uses of cpu_is macros from the driver. Tested on at91sam9263 and at91sam9g45, compile-tested for other AT91-SOCs, and untested for AVR32. Signed-off-by: Johan Hovold Signed-off-by: Nicolas Ferre --- drivers/video/atmel_lcdfb.c | 89 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 2effd35da589..c1a2914447e1 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -34,6 +34,77 @@ #define ATMEL_LCDC_DMA_BURST_LEN 8 /* words */ #define ATMEL_LCDC_FIFO_SIZE 512 /* words */ +struct atmel_lcdfb_config { + bool have_alt_pixclock; + bool have_hozval; + bool have_intensity_bit; +}; + +static struct atmel_lcdfb_config at91sam9261_config = { + .have_hozval = true, + .have_intensity_bit = true, +}; + +static struct atmel_lcdfb_config at91sam9263_config = { + .have_intensity_bit = true, +}; + +static struct atmel_lcdfb_config at91sam9g10_config = { + .have_hozval = true, +}; + +static struct atmel_lcdfb_config at91sam9g45_config = { + .have_alt_pixclock = true, +}; + +static struct atmel_lcdfb_config at91sam9g45es_config = { +}; + +static struct atmel_lcdfb_config at91sam9rl_config = { + .have_intensity_bit = true, +}; + +static struct atmel_lcdfb_config at32ap_config = { + .have_hozval = true, +}; + +static const struct platform_device_id atmel_lcdfb_devtypes[] = { + { + .name = "at91sam9261-lcdfb", + .driver_data = (unsigned long)&at91sam9261_config, + }, { + .name = "at91sam9263-lcdfb", + .driver_data = (unsigned long)&at91sam9263_config, + }, { + .name = "at91sam9g10-lcdfb", + .driver_data = (unsigned long)&at91sam9g10_config, + }, { + .name = "at91sam9g45-lcdfb", + .driver_data = (unsigned long)&at91sam9g45_config, + }, { + .name = "at91sam9g45es-lcdfb", + .driver_data = (unsigned long)&at91sam9g45es_config, + }, { + .name = "at91sam9rl-lcdfb", + .driver_data = (unsigned long)&at91sam9rl_config, + }, { + .name = "at32ap-lcdfb", + .driver_data = (unsigned long)&at32ap_config, + }, { + /* terminator */ + } +}; + +static struct atmel_lcdfb_config * +atmel_lcdfb_get_config(struct platform_device *pdev) +{ + unsigned long data; + + data = platform_get_device_id(pdev)->driver_data; + + return (struct atmel_lcdfb_config *)data; +} + #if defined(CONFIG_ARCH_AT91) #define ATMEL_LCDFB_FBINFO_DEFAULT (FBINFO_DEFAULT \ | FBINFO_PARTIAL_PAN_OK \ @@ -199,8 +270,7 @@ static unsigned long compute_hozval(struct atmel_lcdfb_info *sinfo, unsigned long lcdcon2; unsigned long value; - if (!(cpu_is_at91sam9261() || cpu_is_at91sam9g10() - || cpu_is_at32ap7000())) + if (!sinfo->config->have_hozval) return xres; lcdcon2 = lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2); @@ -426,7 +496,7 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, break; case 16: /* Older SOCs use IBGR:555 rather than BGR:565. */ - if (sinfo->have_intensity_bit) + if (sinfo->config->have_intensity_bit) var->green.length = 5; else var->green.length = 6; @@ -534,7 +604,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info) /* Now, the LCDC core... */ /* Set pixel clock */ - if (cpu_is_at91sam9g45() && !cpu_is_at91sam9g45es()) + if (sinfo->config->have_alt_pixclock) pix_factor = 1; clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; @@ -686,7 +756,7 @@ static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, case FB_VISUAL_PSEUDOCOLOR: if (regno < 256) { - if (sinfo->have_intensity_bit) { + if (sinfo->config->have_intensity_bi