0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/clk-provider.h>
0012 #include <linux/device.h>
0013 #include <linux/module.h>
0014 #include <linux/of.h>
0015 #include <linux/of_device.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/regmap.h>
0018
0019 #include <linux/mfd/lochnagar1_regs.h>
0020 #include <linux/mfd/lochnagar2_regs.h>
0021
0022 #include <dt-bindings/clk/lochnagar.h>
0023
0024 #define LOCHNAGAR_NUM_CLOCKS (LOCHNAGAR_SPDIF_CLKOUT + 1)
0025
0026 struct lochnagar_clk {
0027 const char * const name;
0028 struct clk_hw hw;
0029
0030 struct lochnagar_clk_priv *priv;
0031
0032 u16 cfg_reg;
0033 u16 ena_mask;
0034
0035 u16 src_reg;
0036 u16 src_mask;
0037 };
0038
0039 struct lochnagar_clk_priv {
0040 struct device *dev;
0041 struct regmap *regmap;
0042
0043 struct lochnagar_clk lclks[LOCHNAGAR_NUM_CLOCKS];
0044 };
0045
0046 #define LN_PARENT(NAME) { .name = NAME, .fw_name = NAME }
0047
0048 static const struct clk_parent_data lochnagar1_clk_parents[] = {
0049 LN_PARENT("ln-none"),
0050 LN_PARENT("ln-spdif-mclk"),
0051 LN_PARENT("ln-psia1-mclk"),
0052 LN_PARENT("ln-psia2-mclk"),
0053 LN_PARENT("ln-cdc-clkout"),
0054 LN_PARENT("ln-dsp-clkout"),
0055 LN_PARENT("ln-pmic-32k"),
0056 LN_PARENT("ln-gf-mclk1"),
0057 LN_PARENT("ln-gf-mclk3"),
0058 LN_PARENT("ln-gf-mclk2"),
0059 LN_PARENT("ln-gf-mclk4"),
0060 };
0061
0062 static const struct clk_parent_data lochnagar2_clk_parents[] = {
0063 LN_PARENT("ln-none"),
0064 LN_PARENT("ln-cdc-clkout"),
0065 LN_PARENT("ln-dsp-clkout"),
0066 LN_PARENT("ln-pmic-32k"),
0067 LN_PARENT("ln-spdif-mclk"),
0068 LN_PARENT("ln-clk-12m"),
0069 LN_PARENT("ln-clk-11m"),
0070 LN_PARENT("ln-clk-24m"),
0071 LN_PARENT("ln-clk-22m"),
0072 LN_PARENT("ln-clk-8m"),
0073 LN_PARENT("ln-usb-clk-24m"),
0074 LN_PARENT("ln-gf-mclk1"),
0075 LN_PARENT("ln-gf-mclk3"),
0076 LN_PARENT("ln-gf-mclk2"),
0077 LN_PARENT("ln-psia1-mclk"),
0078 LN_PARENT("ln-psia2-mclk"),
0079 LN_PARENT("ln-spdif-clkout"),
0080 LN_PARENT("ln-adat-mclk"),
0081 LN_PARENT("ln-usb-clk-12m"),
0082 };
0083
0084 #define LN1_CLK(ID, NAME, REG) \
0085 [LOCHNAGAR_##ID] = { \
0086 .name = NAME, \
0087 .cfg_reg = LOCHNAGAR1_##REG, \
0088 .ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \
0089 .src_reg = LOCHNAGAR1_##ID##_SEL, \
0090 .src_mask = LOCHNAGAR1_SRC_MASK, \
0091 }
0092
0093 #define LN2_CLK(ID, NAME) \
0094 [LOCHNAGAR_##ID] = { \
0095 .name = NAME, \
0096 .cfg_reg = LOCHNAGAR2_##ID##_CTRL, \
0097 .src_reg = LOCHNAGAR2_##ID##_CTRL, \
0098 .ena_mask = LOCHNAGAR2_CLK_ENA_MASK, \
0099 .src_mask = LOCHNAGAR2_CLK_SRC_MASK, \
0100 }
0101
0102 static const struct lochnagar_clk lochnagar1_clks[LOCHNAGAR_NUM_CLOCKS] = {
0103 LN1_CLK(CDC_MCLK1, "ln-cdc-mclk1", CDC_AIF_CTRL2),
0104 LN1_CLK(CDC_MCLK2, "ln-cdc-mclk2", CDC_AIF_CTRL2),
0105 LN1_CLK(DSP_CLKIN, "ln-dsp-clkin", DSP_AIF),
0106 LN1_CLK(GF_CLKOUT1, "ln-gf-clkout1", GF_AIF1),
0107 };
0108
0109 static const struct lochnagar_clk lochnagar2_clks[LOCHNAGAR_NUM_CLOCKS] = {
0110 LN2_CLK(CDC_MCLK1, "ln-cdc-mclk1"),
0111 LN2_CLK(CDC_MCLK2, "ln-cdc-mclk2"),
0112 LN2_CLK(DSP_CLKIN, "ln-dsp-clkin"),
0113 LN2_CLK(GF_CLKOUT1, "ln-gf-clkout1"),
0114 LN2_CLK(GF_CLKOUT2, "ln-gf-clkout2"),
0115 LN2_CLK(PSIA1_MCLK, "ln-psia1-mclk"),
0116 LN2_CLK(PSIA2_MCLK, "ln-psia2-mclk"),
0117 LN2_CLK(SPDIF_MCLK, "ln-spdif-mclk"),
0118 LN2_CLK(ADAT_MCLK, "ln-adat-mclk"),
0119 LN2_CLK(SOUNDCARD_MCLK, "ln-soundcard-mclk"),
0120 };
0121
0122 struct lochnagar_config {
0123 const struct clk_parent_data *parents;
0124 int nparents;
0125 const struct lochnagar_clk *clks;
0126 };
0127
0128 static const struct lochnagar_config lochnagar1_conf = {
0129 .parents = lochnagar1_clk_parents,
0130 .nparents = ARRAY_SIZE(lochnagar1_clk_parents),
0131 .clks = lochnagar1_clks,
0132 };
0133
0134 static const struct lochnagar_config lochnagar2_conf = {
0135 .parents = lochnagar2_clk_parents,
0136 .nparents = ARRAY_SIZE(lochnagar2_clk_parents),
0137 .clks = lochnagar2_clks,
0138 };
0139
0140 static inline struct lochnagar_clk *lochnagar_hw_to_lclk(struct clk_hw *hw)
0141 {
0142 return container_of(hw, struct lochnagar_clk, hw);
0143 }
0144
0145 static int lochnagar_clk_prepare(struct clk_hw *hw)
0146 {
0147 struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
0148 struct lochnagar_clk_priv *priv = lclk->priv;
0149 struct regmap *regmap = priv->regmap;
0150 int ret;
0151
0152 ret = regmap_update_bits(regmap, lclk->cfg_reg,
0153 lclk->ena_mask, lclk->ena_mask);
0154 if (ret < 0)
0155 dev_dbg(priv->dev, "Failed to prepare %s: %d\n",
0156 lclk->name, ret);
0157
0158 return ret;
0159 }
0160
0161 static void lochnagar_clk_unprepare(struct clk_hw *hw)
0162 {
0163 struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
0164 struct lochnagar_clk_priv *priv = lclk->priv;
0165 struct regmap *regmap = priv->regmap;
0166 int ret;
0167
0168 ret = regmap_update_bits(regmap, lclk->cfg_reg, lclk->ena_mask, 0);
0169 if (ret < 0)
0170 dev_dbg(priv->dev, "Failed to unprepare %s: %d\n",
0171 lclk->name, ret);
0172 }
0173
0174 static int lochnagar_clk_set_parent(struct clk_hw *hw, u8 index)
0175 {
0176 struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
0177 struct lochnagar_clk_priv *priv = lclk->priv;
0178 struct regmap *regmap = priv->regmap;
0179 int ret;
0180
0181 ret = regmap_update_bits(regmap, lclk->src_reg, lclk->src_mask, index);
0182 if (ret < 0)
0183 dev_dbg(priv->dev, "Failed to reparent %s: %d\n",
0184 lclk->name, ret);
0185
0186 return ret;
0187 }
0188
0189 static u8 lochnagar_clk_get_parent(struct clk_hw *hw)
0190 {
0191 struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
0192 struct lochnagar_clk_priv *priv = lclk->priv;
0193 struct regmap *regmap = priv->regmap;
0194 unsigned int val;
0195 int ret;
0196
0197 ret = regmap_read(regmap, lclk->src_reg, &val);
0198 if (ret < 0) {
0199 dev_dbg(priv->dev, "Failed to read parent of %s: %d\n",
0200 lclk->name, ret);
0201 return clk_hw_get_num_parents(hw);
0202 }
0203
0204 val &= lclk->src_mask;
0205
0206 return val;
0207 }
0208
0209 static const struct clk_ops lochnagar_clk_ops = {
0210 .prepare = lochnagar_clk_prepare,
0211 .unprepare = lochnagar_clk_unprepare,
0212 .set_parent = lochnagar_clk_set_parent,
0213 .get_parent = lochnagar_clk_get_parent,
0214 };
0215
0216 static struct clk_hw *
0217 lochnagar_of_clk_hw_get(struct of_phandle_args *clkspec, void *data)
0218 {
0219 struct lochnagar_clk_priv *priv = data;
0220 unsigned int idx = clkspec->args[0];
0221
0222 if (idx >= ARRAY_SIZE(priv->lclks)) {
0223 dev_err(priv->dev, "Invalid index %u\n", idx);
0224 return ERR_PTR(-EINVAL);
0225 }
0226
0227 return &priv->lclks[idx].hw;
0228 }
0229
0230 static const struct of_device_id lochnagar_of_match[] = {
0231 { .compatible = "cirrus,lochnagar1-clk", .data = &lochnagar1_conf },
0232 { .compatible = "cirrus,lochnagar2-clk", .data = &lochnagar2_conf },
0233 {}
0234 };
0235 MODULE_DEVICE_TABLE(of, lochnagar_of_match);
0236
0237 static int lochnagar_clk_probe(struct platform_device *pdev)
0238 {
0239 struct clk_init_data clk_init = {
0240 .ops = &lochnagar_clk_ops,
0241 };
0242 struct device *dev = &pdev->dev;
0243 struct lochnagar_clk_priv *priv;
0244 const struct of_device_id *of_id;
0245 struct lochnagar_clk *lclk;
0246 struct lochnagar_config *conf;
0247 int ret, i;
0248
0249 of_id = of_match_device(lochnagar_of_match, dev);
0250 if (!of_id)
0251 return -EINVAL;
0252
0253 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0254 if (!priv)
0255 return -ENOMEM;
0256
0257 priv->dev = dev;
0258 priv->regmap = dev_get_regmap(dev->parent, NULL);
0259 conf = (struct lochnagar_config *)of_id->data;
0260
0261 memcpy(priv->lclks, conf->clks, sizeof(priv->lclks));
0262
0263 clk_init.parent_data = conf->parents;
0264 clk_init.num_parents = conf->nparents;
0265
0266 for (i = 0; i < ARRAY_SIZE(priv->lclks); i++) {
0267 lclk = &priv->lclks[i];
0268
0269 if (!lclk->name)
0270 continue;
0271
0272 clk_init.name = lclk->name;
0273
0274 lclk->priv = priv;
0275 lclk->hw.init = &clk_init;
0276
0277 ret = devm_clk_hw_register(dev, &lclk->hw);
0278 if (ret) {
0279 dev_err(dev, "Failed to register %s: %d\n",
0280 lclk->name, ret);
0281 return ret;
0282 }
0283 }
0284
0285 ret = devm_of_clk_add_hw_provider(dev, lochnagar_of_clk_hw_get, priv);
0286 if (ret < 0)
0287 dev_err(dev, "Failed to register provider: %d\n", ret);
0288
0289 return ret;
0290 }
0291
0292 static struct platform_driver lochnagar_clk_driver = {
0293 .driver = {
0294 .name = "lochnagar-clk",
0295 .of_match_table = lochnagar_of_match,
0296 },
0297 .probe = lochnagar_clk_probe,
0298 };
0299 module_platform_driver(lochnagar_clk_driver);
0300
0301 MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
0302 MODULE_DESCRIPTION("Clock driver for Cirrus Logic Lochnagar Board");
0303 MODULE_LICENSE("GPL v2");