164aa7008SMartin Blumenstingl // SPDX-License-Identifier: GPL-2.0+
264aa7008SMartin Blumenstingl /*
364aa7008SMartin Blumenstingl * Amlogic Meson8 DDR clock controller
464aa7008SMartin Blumenstingl *
564aa7008SMartin Blumenstingl * Copyright (C) 2019 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
664aa7008SMartin Blumenstingl */
764aa7008SMartin Blumenstingl
864aa7008SMartin Blumenstingl #include <dt-bindings/clock/meson8-ddr-clkc.h>
964aa7008SMartin Blumenstingl
1064aa7008SMartin Blumenstingl #include <linux/clk-provider.h>
1164aa7008SMartin Blumenstingl #include <linux/platform_device.h>
1264aa7008SMartin Blumenstingl
1364aa7008SMartin Blumenstingl #include "clk-regmap.h"
1464aa7008SMartin Blumenstingl #include "clk-pll.h"
1564aa7008SMartin Blumenstingl
1664aa7008SMartin Blumenstingl #define AM_DDR_PLL_CNTL 0x00
1764aa7008SMartin Blumenstingl #define AM_DDR_PLL_CNTL1 0x04
1864aa7008SMartin Blumenstingl #define AM_DDR_PLL_CNTL2 0x08
1964aa7008SMartin Blumenstingl #define AM_DDR_PLL_CNTL3 0x0c
2064aa7008SMartin Blumenstingl #define AM_DDR_PLL_CNTL4 0x10
2164aa7008SMartin Blumenstingl #define AM_DDR_PLL_STS 0x14
2264aa7008SMartin Blumenstingl #define DDR_CLK_CNTL 0x18
2364aa7008SMartin Blumenstingl #define DDR_CLK_STS 0x1c
2464aa7008SMartin Blumenstingl
2564aa7008SMartin Blumenstingl static struct clk_regmap meson8_ddr_pll_dco = {
2664aa7008SMartin Blumenstingl .data = &(struct meson_clk_pll_data){
2764aa7008SMartin Blumenstingl .en = {
2864aa7008SMartin Blumenstingl .reg_off = AM_DDR_PLL_CNTL,
2964aa7008SMartin Blumenstingl .shift = 30,
3064aa7008SMartin Blumenstingl .width = 1,
3164aa7008SMartin Blumenstingl },
3264aa7008SMartin Blumenstingl .m = {
3364aa7008SMartin Blumenstingl .reg_off = AM_DDR_PLL_CNTL,
3464aa7008SMartin Blumenstingl .shift = 0,
3564aa7008SMartin Blumenstingl .width = 9,
3664aa7008SMartin Blumenstingl },
3764aa7008SMartin Blumenstingl .n = {
3864aa7008SMartin Blumenstingl .reg_off = AM_DDR_PLL_CNTL,
3964aa7008SMartin Blumenstingl .shift = 9,
4064aa7008SMartin Blumenstingl .width = 5,
4164aa7008SMartin Blumenstingl },
4264aa7008SMartin Blumenstingl .l = {
4364aa7008SMartin Blumenstingl .reg_off = AM_DDR_PLL_CNTL,
4464aa7008SMartin Blumenstingl .shift = 31,
4564aa7008SMartin Blumenstingl .width = 1,
4664aa7008SMartin Blumenstingl },
4764aa7008SMartin Blumenstingl .rst = {
4864aa7008SMartin Blumenstingl .reg_off = AM_DDR_PLL_CNTL,
4964aa7008SMartin Blumenstingl .shift = 29,
5064aa7008SMartin Blumenstingl .width = 1,
5164aa7008SMartin Blumenstingl },
5264aa7008SMartin Blumenstingl },
5364aa7008SMartin Blumenstingl .hw.init = &(struct clk_init_data){
5464aa7008SMartin Blumenstingl .name = "ddr_pll_dco",
5564aa7008SMartin Blumenstingl .ops = &meson_clk_pll_ro_ops,
5664aa7008SMartin Blumenstingl .parent_data = &(const struct clk_parent_data) {
5764aa7008SMartin Blumenstingl .fw_name = "xtal",
5864aa7008SMartin Blumenstingl },
5964aa7008SMartin Blumenstingl .num_parents = 1,
6064aa7008SMartin Blumenstingl },
6164aa7008SMartin Blumenstingl };
6264aa7008SMartin Blumenstingl
6364aa7008SMartin Blumenstingl static struct clk_regmap meson8_ddr_pll = {
6464aa7008SMartin Blumenstingl .data = &(struct clk_regmap_div_data){
6564aa7008SMartin Blumenstingl .offset = AM_DDR_PLL_CNTL,
6664aa7008SMartin Blumenstingl .shift = 16,
6764aa7008SMartin Blumenstingl .width = 2,
6864aa7008SMartin Blumenstingl .flags = CLK_DIVIDER_POWER_OF_TWO,
6964aa7008SMartin Blumenstingl },
7064aa7008SMartin Blumenstingl .hw.init = &(struct clk_init_data){
7164aa7008SMartin Blumenstingl .name = "ddr_pll",
7264aa7008SMartin Blumenstingl .ops = &clk_regmap_divider_ro_ops,
7364aa7008SMartin Blumenstingl .parent_hws = (const struct clk_hw *[]) {
7464aa7008SMartin Blumenstingl &meson8_ddr_pll_dco.hw
7564aa7008SMartin Blumenstingl },
7664aa7008SMartin Blumenstingl .num_parents = 1,
7764aa7008SMartin Blumenstingl },
7864aa7008SMartin Blumenstingl };
7964aa7008SMartin Blumenstingl
8064aa7008SMartin Blumenstingl static struct clk_hw_onecell_data meson8_ddr_clk_hw_onecell_data = {
8164aa7008SMartin Blumenstingl .hws = {
8264aa7008SMartin Blumenstingl [DDR_CLKID_DDR_PLL_DCO] = &meson8_ddr_pll_dco.hw,
8364aa7008SMartin Blumenstingl [DDR_CLKID_DDR_PLL] = &meson8_ddr_pll.hw,
8464aa7008SMartin Blumenstingl },
8564aa7008SMartin Blumenstingl .num = 2,
8664aa7008SMartin Blumenstingl };
8764aa7008SMartin Blumenstingl
8864aa7008SMartin Blumenstingl static struct clk_regmap *const meson8_ddr_clk_regmaps[] = {
8964aa7008SMartin Blumenstingl &meson8_ddr_pll_dco,
9064aa7008SMartin Blumenstingl &meson8_ddr_pll,
9164aa7008SMartin Blumenstingl };
9264aa7008SMartin Blumenstingl
9364aa7008SMartin Blumenstingl static const struct regmap_config meson8_ddr_clkc_regmap_config = {
9464aa7008SMartin Blumenstingl .reg_bits = 8,
9564aa7008SMartin Blumenstingl .val_bits = 32,
9664aa7008SMartin Blumenstingl .reg_stride = 4,
9764aa7008SMartin Blumenstingl .max_register = DDR_CLK_STS,
9864aa7008SMartin Blumenstingl };
9964aa7008SMartin Blumenstingl
meson8_ddr_clkc_probe(struct platform_device * pdev)10064aa7008SMartin Blumenstingl static int meson8_ddr_clkc_probe(struct platform_device *pdev)
10164aa7008SMartin Blumenstingl {
10264aa7008SMartin Blumenstingl struct regmap *regmap;
10364aa7008SMartin Blumenstingl void __iomem *base;
10464aa7008SMartin Blumenstingl struct clk_hw *hw;
10564aa7008SMartin Blumenstingl int ret, i;
10664aa7008SMartin Blumenstingl
10764aa7008SMartin Blumenstingl base = devm_platform_ioremap_resource(pdev, 0);
10864aa7008SMartin Blumenstingl if (IS_ERR(base))
10964aa7008SMartin Blumenstingl return PTR_ERR(base);
11064aa7008SMartin Blumenstingl
11164aa7008SMartin Blumenstingl regmap = devm_regmap_init_mmio(&pdev->dev, base,
11264aa7008SMartin Blumenstingl &meson8_ddr_clkc_regmap_config);
11364aa7008SMartin Blumenstingl if (IS_ERR(regmap))
11464aa7008SMartin Blumenstingl return PTR_ERR(regmap);
11564aa7008SMartin Blumenstingl
11664aa7008SMartin Blumenstingl /* Populate regmap */
11764aa7008SMartin Blumenstingl for (i = 0; i < ARRAY_SIZE(meson8_ddr_clk_regmaps); i++)
11864aa7008SMartin Blumenstingl meson8_ddr_clk_regmaps[i]->map = regmap;
11964aa7008SMartin Blumenstingl
12064aa7008SMartin Blumenstingl /* Register all clks */
12164aa7008SMartin Blumenstingl for (i = 0; i < meson8_ddr_clk_hw_onecell_data.num; i++) {
12264aa7008SMartin Blumenstingl hw = meson8_ddr_clk_hw_onecell_data.hws[i];
12364aa7008SMartin Blumenstingl
12464aa7008SMartin Blumenstingl ret = devm_clk_hw_register(&pdev->dev, hw);
12564aa7008SMartin Blumenstingl if (ret) {
12664aa7008SMartin Blumenstingl dev_err(&pdev->dev, "Clock registration failed\n");
12764aa7008SMartin Blumenstingl return ret;
12864aa7008SMartin Blumenstingl }
12964aa7008SMartin Blumenstingl }
13064aa7008SMartin Blumenstingl
13164aa7008SMartin Blumenstingl return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
13264aa7008SMartin Blumenstingl &meson8_ddr_clk_hw_onecell_data);
13364aa7008SMartin Blumenstingl }
13464aa7008SMartin Blumenstingl
13564aa7008SMartin Blumenstingl static const struct of_device_id meson8_ddr_clkc_match_table[] = {
13664aa7008SMartin Blumenstingl { .compatible = "amlogic,meson8-ddr-clkc" },
13764aa7008SMartin Blumenstingl { .compatible = "amlogic,meson8b-ddr-clkc" },
13864aa7008SMartin Blumenstingl { /* sentinel */ }
13964aa7008SMartin Blumenstingl };
14064aa7008SMartin Blumenstingl
14164aa7008SMartin Blumenstingl static struct platform_driver meson8_ddr_clkc_driver = {
14264aa7008SMartin Blumenstingl .probe = meson8_ddr_clkc_probe,
14364aa7008SMartin Blumenstingl .driver = {
14464aa7008SMartin Blumenstingl .name = "meson8-ddr-clkc",
14564aa7008SMartin Blumenstingl .of_match_table = meson8_ddr_clkc_match_table,
14664aa7008SMartin Blumenstingl },
14764aa7008SMartin Blumenstingl };
14864aa7008SMartin Blumenstingl
14964aa7008SMartin Blumenstingl builtin_platform_driver(meson8_ddr_clkc_driver);
150