From 2edb90ae421a0f65edcdef65ef9dd08e3228abbf Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 09:37:45 +0200 Subject: [PATCH 01/22] ARM: at91: move at91_pmc.h to include/linux/clk/at91_pmc.h This patch moves at91_pmc.h header from machine specific directory (arch/arm/mach-at91/include/mach/at91_pmc.h) to clk include directory (include/linux/clk/at91_pmc.h). We need this to avoid reference to machine specific headers in clk drivers. Signed-off-by: Boris BREZILLON Acked-by: Felipe Balbi Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91rm9200.c | 2 +- arch/arm/mach-at91/at91sam9260.c | 2 +- arch/arm/mach-at91/at91sam9261.c | 2 +- arch/arm/mach-at91/at91sam9263.c | 2 +- arch/arm/mach-at91/at91sam9g45.c | 2 +- arch/arm/mach-at91/at91sam9n12.c | 2 +- arch/arm/mach-at91/at91sam9rl.c | 2 +- arch/arm/mach-at91/at91sam9x5.c | 2 +- arch/arm/mach-at91/clock.c | 2 +- arch/arm/mach-at91/pm.c | 2 +- arch/arm/mach-at91/pm_slowclock.S | 2 +- arch/arm/mach-at91/sama5d3.c | 2 +- arch/arm/mach-at91/setup.c | 2 +- drivers/usb/gadget/atmel_usba_udc.c | 2 +- .../arm/mach-at91/include/mach => include/linux/clk}/at91_pmc.h | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) rename {arch/arm/mach-at91/include/mach => include/linux/clk}/at91_pmc.h (99%) diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c index 25805f2f6010..e47f5fd232f5 100644 --- a/arch/arm/mach-at91/at91rm9200.c +++ b/arch/arm/mach-at91/at91rm9200.c @@ -12,13 +12,13 @@ #include #include +#include #include #include #include #include #include -#include #include #include diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index d6a1fa85371d..6c821e562159 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -11,6 +11,7 @@ */ #include +#include #include #include @@ -20,7 +21,6 @@ #include #include #include -#include #include "at91_aic.h" #include "at91_rstc.h" diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index 23ba1d8a1531..6276b4c1acfe 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -11,6 +11,7 @@ */ #include +#include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include "at91_aic.h" #include "at91_rstc.h" diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index 7eccb0fc57bc..37b90f4b990c 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -11,6 +11,7 @@ */ #include +#include #include #include @@ -18,7 +19,6 @@ #include #include #include -#include #include "at91_aic.h" #include "at91_rstc.h" diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index 9405aa08b104..2f455ce35268 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -12,13 +12,13 @@ #include #include +#include #include #include #include #include #include -#include #include #include "at91_aic.h" diff --git a/arch/arm/mach-at91/at91sam9n12.c b/arch/arm/mach-at91/at91sam9n12.c index 388ec3aec4b9..4ef088c62eab 100644 --- a/arch/arm/mach-at91/at91sam9n12.c +++ b/arch/arm/mach-at91/at91sam9n12.c @@ -8,12 +8,12 @@ #include #include +#include #include #include #include #include -#include #include #include "board.h" diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 0750ffb7e6b1..3651517abedf 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include "at91_aic.h" #include "at91_rstc.h" diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index e8a2e075a1b8..3e8ec26e39dc 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -8,12 +8,12 @@ #include #include +#include #include #include #include #include -#include #include #include "board.h" diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c index 6b2630a92f71..5f02aea1021c 100644 --- a/arch/arm/mach-at91/clock.c +++ b/arch/arm/mach-at91/clock.c @@ -24,9 +24,9 @@ #include #include #include +#include #include -#include #include #include diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 9986542e8060..d43b79f56e94 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -19,13 +19,13 @@ #include #include #include +#include #include #include #include #include -#include #include #include "at91_aic.h" diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S index 098c28ddf025..20018779bae7 100644 --- a/arch/arm/mach-at91/pm_slowclock.S +++ b/arch/arm/mach-at91/pm_slowclock.S @@ -13,8 +13,8 @@ */ #include +#include #include -#include #include diff --git a/arch/arm/mach-at91/sama5d3.c b/arch/arm/mach-at91/sama5d3.c index 3ea86428ee09..5c6dc24fafbd 100644 --- a/arch/arm/mach-at91/sama5d3.c +++ b/arch/arm/mach-at91/sama5d3.c @@ -9,12 +9,12 @@ #include #include +#include #include #include #include #include -#include #include #include "soc.h" diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 094b3459c288..ca542dee087d 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -18,7 +19,6 @@ #include #include #include -#include #include "at91_shdwc.h" #include "soc.h" diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 2cb52e0438df..9f71d9fdcc14 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -326,7 +326,7 @@ static int vbus_is_present(struct usba_udc *udc) #if defined(CONFIG_ARCH_AT91SAM9RL) -#include +#include static void toggle_bias(int is_on) { diff --git a/arch/arm/mach-at91/include/mach/at91_pmc.h b/include/linux/clk/at91_pmc.h similarity index 99% rename from arch/arm/mach-at91/include/mach/at91_pmc.h rename to include/linux/clk/at91_pmc.h index c604cc69acb5..00c45b3510bf 100644 --- a/arch/arm/mach-at91/include/mach/at91_pmc.h +++ b/include/linux/clk/at91_pmc.h @@ -1,5 +1,5 @@ /* - * arch/arm/mach-at91/include/mach/at91_pmc.h + * include/linux/clk/at91_pmc.h * * Copyright (C) 2005 Ivan Kokshaysky * Copyright (C) SAN People From c8a8c6300866a51ba41fb41b95800e5982dcf96a Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 09:37:46 +0200 Subject: [PATCH 02/22] ARM: at91: add Kconfig options for common clk support This patch adds the following Kconfig options to prepare the transition to common clk framework: - AT91_USE_OLD_CLK: this option is selected by every SoC which does not support new at91 clks based on common clk framework (SoC which does not define the clock tree in its device tree). This options is also selected when the user choose non dt boards support (new at91 clks can only be registered from a device tree definition). - COMMON_CLK_AT91: this option cannot be selected directly. Instead it is enabled if these 3 conditions are met: * at least one of the selected SoCs have a PMC (Power Management Controller) Unit * device tree support is enabled * the old at91 clk implementation is disabled (every selected SoC define its clks in its device tree and non dt boards support is disabled) - OLD_CLK_AT91: this option cannot be selected directly. Instead it is enabled if these 2 conditions are met: * at least one of the selected SoCs have a PMC (Power Management Controller) Unit * at least one of the selected SoCs does not define its clks in its device tree or non dt-boards support is enabled This patch selects AT91_USE_OLD_CLK in all currently supported SoCs. These selects will be removed after clk definitions are properly added in each soc's device tree. It also selects AT91_USE_OLD_CLK in all non-dt boards support. AT91_PMC_UNIT references are replaced by OLD_CLK_AT91, because PMC Unit is enabled for both old and common clk implementations, and old clk implementation should not be compiled if COMMON_CLK is enabled. To avoid future link errors, a new stub is created for at91_dt_clock_init function if OLD_CLK_AT91 is disabled. A new check is added in dt init functions (setup.c) to prepare for SoCs supporting new clk implementation. These SoCs won't setup the register_clocks callback (clk registration is done using of_clk_init). Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 21 +++++++++++++++++++++ arch/arm/mach-at91/Kconfig.non_dt | 6 ++++++ arch/arm/mach-at91/Makefile | 2 +- arch/arm/mach-at91/generic.h | 3 ++- arch/arm/mach-at91/setup.c | 6 ++++-- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 699b71e7f7ec..85b53a441459 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -6,10 +6,22 @@ config HAVE_AT91_DBGU0 config HAVE_AT91_DBGU1 bool +config AT91_USE_OLD_CLK + bool + config AT91_PMC_UNIT bool default !ARCH_AT91X40 +config COMMON_CLK_AT91 + bool + default AT91_PMC_UNIT && USE_OF && !AT91_USE_OLD_CLK + select COMMON_CLK + +config OLD_CLK_AT91 + bool + default AT91_PMC_UNIT && AT91_USE_OLD_CLK + config AT91_SAM9_ALT_RESET bool default !ARCH_AT91X40 @@ -65,6 +77,7 @@ config SOC_SAMA5D3 select SOC_SAMA5 select HAVE_FB_ATMEL select HAVE_AT91_DBGU1 + select AT91_USE_OLD_CLK help Select this if you are using one of Atmel's SAMA5D3 family SoC. This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35. @@ -78,11 +91,13 @@ config SOC_AT91RM9200 select HAVE_AT91_DBGU0 select MULTI_IRQ_HANDLER select SPARSE_IRQ + select AT91_USE_OLD_CLK config SOC_AT91SAM9260 bool "AT91SAM9260, AT91SAM9XE or AT91SAM9G20" select HAVE_AT91_DBGU0 select SOC_AT91SAM9 + select AT91_USE_OLD_CLK help Select this if you are using one of Atmel's AT91SAM9260, AT91SAM9XE or AT91SAM9G20 SoC. @@ -92,6 +107,7 @@ config SOC_AT91SAM9261 select HAVE_AT91_DBGU0 select HAVE_FB_ATMEL select SOC_AT91SAM9 + select AT91_USE_OLD_CLK help Select this if you are using one of Atmel's AT91SAM9261 or AT91SAM9G10 SoC. @@ -100,18 +116,21 @@ config SOC_AT91SAM9263 select HAVE_AT91_DBGU1 select HAVE_FB_ATMEL select SOC_AT91SAM9 + select AT91_USE_OLD_CLK config SOC_AT91SAM9RL bool "AT91SAM9RL" select HAVE_AT91_DBGU0 select HAVE_FB_ATMEL select SOC_AT91SAM9 + select AT91_USE_OLD_CLK config SOC_AT91SAM9G45 bool "AT91SAM9G45 or AT91SAM9M10 families" select HAVE_AT91_DBGU1 select HAVE_FB_ATMEL select SOC_AT91SAM9 + select AT91_USE_OLD_CLK help Select this if you are using one of Atmel's AT91SAM9G45 family SoC. This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11. @@ -121,6 +140,7 @@ config SOC_AT91SAM9X5 select HAVE_AT91_DBGU0 select HAVE_FB_ATMEL select SOC_AT91SAM9 + select AT91_USE_OLD_CLK help Select this if you are using one of Atmel's AT91SAM9x5 family SoC. This means that your SAM9 name finishes with a '5' (except if it is @@ -133,6 +153,7 @@ config SOC_AT91SAM9N12 select HAVE_AT91_DBGU0 select HAVE_FB_ATMEL select SOC_AT91SAM9 + select AT91_USE_OLD_CLK help Select this if you are using Atmel's AT91SAM9N12 SoC. diff --git a/arch/arm/mach-at91/Kconfig.non_dt b/arch/arm/mach-at91/Kconfig.non_dt index ca900be144ce..b736b571e882 100644 --- a/arch/arm/mach-at91/Kconfig.non_dt +++ b/arch/arm/mach-at91/Kconfig.non_dt @@ -12,26 +12,32 @@ config ARCH_AT91_NONE config ARCH_AT91RM9200 bool "AT91RM9200" select SOC_AT91RM9200 + select AT91_USE_OLD_CLK config ARCH_AT91SAM9260 bool "AT91SAM9260 or AT91SAM9XE or AT91SAM9G20" select SOC_AT91SAM9260 + select AT91_USE_OLD_CLK config ARCH_AT91SAM9261 bool "AT91SAM9261 or AT91SAM9G10" select SOC_AT91SAM9261 + select AT91_USE_OLD_CLK config ARCH_AT91SAM9263 bool "AT91SAM9263" select SOC_AT91SAM9263 + select AT91_USE_OLD_CLK config ARCH_AT91SAM9RL bool "AT91SAM9RL" select SOC_AT91SAM9RL + select AT91_USE_OLD_CLK config ARCH_AT91SAM9G45 bool "AT91SAM9G45" select SOC_AT91SAM9G45 + select AT91_USE_OLD_CLK config ARCH_AT91X40 bool "AT91x40" diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 90aab2d5a07f..705b38a179ec 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -7,7 +7,7 @@ obj-m := obj-n := obj- := -obj-$(CONFIG_AT91_PMC_UNIT) += clock.o +obj-$(CONFIG_OLD_CLK_AT91) += clock.o obj-$(CONFIG_AT91_SAM9_ALT_RESET) += at91sam9_alt_reset.o obj-$(CONFIG_AT91_SAM9G45_RESET) += at91sam9g45_reset.o obj-$(CONFIG_AT91_SAM9_TIME) += at91sam926x_time.o diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 26dee3ce9397..631fa3b8c16d 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -46,11 +46,12 @@ extern void at91sam926x_pit_init(void); extern void at91x40_timer_init(void); /* Clocks */ -#ifdef CONFIG_AT91_PMC_UNIT +#ifdef CONFIG_OLD_CLK_AT91 extern int __init at91_clock_init(unsigned long main_clock); extern int __init at91_dt_clock_init(void); #else static int inline at91_clock_init(unsigned long main_clock) { return 0; } +static int inline at91_dt_clock_init(void) { return 0; } #endif struct device; diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index ca542dee087d..7d3f7cc61081 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -491,7 +491,8 @@ void __init at91rm9200_dt_initialize(void) at91_dt_clock_init(); /* Register the processor-specific clocks */ - at91_boot_soc.register_clocks(); + if (at91_boot_soc.register_clocks) + at91_boot_soc.register_clocks(); at91_boot_soc.init(); } @@ -506,7 +507,8 @@ void __init at91_dt_initialize(void) at91_dt_clock_init(); /* Register the processor-specific clocks */ - at91_boot_soc.register_clocks(); + if (at91_boot_soc.register_clocks) + at91_boot_soc.register_clocks(); if (at91_boot_soc.init) at91_boot_soc.init(); From 0ad6125b1579cac8ccbd0f46ec6d33cf71b79f51 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 2 Dec 2013 15:07:02 +0100 Subject: [PATCH 03/22] clk: at91: add PMC base support This patch adds at91 PMC (Power Management Controller) base support. All at91 clocks managed by the PMC unit will use this framework. This framework provides the following fonctionalities: - define a new struct at91_pmc to hide PMC internals (lock, PMC memory mapping, irq domain, ...) - read/write helper functions (pmc_read/write) to access PMC registers - lock/unlock helper functions (pmc_lock/unlock) to lock/unlock access to pmc registers - a new irq domain and its associated irq chip to request PMC specific interrupts (useful for clk prepare callbacks) The PMC unit is declared as a dt clk provider (CLK_OF_DECLARE), and every clk using this framework will declare a table of of_at91_clk_init_cb_t and add it to the pmc_clk_ids table. When the pmc dt clock setup function is called (by of_clk_init function), it triggers the registration of every supported child clk (those matching the definitions in pmc_clk_ids). This patch copies at91_pmc_base (memory mapping) and at91sam9_idle (function) from arch/arm/mach-at91/clock.c (which is not compiled if COMMON_CLK_AT91 is enabled). Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- drivers/clk/Makefile | 1 + drivers/clk/at91/Makefile | 5 + drivers/clk/at91/pmc.c | 304 ++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 61 ++++++++ 4 files changed, 371 insertions(+) create mode 100644 drivers/clk/at91/Makefile create mode 100644 drivers/clk/at91/pmc.c create mode 100644 drivers/clk/at91/pmc.h diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 7a10bc9a23e7..ace7309c4369 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_X86) += x86/ diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile new file mode 100644 index 000000000000..1d4fb214bbc4 --- /dev/null +++ b/drivers/clk/at91/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for at91 specific clk +# + +obj-y += pmc.o diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c new file mode 100644 index 000000000000..69c116a6ba4f --- /dev/null +++ b/drivers/clk/at91/pmc.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pmc.h" + +void __iomem *at91_pmc_base; +EXPORT_SYMBOL_GPL(at91_pmc_base); + +void at91sam9_idle(void) +{ + at91_pmc_write(AT91_PMC_SCDR, AT91_PMC_PCK); + cpu_do_idle(); +} + +int of_at91_get_clk_range(struct device_node *np, const char *propname, + struct clk_range *range) +{ + u32 min, max; + int ret; + + ret = of_property_read_u32_index(np, propname, 0, &min); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, propname, 1, &max); + if (ret) + return ret; + + if (range) { + range->min = min; + range->max = max; + } + + return 0; +} +EXPORT_SYMBOL_GPL(of_at91_get_clk_range); + +static void pmc_irq_mask(struct irq_data *d) +{ + struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); + + pmc_write(pmc, AT91_PMC_IDR, 1 << d->hwirq); +} + +static void pmc_irq_unmask(struct irq_data *d) +{ + struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); + + pmc_write(pmc, AT91_PMC_IER, 1 << d->hwirq); +} + +static int pmc_irq_set_type(struct irq_data *d, unsigned type) +{ + if (type != IRQ_TYPE_LEVEL_HIGH) { + pr_warn("PMC: type not supported (support only IRQ_TYPE_LEVEL_HIGH type)\n"); + return -EINVAL; + } + + return 0; +} + +static struct irq_chip pmc_irq = { + .name = "PMC", + .irq_disable = pmc_irq_mask, + .irq_mask = pmc_irq_mask, + .irq_unmask = pmc_irq_unmask, + .irq_set_type = pmc_irq_set_type, +}; + +static struct lock_class_key pmc_lock_class; + +static int pmc_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct at91_pmc *pmc = h->host_data; + + irq_set_lockdep_class(virq, &pmc_lock_class); + + irq_set_chip_and_handler(virq, &pmc_irq, + handle_level_irq); + set_irq_flags(virq, IRQF_VALID); + irq_set_chip_data(virq, pmc); + + return 0; +} + +static int pmc_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_type) +{ + struct at91_pmc *pmc = d->host_data; + const struct at91_pmc_caps *caps = pmc->caps; + + if (WARN_ON(intsize < 1)) + return -EINVAL; + + *out_hwirq = intspec[0]; + + if (!(caps->available_irqs & (1 << *out_hwirq))) + return -EINVAL; + + *out_type = IRQ_TYPE_LEVEL_HIGH; + + return 0; +} + +static struct irq_domain_ops pmc_irq_ops = { + .map = pmc_irq_map, + .xlate = pmc_irq_domain_xlate, +}; + +static irqreturn_t pmc_irq_handler(int irq, void *data) +{ + struct at91_pmc *pmc = (struct at91_pmc *)data; + unsigned long sr; + int n; + + sr = pmc_read(pmc, AT91_PMC_SR) & pmc_read(pmc, AT91_PMC_IMR); + if (!sr) + return IRQ_NONE; + + for_each_set_bit(n, &sr, BITS_PER_LONG) + generic_handle_irq(irq_find_mapping(pmc->irqdomain, n)); + + return IRQ_HANDLED; +} + +static const struct at91_pmc_caps at91rm9200_caps = { + .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB | + AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY | + AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY | + AT91_PMC_PCK3RDY, +}; + +static const struct at91_pmc_caps at91sam9260_caps = { + .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB | + AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY | + AT91_PMC_PCK1RDY, +}; + +static const struct at91_pmc_caps at91sam9g45_caps = { + .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY | + AT91_PMC_LOCKU | AT91_PMC_PCK0RDY | + AT91_PMC_PCK1RDY, +}; + +static const struct at91_pmc_caps at91sam9n12_caps = { + .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB | + AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY | + AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS | + AT91_PMC_MOSCRCS | AT91_PMC_CFDEV, +}; + +static const struct at91_pmc_caps at91sam9x5_caps = { + .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY | + AT91_PMC_LOCKU | AT91_PMC_PCK0RDY | + AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS | + AT91_PMC_MOSCRCS | AT91_PMC_CFDEV, +}; + +static const struct at91_pmc_caps sama5d3_caps = { + .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY | + AT91_PMC_LOCKU | AT91_PMC_PCK0RDY | + AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY | + AT91_PMC_MOSCSELS | AT91_PMC_MOSCRCS | + AT91_PMC_CFDEV, +}; + +static struct at91_pmc *__init at91_pmc_init(struct device_node *np, + void __iomem *regbase, int virq, + const struct at91_pmc_caps *caps) +{ + struct at91_pmc *pmc; + + if (!regbase || !virq || !caps) + return NULL; + + at91_pmc_base = regbase; + + pmc = kzalloc(sizeof(*pmc), GFP_KERNEL); + if (!pmc) + return NULL; + + spin_lock_init(&pmc->lock); + pmc->regbase = regbase; + pmc->virq = virq; + pmc->caps = caps; + + pmc->irqdomain = irq_domain_add_linear(np, 32, &pmc_irq_ops, pmc); + + if (!pmc->irqdomain) + goto out_free_pmc; + + pmc_write(pmc, AT91_PMC_IDR, 0xffffffff); + if (request_irq(pmc->virq, pmc_irq_handler, IRQF_SHARED, "pmc", pmc)) + goto out_remove_irqdomain; + + return pmc; + +out_remove_irqdomain: + irq_domain_remove(pmc->irqdomain); +out_free_pmc: + kfree(pmc); + + return NULL; +} + +static const struct of_device_id pmc_clk_ids[] __initdata = { + { /*sentinel*/ } +}; + +static void __init of_at91_pmc_setup(struct device_node *np, + const struct at91_pmc_caps *caps) +{ + struct at91_pmc *pmc; + struct device_node *childnp; + void (*clk_setup)(struct device_node *, struct at91_pmc *); + const struct of_device_id *clk_id; + void __iomem *regbase = of_iomap(np, 0); + int virq; + + if (!regbase) + return; + + virq = irq_of_parse_and_map(np, 0); + if (!virq) + return; + + pmc = at91_pmc_init(np, regbase, virq, caps); + if (!pmc) + return; + for_each_child_of_node(np, childnp) { + clk_id = of_match_node(pmc_clk_ids, childnp); + if (!clk_id) + continue; + clk_setup = clk_id->data; + clk_setup(childnp, pmc); + } +} + +static void __init of_at91rm9200_pmc_setup(struct device_node *np) +{ + of_at91_pmc_setup(np, &at91rm9200_caps); +} +CLK_OF_DECLARE(at91rm9200_clk_pmc, "atmel,at91rm9200-pmc", + of_at91rm9200_pmc_setup); + +static void __init of_at91sam9260_pmc_setup(struct device_node *np) +{ + of_at91_pmc_setup(np, &at91sam9260_caps); +} +CLK_OF_DECLARE(at91sam9260_clk_pmc, "atmel,at91sam9260-pmc", + of_at91sam9260_pmc_setup); + +static void __init of_at91sam9g45_pmc_setup(struct device_node *np) +{ + of_at91_pmc_setup(np, &at91sam9g45_caps); +} +CLK_OF_DECLARE(at91sam9g45_clk_pmc, "atmel,at91sam9g45-pmc", + of_at91sam9g45_pmc_setup); + +static void __init of_at91sam9n12_pmc_setup(struct device_node *np) +{ + of_at91_pmc_setup(np, &at91sam9n12_caps); +} +CLK_OF_DECLARE(at91sam9n12_clk_pmc, "atmel,at91sam9n12-pmc", + of_at91sam9n12_pmc_setup); + +static void __init of_at91sam9x5_pmc_setup(struct device_node *np) +{ + of_at91_pmc_setup(np, &at91sam9x5_caps); +} +CLK_OF_DECLARE(at91sam9x5_clk_pmc, "atmel,at91sam9x5-pmc", + of_at91sam9x5_pmc_setup); + +static void __init of_sama5d3_pmc_setup(struct device_node *np) +{ + of_at91_pmc_setup(np, &sama5d3_caps); +} +CLK_OF_DECLARE(sama5d3_clk_pmc, "atmel,sama5d3-pmc", + of_sama5d3_pmc_setup); diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h new file mode 100644 index 000000000000..d92b946cdc9e --- /dev/null +++ b/drivers/clk/at91/pmc.h @@ -0,0 +1,61 @@ +/* + * drivers/clk/at91/pmc.h + * + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __PMC_H_ +#define __PMC_H_ + +#include +#include +#include + +struct clk_range { + unsigned long min; + unsigned long max; +}; + +#define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,} + +struct at91_pmc_caps { + u32 available_irqs; +}; + +struct at91_pmc { + void __iomem *regbase; + int virq; + spinlock_t lock; + const struct at91_pmc_caps *caps; + struct irq_domain *irqdomain; +}; + +static inline void pmc_lock(struct at91_pmc *pmc) +{ + spin_lock(&pmc->lock); +} + +static inline void pmc_unlock(struct at91_pmc *pmc) +{ + spin_unlock(&pmc->lock); +} + +static inline u32 pmc_read(struct at91_pmc *pmc, int offset) +{ + return readl(pmc->regbase + offset); +} + +static inline void pmc_write(struct at91_pmc *pmc, int offset, u32 value) +{ + writel(value, pmc->regbase + offset); +} + +int of_at91_get_clk_range(struct device_node *np, const char *propname, + struct clk_range *range); + +#endif /* __PMC_H_ */ From 7e682b44376e8caa6fc57acbf818b52747b1f922 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 10:41:25 +0200 Subject: [PATCH 04/22] clk: at91: add PMC macro file for dt definitions This patch adds a new macro file for PMC macros. This macro file includes the definitions of SR (status register) bit offsets and will be use to reference PMC irqs. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- include/dt-bindings/clk/at91.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 include/dt-bindings/clk/at91.h diff --git a/include/dt-bindings/clk/at91.h b/include/dt-bindings/clk/at91.h new file mode 100644 index 000000000000..0b4cb999a3f7 --- /dev/null +++ b/include/dt-bindings/clk/at91.h @@ -0,0 +1,22 @@ +/* + * This header provides constants for AT91 pmc status. + * + * The constants defined in this header are being used in dts. + * + * Licensed under GPLv2 or later. + */ + +#ifndef _DT_BINDINGS_CLK_AT91_H +#define _DT_BINDINGS_CLK_AT91_H + +#define AT91_PMC_MOSCS 0 /* MOSCS Flag */ +#define AT91_PMC_LOCKA 1 /* PLLA Lock */ +#define AT91_PMC_LOCKB 2 /* PLLB Lock */ +#define AT91_PMC_MCKRDY 3 /* Master Clock */ +#define AT91_PMC_LOCKU 6 /* UPLL Lock */ +#define AT91_PMC_PCKRDY(id) (8 + (id)) /* Programmable Clock */ +#define AT91_PMC_MOSCSELS 16 /* Main Oscillator Selection */ +#define AT91_PMC_MOSCRCS 17 /* Main On-Chip RC */ +#define AT91_PMC_CFDEV 18 /* Clock Failure Detector Event */ + +#endif From 38d34c3120b5588e2bd561baa4c5cfef1a4917bb Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 10:44:49 +0200 Subject: [PATCH 05/22] clk: at91: add PMC main clock This patch adds new at91 main oscillator clock implementation using common clk framework. If rate is not provided during clock registration it is calculated using the slow clock (main clk parent in this case) rate and MCFR register. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-main.c | 187 ++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.c | 5 + drivers/clk/at91/pmc.h | 3 + 4 files changed, 196 insertions(+) create mode 100644 drivers/clk/at91/clk-main.c diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 1d4fb214bbc4..44105bd44aa1 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,3 +3,4 @@ # obj-y += pmc.o +obj-y += clk-main.o diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c new file mode 100644 index 000000000000..8e9e8cc0412d --- /dev/null +++ b/drivers/clk/at91/clk-main.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define SLOW_CLOCK_FREQ 32768 +#define MAINF_DIV 16 +#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * USEC_PER_SEC) / \ + SLOW_CLOCK_FREQ) +#define MAINF_LOOP_MIN_WAIT (USEC_PER_SEC / SLOW_CLOCK_FREQ) +#define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT + +struct clk_main { + struct clk_hw hw; + struct at91_pmc *pmc; + unsigned long rate; + unsigned int irq; + wait_queue_head_t wait; +}; + +#define to_clk_main(hw) container_of(hw, struct clk_main, hw) + +static irqreturn_t clk_main_irq_handler(int irq, void *dev_id) +{ + struct clk_main *clkmain = (struct clk_main *)dev_id; + + wake_up(&clkmain->wait); + disable_irq_nosync(clkmain->irq); + + return IRQ_HANDLED; +} + +static int clk_main_prepare(struct clk_hw *hw) +{ + struct clk_main *clkmain = to_clk_main(hw); + struct at91_pmc *pmc = clkmain->pmc; + unsigned long halt_time, timeout; + u32 tmp; + + while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCS)) { + enable_irq(clkmain->irq); + wait_event(clkmain->wait, + pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCS); + } + + if (clkmain->rate) + return 0; + + timeout = jiffies + usecs_to_jiffies(MAINFRDY_TIMEOUT); + do { + halt_time = jiffies; + tmp = pmc_read(pmc, AT91_CKGR_MCFR); + if (tmp & AT91_PMC_MAINRDY) + return 0; + usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT); + } while (time_before(halt_time, timeout)); + + return 0; +} + +static int clk_main_is_prepared(struct clk_hw *hw) +{ + struct clk_main *clkmain = to_clk_main(hw); + + return !!(pmc_read(clkmain->pmc, AT91_PMC_SR) & AT91_PMC_MOSCS); +} + +static unsigned long clk_main_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 tmp; + struct clk_main *clkmain = to_clk_main(hw); + struct at91_pmc *pmc = clkmain->pmc; + + if (clkmain->rate) + return clkmain->rate; + + tmp = pmc_read(pmc, AT91_CKGR_MCFR) & AT91_PMC_MAINF; + clkmain->rate = (tmp * parent_rate) / MAINF_DIV; + + return clkmain->rate; +} + +static const struct clk_ops main_ops = { + .prepare = clk_main_prepare, + .is_prepared = clk_main_is_prepared, + .recalc_rate = clk_main_recalc_rate, +}; + +static struct clk * __init +at91_clk_register_main(struct at91_pmc *pmc, + unsigned int irq, + const char *name, + const char *parent_name, + unsigned long rate) +{ + int ret; + struct clk_main *clkmain; + struct clk *clk = NULL; + struct clk_init_data init; + + if (!pmc || !irq || !name) + return ERR_PTR(-EINVAL); + + if (!rate && !parent_name) + return ERR_PTR(-EINVAL); + + clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL); + if (!clkmain) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &main_ops; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.flags = parent_name ? 0 : CLK_IS_ROOT; + + clkmain->hw.init = &init; + clkmain->rate = rate; + clkmain->pmc = pmc; + clkmain->irq = irq; + init_waitqueue_head(&clkmain->wait); + irq_set_status_flags(clkmain->irq, IRQ_NOAUTOEN); + ret = request_irq(clkmain->irq, clk_main_irq_handler, + IRQF_TRIGGER_HIGH, "clk-main", clkmain); + if (ret) + return ERR_PTR(ret); + + clk = clk_register(NULL, &clkmain->hw); + if (IS_ERR(clk)) { + free_irq(clkmain->irq, clkmain); + kfree(clkmain); + } + + return clk; +} + + + +static void __init +of_at91_clk_main_setup(struct device_node *np, struct at91_pmc *pmc) +{ + struct clk *clk; + unsigned int irq; + const char *parent_name; + const char *name = np->name; + u32 rate = 0; + + parent_name = of_clk_get_parent_name(np, 0); + of_property_read_string(np, "clock-output-names", &name); + of_property_read_u32(np, "clock-frequency", &rate); + irq = irq_of_parse_and_map(np, 0); + if (!irq) + return; + + clk = at91_clk_register_main(pmc, irq, name, parent_name, rate); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +void __init of_at91rm9200_clk_main_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_main_setup(np, pmc); +} diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 69c116a6ba4f..3d35b7313868 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -229,6 +229,11 @@ out_free_pmc: } static const struct of_device_id pmc_clk_ids[] __initdata = { + /* Main clock */ + { + .compatible = "atmel,at91rm9200-clk-main", + .data = of_at91rm9200_clk_main_setup, + }, { /*sentinel*/ } }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index d92b946cdc9e..729aa46c7289 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -58,4 +58,7 @@ static inline void pmc_write(struct at91_pmc *pmc, int offset, u32 value) int of_at91_get_clk_range(struct device_node *np, const char *propname, struct clk_range *range); +extern void __init of_at91rm9200_clk_main_setup(struct device_node *np, + struct at91_pmc *pmc); + #endif /* __PMC_H_ */ From 1a748d2bc5061b72588013a720645661345c0e65 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 10:48:26 +0200 Subject: [PATCH 06/22] clk: at91: add PMC pll clocks This patch adds new at91 pll clock implementation using common clk framework. The pll clock layout describe the PLLX register layout. There are four pll clock layouts: - at91rm9200 - at91sam9g20 - at91sam9g45 - sama5d3 PLL clocks are given characteristics: - min/max clock source rate - ranges of valid clock output rates - values to set in out and icpll fields for each supported output range These characteristics are checked during rate change to avoid over/underclocking. These characteristics are described in atmel's SoC datasheet in "Electrical Characteristics" paragraph. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- drivers/clk/at91/Makefile | 2 +- drivers/clk/at91/clk-pll.c | 537 ++++++++++++++++++++++++++++++++++ drivers/clk/at91/clk-plldiv.c | 135 +++++++++ drivers/clk/at91/pmc.c | 21 ++ drivers/clk/at91/pmc.h | 11 + include/linux/clk/at91_pmc.h | 2 + 6 files changed, 707 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/at91/clk-pll.c create mode 100644 drivers/clk/at91/clk-plldiv.c diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 44105bd44aa1..902bbf1fe584 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,4 +3,4 @@ # obj-y += pmc.o -obj-y += clk-main.o +obj-y += clk-main.o clk-pll.o clk-plldiv.o diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c new file mode 100644 index 000000000000..5e23ee4ce387 --- /dev/null +++ b/drivers/clk/at91/clk-pll.c @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define PLL_STATUS_MASK(id) (1 << (1 + (id))) +#define PLL_REG(id) (AT91_CKGR_PLLAR + ((id) * 4)) +#define PLL_DIV_MASK 0xff +#define PLL_DIV_MAX PLL_DIV_MASK +#define PLL_DIV(reg) ((reg) & PLL_DIV_MASK) +#define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \ + (layout)->mul_mask) +#define PLL_ICPR_SHIFT(id) ((id) * 16) +#define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id)) +#define PLL_MAX_COUNT 0x3ff +#define PLL_COUNT_SHIFT 8 +#define PLL_OUT_SHIFT 14 +#define PLL_MAX_ID 1 + +struct clk_pll_characteristics { + struct clk_range input; + int num_output; + struct clk_range *output; + u16 *icpll; + u8 *out; +}; + +struct clk_pll_layout { + u32 pllr_mask; + u16 mul_mask; + u8 mul_shift; +}; + +#define to_clk_pll(hw) container_of(hw, struct clk_pll, hw) + +struct clk_pll { + struct clk_hw hw; + struct at91_pmc *pmc; + unsigned int irq; + wait_queue_head_t wait; + u8 id; + u8 div; + u8 range; + u16 mul; + const struct clk_pll_layout *layout; + const struct clk_pll_characteristics *characteristics; +}; + +static irqreturn_t clk_pll_irq_handler(int irq, void *dev_id) +{ + struct clk_pll *pll = (struct clk_pll *)dev_id; + + wake_up(&pll->wait); + disable_irq_nosync(pll->irq); + + return IRQ_HANDLED; +} + +static int clk_pll_prepare(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct at91_pmc *pmc = pll->pmc; + const struct clk_pll_layout *layout = pll->layout; + u8 id = pll->id; + u32 mask = PLL_STATUS_MASK(id); + int offset = PLL_REG(id); + u8 out = 0; + u32 pllr, icpr; + u8 div; + u16 mul; + + pllr = pmc_read(pmc, offset); + div = PLL_DIV(pllr); + mul = PLL_MUL(pllr, layout); + + if ((pmc_read(pmc, AT91_PMC_SR) & mask) && + (div == pll->div && mul == pll->mul)) + return 0; + + if (characteristics->out) + out = characteristics->out[pll->range]; + if (characteristics->icpll) { + icpr = pmc_read(pmc, AT91_PMC_PLLICPR) & ~PLL_ICPR_MASK(id); + icpr |= (characteristics->icpll[pll->range] << + PLL_ICPR_SHIFT(id)); + pmc_write(pmc, AT91_PMC_PLLICPR, icpr); + } + + pllr &= ~layout->pllr_mask; + pllr |= layout->pllr_mask & + (pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) | + (out << PLL_OUT_SHIFT) | + ((pll->mul & layout->mul_mask) << layout->mul_shift)); + pmc_write(pmc, offset, pllr); + + while (!(pmc_read(pmc, AT91_PMC_SR) & mask)) { + enable_irq(pll->irq); + wait_event(pll->wait, + pmc_read(pmc, AT91_PMC_SR) & mask); + } + + return 0; +} + +static int clk_pll_is_prepared(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct at91_pmc *pmc = pll->pmc; + + return !!(pmc_read(pmc, AT91_PMC_SR) & + PLL_STATUS_MASK(pll->id)); +} + +static void clk_pll_unprepare(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct at91_pmc *pmc = pll->pmc; + const struct clk_pll_layout *layout = pll->layout; + int offset = PLL_REG(pll->id); + u32 tmp = pmc_read(pmc, offset) & ~(layout->pllr_mask); + + pmc_write(pmc, offset, tmp); +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + const struct clk_pll_layout *layout = pll->layout; + struct at91_pmc *pmc = pll->pmc; + int offset = PLL_REG(pll->id); + u32 tmp = pmc_read(pmc, offset) & layout->pllr_mask; + u8 div = PLL_DIV(tmp); + u16 mul = PLL_MUL(tmp, layout); + if (!div || !mul) + return 0; + + return (parent_rate * (mul + 1)) / div; +} + +static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate, + unsigned long parent_rate, + u32 *div, u32 *mul, + u32 *index) { + unsigned long maxrate; + unsigned long minrate; + unsigned long divrate; + unsigned long bestdiv = 1; + unsigned long bestmul; + unsigned long tmpdiv; + unsigned long roundup; + unsigned long rounddown; + unsigned long remainder; + unsigned long bestremainder; + unsigned long maxmul; + unsigned long maxdiv; + unsigned long mindiv; + int i = 0; + const struct clk_pll_layout *layout = pll->layout; + const struct clk_pll_characteristics *characteristics = + pll->characteristics; + + /* Minimum divider = 1 */ + /* Maximum multiplier = max_mul */ + maxmul = layout->mul_mask + 1; + maxrate = (parent_rate * maxmul) / 1; + + /* Maximum divider = max_div */ + /* Minimum multiplier = 2 */ + maxdiv = PLL_DIV_MAX; + minrate = (parent_rate * 2) / maxdiv; + + if (parent_rate < characteristics->input.min || + parent_rate < characteristics->input.max) + return -ERANGE; + + if (parent_rate < minrate || parent_rate > maxrate) + return -ERANGE; + + for (i = 0; i < characteristics->num_output; i++) { + if (parent_rate >= characteristics->output[i].min && + parent_rate <= characteristics->output[i].max) + break; + } + + if (i >= characteristics->num_output) + return -ERANGE; + + bestmul = rate / parent_rate; + rounddown = parent_rate % rate; + roundup = rate - rounddown; + bestremainder = roundup < rounddown ? roundup : rounddown; + + if (!bestremainder) { + if (div) + *div = bestdiv; + if (mul) + *mul = bestmul; + if (index) + *index = i; + return rate; + } + + maxdiv = 255 / (bestmul + 1); + if (parent_rate / maxdiv < characteristics->input.min) + maxdiv = parent_rate / characteristics->input.min; + mindiv = parent_rate / characteristics->input.max; + if (parent_rate % characteristics->input.max) + mindiv++; + + for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) { + divrate = parent_rate / tmpdiv; + + rounddown = rate % divrate; + roundup = divrate - rounddown; + remainder = roundup < rounddown ? roundup : rounddown; + + if (remainder < bestremainder) { + bestremainder = remainder; + bestmul = rate / divrate; + bestdiv = tmpdiv; + } + + if (!remainder) + break; + } + + rate = (parent_rate / bestdiv) * bestmul; + + if (div) + *div = bestdiv; + if (mul) + *mul = bestmul; + if (index) + *index = i; + + return rate; +} + +static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + + return clk_pll_get_best_div_mul(pll, rate, *parent_rate, + NULL, NULL, NULL); +} + +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct at91_pmc *pmc = pll->pmc; + const struct clk_pll_layout *layout = pll->layout; + const struct clk_pll_characteristics *characteristics = + pll->characteristics; + u8 id = pll->id; + int offset = PLL_REG(id); + long ret; + u32 div; + u32 mul; + u32 index; + u32 tmp; + u8 out = 0; + + ret = clk_pll_get_best_div_mul(pll, rate, parent_rate, + &div, &mul, &index); + if (ret < 0) + return ret; + + pll->range = index; + pll->div = div; + pll->mul = mul; + + return 0; +} + +static const struct clk_ops pll_ops = { + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .is_prepared = clk_pll_is_prepared, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +static struct clk * __init +at91_clk_register_pll(struct at91_pmc *pmc, unsigned int irq, const char *name, + const char *parent_name, u8 id, + const struct clk_pll_layout *layout, + const struct clk_pll_characteristics *characteristics) +{ + struct clk_pll *pll; + struct clk *clk = NULL; + struct clk_init_data init; + int ret; + int offset = PLL_REG(id); + u32 tmp; + + if (id > PLL_MAX_ID) + return ERR_PTR(-EINVAL); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE; + + pll->id = id; + pll->hw.init = &init; + pll->layout = layout; + pll->characteristics = characteristics; + pll->pmc = pmc; + pll->irq = irq; + tmp = pmc_read(pmc, offset) & layout->pllr_mask; + pll->div = PLL_DIV(tmp); + pll->mul = PLL_MUL(tmp, layout); + init_waitqueue_head(&pll->wait); + irq_set_status_flags(pll->irq, IRQ_NOAUTOEN); + ret = request_irq(pll->irq, clk_pll_irq_handler, IRQF_TRIGGER_HIGH, + id ? "clk-pllb" : "clk-plla", pll); + if (ret) + return ERR_PTR(ret); + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + + +static const struct clk_pll_layout at91rm9200_pll_layout = { + .pllr_mask = 0x7FFFFFF, + .mul_shift = 16, + .mul_mask = 0x7FF, +}; + +static const struct clk_pll_layout at91sam9g45_pll_layout = { + .pllr_mask = 0xFFFFFF, + .mul_shift = 16, + .mul_mask = 0xFF, +}; + +static const struct clk_pll_layout at91sam9g20_pllb_layout = { + .pllr_mask = 0x3FFFFF, + .mul_shift = 16, + .mul_mask = 0x3F, +}; + +static const struct clk_pll_layout sama5d3_pll_layout = { + .pllr_mask = 0x1FFFFFF, + .mul_shift = 18, + .mul_mask = 0x7F, +}; + + +static struct clk_pll_characteristics * __init +of_at91_clk_pll_get_characteristics(struct device_node *np) +{ + int i; + int offset; + u32 tmp; + int num_output; + u32 num_cells; + struct clk_range input; + struct clk_range *output; + u8 *out = NULL; + u16 *icpll = NULL; + struct clk_pll_characteristics *characteristics; + + if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input)) + return NULL; + + if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells", + &num_cells)) + return NULL; + + if (num_cells < 2 || num_cells > 4) + return NULL; + + if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp)) + return NULL; + num_output = tmp / (sizeof(u32) * num_cells); + + characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL); + if (!characteristics) + return NULL; + + output = kzalloc(sizeof(*output) * num_output, GFP_KERNEL); + if (!output) + goto out_free_characteristics; + + if (num_cells > 2) { + out = kzalloc(sizeof(*out) * num_output, GFP_KERNEL); + if (!out) + goto out_free_output; + } + + if (num_cells > 3) { + icpll = kzalloc(sizeof(*icpll) * num_output, GFP_KERNEL); + if (!icpll) + goto out_free_output; + } + + for (i = 0; i < num_output; i++) { + offset = i * num_cells; + if (of_property_read_u32_index(np, + "atmel,pll-clk-output-ranges", + offset, &tmp)) + goto out_free_output; + output[i].min = tmp; + if (of_property_read_u32_index(np, + "atmel,pll-clk-output-ranges", + offset + 1, &tmp)) + goto out_free_output; + output[i].max = tmp; + + if (num_cells == 2) + continue; + + if (of_property_read_u32_index(np, + "atmel,pll-clk-output-ranges", + offset + 2, &tmp)) + goto out_free_output; + out[i] = tmp; + + if (num_cells == 3) + continue; + + if (of_property_read_u32_index(np, + "atmel,pll-clk-output-ranges", + offset + 3, &tmp)) + goto out_free_output; + icpll[i] = tmp; + } + + characteristics->input = input; + characteristics->num_output = num_output; + characteristics->output = output; + characteristics->out = out; + characteristics->icpll = icpll; + return characteristics; + +out_free_output: + kfree(icpll); + kfree(out); + kfree(output); +out_free_characteristics: + kfree(characteristics); + return NULL; +} + +static void __init +of_at91_clk_pll_setup(struct device_node *np, struct at91_pmc *pmc, + const struct clk_pll_layout *layout) +{ + u32 id; + unsigned int irq; + struct clk *clk; + const char *parent_name; + const char *name = np->name; + struct clk_pll_characteristics *characteristics; + + if (of_property_read_u32(np, "reg", &id)) + return; + + parent_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &name); + + characteristics = of_at91_clk_pll_get_characteristics(np); + if (!characteristics) + return; + + irq = irq_of_parse_and_map(np, 0); + if (!irq) + return; + + clk = at91_clk_register_pll(pmc, irq, name, parent_name, id, layout, + characteristics); + if (IS_ERR(clk)) + goto out_free_characteristics; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; + +out_free_characteristics: + kfree(characteristics); +} + +void __init of_at91rm9200_clk_pll_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_pll_setup(np, pmc, &at91rm9200_pll_layout); +} + +void __init of_at91sam9g45_clk_pll_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_pll_setup(np, pmc, &at91sam9g45_pll_layout); +} + +void __init of_at91sam9g20_clk_pllb_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_pll_setup(np, pmc, &at91sam9g20_pllb_layout); +} + +void __init of_sama5d3_clk_pll_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_pll_setup(np, pmc, &sama5d3_pll_layout); +} diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c new file mode 100644 index 000000000000..ea226562bb40 --- /dev/null +++ b/drivers/clk/at91/clk-plldiv.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw) + +struct clk_plldiv { + struct clk_hw hw; + struct at91_pmc *pmc; +}; + +static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_plldiv *plldiv = to_clk_plldiv(hw); + struct at91_pmc *pmc = plldiv->pmc; + + if (pmc_read(pmc, AT91_PMC_MCKR) & AT91_PMC_PLLADIV2) + return parent_rate / 2; + + return parent_rate; +} + +static long clk_plldiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long div; + + if (rate > *parent_rate) + return *parent_rate; + div = *parent_rate / 2; + if (rate < div) + return div; + + if (rate - div < *parent_rate - rate) + return div; + + return *parent_rate; +} + +static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_plldiv *plldiv = to_clk_plldiv(hw); + struct at91_pmc *pmc = plldiv->pmc; + u32 tmp; + + if (parent_rate != rate && (parent_rate / 2) != rate) + return -EINVAL; + + pmc_lock(pmc); + tmp = pmc_read(pmc, AT91_PMC_MCKR) & ~AT91_PMC_PLLADIV2; + if ((parent_rate / 2) == rate) + tmp |= AT91_PMC_PLLADIV2; + pmc_write(pmc, AT91_PMC_MCKR, tmp); + pmc_unlock(pmc); + + return 0; +} + +static const struct clk_ops plldiv_ops = { + .recalc_rate = clk_plldiv_recalc_rate, + .round_rate = clk_plldiv_round_rate, + .set_rate = clk_plldiv_set_rate, +}; + +static struct clk * __init +at91_clk_register_plldiv(struct at91_pmc *pmc, const char *name, + const char *parent_name) +{ + struct clk_plldiv *plldiv; + struct clk *clk = NULL; + struct clk_init_data init; + + plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL); + if (!plldiv) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &plldiv_ops; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.flags = CLK_SET_RATE_GATE; + + plldiv->hw.init = &init; + plldiv->pmc = pmc; + + clk = clk_register(NULL, &plldiv->hw); + + if (IS_ERR(clk)) + kfree(plldiv); + + return clk; +} + +static void __init +of_at91_clk_plldiv_setup(struct device_node *np, struct at91_pmc *pmc) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + + parent_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91_clk_register_plldiv(pmc, name, parent_name); + + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +} + +void __init of_at91sam9x5_clk_plldiv_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_plldiv_setup(np, pmc); +} diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 3d35b7313868..5af3f2fbf45e 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -234,6 +234,27 @@ static const struct of_device_id pmc_clk_ids[] __initdata = { .compatible = "atmel,at91rm9200-clk-main", .data = of_at91rm9200_clk_main_setup, }, + /* PLL clocks */ + { + .compatible = "atmel,at91rm9200-clk-pll", + .data = of_at91rm9200_clk_pll_setup, + }, + { + .compatible = "atmel,at91sam9g45-clk-pll", + .data = of_at91sam9g45_clk_pll_setup, + }, + { + .compatible = "atmel,at91sam9g20-clk-pllb", + .data = of_at91sam9g20_clk_pllb_setup, + }, + { + .compatible = "atmel,sama5d3-clk-pll", + .data = of_sama5d3_clk_pll_setup, + }, + { + .compatible = "atmel,at91sam9x5-clk-plldiv", + .data = of_at91sam9x5_clk_plldiv_setup, + }, { /*sentinel*/ } }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 729aa46c7289..15aaf38e37c5 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -61,4 +61,15 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname, extern void __init of_at91rm9200_clk_main_setup(struct device_node *np, struct at91_pmc *pmc); +extern void __init of_at91rm9200_clk_pll_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_at91sam9g45_clk_pll_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_at91sam9g20_clk_pllb_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_sama5d3_clk_pll_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_at91sam9x5_clk_plldiv_setup(struct device_node *np, + struct at91_pmc *pmc); + #endif /* __PMC_H_ */ diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h index 00c45b3510bf..a6911ebbd02a 100644 --- a/include/linux/clk/at91_pmc.h +++ b/include/linux/clk/at91_pmc.h @@ -164,6 +164,8 @@ extern void __iomem *at91_pmc_base; #define AT91_PMC_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */ #define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */ +#define AT91_PMC_PLLICPR 0x80 /* PLL Charge Pump Current Register */ + #define AT91_PMC_PROT 0xe4 /* Write Protect Mode Register [some SAM9] */ #define AT91_PMC_WPEN (0x1 << 0) /* Write Protect Enable */ #define AT91_PMC_WPKEY (0xffffff << 8) /* Write Protect Key */ From e442d234405ad75e2d3d2baf15b364ee2c3573c9 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 10:51:23 +0200 Subject: [PATCH 07/22] clk: at91: add PMC master clock This patch adds new at91 master clock implementation using common clk framework. The master clock layout describe the MCKR register layout. There are 2 master clock layouts: - at91rm9200 - at91sam9x5 Master clocks are given characteristics: - min/max clock output rate These characteristics are checked during rate change to avoid over/underclocking. These characteristics are described in atmel's SoC datasheet in "Electrical Characteristics" paragraph. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- drivers/clk/at91/Makefile | 2 +- drivers/clk/at91/clk-master.c | 270 ++++++++++++++++++++++++++++++++++ drivers/clk/at91/clk-pll.c | 10 +- drivers/clk/at91/pmc.c | 9 ++ drivers/clk/at91/pmc.h | 5 + 5 files changed, 287 insertions(+), 9 deletions(-) create mode 100644 drivers/clk/at91/clk-master.c diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 902bbf1fe584..e28fb2bbb198 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,4 +3,4 @@ # obj-y += pmc.o -obj-y += clk-main.o clk-pll.o clk-plldiv.o +obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c new file mode 100644 index 000000000000..bd313f7816a8 --- /dev/null +++ b/drivers/clk/at91/clk-master.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define MASTER_SOURCE_MAX 4 + +#define MASTER_PRES_MASK 0x7 +#define MASTER_PRES_MAX MASTER_PRES_MASK +#define MASTER_DIV_SHIFT 8 +#define MASTER_DIV_MASK 0x3 + +struct clk_master_characteristics { + struct clk_range output; + u32 divisors[4]; + u8 have_div3_pres; +}; + +struct clk_master_layout { + u32 mask; + u8 pres_shift; +}; + +#define to_clk_master(hw) container_of(hw, struct clk_master, hw) + +struct clk_master { + struct clk_hw hw; + struct at91_pmc *pmc; + unsigned int irq; + wait_queue_head_t wait; + const struct clk_master_layout *layout; + const struct clk_master_characteristics *characteristics; +}; + +static irqreturn_t clk_master_irq_handler(int irq, void *dev_id) +{ + struct clk_master *master = (struct clk_master *)dev_id; + + wake_up(&master->wait); + disable_irq_nosync(master->irq); + + return IRQ_HANDLED; +} +static int clk_master_prepare(struct clk_hw *hw) +{ + struct clk_master *master = to_clk_master(hw); + struct at91_pmc *pmc = master->pmc; + + while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY)) { + enable_irq(master->irq); + wait_event(master->wait, + pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY); + } + + return 0; +} + +static int clk_master_is_prepared(struct clk_hw *hw) +{ + struct clk_master *master = to_clk_master(hw); + + return !!(pmc_read(master->pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY); +} + +static unsigned long clk_master_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u8 pres; + u8 div; + unsigned long rate = parent_rate; + struct clk_master *master = to_clk_master(hw); + struct at91_pmc *pmc = master->pmc; + const struct clk_master_layout *layout = master->layout; + const struct clk_master_characteristics *characteristics = + master->characteristics; + u32 tmp; + + pmc_lock(pmc); + tmp = pmc_read(pmc, AT91_PMC_MCKR) & layout->mask; + pmc_unlock(pmc); + + pres = (tmp >> layout->pres_shift) & MASTER_PRES_MASK; + div = (tmp >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; + + if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX) + rate /= 3; + else + rate >>= pres; + + rate /= characteristics->divisors[div]; + + if (rate < characteristics->output.min) + pr_warn("master clk is underclocked"); + else if (rate > characteristics->output.max) + pr_warn("master clk is overclocked"); + + return rate; +} + +static u8 clk_master_get_parent(struct clk_hw *hw) +{ + struct clk_master *master = to_clk_master(hw); + struct at91_pmc *pmc = master->pmc; + + return pmc_read(pmc, AT91_PMC_MCKR) & AT91_PMC_CSS; +} + +static const struct clk_ops master_ops = { + .prepare = clk_master_prepare, + .is_prepared = clk_master_is_prepared, + .recalc_rate = clk_master_recalc_rate, + .get_parent = clk_master_get_parent, +}; + +static struct clk * __init +at91_clk_register_master(struct at91_pmc *pmc, unsigned int irq, + const char *name, int num_parents, + const char **parent_names, + const struct clk_master_layout *layout, + const struct clk_master_characteristics *characteristics) +{ + int ret; + struct clk_master *master; + struct clk *clk = NULL; + struct clk_init_data init; + + if (!pmc || !irq || !name || !num_parents || !parent_names) + return ERR_PTR(-EINVAL); + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &master_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = 0; + + master->hw.init = &init; + master->layout = layout; + master->characteristics = characteristics; + master->pmc = pmc; + master->irq = irq; + init_waitqueue_head(&master->wait); + irq_set_status_flags(master->irq, IRQ_NOAUTOEN); + ret = request_irq(master->irq, clk_master_irq_handler, + IRQF_TRIGGER_HIGH, "clk-master", master); + if (ret) + return ERR_PTR(ret); + + clk = clk_register(NULL, &master->hw); + if (IS_ERR(clk)) + kfree(master); + + return clk; +} + + +static const struct clk_master_layout at91rm9200_master_layout = { + .mask = 0x31F, + .pres_shift = 2, +}; + +static const struct clk_master_layout at91sam9x5_master_layout = { + .mask = 0x373, + .pres_shift = 4, +}; + + +static struct clk_master_characteristics * __init +of_at91_clk_master_get_characteristics(struct device_node *np) +{ + struct clk_master_characteristics *characteristics; + + characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL); + if (!characteristics) + return NULL; + + if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output)) + goto out_free_characteristics; + + of_property_read_u32_array(np, "atmel,clk-divisors", + characteristics->divisors, 4); + + characteristics->have_div3_pres = + of_property_read_bool(np, "atmel,master-clk-have-div3-pres"); + + return characteristics; + +out_free_characteristics: + kfree(characteristics); + return NULL; +} + +static void __init +of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc, + const struct clk_master_layout *layout) +{ + struct clk *clk; + int num_parents; + int i; + unsigned int irq; + const char *parent_names[MASTER_SOURCE_MAX]; + const char *name = np->name; + struct clk_master_characteristics *characteristics; + + num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + if (num_parents <= 0 || num_parents > MASTER_SOURCE_MAX) + return; + + for (i = 0; i < num_parents; ++i) { + parent_names[i] = of_clk_get_parent_name(np, i); + if (!parent_names[i]) + return; + } + + of_property_read_string(np, "clock-output-names", &name); + + characteristics = of_at91_clk_master_get_characteristics(np); + if (!characteristics) + return; + + irq = irq_of_parse_and_map(np, 0); + if (!irq) + return; + + clk = at91_clk_register_master(pmc, irq, name, num_parents, + parent_names, layout, + characteristics); + if (IS_ERR(clk)) + goto out_free_characteristics; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; + +out_free_characteristics: + kfree(characteristics); +} + +void __init of_at91rm9200_clk_master_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_master_setup(np, pmc, &at91rm9200_master_layout); +} + +void __init of_at91sam9x5_clk_master_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_master_setup(np, pmc, &at91sam9x5_master_layout); +} diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c index 5e23ee4ce387..cf6ed023504c 100644 --- a/drivers/clk/at91/clk-pll.c +++ b/drivers/clk/at91/clk-pll.c @@ -80,6 +80,8 @@ static int clk_pll_prepare(struct clk_hw *hw) struct clk_pll *pll = to_clk_pll(hw); struct at91_pmc *pmc = pll->pmc; const struct clk_pll_layout *layout = pll->layout; + const struct clk_pll_characteristics *characteristics = + pll->characteristics; u8 id = pll->id; u32 mask = PLL_STATUS_MASK(id); int offset = PLL_REG(id); @@ -269,18 +271,10 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_pll *pll = to_clk_pll(hw); - struct at91_pmc *pmc = pll->pmc; - const struct clk_pll_layout *layout = pll->layout; - const struct clk_pll_characteristics *characteristics = - pll->characteristics; - u8 id = pll->id; - int offset = PLL_REG(id); long ret; u32 div; u32 mul; u32 index; - u32 tmp; - u8 out = 0; ret = clk_pll_get_best_div_mul(pll, rate, parent_rate, &div, &mul, &index); diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 5af3f2fbf45e..a311cf3d5358 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -255,6 +255,15 @@ static const struct of_device_id pmc_clk_ids[] __initdata = { .compatible = "atmel,at91sam9x5-clk-plldiv", .data = of_at91sam9x5_clk_plldiv_setup, }, + /* Master clock */ + { + .compatible = "atmel,at91rm9200-clk-master", + .data = of_at91rm9200_clk_master_setup, + }, + { + .compatible = "atmel,at91sam9x5-clk-master", + .data = of_at91sam9x5_clk_master_setup, + }, { /*sentinel*/ } }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 15aaf38e37c5..be5c964accad 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -72,4 +72,9 @@ extern void __init of_sama5d3_clk_pll_setup(struct device_node *np, extern void __init of_at91sam9x5_clk_plldiv_setup(struct device_node *np, struct at91_pmc *pmc); +extern void __init of_at91rm9200_clk_master_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_at91sam9x5_clk_master_setup(struct device_node *np, + struct at91_pmc *pmc); + #endif /* __PMC_H_ */ From 5fba62ea2648f89cbd7f0ed32069f03b179d05e9 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 11:41:41 +0200 Subject: [PATCH 08/22] clk: at91: add PMC system clocks This patch adds new at91 system clock implementation using common clk framework. Some peripherals need to enable a "system" clock in order to work properly. Each system clock is given an id based on the bit position in SCER/SCDR registers. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-system.c | 135 ++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.c | 5 ++ drivers/clk/at91/pmc.h | 3 + 4 files changed, 144 insertions(+) create mode 100644 drivers/clk/at91/clk-system.c diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index e28fb2bbb198..c2b70683e4e0 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -4,3 +4,4 @@ obj-y += pmc.o obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o +obj-y += clk-system.o diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c new file mode 100644 index 000000000000..8f7c0434a09f --- /dev/null +++ b/drivers/clk/at91/clk-system.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define SYSTEM_MAX_ID 31 + +#define SYSTEM_MAX_NAME_SZ 32 + +#define to_clk_system(hw) container_of(hw, struct clk_system, hw) +struct clk_system { + struct clk_hw hw; + struct at91_pmc *pmc; + u8 id; +}; + +static int clk_system_enable(struct clk_hw *hw) +{ + struct clk_system *sys = to_clk_system(hw); + struct at91_pmc *pmc = sys->pmc; + + pmc_write(pmc, AT91_PMC_SCER, 1 << sys->id); + return 0; +} + +static void clk_system_disable(struct clk_hw *hw) +{ + struct clk_system *sys = to_clk_system(hw); + struct at91_pmc *pmc = sys->pmc; + + pmc_write(pmc, AT91_PMC_SCDR, 1 << sys->id); +} + +static int clk_system_is_enabled(struct clk_hw *hw) +{ + struct clk_system *sys = to_clk_system(hw); + struct at91_pmc *pmc = sys->pmc; + + return !!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id)); +} + +static const struct clk_ops system_ops = { + .enable = clk_system_enable, + .disable = clk_system_disable, + .is_enabled = clk_system_is_enabled, +}; + +static struct clk * __init +at91_clk_register_system(struct at91_pmc *pmc, const char *name, + const char *parent_name, u8 id) +{ + struct clk_system *sys; + struct clk *clk = NULL; + struct clk_init_data init; + + if (!parent_name || id > SYSTEM_MAX_ID) + return ERR_PTR(-EINVAL); + + sys = kzalloc(sizeof(*sys), GFP_KERNEL); + if (!sys) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &system_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + /* + * CLK_IGNORE_UNUSED is used to avoid ddrck switch off. + * TODO : we should implement a driver supporting at91 ddr controller + * (see drivers/memory) which would request and enable the ddrck clock. + * When this is done we will be able to remove CLK_IGNORE_UNUSED flag. + */ + init.flags = CLK_IGNORE_UNUSED; + + sys->id = id; + sys->hw.init = &init; + sys->pmc = pmc; + + clk = clk_register(NULL, &sys->hw); + if (IS_ERR(clk)) + kfree(sys); + + return clk; +} + +static void __init +of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc) +{ + int num; + u32 id; + struct clk *clk; + const char *name; + struct device_node *sysclknp; + const char *parent_name; + + num = of_get_child_count(np); + if (num > (SYSTEM_MAX_ID + 1)) + return; + + for_each_child_of_node(np, sysclknp) { + if (of_property_read_u32(sysclknp, "reg", &id)) + continue; + + if (of_property_read_string(np, "clock-output-names", &name)) + name = sysclknp->name; + + parent_name = of_clk_get_parent_name(sysclknp, 0); + + clk = at91_clk_register_system(pmc, name, parent_name, id); + if (IS_ERR(clk)) + continue; + + of_clk_add_provider(sysclknp, of_clk_src_simple_get, clk); + } +} + +void __init of_at91rm9200_clk_sys_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_sys_setup(np, pmc); +} diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index a311cf3d5358..a97554f943e4 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -264,6 +264,11 @@ static const struct of_device_id pmc_clk_ids[] __initdata = { .compatible = "atmel,at91sam9x5-clk-master", .data = of_at91sam9x5_clk_master_setup, }, + /* System clocks */ + { + .compatible = "atmel,at91rm9200-clk-system", + .data = of_at91rm9200_clk_sys_setup, + }, { /*sentinel*/ } }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index be5c964accad..9c8a397cb850 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -77,4 +77,7 @@ extern void __init of_at91rm9200_clk_master_setup(struct device_node *np, extern void __init of_at91sam9x5_clk_master_setup(struct device_node *np, struct at91_pmc *pmc); +extern void __init of_at91rm9200_clk_sys_setup(struct device_node *np, + struct at91_pmc *pmc); + #endif /* __PMC_H_ */ From 6114067e437eb861a8e75741e3d8763475f75512 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 11:44:36 +0200 Subject: [PATCH 09/22] clk: at91: add PMC peripheral clocks This patch adds new at91 peripheral clock implementation using common clk framework. Almost all peripherals provided by at91 SoCs need a clock to work properly. This clock is enabled/disabled using PCER/PCDR resgisters. Each peripheral is given an id (see atmel's datasheets) which is used to define and reference peripheral clocks. Some new SoCs (at91sam9x5 and sama5d3) provide a new register (PCR) where you can configure the peripheral clock as a division of the master clock. This will help reducing the peripherals power comsumption. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- drivers/clk/at91/Makefile | 2 +- drivers/clk/at91/clk-peripheral.c | 410 ++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.c | 9 + drivers/clk/at91/pmc.h | 5 + 4 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/at91/clk-peripheral.c diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index c2b70683e4e0..04deba35893e 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -4,4 +4,4 @@ obj-y += pmc.o obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o -obj-y += clk-system.o +obj-y += clk-system.o clk-peripheral.o diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c new file mode 100644 index 000000000000..597fed423d7d --- /dev/null +++ b/drivers/clk/at91/clk-peripheral.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define PERIPHERAL_MAX 64 + +#define PERIPHERAL_AT91RM9200 0 +#define PERIPHERAL_AT91SAM9X5 1 + +#define PERIPHERAL_ID_MIN 2 +#define PERIPHERAL_ID_MAX 31 +#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) + +#define PERIPHERAL_RSHIFT_MASK 0x3 +#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK) + +#define PERIPHERAL_MAX_SHIFT 4 + +struct clk_peripheral { + struct clk_hw hw; + struct at91_pmc *pmc; + u32 id; +}; + +#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw) + +struct clk_sam9x5_peripheral { + struct clk_hw hw; + struct at91_pmc *pmc; + struct clk_range range; + u32 id; + u32 div; + bool auto_div; +}; + +#define to_clk_sam9x5_peripheral(hw) \ + container_of(hw, struct clk_sam9x5_peripheral, hw) + +static int clk_peripheral_enable(struct clk_hw *hw) +{ + struct clk_peripheral *periph = to_clk_peripheral(hw); + struct at91_pmc *pmc = periph->pmc; + int offset = AT91_PMC_PCER; + u32 id = periph->id; + + if (id < PERIPHERAL_ID_MIN) + return 0; + if (id > PERIPHERAL_ID_MAX) + offset = AT91_PMC_PCER1; + pmc_write(pmc, offset, PERIPHERAL_MASK(id)); + return 0; +} + +static void clk_peripheral_disable(struct clk_hw *hw) +{ + struct clk_peripheral *periph = to_clk_peripheral(hw); + struct at91_pmc *pmc = periph->pmc; + int offset = AT91_PMC_PCDR; + u32 id = periph->id; + + if (id < PERIPHERAL_ID_MIN) + return; + if (id > PERIPHERAL_ID_MAX) + offset = AT91_PMC_PCDR1; + pmc_write(pmc, offset, PERIPHERAL_MASK(id)); +} + +static int clk_peripheral_is_enabled(struct clk_hw *hw) +{ + struct clk_peripheral *periph = to_clk_peripheral(hw); + struct at91_pmc *pmc = periph->pmc; + int offset = AT91_PMC_PCSR; + u32 id = periph->id; + + if (id < PERIPHERAL_ID_MIN) + return 1; + if (id > PERIPHERAL_ID_MAX) + offset = AT91_PMC_PCSR1; + return !!(pmc_read(pmc, offset) & PERIPHERAL_MASK(id)); +} + +static const struct clk_ops peripheral_ops = { + .enable = clk_peripheral_enable, + .disable = clk_peripheral_disable, + .is_enabled = clk_peripheral_is_enabled, +}; + +static struct clk * __init +at91_clk_register_peripheral(struct at91_pmc *pmc, const char *name, + const char *parent_name, u32 id) +{ + struct clk_peripheral *periph; + struct clk *clk = NULL; + struct clk_init_data init; + + if (!pmc || !name || !parent_name || id > PERIPHERAL_ID_MAX) + return ERR_PTR(-EINVAL); + + periph = kzalloc(sizeof(*periph), GFP_KERNEL); + if (!periph) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &peripheral_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + init.flags = 0; + + periph->id = id; + periph->hw.init = &init; + periph->pmc = pmc; + + clk = clk_register(NULL, &periph->hw); + if (IS_ERR(clk)) + kfree(periph); + + return clk; +} + +static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) +{ + struct clk *parent; + unsigned long parent_rate; + int shift = 0; + + if (!periph->auto_div) + return; + + if (periph->range.max) { + parent = clk_get_parent_by_index(periph->hw.clk, 0); + parent_rate = __clk_get_rate(parent); + if (!parent_rate) + return; + + for (; shift < PERIPHERAL_MAX_SHIFT; shift++) { + if (parent_rate >> shift <= periph->range.max) + break; + } + } + + periph->auto_div = false; + periph->div = shift; +} + +static int clk_sam9x5_peripheral_enable(struct clk_hw *hw) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + struct at91_pmc *pmc = periph->pmc; + + if (periph->id < PERIPHERAL_ID_MIN) + return 0; + + pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) | + AT91_PMC_PCR_CMD | + AT91_PMC_PCR_DIV(periph->div) | + AT91_PMC_PCR_EN); + return 0; +} + +static void clk_sam9x5_peripheral_disable(struct clk_hw *hw) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + struct at91_pmc *pmc = periph->pmc; + + if (periph->id < PERIPHERAL_ID_MIN) + return; + + pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) | + AT91_PMC_PCR_CMD); +} + +static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + struct at91_pmc *pmc = periph->pmc; + int ret; + + if (periph->id < PERIPHERAL_ID_MIN) + return 1; + + pmc_lock(pmc); + pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID)); + ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_EN); + pmc_unlock(pmc); + + return ret; +} + +static unsigned long +clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + struct at91_pmc *pmc = periph->pmc; + u32 tmp; + + if (periph->id < PERIPHERAL_ID_MIN) + return parent_rate; + + pmc_lock(pmc); + pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID)); + tmp = pmc_read(pmc, AT91_PMC_PCR); + pmc_unlock(pmc); + + if (tmp & AT91_PMC_PCR_EN) { + periph->div = PERIPHERAL_RSHIFT(tmp); + periph->auto_div = false; + } else { + clk_sam9x5_peripheral_autodiv(periph); + } + + return parent_rate >> periph->div; +} + +static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + int shift = 0; + unsigned long best_rate; + unsigned long best_diff; + unsigned long cur_rate = *parent_rate; + unsigned long cur_diff; + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + + if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) + return *parent_rate; + + if (periph->range.max) { + for (; shift < PERIPHERAL_MAX_SHIFT; shift++) { + cur_rate = *parent_rate >> shift; + if (cur_rate <= periph->range.max) + break; + } + } + + if (rate >= cur_rate) + return cur_rate; + + best_diff = cur_rate - rate; + best_rate = cur_rate; + for (; shift < PERIPHERAL_MAX_SHIFT; shift++) { + cur_rate = *parent_rate >> shift; + if (cur_rate < rate) + cur_diff = rate - cur_rate; + else + cur_diff = cur_rate - rate; + + if (cur_diff < best_diff) { + best_diff = cur_diff; + best_rate = cur_rate; + } + + if (!best_diff || cur_rate < rate) + break; + } + + return best_rate; +} + +static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + int shift; + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) { + if (parent_rate == rate) + return 0; + else + return -EINVAL; + } + + if (periph->range.max && rate > periph->range.max) + return -EINVAL; + + for (shift = 0; shift < PERIPHERAL_MAX_SHIFT; shift++) { + if (parent_rate >> shift == rate) { + periph->auto_div = false; + periph->div = shift; + return 0; + } + } + + return -EINVAL; +} + +static const struct clk_ops sam9x5_peripheral_ops = { + .enable = clk_sam9x5_peripheral_enable, + .disable = clk_sam9x5_peripheral_disable, + .is_enabled = clk_sam9x5_peripheral_is_enabled, + .recalc_rate = clk_sam9x5_peripheral_recalc_rate, + .round_rate = clk_sam9x5_peripheral_round_rate, + .set_rate = clk_sam9x5_peripheral_set_rate, +}; + +static struct clk * __init +at91_clk_register_sam9x5_peripheral(struct at91_pmc *pmc, const char *name, + const char *parent_name, u32 id, + const struct clk_range *range) +{ + struct clk_sam9x5_peripheral *periph; + struct clk *clk = NULL; + struct clk_init_data init; + + if (!pmc || !name || !parent_name) + return ERR_PTR(-EINVAL); + + periph = kzalloc(sizeof(*periph), GFP_KERNEL); + if (!periph) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &sam9x5_peripheral_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + init.flags = 0; + + periph->id = id; + periph->hw.init = &init; + periph->div = 0; + periph->pmc = pmc; + periph->auto_div = true; + periph->range = *range; + + clk = clk_register(NULL, &periph->hw); + if (IS_ERR(clk)) + kfree(periph); + else + clk_sam9x5_peripheral_autodiv(periph); + + return clk; +} + +static void __init +of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type) +{ + int num; + u32 id; + struct clk *clk; + const char *parent_name; + const char *name; + struct device_node *periphclknp; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + num = of_get_child_count(np); + if (!num || num > PERIPHERAL_MAX) + return; + + for_each_child_of_node(np, periphclknp) { + if (of_property_read_u32(periphclknp, "reg", &id)) + continue; + + if (id >= PERIPHERAL_MAX) + continue; + + if (of_property_read_string(np, "clock-output-names", &name)) + name = periphclknp->name; + + if (type == PERIPHERAL_AT91RM9200) { + clk = at91_clk_register_peripheral(pmc, name, + parent_name, id); + } else { + struct clk_range range = CLK_RANGE(0, 0); + + of_at91_get_clk_range(periphclknp, + "atmel,clk-output-range", + &range); + + clk = at91_clk_register_sam9x5_peripheral(pmc, name, + parent_name, + id, &range); + } + + if (IS_ERR(clk)) + continue; + + of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk); + } +} + +void __init of_at91rm9200_clk_periph_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91RM9200); +} + +void __init of_at91sam9x5_clk_periph_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91SAM9X5); +} diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index a97554f943e4..afdfeb27acb8 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -269,6 +269,15 @@ static const struct of_device_id pmc_clk_ids[] __initdata = { .compatible = "atmel,at91rm9200-clk-system", .data = of_at91rm9200_clk_sys_setup, }, + /* Peripheral clocks */ + { + .compatible = "atmel,at91rm9200-clk-peripheral", + .data = of_at91rm9200_clk_periph_setup, + }, + { + .compatible = "atmel,at91sam9x5-clk-peripheral", + .data = of_at91sam9x5_clk_periph_setup, + }, { /*sentinel*/ } }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 9c8a397cb850..a09212bc6bc9 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -80,4 +80,9 @@ extern void __init of_at91sam9x5_clk_master_setup(struct device_node *np, extern void __init of_at91rm9200_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc); +extern void __init of_at91rm9200_clk_periph_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_at91sam9x5_clk_periph_setup(struct device_node *np, + struct at91_pmc *pmc); + #endif /* __PMC_H_ */ From 1f22f8bb64b3619324020b48b03181914b1f5544 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 11:57:04 +0200 Subject: [PATCH 10/22] clk: at91: add PMC programmable clocks This patch adds new at91 programmable clocks implementation using common clk framework. A programmable clock is a clock which can be exported on a given pin to clock external devices. Each programmable clock is given an id (from 0 to 8). The number of available programmable clocks depends on the SoC you're using. Programmable clock driver only implements the clock setting (clock rate and parent setting). It must be chained to a system clock in order to enable/disable the generated clock. The PCKX pins used to output the clock signals must be assigned to the appropriate peripheral (see atmel's datasheets). Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- drivers/clk/at91/Makefile | 2 + drivers/clk/at91/clk-programmable.c | 366 ++++++++++++++++++++++++++++ drivers/clk/at91/pmc.c | 15 ++ drivers/clk/at91/pmc.h | 9 + 4 files changed, 392 insertions(+) create mode 100644 drivers/clk/at91/clk-programmable.c diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 04deba35893e..3873b621768f 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -5,3 +5,5 @@ obj-y += pmc.o obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o obj-y += clk-system.o clk-peripheral.o + +obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS) += clk-programmable.o diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c new file mode 100644 index 000000000000..fd792b203eaf --- /dev/null +++ b/drivers/clk/at91/clk-programmable.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define PROG_SOURCE_MAX 5 +#define PROG_ID_MAX 7 + +#define PROG_STATUS_MASK(id) (1 << ((id) + 8)) +#define PROG_PRES_MASK 0x7 +#define PROG_MAX_RM9200_CSS 3 + +struct clk_programmable_layout { + u8 pres_shift; + u8 css_mask; + u8 have_slck_mck; +}; + +struct clk_programmable { + struct clk_hw hw; + struct at91_pmc *pmc; + unsigned int irq; + wait_queue_head_t wait; + u8 id; + u8 css; + u8 pres; + u8 slckmck; + const struct clk_programmable_layout *layout; +}; + +#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw) + + +static irqreturn_t clk_programmable_irq_handler(int irq, void *dev_id) +{ + struct clk_programmable *prog = (struct clk_programmable *)dev_id; + + wake_up(&prog->wait); + + return IRQ_HANDLED; +} + +static int clk_programmable_prepare(struct clk_hw *hw) +{ + u32 tmp; + struct clk_programmable *prog = to_clk_programmable(hw); + struct at91_pmc *pmc = prog->pmc; + const struct clk_programmable_layout *layout = prog->layout; + u8 id = prog->id; + u32 mask = PROG_STATUS_MASK(id); + + tmp = prog->css | (prog->pres << layout->pres_shift); + if (layout->have_slck_mck && prog->slckmck) + tmp |= AT91_PMC_CSSMCK_MCK; + + pmc_write(pmc, AT91_PMC_PCKR(id), tmp); + + while (!(pmc_read(pmc, AT91_PMC_SR) & mask)) + wait_event(prog->wait, pmc_read(pmc, AT91_PMC_SR) & mask); + + return 0; +} + +static int clk_programmable_is_ready(struct clk_hw *hw) +{ + struct clk_programmable *prog = to_clk_programmable(hw); + struct at91_pmc *pmc = prog->pmc; + + return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_PCKR(prog->id)); +} + +static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 tmp; + struct clk_programmable *prog = to_clk_programmable(hw); + struct at91_pmc *pmc = prog->pmc; + const struct clk_programmable_layout *layout = prog->layout; + + tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)); + prog->pres = (tmp >> layout->pres_shift) & PROG_PRES_MASK; + + return parent_rate >> prog->pres; +} + +static long clk_programmable_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long best_rate = *parent_rate; + unsigned long best_diff; + unsigned long new_diff; + unsigned long cur_rate; + int shift = shift; + + if (rate > *parent_rate) + return *parent_rate; + else + best_diff = *parent_rate - rate; + + if (!best_diff) + return best_rate; + + for (shift = 1; shift < PROG_PRES_MASK; shift++) { + cur_rate = *parent_rate >> shift; + + if (cur_rate > rate) + new_diff = cur_rate - rate; + else + new_diff = rate - cur_rate; + + if (!new_diff) + return cur_rate; + + if (new_diff < best_diff) { + best_diff = new_diff; + best_rate = cur_rate; + } + + if (rate > cur_rate) + break; + } + + return best_rate; +} + +static int clk_programmable_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_programmable *prog = to_clk_programmable(hw); + const struct clk_programmable_layout *layout = prog->layout; + if (index > layout->css_mask) { + if (index > PROG_MAX_RM9200_CSS && layout->have_slck_mck) { + prog->css = 0; + prog->slckmck = 1; + return 0; + } else { + return -EINVAL; + } + } + + prog->css = index; + return 0; +} + +static u8 clk_programmable_get_parent(struct clk_hw *hw) +{ + u32 tmp; + u8 ret; + struct clk_programmable *prog = to_clk_programmable(hw); + struct at91_pmc *pmc = prog->pmc; + const struct clk_programmable_layout *layout = prog->layout; + + tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)); + prog->css = tmp & layout->css_mask; + ret = prog->css; + if (layout->have_slck_mck) { + prog->slckmck = !!(tmp & AT91_PMC_CSSMCK_MCK); + if (prog->slckmck && !ret) + ret = PROG_MAX_RM9200_CSS + 1; + } + + return ret; +} + +static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_programmable *prog = to_clk_programmable(hw); + unsigned long best_rate = parent_rate; + unsigned long best_diff; + unsigned long new_diff; + unsigned long cur_rate; + int shift = 0; + + if (rate > parent_rate) + return parent_rate; + else + best_diff = parent_rate - rate; + + if (!best_diff) { + prog->pres = shift; + return 0; + } + + for (shift = 1; shift < PROG_PRES_MASK; shift++) { + cur_rate = parent_rate >> shift; + + if (cur_rate > rate) + new_diff = cur_rate - rate; + else + new_diff = rate - cur_rate; + + if (!new_diff) + break; + + if (new_diff < best_diff) { + best_diff = new_diff; + best_rate = cur_rate; + } + + if (rate > cur_rate) + break; + } + + prog->pres = shift; + return 0; +} + +static const struct clk_ops programmable_ops = { + .prepare = clk_programmable_prepare, + .is_prepared = clk_programmable_is_ready, + .recalc_rate = clk_programmable_recalc_rate, + .round_rate = clk_programmable_round_rate, + .get_parent = clk_programmable_get_parent, + .set_parent = clk_programmable_set_parent, + .set_rate = clk_programmable_set_rate, +}; + +static struct clk * __init +at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq, + const char *name, const char **parent_names, + u8 num_parents, u8 id, + const struct clk_programmable_layout *layout) +{ + int ret; + struct clk_programmable *prog; + struct clk *clk = NULL; + struct clk_init_data init; + char irq_name[11]; + + if (id > PROG_ID_MAX) + return ERR_PTR(-EINVAL); + + prog = kzalloc(sizeof(*prog), GFP_KERNEL); + if (!prog) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &programmable_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + prog->id = id; + prog->layout = layout; + prog->hw.init = &init; + prog->pmc = pmc; + prog->irq = irq; + init_waitqueue_head(&prog->wait); + irq_set_status_flags(prog->irq, IRQ_NOAUTOEN); + snprintf(irq_name, sizeof(irq_name), "clk-prog%d", id); + ret = request_irq(prog->irq, clk_programmable_irq_handler, + IRQF_TRIGGER_HIGH, irq_name, prog); + if (ret) + return ERR_PTR(ret); + + clk = clk_register(NULL, &prog->hw); + if (IS_ERR(clk)) + kfree(prog); + + return clk; +} + +static const struct clk_programmable_layout at91rm9200_programmable_layout = { + .pres_shift = 2, + .css_mask = 0x3, + .have_slck_mck = 0, +}; + +static const struct clk_programmable_layout at91sam9g45_programmable_layout = { + .pres_shift = 2, + .css_mask = 0x3, + .have_slck_mck = 1, +}; + +static const struct clk_programmable_layout at91sam9x5_programmable_layout = { + .pres_shift = 4, + .css_mask = 0x7, + .have_slck_mck = 0, +}; + +static void __init +of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc, + const struct clk_programmable_layout *layout) +{ + int num; + u32 id; + int i; + unsigned int irq; + struct clk *clk; + int num_parents; + const char *parent_names[PROG_SOURCE_MAX]; + const char *name; + struct device_node *progclknp; + + num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + if (num_parents <= 0 || num_parents > PROG_SOURCE_MAX) + return; + + for (i = 0; i < num_parents; ++i) { + parent_names[i] = of_clk_get_parent_name(np, i); + if (!parent_names[i]) + return; + } + + num = of_get_child_count(np); + if (!num || num > (PROG_ID_MAX + 1)) + return; + + for_each_child_of_node(np, progclknp) { + if (of_property_read_u32(progclknp, "reg", &id)) + continue; + + if (of_property_read_string(np, "clock-output-names", &name)) + name = progclknp->name; + + irq = irq_of_parse_and_map(progclknp, 0); + if (!irq) + continue; + + clk = at91_clk_register_programmable(pmc, irq, name, + parent_names, num_parents, + id, layout); + if (IS_ERR(clk)) + continue; + + of_clk_add_provider(progclknp, of_clk_src_simple_get, clk); + } +} + + +void __init of_at91rm9200_clk_prog_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_prog_setup(np, pmc, &at91rm9200_programmable_layout); +} + +void __init of_at91sam9g45_clk_prog_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_prog_setup(np, pmc, &at91sam9g45_programmable_layout); +} + +void __init of_at91sam9x5_clk_prog_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_prog_setup(np, pmc, &at91sam9x5_programmable_layout); +} diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index afdfeb27acb8..d0a1f93500f0 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -278,6 +278,21 @@ static const struct of_device_id pmc_clk_ids[] __initdata = { .compatible = "atmel,at91sam9x5-clk-peripheral", .data = of_at91sam9x5_clk_periph_setup, }, + /* Programmable clocks */ +#if defined(CONFIG_AT91_PROGRAMMABLE_CLOCKS) + { + .compatible = "atmel,at91rm9200-clk-programmable", + .data = of_at91rm9200_clk_prog_setup, + }, + { + .compatible = "atmel,at91sam9g45-clk-programmable", + .data = of_at91sam9g45_clk_prog_setup, + }, + { + .compatible = "atmel,at91sam9x5-clk-programmable", + .data = of_at91sam9x5_clk_prog_setup, + }, +#endif { /*sentinel*/ } }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index a09212bc6bc9..6eeed491c94c 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -85,4 +85,13 @@ extern void __init of_at91rm9200_clk_periph_setup(struct device_node *np, extern void __init of_at91sam9x5_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc); +#if defined(CONFIG_AT91_PROGRAMMABLE_CLOCKS) +extern void __init of_at91rm9200_clk_prog_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_at91sam9g45_clk_prog_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_at91sam9x5_clk_prog_setup(struct device_node *np, + struct at91_pmc *pmc); +#endif + #endif /* __PMC_H_ */ From f090fb37def3eb9ea49b71751dbe43f423f3a152 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 12:22:06 +0200 Subject: [PATCH 11/22] clk: at91: add PMC utmi clock This adds new at91 utmi clock implementation using common clk framework. This clock is a pll with a fixed factor (x40). It is used as a source for usb clock. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 7 ++ drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-utmi.c | 159 ++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.c | 7 ++ drivers/clk/at91/pmc.h | 5 ++ 5 files changed, 179 insertions(+) create mode 100644 drivers/clk/at91/clk-utmi.c diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 85b53a441459..6ad37daca971 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -1,5 +1,8 @@ if ARCH_AT91 +config HAVE_AT91_UTMI + bool + config HAVE_AT91_DBGU0 bool @@ -78,6 +81,7 @@ config SOC_SAMA5D3 select HAVE_FB_ATMEL select HAVE_AT91_DBGU1 select AT91_USE_OLD_CLK + select HAVE_AT91_UTMI help Select this if you are using one of Atmel's SAMA5D3 family SoC. This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35. @@ -124,6 +128,7 @@ config SOC_AT91SAM9RL select HAVE_FB_ATMEL select SOC_AT91SAM9 select AT91_USE_OLD_CLK + select HAVE_AT91_UTMI config SOC_AT91SAM9G45 bool "AT91SAM9G45 or AT91SAM9M10 families" @@ -131,6 +136,7 @@ config SOC_AT91SAM9G45 select HAVE_FB_ATMEL select SOC_AT91SAM9 select AT91_USE_OLD_CLK + select HAVE_AT91_UTMI help Select this if you are using one of Atmel's AT91SAM9G45 family SoC. This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11. @@ -141,6 +147,7 @@ config SOC_AT91SAM9X5 select HAVE_FB_ATMEL select SOC_AT91SAM9 select AT91_USE_OLD_CLK + select HAVE_AT91_UTMI help Select this if you are using one of Atmel's AT91SAM9x5 family SoC. This means that your SAM9 name finishes with a '5' (except if it is diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 3873b621768f..a82488323ea4 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -7,3 +7,4 @@ obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o obj-y += clk-system.o clk-peripheral.o obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS) += clk-programmable.o +obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c new file mode 100644 index 000000000000..ae3263bc1476 --- /dev/null +++ b/drivers/clk/at91/clk-utmi.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define UTMI_FIXED_MUL 40 + +struct clk_utmi { + struct clk_hw hw; + struct at91_pmc *pmc; + unsigned int irq; + wait_queue_head_t wait; +}; + +#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) + +static irqreturn_t clk_utmi_irq_handler(int irq, void *dev_id) +{ + struct clk_utmi *utmi = (struct clk_utmi *)dev_id; + + wake_up(&utmi->wait); + disable_irq_nosync(utmi->irq); + + return IRQ_HANDLED; +} + +static int clk_utmi_prepare(struct clk_hw *hw) +{ + struct clk_utmi *utmi = to_clk_utmi(hw); + struct at91_pmc *pmc = utmi->pmc; + u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) | AT91_PMC_UPLLEN | + AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN; + + pmc_write(pmc, AT91_CKGR_UCKR, tmp); + + while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU)) { + enable_irq(utmi->irq); + wait_event(utmi->wait, + pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU); + } + + return 0; +} + +static int clk_utmi_is_prepared(struct clk_hw *hw) +{ + struct clk_utmi *utmi = to_clk_utmi(hw); + struct at91_pmc *pmc = utmi->pmc; + + return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU); +} + +static void clk_utmi_unprepare(struct clk_hw *hw) +{ + struct clk_utmi *utmi = to_clk_utmi(hw); + struct at91_pmc *pmc = utmi->pmc; + u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) & ~AT91_PMC_UPLLEN; + + pmc_write(pmc, AT91_CKGR_UCKR, tmp); +} + +static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + /* UTMI clk is a fixed clk multiplier */ + return parent_rate * UTMI_FIXED_MUL; +} + +static const struct clk_ops utmi_ops = { + .prepare = clk_utmi_prepare, + .unprepare = clk_utmi_unprepare, + .is_prepared = clk_utmi_is_prepared, + .recalc_rate = clk_utmi_recalc_rate, +}; + +static struct clk * __init +at91_clk_register_utmi(struct at91_pmc *pmc, unsigned int irq, + const char *name, const char *parent_name) +{ + int ret; + struct clk_utmi *utmi; + struct clk *clk = NULL; + struct clk_init_data init; + + utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); + if (!utmi) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &utmi_ops; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.flags = CLK_SET_RATE_GATE; + + utmi->hw.init = &init; + utmi->pmc = pmc; + utmi->irq = irq; + init_waitqueue_head(&utmi->wait); + irq_set_status_flags(utmi->irq, IRQ_NOAUTOEN); + ret = request_irq(utmi->irq, clk_utmi_irq_handler, + IRQF_TRIGGER_HIGH, "clk-utmi", utmi); + if (ret) + return ERR_PTR(ret); + + clk = clk_register(NULL, &utmi->hw); + if (IS_ERR(clk)) + kfree(utmi); + + return clk; +} + +static void __init +of_at91_clk_utmi_setup(struct device_node *np, struct at91_pmc *pmc) +{ + unsigned int irq; + struct clk *clk; + const char *parent_name; + const char *name = np->name; + + parent_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &name); + + irq = irq_of_parse_and_map(np, 0); + if (!irq) + return; + + clk = at91_clk_register_utmi(pmc, irq, name, parent_name); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +} + +void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + of_at91_clk_utmi_setup(np, pmc); +} diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index d0a1f93500f0..0f667fd4e7fa 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -292,6 +292,13 @@ static const struct of_device_id pmc_clk_ids[] __initdata = { .compatible = "atmel,at91sam9x5-clk-programmable", .data = of_at91sam9x5_clk_prog_setup, }, +#endif + /* UTMI clock */ +#if defined(CONFIG_HAVE_AT91_UTMI) + { + .compatible = "atmel,at91sam9x5-clk-utmi", + .data = of_at91sam9x5_clk_utmi_setup, + }, #endif { /*sentinel*/ } }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 6eeed491c94c..8e18ab0d8e22 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -94,4 +94,9 @@ extern void __init of_at91sam9x5_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc); #endif +#if defined(CONFIG_HAVE_AT91_UTMI) +extern void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np, + struct at91_pmc *pmc); +#endif + #endif /* __PMC_H_ */ From c84a61d87261f91d378cf26be4ffd3199ffd2493 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 17 Oct 2013 18:55:41 +0200 Subject: [PATCH 12/22] clk: at91: add PMC usb clock This patch adds new at91 usb clock implementation using common clk framework. This clock is used to clock usb ports (ohci, ehci and udc). Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 11 + drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-usb.c | 398 +++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.c | 15 ++ drivers/clk/at91/pmc.h | 9 + 5 files changed, 434 insertions(+) create mode 100644 drivers/clk/at91/clk-usb.c diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 6ad37daca971..b76dc4c62af9 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -3,6 +3,9 @@ if ARCH_AT91 config HAVE_AT91_UTMI bool +config HAVE_AT91_USB_CLK + bool + config HAVE_AT91_DBGU0 bool @@ -82,6 +85,7 @@ config SOC_SAMA5D3 select HAVE_AT91_DBGU1 select AT91_USE_OLD_CLK select HAVE_AT91_UTMI + select HAVE_AT91_USB_CLK help Select this if you are using one of Atmel's SAMA5D3 family SoC. This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35. @@ -96,12 +100,14 @@ config SOC_AT91RM9200 select MULTI_IRQ_HANDLER select SPARSE_IRQ select AT91_USE_OLD_CLK + select HAVE_AT91_USB_CLK config SOC_AT91SAM9260 bool "AT91SAM9260, AT91SAM9XE or AT91SAM9G20" select HAVE_AT91_DBGU0 select SOC_AT91SAM9 select AT91_USE_OLD_CLK + select HAVE_AT91_USB_CLK help Select this if you are using one of Atmel's AT91SAM9260, AT91SAM9XE or AT91SAM9G20 SoC. @@ -112,6 +118,7 @@ config SOC_AT91SAM9261 select HAVE_FB_ATMEL select SOC_AT91SAM9 select AT91_USE_OLD_CLK + select HAVE_AT91_USB_CLK help Select this if you are using one of Atmel's AT91SAM9261 or AT91SAM9G10 SoC. @@ -121,6 +128,7 @@ config SOC_AT91SAM9263 select HAVE_FB_ATMEL select SOC_AT91SAM9 select AT91_USE_OLD_CLK + select HAVE_AT91_USB_CLK config SOC_AT91SAM9RL bool "AT91SAM9RL" @@ -137,6 +145,7 @@ config SOC_AT91SAM9G45 select SOC_AT91SAM9 select AT91_USE_OLD_CLK select HAVE_AT91_UTMI + select HAVE_AT91_USB_CLK help Select this if you are using one of Atmel's AT91SAM9G45 family SoC. This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11. @@ -148,6 +157,7 @@ config SOC_AT91SAM9X5 select SOC_AT91SAM9 select AT91_USE_OLD_CLK select HAVE_AT91_UTMI + select HAVE_AT91_USB_CLK help Select this if you are using one of Atmel's AT91SAM9x5 family SoC. This means that your SAM9 name finishes with a '5' (except if it is @@ -161,6 +171,7 @@ config SOC_AT91SAM9N12 select HAVE_FB_ATMEL select SOC_AT91SAM9 select AT91_USE_OLD_CLK + select HAVE_AT91_USB_CLK help Select this if you are using Atmel's AT91SAM9N12 SoC. diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index a82488323ea4..61db058be0c0 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -8,3 +8,4 @@ obj-y += clk-system.o clk-peripheral.o obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS) += clk-programmable.o obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o +obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c new file mode 100644 index 000000000000..7d1d26a4bd04 --- /dev/null +++ b/drivers/clk/at91/clk-usb.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define USB_SOURCE_MAX 2 + +#define SAM9X5_USB_DIV_SHIFT 8 +#define SAM9X5_USB_MAX_DIV 0xf + +#define RM9200_USB_DIV_SHIFT 28 +#define RM9200_USB_DIV_TAB_SIZE 4 + +struct at91sam9x5_clk_usb { + struct clk_hw hw; + struct at91_pmc *pmc; +}; + +#define to_at91sam9x5_clk_usb(hw) \ + container_of(hw, struct at91sam9x5_clk_usb, hw) + +struct at91rm9200_clk_usb { + struct clk_hw hw; + struct at91_pmc *pmc; + u32 divisors[4]; +}; + +#define to_at91rm9200_clk_usb(hw) \ + container_of(hw, struct at91rm9200_clk_usb, hw) + +static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 tmp; + u8 usbdiv; + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); + struct at91_pmc *pmc = usb->pmc; + + tmp = pmc_read(pmc, AT91_PMC_USB); + usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT; + return parent_rate / (usbdiv + 1); +} + +static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long div; + unsigned long bestrate; + unsigned long tmp; + + if (rate >= *parent_rate) + return *parent_rate; + + div = *parent_rate / rate; + if (div >= SAM9X5_USB_MAX_DIV) + return *parent_rate / (SAM9X5_USB_MAX_DIV + 1); + + bestrate = *parent_rate / div; + tmp = *parent_rate / (div + 1); + if (bestrate - rate > rate - tmp) + bestrate = tmp; + + return bestrate; +} + +static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index) +{ + u32 tmp; + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); + struct at91_pmc *pmc = usb->pmc; + + if (index > 1) + return -EINVAL; + tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_USBS; + if (index) + tmp |= AT91_PMC_USBS; + pmc_write(pmc, AT91_PMC_USB, tmp); + return 0; +} + +static u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); + struct at91_pmc *pmc = usb->pmc; + + return pmc_read(pmc, AT91_PMC_USB) & AT91_PMC_USBS; +} + +static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u32 tmp; + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); + struct at91_pmc *pmc = usb->pmc; + unsigned long div = parent_rate / rate; + + if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV) + return -EINVAL; + + tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV; + tmp |= (div - 1) << SAM9X5_USB_DIV_SHIFT; + pmc_write(pmc, AT91_PMC_USB, tmp); + + return 0; +} + +static const struct clk_ops at91sam9x5_usb_ops = { + .recalc_rate = at91sam9x5_clk_usb_recalc_rate, + .round_rate = at91sam9x5_clk_usb_round_rate, + .get_parent = at91sam9x5_clk_usb_get_parent, + .set_parent = at91sam9x5_clk_usb_set_parent, + .set_rate = at91sam9x5_clk_usb_set_rate, +}; + +static int at91sam9n12_clk_usb_enable(struct clk_hw *hw) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); + struct at91_pmc *pmc = usb->pmc; + + pmc_write(pmc, AT91_PMC_USB, + pmc_read(pmc, AT91_PMC_USB) | AT91_PMC_USBS); + return 0; +} + +static void at91sam9n12_clk_usb_disable(struct clk_hw *hw) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); + struct at91_pmc *pmc = usb->pmc; + + pmc_write(pmc, AT91_PMC_USB, + pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_USBS); +} + +static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); + struct at91_pmc *pmc = usb->pmc; + + return !!(pmc_read(pmc, AT91_PMC_USB) & AT91_PMC_USBS); +} + +static const struct clk_ops at91sam9n12_usb_ops = { + .enable = at91sam9n12_clk_usb_enable, + .disable = at91sam9n12_clk_usb_disable, + .is_enabled = at91sam9n12_clk_usb_is_enabled, + .recalc_rate = at91sam9x5_clk_usb_recalc_rate, + .round_rate = at91sam9x5_clk_usb_round_rate, + .set_rate = at91sam9x5_clk_usb_set_rate, +}; + +static struct clk * __init +at91sam9x5_clk_register_usb(struct at91_pmc *pmc, const char *name, + const char **parent_names, u8 num_parents) +{ + struct at91sam9x5_clk_usb *usb; + struct clk *clk = NULL; + struct clk_init_data init; + + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91sam9x5_usb_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + usb->hw.init = &init; + usb->pmc = pmc; + + clk = clk_register(NULL, &usb->hw); + if (IS_ERR(clk)) + kfree(usb); + + return clk; +} + +static struct clk * __init +at91sam9n12_clk_register_usb(struct at91_pmc *pmc, const char *name, + const char *parent_name) +{ + struct at91sam9x5_clk_usb *usb; + struct clk *clk = NULL; + struct clk_init_data init; + + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91sam9n12_usb_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE; + + usb->hw.init = &init; + usb->pmc = pmc; + + clk = clk_register(NULL, &usb->hw); + if (IS_ERR(clk)) + kfree(usb); + + return clk; +} + +static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); + struct at91_pmc *pmc = usb->pmc; + u32 tmp; + u8 usbdiv; + + tmp = pmc_read(pmc, AT91_CKGR_PLLBR); + usbdiv = (tmp & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT; + if (usb->divisors[usbdiv]) + return parent_rate / usb->divisors[usbdiv]; + + return 0; +} + +static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); + unsigned long bestrate = 0; + int bestdiff = -1; + unsigned long tmprate; + int tmpdiff; + int i = 0; + + for (i = 0; i < 4; i++) { + if (!usb->divisors[i]) + continue; + tmprate = *parent_rate / usb->divisors[i]; + if (tmprate < rate) + tmpdiff = rate - tmprate; + else + tmpdiff = tmprate - rate; + + if (bestdiff < 0 || bestdiff > tmpdiff) { + bestrate = tmprate; + bestdiff = tmpdiff; + } + + if (!bestdiff) + break; + } + + return bestrate; +} + +static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u32 tmp; + int i; + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); + struct at91_pmc *pmc = usb->pmc; + unsigned long div = parent_rate / rate; + + if (parent_rate % rate) + return -EINVAL; + for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { + if (usb->divisors[i] == div) { + tmp = pmc_read(pmc, AT91_CKGR_PLLBR) & + ~AT91_PMC_USBDIV; + tmp |= i << RM9200_USB_DIV_SHIFT; + pmc_write(pmc, AT91_CKGR_PLLBR, tmp); + return 0; + } + } + + return -EINVAL; +} + +static const struct clk_ops at91rm9200_usb_ops = { + .recalc_rate = at91rm9200_clk_usb_recalc_rate, + .round_rate = at91rm9200_clk_usb_round_rate, + .set_rate = at91rm9200_clk_usb_set_rate, +}; + +static struct clk * __init +at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name, + const char *parent_name, const u32 *divisors) +{ + struct at91rm9200_clk_usb *usb; + struct clk *clk = NULL; + struct clk_init_data init; + + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91rm9200_usb_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; + + usb->hw.init = &init; + usb->pmc = pmc; + memcpy(usb->divisors, divisors, sizeof(usb->divisors)); + + clk = clk_register(NULL, &usb->hw); + if (IS_ERR(clk)) + kfree(usb); + + return clk; +} + +void __init of_at91sam9x5_clk_usb_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + struct clk *clk; + int i; + int num_parents; + const char *parent_names[USB_SOURCE_MAX]; + const char *name = np->name; + + num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + if (num_parents <= 0 || num_parents > USB_SOURCE_MAX) + return; + + for (i = 0; i < num_parents; i++) { + parent_names[i] = of_clk_get_parent_name(np, i); + if (!parent_names[i]) + return; + } + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91sam9x5_clk_register_usb(pmc, name, parent_names, num_parents); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +void __init of_at91sam9n12_clk_usb_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91sam9n12_clk_register_usb(pmc, name, parent_name); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +void __init of_at91rm9200_clk_usb_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + u32 divisors[4] = {0, 0, 0, 0}; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4); + if (!divisors[0]) + return; + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91rm9200_clk_register_usb(pmc, name, parent_name, divisors); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 0f667fd4e7fa..4fba500dec38 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -299,6 +299,21 @@ static const struct of_device_id pmc_clk_ids[] __initdata = { .compatible = "atmel,at91sam9x5-clk-utmi", .data = of_at91sam9x5_clk_utmi_setup, }, +#endif + /* USB clock */ +#if defined(CONFIG_HAVE_AT91_USB_CLK) + { + .compatible = "atmel,at91rm9200-clk-usb", + .data = of_at91rm9200_clk_usb_setup, + }, + { + .compatible = "atmel,at91sam9x5-clk-usb", + .data = of_at91sam9x5_clk_usb_setup, + }, + { + .compatible = "atmel,at91sam9n12-clk-usb", + .data = of_at91sam9n12_clk_usb_setup, + }, #endif { /*sentinel*/ } }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 8e18ab0d8e22..2483ddc0868b 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -99,4 +99,13 @@ extern void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np, struct at91_pmc *pmc); #endif +#if defined(CONFIG_HAVE_AT91_USB_CLK) +extern void __init of_at91rm9200_clk_usb_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_at91sam9x5_clk_usb_setup(struct device_node *np, + struct at91_pmc *pmc); +extern void __init of_at91sam9n12_clk_usb_setup(struct device_node *np, + struct at91_pmc *pmc); +#endif + #endif /* __PMC_H_ */ From a9c0688fde53c0a44cd75e9fe33b16db73187ba8 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 13:27:06 +0200 Subject: [PATCH 13/22] clk: at91: add PMC smd clock This patch adds at91 smd (Soft Modem) clock implementation using common clk framework. Not used by any driver right now. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 5 ++ drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-smd.c | 171 +++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.c | 7 ++ drivers/clk/at91/pmc.h | 5 ++ 5 files changed, 189 insertions(+) create mode 100644 drivers/clk/at91/clk-smd.c diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index b76dc4c62af9..97033f792eea 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -39,6 +39,9 @@ config AT91_SAM9G45_RESET config AT91_SAM9_TIME bool +config HAVE_AT91_SMD + bool + config SOC_AT91SAM9 bool select AT91_SAM9_TIME @@ -85,6 +88,7 @@ config SOC_SAMA5D3 select HAVE_AT91_DBGU1 select AT91_USE_OLD_CLK select HAVE_AT91_UTMI + select HAVE_AT91_SMD select HAVE_AT91_USB_CLK help Select this if you are using one of Atmel's SAMA5D3 family SoC. @@ -157,6 +161,7 @@ config SOC_AT91SAM9X5 select SOC_AT91SAM9 select AT91_USE_OLD_CLK select HAVE_AT91_UTMI + select HAVE_AT91_SMD select HAVE_AT91_USB_CLK help Select this if you are using one of Atmel's AT91SAM9x5 family SoC. diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 61db058be0c0..0e92b716f934 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -9,3 +9,4 @@ obj-y += clk-system.o clk-peripheral.o obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS) += clk-programmable.o obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o +obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c new file mode 100644 index 000000000000..144d47ecfe63 --- /dev/null +++ b/drivers/clk/at91/clk-smd.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define SMD_SOURCE_MAX 2 + +#define SMD_DIV_SHIFT 8 +#define SMD_MAX_DIV 0xf + +struct at91sam9x5_clk_smd { + struct clk_hw hw; + struct at91_pmc *pmc; +}; + +#define to_at91sam9x5_clk_smd(hw) \ + container_of(hw, struct at91sam9x5_clk_smd, hw) + +static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 tmp; + u8 smddiv; + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw); + struct at91_pmc *pmc = smd->pmc; + + tmp = pmc_read(pmc, AT91_PMC_SMD); + smddiv = (tmp & AT91_PMC_SMD_DIV) >> SMD_DIV_SHIFT; + return parent_rate / (smddiv + 1); +} + +static long at91sam9x5_clk_smd_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long div; + unsigned long bestrate; + unsigned long tmp; + + if (rate >= *parent_rate) + return *parent_rate; + + div = *parent_rate / rate; + if (div > SMD_MAX_DIV) + return *parent_rate / (SMD_MAX_DIV + 1); + + bestrate = *parent_rate / div; + tmp = *parent_rate / (div + 1); + if (bestrate - rate > rate - tmp) + bestrate = tmp; + + return bestrate; +} + +static int at91sam9x5_clk_smd_set_parent(struct clk_hw *hw, u8 index) +{ + u32 tmp; + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw); + struct at91_pmc *pmc = smd->pmc; + + if (index > 1) + return -EINVAL; + tmp = pmc_read(pmc, AT91_PMC_SMD) & ~AT91_PMC_SMDS; + if (index) + tmp |= AT91_PMC_SMDS; + pmc_write(pmc, AT91_PMC_SMD, tmp); + return 0; +} + +static u8 at91sam9x5_clk_smd_get_parent(struct clk_hw *hw) +{ + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw); + struct at91_pmc *pmc = smd->pmc; + + return pmc_read(pmc, AT91_PMC_SMD) & AT91_PMC_SMDS; +} + +static int at91sam9x5_clk_smd_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u32 tmp; + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw); + struct at91_pmc *pmc = smd->pmc; + unsigned long div = parent_rate / rate; + + if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1)) + return -EINVAL; + tmp = pmc_read(pmc, AT91_PMC_SMD) & ~AT91_PMC_SMD_DIV; + tmp |= (div - 1) << SMD_DIV_SHIFT; + pmc_write(pmc, AT91_PMC_SMD, tmp); + + return 0; +} + +static const struct clk_ops at91sam9x5_smd_ops = { + .recalc_rate = at91sam9x5_clk_smd_recalc_rate, + .round_rate = at91sam9x5_clk_smd_round_rate, + .get_parent = at91sam9x5_clk_smd_get_parent, + .set_parent = at91sam9x5_clk_smd_set_parent, + .set_rate = at91sam9x5_clk_smd_set_rate, +}; + +static struct clk * __init +at91sam9x5_clk_register_smd(struct at91_pmc *pmc, const char *name, + const char **parent_names, u8 num_parents) +{ + struct at91sam9x5_clk_smd *smd; + struct clk *clk = NULL; + struct clk_init_data init; + + smd = kzalloc(sizeof(*smd), GFP_KERNEL); + if (!smd) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91sam9x5_smd_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + smd->hw.init = &init; + smd->pmc = pmc; + + clk = clk_register(NULL, &smd->hw); + if (IS_ERR(clk)) + kfree(smd); + + return clk; +} + +void __init of_at91sam9x5_clk_smd_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + struct clk *clk; + int i; + int num_parents; + const char *parent_names[SMD_SOURCE_MAX]; + const char *name = np->name; + + num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + if (num_parents <= 0 || num_parents > SMD_SOURCE_MAX) + return; + + for (i = 0; i < num_parents; i++) { + parent_names[i] = of_clk_get_parent_name(np, i); + if (!parent_names[i]) + return; + } + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91sam9x5_clk_register_smd(pmc, name, parent_names, + num_parents); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 4fba500dec38..7b9db603b936 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -314,6 +314,13 @@ static const struct of_device_id pmc_clk_ids[] __initdata = { .compatible = "atmel,at91sam9n12-clk-usb", .data = of_at91sam9n12_clk_usb_setup, }, +#endif + /* SMD clock */ +#if defined(CONFIG_HAVE_AT91_SMD) + { + .compatible = "atmel,at91sam9x5-clk-smd", + .data = of_at91sam9x5_clk_smd_setup, + }, #endif { /*sentinel*/ } }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 2483ddc0868b..ba8d14233f80 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -108,4 +108,9 @@ extern void __init of_at91sam9n12_clk_usb_setup(struct device_node *np, struct at91_pmc *pmc); #endif +#if defined(CONFIG_HAVE_AT91_SMD) +extern void __init of_at91sam9x5_clk_smd_setup(struct device_node *np, + struct at91_pmc *pmc); +#endif + #endif /* __PMC_H_ */ From 32f955c6da20af07ffb059c060ea2e8f15a2bbb9 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 13:38:52 +0200 Subject: [PATCH 14/22] dt: binding: add at91 clks dt bindings documentation This patch adds new at91 clks dt bindings documentation. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- .../devicetree/bindings/clock/at91-clock.txt | 339 ++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/at91-clock.txt diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt new file mode 100644 index 000000000000..cd5e23912888 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt @@ -0,0 +1,339 @@ +Device Tree Clock bindings for arch-at91 + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be one of the following: + "atmel,at91rm9200-pmc" or + "atmel,at91sam9g45-pmc" or + "atmel,at91sam9n12-pmc" or + "atmel,at91sam9x5-pmc" or + "atmel,sama5d3-pmc": + at91 PMC (Power Management Controller) + All at91 specific clocks (clocks defined below) must be child + node of the PMC node. + + "atmel,at91rm9200-clk-main": + at91 main oscillator + + "atmel,at91rm9200-clk-master" or + "atmel,at91sam9x5-clk-master": + at91 master clock + + "atmel,at91sam9x5-clk-peripheral" or + "atmel,at91rm9200-clk-peripheral": + at91 peripheral clocks + + "atmel,at91rm9200-clk-pll" or + "atmel,at91sam9g45-clk-pll" or + "atmel,at91sam9g20-clk-pllb" or + "atmel,sama5d3-clk-pll": + at91 pll clocks + + "atmel,at91sam9x5-clk-plldiv": + at91 plla divisor + + "atmel,at91rm9200-clk-programmable" or + "atmel,at91sam9g45-clk-programmable" or + "atmel,at91sam9x5-clk-programmable": + at91 programmable clocks + + "atmel,at91sam9x5-clk-smd": + at91 SMD (Soft Modem) clock + + "atmel,at91rm9200-clk-system": + at91 system clocks + + "atmel,at91rm9200-clk-usb" or + "atmel,at91sam9x5-clk-usb" or + "atmel,at91sam9n12-clk-usb": + at91 usb clock + + "atmel,at91sam9x5-clk-utmi": + at91 utmi clock + +Required properties for PMC node: +- reg : defines the IO memory reserved for the PMC. +- #size-cells : shall be 0 (reg is used to encode clk id). +- #address-cells : shall be 1 (reg is used to encode clk id). +- interrupts : shall be set to PMC interrupt line. +- interrupt-controller : tell that the PMC is an interrupt controller. +- #interrupt-cells : must be set to 1. The first cell encodes the interrupt id, + and reflect the bit position in the PMC_ER/DR/SR registers. + You can use the dt macros defined in dt-bindings/clk/at91.h. + 0 (AT91_PMC_MOSCS) -> main oscillator ready + 1 (AT91_PMC_LOCKA) -> PLL A ready + 2 (AT91_PMC_LOCKB) -> PLL B ready + 3 (AT91_PMC_MCKRDY) -> master clock ready + 6 (AT91_PMC_LOCKU) -> UTMI PLL clock ready + 8 .. 15 (AT91_PMC_PCKRDY(id)) -> programmable clock ready + 16 (AT91_PMC_MOSCSELS) -> main oscillator selected + 17 (AT91_PMC_MOSCRCS) -> RC main oscillator stabilized + 18 (AT91_PMC_CFDEV) -> clock failure detected + +For example: + pmc: pmc@fffffc00 { + compatible = "atmel,sama5d3-pmc"; + interrupts = <1 4 7>; + interrupt-controller; + #interrupt-cells = <2>; + #size-cells = <0>; + #address-cells = <1>; + + /* put at91 clocks here */ + }; + +Required properties for main clock: +- interrupt-parent : must reference the PMC node. +- interrupts : shall be set to "<0>". +- #clock-cells : from common clock binding; shall be set to 0. +- clocks (optional if clock-frequency is provided) : shall be the slow clock + phandle. This clock is used to calculate the main clock rate if + "clock-frequency" is not provided. +- clock-frequency : the main oscillator frequency.Prefer the use of + "clock-frequency" over automatic clock rate calculation. + +For example: + main: mainck { + compatible = "atmel,at91rm9200-clk-main"; + interrupt-parent = <&pmc>; + interrupts = <0>; + #clock-cells = <0>; + clocks = <&ck32k>; + clock-frequency = <18432000>; + }; + +Required properties for master clock: +- interrupt-parent : must reference the PMC node. +- interrupts : shall be set to "<3>". +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : shall be the master clock sources (see atmel datasheet) phandles. + e.g. "<&ck32k>, <&main>, <&plla>, <&pllb>". +- atmel,clk-output-range : minimum and maximum clock frequency (two u32 + fields). + e.g. output = <0 133000000>; <=> 0 to 133MHz. +- atmel,clk-divisors : master clock divisors table (four u32 fields). + 0 <=> reserved value. + e.g. divisors = <1 2 4 6>; +- atmel,master-clk-have-div3-pres : some SoC use the reserved value 7 in the + PRES field as CLOCK_DIV3 (e.g sam9x5). + +For example: + mck: mck { + compatible = "atmel,at91rm9200-clk-master"; + interrupt-parent = <&pmc>; + interrupts = <3>; + #clock-cells = <0>; + atmel,clk-output-range = <0 133000000>; + atmel,clk-divisors = <1 2 4 0>; + }; + +Required properties for peripheral clocks: +- #size-cells : shall be 0 (reg is used to encode clk id). +- #address-cells : shall be 1 (reg is used to encode clk id). +- clocks : shall be the master clock phandle. + e.g. clocks = <&mck>; +- name: device tree node describing a specific system clock. + * #clock-cells : from common clock binding; shall be set to 0. + * reg: peripheral id. See Atmel's datasheets to get a full + list of peripheral ids. + * atmel,clk-output-range : minimum and maximum clock frequency + (two u32 fields). Only valid on at91sam9x5-clk-peripheral + compatible IPs. + +For example: + periph: periphck { + compatible = "atmel,at91sam9x5-clk-peripheral"; + #size-cells = <0>; + #address-cells = <1>; + clocks = <&mck>; + + ssc0_clk { + #clock-cells = <0>; + reg = <2>; + atmel,clk-output-range = <0 133000000>; + }; + + usart0_clk { + #clock-cells = <0>; + reg = <3>; + atmel,clk-output-range = <0 66000000>; + }; + }; + + +Required properties for pll clocks: +- interrupt-parent : must reference the PMC node. +- interrupts : shall be set to "<1>". +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : shall be the main clock phandle. +- reg : pll id. + 0 -> PLL A + 1 -> PLL B +- atmel,clk-input-range : minimum and maximum source clock frequency (two u32 + fields). + e.g. input = <1 32000000>; <=> 1 to 32MHz. +- #atmel,pll-clk-output-range-cells : number of cells reserved for pll output + range description. Sould be set to 2, 3 + or 4. + * 1st and 2nd cells represent the frequency range (min-max). + * 3rd cell is optional and represents the OUT field value for the given + range. + * 4th cell is optional and represents the ICPLL field (PLLICPR + register) +- atmel,pll-clk-output-ranges : pll output frequency ranges + optional parameter + depending on #atmel,pll-output-range-cells + property value. + +For example: + plla: pllack { + compatible = "atmel,at91sam9g45-clk-pll"; + interrupt-parent = <&pmc>; + interrupts = <1>; + #clock-cells = <0>; + clocks = <&main>; + reg = <0>; + atmel,clk-input-range = <2000000 32000000>; + #atmel,pll-clk-output-range-cells = <4>; + atmel,pll-clk-output-ranges = <74500000 800000000 0 0 + 69500000 750000000 1 0 + 64500000 700000000 2 0 + 59500000 650000000 3 0 + 54500000 600000000 0 1 + 49500000 550000000 1 1 + 44500000 500000000 2 1 + 40000000 450000000 3 1>; + }; + +Required properties for plldiv clocks (plldiv = pll / 2): +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : shall be the plla clock phandle. + +The pll divisor is equal to 2 and cannot be changed. + +For example: + plladiv: plladivck { + compatible = "atmel,at91sam9x5-clk-plldiv"; + #clock-cells = <0>; + clocks = <&plla>; + }; + +Required properties for programmable clocks: +- interrupt-parent : must reference the PMC node. +- #size-cells : shall be 0 (reg is used to encode clk id). +- #address-cells : shall be 1 (reg is used to encode clk id). +- clocks : shall be the programmable clock source phandles. + e.g. clocks = <&clk32k>, <&main>, <&plla>, <&pllb>; +- name: device tree node describing a specific prog clock. + * #clock-cells : from common clock binding; shall be set to 0. + * reg : programmable clock id (register offset from PCKx + register). + * interrupts : shall be set to "<(8 + id)>". + +For example: + prog: progck { + compatible = "atmel,at91sam9g45-clk-programmable"; + #size-cells = <0>; + #address-cells = <1>; + interrupt-parent = <&pmc>; + clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>; + + prog0 { + #clock-cells = <0>; + reg = <0>; + interrupts = <8>; + }; + + prog1 { + #clock-cells = <0>; + reg = <1>; + interrupts = <9>; + }; + }; + + +Required properties for smd clock: +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : shall be the smd clock source phandles. + e.g. clocks = <&plladiv>, <&utmi>; + +For example: + smd: smdck { + compatible = "atmel,at91sam9x5-clk-smd"; + #clock-cells = <0>; + clocks = <&plladiv>, <&utmi>; + }; + +Required properties for system clocks: +- #size-cells : shall be 0 (reg is used to encode clk id). +- #address-cells : shall be 1 (reg is used to encode clk id). +- name: device tree node describing a specific system clock. + * #clock-cells : from common clock binding; shall be set to 0. + * reg: system clock id (bit position in SCER/SCDR/SCSR registers). + See Atmel's datasheet to get a full list of system clock ids. + +For example: + system: systemck { + compatible = "atmel,at91rm9200-clk-system"; + #address-cells = <1>; + #size-cells = <0>; + + ddrck { + #clock-cells = <0>; + reg = <2>; + clocks = <&mck>; + }; + + uhpck { + #clock-cells = <0>; + reg = <6>; + clocks = <&usb>; + }; + + udpck { + #clock-cells = <0>; + reg = <7>; + clocks = <&usb>; + }; + }; + + +Required properties for usb clock: +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : shall be the smd clock source phandles. + e.g. clocks = <&pllb>; +- atmel,clk-divisors (only available for "atmel,at91rm9200-clk-usb"): + usb clock divisor table. + e.g. divisors = <1 2 4 0>; + +For example: + usb: usbck { + compatible = "atmel,at91sam9x5-clk-usb"; + #clock-cells = <0>; + clocks = <&plladiv>, <&utmi>; + }; + + usb: usbck { + compatible = "atmel,at91rm9200-clk-usb"; + #clock-cells = <0>; + clocks = <&pllb>; + atmel,clk-divisors = <1 2 4 0>; + }; + + +Required properties for utmi clock: +- interrupt-parent : must reference the PMC node. +- interrupts : shall be set to "". +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : shall be the main clock source phandle. + +For example: + utmi: utmick { + compatible = "atmel,at91sam9x5-clk-utmi"; + interrupt-parent = <&pmc>; + interrupts = ; + #clock-cells = <0>; + clocks = <&main>; + }; From 7034be876e6b6f98b0babf359c21aab6b40bb9bd Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 13:46:28 +0200 Subject: [PATCH 15/22] ARM: at91: move pit timer to common clk framework Use device tree to get the source clock of the PIT (Periodic Interval Timer). If the clock is not found in device tree (or dt is not enabled) we'll try to get it using clk_lookup definitions. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam926x_time.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index bb392320a0dd..0f04ffe9c5a8 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -39,6 +39,7 @@ static u32 pit_cycle; /* write-once */ static u32 pit_cnt; /* access only w/system irq blocked */ static void __iomem *pit_base_addr __read_mostly; +static struct clk *mck; static inline unsigned int pit_read(unsigned int reg_offset) { @@ -195,10 +196,14 @@ static int __init of_at91sam926x_pit_init(void) if (!pit_base_addr) goto node_err; + mck = of_clk_get(np, 0); + /* Get the interrupts property */ ret = irq_of_parse_and_map(np, 0); if (!ret) { pr_crit("AT91: PIT: Unable to get IRQ from DT\n"); + if (!IS_ERR(mck)) + clk_put(mck); goto ioremap_err; } at91sam926x_pit_irq.irq = ret; @@ -230,6 +235,8 @@ void __init at91sam926x_pit_init(void) unsigned bits; int ret; + mck = ERR_PTR(-ENOENT); + /* For device tree enabled device: initialize here */ of_at91sam926x_pit_init(); @@ -237,7 +244,12 @@ void __init at91sam926x_pit_init(void) * Use our actual MCK to figure out how many MCK/16 ticks per * 1/HZ period (instead of a compile-time constant LATCH). */ - pit_rate = clk_get_rate(clk_get(NULL, "mck")) / 16; + if (IS_ERR(mck)) + mck = clk_get(NULL, "mck"); + + if (IS_ERR(mck)) + panic("AT91: PIT: Unable to get mck clk\n"); + pit_rate = clk_get_rate(mck) / 16; pit_cycle = (pit_rate + HZ/2) / HZ; WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0); From c59b631517e91300f6a4838f593ebca5ebde8742 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 13:49:10 +0200 Subject: [PATCH 16/22] ARM: at91: add new compatible strings for pmc driver This patch adds new compatible string for PMC node to prepare the transition to common clk. These compatible string come from pmc driver in clk subsystem and are needed to provide new device tree compatibility with old at91 clks (device tree using common clks will use the new compatible strings). Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/clock.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c index 5f02aea1021c..72b257944733 100644 --- a/arch/arm/mach-at91/clock.c +++ b/arch/arm/mach-at91/clock.c @@ -884,6 +884,11 @@ static int __init at91_pmc_init(unsigned long main_clock) #if defined(CONFIG_OF) static struct of_device_id pmc_ids[] = { { .compatible = "atmel,at91rm9200-pmc" }, + { .compatible = "atmel,at91sam9260-pmc" }, + { .compatible = "atmel,at91sam9g45-pmc" }, + { .compatible = "atmel,at91sam9n12-pmc" }, + { .compatible = "atmel,at91sam9x5-pmc" }, + { .compatible = "atmel,sama5d3-pmc" }, { /*sentinel*/ } }; From d4d24c1b4f74e055905db6e677a2ba05f55de30a Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 14:43:31 +0200 Subject: [PATCH 17/22] ARM: at91: prepare sama5 dt boards transition to common clk This patch prepare the transition to common clk for sama5 dt boards by replacing the timer init callback. Clocks registration cannot be done in early init callback (as formerly done by the old clk implementation) because it requires dynamic allocation which is not ready yet during early init. In the other hand, at91 clocks must be registered before at91sam926x_pit_init is called because PIT (Periodic Interval Timer) driver request the master clk (mck). A new function (at91sama5_dt_timer_init) is created to fullfil these needs. This function registers all at91 clks using the dt definition before calling the PIT init function. The device tree clock registration is enabled only if common clk is selected. Else the old clk registration is been done during at91_dt_initialize call. Signed-off-by: Boris BREZILLON Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/board-dt-sama5.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-at91/board-dt-sama5.c b/arch/arm/mach-at91/board-dt-sama5.c index bf00d15d954d..075ec0576ada 100644 --- a/arch/arm/mach-at91/board-dt-sama5.c +++ b/arch/arm/mach-at91/board-dt-sama5.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,13 @@ #include "at91_aic.h" #include "generic.h" +static void __init sama5_dt_timer_init(void) +{ +#if defined(CONFIG_COMMON_CLK) + of_clk_init(NULL); +#endif + at91sam926x_pit_init(); +} static const struct of_device_id irq_of_match[] __initconst = { @@ -72,7 +80,7 @@ static const char *sama5_dt_board_compat[] __initdata = { DT_MACHINE_START(sama5_dt, "Atmel SAMA5 (Device Tree)") /* Maintainer: Atmel */ - .init_time = at91sam926x_pit_init, + .init_time = sama5_dt_timer_init, .map_io = at91_map_io, .handle_irq = at91_aic5_handle_irq, .init_early = at91_dt_initialize, From d1ff2300228e54f1d748f810a28134f4e6f160c7 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 2 Dec 2013 15:10:48 +0100 Subject: [PATCH 18/22] ARM: at91: prepare common clk transition for sama5d3 SoC This patch encloses sama5d3 old clk registration in "#if defined(CONFIG_OLD_CLK_AT91) #endif" sections. Signed-off-by: Boris BREZILLON Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/sama5d3.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-at91/sama5d3.c b/arch/arm/mach-at91/sama5d3.c index 5c6dc24fafbd..f3313b9d084a 100644 --- a/arch/arm/mach-at91/sama5d3.c +++ b/arch/arm/mach-at91/sama5d3.c @@ -19,9 +19,10 @@ #include "soc.h" #include "generic.h" -#include "clock.h" #include "sam9_smc.h" +#if defined(CONFIG_OLD_CLK_AT91) +#include "clock.h" /* -------------------------------------------------------------------- * Clocks * -------------------------------------------------------------------- */ @@ -361,6 +362,7 @@ static void __init sama5d3_register_clocks(void) clk_register(&pck1); clk_register(&pck2); } +#endif /* -------------------------------------------------------------------- * AT91SAM9x5 processor initialization @@ -378,6 +380,8 @@ static void __init sama5d3_initialize(void) AT91_SOC_START(sama5d3) .map_io = sama5d3_map_io, +#if defined(CONFIG_OLD_CLK_AT91) .register_clocks = sama5d3_register_clocks, +#endif .init = sama5d3_initialize, AT91_SOC_END From d2e8190b7916ed5d8a8b5145b3dd85ca07412edd Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 18 Oct 2013 23:48:27 +0200 Subject: [PATCH 19/22] ARM: at91/dt: define sama5d3 clocks Define sama5d3 clocks in sama5d3 device tree. Add references to the appropriate clocks in each peripheral. Signed-off-by: Boris BREZILLON Signed-off-by: Nicolas Ferre --- arch/arm/boot/dts/sama5d3.dtsi | 379 +++++++++++++++++++++++++++- arch/arm/boot/dts/sama5d3_can.dtsi | 20 ++ arch/arm/boot/dts/sama5d3_emac.dtsi | 11 + arch/arm/boot/dts/sama5d3_gmac.dtsi | 11 + arch/arm/boot/dts/sama5d3_lcd.dtsi | 17 ++ arch/arm/boot/dts/sama5d3_mci2.dtsi | 12 + arch/arm/boot/dts/sama5d3_tcb1.dtsi | 12 + arch/arm/boot/dts/sama5d3_uart.dtsi | 21 ++ 8 files changed, 482 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi index 5cdaba4cea86..de9feced9935 100644 --- a/arch/arm/boot/dts/sama5d3.dtsi +++ b/arch/arm/boot/dts/sama5d3.dtsi @@ -13,6 +13,7 @@ #include #include #include +#include / { model = "Atmel SAMA5D3 family SoC"; @@ -56,6 +57,14 @@ reg = <0x20000000 0x8000000>; }; + clocks { + adc_op_clk: adc_op_clk{ + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <20000000>; + }; + }; + ahb { compatible = "simple-bus"; #address-cells = <1>; @@ -79,6 +88,8 @@ status = "disabled"; #address-cells = <1>; #size-cells = <0>; + clocks = <&mci0_clk>; + clock-names = "mci_clk"; }; spi0: spi@f0004000 { @@ -92,6 +103,8 @@ dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_spi0>; + clocks = <&spi0_clk>; + clock-names = "spi_clk"; status = "disabled"; }; @@ -101,6 +114,8 @@ interrupts = <38 IRQ_TYPE_LEVEL_HIGH 4>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>; + clocks = <&ssc0_clk>; + clock-names = "pclk"; status = "disabled"; }; @@ -108,6 +123,8 @@ compatible = "atmel,at91sam9x5-tcb"; reg = <0xf0010000 0x100>; interrupts = <26 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&tcb0_clk>; + clock-names = "t0_clk"; }; i2c0: i2c@f0014000 { @@ -121,6 +138,7 @@ pinctrl-0 = <&pinctrl_i2c0>; #address-cells = <1>; #size-cells = <0>; + clocks = <&twi0_clk>; status = "disabled"; }; @@ -135,6 +153,7 @@ pinctrl-0 = <&pinctrl_i2c1>; #address-cells = <1>; #size-cells = <0>; + clocks = <&twi1_clk>; status = "disabled"; }; @@ -144,6 +163,8 @@ interrupts = <12 IRQ_TYPE_LEVEL_HIGH 5>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usart0>; + clocks = <&usart0_clk>; + clock-names = "usart"; status = "disabled"; }; @@ -153,6 +174,8 @@ interrupts = <13 IRQ_TYPE_LEVEL_HIGH 5>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usart1>; + clocks = <&usart1_clk>; + clock-names = "usart"; status = "disabled"; }; @@ -174,6 +197,8 @@ status = "disabled"; #address-cells = <1>; #size-cells = <0>; + clocks = <&mci1_clk>; + clock-names = "mci_clk"; }; spi1: spi@f8008000 { @@ -187,6 +212,8 @@ dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_spi1>; + clocks = <&spi1_clk>; + clock-names = "spi_clk"; status = "disabled"; }; @@ -196,6 +223,8 @@ interrupts = <39 IRQ_TYPE_LEVEL_HIGH 4>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ssc1_tx &pinctrl_ssc1_rx>; + clocks = <&ssc1_clk>; + clock-names = "pclk"; status = "disabled"; }; @@ -219,6 +248,9 @@ &pinctrl_adc0_ad10 &pinctrl_adc0_ad11 >; + clocks = <&adc_clk>, + <&adc_op_clk>; + clock-names = "adc_clk", "adc_op_clk"; atmel,adc-channel-base = <0x50>; atmel,adc-channels-used = <0xfff>; atmel,adc-drdy-mask = <0x1000000>; @@ -274,6 +306,7 @@ dma-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; + clocks = <&twi2_clk>; status = "disabled"; }; @@ -283,6 +316,8 @@ interrupts = <14 IRQ_TYPE_LEVEL_HIGH 5>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usart2>; + clocks = <&usart2_clk>; + clock-names = "usart"; status = "disabled"; }; @@ -292,6 +327,8 @@ interrupts = <15 IRQ_TYPE_LEVEL_HIGH 5>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usart3>; + clocks = <&usart3_clk>; + clock-names = "usart"; status = "disabled"; }; @@ -318,6 +355,8 @@ reg = <0xffffe600 0x200>; interrupts = <30 IRQ_TYPE_LEVEL_HIGH 0>; #dma-cells = <2>; + clocks = <&dma0_clk>; + clock-names = "dma_clk"; }; dma1: dma-controller@ffffe800 { @@ -325,6 +364,8 @@ reg = <0xffffe800 0x200>; interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>; #dma-cells = <2>; + clocks = <&dma1_clk>; + clock-names = "dma_clk"; }; ramc0: ramc@ffffea00 { @@ -338,6 +379,8 @@ interrupts = <2 IRQ_TYPE_LEVEL_HIGH 7>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_dbgu>; + clocks = <&dbgu_clk>; + clock-names = "usart"; status = "disabled"; }; @@ -626,6 +669,7 @@ gpio-controller; interrupt-controller; #interrupt-cells = <2>; + clocks = <&pioA_clk>; }; pioB: gpio@fffff400 { @@ -636,6 +680,7 @@ gpio-controller; interrupt-controller; #interrupt-cells = <2>; + clocks = <&pioB_clk>; }; pioC: gpio@fffff600 { @@ -646,6 +691,7 @@ gpio-controller; interrupt-controller; #interrupt-cells = <2>; + clocks = <&pioC_clk>; }; pioD: gpio@fffff800 { @@ -656,6 +702,7 @@ gpio-controller; interrupt-controller; #interrupt-cells = <2>; + clocks = <&pioD_clk>; }; pioE: gpio@fffffa00 { @@ -666,12 +713,334 @@ gpio-controller; interrupt-controller; #interrupt-cells = <2>; + clocks = <&pioE_clk>; }; }; pmc: pmc@fffffc00 { - compatible = "atmel,at91rm9200-pmc"; + compatible = "atmel,sama5d3-pmc"; reg = <0xfffffc00 0x120>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>; + interrupt-controller; + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; + + clk32k: slck { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + + main: mainck { + compatible = "atmel,at91rm9200-clk-main"; + #clock-cells = <0>; + interrupt-parent = <&pmc>; + interrupts = ; + clocks = <&clk32k>; + }; + + plla: pllack { + compatible = "atmel,sama5d3-clk-pll"; + #clock-cells = <0>; + interrupt-parent = <&pmc>; + interrupts = ; + clocks = <&main>; + reg = <0>; + atmel,clk-input-range = <8000000 50000000>; + #atmel,pll-clk-output-range-cells = <4>; + atmel,pll-clk-output-ranges = <400000000 1000000000 0 0>; + }; + + plladiv: plladivck { + compatible = "atmel,at91sam9x5-clk-plldiv"; + #clock-cells = <0>; + clocks = <&plla>; + }; + + utmi: utmick { + compatible = "atmel,at91sam9x5-clk-utmi"; + #clock-cells = <0>; + interrupt-parent = <&pmc>; + interrupts = ; + clocks = <&main>; + }; + + mck: masterck { + compatible = "atmel,at91sam9x5-clk-master"; + #clock-cells = <0>; + interrupt-parent = <&pmc>; + interrupts = ; + clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>; + atmel,clk-output-range = <0 166000000>; + atmel,clk-divisors = <1 2 4 3>; + }; + + usb: usbck { + compatible = "atmel,at91sam9x5-clk-usb"; + #clock-cells = <0>; + clocks = <&plladiv>, <&utmi>; + }; + + prog: progck { + compatible = "atmel,at91sam9x5-clk-programmable"; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&pmc>; + clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>; + + prog0: prog0 { + #clock-cells = <0>; + reg = <0>; + interrupts = ; + }; + + prog1: prog1 { + #clock-cells = <0>; + reg = <1>; + interrupts = ; + }; + + prog2: prog2 { + #clock-cells = <0>; + reg = <2>; + interrupts = ; + }; + }; + + smd: smdclk { + compatible = "atmel,at91sam9x5-clk-smd"; + #clock-cells = <0>; + clocks = <&plladiv>, <&utmi>; + }; + + systemck { + compatible = "atmel,at91rm9200-clk-system"; + #address-cells = <1>; + #size-cells = <0>; + + ddrck: ddrck { + #clock-cells = <0>; + reg = <2>; + clocks = <&mck>; + }; + + smdck: smdck { + #clock-cells = <0>; + reg = <4>; + clocks = <&smd>; + }; + + uhpck: uhpck { + #clock-cells = <0>; + reg = <6>; + clocks = <&usb>; + }; + + udpck: udpck { + #clock-cells = <0>; + reg = <7>; + clocks = <&usb>; + }; + + pck0: pck0 { + #clock-cells = <0>; + reg = <8>; + clocks = <&prog0>; + }; + + pck1: pck1 { + #clock-cells = <0>; + reg = <9>; + clocks = <&prog1>; + }; + + pck2: pck2 { + #clock-cells = <0>; + reg = <10>; + clocks = <&prog2>; + }; + }; + + periphck { + compatible = "atmel,at91sam9x5-clk-peripheral"; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&mck>; + + dbgu_clk: dbgu_clk { + #clock-cells = <0>; + reg = <2>; + }; + + pioA_clk: pioA_clk { + #clock-cells = <0>; + reg = <6>; + }; + + pioB_clk: pioB_clk { + #clock-cells = <0>; + reg = <7>; + }; + + pioC_clk: pioC_clk { + #clock-cells = <0>; + reg = <8>; + }; + + pioD_clk: pioD_clk { + #clock-cells = <0>; + reg = <9>; + }; + + pioE_clk: pioE_clk { + #clock-cells = <0>; + reg = <10>; + }; + + usart0_clk: usart0_clk { + #clock-cells = <0>; + reg = <12>; + atmel,clk-output-range = <0 66000000>; + }; + + usart1_clk: usart1_clk { + #clock-cells = <0>; + reg = <13>; + atmel,clk-output-range = <0 66000000>; + }; + + usart2_clk: usart2_clk { + #clock-cells = <0>; + reg = <14>; + atmel,clk-output-range = <0 66000000>; + }; + + usart3_clk: usart3_clk { + #clock-cells = <0>; + reg = <15>; + atmel,clk-output-range = <0 66000000>; + }; + + twi0_clk: twi0_clk { + reg = <18>; + #clock-cells = <0>; + atmel,clk-output-range = <0 16625000>; + }; + + twi1_clk: twi1_clk { + #clock-cells = <0>; + reg = <19>; + atmel,clk-output-range = <0 16625000>; + }; + + twi2_clk: twi2_clk { + #clock-cells = <0>; + reg = <20>; + atmel,clk-output-range = <0 16625000>; + }; + + mci0_clk: mci0_clk { + #clock-cells = <0>; + reg = <21>; + }; + + mci1_clk: mci1_clk { + #clock-cells = <0>; + reg = <22>; + }; + + spi0_clk: spi0_clk { + #clock-cells = <0>; + reg = <24>; + atmel,clk-output-range = <0 133000000>; + }; + + spi1_clk: spi1_clk { + #clock-cells = <0>; + reg = <25>; + atmel,clk-output-range = <0 133000000>; + }; + + tcb0_clk: tcb0_clk { + #clock-cells = <0>; + reg = <26>; + atmel,clk-output-range = <0 133000000>; + }; + + pwm_clk: pwm_clk { + #clock-cells = <0>; + reg = <28>; + }; + + adc_clk: adc_clk { + #clock-cells = <0>; + reg = <29>; + atmel,clk-output-range = <0 66000000>; + }; + + dma0_clk: dma0_clk { + #clock-cells = <0>; + reg = <30>; + }; + + dma1_clk: dma1_clk { + #clock-cells = <0>; + reg = <31>; + }; + + uhphs_clk: uhphs_clk { + #clock-cells = <0>; + reg = <32>; + }; + + udphs_clk: udphs_clk { + #clock-cells = <0>; + reg = <33>; + }; + + isi_clk: isi_clk { + #clock-cells = <0>; + reg = <37>; + }; + + ssc0_clk: ssc0_clk { + #clock-cells = <0>; + reg = <38>; + atmel,clk-output-range = <0 66000000>; + }; + + ssc1_clk: ssc1_clk { + #clock-cells = <0>; + reg = <39>; + atmel,clk-output-range = <0 66000000>; + }; + + sha_clk: sha_clk { + #clock-cells = <0>; + reg = <42>; + }; + + aes_clk: aes_clk { + #clock-cells = <0>; + reg = <43>; + }; + + tdes_clk: tdes_clk { + #clock-cells = <0>; + reg = <44>; + }; + + trng_clk: trng_clk { + #clock-cells = <0>; + reg = <45>; + }; + + fuse_clk: fuse_clk { + #clock-cells = <0>; + reg = <48>; + }; + }; }; rstc@fffffe00 { @@ -683,6 +1052,7 @@ compatible = "atmel,at91sam9260-pit"; reg = <0xfffffe30 0xf>; interrupts = <3 IRQ_TYPE_LEVEL_HIGH 5>; + clocks = <&mck>; }; watchdog@fffffe40 { @@ -705,6 +1075,8 @@ reg = <0x00500000 0x100000 0xf8030000 0x4000>; interrupts = <33 IRQ_TYPE_LEVEL_HIGH 2>; + clocks = <&udphs_clk>, <&utmi>; + clock-names = "pclk", "hclk"; status = "disabled"; ep0 { @@ -817,6 +1189,9 @@ compatible = "atmel,at91rm9200-ohci", "usb-ohci"; reg = <0x00600000 0x100000>; interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>; + clocks = <&usb>, <&uhphs_clk>, <&udphs_clk>, + <&uhpck>; + clock-names = "usb_clk", "ohci_clk", "hclk", "uhpck"; status = "disabled"; }; @@ -824,6 +1199,8 @@ compatible = "atmel,at91sam9g45-ehci", "usb-ehci"; reg = <0x00700000 0x100000>; interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>; + clocks = <&usb>, <&uhphs_clk>, <&uhpck>; + clock-names = "usb_clk", "ehci_clk", "uhpck"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sama5d3_can.dtsi b/arch/arm/boot/dts/sama5d3_can.dtsi index 8ed3260cef66..a0775851cce5 100644 --- a/arch/arm/boot/dts/sama5d3_can.dtsi +++ b/arch/arm/boot/dts/sama5d3_can.dtsi @@ -32,12 +32,30 @@ }; + pmc: pmc@fffffc00 { + periphck { + can0_clk: can0_clk { + #clock-cells = <0>; + reg = <40>; + atmel,clk-output-range = <0 66000000>; + }; + + can1_clk: can0_clk { + #clock-cells = <0>; + reg = <41>; + atmel,clk-output-range = <0 66000000>; + }; + }; + }; + can0: can@f000c000 { compatible = "atmel,at91sam9x5-can"; reg = <0xf000c000 0x300>; interrupts = <40 IRQ_TYPE_LEVEL_HIGH 3>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_can0_rx_tx>; + clocks = <&can0_clk>; + clock-names = "can_clk"; status = "disabled"; }; @@ -47,6 +65,8 @@ interrupts = <41 IRQ_TYPE_LEVEL_HIGH 3>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_can1_rx_tx>; + clocks = <&can1_clk>; + clock-names = "can_clk"; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/sama5d3_emac.dtsi b/arch/arm/boot/dts/sama5d3_emac.dtsi index 4d4f351f1f9f..fe2af9276312 100644 --- a/arch/arm/boot/dts/sama5d3_emac.dtsi +++ b/arch/arm/boot/dts/sama5d3_emac.dtsi @@ -31,12 +31,23 @@ }; }; + pmc: pmc@fffffc00 { + periphck { + macb1_clk: macb1_clk { + #clock-cells = <0>; + reg = <35>; + }; + }; + }; + macb1: ethernet@f802c000 { compatible = "cdns,at32ap7000-macb", "cdns,macb"; reg = <0xf802c000 0x100>; interrupts = <35 IRQ_TYPE_LEVEL_HIGH 3>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_macb1_rmii>; + clocks = <&macb1_clk>, <&macb1_clk>; + clock-names = "hclk", "pclk"; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/sama5d3_gmac.dtsi b/arch/arm/boot/dts/sama5d3_gmac.dtsi index 0ba8be30ccd8..a6cb0508762f 100644 --- a/arch/arm/boot/dts/sama5d3_gmac.dtsi +++ b/arch/arm/boot/dts/sama5d3_gmac.dtsi @@ -64,12 +64,23 @@ }; }; + pmc: pmc@fffffc00 { + periphck { + macb0_clk: macb0_clk { + #clock-cells = <0>; + reg = <34>; + }; + }; + }; + macb0: ethernet@f0028000 { compatible = "cdns,pc302-gem", "cdns,gem"; reg = <0xf0028000 0x100>; interrupts = <34 IRQ_TYPE_LEVEL_HIGH 3>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_macb0_data_rgmii &pinctrl_macb0_signal_rgmii>; + clocks = <&macb0_clk>, <&macb0_clk>; + clock-names = "hclk", "pclk"; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi index 01f52a79f8ba..85d302701565 100644 --- a/arch/arm/boot/dts/sama5d3_lcd.dtsi +++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi @@ -50,6 +50,23 @@ }; }; }; + + pmc: pmc@fffffc00 { + periphck { + lcdc_clk: lcdc_clk { + #clock-cells = <0>; + reg = <36>; + }; + }; + + systemck { + lcdck: lcdck { + #clock-cells = <0>; + reg = <3>; + clocks = <&mck>; + }; + }; + }; }; }; }; diff --git a/arch/arm/boot/dts/sama5d3_mci2.dtsi b/arch/arm/boot/dts/sama5d3_mci2.dtsi index 38e88e39e551..b029fe7ef17a 100644 --- a/arch/arm/boot/dts/sama5d3_mci2.dtsi +++ b/arch/arm/boot/dts/sama5d3_mci2.dtsi @@ -9,6 +9,7 @@ #include #include +#include / { ahb { @@ -30,6 +31,15 @@ }; }; + pmc: pmc@fffffc00 { + periphck { + mci2_clk: mci2_clk { + #clock-cells = <0>; + reg = <23>; + }; + }; + }; + mmc2: mmc@f8004000 { compatible = "atmel,hsmci"; reg = <0xf8004000 0x600>; @@ -38,6 +48,8 @@ dma-names = "rxtx"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mmc2_clk_cmd_dat0 &pinctrl_mmc2_dat1_3>; + clocks = <&mci2_clk>; + clock-names = "mci_clk"; status = "disabled"; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm/boot/dts/sama5d3_tcb1.dtsi b/arch/arm/boot/dts/sama5d3_tcb1.dtsi index 5264bb4a6998..382b04431f66 100644 --- a/arch/arm/boot/dts/sama5d3_tcb1.dtsi +++ b/arch/arm/boot/dts/sama5d3_tcb1.dtsi @@ -9,6 +9,7 @@ #include #include +#include / { aliases { @@ -17,10 +18,21 @@ ahb { apb { + pmc: pmc@fffffc00 { + periphck { + tcb1_clk: tcb1_clk { + #clock-cells = <0>; + reg = <27>; + }; + }; + }; + tcb1: timer@f8014000 { compatible = "atmel,at91sam9x5-tcb"; reg = <0xf8014000 0x100>; interrupts = <27 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&tcb1_clk>; + clock-names = "t0_clk"; }; }; }; diff --git a/arch/arm/boot/dts/sama5d3_uart.dtsi b/arch/arm/boot/dts/sama5d3_uart.dtsi index 98fcb2d57446..49d4d76ca6f4 100644 --- a/arch/arm/boot/dts/sama5d3_uart.dtsi +++ b/arch/arm/boot/dts/sama5d3_uart.dtsi @@ -9,6 +9,7 @@ #include #include +#include / { ahb { @@ -31,12 +32,30 @@ }; }; + pmc: pmc@fffffc00 { + periphck { + uart0_clk: uart0_clk { + #clock-cells = <0>; + reg = <16>; + atmel,clk-output-range = <0 66000000>; + }; + + uart1_clk: uart1_clk { + #clock-cells = <0>; + reg = <17>; + atmel,clk-output-range = <0 66000000>; + }; + }; + }; + uart0: serial@f0024000 { compatible = "atmel,at91sam9260-usart"; reg = <0xf0024000 0x200>; interrupts = <16 IRQ_TYPE_LEVEL_HIGH 5>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart0>; + clocks = <&uart0_clk>; + clock-names = "usart"; status = "disabled"; }; @@ -46,6 +65,8 @@ interrupts = <17 IRQ_TYPE_LEVEL_HIGH 5>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart1>; + clocks = <&uart1_clk>; + clock-names = "usart"; status = "disabled"; }; }; From 72163e5ab52b2d4e27743c7560aff11e971975b2 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 14:43:34 +0200 Subject: [PATCH 20/22] ARM: at91/dt: define sama5d3xek's main clk frequency Define the main clock frequency for the new main clock node in sama5d3xcm.dtsi. Signed-off-by: Boris BREZILLON Signed-off-by: Nicolas Ferre --- arch/arm/boot/dts/sama5d3xcm.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/boot/dts/sama5d3xcm.dtsi b/arch/arm/boot/dts/sama5d3xcm.dtsi index 726a0f35100c..dce5419f773c 100644 --- a/arch/arm/boot/dts/sama5d3xcm.dtsi +++ b/arch/arm/boot/dts/sama5d3xcm.dtsi @@ -38,6 +38,12 @@ macb0: ethernet@f0028000 { phy-mode = "rgmii"; }; + + pmc: pmc@fffffc00 { + main: mainck { + clock-frequency = <12000000>; + }; + }; }; nand0: nand@60000000 { From 8ab033112a39bae52d61fa942fa69c2fc03f40b0 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Oct 2013 14:43:35 +0200 Subject: [PATCH 21/22] ARM: at91: move sama5d3 SoC to common clk This patch removes the selection of AT91_USE_OLD_CLK when selecting sama5d3 SoC support. This will enable automatically enable COMMON_CLK_AT91 option and add support for at91 common clk implementation. Signed-off-by: Boris BREZILLON Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 97033f792eea..b4f7d6ffa30b 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -86,7 +86,6 @@ config SOC_SAMA5D3 select SOC_SAMA5 select HAVE_FB_ATMEL select HAVE_AT91_DBGU1 - select AT91_USE_OLD_CLK select HAVE_AT91_UTMI select HAVE_AT91_SMD select HAVE_AT91_USB_CLK From b46e837d8ef1f3c777bbf9513e2cdb5d87d6c374 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 2 Dec 2013 15:12:14 +0100 Subject: [PATCH 22/22] ARM: at91/dt: remove old clk material This patch removes the old main clk node which is now useless as sama5d3 SoCs and boards are no longer compatible with the old at91 clk implementations. It also remove old clock definitions (clock definitions using at91 old clk framework). Signed-off-by: Boris BREZILLON Signed-off-by: Nicolas Ferre --- arch/arm/boot/dts/sama5d3xcm.dtsi | 11 - arch/arm/mach-at91/sama5d3.c | 346 ------------------------------ 2 files changed, 357 deletions(-) diff --git a/arch/arm/boot/dts/sama5d3xcm.dtsi b/arch/arm/boot/dts/sama5d3xcm.dtsi index dce5419f773c..f55ed072c8e6 100644 --- a/arch/arm/boot/dts/sama5d3xcm.dtsi +++ b/arch/arm/boot/dts/sama5d3xcm.dtsi @@ -18,17 +18,6 @@ reg = <0x20000000 0x20000000>; }; - clocks { - #address-cells = <1>; - #size-cells = <1>; - ranges; - - main_clock: clock@0 { - compatible = "atmel,osc", "fixed-clock"; - clock-frequency = <12000000>; - }; - }; - ahb { apb { spi0: spi@f0004000 { diff --git a/arch/arm/mach-at91/sama5d3.c b/arch/arm/mach-at91/sama5d3.c index f3313b9d084a..3d775d08de08 100644 --- a/arch/arm/mach-at91/sama5d3.c +++ b/arch/arm/mach-at91/sama5d3.c @@ -21,349 +21,6 @@ #include "generic.h" #include "sam9_smc.h" -#if defined(CONFIG_OLD_CLK_AT91) -#include "clock.h" -/* -------------------------------------------------------------------- - * Clocks - * -------------------------------------------------------------------- */ - -/* - * The peripheral clocks. - */ - -static struct clk pioA_clk = { - .name = "pioA_clk", - .pid = SAMA5D3_ID_PIOA, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk pioB_clk = { - .name = "pioB_clk", - .pid = SAMA5D3_ID_PIOB, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk pioC_clk = { - .name = "pioC_clk", - .pid = SAMA5D3_ID_PIOC, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk pioD_clk = { - .name = "pioD_clk", - .pid = SAMA5D3_ID_PIOD, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk pioE_clk = { - .name = "pioE_clk", - .pid = SAMA5D3_ID_PIOE, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk usart0_clk = { - .name = "usart0_clk", - .pid = SAMA5D3_ID_USART0, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk usart1_clk = { - .name = "usart1_clk", - .pid = SAMA5D3_ID_USART1, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk usart2_clk = { - .name = "usart2_clk", - .pid = SAMA5D3_ID_USART2, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk usart3_clk = { - .name = "usart3_clk", - .pid = SAMA5D3_ID_USART3, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk uart0_clk = { - .name = "uart0_clk", - .pid = SAMA5D3_ID_UART0, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk uart1_clk = { - .name = "uart1_clk", - .pid = SAMA5D3_ID_UART1, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk twi0_clk = { - .name = "twi0_clk", - .pid = SAMA5D3_ID_TWI0, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk twi1_clk = { - .name = "twi1_clk", - .pid = SAMA5D3_ID_TWI1, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk twi2_clk = { - .name = "twi2_clk", - .pid = SAMA5D3_ID_TWI2, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk mmc0_clk = { - .name = "mci0_clk", - .pid = SAMA5D3_ID_HSMCI0, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk mmc1_clk = { - .name = "mci1_clk", - .pid = SAMA5D3_ID_HSMCI1, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk mmc2_clk = { - .name = "mci2_clk", - .pid = SAMA5D3_ID_HSMCI2, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk spi0_clk = { - .name = "spi0_clk", - .pid = SAMA5D3_ID_SPI0, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk spi1_clk = { - .name = "spi1_clk", - .pid = SAMA5D3_ID_SPI1, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk tcb0_clk = { - .name = "tcb0_clk", - .pid = SAMA5D3_ID_TC0, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk tcb1_clk = { - .name = "tcb1_clk", - .pid = SAMA5D3_ID_TC1, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk adc_clk = { - .name = "adc_clk", - .pid = SAMA5D3_ID_ADC, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk adc_op_clk = { - .name = "adc_op_clk", - .type = CLK_TYPE_PERIPHERAL, - .rate_hz = 5000000, -}; -static struct clk dma0_clk = { - .name = "dma0_clk", - .pid = SAMA5D3_ID_DMA0, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk dma1_clk = { - .name = "dma1_clk", - .pid = SAMA5D3_ID_DMA1, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk uhphs_clk = { - .name = "uhphs", - .pid = SAMA5D3_ID_UHPHS, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk udphs_clk = { - .name = "udphs_clk", - .pid = SAMA5D3_ID_UDPHS, - .type = CLK_TYPE_PERIPHERAL, -}; -/* gmac only for sama5d33, sama5d34, sama5d35 */ -static struct clk macb0_clk = { - .name = "macb0_clk", - .pid = SAMA5D3_ID_GMAC, - .type = CLK_TYPE_PERIPHERAL, -}; -/* emac only for sama5d31, sama5d35 */ -static struct clk macb1_clk = { - .name = "macb1_clk", - .pid = SAMA5D3_ID_EMAC, - .type = CLK_TYPE_PERIPHERAL, -}; -/* lcd only for sama5d31, sama5d33, sama5d34 */ -static struct clk lcdc_clk = { - .name = "lcdc_clk", - .pid = SAMA5D3_ID_LCDC, - .type = CLK_TYPE_PERIPHERAL, -}; -/* isi only for sama5d33, sama5d35 */ -static struct clk isi_clk = { - .name = "isi_clk", - .pid = SAMA5D3_ID_ISI, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk can0_clk = { - .name = "can0_clk", - .pid = SAMA5D3_ID_CAN0, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk can1_clk = { - .name = "can1_clk", - .pid = SAMA5D3_ID_CAN1, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk ssc0_clk = { - .name = "ssc0_clk", - .pid = SAMA5D3_ID_SSC0, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk ssc1_clk = { - .name = "ssc1_clk", - .pid = SAMA5D3_ID_SSC1, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV2, -}; -static struct clk sha_clk = { - .name = "sha_clk", - .pid = SAMA5D3_ID_SHA, - .type = CLK_TYPE_PERIPHERAL, - .div = AT91_PMC_PCR_DIV8, -}; -static struct clk aes_clk = { - .name = "aes_clk", - .pid = SAMA5D3_ID_AES, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk tdes_clk = { - .name = "tdes_clk", - .pid = SAMA5D3_ID_TDES, - .type = CLK_TYPE_PERIPHERAL, -}; - -static struct clk *periph_clocks[] __initdata = { - &pioA_clk, - &pioB_clk, - &pioC_clk, - &pioD_clk, - &pioE_clk, - &usart0_clk, - &usart1_clk, - &usart2_clk, - &usart3_clk, - &uart0_clk, - &uart1_clk, - &twi0_clk, - &twi1_clk, - &twi2_clk, - &mmc0_clk, - &mmc1_clk, - &mmc2_clk, - &spi0_clk, - &spi1_clk, - &tcb0_clk, - &tcb1_clk, - &adc_clk, - &adc_op_clk, - &dma0_clk, - &dma1_clk, - &uhphs_clk, - &udphs_clk, - &macb0_clk, - &macb1_clk, - &lcdc_clk, - &isi_clk, - &can0_clk, - &can1_clk, - &ssc0_clk, - &ssc1_clk, - &sha_clk, - &aes_clk, - &tdes_clk, -}; - -static struct clk pck0 = { - .name = "pck0", - .pmc_mask = AT91_PMC_PCK0, - .type = CLK_TYPE_PROGRAMMABLE, - .id = 0, -}; - -static struct clk pck1 = { - .name = "pck1", - .pmc_mask = AT91_PMC_PCK1, - .type = CLK_TYPE_PROGRAMMABLE, - .id = 1, -}; - -static struct clk pck2 = { - .name = "pck2", - .pmc_mask = AT91_PMC_PCK2, - .type = CLK_TYPE_PROGRAMMABLE, - .id = 2, -}; - -static struct clk_lookup periph_clocks_lookups[] = { - /* lookup table for DT entries */ - CLKDEV_CON_DEV_ID("usart", "ffffee00.serial", &mck), - CLKDEV_CON_DEV_ID(NULL, "fffff200.gpio", &pioA_clk), - CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioB_clk), - CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioC_clk), - CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioD_clk), - CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioE_clk), - CLKDEV_CON_DEV_ID("usart", "f001c000.serial", &usart0_clk), - CLKDEV_CON_DEV_ID("usart", "f0020000.serial", &usart1_clk), - CLKDEV_CON_DEV_ID("usart", "f8020000.serial", &usart2_clk), - CLKDEV_CON_DEV_ID("usart", "f8024000.serial", &usart3_clk), - CLKDEV_CON_DEV_ID(NULL, "f0014000.i2c", &twi0_clk), - CLKDEV_CON_DEV_ID(NULL, "f0018000.i2c", &twi1_clk), - CLKDEV_CON_DEV_ID(NULL, "f801c000.i2c", &twi2_clk), - CLKDEV_CON_DEV_ID("mci_clk", "f0000000.mmc", &mmc0_clk), - CLKDEV_CON_DEV_ID("mci_clk", "f8000000.mmc", &mmc1_clk), - CLKDEV_CON_DEV_ID("mci_clk", "f8004000.mmc", &mmc2_clk), - CLKDEV_CON_DEV_ID("spi_clk", "f0004000.spi", &spi0_clk), - CLKDEV_CON_DEV_ID("spi_clk", "f8008000.spi", &spi1_clk), - CLKDEV_CON_DEV_ID("t0_clk", "f0010000.timer", &tcb0_clk), - CLKDEV_CON_DEV_ID("t0_clk", "f8014000.timer", &tcb1_clk), - CLKDEV_CON_DEV_ID("tsc_clk", "f8018000.tsadcc", &adc_clk), - CLKDEV_CON_DEV_ID("dma_clk", "ffffe600.dma-controller", &dma0_clk), - CLKDEV_CON_DEV_ID("dma_clk", "ffffe800.dma-controller", &dma1_clk), - CLKDEV_CON_DEV_ID("hclk", "600000.ohci", &uhphs_clk), - CLKDEV_CON_DEV_ID("ohci_clk", "600000.ohci", &uhphs_clk), - CLKDEV_CON_DEV_ID("ehci_clk", "700000.ehci", &uhphs_clk), - CLKDEV_CON_DEV_ID("pclk", "500000.gadget", &udphs_clk), - CLKDEV_CON_DEV_ID("hclk", "500000.gadget", &utmi_clk), - CLKDEV_CON_DEV_ID("hclk", "f0028000.ethernet", &macb0_clk), - CLKDEV_CON_DEV_ID("pclk", "f0028000.ethernet", &macb0_clk), - CLKDEV_CON_DEV_ID("hclk", "f802c000.ethernet", &macb1_clk), - CLKDEV_CON_DEV_ID("pclk", "f802c000.ethernet", &macb1_clk), - CLKDEV_CON_DEV_ID("pclk", "f0008000.ssc", &ssc0_clk), - CLKDEV_CON_DEV_ID("pclk", "f000c000.ssc", &ssc1_clk), - CLKDEV_CON_DEV_ID("can_clk", "f000c000.can", &can0_clk), - CLKDEV_CON_DEV_ID("can_clk", "f8010000.can", &can1_clk), - CLKDEV_CON_DEV_ID("sha_clk", "f8034000.sha", &sha_clk), - CLKDEV_CON_DEV_ID("aes_clk", "f8038000.aes", &aes_clk), - CLKDEV_CON_DEV_ID("tdes_clk", "f803c000.tdes", &tdes_clk), -}; - -static void __init sama5d3_register_clocks(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(periph_clocks); i++) - clk_register(periph_clocks[i]); - - clkdev_add_table(periph_clocks_lookups, - ARRAY_SIZE(periph_clocks_lookups)); - - clk_register(&pck0); - clk_register(&pck1); - clk_register(&pck2); -} -#endif - /* -------------------------------------------------------------------- * AT91SAM9x5 processor initialization * -------------------------------------------------------------------- */ @@ -380,8 +37,5 @@ static void __init sama5d3_initialize(void) AT91_SOC_START(sama5d3) .map_io = sama5d3_map_io, -#if defined(CONFIG_OLD_CLK_AT91) - .register_clocks = sama5d3_register_clocks, -#endif .init = sama5d3_initialize, AT91_SOC_END