1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Lochnagar clock control 4 * 5 * Copyright (c) 2017-2018 Cirrus Logic, Inc. and 6 * Cirrus Logic International Semiconductor Ltd. 7 * 8 * Author: Charles Keepax <ckeepax@opensource.cirrus.com> 9 */ 10 11 #include <linux/clk-provider.h> 12 #include <linux/device.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/of_device.h> 16 #include <linux/platform_device.h> 17 #include <linux/regmap.h> 18 19 #include <linux/mfd/lochnagar.h> 20 #include <linux/mfd/lochnagar1_regs.h> 21 #include <linux/mfd/lochnagar2_regs.h> 22 23 #include <dt-bindings/clk/lochnagar.h> 24 25 #define LOCHNAGAR_NUM_CLOCKS (LOCHNAGAR_SPDIF_CLKOUT + 1) 26 27 struct lochnagar_clk { 28 const char * const name; 29 struct clk_hw hw; 30 31 struct lochnagar_clk_priv *priv; 32 33 u16 cfg_reg; 34 u16 ena_mask; 35 36 u16 src_reg; 37 u16 src_mask; 38 }; 39 40 struct lochnagar_clk_priv { 41 struct device *dev; 42 struct regmap *regmap; 43 enum lochnagar_type type; 44 45 const char **parents; 46 unsigned int nparents; 47 48 struct lochnagar_clk lclks[LOCHNAGAR_NUM_CLOCKS]; 49 }; 50 51 static const char * const lochnagar1_clk_parents[] = { 52 "ln-none", 53 "ln-spdif-mclk", 54 "ln-psia1-mclk", 55 "ln-psia2-mclk", 56 "ln-cdc-clkout", 57 "ln-dsp-clkout", 58 "ln-pmic-32k", 59 "ln-gf-mclk1", 60 "ln-gf-mclk3", 61 "ln-gf-mclk2", 62 "ln-gf-mclk4", 63 }; 64 65 static const char * const lochnagar2_clk_parents[] = { 66 "ln-none", 67 "ln-cdc-clkout", 68 "ln-dsp-clkout", 69 "ln-pmic-32k", 70 "ln-spdif-mclk", 71 "ln-clk-12m", 72 "ln-clk-11m", 73 "ln-clk-24m", 74 "ln-clk-22m", 75 "ln-clk-8m", 76 "ln-usb-clk-24m", 77 "ln-gf-mclk1", 78 "ln-gf-mclk3", 79 "ln-gf-mclk2", 80 "ln-psia1-mclk", 81 "ln-psia2-mclk", 82 "ln-spdif-clkout", 83 "ln-adat-mclk", 84 "ln-usb-clk-12m", 85 }; 86 87 #define LN1_CLK(ID, NAME, REG) \ 88 [LOCHNAGAR_##ID] = { \ 89 .name = NAME, \ 90 .cfg_reg = LOCHNAGAR1_##REG, \ 91 .ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \ 92 .src_reg = LOCHNAGAR1_##ID##_SEL, \ 93 .src_mask = LOCHNAGAR1_SRC_MASK, \ 94 } 95 96 #define LN2_CLK(ID, NAME) \ 97 [LOCHNAGAR_##ID] = { \ 98 .name = NAME, \ 99 .cfg_reg = LOCHNAGAR2_##ID##_CTRL, \ 100 .src_reg = LOCHNAGAR2_##ID##_CTRL, \ 101 .ena_mask = LOCHNAGAR2_CLK_ENA_MASK, \ 102 .src_mask = LOCHNAGAR2_CLK_SRC_MASK, \ 103 } 104 105 static const struct lochnagar_clk lochnagar1_clks[LOCHNAGAR_NUM_CLOCKS] = { 106 LN1_CLK(CDC_MCLK1, "ln-cdc-mclk1", CDC_AIF_CTRL2), 107 LN1_CLK(CDC_MCLK2, "ln-cdc-mclk2", CDC_AIF_CTRL2), 108 LN1_CLK(DSP_CLKIN, "ln-dsp-clkin", DSP_AIF), 109 LN1_CLK(GF_CLKOUT1, "ln-gf-clkout1", GF_AIF1), 110 }; 111 112 static const struct lochnagar_clk lochnagar2_clks[LOCHNAGAR_NUM_CLOCKS] = { 113 LN2_CLK(CDC_MCLK1, "ln-cdc-mclk1"), 114 LN2_CLK(CDC_MCLK2, "ln-cdc-mclk2"), 115 LN2_CLK(DSP_CLKIN, "ln-dsp-clkin"), 116 LN2_CLK(GF_CLKOUT1, "ln-gf-clkout1"), 117 LN2_CLK(GF_CLKOUT2, "ln-gf-clkout2"), 118 LN2_CLK(PSIA1_MCLK, "ln-psia1-mclk"), 119 LN2_CLK(PSIA2_MCLK, "ln-psia2-mclk"), 120 LN2_CLK(SPDIF_MCLK, "ln-spdif-mclk"), 121 LN2_CLK(ADAT_MCLK, "ln-adat-mclk"), 122 LN2_CLK(SOUNDCARD_MCLK, "ln-soundcard-mclk"), 123 }; 124 125 static inline struct lochnagar_clk *lochnagar_hw_to_lclk(struct clk_hw *hw) 126 { 127 return container_of(hw, struct lochnagar_clk, hw); 128 } 129 130 static int lochnagar_clk_prepare(struct clk_hw *hw) 131 { 132 struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw); 133 struct lochnagar_clk_priv *priv = lclk->priv; 134 struct regmap *regmap = priv->regmap; 135 int ret; 136 137 ret = regmap_update_bits(regmap, lclk->cfg_reg, 138 lclk->ena_mask, lclk->ena_mask); 139 if (ret < 0) 140 dev_dbg(priv->dev, "Failed to prepare %s: %d\n", 141 lclk->name, ret); 142 143 return ret; 144 } 145 146 static void lochnagar_clk_unprepare(struct clk_hw *hw) 147 { 148 struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw); 149 struct lochnagar_clk_priv *priv = lclk->priv; 150 struct regmap *regmap = priv->regmap; 151 int ret; 152 153 ret = regmap_update_bits(regmap, lclk->cfg_reg, lclk->ena_mask, 0); 154 if (ret < 0) 155 dev_dbg(priv->dev, "Failed to unprepare %s: %d\n", 156 lclk->name, ret); 157 } 158 159 static int lochnagar_clk_set_parent(struct clk_hw *hw, u8 index) 160 { 161 struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw); 162 struct lochnagar_clk_priv *priv = lclk->priv; 163 struct regmap *regmap = priv->regmap; 164 int ret; 165 166 ret = regmap_update_bits(regmap, lclk->src_reg, lclk->src_mask, index); 167 if (ret < 0) 168 dev_dbg(priv->dev, "Failed to reparent %s: %d\n", 169 lclk->name, ret); 170 171 return ret; 172 } 173 174 static u8 lochnagar_clk_get_parent(struct clk_hw *hw) 175 { 176 struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw); 177 struct lochnagar_clk_priv *priv = lclk->priv; 178 struct regmap *regmap = priv->regmap; 179 unsigned int val; 180 int ret; 181 182 ret = regmap_read(regmap, lclk->src_reg, &val); 183 if (ret < 0) { 184 dev_dbg(priv->dev, "Failed to read parent of %s: %d\n", 185 lclk->name, ret); 186 return priv->nparents; 187 } 188 189 val &= lclk->src_mask; 190 191 return val; 192 } 193 194 static const struct clk_ops lochnagar_clk_ops = { 195 .prepare = lochnagar_clk_prepare, 196 .unprepare = lochnagar_clk_unprepare, 197 .set_parent = lochnagar_clk_set_parent, 198 .get_parent = lochnagar_clk_get_parent, 199 }; 200 201 static int lochnagar_init_parents(struct lochnagar_clk_priv *priv) 202 { 203 struct device_node *np = priv->dev->of_node; 204 int i, j; 205 206 switch (priv->type) { 207 case LOCHNAGAR1: 208 memcpy(priv->lclks, lochnagar1_clks, sizeof(lochnagar1_clks)); 209 210 priv->nparents = ARRAY_SIZE(lochnagar1_clk_parents); 211 priv->parents = devm_kmemdup(priv->dev, lochnagar1_clk_parents, 212 sizeof(lochnagar1_clk_parents), 213 GFP_KERNEL); 214 break; 215 case LOCHNAGAR2: 216 memcpy(priv->lclks, lochnagar2_clks, sizeof(lochnagar2_clks)); 217 218 priv->nparents = ARRAY_SIZE(lochnagar2_clk_parents); 219 priv->parents = devm_kmemdup(priv->dev, lochnagar2_clk_parents, 220 sizeof(lochnagar2_clk_parents), 221 GFP_KERNEL); 222 break; 223 default: 224 dev_err(priv->dev, "Unknown Lochnagar type: %d\n", priv->type); 225 return -EINVAL; 226 } 227 228 if (!priv->parents) 229 return -ENOMEM; 230 231 for (i = 0; i < priv->nparents; i++) { 232 j = of_property_match_string(np, "clock-names", 233 priv->parents[i]); 234 if (j >= 0) 235 priv->parents[i] = of_clk_get_parent_name(np, j); 236 } 237 238 return 0; 239 } 240 241 static struct clk_hw * 242 lochnagar_of_clk_hw_get(struct of_phandle_args *clkspec, void *data) 243 { 244 struct lochnagar_clk_priv *priv = data; 245 unsigned int idx = clkspec->args[0]; 246 247 if (idx >= ARRAY_SIZE(priv->lclks)) { 248 dev_err(priv->dev, "Invalid index %u\n", idx); 249 return ERR_PTR(-EINVAL); 250 } 251 252 return &priv->lclks[idx].hw; 253 } 254 255 static int lochnagar_init_clks(struct lochnagar_clk_priv *priv) 256 { 257 struct clk_init_data clk_init = { 258 .ops = &lochnagar_clk_ops, 259 .parent_names = priv->parents, 260 .num_parents = priv->nparents, 261 }; 262 struct lochnagar_clk *lclk; 263 int ret, i; 264 265 for (i = 0; i < ARRAY_SIZE(priv->lclks); i++) { 266 lclk = &priv->lclks[i]; 267 268 if (!lclk->name) 269 continue; 270 271 clk_init.name = lclk->name; 272 273 lclk->priv = priv; 274 lclk->hw.init = &clk_init; 275 276 ret = devm_clk_hw_register(priv->dev, &lclk->hw); 277 if (ret) { 278 dev_err(priv->dev, "Failed to register %s: %d\n", 279 lclk->name, ret); 280 return ret; 281 } 282 } 283 284 ret = devm_of_clk_add_hw_provider(priv->dev, lochnagar_of_clk_hw_get, 285 priv); 286 if (ret < 0) 287 dev_err(priv->dev, "Failed to register provider: %d\n", ret); 288 289 return ret; 290 } 291 292 static const struct of_device_id lochnagar_of_match[] = { 293 { .compatible = "cirrus,lochnagar1-clk", .data = (void *)LOCHNAGAR1 }, 294 { .compatible = "cirrus,lochnagar2-clk", .data = (void *)LOCHNAGAR2 }, 295 {} 296 }; 297 MODULE_DEVICE_TABLE(of, lochnagar_of_match); 298 299 static int lochnagar_clk_probe(struct platform_device *pdev) 300 { 301 struct device *dev = &pdev->dev; 302 struct lochnagar_clk_priv *priv; 303 const struct of_device_id *of_id; 304 int ret; 305 306 of_id = of_match_device(lochnagar_of_match, dev); 307 if (!of_id) 308 return -EINVAL; 309 310 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 311 if (!priv) 312 return -ENOMEM; 313 314 priv->dev = dev; 315 priv->regmap = dev_get_regmap(dev->parent, NULL); 316 priv->type = (enum lochnagar_type)of_id->data; 317 318 ret = lochnagar_init_parents(priv); 319 if (ret) 320 return ret; 321 322 return lochnagar_init_clks(priv); 323 } 324 325 static struct platform_driver lochnagar_clk_driver = { 326 .driver = { 327 .name = "lochnagar-clk", 328 .of_match_table = lochnagar_of_match, 329 }, 330 .probe = lochnagar_clk_probe, 331 }; 332 module_platform_driver(lochnagar_clk_driver); 333 334 MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>"); 335 MODULE_DESCRIPTION("Clock driver for Cirrus Logic Lochnagar Board"); 336 MODULE_LICENSE("GPL v2"); 337