| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2019 MediaTek Inc. All Rights Reserved. |
| * |
| * Author: Weijie Gao <weijie.gao@mediatek.com> |
| */ |
| |
| #include <clk-uclass.h> |
| #include <dm.h> |
| #include <dt-bindings/clock/mt7628-clk.h> |
| #include <linux/bitops.h> |
| #include <linux/io.h> |
| |
| /* SYSCFG0 */ |
| #define XTAL_40M_SEL BIT(6) |
| |
| /* CLKCFG0 */ |
| #define CLKCFG0_REG 0x0 |
| #define PERI_CLK_FROM_XTAL_SEL BIT(4) |
| #define CPU_PLL_FROM_BBP BIT(1) |
| #define CPU_PLL_FROM_XTAL BIT(0) |
| |
| /* CLKCFG1 */ |
| #define CLKCFG1_REG 0x4 |
| |
| #define CLK_SRC_CPU -1 |
| #define CLK_SRC_CPU_D2 -2 |
| #define CLK_SRC_SYS -3 |
| #define CLK_SRC_XTAL -4 |
| #define CLK_SRC_PERI -5 |
| |
| struct mt7628_clk_priv { |
| void __iomem *base; |
| int cpu_clk; |
| int sys_clk; |
| int xtal_clk; |
| }; |
| |
| static const int mt7628_clks[] = { |
| [CLK_SYS] = CLK_SRC_SYS, |
| [CLK_CPU] = CLK_SRC_CPU, |
| [CLK_XTAL] = CLK_SRC_XTAL, |
| [CLK_PWM] = CLK_SRC_PERI, |
| [CLK_MIPS_CNT] = CLK_SRC_CPU_D2, |
| [CLK_UART2] = CLK_SRC_PERI, |
| [CLK_UART1] = CLK_SRC_PERI, |
| [CLK_UART0] = CLK_SRC_PERI, |
| [CLK_SPI] = CLK_SRC_SYS, |
| [CLK_I2C] = CLK_SRC_PERI, |
| }; |
| |
| static ulong mt7628_clk_get_rate(struct clk *clk) |
| { |
| struct mt7628_clk_priv *priv = dev_get_priv(clk->dev); |
| u32 val; |
| |
| if (clk->id >= ARRAY_SIZE(mt7628_clks)) |
| return 0; |
| |
| switch (mt7628_clks[clk->id]) { |
| case CLK_SRC_CPU: |
| return priv->cpu_clk; |
| case CLK_SRC_CPU_D2: |
| return priv->cpu_clk / 2; |
| case CLK_SRC_SYS: |
| return priv->sys_clk; |
| case CLK_SRC_XTAL: |
| return priv->xtal_clk; |
| case CLK_SRC_PERI: |
| val = readl(priv->base + CLKCFG0_REG); |
| if (val & PERI_CLK_FROM_XTAL_SEL) |
| return priv->xtal_clk; |
| else |
| return 40000000; |
| default: |
| return mt7628_clks[clk->id]; |
| } |
| } |
| |
| static int mt7628_clk_enable(struct clk *clk) |
| { |
| struct mt7628_clk_priv *priv = dev_get_priv(clk->dev); |
| |
| if (clk->id > 31) |
| return -1; |
| |
| setbits_32(priv->base + CLKCFG1_REG, BIT(clk->id)); |
| |
| return 0; |
| } |
| |
| static int mt7628_clk_disable(struct clk *clk) |
| { |
| struct mt7628_clk_priv *priv = dev_get_priv(clk->dev); |
| |
| if (clk->id > 31) |
| return -1; |
| |
| clrbits_32(priv->base + CLKCFG1_REG, BIT(clk->id)); |
| |
| return 0; |
| } |
| |
| const struct clk_ops mt7628_clk_ops = { |
| .enable = mt7628_clk_enable, |
| .disable = mt7628_clk_disable, |
| .get_rate = mt7628_clk_get_rate, |
| }; |
| |
| static int mt7628_clk_probe(struct udevice *dev) |
| { |
| struct mt7628_clk_priv *priv = dev_get_priv(dev); |
| void __iomem *syscfg_base; |
| u32 val; |
| |
| priv->base = (void __iomem *)dev_remap_addr_index(dev, 0); |
| if (!priv->base) |
| return -EINVAL; |
| |
| syscfg_base = (void __iomem *)dev_remap_addr_index(dev, 1); |
| if (!syscfg_base) |
| return -EINVAL; |
| |
| val = readl(syscfg_base); |
| if (val & XTAL_40M_SEL) |
| priv->xtal_clk = 40000000; |
| else |
| priv->xtal_clk = 25000000; |
| |
| val = readl(priv->base + CLKCFG0_REG); |
| if (val & CPU_PLL_FROM_BBP) |
| priv->cpu_clk = 480000000; |
| else if (val & CPU_PLL_FROM_XTAL) |
| priv->cpu_clk = priv->xtal_clk; |
| else if (priv->xtal_clk == 40000000) |
| priv->cpu_clk = 580000000; /* (xtal_freq / 2) * 29 */ |
| else |
| priv->cpu_clk = 575000000; /* xtal_freq * 23 */ |
| |
| priv->sys_clk = priv->cpu_clk / 3; |
| |
| return 0; |
| } |
| |
| static const struct udevice_id mt7628_clk_ids[] = { |
| { .compatible = "mediatek,mt7628-clk" }, |
| { } |
| }; |
| |
| U_BOOT_DRIVER(mt7628_clk) = { |
| .name = "mt7628-clk", |
| .id = UCLASS_CLK, |
| .of_match = mt7628_clk_ids, |
| .probe = mt7628_clk_probe, |
| .priv_auto = sizeof(struct mt7628_clk_priv), |
| .ops = &mt7628_clk_ops, |
| }; |