1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Amlogic Meson8 DDR clock controller
4 *
5 * Copyright (C) 2019 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
6 */
7
8 #include <dt-bindings/clock/meson8-ddr-clkc.h>
9
10 #include <linux/clk-provider.h>
11 #include <linux/platform_device.h>
12
13 #include "clk-regmap.h"
14 #include "clk-pll.h"
15
16 #define AM_DDR_PLL_CNTL 0x00
17 #define AM_DDR_PLL_CNTL1 0x04
18 #define AM_DDR_PLL_CNTL2 0x08
19 #define AM_DDR_PLL_CNTL3 0x0c
20 #define AM_DDR_PLL_CNTL4 0x10
21 #define AM_DDR_PLL_STS 0x14
22 #define DDR_CLK_CNTL 0x18
23 #define DDR_CLK_STS 0x1c
24
25 static struct clk_regmap meson8_ddr_pll_dco = {
26 .data = &(struct meson_clk_pll_data){
27 .en = {
28 .reg_off = AM_DDR_PLL_CNTL,
29 .shift = 30,
30 .width = 1,
31 },
32 .m = {
33 .reg_off = AM_DDR_PLL_CNTL,
34 .shift = 0,
35 .width = 9,
36 },
37 .n = {
38 .reg_off = AM_DDR_PLL_CNTL,
39 .shift = 9,
40 .width = 5,
41 },
42 .l = {
43 .reg_off = AM_DDR_PLL_CNTL,
44 .shift = 31,
45 .width = 1,
46 },
47 .rst = {
48 .reg_off = AM_DDR_PLL_CNTL,
49 .shift = 29,
50 .width = 1,
51 },
52 },
53 .hw.init = &(struct clk_init_data){
54 .name = "ddr_pll_dco",
55 .ops = &meson_clk_pll_ro_ops,
56 .parent_data = &(const struct clk_parent_data) {
57 .fw_name = "xtal",
58 },
59 .num_parents = 1,
60 },
61 };
62
63 static struct clk_regmap meson8_ddr_pll = {
64 .data = &(struct clk_regmap_div_data){
65 .offset = AM_DDR_PLL_CNTL,
66 .shift = 16,
67 .width = 2,
68 .flags = CLK_DIVIDER_POWER_OF_TWO,
69 },
70 .hw.init = &(struct clk_init_data){
71 .name = "ddr_pll",
72 .ops = &clk_regmap_divider_ro_ops,
73 .parent_hws = (const struct clk_hw *[]) {
74 &meson8_ddr_pll_dco.hw
75 },
76 .num_parents = 1,
77 },
78 };
79
80 static struct clk_hw_onecell_data meson8_ddr_clk_hw_onecell_data = {
81 .hws = {
82 [DDR_CLKID_DDR_PLL_DCO] = &meson8_ddr_pll_dco.hw,
83 [DDR_CLKID_DDR_PLL] = &meson8_ddr_pll.hw,
84 },
85 .num = 2,
86 };
87
88 static const struct regmap_config meson8_ddr_clkc_regmap_config = {
89 .reg_bits = 8,
90 .val_bits = 32,
91 .reg_stride = 4,
92 .max_register = DDR_CLK_STS,
93 };
94
meson8_ddr_clkc_probe(struct platform_device * pdev)95 static int meson8_ddr_clkc_probe(struct platform_device *pdev)
96 {
97 struct regmap *regmap;
98 void __iomem *base;
99 struct clk_hw *hw;
100 int ret, i;
101
102 base = devm_platform_ioremap_resource(pdev, 0);
103 if (IS_ERR(base))
104 return PTR_ERR(base);
105
106 regmap = devm_regmap_init_mmio(&pdev->dev, base,
107 &meson8_ddr_clkc_regmap_config);
108 if (IS_ERR(regmap))
109 return PTR_ERR(regmap);
110
111 /* Register all clks */
112 for (i = 0; i < meson8_ddr_clk_hw_onecell_data.num; i++) {
113 hw = meson8_ddr_clk_hw_onecell_data.hws[i];
114
115 ret = devm_clk_hw_register(&pdev->dev, hw);
116 if (ret) {
117 dev_err(&pdev->dev, "Clock registration failed\n");
118 return ret;
119 }
120 }
121
122 return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
123 &meson8_ddr_clk_hw_onecell_data);
124 }
125
126 static const struct of_device_id meson8_ddr_clkc_match_table[] = {
127 { .compatible = "amlogic,meson8-ddr-clkc" },
128 { .compatible = "amlogic,meson8b-ddr-clkc" },
129 { /* sentinel */ }
130 };
131
132 static struct platform_driver meson8_ddr_clkc_driver = {
133 .probe = meson8_ddr_clkc_probe,
134 .driver = {
135 .name = "meson8-ddr-clkc",
136 .of_match_table = meson8_ddr_clkc_match_table,
137 },
138 };
139
140 builtin_platform_driver(meson8_ddr_clkc_driver);
141