Merge tag 'u-boot-atmel-2021.01-a' of https://gitlab.denx.de/u-boot/custodians/u-boot-atmel into next

First set of u-boot-atmel features for 2021.01 cycle:

This feature set includes a new CPU driver for at91 family, new driver
for PIT64B hardware timer, support for new at91 family SoC named sama7g5
which adds: clock support, including conversion of the clock tree to
CCF; SoC support in mach-at91, pinctrl and mmc drivers update.  The
feature set also includes updates for mmc driver and some other minor
fixes and features regarding building without the old Atmel PIT and the
possibility to read a secondary MAC address from a second i2c EEPROM.
This commit is contained in:
Tom Rini
2020-10-05 10:54:27 -04:00
46 changed files with 6736 additions and 951 deletions

View File

@@ -54,3 +54,10 @@ config AT91_GENERIC_CLK
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.
config AT91_SAM9X60_PLL
bool "PLL support for SAM9X60 SoCs"
depends on CLK_AT91
help
This option is used to enable the AT91 SAM9X60's PLL clock
driver.

View File

@@ -2,11 +2,14 @@
# Makefile for at91 specific clk
#
obj-y += pmc.o sckc.o
obj-y += clk-slow.o clk-main.o clk-plla.o clk-plladiv.o clk-master.o
obj-y += clk-system.o clk-peripheral.o
ifdef CONFIG_CLK_CCF
obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o clk-system.o
obj-y += clk-peripheral.o
obj-$(CONFIG_AT91_GENERIC_CLK) += clk-generic.o
obj-$(CONFIG_AT91_UTMI) += clk-utmi.o
obj-$(CONFIG_AT91_USB_CLK) += clk-usb.o
obj-$(CONFIG_AT91_H32MX) += clk-h32mx.o
obj-$(CONFIG_AT91_GENERIC_CLK) += clk-generated.o
obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o
obj-$(CONFIG_SAMA7G5) += sama7g5.o
else
obj-y += compat.o
endif

View File

@@ -1,178 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <log.h>
#include <malloc.h>
#include <linux/err.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
/**
* generated_clk_bind() - for the generated clock driver
* Recursively bind its children as clk devices.
*
* @return: 0 on success, or negative error code on failure
*/
static int generated_clk_bind(struct udevice *dev)
{
return at91_clk_sub_device_bind(dev, "generic-clk");
}
static const struct udevice_id generated_clk_match[] = {
{ .compatible = "atmel,sama5d2-clk-generated" },
{}
};
U_BOOT_DRIVER(generated_clk) = {
.name = "generated-clk",
.id = UCLASS_MISC,
.of_match = generated_clk_match,
.bind = generated_clk_bind,
};
/*-------------------------------------------------------------*/
struct generic_clk_priv {
u32 num_parents;
};
static ulong generic_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;
ulong clk_rate;
u32 tmp, gckdiv;
u8 clock_source, parent_index;
int ret;
writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
tmp = readl(&pmc->pcr);
clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) &
AT91_PMC_PCR_GCKCSS_MASK;
gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK;
parent_index = clock_source - 1;
ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent);
if (ret)
return 0;
clk_rate = clk_get_rate(&parent) / (gckdiv + 1);
clk_free(&parent);
return clk_rate;
}
static ulong generic_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 generic_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_index, best_clock_source = 0;
u8 i;
u32 tmp;
int ret;
for (i = 0; i < priv->num_parents; i++) {
ret = clk_get_by_index(dev_get_parent(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);
tmp_diff = abs(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_index = i;
best_clock_source = best_parent_index + 1;
}
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_clock_source) |
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 generic_clk_ops = {
.of_xlate = at91_clk_of_xlate,
.get_rate = generic_clk_get_rate,
.set_rate = generic_clk_set_rate,
};
static int generic_clk_ofdata_to_platdata(struct udevice *dev)
{
struct generic_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(dev_get_parent(dev)), "clocks", cells,
GENERATED_SOURCE_MAX);
if (!num_parents)
return -1;
priv->num_parents = num_parents;
return 0;
}
U_BOOT_DRIVER(generic_clk) = {
.name = "generic-clk",
.id = UCLASS_CLK,
.probe = at91_clk_probe,
.ofdata_to_platdata = generic_clk_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct generic_clk_priv),
.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
.ops = &generic_clk_ops,
};

View File

@@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Generic clock support for AT91 architectures.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
* Based on drivers/clk/at91/clk-generated.c from Linux.
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <linux/io.h>
#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
#include "pmc.h"
#define UBOOT_DM_CLK_AT91_GCK "at91-gck-clk"
#define GENERATED_MAX_DIV 255
struct clk_gck {
void __iomem *base;
const u32 *clk_mux_table;
const u32 *mux_table;
const struct clk_pcr_layout *layout;
struct clk_range range;
struct clk clk;
u32 num_parents;
u32 id;
};
#define to_clk_gck(_c) container_of(_c, struct clk_gck, clk)
static int clk_gck_enable(struct clk *clk)
{
struct clk_gck *gck = to_clk_gck(clk);
pmc_write(gck->base, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
pmc_update_bits(gck->base, gck->layout->offset,
gck->layout->cmd | AT91_PMC_PCR_GCKEN,
gck->layout->cmd | AT91_PMC_PCR_GCKEN);
return 0;
}
static int clk_gck_disable(struct clk *clk)
{
struct clk_gck *gck = to_clk_gck(clk);
pmc_write(gck->base, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
pmc_update_bits(gck->base, gck->layout->offset,
gck->layout->cmd | AT91_PMC_PCR_GCKEN,
gck->layout->cmd);
return 0;
}
static int clk_gck_set_parent(struct clk *clk, struct clk *parent)
{
struct clk_gck *gck = to_clk_gck(clk);
int index;
index = at91_clk_mux_val_to_index(gck->clk_mux_table, gck->num_parents,
parent->id);
if (index < 0)
return index;
index = at91_clk_mux_index_to_val(gck->mux_table, gck->num_parents,
index);
if (index < 0)
return index;
pmc_write(gck->base, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
pmc_update_bits(gck->base, gck->layout->offset,
gck->layout->gckcss_mask | gck->layout->cmd,
(index << (ffs(gck->layout->gckcss_mask) - 1)) |
gck->layout->cmd);
return 0;
}
static ulong clk_gck_set_rate(struct clk *clk, ulong rate)
{
struct clk_gck *gck = to_clk_gck(clk);
ulong parent_rate = clk_get_parent_rate(clk);
u32 div;
if (!rate || !parent_rate)
return 0;
if (gck->range.max && rate > gck->range.max)
return 0;
div = DIV_ROUND_CLOSEST(parent_rate, rate);
if (div > GENERATED_MAX_DIV + 1 || !div)
return 0;
pmc_write(gck->base, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
pmc_update_bits(gck->base, gck->layout->offset,
AT91_PMC_PCR_GCKDIV_MASK | gck->layout->cmd,
((div - 1) << (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1)) |
gck->layout->cmd);
return parent_rate / div;
}
static ulong clk_gck_get_rate(struct clk *clk)
{
struct clk_gck *gck = to_clk_gck(clk);
ulong parent_rate = clk_get_parent_rate(clk);
u32 val, div;
if (!parent_rate)
return 0;
pmc_write(gck->base, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
pmc_read(gck->base, gck->layout->offset, &val);
div = (val & AT91_PMC_PCR_GCKDIV_MASK) >>
(ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1);
return parent_rate / (div + 1);
}
static const struct clk_ops gck_ops = {
.enable = clk_gck_enable,
.disable = clk_gck_disable,
.set_parent = clk_gck_set_parent,
.set_rate = clk_gck_set_rate,
.get_rate = clk_gck_get_rate,
};
struct clk *
at91_clk_register_generic(void __iomem *base,
const struct clk_pcr_layout *layout,
const char *name, const char * const *parent_names,
const u32 *clk_mux_table, const u32 *mux_table,
u8 num_parents, u8 id,
const struct clk_range *range)
{
struct clk_gck *gck;
struct clk *clk;
int ret, index;
u32 val;
if (!base || !layout || !name || !parent_names || !num_parents ||
!clk_mux_table || !mux_table || !range)
return ERR_PTR(-EINVAL);
gck = kzalloc(sizeof(*gck), GFP_KERNEL);
if (!gck)
return ERR_PTR(-ENOMEM);
gck->id = id;
gck->base = base;
gck->range = *range;
gck->layout = layout;
gck->clk_mux_table = clk_mux_table;
gck->mux_table = mux_table;
gck->num_parents = num_parents;
clk = &gck->clk;
clk->flags = CLK_GET_RATE_NOCACHE;
pmc_write(gck->base, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
pmc_read(gck->base, gck->layout->offset, &val);
val = (val & gck->layout->gckcss_mask) >>
(ffs(gck->layout->gckcss_mask) - 1);
index = at91_clk_mux_val_to_index(gck->mux_table, gck->num_parents,
val);
if (index < 0) {
kfree(gck);
return ERR_PTR(index);
}
ret = clk_register(clk, UBOOT_DM_CLK_AT91_GCK, name,
parent_names[index]);
if (ret) {
kfree(gck);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_gck_clk) = {
.name = UBOOT_DM_CLK_AT91_GCK,
.id = UCLASS_CLK,
.ops = &gck_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -1,56 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <dm/device_compat.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)
dev_dbg(clk->dev, "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,
};

View File

@@ -1,54 +1,387 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
* Main clock support for AT91 architectures.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
* Based on drivers/clk/at91/clk-main.c from Linux.
*/
#include <asm/processor.h>
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <mach/at91_pmc.h>
#include "pmc.h"
DECLARE_GLOBAL_DATA_PTR;
#define UBOOT_DM_CLK_AT91_MAIN_RC "at91-main-rc-clk"
#define UBOOT_DM_CLK_AT91_MAIN_OSC "at91-main-osc-clk"
#define UBOOT_DM_CLK_AT91_RM9200_MAIN "at91-rm9200-main-clk"
#define UBOOT_DM_CLK_AT91_SAM9X5_MAIN "at91-sam9x5-main-clk"
static int main_osc_clk_enable(struct clk *clk)
#define MOR_KEY_MASK GENMASK(23, 16)
#define USEC_PER_SEC 1000000UL
#define SLOW_CLOCK_FREQ 32768
#define clk_main_parent_select(s) (((s) & \
(AT91_PMC_MOSCEN | \
AT91_PMC_OSCBYPASS)) ? 1 : 0)
struct clk_main_rc {
void __iomem *reg;
struct clk clk;
};
#define to_clk_main_rc(_clk) container_of(_clk, struct clk_main_rc, clk)
struct clk_main_osc {
void __iomem *reg;
struct clk clk;
};
#define to_clk_main_osc(_clk) container_of(_clk, struct clk_main_osc, clk)
struct clk_main {
void __iomem *reg;
const unsigned int *clk_mux_table;
const char * const *parent_names;
unsigned int num_parents;
int type;
struct clk clk;
};
#define to_clk_main(_clk) container_of(_clk, struct clk_main, clk)
static int main_rc_enable(struct clk *clk)
{
struct pmc_platdata *plat = dev_get_platdata(clk->dev);
struct at91_pmc *pmc = plat->reg_base;
struct clk_main_rc *main_rc = to_clk_main_rc(clk);
void __iomem *reg = main_rc->reg;
unsigned int val;
if (readl(&pmc->sr) & AT91_PMC_MOSCSELS)
pmc_read(reg, AT91_CKGR_MOR, &val);
if (!(val & AT91_PMC_MOSCRCEN)) {
pmc_update_bits(reg, AT91_CKGR_MOR,
MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
AT91_PMC_KEY | AT91_PMC_MOSCRCEN);
}
pmc_read(reg, AT91_PMC_SR, &val);
while (!(val & AT91_PMC_MOSCRCS)) {
pmc_read(reg, AT91_PMC_SR, &val);
debug("waiting for main rc...\n");
cpu_relax();
}
return 0;
}
static int main_rc_disable(struct clk *clk)
{
struct clk_main_rc *main_rc = to_clk_main_rc(clk);
struct reg *reg = main_rc->reg;
unsigned int val;
pmc_read(reg, AT91_CKGR_MOR, &val);
if (!(val & AT91_PMC_MOSCRCEN))
return 0;
return -EINVAL;
pmc_update_bits(reg, AT91_CKGR_MOR, MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
AT91_PMC_KEY);
return 0;
}
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 const struct clk_ops main_rc_clk_ops = {
.enable = main_rc_enable,
.disable = main_rc_disable,
.get_rate = clk_generic_get_rate,
};
static int main_osc_clk_probe(struct udevice *dev)
struct clk *at91_clk_main_rc(void __iomem *reg, const char *name,
const char *parent_name)
{
return at91_pmc_core_probe(dev);
struct clk_main_rc *main_rc;
struct clk *clk;
int ret;
if (!reg || !name || !parent_name)
return ERR_PTR(-EINVAL);
main_rc = kzalloc(sizeof(*main_rc), GFP_KERNEL);
if (!main_rc)
return ERR_PTR(-ENOMEM);
main_rc->reg = reg;
clk = &main_rc->clk;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_MAIN_RC, name,
parent_name);
if (ret) {
kfree(main_rc);
clk = ERR_PTR(ret);
}
return clk;
}
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",
U_BOOT_DRIVER(at91_main_rc_clk) = {
.name = UBOOT_DM_CLK_AT91_MAIN_RC,
.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,
.ops = &main_rc_clk_ops,
.flags = DM_FLAG_PRE_RELOC,
};
static int clk_main_osc_enable(struct clk *clk)
{
struct clk_main_osc *main = to_clk_main_osc(clk);
void __iomem *reg = main->reg;
unsigned int val;
pmc_read(reg, AT91_CKGR_MOR, &val);
val &= ~MOR_KEY_MASK;
if (val & AT91_PMC_OSCBYPASS)
return 0;
if (!(val & AT91_PMC_MOSCEN)) {
val |= AT91_PMC_MOSCEN | AT91_PMC_KEY;
pmc_write(reg, AT91_CKGR_MOR, val);
}
pmc_read(reg, AT91_PMC_SR, &val);
while (!(val & AT91_PMC_MOSCS)) {
pmc_read(reg, AT91_PMC_SR, &val);
debug("waiting for main osc..\n");
cpu_relax();
}
return 0;
}
static int clk_main_osc_disable(struct clk *clk)
{
struct clk_main_osc *main = to_clk_main_osc(clk);
void __iomem *reg = main->reg;
unsigned int val;
pmc_read(reg, AT91_CKGR_MOR, &val);
if (val & AT91_PMC_OSCBYPASS)
return 0;
if (!(val & AT91_PMC_MOSCEN))
return 0;
val &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN);
pmc_write(reg, AT91_CKGR_MOR, val | AT91_PMC_KEY);
return 0;
}
static const struct clk_ops main_osc_clk_ops = {
.enable = clk_main_osc_enable,
.disable = clk_main_osc_disable,
.get_rate = clk_generic_get_rate,
};
struct clk *at91_clk_main_osc(void __iomem *reg, const char *name,
const char *parent_name, bool bypass)
{
struct clk_main_osc *main;
struct clk *clk;
int ret;
if (!reg || !name || !parent_name)
return ERR_PTR(-EINVAL);
main = kzalloc(sizeof(*main), GFP_KERNEL);
if (!main)
return ERR_PTR(-ENOMEM);
main->reg = reg;
clk = &main->clk;
if (bypass) {
pmc_update_bits(reg, AT91_CKGR_MOR,
MOR_KEY_MASK | AT91_PMC_OSCBYPASS,
AT91_PMC_KEY | AT91_PMC_OSCBYPASS);
}
ret = clk_register(clk, UBOOT_DM_CLK_AT91_MAIN_OSC, name, parent_name);
if (ret) {
kfree(main);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_main_osc_clk) = {
.name = UBOOT_DM_CLK_AT91_MAIN_OSC,
.id = UCLASS_CLK,
.ops = &main_osc_clk_ops,
.flags = DM_FLAG_PRE_RELOC,
};
static int clk_main_probe_frequency(void __iomem *reg)
{
unsigned int cycles = 16;
unsigned int cycle = DIV_ROUND_UP(USEC_PER_SEC, SLOW_CLOCK_FREQ);
unsigned int mcfr;
while (cycles--) {
pmc_read(reg, AT91_CKGR_MCFR, &mcfr);
if (mcfr & AT91_PMC_MAINRDY)
return 0;
udelay(cycle);
}
return -ETIMEDOUT;
}
static int clk_rm9200_main_enable(struct clk *clk)
{
struct clk_main *main = to_clk_main(clk);
return clk_main_probe_frequency(main->reg);
}
static const struct clk_ops rm9200_main_clk_ops = {
.enable = clk_rm9200_main_enable,
};
struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name,
const char *parent_name)
{
struct clk_main *main;
struct clk *clk;
int ret;
if (!reg || !name || !parent_name)
return ERR_PTR(-EINVAL);
main = kzalloc(sizeof(*main), GFP_KERNEL);
if (!main)
return ERR_PTR(-ENOMEM);
main->reg = reg;
clk = &main->clk;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_RM9200_MAIN, name,
parent_name);
if (ret) {
kfree(main);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_rm9200_main_clk) = {
.name = UBOOT_DM_CLK_AT91_RM9200_MAIN,
.id = UCLASS_CLK,
.ops = &rm9200_main_clk_ops,
.flags = DM_FLAG_PRE_RELOC,
};
static inline bool clk_sam9x5_main_ready(void __iomem *reg)
{
unsigned int val;
pmc_read(reg, AT91_PMC_SR, &val);
return !!(val & AT91_PMC_MOSCSELS);
}
static int clk_sam9x5_main_enable(struct clk *clk)
{
struct clk_main *main = to_clk_main(clk);
void __iomem *reg = main->reg;
while (!clk_sam9x5_main_ready(reg)) {
debug("waiting for main...");
cpu_relax();
}
return clk_main_probe_frequency(reg);
}
static int clk_sam9x5_main_set_parent(struct clk *clk, struct clk *parent)
{
struct clk_main *main = to_clk_main(clk);
void __iomem *reg = main->reg;
unsigned int tmp, index;
index = at91_clk_mux_val_to_index(main->clk_mux_table,
main->num_parents, AT91_CLK_ID_TO_DID(parent->id));
if (index < 0)
return index;
pmc_read(reg, AT91_CKGR_MOR, &tmp);
tmp &= ~MOR_KEY_MASK;
tmp |= AT91_PMC_KEY;
if (index && !(tmp & AT91_PMC_MOSCSEL))
pmc_write(reg, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL);
else if (!index && (tmp & AT91_PMC_MOSCSEL))
pmc_write(reg, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
while (!clk_sam9x5_main_ready(reg))
cpu_relax();
return 0;
}
static const struct clk_ops sam9x5_main_clk_ops = {
.enable = clk_sam9x5_main_enable,
.set_parent = clk_sam9x5_main_set_parent,
.get_rate = clk_generic_get_rate,
};
struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name,
const char * const *parent_names,
int num_parents, const u32 *clk_mux_table,
int type)
{
struct clk *clk = ERR_PTR(-ENOMEM);
struct clk_main *main = NULL;
unsigned int val;
int ret;
if (!reg || !name || !parent_names || !num_parents || !clk_mux_table)
return ERR_PTR(-EINVAL);
main = kzalloc(sizeof(*main), GFP_KERNEL);
if (!main)
return ERR_PTR(-ENOMEM);
main->reg = reg;
main->parent_names = parent_names;
main->num_parents = num_parents;
main->clk_mux_table = clk_mux_table;
main->type = type;
clk = &main->clk;
clk->flags = CLK_GET_RATE_NOCACHE;
pmc_read(reg, AT91_CKGR_MOR, &val);
ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X5_MAIN, name,
main->parent_names[clk_main_parent_select(val)]);
if (ret) {
kfree(main);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_sam9x5_main_clk) = {
.name = UBOOT_DM_CLK_AT91_SAM9X5_MAIN,
.id = UCLASS_CLK,
.ops = &sam9x5_main_clk_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -1,33 +1,332 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
* Master clock support for AT91 architectures.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
* Based on drivers/clk/at91/clk-master.c from Linux.
*/
#include <common.h>
#include <asm/processor.h>
#include <clk-uclass.h>
#include <common.h>
#include <dm.h>
#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
DECLARE_GLOBAL_DATA_PTR;
#include "pmc.h"
static ulong at91_master_clk_get_rate(struct clk *clk)
#define UBOOT_DM_CLK_AT91_MASTER "at91-master-clk"
#define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER "at91-sama7g5-master-clk"
#define MASTER_PRES_MASK 0x7
#define MASTER_PRES_MAX MASTER_PRES_MASK
#define MASTER_DIV_SHIFT 8
#define MASTER_DIV_MASK 0x3
#define PMC_MCR 0x30
#define PMC_MCR_ID_MSK GENMASK(3, 0)
#define PMC_MCR_CMD BIT(7)
#define PMC_MCR_DIV GENMASK(10, 8)
#define PMC_MCR_CSS GENMASK(20, 16)
#define PMC_MCR_CSS_SHIFT (16)
#define PMC_MCR_EN BIT(28)
#define PMC_MCR_ID(x) ((x) & PMC_MCR_ID_MSK)
#define MASTER_MAX_ID 4
struct clk_master {
void __iomem *base;
const struct clk_master_layout *layout;
const struct clk_master_characteristics *characteristics;
const u32 *mux_table;
const u32 *clk_mux_table;
u32 num_parents;
struct clk clk;
u8 id;
};
#define to_clk_master(_clk) container_of(_clk, struct clk_master, clk)
static inline bool clk_master_ready(struct clk_master *master)
{
return gd->arch.mck_rate_hz;
unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
unsigned int status;
pmc_read(master->base, AT91_PMC_SR, &status);
return !!(status & bit);
}
static struct clk_ops at91_master_clk_ops = {
.get_rate = at91_master_clk_get_rate,
static int clk_master_enable(struct clk *clk)
{
struct clk_master *master = to_clk_master(clk);
while (!clk_master_ready(master)) {
debug("waiting for mck %d\n", master->id);
cpu_relax();
}
return 0;
}
static ulong clk_master_get_rate(struct clk *clk)
{
struct clk_master *master = to_clk_master(clk);
const struct clk_master_layout *layout = master->layout;
const struct clk_master_characteristics *characteristics =
master->characteristics;
ulong rate = clk_get_parent_rate(clk);
unsigned int mckr;
u8 pres, div;
if (!rate)
return 0;
pmc_read(master->base, master->layout->offset, &mckr);
mckr &= layout->mask;
pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
div = (mckr >> 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 const struct clk_ops master_ops = {
.enable = clk_master_enable,
.get_rate = clk_master_get_rate,
};
static const struct udevice_id at91_master_clk_match[] = {
{ .compatible = "atmel,at91rm9200-clk-master" },
{ .compatible = "atmel,at91sam9x5-clk-master" },
{}
};
struct clk *at91_clk_register_master(void __iomem *base,
const char *name, const char * const *parent_names,
int num_parents, const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
const u32 *mux_table)
{
struct clk_master *master;
struct clk *clk;
unsigned int val;
int ret;
U_BOOT_DRIVER(atmel_at91rm9200_clk_master) = {
.name = "atmel_at91rm9200_clk_master",
if (!base || !name || !num_parents || !parent_names ||
!layout || !characteristics || !mux_table)
return ERR_PTR(-EINVAL);
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return ERR_PTR(-ENOMEM);
master->layout = layout;
master->characteristics = characteristics;
master->base = base;
master->num_parents = num_parents;
master->mux_table = mux_table;
pmc_read(master->base, master->layout->offset, &val);
clk = &master->clk;
clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER, name,
parent_names[val & AT91_PMC_CSS]);
if (ret) {
kfree(master);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_master_clk) = {
.name = UBOOT_DM_CLK_AT91_MASTER,
.id = UCLASS_CLK,
.of_match = at91_master_clk_match,
.ops = &at91_master_clk_ops,
.ops = &master_ops,
.flags = DM_FLAG_PRE_RELOC,
};
static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent)
{
struct clk_master *master = to_clk_master(clk);
int index;
index = at91_clk_mux_val_to_index(master->clk_mux_table,
master->num_parents, parent->id);
if (index < 0)
return index;
index = at91_clk_mux_index_to_val(master->mux_table,
master->num_parents, index);
if (index < 0)
return index;
pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
pmc_update_bits(master->base, PMC_MCR,
PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK,
(index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD |
PMC_MCR_ID(master->id));
return 0;
}
static int clk_sama7g5_master_enable(struct clk *clk)
{
struct clk_master *master = to_clk_master(clk);
pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
pmc_update_bits(master->base, PMC_MCR,
PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id));
return 0;
}
static int clk_sama7g5_master_disable(struct clk *clk)
{
struct clk_master *master = to_clk_master(clk);
pmc_write(master->base, PMC_MCR, master->id);
pmc_update_bits(master->base, PMC_MCR,
PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
PMC_MCR_CMD | PMC_MCR_ID(master->id));
return 0;
}
static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate)
{
struct clk_master *master = to_clk_master(clk);
ulong parent_rate = clk_get_parent_rate(clk);
ulong div, rrate;
if (!parent_rate)
return 0;
div = DIV_ROUND_CLOSEST(parent_rate, rate);
if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) {
return 0;
} else if (div == 3) {
rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX);
div = MASTER_PRES_MAX;
} else {
rrate = DIV_ROUND_CLOSEST(parent_rate, div);
div = ffs(div) - 1;
}
pmc_write(master->base, PMC_MCR, master->id);
pmc_update_bits(master->base, PMC_MCR,
PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK,
(div << MASTER_DIV_SHIFT) | PMC_MCR_CMD |
PMC_MCR_ID(master->id));
return rrate;
}
static ulong clk_sama7g5_master_get_rate(struct clk *clk)
{
struct clk_master *master = to_clk_master(clk);
ulong parent_rate = clk_get_parent_rate(clk);
unsigned int val;
ulong div;
if (!parent_rate)
return 0;
pmc_write(master->base, PMC_MCR, master->id);
pmc_read(master->base, PMC_MCR, &val);
div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
if (div == MASTER_PRES_MAX)
div = 3;
else
div = 1 << div;
return DIV_ROUND_CLOSEST(parent_rate, div);
}
static const struct clk_ops sama7g5_master_ops = {
.enable = clk_sama7g5_master_enable,
.disable = clk_sama7g5_master_disable,
.set_rate = clk_sama7g5_master_set_rate,
.get_rate = clk_sama7g5_master_get_rate,
.set_parent = clk_sama7g5_master_set_parent,
};
struct clk *at91_clk_sama7g5_register_master(void __iomem *base,
const char *name, const char * const *parent_names,
int num_parents, const u32 *mux_table, const u32 *clk_mux_table,
bool critical, u8 id)
{
struct clk_master *master;
struct clk *clk;
u32 val, index;
int ret;
if (!base || !name || !num_parents || !parent_names ||
!mux_table || !clk_mux_table || id > MASTER_MAX_ID)
return ERR_PTR(-EINVAL);
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return ERR_PTR(-ENOMEM);
master->base = base;
master->id = id;
master->mux_table = mux_table;
master->clk_mux_table = clk_mux_table;
master->num_parents = num_parents;
pmc_write(master->base, PMC_MCR, master->id);
pmc_read(master->base, PMC_MCR, &val);
index = at91_clk_mux_val_to_index(master->mux_table,
master->num_parents,
(val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT);
if (index < 0) {
kfree(master);
return ERR_PTR(index);
}
clk = &master->clk;
clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0);
ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name,
parent_names[index]);
if (ret) {
kfree(master);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_sama7g5_master_clk) = {
.name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER,
.id = UCLASS_CLK,
.ops = &sama7g5_master_ops,
.flags = DM_FLAG_PRE_RELOC,
};
const struct clk_master_layout at91rm9200_master_layout = {
.mask = 0x31F,
.pres_shift = 2,
.offset = AT91_PMC_MCKR,
};
const struct clk_master_layout at91sam9x5_master_layout = {
.mask = 0x373,
.pres_shift = 4,
.offset = AT91_PMC_MCKR,
};

View File

@@ -1,113 +1,254 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
* Peripheral clock support for AT91 architectures.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
* Based on drivers/clk/at91/clk-peripheral.c from Linux.
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <malloc.h>
#include <linux/io.h>
#include <mach/at91_pmc.h>
#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
#include "pmc.h"
#define UBOOT_DM_CLK_AT91_PERIPH "at91-periph-clk"
#define UBOOT_DM_CLK_AT91_SAM9X5_PERIPH "at91-sam9x5-periph-clk"
#define PERIPHERAL_ID_MIN 2
#define PERIPHERAL_ID_MAX 31
#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
enum periph_clk_type {
CLK_PERIPH_AT91RM9200 = 0,
CLK_PERIPH_AT91SAM9X5,
#define PERIPHERAL_MAX_SHIFT 3
struct clk_peripheral {
void __iomem *base;
struct clk clk;
u32 id;
};
/**
* sam9x5_periph_clk_bind() - for the periph clock driver
* Recursively bind its children as clk devices.
*
* @return: 0 on success, or negative error code on failure
*/
static int sam9x5_periph_clk_bind(struct udevice *dev)
#define to_clk_peripheral(_c) container_of(_c, struct clk_peripheral, clk)
struct clk_sam9x5_peripheral {
const struct clk_pcr_layout *layout;
void __iomem *base;
struct clk clk;
struct clk_range range;
u32 id;
u32 div;
bool auto_div;
};
#define to_clk_sam9x5_peripheral(_c) \
container_of(_c, struct clk_sam9x5_peripheral, clk)
static int clk_peripheral_enable(struct clk *clk)
{
return at91_clk_sub_device_bind(dev, "periph-clk");
}
struct clk_peripheral *periph = to_clk_peripheral(clk);
int offset = AT91_PMC_PCER;
u32 id = periph->id;
static const struct udevice_id sam9x5_periph_clk_match[] = {
{
.compatible = "atmel,at91rm9200-clk-peripheral",
.data = CLK_PERIPH_AT91RM9200,
},
{
.compatible = "atmel,at91sam9x5-clk-peripheral",
.data = CLK_PERIPH_AT91SAM9X5,
},
{}
};
U_BOOT_DRIVER(atmel_at91rm9200_clk_peripheral) = {
.name = "atmel_at91rm9200_clk_peripheral",
.id = UCLASS_MISC,
.of_match = sam9x5_periph_clk_match,
.bind = sam9x5_periph_clk_bind,
};
/*---------------------------------------------------------*/
static int periph_clk_enable(struct clk *clk)
{
struct pmc_platdata *plat = dev_get_platdata(clk->dev);
struct at91_pmc *pmc = plat->reg_base;
enum periph_clk_type clk_type;
void *addr;
if (clk->id < PERIPHERAL_ID_MIN)
return -1;
clk_type = dev_get_driver_data(dev_get_parent(clk->dev));
if (clk_type == CLK_PERIPH_AT91RM9200) {
addr = &pmc->pcer;
if (clk->id > PERIPHERAL_ID_MAX)
addr = &pmc->pcer1;
setbits_le32(addr, PERIPHERAL_MASK(clk->id));
} else {
writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
setbits_le32(&pmc->pcr,
AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN);
}
if (id < PERIPHERAL_ID_MIN)
return 0;
if (id > PERIPHERAL_ID_MAX)
offset = AT91_PMC_PCER1;
pmc_write(periph->base, offset, PERIPHERAL_MASK(id));
return 0;
}
static ulong periph_get_rate(struct clk *clk)
static int clk_peripheral_disable(struct clk *clk)
{
struct udevice *dev;
struct clk clk_dev;
ulong clk_rate;
int ret;
struct clk_peripheral *periph = to_clk_peripheral(clk);
int offset = AT91_PMC_PCDR;
u32 id = periph->id;
dev = dev_get_parent(clk->dev);
if (id < PERIPHERAL_ID_MIN)
return -EINVAL;
ret = clk_get_by_index(dev, 0, &clk_dev);
if (ret)
return ret;
if (id > PERIPHERAL_ID_MAX)
offset = AT91_PMC_PCDR1;
pmc_write(periph->base, offset, PERIPHERAL_MASK(id));
clk_rate = clk_get_rate(&clk_dev);
clk_free(&clk_dev);
return clk_rate;
return 0;
}
static struct clk_ops periph_clk_ops = {
.of_xlate = at91_clk_of_xlate,
.enable = periph_clk_enable,
.get_rate = periph_get_rate,
static const struct clk_ops peripheral_ops = {
.enable = clk_peripheral_enable,
.disable = clk_peripheral_disable,
.get_rate = clk_generic_get_rate,
};
U_BOOT_DRIVER(clk_periph) = {
.name = "periph-clk",
.id = UCLASS_CLK,
.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
.probe = at91_clk_probe,
.ops = &periph_clk_ops,
struct clk *
at91_clk_register_peripheral(void __iomem *base, const char *name,
const char *parent_name, u32 id)
{
struct clk_peripheral *periph;
struct clk *clk;
int ret;
if (!base || !name || !parent_name || id > PERIPHERAL_ID_MAX)
return ERR_PTR(-EINVAL);
periph = kzalloc(sizeof(*periph), GFP_KERNEL);
if (!periph)
return ERR_PTR(-ENOMEM);
periph->id = id;
periph->base = base;
clk = &periph->clk;
clk->flags = CLK_GET_RATE_NOCACHE;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_PERIPH, name, parent_name);
if (ret) {
kfree(periph);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_periph_clk) = {
.name = UBOOT_DM_CLK_AT91_PERIPH,
.id = UCLASS_CLK,
.ops = &peripheral_ops,
.flags = DM_FLAG_PRE_RELOC,
};
static int clk_sam9x5_peripheral_enable(struct clk *clk)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
if (periph->id < PERIPHERAL_ID_MIN)
return 0;
pmc_write(periph->base, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
pmc_update_bits(periph->base, periph->layout->offset,
periph->layout->cmd | AT91_PMC_PCR_EN,
periph->layout->cmd | AT91_PMC_PCR_EN);
return 0;
}
static int clk_sam9x5_peripheral_disable(struct clk *clk)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
if (periph->id < PERIPHERAL_ID_MIN)
return -EINVAL;
pmc_write(periph->base, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
pmc_update_bits(periph->base, periph->layout->offset,
AT91_PMC_PCR_EN | periph->layout->cmd,
periph->layout->cmd);
return 0;
}
static ulong clk_sam9x5_peripheral_get_rate(struct clk *clk)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
ulong parent_rate = clk_get_parent_rate(clk);
u32 val, shift = ffs(periph->layout->div_mask) - 1;
if (!parent_rate)
return 0;
pmc_write(periph->base, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
pmc_read(periph->base, periph->layout->offset, &val);
shift = (val & periph->layout->div_mask) >> shift;
return parent_rate >> shift;
}
static ulong clk_sam9x5_peripheral_set_rate(struct clk *clk, ulong rate)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
ulong parent_rate = clk_get_parent_rate(clk);
int shift;
if (!parent_rate)
return 0;
if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
if (parent_rate == rate)
return rate;
else
return 0;
}
if (periph->range.max && rate > periph->range.max)
return 0;
for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
if (parent_rate >> shift <= rate)
break;
}
if (shift == PERIPHERAL_MAX_SHIFT + 1)
return 0;
pmc_write(periph->base, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
pmc_update_bits(periph->base, periph->layout->offset,
periph->layout->div_mask | periph->layout->cmd,
(shift << (ffs(periph->layout->div_mask) - 1)) |
periph->layout->cmd);
return parent_rate >> shift;
}
static const struct clk_ops sam9x5_peripheral_ops = {
.enable = clk_sam9x5_peripheral_enable,
.disable = clk_sam9x5_peripheral_disable,
.get_rate = clk_sam9x5_peripheral_get_rate,
.set_rate = clk_sam9x5_peripheral_set_rate,
};
struct clk *
at91_clk_register_sam9x5_peripheral(void __iomem *base,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
u32 id, const struct clk_range *range)
{
struct clk_sam9x5_peripheral *periph;
struct clk *clk;
int ret;
if (!base || !layout || !name || !parent_name || !range)
return ERR_PTR(-EINVAL);
periph = kzalloc(sizeof(*periph), GFP_KERNEL);
if (!periph)
return ERR_PTR(-ENOMEM);
periph->id = id;
periph->base = base;
periph->layout = layout;
periph->range = *range;
clk = &periph->clk;
clk->flags = CLK_GET_RATE_NOCACHE;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X5_PERIPH, name,
parent_name);
if (ret) {
kfree(periph);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_sam9x5_periph_clk) = {
.name = UBOOT_DM_CLK_AT91_SAM9X5_PERIPH,
.id = UCLASS_CLK,
.ops = &sam9x5_peripheral_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -1,54 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.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,
};

View File

@@ -1,85 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018 Microhip / Atmel Corporation
* Wenyou.Yang <wenyou.yang@microchip.com>
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm/device.h>
#include <linux/io.h>
#include <mach/at91_pmc.h>
#include "pmc.h"
static int at91_plladiv_clk_enable(struct clk *clk)
{
return 0;
}
static ulong at91_plladiv_clk_get_rate(struct clk *clk)
{
struct pmc_platdata *plat = dev_get_platdata(clk->dev);
struct at91_pmc *pmc = plat->reg_base;
struct clk source;
ulong clk_rate;
int ret;
ret = clk_get_by_index(clk->dev, 0, &source);
if (ret)
return -EINVAL;
clk_rate = clk_get_rate(&source);
if (readl(&pmc->mckr) & AT91_PMC_MCKR_PLLADIV_2)
clk_rate /= 2;
return clk_rate;
}
static ulong at91_plladiv_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 clk source;
ulong parent_rate;
int ret;
ret = clk_get_by_index(clk->dev, 0, &source);
if (ret)
return -EINVAL;
parent_rate = clk_get_rate(&source);
if ((parent_rate != rate) && ((parent_rate) / 2 != rate))
return -EINVAL;
if (parent_rate != rate) {
writel((readl(&pmc->mckr) | AT91_PMC_MCKR_PLLADIV_2),
&pmc->mckr);
}
return 0;
}
static struct clk_ops at91_plladiv_clk_ops = {
.enable = at91_plladiv_clk_enable,
.get_rate = at91_plladiv_clk_get_rate,
.set_rate = at91_plladiv_clk_set_rate,
};
static int at91_plladiv_clk_probe(struct udevice *dev)
{
return at91_pmc_core_probe(dev);
}
static const struct udevice_id at91_plladiv_clk_match[] = {
{ .compatible = "atmel,at91sam9x5-clk-plldiv" },
{}
};
U_BOOT_DRIVER(at91_plladiv_clk) = {
.name = "at91-plladiv-clk",
.id = UCLASS_CLK,
.of_match = at91_plladiv_clk_match,
.probe = at91_plladiv_clk_probe,
.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
.ops = &at91_plladiv_clk_ops,
};

View File

@@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Programmable clock support for AT91 architectures.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
* Based on drivers/clk/at91/clk-programmable.c from Linux.
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
#include "pmc.h"
#define UBOOT_DM_CLK_AT91_PROG "at91-prog-clk"
#define PROG_ID_MAX 7
#define PROG_STATUS_MASK(id) (1 << ((id) + 8))
#define PROG_PRES(_l, _p) (((_p) >> (_l)->pres_shift) & (_l)->pres_mask)
#define PROG_MAX_RM9200_CSS 3
struct clk_programmable {
void __iomem *base;
const u32 *clk_mux_table;
const u32 *mux_table;
const struct clk_programmable_layout *layout;
u32 num_parents;
struct clk clk;
u8 id;
};
#define to_clk_programmable(_c) container_of(_c, struct clk_programmable, clk)
static ulong clk_programmable_get_rate(struct clk *clk)
{
struct clk_programmable *prog = to_clk_programmable(clk);
const struct clk_programmable_layout *layout = prog->layout;
ulong rate, parent_rate = clk_get_parent_rate(clk);
unsigned int pckr;
pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &pckr);
if (layout->is_pres_direct)
rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
else
rate = parent_rate >> PROG_PRES(layout, pckr);
return rate;
}
static int clk_programmable_set_parent(struct clk *clk, struct clk *parent)
{
struct clk_programmable *prog = to_clk_programmable(clk);
const struct clk_programmable_layout *layout = prog->layout;
unsigned int mask = layout->css_mask;
int index;
index = at91_clk_mux_val_to_index(prog->clk_mux_table,
prog->num_parents, parent->id);
if (index < 0)
return index;
index = at91_clk_mux_index_to_val(prog->mux_table, prog->num_parents,
index);
if (index < 0)
return index;
if (layout->have_slck_mck)
mask |= AT91_PMC_CSSMCK_MCK;
if (index > layout->css_mask) {
if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
return -EINVAL;
index |= AT91_PMC_CSSMCK_MCK;
}
pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), mask, index);
return 0;
}
static ulong clk_programmable_set_rate(struct clk *clk, ulong rate)
{
struct clk_programmable *prog = to_clk_programmable(clk);
const struct clk_programmable_layout *layout = prog->layout;
ulong parent_rate = clk_get_parent_rate(clk);
ulong div = parent_rate / rate;
int shift = 0;
if (!parent_rate || !div)
return -EINVAL;
if (layout->is_pres_direct) {
shift = div - 1;
if (shift > layout->pres_mask)
return -EINVAL;
} else {
shift = fls(div) - 1;
if (div != (1 << shift))
return -EINVAL;
if (shift >= layout->pres_mask)
return -EINVAL;
}
pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id),
layout->pres_mask << layout->pres_shift,
shift << layout->pres_shift);
if (layout->is_pres_direct)
return (parent_rate / shift + 1);
return parent_rate >> shift;
}
static const struct clk_ops programmable_ops = {
.get_rate = clk_programmable_get_rate,
.set_parent = clk_programmable_set_parent,
.set_rate = clk_programmable_set_rate,
};
struct clk *at91_clk_register_programmable(void __iomem *base, const char *name,
const char *const *parent_names, u8 num_parents, u8 id,
const struct clk_programmable_layout *layout,
const u32 *clk_mux_table, const u32 *mux_table)
{
struct clk_programmable *prog;
struct clk *clk;
u32 val, tmp;
int ret;
if (!base || !name || !parent_names || !num_parents ||
!layout || !clk_mux_table || !mux_table || id > PROG_ID_MAX)
return ERR_PTR(-EINVAL);
prog = kzalloc(sizeof(*prog), GFP_KERNEL);
if (!prog)
return ERR_PTR(-ENOMEM);
prog->id = id;
prog->layout = layout;
prog->base = base;
prog->clk_mux_table = clk_mux_table;
prog->mux_table = mux_table;
prog->num_parents = num_parents;
pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &tmp);
val = tmp & prog->layout->css_mask;
if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !val)
ret = PROG_MAX_RM9200_CSS + 1;
else
ret = at91_clk_mux_val_to_index(prog->mux_table,
prog->num_parents, val);
if (ret < 0) {
kfree(prog);
return ERR_PTR(ret);
}
clk = &prog->clk;
clk->flags = CLK_GET_RATE_NOCACHE;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_PROG, name,
parent_names[ret]);
if (ret) {
kfree(prog);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_prog_clk) = {
.name = UBOOT_DM_CLK_AT91_PROG,
.id = UCLASS_CLK,
.ops = &programmable_ops,
.flags = DM_FLAG_PRE_RELOC,
};
const struct clk_programmable_layout at91rm9200_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 2,
.css_mask = 0x3,
.have_slck_mck = 0,
.is_pres_direct = 0,
};
const struct clk_programmable_layout at91sam9g45_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 2,
.css_mask = 0x3,
.have_slck_mck = 1,
.is_pres_direct = 0,
};
const struct clk_programmable_layout at91sam9x5_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 4,
.css_mask = 0x7,
.have_slck_mck = 0,
.is_pres_direct = 0,
};

View File

@@ -0,0 +1,442 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* SAM9X60's PLL clock support.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
* Based on drivers/clk/at91/clk-sam9x60-pll.c from Linux.
*
*/
#include <asm/processor.h>
#include <common.h>
#include <clk-uclass.h>
#include <div64.h>
#include <dm.h>
#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
#include <linux/delay.h>
#include "pmc.h"
#define UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL "at91-sam9x60-div-pll-clk"
#define UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL "at91-sam9x60-frac-pll-clk"
#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
#define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
#define UPLL_DIV 2
#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
#define FCORE_MIN (600000000)
#define FCORE_MAX (1200000000)
#define PLL_MAX_ID 7
struct sam9x60_pll {
void __iomem *base;
const struct clk_pll_characteristics *characteristics;
const struct clk_pll_layout *layout;
struct clk clk;
u8 id;
};
#define to_sam9x60_pll(_clk) container_of(_clk, struct sam9x60_pll, clk)
static inline bool sam9x60_pll_ready(void __iomem *base, int id)
{
unsigned int status;
pmc_read(base, AT91_PMC_PLL_ISR0, &status);
return !!(status & BIT(id));
}
static long sam9x60_frac_pll_compute_mul_frac(u32 *mul, u32 *frac, ulong rate,
ulong parent_rate)
{
unsigned long tmprate, remainder;
unsigned long nmul = 0;
unsigned long nfrac = 0;
if (rate < FCORE_MIN || rate > FCORE_MAX)
return -ERANGE;
/*
* Calculate the multiplier associated with the current
* divider that provide the closest rate to the requested one.
*/
nmul = mult_frac(rate, 1, parent_rate);
tmprate = mult_frac(parent_rate, nmul, 1);
remainder = rate - tmprate;
if (remainder) {
nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
parent_rate);
tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
(1 << 22));
}
/* Check if resulted rate is valid. */
if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
return -ERANGE;
*mul = nmul - 1;
*frac = nfrac;
return tmprate;
}
static ulong sam9x60_frac_pll_set_rate(struct clk *clk, ulong rate)
{
struct sam9x60_pll *pll = to_sam9x60_pll(clk);
void __iomem *base = pll->base;
ulong parent_rate = clk_get_parent_rate(clk);
u32 nmul, cmul, nfrac, cfrac, val;
bool ready = sam9x60_pll_ready(base, pll->id);
long ret;
if (!parent_rate)
return 0;
ret = sam9x60_frac_pll_compute_mul_frac(&nmul, &nfrac, rate,
parent_rate);
if (ret < 0)
return 0;
pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
pll->id);
pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
cmul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift;
cfrac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift;
/* Check against current values. */
if (sam9x60_pll_ready(base, pll->id) &&
nmul == cmul && nfrac == cfrac)
return 0;
/* Update it to hardware. */
pmc_write(base, AT91_PMC_PLL_CTRL1,
(nmul << pll->layout->mul_shift) |
(nfrac << pll->layout->frac_shift));
pmc_update_bits(base, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | pll->id);
while (ready && !sam9x60_pll_ready(base, pll->id)) {
debug("waiting for pll %u...\n", pll->id);
cpu_relax();
}
return parent_rate * (nmul + 1) + ((u64)parent_rate * nfrac >> 22);
}
static ulong sam9x60_frac_pll_get_rate(struct clk *clk)
{
struct sam9x60_pll *pll = to_sam9x60_pll(clk);
void __iomem *base = pll->base;
ulong parent_rate = clk_get_parent_rate(clk);
u32 mul, frac, val;
if (!parent_rate)
return 0;
pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
pll->id);
pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
mul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift;
frac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift;
return (parent_rate * (mul + 1) + ((u64)parent_rate * frac >> 22));
}
static int sam9x60_frac_pll_enable(struct clk *clk)
{
struct sam9x60_pll *pll = to_sam9x60_pll(clk);
void __iomem *base = pll->base;
unsigned int val;
ulong crate;
crate = sam9x60_frac_pll_get_rate(clk);
if (crate < FCORE_MIN || crate > FCORE_MAX)
return -ERANGE;
pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
pll->id);
pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
if (sam9x60_pll_ready(base, pll->id))
return 0;
pmc_update_bits(base, AT91_PMC_PLL_UPDT,
AT91_PMC_PMM_UPDT_STUPTIM_MSK |
AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_STUPTIM(0x3f) | pll->id);
/* Recommended value for AT91_PMC_PLL_ACR */
if (pll->characteristics->upll)
val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
else
val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
pmc_write(base, AT91_PMC_PLL_ACR, val);
if (pll->characteristics->upll) {
/* Enable the UTMI internal bandgap */
val |= AT91_PMC_PLL_ACR_UTMIBG;
pmc_write(base, AT91_PMC_PLL_ACR, val);
udelay(10);
/* Enable the UTMI internal regulator */
val |= AT91_PMC_PLL_ACR_UTMIVR;
pmc_write(base, AT91_PMC_PLL_ACR, val);
udelay(10);
pmc_update_bits(base, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE |
AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | pll->id);
}
pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);
pmc_update_bits(base, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | pll->id);
while (!sam9x60_pll_ready(base, pll->id)) {
debug("waiting for pll %u...\n", pll->id);
cpu_relax();
}
return 0;
}
static int sam9x60_frac_pll_disable(struct clk *clk)
{
struct sam9x60_pll *pll = to_sam9x60_pll(clk);
void __iomem *base = pll->base;
pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
pll->id);
pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENPLL, 0);
if (pll->characteristics->upll)
pmc_update_bits(base, AT91_PMC_PLL_ACR,
AT91_PMC_PLL_ACR_UTMIBG |
AT91_PMC_PLL_ACR_UTMIVR, 0);
pmc_update_bits(base, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | pll->id);
return 0;
}
static const struct clk_ops sam9x60_frac_pll_ops = {
.enable = sam9x60_frac_pll_enable,
.disable = sam9x60_frac_pll_disable,
.set_rate = sam9x60_frac_pll_set_rate,
.get_rate = sam9x60_frac_pll_get_rate,
};
static int sam9x60_div_pll_enable(struct clk *clk)
{
struct sam9x60_pll *pll = to_sam9x60_pll(clk);
void __iomem *base = pll->base;
unsigned int val;
pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
pll->id);
pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
/* Stop if enabled. */
if (val & pll->layout->endiv_mask)
return 0;
pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
pll->layout->endiv_mask,
(1 << pll->layout->endiv_shift));
pmc_update_bits(base, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | pll->id);
while (!sam9x60_pll_ready(base, pll->id)) {
debug("waiting for pll %u...\n", pll->id);
cpu_relax();
}
return 0;
}
static int sam9x60_div_pll_disable(struct clk *clk)
{
struct sam9x60_pll *pll = to_sam9x60_pll(clk);
void __iomem *base = pll->base;
pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
pll->id);
pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
pll->layout->endiv_mask, 0);
pmc_update_bits(base, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | pll->id);
return 0;
}
static ulong sam9x60_div_pll_set_rate(struct clk *clk, ulong rate)
{
struct sam9x60_pll *pll = to_sam9x60_pll(clk);
void __iomem *base = pll->base;
const struct clk_pll_characteristics *characteristics =
pll->characteristics;
ulong parent_rate = clk_get_parent_rate(clk);
u8 div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate) - 1;
ulong req_rate = parent_rate / (div + 1);
bool ready = sam9x60_pll_ready(base, pll->id);
u32 val;
if (!parent_rate || div > pll->layout->div_mask ||
req_rate < characteristics->output[0].min ||
req_rate > characteristics->output[0].max)
return 0;
pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
pll->id);
pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
/* Compare against current value. */
if (div == ((val & pll->layout->div_mask) >> pll->layout->div_shift))
return 0;
/* Update it to hardware. */
pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
pll->layout->div_mask,
div << pll->layout->div_shift);
pmc_update_bits(base, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | pll->id);
while (ready && !sam9x60_pll_ready(base, pll->id)) {
debug("waiting for pll %u...\n", pll->id);
cpu_relax();
}
return req_rate;
}
static ulong sam9x60_div_pll_get_rate(struct clk *clk)
{
struct sam9x60_pll *pll = to_sam9x60_pll(clk);
void __iomem *base = pll->base;
ulong parent_rate = clk_get_parent_rate(clk);
u32 val;
u8 div;
if (!parent_rate)
return 0;
pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
pll->id);
pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
div = (val & pll->layout->div_mask) >> pll->layout->div_shift;
return parent_rate / (div + 1);
}
static const struct clk_ops sam9x60_div_pll_ops = {
.enable = sam9x60_div_pll_enable,
.disable = sam9x60_div_pll_disable,
.set_rate = sam9x60_div_pll_set_rate,
.get_rate = sam9x60_div_pll_get_rate,
};
static struct clk *
sam9x60_clk_register_pll(void __iomem *base, const char *type,
const char *name, const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, u32 flags)
{
struct sam9x60_pll *pll;
struct clk *clk;
int ret;
if (!base || !type || !name || !parent_name || !characteristics ||
!layout || id > PLL_MAX_ID)
return ERR_PTR(-EINVAL);
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
return ERR_PTR(-ENOMEM);
pll->id = id;
pll->characteristics = characteristics;
pll->layout = layout;
pll->base = base;
clk = &pll->clk;
clk->flags = flags;
ret = clk_register(clk, type, name, parent_name);
if (ret) {
kfree(pll);
clk = ERR_PTR(ret);
}
return clk;
}
struct clk *
sam9x60_clk_register_div_pll(void __iomem *base, const char *name,
const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, bool critical)
{
return sam9x60_clk_register_pll(base,
UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL, name, parent_name, id,
characteristics, layout,
CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0));
}
struct clk *
sam9x60_clk_register_frac_pll(void __iomem *base, const char *name,
const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, bool critical)
{
return sam9x60_clk_register_pll(base,
UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL, name, parent_name, id,
characteristics, layout,
CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0));
}
U_BOOT_DRIVER(at91_sam9x60_div_pll_clk) = {
.name = UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL,
.id = UCLASS_CLK,
.ops = &sam9x60_div_pll_ops,
.flags = DM_FLAG_PRE_RELOC,
};
U_BOOT_DRIVER(at91_sam9x60_frac_pll_clk) = {
.name = UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL,
.id = UCLASS_CLK,
.ops = &sam9x60_frac_pll_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -1,36 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.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,
};

View File

@@ -1,111 +1,112 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
* System clock support for AT91 architectures.
*
* Copyright (C) Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
* Based on drivers/clk/at91/clk-system.c from Linux.
*/
#include <asm/processor.h>
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <mach/at91_pmc.h>
#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
#include "pmc.h"
#define UBOOT_DM_CLK_AT91_SYSTEM "at91-system-clk"
#define SYSTEM_MAX_ID 31
/**
* at91_system_clk_bind() - for the system clock driver
* Recursively bind its children as clk devices.
*
* @return: 0 on success, or negative error code on failure
*/
static int at91_system_clk_bind(struct udevice *dev)
{
return at91_clk_sub_device_bind(dev, "system-clk");
}
static const struct udevice_id at91_system_clk_match[] = {
{ .compatible = "atmel,at91rm9200-clk-system" },
{}
struct clk_system {
void __iomem *base;
struct clk clk;
u8 id;
};
U_BOOT_DRIVER(at91_system_clk) = {
.name = "at91-system-clk",
.id = UCLASS_MISC,
.of_match = at91_system_clk_match,
.bind = at91_system_clk_bind,
};
/*----------------------------------------------------------*/
#define to_clk_system(_c) container_of(_c, struct clk_system, clk)
static inline int is_pck(int id)
{
return (id >= 8) && (id <= 15);
}
static ulong system_clk_get_rate(struct clk *clk)
static inline bool clk_system_ready(void __iomem *base, int id)
{
struct clk clk_dev;
int ret;
unsigned int status;
ret = clk_get_by_index(clk->dev, 0, &clk_dev);
if (ret)
return -EINVAL;
pmc_read(base, AT91_PMC_SR, &status);
return clk_get_rate(&clk_dev);
return !!(status & (1 << id));
}
static ulong system_clk_set_rate(struct clk *clk, ulong rate)
static int clk_system_enable(struct clk *clk)
{
struct clk clk_dev;
int ret;
struct clk_system *sys = to_clk_system(clk);
ret = clk_get_by_index(clk->dev, 0, &clk_dev);
if (ret)
return -EINVAL;
pmc_write(sys->base, AT91_PMC_SCER, 1 << sys->id);
return clk_set_rate(&clk_dev, rate);
}
static int 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))
if (!is_pck(sys->id))
return 0;
while (!(readl(&pmc->sr) & mask))
;
while (!clk_system_ready(sys->base, sys->id)) {
debug("waiting for pck%u\n", sys->id);
cpu_relax();
}
return 0;
}
static struct clk_ops system_clk_ops = {
.of_xlate = at91_clk_of_xlate,
.get_rate = system_clk_get_rate,
.set_rate = system_clk_set_rate,
.enable = system_clk_enable,
static int clk_system_disable(struct clk *clk)
{
struct clk_system *sys = to_clk_system(clk);
pmc_write(sys->base, AT91_PMC_SCDR, 1 << sys->id);
return 0;
}
static const struct clk_ops system_ops = {
.enable = clk_system_enable,
.disable = clk_system_disable,
.get_rate = clk_generic_get_rate,
};
U_BOOT_DRIVER(system_clk) = {
.name = "system-clk",
struct clk *at91_clk_register_system(void __iomem *base, const char *name,
const char *parent_name, u8 id)
{
struct clk_system *sys;
struct clk *clk;
int ret;
if (!base || !name || !parent_name || id > SYSTEM_MAX_ID)
return ERR_PTR(-EINVAL);
sys = kzalloc(sizeof(*sys), GFP_KERNEL);
if (!sys)
return ERR_PTR(-ENOMEM);
sys->id = id;
sys->base = base;
clk = &sys->clk;
clk->flags = CLK_GET_RATE_NOCACHE;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_SYSTEM, name, parent_name);
if (ret) {
kfree(sys);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_system_clk) = {
.name = UBOOT_DM_CLK_AT91_SYSTEM,
.id = UCLASS_CLK,
.probe = at91_clk_probe,
.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
.ops = &system_clk_ops,
.ops = &system_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -1,147 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018 Microhip / Atmel Corporation
* Wenyou.Yang <wenyou.yang@microchip.com>
*/
#include <common.h>
#include <clk-uclass.h>
#include <log.h>
#include <dm/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <mach/at91_pmc.h>
#include "pmc.h"
DECLARE_GLOBAL_DATA_PTR;
#define AT91_USB_CLK_SOURCE_MAX 2
#define AT91_USB_CLK_MAX_DIV 15
struct at91_usb_clk_priv {
u32 num_clksource;
};
static ulong at91_usb_clk_get_rate(struct clk *clk)
{
struct pmc_platdata *plat = dev_get_platdata(clk->dev);
struct at91_pmc *pmc = plat->reg_base;
struct clk source;
u32 tmp, usbdiv;
u8 source_index;
int ret;
tmp = readl(&pmc->pcr);
source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) &
AT91_PMC_USB_USBS_MASK;
usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK;
ret = clk_get_by_index(clk->dev, source_index, &source);
if (ret)
return 0;
return clk_get_rate(&source) / (usbdiv + 1);
}
static ulong at91_usb_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 at91_usb_clk_priv *priv = dev_get_priv(clk->dev);
struct clk source, best_source;
ulong tmp_rate, best_rate = rate, source_rate;
int tmp_diff, best_diff = -1;
u32 div, best_div = 0;
u8 best_source_index = 0;
u8 i;
u32 tmp;
int ret;
for (i = 0; i < priv->num_clksource; i++) {
ret = clk_get_by_index(clk->dev, i, &source);
if (ret)
return ret;
source_rate = clk_get_rate(&source);
if (IS_ERR_VALUE(source_rate))
return source_rate;
for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) {
tmp_rate = DIV_ROUND_CLOSEST(source_rate, div);
tmp_diff = abs(rate - tmp_rate);
if (best_diff < 0 || best_diff > tmp_diff) {
best_rate = tmp_rate;
best_diff = tmp_diff;
best_div = div - 1;
best_source = source;
best_source_index = i;
}
if (!best_diff || tmp_rate < rate)
break;
}
if (!best_diff)
break;
}
debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n",
best_source.dev->name, best_rate, best_div);
ret = clk_enable(&best_source);
if (ret)
return ret;
tmp = AT91_PMC_USB_USBS_(best_source_index) |
AT91_PMC_USB_DIV_(best_div);
writel(tmp, &pmc->usb);
return 0;
}
static struct clk_ops at91_usb_clk_ops = {
.get_rate = at91_usb_clk_get_rate,
.set_rate = at91_usb_clk_set_rate,
};
static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev)
{
struct at91_usb_clk_priv *priv = dev_get_priv(dev);
u32 cells[AT91_USB_CLK_SOURCE_MAX];
u32 num_clksource;
num_clksource = fdtdec_get_int_array_count(gd->fdt_blob,
dev_of_offset(dev),
"clocks", cells,
AT91_USB_CLK_SOURCE_MAX);
if (!num_clksource)
return -1;
priv->num_clksource = num_clksource;
return 0;
}
static int at91_usb_clk_probe(struct udevice *dev)
{
return at91_pmc_core_probe(dev);
}
static const struct udevice_id at91_usb_clk_match[] = {
{ .compatible = "atmel,at91sam9x5-clk-usb" },
{}
};
U_BOOT_DRIVER(at91_usb_clk) = {
.name = "at91-usb-clk",
.id = UCLASS_CLK,
.of_match = at91_usb_clk_match,
.probe = at91_usb_clk_probe,
.ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct at91_usb_clk_priv),
.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
.ops = &at91_usb_clk_ops,
};

View File

@@ -1,49 +1,65 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
* UTMI clock support for AT91 architectures.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
* Based on drivers/clk/at91/clk-utmi.c from Linux.
*/
#include <asm/processor.h>
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <syscon.h>
#include <linux/io.h>
#include <mach/at91_pmc.h>
#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
#include <mach/at91_sfr.h>
#include <regmap.h>
#include <syscon.h>
#include "pmc.h"
#define UBOOT_DM_CLK_AT91_UTMI "at91-utmi-clk"
#define UBOOT_DM_CLK_AT91_SAMA7G5_UTMI "at91-sama7g5-utmi-clk"
/*
* 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;
int timeout = 2000000;
struct clk_utmi {
void __iomem *base;
struct regmap *regmap_sfr;
struct clk clk;
};
if (readl(&pmc->sr) & AT91_PMC_LOCKU)
return 0;
#define to_clk_utmi(_clk) container_of(_clk, struct clk_utmi, clk)
static inline bool clk_utmi_ready(struct regmap *regmap)
{
unsigned int status;
pmc_read(regmap, AT91_PMC_SR, &status);
return !!(status & AT91_PMC_LOCKU);
}
static int clk_utmi_enable(struct clk *clk)
{
struct clk_utmi *utmi = to_clk_utmi(clk);
unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
AT91_PMC_BIASEN;
unsigned int utmi_ref_clk_freq;
ulong parent_rate = clk_get_parent_rate(clk);
/*
* 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) {
switch (parent_rate) {
case 12000000:
utmi_ref_clk_freq = 0;
break;
@@ -61,82 +77,158 @@ static int utmi_clk_enable(struct clk *clk)
utmi_ref_clk_freq = 3;
break;
default:
printf("UTMICK: unsupported mainck rate\n");
debug("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;
if (utmi->regmap_sfr) {
regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
} else if (utmi_ref_clk_freq) {
printf("UTMICK: sfr node required\n");
debug("UTMICK: sfr node required\n");
return -EINVAL;
}
tmp = readl(&pmc->uckr);
tmp |= AT91_PMC_UPLLEN |
AT91_PMC_UPLLCOUNT |
AT91_PMC_BIASEN;
writel(tmp, &pmc->uckr);
pmc_update_bits(utmi->base, AT91_CKGR_UCKR, uckr, uckr);
while ((--timeout) && !(readl(&pmc->sr) & AT91_PMC_LOCKU))
;
if (!timeout) {
printf("UTMICK: timeout waiting for UPLL lock\n");
return -ETIMEDOUT;
while (!clk_utmi_ready(utmi->base)) {
debug("waiting for utmi...\n");
cpu_relax();
}
return 0;
}
static ulong utmi_clk_get_rate(struct clk *clk)
static int clk_utmi_disable(struct clk *clk)
{
struct clk_utmi *utmi = to_clk_utmi(clk);
pmc_update_bits(utmi->base, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0);
return 0;
}
static ulong clk_utmi_get_rate(struct clk *clk)
{
/* UTMI clk rate is fixed. */
return UTMI_RATE;
}
static struct clk_ops utmi_clk_ops = {
.enable = utmi_clk_enable,
.get_rate = utmi_clk_get_rate,
static const struct clk_ops utmi_ops = {
.enable = clk_utmi_enable,
.disable = clk_utmi_disable,
.get_rate = clk_utmi_get_rate,
};
static int utmi_clk_ofdata_to_platdata(struct udevice *dev)
struct clk *at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
const char *name, const char *parent_name)
{
struct pmc_platdata *plat = dev_get_platdata(dev);
struct udevice *syscon;
struct clk_utmi *utmi;
struct clk *clk;
int ret;
uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
"regmap-sfr", &syscon);
if (!base || !dev || !name || !parent_name)
return ERR_PTR(-EINVAL);
if (syscon)
plat->regmap_sfr = syscon_get_regmap(syscon);
ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
"regmap-sfr", &syscon);
if (ret)
return ERR_PTR(ret);
utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
if (!utmi)
return ERR_PTR(-ENOMEM);
utmi->base = base;
utmi->regmap_sfr = syscon_get_regmap(syscon);
if (!utmi->regmap_sfr) {
kfree(utmi);
return ERR_PTR(-ENODEV);
}
clk = &utmi->clk;
clk->flags = CLK_GET_RATE_NOCACHE;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_UTMI, name, parent_name);
if (ret) {
kfree(utmi);
clk = ERR_PTR(ret);
}
return clk;
}
U_BOOT_DRIVER(at91_utmi_clk) = {
.name = UBOOT_DM_CLK_AT91_UTMI,
.id = UCLASS_CLK,
.ops = &utmi_ops,
.flags = DM_FLAG_PRE_RELOC,
};
static int clk_utmi_sama7g5_enable(struct clk *clk)
{
struct clk_utmi *utmi = to_clk_utmi(clk);
ulong parent_rate = clk_get_parent_rate(clk);
unsigned int val;
switch (parent_rate) {
case 16000000:
val = 0;
break;
case 20000000:
val = 2;
break;
case 24000000:
val = 3;
break;
case 32000000:
val = 5;
break;
default:
debug("UTMICK: unsupported main_xtal rate\n");
return -EINVAL;
}
pmc_write(utmi->base, AT91_PMC_XTALF, val);
return 0;
}
static int utmi_clk_probe(struct udevice *dev)
static const struct clk_ops sama7g5_utmi_ops = {
.enable = clk_utmi_sama7g5_enable,
.get_rate = clk_utmi_get_rate,
};
struct clk *at91_clk_sama7g5_register_utmi(void __iomem *base,
const char *name, const char *parent_name)
{
return at91_pmc_core_probe(dev);
struct clk_utmi *utmi;
struct clk *clk;
int ret;
if (!base || !name || !parent_name)
return ERR_PTR(-EINVAL);
utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
if (!utmi)
return ERR_PTR(-ENOMEM);
utmi->base = base;
clk = &utmi->clk;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_UTMI, name,
parent_name);
if (ret) {
kfree(utmi);
clk = ERR_PTR(ret);
}
return clk;
}
static const struct udevice_id utmi_clk_match[] = {
{ .compatible = "atmel,at91sam9x5-clk-utmi" },
{}
};
U_BOOT_DRIVER(at91sam9x5_utmi_clk) = {
.name = "at91sam9x5-utmi-clk",
U_BOOT_DRIVER(at91_sama7g5_utmi_clk) = {
.name = UBOOT_DM_CLK_AT91_SAMA7G5_UTMI,
.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,
.ops = &sama7g5_utmi_ops,
.flags = DM_FLAG_PRE_RELOC,
};

1023
drivers/clk/at91/compat.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,119 +4,167 @@
* Wenyou.Yang <wenyou.yang@atmel.com>
*/
#include <common.h>
#include <asm/io.h>
#include <clk-uclass.h>
#include <dm.h>
#include <log.h>
#include <dm/lists.h>
#include <dm/util.h>
#include <common.h>
#include "pmc.h"
DECLARE_GLOBAL_DATA_PTR;
static const struct udevice_id at91_pmc_match[] = {
{ .compatible = "atmel,at91rm9200-pmc" },
{ .compatible = "atmel,at91sam9260-pmc" },
{ .compatible = "atmel,at91sam9g45-pmc" },
{ .compatible = "atmel,at91sam9n12-pmc" },
{ .compatible = "atmel,at91sam9x5-pmc" },
{ .compatible = "atmel,sama5d3-pmc" },
{ .compatible = "atmel,sama5d2-pmc" },
{}
};
U_BOOT_DRIVER(atmel_at91rm9200_pmc) = {
.name = "atmel_at91rm9200_pmc",
.id = UCLASS_SIMPLE_BUS,
.of_match = at91_pmc_match,
};
U_BOOT_DRIVER_ALIAS(atmel_at91rm9200_pmc, atmel_at91sam9260_pmc)
/*---------------------------------------------------------*/
int at91_pmc_core_probe(struct udevice *dev)
static int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
{
struct pmc_platdata *plat = dev_get_platdata(dev);
if (args->args_count != 2) {
debug("AT91: clk: Invalid args_count: %d\n", args->args_count);
return -EINVAL;
}
dev = dev_get_parent(dev);
plat->reg_base = dev_read_addr_ptr(dev);
clk->id = AT91_TO_CLK_ID(args->args[0], args->args[1]);
return 0;
}
static ulong at91_clk_get_rate(struct clk *clk)
{
struct clk *c;
int ret;
ret = clk_get_by_id(clk->id, &c);
if (ret)
return ret;
return clk_get_rate(c);
}
static ulong at91_clk_set_rate(struct clk *clk, ulong rate)
{
struct clk *c;
int ret;
ret = clk_get_by_id(clk->id, &c);
if (ret)
return ret;
return clk_set_rate(c, rate);
}
static int at91_clk_enable(struct clk *clk)
{
struct clk *c;
int ret;
ret = clk_get_by_id(clk->id, &c);
if (ret)
return ret;
return clk_enable(c);
}
static int at91_clk_disable(struct clk *clk)
{
struct clk *c;
int ret;
ret = clk_get_by_id(clk->id, &c);
if (ret)
return ret;
return clk_disable(c);
}
const struct clk_ops at91_clk_ops = {
.of_xlate = at91_clk_of_xlate,
.set_rate = at91_clk_set_rate,
.get_rate = at91_clk_get_rate,
.enable = at91_clk_enable,
.disable = at91_clk_disable,
};
/**
* pmc_read() - read content at address base + off into val
*
* @base: base address
* @off: offset to read from
* @val: where the content of base + off is stored
*
* @return: void
*/
void pmc_read(void __iomem *base, unsigned int off, unsigned int *val)
{
*val = readl(base + off);
}
/**
* at91_clk_sub_device_bind() - for the at91 clock driver
* Recursively bind its children as clk devices.
* pmc_write() - write content of val at address base + off
*
* @return: 0 on success, or negative error code on failure
* @base: base address
* @off: offset to write to
* @val: content to be written at base + off
*
* @return: void
*/
int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name)
void pmc_write(void __iomem *base, unsigned int off, unsigned int val)
{
const void *fdt = gd->fdt_blob;
int offset = dev_of_offset(dev);
bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
const char *name;
int ret;
for (offset = fdt_first_subnode(fdt, offset);
offset > 0;
offset = fdt_next_subnode(fdt, offset)) {
if (pre_reloc_only &&
!ofnode_pre_reloc(offset_to_ofnode(offset)))
continue;
/*
* If this node has "compatible" property, this is not
* a clock sub-node, but a normal device. skip.
*/
fdt_get_property(fdt, offset, "compatible", &ret);
if (ret >= 0)
continue;
if (ret != -FDT_ERR_NOTFOUND)
return ret;
name = fdt_get_name(fdt, offset, NULL);
if (!name)
return -EINVAL;
ret = device_bind_driver_to_node(dev, drv_name, name,
offset_to_ofnode(offset), NULL);
if (ret)
return ret;
}
return 0;
writel(val, base + off);
}
int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
/**
* pmc_update_bits() - update a set of bits at address base + off
*
* @base: base address
* @off: offset to be updated
* @mask: mask of bits to be updated
* @bits: the new value to be updated
*
* @return: void
*/
void pmc_update_bits(void __iomem *base, unsigned int off,
unsigned int mask, unsigned int bits)
{
int periph;
unsigned int tmp;
if (args->args_count) {
debug("Invalid args_count: %d\n", args->args_count);
return -EINVAL;
}
tmp = readl(base + off);
tmp &= ~mask;
writel(tmp | (bits & mask), base + off);
}
periph = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(clk->dev), "reg",
-1);
if (periph < 0)
/**
* at91_clk_mux_val_to_index() - get parent index in mux table
*
* @table: clock mux table
* @num_parents: clock number of parents
* @val: clock id who's mux index should be retrieved
*
* @return: clock index in mux table or a negative error number in case of
* failure
*/
int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val)
{
int i;
if (!table || !num_parents)
return -EINVAL;
clk->id = periph;
for (i = 0; i < num_parents; i++) {
if (table[i] == val)
return i;
}
return 0;
return -EINVAL;
}
int at91_clk_probe(struct udevice *dev)
/**
* at91_clk_mux_index_to_val() - get parent ID corresponding to an entry in
* clock's mux table
*
* @table: clock's mux table
* @num_parents: clock's number of parents
* @index: index in mux table which clock's ID should be retrieved
*
* @return: clock ID or a negative error number in case of failure
*/
int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index)
{
struct udevice *dev_periph_container, *dev_pmc;
struct pmc_platdata *plat = dev_get_platdata(dev);
if (!table || !num_parents || index < 0 || index > num_parents)
return -EINVAL;
dev_periph_container = dev_get_parent(dev);
dev_pmc = dev_get_parent(dev_periph_container);
plat->reg_base = dev_read_addr_ptr(dev_pmc);
return 0;
return table[index];
}

View File

@@ -7,17 +7,141 @@
#ifndef __AT91_PMC_H__
#define __AT91_PMC_H__
#include <regmap.h>
#include <linux/bitops.h>
#include <linux/io.h>
struct pmc_platdata {
struct at91_pmc *reg_base;
struct regmap *regmap_sfr;
/* Keep a range of 256 available clocks for every clock type. */
#define AT91_TO_CLK_ID(_t, _i) (((_t) << 8) | ((_i) & 0xff))
#define AT91_CLK_ID_TO_DID(_i) ((_i) & 0xff)
struct clk_range {
unsigned long min;
unsigned long max;
};
int at91_pmc_core_probe(struct udevice *dev);
int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name);
struct clk_master_layout {
u32 offset;
u32 mask;
u8 pres_shift;
};
int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args);
int at91_clk_probe(struct udevice *dev);
extern const struct clk_master_layout at91rm9200_master_layout;
extern const struct clk_master_layout at91sam9x5_master_layout;
struct clk_master_characteristics {
struct clk_range output;
u32 divisors[4];
u8 have_div3_pres;
};
struct clk_pll_characteristics {
struct clk_range input;
int num_output;
const struct clk_range *output;
u16 *icpll;
u8 *out;
u8 upll : 1;
};
struct clk_pll_layout {
u32 pllr_mask;
u32 mul_mask;
u32 frac_mask;
u32 div_mask;
u32 endiv_mask;
u8 mul_shift;
u8 frac_shift;
u8 div_shift;
u8 endiv_shift;
};
struct clk_programmable_layout {
u8 pres_mask;
u8 pres_shift;
u8 css_mask;
u8 have_slck_mck;
u8 is_pres_direct;
};
struct clk_pcr_layout {
u32 offset;
u32 cmd;
u32 div_mask;
u32 gckcss_mask;
u32 pid_mask;
};
extern const struct clk_programmable_layout at91rm9200_programmable_layout;
extern const struct clk_programmable_layout at91sam9g45_programmable_layout;
extern const struct clk_programmable_layout at91sam9x5_programmable_layout;
extern const struct clk_ops at91_clk_ops;
struct clk *at91_clk_main_rc(void __iomem *reg, const char *name,
const char *parent_name);
struct clk *at91_clk_main_osc(void __iomem *reg, const char *name,
const char *parent_name, bool bypass);
struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name,
const char *parent_name);
struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name,
const char * const *parent_names, int num_parents,
const u32 *mux_table, int type);
struct clk *
sam9x60_clk_register_div_pll(void __iomem *base, const char *name,
const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, bool critical);
struct clk *
sam9x60_clk_register_frac_pll(void __iomem *base, const char *name,
const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, bool critical);
struct clk *
at91_clk_register_master(void __iomem *base, const char *name,
const char * const *parent_names, int num_parents,
const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
const u32 *mux_table);
struct clk *
at91_clk_sama7g5_register_master(void __iomem *base, const char *name,
const char * const *parent_names, int num_parents,
const u32 *mux_table, const u32 *clk_mux_table,
bool critical, u8 id);
struct clk *
at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
const char *name, const char *parent_name);
struct clk *
at91_clk_sama7g5_register_utmi(void __iomem *base, const char *name,
const char *parent_name);
struct clk *
at91_clk_register_programmable(void __iomem *base, const char *name,
const char * const *parent_names, u8 num_parents, u8 id,
const struct clk_programmable_layout *layout,
const u32 *clk_mux_table, const u32 *mux_table);
struct clk *
at91_clk_register_system(void __iomem *base, const char *name,
const char *parent_name, u8 id);
struct clk *
at91_clk_register_peripheral(void __iomem *base, const char *name,
const char *parent_name, u32 id);
struct clk *
at91_clk_register_sam9x5_peripheral(void __iomem *base,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
u32 id, const struct clk_range *range);
struct clk *
at91_clk_register_generic(void __iomem *base,
const struct clk_pcr_layout *layout, const char *name,
const char * const *parent_names,
const u32 *clk_mux_table, const u32 *mux_table,
u8 num_parents, u8 id, const struct clk_range *range);
int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
void pmc_read(void __iomem *base, unsigned int off, unsigned int *val);
void pmc_write(void __iomem *base, unsigned int off, unsigned int val);
void pmc_update_bits(void __iomem *base, unsigned int off, unsigned int mask,
unsigned int bits);
#endif

1401
drivers/clk/at91/sama7g5.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,172 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
* Slow clock support for AT91 architectures.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <dt-bindings/clk/at91.h>
#include <linux/clk-provider.h>
static const struct udevice_id at91_sckc_match[] = {
{ .compatible = "atmel,at91sam9x5-sckc" },
{}
#include "pmc.h"
#define UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK "at91-sam9x60-td-slck"
#define UBOOT_DM_CLK_AT91_SCKC "at91-sckc"
#define AT91_OSC_SEL BIT(24)
#define AT91_OSC_SEL_SHIFT (24)
struct sam9x60_sckc {
void __iomem *reg;
const char **parent_names;
unsigned int num_parents;
struct clk clk;
};
#define to_sam9x60_sckc(c) container_of(c, struct sam9x60_sckc, clk)
static int sam9x60_sckc_of_xlate(struct clk *clk,
struct ofnode_phandle_args *args)
{
if (args->args_count != 1) {
debug("AT91: SCKC: Invalid args_count: %d\n", args->args_count);
return -EINVAL;
}
clk->id = AT91_TO_CLK_ID(PMC_TYPE_SLOW, args->args[0]);
return 0;
}
static const struct clk_ops sam9x60_sckc_ops = {
.of_xlate = sam9x60_sckc_of_xlate,
.get_rate = clk_generic_get_rate,
};
static int sam9x60_td_slck_set_parent(struct clk *clk, struct clk *parent)
{
struct sam9x60_sckc *sckc = to_sam9x60_sckc(clk);
u32 i;
for (i = 0; i < sckc->num_parents; i++) {
if (!strcmp(parent->dev->name, sckc->parent_names[i]))
break;
}
if (i == sckc->num_parents)
return -EINVAL;
pmc_update_bits(sckc->reg, 0, AT91_OSC_SEL, (i << AT91_OSC_SEL_SHIFT));
return 0;
}
static const struct clk_ops sam9x60_td_slck_ops = {
.get_rate = clk_generic_get_rate,
.set_parent = sam9x60_td_slck_set_parent,
};
static struct clk *at91_sam9x60_clk_register_td_slck(struct sam9x60_sckc *sckc,
const char *name, const char * const *parent_names,
int num_parents)
{
struct clk *clk;
int ret = -ENOMEM;
u32 val, i;
if (!sckc || !name || !parent_names || num_parents != 2)
return ERR_PTR(-EINVAL);
sckc->parent_names = kzalloc(sizeof(*sckc->parent_names) * num_parents,
GFP_KERNEL);
if (!sckc->parent_names)
return ERR_PTR(ret);
for (i = 0; i < num_parents; i++) {
sckc->parent_names[i] = kmemdup(parent_names[i],
strlen(parent_names[i]) + 1, GFP_KERNEL);
if (!sckc->parent_names[i])
goto free;
}
sckc->num_parents = num_parents;
pmc_read(sckc->reg, 0, &val);
val = (val & AT91_OSC_SEL) >> AT91_OSC_SEL_SHIFT;
clk = &sckc->clk;
ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK, name,
parent_names[val]);
if (ret)
goto free;
return clk;
free:
for (; i >= 0; i--)
kfree(sckc->parent_names[i]);
kfree(sckc->parent_names);
return ERR_PTR(ret);
}
U_BOOT_DRIVER(at91_sam9x60_td_slck) = {
.name = UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK,
.id = UCLASS_CLK,
.ops = &sam9x60_td_slck_ops,
.flags = DM_FLAG_PRE_RELOC,
};
static int at91_sam9x60_sckc_probe(struct udevice *dev)
{
struct sam9x60_sckc *sckc = dev_get_priv(dev);
void __iomem *base = (void *)devfdt_get_addr(dev);
const char *slow_rc_osc, *slow_osc;
const char *parents[2];
struct clk *clk, c;
int ret;
ret = clk_get_by_index(dev, 0, &c);
if (ret)
return ret;
slow_rc_osc = clk_hw_get_name(&c);
ret = clk_get_by_index(dev, 1, &c);
if (ret)
return ret;
slow_osc = clk_hw_get_name(&c);
clk = clk_register_fixed_factor(NULL, "md_slck", slow_rc_osc, 0, 1, 1);
if (IS_ERR(clk))
return PTR_ERR(clk);
clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 0), clk);
parents[0] = slow_rc_osc;
parents[1] = slow_osc;
sckc[1].reg = base;
clk = at91_sam9x60_clk_register_td_slck(&sckc[1], "td_slck",
parents, 2);
if (IS_ERR(clk))
return PTR_ERR(clk);
clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 1), clk);
return 0;
}
static const struct udevice_id sam9x60_sckc_ids[] = {
{ .compatible = "microchip,sam9x60-sckc" },
{ /* Sentinel. */ },
};
U_BOOT_DRIVER(at91_sckc) = {
.name = "at91-sckc",
.id = UCLASS_SIMPLE_BUS,
.of_match = at91_sckc_match,
.name = UBOOT_DM_CLK_AT91_SCKC,
.id = UCLASS_CLK,
.of_match = sam9x60_sckc_ids,
.priv_auto_alloc_size = sizeof(struct sam9x60_sckc) * 2,
.ops = &sam9x60_sckc_ops,
.probe = at91_sam9x60_sckc_probe,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -14,6 +14,7 @@
#include <errno.h>
#include <log.h>
#include <malloc.h>
#include <dm/device-internal.h>
#include <dm/devres.h>
#include <dm/read.h>
#include <linux/bug.h>
@@ -187,9 +188,26 @@ bulk_get_err:
return ret;
}
static struct clk *clk_set_default_get_by_id(struct clk *clk)
{
struct clk *c = clk;
if (CONFIG_IS_ENABLED(CLK_CCF)) {
int ret = clk_get_by_id(clk->id, &c);
if (ret) {
debug("%s(): could not get parent clock pointer, id %lu\n",
__func__, clk->id);
ERR_PTR(ret);
}
}
return c;
}
static int clk_set_default_parents(struct udevice *dev, int stage)
{
struct clk clk, parent_clk;
struct clk clk, parent_clk, *c, *p;
int index;
int num_parents;
int ret;
@@ -215,6 +233,10 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
return ret;
}
p = clk_set_default_get_by_id(&parent_clk);
if (IS_ERR(p))
return PTR_ERR(p);
ret = clk_get_by_indexed_prop(dev, "assigned-clocks",
index, &clk);
if (ret) {
@@ -234,7 +256,11 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
/* do not setup twice the parent clocks */
continue;
ret = clk_set_parent(&clk, &parent_clk);
c = clk_set_default_get_by_id(&clk);
if (IS_ERR(c))
return PTR_ERR(c);
ret = clk_set_parent(c, p);
/*
* Not all drivers may support clock-reparenting (as of now).
* Ignore errors due to this.
@@ -254,7 +280,7 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
static int clk_set_default_rates(struct udevice *dev, int stage)
{
struct clk clk;
struct clk clk, *c;
int index;
int num_rates;
int size;
@@ -298,7 +324,11 @@ static int clk_set_default_rates(struct udevice *dev, int stage)
/* do not setup twice the parent clocks */
continue;
ret = clk_set_rate(&clk, rates[index]);
c = clk_set_default_get_by_id(&clk);
if (IS_ERR(c))
return PTR_ERR(c);
ret = clk_set_rate(c, rates[index]);
if (ret < 0) {
debug("%s: failed to set rate on clock index %d (%ld) for %s\n",
@@ -512,6 +542,7 @@ ulong clk_set_rate(struct clk *clk, ulong rate)
int clk_set_parent(struct clk *clk, struct clk *parent)
{
const struct clk_ops *ops;
int ret;
debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent);
if (!clk_valid(clk))
@@ -521,7 +552,14 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
if (!ops->set_parent)
return -ENOSYS;
return ops->set_parent(clk, parent);
ret = ops->set_parent(clk, parent);
if (ret)
return ret;
if (CONFIG_IS_ENABLED(CLK_CCF))
ret = device_reparent(clk->dev, parent->dev);
return ret;
}
int clk_enable(struct clk *clk)
@@ -597,6 +635,9 @@ int clk_disable(struct clk *clk)
if (CONFIG_IS_ENABLED(CLK_CCF)) {
if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
if (clkp->flags & CLK_IS_CRITICAL)
return 0;
if (clkp->enable_count == 0) {
printf("clk %s already disabled\n",
clkp->dev->name);

View File

@@ -57,6 +57,9 @@ ulong clk_generic_get_rate(struct clk *clk)
const char *clk_hw_get_name(const struct clk *hw)
{
assert(hw);
assert(hw->dev);
return hw->dev->name;
}