diff options
-rw-r--r-- | arch/arm/mach-at91/include/mach/sama5_sfr.h | 5 | ||||
-rw-r--r-- | drivers/clk/at91/Kconfig | 6 | ||||
-rw-r--r-- | drivers/clk/at91/clk-utmi.c | 77 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.h | 3 |
4 files changed, 88 insertions, 3 deletions
diff --git a/arch/arm/mach-at91/include/mach/sama5_sfr.h b/arch/arm/mach-at91/include/mach/sama5_sfr.h index b805a2c934..965631aad7 100644 --- a/arch/arm/mach-at91/include/mach/sama5_sfr.h +++ b/arch/arm/mach-at91/include/mach/sama5_sfr.h @@ -28,6 +28,9 @@ struct atmel_sfr { u32 l2cc_hramc; /* 0x58 */ }; +/* Register Mapping*/ +#define AT91_SFR_UTMICKTRIM 0x30 /* UTMI Clock Trimming Register */ + /* Bit field in DDRCFG */ #define ATMEL_SFR_DDRCFG_FDQIEN 0x00010000 #define ATMEL_SFR_DDRCFG_FDQSIEN 0x00020000 @@ -56,6 +59,8 @@ struct atmel_sfr { #define AT91_SFR_EBICFG_SCH1_OFF (0x0 << 12) #define AT91_SFR_EBICFG_SCH1_ON (0x1 << 12) +#define AT91_UTMICKTRIM_FREQ GENMASK(1, 0) + /* Bit field in AICREDIR */ #define ATMEL_SFR_AICREDIR_NSAIC 0x00000001 diff --git a/drivers/clk/at91/Kconfig b/drivers/clk/at91/Kconfig index 904ed48e51..c6c57618c1 100644 --- a/drivers/clk/at91/Kconfig +++ b/drivers/clk/at91/Kconfig @@ -14,7 +14,11 @@ config CLK_AT91 config AT91_UTMI bool "Support UTMI PLL Clock" - depends on CLK_AT91 + depends on CLK_AT91 && SPL_DM + select REGMAP + select SPL_REGMAP + select SYSCON + select SPL_SYSCON help This option is used to enable the AT91 UTMI PLL clock driver. It is the clock provider of USB, and UPLLCK is the diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c index af5362da42..875bf293f9 100644 --- a/drivers/clk/at91/clk-utmi.c +++ b/drivers/clk/at91/clk-utmi.c @@ -8,23 +8,80 @@ #include <common.h> #include <clk-uclass.h> #include <dm.h> +#include <syscon.h> #include <linux/io.h> #include <mach/at91_pmc.h> +#include <mach/sama5_sfr.h> #include "pmc.h" DECLARE_GLOBAL_DATA_PTR; -#define UTMI_FIXED_MUL 40 +/* + * The purpose of this clock is to generate a 480 MHz signal. A different + * rate can't be configured. + */ +#define UTMI_RATE 480000000 static int utmi_clk_enable(struct clk *clk) { struct pmc_platdata *plat = dev_get_platdata(clk->dev); struct at91_pmc *pmc = plat->reg_base; + struct clk clk_dev; + ulong clk_rate; + u32 utmi_ref_clk_freq; u32 tmp; + int err; if (readl(&pmc->sr) & AT91_PMC_LOCKU) return 0; + /* + * If mainck rate is different from 12 MHz, we have to configure the + * FREQ field of the SFR_UTMICKTRIM register to generate properly + * the utmi clock. + */ + err = clk_get_by_index(clk->dev, 0, &clk_dev); + if (err) + return -EINVAL; + + clk_rate = clk_get_rate(&clk_dev); + switch (clk_rate) { + case 12000000: + utmi_ref_clk_freq = 0; + break; + case 16000000: + utmi_ref_clk_freq = 1; + break; + case 24000000: + utmi_ref_clk_freq = 2; + break; + /* + * Not supported on SAMA5D2 but it's not an issue since MAINCK + * maximum value is 24 MHz. + */ + case 48000000: + utmi_ref_clk_freq = 3; + break; + default: + printf("UTMICK: unsupported mainck rate\n"); + return -EINVAL; + } + + if (plat->regmap_sfr) { + err = regmap_read(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, &tmp); + if (err) + return -EINVAL; + + tmp &= ~AT91_UTMICKTRIM_FREQ; + tmp |= utmi_ref_clk_freq; + err = regmap_write(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, tmp); + if (err) + return -EINVAL; + } else if (utmi_ref_clk_freq) { + printf("UTMICK: sfr node required\n"); + return -EINVAL; + } + tmp = readl(&pmc->uckr); tmp |= AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | @@ -39,7 +96,8 @@ static int utmi_clk_enable(struct clk *clk) static ulong utmi_clk_get_rate(struct clk *clk) { - return gd->arch.main_clk_rate_hz * UTMI_FIXED_MUL; + /* UTMI clk rate is fixed. */ + return UTMI_RATE; } static struct clk_ops utmi_clk_ops = { @@ -47,6 +105,20 @@ static struct clk_ops utmi_clk_ops = { .get_rate = utmi_clk_get_rate, }; +static int utmi_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct pmc_platdata *plat = dev_get_platdata(dev); + struct udevice *syscon; + + uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + "regmap-sfr", &syscon); + + if (syscon) + plat->regmap_sfr = syscon_get_regmap(syscon); + + return 0; +} + static int utmi_clk_probe(struct udevice *dev) { return at91_pmc_core_probe(dev); @@ -62,6 +134,7 @@ U_BOOT_DRIVER(at91sam9x5_utmi_clk) = { .id = UCLASS_CLK, .of_match = utmi_clk_match, .probe = utmi_clk_probe, + .ofdata_to_platdata = utmi_clk_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct pmc_platdata), .ops = &utmi_clk_ops, }; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index bd3caba48d..5abda764a2 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -8,8 +8,11 @@ #ifndef __AT91_PMC_H__ #define __AT91_PMC_H__ +#include <regmap.h> + struct pmc_platdata { struct at91_pmc *reg_base; + struct regmap *regmap_sfr; }; int at91_pmc_core_probe(struct udevice *dev); |