Merge branch 'stable/for-linus-5.12' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / clk / meson / meson8-ddr.c
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 struct clk_regmap *const meson8_ddr_clk_regmaps[] = {
89         &meson8_ddr_pll_dco,
90         &meson8_ddr_pll,
91 };
92
93 static const struct regmap_config meson8_ddr_clkc_regmap_config = {
94         .reg_bits = 8,
95         .val_bits = 32,
96         .reg_stride = 4,
97         .max_register = DDR_CLK_STS,
98 };
99
100 static int meson8_ddr_clkc_probe(struct platform_device *pdev)
101 {
102         struct regmap *regmap;
103         void __iomem *base;
104         struct clk_hw *hw;
105         int ret, i;
106
107         base = devm_platform_ioremap_resource(pdev, 0);
108         if (IS_ERR(base))
109                 return PTR_ERR(base);
110
111         regmap = devm_regmap_init_mmio(&pdev->dev, base,
112                                        &meson8_ddr_clkc_regmap_config);
113         if (IS_ERR(regmap))
114                 return PTR_ERR(regmap);
115
116         /* Populate regmap */
117         for (i = 0; i < ARRAY_SIZE(meson8_ddr_clk_regmaps); i++)
118                 meson8_ddr_clk_regmaps[i]->map = regmap;
119
120         /* Register all clks */
121         for (i = 0; i < meson8_ddr_clk_hw_onecell_data.num; i++) {
122                 hw = meson8_ddr_clk_hw_onecell_data.hws[i];
123
124                 ret = devm_clk_hw_register(&pdev->dev, hw);
125                 if (ret) {
126                         dev_err(&pdev->dev, "Clock registration failed\n");
127                         return ret;
128                 }
129         }
130
131         return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
132                                            &meson8_ddr_clk_hw_onecell_data);
133 }
134
135 static const struct of_device_id meson8_ddr_clkc_match_table[] = {
136         { .compatible = "amlogic,meson8-ddr-clkc" },
137         { .compatible = "amlogic,meson8b-ddr-clkc" },
138         { /* sentinel */ }
139 };
140
141 static struct platform_driver meson8_ddr_clkc_driver = {
142         .probe          = meson8_ddr_clkc_probe,
143         .driver         = {
144                 .name   = "meson8-ddr-clkc",
145                 .of_match_table = meson8_ddr_clkc_match_table,
146         },
147 };
148
149 builtin_platform_driver(meson8_ddr_clkc_driver);