brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · 5561a2c Raw
300 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Lochnagar clock control4 *5 * Copyright (c) 2017-2018 Cirrus Logic, Inc. and6 *                         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/platform_device.h>16#include <linux/property.h>17#include <linux/regmap.h>18 19#include <linux/mfd/lochnagar1_regs.h>20#include <linux/mfd/lochnagar2_regs.h>21 22#include <dt-bindings/clock/lochnagar.h>23 24#define LOCHNAGAR_NUM_CLOCKS	(LOCHNAGAR_SPDIF_CLKOUT + 1)25 26struct lochnagar_clk {27	const char * const name;28	struct clk_hw hw;29 30	struct lochnagar_clk_priv *priv;31 32	u16 cfg_reg;33	u16 ena_mask;34 35	u16 src_reg;36	u16 src_mask;37};38 39struct lochnagar_clk_priv {40	struct device *dev;41	struct regmap *regmap;42 43	struct lochnagar_clk lclks[LOCHNAGAR_NUM_CLOCKS];44};45 46#define LN_PARENT(NAME) { .name = NAME, .fw_name = NAME }47 48static const struct clk_parent_data lochnagar1_clk_parents[] = {49	LN_PARENT("ln-none"),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"),60};61 62static const struct clk_parent_data lochnagar2_clk_parents[] = {63	LN_PARENT("ln-none"),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"),82};83 84#define LN1_CLK(ID, NAME, REG) \85	[LOCHNAGAR_##ID] = { \86		.name = NAME, \87		.cfg_reg = LOCHNAGAR1_##REG, \88		.ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \89		.src_reg = LOCHNAGAR1_##ID##_SEL, \90		.src_mask = LOCHNAGAR1_SRC_MASK, \91	}92 93#define LN2_CLK(ID, NAME) \94	[LOCHNAGAR_##ID] = { \95		.name = NAME, \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, \100	}101 102static 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),107};108 109static 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"),120};121 122struct lochnagar_config {123	const struct clk_parent_data *parents;124	int nparents;125	const struct lochnagar_clk *clks;126};127 128static const struct lochnagar_config lochnagar1_conf = {129	.parents = lochnagar1_clk_parents,130	.nparents = ARRAY_SIZE(lochnagar1_clk_parents),131	.clks = lochnagar1_clks,132};133 134static const struct lochnagar_config lochnagar2_conf = {135	.parents = lochnagar2_clk_parents,136	.nparents = ARRAY_SIZE(lochnagar2_clk_parents),137	.clks = lochnagar2_clks,138};139 140static inline struct lochnagar_clk *lochnagar_hw_to_lclk(struct clk_hw *hw)141{142	return container_of(hw, struct lochnagar_clk, hw);143}144 145static int lochnagar_clk_prepare(struct clk_hw *hw)146{147	struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);148	struct lochnagar_clk_priv *priv = lclk->priv;149	struct regmap *regmap = priv->regmap;150	int ret;151 152	ret = regmap_update_bits(regmap, lclk->cfg_reg,153				 lclk->ena_mask, lclk->ena_mask);154	if (ret < 0)155		dev_dbg(priv->dev, "Failed to prepare %s: %d\n",156			lclk->name, ret);157 158	return ret;159}160 161static void lochnagar_clk_unprepare(struct clk_hw *hw)162{163	struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);164	struct lochnagar_clk_priv *priv = lclk->priv;165	struct regmap *regmap = priv->regmap;166	int ret;167 168	ret = regmap_update_bits(regmap, lclk->cfg_reg, lclk->ena_mask, 0);169	if (ret < 0)170		dev_dbg(priv->dev, "Failed to unprepare %s: %d\n",171			lclk->name, ret);172}173 174static int lochnagar_clk_set_parent(struct clk_hw *hw, u8 index)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	int ret;180 181	ret = regmap_update_bits(regmap, lclk->src_reg, lclk->src_mask, index);182	if (ret < 0)183		dev_dbg(priv->dev, "Failed to reparent %s: %d\n",184			lclk->name, ret);185 186	return ret;187}188 189static u8 lochnagar_clk_get_parent(struct clk_hw *hw)190{191	struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);192	struct lochnagar_clk_priv *priv = lclk->priv;193	struct regmap *regmap = priv->regmap;194	unsigned int val;195	int ret;196 197	ret = regmap_read(regmap, lclk->src_reg, &val);198	if (ret < 0) {199		dev_dbg(priv->dev, "Failed to read parent of %s: %d\n",200			lclk->name, ret);201		return clk_hw_get_num_parents(hw);202	}203 204	val &= lclk->src_mask;205 206	return val;207}208 209static const struct clk_ops lochnagar_clk_ops = {210	.prepare = lochnagar_clk_prepare,211	.unprepare = lochnagar_clk_unprepare,212	.determine_rate = clk_hw_determine_rate_no_reparent,213	.set_parent = lochnagar_clk_set_parent,214	.get_parent = lochnagar_clk_get_parent,215};216 217static struct clk_hw *218lochnagar_of_clk_hw_get(struct of_phandle_args *clkspec, void *data)219{220	struct lochnagar_clk_priv *priv = data;221	unsigned int idx = clkspec->args[0];222 223	if (idx >= ARRAY_SIZE(priv->lclks)) {224		dev_err(priv->dev, "Invalid index %u\n", idx);225		return ERR_PTR(-EINVAL);226	}227 228	return &priv->lclks[idx].hw;229}230 231static const struct of_device_id lochnagar_of_match[] = {232	{ .compatible = "cirrus,lochnagar1-clk", .data = &lochnagar1_conf },233	{ .compatible = "cirrus,lochnagar2-clk", .data = &lochnagar2_conf },234	{}235};236MODULE_DEVICE_TABLE(of, lochnagar_of_match);237 238static int lochnagar_clk_probe(struct platform_device *pdev)239{240	struct clk_init_data clk_init = {241		.ops = &lochnagar_clk_ops,242	};243	struct device *dev = &pdev->dev;244	struct lochnagar_clk_priv *priv;245	struct lochnagar_clk *lclk;246	struct lochnagar_config *conf;247	int ret, i;248 249	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);250	if (!priv)251		return -ENOMEM;252 253	priv->dev = dev;254	priv->regmap = dev_get_regmap(dev->parent, NULL);255	conf = (struct lochnagar_config *)device_get_match_data(dev);256 257	memcpy(priv->lclks, conf->clks, sizeof(priv->lclks));258 259	clk_init.parent_data = conf->parents;260	clk_init.num_parents = conf->nparents;261 262	for (i = 0; i < ARRAY_SIZE(priv->lclks); i++) {263		lclk = &priv->lclks[i];264 265		if (!lclk->name)266			continue;267 268		clk_init.name = lclk->name;269 270		lclk->priv = priv;271		lclk->hw.init = &clk_init;272 273		ret = devm_clk_hw_register(dev, &lclk->hw);274		if (ret) {275			dev_err(dev, "Failed to register %s: %d\n",276				lclk->name, ret);277			return ret;278		}279	}280 281	ret = devm_of_clk_add_hw_provider(dev, lochnagar_of_clk_hw_get, priv);282	if (ret < 0)283		dev_err(dev, "Failed to register provider: %d\n", ret);284 285	return ret;286}287 288static struct platform_driver lochnagar_clk_driver = {289	.driver = {290		.name = "lochnagar-clk",291		.of_match_table = lochnagar_of_match,292	},293	.probe = lochnagar_clk_probe,294};295module_platform_driver(lochnagar_clk_driver);296 297MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");298MODULE_DESCRIPTION("Clock driver for Cirrus Logic Lochnagar Board");299MODULE_LICENSE("GPL v2");300