aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCodrin Ciubotariu <codrin.ciubotariu@microchip.com>2018-06-18 10:12:36 -0400
committerStephen Boyd <sboyd@kernel.org>2018-07-06 14:12:20 -0400
commit96e4ea8c253d12079db4d7174990455b4bbf0c87 (patch)
treea0cb6049dad098bcad9e8a11fcdc763be3482a90
parent5f273c0d9facfea124f6dea9de41775085056cc2 (diff)
clk: at91: add I2S clock mux driver
This driver is a simple muxing driver that controls the I2S's clock input by using syscon/regmap to change the parent. The available inputs can be peripheral clock and generated clock. Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> [sboyd@kernel.org: Fix SPDX tag comment style] Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-rw-r--r--arch/arm/mach-at91/Kconfig4
-rw-r--r--drivers/clk/at91/Makefile1
-rw-r--r--drivers/clk/at91/clk-i2s-mux.c116
3 files changed, 121 insertions, 0 deletions
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 1254bf9d91b4..903f23c309df 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -27,6 +27,7 @@ config SOC_SAMA5D2
27 select HAVE_AT91_H32MX 27 select HAVE_AT91_H32MX
28 select HAVE_AT91_GENERATED_CLK 28 select HAVE_AT91_GENERATED_CLK
29 select HAVE_AT91_AUDIO_PLL 29 select HAVE_AT91_AUDIO_PLL
30 select HAVE_AT91_I2S_MUX_CLK
30 select PINCTRL_AT91PIO4 31 select PINCTRL_AT91PIO4
31 help 32 help
32 Select this if ou are using one of Microchip's SAMA5D2 family SoC. 33 Select this if ou are using one of Microchip's SAMA5D2 family SoC.
@@ -129,6 +130,9 @@ config HAVE_AT91_GENERATED_CLK
129config HAVE_AT91_AUDIO_PLL 130config HAVE_AT91_AUDIO_PLL
130 bool 131 bool
131 132
133config HAVE_AT91_I2S_MUX_CLK
134 bool
135
132config SOC_SAM_V4_V5 136config SOC_SAM_V4_V5
133 bool 137 bool
134 138
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 082596f37c1d..facc169ebb68 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o
13obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o 13obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o
14obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o 14obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
15obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o 15obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o
16obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o
diff --git a/drivers/clk/at91/clk-i2s-mux.c b/drivers/clk/at91/clk-i2s-mux.c
new file mode 100644
index 000000000000..f0c3c3079f04
--- /dev/null
+++ b/drivers/clk/at91/clk-i2s-mux.c
@@ -0,0 +1,116 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018 Microchip Technology Inc,
4 * Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
5 *
6 *
7 */
8
9#include <linux/clk-provider.h>
10#include <linux/of.h>
11#include <linux/mfd/syscon.h>
12#include <linux/regmap.h>
13#include <linux/slab.h>
14
15#include <soc/at91/atmel-sfr.h>
16
17#define I2S_BUS_NR 2
18
19struct clk_i2s_mux {
20 struct clk_hw hw;
21 struct regmap *regmap;
22 u8 bus_id;
23};
24
25#define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw)
26
27static u8 clk_i2s_mux_get_parent(struct clk_hw *hw)
28{
29 struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
30 u32 val;
31
32 regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val);
33
34 return (val & BIT(mux->bus_id)) >> mux->bus_id;
35}
36
37static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index)
38{
39 struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
40
41 return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL,
42 BIT(mux->bus_id), index << mux->bus_id);
43}
44
45static const struct clk_ops clk_i2s_mux_ops = {
46 .get_parent = clk_i2s_mux_get_parent,
47 .set_parent = clk_i2s_mux_set_parent,
48 .determine_rate = __clk_mux_determine_rate,
49};
50
51static struct clk_hw * __init
52at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
53 const char * const *parent_names,
54 unsigned int num_parents, u8 bus_id)
55{
56 struct clk_init_data init = {};
57 struct clk_i2s_mux *i2s_ck;
58 int ret;
59
60 i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL);
61 if (!i2s_ck)
62 return ERR_PTR(-ENOMEM);
63
64 init.name = name;
65 init.ops = &clk_i2s_mux_ops;
66 init.parent_names = parent_names;
67 init.num_parents = num_parents;
68
69 i2s_ck->hw.init = &init;
70 i2s_ck->bus_id = bus_id;
71 i2s_ck->regmap = regmap;
72
73 ret = clk_hw_register(NULL, &i2s_ck->hw);
74 if (ret) {
75 kfree(i2s_ck);
76 return ERR_PTR(ret);
77 }
78
79 return &i2s_ck->hw;
80}
81
82static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np)
83{
84 struct regmap *regmap_sfr;
85 u8 bus_id;
86 const char *parent_names[2];
87 struct device_node *i2s_mux_np;
88 struct clk_hw *hw;
89 int ret;
90
91 regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
92 if (IS_ERR(regmap_sfr))
93 return;
94
95 for_each_child_of_node(np, i2s_mux_np) {
96 if (of_property_read_u8(i2s_mux_np, "reg", &bus_id))
97 continue;
98
99 if (bus_id > I2S_BUS_NR)
100 continue;
101
102 ret = of_clk_parent_fill(i2s_mux_np, parent_names, 2);
103 if (ret != 2)
104 continue;
105
106 hw = at91_clk_i2s_mux_register(regmap_sfr, i2s_mux_np->name,
107 parent_names, 2, bus_id);
108 if (IS_ERR(hw))
109 continue;
110
111 of_clk_add_hw_provider(i2s_mux_np, of_clk_hw_simple_get, hw);
112 }
113}
114
115CLK_OF_DECLARE(sama5d2_clk_i2s_mux, "atmel,sama5d2-clk-i2s-mux",
116 of_sama5d2_clk_i2s_mux_setup);