mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-25 04:23:46 +00:00
e25080664a
When a driver declares DM_FLAG_PRE_RELOC flag, it wishes to be bound before relocation. However due to a bug in the DM core, the flag only takes effect when devices are statically declared via U_BOOT_DEVICE(). This bug has been fixed recently by commit "dm: core: Respect drivers with the DM_FLAG_PRE_RELOC flag in lists_bind_fdt()", but with the fix, it has a side effect that all existing drivers that declared DM_FLAG_PRE_RELOC flag will be bound before relocation now. This may expose potential boot failure on some boards due to insufficient memory during the pre-relocation stage. To mitigate this potential impact, the following changes are implemented: - Remove DM_FLAG_PRE_RELOC flag in the driver, if the driver only supports configuration from device tree (OF_CONTROL) - Keep DM_FLAG_PRE_RELOC flag in the driver only if the device is statically declared via U_BOOT_DEVICE() - Surround DM_FLAG_PRE_RELOC flag with OF_CONTROL check, for drivers that support both statically declared devices and configuration from device tree Signed-off-by: Bin Meng <bmeng.cn@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org>
230 lines
5.5 KiB
C
230 lines
5.5 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Samsung Exynos7420 clock driver.
|
|
* Copyright (C) 2016 Samsung Electronics
|
|
* Thomas Abraham <thomas.ab@samsung.com>
|
|
*/
|
|
|
|
#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"
|
|
|
|
#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 = devfdt_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 = devfdt_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,
|
|
};
|
|
|
|
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,
|
|
};
|
|
|
|
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,
|
|
};
|