1 // SPDX-License-Identifier: GPL-2.0
3 * Lochnagar clock control
5 * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
6 * Cirrus Logic International Semiconductor Ltd.
8 * Author: Charles Keepax <ckeepax@opensource.cirrus.com>
11 #include <linux/clk-provider.h>
12 #include <linux/device.h>
13 #include <linux/module.h>
15 #include <linux/of_device.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
19 #include <linux/mfd/lochnagar1_regs.h>
20 #include <linux/mfd/lochnagar2_regs.h>
22 #include <dt-bindings/clk/lochnagar.h>
24 #define LOCHNAGAR_NUM_CLOCKS (LOCHNAGAR_SPDIF_CLKOUT + 1)
26 struct lochnagar_clk
{
27 const char * const name
;
30 struct lochnagar_clk_priv
*priv
;
39 struct lochnagar_clk_priv
{
41 struct regmap
*regmap
;
43 struct lochnagar_clk lclks
[LOCHNAGAR_NUM_CLOCKS
];
46 #define LN_PARENT(NAME) { .name = NAME, .fw_name = NAME }
48 static const struct clk_parent_data lochnagar1_clk_parents
[] = {
50 LN_PARENT("ln-spdif-mclk"),
51 LN_PARENT("ln-psia1-mclk"),
52 LN_PARENT("ln-psia2-mclk"),
53 LN_PARENT("ln-cdc-clkout"),
54 LN_PARENT("ln-dsp-clkout"),
55 LN_PARENT("ln-pmic-32k"),
56 LN_PARENT("ln-gf-mclk1"),
57 LN_PARENT("ln-gf-mclk3"),
58 LN_PARENT("ln-gf-mclk2"),
59 LN_PARENT("ln-gf-mclk4"),
62 static const struct clk_parent_data lochnagar2_clk_parents
[] = {
64 LN_PARENT("ln-cdc-clkout"),
65 LN_PARENT("ln-dsp-clkout"),
66 LN_PARENT("ln-pmic-32k"),
67 LN_PARENT("ln-spdif-mclk"),
68 LN_PARENT("ln-clk-12m"),
69 LN_PARENT("ln-clk-11m"),
70 LN_PARENT("ln-clk-24m"),
71 LN_PARENT("ln-clk-22m"),
72 LN_PARENT("ln-clk-8m"),
73 LN_PARENT("ln-usb-clk-24m"),
74 LN_PARENT("ln-gf-mclk1"),
75 LN_PARENT("ln-gf-mclk3"),
76 LN_PARENT("ln-gf-mclk2"),
77 LN_PARENT("ln-psia1-mclk"),
78 LN_PARENT("ln-psia2-mclk"),
79 LN_PARENT("ln-spdif-clkout"),
80 LN_PARENT("ln-adat-mclk"),
81 LN_PARENT("ln-usb-clk-12m"),
84 #define LN1_CLK(ID, NAME, REG) \
85 [LOCHNAGAR_##ID] = { \
87 .cfg_reg = LOCHNAGAR1_##REG, \
88 .ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \
89 .src_reg = LOCHNAGAR1_##ID##_SEL, \
90 .src_mask = LOCHNAGAR1_SRC_MASK, \
93 #define LN2_CLK(ID, NAME) \
94 [LOCHNAGAR_##ID] = { \
96 .cfg_reg = LOCHNAGAR2_##ID##_CTRL, \
97 .src_reg = LOCHNAGAR2_##ID##_CTRL, \
98 .ena_mask = LOCHNAGAR2_CLK_ENA_MASK, \
99 .src_mask = LOCHNAGAR2_CLK_SRC_MASK, \
102 static const struct lochnagar_clk lochnagar1_clks
[LOCHNAGAR_NUM_CLOCKS
] = {
103 LN1_CLK(CDC_MCLK1
, "ln-cdc-mclk1", CDC_AIF_CTRL2
),
104 LN1_CLK(CDC_MCLK2
, "ln-cdc-mclk2", CDC_AIF_CTRL2
),
105 LN1_CLK(DSP_CLKIN
, "ln-dsp-clkin", DSP_AIF
),
106 LN1_CLK(GF_CLKOUT1
, "ln-gf-clkout1", GF_AIF1
),
109 static const struct lochnagar_clk lochnagar2_clks
[LOCHNAGAR_NUM_CLOCKS
] = {
110 LN2_CLK(CDC_MCLK1
, "ln-cdc-mclk1"),
111 LN2_CLK(CDC_MCLK2
, "ln-cdc-mclk2"),
112 LN2_CLK(DSP_CLKIN
, "ln-dsp-clkin"),
113 LN2_CLK(GF_CLKOUT1
, "ln-gf-clkout1"),
114 LN2_CLK(GF_CLKOUT2
, "ln-gf-clkout2"),
115 LN2_CLK(PSIA1_MCLK
, "ln-psia1-mclk"),
116 LN2_CLK(PSIA2_MCLK
, "ln-psia2-mclk"),
117 LN2_CLK(SPDIF_MCLK
, "ln-spdif-mclk"),
118 LN2_CLK(ADAT_MCLK
, "ln-adat-mclk"),
119 LN2_CLK(SOUNDCARD_MCLK
, "ln-soundcard-mclk"),
122 struct lochnagar_config
{
123 const struct clk_parent_data
*parents
;
125 const struct lochnagar_clk
*clks
;
128 static const struct lochnagar_config lochnagar1_conf
= {
129 .parents
= lochnagar1_clk_parents
,
130 .nparents
= ARRAY_SIZE(lochnagar1_clk_parents
),
131 .clks
= lochnagar1_clks
,
134 static const struct lochnagar_config lochnagar2_conf
= {
135 .parents
= lochnagar2_clk_parents
,
136 .nparents
= ARRAY_SIZE(lochnagar2_clk_parents
),
137 .clks
= lochnagar2_clks
,
140 static inline struct lochnagar_clk
*lochnagar_hw_to_lclk(struct clk_hw
*hw
)
142 return container_of(hw
, struct lochnagar_clk
, hw
);
145 static int lochnagar_clk_prepare(struct clk_hw
*hw
)
147 struct lochnagar_clk
*lclk
= lochnagar_hw_to_lclk(hw
);
148 struct lochnagar_clk_priv
*priv
= lclk
->priv
;
149 struct regmap
*regmap
= priv
->regmap
;
152 ret
= regmap_update_bits(regmap
, lclk
->cfg_reg
,
153 lclk
->ena_mask
, lclk
->ena_mask
);
155 dev_dbg(priv
->dev
, "Failed to prepare %s: %d\n",
161 static void lochnagar_clk_unprepare(struct clk_hw
*hw
)
163 struct lochnagar_clk
*lclk
= lochnagar_hw_to_lclk(hw
);
164 struct lochnagar_clk_priv
*priv
= lclk
->priv
;
165 struct regmap
*regmap
= priv
->regmap
;
168 ret
= regmap_update_bits(regmap
, lclk
->cfg_reg
, lclk
->ena_mask
, 0);
170 dev_dbg(priv
->dev
, "Failed to unprepare %s: %d\n",
174 static int lochnagar_clk_set_parent(struct clk_hw
*hw
, u8 index
)
176 struct lochnagar_clk
*lclk
= lochnagar_hw_to_lclk(hw
);
177 struct lochnagar_clk_priv
*priv
= lclk
->priv
;
178 struct regmap
*regmap
= priv
->regmap
;
181 ret
= regmap_update_bits(regmap
, lclk
->src_reg
, lclk
->src_mask
, index
);
183 dev_dbg(priv
->dev
, "Failed to reparent %s: %d\n",
189 static u8
lochnagar_clk_get_parent(struct clk_hw
*hw
)
191 struct lochnagar_clk
*lclk
= lochnagar_hw_to_lclk(hw
);
192 struct lochnagar_clk_priv
*priv
= lclk
->priv
;
193 struct regmap
*regmap
= priv
->regmap
;
197 ret
= regmap_read(regmap
, lclk
->src_reg
, &val
);
199 dev_dbg(priv
->dev
, "Failed to read parent of %s: %d\n",
201 return hw
->init
->num_parents
;
204 val
&= lclk
->src_mask
;
209 static const struct clk_ops lochnagar_clk_ops
= {
210 .prepare
= lochnagar_clk_prepare
,
211 .unprepare
= lochnagar_clk_unprepare
,
212 .set_parent
= lochnagar_clk_set_parent
,
213 .get_parent
= lochnagar_clk_get_parent
,
216 static struct clk_hw
*
217 lochnagar_of_clk_hw_get(struct of_phandle_args
*clkspec
, void *data
)
219 struct lochnagar_clk_priv
*priv
= data
;
220 unsigned int idx
= clkspec
->args
[0];
222 if (idx
>= ARRAY_SIZE(priv
->lclks
)) {
223 dev_err(priv
->dev
, "Invalid index %u\n", idx
);
224 return ERR_PTR(-EINVAL
);
227 return &priv
->lclks
[idx
].hw
;
230 static const struct of_device_id lochnagar_of_match
[] = {
231 { .compatible
= "cirrus,lochnagar1-clk", .data
= &lochnagar1_conf
},
232 { .compatible
= "cirrus,lochnagar2-clk", .data
= &lochnagar2_conf
},
235 MODULE_DEVICE_TABLE(of
, lochnagar_of_match
);
237 static int lochnagar_clk_probe(struct platform_device
*pdev
)
239 struct clk_init_data clk_init
= {
240 .ops
= &lochnagar_clk_ops
,
242 struct device
*dev
= &pdev
->dev
;
243 struct lochnagar_clk_priv
*priv
;
244 const struct of_device_id
*of_id
;
245 struct lochnagar_clk
*lclk
;
246 struct lochnagar_config
*conf
;
249 of_id
= of_match_device(lochnagar_of_match
, dev
);
253 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
258 priv
->regmap
= dev_get_regmap(dev
->parent
, NULL
);
259 conf
= (struct lochnagar_config
*)of_id
->data
;
261 memcpy(priv
->lclks
, conf
->clks
, sizeof(priv
->lclks
));
263 clk_init
.parent_data
= conf
->parents
;
264 clk_init
.num_parents
= conf
->nparents
;
266 for (i
= 0; i
< ARRAY_SIZE(priv
->lclks
); i
++) {
267 lclk
= &priv
->lclks
[i
];
272 clk_init
.name
= lclk
->name
;
275 lclk
->hw
.init
= &clk_init
;
277 ret
= devm_clk_hw_register(dev
, &lclk
->hw
);
279 dev_err(dev
, "Failed to register %s: %d\n",
285 ret
= devm_of_clk_add_hw_provider(dev
, lochnagar_of_clk_hw_get
, priv
);
287 dev_err(dev
, "Failed to register provider: %d\n", ret
);
292 static struct platform_driver lochnagar_clk_driver
= {
294 .name
= "lochnagar-clk",
295 .of_match_table
= lochnagar_of_match
,
297 .probe
= lochnagar_clk_probe
,
299 module_platform_driver(lochnagar_clk_driver
);
301 MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
302 MODULE_DESCRIPTION("Clock driver for Cirrus Logic Lochnagar Board");
303 MODULE_LICENSE("GPL v2");