Skip to content

Commit

Permalink
drivers: clk: Add ZynqMP clock driver
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jolly Shah authored and Michal Simek committed Oct 9, 2018
1 parent 26372d0 commit 3fde0e1
Show file tree
Hide file tree
Showing 11 changed files with 1,638 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/clk/Kconfig
Expand Up @@ -299,5 +299,6 @@ source "drivers/clk/sunxi-ng/Kconfig"
source "drivers/clk/tegra/Kconfig"
source "drivers/clk/ti/Kconfig"
source "drivers/clk/uniphier/Kconfig"
source "drivers/clk/zynqmp/Kconfig"

endmenu
1 change: 1 addition & 0 deletions drivers/clk/Makefile
Expand Up @@ -108,3 +108,4 @@ obj-$(CONFIG_X86) += x86/
endif
obj-$(CONFIG_ARCH_ZX) += zte/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
obj-$(CONFIG_COMMON_CLK_ZYNQMP) += zynqmp/
10 changes: 10 additions & 0 deletions drivers/clk/zynqmp/Kconfig
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0

config COMMON_CLK_ZYNQMP
bool "Support for Xilinx ZynqMP Ultrascale+ clock controllers"
depends on ARCH_ZYNQMP || COMPILE_TEST
depends on ZYNQMP_FIRMWARE
help
Support for the Zynqmp Ultrascale clock controller.
It has a dependency on the PMU firmware.
Say Y if you want to include clock support.
4 changes: 4 additions & 0 deletions drivers/clk/zynqmp/Makefile
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
# Zynq Ultrascale+ MPSoC clock specific Makefile

obj-$(CONFIG_ARCH_ZYNQMP) += pll.o clk-gate-zynqmp.o divider.o clk-mux-zynqmp.o clkc.o
144 changes: 144 additions & 0 deletions drivers/clk/zynqmp/clk-gate-zynqmp.c
@@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Zynq UltraScale+ MPSoC clock controller
*
* Copyright (C) 2016-2018 Xilinx
*
* Gated clock implementation
*/

#include <linux/clk-provider.h>
#include <linux/slab.h>
#include "clk-zynqmp.h"

/**
* struct clk_gate - gating clock
* @hw: handle between common and hardware-specific interfaces
* @flags: hardware-specific flags
* @clk_id: Id of clock
*/
struct zynqmp_clk_gate {
struct clk_hw hw;
u8 flags;
u32 clk_id;
};

#define to_zynqmp_clk_gate(_hw) container_of(_hw, struct zynqmp_clk_gate, hw)

/**
* zynqmp_clk_gate_enable() - Enable clock
* @hw: handle between common and hardware-specific interfaces
*
* Return: 0 on success else error code
*/
static int zynqmp_clk_gate_enable(struct clk_hw *hw)
{
struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = gate->clk_id;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();

ret = eemi_ops->clock_enable(clk_id);

if (ret)
pr_warn_once("%s() clock enabled failed for %s, ret = %d\n",
__func__, clk_name, ret);

return ret;
}

/*
* zynqmp_clk_gate_disable() - Disable clock
* @hw: handle between common and hardware-specific interfaces
*/
static void zynqmp_clk_gate_disable(struct clk_hw *hw)
{
struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = gate->clk_id;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();

ret = eemi_ops->clock_disable(clk_id);

if (ret)
pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
__func__, clk_name, ret);
}

/**
* zynqmp_clk_gate_is_enable() - Check clock state
* @hw: handle between common and hardware-specific interfaces
*
* Return: 1 if enabled, 0 if disabled else error code
*/
static int zynqmp_clk_gate_is_enabled(struct clk_hw *hw)
{
struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = gate->clk_id;
int state, ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();

ret = eemi_ops->clock_getstate(clk_id, &state);
if (ret) {
pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
__func__, clk_name, ret);
return -EIO;
}

return state ? 1 : 0;
}

static const struct clk_ops zynqmp_clk_gate_ops = {
.enable = zynqmp_clk_gate_enable,
.disable = zynqmp_clk_gate_disable,
.is_enabled = zynqmp_clk_gate_is_enabled,
};

/**
* zynqmp_clk_register_gate() - Register a gate clock with the clock framework
* @name: Name of this clock
* @clk_id: Id of this clock
* @parents: Name of this clock's parents
* @num_parents: Number of parents
* @nodes: Clock topology node
*
* Return: clock hardware of the registered clock gate
*/
struct clk_hw *zynqmp_clk_register_gate(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes)
{
struct zynqmp_clk_gate *gate;
struct clk_hw *hw;
int ret;
struct clk_init_data init;

/* allocate the gate */
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);

init.name = name;
init.ops = &zynqmp_clk_gate_ops;
init.flags = nodes->flag;
init.parent_names = parents;
init.num_parents = 1;

/* struct clk_gate assignments */
gate->flags = nodes->type_flag;
gate->hw.init = &init;
gate->clk_id = clk_id;

hw = &gate->hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(gate);
hw = ERR_PTR(ret);
}

return hw;
}
141 changes: 141 additions & 0 deletions drivers/clk/zynqmp/clk-mux-zynqmp.c
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Zynq UltraScale+ MPSoC mux
*
* Copyright (C) 2016-2018 Xilinx
*/

#include <linux/clk-provider.h>
#include <linux/slab.h>
#include "clk-zynqmp.h"

/*
* DOC: basic adjustable multiplexer clock that cannot gate
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is only affected by parent switching. No clk_set_rate support
* parent - parent is adjustable through clk_set_parent
*/

/**
* struct zynqmp_clk_mux - multiplexer clock
*
* @hw: handle between common and hardware-specific interfaces
* @flags: hardware-specific flags
* @clk_id: Id of clock
*/
struct zynqmp_clk_mux {
struct clk_hw hw;
u8 flags;
u32 clk_id;
};

#define to_zynqmp_clk_mux(_hw) container_of(_hw, struct zynqmp_clk_mux, hw)

/**
* zynqmp_clk_mux_get_parent() - Get parent of clock
* @hw: handle between common and hardware-specific interfaces
*
* Return: Parent index
*/
static u8 zynqmp_clk_mux_get_parent(struct clk_hw *hw)
{
struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = mux->clk_id;
u32 val;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();

ret = eemi_ops->clock_getparent(clk_id, &val);

if (ret)
pr_warn_once("%s() getparent failed for clock: %s, ret = %d\n",
__func__, clk_name, ret);

return val;
}

/**
* zynqmp_clk_mux_set_parent() - Set parent of clock
* @hw: handle between common and hardware-specific interfaces
* @index: Parent index
*
* Return: 0 on success else error+reason
*/
static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index)
{
struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = mux->clk_id;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();

ret = eemi_ops->clock_setparent(clk_id, index);

if (ret)
pr_warn_once("%s() set parent failed for clock: %s, ret = %d\n",
__func__, clk_name, ret);

return ret;
}

static const struct clk_ops zynqmp_clk_mux_ops = {
.get_parent = zynqmp_clk_mux_get_parent,
.set_parent = zynqmp_clk_mux_set_parent,
.determine_rate = __clk_mux_determine_rate,
};

static const struct clk_ops zynqmp_clk_mux_ro_ops = {
.get_parent = zynqmp_clk_mux_get_parent,
};

/**
* zynqmp_clk_register_mux() - Register a mux table with the clock
* framework
* @name: Name of this clock
* @clk_id: Id of this clock
* @parents: Name of this clock's parents
* @num_parents: Number of parents
* @nodes: Clock topology node
*
* Return: clock hardware of the registered clock mux
*/
struct clk_hw *zynqmp_clk_register_mux(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes)
{
struct zynqmp_clk_mux *mux;
struct clk_hw *hw;
struct clk_init_data init;
int ret;

mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);

init.name = name;
if (nodes->type_flag & CLK_MUX_READ_ONLY)
init.ops = &zynqmp_clk_mux_ro_ops;
else
init.ops = &zynqmp_clk_mux_ops;
init.flags = nodes->flag;
init.parent_names = parents;
init.num_parents = num_parents;
mux->flags = nodes->type_flag;
mux->hw.init = &init;
mux->clk_id = clk_id;

hw = &mux->hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(hw);
hw = ERR_PTR(ret);
}

return hw;
}
EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux);
68 changes: 68 additions & 0 deletions drivers/clk/zynqmp/clk-zynqmp.h
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016-2018 Xilinx
*/

#ifndef __LINUX_CLK_ZYNQMP_H_
#define __LINUX_CLK_ZYNQMP_H_

#include <linux/spinlock.h>

#include <linux/firmware/xlnx-zynqmp.h>

/* Clock APIs payload parameters */
#define CLK_GET_NAME_RESP_LEN 16
#define CLK_GET_TOPOLOGY_RESP_WORDS 3
#define CLK_GET_PARENTS_RESP_WORDS 3
#define CLK_GET_ATTR_RESP_WORDS 1

enum topology_type {
TYPE_INVALID,
TYPE_MUX,
TYPE_PLL,
TYPE_FIXEDFACTOR,
TYPE_DIV1,
TYPE_DIV2,
TYPE_GATE,
};

/**
* struct clock_topology - Clock topology
* @type: Type of topology
* @flag: Topology flags
* @type_flag: Topology type specific flag
*/
struct clock_topology {
u32 type;
u32 flag;
u32 type_flag;
};

struct clk_hw *zynqmp_clk_register_pll(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);

struct clk_hw *zynqmp_clk_register_gate(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);

struct clk_hw *zynqmp_clk_register_divider(const char *name,
u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);

struct clk_hw *zynqmp_clk_register_mux(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);

struct clk_hw *zynqmp_clk_register_fixed_factor(const char *name,
u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);

#endif

0 comments on commit 3fde0e1

Please sign in to comment.