linux/drivers/clk/meson/meson8-ddr.c
<<
>>
Prefs
   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
  25static 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
  63static 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
  80static 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
  88static struct clk_regmap *const meson8_ddr_clk_regmaps[] = {
  89        &meson8_ddr_pll_dco,
  90        &meson8_ddr_pll,
  91};
  92
  93static 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
 100static 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
 135static 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
 141static 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
 149builtin_platform_driver(meson8_ddr_clkc_driver);
 150