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 b3643ff..db098ce 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.1 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 db098ce..58aa28f 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.1 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 538b9dd..8738933 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 f206df1..3d5cf63 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 e2d4e67..ee964aa 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.1 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 b3643ff..49b4292 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.1 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 e507ab7..e8c4532 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 4d8283a..1c1b15d 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 0000000..545c989 --- /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.1 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 488c14c..4b8d296 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.1 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 4b8d296..2e49684 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.1 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 2e49684..94fd3ab 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.1 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 94fd3ab..a2f8023a 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.1 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 a2f8023a..eefacc3 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.1 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 eefacc3..da904d7 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.1 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 da904d7..7108963 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.1 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 7108963..b72b724 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.1 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 b72b724..08d0c41 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.1 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 b4502edc..78b8dae 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.1 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 e6a553c..4329a29 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.1 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 83943e2..4aac9ee 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.1 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 025428e..c5883ca 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.1 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 c5883ca..2effd35 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.1 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 2effd35..c1a2914 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_bit) { /* old style I+BGR:555 */ val = ((red >> 11) & 0x001f); val |= ((green >> 6) & 0x03e0); @@ -874,10 +944,9 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) } sinfo->info = info; sinfo->pdev = pdev; - if (cpu_is_at91sam9261() || cpu_is_at91sam9263() || - cpu_is_at91sam9rl()) { - sinfo->have_intensity_bit = true; - } + sinfo->config = atmel_lcdfb_get_config(pdev); + if (!sinfo->config) + goto free_info; strcpy(info->fix.id, sinfo->pdev->name); info->flags = ATMEL_LCDFB_FBINFO_DEFAULT; @@ -1146,7 +1215,7 @@ static struct platform_driver atmel_lcdfb_driver = { .remove = __exit_p(atmel_lcdfb_remove), .suspend = atmel_lcdfb_suspend, .resume = atmel_lcdfb_resume, - + .id_table = atmel_lcdfb_devtypes, .driver = { .name = "atmel_lcdfb", .owner = THIS_MODULE, -- cgit v1.1 From 443580486e3b96578928c1c91e8fbdcf0c9c9c7f Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Mon, 18 Feb 2013 23:28:34 +0900 Subject: irqchip: Renesas INTC External IRQ pin driver This patch adds a driver for external IRQ pins connected to the INTC block on recent SoCs from Renesas. The INTC hardware block usually contains a rather wide range of features ranging from external IRQ pin handling to legacy interrupt controller support. On older SoCs the INTC is used as a general purpose interrupt controller both for external IRQ pins and on-chip devices. On more recent ARM based SoCs with Cortex-A9 the main interrupt controller is the GIC, but IRQ trigger setup still need to happen in the INTC hardware block. This driver implements the glue code needed to configure IRQ trigger and also handle mask/unmask and demux of external IRQ pins hooked up from the INTC to the GIC. Tested on sh73a0 and r8a7779. The hardware varies quite a bit with SoC model, for instance register width and bitfield widths vary wildly. The driver requires one GIC SPI per external IRQ pin to operate. Each driver instance will handle up to 8 external IRQ pins. The SoCs using this driver are currently mainly used together with regular platform devices so this driver allows configuration via platform data to support things like static interrupt base address. DT support will be added incrementally in the not so distant future. Signed-off-by: Magnus Damm Acked-by: Thomas Gleixner Signed-off-by: Simon Horman --- drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-renesas-intc-irqpin.c | 464 ++++++++++++++++++++++++++++++ 3 files changed, 469 insertions(+) create mode 100644 drivers/irqchip/irq-renesas-intc-irqpin.c (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a350969..0f5f1c3 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -25,6 +25,10 @@ config ARM_VIC_NR The maximum number of VICs available in the system, for power management. +config RENESAS_INTC_IRQPIN + bool + select IRQ_DOMAIN + config VERSATILE_FPGA_IRQ bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 98e3b87..1aaa407 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o +obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c new file mode 100644 index 0000000..1e5058a --- /dev/null +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -0,0 +1,464 @@ +/* + * Renesas INTC External IRQ Pin Driver + * + * Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ + +#define INTC_IRQPIN_REG_SENSE 0 /* ICRn */ +#define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */ +#define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */ +#define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */ +#define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */ +#define INTC_IRQPIN_REG_NR 5 + +/* INTC external IRQ PIN hardware register access: + * + * SENSE is read-write 32-bit with 2-bits or 4-bits per IRQ (*) + * PRIO is read-write 32-bit with 4-bits per IRQ (**) + * SOURCE is read-only 32-bit or 8-bit with 1-bit per IRQ (***) + * MASK is write-only 32-bit or 8-bit with 1-bit per IRQ (***) + * CLEAR is write-only 32-bit or 8-bit with 1-bit per IRQ (***) + * + * (*) May be accessed by more than one driver instance - lock needed + * (**) Read-modify-write access by one driver instance - lock needed + * (***) Accessed by one driver instance only - no locking needed + */ + +struct intc_irqpin_iomem { + void __iomem *iomem; + unsigned long (*read)(void __iomem *iomem); + void (*write)(void __iomem *iomem, unsigned long data); + int width; +}; + +struct intc_irqpin_irq { + int hw_irq; + int irq; + struct intc_irqpin_priv *p; +}; + +struct intc_irqpin_priv { + struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; + struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; + struct renesas_intc_irqpin_config config; + unsigned int number_of_irqs; + struct platform_device *pdev; + struct irq_chip irq_chip; + struct irq_domain *irq_domain; +}; + +static unsigned long intc_irqpin_read32(void __iomem *iomem) +{ + return ioread32(iomem); +} + +static unsigned long intc_irqpin_read8(void __iomem *iomem) +{ + return ioread8(iomem); +} + +static void intc_irqpin_write32(void __iomem *iomem, unsigned long data) +{ + iowrite32(data, iomem); +} + +static void intc_irqpin_write8(void __iomem *iomem, unsigned long data) +{ + iowrite8(data, iomem); +} + +static inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, + int reg) +{ + struct intc_irqpin_iomem *i = &p->iomem[reg]; + return i->read(i->iomem); +} + +static inline void intc_irqpin_write(struct intc_irqpin_priv *p, + int reg, unsigned long data) +{ + struct intc_irqpin_iomem *i = &p->iomem[reg]; + i->write(i->iomem, data); +} + +static inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p, + int reg, int hw_irq) +{ + return BIT((p->iomem[reg].width - 1) - hw_irq); +} + +static inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p, + int reg, int hw_irq) +{ + intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq)); +} + +static DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ + +static void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p, + int reg, int shift, + int width, int value) +{ + unsigned long flags; + unsigned long tmp; + + raw_spin_lock_irqsave(&intc_irqpin_lock, flags); + + tmp = intc_irqpin_read(p, reg); + tmp &= ~(((1 << width) - 1) << shift); + tmp |= value << shift; + intc_irqpin_write(p, reg, tmp); + + raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags); +} + +static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, + int irq, int do_mask) +{ + int bitfield_width = 4; /* PRIO assumed to have fixed bitfield width */ + int shift = (7 - irq) * bitfield_width; /* PRIO assumed to be 32-bit */ + + intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO, + shift, bitfield_width, + do_mask ? 0 : (1 << bitfield_width) - 1); +} + +static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) +{ + int bitfield_width = p->config.sense_bitfield_width; + int shift = (7 - irq) * bitfield_width; /* SENSE assumed to be 32-bit */ + + dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); + + if (value >= (1 << bitfield_width)) + return -EINVAL; + + intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift, + bitfield_width, value); + return 0; +} + +static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) +{ + dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", + str, i->irq, i->hw_irq, + irq_find_mapping(i->p->irq_domain, i->hw_irq)); +} + +static void intc_irqpin_irq_enable(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + intc_irqpin_dbg(&p->irq[hw_irq], "enable"); + intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); +} + +static void intc_irqpin_irq_disable(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + intc_irqpin_dbg(&p->irq[hw_irq], "disable"); + intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); +} + +static void intc_irqpin_irq_enable_force(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int irq = p->irq[irqd_to_hwirq(d)].irq; + + intc_irqpin_irq_enable(d); + irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); +} + +static void intc_irqpin_irq_disable_force(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int irq = p->irq[irqd_to_hwirq(d)].irq; + + irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); + intc_irqpin_irq_disable(d); +} + +#define INTC_IRQ_SENSE_VALID 0x10 +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) + +static unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = { + [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00), + [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01), + [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02), + [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03), + [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04), +}; + +static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) +{ + unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK]; + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + + if (!(value & INTC_IRQ_SENSE_VALID)) + return -EINVAL; + + return intc_irqpin_set_sense(p, irqd_to_hwirq(d), + value ^ INTC_IRQ_SENSE_VALID); +} + +static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) +{ + struct intc_irqpin_irq *i = dev_id; + struct intc_irqpin_priv *p = i->p; + unsigned long bit; + + intc_irqpin_dbg(i, "demux1"); + bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq); + + if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { + intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); + intc_irqpin_dbg(i, "demux2"); + generic_handle_irq(irq_find_mapping(p->irq_domain, i->hw_irq)); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct intc_irqpin_priv *p = h->host_data; + + intc_irqpin_dbg(&p->irq[hw], "map"); + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); + set_irq_flags(virq, IRQF_VALID); /* kill me now */ + return 0; +} + +static struct irq_domain_ops intc_irqpin_irq_domain_ops = { + .map = intc_irqpin_irq_domain_map, +}; + +static int intc_irqpin_probe(struct platform_device *pdev) +{ + struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data; + struct intc_irqpin_priv *p; + struct intc_irqpin_iomem *i; + struct resource *io[INTC_IRQPIN_REG_NR]; + struct resource *irq; + struct irq_chip *irq_chip; + void (*enable_fn)(struct irq_data *d); + void (*disable_fn)(struct irq_data *d); + const char *name = dev_name(&pdev->dev); + int ret; + int k; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + ret = -ENOMEM; + goto err0; + } + + /* deal with driver instance configuration */ + if (pdata) + memcpy(&p->config, pdata, sizeof(*pdata)); + if (!p->config.sense_bitfield_width) + p->config.sense_bitfield_width = 4; /* default to 4 bits */ + + p->pdev = pdev; + platform_set_drvdata(pdev, p); + + /* get hold of manadatory IOMEM */ + for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { + io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); + if (!io[k]) { + dev_err(&pdev->dev, "not enough IOMEM resources\n"); + ret = -EINVAL; + goto err1; + } + } + + /* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */ + for (k = 0; k < INTC_IRQPIN_MAX; k++) { + irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); + if (!irq) + break; + + p->irq[k].hw_irq = k; + p->irq[k].p = p; + p->irq[k].irq = irq->start; + } + + p->number_of_irqs = k; + if (p->number_of_irqs < 1) { + dev_err(&pdev->dev, "not enough IRQ resources\n"); + ret = -EINVAL; + goto err1; + } + + /* ioremap IOMEM and setup read/write callbacks */ + for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { + i = &p->iomem[k]; + + switch (resource_size(io[k])) { + case 1: + i->width = 8; + i->read = intc_irqpin_read8; + i->write = intc_irqpin_write8; + break; + case 4: + i->width = 32; + i->read = intc_irqpin_read32; + i->write = intc_irqpin_write32; + break; + default: + dev_err(&pdev->dev, "IOMEM size mismatch\n"); + ret = -EINVAL; + goto err2; + } + + i->iomem = ioremap_nocache(io[k]->start, resource_size(io[k])); + if (!i->iomem) { + dev_err(&pdev->dev, "failed to remap IOMEM\n"); + ret = -ENXIO; + goto err2; + } + } + + /* mask all interrupts using priority */ + for (k = 0; k < p->number_of_irqs; k++) + intc_irqpin_mask_unmask_prio(p, k, 1); + + /* use more severe masking method if requested */ + if (p->config.control_parent) { + enable_fn = intc_irqpin_irq_enable_force; + disable_fn = intc_irqpin_irq_disable_force; + } else { + enable_fn = intc_irqpin_irq_enable; + disable_fn = intc_irqpin_irq_disable; + } + + irq_chip = &p->irq_chip; + irq_chip->name = name; + irq_chip->irq_mask = disable_fn; + irq_chip->irq_unmask = enable_fn; + irq_chip->irq_enable = enable_fn; + irq_chip->irq_disable = disable_fn; + irq_chip->irq_set_type = intc_irqpin_irq_set_type; + irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; + + p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, + p->number_of_irqs, + p->config.irq_base, + &intc_irqpin_irq_domain_ops, p); + if (!p->irq_domain) { + ret = -ENXIO; + dev_err(&pdev->dev, "cannot initialize irq domain\n"); + goto err2; + } + + /* request and set priority on interrupts one by one */ + for (k = 0; k < p->number_of_irqs; k++) { + if (request_irq(p->irq[k].irq, intc_irqpin_irq_handler, + 0, name, &p->irq[k])) { + dev_err(&pdev->dev, "failed to request low IRQ\n"); + ret = -ENOENT; + goto err3; + } + intc_irqpin_mask_unmask_prio(p, k, 0); + } + + dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); + + /* warn in case of mismatch if irq base is specified */ + if (p->config.irq_base) { + k = irq_find_mapping(p->irq_domain, 0); + if (p->config.irq_base != k) + dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", + p->config.irq_base, k); + } + + return 0; + +err3: + for (; k >= 0; k--) + free_irq(p->irq[k - 1].irq, &p->irq[k - 1]); + + irq_domain_remove(p->irq_domain); +err2: + for (k = 0; k < INTC_IRQPIN_REG_NR; k++) + iounmap(p->iomem[k].iomem); +err1: + kfree(p); +err0: + return ret; +} + +static int intc_irqpin_remove(struct platform_device *pdev) +{ + struct intc_irqpin_priv *p = platform_get_drvdata(pdev); + int k; + + for (k = 0; k < p->number_of_irqs; k++) + free_irq(p->irq[k].irq, &p->irq[k]); + + irq_domain_remove(p->irq_domain); + + for (k = 0; k < INTC_IRQPIN_REG_NR; k++) + iounmap(p->iomem[k].iomem); + + kfree(p); + return 0; +} + +static struct platform_driver intc_irqpin_device_driver = { + .probe = intc_irqpin_probe, + .remove = intc_irqpin_remove, + .driver = { + .name = "renesas_intc_irqpin", + } +}; + +static int __init intc_irqpin_init(void) +{ + return platform_driver_register(&intc_irqpin_device_driver); +} +postcore_initcall(intc_irqpin_init); + +static void __exit intc_irqpin_exit(void) +{ + platform_driver_unregister(&intc_irqpin_device_driver); +} +module_exit(intc_irqpin_exit); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 341eb5465f67437ad37ef2f6302b581beda4614a Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 26 Feb 2013 12:01:09 +0900 Subject: ARM: shmobile: INTC External IRQ pin driver on sh73a0 Adjust the sh73a0 IRQ code to make use of the INTC External IRQ pin driver for external interrupt pins IRQ0 -> IRQ31. This removes quite a bit of special-case code in intc-sh73a0.c but the number of lines get replaced with platform device information in setup-sh73a0.c. The PFC code is also adjusted to make gpio_to_irq() return the correct interrupt number. At this point the DT reference implementations are not covered. In the future such code shall tie in the INTC External IRQ pin driver via DT, so this kind of verbose code is not needed for the long term DT case. Signed-off-by: Magnus Damm Signed-off-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-sh73a0.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c index 709008e..6f15c03 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c @@ -2733,9 +2733,9 @@ static struct pinmux_data_reg pinmux_data_regs[] = { { }, }; -/* IRQ pins through INTCS with IRQ0->15 from 0x200 and IRQ16-31 from 0x3200 */ -#define EXT_IRQ16L(n) intcs_evt2irq(0x200 + ((n) << 5)) -#define EXT_IRQ16H(n) intcs_evt2irq(0x3200 + ((n - 16) << 5)) +/* External IRQ pins mapped at IRQPIN_BASE */ +#define EXT_IRQ16L(n) irq_pin(n) +#define EXT_IRQ16H(n) irq_pin(n) static struct pinmux_irq pinmux_irqs[] = { PINMUX_IRQ(EXT_IRQ16H(19), PORT9_FN0), -- cgit v1.1 From 862d309883c69d67e1a2095e6f9e8ef35bf72dd6 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 26 Feb 2013 20:58:44 +0900 Subject: irqchip: intc-irqpin: Whitespace fixes Remove whitespace damage and add newline between variables and code. Signed-off-by: Magnus Damm Reviewed-by: Thomas Gleixner Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/irqchip/irq-renesas-intc-irqpin.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 1e5058a..4b5933f 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -57,13 +57,13 @@ struct intc_irqpin_iomem { unsigned long (*read)(void __iomem *iomem); void (*write)(void __iomem *iomem, unsigned long data); int width; -}; +}; struct intc_irqpin_irq { int hw_irq; int irq; struct intc_irqpin_priv *p; -}; +}; struct intc_irqpin_priv { struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; @@ -99,6 +99,7 @@ static inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, int reg) { struct intc_irqpin_iomem *i = &p->iomem[reg]; + return i->read(i->iomem); } @@ -106,6 +107,7 @@ static inline void intc_irqpin_write(struct intc_irqpin_priv *p, int reg, unsigned long data) { struct intc_irqpin_iomem *i = &p->iomem[reg]; + i->write(i->iomem, data); } @@ -405,7 +407,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", p->config.irq_base, k); } - + return 0; err3: -- cgit v1.1 From 33f958f2a71c44164698d1cae5463c0b85296a2c Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 26 Feb 2013 20:58:54 +0900 Subject: irqchip: intc-irqpin: Cache mapped IRQ Cache IRQ in domain_irq variable instead of making use of irq_find_mapping(). While at it rename the irq variable to requested_irq. Signed-off-by: Magnus Damm Reviewed-by: Thomas Gleixner Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/irqchip/irq-renesas-intc-irqpin.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 4b5933f..0ac2bf6 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -61,7 +61,8 @@ struct intc_irqpin_iomem { struct intc_irqpin_irq { int hw_irq; - int irq; + int requested_irq; + int domain_irq; struct intc_irqpin_priv *p; }; @@ -171,8 +172,7 @@ static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) { dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", - str, i->irq, i->hw_irq, - irq_find_mapping(i->p->irq_domain, i->hw_irq)); + str, i->requested_irq, i->hw_irq, i->domain_irq); } static void intc_irqpin_irq_enable(struct irq_data *d) @@ -196,7 +196,7 @@ static void intc_irqpin_irq_disable(struct irq_data *d) static void intc_irqpin_irq_enable_force(struct irq_data *d) { struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); - int irq = p->irq[irqd_to_hwirq(d)].irq; + int irq = p->irq[irqd_to_hwirq(d)].requested_irq; intc_irqpin_irq_enable(d); irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); @@ -205,7 +205,7 @@ static void intc_irqpin_irq_enable_force(struct irq_data *d) static void intc_irqpin_irq_disable_force(struct irq_data *d) { struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); - int irq = p->irq[irqd_to_hwirq(d)].irq; + int irq = p->irq[irqd_to_hwirq(d)].requested_irq; irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); intc_irqpin_irq_disable(d); @@ -246,7 +246,7 @@ static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); intc_irqpin_dbg(i, "demux2"); - generic_handle_irq(irq_find_mapping(p->irq_domain, i->hw_irq)); + generic_handle_irq(i->domain_irq); return IRQ_HANDLED; } return IRQ_NONE; @@ -257,6 +257,9 @@ static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, { struct intc_irqpin_priv *p = h->host_data; + p->irq[hw].domain_irq = virq; + p->irq[hw].hw_irq = hw; + intc_irqpin_dbg(&p->irq[hw], "map"); irq_set_chip_data(virq, h->host_data); irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); @@ -314,9 +317,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) if (!irq) break; - p->irq[k].hw_irq = k; p->irq[k].p = p; - p->irq[k].irq = irq->start; + p->irq[k].requested_irq = irq->start; } p->number_of_irqs = k; @@ -389,7 +391,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) /* request and set priority on interrupts one by one */ for (k = 0; k < p->number_of_irqs; k++) { - if (request_irq(p->irq[k].irq, intc_irqpin_irq_handler, + if (request_irq(p->irq[k].requested_irq, + intc_irqpin_irq_handler, 0, name, &p->irq[k])) { dev_err(&pdev->dev, "failed to request low IRQ\n"); ret = -ENOENT; @@ -402,17 +405,16 @@ static int intc_irqpin_probe(struct platform_device *pdev) /* warn in case of mismatch if irq base is specified */ if (p->config.irq_base) { - k = irq_find_mapping(p->irq_domain, 0); - if (p->config.irq_base != k) + if (p->config.irq_base != p->irq[0].domain_irq) dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", - p->config.irq_base, k); + p->config.irq_base, p->irq[0].domain_irq); } return 0; err3: for (; k >= 0; k--) - free_irq(p->irq[k - 1].irq, &p->irq[k - 1]); + free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); irq_domain_remove(p->irq_domain); err2: @@ -430,7 +432,7 @@ static int intc_irqpin_remove(struct platform_device *pdev) int k; for (k = 0; k < p->number_of_irqs; k++) - free_irq(p->irq[k].irq, &p->irq[k]); + free_irq(p->irq[k].requested_irq, &p->irq[k]); irq_domain_remove(p->irq_domain); -- cgit v1.1 From d1b6aecde4ab146d115abcaf3bb1940d8e980b5a Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 26 Feb 2013 20:59:04 +0900 Subject: irqchip: intc-irqpin: Add force comments Add comments to describe the special case for "force" versions of enable and disable functions. Signed-off-by: Magnus Damm Reviewed-by: Thomas Gleixner Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/irqchip/irq-renesas-intc-irqpin.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 0ac2bf6..59c0cbc 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -199,6 +199,11 @@ static void intc_irqpin_irq_enable_force(struct irq_data *d) int irq = p->irq[irqd_to_hwirq(d)].requested_irq; intc_irqpin_irq_enable(d); + + /* enable interrupt through parent interrupt controller, + * assumes non-shared interrupt with 1:1 mapping + * needed for busted IRQs on some SoCs like sh73a0 + */ irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); } @@ -207,6 +212,10 @@ static void intc_irqpin_irq_disable_force(struct irq_data *d) struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); int irq = p->irq[irqd_to_hwirq(d)].requested_irq; + /* disable interrupt through parent interrupt controller, + * assumes non-shared interrupt with 1:1 mapping + * needed for busted IRQs on some SoCs like sh73a0 + */ irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); intc_irqpin_irq_disable(d); } -- cgit v1.1 From 08eba5ba4f321c4b1806ecad0e626904f89263a1 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 26 Feb 2013 20:59:13 +0900 Subject: irqchip: intc-irqpin: Make use of devm functions Use devm_kzalloc(), devm_ioremap_nocache() and devm_request_irq() to simplify error handling. Signed-off-by: Magnus Damm Reviewed-by: Thomas Gleixner Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/irqchip/irq-renesas-intc-irqpin.c | 41 ++++++++++--------------------- 1 file changed, 13 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 59c0cbc..21f4602 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -294,7 +294,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) int ret; int k; - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); if (!p) { dev_err(&pdev->dev, "failed to allocate driver data\n"); ret = -ENOMEM; @@ -316,7 +316,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) if (!io[k]) { dev_err(&pdev->dev, "not enough IOMEM resources\n"); ret = -EINVAL; - goto err1; + goto err0; } } @@ -334,7 +334,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) if (p->number_of_irqs < 1) { dev_err(&pdev->dev, "not enough IRQ resources\n"); ret = -EINVAL; - goto err1; + goto err0; } /* ioremap IOMEM and setup read/write callbacks */ @@ -355,14 +355,15 @@ static int intc_irqpin_probe(struct platform_device *pdev) default: dev_err(&pdev->dev, "IOMEM size mismatch\n"); ret = -EINVAL; - goto err2; + goto err0; } - i->iomem = ioremap_nocache(io[k]->start, resource_size(io[k])); + i->iomem = devm_ioremap_nocache(&pdev->dev, io[k]->start, + resource_size(io[k])); if (!i->iomem) { dev_err(&pdev->dev, "failed to remap IOMEM\n"); ret = -ENXIO; - goto err2; + goto err0; } } @@ -395,17 +396,17 @@ static int intc_irqpin_probe(struct platform_device *pdev) if (!p->irq_domain) { ret = -ENXIO; dev_err(&pdev->dev, "cannot initialize irq domain\n"); - goto err2; + goto err0; } /* request and set priority on interrupts one by one */ for (k = 0; k < p->number_of_irqs; k++) { - if (request_irq(p->irq[k].requested_irq, - intc_irqpin_irq_handler, - 0, name, &p->irq[k])) { + if (devm_request_irq(&pdev->dev, p->irq[k].requested_irq, + intc_irqpin_irq_handler, + 0, name, &p->irq[k])) { dev_err(&pdev->dev, "failed to request low IRQ\n"); ret = -ENOENT; - goto err3; + goto err1; } intc_irqpin_mask_unmask_prio(p, k, 0); } @@ -421,16 +422,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) return 0; -err3: - for (; k >= 0; k--) - free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); - - irq_domain_remove(p->irq_domain); -err2: - for (k = 0; k < INTC_IRQPIN_REG_NR; k++) - iounmap(p->iomem[k].iomem); err1: - kfree(p); + irq_domain_remove(p->irq_domain); err0: return ret; } @@ -438,17 +431,9 @@ err0: static int intc_irqpin_remove(struct platform_device *pdev) { struct intc_irqpin_priv *p = platform_get_drvdata(pdev); - int k; - - for (k = 0; k < p->number_of_irqs; k++) - free_irq(p->irq[k].requested_irq, &p->irq[k]); irq_domain_remove(p->irq_domain); - for (k = 0; k < INTC_IRQPIN_REG_NR; k++) - iounmap(p->iomem[k].iomem); - - kfree(p); return 0; } -- cgit v1.1 From fbc83b7f59dd8ed1154286b6de00b6d03c24a3c4 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 27 Feb 2013 17:15:01 +0900 Subject: irqchip: Renesas IRQC driver This patch adds a driver for external IRQ pins connected to the IRQC hardware block on recent SoCs from Renesas. The IRQC hardware block is used together with more recent ARM based SoCs using the GIC. As usual the GIC requires external IRQ trigger setup somewhere else which in this particular case happens to be IRQC. This driver implements the glue code needed to configure IRQ trigger and also handle mask/unmask and demux of external IRQ pins hooked up from the IRQC to the GIC. Tested on r8a73a4 but is designed to work with a wide range of SoCs. The driver requires one GIC SPI per external IRQ pin to operate. Each driver instance will handle up to 32 external IRQ pins. The SoCs using this driver are currently mainly used together with regular platform devices so this driver allows configuration via platform data to support things like static interrupt base address. DT support will be added incrementally in the not so distant future. Signed-off-by: Magnus Damm Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-renesas-irqc.c | 298 +++++++++++++++++++++++++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 drivers/irqchip/irq-renesas-irqc.c (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 0f5f1c3..4a33351 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -29,6 +29,10 @@ config RENESAS_INTC_IRQPIN bool select IRQ_DOMAIN +config RENESAS_IRQC + bool + select IRQ_DOMAIN + config VERSATILE_FPGA_IRQ bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 1aaa407..e41ceb9 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o +obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c new file mode 100644 index 0000000..95d69bf --- /dev/null +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -0,0 +1,298 @@ +/* + * Renesas IRQC Driver + * + * Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ + +#define IRQC_REQ_STS 0x00 +#define IRQC_EN_STS 0x04 +#define IRQC_EN_SET 0x08 +#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) +#define DETECT_STATUS 0x100 +#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) + +struct irqc_irq { + int hw_irq; + int requested_irq; + int domain_irq; + struct irqc_priv *p; +}; + +struct irqc_priv { + void __iomem *iomem; + void __iomem *cpu_int_base; + struct irqc_irq irq[IRQC_IRQ_MAX]; + struct renesas_irqc_config config; + unsigned int number_of_irqs; + struct platform_device *pdev; + struct irq_chip irq_chip; + struct irq_domain *irq_domain; +}; + +static void irqc_dbg(struct irqc_irq *i, char *str) +{ + dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", + str, i->requested_irq, i->hw_irq, i->domain_irq); +} + +static void irqc_irq_enable(struct irq_data *d) +{ + struct irqc_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + irqc_dbg(&p->irq[hw_irq], "enable"); + iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET); +} + +static void irqc_irq_disable(struct irq_data *d) +{ + struct irqc_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + irqc_dbg(&p->irq[hw_irq], "disable"); + iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS); +} + +#define INTC_IRQ_SENSE_VALID 0x10 +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) + +static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { + [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01), + [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02), + [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */ + [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */ + [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c), /* Synchronous */ +}; + +static int irqc_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct irqc_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; + unsigned long tmp; + + irqc_dbg(&p->irq[hw_irq], "sense"); + + if (!(value & INTC_IRQ_SENSE_VALID)) + return -EINVAL; + + tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq)); + tmp &= ~0x3f; + tmp |= value ^ INTC_IRQ_SENSE_VALID; + iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq)); + return 0; +} + +static irqreturn_t irqc_irq_handler(int irq, void *dev_id) +{ + struct irqc_irq *i = dev_id; + struct irqc_priv *p = i->p; + unsigned long bit = BIT(i->hw_irq); + + irqc_dbg(i, "demux1"); + + if (ioread32(p->iomem + DETECT_STATUS) & bit) { + iowrite32(bit, p->iomem + DETECT_STATUS); + irqc_dbg(i, "demux2"); + generic_handle_irq(i->domain_irq); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct irqc_priv *p = h->host_data; + + p->irq[hw].domain_irq = virq; + p->irq[hw].hw_irq = hw; + + irqc_dbg(&p->irq[hw], "map"); + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); + set_irq_flags(virq, IRQF_VALID); /* kill me now */ + return 0; +} + +static struct irq_domain_ops irqc_irq_domain_ops = { + .map = irqc_irq_domain_map, +}; + +static int irqc_probe(struct platform_device *pdev) +{ + struct renesas_irqc_config *pdata = pdev->dev.platform_data; + struct irqc_priv *p; + struct resource *io; + struct resource *irq; + struct irq_chip *irq_chip; + const char *name = dev_name(&pdev->dev); + int ret; + int k; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + ret = -ENOMEM; + goto err0; + } + + /* deal with driver instance configuration */ + if (pdata) + memcpy(&p->config, pdata, sizeof(*pdata)); + + p->pdev = pdev; + platform_set_drvdata(pdev, p); + + /* get hold of manadatory IOMEM */ + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!io) { + dev_err(&pdev->dev, "not enough IOMEM resources\n"); + ret = -EINVAL; + goto err1; + } + + /* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ + for (k = 0; k < IRQC_IRQ_MAX; k++) { + irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); + if (!irq) + break; + + p->irq[k].p = p; + p->irq[k].requested_irq = irq->start; + } + + p->number_of_irqs = k; + if (p->number_of_irqs < 1) { + dev_err(&pdev->dev, "not enough IRQ resources\n"); + ret = -EINVAL; + goto err1; + } + + /* ioremap IOMEM and setup read/write callbacks */ + p->iomem = ioremap_nocache(io->start, resource_size(io)); + if (!p->iomem) { + dev_err(&pdev->dev, "failed to remap IOMEM\n"); + ret = -ENXIO; + goto err2; + } + + p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ + + irq_chip = &p->irq_chip; + irq_chip->name = name; + irq_chip->irq_mask = irqc_irq_disable; + irq_chip->irq_unmask = irqc_irq_enable; + irq_chip->irq_enable = irqc_irq_enable; + irq_chip->irq_disable = irqc_irq_disable; + irq_chip->irq_set_type = irqc_irq_set_type; + irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; + + p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, + p->number_of_irqs, + p->config.irq_base, + &irqc_irq_domain_ops, p); + if (!p->irq_domain) { + ret = -ENXIO; + dev_err(&pdev->dev, "cannot initialize irq domain\n"); + goto err2; + } + + /* request interrupts one by one */ + for (k = 0; k < p->number_of_irqs; k++) { + if (request_irq(p->irq[k].requested_irq, irqc_irq_handler, + 0, name, &p->irq[k])) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + ret = -ENOENT; + goto err3; + } + } + + dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); + + /* warn in case of mismatch if irq base is specified */ + if (p->config.irq_base) { + if (p->config.irq_base != p->irq[0].domain_irq) + dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", + p->config.irq_base, p->irq[0].domain_irq); + } + + return 0; +err3: + for (; k >= 0; k--) + free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); + + irq_domain_remove(p->irq_domain); +err2: + iounmap(p->iomem); +err1: + kfree(p); +err0: + return ret; +} + +static int irqc_remove(struct platform_device *pdev) +{ + struct irqc_priv *p = platform_get_drvdata(pdev); + int k; + + for (k = 0; k < p->number_of_irqs; k++) + free_irq(p->irq[k].requested_irq, &p->irq[k]); + + irq_domain_remove(p->irq_domain); + iounmap(p->iomem); + kfree(p); + return 0; +} + +static struct platform_driver irqc_device_driver = { + .probe = irqc_probe, + .remove = irqc_remove, + .driver = { + .name = "renesas_irqc", + } +}; + +static int __init irqc_init(void) +{ + return platform_driver_register(&irqc_device_driver); +} +postcore_initcall(irqc_init); + +static void __exit irqc_exit(void) +{ + platform_driver_unregister(&irqc_device_driver); +} +module_exit(irqc_exit); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas IRQC Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 9d833bbe49953a9a07f9ebd7a9ad170c308bd692 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 6 Mar 2013 15:16:08 +0900 Subject: irqchip: intc-irqpin: Initial DT support Add initial DT support to the INTC External IRQ Pin driver. At this point only hardware with 4-bit wide sense registers is supported via DT. Signed-off-by: Magnus Damm Signed-off-by: Simon Horman --- drivers/irqchip/irq-renesas-intc-irqpin.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 21f4602..fd5dabc 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -278,6 +278,7 @@ static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, static struct irq_domain_ops intc_irqpin_irq_domain_ops = { .map = intc_irqpin_irq_domain_map, + .xlate = irq_domain_xlate_twocell, }; static int intc_irqpin_probe(struct platform_device *pdev) @@ -437,11 +438,19 @@ static int intc_irqpin_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id intc_irqpin_dt_ids[] = { + { .compatible = "renesas,intc-irqpin", }, + {}, +}; +MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); + static struct platform_driver intc_irqpin_device_driver = { .probe = intc_irqpin_probe, .remove = intc_irqpin_remove, .driver = { .name = "renesas_intc_irqpin", + .of_match_table = intc_irqpin_dt_ids, + .owner = THIS_MODULE, } }; -- cgit v1.1 From 3b8dfa7c2f8af7613dae28ac0f3419bf75ead5d0 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 6 Mar 2013 15:23:39 +0900 Subject: irqchip: irqc: Add DT support Add DT support to the IRQC External IRQ Pin driver. Signed-off-by: Magnus Damm Signed-off-by: Simon Horman --- drivers/irqchip/irq-renesas-irqc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index 95d69bf..927bff3 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -145,6 +145,7 @@ static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, static struct irq_domain_ops irqc_irq_domain_ops = { .map = irqc_irq_domain_map, + .xlate = irq_domain_xlate_twocell, }; static int irqc_probe(struct platform_device *pdev) @@ -273,11 +274,19 @@ static int irqc_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id irqc_dt_ids[] = { + { .compatible = "renesas,irqc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, irqc_dt_ids); + static struct platform_driver irqc_device_driver = { .probe = irqc_probe, .remove = irqc_remove, .driver = { .name = "renesas_irqc", + .of_match_table = irqc_dt_ids, + .owner = THIS_MODULE, } }; -- cgit v1.1 From 721c42a351b113a9633d2447d9131fe620f2d805 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 9 Mar 2013 17:02:44 +0900 Subject: clk: samsung: add common clock framework helper functions for Samsung platforms All Samsung platforms include different types of clock including fixed-rate, mux, divider and gate clock types. There are typically hundreds of such clocks on each of the Samsung platforms. To enable Samsung platforms to register these clocks using the common clock framework, a bunch of utility functions are introduced here which simplify the clock registration process. The clocks are usually statically instantiated and registered with common clock framework. Reviewed-by: Sylwester Nawrocki Tested-by: Sylwester Nawrocki Reviewed-by: Tomasz Figa Tested-by: Tomasz Figa Tested-by: Heiko Stuebner Signed-off-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/Makefile | 1 + drivers/clk/samsung/Makefile | 5 + drivers/clk/samsung/clk.c | 273 +++++++++++++++++++++++++++++++++++++++++++ drivers/clk/samsung/clk.h | 262 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 541 insertions(+) create mode 100644 drivers/clk/samsung/Makefile create mode 100644 drivers/clk/samsung/clk.c create mode 100644 drivers/clk/samsung/clk.h (limited to 'drivers') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 300d477..0147022 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ obj-$(CONFIG_X86) += x86/ diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile new file mode 100644 index 0000000..bd920b4 --- /dev/null +++ b/drivers/clk/samsung/Makefile @@ -0,0 +1,5 @@ +# +# Samsung Clock specific Makefile +# + +obj-$(CONFIG_COMMON_CLK) += clk.o diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c new file mode 100644 index 0000000..91d12f3 --- /dev/null +++ b/drivers/clk/samsung/clk.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham + * + * 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. + * + * This file includes utility functions to register clocks to common + * clock framework for Samsung platforms. +*/ + +#include +#include "clk.h" + +static DEFINE_SPINLOCK(lock); +static struct clk **clk_table; +static void __iomem *reg_base; +#ifdef CONFIG_OF +static struct clk_onecell_data clk_data; +#endif + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *reg_dump; +static unsigned long nr_reg_dump; + +static int samsung_clk_suspend(void) +{ + struct samsung_clk_reg_dump *rd = reg_dump; + unsigned long i; + + for (i = 0; i < nr_reg_dump; i++, rd++) + rd->value = __raw_readl(reg_base + rd->offset); + + return 0; +} + +static void samsung_clk_resume(void) +{ + struct samsung_clk_reg_dump *rd = reg_dump; + unsigned long i; + + for (i = 0; i < nr_reg_dump; i++, rd++) + __raw_writel(rd->value, reg_base + rd->offset); +} + +static struct syscore_ops samsung_clk_syscore_ops = { + .suspend = samsung_clk_suspend, + .resume = samsung_clk_resume, +}; +#endif /* CONFIG_PM_SLEEP */ + +/* setup the essentials required to support clock lookup using ccf */ +void __init samsung_clk_init(struct device_node *np, void __iomem *base, + unsigned long nr_clks, unsigned long *rdump, + unsigned long nr_rdump) +{ + reg_base = base; + if (!np) + return; + +#ifdef CONFIG_OF + clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); + if (!clk_table) + panic("could not allocate clock lookup table\n"); + + clk_data.clks = clk_table; + clk_data.clk_num = nr_clks; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +#endif + +#ifdef CONFIG_PM_SLEEP + if (rdump && nr_rdump) { + unsigned int idx; + reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump) + * nr_rdump, GFP_KERNEL); + if (!reg_dump) { + pr_err("%s: memory alloc for register dump failed\n", + __func__); + return; + } + + for (idx = 0; idx < nr_rdump; idx++) + reg_dump[idx].offset = rdump[idx]; + nr_reg_dump = nr_rdump; + register_syscore_ops(&samsung_clk_syscore_ops); + } +#endif +} + +/* add a clock instance to the clock lookup table used for dt based lookup */ +void samsung_clk_add_lookup(struct clk *clk, unsigned int id) +{ + if (clk_table && id) + clk_table[id] = clk; +} + +/* register a list of fixed clocks */ +void __init samsung_clk_register_fixed_rate( + struct samsung_fixed_rate_clock *list, unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + for (idx = 0; idx < nr_clk; idx++, list++) { + clk = clk_register_fixed_rate(NULL, list->name, + list->parent_name, list->flags, list->fixed_rate); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + samsung_clk_add_lookup(clk, list->id); + + /* + * Unconditionally add a clock lookup for the fixed rate clocks. + * There are not many of these on any of Samsung platforms. + */ + ret = clk_register_clkdev(clk, list->name, NULL); + if (ret) + pr_err("%s: failed to register clock lookup for %s", + __func__, list->name); + } +} + +/* register a list of fixed factor clocks */ +void __init samsung_clk_register_fixed_factor( + struct samsung_fixed_factor_clock *list, unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx; + + for (idx = 0; idx < nr_clk; idx++, list++) { + clk = clk_register_fixed_factor(NULL, list->name, + list->parent_name, list->flags, list->mult, list->div); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + samsung_clk_add_lookup(clk, list->id); + } +} + +/* register a list of mux clocks */ +void __init samsung_clk_register_mux(struct samsung_mux_clock *list, + unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + for (idx = 0; idx < nr_clk; idx++, list++) { + clk = clk_register_mux(NULL, list->name, list->parent_names, + list->num_parents, list->flags, reg_base + list->offset, + list->shift, list->width, list->mux_flags, &lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + samsung_clk_add_lookup(clk, list->id); + + /* register a clock lookup only if a clock alias is specified */ + if (list->alias) { + ret = clk_register_clkdev(clk, list->alias, + list->dev_name); + if (ret) + pr_err("%s: failed to register lookup %s\n", + __func__, list->alias); + } + } +} + +/* register a list of div clocks */ +void __init samsung_clk_register_div(struct samsung_div_clock *list, + unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + for (idx = 0; idx < nr_clk; idx++, list++) { + clk = clk_register_divider(NULL, list->name, list->parent_name, + list->flags, reg_base + list->offset, list->shift, + list->width, list->div_flags, &lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + samsung_clk_add_lookup(clk, list->id); + + /* register a clock lookup only if a clock alias is specified */ + if (list->alias) { + ret = clk_register_clkdev(clk, list->alias, + list->dev_name); + if (ret) + pr_err("%s: failed to register lookup %s\n", + __func__, list->alias); + } + } +} + +/* register a list of gate clocks */ +void __init samsung_clk_register_gate(struct samsung_gate_clock *list, + unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + for (idx = 0; idx < nr_clk; idx++, list++) { + clk = clk_register_gate(NULL, list->name, list->parent_name, + list->flags, reg_base + list->offset, + list->bit_idx, list->gate_flags, &lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + /* register a clock lookup only if a clock alias is specified */ + if (list->alias) { + ret = clk_register_clkdev(clk, list->alias, + list->dev_name); + if (ret) + pr_err("%s: failed to register lookup %s\n", + __func__, list->alias); + } + + samsung_clk_add_lookup(clk, list->id); + } +} + +/* + * obtain the clock speed of all external fixed clock sources from device + * tree and register it + */ +void __init samsung_clk_of_register_fixed_ext( + struct samsung_fixed_rate_clock *fixed_rate_clk, + unsigned int nr_fixed_rate_clk, + struct of_device_id *clk_matches) +{ + const struct of_device_id *match; + struct device_node *np; + u32 freq; + + for_each_matching_node_and_match(np, clk_matches, &match) { + if (of_property_read_u32(np, "clock-frequency", &freq)) + continue; + fixed_rate_clk[(u32)match->data].fixed_rate = freq; + } + samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk); +} + +/* utility function to get the rate of a specified clock */ +unsigned long _get_rate(const char *clk_name) +{ + struct clk *clk; + unsigned long rate; + + clk = clk_get(NULL, clk_name); + if (IS_ERR(clk)) { + pr_err("%s: could not find clock %s\n", __func__, clk_name); + return 0; + } + rate = clk_get_rate(clk); + clk_put(clk); + return rate; +} diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h new file mode 100644 index 0000000..961192f --- /dev/null +++ b/drivers/clk/samsung/clk.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham + * + * 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 Clock Framework support for all Samsung platforms +*/ + +#ifndef __SAMSUNG_CLK_H +#define __SAMSUNG_CLK_H + +#include +#include +#include +#include +#include +#include + +#include + +/** + * struct samsung_fixed_rate_clock: information about fixed-rate clock + * @id: platform specific id of the clock. + * @name: name of this fixed-rate clock. + * @parent_name: optional parent clock name. + * @flags: optional fixed-rate clock flags. + * @fixed-rate: fixed clock rate of this clock. + */ +struct samsung_fixed_rate_clock { + unsigned int id; + char *name; + const char *parent_name; + unsigned long flags; + unsigned long fixed_rate; +}; + +#define FRATE(_id, cname, pname, f, frate) \ + { \ + .id = _id, \ + .name = cname, \ + .parent_name = pname, \ + .flags = f, \ + .fixed_rate = frate, \ + } + +/* + * struct samsung_fixed_factor_clock: information about fixed-factor clock + * @id: platform specific id of the clock. + * @name: name of this fixed-factor clock. + * @parent_name: parent clock name. + * @mult: fixed multiplication factor. + * @div: fixed division factor. + * @flags: optional fixed-factor clock flags. + */ +struct samsung_fixed_factor_clock { + unsigned int id; + char *name; + const char *parent_name; + unsigned long mult; + unsigned long div; + unsigned long flags; +}; + +#define FFACTOR(_id, cname, pname, m, d, f) \ + { \ + .id = _id, \ + .name = cname, \ + .parent_name = pname, \ + .mult = m, \ + .div = d, \ + .flags = f, \ + } + +/** + * struct samsung_mux_clock: information about mux clock + * @id: platform specific id of the clock. + * @dev_name: name of the device to which this clock belongs. + * @name: name of this mux clock. + * @parent_names: array of pointer to parent clock names. + * @num_parents: number of parents listed in @parent_names. + * @flags: optional flags for basic clock. + * @offset: offset of the register for configuring the mux. + * @shift: starting bit location of the mux control bit-field in @reg. + * @width: width of the mux control bit-field in @reg. + * @mux_flags: flags for mux-type clock. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_mux_clock { + unsigned int id; + const char *dev_name; + const char *name; + const char **parent_names; + u8 num_parents; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 mux_flags; + const char *alias; +}; + +#define __MUX(_id, dname, cname, pnames, o, s, w, f, mf, a) \ + { \ + .id = _id, \ + .dev_name = dname, \ + .name = cname, \ + .parent_names = pnames, \ + .num_parents = ARRAY_SIZE(pnames), \ + .flags = f, \ + .offset = o, \ + .shift = s, \ + .width = w, \ + .mux_flags = mf, \ + .alias = a, \ + } + +#define MUX(_id, cname, pnames, o, s, w) \ + __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, NULL) + +#define MUX_A(_id, cname, pnames, o, s, w, a) \ + __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, a) + +#define MUX_F(_id, cname, pnames, o, s, w, f, mf) \ + __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, NULL) + +/** + * @id: platform specific id of the clock. + * struct samsung_div_clock: information about div clock + * @dev_name: name of the device to which this clock belongs. + * @name: name of this div clock. + * @parent_name: name of the parent clock. + * @flags: optional flags for basic clock. + * @offset: offset of the register for configuring the div. + * @shift: starting bit location of the div control bit-field in @reg. + * @div_flags: flags for div-type clock. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_div_clock { + unsigned int id; + const char *dev_name; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 div_flags; + const char *alias; +}; + +#define __DIV(_id, dname, cname, pname, o, s, w, f, df, a) \ + { \ + .id = _id, \ + .dev_name = dname, \ + .name = cname, \ + .parent_name = pname, \ + .flags = f, \ + .offset = o, \ + .shift = s, \ + .width = w, \ + .div_flags = df, \ + .alias = a, \ + } + +#define DIV(_id, cname, pname, o, s, w) \ + __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL) + +#define DIV_A(_id, cname, pname, o, s, w, a) \ + __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, a) + +#define DIV_F(_id, cname, pname, o, s, w, f, df) \ + __DIV(_id, NULL, cname, pname, o, s, w, f, df, NULL) + +/** + * struct samsung_gate_clock: information about gate clock + * @id: platform specific id of the clock. + * @dev_name: name of the device to which this clock belongs. + * @name: name of this gate clock. + * @parent_name: name of the parent clock. + * @flags: optional flags for basic clock. + * @offset: offset of the register for configuring the gate. + * @bit_idx: bit index of the gate control bit-field in @reg. + * @gate_flags: flags for gate-type clock. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_gate_clock { + unsigned int id; + const char *dev_name; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 bit_idx; + u8 gate_flags; + const char *alias; +}; + +#define __GATE(_id, dname, cname, pname, o, b, f, gf, a) \ + { \ + .id = _id, \ + .dev_name = dname, \ + .name = cname, \ + .parent_name = pname, \ + .flags = f, \ + .offset = o, \ + .bit_idx = b, \ + .gate_flags = gf, \ + .alias = a, \ + } + +#define GATE(_id, cname, pname, o, b, f, gf) \ + __GATE(_id, NULL, cname, pname, o, b, f, gf, NULL) + +#define GATE_A(_id, cname, pname, o, b, f, gf, a) \ + __GATE(_id, NULL, cname, pname, o, b, f, gf, a) + +#define GATE_D(_id, dname, cname, pname, o, b, f, gf) \ + __GATE(_id, dname, cname, pname, o, b, f, gf, NULL) + +#define GATE_DA(_id, dname, cname, pname, o, b, f, gf, a) \ + __GATE(_id, dname, cname, pname, o, b, f, gf, a) + +#define PNAME(x) static const char *x[] __initdata + +/** + * struct samsung_clk_reg_dump: register dump of clock controller registers. + * @offset: clock register offset from the controller base address. + * @value: the value to be register at offset. + */ +struct samsung_clk_reg_dump { + u32 offset; + u32 value; +}; + +extern void __init samsung_clk_init(struct device_node *np, void __iomem *base, + unsigned long nr_clks, unsigned long *rdump, + unsigned long nr_rdump); +extern void __init samsung_clk_of_register_fixed_ext( + struct samsung_fixed_rate_clock *fixed_rate_clk, + unsigned int nr_fixed_rate_clk, + struct of_device_id *clk_matches); + +extern void samsung_clk_add_lookup(struct clk *clk, unsigned int id); + +extern void __init samsung_clk_register_fixed_rate( + struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk); +extern void __init samsung_clk_register_fixed_factor( + struct samsung_fixed_factor_clock *list, unsigned int nr_clk); +extern void __init samsung_clk_register_mux(struct samsung_mux_clock *clk_list, + unsigned int nr_clk); +extern void __init samsung_clk_register_div(struct samsung_div_clock *clk_list, + unsigned int nr_clk); +extern void __init samsung_clk_register_gate( + struct samsung_gate_clock *clk_list, unsigned int nr_clk); + +extern unsigned long _get_rate(const char *clk_name); + +#endif /* __SAMSUNG_CLK_H */ -- cgit v1.1 From 1c4c5fe0b787ab02bf7c01b091e82e8d09ee1d64 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 9 Mar 2013 17:02:48 +0900 Subject: clk: samsung: add pll clock registration helper functions There are several types of pll clocks used in Samsung SoC's and these pll clocks can be represented as Samsung specific pll clock types and registered with the common clock framework. Add support for pll35xx, pll36xx, pll45xx, pll46xx and pll2550x clock types and helper functions to register them. Reviewed-by: Sylwester Nawrocki Tested-by: Sylwester Nawrocki Reviewed-by: Tomasz Figa Tested-by: Tomasz Figa Signed-off-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 2 +- drivers/clk/samsung/clk-pll.c | 499 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/samsung/clk-pll.h | 41 ++++ 3 files changed, 541 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/samsung/clk-pll.c create mode 100644 drivers/clk/samsung/clk-pll.h (limited to 'drivers') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index bd920b4..78e5aaa 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -2,4 +2,4 @@ # Samsung Clock specific Makefile # -obj-$(CONFIG_COMMON_CLK) += clk.o +obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c new file mode 100644 index 0000000..4b24511 --- /dev/null +++ b/drivers/clk/samsung/clk-pll.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * + * 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. + * + * This file contains the utility functions to register the pll clocks. +*/ + +#include +#include "clk.h" +#include "clk-pll.h" + +/* + * PLL35xx Clock Type + */ + +#define PLL35XX_MDIV_MASK (0x3FF) +#define PLL35XX_PDIV_MASK (0x3F) +#define PLL35XX_SDIV_MASK (0x7) +#define PLL35XX_MDIV_SHIFT (16) +#define PLL35XX_PDIV_SHIFT (8) +#define PLL35XX_SDIV_SHIFT (0) + +struct samsung_clk_pll35xx { + struct clk_hw hw; + const void __iomem *con_reg; +}; + +#define to_clk_pll35xx(_hw) container_of(_hw, struct samsung_clk_pll35xx, hw) + +static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll35xx *pll = to_clk_pll35xx(hw); + u32 mdiv, pdiv, sdiv, pll_con; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; + pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK; + sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK; + + fvco *= mdiv; + do_div(fvco, (pdiv << sdiv)); + + return (unsigned long)fvco; +} + +/* todo: implement pl35xx clock round rate operation */ +static long samsung_pll35xx_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + return -ENOTSUPP; +} + +/* todo: implement pl35xx clock set rate */ +static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + return -ENOTSUPP; +} + +static const struct clk_ops samsung_pll35xx_clk_ops = { + .recalc_rate = samsung_pll35xx_recalc_rate, + .round_rate = samsung_pll35xx_round_rate, + .set_rate = samsung_pll35xx_set_rate, +}; + +struct clk * __init samsung_clk_register_pll35xx(const char *name, + const char *pname, const void __iomem *con_reg) +{ + struct samsung_clk_pll35xx *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll35xx_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->con_reg = con_reg; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} + +/* + * PLL36xx Clock Type + */ + +#define PLL36XX_KDIV_MASK (0xFFFF) +#define PLL36XX_MDIV_MASK (0x1FF) +#define PLL36XX_PDIV_MASK (0x3F) +#define PLL36XX_SDIV_MASK (0x7) +#define PLL36XX_MDIV_SHIFT (16) +#define PLL36XX_PDIV_SHIFT (8) +#define PLL36XX_SDIV_SHIFT (0) + +struct samsung_clk_pll36xx { + struct clk_hw hw; + const void __iomem *con_reg; +}; + +#define to_clk_pll36xx(_hw) container_of(_hw, struct samsung_clk_pll36xx, hw) + +static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll36xx *pll = to_clk_pll36xx(hw); + u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1; + u64 fvco = parent_rate; + + pll_con0 = __raw_readl(pll->con_reg); + pll_con1 = __raw_readl(pll->con_reg + 4); + mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK; + pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK; + sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK; + kdiv = pll_con1 & PLL36XX_KDIV_MASK; + + fvco *= (mdiv << 16) + kdiv; + do_div(fvco, (pdiv << sdiv)); + fvco >>= 16; + + return (unsigned long)fvco; +} + +/* todo: implement pl36xx clock round rate operation */ +static long samsung_pll36xx_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + return -ENOTSUPP; +} + +/* todo: implement pl36xx clock set rate */ +static int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + return -ENOTSUPP; +} + +static const struct clk_ops samsung_pll36xx_clk_ops = { + .recalc_rate = samsung_pll36xx_recalc_rate, + .round_rate = samsung_pll36xx_round_rate, + .set_rate = samsung_pll36xx_set_rate, +}; + +struct clk * __init samsung_clk_register_pll36xx(const char *name, + const char *pname, const void __iomem *con_reg) +{ + struct samsung_clk_pll36xx *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll36xx_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->con_reg = con_reg; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} + +/* + * PLL45xx Clock Type + */ + +#define PLL45XX_MDIV_MASK (0x3FF) +#define PLL45XX_PDIV_MASK (0x3F) +#define PLL45XX_SDIV_MASK (0x7) +#define PLL45XX_MDIV_SHIFT (16) +#define PLL45XX_PDIV_SHIFT (8) +#define PLL45XX_SDIV_SHIFT (0) + +struct samsung_clk_pll45xx { + struct clk_hw hw; + enum pll45xx_type type; + const void __iomem *con_reg; +}; + +#define to_clk_pll45xx(_hw) container_of(_hw, struct samsung_clk_pll45xx, hw) + +static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw); + u32 mdiv, pdiv, sdiv, pll_con; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK; + pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK; + sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK; + + if (pll->type == pll_4508) + sdiv = sdiv - 1; + + fvco *= mdiv; + do_div(fvco, (pdiv << sdiv)); + + return (unsigned long)fvco; +} + +/* todo: implement pl45xx clock round rate operation */ +static long samsung_pll45xx_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + return -ENOTSUPP; +} + +/* todo: implement pl45xx clock set rate */ +static int samsung_pll45xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + return -ENOTSUPP; +} + +static const struct clk_ops samsung_pll45xx_clk_ops = { + .recalc_rate = samsung_pll45xx_recalc_rate, + .round_rate = samsung_pll45xx_round_rate, + .set_rate = samsung_pll45xx_set_rate, +}; + +struct clk * __init samsung_clk_register_pll45xx(const char *name, + const char *pname, const void __iomem *con_reg, + enum pll45xx_type type) +{ + struct samsung_clk_pll45xx *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll45xx_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->con_reg = con_reg; + pll->type = type; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} + +/* + * PLL46xx Clock Type + */ + +#define PLL46XX_MDIV_MASK (0x1FF) +#define PLL46XX_PDIV_MASK (0x3F) +#define PLL46XX_SDIV_MASK (0x7) +#define PLL46XX_MDIV_SHIFT (16) +#define PLL46XX_PDIV_SHIFT (8) +#define PLL46XX_SDIV_SHIFT (0) + +#define PLL46XX_KDIV_MASK (0xFFFF) +#define PLL4650C_KDIV_MASK (0xFFF) +#define PLL46XX_KDIV_SHIFT (0) + +struct samsung_clk_pll46xx { + struct clk_hw hw; + enum pll46xx_type type; + const void __iomem *con_reg; +}; + +#define to_clk_pll46xx(_hw) container_of(_hw, struct samsung_clk_pll46xx, hw) + +static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll46xx *pll = to_clk_pll46xx(hw); + u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift; + u64 fvco = parent_rate; + + pll_con0 = __raw_readl(pll->con_reg); + pll_con1 = __raw_readl(pll->con_reg + 4); + mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK; + pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK; + sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK; + kdiv = pll->type == pll_4650c ? pll_con1 & PLL4650C_KDIV_MASK : + pll_con1 & PLL46XX_KDIV_MASK; + + shift = pll->type == pll_4600 ? 16 : 10; + fvco *= (mdiv << shift) + kdiv; + do_div(fvco, (pdiv << sdiv)); + fvco >>= shift; + + return (unsigned long)fvco; +} + +/* todo: implement pl46xx clock round rate operation */ +static long samsung_pll46xx_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + return -ENOTSUPP; +} + +/* todo: implement pl46xx clock set rate */ +static int samsung_pll46xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + return -ENOTSUPP; +} + +static const struct clk_ops samsung_pll46xx_clk_ops = { + .recalc_rate = samsung_pll46xx_recalc_rate, + .round_rate = samsung_pll46xx_round_rate, + .set_rate = samsung_pll46xx_set_rate, +}; + +struct clk * __init samsung_clk_register_pll46xx(const char *name, + const char *pname, const void __iomem *con_reg, + enum pll46xx_type type) +{ + struct samsung_clk_pll46xx *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll46xx_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->con_reg = con_reg; + pll->type = type; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} + +/* + * PLL2550x Clock Type + */ + +#define PLL2550X_R_MASK (0x1) +#define PLL2550X_P_MASK (0x3F) +#define PLL2550X_M_MASK (0x3FF) +#define PLL2550X_S_MASK (0x7) +#define PLL2550X_R_SHIFT (20) +#define PLL2550X_P_SHIFT (14) +#define PLL2550X_M_SHIFT (4) +#define PLL2550X_S_SHIFT (0) + +struct samsung_clk_pll2550x { + struct clk_hw hw; + const void __iomem *reg_base; + unsigned long offset; +}; + +#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw) + +static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw); + u32 r, p, m, s, pll_stat; + u64 fvco = parent_rate; + + pll_stat = __raw_readl(pll->reg_base + pll->offset * 3); + r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK; + if (!r) + return 0; + p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK; + m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK; + s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK; + + fvco *= m; + do_div(fvco, (p << s)); + + return (unsigned long)fvco; +} + +/* todo: implement pl2550x clock round rate operation */ +static long samsung_pll2550x_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + return -ENOTSUPP; +} + +/* todo: implement pl2550x clock set rate */ +static int samsung_pll2550x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + return -ENOTSUPP; +} + +static const struct clk_ops samsung_pll2550x_clk_ops = { + .recalc_rate = samsung_pll2550x_recalc_rate, + .round_rate = samsung_pll2550x_round_rate, + .set_rate = samsung_pll2550x_set_rate, +}; + +struct clk * __init samsung_clk_register_pll2550x(const char *name, + const char *pname, const void __iomem *reg_base, + const unsigned long offset) +{ + struct samsung_clk_pll2550x *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll2550x_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->reg_base = reg_base; + pll->offset = offset; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h new file mode 100644 index 0000000..f33786e --- /dev/null +++ b/drivers/clk/samsung/clk-pll.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * + * 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 Clock Framework support for all PLL's in Samsung platforms +*/ + +#ifndef __SAMSUNG_CLK_PLL_H +#define __SAMSUNG_CLK_PLL_H + +enum pll45xx_type { + pll_4500, + pll_4502, + pll_4508 +}; + +enum pll46xx_type { + pll_4600, + pll_4650, + pll_4650c, +}; + +extern struct clk * __init samsung_clk_register_pll35xx(const char *name, + const char *pname, const void __iomem *con_reg); +extern struct clk * __init samsung_clk_register_pll36xx(const char *name, + const char *pname, const void __iomem *con_reg); +extern struct clk * __init samsung_clk_register_pll45xx(const char *name, + const char *pname, const void __iomem *con_reg, + enum pll45xx_type type); +extern struct clk * __init samsung_clk_register_pll46xx(const char *name, + const char *pname, const void __iomem *con_reg, + enum pll46xx_type type); +extern struct clk * __init samsung_clk_register_pll2550x(const char *name, + const char *pname, const void __iomem *reg_base, + const unsigned long offset); + +#endif /* __SAMSUNG_CLK_PLL_H */ -- cgit v1.1 From e062b571777f52dfbfc15f9edc2d36a45664bb3a Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 9 Mar 2013 17:02:52 +0900 Subject: clk: exynos4: register clocks using common clock framework The Exynos4 clocks are statically listed and registered using the Samsung specific common clock helper functions. Both device tree based clock lookup and clkdev based clock lookups are supported. Reviewed-by: Sylwester Nawrocki Tested-by: Sylwester Nawrocki Reviewed-by: Tomasz Figa Tested-by: Tomasz Figa Signed-off-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-exynos4.c | 843 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 844 insertions(+) create mode 100644 drivers/clk/samsung/clk-exynos4.c (limited to 'drivers') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 78e5aaa..8862f0d 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o +obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c new file mode 100644 index 0000000..e1bb81a --- /dev/null +++ b/drivers/clk/samsung/clk-exynos4.c @@ -0,0 +1,843 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham + * + * 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 Clock Framework support for all Exynos4 SoCs. +*/ + +#include +#include +#include +#include +#include + +#include +#include "clk.h" +#include "clk-pll.h" + +/* Exynos4 clock controller register offsets */ +#define SRC_LEFTBUS 0x4200 +#define E4X12_GATE_IP_IMAGE 0x4930 +#define GATE_IP_RIGHTBUS 0x8800 +#define E4X12_GATE_IP_PERIR 0x8960 +#define SRC_TOP0 0xc210 +#define SRC_TOP1 0xc214 +#define SRC_CAM 0xc220 +#define SRC_TV 0xc224 +#define SRC_MFC 0xcc28 +#define SRC_G3D 0xc22c +#define E4210_SRC_IMAGE 0xc230 +#define SRC_LCD0 0xc234 +#define SRC_LCD1 0xc238 +#define SRC_MAUDIO 0xc23c +#define SRC_FSYS 0xc240 +#define SRC_PERIL0 0xc250 +#define SRC_PERIL1 0xc254 +#define E4X12_SRC_CAM1 0xc258 +#define SRC_MASK_CAM 0xc320 +#define SRC_MASK_TV 0xc324 +#define SRC_MASK_LCD0 0xc334 +#define SRC_MASK_LCD1 0xc338 +#define SRC_MASK_MAUDIO 0xc33c +#define SRC_MASK_FSYS 0xc340 +#define SRC_MASK_PERIL0 0xc350 +#define SRC_MASK_PERIL1 0xc354 +#define DIV_TOP 0xc510 +#define DIV_CAM 0xc520 +#define DIV_TV 0xc524 +#define DIV_MFC 0xc528 +#define DIV_G3D 0xc52c +#define DIV_IMAGE 0xc530 +#define DIV_LCD0 0xc534 +#define E4210_DIV_LCD1 0xc538 +#define E4X12_DIV_ISP 0xc538 +#define DIV_MAUDIO 0xc53c +#define DIV_FSYS0 0xc540 +#define DIV_FSYS1 0xc544 +#define DIV_FSYS2 0xc548 +#define DIV_FSYS3 0xc54c +#define DIV_PERIL0 0xc550 +#define DIV_PERIL1 0xc554 +#define DIV_PERIL2 0xc558 +#define DIV_PERIL3 0xc55c +#define DIV_PERIL4 0xc560 +#define DIV_PERIL5 0xc564 +#define E4X12_DIV_CAM1 0xc568 +#define GATE_SCLK_CAM 0xc820 +#define GATE_IP_CAM 0xc920 +#define GATE_IP_TV 0xc924 +#define GATE_IP_MFC 0xc928 +#define GATE_IP_G3D 0xc92c +#define E4210_GATE_IP_IMAGE 0xc930 +#define GATE_IP_LCD0 0xc934 +#define GATE_IP_LCD1 0xc938 +#define E4X12_GATE_IP_MAUDIO 0xc93c +#define GATE_IP_FSYS 0xc940 +#define GATE_IP_GPS 0xc94c +#define GATE_IP_PERIL 0xc950 +#define GATE_IP_PERIR 0xc960 +#define E4X12_MPLL_CON0 0x10108 +#define E4X12_SRC_DMC 0x10200 +#define APLL_CON0 0x14100 +#define E4210_MPLL_CON0 0x14108 +#define SRC_CPU 0x14200 +#define DIV_CPU0 0x14500 + +/* the exynos4 soc type */ +enum exynos4_soc { + EXYNOS4210, + EXYNOS4X12, +}; + +/* + * Let each supported clock get a unique id. This id is used to lookup the clock + * for device tree based platforms. The clocks are categorized into three + * sections: core, sclk gate and bus interface gate clocks. + * + * When adding a new clock to this list, it is advised to choose a clock + * category and add it to the end of that category. That is because the the + * device tree source file is referring to these ids and any change in the + * sequence number of existing clocks will require corresponding change in the + * device tree files. This limitation would go away when pre-processor support + * for dtc would be available. + */ +enum exynos4_clks { + none, + + /* core clocks */ + xxti, xusbxti, fin_pll, fout_apll, fout_mpll, fout_epll, fout_vpll, + sclk_apll, sclk_mpll, sclk_epll, sclk_vpll, arm_clk, aclk200, aclk100, + aclk160, aclk133, + + /* gate for special clocks (sclk) */ + sclk_fimc0 = 128, sclk_fimc1, sclk_fimc2, sclk_fimc3, sclk_cam0, + sclk_cam1, sclk_csis0, sclk_csis1, sclk_hdmi, sclk_mixer, sclk_dac, + sclk_pixel, sclk_fimd0, sclk_mdnie0, sclk_mdnie_pwm0, sclk_mipi0, + sclk_audio0, sclk_mmc0, sclk_mmc1, sclk_mmc2, sclk_mmc3, sclk_mmc4, + sclk_sata, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_uart4, + sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2, + sclk_slimbus, sclk_fimd1, sclk_mipi1, sclk_pcm1, sclk_pcm2, sclk_i2s1, + sclk_i2s2, sclk_mipihsi, + + /* gate clocks */ + fimc0 = 256, fimc1, fimc2, fimc3, csis0, csis1, jpeg, smmu_fimc0, + smmu_fimc1, smmu_fimc2, smmu_fimc3, smmu_jpeg, vp, mixer, tvenc, hdmi, + smmu_tv, mfc, smmu_mfcl, smmu_mfcr, g3d, g2d, rotator, mdma, smmu_g2d, + smmu_rotator, smmu_mdma, fimd0, mie0, mdnie0, dsim0, smmu_fimd0, fimd1, + mie1, dsim1, smmu_fimd1, pdma0, pdma1, pcie_phy, sata_phy, tsi, sdmmc0, + sdmmc1, sdmmc2, sdmmc3, sdmmc4, sata, sromc, usb_host, usb_device, pcie, + onenand, nfcon, smmu_pcie, gps, smmu_gps, uart0, uart1, uart2, uart3, + uart4, i2c0, i2c1, i2c2, i2c3, i2c4, i2c5, i2c6, i2c7, i2c_hdmi, tsadc, + spi0, spi1, spi2, i2s1, i2s2, pcm0, i2s0, pcm1, pcm2, pwm, slimbus, + spdif, ac97, modemif, chipid, sysreg, hdmi_cec, mct, wdt, rtc, keyif, + audss, mipi_hsi, mdma2, + + nr_clks, +}; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static __initdata unsigned long exynos4_clk_regs[] = { + SRC_LEFTBUS, + E4X12_GATE_IP_IMAGE, + GATE_IP_RIGHTBUS, + E4X12_GATE_IP_PERIR, + SRC_TOP0, + SRC_TOP1, + SRC_CAM, + SRC_TV, + SRC_MFC, + SRC_G3D, + E4210_SRC_IMAGE, + SRC_LCD0, + SRC_LCD1, + SRC_MAUDIO, + SRC_FSYS, + SRC_PERIL0, + SRC_PERIL1, + E4X12_SRC_CAM1, + SRC_MASK_CAM, + SRC_MASK_TV, + SRC_MASK_LCD0, + SRC_MASK_LCD1, + SRC_MASK_MAUDIO, + SRC_MASK_FSYS, + SRC_MASK_PERIL0, + SRC_MASK_PERIL1, + DIV_TOP, + DIV_CAM, + DIV_TV, + DIV_MFC, + DIV_G3D, + DIV_IMAGE, + DIV_LCD0, + E4210_DIV_LCD1, + E4X12_DIV_ISP, + DIV_MAUDIO, + DIV_FSYS0, + DIV_FSYS1, + DIV_FSYS2, + DIV_FSYS3, + DIV_PERIL0, + DIV_PERIL1, + DIV_PERIL2, + DIV_PERIL3, + DIV_PERIL4, + DIV_PERIL5, + E4X12_DIV_CAM1, + GATE_SCLK_CAM, + GATE_IP_CAM, + GATE_IP_TV, + GATE_IP_MFC, + GATE_IP_G3D, + E4210_GATE_IP_IMAGE, + GATE_IP_LCD0, + GATE_IP_LCD1, + E4X12_GATE_IP_MAUDIO, + GATE_IP_FSYS, + GATE_IP_GPS, + GATE_IP_PERIL, + GATE_IP_PERIR, + E4X12_MPLL_CON0, + E4X12_SRC_DMC, + APLL_CON0, + E4210_MPLL_CON0, + SRC_CPU, + DIV_CPU0, +}; + +/* list of all parent clock list */ +PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; +PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", }; +PNAME(mout_epll_p) = { "fin_pll", "fout_epll", }; +PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi24m", }; +PNAME(sclk_vpll_p4210) = { "mout_vpllsrc", "fout_vpll", }; +PNAME(mout_vpll_p) = { "fin_pll", "fout_vpll", }; +PNAME(mout_core_p) = { "mout_apll", "sclk_mpll", }; +PNAME(sclk_ampll_p) = { "sclk_mpll", "sclk_apll", }; +PNAME(mout_mpll_user_p) = { "fin_pll", "sclk_mpll", }; +PNAME(aclk_p4412) = { "mout_mpll_user", "sclk_apll", }; +PNAME(sclk_evpll_p) = { "sclk_epll", "sclk_vpll", }; +PNAME(mout_mfc_p) = { "mout_mfc0", "mout_mfc1", }; +PNAME(mout_g3d_p) = { "mout_g3d0", "mout_g3d1", }; +PNAME(mout_g2d_p) = { "mout_g2d0", "mout_g2d1", }; +PNAME(mout_mixer_p4210) = { "sclk_dac", "sclk_hdmi", }; +PNAME(mout_dac_p4210) = { "sclk_vpll", "sclk_hdmiphy", }; +PNAME(mout_hdmi_p) = { "sclk_pixel", "sclk_hdmiphy", }; +PNAME(mout_jpeg_p) = { "mout_jpeg0", "mout_jpeg1", }; +PNAME(group1_p) = { "xxti", "xusbxti", "sclk_hdmi24m", "sclk_usbphy0", + "none", "sclk_hdmiphy", "sclk_mpll", + "sclk_epll", "sclk_vpll", }; +PNAME(mout_audio0_p) = { "cdclk0", "none", "sclk_hdmi24m", "sclk_usbphy0", + "xxti", "xusbxti", "sclk_mpll", "sclk_epll", + "sclk_vpll" }; +PNAME(mout_audio1_p) = { "cdclk1", "none", "sclk_hdmi24m", "sclk_usbphy0", + "xxti", "xusbxti", "sclk_mpll", "sclk_epll", + "sclk_vpll", }; +PNAME(mout_audio2_p) = { "cdclk2", "none", "sclk_hdmi24m", "sclk_usbphy0", + "xxti", "xusbxti", "sclk_mpll", "sclk_epll", + "sclk_vpll", }; +PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2", + "spdif_extclk", }; + +/* fixed rate clocks generated outside the soc */ +struct samsung_fixed_rate_clock exynos4_fixed_rate_ext_clks[] __initdata = { + FRATE(xxti, "xxti", NULL, CLK_IS_ROOT, 0), + FRATE(xusbxti, "xusbxti", NULL, CLK_IS_ROOT, 0), +}; + +/* fixed rate clocks generated inside the soc */ +struct samsung_fixed_rate_clock exynos4_fixed_rate_clks[] __initdata = { + FRATE(none, "sclk_hdmi24m", NULL, CLK_IS_ROOT, 24000000), + FRATE(none, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000), + FRATE(none, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000), +}; + +struct samsung_fixed_rate_clock exynos4210_fixed_rate_clks[] __initdata = { + FRATE(none, "sclk_usbphy1", NULL, CLK_IS_ROOT, 48000000), +}; + +/* list of mux clocks supported in all exynos4 soc's */ +struct samsung_mux_clock exynos4_mux_clks[] __initdata = { + MUX(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), + MUX(none, "mout_core", mout_core_p, SRC_CPU, 16, 1), + MUX(none, "mout_fimc0", group1_p, SRC_CAM, 0, 4), + MUX(none, "mout_fimc1", group1_p, SRC_CAM, 4, 4), + MUX(none, "mout_fimc2", group1_p, SRC_CAM, 8, 4), + MUX(none, "mout_fimc3", group1_p, SRC_CAM, 12, 4), + MUX(none, "mout_cam0", group1_p, SRC_CAM, 16, 4), + MUX(none, "mout_cam1", group1_p, SRC_CAM, 20, 4), + MUX(none, "mout_csis0", group1_p, SRC_CAM, 24, 4), + MUX(none, "mout_csis1", group1_p, SRC_CAM, 28, 4), + MUX(none, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), + MUX(none, "mout_mfc0", sclk_ampll_p, SRC_MFC, 0, 1), + MUX(none, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), + MUX(none, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), + MUX(none, "mout_g3d0", sclk_ampll_p, SRC_G3D, 0, 1), + MUX(none, "mout_g3d1", sclk_evpll_p, SRC_G3D, 4, 1), + MUX(none, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1), + MUX(none, "mout_fimd0", group1_p, SRC_LCD0, 0, 4), + MUX(none, "mout_mipi0", group1_p, SRC_LCD0, 12, 4), + MUX(none, "mout_audio0", mout_audio0_p, SRC_MAUDIO, 0, 4), + MUX(none, "mout_mmc0", group1_p, SRC_FSYS, 0, 4), + MUX(none, "mout_mmc1", group1_p, SRC_FSYS, 4, 4), + MUX(none, "mout_mmc2", group1_p, SRC_FSYS, 8, 4), + MUX(none, "mout_mmc3", group1_p, SRC_FSYS, 12, 4), + MUX(none, "mout_mmc4", group1_p, SRC_FSYS, 16, 4), + MUX(none, "mout_uart0", group1_p, SRC_PERIL0, 0, 4), + MUX(none, "mout_uart1", group1_p, SRC_PERIL0, 4, 4), + MUX(none, "mout_uart2", group1_p, SRC_PERIL0, 8, 4), + MUX(none, "mout_uart3", group1_p, SRC_PERIL0, 12, 4), + MUX(none, "mout_uart4", group1_p, SRC_PERIL0, 16, 4), + MUX(none, "mout_audio1", mout_audio1_p, SRC_PERIL1, 0, 4), + MUX(none, "mout_audio2", mout_audio2_p, SRC_PERIL1, 4, 4), + MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIL1, 8, 2), + MUX(none, "mout_spi0", group1_p, SRC_PERIL1, 16, 4), + MUX(none, "mout_spi1", group1_p, SRC_PERIL1, 20, 4), + MUX(none, "mout_spi2", group1_p, SRC_PERIL1, 24, 4), + MUX_A(sclk_epll, "sclk_epll", mout_epll_p, SRC_TOP0, 4, 1, "sclk_epll"), +}; + +/* list of mux clocks supported in exynos4210 soc */ +struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { + MUX(none, "mout_aclk200", sclk_ampll_p, SRC_TOP0, 12, 1), + MUX(none, "mout_aclk100", sclk_ampll_p, SRC_TOP0, 16, 1), + MUX(none, "mout_aclk160", sclk_ampll_p, SRC_TOP0, 20, 1), + MUX(none, "mout_aclk133", sclk_ampll_p, SRC_TOP0, 24, 1), + MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP1, 0, 1), + MUX(none, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1), + MUX(none, "mout_dac", mout_dac_p4210, SRC_TV, 8, 1), + MUX(none, "mout_g2d0", sclk_ampll_p, E4210_SRC_IMAGE, 0, 1), + MUX(none, "mout_g2d1", sclk_evpll_p, E4210_SRC_IMAGE, 4, 1), + MUX(none, "mout_g2d", mout_g2d_p, E4210_SRC_IMAGE, 8, 1), + MUX(none, "mout_fimd1", group1_p, SRC_LCD1, 0, 4), + MUX(none, "mout_mipi1", group1_p, SRC_LCD1, 12, 4), + MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1, "sclk_mpll"), + MUX_A(sclk_vpll, "sclk_vpll", sclk_vpll_p4210, + SRC_TOP0, 8, 1, "sclk_vpll"), +}; + +/* list of mux clocks supported in exynos4x12 soc */ +struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { + MUX(none, "mout_mpll_user", mout_mpll_user_p, SRC_LEFTBUS, 4, 1), + MUX(none, "mout_aclk200", aclk_p4412, SRC_TOP0, 12, 1), + MUX(none, "mout_aclk100", aclk_p4412, SRC_TOP0, 16, 1), + MUX(none, "mout_aclk160", aclk_p4412, SRC_TOP0, 20, 1), + MUX(none, "mout_aclk133", aclk_p4412, SRC_TOP0, 24, 1), + MUX(none, "mout_mdnie0", group1_p, SRC_LCD0, 4, 4), + MUX(none, "mout_mdnie_pwm0", group1_p, SRC_LCD0, 8, 4), + MUX(none, "mout_sata", sclk_ampll_p, SRC_FSYS, 24, 1), + MUX(none, "mout_jpeg0", sclk_ampll_p, E4X12_SRC_CAM1, 0, 1), + MUX(none, "mout_jpeg1", sclk_evpll_p, E4X12_SRC_CAM1, 4, 1), + MUX(none, "mout_jpeg", mout_jpeg_p, E4X12_SRC_CAM1, 8, 1), + MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, + E4X12_SRC_DMC, 12, 1, "sclk_mpll"), + MUX_A(sclk_vpll, "sclk_vpll", mout_vpll_p, + SRC_TOP0, 8, 1, "sclk_vpll"), +}; + +/* list of divider clocks supported in all exynos4 soc's */ +struct samsung_div_clock exynos4_div_clks[] __initdata = { + DIV(none, "div_core", "mout_core", DIV_CPU0, 0, 3), + DIV(none, "div_core2", "div_core", DIV_CPU0, 28, 3), + DIV(none, "div_fimc0", "mout_fimc0", DIV_CAM, 0, 4), + DIV(none, "div_fimc1", "mout_fimc1", DIV_CAM, 4, 4), + DIV(none, "div_fimc2", "mout_fimc2", DIV_CAM, 8, 4), + DIV(none, "div_fimc3", "mout_fimc3", DIV_CAM, 12, 4), + DIV(none, "div_cam0", "mout_cam0", DIV_CAM, 16, 4), + DIV(none, "div_cam1", "mout_cam1", DIV_CAM, 20, 4), + DIV(none, "div_csis0", "mout_csis0", DIV_CAM, 24, 4), + DIV(none, "div_csis1", "mout_csis1", DIV_CAM, 28, 4), + DIV(none, "div_mfc", "mout_mfc", DIV_MFC, 0, 4), + DIV(none, "div_g3d", "mout_g3d", DIV_G3D, 0, 4), + DIV(none, "div_fimd0", "mout_fimd0", DIV_LCD0, 0, 4), + DIV(none, "div_mipi0", "mout_mipi0", DIV_LCD0, 16, 4), + DIV(none, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4), + DIV(none, "div_pcm0", "sclk_audio0", DIV_MAUDIO, 4, 8), + DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4), + DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4), + DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4), + DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4), + DIV(sclk_pixel, "sclk_pixel", "sclk_vpll", DIV_TV, 0, 4), + DIV(aclk200, "aclk200", "mout_aclk200", DIV_TOP, 0, 3), + DIV(aclk100, "aclk100", "mout_aclk100", DIV_TOP, 4, 4), + DIV(aclk160, "aclk160", "mout_aclk160", DIV_TOP, 8, 3), + DIV(aclk133, "aclk133", "mout_aclk133", DIV_TOP, 12, 3), + DIV(sclk_slimbus, "sclk_slimbus", "sclk_epll", DIV_PERIL3, 4, 4), + DIV(sclk_pcm1, "sclk_pcm1", "sclk_audio1", DIV_PERIL4, 4, 8), + DIV(sclk_pcm2, "sclk_pcm2", "sclk_audio2", DIV_PERIL4, 20, 8), + DIV(sclk_i2s1, "sclk_i2s1", "sclk_audio1", DIV_PERIL5, 0, 6), + DIV(sclk_i2s2, "sclk_i2s2", "sclk_audio2", DIV_PERIL5, 8, 6), + DIV(none, "div_mmc4", "mout_mmc4", DIV_FSYS3, 0, 4), + DIV(none, "div_mmc_pre4", "div_mmc4", DIV_FSYS3, 8, 8), + DIV(none, "div_uart0", "mout_uart0", DIV_PERIL0, 0, 4), + DIV(none, "div_uart1", "mout_uart1", DIV_PERIL0, 4, 4), + DIV(none, "div_uart2", "mout_uart2", DIV_PERIL0, 8, 4), + DIV(none, "div_uart3", "mout_uart3", DIV_PERIL0, 12, 4), + DIV(none, "div_uart4", "mout_uart4", DIV_PERIL0, 16, 4), + DIV(none, "div_spi0", "mout_spi0", DIV_PERIL1, 0, 4), + DIV(none, "div_spi_pre0", "div_spi0", DIV_PERIL1, 8, 8), + DIV(none, "div_spi1", "mout_spi1", DIV_PERIL1, 16, 4), + DIV(none, "div_spi_pre1", "div_spi1", DIV_PERIL1, 24, 8), + DIV(none, "div_spi2", "mout_spi2", DIV_PERIL2, 0, 4), + DIV(none, "div_spi_pre2", "div_spi2", DIV_PERIL2, 8, 8), + DIV(none, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4), + DIV(none, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4), + DIV_A(arm_clk, "arm_clk", "div_core2", DIV_CPU0, 28, 3, "arm_clk"), + DIV_A(sclk_apll, "sclk_apll", "mout_apll", + DIV_CPU0, 24, 3, "sclk_apll"), + DIV_F(none, "div_mipi_pre0", "div_mipi0", DIV_LCD0, 20, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre0", "div_mmc0", DIV_FSYS1, 8, 8, + CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre1", "div_mmc1", DIV_FSYS1, 24, 8, + CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre2", "div_mmc2", DIV_FSYS2, 8, 8, + CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre3", "div_mmc3", DIV_FSYS2, 24, 8, + CLK_SET_RATE_PARENT, 0), +}; + +/* list of divider clocks supported in exynos4210 soc */ +struct samsung_div_clock exynos4210_div_clks[] __initdata = { + DIV(none, "div_g2d", "mout_g2d", DIV_IMAGE, 0, 4), + DIV(none, "div_fimd1", "mout_fimd1", E4210_DIV_LCD1, 0, 4), + DIV(none, "div_mipi1", "mout_mipi1", E4210_DIV_LCD1, 16, 4), + DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4), + DIV_F(none, "div_mipi_pre1", "div_mipi1", E4210_DIV_LCD1, 20, 4, + CLK_SET_RATE_PARENT, 0), +}; + +/* list of divider clocks supported in exynos4x12 soc */ +struct samsung_div_clock exynos4x12_div_clks[] __initdata = { + DIV(none, "div_mdnie0", "mout_mdnie0", DIV_LCD0, 4, 4), + DIV(none, "div_mdnie_pwm0", "mout_mdnie_pwm0", DIV_LCD0, 8, 4), + DIV(none, "div_mdnie_pwm_pre0", "div_mdnie_pwm0", DIV_LCD0, 12, 4), + DIV(none, "div_mipihsi", "mout_mipihsi", DIV_FSYS0, 20, 4), + DIV(none, "div_jpeg", "mout_jpeg", E4X12_DIV_CAM1, 0, 4), +}; + +/* list of gate clocks supported in all exynos4 soc's */ +struct samsung_gate_clock exynos4_gate_clks[] __initdata = { + /* + * After all Exynos4 based platforms are migrated to use device tree, + * the device name and clock alias names specified below for some + * of the clocks can be removed. + */ + GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi", SRC_MASK_TV, 0, 0, 0), + GATE(sclk_mixer, "sclk_mixer", "mout_mixer", SRC_MASK_TV, 4, 0, 0), + GATE(sclk_dac, "sclk_dac", "mout_dac", SRC_MASK_TV, 8, 0, 0), + GATE(sclk_spdif, "sclk_spdif", "mout_spdif", 0xc354, 8, 0, 0), + GATE(jpeg, "jpeg", "aclk160", GATE_IP_CAM, 6, 0, 0), + GATE(mie0, "mie0", "aclk160", GATE_IP_LCD0, 1, 0, 0), + GATE(dsim0, "dsim0", "aclk160", GATE_IP_LCD0, 3, 0, 0), + GATE(fimd1, "fimd1", "aclk160", GATE_IP_LCD1, 0, 0, 0), + GATE(mie1, "mie1", "aclk160", GATE_IP_LCD1, 1, 0, 0), + GATE(dsim1, "dsim1", "aclk160", GATE_IP_LCD1, 3, 0, 0), + GATE(smmu_fimd1, "smmu_fimd1", "aclk160", GATE_IP_LCD1, 4, 0, 0), + GATE(tsi, "tsi", "aclk133", GATE_IP_FSYS, 4, 0, 0), + GATE(sromc, "sromc", "aclk133", GATE_IP_FSYS, 11, 0, 0), + GATE(g3d, "g3d", "aclk200", GATE_IP_G3D, 0, 0, 0), + GATE(usb_device, "usb_device", "aclk133", GATE_IP_FSYS, 13, 0, 0), + GATE(onenand, "onenand", "aclk133", GATE_IP_FSYS, 15, 0, 0), + GATE(nfcon, "nfcon", "aclk133", GATE_IP_FSYS, 16, 0, 0), + GATE(gps, "gps", "aclk133", GATE_IP_GPS, 0, 0, 0), + GATE(smmu_gps, "smmu_gps", "aclk133", GATE_IP_GPS, 1, 0, 0), + GATE(slimbus, "slimbus", "aclk100", GATE_IP_PERIL, 25, 0, 0), + GATE(sclk_cam0, "sclk_cam0", "div_cam0", GATE_SCLK_CAM, 4, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_cam1, "sclk_cam1", "div_cam1", GATE_SCLK_CAM, 5, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_mipi0, "sclk_mipi0", "div_mipi_pre0", + SRC_MASK_LCD0, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_audio1, "sclk_audio1", "div_audio1", 0xc354, 0, + CLK_SET_RATE_PARENT, 0), + GATE_D(vp, "s5p-mixer", "vp", "aclk160", GATE_IP_TV, 0, 0, 0), + GATE_D(mixer, "s5p-mixer", "mixer", "aclk160", GATE_IP_TV, 1, 0, 0), + GATE_D(hdmi, "exynos4-hdmi", "hdmi", "aclk160", GATE_IP_TV, 3, 0, 0), + GATE_A(pwm, "pwm", "aclk100", GATE_IP_PERIL, 24, 0, 0, "timers"), + GATE_A(sdmmc4, "sdmmc4", "aclk133", GATE_IP_FSYS, 9, 0, 0, "biu"), + GATE_A(usb_host, "usb_host", "aclk133", + GATE_IP_FSYS, 12, 0, 0, "usbhost"), + GATE_DA(sclk_fimc0, "exynos4-fimc.0", "sclk_fimc0", "div_fimc0", + SRC_MASK_CAM, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimc"), + GATE_DA(sclk_fimc1, "exynos4-fimc.1", "sclk_fimc1", "div_fimc1", + SRC_MASK_CAM, 4, CLK_SET_RATE_PARENT, 0, "sclk_fimc"), + GATE_DA(sclk_fimc2, "exynos4-fimc.2", "sclk_fimc2", "div_fimc2", + SRC_MASK_CAM, 8, CLK_SET_RATE_PARENT, 0, "sclk_fimc"), + GATE_DA(sclk_fimc3, "exynos4-fimc.3", "sclk_fimc3", "div_fimc3", + SRC_MASK_CAM, 12, CLK_SET_RATE_PARENT, 0, "sclk_fimc"), + GATE_DA(sclk_csis0, "s5p-mipi-csis.0", "sclk_csis0", "div_csis0", + SRC_MASK_CAM, 24, CLK_SET_RATE_PARENT, 0, "sclk_csis"), + GATE_DA(sclk_csis1, "s5p-mipi-csis.1", "sclk_csis1", "div_csis1", + SRC_MASK_CAM, 28, CLK_SET_RATE_PARENT, 0, "sclk_csis"), + GATE_DA(sclk_fimd0, "exynos4-fb.0", "sclk_fimd0", "div_fimd0", + SRC_MASK_LCD0, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimd"), + GATE_DA(sclk_mmc0, "exynos4-sdhci.0", "sclk_mmc0", "div_mmc_pre0", + SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0, + "mmc_busclk.2"), + GATE_DA(sclk_mmc1, "exynos4-sdhci.1", "sclk_mmc1", "div_mmc_pre1", + SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0, + "mmc_busclk.2"), + GATE_DA(sclk_mmc2, "exynos4-sdhci.2", "sclk_mmc2", "div_mmc_pre2", + SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0, + "mmc_busclk.2"), + GATE_DA(sclk_mmc3, "exynos4-sdhci.3", "sclk_mmc3", "div_mmc_pre3", + SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0, + "mmc_busclk.2"), + GATE_DA(sclk_mmc4, NULL, "sclk_mmc4", "div_mmc_pre4", + SRC_MASK_FSYS, 16, CLK_SET_RATE_PARENT, 0, "ciu"), + GATE_DA(sclk_uart0, "exynos4210-uart.0", "uclk0", "div_uart0", + 0xc350, 0, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), + GATE_DA(sclk_uart1, "exynos4210-uart.1", "uclk1", "div_uart1", + 0xc350, 4, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), + GATE_DA(sclk_uart2, "exynos4210-uart.2", "uclk2", "div_uart2", + 0xc350, 8, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), + GATE_DA(sclk_uart3, "exynos4210-uart.3", "uclk3", "div_uart3", + 0xc350, 12, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), + GATE_DA(sclk_uart4, "exynos4210-uart.4", "uclk4", "div_uart4", + 0xc350, 16, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), + GATE(sclk_audio2, "sclk_audio2", "div_audio2", 0xc354, 4, + CLK_SET_RATE_PARENT, 0), + GATE_DA(sclk_spi0, "exynos4210-spi.0", "sclk_spi0", "div_spi_pre0", + 0xc354, 16, CLK_SET_RATE_PARENT, 0, "spi_busclk0"), + GATE_DA(sclk_spi1, "exynos4210-spi.1", "sclk_spi1", "div_spi_pre1", + 0xc354, 20, CLK_SET_RATE_PARENT, 0, "spi_busclk0"), + GATE_DA(sclk_spi2, "exynos4210-spi.2", "sclk_spi2", "div_spi_pre2", + 0xc354, 24, CLK_SET_RATE_PARENT, 0, "spi_busclk0"), + GATE_DA(fimc0, "exynos4-fimc.0", "fimc0", "aclk160", + GATE_IP_CAM, 0, 0, 0, "fimc"), + GATE_DA(fimc1, "exynos4-fimc.1", "fimc1", "aclk160", + GATE_IP_CAM, 1, 0, 0, "fimc"), + GATE_DA(fimc2, "exynos4-fimc.2", "fimc2", "aclk160", + GATE_IP_CAM, 2, 0, 0, "fimc"), + GATE_DA(fimc3, "exynos4-fimc.3", "fimc3", "aclk160", + GATE_IP_CAM, 3, 0, 0, "fimc"), + GATE_DA(csis0, "s5p-mipi-csis.0", "csis0", "aclk160", + GATE_IP_CAM, 4, 0, 0, "fimc"), + GATE_DA(csis1, "s5p-mipi-csis.1", "csis1", "aclk160", + GATE_IP_CAM, 5, 0, 0, "fimc"), + GATE_DA(smmu_fimc0, "exynos-sysmmu.5", "smmu_fimc0", "aclk160", + GATE_IP_CAM, 7, 0, 0, "sysmmu"), + GATE_DA(smmu_fimc1, "exynos-sysmmu.6", "smmu_fimc1", "aclk160", + GATE_IP_CAM, 8, 0, 0, "sysmmu"), + GATE_DA(smmu_fimc2, "exynos-sysmmu.7", "smmu_fimc2", "aclk160", + GATE_IP_CAM, 9, 0, 0, "sysmmu"), + GATE_DA(smmu_fimc3, "exynos-sysmmu.8", "smmu_fimc3", "aclk160", + GATE_IP_CAM, 10, 0, 0, "sysmmu"), + GATE_DA(smmu_jpeg, "exynos-sysmmu.3", "smmu_jpeg", "aclk160", + GATE_IP_CAM, 11, 0, 0, "sysmmu"), + GATE_DA(smmu_tv, "exynos-sysmmu.2", "smmu_tv", "aclk160", + GATE_IP_TV, 4, 0, 0, "sysmmu"), + GATE_DA(mfc, "s5p-mfc", "mfc", "aclk100", GATE_IP_MFC, 0, 0, 0, "mfc"), + GATE_DA(smmu_mfcl, "exynos-sysmmu.0", "smmu_mfcl", "aclk100", + GATE_IP_MFC, 1, 0, 0, "sysmmu"), + GATE_DA(smmu_mfcr, "exynos-sysmmu.1", "smmu_mfcr", "aclk100", + GATE_IP_MFC, 2, 0, 0, "sysmmu"), + GATE_DA(fimd0, "exynos4-fb.0", "fimd0", "aclk160", + GATE_IP_LCD0, 0, 0, 0, "fimd"), + GATE_DA(smmu_fimd0, "exynos-sysmmu.10", "smmu_fimd0", "aclk160", + GATE_IP_LCD0, 4, 0, 0, "sysmmu"), + GATE_DA(pdma0, "dma-pl330.0", "pdma0", "aclk133", + GATE_IP_FSYS, 0, 0, 0, "dma"), + GATE_DA(pdma1, "dma-pl330.1", "pdma1", "aclk133", + GATE_IP_FSYS, 1, 0, 0, "dma"), + GATE_DA(sdmmc0, "exynos4-sdhci.0", "sdmmc0", "aclk133", + GATE_IP_FSYS, 5, 0, 0, "hsmmc"), + GATE_DA(sdmmc1, "exynos4-sdhci.1", "sdmmc1", "aclk133", + GATE_IP_FSYS, 6, 0, 0, "hsmmc"), + GATE_DA(sdmmc2, "exynos4-sdhci.2", "sdmmc2", "aclk133", + GATE_IP_FSYS, 7, 0, 0, "hsmmc"), + GATE_DA(sdmmc3, "exynos4-sdhci.3", "sdmmc3", "aclk133", + GATE_IP_FSYS, 8, 0, 0, "hsmmc"), + GATE_DA(uart0, "exynos4210-uart.0", "uart0", "aclk100", + GATE_IP_PERIL, 0, 0, 0, "uart"), + GATE_DA(uart1, "exynos4210-uart.1", "uart1", "aclk100", + GATE_IP_PERIL, 1, 0, 0, "uart"), + GATE_DA(uart2, "exynos4210-uart.2", "uart2", "aclk100", + GATE_IP_PERIL, 2, 0, 0, "uart"), + GATE_DA(uart3, "exynos4210-uart.3", "uart3", "aclk100", + GATE_IP_PERIL, 3, 0, 0, "uart"), + GATE_DA(uart4, "exynos4210-uart.4", "uart4", "aclk100", + GATE_IP_PERIL, 4, 0, 0, "uart"), + GATE_DA(i2c0, "s3c2440-i2c.0", "i2c0", "aclk100", + GATE_IP_PERIL, 6, 0, 0, "i2c"), + GATE_DA(i2c1, "s3c2440-i2c.1", "i2c1", "aclk100", + GATE_IP_PERIL, 7, 0, 0, "i2c"), + GATE_DA(i2c2, "s3c2440-i2c.2", "i2c2", "aclk100", + GATE_IP_PERIL, 8, 0, 0, "i2c"), + GATE_DA(i2c3, "s3c2440-i2c.3", "i2c3", "aclk100", + GATE_IP_PERIL, 9, 0, 0, "i2c"), + GATE_DA(i2c4, "s3c2440-i2c.4", "i2c4", "aclk100", + GATE_IP_PERIL, 10, 0, 0, "i2c"), + GATE_DA(i2c5, "s3c2440-i2c.5", "i2c5", "aclk100", + GATE_IP_PERIL, 11, 0, 0, "i2c"), + GATE_DA(i2c6, "s3c2440-i2c.6", "i2c6", "aclk100", + GATE_IP_PERIL, 12, 0, 0, "i2c"), + GATE_DA(i2c7, "s3c2440-i2c.7", "i2c7", "aclk100", + GATE_IP_PERIL, 13, 0, 0, "i2c"), + GATE_DA(i2c_hdmi, "s3c2440-hdmiphy-i2c", "i2c-hdmi", "aclk100", + GATE_IP_PERIL, 14, 0, 0, "i2c"), + GATE_DA(spi0, "exynos4210-spi.0", "spi0", "aclk100", + GATE_IP_PERIL, 16, 0, 0, "spi"), + GATE_DA(spi1, "exynos4210-spi.1", "spi1", "aclk100", + GATE_IP_PERIL, 17, 0, 0, "spi"), + GATE_DA(spi2, "exynos4210-spi.2", "spi2", "aclk100", + GATE_IP_PERIL, 18, 0, 0, "spi"), + GATE_DA(i2s1, "samsung-i2s.1", "i2s1", "aclk100", + GATE_IP_PERIL, 20, 0, 0, "iis"), + GATE_DA(i2s2, "samsung-i2s.2", "i2s2", "aclk100", + GATE_IP_PERIL, 21, 0, 0, "iis"), + GATE_DA(pcm1, "samsung-pcm.1", "pcm1", "aclk100", + GATE_IP_PERIL, 22, 0, 0, "pcm"), + GATE_DA(pcm2, "samsung-pcm.2", "pcm2", "aclk100", + GATE_IP_PERIL, 23, 0, 0, "pcm"), + GATE_DA(spdif, "samsung-spdif", "spdif", "aclk100", + GATE_IP_PERIL, 26, 0, 0, "spdif"), + GATE_DA(ac97, "samsung-ac97", "ac97", "aclk100", + GATE_IP_PERIL, 27, 0, 0, "ac97"), +}; + +/* list of gate clocks supported in exynos4210 soc */ +struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { + GATE(tvenc, "tvenc", "aclk160", GATE_IP_TV, 2, 0, 0), + GATE(g2d, "g2d", "aclk200", E4210_GATE_IP_IMAGE, 0, 0, 0), + GATE(rotator, "rotator", "aclk200", E4210_GATE_IP_IMAGE, 1, 0, 0), + GATE(mdma, "mdma", "aclk200", E4210_GATE_IP_IMAGE, 2, 0, 0), + GATE(smmu_g2d, "smmu_g2d", "aclk200", E4210_GATE_IP_IMAGE, 3, 0, 0), + GATE(smmu_mdma, "smmu_mdma", "aclk200", E4210_GATE_IP_IMAGE, 5, 0, 0), + GATE(pcie_phy, "pcie_phy", "aclk133", GATE_IP_FSYS, 2, 0, 0), + GATE(sata_phy, "sata_phy", "aclk133", GATE_IP_FSYS, 3, 0, 0), + GATE(sata, "sata", "aclk133", GATE_IP_FSYS, 10, 0, 0), + GATE(pcie, "pcie", "aclk133", GATE_IP_FSYS, 14, 0, 0), + GATE(smmu_pcie, "smmu_pcie", "aclk133", GATE_IP_FSYS, 18, 0, 0), + GATE(modemif, "modemif", "aclk100", GATE_IP_PERIL, 28, 0, 0), + GATE(chipid, "chipid", "aclk100", GATE_IP_PERIR, 0, 0, 0), + GATE(sysreg, "sysreg", "aclk100", GATE_IP_PERIR, 0, 0, 0), + GATE(hdmi_cec, "hdmi_cec", "aclk100", GATE_IP_PERIR, 11, 0, 0), + GATE(smmu_rotator, "smmu_rotator", "aclk200", + E4210_GATE_IP_IMAGE, 4, 0, 0), + GATE(sclk_mipi1, "sclk_mipi1", "div_mipi_pre1", + SRC_MASK_LCD1, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_sata, "sclk_sata", "div_sata", + SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), + GATE_A(tsadc, "tsadc", "aclk100", GATE_IP_PERIL, 15, 0, 0, "adc"), + GATE_A(mct, "mct", "aclk100", GATE_IP_PERIR, 13, 0, 0, "mct"), + GATE_A(wdt, "watchdog", "aclk100", GATE_IP_PERIR, 14, 0, 0, "watchdog"), + GATE_A(rtc, "rtc", "aclk100", GATE_IP_PERIR, 15, 0, 0, "rtc"), + GATE_A(keyif, "keyif", "aclk100", GATE_IP_PERIR, 16, 0, 0, "keypad"), + GATE_DA(sclk_fimd1, "exynos4-fb.1", "sclk_fimd1", "div_fimd1", + SRC_MASK_LCD1, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimd"), +}; + +/* list of gate clocks supported in exynos4x12 soc */ +struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { + GATE(audss, "audss", "sclk_epll", E4X12_GATE_IP_MAUDIO, 0, 0, 0), + GATE(mdnie0, "mdnie0", "aclk160", GATE_IP_LCD0, 2, 0, 0), + GATE(rotator, "rotator", "aclk200", E4X12_GATE_IP_IMAGE, 1, 0, 0), + GATE(mdma2, "mdma2", "aclk200", E4X12_GATE_IP_IMAGE, 2, 0, 0), + GATE(smmu_mdma, "smmu_mdma", "aclk200", E4X12_GATE_IP_IMAGE, 5, 0, 0), + GATE(mipi_hsi, "mipi_hsi", "aclk133", GATE_IP_FSYS, 10, 0, 0), + GATE(chipid, "chipid", "aclk100", E4X12_GATE_IP_PERIR, 0, 0, 0), + GATE(sysreg, "sysreg", "aclk100", E4X12_GATE_IP_PERIR, 1, 0, 0), + GATE(hdmi_cec, "hdmi_cec", "aclk100", E4X12_GATE_IP_PERIR, 11, 0, 0), + GATE(sclk_mdnie0, "sclk_mdnie0", "div_mdnie0", + SRC_MASK_LCD0, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mdnie_pwm0, "sclk_mdnie_pwm0", "div_mdnie_pwm_pre0", + SRC_MASK_LCD0, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mipihsi, "sclk_mipihsi", "div_mipihsi", + SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), + GATE(smmu_rotator, "smmu_rotator", "aclk200", + E4X12_GATE_IP_IMAGE, 4, 0, 0), + GATE_A(mct, "mct", "aclk100", E4X12_GATE_IP_PERIR, 13, 0, 0, "mct"), + GATE_A(rtc, "rtc", "aclk100", E4X12_GATE_IP_PERIR, 15, 0, 0, "rtc"), + GATE_A(keyif, "keyif", "aclk100", + E4X12_GATE_IP_PERIR, 16, 0, 0, "keypad"), + GATE_A(wdt, "watchdog", "aclk100", + E4X12_GATE_IP_PERIR, 14, 0, 0, "watchdog"), + GATE_DA(pcm0, "samsung-pcm.0", "pcm0", "aclk100", + E4X12_GATE_IP_MAUDIO, 2, 0, 0, "pcm"), + GATE_DA(i2s0, "samsung-i2s.0", "i2s0", "aclk100", + E4X12_GATE_IP_MAUDIO, 3, 0, 0, "iis"), +}; + +#ifdef CONFIG_OF +static struct of_device_id exynos4_clk_ids[] __initdata = { + { .compatible = "samsung,exynos4210-clock", + .data = (void *)EXYNOS4210, }, + { .compatible = "samsung,exynos4412-clock", + .data = (void *)EXYNOS4X12, }, + { }, +}; +#endif + +/* + * The parent of the fin_pll clock is selected by the XOM[0] bit. This bit + * resides in chipid register space, outside of the clock controller memory + * mapped space. So to determine the parent of fin_pll clock, the chipid + * controller is first remapped and the value of XOM[0] bit is read to + * determine the parent clock. + */ +static void __init exynos4_clk_register_finpll(void) +{ + struct samsung_fixed_rate_clock fclk; + struct device_node *np; + struct clk *clk; + void __iomem *chipid_base = S5P_VA_CHIPID; + unsigned long xom, finpll_f = 24000000; + char *parent_name; + + np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid"); + if (np) + chipid_base = of_iomap(np, 0); + + if (chipid_base) { + xom = readl(chipid_base + 8); + parent_name = xom & 1 ? "xusbxti" : "xxti"; + clk = clk_get(NULL, parent_name); + if (IS_ERR(clk)) { + pr_err("%s: failed to lookup parent clock %s, assuming " + "fin_pll clock frequency is 24MHz\n", __func__, + parent_name); + } else { + finpll_f = clk_get_rate(clk); + } + } else { + pr_err("%s: failed to map chipid registers, assuming " + "fin_pll clock frequency is 24MHz\n", __func__); + } + + fclk.id = fin_pll; + fclk.name = "fin_pll"; + fclk.parent_name = NULL; + fclk.flags = CLK_IS_ROOT; + fclk.fixed_rate = finpll_f; + samsung_clk_register_fixed_rate(&fclk, 1); + + if (np) + iounmap(chipid_base); +} + +/* + * This function allows non-dt platforms to specify the clock speed of the + * xxti and xusbxti clocks. These clocks are then registered with the specified + * clock speed. + */ +void __init exynos4_clk_register_fixed_ext(unsigned long xxti_f, + unsigned long xusbxti_f) +{ + exynos4_fixed_rate_ext_clks[0].fixed_rate = xxti_f; + exynos4_fixed_rate_ext_clks[1].fixed_rate = xusbxti_f; + samsung_clk_register_fixed_rate(exynos4_fixed_rate_ext_clks, + ARRAY_SIZE(exynos4_fixed_rate_ext_clks)); +} + +static __initdata struct of_device_id ext_clk_match[] = { + { .compatible = "samsung,clock-xxti", .data = (void *)0, }, + { .compatible = "samsung,clock-xusbxti", .data = (void *)1, }, + {}, +}; + +/* register exynos4 clocks */ +void __init exynos4_clk_init(struct device_node *np) +{ + void __iomem *reg_base; + struct clk *apll, *mpll, *epll, *vpll; + u32 exynos4_soc; + + if (np) { + const struct of_device_id *match; + match = of_match_node(exynos4_clk_ids, np); + exynos4_soc = (u32)match->data; + + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } else { + reg_base = S5P_VA_CMU; + if (soc_is_exynos4210()) + exynos4_soc = EXYNOS4210; + else if (soc_is_exynos4212() || soc_is_exynos4412()) + exynos4_soc = EXYNOS4X12; + else + panic("%s: unable to determine soc\n", __func__); + } + + samsung_clk_init(np, reg_base, nr_clks, + exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs)); + + if (np) + samsung_clk_of_register_fixed_ext(exynos4_fixed_rate_ext_clks, + ARRAY_SIZE(exynos4_fixed_rate_ext_clks), + ext_clk_match); + + exynos4_clk_register_finpll(); + + if (exynos4_soc == EXYNOS4210) { + apll = samsung_clk_register_pll45xx("fout_apll", "fin_pll", + reg_base + APLL_CON0, pll_4508); + mpll = samsung_clk_register_pll45xx("fout_mpll", "fin_pll", + reg_base + E4210_MPLL_CON0, pll_4508); + epll = samsung_clk_register_pll46xx("fout_epll", "fin_pll", + reg_base + 0xc110, pll_4600); + vpll = samsung_clk_register_pll46xx("fout_vpll", "mout_vpllsrc", + reg_base + 0xc120, pll_4650c); + } else { + apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll", + reg_base + APLL_CON0); + mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll", + reg_base + E4X12_MPLL_CON0); + epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll", + reg_base + 0xc110); + vpll = samsung_clk_register_pll36xx("fout_vpll", "fin_pll", + reg_base + 0xc120); + } + + samsung_clk_add_lookup(apll, fout_apll); + samsung_clk_add_lookup(mpll, fout_mpll); + samsung_clk_add_lookup(epll, fout_epll); + samsung_clk_add_lookup(vpll, fout_vpll); + + samsung_clk_register_fixed_rate(exynos4_fixed_rate_clks, + ARRAY_SIZE(exynos4_fixed_rate_clks)); + samsung_clk_register_mux(exynos4_mux_clks, + ARRAY_SIZE(exynos4_mux_clks)); + samsung_clk_register_div(exynos4_div_clks, + ARRAY_SIZE(exynos4_div_clks)); + samsung_clk_register_gate(exynos4_gate_clks, + ARRAY_SIZE(exynos4_gate_clks)); + + if (exynos4_soc == EXYNOS4210) { + samsung_clk_register_fixed_rate(exynos4210_fixed_rate_clks, + ARRAY_SIZE(exynos4210_fixed_rate_clks)); + samsung_clk_register_mux(exynos4210_mux_clks, + ARRAY_SIZE(exynos4210_mux_clks)); + samsung_clk_register_div(exynos4210_div_clks, + ARRAY_SIZE(exynos4210_div_clks)); + samsung_clk_register_gate(exynos4210_gate_clks, + ARRAY_SIZE(exynos4210_gate_clks)); + } else { + samsung_clk_register_mux(exynos4x12_mux_clks, + ARRAY_SIZE(exynos4x12_mux_clks)); + samsung_clk_register_div(exynos4x12_div_clks, + ARRAY_SIZE(exynos4x12_div_clks)); + samsung_clk_register_gate(exynos4x12_gate_clks, + ARRAY_SIZE(exynos4x12_gate_clks)); + } + + pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n" + "\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n", + exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12", + _get_rate("sclk_apll"), _get_rate("sclk_mpll"), + _get_rate("sclk_epll"), _get_rate("sclk_vpll"), + _get_rate("arm_clk")); +} +CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4_clk_init); +CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4_clk_init); -- cgit v1.1 From 6e3ad26816b7281ce3b51296180aeba5d1528d1c Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 9 Mar 2013 17:02:57 +0900 Subject: clk: exynos5250: register clocks using common clock framework The Exynos5250 clocks are statically listed and registered using the Samsung specific common clock helper functions. Both device tree based clock lookup and clkdev based clock lookups are supported. Signed-off-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-exynos5250.c | 514 +++++++++++++++++++++++++++++++++++ 2 files changed, 515 insertions(+) create mode 100644 drivers/clk/samsung/clk-exynos5250.c (limited to 'drivers') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 8862f0d..f18fb07 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o +obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c new file mode 100644 index 0000000..1152125 --- /dev/null +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham + * + * 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 Clock Framework support for Exynos5250 SoC. +*/ + +#include +#include +#include +#include +#include + +#include +#include "clk.h" +#include "clk-pll.h" + +#define SRC_CPU 0x200 +#define DIV_CPU0 0x500 +#define SRC_CORE1 0x4204 +#define SRC_TOP0 0x10210 +#define SRC_TOP2 0x10218 +#define SRC_GSCL 0x10220 +#define SRC_DISP1_0 0x1022c +#define SRC_MAU 0x10240 +#define SRC_FSYS 0x10244 +#define SRC_GEN 0x10248 +#define SRC_PERIC0 0x10250 +#define SRC_PERIC1 0x10254 +#define SRC_MASK_GSCL 0x10320 +#define SRC_MASK_DISP1_0 0x1032c +#define SRC_MASK_MAU 0x10334 +#define SRC_MASK_FSYS 0x10340 +#define SRC_MASK_GEN 0x10344 +#define SRC_MASK_PERIC0 0x10350 +#define SRC_MASK_PERIC1 0x10354 +#define DIV_TOP0 0x10510 +#define DIV_TOP1 0x10514 +#define DIV_GSCL 0x10520 +#define DIV_DISP1_0 0x1052c +#define DIV_GEN 0x1053c +#define DIV_MAU 0x10544 +#define DIV_FSYS0 0x10548 +#define DIV_FSYS1 0x1054c +#define DIV_FSYS2 0x10550 +#define DIV_PERIC0 0x10558 +#define DIV_PERIC1 0x1055c +#define DIV_PERIC2 0x10560 +#define DIV_PERIC3 0x10564 +#define DIV_PERIC4 0x10568 +#define DIV_PERIC5 0x1056c +#define GATE_IP_GSCL 0x10920 +#define GATE_IP_MFC 0x1092c +#define GATE_IP_GEN 0x10934 +#define GATE_IP_FSYS 0x10944 +#define GATE_IP_PERIC 0x10950 +#define GATE_IP_PERIS 0x10960 +#define SRC_CDREX 0x20200 +#define PLL_DIV2_SEL 0x20a24 + +/* + * Let each supported clock get a unique id. This id is used to lookup the clock + * for device tree based platforms. The clocks are categorized into three + * sections: core, sclk gate and bus interface gate clocks. + * + * When adding a new clock to this list, it is advised to choose a clock + * category and add it to the end of that category. That is because the the + * device tree source file is referring to these ids and any change in the + * sequence number of existing clocks will require corresponding change in the + * device tree files. This limitation would go away when pre-processor support + * for dtc would be available. + */ +enum exynos5250_clks { + none, + + /* core clocks */ + fin_pll, + + /* gate for special clocks (sclk) */ + sclk_cam_bayer = 128, sclk_cam0, sclk_cam1, sclk_gscl_wa, sclk_gscl_wb, + sclk_fimd1, sclk_mipi1, sclk_dp, sclk_hdmi, sclk_pixel, sclk_audio0, + sclk_mmc0, sclk_mmc1, sclk_mmc2, sclk_mmc3, sclk_sata, sclk_usb3, + sclk_jpeg, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_pwm, + sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2, + + /* gate clocks */ + gscl0 = 256, gscl1, gscl2, gscl3, gscl_wa, gscl_wb, smmu_gscl0, + smmu_gscl1, smmu_gscl2, smmu_gscl3, mfc, smmu_mfcl, smmu_mfcr, rotator, + jpeg, mdma1, smmu_rotator, smmu_jpeg, smmu_mdma1, pdma0, pdma1, sata, + usbotg, mipi_hsi, sdmmc0, sdmmc1, sdmmc2, sdmmc3, sromc, usb2, usb3, + sata_phyctrl, sata_phyi2c, uart0, uart1, uart2, uart3, uart4, i2c0, + i2c1, i2c2, i2c3, i2c4, i2c5, i2c6, i2c7, i2c_hdmi, adc, spi0, spi1, + spi2, i2s1, i2s2, pcm1, pcm2, pwm, spdif, ac97, hsi2c0, hsi2c1, hsi2c2, + hsi2c3, chipid, sysreg, pmu, cmu_top, cmu_core, cmu_mem, tzpc0, tzpc1, + tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, tzpc8, tzpc9, hdmi_cec, mct, + wdt, rtc, tmu, + + nr_clks, +}; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static __initdata unsigned long exynos5250_clk_regs[] = { + SRC_CPU, + DIV_CPU0, + SRC_CORE1, + SRC_TOP0, + SRC_TOP2, + SRC_GSCL, + SRC_DISP1_0, + SRC_MAU, + SRC_FSYS, + SRC_GEN, + SRC_PERIC0, + SRC_PERIC1, + SRC_MASK_GSCL, + SRC_MASK_DISP1_0, + SRC_MASK_MAU, + SRC_MASK_FSYS, + SRC_MASK_GEN, + SRC_MASK_PERIC0, + SRC_MASK_PERIC1, + DIV_TOP0, + DIV_TOP1, + DIV_GSCL, + DIV_DISP1_0, + DIV_GEN, + DIV_MAU, + DIV_FSYS0, + DIV_FSYS1, + DIV_FSYS2, + DIV_PERIC0, + DIV_PERIC1, + DIV_PERIC2, + DIV_PERIC3, + DIV_PERIC4, + DIV_PERIC5, + GATE_IP_GSCL, + GATE_IP_MFC, + GATE_IP_GEN, + GATE_IP_FSYS, + GATE_IP_PERIC, + GATE_IP_PERIS, + SRC_CDREX, + PLL_DIV2_SEL, +}; + +/* list of all parent clock list */ +PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; +PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", }; +PNAME(mout_mpll_fout_p) = { "fout_mplldiv2", "fout_mpll" }; +PNAME(mout_mpll_p) = { "fin_pll", "mout_mpll_fout" }; +PNAME(mout_bpll_fout_p) = { "fout_bplldiv2", "fout_bpll" }; +PNAME(mout_bpll_p) = { "fin_pll", "mout_bpll_fout" }; +PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi27m" }; +PNAME(mout_vpll_p) = { "mout_vpllsrc", "fout_vpll" }; +PNAME(mout_cpll_p) = { "fin_pll", "fout_cpll" }; +PNAME(mout_epll_p) = { "fin_pll", "fout_epll" }; +PNAME(mout_mpll_user_p) = { "fin_pll", "sclk_mpll" }; +PNAME(mout_bpll_user_p) = { "fin_pll", "sclk_bpll" }; +PNAME(mout_aclk166_p) = { "sclk_cpll", "sclk_mpll_user" }; +PNAME(mout_aclk200_p) = { "sclk_mpll_user", "sclk_bpll_user" }; +PNAME(mout_hdmi_p) = { "div_hdmi_pixel", "sclk_hdmiphy" }; +PNAME(mout_usb3_p) = { "sclk_mpll_user", "sclk_cpll" }; +PNAME(mout_group1_p) = { "fin_pll", "fin_pll", "sclk_hdmi27m", + "sclk_dptxphy", "sclk_uhostphy", "sclk_hdmiphy", + "sclk_mpll_user", "sclk_epll", "sclk_vpll", + "sclk_cpll" }; +PNAME(mout_audio0_p) = { "cdclk0", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy", + "sclk_uhostphy", "sclk_hdmiphy", + "sclk_mpll_user", "sclk_epll", "sclk_vpll", + "sclk_cpll" }; +PNAME(mout_audio1_p) = { "cdclk1", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy", + "sclk_uhostphy", "sclk_hdmiphy", + "sclk_mpll_user", "sclk_epll", "sclk_vpll", + "sclk_cpll" }; +PNAME(mout_audio2_p) = { "cdclk2", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy", + "sclk_uhostphy", "sclk_hdmiphy", + "sclk_mpll_user", "sclk_epll", "sclk_vpll", + "sclk_cpll" }; +PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2", + "spdif_extclk" }; + +/* fixed rate clocks generated outside the soc */ +struct samsung_fixed_rate_clock exynos5250_fixed_rate_ext_clks[] __initdata = { + FRATE(fin_pll, "fin_pll", NULL, CLK_IS_ROOT, 0), +}; + +/* fixed rate clocks generated inside the soc */ +struct samsung_fixed_rate_clock exynos5250_fixed_rate_clks[] __initdata = { + FRATE(none, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000), + FRATE(none, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000), + FRATE(none, "sclk_dptxphy", NULL, CLK_IS_ROOT, 24000000), + FRATE(none, "sclk_uhostphy", NULL, CLK_IS_ROOT, 48000000), +}; + +struct samsung_fixed_factor_clock exynos5250_fixed_factor_clks[] __initdata = { + FFACTOR(none, "fout_mplldiv2", "fout_mpll", 1, 2, 0), + FFACTOR(none, "fout_bplldiv2", "fout_bpll", 1, 2, 0), +}; + +struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { + MUX(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), + MUX(none, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1), + MUX(none, "mout_mpll_fout", mout_mpll_fout_p, PLL_DIV2_SEL, 4, 1), + MUX(none, "sclk_mpll", mout_mpll_p, SRC_CORE1, 8, 1), + MUX(none, "mout_bpll_fout", mout_bpll_fout_p, PLL_DIV2_SEL, 0, 1), + MUX(none, "sclk_bpll", mout_bpll_p, SRC_CDREX, 0, 1), + MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1), + MUX(none, "sclk_vpll", mout_vpll_p, SRC_TOP2, 16, 1), + MUX(none, "sclk_epll", mout_epll_p, SRC_TOP2, 12, 1), + MUX(none, "sclk_cpll", mout_cpll_p, SRC_TOP2, 8, 1), + MUX(none, "sclk_mpll_user", mout_mpll_user_p, SRC_TOP2, 20, 1), + MUX(none, "sclk_bpll_user", mout_bpll_user_p, SRC_TOP2, 24, 1), + MUX(none, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1), + MUX(none, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1), + MUX(none, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1), + MUX(none, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4), + MUX(none, "mout_cam0", mout_group1_p, SRC_GSCL, 16, 4), + MUX(none, "mout_cam1", mout_group1_p, SRC_GSCL, 20, 4), + MUX(none, "mout_gscl_wa", mout_group1_p, SRC_GSCL, 24, 4), + MUX(none, "mout_gscl_wb", mout_group1_p, SRC_GSCL, 28, 4), + MUX(none, "mout_fimd1", mout_group1_p, SRC_DISP1_0, 0, 4), + MUX(none, "mout_mipi1", mout_group1_p, SRC_DISP1_0, 12, 4), + MUX(none, "mout_dp", mout_group1_p, SRC_DISP1_0, 16, 4), + MUX(none, "mout_hdmi", mout_hdmi_p, SRC_DISP1_0, 20, 1), + MUX(none, "mout_audio0", mout_audio0_p, SRC_MAU, 0, 4), + MUX(none, "mout_mmc0", mout_group1_p, SRC_FSYS, 0, 4), + MUX(none, "mout_mmc1", mout_group1_p, SRC_FSYS, 4, 4), + MUX(none, "mout_mmc2", mout_group1_p, SRC_FSYS, 8, 4), + MUX(none, "mout_mmc3", mout_group1_p, SRC_FSYS, 12, 4), + MUX(none, "mout_sata", mout_aclk200_p, SRC_FSYS, 24, 1), + MUX(none, "mout_usb3", mout_usb3_p, SRC_FSYS, 28, 1), + MUX(none, "mout_jpeg", mout_group1_p, SRC_GEN, 0, 4), + MUX(none, "mout_uart0", mout_group1_p, SRC_PERIC0, 0, 4), + MUX(none, "mout_uart1", mout_group1_p, SRC_PERIC0, 4, 4), + MUX(none, "mout_uart2", mout_group1_p, SRC_PERIC0, 8, 4), + MUX(none, "mout_uart3", mout_group1_p, SRC_PERIC0, 12, 4), + MUX(none, "mout_pwm", mout_group1_p, SRC_PERIC0, 24, 4), + MUX(none, "mout_audio1", mout_audio1_p, SRC_PERIC1, 0, 4), + MUX(none, "mout_audio2", mout_audio2_p, SRC_PERIC1, 4, 4), + MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIC1, 8, 2), + MUX(none, "mout_spi0", mout_group1_p, SRC_PERIC1, 16, 4), + MUX(none, "mout_spi1", mout_group1_p, SRC_PERIC1, 20, 4), + MUX(none, "mout_spi2", mout_group1_p, SRC_PERIC1, 24, 4), +}; + +struct samsung_div_clock exynos5250_div_clks[] __initdata = { + DIV(none, "div_arm", "mout_cpu", DIV_CPU0, 0, 3), + DIV(none, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3), + DIV(none, "aclk66_pre", "sclk_mpll_user", DIV_TOP1, 24, 3), + DIV(none, "aclk66", "aclk66_pre", DIV_TOP0, 0, 3), + DIV(none, "aclk266", "sclk_mpll_user", DIV_TOP0, 16, 3), + DIV(none, "aclk166", "mout_aclk166", DIV_TOP0, 8, 3), + DIV(none, "aclk333", "mout_aclk333", DIV_TOP0, 20, 3), + DIV(none, "aclk200", "mout_aclk200", DIV_TOP0, 12, 3), + DIV(none, "div_cam_bayer", "mout_cam_bayer", DIV_GSCL, 12, 4), + DIV(none, "div_cam0", "mout_cam0", DIV_GSCL, 16, 4), + DIV(none, "div_cam1", "mout_cam1", DIV_GSCL, 20, 4), + DIV(none, "div_gscl_wa", "mout_gscl_wa", DIV_GSCL, 24, 4), + DIV(none, "div_gscl_wb", "mout_gscl_wb", DIV_GSCL, 28, 4), + DIV(none, "div_fimd1", "mout_fimd1", DIV_DISP1_0, 0, 4), + DIV(none, "div_mipi1", "mout_mipi1", DIV_DISP1_0, 16, 4), + DIV(none, "div_dp", "mout_dp", DIV_DISP1_0, 24, 4), + DIV(none, "div_jpeg", "mout_jpeg", DIV_GEN, 4, 4), + DIV(none, "div_audio0", "mout_audio0", DIV_MAU, 0, 4), + DIV(none, "div_pcm0", "sclk_audio0", DIV_MAU, 4, 8), + DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4), + DIV(none, "div_usb3", "mout_usb3", DIV_FSYS0, 24, 4), + DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4), + DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4), + DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4), + DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4), + DIV(none, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4), + DIV(none, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4), + DIV(none, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4), + DIV(none, "div_uart3", "mout_uart3", DIV_PERIC0, 12, 4), + DIV(none, "div_spi0", "mout_spi0", DIV_PERIC1, 0, 4), + DIV(none, "div_spi1", "mout_spi1", DIV_PERIC1, 16, 4), + DIV(none, "div_spi2", "mout_spi2", DIV_PERIC2, 0, 4), + DIV(none, "div_pwm", "mout_pwm", DIV_PERIC3, 0, 4), + DIV(none, "div_audio1", "mout_audio1", DIV_PERIC4, 0, 4), + DIV(none, "div_pcm1", "sclk_audio1", DIV_PERIC4, 4, 8), + DIV(none, "div_audio2", "mout_audio2", DIV_PERIC4, 16, 4), + DIV(none, "div_pcm2", "sclk_audio2", DIV_PERIC4, 20, 8), + DIV(none, "div_i2s1", "sclk_audio1", DIV_PERIC5, 0, 6), + DIV(none, "div_i2s2", "sclk_audio2", DIV_PERIC5, 8, 6), + DIV(sclk_pixel, "div_hdmi_pixel", "sclk_vpll", DIV_DISP1_0, 28, 4), + DIV_A(none, "armclk", "div_arm", DIV_CPU0, 28, 3, "armclk"), + DIV_F(none, "div_mipi1_pre", "div_mipi1", + DIV_DISP1_0, 20, 4, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre0", "div_mmc0", + DIV_FSYS1, 8, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre1", "div_mmc1", + DIV_FSYS1, 24, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre2", "div_mmc2", + DIV_FSYS2, 8, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre3", "div_mmc3", + DIV_FSYS2, 24, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_spi_pre0", "div_spi0", + DIV_PERIC1, 8, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_spi_pre1", "div_spi1", + DIV_PERIC1, 24, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_spi_pre2", "div_spi2", + DIV_PERIC2, 8, 8, CLK_SET_RATE_PARENT, 0), +}; + +struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { + GATE(gscl0, "gscl0", "none", GATE_IP_GSCL, 0, 0, 0), + GATE(gscl1, "gscl1", "none", GATE_IP_GSCL, 1, 0, 0), + GATE(gscl2, "gscl2", "aclk266", GATE_IP_GSCL, 2, 0, 0), + GATE(gscl3, "gscl3", "aclk266", GATE_IP_GSCL, 3, 0, 0), + GATE(gscl_wa, "gscl_wa", "div_gscl_wa", GATE_IP_GSCL, 5, 0, 0), + GATE(gscl_wb, "gscl_wb", "div_gscl_wb", GATE_IP_GSCL, 6, 0, 0), + GATE(smmu_gscl0, "smmu_gscl0", "aclk266", GATE_IP_GSCL, 7, 0, 0), + GATE(smmu_gscl1, "smmu_gscl1", "aclk266", GATE_IP_GSCL, 8, 0, 0), + GATE(smmu_gscl2, "smmu_gscl2", "aclk266", GATE_IP_GSCL, 9, 0, 0), + GATE(smmu_gscl3, "smmu_gscl3", "aclk266", GATE_IP_GSCL, 10, 0, 0), + GATE(mfc, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0), + GATE(smmu_mfcl, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0), + GATE(smmu_mfcr, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0), + GATE(rotator, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0), + GATE(jpeg, "jpeg", "aclk166", GATE_IP_GEN, 2, 0, 0), + GATE(mdma1, "mdma1", "aclk266", GATE_IP_GEN, 4, 0, 0), + GATE(smmu_rotator, "smmu_rotator", "aclk266", GATE_IP_GEN, 6, 0, 0), + GATE(smmu_jpeg, "smmu_jpeg", "aclk166", GATE_IP_GEN, 7, 0, 0), + GATE(smmu_mdma1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0), + GATE(pdma0, "pdma0", "aclk200", GATE_IP_FSYS, 1, 0, 0), + GATE(pdma1, "pdma1", "aclk200", GATE_IP_FSYS, 2, 0, 0), + GATE(sata, "sata", "aclk200", GATE_IP_FSYS, 6, 0, 0), + GATE(usbotg, "usbotg", "aclk200", GATE_IP_FSYS, 7, 0, 0), + GATE(mipi_hsi, "mipi_hsi", "aclk200", GATE_IP_FSYS, 8, 0, 0), + GATE(sdmmc0, "sdmmc0", "aclk200", GATE_IP_FSYS, 12, 0, 0), + GATE(sdmmc1, "sdmmc1", "aclk200", GATE_IP_FSYS, 13, 0, 0), + GATE(sdmmc2, "sdmmc2", "aclk200", GATE_IP_FSYS, 14, 0, 0), + GATE(sdmmc3, "sdmmc3", "aclk200", GATE_IP_FSYS, 15, 0, 0), + GATE(sromc, "sromc", "aclk200", GATE_IP_FSYS, 17, 0, 0), + GATE(usb2, "usb2", "aclk200", GATE_IP_FSYS, 18, 0, 0), + GATE(usb3, "usb3", "aclk200", GATE_IP_FSYS, 19, 0, 0), + GATE(sata_phyctrl, "sata_phyctrl", "aclk200", GATE_IP_FSYS, 24, 0, 0), + GATE(sata_phyi2c, "sata_phyi2c", "aclk200", GATE_IP_FSYS, 25, 0, 0), + GATE(uart0, "uart0", "aclk66", GATE_IP_PERIC, 0, 0, 0), + GATE(uart1, "uart1", "aclk66", GATE_IP_PERIC, 1, 0, 0), + GATE(uart2, "uart2", "aclk66", GATE_IP_PERIC, 2, 0, 0), + GATE(uart3, "uart3", "aclk66", GATE_IP_PERIC, 3, 0, 0), + GATE(uart4, "uart4", "aclk66", GATE_IP_PERIC, 4, 0, 0), + GATE(i2c0, "i2c0", "aclk66", GATE_IP_PERIC, 6, 0, 0), + GATE(i2c1, "i2c1", "aclk66", GATE_IP_PERIC, 7, 0, 0), + GATE(i2c2, "i2c2", "aclk66", GATE_IP_PERIC, 8, 0, 0), + GATE(i2c3, "i2c3", "aclk66", GATE_IP_PERIC, 9, 0, 0), + GATE(i2c4, "i2c4", "aclk66", GATE_IP_PERIC, 10, 0, 0), + GATE(i2c5, "i2c5", "aclk66", GATE_IP_PERIC, 11, 0, 0), + GATE(i2c6, "i2c6", "aclk66", GATE_IP_PERIC, 12, 0, 0), + GATE(i2c7, "i2c7", "aclk66", GATE_IP_PERIC, 13, 0, 0), + GATE(i2c_hdmi, "i2c_hdmi", "aclk66", GATE_IP_PERIC, 14, 0, 0), + GATE(adc, "adc", "aclk66", GATE_IP_PERIC, 15, 0, 0), + GATE(spi0, "spi0", "aclk66", GATE_IP_PERIC, 16, 0, 0), + GATE(spi1, "spi1", "aclk66", GATE_IP_PERIC, 17, 0, 0), + GATE(spi2, "spi2", "aclk66", GATE_IP_PERIC, 18, 0, 0), + GATE(i2s1, "i2s1", "aclk66", GATE_IP_PERIC, 20, 0, 0), + GATE(i2s2, "i2s2", "aclk66", GATE_IP_PERIC, 21, 0, 0), + GATE(pcm1, "pcm1", "aclk66", GATE_IP_PERIC, 22, 0, 0), + GATE(pcm2, "pcm2", "aclk66", GATE_IP_PERIC, 23, 0, 0), + GATE(pwm, "pwm", "aclk66", GATE_IP_PERIC, 24, 0, 0), + GATE(spdif, "spdif", "aclk66", GATE_IP_PERIC, 26, 0, 0), + GATE(ac97, "ac97", "aclk66", GATE_IP_PERIC, 27, 0, 0), + GATE(hsi2c0, "hsi2c0", "aclk66", GATE_IP_PERIC, 28, 0, 0), + GATE(hsi2c1, "hsi2c1", "aclk66", GATE_IP_PERIC, 29, 0, 0), + GATE(hsi2c2, "hsi2c2", "aclk66", GATE_IP_PERIC, 30, 0, 0), + GATE(hsi2c3, "hsi2c3", "aclk66", GATE_IP_PERIC, 31, 0, 0), + GATE(chipid, "chipid", "aclk66", GATE_IP_PERIS, 0, 0, 0), + GATE(sysreg, "sysreg", "aclk66", GATE_IP_PERIS, 1, 0, 0), + GATE(pmu, "pmu", "aclk66", GATE_IP_PERIS, 2, 0, 0), + GATE(tzpc0, "tzpc0", "aclk66", GATE_IP_PERIS, 6, 0, 0), + GATE(tzpc1, "tzpc1", "aclk66", GATE_IP_PERIS, 7, 0, 0), + GATE(tzpc2, "tzpc2", "aclk66", GATE_IP_PERIS, 8, 0, 0), + GATE(tzpc3, "tzpc3", "aclk66", GATE_IP_PERIS, 9, 0, 0), + GATE(tzpc4, "tzpc4", "aclk66", GATE_IP_PERIS, 10, 0, 0), + GATE(tzpc5, "tzpc5", "aclk66", GATE_IP_PERIS, 11, 0, 0), + GATE(tzpc6, "tzpc6", "aclk66", GATE_IP_PERIS, 12, 0, 0), + GATE(tzpc7, "tzpc7", "aclk66", GATE_IP_PERIS, 13, 0, 0), + GATE(tzpc8, "tzpc8", "aclk66", GATE_IP_PERIS, 14, 0, 0), + GATE(tzpc9, "tzpc9", "aclk66", GATE_IP_PERIS, 15, 0, 0), + GATE(hdmi_cec, "hdmi_cec", "aclk66", GATE_IP_PERIS, 16, 0, 0), + GATE(mct, "mct", "aclk66", GATE_IP_PERIS, 18, 0, 0), + GATE(wdt, "wdt", "aclk66", GATE_IP_PERIS, 19, 0, 0), + GATE(rtc, "rtc", "aclk66", GATE_IP_PERIS, 20, 0, 0), + GATE(tmu, "tmu", "aclk66", GATE_IP_PERIS, 21, 0, 0), + GATE(cmu_top, "cmu_top", "aclk66", + GATE_IP_PERIS, 3, CLK_IGNORE_UNUSED, 0), + GATE(cmu_core, "cmu_core", "aclk66", + GATE_IP_PERIS, 4, CLK_IGNORE_UNUSED, 0), + GATE(cmu_mem, "cmu_mem", "aclk66", + GATE_IP_PERIS, 5, CLK_IGNORE_UNUSED, 0), + GATE(sclk_cam_bayer, "sclk_cam_bayer", "div_cam_bayer", + SRC_MASK_GSCL, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_cam0, "sclk_cam0", "div_cam0", + SRC_MASK_GSCL, 16, CLK_SET_RATE_PARENT, 0), + GATE(sclk_cam1, "sclk_cam1", "div_cam1", + SRC_MASK_GSCL, 20, CLK_SET_RATE_PARENT, 0), + GATE(sclk_gscl_wa, "sclk_gscl_wa", "div_gscl_wa", + SRC_MASK_GSCL, 24, CLK_SET_RATE_PARENT, 0), + GATE(sclk_gscl_wb, "sclk_gscl_wb", "div_gscl_wb", + SRC_MASK_GSCL, 28, CLK_SET_RATE_PARENT, 0), + GATE(sclk_fimd1, "sclk_fimd1", "div_fimd1", + SRC_MASK_DISP1_0, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mipi1, "sclk_mipi1", "div_mipi1", + SRC_MASK_DISP1_0, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_dp, "sclk_dp", "div_dp", + SRC_MASK_DISP1_0, 16, CLK_SET_RATE_PARENT, 0), + GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi", + SRC_MASK_DISP1_0, 20, 0, 0), + GATE(sclk_audio0, "sclk_audio0", "div_audio0", + SRC_MASK_MAU, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc0, "sclk_mmc0", "div_mmc0", + SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc1, "sclk_mmc1", "div_mmc1", + SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc2, "sclk_mmc2", "div_mmc2", + SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc3, "sclk_mmc3", "div_mmc3", + SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_sata, "sclk_sata", "div_sata", + SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), + GATE(sclk_usb3, "sclk_usb3", "div_usb3", + SRC_MASK_FSYS, 28, CLK_SET_RATE_PARENT, 0), + GATE(sclk_jpeg, "sclk_jpeg", "div_jpeg", + SRC_MASK_GEN, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart0, "sclk_uart0", "div_uart0", + SRC_MASK_PERIC0, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart1, "sclk_uart1", "div_uart1", + SRC_MASK_PERIC0, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart2, "sclk_uart2", "div_uart2", + SRC_MASK_PERIC0, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart3, "sclk_uart3", "div_uart3", + SRC_MASK_PERIC0, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_pwm, "sclk_pwm", "div_pwm", + SRC_MASK_PERIC0, 24, CLK_SET_RATE_PARENT, 0), + GATE(sclk_audio1, "sclk_audio1", "div_audio1", + SRC_MASK_PERIC1, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_audio2, "sclk_audio2", "div_audio2", + SRC_MASK_PERIC1, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spdif, "sclk_spdif", "mout_spdif", + SRC_MASK_PERIC1, 4, 0, 0), + GATE(sclk_spi0, "sclk_spi0", "div_spi_pre0", + SRC_MASK_PERIC1, 16, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi1, "sclk_spi1", "div_spi_pre1", + SRC_MASK_PERIC1, 20, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi2, "sclk_spi2", "div_spi_pre2", + SRC_MASK_PERIC1, 24, CLK_SET_RATE_PARENT, 0), +}; + +static __initdata struct of_device_id ext_clk_match[] = { + { .compatible = "samsung,clock-xxti", .data = (void *)0, }, + { }, +}; + +/* register exynox5250 clocks */ +void __init exynos5250_clk_init(struct device_node *np) +{ + void __iomem *reg_base; + struct clk *apll, *mpll, *epll, *vpll, *bpll, *gpll, *cpll; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } else { + panic("%s: unable to determine soc\n", __func__); + } + + samsung_clk_init(np, reg_base, nr_clks, + exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs)); + samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks, + ARRAY_SIZE(exynos5250_fixed_rate_ext_clks), + ext_clk_match); + + apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll", + reg_base + 0x100); + mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll", + reg_base + 0x4100); + bpll = samsung_clk_register_pll35xx("fout_bpll", "fin_pll", + reg_base + 0x20110); + gpll = samsung_clk_register_pll35xx("fout_gpll", "fin_pll", + reg_base + 0x10150); + cpll = samsung_clk_register_pll35xx("fout_cpll", "fin_pll", + reg_base + 0x10120); + epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll", + reg_base + 0x10130); + vpll = samsung_clk_register_pll36xx("fout_vpll", "mout_vpllsrc", + reg_base + 0x10140); + + samsung_clk_register_fixed_rate(exynos5250_fixed_rate_clks, + ARRAY_SIZE(exynos5250_fixed_rate_clks)); + samsung_clk_register_fixed_factor(exynos5250_fixed_factor_clks, + ARRAY_SIZE(exynos5250_fixed_factor_clks)); + samsung_clk_register_mux(exynos5250_mux_clks, + ARRAY_SIZE(exynos5250_mux_clks)); + samsung_clk_register_div(exynos5250_div_clks, + ARRAY_SIZE(exynos5250_div_clks)); + samsung_clk_register_gate(exynos5250_gate_clks, + ARRAY_SIZE(exynos5250_gate_clks)); + + pr_info("Exynos5250: clock setup completed, armclk=%ld\n", + _get_rate("armclk")); +} +CLK_OF_DECLARE(exynos5250_clk, "samsung,exynos5250-clock", exynos5250_clk_init); -- cgit v1.1 From f2585b1cce24d7bc5b4a1de582bf81e43813f840 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 9 Mar 2013 17:03:01 +0900 Subject: clk: exynos5440: register clocks using common clock framework The Exynos5440 clocks are statically listed and registered using the Samsung specific common clock helper functions. Signed-off-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-exynos5440.c | 139 +++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 drivers/clk/samsung/clk-exynos5440.c (limited to 'drivers') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index f18fb07..b7c232e 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o +obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c new file mode 100644 index 0000000..d588e93 --- /dev/null +++ b/drivers/clk/samsung/clk-exynos5440.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Thomas Abraham + * + * 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 Clock Framework support for Exynos5440 SoC. +*/ + +#include +#include +#include +#include +#include + +#include +#include "clk.h" +#include "clk-pll.h" + +#define CLKEN_OV_VAL 0xf8 +#define CPU_CLK_STATUS 0xfc +#define MISC_DOUT1 0x558 + +/* + * Let each supported clock get a unique id. This id is used to lookup the clock + * for device tree based platforms. + */ +enum exynos5440_clks { + none, xtal, arm_clk, + + spi_baud = 16, pb0_250, pr0_250, pr1_250, b_250, b_125, b_200, sata, + usb, gmac0, cs250, pb0_250_o, pr0_250_o, pr1_250_o, b_250_o, b_125_o, + b_200_o, sata_o, usb_o, gmac0_o, cs250_o, + + nr_clks, +}; + +/* parent clock name list */ +PNAME(mout_armclk_p) = { "cplla", "cpllb" }; +PNAME(mout_spi_p) = { "div125", "div200" }; + +/* fixed rate clocks generated outside the soc */ +struct samsung_fixed_rate_clock exynos5440_fixed_rate_ext_clks[] __initdata = { + FRATE(none, "xtal", NULL, CLK_IS_ROOT, 0), +}; + +/* fixed rate clocks */ +struct samsung_fixed_rate_clock exynos5440_fixed_rate_clks[] __initdata = { + FRATE(none, "ppll", NULL, CLK_IS_ROOT, 1000000000), + FRATE(none, "usb_phy0", NULL, CLK_IS_ROOT, 60000000), + FRATE(none, "usb_phy1", NULL, CLK_IS_ROOT, 60000000), + FRATE(none, "usb_ohci12", NULL, CLK_IS_ROOT, 12000000), + FRATE(none, "usb_ohci48", NULL, CLK_IS_ROOT, 48000000), +}; + +/* fixed factor clocks */ +struct samsung_fixed_factor_clock exynos5440_fixed_factor_clks[] __initdata = { + FFACTOR(none, "div250", "ppll", 1, 4, 0), + FFACTOR(none, "div200", "ppll", 1, 5, 0), + FFACTOR(none, "div125", "div250", 1, 2, 0), +}; + +/* mux clocks */ +struct samsung_mux_clock exynos5440_mux_clks[] __initdata = { + MUX(none, "mout_spi", mout_spi_p, MISC_DOUT1, 5, 1), + MUX_A(arm_clk, "arm_clk", mout_armclk_p, + CPU_CLK_STATUS, 0, 1, "armclk"), +}; + +/* divider clocks */ +struct samsung_div_clock exynos5440_div_clks[] __initdata = { + DIV(spi_baud, "div_spi", "mout_spi", MISC_DOUT1, 3, 2), +}; + +/* gate clocks */ +struct samsung_gate_clock exynos5440_gate_clks[] __initdata = { + GATE(pb0_250, "pb0_250", "div250", CLKEN_OV_VAL, 3, 0, 0), + GATE(pr0_250, "pr0_250", "div250", CLKEN_OV_VAL, 4, 0, 0), + GATE(pr1_250, "pr1_250", "div250", CLKEN_OV_VAL, 5, 0, 0), + GATE(b_250, "b_250", "div250", CLKEN_OV_VAL, 9, 0, 0), + GATE(b_125, "b_125", "div125", CLKEN_OV_VAL, 10, 0, 0), + GATE(b_200, "b_200", "div200", CLKEN_OV_VAL, 11, 0, 0), + GATE(sata, "sata", "div200", CLKEN_OV_VAL, 12, 0, 0), + GATE(usb, "usb", "div200", CLKEN_OV_VAL, 13, 0, 0), + GATE(gmac0, "gmac0", "div200", CLKEN_OV_VAL, 14, 0, 0), + GATE(cs250, "cs250", "div250", CLKEN_OV_VAL, 19, 0, 0), + GATE(pb0_250_o, "pb0_250_o", "pb0_250", CLKEN_OV_VAL, 3, 0, 0), + GATE(pr0_250_o, "pr0_250_o", "pr0_250", CLKEN_OV_VAL, 4, 0, 0), + GATE(pr1_250_o, "pr1_250_o", "pr1_250", CLKEN_OV_VAL, 5, 0, 0), + GATE(b_250_o, "b_250_o", "b_250", CLKEN_OV_VAL, 9, 0, 0), + GATE(b_125_o, "b_125_o", "b_125", CLKEN_OV_VAL, 10, 0, 0), + GATE(b_200_o, "b_200_o", "b_200", CLKEN_OV_VAL, 11, 0, 0), + GATE(sata_o, "sata_o", "sata", CLKEN_OV_VAL, 12, 0, 0), + GATE(usb_o, "usb_o", "usb", CLKEN_OV_VAL, 13, 0, 0), + GATE(gmac0_o, "gmac0_o", "gmac", CLKEN_OV_VAL, 14, 0, 0), + GATE(cs250_o, "cs250_o", "cs250", CLKEN_OV_VAL, 19, 0, 0), +}; + +static __initdata struct of_device_id ext_clk_match[] = { + { .compatible = "samsung,clock-xtal", .data = (void *)0, }, + {}, +}; + +/* register exynos5440 clocks */ +void __init exynos5440_clk_init(struct device_node *np) +{ + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: failed to map clock controller registers," + " aborting clock initialization\n", __func__); + return; + } + + samsung_clk_init(np, reg_base, nr_clks, NULL, 0); + samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks, + ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match); + + samsung_clk_register_pll2550x("cplla", "xtal", reg_base + 0x1c, 0x10); + samsung_clk_register_pll2550x("cpllb", "xtal", reg_base + 0x20, 0x10); + + samsung_clk_register_fixed_rate(exynos5440_fixed_rate_clks, + ARRAY_SIZE(exynos5440_fixed_rate_clks)); + samsung_clk_register_fixed_factor(exynos5440_fixed_factor_clks, + ARRAY_SIZE(exynos5440_fixed_factor_clks)); + samsung_clk_register_mux(exynos5440_mux_clks, + ARRAY_SIZE(exynos5440_mux_clks)); + samsung_clk_register_div(exynos5440_div_clks, + ARRAY_SIZE(exynos5440_div_clks)); + samsung_clk_register_gate(exynos5440_gate_clks, + ARRAY_SIZE(exynos5440_gate_clks)); + + pr_info("Exynos5440: arm_clk = %ldHz\n", _get_rate("armclk")); + pr_info("exynos5440 clock initialization complete\n"); +} +CLK_OF_DECLARE(exynos5440_clk, "samsung,exynos5440-clock", exynos5440_clk_init); -- cgit v1.1 From 415ac2e240f7f3e1b609f34ba7aa1c340589fdb1 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 9 Mar 2013 17:10:31 +0900 Subject: clocksource: mct: use fin_pll clock as the tick clock source for mct With the migration of Exynos4 clocks to use common clock framework, the old styled 'xtal' clock is not used anymore. Instead, the clock 'fin_pll' is used as the tick clock for mct controller. Signed-off-by: Thomas Abraham Signed-off-by: Kukjin Kim --- drivers/clocksource/exynos_mct.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 545c989..f817c54 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -479,10 +479,13 @@ static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = { static void __init exynos4_timer_resources(struct device_node *np) { - struct clk *mct_clk; - mct_clk = clk_get(NULL, "xtal"); + struct clk *tick_clk; - clk_rate = clk_get_rate(mct_clk); + tick_clk = np ? of_clk_get_by_name(np, "fin_pll") : + clk_get(NULL, "fin_pll"); + if (IS_ERR(tick_clk)) + panic("%s: unable to determine tick clock rate\n", __func__); + clk_rate = clk_get_rate(tick_clk); reg_base = np ? of_iomap(np, 0) : S5P_VA_SYSTIMER; if (!reg_base) -- cgit v1.1 From ca9048ec31ab5e50c79bf18eccb79396c1f24b22 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 9 Mar 2013 17:10:37 +0900 Subject: clocksource: mct: add support for mct clock setup Add support for mct clock lookup and setup to ensure that the mct clock is has been turned on. Signed-off-by: Thomas Abraham Signed-off-by: Kukjin Kim --- drivers/clocksource/exynos_mct.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index f817c54..203ac05 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -479,7 +479,7 @@ static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = { static void __init exynos4_timer_resources(struct device_node *np) { - struct clk *tick_clk; + struct clk *mct_clk, *tick_clk; tick_clk = np ? of_clk_get_by_name(np, "fin_pll") : clk_get(NULL, "fin_pll"); @@ -487,6 +487,11 @@ static void __init exynos4_timer_resources(struct device_node *np) panic("%s: unable to determine tick clock rate\n", __func__); clk_rate = clk_get_rate(tick_clk); + mct_clk = np ? of_clk_get_by_name(np, "mct") : clk_get(NULL, "mct"); + if (IS_ERR(mct_clk)) + panic("%s: unable to retrieve mct clock instance\n", __func__); + clk_prepare_enable(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__); -- cgit v1.1 From 798ed613f5db7f61a7773412b9a6bc3d37d17ecb Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 18 Mar 2013 13:43:52 +0900 Subject: clk: samsung: register clk_div_tables for divider clocks On some Samsung platforms divider clocks only use specific divider combinations like the armdiv on s3c2443 and s3c2416. For these usecases the generic divider clock already provides the option of providing a lookup table mapping register values to divider values. Therefore add a new field to samsung_div_clock and if filled with a table, use clk_register_divider_table instead of clk_register_divider to register a divider clock Signed-off-by: Heiko Stuebner Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk.c | 14 +++++++++++--- drivers/clk/samsung/clk.h | 13 +++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 91d12f3..d36cdd5 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -183,9 +183,17 @@ void __init samsung_clk_register_div(struct samsung_div_clock *list, unsigned int idx, ret; for (idx = 0; idx < nr_clk; idx++, list++) { - clk = clk_register_divider(NULL, list->name, list->parent_name, - list->flags, reg_base + list->offset, list->shift, - list->width, list->div_flags, &lock); + if (list->table) + clk = clk_register_divider_table(NULL, list->name, + list->parent_name, list->flags, + reg_base + list->offset, list->shift, + list->width, list->div_flags, + list->table, &lock); + else + clk = clk_register_divider(NULL, list->name, + list->parent_name, list->flags, + reg_base + list->offset, list->shift, + list->width, list->div_flags, &lock); if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s\n", __func__, list->name); diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index 961192f..26a752b 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -150,9 +150,10 @@ struct samsung_div_clock { u8 width; u8 div_flags; const char *alias; + struct clk_div_table *table; }; -#define __DIV(_id, dname, cname, pname, o, s, w, f, df, a) \ +#define __DIV(_id, dname, cname, pname, o, s, w, f, df, a, t) \ { \ .id = _id, \ .dev_name = dname, \ @@ -164,16 +165,20 @@ struct samsung_div_clock { .width = w, \ .div_flags = df, \ .alias = a, \ + .table = t, \ } #define DIV(_id, cname, pname, o, s, w) \ - __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL) + __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, NULL) #define DIV_A(_id, cname, pname, o, s, w, a) \ - __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, a) + __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, a, NULL) #define DIV_F(_id, cname, pname, o, s, w, f, df) \ - __DIV(_id, NULL, cname, pname, o, s, w, f, df, NULL) + __DIV(_id, NULL, cname, pname, o, s, w, f, df, NULL, NULL) + +#define DIV_T(_id, cname, pname, o, s, w, t) \ + __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, t) /** * struct samsung_gate_clock: information about gate clock -- cgit v1.1 From 2466196d3e4f1fbe902ca8480bf4a03db78572b2 Mon Sep 17 00:00:00 2001 From: Heiko Stueber Date: Mon, 18 Mar 2013 13:43:52 +0900 Subject: clk: samsung: fix pm init on non-dt platforms The clock_init function checked for a dt node, returning immediately for non-dt machines. This let to the suspend init never being reached on those non-DT machines. So fix this by moving the pm init code above the check. Signed-off-by: Heiko Stueber Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index d36cdd5..ca04b9e 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -57,18 +57,6 @@ void __init samsung_clk_init(struct device_node *np, void __iomem *base, unsigned long nr_rdump) { reg_base = base; - if (!np) - return; - -#ifdef CONFIG_OF - clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); - if (!clk_table) - panic("could not allocate clock lookup table\n"); - - clk_data.clks = clk_table; - clk_data.clk_num = nr_clks; - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); -#endif #ifdef CONFIG_PM_SLEEP if (rdump && nr_rdump) { @@ -87,6 +75,19 @@ void __init samsung_clk_init(struct device_node *np, void __iomem *base, register_syscore_ops(&samsung_clk_syscore_ops); } #endif + + if (!np) + return; + +#ifdef CONFIG_OF + clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); + if (!clk_table) + panic("could not allocate clock lookup table\n"); + + clk_data.clks = clk_table; + clk_data.clk_num = nr_clks; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +#endif } /* add a clock instance to the clock lookup table used for dt based lookup */ -- cgit v1.1 From 6e92bf5a01afb1f897aa15a34517da07d7c0c320 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 18 Mar 2013 13:43:52 +0900 Subject: clk: samsung: always allocate the clk_table This is needed to allow looking up previous created clocks when adding separate aliases to them. Signed-off-by: Heiko Stuebner Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index ca04b9e..1ed5716 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -76,14 +76,14 @@ void __init samsung_clk_init(struct device_node *np, void __iomem *base, } #endif - if (!np) - return; - -#ifdef CONFIG_OF clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); if (!clk_table) panic("could not allocate clock lookup table\n"); + if (!np) + return; + +#ifdef CONFIG_OF clk_data.clks = clk_table; clk_data.clk_num = nr_clks; of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); -- cgit v1.1 From 5e2e0195ec89d8e266a2530ffec335c483c64899 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 18 Mar 2013 13:43:56 +0900 Subject: clk: samsung: add infrastructure to add separate aliases The current code adds aliases, if necessary, directly when adding the clock, limiting the number of possible aliases to one. Some platforms need more than one alias, like the hsmmc pclocks on s3c2416 which need a "hsmmc" and "mmc_busclk.0" alias for the s3c- sdhci driver. Therefore add the possibility to separately add clock aliases for previously created clocks. Signed-off-by: Heiko Stuebner Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk.c | 33 +++++++++++++++++++++++++++++++++ drivers/clk/samsung/clk.h | 21 +++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 1ed5716..82f27f6 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -97,6 +97,39 @@ void samsung_clk_add_lookup(struct clk *clk, unsigned int id) clk_table[id] = clk; } +/* register a list of aliases */ +void __init samsung_clk_register_alias(struct samsung_clock_alias *list, + unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + if (!clk_table) { + pr_err("%s: clock table missing\n", __func__); + return; + } + + for (idx = 0; idx < nr_clk; idx++, list++) { + if (!list->id) { + pr_err("%s: clock id missing for index %d\n", __func__, + idx); + continue; + } + + clk = clk_table[list->id]; + if (!clk) { + pr_err("%s: failed to find clock %d\n", __func__, + list->id); + continue; + } + + ret = clk_register_clkdev(clk, list->alias, list->dev_name); + if (ret) + pr_err("%s: failed to register lookup %s\n", + __func__, list->alias); + } +} + /* register a list of fixed clocks */ void __init samsung_clk_register_fixed_rate( struct samsung_fixed_rate_clock *list, unsigned int nr_clk) diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index 26a752b..6bacd6f 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -23,6 +23,25 @@ #include /** + * struct samsung_clock_alias: information about mux clock + * @id: platform specific id of the clock. + * @dev_name: name of the device to which this clock belongs. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_clock_alias { + unsigned int id; + const char *dev_name; + const char *alias; +}; + +#define ALIAS(_id, dname, a) \ + { \ + .id = _id, \ + .dev_name = dname, \ + .alias = a, \ + } + +/** * struct samsung_fixed_rate_clock: information about fixed-rate clock * @id: platform specific id of the clock. * @name: name of this fixed-rate clock. @@ -251,6 +270,8 @@ extern void __init samsung_clk_of_register_fixed_ext( extern void samsung_clk_add_lookup(struct clk *clk, unsigned int id); +extern void samsung_clk_register_alias(struct samsung_clock_alias *list, + unsigned int nr_clk); extern void __init samsung_clk_register_fixed_rate( struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_fixed_factor( -- cgit v1.1 From 427cc720277c140e6a63a03237f9bf37d8076ac3 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Wed, 27 Mar 2013 14:54:03 +0100 Subject: irqchip: intc-irqpin: Add support for shared interrupt lines On some hardware we don't have a 1-1 mapping from the external interrupts coming from INTC to the GIC SPI pins. We can however share lines to demux incoming IRQs on these SoCs. This patch enables the intc_irqpin driver to detect requests for shared interrupt lines and demuxes them properly by querying the INTC INTREQx0A registers. If you need multiple shared intc_irqpin device instances, be sure to mask out all interrupts on the INTC that share the one line before you start to register them. Else you run into IRQ floods that would be caused by interrupts for which no handler has been set up yet when the first intc_irqpin device is registered. Signed-off-by: Bastian Hecht Acked-by: Magnus Damm Signed-off-by: Simon Horman --- drivers/irqchip/irq-renesas-intc-irqpin.c | 90 ++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index fd5dabc..5a68e5a 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -74,6 +74,8 @@ struct intc_irqpin_priv { struct platform_device *pdev; struct irq_chip irq_chip; struct irq_domain *irq_domain; + bool shared_irqs; + u8 shared_irq_mask; }; static unsigned long intc_irqpin_read32(void __iomem *iomem) @@ -193,6 +195,28 @@ static void intc_irqpin_irq_disable(struct irq_data *d) intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); } +static void intc_irqpin_shared_irq_enable(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + intc_irqpin_dbg(&p->irq[hw_irq], "shared enable"); + intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); + + p->shared_irq_mask &= ~BIT(hw_irq); +} + +static void intc_irqpin_shared_irq_disable(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + intc_irqpin_dbg(&p->irq[hw_irq], "shared disable"); + intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); + + p->shared_irq_mask |= BIT(hw_irq); +} + static void intc_irqpin_irq_enable_force(struct irq_data *d) { struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); @@ -261,6 +285,25 @@ static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) return IRQ_NONE; } +static irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id) +{ + struct intc_irqpin_priv *p = dev_id; + unsigned int reg_source = intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE); + irqreturn_t status = IRQ_NONE; + int k; + + for (k = 0; k < 8; k++) { + if (reg_source & BIT(7 - k)) { + if (BIT(k) & p->shared_irq_mask) + continue; + + status |= intc_irqpin_irq_handler(irq, &p->irq[k]); + } + } + + return status; +} + static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { @@ -292,6 +335,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) void (*enable_fn)(struct irq_data *d); void (*disable_fn)(struct irq_data *d); const char *name = dev_name(&pdev->dev); + int ref_irq; int ret; int k; @@ -372,13 +416,29 @@ static int intc_irqpin_probe(struct platform_device *pdev) for (k = 0; k < p->number_of_irqs; k++) intc_irqpin_mask_unmask_prio(p, k, 1); + /* clear all pending interrupts */ + intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, 0x0); + + /* scan for shared interrupt lines */ + ref_irq = p->irq[0].requested_irq; + p->shared_irqs = true; + for (k = 1; k < p->number_of_irqs; k++) { + if (ref_irq != p->irq[k].requested_irq) { + p->shared_irqs = false; + break; + } + } + /* use more severe masking method if requested */ if (p->config.control_parent) { enable_fn = intc_irqpin_irq_enable_force; disable_fn = intc_irqpin_irq_disable_force; - } else { + } else if (!p->shared_irqs) { enable_fn = intc_irqpin_irq_enable; disable_fn = intc_irqpin_irq_disable; + } else { + enable_fn = intc_irqpin_shared_irq_enable; + disable_fn = intc_irqpin_shared_irq_disable; } irq_chip = &p->irq_chip; @@ -400,18 +460,34 @@ static int intc_irqpin_probe(struct platform_device *pdev) goto err0; } - /* request and set priority on interrupts one by one */ - for (k = 0; k < p->number_of_irqs; k++) { - if (devm_request_irq(&pdev->dev, p->irq[k].requested_irq, - intc_irqpin_irq_handler, - 0, name, &p->irq[k])) { + if (p->shared_irqs) { + /* request one shared interrupt */ + if (devm_request_irq(&pdev->dev, p->irq[0].requested_irq, + intc_irqpin_shared_irq_handler, + IRQF_SHARED, name, p)) { dev_err(&pdev->dev, "failed to request low IRQ\n"); ret = -ENOENT; goto err1; } - intc_irqpin_mask_unmask_prio(p, k, 0); + } else { + /* request interrupts one by one */ + for (k = 0; k < p->number_of_irqs; k++) { + if (devm_request_irq(&pdev->dev, + p->irq[k].requested_irq, + intc_irqpin_irq_handler, + 0, name, &p->irq[k])) { + dev_err(&pdev->dev, + "failed to request low IRQ\n"); + ret = -ENOENT; + goto err1; + } + } } + /* unmask all interrupts on prio level */ + for (k = 0; k < p->number_of_irqs; k++) + intc_irqpin_mask_unmask_prio(p, k, 0); + dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); /* warn in case of mismatch if irq base is specified */ -- cgit v1.1 From 3daf37260e965aa4bb060db99c2ed10b28109e04 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sat, 23 Mar 2013 17:02:15 +1300 Subject: of: Add support for reading a u32 from a multi-value property. This patch adds an of_property_read_u32_index() function to allow reading a single indexed u32 value from a property containing multiple u32 values. Signed-off-by: Tony Prisk Reviewed-by: Stephen Warren Acked-by: Linus Walleij Acked-by: Rob Herring --- drivers/of/base.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index 321d3ef..f6c89ed 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -746,6 +746,39 @@ struct device_node *of_find_node_by_phandle(phandle handle) EXPORT_SYMBOL(of_find_node_by_phandle); /** + * of_property_read_u32_index - Find and read a u32 from a multi-value property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @index: index of the u32 in the list of values + * @out_value: pointer to return value, modified only if no error. + * + * Search for a property in a device node and read nth 32-bit value from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_value is modified only if a valid u32 value can be decoded. + */ +int of_property_read_u32_index(const struct device_node *np, + const char *propname, + u32 index, u32 *out_value) +{ + struct property *prop = of_find_property(np, propname, NULL); + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + if (((index + 1) * sizeof(*out_value)) > prop->length) + return -EOVERFLOW; + + *out_value = be32_to_cpup(((__be32 *)prop->value) + index); + return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_u32_index); + +/** * of_property_read_u8_array - Find and read an array of u8 from a property. * * @np: device node from which the property value is to be read. -- cgit v1.1 From daeec1f083e02c9ee235e29d2cb28d7b9e81d899 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Wed, 3 Apr 2013 17:57:11 +1300 Subject: of: Remove duplicated code for validating property and value Several functions in of/base.c have the same code duplicated for finding and validating a property and value. struct property *prop = of_find_property(np, propname, NULL); if (!prop) return -EINVAL; if (!prop->value) return -ENODATA; if ( > prop->length) return -EOVERFLOW; This patch adds of_find_property_value_of_size() which performs the equivalent of the above code and removes the instances where it was duplicated in several functions. Reported-by: Rob Herring Signed-off-by: Tony Prisk Acked-by: Linus Walleij Acked-by: Rob Herring --- drivers/of/base.c | 94 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index f6c89ed..c6443de 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -746,6 +746,34 @@ struct device_node *of_find_node_by_phandle(phandle handle) EXPORT_SYMBOL(of_find_node_by_phandle); /** + * of_find_property_value_of_size + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @len: requested length of property value + * + * Search for a property in a device node and valid the requested size. + * Returns the property value on success, -EINVAL if the property does not + * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + */ +static void *of_find_property_value_of_size(const struct device_node *np, + const char *propname, u32 len) +{ + struct property *prop = of_find_property(np, propname, NULL); + + if (!prop) + return ERR_PTR(-EINVAL); + if (!prop->value) + return ERR_PTR(-ENODATA); + if (len > prop->length) + return ERR_PTR(-EOVERFLOW); + + return prop->value; +} + +/** * of_property_read_u32_index - Find and read a u32 from a multi-value property. * * @np: device node from which the property value is to be read. @@ -764,16 +792,13 @@ int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value) { - struct property *prop = of_find_property(np, propname, NULL); + const u32 *val = of_find_property_value_of_size(np, propname, + ((index + 1) * sizeof(*out_value))); - if (!prop) - return -EINVAL; - if (!prop->value) - return -ENODATA; - if (((index + 1) * sizeof(*out_value)) > prop->length) - return -EOVERFLOW; + if (IS_ERR(val)) + return PTR_ERR(val); - *out_value = be32_to_cpup(((__be32 *)prop->value) + index); + *out_value = be32_to_cpup(((__be32 *)val) + index); return 0; } EXPORT_SYMBOL_GPL(of_property_read_u32_index); @@ -799,17 +824,12 @@ EXPORT_SYMBOL_GPL(of_property_read_u32_index); int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz) { - struct property *prop = of_find_property(np, propname, NULL); - const u8 *val; + const u8 *val = of_find_property_value_of_size(np, propname, + (sz * sizeof(*out_values))); - if (!prop) - return -EINVAL; - if (!prop->value) - return -ENODATA; - if ((sz * sizeof(*out_values)) > prop->length) - return -EOVERFLOW; + if (IS_ERR(val)) + return PTR_ERR(val); - val = prop->value; while (sz--) *out_values++ = *val++; return 0; @@ -837,17 +857,12 @@ EXPORT_SYMBOL_GPL(of_property_read_u8_array); int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz) { - struct property *prop = of_find_property(np, propname, NULL); - const __be16 *val; + const __be16 *val = of_find_property_value_of_size(np, propname, + (sz * sizeof(*out_values))); - if (!prop) - return -EINVAL; - if (!prop->value) - return -ENODATA; - if ((sz * sizeof(*out_values)) > prop->length) - return -EOVERFLOW; + if (IS_ERR(val)) + return PTR_ERR(val); - val = prop->value; while (sz--) *out_values++ = be16_to_cpup(val++); return 0; @@ -874,17 +889,12 @@ int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz) { - struct property *prop = of_find_property(np, propname, NULL); - const __be32 *val; + const __be32 *val = of_find_property_value_of_size(np, propname, + (sz * sizeof(*out_values))); - if (!prop) - return -EINVAL; - if (!prop->value) - return -ENODATA; - if ((sz * sizeof(*out_values)) > prop->length) - return -EOVERFLOW; + if (IS_ERR(val)) + return PTR_ERR(val); - val = prop->value; while (sz--) *out_values++ = be32_to_cpup(val++); return 0; @@ -907,15 +917,13 @@ EXPORT_SYMBOL_GPL(of_property_read_u32_array); int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value) { - struct property *prop = of_find_property(np, propname, NULL); + const __be32 *val = of_find_property_value_of_size(np, propname, + sizeof(*out_value)); - if (!prop) - return -EINVAL; - if (!prop->value) - return -ENODATA; - if (sizeof(*out_value) > prop->length) - return -EOVERFLOW; - *out_value = of_read_number(prop->value, 2); + if (IS_ERR(val)) + return PTR_ERR(val); + + *out_value = of_read_number(val, 2); return 0; } EXPORT_SYMBOL_GPL(of_property_read_u64); -- cgit v1.1 From 170c6152aebc5538db22dbdf56fba11ccba7a6f4 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Wed, 20 Feb 2013 09:32:19 +1300 Subject: pinctrl: gpio: vt8500: Add pincontrol driver for arch-vt8500 This patch adds support for the GPIO/pinmux controller found on the VIA VT8500 and Wondermedia WM8xxx-series SoCs. Each pin within the controller is capable of operating as a GPIO or as an alternate function. The pins are numbered according to their control bank/bit so that if new pins are added, the existing numbering is maintained. All currently supported SoCs are included: VT8500, WM8505, WM8650, WM8750 and WM8850. Signed-off-by: Tony Prisk Reviewed-by: Stephen Warren Acked-by: Linus Walleij --- drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/vt8500/Kconfig | 52 +++ drivers/pinctrl/vt8500/Makefile | 8 + drivers/pinctrl/vt8500/pinctrl-vt8500.c | 501 +++++++++++++++++++++++++ drivers/pinctrl/vt8500/pinctrl-wm8505.c | 532 +++++++++++++++++++++++++++ drivers/pinctrl/vt8500/pinctrl-wm8650.c | 370 +++++++++++++++++++ drivers/pinctrl/vt8500/pinctrl-wm8750.c | 409 +++++++++++++++++++++ drivers/pinctrl/vt8500/pinctrl-wm8850.c | 388 ++++++++++++++++++++ drivers/pinctrl/vt8500/pinctrl-wmt.c | 632 ++++++++++++++++++++++++++++++++ drivers/pinctrl/vt8500/pinctrl-wmt.h | 79 ++++ 11 files changed, 2973 insertions(+) create mode 100644 drivers/pinctrl/vt8500/Kconfig create mode 100644 drivers/pinctrl/vt8500/Makefile create mode 100644 drivers/pinctrl/vt8500/pinctrl-vt8500.c create mode 100644 drivers/pinctrl/vt8500/pinctrl-wm8505.c create mode 100644 drivers/pinctrl/vt8500/pinctrl-wm8650.c create mode 100644 drivers/pinctrl/vt8500/pinctrl-wm8750.c create mode 100644 drivers/pinctrl/vt8500/pinctrl-wm8850.c create mode 100644 drivers/pinctrl/vt8500/pinctrl-wmt.c create mode 100644 drivers/pinctrl/vt8500/pinctrl-wmt.h (limited to 'drivers') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 34f51d2..35e9400 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -229,6 +229,7 @@ config PINCTRL_EXYNOS5440 source "drivers/pinctrl/mvebu/Kconfig" source "drivers/pinctrl/sh-pfc/Kconfig" source "drivers/pinctrl/spear/Kconfig" +source "drivers/pinctrl/vt8500/Kconfig" config PINCTRL_XWAY bool diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index f82cc5b..a5a52c8 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -52,3 +52,4 @@ obj-$(CONFIG_PLAT_ORION) += mvebu/ obj-$(CONFIG_ARCH_SHMOBILE) += sh-pfc/ obj-$(CONFIG_SUPERH) += sh-pfc/ obj-$(CONFIG_PLAT_SPEAR) += spear/ +obj-$(CONFIG_ARCH_VT8500) += vt8500/ diff --git a/drivers/pinctrl/vt8500/Kconfig b/drivers/pinctrl/vt8500/Kconfig new file mode 100644 index 0000000..55724a7 --- /dev/null +++ b/drivers/pinctrl/vt8500/Kconfig @@ -0,0 +1,52 @@ +# +# VIA/Wondermedia PINCTRL drivers +# + +if ARCH_VT8500 + +config PINCTRL_WMT + bool + select PINMUX + select GENERIC_PINCONF + +config PINCTRL_VT8500 + bool "VIA VT8500 pin controller driver" + depends on ARCH_WM8505 + select PINCTRL_WMT + help + Say yes here to support the gpio/pin control module on + VIA VT8500 SoCs. + +config PINCTRL_WM8505 + bool "Wondermedia WM8505 pin controller driver" + depends on ARCH_WM8505 + select PINCTRL_WMT + help + Say yes here to support the gpio/pin control module on + Wondermedia WM8505 SoCs. + +config PINCTRL_WM8650 + bool "Wondermedia WM8650 pin controller driver" + depends on ARCH_WM8505 + select PINCTRL_WMT + help + Say yes here to support the gpio/pin control module on + Wondermedia WM8650 SoCs. + +config PINCTRL_WM8750 + bool "Wondermedia WM8750 pin controller driver" + depends on ARCH_WM8750 + select PINCTRL_WMT + help + Say yes here to support the gpio/pin control module on + Wondermedia WM8750 SoCs. + +config PINCTRL_WM8850 + bool "Wondermedia WM8850 pin controller driver" + depends on ARCH_WM8850 + select PINCTRL_WMT + help + Say yes here to support the gpio/pin control module on + Wondermedia WM8850 SoCs. + +endif diff --git a/drivers/pinctrl/vt8500/Makefile b/drivers/pinctrl/vt8500/Makefile new file mode 100644 index 0000000..24ec45d --- /dev/null +++ b/drivers/pinctrl/vt8500/Makefile @@ -0,0 +1,8 @@ +# VIA/Wondermedia pinctrl support + +obj-$(CONFIG_PINCTRL_WMT) += pinctrl-wmt.o +obj-$(CONFIG_PINCTRL_VT8500) += pinctrl-vt8500.o +obj-$(CONFIG_PINCTRL_WM8505) += pinctrl-wm8505.o +obj-$(CONFIG_PINCTRL_WM8650) += pinctrl-wm8650.o +obj-$(CONFIG_PINCTRL_WM8750) += pinctrl-wm8750.o +obj-$(CONFIG_PINCTRL_WM8850) += pinctrl-wm8850.o diff --git a/drivers/pinctrl/vt8500/pinctrl-vt8500.c b/drivers/pinctrl/vt8500/pinctrl-vt8500.c new file mode 100644 index 0000000..f2fe9f8 --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-vt8500.c @@ -0,0 +1,501 @@ +/* + * Pinctrl data for VIA VT8500 SoC + * + * Copyright (c) 2013 Tony Prisk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers vt8500_banks[] = { + WMT_PINCTRL_BANK(NO_REG, 0x3C, 0x5C, 0x7C, NO_REG, NO_REG), /* 0 */ + WMT_PINCTRL_BANK(0x00, 0x20, 0x40, 0x60, NO_REG, NO_REG), /* 1 */ + WMT_PINCTRL_BANK(0x04, 0x24, 0x44, 0x64, NO_REG, NO_REG), /* 2 */ + WMT_PINCTRL_BANK(0x08, 0x28, 0x48, 0x68, NO_REG, NO_REG), /* 3 */ + WMT_PINCTRL_BANK(0x0C, 0x2C, 0x4C, 0x6C, NO_REG, NO_REG), /* 4 */ + WMT_PINCTRL_BANK(0x10, 0x30, 0x50, 0x70, NO_REG, NO_REG), /* 5 */ + WMT_PINCTRL_BANK(0x14, 0x34, 0x54, 0x74, NO_REG, NO_REG), /* 6 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0 WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1 WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2 WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3 WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4 WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5 WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6 WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7 WMT_PIN(0, 7) +#define WMT_PIN_EXTGPIO8 WMT_PIN(0, 8) +#define WMT_PIN_UART0RTS WMT_PIN(1, 0) +#define WMT_PIN_UART0TXD WMT_PIN(1, 1) +#define WMT_PIN_UART0CTS WMT_PIN(1, 2) +#define WMT_PIN_UART0RXD WMT_PIN(1, 3) +#define WMT_PIN_UART1RTS WMT_PIN(1, 4) +#define WMT_PIN_UART1TXD WMT_PIN(1, 5) +#define WMT_PIN_UART1CTS WMT_PIN(1, 6) +#define WMT_PIN_UART1RXD WMT_PIN(1, 7) +#define WMT_PIN_SPI0CLK WMT_PIN(1, 8) +#define WMT_PIN_SPI0SS WMT_PIN(1, 9) +#define WMT_PIN_SPI0MISO WMT_PIN(1, 10) +#define WMT_PIN_SPI0MOSI WMT_PIN(1, 11) +#define WMT_PIN_SPI1CLK WMT_PIN(1, 12) +#define WMT_PIN_SPI1SS WMT_PIN(1, 13) +#define WMT_PIN_SPI1MISO WMT_PIN(1, 14) +#define WMT_PIN_SPI1MOSI WMT_PIN(1, 15) +#define WMT_PIN_SPI2CLK WMT_PIN(1, 16) +#define WMT_PIN_SPI2SS WMT_PIN(1, 17) +#define WMT_PIN_SPI2MISO WMT_PIN(1, 18) +#define WMT_PIN_SPI2MOSI WMT_PIN(1, 19) +#define WMT_PIN_SDDATA0 WMT_PIN(2, 0) +#define WMT_PIN_SDDATA1 WMT_PIN(2, 1) +#define WMT_PIN_SDDATA2 WMT_PIN(2, 2) +#define WMT_PIN_SDDATA3 WMT_PIN(2, 3) +#define WMT_PIN_MMCDATA0 WMT_PIN(2, 4) +#define WMT_PIN_MMCDATA1 WMT_PIN(2, 5) +#define WMT_PIN_MMCDATA2 WMT_PIN(2, 6) +#define WMT_PIN_MMCDATA3 WMT_PIN(2, 7) +#define WMT_PIN_SDCLK WMT_PIN(2, 8) +#define WMT_PIN_SDWP WMT_PIN(2, 9) +#define WMT_PIN_SDCMD WMT_PIN(2, 10) +#define WMT_PIN_MSDATA0 WMT_PIN(2, 16) +#define WMT_PIN_MSDATA1 WMT_PIN(2, 17) +#define WMT_PIN_MSDATA2 WMT_PIN(2, 18) +#define WMT_PIN_MSDATA3 WMT_PIN(2, 19) +#define WMT_PIN_MSCLK WMT_PIN(2, 20) +#define WMT_PIN_MSBS WMT_PIN(2, 21) +#define WMT_PIN_MSINS WMT_PIN(2, 22) +#define WMT_PIN_I2C0SCL WMT_PIN(2, 24) +#define WMT_PIN_I2C0SDA WMT_PIN(2, 25) +#define WMT_PIN_I2C1SCL WMT_PIN(2, 26) +#define WMT_PIN_I2C1SDA WMT_PIN(2, 27) +#define WMT_PIN_MII0RXD0 WMT_PIN(3, 0) +#define WMT_PIN_MII0RXD1 WMT_PIN(3, 1) +#define WMT_PIN_MII0RXD2 WMT_PIN(3, 2) +#define WMT_PIN_MII0RXD3 WMT_PIN(3, 3) +#define WMT_PIN_MII0RXCLK WMT_PIN(3, 4) +#define WMT_PIN_MII0RXDV WMT_PIN(3, 5) +#define WMT_PIN_MII0RXERR WMT_PIN(3, 6) +#define WMT_PIN_MII0PHYRST WMT_PIN(3, 7) +#define WMT_PIN_MII0TXD0 WMT_PIN(3, 8) +#define WMT_PIN_MII0TXD1 WMT_PIN(3, 9) +#define WMT_PIN_MII0TXD2 WMT_PIN(3, 10) +#define WMT_PIN_MII0TXD3 WMT_PIN(3, 11) +#define WMT_PIN_MII0TXCLK WMT_PIN(3, 12) +#define WMT_PIN_MII0TXEN WMT_PIN(3, 13) +#define WMT_PIN_MII0TXERR WMT_PIN(3, 14) +#define WMT_PIN_MII0PHYPD WMT_PIN(3, 15) +#define WMT_PIN_MII0COL WMT_PIN(3, 16) +#define WMT_PIN_MII0CRS WMT_PIN(3, 17) +#define WMT_PIN_MII0MDIO WMT_PIN(3, 18) +#define WMT_PIN_MII0MDC WMT_PIN(3, 19) +#define WMT_PIN_SEECS WMT_PIN(3, 20) +#define WMT_PIN_SEECK WMT_PIN(3, 21) +#define WMT_PIN_SEEDI WMT_PIN(3, 22) +#define WMT_PIN_SEEDO WMT_PIN(3, 23) +#define WMT_PIN_IDEDREQ0 WMT_PIN(3, 24) +#define WMT_PIN_IDEDREQ1 WMT_PIN(3, 25) +#define WMT_PIN_IDEIOW WMT_PIN(3, 26) +#define WMT_PIN_IDEIOR WMT_PIN(3, 27) +#define WMT_PIN_IDEDACK WMT_PIN(3, 28) +#define WMT_PIN_IDEIORDY WMT_PIN(3, 29) +#define WMT_PIN_IDEINTRQ WMT_PIN(3, 30) +#define WMT_PIN_VDIN0 WMT_PIN(4, 0) +#define WMT_PIN_VDIN1 WMT_PIN(4, 1) +#define WMT_PIN_VDIN2 WMT_PIN(4, 2) +#define WMT_PIN_VDIN3 WMT_PIN(4, 3) +#define WMT_PIN_VDIN4 WMT_PIN(4, 4) +#define WMT_PIN_VDIN5 WMT_PIN(4, 5) +#define WMT_PIN_VDIN6 WMT_PIN(4, 6) +#define WMT_PIN_VDIN7 WMT_PIN(4, 7) +#define WMT_PIN_VDOUT0 WMT_PIN(4, 8) +#define WMT_PIN_VDOUT1 WMT_PIN(4, 9) +#define WMT_PIN_VDOUT2 WMT_PIN(4, 10) +#define WMT_PIN_VDOUT3 WMT_PIN(4, 11) +#define WMT_PIN_VDOUT4 WMT_PIN(4, 12) +#define WMT_PIN_VDOUT5 WMT_PIN(4, 13) +#define WMT_PIN_NANDCLE0 WMT_PIN(4, 14) +#define WMT_PIN_NANDCLE1 WMT_PIN(4, 15) +#define WMT_PIN_VDOUT6_7 WMT_PIN(4, 16) +#define WMT_PIN_VHSYNC WMT_PIN(4, 17) +#define WMT_PIN_VVSYNC WMT_PIN(4, 18) +#define WMT_PIN_TSDIN0 WMT_PIN(5, 8) +#define WMT_PIN_TSDIN1 WMT_PIN(5, 9) +#define WMT_PIN_TSDIN2 WMT_PIN(5, 10) +#define WMT_PIN_TSDIN3 WMT_PIN(5, 11) +#define WMT_PIN_TSDIN4 WMT_PIN(5, 12) +#define WMT_PIN_TSDIN5 WMT_PIN(5, 13) +#define WMT_PIN_TSDIN6 WMT_PIN(5, 14) +#define WMT_PIN_TSDIN7 WMT_PIN(5, 15) +#define WMT_PIN_TSSYNC WMT_PIN(5, 16) +#define WMT_PIN_TSVALID WMT_PIN(5, 17) +#define WMT_PIN_TSCLK WMT_PIN(5, 18) +#define WMT_PIN_LCDD0 WMT_PIN(6, 0) +#define WMT_PIN_LCDD1 WMT_PIN(6, 1) +#define WMT_PIN_LCDD2 WMT_PIN(6, 2) +#define WMT_PIN_LCDD3 WMT_PIN(6, 3) +#define WMT_PIN_LCDD4 WMT_PIN(6, 4) +#define WMT_PIN_LCDD5 WMT_PIN(6, 5) +#define WMT_PIN_LCDD6 WMT_PIN(6, 6) +#define WMT_PIN_LCDD7 WMT_PIN(6, 7) +#define WMT_PIN_LCDD8 WMT_PIN(6, 8) +#define WMT_PIN_LCDD9 WMT_PIN(6, 9) +#define WMT_PIN_LCDD10 WMT_PIN(6, 10) +#define WMT_PIN_LCDD11 WMT_PIN(6, 11) +#define WMT_PIN_LCDD12 WMT_PIN(6, 12) +#define WMT_PIN_LCDD13 WMT_PIN(6, 13) +#define WMT_PIN_LCDD14 WMT_PIN(6, 14) +#define WMT_PIN_LCDD15 WMT_PIN(6, 15) +#define WMT_PIN_LCDD16 WMT_PIN(6, 16) +#define WMT_PIN_LCDD17 WMT_PIN(6, 17) +#define WMT_PIN_LCDCLK WMT_PIN(6, 18) +#define WMT_PIN_LCDDEN WMT_PIN(6, 19) +#define WMT_PIN_LCDLINE WMT_PIN(6, 20) +#define WMT_PIN_LCDFRM WMT_PIN(6, 21) +#define WMT_PIN_LCDBIAS WMT_PIN(6, 22) + +static const struct pinctrl_pin_desc vt8500_pins[] = { + PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), + PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), + PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), + PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), + PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), + PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), + PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), + PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), + PINCTRL_PIN(WMT_PIN_EXTGPIO8, "extgpio8"), + PINCTRL_PIN(WMT_PIN_UART0RTS, "uart0_rts"), + PINCTRL_PIN(WMT_PIN_UART0TXD, "uart0_txd"), + PINCTRL_PIN(WMT_PIN_UART0CTS, "uart0_cts"), + PINCTRL_PIN(WMT_PIN_UART0RXD, "uart0_rxd"), + PINCTRL_PIN(WMT_PIN_UART1RTS, "uart1_rts"), + PINCTRL_PIN(WMT_PIN_UART1TXD, "uart1_txd"), + PINCTRL_PIN(WMT_PIN_UART1CTS, "uart1_cts"), + PINCTRL_PIN(WMT_PIN_UART1RXD, "uart1_rxd"), + PINCTRL_PIN(WMT_PIN_SPI0CLK, "spi0_clk"), + PINCTRL_PIN(WMT_PIN_SPI0SS, "spi0_ss"), + PINCTRL_PIN(WMT_PIN_SPI0MISO, "spi0_miso"), + PINCTRL_PIN(WMT_PIN_SPI0MOSI, "spi0_mosi"), + PINCTRL_PIN(WMT_PIN_SPI1CLK, "spi1_clk"), + PINCTRL_PIN(WMT_PIN_SPI1SS, "spi1_ss"), + PINCTRL_PIN(WMT_PIN_SPI1MISO, "spi1_miso"), + PINCTRL_PIN(WMT_PIN_SPI1MOSI, "spi1_mosi"), + PINCTRL_PIN(WMT_PIN_SPI2CLK, "spi2_clk"), + PINCTRL_PIN(WMT_PIN_SPI2SS, "spi2_ss"), + PINCTRL_PIN(WMT_PIN_SPI2MISO, "spi2_miso"), + PINCTRL_PIN(WMT_PIN_SPI2MOSI, "spi2_mosi"), + PINCTRL_PIN(WMT_PIN_SDDATA0, "sd_data0"), + PINCTRL_PIN(WMT_PIN_SDDATA1, "sd_data1"), + PINCTRL_PIN(WMT_PIN_SDDATA2, "sd_data2"), + PINCTRL_PIN(WMT_PIN_SDDATA3, "sd_data3"), + PINCTRL_PIN(WMT_PIN_MMCDATA0, "mmc_data0"), + PINCTRL_PIN(WMT_PIN_MMCDATA1, "mmc_data1"), + PINCTRL_PIN(WMT_PIN_MMCDATA2, "mmc_data2"), + PINCTRL_PIN(WMT_PIN_MMCDATA3, "mmc_data3"), + PINCTRL_PIN(WMT_PIN_SDCLK, "sd_clk"), + PINCTRL_PIN(WMT_PIN_SDWP, "sd_wp"), + PINCTRL_PIN(WMT_PIN_SDCMD, "sd_cmd"), + PINCTRL_PIN(WMT_PIN_MSDATA0, "ms_data0"), + PINCTRL_PIN(WMT_PIN_MSDATA1, "ms_data1"), + PINCTRL_PIN(WMT_PIN_MSDATA2, "ms_data2"), + PINCTRL_PIN(WMT_PIN_MSDATA3, "ms_data3"), + PINCTRL_PIN(WMT_PIN_MSCLK, "ms_clk"), + PINCTRL_PIN(WMT_PIN_MSBS, "ms_bs"), + PINCTRL_PIN(WMT_PIN_MSINS, "ms_ins"), + PINCTRL_PIN(WMT_PIN_I2C0SCL, "i2c0_scl"), + PINCTRL_PIN(WMT_PIN_I2C0SDA, "i2c0_sda"), + PINCTRL_PIN(WMT_PIN_I2C1SCL, "i2c1_scl"), + PINCTRL_PIN(WMT_PIN_I2C1SDA, "i2c1_sda"), + PINCTRL_PIN(WMT_PIN_MII0RXD0, "mii0_rxd0"), + PINCTRL_PIN(WMT_PIN_MII0RXD1, "mii0_rxd1"), + PINCTRL_PIN(WMT_PIN_MII0RXD2, "mii0_rxd2"), + PINCTRL_PIN(WMT_PIN_MII0RXD3, "mii0_rxd3"), + PINCTRL_PIN(WMT_PIN_MII0RXCLK, "mii0_rxclk"), + PINCTRL_PIN(WMT_PIN_MII0RXDV, "mii0_rxdv"), + PINCTRL_PIN(WMT_PIN_MII0RXERR, "mii0_rxerr"), + PINCTRL_PIN(WMT_PIN_MII0PHYRST, "mii0_phyrst"), + PINCTRL_PIN(WMT_PIN_MII0TXD0, "mii0_txd0"), + PINCTRL_PIN(WMT_PIN_MII0TXD1, "mii0_txd1"), + PINCTRL_PIN(WMT_PIN_MII0TXD2, "mii0_txd2"), + PINCTRL_PIN(WMT_PIN_MII0TXD3, "mii0_txd3"), + PINCTRL_PIN(WMT_PIN_MII0TXCLK, "mii0_txclk"), + PINCTRL_PIN(WMT_PIN_MII0TXEN, "mii0_txen"), + PINCTRL_PIN(WMT_PIN_MII0TXERR, "mii0_txerr"), + PINCTRL_PIN(WMT_PIN_MII0PHYPD, "mii0_phypd"), + PINCTRL_PIN(WMT_PIN_MII0COL, "mii0_col"), + PINCTRL_PIN(WMT_PIN_MII0CRS, "mii0_crs"), + PINCTRL_PIN(WMT_PIN_MII0MDIO, "mii0_mdio"), + PINCTRL_PIN(WMT_PIN_MII0MDC, "mii0_mdc"), + PINCTRL_PIN(WMT_PIN_SEECS, "see_cs"), + PINCTRL_PIN(WMT_PIN_SEECK, "see_ck"), + PINCTRL_PIN(WMT_PIN_SEEDI, "see_di"), + PINCTRL_PIN(WMT_PIN_SEEDO, "see_do"), + PINCTRL_PIN(WMT_PIN_IDEDREQ0, "ide_dreq0"), + PINCTRL_PIN(WMT_PIN_IDEDREQ1, "ide_dreq1"), + PINCTRL_PIN(WMT_PIN_IDEIOW, "ide_iow"), + PINCTRL_PIN(WMT_PIN_IDEIOR, "ide_ior"), + PINCTRL_PIN(WMT_PIN_IDEDACK, "ide_dack"), + PINCTRL_PIN(WMT_PIN_IDEIORDY, "ide_iordy"), + PINCTRL_PIN(WMT_PIN_IDEINTRQ, "ide_intrq"), + PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), + PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), + PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), + PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), + PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), + PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), + PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), + PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), + PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), + PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), + PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), + PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), + PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), + PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), + PINCTRL_PIN(WMT_PIN_NANDCLE0, "nand_cle0"), + PINCTRL_PIN(WMT_PIN_NANDCLE1, "nand_cle1"), + PINCTRL_PIN(WMT_PIN_VDOUT6_7, "vdout6_7"), + PINCTRL_PIN(WMT_PIN_VHSYNC, "vhsync"), + PINCTRL_PIN(WMT_PIN_VVSYNC, "vvsync"), + PINCTRL_PIN(WMT_PIN_TSDIN0, "tsdin0"), + PINCTRL_PIN(WMT_PIN_TSDIN1, "tsdin1"), + PINCTRL_PIN(WMT_PIN_TSDIN2, "tsdin2"), + PINCTRL_PIN(WMT_PIN_TSDIN3, "tsdin3"), + PINCTRL_PIN(WMT_PIN_TSDIN4, "tsdin4"), + PINCTRL_PIN(WMT_PIN_TSDIN5, "tsdin5"), + PINCTRL_PIN(WMT_PIN_TSDIN6, "tsdin6"), + PINCTRL_PIN(WMT_PIN_TSDIN7, "tsdin7"), + PINCTRL_PIN(WMT_PIN_TSSYNC, "tssync"), + PINCTRL_PIN(WMT_PIN_TSVALID, "tsvalid"), + PINCTRL_PIN(WMT_PIN_TSCLK, "tsclk"), + PINCTRL_PIN(WMT_PIN_LCDD0, "lcd_d0"), + PINCTRL_PIN(WMT_PIN_LCDD1, "lcd_d1"), + PINCTRL_PIN(WMT_PIN_LCDD2, "lcd_d2"), + PINCTRL_PIN(WMT_PIN_LCDD3, "lcd_d3"), + PINCTRL_PIN(WMT_PIN_LCDD4, "lcd_d4"), + PINCTRL_PIN(WMT_PIN_LCDD5, "lcd_d5"), + PINCTRL_PIN(WMT_PIN_LCDD6, "lcd_d6"), + PINCTRL_PIN(WMT_PIN_LCDD7, "lcd_d7"), + PINCTRL_PIN(WMT_PIN_LCDD8, "lcd_d8"), + PINCTRL_PIN(WMT_PIN_LCDD9, "lcd_d9"), + PINCTRL_PIN(WMT_PIN_LCDD10, "lcd_d10"), + PINCTRL_PIN(WMT_PIN_LCDD11, "lcd_d11"), + PINCTRL_PIN(WMT_PIN_LCDD12, "lcd_d12"), + PINCTRL_PIN(WMT_PIN_LCDD13, "lcd_d13"), + PINCTRL_PIN(WMT_PIN_LCDD14, "lcd_d14"), + PINCTRL_PIN(WMT_PIN_LCDD15, "lcd_d15"), + PINCTRL_PIN(WMT_PIN_LCDD16, "lcd_d16"), + PINCTRL_PIN(WMT_PIN_LCDD17, "lcd_d17"), + PINCTRL_PIN(WMT_PIN_LCDCLK, "lcd_clk"), + PINCTRL_PIN(WMT_PIN_LCDDEN, "lcd_den"), + PINCTRL_PIN(WMT_PIN_LCDLINE, "lcd_line"), + PINCTRL_PIN(WMT_PIN_LCDFRM, "lcd_frm"), + PINCTRL_PIN(WMT_PIN_LCDBIAS, "lcd_bias"), +}; + +/* Order of these names must match the above list */ +static const char * const vt8500_groups[] = { + "extgpio0", + "extgpio1", + "extgpio2", + "extgpio3", + "extgpio4", + "extgpio5", + "extgpio6", + "extgpio7", + "extgpio8", + "uart0_rts", + "uart0_txd", + "uart0_cts", + "uart0_rxd", + "uart1_rts", + "uart1_txd", + "uart1_cts", + "uart1_rxd", + "spi0_clk", + "spi0_ss", + "spi0_miso", + "spi0_mosi", + "spi1_clk", + "spi1_ss", + "spi1_miso", + "spi1_mosi", + "spi2_clk", + "spi2_ss", + "spi2_miso", + "spi2_mosi", + "sd_data0", + "sd_data1", + "sd_data2", + "sd_data3", + "mmc_data0", + "mmc_data1", + "mmc_data2", + "mmc_data3", + "sd_clk", + "sd_wp", + "sd_cmd", + "ms_data0", + "ms_data1", + "ms_data2", + "ms_data3", + "ms_clk", + "ms_bs", + "ms_ins", + "i2c0_scl", + "i2c0_sda", + "i2c1_scl", + "i2c1_sda", + "mii0_rxd0", + "mii0_rxd1", + "mii0_rxd2", + "mii0_rxd3", + "mii0_rxclk", + "mii0_rxdv", + "mii0_rxerr", + "mii0_phyrst", + "mii0_txd0", + "mii0_txd1", + "mii0_txd2", + "mii0_txd3", + "mii0_txclk", + "mii0_txen", + "mii0_txerr", + "mii0_phypd", + "mii0_col", + "mii0_crs", + "mii0_mdio", + "mii0_mdc", + "see_cs", + "see_ck", + "see_di", + "see_do", + "ide_dreq0", + "ide_dreq1", + "ide_iow", + "ide_ior", + "ide_dack", + "ide_iordy", + "ide_intrq", + "vdin0", + "vdin1", + "vdin2", + "vdin3", + "vdin4", + "vdin5", + "vdin6", + "vdin7", + "vdout0", + "vdout1", + "vdout2", + "vdout3", + "vdout4", + "vdout5", + "nand_cle0", + "nand_cle1", + "vdout6_7", + "vhsync", + "vvsync", + "tsdin0", + "tsdin1", + "tsdin2", + "tsdin3", + "tsdin4", + "tsdin5", + "tsdin6", + "tsdin7", + "tssync", + "tsvalid", + "tsclk", + "lcd_d0", + "lcd_d1", + "lcd_d2", + "lcd_d3", + "lcd_d4", + "lcd_d5", + "lcd_d6", + "lcd_d7", + "lcd_d8", + "lcd_d9", + "lcd_d10", + "lcd_d11", + "lcd_d12", + "lcd_d13", + "lcd_d14", + "lcd_d15", + "lcd_d16", + "lcd_d17", + "lcd_clk", + "lcd_den", + "lcd_line", + "lcd_frm", + "lcd_bias", +}; + +static int vt8500_pinctrl_probe(struct platform_device *pdev) +{ + struct wmt_pinctrl_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "failed to allocate data\n"); + return -ENOMEM; + } + + data->banks = vt8500_banks; + data->nbanks = ARRAY_SIZE(vt8500_banks); + data->pins = vt8500_pins; + data->npins = ARRAY_SIZE(vt8500_pins); + data->groups = vt8500_groups; + data->ngroups = ARRAY_SIZE(vt8500_groups); + + return wmt_pinctrl_probe(pdev, data); +} + +static int vt8500_pinctrl_remove(struct platform_device *pdev) +{ + return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { + { .compatible = "via,vt8500-pinctrl" }, + { /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { + .probe = vt8500_pinctrl_probe, + .remove = vt8500_pinctrl_remove, + .driver = { + .name = "pinctrl-vt8500", + .owner = THIS_MODULE, + .of_match_table = wmt_pinctrl_of_match, + }, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk "); +MODULE_DESCRIPTION("VIA VT8500 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8505.c b/drivers/pinctrl/vt8500/pinctrl-wm8505.c new file mode 100644 index 0000000..483ba73 --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wm8505.c @@ -0,0 +1,532 @@ +/* + * Pinctrl data for Wondermedia WM8505 SoC + * + * Copyright (c) 2013 Tony Prisk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers wm8505_banks[] = { + WMT_PINCTRL_BANK(0x64, 0x8C, 0xB4, 0xDC, NO_REG, NO_REG), /* 0 */ + WMT_PINCTRL_BANK(0x40, 0x68, 0x90, 0xB8, NO_REG, NO_REG), /* 1 */ + WMT_PINCTRL_BANK(0x44, 0x6C, 0x94, 0xBC, NO_REG, NO_REG), /* 2 */ + WMT_PINCTRL_BANK(0x48, 0x70, 0x98, 0xC0, NO_REG, NO_REG), /* 3 */ + WMT_PINCTRL_BANK(0x4C, 0x74, 0x9C, 0xC4, NO_REG, NO_REG), /* 4 */ + WMT_PINCTRL_BANK(0x50, 0x78, 0xA0, 0xC8, NO_REG, NO_REG), /* 5 */ + WMT_PINCTRL_BANK(0x54, 0x7C, 0xA4, 0xD0, NO_REG, NO_REG), /* 6 */ + WMT_PINCTRL_BANK(0x58, 0x80, 0xA8, 0xD4, NO_REG, NO_REG), /* 7 */ + WMT_PINCTRL_BANK(0x5C, 0x84, 0xAC, 0xD8, NO_REG, NO_REG), /* 8 */ + WMT_PINCTRL_BANK(0x60, 0x88, 0xB0, 0xDC, NO_REG, NO_REG), /* 9 */ + WMT_PINCTRL_BANK(0x500, 0x504, 0x508, 0x50C, NO_REG, NO_REG), /* 10 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0 WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1 WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2 WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3 WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4 WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5 WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6 WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7 WMT_PIN(0, 7) +#define WMT_PIN_WAKEUP0 WMT_PIN(0, 16) +#define WMT_PIN_WAKEUP1 WMT_PIN(0, 17) +#define WMT_PIN_WAKEUP2 WMT_PIN(0, 18) +#define WMT_PIN_WAKEUP3 WMT_PIN(0, 19) +#define WMT_PIN_SUSGPIO0 WMT_PIN(0, 21) +#define WMT_PIN_SDDATA0 WMT_PIN(1, 0) +#define WMT_PIN_SDDATA1 WMT_PIN(1, 1) +#define WMT_PIN_SDDATA2 WMT_PIN(1, 2) +#define WMT_PIN_SDDATA3 WMT_PIN(1, 3) +#define WMT_PIN_MMCDATA0 WMT_PIN(1, 4) +#define WMT_PIN_MMCDATA1 WMT_PIN(1, 5) +#define WMT_PIN_MMCDATA2 WMT_PIN(1, 6) +#define WMT_PIN_MMCDATA3 WMT_PIN(1, 7) +#define WMT_PIN_VDIN0 WMT_PIN(2, 0) +#define WMT_PIN_VDIN1 WMT_PIN(2, 1) +#define WMT_PIN_VDIN2 WMT_PIN(2, 2) +#define WMT_PIN_VDIN3 WMT_PIN(2, 3) +#define WMT_PIN_VDIN4 WMT_PIN(2, 4) +#define WMT_PIN_VDIN5 WMT_PIN(2, 5) +#define WMT_PIN_VDIN6 WMT_PIN(2, 6) +#define WMT_PIN_VDIN7 WMT_PIN(2, 7) +#define WMT_PIN_VDOUT0 WMT_PIN(2, 8) +#define WMT_PIN_VDOUT1 WMT_PIN(2, 9) +#define WMT_PIN_VDOUT2 WMT_PIN(2, 10) +#define WMT_PIN_VDOUT3 WMT_PIN(2, 11) +#define WMT_PIN_VDOUT4 WMT_PIN(2, 12) +#define WMT_PIN_VDOUT5 WMT_PIN(2, 13) +#define WMT_PIN_VDOUT6 WMT_PIN(2, 14) +#define WMT_PIN_VDOUT7 WMT_PIN(2, 15) +#define WMT_PIN_VDOUT8 WMT_PIN(2, 16) +#define WMT_PIN_VDOUT9 WMT_PIN(2, 17) +#define WMT_PIN_VDOUT10 WMT_PIN(2, 18) +#define WMT_PIN_VDOUT11 WMT_PIN(2, 19) +#define WMT_PIN_VDOUT12 WMT_PIN(2, 20) +#define WMT_PIN_VDOUT13 WMT_PIN(2, 21) +#define WMT_PIN_VDOUT14 WMT_PIN(2, 22) +#define WMT_PIN_VDOUT15 WMT_PIN(2, 23) +#define WMT_PIN_VDOUT16 WMT_PIN(2, 24) +#define WMT_PIN_VDOUT17 WMT_PIN(2, 25) +#define WMT_PIN_VDOUT18 WMT_PIN(2, 26) +#define WMT_PIN_VDOUT19 WMT_PIN(2, 27) +#define WMT_PIN_VDOUT20 WMT_PIN(2, 28) +#define WMT_PIN_VDOUT21 WMT_PIN(2, 29) +#define WMT_PIN_VDOUT22 WMT_PIN(2, 30) +#define WMT_PIN_VDOUT23 WMT_PIN(2, 31) +#define WMT_PIN_VHSYNC WMT_PIN(3, 0) +#define WMT_PIN_VVSYNC WMT_PIN(3, 1) +#define WMT_PIN_VGAHSYNC WMT_PIN(3, 2) +#define WMT_PIN_VGAVSYNC WMT_PIN(3, 3) +#define WMT_PIN_VDHSYNC WMT_PIN(3, 4) +#define WMT_PIN_VDVSYNC WMT_PIN(3, 5) +#define WMT_PIN_NORD0 WMT_PIN(4, 0) +#define WMT_PIN_NORD1 WMT_PIN(4, 1) +#define WMT_PIN_NORD2 WMT_PIN(4, 2) +#define WMT_PIN_NORD3 WMT_PIN(4, 3) +#define WMT_PIN_NORD4 WMT_PIN(4, 4) +#define WMT_PIN_NORD5 WMT_PIN(4, 5) +#define WMT_PIN_NORD6 WMT_PIN(4, 6) +#define WMT_PIN_NORD7 WMT_PIN(4, 7) +#define WMT_PIN_NORD8 WMT_PIN(4, 8) +#define WMT_PIN_NORD9 WMT_PIN(4, 9) +#define WMT_PIN_NORD10 WMT_PIN(4, 10) +#define WMT_PIN_NORD11 WMT_PIN(4, 11) +#define WMT_PIN_NORD12 WMT_PIN(4, 12) +#define WMT_PIN_NORD13 WMT_PIN(4, 13) +#define WMT_PIN_NORD14 WMT_PIN(4, 14) +#define WMT_PIN_NORD15 WMT_PIN(4, 15) +#define WMT_PIN_NORA0 WMT_PIN(5, 0) +#define WMT_PIN_NORA1 WMT_PIN(5, 1) +#define WMT_PIN_NORA2 WMT_PIN(5, 2) +#define WMT_PIN_NORA3 WMT_PIN(5, 3) +#define WMT_PIN_NORA4 WMT_PIN(5, 4) +#define WMT_PIN_NORA5 WMT_PIN(5, 5) +#define WMT_PIN_NORA6 WMT_PIN(5, 6) +#define WMT_PIN_NORA7 WMT_PIN(5, 7) +#define WMT_PIN_NORA8 WMT_PIN(5, 8) +#define WMT_PIN_NORA9 WMT_PIN(5, 9) +#define WMT_PIN_NORA10 WMT_PIN(5, 10) +#define WMT_PIN_NORA11 WMT_PIN(5, 11) +#define WMT_PIN_NORA12 WMT_PIN(5, 12) +#define WMT_PIN_NORA13 WMT_PIN(5, 13) +#define WMT_PIN_NORA14 WMT_PIN(5, 14) +#define WMT_PIN_NORA15 WMT_PIN(5, 15) +#define WMT_PIN_NORA16 WMT_PIN(5, 16) +#define WMT_PIN_NORA17 WMT_PIN(5, 17) +#define WMT_PIN_NORA18 WMT_PIN(5, 18) +#define WMT_PIN_NORA19 WMT_PIN(5, 19) +#define WMT_PIN_NORA20 WMT_PIN(5, 20) +#define WMT_PIN_NORA21 WMT_PIN(5, 21) +#define WMT_PIN_NORA22 WMT_PIN(5, 22) +#define WMT_PIN_NORA23 WMT_PIN(5, 23) +#define WMT_PIN_NORA24 WMT_PIN(5, 24) +#define WMT_PIN_AC97SDI WMT_PIN(6, 0) +#define WMT_PIN_AC97SYNC WMT_PIN(6, 1) +#define WMT_PIN_AC97SDO WMT_PIN(6, 2) +#define WMT_PIN_AC97BCLK WMT_PIN(6, 3) +#define WMT_PIN_AC97RST WMT_PIN(6, 4) +#define WMT_PIN_SFDO WMT_PIN(7, 0) +#define WMT_PIN_SFCS0 WMT_PIN(7, 1) +#define WMT_PIN_SFCS1 WMT_PIN(7, 2) +#define WMT_PIN_SFCLK WMT_PIN(7, 3) +#define WMT_PIN_SFDI WMT_PIN(7, 4) +#define WMT_PIN_SPI0CLK WMT_PIN(8, 0) +#define WMT_PIN_SPI0MISO WMT_PIN(8, 1) +#define WMT_PIN_SPI0MOSI WMT_PIN(8, 2) +#define WMT_PIN_SPI0SS WMT_PIN(8, 3) +#define WMT_PIN_SPI1CLK WMT_PIN(8, 4) +#define WMT_PIN_SPI1MISO WMT_PIN(8, 5) +#define WMT_PIN_SPI1MOSI WMT_PIN(8, 6) +#define WMT_PIN_SPI1SS WMT_PIN(8, 7) +#define WMT_PIN_SPI2CLK WMT_PIN(8, 8) +#define WMT_PIN_SPI2MISO WMT_PIN(8, 9) +#define WMT_PIN_SPI2MOSI WMT_PIN(8, 10) +#define WMT_PIN_SPI2SS WMT_PIN(8, 11) +#define WMT_PIN_UART0_RTS WMT_PIN(9, 0) +#define WMT_PIN_UART0_TXD WMT_PIN(9, 1) +#define WMT_PIN_UART0_CTS WMT_PIN(9, 2) +#define WMT_PIN_UART0_RXD WMT_PIN(9, 3) +#define WMT_PIN_UART1_RTS WMT_PIN(9, 4) +#define WMT_PIN_UART1_TXD WMT_PIN(9, 5) +#define WMT_PIN_UART1_CTS WMT_PIN(9, 6) +#define WMT_PIN_UART1_RXD WMT_PIN(9, 7) +#define WMT_PIN_UART2_RTS WMT_PIN(9, 8) +#define WMT_PIN_UART2_TXD WMT_PIN(9, 9) +#define WMT_PIN_UART2_CTS WMT_PIN(9, 10) +#define WMT_PIN_UART2_RXD WMT_PIN(9, 11) +#define WMT_PIN_UART3_RTS WMT_PIN(9, 12) +#define WMT_PIN_UART3_TXD WMT_PIN(9, 13) +#define WMT_PIN_UART3_CTS WMT_PIN(9, 14) +#define WMT_PIN_UART3_RXD WMT_PIN(9, 15) +#define WMT_PIN_I2C0SCL WMT_PIN(10, 0) +#define WMT_PIN_I2C0SDA WMT_PIN(10, 1) +#define WMT_PIN_I2C1SCL WMT_PIN(10, 2) +#define WMT_PIN_I2C1SDA WMT_PIN(10, 3) +#define WMT_PIN_I2C2SCL WMT_PIN(10, 4) +#define WMT_PIN_I2C2SDA WMT_PIN(10, 5) + +static const struct pinctrl_pin_desc wm8505_pins[] = { + PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), + PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), + PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), + PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), + PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), + PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), + PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), + PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), + PINCTRL_PIN(WMT_PIN_WAKEUP0, "wakeup0"), + PINCTRL_PIN(WMT_PIN_WAKEUP1, "wakeup1"), + PINCTRL_PIN(WMT_PIN_WAKEUP2, "wakeup2"), + PINCTRL_PIN(WMT_PIN_WAKEUP3, "wakeup3"), + PINCTRL_PIN(WMT_PIN_SUSGPIO0, "susgpio0"), + PINCTRL_PIN(WMT_PIN_SDDATA0, "sd_data0"), + PINCTRL_PIN(WMT_PIN_SDDATA1, "sd_data1"), + PINCTRL_PIN(WMT_PIN_SDDATA2, "sd_data2"), + PINCTRL_PIN(WMT_PIN_SDDATA3, "sd_data3"), + PINCTRL_PIN(WMT_PIN_MMCDATA0, "mmc_data0"), + PINCTRL_PIN(WMT_PIN_MMCDATA1, "mmc_data1"), + PINCTRL_PIN(WMT_PIN_MMCDATA2, "mmc_data2"), + PINCTRL_PIN(WMT_PIN_MMCDATA3, "mmc_data3"), + PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), + PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), + PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), + PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), + PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), + PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), + PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), + PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), + PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), + PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), + PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), + PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), + PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), + PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), + PINCTRL_PIN(WMT_PIN_VDOUT6, "vdout6"), + PINCTRL_PIN(WMT_PIN_VDOUT7, "vdout7"), + PINCTRL_PIN(WMT_PIN_VDOUT8, "vdout8"), + PINCTRL_PIN(WMT_PIN_VDOUT9, "vdout9"), + PINCTRL_PIN(WMT_PIN_VDOUT10, "vdout10"), + PINCTRL_PIN(WMT_PIN_VDOUT11, "vdout11"), + PINCTRL_PIN(WMT_PIN_VDOUT12, "vdout12"), + PINCTRL_PIN(WMT_PIN_VDOUT13, "vdout13"), + PINCTRL_PIN(WMT_PIN_VDOUT14, "vdout14"), + PINCTRL_PIN(WMT_PIN_VDOUT15, "vdout15"), + PINCTRL_PIN(WMT_PIN_VDOUT16, "vdout16"), + PINCTRL_PIN(WMT_PIN_VDOUT17, "vdout17"), + PINCTRL_PIN(WMT_PIN_VDOUT18, "vdout18"), + PINCTRL_PIN(WMT_PIN_VDOUT19, "vdout19"), + PINCTRL_PIN(WMT_PIN_VDOUT20, "vdout20"), + PINCTRL_PIN(WMT_PIN_VDOUT21, "vdout21"), + PINCTRL_PIN(WMT_PIN_VDOUT22, "vdout22"), + PINCTRL_PIN(WMT_PIN_VDOUT23, "vdout23"), + PINCTRL_PIN(WMT_PIN_VHSYNC, "v_hsync"), + PINCTRL_PIN(WMT_PIN_VVSYNC, "v_vsync"), + PINCTRL_PIN(WMT_PIN_VGAHSYNC, "vga_hsync"), + PINCTRL_PIN(WMT_PIN_VGAVSYNC, "vga_vsync"), + PINCTRL_PIN(WMT_PIN_VDHSYNC, "vd_hsync"), + PINCTRL_PIN(WMT_PIN_VDVSYNC, "vd_vsync"), + PINCTRL_PIN(WMT_PIN_NORD0, "nor_d0"), + PINCTRL_PIN(WMT_PIN_NORD1, "nor_d1"), + PINCTRL_PIN(WMT_PIN_NORD2, "nor_d2"), + PINCTRL_PIN(WMT_PIN_NORD3, "nor_d3"), + PINCTRL_PIN(WMT_PIN_NORD4, "nor_d4"), + PINCTRL_PIN(WMT_PIN_NORD5, "nor_d5"), + PINCTRL_PIN(WMT_PIN_NORD6, "nor_d6"), + PINCTRL_PIN(WMT_PIN_NORD7, "nor_d7"), + PINCTRL_PIN(WMT_PIN_NORD8, "nor_d8"), + PINCTRL_PIN(WMT_PIN_NORD9, "nor_d9"), + PINCTRL_PIN(WMT_PIN_NORD10, "nor_d10"), + PINCTRL_PIN(WMT_PIN_NORD11, "nor_d11"), + PINCTRL_PIN(WMT_PIN_NORD12, "nor_d12"), + PINCTRL_PIN(WMT_PIN_NORD13, "nor_d13"), + PINCTRL_PIN(WMT_PIN_NORD14, "nor_d14"), + PINCTRL_PIN(WMT_PIN_NORD15, "nor_d15"), + PINCTRL_PIN(WMT_PIN_NORA0, "nor_a0"), + PINCTRL_PIN(WMT_PIN_NORA1, "nor_a1"), + PINCTRL_PIN(WMT_PIN_NORA2, "nor_a2"), + PINCTRL_PIN(WMT_PIN_NORA3, "nor_a3"), + PINCTRL_PIN(WMT_PIN_NORA4, "nor_a4"), + PINCTRL_PIN(WMT_PIN_NORA5, "nor_a5"), + PINCTRL_PIN(WMT_PIN_NORA6, "nor_a6"), + PINCTRL_PIN(WMT_PIN_NORA7, "nor_a7"), + PINCTRL_PIN(WMT_PIN_NORA8, "nor_a8"), + PINCTRL_PIN(WMT_PIN_NORA9, "nor_a9"), + PINCTRL_PIN(WMT_PIN_NORA10, "nor_a10"), + PINCTRL_PIN(WMT_PIN_NORA11, "nor_a11"), + PINCTRL_PIN(WMT_PIN_NORA12, "nor_a12"), + PINCTRL_PIN(WMT_PIN_NORA13, "nor_a13"), + PINCTRL_PIN(WMT_PIN_NORA14, "nor_a14"), + PINCTRL_PIN(WMT_PIN_NORA15, "nor_a15"), + PINCTRL_PIN(WMT_PIN_NORA16, "nor_a16"), + PINCTRL_PIN(WMT_PIN_NORA17, "nor_a17"), + PINCTRL_PIN(WMT_PIN_NORA18, "nor_a18"), + PINCTRL_PIN(WMT_PIN_NORA19, "nor_a19"), + PINCTRL_PIN(WMT_PIN_NORA20, "nor_a20"), + PINCTRL_PIN(WMT_PIN_NORA21, "nor_a21"), + PINCTRL_PIN(WMT_PIN_NORA22, "nor_a22"), + PINCTRL_PIN(WMT_PIN_NORA23, "nor_a23"), + PINCTRL_PIN(WMT_PIN_NORA24, "nor_a24"), + PINCTRL_PIN(WMT_PIN_AC97SDI, "ac97_sdi"), + PINCTRL_PIN(WMT_PIN_AC97SYNC, "ac97_sync"), + PINCTRL_PIN(WMT_PIN_AC97SDO, "ac97_sdo"), + PINCTRL_PIN(WMT_PIN_AC97BCLK, "ac97_bclk"), + PINCTRL_PIN(WMT_PIN_AC97RST, "ac97_rst"), + PINCTRL_PIN(WMT_PIN_SFDO, "sf_do"), + PINCTRL_PIN(WMT_PIN_SFCS0, "sf_cs0"), + PINCTRL_PIN(WMT_PIN_SFCS1, "sf_cs1"), + PINCTRL_PIN(WMT_PIN_SFCLK, "sf_clk"), + PINCTRL_PIN(WMT_PIN_SFDI, "sf_di"), + PINCTRL_PIN(WMT_PIN_SPI0CLK, "spi0_clk"), + PINCTRL_PIN(WMT_PIN_SPI0MISO, "spi0_miso"), + PINCTRL_PIN(WMT_PIN_SPI0MOSI, "spi0_mosi"), + PINCTRL_PIN(WMT_PIN_SPI0SS, "spi0_ss"), + PINCTRL_PIN(WMT_PIN_SPI1CLK, "spi1_clk"), + PINCTRL_PIN(WMT_PIN_SPI1MISO, "spi1_miso"), + PINCTRL_PIN(WMT_PIN_SPI1MOSI, "spi1_mosi"), + PINCTRL_PIN(WMT_PIN_SPI1SS, "spi1_ss"), + PINCTRL_PIN(WMT_PIN_SPI2CLK, "spi2_clk"), + PINCTRL_PIN(WMT_PIN_SPI2MISO, "spi2_miso"), + PINCTRL_PIN(WMT_PIN_SPI2MOSI, "spi2_mosi"), + PINCTRL_PIN(WMT_PIN_SPI2SS, "spi2_ss"), + PINCTRL_PIN(WMT_PIN_UART0_RTS, "uart0_rts"), + PINCTRL_PIN(WMT_PIN_UART0_TXD, "uart0_txd"), + PINCTRL_PIN(WMT_PIN_UART0_CTS, "uart0_cts"), + PINCTRL_PIN(WMT_PIN_UART0_RXD, "uart0_rxd"), + PINCTRL_PIN(WMT_PIN_UART1_RTS, "uart1_rts"), + PINCTRL_PIN(WMT_PIN_UART1_TXD, "uart1_txd"), + PINCTRL_PIN(WMT_PIN_UART1_CTS, "uart1_cts"), + PINCTRL_PIN(WMT_PIN_UART1_RXD, "uart1_rxd"), + PINCTRL_PIN(WMT_PIN_UART2_RTS, "uart2_rts"), + PINCTRL_PIN(WMT_PIN_UART2_TXD, "uart2_txd"), + PINCTRL_PIN(WMT_PIN_UART2_CTS, "uart2_cts"), + PINCTRL_PIN(WMT_PIN_UART2_RXD, "uart2_rxd"), + PINCTRL_PIN(WMT_PIN_UART3_RTS, "uart3_rts"), + PINCTRL_PIN(WMT_PIN_UART3_TXD, "uart3_txd"), + PINCTRL_PIN(WMT_PIN_UART3_CTS, "uart3_cts"), + PINCTRL_PIN(WMT_PIN_UART3_RXD, "uart3_rxd"), + PINCTRL_PIN(WMT_PIN_I2C0SCL, "i2c0_scl"), + PINCTRL_PIN(WMT_PIN_I2C0SDA, "i2c0_sda"), + PINCTRL_PIN(WMT_PIN_I2C1SCL, "i2c1_scl"), + PINCTRL_PIN(WMT_PIN_I2C1SDA, "i2c1_sda"), + PINCTRL_PIN(WMT_PIN_I2C2SCL, "i2c2_scl"), + PINCTRL_PIN(WMT_PIN_I2C2SDA, "i2c2_sda"), +}; + +/* Order of these names must match the above list */ +static const char * const wm8505_groups[] = { + "extgpio0", + "extgpio1", + "extgpio2", + "extgpio3", + "extgpio4", + "extgpio5", + "extgpio6", + "extgpio7", + "wakeup0", + "wakeup1", + "wakeup2", + "wakeup3", + "susgpio0", + "sd_data0", + "sd_data1", + "sd_data2", + "sd_data3", + "mmc_data0", + "mmc_data1", + "mmc_data2", + "mmc_data3", + "vdin0", + "vdin1", + "vdin2", + "vdin3", + "vdin4", + "vdin5", + "vdin6", + "vdin7", + "vdout0", + "vdout1", + "vdout2", + "vdout3", + "vdout4", + "vdout5", + "vdout6", + "vdout7", + "vdout8", + "vdout9", + "vdout10", + "vdout11", + "vdout12", + "vdout13", + "vdout14", + "vdout15", + "vdout16", + "vdout17", + "vdout18", + "vdout19", + "vdout20", + "vdout21", + "vdout22", + "vdout23", + "v_hsync", + "v_vsync", + "vga_hsync", + "vga_vsync", + "vd_hsync", + "vd_vsync", + "nor_d0", + "nor_d1", + "nor_d2", + "nor_d3", + "nor_d4", + "nor_d5", + "nor_d6", + "nor_d7", + "nor_d8", + "nor_d9", + "nor_d10", + "nor_d11", + "nor_d12", + "nor_d13", + "nor_d14", + "nor_d15", + "nor_a0", + "nor_a1", + "nor_a2", + "nor_a3", + "nor_a4", + "nor_a5", + "nor_a6", + "nor_a7", + "nor_a8", + "nor_a9", + "nor_a10", + "nor_a11", + "nor_a12", + "nor_a13", + "nor_a14", + "nor_a15", + "nor_a16", + "nor_a17", + "nor_a18", + "nor_a19", + "nor_a20", + "nor_a21", + "nor_a22", + "nor_a23", + "nor_a24", + "ac97_sdi", + "ac97_sync", + "ac97_sdo", + "ac97_bclk", + "ac97_rst", + "sf_do", + "sf_cs0", + "sf_cs1", + "sf_clk", + "sf_di", + "spi0_clk", + "spi0_miso", + "spi0_mosi", + "spi0_ss", + "spi1_clk", + "spi1_miso", + "spi1_mosi", + "spi1_ss", + "spi2_clk", + "spi2_miso", + "spi2_mosi", + "spi2_ss", + "uart0_rts", + "uart0_txd", + "uart0_cts", + "uart0_rxd", + "uart1_rts", + "uart1_txd", + "uart1_cts", + "uart1_rxd", + "uart2_rts", + "uart2_txd", + "uart2_cts", + "uart2_rxd", + "uart3_rts", + "uart3_txd", + "uart3_cts", + "uart3_rxd", + "i2c0_scl", + "i2c0_sda", + "i2c1_scl", + "i2c1_sda", + "i2c2_scl", + "i2c2_sda", +}; + +static int wm8505_pinctrl_probe(struct platform_device *pdev) +{ + struct wmt_pinctrl_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "failed to allocate data\n"); + return -ENOMEM; + } + + data->banks = wm8505_banks; + data->nbanks = ARRAY_SIZE(wm8505_banks); + data->pins = wm8505_pins; + data->npins = ARRAY_SIZE(wm8505_pins); + data->groups = wm8505_groups; + data->ngroups = ARRAY_SIZE(wm8505_groups); + + return wmt_pinctrl_probe(pdev, data); +} + +static int wm8505_pinctrl_remove(struct platform_device *pdev) +{ + return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { + { .compatible = "wm,wm8505-pinctrl" }, + { /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { + .probe = wm8505_pinctrl_probe, + .remove = wm8505_pinctrl_remove, + .driver = { + .name = "pinctrl-wm8505", + .owner = THIS_MODULE, + .of_match_table = wmt_pinctrl_of_match, + }, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk "); +MODULE_DESCRIPTION("Wondermedia WM8505 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8650.c b/drivers/pinctrl/vt8500/pinctrl-wm8650.c new file mode 100644 index 0000000..7de57f0 --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wm8650.c @@ -0,0 +1,370 @@ +/* + * Pinctrl data for Wondermedia WM8650 SoC + * + * Copyright (c) 2013 Tony Prisk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers wm8650_banks[] = { + WMT_PINCTRL_BANK(0x40, 0x80, 0xC0, 0x00, 0x480, 0x4C0), /* 0 */ + WMT_PINCTRL_BANK(0x44, 0x84, 0xC4, 0x04, 0x484, 0x4C4), /* 1 */ + WMT_PINCTRL_BANK(0x48, 0x88, 0xC8, 0x08, 0x488, 0x4C8), /* 2 */ + WMT_PINCTRL_BANK(0x4C, 0x8C, 0xCC, 0x0C, 0x48C, 0x4CC), /* 3 */ + WMT_PINCTRL_BANK(0x50, 0x90, 0xD0, 0x10, 0x490, 0x4D0), /* 4 */ + WMT_PINCTRL_BANK(0x54, 0x94, 0xD4, 0x14, 0x494, 0x4D4), /* 5 */ + WMT_PINCTRL_BANK(0x58, 0x98, 0xD8, 0x18, 0x498, 0x4D8), /* 6 */ + WMT_PINCTRL_BANK(0x5C, 0x9C, 0xDC, 0x1C, 0x49C, 0x4DC), /* 7 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0 WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1 WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2 WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3 WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4 WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5 WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6 WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7 WMT_PIN(0, 7) +#define WMT_PIN_WAKEUP0 WMT_PIN(0, 16) +#define WMT_PIN_WAKEUP1 WMT_PIN(0, 17) +#define WMT_PIN_SUSGPIO0 WMT_PIN(0, 21) +#define WMT_PIN_SD0CD WMT_PIN(0, 28) +#define WMT_PIN_SD1CD WMT_PIN(0, 29) +#define WMT_PIN_VDOUT0 WMT_PIN(1, 0) +#define WMT_PIN_VDOUT1 WMT_PIN(1, 1) +#define WMT_PIN_VDOUT2 WMT_PIN(1, 2) +#define WMT_PIN_VDOUT3 WMT_PIN(1, 3) +#define WMT_PIN_VDOUT4 WMT_PIN(1, 4) +#define WMT_PIN_VDOUT5 WMT_PIN(1, 5) +#define WMT_PIN_VDOUT6 WMT_PIN(1, 6) +#define WMT_PIN_VDOUT7 WMT_PIN(1, 7) +#define WMT_PIN_VDOUT8 WMT_PIN(1, 8) +#define WMT_PIN_VDOUT9 WMT_PIN(1, 9) +#define WMT_PIN_VDOUT10 WMT_PIN(1, 10) +#define WMT_PIN_VDOUT11 WMT_PIN(1, 11) +#define WMT_PIN_VDOUT12 WMT_PIN(1, 12) +#define WMT_PIN_VDOUT13 WMT_PIN(1, 13) +#define WMT_PIN_VDOUT14 WMT_PIN(1, 14) +#define WMT_PIN_VDOUT15 WMT_PIN(1, 15) +#define WMT_PIN_VDOUT16 WMT_PIN(1, 16) +#define WMT_PIN_VDOUT17 WMT_PIN(1, 17) +#define WMT_PIN_VDOUT18 WMT_PIN(1, 18) +#define WMT_PIN_VDOUT19 WMT_PIN(1, 19) +#define WMT_PIN_VDOUT20 WMT_PIN(1, 20) +#define WMT_PIN_VDOUT21 WMT_PIN(1, 21) +#define WMT_PIN_VDOUT22 WMT_PIN(1, 22) +#define WMT_PIN_VDOUT23 WMT_PIN(1, 23) +#define WMT_PIN_VDIN0 WMT_PIN(2, 0) +#define WMT_PIN_VDIN1 WMT_PIN(2, 1) +#define WMT_PIN_VDIN2 WMT_PIN(2, 2) +#define WMT_PIN_VDIN3 WMT_PIN(2, 3) +#define WMT_PIN_VDIN4 WMT_PIN(2, 4) +#define WMT_PIN_VDIN5 WMT_PIN(2, 5) +#define WMT_PIN_VDIN6 WMT_PIN(2, 6) +#define WMT_PIN_VDIN7 WMT_PIN(2, 7) +#define WMT_PIN_I2C1SCL WMT_PIN(2, 12) +#define WMT_PIN_I2C1SDA WMT_PIN(2, 13) +#define WMT_PIN_SPI0MOSI WMT_PIN(2, 24) +#define WMT_PIN_SPI0MISO WMT_PIN(2, 25) +#define WMT_PIN_SPI0SS0 WMT_PIN(2, 26) +#define WMT_PIN_SPI0CLK WMT_PIN(2, 27) +#define WMT_PIN_SD0DATA0 WMT_PIN(3, 8) +#define WMT_PIN_SD0DATA1 WMT_PIN(3, 9) +#define WMT_PIN_SD0DATA2 WMT_PIN(3, 10) +#define WMT_PIN_SD0DATA3 WMT_PIN(3, 11) +#define WMT_PIN_SD0CLK WMT_PIN(3, 12) +#define WMT_PIN_SD0WP WMT_PIN(3, 13) +#define WMT_PIN_SD0CMD WMT_PIN(3, 14) +#define WMT_PIN_SD1DATA0 WMT_PIN(3, 24) +#define WMT_PIN_SD1DATA1 WMT_PIN(3, 25) +#define WMT_PIN_SD1DATA2 WMT_PIN(3, 26) +#define WMT_PIN_SD1DATA3 WMT_PIN(3, 27) +#define WMT_PIN_SD1DATA4 WMT_PIN(3, 28) +#define WMT_PIN_SD1DATA5 WMT_PIN(3, 29) +#define WMT_PIN_SD1DATA6 WMT_PIN(3, 30) +#define WMT_PIN_SD1DATA7 WMT_PIN(3, 31) +#define WMT_PIN_I2C0SCL WMT_PIN(5, 8) +#define WMT_PIN_I2C0SDA WMT_PIN(5, 9) +#define WMT_PIN_UART0RTS WMT_PIN(5, 16) +#define WMT_PIN_UART0TXD WMT_PIN(5, 17) +#define WMT_PIN_UART0CTS WMT_PIN(5, 18) +#define WMT_PIN_UART0RXD WMT_PIN(5, 19) +#define WMT_PIN_UART1RTS WMT_PIN(5, 20) +#define WMT_PIN_UART1TXD WMT_PIN(5, 21) +#define WMT_PIN_UART1CTS WMT_PIN(5, 22) +#define WMT_PIN_UART1RXD WMT_PIN(5, 23) +#define WMT_PIN_UART2RTS WMT_PIN(5, 24) +#define WMT_PIN_UART2TXD WMT_PIN(5, 25) +#define WMT_PIN_UART2CTS WMT_PIN(5, 26) +#define WMT_PIN_UART2RXD WMT_PIN(5, 27) +#define WMT_PIN_UART3RTS WMT_PIN(5, 28) +#define WMT_PIN_UART3TXD WMT_PIN(5, 29) +#define WMT_PIN_UART3CTS WMT_PIN(5, 30) +#define WMT_PIN_UART3RXD WMT_PIN(5, 31) +#define WMT_PIN_KPADROW0 WMT_PIN(6, 16) +#define WMT_PIN_KPADROW1 WMT_PIN(6, 17) +#define WMT_PIN_KPADCOL0 WMT_PIN(6, 18) +#define WMT_PIN_KPADCOL1 WMT_PIN(6, 19) +#define WMT_PIN_SD1CLK WMT_PIN(7, 0) +#define WMT_PIN_SD1CMD WMT_PIN(7, 1) +#define WMT_PIN_SD1WP WMT_PIN(7, 13) + +static const struct pinctrl_pin_desc wm8650_pins[] = { + PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), + PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), + PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), + PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), + PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), + PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), + PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), + PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), + PINCTRL_PIN(WMT_PIN_WAKEUP0, "wakeup0"), + PINCTRL_PIN(WMT_PIN_WAKEUP1, "wakeup1"), + PINCTRL_PIN(WMT_PIN_SUSGPIO0, "susgpio0"), + PINCTRL_PIN(WMT_PIN_SD0CD, "sd0_cd"), + PINCTRL_PIN(WMT_PIN_SD1CD, "sd1_cd"), + PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), + PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), + PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), + PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), + PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), + PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), + PINCTRL_PIN(WMT_PIN_VDOUT6, "vdout6"), + PINCTRL_PIN(WMT_PIN_VDOUT7, "vdout7"), + PINCTRL_PIN(WMT_PIN_VDOUT8, "vdout8"), + PINCTRL_PIN(WMT_PIN_VDOUT9, "vdout9"), + PINCTRL_PIN(WMT_PIN_VDOUT10, "vdout10"), + PINCTRL_PIN(WMT_PIN_VDOUT11, "vdout11"), + PINCTRL_PIN(WMT_PIN_VDOUT12, "vdout12"), + PINCTRL_PIN(WMT_PIN_VDOUT13, "vdout13"), + PINCTRL_PIN(WMT_PIN_VDOUT14, "vdout14"), + PINCTRL_PIN(WMT_PIN_VDOUT15, "vdout15"), + PINCTRL_PIN(WMT_PIN_VDOUT16, "vdout16"), + PINCTRL_PIN(WMT_PIN_VDOUT17, "vdout17"), + PINCTRL_PIN(WMT_PIN_VDOUT18, "vdout18"), + PINCTRL_PIN(WMT_PIN_VDOUT19, "vdout19"), + PINCTRL_PIN(WMT_PIN_VDOUT20, "vdout20"), + PINCTRL_PIN(WMT_PIN_VDOUT21, "vdout21"), + PINCTRL_PIN(WMT_PIN_VDOUT22, "vdout22"), + PINCTRL_PIN(WMT_PIN_VDOUT23, "vdout23"), + PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), + PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), + PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), + PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), + PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), + PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), + PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), + PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), + PINCTRL_PIN(WMT_PIN_I2C1SCL, "i2c1_scl"), + PINCTRL_PIN(WMT_PIN_I2C1SDA, "i2c1_sda"), + PINCTRL_PIN(WMT_PIN_SPI0MOSI, "spi0_mosi"), + PINCTRL_PIN(WMT_PIN_SPI0MISO, "spi0_miso"), + PINCTRL_PIN(WMT_PIN_SPI0SS0, "spi0_ss0"), + PINCTRL_PIN(WMT_PIN_SPI0CLK, "spi0_clk"), + PINCTRL_PIN(WMT_PIN_SD0DATA0, "sd0_data0"), + PINCTRL_PIN(WMT_PIN_SD0DATA1, "sd0_data1"), + PINCTRL_PIN(WMT_PIN_SD0DATA2, "sd0_data2"), + PINCTRL_PIN(WMT_PIN_SD0DATA3, "sd0_data3"), + PINCTRL_PIN(WMT_PIN_SD0CLK, "sd0_clk"), + PINCTRL_PIN(WMT_PIN_SD0WP, "sd0_wp"), + PINCTRL_PIN(WMT_PIN_SD0CMD, "sd0_cmd"), + PINCTRL_PIN(WMT_PIN_SD1DATA0, "sd1_data0"), + PINCTRL_PIN(WMT_PIN_SD1DATA1, "sd1_data1"), + PINCTRL_PIN(WMT_PIN_SD1DATA2, "sd1_data2"), + PINCTRL_PIN(WMT_PIN_SD1DATA3, "sd1_data3"), + PINCTRL_PIN(WMT_PIN_SD1DATA4, "sd1_data4"), + PINCTRL_PIN(WMT_PIN_SD1DATA5, "sd1_data5"), + PINCTRL_PIN(WMT_PIN_SD1DATA6, "sd1_data6"), + PINCTRL_PIN(WMT_PIN_SD1DATA7, "sd1_data7"), + PINCTRL_PIN(WMT_PIN_I2C0SCL, "i2c0_scl"), + PINCTRL_PIN(WMT_PIN_I2C0SDA, "i2c0_sda"), + PINCTRL_PIN(WMT_PIN_UART0RTS, "uart0_rts"), + PINCTRL_PIN(WMT_PIN_UART0TXD, "uart0_txd"), + PINCTRL_PIN(WMT_PIN_UART0CTS, "uart0_cts"), + PINCTRL_PIN(WMT_PIN_UART0RXD, "uart0_rxd"), + PINCTRL_PIN(WMT_PIN_UART1RTS, "uart1_rts"), + PINCTRL_PIN(WMT_PIN_UART1TXD, "uart1_txd"), + PINCTRL_PIN(WMT_PIN_UART1CTS, "uart1_cts"), + PINCTRL_PIN(WMT_PIN_UART1RXD, "uart1_rxd"), + PINCTRL_PIN(WMT_PIN_UART2RTS, "uart2_rts"), + PINCTRL_PIN(WMT_PIN_UART2TXD, "uart2_txd"), + PINCTRL_PIN(WMT_PIN_UART2CTS, "uart2_cts"), + PINCTRL_PIN(WMT_PIN_UART2RXD, "uart2_rxd"), + PINCTRL_PIN(WMT_PIN_UART3RTS, "uart3_rts"), + PINCTRL_PIN(WMT_PIN_UART3TXD, "uart3_txd"), + PINCTRL_PIN(WMT_PIN_UART3CTS, "uart3_cts"), + PINCTRL_PIN(WMT_PIN_UART3RXD, "uart3_rxd"), + PINCTRL_PIN(WMT_PIN_KPADROW0, "kpadrow0"), + PINCTRL_PIN(WMT_PIN_KPADROW1, "kpadrow1"), + PINCTRL_PIN(WMT_PIN_KPADCOL0, "kpadcol0"), + PINCTRL_PIN(WMT_PIN_KPADCOL1, "kpadcol1"), + PINCTRL_PIN(WMT_PIN_SD1CLK, "sd1_clk"), + PINCTRL_PIN(WMT_PIN_SD1CMD, "sd1_cmd"), + PINCTRL_PIN(WMT_PIN_SD1WP, "sd1_wp"), +}; + +/* Order of these names must match the above list */ +static const char * const wm8650_groups[] = { + "extgpio0", + "extgpio1", + "extgpio2", + "extgpio3", + "extgpio4", + "extgpio5", + "extgpio6", + "extgpio7", + "wakeup0", + "wakeup1", + "susgpio0", + "sd0_cd", + "sd1_cd", + "vdout0", + "vdout1", + "vdout2", + "vdout3", + "vdout4", + "vdout5", + "vdout6", + "vdout7", + "vdout8", + "vdout9", + "vdout10", + "vdout11", + "vdout12", + "vdout13", + "vdout14", + "vdout15", + "vdout16", + "vdout17", + "vdout18", + "vdout19", + "vdout20", + "vdout21", + "vdout22", + "vdout23", + "vdin0", + "vdin1", + "vdin2", + "vdin3", + "vdin4", + "vdin5", + "vdin6", + "vdin7", + "i2c1_scl", + "i2c1_sda", + "spi0_mosi", + "spi0_miso", + "spi0_ss0", + "spi0_clk", + "sd0_data0", + "sd0_data1", + "sd0_data2", + "sd0_data3", + "sd0_clk", + "sd0_wp", + "sd0_cmd", + "sd1_data0", + "sd1_data1", + "sd1_data2", + "sd1_data3", + "sd1_data4", + "sd1_data5", + "sd1_data6", + "sd1_data7", + "i2c0_scl", + "i2c0_sda", + "uart0_rts", + "uart0_txd", + "uart0_cts", + "uart0_rxd", + "uart1_rts", + "uart1_txd", + "uart1_cts", + "uart1_rxd", + "uart2_rts", + "uart2_txd", + "uart2_cts", + "uart2_rxd", + "uart3_rts", + "uart3_txd", + "uart3_cts", + "uart3_rxd", + "kpadrow0", + "kpadrow1", + "kpadcol0", + "kpadcol1", + "sd1_clk", + "sd1_cmd", + "sd1_wp", +}; + +static int wm8650_pinctrl_probe(struct platform_device *pdev) +{ + struct wmt_pinctrl_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "failed to allocate data\n"); + return -ENOMEM; + } + + data->banks = wm8650_banks; + data->nbanks = ARRAY_SIZE(wm8650_banks); + data->pins = wm8650_pins; + data->npins = ARRAY_SIZE(wm8650_pins); + data->groups = wm8650_groups; + data->ngroups = ARRAY_SIZE(wm8650_groups); + + return wmt_pinctrl_probe(pdev, data); +} + +static int wm8650_pinctrl_remove(struct platform_device *pdev) +{ + return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { + { .compatible = "wm,wm8650-pinctrl" }, + { /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { + .probe = wm8650_pinctrl_probe, + .remove = wm8650_pinctrl_remove, + .driver = { + .name = "pinctrl-wm8650", + .owner = THIS_MODULE, + .of_match_table = wmt_pinctrl_of_match, + }, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk "); +MODULE_DESCRIPTION("Wondermedia WM8650 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8750.c b/drivers/pinctrl/vt8500/pinctrl-wm8750.c new file mode 100644 index 0000000..b964cc5 --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wm8750.c @@ -0,0 +1,409 @@ +/* + * Pinctrl data for Wondermedia WM8750 SoC + * + * Copyright (c) 2013 Tony Prisk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers wm8750_banks[] = { + WMT_PINCTRL_BANK(0x40, 0x80, 0xC0, 0x00, 0x480, 0x4C0), /* 0 */ + WMT_PINCTRL_BANK(0x44, 0x84, 0xC4, 0x04, 0x484, 0x4C4), /* 1 */ + WMT_PINCTRL_BANK(0x48, 0x88, 0xC8, 0x08, 0x488, 0x4C8), /* 2 */ + WMT_PINCTRL_BANK(0x4C, 0x8C, 0xCC, 0x0C, 0x48C, 0x4CC), /* 3 */ + WMT_PINCTRL_BANK(0x50, 0x90, 0xD0, 0x10, 0x490, 0x4D0), /* 4 */ + WMT_PINCTRL_BANK(0x54, 0x94, 0xD4, 0x14, 0x494, 0x4D4), /* 5 */ + WMT_PINCTRL_BANK(0x58, 0x98, 0xD8, 0x18, 0x498, 0x4D8), /* 6 */ + WMT_PINCTRL_BANK(0x5C, 0x9C, 0xDC, 0x1C, 0x49C, 0x4DC), /* 7 */ + WMT_PINCTRL_BANK(0x60, 0xA0, 0xE0, 0x20, 0x4A0, 0x4E0), /* 8 */ + WMT_PINCTRL_BANK(0x70, 0xB0, 0xF0, 0x30, 0x4B0, 0x4F0), /* 9 */ + WMT_PINCTRL_BANK(0x7C, 0xBC, 0xDC, 0x3C, 0x4BC, 0x4FC), /* 10 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0 WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1 WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2 WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3 WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4 WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5 WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6 WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7 WMT_PIN(0, 7) +#define WMT_PIN_WAKEUP0 WMT_PIN(0, 16) +#define WMT_PIN_WAKEUP1 WMT_PIN(0, 16) +#define WMT_PIN_SD0CD WMT_PIN(0, 28) +#define WMT_PIN_VDOUT0 WMT_PIN(1, 0) +#define WMT_PIN_VDOUT1 WMT_PIN(1, 1) +#define WMT_PIN_VDOUT2 WMT_PIN(1, 2) +#define WMT_PIN_VDOUT3 WMT_PIN(1, 3) +#define WMT_PIN_VDOUT4 WMT_PIN(1, 4) +#define WMT_PIN_VDOUT5 WMT_PIN(1, 5) +#define WMT_PIN_VDOUT6 WMT_PIN(1, 6) +#define WMT_PIN_VDOUT7 WMT_PIN(1, 7) +#define WMT_PIN_VDOUT8 WMT_PIN(1, 8) +#define WMT_PIN_VDOUT9 WMT_PIN(1, 9) +#define WMT_PIN_VDOUT10 WMT_PIN(1, 10) +#define WMT_PIN_VDOUT11 WMT_PIN(1, 11) +#define WMT_PIN_VDOUT12 WMT_PIN(1, 12) +#define WMT_PIN_VDOUT13 WMT_PIN(1, 13) +#define WMT_PIN_VDOUT14 WMT_PIN(1, 14) +#define WMT_PIN_VDOUT15 WMT_PIN(1, 15) +#define WMT_PIN_VDOUT16 WMT_PIN(1, 16) +#define WMT_PIN_VDOUT17 WMT_PIN(1, 17) +#define WMT_PIN_VDOUT18 WMT_PIN(1, 18) +#define WMT_PIN_VDOUT19 WMT_PIN(1, 19) +#define WMT_PIN_VDOUT20 WMT_PIN(1, 20) +#define WMT_PIN_VDOUT21 WMT_PIN(1, 21) +#define WMT_PIN_VDOUT22 WMT_PIN(1, 22) +#define WMT_PIN_VDOUT23 WMT_PIN(1, 23) +#define WMT_PIN_VDIN0 WMT_PIN(2, 0) +#define WMT_PIN_VDIN1 WMT_PIN(2, 1) +#define WMT_PIN_VDIN2 WMT_PIN(2, 2) +#define WMT_PIN_VDIN3 WMT_PIN(2, 3) +#define WMT_PIN_VDIN4 WMT_PIN(2, 4) +#define WMT_PIN_VDIN5 WMT_PIN(2, 5) +#define WMT_PIN_VDIN6 WMT_PIN(2, 6) +#define WMT_PIN_VDIN7 WMT_PIN(2, 7) +#define WMT_PIN_SPI0_MOSI WMT_PIN(2, 24) +#define WMT_PIN_SPI0_MISO WMT_PIN(2, 25) +#define WMT_PIN_SPI0_SS WMT_PIN(2, 26) +#define WMT_PIN_SPI0_CLK WMT_PIN(2, 27) +#define WMT_PIN_SPI0_SSB WMT_PIN(2, 28) +#define WMT_PIN_SD0CLK WMT_PIN(3, 17) +#define WMT_PIN_SD0CMD WMT_PIN(3, 18) +#define WMT_PIN_SD0WP WMT_PIN(3, 19) +#define WMT_PIN_SD0DATA0 WMT_PIN(3, 20) +#define WMT_PIN_SD0DATA1 WMT_PIN(3, 21) +#define WMT_PIN_SD0DATA2 WMT_PIN(3, 22) +#define WMT_PIN_SD0DATA3 WMT_PIN(3, 23) +#define WMT_PIN_SD1DATA0 WMT_PIN(3, 24) +#define WMT_PIN_SD1DATA1 WMT_PIN(3, 25) +#define WMT_PIN_SD1DATA2 WMT_PIN(3, 26) +#define WMT_PIN_SD1DATA3 WMT_PIN(3, 27) +#define WMT_PIN_SD1DATA4 WMT_PIN(3, 28) +#define WMT_PIN_SD1DATA5 WMT_PIN(3, 29) +#define WMT_PIN_SD1DATA6 WMT_PIN(3, 30) +#define WMT_PIN_SD1DATA7 WMT_PIN(3, 31) +#define WMT_PIN_I2C0_SCL WMT_PIN(5, 8) +#define WMT_PIN_I2C0_SDA WMT_PIN(5, 9) +#define WMT_PIN_I2C1_SCL WMT_PIN(5, 10) +#define WMT_PIN_I2C1_SDA WMT_PIN(5, 11) +#define WMT_PIN_I2C2_SCL WMT_PIN(5, 12) +#define WMT_PIN_I2C2_SDA WMT_PIN(5, 13) +#define WMT_PIN_UART0_RTS WMT_PIN(5, 16) +#define WMT_PIN_UART0_TXD WMT_PIN(5, 17) +#define WMT_PIN_UART0_CTS WMT_PIN(5, 18) +#define WMT_PIN_UART0_RXD WMT_PIN(5, 19) +#define WMT_PIN_UART1_RTS WMT_PIN(5, 20) +#define WMT_PIN_UART1_TXD WMT_PIN(5, 21) +#define WMT_PIN_UART1_CTS WMT_PIN(5, 22) +#define WMT_PIN_UART1_RXD WMT_PIN(5, 23) +#define WMT_PIN_UART2_RTS WMT_PIN(5, 24) +#define WMT_PIN_UART2_TXD WMT_PIN(5, 25) +#define WMT_PIN_UART2_CTS WMT_PIN(5, 26) +#define WMT_PIN_UART2_RXD WMT_PIN(5, 27) +#define WMT_PIN_UART3_RTS WMT_PIN(5, 28) +#define WMT_PIN_UART3_TXD WMT_PIN(5, 29) +#define WMT_PIN_UART3_CTS WMT_PIN(5, 30) +#define WMT_PIN_UART3_RXD WMT_PIN(5, 31) +#define WMT_PIN_SD2CD WMT_PIN(6, 0) +#define WMT_PIN_SD2DATA3 WMT_PIN(6, 1) +#define WMT_PIN_SD2DATA0 WMT_PIN(6, 2) +#define WMT_PIN_SD2WP WMT_PIN(6, 3) +#define WMT_PIN_SD2DATA1 WMT_PIN(6, 4) +#define WMT_PIN_SD2DATA2 WMT_PIN(6, 5) +#define WMT_PIN_SD2CMD WMT_PIN(6, 6) +#define WMT_PIN_SD2CLK WMT_PIN(6, 7) +#define WMT_PIN_SD2PWR WMT_PIN(6, 9) +#define WMT_PIN_SD1CLK WMT_PIN(7, 0) +#define WMT_PIN_SD1CMD WMT_PIN(7, 1) +#define WMT_PIN_SD1PWR WMT_PIN(7, 10) +#define WMT_PIN_SD1WP WMT_PIN(7, 11) +#define WMT_PIN_SD1CD WMT_PIN(7, 12) +#define WMT_PIN_SPI0SS3 WMT_PIN(7, 24) +#define WMT_PIN_SPI0SS2 WMT_PIN(7, 25) +#define WMT_PIN_PWMOUT1 WMT_PIN(7, 26) +#define WMT_PIN_PWMOUT0 WMT_PIN(7, 27) + +static const struct pinctrl_pin_desc wm8750_pins[] = { + PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), + PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), + PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), + PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), + PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), + PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), + PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), + PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), + PINCTRL_PIN(WMT_PIN_WAKEUP0, "wakeup0"), + PINCTRL_PIN(WMT_PIN_WAKEUP1, "wakeup1"), + PINCTRL_PIN(WMT_PIN_SD0CD, "sd0_cd"), + PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), + PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), + PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), + PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), + PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), + PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), + PINCTRL_PIN(WMT_PIN_VDOUT6, "vdout6"), + PINCTRL_PIN(WMT_PIN_VDOUT7, "vdout7"), + PINCTRL_PIN(WMT_PIN_VDOUT8, "vdout8"), + PINCTRL_PIN(WMT_PIN_VDOUT9, "vdout9"), + PINCTRL_PIN(WMT_PIN_VDOUT10, "vdout10"), + PINCTRL_PIN(WMT_PIN_VDOUT11, "vdout11"), + PINCTRL_PIN(WMT_PIN_VDOUT12, "vdout12"), + PINCTRL_PIN(WMT_PIN_VDOUT13, "vdout13"), + PINCTRL_PIN(WMT_PIN_VDOUT14, "vdout14"), + PINCTRL_PIN(WMT_PIN_VDOUT15, "vdout15"), + PINCTRL_PIN(WMT_PIN_VDOUT16, "vdout16"), + PINCTRL_PIN(WMT_PIN_VDOUT17, "vdout17"), + PINCTRL_PIN(WMT_PIN_VDOUT18, "vdout18"), + PINCTRL_PIN(WMT_PIN_VDOUT19, "vdout19"), + PINCTRL_PIN(WMT_PIN_VDOUT20, "vdout20"), + PINCTRL_PIN(WMT_PIN_VDOUT21, "vdout21"), + PINCTRL_PIN(WMT_PIN_VDOUT22, "vdout22"), + PINCTRL_PIN(WMT_PIN_VDOUT23, "vdout23"), + PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), + PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), + PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), + PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), + PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), + PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), + PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), + PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), + PINCTRL_PIN(WMT_PIN_SPI0_MOSI, "spi0_mosi"), + PINCTRL_PIN(WMT_PIN_SPI0_MISO, "spi0_miso"), + PINCTRL_PIN(WMT_PIN_SPI0_SS, "spi0_ss"), + PINCTRL_PIN(WMT_PIN_SPI0_CLK, "spi0_clk"), + PINCTRL_PIN(WMT_PIN_SPI0_SSB, "spi0_ssb"), + PINCTRL_PIN(WMT_PIN_SD0CLK, "sd0_clk"), + PINCTRL_PIN(WMT_PIN_SD0CMD, "sd0_cmd"), + PINCTRL_PIN(WMT_PIN_SD0WP, "sd0_wp"), + PINCTRL_PIN(WMT_PIN_SD0DATA0, "sd0_data0"), + PINCTRL_PIN(WMT_PIN_SD0DATA1, "sd0_data1"), + PINCTRL_PIN(WMT_PIN_SD0DATA2, "sd0_data2"), + PINCTRL_PIN(WMT_PIN_SD0DATA3, "sd0_data3"), + PINCTRL_PIN(WMT_PIN_SD1DATA0, "sd1_data0"), + PINCTRL_PIN(WMT_PIN_SD1DATA1, "sd1_data1"), + PINCTRL_PIN(WMT_PIN_SD1DATA2, "sd1_data2"), + PINCTRL_PIN(WMT_PIN_SD1DATA3, "sd1_data3"), + PINCTRL_PIN(WMT_PIN_SD1DATA4, "sd1_data4"), + PINCTRL_PIN(WMT_PIN_SD1DATA5, "sd1_data5"), + PINCTRL_PIN(WMT_PIN_SD1DATA6, "sd1_data6"), + PINCTRL_PIN(WMT_PIN_SD1DATA7, "sd1_data7"), + PINCTRL_PIN(WMT_PIN_I2C0_SCL, "i2c0_scl"), + PINCTRL_PIN(WMT_PIN_I2C0_SDA, "i2c0_sda"), + PINCTRL_PIN(WMT_PIN_I2C1_SCL, "i2c1_scl"), + PINCTRL_PIN(WMT_PIN_I2C1_SDA, "i2c1_sda"), + PINCTRL_PIN(WMT_PIN_I2C2_SCL, "i2c2_scl"), + PINCTRL_PIN(WMT_PIN_I2C2_SDA, "i2c2_sda"), + PINCTRL_PIN(WMT_PIN_UART0_RTS, "uart0_rts"), + PINCTRL_PIN(WMT_PIN_UART0_TXD, "uart0_txd"), + PINCTRL_PIN(WMT_PIN_UART0_CTS, "uart0_cts"), + PINCTRL_PIN(WMT_PIN_UART0_RXD, "uart0_rxd"), + PINCTRL_PIN(WMT_PIN_UART1_RTS, "uart1_rts"), + PINCTRL_PIN(WMT_PIN_UART1_TXD, "uart1_txd"), + PINCTRL_PIN(WMT_PIN_UART1_CTS, "uart1_cts"), + PINCTRL_PIN(WMT_PIN_UART1_RXD, "uart1_rxd"), + PINCTRL_PIN(WMT_PIN_UART2_RTS, "uart2_rts"), + PINCTRL_PIN(WMT_PIN_UART2_TXD, "uart2_txd"), + PINCTRL_PIN(WMT_PIN_UART2_CTS, "uart2_cts"), + PINCTRL_PIN(WMT_PIN_UART2_RXD, "uart2_rxd"), + PINCTRL_PIN(WMT_PIN_UART3_RTS, "uart3_rts"), + PINCTRL_PIN(WMT_PIN_UART3_TXD, "uart3_txd"), + PINCTRL_PIN(WMT_PIN_UART3_CTS, "uart3_cts"), + PINCTRL_PIN(WMT_PIN_UART3_RXD, "uart3_rxd"), + PINCTRL_PIN(WMT_PIN_SD2CD, "sd2_cd"), + PINCTRL_PIN(WMT_PIN_SD2DATA3, "sd2_data3"), + PINCTRL_PIN(WMT_PIN_SD2DATA0, "sd2_data0"), + PINCTRL_PIN(WMT_PIN_SD2WP, "sd2_wp"), + PINCTRL_PIN(WMT_PIN_SD2DATA1, "sd2_data1"), + PINCTRL_PIN(WMT_PIN_SD2DATA2, "sd2_data2"), + PINCTRL_PIN(WMT_PIN_SD2CMD, "sd2_cmd"), + PINCTRL_PIN(WMT_PIN_SD2CLK, "sd2_clk"), + PINCTRL_PIN(WMT_PIN_SD2PWR, "sd2_pwr"), + PINCTRL_PIN(WMT_PIN_SD1CLK, "sd1_clk"), + PINCTRL_PIN(WMT_PIN_SD1CMD, "sd1_cmd"), + PINCTRL_PIN(WMT_PIN_SD1PWR, "sd1_pwr"), + PINCTRL_PIN(WMT_PIN_SD1WP, "sd1_wp"), + PINCTRL_PIN(WMT_PIN_SD1CD, "sd1_cd"), + PINCTRL_PIN(WMT_PIN_SPI0SS3, "spi0_ss3"), + PINCTRL_PIN(WMT_PIN_SPI0SS2, "spi0_ss2"), + PINCTRL_PIN(WMT_PIN_PWMOUT1, "pwmout1"), + PINCTRL_PIN(WMT_PIN_PWMOUT0, "pwmout0"), +}; + +/* Order of these names must match the above list */ +static const char * const wm8750_groups[] = { + "extgpio0", + "extgpio1", + "extgpio2", + "extgpio3", + "extgpio4", + "extgpio5", + "extgpio6", + "extgpio7", + "wakeup0", + "wakeup1", + "sd0_cd", + "vdout0", + "vdout1", + "vdout2", + "vdout3", + "vdout4", + "vdout5", + "vdout6", + "vdout7", + "vdout8", + "vdout9", + "vdout10", + "vdout11", + "vdout12", + "vdout13", + "vdout14", + "vdout15", + "vdout16", + "vdout17", + "vdout18", + "vdout19", + "vdout20", + "vdout21", + "vdout22", + "vdout23", + "vdin0", + "vdin1", + "vdin2", + "vdin3", + "vdin4", + "vdin5", + "vdin6", + "vdin7", + "spi0_mosi", + "spi0_miso", + "spi0_ss", + "spi0_clk", + "spi0_ssb", + "sd0_clk", + "sd0_cmd", + "sd0_wp", + "sd0_data0", + "sd0_data1", + "sd0_data2", + "sd0_data3", + "sd1_data0", + "sd1_data1", + "sd1_data2", + "sd1_data3", + "sd1_data4", + "sd1_data5", + "sd1_data6", + "sd1_data7", + "i2c0_scl", + "i2c0_sda", + "i2c1_scl", + "i2c1_sda", + "i2c2_scl", + "i2c2_sda", + "uart0_rts", + "uart0_txd", + "uart0_cts", + "uart0_rxd", + "uart1_rts", + "uart1_txd", + "uart1_cts", + "uart1_rxd", + "uart2_rts", + "uart2_txd", + "uart2_cts", + "uart2_rxd", + "uart3_rts", + "uart3_txd", + "uart3_cts", + "uart3_rxd", + "sd2_cd", + "sd2_data3", + "sd2_data0", + "sd2_wp", + "sd2_data1", + "sd2_data2", + "sd2_cmd", + "sd2_clk", + "sd2_pwr", + "sd1_clk", + "sd1_cmd", + "sd1_pwr", + "sd1_wp", + "sd1_cd", + "spi0_ss3", + "spi0_ss2", + "pwmout1", + "pwmout0", +}; + +static int wm8750_pinctrl_probe(struct platform_device *pdev) +{ + struct wmt_pinctrl_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "failed to allocate data\n"); + return -ENOMEM; + } + + data->banks = wm8750_banks; + data->nbanks = ARRAY_SIZE(wm8750_banks); + data->pins = wm8750_pins; + data->npins = ARRAY_SIZE(wm8750_pins); + data->groups = wm8750_groups; + data->ngroups = ARRAY_SIZE(wm8750_groups); + + return wmt_pinctrl_probe(pdev, data); +} + +static int wm8750_pinctrl_remove(struct platform_device *pdev) +{ + return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { + { .compatible = "wm,wm8750-pinctrl" }, + { /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { + .probe = wm8750_pinctrl_probe, + .remove = wm8750_pinctrl_remove, + .driver = { + .name = "pinctrl-wm8750", + .owner = THIS_MODULE, + .of_match_table = wmt_pinctrl_of_match, + }, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk "); +MODULE_DESCRIPTION("Wondermedia WM8750 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8850.c b/drivers/pinctrl/vt8500/pinctrl-wm8850.c new file mode 100644 index 0000000..ecadce9c --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wm8850.c @@ -0,0 +1,388 @@ +/* + * Pinctrl data for Wondermedia WM8850 SoC + * + * Copyright (c) 2013 Tony Prisk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers wm8850_banks[] = { + WMT_PINCTRL_BANK(0x40, 0x80, 0xC0, 0x00, 0x480, 0x4C0), /* 0 */ + WMT_PINCTRL_BANK(0x44, 0x84, 0xC4, 0x04, 0x484, 0x4C4), /* 1 */ + WMT_PINCTRL_BANK(0x48, 0x88, 0xC8, 0x08, 0x488, 0x4C8), /* 2 */ + WMT_PINCTRL_BANK(0x4C, 0x8C, 0xCC, 0x0C, 0x48C, 0x4CC), /* 3 */ + WMT_PINCTRL_BANK(0x50, 0x90, 0xD0, 0x10, 0x490, 0x4D0), /* 4 */ + WMT_PINCTRL_BANK(0x54, 0x94, 0xD4, 0x14, 0x494, 0x4D4), /* 5 */ + WMT_PINCTRL_BANK(0x58, 0x98, 0xD8, 0x18, 0x498, 0x4D8), /* 6 */ + WMT_PINCTRL_BANK(0x5C, 0x9C, 0xDC, 0x1C, 0x49C, 0x4DC), /* 7 */ + WMT_PINCTRL_BANK(0x60, 0xA0, 0xE0, 0x20, 0x4A0, 0x4E0), /* 8 */ + WMT_PINCTRL_BANK(0x70, 0xB0, 0xF0, 0x30, 0x4B0, 0x4F0), /* 9 */ + WMT_PINCTRL_BANK(0x7C, 0xBC, 0xDC, 0x3C, 0x4BC, 0x4FC), /* 10 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0 WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1 WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2 WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3 WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4 WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5 WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6 WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7 WMT_PIN(0, 7) +#define WMT_PIN_WAKEUP0 WMT_PIN(0, 16) +#define WMT_PIN_WAKEUP1 WMT_PIN(0, 17) +#define WMT_PIN_WAKEUP2 WMT_PIN(0, 18) +#define WMT_PIN_WAKEUP3 WMT_PIN(0, 19) +#define WMT_PIN_SUSGPIO0 WMT_PIN(0, 21) +#define WMT_PIN_SUSGPIO1 WMT_PIN(0, 22) +#define WMT_PIN_SD0CD WMT_PIN(0, 28) +#define WMT_PIN_VDOUT0 WMT_PIN(1, 0) +#define WMT_PIN_VDOUT1 WMT_PIN(1, 1) +#define WMT_PIN_VDOUT2 WMT_PIN(1, 2) +#define WMT_PIN_VDOUT3 WMT_PIN(1, 3) +#define WMT_PIN_VDOUT4 WMT_PIN(1, 4) +#define WMT_PIN_VDOUT5 WMT_PIN(1, 5) +#define WMT_PIN_VDOUT6 WMT_PIN(1, 6) +#define WMT_PIN_VDOUT7 WMT_PIN(1, 7) +#define WMT_PIN_VDOUT8 WMT_PIN(1, 8) +#define WMT_PIN_VDOUT9 WMT_PIN(1, 9) +#define WMT_PIN_VDOUT10 WMT_PIN(1, 10) +#define WMT_PIN_VDOUT11 WMT_PIN(1, 11) +#define WMT_PIN_VDOUT12 WMT_PIN(1, 12) +#define WMT_PIN_VDOUT13 WMT_PIN(1, 13) +#define WMT_PIN_VDOUT14 WMT_PIN(1, 14) +#define WMT_PIN_VDOUT15 WMT_PIN(1, 15) +#define WMT_PIN_VDOUT16 WMT_PIN(1, 16) +#define WMT_PIN_VDOUT17 WMT_PIN(1, 17) +#define WMT_PIN_VDOUT18 WMT_PIN(1, 18) +#define WMT_PIN_VDOUT19 WMT_PIN(1, 19) +#define WMT_PIN_VDOUT20 WMT_PIN(1, 20) +#define WMT_PIN_VDOUT21 WMT_PIN(1, 21) +#define WMT_PIN_VDOUT22 WMT_PIN(1, 22) +#define WMT_PIN_VDOUT23 WMT_PIN(1, 23) +#define WMT_PIN_VDIN0 WMT_PIN(2, 0) +#define WMT_PIN_VDIN1 WMT_PIN(2, 1) +#define WMT_PIN_VDIN2 WMT_PIN(2, 2) +#define WMT_PIN_VDIN3 WMT_PIN(2, 3) +#define WMT_PIN_VDIN4 WMT_PIN(2, 4) +#define WMT_PIN_VDIN5 WMT_PIN(2, 5) +#define WMT_PIN_VDIN6 WMT_PIN(2, 6) +#define WMT_PIN_VDIN7 WMT_PIN(2, 7) +#define WMT_PIN_SPI0_MOSI WMT_PIN(2, 24) +#define WMT_PIN_SPI0_MISO WMT_PIN(2, 25) +#define WMT_PIN_SPI0_SS WMT_PIN(2, 26) +#define WMT_PIN_SPI0_CLK WMT_PIN(2, 27) +#define WMT_PIN_SPI0_SSB WMT_PIN(2, 28) +#define WMT_PIN_SD0CLK WMT_PIN(3, 17) +#define WMT_PIN_SD0CMD WMT_PIN(3, 18) +#define WMT_PIN_SD0WP WMT_PIN(3, 19) +#define WMT_PIN_SD0DATA0 WMT_PIN(3, 20) +#define WMT_PIN_SD0DATA1 WMT_PIN(3, 21) +#define WMT_PIN_SD0DATA2 WMT_PIN(3, 22) +#define WMT_PIN_SD0DATA3 WMT_PIN(3, 23) +#define WMT_PIN_SD1DATA0 WMT_PIN(3, 24) +#define WMT_PIN_SD1DATA1 WMT_PIN(3, 25) +#define WMT_PIN_SD1DATA2 WMT_PIN(3, 26) +#define WMT_PIN_SD1DATA3 WMT_PIN(3, 27) +#define WMT_PIN_SD1DATA4 WMT_PIN(3, 28) +#define WMT_PIN_SD1DATA5 WMT_PIN(3, 29) +#define WMT_PIN_SD1DATA6 WMT_PIN(3, 30) +#define WMT_PIN_SD1DATA7 WMT_PIN(3, 31) +#define WMT_PIN_I2C0_SCL WMT_PIN(5, 8) +#define WMT_PIN_I2C0_SDA WMT_PIN(5, 9) +#define WMT_PIN_I2C1_SCL WMT_PIN(5, 10) +#define WMT_PIN_I2C1_SDA WMT_PIN(5, 11) +#define WMT_PIN_I2C2_SCL WMT_PIN(5, 12) +#define WMT_PIN_I2C2_SDA WMT_PIN(5, 13) +#define WMT_PIN_UART0_RTS WMT_PIN(5, 16) +#define WMT_PIN_UART0_TXD WMT_PIN(5, 17) +#define WMT_PIN_UART0_CTS WMT_PIN(5, 18) +#define WMT_PIN_UART0_RXD WMT_PIN(5, 19) +#define WMT_PIN_UART1_RTS WMT_PIN(5, 20) +#define WMT_PIN_UART1_TXD WMT_PIN(5, 21) +#define WMT_PIN_UART1_CTS WMT_PIN(5, 22) +#define WMT_PIN_UART1_RXD WMT_PIN(5, 23) +#define WMT_PIN_UART2_RTS WMT_PIN(5, 24) +#define WMT_PIN_UART2_TXD WMT_PIN(5, 25) +#define WMT_PIN_UART2_CTS WMT_PIN(5, 26) +#define WMT_PIN_UART2_RXD WMT_PIN(5, 27) +#define WMT_PIN_SD2WP WMT_PIN(6, 3) +#define WMT_PIN_SD2CMD WMT_PIN(6, 6) +#define WMT_PIN_SD2CLK WMT_PIN(6, 7) +#define WMT_PIN_SD2PWR WMT_PIN(6, 9) +#define WMT_PIN_SD1CLK WMT_PIN(7, 0) +#define WMT_PIN_SD1CMD WMT_PIN(7, 1) +#define WMT_PIN_SD1PWR WMT_PIN(7, 10) +#define WMT_PIN_SD1WP WMT_PIN(7, 11) +#define WMT_PIN_SD1CD WMT_PIN(7, 12) +#define WMT_PIN_PWMOUT1 WMT_PIN(7, 26) +#define WMT_PIN_PWMOUT0 WMT_PIN(7, 27) + +static const struct pinctrl_pin_desc wm8850_pins[] = { + PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), + PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), + PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), + PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), + PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), + PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), + PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), + PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), + PINCTRL_PIN(WMT_PIN_WAKEUP0, "wakeup0"), + PINCTRL_PIN(WMT_PIN_WAKEUP1, "wakeup1"), + PINCTRL_PIN(WMT_PIN_WAKEUP2, "wakeup2"), + PINCTRL_PIN(WMT_PIN_WAKEUP3, "wakeup3"), + PINCTRL_PIN(WMT_PIN_SUSGPIO0, "susgpio0"), + PINCTRL_PIN(WMT_PIN_SUSGPIO1, "susgpio1"), + PINCTRL_PIN(WMT_PIN_SD0CD, "sd0_cd"), + PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), + PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), + PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), + PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), + PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), + PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), + PINCTRL_PIN(WMT_PIN_VDOUT6, "vdout6"), + PINCTRL_PIN(WMT_PIN_VDOUT7, "vdout7"), + PINCTRL_PIN(WMT_PIN_VDOUT8, "vdout8"), + PINCTRL_PIN(WMT_PIN_VDOUT9, "vdout9"), + PINCTRL_PIN(WMT_PIN_VDOUT10, "vdout10"), + PINCTRL_PIN(WMT_PIN_VDOUT11, "vdout11"), + PINCTRL_PIN(WMT_PIN_VDOUT12, "vdout12"), + PINCTRL_PIN(WMT_PIN_VDOUT13, "vdout13"), + PINCTRL_PIN(WMT_PIN_VDOUT14, "vdout14"), + PINCTRL_PIN(WMT_PIN_VDOUT15, "vdout15"), + PINCTRL_PIN(WMT_PIN_VDOUT16, "vdout16"), + PINCTRL_PIN(WMT_PIN_VDOUT17, "vdout17"), + PINCTRL_PIN(WMT_PIN_VDOUT18, "vdout18"), + PINCTRL_PIN(WMT_PIN_VDOUT19, "vdout19"), + PINCTRL_PIN(WMT_PIN_VDOUT20, "vdout20"), + PINCTRL_PIN(WMT_PIN_VDOUT21, "vdout21"), + PINCTRL_PIN(WMT_PIN_VDOUT22, "vdout22"), + PINCTRL_PIN(WMT_PIN_VDOUT23, "vdout23"), + PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), + PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), + PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), + PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), + PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), + PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), + PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), + PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), + PINCTRL_PIN(WMT_PIN_SPI0_MOSI, "spi0_mosi"), + PINCTRL_PIN(WMT_PIN_SPI0_MISO, "spi0_miso"), + PINCTRL_PIN(WMT_PIN_SPI0_SS, "spi0_ss"), + PINCTRL_PIN(WMT_PIN_SPI0_CLK, "spi0_clk"), + PINCTRL_PIN(WMT_PIN_SPI0_SSB, "spi0_ssb"), + PINCTRL_PIN(WMT_PIN_SD0CLK, "sd0_clk"), + PINCTRL_PIN(WMT_PIN_SD0CMD, "sd0_cmd"), + PINCTRL_PIN(WMT_PIN_SD0WP, "sd0_wp"), + PINCTRL_PIN(WMT_PIN_SD0DATA0, "sd0_data0"), + PINCTRL_PIN(WMT_PIN_SD0DATA1, "sd0_data1"), + PINCTRL_PIN(WMT_PIN_SD0DATA2, "sd0_data2"), + PINCTRL_PIN(WMT_PIN_SD0DATA3, "sd0_data3"), + PINCTRL_PIN(WMT_PIN_SD1DATA0, "sd1_data0"), + PINCTRL_PIN(WMT_PIN_SD1DATA1, "sd1_data1"), + PINCTRL_PIN(WMT_PIN_SD1DATA2, "sd1_data2"), + PINCTRL_PIN(WMT_PIN_SD1DATA3, "sd1_data3"), + PINCTRL_PIN(WMT_PIN_SD1DATA4, "sd1_data4"), + PINCTRL_PIN(WMT_PIN_SD1DATA5, "sd1_data5"), + PINCTRL_PIN(WMT_PIN_SD1DATA6, "sd1_data6"), + PINCTRL_PIN(WMT_PIN_SD1DATA7, "sd1_data7"), + PINCTRL_PIN(WMT_PIN_I2C0_SCL, "i2c0_scl"), + PINCTRL_PIN(WMT_PIN_I2C0_SDA, "i2c0_sda"), + PINCTRL_PIN(WMT_PIN_I2C1_SCL, "i2c1_scl"), + PINCTRL_PIN(WMT_PIN_I2C1_SDA, "i2c1_sda"), + PINCTRL_PIN(WMT_PIN_I2C2_SCL, "i2c2_scl"), + PINCTRL_PIN(WMT_PIN_I2C2_SDA, "i2c2_sda"), + PINCTRL_PIN(WMT_PIN_UART0_RTS, "uart0_rts"), + PINCTRL_PIN(WMT_PIN_UART0_TXD, "uart0_txd"), + PINCTRL_PIN(WMT_PIN_UART0_CTS, "uart0_cts"), + PINCTRL_PIN(WMT_PIN_UART0_RXD, "uart0_rxd"), + PINCTRL_PIN(WMT_PIN_UART1_RTS, "uart1_rts"), + PINCTRL_PIN(WMT_PIN_UART1_TXD, "uart1_txd"), + PINCTRL_PIN(WMT_PIN_UART1_CTS, "uart1_cts"), + PINCTRL_PIN(WMT_PIN_UART1_RXD, "uart1_rxd"), + PINCTRL_PIN(WMT_PIN_UART2_RTS, "uart2_rts"), + PINCTRL_PIN(WMT_PIN_UART2_TXD, "uart2_txd"), + PINCTRL_PIN(WMT_PIN_UART2_CTS, "uart2_cts"), + PINCTRL_PIN(WMT_PIN_UART2_RXD, "uart2_rxd"), + PINCTRL_PIN(WMT_PIN_SD2WP, "sd2_wp"), + PINCTRL_PIN(WMT_PIN_SD2CMD, "sd2_cmd"), + PINCTRL_PIN(WMT_PIN_SD2CLK, "sd2_clk"), + PINCTRL_PIN(WMT_PIN_SD2PWR, "sd2_pwr"), + PINCTRL_PIN(WMT_PIN_SD1CLK, "sd1_clk"), + PINCTRL_PIN(WMT_PIN_SD1CMD, "sd1_cmd"), + PINCTRL_PIN(WMT_PIN_SD1PWR, "sd1_pwr"), + PINCTRL_PIN(WMT_PIN_SD1WP, "sd1_wp"), + PINCTRL_PIN(WMT_PIN_SD1CD, "sd1_cd"), + PINCTRL_PIN(WMT_PIN_PWMOUT1, "pwmout1"), + PINCTRL_PIN(WMT_PIN_PWMOUT0, "pwmout0"), +}; + +/* Order of these names must match the above list */ +static const char * const wm8850_groups[] = { + "extgpio0", + "extgpio1", + "extgpio2", + "extgpio3", + "extgpio4", + "extgpio5", + "extgpio6", + "extgpio7", + "wakeup0", + "wakeup1", + "wakeup2", + "wakeup3", + "susgpio0", + "susgpio1", + "sd0_cd", + "vdout0", + "vdout1", + "vdout2", + "vdout3", + "vdout4", + "vdout5", + "vdout6", + "vdout7", + "vdout8", + "vdout9", + "vdout10", + "vdout11", + "vdout12", + "vdout13", + "vdout14", + "vdout15", + "vdout16", + "vdout17", + "vdout18", + "vdout19", + "vdout20", + "vdout21", + "vdout22", + "vdout23", + "vdin0", + "vdin1", + "vdin2", + "vdin3", + "vdin4", + "vdin5", + "vdin6", + "vdin7", + "spi0_mosi", + "spi0_miso", + "spi0_ss", + "spi0_clk", + "spi0_ssb", + "sd0_clk", + "sd0_cmd", + "sd0_wp", + "sd0_data0", + "sd0_data1", + "sd0_data2", + "sd0_data3", + "sd1_data0", + "sd1_data1", + "sd1_data2", + "sd1_data3", + "sd1_data4", + "sd1_data5", + "sd1_data6", + "sd1_data7", + "i2c0_scl", + "i2c0_sda", + "i2c1_scl", + "i2c1_sda", + "i2c2_scl", + "i2c2_sda", + "uart0_rts", + "uart0_txd", + "uart0_cts", + "uart0_rxd", + "uart1_rts", + "uart1_txd", + "uart1_cts", + "uart1_rxd", + "uart2_rts", + "uart2_txd", + "uart2_cts", + "uart2_rxd", + "sd2_wp", + "sd2_cmd", + "sd2_clk", + "sd2_pwr", + "sd1_clk", + "sd1_cmd", + "sd1_pwr", + "sd1_wp", + "sd1_cd", + "pwmout1", + "pwmout0", +}; + +static int wm8850_pinctrl_probe(struct platform_device *pdev) +{ + struct wmt_pinctrl_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "failed to allocate data\n"); + return -ENOMEM; + } + + data->banks = wm8850_banks; + data->nbanks = ARRAY_SIZE(wm8850_banks); + data->pins = wm8850_pins; + data->npins = ARRAY_SIZE(wm8850_pins); + data->groups = wm8850_groups; + data->ngroups = ARRAY_SIZE(wm8850_groups); + + return wmt_pinctrl_probe(pdev, data); +} + +static int wm8850_pinctrl_remove(struct platform_device *pdev) +{ + return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { + { .compatible = "wm,wm8850-pinctrl" }, + { /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { + .probe = wm8850_pinctrl_probe, + .remove = wm8850_pinctrl_remove, + .driver = { + .name = "pinctrl-wm8850", + .owner = THIS_MODULE, + .of_match_table = wmt_pinctrl_of_match, + }, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk "); +MODULE_DESCRIPTION("Wondermedia WM8850 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.c b/drivers/pinctrl/vt8500/pinctrl-wmt.c new file mode 100644 index 0000000..14400a7 --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.c @@ -0,0 +1,632 @@ +/* + * Pinctrl driver for the Wondermedia SoC's + * + * Copyright (c) 2013 Tony Prisk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-wmt.h" + +static inline void wmt_setbits(struct wmt_pinctrl_data *data, u32 reg, + u32 mask) +{ + u32 val; + + val = readl_relaxed(data->base + reg); + val |= mask; + writel_relaxed(val, data->base + reg); +} + +static inline void wmt_clearbits(struct wmt_pinctrl_data *data, u32 reg, + u32 mask) +{ + u32 val; + + val = readl_relaxed(data->base + reg); + val &= ~mask; + writel_relaxed(val, data->base + reg); +} + +enum wmt_func_sel { + WMT_FSEL_GPIO_IN = 0, + WMT_FSEL_GPIO_OUT = 1, + WMT_FSEL_ALT = 2, + WMT_FSEL_COUNT = 3, +}; + +static const char * const wmt_functions[WMT_FSEL_COUNT] = { + [WMT_FSEL_GPIO_IN] = "gpio_in", + [WMT_FSEL_GPIO_OUT] = "gpio_out", + [WMT_FSEL_ALT] = "alt", +}; + +static int wmt_pmx_get_functions_count(struct pinctrl_dev *pctldev) +{ + return WMT_FSEL_COUNT; +} + +static const char *wmt_pmx_get_function_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return wmt_functions[selector]; +} + +static int wmt_pmx_get_function_groups(struct pinctrl_dev *pctldev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + + /* every pin does every function */ + *groups = data->groups; + *num_groups = data->ngroups; + + return 0; +} + +static int wmt_set_pinmux(struct wmt_pinctrl_data *data, unsigned func, + unsigned pin) +{ + u32 bank = WMT_BANK_FROM_PIN(pin); + u32 bit = WMT_BIT_FROM_PIN(pin); + u32 reg_en = data->banks[bank].reg_en; + u32 reg_dir = data->banks[bank].reg_dir; + + if (reg_dir == NO_REG) { + dev_err(data->dev, "pin:%d no direction register defined\n", + pin); + return -EINVAL; + } + + /* + * If reg_en == NO_REG, we assume it is a dedicated GPIO and cannot be + * disabled (as on VT8500) and that no alternate function is available. + */ + switch (func) { + case WMT_FSEL_GPIO_IN: + if (reg_en != NO_REG) + wmt_setbits(data, reg_en, BIT(bit)); + wmt_clearbits(data, reg_dir, BIT(bit)); + break; + case WMT_FSEL_GPIO_OUT: + if (reg_en != NO_REG) + wmt_setbits(data, reg_en, BIT(bit)); + wmt_setbits(data, reg_dir, BIT(bit)); + break; + case WMT_FSEL_ALT: + if (reg_en == NO_REG) { + dev_err(data->dev, "pin:%d no alt function available\n", + pin); + return -EINVAL; + } + wmt_clearbits(data, reg_en, BIT(bit)); + } + + return 0; +} + +static int wmt_pmx_enable(struct pinctrl_dev *pctldev, + unsigned func_selector, + unsigned group_selector) +{ + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + u32 pinnum = data->pins[group_selector].number; + + return wmt_set_pinmux(data, func_selector, pinnum); +} + +static void wmt_pmx_disable(struct pinctrl_dev *pctldev, + unsigned func_selector, + unsigned group_selector) +{ + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + u32 pinnum = data->pins[group_selector].number; + + /* disable by setting GPIO_IN */ + wmt_set_pinmux(data, WMT_FSEL_GPIO_IN, pinnum); +} + +static void wmt_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + + /* disable by setting GPIO_IN */ + wmt_set_pinmux(data, WMT_FSEL_GPIO_IN, offset); +} + +static int wmt_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, + bool input) +{ + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + + wmt_set_pinmux(data, (input ? WMT_FSEL_GPIO_IN : WMT_FSEL_GPIO_OUT), + offset); + + return 0; +} + +static struct pinmux_ops wmt_pinmux_ops = { + .get_functions_count = wmt_pmx_get_functions_count, + .get_function_name = wmt_pmx_get_function_name, + .get_function_groups = wmt_pmx_get_function_groups, + .enable = wmt_pmx_enable, + .disable = wmt_pmx_disable, + .gpio_disable_free = wmt_pmx_gpio_disable_free, + .gpio_set_direction = wmt_pmx_gpio_set_direction, +}; + +static int wmt_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + + return data->ngroups; +} + +static const char *wmt_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + + return data->groups[selector]; +} + +static int wmt_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, + const unsigned **pins, + unsigned *num_pins) +{ + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + + *pins = &data->pins[selector].number; + *num_pins = 1; + + return 0; +} + +static int wmt_pctl_find_group_by_pin(struct wmt_pinctrl_data *data, u32 pin) +{ + int i; + + for (i = 0; i < data->npins; i++) { + if (data->pins[i].number == pin) + return i; + } + + return -EINVAL; +} + +static int wmt_pctl_dt_node_to_map_func(struct wmt_pinctrl_data *data, + struct device_node *np, + u32 pin, u32 fnum, + struct pinctrl_map **maps) +{ + int group; + struct pinctrl_map *map = *maps; + + if (fnum >= ARRAY_SIZE(wmt_functions)) { + dev_err(data->dev, "invalid wm,function %d\n", fnum); + return -EINVAL; + } + + group = wmt_pctl_find_group_by_pin(data, pin); + if (group < 0) { + dev_err(data->dev, "unable to match pin %d to group\n", pin); + return group; + } + + map->type = PIN_MAP_TYPE_MUX_GROUP; + map->data.mux.group = data->groups[group]; + map->data.mux.function = wmt_functions[fnum]; + (*maps)++; + + return 0; +} + +static int wmt_pctl_dt_node_to_map_pull(struct wmt_pinctrl_data *data, + struct device_node *np, + u32 pin, u32 pull, + struct pinctrl_map **maps) +{ + int group; + unsigned long *configs; + struct pinctrl_map *map = *maps; + + if (pull > 2) { + dev_err(data->dev, "invalid wm,pull %d\n", pull); + return -EINVAL; + } + + group = wmt_pctl_find_group_by_pin(data, pin); + if (group < 0) { + dev_err(data->dev, "unable to match pin %d to group\n", pin); + return group; + } + + configs = kzalloc(sizeof(*configs), GFP_KERNEL); + if (!configs) + return -ENOMEM; + + configs[0] = pull; + + map->type = PIN_MAP_TYPE_CONFIGS_PIN; + map->data.configs.group_or_pin = data->groups[group]; + map->data.configs.configs = configs; + map->data.configs.num_configs = 1; + (*maps)++; + + return 0; +} + +static void wmt_pctl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *maps, + unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(maps[i].data.configs.configs); + + kfree(maps); +} + +static int wmt_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned *num_maps) +{ + struct pinctrl_map *maps, *cur_map; + struct property *pins, *funcs, *pulls; + u32 pin, func, pull; + int num_pins, num_funcs, num_pulls, maps_per_pin; + int i, err; + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + + pins = of_find_property(np, "wm,pins", NULL); + if (!pins) { + dev_err(data->dev, "missing wmt,pins property\n"); + return -EINVAL; + } + + funcs = of_find_property(np, "wm,function", NULL); + pulls = of_find_property(np, "wm,pull", NULL); + + if (!funcs && !pulls) { + dev_err(data->dev, "neither wm,function nor wm,pull specified\n"); + return -EINVAL; + } + + /* + * The following lines calculate how many values are defined for each + * of the properties. + */ + num_pins = pins->length / sizeof(u32); + num_funcs = funcs ? (funcs->length / sizeof(u32)) : 0; + num_pulls = pulls ? (pulls->length / sizeof(u32)) : 0; + + if (num_funcs > 1 && num_funcs != num_pins) { + dev_err(data->dev, "wm,function must have 1 or %d entries\n", + num_pins); + return -EINVAL; + } + + if (num_pulls > 1 && num_pulls != num_pins) { + dev_err(data->dev, "wm,pull must have 1 or %d entries\n", + num_pins); + return -EINVAL; + } + + maps_per_pin = 0; + if (num_funcs) + maps_per_pin++; + if (num_pulls) + maps_per_pin++; + + cur_map = maps = kzalloc(num_pins * maps_per_pin * sizeof(*maps), + GFP_KERNEL); + if (!maps) + return -ENOMEM; + + for (i = 0; i < num_pins; i++) { + err = of_property_read_u32_index(np, "wm,pins", i, &pin); + if (err) + goto fail; + + if (pin >= (data->nbanks * 32)) { + dev_err(data->dev, "invalid wm,pins value\n"); + err = -EINVAL; + goto fail; + } + + if (num_funcs) { + err = of_property_read_u32_index(np, "wm,function", + (num_funcs > 1 ? i : 0), &func); + if (err) + goto fail; + + err = wmt_pctl_dt_node_to_map_func(data, np, pin, func, + &cur_map); + if (err) + goto fail; + } + + if (num_pulls) { + err = of_property_read_u32_index(np, "wm,pull", + (num_pulls > 1 ? i : 0), &pull); + if (err) + goto fail; + + err = wmt_pctl_dt_node_to_map_pull(data, np, pin, pull, + &cur_map); + if (err) + goto fail; + } + } + *map = maps; + *num_maps = num_pins * maps_per_pin; + return 0; + +/* + * The fail path removes any maps that have been allocated. The fail path is + * only called from code after maps has been kzalloc'd. It is also safe to + * pass 'num_pins * maps_per_pin' as the map count even though we probably + * failed before all the mappings were read as all maps are allocated at once, + * and configs are only allocated for .type = PIN_MAP_TYPE_CONFIGS_PIN - there + * is no failpath where a config can be allocated without .type being set. + */ +fail: + wmt_pctl_dt_free_map(pctldev, maps, num_pins * maps_per_pin); + return err; +} + +static struct pinctrl_ops wmt_pctl_ops = { + .get_groups_count = wmt_get_groups_count, + .get_group_name = wmt_get_group_name, + .get_group_pins = wmt_get_group_pins, + .dt_node_to_map = wmt_pctl_dt_node_to_map, + .dt_free_map = wmt_pctl_dt_free_map, +}; + +static int wmt_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + return -ENOTSUPP; +} + +static int wmt_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long config) +{ + struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(config); + u16 arg = pinconf_to_config_argument(config); + u32 bank = WMT_BANK_FROM_PIN(pin); + u32 bit = WMT_BIT_FROM_PIN(pin); + u32 reg_pull_en = data->banks[bank].reg_pull_en; + u32 reg_pull_cfg = data->banks[bank].reg_pull_cfg; + + if ((reg_pull_en == NO_REG) || (reg_pull_cfg == NO_REG)) { + dev_err(data->dev, "bias functions not supported on pin %d\n", + pin); + return -EINVAL; + } + + if ((param == PIN_CONFIG_BIAS_PULL_DOWN) || + (param == PIN_CONFIG_BIAS_PULL_UP)) { + if (arg == 0) + param = PIN_CONFIG_BIAS_DISABLE; + } + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + wmt_clearbits(data, reg_pull_en, BIT(bit)); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + wmt_clearbits(data, reg_pull_cfg, BIT(bit)); + wmt_setbits(data, reg_pull_en, BIT(bit)); + break; + case PIN_CONFIG_BIAS_PULL_UP: + wmt_setbits(data, reg_pull_cfg, BIT(bit)); + wmt_setbits(data, reg_pull_en, BIT(bit)); + break; + default: + dev_err(data->dev, "unknown pinconf param\n"); + return -EINVAL; + } + + return 0; +} + +static struct pinconf_ops wmt_pinconf_ops = { + .pin_config_get = wmt_pinconf_get, + .pin_config_set = wmt_pinconf_set, +}; + +static struct pinctrl_desc wmt_desc = { + .owner = THIS_MODULE, + .name = "pinctrl-wmt", + .pctlops = &wmt_pctl_ops, + .pmxops = &wmt_pinmux_ops, + .confops = &wmt_pinconf_ops, +}; + +static int wmt_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void wmt_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static int wmt_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct wmt_pinctrl_data *data = dev_get_drvdata(chip->dev); + u32 bank = WMT_BANK_FROM_PIN(offset); + u32 bit = WMT_BIT_FROM_PIN(offset); + u32 reg_dir = data->banks[bank].reg_dir; + u32 val; + + val = readl_relaxed(data->base + reg_dir); + if (val & BIT(bit)) + return GPIOF_DIR_OUT; + else + return GPIOF_DIR_IN; +} + +static int wmt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_gpio_direction_input(chip->base + offset); +} + +static int wmt_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + return pinctrl_gpio_direction_output(chip->base + offset); +} + +static int wmt_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + struct wmt_pinctrl_data *data = dev_get_drvdata(chip->dev); + u32 bank = WMT_BANK_FROM_PIN(offset); + u32 bit = WMT_BIT_FROM_PIN(offset); + u32 reg_data_in = data->banks[bank].reg_data_in; + + if (reg_data_in == NO_REG) { + dev_err(data->dev, "no data in register defined\n"); + return -EINVAL; + } + + return !!(readl_relaxed(data->base + reg_data_in) & BIT(bit)); +} + +static void wmt_gpio_set_value(struct gpio_chip *chip, unsigned offset, + int val) +{ + struct wmt_pinctrl_data *data = dev_get_drvdata(chip->dev); + u32 bank = WMT_BANK_FROM_PIN(offset); + u32 bit = WMT_BIT_FROM_PIN(offset); + u32 reg_data_out = data->banks[bank].reg_data_out; + + if (reg_data_out == NO_REG) { + dev_err(data->dev, "no data out register defined\n"); + return; + } + + if (val) + wmt_setbits(data, reg_data_out, BIT(bit)); + else + wmt_clearbits(data, reg_data_out, BIT(bit)); +} + +static struct gpio_chip wmt_gpio_chip = { + .label = "gpio-wmt", + .owner = THIS_MODULE, + .request = wmt_gpio_request, + .free = wmt_gpio_free, + .get_direction = wmt_gpio_get_direction, + .direction_input = wmt_gpio_direction_input, + .direction_output = wmt_gpio_direction_output, + .get = wmt_gpio_get_value, + .set = wmt_gpio_set_value, + .can_sleep = 0, +}; + +int wmt_pinctrl_probe(struct platform_device *pdev, + struct wmt_pinctrl_data *data) +{ + int err; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_request_and_ioremap(&pdev->dev, res); + if (!data->base) { + dev_err(&pdev->dev, "failed to map memory resource\n"); + return -EBUSY; + } + + wmt_desc.pins = data->pins; + wmt_desc.npins = data->npins; + + data->gpio_chip = wmt_gpio_chip; + data->gpio_chip.dev = &pdev->dev; + data->gpio_chip.of_node = pdev->dev.of_node; + data->gpio_chip.ngpio = data->nbanks * 32; + + platform_set_drvdata(pdev, data); + + data->dev = &pdev->dev; + + data->pctl_dev = pinctrl_register(&wmt_desc, &pdev->dev, data); + if (IS_ERR(data->pctl_dev)) { + dev_err(&pdev->dev, "Failed to register pinctrl\n"); + return -EINVAL; + } + + err = gpiochip_add(&data->gpio_chip); + if (err) { + dev_err(&pdev->dev, "could not add GPIO chip\n"); + goto fail_gpio; + } + + err = gpiochip_add_pin_range(&data->gpio_chip, dev_name(data->dev), + 0, 0, data->nbanks * 32); + if (err) + goto fail_range; + + dev_info(&pdev->dev, "Pin controller initialized\n"); + + return 0; + +fail_range: + err = gpiochip_remove(&data->gpio_chip); + if (err) + dev_err(&pdev->dev, "failed to remove gpio chip\n"); +fail_gpio: + pinctrl_unregister(data->pctl_dev); + return err; +} + +int wmt_pinctrl_remove(struct platform_device *pdev) +{ + struct wmt_pinctrl_data *data = platform_get_drvdata(pdev); + int err; + + err = gpiochip_remove(&data->gpio_chip); + if (err) + dev_err(&pdev->dev, "failed to remove gpio chip\n"); + + pinctrl_unregister(data->pctl_dev); + + return 0; +} diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.h b/drivers/pinctrl/vt8500/pinctrl-wmt.h new file mode 100644 index 0000000..41f5f2d --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.h @@ -0,0 +1,79 @@ +/* + * Pinctrl driver for the Wondermedia SoC's + * + * Copyright (c) 2013 Tony Prisk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include + +/* VT8500 has no enable register in the extgpio bank. */ +#define NO_REG 0xFFFF + +#define WMT_PINCTRL_BANK(__en, __dir, __dout, __din, __pen, __pcfg) \ +{ \ + .reg_en = __en, \ + .reg_dir = __dir, \ + .reg_data_out = __dout, \ + .reg_data_in = __din, \ + .reg_pull_en = __pen, \ + .reg_pull_cfg = __pcfg, \ +} + +/* Encode/decode the bank/bit pairs into a pin value */ +#define WMT_PIN(__bank, __offset) ((__bank << 5) | __offset) +#define WMT_BANK_FROM_PIN(__pin) (__pin >> 5) +#define WMT_BIT_FROM_PIN(__pin) (__pin & 0x1f) + +#define WMT_GROUP(__name, __data) \ +{ \ + .name = __name, \ + .pins = __data, \ + .npins = ARRAY_SIZE(__data), \ +} + +struct wmt_pinctrl_bank_registers { + u32 reg_en; + u32 reg_dir; + u32 reg_data_out; + u32 reg_data_in; + + u32 reg_pull_en; + u32 reg_pull_cfg; +}; + +struct wmt_pinctrl_group { + const char *name; + const unsigned int *pins; + const unsigned npins; +}; + +struct wmt_pinctrl_data { + struct device *dev; + struct pinctrl_dev *pctl_dev; + + /* must be initialized before calling wmt_pinctrl_probe */ + void __iomem *base; + const struct wmt_pinctrl_bank_registers *banks; + const struct pinctrl_pin_desc *pins; + const char * const *groups; + + u32 nbanks; + u32 npins; + u32 ngroups; + + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range gpio_range; +}; + +int wmt_pinctrl_probe(struct platform_device *pdev, + struct wmt_pinctrl_data *data); +int wmt_pinctrl_remove(struct platform_device *pdev); -- cgit v1.1 From dc1010860b03a0db7683bafb69a4bc2310f4d9ec Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Wed, 20 Feb 2013 17:29:33 +1300 Subject: gpio: vt8500: Remove arch-vt8500 gpio driver With the move to a combined pinctrl/gpio driver, the arch-vt8500 gpio driver is no longer required. Signed-off-by: Tony Prisk Acked-by: Linus Walleij --- drivers/gpio/Kconfig | 6 - drivers/gpio/Makefile | 1 - drivers/gpio/gpio-vt8500.c | 355 --------------------------------------------- 3 files changed, 362 deletions(-) delete mode 100644 drivers/gpio/gpio-vt8500.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 93aaadf..b166e30 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -227,12 +227,6 @@ config GPIO_TS5500 blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600 LCD port. -config GPIO_VT8500 - bool "VIA/Wondermedia SoC GPIO Support" - depends on ARCH_VT8500 - help - Say yes here to support the VT8500/WM8505/WM8650 GPIO controller. - config GPIO_XILINX bool "Xilinx GPIO support" depends on PPC_OF || MICROBLAZE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 22e07bc..a274d7d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -80,7 +80,6 @@ obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o -obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c deleted file mode 100644 index 81683ca..0000000 --- a/drivers/gpio/gpio-vt8500.c +++ /dev/null @@ -1,355 +0,0 @@ -/* drivers/gpio/gpio-vt8500.c - * - * Copyright (C) 2012 Tony Prisk - * Based on arch/arm/mach-vt8500/gpio.c: - * - Copyright (C) 2010 Alexey Charkov - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - We handle GPIOs by bank, each bank containing up to 32 GPIOs covered - by one set of registers (although not all may be valid). - - Because different SoC's have different register offsets, we pass the - register offsets as data in vt8500_gpio_dt_ids[]. - - A value of NO_REG is used to indicate that this register is not - supported. Only used for ->en at the moment. -*/ - -#define NO_REG 0xFFFF - -/* - * struct vt8500_gpio_bank_regoffsets - * @en: offset to enable register of the bank - * @dir: offset to direction register of the bank - * @data_out: offset to the data out register of the bank - * @data_in: offset to the data in register of the bank - * @ngpio: highest valid pin in this bank - */ - -struct vt8500_gpio_bank_regoffsets { - unsigned int en; - unsigned int dir; - unsigned int data_out; - unsigned int data_in; - unsigned char ngpio; -}; - -struct vt8500_gpio_data { - unsigned int num_banks; - struct vt8500_gpio_bank_regoffsets banks[]; -}; - -#define VT8500_BANK(__en, __dir, __out, __in, __ngpio) \ -{ \ - .en = __en, \ - .dir = __dir, \ - .data_out = __out, \ - .data_in = __in, \ - .ngpio = __ngpio, \ -} - -static struct vt8500_gpio_data vt8500_data = { - .num_banks = 7, - .banks = { - VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9), - VT8500_BANK(0x00, 0x20, 0x40, 0x60, 26), - VT8500_BANK(0x04, 0x24, 0x44, 0x64, 28), - VT8500_BANK(0x08, 0x28, 0x48, 0x68, 31), - VT8500_BANK(0x0C, 0x2C, 0x4C, 0x6C, 19), - VT8500_BANK(0x10, 0x30, 0x50, 0x70, 19), - VT8500_BANK(0x14, 0x34, 0x54, 0x74, 23), - }, -}; - -static struct vt8500_gpio_data wm8505_data = { - .num_banks = 10, - .banks = { - VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22), - VT8500_BANK(0x40, 0x68, 0x90, 0xB8, 8), - VT8500_BANK(0x44, 0x6C, 0x94, 0xBC, 32), - VT8500_BANK(0x48, 0x70, 0x98, 0xC0, 6), - VT8500_BANK(0x4C, 0x74, 0x9C, 0xC4, 16), - VT8500_BANK(0x50, 0x78, 0xA0, 0xC8, 25), - VT8500_BANK(0x54, 0x7C, 0xA4, 0xCC, 5), - VT8500_BANK(0x58, 0x80, 0xA8, 0xD0, 5), - VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12), - VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16), - VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6), - }, -}; - -/* - * No information about which bits are valid so we just make - * them all available until its figured out. - */ -static struct vt8500_gpio_data wm8650_data = { - .num_banks = 9, - .banks = { - VT8500_BANK(0x40, 0x80, 0xC0, 0x00, 32), - VT8500_BANK(0x44, 0x84, 0xC4, 0x04, 32), - VT8500_BANK(0x48, 0x88, 0xC8, 0x08, 32), - VT8500_BANK(0x4C, 0x8C, 0xCC, 0x0C, 32), - VT8500_BANK(0x50, 0x90, 0xD0, 0x10, 32), - VT8500_BANK(0x54, 0x94, 0xD4, 0x14, 32), - VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32), - VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32), - VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32), - VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6), - }, -}; - -struct vt8500_gpio_chip { - struct gpio_chip chip; - - const struct vt8500_gpio_bank_regoffsets *regs; - void __iomem *base; -}; - -struct vt8500_data { - struct vt8500_gpio_chip *chip; - void __iomem *iobase; - int num_banks; -}; - - -#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip) - -static int vt8500_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - u32 val; - struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - - if (vt8500_chip->regs->en == NO_REG) - return 0; - - val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en); - val |= BIT(offset); - writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en); - - return 0; -} - -static void vt8500_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - u32 val; - - if (vt8500_chip->regs->en == NO_REG) - return; - - val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en); - val &= ~BIT(offset); - writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en); -} - -static int vt8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - - u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir); - val &= ~BIT(offset); - writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir); - - return 0; -} - -static int vt8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - - u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir); - val |= BIT(offset); - writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir); - - if (value) { - val = readl_relaxed(vt8500_chip->base + - vt8500_chip->regs->data_out); - val |= BIT(offset); - writel_relaxed(val, vt8500_chip->base + - vt8500_chip->regs->data_out); - } - return 0; -} - -static int vt8500_gpio_get_value(struct gpio_chip *chip, unsigned offset) -{ - struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - - return (readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) >> - offset) & 1; -} - -static void vt8500_gpio_set_value(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - - u32 val = readl_relaxed(vt8500_chip->base + - vt8500_chip->regs->data_out); - if (value) - val |= BIT(offset); - else - val &= ~BIT(offset); - - writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out); -} - -static int vt8500_of_xlate(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, u32 *flags) -{ - /* bank if specificed in gpiospec->args[0] */ - if (flags) - *flags = gpiospec->args[2]; - - return gpiospec->args[1]; -} - -static int vt8500_add_chips(struct platform_device *pdev, void __iomem *base, - const struct vt8500_gpio_data *data) -{ - struct vt8500_data *priv; - struct vt8500_gpio_chip *vtchip; - struct gpio_chip *chip; - int i; - int pin_cnt = 0; - - priv = devm_kzalloc(&pdev->dev, sizeof(struct vt8500_data), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "failed to allocate memory\n"); - return -ENOMEM; - } - - priv->chip = devm_kzalloc(&pdev->dev, - sizeof(struct vt8500_gpio_chip) * data->num_banks, - GFP_KERNEL); - if (!priv->chip) { - dev_err(&pdev->dev, "failed to allocate chip memory\n"); - return -ENOMEM; - } - - priv->iobase = base; - priv->num_banks = data->num_banks; - platform_set_drvdata(pdev, priv); - - vtchip = priv->chip; - - for (i = 0; i < data->num_banks; i++) { - vtchip[i].base = base; - vtchip[i].regs = &data->banks[i]; - - chip = &vtchip[i].chip; - - chip->of_xlate = vt8500_of_xlate; - chip->of_gpio_n_cells = 3; - chip->of_node = pdev->dev.of_node; - - chip->request = vt8500_gpio_request; - chip->free = vt8500_gpio_free; - chip->direction_input = vt8500_gpio_direction_input; - chip->direction_output = vt8500_gpio_direction_output; - chip->get = vt8500_gpio_get_value; - chip->set = vt8500_gpio_set_value; - chip->can_sleep = 0; - chip->base = pin_cnt; - chip->ngpio = data->banks[i].ngpio; - - pin_cnt += data->banks[i].ngpio; - - gpiochip_add(chip); - } - return 0; -} - -static struct of_device_id vt8500_gpio_dt_ids[] = { - { .compatible = "via,vt8500-gpio", .data = &vt8500_data, }, - { .compatible = "wm,wm8505-gpio", .data = &wm8505_data, }, - { .compatible = "wm,wm8650-gpio", .data = &wm8650_data, }, - { /* Sentinel */ }, -}; - -static int vt8500_gpio_probe(struct platform_device *pdev) -{ - int ret; - void __iomem *gpio_base; - struct resource *res; - const struct of_device_id *of_id = - of_match_device(vt8500_gpio_dt_ids, &pdev->dev); - - if (!of_id) { - dev_err(&pdev->dev, "No matching driver data\n"); - return -ENODEV; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get IO resource\n"); - return -ENODEV; - } - - gpio_base = devm_request_and_ioremap(&pdev->dev, res); - if (!gpio_base) { - dev_err(&pdev->dev, "Unable to map GPIO registers\n"); - return -ENOMEM; - } - - ret = vt8500_add_chips(pdev, gpio_base, of_id->data); - - return ret; -} - -static int vt8500_gpio_remove(struct platform_device *pdev) -{ - int i; - int ret; - struct vt8500_data *priv = platform_get_drvdata(pdev); - struct vt8500_gpio_chip *vtchip = priv->chip; - - for (i = 0; i < priv->num_banks; i++) { - ret = gpiochip_remove(&vtchip[i].chip); - if (ret) - dev_warn(&pdev->dev, "gpiochip_remove returned %d\n", - ret); - } - - return 0; -} - -static struct platform_driver vt8500_gpio_driver = { - .probe = vt8500_gpio_probe, - .remove = vt8500_gpio_remove, - .driver = { - .name = "vt8500-gpio", - .owner = THIS_MODULE, - .of_match_table = vt8500_gpio_dt_ids, - }, -}; - -module_platform_driver(vt8500_gpio_driver); - -MODULE_DESCRIPTION("VT8500 GPIO Driver"); -MODULE_AUTHOR("Tony Prisk "); -MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, vt8500_gpio_dt_ids); -- cgit v1.1 From ce63d6d4bb9f601de32d4b99f925a65182521873 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 28 Mar 2013 05:09:57 +0000 Subject: pinctrl: bcm2835: make use of of_property_read_u32_index() Use the new standard API of_property_read_u32_index() instead of open- coding it. Signed-off-by: Stephen Warren Signed-off-by: Tony Prisk --- drivers/pinctrl/pinctrl-bcm2835.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c index 4eb6d2c..2a2e427 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/pinctrl-bcm2835.c @@ -699,11 +699,6 @@ static int bcm2835_pctl_dt_node_to_map_pull(struct bcm2835_pinctrl *pc, return 0; } -static inline u32 prop_u32(struct property *p, int i) -{ - return be32_to_cpup(((__be32 *)p->value) + i); -} - static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, unsigned *num_maps) @@ -761,7 +756,9 @@ static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, return -ENOMEM; for (i = 0; i < num_pins; i++) { - pin = prop_u32(pins, i); + err = of_property_read_u32_index(np, "brcm,pins", i, &pin); + if (err) + goto out; if (pin >= ARRAY_SIZE(bcm2835_gpio_pins)) { dev_err(pc->dev, "%s: invalid brcm,pins value %d\n", of_node_full_name(np), pin); @@ -770,14 +767,20 @@ static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, } if (num_funcs) { - func = prop_u32(funcs, (num_funcs > 1) ? i : 0); + err = of_property_read_u32_index(np, "brcm,function", + (num_funcs > 1) ? i : 0, &func); + if (err) + goto out; err = bcm2835_pctl_dt_node_to_map_func(pc, np, pin, func, &cur_map); if (err) goto out; } if (num_pulls) { - pull = prop_u32(pulls, (num_pulls > 1) ? i : 0); + err = of_property_read_u32_index(np, "brcm,pull", + (num_funcs > 1) ? i : 0, &pull); + if (err) + goto out; err = bcm2835_pctl_dt_node_to_map_pull(pc, np, pin, pull, &cur_map); if (err) -- cgit v1.1 From 8a407835bef6d47dcef9594d8c85900f994fbedf Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:33 +0900 Subject: ARM: S3C24XX: move irq driver to drivers/irqchip This move is necessary to make use of the irqchip infrastructure for the following devicetree support for s3c24xx architectures. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-s3c24xx.c | 1107 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1108 insertions(+) create mode 100644 drivers/irqchip/irq-s3c24xx.c (limited to 'drivers') diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 98e3b87..4d65a21 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o +obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c new file mode 100644 index 0000000..5c9f8b7 --- /dev/null +++ b/drivers/irqchip/irq-s3c24xx.c @@ -0,0 +1,1107 @@ +/* + * S3C24XX IRQ handling + * + * Copyright (c) 2003-2004 Simtec Electronics + * Ben Dooks + * Copyright (c) 2012 Heiko Stuebner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#define S3C_IRQTYPE_NONE 0 +#define S3C_IRQTYPE_EINT 1 +#define S3C_IRQTYPE_EDGE 2 +#define S3C_IRQTYPE_LEVEL 3 + +struct s3c_irq_data { + unsigned int type; + unsigned long parent_irq; + + /* data gets filled during init */ + struct s3c_irq_intc *intc; + unsigned long sub_bits; + struct s3c_irq_intc *sub_intc; +}; + +/* + * Sructure holding the controller data + * @reg_pending register holding pending irqs + * @reg_intpnd special register intpnd in main intc + * @reg_mask mask register + * @domain irq_domain of the controller + * @parent parent controller for ext and sub irqs + * @irqs irq-data, always s3c_irq_data[32] + */ +struct s3c_irq_intc { + void __iomem *reg_pending; + void __iomem *reg_intpnd; + void __iomem *reg_mask; + struct irq_domain *domain; + struct s3c_irq_intc *parent; + struct s3c_irq_data *irqs; +}; + +static void s3c_irq_mask(struct irq_data *data) +{ + struct s3c_irq_intc *intc = data->domain->host_data; + struct s3c_irq_intc *parent_intc = intc->parent; + struct s3c_irq_data *irq_data = &intc->irqs[data->hwirq]; + struct s3c_irq_data *parent_data; + unsigned long mask; + unsigned int irqno; + + mask = __raw_readl(intc->reg_mask); + mask |= (1UL << data->hwirq); + __raw_writel(mask, intc->reg_mask); + + if (parent_intc) { + parent_data = &parent_intc->irqs[irq_data->parent_irq]; + + /* check to see if we need to mask the parent IRQ */ + if ((mask & parent_data->sub_bits) == parent_data->sub_bits) { + irqno = irq_find_mapping(parent_intc->domain, + irq_data->parent_irq); + s3c_irq_mask(irq_get_irq_data(irqno)); + } + } +} + +static void s3c_irq_unmask(struct irq_data *data) +{ + struct s3c_irq_intc *intc = data->domain->host_data; + struct s3c_irq_intc *parent_intc = intc->parent; + struct s3c_irq_data *irq_data = &intc->irqs[data->hwirq]; + unsigned long mask; + unsigned int irqno; + + mask = __raw_readl(intc->reg_mask); + mask &= ~(1UL << data->hwirq); + __raw_writel(mask, intc->reg_mask); + + if (parent_intc) { + irqno = irq_find_mapping(parent_intc->domain, + irq_data->parent_irq); + s3c_irq_unmask(irq_get_irq_data(irqno)); + } +} + +static inline void s3c_irq_ack(struct irq_data *data) +{ + struct s3c_irq_intc *intc = data->domain->host_data; + unsigned long bitval = 1UL << data->hwirq; + + __raw_writel(bitval, intc->reg_pending); + if (intc->reg_intpnd) + __raw_writel(bitval, intc->reg_intpnd); +} + +static int s3c_irqext_type_set(void __iomem *gpcon_reg, + void __iomem *extint_reg, + unsigned long gpcon_offset, + unsigned long extint_offset, + unsigned int type) +{ + unsigned long newvalue = 0, value; + + /* Set the GPIO to external interrupt mode */ + value = __raw_readl(gpcon_reg); + value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); + __raw_writel(value, gpcon_reg); + + /* Set the external interrupt to pointed trigger type */ + switch (type) + { + case IRQ_TYPE_NONE: + pr_warn("No edge setting!\n"); + break; + + case IRQ_TYPE_EDGE_RISING: + newvalue = S3C2410_EXTINT_RISEEDGE; + break; + + case IRQ_TYPE_EDGE_FALLING: + newvalue = S3C2410_EXTINT_FALLEDGE; + break; + + case IRQ_TYPE_EDGE_BOTH: + newvalue = S3C2410_EXTINT_BOTHEDGE; + break; + + case IRQ_TYPE_LEVEL_LOW: + newvalue = S3C2410_EXTINT_LOWLEV; + break; + + case IRQ_TYPE_LEVEL_HIGH: + newvalue = S3C2410_EXTINT_HILEV; + break; + + default: + pr_err("No such irq type %d", type); + return -EINVAL; + } + + value = __raw_readl(extint_reg); + value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); + __raw_writel(value, extint_reg); + + return 0; +} + +static int s3c_irqext_type(struct irq_data *data, unsigned int type) +{ + void __iomem *extint_reg; + void __iomem *gpcon_reg; + unsigned long gpcon_offset, extint_offset; + + if ((data->hwirq >= 4) && (data->hwirq <= 7)) { + gpcon_reg = S3C2410_GPFCON; + extint_reg = S3C24XX_EXTINT0; + gpcon_offset = (data->hwirq) * 2; + extint_offset = (data->hwirq) * 4; + } else if ((data->hwirq >= 8) && (data->hwirq <= 15)) { + gpcon_reg = S3C2410_GPGCON; + extint_reg = S3C24XX_EXTINT1; + gpcon_offset = (data->hwirq - 8) * 2; + extint_offset = (data->hwirq - 8) * 4; + } else if ((data->hwirq >= 16) && (data->hwirq <= 23)) { + gpcon_reg = S3C2410_GPGCON; + extint_reg = S3C24XX_EXTINT2; + gpcon_offset = (data->hwirq - 8) * 2; + extint_offset = (data->hwirq - 16) * 4; + } else { + return -EINVAL; + } + + return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset, + extint_offset, type); +} + +static int s3c_irqext0_type(struct irq_data *data, unsigned int type) +{ + void __iomem *extint_reg; + void __iomem *gpcon_reg; + unsigned long gpcon_offset, extint_offset; + + if ((data->hwirq >= 0) && (data->hwirq <= 3)) { + gpcon_reg = S3C2410_GPFCON; + extint_reg = S3C24XX_EXTINT0; + gpcon_offset = (data->hwirq) * 2; + extint_offset = (data->hwirq) * 4; + } else { + return -EINVAL; + } + + return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset, + extint_offset, type); +} + +static struct irq_chip s3c_irq_chip = { + .name = "s3c", + .irq_ack = s3c_irq_ack, + .irq_mask = s3c_irq_mask, + .irq_unmask = s3c_irq_unmask, + .irq_set_wake = s3c_irq_wake +}; + +static struct irq_chip s3c_irq_level_chip = { + .name = "s3c-level", + .irq_mask = s3c_irq_mask, + .irq_unmask = s3c_irq_unmask, + .irq_ack = s3c_irq_ack, +}; + +static struct irq_chip s3c_irqext_chip = { + .name = "s3c-ext", + .irq_mask = s3c_irq_mask, + .irq_unmask = s3c_irq_unmask, + .irq_ack = s3c_irq_ack, + .irq_set_type = s3c_irqext_type, + .irq_set_wake = s3c_irqext_wake +}; + +static struct irq_chip s3c_irq_eint0t4 = { + .name = "s3c-ext0", + .irq_ack = s3c_irq_ack, + .irq_mask = s3c_irq_mask, + .irq_unmask = s3c_irq_unmask, + .irq_set_wake = s3c_irq_wake, + .irq_set_type = s3c_irqext0_type, +}; + +static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct s3c_irq_intc *intc = desc->irq_data.domain->host_data; + struct s3c_irq_data *irq_data = &intc->irqs[desc->irq_data.hwirq]; + struct s3c_irq_intc *sub_intc = irq_data->sub_intc; + unsigned long src; + unsigned long msk; + unsigned int n; + + chained_irq_enter(chip, desc); + + src = __raw_readl(sub_intc->reg_pending); + msk = __raw_readl(sub_intc->reg_mask); + + src &= ~msk; + src &= irq_data->sub_bits; + + while (src) { + n = __ffs(src); + src &= ~(1 << n); + generic_handle_irq(irq_find_mapping(sub_intc->domain, n)); + } + + chained_irq_exit(chip, desc); +} + +static struct s3c_irq_intc *main_intc; +static struct s3c_irq_intc *main_intc2; + +static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, + struct pt_regs *regs) +{ + int pnd; + int offset; + int irq; + + pnd = __raw_readl(intc->reg_intpnd); + if (!pnd) + return false; + + /* We have a problem that the INTOFFSET register does not always + * show one interrupt. Occasionally we get two interrupts through + * the prioritiser, and this causes the INTOFFSET register to show + * what looks like the logical-or of the two interrupt numbers. + * + * Thanks to Klaus, Shannon, et al for helping to debug this problem + */ + offset = __raw_readl(intc->reg_intpnd + 4); + + /* Find the bit manually, when the offset is wrong. + * The pending register only ever contains the one bit of the next + * interrupt to handle. + */ + if (!(pnd & (1 << offset))) + offset = __ffs(pnd); + + irq = irq_find_mapping(intc->domain, offset); + handle_IRQ(irq, regs); + return true; +} + +asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs) +{ + do { + if (likely(main_intc)) + if (s3c24xx_handle_intc(main_intc, regs)) + continue; + + if (main_intc2) + if (s3c24xx_handle_intc(main_intc2, regs)) + continue; + + break; + } while (1); +} + +#ifdef CONFIG_FIQ +/** + * s3c24xx_set_fiq - set the FIQ routing + * @irq: IRQ number to route to FIQ on processor. + * @on: Whether to route @irq to the FIQ, or to remove the FIQ routing. + * + * Change the state of the IRQ to FIQ routing depending on @irq and @on. If + * @on is true, the @irq is checked to see if it can be routed and the + * interrupt controller updated to route the IRQ. If @on is false, the FIQ + * routing is cleared, regardless of which @irq is specified. + */ +int s3c24xx_set_fiq(unsigned int irq, bool on) +{ + u32 intmod; + unsigned offs; + + if (on) { + offs = irq - FIQ_START; + if (offs > 31) + return -EINVAL; + + intmod = 1 << offs; + } else { + intmod = 0; + } + + __raw_writel(intmod, S3C2410_INTMOD); + return 0; +} + +EXPORT_SYMBOL_GPL(s3c24xx_set_fiq); +#endif + +static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct s3c_irq_intc *intc = h->host_data; + struct s3c_irq_data *irq_data = &intc->irqs[hw]; + struct s3c_irq_intc *parent_intc; + struct s3c_irq_data *parent_irq_data; + unsigned int irqno; + + /* attach controller pointer to irq_data */ + irq_data->intc = intc; + + parent_intc = intc->parent; + + /* set handler and flags */ + switch (irq_data->type) { + case S3C_IRQTYPE_NONE: + return 0; + case S3C_IRQTYPE_EINT: + /* On the S3C2412, the EINT0to3 have a parent irq + * but need the s3c_irq_eint0t4 chip + */ + if (parent_intc && (!soc_is_s3c2412() || hw >= 4)) + irq_set_chip_and_handler(virq, &s3c_irqext_chip, + handle_edge_irq); + else + irq_set_chip_and_handler(virq, &s3c_irq_eint0t4, + handle_edge_irq); + break; + case S3C_IRQTYPE_EDGE: + if (parent_intc || intc->reg_pending == S3C2416_SRCPND2) + irq_set_chip_and_handler(virq, &s3c_irq_level_chip, + handle_edge_irq); + else + irq_set_chip_and_handler(virq, &s3c_irq_chip, + handle_edge_irq); + break; + case S3C_IRQTYPE_LEVEL: + if (parent_intc) + irq_set_chip_and_handler(virq, &s3c_irq_level_chip, + handle_level_irq); + else + irq_set_chip_and_handler(virq, &s3c_irq_chip, + handle_level_irq); + break; + default: + pr_err("irq-s3c24xx: unsupported irqtype %d\n", irq_data->type); + return -EINVAL; + } + set_irq_flags(virq, IRQF_VALID); + + if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) { + if (irq_data->parent_irq > 31) { + pr_err("irq-s3c24xx: parent irq %lu is out of range\n", + irq_data->parent_irq); + goto err; + } + + parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; + parent_irq_data->sub_intc = intc; + parent_irq_data->sub_bits |= (1UL << hw); + + /* attach the demuxer to the parent irq */ + irqno = irq_find_mapping(parent_intc->domain, + irq_data->parent_irq); + if (!irqno) { + pr_err("irq-s3c24xx: could not find mapping for parent irq %lu\n", + irq_data->parent_irq); + goto err; + } + irq_set_chained_handler(irqno, s3c_irq_demux); + } + + return 0; + +err: + set_irq_flags(virq, 0); + + /* the only error can result from bad mapping data*/ + return -EINVAL; +} + +static struct irq_domain_ops s3c24xx_irq_ops = { + .map = s3c24xx_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static void s3c24xx_clear_intc(struct s3c_irq_intc *intc) +{ + void __iomem *reg_source; + unsigned long pend; + unsigned long last; + int i; + + /* if intpnd is set, read the next pending irq from there */ + reg_source = intc->reg_intpnd ? intc->reg_intpnd : intc->reg_pending; + + last = 0; + for (i = 0; i < 4; i++) { + pend = __raw_readl(reg_source); + + if (pend == 0 || pend == last) + break; + + __raw_writel(pend, intc->reg_pending); + if (intc->reg_intpnd) + __raw_writel(pend, intc->reg_intpnd); + + pr_info("irq: clearing pending status %08x\n", (int)pend); + last = pend; + } +} + +static struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, + struct s3c_irq_data *irq_data, + struct s3c_irq_intc *parent, + unsigned long address) +{ + struct s3c_irq_intc *intc; + void __iomem *base = (void *)0xf6000000; /* static mapping */ + int irq_num; + int irq_start; + int ret; + + intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL); + if (!intc) + return ERR_PTR(-ENOMEM); + + intc->irqs = irq_data; + + if (parent) + intc->parent = parent; + + /* select the correct data for the controller. + * Need to hard code the irq num start and offset + * to preserve the static mapping for now + */ + switch (address) { + case 0x4a000000: + pr_debug("irq: found main intc\n"); + intc->reg_pending = base; + intc->reg_mask = base + 0x08; + intc->reg_intpnd = base + 0x10; + irq_num = 32; + irq_start = S3C2410_IRQ(0); + break; + case 0x4a000018: + pr_debug("irq: found subintc\n"); + intc->reg_pending = base + 0x18; + intc->reg_mask = base + 0x1c; + irq_num = 29; + irq_start = S3C2410_IRQSUB(0); + break; + case 0x4a000040: + pr_debug("irq: found intc2\n"); + intc->reg_pending = base + 0x40; + intc->reg_mask = base + 0x48; + intc->reg_intpnd = base + 0x50; + irq_num = 8; + irq_start = S3C2416_IRQ(0); + break; + case 0x560000a4: + pr_debug("irq: found eintc\n"); + base = (void *)0xfd000000; + + intc->reg_mask = base + 0xa4; + intc->reg_pending = base + 0x08; + irq_num = 24; + irq_start = S3C2410_IRQ(32); + break; + default: + pr_err("irq: unsupported controller address\n"); + ret = -EINVAL; + goto err; + } + + /* now that all the data is complete, init the irq-domain */ + s3c24xx_clear_intc(intc); + intc->domain = irq_domain_add_legacy(np, irq_num, irq_start, + 0, &s3c24xx_irq_ops, + intc); + if (!intc->domain) { + pr_err("irq: could not create irq-domain\n"); + ret = -EINVAL; + goto err; + } + + if (address == 0x4a000000) + main_intc = intc; + else if (address == 0x4a000040) + main_intc2 = intc; + + set_handle_irq(s3c24xx_handle_irq); + + return intc; + +err: + kfree(intc); + return ERR_PTR(ret); +} + +static struct s3c_irq_data init_eint[32] = { + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */ +}; + +#ifdef CONFIG_CPU_S3C2410 +static struct s3c_irq_data init_s3c2410base[32] = { + { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ + { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ + { .type = S3C_IRQTYPE_EDGE, }, /* WDT */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* LCD */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SDI */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ + { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2410subint[32] = { + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ +}; + +void __init s3c2410_init_irq(void) +{ + struct s3c_irq_intc *main_intc; + +#ifdef CONFIG_FIQ + init_FIQ(FIQ_START); +#endif + + main_intc = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL, 0x4a000000); + if (IS_ERR(main_intc)) { + pr_err("irq: could not create main interrupt controller\n"); + return; + } + + s3c24xx_init_intc(NULL, &init_s3c2410subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); +} +#endif + +#ifdef CONFIG_CPU_S3C2412 +static struct s3c_irq_data init_s3c2412base[32] = { + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT0 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT1 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT2 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT3 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ + { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ + { .type = S3C_IRQTYPE_EDGE, }, /* WDT */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* LCD */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* SDI/CF */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ + { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2412eint[32] = { + { .type = S3C_IRQTYPE_EINT, .parent_irq = 0 }, /* EINT0 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 1 }, /* EINT1 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 2 }, /* EINT2 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 3 }, /* EINT3 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */ +}; + +static struct s3c_irq_data init_s3c2412subint[32] = { + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ + { .type = S3C_IRQTYPE_NONE, }, + { .type = S3C_IRQTYPE_NONE, }, + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* SDI */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* CF */ +}; + +void s3c2412_init_irq(void) +{ + struct s3c_irq_intc *main_intc; + + pr_info("S3C2412: IRQ Support\n"); + +#ifdef CONFIG_FIQ + init_FIQ(FIQ_START); +#endif + + main_intc = s3c24xx_init_intc(NULL, &init_s3c2412base[0], NULL, 0x4a000000); + if (IS_ERR(main_intc)) { + pr_err("irq: could not create main interrupt controller\n"); + return; + } + + s3c24xx_init_intc(NULL, &init_s3c2412eint[0], main_intc, 0x560000a4); + s3c24xx_init_intc(NULL, &init_s3c2412subint[0], main_intc, 0x4a000018); +} +#endif + +#ifdef CONFIG_CPU_S3C2416 +static struct s3c_irq_data init_s3c2416base[32] = { + { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ + { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ + { .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* LCD */ + { .type = S3C_IRQTYPE_LEVEL, }, /* DMA */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* NAND */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ + { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ + { .type = S3C_IRQTYPE_NONE, }, + { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2416subint[32] = { + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ +}; + +static struct s3c_irq_data init_s3c2416_second[32] = { + { .type = S3C_IRQTYPE_EDGE }, /* 2D */ + { .type = S3C_IRQTYPE_EDGE }, /* IIC1 */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ + { .type = S3C_IRQTYPE_EDGE }, /* PCM0 */ + { .type = S3C_IRQTYPE_EDGE }, /* PCM1 */ + { .type = S3C_IRQTYPE_EDGE }, /* I2S0 */ + { .type = S3C_IRQTYPE_EDGE }, /* I2S1 */ +}; + +void __init s3c2416_init_irq(void) +{ + struct s3c_irq_intc *main_intc; + + pr_info("S3C2416: IRQ Support\n"); + +#ifdef CONFIG_FIQ + init_FIQ(FIQ_START); +#endif + + main_intc = s3c24xx_init_intc(NULL, &init_s3c2416base[0], NULL, 0x4a000000); + if (IS_ERR(main_intc)) { + pr_err("irq: could not create main interrupt controller\n"); + return; + } + + s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); + s3c24xx_init_intc(NULL, &init_s3c2416subint[0], main_intc, 0x4a000018); + + s3c24xx_init_intc(NULL, &init_s3c2416_second[0], NULL, 0x4a000040); +} + +#endif + +#ifdef CONFIG_CPU_S3C2440 +static struct s3c_irq_data init_s3c2440base[32] = { + { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* CAM */ + { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ + { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ + { .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* LCD */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SDI */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ + { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2440subint[32] = { + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* TC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* ADC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ +}; + +void __init s3c2440_init_irq(void) +{ + struct s3c_irq_intc *main_intc; + + pr_info("S3C2440: IRQ Support\n"); + +#ifdef CONFIG_FIQ + init_FIQ(FIQ_START); +#endif + + main_intc = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL, 0x4a000000); + if (IS_ERR(main_intc)) { + pr_err("irq: could not create main interrupt controller\n"); + return; + } + + s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); + s3c24xx_init_intc(NULL, &init_s3c2440subint[0], main_intc, 0x4a000018); +} +#endif + +#ifdef CONFIG_CPU_S3C2442 +static struct s3c_irq_data init_s3c2442base[32] = { + { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* CAM */ + { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ + { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ + { .type = S3C_IRQTYPE_EDGE, }, /* WDT */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* LCD */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SDI */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ + { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2442subint[32] = { + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* TC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* ADC */ +}; + +void __init s3c2442_init_irq(void) +{ + struct s3c_irq_intc *main_intc; + + pr_info("S3C2442: IRQ Support\n"); + +#ifdef CONFIG_FIQ + init_FIQ(FIQ_START); +#endif + + main_intc = s3c24xx_init_intc(NULL, &init_s3c2442base[0], NULL, 0x4a000000); + if (IS_ERR(main_intc)) { + pr_err("irq: could not create main interrupt controller\n"); + return; + } + + s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); + s3c24xx_init_intc(NULL, &init_s3c2442subint[0], main_intc, 0x4a000018); +} +#endif + +#ifdef CONFIG_CPU_S3C2443 +static struct s3c_irq_data init_s3c2443base[32] = { + { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ + { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* CAM */ + { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ + { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ + { .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* LCD */ + { .type = S3C_IRQTYPE_LEVEL, }, /* DMA */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */ + { .type = S3C_IRQTYPE_EDGE, }, /* CFON */ + { .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* NAND */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ + { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ + { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ + { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ + { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ + { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + + +static struct s3c_irq_data init_s3c2443subint[32] = { + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ + { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD1 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ +}; + +void __init s3c2443_init_irq(void) +{ + struct s3c_irq_intc *main_intc; + + pr_info("S3C2443: IRQ Support\n"); + +#ifdef CONFIG_FIQ + init_FIQ(FIQ_START); +#endif + + main_intc = s3c24xx_init_intc(NULL, &init_s3c2443base[0], NULL, 0x4a000000); + if (IS_ERR(main_intc)) { + pr_err("irq: could not create main interrupt controller\n"); + return; + } + + s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); + s3c24xx_init_intc(NULL, &init_s3c2443subint[0], main_intc, 0x4a000018); +} +#endif -- cgit v1.1 From e2714f79edb8ed4f9afd09ca48bf2abaa994b1c1 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:37 +0900 Subject: irqchip: s3c24xx: fix comments on some camera interrupts Might be confusing for people to read the code without having the datasheet nearby. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 5c9f8b7..84afbc1 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -916,8 +916,8 @@ static struct s3c_irq_data init_s3c2440subint[32] = { { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ - { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* TC */ - { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* ADC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ }; @@ -991,8 +991,8 @@ static struct s3c_irq_data init_s3c2442subint[32] = { { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ - { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* TC */ - { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* ADC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ }; void __init s3c2442_init_irq(void) -- cgit v1.1 From 1ebc7e83b1cc58edae3f371ac9cf1614e0d3b345 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:41 +0900 Subject: irqchip: s3c24xx: fix irqlist of second s3c2416 controller The list in used was from the s3c2450, a close cousin of the s3c2416. As it's not possible to distinguish between the s3c2416 and s3c2450 the additional interrupts of the s3c2450 will only be available thru devicetree later. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 84afbc1..a565eb8 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -835,13 +835,12 @@ static struct s3c_irq_data init_s3c2416subint[32] = { static struct s3c_irq_data init_s3c2416_second[32] = { { .type = S3C_IRQTYPE_EDGE }, /* 2D */ - { .type = S3C_IRQTYPE_EDGE }, /* IIC1 */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ { .type = S3C_IRQTYPE_NONE }, /* reserved */ { .type = S3C_IRQTYPE_NONE }, /* reserved */ { .type = S3C_IRQTYPE_EDGE }, /* PCM0 */ - { .type = S3C_IRQTYPE_EDGE }, /* PCM1 */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ { .type = S3C_IRQTYPE_EDGE }, /* I2S0 */ - { .type = S3C_IRQTYPE_EDGE }, /* I2S1 */ }; void __init s3c2416_init_irq(void) -- cgit v1.1 From bd7c0da2c1590ea292052b873f93d2c1dbbf7d28 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:45 +0900 Subject: irqchip: s3c24xx: add irq_set_type callback for basic interrupt types Enables post-init setting of the desired typehandler for the interrupt. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index a565eb8..7cba4f00 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -123,6 +123,28 @@ static inline void s3c_irq_ack(struct irq_data *data) __raw_writel(bitval, intc->reg_intpnd); } +static int s3c_irq_type(struct irq_data *data, unsigned int type) +{ + switch (type) { + case IRQ_TYPE_NONE: + break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + irq_set_handler(data->irq, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_LOW: + case IRQ_TYPE_LEVEL_HIGH: + irq_set_handler(data->irq, handle_level_irq); + break; + default: + pr_err("No such irq type %d", type); + return -EINVAL; + } + + return 0; +} + static int s3c_irqext_type_set(void __iomem *gpcon_reg, void __iomem *extint_reg, unsigned long gpcon_offset, @@ -228,6 +250,7 @@ static struct irq_chip s3c_irq_chip = { .irq_ack = s3c_irq_ack, .irq_mask = s3c_irq_mask, .irq_unmask = s3c_irq_unmask, + .irq_set_type = s3c_irq_type, .irq_set_wake = s3c_irq_wake }; @@ -236,6 +259,7 @@ static struct irq_chip s3c_irq_level_chip = { .irq_mask = s3c_irq_mask, .irq_unmask = s3c_irq_unmask, .irq_ack = s3c_irq_ack, + .irq_set_type = s3c_irq_type, }; static struct irq_chip s3c_irqext_chip = { -- cgit v1.1 From 658dc8fb9cbc7712b1518ce79211ffadcabd1c9e Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:49 +0900 Subject: irqchip: s3c24xx: globally keep track of the created intc instances For dt-enabled machines we want to use a big irq_domain over all controllers and therefore need to access not only the main controllers but the sub-controller as well. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 99 ++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 7cba4f00..9914abd 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -69,6 +69,14 @@ struct s3c_irq_intc { struct s3c_irq_data *irqs; }; +/* + * Array holding pointers to the global controller structs + * [0] ... main_intc + * [1] ... sub_intc + * [2] ... main_intc2 on s3c2416 + */ +static struct s3c_irq_intc *s3c_intc[3]; + static void s3c_irq_mask(struct irq_data *data) { struct s3c_irq_intc *intc = data->domain->host_data; @@ -307,9 +315,6 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } -static struct s3c_irq_intc *main_intc; -static struct s3c_irq_intc *main_intc2; - static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, struct pt_regs *regs) { @@ -345,12 +350,12 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs) { do { - if (likely(main_intc)) - if (s3c24xx_handle_intc(main_intc, regs)) + if (likely(s3c_intc[0])) + if (s3c24xx_handle_intc(s3c_intc[0], regs)) continue; - if (main_intc2) - if (s3c24xx_handle_intc(main_intc2, regs)) + if (s3c_intc[2]) + if (s3c24xx_handle_intc(s3c_intc[2], regs)) continue; break; @@ -577,11 +582,6 @@ static struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, goto err; } - if (address == 0x4a000000) - main_intc = intc; - else if (address == 0x4a000040) - main_intc2 = intc; - set_handle_irq(s3c24xx_handle_irq); return intc; @@ -670,20 +670,20 @@ static struct s3c_irq_data init_s3c2410subint[32] = { void __init s3c2410_init_irq(void) { - struct s3c_irq_intc *main_intc; - #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_s3c2410subint[0], main_intc, 0x4a000018); - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2410subint[0], + s3c_intc[0], 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); } #endif @@ -770,22 +770,22 @@ static struct s3c_irq_data init_s3c2412subint[32] = { void s3c2412_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2412: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2412base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2412base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_s3c2412eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2412subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_s3c2412eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2412subint[0], + s3c_intc[0], 0x4a000018); } #endif @@ -869,24 +869,25 @@ static struct s3c_irq_data init_s3c2416_second[32] = { void __init s3c2416_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2416: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2416base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2416base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2416subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2416subint[0], + s3c_intc[0], 0x4a000018); - s3c24xx_init_intc(NULL, &init_s3c2416_second[0], NULL, 0x4a000040); + s3c_intc[2] = s3c24xx_init_intc(NULL, &init_s3c2416_second[0], + NULL, 0x4a000040); } #endif @@ -947,22 +948,22 @@ static struct s3c_irq_data init_s3c2440subint[32] = { void __init s3c2440_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2440: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2440subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2440subint[0], + s3c_intc[0], 0x4a000018); } #endif @@ -1020,22 +1021,22 @@ static struct s3c_irq_data init_s3c2442subint[32] = { void __init s3c2442_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2442: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2442base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2442base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2442subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2442subint[0], + s3c_intc[0], 0x4a000018); } #endif @@ -1110,21 +1111,21 @@ static struct s3c_irq_data init_s3c2443subint[32] = { void __init s3c2443_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2443: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2443base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2443base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2443subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2443subint[0], + s3c_intc[0], 0x4a000018); } #endif -- cgit v1.1 From f5a25524508e68ac670b28db9112d3962dca4703 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:52 +0900 Subject: irqchip: s3c24xx: make interrupt handling independent of irq_domain structure Keep a pointer to the corresponding s3c_irq_data struct as irq_chip_data. This removes the need to fetch the intc struct from the irq_domains host_data, thus making it independent of the underlying irq_domain structure. Also keep the real register offset of the interrupt in the s3c_irq_data struct to make it independent of the hwirq structure in the irq_domain Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 9914abd..02b82db 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -43,6 +43,7 @@ struct s3c_irq_data { unsigned int type; + unsigned long offset; unsigned long parent_irq; /* data gets filled during init */ @@ -79,15 +80,15 @@ static struct s3c_irq_intc *s3c_intc[3]; static void s3c_irq_mask(struct irq_data *data) { - struct s3c_irq_intc *intc = data->domain->host_data; + struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); + struct s3c_irq_intc *intc = irq_data->intc; struct s3c_irq_intc *parent_intc = intc->parent; - struct s3c_irq_data *irq_data = &intc->irqs[data->hwirq]; struct s3c_irq_data *parent_data; unsigned long mask; unsigned int irqno; mask = __raw_readl(intc->reg_mask); - mask |= (1UL << data->hwirq); + mask |= (1UL << irq_data->offset); __raw_writel(mask, intc->reg_mask); if (parent_intc) { @@ -104,14 +105,14 @@ static void s3c_irq_mask(struct irq_data *data) static void s3c_irq_unmask(struct irq_data *data) { - struct s3c_irq_intc *intc = data->domain->host_data; + struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); + struct s3c_irq_intc *intc = irq_data->intc; struct s3c_irq_intc *parent_intc = intc->parent; - struct s3c_irq_data *irq_data = &intc->irqs[data->hwirq]; unsigned long mask; unsigned int irqno; mask = __raw_readl(intc->reg_mask); - mask &= ~(1UL << data->hwirq); + mask &= ~(1UL << irq_data->offset); __raw_writel(mask, intc->reg_mask); if (parent_intc) { @@ -123,8 +124,9 @@ static void s3c_irq_unmask(struct irq_data *data) static inline void s3c_irq_ack(struct irq_data *data) { - struct s3c_irq_intc *intc = data->domain->host_data; - unsigned long bitval = 1UL << data->hwirq; + struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); + struct s3c_irq_intc *intc = irq_data->intc; + unsigned long bitval = 1UL << irq_data->offset; __raw_writel(bitval, intc->reg_pending); if (intc->reg_intpnd) @@ -291,8 +293,7 @@ static struct irq_chip s3c_irq_eint0t4 = { static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); - struct s3c_irq_intc *intc = desc->irq_data.domain->host_data; - struct s3c_irq_data *irq_data = &intc->irqs[desc->irq_data.hwirq]; + struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc); struct s3c_irq_intc *sub_intc = irq_data->sub_intc; unsigned long src; unsigned long msk; @@ -406,6 +407,7 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, /* attach controller pointer to irq_data */ irq_data->intc = intc; + irq_data->offset = hw; parent_intc = intc->parent; @@ -444,6 +446,9 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, pr_err("irq-s3c24xx: unsupported irqtype %d\n", irq_data->type); return -EINVAL; } + + irq_set_chip_data(virq, irq_data); + set_irq_flags(virq, IRQF_VALID); if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) { -- cgit v1.1 From f0774d41da0e607b70e54ecc50aeb6684f54c2b1 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:55:10 +0900 Subject: irqchip: s3c24xx: add devicetree support Add the necessary code to initialize the interrupt controller thru devicetree data using the irqchip infrastructure. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Acked-by: Rob Herring Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 231 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 225 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 02b82db..5e40b34 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include #include @@ -36,6 +39,8 @@ #include #include +#include "irqchip.h" + #define S3C_IRQTYPE_NONE 0 #define S3C_IRQTYPE_EINT 1 #define S3C_IRQTYPE_EDGE 2 @@ -94,7 +99,10 @@ static void s3c_irq_mask(struct irq_data *data) if (parent_intc) { parent_data = &parent_intc->irqs[irq_data->parent_irq]; - /* check to see if we need to mask the parent IRQ */ + /* check to see if we need to mask the parent IRQ + * The parent_irq is always in main_intc, so the hwirq + * for find_mapping does not need an offset in any case. + */ if ((mask & parent_data->sub_bits) == parent_data->sub_bits) { irqno = irq_find_mapping(parent_intc->domain, irq_data->parent_irq); @@ -294,10 +302,18 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc); + struct s3c_irq_intc *intc = irq_data->intc; struct s3c_irq_intc *sub_intc = irq_data->sub_intc; unsigned long src; unsigned long msk; unsigned int n; + unsigned int offset; + + /* we're using individual domains for the non-dt case + * and one big domain for the dt case where the subintc + * starts at hwirq number 32. + */ + offset = (intc->domain->of_node) ? 32 : 0; chained_irq_enter(chip, desc); @@ -310,14 +326,15 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) while (src) { n = __ffs(src); src &= ~(1 << n); - generic_handle_irq(irq_find_mapping(sub_intc->domain, n)); + irq = irq_find_mapping(sub_intc->domain, offset + n); + generic_handle_irq(irq); } chained_irq_exit(chip, desc); } static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, - struct pt_regs *regs) + struct pt_regs *regs, int intc_offset) { int pnd; int offset; @@ -327,6 +344,10 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, if (!pnd) return false; + /* non-dt machines use individual domains */ + if (!intc->domain->of_node) + intc_offset = 0; + /* We have a problem that the INTOFFSET register does not always * show one interrupt. Occasionally we get two interrupts through * the prioritiser, and this causes the INTOFFSET register to show @@ -343,7 +364,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, if (!(pnd & (1 << offset))) offset = __ffs(pnd); - irq = irq_find_mapping(intc->domain, offset); + irq = irq_find_mapping(intc->domain, intc_offset + offset); handle_IRQ(irq, regs); return true; } @@ -352,11 +373,11 @@ asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs) { do { if (likely(s3c_intc[0])) - if (s3c24xx_handle_intc(s3c_intc[0], regs)) + if (s3c24xx_handle_intc(s3c_intc[0], regs, 0)) continue; if (s3c_intc[2]) - if (s3c24xx_handle_intc(s3c_intc[2], regs)) + if (s3c24xx_handle_intc(s3c_intc[2], regs, 64)) continue; break; @@ -1134,3 +1155,201 @@ void __init s3c2443_init_irq(void) s3c_intc[0], 0x4a000018); } #endif + +#ifdef CONFIG_OF +static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + unsigned int ctrl_num = hw / 32; + unsigned int intc_hw = hw % 32; + struct s3c_irq_intc *intc = s3c_intc[ctrl_num]; + struct s3c_irq_intc *parent_intc = intc->parent; + struct s3c_irq_data *irq_data = &intc->irqs[intc_hw]; + + /* attach controller pointer to irq_data */ + irq_data->intc = intc; + irq_data->offset = intc_hw; + + if (!parent_intc) + irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq); + else + irq_set_chip_and_handler(virq, &s3c_irq_level_chip, + handle_edge_irq); + + irq_set_chip_data(virq, irq_data); + + set_irq_flags(virq, IRQF_VALID); + + return 0; +} + +/* Translate our of irq notation + * format: + */ +static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_type) +{ + struct s3c_irq_intc *intc; + struct s3c_irq_intc *parent_intc; + struct s3c_irq_data *irq_data; + struct s3c_irq_data *parent_irq_data; + int irqno; + + if (WARN_ON(intsize < 4)) + return -EINVAL; + + if (intspec[0] > 2 || !s3c_intc[intspec[0]]) { + pr_err("controller number %d invalid\n", intspec[0]); + return -EINVAL; + } + intc = s3c_intc[intspec[0]]; + + *out_hwirq = intspec[0] * 32 + intspec[2]; + *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; + + parent_intc = intc->parent; + if (parent_intc) { + irq_data = &intc->irqs[intspec[2]]; + irq_data->parent_irq = intspec[1]; + parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; + parent_irq_data->sub_intc = intc; + parent_irq_data->sub_bits |= (1UL << intspec[2]); + + /* parent_intc is always s3c_intc[0], so no offset */ + irqno = irq_create_mapping(parent_intc->domain, intspec[1]); + if (irqno < 0) { + pr_err("irq: could not map parent interrupt\n"); + return irqno; + } + + irq_set_chained_handler(irqno, s3c_irq_demux); + } + + return 0; +} + +static struct irq_domain_ops s3c24xx_irq_ops_of = { + .map = s3c24xx_irq_map_of, + .xlate = s3c24xx_irq_xlate_of, +}; + +struct s3c24xx_irq_of_ctrl { + char *name; + unsigned long offset; + struct s3c_irq_intc **handle; + struct s3c_irq_intc **parent; + struct irq_domain_ops *ops; +}; + +static int __init s3c_init_intc_of(struct device_node *np, + struct device_node *interrupt_parent, + struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl) +{ + struct s3c_irq_intc *intc; + struct s3c24xx_irq_of_ctrl *ctrl; + struct irq_domain *domain; + void __iomem *reg_base; + int i; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("irq-s3c24xx: could not map irq registers\n"); + return -EINVAL; + } + + domain = irq_domain_add_linear(np, num_ctrl * 32, + &s3c24xx_irq_ops_of, NULL); + if (!domain) { + pr_err("irq: could not create irq-domain\n"); + return -EINVAL; + } + + for (i = 0; i < num_ctrl; i++) { + ctrl = &s3c_ctrl[i]; + + pr_debug("irq: found controller %s\n", ctrl->name); + + intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL); + if (!intc) + return -ENOMEM; + + intc->domain = domain; + intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32, + GFP_KERNEL); + if (!intc->irqs) { + kfree(intc); + return -ENOMEM; + } + + if (ctrl->parent) { + intc->reg_pending = reg_base + ctrl->offset; + intc->reg_mask = reg_base + ctrl->offset + 0x4; + + if (*(ctrl->parent)) { + intc->parent = *(ctrl->parent); + } else { + pr_warn("irq: parent of %s missing\n", + ctrl->name); + kfree(intc->irqs); + kfree(intc); + continue; + } + } else { + intc->reg_pending = reg_base + ctrl->offset; + intc->reg_mask = reg_base + ctrl->offset + 0x08; + intc->reg_intpnd = reg_base + ctrl->offset + 0x10; + } + + s3c24xx_clear_intc(intc); + s3c_intc[i] = intc; + } + + set_handle_irq(s3c24xx_handle_irq); + + return 0; +} + +static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = { + { + .name = "intc", + .offset = 0, + }, { + .name = "subintc", + .offset = 0x18, + .parent = &s3c_intc[0], + } +}; + +int __init s3c2410_init_intc_of(struct device_node *np, + struct device_node *interrupt_parent, + struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) +{ + return s3c_init_intc_of(np, interrupt_parent, + s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl)); +} +IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of); + +static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = { + { + .name = "intc", + .offset = 0, + }, { + .name = "subintc", + .offset = 0x18, + .parent = &s3c_intc[0], + }, { + .name = "intc2", + .offset = 0x40, + } +}; + +int __init s3c2416_init_intc_of(struct device_node *np, + struct device_node *interrupt_parent, + struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) +{ + return s3c_init_intc_of(np, interrupt_parent, + s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl)); +} +IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c2416_init_intc_of); +#endif -- cgit v1.1 From d8acac566c9b8ba96c278bf17b8acd549c99e621 Mon Sep 17 00:00:00 2001 From: Doug Anderson <[dianders@chromium.org]> Date: Thu, 4 Apr 2013 15:24:32 +0900 Subject: clocksource: mct: Add missing semicolons in exynos_mct.c The CLOCKSOURCE_OF_DECLARE lines were added without a semicolon at the end. On my system this causes a compile-time error that looks like: drivers/clocksource/exynos_mct.c:557:202: warning: comparison of distinct pointer types lacks a cast [enabled by default] drivers/clocksource/exynos_mct.c:558:1: error: expected ',' or ';' before 'static' The error didn't show up till now because there was an extra semicolon at end of the CLOCKSOURCE_OF_DECLARE definition that was removed by Arnd Bergmann in "clocksource: make CLOCKSOURCE_OF_DECLARE type safe" Signed-off-by: Doug Anderson Signed-off-by: Kukjin Kim --- drivers/clocksource/exynos_mct.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 545c989..401fcf3 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -546,5 +546,5 @@ void __init mct_init(void) exynos4_clocksource_init(); exynos4_clockevent_init(); } -CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init) -CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init) +CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init); +CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init); -- cgit v1.1 From 354599f460ba79c9fb00f220e42de5a7509ceeb4 Mon Sep 17 00:00:00 2001 From: Axel Lin <[axel.lin@ingics.com]> Date: Thu, 4 Apr 2013 15:34:45 +0900 Subject: clocksource: mct: Add terminating entry for exynos_mct_ids table The of_device_id table is supposed to be zero-terminated. Signed-off-by: Axel Lin Signed-off-by: Kukjin Kim --- drivers/clocksource/exynos_mct.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 401fcf3..d3f1327 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -506,6 +506,7 @@ static void __init exynos4_timer_resources(struct device_node *np) 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) -- cgit v1.1 From 36fc09722d49077c6a602e8c07b06d21e798b75a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 4 Apr 2013 13:32:33 +0900 Subject: clk: exynos4: Correct sclk_mfc clock definition This clock must be exported to allow lookup using device tree. Signed-off-by: Sylwester Nawrocki Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index e1bb81a..44a99b5 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -122,7 +122,7 @@ enum exynos4_clks { sclk_sata, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_uart4, sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2, sclk_slimbus, sclk_fimd1, sclk_mipi1, sclk_pcm1, sclk_pcm2, sclk_i2s1, - sclk_i2s2, sclk_mipihsi, + sclk_i2s2, sclk_mipihsi, sclk_mfc, /* gate clocks */ fimc0 = 256, fimc1, fimc2, fimc3, csis0, csis1, jpeg, smmu_fimc0, @@ -355,7 +355,7 @@ struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(none, "div_cam1", "mout_cam1", DIV_CAM, 20, 4), DIV(none, "div_csis0", "mout_csis0", DIV_CAM, 24, 4), DIV(none, "div_csis1", "mout_csis1", DIV_CAM, 28, 4), - DIV(none, "div_mfc", "mout_mfc", DIV_MFC, 0, 4), + DIV(sclk_mfc, "sclk_mfc", "mout_mfc", DIV_MFC, 0, 4), DIV(none, "div_g3d", "mout_g3d", DIV_G3D, 0, 4), DIV(none, "div_fimd0", "mout_fimd0", DIV_LCD0, 0, 4), DIV(none, "div_mipi0", "mout_mipi0", DIV_LCD0, 16, 4), -- cgit v1.1 From 74f7f8ba5092a76da1e9d07f245575cef86f15ab Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:32:37 +0900 Subject: clk: exynos4: Use mout_mpll_user_* on Exynos4x12 Many clock muxes of Exynos 4x12 uses mout_mpll_user_* clocks instead of sclk_mpll as one of their parents. This patch moves such clocks from common array into SoC-specific arrays and adjusts their parent lists respectively. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 172 ++++++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 44a99b5..8edd64c 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -112,7 +112,7 @@ enum exynos4_clks { /* core clocks */ xxti, xusbxti, fin_pll, fout_apll, fout_mpll, fout_epll, fout_vpll, sclk_apll, sclk_mpll, sclk_epll, sclk_vpll, arm_clk, aclk200, aclk100, - aclk160, aclk133, + aclk160, aclk133, mout_mpll_user_t, mout_mpll_user_c, /* 18 */ /* gate for special clocks (sclk) */ sclk_fimc0 = 128, sclk_fimc1, sclk_fimc2, sclk_fimc3, sclk_cam0, @@ -218,35 +218,53 @@ PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", }; PNAME(mout_epll_p) = { "fin_pll", "fout_epll", }; PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi24m", }; -PNAME(sclk_vpll_p4210) = { "mout_vpllsrc", "fout_vpll", }; PNAME(mout_vpll_p) = { "fin_pll", "fout_vpll", }; -PNAME(mout_core_p) = { "mout_apll", "sclk_mpll", }; -PNAME(sclk_ampll_p) = { "sclk_mpll", "sclk_apll", }; -PNAME(mout_mpll_user_p) = { "fin_pll", "sclk_mpll", }; -PNAME(aclk_p4412) = { "mout_mpll_user", "sclk_apll", }; PNAME(sclk_evpll_p) = { "sclk_epll", "sclk_vpll", }; PNAME(mout_mfc_p) = { "mout_mfc0", "mout_mfc1", }; PNAME(mout_g3d_p) = { "mout_g3d0", "mout_g3d1", }; PNAME(mout_g2d_p) = { "mout_g2d0", "mout_g2d1", }; -PNAME(mout_mixer_p4210) = { "sclk_dac", "sclk_hdmi", }; -PNAME(mout_dac_p4210) = { "sclk_vpll", "sclk_hdmiphy", }; PNAME(mout_hdmi_p) = { "sclk_pixel", "sclk_hdmiphy", }; PNAME(mout_jpeg_p) = { "mout_jpeg0", "mout_jpeg1", }; -PNAME(group1_p) = { "xxti", "xusbxti", "sclk_hdmi24m", "sclk_usbphy0", - "none", "sclk_hdmiphy", "sclk_mpll", - "sclk_epll", "sclk_vpll", }; -PNAME(mout_audio0_p) = { "cdclk0", "none", "sclk_hdmi24m", "sclk_usbphy0", - "xxti", "xusbxti", "sclk_mpll", "sclk_epll", - "sclk_vpll" }; -PNAME(mout_audio1_p) = { "cdclk1", "none", "sclk_hdmi24m", "sclk_usbphy0", - "xxti", "xusbxti", "sclk_mpll", "sclk_epll", - "sclk_vpll", }; -PNAME(mout_audio2_p) = { "cdclk2", "none", "sclk_hdmi24m", "sclk_usbphy0", - "xxti", "xusbxti", "sclk_mpll", "sclk_epll", - "sclk_vpll", }; PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2", "spdif_extclk", }; +/* Exynos 4210-specific parent groups */ +PNAME(sclk_vpll_p4210) = { "mout_vpllsrc", "fout_vpll", }; +PNAME(mout_core_p4210) = { "mout_apll", "sclk_mpll", }; +PNAME(sclk_ampll_p4210) = { "sclk_mpll", "sclk_apll", }; +PNAME(group1_p4210) = { "xxti", "xusbxti", "sclk_hdmi24m", + "sclk_usbphy0", "none", "sclk_hdmiphy", + "sclk_mpll", "sclk_epll", "sclk_vpll", }; +PNAME(mout_audio0_p4210) = { "cdclk0", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", "sclk_mpll", + "sclk_epll", "sclk_vpll" }; +PNAME(mout_audio1_p4210) = { "cdclk1", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", "sclk_mpll", + "sclk_epll", "sclk_vpll", }; +PNAME(mout_audio2_p4210) = { "cdclk2", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", "sclk_mpll", + "sclk_epll", "sclk_vpll", }; +PNAME(mout_mixer_p4210) = { "sclk_dac", "sclk_hdmi", }; +PNAME(mout_dac_p4210) = { "sclk_vpll", "sclk_hdmiphy", }; + +/* Exynos 4x12-specific parent groups */ +PNAME(mout_mpll_user_p4x12) = { "fin_pll", "sclk_mpll", }; +PNAME(mout_core_p4x12) = { "mout_apll", "mout_mpll_user_c", }; +PNAME(sclk_ampll_p4x12) = { "mout_mpll_user_t", "sclk_apll", }; +PNAME(group1_p4x12) = { "xxti", "xusbxti", "sclk_hdmi24m", "sclk_usbphy0", + "none", "sclk_hdmiphy", "mout_mpll_user_t", + "sclk_epll", "sclk_vpll", }; +PNAME(mout_audio0_p4x12) = { "cdclk0", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", + "mout_mpll_user_t", "sclk_epll", "sclk_vpll" }; +PNAME(mout_audio1_p4x12) = { "cdclk1", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", + "mout_mpll_user_t", "sclk_epll", "sclk_vpll", }; +PNAME(mout_audio2_p4x12) = { "cdclk2", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", + "mout_mpll_user_t", "sclk_epll", "sclk_vpll", }; +PNAME(aclk_p4412) = { "mout_mpll_user_t", "sclk_apll", }; + /* fixed rate clocks generated outside the soc */ struct samsung_fixed_rate_clock exynos4_fixed_rate_ext_clks[] __initdata = { FRATE(xxti, "xxti", NULL, CLK_IS_ROOT, 0), @@ -267,80 +285,112 @@ struct samsung_fixed_rate_clock exynos4210_fixed_rate_clks[] __initdata = { /* list of mux clocks supported in all exynos4 soc's */ struct samsung_mux_clock exynos4_mux_clks[] __initdata = { MUX(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), - MUX(none, "mout_core", mout_core_p, SRC_CPU, 16, 1), - MUX(none, "mout_fimc0", group1_p, SRC_CAM, 0, 4), - MUX(none, "mout_fimc1", group1_p, SRC_CAM, 4, 4), - MUX(none, "mout_fimc2", group1_p, SRC_CAM, 8, 4), - MUX(none, "mout_fimc3", group1_p, SRC_CAM, 12, 4), - MUX(none, "mout_cam0", group1_p, SRC_CAM, 16, 4), - MUX(none, "mout_cam1", group1_p, SRC_CAM, 20, 4), - MUX(none, "mout_csis0", group1_p, SRC_CAM, 24, 4), - MUX(none, "mout_csis1", group1_p, SRC_CAM, 28, 4), MUX(none, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), - MUX(none, "mout_mfc0", sclk_ampll_p, SRC_MFC, 0, 1), MUX(none, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), MUX(none, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), - MUX(none, "mout_g3d0", sclk_ampll_p, SRC_G3D, 0, 1), MUX(none, "mout_g3d1", sclk_evpll_p, SRC_G3D, 4, 1), MUX(none, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1), - MUX(none, "mout_fimd0", group1_p, SRC_LCD0, 0, 4), - MUX(none, "mout_mipi0", group1_p, SRC_LCD0, 12, 4), - MUX(none, "mout_audio0", mout_audio0_p, SRC_MAUDIO, 0, 4), - MUX(none, "mout_mmc0", group1_p, SRC_FSYS, 0, 4), - MUX(none, "mout_mmc1", group1_p, SRC_FSYS, 4, 4), - MUX(none, "mout_mmc2", group1_p, SRC_FSYS, 8, 4), - MUX(none, "mout_mmc3", group1_p, SRC_FSYS, 12, 4), - MUX(none, "mout_mmc4", group1_p, SRC_FSYS, 16, 4), - MUX(none, "mout_uart0", group1_p, SRC_PERIL0, 0, 4), - MUX(none, "mout_uart1", group1_p, SRC_PERIL0, 4, 4), - MUX(none, "mout_uart2", group1_p, SRC_PERIL0, 8, 4), - MUX(none, "mout_uart3", group1_p, SRC_PERIL0, 12, 4), - MUX(none, "mout_uart4", group1_p, SRC_PERIL0, 16, 4), - MUX(none, "mout_audio1", mout_audio1_p, SRC_PERIL1, 0, 4), - MUX(none, "mout_audio2", mout_audio2_p, SRC_PERIL1, 4, 4), MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIL1, 8, 2), - MUX(none, "mout_spi0", group1_p, SRC_PERIL1, 16, 4), - MUX(none, "mout_spi1", group1_p, SRC_PERIL1, 20, 4), - MUX(none, "mout_spi2", group1_p, SRC_PERIL1, 24, 4), MUX_A(sclk_epll, "sclk_epll", mout_epll_p, SRC_TOP0, 4, 1, "sclk_epll"), }; /* list of mux clocks supported in exynos4210 soc */ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { - MUX(none, "mout_aclk200", sclk_ampll_p, SRC_TOP0, 12, 1), - MUX(none, "mout_aclk100", sclk_ampll_p, SRC_TOP0, 16, 1), - MUX(none, "mout_aclk160", sclk_ampll_p, SRC_TOP0, 20, 1), - MUX(none, "mout_aclk133", sclk_ampll_p, SRC_TOP0, 24, 1), + MUX(none, "mout_aclk200", sclk_ampll_p4210, SRC_TOP0, 12, 1), + MUX(none, "mout_aclk100", sclk_ampll_p4210, SRC_TOP0, 16, 1), + MUX(none, "mout_aclk160", sclk_ampll_p4210, SRC_TOP0, 20, 1), + MUX(none, "mout_aclk133", sclk_ampll_p4210, SRC_TOP0, 24, 1), MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP1, 0, 1), MUX(none, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1), MUX(none, "mout_dac", mout_dac_p4210, SRC_TV, 8, 1), - MUX(none, "mout_g2d0", sclk_ampll_p, E4210_SRC_IMAGE, 0, 1), + MUX(none, "mout_g2d0", sclk_ampll_p4210, E4210_SRC_IMAGE, 0, 1), MUX(none, "mout_g2d1", sclk_evpll_p, E4210_SRC_IMAGE, 4, 1), MUX(none, "mout_g2d", mout_g2d_p, E4210_SRC_IMAGE, 8, 1), - MUX(none, "mout_fimd1", group1_p, SRC_LCD1, 0, 4), - MUX(none, "mout_mipi1", group1_p, SRC_LCD1, 12, 4), + MUX(none, "mout_fimd1", group1_p4210, SRC_LCD1, 0, 4), + MUX(none, "mout_mipi1", group1_p4210, SRC_LCD1, 12, 4), MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1, "sclk_mpll"), + MUX(none, "mout_core", mout_core_p4210, SRC_CPU, 16, 1), MUX_A(sclk_vpll, "sclk_vpll", sclk_vpll_p4210, SRC_TOP0, 8, 1, "sclk_vpll"), + MUX(none, "mout_fimc0", group1_p4210, SRC_CAM, 0, 4), + MUX(none, "mout_fimc1", group1_p4210, SRC_CAM, 4, 4), + MUX(none, "mout_fimc2", group1_p4210, SRC_CAM, 8, 4), + MUX(none, "mout_fimc3", group1_p4210, SRC_CAM, 12, 4), + MUX(none, "mout_cam0", group1_p4210, SRC_CAM, 16, 4), + MUX(none, "mout_cam1", group1_p4210, SRC_CAM, 20, 4), + MUX(none, "mout_csis0", group1_p4210, SRC_CAM, 24, 4), + MUX(none, "mout_csis1", group1_p4210, SRC_CAM, 28, 4), + MUX(none, "mout_mfc0", sclk_ampll_p4210, SRC_MFC, 0, 1), + MUX(none, "mout_g3d0", sclk_ampll_p4210, SRC_G3D, 0, 1), + MUX(none, "mout_fimd0", group1_p4210, SRC_LCD0, 0, 4), + MUX(none, "mout_mipi0", group1_p4210, SRC_LCD0, 12, 4), + MUX(none, "mout_audio0", mout_audio0_p4210, SRC_MAUDIO, 0, 4), + MUX(none, "mout_mmc0", group1_p4210, SRC_FSYS, 0, 4), + MUX(none, "mout_mmc1", group1_p4210, SRC_FSYS, 4, 4), + MUX(none, "mout_mmc2", group1_p4210, SRC_FSYS, 8, 4), + MUX(none, "mout_mmc3", group1_p4210, SRC_FSYS, 12, 4), + MUX(none, "mout_mmc4", group1_p4210, SRC_FSYS, 16, 4), + MUX(none, "mout_uart0", group1_p4210, SRC_PERIL0, 0, 4), + MUX(none, "mout_uart1", group1_p4210, SRC_PERIL0, 4, 4), + MUX(none, "mout_uart2", group1_p4210, SRC_PERIL0, 8, 4), + MUX(none, "mout_uart3", group1_p4210, SRC_PERIL0, 12, 4), + MUX(none, "mout_uart4", group1_p4210, SRC_PERIL0, 16, 4), + MUX(none, "mout_audio1", mout_audio1_p4210, SRC_PERIL1, 0, 4), + MUX(none, "mout_audio2", mout_audio2_p4210, SRC_PERIL1, 4, 4), + MUX(none, "mout_spi0", group1_p4210, SRC_PERIL1, 16, 4), + MUX(none, "mout_spi1", group1_p4210, SRC_PERIL1, 20, 4), + MUX(none, "mout_spi2", group1_p4210, SRC_PERIL1, 24, 4), }; /* list of mux clocks supported in exynos4x12 soc */ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { - MUX(none, "mout_mpll_user", mout_mpll_user_p, SRC_LEFTBUS, 4, 1), + MUX(mout_mpll_user_c, "mout_mpll_user_c", mout_mpll_user_p4x12, + SRC_CPU, 24, 1), + MUX(mout_mpll_user_t, "mout_mpll_user_t", mout_mpll_user_p4x12, + SRC_TOP1, 12, 1), MUX(none, "mout_aclk200", aclk_p4412, SRC_TOP0, 12, 1), MUX(none, "mout_aclk100", aclk_p4412, SRC_TOP0, 16, 1), MUX(none, "mout_aclk160", aclk_p4412, SRC_TOP0, 20, 1), MUX(none, "mout_aclk133", aclk_p4412, SRC_TOP0, 24, 1), - MUX(none, "mout_mdnie0", group1_p, SRC_LCD0, 4, 4), - MUX(none, "mout_mdnie_pwm0", group1_p, SRC_LCD0, 8, 4), - MUX(none, "mout_sata", sclk_ampll_p, SRC_FSYS, 24, 1), - MUX(none, "mout_jpeg0", sclk_ampll_p, E4X12_SRC_CAM1, 0, 1), + MUX(none, "mout_mdnie0", group1_p4x12, SRC_LCD0, 4, 4), + MUX(none, "mout_mdnie_pwm0", group1_p4x12, SRC_LCD0, 8, 4), + MUX(none, "mout_sata", sclk_ampll_p4x12, SRC_FSYS, 24, 1), + MUX(none, "mout_jpeg0", sclk_ampll_p4x12, E4X12_SRC_CAM1, 0, 1), MUX(none, "mout_jpeg1", sclk_evpll_p, E4X12_SRC_CAM1, 4, 1), MUX(none, "mout_jpeg", mout_jpeg_p, E4X12_SRC_CAM1, 8, 1), MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, E4X12_SRC_DMC, 12, 1, "sclk_mpll"), MUX_A(sclk_vpll, "sclk_vpll", mout_vpll_p, SRC_TOP0, 8, 1, "sclk_vpll"), + MUX(none, "mout_core", mout_core_p4x12, SRC_CPU, 16, 1), + MUX(none, "mout_fimc0", group1_p4x12, SRC_CAM, 0, 4), + MUX(none, "mout_fimc1", group1_p4x12, SRC_CAM, 4, 4), + MUX(none, "mout_fimc2", group1_p4x12, SRC_CAM, 8, 4), + MUX(none, "mout_fimc3", group1_p4x12, SRC_CAM, 12, 4), + MUX(none, "mout_cam0", group1_p4x12, SRC_CAM, 16, 4), + MUX(none, "mout_cam1", group1_p4x12, SRC_CAM, 20, 4), + MUX(none, "mout_csis0", group1_p4x12, SRC_CAM, 24, 4), + MUX(none, "mout_csis1", group1_p4x12, SRC_CAM, 28, 4), + MUX(none, "mout_mfc0", sclk_ampll_p4x12, SRC_MFC, 0, 1), + MUX(none, "mout_g3d0", sclk_ampll_p4x12, SRC_G3D, 0, 1), + MUX(none, "mout_fimd0", group1_p4x12, SRC_LCD0, 0, 4), + MUX(none, "mout_mipi0", group1_p4x12, SRC_LCD0, 12, 4), + MUX(none, "mout_audio0", mout_audio0_p4x12, SRC_MAUDIO, 0, 4), + MUX(none, "mout_mmc0", group1_p4x12, SRC_FSYS, 0, 4), + MUX(none, "mout_mmc1", group1_p4x12, SRC_FSYS, 4, 4), + MUX(none, "mout_mmc2", group1_p4x12, SRC_FSYS, 8, 4), + MUX(none, "mout_mmc3", group1_p4x12, SRC_FSYS, 12, 4), + MUX(none, "mout_mmc4", group1_p4x12, SRC_FSYS, 16, 4), + MUX(none, "mout_uart0", group1_p4x12, SRC_PERIL0, 0, 4), + MUX(none, "mout_uart1", group1_p4x12, SRC_PERIL0, 4, 4), + MUX(none, "mout_uart2", group1_p4x12, SRC_PERIL0, 8, 4), + MUX(none, "mout_uart3", group1_p4x12, SRC_PERIL0, 12, 4), + MUX(none, "mout_uart4", group1_p4x12, SRC_PERIL0, 16, 4), + MUX(none, "mout_audio1", mout_audio1_p4x12, SRC_PERIL1, 0, 4), + MUX(none, "mout_audio2", mout_audio2_p4x12, SRC_PERIL1, 4, 4), + MUX(none, "mout_spi0", group1_p4x12, SRC_PERIL1, 16, 4), + MUX(none, "mout_spi1", group1_p4x12, SRC_PERIL1, 20, 4), + MUX(none, "mout_spi2", group1_p4x12, SRC_PERIL1, 24, 4), }; /* list of divider clocks supported in all exynos4 soc's */ -- cgit v1.1 From 4c3cc72cc7760f2aa3411e1e0f1a6cfca2659653 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:32:43 +0900 Subject: clk: exynos4: Add missing mout_mipihsi clock This patch adds missing output of mux MIPIHSI which is needed for div_mipihsi clock. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 8edd64c..42c098d 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -381,6 +381,7 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { MUX(none, "mout_mmc2", group1_p4x12, SRC_FSYS, 8, 4), MUX(none, "mout_mmc3", group1_p4x12, SRC_FSYS, 12, 4), MUX(none, "mout_mmc4", group1_p4x12, SRC_FSYS, 16, 4), + MUX(none, "mout_mipihsi", aclk_p4412, SRC_FSYS, 24, 1), MUX(none, "mout_uart0", group1_p4x12, SRC_PERIL0, 0, 4), MUX(none, "mout_uart1", group1_p4x12, SRC_PERIL0, 4, 4), MUX(none, "mout_uart2", group1_p4x12, SRC_PERIL0, 8, 4), -- cgit v1.1 From 69aff2fd1d01f10268bb972d4d52931c766c44d8 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:32:47 +0900 Subject: clk: exynos4: Add missing sclk_audio0 clock This clock is a parent of mout_spdif and sclk_pcm0. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 42c098d..0e89d97 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -508,6 +508,8 @@ struct samsung_gate_clock exynos4_gate_clks[] __initdata = { CLK_SET_RATE_PARENT, 0), GATE(sclk_mipi0, "sclk_mipi0", "div_mipi_pre0", SRC_MASK_LCD0, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_audio0, "sclk_audio0", "div_audio0", SRC_MASK_MAUDIO, 0, + CLK_SET_RATE_PARENT, 0), GATE(sclk_audio1, "sclk_audio1", "div_audio1", 0xc354, 0, CLK_SET_RATE_PARENT, 0), GATE_D(vp, "s5p-mixer", "vp", "aclk160", GATE_IP_TV, 0, 0, 0), -- cgit v1.1 From 6976d27415a10d19a718a2e4f862f5c5cc25fd19 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:32:51 +0900 Subject: clk: exynos4: Export sclk_pcm0 This clock is used by PCM interface 0. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 0e89d97..96f2e74 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -122,7 +122,7 @@ enum exynos4_clks { sclk_sata, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_uart4, sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2, sclk_slimbus, sclk_fimd1, sclk_mipi1, sclk_pcm1, sclk_pcm2, sclk_i2s1, - sclk_i2s2, sclk_mipihsi, sclk_mfc, + sclk_i2s2, sclk_mipihsi, sclk_mfc, sclk_pcm0, /* gate clocks */ fimc0 = 256, fimc1, fimc2, fimc3, csis0, csis1, jpeg, smmu_fimc0, @@ -411,7 +411,7 @@ struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(none, "div_fimd0", "mout_fimd0", DIV_LCD0, 0, 4), DIV(none, "div_mipi0", "mout_mipi0", DIV_LCD0, 16, 4), DIV(none, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4), - DIV(none, "div_pcm0", "sclk_audio0", DIV_MAUDIO, 4, 8), + DIV(sclk_pcm0, "sclk_pcm0", "sclk_audio0", DIV_MAUDIO, 4, 8), DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4), DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4), DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4), -- cgit v1.1 From 7bc1d2da0aa80ff387507e3bcb68bd72abec8a92 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:32:55 +0900 Subject: clk: exynos4: Move dac and mixer to Exynos4210-specific clocks The sclk_dac and sclk_mixer clocks are not present on Exynos4x12. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 96f2e74..c1e6451 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -483,8 +483,6 @@ struct samsung_gate_clock exynos4_gate_clks[] __initdata = { * of the clocks can be removed. */ GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi", SRC_MASK_TV, 0, 0, 0), - GATE(sclk_mixer, "sclk_mixer", "mout_mixer", SRC_MASK_TV, 4, 0, 0), - GATE(sclk_dac, "sclk_dac", "mout_dac", SRC_MASK_TV, 8, 0, 0), GATE(sclk_spdif, "sclk_spdif", "mout_spdif", 0xc354, 8, 0, 0), GATE(jpeg, "jpeg", "aclk160", GATE_IP_CAM, 6, 0, 0), GATE(mie0, "mie0", "aclk160", GATE_IP_LCD0, 1, 0, 0), @@ -681,6 +679,8 @@ struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { SRC_MASK_LCD1, 12, CLK_SET_RATE_PARENT, 0), GATE(sclk_sata, "sclk_sata", "div_sata", SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mixer, "sclk_mixer", "mout_mixer", SRC_MASK_TV, 4, 0, 0), + GATE(sclk_dac, "sclk_dac", "mout_dac", SRC_MASK_TV, 8, 0, 0), GATE_A(tsadc, "tsadc", "aclk100", GATE_IP_PERIL, 15, 0, 0, "adc"), GATE_A(mct, "mct", "aclk100", GATE_IP_PERIR, 13, 0, 0, "mct"), GATE_A(wdt, "watchdog", "aclk100", GATE_IP_PERIR, 14, 0, 0, "watchdog"), -- cgit v1.1 From e77ba804c103db5380d182aaa83af4566699fca1 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 4 Apr 2013 13:32:59 +0900 Subject: clk: exynos4: Export clocks used by exynos cpufreq drivers This patch exports clocks used by Exynos cpufreq drivers to allow lookup using device tree. (Support to cpufreq drivers will be added in further patches.) Signed-off-by: Lukasz Majewski Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index c1e6451..5592a78 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -112,7 +112,8 @@ enum exynos4_clks { /* core clocks */ xxti, xusbxti, fin_pll, fout_apll, fout_mpll, fout_epll, fout_vpll, sclk_apll, sclk_mpll, sclk_epll, sclk_vpll, arm_clk, aclk200, aclk100, - aclk160, aclk133, mout_mpll_user_t, mout_mpll_user_c, /* 18 */ + aclk160, aclk133, mout_mpll_user_t, mout_mpll_user_c, mout_core, + mout_apll, /* 20 */ /* gate for special clocks (sclk) */ sclk_fimc0 = 128, sclk_fimc1, sclk_fimc2, sclk_fimc3, sclk_cam0, @@ -284,7 +285,8 @@ struct samsung_fixed_rate_clock exynos4210_fixed_rate_clks[] __initdata = { /* list of mux clocks supported in all exynos4 soc's */ struct samsung_mux_clock exynos4_mux_clks[] __initdata = { - MUX(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), + MUX_F(mout_apll, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, + CLK_SET_RATE_PARENT, 0), MUX(none, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), MUX(none, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), MUX(none, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), @@ -362,7 +364,7 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { E4X12_SRC_DMC, 12, 1, "sclk_mpll"), MUX_A(sclk_vpll, "sclk_vpll", mout_vpll_p, SRC_TOP0, 8, 1, "sclk_vpll"), - MUX(none, "mout_core", mout_core_p4x12, SRC_CPU, 16, 1), + MUX(mout_core, "mout_core", mout_core_p4x12, SRC_CPU, 16, 1), MUX(none, "mout_fimc0", group1_p4x12, SRC_CAM, 0, 4), MUX(none, "mout_fimc1", group1_p4x12, SRC_CAM, 4, 4), MUX(none, "mout_fimc2", group1_p4x12, SRC_CAM, 8, 4), -- cgit v1.1 From a8b5a39ecbbe5b00554b5025af4464abe12bfcd8 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:33:02 +0900 Subject: clk: samsung: Remove unimplemented ops for pll Unimplemented clock operations should be simply omitted instead of returning error values. This patch removes unimplemented PLL operations to fix problems caused by returning error code in round_rate callback. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-pll.c | 80 ------------------------------------------- 1 file changed, 80 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 4b24511..89135f6 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -49,24 +49,8 @@ static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, return (unsigned long)fvco; } -/* todo: implement pl35xx clock round rate operation */ -static long samsung_pll35xx_round_rate(struct clk_hw *hw, - unsigned long drate, unsigned long *prate) -{ - return -ENOTSUPP; -} - -/* todo: implement pl35xx clock set rate */ -static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, - unsigned long prate) -{ - return -ENOTSUPP; -} - static const struct clk_ops samsung_pll35xx_clk_ops = { .recalc_rate = samsung_pll35xx_recalc_rate, - .round_rate = samsung_pll35xx_round_rate, - .set_rate = samsung_pll35xx_set_rate, }; struct clk * __init samsung_clk_register_pll35xx(const char *name, @@ -144,24 +128,8 @@ static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw, return (unsigned long)fvco; } -/* todo: implement pl36xx clock round rate operation */ -static long samsung_pll36xx_round_rate(struct clk_hw *hw, - unsigned long drate, unsigned long *prate) -{ - return -ENOTSUPP; -} - -/* todo: implement pl36xx clock set rate */ -static int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate, - unsigned long prate) -{ - return -ENOTSUPP; -} - static const struct clk_ops samsung_pll36xx_clk_ops = { .recalc_rate = samsung_pll36xx_recalc_rate, - .round_rate = samsung_pll36xx_round_rate, - .set_rate = samsung_pll36xx_set_rate, }; struct clk * __init samsung_clk_register_pll36xx(const char *name, @@ -239,24 +207,8 @@ static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, return (unsigned long)fvco; } -/* todo: implement pl45xx clock round rate operation */ -static long samsung_pll45xx_round_rate(struct clk_hw *hw, - unsigned long drate, unsigned long *prate) -{ - return -ENOTSUPP; -} - -/* todo: implement pl45xx clock set rate */ -static int samsung_pll45xx_set_rate(struct clk_hw *hw, unsigned long drate, - unsigned long prate) -{ - return -ENOTSUPP; -} - static const struct clk_ops samsung_pll45xx_clk_ops = { .recalc_rate = samsung_pll45xx_recalc_rate, - .round_rate = samsung_pll45xx_round_rate, - .set_rate = samsung_pll45xx_set_rate, }; struct clk * __init samsung_clk_register_pll45xx(const char *name, @@ -342,24 +294,8 @@ static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw, return (unsigned long)fvco; } -/* todo: implement pl46xx clock round rate operation */ -static long samsung_pll46xx_round_rate(struct clk_hw *hw, - unsigned long drate, unsigned long *prate) -{ - return -ENOTSUPP; -} - -/* todo: implement pl46xx clock set rate */ -static int samsung_pll46xx_set_rate(struct clk_hw *hw, unsigned long drate, - unsigned long prate) -{ - return -ENOTSUPP; -} - static const struct clk_ops samsung_pll46xx_clk_ops = { .recalc_rate = samsung_pll46xx_recalc_rate, - .round_rate = samsung_pll46xx_round_rate, - .set_rate = samsung_pll46xx_set_rate, }; struct clk * __init samsung_clk_register_pll46xx(const char *name, @@ -441,24 +377,8 @@ static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw, return (unsigned long)fvco; } -/* todo: implement pl2550x clock round rate operation */ -static long samsung_pll2550x_round_rate(struct clk_hw *hw, - unsigned long drate, unsigned long *prate) -{ - return -ENOTSUPP; -} - -/* todo: implement pl2550x clock set rate */ -static int samsung_pll2550x_set_rate(struct clk_hw *hw, unsigned long drate, - unsigned long prate) -{ - return -ENOTSUPP; -} - static const struct clk_ops samsung_pll2550x_clk_ops = { .recalc_rate = samsung_pll2550x_recalc_rate, - .round_rate = samsung_pll2550x_round_rate, - .set_rate = samsung_pll2550x_set_rate, }; struct clk * __init samsung_clk_register_pll2550x(const char *name, -- cgit v1.1 From fba79e32a7dfe467a0803a372afa20ccb51ba294 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:33:08 +0900 Subject: clk: exynos4: Export mout_core clock of Exynos4210 This patch enables clock lookup registration for mout_core clock used in Exynos4210 cpufreq driver. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 5592a78..f81888d 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -311,7 +311,8 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { MUX(none, "mout_fimd1", group1_p4210, SRC_LCD1, 0, 4), MUX(none, "mout_mipi1", group1_p4210, SRC_LCD1, 12, 4), MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1, "sclk_mpll"), - MUX(none, "mout_core", mout_core_p4210, SRC_CPU, 16, 1), + MUX_A(mout_core, "mout_core", mout_core_p4210, + SRC_CPU, 16, 1, "mout_core"), MUX_A(sclk_vpll, "sclk_vpll", sclk_vpll_p4210, SRC_TOP0, 8, 1, "sclk_vpll"), MUX(none, "mout_fimc0", group1_p4210, SRC_CAM, 0, 4), -- cgit v1.1 From 1e25810bbbc927348070da3f47a591315f3aa926 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 4 Apr 2013 13:33:12 +0900 Subject: clk: exynos4: Add camera related clock definitions This patch adds several gate and mux clocks related to camera and ISP blocks. Signed-off-by: Sylwester Nawrocki Signed-off-by: Andrzej Hajda Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 50 ++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index f81888d..6d7fa82 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -87,6 +87,7 @@ #define E4210_MPLL_CON0 0x14108 #define SRC_CPU 0x14200 #define DIV_CPU0 0x14500 +#define E4X12_GATE_ISP0 0x18800 /* the exynos4 soc type */ enum exynos4_soc { @@ -136,7 +137,12 @@ enum exynos4_clks { uart4, i2c0, i2c1, i2c2, i2c3, i2c4, i2c5, i2c6, i2c7, i2c_hdmi, tsadc, spi0, spi1, spi2, i2s1, i2s2, pcm0, i2s0, pcm1, pcm2, pwm, slimbus, spdif, ac97, modemif, chipid, sysreg, hdmi_cec, mct, wdt, rtc, keyif, - audss, mipi_hsi, mdma2, + audss, mipi_hsi, mdma2, pixelasyncm0, pixelasyncm1, fimc_lite0, + fimc_lite1, ppmuispx, ppmuispmx, + + /* mux clocks */ + mout_fimc0 = 384, mout_fimc1, mout_fimc2, mout_fimc3, mout_cam0, + mout_cam1, mout_csis0, mout_csis1, nr_clks, }; @@ -315,14 +321,14 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { SRC_CPU, 16, 1, "mout_core"), MUX_A(sclk_vpll, "sclk_vpll", sclk_vpll_p4210, SRC_TOP0, 8, 1, "sclk_vpll"), - MUX(none, "mout_fimc0", group1_p4210, SRC_CAM, 0, 4), - MUX(none, "mout_fimc1", group1_p4210, SRC_CAM, 4, 4), - MUX(none, "mout_fimc2", group1_p4210, SRC_CAM, 8, 4), - MUX(none, "mout_fimc3", group1_p4210, SRC_CAM, 12, 4), - MUX(none, "mout_cam0", group1_p4210, SRC_CAM, 16, 4), - MUX(none, "mout_cam1", group1_p4210, SRC_CAM, 20, 4), - MUX(none, "mout_csis0", group1_p4210, SRC_CAM, 24, 4), - MUX(none, "mout_csis1", group1_p4210, SRC_CAM, 28, 4), + MUX(mout_fimc0, "mout_fimc0", group1_p4210, SRC_CAM, 0, 4), + MUX(mout_fimc1, "mout_fimc1", group1_p4210, SRC_CAM, 4, 4), + MUX(mout_fimc2, "mout_fimc2", group1_p4210, SRC_CAM, 8, 4), + MUX(mout_fimc3, "mout_fimc3", group1_p4210, SRC_CAM, 12, 4), + MUX(mout_cam0, "mout_cam0", group1_p4210, SRC_CAM, 16, 4), + MUX(mout_cam1, "mout_cam1", group1_p4210, SRC_CAM, 20, 4), + MUX(mout_csis0, "mout_csis0", group1_p4210, SRC_CAM, 24, 4), + MUX(mout_csis1, "mout_csis1", group1_p4210, SRC_CAM, 28, 4), MUX(none, "mout_mfc0", sclk_ampll_p4210, SRC_MFC, 0, 1), MUX(none, "mout_g3d0", sclk_ampll_p4210, SRC_G3D, 0, 1), MUX(none, "mout_fimd0", group1_p4210, SRC_LCD0, 0, 4), @@ -366,14 +372,14 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { MUX_A(sclk_vpll, "sclk_vpll", mout_vpll_p, SRC_TOP0, 8, 1, "sclk_vpll"), MUX(mout_core, "mout_core", mout_core_p4x12, SRC_CPU, 16, 1), - MUX(none, "mout_fimc0", group1_p4x12, SRC_CAM, 0, 4), - MUX(none, "mout_fimc1", group1_p4x12, SRC_CAM, 4, 4), - MUX(none, "mout_fimc2", group1_p4x12, SRC_CAM, 8, 4), - MUX(none, "mout_fimc3", group1_p4x12, SRC_CAM, 12, 4), - MUX(none, "mout_cam0", group1_p4x12, SRC_CAM, 16, 4), - MUX(none, "mout_cam1", group1_p4x12, SRC_CAM, 20, 4), - MUX(none, "mout_csis0", group1_p4x12, SRC_CAM, 24, 4), - MUX(none, "mout_csis1", group1_p4x12, SRC_CAM, 28, 4), + MUX(mout_fimc0, "mout_fimc0", group1_p4x12, SRC_CAM, 0, 4), + MUX(mout_fimc1, "mout_fimc1", group1_p4x12, SRC_CAM, 4, 4), + MUX(mout_fimc2, "mout_fimc2", group1_p4x12, SRC_CAM, 8, 4), + MUX(mout_fimc3, "mout_fimc3", group1_p4x12, SRC_CAM, 12, 4), + MUX(mout_cam0, "mout_cam0", group1_p4x12, SRC_CAM, 16, 4), + MUX(mout_cam1, "mout_cam1", group1_p4x12, SRC_CAM, 20, 4), + MUX(mout_csis0, "mout_csis0", group1_p4x12, SRC_CAM, 24, 4), + MUX(mout_csis1, "mout_csis1", group1_p4x12, SRC_CAM, 28, 4), MUX(none, "mout_mfc0", sclk_ampll_p4x12, SRC_MFC, 0, 1), MUX(none, "mout_g3d0", sclk_ampll_p4x12, SRC_G3D, 0, 1), MUX(none, "mout_fimd0", group1_p4x12, SRC_LCD0, 0, 4), @@ -588,6 +594,8 @@ struct samsung_gate_clock exynos4_gate_clks[] __initdata = { GATE_IP_CAM, 10, 0, 0, "sysmmu"), GATE_DA(smmu_jpeg, "exynos-sysmmu.3", "smmu_jpeg", "aclk160", GATE_IP_CAM, 11, 0, 0, "sysmmu"), + GATE(pixelasyncm0, "pxl_async0", "aclk160", GATE_IP_CAM, 17, 0, 0), + GATE(pixelasyncm1, "pxl_async1", "aclk160", GATE_IP_CAM, 18, 0, 0), GATE_DA(smmu_tv, "exynos-sysmmu.2", "smmu_tv", "aclk160", GATE_IP_TV, 4, 0, 0, "sysmmu"), GATE_DA(mfc, "s5p-mfc", "mfc", "aclk100", GATE_IP_MFC, 0, 0, 0, "mfc"), @@ -722,6 +730,14 @@ struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { E4X12_GATE_IP_MAUDIO, 2, 0, 0, "pcm"), GATE_DA(i2s0, "samsung-i2s.0", "i2s0", "aclk100", E4X12_GATE_IP_MAUDIO, 3, 0, 0, "iis"), + GATE(fimc_lite0, "lite0", "aclk200", E4X12_GATE_ISP0, 3, + CLK_IGNORE_UNUSED, 0), + GATE(fimc_lite1, "lite1", "aclk200", E4X12_GATE_ISP0, 4, + CLK_IGNORE_UNUSED, 0), + GATE(ppmuispmx, "ppmuispmx", "aclk200", E4X12_GATE_ISP0, 20, + CLK_IGNORE_UNUSED, 0), + GATE(ppmuispx, "ppmuispx", "aclk200", E4X12_GATE_ISP0, 21, + CLK_IGNORE_UNUSED, 0), }; #ifdef CONFIG_OF -- cgit v1.1 From 8e1ce8393eb7c27a8aa38da3d245187ec808ba88 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:33:17 +0900 Subject: clk: exynos4: Add G3D clocks This patch adds clocks needed for G3D block present on Exynos 4 SoCs. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 6d7fa82..ddd654b 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -124,7 +124,7 @@ enum exynos4_clks { sclk_sata, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_uart4, sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2, sclk_slimbus, sclk_fimd1, sclk_mipi1, sclk_pcm1, sclk_pcm2, sclk_i2s1, - sclk_i2s2, sclk_mipihsi, sclk_mfc, sclk_pcm0, + sclk_i2s2, sclk_mipihsi, sclk_mfc, sclk_pcm0, sclk_g3d, /* gate clocks */ fimc0 = 256, fimc1, fimc2, fimc3, csis0, csis1, jpeg, smmu_fimc0, @@ -142,7 +142,7 @@ enum exynos4_clks { /* mux clocks */ mout_fimc0 = 384, mout_fimc1, mout_fimc2, mout_fimc3, mout_cam0, - mout_cam1, mout_csis0, mout_csis1, + mout_cam1, mout_csis0, mout_csis1, mout_g3d0, mout_g3d1, mout_g3d, nr_clks, }; @@ -296,8 +296,10 @@ struct samsung_mux_clock exynos4_mux_clks[] __initdata = { MUX(none, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), MUX(none, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), MUX(none, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), - MUX(none, "mout_g3d1", sclk_evpll_p, SRC_G3D, 4, 1), - MUX(none, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1), + MUX_F(mout_g3d1, "mout_g3d1", sclk_evpll_p, SRC_G3D, 4, 1, + CLK_SET_RATE_PARENT, 0), + MUX_F(mout_g3d, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1, + CLK_SET_RATE_PARENT, 0), MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIL1, 8, 2), MUX_A(sclk_epll, "sclk_epll", mout_epll_p, SRC_TOP0, 4, 1, "sclk_epll"), }; @@ -330,7 +332,8 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { MUX(mout_csis0, "mout_csis0", group1_p4210, SRC_CAM, 24, 4), MUX(mout_csis1, "mout_csis1", group1_p4210, SRC_CAM, 28, 4), MUX(none, "mout_mfc0", sclk_ampll_p4210, SRC_MFC, 0, 1), - MUX(none, "mout_g3d0", sclk_ampll_p4210, SRC_G3D, 0, 1), + MUX_F(mout_g3d0, "mout_g3d0", sclk_ampll_p4210, SRC_G3D, 0, 1, + CLK_SET_RATE_PARENT, 0), MUX(none, "mout_fimd0", group1_p4210, SRC_LCD0, 0, 4), MUX(none, "mout_mipi0", group1_p4210, SRC_LCD0, 12, 4), MUX(none, "mout_audio0", mout_audio0_p4210, SRC_MAUDIO, 0, 4), @@ -381,7 +384,8 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { MUX(mout_csis0, "mout_csis0", group1_p4x12, SRC_CAM, 24, 4), MUX(mout_csis1, "mout_csis1", group1_p4x12, SRC_CAM, 28, 4), MUX(none, "mout_mfc0", sclk_ampll_p4x12, SRC_MFC, 0, 1), - MUX(none, "mout_g3d0", sclk_ampll_p4x12, SRC_G3D, 0, 1), + MUX_F(mout_g3d0, "mout_g3d0", sclk_ampll_p4x12, SRC_G3D, 0, 1, + CLK_SET_RATE_PARENT, 0), MUX(none, "mout_fimd0", group1_p4x12, SRC_LCD0, 0, 4), MUX(none, "mout_mipi0", group1_p4x12, SRC_LCD0, 12, 4), MUX(none, "mout_audio0", mout_audio0_p4x12, SRC_MAUDIO, 0, 4), @@ -416,7 +420,8 @@ struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(none, "div_csis0", "mout_csis0", DIV_CAM, 24, 4), DIV(none, "div_csis1", "mout_csis1", DIV_CAM, 28, 4), DIV(sclk_mfc, "sclk_mfc", "mout_mfc", DIV_MFC, 0, 4), - DIV(none, "div_g3d", "mout_g3d", DIV_G3D, 0, 4), + DIV_F(none, "div_g3d", "mout_g3d", DIV_G3D, 0, 4, + CLK_SET_RATE_PARENT, 0), DIV(none, "div_fimd0", "mout_fimd0", DIV_LCD0, 0, 4), DIV(none, "div_mipi0", "mout_mipi0", DIV_LCD0, 16, 4), DIV(none, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4), @@ -502,7 +507,8 @@ struct samsung_gate_clock exynos4_gate_clks[] __initdata = { GATE(smmu_fimd1, "smmu_fimd1", "aclk160", GATE_IP_LCD1, 4, 0, 0), GATE(tsi, "tsi", "aclk133", GATE_IP_FSYS, 4, 0, 0), GATE(sromc, "sromc", "aclk133", GATE_IP_FSYS, 11, 0, 0), - GATE(g3d, "g3d", "aclk200", GATE_IP_G3D, 0, 0, 0), + GATE(sclk_g3d, "sclk_g3d", "div_g3d", GATE_IP_G3D, 0, + CLK_SET_RATE_PARENT, 0), GATE(usb_device, "usb_device", "aclk133", GATE_IP_FSYS, 13, 0, 0), GATE(onenand, "onenand", "aclk133", GATE_IP_FSYS, 15, 0, 0), GATE(nfcon, "nfcon", "aclk133", GATE_IP_FSYS, 16, 0, 0), -- cgit v1.1 From 1554701528479c81240076b0c9251f4544be6319 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 4 Apr 2013 13:33:22 +0900 Subject: clk: exynos4: Add missing CMU_TOP and ISP clocks The patch adds missing clocks to TOP and ISP clock domains. It also adds clock gates for ISP sub-blocks. Signed-off-by: Andrzej Hajda Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 110 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index ddd654b..7e875a4 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -34,6 +34,7 @@ #define E4210_SRC_IMAGE 0xc230 #define SRC_LCD0 0xc234 #define SRC_LCD1 0xc238 +#define E4X12_SRC_ISP 0xc238 #define SRC_MAUDIO 0xc23c #define SRC_FSYS 0xc240 #define SRC_PERIL0 0xc250 @@ -43,6 +44,7 @@ #define SRC_MASK_TV 0xc324 #define SRC_MASK_LCD0 0xc334 #define SRC_MASK_LCD1 0xc338 +#define E4X12_SRC_MASK_ISP 0xc338 #define SRC_MASK_MAUDIO 0xc33c #define SRC_MASK_FSYS 0xc340 #define SRC_MASK_PERIL0 0xc350 @@ -76,6 +78,7 @@ #define E4210_GATE_IP_IMAGE 0xc930 #define GATE_IP_LCD0 0xc934 #define GATE_IP_LCD1 0xc938 +#define E4X12_GATE_IP_ISP 0xc938 #define E4X12_GATE_IP_MAUDIO 0xc93c #define GATE_IP_FSYS 0xc940 #define GATE_IP_GPS 0xc94c @@ -87,7 +90,10 @@ #define E4210_MPLL_CON0 0x14108 #define SRC_CPU 0x14200 #define DIV_CPU0 0x14500 +#define E4X12_DIV_ISP0 0x18300 +#define E4X12_DIV_ISP1 0x18304 #define E4X12_GATE_ISP0 0x18800 +#define E4X12_GATE_ISP1 0x18804 /* the exynos4 soc type */ enum exynos4_soc { @@ -124,7 +130,8 @@ enum exynos4_clks { sclk_sata, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_uart4, sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2, sclk_slimbus, sclk_fimd1, sclk_mipi1, sclk_pcm1, sclk_pcm2, sclk_i2s1, - sclk_i2s2, sclk_mipihsi, sclk_mfc, sclk_pcm0, sclk_g3d, + sclk_i2s2, sclk_mipihsi, sclk_mfc, sclk_pcm0, sclk_g3d, sclk_pwm_isp, + sclk_spi0_isp, sclk_spi1_isp, sclk_uart_isp, /* gate clocks */ fimc0 = 256, fimc1, fimc2, fimc3, csis0, csis1, jpeg, smmu_fimc0, @@ -138,7 +145,11 @@ enum exynos4_clks { spi0, spi1, spi2, i2s1, i2s2, pcm0, i2s0, pcm1, pcm2, pwm, slimbus, spdif, ac97, modemif, chipid, sysreg, hdmi_cec, mct, wdt, rtc, keyif, audss, mipi_hsi, mdma2, pixelasyncm0, pixelasyncm1, fimc_lite0, - fimc_lite1, ppmuispx, ppmuispmx, + fimc_lite1, ppmuispx, ppmuispmx, fimc_isp, fimc_drc, fimc_fd, mcuisp, + gicisp, smmu_isp, smmu_drc, smmu_fd, smmu_lite0, smmu_lite1, mcuctl_isp, + mpwm_isp, i2c0_isp, i2c1_isp, mtcadc_isp, pwm_isp, wdt_isp, uart_isp, + asyncaxim, smmu_ispcx, spi0_isp, spi1_isp, pwm_isp_sclk, spi0_isp_sclk, + spi1_isp_sclk, uart_isp_sclk, /* mux clocks */ mout_fimc0 = 384, mout_fimc1, mout_fimc2, mout_fimc3, mout_cam0, @@ -234,6 +245,8 @@ PNAME(mout_hdmi_p) = { "sclk_pixel", "sclk_hdmiphy", }; PNAME(mout_jpeg_p) = { "mout_jpeg0", "mout_jpeg1", }; PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2", "spdif_extclk", }; +PNAME(mout_onenand_p) = {"aclk133", "aclk160", }; +PNAME(mout_onenand1_p) = {"mout_onenand", "sclk_vpll", }; /* Exynos 4210-specific parent groups */ PNAME(sclk_vpll_p4210) = { "mout_vpllsrc", "fout_vpll", }; @@ -271,6 +284,9 @@ PNAME(mout_audio2_p4x12) = { "cdclk2", "none", "sclk_hdmi24m", "sclk_usbphy0", "xxti", "xusbxti", "mout_mpll_user_t", "sclk_epll", "sclk_vpll", }; PNAME(aclk_p4412) = { "mout_mpll_user_t", "sclk_apll", }; +PNAME(mout_user_aclk400_mcuisp_p4x12) = {"fin_pll", "div_aclk400_mcuisp", }; +PNAME(mout_user_aclk200_p4x12) = {"fin_pll", "div_aclk200", }; +PNAME(mout_user_aclk266_gps_p4x12) = {"fin_pll", "div_aclk266_gps", }; /* fixed rate clocks generated outside the soc */ struct samsung_fixed_rate_clock exynos4_fixed_rate_ext_clks[] __initdata = { @@ -301,7 +317,9 @@ struct samsung_mux_clock exynos4_mux_clks[] __initdata = { MUX_F(mout_g3d, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1, CLK_SET_RATE_PARENT, 0), MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIL1, 8, 2), + MUX(none, "mout_onenand1", mout_onenand1_p, SRC_TOP0, 0, 1), MUX_A(sclk_epll, "sclk_epll", mout_epll_p, SRC_TOP0, 4, 1, "sclk_epll"), + MUX(none, "mout_onenand", mout_onenand_p, SRC_TOP0, 28, 1), }; /* list of mux clocks supported in exynos4210 soc */ @@ -358,8 +376,15 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { MUX(mout_mpll_user_c, "mout_mpll_user_c", mout_mpll_user_p4x12, SRC_CPU, 24, 1), + MUX(none, "mout_aclk266_gps", aclk_p4412, SRC_TOP1, 4, 1), + MUX(none, "mout_aclk400_mcuisp", aclk_p4412, SRC_TOP1, 8, 1), MUX(mout_mpll_user_t, "mout_mpll_user_t", mout_mpll_user_p4x12, SRC_TOP1, 12, 1), + MUX(none, "mout_user_aclk266_gps", mout_user_aclk266_gps_p4x12, + SRC_TOP1, 16, 1), + MUX(aclk200, "aclk200", mout_user_aclk200_p4x12, SRC_TOP1, 20, 1), + MUX(none, "aclk400_mcuisp", mout_user_aclk400_mcuisp_p4x12, + SRC_TOP1, 24, 1), MUX(none, "mout_aclk200", aclk_p4412, SRC_TOP0, 12, 1), MUX(none, "mout_aclk100", aclk_p4412, SRC_TOP0, 16, 1), MUX(none, "mout_aclk160", aclk_p4412, SRC_TOP0, 20, 1), @@ -405,6 +430,10 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { MUX(none, "mout_spi0", group1_p4x12, SRC_PERIL1, 16, 4), MUX(none, "mout_spi1", group1_p4x12, SRC_PERIL1, 20, 4), MUX(none, "mout_spi2", group1_p4x12, SRC_PERIL1, 24, 4), + MUX(none, "mout_pwm_isp", group1_p4x12, E4X12_SRC_ISP, 0, 4), + MUX(none, "mout_spi0_isp", group1_p4x12, E4X12_SRC_ISP, 4, 4), + MUX(none, "mout_spi1_isp", group1_p4x12, E4X12_SRC_ISP, 8, 4), + MUX(none, "mout_uart_isp", group1_p4x12, E4X12_SRC_ISP, 12, 4), }; /* list of divider clocks supported in all exynos4 soc's */ @@ -431,10 +460,10 @@ struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4), DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4), DIV(sclk_pixel, "sclk_pixel", "sclk_vpll", DIV_TV, 0, 4), - DIV(aclk200, "aclk200", "mout_aclk200", DIV_TOP, 0, 3), DIV(aclk100, "aclk100", "mout_aclk100", DIV_TOP, 4, 4), DIV(aclk160, "aclk160", "mout_aclk160", DIV_TOP, 8, 3), DIV(aclk133, "aclk133", "mout_aclk133", DIV_TOP, 12, 3), + DIV(none, "div_onenand", "mout_onenand1", DIV_TOP, 16, 3), DIV(sclk_slimbus, "sclk_slimbus", "sclk_epll", DIV_PERIL3, 4, 4), DIV(sclk_pcm1, "sclk_pcm1", "sclk_audio1", DIV_PERIL4, 4, 8), DIV(sclk_pcm2, "sclk_pcm2", "sclk_audio2", DIV_PERIL4, 20, 8), @@ -472,6 +501,7 @@ struct samsung_div_clock exynos4_div_clks[] __initdata = { /* list of divider clocks supported in exynos4210 soc */ struct samsung_div_clock exynos4210_div_clks[] __initdata = { + DIV(aclk200, "aclk200", "mout_aclk200", DIV_TOP, 0, 3), DIV(none, "div_g2d", "mout_g2d", DIV_IMAGE, 0, 4), DIV(none, "div_fimd1", "mout_fimd1", E4210_DIV_LCD1, 0, 4), DIV(none, "div_mipi1", "mout_mipi1", E4210_DIV_LCD1, 16, 4), @@ -487,6 +517,20 @@ struct samsung_div_clock exynos4x12_div_clks[] __initdata = { DIV(none, "div_mdnie_pwm_pre0", "div_mdnie_pwm0", DIV_LCD0, 12, 4), DIV(none, "div_mipihsi", "mout_mipihsi", DIV_FSYS0, 20, 4), DIV(none, "div_jpeg", "mout_jpeg", E4X12_DIV_CAM1, 0, 4), + DIV(none, "div_aclk200", "mout_aclk200", DIV_TOP, 0, 3), + DIV(none, "div_aclk266_gps", "mout_aclk266_gps", DIV_TOP, 20, 3), + DIV(none, "div_aclk400_mcuisp", "mout_aclk400_mcuisp", DIV_TOP, 24, 3), + DIV(none, "div_pwm_isp", "mout_pwm_isp", E4X12_DIV_ISP, 0, 4), + DIV(none, "div_spi0_isp", "mout_spi0_isp", E4X12_DIV_ISP, 4, 4), + DIV(none, "div_spi0_isp_pre", "div_spi0_isp", E4X12_DIV_ISP, 8, 8), + DIV(none, "div_spi1_isp", "mout_spi1_isp", E4X12_DIV_ISP, 16, 4), + DIV(none, "div_spi1_isp_pre", "div_spi1_isp", E4X12_DIV_ISP, 20, 8), + DIV(none, "div_uart_isp", "mout_uart_isp", E4X12_DIV_ISP, 28, 4), + DIV(none, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3), + DIV(none, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3), + DIV(none, "div_mpwm", "div_isp1", E4X12_DIV_ISP1, 0, 3), + DIV(none, "div_mcuisp0", "aclk400_mcuisp", E4X12_DIV_ISP1, 4, 3), + DIV(none, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, 8, 3), }; /* list of gate clocks supported in all exynos4 soc's */ @@ -730,20 +774,80 @@ struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { GATE_A(rtc, "rtc", "aclk100", E4X12_GATE_IP_PERIR, 15, 0, 0, "rtc"), GATE_A(keyif, "keyif", "aclk100", E4X12_GATE_IP_PERIR, 16, 0, 0, "keypad"), + GATE(sclk_pwm_isp, "sclk_pwm_isp", "div_pwm_isp", + E4X12_SRC_MASK_ISP, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi0_isp, "sclk_spi0_isp", "div_spi0_isp_pre", + E4X12_SRC_MASK_ISP, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi1_isp, "sclk_spi1_isp", "div_spi1_isp_pre", + E4X12_SRC_MASK_ISP, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart_isp, "sclk_uart_isp", "div_uart_isp", + E4X12_SRC_MASK_ISP, 12, CLK_SET_RATE_PARENT, 0), + GATE(pwm_isp_sclk, "pwm_isp_sclk", "sclk_pwm_isp", + E4X12_GATE_IP_ISP, 0, 0, 0), + GATE(spi0_isp_sclk, "spi0_isp_sclk", "sclk_spi0_isp", + E4X12_GATE_IP_ISP, 1, 0, 0), + GATE(spi1_isp_sclk, "spi1_isp_sclk", "sclk_spi1_isp", + E4X12_GATE_IP_ISP, 2, 0, 0), + GATE(uart_isp_sclk, "uart_isp_sclk", "sclk_uart_isp", + E4X12_GATE_IP_ISP, 3, 0, 0), GATE_A(wdt, "watchdog", "aclk100", E4X12_GATE_IP_PERIR, 14, 0, 0, "watchdog"), GATE_DA(pcm0, "samsung-pcm.0", "pcm0", "aclk100", E4X12_GATE_IP_MAUDIO, 2, 0, 0, "pcm"), GATE_DA(i2s0, "samsung-i2s.0", "i2s0", "aclk100", E4X12_GATE_IP_MAUDIO, 3, 0, 0, "iis"), + GATE(fimc_isp, "isp", "aclk200", E4X12_GATE_ISP0, 0, + CLK_IGNORE_UNUSED, 0), + GATE(fimc_drc, "drc", "aclk200", E4X12_GATE_ISP0, 1, + CLK_IGNORE_UNUSED, 0), + GATE(fimc_fd, "fd", "aclk200", E4X12_GATE_ISP0, 2, + CLK_IGNORE_UNUSED, 0), GATE(fimc_lite0, "lite0", "aclk200", E4X12_GATE_ISP0, 3, CLK_IGNORE_UNUSED, 0), GATE(fimc_lite1, "lite1", "aclk200", E4X12_GATE_ISP0, 4, CLK_IGNORE_UNUSED, 0), + GATE(mcuisp, "mcuisp", "aclk200", E4X12_GATE_ISP0, 5, + CLK_IGNORE_UNUSED, 0), + GATE(gicisp, "gicisp", "aclk200", E4X12_GATE_ISP0, 7, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_isp, "smmu_isp", "aclk200", E4X12_GATE_ISP0, 8, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_drc, "smmu_drc", "aclk200", E4X12_GATE_ISP0, 9, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_fd, "smmu_fd", "aclk200", E4X12_GATE_ISP0, 10, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_lite0, "smmu_lite0", "aclk200", E4X12_GATE_ISP0, 11, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_lite1, "smmu_lite1", "aclk200", E4X12_GATE_ISP0, 12, + CLK_IGNORE_UNUSED, 0), GATE(ppmuispmx, "ppmuispmx", "aclk200", E4X12_GATE_ISP0, 20, CLK_IGNORE_UNUSED, 0), GATE(ppmuispx, "ppmuispx", "aclk200", E4X12_GATE_ISP0, 21, CLK_IGNORE_UNUSED, 0), + GATE(mcuctl_isp, "mcuctl_isp", "aclk200", E4X12_GATE_ISP0, 23, + CLK_IGNORE_UNUSED, 0), + GATE(mpwm_isp, "mpwm_isp", "aclk200", E4X12_GATE_ISP0, 24, + CLK_IGNORE_UNUSED, 0), + GATE(i2c0_isp, "i2c0_isp", "aclk200", E4X12_GATE_ISP0, 25, + CLK_IGNORE_UNUSED, 0), + GATE(i2c1_isp, "i2c1_isp", "aclk200", E4X12_GATE_ISP0, 26, + CLK_IGNORE_UNUSED, 0), + GATE(mtcadc_isp, "mtcadc_isp", "aclk200", E4X12_GATE_ISP0, 27, + CLK_IGNORE_UNUSED, 0), + GATE(pwm_isp, "pwm_isp", "aclk200", E4X12_GATE_ISP0, 28, + CLK_IGNORE_UNUSED, 0), + GATE(wdt_isp, "wdt_isp", "aclk200", E4X12_GATE_ISP0, 30, + CLK_IGNORE_UNUSED, 0), + GATE(uart_isp, "uart_isp", "aclk200", E4X12_GATE_ISP0, 31, + CLK_IGNORE_UNUSED, 0), + GATE(asyncaxim, "asyncaxim", "aclk200", E4X12_GATE_ISP1, 0, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_ispcx, "smmu_ispcx", "aclk200", E4X12_GATE_ISP1, 4, + CLK_IGNORE_UNUSED, 0), + GATE(spi0_isp, "spi0_isp", "aclk200", E4X12_GATE_ISP1, 12, + CLK_IGNORE_UNUSED, 0), + GATE(spi1_isp, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13, + CLK_IGNORE_UNUSED, 0), }; #ifdef CONFIG_OF -- cgit v1.1 From 8e79561c41ec7746361a1e9a079c7225e010515e Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:33:27 +0900 Subject: clk: exynos4: Add missing mout_sata on Exynos4210 This patch adds missing mout_sata that is a parent of div_sata clock. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 7e875a4..e572f62 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -360,6 +360,7 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { MUX(none, "mout_mmc2", group1_p4210, SRC_FSYS, 8, 4), MUX(none, "mout_mmc3", group1_p4210, SRC_FSYS, 12, 4), MUX(none, "mout_mmc4", group1_p4210, SRC_FSYS, 16, 4), + MUX(none, "mout_sata", sclk_ampll_p4210, SRC_FSYS, 24, 1), MUX(none, "mout_uart0", group1_p4210, SRC_PERIL0, 0, 4), MUX(none, "mout_uart1", group1_p4210, SRC_PERIL0, 4, 4), MUX(none, "mout_uart2", group1_p4210, SRC_PERIL0, 8, 4), -- cgit v1.1 From 6d7190f846e74be2cbaae4cd56d1a5385e46f6ff Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:33:30 +0900 Subject: clk: exynos4: Define {E,V}PLL registers This patch adds preprocessor definitions of EPLL and VPLL registers and replaces all occurences of offsets of related registers with new definitions. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index e572f62..57aa527 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -25,6 +25,14 @@ #define E4X12_GATE_IP_IMAGE 0x4930 #define GATE_IP_RIGHTBUS 0x8800 #define E4X12_GATE_IP_PERIR 0x8960 +#define EPLL_LOCK 0xc010 +#define VPLL_LOCK 0xc020 +#define EPLL_CON0 0xc110 +#define EPLL_CON1 0xc114 +#define EPLL_CON2 0xc118 +#define VPLL_CON0 0xc120 +#define VPLL_CON1 0xc124 +#define VPLL_CON2 0xc128 #define SRC_TOP0 0xc210 #define SRC_TOP1 0xc214 #define SRC_CAM 0xc220 @@ -969,18 +977,18 @@ void __init exynos4_clk_init(struct device_node *np) mpll = samsung_clk_register_pll45xx("fout_mpll", "fin_pll", reg_base + E4210_MPLL_CON0, pll_4508); epll = samsung_clk_register_pll46xx("fout_epll", "fin_pll", - reg_base + 0xc110, pll_4600); + reg_base + EPLL_CON0, pll_4600); vpll = samsung_clk_register_pll46xx("fout_vpll", "mout_vpllsrc", - reg_base + 0xc120, pll_4650c); + reg_base + VPLL_CON0, pll_4650c); } else { apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll", reg_base + APLL_CON0); mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll", reg_base + E4X12_MPLL_CON0); epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll", - reg_base + 0xc110); + reg_base + EPLL_CON0); vpll = samsung_clk_register_pll36xx("fout_vpll", "fin_pll", - reg_base + 0xc120); + reg_base + VPLL_CON0); } samsung_clk_add_lookup(apll, fout_apll); -- cgit v1.1 From 017ab64bdbca6f2f421d59a8235cdee90da08463 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:33:34 +0900 Subject: clk: exynos4: Use SRC_MASK_PERIL{0,1} definitions There are definitions of SRC_MASK_PERIL0 and SRC_MASK_PERIL1 registers, but they are not used for clock definitions. This patch modifies related clock definitions to use defined macros instead of numeric offsets. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 57aa527..5e26d5d 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -550,7 +550,7 @@ struct samsung_gate_clock exynos4_gate_clks[] __initdata = { * of the clocks can be removed. */ GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi", SRC_MASK_TV, 0, 0, 0), - GATE(sclk_spdif, "sclk_spdif", "mout_spdif", 0xc354, 8, 0, 0), + GATE(sclk_spdif, "sclk_spdif", "mout_spdif", SRC_MASK_PERIL1, 8, 0, 0), GATE(jpeg, "jpeg", "aclk160", GATE_IP_CAM, 6, 0, 0), GATE(mie0, "mie0", "aclk160", GATE_IP_LCD0, 1, 0, 0), GATE(dsim0, "dsim0", "aclk160", GATE_IP_LCD0, 3, 0, 0), @@ -576,7 +576,7 @@ struct samsung_gate_clock exynos4_gate_clks[] __initdata = { SRC_MASK_LCD0, 12, CLK_SET_RATE_PARENT, 0), GATE(sclk_audio0, "sclk_audio0", "div_audio0", SRC_MASK_MAUDIO, 0, CLK_SET_RATE_PARENT, 0), - GATE(sclk_audio1, "sclk_audio1", "div_audio1", 0xc354, 0, + GATE(sclk_audio1, "sclk_audio1", "div_audio1", SRC_MASK_PERIL1, 0, CLK_SET_RATE_PARENT, 0), GATE_D(vp, "s5p-mixer", "vp", "aclk160", GATE_IP_TV, 0, 0, 0), GATE_D(mixer, "s5p-mixer", "mixer", "aclk160", GATE_IP_TV, 1, 0, 0), @@ -614,23 +614,31 @@ struct samsung_gate_clock exynos4_gate_clks[] __initdata = { GATE_DA(sclk_mmc4, NULL, "sclk_mmc4", "div_mmc_pre4", SRC_MASK_FSYS, 16, CLK_SET_RATE_PARENT, 0, "ciu"), GATE_DA(sclk_uart0, "exynos4210-uart.0", "uclk0", "div_uart0", - 0xc350, 0, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), + SRC_MASK_PERIL0, 0, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), GATE_DA(sclk_uart1, "exynos4210-uart.1", "uclk1", "div_uart1", - 0xc350, 4, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), + SRC_MASK_PERIL0, 4, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), GATE_DA(sclk_uart2, "exynos4210-uart.2", "uclk2", "div_uart2", - 0xc350, 8, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), + SRC_MASK_PERIL0, 8, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), GATE_DA(sclk_uart3, "exynos4210-uart.3", "uclk3", "div_uart3", - 0xc350, 12, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), + SRC_MASK_PERIL0, 12, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), GATE_DA(sclk_uart4, "exynos4210-uart.4", "uclk4", "div_uart4", - 0xc350, 16, CLK_SET_RATE_PARENT, 0, "clk_uart_baud0"), - GATE(sclk_audio2, "sclk_audio2", "div_audio2", 0xc354, 4, + SRC_MASK_PERIL0, 16, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), + GATE(sclk_audio2, "sclk_audio2", "div_audio2", SRC_MASK_PERIL1, 4, CLK_SET_RATE_PARENT, 0), GATE_DA(sclk_spi0, "exynos4210-spi.0", "sclk_spi0", "div_spi_pre0", - 0xc354, 16, CLK_SET_RATE_PARENT, 0, "spi_busclk0"), + SRC_MASK_PERIL1, 16, CLK_SET_RATE_PARENT, + 0, "spi_busclk0"), GATE_DA(sclk_spi1, "exynos4210-spi.1", "sclk_spi1", "div_spi_pre1", - 0xc354, 20, CLK_SET_RATE_PARENT, 0, "spi_busclk0"), + SRC_MASK_PERIL1, 20, CLK_SET_RATE_PARENT, + 0, "spi_busclk0"), GATE_DA(sclk_spi2, "exynos4210-spi.2", "sclk_spi2", "div_spi_pre2", - 0xc354, 24, CLK_SET_RATE_PARENT, 0, "spi_busclk0"), + SRC_MASK_PERIL1, 24, CLK_SET_RATE_PARENT, + 0, "spi_busclk0"), GATE_DA(fimc0, "exynos4-fimc.0", "fimc0", "aclk160", GATE_IP_CAM, 0, 0, 0, "fimc"), GATE_DA(fimc1, "exynos4-fimc.1", "fimc1", "aclk160", -- cgit v1.1 From 0f1fce908efb2aa76b713d424b422f117376a04a Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:33:37 +0900 Subject: clk: exynos4: Remove SoC-specific registers from save list Current clock save list is shared for all Exynos4 SoCs, so it must contain only registers present in all supported SoCs, because accessing unavailable registers might have undefined effect. This patch removes registers specific for particular SoCs from shared save list, as they should be supported by separate SoC-specific lists. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 5e26d5d..7ae0a05 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -172,27 +172,21 @@ enum exynos4_clks { */ static __initdata unsigned long exynos4_clk_regs[] = { SRC_LEFTBUS, - E4X12_GATE_IP_IMAGE, GATE_IP_RIGHTBUS, - E4X12_GATE_IP_PERIR, SRC_TOP0, SRC_TOP1, SRC_CAM, SRC_TV, SRC_MFC, SRC_G3D, - E4210_SRC_IMAGE, SRC_LCD0, - SRC_LCD1, SRC_MAUDIO, SRC_FSYS, SRC_PERIL0, SRC_PERIL1, - E4X12_SRC_CAM1, SRC_MASK_CAM, SRC_MASK_TV, SRC_MASK_LCD0, - SRC_MASK_LCD1, SRC_MASK_MAUDIO, SRC_MASK_FSYS, SRC_MASK_PERIL0, @@ -204,8 +198,6 @@ static __initdata unsigned long exynos4_clk_regs[] = { DIV_G3D, DIV_IMAGE, DIV_LCD0, - E4210_DIV_LCD1, - E4X12_DIV_ISP, DIV_MAUDIO, DIV_FSYS0, DIV_FSYS1, @@ -217,24 +209,16 @@ static __initdata unsigned long exynos4_clk_regs[] = { DIV_PERIL3, DIV_PERIL4, DIV_PERIL5, - E4X12_DIV_CAM1, GATE_SCLK_CAM, GATE_IP_CAM, GATE_IP_TV, GATE_IP_MFC, GATE_IP_G3D, - E4210_GATE_IP_IMAGE, GATE_IP_LCD0, - GATE_IP_LCD1, - E4X12_GATE_IP_MAUDIO, GATE_IP_FSYS, GATE_IP_GPS, GATE_IP_PERIL, - GATE_IP_PERIR, - E4X12_MPLL_CON0, - E4X12_SRC_DMC, APLL_CON0, - E4210_MPLL_CON0, SRC_CPU, DIV_CPU0, }; -- cgit v1.1 From 7406ee7c2a31c0fad0d8062de93fcb58d1fa498c Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:35:18 +0900 Subject: clk: exynos4: Add E4210 prefix to LCD1 clock registers This patch adds E4210 prefix to all registers related to LCD1 clock domain, because they are present only on Exynos4210. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 7ae0a05..3d8a8a6 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -41,7 +41,7 @@ #define SRC_G3D 0xc22c #define E4210_SRC_IMAGE 0xc230 #define SRC_LCD0 0xc234 -#define SRC_LCD1 0xc238 +#define E4210_SRC_LCD1 0xc238 #define E4X12_SRC_ISP 0xc238 #define SRC_MAUDIO 0xc23c #define SRC_FSYS 0xc240 @@ -51,7 +51,7 @@ #define SRC_MASK_CAM 0xc320 #define SRC_MASK_TV 0xc324 #define SRC_MASK_LCD0 0xc334 -#define SRC_MASK_LCD1 0xc338 +#define E4210_SRC_MASK_LCD1 0xc338 #define E4X12_SRC_MASK_ISP 0xc338 #define SRC_MASK_MAUDIO 0xc33c #define SRC_MASK_FSYS 0xc340 @@ -85,7 +85,7 @@ #define GATE_IP_G3D 0xc92c #define E4210_GATE_IP_IMAGE 0xc930 #define GATE_IP_LCD0 0xc934 -#define GATE_IP_LCD1 0xc938 +#define E4210_GATE_IP_LCD1 0xc938 #define E4X12_GATE_IP_ISP 0xc938 #define E4X12_GATE_IP_MAUDIO 0xc93c #define GATE_IP_FSYS 0xc940 @@ -326,8 +326,8 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { MUX(none, "mout_g2d0", sclk_ampll_p4210, E4210_SRC_IMAGE, 0, 1), MUX(none, "mout_g2d1", sclk_evpll_p, E4210_SRC_IMAGE, 4, 1), MUX(none, "mout_g2d", mout_g2d_p, E4210_SRC_IMAGE, 8, 1), - MUX(none, "mout_fimd1", group1_p4210, SRC_LCD1, 0, 4), - MUX(none, "mout_mipi1", group1_p4210, SRC_LCD1, 12, 4), + MUX(none, "mout_fimd1", group1_p4210, E4210_SRC_LCD1, 0, 4), + MUX(none, "mout_mipi1", group1_p4210, E4210_SRC_LCD1, 12, 4), MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1, "sclk_mpll"), MUX_A(mout_core, "mout_core", mout_core_p4210, SRC_CPU, 16, 1, "mout_core"), @@ -538,10 +538,10 @@ struct samsung_gate_clock exynos4_gate_clks[] __initdata = { GATE(jpeg, "jpeg", "aclk160", GATE_IP_CAM, 6, 0, 0), GATE(mie0, "mie0", "aclk160", GATE_IP_LCD0, 1, 0, 0), GATE(dsim0, "dsim0", "aclk160", GATE_IP_LCD0, 3, 0, 0), - GATE(fimd1, "fimd1", "aclk160", GATE_IP_LCD1, 0, 0, 0), - GATE(mie1, "mie1", "aclk160", GATE_IP_LCD1, 1, 0, 0), - GATE(dsim1, "dsim1", "aclk160", GATE_IP_LCD1, 3, 0, 0), - GATE(smmu_fimd1, "smmu_fimd1", "aclk160", GATE_IP_LCD1, 4, 0, 0), + GATE(fimd1, "fimd1", "aclk160", E4210_GATE_IP_LCD1, 0, 0, 0), + GATE(mie1, "mie1", "aclk160", E4210_GATE_IP_LCD1, 1, 0, 0), + GATE(dsim1, "dsim1", "aclk160", E4210_GATE_IP_LCD1, 3, 0, 0), + GATE(smmu_fimd1, "smmu_fimd1", "aclk160", E4210_GATE_IP_LCD1, 4, 0, 0), GATE(tsi, "tsi", "aclk133", GATE_IP_FSYS, 4, 0, 0), GATE(sromc, "sromc", "aclk133", GATE_IP_FSYS, 11, 0, 0), GATE(sclk_g3d, "sclk_g3d", "div_g3d", GATE_IP_G3D, 0, @@ -738,7 +738,7 @@ struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { GATE(smmu_rotator, "smmu_rotator", "aclk200", E4210_GATE_IP_IMAGE, 4, 0, 0), GATE(sclk_mipi1, "sclk_mipi1", "div_mipi_pre1", - SRC_MASK_LCD1, 12, CLK_SET_RATE_PARENT, 0), + E4210_SRC_MASK_LCD1, 12, CLK_SET_RATE_PARENT, 0), GATE(sclk_sata, "sclk_sata", "div_sata", SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), GATE(sclk_mixer, "sclk_mixer", "mout_mixer", SRC_MASK_TV, 4, 0, 0), @@ -749,7 +749,7 @@ struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { GATE_A(rtc, "rtc", "aclk100", GATE_IP_PERIR, 15, 0, 0, "rtc"), GATE_A(keyif, "keyif", "aclk100", GATE_IP_PERIR, 16, 0, 0, "keypad"), GATE_DA(sclk_fimd1, "exynos4-fb.1", "sclk_fimd1", "div_fimd1", - SRC_MASK_LCD1, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimd"), + E4210_SRC_MASK_LCD1, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimd"), }; /* list of gate clocks supported in exynos4x12 soc */ -- cgit v1.1 From 1f1f326763cf2352173eca1fc4116de6950ba773 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:35:22 +0900 Subject: clk: exynos4: Add E4210 prefix to GATE_IP_PERIR register This definition is specific for Exynos4210 (which has another location than the same register on Exynos4x12 SoCs) and so needs appropriate prefix. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 3d8a8a6..b4daffa 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -91,7 +91,7 @@ #define GATE_IP_FSYS 0xc940 #define GATE_IP_GPS 0xc94c #define GATE_IP_PERIL 0xc950 -#define GATE_IP_PERIR 0xc960 +#define E4210_GATE_IP_PERIR 0xc960 #define E4X12_MPLL_CON0 0x10108 #define E4X12_SRC_DMC 0x10200 #define APLL_CON0 0x14100 @@ -732,9 +732,9 @@ struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { GATE(pcie, "pcie", "aclk133", GATE_IP_FSYS, 14, 0, 0), GATE(smmu_pcie, "smmu_pcie", "aclk133", GATE_IP_FSYS, 18, 0, 0), GATE(modemif, "modemif", "aclk100", GATE_IP_PERIL, 28, 0, 0), - GATE(chipid, "chipid", "aclk100", GATE_IP_PERIR, 0, 0, 0), - GATE(sysreg, "sysreg", "aclk100", GATE_IP_PERIR, 0, 0, 0), - GATE(hdmi_cec, "hdmi_cec", "aclk100", GATE_IP_PERIR, 11, 0, 0), + GATE(chipid, "chipid", "aclk100", E4210_GATE_IP_PERIR, 0, 0, 0), + GATE(sysreg, "sysreg", "aclk100", E4210_GATE_IP_PERIR, 0, 0, 0), + GATE(hdmi_cec, "hdmi_cec", "aclk100", E4210_GATE_IP_PERIR, 11, 0, 0), GATE(smmu_rotator, "smmu_rotator", "aclk200", E4210_GATE_IP_IMAGE, 4, 0, 0), GATE(sclk_mipi1, "sclk_mipi1", "div_mipi_pre1", @@ -744,10 +744,10 @@ struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { GATE(sclk_mixer, "sclk_mixer", "mout_mixer", SRC_MASK_TV, 4, 0, 0), GATE(sclk_dac, "sclk_dac", "mout_dac", SRC_MASK_TV, 8, 0, 0), GATE_A(tsadc, "tsadc", "aclk100", GATE_IP_PERIL, 15, 0, 0, "adc"), - GATE_A(mct, "mct", "aclk100", GATE_IP_PERIR, 13, 0, 0, "mct"), - GATE_A(wdt, "watchdog", "aclk100", GATE_IP_PERIR, 14, 0, 0, "watchdog"), - GATE_A(rtc, "rtc", "aclk100", GATE_IP_PERIR, 15, 0, 0, "rtc"), - GATE_A(keyif, "keyif", "aclk100", GATE_IP_PERIR, 16, 0, 0, "keypad"), + GATE_A(mct, "mct", "aclk100", E4210_GATE_IP_PERIR, 13, 0, 0, "mct"), + GATE_A(wdt, "watchdog", "aclk100", E4210_GATE_IP_PERIR, 14, 0, 0, "watchdog"), + GATE_A(rtc, "rtc", "aclk100", E4210_GATE_IP_PERIR, 15, 0, 0, "rtc"), + GATE_A(keyif, "keyif", "aclk100", E4210_GATE_IP_PERIR, 16, 0, 0, "keypad"), GATE_DA(sclk_fimd1, "exynos4-fb.1", "sclk_fimd1", "div_fimd1", E4210_SRC_MASK_LCD1, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimd"), }; -- cgit v1.1 From b950622bddc1c15f1e17041f1aec5816912ccca5 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:35:27 +0900 Subject: clk: exynos4: Remove E4X12 prefix from SRC_DMC register This register is present on all Exynos4 SoCs and so the prefix is misleading. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index b4daffa..a33b0ac 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -93,7 +93,7 @@ #define GATE_IP_PERIL 0xc950 #define E4210_GATE_IP_PERIR 0xc960 #define E4X12_MPLL_CON0 0x10108 -#define E4X12_SRC_DMC 0x10200 +#define SRC_DMC 0x10200 #define APLL_CON0 0x14100 #define E4210_MPLL_CON0 0x14108 #define SRC_CPU 0x14200 @@ -389,7 +389,7 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { MUX(none, "mout_jpeg1", sclk_evpll_p, E4X12_SRC_CAM1, 4, 1), MUX(none, "mout_jpeg", mout_jpeg_p, E4X12_SRC_CAM1, 8, 1), MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, - E4X12_SRC_DMC, 12, 1, "sclk_mpll"), + SRC_DMC, 12, 1, "sclk_mpll"), MUX_A(sclk_vpll, "sclk_vpll", mout_vpll_p, SRC_TOP0, 8, 1, "sclk_vpll"), MUX(mout_core, "mout_core", mout_core_p4x12, SRC_CPU, 16, 1), -- cgit v1.1 From fb948f74ce05c5540f9ad7e92242e1c931f7c2f6 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:35:32 +0900 Subject: clk: exynos4: Add missing registers to suspend save list This patch adds missing clock control registers to the list of registers that should be saved across system suspend. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index a33b0ac..2321000 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -22,7 +22,11 @@ /* Exynos4 clock controller register offsets */ #define SRC_LEFTBUS 0x4200 +#define DIV_LEFTBUS 0x4500 +#define GATE_IP_LEFTBUS 0x4800 #define E4X12_GATE_IP_IMAGE 0x4930 +#define SRC_RIGHTBUS 0x8200 +#define DIV_RIGHTBUS 0x8500 #define GATE_IP_RIGHTBUS 0x8800 #define E4X12_GATE_IP_PERIR 0x8960 #define EPLL_LOCK 0xc010 @@ -48,6 +52,7 @@ #define SRC_PERIL0 0xc250 #define SRC_PERIL1 0xc254 #define E4X12_SRC_CAM1 0xc258 +#define SRC_MASK_TOP 0xc310 #define SRC_MASK_CAM 0xc320 #define SRC_MASK_TV 0xc324 #define SRC_MASK_LCD0 0xc334 @@ -92,12 +97,20 @@ #define GATE_IP_GPS 0xc94c #define GATE_IP_PERIL 0xc950 #define E4210_GATE_IP_PERIR 0xc960 +#define GATE_BLOCK 0xc970 #define E4X12_MPLL_CON0 0x10108 #define SRC_DMC 0x10200 +#define SRC_MASK_DMC 0x10300 +#define DIV_DMC0 0x10500 +#define DIV_DMC1 0x10504 +#define GATE_IP_DMC 0x10900 #define APLL_CON0 0x14100 #define E4210_MPLL_CON0 0x14108 #define SRC_CPU 0x14200 #define DIV_CPU0 0x14500 +#define DIV_CPU1 0x14504 +#define GATE_SCLK_CPU 0x14800 +#define GATE_IP_CPU 0x14900 #define E4X12_DIV_ISP0 0x18300 #define E4X12_DIV_ISP1 0x18304 #define E4X12_GATE_ISP0 0x18800 @@ -172,7 +185,17 @@ enum exynos4_clks { */ static __initdata unsigned long exynos4_clk_regs[] = { SRC_LEFTBUS, + DIV_LEFTBUS, + GATE_IP_LEFTBUS, + SRC_RIGHTBUS, + DIV_RIGHTBUS, GATE_IP_RIGHTBUS, + EPLL_CON0, + EPLL_CON1, + EPLL_CON2, + VPLL_CON0, + VPLL_CON1, + VPLL_CON2, SRC_TOP0, SRC_TOP1, SRC_CAM, @@ -184,6 +207,7 @@ static __initdata unsigned long exynos4_clk_regs[] = { SRC_FSYS, SRC_PERIL0, SRC_PERIL1, + SRC_MASK_TOP, SRC_MASK_CAM, SRC_MASK_TV, SRC_MASK_LCD0, @@ -218,9 +242,18 @@ static __initdata unsigned long exynos4_clk_regs[] = { GATE_IP_FSYS, GATE_IP_GPS, GATE_IP_PERIL, + GATE_BLOCK, + SRC_MASK_DMC, + SRC_DMC, + DIV_DMC0, + DIV_DMC1, + GATE_IP_DMC, APLL_CON0, SRC_CPU, DIV_CPU0, + DIV_CPU1, + GATE_SCLK_CPU, + GATE_IP_CPU, }; /* list of all parent clock list */ -- cgit v1.1 From 6b5756e8bd19f8f1f23386d41997d0309e7a82a6 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 4 Apr 2013 13:35:35 +0900 Subject: clk: exynos4: Add support for SoC-specific register save list This patch extends suspend/resume support for SoC-specific registers to handle differences in register sets on particular SoCs. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 30 ++++++++++++++++++++++++++++-- drivers/clk/samsung/clk-exynos5250.c | 3 ++- drivers/clk/samsung/clk-exynos5440.c | 2 +- drivers/clk/samsung/clk.c | 9 ++++++--- drivers/clk/samsung/clk.h | 3 ++- 5 files changed, 39 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 2321000..17674da 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -183,6 +183,26 @@ enum exynos4_clks { * list of controller registers to be saved and restored during a * suspend/resume cycle. */ +static __initdata unsigned long exynos4210_clk_save[] = { + E4210_SRC_IMAGE, + E4210_SRC_LCD1, + E4210_SRC_MASK_LCD1, + E4210_DIV_LCD1, + E4210_GATE_IP_IMAGE, + E4210_GATE_IP_LCD1, + E4210_GATE_IP_PERIR, + E4210_MPLL_CON0, +}; + +static __initdata unsigned long exynos4x12_clk_save[] = { + E4X12_GATE_IP_IMAGE, + E4X12_GATE_IP_PERIR, + E4X12_SRC_CAM1, + E4X12_DIV_ISP, + E4X12_DIV_CAM1, + E4X12_MPLL_CON0, +}; + static __initdata unsigned long exynos4_clk_regs[] = { SRC_LEFTBUS, DIV_LEFTBUS, @@ -986,8 +1006,14 @@ void __init exynos4_clk_init(struct device_node *np) panic("%s: unable to determine soc\n", __func__); } - samsung_clk_init(np, reg_base, nr_clks, - exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs)); + if (exynos4_soc == EXYNOS4210) + samsung_clk_init(np, reg_base, nr_clks, + exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs), + exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save)); + else + samsung_clk_init(np, reg_base, nr_clks, + exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs), + exynos4x12_clk_save, ARRAY_SIZE(exynos4x12_clk_save)); if (np) samsung_clk_of_register_fixed_ext(exynos4_fixed_rate_ext_clks, diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 1152125..5cd9a0c 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -477,7 +477,8 @@ void __init exynos5250_clk_init(struct device_node *np) } samsung_clk_init(np, reg_base, nr_clks, - exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs)); + exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs), + NULL, 0); samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks, ARRAY_SIZE(exynos5250_fixed_rate_ext_clks), ext_clk_match); diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c index d588e93..a0a094c 100644 --- a/drivers/clk/samsung/clk-exynos5440.c +++ b/drivers/clk/samsung/clk-exynos5440.c @@ -115,7 +115,7 @@ void __init exynos5440_clk_init(struct device_node *np) return; } - samsung_clk_init(np, reg_base, nr_clks, NULL, 0); + samsung_clk_init(np, reg_base, nr_clks, NULL, 0, NULL, 0); samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks, ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match); diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 82f27f6..3a50d4f 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -54,7 +54,8 @@ static struct syscore_ops samsung_clk_syscore_ops = { /* setup the essentials required to support clock lookup using ccf */ void __init samsung_clk_init(struct device_node *np, void __iomem *base, unsigned long nr_clks, unsigned long *rdump, - unsigned long nr_rdump) + unsigned long nr_rdump, unsigned long *soc_rdump, + unsigned long nr_soc_rdump) { reg_base = base; @@ -62,7 +63,7 @@ void __init samsung_clk_init(struct device_node *np, void __iomem *base, if (rdump && nr_rdump) { unsigned int idx; reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump) - * nr_rdump, GFP_KERNEL); + * (nr_rdump + nr_soc_rdump), GFP_KERNEL); if (!reg_dump) { pr_err("%s: memory alloc for register dump failed\n", __func__); @@ -71,7 +72,9 @@ void __init samsung_clk_init(struct device_node *np, void __iomem *base, for (idx = 0; idx < nr_rdump; idx++) reg_dump[idx].offset = rdump[idx]; - nr_reg_dump = nr_rdump; + for (idx = 0; idx < nr_soc_rdump; idx++) + reg_dump[nr_rdump + idx].offset = soc_rdump[idx]; + nr_reg_dump = nr_rdump + nr_soc_rdump; register_syscore_ops(&samsung_clk_syscore_ops); } #endif diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index 6bacd6f..10b2111 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -262,7 +262,8 @@ struct samsung_clk_reg_dump { extern void __init samsung_clk_init(struct device_node *np, void __iomem *base, unsigned long nr_clks, unsigned long *rdump, - unsigned long nr_rdump); + unsigned long nr_rdump, unsigned long *soc_rdump, + unsigned long nr_soc_rdump); extern void __init samsung_clk_of_register_fixed_ext( struct samsung_fixed_rate_clock *fixed_rate_clk, unsigned int nr_fixed_rate_clk, -- cgit v1.1 From 17d4caccefd138c3e4970132c1db177024caf3c6 Mon Sep 17 00:00:00 2001 From: Leela Krishna Amudala Date: Thu, 4 Apr 2013 15:44:40 +0900 Subject: clk: exynos5250: register display block gate clocks to common clock framework Add gate clocks for fimd, mie, dsim, dp, mixer and hdmi. Register it to common clock framework. Signed-off-by: Leela Krishna Amudala Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos5250.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 5cd9a0c..e5e733a 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -62,6 +62,7 @@ #define GATE_IP_PERIS 0x10960 #define SRC_CDREX 0x20200 #define PLL_DIV2_SEL 0x20a24 +#define GATE_IP_DISP1 0x10928 /* * Let each supported clock get a unique id. This id is used to lookup the clock @@ -98,7 +99,7 @@ enum exynos5250_clks { spi2, i2s1, i2s2, pcm1, pcm2, pwm, spdif, ac97, hsi2c0, hsi2c1, hsi2c2, hsi2c3, chipid, sysreg, pmu, cmu_top, cmu_core, cmu_mem, tzpc0, tzpc1, tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, tzpc8, tzpc9, hdmi_cec, mct, - wdt, rtc, tmu, + wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi, nr_clks, }; @@ -150,6 +151,7 @@ static __initdata unsigned long exynos5250_clk_regs[] = { GATE_IP_PERIS, SRC_CDREX, PLL_DIV2_SEL, + GATE_IP_DISP1, }; /* list of all parent clock list */ @@ -455,6 +457,12 @@ struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { SRC_MASK_PERIC1, 20, CLK_SET_RATE_PARENT, 0), GATE(sclk_spi2, "sclk_spi2", "div_spi_pre2", SRC_MASK_PERIC1, 24, CLK_SET_RATE_PARENT, 0), + GATE(fimd1, "fimd1", "aclk200", GATE_IP_DISP1, 0, 0, 0), + GATE(mie1, "mie1", "aclk200", GATE_IP_DISP1, 1, 0, 0), + GATE(dsim0, "dsim0", "aclk200", GATE_IP_DISP1, 3, 0, 0), + GATE(dp, "dp", "aclk200", GATE_IP_DISP1, 4, 0, 0), + GATE(mixer, "mixer", "aclk200", GATE_IP_DISP1, 5, 0, 0), + GATE(hdmi, "hdmi", "aclk200", GATE_IP_DISP1, 6, 0, 0), }; static __initdata struct of_device_id ext_clk_match[] = { -- cgit v1.1 From 4f0f234fce1d263cc9881456352e8fd56ead0514 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 20 Mar 2013 10:46:01 +0100 Subject: arm: zynq: Move timer to generic location Move zynq timer out of mach folder to generic location and enable it. Signed-off-by: Michal Simek --- drivers/clocksource/Kconfig | 3 + drivers/clocksource/Makefile | 1 + drivers/clocksource/cadence_ttc_timer.c | 436 ++++++++++++++++++++++++++++++++ 3 files changed, 440 insertions(+) create mode 100644 drivers/clocksource/cadence_ttc_timer.c (limited to 'drivers') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index e507ab7..3167fda 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -31,6 +31,9 @@ config SUNXI_TIMER config VT8500_TIMER bool +config CADENCE_TTC_TIMER + bool + config CLKSRC_NOMADIK_MTU bool depends on (ARCH_NOMADIK || ARCH_U8500) diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 4d8283a..e74c8ce 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_CADENCE_TTC_TIMER) += cadence_ttc_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c new file mode 100644 index 0000000..685bc60 --- /dev/null +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -0,0 +1,436 @@ +/* + * This file contains driver for the Cadence Triple Timer Counter Rev 06 + * + * Copyright (C) 2011-2013 Xilinx + * + * based on arch/mips/kernel/time.c timer driver + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * This driver configures the 2 16-bit count-up timers as follows: + * + * T1: Timer 1, clocksource for generic timekeeping + * T2: Timer 2, clockevent source for hrtimers + * T3: Timer 3, + * + * The input frequency to the timer module for emulation is 2.5MHz which is + * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32, + * the timers are clocked at 78.125KHz (12.8 us resolution). + + * The input frequency to the timer module in silicon is configurable and + * obtained from device tree. The pre-scaler of 32 is used. + */ + +/* + * Timer Register Offset Definitions of Timer 1, Increment base address by 4 + * and use same offsets for Timer 2 + */ +#define TTC_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */ +#define TTC_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */ +#define TTC_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */ +#define TTC_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */ +#define TTC_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */ +#define TTC_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */ + +#define TTC_CNT_CNTRL_DISABLE_MASK 0x1 + +/* + * Setup the timers to use pre-scaling, using a fixed value for now that will + * work across most input frequency, but it may need to be more dynamic + */ +#define PRESCALE_EXPONENT 11 /* 2 ^ PRESCALE_EXPONENT = PRESCALE */ +#define PRESCALE 2048 /* The exponent must match this */ +#define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1) +#define CLK_CNTRL_PRESCALE_EN 1 +#define CNT_CNTRL_RESET (1 << 4) + +/** + * struct ttc_timer - This definition defines local timer structure + * + * @base_addr: Base address of timer + * @clk: Associated clock source + * @clk_rate_change_nb Notifier block for clock rate changes + */ +struct ttc_timer { + void __iomem *base_addr; + struct clk *clk; + struct notifier_block clk_rate_change_nb; +}; + +#define to_ttc_timer(x) \ + container_of(x, struct ttc_timer, clk_rate_change_nb) + +struct ttc_timer_clocksource { + struct ttc_timer ttc; + struct clocksource cs; +}; + +#define to_ttc_timer_clksrc(x) \ + container_of(x, struct ttc_timer_clocksource, cs) + +struct ttc_timer_clockevent { + struct ttc_timer ttc; + struct clock_event_device ce; +}; + +#define to_ttc_timer_clkevent(x) \ + container_of(x, struct ttc_timer_clockevent, ce) + +/** + * ttc_set_interval - Set the timer interval value + * + * @timer: Pointer to the timer instance + * @cycles: Timer interval ticks + **/ +static void ttc_set_interval(struct ttc_timer *timer, + unsigned long cycles) +{ + u32 ctrl_reg; + + /* Disable the counter, set the counter value and re-enable counter */ + ctrl_reg = __raw_readl(timer->base_addr + TTC_CNT_CNTRL_OFFSET); + ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; + __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET); + + __raw_writel(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET); + + /* + * Reset the counter (0x10) so that it starts from 0, one-shot + * mode makes this needed for timing to be right. + */ + ctrl_reg |= CNT_CNTRL_RESET; + ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK; + __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET); +} + +/** + * ttc_clock_event_interrupt - Clock event timer interrupt handler + * + * @irq: IRQ number of the Timer + * @dev_id: void pointer to the ttc_timer instance + * + * returns: Always IRQ_HANDLED - success + **/ +static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id) +{ + struct ttc_timer_clockevent *ttce = dev_id; + struct ttc_timer *timer = &ttce->ttc; + + /* Acknowledge the interrupt and call event handler */ + __raw_readl(timer->base_addr + TTC_ISR_OFFSET); + + ttce->ce.event_handler(&ttce->ce); + + return IRQ_HANDLED; +} + +/** + * __ttc_clocksource_read - Reads the timer counter register + * + * returns: Current timer counter register value + **/ +static cycle_t __ttc_clocksource_read(struct clocksource *cs) +{ + struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc; + + return (cycle_t)__raw_readl(timer->base_addr + + TTC_COUNT_VAL_OFFSET); +} + +/** + * ttc_set_next_event - Sets the time interval for next event + * + * @cycles: Timer interval ticks + * @evt: Address of clock event instance + * + * returns: Always 0 - success + **/ +static int ttc_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt); + struct ttc_timer *timer = &ttce->ttc; + + ttc_set_interval(timer, cycles); + return 0; +} + +/** + * ttc_set_mode - Sets the mode of timer + * + * @mode: Mode to be set + * @evt: Address of clock event instance + **/ +static void ttc_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt); + struct ttc_timer *timer = &ttce->ttc; + u32 ctrl_reg; + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + ttc_set_interval(timer, + DIV_ROUND_CLOSEST(clk_get_rate(ttce->ttc.clk), + PRESCALE * HZ)); + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + ctrl_reg = __raw_readl(timer->base_addr + + TTC_CNT_CNTRL_OFFSET); + ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; + __raw_writel(ctrl_reg, + timer->base_addr + TTC_CNT_CNTRL_OFFSET); + break; + case CLOCK_EVT_MODE_RESUME: + ctrl_reg = __raw_readl(timer->base_addr + + TTC_CNT_CNTRL_OFFSET); + ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK; + __raw_writel(ctrl_reg, + timer->base_addr + TTC_CNT_CNTRL_OFFSET); + break; + } +} + +static int ttc_rate_change_clocksource_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct ttc_timer *ttc = to_ttc_timer(nb); + struct ttc_timer_clocksource *ttccs = container_of(ttc, + struct ttc_timer_clocksource, ttc); + + switch (event) { + case POST_RATE_CHANGE: + /* + * Do whatever is necessary to maintain a proper time base + * + * I cannot find a way to adjust the currently used clocksource + * to the new frequency. __clocksource_updatefreq_hz() sounds + * good, but does not work. Not sure what's that missing. + * + * This approach works, but triggers two clocksource switches. + * The first after unregister to clocksource jiffies. And + * another one after the register to the newly registered timer. + * + * Alternatively we could 'waste' another HW timer to ping pong + * between clock sources. That would also use one register and + * one unregister call, but only trigger one clocksource switch + * for the cost of another HW timer used by the OS. + */ + clocksource_unregister(&ttccs->cs); + clocksource_register_hz(&ttccs->cs, + ndata->new_rate / PRESCALE); + /* fall through */ + case PRE_RATE_CHANGE: + case ABORT_RATE_CHANGE: + default: + return NOTIFY_DONE; + } +} + +static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base) +{ + struct ttc_timer_clocksource *ttccs; + int err; + + ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL); + if (WARN_ON(!ttccs)) + return; + + ttccs->ttc.clk = clk; + + err = clk_prepare_enable(ttccs->ttc.clk); + if (WARN_ON(err)) { + kfree(ttccs); + return; + } + + ttccs->ttc.clk_rate_change_nb.notifier_call = + ttc_rate_change_clocksource_cb; + ttccs->ttc.clk_rate_change_nb.next = NULL; + if (clk_notifier_register(ttccs->ttc.clk, + &ttccs->ttc.clk_rate_change_nb)) + pr_warn("Unable to register clock notifier.\n"); + + ttccs->ttc.base_addr = base; + ttccs->cs.name = "ttc_clocksource"; + ttccs->cs.rating = 200; + ttccs->cs.read = __ttc_clocksource_read; + ttccs->cs.mask = CLOCKSOURCE_MASK(16); + ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; + + /* + * Setup the clock source counter to be an incrementing counter + * with no interrupt and it rolls over at 0xFFFF. Pre-scale + * it by 32 also. Let it start running now. + */ + __raw_writel(0x0, ttccs->ttc.base_addr + TTC_IER_OFFSET); + __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, + ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET); + __raw_writel(CNT_CNTRL_RESET, + ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET); + + err = clocksource_register_hz(&ttccs->cs, + clk_get_rate(ttccs->ttc.clk) / PRESCALE); + if (WARN_ON(err)) { + kfree(ttccs); + return; + } +} + +static int ttc_rate_change_clockevent_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct ttc_timer *ttc = to_ttc_timer(nb); + struct ttc_timer_clockevent *ttcce = container_of(ttc, + struct ttc_timer_clockevent, ttc); + + switch (event) { + case POST_RATE_CHANGE: + { + unsigned long flags; + + /* + * clockevents_update_freq should be called with IRQ disabled on + * the CPU the timer provides events for. The timer we use is + * common to both CPUs, not sure if we need to run on both + * cores. + */ + local_irq_save(flags); + clockevents_update_freq(&ttcce->ce, + ndata->new_rate / PRESCALE); + local_irq_restore(flags); + + /* fall through */ + } + case PRE_RATE_CHANGE: + case ABORT_RATE_CHANGE: + default: + return NOTIFY_DONE; + } +} + +static void __init ttc_setup_clockevent(struct clk *clk, + void __iomem *base, u32 irq) +{ + struct ttc_timer_clockevent *ttcce; + int err; + + ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL); + if (WARN_ON(!ttcce)) + return; + + ttcce->ttc.clk = clk; + + err = clk_prepare_enable(ttcce->ttc.clk); + if (WARN_ON(err)) { + kfree(ttcce); + return; + } + + ttcce->ttc.clk_rate_change_nb.notifier_call = + ttc_rate_change_clockevent_cb; + ttcce->ttc.clk_rate_change_nb.next = NULL; + if (clk_notifier_register(ttcce->ttc.clk, + &ttcce->ttc.clk_rate_change_nb)) + pr_warn("Unable to register clock notifier.\n"); + + ttcce->ttc.base_addr = base; + ttcce->ce.name = "ttc_clockevent"; + ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + ttcce->ce.set_next_event = ttc_set_next_event; + ttcce->ce.set_mode = ttc_set_mode; + ttcce->ce.rating = 200; + ttcce->ce.irq = irq; + ttcce->ce.cpumask = cpu_possible_mask; + + /* + * Setup the clock event timer to be an interval timer which + * is prescaled by 32 using the interval interrupt. Leave it + * disabled for now. + */ + __raw_writel(0x23, ttcce->ttc.base_addr + TTC_CNT_CNTRL_OFFSET); + __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, + ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET); + __raw_writel(0x1, ttcce->ttc.base_addr + TTC_IER_OFFSET); + + err = request_irq(irq, ttc_clock_event_interrupt, + IRQF_DISABLED | IRQF_TIMER, + ttcce->ce.name, ttcce); + if (WARN_ON(err)) { + kfree(ttcce); + return; + } + + clockevents_config_and_register(&ttcce->ce, + clk_get_rate(ttcce->ttc.clk) / PRESCALE, 1, 0xfffe); +} + +/** + * ttc_timer_init - Initialize the timer + * + * Initializes the timer hardware and register the clock source and clock event + * timers with Linux kernal timer framework + */ +static void __init ttc_timer_init(struct device_node *timer) +{ + unsigned int irq; + void __iomem *timer_baseaddr; + struct clk *clk; + static int initialized; + + if (initialized) + return; + + initialized = 1; + + /* + * Get the 1st Triple Timer Counter (TTC) block from the device tree + * and use it. Note that the event timer uses the interrupt and it's the + * 2nd TTC hence the irq_of_parse_and_map(,1) + */ + timer_baseaddr = of_iomap(timer, 0); + if (!timer_baseaddr) { + pr_err("ERROR: invalid timer base address\n"); + BUG(); + } + + irq = irq_of_parse_and_map(timer, 1); + if (irq <= 0) { + pr_err("ERROR: invalid interrupt number\n"); + BUG(); + } + + clk = of_clk_get_by_name(timer, "cpu_1x"); + if (IS_ERR(clk)) { + pr_err("ERROR: timer input clock not found\n"); + BUG(); + } + + ttc_setup_clocksource(clk, timer_baseaddr); + ttc_setup_clockevent(clk, timer_baseaddr + 4, irq); + + pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq); +} + +CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init); -- cgit v1.1 From 5a88b0d10f198ddd5f988f40d34b52f34c87a5c6 Mon Sep 17 00:00:00 2001 From: Yen Lin Date: Wed, 6 Mar 2013 11:47:24 +0000 Subject: clk: tegra: Fix periph_clk_to_bit macro The parameter name should be "gate", not "periph". This worked, however, because it happens that everywhere periph_clk_to_bit is called, "gate" was in the local scope. Signed-off-by: Yen Lin Signed-off-by: Andrew Chew Reviewed-by: Thierry Reding Reviewed-by: Prashant Gaikwad Acked-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-periph-gate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index 6dd5332..d87e1ce 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -41,7 +41,7 @@ static DEFINE_SPINLOCK(periph_ref_lock); #define write_rst_clr(val, gate) \ writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg)) -#define periph_clk_to_bit(periph) (1 << (gate->clk_num % 32)) +#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32)) /* Peripheral gate clock ops */ static int clk_periph_is_enabled(struct clk_hw *hw) -- cgit v1.1 From 4dd59cdd35506b77ed4ebf4bb90347d7653ba585 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 28 Mar 2013 21:31:27 +0100 Subject: clk: tegra: Export peripheral reset functions The tegra_periph_reset_assert() and tegra_periph_reset_deassert() functions can be used by drivers to reset peripherals. In order to allow such drivers to be built as modules, export the functions. Note that this restores the status quo as the functions were exported before the move to the drivers/clk tree. Signed-off-by: Thierry Reding Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-periph.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 788486e6..9dbd301 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -128,6 +129,7 @@ void tegra_periph_reset_deassert(struct clk *c) tegra_periph_reset(gate, 0); } +EXPORT_SYMBOL(tegra_periph_reset_deassert); void tegra_periph_reset_assert(struct clk *c) { @@ -147,6 +149,7 @@ void tegra_periph_reset_assert(struct clk *c) tegra_periph_reset(gate, 1); } +EXPORT_SYMBOL(tegra_periph_reset_assert); const struct clk_ops tegra_clk_periph_ops = { .get_parent = clk_periph_get_parent, -- cgit v1.1 From ce910686f814fe220f4e55ecca5cfdea7082b3de Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 2 Apr 2013 16:18:44 +0200 Subject: clk: tegra: Make gr2d and gr3d clocks children of pll_c By default these clocks are children of pll_m, but in downstream kernels they are reparented to pll_c. While at it, decrease their frequencies to 300 MHz because the defaults aren't in the specified range. gr2d can reportedly run at much higher frequencies, but 300 MHz works and is a more conservative default. Signed-off-by: Thierry Reding Acked-by: Mike Turquette Acked-By: Peter De Schrijver Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra20.c | 2 ++ drivers/clk/tegra/clk-tegra30.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index b92d48b..f87bd4a 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -1247,6 +1247,8 @@ static __initdata struct tegra_clk_init_table init_table[] = { {host1x, pll_c, 150000000, 0}, {disp1, pll_p, 600000000, 0}, {disp2, pll_p, 600000000, 0}, + {gr2d, pll_c, 300000000, 0}, + {gr3d, pll_c, 300000000, 0}, {clk_max, clk_max, 0, 0}, /* This MUST be the last entry */ }; diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index ba6f51b..b8b241d 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1911,6 +1911,8 @@ static __initdata struct tegra_clk_init_table init_table[] = { {disp1, pll_p, 600000000, 0}, {disp2, pll_p, 600000000, 0}, {twd, clk_max, 0, 1}, + {gr2d, pll_c, 300000000, 0}, + {gr3d, pll_c, 300000000, 0}, {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ }; -- cgit v1.1 From 82ce742140f32394cc5be75f1c98cdbbff284582 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Thu, 4 Apr 2013 14:35:04 +0530 Subject: clk: tegra: Fix cdev1 and cdev2 IDs Correct IDs for cdev1 and cdev2 are 94 and 93 respectively. Signed-off-by: Prashant Gaikwad [swarren: split into separate driver and device-tree patches] Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra20.c | 2 +- drivers/clk/tegra/clk-tegra30.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index f87bd4a..a7dc0a9 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -236,7 +236,7 @@ enum tegra20_clk { dvc, dsi, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2, usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3, pex, owr, afi, csite, pcie_xclk, avpucq = 75, la, irama = 84, iramb, - iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev1, cdev2, + iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev2, cdev1, uartb = 96, vfir, spdif_in, spdif_out, vi, vi_sensor, tvo, cve, osc, clk_32k, clk_m, sclk, cclk, hclk, pclk, blink, pll_a, pll_a_out0, pll_c, pll_c_out1, pll_d, pll_d_out0, pll_e, pll_m, pll_m_out1, diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index b8b241d..181a6ee 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -330,7 +330,7 @@ enum tegra30_clk { usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3, pcie, owr, afi, csite, pciex, avpucq, la, dtv = 79, ndspeed, i2cslow, dsib, irama = 84, iramb, iramc, iramd, cram2, audio_2x = 90, csus = 92, - cdev1, cdev2, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4, + cdev2, cdev1, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4, i2c4, sbc5, sbc6, d_audio, apbif, dam0, dam1, dam2, hda2codec_2x, atomics, audio0_2x, audio1_2x, audio2_2x, audio3_2x, audio4_2x, spdif_2x, actmon, extern1, extern2, extern3, sata_oob, sata, hda, -- cgit v1.1 From 441f199a37cfd66c5dd8dd45490bd3ea6971117d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 25 Mar 2013 13:22:24 -0600 Subject: clk: tegra: defer application of init table The Tegra clock driver is initialized during the ARM machine descriptor's .init_irq() hook. It can't be initialized earlier, since dynamic memory usage is required. It can't be initialized later, since the .init_timer() hook needs the clocks initialized. However, at this time, udelay() doesn't work. The Tegra clock initialization table may enable some PLLs. Enabling a PLL may require usage of udelay(). Hence, this can't happen right when the clock driver is initialized. To solve this, separate the clock driver initialization from the clock table processing, so they can execute at separate times. Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra20.c | 7 ++++++- drivers/clk/tegra/clk-tegra30.c | 7 ++++++- drivers/clk/tegra/clk.c | 10 ++++++++++ drivers/clk/tegra/clk.h | 3 +++ 4 files changed, 25 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index a7dc0a9..a15fb28 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -1252,6 +1252,11 @@ static __initdata struct tegra_clk_init_table init_table[] = { {clk_max, clk_max, 0, 0}, /* This MUST be the last entry */ }; +static void __init tegra20_clock_apply_init_table(void) +{ + tegra_init_from_table(init_table, clks, clk_max); +} + /* * Some clocks may be used by different drivers depending on the board * configuration. List those here to register them twice in the clock lookup @@ -1318,7 +1323,7 @@ void __init tegra20_clock_init(struct device_node *np) clk_data.clk_num = ARRAY_SIZE(clks); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - tegra_init_from_table(init_table, clks, clk_max); + tegra_clk_apply_init_table = tegra20_clock_apply_init_table; tegra_cpu_car_ops = &tegra20_cpu_car_ops; } diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 181a6ee..e0ee93c 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1916,6 +1916,11 @@ static __initdata struct tegra_clk_init_table init_table[] = { {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ }; +static void __init tegra30_clock_apply_init_table(void) +{ + tegra_init_from_table(init_table, clks, clk_max); +} + /* * Some clocks may be used by different drivers depending on the board * configuration. List those here to register them twice in the clock lookup @@ -1989,7 +1994,7 @@ void __init tegra30_clock_init(struct device_node *np) clk_data.clk_num = ARRAY_SIZE(clks); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - tegra_init_from_table(init_table, clks, clk_max); + tegra_clk_apply_init_table = tegra30_clock_apply_init_table; tegra_cpu_car_ops = &tegra30_cpu_car_ops; } diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index a603b9a..4a61d15 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -83,3 +83,13 @@ void __init tegra_clocks_init(void) { of_clk_init(tegra_dt_clk_match); } + +tegra_clk_apply_init_table_func tegra_clk_apply_init_table; + +void __init tegra_clocks_apply_init_table(void) +{ + if (!tegra_clk_apply_init_table) + return; + + tegra_clk_apply_init_table(); +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index a09d7dc..3c566e2 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -510,4 +510,7 @@ void tegra30_clock_init(struct device_node *np); static inline void tegra30_clock_init(struct device_node *np) {} #endif /* CONFIG_ARCH_TEGRA_3x_SOC */ +typedef void (*tegra_clk_apply_init_table_func)(void); +extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; + #endif /* TEGRA_CLK_H */ -- cgit v1.1 From 6a676fa0af4e2bd11ab3950e277e81a959a9a198 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:35 +0300 Subject: clk: tegra: provide dummy cpu car ops tegra_boot_secondary() relies on some of the car ops. This means having an uninitialized tegra_cpu_car_ops will lead to an early boot panic. Providing a dummy struct avoids this and makes adding Tegra114 clock support in a bisectable way a lot easier. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 4a61d15..70b7a47 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -22,7 +22,8 @@ #include "clk.h" /* Global data of Tegra CPU CAR ops */ -struct tegra_cpu_car_ops *tegra_cpu_car_ops; +static struct tegra_cpu_car_ops dummy_car_ops; +struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops; void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, struct clk *clks[], int clk_max) -- cgit v1.1 From dba4072a4a20b2986562cced98ce04a887614528 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:36 +0300 Subject: clk: tegra: Refactor PLL programming code Refactor the PLL programming code to make it useable by the new PLL types introduced by Tegra114. The following changes were done: * Split programming the PLL into updating m,n,p and updating cpcon * Move locking from _update_pll_cpcon() to clk_pll_set_rate() * Introduce _get_pll_mnp() helper * Move check for identical m,n,p values to clk_pll_set_rate() * struct tegra_clk_pll_freq_table will always contain the values as defined by the hardware. * Simplify the arguments to clk_pll_wait_for_lock() * Split _tegra_clk_register_pll() Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 262 ++++++++++++++++++++++++---------------- drivers/clk/tegra/clk-tegra20.c | 144 +++++++++++----------- drivers/clk/tegra/clk-tegra30.c | 234 +++++++++++++++++------------------ drivers/clk/tegra/clk.h | 9 +- 4 files changed, 356 insertions(+), 293 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 165f247..3feefb1 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -113,20 +113,28 @@ static void clk_pll_enable_lock(struct tegra_clk_pll *pll) pll_writel_misc(val, pll); } -static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll, - void __iomem *lock_addr, u32 lock_bit_idx) +static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll) { int i; - u32 val; + u32 val, lock_bit; + void __iomem *lock_addr; if (!(pll->flags & TEGRA_PLL_USE_LOCK)) { udelay(pll->params->lock_delay); return 0; } + lock_addr = pll->clk_base; + if (pll->flags & TEGRA_PLL_LOCK_MISC) + lock_addr += pll->params->misc_reg; + else + lock_addr += pll->params->base_reg; + + lock_bit = BIT(pll->params->lock_bit_idx); + for (i = 0; i < pll->params->lock_delay; i++) { val = readl_relaxed(lock_addr); - if (val & BIT(lock_bit_idx)) { + if (val & lock_bit) { udelay(PLL_POST_LOCK_DELAY); return 0; } @@ -155,7 +163,7 @@ static int clk_pll_is_enabled(struct clk_hw *hw) return val & PLL_BASE_ENABLE ? 1 : 0; } -static int _clk_pll_enable(struct clk_hw *hw) +static void _clk_pll_enable(struct clk_hw *hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); u32 val; @@ -172,11 +180,6 @@ static int _clk_pll_enable(struct clk_hw *hw) val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE; writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE); } - - clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->base_reg, - pll->params->lock_bit_idx); - - return 0; } static void _clk_pll_disable(struct clk_hw *hw) @@ -204,7 +207,9 @@ static int clk_pll_enable(struct clk_hw *hw) if (pll->lock) spin_lock_irqsave(pll->lock, flags); - ret = _clk_pll_enable(hw); + _clk_pll_enable(hw); + + ret = clk_pll_wait_for_lock(pll); if (pll->lock) spin_unlock_irqrestore(pll->lock, flags); @@ -241,8 +246,6 @@ static int _get_table_rate(struct clk_hw *hw, if (sel->input_rate == 0) return -EINVAL; - BUG_ON(sel->p < 1); - cfg->input_rate = sel->input_rate; cfg->output_rate = sel->output_rate; cfg->m = sel->m; @@ -290,88 +293,109 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, cfg->output_rate <<= 1) p_div++; - cfg->p = 1 << p_div; + cfg->p = p_div; cfg->m = parent_rate / cfreq; cfg->n = cfg->output_rate / cfreq; cfg->cpcon = OUT_OF_TABLE_CPCON; if (cfg->m > divm_max(pll) || cfg->n > divn_max(pll) || - cfg->p > divp_max(pll) || cfg->output_rate > pll->params->vco_max) { + (1 << p_div) > divp_max(pll) + || cfg->output_rate > pll->params->vco_max) { pr_err("%s: Failed to set %s rate %lu\n", __func__, __clk_get_name(hw->clk), rate); return -EINVAL; } + if (pll->flags & TEGRA_PLLU) + cfg->p ^= 1; + return 0; } -static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, - unsigned long rate) +static void _update_pll_mnp(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg) { - struct tegra_clk_pll *pll = to_clk_pll(hw); - unsigned long flags = 0; - u32 divp, val, old_base; - int state; - - divp = __ffs(cfg->p); - - if (pll->flags & TEGRA_PLLU) - divp ^= 1; + u32 val; - if (pll->lock) - spin_lock_irqsave(pll->lock, flags); + val = pll_readl_base(pll); - old_base = val = pll_readl_base(pll); val &= ~((divm_mask(pll) << pll->divm_shift) | (divn_mask(pll) << pll->divn_shift) | (divp_mask(pll) << pll->divp_shift)); val |= ((cfg->m << pll->divm_shift) | (cfg->n << pll->divn_shift) | - (divp << pll->divp_shift)); - if (val == old_base) { - if (pll->lock) - spin_unlock_irqrestore(pll->lock, flags); - return 0; + (cfg->p << pll->divp_shift)); + + pll_writel_base(val, pll); +} + +static void _get_pll_mnp(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg) +{ + u32 val; + + val = pll_readl_base(pll); + + cfg->m = (val >> pll->divm_shift) & (divm_mask(pll)); + cfg->n = (val >> pll->divn_shift) & (divn_mask(pll)); + cfg->p = (val >> pll->divp_shift) & (divp_mask(pll)); +} + +static void _update_pll_cpcon(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate) +{ + u32 val; + + val = pll_readl_misc(pll); + + val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT); + val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT; + + if (pll->flags & TEGRA_PLL_SET_LFCON) { + val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT); + if (cfg->n >= PLLDU_LFCON_SET_DIVN) + val |= 1 << PLL_MISC_LFCON_SHIFT; + } else if (pll->flags & TEGRA_PLL_SET_DCCON) { + val &= ~(1 << PLL_MISC_DCCON_SHIFT); + if (rate >= (pll->params->vco_max >> 1)) + val |= 1 << PLL_MISC_DCCON_SHIFT; } + pll_writel_misc(val, pll); +} + +static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, + unsigned long rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + int state, ret = 0; + state = clk_pll_is_enabled(hw); - if (state) { + if (state) _clk_pll_disable(hw); - val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); - } - pll_writel_base(val, pll); - if (pll->flags & TEGRA_PLL_HAS_CPCON) { - val = pll_readl_misc(pll); - val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT); - val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT; - if (pll->flags & TEGRA_PLL_SET_LFCON) { - val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT); - if (cfg->n >= PLLDU_LFCON_SET_DIVN) - val |= 0x1 << PLL_MISC_LFCON_SHIFT; - } else if (pll->flags & TEGRA_PLL_SET_DCCON) { - val &= ~(0x1 << PLL_MISC_DCCON_SHIFT); - if (rate >= (pll->params->vco_max >> 1)) - val |= 0x1 << PLL_MISC_DCCON_SHIFT; - } - pll_writel_misc(val, pll); - } + _update_pll_mnp(pll, cfg); - if (pll->lock) - spin_unlock_irqrestore(pll->lock, flags); + if (pll->flags & TEGRA_PLL_HAS_CPCON) + _update_pll_cpcon(pll, cfg, rate); - if (state) - clk_pll_enable(hw); + if (state) { + _clk_pll_enable(hw); + ret = clk_pll_wait_for_lock(pll); + } - return 0; + return ret; } static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll_freq_table cfg, old_cfg; + unsigned long flags = 0; + int ret = 0; if (pll->flags & TEGRA_PLL_FIXED) { if (rate != pll->fixed_rate) { @@ -387,7 +411,18 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, _calc_rate(hw, &cfg, rate, parent_rate)) return -EINVAL; - return _program_pll(hw, &cfg, rate); + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _get_pll_mnp(pll, &old_cfg); + + if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p) + ret = _program_pll(hw, &cfg, rate); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; } static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, @@ -409,7 +444,7 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, return -EINVAL; output_rate *= cfg.n; - do_div(output_rate, cfg.m * cfg.p); + do_div(output_rate, cfg.m * (1 << cfg.p)); return output_rate; } @@ -418,10 +453,12 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - u32 val = pll_readl_base(pll); - u32 divn = 0, divm = 0, divp = 0; + struct tegra_clk_pll_freq_table cfg; + u32 val; u64 rate = parent_rate; + val = pll_readl_base(pll); + if (val & PLL_BASE_BYPASS) return parent_rate; @@ -435,16 +472,16 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, return pll->fixed_rate; } - divp = (val >> pll->divp_shift) & (divp_mask(pll)); + _get_pll_mnp(pll, &cfg); + if (pll->flags & TEGRA_PLLU) - divp ^= 1; + cfg.p ^= 1; - divn = (val >> pll->divn_shift) & (divn_mask(pll)); - divm = (val >> pll->divm_shift) & (divm_mask(pll)); - divm *= (1 << divp); + cfg.m *= 1 << cfg.p; + + rate *= cfg.n; + do_div(rate, cfg.m); - rate *= divn; - do_div(rate, divm); return rate; } @@ -538,8 +575,8 @@ static int clk_plle_enable(struct clk_hw *hw) val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE); pll_writel_base(val, pll); - clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->misc_reg, - pll->params->lock_bit_idx); + clk_pll_wait_for_lock(pll); + return 0; } @@ -577,28 +614,17 @@ const struct clk_ops tegra_clk_plle_ops = { .enable = clk_plle_enable, }; -static struct clk *_tegra_clk_register_pll(const char *name, - const char *parent_name, void __iomem *clk_base, - void __iomem *pmc, unsigned long flags, - unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, - struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock, - const struct clk_ops *ops) +static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, + void __iomem *pmc, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) { struct tegra_clk_pll *pll; - struct clk *clk; - struct clk_init_data init; pll = kzalloc(sizeof(*pll), GFP_KERNEL); if (!pll) return ERR_PTR(-ENOMEM); - init.name = name; - init.ops = ops; - init.flags = flags; - init.parent_names = (parent_name ? &parent_name : NULL); - init.num_parents = (parent_name ? 1 : 0); - pll->clk_base = clk_base; pll->pmc = pmc; @@ -615,34 +641,68 @@ static struct clk *_tegra_clk_register_pll(const char *name, pll->divm_shift = PLL_BASE_DIVM_SHIFT; pll->divm_width = PLL_BASE_DIVM_WIDTH; + return pll; +} + +static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll, + const char *name, const char *parent_name, unsigned long flags, + const struct clk_ops *ops) +{ + struct clk_init_data init; + + init.name = name; + init.ops = ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + /* Data in .init is copied by clk_register(), so stack variable OK */ pll->hw.init = &init; - clk = clk_register(NULL, &pll->hw); - if (IS_ERR(clk)) - kfree(pll); - - return clk; + return clk_register(NULL, &pll->hw); } struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) { - return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, - flags, fixed_rate, pll_params, pll_flags, freq_table, - lock, &tegra_clk_pll_ops); + struct tegra_clk_pll *pll; + struct clk *clk; + + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pll_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; } struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) { - return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, - flags, fixed_rate, pll_params, pll_flags, freq_table, - lock, &tegra_clk_plle_ops); + struct tegra_clk_pll *pll; + struct clk *clk; + pll_flags |= TEGRA_PLL_LOCK_MISC; + + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_plle_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; } diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index a15fb28..c2a1c4c 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -248,125 +248,125 @@ static struct clk *clks[clk_max]; static struct clk_onecell_data clk_data; static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { - { 12000000, 600000000, 600, 12, 1, 8 }, - { 13000000, 600000000, 600, 13, 1, 8 }, - { 19200000, 600000000, 500, 16, 1, 6 }, - { 26000000, 600000000, 600, 26, 1, 8 }, + { 12000000, 600000000, 600, 12, 0, 8 }, + { 13000000, 600000000, 600, 13, 0, 8 }, + { 19200000, 600000000, 500, 16, 0, 6 }, + { 26000000, 600000000, 600, 26, 0, 8 }, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { - { 12000000, 666000000, 666, 12, 1, 8}, - { 13000000, 666000000, 666, 13, 1, 8}, - { 19200000, 666000000, 555, 16, 1, 8}, - { 26000000, 666000000, 666, 26, 1, 8}, - { 12000000, 600000000, 600, 12, 1, 8}, - { 13000000, 600000000, 600, 13, 1, 8}, - { 19200000, 600000000, 375, 12, 1, 6}, - { 26000000, 600000000, 600, 26, 1, 8}, + { 12000000, 666000000, 666, 12, 0, 8}, + { 13000000, 666000000, 666, 13, 0, 8}, + { 19200000, 666000000, 555, 16, 0, 8}, + { 26000000, 666000000, 666, 26, 0, 8}, + { 12000000, 600000000, 600, 12, 0, 8}, + { 13000000, 600000000, 600, 13, 0, 8}, + { 19200000, 600000000, 375, 12, 0, 6}, + { 26000000, 600000000, 600, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { - { 12000000, 216000000, 432, 12, 2, 8}, - { 13000000, 216000000, 432, 13, 2, 8}, - { 19200000, 216000000, 90, 4, 2, 1}, - { 26000000, 216000000, 432, 26, 2, 8}, - { 12000000, 432000000, 432, 12, 1, 8}, - { 13000000, 432000000, 432, 13, 1, 8}, - { 19200000, 432000000, 90, 4, 1, 1}, - { 26000000, 432000000, 432, 26, 1, 8}, + { 12000000, 216000000, 432, 12, 1, 8}, + { 13000000, 216000000, 432, 13, 1, 8}, + { 19200000, 216000000, 90, 4, 1, 1}, + { 26000000, 216000000, 432, 26, 1, 8}, + { 12000000, 432000000, 432, 12, 0, 8}, + { 13000000, 432000000, 432, 13, 0, 8}, + { 19200000, 432000000, 90, 4, 0, 1}, + { 26000000, 432000000, 432, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { - { 28800000, 56448000, 49, 25, 1, 1}, - { 28800000, 73728000, 64, 25, 1, 1}, - { 28800000, 24000000, 5, 6, 1, 1}, + { 28800000, 56448000, 49, 25, 0, 1}, + { 28800000, 73728000, 64, 25, 0, 1}, + { 28800000, 24000000, 5, 6, 0, 1}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { - { 12000000, 216000000, 216, 12, 1, 4}, - { 13000000, 216000000, 216, 13, 1, 4}, - { 19200000, 216000000, 135, 12, 1, 3}, - { 26000000, 216000000, 216, 26, 1, 4}, + { 12000000, 216000000, 216, 12, 0, 4}, + { 13000000, 216000000, 216, 13, 0, 4}, + { 19200000, 216000000, 135, 12, 0, 3}, + { 26000000, 216000000, 216, 26, 0, 4}, - { 12000000, 594000000, 594, 12, 1, 8}, - { 13000000, 594000000, 594, 13, 1, 8}, - { 19200000, 594000000, 495, 16, 1, 8}, - { 26000000, 594000000, 594, 26, 1, 8}, + { 12000000, 594000000, 594, 12, 0, 8}, + { 13000000, 594000000, 594, 13, 0, 8}, + { 19200000, 594000000, 495, 16, 0, 8}, + { 26000000, 594000000, 594, 26, 0, 8}, - { 12000000, 1000000000, 1000, 12, 1, 12}, - { 13000000, 1000000000, 1000, 13, 1, 12}, - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 12}, + { 12000000, 1000000000, 1000, 12, 0, 12}, + { 13000000, 1000000000, 1000, 13, 0, 12}, + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { - { 12000000, 480000000, 960, 12, 2, 0}, - { 13000000, 480000000, 960, 13, 2, 0}, - { 19200000, 480000000, 200, 4, 2, 0}, - { 26000000, 480000000, 960, 26, 2, 0}, + { 12000000, 480000000, 960, 12, 0, 0}, + { 13000000, 480000000, 960, 13, 0, 0}, + { 19200000, 480000000, 200, 4, 0, 0}, + { 26000000, 480000000, 960, 26, 0, 0}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { /* 1 GHz */ - { 12000000, 1000000000, 1000, 12, 1, 12}, - { 13000000, 1000000000, 1000, 13, 1, 12}, - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 12}, + { 12000000, 1000000000, 1000, 12, 0, 12}, + { 13000000, 1000000000, 1000, 13, 0, 12}, + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 12}, /* 912 MHz */ - { 12000000, 912000000, 912, 12, 1, 12}, - { 13000000, 912000000, 912, 13, 1, 12}, - { 19200000, 912000000, 760, 16, 1, 8}, - { 26000000, 912000000, 912, 26, 1, 12}, + { 12000000, 912000000, 912, 12, 0, 12}, + { 13000000, 912000000, 912, 13, 0, 12}, + { 19200000, 912000000, 760, 16, 0, 8}, + { 26000000, 912000000, 912, 26, 0, 12}, /* 816 MHz */ - { 12000000, 816000000, 816, 12, 1, 12}, - { 13000000, 816000000, 816, 13, 1, 12}, - { 19200000, 816000000, 680, 16, 1, 8}, - { 26000000, 816000000, 816, 26, 1, 12}, + { 12000000, 816000000, 816, 12, 0, 12}, + { 13000000, 816000000, 816, 13, 0, 12}, + { 19200000, 816000000, 680, 16, 0, 8}, + { 26000000, 816000000, 816, 26, 0, 12}, /* 760 MHz */ - { 12000000, 760000000, 760, 12, 1, 12}, - { 13000000, 760000000, 760, 13, 1, 12}, - { 19200000, 760000000, 950, 24, 1, 8}, - { 26000000, 760000000, 760, 26, 1, 12}, + { 12000000, 760000000, 760, 12, 0, 12}, + { 13000000, 760000000, 760, 13, 0, 12}, + { 19200000, 760000000, 950, 24, 0, 8}, + { 26000000, 760000000, 760, 26, 0, 12}, /* 750 MHz */ - { 12000000, 750000000, 750, 12, 1, 12}, - { 13000000, 750000000, 750, 13, 1, 12}, - { 19200000, 750000000, 625, 16, 1, 8}, - { 26000000, 750000000, 750, 26, 1, 12}, + { 12000000, 750000000, 750, 12, 0, 12}, + { 13000000, 750000000, 750, 13, 0, 12}, + { 19200000, 750000000, 625, 16, 0, 8}, + { 26000000, 750000000, 750, 26, 0, 12}, /* 608 MHz */ - { 12000000, 608000000, 608, 12, 1, 12}, - { 13000000, 608000000, 608, 13, 1, 12}, - { 19200000, 608000000, 380, 12, 1, 8}, - { 26000000, 608000000, 608, 26, 1, 12}, + { 12000000, 608000000, 608, 12, 0, 12}, + { 13000000, 608000000, 608, 13, 0, 12}, + { 19200000, 608000000, 380, 12, 0, 8}, + { 26000000, 608000000, 608, 26, 0, 12}, /* 456 MHz */ - { 12000000, 456000000, 456, 12, 1, 12}, - { 13000000, 456000000, 456, 13, 1, 12}, - { 19200000, 456000000, 380, 16, 1, 8}, - { 26000000, 456000000, 456, 26, 1, 12}, + { 12000000, 456000000, 456, 12, 0, 12}, + { 13000000, 456000000, 456, 13, 0, 12}, + { 19200000, 456000000, 380, 16, 0, 8}, + { 26000000, 456000000, 456, 26, 0, 12}, /* 312 MHz */ - { 12000000, 312000000, 312, 12, 1, 12}, - { 13000000, 312000000, 312, 13, 1, 12}, - { 19200000, 312000000, 260, 16, 1, 8}, - { 26000000, 312000000, 312, 26, 1, 12}, + { 12000000, 312000000, 312, 12, 0, 12}, + { 13000000, 312000000, 312, 13, 0, 12}, + { 19200000, 312000000, 260, 16, 0, 8}, + { 26000000, 312000000, 312, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { - { 12000000, 100000000, 200, 24, 1, 0 }, + { 12000000, 100000000, 200, 24, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, }; diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index e0ee93c..02609d1 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -374,164 +374,164 @@ static const struct utmi_clk_param utmi_parameters[] = { }; static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { - { 12000000, 1040000000, 520, 6, 1, 8}, - { 13000000, 1040000000, 480, 6, 1, 8}, - { 16800000, 1040000000, 495, 8, 1, 8}, /* actual: 1039.5 MHz */ - { 19200000, 1040000000, 325, 6, 1, 6}, - { 26000000, 1040000000, 520, 13, 1, 8}, - - { 12000000, 832000000, 416, 6, 1, 8}, - { 13000000, 832000000, 832, 13, 1, 8}, - { 16800000, 832000000, 396, 8, 1, 8}, /* actual: 831.6 MHz */ - { 19200000, 832000000, 260, 6, 1, 8}, - { 26000000, 832000000, 416, 13, 1, 8}, - - { 12000000, 624000000, 624, 12, 1, 8}, - { 13000000, 624000000, 624, 13, 1, 8}, - { 16800000, 600000000, 520, 14, 1, 8}, - { 19200000, 624000000, 520, 16, 1, 8}, - { 26000000, 624000000, 624, 26, 1, 8}, - - { 12000000, 600000000, 600, 12, 1, 8}, - { 13000000, 600000000, 600, 13, 1, 8}, - { 16800000, 600000000, 500, 14, 1, 8}, - { 19200000, 600000000, 375, 12, 1, 6}, - { 26000000, 600000000, 600, 26, 1, 8}, - - { 12000000, 520000000, 520, 12, 1, 8}, - { 13000000, 520000000, 520, 13, 1, 8}, - { 16800000, 520000000, 495, 16, 1, 8}, /* actual: 519.75 MHz */ - { 19200000, 520000000, 325, 12, 1, 6}, - { 26000000, 520000000, 520, 26, 1, 8}, - - { 12000000, 416000000, 416, 12, 1, 8}, - { 13000000, 416000000, 416, 13, 1, 8}, - { 16800000, 416000000, 396, 16, 1, 8}, /* actual: 415.8 MHz */ - { 19200000, 416000000, 260, 12, 1, 6}, - { 26000000, 416000000, 416, 26, 1, 8}, + { 12000000, 1040000000, 520, 6, 0, 8}, + { 13000000, 1040000000, 480, 6, 0, 8}, + { 16800000, 1040000000, 495, 8, 0, 8}, /* actual: 1039.5 MHz */ + { 19200000, 1040000000, 325, 6, 0, 6}, + { 26000000, 1040000000, 520, 13, 0, 8}, + + { 12000000, 832000000, 416, 6, 0, 8}, + { 13000000, 832000000, 832, 13, 0, 8}, + { 16800000, 832000000, 396, 8, 0, 8}, /* actual: 831.6 MHz */ + { 19200000, 832000000, 260, 6, 0, 8}, + { 26000000, 832000000, 416, 13, 0, 8}, + + { 12000000, 624000000, 624, 12, 0, 8}, + { 13000000, 624000000, 624, 13, 0, 8}, + { 16800000, 600000000, 520, 14, 0, 8}, + { 19200000, 624000000, 520, 16, 0, 8}, + { 26000000, 624000000, 624, 26, 0, 8}, + + { 12000000, 600000000, 600, 12, 0, 8}, + { 13000000, 600000000, 600, 13, 0, 8}, + { 16800000, 600000000, 500, 14, 0, 8}, + { 19200000, 600000000, 375, 12, 0, 6}, + { 26000000, 600000000, 600, 26, 0, 8}, + + { 12000000, 520000000, 520, 12, 0, 8}, + { 13000000, 520000000, 520, 13, 0, 8}, + { 16800000, 520000000, 495, 16, 0, 8}, /* actual: 519.75 MHz */ + { 19200000, 520000000, 325, 12, 0, 6}, + { 26000000, 520000000, 520, 26, 0, 8}, + + { 12000000, 416000000, 416, 12, 0, 8}, + { 13000000, 416000000, 416, 13, 0, 8}, + { 16800000, 416000000, 396, 16, 0, 8}, /* actual: 415.8 MHz */ + { 19200000, 416000000, 260, 12, 0, 6}, + { 26000000, 416000000, 416, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { - { 12000000, 666000000, 666, 12, 1, 8}, - { 13000000, 666000000, 666, 13, 1, 8}, - { 16800000, 666000000, 555, 14, 1, 8}, - { 19200000, 666000000, 555, 16, 1, 8}, - { 26000000, 666000000, 666, 26, 1, 8}, - { 12000000, 600000000, 600, 12, 1, 8}, - { 13000000, 600000000, 600, 13, 1, 8}, - { 16800000, 600000000, 500, 14, 1, 8}, - { 19200000, 600000000, 375, 12, 1, 6}, - { 26000000, 600000000, 600, 26, 1, 8}, + { 12000000, 666000000, 666, 12, 0, 8}, + { 13000000, 666000000, 666, 13, 0, 8}, + { 16800000, 666000000, 555, 14, 0, 8}, + { 19200000, 666000000, 555, 16, 0, 8}, + { 26000000, 666000000, 666, 26, 0, 8}, + { 12000000, 600000000, 600, 12, 0, 8}, + { 13000000, 600000000, 600, 13, 0, 8}, + { 16800000, 600000000, 500, 14, 0, 8}, + { 19200000, 600000000, 375, 12, 0, 6}, + { 26000000, 600000000, 600, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { - { 12000000, 216000000, 432, 12, 2, 8}, - { 13000000, 216000000, 432, 13, 2, 8}, - { 16800000, 216000000, 360, 14, 2, 8}, - { 19200000, 216000000, 360, 16, 2, 8}, - { 26000000, 216000000, 432, 26, 2, 8}, + { 12000000, 216000000, 432, 12, 1, 8}, + { 13000000, 216000000, 432, 13, 1, 8}, + { 16800000, 216000000, 360, 14, 1, 8}, + { 19200000, 216000000, 360, 16, 1, 8}, + { 26000000, 216000000, 432, 26, 1, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { - { 9600000, 564480000, 294, 5, 1, 4}, - { 9600000, 552960000, 288, 5, 1, 4}, - { 9600000, 24000000, 5, 2, 1, 1}, + { 9600000, 564480000, 294, 5, 0, 4}, + { 9600000, 552960000, 288, 5, 0, 4}, + { 9600000, 24000000, 5, 2, 0, 1}, - { 28800000, 56448000, 49, 25, 1, 1}, - { 28800000, 73728000, 64, 25, 1, 1}, - { 28800000, 24000000, 5, 6, 1, 1}, + { 28800000, 56448000, 49, 25, 0, 1}, + { 28800000, 73728000, 64, 25, 0, 1}, + { 28800000, 24000000, 5, 6, 0, 1}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { - { 12000000, 216000000, 216, 12, 1, 4}, - { 13000000, 216000000, 216, 13, 1, 4}, - { 16800000, 216000000, 180, 14, 1, 4}, - { 19200000, 216000000, 180, 16, 1, 4}, - { 26000000, 216000000, 216, 26, 1, 4}, - - { 12000000, 594000000, 594, 12, 1, 8}, - { 13000000, 594000000, 594, 13, 1, 8}, - { 16800000, 594000000, 495, 14, 1, 8}, - { 19200000, 594000000, 495, 16, 1, 8}, - { 26000000, 594000000, 594, 26, 1, 8}, - - { 12000000, 1000000000, 1000, 12, 1, 12}, - { 13000000, 1000000000, 1000, 13, 1, 12}, - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 12}, + { 12000000, 216000000, 216, 12, 0, 4}, + { 13000000, 216000000, 216, 13, 0, 4}, + { 16800000, 216000000, 180, 14, 0, 4}, + { 19200000, 216000000, 180, 16, 0, 4}, + { 26000000, 216000000, 216, 26, 0, 4}, + + { 12000000, 594000000, 594, 12, 0, 8}, + { 13000000, 594000000, 594, 13, 0, 8}, + { 16800000, 594000000, 495, 14, 0, 8}, + { 19200000, 594000000, 495, 16, 0, 8}, + { 26000000, 594000000, 594, 26, 0, 8}, + + { 12000000, 1000000000, 1000, 12, 0, 12}, + { 13000000, 1000000000, 1000, 13, 0, 12}, + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { - { 12000000, 480000000, 960, 12, 2, 12}, - { 13000000, 480000000, 960, 13, 2, 12}, - { 16800000, 480000000, 400, 7, 2, 5}, - { 19200000, 480000000, 200, 4, 2, 3}, - { 26000000, 480000000, 960, 26, 2, 12}, + { 12000000, 480000000, 960, 12, 0, 12}, + { 13000000, 480000000, 960, 13, 0, 12}, + { 16800000, 480000000, 400, 7, 0, 5}, + { 19200000, 480000000, 200, 4, 0, 3}, + { 26000000, 480000000, 960, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { /* 1.7 GHz */ - { 12000000, 1700000000, 850, 6, 1, 8}, - { 13000000, 1700000000, 915, 7, 1, 8}, /* actual: 1699.2 MHz */ - { 16800000, 1700000000, 708, 7, 1, 8}, /* actual: 1699.2 MHz */ - { 19200000, 1700000000, 885, 10, 1, 8}, /* actual: 1699.2 MHz */ - { 26000000, 1700000000, 850, 13, 1, 8}, + { 12000000, 1700000000, 850, 6, 0, 8}, + { 13000000, 1700000000, 915, 7, 0, 8}, /* actual: 1699.2 MHz */ + { 16800000, 1700000000, 708, 7, 0, 8}, /* actual: 1699.2 MHz */ + { 19200000, 1700000000, 885, 10, 0, 8}, /* actual: 1699.2 MHz */ + { 26000000, 1700000000, 850, 13, 0, 8}, /* 1.6 GHz */ - { 12000000, 1600000000, 800, 6, 1, 8}, - { 13000000, 1600000000, 738, 6, 1, 8}, /* actual: 1599.0 MHz */ - { 16800000, 1600000000, 857, 9, 1, 8}, /* actual: 1599.7 MHz */ - { 19200000, 1600000000, 500, 6, 1, 8}, - { 26000000, 1600000000, 800, 13, 1, 8}, + { 12000000, 1600000000, 800, 6, 0, 8}, + { 13000000, 1600000000, 738, 6, 0, 8}, /* actual: 1599.0 MHz */ + { 16800000, 1600000000, 857, 9, 0, 8}, /* actual: 1599.7 MHz */ + { 19200000, 1600000000, 500, 6, 0, 8}, + { 26000000, 1600000000, 800, 13, 0, 8}, /* 1.5 GHz */ - { 12000000, 1500000000, 750, 6, 1, 8}, - { 13000000, 1500000000, 923, 8, 1, 8}, /* actual: 1499.8 MHz */ - { 16800000, 1500000000, 625, 7, 1, 8}, - { 19200000, 1500000000, 625, 8, 1, 8}, - { 26000000, 1500000000, 750, 13, 1, 8}, + { 12000000, 1500000000, 750, 6, 0, 8}, + { 13000000, 1500000000, 923, 8, 0, 8}, /* actual: 1499.8 MHz */ + { 16800000, 1500000000, 625, 7, 0, 8}, + { 19200000, 1500000000, 625, 8, 0, 8}, + { 26000000, 1500000000, 750, 13, 0, 8}, /* 1.4 GHz */ - { 12000000, 1400000000, 700, 6, 1, 8}, - { 13000000, 1400000000, 969, 9, 1, 8}, /* actual: 1399.7 MHz */ - { 16800000, 1400000000, 1000, 12, 1, 8}, - { 19200000, 1400000000, 875, 12, 1, 8}, - { 26000000, 1400000000, 700, 13, 1, 8}, + { 12000000, 1400000000, 700, 6, 0, 8}, + { 13000000, 1400000000, 969, 9, 0, 8}, /* actual: 1399.7 MHz */ + { 16800000, 1400000000, 1000, 12, 0, 8}, + { 19200000, 1400000000, 875, 12, 0, 8}, + { 26000000, 1400000000, 700, 13, 0, 8}, /* 1.3 GHz */ - { 12000000, 1300000000, 975, 9, 1, 8}, - { 13000000, 1300000000, 1000, 10, 1, 8}, - { 16800000, 1300000000, 928, 12, 1, 8}, /* actual: 1299.2 MHz */ - { 19200000, 1300000000, 812, 12, 1, 8}, /* actual: 1299.2 MHz */ - { 26000000, 1300000000, 650, 13, 1, 8}, + { 12000000, 1300000000, 975, 9, 0, 8}, + { 13000000, 1300000000, 1000, 10, 0, 8}, + { 16800000, 1300000000, 928, 12, 0, 8}, /* actual: 1299.2 MHz */ + { 19200000, 1300000000, 812, 12, 0, 8}, /* actual: 1299.2 MHz */ + { 26000000, 1300000000, 650, 13, 0, 8}, /* 1.2 GHz */ - { 12000000, 1200000000, 1000, 10, 1, 8}, - { 13000000, 1200000000, 923, 10, 1, 8}, /* actual: 1199.9 MHz */ - { 16800000, 1200000000, 1000, 14, 1, 8}, - { 19200000, 1200000000, 1000, 16, 1, 8}, - { 26000000, 1200000000, 600, 13, 1, 8}, + { 12000000, 1200000000, 1000, 10, 0, 8}, + { 13000000, 1200000000, 923, 10, 0, 8}, /* actual: 1199.9 MHz */ + { 16800000, 1200000000, 1000, 14, 0, 8}, + { 19200000, 1200000000, 1000, 16, 0, 8}, + { 26000000, 1200000000, 600, 13, 0, 8}, /* 1.1 GHz */ - { 12000000, 1100000000, 825, 9, 1, 8}, - { 13000000, 1100000000, 846, 10, 1, 8}, /* actual: 1099.8 MHz */ - { 16800000, 1100000000, 982, 15, 1, 8}, /* actual: 1099.8 MHz */ - { 19200000, 1100000000, 859, 15, 1, 8}, /* actual: 1099.5 MHz */ - { 26000000, 1100000000, 550, 13, 1, 8}, + { 12000000, 1100000000, 825, 9, 0, 8}, + { 13000000, 1100000000, 846, 10, 0, 8}, /* actual: 1099.8 MHz */ + { 16800000, 1100000000, 982, 15, 0, 8}, /* actual: 1099.8 MHz */ + { 19200000, 1100000000, 859, 15, 0, 8}, /* actual: 1099.5 MHz */ + { 26000000, 1100000000, 550, 13, 0, 8}, /* 1 GHz */ - { 12000000, 1000000000, 1000, 12, 1, 8}, - { 13000000, 1000000000, 1000, 13, 1, 8}, - { 16800000, 1000000000, 833, 14, 1, 8}, /* actual: 999.6 MHz */ - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 8}, + { 12000000, 1000000000, 1000, 12, 0, 8}, + { 13000000, 1000000000, 1000, 13, 0, 8}, + { 16800000, 1000000000, 833, 14, 0, 8}, /* actual: 999.6 MHz */ + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 3c566e2..b9691dd 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -182,12 +182,14 @@ struct tegra_clk_pll_params { * TEGRA_PLL_FIXED - We are not supposed to change output frequency * of some plls. * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling. + * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the + * base register. */ struct tegra_clk_pll { struct clk_hw hw; void __iomem *clk_base; void __iomem *pmc; - u8 flags; + u32 flags; unsigned long fixed_rate; spinlock_t *lock; u8 divn_shift; @@ -210,18 +212,19 @@ struct tegra_clk_pll { #define TEGRA_PLLM BIT(5) #define TEGRA_PLL_FIXED BIT(6) #define TEGRA_PLLE_CONFIGURE BIT(7) +#define TEGRA_PLL_LOCK_MISC BIT(8) extern const struct clk_ops tegra_clk_pll_ops; extern const struct clk_ops tegra_clk_plle_ops; struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); /** -- cgit v1.1 From dd93587be8dc8acf23a0d8a23efc74a91d8f0dfe Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:37 +0300 Subject: clk: tegra: Add TEGRA_PLL_BYPASS flag Not all PLLs in Tegra114 have a bypass bit. Adapt the common code to only use this bit when available. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 12 ++++++++---- drivers/clk/tegra/clk.h | 2 ++ 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 3feefb1..4ee6d03 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -171,7 +171,8 @@ static void _clk_pll_enable(struct clk_hw *hw) clk_pll_enable_lock(pll); val = pll_readl_base(pll); - val &= ~PLL_BASE_BYPASS; + if (pll->flags & TEGRA_PLL_BYPASS) + val &= ~PLL_BASE_BYPASS; val |= PLL_BASE_ENABLE; pll_writel_base(val, pll); @@ -188,7 +189,9 @@ static void _clk_pll_disable(struct clk_hw *hw) u32 val; val = pll_readl_base(pll); - val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); + if (pll->flags & TEGRA_PLL_BYPASS) + val &= ~PLL_BASE_BYPASS; + val &= ~PLL_BASE_ENABLE; pll_writel_base(val, pll); if (pll->flags & TEGRA_PLLM) { @@ -459,7 +462,7 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, val = pll_readl_base(pll); - if (val & PLL_BASE_BYPASS) + if ((pll->flags & TEGRA_PLL_BYPASS) && (val & PLL_BASE_BYPASS)) return parent_rate; if ((pll->flags & TEGRA_PLL_FIXED) && !(val & PLL_BASE_OVERRIDE)) { @@ -671,6 +674,7 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, struct tegra_clk_pll *pll; struct clk *clk; + pll_flags |= TEGRA_PLL_BYPASS; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) @@ -692,8 +696,8 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, { struct tegra_clk_pll *pll; struct clk *clk; - pll_flags |= TEGRA_PLL_LOCK_MISC; + pll_flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index b9691dd..fff520a 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -184,6 +184,7 @@ struct tegra_clk_pll_params { * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling. * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the * base register. + * TEGRA_PLL_BYPASS - PLL has bypass bit */ struct tegra_clk_pll { struct clk_hw hw; @@ -213,6 +214,7 @@ struct tegra_clk_pll { #define TEGRA_PLL_FIXED BIT(6) #define TEGRA_PLLE_CONFIGURE BIT(7) #define TEGRA_PLL_LOCK_MISC BIT(8) +#define TEGRA_PLL_BYPASS BIT(9) extern const struct clk_ops tegra_clk_pll_ops; extern const struct clk_ops tegra_clk_plle_ops; -- cgit v1.1 From 7ba28813b41120dd67329fd04dc732ea7fef05a0 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:38 +0300 Subject: clk: tegra: introduce TEGRA_PLL_HAS_LOCK_ENABLE Tegra114 PLLC2 and PLLC3 don't have a lock enable bit. The lock bits are always functional. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 5 +++++ drivers/clk/tegra/clk.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 4ee6d03..eaab060 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -108,6 +108,9 @@ static void clk_pll_enable_lock(struct tegra_clk_pll *pll) if (!(pll->flags & TEGRA_PLL_USE_LOCK)) return; + if (!(pll->flags & TEGRA_PLL_HAS_LOCK_ENABLE)) + return; + val = pll_readl_misc(pll); val |= BIT(pll->params->lock_enable_bit_idx); pll_writel_misc(val, pll); @@ -675,6 +678,7 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, struct clk *clk; pll_flags |= TEGRA_PLL_BYPASS; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) @@ -698,6 +702,7 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, struct clk *clk; pll_flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index fff520a..17ddb22 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -185,6 +185,7 @@ struct tegra_clk_pll_params { * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the * base register. * TEGRA_PLL_BYPASS - PLL has bypass bit + * TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring */ struct tegra_clk_pll { struct clk_hw hw; @@ -215,6 +216,7 @@ struct tegra_clk_pll { #define TEGRA_PLLE_CONFIGURE BIT(7) #define TEGRA_PLL_LOCK_MISC BIT(8) #define TEGRA_PLL_BYPASS BIT(9) +#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10) extern const struct clk_ops tegra_clk_pll_ops; extern const struct clk_ops tegra_clk_plle_ops; -- cgit v1.1 From 0b6525acd13f2d33cd3be86d0dbd2ddd1ffeda8f Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:39 +0300 Subject: clk: tegra: Add PLL post divider table Some PLLs in Tegra114 don't use a power of 2 mapping for the post divider. Introduce a table based approach and switch PLLU to it. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 38 ++++++++++++++++++++++++++++++++------ drivers/clk/tegra/clk-tegra20.c | 7 +++++++ drivers/clk/tegra/clk-tegra30.c | 7 +++++++ drivers/clk/tegra/clk.h | 13 +++++++++++++ 4 files changed, 59 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index eaab060..ccb367e 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -266,6 +266,7 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; unsigned long cfreq; u32 p_div = 0; @@ -299,7 +300,6 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, cfg->output_rate <<= 1) p_div++; - cfg->p = p_div; cfg->m = parent_rate / cfreq; cfg->n = cfg->output_rate / cfreq; cfg->cpcon = OUT_OF_TABLE_CPCON; @@ -312,8 +312,19 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, return -EINVAL; } - if (pll->flags & TEGRA_PLLU) - cfg->p ^= 1; + if (p_tohw) { + p_div = 1 << p_div; + while (p_tohw->pdiv) { + if (p_div <= p_tohw->pdiv) { + cfg->p = p_tohw->hw_val; + break; + } + p_tohw++; + } + if (!p_tohw->pdiv) + return -EINVAL; + } else + cfg->p = p_div; return 0; } @@ -460,8 +471,10 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; u32 val; u64 rate = parent_rate; + int pdiv; val = pll_readl_base(pll); @@ -480,10 +493,23 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, _get_pll_mnp(pll, &cfg); - if (pll->flags & TEGRA_PLLU) - cfg.p ^= 1; + if (p_tohw) { + while (p_tohw->pdiv) { + if (cfg.p == p_tohw->hw_val) { + pdiv = p_tohw->pdiv; + break; + } + p_tohw++; + } + + if (!p_tohw->pdiv) { + WARN_ON(1); + pdiv = 1; + } + } else + pdiv = 1 << cfg.p; - cfg.m *= 1 << cfg.p; + cfg.m *= pdiv; rate *= cfg.n; do_div(rate, cfg.m); diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index c2a1c4c..f215bf1 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -441,6 +441,12 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_delay = 1000, }; +static struct pdiv_map pllu_p[] = { + { .pdiv = 1, .hw_val = 1 }, + { .pdiv = 2, .hw_val = 0 }, + { .pdiv = 0, .hw_val = 0 }, +}; + static struct tegra_clk_pll_params pll_u_params = { .input_min = 2000000, .input_max = 40000000, @@ -453,6 +459,7 @@ static struct tegra_clk_pll_params pll_u_params = { .lock_bit_idx = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .pdiv_tohw = pllu_p, }; static struct tegra_clk_pll_params pll_x_params = { diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 02609d1..fe768fe 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -467,6 +467,12 @@ static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { { 0, 0, 0, 0, 0, 0 }, }; +static struct pdiv_map pllu_p[] = { + { .pdiv = 1, .hw_val = 1 }, + { .pdiv = 2, .hw_val = 0 }, + { .pdiv = 0, .hw_val = 0 }, +}; + static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { { 12000000, 480000000, 960, 12, 0, 12}, { 13000000, 480000000, 960, 13, 0, 12}, @@ -640,6 +646,7 @@ static struct tegra_clk_pll_params pll_u_params = { .lock_bit_idx = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .pdiv_tohw = pllu_p, }; static struct tegra_clk_pll_params pll_x_params = { diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 17ddb22..925da45 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -117,6 +117,17 @@ struct tegra_clk_pll_freq_table { }; /** + * struct pdiv_map - map post divider to hw value + * + * @pdiv: post divider + * @hw_val: value to be written to the PLL hw + */ +struct pdiv_map { + u8 pdiv; + u8 hw_val; +}; + +/** * struct clk_pll_params - PLL parameters * * @input_min: Minimum input frequency @@ -146,6 +157,8 @@ struct tegra_clk_pll_params { u32 lock_bit_idx; u32 lock_enable_bit_idx; int lock_delay; + int max_p; + struct pdiv_map *pdiv_tohw; }; /** -- cgit v1.1 From 3e72771e210348fbd7ff0ea1b9e14cd88380c05b Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:40 +0300 Subject: clk: tegra: move from a lock bit idx to a lock mask PLLC2 and PLLC3 on Tegra114 have separate phaselock and frequencylock bits. So switch to a lock mask to be able to test both at the same time. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 6 +++--- drivers/clk/tegra/clk-tegra20.c | 20 ++++++++++---------- drivers/clk/tegra/clk-tegra30.c | 22 +++++++++++----------- drivers/clk/tegra/clk.h | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index ccb367e..0b96352 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -119,7 +119,7 @@ static void clk_pll_enable_lock(struct tegra_clk_pll *pll) static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll) { int i; - u32 val, lock_bit; + u32 val, lock_mask; void __iomem *lock_addr; if (!(pll->flags & TEGRA_PLL_USE_LOCK)) { @@ -133,11 +133,11 @@ static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll) else lock_addr += pll->params->base_reg; - lock_bit = BIT(pll->params->lock_bit_idx); + lock_mask = pll->params->lock_mask; for (i = 0; i < pll->params->lock_delay; i++) { val = readl_relaxed(lock_addr); - if (val & lock_bit) { + if ((val & lock_mask) == lock_mask) { udelay(PLL_POST_LOCK_DELAY); return 0; } diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index f215bf1..5c7b58b 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -86,8 +86,8 @@ #define PLLE_BASE 0xe8 #define PLLE_MISC 0xec -#define PLL_BASE_LOCK 27 -#define PLLE_MISC_LOCK 11 +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) #define PLL_MISC_LOCK_ENABLE 18 #define PLLDU_MISC_LOCK_ENABLE 22 @@ -380,7 +380,7 @@ static struct tegra_clk_pll_params pll_c_params = { .vco_max = 1400000000, .base_reg = PLLC_BASE, .misc_reg = PLLC_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -394,7 +394,7 @@ static struct tegra_clk_pll_params pll_m_params = { .vco_max = 1200000000, .base_reg = PLLM_BASE, .misc_reg = PLLM_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -408,7 +408,7 @@ static struct tegra_clk_pll_params pll_p_params = { .vco_max = 1400000000, .base_reg = PLLP_BASE, .misc_reg = PLLP_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -422,7 +422,7 @@ static struct tegra_clk_pll_params pll_a_params = { .vco_max = 1400000000, .base_reg = PLLA_BASE, .misc_reg = PLLA_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -436,7 +436,7 @@ static struct tegra_clk_pll_params pll_d_params = { .vco_max = 1000000000, .base_reg = PLLD_BASE, .misc_reg = PLLD_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, }; @@ -456,7 +456,7 @@ static struct tegra_clk_pll_params pll_u_params = { .vco_max = 960000000, .base_reg = PLLU_BASE, .misc_reg = PLLU_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, .pdiv_tohw = pllu_p, @@ -471,7 +471,7 @@ static struct tegra_clk_pll_params pll_x_params = { .vco_max = 1200000000, .base_reg = PLLX_BASE, .misc_reg = PLLX_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -485,7 +485,7 @@ static struct tegra_clk_pll_params pll_e_params = { .vco_max = 0, .base_reg = PLLE_BASE, .misc_reg = PLLE_MISC, - .lock_bit_idx = PLLE_MISC_LOCK, + .lock_mask = PLLE_MISC_LOCK, .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, .lock_delay = 0, }; diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index fe768fe..735f964 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -116,8 +116,8 @@ #define PLLDU_MISC_LOCK_ENABLE 22 #define PLLE_MISC_LOCK_ENABLE 9 -#define PLL_BASE_LOCK 27 -#define PLLE_MISC_LOCK 11 +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) #define PLLE_AUX 0x48c #define PLLC_OUT 0x84 @@ -559,7 +559,7 @@ static struct tegra_clk_pll_params pll_c_params = { .vco_max = 1400000000, .base_reg = PLLC_BASE, .misc_reg = PLLC_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -573,7 +573,7 @@ static struct tegra_clk_pll_params pll_m_params = { .vco_max = 1200000000, .base_reg = PLLM_BASE, .misc_reg = PLLM_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -587,7 +587,7 @@ static struct tegra_clk_pll_params pll_p_params = { .vco_max = 1400000000, .base_reg = PLLP_BASE, .misc_reg = PLLP_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -601,7 +601,7 @@ static struct tegra_clk_pll_params pll_a_params = { .vco_max = 1400000000, .base_reg = PLLA_BASE, .misc_reg = PLLA_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -615,7 +615,7 @@ static struct tegra_clk_pll_params pll_d_params = { .vco_max = 1000000000, .base_reg = PLLD_BASE, .misc_reg = PLLD_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, }; @@ -629,7 +629,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .vco_max = 1000000000, .base_reg = PLLD2_BASE, .misc_reg = PLLD2_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, }; @@ -643,7 +643,7 @@ static struct tegra_clk_pll_params pll_u_params = { .vco_max = 960000000, .base_reg = PLLU_BASE, .misc_reg = PLLU_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, .pdiv_tohw = pllu_p, @@ -658,7 +658,7 @@ static struct tegra_clk_pll_params pll_x_params = { .vco_max = 1700000000, .base_reg = PLLX_BASE, .misc_reg = PLLX_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -672,7 +672,7 @@ static struct tegra_clk_pll_params pll_e_params = { .vco_max = 2400000000U, .base_reg = PLLE_BASE, .misc_reg = PLLE_MISC, - .lock_bit_idx = PLLE_MISC_LOCK, + .lock_mask = PLLE_MISC_LOCK, .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, .lock_delay = 300, }; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 925da45..3b498e0 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -154,7 +154,7 @@ struct tegra_clk_pll_params { u32 base_reg; u32 misc_reg; u32 lock_reg; - u32 lock_bit_idx; + u32 lock_mask; u32 lock_enable_bit_idx; int lock_delay; int max_p; -- cgit v1.1 From c1d1939c5163088e5f12011c6b3a6a9fab40215f Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:41 +0300 Subject: clk: tegra: Add new fields and PLL types for Tegra114 Tegra114 introduces new PLL types. This requires new clocktypes as well as some new fields in the pll structure. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 839 ++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/tegra/clk.h | 50 ++- 2 files changed, 888 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 0b96352..17c2cc0 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -79,6 +79,48 @@ #define PLLE_SS_CTRL 0x68 #define PLLE_SS_DISABLE (7 << 10) +#define PLLE_AUX_PLLP_SEL BIT(2) +#define PLLE_AUX_ENABLE_SWCTL BIT(4) +#define PLLE_AUX_SEQ_ENABLE BIT(24) +#define PLLE_AUX_PLLRE_SEL BIT(28) + +#define PLLE_MISC_PLLE_PTS BIT(8) +#define PLLE_MISC_IDDQ_SW_VALUE BIT(13) +#define PLLE_MISC_IDDQ_SW_CTRL BIT(14) +#define PLLE_MISC_VREG_BG_CTRL_SHIFT 4 +#define PLLE_MISC_VREG_BG_CTRL_MASK (3 << PLLE_MISC_VREG_BG_CTRL_SHIFT) +#define PLLE_MISC_VREG_CTRL_SHIFT 2 +#define PLLE_MISC_VREG_CTRL_MASK (2 << PLLE_MISC_VREG_CTRL_SHIFT) + +#define PLLCX_MISC_STROBE BIT(31) +#define PLLCX_MISC_RESET BIT(30) +#define PLLCX_MISC_SDM_DIV_SHIFT 28 +#define PLLCX_MISC_SDM_DIV_MASK (0x3 << PLLCX_MISC_SDM_DIV_SHIFT) +#define PLLCX_MISC_FILT_DIV_SHIFT 26 +#define PLLCX_MISC_FILT_DIV_MASK (0x3 << PLLCX_MISC_FILT_DIV_SHIFT) +#define PLLCX_MISC_ALPHA_SHIFT 18 +#define PLLCX_MISC_DIV_LOW_RANGE \ + ((0x1 << PLLCX_MISC_SDM_DIV_SHIFT) | \ + (0x1 << PLLCX_MISC_FILT_DIV_SHIFT)) +#define PLLCX_MISC_DIV_HIGH_RANGE \ + ((0x2 << PLLCX_MISC_SDM_DIV_SHIFT) | \ + (0x2 << PLLCX_MISC_FILT_DIV_SHIFT)) +#define PLLCX_MISC_COEF_LOW_RANGE \ + ((0x14 << PLLCX_MISC_KA_SHIFT) | (0x38 << PLLCX_MISC_KB_SHIFT)) +#define PLLCX_MISC_KA_SHIFT 2 +#define PLLCX_MISC_KB_SHIFT 9 +#define PLLCX_MISC_DEFAULT (PLLCX_MISC_COEF_LOW_RANGE | \ + (0x19 << PLLCX_MISC_ALPHA_SHIFT) | \ + PLLCX_MISC_DIV_LOW_RANGE | \ + PLLCX_MISC_RESET) +#define PLLCX_MISC1_DEFAULT 0x000d2308 +#define PLLCX_MISC2_DEFAULT 0x30211200 +#define PLLCX_MISC3_DEFAULT 0x200 + +#define PMC_PLLM_WB0_OVERRIDE 0x1dc +#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0 +#define PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK BIT(27) + #define PMC_SATA_PWRGT 0x1ac #define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5) #define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4) @@ -101,6 +143,24 @@ #define divn_max(p) (divn_mask(p)) #define divp_max(p) (1 << (divp_mask(p))) + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +/* PLLXC has 4-bit PDIV, but entry 15 is not allowed in h/w */ +#define PLLXC_PDIV_MAX 14 + +/* non-monotonic mapping below is not a typo */ +static u8 pllxc_p[PLLXC_PDIV_MAX + 1] = { + /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ + /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32 +}; + +#define PLLCX_PDIV_MAX 7 +static u8 pllcx_p[PLLCX_PDIV_MAX + 1] = { + /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7 */ + /* p: */ 1, 2, 3, 4, 6, 8, 12, 16 +}; +#endif + static void clk_pll_enable_lock(struct tegra_clk_pll *pll) { u32 val; @@ -646,6 +706,520 @@ const struct clk_ops tegra_clk_plle_ops = { .enable = clk_plle_enable, }; +#ifdef CONFIG_ARCH_TEGRA_114_SOC + +static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params, + unsigned long parent_rate) +{ + if (parent_rate > pll_params->cf_max) + return 2; + else + return 1; +} + +static int clk_pll_iddq_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + + u32 val; + int ret; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + val = pll_readl(pll->params->iddq_reg, pll); + val &= ~BIT(pll->params->iddq_bit_idx); + pll_writel(val, pll->params->iddq_reg, pll); + udelay(2); + + _clk_pll_enable(hw); + + ret = clk_pll_wait_for_lock(pll); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return 0; +} + +static void clk_pll_iddq_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + u32 val; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_disable(hw); + + val = pll_readl(pll->params->iddq_reg, pll); + val |= BIT(pll->params->iddq_bit_idx); + pll_writel(val, pll->params->iddq_reg, pll); + udelay(2); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + +static int _calc_dynamic_ramp_rate(struct clk_hw *hw, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned int p; + + if (!rate) + return -EINVAL; + + p = DIV_ROUND_UP(pll->params->vco_min, rate); + cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); + cfg->p = p; + cfg->output_rate = rate * cfg->p; + cfg->n = cfg->output_rate * cfg->m / parent_rate; + + if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) + return -EINVAL; + + return 0; +} + +static int _pll_ramp_calc_pll(struct clk_hw *hw, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + int err = 0; + + err = _get_table_rate(hw, cfg, rate, parent_rate); + if (err < 0) + err = _calc_dynamic_ramp_rate(hw, cfg, rate, parent_rate); + else if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) { + WARN_ON(1); + err = -EINVAL; + goto out; + } + + if (!cfg->p || (cfg->p > pll->params->max_p)) + err = -EINVAL; + +out: + return err; +} + +static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct tegra_clk_pll_freq_table cfg, old_cfg; + unsigned long flags = 0; + int ret = 0; + u8 old_p; + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); + if (ret < 0) + return ret; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _get_pll_mnp(pll, &old_cfg); + + old_p = pllxc_p[old_cfg.p]; + if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_p != cfg.p) { + cfg.p -= 1; + ret = _program_pll(hw, &cfg, rate); + } + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static long clk_pll_ramp_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct tegra_clk_pll_freq_table cfg; + int ret = 0; + u64 output_rate = *prate; + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, *prate); + if (ret < 0) + return ret; + + output_rate *= cfg.n; + do_div(output_rate, cfg.m * cfg.p); + + return output_rate; +} + +static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int state, ret = 0; + u32 val; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + state = clk_pll_is_enabled(hw); + if (state) { + if (rate != clk_get_rate(hw->clk)) { + pr_err("%s: Cannot change active PLLM\n", __func__); + ret = -EINVAL; + goto out; + } + goto out; + } + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); + if (ret < 0) + goto out; + + cfg.p -= 1; + + val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); + if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) { + val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); + val = cfg.p ? (val | PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK) : + (val & ~PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK); + writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); + + val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); + val &= ~(divn_mask(pll) | divm_mask(pll)); + val |= (cfg.m << pll->divm_shift) | (cfg.n << pll->divn_shift); + writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE); + } else + _update_pll_mnp(pll, &cfg); + + +out: + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void _pllcx_strobe(struct tegra_clk_pll *pll) +{ + u32 val; + + val = pll_readl_misc(pll); + val |= PLLCX_MISC_STROBE; + pll_writel_misc(val, pll); + udelay(2); + + val &= ~PLLCX_MISC_STROBE; + pll_writel_misc(val, pll); +} + +static int clk_pllc_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + int ret = 0; + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_enable(hw); + udelay(2); + + val = pll_readl_misc(pll); + val &= ~PLLCX_MISC_RESET; + pll_writel_misc(val, pll); + udelay(2); + + _pllcx_strobe(pll); + + ret = clk_pll_wait_for_lock(pll); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void _clk_pllc_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + + _clk_pll_disable(hw); + + val = pll_readl_misc(pll); + val |= PLLCX_MISC_RESET; + pll_writel_misc(val, pll); + udelay(2); +} + +static void clk_pllc_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pllc_disable(hw); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + +static int _pllcx_update_dynamic_coef(struct tegra_clk_pll *pll, + unsigned long input_rate, u32 n) +{ + u32 val, n_threshold; + + switch (input_rate) { + case 12000000: + n_threshold = 70; + break; + case 13000000: + case 26000000: + n_threshold = 71; + break; + case 16800000: + n_threshold = 55; + break; + case 19200000: + n_threshold = 48; + break; + default: + pr_err("%s: Unexpected reference rate %lu\n", + __func__, input_rate); + return -EINVAL; + } + + val = pll_readl_misc(pll); + val &= ~(PLLCX_MISC_SDM_DIV_MASK | PLLCX_MISC_FILT_DIV_MASK); + val |= n <= n_threshold ? + PLLCX_MISC_DIV_LOW_RANGE : PLLCX_MISC_DIV_HIGH_RANGE; + pll_writel_misc(val, pll); + + return 0; +} + +static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int state, ret = 0; + u32 val; + u16 old_m, old_n; + u8 old_p; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); + if (ret < 0) + goto out; + + val = pll_readl_base(pll); + old_m = (val >> pll->divm_shift) & (divm_mask(pll)); + old_n = (val >> pll->divn_shift) & (divn_mask(pll)); + old_p = pllcx_p[(val >> pll->divp_shift) & (divp_mask(pll))]; + + if (cfg.m != old_m) { + WARN_ON(1); + goto out; + } + + if (old_n == cfg.n && old_p == cfg.p) + goto out; + + cfg.p -= 1; + + state = clk_pll_is_enabled(hw); + if (state) + _clk_pllc_disable(hw); + + ret = _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n); + if (ret < 0) + goto out; + + _update_pll_mnp(pll, &cfg); + + if (state) + ret = clk_pllc_enable(hw); + +out: + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static long _pllre_calc_rate(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + u16 m, n; + u64 output_rate = parent_rate; + + m = _pll_fixed_mdiv(pll->params, parent_rate); + n = rate * m / parent_rate; + + output_rate *= n; + do_div(output_rate, m); + + if (cfg) { + cfg->m = m; + cfg->n = n; + } + + return output_rate; +} +static int clk_pllre_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg, old_cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int state, ret = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _pllre_calc_rate(pll, &cfg, rate, parent_rate); + _get_pll_mnp(pll, &old_cfg); + cfg.p = old_cfg.p; + + if (cfg.m != old_cfg.m || cfg.n != old_cfg.n) { + state = clk_pll_is_enabled(hw); + if (state) + _clk_pll_disable(hw); + + _update_pll_mnp(pll, &cfg); + + if (state) { + _clk_pll_enable(hw); + ret = clk_pll_wait_for_lock(pll); + } + } + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static unsigned long clk_pllre_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + u64 rate = parent_rate; + + _get_pll_mnp(pll, &cfg); + + rate *= cfg.n; + do_div(rate, cfg.m); + + return rate; +} + +static long clk_pllre_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + + return _pllre_calc_rate(pll, NULL, rate, *prate); +} + +static int clk_plle_tegra114_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct tegra_clk_pll_freq_table sel; + u32 val; + int ret; + unsigned long flags = 0; + unsigned long input_rate = clk_get_rate(clk_get_parent(hw->clk)); + + if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate)) + return -EINVAL; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + val = pll_readl_base(pll); + val &= ~BIT(29); /* Disable lock override */ + pll_writel_base(val, pll); + + val = pll_readl(pll->params->aux_reg, pll); + val |= PLLE_AUX_ENABLE_SWCTL; + val &= ~PLLE_AUX_SEQ_ENABLE; + pll_writel(val, pll->params->aux_reg, pll); + udelay(1); + + val = pll_readl_misc(pll); + val |= PLLE_MISC_LOCK_ENABLE; + val |= PLLE_MISC_IDDQ_SW_CTRL; + val &= ~PLLE_MISC_IDDQ_SW_VALUE; + val |= PLLE_MISC_PLLE_PTS; + val |= PLLE_MISC_VREG_BG_CTRL_MASK | PLLE_MISC_VREG_CTRL_MASK; + pll_writel_misc(val, pll); + udelay(5); + + val = pll_readl(PLLE_SS_CTRL, pll); + val |= PLLE_SS_DISABLE; + pll_writel(val, PLLE_SS_CTRL, pll); + + val = pll_readl_base(pll); + val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll)); + val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT); + val |= sel.m << pll->divm_shift; + val |= sel.n << pll->divn_shift; + val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; + pll_writel_base(val, pll); + udelay(1); + + _clk_pll_enable(hw); + ret = clk_pll_wait_for_lock(pll); + + if (ret < 0) + goto out; + + /* TODO: enable hw control of xusb brick pll */ + +out: + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void clk_plle_tegra114_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + u32 val; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_disable(hw); + + val = pll_readl_misc(pll); + val |= PLLE_MISC_IDDQ_SW_CTRL | PLLE_MISC_IDDQ_SW_VALUE; + pll_writel_misc(val, pll); + udelay(1); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} +#endif + static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, void __iomem *pmc, unsigned long fixed_rate, struct tegra_clk_pll_params *pll_params, u32 pll_flags, @@ -741,3 +1315,268 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, return clk; } + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +const struct clk_ops tegra_clk_pllxc_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_iddq_enable, + .disable = clk_pll_iddq_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_ramp_round_rate, + .set_rate = clk_pllxc_set_rate, +}; + +const struct clk_ops tegra_clk_pllm_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_iddq_enable, + .disable = clk_pll_iddq_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_ramp_round_rate, + .set_rate = clk_pllm_set_rate, +}; + +const struct clk_ops tegra_clk_pllc_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pllc_enable, + .disable = clk_pllc_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_ramp_round_rate, + .set_rate = clk_pllc_set_rate, +}; + +const struct clk_ops tegra_clk_pllre_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_iddq_enable, + .disable = clk_pll_iddq_disable, + .recalc_rate = clk_pllre_recalc_rate, + .round_rate = clk_pllre_round_rate, + .set_rate = clk_pllre_set_rate, +}; + +const struct clk_ops tegra_clk_plle_tegra114_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_plle_tegra114_enable, + .disable = clk_plle_tegra114_disable, + .recalc_rate = clk_pll_recalc_rate, +}; + + +struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + + if (!pll_params->pdiv_tohw) + return ERR_PTR(-EINVAL); + + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllxc_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock, unsigned long parent_rate) +{ + u32 val; + struct tegra_clk_pll *pll; + struct clk *clk; + + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + /* program minimum rate by default */ + + val = pll_readl_base(pll); + if (val & PLL_BASE_ENABLE) + WARN_ON(val & pll_params->iddq_bit_idx); + else { + int m; + + m = _pll_fixed_mdiv(pll_params, parent_rate); + val = m << PLL_BASE_DIVM_SHIFT; + val |= (pll_params->vco_min / parent_rate) + << PLL_BASE_DIVN_SHIFT; + pll_writel_base(val, pll); + } + + /* disable lock override */ + + val = pll_readl_misc(pll); + val &= ~BIT(29); + pll_writel_misc(val, pll); + + pll_flags |= TEGRA_PLL_LOCK_MISC; + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllre_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + + if (!pll_params->pdiv_tohw) + return ERR_PTR(-EINVAL); + + pll_flags |= TEGRA_PLL_BYPASS; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllm_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct clk *parent, *clk; + struct pdiv_map *p_tohw = pll_params->pdiv_tohw; + struct tegra_clk_pll *pll; + struct tegra_clk_pll_freq_table cfg; + unsigned long parent_rate; + + if (!p_tohw) + return ERR_PTR(-EINVAL); + + parent = __clk_lookup(parent_name); + if (IS_ERR(parent)) { + WARN(1, "parent clk %s of %s must be registered first\n", + name, parent_name); + return ERR_PTR(-EINVAL); + } + + pll_flags |= TEGRA_PLL_BYPASS; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + parent_rate = __clk_get_rate(parent); + + /* + * Most of PLLC register fields are shadowed, and can not be read + * directly from PLL h/w. Hence, actual PLLC boot state is unknown. + * Initialize PLL to default state: disabled, reset; shadow registers + * loaded with default parameters; dividers are preset for half of + * minimum VCO rate (the latter assured that shadowed divider settings + * are within supported range). + */ + + cfg.m = _pll_fixed_mdiv(pll_params, parent_rate); + cfg.n = cfg.m * pll_params->vco_min / parent_rate; + + while (p_tohw->pdiv) { + if (p_tohw->pdiv == 2) { + cfg.p = p_tohw->hw_val; + break; + } + p_tohw++; + } + + if (!p_tohw->pdiv) { + WARN_ON(1); + return ERR_PTR(-EINVAL); + } + + pll_writel_base(0, pll); + _update_pll_mnp(pll, &cfg); + + pll_writel_misc(PLLCX_MISC_DEFAULT, pll); + pll_writel(PLLCX_MISC1_DEFAULT, pll_params->ext_misc_reg[0], pll); + pll_writel(PLLCX_MISC2_DEFAULT, pll_params->ext_misc_reg[1], pll); + pll_writel(PLLCX_MISC3_DEFAULT, pll_params->ext_misc_reg[2], pll); + + _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllc_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_plle_tegra114(const char *name, + const char *parent_name, + void __iomem *clk_base, unsigned long flags, + unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + u32 val, val_aux; + + pll = _tegra_init_pll(clk_base, NULL, fixed_rate, pll_params, + TEGRA_PLL_HAS_LOCK_ENABLE, freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + /* ensure parent is set to pll_re_vco */ + + val = pll_readl_base(pll); + val_aux = pll_readl(pll_params->aux_reg, pll); + + if (val & PLL_BASE_ENABLE) { + if (!(val_aux & PLLE_AUX_PLLRE_SEL)) + WARN(1, "pll_e enabled with unsupported parent %s\n", + (val & PLLE_AUX_PLLP_SEL) ? "pllp_out0" : "pll_ref"); + } else { + val_aux |= PLLE_AUX_PLLRE_SEL; + pll_writel(val, pll_params->aux_reg, pll); + } + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_plle_tegra114_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} +#endif diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 3b498e0..8cedb09 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -1,4 +1,4 @@ -/* + /* * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it @@ -156,6 +156,13 @@ struct tegra_clk_pll_params { u32 lock_reg; u32 lock_mask; u32 lock_enable_bit_idx; + u32 iddq_reg; + u32 iddq_bit_idx; + u32 aux_reg; + u32 dyn_ramp_reg; + u32 ext_misc_reg[3]; + int stepa_shift; + int stepb_shift; int lock_delay; int max_p; struct pdiv_map *pdiv_tohw; @@ -238,12 +245,53 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate, struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); + struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); +struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + +struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + +struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + +struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock, unsigned long parent_rate); + +struct clk *tegra_clk_register_plle_tegra114(const char *name, + const char *parent_name, + void __iomem *clk_base, unsigned long flags, + unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + /** * struct tegra_clk_pll_out - PLL divider down clock * -- cgit v1.1 From a26a029893096204f08a3ff5e262f99e1a75e273 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:42 +0300 Subject: clk: tegra: Add flags to tegra_clk_periph() We will need some tegra peripheral clocks with the CLK_IGNORE_UNUSED flag, most notably mselect, which is a bridge between AXI and most peripherals. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-periph.c | 11 ++++++----- drivers/clk/tegra/clk-tegra20.c | 2 +- drivers/clk/tegra/clk-tegra30.c | 2 +- drivers/clk/tegra/clk.h | 9 ++++++--- 4 files changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 9dbd301..b2309d3 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -173,14 +173,15 @@ const struct clk_ops tegra_clk_periph_nodiv_ops = { static struct clk *_tegra_clk_register_periph(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, - void __iomem *clk_base, u32 offset, bool div) + void __iomem *clk_base, u32 offset, bool div, + unsigned long flags) { struct clk *clk; struct clk_init_data init; init.name = name; init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops; - init.flags = div ? 0 : CLK_SET_RATE_PARENT; + init.flags = flags; init.parent_names = parent_names; init.num_parents = num_parents; @@ -205,10 +206,10 @@ static struct clk *_tegra_clk_register_periph(const char *name, struct clk *tegra_clk_register_periph(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, - u32 offset) + u32 offset, unsigned long flags) { return _tegra_clk_register_periph(name, parent_names, num_parents, - periph, clk_base, offset, true); + periph, clk_base, offset, true, flags); } struct clk *tegra_clk_register_periph_nodiv(const char *name, @@ -217,5 +218,5 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name, u32 offset) { return _tegra_clk_register_periph(name, parent_names, num_parents, - periph, clk_base, offset, false); + periph, clk_base, offset, false, CLK_SET_RATE_PARENT); } diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 5c7b58b..b0405b6 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -1019,7 +1019,7 @@ static void __init tegra20_periph_clk_init(void) data = &tegra_periph_clk_list[i]; clk = tegra_clk_register_periph(data->name, data->parent_names, data->num_parents, &data->periph, - clk_base, data->offset); + clk_base, data->offset, data->flags); clk_register_clkdev(clk, data->con_id, data->dev_id); clks[data->clk_id] = clk; } diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 735f964..2dc0c56 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1668,7 +1668,7 @@ static void __init tegra30_periph_clk_init(void) data = &tegra_periph_clk_list[i]; clk = tegra_clk_register_periph(data->name, data->parent_names, data->num_parents, &data->periph, - clk_base, data->offset); + clk_base, data->offset, data->flags); clk_register_clkdev(clk, data->con_id, data->dev_id); clks[data->clk_id] = clk; } diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 8cedb09..fd12b77 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -417,7 +417,7 @@ extern const struct clk_ops tegra_clk_periph_ops; struct clk *tegra_clk_register_periph(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, - u32 offset); + u32 offset, unsigned long flags); struct clk *tegra_clk_register_periph_nodiv(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, @@ -460,12 +460,14 @@ struct tegra_periph_init_data { u32 offset; const char *con_id; const char *dev_id; + unsigned long flags; }; #define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ _mux_shift, _mux_mask, _mux_flags, _div_shift, \ _div_width, _div_frac_width, _div_flags, _regs, \ - _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table) \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table,\ + _flags) \ { \ .name = _name, \ .clk_id = _clk_id, \ @@ -480,6 +482,7 @@ struct tegra_periph_init_data { .offset = _offset, \ .con_id = _con_id, \ .dev_id = _dev_id, \ + .flags = _flags \ } #define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\ @@ -490,7 +493,7 @@ struct tegra_periph_init_data { _mux_shift, BIT(_mux_width) - 1, _mux_flags, \ _div_shift, _div_width, _div_frac_width, _div_flags, \ _regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\ - NULL) + NULL, 0) /** * struct clk_super_mux - super clock -- cgit v1.1 From fdcccbd804088eb96881c9f6532de04868f9dbc1 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:44 +0300 Subject: clk: tegra: Workaround for Tegra114 MSENC problem Workaround a hardware bug in MSENC during clock enable. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-periph-gate.c | 9 +++++++++ drivers/clk/tegra/clk.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index d87e1ce..bafee98 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -43,6 +43,8 @@ static DEFINE_SPINLOCK(periph_ref_lock); #define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32)) +#define LVL2_CLK_GATE_OVRE 0x554 + /* Peripheral gate clock ops */ static int clk_periph_is_enabled(struct clk_hw *hw) { @@ -83,6 +85,13 @@ static int clk_periph_enable(struct clk_hw *hw) } } + if (gate->flags & TEGRA_PERIPH_WAR_1005168) { + writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE); + writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE); + udelay(1); + writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE); + } + spin_unlock_irqrestore(&periph_ref_lock, flags); return 0; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index fd12b77..fb48f04 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -358,6 +358,7 @@ struct tegra_clk_periph_regs { * TEGRA_PERIPH_ON_APB - If peripheral is in the APB bus then read the * bus to flush the write operation in apb bus. This flag indicates * that this peripheral is in apb bus. + * TEGRA_PERIPH_WAR_1005168 - Apply workaround for Tegra114 MSENC bug */ struct tegra_clk_periph_gate { u32 magic; @@ -377,6 +378,7 @@ struct tegra_clk_periph_gate { #define TEGRA_PERIPH_NO_RESET BIT(0) #define TEGRA_PERIPH_MANUAL_RESET BIT(1) #define TEGRA_PERIPH_ON_APB BIT(2) +#define TEGRA_PERIPH_WAR_1005168 BIT(3) void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert); extern const struct clk_ops tegra_clk_periph_gate_ops; -- cgit v1.1 From 2cb5efefd6f7d3e7df9a7430b910a80515821256 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:45 +0300 Subject: clk: tegra: Implement clocks for Tegra114 Implement clocks for Tegra114. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/Makefile | 1 + drivers/clk/tegra/clk-tegra114.c | 2085 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 2086 insertions(+) create mode 100644 drivers/clk/tegra/clk-tegra114.c (limited to 'drivers') diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 2b41b0f..f49fac2 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -9,3 +9,4 @@ obj-y += clk-super.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c new file mode 100644 index 0000000..e2a7fa1 --- /dev/null +++ b/drivers/clk/tegra/clk-tegra114.c @@ -0,0 +1,2085 @@ +/* + * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define RST_DEVICES_L 0x004 +#define RST_DEVICES_H 0x008 +#define RST_DEVICES_U 0x00C +#define RST_DEVICES_V 0x358 +#define RST_DEVICES_W 0x35C +#define RST_DEVICES_X 0x28C +#define RST_DEVICES_SET_L 0x300 +#define RST_DEVICES_CLR_L 0x304 +#define RST_DEVICES_SET_H 0x308 +#define RST_DEVICES_CLR_H 0x30c +#define RST_DEVICES_SET_U 0x310 +#define RST_DEVICES_CLR_U 0x314 +#define RST_DEVICES_SET_V 0x430 +#define RST_DEVICES_CLR_V 0x434 +#define RST_DEVICES_SET_W 0x438 +#define RST_DEVICES_CLR_W 0x43c +#define RST_DEVICES_NUM 5 + +#define CLK_OUT_ENB_L 0x010 +#define CLK_OUT_ENB_H 0x014 +#define CLK_OUT_ENB_U 0x018 +#define CLK_OUT_ENB_V 0x360 +#define CLK_OUT_ENB_W 0x364 +#define CLK_OUT_ENB_X 0x280 +#define CLK_OUT_ENB_SET_L 0x320 +#define CLK_OUT_ENB_CLR_L 0x324 +#define CLK_OUT_ENB_SET_H 0x328 +#define CLK_OUT_ENB_CLR_H 0x32c +#define CLK_OUT_ENB_SET_U 0x330 +#define CLK_OUT_ENB_CLR_U 0x334 +#define CLK_OUT_ENB_SET_V 0x440 +#define CLK_OUT_ENB_CLR_V 0x444 +#define CLK_OUT_ENB_SET_W 0x448 +#define CLK_OUT_ENB_CLR_W 0x44c +#define CLK_OUT_ENB_SET_X 0x284 +#define CLK_OUT_ENB_CLR_X 0x288 +#define CLK_OUT_ENB_NUM 6 + +#define PLLC_BASE 0x80 +#define PLLC_MISC2 0x88 +#define PLLC_MISC 0x8c +#define PLLC2_BASE 0x4e8 +#define PLLC2_MISC 0x4ec +#define PLLC3_BASE 0x4fc +#define PLLC3_MISC 0x500 +#define PLLM_BASE 0x90 +#define PLLM_MISC 0x9c +#define PLLP_BASE 0xa0 +#define PLLP_MISC 0xac +#define PLLX_BASE 0xe0 +#define PLLX_MISC 0xe4 +#define PLLX_MISC2 0x514 +#define PLLX_MISC3 0x518 +#define PLLD_BASE 0xd0 +#define PLLD_MISC 0xdc +#define PLLD2_BASE 0x4b8 +#define PLLD2_MISC 0x4bc +#define PLLE_BASE 0xe8 +#define PLLE_MISC 0xec +#define PLLA_BASE 0xb0 +#define PLLA_MISC 0xbc +#define PLLU_BASE 0xc0 +#define PLLU_MISC 0xcc +#define PLLRE_BASE 0x4c4 +#define PLLRE_MISC 0x4c8 + +#define PLL_MISC_LOCK_ENABLE 18 +#define PLLC_MISC_LOCK_ENABLE 24 +#define PLLDU_MISC_LOCK_ENABLE 22 +#define PLLE_MISC_LOCK_ENABLE 9 +#define PLLRE_MISC_LOCK_ENABLE 30 + +#define PLLC_IDDQ_BIT 26 +#define PLLX_IDDQ_BIT 3 +#define PLLRE_IDDQ_BIT 16 + +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) +#define PLLRE_MISC_LOCK BIT(24) +#define PLLCX_BASE_LOCK (BIT(26)|BIT(27)) + +#define PLLE_AUX 0x48c +#define PLLC_OUT 0x84 +#define PLLM_OUT 0x94 +#define PLLP_OUTA 0xa4 +#define PLLP_OUTB 0xa8 +#define PLLA_OUT 0xb4 + +#define AUDIO_SYNC_CLK_I2S0 0x4a0 +#define AUDIO_SYNC_CLK_I2S1 0x4a4 +#define AUDIO_SYNC_CLK_I2S2 0x4a8 +#define AUDIO_SYNC_CLK_I2S3 0x4ac +#define AUDIO_SYNC_CLK_I2S4 0x4b0 +#define AUDIO_SYNC_CLK_SPDIF 0x4b4 + +#define AUDIO_SYNC_DOUBLER 0x49c + +#define PMC_CLK_OUT_CNTRL 0x1a8 +#define PMC_DPD_PADS_ORIDE 0x1c +#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20 +#define PMC_CTRL 0 +#define PMC_CTRL_BLINK_ENB 7 + +#define OSC_CTRL 0x50 +#define OSC_CTRL_OSC_FREQ_SHIFT 28 +#define OSC_CTRL_PLL_REF_DIV_SHIFT 26 + +#define PLLXC_SW_MAX_P 6 + +#define CCLKG_BURST_POLICY 0x368 +#define CCLKLP_BURST_POLICY 0x370 +#define SCLK_BURST_POLICY 0x028 +#define SYSTEM_CLK_RATE 0x030 + +#define UTMIP_PLL_CFG2 0x488 +#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6) +#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4) + +#define UTMIP_PLL_CFG1 0x484 +#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6) +#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP BIT(17) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP BIT(15) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14) +#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12) + +#define UTMIPLL_HW_PWRDN_CFG0 0x52c +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24) +#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET BIT(6) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE BIT(5) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL BIT(4) +#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE BIT(1) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0) + +#define CLK_SOURCE_I2S0 0x1d8 +#define CLK_SOURCE_I2S1 0x100 +#define CLK_SOURCE_I2S2 0x104 +#define CLK_SOURCE_NDFLASH 0x160 +#define CLK_SOURCE_I2S3 0x3bc +#define CLK_SOURCE_I2S4 0x3c0 +#define CLK_SOURCE_SPDIF_OUT 0x108 +#define CLK_SOURCE_SPDIF_IN 0x10c +#define CLK_SOURCE_PWM 0x110 +#define CLK_SOURCE_ADX 0x638 +#define CLK_SOURCE_AMX 0x63c +#define CLK_SOURCE_HDA 0x428 +#define CLK_SOURCE_HDA2CODEC_2X 0x3e4 +#define CLK_SOURCE_SBC1 0x134 +#define CLK_SOURCE_SBC2 0x118 +#define CLK_SOURCE_SBC3 0x11c +#define CLK_SOURCE_SBC4 0x1b4 +#define CLK_SOURCE_SBC5 0x3c8 +#define CLK_SOURCE_SBC6 0x3cc +#define CLK_SOURCE_SATA_OOB 0x420 +#define CLK_SOURCE_SATA 0x424 +#define CLK_SOURCE_NDSPEED 0x3f8 +#define CLK_SOURCE_VFIR 0x168 +#define CLK_SOURCE_SDMMC1 0x150 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC3 0x1bc +#define CLK_SOURCE_SDMMC4 0x164 +#define CLK_SOURCE_VDE 0x1c8 +#define CLK_SOURCE_CSITE 0x1d4 +#define CLK_SOURCE_LA 0x1f8 +#define CLK_SOURCE_TRACE 0x634 +#define CLK_SOURCE_OWR 0x1cc +#define CLK_SOURCE_NOR 0x1d0 +#define CLK_SOURCE_MIPI 0x174 +#define CLK_SOURCE_I2C1 0x124 +#define CLK_SOURCE_I2C2 0x198 +#define CLK_SOURCE_I2C3 0x1b8 +#define CLK_SOURCE_I2C4 0x3c4 +#define CLK_SOURCE_I2C5 0x128 +#define CLK_SOURCE_UARTA 0x178 +#define CLK_SOURCE_UARTB 0x17c +#define CLK_SOURCE_UARTC 0x1a0 +#define CLK_SOURCE_UARTD 0x1c0 +#define CLK_SOURCE_UARTE 0x1c4 +#define CLK_SOURCE_UARTA_DBG 0x178 +#define CLK_SOURCE_UARTB_DBG 0x17c +#define CLK_SOURCE_UARTC_DBG 0x1a0 +#define CLK_SOURCE_UARTD_DBG 0x1c0 +#define CLK_SOURCE_UARTE_DBG 0x1c4 +#define CLK_SOURCE_3D 0x158 +#define CLK_SOURCE_2D 0x15c +#define CLK_SOURCE_VI_SENSOR 0x1a8 +#define CLK_SOURCE_VI 0x148 +#define CLK_SOURCE_EPP 0x16c +#define CLK_SOURCE_MSENC 0x1f0 +#define CLK_SOURCE_TSEC 0x1f4 +#define CLK_SOURCE_HOST1X 0x180 +#define CLK_SOURCE_HDMI 0x18c +#define CLK_SOURCE_DISP1 0x138 +#define CLK_SOURCE_DISP2 0x13c +#define CLK_SOURCE_CILAB 0x614 +#define CLK_SOURCE_CILCD 0x618 +#define CLK_SOURCE_CILE 0x61c +#define CLK_SOURCE_DSIALP 0x620 +#define CLK_SOURCE_DSIBLP 0x624 +#define CLK_SOURCE_TSENSOR 0x3b8 +#define CLK_SOURCE_D_AUDIO 0x3d0 +#define CLK_SOURCE_DAM0 0x3d8 +#define CLK_SOURCE_DAM1 0x3dc +#define CLK_SOURCE_DAM2 0x3e0 +#define CLK_SOURCE_ACTMON 0x3e8 +#define CLK_SOURCE_EXTERN1 0x3ec +#define CLK_SOURCE_EXTERN2 0x3f0 +#define CLK_SOURCE_EXTERN3 0x3f4 +#define CLK_SOURCE_I2CSLOW 0x3fc +#define CLK_SOURCE_SE 0x42c +#define CLK_SOURCE_MSELECT 0x3b4 +#define CLK_SOURCE_SOC_THERM 0x644 +#define CLK_SOURCE_XUSB_HOST_SRC 0x600 +#define CLK_SOURCE_XUSB_FALCON_SRC 0x604 +#define CLK_SOURCE_XUSB_FS_SRC 0x608 +#define CLK_SOURCE_XUSB_SS_SRC 0x610 +#define CLK_SOURCE_XUSB_DEV_SRC 0x60c +#define CLK_SOURCE_EMC 0x19c + +static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32]; + +static void __iomem *clk_base; +static void __iomem *pmc_base; + +static DEFINE_SPINLOCK(pll_d_lock); +static DEFINE_SPINLOCK(pll_d2_lock); +static DEFINE_SPINLOCK(pll_u_lock); +static DEFINE_SPINLOCK(pll_div_lock); +static DEFINE_SPINLOCK(pll_re_lock); +static DEFINE_SPINLOCK(clk_doubler_lock); +static DEFINE_SPINLOCK(clk_out_lock); +static DEFINE_SPINLOCK(sysrate_lock); + +static struct pdiv_map pllxc_p[] = { + { .pdiv = 1, .hw_val = 0 }, + { .pdiv = 2, .hw_val = 1 }, + { .pdiv = 3, .hw_val = 2 }, + { .pdiv = 4, .hw_val = 3 }, + { .pdiv = 5, .hw_val = 4 }, + { .pdiv = 6, .hw_val = 5 }, + { .pdiv = 8, .hw_val = 6 }, + { .pdiv = 10, .hw_val = 7 }, + { .pdiv = 12, .hw_val = 8 }, + { .pdiv = 16, .hw_val = 9 }, + { .pdiv = 12, .hw_val = 10 }, + { .pdiv = 16, .hw_val = 11 }, + { .pdiv = 20, .hw_val = 12 }, + { .pdiv = 24, .hw_val = 13 }, + { .pdiv = 32, .hw_val = 14 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { + { 12000000, 624000000, 104, 0, 2}, + { 12000000, 600000000, 100, 0, 2}, + { 13000000, 600000000, 92, 0, 2}, /* actual: 598.0 MHz */ + { 16800000, 600000000, 71, 0, 2}, /* actual: 596.4 MHz */ + { 19200000, 600000000, 62, 0, 2}, /* actual: 595.2 MHz */ + { 26000000, 600000000, 92, 1, 2}, /* actual: 598.0 MHz */ + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_params pll_c_params = { + .input_min = 12000000, + .input_max = 800000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */ + .vco_min = 600000000, + .vco_max = 1400000000, + .base_reg = PLLC_BASE, + .misc_reg = PLLC_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLC_MISC_LOCK_ENABLE, + .lock_delay = 300, + .iddq_reg = PLLC_MISC, + .iddq_bit_idx = PLLC_IDDQ_BIT, + .max_p = PLLXC_SW_MAX_P, + .dyn_ramp_reg = PLLC_MISC2, + .stepa_shift = 17, + .stepb_shift = 9, + .pdiv_tohw = pllxc_p, +}; + +static struct pdiv_map pllc_p[] = { + { .pdiv = 1, .hw_val = 0 }, + { .pdiv = 2, .hw_val = 1 }, + { .pdiv = 4, .hw_val = 3 }, + { .pdiv = 8, .hw_val = 5 }, + { .pdiv = 16, .hw_val = 7 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_cx_freq_table[] = { + {12000000, 600000000, 100, 0, 2}, + {13000000, 600000000, 92, 0, 2}, /* actual: 598.0 MHz */ + {16800000, 600000000, 71, 0, 2}, /* actual: 596.4 MHz */ + {19200000, 600000000, 62, 0, 2}, /* actual: 595.2 MHz */ + {26000000, 600000000, 92, 1, 2}, /* actual: 598.0 MHz */ + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_c2_params = { + .input_min = 12000000, + .input_max = 48000000, + .cf_min = 12000000, + .cf_max = 19200000, + .vco_min = 600000000, + .vco_max = 1200000000, + .base_reg = PLLC2_BASE, + .misc_reg = PLLC2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .pdiv_tohw = pllc_p, + .ext_misc_reg[0] = 0x4f0, + .ext_misc_reg[1] = 0x4f4, + .ext_misc_reg[2] = 0x4f8, +}; + +static struct tegra_clk_pll_params pll_c3_params = { + .input_min = 12000000, + .input_max = 48000000, + .cf_min = 12000000, + .cf_max = 19200000, + .vco_min = 600000000, + .vco_max = 1200000000, + .base_reg = PLLC3_BASE, + .misc_reg = PLLC3_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .pdiv_tohw = pllc_p, + .ext_misc_reg[0] = 0x504, + .ext_misc_reg[1] = 0x508, + .ext_misc_reg[2] = 0x50c, +}; + +static struct pdiv_map pllm_p[] = { + { .pdiv = 1, .hw_val = 0 }, + { .pdiv = 2, .hw_val = 1 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { + {12000000, 800000000, 66, 0, 1}, /* actual: 792.0 MHz */ + {13000000, 800000000, 61, 0, 1}, /* actual: 793.0 MHz */ + {16800000, 800000000, 47, 0, 1}, /* actual: 789.6 MHz */ + {19200000, 800000000, 41, 0, 1}, /* actual: 787.2 MHz */ + {26000000, 800000000, 61, 1, 1}, /* actual: 793.0 MHz */ + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_m_params = { + .input_min = 12000000, + .input_max = 500000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */ + .vco_min = 400000000, + .vco_max = 1066000000, + .base_reg = PLLM_BASE, + .misc_reg = PLLM_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .max_p = 2, + .pdiv_tohw = pllm_p, +}; + +static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { + {12000000, 216000000, 432, 12, 1, 8}, + {13000000, 216000000, 432, 13, 1, 8}, + {16800000, 216000000, 360, 14, 1, 8}, + {19200000, 216000000, 360, 16, 1, 8}, + {26000000, 216000000, 432, 26, 1, 8}, + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_p_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 200000000, + .vco_max = 700000000, + .base_reg = PLLP_BASE, + .misc_reg = PLLP_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { + {9600000, 282240000, 147, 5, 0, 4}, + {9600000, 368640000, 192, 5, 0, 4}, + {9600000, 240000000, 200, 8, 0, 8}, + + {28800000, 282240000, 245, 25, 0, 8}, + {28800000, 368640000, 320, 25, 0, 8}, + {28800000, 240000000, 200, 24, 0, 8}, + {0, 0, 0, 0, 0, 0}, +}; + + +static struct tegra_clk_pll_params pll_a_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 200000000, + .vco_max = 700000000, + .base_reg = PLLA_BASE, + .misc_reg = PLLA_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { + {12000000, 216000000, 864, 12, 2, 12}, + {13000000, 216000000, 864, 13, 2, 12}, + {16800000, 216000000, 720, 14, 2, 12}, + {19200000, 216000000, 720, 16, 2, 12}, + {26000000, 216000000, 864, 26, 2, 12}, + + {12000000, 594000000, 594, 12, 0, 12}, + {13000000, 594000000, 594, 13, 0, 12}, + {16800000, 594000000, 495, 14, 0, 12}, + {19200000, 594000000, 495, 16, 0, 12}, + {26000000, 594000000, 594, 26, 0, 12}, + + {12000000, 1000000000, 1000, 12, 0, 12}, + {13000000, 1000000000, 1000, 13, 0, 12}, + {19200000, 1000000000, 625, 12, 0, 12}, + {26000000, 1000000000, 1000, 26, 0, 12}, + + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_d_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 500000000, + .vco_max = 1000000000, + .base_reg = PLLD_BASE, + .misc_reg = PLLD_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct tegra_clk_pll_params pll_d2_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 500000000, + .vco_max = 1000000000, + .base_reg = PLLD2_BASE, + .misc_reg = PLLD2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct pdiv_map pllu_p[] = { + { .pdiv = 1, .hw_val = 1 }, + { .pdiv = 2, .hw_val = 0 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { + {12000000, 480000000, 960, 12, 0, 12}, + {13000000, 480000000, 960, 13, 0, 12}, + {16800000, 480000000, 400, 7, 0, 5}, + {19200000, 480000000, 200, 4, 0, 3}, + {26000000, 480000000, 960, 26, 0, 12}, + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_u_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 480000000, + .vco_max = 960000000, + .base_reg = PLLU_BASE, + .misc_reg = PLLU_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, + .pdiv_tohw = pllu_p, +}; + +static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { + /* 1 GHz */ + {12000000, 1000000000, 83, 0, 1}, /* actual: 996.0 MHz */ + {13000000, 1000000000, 76, 0, 1}, /* actual: 988.0 MHz */ + {16800000, 1000000000, 59, 0, 1}, /* actual: 991.2 MHz */ + {19200000, 1000000000, 52, 0, 1}, /* actual: 998.4 MHz */ + {26000000, 1000000000, 76, 1, 1}, /* actual: 988.0 MHz */ + + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_x_params = { + .input_min = 12000000, + .input_max = 800000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */ + .vco_min = 700000000, + .vco_max = 2400000000U, + .base_reg = PLLX_BASE, + .misc_reg = PLLX_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .iddq_reg = PLLX_MISC3, + .iddq_bit_idx = PLLX_IDDQ_BIT, + .max_p = PLLXC_SW_MAX_P, + .dyn_ramp_reg = PLLX_MISC2, + .stepa_shift = 16, + .stepb_shift = 24, + .pdiv_tohw = pllxc_p, +}; + +static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { + /* PLLE special case: use cpcon field to store cml divider value */ + {336000000, 100000000, 100, 21, 16, 11}, + {312000000, 100000000, 200, 26, 24, 13}, + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_e_params = { + .input_min = 12000000, + .input_max = 1000000000, + .cf_min = 12000000, + .cf_max = 75000000, + .vco_min = 1600000000, + .vco_max = 2400000000U, + .base_reg = PLLE_BASE, + .misc_reg = PLLE_MISC, + .aux_reg = PLLE_AUX, + .lock_mask = PLLE_MISC_LOCK, + .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_re_vco_params = { + .input_min = 12000000, + .input_max = 1000000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */ + .vco_min = 300000000, + .vco_max = 600000000, + .base_reg = PLLRE_BASE, + .misc_reg = PLLRE_MISC, + .lock_mask = PLLRE_MISC_LOCK, + .lock_enable_bit_idx = PLLRE_MISC_LOCK_ENABLE, + .lock_delay = 300, + .iddq_reg = PLLRE_MISC, + .iddq_bit_idx = PLLRE_IDDQ_BIT, +}; + +/* Peripheral clock registers */ + +static struct tegra_clk_periph_regs periph_l_regs = { + .enb_reg = CLK_OUT_ENB_L, + .enb_set_reg = CLK_OUT_ENB_SET_L, + .enb_clr_reg = CLK_OUT_ENB_CLR_L, + .rst_reg = RST_DEVICES_L, + .rst_set_reg = RST_DEVICES_SET_L, + .rst_clr_reg = RST_DEVICES_CLR_L, +}; + +static struct tegra_clk_periph_regs periph_h_regs = { + .enb_reg = CLK_OUT_ENB_H, + .enb_set_reg = CLK_OUT_ENB_SET_H, + .enb_clr_reg = CLK_OUT_ENB_CLR_H, + .rst_reg = RST_DEVICES_H, + .rst_set_reg = RST_DEVICES_SET_H, + .rst_clr_reg = RST_DEVICES_CLR_H, +}; + +static struct tegra_clk_periph_regs periph_u_regs = { + .enb_reg = CLK_OUT_ENB_U, + .enb_set_reg = CLK_OUT_ENB_SET_U, + .enb_clr_reg = CLK_OUT_ENB_CLR_U, + .rst_reg = RST_DEVICES_U, + .rst_set_reg = RST_DEVICES_SET_U, + .rst_clr_reg = RST_DEVICES_CLR_U, +}; + +static struct tegra_clk_periph_regs periph_v_regs = { + .enb_reg = CLK_OUT_ENB_V, + .enb_set_reg = CLK_OUT_ENB_SET_V, + .enb_clr_reg = CLK_OUT_ENB_CLR_V, + .rst_reg = RST_DEVICES_V, + .rst_set_reg = RST_DEVICES_SET_V, + .rst_clr_reg = RST_DEVICES_CLR_V, +}; + +static struct tegra_clk_periph_regs periph_w_regs = { + .enb_reg = CLK_OUT_ENB_W, + .enb_set_reg = CLK_OUT_ENB_SET_W, + .enb_clr_reg = CLK_OUT_ENB_CLR_W, + .rst_reg = RST_DEVICES_W, + .rst_set_reg = RST_DEVICES_SET_W, + .rst_clr_reg = RST_DEVICES_CLR_W, +}; + +/* possible OSC frequencies in Hz */ +static unsigned long tegra114_input_freq[] = { + [0] = 13000000, + [1] = 16800000, + [4] = 19200000, + [5] = 38400000, + [8] = 12000000, + [9] = 48000000, + [12] = 260000000, +}; + +#define MASK(x) (BIT(x) - 1) + +#define TEGRA_INIT_DATA_MUX(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id, \ + _parents##_idx, 0) + +#define TEGRA_INIT_DATA_MUX_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _gate_flags, _clk_id, flags)\ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id, \ + _parents##_idx, flags) + +#define TEGRA_INIT_DATA_MUX8(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 29, MASK(3), 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id, \ + _parents##_idx, 0) + +#define TEGRA_INIT_DATA_INT(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_INT_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _gate_flags, _clk_id, flags)\ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, flags) + +#define TEGRA_INIT_DATA_INT8(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_UART(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 16, 1, TEGRA_DIVIDER_UART, _regs,\ + _clk_num, periph_clk_enb_refcnt, 0, _clk_id, \ + _parents##_idx, 0) + +#define TEGRA_INIT_DATA_I2C(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 16, 0, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, 0, _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_NODIV(_name, _con_id, _dev_id, _parents, _offset, \ + _mux_shift, _mux_mask, _clk_num, _regs, \ + _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + _mux_shift, _mux_mask, 0, 0, 0, 0, 0, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_XUSB(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset, \ + 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_AUDIO(_name, _con_id, _dev_id, _offset, _clk_num,\ + _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, mux_d_audio_clk, \ + _offset, 16, 0xE01F, 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags , _clk_id, \ + mux_d_audio_clk_idx, 0) + +enum tegra114_clk { + rtc = 4, timer = 5, uarta = 6, sdmmc2 = 9, i2s1 = 11, i2c1 = 12, + ndflash = 13, sdmmc1 = 14, sdmmc4 = 15, pwm = 17, i2s2 = 18, epp = 19, + gr_2d = 21, usbd = 22, isp = 23, gr_3d = 24, disp2 = 26, disp1 = 27, + host1x = 28, vcp = 29, i2s0 = 30, apbdma = 34, kbc = 36, kfuse = 40, + sbc1 = 41, nor = 42, sbc2 = 44, sbc3 = 46, i2c5 = 47, dsia = 48, + mipi = 50, hdmi = 51, csi = 52, i2c2 = 54, uartc = 55, mipi_cal = 56, + emc, usb2, usb3, vde = 61, bsea = 62, bsev = 63, uartd = 65, + i2c3 = 67, sbc4 = 68, sdmmc3 = 69, owr = 71, csite = 73, + la = 76, trace = 77, soc_therm = 78, dtv = 79, ndspeed = 80, + i2cslow = 81, dsib = 82, tsec = 83, xusb_host = 89, msenc = 91, + csus = 92, mselect = 99, tsensor = 100, i2s3 = 101, i2s4 = 102, + i2c4 = 103, sbc5 = 104, sbc6 = 105, d_audio, apbif = 107, dam0, dam1, + dam2, hda2codec_2x = 111, audio0_2x = 113, audio1_2x, audio2_2x, + audio3_2x, audio4_2x, spdif_2x, actmon = 119, extern1 = 120, + extern2 = 121, extern3 = 122, hda = 125, se = 127, hda2hdmi = 128, + cilab = 144, cilcd = 145, cile = 146, dsialp = 147, dsiblp = 148, + dds = 150, dp2 = 152, amx = 153, adx = 154, xusb_ss = 156, uartb = 192, + vfir, spdif_in, spdif_out, vi, vi_sensor, fuse, fuse_burn, clk_32k, + clk_m, clk_m_div2, clk_m_div4, pll_ref, pll_c, pll_c_out1, pll_c2, + pll_c3, pll_m, pll_m_out1, pll_p, pll_p_out1, pll_p_out2, pll_p_out3, + pll_p_out4, pll_a, pll_a_out0, pll_d, pll_d_out0, pll_d2, pll_d2_out0, + pll_u, pll_u_480M, pll_u_60M, pll_u_48M, pll_u_12M, pll_x, pll_x_out0, + pll_re_vco, pll_re_out, pll_e_out0, spdif_in_sync, i2s0_sync, + i2s1_sync, i2s2_sync, i2s3_sync, i2s4_sync, vimclk_sync, audio0, + audio1, audio2, audio3, audio4, spdif, clk_out_1, clk_out_2, clk_out_3, + blink, xusb_host_src, xusb_falcon_src, xusb_fs_src, xusb_ss_src, + xusb_dev_src, xusb_dev, xusb_hs_src, sclk, hclk, pclk, cclk_g, cclk_lp, + + /* Mux clocks */ + + audio0_mux = 300, audio1_mux, audio2_mux, audio3_mux, audio4_mux, + spdif_mux, clk_out_1_mux, clk_out_2_mux, clk_out_3_mux, dsia_mux, + dsib_mux, clk_max, +}; + +struct utmi_clk_param { + /* Oscillator Frequency in KHz */ + u32 osc_frequency; + /* UTMIP PLL Enable Delay Count */ + u8 enable_delay_count; + /* UTMIP PLL Stable count */ + u8 stable_count; + /* UTMIP PLL Active delay count */ + u8 active_delay_count; + /* UTMIP PLL Xtal frequency count */ + u8 xtal_freq_count; +}; + +static const struct utmi_clk_param utmi_parameters[] = { + {.osc_frequency = 13000000, .enable_delay_count = 0x02, + .stable_count = 0x33, .active_delay_count = 0x05, + .xtal_freq_count = 0x7F}, + {.osc_frequency = 19200000, .enable_delay_count = 0x03, + .stable_count = 0x4B, .active_delay_count = 0x06, + .xtal_freq_count = 0xBB}, + {.osc_frequency = 12000000, .enable_delay_count = 0x02, + .stable_count = 0x2F, .active_delay_count = 0x04, + .xtal_freq_count = 0x76}, + {.osc_frequency = 26000000, .enable_delay_count = 0x04, + .stable_count = 0x66, .active_delay_count = 0x09, + .xtal_freq_count = 0xFE}, + {.osc_frequency = 16800000, .enable_delay_count = 0x03, + .stable_count = 0x41, .active_delay_count = 0x0A, + .xtal_freq_count = 0xA4}, +}; + +/* peripheral mux definitions */ + +#define MUX_I2S_SPDIF(_id) \ +static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \ + #_id, "pll_p",\ + "clk_m"}; +MUX_I2S_SPDIF(audio0) +MUX_I2S_SPDIF(audio1) +MUX_I2S_SPDIF(audio2) +MUX_I2S_SPDIF(audio3) +MUX_I2S_SPDIF(audio4) +MUX_I2S_SPDIF(audio) + +#define mux_pllaout0_audio0_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio1_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio2_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio3_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio4_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio_2x_pllp_clkm_idx NULL + +static const char *mux_pllp_pllc_pllm_clkm[] = { + "pll_p", "pll_c", "pll_m", "clk_m" +}; +#define mux_pllp_pllc_pllm_clkm_idx NULL + +static const char *mux_pllp_pllc_pllm[] = { "pll_p", "pll_c", "pll_m" }; +#define mux_pllp_pllc_pllm_idx NULL + +static const char *mux_pllp_pllc_clk32_clkm[] = { + "pll_p", "pll_c", "clk_32k", "clk_m" +}; +#define mux_pllp_pllc_clk32_clkm_idx NULL + +static const char *mux_plla_pllc_pllp_clkm[] = { + "pll_a_out0", "pll_c", "pll_p", "clk_m" +}; +#define mux_plla_pllc_pllp_clkm_idx mux_pllp_pllc_pllm_clkm_idx + +static const char *mux_pllp_pllc2_c_c3_pllm_clkm[] = { + "pll_p", "pll_c2", "pll_c", "pll_c3", "pll_m", "clk_m" +}; +static u32 mux_pllp_pllc2_c_c3_pllm_clkm_idx[] = { + [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6, +}; + +static const char *mux_pllp_clkm[] = { + "pll_p", "clk_m" +}; +static u32 mux_pllp_clkm_idx[] = { + [0] = 0, [1] = 3, +}; + +static const char *mux_pllm_pllc2_c_c3_pllp_plla[] = { + "pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0" +}; +#define mux_pllm_pllc2_c_c3_pllp_plla_idx mux_pllp_pllc2_c_c3_pllm_clkm_idx + +static const char *mux_pllp_pllm_plld_plla_pllc_plld2_clkm[] = { + "pll_p", "pll_m", "pll_d_out0", "pll_a_out0", "pll_c", + "pll_d2_out0", "clk_m" +}; +#define mux_pllp_pllm_plld_plla_pllc_plld2_clkm_idx NULL + +static const char *mux_pllm_pllc_pllp_plla[] = { + "pll_m", "pll_c", "pll_p", "pll_a_out0" +}; +#define mux_pllm_pllc_pllp_plla_idx mux_pllp_pllc_pllm_clkm_idx + +static const char *mux_pllp_pllc_clkm[] = { + "pll_p", "pll_c", "pll_m" +}; +static u32 mux_pllp_pllc_clkm_idx[] = { + [0] = 0, [1] = 1, [2] = 3, +}; + +static const char *mux_pllp_pllc_clkm_clk32[] = { + "pll_p", "pll_c", "clk_m", "clk_32k" +}; +#define mux_pllp_pllc_clkm_clk32_idx NULL + +static const char *mux_plla_clk32_pllp_clkm_plle[] = { + "pll_a_out0", "clk_32k", "pll_p", "clk_m", "pll_e_out0" +}; +#define mux_plla_clk32_pllp_clkm_plle_idx NULL + +static const char *mux_clkm_pllp_pllc_pllre[] = { + "clk_m", "pll_p", "pll_c", "pll_re_out" +}; +static u32 mux_clkm_pllp_pllc_pllre_idx[] = { + [0] = 0, [1] = 1, [2] = 3, [3] = 5, +}; + +static const char *mux_clkm_48M_pllp_480M[] = { + "clk_m", "pll_u_48M", "pll_p", "pll_u_480M" +}; +#define mux_clkm_48M_pllp_480M_idx NULL + +static const char *mux_clkm_pllre_clk32_480M_pllc_ref[] = { + "clk_m", "pll_re_out", "clk_32k", "pll_u_480M", "pll_c", "pll_ref" +}; +static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = { + [0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7, +}; + +static const char *mux_plld_out0_plld2_out0[] = { + "pll_d_out0", "pll_d2_out0", +}; +#define mux_plld_out0_plld2_out0_idx NULL + +static const char *mux_d_audio_clk[] = { + "pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync", + "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", +}; +static u32 mux_d_audio_clk_idx[] = { + [0] = 0, [1] = 0x8000, [2] = 0xc000, [3] = 0xE000, [4] = 0xE001, + [5] = 0xE002, [6] = 0xE003, [7] = 0xE004, [8] = 0xE005, [9] = 0xE007, +}; + +static const char *mux_pllmcp_clkm[] = { + "pll_m_out0", "pll_c_out0", "pll_p_out0", "clk_m", "pll_m_ud", +}; + +static const struct clk_div_table pll_re_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 4, .div = 5 }, + { .val = 5, .div = 6 }, + { .val = 0, .div = 0 }, +}; + +static struct clk *clks[clk_max]; +static struct clk_onecell_data clk_data; + +static unsigned long osc_freq; +static unsigned long pll_ref_freq; + +static int __init tegra114_osc_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + u32 val, pll_ref_div; + + val = readl_relaxed(clk_base + OSC_CTRL); + + osc_freq = tegra114_input_freq[val >> OSC_CTRL_OSC_FREQ_SHIFT]; + if (!osc_freq) { + WARN_ON(1); + return -EINVAL; + } + + /* clk_m */ + clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT, + osc_freq); + clk_register_clkdev(clk, "clk_m", NULL); + clks[clk_m] = clk; + + /* pll_ref */ + val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; + pll_ref_div = 1 << val; + clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m", + CLK_SET_RATE_PARENT, 1, pll_ref_div); + clk_register_clkdev(clk, "pll_ref", NULL); + clks[pll_ref] = clk; + + pll_ref_freq = osc_freq / pll_ref_div; + + return 0; +} + +static void __init tegra114_fixed_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + + /* clk_32k */ + clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, CLK_IS_ROOT, + 32768); + clk_register_clkdev(clk, "clk_32k", NULL); + clks[clk_32k] = clk; + + /* clk_m_div2 */ + clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "clk_m_div2", NULL); + clks[clk_m_div2] = clk; + + /* clk_m_div4 */ + clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m", + CLK_SET_RATE_PARENT, 1, 4); + clk_register_clkdev(clk, "clk_m_div4", NULL); + clks[clk_m_div4] = clk; + +} + +static __init void tegra114_utmi_param_configure(void __iomem *clk_base) +{ + u32 reg; + int i; + + for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) { + if (osc_freq == utmi_parameters[i].osc_frequency) + break; + } + + if (i >= ARRAY_SIZE(utmi_parameters)) { + pr_err("%s: Unexpected oscillator freq %lu\n", __func__, + osc_freq); + return; + } + + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL stable and active counts */ + /* [FIXME] arclk_rst.h says WRONG! This should be 1ms -> 0x50 Check! */ + reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0); + reg |= UTMIP_PLL_CFG2_STABLE_COUNT(utmi_parameters[i].stable_count); + + reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); + + reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(utmi_parameters[i]. + active_delay_count); + + /* Remove power downs from UTMIP PLL control bits */ + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN; + + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL delay and oscillator frequency counts */ + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + + reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(utmi_parameters[i]. + enable_delay_count); + + reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0); + reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(utmi_parameters[i]. + xtal_freq_count); + + /* Remove power downs from UTMIP PLL control bits */ + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + + /* Setup HW control of UTMIPLL */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL; + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + + udelay(1); + + /* Setup SW override of UTMIPLL assuming USB2.0 + ports are assigned to USB2 */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + + udelay(1); + + /* Enable HW control UTMIPLL */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); +} + +static void __init _clip_vco_min(struct tegra_clk_pll_params *pll_params) +{ + pll_params->vco_min = + DIV_ROUND_UP(pll_params->vco_min, pll_ref_freq) * pll_ref_freq; +} + +static int __init _setup_dynamic_ramp(struct tegra_clk_pll_params *pll_params, + void __iomem *clk_base) +{ + u32 val; + u32 step_a, step_b; + + switch (pll_ref_freq) { + case 12000000: + case 13000000: + case 26000000: + step_a = 0x2B; + step_b = 0x0B; + break; + case 16800000: + step_a = 0x1A; + step_b = 0x09; + break; + case 19200000: + step_a = 0x12; + step_b = 0x08; + break; + default: + pr_err("%s: Unexpected reference rate %lu\n", + __func__, pll_ref_freq); + WARN_ON(1); + return -EINVAL; + } + + val = step_a << pll_params->stepa_shift; + val |= step_b << pll_params->stepb_shift; + writel_relaxed(val, clk_base + pll_params->dyn_ramp_reg); + + return 0; +} + +static void __init _init_iddq(struct tegra_clk_pll_params *pll_params, + void __iomem *clk_base) +{ + u32 val, val_iddq; + + val = readl_relaxed(clk_base + pll_params->base_reg); + val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg); + + if (val & BIT(30)) + WARN_ON(val_iddq & BIT(pll_params->iddq_bit_idx)); + else { + val_iddq |= BIT(pll_params->iddq_bit_idx); + writel_relaxed(val_iddq, clk_base + pll_params->iddq_reg); + } +} + +static void __init tegra114_pll_init(void __iomem *clk_base, + void __iomem *pmc) +{ + u32 val; + struct clk *clk; + + /* PLLC */ + _clip_vco_min(&pll_c_params); + if (_setup_dynamic_ramp(&pll_c_params, clk_base) >= 0) { + _init_iddq(&pll_c_params, clk_base); + clk = tegra_clk_register_pllxc("pll_c", "pll_ref", clk_base, + pmc, 0, 0, &pll_c_params, TEGRA_PLL_USE_LOCK, + pll_c_freq_table, NULL); + clk_register_clkdev(clk, "pll_c", NULL); + clks[pll_c] = clk; + + /* PLLC_OUT1 */ + clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c", + clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div", + clk_base + PLLC_OUT, 1, 0, + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_c_out1", NULL); + clks[pll_c_out1] = clk; + } + + /* PLLC2 */ + _clip_vco_min(&pll_c2_params); + clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0, 0, + &pll_c2_params, TEGRA_PLL_USE_LOCK, + pll_cx_freq_table, NULL); + clk_register_clkdev(clk, "pll_c2", NULL); + clks[pll_c2] = clk; + + /* PLLC3 */ + _clip_vco_min(&pll_c3_params); + clk = tegra_clk_register_pllc("pll_c3", "pll_ref", clk_base, pmc, 0, 0, + &pll_c3_params, TEGRA_PLL_USE_LOCK, + pll_cx_freq_table, NULL); + clk_register_clkdev(clk, "pll_c3", NULL); + clks[pll_c3] = clk; + + /* PLLP */ + clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base, pmc, 0, + 408000000, &pll_p_params, + TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK, + pll_p_freq_table, NULL); + clk_register_clkdev(clk, "pll_p", NULL); + clks[pll_p] = clk; + + /* PLLP_OUT1 */ + clk = tegra_clk_register_divider("pll_p_out1_div", "pll_p", + clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out1", "pll_p_out1_div", + clk_base + PLLP_OUTA, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out1", NULL); + clks[pll_p_out1] = clk; + + /* PLLP_OUT2 */ + clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p", + clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div", + clk_base + PLLP_OUTA, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out2", NULL); + clks[pll_p_out2] = clk; + + /* PLLP_OUT3 */ + clk = tegra_clk_register_divider("pll_p_out3_div", "pll_p", + clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out3", "pll_p_out3_div", + clk_base + PLLP_OUTB, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out3", NULL); + clks[pll_p_out3] = clk; + + /* PLLP_OUT4 */ + clk = tegra_clk_register_divider("pll_p_out4_div", "pll_p", + clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out4", "pll_p_out4_div", + clk_base + PLLP_OUTB, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out4", NULL); + clks[pll_p_out4] = clk; + + /* PLLM */ + _clip_vco_min(&pll_m_params); + clk = tegra_clk_register_pllm("pll_m", "pll_ref", clk_base, pmc, + CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, 0, + &pll_m_params, TEGRA_PLL_USE_LOCK, + pll_m_freq_table, NULL); + clk_register_clkdev(clk, "pll_m", NULL); + clks[pll_m] = clk; + + /* PLLM_OUT1 */ + clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m", + clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div", + clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_m_out1", NULL); + clks[pll_m_out1] = clk; + + /* PLLM_UD */ + clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m", + CLK_SET_RATE_PARENT, 1, 1); + + /* PLLX */ + _clip_vco_min(&pll_x_params); + if (_setup_dynamic_ramp(&pll_x_params, clk_base) >= 0) { + _init_iddq(&pll_x_params, clk_base); + clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, + pmc, CLK_IGNORE_UNUSED, 0, &pll_x_params, + TEGRA_PLL_USE_LOCK, pll_x_freq_table, NULL); + clk_register_clkdev(clk, "pll_x", NULL); + clks[pll_x] = clk; + } + + /* PLLX_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_x_out0", NULL); + clks[pll_x_out0] = clk; + + /* PLLU */ + val = readl(clk_base + pll_u_params.base_reg); + val &= ~BIT(24); /* disable PLLU_OVERRIDE */ + writel(val, clk_base + pll_u_params.base_reg); + + clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, pmc, 0, + 0, &pll_u_params, TEGRA_PLLU | + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | + TEGRA_PLL_USE_LOCK, pll_u_freq_table, &pll_u_lock); + clk_register_clkdev(clk, "pll_u", NULL); + clks[pll_u] = clk; + + tegra114_utmi_param_configure(clk_base); + + /* PLLU_480M */ + clk = clk_register_gate(NULL, "pll_u_480M", "pll_u", + CLK_SET_RATE_PARENT, clk_base + PLLU_BASE, + 22, 0, &pll_u_lock); + clk_register_clkdev(clk, "pll_u_480M", NULL); + clks[pll_u_480M] = clk; + + /* PLLU_60M */ + clk = clk_register_fixed_factor(NULL, "pll_u_60M", "pll_u", + CLK_SET_RATE_PARENT, 1, 8); + clk_register_clkdev(clk, "pll_u_60M", NULL); + clks[pll_u_60M] = clk; + + /* PLLU_48M */ + clk = clk_register_fixed_factor(NULL, "pll_u_48M", "pll_u", + CLK_SET_RATE_PARENT, 1, 10); + clk_register_clkdev(clk, "pll_u_48M", NULL); + clks[pll_u_48M] = clk; + + /* PLLU_12M */ + clk = clk_register_fixed_factor(NULL, "pll_u_12M", "pll_u", + CLK_SET_RATE_PARENT, 1, 40); + clk_register_clkdev(clk, "pll_u_12M", NULL); + clks[pll_u_12M] = clk; + + /* PLLD */ + clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc, 0, + 0, &pll_d_params, + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | + TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d_lock); + clk_register_clkdev(clk, "pll_d", NULL); + clks[pll_d] = clk; + + /* PLLD_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_d_out0", NULL); + clks[pll_d_out0] = clk; + + /* PLLD2 */ + clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc, 0, + 0, &pll_d2_params, + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | + TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d2_lock); + clk_register_clkdev(clk, "pll_d2", NULL); + clks[pll_d2] = clk; + + /* PLLD2_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_d2_out0", NULL); + clks[pll_d2_out0] = clk; + + /* PLLA */ + clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, pmc, 0, + 0, &pll_a_params, TEGRA_PLL_HAS_CPCON | + TEGRA_PLL_USE_LOCK, pll_a_freq_table, NULL); + clk_register_clkdev(clk, "pll_a", NULL); + clks[pll_a] = clk; + + /* PLLA_OUT0 */ + clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a", + clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div", + clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_a_out0", NULL); + clks[pll_a_out0] = clk; + + /* PLLRE */ + _clip_vco_min(&pll_re_vco_params); + clk = tegra_clk_register_pllre("pll_re_vco", "pll_ref", clk_base, pmc, + 0, 0, &pll_re_vco_params, TEGRA_PLL_USE_LOCK, + NULL, &pll_re_lock, pll_ref_freq); + clk_register_clkdev(clk, "pll_re_vco", NULL); + clks[pll_re_vco] = clk; + + clk = clk_register_divider_table(NULL, "pll_re_out", "pll_re_vco", 0, + clk_base + PLLRE_BASE, 16, 4, 0, + pll_re_div_table, &pll_re_lock); + clk_register_clkdev(clk, "pll_re_out", NULL); + clks[pll_re_out] = clk; + + /* PLLE */ + clk = tegra_clk_register_plle_tegra114("pll_e_out0", "pll_re_vco", + clk_base, 0, 100000000, &pll_e_params, + pll_e_freq_table, NULL); + clk_register_clkdev(clk, "pll_e_out0", NULL); + clks[pll_e_out0] = clk; +} + +static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync", + "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", +}; + +static const char *clk_out1_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern1", +}; + +static const char *clk_out2_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern2", +}; + +static const char *clk_out3_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern3", +}; + +static void __init tegra114_audio_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + + /* spdif_in_sync */ + clk = tegra_clk_register_sync_source("spdif_in_sync", 24000000, + 24000000); + clk_register_clkdev(clk, "spdif_in_sync", NULL); + clks[spdif_in_sync] = clk; + + /* i2s0_sync */ + clk = tegra_clk_register_sync_source("i2s0_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s0_sync", NULL); + clks[i2s0_sync] = clk; + + /* i2s1_sync */ + clk = tegra_clk_register_sync_source("i2s1_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s1_sync", NULL); + clks[i2s1_sync] = clk; + + /* i2s2_sync */ + clk = tegra_clk_register_sync_source("i2s2_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s2_sync", NULL); + clks[i2s2_sync] = clk; + + /* i2s3_sync */ + clk = tegra_clk_register_sync_source("i2s3_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s3_sync", NULL); + clks[i2s3_sync] = clk; + + /* i2s4_sync */ + clk = tegra_clk_register_sync_source("i2s4_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s4_sync", NULL); + clks[i2s4_sync] = clk; + + /* vimclk_sync */ + clk = tegra_clk_register_sync_source("vimclk_sync", 24000000, 24000000); + clk_register_clkdev(clk, "vimclk_sync", NULL); + clks[vimclk_sync] = clk; + + /* audio0 */ + clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, + NULL); + clks[audio0_mux] = clk; + clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S0, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio0", NULL); + clks[audio0] = clk; + + /* audio1 */ + clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, + NULL); + clks[audio1_mux] = clk; + clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S1, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio1", NULL); + clks[audio1] = clk; + + /* audio2 */ + clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, + NULL); + clks[audio2_mux] = clk; + clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S2, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio2", NULL); + clks[audio2] = clk; + + /* audio3 */ + clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, + NULL); + clks[audio3_mux] = clk; + clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S3, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio3", NULL); + clks[audio3] = clk; + + /* audio4 */ + clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, + NULL); + clks[audio4_mux] = clk; + clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S4, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio4", NULL); + clks[audio4] = clk; + + /* spdif */ + clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, + NULL); + clks[spdif_mux] = clk; + clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0, + clk_base + AUDIO_SYNC_CLK_SPDIF, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "spdif", NULL); + clks[spdif] = clk; + + /* audio0_2x */ + clk = clk_register_fixed_factor(NULL, "audio0_doubler", "audio0", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio0_div", "audio0_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 24, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio0_2x", "audio0_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 113, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio0_2x", NULL); + clks[audio0_2x] = clk; + + /* audio1_2x */ + clk = clk_register_fixed_factor(NULL, "audio1_doubler", "audio1", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio1_div", "audio1_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 25, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio1_2x", "audio1_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 114, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio1_2x", NULL); + clks[audio1_2x] = clk; + + /* audio2_2x */ + clk = clk_register_fixed_factor(NULL, "audio2_doubler", "audio2", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio2_div", "audio2_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 26, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio2_2x", "audio2_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 115, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio2_2x", NULL); + clks[audio2_2x] = clk; + + /* audio3_2x */ + clk = clk_register_fixed_factor(NULL, "audio3_doubler", "audio3", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio3_div", "audio3_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 27, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio3_2x", "audio3_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 116, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio3_2x", NULL); + clks[audio3_2x] = clk; + + /* audio4_2x */ + clk = clk_register_fixed_factor(NULL, "audio4_doubler", "audio4", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio4_div", "audio4_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 28, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio4_2x", "audio4_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 117, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio4_2x", NULL); + clks[audio4_2x] = clk; + + /* spdif_2x */ + clk = clk_register_fixed_factor(NULL, "spdif_doubler", "spdif", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("spdif_div", "spdif_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 29, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("spdif_2x", "spdif_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 118, + &periph_v_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "spdif_2x", NULL); + clks[spdif_2x] = clk; +} + +static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) +{ + struct clk *clk; + + /* clk_out_1 */ + clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, + &clk_out_lock); + clks[clk_out_1_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_1", "clk_out_1_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 2, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern1", "clk_out_1"); + clks[clk_out_1] = clk; + + /* clk_out_2 */ + clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, + &clk_out_lock); + clks[clk_out_2_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 10, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern2", "clk_out_2"); + clks[clk_out_2] = clk; + + /* clk_out_3 */ + clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, + &clk_out_lock); + clks[clk_out_3_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 18, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern3", "clk_out_3"); + clks[clk_out_3] = clk; + + /* blink */ + clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0, + pmc_base + PMC_DPD_PADS_ORIDE, + PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL); + clk = clk_register_gate(NULL, "blink", "blink_override", 0, + pmc_base + PMC_CTRL, + PMC_CTRL_BLINK_ENB, 0, NULL); + clk_register_clkdev(clk, "blink", NULL); + clks[blink] = clk; + +} + +static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", + "pll_p_out3", "pll_p_out2", "unused", + "clk_32k", "pll_m_out1" }; + +static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", + "pll_p", "pll_p_out4", "unused", + "unused", "pll_x" }; + +static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", + "pll_p", "pll_p_out4", "unused", + "unused", "pll_x", "pll_x_out0" }; + +static void __init tegra114_super_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + + /* CCLKG */ + clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents, + ARRAY_SIZE(cclk_g_parents), + CLK_SET_RATE_PARENT, + clk_base + CCLKG_BURST_POLICY, + 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "cclk_g", NULL); + clks[cclk_g] = clk; + + /* CCLKLP */ + clk = tegra_clk_register_super_mux("cclk_lp", cclk_lp_parents, + ARRAY_SIZE(cclk_lp_parents), + CLK_SET_RATE_PARENT, + clk_base + CCLKLP_BURST_POLICY, + 0, 4, 8, 9, NULL); + clk_register_clkdev(clk, "cclk_lp", NULL); + clks[cclk_lp] = clk; + + /* SCLK */ + clk = tegra_clk_register_super_mux("sclk", sclk_parents, + ARRAY_SIZE(sclk_parents), + CLK_SET_RATE_PARENT, + clk_base + SCLK_BURST_POLICY, + 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "sclk", NULL); + clks[sclk] = clk; + + /* HCLK */ + clk = clk_register_divider(NULL, "hclk_div", "sclk", 0, + clk_base + SYSTEM_CLK_RATE, 4, 2, 0, + &sysrate_lock); + clk = clk_register_gate(NULL, "hclk", "hclk_div", CLK_SET_RATE_PARENT | + CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE, + 7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); + clk_register_clkdev(clk, "hclk", NULL); + clks[hclk] = clk; + + /* PCLK */ + clk = clk_register_divider(NULL, "pclk_div", "hclk", 0, + clk_base + SYSTEM_CLK_RATE, 0, 2, 0, + &sysrate_lock); + clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT | + CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE, + 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); + clk_register_clkdev(clk, "pclk", NULL); + clks[pclk] = clk; +} + +static struct tegra_periph_init_data tegra_periph_clk_list[] = { + TEGRA_INIT_DATA_MUX("i2s0", NULL, "tegra30-i2s.0", mux_pllaout0_audio0_2x_pllp_clkm, CLK_SOURCE_I2S0, 30, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s0), + TEGRA_INIT_DATA_MUX("i2s1", NULL, "tegra30-i2s.1", mux_pllaout0_audio1_2x_pllp_clkm, CLK_SOURCE_I2S1, 11, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s1), + TEGRA_INIT_DATA_MUX("i2s2", NULL, "tegra30-i2s.2", mux_pllaout0_audio2_2x_pllp_clkm, CLK_SOURCE_I2S2, 18, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s2), + TEGRA_INIT_DATA_MUX("i2s3", NULL, "tegra30-i2s.3", mux_pllaout0_audio3_2x_pllp_clkm, CLK_SOURCE_I2S3, 101, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s3), + TEGRA_INIT_DATA_MUX("i2s4", NULL, "tegra30-i2s.4", mux_pllaout0_audio4_2x_pllp_clkm, CLK_SOURCE_I2S4, 102, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s4), + TEGRA_INIT_DATA_MUX("spdif_out", "spdif_out", "tegra30-spdif", mux_pllaout0_audio_2x_pllp_clkm, CLK_SOURCE_SPDIF_OUT, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_out), + TEGRA_INIT_DATA_MUX("spdif_in", "spdif_in", "tegra30-spdif", mux_pllp_pllc_pllm, CLK_SOURCE_SPDIF_IN, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_in), + TEGRA_INIT_DATA_MUX("pwm", NULL, "pwm", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_PWM, 17, &periph_l_regs, TEGRA_PERIPH_ON_APB, pwm), + TEGRA_INIT_DATA_MUX("adx", NULL, "adx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX, 154, &periph_w_regs, TEGRA_PERIPH_ON_APB, adx), + TEGRA_INIT_DATA_MUX("amx", NULL, "amx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX, 153, &periph_w_regs, TEGRA_PERIPH_ON_APB, amx), + TEGRA_INIT_DATA_MUX("hda", "hda", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA, 125, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda), + TEGRA_INIT_DATA_MUX("hda2codec_2x", "hda2codec", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda2codec_2x), + TEGRA_INIT_DATA_MUX("sbc1", NULL, "tegra11-spi.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC1, 41, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc1), + TEGRA_INIT_DATA_MUX("sbc2", NULL, "tegra11-spi.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC2, 44, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc2), + TEGRA_INIT_DATA_MUX("sbc3", NULL, "tegra11-spi.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC3, 46, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc3), + TEGRA_INIT_DATA_MUX("sbc4", NULL, "tegra11-spi.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC4, 68, &periph_u_regs, TEGRA_PERIPH_ON_APB, sbc4), + TEGRA_INIT_DATA_MUX("sbc5", NULL, "tegra11-spi.4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC5, 104, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc5), + TEGRA_INIT_DATA_MUX("sbc6", NULL, "tegra11-spi.5", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC6, 105, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc6), + TEGRA_INIT_DATA_MUX8("ndflash", NULL, "tegra_nand", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDFLASH, 13, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed), + TEGRA_INIT_DATA_MUX8("ndspeed", NULL, "tegra_nand_speed", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDSPEED, 80, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed), + TEGRA_INIT_DATA_MUX("vfir", NULL, "vfir", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_VFIR, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, vfir), + TEGRA_INIT_DATA_MUX("sdmmc1", NULL, "sdhci-tegra.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC1, 14, &periph_l_regs, 0, sdmmc1), + TEGRA_INIT_DATA_MUX("sdmmc2", NULL, "sdhci-tegra.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC2, 9, &periph_l_regs, 0, sdmmc2), + TEGRA_INIT_DATA_MUX("sdmmc3", NULL, "sdhci-tegra.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC3, 69, &periph_u_regs, 0, sdmmc3), + TEGRA_INIT_DATA_MUX("sdmmc4", NULL, "sdhci-tegra.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC4, 15, &periph_l_regs, 0, sdmmc4), + TEGRA_INIT_DATA_INT("vde", NULL, "vde", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_VDE, 61, &periph_h_regs, 0, vde), + TEGRA_INIT_DATA_MUX_FLAGS("csite", NULL, "csite", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_CSITE, 73, &periph_u_regs, TEGRA_PERIPH_ON_APB, csite, CLK_IGNORE_UNUSED), + TEGRA_INIT_DATA_MUX("la", NULL, "la", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_LA, 76, &periph_u_regs, TEGRA_PERIPH_ON_APB, la), + TEGRA_INIT_DATA_MUX("trace", NULL, "trace", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_TRACE, 77, &periph_u_regs, TEGRA_PERIPH_ON_APB, trace), + TEGRA_INIT_DATA_MUX("owr", NULL, "tegra_w1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_OWR, 71, &periph_u_regs, TEGRA_PERIPH_ON_APB, owr), + TEGRA_INIT_DATA_MUX("nor", NULL, "tegra-nor", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NOR, 42, &periph_h_regs, 0, nor), + TEGRA_INIT_DATA_MUX("mipi", NULL, "mipi", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_MIPI, 50, &periph_h_regs, TEGRA_PERIPH_ON_APB, mipi), + TEGRA_INIT_DATA_I2C("i2c1", "div-clk", "tegra11-i2c.0", mux_pllp_clkm, CLK_SOURCE_I2C1, 12, &periph_l_regs, i2c1), + TEGRA_INIT_DATA_I2C("i2c2", "div-clk", "tegra11-i2c.1", mux_pllp_clkm, CLK_SOURCE_I2C2, 54, &periph_h_regs, i2c2), + TEGRA_INIT_DATA_I2C("i2c3", "div-clk", "tegra11-i2c.2", mux_pllp_clkm, CLK_SOURCE_I2C3, 67, &periph_u_regs, i2c3), + TEGRA_INIT_DATA_I2C("i2c4", "div-clk", "tegra11-i2c.3", mux_pllp_clkm, CLK_SOURCE_I2C4, 103, &periph_v_regs, i2c4), + TEGRA_INIT_DATA_I2C("i2c5", "div-clk", "tegra11-i2c.4", mux_pllp_clkm, CLK_SOURCE_I2C5, 47, &periph_h_regs, i2c5), + TEGRA_INIT_DATA_UART("uarta", NULL, "tegra_uart.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTA, 6, &periph_l_regs, uarta), + TEGRA_INIT_DATA_UART("uartb", NULL, "tegra_uart.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, &periph_l_regs, uartb), + TEGRA_INIT_DATA_UART("uartc", NULL, "tegra_uart.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, &periph_h_regs, uartc), + TEGRA_INIT_DATA_UART("uartd", NULL, "tegra_uart.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTD, 65, &periph_u_regs, uartd), + TEGRA_INIT_DATA_INT("3d", NULL, "3d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_3D, 24, &periph_l_regs, 0, gr_3d), + TEGRA_INIT_DATA_INT("2d", NULL, "2d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_2D, 21, &periph_l_regs, 0, gr_2d), + TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor), + TEGRA_INIT_DATA_INT8("vi", "vi", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi), + TEGRA_INIT_DATA_INT8("epp", NULL, "epp", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp), + TEGRA_INIT_DATA_INT8("msenc", NULL, "msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, &periph_h_regs, TEGRA_PERIPH_WAR_1005168, msenc), + TEGRA_INIT_DATA_INT8("tsec", NULL, "tsec", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_TSEC, 83, &periph_u_regs, 0, tsec), + TEGRA_INIT_DATA_INT8("host1x", NULL, "host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x), + TEGRA_INIT_DATA_MUX8("hdmi", NULL, "hdmi", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi), + TEGRA_INIT_DATA_MUX("cilab", "cilab", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILAB, 144, &periph_w_regs, 0, cilab), + TEGRA_INIT_DATA_MUX("cilcd", "cilcd", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILCD, 145, &periph_w_regs, 0, cilcd), + TEGRA_INIT_DATA_MUX("cile", "cile", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILE, 146, &periph_w_regs, 0, cile), + TEGRA_INIT_DATA_MUX("dsialp", "dsialp", "tegradc.0", mux_pllp_pllc_clkm, CLK_SOURCE_DSIALP, 147, &periph_w_regs, 0, dsialp), + TEGRA_INIT_DATA_MUX("dsiblp", "dsiblp", "tegradc.1", mux_pllp_pllc_clkm, CLK_SOURCE_DSIBLP, 148, &periph_w_regs, 0, dsiblp), + TEGRA_INIT_DATA_MUX("tsensor", NULL, "tegra-tsensor", mux_pllp_pllc_clkm_clk32, CLK_SOURCE_TSENSOR, 100, &periph_v_regs, TEGRA_PERIPH_ON_APB, tsensor), + TEGRA_INIT_DATA_MUX("actmon", NULL, "actmon", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_ACTMON, 119, &periph_v_regs, 0, actmon), + TEGRA_INIT_DATA_MUX8("extern1", NULL, "extern1", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN1, 120, &periph_v_regs, 0, extern1), + TEGRA_INIT_DATA_MUX8("extern2", NULL, "extern2", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN2, 121, &periph_v_regs, 0, extern2), + TEGRA_INIT_DATA_MUX8("extern3", NULL, "extern3", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN3, 122, &periph_v_regs, 0, extern3), + TEGRA_INIT_DATA_MUX("i2cslow", NULL, "i2cslow", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_I2CSLOW, 81, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2cslow), + TEGRA_INIT_DATA_INT8("se", NULL, "se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, &periph_v_regs, TEGRA_PERIPH_ON_APB, se), + TEGRA_INIT_DATA_INT_FLAGS("mselect", NULL, "mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, &periph_v_regs, 0, mselect, CLK_IGNORE_UNUSED), + TEGRA_INIT_DATA_MUX8("soc_therm", NULL, "soc_therm", mux_pllm_pllc_pllp_plla, CLK_SOURCE_SOC_THERM, 78, &periph_u_regs, TEGRA_PERIPH_ON_APB, soc_therm), + TEGRA_INIT_DATA_XUSB("xusb_host_src", "host_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, &periph_w_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_host_src), + TEGRA_INIT_DATA_XUSB("xusb_falcon_src", "falcon_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_falcon_src), + TEGRA_INIT_DATA_XUSB("xusb_fs_src", "fs_src", "tegra_xhci", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_fs_src), + TEGRA_INIT_DATA_XUSB("xusb_ss_src", "ss_src", "tegra_xhci", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_ss_src), + TEGRA_INIT_DATA_XUSB("xusb_dev_src", "dev_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, &periph_u_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_dev_src), + TEGRA_INIT_DATA_AUDIO("d_audio", "d_audio", "tegra30-ahub", CLK_SOURCE_D_AUDIO, 106, &periph_v_regs, TEGRA_PERIPH_ON_APB, d_audio), + TEGRA_INIT_DATA_AUDIO("dam0", NULL, "tegra30-dam.0", CLK_SOURCE_DAM0, 108, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam0), + TEGRA_INIT_DATA_AUDIO("dam1", NULL, "tegra30-dam.1", CLK_SOURCE_DAM1, 109, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam1), + TEGRA_INIT_DATA_AUDIO("dam2", NULL, "tegra30-dam.2", CLK_SOURCE_DAM2, 110, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam2), +}; + +static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = { + TEGRA_INIT_DATA_NODIV("disp1", NULL, "tegradc.0", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, &periph_l_regs, 0, disp1), + TEGRA_INIT_DATA_NODIV("disp2", NULL, "tegradc.1", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, &periph_l_regs, 0, disp2), +}; + +static __init void tegra114_periph_clk_init(void __iomem *clk_base) +{ + struct tegra_periph_init_data *data; + struct clk *clk; + int i; + u32 val; + + /* apbdma */ + clk = tegra_clk_register_periph_gate("apbdma", "clk_m", 0, clk_base, + 0, 34, &periph_h_regs, + periph_clk_enb_refcnt); + clks[apbdma] = clk; + + /* rtc */ + clk = tegra_clk_register_periph_gate("rtc", "clk_32k", + TEGRA_PERIPH_ON_APB | + TEGRA_PERIPH_NO_RESET, clk_base, + 0, 4, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "rtc-tegra"); + clks[rtc] = clk; + + /* kbc */ + clk = tegra_clk_register_periph_gate("kbc", "clk_32k", + TEGRA_PERIPH_ON_APB | + TEGRA_PERIPH_NO_RESET, clk_base, + 0, 36, &periph_h_regs, + periph_clk_enb_refcnt); + clks[kbc] = clk; + + /* timer */ + clk = tegra_clk_register_periph_gate("timer", "clk_m", 0, clk_base, + 0, 5, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "timer"); + clks[timer] = clk; + + /* kfuse */ + clk = tegra_clk_register_periph_gate("kfuse", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 40, + &periph_h_regs, periph_clk_enb_refcnt); + clks[kfuse] = clk; + + /* fuse */ + clk = tegra_clk_register_periph_gate("fuse", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 39, + &periph_h_regs, periph_clk_enb_refcnt); + clks[fuse] = clk; + + /* fuse_burn */ + clk = tegra_clk_register_periph_gate("fuse_burn", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 39, + &periph_h_regs, periph_clk_enb_refcnt); + clks[fuse_burn] = clk; + + /* apbif */ + clk = tegra_clk_register_periph_gate("apbif", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 107, + &periph_v_regs, periph_clk_enb_refcnt); + clks[apbif] = clk; + + /* hda2hdmi */ + clk = tegra_clk_register_periph_gate("hda2hdmi", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 128, + &periph_w_regs, periph_clk_enb_refcnt); + clks[hda2hdmi] = clk; + + /* vcp */ + clk = tegra_clk_register_periph_gate("vcp", "clk_m", 0, clk_base, 0, + 29, &periph_l_regs, + periph_clk_enb_refcnt); + clks[vcp] = clk; + + /* bsea */ + clk = tegra_clk_register_periph_gate("bsea", "clk_m", 0, clk_base, + 0, 62, &periph_h_regs, + periph_clk_enb_refcnt); + clks[bsea] = clk; + + /* bsev */ + clk = tegra_clk_register_periph_gate("bsev", "clk_m", 0, clk_base, + 0, 63, &periph_h_regs, + periph_clk_enb_refcnt); + clks[bsev] = clk; + + /* mipi-cal */ + clk = tegra_clk_register_periph_gate("mipi-cal", "clk_m", 0, clk_base, + 0, 56, &periph_h_regs, + periph_clk_enb_refcnt); + clks[mipi_cal] = clk; + + /* usbd */ + clk = tegra_clk_register_periph_gate("usbd", "clk_m", 0, clk_base, + 0, 22, &periph_l_regs, + periph_clk_enb_refcnt); + clks[usbd] = clk; + + /* usb2 */ + clk = tegra_clk_register_periph_gate("usb2", "clk_m", 0, clk_base, + 0, 58, &periph_h_regs, + periph_clk_enb_refcnt); + clks[usb2] = clk; + + /* usb3 */ + clk = tegra_clk_register_periph_gate("usb3", "clk_m", 0, clk_base, + 0, 59, &periph_h_regs, + periph_clk_enb_refcnt); + clks[usb3] = clk; + + /* csi */ + clk = tegra_clk_register_periph_gate("csi", "pll_p_out3", 0, clk_base, + 0, 52, &periph_h_regs, + periph_clk_enb_refcnt); + clks[csi] = clk; + + /* isp */ + clk = tegra_clk_register_periph_gate("isp", "clk_m", 0, clk_base, 0, + 23, &periph_l_regs, + periph_clk_enb_refcnt); + clks[isp] = clk; + + /* csus */ + clk = tegra_clk_register_periph_gate("csus", "clk_m", + TEGRA_PERIPH_NO_RESET, clk_base, 0, 92, + &periph_u_regs, periph_clk_enb_refcnt); + clks[csus] = clk; + + /* dds */ + clk = tegra_clk_register_periph_gate("dds", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 150, + &periph_w_regs, periph_clk_enb_refcnt); + clks[dds] = clk; + + /* dp2 */ + clk = tegra_clk_register_periph_gate("dp2", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 152, + &periph_w_regs, periph_clk_enb_refcnt); + clks[dp2] = clk; + + /* dtv */ + clk = tegra_clk_register_periph_gate("dtv", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 79, + &periph_u_regs, periph_clk_enb_refcnt); + clks[dtv] = clk; + + /* dsia */ + clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + clk_base + PLLD_BASE, 25, 1, 0, &pll_d_lock); + clks[dsia_mux] = clk; + clk = tegra_clk_register_periph_gate("dsia", "dsia_mux", 0, clk_base, + 0, 48, &periph_h_regs, + periph_clk_enb_refcnt); + clks[dsia] = clk; + + /* dsib */ + clk = clk_register_mux(NULL, "dsib_mux", mux_plld_out0_plld2_out0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + clk_base + PLLD2_BASE, 25, 1, 0, &pll_d2_lock); + clks[dsib_mux] = clk; + clk = tegra_clk_register_periph_gate("dsib", "dsib_mux", 0, clk_base, + 0, 82, &periph_u_regs, + periph_clk_enb_refcnt); + clks[dsib] = clk; + + /* xusb_hs_src */ + val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC); + val |= BIT(25); /* always select PLLU_60M */ + writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC); + + clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0, + 1, 1); + clks[xusb_hs_src] = clk; + + /* xusb_host */ + clk = tegra_clk_register_periph_gate("xusb_host", "xusb_host_src", 0, + clk_base, 0, 89, &periph_u_regs, + periph_clk_enb_refcnt); + clks[xusb_host] = clk; + + /* xusb_ss */ + clk = tegra_clk_register_periph_gate("xusb_ss", "xusb_ss_src", 0, + clk_base, 0, 156, &periph_w_regs, + periph_clk_enb_refcnt); + clks[xusb_host] = clk; + + /* xusb_dev */ + clk = tegra_clk_register_periph_gate("xusb_dev", "xusb_dev_src", 0, + clk_base, 0, 95, &periph_u_regs, + periph_clk_enb_refcnt); + clks[xusb_dev] = clk; + + /* emc */ + clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, + ARRAY_SIZE(mux_pllmcp_clkm), 0, + clk_base + CLK_SOURCE_EMC, + 29, 3, 0, NULL); + clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, + CLK_IGNORE_UNUSED, 57, &periph_h_regs, + periph_clk_enb_refcnt); + clks[emc] = clk; + + for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { + data = &tegra_periph_clk_list[i]; + clk = tegra_clk_register_periph(data->name, data->parent_names, + data->num_parents, &data->periph, + clk_base, data->offset, data->flags); + clks[data->clk_id] = clk; + } + + for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) { + data = &tegra_periph_nodiv_clk_list[i]; + clk = tegra_clk_register_periph_nodiv(data->name, + data->parent_names, data->num_parents, + &data->periph, clk_base, data->offset); + clks[data->clk_id] = clk; + } +} + +static struct tegra_cpu_car_ops tegra114_cpu_car_ops; + +static const struct of_device_id pmc_match[] __initconst = { + { .compatible = "nvidia,tegra114-pmc" }, + {}, +}; + +static __initdata struct tegra_clk_init_table init_table[] = { + {uarta, pll_p, 408000000, 0}, + {uartb, pll_p, 408000000, 0}, + {uartc, pll_p, 408000000, 0}, + {uartd, pll_p, 408000000, 1}, + {pll_a, clk_max, 564480000, 1}, + {pll_a_out0, clk_max, 11289600, 1}, + {extern1, pll_a_out0, 0, 1}, + {clk_out_1_mux, extern1, 0, 1}, + {clk_out_1, clk_max, 0, 1}, + {i2s0, pll_a_out0, 11289600, 0}, + {i2s1, pll_a_out0, 11289600, 0}, + {i2s2, pll_a_out0, 11289600, 0}, + {i2s3, pll_a_out0, 11289600, 0}, + {i2s4, pll_a_out0, 11289600, 0}, + {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ +}; + +static void __init tegra114_clock_apply_init_table(void) +{ + tegra_init_from_table(init_table, clks, clk_max); +} + +void __init tegra114_clock_init(struct device_node *np) +{ + struct device_node *node; + int i; + + clk_base = of_iomap(np, 0); + if (!clk_base) { + pr_err("ioremap tegra114 CAR failed\n"); + return; + } + + node = of_find_matching_node(NULL, pmc_match); + if (!node) { + pr_err("Failed to find pmc node\n"); + WARN_ON(1); + return; + } + + pmc_base = of_iomap(node, 0); + if (!pmc_base) { + pr_err("Can't map pmc registers\n"); + WARN_ON(1); + return; + } + + if (tegra114_osc_clk_init(clk_base) < 0) + return; + + tegra114_fixed_clk_init(clk_base); + tegra114_pll_init(clk_base, pmc_base); + tegra114_periph_clk_init(clk_base); + tegra114_audio_clk_init(clk_base); + tegra114_pmc_clk_init(pmc_base); + tegra114_super_clk_init(clk_base); + + for (i = 0; i < ARRAY_SIZE(clks); i++) { + if (IS_ERR(clks[i])) { + pr_err + ("Tegra114 clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + } + if (!clks[i]) + clks[i] = ERR_PTR(-EINVAL); + } + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + tegra_clk_apply_init_table = tegra114_clock_apply_init_table; + + tegra_cpu_car_ops = &tegra114_cpu_car_ops; +} -- cgit v1.1 From 27aa99dc0ec188771bd22f2188e56a571368303e Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:46 +0300 Subject: clk: tegra: devicetree match for nvidia,tegra114-car Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk.c | 1 + drivers/clk/tegra/clk.h | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 70b7a47..923ca7e 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -77,6 +77,7 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, static const struct of_device_id tegra_dt_clk_match[] = { { .compatible = "nvidia,tegra20-car", .data = tegra20_clock_init }, { .compatible = "nvidia,tegra30-car", .data = tegra30_clock_init }, + { .compatible = "nvidia,tegra114-car", .data = tegra114_clock_init }, { } }; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index fb48f04..e056562 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -583,6 +583,12 @@ void tegra30_clock_init(struct device_node *np); static inline void tegra30_clock_init(struct device_node *np) {} #endif /* CONFIG_ARCH_TEGRA_3x_SOC */ +#ifdef CONFIG_ARCH_TEGRA_114_SOC +void tegra114_clock_init(struct device_node *np); +#else +static inline void tegra114_clock_init(struct device_node *np) {} +#endif /* CONFIG_ARCH_TEGRA114_SOC */ + typedef void (*tegra_clk_apply_init_table_func)(void); extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; -- cgit v1.1 From c604283f52855a4568c18cfd4011bdcfeccf2c52 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:49 +0300 Subject: clk: tegra: Remove forced clk_enable of uartd The UART driver enables the console uart clock, so we don't need to do that anymore in this file. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index e2a7fa1..0db81dd 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -2011,7 +2011,7 @@ static __initdata struct tegra_clk_init_table init_table[] = { {uarta, pll_p, 408000000, 0}, {uartb, pll_p, 408000000, 0}, {uartc, pll_p, 408000000, 0}, - {uartd, pll_p, 408000000, 1}, + {uartd, pll_p, 408000000, 0}, {pll_a, clk_max, 564480000, 1}, {pll_a_out0, clk_max, 11289600, 1}, {extern1, pll_a_out0, 0, 1}, -- cgit v1.1 From 964ea47572b89589b61b553e44bbe9907d4f12a6 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 4 Apr 2013 17:13:54 -0600 Subject: clk: tegra: fix enum tegra114_clk to match binding A gap exists in the binding's clock ID definitions. Fix the clock driver to be consistent. This allows pclk to be looked up through device tree and prevents: ERROR: could not get clock /pmc:pclk(0) Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 0db81dd..d78e16e 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -760,7 +760,7 @@ enum tegra114_clk { pll_re_vco, pll_re_out, pll_e_out0, spdif_in_sync, i2s0_sync, i2s1_sync, i2s2_sync, i2s3_sync, i2s4_sync, vimclk_sync, audio0, audio1, audio2, audio3, audio4, spdif, clk_out_1, clk_out_2, clk_out_3, - blink, xusb_host_src, xusb_falcon_src, xusb_fs_src, xusb_ss_src, + blink, xusb_host_src = 252, xusb_falcon_src, xusb_fs_src, xusb_ss_src, xusb_dev_src, xusb_dev, xusb_hs_src, sclk, hclk, pclk, cclk_g, cclk_lp, /* Mux clocks */ -- cgit v1.1 From 6cec90826e9a3e505c9df91a62de59078f521dd3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 8 Apr 2013 21:35:25 +0900 Subject: clk: samsung: Fix compilation error Fixes the below compilation error during non-dt build. drivers/clk/samsung/clk.c: In function 'samsung_clk_of_register_fixed_ext': drivers/clk/samsung/clk.c:252:2: error: implicit declaration of function 'for_each_matching_node_and_match' [-Werror=implicit-function-declaration] drivers/clk/samsung/clk.c:252:60: error: expected ';' before '{' token Signed-off-by: Sachin Kamat Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 3a50d4f..cd3c40a 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -284,6 +284,7 @@ void __init samsung_clk_register_gate(struct samsung_gate_clock *list, * obtain the clock speed of all external fixed clock sources from device * tree and register it */ +#ifdef CONFIG_OF void __init samsung_clk_of_register_fixed_ext( struct samsung_fixed_rate_clock *fixed_rate_clk, unsigned int nr_fixed_rate_clk, @@ -300,6 +301,7 @@ void __init samsung_clk_of_register_fixed_ext( } samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk); } +#endif /* utility function to get the rate of a specified clock */ unsigned long _get_rate(const char *clk_name) -- cgit v1.1 From cdbf618ab8a326cb3bdc65e8adb74bac9c347e64 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 8 Apr 2013 15:24:47 +0900 Subject: clk: exynos4: export clocks required for fimc-is This patch adds clock indexes for ACLK_DIV0, ACLK_DIV1, ACLK_400_MCUISP, ACLK_MCUISP_DIV0, ACLK_MCUISP_DIV1, DIVACLK_400_MCUISP and DIVACLK_200 so these clocks are available to the consumers (Exynos4x12 FIMC-IS subsystem). While at it, indentation of the mux clocks table is corrected. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos4.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 17674da..7104669 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -175,6 +175,11 @@ enum exynos4_clks { /* mux clocks */ mout_fimc0 = 384, mout_fimc1, mout_fimc2, mout_fimc3, mout_cam0, mout_cam1, mout_csis0, mout_csis1, mout_g3d0, mout_g3d1, mout_g3d, + aclk400_mcuisp, + + /* div clocks */ + div_isp0 = 450, div_isp1, div_mcuisp0, div_mcuisp1, div_aclk200, + div_aclk400_mcuisp, nr_clks, }; @@ -429,7 +434,7 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { MUX(none, "mout_user_aclk266_gps", mout_user_aclk266_gps_p4x12, SRC_TOP1, 16, 1), MUX(aclk200, "aclk200", mout_user_aclk200_p4x12, SRC_TOP1, 20, 1), - MUX(none, "aclk400_mcuisp", mout_user_aclk400_mcuisp_p4x12, + MUX(aclk400_mcuisp, "aclk400_mcuisp", mout_user_aclk400_mcuisp_p4x12, SRC_TOP1, 24, 1), MUX(none, "mout_aclk200", aclk_p4412, SRC_TOP0, 12, 1), MUX(none, "mout_aclk100", aclk_p4412, SRC_TOP0, 16, 1), @@ -563,20 +568,21 @@ struct samsung_div_clock exynos4x12_div_clks[] __initdata = { DIV(none, "div_mdnie_pwm_pre0", "div_mdnie_pwm0", DIV_LCD0, 12, 4), DIV(none, "div_mipihsi", "mout_mipihsi", DIV_FSYS0, 20, 4), DIV(none, "div_jpeg", "mout_jpeg", E4X12_DIV_CAM1, 0, 4), - DIV(none, "div_aclk200", "mout_aclk200", DIV_TOP, 0, 3), + DIV(div_aclk200, "div_aclk200", "mout_aclk200", DIV_TOP, 0, 3), DIV(none, "div_aclk266_gps", "mout_aclk266_gps", DIV_TOP, 20, 3), - DIV(none, "div_aclk400_mcuisp", "mout_aclk400_mcuisp", DIV_TOP, 24, 3), + DIV(div_aclk400_mcuisp, "div_aclk400_mcuisp", "mout_aclk400_mcuisp", + DIV_TOP, 24, 3), DIV(none, "div_pwm_isp", "mout_pwm_isp", E4X12_DIV_ISP, 0, 4), DIV(none, "div_spi0_isp", "mout_spi0_isp", E4X12_DIV_ISP, 4, 4), DIV(none, "div_spi0_isp_pre", "div_spi0_isp", E4X12_DIV_ISP, 8, 8), DIV(none, "div_spi1_isp", "mout_spi1_isp", E4X12_DIV_ISP, 16, 4), DIV(none, "div_spi1_isp_pre", "div_spi1_isp", E4X12_DIV_ISP, 20, 8), DIV(none, "div_uart_isp", "mout_uart_isp", E4X12_DIV_ISP, 28, 4), - DIV(none, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3), - DIV(none, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3), + DIV(div_isp0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3), + DIV(div_isp1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3), DIV(none, "div_mpwm", "div_isp1", E4X12_DIV_ISP1, 0, 3), - DIV(none, "div_mcuisp0", "aclk400_mcuisp", E4X12_DIV_ISP1, 4, 3), - DIV(none, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, 8, 3), + DIV(div_mcuisp0, "div_mcuisp0", "aclk400_mcuisp", E4X12_DIV_ISP1, 4, 3), + DIV(div_mcuisp1, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, 8, 3), }; /* list of gate clocks supported in all exynos4 soc's */ -- cgit v1.1 From 688f7d8c9fef621c53c7b385ff6baf62bcb6b077 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 8 Apr 2013 15:28:12 +0900 Subject: clk: exynos5250: Fix divider values for sclk_mmc{0,1,2,3} In legacy setup, sclk_mmc{0,1,2,3} used PRE_RATIO bit-field (8-bit wide) instead of RATIO bit-field (4-bit wide) for dividing clock rate. With current common clock setup, we are using RATIO bit-field which is creating FIFO read errors while accessing eMMC. Changing over to use PRE_RATIO bit-field fixes this issue. dwmmc_exynos 12200000.dwmmc0: data FIFO error (status=00008020) mmcblk0: error -5 transferring data, sector 1, nr 7, cmd response 0x900, card status 0x0 end_request: I/O error, dev mmcblk0, sector 1 Signed-off-by: Tushar Behera CC: Thomas Abraham Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos5250.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index e5e733a..7290faa 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -276,10 +276,10 @@ struct samsung_div_clock exynos5250_div_clks[] __initdata = { DIV(none, "div_pcm0", "sclk_audio0", DIV_MAU, 4, 8), DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4), DIV(none, "div_usb3", "mout_usb3", DIV_FSYS0, 24, 4), - DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4), - DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4), - DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4), - DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4), + DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 8, 8), + DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 24, 8), + DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 8, 8), + DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 24, 8), DIV(none, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4), DIV(none, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4), DIV(none, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4), -- cgit v1.1 From df7ef462a2f8036430940b69871c762a92efead2 Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Wed, 12 Dec 2012 14:02:45 +0900 Subject: irqchip: exynos-combiner: Add set_irq_affinity function for combiner_irq This patch adds set_irq_affinity function for combiner_irq. We need this function to enable a arm-pmu because the pmu of exynos has combined type irqs. Reviewed-by: Thomas Abraham Signed-off-by: Chanho Park Signed-off-by: Kyungmin Park [kgene.kim@samsung.com: changes moved into drivers/irqchip/] Signed-off-by: Kukjin Kim --- drivers/irqchip/exynos-combiner.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 04d86a9..b5ff271 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -31,6 +31,7 @@ struct combiner_chip_data { unsigned int irq_offset; unsigned int irq_mask; void __iomem *base; + unsigned int parent_irq; }; static struct irq_domain *combiner_irq_domain; @@ -87,10 +88,28 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } +#ifdef CONFIG_SMP +static int combiner_set_affinity(struct irq_data *d, + const struct cpumask *mask_val, bool force) +{ + struct combiner_chip_data *chip_data = irq_data_get_irq_chip_data(d); + struct irq_chip *chip = irq_get_chip(chip_data->parent_irq); + struct irq_data *data = irq_get_irq_data(chip_data->parent_irq); + + if (chip && chip->irq_set_affinity) + return chip->irq_set_affinity(data, mask_val, force); + else + return -EINVAL; +} +#endif + static struct irq_chip combiner_chip = { - .name = "COMBINER", - .irq_mask = combiner_mask_irq, - .irq_unmask = combiner_unmask_irq, + .name = "COMBINER", + .irq_mask = combiner_mask_irq, + .irq_unmask = combiner_unmask_irq, +#ifdef CONFIG_SMP + .irq_set_affinity = combiner_set_affinity, +#endif }; static void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq) @@ -110,12 +129,13 @@ static void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int i } static void __init combiner_init_one(unsigned int combiner_nr, - void __iomem *base) + void __iomem *base, unsigned int irq) { combiner_data[combiner_nr].base = base; combiner_data[combiner_nr].irq_offset = irq_find_mapping( combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER); combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); + combiner_data[combiner_nr].parent_irq = irq; /* Disable all interrupts */ __raw_writel(combiner_data[combiner_nr].irq_mask, @@ -199,12 +219,12 @@ void __init combiner_init(void __iomem *combiner_base, } for (i = 0; i < max_nr; i++) { - combiner_init_one(i, combiner_base + (i >> 2) * 0x10); irq = IRQ_SPI(i); #ifdef CONFIG_OF if (np) irq = irq_of_parse_and_map(np, i); #endif + combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq); combiner_cascade_irq(i, irq); } } -- cgit v1.1 From 4e164dc5fa512ad66355b583f1f70c602e4717d6 Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Wed, 12 Dec 2012 14:02:49 +0900 Subject: irqchip: exynos-combiner: Correct combined IRQs for exynos4 This patch corrects combined IRQs for exynos4 series platform. The exynos4412 has four extra combined irq group and the exynos4212 has two more combined irqs than exynos4210. Each irq is mapped to IRQ_SPI(xx). Unfortunately, extra 4 combined IRQs isn't sequential. So, we need to map the irqs manually. Signed-off-by: Chanho Park Signed-off-by: Kyungmin Park [kgene.kim@samsung.com: changes moved into drivers/irqchip/] Signed-off-by: Kukjin Kim --- drivers/irqchip/exynos-combiner.c | 50 +++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index b5ff271..e8501db 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -112,16 +112,22 @@ static struct irq_chip combiner_chip = { #endif }; -static void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq) +static unsigned int max_combiner_nr(void) { - unsigned int max_nr; - if (soc_is_exynos5250()) - max_nr = EXYNOS5_MAX_COMBINER_NR; + return EXYNOS5_MAX_COMBINER_NR; + else if (soc_is_exynos4412()) + return EXYNOS4412_MAX_COMBINER_NR; + else if (soc_is_exynos4212()) + return EXYNOS4212_MAX_COMBINER_NR; else - max_nr = EXYNOS4_MAX_COMBINER_NR; + return EXYNOS4210_MAX_COMBINER_NR; +} - if (combiner_nr >= max_nr) +static void __init combiner_cascade_irq(unsigned int combiner_nr, + unsigned int irq) +{ + if (combiner_nr >= max_combiner_nr()) BUG(); if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0) BUG(); @@ -186,23 +192,38 @@ static struct irq_domain_ops combiner_irq_domain_ops = { .map = combiner_irq_domain_map, }; +static unsigned int exynos4x12_combiner_extra_irq(int group) +{ + switch (group) { + case 16: + return IRQ_SPI(107); + case 17: + return IRQ_SPI(108); + case 18: + return IRQ_SPI(48); + case 19: + return IRQ_SPI(42); + default: + return 0; + } +} + void __init combiner_init(void __iomem *combiner_base, struct device_node *np) { int i, irq, irq_base; unsigned int max_nr, nr_irq; + max_nr = max_combiner_nr(); + if (np) { if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { - pr_warning("%s: number of combiners not specified, " + pr_info("%s: number of combiners not specified, " "setting default as %d.\n", - __func__, EXYNOS4_MAX_COMBINER_NR); - max_nr = EXYNOS4_MAX_COMBINER_NR; + __func__, max_nr); } - } else { - max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR : - EXYNOS4_MAX_COMBINER_NR; } + nr_irq = max_nr * MAX_IRQ_IN_COMBINER; irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0); @@ -219,7 +240,10 @@ void __init combiner_init(void __iomem *combiner_base, } for (i = 0; i < max_nr; i++) { - irq = IRQ_SPI(i); + if (i < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250()) + irq = IRQ_SPI(i); + else + irq = exynos4x12_combiner_extra_irq(i); #ifdef CONFIG_OF if (np) irq = irq_of_parse_and_map(np, i); -- cgit v1.1 From 61fc41317666be400802ac793f47de816ef7bd57 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 19 Nov 2012 17:23:13 +0100 Subject: reset: Add reset controller API This adds a simple API for devices to request being reset by separate reset controller hardware and implements the reset signal device tree binding. Signed-off-by: Philipp Zabel Reviewed-by: Stephen Warren Reviewed-by: Shawn Guo Reviewed-by: Marek Vasut Reviewed-by: Pavel Machek --- drivers/Kconfig | 2 + drivers/Makefile | 3 + drivers/reset/Kconfig | 13 +++ drivers/reset/Makefile | 1 + drivers/reset/core.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 316 insertions(+) create mode 100644 drivers/reset/Kconfig create mode 100644 drivers/reset/Makefile create mode 100644 drivers/reset/core.c (limited to 'drivers') diff --git a/drivers/Kconfig b/drivers/Kconfig index 202fa6d..847f8e3 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -162,4 +162,6 @@ source "drivers/irqchip/Kconfig" source "drivers/ipack/Kconfig" +source "drivers/reset/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index dce39a9..1a64c4c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -37,6 +37,9 @@ obj-$(CONFIG_XEN) += xen/ # regulators early, since some subsystems rely on them to initialize obj-$(CONFIG_REGULATOR) += regulator/ +# reset controllers early, since gpu drivers might rely on them to initialize +obj-$(CONFIG_RESET_CONTROLLER) += reset/ + # tty/ comes before char/ so that the VT console is the boot-time # default. obj-y += tty/ diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig new file mode 100644 index 0000000..c9d04f7 --- /dev/null +++ b/drivers/reset/Kconfig @@ -0,0 +1,13 @@ +config ARCH_HAS_RESET_CONTROLLER + bool + +menuconfig RESET_CONTROLLER + bool "Reset Controller Support" + default y if ARCH_HAS_RESET_CONTROLLER + help + Generic Reset Controller support. + + This framework is designed to abstract reset handling of devices + via GPIOs or SoC-internal reset controller modules. + + If unsure, say no. diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile new file mode 100644 index 0000000..1e2d83f --- /dev/null +++ b/drivers/reset/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_RESET_CONTROLLER) += core.o diff --git a/drivers/reset/core.c b/drivers/reset/core.c new file mode 100644 index 0000000..a258277 --- /dev/null +++ b/drivers/reset/core.c @@ -0,0 +1,297 @@ +/* + * Reset Controller framework + * + * Copyright 2013 Philipp Zabel, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(reset_controller_list_mutex); +static LIST_HEAD(reset_controller_list); + +/** + * struct reset_control - a reset control + * @rcdev: a pointer to the reset controller device + * this reset control belongs to + * @id: ID of the reset controller in the reset + * controller device + */ +struct reset_control { + struct reset_controller_dev *rcdev; + struct device *dev; + unsigned int id; +}; + +/** + * of_reset_simple_xlate - translate reset_spec to the reset line number + * @rcdev: a pointer to the reset controller device + * @reset_spec: reset line specifier as found in the device tree + * @flags: a flags pointer to fill in (optional) + * + * This simple translation function should be used for reset controllers + * with 1:1 mapping, where reset lines can be indexed by number without gaps. + */ +int of_reset_simple_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells)) + return -EINVAL; + + if (reset_spec->args[0] >= rcdev->nr_resets) + return -EINVAL; + + return reset_spec->args[0]; +} +EXPORT_SYMBOL_GPL(of_reset_simple_xlate); + +/** + * reset_controller_register - register a reset controller device + * @rcdev: a pointer to the initialized reset controller device + */ +int reset_controller_register(struct reset_controller_dev *rcdev) +{ + if (!rcdev->of_xlate) { + rcdev->of_reset_n_cells = 1; + rcdev->of_xlate = of_reset_simple_xlate; + } + + mutex_lock(&reset_controller_list_mutex); + list_add(&rcdev->list, &reset_controller_list); + mutex_unlock(&reset_controller_list_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(reset_controller_register); + +/** + * reset_controller_unregister - unregister a reset controller device + * @rcdev: a pointer to the reset controller device + */ +void reset_controller_unregister(struct reset_controller_dev *rcdev) +{ + mutex_lock(&reset_controller_list_mutex); + list_del(&rcdev->list); + mutex_unlock(&reset_controller_list_mutex); +} +EXPORT_SYMBOL_GPL(reset_controller_unregister); + +/** + * reset_control_reset - reset the controlled device + * @rstc: reset controller + */ +int reset_control_reset(struct reset_control *rstc) +{ + if (rstc->rcdev->ops->reset) + return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(reset_control_reset); + +/** + * reset_control_assert - asserts the reset line + * @rstc: reset controller + */ +int reset_control_assert(struct reset_control *rstc) +{ + if (rstc->rcdev->ops->assert) + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(reset_control_assert); + +/** + * reset_control_deassert - deasserts the reset line + * @rstc: reset controller + */ +int reset_control_deassert(struct reset_control *rstc) +{ + if (rstc->rcdev->ops->deassert) + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(reset_control_deassert); + +/** + * reset_control_get - Lookup and obtain a reference to a reset controller. + * @dev: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * + * Use of id names is optional. + */ +struct reset_control *reset_control_get(struct device *dev, const char *id) +{ + struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); + struct reset_controller_dev *r, *rcdev; + struct of_phandle_args args; + int index = 0; + int rstc_id; + int ret; + + if (!dev) + return ERR_PTR(-EINVAL); + + if (id) + index = of_property_match_string(dev->of_node, + "reset-names", id); + ret = of_parse_phandle_with_args(dev->of_node, "resets", "#reset-cells", + index, &args); + if (ret) + return ERR_PTR(ret); + + mutex_lock(&reset_controller_list_mutex); + rcdev = NULL; + list_for_each_entry(r, &reset_controller_list, list) { + if (args.np == r->of_node) { + rcdev = r; + break; + } + } + of_node_put(args.np); + + if (!rcdev) { + mutex_unlock(&reset_controller_list_mutex); + return ERR_PTR(-ENODEV); + } + + rstc_id = rcdev->of_xlate(rcdev, &args); + if (rstc_id < 0) { + mutex_unlock(&reset_controller_list_mutex); + return ERR_PTR(rstc_id); + } + + try_module_get(rcdev->owner); + mutex_unlock(&reset_controller_list_mutex); + + rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); + if (!rstc) { + module_put(rstc->rcdev->owner); + return ERR_PTR(-ENOMEM); + } + + rstc->dev = dev; + rstc->rcdev = rcdev; + rstc->id = rstc_id; + + return rstc; +} +EXPORT_SYMBOL_GPL(reset_control_get); + +/** + * reset_control_put - free the reset controller + * @rstc: reset controller + */ + +void reset_control_put(struct reset_control *rstc) +{ + if (IS_ERR(rstc)) + return; + + module_put(rstc->rcdev->owner); + kfree(rstc); +} +EXPORT_SYMBOL_GPL(reset_control_put); + +static void devm_reset_control_release(struct device *dev, void *res) +{ + reset_control_put(*(struct reset_control **)res); +} + +/** + * devm_reset_control_get - resource managed reset_control_get() + * @dev: device to be reset by the controller + * @id: reset line name + * + * Managed reset_control_get(). For reset controllers returned from this + * function, reset_control_put() is called automatically on driver detach. + * See reset_control_get() for more information. + */ +struct reset_control *devm_reset_control_get(struct device *dev, const char *id) +{ + struct reset_control **ptr, *rstc; + + ptr = devres_alloc(devm_reset_control_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + rstc = reset_control_get(dev, id); + if (!IS_ERR(rstc)) { + *ptr = rstc; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return rstc; +} +EXPORT_SYMBOL_GPL(devm_reset_control_get); + +static int devm_reset_control_match(struct device *dev, void *res, void *data) +{ + struct reset_control **rstc = res; + if (WARN_ON(!rstc || !*rstc)) + return 0; + return *rstc == data; +} + +/** + * devm_reset_control_put - resource managed reset_control_put() + * @rstc: reset controller to free + * + * Deallocate a reset control allocated withd devm_reset_control_get(). + * This function will not need to be called normally, as devres will take + * care of freeing the resource. + */ +void devm_reset_control_put(struct reset_control *rstc) +{ + int ret; + + ret = devres_release(rstc->dev, devm_reset_control_release, + devm_reset_control_match, rstc); + if (ret) + WARN_ON(ret); +} +EXPORT_SYMBOL_GPL(devm_reset_control_put); + +/** + * device_reset - find reset controller associated with the device + * and perform reset + * @dev: device to be reset by the controller + * + * Convenience wrapper for reset_control_get() and reset_control_reset(). + * This is useful for the common case of devices with single, dedicated reset + * lines. + */ +int device_reset(struct device *dev) +{ + struct reset_control *rstc; + int ret; + + rstc = reset_control_get(dev, NULL); + if (IS_ERR(rstc)) + return PTR_ERR(rstc); + + ret = reset_control_reset(rstc); + + reset_control_put(rstc); + + return ret; +} +EXPORT_SYMBOL_GPL(device_reset); -- cgit v1.1 From 6034bb22d8387708075c083385e5d2e1072a4f33 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Apr 2013 08:02:53 +0300 Subject: reset: NULL deref on allocation failure "rstc" is NULL here and we should use "rcdev" instead of "rstc->rcdev". Signed-off-by: Dan Carpenter Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/reset/core.c b/drivers/reset/core.c index a258277..d1b6089 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -181,7 +181,7 @@ struct reset_control *reset_control_get(struct device *dev, const char *id) rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); if (!rstc) { - module_put(rstc->rcdev->owner); + module_put(rcdev->owner); return ERR_PTR(-ENOMEM); } -- cgit v1.1 From 06ff14c05426ec7b7600521c4e1ae19732797e15 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sun, 24 Mar 2013 01:12:25 +0000 Subject: irqchip: vt8500: Convert arch-vt8500 to new irqchip infrastructure This patch moves the arch-vt8500 irq code to drivers/irqchip and converts it to use the new IRQCHIP_DECLARE and irqchip_init. This allows the removal of some more functions from common.h Signed-off-by: Tony Prisk Signed-off-by: Olof Johansson --- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-vt8500.c | 259 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 drivers/irqchip/irq-vt8500.c (limited to 'drivers') diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index acf9895..48fbdf9 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_ARM_VIC) += irq-vic.o obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o +obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c new file mode 100644 index 0000000..d970595 --- /dev/null +++ b/drivers/irqchip/irq-vt8500.c @@ -0,0 +1,259 @@ +/* + * arch/arm/mach-vt8500/irq.c + * + * Copyright (C) 2012 Tony Prisk + * Copyright (C) 2010 Alexey Charkov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This file is copied and modified from the original irq.c provided by + * Alexey Charkov. Minor changes have been made for Device Tree Support. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "irqchip.h" + +#define VT8500_ICPC_IRQ 0x20 +#define VT8500_ICPC_FIQ 0x24 +#define VT8500_ICDC 0x40 /* Destination Control 64*u32 */ +#define VT8500_ICIS 0x80 /* Interrupt status, 16*u32 */ + +/* ICPC */ +#define ICPC_MASK 0x3F +#define ICPC_ROTATE BIT(6) + +/* IC_DCTR */ +#define ICDC_IRQ 0x00 +#define ICDC_FIQ 0x01 +#define ICDC_DSS0 0x02 +#define ICDC_DSS1 0x03 +#define ICDC_DSS2 0x04 +#define ICDC_DSS3 0x05 +#define ICDC_DSS4 0x06 +#define ICDC_DSS5 0x07 + +#define VT8500_INT_DISABLE 0 +#define VT8500_INT_ENABLE BIT(3) + +#define VT8500_TRIGGER_HIGH 0 +#define VT8500_TRIGGER_RISING BIT(5) +#define VT8500_TRIGGER_FALLING BIT(6) +#define VT8500_EDGE ( VT8500_TRIGGER_RISING \ + | VT8500_TRIGGER_FALLING) + +/* vt8500 has 1 intc, wm8505 and wm8650 have 2 */ +#define VT8500_INTC_MAX 2 + +struct vt8500_irq_data { + void __iomem *base; /* IO Memory base address */ + struct irq_domain *domain; /* Domain for this controller */ +}; + +/* Global variable for accessing io-mem addresses */ +static struct vt8500_irq_data intc[VT8500_INTC_MAX]; +static u32 active_cnt = 0; + +static void vt8500_irq_mask(struct irq_data *d) +{ + struct vt8500_irq_data *priv = d->domain->host_data; + void __iomem *base = priv->base; + void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4); + u8 edge, dctr; + u32 status; + + edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE; + if (edge) { + status = readl(stat_reg); + + status |= (1 << (d->hwirq & 0x1f)); + writel(status, stat_reg); + } else { + dctr = readb(base + VT8500_ICDC + d->hwirq); + dctr &= ~VT8500_INT_ENABLE; + writeb(dctr, base + VT8500_ICDC + d->hwirq); + } +} + +static void vt8500_irq_unmask(struct irq_data *d) +{ + struct vt8500_irq_data *priv = d->domain->host_data; + void __iomem *base = priv->base; + u8 dctr; + + dctr = readb(base + VT8500_ICDC + d->hwirq); + dctr |= VT8500_INT_ENABLE; + writeb(dctr, base + VT8500_ICDC + d->hwirq); +} + +static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct vt8500_irq_data *priv = d->domain->host_data; + void __iomem *base = priv->base; + u8 dctr; + + dctr = readb(base + VT8500_ICDC + d->hwirq); + dctr &= ~VT8500_EDGE; + + switch (flow_type) { + case IRQF_TRIGGER_LOW: + return -EINVAL; + case IRQF_TRIGGER_HIGH: + dctr |= VT8500_TRIGGER_HIGH; + __irq_set_handler_locked(d->irq, handle_level_irq); + break; + case IRQF_TRIGGER_FALLING: + dctr |= VT8500_TRIGGER_FALLING; + __irq_set_handler_locked(d->irq, handle_edge_irq); + break; + case IRQF_TRIGGER_RISING: + dctr |= VT8500_TRIGGER_RISING; + __irq_set_handler_locked(d->irq, handle_edge_irq); + break; + } + writeb(dctr, base + VT8500_ICDC + d->hwirq); + + return 0; +} + +static struct irq_chip vt8500_irq_chip = { + .name = "vt8500", + .irq_ack = vt8500_irq_mask, + .irq_mask = vt8500_irq_mask, + .irq_unmask = vt8500_irq_unmask, + .irq_set_type = vt8500_irq_set_type, +}; + +static void __init vt8500_init_irq_hw(void __iomem *base) +{ + u32 i; + + /* Enable rotating priority for IRQ */ + writel(ICPC_ROTATE, base + VT8500_ICPC_IRQ); + writel(0x00, base + VT8500_ICPC_FIQ); + + /* Disable all interrupts and route them to IRQ */ + for (i = 0; i < 64; i++) + writeb(VT8500_INT_DISABLE | ICDC_IRQ, base + VT8500_ICDC + i); +} + +static int vt8500_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq); + set_irq_flags(virq, IRQF_VALID); + + return 0; +} + +static struct irq_domain_ops vt8500_irq_domain_ops = { + .map = vt8500_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +asmlinkage void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) +{ + u32 stat, i; + int irqnr, virq; + void __iomem *base; + + /* Loop through each active controller */ + for (i=0; i VT8500_INTC_MAX\n", + __func__); + goto out; + } + + intc[active_cnt].base = of_iomap(np, 0); + intc[active_cnt].domain = irq_domain_add_linear(node, 64, + &vt8500_irq_domain_ops, &intc[active_cnt]); + + if (!intc[active_cnt].base) { + pr_err("%s: Unable to map IO memory\n", __func__); + goto out; + } + + if (!intc[active_cnt].domain) { + pr_err("%s: Unable to add irq domain!\n", __func__); + goto out; + } + + set_handle_irq(vt8500_handle_irq); + + vt8500_init_irq_hw(intc[active_cnt].base); + + pr_info("vt8500-irq: Added interrupt controller\n"); + + active_cnt++; + + /* check if this is a slaved controller */ + if (of_irq_count(np) != 0) { + /* check that we have the correct number of interrupts */ + if (of_irq_count(np) != 8) { + pr_err("%s: Incorrect IRQ map for slaved controller\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < 8; i++) { + irq = irq_of_parse_and_map(np, i); + enable_irq(irq); + } + + pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); + } +out: + return 0; +} + +IRQCHIP_DECLARE(vt8500_irq, "via,vt8500-intc", vt8500_irq_init); -- cgit v1.1 From 1f1a73571cb64713641cb75690c4686a4cabc96d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 9 Apr 2013 23:59:58 +0800 Subject: pinctrl: vt8500: wmt: Fix checking return value of pinctrl_register() pinctrl_register() returns NULL on error. Signed-off-by: Axel Lin Acked-by: Tony Prisk Acked-by: Linus Walleij Signed-off-by: Olof Johansson --- drivers/pinctrl/vt8500/pinctrl-wmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.c b/drivers/pinctrl/vt8500/pinctrl-wmt.c index 14400a7..ab63104 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wmt.c +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.c @@ -588,7 +588,7 @@ int wmt_pinctrl_probe(struct platform_device *pdev, data->dev = &pdev->dev; data->pctl_dev = pinctrl_register(&wmt_desc, &pdev->dev, data); - if (IS_ERR(data->pctl_dev)) { + if (!data->pctl_dev) { dev_err(&pdev->dev, "Failed to register pinctrl\n"); return -EINVAL; } -- cgit v1.1 From f4636d0ad7eee4741ef5146080e9ce57b9e2de0b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 19 Apr 2013 22:00:04 +0200 Subject: clocksource: exynos_mct: fix build error on non-DT There is currently no alternative implementation for of_irq_count when the function is not defined, and the declaration is hidden, so this works around calling an undeclared function. It should really not be needed. Signed-off-by: Arnd Bergmann --- drivers/clocksource/exynos_mct.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 509a601..6610268 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -541,7 +541,11 @@ static void __init mct_init_dt(struct device_node *np, unsigned int int_type) * timer irqs are specified after the four global timer * irqs are specified. */ +#ifdef CONFIG_OF nr_irqs = of_irq_count(np); +#else + nr_irqs = 0; +#endif for (i = MCT_L0_IRQ; i < nr_irqs; i++) mct_irqs[i] = irq_of_parse_and_map(np, i); -- cgit v1.1 From 37746c9a2dd28d52790dd84267b848c087a63b2e Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Tue, 23 Apr 2013 12:01:51 +0530 Subject: clk: exynos5250: Fix parent clock for sclk_mmc{0,1,2,3} commit 688f7d8c9fef ("clk: exynos5250: Fix divider values for sclk_mmc{0,1,2,3}") incorrectly sets the divider for sclk_mmc{0,1,2,3} to fix the wrong clock value. Though this fixed issue with Arndale, it created regressions for other boards like Snow. On Exynos5250, sclk_mmc is generated like below (as per the clock names in drivers/clk/samsung/clk-exynos5250.c) mout_group1_p ==> mout_mmc ==> div_mmc ==> div_mmc_pre => sclk_mmc Earlier div_mmc was set as the parent for sclk_mmc, hence div_mmc_pre was not getting referred in kernel code and depending on its value set during preboot, sclk_mmc value was different for various boards. Setting the correct clock generation path should fix the issues reported in above referenced commit. The changes committed during the earlier patch has also been reverted here. Reported-by: Doug Anderson Signed-off-by: Tushar Behera Tested-by: Doug Anderson Acked-by: Kukjin Kim Signed-off-by: Olof Johansson --- drivers/clk/samsung/clk-exynos5250.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 7290faa..bb54606 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -276,10 +276,10 @@ struct samsung_div_clock exynos5250_div_clks[] __initdata = { DIV(none, "div_pcm0", "sclk_audio0", DIV_MAU, 4, 8), DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4), DIV(none, "div_usb3", "mout_usb3", DIV_FSYS0, 24, 4), - DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 8, 8), - DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 24, 8), - DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 8, 8), - DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 24, 8), + DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4), + DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4), + DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4), + DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4), DIV(none, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4), DIV(none, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4), DIV(none, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4), @@ -421,13 +421,13 @@ struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { SRC_MASK_DISP1_0, 20, 0, 0), GATE(sclk_audio0, "sclk_audio0", "div_audio0", SRC_MASK_MAU, 0, CLK_SET_RATE_PARENT, 0), - GATE(sclk_mmc0, "sclk_mmc0", "div_mmc0", + GATE(sclk_mmc0, "sclk_mmc0", "div_mmc_pre0", SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0), - GATE(sclk_mmc1, "sclk_mmc1", "div_mmc1", + GATE(sclk_mmc1, "sclk_mmc1", "div_mmc_pre1", SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0), - GATE(sclk_mmc2, "sclk_mmc2", "div_mmc2", + GATE(sclk_mmc2, "sclk_mmc2", "div_mmc_pre2", SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0), - GATE(sclk_mmc3, "sclk_mmc3", "div_mmc3", + GATE(sclk_mmc3, "sclk_mmc3", "div_mmc_pre3", SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0), GATE(sclk_sata, "sclk_sata", "div_sata", SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), -- cgit v1.1 From bc8fd900c4d460b4e4bf785bb48bfced0ac9941b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 25 Apr 2013 16:49:05 +0200 Subject: irqchip: s3c24xx: add missing __init annotations The s3c24xx_init_intc and s3c2412_init_irq functions are only called at init time, and they call functions already marked __init, so they should be marked in the same way. This was reported as WARNING: vmlinux.o(.text+0x19e0b4): Section mismatch in reference from the function s3c2412_init_irq() to the function .init.text:s3c24xx_init_intc.constprop.8() The function s3c2412_init_irq() references the function __init s3c24xx_init_intc.constprop.8(). This is often because s3c2412_init_irq lacks a __init annotation or the annotation of s3c24xx_init_intc.constprop.8 is wrong. Signed-off-by: Arnd Bergmann Acked-by: Heiko Stuebner Cc: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 5e40b34..f750f55 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -534,7 +534,7 @@ static void s3c24xx_clear_intc(struct s3c_irq_intc *intc) } } -static struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, +static struct s3c_irq_intc * __init s3c24xx_init_intc(struct device_node *np, struct s3c_irq_data *irq_data, struct s3c_irq_intc *parent, unsigned long address) @@ -794,7 +794,7 @@ static struct s3c_irq_data init_s3c2412subint[32] = { { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* CF */ }; -void s3c2412_init_irq(void) +void __init s3c2412_init_irq(void) { pr_info("S3C2412: IRQ Support\n"); -- cgit v1.1