Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Amlogic Meson8 DDR clock controller
0004  *
0005  * Copyright (C) 2019 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
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     /* Populate regmap */
0117     for (i = 0; i < ARRAY_SIZE(meson8_ddr_clk_regmaps); i++)
0118         meson8_ddr_clk_regmaps[i]->map = regmap;
0119 
0120     /* Register all clks */
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     { /* sentinel */ }
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);