avionic design with actual uboot and tooling
submodule of avionic design uboot bootloader and with included tools to get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
26
u-boot/drivers/clk/Kconfig
Normal file
26
u-boot/drivers/clk/Kconfig
Normal file
@@ -0,0 +1,26 @@
|
||||
menu "Clock"
|
||||
|
||||
config CLK
|
||||
bool "Enable clock driver support"
|
||||
depends on DM
|
||||
help
|
||||
This allows drivers to be provided for clock generators, including
|
||||
oscillators and PLLs. Devices can use a common clock API to request
|
||||
a particular clock rate and check on available clocks. Clocks can
|
||||
feed into other clocks in a tree structure, with multiplexers to
|
||||
choose the source for each clock.
|
||||
|
||||
config SPL_CLK
|
||||
bool "Enable clock support in SPL"
|
||||
depends on CLK
|
||||
help
|
||||
The clock subsystem adds a small amount of overhead to the image.
|
||||
If this is acceptable and you have a need to use clock drivers in
|
||||
SPL, enable this option. It might provide a cleaner interface to
|
||||
setting up clocks within SPL, and allows the same drivers to be
|
||||
used as U-Boot proper.
|
||||
|
||||
source "drivers/clk/uniphier/Kconfig"
|
||||
source "drivers/clk/exynos/Kconfig"
|
||||
|
||||
endmenu
|
||||
15
u-boot/drivers/clk/Makefile
Normal file
15
u-boot/drivers/clk/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Copyright (c) 2015 Google, Inc
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CLK) += clk-uclass.o clk_fixed_rate.o
|
||||
obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o
|
||||
obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o
|
||||
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
|
||||
obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
|
||||
obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
|
||||
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
|
||||
obj-$(CONFIG_CLK_EXYNOS) += exynos/
|
||||
200
u-boot/drivers/clk/clk-uclass.c
Normal file
200
u-boot/drivers/clk/clk-uclass.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static inline struct clk_ops *clk_dev_ops(struct udevice *dev)
|
||||
{
|
||||
return (struct clk_ops *)dev->driver->ops;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
|
||||
{
|
||||
int ret;
|
||||
u32 cell[2];
|
||||
|
||||
if (index != 0)
|
||||
return -ENOSYS;
|
||||
assert(clk);
|
||||
ret = uclass_get_device(UCLASS_CLK, 0, &clk->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clocks",
|
||||
cell, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
clk->id = cell[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#else
|
||||
static int clk_of_xlate_default(struct clk *clk,
|
||||
struct fdtdec_phandle_args *args)
|
||||
{
|
||||
debug("%s(clk=%p)\n", __func__, clk);
|
||||
|
||||
if (args->args_count > 1) {
|
||||
debug("Invaild args_count: %d\n", args->args_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (args->args_count)
|
||||
clk->id = args->args[0];
|
||||
else
|
||||
clk->id = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
|
||||
{
|
||||
int ret;
|
||||
struct fdtdec_phandle_args args;
|
||||
struct udevice *dev_clk;
|
||||
struct clk_ops *ops;
|
||||
|
||||
debug("%s(dev=%p, index=%d, clk=%p)\n", __func__, dev, index, clk);
|
||||
|
||||
assert(clk);
|
||||
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset,
|
||||
"clocks", "#clock-cells", 0, index,
|
||||
&args);
|
||||
if (ret) {
|
||||
debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = uclass_get_device_by_of_offset(UCLASS_CLK, args.node, &dev_clk);
|
||||
if (ret) {
|
||||
debug("%s: uclass_get_device_by_of_offset failed: err=%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
ops = clk_dev_ops(dev_clk);
|
||||
|
||||
if (ops->of_xlate)
|
||||
ret = ops->of_xlate(clk, &args);
|
||||
else
|
||||
ret = clk_of_xlate_default(clk, &args);
|
||||
if (ret) {
|
||||
debug("of_xlate() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return clk_request(dev_clk, clk);
|
||||
}
|
||||
|
||||
int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk)
|
||||
{
|
||||
int index;
|
||||
|
||||
debug("%s(dev=%p, name=%s, clk=%p)\n", __func__, dev, name, clk);
|
||||
|
||||
index = fdt_find_string(gd->fdt_blob, dev->of_offset, "clock-names",
|
||||
name);
|
||||
if (index < 0) {
|
||||
debug("fdt_find_string() failed: %d\n", index);
|
||||
return index;
|
||||
}
|
||||
|
||||
return clk_get_by_index(dev, index, clk);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int clk_request(struct udevice *dev, struct clk *clk)
|
||||
{
|
||||
struct clk_ops *ops = clk_dev_ops(dev);
|
||||
|
||||
debug("%s(dev=%p, clk=%p)\n", __func__, dev, clk);
|
||||
|
||||
clk->dev = dev;
|
||||
|
||||
if (!ops->request)
|
||||
return 0;
|
||||
|
||||
return ops->request(clk);
|
||||
}
|
||||
|
||||
int clk_free(struct clk *clk)
|
||||
{
|
||||
struct clk_ops *ops = clk_dev_ops(clk->dev);
|
||||
|
||||
debug("%s(clk=%p)\n", __func__, clk);
|
||||
|
||||
if (!ops->free)
|
||||
return 0;
|
||||
|
||||
return ops->free(clk);
|
||||
}
|
||||
|
||||
ulong clk_get_rate(struct clk *clk)
|
||||
{
|
||||
struct clk_ops *ops = clk_dev_ops(clk->dev);
|
||||
|
||||
debug("%s(clk=%p)\n", __func__, clk);
|
||||
|
||||
if (!ops->get_rate)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_rate(clk);
|
||||
}
|
||||
|
||||
ulong clk_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct clk_ops *ops = clk_dev_ops(clk->dev);
|
||||
|
||||
debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate);
|
||||
|
||||
if (!ops->set_rate)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->set_rate(clk, rate);
|
||||
}
|
||||
|
||||
int clk_enable(struct clk *clk)
|
||||
{
|
||||
struct clk_ops *ops = clk_dev_ops(clk->dev);
|
||||
|
||||
debug("%s(clk=%p)\n", __func__, clk);
|
||||
|
||||
if (!ops->enable)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->enable(clk);
|
||||
}
|
||||
|
||||
int clk_disable(struct clk *clk)
|
||||
{
|
||||
struct clk_ops *ops = clk_dev_ops(clk->dev);
|
||||
|
||||
debug("%s(clk=%p)\n", __func__, clk);
|
||||
|
||||
if (!ops->disable)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->disable(clk);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(clk) = {
|
||||
.id = UCLASS_CLK,
|
||||
.name = "clk",
|
||||
};
|
||||
54
u-boot/drivers/clk/clk_fixed_rate.c
Normal file
54
u-boot/drivers/clk/clk_fixed_rate.c
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm/device.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct clk_fixed_rate {
|
||||
unsigned long fixed_rate;
|
||||
};
|
||||
|
||||
#define to_clk_fixed_rate(dev) ((struct clk_fixed_rate *)dev_get_platdata(dev))
|
||||
|
||||
static ulong clk_fixed_rate_get_rate(struct clk *clk)
|
||||
{
|
||||
if (clk->id != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return to_clk_fixed_rate(clk->dev)->fixed_rate;
|
||||
}
|
||||
|
||||
const struct clk_ops clk_fixed_rate_ops = {
|
||||
.get_rate = clk_fixed_rate_get_rate,
|
||||
};
|
||||
|
||||
static int clk_fixed_rate_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
to_clk_fixed_rate(dev)->fixed_rate =
|
||||
fdtdec_get_int(gd->fdt_blob, dev->of_offset,
|
||||
"clock-frequency", 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id clk_fixed_rate_match[] = {
|
||||
{
|
||||
.compatible = "fixed-clock",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(clk_fixed_rate) = {
|
||||
.name = "fixed_rate_clock",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = clk_fixed_rate_match,
|
||||
.ofdata_to_platdata = clk_fixed_rate_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct clk_fixed_rate),
|
||||
.ops = &clk_fixed_rate_ops,
|
||||
};
|
||||
425
u-boot/drivers/clk/clk_pic32.c
Normal file
425
u-boot/drivers/clk/clk_pic32.c
Normal file
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm.h>
|
||||
#include <div64.h>
|
||||
#include <wait_bit.h>
|
||||
#include <dm/lists.h>
|
||||
#include <asm/io.h>
|
||||
#include <mach/pic32.h>
|
||||
#include <dt-bindings/clock/microchip,clock.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* Primary oscillator */
|
||||
#define SYS_POSC_CLK_HZ 24000000
|
||||
|
||||
/* FRC clk rate */
|
||||
#define SYS_FRC_CLK_HZ 8000000
|
||||
|
||||
/* Clock Registers */
|
||||
#define OSCCON 0x0000
|
||||
#define OSCTUNE 0x0010
|
||||
#define SPLLCON 0x0020
|
||||
#define REFO1CON 0x0080
|
||||
#define REFO1TRIM 0x0090
|
||||
#define PB1DIV 0x0140
|
||||
|
||||
/* SPLL */
|
||||
#define ICLK_MASK 0x00000080
|
||||
#define PLLIDIV_MASK 0x00000007
|
||||
#define PLLODIV_MASK 0x00000007
|
||||
#define CUROSC_MASK 0x00000007
|
||||
#define PLLMUL_MASK 0x0000007F
|
||||
#define FRCDIV_MASK 0x00000007
|
||||
|
||||
/* PBCLK */
|
||||
#define PBDIV_MASK 0x00000007
|
||||
|
||||
/* SYSCLK MUX */
|
||||
#define SCLK_SRC_FRC1 0
|
||||
#define SCLK_SRC_SPLL 1
|
||||
#define SCLK_SRC_POSC 2
|
||||
#define SCLK_SRC_FRC2 7
|
||||
|
||||
/* Reference Oscillator Control Reg fields */
|
||||
#define REFO_SEL_MASK 0x0f
|
||||
#define REFO_SEL_SHIFT 0
|
||||
#define REFO_ACTIVE BIT(8)
|
||||
#define REFO_DIVSW_EN BIT(9)
|
||||
#define REFO_OE BIT(12)
|
||||
#define REFO_ON BIT(15)
|
||||
#define REFO_DIV_SHIFT 16
|
||||
#define REFO_DIV_MASK 0x7fff
|
||||
|
||||
/* Reference Oscillator Trim Register Fields */
|
||||
#define REFO_TRIM_REG 0x10
|
||||
#define REFO_TRIM_MASK 0x1ff
|
||||
#define REFO_TRIM_SHIFT 23
|
||||
#define REFO_TRIM_MAX 511
|
||||
|
||||
#define ROCLK_SRC_SCLK 0x0
|
||||
#define ROCLK_SRC_SPLL 0x7
|
||||
#define ROCLK_SRC_ROCLKI 0x8
|
||||
|
||||
/* Memory PLL */
|
||||
#define MPLL_IDIV 0x3f
|
||||
#define MPLL_MULT 0xff
|
||||
#define MPLL_ODIV1 0x7
|
||||
#define MPLL_ODIV2 0x7
|
||||
#define MPLL_VREG_RDY BIT(23)
|
||||
#define MPLL_RDY BIT(31)
|
||||
#define MPLL_IDIV_SHIFT 0
|
||||
#define MPLL_MULT_SHIFT 8
|
||||
#define MPLL_ODIV1_SHIFT 24
|
||||
#define MPLL_ODIV2_SHIFT 27
|
||||
#define MPLL_IDIV_INIT 0x03
|
||||
#define MPLL_MULT_INIT 0x32
|
||||
#define MPLL_ODIV1_INIT 0x02
|
||||
#define MPLL_ODIV2_INIT 0x01
|
||||
|
||||
struct pic32_clk_priv {
|
||||
void __iomem *iobase;
|
||||
void __iomem *syscfg_base;
|
||||
};
|
||||
|
||||
static ulong pic32_get_pll_rate(struct pic32_clk_priv *priv)
|
||||
{
|
||||
u32 iclk, idiv, odiv, mult;
|
||||
ulong plliclk, v;
|
||||
|
||||
v = readl(priv->iobase + SPLLCON);
|
||||
iclk = (v & ICLK_MASK);
|
||||
idiv = ((v >> 8) & PLLIDIV_MASK) + 1;
|
||||
odiv = ((v >> 24) & PLLODIV_MASK);
|
||||
mult = ((v >> 16) & PLLMUL_MASK) + 1;
|
||||
|
||||
plliclk = iclk ? SYS_FRC_CLK_HZ : SYS_POSC_CLK_HZ;
|
||||
|
||||
if (odiv < 2)
|
||||
odiv = 2;
|
||||
else if (odiv < 5)
|
||||
odiv = (1 << odiv);
|
||||
else
|
||||
odiv = 32;
|
||||
|
||||
return ((plliclk / idiv) * mult) / odiv;
|
||||
}
|
||||
|
||||
static ulong pic32_get_sysclk(struct pic32_clk_priv *priv)
|
||||
{
|
||||
ulong v;
|
||||
ulong hz;
|
||||
ulong div, frcdiv;
|
||||
ulong curr_osc;
|
||||
|
||||
/* get clk source */
|
||||
v = readl(priv->iobase + OSCCON);
|
||||
curr_osc = (v >> 12) & CUROSC_MASK;
|
||||
switch (curr_osc) {
|
||||
case SCLK_SRC_FRC1:
|
||||
case SCLK_SRC_FRC2:
|
||||
frcdiv = ((v >> 24) & FRCDIV_MASK);
|
||||
div = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
|
||||
hz = SYS_FRC_CLK_HZ / div;
|
||||
break;
|
||||
|
||||
case SCLK_SRC_SPLL:
|
||||
hz = pic32_get_pll_rate(priv);
|
||||
break;
|
||||
|
||||
case SCLK_SRC_POSC:
|
||||
hz = SYS_POSC_CLK_HZ;
|
||||
break;
|
||||
|
||||
default:
|
||||
hz = 0;
|
||||
printf("clk: unknown sclk_src.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return hz;
|
||||
}
|
||||
|
||||
static ulong pic32_get_pbclk(struct pic32_clk_priv *priv, int periph)
|
||||
{
|
||||
void __iomem *reg;
|
||||
ulong div, clk_freq;
|
||||
|
||||
WARN_ON((periph < PB1CLK) || (periph > PB7CLK));
|
||||
|
||||
clk_freq = pic32_get_sysclk(priv);
|
||||
|
||||
reg = priv->iobase + PB1DIV + (periph - PB1CLK) * 0x10;
|
||||
div = (readl(reg) & PBDIV_MASK) + 1;
|
||||
|
||||
return clk_freq / div;
|
||||
}
|
||||
|
||||
static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
|
||||
{
|
||||
return pic32_get_pbclk(priv, PB7CLK);
|
||||
}
|
||||
|
||||
static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph,
|
||||
int parent_rate, int rate, int parent_id)
|
||||
{
|
||||
void __iomem *reg;
|
||||
u32 div, trim, v;
|
||||
u64 frac;
|
||||
|
||||
WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
|
||||
|
||||
/* calculate dividers,
|
||||
* rate = parent_rate / [2 * (div + (trim / 512))]
|
||||
*/
|
||||
if (parent_rate <= rate) {
|
||||
div = 0;
|
||||
trim = 0;
|
||||
} else {
|
||||
div = parent_rate / (rate << 1);
|
||||
frac = parent_rate;
|
||||
frac <<= 8;
|
||||
do_div(frac, rate);
|
||||
frac -= (u64)(div << 9);
|
||||
trim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u32)frac;
|
||||
}
|
||||
|
||||
reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
|
||||
|
||||
/* disable clk */
|
||||
writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
|
||||
|
||||
/* wait till previous src change is active */
|
||||
wait_for_bit(__func__, reg, REFO_DIVSW_EN | REFO_ACTIVE,
|
||||
false, CONFIG_SYS_HZ, false);
|
||||
|
||||
/* parent_id */
|
||||
v = readl(reg);
|
||||
v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
|
||||
v |= (parent_id << REFO_SEL_SHIFT);
|
||||
|
||||
/* apply rodiv */
|
||||
v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
|
||||
v |= (div << REFO_DIV_SHIFT);
|
||||
writel(v, reg);
|
||||
|
||||
/* apply trim */
|
||||
v = readl(reg + REFO_TRIM_REG);
|
||||
v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
|
||||
v |= (trim << REFO_TRIM_SHIFT);
|
||||
writel(v, reg + REFO_TRIM_REG);
|
||||
|
||||
/* enable clk */
|
||||
writel(REFO_ON | REFO_OE, reg + _SET_OFFSET);
|
||||
|
||||
/* switch divider */
|
||||
writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
|
||||
|
||||
/* wait for divider switching to complete */
|
||||
return wait_for_bit(__func__, reg, REFO_DIVSW_EN, false,
|
||||
CONFIG_SYS_HZ, false);
|
||||
}
|
||||
|
||||
static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph)
|
||||
{
|
||||
u32 rodiv, rotrim, rosel, v, parent_rate;
|
||||
void __iomem *reg;
|
||||
u64 rate64;
|
||||
|
||||
WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
|
||||
|
||||
reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
|
||||
v = readl(reg);
|
||||
/* get rosel */
|
||||
rosel = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
|
||||
/* get div */
|
||||
rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
|
||||
|
||||
/* get trim */
|
||||
v = readl(reg + REFO_TRIM_REG);
|
||||
rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
|
||||
|
||||
if (!rodiv)
|
||||
return 0;
|
||||
|
||||
/* get parent rate */
|
||||
switch (rosel) {
|
||||
case ROCLK_SRC_SCLK:
|
||||
parent_rate = pic32_get_cpuclk(priv);
|
||||
break;
|
||||
case ROCLK_SRC_SPLL:
|
||||
parent_rate = pic32_get_pll_rate(priv);
|
||||
break;
|
||||
default:
|
||||
parent_rate = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculation
|
||||
* rate = parent_rate / [2 * (div + (trim / 512))]
|
||||
*/
|
||||
if (rotrim) {
|
||||
rodiv <<= 9;
|
||||
rodiv += rotrim;
|
||||
rate64 = parent_rate;
|
||||
rate64 <<= 8;
|
||||
do_div(rate64, rodiv);
|
||||
v = (u32)rate64;
|
||||
} else {
|
||||
v = parent_rate / (rodiv << 1);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static ulong pic32_get_mpll_rate(struct pic32_clk_priv *priv)
|
||||
{
|
||||
u32 v, idiv, mul;
|
||||
u32 odiv1, odiv2;
|
||||
u64 rate;
|
||||
|
||||
v = readl(priv->syscfg_base + CFGMPLL);
|
||||
idiv = v & MPLL_IDIV;
|
||||
mul = (v >> MPLL_MULT_SHIFT) & MPLL_MULT;
|
||||
odiv1 = (v >> MPLL_ODIV1_SHIFT) & MPLL_ODIV1;
|
||||
odiv2 = (v >> MPLL_ODIV2_SHIFT) & MPLL_ODIV2;
|
||||
|
||||
rate = (SYS_POSC_CLK_HZ / idiv) * mul;
|
||||
do_div(rate, odiv1);
|
||||
do_div(rate, odiv2);
|
||||
|
||||
return (ulong)rate;
|
||||
}
|
||||
|
||||
static int pic32_mpll_init(struct pic32_clk_priv *priv)
|
||||
{
|
||||
u32 v, mask;
|
||||
|
||||
/* initialize */
|
||||
v = (MPLL_IDIV_INIT << MPLL_IDIV_SHIFT) |
|
||||
(MPLL_MULT_INIT << MPLL_MULT_SHIFT) |
|
||||
(MPLL_ODIV1_INIT << MPLL_ODIV1_SHIFT) |
|
||||
(MPLL_ODIV2_INIT << MPLL_ODIV2_SHIFT);
|
||||
|
||||
writel(v, priv->syscfg_base + CFGMPLL);
|
||||
|
||||
/* Wait for ready */
|
||||
mask = MPLL_RDY | MPLL_VREG_RDY;
|
||||
return wait_for_bit(__func__, priv->syscfg_base + CFGMPLL, mask,
|
||||
true, get_tbclk(), false);
|
||||
}
|
||||
|
||||
static void pic32_clk_init(struct udevice *dev)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
struct pic32_clk_priv *priv;
|
||||
ulong rate, pll_hz;
|
||||
char propname[50];
|
||||
int i;
|
||||
|
||||
priv = dev_get_priv(dev);
|
||||
pll_hz = pic32_get_pll_rate(priv);
|
||||
|
||||
/* Initialize REFOs as not initialized and enabled on reset. */
|
||||
for (i = REF1CLK; i <= REF5CLK; i++) {
|
||||
snprintf(propname, sizeof(propname),
|
||||
"microchip,refo%d-frequency", i - REF1CLK + 1);
|
||||
rate = fdtdec_get_int(blob, dev->of_offset, propname, 0);
|
||||
if (rate)
|
||||
pic32_set_refclk(priv, i, pll_hz, rate, ROCLK_SRC_SPLL);
|
||||
}
|
||||
|
||||
/* Memory PLL */
|
||||
pic32_mpll_init(priv);
|
||||
}
|
||||
|
||||
static ulong pic32_get_rate(struct clk *clk)
|
||||
{
|
||||
struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
ulong rate;
|
||||
|
||||
switch (clk->id) {
|
||||
case PB1CLK ... PB7CLK:
|
||||
rate = pic32_get_pbclk(priv, clk->id);
|
||||
break;
|
||||
case REF1CLK ... REF5CLK:
|
||||
rate = pic32_get_refclk(priv, clk->id);
|
||||
break;
|
||||
case PLLCLK:
|
||||
rate = pic32_get_pll_rate(priv);
|
||||
break;
|
||||
case MPLL:
|
||||
rate = pic32_get_mpll_rate(priv);
|
||||
break;
|
||||
default:
|
||||
rate = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static ulong pic32_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
ulong pll_hz;
|
||||
|
||||
switch (clk->id) {
|
||||
case REF1CLK ... REF5CLK:
|
||||
pll_hz = pic32_get_pll_rate(priv);
|
||||
pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static struct clk_ops pic32_pic32_clk_ops = {
|
||||
.set_rate = pic32_set_rate,
|
||||
.get_rate = pic32_get_rate,
|
||||
};
|
||||
|
||||
static int pic32_clk_probe(struct udevice *dev)
|
||||
{
|
||||
struct pic32_clk_priv *priv = dev_get_priv(dev);
|
||||
fdt_addr_t addr;
|
||||
fdt_size_t size;
|
||||
|
||||
addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
priv->iobase = ioremap(addr, size);
|
||||
if (!priv->iobase)
|
||||
return -EINVAL;
|
||||
|
||||
priv->syscfg_base = pic32_get_syscfg_base();
|
||||
|
||||
/* initialize clocks */
|
||||
pic32_clk_init(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id pic32_clk_ids[] = {
|
||||
{ .compatible = "microchip,pic32mzda-clk"},
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pic32_clk) = {
|
||||
.name = "pic32_clk",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = pic32_clk_ids,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
.ops = &pic32_pic32_clk_ops,
|
||||
.probe = pic32_clk_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
|
||||
};
|
||||
386
u-boot/drivers/clk/clk_rk3036.c
Normal file
386
u-boot/drivers/clk/clk_rk3036.c
Normal file
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* (C) Copyright 2015 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/cru_rk3036.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dt-bindings/clock/rk3036-cru.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct rk3036_clk_priv {
|
||||
struct rk3036_cru *cru;
|
||||
ulong rate;
|
||||
};
|
||||
|
||||
enum {
|
||||
VCO_MAX_HZ = 2400U * 1000000,
|
||||
VCO_MIN_HZ = 600 * 1000000,
|
||||
OUTPUT_MAX_HZ = 2400U * 1000000,
|
||||
OUTPUT_MIN_HZ = 24 * 1000000,
|
||||
};
|
||||
|
||||
#define RATE_TO_DIV(input_rate, output_rate) \
|
||||
((input_rate) / (output_rate) - 1);
|
||||
|
||||
#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
|
||||
|
||||
#define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\
|
||||
.refdiv = _refdiv,\
|
||||
.fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
|
||||
.postdiv1 = _postdiv1, .postdiv2 = _postdiv2};\
|
||||
_Static_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\
|
||||
OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\
|
||||
#hz "Hz cannot be hit with PLL "\
|
||||
"divisors on line " __stringify(__LINE__));
|
||||
|
||||
/* use interge mode*/
|
||||
static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1);
|
||||
static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1);
|
||||
|
||||
static inline unsigned int log2(unsigned int value)
|
||||
{
|
||||
return fls(value) - 1;
|
||||
}
|
||||
|
||||
void *rockchip_get_cru(void)
|
||||
{
|
||||
struct udevice *dev;
|
||||
fdt_addr_t addr;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device(UCLASS_CLK, 0, &dev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
addr = dev_get_addr(dev);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return (void *)addr;
|
||||
}
|
||||
|
||||
static int rkclk_set_pll(struct rk3036_cru *cru, enum rk_clk_id clk_id,
|
||||
const struct pll_div *div)
|
||||
{
|
||||
int pll_id = rk_pll_id(clk_id);
|
||||
struct rk3036_pll *pll = &cru->pll[pll_id];
|
||||
|
||||
/* All PLLs have same VCO and output frequency range restrictions. */
|
||||
uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000;
|
||||
uint output_hz = vco_hz / div->postdiv1 / div->postdiv2;
|
||||
|
||||
debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, postdiv2=%d,\
|
||||
vco=%u Hz, output=%u Hz\n",
|
||||
pll, div->fbdiv, div->refdiv, div->postdiv1,
|
||||
div->postdiv2, vco_hz, output_hz);
|
||||
assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
|
||||
output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ);
|
||||
|
||||
/* use interger mode */
|
||||
rk_clrreg(&pll->con1, 1 << PLL_DSMPD_SHIFT);
|
||||
|
||||
rk_clrsetreg(&pll->con0,
|
||||
PLL_POSTDIV1_MASK << PLL_POSTDIV1_SHIFT | PLL_FBDIV_MASK,
|
||||
(div->postdiv1 << PLL_POSTDIV1_SHIFT) | div->fbdiv);
|
||||
rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK << PLL_POSTDIV2_SHIFT |
|
||||
PLL_REFDIV_MASK << PLL_REFDIV_SHIFT,
|
||||
(div->postdiv2 << PLL_POSTDIV2_SHIFT |
|
||||
div->refdiv << PLL_REFDIV_SHIFT));
|
||||
|
||||
/* waiting for pll lock */
|
||||
while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT))
|
||||
udelay(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rkclk_init(struct rk3036_cru *cru)
|
||||
{
|
||||
u32 aclk_div;
|
||||
u32 hclk_div;
|
||||
u32 pclk_div;
|
||||
|
||||
/* pll enter slow-mode */
|
||||
rk_clrsetreg(&cru->cru_mode_con,
|
||||
GPLL_MODE_MASK << GPLL_MODE_SHIFT |
|
||||
APLL_MODE_MASK << APLL_MODE_SHIFT,
|
||||
GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
|
||||
APLL_MODE_SLOW << APLL_MODE_SHIFT);
|
||||
|
||||
/* init pll */
|
||||
rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg);
|
||||
rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg);
|
||||
|
||||
/*
|
||||
* select apll as core clock pll source and
|
||||
* set up dependent divisors for PCLK/HCLK and ACLK clocks.
|
||||
* core hz : apll = 1:1
|
||||
*/
|
||||
aclk_div = APLL_HZ / CORE_ACLK_HZ - 1;
|
||||
assert((aclk_div + 1) * CORE_ACLK_HZ == APLL_HZ && aclk_div < 0x7);
|
||||
|
||||
pclk_div = APLL_HZ / CORE_PERI_HZ - 1;
|
||||
assert((pclk_div + 1) * CORE_PERI_HZ == APLL_HZ && pclk_div < 0xf);
|
||||
|
||||
rk_clrsetreg(&cru->cru_clksel_con[0],
|
||||
CORE_CLK_PLL_SEL_MASK << CORE_CLK_PLL_SEL_SHIFT |
|
||||
CORE_DIV_CON_MASK << CORE_DIV_CON_SHIFT,
|
||||
CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
|
||||
0 << CORE_DIV_CON_SHIFT);
|
||||
|
||||
rk_clrsetreg(&cru->cru_clksel_con[1],
|
||||
CORE_ACLK_DIV_MASK << CORE_ACLK_DIV_SHIFT |
|
||||
CORE_PERI_DIV_MASK << CORE_PERI_DIV_SHIFT,
|
||||
aclk_div << CORE_ACLK_DIV_SHIFT |
|
||||
pclk_div << CORE_PERI_DIV_SHIFT);
|
||||
|
||||
/*
|
||||
* select apll as cpu clock pll source and
|
||||
* set up dependent divisors for PCLK/HCLK and ACLK clocks.
|
||||
*/
|
||||
aclk_div = APLL_HZ / CPU_ACLK_HZ - 1;
|
||||
assert((aclk_div + 1) * CPU_ACLK_HZ == APLL_HZ && aclk_div < 0x1f);
|
||||
|
||||
pclk_div = APLL_HZ / CPU_PCLK_HZ - 1;
|
||||
assert((pclk_div + 1) * CPU_PCLK_HZ == APLL_HZ && pclk_div < 0x7);
|
||||
|
||||
hclk_div = APLL_HZ / CPU_HCLK_HZ - 1;
|
||||
assert((hclk_div + 1) * CPU_HCLK_HZ == APLL_HZ && hclk_div < 0x3);
|
||||
|
||||
rk_clrsetreg(&cru->cru_clksel_con[0],
|
||||
CPU_CLK_PLL_SEL_MASK << CPU_CLK_PLL_SEL_SHIFT |
|
||||
ACLK_CPU_DIV_MASK << ACLK_CPU_DIV_SHIFT,
|
||||
CPU_CLK_PLL_SEL_APLL << CPU_CLK_PLL_SEL_SHIFT |
|
||||
aclk_div << ACLK_CPU_DIV_SHIFT);
|
||||
|
||||
rk_clrsetreg(&cru->cru_clksel_con[1],
|
||||
CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT |
|
||||
CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT,
|
||||
pclk_div << CPU_PCLK_DIV_SHIFT |
|
||||
hclk_div << CPU_HCLK_DIV_SHIFT);
|
||||
|
||||
/*
|
||||
* select gpll as peri clock pll source and
|
||||
* set up dependent divisors for PCLK/HCLK and ACLK clocks.
|
||||
*/
|
||||
aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
|
||||
assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
|
||||
|
||||
hclk_div = log2(PERI_ACLK_HZ / PERI_HCLK_HZ);
|
||||
assert((1 << hclk_div) * PERI_HCLK_HZ ==
|
||||
PERI_ACLK_HZ && (pclk_div < 0x4));
|
||||
|
||||
pclk_div = log2(PERI_ACLK_HZ / PERI_PCLK_HZ);
|
||||
assert((1 << pclk_div) * PERI_PCLK_HZ ==
|
||||
PERI_ACLK_HZ && pclk_div < 0x8);
|
||||
|
||||
rk_clrsetreg(&cru->cru_clksel_con[10],
|
||||
PERI_PLL_SEL_MASK << PERI_PLL_SEL_SHIFT |
|
||||
PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT |
|
||||
PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT |
|
||||
PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT,
|
||||
PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
|
||||
pclk_div << PERI_PCLK_DIV_SHIFT |
|
||||
hclk_div << PERI_HCLK_DIV_SHIFT |
|
||||
aclk_div << PERI_ACLK_DIV_SHIFT);
|
||||
|
||||
/* PLL enter normal-mode */
|
||||
rk_clrsetreg(&cru->cru_mode_con,
|
||||
GPLL_MODE_MASK << GPLL_MODE_SHIFT |
|
||||
APLL_MODE_MASK << APLL_MODE_SHIFT,
|
||||
GPLL_MODE_NORM << GPLL_MODE_SHIFT |
|
||||
APLL_MODE_NORM << APLL_MODE_SHIFT);
|
||||
}
|
||||
|
||||
/* Get pll rate by id */
|
||||
static uint32_t rkclk_pll_get_rate(struct rk3036_cru *cru,
|
||||
enum rk_clk_id clk_id)
|
||||
{
|
||||
uint32_t refdiv, fbdiv, postdiv1, postdiv2;
|
||||
uint32_t con;
|
||||
int pll_id = rk_pll_id(clk_id);
|
||||
struct rk3036_pll *pll = &cru->pll[pll_id];
|
||||
static u8 clk_shift[CLK_COUNT] = {
|
||||
0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, 0xff,
|
||||
GPLL_MODE_SHIFT, 0xff
|
||||
};
|
||||
static u8 clk_mask[CLK_COUNT] = {
|
||||
0xff, APLL_MODE_MASK, DPLL_MODE_MASK, 0xff,
|
||||
GPLL_MODE_MASK, 0xff
|
||||
};
|
||||
uint shift;
|
||||
uint mask;
|
||||
|
||||
con = readl(&cru->cru_mode_con);
|
||||
shift = clk_shift[clk_id];
|
||||
mask = clk_mask[clk_id];
|
||||
|
||||
switch ((con >> shift) & mask) {
|
||||
case GPLL_MODE_SLOW:
|
||||
return OSC_HZ;
|
||||
case GPLL_MODE_NORM:
|
||||
|
||||
/* normal mode */
|
||||
con = readl(&pll->con0);
|
||||
postdiv1 = (con >> PLL_POSTDIV1_SHIFT) & PLL_POSTDIV1_MASK;
|
||||
fbdiv = (con >> PLL_FBDIV_SHIFT) & PLL_FBDIV_MASK;
|
||||
con = readl(&pll->con1);
|
||||
postdiv2 = (con >> PLL_POSTDIV2_SHIFT) & PLL_POSTDIV2_MASK;
|
||||
refdiv = (con >> PLL_REFDIV_SHIFT) & PLL_REFDIV_MASK;
|
||||
return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
|
||||
case GPLL_MODE_DEEP:
|
||||
default:
|
||||
return 32768;
|
||||
}
|
||||
}
|
||||
|
||||
static ulong rockchip_mmc_get_clk(struct rk3036_cru *cru, uint clk_general_rate,
|
||||
int periph)
|
||||
{
|
||||
uint src_rate;
|
||||
uint div, mux;
|
||||
u32 con;
|
||||
|
||||
switch (periph) {
|
||||
case HCLK_EMMC:
|
||||
con = readl(&cru->cru_clksel_con[12]);
|
||||
mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK;
|
||||
div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK;
|
||||
break;
|
||||
case HCLK_SDIO:
|
||||
con = readl(&cru->cru_clksel_con[12]);
|
||||
mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK;
|
||||
div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
src_rate = mux == EMMC_SEL_24M ? OSC_HZ : clk_general_rate;
|
||||
return DIV_TO_RATE(src_rate, div);
|
||||
}
|
||||
|
||||
static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate,
|
||||
int periph, uint freq)
|
||||
{
|
||||
int src_clk_div;
|
||||
int mux;
|
||||
|
||||
debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate);
|
||||
|
||||
/* mmc clock auto divide 2 in internal */
|
||||
src_clk_div = (clk_general_rate / 2 + freq - 1) / freq;
|
||||
|
||||
if (src_clk_div > 0x7f) {
|
||||
src_clk_div = (OSC_HZ / 2 + freq - 1) / freq;
|
||||
mux = EMMC_SEL_24M;
|
||||
} else {
|
||||
mux = EMMC_SEL_GPLL;
|
||||
}
|
||||
|
||||
switch (periph) {
|
||||
case HCLK_EMMC:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[12],
|
||||
EMMC_PLL_MASK << EMMC_PLL_SHIFT |
|
||||
EMMC_DIV_MASK << EMMC_DIV_SHIFT,
|
||||
mux << EMMC_PLL_SHIFT |
|
||||
(src_clk_div - 1) << EMMC_DIV_SHIFT);
|
||||
break;
|
||||
case HCLK_SDIO:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[11],
|
||||
MMC0_PLL_MASK << MMC0_PLL_SHIFT |
|
||||
MMC0_DIV_MASK << MMC0_DIV_SHIFT,
|
||||
mux << MMC0_PLL_SHIFT |
|
||||
(src_clk_div - 1) << MMC0_DIV_SHIFT);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return rockchip_mmc_get_clk(cru, clk_general_rate, periph);
|
||||
}
|
||||
|
||||
static ulong rk3036_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
|
||||
switch (clk->id) {
|
||||
case 0 ... 63:
|
||||
return rkclk_pll_get_rate(priv->cru, clk->id);
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
static ulong rk3036_clk_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
ulong new_rate, gclk_rate;
|
||||
|
||||
gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
|
||||
switch (clk->id) {
|
||||
case 0 ... 63:
|
||||
return 0;
|
||||
case HCLK_EMMC:
|
||||
new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate,
|
||||
clk->id, rate);
|
||||
break;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return new_rate;
|
||||
}
|
||||
|
||||
static struct clk_ops rk3036_clk_ops = {
|
||||
.get_rate = rk3036_clk_get_rate,
|
||||
.set_rate = rk3036_clk_set_rate,
|
||||
};
|
||||
|
||||
static int rk3036_clk_probe(struct udevice *dev)
|
||||
{
|
||||
struct rk3036_clk_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->cru = (struct rk3036_cru *)dev_get_addr(dev);
|
||||
rkclk_init(priv->cru);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_clk_bind(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* The reset driver does not have a device node, so bind it here */
|
||||
ret = device_bind_driver(gd->dm_root, "rk3036_sysreset", "reset", &dev);
|
||||
if (ret)
|
||||
debug("Warning: No RK3036 reset driver: ret=%d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id rk3036_clk_ids[] = {
|
||||
{ .compatible = "rockchip,rk3036-cru" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(clk_rk3036) = {
|
||||
.name = "clk_rk3036",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = rk3036_clk_ids,
|
||||
.priv_auto_alloc_size = sizeof(struct rk3036_clk_priv),
|
||||
.ops = &rk3036_clk_ops,
|
||||
.bind = rk3036_clk_bind,
|
||||
.probe = rk3036_clk_probe,
|
||||
};
|
||||
824
u-boot/drivers/clk/clk_rk3288.c
Normal file
824
u-boot/drivers/clk/clk_rk3288.c
Normal file
@@ -0,0 +1,824 @@
|
||||
/*
|
||||
* (C) Copyright 2015 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/cru_rk3288.h>
|
||||
#include <asm/arch/grf_rk3288.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <dt-bindings/clock/rk3288-cru.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct rk3288_clk_priv {
|
||||
struct rk3288_grf *grf;
|
||||
struct rk3288_cru *cru;
|
||||
ulong rate;
|
||||
};
|
||||
|
||||
struct pll_div {
|
||||
u32 nr;
|
||||
u32 nf;
|
||||
u32 no;
|
||||
};
|
||||
|
||||
enum {
|
||||
VCO_MAX_HZ = 2200U * 1000000,
|
||||
VCO_MIN_HZ = 440 * 1000000,
|
||||
OUTPUT_MAX_HZ = 2200U * 1000000,
|
||||
OUTPUT_MIN_HZ = 27500000,
|
||||
FREF_MAX_HZ = 2200U * 1000000,
|
||||
FREF_MIN_HZ = 269 * 1000000,
|
||||
};
|
||||
|
||||
enum {
|
||||
/* PLL CON0 */
|
||||
PLL_OD_MASK = 0x0f,
|
||||
|
||||
/* PLL CON1 */
|
||||
PLL_NF_MASK = 0x1fff,
|
||||
|
||||
/* PLL CON2 */
|
||||
PLL_BWADJ_MASK = 0x0fff,
|
||||
|
||||
/* PLL CON3 */
|
||||
PLL_RESET_SHIFT = 5,
|
||||
|
||||
/* CLKSEL0 */
|
||||
CORE_SEL_PLL_MASK = 1,
|
||||
CORE_SEL_PLL_SHIFT = 15,
|
||||
A17_DIV_MASK = 0x1f,
|
||||
A17_DIV_SHIFT = 8,
|
||||
MP_DIV_MASK = 0xf,
|
||||
MP_DIV_SHIFT = 4,
|
||||
M0_DIV_MASK = 0xf,
|
||||
M0_DIV_SHIFT = 0,
|
||||
|
||||
/* CLKSEL1: pd bus clk pll sel: codec or general */
|
||||
PD_BUS_SEL_PLL_MASK = 15,
|
||||
PD_BUS_SEL_CPLL = 0,
|
||||
PD_BUS_SEL_GPLL,
|
||||
|
||||
/* pd bus pclk div: pclk = pd_bus_aclk /(div + 1) */
|
||||
PD_BUS_PCLK_DIV_SHIFT = 12,
|
||||
PD_BUS_PCLK_DIV_MASK = 7,
|
||||
|
||||
/* pd bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */
|
||||
PD_BUS_HCLK_DIV_SHIFT = 8,
|
||||
PD_BUS_HCLK_DIV_MASK = 3,
|
||||
|
||||
/* pd bus aclk div: pd_bus_aclk = pd_bus_src_clk /(div0 * div1) */
|
||||
PD_BUS_ACLK_DIV0_SHIFT = 3,
|
||||
PD_BUS_ACLK_DIV0_MASK = 0x1f,
|
||||
PD_BUS_ACLK_DIV1_SHIFT = 0,
|
||||
PD_BUS_ACLK_DIV1_MASK = 0x7,
|
||||
|
||||
/*
|
||||
* CLKSEL10
|
||||
* peripheral bus pclk div:
|
||||
* aclk_bus: pclk_bus = 1:1 or 2:1 or 4:1 or 8:1
|
||||
*/
|
||||
PERI_SEL_PLL_MASK = 1,
|
||||
PERI_SEL_PLL_SHIFT = 15,
|
||||
PERI_SEL_CPLL = 0,
|
||||
PERI_SEL_GPLL,
|
||||
|
||||
PERI_PCLK_DIV_SHIFT = 12,
|
||||
PERI_PCLK_DIV_MASK = 3,
|
||||
|
||||
/* peripheral bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */
|
||||
PERI_HCLK_DIV_SHIFT = 8,
|
||||
PERI_HCLK_DIV_MASK = 3,
|
||||
|
||||
/*
|
||||
* peripheral bus aclk div:
|
||||
* aclk_periph = periph_clk_src / (peri_aclk_div_con + 1)
|
||||
*/
|
||||
PERI_ACLK_DIV_SHIFT = 0,
|
||||
PERI_ACLK_DIV_MASK = 0x1f,
|
||||
|
||||
SOCSTS_DPLL_LOCK = 1 << 5,
|
||||
SOCSTS_APLL_LOCK = 1 << 6,
|
||||
SOCSTS_CPLL_LOCK = 1 << 7,
|
||||
SOCSTS_GPLL_LOCK = 1 << 8,
|
||||
SOCSTS_NPLL_LOCK = 1 << 9,
|
||||
};
|
||||
|
||||
#define RATE_TO_DIV(input_rate, output_rate) \
|
||||
((input_rate) / (output_rate) - 1);
|
||||
|
||||
#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
|
||||
|
||||
#define PLL_DIVISORS(hz, _nr, _no) {\
|
||||
.nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\
|
||||
_Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
|
||||
(_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\
|
||||
"divisors on line " __stringify(__LINE__));
|
||||
|
||||
/* Keep divisors as low as possible to reduce jitter and power usage */
|
||||
static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 1);
|
||||
static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2);
|
||||
static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
|
||||
|
||||
void *rockchip_get_cru(void)
|
||||
{
|
||||
struct rk3288_clk_priv *priv;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device(UCLASS_CLK, 0, &dev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
priv = dev_get_priv(dev);
|
||||
|
||||
return priv->cru;
|
||||
}
|
||||
|
||||
static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id,
|
||||
const struct pll_div *div)
|
||||
{
|
||||
int pll_id = rk_pll_id(clk_id);
|
||||
struct rk3288_pll *pll = &cru->pll[pll_id];
|
||||
/* All PLLs have same VCO and output frequency range restrictions. */
|
||||
uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
|
||||
uint output_hz = vco_hz / div->no;
|
||||
|
||||
debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
|
||||
(uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz);
|
||||
assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
|
||||
output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ &&
|
||||
(div->no == 1 || !(div->no % 2)));
|
||||
|
||||
/* enter reset */
|
||||
rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT);
|
||||
|
||||
rk_clrsetreg(&pll->con0,
|
||||
CLKR_MASK << CLKR_SHIFT | PLL_OD_MASK,
|
||||
((div->nr - 1) << CLKR_SHIFT) | (div->no - 1));
|
||||
rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1);
|
||||
rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1);
|
||||
|
||||
udelay(10);
|
||||
|
||||
/* return from reset */
|
||||
rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned int log2(unsigned int value)
|
||||
{
|
||||
return fls(value) - 1;
|
||||
}
|
||||
|
||||
static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf,
|
||||
unsigned int hz)
|
||||
{
|
||||
static const struct pll_div dpll_cfg[] = {
|
||||
{.nf = 25, .nr = 2, .no = 1},
|
||||
{.nf = 400, .nr = 9, .no = 2},
|
||||
{.nf = 500, .nr = 9, .no = 2},
|
||||
{.nf = 100, .nr = 3, .no = 1},
|
||||
};
|
||||
int cfg;
|
||||
|
||||
switch (hz) {
|
||||
case 300000000:
|
||||
cfg = 0;
|
||||
break;
|
||||
case 533000000: /* actually 533.3P MHz */
|
||||
cfg = 1;
|
||||
break;
|
||||
case 666000000: /* actually 666.6P MHz */
|
||||
cfg = 2;
|
||||
break;
|
||||
case 800000000:
|
||||
cfg = 3;
|
||||
break;
|
||||
default:
|
||||
debug("Unsupported SDRAM frequency");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* pll enter slow-mode */
|
||||
rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
|
||||
DPLL_MODE_SLOW << DPLL_MODE_SHIFT);
|
||||
|
||||
rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg]);
|
||||
|
||||
/* wait for pll lock */
|
||||
while (!(readl(&grf->soc_status[1]) & SOCSTS_DPLL_LOCK))
|
||||
udelay(1);
|
||||
|
||||
/* PLL enter normal-mode */
|
||||
rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
|
||||
DPLL_MODE_NORMAL << DPLL_MODE_SHIFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
#define VCO_MAX_KHZ 2200000
|
||||
#define VCO_MIN_KHZ 440000
|
||||
#define FREF_MAX_KHZ 2200000
|
||||
#define FREF_MIN_KHZ 269
|
||||
|
||||
static int pll_para_config(ulong freq_hz, struct pll_div *div, uint *ext_div)
|
||||
{
|
||||
uint ref_khz = OSC_HZ / 1000, nr, nf = 0;
|
||||
uint fref_khz;
|
||||
uint diff_khz, best_diff_khz;
|
||||
const uint max_nr = 1 << 6, max_nf = 1 << 12, max_no = 1 << 4;
|
||||
uint vco_khz;
|
||||
uint no = 1;
|
||||
uint freq_khz = freq_hz / 1000;
|
||||
|
||||
if (!freq_hz) {
|
||||
printf("%s: the frequency can not be 0 Hz\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
no = DIV_ROUND_UP(VCO_MIN_KHZ, freq_khz);
|
||||
if (ext_div) {
|
||||
*ext_div = DIV_ROUND_UP(no, max_no);
|
||||
no = DIV_ROUND_UP(no, *ext_div);
|
||||
}
|
||||
|
||||
/* only even divisors (and 1) are supported */
|
||||
if (no > 1)
|
||||
no = DIV_ROUND_UP(no, 2) * 2;
|
||||
|
||||
vco_khz = freq_khz * no;
|
||||
if (ext_div)
|
||||
vco_khz *= *ext_div;
|
||||
|
||||
if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || no > max_no) {
|
||||
printf("%s: Cannot find out a supported VCO for Frequency (%luHz).\n",
|
||||
__func__, freq_hz);
|
||||
return -1;
|
||||
}
|
||||
|
||||
div->no = no;
|
||||
|
||||
best_diff_khz = vco_khz;
|
||||
for (nr = 1; nr < max_nr && best_diff_khz; nr++) {
|
||||
fref_khz = ref_khz / nr;
|
||||
if (fref_khz < FREF_MIN_KHZ)
|
||||
break;
|
||||
if (fref_khz > FREF_MAX_KHZ)
|
||||
continue;
|
||||
|
||||
nf = vco_khz / fref_khz;
|
||||
if (nf >= max_nf)
|
||||
continue;
|
||||
diff_khz = vco_khz - nf * fref_khz;
|
||||
if (nf + 1 < max_nf && diff_khz > fref_khz / 2) {
|
||||
nf++;
|
||||
diff_khz = fref_khz - diff_khz;
|
||||
}
|
||||
|
||||
if (diff_khz >= best_diff_khz)
|
||||
continue;
|
||||
|
||||
best_diff_khz = diff_khz;
|
||||
div->nr = nr;
|
||||
div->nf = nf;
|
||||
}
|
||||
|
||||
if (best_diff_khz > 4 * 1000) {
|
||||
printf("%s: Failed to match output frequency %lu, difference is %u Hz, exceed 4MHZ\n",
|
||||
__func__, freq_hz, best_diff_khz * 1000);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mac_set_clk(struct rk3288_cru *cru,
|
||||
int periph, uint freq)
|
||||
{
|
||||
/* Assuming mac_clk is fed by an external clock */
|
||||
rk_clrsetreg(&cru->cru_clksel_con[21],
|
||||
RMII_EXTCLK_MASK << RMII_EXTCLK_SHIFT,
|
||||
RMII_EXTCLK_SELECT_EXT_CLK << RMII_EXTCLK_SHIFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_vop_set_clk(struct rk3288_cru *cru, struct rk3288_grf *grf,
|
||||
int periph, unsigned int rate_hz)
|
||||
{
|
||||
struct pll_div npll_config = {0};
|
||||
u32 lcdc_div;
|
||||
int ret;
|
||||
|
||||
ret = pll_para_config(rate_hz, &npll_config, &lcdc_div);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT,
|
||||
NPLL_MODE_SLOW << NPLL_MODE_SHIFT);
|
||||
rkclk_set_pll(cru, CLK_NEW, &npll_config);
|
||||
|
||||
/* waiting for pll lock */
|
||||
while (1) {
|
||||
if (readl(&grf->soc_status[1]) & SOCSTS_NPLL_LOCK)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT,
|
||||
NPLL_MODE_NORMAL << NPLL_MODE_SHIFT);
|
||||
|
||||
/* vop dclk source clk: npll,dclk_div: 1 */
|
||||
switch (periph) {
|
||||
case DCLK_VOP0:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[27], 0xff << 8 | 3 << 0,
|
||||
(lcdc_div - 1) << 8 | 2 << 0);
|
||||
break;
|
||||
case DCLK_VOP1:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[29], 0xff << 8 | 3 << 6,
|
||||
(lcdc_div - 1) << 8 | 2 << 6);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf)
|
||||
{
|
||||
u32 aclk_div;
|
||||
u32 hclk_div;
|
||||
u32 pclk_div;
|
||||
|
||||
/* pll enter slow-mode */
|
||||
rk_clrsetreg(&cru->cru_mode_con,
|
||||
GPLL_MODE_MASK << GPLL_MODE_SHIFT |
|
||||
CPLL_MODE_MASK << CPLL_MODE_SHIFT,
|
||||
GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
|
||||
CPLL_MODE_SLOW << CPLL_MODE_SHIFT);
|
||||
|
||||
/* init pll */
|
||||
rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg);
|
||||
rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg);
|
||||
|
||||
/* waiting for pll lock */
|
||||
while ((readl(&grf->soc_status[1]) &
|
||||
(SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) !=
|
||||
(SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK))
|
||||
udelay(1);
|
||||
|
||||
/*
|
||||
* pd_bus clock pll source selection and
|
||||
* set up dependent divisors for PCLK/HCLK and ACLK clocks.
|
||||
*/
|
||||
aclk_div = GPLL_HZ / PD_BUS_ACLK_HZ - 1;
|
||||
assert((aclk_div + 1) * PD_BUS_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
|
||||
hclk_div = PD_BUS_ACLK_HZ / PD_BUS_HCLK_HZ - 1;
|
||||
assert((hclk_div + 1) * PD_BUS_HCLK_HZ ==
|
||||
PD_BUS_ACLK_HZ && (hclk_div < 0x4) && (hclk_div != 0x2));
|
||||
|
||||
pclk_div = PD_BUS_ACLK_HZ / PD_BUS_PCLK_HZ - 1;
|
||||
assert((pclk_div + 1) * PD_BUS_PCLK_HZ ==
|
||||
PD_BUS_ACLK_HZ && pclk_div < 0x7);
|
||||
|
||||
rk_clrsetreg(&cru->cru_clksel_con[1],
|
||||
PD_BUS_PCLK_DIV_MASK << PD_BUS_PCLK_DIV_SHIFT |
|
||||
PD_BUS_HCLK_DIV_MASK << PD_BUS_HCLK_DIV_SHIFT |
|
||||
PD_BUS_ACLK_DIV0_MASK << PD_BUS_ACLK_DIV0_SHIFT |
|
||||
PD_BUS_ACLK_DIV1_MASK << PD_BUS_ACLK_DIV1_SHIFT,
|
||||
pclk_div << PD_BUS_PCLK_DIV_SHIFT |
|
||||
hclk_div << PD_BUS_HCLK_DIV_SHIFT |
|
||||
aclk_div << PD_BUS_ACLK_DIV0_SHIFT |
|
||||
0 << 0);
|
||||
|
||||
/*
|
||||
* peri clock pll source selection and
|
||||
* set up dependent divisors for PCLK/HCLK and ACLK clocks.
|
||||
*/
|
||||
aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
|
||||
assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
|
||||
|
||||
hclk_div = log2(PERI_ACLK_HZ / PERI_HCLK_HZ);
|
||||
assert((1 << hclk_div) * PERI_HCLK_HZ ==
|
||||
PERI_ACLK_HZ && (hclk_div < 0x4));
|
||||
|
||||
pclk_div = log2(PERI_ACLK_HZ / PERI_PCLK_HZ);
|
||||
assert((1 << pclk_div) * PERI_PCLK_HZ ==
|
||||
PERI_ACLK_HZ && (pclk_div < 0x4));
|
||||
|
||||
rk_clrsetreg(&cru->cru_clksel_con[10],
|
||||
PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT |
|
||||
PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT |
|
||||
PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT,
|
||||
PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT |
|
||||
pclk_div << PERI_PCLK_DIV_SHIFT |
|
||||
hclk_div << PERI_HCLK_DIV_SHIFT |
|
||||
aclk_div << PERI_ACLK_DIV_SHIFT);
|
||||
|
||||
/* PLL enter normal-mode */
|
||||
rk_clrsetreg(&cru->cru_mode_con,
|
||||
GPLL_MODE_MASK << GPLL_MODE_SHIFT |
|
||||
CPLL_MODE_MASK << CPLL_MODE_SHIFT,
|
||||
GPLL_MODE_NORMAL << GPLL_MODE_SHIFT |
|
||||
CPLL_MODE_NORMAL << CPLL_MODE_SHIFT);
|
||||
}
|
||||
#endif
|
||||
|
||||
void rkclk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf)
|
||||
{
|
||||
/* pll enter slow-mode */
|
||||
rk_clrsetreg(&cru->cru_mode_con,
|
||||
APLL_MODE_MASK << APLL_MODE_SHIFT,
|
||||
APLL_MODE_SLOW << APLL_MODE_SHIFT);
|
||||
|
||||
rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg);
|
||||
|
||||
/* waiting for pll lock */
|
||||
while (!(readl(&grf->soc_status[1]) & SOCSTS_APLL_LOCK))
|
||||
udelay(1);
|
||||
|
||||
/*
|
||||
* core clock pll source selection and
|
||||
* set up dependent divisors for MPAXI/M0AXI and ARM clocks.
|
||||
* core clock select apll, apll clk = 1800MHz
|
||||
* arm clk = 1800MHz, mpclk = 450MHz, m0clk = 900MHz
|
||||
*/
|
||||
rk_clrsetreg(&cru->cru_clksel_con[0],
|
||||
CORE_SEL_PLL_MASK << CORE_SEL_PLL_SHIFT |
|
||||
A17_DIV_MASK << A17_DIV_SHIFT |
|
||||
MP_DIV_MASK << MP_DIV_SHIFT |
|
||||
M0_DIV_MASK << M0_DIV_SHIFT,
|
||||
0 << A17_DIV_SHIFT |
|
||||
3 << MP_DIV_SHIFT |
|
||||
1 << M0_DIV_SHIFT);
|
||||
|
||||
/*
|
||||
* set up dependent divisors for L2RAM/ATCLK and PCLK clocks.
|
||||
* l2ramclk = 900MHz, atclk = 450MHz, pclk_dbg = 450MHz
|
||||
*/
|
||||
rk_clrsetreg(&cru->cru_clksel_con[37],
|
||||
CLK_L2RAM_DIV_MASK << CLK_L2RAM_DIV_SHIFT |
|
||||
ATCLK_CORE_DIV_CON_MASK << ATCLK_CORE_DIV_CON_SHIFT |
|
||||
PCLK_CORE_DBG_DIV_MASK >> PCLK_CORE_DBG_DIV_SHIFT,
|
||||
1 << CLK_L2RAM_DIV_SHIFT |
|
||||
3 << ATCLK_CORE_DIV_CON_SHIFT |
|
||||
3 << PCLK_CORE_DBG_DIV_SHIFT);
|
||||
|
||||
/* PLL enter normal-mode */
|
||||
rk_clrsetreg(&cru->cru_mode_con,
|
||||
APLL_MODE_MASK << APLL_MODE_SHIFT,
|
||||
APLL_MODE_NORMAL << APLL_MODE_SHIFT);
|
||||
}
|
||||
|
||||
/* Get pll rate by id */
|
||||
static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru,
|
||||
enum rk_clk_id clk_id)
|
||||
{
|
||||
uint32_t nr, no, nf;
|
||||
uint32_t con;
|
||||
int pll_id = rk_pll_id(clk_id);
|
||||
struct rk3288_pll *pll = &cru->pll[pll_id];
|
||||
static u8 clk_shift[CLK_COUNT] = {
|
||||
0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
|
||||
GPLL_MODE_SHIFT, NPLL_MODE_SHIFT
|
||||
};
|
||||
uint shift;
|
||||
|
||||
con = readl(&cru->cru_mode_con);
|
||||
shift = clk_shift[clk_id];
|
||||
switch ((con >> shift) & APLL_MODE_MASK) {
|
||||
case APLL_MODE_SLOW:
|
||||
return OSC_HZ;
|
||||
case APLL_MODE_NORMAL:
|
||||
/* normal mode */
|
||||
con = readl(&pll->con0);
|
||||
no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1;
|
||||
nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1;
|
||||
con = readl(&pll->con1);
|
||||
nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1;
|
||||
|
||||
return (24 * nf / (nr * no)) * 1000000;
|
||||
case APLL_MODE_DEEP:
|
||||
default:
|
||||
return 32768;
|
||||
}
|
||||
}
|
||||
|
||||
static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint gclk_rate,
|
||||
int periph)
|
||||
{
|
||||
uint src_rate;
|
||||
uint div, mux;
|
||||
u32 con;
|
||||
|
||||
switch (periph) {
|
||||
case HCLK_EMMC:
|
||||
con = readl(&cru->cru_clksel_con[12]);
|
||||
mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK;
|
||||
div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK;
|
||||
break;
|
||||
case HCLK_SDMMC:
|
||||
con = readl(&cru->cru_clksel_con[11]);
|
||||
mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK;
|
||||
div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK;
|
||||
break;
|
||||
case HCLK_SDIO0:
|
||||
con = readl(&cru->cru_clksel_con[12]);
|
||||
mux = (con >> SDIO0_PLL_SHIFT) & SDIO0_PLL_MASK;
|
||||
div = (con >> SDIO0_DIV_SHIFT) & SDIO0_DIV_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
src_rate = mux == EMMC_PLL_SELECT_24MHZ ? OSC_HZ : gclk_rate;
|
||||
return DIV_TO_RATE(src_rate, div);
|
||||
}
|
||||
|
||||
static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint gclk_rate,
|
||||
int periph, uint freq)
|
||||
{
|
||||
int src_clk_div;
|
||||
int mux;
|
||||
|
||||
debug("%s: gclk_rate=%u\n", __func__, gclk_rate);
|
||||
src_clk_div = RATE_TO_DIV(gclk_rate, freq);
|
||||
|
||||
if (src_clk_div > 0x3f) {
|
||||
src_clk_div = RATE_TO_DIV(OSC_HZ, freq);
|
||||
mux = EMMC_PLL_SELECT_24MHZ;
|
||||
assert((int)EMMC_PLL_SELECT_24MHZ ==
|
||||
(int)MMC0_PLL_SELECT_24MHZ);
|
||||
} else {
|
||||
mux = EMMC_PLL_SELECT_GENERAL;
|
||||
assert((int)EMMC_PLL_SELECT_GENERAL ==
|
||||
(int)MMC0_PLL_SELECT_GENERAL);
|
||||
}
|
||||
switch (periph) {
|
||||
case HCLK_EMMC:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[12],
|
||||
EMMC_PLL_MASK << EMMC_PLL_SHIFT |
|
||||
EMMC_DIV_MASK << EMMC_DIV_SHIFT,
|
||||
mux << EMMC_PLL_SHIFT |
|
||||
(src_clk_div - 1) << EMMC_DIV_SHIFT);
|
||||
break;
|
||||
case HCLK_SDMMC:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[11],
|
||||
MMC0_PLL_MASK << MMC0_PLL_SHIFT |
|
||||
MMC0_DIV_MASK << MMC0_DIV_SHIFT,
|
||||
mux << MMC0_PLL_SHIFT |
|
||||
(src_clk_div - 1) << MMC0_DIV_SHIFT);
|
||||
break;
|
||||
case HCLK_SDIO0:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[12],
|
||||
SDIO0_PLL_MASK << SDIO0_PLL_SHIFT |
|
||||
SDIO0_DIV_MASK << SDIO0_DIV_SHIFT,
|
||||
mux << SDIO0_PLL_SHIFT |
|
||||
(src_clk_div - 1) << SDIO0_DIV_SHIFT);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return rockchip_mmc_get_clk(cru, gclk_rate, periph);
|
||||
}
|
||||
|
||||
static ulong rockchip_spi_get_clk(struct rk3288_cru *cru, uint gclk_rate,
|
||||
int periph)
|
||||
{
|
||||
uint div, mux;
|
||||
u32 con;
|
||||
|
||||
switch (periph) {
|
||||
case SCLK_SPI0:
|
||||
con = readl(&cru->cru_clksel_con[25]);
|
||||
mux = (con >> SPI0_PLL_SHIFT) & SPI0_PLL_MASK;
|
||||
div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK;
|
||||
break;
|
||||
case SCLK_SPI1:
|
||||
con = readl(&cru->cru_clksel_con[25]);
|
||||
mux = (con >> SPI1_PLL_SHIFT) & SPI1_PLL_MASK;
|
||||
div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK;
|
||||
break;
|
||||
case SCLK_SPI2:
|
||||
con = readl(&cru->cru_clksel_con[39]);
|
||||
mux = (con >> SPI2_PLL_SHIFT) & SPI2_PLL_MASK;
|
||||
div = (con >> SPI2_DIV_SHIFT) & SPI2_DIV_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
assert(mux == SPI0_PLL_SELECT_GENERAL);
|
||||
|
||||
return DIV_TO_RATE(gclk_rate, div);
|
||||
}
|
||||
|
||||
static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint gclk_rate,
|
||||
int periph, uint freq)
|
||||
{
|
||||
int src_clk_div;
|
||||
|
||||
debug("%s: clk_general_rate=%u\n", __func__, gclk_rate);
|
||||
src_clk_div = RATE_TO_DIV(gclk_rate, freq);
|
||||
switch (periph) {
|
||||
case SCLK_SPI0:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[25],
|
||||
SPI0_PLL_MASK << SPI0_PLL_SHIFT |
|
||||
SPI0_DIV_MASK << SPI0_DIV_SHIFT,
|
||||
SPI0_PLL_SELECT_GENERAL << SPI0_PLL_SHIFT |
|
||||
src_clk_div << SPI0_DIV_SHIFT);
|
||||
break;
|
||||
case SCLK_SPI1:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[25],
|
||||
SPI1_PLL_MASK << SPI1_PLL_SHIFT |
|
||||
SPI1_DIV_MASK << SPI1_DIV_SHIFT,
|
||||
SPI1_PLL_SELECT_GENERAL << SPI1_PLL_SHIFT |
|
||||
src_clk_div << SPI1_DIV_SHIFT);
|
||||
break;
|
||||
case SCLK_SPI2:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[39],
|
||||
SPI2_PLL_MASK << SPI2_PLL_SHIFT |
|
||||
SPI2_DIV_MASK << SPI2_DIV_SHIFT,
|
||||
SPI2_PLL_SELECT_GENERAL << SPI2_PLL_SHIFT |
|
||||
src_clk_div << SPI2_DIV_SHIFT);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return rockchip_spi_get_clk(cru, gclk_rate, periph);
|
||||
}
|
||||
|
||||
static ulong rk3288_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
ulong new_rate, gclk_rate;
|
||||
|
||||
gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
|
||||
switch (clk->id) {
|
||||
case 0 ... 63:
|
||||
new_rate = rkclk_pll_get_rate(priv->cru, clk->id);
|
||||
break;
|
||||
case HCLK_EMMC:
|
||||
case HCLK_SDMMC:
|
||||
case HCLK_SDIO0:
|
||||
new_rate = rockchip_mmc_get_clk(priv->cru, gclk_rate, clk->id);
|
||||
break;
|
||||
case SCLK_SPI0:
|
||||
case SCLK_SPI1:
|
||||
case SCLK_SPI2:
|
||||
new_rate = rockchip_spi_get_clk(priv->cru, gclk_rate, clk->id);
|
||||
break;
|
||||
case PCLK_I2C0:
|
||||
case PCLK_I2C1:
|
||||
case PCLK_I2C2:
|
||||
case PCLK_I2C3:
|
||||
case PCLK_I2C4:
|
||||
case PCLK_I2C5:
|
||||
return gclk_rate;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return new_rate;
|
||||
}
|
||||
|
||||
static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
struct rk3288_cru *cru = priv->cru;
|
||||
ulong new_rate, gclk_rate;
|
||||
|
||||
gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
|
||||
switch (clk->id) {
|
||||
case CLK_DDR:
|
||||
new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate);
|
||||
break;
|
||||
case HCLK_EMMC:
|
||||
case HCLK_SDMMC:
|
||||
case HCLK_SDIO0:
|
||||
new_rate = rockchip_mmc_set_clk(cru, gclk_rate, clk->id, rate);
|
||||
break;
|
||||
case SCLK_SPI0:
|
||||
case SCLK_SPI1:
|
||||
case SCLK_SPI2:
|
||||
new_rate = rockchip_spi_set_clk(cru, gclk_rate, clk->id, rate);
|
||||
break;
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
case SCLK_MAC:
|
||||
new_rate = rockchip_mac_set_clk(priv->cru, clk->id, rate);
|
||||
break;
|
||||
case DCLK_VOP0:
|
||||
case DCLK_VOP1:
|
||||
new_rate = rockchip_vop_set_clk(cru, priv->grf, clk->id, rate);
|
||||
break;
|
||||
case SCLK_EDP_24M:
|
||||
/* clk_edp_24M source: 24M */
|
||||
rk_setreg(&cru->cru_clksel_con[28], 1 << 15);
|
||||
|
||||
/* rst edp */
|
||||
rk_setreg(&cru->cru_clksel_con[6], 1 << 15);
|
||||
udelay(1);
|
||||
rk_clrreg(&cru->cru_clksel_con[6], 1 << 15);
|
||||
new_rate = rate;
|
||||
break;
|
||||
case ACLK_VOP0:
|
||||
case ACLK_VOP1: {
|
||||
u32 div;
|
||||
|
||||
/* vop aclk source clk: cpll */
|
||||
div = CPLL_HZ / rate;
|
||||
assert((div - 1 < 64) && (div * rate == CPLL_HZ));
|
||||
|
||||
switch (clk->id) {
|
||||
case ACLK_VOP0:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[31],
|
||||
3 << 6 | 0x1f << 0,
|
||||
0 << 6 | (div - 1) << 0);
|
||||
break;
|
||||
case ACLK_VOP1:
|
||||
rk_clrsetreg(&cru->cru_clksel_con[31],
|
||||
3 << 14 | 0x1f << 8,
|
||||
0 << 14 | (div - 1) << 8);
|
||||
break;
|
||||
}
|
||||
new_rate = rate;
|
||||
break;
|
||||
}
|
||||
case PCLK_HDMI_CTRL:
|
||||
/* enable pclk hdmi ctrl */
|
||||
rk_clrreg(&cru->cru_clkgate_con[16], 1 << 9);
|
||||
|
||||
/* software reset hdmi */
|
||||
rk_setreg(&cru->cru_clkgate_con[7], 1 << 9);
|
||||
udelay(1);
|
||||
rk_clrreg(&cru->cru_clkgate_con[7], 1 << 9);
|
||||
new_rate = rate;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return new_rate;
|
||||
}
|
||||
|
||||
static struct clk_ops rk3288_clk_ops = {
|
||||
.get_rate = rk3288_clk_get_rate,
|
||||
.set_rate = rk3288_clk_set_rate,
|
||||
};
|
||||
|
||||
static int rk3288_clk_probe(struct udevice *dev)
|
||||
{
|
||||
struct rk3288_clk_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->cru = (struct rk3288_cru *)dev_get_addr(dev);
|
||||
priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
rkclk_init(priv->cru, priv->grf);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3288_clk_bind(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* The reset driver does not have a device node, so bind it here */
|
||||
ret = device_bind_driver(gd->dm_root, "rk3288_sysreset", "reset", &dev);
|
||||
if (ret)
|
||||
debug("Warning: No RK3288 reset driver: ret=%d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id rk3288_clk_ids[] = {
|
||||
{ .compatible = "rockchip,rk3288-cru" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(clk_rk3288) = {
|
||||
.name = "clk_rk3288",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = rk3288_clk_ids,
|
||||
.priv_auto_alloc_size = sizeof(struct rk3288_clk_priv),
|
||||
.ops = &rk3288_clk_ops,
|
||||
.bind = rk3288_clk_bind,
|
||||
.probe = rk3288_clk_probe,
|
||||
};
|
||||
107
u-boot/drivers/clk/clk_sandbox.c
Normal file
107
u-boot/drivers/clk/clk_sandbox.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* (C) Copyright 2015 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <asm/clk.h>
|
||||
|
||||
struct sandbox_clk_priv {
|
||||
ulong rate[SANDBOX_CLK_ID_COUNT];
|
||||
bool enabled[SANDBOX_CLK_ID_COUNT];
|
||||
};
|
||||
|
||||
static ulong sandbox_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
|
||||
if (clk->id >= SANDBOX_CLK_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
return priv->rate[clk->id];
|
||||
}
|
||||
|
||||
static ulong sandbox_clk_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
ulong old_rate;
|
||||
|
||||
if (clk->id >= SANDBOX_CLK_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
|
||||
old_rate = priv->rate[clk->id];
|
||||
priv->rate[clk->id] = rate;
|
||||
|
||||
return old_rate;
|
||||
}
|
||||
|
||||
static int sandbox_clk_enable(struct clk *clk)
|
||||
{
|
||||
struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
|
||||
if (clk->id >= SANDBOX_CLK_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
priv->enabled[clk->id] = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_clk_disable(struct clk *clk)
|
||||
{
|
||||
struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
|
||||
if (clk->id >= SANDBOX_CLK_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
priv->enabled[clk->id] = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clk_ops sandbox_clk_ops = {
|
||||
.get_rate = sandbox_clk_get_rate,
|
||||
.set_rate = sandbox_clk_set_rate,
|
||||
.enable = sandbox_clk_enable,
|
||||
.disable = sandbox_clk_disable,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_clk_ids[] = {
|
||||
{ .compatible = "sandbox,clk" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(clk_sandbox) = {
|
||||
.name = "clk_sandbox",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = sandbox_clk_ids,
|
||||
.ops = &sandbox_clk_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_clk_priv),
|
||||
};
|
||||
|
||||
ulong sandbox_clk_query_rate(struct udevice *dev, int id)
|
||||
{
|
||||
struct sandbox_clk_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (id < 0 || id >= SANDBOX_CLK_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
return priv->rate[id];
|
||||
}
|
||||
|
||||
int sandbox_clk_query_enable(struct udevice *dev, int id)
|
||||
{
|
||||
struct sandbox_clk_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (id < 0 || id >= SANDBOX_CLK_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
return priv->enabled[id];
|
||||
}
|
||||
101
u-boot/drivers/clk/clk_sandbox_test.c
Normal file
101
u-boot/drivers/clk/clk_sandbox_test.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <clk.h>
|
||||
#include <asm/clk.h>
|
||||
|
||||
struct sandbox_clk_test {
|
||||
struct clk clks[SANDBOX_CLK_TEST_ID_COUNT];
|
||||
};
|
||||
|
||||
static const char * const sandbox_clk_test_names[] = {
|
||||
[SANDBOX_CLK_TEST_ID_FIXED] = "fixed",
|
||||
[SANDBOX_CLK_TEST_ID_SPI] = "spi",
|
||||
[SANDBOX_CLK_TEST_ID_I2C] = "i2c",
|
||||
};
|
||||
|
||||
int sandbox_clk_test_get(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_clk_test *sbct = dev_get_priv(dev);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < SANDBOX_CLK_TEST_ID_COUNT; i++) {
|
||||
ret = clk_get_by_name(dev, sandbox_clk_test_names[i],
|
||||
&sbct->clks[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong sandbox_clk_test_get_rate(struct udevice *dev, int id)
|
||||
{
|
||||
struct sandbox_clk_test *sbct = dev_get_priv(dev);
|
||||
|
||||
if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
return clk_get_rate(&sbct->clks[id]);
|
||||
}
|
||||
|
||||
ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate)
|
||||
{
|
||||
struct sandbox_clk_test *sbct = dev_get_priv(dev);
|
||||
|
||||
if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
return clk_set_rate(&sbct->clks[id], rate);
|
||||
}
|
||||
|
||||
int sandbox_clk_test_enable(struct udevice *dev, int id)
|
||||
{
|
||||
struct sandbox_clk_test *sbct = dev_get_priv(dev);
|
||||
|
||||
if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
return clk_enable(&sbct->clks[id]);
|
||||
}
|
||||
|
||||
int sandbox_clk_test_disable(struct udevice *dev, int id)
|
||||
{
|
||||
struct sandbox_clk_test *sbct = dev_get_priv(dev);
|
||||
|
||||
if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
return clk_disable(&sbct->clks[id]);
|
||||
}
|
||||
|
||||
int sandbox_clk_test_free(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_clk_test *sbct = dev_get_priv(dev);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < SANDBOX_CLK_TEST_ID_COUNT; i++) {
|
||||
ret = clk_free(&sbct->clks[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id sandbox_clk_test_ids[] = {
|
||||
{ .compatible = "sandbox,clk-test" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sandbox_clk_test) = {
|
||||
.name = "sandbox_clk_test",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = sandbox_clk_test_ids,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_clk_test),
|
||||
};
|
||||
18
u-boot/drivers/clk/exynos/Kconfig
Normal file
18
u-boot/drivers/clk/exynos/Kconfig
Normal file
@@ -0,0 +1,18 @@
|
||||
config CLK_EXYNOS
|
||||
bool
|
||||
select CLK
|
||||
help
|
||||
This enables support for common clock driver API on Samsung
|
||||
Exynos SoCs.
|
||||
|
||||
menu "Clock drivers for Exynos SoCs"
|
||||
depends on CLK_EXYNOS
|
||||
|
||||
config CLK_EXYNOS7420
|
||||
bool "Clock driver for Samsung's Exynos7420 SoC"
|
||||
default y
|
||||
help
|
||||
This enables common clock driver support for platforms based
|
||||
on Samsung Exynos7420 SoC.
|
||||
|
||||
endmenu
|
||||
9
u-boot/drivers/clk/exynos/Makefile
Normal file
9
u-boot/drivers/clk/exynos/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# Copyright (C) 2016 Samsung Electronics
|
||||
# Thomas Abraham <thomas.ab@samsung.com>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-y += clk-pll.o
|
||||
obj-$(CONFIG_CLK_EXYNOS7420) += clk-exynos7420.o
|
||||
236
u-boot/drivers/clk/exynos/clk-exynos7420.c
Normal file
236
u-boot/drivers/clk/exynos/clk-exynos7420.c
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Samsung Exynos7420 clock driver.
|
||||
* Copyright (C) 2016 Samsung Electronics
|
||||
* Thomas Abraham <thomas.ab@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <asm/io.h>
|
||||
#include <dt-bindings/clock/exynos7420-clk.h>
|
||||
#include "clk-pll.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define DIVIDER(reg, shift, mask) \
|
||||
(((readl(reg) >> shift) & mask) + 1)
|
||||
|
||||
/* CMU TOPC block device structure */
|
||||
struct exynos7420_clk_cmu_topc {
|
||||
unsigned int rsvd1[68];
|
||||
unsigned int bus0_pll_con[2];
|
||||
unsigned int rsvd2[2];
|
||||
unsigned int bus1_pll_con[2];
|
||||
unsigned int rsvd3[54];
|
||||
unsigned int mux_sel[6];
|
||||
unsigned int rsvd4[250];
|
||||
unsigned int div[4];
|
||||
};
|
||||
|
||||
/* CMU TOP0 block device structure */
|
||||
struct exynos7420_clk_cmu_top0 {
|
||||
unsigned int rsvd0[128];
|
||||
unsigned int mux_sel[7];
|
||||
unsigned int rsvd1[261];
|
||||
unsigned int div_peric[5];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct exynos7420_clk_topc_priv - private data for CMU topc clock driver.
|
||||
*
|
||||
* @topc: base address of the memory mapped CMU TOPC controller.
|
||||
* @fin_freq: frequency of the Oscillator clock.
|
||||
* @sclk_bus0_pll_a: frequency of sclk_bus0_pll_a clock.
|
||||
* @sclk_bus1_pll_a: frequency of sclk_bus1_pll_a clock.
|
||||
*/
|
||||
struct exynos7420_clk_topc_priv {
|
||||
struct exynos7420_clk_cmu_topc *topc;
|
||||
unsigned long fin_freq;
|
||||
unsigned long sclk_bus0_pll_a;
|
||||
unsigned long sclk_bus1_pll_a;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct exynos7420_clk_top0_priv - private data for CMU top0 clock driver.
|
||||
*
|
||||
* @top0: base address of the memory mapped CMU TOP0 controller.
|
||||
* @mout_top0_bus0_pll_half: frequency of mout_top0_bus0_pll_half clock
|
||||
* @sclk_uart2: frequency of sclk_uart2 clock.
|
||||
*/
|
||||
struct exynos7420_clk_top0_priv {
|
||||
struct exynos7420_clk_cmu_top0 *top0;
|
||||
unsigned long mout_top0_bus0_pll_half;
|
||||
unsigned long sclk_uart2;
|
||||
};
|
||||
|
||||
static ulong exynos7420_topc_get_rate(struct clk *clk)
|
||||
{
|
||||
struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev);
|
||||
|
||||
switch (clk->id) {
|
||||
case DOUT_SCLK_BUS0_PLL:
|
||||
case SCLK_BUS0_PLL_A:
|
||||
case SCLK_BUS0_PLL_B:
|
||||
return priv->sclk_bus0_pll_a;
|
||||
case DOUT_SCLK_BUS1_PLL:
|
||||
case SCLK_BUS1_PLL_A:
|
||||
case SCLK_BUS1_PLL_B:
|
||||
return priv->sclk_bus1_pll_a;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct clk_ops exynos7420_clk_topc_ops = {
|
||||
.get_rate = exynos7420_topc_get_rate,
|
||||
};
|
||||
|
||||
static int exynos7420_clk_topc_probe(struct udevice *dev)
|
||||
{
|
||||
struct exynos7420_clk_topc_priv *priv = dev_get_priv(dev);
|
||||
struct exynos7420_clk_cmu_topc *topc;
|
||||
struct clk in_clk;
|
||||
unsigned long rate;
|
||||
fdt_addr_t base;
|
||||
int ret;
|
||||
|
||||
base = dev_get_addr(dev);
|
||||
if (base == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
topc = (struct exynos7420_clk_cmu_topc *)base;
|
||||
priv->topc = topc;
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &in_clk);
|
||||
if (ret >= 0)
|
||||
priv->fin_freq = clk_get_rate(&in_clk);
|
||||
|
||||
rate = pll145x_get_rate(&topc->bus0_pll_con[0], priv->fin_freq);
|
||||
if (readl(&topc->mux_sel[1]) & (1 << 16))
|
||||
rate >>= 1;
|
||||
rate /= DIVIDER(&topc->div[3], 0, 0xf);
|
||||
priv->sclk_bus0_pll_a = rate;
|
||||
|
||||
rate = pll145x_get_rate(&topc->bus1_pll_con[0], priv->fin_freq) /
|
||||
DIVIDER(&topc->div[3], 8, 0xf);
|
||||
priv->sclk_bus1_pll_a = rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong exynos7420_top0_get_rate(struct clk *clk)
|
||||
{
|
||||
struct exynos7420_clk_top0_priv *priv = dev_get_priv(clk->dev);
|
||||
struct exynos7420_clk_cmu_top0 *top0 = priv->top0;
|
||||
|
||||
switch (clk->id) {
|
||||
case CLK_SCLK_UART2:
|
||||
return priv->mout_top0_bus0_pll_half /
|
||||
DIVIDER(&top0->div_peric[3], 8, 0xf);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct clk_ops exynos7420_clk_top0_ops = {
|
||||
.get_rate = exynos7420_top0_get_rate,
|
||||
};
|
||||
|
||||
static int exynos7420_clk_top0_probe(struct udevice *dev)
|
||||
{
|
||||
struct exynos7420_clk_top0_priv *priv;
|
||||
struct exynos7420_clk_cmu_top0 *top0;
|
||||
struct clk in_clk;
|
||||
fdt_addr_t base;
|
||||
int ret;
|
||||
|
||||
priv = dev_get_priv(dev);
|
||||
if (!priv)
|
||||
return -EINVAL;
|
||||
|
||||
base = dev_get_addr(dev);
|
||||
if (base == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
top0 = (struct exynos7420_clk_cmu_top0 *)base;
|
||||
priv->top0 = top0;
|
||||
|
||||
ret = clk_get_by_index(dev, 1, &in_clk);
|
||||
if (ret >= 0) {
|
||||
priv->mout_top0_bus0_pll_half =
|
||||
clk_get_rate(&in_clk);
|
||||
if (readl(&top0->mux_sel[1]) & (1 << 16))
|
||||
priv->mout_top0_bus0_pll_half >>= 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong exynos7420_peric1_get_rate(struct clk *clk)
|
||||
{
|
||||
struct clk in_clk;
|
||||
unsigned int ret;
|
||||
unsigned long freq = 0;
|
||||
|
||||
switch (clk->id) {
|
||||
case SCLK_UART2:
|
||||
ret = clk_get_by_index(clk->dev, 3, &in_clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
freq = clk_get_rate(&in_clk);
|
||||
break;
|
||||
}
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static struct clk_ops exynos7420_clk_peric1_ops = {
|
||||
.get_rate = exynos7420_peric1_get_rate,
|
||||
};
|
||||
|
||||
static const struct udevice_id exynos7420_clk_topc_compat[] = {
|
||||
{ .compatible = "samsung,exynos7-clock-topc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(exynos7420_clk_topc) = {
|
||||
.name = "exynos7420-clock-topc",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = exynos7420_clk_topc_compat,
|
||||
.probe = exynos7420_clk_topc_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct exynos7420_clk_topc_priv),
|
||||
.ops = &exynos7420_clk_topc_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
|
||||
static const struct udevice_id exynos7420_clk_top0_compat[] = {
|
||||
{ .compatible = "samsung,exynos7-clock-top0" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(exynos7420_clk_top0) = {
|
||||
.name = "exynos7420-clock-top0",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = exynos7420_clk_top0_compat,
|
||||
.probe = exynos7420_clk_top0_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct exynos7420_clk_top0_priv),
|
||||
.ops = &exynos7420_clk_top0_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
|
||||
static const struct udevice_id exynos7420_clk_peric1_compat[] = {
|
||||
{ .compatible = "samsung,exynos7-clock-peric1" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(exynos7420_clk_peric1) = {
|
||||
.name = "exynos7420-clock-peric1",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = exynos7420_clk_peric1_compat,
|
||||
.ops = &exynos7420_clk_peric1_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
33
u-boot/drivers/clk/exynos/clk-pll.c
Normal file
33
u-boot/drivers/clk/exynos/clk-pll.c
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Exynos PLL helper functions for clock drivers.
|
||||
* Copyright (C) 2016 Samsung Electronics
|
||||
* Thomas Abraham <thomas.ab@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <div64.h>
|
||||
|
||||
#define PLL145X_MDIV_SHIFT 16
|
||||
#define PLL145X_MDIV_MASK 0x3ff
|
||||
#define PLL145X_PDIV_SHIFT 8
|
||||
#define PLL145X_PDIV_MASK 0x3f
|
||||
#define PLL145X_SDIV_SHIFT 0
|
||||
#define PLL145X_SDIV_MASK 0x7
|
||||
|
||||
unsigned long pll145x_get_rate(unsigned int *con1, unsigned long fin_freq)
|
||||
{
|
||||
unsigned long pll_con1 = readl(con1);
|
||||
unsigned long mdiv, sdiv, pdiv;
|
||||
uint64_t fvco = fin_freq;
|
||||
|
||||
mdiv = (pll_con1 >> PLL145X_MDIV_SHIFT) & PLL145X_MDIV_MASK;
|
||||
pdiv = (pll_con1 >> PLL145X_PDIV_SHIFT) & PLL145X_PDIV_MASK;
|
||||
sdiv = (pll_con1 >> PLL145X_SDIV_SHIFT) & PLL145X_SDIV_MASK;
|
||||
|
||||
fvco *= mdiv;
|
||||
do_div(fvco, (pdiv << sdiv));
|
||||
return (unsigned long)fvco;
|
||||
}
|
||||
9
u-boot/drivers/clk/exynos/clk-pll.h
Normal file
9
u-boot/drivers/clk/exynos/clk-pll.h
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Exynos PLL helper functions for clock drivers.
|
||||
* Copyright (C) 2016 Samsung Electronics
|
||||
* Thomas Abraham <thomas.ab@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
unsigned long pll145x_get_rate(unsigned int *con1, unsigned long fin_freq);
|
||||
13
u-boot/drivers/clk/uniphier/Kconfig
Normal file
13
u-boot/drivers/clk/uniphier/Kconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
config CLK_UNIPHIER
|
||||
bool
|
||||
select CLK
|
||||
select SPL_CLK
|
||||
|
||||
menu "Clock drivers for UniPhier SoCs"
|
||||
depends on CLK_UNIPHIER
|
||||
|
||||
config CLK_UNIPHIER_MIO
|
||||
bool "Clock driver for UniPhier Media I/O block"
|
||||
default y
|
||||
|
||||
endmenu
|
||||
3
u-boot/drivers/clk/uniphier/Makefile
Normal file
3
u-boot/drivers/clk/uniphier/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
obj-y += clk-uniphier-core.o
|
||||
|
||||
obj-$(CONFIG_CLK_UNIPHIER_MIO) += clk-uniphier-mio.o
|
||||
156
u-boot/drivers/clk/uniphier/clk-uniphier-core.c
Normal file
156
u-boot/drivers/clk/uniphier/clk-uniphier-core.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <mapmem.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm/device.h>
|
||||
|
||||
#include "clk-uniphier.h"
|
||||
|
||||
static int uniphier_clk_enable(struct clk *clk)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
struct uniphier_clk_gate_data *gate = priv->socdata->gate;
|
||||
unsigned int nr_gate = priv->socdata->nr_gate;
|
||||
void __iomem *reg;
|
||||
u32 mask, data, tmp;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_gate; i++) {
|
||||
if (gate[i].index != clk->id)
|
||||
continue;
|
||||
|
||||
reg = priv->base + gate[i].reg;
|
||||
mask = gate[i].mask;
|
||||
data = gate[i].data & mask;
|
||||
|
||||
tmp = readl(reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= data & mask;
|
||||
debug("%s: %p: %08x\n", __func__, reg, tmp);
|
||||
writel(tmp, reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong uniphier_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
struct uniphier_clk_rate_data *rdata = priv->socdata->rate;
|
||||
unsigned int nr_rdata = priv->socdata->nr_rate;
|
||||
void __iomem *reg;
|
||||
u32 mask, data;
|
||||
ulong matched_rate = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_rdata; i++) {
|
||||
if (rdata[i].index != clk->id)
|
||||
continue;
|
||||
|
||||
if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED)
|
||||
return rdata[i].rate;
|
||||
|
||||
reg = priv->base + rdata[i].reg;
|
||||
mask = rdata[i].mask;
|
||||
data = rdata[i].data & mask;
|
||||
if ((readl(reg) & mask) == data) {
|
||||
if (matched_rate && rdata[i].rate != matched_rate) {
|
||||
printf("failed to get clk rate for insane register values\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
matched_rate = rdata[i].rate;
|
||||
}
|
||||
}
|
||||
|
||||
debug("%s: rate = %lu\n", __func__, matched_rate);
|
||||
|
||||
return matched_rate;
|
||||
}
|
||||
|
||||
static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
struct uniphier_clk_rate_data *rdata = priv->socdata->rate;
|
||||
unsigned int nr_rdata = priv->socdata->nr_rate;
|
||||
void __iomem *reg;
|
||||
u32 mask, data, tmp;
|
||||
ulong best_rate = 0;
|
||||
int i;
|
||||
|
||||
/* first, decide the best match rate */
|
||||
for (i = 0; i < nr_rdata; i++) {
|
||||
if (rdata[i].index != clk->id)
|
||||
continue;
|
||||
|
||||
if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED)
|
||||
return 0;
|
||||
|
||||
if (rdata[i].rate > best_rate && rdata[i].rate <= rate)
|
||||
best_rate = rdata[i].rate;
|
||||
}
|
||||
|
||||
if (!best_rate)
|
||||
return -ENODEV;
|
||||
|
||||
debug("%s: requested rate = %lu, set rate = %lu\n", __func__,
|
||||
rate, best_rate);
|
||||
|
||||
/* second, really set registers */
|
||||
for (i = 0; i < nr_rdata; i++) {
|
||||
if (rdata[i].index != clk->id || rdata[i].rate != best_rate)
|
||||
continue;
|
||||
|
||||
reg = priv->base + rdata[i].reg;
|
||||
mask = rdata[i].mask;
|
||||
data = rdata[i].data & mask;
|
||||
|
||||
tmp = readl(reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= data;
|
||||
debug("%s: %p: %08x\n", __func__, reg, tmp);
|
||||
writel(tmp, reg);
|
||||
}
|
||||
|
||||
return best_rate;
|
||||
}
|
||||
|
||||
const struct clk_ops uniphier_clk_ops = {
|
||||
.enable = uniphier_clk_enable,
|
||||
.get_rate = uniphier_clk_get_rate,
|
||||
.set_rate = uniphier_clk_set_rate,
|
||||
};
|
||||
|
||||
int uniphier_clk_probe(struct udevice *dev)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(dev);
|
||||
fdt_addr_t addr;
|
||||
|
||||
addr = dev_get_addr(dev);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
priv->base = map_sysmem(addr, SZ_4K);
|
||||
if (!priv->base)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->socdata = (void *)dev_get_driver_data(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uniphier_clk_remove(struct udevice *dev)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(dev);
|
||||
|
||||
unmap_sysmem(priv->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
185
u-boot/drivers/clk/uniphier/clk-uniphier-mio.c
Normal file
185
u-boot/drivers/clk/uniphier/clk-uniphier-mio.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <dm/device.h>
|
||||
|
||||
#include "clk-uniphier.h"
|
||||
|
||||
#define UNIPHIER_MIO_CLK_GATE_SD(ch, idx) \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x20 + 0x200 * (ch), \
|
||||
.mask = 0x00000100, \
|
||||
.data = 0x00000100, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x110 + 0x200 * (ch), \
|
||||
.mask = 0x00000001, \
|
||||
.data = 0x00000001, \
|
||||
}
|
||||
|
||||
#define UNIPHIER_MIO_CLK_RATE_SD(ch, idx) \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00000000, \
|
||||
.rate = 44444444, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00010000, \
|
||||
.rate = 33333333, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00020000, \
|
||||
.rate = 50000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00020000, \
|
||||
.rate = 66666666, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00001000, \
|
||||
.rate = 100000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00001100, \
|
||||
.rate = 40000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00001200, \
|
||||
.rate = 25000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00001300, \
|
||||
.rate = 22222222, \
|
||||
}
|
||||
|
||||
#define UNIPHIER_MIO_CLK_GATE_USB(ch, idx) \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x20 + 0x200 * (ch), \
|
||||
.mask = 0x30000000, \
|
||||
.data = 0x30000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x110 + 0x200 * (ch), \
|
||||
.mask = 0x01000000, \
|
||||
.data = 0x01000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x114 + 0x200 * (ch), \
|
||||
.mask = 0x00000001, \
|
||||
.data = 0x00000001, \
|
||||
}
|
||||
|
||||
#define UNIPHIER_MIO_CLK_GATE_DMAC(idx) \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x20, \
|
||||
.mask = 0x02000000, \
|
||||
.data = 0x02000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x110, \
|
||||
.mask = 0x00020000, \
|
||||
.data = 0x00020000, \
|
||||
}
|
||||
|
||||
static struct uniphier_clk_gate_data uniphier_mio_clk_gate[] = {
|
||||
UNIPHIER_MIO_CLK_GATE_SD(0, 0),
|
||||
UNIPHIER_MIO_CLK_GATE_SD(1, 1),
|
||||
UNIPHIER_MIO_CLK_GATE_SD(2, 2), /* for PH1-Pro4 only */
|
||||
UNIPHIER_MIO_CLK_GATE_USB(0, 3),
|
||||
UNIPHIER_MIO_CLK_GATE_USB(1, 4),
|
||||
UNIPHIER_MIO_CLK_GATE_USB(2, 5),
|
||||
UNIPHIER_MIO_CLK_GATE_DMAC(6),
|
||||
UNIPHIER_MIO_CLK_GATE_USB(3, 7), /* for PH1-sLD3 only */
|
||||
};
|
||||
|
||||
static struct uniphier_clk_rate_data uniphier_mio_clk_rate[] = {
|
||||
UNIPHIER_MIO_CLK_RATE_SD(0, 0),
|
||||
UNIPHIER_MIO_CLK_RATE_SD(1, 1),
|
||||
UNIPHIER_MIO_CLK_RATE_SD(2, 2), /* for PH1-Pro4 only */
|
||||
};
|
||||
|
||||
static struct uniphier_clk_soc_data uniphier_mio_clk_data = {
|
||||
.gate = uniphier_mio_clk_gate,
|
||||
.nr_gate = ARRAY_SIZE(uniphier_mio_clk_gate),
|
||||
.rate = uniphier_mio_clk_rate,
|
||||
.nr_rate = ARRAY_SIZE(uniphier_mio_clk_rate),
|
||||
};
|
||||
|
||||
static const struct udevice_id uniphier_mio_clk_match[] = {
|
||||
{
|
||||
.compatible = "socionext,ph1-sld3-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-ld4-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-pro4-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-sld8-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-pro5-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,proxstream2-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-ld11-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-ld20-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(uniphier_mio_clk) = {
|
||||
.name = "uniphier-mio-clk",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = uniphier_mio_clk_match,
|
||||
.probe = uniphier_clk_probe,
|
||||
.remove = uniphier_clk_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct uniphier_clk_priv),
|
||||
.ops = &uniphier_clk_ops,
|
||||
};
|
||||
57
u-boot/drivers/clk/uniphier/clk-uniphier.h
Normal file
57
u-boot/drivers/clk/uniphier/clk-uniphier.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __CLK_UNIPHIER_H__
|
||||
#define __CLK_UNIPHIER_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
struct uniphier_clk_gate_data {
|
||||
int index;
|
||||
unsigned int reg;
|
||||
u32 mask;
|
||||
u32 data;
|
||||
};
|
||||
|
||||
struct uniphier_clk_rate_data {
|
||||
int index;
|
||||
unsigned int reg;
|
||||
#define UNIPHIER_CLK_RATE_IS_FIXED UINT_MAX
|
||||
u32 mask;
|
||||
u32 data;
|
||||
unsigned long rate;
|
||||
};
|
||||
|
||||
struct uniphier_clk_soc_data {
|
||||
struct uniphier_clk_gate_data *gate;
|
||||
unsigned int nr_gate;
|
||||
struct uniphier_clk_rate_data *rate;
|
||||
unsigned int nr_rate;
|
||||
};
|
||||
|
||||
#define UNIPHIER_CLK_FIXED_RATE(i, f) \
|
||||
{ \
|
||||
.index = i, \
|
||||
.reg = UNIPHIER_CLK_RATE_IS_FIXED, \
|
||||
.rate = f, \
|
||||
}
|
||||
|
||||
/**
|
||||
* struct uniphier_clk_priv - private data for UniPhier clock driver
|
||||
*
|
||||
* @base: base address of the clock provider
|
||||
* @socdata: SoC specific data
|
||||
*/
|
||||
struct uniphier_clk_priv {
|
||||
void __iomem *base;
|
||||
struct uniphier_clk_soc_data *socdata;
|
||||
};
|
||||
|
||||
extern const struct clk_ops uniphier_clk_ops;
|
||||
int uniphier_clk_probe(struct udevice *dev);
|
||||
int uniphier_clk_remove(struct udevice *dev);
|
||||
|
||||
#endif /* __CLK_UNIPHIER_H__ */
|
||||
Reference in New Issue
Block a user