[v7,RESEND,1/2] clk: lochnagar: Add initial binding documentation
diff mbox series

Message ID 20190319133700.23878-1-ckeepax@opensource.cirrus.com
State Not Applicable
Headers show
Series
  • [v7,RESEND,1/2] clk: lochnagar: Add initial binding documentation
Related show

Commit Message

Charles Keepax March 19, 2019, 1:36 p.m. UTC
Lochnagar is an evaluation and development board for Cirrus
Logic Smart CODEC and Amp devices. It allows the connection of
most Cirrus Logic devices on mini-cards, as well as allowing
connection of various application processor systems to provide a
full evaluation platform. This driver supports the board
controller chip on the Lochnagar board.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/clock/cirrus,lochnagar.txt | 93 ++++++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/cirrus,lochnagar.txt

Comments

Charles Keepax April 1, 2019, 8:37 a.m. UTC | #1
On Tue, Mar 19, 2019 at 01:37:00PM +0000, Charles Keepax wrote:
> Lochnagar is an evaluation and development board for Cirrus
> Logic Smart CODEC and Amp devices. It allows the connection of
> most Cirrus Logic devices on mini-cards, as well as allowing
> connection of various application processor systems to provide a
> full evaluation platform. This driver supports the board
> controller chip on the Lochnagar board.
> 
> The Lochnagar can take several input clocks from the host system,
> provides several of its own clock sources, and provides extensive
> routing options for those clocks to be supplied to the attached
> CODEC/Amp device.
> 
> Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
> ---

Apologies for the ping, is there any chance to get a look at this
one? This and the pinctrl are the last two bits of the Lochnagar
that need merged but are somewhat critical to the working of the
other parts (regulator/sound/hwmon and MFD all merged already).

Thanks,
Charles

>  drivers/clk/Kconfig         |   7 +
>  drivers/clk/Makefile        |   1 +
>  drivers/clk/clk-lochnagar.c | 340 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 348 insertions(+)
>  create mode 100644 drivers/clk/clk-lochnagar.c
> 
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index e705aab9e38ba..1375ed43e72bc 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -219,6 +219,13 @@ config COMMON_CLK_XGENE
>  	---help---
>  	  Sypport for the APM X-Gene SoC reference, PLL, and device clocks.
>  
> +config COMMON_CLK_LOCHNAGAR
> +	tristate "Cirrus Logic Lochnagar clock driver"
> +	depends on MFD_LOCHNAGAR
> +	help
> +	  This driver supports the clocking features of the Cirrus Logic
> +	  Lochnagar audio development board.
> +
>  config COMMON_CLK_NXP
>  	def_bool COMMON_CLK && (ARCH_LPC18XX || ARCH_LPC32XX)
>  	select REGMAP_MMIO if ARCH_LPC32XX
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 1db133652f0c3..39b39e0986120 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -32,6 +32,7 @@ obj-$(CONFIG_COMMON_CLK_GEMINI)		+= clk-gemini.o
>  obj-$(CONFIG_COMMON_CLK_ASPEED)		+= clk-aspeed.o
>  obj-$(CONFIG_ARCH_HIGHBANK)		+= clk-highbank.o
>  obj-$(CONFIG_CLK_HSDK)			+= clk-hsdk-pll.o
> +obj-$(CONFIG_COMMON_CLK_LOCHNAGAR)	+= clk-lochnagar.o
>  obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
>  obj-$(CONFIG_COMMON_CLK_MAX9485)	+= clk-max9485.o
>  obj-$(CONFIG_ARCH_MOXART)		+= clk-moxart.o
> diff --git a/drivers/clk/clk-lochnagar.c b/drivers/clk/clk-lochnagar.c
> new file mode 100644
> index 0000000000000..ced832e82f46b
> --- /dev/null
> +++ b/drivers/clk/clk-lochnagar.c
> @@ -0,0 +1,340 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Lochnagar clock control
> + *
> + * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
> + *                         Cirrus Logic International Semiconductor Ltd.
> + *
> + * Author: Charles Keepax <ckeepax@opensource.cirrus.com>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#include <linux/mfd/lochnagar.h>
> +#include <linux/mfd/lochnagar1_regs.h>
> +#include <linux/mfd/lochnagar2_regs.h>
> +
> +#include <dt-bindings/clk/lochnagar.h>
> +
> +#define LOCHNAGAR_NUM_CLOCKS	(LOCHNAGAR_SPDIF_CLKOUT + 1)
> +
> +struct lochnagar_clk {
> +	const char * const name;
> +	struct clk_hw hw;
> +
> +	struct lochnagar_clk_priv *priv;
> +
> +	u16 cfg_reg;
> +	u16 ena_mask;
> +
> +	u16 src_reg;
> +	u16 src_mask;
> +};
> +
> +struct lochnagar_clk_priv {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	enum lochnagar_type type;
> +
> +	const char **parents;
> +	unsigned int nparents;
> +
> +	struct lochnagar_clk lclks[LOCHNAGAR_NUM_CLOCKS];
> +};
> +
> +static const char * const lochnagar1_clk_parents[] = {
> +	"ln-none",
> +	"ln-spdif-mclk",
> +	"ln-psia1-mclk",
> +	"ln-psia2-mclk",
> +	"ln-cdc-clkout",
> +	"ln-dsp-clkout",
> +	"ln-pmic-32k",
> +	"ln-gf-mclk1",
> +	"ln-gf-mclk3",
> +	"ln-gf-mclk2",
> +	"ln-gf-mclk4",
> +};
> +
> +static const char * const lochnagar2_clk_parents[] = {
> +	"ln-none",
> +	"ln-cdc-clkout",
> +	"ln-dsp-clkout",
> +	"ln-pmic-32k",
> +	"ln-spdif-mclk",
> +	"ln-clk-12m",
> +	"ln-clk-11m",
> +	"ln-clk-24m",
> +	"ln-clk-22m",
> +	"ln-clk-8m",
> +	"ln-usb-clk-24m",
> +	"ln-gf-mclk1",
> +	"ln-gf-mclk3",
> +	"ln-gf-mclk2",
> +	"ln-psia1-mclk",
> +	"ln-psia2-mclk",
> +	"ln-spdif-clkout",
> +	"ln-adat-mclk",
> +	"ln-usb-clk-12m",
> +};
> +
> +#define LN1_CLK(ID, NAME, REG) \
> +	[LOCHNAGAR_##ID] = { \
> +		.name = NAME, \
> +		.cfg_reg = LOCHNAGAR1_##REG, \
> +		.ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \
> +		.src_reg = LOCHNAGAR1_##ID##_SEL, \
> +		.src_mask = LOCHNAGAR1_SRC_MASK, \
> +	}
> +
> +#define LN2_CLK(ID, NAME) \
> +	[LOCHNAGAR_##ID] = { \
> +		.name = NAME, \
> +		.cfg_reg = LOCHNAGAR2_##ID##_CTRL, \
> +		.src_reg = LOCHNAGAR2_##ID##_CTRL, \
> +		.ena_mask = LOCHNAGAR2_CLK_ENA_MASK, \
> +		.src_mask = LOCHNAGAR2_CLK_SRC_MASK, \
> +	}
> +
> +static const struct lochnagar_clk lochnagar1_clks[LOCHNAGAR_NUM_CLOCKS] = {
> +	LN1_CLK(CDC_MCLK1,      "ln-cdc-mclk1",  CDC_AIF_CTRL2),
> +	LN1_CLK(CDC_MCLK2,      "ln-cdc-mclk2",  CDC_AIF_CTRL2),
> +	LN1_CLK(DSP_CLKIN,      "ln-dsp-clkin",  DSP_AIF),
> +	LN1_CLK(GF_CLKOUT1,     "ln-gf-clkout1", GF_AIF1),
> +};
> +
> +static const struct lochnagar_clk lochnagar2_clks[LOCHNAGAR_NUM_CLOCKS] = {
> +	LN2_CLK(CDC_MCLK1,      "ln-cdc-mclk1"),
> +	LN2_CLK(CDC_MCLK2,      "ln-cdc-mclk2"),
> +	LN2_CLK(DSP_CLKIN,      "ln-dsp-clkin"),
> +	LN2_CLK(GF_CLKOUT1,     "ln-gf-clkout1"),
> +	LN2_CLK(GF_CLKOUT2,     "ln-gf-clkout2"),
> +	LN2_CLK(PSIA1_MCLK,     "ln-psia1-mclk"),
> +	LN2_CLK(PSIA2_MCLK,     "ln-psia2-mclk"),
> +	LN2_CLK(SPDIF_MCLK,     "ln-spdif-mclk"),
> +	LN2_CLK(ADAT_MCLK,      "ln-adat-mclk"),
> +	LN2_CLK(SOUNDCARD_MCLK, "ln-soundcard-mclk"),
> +};
> +
> +static inline struct lochnagar_clk *lochnagar_hw_to_lclk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct lochnagar_clk, hw);
> +}
> +
> +static int lochnagar_clk_prepare(struct clk_hw *hw)
> +{
> +	struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
> +	struct lochnagar_clk_priv *priv = lclk->priv;
> +	struct regmap *regmap = priv->regmap;
> +	int ret;
> +
> +	ret = regmap_update_bits(regmap, lclk->cfg_reg,
> +				 lclk->ena_mask, lclk->ena_mask);
> +	if (ret < 0)
> +		dev_dbg(priv->dev, "Failed to prepare %s: %d\n",
> +			lclk->name, ret);
> +
> +	return ret;
> +}
> +
> +static void lochnagar_clk_unprepare(struct clk_hw *hw)
> +{
> +	struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
> +	struct lochnagar_clk_priv *priv = lclk->priv;
> +	struct regmap *regmap = priv->regmap;
> +	int ret;
> +
> +	ret = regmap_update_bits(regmap, lclk->cfg_reg, lclk->ena_mask, 0);
> +	if (ret < 0)
> +		dev_dbg(priv->dev, "Failed to unprepare %s: %d\n",
> +			lclk->name, ret);
> +}
> +
> +static int lochnagar_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
> +	struct lochnagar_clk_priv *priv = lclk->priv;
> +	struct regmap *regmap = priv->regmap;
> +	int ret;
> +
> +	ret = regmap_update_bits(regmap, lclk->src_reg, lclk->src_mask, index);
> +	if (ret < 0)
> +		dev_dbg(priv->dev, "Failed to reparent %s: %d\n",
> +			lclk->name, ret);
> +
> +	return ret;
> +}
> +
> +static u8 lochnagar_clk_get_parent(struct clk_hw *hw)
> +{
> +	struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
> +	struct lochnagar_clk_priv *priv = lclk->priv;
> +	struct regmap *regmap = priv->regmap;
> +	unsigned int val;
> +	int ret;
> +
> +	ret = regmap_read(regmap, lclk->src_reg, &val);
> +	if (ret < 0) {
> +		dev_dbg(priv->dev, "Failed to read parent of %s: %d\n",
> +			lclk->name, ret);
> +		return priv->nparents;
> +	}
> +
> +	val &= lclk->src_mask;
> +
> +	return val;
> +}
> +
> +static const struct clk_ops lochnagar_clk_ops = {
> +	.prepare = lochnagar_clk_prepare,
> +	.unprepare = lochnagar_clk_unprepare,
> +	.set_parent = lochnagar_clk_set_parent,
> +	.get_parent = lochnagar_clk_get_parent,
> +};
> +
> +static int lochnagar_init_parents(struct lochnagar_clk_priv *priv)
> +{
> +	struct device_node *np = priv->dev->of_node;
> +	int i, j;
> +
> +	switch (priv->type) {
> +	case LOCHNAGAR1:
> +		memcpy(priv->lclks, lochnagar1_clks, sizeof(lochnagar1_clks));
> +
> +		priv->nparents = ARRAY_SIZE(lochnagar1_clk_parents);
> +		priv->parents = devm_kmemdup(priv->dev, lochnagar1_clk_parents,
> +					     sizeof(lochnagar1_clk_parents),
> +					     GFP_KERNEL);
> +		break;
> +	case LOCHNAGAR2:
> +		memcpy(priv->lclks, lochnagar2_clks, sizeof(lochnagar2_clks));
> +
> +		priv->nparents = ARRAY_SIZE(lochnagar2_clk_parents);
> +		priv->parents = devm_kmemdup(priv->dev, lochnagar2_clk_parents,
> +					     sizeof(lochnagar2_clk_parents),
> +					     GFP_KERNEL);
> +		break;
> +	default:
> +		dev_err(priv->dev, "Unknown Lochnagar type: %d\n", priv->type);
> +		return -EINVAL;
> +	}
> +
> +	if (!priv->parents)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < priv->nparents; i++) {
> +		j = of_property_match_string(np, "clock-names",
> +					     priv->parents[i]);
> +		if (j >= 0)
> +			priv->parents[i] = of_clk_get_parent_name(np, j);
> +	}
> +
> +	return 0;
> +}
> +
> +static struct clk_hw *
> +lochnagar_of_clk_hw_get(struct of_phandle_args *clkspec, void *data)
> +{
> +	struct lochnagar_clk_priv *priv = data;
> +	unsigned int idx = clkspec->args[0];
> +
> +	if (idx >= ARRAY_SIZE(priv->lclks)) {
> +		dev_err(priv->dev, "Invalid index %u\n", idx);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return &priv->lclks[idx].hw;
> +}
> +
> +static int lochnagar_init_clks(struct lochnagar_clk_priv *priv)
> +{
> +	struct clk_init_data clk_init = {
> +		.ops = &lochnagar_clk_ops,
> +		.parent_names = priv->parents,
> +		.num_parents = priv->nparents,
> +	};
> +	struct lochnagar_clk *lclk;
> +	int ret, i;
> +
> +	for (i = 0; i < ARRAY_SIZE(priv->lclks); i++) {
> +		lclk = &priv->lclks[i];
> +
> +		if (!lclk->name)
> +			continue;
> +
> +		clk_init.name = lclk->name;
> +
> +		lclk->priv = priv;
> +		lclk->hw.init = &clk_init;
> +
> +		ret = devm_clk_hw_register(priv->dev, &lclk->hw);
> +		if (ret) {
> +			dev_err(priv->dev, "Failed to register %s: %d\n",
> +				lclk->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	ret = devm_of_clk_add_hw_provider(priv->dev, lochnagar_of_clk_hw_get,
> +					  priv);
> +	if (ret < 0)
> +		dev_err(priv->dev, "Failed to register provider: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id lochnagar_of_match[] = {
> +	{ .compatible = "cirrus,lochnagar1-clk", .data = (void *)LOCHNAGAR1 },
> +	{ .compatible = "cirrus,lochnagar2-clk", .data = (void *)LOCHNAGAR2 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, lochnagar_of_match);
> +
> +static int lochnagar_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct lochnagar_clk_priv *priv;
> +	const struct of_device_id *of_id;
> +	int ret;
> +
> +	of_id = of_match_device(lochnagar_of_match, dev);
> +	if (!of_id)
> +		return -EINVAL;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = dev;
> +	priv->regmap = dev_get_regmap(dev->parent, NULL);
> +	priv->type = (enum lochnagar_type)of_id->data;
> +
> +	ret = lochnagar_init_parents(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = lochnagar_init_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static struct platform_driver lochnagar_clk_driver = {
> +	.driver = {
> +		.name = "lochnagar-clk",
> +		.of_match_table = lochnagar_of_match,
> +	},
> +	.probe = lochnagar_clk_probe,
> +};
> +module_platform_driver(lochnagar_clk_driver);
> +
> +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
> +MODULE_DESCRIPTION("Clock driver for Cirrus Logic Lochnagar Board");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.11.0
>
Stephen Boyd April 23, 2019, 10:01 p.m. UTC | #2
Quoting Charles Keepax (2019-03-19 06:36:59)
> Lochnagar is an evaluation and development board for Cirrus
> Logic Smart CODEC and Amp devices. It allows the connection of
> most Cirrus Logic devices on mini-cards, as well as allowing
> connection of various application processor systems to provide a
> full evaluation platform. This driver supports the board
> controller chip on the Lochnagar board.
> 
> Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---

Applied to clk-next
Stephen Boyd April 23, 2019, 10:03 p.m. UTC | #3
Quoting Charles Keepax (2019-03-19 06:37:00)
> Lochnagar is an evaluation and development board for Cirrus
> Logic Smart CODEC and Amp devices. It allows the connection of
> most Cirrus Logic devices on mini-cards, as well as allowing
> connection of various application processor systems to provide a
> full evaluation platform. This driver supports the board
> controller chip on the Lochnagar board.
> 
> The Lochnagar can take several input clocks from the host system,
> provides several of its own clock sources, and provides extensive
> routing options for those clocks to be supplied to the attached
> CODEC/Amp device.
> 
> Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
> ---

Applied to clk-next

You may want to take a look at the clk-parent-rewrite-1 branch on
clk.git and see if you can convert this driver to use that method
instead of of_clk_get_parent_name(). It may simplify some of the driver.

Patch
diff mbox series

diff --git a/Documentation/devicetree/bindings/clock/cirrus,lochnagar.txt b/Documentation/devicetree/bindings/clock/cirrus,lochnagar.txt
new file mode 100644
index 0000000000000..b8d8ef3bdc5f3
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/cirrus,lochnagar.txt
@@ -0,0 +1,93 @@ 
+Cirrus Logic Lochnagar Audio Development Board
+
+Lochnagar is an evaluation and development board for Cirrus Logic
+Smart CODEC and Amp devices. It allows the connection of most Cirrus
+Logic devices on mini-cards, as well as allowing connection of
+various application processor systems to provide a full evaluation
+platform.  Audio system topology, clocking and power can all be
+controlled through the Lochnagar, allowing the device under test
+to be used in a variety of possible use cases.
+
+This binding document describes the binding for the clock portion of
+the driver.
+
+Also see these documents for generic binding information:
+  [1] Clock : ../clock/clock-bindings.txt
+
+And these for relevant defines:
+  [2] include/dt-bindings/clock/lochnagar.h
+
+This binding must be part of the Lochnagar MFD binding:
+  [3] ../mfd/cirrus,lochnagar.txt
+
+Required properties:
+
+  - compatible : One of the following strings:
+                 "cirrus,lochnagar1-clk"
+                 "cirrus,lochnagar2-clk"
+
+  - #clock-cells : Must be 1. The first cell indicates the clock
+    number, see [2] for available clocks and [1].
+
+Optional properties:
+
+  - clocks : Must contain an entry for each clock in clock-names.
+  - clock-names : May contain entries for each of the following
+    clocks:
+     - ln-cdc-clkout : Output clock from CODEC card.
+     - ln-dsp-clkout : Output clock from DSP card.
+     - ln-gf-mclk1,ln-gf-mclk2,ln-gf-mclk3,ln-gf-mclk4 : Optional
+       input audio clocks from host system.
+     - ln-psia1-mclk, ln-psia2-mclk : Optional input audio clocks from
+       external connector.
+     - ln-spdif-clkout : Optional input audio clock from SPDIF.
+     - ln-adat-mclk : Optional input audio clock from ADAT.
+     - ln-pmic-32k : On board fixed clock.
+     - ln-clk-12m : On board fixed clock.
+     - ln-clk-11m : On board fixed clock.
+     - ln-clk-24m : On board fixed clock.
+     - ln-clk-22m : On board fixed clock.
+     - ln-clk-8m : On board fixed clock.
+     - ln-usb-clk-24m : On board fixed clock.
+     - ln-usb-clk-12m : On board fixed clock.
+
+  - assigned-clocks : A list of Lochnagar clocks to be reparented, see
+    [2] for available clocks.
+  - assigned-clock-parents : Parents to be assigned to the clocks
+    listed in "assigned-clocks".
+
+Optional nodes:
+
+  - fixed-clock nodes may be registered for the following on board clocks:
+     - ln-pmic-32k : 32768 Hz
+     - ln-clk-12m : 12288000 Hz
+     - ln-clk-11m : 11298600 Hz
+     - ln-clk-24m : 24576000 Hz
+     - ln-clk-22m : 22579200 Hz
+     - ln-clk-8m : 8192000 Hz
+     - ln-usb-clk-24m : 24576000 Hz
+     - ln-usb-clk-12m : 12288000 Hz
+
+Example:
+
+lochnagar {
+	lochnagar-clk {
+		compatible = "cirrus,lochnagar2-clk";
+
+		#clock-cells = <1>;
+
+		clocks = <&clk-audio>, <&clk_pmic>;
+		clock-names = "ln-gf-mclk2", "ln-pmic-32k";
+
+		assigned-clocks = <&lochnagar-clk LOCHNAGAR_CDC_MCLK1>,
+				  <&lochnagar-clk LOCHNAGAR_CDC_MCLK2>;
+		assigned-clock-parents = <&clk-audio>,
+					 <&clk-pmic>;
+	};
+
+	clk-pmic: clk-pmic {
+		compatible = "fixed-clock";
+		clock-cells = <0>;
+		clock-frequency = <32768>;
+	};
+};