diff options
author | Tom Rini <trini@konsulko.com> | 2024-02-20 08:02:49 -0500 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2024-02-20 08:02:49 -0500 |
commit | bebf916f9eb13aaf5bbf83fbd33204df5c6c9f8e (patch) | |
tree | ce68a4801a1f9c82516aec08e641c3d29cb45c2e /drivers | |
parent | 3e6f2a94bfc25f1782ce2d45db27f47ec781feb1 (diff) | |
parent | 1e81d12e34e874319e041652198a4ba561d751ab (diff) |
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-samsung
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/exynos/Kconfig | 7 | ||||
-rw-r--r-- | drivers/clk/exynos/Makefile | 11 | ||||
-rw-r--r-- | drivers/clk/exynos/clk-exynos7420.c | 25 | ||||
-rw-r--r-- | drivers/clk/exynos/clk-exynos850.c | 189 | ||||
-rw-r--r-- | drivers/clk/exynos/clk-pll.c | 167 | ||||
-rw-r--r-- | drivers/clk/exynos/clk-pll.h | 16 | ||||
-rw-r--r-- | drivers/clk/exynos/clk.c | 121 | ||||
-rw-r--r-- | drivers/clk/exynos/clk.h | 228 | ||||
-rw-r--r-- | drivers/pinctrl/exynos/Kconfig | 8 | ||||
-rw-r--r-- | drivers/pinctrl/exynos/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/exynos/pinctrl-exynos850.c | 125 | ||||
-rw-r--r-- | drivers/serial/serial_s5p.c | 1 | ||||
-rw-r--r-- | drivers/soc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/soc/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/samsung/Kconfig | 33 | ||||
-rw-r--r-- | drivers/soc/samsung/Makefile | 4 | ||||
-rw-r--r-- | drivers/soc/samsung/exynos-pmu.c | 102 | ||||
-rw-r--r-- | drivers/soc/samsung/exynos-usi.c | 208 |
18 files changed, 1225 insertions, 23 deletions
diff --git a/drivers/clk/exynos/Kconfig b/drivers/clk/exynos/Kconfig index eb0efa97d1..85ce9d6e24 100644 --- a/drivers/clk/exynos/Kconfig +++ b/drivers/clk/exynos/Kconfig @@ -15,4 +15,11 @@ config CLK_EXYNOS7420 This enables common clock driver support for platforms based on Samsung Exynos7420 SoC. +config CLK_EXYNOS850 + bool "Clock driver for Samsung's Exynos850 SoC" + select CLK_CCF + help + This enables common clock driver support for platforms based + on Samsung Exynos850 SoC. + endmenu diff --git a/drivers/clk/exynos/Makefile b/drivers/clk/exynos/Makefile index c9f29c873e..734100e2bf 100644 --- a/drivers/clk/exynos/Makefile +++ b/drivers/clk/exynos/Makefile @@ -1,7 +1,12 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (C) 2016 Samsung Electronics -# Thomas Abraham <thomas.ab@samsung.com> +# Copyright (C) 2023 Linaro Ltd. +# +# Authors: +# Thomas Abraham <thomas.ab@samsung.com> +# Sam Protsenko <semen.protsenko@linaro.org> -obj-y += clk-pll.o -obj-$(CONFIG_CLK_EXYNOS7420) += clk-exynos7420.o +obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-pll.o +obj-$(CONFIG_CLK_EXYNOS7420) += clk-exynos7420.o +obj-$(CONFIG_CLK_EXYNOS850) += clk-exynos850.o diff --git a/drivers/clk/exynos/clk-exynos7420.c b/drivers/clk/exynos/clk-exynos7420.c index 7d869eb02b..9caa932e12 100644 --- a/drivers/clk/exynos/clk-exynos7420.c +++ b/drivers/clk/exynos/clk-exynos7420.c @@ -10,8 +10,15 @@ #include <errno.h> #include <clk-uclass.h> #include <asm/io.h> +#include <div64.h> #include <dt-bindings/clock/exynos7420-clk.h> -#include "clk-pll.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 #define DIVIDER(reg, shift, mask) \ (((readl(reg) >> shift) & mask) + 1) @@ -64,6 +71,22 @@ struct exynos7420_clk_top0_priv { unsigned long sclk_uart2; }; +static unsigned long pll145x_get_rate(unsigned int *con1, + unsigned long fin_freq) +{ + unsigned long pll_con1 = readl(con1); + unsigned long mdiv, sdiv, pdiv; + u64 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; +} + static ulong exynos7420_topc_get_rate(struct clk *clk) { struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev); diff --git a/drivers/clk/exynos/clk-exynos850.c b/drivers/clk/exynos/clk-exynos850.c new file mode 100644 index 0000000000..cf94a3e1b6 --- /dev/null +++ b/drivers/clk/exynos/clk-exynos850.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Samsung Exynos850 clock driver. + * Copyright (c) 2023 Linaro Ltd. + * Author: Sam Protsenko <semen.protsenko@linaro.org> + */ + +#include <dm.h> +#include <asm/io.h> +#include <dt-bindings/clock/exynos850.h> +#include "clk.h" + +/* ---- CMU_TOP ------------------------------------------------------------- */ + +/* Register Offset definitions for CMU_TOP (0x120e0000) */ +#define PLL_CON0_PLL_MMC 0x0100 +#define PLL_CON3_PLL_MMC 0x010c +#define PLL_CON0_PLL_SHARED0 0x0140 +#define PLL_CON3_PLL_SHARED0 0x014c +#define PLL_CON0_PLL_SHARED1 0x0180 +#define PLL_CON3_PLL_SHARED1 0x018c +#define CLK_CON_MUX_MUX_CLKCMU_PERI_BUS 0x1070 +#define CLK_CON_MUX_MUX_CLKCMU_PERI_IP 0x1074 +#define CLK_CON_MUX_MUX_CLKCMU_PERI_UART 0x1078 +#define CLK_CON_DIV_CLKCMU_PERI_BUS 0x187c +#define CLK_CON_DIV_CLKCMU_PERI_IP 0x1880 +#define CLK_CON_DIV_CLKCMU_PERI_UART 0x1884 +#define CLK_CON_DIV_PLL_SHARED0_DIV2 0x188c +#define CLK_CON_DIV_PLL_SHARED0_DIV3 0x1890 +#define CLK_CON_DIV_PLL_SHARED0_DIV4 0x1894 +#define CLK_CON_DIV_PLL_SHARED1_DIV2 0x1898 +#define CLK_CON_DIV_PLL_SHARED1_DIV3 0x189c +#define CLK_CON_DIV_PLL_SHARED1_DIV4 0x18a0 +#define CLK_CON_GAT_GATE_CLKCMU_PERI_BUS 0x2080 +#define CLK_CON_GAT_GATE_CLKCMU_PERI_IP 0x2084 +#define CLK_CON_GAT_GATE_CLKCMU_PERI_UART 0x2088 + +static const struct samsung_pll_clock top_pure_pll_clks[] = { + PLL(pll_0822x, CLK_FOUT_SHARED0_PLL, "fout_shared0_pll", "clock-oscclk", + PLL_CON3_PLL_SHARED0), + PLL(pll_0822x, CLK_FOUT_SHARED1_PLL, "fout_shared1_pll", "clock-oscclk", + PLL_CON3_PLL_SHARED1), + PLL(pll_0831x, CLK_FOUT_MMC_PLL, "fout_mmc_pll", "clock-oscclk", + PLL_CON3_PLL_MMC), +}; + +/* List of parent clocks for Muxes in CMU_TOP */ +PNAME(mout_shared0_pll_p) = { "clock-oscclk", "fout_shared0_pll" }; +PNAME(mout_shared1_pll_p) = { "clock-oscclk", "fout_shared1_pll" }; +PNAME(mout_mmc_pll_p) = { "clock-oscclk", "fout_mmc_pll" }; +/* List of parent clocks for Muxes in CMU_TOP: for CMU_PERI */ +PNAME(mout_peri_bus_p) = { "dout_shared0_div4", "dout_shared1_div4" }; +PNAME(mout_peri_uart_p) = { "clock-oscclk", "dout_shared0_div4", + "dout_shared1_div4", "clock-oscclk" }; +PNAME(mout_peri_ip_p) = { "clock-oscclk", "dout_shared0_div4", + "dout_shared1_div4", "clock-oscclk" }; + +static const struct samsung_mux_clock top_pure_mux_clks[] = { + MUX(CLK_MOUT_SHARED0_PLL, "mout_shared0_pll", mout_shared0_pll_p, + PLL_CON0_PLL_SHARED0, 4, 1), + MUX(CLK_MOUT_SHARED1_PLL, "mout_shared1_pll", mout_shared1_pll_p, + PLL_CON0_PLL_SHARED1, 4, 1), + MUX(CLK_MOUT_MMC_PLL, "mout_mmc_pll", mout_mmc_pll_p, + PLL_CON0_PLL_MMC, 4, 1), +}; + +static const struct samsung_mux_clock top_peri_mux_clks[] = { + MUX(CLK_MOUT_PERI_BUS, "mout_peri_bus", mout_peri_bus_p, + CLK_CON_MUX_MUX_CLKCMU_PERI_BUS, 0, 1), + MUX(CLK_MOUT_PERI_UART, "mout_peri_uart", mout_peri_uart_p, + CLK_CON_MUX_MUX_CLKCMU_PERI_UART, 0, 2), + MUX(CLK_MOUT_PERI_IP, "mout_peri_ip", mout_peri_ip_p, + CLK_CON_MUX_MUX_CLKCMU_PERI_IP, 0, 2), +}; + +static const struct samsung_div_clock top_pure_div_clks[] = { + DIV(CLK_DOUT_SHARED0_DIV3, "dout_shared0_div3", "mout_shared0_pll", + CLK_CON_DIV_PLL_SHARED0_DIV3, 0, 2), + DIV(CLK_DOUT_SHARED0_DIV2, "dout_shared0_div2", "mout_shared0_pll", + CLK_CON_DIV_PLL_SHARED0_DIV2, 0, 1), + DIV(CLK_DOUT_SHARED1_DIV3, "dout_shared1_div3", "mout_shared1_pll", + CLK_CON_DIV_PLL_SHARED1_DIV3, 0, 2), + DIV(CLK_DOUT_SHARED1_DIV2, "dout_shared1_div2", "mout_shared1_pll", + CLK_CON_DIV_PLL_SHARED1_DIV2, 0, 1), + DIV(CLK_DOUT_SHARED0_DIV4, "dout_shared0_div4", "dout_shared0_div2", + CLK_CON_DIV_PLL_SHARED0_DIV4, 0, 1), + DIV(CLK_DOUT_SHARED1_DIV4, "dout_shared1_div4", "dout_shared1_div2", + CLK_CON_DIV_PLL_SHARED1_DIV4, 0, 1), +}; + +static const struct samsung_div_clock top_peri_div_clks[] = { + DIV(CLK_DOUT_PERI_BUS, "dout_peri_bus", "gout_peri_bus", + CLK_CON_DIV_CLKCMU_PERI_BUS, 0, 4), + DIV(CLK_DOUT_PERI_UART, "dout_peri_uart", "gout_peri_uart", + CLK_CON_DIV_CLKCMU_PERI_UART, 0, 4), + DIV(CLK_DOUT_PERI_IP, "dout_peri_ip", "gout_peri_ip", + CLK_CON_DIV_CLKCMU_PERI_IP, 0, 4), +}; + +static const struct samsung_gate_clock top_peri_gate_clks[] = { + GATE(CLK_GOUT_PERI_BUS, "gout_peri_bus", "mout_peri_bus", + CLK_CON_GAT_GATE_CLKCMU_PERI_BUS, 21, 0, 0), + GATE(CLK_GOUT_PERI_UART, "gout_peri_uart", "mout_peri_uart", + CLK_CON_GAT_GATE_CLKCMU_PERI_UART, 21, 0, 0), + GATE(CLK_GOUT_PERI_IP, "gout_peri_ip", "mout_peri_ip", + CLK_CON_GAT_GATE_CLKCMU_PERI_IP, 21, 0, 0), +}; + +static const struct samsung_clk_group top_cmu_clks[] = { + /* CMU_TOP_PURECLKCOMP */ + { S_CLK_PLL, top_pure_pll_clks, ARRAY_SIZE(top_pure_pll_clks) }, + { S_CLK_MUX, top_pure_mux_clks, ARRAY_SIZE(top_pure_mux_clks) }, + { S_CLK_DIV, top_pure_div_clks, ARRAY_SIZE(top_pure_div_clks) }, + + /* CMU_TOP clocks for CMU_PERI */ + { S_CLK_MUX, top_peri_mux_clks, ARRAY_SIZE(top_peri_mux_clks) }, + { S_CLK_GATE, top_peri_gate_clks, ARRAY_SIZE(top_peri_gate_clks) }, + { S_CLK_DIV, top_peri_div_clks, ARRAY_SIZE(top_peri_div_clks) }, +}; + +static int exynos850_cmu_top_probe(struct udevice *dev) +{ + return samsung_cmu_register_one(dev, top_cmu_clks, + ARRAY_SIZE(top_cmu_clks)); +} + +static const struct udevice_id exynos850_cmu_top_ids[] = { + { .compatible = "samsung,exynos850-cmu-top" }, + { } +}; + +U_BOOT_DRIVER(exynos850_cmu_top) = { + .name = "exynos850-cmu-top", + .id = UCLASS_CLK, + .of_match = exynos850_cmu_top_ids, + .ops = &ccf_clk_ops, + .probe = exynos850_cmu_top_probe, + .flags = DM_FLAG_PRE_RELOC, +}; + +/* ---- CMU_PERI ------------------------------------------------------------ */ + +/* Register Offset definitions for CMU_PERI (0x10030000) */ +#define PLL_CON0_MUX_CLKCMU_PERI_BUS_USER 0x0600 +#define PLL_CON0_MUX_CLKCMU_PERI_UART_USER 0x0630 +#define CLK_CON_GAT_GOUT_PERI_UART_IPCLK 0x20a8 +#define CLK_CON_GAT_GOUT_PERI_UART_PCLK 0x20ac + +/* List of parent clocks for Muxes in CMU_PERI */ +PNAME(mout_peri_bus_user_p) = { "clock-oscclk", "dout_peri_bus" }; +PNAME(mout_peri_uart_user_p) = { "clock-oscclk", "dout_peri_uart" }; + +static const struct samsung_mux_clock peri_mux_clks[] = { + MUX(CLK_MOUT_PERI_BUS_USER, "mout_peri_bus_user", mout_peri_bus_user_p, + PLL_CON0_MUX_CLKCMU_PERI_BUS_USER, 4, 1), + MUX(CLK_MOUT_PERI_UART_USER, "mout_peri_uart_user", + mout_peri_uart_user_p, PLL_CON0_MUX_CLKCMU_PERI_UART_USER, 4, 1), +}; + +static const struct samsung_gate_clock peri_gate_clks[] = { + GATE(CLK_GOUT_UART_IPCLK, "gout_uart_ipclk", "mout_peri_uart_user", + CLK_CON_GAT_GOUT_PERI_UART_IPCLK, 21, 0, 0), + GATE(CLK_GOUT_UART_PCLK, "gout_uart_pclk", "mout_peri_bus_user", + CLK_CON_GAT_GOUT_PERI_UART_PCLK, 21, 0, 0), +}; + +static const struct samsung_clk_group peri_cmu_clks[] = { + { S_CLK_MUX, peri_mux_clks, ARRAY_SIZE(peri_mux_clks) }, + { S_CLK_GATE, peri_gate_clks, ARRAY_SIZE(peri_gate_clks) }, +}; + +static int exynos850_cmu_peri_probe(struct udevice *dev) +{ + return samsung_register_cmu(dev, peri_cmu_clks, exynos850_cmu_top); +} + +static const struct udevice_id exynos850_cmu_peri_ids[] = { + { .compatible = "samsung,exynos850-cmu-peri" }, + { } +}; + +U_BOOT_DRIVER(exynos850_cmu_peri) = { + .name = "exynos850-cmu-peri", + .id = UCLASS_CLK, + .of_match = exynos850_cmu_peri_ids, + .ops = &ccf_clk_ops, + .probe = exynos850_cmu_peri_probe, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/exynos/clk-pll.c b/drivers/clk/exynos/clk-pll.c index 407fc71d41..4aacbc26b2 100644 --- a/drivers/clk/exynos/clk-pll.c +++ b/drivers/clk/exynos/clk-pll.c @@ -1,32 +1,167 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Exynos PLL helper functions for clock drivers. * Copyright (C) 2016 Samsung Electronics - * Thomas Abraham <thomas.ab@samsung.com> + * Copyright (C) 2023 Linaro Ltd. + * + * Authors: + * Thomas Abraham <thomas.ab@samsung.com> + * Sam Protsenko <semen.protsenko@linaro.org> + * + * This file contains the utility functions to register the pll clocks. */ -#include <common.h> #include <asm/io.h> #include <div64.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <clk.h> +#include "clk.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 +#define UBOOT_DM_CLK_SAMSUNG_PLL0822X "samsung_clk_pll0822x" +#define UBOOT_DM_CLK_SAMSUNG_PLL0831X "samsung_clk_pll0831x" -unsigned long pll145x_get_rate(unsigned int *con1, unsigned long fin_freq) +struct samsung_clk_pll { + struct clk clk; + void __iomem *con_reg; + enum samsung_pll_type type; +}; + +#define to_clk_pll(_clk) container_of(_clk, struct samsung_clk_pll, clk) + +/* + * PLL0822x Clock Type + */ + +#define PLL0822X_MDIV_MASK 0x3ff +#define PLL0822X_PDIV_MASK 0x3f +#define PLL0822X_SDIV_MASK 0x7 +#define PLL0822X_MDIV_SHIFT 16 +#define PLL0822X_PDIV_SHIFT 8 +#define PLL0822X_SDIV_SHIFT 0 + +static unsigned long samsung_pll0822x_recalc_rate(struct clk *clk) { - unsigned long pll_con1 = readl(con1); - unsigned long mdiv, sdiv, pdiv; - uint64_t fvco = fin_freq; + struct samsung_clk_pll *pll = to_clk_pll(clk); + u32 mdiv, pdiv, sdiv, pll_con3; + u64 fvco = clk_get_parent_rate(clk); - 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; + pll_con3 = readl_relaxed(pll->con_reg); + mdiv = (pll_con3 >> PLL0822X_MDIV_SHIFT) & PLL0822X_MDIV_MASK; + pdiv = (pll_con3 >> PLL0822X_PDIV_SHIFT) & PLL0822X_PDIV_MASK; + sdiv = (pll_con3 >> PLL0822X_SDIV_SHIFT) & PLL0822X_SDIV_MASK; fvco *= mdiv; do_div(fvco, (pdiv << sdiv)); return (unsigned long)fvco; } + +static const struct clk_ops samsung_pll0822x_clk_min_ops = { + .get_rate = samsung_pll0822x_recalc_rate, +}; + +/* + * PLL0831x Clock Type + */ + +#define PLL0831X_KDIV_MASK 0xffff +#define PLL0831X_MDIV_MASK 0x1ff +#define PLL0831X_PDIV_MASK 0x3f +#define PLL0831X_SDIV_MASK 0x7 +#define PLL0831X_MDIV_SHIFT 16 +#define PLL0831X_PDIV_SHIFT 8 +#define PLL0831X_SDIV_SHIFT 0 +#define PLL0831X_KDIV_SHIFT 0 + +static unsigned long samsung_pll0831x_recalc_rate(struct clk *clk) +{ + struct samsung_clk_pll *pll = to_clk_pll(clk); + u32 mdiv, pdiv, sdiv, pll_con3, pll_con5; + s16 kdiv; + u64 fvco = clk_get_parent_rate(clk); + + pll_con3 = readl_relaxed(pll->con_reg); + pll_con5 = readl_relaxed(pll->con_reg + 8); + mdiv = (pll_con3 >> PLL0831X_MDIV_SHIFT) & PLL0831X_MDIV_MASK; + pdiv = (pll_con3 >> PLL0831X_PDIV_SHIFT) & PLL0831X_PDIV_MASK; + sdiv = (pll_con3 >> PLL0831X_SDIV_SHIFT) & PLL0831X_SDIV_MASK; + kdiv = (s16)((pll_con5 >> PLL0831X_KDIV_SHIFT) & PLL0831X_KDIV_MASK); + + fvco *= (mdiv << 16) + kdiv; + do_div(fvco, (pdiv << sdiv)); + fvco >>= 16; + + return (unsigned long)fvco; +} + +static const struct clk_ops samsung_pll0831x_clk_min_ops = { + .get_rate = samsung_pll0831x_recalc_rate, +}; + +static struct clk *_samsung_clk_register_pll(void __iomem *base, + const struct samsung_pll_clock *pll_clk) +{ + struct samsung_clk_pll *pll; + struct clk *clk; + const char *drv_name; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->con_reg = base + pll_clk->con_offset; + pll->type = pll_clk->type; + clk = &pll->clk; + clk->flags = pll_clk->flags; + + switch (pll_clk->type) { + case pll_0822x: + drv_name = UBOOT_DM_CLK_SAMSUNG_PLL0822X; + break; + case pll_0831x: + drv_name = UBOOT_DM_CLK_SAMSUNG_PLL0831X; + break; + default: + kfree(pll); + return ERR_PTR(-ENODEV); + } + + ret = clk_register(clk, drv_name, pll_clk->name, pll_clk->parent_name); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return clk; +} + +void samsung_clk_register_pll(void __iomem *base, + const struct samsung_pll_clock *clk_list, + unsigned int nr_clk) +{ + unsigned int cnt; + + for (cnt = 0; cnt < nr_clk; cnt++) { + struct clk *clk; + const struct samsung_pll_clock *pll_clk; + + pll_clk = &clk_list[cnt]; + clk = _samsung_clk_register_pll(base, pll_clk); + clk_dm(pll_clk->id, clk); + } +} + +U_BOOT_DRIVER(samsung_pll0822x_clk) = { + .name = UBOOT_DM_CLK_SAMSUNG_PLL0822X, + .id = UCLASS_CLK, + .ops = &samsung_pll0822x_clk_min_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +U_BOOT_DRIVER(samsung_pll0831x_clk) = { + .name = UBOOT_DM_CLK_SAMSUNG_PLL0831X, + .id = UCLASS_CLK, + .ops = &samsung_pll0831x_clk_min_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/exynos/clk-pll.h b/drivers/clk/exynos/clk-pll.h index 7b7af5e676..bd79309fa1 100644 --- a/drivers/clk/exynos/clk-pll.h +++ b/drivers/clk/exynos/clk-pll.h @@ -1,13 +1,23 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Exynos PLL helper functions for clock drivers. * Copyright (C) 2016 Samsung Electronics - * Thomas Abraham <thomas.ab@samsung.com> + * Copyright (C) 2023 Linaro Ltd. + * + * Authors: + * Thomas Abraham <thomas.ab@samsung.com> + * Sam Protsenko <semen.protsenko@linaro.org> + * + * Common Clock Framework support for all PLL's in Samsung platforms. */ #ifndef __EXYNOS_CLK_PLL_H #define __EXYNOS_CLK_PLL_H -unsigned long pll145x_get_rate(unsigned int *con1, unsigned long fin_freq); +#include <linux/clk-provider.h> + +enum samsung_pll_type { + pll_0822x, + pll_0831x, +}; #endif /* __EXYNOS_CLK_PLL_H */ diff --git a/drivers/clk/exynos/clk.c b/drivers/clk/exynos/clk.c new file mode 100644 index 0000000000..430767f072 --- /dev/null +++ b/drivers/clk/exynos/clk.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Linaro Ltd. + * Sam Protsenko <semen.protsenko@linaro.org> + * + * This file includes utility functions to register clocks to common + * clock framework for Samsung platforms. + */ + +#include <dm.h> +#include "clk.h" + +void samsung_clk_register_mux(void __iomem *base, + const struct samsung_mux_clock *clk_list, + unsigned int nr_clk) +{ + unsigned int cnt; + + for (cnt = 0; cnt < nr_clk; cnt++) { + struct clk *clk; + const struct samsung_mux_clock *m; + + m = &clk_list[cnt]; + clk = clk_register_mux(NULL, m->name, m->parent_names, + m->num_parents, m->flags, base + m->offset, m->shift, + m->width, m->mux_flags); + clk_dm(m->id, clk); + } +} + +void samsung_clk_register_div(void __iomem *base, + const struct samsung_div_clock *clk_list, + unsigned int nr_clk) +{ + unsigned int cnt; + + for (cnt = 0; cnt < nr_clk; cnt++) { + struct clk *clk; + const struct samsung_div_clock *d; + + d = &clk_list[cnt]; + clk = clk_register_divider(NULL, d->name, d->parent_name, + d->flags, base + d->offset, d->shift, + d->width, d->div_flags); + clk_dm(d->id, clk); + } +} + +void samsung_clk_register_gate(void __iomem *base, + const struct samsung_gate_clock *clk_list, + unsigned int nr_clk) +{ + unsigned int cnt; + + for (cnt = 0; cnt < nr_clk; cnt++) { + struct clk *clk; + const struct samsung_gate_clock *g; + + g = &clk_list[cnt]; + clk = clk_register_gate(NULL, g->name, g->parent_name, + g->flags, base + g->offset, g->bit_idx, + g->gate_flags, NULL); + clk_dm(g->id, clk); + } +} + +typedef void (*samsung_clk_register_fn)(void __iomem *base, + const void *clk_list, + unsigned int nr_clk); + +static const samsung_clk_register_fn samsung_clk_register_fns[] = { + [S_CLK_MUX] = (samsung_clk_register_fn)samsung_clk_register_mux, + [S_CLK_DIV] = (samsung_clk_register_fn)samsung_clk_register_div, + [S_CLK_GATE] = (samsung_clk_register_fn)samsung_clk_register_gate, + [S_CLK_PLL] = (samsung_clk_register_fn)samsung_clk_register_pll, +}; + +/** + * samsung_cmu_register_clocks() - Register provided clock groups + * @base: Base address of CMU registers + * @clk_groups: list of clock groups + * @nr_groups: count of clock groups in @clk_groups + * + * Having the array of clock groups @clk_groups makes it possible to keep a + * correct clocks registration order. + */ +void samsung_cmu_register_clocks(void __iomem *base, + const struct samsung_clk_group *clk_groups, + unsigned int nr_groups) +{ + unsigned int i; + + for (i = 0; i < nr_groups; i++) { + const struct samsung_clk_group *g = &clk_groups[i]; + + samsung_clk_register_fns[g->type](base, g->clk_list, g->nr_clk); + } +} + +/** + * samsung_cmu_register_one - Register all CMU clocks + * @dev: CMU device + * @clk_groups: list of CMU clock groups + * @nr_groups: count of CMU clock groups in @clk_groups + * + * Return: 0 on success or negative value on error. + */ +int samsung_cmu_register_one(struct udevice *dev, + const struct samsung_clk_group *clk_groups, + unsigned int nr_groups) +{ + void __iomem *base; + + base = dev_read_addr_ptr(dev); + if (!base) + return -EINVAL; + + samsung_cmu_register_clocks(base, clk_groups, nr_groups); + + return 0; +} diff --git a/drivers/clk/exynos/clk.h b/drivers/clk/exynos/clk.h new file mode 100644 index 0000000000..91a51b877a --- /dev/null +++ b/drivers/clk/exynos/clk.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2023 Linaro Ltd. + * Sam Protsenko <semen.protsenko@linaro.org> + * + * Common Clock Framework support for all Samsung platforms. + */ + +#ifndef __EXYNOS_CLK_H +#define __EXYNOS_CLK_H + +#include <errno.h> +#include <linux/clk-provider.h> +#include "clk-pll.h" + +/** + * struct samsung_mux_clock - information about mux clock + * @id: platform specific id of the clock + * @name: name of this mux clock + * @parent_names: array of pointer to parent clock names + * @num_parents: number of parents listed in @parent_names + * @flags: optional flags for basic clock + * @offset: offset of the register for configuring the mux + * @shift: starting bit location of the mux control bit-field in @reg + * @width: width of the mux control bit-field in @reg + * @mux_flags: flags for mux-type clock + */ +struct samsung_mux_clock { + unsigned int id; + const char *name; + const char * const *parent_names; + u8 num_parents; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 mux_flags; +}; + +#define PNAME(x) static const char * const x[] + +#define __MUX(_id, cname, pnames, o, s, w, f, mf) \ + { \ + .id = _id, \ + .name = cname, \ + .parent_names = pnames, \ + .num_parents = ARRAY_SIZE(pnames), \ + .flags = (f) | CLK_SET_RATE_NO_REPARENT, \ + .offset = o, \ + .shift = s, \ + .width = w, \ + .mux_flags = mf, \ + } + +#define MUX(_id, cname, pnames, o, s, w) \ + __MUX(_id, cname, pnames, o, s, w, 0, 0) + +#define MUX_F(_id, cname, pnames, o, s, w, f, mf) \ + __MUX(_id, cname, pnames, o, s, w, f, mf) + +/** + * struct samsung_div_clock - information about div clock + * @id: platform specific id of the clock + * @name: name of this div clock + * @parent_name: name of the parent clock + * @flags: optional flags for basic clock + * @offset: offset of the register for configuring the div + * @shift: starting bit location of the div control bit-field in @reg + * @width: width of the bitfield + * @div_flags: flags for div-type clock + */ +struct samsung_div_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 div_flags; +}; + +#define __DIV(_id, cname, pname, o, s, w, f, df) \ + { \ + .id = _id, \ + .name = cname, \ + .parent_name = pname, \ + .flags = f, \ + .offset = o, \ + .shift = s, \ + .width = w, \ + .div_flags = df, \ + } + +#define DIV(_id, cname, pname, o, s, w) \ + __DIV(_id, cname, pname, o, s, w, 0, 0) + +#define DIV_F(_id, cname, pname, o, s, w, f, df) \ + __DIV(_id, cname, pname, o, s, w, f, df) + +/** + * struct samsung_gate_clock - information about gate clock + * @id: platform specific id of the clock + * @name: name of this gate clock + * @parent_name: name of the parent clock + * @flags: optional flags for basic clock + * @offset: offset of the register for configuring the gate + * @bit_idx: bit index of the gate control bit-field in @reg + * @gate_flags: flags for gate-type clock + */ +struct samsung_gate_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 bit_idx; + u8 gate_flags; +}; + +#define __GATE(_id, cname, pname, o, b, f, gf) \ + { \ + .id = _id, \ + .name = cname, \ + .parent_name = pname, \ + .flags = f, \ + .offset = o, \ + .bit_idx = b, \ + .gate_flags = gf, \ + } + +#define GATE(_id, cname, pname, o, b, f, gf) \ + __GATE(_id, cname, pname, o, b, f, gf) + +/** + * struct samsung_pll_clock - information about pll clock + * @id: platform specific id of the clock + * @name: name of this pll clock + * @parent_name: name of the parent clock + * @flags: optional flags for basic clock + * @con_offset: offset of the register for configuring the PLL + * @type: type of PLL to be registered + */ +struct samsung_pll_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + int con_offset; + enum samsung_pll_type type; +}; + +#define PLL(_typ, _id, _name, _pname, _con) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_name = _pname, \ + .flags = CLK_GET_RATE_NOCACHE, \ + .con_offset = _con, \ + .type = _typ, \ + } + +enum samsung_clock_type { + S_CLK_MUX, + S_CLK_DIV, + S_CLK_GATE, + S_CLK_PLL, +}; + +/** + * struct samsung_clock_group - contains a list of clocks of one type + * @type: type of clocks this structure contains + * @clk_list: list of clocks + * @nr_clk: count of clocks in @clk_list + */ +struct samsung_clk_group { + enum samsung_clock_type type; + const void *clk_list; + unsigned int nr_clk; +}; + +void samsung_clk_register_mux(void __iomem *base, + const struct samsung_mux_clock *clk_list, + unsigned int nr_clk); +void samsung_clk_register_div(void __iomem *base, + const struct samsung_div_clock *clk_list, + unsigned int nr_clk); +void samsung_clk_register_gate(void __iomem *base, + const struct samsung_gate_clock *clk_list, + unsigned int nr_clk); +void samsung_clk_register_pll(void __iomem *base, + const struct samsung_pll_clock *clk_list, + unsigned int nr_clk); + +void samsung_cmu_register_clocks(void __iomem *base, + const struct samsung_clk_group *clk_groups, + unsigned int nr_groups); +int samsung_cmu_register_one(struct udevice *dev, + const struct samsung_clk_group *clk_groups, + unsigned int nr_groups); + +/** + * samsung_register_cmu - Register CMU clocks ensuring parent CMU is present + * @dev: CMU device + * @clk_groups: list of CMU clock groups + * @parent_drv: name of parent CMU driver + * + * Register provided CMU clocks, but make sure CMU_TOP driver is instantiated + * first. + * + * Return: 0 on success or negative value on error. + */ +#define samsung_register_cmu(dev, clk_groups, parent_drv) \ +({ \ + struct udevice *__parent; \ + int __ret; \ + \ + __ret = uclass_get_device_by_driver(UCLASS_CLK, \ + DM_DRIVER_GET(parent_drv), &__parent); \ + if (__ret || !__parent) \ + __ret = -ENOENT; \ + else \ + __ret = samsung_cmu_register_one(dev, clk_groups, \ + ARRAY_SIZE(clk_groups)); \ + __ret; \ +}) + +#endif /* __EXYNOS_CLK_H */ diff --git a/drivers/pinctrl/exynos/Kconfig b/drivers/pinctrl/exynos/Kconfig index a60f49869b..1b7fb62bc4 100644 --- a/drivers/pinctrl/exynos/Kconfig +++ b/drivers/pinctrl/exynos/Kconfig @@ -16,3 +16,11 @@ config PINCTRL_EXYNOS78x0 help Support pin multiplexing and pin configuration control on Samsung's Exynos78x0 SoC. + +config PINCTRL_EXYNOS850 + bool "Samsung Exynos850 pinctrl driver" + depends on ARCH_EXYNOS && PINCTRL_FULL + select PINCTRL_EXYNOS + help + Support pin multiplexing and pin configuration control on + Samsung's Exynos850 SoC. diff --git a/drivers/pinctrl/exynos/Makefile b/drivers/pinctrl/exynos/Makefile index 07db970ca9..3abe1226eb 100644 --- a/drivers/pinctrl/exynos/Makefile +++ b/drivers/pinctrl/exynos/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_PINCTRL_EXYNOS) += pinctrl-exynos.o obj-$(CONFIG_PINCTRL_EXYNOS7420) += pinctrl-exynos7420.o obj-$(CONFIG_PINCTRL_EXYNOS78x0) += pinctrl-exynos78x0.o +obj-$(CONFIG_PINCTRL_EXYNOS850) += pinctrl-exynos850.o diff --git a/drivers/pinctrl/exynos/pinctrl-exynos850.c b/drivers/pinctrl/exynos/pinctrl-exynos850.c new file mode 100644 index 0000000000..3ec2636e0d --- /dev/null +++ b/drivers/pinctrl/exynos/pinctrl-exynos850.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Linaro Ltd. + * Author: Sam Protsenko <semen.protsenko@linaro.org> + * + * Exynos850 pinctrl driver. + */ + +#include <dm.h> +#include <dm/pinctrl.h> +#include "pinctrl-exynos.h" + +#define EXYNOS850_PIN_BANK(pins, reg, id) \ + { \ + .type = &exynos850_bank_type, \ + .offset = reg, \ + .nr_pins = pins, \ + .name = id \ + } + +/* CON, DAT, PUD, DRV */ +static const struct samsung_pin_bank_type exynos850_bank_type = { + .fld_width = { 4, 1, 4, 4, }, + .reg_offset = { 0x00, 0x04, 0x08, 0x0c, }, +}; + +static const struct pinctrl_ops exynos850_pinctrl_ops = { + .set_state = exynos_pinctrl_set_state +}; + +/* pin banks of exynos850 pin-controller 0 (ALIVE) */ +static const struct samsung_pin_bank_data exynos850_pin_banks0[] = { + EXYNOS850_PIN_BANK(8, 0x000, "gpa0"), + EXYNOS850_PIN_BANK(8, 0x020, "gpa1"), + EXYNOS850_PIN_BANK(8, 0x040, "gpa2"), + EXYNOS850_PIN_BANK(8, 0x060, "gpa3"), + EXYNOS850_PIN_BANK(4, 0x080, "gpa4"), + EXYNOS850_PIN_BANK(3, 0x0a0, "gpq0"), +}; + +/* pin banks of exynos850 pin-controller 1 (CMGP) */ +static const struct samsung_pin_bank_data exynos850_pin_banks1[] = { + EXYNOS850_PIN_BANK(1, 0x000, "gpm0"), + EXYNOS850_PIN_BANK(1, 0x020, "gpm1"), + EXYNOS850_PIN_BANK(1, 0x040, "gpm2"), + EXYNOS850_PIN_BANK(1, 0x060, "gpm3"), + EXYNOS850_PIN_BANK(1, 0x080, "gpm4"), + EXYNOS850_PIN_BANK(1, 0x0a0, "gpm5"), + EXYNOS850_PIN_BANK(1, 0x0c0, "gpm6"), + EXYNOS850_PIN_BANK(1, 0x0e0, "gpm7"), +}; + +/* pin banks of exynos850 pin-controller 2 (AUD) */ +static const struct samsung_pin_bank_data exynos850_pin_banks2[] = { + EXYNOS850_PIN_BANK(5, 0x000, "gpb0"), + EXYNOS850_PIN_BANK(5, 0x020, "gpb1"), +}; + +/* pin banks of exynos850 pin-controller 3 (HSI) */ +static const struct samsung_pin_bank_data exynos850_pin_banks3[] = { + EXYNOS850_PIN_BANK(6, 0x000, "gpf2"), +}; + +/* pin banks of exynos850 pin-controller 4 (CORE) */ +static const struct samsung_pin_bank_data exynos850_pin_banks4[] = { + EXYNOS850_PIN_BANK(4, 0x000, "gpf0"), + EXYNOS850_PIN_BANK(8, 0x020, "gpf1"), +}; + +/* pin banks of exynos850 pin-controller 5 (PERI) */ +static const struct samsung_pin_bank_data exynos850_pin_banks5[] = { + EXYNOS850_PIN_BANK(2, 0x000, "gpg0"), + EXYNOS850_PIN_BANK(6, 0x020, "gpp0"), + EXYNOS850_PIN_BANK(4, 0x040, "gpp1"), + EXYNOS850_PIN_BANK(4, 0x060, "gpp2"), + EXYNOS850_PIN_BANK(8, 0x080, "gpg1"), + EXYNOS850_PIN_BANK(8, 0x0a0, "gpg2"), + EXYNOS850_PIN_BANK(1, 0x0c0, "gpg3"), + EXYNOS850_PIN_BANK(3, 0x0e0, "gpc0"), + EXYNOS850_PIN_BANK(6, 0x100, "gpc1"), +}; + +static const struct samsung_pin_ctrl exynos850_pin_ctrl[] = { + { + /* pin-controller instance 0 ALIVE data */ + .pin_banks = exynos850_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos850_pin_banks0), + }, { + /* pin-controller instance 1 CMGP data */ + .pin_banks = exynos850_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos850_pin_banks1), + }, { + /* pin-controller instance 2 AUD data */ + .pin_banks = exynos850_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos850_pin_banks2), + }, { + /* pin-controller instance 3 HSI data */ + .pin_banks = exynos850_pin_banks3, + .nr_banks = ARRAY_SIZE(exynos850_pin_banks3), + }, { + /* pin-controller instance 4 CORE data */ + .pin_banks = exynos850_pin_banks4, + .nr_banks = ARRAY_SIZE(exynos850_pin_banks4), + }, { + /* pin-controller instance 5 PERI data */ + .pin_banks = exynos850_pin_banks5, + .nr_banks = ARRAY_SIZE(exynos850_pin_banks5), + }, + {/* list terminator */} +}; + +static const struct udevice_id exynos850_pinctrl_ids[] = { + { .compatible = "samsung,exynos850-pinctrl", + .data = (ulong)exynos850_pin_ctrl }, + { } +}; + +U_BOOT_DRIVER(pinctrl_exynos850) = { + .name = "pinctrl_exynos850", + .id = UCLASS_PINCTRL, + .of_match = exynos850_pinctrl_ids, + .priv_auto = sizeof(struct exynos_pinctrl_priv), + .ops = &exynos850_pinctrl_ops, + .probe = exynos_pinctrl_probe, +}; diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c index 7d04dcff54..801b7645af 100644 --- a/drivers/serial/serial_s5p.c +++ b/drivers/serial/serial_s5p.c @@ -257,6 +257,7 @@ static const struct dm_serial_ops s5p_serial_ops = { static const struct udevice_id s5p_serial_ids[] = { { .compatible = "samsung,exynos4210-uart", .data = PORT_S5P }, + { .compatible = "samsung,exynos850-uart", .data = PORT_S5P }, { .compatible = "apple,s5l-uart", .data = PORT_S5L }, { } }; diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 85dac9de78..03433bc0e6 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -40,6 +40,7 @@ config SOC_XILINX_VERSAL_NET This allows other drivers to verify the SoC familiy & revision using matching SoC attributes. +source "drivers/soc/samsung/Kconfig" source "drivers/soc/ti/Kconfig" endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 84385650d4..610bf816d4 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -2,6 +2,7 @@ # # Makefile for the U-Boot SOC specific device drivers. +obj-$(CONFIG_SOC_SAMSUNG) += samsung/ obj-$(CONFIG_SOC_TI) += ti/ obj-$(CONFIG_SOC_DEVICE) += soc-uclass.o obj-$(CONFIG_SOC_DEVICE_TI_K3) += soc_ti_k3.o diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig new file mode 100644 index 0000000000..737b7ca8cd --- /dev/null +++ b/drivers/soc/samsung/Kconfig @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0+ + +menuconfig SOC_SAMSUNG + bool "Samsung SoC drivers support" + +if SOC_SAMSUNG + +config EXYNOS_PMU + bool "Exynos PMU controller driver" + depends on ARCH_EXYNOS + select REGMAP + select SYSCON + help + Enable support for system controller configuration driver. It allows + one to configure system controller registers (e.g. some register in + PMU syscon) by providing register's offset, mask and value. + +config EXYNOS_USI + bool "Exynos USI (Universal Serial Interface) driver" + depends on ARCH_EXYNOS + select MISC + select REGMAP + select SYSCON + help + Enable support for USI block. USI (Universal Serial Interface) is an + IP-core found in modern Samsung Exynos SoCs, like Exynos850 and + ExynosAutoV9. USI block can be configured to provide one of the + following serial protocols: UART, SPI or High Speed I2C. + + This driver allows one to configure USI for desired protocol, which + is usually done in USI node in Device Tree. + +endif diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile new file mode 100644 index 0000000000..0eb3ed8353 --- /dev/null +++ b/drivers/soc/samsung/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o +obj-$(CONFIG_EXYNOS_USI) += exynos-usi.o diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c new file mode 100644 index 0000000000..233ad4a908 --- /dev/null +++ b/drivers/soc/samsung/exynos-pmu.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Linaro Ltd. + * Author: Sam Protsenko <semen.protsenko@linaro.org> + * + * Exynos PMU (Power Management Unit) driver. + */ + +#include <dm.h> +#include <errno.h> +#include <regmap.h> +#include <syscon.h> +#include <linux/bitops.h> +#include <linux/err.h> + +#define EXYNOS850_UART_IO_SHARE_CTRL 0x0760 +#define SEL_RXD_AP_UART_SHIFT 16 +#define SEL_RXD_AP_UART_MASK GENMASK(17, 16) +#define SEL_TXD_GPIO_1_SHIFT 20 +#define SEL_TXD_GPIO_1_MASK GENMASK(21, 20) +#define RXD_GPIO_1 0x3 +#define TXD_AP_UART 0x0 + +struct exynos_pmu { + struct udevice *dev; + const struct exynos_pmu_data *pmu_data; + struct regmap *regmap; +}; + +struct exynos_pmu_data { + int (*pmu_init)(struct exynos_pmu *priv); +}; + +static int exynos850_pmu_init(struct exynos_pmu *priv) +{ + ofnode node; + bool uart_debug_1; + unsigned int offset, mask, value; + + node = dev_ofnode(priv->dev); + uart_debug_1 = ofnode_read_bool(node, "samsung,uart-debug-1"); + if (!uart_debug_1) + return 0; + + /* + * If uart1_pins are used for serial, AP UART lines have to be muxed + * in PMU block to UART_DEBUG_1 path (GPIO_1). By default (reset value) + * UART_DEBUG_0 path (uart0_pins) is connected to AP UART lines. + */ + offset = EXYNOS850_UART_IO_SHARE_CTRL; + mask = SEL_RXD_AP_UART_MASK | SEL_TXD_GPIO_1_MASK; + value = RXD_GPIO_1 << SEL_RXD_AP_UART_SHIFT | + TXD_AP_UART << SEL_TXD_GPIO_1_SHIFT; + return regmap_update_bits(priv->regmap, offset, mask, value); +} + +static const struct exynos_pmu_data exynos850_pmu_data = { + .pmu_init = exynos850_pmu_init, +}; + +static int exynos_pmu_bind(struct udevice *dev) +{ + dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); + return 0; +} + +static int exynos_pmu_probe(struct udevice *dev) +{ + ofnode node; + struct exynos_pmu *priv; + + priv = dev_get_priv(dev); + priv->dev = dev; + + node = dev_ofnode(dev); + priv->regmap = syscon_node_to_regmap(node); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->pmu_data = (struct exynos_pmu_data *)dev_get_driver_data(dev); + if (priv->pmu_data && priv->pmu_data->pmu_init) + return priv->pmu_data->pmu_init(priv); + + return 0; +} + +static const struct udevice_id exynos_pmu_ids[] = { + { + .compatible = "samsung,exynos850-pmu", + .data = (ulong)&exynos850_pmu_data + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(exynos_pmu) = { + .name = "exynos-pmu", + .id = UCLASS_NOP, + .of_match = exynos_pmu_ids, + .bind = exynos_pmu_bind, + .probe = exynos_pmu_probe, + .priv_auto = sizeof(struct exynos_pmu), +}; diff --git a/drivers/soc/samsung/exynos-usi.c b/drivers/soc/samsung/exynos-usi.c new file mode 100644 index 0000000000..b746a7838e --- /dev/null +++ b/drivers/soc/samsung/exynos-usi.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Linaro Ltd. + * Author: Sam Protsenko <semen.protsenko@linaro.org> + * + * Samsung Exynos USI driver (Universal Serial Interface). + */ + +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include <dt-bindings/soc/samsung,exynos-usi.h> + +/* USIv2: System Register: SW_CONF register bits */ +#define USI_V2_SW_CONF_NONE 0x0 +#define USI_V2_SW_CONF_UART BIT(0) +#define USI_V2_SW_CONF_SPI BIT(1) +#define USI_V2_SW_CONF_I2C BIT(2) +#define USI_V2_SW_CONF_MASK (USI_V2_SW_CONF_UART | USI_V2_SW_CONF_SPI | \ + USI_V2_SW_CONF_I2C) + +/* USIv2: USI register offsets */ +#define USI_CON 0x04 +#define USI_OPTION 0x08 + +/* USIv2: USI register bits */ +#define USI_CON_RESET BIT(0) +#define USI_OPTION_CLKREQ_ON BIT(1) +#define USI_OPTION_CLKSTOP_ON BIT(2) + +enum exynos_usi_ver { + USI_VER2 = 2, +}; + +struct exynos_usi_variant { + enum exynos_usi_ver ver; /* USI IP-core version */ + unsigned int sw_conf_mask; /* SW_CONF mask for all protocols */ + size_t min_mode; /* first index in exynos_usi_modes[] */ + size_t max_mode; /* last index in exynos_usi_modes[] */ +}; + +struct exynos_usi { + void __iomem *regs; /* USI register map */ + + size_t mode; /* current USI SW_CONF mode index */ + bool clkreq_on; /* always provide clock to IP */ + + /* System Register */ + struct regmap *sysreg; /* System Register map */ + unsigned int sw_conf; /* SW_CONF register offset in sysreg */ + + const struct exynos_usi_variant *data; +}; + +struct exynos_usi_mode { + const char *name; /* mode name */ + unsigned int val; /* mode register value */ +}; + +static const struct exynos_usi_mode exynos_usi_modes[] = { + [USI_V2_NONE] = { .name = "none", .val = USI_V2_SW_CONF_NONE }, + [USI_V2_UART] = { .name = "uart", .val = USI_V2_SW_CONF_UART }, + [USI_V2_SPI] = { .name = "spi", .val = USI_V2_SW_CONF_SPI }, + [USI_V2_I2C] = { .name = "i2c", .val = USI_V2_SW_CONF_I2C }, +}; + +static const struct exynos_usi_variant exynos850_usi_data = { + .ver = USI_VER2, + .sw_conf_mask = USI_V2_SW_CONF_MASK, + .min_mode = USI_V2_NONE, + .max_mode = USI_V2_I2C, +}; + +static const struct udevice_id exynos_usi_ids[] = { + { + .compatible = "samsung,exynos850-usi", + .data = (ulong)&exynos850_usi_data, + }, + { } /* sentinel */ +}; + +/** + * exynos_usi_set_sw_conf - Set USI block configuration mode + * @dev: Driver object + * + * Select underlying serial protocol (UART/SPI/I2C) in USI IP-core as specified + * in @usi.mode. + * + * Return: 0 on success, or negative error code on failure. + */ +static int exynos_usi_set_sw_conf(struct udevice *dev) +{ + struct exynos_usi *usi = dev_get_priv(dev); + size_t mode = usi->mode; + unsigned int val; + int ret; + + if (mode < usi->data->min_mode || mode > usi->data->max_mode) + return -EINVAL; + + val = exynos_usi_modes[mode].val; + ret = regmap_update_bits(usi->sysreg, usi->sw_conf, + usi->data->sw_conf_mask, val); + if (ret) + return ret; + + dev_dbg(dev, "protocol: %s\n", exynos_usi_modes[mode].name); + + return 0; +} + +/** + * exynos_usi_enable - Initialize USI block + * @usi: USI driver object + * + * USI IP-core start state is "reset" (on startup and after CPU resume). This + * routine enables the USI block by clearing the reset flag. It also configures + * HWACG behavior (needed e.g. for UART Rx). It should be performed before + * underlying protocol becomes functional. + */ +static void exynos_usi_enable(const struct exynos_usi *usi) +{ + u32 val; + + /* Enable USI block */ + val = readl(usi->regs + USI_CON); + val &= ~USI_CON_RESET; + writel(val, usi->regs + USI_CON); + udelay(1); + + /* Continuously provide the clock to USI IP w/o gating */ + if (usi->clkreq_on) { + val = readl(usi->regs + USI_OPTION); + val &= ~USI_OPTION_CLKSTOP_ON; + val |= USI_OPTION_CLKREQ_ON; + writel(val, usi->regs + USI_OPTION); + } +} + +static int exynos_usi_configure(struct udevice *dev) +{ + struct exynos_usi *usi = dev_get_priv(dev); + int ret; + + ret = exynos_usi_set_sw_conf(dev); + if (ret) + return ret; + + if (usi->data->ver == USI_VER2) + exynos_usi_enable(usi); + + return 0; +} + +static int exynos_usi_of_to_plat(struct udevice *dev) +{ + struct exynos_usi *usi = dev_get_priv(dev); + ofnode node = dev_ofnode(dev); + int ret; + u32 mode; + + usi->data = (struct exynos_usi_variant *)dev_get_driver_data(dev); + if (usi->data->ver == USI_VER2) { + usi->regs = dev_read_addr_ptr(dev); + if (!usi->regs) + return -ENODEV; + } + + ret = ofnode_read_u32(node, "samsung,mode", &mode); + if (ret) + return ret; + if (mode < usi->data->min_mode || mode > usi->data->max_mode) + return -EINVAL; + usi->mode = mode; + + usi->sysreg = syscon_regmap_lookup_by_phandle(dev, "samsung,sysreg"); + if (IS_ERR(usi->sysreg)) + return PTR_ERR(usi->sysreg); + + ret = ofnode_read_u32_index(node, "samsung,sysreg", 1, &usi->sw_conf); + if (ret) + return ret; + + usi->clkreq_on = ofnode_read_bool(node, "samsung,clkreq-on"); + + return 0; +} + +static int exynos_usi_probe(struct udevice *dev) +{ + return exynos_usi_configure(dev); +} + +U_BOOT_DRIVER(exynos_usi) = { + .name = "exynos-usi", + .id = UCLASS_MISC, + .of_match = exynos_usi_ids, + .of_to_plat = exynos_usi_of_to_plat, + .probe = exynos_usi_probe, + .priv_auto = sizeof(struct exynos_usi), +}; |