150 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Amlogic Meson8 DDR clock controller4 *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 0x0017#define AM_DDR_PLL_CNTL1 0x0418#define AM_DDR_PLL_CNTL2 0x0819#define AM_DDR_PLL_CNTL3 0x0c20#define AM_DDR_PLL_CNTL4 0x1021#define AM_DDR_PLL_STS 0x1422#define DDR_CLK_CNTL 0x1823#define DDR_CLK_STS 0x1c24 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.hw75 },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