1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * StarFive JH7100 Audio Clock Driver |
4 | * |
5 | * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk> |
6 | */ |
7 | |
8 | #include <linux/bits.h> |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/device.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/module.h> |
14 | #include <linux/platform_device.h> |
15 | |
16 | #include <dt-bindings/clock/starfive-jh7100-audio.h> |
17 | |
18 | #include "clk-starfive-jh71x0.h" |
19 | |
20 | /* external clocks */ |
21 | #define JH7100_AUDCLK_AUDIO_SRC (JH7100_AUDCLK_END + 0) |
22 | #define JH7100_AUDCLK_AUDIO_12288 (JH7100_AUDCLK_END + 1) |
23 | #define JH7100_AUDCLK_DOM7AHB_BUS (JH7100_AUDCLK_END + 2) |
24 | #define JH7100_AUDCLK_I2SADC_BCLK_IOPAD (JH7100_AUDCLK_END + 3) |
25 | #define JH7100_AUDCLK_I2SADC_LRCLK_IOPAD (JH7100_AUDCLK_END + 4) |
26 | #define JH7100_AUDCLK_I2SDAC_BCLK_IOPAD (JH7100_AUDCLK_END + 5) |
27 | #define JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD (JH7100_AUDCLK_END + 6) |
28 | #define JH7100_AUDCLK_VAD_INTMEM (JH7100_AUDCLK_END + 7) |
29 | |
30 | static const struct jh71x0_clk_data jh7100_audclk_data[] = { |
31 | JH71X0__GMD(JH7100_AUDCLK_ADC_MCLK, "adc_mclk" , 0, 15, 2, |
32 | JH7100_AUDCLK_AUDIO_SRC, |
33 | JH7100_AUDCLK_AUDIO_12288), |
34 | JH71X0__GMD(JH7100_AUDCLK_I2S1_MCLK, "i2s1_mclk" , 0, 15, 2, |
35 | JH7100_AUDCLK_AUDIO_SRC, |
36 | JH7100_AUDCLK_AUDIO_12288), |
37 | JH71X0_GATE(JH7100_AUDCLK_I2SADC_APB, "i2sadc_apb" , 0, JH7100_AUDCLK_APB0_BUS), |
38 | JH71X0_MDIV(JH7100_AUDCLK_I2SADC_BCLK, "i2sadc_bclk" , 31, 2, |
39 | JH7100_AUDCLK_ADC_MCLK, |
40 | JH7100_AUDCLK_I2SADC_BCLK_IOPAD), |
41 | JH71X0__INV(JH7100_AUDCLK_I2SADC_BCLK_N, "i2sadc_bclk_n" , JH7100_AUDCLK_I2SADC_BCLK), |
42 | JH71X0_MDIV(JH7100_AUDCLK_I2SADC_LRCLK, "i2sadc_lrclk" , 63, 3, |
43 | JH7100_AUDCLK_I2SADC_BCLK_N, |
44 | JH7100_AUDCLK_I2SADC_LRCLK_IOPAD, |
45 | JH7100_AUDCLK_I2SADC_BCLK), |
46 | JH71X0_GATE(JH7100_AUDCLK_PDM_APB, "pdm_apb" , 0, JH7100_AUDCLK_APB0_BUS), |
47 | JH71X0__GMD(JH7100_AUDCLK_PDM_MCLK, "pdm_mclk" , 0, 15, 2, |
48 | JH7100_AUDCLK_AUDIO_SRC, |
49 | JH7100_AUDCLK_AUDIO_12288), |
50 | JH71X0_GATE(JH7100_AUDCLK_I2SVAD_APB, "i2svad_apb" , 0, JH7100_AUDCLK_APB0_BUS), |
51 | JH71X0__GMD(JH7100_AUDCLK_SPDIF, "spdif" , 0, 15, 2, |
52 | JH7100_AUDCLK_AUDIO_SRC, |
53 | JH7100_AUDCLK_AUDIO_12288), |
54 | JH71X0_GATE(JH7100_AUDCLK_SPDIF_APB, "spdif_apb" , 0, JH7100_AUDCLK_APB0_BUS), |
55 | JH71X0_GATE(JH7100_AUDCLK_PWMDAC_APB, "pwmdac_apb" , 0, JH7100_AUDCLK_APB0_BUS), |
56 | JH71X0__GMD(JH7100_AUDCLK_DAC_MCLK, "dac_mclk" , 0, 15, 2, |
57 | JH7100_AUDCLK_AUDIO_SRC, |
58 | JH7100_AUDCLK_AUDIO_12288), |
59 | JH71X0_GATE(JH7100_AUDCLK_I2SDAC_APB, "i2sdac_apb" , 0, JH7100_AUDCLK_APB0_BUS), |
60 | JH71X0_MDIV(JH7100_AUDCLK_I2SDAC_BCLK, "i2sdac_bclk" , 31, 2, |
61 | JH7100_AUDCLK_DAC_MCLK, |
62 | JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), |
63 | JH71X0__INV(JH7100_AUDCLK_I2SDAC_BCLK_N, "i2sdac_bclk_n" , JH7100_AUDCLK_I2SDAC_BCLK), |
64 | JH71X0_MDIV(JH7100_AUDCLK_I2SDAC_LRCLK, "i2sdac_lrclk" , 31, 2, |
65 | JH7100_AUDCLK_I2S1_MCLK, |
66 | JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), |
67 | JH71X0_GATE(JH7100_AUDCLK_I2S1_APB, "i2s1_apb" , 0, JH7100_AUDCLK_APB0_BUS), |
68 | JH71X0_MDIV(JH7100_AUDCLK_I2S1_BCLK, "i2s1_bclk" , 31, 2, |
69 | JH7100_AUDCLK_I2S1_MCLK, |
70 | JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), |
71 | JH71X0__INV(JH7100_AUDCLK_I2S1_BCLK_N, "i2s1_bclk_n" , JH7100_AUDCLK_I2S1_BCLK), |
72 | JH71X0_MDIV(JH7100_AUDCLK_I2S1_LRCLK, "i2s1_lrclk" , 63, 3, |
73 | JH7100_AUDCLK_I2S1_BCLK_N, |
74 | JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD), |
75 | JH71X0_GATE(JH7100_AUDCLK_I2SDAC16K_APB, "i2s1dac16k_apb" , 0, JH7100_AUDCLK_APB0_BUS), |
76 | JH71X0__DIV(JH7100_AUDCLK_APB0_BUS, "apb0_bus" , 8, JH7100_AUDCLK_DOM7AHB_BUS), |
77 | JH71X0_GATE(JH7100_AUDCLK_DMA1P_AHB, "dma1p_ahb" , 0, JH7100_AUDCLK_DOM7AHB_BUS), |
78 | JH71X0_GATE(JH7100_AUDCLK_USB_APB, "usb_apb" , CLK_IGNORE_UNUSED, JH7100_AUDCLK_APB_EN), |
79 | JH71X0_GDIV(JH7100_AUDCLK_USB_LPM, "usb_lpm" , CLK_IGNORE_UNUSED, 4, JH7100_AUDCLK_USB_APB), |
80 | JH71X0_GDIV(JH7100_AUDCLK_USB_STB, "usb_stb" , CLK_IGNORE_UNUSED, 3, JH7100_AUDCLK_USB_APB), |
81 | JH71X0__DIV(JH7100_AUDCLK_APB_EN, "apb_en" , 8, JH7100_AUDCLK_DOM7AHB_BUS), |
82 | JH71X0__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem" , 0, 2, |
83 | JH7100_AUDCLK_VAD_INTMEM, |
84 | JH7100_AUDCLK_AUDIO_12288), |
85 | }; |
86 | |
87 | static struct clk_hw *jh7100_audclk_get(struct of_phandle_args *clkspec, void *data) |
88 | { |
89 | struct jh71x0_clk_priv *priv = data; |
90 | unsigned int idx = clkspec->args[0]; |
91 | |
92 | if (idx < JH7100_AUDCLK_END) |
93 | return &priv->reg[idx].hw; |
94 | |
95 | return ERR_PTR(error: -EINVAL); |
96 | } |
97 | |
98 | static int jh7100_audclk_probe(struct platform_device *pdev) |
99 | { |
100 | struct jh71x0_clk_priv *priv; |
101 | unsigned int idx; |
102 | int ret; |
103 | |
104 | priv = devm_kzalloc(dev: &pdev->dev, struct_size(priv, reg, JH7100_AUDCLK_END), GFP_KERNEL); |
105 | if (!priv) |
106 | return -ENOMEM; |
107 | |
108 | spin_lock_init(&priv->rmw_lock); |
109 | priv->dev = &pdev->dev; |
110 | priv->base = devm_platform_ioremap_resource(pdev, index: 0); |
111 | if (IS_ERR(ptr: priv->base)) |
112 | return PTR_ERR(ptr: priv->base); |
113 | |
114 | for (idx = 0; idx < JH7100_AUDCLK_END; idx++) { |
115 | u32 max = jh7100_audclk_data[idx].max; |
116 | struct clk_parent_data parents[4] = {}; |
117 | struct clk_init_data init = { |
118 | .name = jh7100_audclk_data[idx].name, |
119 | .ops = starfive_jh71x0_clk_ops(max), |
120 | .parent_data = parents, |
121 | .num_parents = ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, |
122 | .flags = jh7100_audclk_data[idx].flags, |
123 | }; |
124 | struct jh71x0_clk *clk = &priv->reg[idx]; |
125 | unsigned int i; |
126 | |
127 | for (i = 0; i < init.num_parents; i++) { |
128 | unsigned int pidx = jh7100_audclk_data[idx].parents[i]; |
129 | |
130 | if (pidx < JH7100_AUDCLK_END) |
131 | parents[i].hw = &priv->reg[pidx].hw; |
132 | else if (pidx == JH7100_AUDCLK_AUDIO_SRC) |
133 | parents[i].fw_name = "audio_src" ; |
134 | else if (pidx == JH7100_AUDCLK_AUDIO_12288) |
135 | parents[i].fw_name = "audio_12288" ; |
136 | else if (pidx == JH7100_AUDCLK_DOM7AHB_BUS) |
137 | parents[i].fw_name = "dom7ahb_bus" ; |
138 | } |
139 | |
140 | clk->hw.init = &init; |
141 | clk->idx = idx; |
142 | clk->max_div = max & JH71X0_CLK_DIV_MASK; |
143 | |
144 | ret = devm_clk_hw_register(dev: priv->dev, hw: &clk->hw); |
145 | if (ret) |
146 | return ret; |
147 | } |
148 | |
149 | return devm_of_clk_add_hw_provider(dev: priv->dev, get: jh7100_audclk_get, data: priv); |
150 | } |
151 | |
152 | static const struct of_device_id jh7100_audclk_match[] = { |
153 | { .compatible = "starfive,jh7100-audclk" }, |
154 | { /* sentinel */ } |
155 | }; |
156 | MODULE_DEVICE_TABLE(of, jh7100_audclk_match); |
157 | |
158 | static struct platform_driver jh7100_audclk_driver = { |
159 | .probe = jh7100_audclk_probe, |
160 | .driver = { |
161 | .name = "clk-starfive-jh7100-audio" , |
162 | .of_match_table = jh7100_audclk_match, |
163 | }, |
164 | }; |
165 | module_platform_driver(jh7100_audclk_driver); |
166 | |
167 | MODULE_AUTHOR("Emil Renner Berthing" ); |
168 | MODULE_DESCRIPTION("StarFive JH7100 audio clock driver" ); |
169 | MODULE_LICENSE("GPL v2" ); |
170 | |