18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Amlogic Meson8 DDR clock controller
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <dt-bindings/clock/meson8-ddr-clkc.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "clk-regmap.h"
148c2ecf20Sopenharmony_ci#include "clk-pll.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define AM_DDR_PLL_CNTL			0x00
178c2ecf20Sopenharmony_ci#define AM_DDR_PLL_CNTL1		0x04
188c2ecf20Sopenharmony_ci#define AM_DDR_PLL_CNTL2		0x08
198c2ecf20Sopenharmony_ci#define AM_DDR_PLL_CNTL3		0x0c
208c2ecf20Sopenharmony_ci#define AM_DDR_PLL_CNTL4		0x10
218c2ecf20Sopenharmony_ci#define AM_DDR_PLL_STS			0x14
228c2ecf20Sopenharmony_ci#define DDR_CLK_CNTL			0x18
238c2ecf20Sopenharmony_ci#define DDR_CLK_STS			0x1c
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic struct clk_regmap meson8_ddr_pll_dco = {
268c2ecf20Sopenharmony_ci	.data = &(struct meson_clk_pll_data){
278c2ecf20Sopenharmony_ci		.en = {
288c2ecf20Sopenharmony_ci			.reg_off = AM_DDR_PLL_CNTL,
298c2ecf20Sopenharmony_ci			.shift   = 30,
308c2ecf20Sopenharmony_ci			.width   = 1,
318c2ecf20Sopenharmony_ci		},
328c2ecf20Sopenharmony_ci		.m = {
338c2ecf20Sopenharmony_ci			.reg_off = AM_DDR_PLL_CNTL,
348c2ecf20Sopenharmony_ci			.shift   = 0,
358c2ecf20Sopenharmony_ci			.width   = 9,
368c2ecf20Sopenharmony_ci		},
378c2ecf20Sopenharmony_ci		.n = {
388c2ecf20Sopenharmony_ci			.reg_off = AM_DDR_PLL_CNTL,
398c2ecf20Sopenharmony_ci			.shift   = 9,
408c2ecf20Sopenharmony_ci			.width   = 5,
418c2ecf20Sopenharmony_ci		},
428c2ecf20Sopenharmony_ci		.l = {
438c2ecf20Sopenharmony_ci			.reg_off = AM_DDR_PLL_CNTL,
448c2ecf20Sopenharmony_ci			.shift   = 31,
458c2ecf20Sopenharmony_ci			.width   = 1,
468c2ecf20Sopenharmony_ci		},
478c2ecf20Sopenharmony_ci		.rst = {
488c2ecf20Sopenharmony_ci			.reg_off = AM_DDR_PLL_CNTL,
498c2ecf20Sopenharmony_ci			.shift   = 29,
508c2ecf20Sopenharmony_ci			.width   = 1,
518c2ecf20Sopenharmony_ci		},
528c2ecf20Sopenharmony_ci	},
538c2ecf20Sopenharmony_ci	.hw.init = &(struct clk_init_data){
548c2ecf20Sopenharmony_ci		.name = "ddr_pll_dco",
558c2ecf20Sopenharmony_ci		.ops = &meson_clk_pll_ro_ops,
568c2ecf20Sopenharmony_ci		.parent_data = &(const struct clk_parent_data) {
578c2ecf20Sopenharmony_ci			.fw_name = "xtal",
588c2ecf20Sopenharmony_ci		},
598c2ecf20Sopenharmony_ci		.num_parents = 1,
608c2ecf20Sopenharmony_ci	},
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic struct clk_regmap meson8_ddr_pll = {
648c2ecf20Sopenharmony_ci	.data = &(struct clk_regmap_div_data){
658c2ecf20Sopenharmony_ci		.offset = AM_DDR_PLL_CNTL,
668c2ecf20Sopenharmony_ci		.shift = 16,
678c2ecf20Sopenharmony_ci		.width = 2,
688c2ecf20Sopenharmony_ci		.flags = CLK_DIVIDER_POWER_OF_TWO,
698c2ecf20Sopenharmony_ci	},
708c2ecf20Sopenharmony_ci	.hw.init = &(struct clk_init_data){
718c2ecf20Sopenharmony_ci		.name = "ddr_pll",
728c2ecf20Sopenharmony_ci		.ops = &clk_regmap_divider_ro_ops,
738c2ecf20Sopenharmony_ci		.parent_hws = (const struct clk_hw *[]) {
748c2ecf20Sopenharmony_ci			&meson8_ddr_pll_dco.hw
758c2ecf20Sopenharmony_ci		},
768c2ecf20Sopenharmony_ci		.num_parents = 1,
778c2ecf20Sopenharmony_ci	},
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic struct clk_hw_onecell_data meson8_ddr_clk_hw_onecell_data = {
818c2ecf20Sopenharmony_ci	.hws = {
828c2ecf20Sopenharmony_ci		[DDR_CLKID_DDR_PLL_DCO]		= &meson8_ddr_pll_dco.hw,
838c2ecf20Sopenharmony_ci		[DDR_CLKID_DDR_PLL]		= &meson8_ddr_pll.hw,
848c2ecf20Sopenharmony_ci	},
858c2ecf20Sopenharmony_ci	.num = 2,
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic struct clk_regmap *const meson8_ddr_clk_regmaps[] = {
898c2ecf20Sopenharmony_ci	&meson8_ddr_pll_dco,
908c2ecf20Sopenharmony_ci	&meson8_ddr_pll,
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic const struct regmap_config meson8_ddr_clkc_regmap_config = {
948c2ecf20Sopenharmony_ci	.reg_bits = 8,
958c2ecf20Sopenharmony_ci	.val_bits = 32,
968c2ecf20Sopenharmony_ci	.reg_stride = 4,
978c2ecf20Sopenharmony_ci	.max_register = DDR_CLK_STS,
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int meson8_ddr_clkc_probe(struct platform_device *pdev)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct regmap *regmap;
1038c2ecf20Sopenharmony_ci	void __iomem *base;
1048c2ecf20Sopenharmony_ci	struct clk_hw *hw;
1058c2ecf20Sopenharmony_ci	int ret, i;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
1088c2ecf20Sopenharmony_ci	if (IS_ERR(base))
1098c2ecf20Sopenharmony_ci		return PTR_ERR(base);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	regmap = devm_regmap_init_mmio(&pdev->dev, base,
1128c2ecf20Sopenharmony_ci				       &meson8_ddr_clkc_regmap_config);
1138c2ecf20Sopenharmony_ci	if (IS_ERR(regmap))
1148c2ecf20Sopenharmony_ci		return PTR_ERR(regmap);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* Populate regmap */
1178c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(meson8_ddr_clk_regmaps); i++)
1188c2ecf20Sopenharmony_ci		meson8_ddr_clk_regmaps[i]->map = regmap;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Register all clks */
1218c2ecf20Sopenharmony_ci	for (i = 0; i < meson8_ddr_clk_hw_onecell_data.num; i++) {
1228c2ecf20Sopenharmony_ci		hw = meson8_ddr_clk_hw_onecell_data.hws[i];
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		ret = devm_clk_hw_register(&pdev->dev, hw);
1258c2ecf20Sopenharmony_ci		if (ret) {
1268c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Clock registration failed\n");
1278c2ecf20Sopenharmony_ci			return ret;
1288c2ecf20Sopenharmony_ci		}
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
1328c2ecf20Sopenharmony_ci					   &meson8_ddr_clk_hw_onecell_data);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic const struct of_device_id meson8_ddr_clkc_match_table[] = {
1368c2ecf20Sopenharmony_ci	{ .compatible = "amlogic,meson8-ddr-clkc" },
1378c2ecf20Sopenharmony_ci	{ .compatible = "amlogic,meson8b-ddr-clkc" },
1388c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic struct platform_driver meson8_ddr_clkc_driver = {
1428c2ecf20Sopenharmony_ci	.probe		= meson8_ddr_clkc_probe,
1438c2ecf20Sopenharmony_ci	.driver		= {
1448c2ecf20Sopenharmony_ci		.name	= "meson8-ddr-clkc",
1458c2ecf20Sopenharmony_ci		.of_match_table = meson8_ddr_clkc_match_table,
1468c2ecf20Sopenharmony_ci	},
1478c2ecf20Sopenharmony_ci};
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cibuiltin_platform_driver(meson8_ddr_clkc_driver);
150