From 1dc7d8374dccf2815294ceb6a1092253f45ba860 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 27 Feb 2014 14:44:17 +0100 Subject: ARM/leds: move ARM Versatile LED driver to leds subsystem Now that we have converted this driver to a real platform device module-based thing, we move the driver down into the LEDs subsystem and rename the config option to LEDS_VERSATILE. Cc: Richard Purdie Cc: Russell King Cc: Pawel Moll Acked-by: Bryan Wu Signed-off-by: Linus Walleij --- drivers/leds/Kconfig | 8 +++ drivers/leds/Makefile | 1 + drivers/leds/leds-versatile.c | 110 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 drivers/leds/leds-versatile.c (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 72156c123033..93235f7378ba 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -487,6 +487,14 @@ config LEDS_BLINKM This option enables support for the BlinkM RGB LED connected through I2C. Say Y to enable support for the BlinkM LED. +config LEDS_VERSATILE + bool "LED support for the ARM Versatile and RealView" + depends on ARCH_REALVIEW || ARCH_VERSATILE + depends on LEDS_CLASS + help + This option enabled support for the LEDs on the ARM Versatile + and RealView boards. Say Y to enabled these. + comment "LED Triggers" source "drivers/leds/trigger/Kconfig" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3cd76dbd9be2..8b4c956e11ba 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o +obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-versatile.c b/drivers/leds/leds-versatile.c new file mode 100644 index 000000000000..80553022d661 --- /dev/null +++ b/drivers/leds/leds-versatile.c @@ -0,0 +1,110 @@ +/* + * Driver for the 8 user LEDs found on the RealViews and Versatiles + * Based on DaVinci's DM365 board code + * + * License terms: GNU General Public License (GPL) version 2 + * Author: Linus Walleij + */ +#include +#include +#include +#include +#include +#include +#include + +struct versatile_led { + void __iomem *base; + struct led_classdev cdev; + u8 mask; +}; + +/* + * The triggers lines up below will only be used if the + * LED triggers are compiled in. + */ +static const struct { + const char *name; + const char *trigger; +} versatile_leds[] = { + { "versatile:0", "heartbeat", }, + { "versatile:1", "mmc0", }, + { "versatile:2", "cpu0" }, + { "versatile:3", "cpu1" }, + { "versatile:4", "cpu2" }, + { "versatile:5", "cpu3" }, + { "versatile:6", }, + { "versatile:7", }, +}; + +static void versatile_led_set(struct led_classdev *cdev, + enum led_brightness b) +{ + struct versatile_led *led = container_of(cdev, + struct versatile_led, cdev); + u32 reg = readl(led->base); + + if (b != LED_OFF) + reg |= led->mask; + else + reg &= ~led->mask; + writel(reg, led->base); +} + +static enum led_brightness versatile_led_get(struct led_classdev *cdev) +{ + struct versatile_led *led = container_of(cdev, + struct versatile_led, cdev); + u32 reg = readl(led->base); + + return (reg & led->mask) ? LED_FULL : LED_OFF; +} + +static int versatile_leds_probe(struct platform_device *dev) +{ + int i; + struct resource *res; + void __iomem *base; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + /* All off */ + writel(0, base); + for (i = 0; i < ARRAY_SIZE(versatile_leds); i++) { + struct versatile_led *led; + + led = kzalloc(sizeof(*led), GFP_KERNEL); + if (!led) + break; + + led->base = base; + led->cdev.name = versatile_leds[i].name; + led->cdev.brightness_set = versatile_led_set; + led->cdev.brightness_get = versatile_led_get; + led->cdev.default_trigger = versatile_leds[i].trigger; + led->mask = BIT(i); + + if (led_classdev_register(NULL, &led->cdev) < 0) { + kfree(led); + break; + } + } + + return 0; +} + +static struct platform_driver versatile_leds_driver = { + .driver = { + .name = "versatile-leds", + }, + .probe = versatile_leds_probe, +}; + +module_platform_driver(versatile_leds_driver); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_DESCRIPTION("ARM Versatile LED driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 06654acb6698a92bb7e6deb6897006ed501cdc47 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Wed, 19 Feb 2014 09:25:36 +0900 Subject: clk: samsung: add pll_6552 variant for s3c2416 According to the manual s3c2416 and s3c2450 use a pll 6552 and 6553 and while the pll_6553 matches exactly the one already implemented the pll_6552 differs to the one from the s3c64xx series. The change is solely in the bit locations of the mdiv and pdiv values. All calculations are the same for both implementatons and even the proposed divider-values for specific frequencies in the manuals are the same. Therefore implement a variant that simply uses the changed bit locations if necessary. Signed-off-by: Heiko Stuebner Acked-by: Tomasz Figa Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-pll.c | 12 ++++++++++-- drivers/clk/samsung/clk-pll.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 81e6d2f49aa0..f9a35a612705 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -564,7 +564,9 @@ static const struct clk_ops samsung_pll46xx_clk_min_ops = { #define PLL6552_PDIV_MASK 0x3f #define PLL6552_SDIV_MASK 0x7 #define PLL6552_MDIV_SHIFT 16 +#define PLL6552_MDIV_SHIFT_2416 14 #define PLL6552_PDIV_SHIFT 8 +#define PLL6552_PDIV_SHIFT_2416 5 #define PLL6552_SDIV_SHIFT 0 static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw, @@ -575,8 +577,13 @@ static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw, u64 fvco = parent_rate; pll_con = __raw_readl(pll->con_reg); - mdiv = (pll_con >> PLL6552_MDIV_SHIFT) & PLL6552_MDIV_MASK; - pdiv = (pll_con >> PLL6552_PDIV_SHIFT) & PLL6552_PDIV_MASK; + if (pll->type == pll_6552_s3c2416) { + mdiv = (pll_con >> PLL6552_MDIV_SHIFT_2416) & PLL6552_MDIV_MASK; + pdiv = (pll_con >> PLL6552_PDIV_SHIFT_2416) & PLL6552_PDIV_MASK; + } else { + mdiv = (pll_con >> PLL6552_MDIV_SHIFT) & PLL6552_MDIV_MASK; + pdiv = (pll_con >> PLL6552_PDIV_SHIFT) & PLL6552_PDIV_MASK; + } sdiv = (pll_con >> PLL6552_SDIV_SHIFT) & PLL6552_SDIV_MASK; fvco *= mdiv; @@ -773,6 +780,7 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk, init.ops = &samsung_pll36xx_clk_ops; break; case pll_6552: + case pll_6552_s3c2416: init.ops = &samsung_pll6552_clk_ops; break; case pll_6553: diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index 6c39030080fb..ddf9029c13c9 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h @@ -24,6 +24,7 @@ enum samsung_pll_type { pll_4650, pll_4650c, pll_6552, + pll_6552_s3c2416, pll_6553, }; -- cgit v1.2.3 From a951b1d91dcca8d373c92666c5e006de8234d34b Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Wed, 19 Feb 2014 09:25:41 +0900 Subject: clk: samsung: add plls used by the s3c2443 The s3c2443 uses different plls that are not present yet. Therefore add the two needed types. Signed-off-by: Heiko Stuebner Acked-by: Tomasz Figa Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-pll.c | 72 +++++++++++++++++++++++++++++++++++++++++++ drivers/clk/samsung/clk-pll.h | 2 ++ 2 files changed, 74 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index f9a35a612705..8c9c015a4538 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -58,6 +58,72 @@ static long samsung_pll_round_rate(struct clk_hw *hw, return rate_table[i - 1].rate; } +/* + * PLL2126 Clock Type + */ + +#define PLL2126_MDIV_MASK (0xff) +#define PLL2126_PDIV_MASK (0x3f) +#define PLL2126_SDIV_MASK (0x3) +#define PLL2126_MDIV_SHIFT (16) +#define PLL2126_PDIV_SHIFT (8) +#define PLL2126_SDIV_SHIFT (0) + +static unsigned long samsung_pll2126_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 pll_con, mdiv, pdiv, sdiv; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL2126_MDIV_SHIFT) & PLL2126_MDIV_MASK; + pdiv = (pll_con >> PLL2126_PDIV_SHIFT) & PLL2126_PDIV_MASK; + sdiv = (pll_con >> PLL2126_SDIV_SHIFT) & PLL2126_SDIV_MASK; + + fvco *= (mdiv + 8); + do_div(fvco, (pdiv + 2) << sdiv); + + return (unsigned long)fvco; +} + +static const struct clk_ops samsung_pll2126_clk_ops = { + .recalc_rate = samsung_pll2126_recalc_rate, +}; + +/* + * PLL3000 Clock Type + */ + +#define PLL3000_MDIV_MASK (0xff) +#define PLL3000_PDIV_MASK (0x3) +#define PLL3000_SDIV_MASK (0x3) +#define PLL3000_MDIV_SHIFT (16) +#define PLL3000_PDIV_SHIFT (8) +#define PLL3000_SDIV_SHIFT (0) + +static unsigned long samsung_pll3000_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 pll_con, mdiv, pdiv, sdiv; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL3000_MDIV_SHIFT) & PLL3000_MDIV_MASK; + pdiv = (pll_con >> PLL3000_PDIV_SHIFT) & PLL3000_PDIV_MASK; + sdiv = (pll_con >> PLL3000_SDIV_SHIFT) & PLL3000_SDIV_MASK; + + fvco *= (2 * (mdiv + 8)); + do_div(fvco, pdiv << sdiv); + + return (unsigned long)fvco; +} + +static const struct clk_ops samsung_pll3000_clk_ops = { + .recalc_rate = samsung_pll3000_recalc_rate, +}; + /* * PLL35xx Clock Type */ @@ -753,6 +819,12 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk, } switch (pll_clk->type) { + case pll_2126: + init.ops = &samsung_pll2126_clk_ops; + break; + case pll_3000: + init.ops = &samsung_pll3000_clk_ops; + break; /* clk_ops for 35xx and 2550 are similar */ case pll_35xx: case pll_2550: diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index ddf9029c13c9..5b64bdbb0906 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h @@ -13,6 +13,8 @@ #define __SAMSUNG_CLK_PLL_H enum samsung_pll_type { + pll_2126, + pll_3000, pll_35xx, pll_36xx, pll_2550, -- cgit v1.2.3 From 61fbb1d278cf09f29d67629b139b3ca08acbf177 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Wed, 19 Feb 2014 09:25:49 +0900 Subject: clk: samsung: add clock-driver for s3c2416, s3c2443 and s3c2450 The three SoCs share a common clock tree which only differs in the existence of some special clocks. As with all parts common to these three SoCs the driver is named after the s3c2443, as it was the first SoC introducing this structure and there exists no other label to describe this s3c24xx epoch. The clock structure is built according to the manuals of the included SoCs and might include changes in comparison to the previous clock structure. As an example the sclk_uart gate was never handled previously and the div_uart was made to be the clock used by the serial driver. Signed-off-by: Heiko Stuebner Acked-by: Tomasz Figa Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-s3c2443.c | 462 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 463 insertions(+) create mode 100644 drivers/clk/samsung/clk-s3c2443.c (limited to 'drivers') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 8eb4799237f0..4c892c63a8dd 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o +obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c new file mode 100644 index 000000000000..8e4f4517e95c --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2443.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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 S3C2443 and following SoCs. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "clk-pll.h" + +/* S3C2416 clock controller register offsets */ +#define LOCKCON0 0x00 +#define LOCKCON1 0x04 +#define MPLLCON 0x10 +#define EPLLCON 0x18 +#define EPLLCON_K 0x1C +#define CLKSRC 0x20 +#define CLKDIV0 0x24 +#define CLKDIV1 0x28 +#define CLKDIV2 0x2C +#define HCLKCON 0x30 +#define PCLKCON 0x34 +#define SCLKCON 0x38 + +/* the soc types */ +enum supported_socs { + S3C2416, + S3C2443, + S3C2450, +}; + +/* list of PLLs to be registered */ +enum s3c2443_plls { + mpll, epll, +}; + +static void __iomem *reg_base; + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *s3c2443_save; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static unsigned long s3c2443_clk_regs[] __initdata = { + LOCKCON0, + LOCKCON1, + MPLLCON, + EPLLCON, + EPLLCON_K, + CLKSRC, + CLKDIV0, + CLKDIV1, + CLKDIV2, + PCLKCON, + HCLKCON, + SCLKCON, +}; + +static int s3c2443_clk_suspend(void) +{ + samsung_clk_save(reg_base, s3c2443_save, + ARRAY_SIZE(s3c2443_clk_regs)); + + return 0; +} + +static void s3c2443_clk_resume(void) +{ + samsung_clk_restore(reg_base, s3c2443_save, + ARRAY_SIZE(s3c2443_clk_regs)); +} + +static struct syscore_ops s3c2443_clk_syscore_ops = { + .suspend = s3c2443_clk_suspend, + .resume = s3c2443_clk_resume, +}; + +static void s3c2443_clk_sleep_init(void) +{ + s3c2443_save = samsung_clk_alloc_reg_dump(s3c2443_clk_regs, + ARRAY_SIZE(s3c2443_clk_regs)); + if (!s3c2443_save) { + pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", + __func__); + return; + } + + register_syscore_ops(&s3c2443_clk_syscore_ops); + return; +} +#else +static void s3c2443_clk_sleep_init(void) {} +#endif + +PNAME(epllref_p) = { "mpllref", "mpllref", "xti", "ext" }; +PNAME(esysclk_p) = { "epllref", "epll" }; +PNAME(mpllref_p) = { "xti", "mdivclk" }; +PNAME(msysclk_p) = { "mpllref", "mpll" }; +PNAME(armclk_p) = { "armdiv" , "hclk" }; +PNAME(i2s0_p) = { "div_i2s0", "ext_i2s", "epllref", "epllref" }; + +struct samsung_mux_clock s3c2443_common_muxes[] __initdata = { + MUX(0, "epllref", epllref_p, CLKSRC, 7, 2), + MUX(ESYSCLK, "esysclk", esysclk_p, CLKSRC, 6, 1), + MUX(0, "mpllref", mpllref_p, CLKSRC, 3, 1), + MUX_A(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1, "msysclk"), + MUX_A(ARMCLK, "armclk", armclk_p, CLKDIV0, 13, 1, "armclk"), + MUX(0, "mux_i2s0", i2s0_p, CLKSRC, 14, 2), +}; + +static struct clk_div_table hclk_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 3, .div = 4 }, + { /* sentinel */ }, +}; + +static struct clk_div_table mdivclk_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 3 }, + { .val = 2, .div = 5 }, + { .val = 3, .div = 7 }, + { .val = 4, .div = 9 }, + { .val = 5, .div = 11 }, + { .val = 6, .div = 13 }, + { .val = 7, .div = 15 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2443_common_dividers[] __initdata = { + DIV_T(0, "mdivclk", "xti", CLKDIV0, 6, 3, mdivclk_d), + DIV(0, "prediv", "msysclk", CLKDIV0, 4, 2), + DIV_T(HCLK, "hclk", "prediv", CLKDIV0, 0, 2, hclk_d), + DIV(PCLK, "pclk", "hclk", CLKDIV0, 2, 1), + DIV(0, "div_hsspi0_epll", "esysclk", CLKDIV1, 24, 2), + DIV(0, "div_fimd", "esysclk", CLKDIV1, 16, 8), + DIV(0, "div_i2s0", "esysclk", CLKDIV1, 12, 4), + DIV(0, "div_uart", "esysclk", CLKDIV1, 8, 4), + DIV(0, "div_hsmmc1", "esysclk", CLKDIV1, 6, 2), + DIV(0, "div_usbhost", "esysclk", CLKDIV1, 4, 2), +}; + +struct samsung_gate_clock s3c2443_common_gates[] __initdata = { + GATE(SCLK_HSMMC_EXT, "sclk_hsmmcext", "ext", SCLKCON, 13, 0, 0), + GATE(SCLK_HSMMC1, "sclk_hsmmc1", "div_hsmmc1", SCLKCON, 12, 0, 0), + GATE(SCLK_FIMD, "sclk_fimd", "div_fimd", SCLKCON, 10, 0, 0), + GATE(SCLK_I2S0, "sclk_i2s0", "mux_i2s0", SCLKCON, 9, 0, 0), + GATE(SCLK_UART, "sclk_uart", "div_uart", SCLKCON, 8, 0, 0), + GATE(SCLK_USBH, "sclk_usbhost", "div_usbhost", SCLKCON, 1, 0, 0), + GATE(HCLK_DRAM, "dram", "hclk", HCLKCON, 19, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_SSMC, "ssmc", "hclk", HCLKCON, 18, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_HSMMC1, "hsmmc1", "hclk", HCLKCON, 16, 0, 0), + GATE(HCLK_USBD, "usb-device", "hclk", HCLKCON, 12, 0, 0), + GATE(HCLK_USBH, "usb-host", "hclk", HCLKCON, 11, 0, 0), + GATE(HCLK_LCD, "lcd", "hclk", HCLKCON, 9, 0, 0), + GATE(HCLK_DMA5, "dma5", "hclk", HCLKCON, 5, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA4, "dma4", "hclk", HCLKCON, 4, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA3, "dma3", "hclk", HCLKCON, 3, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA2, "dma2", "hclk", HCLKCON, 2, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA1, "dma1", "hclk", HCLKCON, 1, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA0, "dma0", "hclk", HCLKCON, 0, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_GPIO, "gpio", "pclk", PCLKCON, 13, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_RTC, "rtc", "pclk", PCLKCON, 12, 0, 0), + GATE(PCLK_WDT, "wdt", "pclk", PCLKCON, 11, 0, 0), + GATE(PCLK_PWM, "pwm", "pclk", PCLKCON, 10, 0, 0), + GATE(PCLK_I2S0, "i2s0", "pclk", PCLKCON, 9, 0, 0), + GATE(PCLK_AC97, "ac97", "pclk", PCLKCON, 8, 0, 0), + GATE(PCLK_ADC, "adc", "pclk", PCLKCON, 7, 0, 0), + GATE(PCLK_SPI0, "spi0", "pclk", PCLKCON, 6, 0, 0), + GATE(PCLK_I2C0, "i2c0", "pclk", PCLKCON, 4, 0, 0), + GATE(PCLK_UART3, "uart3", "pclk", PCLKCON, 3, 0, 0), + GATE(PCLK_UART2, "uart2", "pclk", PCLKCON, 2, 0, 0), + GATE(PCLK_UART1, "uart1", "pclk", PCLKCON, 1, 0, 0), + GATE(PCLK_UART0, "uart0", "pclk", PCLKCON, 0, 0, 0), +}; + +struct samsung_clock_alias s3c2443_common_aliases[] __initdata = { + ALIAS(HCLK, NULL, "hclk"), + ALIAS(HCLK_SSMC, NULL, "nand"), + ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"), + ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"), + ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"), + ALIAS(PCLK_UART3, "s3c2440-uart.3", "uart"), + ALIAS(PCLK_UART0, "s3c2440-uart.0", "clk_uart_baud2"), + ALIAS(PCLK_UART1, "s3c2440-uart.1", "clk_uart_baud2"), + ALIAS(PCLK_UART2, "s3c2440-uart.2", "clk_uart_baud2"), + ALIAS(PCLK_UART3, "s3c2440-uart.3", "clk_uart_baud2"), + ALIAS(SCLK_UART, NULL, "clk_uart_baud3"), + ALIAS(PCLK_PWM, NULL, "timers"), + ALIAS(PCLK_RTC, NULL, "rtc"), + ALIAS(PCLK_WDT, NULL, "watchdog"), + ALIAS(PCLK_ADC, NULL, "adc"), + ALIAS(PCLK_I2C0, "s3c2410-i2c.0", "i2c"), + ALIAS(HCLK_USBD, NULL, "usb-device"), + ALIAS(HCLK_USBH, NULL, "usb-host"), + ALIAS(SCLK_USBH, NULL, "usb-bus-host"), + ALIAS(PCLK_SPI0, "s3c2443-spi.0", "spi"), + ALIAS(PCLK_SPI0, "s3c2443-spi.0", "spi_busclk0"), + ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "hsmmc"), + ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.0"), + ALIAS(PCLK_I2S0, "samsung-i2s.0", "iis"), + ALIAS(SCLK_I2S0, NULL, "i2s-if"), + ALIAS(HCLK_LCD, NULL, "lcd"), + ALIAS(SCLK_FIMD, NULL, "sclk_fimd"), +}; + +/* S3C2416 specific clocks */ + +static struct samsung_pll_clock s3c2416_pll_clks[] __initdata = { + [mpll] = PLL(pll_6552_s3c2416, 0, "mpll", "mpllref", + LOCKCON0, MPLLCON, NULL), + [epll] = PLL(pll_6553, 0, "epll", "epllref", + LOCKCON1, EPLLCON, NULL), +}; + +PNAME(s3c2416_hsmmc0_p) = { "sclk_hsmmc0", "sclk_hsmmcext" }; +PNAME(s3c2416_hsmmc1_p) = { "sclk_hsmmc1", "sclk_hsmmcext" }; +PNAME(s3c2416_hsspi0_p) = { "hsspi0_epll", "hsspi0_mpll" }; + +static struct clk_div_table armdiv_s3c2416_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 5, .div = 6 }, + { .val = 7, .div = 8 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2416_dividers[] __initdata = { + DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 3, armdiv_s3c2416_d), + DIV(0, "div_hsspi0_mpll", "msysclk", CLKDIV2, 0, 4), + DIV(0, "div_hsmmc0", "esysclk", CLKDIV2, 6, 2), +}; + +struct samsung_mux_clock s3c2416_muxes[] __initdata = { + MUX(MUX_HSMMC0, "mux_hsmmc0", s3c2416_hsmmc0_p, CLKSRC, 16, 1), + MUX(MUX_HSMMC1, "mux_hsmmc1", s3c2416_hsmmc1_p, CLKSRC, 17, 1), + MUX(MUX_HSSPI0, "mux_hsspi0", s3c2416_hsspi0_p, CLKSRC, 18, 1), +}; + +struct samsung_gate_clock s3c2416_gates[] __initdata = { + GATE(0, "hsspi0_mpll", "div_hsspi0_mpll", SCLKCON, 19, 0, 0), + GATE(0, "hsspi0_epll", "div_hsspi0_epll", SCLKCON, 14, 0, 0), + GATE(0, "sclk_hsmmc0", "div_hsmmc0", SCLKCON, 6, 0, 0), + GATE(HCLK_2D, "2d", "hclk", HCLKCON, 20, 0, 0), + GATE(HCLK_HSMMC0, "hsmmc0", "hclk", HCLKCON, 15, 0, 0), + GATE(HCLK_IROM, "irom", "hclk", HCLKCON, 13, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_PCM, "pcm", "pclk", PCLKCON, 19, 0, 0), +}; + +struct samsung_clock_alias s3c2416_aliases[] __initdata = { + ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "hsmmc"), + ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"), + ALIAS(MUX_HSMMC0, "s3c-sdhci.0", "mmc_busclk.2"), + ALIAS(MUX_HSMMC1, "s3c-sdhci.1", "mmc_busclk.2"), + ALIAS(MUX_HSSPI0, "s3c2443-spi.0", "spi_busclk2"), + ALIAS(ARMDIV, NULL, "armdiv"), +}; + +/* S3C2443 specific clocks */ + +static struct samsung_pll_clock s3c2443_pll_clks[] __initdata = { + [mpll] = PLL(pll_3000, 0, "mpll", "mpllref", + LOCKCON0, MPLLCON, NULL), + [epll] = PLL(pll_2126, 0, "epll", "epllref", + LOCKCON1, EPLLCON, NULL), +}; + +static struct clk_div_table armdiv_s3c2443_d[] = { + { .val = 0, .div = 1 }, + { .val = 8, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 9, .div = 4 }, + { .val = 10, .div = 6 }, + { .val = 11, .div = 8 }, + { .val = 13, .div = 12 }, + { .val = 15, .div = 16 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2443_dividers[] __initdata = { + DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 4, armdiv_s3c2443_d), + DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4), +}; + +struct samsung_gate_clock s3c2443_gates[] __initdata = { + GATE(SCLK_HSSPI0, "sclk_hsspi0", "div_hsspi0_epll", SCLKCON, 14, 0, 0), + GATE(SCLK_CAM, "sclk_cam", "div_cam", SCLKCON, 11, 0, 0), + GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_CAM, "cam", "hclk", HCLKCON, 8, 0, 0), + GATE(PCLK_SPI1, "spi1", "pclk", PCLKCON, 15, 0, 0), + GATE(PCLK_SDI, "sdi", "pclk", PCLKCON, 5, 0, 0), +}; + +struct samsung_clock_alias s3c2443_aliases[] __initdata = { + ALIAS(SCLK_HSSPI0, "s3c2443-spi.0", "spi_busclk2"), + ALIAS(SCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.2"), + ALIAS(SCLK_CAM, NULL, "camif-upll"), + ALIAS(PCLK_SPI1, "s3c2410-spi.0", "spi"), + ALIAS(PCLK_SDI, NULL, "sdi"), + ALIAS(HCLK_CFC, NULL, "cfc"), + ALIAS(ARMDIV, NULL, "armdiv"), +}; + +/* S3C2450 specific clocks */ + +PNAME(s3c2450_cam_p) = { "div_cam", "hclk" }; +PNAME(s3c2450_hsspi1_p) = { "hsspi1_epll", "hsspi1_mpll" }; +PNAME(i2s1_p) = { "div_i2s1", "ext_i2s", "epllref", "epllref" }; + +struct samsung_div_clock s3c2450_dividers[] __initdata = { + DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4), + DIV(0, "div_hsspi1_epll", "esysclk", CLKDIV2, 24, 2), + DIV(0, "div_hsspi1_mpll", "msysclk", CLKDIV2, 16, 4), + DIV(0, "div_i2s1", "esysclk", CLKDIV2, 12, 4), +}; + +struct samsung_mux_clock s3c2450_muxes[] __initdata = { + MUX(0, "mux_cam", s3c2450_cam_p, CLKSRC, 20, 1), + MUX(MUX_HSSPI1, "mux_hsspi1", s3c2450_hsspi1_p, CLKSRC, 19, 1), + MUX(0, "mux_i2s1", i2s1_p, CLKSRC, 12, 2), +}; + +struct samsung_gate_clock s3c2450_gates[] __initdata = { + GATE(SCLK_I2S1, "sclk_i2s1", "div_i2s1", SCLKCON, 5, 0, 0), + GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, 0, 0), + GATE(HCLK_CAM, "cam", "hclk", HCLKCON, 8, 0, 0), + GATE(HCLK_DMA7, "dma7", "hclk", HCLKCON, 7, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA6, "dma6", "hclk", HCLKCON, 6, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_I2S1, "i2s1", "pclk", PCLKCON, 17, 0, 0), + GATE(PCLK_I2C1, "i2c1", "pclk", PCLKCON, 16, 0, 0), + GATE(PCLK_SPI1, "spi1", "pclk", PCLKCON, 14, 0, 0), +}; + +struct samsung_clock_alias s3c2450_aliases[] __initdata = { + ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi"), + ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi_busclk0"), + ALIAS(MUX_HSSPI1, "s3c2443-spi.1", "spi_busclk2"), + ALIAS(PCLK_I2C1, "s3c2410-i2c.1", "i2c"), +}; + +/* + * fixed rate clocks generated outside the soc + * Only necessary until the devicetree-move is complete + */ +struct samsung_fixed_rate_clock s3c2443_common_frate_clks[] __initdata = { + FRATE(0, "xti", NULL, CLK_IS_ROOT, 0), + FRATE(0, "ext", NULL, CLK_IS_ROOT, 0), + FRATE(0, "ext_i2s", NULL, CLK_IS_ROOT, 0), + FRATE(0, "ext_uart", NULL, CLK_IS_ROOT, 0), +}; + +static void __init s3c2443_common_clk_register_fixed_ext(unsigned long xti_f) +{ + s3c2443_common_frate_clks[0].fixed_rate = xti_f; + samsung_clk_register_fixed_rate(s3c2443_common_frate_clks, + ARRAY_SIZE(s3c2443_common_frate_clks)); +} + +void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, + int current_soc, + void __iomem *base) +{ + reg_base = base; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } + + samsung_clk_init(np, reg_base, NR_CLKS); + + /* Register external clocks only in non-dt cases */ + if (!np) + s3c2443_common_clk_register_fixed_ext(xti_f); + + /* Register PLLs. */ + if (current_soc == S3C2416 || current_soc == S3C2450) + samsung_clk_register_pll(s3c2416_pll_clks, + ARRAY_SIZE(s3c2416_pll_clks), reg_base); + else + samsung_clk_register_pll(s3c2443_pll_clks, + ARRAY_SIZE(s3c2443_pll_clks), reg_base); + + /* Register common internal clocks. */ + samsung_clk_register_mux(s3c2443_common_muxes, + ARRAY_SIZE(s3c2443_common_muxes)); + samsung_clk_register_div(s3c2443_common_dividers, + ARRAY_SIZE(s3c2443_common_dividers)); + samsung_clk_register_gate(s3c2443_common_gates, + ARRAY_SIZE(s3c2443_common_gates)); + samsung_clk_register_alias(s3c2443_common_aliases, + ARRAY_SIZE(s3c2443_common_aliases)); + + /* Register SoC-specific clocks. */ + switch (current_soc) { + case S3C2450: + samsung_clk_register_div(s3c2450_dividers, + ARRAY_SIZE(s3c2450_dividers)); + samsung_clk_register_mux(s3c2450_muxes, + ARRAY_SIZE(s3c2450_muxes)); + samsung_clk_register_gate(s3c2450_gates, + ARRAY_SIZE(s3c2450_gates)); + samsung_clk_register_alias(s3c2450_aliases, + ARRAY_SIZE(s3c2450_aliases)); + /* fall through, as s3c2450 extends the s3c2416 clocks */ + case S3C2416: + samsung_clk_register_div(s3c2416_dividers, + ARRAY_SIZE(s3c2416_dividers)); + samsung_clk_register_mux(s3c2416_muxes, + ARRAY_SIZE(s3c2416_muxes)); + samsung_clk_register_gate(s3c2416_gates, + ARRAY_SIZE(s3c2416_gates)); + samsung_clk_register_alias(s3c2416_aliases, + ARRAY_SIZE(s3c2416_aliases)); + break; + case S3C2443: + samsung_clk_register_div(s3c2443_dividers, + ARRAY_SIZE(s3c2443_dividers)); + samsung_clk_register_gate(s3c2443_gates, + ARRAY_SIZE(s3c2443_gates)); + samsung_clk_register_alias(s3c2443_aliases, + ARRAY_SIZE(s3c2443_aliases)); + break; + } + + s3c2443_clk_sleep_init(); +} + +static void __init s3c2416_clk_init(struct device_node *np) +{ + s3c2443_common_clk_init(np, 0, S3C2416, 0); +} +CLK_OF_DECLARE(s3c2416_clk, "samsung,s3c2416-clock", s3c2416_clk_init); + +static void __init s3c2443_clk_init(struct device_node *np) +{ + s3c2443_common_clk_init(np, 0, S3C2443, 0); +} +CLK_OF_DECLARE(s3c2443_clk, "samsung,s3c2443-clock", s3c2443_clk_init); + +static void __init s3c2450_clk_init(struct device_node *np) +{ + s3c2443_common_clk_init(np, 0, S3C2450, 0); +} +CLK_OF_DECLARE(s3c2450_clk, "samsung,s3c2450-clock", s3c2450_clk_init); -- cgit v1.2.3 From ea5d6a8d3ee606086118d3b4d4b3a49693e92960 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 25 Feb 2014 09:50:43 +0900 Subject: clk: samsung: add plls used by the early s3c24xx cpus The manuals do not give them explicit names like in later socs, so more generic names with a s3c2410-prefix were used for them. As it was common to do so in the previous implementation, functionality to change the pll rate is already included. Signed-off-by: Heiko Stuebner Acked-by: Mike Turquette Acked-by: Tomasz Figa Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-pll.c | 182 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/samsung/clk-pll.h | 3 + 2 files changed, 185 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 8c9c015a4538..7fb0a28e65d5 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -11,6 +11,7 @@ #include #include +#include #include "clk.h" #include "clk-pll.h" @@ -700,6 +701,169 @@ static const struct clk_ops samsung_pll6553_clk_ops = { .recalc_rate = samsung_pll6553_recalc_rate, }; +/* + * PLL Clock Type of S3C24XX before S3C2443 + */ + +#define PLLS3C2410_MDIV_MASK (0xff) +#define PLLS3C2410_PDIV_MASK (0x1f) +#define PLLS3C2410_SDIV_MASK (0x3) +#define PLLS3C2410_MDIV_SHIFT (12) +#define PLLS3C2410_PDIV_SHIFT (4) +#define PLLS3C2410_SDIV_SHIFT (0) + +#define PLLS3C2410_ENABLE_REG_OFFSET 0x10 + +static unsigned long samsung_s3c2410_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 pll_con, mdiv, pdiv, sdiv; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK; + pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK; + sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK; + + fvco *= (mdiv + 8); + do_div(fvco, (pdiv + 2) << sdiv); + + return (unsigned int)fvco; +} + +static unsigned long samsung_s3c2440_mpll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 pll_con, mdiv, pdiv, sdiv; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK; + pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK; + sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK; + + fvco *= (2 * (mdiv + 8)); + do_div(fvco, (pdiv + 2) << sdiv); + + return (unsigned int)fvco; +} + +static int samsung_s3c2410_pll_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + const struct samsung_pll_rate_table *rate; + u32 tmp; + + /* Get required rate settings from table */ + rate = samsung_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, __clk_get_name(hw->clk)); + return -EINVAL; + } + + tmp = __raw_readl(pll->con_reg); + + /* Change PLL PMS values */ + tmp &= ~((PLLS3C2410_MDIV_MASK << PLLS3C2410_MDIV_SHIFT) | + (PLLS3C2410_PDIV_MASK << PLLS3C2410_PDIV_SHIFT) | + (PLLS3C2410_SDIV_MASK << PLLS3C2410_SDIV_SHIFT)); + tmp |= (rate->mdiv << PLLS3C2410_MDIV_SHIFT) | + (rate->pdiv << PLLS3C2410_PDIV_SHIFT) | + (rate->sdiv << PLLS3C2410_SDIV_SHIFT); + __raw_writel(tmp, pll->con_reg); + + /* Time to settle according to the manual */ + udelay(300); + + return 0; +} + +static int samsung_s3c2410_pll_enable(struct clk_hw *hw, int bit, bool enable) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 pll_en = __raw_readl(pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET); + u32 pll_en_orig = pll_en; + + if (enable) + pll_en &= ~BIT(bit); + else + pll_en |= BIT(bit); + + __raw_writel(pll_en, pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET); + + /* if we started the UPLL, then allow to settle */ + if (enable && (pll_en_orig & BIT(bit))) + udelay(300); + + return 0; +} + +static int samsung_s3c2410_mpll_enable(struct clk_hw *hw) +{ + return samsung_s3c2410_pll_enable(hw, 5, true); +} + +static void samsung_s3c2410_mpll_disable(struct clk_hw *hw) +{ + samsung_s3c2410_pll_enable(hw, 5, false); +} + +static int samsung_s3c2410_upll_enable(struct clk_hw *hw) +{ + return samsung_s3c2410_pll_enable(hw, 7, true); +} + +static void samsung_s3c2410_upll_disable(struct clk_hw *hw) +{ + samsung_s3c2410_pll_enable(hw, 7, false); +} + +static const struct clk_ops samsung_s3c2410_mpll_clk_min_ops = { + .recalc_rate = samsung_s3c2410_pll_recalc_rate, + .enable = samsung_s3c2410_mpll_enable, + .disable = samsung_s3c2410_mpll_disable, +}; + +static const struct clk_ops samsung_s3c2410_upll_clk_min_ops = { + .recalc_rate = samsung_s3c2410_pll_recalc_rate, + .enable = samsung_s3c2410_upll_enable, + .disable = samsung_s3c2410_upll_disable, +}; + +static const struct clk_ops samsung_s3c2440_mpll_clk_min_ops = { + .recalc_rate = samsung_s3c2440_mpll_recalc_rate, + .enable = samsung_s3c2410_mpll_enable, + .disable = samsung_s3c2410_mpll_disable, +}; + +static const struct clk_ops samsung_s3c2410_mpll_clk_ops = { + .recalc_rate = samsung_s3c2410_pll_recalc_rate, + .enable = samsung_s3c2410_mpll_enable, + .disable = samsung_s3c2410_mpll_disable, + .round_rate = samsung_pll_round_rate, + .set_rate = samsung_s3c2410_pll_set_rate, +}; + +static const struct clk_ops samsung_s3c2410_upll_clk_ops = { + .recalc_rate = samsung_s3c2410_pll_recalc_rate, + .enable = samsung_s3c2410_upll_enable, + .disable = samsung_s3c2410_upll_disable, + .round_rate = samsung_pll_round_rate, + .set_rate = samsung_s3c2410_pll_set_rate, +}; + +static const struct clk_ops samsung_s3c2440_mpll_clk_ops = { + .recalc_rate = samsung_s3c2440_mpll_recalc_rate, + .enable = samsung_s3c2410_mpll_enable, + .disable = samsung_s3c2410_mpll_disable, + .round_rate = samsung_pll_round_rate, + .set_rate = samsung_s3c2410_pll_set_rate, +}; + /* * PLL2550x Clock Type */ @@ -866,6 +1030,24 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk, else init.ops = &samsung_pll46xx_clk_ops; break; + case pll_s3c2410_mpll: + if (!pll->rate_table) + init.ops = &samsung_s3c2410_mpll_clk_min_ops; + else + init.ops = &samsung_s3c2410_mpll_clk_ops; + break; + case pll_s3c2410_upll: + if (!pll->rate_table) + init.ops = &samsung_s3c2410_upll_clk_min_ops; + else + init.ops = &samsung_s3c2410_upll_clk_ops; + break; + case pll_s3c2440_mpll: + if (!pll->rate_table) + init.ops = &samsung_s3c2440_mpll_clk_min_ops; + else + init.ops = &samsung_s3c2440_mpll_clk_ops; + break; default: pr_warn("%s: Unknown pll type for pll clk %s\n", __func__, pll_clk->name); diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index 5b64bdbb0906..6428bcc6df6f 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h @@ -28,6 +28,9 @@ enum samsung_pll_type { pll_6552, pll_6552_s3c2416, pll_6553, + pll_s3c2410_mpll, + pll_s3c2410_upll, + pll_s3c2440_mpll, }; #define PLL_35XX_RATE(_rate, _m, _p, _s) \ -- cgit v1.2.3 From ca2e90ac1809c49c2306df4e23e17dad67c785b6 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 25 Feb 2014 09:50:44 +0900 Subject: clk: samsung: add clock controller driver for s3c2412 This driver can handle the clock controller in the s3c2412 soc. The clock structure is built according to the manuals of the included SoCs and might include changes in comparison to the previous clock structure. Signed-off-by: Heiko Stuebner Reviewed-by: Tomasz Figa Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-s3c2412.c | 269 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+) create mode 100644 drivers/clk/samsung/clk-s3c2412.c (limited to 'drivers') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 4c892c63a8dd..77313e27bc39 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o +obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c new file mode 100644 index 000000000000..0f11a07d5c01 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2412.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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 S3C2412 and S3C2413. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "clk-pll.h" + +#define LOCKTIME 0x00 +#define MPLLCON 0x04 +#define UPLLCON 0x08 +#define CLKCON 0x0c +#define CLKDIVN 0x14 +#define CLKSRC 0x1c + +/* list of PLLs to be registered */ +enum s3c2412_plls { + mpll, upll, +}; + +static void __iomem *reg_base; + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *s3c2412_save; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static unsigned long s3c2412_clk_regs[] __initdata = { + LOCKTIME, + MPLLCON, + UPLLCON, + CLKCON, + CLKDIVN, + CLKSRC, +}; + +static int s3c2412_clk_suspend(void) +{ + samsung_clk_save(reg_base, s3c2412_save, + ARRAY_SIZE(s3c2412_clk_regs)); + + return 0; +} + +static void s3c2412_clk_resume(void) +{ + samsung_clk_restore(reg_base, s3c2412_save, + ARRAY_SIZE(s3c2412_clk_regs)); +} + +static struct syscore_ops s3c2412_clk_syscore_ops = { + .suspend = s3c2412_clk_suspend, + .resume = s3c2412_clk_resume, +}; + +static void s3c2412_clk_sleep_init(void) +{ + s3c2412_save = samsung_clk_alloc_reg_dump(s3c2412_clk_regs, + ARRAY_SIZE(s3c2412_clk_regs)); + if (!s3c2412_save) { + pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", + __func__); + return; + } + + register_syscore_ops(&s3c2412_clk_syscore_ops); + return; +} +#else +static void s3c2412_clk_sleep_init(void) {} +#endif + +static struct clk_div_table divxti_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, + { .val = 4, .div = 8 }, + { .val = 5, .div = 10 }, + { .val = 6, .div = 12 }, + { .val = 7, .div = 14 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2412_dividers[] __initdata = { + DIV_T(0, "div_xti", "xti", CLKSRC, 0, 3, divxti_d), + DIV(0, "div_cam", "mux_cam", CLKDIVN, 16, 4), + DIV(0, "div_i2s", "mux_i2s", CLKDIVN, 12, 4), + DIV(0, "div_uart", "mux_uart", CLKDIVN, 8, 4), + DIV(0, "div_usb", "mux_usb", CLKDIVN, 6, 1), + DIV(0, "div_hclk_half", "hclk", CLKDIVN, 5, 1), + DIV(ARMDIV, "armdiv", "msysclk", CLKDIVN, 3, 1), + DIV(PCLK, "pclk", "hclk", CLKDIVN, 2, 1), + DIV(HCLK, "hclk", "armdiv", CLKDIVN, 0, 2), +}; + +struct samsung_fixed_factor_clock s3c2412_ffactor[] __initdata = { + FFACTOR(0, "ff_hclk", "hclk", 2, 1, CLK_SET_RATE_PARENT), +}; + +/* + * The first two use the OM[4] setting, which is not readable from + * software, so assume it is set to xti. + */ +PNAME(erefclk_p) = { "xti", "xti", "xti", "ext" }; +PNAME(urefclk_p) = { "xti", "xti", "xti", "ext" }; + +PNAME(camclk_p) = { "usysclk", "hclk" }; +PNAME(usbclk_p) = { "usysclk", "hclk" }; +PNAME(i2sclk_p) = { "erefclk", "mpll" }; +PNAME(uartclk_p) = { "erefclk", "mpll" }; +PNAME(usysclk_p) = { "urefclk", "upll" }; +PNAME(msysclk_p) = { "mdivclk", "mpll" }; +PNAME(mdivclk_p) = { "xti", "div_xti" }; +PNAME(armclk_p) = { "armdiv", "hclk" }; + +struct samsung_mux_clock s3c2412_muxes[] __initdata = { + MUX(0, "erefclk", erefclk_p, CLKSRC, 14, 2), + MUX(0, "urefclk", urefclk_p, CLKSRC, 12, 2), + MUX(0, "mux_cam", camclk_p, CLKSRC, 11, 1), + MUX(0, "mux_usb", usbclk_p, CLKSRC, 10, 1), + MUX(0, "mux_i2s", i2sclk_p, CLKSRC, 9, 1), + MUX(0, "mux_uart", uartclk_p, CLKSRC, 8, 1), + MUX(USYSCLK, "usysclk", usysclk_p, CLKSRC, 5, 1), + MUX(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1), + MUX(MDIVCLK, "mdivclk", mdivclk_p, CLKSRC, 3, 1), + MUX(ARMCLK, "armclk", armclk_p, CLKDIVN, 4, 1), +}; + +static struct samsung_pll_clock s3c2412_plls[] __initdata = { + [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti", + LOCKTIME, MPLLCON, NULL), + [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "urefclk", + LOCKTIME, UPLLCON, NULL), +}; + +struct samsung_gate_clock s3c2412_gates[] __initdata = { + GATE(PCLK_WDT, "wdt", "pclk", CLKCON, 28, 0, 0), + GATE(PCLK_SPI, "spi", "pclk", CLKCON, 27, 0, 0), + GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 26, 0, 0), + GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 25, 0, 0), + GATE(PCLK_ADC, "adc", "pclk", CLKCON, 24, 0, 0), + GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 23, 0, 0), + GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 22, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 21, 0, 0), + GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 20, 0, 0), + GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 19, 0, 0), + GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 18, 0, 0), + GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 17, 0, 0), + GATE(PCLK_USBD, "usb-device", "pclk", CLKCON, 16, 0, 0), + GATE(SCLK_CAM, "sclk_cam", "div_cam", CLKCON, 15, 0, 0), + GATE(SCLK_UART, "sclk_uart", "div_uart", CLKCON, 14, 0, 0), + GATE(SCLK_I2S, "sclk_i2s", "div_i2s", CLKCON, 13, 0, 0), + GATE(SCLK_USBH, "sclk_usbh", "div_usb", CLKCON, 12, 0, 0), + GATE(SCLK_USBD, "sclk_usbd", "div_usb", CLKCON, 11, 0, 0), + GATE(HCLK_HALF, "hclk_half", "div_hclk_half", CLKCON, 10, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_X2, "hclkx2", "ff_hclk", CLKCON, 9, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_SDRAM, "sdram", "hclk", CLKCON, 8, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0), + GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0), + GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0), + GATE(HCLK_DMA3, "dma3", "hclk", CLKCON, 3, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA2, "dma2", "hclk", CLKCON, 2, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA1, "dma1", "hclk", CLKCON, 1, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA0, "dma0", "hclk", CLKCON, 0, CLK_IGNORE_UNUSED, 0), +}; + +struct samsung_clock_alias s3c2412_aliases[] __initdata = { + ALIAS(PCLK_UART0, "s3c2412-uart.0", "uart"), + ALIAS(PCLK_UART1, "s3c2412-uart.1", "uart"), + ALIAS(PCLK_UART2, "s3c2412-uart.2", "uart"), + ALIAS(PCLK_UART0, "s3c2412-uart.0", "clk_uart_baud2"), + ALIAS(PCLK_UART1, "s3c2412-uart.1", "clk_uart_baud2"), + ALIAS(PCLK_UART2, "s3c2412-uart.2", "clk_uart_baud2"), + ALIAS(SCLK_UART, NULL, "clk_uart_baud3"), + ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"), + ALIAS(PCLK_ADC, NULL, "adc"), + ALIAS(PCLK_RTC, NULL, "rtc"), + ALIAS(PCLK_PWM, NULL, "timers"), + ALIAS(HCLK_LCD, NULL, "lcd"), + ALIAS(PCLK_USBD, NULL, "usb-device"), + ALIAS(SCLK_USBD, NULL, "usb-bus-gadget"), + ALIAS(HCLK_USBH, NULL, "usb-host"), + ALIAS(SCLK_USBH, NULL, "usb-bus-host"), + ALIAS(ARMCLK, NULL, "armclk"), + ALIAS(HCLK, NULL, "hclk"), + ALIAS(MPLL, NULL, "mpll"), + ALIAS(MSYSCLK, NULL, "fclk"), +}; + +/* + * fixed rate clocks generated outside the soc + * Only necessary until the devicetree-move is complete + */ +#define XTI 1 +struct samsung_fixed_rate_clock s3c2412_common_frate_clks[] __initdata = { + FRATE(XTI, "xti", NULL, CLK_IS_ROOT, 0), + FRATE(0, "ext", NULL, CLK_IS_ROOT, 0), +}; + +static void __init s3c2412_common_clk_register_fixed_ext(unsigned long xti_f, + unsigned long ext_f) +{ + /* xtal alias is necessary for the current cpufreq driver */ + struct samsung_clock_alias xti_alias = ALIAS(XTI, NULL, "xtal"); + + s3c2412_common_frate_clks[0].fixed_rate = xti_f; + s3c2412_common_frate_clks[1].fixed_rate = ext_f; + samsung_clk_register_fixed_rate(s3c2412_common_frate_clks, + ARRAY_SIZE(s3c2412_common_frate_clks)); + + samsung_clk_register_alias(&xti_alias, 1); +} + +void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f, + unsigned long ext_f, void __iomem *base) +{ + reg_base = base; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } + + samsung_clk_init(np, reg_base, NR_CLKS); + + /* Register external clocks only in non-dt cases */ + if (!np) + s3c2412_common_clk_register_fixed_ext(xti_f, ext_f); + + /* Register PLLs. */ + samsung_clk_register_pll(s3c2412_plls, ARRAY_SIZE(s3c2412_plls), + reg_base); + + /* Register common internal clocks. */ + samsung_clk_register_mux(s3c2412_muxes, ARRAY_SIZE(s3c2412_muxes)); + samsung_clk_register_div(s3c2412_dividers, + ARRAY_SIZE(s3c2412_dividers)); + samsung_clk_register_gate(s3c2412_gates, ARRAY_SIZE(s3c2412_gates)); + samsung_clk_register_fixed_factor(s3c2412_ffactor, + ARRAY_SIZE(s3c2412_ffactor)); + samsung_clk_register_alias(s3c2412_aliases, + ARRAY_SIZE(s3c2412_aliases)); + + s3c2412_clk_sleep_init(); +} + +static void __init s3c2412_clk_init(struct device_node *np) +{ + s3c2412_common_clk_init(np, 0, 0, 0); +} +CLK_OF_DECLARE(s3c2412_clk, "samsung,s3c2412-clock", s3c2412_clk_init); -- cgit v1.2.3 From d8b532578f39fdec159105bc415938910351a699 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 9 May 2014 05:48:44 +0900 Subject: ARM: S3C24XX: cpufreq-utils: don't write raw values to MPLLCON when using ccf The s3c24xx cpufreq driver needs to change the mpll speed and was doing this by writing raw values from a translation table into the MPLLCON register. Change this to use a regular clk_set_rate call when using the common clock framework and only write the raw value in the samsung_clock case. The s3c cpufreq driver does already aquire the mpll, so simply add a reference to struct s3c_cpufreq_config to let set_fvco access it. While struct clk is opaque the differenciation between samsung clock and common clock is kept, as the samsung-clock mpll clk does not implement a real set_rate. Signed-off-by: Heiko Stuebner Acked-by: Tomasz Figa Signed-off-by: Kukjin Kim --- drivers/cpufreq/s3c24xx-cpufreq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index be1b2b5c9753..227ebf7c1eea 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -141,6 +141,7 @@ static int s3c_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) static void s3c_cpufreq_setfvco(struct s3c_cpufreq_config *cfg) { + cfg->mpll = _clk_mpll; (cfg->info->set_fvco)(cfg); } -- cgit v1.2.3 From 5799ea12a41286d9588155a1abd828f43bc63d6b Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 9 May 2014 05:48:51 +0900 Subject: clk: samsung: add clock driver for external clock outputs This adds a driver for controlling the external clock outputs of s3c24xx architectures including the dclk muxes and dividers. The driver at the moment only supports the legacy non-dt boards using these clock outputs. The clock-output control itself is part of the system-controller mainly controlled by the pinctrl drivers. So it should most likely be integrated there for dt platforms. Signed-off-by: Heiko Stuebner Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-s3c2410-dclk.c | 440 +++++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+) create mode 100644 drivers/clk/samsung/clk-s3c2410-dclk.c (limited to 'drivers') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 77313e27bc39..9892de4978e3 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o +obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c new file mode 100644 index 000000000000..8d8dff005c10 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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 s3c24xx external clock output. + */ + +#include +#include +#include "clk.h" + +/* legacy access to misccr, until dt conversion is finished */ +#include +#include + +#define MUX_DCLK0 0 +#define MUX_DCLK1 1 +#define DIV_DCLK0 2 +#define DIV_DCLK1 3 +#define GATE_DCLK0 4 +#define GATE_DCLK1 5 +#define MUX_CLKOUT0 6 +#define MUX_CLKOUT1 7 +#define DCLK_MAX_CLKS (MUX_CLKOUT1 + 1) + +enum supported_socs { + S3C2410, + S3C2412, + S3C2440, + S3C2443, +}; + +struct s3c24xx_dclk_drv_data { + const char **clkout0_parent_names; + int clkout0_num_parents; + const char **clkout1_parent_names; + int clkout1_num_parents; + const char **mux_parent_names; + int mux_num_parents; +}; + +/* + * Clock for output-parent selection in misccr + */ + +struct s3c24xx_clkout { + struct clk_hw hw; + u32 mask; + u8 shift; +}; + +#define to_s3c24xx_clkout(_hw) container_of(_hw, struct s3c24xx_clkout, hw) + +static u8 s3c24xx_clkout_get_parent(struct clk_hw *hw) +{ + struct s3c24xx_clkout *clkout = to_s3c24xx_clkout(hw); + int num_parents = __clk_get_num_parents(hw->clk); + u32 val; + + val = readl_relaxed(S3C24XX_MISCCR) >> clkout->shift; + val >>= clkout->shift; + val &= clkout->mask; + + if (val >= num_parents) + return -EINVAL; + + return val; +} + +static int s3c24xx_clkout_set_parent(struct clk_hw *hw, u8 index) +{ + struct s3c24xx_clkout *clkout = to_s3c24xx_clkout(hw); + int ret = 0; + + s3c2410_modify_misccr((clkout->mask << clkout->shift), + (index << clkout->shift)); + + return ret; +} + +const struct clk_ops s3c24xx_clkout_ops = { + .get_parent = s3c24xx_clkout_get_parent, + .set_parent = s3c24xx_clkout_set_parent, + .determine_rate = __clk_mux_determine_rate, +}; + +struct clk *s3c24xx_register_clkout(struct device *dev, const char *name, + const char **parent_names, u8 num_parents, + u8 shift, u32 mask) +{ + struct s3c24xx_clkout *clkout; + struct clk *clk; + struct clk_init_data init; + + /* allocate the clkout */ + clkout = kzalloc(sizeof(*clkout), GFP_KERNEL); + if (!clkout) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &s3c24xx_clkout_ops; + init.flags = CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = num_parents; + + clkout->shift = shift; + clkout->mask = mask; + clkout->hw.init = &init; + + clk = clk_register(dev, &clkout->hw); + + return clk; +} + +/* + * dclk and clkout init + */ + +struct s3c24xx_dclk { + struct device *dev; + void __iomem *base; + struct clk_onecell_data clk_data; + struct notifier_block dclk0_div_change_nb; + struct notifier_block dclk1_div_change_nb; + spinlock_t dclk_lock; + unsigned long reg_save; +}; + +#define to_s3c24xx_dclk0(x) \ + container_of(x, struct s3c24xx_dclk, dclk0_div_change_nb) + +#define to_s3c24xx_dclk1(x) \ + container_of(x, struct s3c24xx_dclk, dclk1_div_change_nb) + +PNAME(dclk_s3c2410_p) = { "pclk", "uclk" }; +PNAME(clkout0_s3c2410_p) = { "mpll", "upll", "fclk", "hclk", "pclk", + "gate_dclk0" }; +PNAME(clkout1_s3c2410_p) = { "mpll", "upll", "fclk", "hclk", "pclk", + "gate_dclk1" }; + +PNAME(clkout0_s3c2412_p) = { "mpll", "upll", "rtc_clkout", + "hclk", "pclk", "gate_dclk0" }; +PNAME(clkout1_s3c2412_p) = { "xti", "upll", "fclk", "hclk", "pclk", + "gate_dclk1" }; + +PNAME(clkout0_s3c2440_p) = { "xti", "upll", "fclk", "hclk", "pclk", + "gate_dclk0" }; +PNAME(clkout1_s3c2440_p) = { "mpll", "upll", "rtc_clkout", + "hclk", "pclk", "gate_dclk1" }; + +PNAME(dclk_s3c2443_p) = { "pclk", "epll" }; +PNAME(clkout0_s3c2443_p) = { "xti", "epll", "armclk", "hclk", "pclk", + "gate_dclk0" }; +PNAME(clkout1_s3c2443_p) = { "dummy", "epll", "rtc_clkout", + "hclk", "pclk", "gate_dclk1" }; + +#define DCLKCON_DCLK_DIV_MASK 0xf +#define DCLKCON_DCLK0_DIV_SHIFT 4 +#define DCLKCON_DCLK0_CMP_SHIFT 8 +#define DCLKCON_DCLK1_DIV_SHIFT 20 +#define DCLKCON_DCLK1_CMP_SHIFT 24 + +static void s3c24xx_dclk_update_cmp(struct s3c24xx_dclk *s3c24xx_dclk, + int div_shift, int cmp_shift) +{ + unsigned long flags = 0; + u32 dclk_con, div, cmp; + + spin_lock_irqsave(&s3c24xx_dclk->dclk_lock, flags); + + dclk_con = readl_relaxed(s3c24xx_dclk->base); + + div = ((dclk_con >> div_shift) & DCLKCON_DCLK_DIV_MASK) + 1; + cmp = ((div + 1) / 2) - 1; + + dclk_con &= ~(DCLKCON_DCLK_DIV_MASK << cmp_shift); + dclk_con |= (cmp << cmp_shift); + + writel_relaxed(dclk_con, s3c24xx_dclk->base); + + spin_unlock_irqrestore(&s3c24xx_dclk->dclk_lock, flags); +} + +static int s3c24xx_dclk0_div_notify(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct s3c24xx_dclk *s3c24xx_dclk = to_s3c24xx_dclk0(nb); + + if (event == POST_RATE_CHANGE) { + s3c24xx_dclk_update_cmp(s3c24xx_dclk, + DCLKCON_DCLK0_DIV_SHIFT, DCLKCON_DCLK0_CMP_SHIFT); + } + + return NOTIFY_DONE; +} + +static int s3c24xx_dclk1_div_notify(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct s3c24xx_dclk *s3c24xx_dclk = to_s3c24xx_dclk1(nb); + + if (event == POST_RATE_CHANGE) { + s3c24xx_dclk_update_cmp(s3c24xx_dclk, + DCLKCON_DCLK1_DIV_SHIFT, DCLKCON_DCLK1_CMP_SHIFT); + } + + return NOTIFY_DONE; +} + +#ifdef CONFIG_PM_SLEEP +static int s3c24xx_dclk_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev); + + s3c24xx_dclk->reg_save = readl_relaxed(s3c24xx_dclk->base); + return 0; +} + +static int s3c24xx_dclk_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev); + + writel_relaxed(s3c24xx_dclk->reg_save, s3c24xx_dclk->base); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(s3c24xx_dclk_pm_ops, + s3c24xx_dclk_suspend, s3c24xx_dclk_resume); + +static int s3c24xx_dclk_probe(struct platform_device *pdev) +{ + struct s3c24xx_dclk *s3c24xx_dclk; + struct resource *mem; + struct clk **clk_table; + struct s3c24xx_dclk_drv_data *dclk_variant; + int ret, i; + + s3c24xx_dclk = devm_kzalloc(&pdev->dev, sizeof(*s3c24xx_dclk), + GFP_KERNEL); + if (!s3c24xx_dclk) + return -ENOMEM; + + s3c24xx_dclk->dev = &pdev->dev; + platform_set_drvdata(pdev, s3c24xx_dclk); + spin_lock_init(&s3c24xx_dclk->dclk_lock); + + clk_table = devm_kzalloc(&pdev->dev, + sizeof(struct clk *) * DCLK_MAX_CLKS, + GFP_KERNEL); + if (!clk_table) + return -ENOMEM; + + s3c24xx_dclk->clk_data.clks = clk_table; + s3c24xx_dclk->clk_data.clk_num = DCLK_MAX_CLKS; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + s3c24xx_dclk->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(s3c24xx_dclk->base)) + return PTR_ERR(s3c24xx_dclk->base); + + dclk_variant = (struct s3c24xx_dclk_drv_data *) + platform_get_device_id(pdev)->driver_data; + + + clk_table[MUX_DCLK0] = clk_register_mux(&pdev->dev, "mux_dclk0", + dclk_variant->mux_parent_names, + dclk_variant->mux_num_parents, 0, + s3c24xx_dclk->base, 1, 1, 0, + &s3c24xx_dclk->dclk_lock); + clk_table[MUX_DCLK1] = clk_register_mux(&pdev->dev, "mux_dclk1", + dclk_variant->mux_parent_names, + dclk_variant->mux_num_parents, 0, + s3c24xx_dclk->base, 17, 1, 0, + &s3c24xx_dclk->dclk_lock); + + clk_table[DIV_DCLK0] = clk_register_divider(&pdev->dev, "div_dclk0", + "mux_dclk0", 0, s3c24xx_dclk->base, + 4, 4, 0, &s3c24xx_dclk->dclk_lock); + clk_table[DIV_DCLK1] = clk_register_divider(&pdev->dev, "div_dclk1", + "mux_dclk1", 0, s3c24xx_dclk->base, + 20, 4, 0, &s3c24xx_dclk->dclk_lock); + + clk_table[GATE_DCLK0] = clk_register_gate(&pdev->dev, "gate_dclk0", + "div_dclk0", CLK_SET_RATE_PARENT, + s3c24xx_dclk->base, 0, 0, + &s3c24xx_dclk->dclk_lock); + clk_table[GATE_DCLK1] = clk_register_gate(&pdev->dev, "gate_dclk1", + "div_dclk1", CLK_SET_RATE_PARENT, + s3c24xx_dclk->base, 16, 0, + &s3c24xx_dclk->dclk_lock); + + clk_table[MUX_CLKOUT0] = s3c24xx_register_clkout(&pdev->dev, + "clkout0", dclk_variant->clkout0_parent_names, + dclk_variant->clkout0_num_parents, 4, 7); + clk_table[MUX_CLKOUT1] = s3c24xx_register_clkout(&pdev->dev, + "clkout1", dclk_variant->clkout1_parent_names, + dclk_variant->clkout1_num_parents, 8, 7); + + for (i = 0; i < DCLK_MAX_CLKS; i++) + if (IS_ERR(clk_table[i])) { + dev_err(&pdev->dev, "clock %d failed to register\n", i); + ret = PTR_ERR(clk_table[i]); + goto err_clk_register; + } + + ret = clk_register_clkdev(clk_table[MUX_DCLK0], "dclk0", NULL); + if (!ret) + ret = clk_register_clkdev(clk_table[MUX_DCLK1], "dclk1", NULL); + if (!ret) + ret = clk_register_clkdev(clk_table[MUX_CLKOUT0], + "clkout0", NULL); + if (!ret) + ret = clk_register_clkdev(clk_table[MUX_CLKOUT1], + "clkout1", NULL); + if (ret) { + dev_err(&pdev->dev, "failed to register aliases, %d\n", ret); + goto err_clk_register; + } + + s3c24xx_dclk->dclk0_div_change_nb.notifier_call = + s3c24xx_dclk0_div_notify; + + s3c24xx_dclk->dclk1_div_change_nb.notifier_call = + s3c24xx_dclk1_div_notify; + + ret = clk_notifier_register(clk_table[DIV_DCLK0], + &s3c24xx_dclk->dclk0_div_change_nb); + if (ret) + goto err_clk_register; + + ret = clk_notifier_register(clk_table[DIV_DCLK1], + &s3c24xx_dclk->dclk1_div_change_nb); + if (ret) + goto err_dclk_notify; + + return 0; + +err_dclk_notify: + clk_notifier_unregister(clk_table[DIV_DCLK0], + &s3c24xx_dclk->dclk0_div_change_nb); +err_clk_register: + for (i = 0; i < DCLK_MAX_CLKS; i++) + if (clk_table[i] && !IS_ERR(clk_table[i])) + clk_unregister(clk_table[i]); + + return ret; +} + +static int s3c24xx_dclk_remove(struct platform_device *pdev) +{ + struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev); + struct clk **clk_table = s3c24xx_dclk->clk_data.clks; + int i; + + clk_notifier_unregister(clk_table[DIV_DCLK1], + &s3c24xx_dclk->dclk1_div_change_nb); + clk_notifier_unregister(clk_table[DIV_DCLK0], + &s3c24xx_dclk->dclk0_div_change_nb); + + for (i = 0; i < DCLK_MAX_CLKS; i++) + clk_unregister(clk_table[i]); + + return 0; +} + +static struct s3c24xx_dclk_drv_data dclk_variants[] = { + [S3C2410] = { + .clkout0_parent_names = clkout0_s3c2410_p, + .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2410_p), + .clkout1_parent_names = clkout1_s3c2410_p, + .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2410_p), + .mux_parent_names = dclk_s3c2410_p, + .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p), + }, + [S3C2412] = { + .clkout0_parent_names = clkout0_s3c2412_p, + .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2412_p), + .clkout1_parent_names = clkout1_s3c2412_p, + .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2412_p), + .mux_parent_names = dclk_s3c2410_p, + .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p), + }, + [S3C2440] = { + .clkout0_parent_names = clkout0_s3c2440_p, + .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2440_p), + .clkout1_parent_names = clkout1_s3c2440_p, + .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2440_p), + .mux_parent_names = dclk_s3c2410_p, + .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p), + }, + [S3C2443] = { + .clkout0_parent_names = clkout0_s3c2443_p, + .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2443_p), + .clkout1_parent_names = clkout1_s3c2443_p, + .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2443_p), + .mux_parent_names = dclk_s3c2443_p, + .mux_num_parents = ARRAY_SIZE(dclk_s3c2443_p), + }, +}; + +static struct platform_device_id s3c24xx_dclk_driver_ids[] = { + { + .name = "s3c2410-dclk", + .driver_data = (kernel_ulong_t)&dclk_variants[S3C2410], + }, { + .name = "s3c2412-dclk", + .driver_data = (kernel_ulong_t)&dclk_variants[S3C2412], + }, { + .name = "s3c2440-dclk", + .driver_data = (kernel_ulong_t)&dclk_variants[S3C2440], + }, { + .name = "s3c2443-dclk", + .driver_data = (kernel_ulong_t)&dclk_variants[S3C2443], + }, + { } +}; + +MODULE_DEVICE_TABLE(platform, s3c24xx_dclk_driver_ids); + +static struct platform_driver s3c24xx_dclk_driver = { + .driver = { + .name = "s3c24xx-dclk", + .owner = THIS_MODULE, + .pm = &s3c24xx_dclk_pm_ops, + }, + .probe = s3c24xx_dclk_probe, + .remove = s3c24xx_dclk_remove, + .id_table = s3c24xx_dclk_driver_ids, +}; +module_platform_driver(s3c24xx_dclk_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("Driver for the S3C24XX external clock outputs"); -- cgit v1.2.3 From a501fd37f77a703a8710af8f6964e0ae269df973 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 6 May 2014 14:58:22 +0200 Subject: leds: Fix build for LEDS_CLASS=m on versatile I got a build error today, since LEDS_VERSATILE can be built-in while LEDS_CLASS is a module: drivers/built-in.o: In function `versatile_leds_probe': :(.text+0x155020): undefined reference to `led_classdev_register' I suggest we turn this option into 'tristate' so that the dependency tracking works correctly. Signed-off-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Olof Johansson --- drivers/leds/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 9adc79406ff3..39e717797cc0 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -488,7 +488,7 @@ config LEDS_BLINKM through I2C. Say Y to enable support for the BlinkM LED. config LEDS_VERSATILE - bool "LED support for the ARM Versatile and RealView" + tristate "LED support for the ARM Versatile and RealView" depends on ARCH_REALVIEW || ARCH_VERSATILE depends on LEDS_CLASS help -- cgit v1.2.3 From 4c1ad70921caa9831b4926e83481c9f825016dd6 Mon Sep 17 00:00:00 2001 From: Bin Shi Date: Tue, 6 May 2014 22:42:29 +0800 Subject: clocksource: prima2: fix some minor checkpatch issues Fix the "line over 80 characters". users of the codes - key customers really care about that. WARNING: line over 80 characters 64: FILE: timer-prima2.c:64: + WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0))); WARNING: line over 80 characters 80: FILE: timer-prima2.c:80: + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); WARNING: line over 80 characters 82: FILE: timer-prima2.c:82: + cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); WARNING: line over 80 characters 92: FILE: timer-prima2.c:92: + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); WARNING: line over 80 characters 96: FILE: timer-prima2.c:96: + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); WARNING: line over 80 characters 111: FILE: timer-prima2.c:111: + writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); WARNING: line over 80 characters 114: FILE: timer-prima2.c:114: + writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); WARNING: line over 80 characters 126: FILE: timer-prima2.c:126: + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); WARNING: line over 80 characters 129: FILE: timer-prima2.c:129: + sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); WARNING: line over 80 characters 137: FILE: timer-prima2.c:137: + writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); WARNING: line over 80 characters 139: FILE: timer-prima2.c:139: + writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO); WARNING: line over 80 characters 140: FILE: timer-prima2.c:140: + writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI); WARNING: line over 80 characters 216: FILE: timer-prima2.c:216: +CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer, "sirf,prima2-tick", sirfsoc_prima2_timer_init); total: 0 errors, 13 warnings, 216 lines checked timer-prima2.c has style problems, please review. If any of these errors are false positives, please report them to the maintainer, see CHECKPATCH in MAINTAINERS. Signed-off-by: Bin Shi Signed-off-by: Barry Song --- drivers/clocksource/timer-prima2.c | 42 +++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c index 1a6b2d6356d6..84fdb15eae79 100644 --- a/drivers/clocksource/timer-prima2.c +++ b/drivers/clocksource/timer-prima2.c @@ -61,7 +61,8 @@ static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *ce = dev_id; - WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0))); + WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & + BIT(0))); /* clear timer0 interrupt */ writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS); @@ -77,9 +78,11 @@ static cycle_t sirfsoc_timer_read(struct clocksource *cs) u64 cycles; /* latch the 64-bit timer counter */ - writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, + sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI); - cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); + cycles = (cycles << 32) | + readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); return cycles; } @@ -89,11 +92,13 @@ static int sirfsoc_timer_set_next_event(unsigned long delta, { unsigned long now, next; - writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, + sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); next = now + delta; writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0); - writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, + sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); return next - now > delta ? -ETIME : 0; @@ -108,10 +113,12 @@ static void sirfsoc_timer_set_mode(enum clock_event_mode mode, WARN_ON(1); break; case CLOCK_EVT_MODE_ONESHOT: - writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); + writel_relaxed(val | BIT(0), + sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); break; case CLOCK_EVT_MODE_SHUTDOWN: - writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); + writel_relaxed(val & ~BIT(0), + sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_RESUME: @@ -123,10 +130,13 @@ static void sirfsoc_clocksource_suspend(struct clocksource *cs) { int i; - writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, + sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++) - sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); + sirfsoc_timer_reg_val[i] = + readl_relaxed(sirfsoc_timer_base + + sirfsoc_timer_reg_list[i]); } static void sirfsoc_clocksource_resume(struct clocksource *cs) @@ -134,10 +144,13 @@ static void sirfsoc_clocksource_resume(struct clocksource *cs) int i; for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++) - writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); + writel_relaxed(sirfsoc_timer_reg_val[i], + sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); - writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO); - writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI); + writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], + sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO); + writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], + sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI); } static struct clock_event_device sirfsoc_clockevent = { @@ -202,7 +215,7 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np) sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0); writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1, - sirfsoc_timer_base + SIRFSOC_TIMER_DIV); + sirfsoc_timer_base + SIRFSOC_TIMER_DIV); writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO); writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI); writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS); @@ -216,4 +229,5 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np) sirfsoc_clockevent_init(); } -CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer, "sirf,prima2-tick", sirfsoc_prima2_timer_init); +CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer, + "sirf,prima2-tick", sirfsoc_prima2_timer_init); -- cgit v1.2.3 From 7caf6852018a7550a2451972522688caef350549 Mon Sep 17 00:00:00 2001 From: Bin Shi Date: Tue, 6 May 2014 22:58:36 +0800 Subject: irqchip: sirf: fix one minor checkpatch issue fix "line line over 80 characters" for the below: static int __init sirfsoc_irq_init(struct device_node *np, struct device_node *parent) the users of the codes - key customers really care about that. Signed-off-by: Bin Shi Signed-off-by: Barry Song --- drivers/irqchip/irq-sirfsoc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-sirfsoc.c b/drivers/irqchip/irq-sirfsoc.c index 581eefe331ae..5e54f6d71e77 100644 --- a/drivers/irqchip/irq-sirfsoc.c +++ b/drivers/irqchip/irq-sirfsoc.c @@ -58,7 +58,8 @@ static void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs) handle_IRQ(irqnr, regs); } -static int __init sirfsoc_irq_init(struct device_node *np, struct device_node *parent) +static int __init sirfsoc_irq_init(struct device_node *np, + struct device_node *parent) { void __iomem *base = of_iomap(np, 0); if (!base) -- cgit v1.2.3 From c7cff54d5926e3f419c23eff2ebaf6f5e12da05d Mon Sep 17 00:00:00 2001 From: Zhiwu Song Date: Mon, 5 May 2014 19:30:04 +0800 Subject: clocksource:sirf: remove the hardcode for the clk of timers Nobody want to know the connection between io clk and timer clk, so exposing this information to timer module is not reasonable. this patch moves to define the timers' clk in dt. Signed-off-by: Zhiwu Song Signed-off-by: Barry Song --- drivers/clocksource/timer-marco.c | 8 +++----- drivers/clocksource/timer-prima2.c | 5 +---- 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c index b52e1c078b99..571d10974139 100644 --- a/drivers/clocksource/timer-marco.c +++ b/drivers/clocksource/timer-marco.c @@ -252,15 +252,13 @@ static void __init sirfsoc_clockevent_init(void) } /* initialize the kernel jiffy timer source */ -static void __init sirfsoc_marco_timer_init(void) +static void __init sirfsoc_marco_timer_init(struct device_node *np) { unsigned long rate; u32 timer_div; struct clk *clk; - /* timer's input clock is io clock */ - clk = clk_get_sys("io", NULL); - + clk = of_clk_get(np, 0); BUG_ON(IS_ERR(clk)); rate = clk_get_rate(clk); @@ -303,6 +301,6 @@ static void __init sirfsoc_of_timer_init(struct device_node *np) if (!sirfsoc_timer1_irq.irq) panic("No irq passed for timer1 via DT\n"); - sirfsoc_marco_timer_init(); + sirfsoc_marco_timer_init(np); } CLOCKSOURCE_OF_DECLARE(sirfsoc_marco_timer, "sirf,marco-tick", sirfsoc_of_timer_init ); diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c index 84fdb15eae79..a722aac7ac02 100644 --- a/drivers/clocksource/timer-prima2.c +++ b/drivers/clocksource/timer-prima2.c @@ -198,11 +198,8 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np) unsigned long rate; struct clk *clk; - /* timer's input clock is io clock */ - clk = clk_get_sys("io", NULL); - + clk = of_clk_get(np, 0); BUG_ON(IS_ERR(clk)); - rate = clk_get_rate(clk); BUG_ON(rate < PRIMA2_CLOCK_FREQ); -- cgit v1.2.3 From 3f7c01ade226e7006d76e42f8c9b99ada7085312 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 9 May 2014 05:49:10 +0900 Subject: clk: samsung: add clock controller driver for s3c2410, s3c2440 and s3c2442 This driver can handle the clock controllers of the socs mentioned above, as they share a common clock tree with only small differences. The clock structure is built according to the manuals of the included SoCs and might include changes in comparison to the previous clock structure. As pll-rate-tables only the 12mhz variants are currently included. The original code was wrongly checking for 169mhz xti values [a 0 to much at the end], so the original 16mhz pll table would have never been included and its values are so obscure that I have no possibility to at least check their sane-ness. When using the formula from the manual the resulting frequency is near the table value but still slightly off. Signed-off-by: Heiko Stuebner Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-s3c2410.c | 477 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 478 insertions(+) create mode 100644 drivers/clk/samsung/clk-s3c2410.c (limited to 'drivers') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 9892de4978e3..2cb62f87e068 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o +obj-$(CONFIG_S3C2410_COMMON_CLK)+= clk-s3c2410.o obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c new file mode 100644 index 000000000000..7b4182186a30 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2410.c @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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 S3C2410 and following SoCs. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "clk-pll.h" + +#define LOCKTIME 0x00 +#define MPLLCON 0x04 +#define UPLLCON 0x08 +#define CLKCON 0x0c +#define CLKSLOW 0x10 +#define CLKDIVN 0x14 +#define CAMDIVN 0x18 + +/* the soc types */ +enum supported_socs { + S3C2410, + S3C2440, + S3C2442, +}; + +/* list of PLLs to be registered */ +enum s3c2410_plls { + mpll, upll, +}; + +static void __iomem *reg_base; + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *s3c2410_save; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static unsigned long s3c2410_clk_regs[] __initdata = { + LOCKTIME, + MPLLCON, + UPLLCON, + CLKCON, + CLKSLOW, + CLKDIVN, + CAMDIVN, +}; + +static int s3c2410_clk_suspend(void) +{ + samsung_clk_save(reg_base, s3c2410_save, + ARRAY_SIZE(s3c2410_clk_regs)); + + return 0; +} + +static void s3c2410_clk_resume(void) +{ + samsung_clk_restore(reg_base, s3c2410_save, + ARRAY_SIZE(s3c2410_clk_regs)); +} + +static struct syscore_ops s3c2410_clk_syscore_ops = { + .suspend = s3c2410_clk_suspend, + .resume = s3c2410_clk_resume, +}; + +static void s3c2410_clk_sleep_init(void) +{ + s3c2410_save = samsung_clk_alloc_reg_dump(s3c2410_clk_regs, + ARRAY_SIZE(s3c2410_clk_regs)); + if (!s3c2410_save) { + pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", + __func__); + return; + } + + register_syscore_ops(&s3c2410_clk_syscore_ops); + return; +} +#else +static void s3c2410_clk_sleep_init(void) {} +#endif + +PNAME(fclk_p) = { "mpll", "div_slow" }; + +struct samsung_mux_clock s3c2410_common_muxes[] __initdata = { + MUX(FCLK, "fclk", fclk_p, CLKSLOW, 4, 1), +}; + +static struct clk_div_table divslow_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, + { .val = 4, .div = 8 }, + { .val = 5, .div = 10 }, + { .val = 6, .div = 12 }, + { .val = 7, .div = 14 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2410_common_dividers[] __initdata = { + DIV_T(0, "div_slow", "xti", CLKSLOW, 0, 3, divslow_d), + DIV(PCLK, "pclk", "hclk", CLKDIVN, 0, 1), +}; + +struct samsung_gate_clock s3c2410_common_gates[] __initdata = { + GATE(PCLK_SPI, "spi", "pclk", CLKCON, 18, 0, 0), + GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 17, 0, 0), + GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 16, 0, 0), + GATE(PCLK_ADC, "adc", "pclk", CLKCON, 15, 0, 0), + GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 14, 0, 0), + GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 13, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 12, 0, 0), + GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 11, 0, 0), + GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 10, 0, 0), + GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 9, 0, 0), + GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 8, 0, 0), + GATE(HCLK_USBD, "usb-device", "hclk", CLKCON, 7, 0, 0), + GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0), + GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0), + GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0), +}; + +/* should be added _after_ the soc-specific clocks are created */ +struct samsung_clock_alias s3c2410_common_aliases[] __initdata = { + ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"), + ALIAS(PCLK_ADC, NULL, "adc"), + ALIAS(PCLK_RTC, NULL, "rtc"), + ALIAS(PCLK_PWM, NULL, "timers"), + ALIAS(HCLK_LCD, NULL, "lcd"), + ALIAS(HCLK_USBD, NULL, "usb-device"), + ALIAS(HCLK_USBH, NULL, "usb-host"), + ALIAS(UCLK, NULL, "usb-bus-host"), + ALIAS(UCLK, NULL, "usb-bus-gadget"), + ALIAS(ARMCLK, NULL, "armclk"), + ALIAS(UCLK, NULL, "uclk"), + ALIAS(HCLK, NULL, "hclk"), + ALIAS(MPLL, NULL, "mpll"), + ALIAS(FCLK, NULL, "fclk"), +}; + +/* S3C2410 specific clocks */ + +static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = { + /* sorted in descending order */ + /* 2410A extras */ + PLL_35XX_RATE(270000000, 127, 1, 1), + PLL_35XX_RATE(268000000, 126, 1, 1), + PLL_35XX_RATE(266000000, 125, 1, 1), + PLL_35XX_RATE(226000000, 105, 1, 1), + PLL_35XX_RATE(210000000, 132, 2, 1), + /* 2410 common */ + PLL_35XX_RATE(203000000, 161, 3, 1), + PLL_35XX_RATE(192000000, 88, 1, 1), + PLL_35XX_RATE(186000000, 85, 1, 1), + PLL_35XX_RATE(180000000, 82, 1, 1), + PLL_35XX_RATE(170000000, 77, 1, 1), + PLL_35XX_RATE(158000000, 71, 1, 1), + PLL_35XX_RATE(152000000, 68, 1, 1), + PLL_35XX_RATE(147000000, 90, 2, 1), + PLL_35XX_RATE(135000000, 82, 2, 1), + PLL_35XX_RATE(124000000, 116, 1, 2), + PLL_35XX_RATE(118000000, 150, 2, 2), + PLL_35XX_RATE(113000000, 105, 1, 2), + PLL_35XX_RATE(101000000, 127, 2, 2), + PLL_35XX_RATE(90000000, 112, 2, 2), + PLL_35XX_RATE(85000000, 105, 2, 2), + PLL_35XX_RATE(79000000, 71, 1, 2), + PLL_35XX_RATE(68000000, 82, 2, 2), + PLL_35XX_RATE(56000000, 142, 2, 3), + PLL_35XX_RATE(48000000, 120, 2, 3), + PLL_35XX_RATE(51000000, 161, 3, 3), + PLL_35XX_RATE(45000000, 82, 1, 3), + PLL_35XX_RATE(34000000, 82, 2, 3), + { /* sentinel */ }, +}; + +static struct samsung_pll_clock s3c2410_plls[] __initdata = { + [mpll] = PLL(pll_s3c2410_mpll, MPLL, "mpll", "xti", + LOCKTIME, MPLLCON, NULL), + [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti", + LOCKTIME, UPLLCON, NULL), +}; + +struct samsung_div_clock s3c2410_dividers[] __initdata = { + DIV(HCLK, "hclk", "mpll", CLKDIVN, 1, 1), +}; + +struct samsung_fixed_factor_clock s3c2410_ffactor[] __initdata = { + /* + * armclk is directly supplied by the fclk, without + * switching possibility like on the s3c244x below. + */ + FFACTOR(ARMCLK, "armclk", "fclk", 1, 1, 0), + + /* uclk is fed from the unmodified upll */ + FFACTOR(UCLK, "uclk", "upll", 1, 1, 0), +}; + +struct samsung_clock_alias s3c2410_aliases[] __initdata = { + ALIAS(PCLK_UART0, "s3c2410-uart.0", "uart"), + ALIAS(PCLK_UART1, "s3c2410-uart.1", "uart"), + ALIAS(PCLK_UART2, "s3c2410-uart.2", "uart"), + ALIAS(PCLK_UART0, "s3c2410-uart.0", "clk_uart_baud0"), + ALIAS(PCLK_UART1, "s3c2410-uart.1", "clk_uart_baud0"), + ALIAS(PCLK_UART2, "s3c2410-uart.2", "clk_uart_baud0"), + ALIAS(UCLK, NULL, "clk_uart_baud1"), +}; + +/* S3C244x specific clocks */ + +static struct samsung_pll_rate_table pll_s3c244x_12mhz_tbl[] __initdata = { + /* sorted in descending order */ + PLL_35XX_RATE(400000000, 0x5c, 1, 1), + PLL_35XX_RATE(390000000, 0x7a, 2, 1), + PLL_35XX_RATE(380000000, 0x57, 1, 1), + PLL_35XX_RATE(370000000, 0xb1, 4, 1), + PLL_35XX_RATE(360000000, 0x70, 2, 1), + PLL_35XX_RATE(350000000, 0xa7, 4, 1), + PLL_35XX_RATE(340000000, 0x4d, 1, 1), + PLL_35XX_RATE(330000000, 0x66, 2, 1), + PLL_35XX_RATE(320000000, 0x98, 4, 1), + PLL_35XX_RATE(310000000, 0x93, 4, 1), + PLL_35XX_RATE(300000000, 0x75, 3, 1), + PLL_35XX_RATE(240000000, 0x70, 1, 2), + PLL_35XX_RATE(230000000, 0x6b, 1, 2), + PLL_35XX_RATE(220000000, 0x66, 1, 2), + PLL_35XX_RATE(210000000, 0x84, 2, 2), + PLL_35XX_RATE(200000000, 0x5c, 1, 2), + PLL_35XX_RATE(190000000, 0x57, 1, 2), + PLL_35XX_RATE(180000000, 0x70, 2, 2), + PLL_35XX_RATE(170000000, 0x4d, 1, 2), + PLL_35XX_RATE(160000000, 0x98, 4, 2), + PLL_35XX_RATE(150000000, 0x75, 3, 2), + PLL_35XX_RATE(120000000, 0x70, 1, 3), + PLL_35XX_RATE(110000000, 0x66, 1, 3), + PLL_35XX_RATE(100000000, 0x5c, 1, 3), + PLL_35XX_RATE(90000000, 0x70, 2, 3), + PLL_35XX_RATE(80000000, 0x98, 4, 3), + PLL_35XX_RATE(75000000, 0x75, 3, 3), + { /* sentinel */ }, +}; + +static struct samsung_pll_clock s3c244x_common_plls[] __initdata = { + [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti", + LOCKTIME, MPLLCON, NULL), + [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti", + LOCKTIME, UPLLCON, NULL), +}; + +PNAME(hclk_p) = { "fclk", "div_hclk_2", "div_hclk_4", "div_hclk_3" }; +PNAME(armclk_p) = { "fclk", "hclk" }; + +struct samsung_mux_clock s3c244x_common_muxes[] __initdata = { + MUX(HCLK, "hclk", hclk_p, CLKDIVN, 1, 2), + MUX(ARMCLK, "armclk", armclk_p, CAMDIVN, 12, 1), +}; + +struct samsung_fixed_factor_clock s3c244x_common_ffactor[] __initdata = { + FFACTOR(0, "div_hclk_2", "fclk", 1, 2, 0), + FFACTOR(0, "ff_cam", "div_cam", 2, 1, CLK_SET_RATE_PARENT), +}; + +static struct clk_div_table div_hclk_4_d[] = { + { .val = 0, .div = 4 }, + { .val = 1, .div = 8 }, + { /* sentinel */ }, +}; + +static struct clk_div_table div_hclk_3_d[] = { + { .val = 0, .div = 3 }, + { .val = 1, .div = 6 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c244x_common_dividers[] __initdata = { + DIV(UCLK, "uclk", "upll", CLKDIVN, 3, 1), + DIV(0, "div_hclk", "fclk", CLKDIVN, 1, 1), + DIV_T(0, "div_hclk_4", "fclk", CAMDIVN, 9, 1, div_hclk_4_d), + DIV_T(0, "div_hclk_3", "fclk", CAMDIVN, 8, 1, div_hclk_3_d), + DIV(0, "div_cam", "upll", CAMDIVN, 0, 3), +}; + +struct samsung_gate_clock s3c244x_common_gates[] __initdata = { + GATE(HCLK_CAM, "cam", "hclk", CLKCON, 19, 0, 0), +}; + +struct samsung_clock_alias s3c244x_common_aliases[] __initdata = { + ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"), + ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"), + ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"), + ALIAS(PCLK_UART0, "s3c2440-uart.0", "clk_uart_baud2"), + ALIAS(PCLK_UART1, "s3c2440-uart.1", "clk_uart_baud2"), + ALIAS(PCLK_UART2, "s3c2440-uart.2", "clk_uart_baud2"), + ALIAS(HCLK_CAM, NULL, "camif"), + ALIAS(CAMIF, NULL, "camif-upll"), +}; + +/* S3C2440 specific clocks */ + +PNAME(s3c2440_camif_p) = { "upll", "ff_cam" }; + +struct samsung_mux_clock s3c2440_muxes[] __initdata = { + MUX(CAMIF, "camif", s3c2440_camif_p, CAMDIVN, 4, 1), +}; + +struct samsung_gate_clock s3c2440_gates[] __initdata = { + GATE(PCLK_AC97, "ac97", "pclk", CLKCON, 20, 0, 0), +}; + +/* S3C2442 specific clocks */ + +struct samsung_fixed_factor_clock s3c2442_ffactor[] __initdata = { + FFACTOR(0, "upll_3", "upll", 1, 3, 0), +}; + +PNAME(s3c2442_camif_p) = { "upll", "ff_cam", "upll", "upll_3" }; + +struct samsung_mux_clock s3c2442_muxes[] __initdata = { + MUX(CAMIF, "camif", s3c2442_camif_p, CAMDIVN, 4, 2), +}; + +/* + * fixed rate clocks generated outside the soc + * Only necessary until the devicetree-move is complete + */ +#define XTI 1 +struct samsung_fixed_rate_clock s3c2410_common_frate_clks[] __initdata = { + FRATE(XTI, "xti", NULL, CLK_IS_ROOT, 0), +}; + +static void __init s3c2410_common_clk_register_fixed_ext(unsigned long xti_f) +{ + struct samsung_clock_alias xti_alias = ALIAS(XTI, NULL, "xtal"); + + s3c2410_common_frate_clks[0].fixed_rate = xti_f; + samsung_clk_register_fixed_rate(s3c2410_common_frate_clks, + ARRAY_SIZE(s3c2410_common_frate_clks)); + + samsung_clk_register_alias(&xti_alias, 1); +} + +void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f, + int current_soc, + void __iomem *base) +{ + reg_base = base; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } + + samsung_clk_init(np, reg_base, NR_CLKS); + + /* Register external clocks only in non-dt cases */ + if (!np) + s3c2410_common_clk_register_fixed_ext(xti_f); + + if (current_soc == 2410) { + if (_get_rate("xti") == 12 * MHZ) { + s3c2410_plls[mpll].rate_table = pll_s3c2410_12mhz_tbl; + s3c2410_plls[upll].rate_table = pll_s3c2410_12mhz_tbl; + } + + /* Register PLLs. */ + samsung_clk_register_pll(s3c2410_plls, + ARRAY_SIZE(s3c2410_plls), reg_base); + + } else { /* S3C2440, S3C2442 */ + if (_get_rate("xti") == 12 * MHZ) { + /* + * plls follow different calculation schemes, with the + * upll following the same scheme as the s3c2410 plls + */ + s3c244x_common_plls[mpll].rate_table = + pll_s3c244x_12mhz_tbl; + s3c244x_common_plls[upll].rate_table = + pll_s3c2410_12mhz_tbl; + } + + /* Register PLLs. */ + samsung_clk_register_pll(s3c244x_common_plls, + ARRAY_SIZE(s3c244x_common_plls), reg_base); + } + + /* Register common internal clocks. */ + samsung_clk_register_mux(s3c2410_common_muxes, + ARRAY_SIZE(s3c2410_common_muxes)); + samsung_clk_register_div(s3c2410_common_dividers, + ARRAY_SIZE(s3c2410_common_dividers)); + samsung_clk_register_gate(s3c2410_common_gates, + ARRAY_SIZE(s3c2410_common_gates)); + + if (current_soc == S3C2440 || current_soc == S3C2442) { + samsung_clk_register_div(s3c244x_common_dividers, + ARRAY_SIZE(s3c244x_common_dividers)); + samsung_clk_register_gate(s3c244x_common_gates, + ARRAY_SIZE(s3c244x_common_gates)); + samsung_clk_register_mux(s3c244x_common_muxes, + ARRAY_SIZE(s3c244x_common_muxes)); + samsung_clk_register_fixed_factor(s3c244x_common_ffactor, + ARRAY_SIZE(s3c244x_common_ffactor)); + } + + /* Register SoC-specific clocks. */ + switch (current_soc) { + case S3C2410: + samsung_clk_register_div(s3c2410_dividers, + ARRAY_SIZE(s3c2410_dividers)); + samsung_clk_register_fixed_factor(s3c2410_ffactor, + ARRAY_SIZE(s3c2410_ffactor)); + samsung_clk_register_alias(s3c2410_aliases, + ARRAY_SIZE(s3c2410_common_aliases)); + break; + case S3C2440: + samsung_clk_register_mux(s3c2440_muxes, + ARRAY_SIZE(s3c2440_muxes)); + samsung_clk_register_gate(s3c2440_gates, + ARRAY_SIZE(s3c2440_gates)); + break; + case S3C2442: + samsung_clk_register_mux(s3c2442_muxes, + ARRAY_SIZE(s3c2442_muxes)); + samsung_clk_register_fixed_factor(s3c2442_ffactor, + ARRAY_SIZE(s3c2442_ffactor)); + break; + } + + /* + * Register common aliases at the end, as some of the aliased clocks + * are SoC specific. + */ + samsung_clk_register_alias(s3c2410_common_aliases, + ARRAY_SIZE(s3c2410_common_aliases)); + + if (current_soc == S3C2440 || current_soc == S3C2442) { + samsung_clk_register_alias(s3c244x_common_aliases, + ARRAY_SIZE(s3c244x_common_aliases)); + } + + s3c2410_clk_sleep_init(); +} + +static void __init s3c2410_clk_init(struct device_node *np) +{ + s3c2410_common_clk_init(np, 0, S3C2410, 0); +} +CLK_OF_DECLARE(s3c2410_clk, "samsung,s3c2410-clock", s3c2410_clk_init); + +static void __init s3c2440_clk_init(struct device_node *np) +{ + s3c2410_common_clk_init(np, 0, S3C2440, 0); +} +CLK_OF_DECLARE(s3c2440_clk, "samsung,s3c2440-clock", s3c2440_clk_init); + +static void __init s3c2442_clk_init(struct device_node *np) +{ + s3c2410_common_clk_init(np, 0, S3C2442, 0); +} +CLK_OF_DECLARE(s3c2442_clk, "samsung,s3c2442-clock", s3c2442_clk_init); -- cgit v1.2.3 From c6e126de43e7d4abfd6cf796b40589db3a046167 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Thu, 15 May 2014 16:55:24 +0100 Subject: of: Keep track of populated platform devices In "Device Tree powered" systems, platform devices are usually massively populated with of_platform_populate() call, executed at some level of initcalls, either by generic architecture or by platform-specific code. There are situations though where certain devices must be created (and bound with drivers) before all the others. This presents a challenge, as devices created explicitly would be created again by of_platform_populate(). This patch tries to solve that issue in a generic way, adding a "populated" flag for a DT node description. Subsequent of_platform_populate() will skip such nodes (and its children) in a similar way to the non-available ones. This patch also adds of_platform_depopulate() as an operation complementary to the _populate() one. It removes a platform or an amba device populated from the Device Tree, together with its all children (leaving, however, devices without associated of_node untouched) clearing the "populated" flag on the way. Signed-off-by: Pawel Moll Reviewed-by: Rob Herring Acked-by: Grant Likely --- drivers/of/platform.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index bd47fbc53dc9..e8376d646d98 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -206,12 +206,13 @@ static struct platform_device *of_platform_device_create_pdata( { struct platform_device *dev; - if (!of_device_is_available(np)) + if (!of_device_is_available(np) || + of_node_test_and_set_flag(np, OF_POPULATED)) return NULL; dev = of_device_alloc(np, bus_id, parent); if (!dev) - return NULL; + goto err_clear_flag; #if defined(CONFIG_MICROBLAZE) dev->archdata.dma_mask = 0xffffffffUL; @@ -229,10 +230,14 @@ static struct platform_device *of_platform_device_create_pdata( if (of_device_add(dev) != 0) { platform_device_put(dev); - return NULL; + goto err_clear_flag; } return dev; + +err_clear_flag: + of_node_clear_flag(np, OF_POPULATED); + return NULL; } /** @@ -264,14 +269,15 @@ static struct amba_device *of_amba_device_create(struct device_node *node, pr_debug("Creating amba device %s\n", node->full_name); - if (!of_device_is_available(node)) + if (!of_device_is_available(node) || + of_node_test_and_set_flag(node, OF_POPULATED)) return NULL; dev = amba_device_alloc(NULL, 0, 0); if (!dev) { pr_err("%s(): amba_device_alloc() failed for %s\n", __func__, node->full_name); - return NULL; + goto err_clear_flag; } /* setup generic device info */ @@ -311,6 +317,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node, err_free: amba_device_put(dev); +err_clear_flag: + of_node_clear_flag(node, OF_POPULATED); return NULL; } #else /* CONFIG_ARM_AMBA */ @@ -487,4 +495,60 @@ int of_platform_populate(struct device_node *root, return rc; } EXPORT_SYMBOL_GPL(of_platform_populate); + +static int of_platform_device_destroy(struct device *dev, void *data) +{ + bool *children_left = data; + + /* Do not touch devices not populated from the device tree */ + if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) { + *children_left = true; + return 0; + } + + /* Recurse, but don't touch this device if it has any children left */ + if (of_platform_depopulate(dev) != 0) { + *children_left = true; + return 0; + } + + if (dev->bus == &platform_bus_type) + platform_device_unregister(to_platform_device(dev)); +#ifdef CONFIG_ARM_AMBA + else if (dev->bus == &amba_bustype) + amba_device_unregister(to_amba_device(dev)); +#endif + else { + *children_left = true; + return 0; + } + + of_node_clear_flag(dev->of_node, OF_POPULATED); + + return 0; +} + +/** + * of_platform_depopulate() - Remove devices populated from device tree + * @parent: device which childred will be removed + * + * Complementary to of_platform_populate(), this function removes children + * of the given device (and, recurrently, their children) that have been + * created from their respective device tree nodes (and only those, + * leaving others - eg. manually created - unharmed). + * + * Returns 0 when all children devices have been removed or + * -EBUSY when some children remained. + */ +int of_platform_depopulate(struct device *parent) +{ + bool children_left = false; + + device_for_each_child(parent, &children_left, + of_platform_device_destroy); + + return children_left ? -EBUSY : 0; +} +EXPORT_SYMBOL_GPL(of_platform_depopulate); + #endif /* CONFIG_OF_ADDRESS */ -- cgit v1.2.3 From 3b9334ac835bb431e2186645230c9f1eb94b5d49 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 30 Apr 2014 16:46:29 +0100 Subject: mfd: vexpress: Convert custom func API to regmap Components of the Versatile Express platform (configuration microcontrollers on motherboard and daughterboards in particular) talk to each other over a custom configuration bus. They provide miscellaneous functions (from clock generator control to energy sensors) which are represented as platform devices (and Device Tree nodes). The transactions on the bus can be generated by different "bridges" in the system, some of which are universal for the whole platform (for the price of high transfer latencies), others restricted to a subsystem (but much faster). Until now drivers for such functions were using custom "func" API, which is being replaced in this patch by regmap calls. This required: * a rework (and move to drivers/bus directory, as suggested by Samuel and Arnd) of the config bus core, which is much simpler now and uses device model infrastructure (class) to keep track of the bridges; non-DT case (soon to be retired anyway) is simply covered by a special device registration function * the new config-bus driver also takes over device population, so there is no need for special matching table for of_platform_populate nor "simple-bus" hack in the arm64 model dtsi file (relevant bindings documentation has been updated); this allows all the vexpress devices fit into normal device model, making it possible to remove plenty of early inits and other hacks in the near future * adaptation of the syscfg bridge implementation in the sysreg driver, again making it much simpler; there is a special case of the "energy" function spanning two registers, where they should be both defined in the tree now, but backward compatibility is maintained in the code * modification of the relevant drivers: * hwmon - just a straight-forward API change * power/reset driver - API change * regulator - API change plus error handling simplification * osc clock driver - this one required larger rework in order to turn in into a standard platform driver Signed-off-by: Pawel Moll Acked-by: Mark Brown Acked-by: Lee Jones Acked-by: Guenter Roeck Acked-by: Mike Turquette --- drivers/bus/Kconfig | 9 + drivers/bus/Makefile | 2 + drivers/bus/vexpress-config.c | 202 ++++++++++++++++ drivers/clk/versatile/clk-vexpress-osc.c | 96 ++++---- drivers/hwmon/vexpress.c | 17 +- drivers/mfd/Makefile | 2 +- drivers/mfd/vexpress-config.c | 287 ---------------------- drivers/mfd/vexpress-sysreg.c | 395 ++++++++++++++++--------------- drivers/power/reset/vexpress-poweroff.c | 16 +- drivers/regulator/vexpress.c | 50 +--- 10 files changed, 491 insertions(+), 585 deletions(-) create mode 100644 drivers/bus/vexpress-config.c delete mode 100644 drivers/mfd/vexpress-config.c (limited to 'drivers') diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 552373c4e362..f24e79dd51bf 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -41,4 +41,13 @@ config ARM_CCI help Driver supporting the CCI cache coherent interconnect for ARM platforms. + +config VEXPRESS_CONFIG + bool "Versatile Express configuration bus" + default y if ARCH_VEXPRESS + depends on ARM || ARM64 + select REGMAP + help + Platform configuration infrastructure for the ARM Ltd. + Versatile Express. endmenu diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 8947bdd0de8b..f095aa771de9 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -10,3 +10,5 @@ obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o # CCI cache coherent interconnect for ARM platforms obj-$(CONFIG_ARM_CCI) += arm-cci.o + +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c new file mode 100644 index 000000000000..27a07dfcd626 --- /dev/null +++ b/drivers/bus/vexpress-config.c @@ -0,0 +1,202 @@ +/* + * 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 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. + * + * Copyright (C) 2014 ARM Limited + */ + +#include +#include +#include +#include +#include + + +struct vexpress_config_bridge { + struct vexpress_config_bridge_ops *ops; + void *context; +}; + + +static DEFINE_MUTEX(vexpress_config_mutex); +static struct class *vexpress_config_class; +static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER; + + +void vexpress_config_set_master(u32 site) +{ + vexpress_config_site_master = site; +} + +u32 vexpress_config_get_master(void) +{ + return vexpress_config_site_master; +} + +void vexpress_config_lock(void *arg) +{ + mutex_lock(&vexpress_config_mutex); +} + +void vexpress_config_unlock(void *arg) +{ + mutex_unlock(&vexpress_config_mutex); +} + + +static void vexpress_config_find_prop(struct device_node *node, + const char *name, u32 *val) +{ + /* Default value */ + *val = 0; + + of_node_get(node); + while (node) { + if (of_property_read_u32(node, name, val) == 0) { + of_node_put(node); + return; + } + node = of_get_next_parent(node); + } +} + +int vexpress_config_get_topo(struct device_node *node, u32 *site, + u32 *position, u32 *dcc) +{ + vexpress_config_find_prop(node, "arm,vexpress,site", site); + if (*site == VEXPRESS_SITE_MASTER) + *site = vexpress_config_site_master; + if (WARN_ON(vexpress_config_site_master == VEXPRESS_SITE_MASTER)) + return -EINVAL; + vexpress_config_find_prop(node, "arm,vexpress,position", position); + vexpress_config_find_prop(node, "arm,vexpress,dcc", dcc); + + return 0; +} + + +static void vexpress_config_devres_release(struct device *dev, void *res) +{ + struct vexpress_config_bridge *bridge = dev_get_drvdata(dev->parent); + struct regmap *regmap = res; + + bridge->ops->regmap_exit(regmap, bridge->context); +} + +struct regmap *devm_regmap_init_vexpress_config(struct device *dev) +{ + struct vexpress_config_bridge *bridge; + struct regmap *regmap; + struct regmap **res; + + if (WARN_ON(dev->parent->class != vexpress_config_class)) + return ERR_PTR(-ENODEV); + + bridge = dev_get_drvdata(dev->parent); + if (WARN_ON(!bridge)) + return ERR_PTR(-EINVAL); + + res = devres_alloc(vexpress_config_devres_release, sizeof(*res), + GFP_KERNEL); + if (!res) + return ERR_PTR(-ENOMEM); + + regmap = bridge->ops->regmap_init(dev, bridge->context); + if (IS_ERR(regmap)) { + devres_free(res); + return regmap; + } + + *res = regmap; + devres_add(dev, res); + + return regmap; +} + + +struct device *vexpress_config_bridge_register(struct device *parent, + struct vexpress_config_bridge_ops *ops, void *context) +{ + struct device *dev; + struct vexpress_config_bridge *bridge; + + if (!vexpress_config_class) { + vexpress_config_class = class_create(THIS_MODULE, + "vexpress-config"); + if (IS_ERR(vexpress_config_class)) + return (void *)vexpress_config_class; + } + + dev = device_create(vexpress_config_class, parent, 0, + NULL, "%s.bridge", dev_name(parent)); + + if (IS_ERR(dev)) + return dev; + + bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + put_device(dev); + device_unregister(dev); + return ERR_PTR(-ENOMEM); + } + bridge->ops = ops; + bridge->context = context; + + dev_set_drvdata(dev, bridge); + + dev_dbg(parent, "Registered bridge '%s', parent node %p\n", + dev_name(dev), parent->of_node); + + return dev; +} + + +static int vexpress_config_node_match(struct device *dev, const void *data) +{ + const struct device_node *node = data; + + dev_dbg(dev, "Parent node %p, looking for %p\n", + dev->parent->of_node, node); + + return dev->parent->of_node == node; +} + +static int vexpress_config_populate(struct device_node *node) +{ + struct device_node *bridge; + struct device *parent; + + bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0); + if (!bridge) + return -EINVAL; + + parent = class_find_device(vexpress_config_class, NULL, bridge, + vexpress_config_node_match); + if (WARN_ON(!parent)) + return -ENODEV; + + return of_platform_populate(node, NULL, NULL, parent); +} + +static int __init vexpress_config_init(void) +{ + int err = 0; + struct device_node *node; + + /* Need the config devices early, before the "normal" devices... */ + for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") { + err = vexpress_config_populate(node); + if (err) + break; + } + + return err; +} +postcore_initcall(vexpress_config_init); + diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c index 422391242b39..529a59c0fbfa 100644 --- a/drivers/clk/versatile/clk-vexpress-osc.c +++ b/drivers/clk/versatile/clk-vexpress-osc.c @@ -11,8 +11,6 @@ * Copyright (C) 2012 ARM Limited */ -#define pr_fmt(fmt) "vexpress-osc: " fmt - #include #include #include @@ -22,7 +20,7 @@ #include struct vexpress_osc { - struct vexpress_config_func *func; + struct regmap *reg; struct clk_hw hw; unsigned long rate_min; unsigned long rate_max; @@ -36,7 +34,7 @@ static unsigned long vexpress_osc_recalc_rate(struct clk_hw *hw, struct vexpress_osc *osc = to_vexpress_osc(hw); u32 rate; - vexpress_config_read(osc->func, 0, &rate); + regmap_read(osc->reg, 0, &rate); return rate; } @@ -60,7 +58,7 @@ static int vexpress_osc_set_rate(struct clk_hw *hw, unsigned long rate, { struct vexpress_osc *osc = to_vexpress_osc(hw); - return vexpress_config_write(osc->func, 0, rate); + return regmap_write(osc->reg, 0, rate); } static struct clk_ops vexpress_osc_ops = { @@ -70,58 +68,31 @@ static struct clk_ops vexpress_osc_ops = { }; -struct clk * __init vexpress_osc_setup(struct device *dev) -{ - struct clk_init_data init; - struct vexpress_osc *osc = kzalloc(sizeof(*osc), GFP_KERNEL); - - if (!osc) - return NULL; - - osc->func = vexpress_config_func_get_by_dev(dev); - if (!osc->func) { - kfree(osc); - return NULL; - } - - init.name = dev_name(dev); - init.ops = &vexpress_osc_ops; - init.flags = CLK_IS_ROOT; - init.num_parents = 0; - osc->hw.init = &init; - - return clk_register(NULL, &osc->hw); -} - -void __init vexpress_osc_of_setup(struct device_node *node) +static int vexpress_osc_probe(struct platform_device *pdev) { + struct clk_lookup *cl = pdev->dev.platform_data; /* Non-DT lookup */ struct clk_init_data init; struct vexpress_osc *osc; struct clk *clk; u32 range[2]; - vexpress_sysreg_of_early_init(); - - osc = kzalloc(sizeof(*osc), GFP_KERNEL); + osc = devm_kzalloc(&pdev->dev, sizeof(*osc), GFP_KERNEL); if (!osc) - return; + return -ENOMEM; - osc->func = vexpress_config_func_get_by_node(node); - if (!osc->func) { - pr_err("Failed to obtain config func for node '%s'!\n", - node->full_name); - goto error; - } + osc->reg = devm_regmap_init_vexpress_config(&pdev->dev); + if (IS_ERR(osc->reg)) + return PTR_ERR(osc->reg); - if (of_property_read_u32_array(node, "freq-range", range, + if (of_property_read_u32_array(pdev->dev.of_node, "freq-range", range, ARRAY_SIZE(range)) == 0) { osc->rate_min = range[0]; osc->rate_max = range[1]; } - of_property_read_string(node, "clock-output-names", &init.name); - if (!init.name) - init.name = node->full_name; + if (of_property_read_string(pdev->dev.of_node, "clock-output-names", + &init.name) != 0) + init.name = dev_name(&pdev->dev); init.ops = &vexpress_osc_ops; init.flags = CLK_IS_ROOT; @@ -130,20 +101,37 @@ void __init vexpress_osc_of_setup(struct device_node *node) osc->hw.init = &init; clk = clk_register(NULL, &osc->hw); - if (IS_ERR(clk)) { - pr_err("Failed to register clock '%s'!\n", init.name); - goto error; + if (IS_ERR(clk)) + return PTR_ERR(clk); + + of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk); + + /* Only happens for non-DT cases */ + if (cl) { + cl->clk = clk; + clkdev_add(cl); } - of_clk_add_provider(node, of_clk_src_simple_get, clk); + dev_dbg(&pdev->dev, "Registered clock '%s'\n", init.name); + + return 0; +} - pr_debug("Registered clock '%s'\n", init.name); +static struct of_device_id vexpress_osc_of_match[] = { + { .compatible = "arm,vexpress-osc", }, + {} +}; - return; +static struct platform_driver vexpress_osc_driver = { + .driver = { + .name = "vexpress-osc", + .of_match_table = vexpress_osc_of_match, + }, + .probe = vexpress_osc_probe, +}; -error: - if (osc->func) - vexpress_config_func_put(osc->func); - kfree(osc); +static int __init vexpress_osc_init(void) +{ + return platform_driver_register(&vexpress_osc_driver); } -CLK_OF_DECLARE(vexpress_soc, "arm,vexpress-osc", vexpress_osc_of_setup); +core_initcall(vexpress_osc_init); diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c index 8242b75d96c8..611f34c7333d 100644 --- a/drivers/hwmon/vexpress.c +++ b/drivers/hwmon/vexpress.c @@ -26,7 +26,7 @@ struct vexpress_hwmon_data { struct device *hwmon_dev; - struct vexpress_config_func *func; + struct regmap *reg; const char *name; }; @@ -53,7 +53,7 @@ static ssize_t vexpress_hwmon_u32_show(struct device *dev, int err; u32 value; - err = vexpress_config_read(data->func, 0, &value); + err = regmap_read(data->reg, 0, &value); if (err) return err; @@ -68,11 +68,11 @@ static ssize_t vexpress_hwmon_u64_show(struct device *dev, int err; u32 value_hi, value_lo; - err = vexpress_config_read(data->func, 0, &value_lo); + err = regmap_read(data->reg, 0, &value_lo); if (err) return err; - err = vexpress_config_read(data->func, 1, &value_hi); + err = regmap_read(data->reg, 1, &value_hi); if (err) return err; @@ -234,9 +234,9 @@ static int vexpress_hwmon_probe(struct platform_device *pdev) type = match->data; data->name = type->name; - data->func = vexpress_config_func_get_by_dev(&pdev->dev); - if (!data->func) - return -ENODEV; + data->reg = devm_regmap_init_vexpress_config(&pdev->dev); + if (IS_ERR(data->reg)) + return PTR_ERR(data->reg); err = sysfs_create_groups(&pdev->dev.kobj, type->attr_groups); if (err) @@ -252,7 +252,6 @@ static int vexpress_hwmon_probe(struct platform_device *pdev) error: sysfs_remove_group(&pdev->dev.kobj, match->data); - vexpress_config_func_put(data->func); return err; } @@ -266,8 +265,6 @@ static int vexpress_hwmon_remove(struct platform_device *pdev) match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); sysfs_remove_group(&pdev->dev.kobj, match->data); - vexpress_config_func_put(data->func); - return 0; } diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2851275e2656..9ba838eb5131 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -161,7 +161,7 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o -obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-sysreg.o obj-$(CONFIG_MFD_RETU) += retu-mfd.o obj-$(CONFIG_MFD_AS3711) += as3711.o obj-$(CONFIG_MFD_AS3722) += as3722.o diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c deleted file mode 100644 index d0db89d13e01..000000000000 --- a/drivers/mfd/vexpress-config.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * 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 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. - * - * Copyright (C) 2012 ARM Limited - */ - -#define pr_fmt(fmt) "vexpress-config: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define VEXPRESS_CONFIG_MAX_BRIDGES 2 - -static struct vexpress_config_bridge { - struct device_node *node; - struct vexpress_config_bridge_info *info; - struct list_head transactions; - spinlock_t transactions_lock; -} vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES]; - -static DECLARE_BITMAP(vexpress_config_bridges_map, - ARRAY_SIZE(vexpress_config_bridges)); -static DEFINE_MUTEX(vexpress_config_bridges_mutex); - -struct vexpress_config_bridge *vexpress_config_bridge_register( - struct device_node *node, - struct vexpress_config_bridge_info *info) -{ - struct vexpress_config_bridge *bridge; - int i; - - pr_debug("Registering bridge '%s'\n", info->name); - - mutex_lock(&vexpress_config_bridges_mutex); - i = find_first_zero_bit(vexpress_config_bridges_map, - ARRAY_SIZE(vexpress_config_bridges)); - if (i >= ARRAY_SIZE(vexpress_config_bridges)) { - pr_err("Can't register more bridges!\n"); - mutex_unlock(&vexpress_config_bridges_mutex); - return NULL; - } - __set_bit(i, vexpress_config_bridges_map); - bridge = &vexpress_config_bridges[i]; - - bridge->node = node; - bridge->info = info; - INIT_LIST_HEAD(&bridge->transactions); - spin_lock_init(&bridge->transactions_lock); - - mutex_unlock(&vexpress_config_bridges_mutex); - - return bridge; -} -EXPORT_SYMBOL(vexpress_config_bridge_register); - -void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge) -{ - struct vexpress_config_bridge __bridge = *bridge; - int i; - - mutex_lock(&vexpress_config_bridges_mutex); - for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) - if (&vexpress_config_bridges[i] == bridge) - __clear_bit(i, vexpress_config_bridges_map); - mutex_unlock(&vexpress_config_bridges_mutex); - - WARN_ON(!list_empty(&__bridge.transactions)); - while (!list_empty(&__bridge.transactions)) - cpu_relax(); -} -EXPORT_SYMBOL(vexpress_config_bridge_unregister); - - -struct vexpress_config_func { - struct vexpress_config_bridge *bridge; - void *func; -}; - -struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, - struct device_node *node) -{ - struct device_node *bridge_node; - struct vexpress_config_func *func; - int i; - - if (WARN_ON(dev && node && dev->of_node != node)) - return NULL; - if (dev && !node) - node = dev->of_node; - - func = kzalloc(sizeof(*func), GFP_KERNEL); - if (!func) - return NULL; - - bridge_node = of_node_get(node); - while (bridge_node) { - const __be32 *prop = of_get_property(bridge_node, - "arm,vexpress,config-bridge", NULL); - - if (prop) { - bridge_node = of_find_node_by_phandle( - be32_to_cpup(prop)); - break; - } - - bridge_node = of_get_next_parent(bridge_node); - } - - mutex_lock(&vexpress_config_bridges_mutex); - for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) { - struct vexpress_config_bridge *bridge = - &vexpress_config_bridges[i]; - - if (test_bit(i, vexpress_config_bridges_map) && - bridge->node == bridge_node) { - func->bridge = bridge; - func->func = bridge->info->func_get(dev, node); - break; - } - } - mutex_unlock(&vexpress_config_bridges_mutex); - - if (!func->func) { - of_node_put(node); - kfree(func); - return NULL; - } - - return func; -} -EXPORT_SYMBOL(__vexpress_config_func_get); - -void vexpress_config_func_put(struct vexpress_config_func *func) -{ - func->bridge->info->func_put(func->func); - of_node_put(func->bridge->node); - kfree(func); -} -EXPORT_SYMBOL(vexpress_config_func_put); - -struct vexpress_config_trans { - struct vexpress_config_func *func; - int offset; - bool write; - u32 *data; - int status; - struct completion completion; - struct list_head list; -}; - -static void vexpress_config_dump_trans(const char *what, - struct vexpress_config_trans *trans) -{ - pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n", - what, trans->write ? "write" : "read", trans, - trans->func->func, trans->offset, - trans->data ? *trans->data : 0, trans->status); -} - -static int vexpress_config_schedule(struct vexpress_config_trans *trans) -{ - int status; - struct vexpress_config_bridge *bridge = trans->func->bridge; - unsigned long flags; - - init_completion(&trans->completion); - trans->status = -EFAULT; - - spin_lock_irqsave(&bridge->transactions_lock, flags); - - if (list_empty(&bridge->transactions)) { - vexpress_config_dump_trans("Executing", trans); - status = bridge->info->func_exec(trans->func->func, - trans->offset, trans->write, trans->data); - } else { - vexpress_config_dump_trans("Queuing", trans); - status = VEXPRESS_CONFIG_STATUS_WAIT; - } - - switch (status) { - case VEXPRESS_CONFIG_STATUS_DONE: - vexpress_config_dump_trans("Finished", trans); - trans->status = status; - break; - case VEXPRESS_CONFIG_STATUS_WAIT: - list_add_tail(&trans->list, &bridge->transactions); - break; - } - - spin_unlock_irqrestore(&bridge->transactions_lock, flags); - - return status; -} - -void vexpress_config_complete(struct vexpress_config_bridge *bridge, - int status) -{ - struct vexpress_config_trans *trans; - unsigned long flags; - const char *message = "Completed"; - - spin_lock_irqsave(&bridge->transactions_lock, flags); - - trans = list_first_entry(&bridge->transactions, - struct vexpress_config_trans, list); - trans->status = status; - - do { - vexpress_config_dump_trans(message, trans); - list_del(&trans->list); - complete(&trans->completion); - - if (list_empty(&bridge->transactions)) - break; - - trans = list_first_entry(&bridge->transactions, - struct vexpress_config_trans, list); - vexpress_config_dump_trans("Executing pending", trans); - trans->status = bridge->info->func_exec(trans->func->func, - trans->offset, trans->write, trans->data); - message = "Finished pending"; - } while (trans->status == VEXPRESS_CONFIG_STATUS_DONE); - - spin_unlock_irqrestore(&bridge->transactions_lock, flags); -} -EXPORT_SYMBOL(vexpress_config_complete); - -int vexpress_config_wait(struct vexpress_config_trans *trans) -{ - wait_for_completion(&trans->completion); - - return trans->status; -} -EXPORT_SYMBOL(vexpress_config_wait); - -int vexpress_config_read(struct vexpress_config_func *func, int offset, - u32 *data) -{ - struct vexpress_config_trans trans = { - .func = func, - .offset = offset, - .write = false, - .data = data, - .status = 0, - }; - int status = vexpress_config_schedule(&trans); - - if (status == VEXPRESS_CONFIG_STATUS_WAIT) - status = vexpress_config_wait(&trans); - - return status; -} -EXPORT_SYMBOL(vexpress_config_read); - -int vexpress_config_write(struct vexpress_config_func *func, int offset, - u32 data) -{ - struct vexpress_config_trans trans = { - .func = func, - .offset = offset, - .write = true, - .data = &data, - .status = 0, - }; - int status = vexpress_config_schedule(&trans); - - if (status == VEXPRESS_CONFIG_STATUS_WAIT) - status = vexpress_config_wait(&trans); - - return status; -} -EXPORT_SYMBOL(vexpress_config_write); diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 35281e804e7e..b4138a7168db 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -72,9 +74,18 @@ static void __iomem *vexpress_sysreg_base; static struct device *vexpress_sysreg_dev; -static int vexpress_master_site; +static LIST_HEAD(vexpress_sysreg_config_funcs); +static struct device *vexpress_sysreg_config_bridge; +static int vexpress_sysreg_get_master(void) +{ + if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) + return VEXPRESS_SITE_DB2; + + return VEXPRESS_SITE_DB1; +} + void vexpress_flags_set(u32 data) { writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); @@ -84,7 +95,7 @@ void vexpress_flags_set(u32 data) u32 vexpress_get_procid(int site) { if (site == VEXPRESS_SITE_MASTER) - site = vexpress_master_site; + site = vexpress_sysreg_get_master(); return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? SYS_PROCID0 : SYS_PROCID1)); @@ -114,130 +125,33 @@ void __iomem *vexpress_get_24mhz_clock_base(void) } -static void vexpress_sysreg_find_prop(struct device_node *node, - const char *name, u32 *val) -{ - of_node_get(node); - while (node) { - if (of_property_read_u32(node, name, val) == 0) { - of_node_put(node); - return; - } - node = of_get_next_parent(node); - } -} - -unsigned __vexpress_get_site(struct device *dev, struct device_node *node) -{ - u32 site = 0; - - WARN_ON(dev && node && dev->of_node != node); - if (dev && !node) - node = dev->of_node; - - if (node) { - vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); - } else if (dev && dev->bus == &platform_bus_type) { - struct platform_device *pdev = to_platform_device(dev); - - if (pdev->num_resources == 1 && - pdev->resource[0].flags == IORESOURCE_BUS) - site = pdev->resource[0].start; - } else if (dev && strncmp(dev_name(dev), "ct:", 3) == 0) { - site = VEXPRESS_SITE_MASTER; - } - - if (site == VEXPRESS_SITE_MASTER) - site = vexpress_master_site; - - return site; -} - - struct vexpress_sysreg_config_func { - u32 template; - u32 device; + struct list_head list; + struct regmap *regmap; + int num_templates; + u32 template[0]; /* Keep this last */ }; -static struct vexpress_config_bridge *vexpress_sysreg_config_bridge; -static struct timer_list vexpress_sysreg_config_timer; -static u32 *vexpress_sysreg_config_data; -static int vexpress_sysreg_config_tries; - -static void *vexpress_sysreg_config_func_get(struct device *dev, - struct device_node *node) +static int vexpress_sysreg_config_exec(struct vexpress_sysreg_config_func *func, + int index, bool write, u32 *data) { - struct vexpress_sysreg_config_func *config_func; - u32 site = 0; - u32 position = 0; - u32 dcc = 0; - u32 func_device[2]; - int err = -EFAULT; - - if (node) { - of_node_get(node); - vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); - vexpress_sysreg_find_prop(node, "arm,vexpress,position", - &position); - vexpress_sysreg_find_prop(node, "arm,vexpress,dcc", &dcc); - err = of_property_read_u32_array(node, - "arm,vexpress-sysreg,func", func_device, - ARRAY_SIZE(func_device)); - of_node_put(node); - } else if (dev && dev->bus == &platform_bus_type) { - struct platform_device *pdev = to_platform_device(dev); - - if (pdev->num_resources == 1 && - pdev->resource[0].flags == IORESOURCE_BUS) { - site = pdev->resource[0].start; - func_device[0] = pdev->resource[0].end; - func_device[1] = pdev->id; - err = 0; - } - } - if (err) - return NULL; - - config_func = kzalloc(sizeof(*config_func), GFP_KERNEL); - if (!config_func) - return NULL; - - config_func->template = SYS_CFGCTRL_DCC(dcc); - config_func->template |= SYS_CFGCTRL_FUNC(func_device[0]); - config_func->template |= SYS_CFGCTRL_SITE(site == VEXPRESS_SITE_MASTER ? - vexpress_master_site : site); - config_func->template |= SYS_CFGCTRL_POSITION(position); - config_func->device |= func_device[1]; - - dev_dbg(vexpress_sysreg_dev, "func 0x%p = 0x%x, %d\n", config_func, - config_func->template, config_func->device); - - return config_func; -} - -static void vexpress_sysreg_config_func_put(void *func) -{ - kfree(func); -} - -static int vexpress_sysreg_config_func_exec(void *func, int offset, - bool write, u32 *data) -{ - int status; - struct vexpress_sysreg_config_func *config_func = func; - u32 command; + u32 command, status; + int tries; + long timeout; if (WARN_ON(!vexpress_sysreg_base)) return -ENOENT; + if (WARN_ON(index > func->num_templates)) + return -EINVAL; + command = readl(vexpress_sysreg_base + SYS_CFGCTRL); if (WARN_ON(command & SYS_CFGCTRL_START)) return -EBUSY; - command = SYS_CFGCTRL_START; + command = func->template[index]; + command |= SYS_CFGCTRL_START; command |= write ? SYS_CFGCTRL_WRITE : 0; - command |= config_func->template; - command |= SYS_CFGCTRL_DEVICE(config_func->device + offset); /* Use a canary for reads */ if (!write) @@ -250,90 +164,190 @@ static int vexpress_sysreg_config_func_exec(void *func, int offset, writel(command, vexpress_sysreg_base + SYS_CFGCTRL); mb(); - if (vexpress_sysreg_dev) { - /* Schedule completion check */ - if (!write) - vexpress_sysreg_config_data = data; - vexpress_sysreg_config_tries = 100; - mod_timer(&vexpress_sysreg_config_timer, - jiffies + usecs_to_jiffies(100)); - status = VEXPRESS_CONFIG_STATUS_WAIT; - } else { - /* Early execution, no timer available, have to spin */ - u32 cfgstat; + /* The operation can take ages... Go to sleep, 100us initially */ + tries = 100; + timeout = 100; + do { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(usecs_to_jiffies(timeout)); + if (signal_pending(current)) + return -EINTR; + + status = readl(vexpress_sysreg_base + SYS_CFGSTAT); + if (status & SYS_CFGSTAT_ERR) + return -EFAULT; + + if (timeout > 20) + timeout -= 20; + } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); + if (WARN_ON_ONCE(!tries)) + return -ETIMEDOUT; + + if (!write) { + *data = readl(vexpress_sysreg_base + SYS_CFGDATA); + dev_dbg(vexpress_sysreg_dev, "func %p, read data %x\n", + func, *data); + } - do { - cpu_relax(); - cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); - } while (!cfgstat); + return 0; +} - if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE)) - *data = readl(vexpress_sysreg_base + SYS_CFGDATA); - status = VEXPRESS_CONFIG_STATUS_DONE; +static int vexpress_sysreg_config_read(void *context, unsigned int index, + unsigned int *val) +{ + struct vexpress_sysreg_config_func *func = context; - if (cfgstat & SYS_CFGSTAT_ERR) - status = -EINVAL; - } + return vexpress_sysreg_config_exec(func, index, false, val); +} - return status; +static int vexpress_sysreg_config_write(void *context, unsigned int index, + unsigned int val) +{ + struct vexpress_sysreg_config_func *func = context; + + return vexpress_sysreg_config_exec(func, index, true, &val); } -struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = { - .name = "vexpress-sysreg", - .func_get = vexpress_sysreg_config_func_get, - .func_put = vexpress_sysreg_config_func_put, - .func_exec = vexpress_sysreg_config_func_exec, +struct regmap_config vexpress_sysreg_regmap_config = { + .lock = vexpress_config_lock, + .unlock = vexpress_config_unlock, + .reg_bits = 32, + .val_bits = 32, + .reg_read = vexpress_sysreg_config_read, + .reg_write = vexpress_sysreg_config_write, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, }; -static void vexpress_sysreg_config_complete(unsigned long data) +static struct regmap *vexpress_sysreg_config_regmap_init(struct device *dev, + void *context) { - int status = VEXPRESS_CONFIG_STATUS_DONE; - u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); - - if (cfgstat & SYS_CFGSTAT_ERR) - status = -EINVAL; - if (!vexpress_sysreg_config_tries--) - status = -ETIMEDOUT; - - if (status < 0) { - dev_err(vexpress_sysreg_dev, "error %d\n", status); - } else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) { - mod_timer(&vexpress_sysreg_config_timer, - jiffies + usecs_to_jiffies(50)); - return; + struct platform_device *pdev = to_platform_device(dev); + struct vexpress_sysreg_config_func *func; + struct property *prop; + const __be32 *val = NULL; + __be32 energy_quirk[4]; + int num; + u32 site, position, dcc; + int err; + int i; + + if (dev->of_node) { + err = vexpress_config_get_topo(dev->of_node, &site, &position, + &dcc); + if (err) + return ERR_PTR(err); + + prop = of_find_property(dev->of_node, + "arm,vexpress-sysreg,func", NULL); + if (!prop) + return ERR_PTR(-EINVAL); + + num = prop->length / sizeof(u32) / 2; + val = prop->value; + } else { + if (pdev->num_resources != 1 || + pdev->resource[0].flags != IORESOURCE_BUS) + return ERR_PTR(-EFAULT); + + site = pdev->resource[0].start; + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_sysreg_get_master(); + position = 0; + dcc = 0; + num = 1; } - if (vexpress_sysreg_config_data) { - *vexpress_sysreg_config_data = readl(vexpress_sysreg_base + - SYS_CFGDATA); - dev_dbg(vexpress_sysreg_dev, "read data %x\n", - *vexpress_sysreg_config_data); - vexpress_sysreg_config_data = NULL; + /* + * "arm,vexpress-energy" function used to be described + * by its first device only, now it requires both + */ + if (num == 1 && of_device_is_compatible(dev->of_node, + "arm,vexpress-energy")) { + num = 2; + energy_quirk[0] = *val; + energy_quirk[2] = *val++; + energy_quirk[1] = *val; + energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1); + val = energy_quirk; } - vexpress_config_complete(vexpress_sysreg_config_bridge, status); -} + func = kzalloc(sizeof(*func) + sizeof(*func->template) * num, + GFP_KERNEL); + if (!func) + return NULL; + func->num_templates = num; -void vexpress_sysreg_setup(struct device_node *node) -{ - if (WARN_ON(!vexpress_sysreg_base)) - return; + for (i = 0; i < num; i++) { + u32 function, device; - if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) - vexpress_master_site = VEXPRESS_SITE_DB2; + if (dev->of_node) { + function = be32_to_cpup(val++); + device = be32_to_cpup(val++); + } else { + function = pdev->resource[0].end; + device = pdev->id; + } + + dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", + func, site, position, dcc, + function, device); + + func->template[i] = SYS_CFGCTRL_DCC(dcc); + func->template[i] |= SYS_CFGCTRL_SITE(site); + func->template[i] |= SYS_CFGCTRL_POSITION(position); + func->template[i] |= SYS_CFGCTRL_FUNC(function); + func->template[i] |= SYS_CFGCTRL_DEVICE(device); + } + + vexpress_sysreg_regmap_config.max_register = num - 1; + + func->regmap = regmap_init(dev, NULL, func, + &vexpress_sysreg_regmap_config); + + if (IS_ERR(func->regmap)) + kfree(func); else - vexpress_master_site = VEXPRESS_SITE_DB1; + list_add(&func->list, &vexpress_sysreg_config_funcs); - vexpress_sysreg_config_bridge = vexpress_config_bridge_register( - node, &vexpress_sysreg_config_bridge_info); - WARN_ON(!vexpress_sysreg_config_bridge); + return func->regmap; +} + +static void vexpress_sysreg_config_regmap_exit(struct regmap *regmap, + void *context) +{ + struct vexpress_sysreg_config_func *func, *tmp; + + regmap_exit(regmap); + + list_for_each_entry_safe(func, tmp, &vexpress_sysreg_config_funcs, + list) { + if (func->regmap == regmap) { + list_del(&vexpress_sysreg_config_funcs); + kfree(func); + break; + } + } +} + +static struct vexpress_config_bridge_ops vexpress_sysreg_config_bridge_ops = { + .regmap_init = vexpress_sysreg_config_regmap_init, + .regmap_exit = vexpress_sysreg_config_regmap_exit, +}; + +int vexpress_sysreg_config_device_register(struct platform_device *pdev) +{ + pdev->dev.parent = vexpress_sysreg_config_bridge; + + return platform_device_register(pdev); } + void __init vexpress_sysreg_early_init(void __iomem *base) { vexpress_sysreg_base = base; - vexpress_sysreg_setup(NULL); + vexpress_config_set_master(vexpress_sysreg_get_master()); } void __init vexpress_sysreg_of_early_init(void) @@ -344,10 +358,14 @@ void __init vexpress_sysreg_of_early_init(void) return; node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); - if (node) { - vexpress_sysreg_base = of_iomap(node, 0); - vexpress_sysreg_setup(node); - } + if (WARN_ON(!node)) + return; + + vexpress_sysreg_base = of_iomap(node, 0); + if (WARN_ON(!vexpress_sysreg_base)) + return; + + vexpress_config_set_master(vexpress_sysreg_get_master()); } @@ -470,28 +488,22 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) return -EBUSY; } - if (!vexpress_sysreg_base) { + if (!vexpress_sysreg_base) vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - vexpress_sysreg_setup(pdev->dev.of_node); - } if (!vexpress_sysreg_base) { dev_err(&pdev->dev, "Failed to obtain base address!\n"); return -EFAULT; } - setup_timer(&vexpress_sysreg_config_timer, - vexpress_sysreg_config_complete, 0); - + vexpress_config_set_master(vexpress_sysreg_get_master()); vexpress_sysreg_dev = &pdev->dev; #ifdef CONFIG_GPIOLIB vexpress_sysreg_gpio_chip.dev = &pdev->dev; err = gpiochip_add(&vexpress_sysreg_gpio_chip); if (err) { - vexpress_config_bridge_unregister( - vexpress_sysreg_config_bridge); dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", err); return err; @@ -502,6 +514,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) sizeof(vexpress_sysreg_leds_pdata)); #endif + vexpress_sysreg_config_bridge = vexpress_config_bridge_register( + &pdev->dev, &vexpress_sysreg_config_bridge_ops, NULL); + WARN_ON(!vexpress_sysreg_config_bridge); + device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); return 0; @@ -522,7 +538,12 @@ static struct platform_driver vexpress_sysreg_driver = { static int __init vexpress_sysreg_init(void) { - vexpress_sysreg_of_early_init(); + struct device_node *node; + + /* Need the sysreg early, before any other device... */ + for_each_matching_node(node, vexpress_sysreg_match) + of_platform_device_create(node, NULL, NULL); + return platform_driver_register(&vexpress_sysreg_driver); } core_initcall(vexpress_sysreg_init); diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c index b95cf71ed695..4dc102e2b230 100644 --- a/drivers/power/reset/vexpress-poweroff.c +++ b/drivers/power/reset/vexpress-poweroff.c @@ -23,10 +23,10 @@ static void vexpress_reset_do(struct device *dev, const char *what) { int err = -ENOENT; - struct vexpress_config_func *func = dev_get_drvdata(dev); + struct regmap *reg = dev_get_drvdata(dev); - if (func) { - err = vexpress_config_write(func, 0, 0); + if (reg) { + err = regmap_write(reg, 0, 0); if (!err) mdelay(1000); } @@ -91,17 +91,17 @@ static int vexpress_reset_probe(struct platform_device *pdev) enum vexpress_reset_func func; const struct of_device_id *match = of_match_device(vexpress_reset_of_match, &pdev->dev); - struct vexpress_config_func *config_func; + struct regmap *regmap; if (match) func = (enum vexpress_reset_func)match->data; else func = pdev->id_entry->driver_data; - config_func = vexpress_config_func_get_by_dev(&pdev->dev); - if (!config_func) - return -EINVAL; - dev_set_drvdata(&pdev->dev, config_func); + regmap = devm_regmap_init_vexpress_config(&pdev->dev); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + dev_set_drvdata(&pdev->dev, regmap); switch (func) { case FUNC_SHUTDOWN: diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c index f3ae28a7e663..2863428813e4 100644 --- a/drivers/regulator/vexpress.c +++ b/drivers/regulator/vexpress.c @@ -26,14 +26,14 @@ struct vexpress_regulator { struct regulator_desc desc; struct regulator_dev *regdev; - struct vexpress_config_func *func; + struct regmap *regmap; }; static int vexpress_regulator_get_voltage(struct regulator_dev *regdev) { struct vexpress_regulator *reg = rdev_get_drvdata(regdev); u32 uV; - int err = vexpress_config_read(reg->func, 0, &uV); + int err = regmap_read(reg->regmap, 0, &uV); return err ? err : uV; } @@ -43,7 +43,7 @@ static int vexpress_regulator_set_voltage(struct regulator_dev *regdev, { struct vexpress_regulator *reg = rdev_get_drvdata(regdev); - return vexpress_config_write(reg->func, 0, min_uV); + return regmap_write(reg->regmap, 0, min_uV); } static struct regulator_ops vexpress_regulator_ops_ro = { @@ -57,22 +57,17 @@ static struct regulator_ops vexpress_regulator_ops = { static int vexpress_regulator_probe(struct platform_device *pdev) { - int err; struct vexpress_regulator *reg; struct regulator_init_data *init_data; struct regulator_config config = { }; reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL); - if (!reg) { - err = -ENOMEM; - goto error_kzalloc; - } + if (!reg) + return -ENOMEM; - reg->func = vexpress_config_func_get_by_dev(&pdev->dev); - if (!reg->func) { - err = -ENXIO; - goto error_get_func; - } + reg->regmap = devm_regmap_init_vexpress_config(&pdev->dev); + if (IS_ERR(reg->regmap)) + return PTR_ERR(reg->regmap); reg->desc.name = dev_name(&pdev->dev); reg->desc.type = REGULATOR_VOLTAGE; @@ -80,10 +75,8 @@ static int vexpress_regulator_probe(struct platform_device *pdev) reg->desc.continuous_voltage_range = true; init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); - if (!init_data) { - err = -EINVAL; - goto error_get_regulator_init_data; - } + if (!init_data) + return -EINVAL; init_data->constraints.apply_uV = 0; if (init_data->constraints.min_uV && init_data->constraints.max_uV) @@ -97,29 +90,11 @@ static int vexpress_regulator_probe(struct platform_device *pdev) config.of_node = pdev->dev.of_node; reg->regdev = devm_regulator_register(&pdev->dev, ®->desc, &config); - if (IS_ERR(reg->regdev)) { - err = PTR_ERR(reg->regdev); - goto error_regulator_register; - } + if (IS_ERR(reg->regdev)) + return PTR_ERR(reg->regdev); platform_set_drvdata(pdev, reg); - return 0; - -error_regulator_register: -error_get_regulator_init_data: - vexpress_config_func_put(reg->func); -error_get_func: -error_kzalloc: - return err; -} - -static int vexpress_regulator_remove(struct platform_device *pdev) -{ - struct vexpress_regulator *reg = platform_get_drvdata(pdev); - - vexpress_config_func_put(reg->func); - return 0; } @@ -130,7 +105,6 @@ static struct of_device_id vexpress_regulator_of_match[] = { static struct platform_driver vexpress_regulator_driver = { .probe = vexpress_regulator_probe, - .remove = vexpress_regulator_remove, .driver = { .name = DRVNAME, .owner = THIS_MODULE, -- cgit v1.2.3 From 29f9b6cf7bff6a118130163c848811e14f8022da Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 12 Feb 2014 10:47:10 +0000 Subject: mfd: syscon: Add platform data with a regmap config name Define syscon platform data structure that can be used to define a regmap config name. This is particularly useful in the regmap debugfs when there is more than one syscon device registered, to distinguish the register blocks. Signed-off-by: Pawel Moll Acked-by: Lee Jones --- drivers/mfd/syscon.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index dbea55de4397..e2a04bb8bc1e 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -119,6 +120,7 @@ static struct regmap_config syscon_regmap_config = { static int syscon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct syscon_platform_data *pdata = dev_get_platdata(dev); struct syscon *syscon; struct resource *res; void __iomem *base; @@ -136,6 +138,8 @@ static int syscon_probe(struct platform_device *pdev) return -ENOMEM; syscon_regmap_config.max_register = res->end - res->start - 3; + if (pdata) + syscon_regmap_config.name = pdata->label; syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_regmap_config); if (IS_ERR(syscon->regmap)) { -- cgit v1.2.3 From 974cc7b93441a0e78f030495436d1be7eb7c208d Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 23 Apr 2014 10:49:31 +0100 Subject: mfd: vexpress: Define the device as MFD cells This patch - finally, after over 6 months! :-( - addresses Samuel's request to split the vexpress-sysreg driver into smaller portions and define the device in a form of MFD cells: * LEDs code has been completely removed and replaced with "gpio-leds" nodes in the tree (referencing dedicated GPIO subnodes in sysreg - bindings documentation updated); this also better fits the reality as some variants of the motherboard don't have all the LEDs populated * syscfg bridge code has been extracted into a separate driver (placed in drivers/misc for no better place) * all the ID & MISC registers are defined as sysconf making them available for other drivers should they need to use them (and also to the user via /sys/kernel/debug/regmap which can be helpful in platform debugging) Signed-off-by: Pawel Moll Acked-by: Lee Jones --- drivers/mfd/Kconfig | 15 +- drivers/mfd/Makefile | 2 +- drivers/mfd/vexpress-sysreg.c | 533 +++++++++++------------------------------ drivers/misc/Kconfig | 9 + drivers/misc/Makefile | 1 + drivers/misc/vexpress-syscfg.c | 324 +++++++++++++++++++++++++ 6 files changed, 479 insertions(+), 405 deletions(-) create mode 100644 drivers/misc/vexpress-syscfg.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 33834120d057..490fd48a9541 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1227,12 +1227,17 @@ config MCP_UCB1200_TS endmenu -config VEXPRESS_CONFIG - bool "ARM Versatile Express platform infrastructure" - depends on ARM || ARM64 +config MFD_VEXPRESS_SYSREG + bool "Versatile Express System Registers" + depends on VEXPRESS_CONFIG + default y + select CLKSRC_MMIO + select GPIO_GENERIC_PLATFORM + select MFD_CORE + select MFD_SYSCON help - Platform configuration infrastructure for the ARM Ltd. - Versatile Express. + System Registers are the platform configuration block + on the ARM Ltd. Versatile Express board. endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9ba838eb5131..cec3487b539e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -161,7 +161,7 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o -obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-sysreg.o +obj-$(CONFIG_MFD_VEXPRESS_SYSREG) += vexpress-sysreg.o obj-$(CONFIG_MFD_RETU) += retu-mfd.o obj-$(CONFIG_MFD_AS3711) += as3711.o obj-$(CONFIG_MFD_AS3722) += as3722.o diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index b4138a7168db..952df843b6be 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -11,25 +11,22 @@ * Copyright (C) 2012 ARM Limited */ +#include #include -#include #include -#include +#include #include #include +#include #include -#include -#include #include #include -#include #include #define SYS_ID 0x000 #define SYS_SW 0x004 #define SYS_LED 0x008 #define SYS_100HZ 0x024 -#define SYS_FLAGS 0x030 #define SYS_FLAGSSET 0x030 #define SYS_FLAGSCLR 0x034 #define SYS_NVFLAGS 0x038 @@ -51,36 +48,32 @@ #define SYS_ID_HBI_SHIFT 16 #define SYS_PROCIDx_HBI_SHIFT 0 -#define SYS_LED_LED(n) (1 << (n)) - #define SYS_MCI_CARDIN (1 << 0) #define SYS_MCI_WPROT (1 << 1) -#define SYS_FLASH_WPn (1 << 0) - #define SYS_MISC_MASTERSITE (1 << 14) -#define SYS_CFGCTRL_START (1 << 31) -#define SYS_CFGCTRL_WRITE (1 << 30) -#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) -#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) -#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) -#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) -#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) -#define SYS_CFGSTAT_ERR (1 << 1) -#define SYS_CFGSTAT_COMPLETE (1 << 0) +static void __iomem *__vexpress_sysreg_base; +static void __iomem *vexpress_sysreg_base(void) +{ + if (!__vexpress_sysreg_base) { + struct device_node *node = of_find_compatible_node(NULL, NULL, + "arm,vexpress-sysreg"); -static void __iomem *vexpress_sysreg_base; -static struct device *vexpress_sysreg_dev; -static LIST_HEAD(vexpress_sysreg_config_funcs); -static struct device *vexpress_sysreg_config_bridge; + __vexpress_sysreg_base = of_iomap(node, 0); + } + + WARN_ON(!__vexpress_sysreg_base); + + return __vexpress_sysreg_base; +} static int vexpress_sysreg_get_master(void) { - if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) + if (readl(vexpress_sysreg_base() + SYS_MISC) & SYS_MISC_MASTERSITE) return VEXPRESS_SITE_DB2; return VEXPRESS_SITE_DB1; @@ -88,8 +81,13 @@ static int vexpress_sysreg_get_master(void) void vexpress_flags_set(u32 data) { - writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); - writel(data, vexpress_sysreg_base + SYS_FLAGSSET); + writel(~0, vexpress_sysreg_base() + SYS_FLAGSCLR); + writel(data, vexpress_sysreg_base() + SYS_FLAGSSET); +} + +unsigned int vexpress_get_mci_cardin(struct device *dev) +{ + return readl(vexpress_sysreg_base() + SYS_MCI) & SYS_MCI_CARDIN; } u32 vexpress_get_procid(int site) @@ -97,7 +95,7 @@ u32 vexpress_get_procid(int site) if (site == VEXPRESS_SITE_MASTER) site = vexpress_sysreg_get_master(); - return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? + return readl(vexpress_sysreg_base() + (site == VEXPRESS_SITE_DB1 ? SYS_PROCID0 : SYS_PROCID1)); } @@ -107,7 +105,7 @@ u32 vexpress_get_hbi(int site) switch (site) { case VEXPRESS_SITE_MB: - id = readl(vexpress_sysreg_base + SYS_ID); + id = readl(vexpress_sysreg_base() + SYS_ID); return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; case VEXPRESS_SITE_MASTER: case VEXPRESS_SITE_DB1: @@ -121,406 +119,143 @@ u32 vexpress_get_hbi(int site) void __iomem *vexpress_get_24mhz_clock_base(void) { - return vexpress_sysreg_base + SYS_24MHZ; -} - - -struct vexpress_sysreg_config_func { - struct list_head list; - struct regmap *regmap; - int num_templates; - u32 template[0]; /* Keep this last */ -}; - -static int vexpress_sysreg_config_exec(struct vexpress_sysreg_config_func *func, - int index, bool write, u32 *data) -{ - u32 command, status; - int tries; - long timeout; - - if (WARN_ON(!vexpress_sysreg_base)) - return -ENOENT; - - if (WARN_ON(index > func->num_templates)) - return -EINVAL; - - command = readl(vexpress_sysreg_base + SYS_CFGCTRL); - if (WARN_ON(command & SYS_CFGCTRL_START)) - return -EBUSY; - - command = func->template[index]; - command |= SYS_CFGCTRL_START; - command |= write ? SYS_CFGCTRL_WRITE : 0; - - /* Use a canary for reads */ - if (!write) - *data = 0xdeadbeef; - - dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n", - command, *data); - writel(*data, vexpress_sysreg_base + SYS_CFGDATA); - writel(0, vexpress_sysreg_base + SYS_CFGSTAT); - writel(command, vexpress_sysreg_base + SYS_CFGCTRL); - mb(); - - /* The operation can take ages... Go to sleep, 100us initially */ - tries = 100; - timeout = 100; - do { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(usecs_to_jiffies(timeout)); - if (signal_pending(current)) - return -EINTR; - - status = readl(vexpress_sysreg_base + SYS_CFGSTAT); - if (status & SYS_CFGSTAT_ERR) - return -EFAULT; - - if (timeout > 20) - timeout -= 20; - } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); - if (WARN_ON_ONCE(!tries)) - return -ETIMEDOUT; - - if (!write) { - *data = readl(vexpress_sysreg_base + SYS_CFGDATA); - dev_dbg(vexpress_sysreg_dev, "func %p, read data %x\n", - func, *data); - } - - return 0; -} - -static int vexpress_sysreg_config_read(void *context, unsigned int index, - unsigned int *val) -{ - struct vexpress_sysreg_config_func *func = context; - - return vexpress_sysreg_config_exec(func, index, false, val); -} - -static int vexpress_sysreg_config_write(void *context, unsigned int index, - unsigned int val) -{ - struct vexpress_sysreg_config_func *func = context; - - return vexpress_sysreg_config_exec(func, index, true, &val); -} - -struct regmap_config vexpress_sysreg_regmap_config = { - .lock = vexpress_config_lock, - .unlock = vexpress_config_unlock, - .reg_bits = 32, - .val_bits = 32, - .reg_read = vexpress_sysreg_config_read, - .reg_write = vexpress_sysreg_config_write, - .reg_format_endian = REGMAP_ENDIAN_LITTLE, - .val_format_endian = REGMAP_ENDIAN_LITTLE, -}; - -static struct regmap *vexpress_sysreg_config_regmap_init(struct device *dev, - void *context) -{ - struct platform_device *pdev = to_platform_device(dev); - struct vexpress_sysreg_config_func *func; - struct property *prop; - const __be32 *val = NULL; - __be32 energy_quirk[4]; - int num; - u32 site, position, dcc; - int err; - int i; - - if (dev->of_node) { - err = vexpress_config_get_topo(dev->of_node, &site, &position, - &dcc); - if (err) - return ERR_PTR(err); - - prop = of_find_property(dev->of_node, - "arm,vexpress-sysreg,func", NULL); - if (!prop) - return ERR_PTR(-EINVAL); - - num = prop->length / sizeof(u32) / 2; - val = prop->value; - } else { - if (pdev->num_resources != 1 || - pdev->resource[0].flags != IORESOURCE_BUS) - return ERR_PTR(-EFAULT); - - site = pdev->resource[0].start; - if (site == VEXPRESS_SITE_MASTER) - site = vexpress_sysreg_get_master(); - position = 0; - dcc = 0; - num = 1; - } - - /* - * "arm,vexpress-energy" function used to be described - * by its first device only, now it requires both - */ - if (num == 1 && of_device_is_compatible(dev->of_node, - "arm,vexpress-energy")) { - num = 2; - energy_quirk[0] = *val; - energy_quirk[2] = *val++; - energy_quirk[1] = *val; - energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1); - val = energy_quirk; - } - - func = kzalloc(sizeof(*func) + sizeof(*func->template) * num, - GFP_KERNEL); - if (!func) - return NULL; - - func->num_templates = num; - - for (i = 0; i < num; i++) { - u32 function, device; - - if (dev->of_node) { - function = be32_to_cpup(val++); - device = be32_to_cpup(val++); - } else { - function = pdev->resource[0].end; - device = pdev->id; - } - - dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", - func, site, position, dcc, - function, device); - - func->template[i] = SYS_CFGCTRL_DCC(dcc); - func->template[i] |= SYS_CFGCTRL_SITE(site); - func->template[i] |= SYS_CFGCTRL_POSITION(position); - func->template[i] |= SYS_CFGCTRL_FUNC(function); - func->template[i] |= SYS_CFGCTRL_DEVICE(device); - } - - vexpress_sysreg_regmap_config.max_register = num - 1; - - func->regmap = regmap_init(dev, NULL, func, - &vexpress_sysreg_regmap_config); - - if (IS_ERR(func->regmap)) - kfree(func); - else - list_add(&func->list, &vexpress_sysreg_config_funcs); - - return func->regmap; -} - -static void vexpress_sysreg_config_regmap_exit(struct regmap *regmap, - void *context) -{ - struct vexpress_sysreg_config_func *func, *tmp; - - regmap_exit(regmap); - - list_for_each_entry_safe(func, tmp, &vexpress_sysreg_config_funcs, - list) { - if (func->regmap == regmap) { - list_del(&vexpress_sysreg_config_funcs); - kfree(func); - break; - } - } -} - -static struct vexpress_config_bridge_ops vexpress_sysreg_config_bridge_ops = { - .regmap_init = vexpress_sysreg_config_regmap_init, - .regmap_exit = vexpress_sysreg_config_regmap_exit, -}; - -int vexpress_sysreg_config_device_register(struct platform_device *pdev) -{ - pdev->dev.parent = vexpress_sysreg_config_bridge; - - return platform_device_register(pdev); + return vexpress_sysreg_base() + SYS_24MHZ; } void __init vexpress_sysreg_early_init(void __iomem *base) { - vexpress_sysreg_base = base; - vexpress_config_set_master(vexpress_sysreg_get_master()); -} - -void __init vexpress_sysreg_of_early_init(void) -{ - struct device_node *node; - - if (vexpress_sysreg_base) - return; - - node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); - if (WARN_ON(!node)) - return; - - vexpress_sysreg_base = of_iomap(node, 0); - if (WARN_ON(!vexpress_sysreg_base)) - return; + __vexpress_sysreg_base = base; vexpress_config_set_master(vexpress_sysreg_get_master()); } -#ifdef CONFIG_GPIOLIB - -#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \ - [VEXPRESS_GPIO_##_name] = { \ - .reg = _reg, \ - .value = _reg##_##_value, \ - } +/* The sysreg block is just a random collection of various functions... */ -static struct vexpress_sysreg_gpio { - unsigned long reg; - u32 value; -} vexpress_sysreg_gpios[] = { - VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN), - VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT), - VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn), - VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)), - VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)), - VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)), - VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)), - VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)), - VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)), - VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)), - VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)), +static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = { + .label = "sys_id", }; -static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - return 0; -} - -static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, - unsigned offset) -{ - struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; - u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); - - return !!(reg_value & gpio->value); -} - -static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; - u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); - - if (value) - reg_value |= gpio->value; - else - reg_value &= ~gpio->value; - - writel(reg_value, vexpress_sysreg_base + gpio->reg); -} - -static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - vexpress_sysreg_gpio_set(chip, offset, value); - - return 0; -} - -static struct gpio_chip vexpress_sysreg_gpio_chip = { - .label = "vexpress-sysreg", - .direction_input = vexpress_sysreg_gpio_direction_input, - .direction_output = vexpress_sysreg_gpio_direction_output, - .get = vexpress_sysreg_gpio_get, - .set = vexpress_sysreg_gpio_set, - .ngpio = ARRAY_SIZE(vexpress_sysreg_gpios), - .base = 0, +static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = { + .label = "sys_led", + .base = -1, + .ngpio = 8, }; - -#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \ - { \ - .name = "v2m:green:"_name, \ - .default_trigger = _default_trigger, \ - .gpio = VEXPRESS_GPIO_##_gpio, \ - } - -struct gpio_led vexpress_sysreg_leds[] = { - VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0), - VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1), - VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2), - VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3), - VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4), - VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5), - VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6), - VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7), +static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = { + .label = "sys_mci", + .base = -1, + .ngpio = 2, }; -struct gpio_led_platform_data vexpress_sysreg_leds_pdata = { - .num_leds = ARRAY_SIZE(vexpress_sysreg_leds), - .leds = vexpress_sysreg_leds, +static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = { + .label = "sys_flash", + .base = -1, + .ngpio = 1, }; -#endif - +static struct syscon_platform_data vexpress_sysreg_sys_misc_pdata = { + .label = "sys_misc", +}; -static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID)); -} +static struct syscon_platform_data vexpress_sysreg_sys_procid_pdata = { + .label = "sys_procid", +}; -DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL); +static struct mfd_cell vexpress_sysreg_cells[] = { + { + .name = "syscon", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM(SYS_ID, 0x4), + }, + .platform_data = &vexpress_sysreg_sys_id_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_id_pdata), + }, { + .name = "basic-mmio-gpio", + .of_compatible = "arm,vexpress-sysreg,sys_led", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"), + }, + .platform_data = &vexpress_sysreg_sys_led_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata), + }, { + .name = "basic-mmio-gpio", + .of_compatible = "arm,vexpress-sysreg,sys_mci", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"), + }, + .platform_data = &vexpress_sysreg_sys_mci_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata), + }, { + .name = "basic-mmio-gpio", + .of_compatible = "arm,vexpress-sysreg,sys_flash", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"), + }, + .platform_data = &vexpress_sysreg_sys_flash_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata), + }, { + .name = "syscon", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM(SYS_MISC, 0x4), + }, + .platform_data = &vexpress_sysreg_sys_misc_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_misc_pdata), + }, { + .name = "syscon", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM(SYS_PROCID0, 0x8), + }, + .platform_data = &vexpress_sysreg_sys_procid_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_procid_pdata), + }, { + .name = "vexpress-syscfg", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM(SYS_CFGDATA, 0xc), + }, + } +}; static int vexpress_sysreg_probe(struct platform_device *pdev) { - int err; - struct resource *res = platform_get_resource(pdev, - IORESOURCE_MEM, 0); - - if (!devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "Failed to request memory region!\n"); - return -EBUSY; - } + struct resource *mem; + void __iomem *base; + struct bgpio_chip *mmc_gpio_chip; - if (!vexpress_sysreg_base) - vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return -EINVAL; - if (!vexpress_sysreg_base) { - dev_err(&pdev->dev, "Failed to obtain base address!\n"); - return -EFAULT; - } + base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!base) + return -ENOMEM; vexpress_config_set_master(vexpress_sysreg_get_master()); - vexpress_sysreg_dev = &pdev->dev; - -#ifdef CONFIG_GPIOLIB - vexpress_sysreg_gpio_chip.dev = &pdev->dev; - err = gpiochip_add(&vexpress_sysreg_gpio_chip); - if (err) { - dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", - err); - return err; - } - platform_device_register_data(vexpress_sysreg_dev, "leds-gpio", - PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata, - sizeof(vexpress_sysreg_leds_pdata)); -#endif - - vexpress_sysreg_config_bridge = vexpress_config_bridge_register( - &pdev->dev, &vexpress_sysreg_config_bridge_ops, NULL); - WARN_ON(!vexpress_sysreg_config_bridge); - - device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); - - return 0; + /* + * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with + * older trees using sysreg node for MMC control lines. + */ + mmc_gpio_chip = devm_kzalloc(&pdev->dev, sizeof(*mmc_gpio_chip), + GFP_KERNEL); + if (!mmc_gpio_chip) + return -ENOMEM; + bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI, + NULL, NULL, NULL, NULL, 0); + mmc_gpio_chip->gc.ngpio = 2; + gpiochip_add(&mmc_gpio_chip->gc); + + return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, + vexpress_sysreg_cells, + ARRAY_SIZE(vexpress_sysreg_cells), mem, 0, NULL); } static const struct of_device_id vexpress_sysreg_match[] = { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8baff0effc7d..d9663ef90ce8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -515,6 +515,15 @@ config SRAM the genalloc API. It is supposed to be used for small on-chip SRAM areas found on many SoCs. +config VEXPRESS_SYSCFG + bool "Versatile Express System Configuration driver" + depends on VEXPRESS_CONFIG + default y + help + ARM Ltd. Versatile Express uses specialised platform configuration + bus. System Configuration interface is one of the possible means + of generating transactions on this bus. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7eb4b69580c0..d59ce1261b38 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,3 +55,4 @@ obj-$(CONFIG_SRAM) += sram.o obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ +obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c new file mode 100644 index 000000000000..73068e50e56d --- /dev/null +++ b/drivers/misc/vexpress-syscfg.c @@ -0,0 +1,324 @@ +/* + * 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 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. + * + * Copyright (C) 2014 ARM Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define SYS_CFGDATA 0x0 + +#define SYS_CFGCTRL 0x4 +#define SYS_CFGCTRL_START (1 << 31) +#define SYS_CFGCTRL_WRITE (1 << 30) +#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) +#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) +#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) +#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) +#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) + +#define SYS_CFGSTAT 0x8 +#define SYS_CFGSTAT_ERR (1 << 1) +#define SYS_CFGSTAT_COMPLETE (1 << 0) + + +struct vexpress_syscfg { + struct device *dev; + void __iomem *base; + struct list_head funcs; +}; + +struct vexpress_syscfg_func { + struct list_head list; + struct vexpress_syscfg *syscfg; + struct regmap *regmap; + int num_templates; + u32 template[0]; /* Keep it last! */ +}; + + +static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func, + int index, bool write, u32 *data) +{ + struct vexpress_syscfg *syscfg = func->syscfg; + u32 command, status; + int tries; + long timeout; + + if (WARN_ON(index > func->num_templates)) + return -EINVAL; + + command = readl(syscfg->base + SYS_CFGCTRL); + if (WARN_ON(command & SYS_CFGCTRL_START)) + return -EBUSY; + + command = func->template[index]; + command |= SYS_CFGCTRL_START; + command |= write ? SYS_CFGCTRL_WRITE : 0; + + /* Use a canary for reads */ + if (!write) + *data = 0xdeadbeef; + + dev_dbg(syscfg->dev, "func %p, command %x, data %x\n", + func, command, *data); + writel(*data, syscfg->base + SYS_CFGDATA); + writel(0, syscfg->base + SYS_CFGSTAT); + writel(command, syscfg->base + SYS_CFGCTRL); + mb(); + + /* The operation can take ages... Go to sleep, 100us initially */ + tries = 100; + timeout = 100; + do { + if (!irqs_disabled()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(usecs_to_jiffies(timeout)); + if (signal_pending(current)) + return -EINTR; + } else { + udelay(timeout); + } + + status = readl(syscfg->base + SYS_CFGSTAT); + if (status & SYS_CFGSTAT_ERR) + return -EFAULT; + + if (timeout > 20) + timeout -= 20; + } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); + if (WARN_ON_ONCE(!tries)) + return -ETIMEDOUT; + + if (!write) { + *data = readl(syscfg->base + SYS_CFGDATA); + dev_dbg(syscfg->dev, "func %p, read data %x\n", func, *data); + } + + return 0; +} + +static int vexpress_syscfg_read(void *context, unsigned int index, + unsigned int *val) +{ + struct vexpress_syscfg_func *func = context; + + return vexpress_syscfg_exec(func, index, false, val); +} + +static int vexpress_syscfg_write(void *context, unsigned int index, + unsigned int val) +{ + struct vexpress_syscfg_func *func = context; + + return vexpress_syscfg_exec(func, index, true, &val); +} + +struct regmap_config vexpress_syscfg_regmap_config = { + .lock = vexpress_config_lock, + .unlock = vexpress_config_unlock, + .reg_bits = 32, + .val_bits = 32, + .reg_read = vexpress_syscfg_read, + .reg_write = vexpress_syscfg_write, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + + +static struct regmap *vexpress_syscfg_regmap_init(struct device *dev, + void *context) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vexpress_syscfg *syscfg = context; + struct vexpress_syscfg_func *func; + struct property *prop; + const __be32 *val = NULL; + __be32 energy_quirk[4]; + int num; + u32 site, position, dcc; + int i; + + if (dev->of_node) { + int err = vexpress_config_get_topo(dev->of_node, &site, + &position, &dcc); + + if (err) + return ERR_PTR(err); + + prop = of_find_property(dev->of_node, + "arm,vexpress-sysreg,func", NULL); + if (!prop) + return ERR_PTR(-EINVAL); + + num = prop->length / sizeof(u32) / 2; + val = prop->value; + } else { + if (pdev->num_resources != 1 || + pdev->resource[0].flags != IORESOURCE_BUS) + return ERR_PTR(-EFAULT); + + site = pdev->resource[0].start; + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_config_get_master(); + position = 0; + dcc = 0; + num = 1; + } + + /* + * "arm,vexpress-energy" function used to be described + * by its first device only, now it requires both + */ + if (num == 1 && of_device_is_compatible(dev->of_node, + "arm,vexpress-energy")) { + num = 2; + energy_quirk[0] = *val; + energy_quirk[2] = *val++; + energy_quirk[1] = *val; + energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1); + val = energy_quirk; + } + + func = kzalloc(sizeof(*func) + sizeof(*func->template) * num, + GFP_KERNEL); + if (!func) + return NULL; + + func->syscfg = syscfg; + func->num_templates = num; + + for (i = 0; i < num; i++) { + u32 function, device; + + if (dev->of_node) { + function = be32_to_cpup(val++); + device = be32_to_cpup(val++); + } else { + function = pdev->resource[0].end; + device = pdev->id; + } + + dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", + func, site, position, dcc, + function, device); + + func->template[i] = SYS_CFGCTRL_DCC(dcc); + func->template[i] |= SYS_CFGCTRL_SITE(site); + func->template[i] |= SYS_CFGCTRL_POSITION(position); + func->template[i] |= SYS_CFGCTRL_FUNC(function); + func->template[i] |= SYS_CFGCTRL_DEVICE(device); + } + + vexpress_syscfg_regmap_config.max_register = num - 1; + + func->regmap = regmap_init(dev, NULL, func, + &vexpress_syscfg_regmap_config); + + if (IS_ERR(func->regmap)) + kfree(func); + else + list_add(&func->list, &syscfg->funcs); + + return func->regmap; +} + +static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context) +{ + struct vexpress_syscfg *syscfg = context; + struct vexpress_syscfg_func *func, *tmp; + + regmap_exit(regmap); + + list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) { + if (func->regmap == regmap) { + list_del(&syscfg->funcs); + kfree(func); + break; + } + } +} + +static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = { + .regmap_init = vexpress_syscfg_regmap_init, + .regmap_exit = vexpress_syscfg_regmap_exit, +}; + + +/* Non-DT hack, to be gone... */ +static struct device *vexpress_syscfg_bridge; + +int vexpress_syscfg_device_register(struct platform_device *pdev) +{ + pdev->dev.parent = vexpress_syscfg_bridge; + + return platform_device_register(pdev); +} + + +int vexpress_syscfg_probe(struct platform_device *pdev) +{ + struct vexpress_syscfg *syscfg; + struct resource *res; + struct device *bridge; + + syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL); + if (!syscfg) + return -ENOMEM; + syscfg->dev = &pdev->dev; + INIT_LIST_HEAD(&syscfg->funcs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) + return -EBUSY; + + syscfg->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!syscfg->base) + return -EFAULT; + + /* Must use dev.parent (MFD), as that's where DT phandle points at... */ + bridge = vexpress_config_bridge_register(pdev->dev.parent, + &vexpress_syscfg_bridge_ops, syscfg); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + + /* Non-DT case */ + if (!pdev->dev.of_node) + vexpress_syscfg_bridge = bridge; + + return 0; +} + +static const struct platform_device_id vexpress_syscfg_id_table[] = { + { "vexpress-syscfg", }, + {}, +}; + +static struct platform_driver vexpress_syscfg_driver = { + .driver.name = "vexpress-syscfg", + .id_table = vexpress_syscfg_id_table, + .probe = vexpress_syscfg_probe, +}; + +static int __init vexpress_syscfg_init(void) +{ + return platform_driver_register(&vexpress_syscfg_driver); +} +core_initcall(vexpress_syscfg_init); -- cgit v1.2.3 From 5ee2b877793d89f1d73338d08af12f73fbbfed4a Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Tue, 17 Sep 2013 17:16:15 +0100 Subject: clk: versatile: Split config options for sp810 and vexpress_osc Move the Kconfig entry for Versatile (& Express) clock drivers into a separate file and add individual options for sp810 and vexpress_osc drivers, as they are optional in some configurations and may have separate dependencies. Signed-off-by: Pawel Moll Acked-by: Mike Turquette --- drivers/clk/Kconfig | 9 +-------- drivers/clk/versatile/Kconfig | 26 ++++++++++++++++++++++++++ drivers/clk/versatile/Makefile | 5 +++-- 3 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 drivers/clk/versatile/Kconfig (limited to 'drivers') diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6f56d3a4f010..4fdfd6c70bd3 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -30,14 +30,7 @@ config COMMON_CLK_WM831X Supports the clocking subsystem of the WM831x/2x series of PMICs from Wolfson Microlectronics. -config COMMON_CLK_VERSATILE - bool "Clock driver for ARM Reference designs" - depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64 - ---help--- - Supports clocking on ARM Reference designs: - - Integrator/AP and Integrator/CP - - RealView PB1176, EB, PB11MP and PBX - - Versatile Express +source "drivers/clk/versatile/Kconfig" config COMMON_CLK_MAX77686 tristate "Clock driver for Maxim 77686 MFD" diff --git a/drivers/clk/versatile/Kconfig b/drivers/clk/versatile/Kconfig new file mode 100644 index 000000000000..1530c9352a76 --- /dev/null +++ b/drivers/clk/versatile/Kconfig @@ -0,0 +1,26 @@ +config COMMON_CLK_VERSATILE + bool "Clock driver for ARM Reference designs" + depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64 + ---help--- + Supports clocking on ARM Reference designs: + - Integrator/AP and Integrator/CP + - RealView PB1176, EB, PB11MP and PBX + - Versatile Express + +config CLK_SP810 + bool "Clock driver for ARM SP810 System Controller" + depends on COMMON_CLK_VERSATILE + default y if ARCH_VEXPRESS + ---help--- + Supports clock muxing (REFCLK/TIMCLK to TIMERCLKEN0-3) capabilities + of the ARM SP810 System Controller cell. + +config CLK_VEXPRESS_OSC + bool "Clock driver for Versatile Express OSC clock generators" + depends on COMMON_CLK_VERSATILE + depends on VEXPRESS_CONFIG + default y if ARCH_VEXPRESS + ---help--- + Simple regmap-based driver driving clock generators on Versatile + Express platforms hidden behind its configuration infrastructure, + commonly known as OSCs. diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile index c16ca787170a..fd449f9b006d 100644 --- a/drivers/clk/versatile/Makefile +++ b/drivers/clk/versatile/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_ICST) += clk-icst.o obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o -obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o clk-sp810.o -obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o +obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o +obj-$(CONFIG_CLK_SP810) += clk-sp810.o +obj-$(CONFIG_CLK_VEXPRESS_OSC) += clk-vexpress-osc.o -- cgit v1.2.3 From 220e2a8d22cd57d5ec8111465923c6c25691394d Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 16 Apr 2014 18:22:59 +0100 Subject: clocksource: Sched clock source for Versatile Express This patch adds a trival sched clock source using free running, 24MHz clocked counter present in the ARM Ltd. reference platforms (Versatile, RealView, Versatile Express) System Registers block. This code replaces the call in the VE machine code. Signed-off-by: Pawel Moll Reviewed-by: Linus Walleij --- drivers/clocksource/Kconfig | 11 +++++++++++ drivers/clocksource/Makefile | 1 + drivers/clocksource/versatile.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 drivers/clocksource/versatile.c (limited to 'drivers') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 96918e1f26a3..2c27b02f0860 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -191,3 +191,14 @@ config EM_TIMER_STI config CLKSRC_QCOM bool + +config CLKSRC_VERSATILE + bool "ARM Versatile (Express) reference platforms clock source" + depends on GENERIC_SCHED_CLOCK + select CLKSRC_OF + default y if MFD_VEXPRESS_SYSREG + help + This option enables clock source based on free running + counter available in the "System Registers" block of + ARM Versatile, RealView and Versatile Express reference + platforms. diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 98cb6c51aa87..6f25bdffc176 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o +obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o diff --git a/drivers/clocksource/versatile.c b/drivers/clocksource/versatile.c new file mode 100644 index 000000000000..e4c50ad2f9d9 --- /dev/null +++ b/drivers/clocksource/versatile.c @@ -0,0 +1,40 @@ +/* + * 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 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. + * + * Copyright (C) 2014 ARM Limited + */ + +#include +#include +#include +#include + +#define SYS_24MHZ 0x05c + +static void __iomem *versatile_sys_24mhz; + +static u32 notrace versatile_sys_24mhz_read(void) +{ + return readl(versatile_sys_24mhz); +} + +static void __init versatile_sched_clock_init(struct device_node *node) +{ + void __iomem *base = of_iomap(node, 0); + + if (!base) + return; + + versatile_sys_24mhz = base + SYS_24MHZ; + + setup_sched_clock(versatile_sys_24mhz_read, 32, 24000000); +} +CLOCKSOURCE_OF_DECLARE(versatile, "arm,vexpress-sysreg", + versatile_sched_clock_init); -- cgit v1.2.3 From 6b2c31c71d6fa8896c5f3f2354d790a5bd3f0a1e Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Thu, 6 Feb 2014 14:33:44 +0000 Subject: ARM: vexpress: move HBI check to sysreg driver The last reason for static memory mapping is the HBI (board identification number) check early in the machine code. Moving the check to the sysreg driver makes it possible to completely remove the early mapping and init functions. Signed-off-by: Pawel Moll Acked-by: Lee Jones --- drivers/mfd/vexpress-sysreg.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 952df843b6be..9e21e4fc9599 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -45,7 +45,6 @@ #define SYS_CFGSTAT 0x0a8 #define SYS_HBI_MASK 0xfff -#define SYS_ID_HBI_SHIFT 16 #define SYS_PROCIDx_HBI_SHIFT 0 #define SYS_MCI_CARDIN (1 << 0) @@ -99,24 +98,6 @@ u32 vexpress_get_procid(int site) SYS_PROCID0 : SYS_PROCID1)); } -u32 vexpress_get_hbi(int site) -{ - u32 id; - - switch (site) { - case VEXPRESS_SITE_MB: - id = readl(vexpress_sysreg_base() + SYS_ID); - return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; - case VEXPRESS_SITE_MASTER: - case VEXPRESS_SITE_DB1: - case VEXPRESS_SITE_DB2: - id = vexpress_get_procid(site); - return (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; - } - - return ~0; -} - void __iomem *vexpress_get_24mhz_clock_base(void) { return vexpress_sysreg_base() + SYS_24MHZ; @@ -229,6 +210,7 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) struct resource *mem; void __iomem *base; struct bgpio_chip *mmc_gpio_chip; + u32 dt_hbi; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) @@ -240,6 +222,16 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) vexpress_config_set_master(vexpress_sysreg_get_master()); + /* Confirm board type against DT property, if available */ + if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) { + u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER); + u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; + + if (WARN_ON(dt_hbi != hbi)) + dev_warn(&pdev->dev, "DT HBI (%x) is not matching hardware (%x)!\n", + dt_hbi, hbi); + } + /* * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with * older trees using sysreg node for MMC control lines. -- cgit v1.2.3 From be1f7c8d7e2bc8b8c76846aa6f276e8d2ef8975a Mon Sep 17 00:00:00 2001 From: Jonghwan Choi Date: Sat, 17 May 2014 08:19:30 +0900 Subject: cpufreq: exynos: Fix the compile error Commit 7da83a80 ("ARM: EXYNOS: Migrate Exynos specific macros from plat to mach") which lands in samsung tree causes build breakage for cpufreq-exynos like following: drivers/cpufreq/exynos-cpufreq.c: In function 'exynos_cpufreq_probe': drivers/cpufreq/exynos-cpufreq.c:166:2: error: implicit declaration of function 'soc_is_exynos4210' [-Werror=implicit-function-declaration] drivers/cpufreq/exynos-cpufreq.c:168:2: error: implicit declaration of function 'soc_is_exynos4212' [-Werror=implicit-function-declaration] drivers/cpufreq/exynos-cpufreq.c:168:2: error: implicit declaration of function 'soc_is_exynos4412' [-Werror=implicit-function-declaration] drivers/cpufreq/exynos-cpufreq.c:170:2: error: implicit declaration of function 'soc_is_exynos5250' [-Werror=implicit-function-declaration] cc1: some warnings being treated as errors make[2]: *** [drivers/cpufreq/exynos-cpufreq.o] Error 1 make[2]: *** Waiting for unfinished jobs.... drivers/cpufreq/exynos4x12-cpufreq.c: In function 'exynos4x12_set_clkdiv': drivers/cpufreq/exynos4x12-cpufreq.c:118:2: error: implicit declaration of function 'soc_is_exynos4212' [-Werror=implicit-function-declaration] cc1: some warnings being treated as errors make[2]: *** [drivers/cpufreq/exynos4x12-cpufreq.o] Error 1 make[1]: *** [drivers/cpufreq] Error 2 This fixes above error with getting SoC information via of_machine_is_compatible() instead of soc_is_exynosXXXX(). Suggested-by: Tomasz Figa Signed-off-by: Jonghwan Choi [kgene.kim@samsung.com: fixed typo and modified as per Viresh's suggestion] [kgene.kim@samsung.com: Rafael agreed] Signed-off-by: Kukjin Kim --- drivers/cpufreq/exynos-cpufreq.c | 19 ++++++++++++++----- drivers/cpufreq/exynos-cpufreq.h | 8 ++++++++ drivers/cpufreq/exynos4x12-cpufreq.c | 11 ++++------- 3 files changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index f99cfe24e7bc..e8a4a7ed38c1 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -163,14 +164,22 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) if (!exynos_info) return -ENOMEM; - if (soc_is_exynos4210()) + if (of_machine_is_compatible("samsung,exynos4210")) { + exynos_info->type = EXYNOS_SOC_4210; ret = exynos4210_cpufreq_init(exynos_info); - else if (soc_is_exynos4212() || soc_is_exynos4412()) + } else if (of_machine_is_compatible("samsung,exynos4212")) { + exynos_info->type = EXYNOS_SOC_4212; ret = exynos4x12_cpufreq_init(exynos_info); - else if (soc_is_exynos5250()) + } else if (of_machine_is_compatible("samsung,exynos4412")) { + exynos_info->type = EXYNOS_SOC_4412; + ret = exynos4x12_cpufreq_init(exynos_info); + } else if (of_machine_is_compatible("samsung,exynos5250")) { + exynos_info->type = EXYNOS_SOC_5250; ret = exynos5250_cpufreq_init(exynos_info); - else - return 0; + } else { + pr_err("%s: Unknown SoC type\n", __func__); + return -ENODEV; + } if (ret) goto err_vdd_arm; diff --git a/drivers/cpufreq/exynos-cpufreq.h b/drivers/cpufreq/exynos-cpufreq.h index 3ddade8a5125..f189547bb447 100644 --- a/drivers/cpufreq/exynos-cpufreq.h +++ b/drivers/cpufreq/exynos-cpufreq.h @@ -17,6 +17,13 @@ enum cpufreq_level_index { L20, }; +enum exynos_soc_type { + EXYNOS_SOC_4210, + EXYNOS_SOC_4212, + EXYNOS_SOC_4412, + EXYNOS_SOC_5250, +}; + #define APLL_FREQ(f, a0, a1, a2, a3, a4, a5, a6, a7, b0, b1, b2, m, p, s) \ { \ .freq = (f) * 1000, \ @@ -34,6 +41,7 @@ struct apll_freq { }; struct exynos_dvfs_info { + enum exynos_soc_type type; unsigned long mpll_freq_khz; unsigned int pll_safe_idx; struct clk *cpu_clk; diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c index 466c76ad335b..63a3907ce578 100644 --- a/drivers/cpufreq/exynos4x12-cpufreq.c +++ b/drivers/cpufreq/exynos4x12-cpufreq.c @@ -100,7 +100,6 @@ static struct apll_freq apll_freq_4412[] = { static void exynos4x12_set_clkdiv(unsigned int div_index) { unsigned int tmp; - unsigned int stat_cpu1; /* Change Divider - CPU0 */ @@ -115,13 +114,11 @@ static void exynos4x12_set_clkdiv(unsigned int div_index) tmp = apll_freq_4x12[div_index].clk_div_cpu1; __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1); - if (soc_is_exynos4212()) - stat_cpu1 = 0x11; - else - stat_cpu1 = 0x111; - while (__raw_readl(EXYNOS4_CLKDIV_STATCPU1) & stat_cpu1) + do { cpu_relax(); + tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU1); + } while (tmp != 0x0); } static void exynos4x12_set_apll(unsigned int index) @@ -184,7 +181,7 @@ int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info) if (IS_ERR(mout_apll)) goto err_mout_apll; - if (soc_is_exynos4212()) + if (info->type == EXYNOS_SOC_4212) apll_freq_4x12 = apll_freq_4212; else apll_freq_4x12 = apll_freq_4412; -- cgit v1.2.3 From 5a3babfcd2354fb1063de2895cab0320fb2027ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Fri, 23 May 2014 22:58:53 +0200 Subject: clk: samsung: clk-s3c2410-dlck: do not use PNAME macro as it declares __initdata The originally used PNAME macro from the core samsung clock infrastructure declares the created array as initdata, creating section mismatch warnings in the dclk driver. Thus declare them directly, removing these warning. Reported-by: Olof Johansson Signed-off-by: Heiko Stuebner Signed-off-by: Olof Johansson --- drivers/clk/samsung/clk-s3c2410-dclk.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c index 8d8dff005c10..c1726f46eddd 100644 --- a/drivers/clk/samsung/clk-s3c2410-dclk.c +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -135,26 +135,26 @@ struct s3c24xx_dclk { #define to_s3c24xx_dclk1(x) \ container_of(x, struct s3c24xx_dclk, dclk1_div_change_nb) -PNAME(dclk_s3c2410_p) = { "pclk", "uclk" }; -PNAME(clkout0_s3c2410_p) = { "mpll", "upll", "fclk", "hclk", "pclk", +static const char *dclk_s3c2410_p[] = { "pclk", "uclk" }; +static const char *clkout0_s3c2410_p[] = { "mpll", "upll", "fclk", "hclk", "pclk", "gate_dclk0" }; -PNAME(clkout1_s3c2410_p) = { "mpll", "upll", "fclk", "hclk", "pclk", +static const char *clkout1_s3c2410_p[] = { "mpll", "upll", "fclk", "hclk", "pclk", "gate_dclk1" }; -PNAME(clkout0_s3c2412_p) = { "mpll", "upll", "rtc_clkout", +static const char *clkout0_s3c2412_p[] = { "mpll", "upll", "rtc_clkout", "hclk", "pclk", "gate_dclk0" }; -PNAME(clkout1_s3c2412_p) = { "xti", "upll", "fclk", "hclk", "pclk", +static const char *clkout1_s3c2412_p) = { "xti", "upll", "fclk", "hclk", "pclk", "gate_dclk1" }; -PNAME(clkout0_s3c2440_p) = { "xti", "upll", "fclk", "hclk", "pclk", +static const char *clkout0_s3c2440_p[] = { "xti", "upll", "fclk", "hclk", "pclk", "gate_dclk0" }; -PNAME(clkout1_s3c2440_p) = { "mpll", "upll", "rtc_clkout", +static const char *clkout1_s3c2440_p[] = { "mpll", "upll", "rtc_clkout", "hclk", "pclk", "gate_dclk1" }; -PNAME(dclk_s3c2443_p) = { "pclk", "epll" }; -PNAME(clkout0_s3c2443_p) = { "xti", "epll", "armclk", "hclk", "pclk", +static const char *dclk_s3c2443_p[] = { "pclk", "epll" }; +static const char *clkout0_s3c2443_p[] = { "xti", "epll", "armclk", "hclk", "pclk", "gate_dclk0" }; -PNAME(clkout1_s3c2443_p) = { "dummy", "epll", "rtc_clkout", +static const char *clkout1_s3c2443_p[] = { "dummy", "epll", "rtc_clkout", "hclk", "pclk", "gate_dclk1" }; #define DCLKCON_DCLK_DIV_MASK 0xf -- cgit v1.2.3 From b33cdd283bd917d431469c29419c2cf2624bd683 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 26 May 2014 17:25:22 +0200 Subject: ARM: vexpress: refine dependencies for new code The versatile express changes for 3.16 introduced a number of build regressions for randconfig kernels by not tracking dependencies between the components right. This patch tries to rectify that: * the mach-vexpress code cannot link without the syscfg driver, which in turn needs MFD_VEXPRESS_SYSREG * various drivers call devm_regmap_init_vexpress_config(), which has to be exported so it can be used by loadable modules * the configuration bus uses OF DT helper functions that are not available to platforms disable CONFIG_OF * The sysreg driver exports GPIOs through gpiolib, which can be disabled on some platforms. * The clocksource code cannot be built on platforms that don't use modern timekeeping but rely on gettimeoffset. Signed-off-by: Arnd Bergmann --- drivers/bus/Kconfig | 1 + drivers/bus/vexpress-config.c | 2 +- drivers/clocksource/Kconfig | 2 +- drivers/mfd/Kconfig | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index f24e79dd51bf..286342778884 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -46,6 +46,7 @@ config VEXPRESS_CONFIG bool "Versatile Express configuration bus" default y if ARCH_VEXPRESS depends on ARM || ARM64 + depends on OF select REGMAP help Platform configuration infrastructure for the ARM Ltd. diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c index 27a07dfcd626..a64763b6b5fd 100644 --- a/drivers/bus/vexpress-config.c +++ b/drivers/bus/vexpress-config.c @@ -118,7 +118,7 @@ struct regmap *devm_regmap_init_vexpress_config(struct device *dev) return regmap; } - +EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config); struct device *vexpress_config_bridge_register(struct device *parent, struct vexpress_config_bridge_ops *ops, void *context) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 2c27b02f0860..43f1acf0d1d2 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -194,7 +194,7 @@ config CLKSRC_QCOM config CLKSRC_VERSATILE bool "ARM Versatile (Express) reference platforms clock source" - depends on GENERIC_SCHED_CLOCK + depends on GENERIC_SCHED_CLOCK && !ARCH_USES_GETTIMEOFFSET select CLKSRC_OF default y if MFD_VEXPRESS_SYSREG help diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 490fd48a9541..f04ac62dd76b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1229,7 +1229,7 @@ endmenu config MFD_VEXPRESS_SYSREG bool "Versatile Express System Registers" - depends on VEXPRESS_CONFIG + depends on VEXPRESS_CONFIG && GPIOLIB default y select CLKSRC_MMIO select GPIO_GENERIC_PLATFORM -- cgit v1.2.3 From 708cec66429cf1ad56d0e849acc3367e8469b1af Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Mon, 26 May 2014 14:19:34 -0700 Subject: clk: samsung: fix build error "clk: samsung: clk-s3c2410-dlck: do not use PNAME macro as it declares __initdata" had a typo in it which caused build failure. Trivial fix. Signed-off-by: Olof Johansson --- drivers/clk/samsung/clk-s3c2410-dclk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c index c1726f46eddd..0449cc0458ed 100644 --- a/drivers/clk/samsung/clk-s3c2410-dclk.c +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -143,7 +143,7 @@ static const char *clkout1_s3c2410_p[] = { "mpll", "upll", "fclk", "hclk", "pclk static const char *clkout0_s3c2412_p[] = { "mpll", "upll", "rtc_clkout", "hclk", "pclk", "gate_dclk0" }; -static const char *clkout1_s3c2412_p) = { "xti", "upll", "fclk", "hclk", "pclk", +static const char *clkout1_s3c2412_p[] = { "xti", "upll", "fclk", "hclk", "pclk", "gate_dclk1" }; static const char *clkout0_s3c2440_p[] = { "xti", "upll", "fclk", "hclk", "pclk", -- cgit v1.2.3