Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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);