Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Lochnagar clock control
0004  *
0005  * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
0006  *                         Cirrus Logic International Semiconductor Ltd.
0007  *
0008  * Author: Charles Keepax <ckeepax@opensource.cirrus.com>
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");