[U-Boot] [PATCH v5] clk: at91: Add clock driver

The patch is referred to at91 clock driver of Linux, to make the clock node descriptions in DT aligned with the Linux's.
Signed-off-by: Wenyou Yang wenyou.yang@atmel.com Reviewed-by: Simon Glass sjg@chromium.org ---
Changes in v5: - Change clk_client.h -> clk.h to adapt to clk API conversion. - Fix missing semicolon and clk->dev in clk-generated.c. - Make clock options selectable via menuconfig.
Changes in v4: - Add Reviewed-by tag. - Add more information in Kconfig help. - Use u32 for num_parents variable, not u8. - Change the check return from clk_get_rate(). - Remove return -ENODEV line, use return ret. - Improve the comments in at91_system_clk_enable().
Changes in v3: - Update based on [PATCH] clk: convert API to match reset/mailbox style (http://patchwork.ozlabs.org/patch/625342/). - Remove [PATCH] clk: clk-uclass: Add post binding for CLK uclass, add bind() method to bind the clk node without compatible. - Add help for Kconfig HAVE_AT91_XX option. - Add ofdata_to_platdata() method for generated clock driver to handle the device tree. - Use setbits_le32() to replace readl()/writel(). - Fixed the return value, -ENODEV->-EINVAL. - Use dev_get_addr_ptr() to replace dev_get_addr(). - Remove check on dev_get_parent() return.
Changes in v2: - Remove the redundant log print.
arch/arm/mach-at91/include/mach/at91_pmc.h | 11 +- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/at91/Kconfig | 43 ++++++++ drivers/clk/at91/Makefile | 11 ++ drivers/clk/at91/clk-generated.c | 162 +++++++++++++++++++++++++++++ drivers/clk/at91/clk-h32mx.c | 56 ++++++++++ drivers/clk/at91/clk-main.c | 55 ++++++++++ drivers/clk/at91/clk-master.c | 33 ++++++ drivers/clk/at91/clk-peripheral.c | 60 +++++++++++ drivers/clk/at91/clk-plla.c | 55 ++++++++++ drivers/clk/at91/clk-slow.c | 37 +++++++ drivers/clk/at91/clk-system.c | 76 ++++++++++++++ drivers/clk/at91/clk-utmi.c | 67 ++++++++++++ drivers/clk/at91/pmc.c | 71 +++++++++++++ drivers/clk/at91/pmc.h | 18 ++++ drivers/clk/at91/sckc.c | 30 ++++++ 17 files changed, 784 insertions(+), 3 deletions(-) create mode 100644 drivers/clk/at91/Kconfig create mode 100644 drivers/clk/at91/Makefile create mode 100644 drivers/clk/at91/clk-generated.c create mode 100644 drivers/clk/at91/clk-h32mx.c create mode 100644 drivers/clk/at91/clk-main.c create mode 100644 drivers/clk/at91/clk-master.c create mode 100644 drivers/clk/at91/clk-peripheral.c create mode 100644 drivers/clk/at91/clk-plla.c create mode 100644 drivers/clk/at91/clk-slow.c create mode 100644 drivers/clk/at91/clk-system.c create mode 100644 drivers/clk/at91/clk-utmi.c create mode 100644 drivers/clk/at91/pmc.c create mode 100644 drivers/clk/at91/pmc.h create mode 100644 drivers/clk/at91/sckc.c
diff --git a/arch/arm/mach-at91/include/mach/at91_pmc.h b/arch/arm/mach-at91/include/mach/at91_pmc.h index 680ceb0..2875ff2 100644 --- a/arch/arm/mach-at91/include/mach/at91_pmc.h +++ b/arch/arm/mach-at91/include/mach/at91_pmc.h @@ -149,6 +149,9 @@ typedef struct at91_pmc {
#define AT91_PMC_PCR_PID_MASK (0x3f) #define AT91_PMC_PCR_GCKCSS (0x7 << 8) +#define AT91_PMC_PCR_GCKCSS_MASK 0x07 +#define AT91_PMC_PCR_GCKCSS_OFFSET 8 +#define AT91_PMC_PCR_GCKCSS_(x) ((x & 0x07) << 8) #define AT91_PMC_PCR_GCKCSS_SLOW_CLK (0x0 << 8) #define AT91_PMC_PCR_GCKCSS_MAIN_CLK (0x1 << 8) #define AT91_PMC_PCR_GCKCSS_PLLA_CLK (0x2 << 8) @@ -158,8 +161,9 @@ typedef struct at91_pmc { #define AT91_PMC_PCR_CMD_WRITE (0x1 << 12) #define AT91_PMC_PCR_DIV (0x3 << 16) #define AT91_PMC_PCR_GCKDIV (0xff << 20) -#define AT91_PMC_PCR_GCKDIV_(x) (((x) & 0xff) << 20) -#define AT91_PMC_PCR_GCKDIV_OFFSET 20 +#define AT91_PMC_PCR_GCKDIV_MASK 0xff +#define AT91_PMC_PCR_GCKDIV_OFFSET 20 +#define AT91_PMC_PCR_GCKDIV_(x) ((x & 0xff) << 20) #define AT91_PMC_PCR_EN (0x1 << 28) #define AT91_PMC_PCR_GCKEN (0x1 << 29)
@@ -243,8 +247,9 @@ typedef struct at91_pmc { #define AT91_PMC_PCK1RDY (1 << 9) /* Programmable Clock 1 */ #define AT91_PMC_PCK2RDY (1 << 10) /* Programmable Clock 2 */ #define AT91_PMC_PCK3RDY (1 << 11) /* Programmable Clock 3 */ +#define AT91_PMC_MOSCSELS BIT(16) /* Main Oscillator Selection Status */ +#define AT91_PMC_MOSCRCS BIT(17) /* 12 MHz RC Oscillator Status */ #define AT91_PMC_GCKRDY (1 << 24) - #define AT91_PMC_PROTKEY 0x504d4301 /* Activation Code */
/* PLL Charge Pump Current Register (PMC_PLLICPR) */ diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6eee8eb..3da63c0 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -22,5 +22,6 @@ config SPL_CLK
source "drivers/clk/uniphier/Kconfig" source "drivers/clk/exynos/Kconfig" +source "drivers/clk/at91/Kconfig"
endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f7a8891..863a8d7 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o obj-$(CONFIG_MACH_PIC32) += clk_pic32.o obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_CLK_EXYNOS) += exynos/ +obj-$(CONFIG_CLK_AT91) += at91/ diff --git a/drivers/clk/at91/Kconfig b/drivers/clk/at91/Kconfig new file mode 100644 index 0000000..10050d8 --- /dev/null +++ b/drivers/clk/at91/Kconfig @@ -0,0 +1,43 @@ +config CLK_AT91 + bool "AT91 clock drivers" + depends on CLK + help + This option is used to enable the AT91 clock driver. + The driver supports the AT91 clock generator, including + the oscillators and PLLs, such as main clock, slow clock, + PLLA, UTMI PLL. Clocks can also be a source clock of other + clocks a tree structure, such as master clock, usb device + clock, matrix clock and generic clock. + Devices can use a common clock API to request a particular + clock, enable it and get its rate. + +config AT91_UTMI + bool "Support UTMI PLL Clock" + depends on CLK_AT91 + help + This option is used to enable the AT91 UTMI PLL clock + driver. It is the clock provider of USB, and UPLLCK is the + output of 480 MHz UTMI PLL, The souce clock of the UTMI + PLL is the main clock, so the main clock must select the + fast crystal oscillator to meet the frequency accuracy + required by USB. + +config AT91_H32MX + bool "Support H32MX 32-bit Matrix Clock" + depends on CLK_AT91 + help + This option is used to enable the AT91 H32MX matrixes + clock driver. There are H64MX and H32MX matrixes clocks, + H64MX 64-bit matrix clocks are MCK. The H32MX 32-bit + matrix clock is to be configured as MCK if MCK does not + exceed 83 MHz, else it is to be configured as MCK/2. + +config AT91_GENERIC_CLK + bool "Support Generic Clock" + depends on CLK_AT91 + help + This option is used to enable the AT91 generic clock + driver. Some peripherals may need a second clock source + that may be different from the system clock. This second + clock is the generic clock (GCLK) and is managed by + the PMC via PMC_PCR register. diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile new file mode 100644 index 0000000..fbe3cb6 --- /dev/null +++ b/drivers/clk/at91/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for at91 specific clk +# + +obj-y += pmc.o sckc.o +obj-y += clk-slow.o clk-main.o clk-plla.o clk-master.o +obj-y += clk-system.o clk-peripheral.o + +obj-$(CONFIG_AT91_UTMI) += clk-utmi.o +obj-$(CONFIG_AT91_H32MX) += clk-h32mx.o +obj-$(CONFIG_AT91_GENERIC_CLK) += clk-generated.o diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c new file mode 100644 index 0000000..f6164cc --- /dev/null +++ b/drivers/clk/at91/clk-generated.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define GENERATED_SOURCE_MAX 6 +#define GENERATED_MAX_DIV 255 + +struct generated_clk_priv { + u32 num_parents; +}; + +static ulong generated_clk_get_rate(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct clk parent; + u32 tmp, gckdiv; + u8 parent_id; + int ret; + + writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); + tmp = readl(&pmc->pcr); + parent_id = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) & + AT91_PMC_PCR_GCKCSS_MASK; + gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK; + + ret = clk_get_by_index(clk->dev, parent_id, &parent); + if (ret) + return 0; + + return clk_get_rate(&parent) / (gckdiv + 1); +} + +static ulong generated_clk_set_rate(struct clk *clk, ulong rate) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct generated_clk_priv *priv = dev_get_priv(clk->dev); + struct clk parent, best_parent; + ulong tmp_rate, best_rate = rate, parent_rate; + int tmp_diff, best_diff = -1; + u32 div, best_div = 0; + u8 best_parent_id = 0; + u8 i; + u32 tmp; + int ret; + + for (i = 0; i < priv->num_parents; i++) { + ret = clk_get_by_index(clk->dev, i, &parent); + if (ret) + return ret; + + parent_rate = clk_get_rate(&parent); + if (IS_ERR_VALUE(parent_rate)) + return parent_rate; + + for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { + tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); + if (rate < tmp_rate) + continue; + tmp_diff = rate - tmp_rate; + + if (best_diff < 0 || best_diff > tmp_diff) { + best_rate = tmp_rate; + best_diff = tmp_diff; + + best_div = div - 1; + best_parent = parent; + best_parent_id = i; + } + + if (!best_diff || tmp_rate < rate) + break; + } + + if (!best_diff) + break; + } + + debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n", + best_parent.dev->name, best_rate, best_div); + + ret = clk_enable(&best_parent); + if (ret) + return ret; + + writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); + tmp = readl(&pmc->pcr); + tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS); + tmp |= AT91_PMC_PCR_GCKCSS_(best_parent_id) | + AT91_PMC_PCR_CMD_WRITE | + AT91_PMC_PCR_GCKDIV_(best_div) | + AT91_PMC_PCR_GCKEN; + writel(tmp, &pmc->pcr); + + while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY)) + ; + + return 0; +} + +static struct clk_ops generated_clk_ops = { + .get_rate = generated_clk_get_rate, + .set_rate = generated_clk_set_rate, +}; + +static int generated_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct generated_clk_priv *priv = dev_get_priv(dev); + u32 cells[GENERATED_SOURCE_MAX]; + u32 num_parents; + + num_parents = fdtdec_get_int_array_count(gd->fdt_blob, dev->of_offset, + "clocks", cells, + GENERATED_SOURCE_MAX); + + if (!num_parents) + return -1; + + priv->num_parents = num_parents; + + return 0; +} + +static int generated_clk_bind(struct udevice *dev) +{ + return at91_pmc_clk_node_bind(dev); +} + +static int generated_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id generated_clk_match[] = { + { .compatible = "atmel,sama5d2-clk-generated" }, + {} +}; + +U_BOOT_DRIVER(generated_clk) = { + .name = "generated-clk", + .id = UCLASS_CLK, + .of_match = generated_clk_match, + .bind = generated_clk_bind, + .probe = generated_clk_probe, + .ofdata_to_platdata = generated_clk_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct generated_clk_priv), + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &generated_clk_ops, +}; diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c new file mode 100644 index 0000000..1a304ba --- /dev/null +++ b/drivers/clk/at91/clk-h32mx.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/util.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define H32MX_MAX_FREQ 90000000 + +static ulong sama5d4_h32mx_clk_get_rate(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + ulong rate = gd->arch.mck_rate_hz; + + if (readl(&pmc->mckr) & AT91_PMC_MCKR_H32MXDIV) + rate /= 2; + + if (rate > H32MX_MAX_FREQ) + dm_warn("H32MX clock is too fast\n"); + + return rate; +} + +static struct clk_ops sama5d4_h32mx_clk_ops = { + .get_rate = sama5d4_h32mx_clk_get_rate, +}; + +static int sama5d4_h32mx_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id sama5d4_h32mx_clk_match[] = { + { .compatible = "atmel,sama5d4-clk-h32mx" }, + {} +}; + +U_BOOT_DRIVER(sama5d4_h32mx_clk) = { + .name = "sama5d4-h32mx-clk", + .id = UCLASS_CLK, + .of_match = sama5d4_h32mx_clk_match, + .probe = sama5d4_h32mx_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &sama5d4_h32mx_clk_ops, +}; diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c new file mode 100644 index 0000000..252d076 --- /dev/null +++ b/drivers/clk/at91/clk-main.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h" + +DECLARE_GLOBAL_DATA_PTR; + +static int main_osc_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + + if (readl(&pmc->sr) & AT91_PMC_MOSCSELS) + return 0; + + return -EINVAL; +} + +static ulong main_osc_clk_get_rate(struct clk *clk) +{ + return gd->arch.main_clk_rate_hz; +} + +static struct clk_ops main_osc_clk_ops = { + .enable = main_osc_clk_enable, + .get_rate = main_osc_clk_get_rate, +}; + +static int main_osc_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id main_osc_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-main" }, + {} +}; + +U_BOOT_DRIVER(at91sam9x5_main_osc_clk) = { + .name = "at91sam9x5-main-osc-clk", + .id = UCLASS_CLK, + .of_match = main_osc_clk_match, + .probe = main_osc_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &main_osc_clk_ops, +}; diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c new file mode 100644 index 0000000..284b248 --- /dev/null +++ b/drivers/clk/at91/clk-master.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> + +DECLARE_GLOBAL_DATA_PTR; + +static ulong at91_master_clk_get_rate(struct clk *clk) +{ + return gd->arch.mck_rate_hz; +} + +static struct clk_ops at91_master_clk_ops = { + .get_rate = at91_master_clk_get_rate, +}; + +static const struct udevice_id at91_master_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-master" }, + {} +}; + +U_BOOT_DRIVER(at91_master_clk) = { + .name = "at91-master-clk", + .id = UCLASS_CLK, + .of_match = at91_master_clk_match, + .ops = &at91_master_clk_ops, +}; diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c new file mode 100644 index 0000000..16688e9 --- /dev/null +++ b/drivers/clk/at91/clk-peripheral.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h" + +#define PERIPHERAL_ID_MIN 2 +#define PERIPHERAL_ID_MAX 31 +#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) + +static int sam9x5_periph_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + + if (clk->id < PERIPHERAL_ID_MIN) + return -1; + + writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); + setbits_le32(&pmc->pcr, AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN); + + return 0; +} + +static struct clk_ops sam9x5_periph_clk_ops = { + .enable = sam9x5_periph_clk_enable, +}; + +static int sam9x5_periph_clk_bind(struct udevice *dev) +{ + return at91_pmc_clk_node_bind(dev); +} + +static int sam9x5_periph_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id sam9x5_periph_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-peripheral" }, + {} +}; + +U_BOOT_DRIVER(sam9x5_periph_clk) = { + .name = "sam9x5-periph-clk", + .id = UCLASS_CLK, + .of_match = sam9x5_periph_clk_match, + .bind = sam9x5_periph_clk_bind, + .probe = sam9x5_periph_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &sam9x5_periph_clk_ops, +}; diff --git a/drivers/clk/at91/clk-plla.c b/drivers/clk/at91/clk-plla.c new file mode 100644 index 0000000..2a71399 --- /dev/null +++ b/drivers/clk/at91/clk-plla.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h" + +DECLARE_GLOBAL_DATA_PTR; + +static int plla_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + + if (readl(&pmc->sr) & AT91_PMC_LOCKA) + return 0; + + return -EINVAL; +} + +static ulong plla_clk_get_rate(struct clk *clk) +{ + return gd->arch.plla_rate_hz; +} + +static struct clk_ops plla_clk_ops = { + .enable = plla_clk_enable, + .get_rate = plla_clk_get_rate, +}; + +static int plla_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id plla_clk_match[] = { + { .compatible = "atmel,sama5d3-clk-pll" }, + {} +}; + +U_BOOT_DRIVER(at91_plla_clk) = { + .name = "at91-plla-clk", + .id = UCLASS_CLK, + .of_match = plla_clk_match, + .probe = plla_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &plla_clk_ops, +}; diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c new file mode 100644 index 0000000..f7666b4 --- /dev/null +++ b/drivers/clk/at91/clk-slow.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> + +static int at91_slow_clk_enable(struct clk *clk) +{ + return 0; +} + +static ulong at91_slow_clk_get_rate(struct clk *clk) +{ + return CONFIG_SYS_AT91_SLOW_CLOCK; +} + +static struct clk_ops at91_slow_clk_ops = { + .enable = at91_slow_clk_enable, + .get_rate = at91_slow_clk_get_rate, +}; + +static const struct udevice_id at91_slow_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-slow" }, + {} +}; + +U_BOOT_DRIVER(at91_slow_clk) = { + .name = "at91-slow-clk", + .id = UCLASS_CLK, + .of_match = at91_slow_clk_match, + .ops = &at91_slow_clk_ops, +}; diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c new file mode 100644 index 0000000..fa80bad --- /dev/null +++ b/drivers/clk/at91/clk-system.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h" + +#define SYSTEM_MAX_ID 31 + +static inline int is_pck(int id) +{ + return (id >= 8) && (id <= 15); +} + +static int at91_system_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + u32 mask; + + if (clk->id > SYSTEM_MAX_ID) + return -EINVAL; + + mask = BIT(clk->id); + + writel(mask, &pmc->scer); + + /** + * For the programmable clocks the Ready status in the PMC + * status register should be checked after enabling. + * For other clocks this is unnecessary. + */ + if (!is_pck(clk->id)) + return 0; + + while (!(readl(&pmc->sr) & mask)) + ; + + return 0; +} + +static struct clk_ops at91_system_clk_ops = { + .enable = at91_system_clk_enable, +}; + +static int at91_system_clk_bind(struct udevice *dev) +{ + return at91_pmc_clk_node_bind(dev); +} + +static int at91_system_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id at91_system_clk_match[] = { + { .compatible = "atmel,at91rm9200-clk-system" }, + {} +}; + +U_BOOT_DRIVER(at91_system_clk) = { + .name = "at91-system-clk", + .id = UCLASS_CLK, + .of_match = at91_system_clk_match, + .bind = at91_system_clk_bind, + .probe = at91_system_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &at91_system_clk_ops, +}; diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c new file mode 100644 index 0000000..369a687 --- /dev/null +++ b/drivers/clk/at91/clk-utmi.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define UTMI_FIXED_MUL 40 + +static int utmi_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + u32 tmp; + + if (readl(&pmc->sr) & AT91_PMC_LOCKU) + return 0; + + tmp = readl(&pmc->uckr); + tmp |= AT91_PMC_UPLLEN | + AT91_PMC_UPLLCOUNT | + AT91_PMC_BIASEN; + writel(tmp, &pmc->uckr); + + while (!(readl(&pmc->sr) & AT91_PMC_LOCKU)) + ; + + return 0; +} + +static ulong utmi_clk_get_rate(struct clk *clk) +{ + return gd->arch.main_clk_rate_hz * UTMI_FIXED_MUL; +} + +static struct clk_ops utmi_clk_ops = { + .enable = utmi_clk_enable, + .get_rate = utmi_clk_get_rate, +}; + +static int utmi_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id utmi_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-utmi" }, + {} +}; + +U_BOOT_DRIVER(at91sam9x5_utmi_clk) = { + .name = "at91sam9x5-utmi-clk", + .id = UCLASS_CLK, + .of_match = utmi_clk_match, + .probe = utmi_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &utmi_clk_ops, +}; diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c new file mode 100644 index 0000000..a08d7e8 --- /dev/null +++ b/drivers/clk/at91/pmc.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/lists.h> +#include <dm/root.h> +#include "pmc.h" + +DECLARE_GLOBAL_DATA_PTR; + +static int at91_pmc_bind(struct udevice *dev) +{ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +static const struct udevice_id at91_pmc_match[] = { + { .compatible = "atmel,sama5d2-pmc" }, + {} +}; + +U_BOOT_DRIVER(at91_pmc) = { + .name = "at91-pmc-core", + .id = UCLASS_CLK, + .of_match = at91_pmc_match, + .bind = at91_pmc_bind, +}; + +int at91_pmc_core_probe(struct udevice *dev) +{ + struct pmc_platdata *plat = dev_get_platdata(dev); + + dev = dev_get_parent(dev); + + plat->reg_base = (struct at91_pmc *)dev_get_addr_ptr(dev); + + return 0; +} + +int at91_pmc_clk_node_bind(struct udevice *dev) +{ + const void *fdt = gd->fdt_blob; + int offset = dev->of_offset; + const char *name; + int ret; + + for (offset = fdt_first_subnode(fdt, offset); + offset > 0; + offset = fdt_next_subnode(fdt, offset)) { + name = fdt_get_name(fdt, offset, NULL); + if (!name) + return -EINVAL; + + ret = device_bind_driver_to_node(dev, "clk", name, + offset, NULL); + if (ret) + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(clk_generic) = { + .id = UCLASS_CLK, + .name = "clk", +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h new file mode 100644 index 0000000..5444c84 --- /dev/null +++ b/drivers/clk/at91/pmc.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __AT91_PMC_H__ +#define __AT91_PMC_H__ + +struct pmc_platdata { + struct at91_pmc *reg_base; +}; + +int at91_pmc_core_probe(struct udevice *dev); +int at91_pmc_clk_node_bind(struct udevice *dev); + +#endif diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c new file mode 100644 index 0000000..b207611 --- /dev/null +++ b/drivers/clk/at91/sckc.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Atmel Corporation + * Wenyou.Yang wenyou.yang@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/root.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int at91_sckc_clk_bind(struct udevice *dev) +{ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +static const struct udevice_id at91_sckc_clk_match[] = { + { .compatible = "atmel,at91sam9x5-sckc" }, + {} +}; + +U_BOOT_DRIVER(at91_sckc_clk) = { + .name = "at91_sckc_clk", + .id = UCLASS_CLK, + .of_match = at91_sckc_clk_match, + .bind = at91_sckc_clk_bind, +};

Hi Andreas,
Do you have some comments?
Best Regards, Wenyou Yang
-----Original Message----- From: Wenyou Yang [mailto:wenyou.yang@atmel.com] Sent: 2016年7月20日 17:55 To: U-Boot Mailing List u-boot@lists.denx.de Cc: Stephen Warren swarren@nvidia.com; Andreas Bießmann andreas@biessmann.org; Simon Glass sjg@chromium.org; Wenyou Yang wenyou.yang@atmel.com Subject: [PATCH v5] clk: at91: Add clock driver
The patch is referred to at91 clock driver of Linux, to make the clock node descriptions in DT aligned with the Linux's.
Signed-off-by: Wenyou Yang wenyou.yang@atmel.com Reviewed-by: Simon Glass sjg@chromium.org
Changes in v5:
- Change clk_client.h -> clk.h to adapt to clk API conversion.
- Fix missing semicolon and clk->dev in clk-generated.c.
- Make clock options selectable via menuconfig.
Changes in v4:
- Add Reviewed-by tag.
- Add more information in Kconfig help.
- Use u32 for num_parents variable, not u8.
- Change the check return from clk_get_rate().
- Remove return -ENODEV line, use return ret.
- Improve the comments in at91_system_clk_enable().
Changes in v3:
- Update based on [PATCH] clk: convert API to match reset/mailbox style (http://patchwork.ozlabs.org/patch/625342/).
- Remove [PATCH] clk: clk-uclass: Add post binding for CLK uclass, add bind() method to bind the clk node without compatible.
- Add help for Kconfig HAVE_AT91_XX option.
- Add ofdata_to_platdata() method for generated clock driver to handle the device tree.
- Use setbits_le32() to replace readl()/writel().
- Fixed the return value, -ENODEV->-EINVAL.
- Use dev_get_addr_ptr() to replace dev_get_addr().
- Remove check on dev_get_parent() return.
Changes in v2:
- Remove the redundant log print.
arch/arm/mach-at91/include/mach/at91_pmc.h | 11 +- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/at91/Kconfig | 43 ++++++++ drivers/clk/at91/Makefile | 11 ++ drivers/clk/at91/clk-generated.c | 162 +++++++++++++++++++++++++++++ drivers/clk/at91/clk-h32mx.c | 56 ++++++++++ drivers/clk/at91/clk-main.c | 55 ++++++++++ drivers/clk/at91/clk-master.c | 33 ++++++ drivers/clk/at91/clk-peripheral.c | 60 +++++++++++ drivers/clk/at91/clk-plla.c | 55 ++++++++++ drivers/clk/at91/clk-slow.c | 37 +++++++ drivers/clk/at91/clk-system.c | 76 ++++++++++++++ drivers/clk/at91/clk-utmi.c | 67 ++++++++++++ drivers/clk/at91/pmc.c | 71 +++++++++++++ drivers/clk/at91/pmc.h | 18 ++++ drivers/clk/at91/sckc.c | 30 ++++++ 17 files changed, 784 insertions(+), 3 deletions(-) create mode 100644 drivers/clk/at91/Kconfig create mode 100644 drivers/clk/at91/Makefile create mode 100644 drivers/clk/at91/clk-generated.c create mode 100644 drivers/clk/at91/clk-h32mx.c create mode 100644 drivers/clk/at91/clk-main.c create mode 100644 drivers/clk/at91/clk-master.c create mode 100644 drivers/clk/at91/clk-peripheral.c create mode 100644 drivers/clk/at91/clk-plla.c create mode 100644 drivers/clk/at91/clk-slow.c create mode 100644 drivers/clk/at91/clk-system.c create mode 100644 drivers/clk/at91/clk-utmi.c create mode 100644 drivers/clk/at91/pmc.c create mode 100644 drivers/clk/at91/pmc.h create mode 100644 drivers/clk/at91/sckc.c
diff --git a/arch/arm/mach-at91/include/mach/at91_pmc.h b/arch/arm/mach- at91/include/mach/at91_pmc.h index 680ceb0..2875ff2 100644 --- a/arch/arm/mach-at91/include/mach/at91_pmc.h +++ b/arch/arm/mach-at91/include/mach/at91_pmc.h @@ -149,6 +149,9 @@ typedef struct at91_pmc {
#define AT91_PMC_PCR_PID_MASK (0x3f) #define AT91_PMC_PCR_GCKCSS (0x7 << 8) +#define AT91_PMC_PCR_GCKCSS_MASK 0x07 +#define AT91_PMC_PCR_GCKCSS_OFFSET 8 +#define AT91_PMC_PCR_GCKCSS_(x) ((x & 0x07) << 8) #define AT91_PMC_PCR_GCKCSS_SLOW_CLK (0x0 << 8) #define AT91_PMC_PCR_GCKCSS_MAIN_CLK (0x1 << 8) #define AT91_PMC_PCR_GCKCSS_PLLA_CLK (0x2 << 8) @@ -158,8 +161,9 @@ typedef struct at91_pmc { #define AT91_PMC_PCR_CMD_WRITE (0x1 << 12) #define AT91_PMC_PCR_DIV (0x3 << 16) #define AT91_PMC_PCR_GCKDIV (0xff << 20) -#define AT91_PMC_PCR_GCKDIV_(x) (((x) & 0xff) << 20) -#define AT91_PMC_PCR_GCKDIV_OFFSET 20 +#define AT91_PMC_PCR_GCKDIV_MASK 0xff +#define AT91_PMC_PCR_GCKDIV_OFFSET 20 +#define AT91_PMC_PCR_GCKDIV_(x) ((x & 0xff) << 20) #define AT91_PMC_PCR_EN (0x1 << 28) #define AT91_PMC_PCR_GCKEN (0x1 << 29)
@@ -243,8 +247,9 @@ typedef struct at91_pmc { #define AT91_PMC_PCK1RDY (1 << 9) /* Programmable Clock 1 */ #define AT91_PMC_PCK2RDY (1 << 10) /* Programmable Clock 2 */ #define AT91_PMC_PCK3RDY (1 << 11) /* Programmable Clock 3 */ +#define AT91_PMC_MOSCSELS BIT(16) /* Main Oscillator Selection Status */ +#define AT91_PMC_MOSCRCS BIT(17) /* 12 MHz RC Oscillator Status */ #define AT91_PMC_GCKRDY (1 << 24)
#define AT91_PMC_PROTKEY 0x504d4301 /* Activation Code */
/* PLL Charge Pump Current Register (PMC_PLLICPR) */ diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6eee8eb..3da63c0 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -22,5 +22,6 @@ config SPL_CLK
source "drivers/clk/uniphier/Kconfig" source "drivers/clk/exynos/Kconfig" +source "drivers/clk/at91/Kconfig"
endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f7a8891..863a8d7 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o obj-$(CONFIG_MACH_PIC32) += clk_pic32.o obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_CLK_EXYNOS) += exynos/ +obj-$(CONFIG_CLK_AT91) += at91/ diff --git a/drivers/clk/at91/Kconfig b/drivers/clk/at91/Kconfig new file mode 100644 index 0000000..10050d8 --- /dev/null +++ b/drivers/clk/at91/Kconfig @@ -0,0 +1,43 @@ +config CLK_AT91
- bool "AT91 clock drivers"
- depends on CLK
- help
This option is used to enable the AT91 clock driver.
The driver supports the AT91 clock generator, including
the oscillators and PLLs, such as main clock, slow clock,
PLLA, UTMI PLL. Clocks can also be a source clock of other
clocks a tree structure, such as master clock, usb device
clock, matrix clock and generic clock.
Devices can use a common clock API to request a particular
clock, enable it and get its rate.
+config AT91_UTMI
- bool "Support UTMI PLL Clock"
- depends on CLK_AT91
- help
This option is used to enable the AT91 UTMI PLL clock
driver. It is the clock provider of USB, and UPLLCK is the
output of 480 MHz UTMI PLL, The souce clock of the UTMI
PLL is the main clock, so the main clock must select the
fast crystal oscillator to meet the frequency accuracy
required by USB.
+config AT91_H32MX
- bool "Support H32MX 32-bit Matrix Clock"
- depends on CLK_AT91
- help
This option is used to enable the AT91 H32MX matrixes
clock driver. There are H64MX and H32MX matrixes clocks,
H64MX 64-bit matrix clocks are MCK. The H32MX 32-bit
matrix clock is to be configured as MCK if MCK does not
exceed 83 MHz, else it is to be configured as MCK/2.
+config AT91_GENERIC_CLK
- bool "Support Generic Clock"
- depends on CLK_AT91
- help
This option is used to enable the AT91 generic clock
driver. Some peripherals may need a second clock source
that may be different from the system clock. This second
clock is the generic clock (GCLK) and is managed by
the PMC via PMC_PCR register.
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile new file mode 100644 index 0000000..fbe3cb6 --- /dev/null +++ b/drivers/clk/at91/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for at91 specific clk +#
+obj-y += pmc.o sckc.o +obj-y += clk-slow.o clk-main.o clk-plla.o clk-master.o obj-y += +clk-system.o clk-peripheral.o
+obj-$(CONFIG_AT91_UTMI) += clk-utmi.o +obj-$(CONFIG_AT91_H32MX) += clk-h32mx.o +obj-$(CONFIG_AT91_GENERIC_CLK) += clk-generated.o diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c new file mode 100644 index 0000000..f6164cc --- /dev/null +++ b/drivers/clk/at91/clk-generated.c @@ -0,0 +1,162 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h"
+DECLARE_GLOBAL_DATA_PTR;
+#define GENERATED_SOURCE_MAX 6 +#define GENERATED_MAX_DIV 255
+struct generated_clk_priv {
- u32 num_parents;
+};
+static ulong generated_clk_get_rate(struct clk *clk) {
- struct pmc_platdata *plat = dev_get_platdata(clk->dev);
- struct at91_pmc *pmc = plat->reg_base;
- struct clk parent;
- u32 tmp, gckdiv;
- u8 parent_id;
- int ret;
- writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
- tmp = readl(&pmc->pcr);
- parent_id = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) &
AT91_PMC_PCR_GCKCSS_MASK;
- gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) &
+AT91_PMC_PCR_GCKDIV_MASK;
- ret = clk_get_by_index(clk->dev, parent_id, &parent);
- if (ret)
return 0;
- return clk_get_rate(&parent) / (gckdiv + 1); }
+static ulong generated_clk_set_rate(struct clk *clk, ulong rate) {
- struct pmc_platdata *plat = dev_get_platdata(clk->dev);
- struct at91_pmc *pmc = plat->reg_base;
- struct generated_clk_priv *priv = dev_get_priv(clk->dev);
- struct clk parent, best_parent;
- ulong tmp_rate, best_rate = rate, parent_rate;
- int tmp_diff, best_diff = -1;
- u32 div, best_div = 0;
- u8 best_parent_id = 0;
- u8 i;
- u32 tmp;
- int ret;
- for (i = 0; i < priv->num_parents; i++) {
ret = clk_get_by_index(clk->dev, i, &parent);
if (ret)
return ret;
parent_rate = clk_get_rate(&parent);
if (IS_ERR_VALUE(parent_rate))
return parent_rate;
for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
if (rate < tmp_rate)
continue;
tmp_diff = rate - tmp_rate;
if (best_diff < 0 || best_diff > tmp_diff) {
best_rate = tmp_rate;
best_diff = tmp_diff;
best_div = div - 1;
best_parent = parent;
best_parent_id = i;
}
if (!best_diff || tmp_rate < rate)
break;
}
if (!best_diff)
break;
- }
- debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n",
best_parent.dev->name, best_rate, best_div);
- ret = clk_enable(&best_parent);
- if (ret)
return ret;
- writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
- tmp = readl(&pmc->pcr);
- tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS);
- tmp |= AT91_PMC_PCR_GCKCSS_(best_parent_id) |
AT91_PMC_PCR_CMD_WRITE |
AT91_PMC_PCR_GCKDIV_(best_div) |
AT91_PMC_PCR_GCKEN;
- writel(tmp, &pmc->pcr);
- while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY))
;
- return 0;
+}
+static struct clk_ops generated_clk_ops = {
- .get_rate = generated_clk_get_rate,
- .set_rate = generated_clk_set_rate,
+};
+static int generated_clk_ofdata_to_platdata(struct udevice *dev) {
- struct generated_clk_priv *priv = dev_get_priv(dev);
- u32 cells[GENERATED_SOURCE_MAX];
- u32 num_parents;
- num_parents = fdtdec_get_int_array_count(gd->fdt_blob, dev->of_offset,
"clocks", cells,
GENERATED_SOURCE_MAX);
- if (!num_parents)
return -1;
- priv->num_parents = num_parents;
- return 0;
+}
+static int generated_clk_bind(struct udevice *dev) {
- return at91_pmc_clk_node_bind(dev);
+}
+static int generated_clk_probe(struct udevice *dev) {
- return at91_pmc_core_probe(dev);
+}
+static const struct udevice_id generated_clk_match[] = {
- { .compatible = "atmel,sama5d2-clk-generated" },
- {}
+};
+U_BOOT_DRIVER(generated_clk) = {
- .name = "generated-clk",
- .id = UCLASS_CLK,
- .of_match = generated_clk_match,
- .bind = generated_clk_bind,
- .probe = generated_clk_probe,
- .ofdata_to_platdata = generated_clk_ofdata_to_platdata,
- .priv_auto_alloc_size = sizeof(struct generated_clk_priv),
- .platdata_auto_alloc_size = sizeof(struct pmc_platdata),
- .ops = &generated_clk_ops,
+}; diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c new file mode 100644 index 0000000..1a304ba --- /dev/null +++ b/drivers/clk/at91/clk-h32mx.c @@ -0,0 +1,56 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/util.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h"
+DECLARE_GLOBAL_DATA_PTR;
+#define H32MX_MAX_FREQ 90000000
+static ulong sama5d4_h32mx_clk_get_rate(struct clk *clk) {
- struct pmc_platdata *plat = dev_get_platdata(clk->dev);
- struct at91_pmc *pmc = plat->reg_base;
- ulong rate = gd->arch.mck_rate_hz;
- if (readl(&pmc->mckr) & AT91_PMC_MCKR_H32MXDIV)
rate /= 2;
- if (rate > H32MX_MAX_FREQ)
dm_warn("H32MX clock is too fast\n");
- return rate;
+}
+static struct clk_ops sama5d4_h32mx_clk_ops = {
- .get_rate = sama5d4_h32mx_clk_get_rate, };
+static int sama5d4_h32mx_clk_probe(struct udevice *dev) {
- return at91_pmc_core_probe(dev);
+}
+static const struct udevice_id sama5d4_h32mx_clk_match[] = {
- { .compatible = "atmel,sama5d4-clk-h32mx" },
- {}
+};
+U_BOOT_DRIVER(sama5d4_h32mx_clk) = {
- .name = "sama5d4-h32mx-clk",
- .id = UCLASS_CLK,
- .of_match = sama5d4_h32mx_clk_match,
- .probe = sama5d4_h32mx_clk_probe,
- .platdata_auto_alloc_size = sizeof(struct pmc_platdata),
- .ops = &sama5d4_h32mx_clk_ops,
+}; diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c new file mode 100644 index 0000000..252d076 --- /dev/null +++ b/drivers/clk/at91/clk-main.c @@ -0,0 +1,55 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h"
+DECLARE_GLOBAL_DATA_PTR;
+static int main_osc_clk_enable(struct clk *clk) {
- struct pmc_platdata *plat = dev_get_platdata(clk->dev);
- struct at91_pmc *pmc = plat->reg_base;
- if (readl(&pmc->sr) & AT91_PMC_MOSCSELS)
return 0;
- return -EINVAL;
+}
+static ulong main_osc_clk_get_rate(struct clk *clk) {
- return gd->arch.main_clk_rate_hz;
+}
+static struct clk_ops main_osc_clk_ops = {
- .enable = main_osc_clk_enable,
- .get_rate = main_osc_clk_get_rate,
+};
+static int main_osc_clk_probe(struct udevice *dev) {
- return at91_pmc_core_probe(dev);
+}
+static const struct udevice_id main_osc_clk_match[] = {
- { .compatible = "atmel,at91sam9x5-clk-main" },
- {}
+};
+U_BOOT_DRIVER(at91sam9x5_main_osc_clk) = {
- .name = "at91sam9x5-main-osc-clk",
- .id = UCLASS_CLK,
- .of_match = main_osc_clk_match,
- .probe = main_osc_clk_probe,
- .platdata_auto_alloc_size = sizeof(struct pmc_platdata),
- .ops = &main_osc_clk_ops,
+}; diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c new file mode 100644 index 0000000..284b248 --- /dev/null +++ b/drivers/clk/at91/clk-master.c @@ -0,0 +1,33 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h>
+DECLARE_GLOBAL_DATA_PTR;
+static ulong at91_master_clk_get_rate(struct clk *clk) {
- return gd->arch.mck_rate_hz;
+}
+static struct clk_ops at91_master_clk_ops = {
- .get_rate = at91_master_clk_get_rate,
+};
+static const struct udevice_id at91_master_clk_match[] = {
- { .compatible = "atmel,at91sam9x5-clk-master" },
- {}
+};
+U_BOOT_DRIVER(at91_master_clk) = {
- .name = "at91-master-clk",
- .id = UCLASS_CLK,
- .of_match = at91_master_clk_match,
- .ops = &at91_master_clk_ops,
+}; diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c new file mode 100644 index 0000000..16688e9 --- /dev/null +++ b/drivers/clk/at91/clk-peripheral.c @@ -0,0 +1,60 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h"
+#define PERIPHERAL_ID_MIN 2 +#define PERIPHERAL_ID_MAX 31 +#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
+static int sam9x5_periph_clk_enable(struct clk *clk) {
- struct pmc_platdata *plat = dev_get_platdata(clk->dev);
- struct at91_pmc *pmc = plat->reg_base;
- if (clk->id < PERIPHERAL_ID_MIN)
return -1;
- writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
- setbits_le32(&pmc->pcr, AT91_PMC_PCR_CMD_WRITE |
AT91_PMC_PCR_EN);
- return 0;
+}
+static struct clk_ops sam9x5_periph_clk_ops = {
- .enable = sam9x5_periph_clk_enable,
+};
+static int sam9x5_periph_clk_bind(struct udevice *dev) {
- return at91_pmc_clk_node_bind(dev);
+}
+static int sam9x5_periph_clk_probe(struct udevice *dev) {
- return at91_pmc_core_probe(dev);
+}
+static const struct udevice_id sam9x5_periph_clk_match[] = {
- { .compatible = "atmel,at91sam9x5-clk-peripheral" },
- {}
+};
+U_BOOT_DRIVER(sam9x5_periph_clk) = {
- .name = "sam9x5-periph-clk",
- .id = UCLASS_CLK,
- .of_match = sam9x5_periph_clk_match,
- .bind = sam9x5_periph_clk_bind,
- .probe = sam9x5_periph_clk_probe,
- .platdata_auto_alloc_size = sizeof(struct pmc_platdata),
- .ops = &sam9x5_periph_clk_ops,
+}; diff --git a/drivers/clk/at91/clk-plla.c b/drivers/clk/at91/clk-plla.c new file mode 100644 index 0000000..2a71399 --- /dev/null +++ b/drivers/clk/at91/clk-plla.c @@ -0,0 +1,55 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h"
+DECLARE_GLOBAL_DATA_PTR;
+static int plla_clk_enable(struct clk *clk) {
- struct pmc_platdata *plat = dev_get_platdata(clk->dev);
- struct at91_pmc *pmc = plat->reg_base;
- if (readl(&pmc->sr) & AT91_PMC_LOCKA)
return 0;
- return -EINVAL;
+}
+static ulong plla_clk_get_rate(struct clk *clk) {
- return gd->arch.plla_rate_hz;
+}
+static struct clk_ops plla_clk_ops = {
- .enable = plla_clk_enable,
- .get_rate = plla_clk_get_rate,
+};
+static int plla_clk_probe(struct udevice *dev) {
- return at91_pmc_core_probe(dev);
+}
+static const struct udevice_id plla_clk_match[] = {
- { .compatible = "atmel,sama5d3-clk-pll" },
- {}
+};
+U_BOOT_DRIVER(at91_plla_clk) = {
- .name = "at91-plla-clk",
- .id = UCLASS_CLK,
- .of_match = plla_clk_match,
- .probe = plla_clk_probe,
- .platdata_auto_alloc_size = sizeof(struct pmc_platdata),
- .ops = &plla_clk_ops,
+}; diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c new file mode 100644 index 0000000..f7666b4 --- /dev/null +++ b/drivers/clk/at91/clk-slow.c @@ -0,0 +1,37 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h>
+static int at91_slow_clk_enable(struct clk *clk) {
- return 0;
+}
+static ulong at91_slow_clk_get_rate(struct clk *clk) {
- return CONFIG_SYS_AT91_SLOW_CLOCK;
+}
+static struct clk_ops at91_slow_clk_ops = {
- .enable = at91_slow_clk_enable,
- .get_rate = at91_slow_clk_get_rate,
+};
+static const struct udevice_id at91_slow_clk_match[] = {
- { .compatible = "atmel,at91sam9x5-clk-slow" },
- {}
+};
+U_BOOT_DRIVER(at91_slow_clk) = {
- .name = "at91-slow-clk",
- .id = UCLASS_CLK,
- .of_match = at91_slow_clk_match,
- .ops = &at91_slow_clk_ops,
+}; diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c new file mode 100644 index 0000000..fa80bad --- /dev/null +++ b/drivers/clk/at91/clk-system.c @@ -0,0 +1,76 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h"
+#define SYSTEM_MAX_ID 31
+static inline int is_pck(int id) +{
- return (id >= 8) && (id <= 15);
+}
+static int at91_system_clk_enable(struct clk *clk) {
- struct pmc_platdata *plat = dev_get_platdata(clk->dev);
- struct at91_pmc *pmc = plat->reg_base;
- u32 mask;
- if (clk->id > SYSTEM_MAX_ID)
return -EINVAL;
- mask = BIT(clk->id);
- writel(mask, &pmc->scer);
- /**
* For the programmable clocks the Ready status in the PMC
* status register should be checked after enabling.
* For other clocks this is unnecessary.
*/
- if (!is_pck(clk->id))
return 0;
- while (!(readl(&pmc->sr) & mask))
;
- return 0;
+}
+static struct clk_ops at91_system_clk_ops = {
- .enable = at91_system_clk_enable,
+};
+static int at91_system_clk_bind(struct udevice *dev) {
- return at91_pmc_clk_node_bind(dev);
+}
+static int at91_system_clk_probe(struct udevice *dev) {
- return at91_pmc_core_probe(dev);
+}
+static const struct udevice_id at91_system_clk_match[] = {
- { .compatible = "atmel,at91rm9200-clk-system" },
- {}
+};
+U_BOOT_DRIVER(at91_system_clk) = {
- .name = "at91-system-clk",
- .id = UCLASS_CLK,
- .of_match = at91_system_clk_match,
- .bind = at91_system_clk_bind,
- .probe = at91_system_clk_probe,
- .platdata_auto_alloc_size = sizeof(struct pmc_platdata),
- .ops = &at91_system_clk_ops,
+}; diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c new file mode 100644 index 0000000..369a687 --- /dev/null +++ b/drivers/clk/at91/clk-utmi.c @@ -0,0 +1,67 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h"
+DECLARE_GLOBAL_DATA_PTR;
+#define UTMI_FIXED_MUL 40
+static int utmi_clk_enable(struct clk *clk) {
- struct pmc_platdata *plat = dev_get_platdata(clk->dev);
- struct at91_pmc *pmc = plat->reg_base;
- u32 tmp;
- if (readl(&pmc->sr) & AT91_PMC_LOCKU)
return 0;
- tmp = readl(&pmc->uckr);
- tmp |= AT91_PMC_UPLLEN |
AT91_PMC_UPLLCOUNT |
AT91_PMC_BIASEN;
- writel(tmp, &pmc->uckr);
- while (!(readl(&pmc->sr) & AT91_PMC_LOCKU))
;
- return 0;
+}
+static ulong utmi_clk_get_rate(struct clk *clk) {
- return gd->arch.main_clk_rate_hz * UTMI_FIXED_MUL; }
+static struct clk_ops utmi_clk_ops = {
- .enable = utmi_clk_enable,
- .get_rate = utmi_clk_get_rate,
+};
+static int utmi_clk_probe(struct udevice *dev) {
- return at91_pmc_core_probe(dev);
+}
+static const struct udevice_id utmi_clk_match[] = {
- { .compatible = "atmel,at91sam9x5-clk-utmi" },
- {}
+};
+U_BOOT_DRIVER(at91sam9x5_utmi_clk) = {
- .name = "at91sam9x5-utmi-clk",
- .id = UCLASS_CLK,
- .of_match = utmi_clk_match,
- .probe = utmi_clk_probe,
- .platdata_auto_alloc_size = sizeof(struct pmc_platdata),
- .ops = &utmi_clk_ops,
+}; diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c new file mode 100644 index 0000000..a08d7e8 --- /dev/null +++ b/drivers/clk/at91/pmc.c @@ -0,0 +1,71 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/lists.h> +#include <dm/root.h> +#include "pmc.h"
+DECLARE_GLOBAL_DATA_PTR;
+static int at91_pmc_bind(struct udevice *dev) {
- return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); }
+static const struct udevice_id at91_pmc_match[] = {
- { .compatible = "atmel,sama5d2-pmc" },
- {}
+};
+U_BOOT_DRIVER(at91_pmc) = {
- .name = "at91-pmc-core",
- .id = UCLASS_CLK,
- .of_match = at91_pmc_match,
- .bind = at91_pmc_bind,
+};
+int at91_pmc_core_probe(struct udevice *dev) {
- struct pmc_platdata *plat = dev_get_platdata(dev);
- dev = dev_get_parent(dev);
- plat->reg_base = (struct at91_pmc *)dev_get_addr_ptr(dev);
- return 0;
+}
+int at91_pmc_clk_node_bind(struct udevice *dev) {
- const void *fdt = gd->fdt_blob;
- int offset = dev->of_offset;
- const char *name;
- int ret;
- for (offset = fdt_first_subnode(fdt, offset);
offset > 0;
offset = fdt_next_subnode(fdt, offset)) {
name = fdt_get_name(fdt, offset, NULL);
if (!name)
return -EINVAL;
ret = device_bind_driver_to_node(dev, "clk", name,
offset, NULL);
if (ret)
return ret;
- }
- return 0;
+}
+U_BOOT_DRIVER(clk_generic) = {
- .id = UCLASS_CLK,
- .name = "clk",
+}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h new file mode 100644 index 0000000..5444c84 --- /dev/null +++ b/drivers/clk/at91/pmc.h @@ -0,0 +1,18 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __AT91_PMC_H__ +#define __AT91_PMC_H__
+struct pmc_platdata {
- struct at91_pmc *reg_base;
+};
+int at91_pmc_core_probe(struct udevice *dev); int +at91_pmc_clk_node_bind(struct udevice *dev);
+#endif diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c new file mode 100644 index 0000000..b207611 --- /dev/null +++ b/drivers/clk/at91/sckc.c @@ -0,0 +1,30 @@ +/*
- Copyright (C) 2016 Atmel Corporation
Wenyou.Yang <wenyou.yang@atmel.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/root.h>
+DECLARE_GLOBAL_DATA_PTR;
+static int at91_sckc_clk_bind(struct udevice *dev) {
- return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); }
+static const struct udevice_id at91_sckc_clk_match[] = {
- { .compatible = "atmel,at91sam9x5-sckc" },
- {}
+};
+U_BOOT_DRIVER(at91_sckc_clk) = {
- .name = "at91_sckc_clk",
- .id = UCLASS_CLK,
- .of_match = at91_sckc_clk_match,
- .bind = at91_sckc_clk_bind,
+};
2.7.4

Dear Wenyou Yang,
Wenyou Yang wenyou.yang@atmel.com writes:
The patch is referred to at91 clock driver of Linux, to make the clock node descriptions in DT aligned with the Linux's.
Signed-off-by: Wenyou Yang wenyou.yang@atmel.com Reviewed-by: Simon Glass sjg@chromium.org
Changes in v5:
- Change clk_client.h -> clk.h to adapt to clk API conversion.
- Fix missing semicolon and clk->dev in clk-generated.c.
- Make clock options selectable via menuconfig.
Changes in v4:
- Add Reviewed-by tag.
- Add more information in Kconfig help.
- Use u32 for num_parents variable, not u8.
- Change the check return from clk_get_rate().
- Remove return -ENODEV line, use return ret.
- Improve the comments in at91_system_clk_enable().
Changes in v3:
- Update based on [PATCH] clk: convert API to match reset/mailbox style (http://patchwork.ozlabs.org/patch/625342/).
- Remove [PATCH] clk: clk-uclass: Add post binding for CLK uclass, add bind() method to bind the clk node without compatible.
- Add help for Kconfig HAVE_AT91_XX option.
- Add ofdata_to_platdata() method for generated clock driver to handle the device tree.
- Use setbits_le32() to replace readl()/writel().
- Fixed the return value, -ENODEV->-EINVAL.
- Use dev_get_addr_ptr() to replace dev_get_addr().
- Remove check on dev_get_parent() return.
Changes in v2:
- Remove the redundant log print.
arch/arm/mach-at91/include/mach/at91_pmc.h | 11 +- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/at91/Kconfig | 43 ++++++++ drivers/clk/at91/Makefile | 11 ++ drivers/clk/at91/clk-generated.c | 162 +++++++++++++++++++++++++++++ drivers/clk/at91/clk-h32mx.c | 56 ++++++++++ drivers/clk/at91/clk-main.c | 55 ++++++++++ drivers/clk/at91/clk-master.c | 33 ++++++ drivers/clk/at91/clk-peripheral.c | 60 +++++++++++ drivers/clk/at91/clk-plla.c | 55 ++++++++++ drivers/clk/at91/clk-slow.c | 37 +++++++ drivers/clk/at91/clk-system.c | 76 ++++++++++++++ drivers/clk/at91/clk-utmi.c | 67 ++++++++++++ drivers/clk/at91/pmc.c | 71 +++++++++++++ drivers/clk/at91/pmc.h | 18 ++++ drivers/clk/at91/sckc.c | 30 ++++++ 17 files changed, 784 insertions(+), 3 deletions(-) create mode 100644 drivers/clk/at91/Kconfig create mode 100644 drivers/clk/at91/Makefile create mode 100644 drivers/clk/at91/clk-generated.c create mode 100644 drivers/clk/at91/clk-h32mx.c create mode 100644 drivers/clk/at91/clk-main.c create mode 100644 drivers/clk/at91/clk-master.c create mode 100644 drivers/clk/at91/clk-peripheral.c create mode 100644 drivers/clk/at91/clk-plla.c create mode 100644 drivers/clk/at91/clk-slow.c create mode 100644 drivers/clk/at91/clk-system.c create mode 100644 drivers/clk/at91/clk-utmi.c create mode 100644 drivers/clk/at91/pmc.c create mode 100644 drivers/clk/at91/pmc.h create mode 100644 drivers/clk/at91/sckc.c
applied to u-boot-atmel/master, thanks!
Best regards, Andreas Bießmann
participants (3)
-
Andreas Bießmann
-
Wenyou Yang
-
Wenyou.Yang@microchip.com