mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-09-11 15:23:22 +09:00
![Lukasz Majewski](/assets/img/avatar_default.png)
This patch brings the files from Linux kernel (linux-stable/linux-5.1.y SHA1: 5752b50477da)to provide clocks support as it is used on the Linux kernel with Common Clock Framework [CCF] setup. The directory structure has been preserved. The ported code only supports reading information from PLL, MUX, Divider, etc and enabling/disabling the clocks USDHCx/ECSPIx depending on used bus. Moreover, it is agnostic to the alias numbering as the information about the clock is read from the device tree. One needs to pay attention to the comments indicating necessary for U-Boot's driver model changes. If needed, the code can be extended to support the "set" part of the clock management. Signed-off-by: Lukasz Majewski <lukma@denx.de>
180 lines
4.5 KiB
C
180 lines
4.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2019 DENX Software Engineering
|
|
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <clk-uclass.h>
|
|
#include <dm.h>
|
|
#include <asm/arch/clock.h>
|
|
#include <asm/arch/imx-regs.h>
|
|
#include <dt-bindings/clock/imx6qdl-clock.h>
|
|
|
|
#include "clk.h"
|
|
|
|
static int imx6q_check_id(ulong id)
|
|
{
|
|
if (id < IMX6QDL_CLK_DUMMY || id >= IMX6QDL_CLK_END) {
|
|
printf("%s: Invalid clk ID #%lu\n", __func__, id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ulong imx6q_clk_get_rate(struct clk *clk)
|
|
{
|
|
struct clk *c;
|
|
int ret;
|
|
|
|
debug("%s(#%lu)\n", __func__, clk->id);
|
|
|
|
ret = imx6q_check_id(clk->id);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = clk_get_by_id(clk->id, &c);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return clk_get_rate(c);
|
|
}
|
|
|
|
static ulong imx6q_clk_set_rate(struct clk *clk, unsigned long rate)
|
|
{
|
|
debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate);
|
|
|
|
return rate;
|
|
}
|
|
|
|
static int __imx6q_clk_enable(struct clk *clk, bool enable)
|
|
{
|
|
struct clk *c;
|
|
int ret = 0;
|
|
|
|
debug("%s(#%lu) en: %d\n", __func__, clk->id, enable);
|
|
|
|
ret = imx6q_check_id(clk->id);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = clk_get_by_id(clk->id, &c);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (enable)
|
|
ret = clk_enable(c);
|
|
else
|
|
ret = clk_disable(c);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int imx6q_clk_disable(struct clk *clk)
|
|
{
|
|
return __imx6q_clk_enable(clk, 0);
|
|
}
|
|
|
|
static int imx6q_clk_enable(struct clk *clk)
|
|
{
|
|
return __imx6q_clk_enable(clk, 1);
|
|
}
|
|
|
|
static struct clk_ops imx6q_clk_ops = {
|
|
.set_rate = imx6q_clk_set_rate,
|
|
.get_rate = imx6q_clk_get_rate,
|
|
.enable = imx6q_clk_enable,
|
|
.disable = imx6q_clk_disable,
|
|
};
|
|
|
|
static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
|
|
|
|
static int imx6q_clk_probe(struct udevice *dev)
|
|
{
|
|
void *base;
|
|
|
|
/* Anatop clocks */
|
|
base = (void *)ANATOP_BASE_ADDR;
|
|
|
|
clk_dm(IMX6QDL_CLK_PLL2,
|
|
imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc",
|
|
base + 0x30, 0x1));
|
|
clk_dm(IMX6QDL_CLK_PLL3_USB_OTG,
|
|
imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc",
|
|
base + 0x10, 0x3));
|
|
clk_dm(IMX6QDL_CLK_PLL3_60M,
|
|
imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8));
|
|
clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M,
|
|
imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0));
|
|
clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M,
|
|
imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2));
|
|
|
|
/* CCM clocks */
|
|
base = dev_read_addr_ptr(dev);
|
|
if (base == (void *)FDT_ADDR_T_NONE)
|
|
return -EINVAL;
|
|
|
|
clk_dm(IMX6QDL_CLK_USDHC1_SEL,
|
|
imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1,
|
|
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
|
|
clk_dm(IMX6QDL_CLK_USDHC2_SEL,
|
|
imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1,
|
|
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
|
|
clk_dm(IMX6QDL_CLK_USDHC3_SEL,
|
|
imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1,
|
|
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
|
|
clk_dm(IMX6QDL_CLK_USDHC4_SEL,
|
|
imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1,
|
|
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
|
|
|
|
clk_dm(IMX6QDL_CLK_USDHC1_PODF,
|
|
imx_clk_divider("usdhc1_podf", "usdhc1_sel",
|
|
base + 0x24, 11, 3));
|
|
clk_dm(IMX6QDL_CLK_USDHC2_PODF,
|
|
imx_clk_divider("usdhc2_podf", "usdhc2_sel",
|
|
base + 0x24, 16, 3));
|
|
clk_dm(IMX6QDL_CLK_USDHC3_PODF,
|
|
imx_clk_divider("usdhc3_podf", "usdhc3_sel",
|
|
base + 0x24, 19, 3));
|
|
clk_dm(IMX6QDL_CLK_USDHC4_PODF,
|
|
imx_clk_divider("usdhc4_podf", "usdhc4_sel",
|
|
base + 0x24, 22, 3));
|
|
|
|
clk_dm(IMX6QDL_CLK_ECSPI_ROOT,
|
|
imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6));
|
|
|
|
clk_dm(IMX6QDL_CLK_ECSPI1,
|
|
imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0));
|
|
clk_dm(IMX6QDL_CLK_ECSPI2,
|
|
imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2));
|
|
clk_dm(IMX6QDL_CLK_ECSPI3,
|
|
imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4));
|
|
clk_dm(IMX6QDL_CLK_ECSPI4,
|
|
imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6));
|
|
clk_dm(IMX6QDL_CLK_USDHC1,
|
|
imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2));
|
|
clk_dm(IMX6QDL_CLK_USDHC2,
|
|
imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4));
|
|
clk_dm(IMX6QDL_CLK_USDHC3,
|
|
imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6));
|
|
clk_dm(IMX6QDL_CLK_USDHC4,
|
|
imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct udevice_id imx6q_clk_ids[] = {
|
|
{ .compatible = "fsl,imx6q-ccm" },
|
|
{ },
|
|
};
|
|
|
|
U_BOOT_DRIVER(imx6q_clk) = {
|
|
.name = "clk_imx6q",
|
|
.id = UCLASS_CLK,
|
|
.of_match = imx6q_clk_ids,
|
|
.ops = &imx6q_clk_ops,
|
|
.probe = imx6q_clk_probe,
|
|
.flags = DM_FLAG_PRE_RELOC,
|
|
};
|