162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MMP Audio Clock Controller driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/pm_clock.h> 1362306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <dt-bindings/clock/marvell,mmp2-audio.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* Audio Controller Registers */ 1862306a36Sopenharmony_ci#define SSPA_AUD_CTRL 0x04 1962306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0 0x08 2062306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1 0x0c 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* SSPA Audio Control Register */ 2362306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SYSCLK_SHIFT 0 2462306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SYSCLK_DIV_SHIFT 1 2562306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA0_MUX_SHIFT 7 2662306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA0_SHIFT 8 2762306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA0_DIV_SHIFT 9 2862306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA1_SHIFT 16 2962306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA1_DIV_SHIFT 17 3062306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA1_MUX_SHIFT 23 3162306a36Sopenharmony_ci#define SSPA_AUD_CTRL_DIV_MASK 0x7e 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* SSPA Audio PLL Control 0 Register */ 3462306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO_MASK (0x7 << 28) 3562306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(x) ((x) << 28) 3662306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_FRACT_MASK (0xfffff << 8) 3762306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_FRACT(x) ((x) << 8) 3862306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ENA_DITHER (1 << 7) 3962306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ICP_2UA (0 << 5) 4062306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ICP_5UA (1 << 5) 4162306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ICP_7UA (2 << 5) 4262306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ICP_10UA (3 << 5) 4362306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_FBCCLK_MASK (0x3 << 3) 4462306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(x) ((x) << 3) 4562306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_MCLK_MASK (0x1 << 2) 4662306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_MCLK(x) ((x) << 2) 4762306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_PD_OVPROT_DIS (1 << 1) 4862306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_PU (1 << 0) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* SSPA Audio PLL Control 1 Register */ 5162306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_SEL_FAST_CLK (1 << 24) 5262306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_CLK_SEL_MASK (1 << 11) 5362306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL (1 << 11) 5462306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_CLK_SEL_VCXO (0 << 11) 5562306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN_MASK (0x7ff << 0) 5662306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(x) ((x) << 0) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define CLK_AUDIO_NR_CLKS 3 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct mmp2_audio_clk { 6162306a36Sopenharmony_ci void __iomem *mmio_base; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci struct clk_hw audio_pll_hw; 6462306a36Sopenharmony_ci struct clk_mux sspa_mux; 6562306a36Sopenharmony_ci struct clk_mux sspa1_mux; 6662306a36Sopenharmony_ci struct clk_divider sysclk_div; 6762306a36Sopenharmony_ci struct clk_divider sspa0_div; 6862306a36Sopenharmony_ci struct clk_divider sspa1_div; 6962306a36Sopenharmony_ci struct clk_gate sysclk_gate; 7062306a36Sopenharmony_ci struct clk_gate sspa0_gate; 7162306a36Sopenharmony_ci struct clk_gate sspa1_gate; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci u32 aud_ctrl; 7462306a36Sopenharmony_ci u32 aud_pll_ctrl0; 7562306a36Sopenharmony_ci u32 aud_pll_ctrl1; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci spinlock_t lock; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Must be last */ 8062306a36Sopenharmony_ci struct clk_hw_onecell_data clk_data; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct { 8462306a36Sopenharmony_ci unsigned long parent_rate; 8562306a36Sopenharmony_ci unsigned long freq_vco; 8662306a36Sopenharmony_ci unsigned char mclk; 8762306a36Sopenharmony_ci unsigned char fbcclk; 8862306a36Sopenharmony_ci unsigned short fract; 8962306a36Sopenharmony_ci} predivs[] = { 9062306a36Sopenharmony_ci { 26000000, 135475200, 0, 0, 0x8a18 }, 9162306a36Sopenharmony_ci { 26000000, 147456000, 0, 1, 0x0da1 }, 9262306a36Sopenharmony_ci { 38400000, 135475200, 1, 2, 0x8208 }, 9362306a36Sopenharmony_ci { 38400000, 147456000, 1, 3, 0xaaaa }, 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct { 9762306a36Sopenharmony_ci unsigned char divisor; 9862306a36Sopenharmony_ci unsigned char modulo; 9962306a36Sopenharmony_ci unsigned char pattern; 10062306a36Sopenharmony_ci} postdivs[] = { 10162306a36Sopenharmony_ci { 1, 3, 0, }, 10262306a36Sopenharmony_ci { 2, 5, 0, }, 10362306a36Sopenharmony_ci { 4, 0, 0, }, 10462306a36Sopenharmony_ci { 6, 1, 1, }, 10562306a36Sopenharmony_ci { 8, 1, 0, }, 10662306a36Sopenharmony_ci { 9, 1, 2, }, 10762306a36Sopenharmony_ci { 12, 2, 1, }, 10862306a36Sopenharmony_ci { 16, 2, 0, }, 10962306a36Sopenharmony_ci { 18, 2, 2, }, 11062306a36Sopenharmony_ci { 24, 4, 1, }, 11162306a36Sopenharmony_ci { 36, 4, 2, }, 11262306a36Sopenharmony_ci { 48, 6, 1, }, 11362306a36Sopenharmony_ci { 72, 6, 2, }, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic unsigned long audio_pll_recalc_rate(struct clk_hw *hw, 11762306a36Sopenharmony_ci unsigned long parent_rate) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct mmp2_audio_clk *priv = container_of(hw, struct mmp2_audio_clk, audio_pll_hw); 12062306a36Sopenharmony_ci unsigned int prediv; 12162306a36Sopenharmony_ci unsigned int postdiv; 12262306a36Sopenharmony_ci u32 aud_pll_ctrl0; 12362306a36Sopenharmony_ci u32 aud_pll_ctrl1; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci aud_pll_ctrl0 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL0); 12662306a36Sopenharmony_ci aud_pll_ctrl0 &= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO_MASK | 12762306a36Sopenharmony_ci SSPA_AUD_PLL_CTRL0_FRACT_MASK | 12862306a36Sopenharmony_ci SSPA_AUD_PLL_CTRL0_ENA_DITHER | 12962306a36Sopenharmony_ci SSPA_AUD_PLL_CTRL0_DIV_FBCCLK_MASK | 13062306a36Sopenharmony_ci SSPA_AUD_PLL_CTRL0_DIV_MCLK_MASK | 13162306a36Sopenharmony_ci SSPA_AUD_PLL_CTRL0_PU; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci aud_pll_ctrl1 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL1); 13462306a36Sopenharmony_ci aud_pll_ctrl1 &= SSPA_AUD_PLL_CTRL1_CLK_SEL_MASK | 13562306a36Sopenharmony_ci SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN_MASK; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) { 13862306a36Sopenharmony_ci if (predivs[prediv].parent_rate != parent_rate) 13962306a36Sopenharmony_ci continue; 14062306a36Sopenharmony_ci for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) { 14162306a36Sopenharmony_ci unsigned long freq; 14262306a36Sopenharmony_ci u32 val; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci val = SSPA_AUD_PLL_CTRL0_ENA_DITHER; 14562306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_PU; 14662306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(postdivs[postdiv].modulo); 14762306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_FRACT(predivs[prediv].fract); 14862306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(predivs[prediv].fbcclk); 14962306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_DIV_MCLK(predivs[prediv].mclk); 15062306a36Sopenharmony_ci if (val != aud_pll_ctrl0) 15162306a36Sopenharmony_ci continue; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci val = SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL; 15462306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(postdivs[postdiv].pattern); 15562306a36Sopenharmony_ci if (val != aud_pll_ctrl1) 15662306a36Sopenharmony_ci continue; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci freq = predivs[prediv].freq_vco; 15962306a36Sopenharmony_ci freq /= postdivs[postdiv].divisor; 16062306a36Sopenharmony_ci return freq; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic long audio_pll_round_rate(struct clk_hw *hw, unsigned long rate, 16862306a36Sopenharmony_ci unsigned long *parent_rate) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci unsigned int prediv; 17162306a36Sopenharmony_ci unsigned int postdiv; 17262306a36Sopenharmony_ci long rounded = 0; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) { 17562306a36Sopenharmony_ci if (predivs[prediv].parent_rate != *parent_rate) 17662306a36Sopenharmony_ci continue; 17762306a36Sopenharmony_ci for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) { 17862306a36Sopenharmony_ci long freq = predivs[prediv].freq_vco; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci freq /= postdivs[postdiv].divisor; 18162306a36Sopenharmony_ci if (freq == rate) 18262306a36Sopenharmony_ci return rate; 18362306a36Sopenharmony_ci if (freq < rate) 18462306a36Sopenharmony_ci continue; 18562306a36Sopenharmony_ci if (rounded && freq > rounded) 18662306a36Sopenharmony_ci continue; 18762306a36Sopenharmony_ci rounded = freq; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return rounded; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int audio_pll_set_rate(struct clk_hw *hw, unsigned long rate, 19562306a36Sopenharmony_ci unsigned long parent_rate) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct mmp2_audio_clk *priv = container_of(hw, struct mmp2_audio_clk, audio_pll_hw); 19862306a36Sopenharmony_ci unsigned int prediv; 19962306a36Sopenharmony_ci unsigned int postdiv; 20062306a36Sopenharmony_ci unsigned long val; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) { 20362306a36Sopenharmony_ci if (predivs[prediv].parent_rate != parent_rate) 20462306a36Sopenharmony_ci continue; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) { 20762306a36Sopenharmony_ci if (rate * postdivs[postdiv].divisor != predivs[prediv].freq_vco) 20862306a36Sopenharmony_ci continue; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci val = SSPA_AUD_PLL_CTRL0_ENA_DITHER; 21162306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_PU; 21262306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(postdivs[postdiv].modulo); 21362306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_FRACT(predivs[prediv].fract); 21462306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(predivs[prediv].fbcclk); 21562306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL0_DIV_MCLK(predivs[prediv].mclk); 21662306a36Sopenharmony_ci writel(val, priv->mmio_base + SSPA_AUD_PLL_CTRL0); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci val = SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL; 21962306a36Sopenharmony_ci val |= SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(postdivs[postdiv].pattern); 22062306a36Sopenharmony_ci writel(val, priv->mmio_base + SSPA_AUD_PLL_CTRL1); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return -ERANGE; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic const struct clk_ops audio_pll_ops = { 23062306a36Sopenharmony_ci .recalc_rate = audio_pll_recalc_rate, 23162306a36Sopenharmony_ci .round_rate = audio_pll_round_rate, 23262306a36Sopenharmony_ci .set_rate = audio_pll_set_rate, 23362306a36Sopenharmony_ci}; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int register_clocks(struct mmp2_audio_clk *priv, struct device *dev) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci const struct clk_parent_data sspa_mux_parents[] = { 23862306a36Sopenharmony_ci { .hw = &priv->audio_pll_hw }, 23962306a36Sopenharmony_ci { .fw_name = "i2s0" }, 24062306a36Sopenharmony_ci }; 24162306a36Sopenharmony_ci const struct clk_parent_data sspa1_mux_parents[] = { 24262306a36Sopenharmony_ci { .hw = &priv->audio_pll_hw }, 24362306a36Sopenharmony_ci { .fw_name = "i2s1" }, 24462306a36Sopenharmony_ci }; 24562306a36Sopenharmony_ci int ret; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci priv->audio_pll_hw.init = CLK_HW_INIT_FW_NAME("audio_pll", 24862306a36Sopenharmony_ci "vctcxo", &audio_pll_ops, 24962306a36Sopenharmony_ci CLK_SET_RATE_PARENT); 25062306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &priv->audio_pll_hw); 25162306a36Sopenharmony_ci if (ret) 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci priv->sspa_mux.hw.init = CLK_HW_INIT_PARENTS_DATA("sspa_mux", 25562306a36Sopenharmony_ci sspa_mux_parents, &clk_mux_ops, 25662306a36Sopenharmony_ci CLK_SET_RATE_PARENT); 25762306a36Sopenharmony_ci priv->sspa_mux.reg = priv->mmio_base + SSPA_AUD_CTRL; 25862306a36Sopenharmony_ci priv->sspa_mux.mask = 1; 25962306a36Sopenharmony_ci priv->sspa_mux.shift = SSPA_AUD_CTRL_SSPA0_MUX_SHIFT; 26062306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &priv->sspa_mux.hw); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci priv->sysclk_div.hw.init = CLK_HW_INIT_HW("sys_div", 26562306a36Sopenharmony_ci &priv->sspa_mux.hw, &clk_divider_ops, 26662306a36Sopenharmony_ci CLK_SET_RATE_PARENT); 26762306a36Sopenharmony_ci priv->sysclk_div.reg = priv->mmio_base + SSPA_AUD_CTRL; 26862306a36Sopenharmony_ci priv->sysclk_div.shift = SSPA_AUD_CTRL_SYSCLK_DIV_SHIFT; 26962306a36Sopenharmony_ci priv->sysclk_div.width = 6; 27062306a36Sopenharmony_ci priv->sysclk_div.flags = CLK_DIVIDER_ONE_BASED; 27162306a36Sopenharmony_ci priv->sysclk_div.flags |= CLK_DIVIDER_ROUND_CLOSEST; 27262306a36Sopenharmony_ci priv->sysclk_div.flags |= CLK_DIVIDER_ALLOW_ZERO; 27362306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &priv->sysclk_div.hw); 27462306a36Sopenharmony_ci if (ret) 27562306a36Sopenharmony_ci return ret; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci priv->sysclk_gate.hw.init = CLK_HW_INIT_HW("sys_clk", 27862306a36Sopenharmony_ci &priv->sysclk_div.hw, &clk_gate_ops, 27962306a36Sopenharmony_ci CLK_SET_RATE_PARENT); 28062306a36Sopenharmony_ci priv->sysclk_gate.reg = priv->mmio_base + SSPA_AUD_CTRL; 28162306a36Sopenharmony_ci priv->sysclk_gate.bit_idx = SSPA_AUD_CTRL_SYSCLK_SHIFT; 28262306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &priv->sysclk_gate.hw); 28362306a36Sopenharmony_ci if (ret) 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci priv->sspa0_div.hw.init = CLK_HW_INIT_HW("sspa0_div", 28762306a36Sopenharmony_ci &priv->sspa_mux.hw, &clk_divider_ops, 0); 28862306a36Sopenharmony_ci priv->sspa0_div.reg = priv->mmio_base + SSPA_AUD_CTRL; 28962306a36Sopenharmony_ci priv->sspa0_div.shift = SSPA_AUD_CTRL_SSPA0_DIV_SHIFT; 29062306a36Sopenharmony_ci priv->sspa0_div.width = 6; 29162306a36Sopenharmony_ci priv->sspa0_div.flags = CLK_DIVIDER_ONE_BASED; 29262306a36Sopenharmony_ci priv->sspa0_div.flags |= CLK_DIVIDER_ROUND_CLOSEST; 29362306a36Sopenharmony_ci priv->sspa0_div.flags |= CLK_DIVIDER_ALLOW_ZERO; 29462306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &priv->sspa0_div.hw); 29562306a36Sopenharmony_ci if (ret) 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci priv->sspa0_gate.hw.init = CLK_HW_INIT_HW("sspa0_clk", 29962306a36Sopenharmony_ci &priv->sspa0_div.hw, &clk_gate_ops, 30062306a36Sopenharmony_ci CLK_SET_RATE_PARENT); 30162306a36Sopenharmony_ci priv->sspa0_gate.reg = priv->mmio_base + SSPA_AUD_CTRL; 30262306a36Sopenharmony_ci priv->sspa0_gate.bit_idx = SSPA_AUD_CTRL_SSPA0_SHIFT; 30362306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &priv->sspa0_gate.hw); 30462306a36Sopenharmony_ci if (ret) 30562306a36Sopenharmony_ci return ret; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci priv->sspa1_mux.hw.init = CLK_HW_INIT_PARENTS_DATA("sspa1_mux", 30862306a36Sopenharmony_ci sspa1_mux_parents, &clk_mux_ops, 30962306a36Sopenharmony_ci CLK_SET_RATE_PARENT); 31062306a36Sopenharmony_ci priv->sspa1_mux.reg = priv->mmio_base + SSPA_AUD_CTRL; 31162306a36Sopenharmony_ci priv->sspa1_mux.mask = 1; 31262306a36Sopenharmony_ci priv->sspa1_mux.shift = SSPA_AUD_CTRL_SSPA1_MUX_SHIFT; 31362306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &priv->sspa1_mux.hw); 31462306a36Sopenharmony_ci if (ret) 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci priv->sspa1_div.hw.init = CLK_HW_INIT_HW("sspa1_div", 31862306a36Sopenharmony_ci &priv->sspa1_mux.hw, &clk_divider_ops, 0); 31962306a36Sopenharmony_ci priv->sspa1_div.reg = priv->mmio_base + SSPA_AUD_CTRL; 32062306a36Sopenharmony_ci priv->sspa1_div.shift = SSPA_AUD_CTRL_SSPA1_DIV_SHIFT; 32162306a36Sopenharmony_ci priv->sspa1_div.width = 6; 32262306a36Sopenharmony_ci priv->sspa1_div.flags = CLK_DIVIDER_ONE_BASED; 32362306a36Sopenharmony_ci priv->sspa1_div.flags |= CLK_DIVIDER_ROUND_CLOSEST; 32462306a36Sopenharmony_ci priv->sspa1_div.flags |= CLK_DIVIDER_ALLOW_ZERO; 32562306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &priv->sspa1_div.hw); 32662306a36Sopenharmony_ci if (ret) 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci priv->sspa1_gate.hw.init = CLK_HW_INIT_HW("sspa1_clk", 33062306a36Sopenharmony_ci &priv->sspa1_div.hw, &clk_gate_ops, 33162306a36Sopenharmony_ci CLK_SET_RATE_PARENT); 33262306a36Sopenharmony_ci priv->sspa1_gate.reg = priv->mmio_base + SSPA_AUD_CTRL; 33362306a36Sopenharmony_ci priv->sspa1_gate.bit_idx = SSPA_AUD_CTRL_SSPA1_SHIFT; 33462306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &priv->sspa1_gate.hw); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci priv->clk_data.hws[MMP2_CLK_AUDIO_SYSCLK] = &priv->sysclk_gate.hw; 33962306a36Sopenharmony_ci priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA0] = &priv->sspa0_gate.hw; 34062306a36Sopenharmony_ci priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA1] = &priv->sspa1_gate.hw; 34162306a36Sopenharmony_ci priv->clk_data.num = CLK_AUDIO_NR_CLKS; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, 34462306a36Sopenharmony_ci &priv->clk_data); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int mmp2_audio_clk_probe(struct platform_device *pdev) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct mmp2_audio_clk *priv; 35062306a36Sopenharmony_ci int ret; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, 35362306a36Sopenharmony_ci struct_size(priv, clk_data.hws, 35462306a36Sopenharmony_ci CLK_AUDIO_NR_CLKS), 35562306a36Sopenharmony_ci GFP_KERNEL); 35662306a36Sopenharmony_ci if (!priv) 35762306a36Sopenharmony_ci return -ENOMEM; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci spin_lock_init(&priv->lock); 36062306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci priv->mmio_base = devm_platform_ioremap_resource(pdev, 0); 36362306a36Sopenharmony_ci if (IS_ERR(priv->mmio_base)) 36462306a36Sopenharmony_ci return PTR_ERR(priv->mmio_base); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 36762306a36Sopenharmony_ci ret = pm_clk_create(&pdev->dev); 36862306a36Sopenharmony_ci if (ret) 36962306a36Sopenharmony_ci goto disable_pm_runtime; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = pm_clk_add(&pdev->dev, "audio"); 37262306a36Sopenharmony_ci if (ret) 37362306a36Sopenharmony_ci goto destroy_pm_clk; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ret = register_clocks(priv, &pdev->dev); 37662306a36Sopenharmony_ci if (ret) 37762306a36Sopenharmony_ci goto destroy_pm_clk; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cidestroy_pm_clk: 38262306a36Sopenharmony_ci pm_clk_destroy(&pdev->dev); 38362306a36Sopenharmony_cidisable_pm_runtime: 38462306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void mmp2_audio_clk_remove(struct platform_device *pdev) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci pm_clk_destroy(&pdev->dev); 39262306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci#ifdef CONFIG_PM 39662306a36Sopenharmony_cistatic int mmp2_audio_clk_suspend(struct device *dev) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct mmp2_audio_clk *priv = dev_get_drvdata(dev); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci priv->aud_ctrl = readl(priv->mmio_base + SSPA_AUD_CTRL); 40162306a36Sopenharmony_ci priv->aud_pll_ctrl0 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL0); 40262306a36Sopenharmony_ci priv->aud_pll_ctrl1 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL1); 40362306a36Sopenharmony_ci pm_clk_suspend(dev); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int mmp2_audio_clk_resume(struct device *dev) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct mmp2_audio_clk *priv = dev_get_drvdata(dev); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci pm_clk_resume(dev); 41362306a36Sopenharmony_ci writel(priv->aud_ctrl, priv->mmio_base + SSPA_AUD_CTRL); 41462306a36Sopenharmony_ci writel(priv->aud_pll_ctrl0, priv->mmio_base + SSPA_AUD_PLL_CTRL0); 41562306a36Sopenharmony_ci writel(priv->aud_pll_ctrl1, priv->mmio_base + SSPA_AUD_PLL_CTRL1); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci#endif 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic const struct dev_pm_ops mmp2_audio_clk_pm_ops = { 42262306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(mmp2_audio_clk_suspend, mmp2_audio_clk_resume, NULL) 42362306a36Sopenharmony_ci}; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic const struct of_device_id mmp2_audio_clk_of_match[] = { 42662306a36Sopenharmony_ci { .compatible = "marvell,mmp2-audio-clock" }, 42762306a36Sopenharmony_ci {} 42862306a36Sopenharmony_ci}; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mmp2_audio_clk_of_match); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic struct platform_driver mmp2_audio_clk_driver = { 43362306a36Sopenharmony_ci .driver = { 43462306a36Sopenharmony_ci .name = "mmp2-audio-clock", 43562306a36Sopenharmony_ci .of_match_table = of_match_ptr(mmp2_audio_clk_of_match), 43662306a36Sopenharmony_ci .pm = &mmp2_audio_clk_pm_ops, 43762306a36Sopenharmony_ci }, 43862306a36Sopenharmony_ci .probe = mmp2_audio_clk_probe, 43962306a36Sopenharmony_ci .remove_new = mmp2_audio_clk_remove, 44062306a36Sopenharmony_ci}; 44162306a36Sopenharmony_cimodule_platform_driver(mmp2_audio_clk_driver); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ciMODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); 44462306a36Sopenharmony_ciMODULE_DESCRIPTION("Clock driver for MMP2 Audio subsystem"); 44562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 446