Skip to content

Commit 3fde0e1

Browse files
Jolly ShahMichal Simek
authored andcommitted
drivers: clk: Add ZynqMP clock driver
This patch adds CCF compliant clock driver for ZynqMP. Clock driver queries supported clock information from firmware and regiters pll and output clocks with CCF. Signed-off-by: Rajan Vaja <rajan.vaja@xilinx.com> Signed-off-by: Tejas Patel <tejasp@xilinx.com> Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@xilinx.com> Signed-off-by: Jolly Shah <jolly.shah@xilinx.com> Acked-by: Olof Johansson <olof@lixom.net> Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
1 parent 26372d0 commit 3fde0e1

File tree

11 files changed

+1638
-0
lines changed

11 files changed

+1638
-0
lines changed

drivers/clk/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,5 +299,6 @@ source "drivers/clk/sunxi-ng/Kconfig"
299299
source "drivers/clk/tegra/Kconfig"
300300
source "drivers/clk/ti/Kconfig"
301301
source "drivers/clk/uniphier/Kconfig"
302+
source "drivers/clk/zynqmp/Kconfig"
302303

303304
endmenu

drivers/clk/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,4 @@ obj-$(CONFIG_X86) += x86/
108108
endif
109109
obj-$(CONFIG_ARCH_ZX) += zte/
110110
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
111+
obj-$(CONFIG_COMMON_CLK_ZYNQMP) += zynqmp/

drivers/clk/zynqmp/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
config COMMON_CLK_ZYNQMP
4+
bool "Support for Xilinx ZynqMP Ultrascale+ clock controllers"
5+
depends on ARCH_ZYNQMP || COMPILE_TEST
6+
depends on ZYNQMP_FIRMWARE
7+
help
8+
Support for the Zynqmp Ultrascale clock controller.
9+
It has a dependency on the PMU firmware.
10+
Say Y if you want to include clock support.

drivers/clk/zynqmp/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
# Zynq Ultrascale+ MPSoC clock specific Makefile
3+
4+
obj-$(CONFIG_ARCH_ZYNQMP) += pll.o clk-gate-zynqmp.o divider.o clk-mux-zynqmp.o clkc.o

drivers/clk/zynqmp/clk-gate-zynqmp.c

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Zynq UltraScale+ MPSoC clock controller
4+
*
5+
* Copyright (C) 2016-2018 Xilinx
6+
*
7+
* Gated clock implementation
8+
*/
9+
10+
#include <linux/clk-provider.h>
11+
#include <linux/slab.h>
12+
#include "clk-zynqmp.h"
13+
14+
/**
15+
* struct clk_gate - gating clock
16+
* @hw: handle between common and hardware-specific interfaces
17+
* @flags: hardware-specific flags
18+
* @clk_id: Id of clock
19+
*/
20+
struct zynqmp_clk_gate {
21+
struct clk_hw hw;
22+
u8 flags;
23+
u32 clk_id;
24+
};
25+
26+
#define to_zynqmp_clk_gate(_hw) container_of(_hw, struct zynqmp_clk_gate, hw)
27+
28+
/**
29+
* zynqmp_clk_gate_enable() - Enable clock
30+
* @hw: handle between common and hardware-specific interfaces
31+
*
32+
* Return: 0 on success else error code
33+
*/
34+
static int zynqmp_clk_gate_enable(struct clk_hw *hw)
35+
{
36+
struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
37+
const char *clk_name = clk_hw_get_name(hw);
38+
u32 clk_id = gate->clk_id;
39+
int ret;
40+
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
41+
42+
ret = eemi_ops->clock_enable(clk_id);
43+
44+
if (ret)
45+
pr_warn_once("%s() clock enabled failed for %s, ret = %d\n",
46+
__func__, clk_name, ret);
47+
48+
return ret;
49+
}
50+
51+
/*
52+
* zynqmp_clk_gate_disable() - Disable clock
53+
* @hw: handle between common and hardware-specific interfaces
54+
*/
55+
static void zynqmp_clk_gate_disable(struct clk_hw *hw)
56+
{
57+
struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
58+
const char *clk_name = clk_hw_get_name(hw);
59+
u32 clk_id = gate->clk_id;
60+
int ret;
61+
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
62+
63+
ret = eemi_ops->clock_disable(clk_id);
64+
65+
if (ret)
66+
pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
67+
__func__, clk_name, ret);
68+
}
69+
70+
/**
71+
* zynqmp_clk_gate_is_enable() - Check clock state
72+
* @hw: handle between common and hardware-specific interfaces
73+
*
74+
* Return: 1 if enabled, 0 if disabled else error code
75+
*/
76+
static int zynqmp_clk_gate_is_enabled(struct clk_hw *hw)
77+
{
78+
struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
79+
const char *clk_name = clk_hw_get_name(hw);
80+
u32 clk_id = gate->clk_id;
81+
int state, ret;
82+
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
83+
84+
ret = eemi_ops->clock_getstate(clk_id, &state);
85+
if (ret) {
86+
pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
87+
__func__, clk_name, ret);
88+
return -EIO;
89+
}
90+
91+
return state ? 1 : 0;
92+
}
93+
94+
static const struct clk_ops zynqmp_clk_gate_ops = {
95+
.enable = zynqmp_clk_gate_enable,
96+
.disable = zynqmp_clk_gate_disable,
97+
.is_enabled = zynqmp_clk_gate_is_enabled,
98+
};
99+
100+
/**
101+
* zynqmp_clk_register_gate() - Register a gate clock with the clock framework
102+
* @name: Name of this clock
103+
* @clk_id: Id of this clock
104+
* @parents: Name of this clock's parents
105+
* @num_parents: Number of parents
106+
* @nodes: Clock topology node
107+
*
108+
* Return: clock hardware of the registered clock gate
109+
*/
110+
struct clk_hw *zynqmp_clk_register_gate(const char *name, u32 clk_id,
111+
const char * const *parents,
112+
u8 num_parents,
113+
const struct clock_topology *nodes)
114+
{
115+
struct zynqmp_clk_gate *gate;
116+
struct clk_hw *hw;
117+
int ret;
118+
struct clk_init_data init;
119+
120+
/* allocate the gate */
121+
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
122+
if (!gate)
123+
return ERR_PTR(-ENOMEM);
124+
125+
init.name = name;
126+
init.ops = &zynqmp_clk_gate_ops;
127+
init.flags = nodes->flag;
128+
init.parent_names = parents;
129+
init.num_parents = 1;
130+
131+
/* struct clk_gate assignments */
132+
gate->flags = nodes->type_flag;
133+
gate->hw.init = &init;
134+
gate->clk_id = clk_id;
135+
136+
hw = &gate->hw;
137+
ret = clk_hw_register(NULL, hw);
138+
if (ret) {
139+
kfree(gate);
140+
hw = ERR_PTR(ret);
141+
}
142+
143+
return hw;
144+
}

drivers/clk/zynqmp/clk-mux-zynqmp.c

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Zynq UltraScale+ MPSoC mux
4+
*
5+
* Copyright (C) 2016-2018 Xilinx
6+
*/
7+
8+
#include <linux/clk-provider.h>
9+
#include <linux/slab.h>
10+
#include "clk-zynqmp.h"
11+
12+
/*
13+
* DOC: basic adjustable multiplexer clock that cannot gate
14+
*
15+
* Traits of this clock:
16+
* prepare - clk_prepare only ensures that parents are prepared
17+
* enable - clk_enable only ensures that parents are enabled
18+
* rate - rate is only affected by parent switching. No clk_set_rate support
19+
* parent - parent is adjustable through clk_set_parent
20+
*/
21+
22+
/**
23+
* struct zynqmp_clk_mux - multiplexer clock
24+
*
25+
* @hw: handle between common and hardware-specific interfaces
26+
* @flags: hardware-specific flags
27+
* @clk_id: Id of clock
28+
*/
29+
struct zynqmp_clk_mux {
30+
struct clk_hw hw;
31+
u8 flags;
32+
u32 clk_id;
33+
};
34+
35+
#define to_zynqmp_clk_mux(_hw) container_of(_hw, struct zynqmp_clk_mux, hw)
36+
37+
/**
38+
* zynqmp_clk_mux_get_parent() - Get parent of clock
39+
* @hw: handle between common and hardware-specific interfaces
40+
*
41+
* Return: Parent index
42+
*/
43+
static u8 zynqmp_clk_mux_get_parent(struct clk_hw *hw)
44+
{
45+
struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
46+
const char *clk_name = clk_hw_get_name(hw);
47+
u32 clk_id = mux->clk_id;
48+
u32 val;
49+
int ret;
50+
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
51+
52+
ret = eemi_ops->clock_getparent(clk_id, &val);
53+
54+
if (ret)
55+
pr_warn_once("%s() getparent failed for clock: %s, ret = %d\n",
56+
__func__, clk_name, ret);
57+
58+
return val;
59+
}
60+
61+
/**
62+
* zynqmp_clk_mux_set_parent() - Set parent of clock
63+
* @hw: handle between common and hardware-specific interfaces
64+
* @index: Parent index
65+
*
66+
* Return: 0 on success else error+reason
67+
*/
68+
static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index)
69+
{
70+
struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
71+
const char *clk_name = clk_hw_get_name(hw);
72+
u32 clk_id = mux->clk_id;
73+
int ret;
74+
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
75+
76+
ret = eemi_ops->clock_setparent(clk_id, index);
77+
78+
if (ret)
79+
pr_warn_once("%s() set parent failed for clock: %s, ret = %d\n",
80+
__func__, clk_name, ret);
81+
82+
return ret;
83+
}
84+
85+
static const struct clk_ops zynqmp_clk_mux_ops = {
86+
.get_parent = zynqmp_clk_mux_get_parent,
87+
.set_parent = zynqmp_clk_mux_set_parent,
88+
.determine_rate = __clk_mux_determine_rate,
89+
};
90+
91+
static const struct clk_ops zynqmp_clk_mux_ro_ops = {
92+
.get_parent = zynqmp_clk_mux_get_parent,
93+
};
94+
95+
/**
96+
* zynqmp_clk_register_mux() - Register a mux table with the clock
97+
* framework
98+
* @name: Name of this clock
99+
* @clk_id: Id of this clock
100+
* @parents: Name of this clock's parents
101+
* @num_parents: Number of parents
102+
* @nodes: Clock topology node
103+
*
104+
* Return: clock hardware of the registered clock mux
105+
*/
106+
struct clk_hw *zynqmp_clk_register_mux(const char *name, u32 clk_id,
107+
const char * const *parents,
108+
u8 num_parents,
109+
const struct clock_topology *nodes)
110+
{
111+
struct zynqmp_clk_mux *mux;
112+
struct clk_hw *hw;
113+
struct clk_init_data init;
114+
int ret;
115+
116+
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
117+
if (!mux)
118+
return ERR_PTR(-ENOMEM);
119+
120+
init.name = name;
121+
if (nodes->type_flag & CLK_MUX_READ_ONLY)
122+
init.ops = &zynqmp_clk_mux_ro_ops;
123+
else
124+
init.ops = &zynqmp_clk_mux_ops;
125+
init.flags = nodes->flag;
126+
init.parent_names = parents;
127+
init.num_parents = num_parents;
128+
mux->flags = nodes->type_flag;
129+
mux->hw.init = &init;
130+
mux->clk_id = clk_id;
131+
132+
hw = &mux->hw;
133+
ret = clk_hw_register(NULL, hw);
134+
if (ret) {
135+
kfree(hw);
136+
hw = ERR_PTR(ret);
137+
}
138+
139+
return hw;
140+
}
141+
EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux);

drivers/clk/zynqmp/clk-zynqmp.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Copyright (C) 2016-2018 Xilinx
4+
*/
5+
6+
#ifndef __LINUX_CLK_ZYNQMP_H_
7+
#define __LINUX_CLK_ZYNQMP_H_
8+
9+
#include <linux/spinlock.h>
10+
11+
#include <linux/firmware/xlnx-zynqmp.h>
12+
13+
/* Clock APIs payload parameters */
14+
#define CLK_GET_NAME_RESP_LEN 16
15+
#define CLK_GET_TOPOLOGY_RESP_WORDS 3
16+
#define CLK_GET_PARENTS_RESP_WORDS 3
17+
#define CLK_GET_ATTR_RESP_WORDS 1
18+
19+
enum topology_type {
20+
TYPE_INVALID,
21+
TYPE_MUX,
22+
TYPE_PLL,
23+
TYPE_FIXEDFACTOR,
24+
TYPE_DIV1,
25+
TYPE_DIV2,
26+
TYPE_GATE,
27+
};
28+
29+
/**
30+
* struct clock_topology - Clock topology
31+
* @type: Type of topology
32+
* @flag: Topology flags
33+
* @type_flag: Topology type specific flag
34+
*/
35+
struct clock_topology {
36+
u32 type;
37+
u32 flag;
38+
u32 type_flag;
39+
};
40+
41+
struct clk_hw *zynqmp_clk_register_pll(const char *name, u32 clk_id,
42+
const char * const *parents,
43+
u8 num_parents,
44+
const struct clock_topology *nodes);
45+
46+
struct clk_hw *zynqmp_clk_register_gate(const char *name, u32 clk_id,
47+
const char * const *parents,
48+
u8 num_parents,
49+
const struct clock_topology *nodes);
50+
51+
struct clk_hw *zynqmp_clk_register_divider(const char *name,
52+
u32 clk_id,
53+
const char * const *parents,
54+
u8 num_parents,
55+
const struct clock_topology *nodes);
56+
57+
struct clk_hw *zynqmp_clk_register_mux(const char *name, u32 clk_id,
58+
const char * const *parents,
59+
u8 num_parents,
60+
const struct clock_topology *nodes);
61+
62+
struct clk_hw *zynqmp_clk_register_fixed_factor(const char *name,
63+
u32 clk_id,
64+
const char * const *parents,
65+
u8 num_parents,
66+
const struct clock_topology *nodes);
67+
68+
#endif

0 commit comments

Comments
 (0)