cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-audio.c (13082B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * MMP Audio Clock Controller driver
      4 *
      5 * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
      6 */
      7
      8#include <linux/clk-provider.h>
      9#include <linux/io.h>
     10#include <linux/module.h>
     11#include <linux/platform_device.h>
     12#include <linux/pm_clock.h>
     13#include <linux/pm_runtime.h>
     14#include <linux/slab.h>
     15#include <dt-bindings/clock/marvell,mmp2-audio.h>
     16
     17/* Audio Controller Registers */
     18#define SSPA_AUD_CTRL				0x04
     19#define SSPA_AUD_PLL_CTRL0			0x08
     20#define SSPA_AUD_PLL_CTRL1			0x0c
     21
     22/* SSPA Audio Control Register */
     23#define SSPA_AUD_CTRL_SYSCLK_SHIFT		0
     24#define SSPA_AUD_CTRL_SYSCLK_DIV_SHIFT		1
     25#define SSPA_AUD_CTRL_SSPA0_MUX_SHIFT		7
     26#define SSPA_AUD_CTRL_SSPA0_SHIFT		8
     27#define SSPA_AUD_CTRL_SSPA0_DIV_SHIFT		9
     28#define SSPA_AUD_CTRL_SSPA1_SHIFT		16
     29#define SSPA_AUD_CTRL_SSPA1_DIV_SHIFT		17
     30#define SSPA_AUD_CTRL_SSPA1_MUX_SHIFT		23
     31#define SSPA_AUD_CTRL_DIV_MASK			0x7e
     32
     33/* SSPA Audio PLL Control 0 Register */
     34#define SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO_MASK (0x7 << 28)
     35#define SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(x)	((x) << 28)
     36#define SSPA_AUD_PLL_CTRL0_FRACT_MASK		(0xfffff << 8)
     37#define SSPA_AUD_PLL_CTRL0_FRACT(x)		((x) << 8)
     38#define SSPA_AUD_PLL_CTRL0_ENA_DITHER		(1 << 7)
     39#define SSPA_AUD_PLL_CTRL0_ICP_2UA		(0 << 5)
     40#define SSPA_AUD_PLL_CTRL0_ICP_5UA		(1 << 5)
     41#define SSPA_AUD_PLL_CTRL0_ICP_7UA		(2 << 5)
     42#define SSPA_AUD_PLL_CTRL0_ICP_10UA		(3 << 5)
     43#define SSPA_AUD_PLL_CTRL0_DIV_FBCCLK_MASK	(0x3 << 3)
     44#define SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(x)	((x) << 3)
     45#define SSPA_AUD_PLL_CTRL0_DIV_MCLK_MASK	(0x1 << 2)
     46#define SSPA_AUD_PLL_CTRL0_DIV_MCLK(x)		((x) << 2)
     47#define SSPA_AUD_PLL_CTRL0_PD_OVPROT_DIS	(1 << 1)
     48#define SSPA_AUD_PLL_CTRL0_PU			(1 << 0)
     49
     50/* SSPA Audio PLL Control 1 Register */
     51#define SSPA_AUD_PLL_CTRL1_SEL_FAST_CLK		(1 << 24)
     52#define SSPA_AUD_PLL_CTRL1_CLK_SEL_MASK		(1 << 11)
     53#define SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL	(1 << 11)
     54#define SSPA_AUD_PLL_CTRL1_CLK_SEL_VCXO		(0 << 11)
     55#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN_MASK (0x7ff << 0)
     56#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(x)	((x) << 0)
     57
     58struct mmp2_audio_clk {
     59	void __iomem *mmio_base;
     60
     61	struct clk_hw audio_pll_hw;
     62	struct clk_mux sspa_mux;
     63	struct clk_mux sspa1_mux;
     64	struct clk_divider sysclk_div;
     65	struct clk_divider sspa0_div;
     66	struct clk_divider sspa1_div;
     67	struct clk_gate sysclk_gate;
     68	struct clk_gate sspa0_gate;
     69	struct clk_gate sspa1_gate;
     70
     71	u32 aud_ctrl;
     72	u32 aud_pll_ctrl0;
     73	u32 aud_pll_ctrl1;
     74
     75	spinlock_t lock;
     76
     77	/* Must be last */
     78	struct clk_hw_onecell_data clk_data;
     79};
     80
     81static const struct {
     82	unsigned long parent_rate;
     83	unsigned long freq_vco;
     84	unsigned char mclk;
     85	unsigned char fbcclk;
     86	unsigned short fract;
     87} predivs[] = {
     88	{ 26000000, 135475200, 0, 0, 0x8a18 },
     89	{ 26000000, 147456000, 0, 1, 0x0da1 },
     90	{ 38400000, 135475200, 1, 2, 0x8208 },
     91	{ 38400000, 147456000, 1, 3, 0xaaaa },
     92};
     93
     94static const struct {
     95	unsigned char divisor;
     96	unsigned char modulo;
     97	unsigned char pattern;
     98} postdivs[] = {
     99	{   1,	3,  0, },
    100	{   2,	5,  0, },
    101	{   4,	0,  0, },
    102	{   6,	1,  1, },
    103	{   8,	1,  0, },
    104	{   9,	1,  2, },
    105	{  12,	2,  1, },
    106	{  16,	2,  0, },
    107	{  18,	2,  2, },
    108	{  24,	4,  1, },
    109	{  36,	4,  2, },
    110	{  48,	6,  1, },
    111	{  72,	6,  2, },
    112};
    113
    114static unsigned long audio_pll_recalc_rate(struct clk_hw *hw,
    115					   unsigned long parent_rate)
    116{
    117	struct mmp2_audio_clk *priv = container_of(hw, struct mmp2_audio_clk, audio_pll_hw);
    118	unsigned int prediv;
    119	unsigned int postdiv;
    120	u32 aud_pll_ctrl0;
    121	u32 aud_pll_ctrl1;
    122
    123	aud_pll_ctrl0 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL0);
    124	aud_pll_ctrl0 &= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO_MASK |
    125			 SSPA_AUD_PLL_CTRL0_FRACT_MASK |
    126			 SSPA_AUD_PLL_CTRL0_ENA_DITHER |
    127			 SSPA_AUD_PLL_CTRL0_DIV_FBCCLK_MASK |
    128			 SSPA_AUD_PLL_CTRL0_DIV_MCLK_MASK |
    129			 SSPA_AUD_PLL_CTRL0_PU;
    130
    131	aud_pll_ctrl1 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL1);
    132	aud_pll_ctrl1 &= SSPA_AUD_PLL_CTRL1_CLK_SEL_MASK |
    133			 SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN_MASK;
    134
    135	for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) {
    136		if (predivs[prediv].parent_rate != parent_rate)
    137			continue;
    138		for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) {
    139			unsigned long freq;
    140			u32 val;
    141
    142			val = SSPA_AUD_PLL_CTRL0_ENA_DITHER;
    143			val |= SSPA_AUD_PLL_CTRL0_PU;
    144			val |= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(postdivs[postdiv].modulo);
    145			val |= SSPA_AUD_PLL_CTRL0_FRACT(predivs[prediv].fract);
    146			val |= SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(predivs[prediv].fbcclk);
    147			val |= SSPA_AUD_PLL_CTRL0_DIV_MCLK(predivs[prediv].mclk);
    148			if (val != aud_pll_ctrl0)
    149				continue;
    150
    151			val = SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL;
    152			val |= SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(postdivs[postdiv].pattern);
    153			if (val != aud_pll_ctrl1)
    154				continue;
    155
    156			freq = predivs[prediv].freq_vco;
    157			freq /= postdivs[postdiv].divisor;
    158			return freq;
    159		}
    160	}
    161
    162	return 0;
    163}
    164
    165static long audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
    166				 unsigned long *parent_rate)
    167{
    168	unsigned int prediv;
    169	unsigned int postdiv;
    170	long rounded = 0;
    171
    172	for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) {
    173		if (predivs[prediv].parent_rate != *parent_rate)
    174			continue;
    175		for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) {
    176			long freq = predivs[prediv].freq_vco;
    177
    178			freq /= postdivs[postdiv].divisor;
    179			if (freq == rate)
    180				return rate;
    181			if (freq < rate)
    182				continue;
    183			if (rounded && freq > rounded)
    184				continue;
    185			rounded = freq;
    186		}
    187	}
    188
    189	return rounded;
    190}
    191
    192static int audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
    193			      unsigned long parent_rate)
    194{
    195	struct mmp2_audio_clk *priv = container_of(hw, struct mmp2_audio_clk, audio_pll_hw);
    196	unsigned int prediv;
    197	unsigned int postdiv;
    198	unsigned long val;
    199
    200	for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) {
    201		if (predivs[prediv].parent_rate != parent_rate)
    202			continue;
    203
    204		for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) {
    205			if (rate * postdivs[postdiv].divisor != predivs[prediv].freq_vco)
    206				continue;
    207
    208			val = SSPA_AUD_PLL_CTRL0_ENA_DITHER;
    209			val |= SSPA_AUD_PLL_CTRL0_PU;
    210			val |= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(postdivs[postdiv].modulo);
    211			val |= SSPA_AUD_PLL_CTRL0_FRACT(predivs[prediv].fract);
    212			val |= SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(predivs[prediv].fbcclk);
    213			val |= SSPA_AUD_PLL_CTRL0_DIV_MCLK(predivs[prediv].mclk);
    214			writel(val, priv->mmio_base + SSPA_AUD_PLL_CTRL0);
    215
    216			val = SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL;
    217			val |= SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(postdivs[postdiv].pattern);
    218			writel(val, priv->mmio_base + SSPA_AUD_PLL_CTRL1);
    219
    220			return 0;
    221		}
    222	}
    223
    224	return -ERANGE;
    225}
    226
    227static const struct clk_ops audio_pll_ops = {
    228	.recalc_rate = audio_pll_recalc_rate,
    229	.round_rate = audio_pll_round_rate,
    230	.set_rate = audio_pll_set_rate,
    231};
    232
    233static int register_clocks(struct mmp2_audio_clk *priv, struct device *dev)
    234{
    235	const struct clk_parent_data sspa_mux_parents[] = {
    236		{ .hw = &priv->audio_pll_hw },
    237		{ .fw_name = "i2s0" },
    238	};
    239	const struct clk_parent_data sspa1_mux_parents[] = {
    240		{ .hw = &priv->audio_pll_hw },
    241		{ .fw_name = "i2s1" },
    242	};
    243	int ret;
    244
    245	priv->audio_pll_hw.init = CLK_HW_INIT_FW_NAME("audio_pll",
    246				"vctcxo", &audio_pll_ops,
    247				CLK_SET_RATE_PARENT);
    248	ret = devm_clk_hw_register(dev, &priv->audio_pll_hw);
    249	if (ret)
    250		return ret;
    251
    252	priv->sspa_mux.hw.init = CLK_HW_INIT_PARENTS_DATA("sspa_mux",
    253				sspa_mux_parents, &clk_mux_ops,
    254				CLK_SET_RATE_PARENT);
    255	priv->sspa_mux.reg = priv->mmio_base + SSPA_AUD_CTRL;
    256	priv->sspa_mux.mask = 1;
    257	priv->sspa_mux.shift = SSPA_AUD_CTRL_SSPA0_MUX_SHIFT;
    258	ret = devm_clk_hw_register(dev, &priv->sspa_mux.hw);
    259	if (ret)
    260		return ret;
    261
    262	priv->sysclk_div.hw.init = CLK_HW_INIT_HW("sys_div",
    263				&priv->sspa_mux.hw, &clk_divider_ops,
    264				CLK_SET_RATE_PARENT);
    265	priv->sysclk_div.reg = priv->mmio_base + SSPA_AUD_CTRL;
    266	priv->sysclk_div.shift = SSPA_AUD_CTRL_SYSCLK_DIV_SHIFT;
    267	priv->sysclk_div.width = 6;
    268	priv->sysclk_div.flags = CLK_DIVIDER_ONE_BASED;
    269	priv->sysclk_div.flags |= CLK_DIVIDER_ROUND_CLOSEST;
    270	priv->sysclk_div.flags |= CLK_DIVIDER_ALLOW_ZERO;
    271	ret = devm_clk_hw_register(dev, &priv->sysclk_div.hw);
    272	if (ret)
    273		return ret;
    274
    275	priv->sysclk_gate.hw.init = CLK_HW_INIT_HW("sys_clk",
    276				&priv->sysclk_div.hw, &clk_gate_ops,
    277				CLK_SET_RATE_PARENT);
    278	priv->sysclk_gate.reg = priv->mmio_base + SSPA_AUD_CTRL;
    279	priv->sysclk_gate.bit_idx = SSPA_AUD_CTRL_SYSCLK_SHIFT;
    280	ret = devm_clk_hw_register(dev, &priv->sysclk_gate.hw);
    281	if (ret)
    282		return ret;
    283
    284	priv->sspa0_div.hw.init = CLK_HW_INIT_HW("sspa0_div",
    285				&priv->sspa_mux.hw, &clk_divider_ops, 0);
    286	priv->sspa0_div.reg = priv->mmio_base + SSPA_AUD_CTRL;
    287	priv->sspa0_div.shift = SSPA_AUD_CTRL_SSPA0_DIV_SHIFT;
    288	priv->sspa0_div.width = 6;
    289	priv->sspa0_div.flags = CLK_DIVIDER_ONE_BASED;
    290	priv->sspa0_div.flags |= CLK_DIVIDER_ROUND_CLOSEST;
    291	priv->sspa0_div.flags |= CLK_DIVIDER_ALLOW_ZERO;
    292	ret = devm_clk_hw_register(dev, &priv->sspa0_div.hw);
    293	if (ret)
    294		return ret;
    295
    296	priv->sspa0_gate.hw.init = CLK_HW_INIT_HW("sspa0_clk",
    297				&priv->sspa0_div.hw, &clk_gate_ops,
    298				CLK_SET_RATE_PARENT);
    299	priv->sspa0_gate.reg = priv->mmio_base + SSPA_AUD_CTRL;
    300	priv->sspa0_gate.bit_idx = SSPA_AUD_CTRL_SSPA0_SHIFT;
    301	ret = devm_clk_hw_register(dev, &priv->sspa0_gate.hw);
    302	if (ret)
    303		return ret;
    304
    305	priv->sspa1_mux.hw.init = CLK_HW_INIT_PARENTS_DATA("sspa1_mux",
    306				sspa1_mux_parents, &clk_mux_ops,
    307				CLK_SET_RATE_PARENT);
    308	priv->sspa1_mux.reg = priv->mmio_base + SSPA_AUD_CTRL;
    309	priv->sspa1_mux.mask = 1;
    310	priv->sspa1_mux.shift = SSPA_AUD_CTRL_SSPA1_MUX_SHIFT;
    311	ret = devm_clk_hw_register(dev, &priv->sspa1_mux.hw);
    312	if (ret)
    313		return ret;
    314
    315	priv->sspa1_div.hw.init = CLK_HW_INIT_HW("sspa1_div",
    316				&priv->sspa1_mux.hw, &clk_divider_ops, 0);
    317	priv->sspa1_div.reg = priv->mmio_base + SSPA_AUD_CTRL;
    318	priv->sspa1_div.shift = SSPA_AUD_CTRL_SSPA1_DIV_SHIFT;
    319	priv->sspa1_div.width = 6;
    320	priv->sspa1_div.flags = CLK_DIVIDER_ONE_BASED;
    321	priv->sspa1_div.flags |= CLK_DIVIDER_ROUND_CLOSEST;
    322	priv->sspa1_div.flags |= CLK_DIVIDER_ALLOW_ZERO;
    323	ret = devm_clk_hw_register(dev, &priv->sspa1_div.hw);
    324	if (ret)
    325		return ret;
    326
    327	priv->sspa1_gate.hw.init = CLK_HW_INIT_HW("sspa1_clk",
    328				&priv->sspa1_div.hw, &clk_gate_ops,
    329				CLK_SET_RATE_PARENT);
    330	priv->sspa1_gate.reg = priv->mmio_base + SSPA_AUD_CTRL;
    331	priv->sspa1_gate.bit_idx = SSPA_AUD_CTRL_SSPA1_SHIFT;
    332	ret = devm_clk_hw_register(dev, &priv->sspa1_gate.hw);
    333	if (ret)
    334		return ret;
    335
    336	priv->clk_data.hws[MMP2_CLK_AUDIO_SYSCLK] = &priv->sysclk_gate.hw;
    337	priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA0] = &priv->sspa0_gate.hw;
    338	priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA1] = &priv->sspa1_gate.hw;
    339	priv->clk_data.num = MMP2_CLK_AUDIO_NR_CLKS;
    340
    341	return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
    342				      &priv->clk_data);
    343}
    344
    345static int mmp2_audio_clk_probe(struct platform_device *pdev)
    346{
    347	struct mmp2_audio_clk *priv;
    348	int ret;
    349
    350	priv = devm_kzalloc(&pdev->dev,
    351			    struct_size(priv, clk_data.hws,
    352					MMP2_CLK_AUDIO_NR_CLKS),
    353			    GFP_KERNEL);
    354	if (!priv)
    355		return -ENOMEM;
    356
    357	spin_lock_init(&priv->lock);
    358	platform_set_drvdata(pdev, priv);
    359
    360	priv->mmio_base = devm_platform_ioremap_resource(pdev, 0);
    361	if (IS_ERR(priv->mmio_base))
    362		return PTR_ERR(priv->mmio_base);
    363
    364	pm_runtime_enable(&pdev->dev);
    365	ret = pm_clk_create(&pdev->dev);
    366	if (ret)
    367		goto disable_pm_runtime;
    368
    369	ret = pm_clk_add(&pdev->dev, "audio");
    370	if (ret)
    371		goto destroy_pm_clk;
    372
    373	ret = register_clocks(priv, &pdev->dev);
    374	if (ret)
    375		goto destroy_pm_clk;
    376
    377	return 0;
    378
    379destroy_pm_clk:
    380	pm_clk_destroy(&pdev->dev);
    381disable_pm_runtime:
    382	pm_runtime_disable(&pdev->dev);
    383
    384	return ret;
    385}
    386
    387static int mmp2_audio_clk_remove(struct platform_device *pdev)
    388{
    389	pm_clk_destroy(&pdev->dev);
    390	pm_runtime_disable(&pdev->dev);
    391
    392	return 0;
    393}
    394
    395#ifdef CONFIG_PM
    396static int mmp2_audio_clk_suspend(struct device *dev)
    397{
    398	struct mmp2_audio_clk *priv = dev_get_drvdata(dev);
    399
    400	priv->aud_ctrl = readl(priv->mmio_base + SSPA_AUD_CTRL);
    401	priv->aud_pll_ctrl0 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL0);
    402	priv->aud_pll_ctrl1 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL1);
    403	pm_clk_suspend(dev);
    404
    405	return 0;
    406}
    407
    408static int mmp2_audio_clk_resume(struct device *dev)
    409{
    410	struct mmp2_audio_clk *priv = dev_get_drvdata(dev);
    411
    412	pm_clk_resume(dev);
    413	writel(priv->aud_ctrl, priv->mmio_base + SSPA_AUD_CTRL);
    414	writel(priv->aud_pll_ctrl0, priv->mmio_base + SSPA_AUD_PLL_CTRL0);
    415	writel(priv->aud_pll_ctrl1, priv->mmio_base + SSPA_AUD_PLL_CTRL1);
    416
    417	return 0;
    418}
    419#endif
    420
    421static const struct dev_pm_ops mmp2_audio_clk_pm_ops = {
    422	SET_RUNTIME_PM_OPS(mmp2_audio_clk_suspend, mmp2_audio_clk_resume, NULL)
    423};
    424
    425static const struct of_device_id mmp2_audio_clk_of_match[] = {
    426	{ .compatible = "marvell,mmp2-audio-clock" },
    427	{}
    428};
    429
    430MODULE_DEVICE_TABLE(of, mmp2_audio_clk_of_match);
    431
    432static struct platform_driver mmp2_audio_clk_driver = {
    433	.driver = {
    434		.name = "mmp2-audio-clock",
    435		.of_match_table = of_match_ptr(mmp2_audio_clk_of_match),
    436		.pm = &mmp2_audio_clk_pm_ops,
    437	},
    438	.probe = mmp2_audio_clk_probe,
    439	.remove = mmp2_audio_clk_remove,
    440};
    441module_platform_driver(mmp2_audio_clk_driver);
    442
    443MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
    444MODULE_DESCRIPTION("Clock driver for MMP2 Audio subsystem");
    445MODULE_LICENSE("GPL");