0001
0002
0003
0004
0005
0006
0007
0008 #include <dt-bindings/clock/meson8-ddr-clkc.h>
0009
0010 #include <linux/clk-provider.h>
0011 #include <linux/platform_device.h>
0012
0013 #include "clk-regmap.h"
0014 #include "clk-pll.h"
0015
0016 #define AM_DDR_PLL_CNTL 0x00
0017 #define AM_DDR_PLL_CNTL1 0x04
0018 #define AM_DDR_PLL_CNTL2 0x08
0019 #define AM_DDR_PLL_CNTL3 0x0c
0020 #define AM_DDR_PLL_CNTL4 0x10
0021 #define AM_DDR_PLL_STS 0x14
0022 #define DDR_CLK_CNTL 0x18
0023 #define DDR_CLK_STS 0x1c
0024
0025 static struct clk_regmap meson8_ddr_pll_dco = {
0026 .data = &(struct meson_clk_pll_data){
0027 .en = {
0028 .reg_off = AM_DDR_PLL_CNTL,
0029 .shift = 30,
0030 .width = 1,
0031 },
0032 .m = {
0033 .reg_off = AM_DDR_PLL_CNTL,
0034 .shift = 0,
0035 .width = 9,
0036 },
0037 .n = {
0038 .reg_off = AM_DDR_PLL_CNTL,
0039 .shift = 9,
0040 .width = 5,
0041 },
0042 .l = {
0043 .reg_off = AM_DDR_PLL_CNTL,
0044 .shift = 31,
0045 .width = 1,
0046 },
0047 .rst = {
0048 .reg_off = AM_DDR_PLL_CNTL,
0049 .shift = 29,
0050 .width = 1,
0051 },
0052 },
0053 .hw.init = &(struct clk_init_data){
0054 .name = "ddr_pll_dco",
0055 .ops = &meson_clk_pll_ro_ops,
0056 .parent_data = &(const struct clk_parent_data) {
0057 .fw_name = "xtal",
0058 },
0059 .num_parents = 1,
0060 },
0061 };
0062
0063 static struct clk_regmap meson8_ddr_pll = {
0064 .data = &(struct clk_regmap_div_data){
0065 .offset = AM_DDR_PLL_CNTL,
0066 .shift = 16,
0067 .width = 2,
0068 .flags = CLK_DIVIDER_POWER_OF_TWO,
0069 },
0070 .hw.init = &(struct clk_init_data){
0071 .name = "ddr_pll",
0072 .ops = &clk_regmap_divider_ro_ops,
0073 .parent_hws = (const struct clk_hw *[]) {
0074 &meson8_ddr_pll_dco.hw
0075 },
0076 .num_parents = 1,
0077 },
0078 };
0079
0080 static struct clk_hw_onecell_data meson8_ddr_clk_hw_onecell_data = {
0081 .hws = {
0082 [DDR_CLKID_DDR_PLL_DCO] = &meson8_ddr_pll_dco.hw,
0083 [DDR_CLKID_DDR_PLL] = &meson8_ddr_pll.hw,
0084 },
0085 .num = 2,
0086 };
0087
0088 static struct clk_regmap *const meson8_ddr_clk_regmaps[] = {
0089 &meson8_ddr_pll_dco,
0090 &meson8_ddr_pll,
0091 };
0092
0093 static const struct regmap_config meson8_ddr_clkc_regmap_config = {
0094 .reg_bits = 8,
0095 .val_bits = 32,
0096 .reg_stride = 4,
0097 .max_register = DDR_CLK_STS,
0098 };
0099
0100 static int meson8_ddr_clkc_probe(struct platform_device *pdev)
0101 {
0102 struct regmap *regmap;
0103 void __iomem *base;
0104 struct clk_hw *hw;
0105 int ret, i;
0106
0107 base = devm_platform_ioremap_resource(pdev, 0);
0108 if (IS_ERR(base))
0109 return PTR_ERR(base);
0110
0111 regmap = devm_regmap_init_mmio(&pdev->dev, base,
0112 &meson8_ddr_clkc_regmap_config);
0113 if (IS_ERR(regmap))
0114 return PTR_ERR(regmap);
0115
0116
0117 for (i = 0; i < ARRAY_SIZE(meson8_ddr_clk_regmaps); i++)
0118 meson8_ddr_clk_regmaps[i]->map = regmap;
0119
0120
0121 for (i = 0; i < meson8_ddr_clk_hw_onecell_data.num; i++) {
0122 hw = meson8_ddr_clk_hw_onecell_data.hws[i];
0123
0124 ret = devm_clk_hw_register(&pdev->dev, hw);
0125 if (ret) {
0126 dev_err(&pdev->dev, "Clock registration failed\n");
0127 return ret;
0128 }
0129 }
0130
0131 return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
0132 &meson8_ddr_clk_hw_onecell_data);
0133 }
0134
0135 static const struct of_device_id meson8_ddr_clkc_match_table[] = {
0136 { .compatible = "amlogic,meson8-ddr-clkc" },
0137 { .compatible = "amlogic,meson8b-ddr-clkc" },
0138 { }
0139 };
0140
0141 static struct platform_driver meson8_ddr_clkc_driver = {
0142 .probe = meson8_ddr_clkc_probe,
0143 .driver = {
0144 .name = "meson8-ddr-clkc",
0145 .of_match_table = meson8_ddr_clkc_match_table,
0146 },
0147 };
0148
0149 builtin_platform_driver(meson8_ddr_clkc_driver);