diff mbox

[4/7] clk: add clk-asm9260 driver

Message ID 1410646408-28901-5-git-send-email-linux@rempel-privat.de (mailing list archive)
State New, archived
Headers show

Commit Message

Oleksij Rempel Sept. 13, 2014, 10:13 p.m. UTC
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
---
 drivers/clk/Makefile      |   1 +
 drivers/clk/clk-asm9260.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 160 insertions(+)
 create mode 100644 drivers/clk/clk-asm9260.c

Comments

Alexandre Belloni Sept. 17, 2014, 1:54 p.m. UTC | #1
Hi,

Please have a look at https://lkml.org/lkml/2014/5/14/598 which describe
the preferred way of implementing clocks in the CCF where you only
declare the clock generator in the DT instead of each separate clocks.

On 14/09/2014 at 00:13:25 +0200, Oleksij Rempel wrote :
> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
> ---
>  drivers/clk/Makefile      |   1 +
>  drivers/clk/clk-asm9260.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 160 insertions(+)
>  create mode 100644 drivers/clk/clk-asm9260.c
> 
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 567f102..351dd48 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_ARCH_HI3xxx)		+= hisilicon/
>  obj-$(CONFIG_ARCH_HIP04)		+= hisilicon/
>  obj-$(CONFIG_ARCH_HIX5HD2)		+= hisilicon/
>  obj-$(CONFIG_COMMON_CLK_KEYSTONE)	+= keystone/
> +obj-$(CONFIG_MACH_ASM9260)		+= clk-asm9260.o
>  ifeq ($(CONFIG_COMMON_CLK), y)
>  obj-$(CONFIG_ARCH_MMP)			+= mmp/
>  endif
> diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c
> new file mode 100644
> index 0000000..396cc09
> --- /dev/null
> +++ b/drivers/clk/clk-asm9260.c
> @@ -0,0 +1,159 @@
> +/*
> + * U300 clock implementation
> + * Copyright (C) 2007-2012 ST-Ericsson AB
> + * License terms: GNU General Public License (GPL) version 2
> + * Author: Linus Walleij <linus.walleij@stericsson.com>
> + * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
> + */
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/clk-provider.h>
> +#include <linux/spinlock.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +
> +static DEFINE_SPINLOCK(asm9260_clk_lock);
> +
> +struct asm9260_clk {
> +	void __iomem    *reg;
> +	char *parent_name;
> +};
> +
> +static void __iomem *asm9260_get_sreg(struct device_node *node)
> +{
> +	u32 reg;
> +	void __iomem *iomem;
> +	struct device_node *srnp;
> +	int ret;
> +
> +	ret = of_property_read_u32(node, "reg", &reg);
> +	if (WARN_ON(ret))
> +		return NULL;
> +
> +	srnp = of_find_compatible_node(NULL, NULL, "alpscale,asm9260-sregs");
> +	iomem = of_iomap(srnp, 0);
> +	iomem += reg;
> +
> +	return iomem;
> +}
> +
> +/*
> + * On this chip gate used to disable or to update clock
> + * after new source was selected
> + */
> +
> +static void __init asm9260_gate_init(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	void __iomem *iomem;
> +	const char *parent_name;
> +	u32 bit;
> +	int ret;
> +
> +	iomem = asm9260_get_sreg(node);
> +	parent_name = of_clk_get_parent_name(node, 0);
> +
> +	ret = of_property_read_u32(node, "bit-index", &bit);
> +	if (WARN_ON(ret))
> +		return;
> +
> +	clk = clk_register_gate(NULL, clk_name, parent_name,
> +			CLK_SET_RATE_PARENT, iomem, bit, 0,
> +			&asm9260_clk_lock);
> +
> +	if (!IS_ERR(clk))
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +CLK_OF_DECLARE(asm9260_gate, "alpscale,asm9260-gate-clock", asm9260_gate_init);
> +
> +
> +static void __init asm9260_div_init(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	void __iomem *iomem;
> +	const char *parent_name;
> +
> +	iomem = asm9260_get_sreg(node);
> +
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	clk = clk_register_divider(NULL, clk_name, parent_name,
> +			CLK_SET_RATE_PARENT, iomem, 0, 8, CLK_DIVIDER_ONE_BASED,
> +			&asm9260_clk_lock);
> +
> +	if (!IS_ERR(clk))
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +CLK_OF_DECLARE(asm9260_div, "alpscale,asm9260-div-clock", asm9260_div_init);
> +
> +/*
> + * Simple one bit MUX for two sources
> + */
> +static void __init asm9260_bimux_init(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	u8 num_parents;
> +	void __iomem *iomem;
> +	const char **parent_names;
> +	int ret, i;
> +	u32 *table;
> +
> +	iomem = asm9260_get_sreg(node);
> +	if (!iomem)
> +		return;
> +
> +	num_parents = of_clk_get_parent_count(node);
> +	if (WARN_ON(!num_parents || num_parents > 2))
> +		return;
> +
> +	parent_names = kzalloc(sizeof(char *) * num_parents, GFP_KERNEL);
> +	if (WARN_ON(!parent_names))
> +		return;
> +
> +	table = kzalloc(sizeof(u32) * num_parents, GFP_KERNEL);
> +	if (WARN_ON(!table))
> +		return;
> +
> +	ret = of_property_read_u32_array(node, "mux-table", table,
> +			num_parents);
> +	if (WARN_ON(ret))
> +		return;
> +
> +	for (i = 0; i < num_parents; i++)
> +		parent_names[i] = of_clk_get_parent_name(node, i);
> +
> +	clk = clk_register_mux_table(NULL, clk_name, parent_names,
> +			num_parents, 0, iomem, 0, 1, 0, table,
> +			&asm9260_clk_lock);
> +
> +	if (!IS_ERR(clk))
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +CLK_OF_DECLARE(asm9260_bimux, "alpscale,asm9260-bimux-clock", asm9260_bimux_init);
> +
> +static void __init asm9260_pll_init(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	u32 rate;
> +	void __iomem *iomem;
> +	const char *parent_name;
> +	u32 accuracy = 0;
> +
> +	iomem = asm9260_get_sreg(node);
> +	rate = (ioread32(iomem) & 0xffff) * 1000000;
> +
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	accuracy = clk_get_accuracy(__clk_lookup(parent_name));
> +	clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, parent_name,
> +			0, rate,
> +			accuracy);
> +
> +	if (!IS_ERR(clk))
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +CLK_OF_DECLARE(asm9260_pll, "alpscale,asm9260-pll-clock", asm9260_pll_init);
> -- 
> 1.9.1
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Oleksij Rempel Sept. 18, 2014, 6:46 a.m. UTC | #2
Am 17.09.2014 um 15:54 schrieb Alexandre Belloni:
> Hi,
> 
> Please have a look at https://lkml.org/lkml/2014/5/14/598 which describe
> the preferred way of implementing clocks in the CCF where you only
> declare the clock generator in the DT instead of each separate clocks.

hm... if i see it correctly. i will need to move almost everything from
DT to clk-driver. And i will need to create separate driver for other
SoC provided by AlphaScale. On other side with current (generic) driver
i will need only to change DT and it will work.

May be instead of going qcom way, it will be better to have generic
gate, div and mux bindings for DT? Sure it will make DT a bit fuzzy, but
it will dramatically reduce kernel code and reduce the time of providing
code to upstream.

I don't think drivers/clk/qcom/gcc-msm8974.c is less complicated solution.
Alexandre Belloni Sept. 18, 2014, 7:56 a.m. UTC | #3
Hi,

On 18/09/2014 at 08:46:57 +0200, Oleksij Rempel wrote :
> Am 17.09.2014 um 15:54 schrieb Alexandre Belloni:
> > Hi,
> > 
> > Please have a look at https://lkml.org/lkml/2014/5/14/598 which describe
> > the preferred way of implementing clocks in the CCF where you only
> > declare the clock generator in the DT instead of each separate clocks.
> 
> hm... if i see it correctly. i will need to move almost everything from
> DT to clk-driver. And i will need to create separate driver for other
> SoC provided by AlphaScale. On other side with current (generic) driver
> i will need only to change DT and it will work.
> 
> May be instead of going qcom way, it will be better to have generic
> gate, div and mux bindings for DT? Sure it will make DT a bit fuzzy, but
> it will dramatically reduce kernel code and reduce the time of providing
> code to upstream.
> 
> I don't think drivers/clk/qcom/gcc-msm8974.c is less complicated solution.

IT is not less complicated but it is more flexible (for exemple when
setting flags on individual clocks, like CLK_SET_RATE_PARENT or
CLK_IGNORE_UNUSED). Describing all the individual clocks in DT is a
mistake we made in at91 and it will definitely bother you in the future,
for example when you realize that your clock controller is also taking
care of reset or power management.

Also, this is the kind of driver you write only once per SoC so it is
about the same doing it in DT or in C with the added advantage that
doing it in C takes less memory and is probably faster.

You can have a look at how it has been done for the berlin SoCs to see
how you can easily reuse code between drivers.
Oleksij Rempel Sept. 18, 2014, 8:46 a.m. UTC | #4
Am 18.09.2014 um 09:56 schrieb Alexandre Belloni:
> Hi,
> 
> On 18/09/2014 at 08:46:57 +0200, Oleksij Rempel wrote :
>> Am 17.09.2014 um 15:54 schrieb Alexandre Belloni:
>>> Hi,
>>>
>>> Please have a look at https://lkml.org/lkml/2014/5/14/598 which describe
>>> the preferred way of implementing clocks in the CCF where you only
>>> declare the clock generator in the DT instead of each separate clocks.
>>
>> hm... if i see it correctly. i will need to move almost everything from
>> DT to clk-driver. And i will need to create separate driver for other
>> SoC provided by AlphaScale. On other side with current (generic) driver
>> i will need only to change DT and it will work.
>>
>> May be instead of going qcom way, it will be better to have generic
>> gate, div and mux bindings for DT? Sure it will make DT a bit fuzzy, but
>> it will dramatically reduce kernel code and reduce the time of providing
>> code to upstream.
>>
>> I don't think drivers/clk/qcom/gcc-msm8974.c is less complicated solution.
> 
> IT is not less complicated but it is more flexible (for exemple when
> setting flags on individual clocks, like CLK_SET_RATE_PARENT or
> CLK_IGNORE_UNUSED). Describing all the individual clocks in DT is a
> mistake we made in at91 and it will definitely bother you in the future,
> for example when you realize that your clock controller is also taking
> care of reset or power management.
> 
> Also, this is the kind of driver you write only once per SoC so it is
> about the same doing it in DT or in C with the added advantage that
> doing it in C takes less memory and is probably faster.
> 
> You can have a look at how it has been done for the berlin SoCs to see
> how you can easily reuse code between drivers.

Ok, thank you. i'll rewrite it.
Oleksij Rempel Sept. 20, 2014, 6:06 p.m. UTC | #5
Am 18.09.2014 um 09:56 schrieb Alexandre Belloni:
> Hi,
> 
> On 18/09/2014 at 08:46:57 +0200, Oleksij Rempel wrote :
>> Am 17.09.2014 um 15:54 schrieb Alexandre Belloni:
>>> Hi,
>>>
>>> Please have a look at https://lkml.org/lkml/2014/5/14/598 which describe
>>> the preferred way of implementing clocks in the CCF where you only
>>> declare the clock generator in the DT instead of each separate clocks.
>>
>> hm... if i see it correctly. i will need to move almost everything from
>> DT to clk-driver. And i will need to create separate driver for other
>> SoC provided by AlphaScale. On other side with current (generic) driver
>> i will need only to change DT and it will work.
>>
>> May be instead of going qcom way, it will be better to have generic
>> gate, div and mux bindings for DT? Sure it will make DT a bit fuzzy, but
>> it will dramatically reduce kernel code and reduce the time of providing
>> code to upstream.
>>
>> I don't think drivers/clk/qcom/gcc-msm8974.c is less complicated solution.
> 
> IT is not less complicated but it is more flexible (for exemple when
> setting flags on individual clocks, like CLK_SET_RATE_PARENT or
> CLK_IGNORE_UNUSED). Describing all the individual clocks in DT is a
> mistake we made in at91 and it will definitely bother you in the future,
> for example when you realize that your clock controller is also taking
> care of reset or power management.
> 
> Also, this is the kind of driver you write only once per SoC so it is
> about the same doing it in DT or in C with the added advantage that
> doing it in C takes less memory and is probably faster.
> 
> You can have a look at how it has been done for the berlin SoCs to see
> how you can easily reuse code between drivers.

What is the correct way to handle/define i2s MCLK input? I have I2S mux
clock with choice of 3 sources: Xtal, PLL and MCLK. One of pins can be
configured as MCLK src. Should i define fixedrate-clk?
Alexandre Belloni Sept. 20, 2014, 6:43 p.m. UTC | #6
On 20/09/2014 at 20:06:21 +0200, Oleksij Rempel wrote :
> Am 18.09.2014 um 09:56 schrieb Alexandre Belloni:
> > You can have a look at how it has been done for the berlin SoCs to see
> > how you can easily reuse code between drivers.
> 
> What is the correct way to handle/define i2s MCLK input? I have I2S mux
> clock with choice of 3 sources: Xtal, PLL and MCLK. One of pins can be
> configured as MCLK src. Should i define fixedrate-clk?
> 

It depends on where is your mux, if it is in the I2S controller adress
range, I would simply give 3 parent clocks to your I2S controller, like:

i2s@8fffffff {
 compatible = "...";
 reg = <0x8fffffff 0x100>;
 clocks = <&xtal, &clkc CLKID_MCK, &clkc CLKID_PLL>;
}

If it is part of your clock generator, then simply register a clock mux
with 3 parents as part of your clock controller binding. And use
something like:
 clocks = <&clck CLKID_I2S>;

Have a look at how this is done for berlin2, search for
clk_register_mux()
diff mbox

Patch

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 567f102..351dd48 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -38,6 +38,7 @@  obj-$(CONFIG_ARCH_HI3xxx)		+= hisilicon/
 obj-$(CONFIG_ARCH_HIP04)		+= hisilicon/
 obj-$(CONFIG_ARCH_HIX5HD2)		+= hisilicon/
 obj-$(CONFIG_COMMON_CLK_KEYSTONE)	+= keystone/
+obj-$(CONFIG_MACH_ASM9260)		+= clk-asm9260.o
 ifeq ($(CONFIG_COMMON_CLK), y)
 obj-$(CONFIG_ARCH_MMP)			+= mmp/
 endif
diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c
new file mode 100644
index 0000000..396cc09
--- /dev/null
+++ b/drivers/clk/clk-asm9260.c
@@ -0,0 +1,159 @@ 
+/*
+ * U300 clock implementation
+ * Copyright (C) 2007-2012 ST-Ericsson AB
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+static DEFINE_SPINLOCK(asm9260_clk_lock);
+
+struct asm9260_clk {
+	void __iomem    *reg;
+	char *parent_name;
+};
+
+static void __iomem *asm9260_get_sreg(struct device_node *node)
+{
+	u32 reg;
+	void __iomem *iomem;
+	struct device_node *srnp;
+	int ret;
+
+	ret = of_property_read_u32(node, "reg", &reg);
+	if (WARN_ON(ret))
+		return NULL;
+
+	srnp = of_find_compatible_node(NULL, NULL, "alpscale,asm9260-sregs");
+	iomem = of_iomap(srnp, 0);
+	iomem += reg;
+
+	return iomem;
+}
+
+/*
+ * On this chip gate used to disable or to update clock
+ * after new source was selected
+ */
+
+static void __init asm9260_gate_init(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	void __iomem *iomem;
+	const char *parent_name;
+	u32 bit;
+	int ret;
+
+	iomem = asm9260_get_sreg(node);
+	parent_name = of_clk_get_parent_name(node, 0);
+
+	ret = of_property_read_u32(node, "bit-index", &bit);
+	if (WARN_ON(ret))
+		return;
+
+	clk = clk_register_gate(NULL, clk_name, parent_name,
+			CLK_SET_RATE_PARENT, iomem, bit, 0,
+			&asm9260_clk_lock);
+
+	if (!IS_ERR(clk))
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(asm9260_gate, "alpscale,asm9260-gate-clock", asm9260_gate_init);
+
+
+static void __init asm9260_div_init(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	void __iomem *iomem;
+	const char *parent_name;
+
+	iomem = asm9260_get_sreg(node);
+
+	parent_name = of_clk_get_parent_name(node, 0);
+	clk = clk_register_divider(NULL, clk_name, parent_name,
+			CLK_SET_RATE_PARENT, iomem, 0, 8, CLK_DIVIDER_ONE_BASED,
+			&asm9260_clk_lock);
+
+	if (!IS_ERR(clk))
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(asm9260_div, "alpscale,asm9260-div-clock", asm9260_div_init);
+
+/*
+ * Simple one bit MUX for two sources
+ */
+static void __init asm9260_bimux_init(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	u8 num_parents;
+	void __iomem *iomem;
+	const char **parent_names;
+	int ret, i;
+	u32 *table;
+
+	iomem = asm9260_get_sreg(node);
+	if (!iomem)
+		return;
+
+	num_parents = of_clk_get_parent_count(node);
+	if (WARN_ON(!num_parents || num_parents > 2))
+		return;
+
+	parent_names = kzalloc(sizeof(char *) * num_parents, GFP_KERNEL);
+	if (WARN_ON(!parent_names))
+		return;
+
+	table = kzalloc(sizeof(u32) * num_parents, GFP_KERNEL);
+	if (WARN_ON(!table))
+		return;
+
+	ret = of_property_read_u32_array(node, "mux-table", table,
+			num_parents);
+	if (WARN_ON(ret))
+		return;
+
+	for (i = 0; i < num_parents; i++)
+		parent_names[i] = of_clk_get_parent_name(node, i);
+
+	clk = clk_register_mux_table(NULL, clk_name, parent_names,
+			num_parents, 0, iomem, 0, 1, 0, table,
+			&asm9260_clk_lock);
+
+	if (!IS_ERR(clk))
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(asm9260_bimux, "alpscale,asm9260-bimux-clock", asm9260_bimux_init);
+
+static void __init asm9260_pll_init(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	u32 rate;
+	void __iomem *iomem;
+	const char *parent_name;
+	u32 accuracy = 0;
+
+	iomem = asm9260_get_sreg(node);
+	rate = (ioread32(iomem) & 0xffff) * 1000000;
+
+	parent_name = of_clk_get_parent_name(node, 0);
+	accuracy = clk_get_accuracy(__clk_lookup(parent_name));
+	clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, parent_name,
+			0, rate,
+			accuracy);
+
+	if (!IS_ERR(clk))
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(asm9260_pll, "alpscale,asm9260-pll-clock", asm9260_pll_init);