| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * (C) Copyright 2018 - Jian Hu <jian.hu@amlogic.com> |
| * Author: Jian Hu <jian.hu@amlogic.com> |
| */ |
| |
| #include <common.h> |
| #include <asm/arch/clock.h> |
| #include <asm/io.h> |
| #include <clk-uclass.h> |
| #include <div64.h> |
| #include <dm.h> |
| #include <dt-bindings/clock/g12-clkc.h> |
| #include "clk_meson.h" |
| |
| /* clk81 gates */ |
| static struct meson_gate gates[] = { |
| {CLKID_MIPI_DSI_HOST, HHI_GCLK_MPEG0, 3}, |
| {CLKID_ETH_PHY, HHI_GCLK_MPEG0, 4}, |
| {CLKID_SPICC0, HHI_GCLK_MPEG0, 8}, |
| {CLKID_I2C, HHI_GCLK_MPEG0, 9}, |
| {CLKID_UART0, HHI_GCLK_MPEG0, 13}, |
| {CLKID_SPICC1, HHI_GCLK_MPEG0, 14}, |
| {CLKID_MIPI_DSI_PHY, HHI_GCLK_MPEG0, 20}, |
| {CLKID_SD_EMMC_A, HHI_GCLK_MPEG0, 24}, |
| {CLKID_SD_EMMC_B, HHI_GCLK_MPEG0, 25}, |
| {CLKID_SD_EMMC_C, HHI_GCLK_MPEG0, 26}, |
| {CLKID_ETH_CORE, HHI_GCLK_MPEG1, 3}, |
| {CLKID_ADC, HHI_GCLK_MPEG1, 13}, |
| {CLKID_UART1, HHI_GCLK_MPEG1, 16}, |
| {CLKID_HTX_HDCP22, HHI_GCLK_MPEG2, 3}, |
| {CLKID_HTX_PCLK, HHI_GCLK_MPEG2, 4}, |
| {CLKID_UART2, HHI_GCLK_MPEG2, 15}, |
| {CLKID_VCLK2_VENCI0, HHI_GCLK_OTHER, 1}, |
| {CLKID_VCLK2_VENCI1, HHI_GCLK_OTHER, 2}, |
| {CLKID_VCLK2_VENCP0, HHI_GCLK_OTHER, 3}, |
| {CLKID_VCLK2_VENCP1, HHI_GCLK_OTHER, 4}, |
| {CLKID_VCLK2_VENCT0, HHI_GCLK_OTHER, 5}, |
| {CLKID_VCLK2_VENCT1, HHI_GCLK_OTHER, 6}, |
| {CLKID_VCLK2_OTHER, HHI_GCLK_OTHER, 7}, |
| {CLKID_VCLK2_ENCI, HHI_GCLK_OTHER, 8}, |
| {CLKID_VCLK2_ENCP, HHI_GCLK_OTHER, 9}, |
| {CLKID_VCLK2_ENCT, HHI_GCLK_OTHER, 22}, |
| {CLKID_VCLK2_ENCL, HHI_GCLK_OTHER, 23}, |
| {CLKID_VCLK2_VENCLMMC, HHI_GCLK_OTHER, 24}, |
| {CLKID_VCLK2_VENCL, HHI_GCLK_OTHER, 25}, |
| {CLKID_VCLK2_OTHER1, HHI_GCLK_OTHER, 26}, |
| {CLKID_SPICC0_GATE, HHI_SPICC_CLK_CNTL, 6}, |
| {CLKID_SPICC1_GATE, HHI_SPICC_CLK_CNTL, 22}, |
| {CLKID_SARADC_GATE, AO_SAR_CLK, 8}, |
| {CLKID_AO_I2C, AO_CLK_GATE0, 2}, |
| {CLKID_SD_EMMC_A_P0_GATE, HHI_SD_EMMC_CLK_CNTL, 7}, |
| {CLKID_SD_EMMC_B_P0_GATE, HHI_SD_EMMC_CLK_CNTL, 23}, |
| {CLKID_SD_EMMC_C_P0_GATE, HHI_NAND_CLK_CNTL, 7}, |
| }; |
| |
| static unsigned int spicc_parents[] = {CLKID_XTAL, CLKID_CLK81, CLKID_FCLK_DIV4, |
| CLKID_FCLK_DIV3, CLKID_UNREALIZED, CLKID_FCLK_DIV5, CLKID_FCLK_DIV7, CLKID_UNREALIZED}; |
| |
| static unsigned int saradc_parents[] = {CLKID_XTAL, CLKID_AO_CLK81}; |
| static unsigned int sd_emmc_parents[] = {CLKID_XTAL, CLKID_FCLK_DIV2, CLKID_FCLK_DIV3, |
| CLKID_FCLK_DIV5, CLKID_FCLK_DIV7, CLKID_UNREALIZED, CLKID_UNREALIZED, CLKID_UNREALIZED}; |
| |
| |
| static struct meson_mux muxes[] = { |
| {CLKID_SPICC0_MUX, HHI_SPICC_CLK_CNTL, 7, 7, spicc_parents, ARRAY_SIZE(spicc_parents)}, |
| {CLKID_SPICC1_MUX, HHI_SPICC_CLK_CNTL, 23, 7, spicc_parents, ARRAY_SIZE(spicc_parents)}, |
| {CLKID_SARADC_MUX, AO_SAR_CLK, 9, 3, saradc_parents, ARRAY_SIZE(saradc_parents)}, |
| {CLKID_SD_EMMC_A_P0_MUX, HHI_SD_EMMC_CLK_CNTL, 9, 7, sd_emmc_parents, ARRAY_SIZE(sd_emmc_parents)}, |
| {CLKID_SD_EMMC_B_P0_MUX, HHI_SD_EMMC_CLK_CNTL, 25, 7, sd_emmc_parents, ARRAY_SIZE(sd_emmc_parents)}, |
| {CLKID_SD_EMMC_C_P0_MUX, HHI_NAND_CLK_CNTL, 9, 7, sd_emmc_parents, ARRAY_SIZE(sd_emmc_parents)}, |
| }; |
| |
| static struct meson_div divs[] = { |
| {CLKID_SPICC0_DIV, HHI_SPICC_CLK_CNTL, 0, 6, CLKID_SPICC0_MUX}, |
| {CLKID_SPICC1_DIV, HHI_SPICC_CLK_CNTL, 16, 7, CLKID_SPICC1_MUX}, |
| {CLKID_SARADC_DIV, AO_SAR_CLK, 0, 8, CLKID_SARADC_MUX}, |
| {CLKID_SD_EMMC_A_P0_DIV, HHI_SD_EMMC_CLK_CNTL, 0, 7, CLKID_SD_EMMC_A_P0_MUX}, |
| {CLKID_SD_EMMC_B_P0_DIV, HHI_SD_EMMC_CLK_CNTL, 16, 7, CLKID_SD_EMMC_B_P0_MUX}, |
| {CLKID_SD_EMMC_C_P0_DIV, HHI_NAND_CLK_CNTL, 0, 7, CLKID_SD_EMMC_C_P0_MUX}, |
| }; |
| |
| static struct parm meson_fixed_pll_parm[3] = { |
| {HHI_FIX_PLL_CNTL0, 0, 8}, /* pm */ |
| {HHI_FIX_PLL_CNTL0, 10, 5}, /* pn */ |
| {HHI_FIX_PLL_CNTL0, 16, 2}, /* pod */ |
| }; |
| |
| static struct parm meson_sys_pll_parm[3] = { |
| {HHI_SYS_PLL_CNTL0, 0, 8}, /* pm */ |
| {HHI_SYS_PLL_CNTL0, 10, 5}, /* pn */ |
| {HHI_SYS_PLL_CNTL0, 16, 2}, /* pod */ |
| }; |
| |
| static int meson_clk_enable(struct clk *clk) |
| { |
| return meson_set_gate_by_id(clk, gates, ARRAY_SIZE(gates), true); |
| } |
| |
| static int meson_clk_disable(struct clk *clk) |
| { |
| return meson_set_gate_by_id(clk, gates, ARRAY_SIZE(gates), false); |
| } |
| |
| static ulong meson_pll_get_rate(struct clk *clk, unsigned long id) |
| { |
| struct meson_clk *priv = dev_get_priv(clk->dev); |
| struct parm *pm, *pn, *pod; |
| unsigned long parent_rate_mhz = clk_get_rate(&priv->clkin)/1000000; |
| u16 n, m, od; |
| u32 reg; |
| |
| switch (id) { |
| case CLKID_FIXED_PLL: |
| pm = &meson_fixed_pll_parm[0]; |
| pn = &meson_fixed_pll_parm[1]; |
| pod = &meson_fixed_pll_parm[2]; |
| break; |
| case CLKID_SYS_PLL: |
| pm = &meson_sys_pll_parm[0]; |
| pn = &meson_sys_pll_parm[1]; |
| pod = &meson_sys_pll_parm[2]; |
| break; |
| default: |
| return -ENOENT; |
| } |
| |
| reg = readl(priv->addr + pn->reg_off); |
| n = PARM_GET(pn->width, pn->shift, reg); |
| |
| reg = readl(priv->addr + pm->reg_off); |
| m = PARM_GET(pm->width, pm->shift, reg); |
| |
| reg = readl(priv->addr + pod->reg_off); |
| od = PARM_GET(pod->width, pod->shift, reg); |
| |
| return ((parent_rate_mhz * m / n) >> od) * 1000000; |
| } |
| |
| static ulong meson_clk_get_rate_by_id(struct clk *clk, ulong id) |
| { |
| ulong rate; |
| struct meson_clk *priv = dev_get_priv(clk->dev); |
| |
| switch (id) { |
| case CLKID_XTAL: |
| rate = clk_get_rate(&priv->clkin); |
| break; |
| case CLKID_FIXED_PLL: |
| case CLKID_SYS_PLL: |
| rate = meson_pll_get_rate(clk, id); |
| break; |
| case CLKID_FCLK_DIV2: |
| rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 2; |
| break; |
| case CLKID_FCLK_DIV3: |
| rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 3; |
| break; |
| case CLKID_FCLK_DIV4: |
| rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 4; |
| break; |
| case CLKID_FCLK_DIV5: |
| rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 5; |
| break; |
| case CLKID_FCLK_DIV7: |
| rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 7; |
| break; |
| /* clk81 has realized in rom code*/ |
| case CLKID_CLK81: |
| case CLKID_AO_CLK81: |
| rate = CLK81_RATE; |
| break; |
| default: |
| pr_err("Unknown clock, Can not get its rate\n"); |
| rate = 0; |
| break; |
| } |
| |
| return rate; |
| } |
| |
| static ulong meson_clk_get_rate(struct clk *clk) |
| { |
| return meson_clk_get_rate_by_id(clk, clk->id); |
| } |
| |
| static ulong meson_clk_set_rate(struct clk *clk, ulong rate) |
| { |
| ulong div_parent, mux_parent, parent_rate; |
| unsigned int div_val; |
| struct meson_clk *priv = dev_get_priv(clk->dev); |
| unsigned int i; |
| struct meson_div *div = NULL; |
| |
| for (i = 0; i < ARRAY_SIZE(divs); i++) { |
| if (clk->id == divs[i].index) |
| div = &divs[i]; |
| } |
| |
| if (div == NULL) { |
| pr_err("Failed to find clock %lu\n", clk->id); |
| return -ENOENT; |
| } |
| |
| div_parent = div->parent_index; |
| mux_parent = meson_clk_get_mux_parent(clk, muxes, |
| ARRAY_SIZE(muxes), div_parent); |
| parent_rate = meson_clk_get_rate_by_id(clk, mux_parent); |
| |
| div_val = DIV_ROUND_CLOSEST(parent_rate, rate) - 1; |
| |
| meson_clk_set_div(priv, div, div_val); |
| |
| return 0; |
| } |
| |
| static int meson_clk_set_parent(struct clk* clk, struct clk* parent_clk) |
| { |
| return meson_mux_set_parent_by_id(clk, muxes, ARRAY_SIZE(muxes), parent_clk); |
| } |
| |
| static struct clk_ops meson_clk_ops = { |
| .disable = meson_clk_disable, |
| .enable = meson_clk_enable, |
| .get_rate = meson_clk_get_rate, |
| .set_rate = meson_clk_set_rate, |
| .set_parent = meson_clk_set_parent, |
| }; |
| |
| static int meson_clk_probe(struct udevice *dev) |
| { |
| struct meson_clk *priv = dev_get_priv(dev); |
| |
| clk_get_by_name(dev, "xtal", &priv->clkin); |
| priv->addr = dev_read_addr_ptr(dev); |
| |
| debug("meson-clk: probed at addr %p\n", priv->addr); |
| |
| return 0; |
| } |
| |
| static const struct udevice_id meson_clk_ids[] = { |
| { .compatible = "amlogic,g12a-clkc" }, |
| { .compatible = "amlogic,g12a-aoclkc" }, |
| { } |
| }; |
| |
| U_BOOT_DRIVER(meson_clk) = { |
| .name = "meson-clk-g12a", |
| .id = UCLASS_CLK, |
| .of_match = meson_clk_ids, |
| .priv_auto_alloc_size = sizeof(struct meson_clk), |
| .ops = &meson_clk_ops, |
| .probe = meson_clk_probe, |
| }; |