Skip to content

Commit

Permalink
Merge tag 'zynqmp-soc-clk-for-v4.20' of https://github.com/Xilinx/lin…
Browse files Browse the repository at this point in the history
…ux-xlnx into next/drivers

arm64: zynqmp: SoC CLK changes for v4.20

This patchset adds CCF compliant clock driver for ZynqMP.
Clock driver queries supported clock information from firmware
and regiters pll and output clocks with CCF.

* tag 'zynqmp-soc-clk-for-v4.20' of https://github.com/Xilinx/linux-xlnx:
  drivers: clk: Add ZynqMP clock driver
  dt-bindings: clock: Add bindings for ZynqMP clock driver
  firmware: xilinx: Add zynqmp IOCTL API for device control
  Documentation: xilinx: Add documentation for eemi APIs

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
  • Loading branch information
Arnd Bergmann committed Oct 10, 2018
2 parents f7d8782 + 3fde0e1 commit e4c080a
Show file tree
Hide file tree
Showing 15 changed files with 1,919 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,53 @@ Required properties:
- "smc" : SMC #0, following the SMCCC
- "hvc" : HVC #0, following the SMCCC

--------------------------------------------------------------------------
Device Tree Clock bindings for the Zynq Ultrascale+ MPSoC controlled using
Zynq MPSoC firmware interface
--------------------------------------------------------------------------
The clock controller is a h/w block of Zynq Ultrascale+ MPSoC clock
tree. It reads required input clock frequencies from the devicetree and acts
as clock provider for all clock consumers of PS clocks.

See clock_bindings.txt for more information on the generic clock bindings.

Required properties:
- #clock-cells: Must be 1
- compatible: Must contain: "xlnx,zynqmp-clk"
- clocks: List of clock specifiers which are external input
clocks to the given clock controller. Please refer
the next section to find the input clocks for a
given controller.
- clock-names: List of clock names which are exteral input clocks
to the given clock controller. Please refer to the
clock bindings for more details.

Input clocks for zynqmp Ultrascale+ clock controller:

The Zynq UltraScale+ MPSoC has one primary and four alternative reference clock
inputs. These required clock inputs are:
- pss_ref_clk (PS reference clock)
- video_clk (reference clock for video system )
- pss_alt_ref_clk (alternative PS reference clock)
- aux_ref_clk
- gt_crx_ref_clk (transceiver reference clock)

The following strings are optional parameters to the 'clock-names' property in
order to provide an optional (E)MIO clock source:
- swdt0_ext_clk
- swdt1_ext_clk
- gem0_emio_clk
- gem1_emio_clk
- gem2_emio_clk
- gem3_emio_clk
- mio_clk_XX # with XX = 00..77
- mio_clk_50_or_51 #for the mux clock to gem tsu from 50 or 51


Output clocks are registered based on clock information received
from firmware. Output clocks indexes are mentioned in
include/dt-bindings/clock/xlnx,zynqmp-clk.h.

-------
Example
-------
Expand All @@ -25,5 +72,11 @@ firmware {
zynqmp_firmware: zynqmp-firmware {
compatible = "xlnx,zynqmp-firmware";
method = "smc";
zynqmp_clk: clock-controller {
#clock-cells = <1>;
compatible = "xlnx,zynqmp-clk";
clocks = <&pss_ref_clk>, <&video_clk>, <&pss_alt_ref_clk>, <&aux_ref_clk>, <&gt_crx_ref_clk>;
clock-names = "pss_ref_clk", "video_clk", "pss_alt_ref_clk","aux_ref_clk", "gt_crx_ref_clk";
};
};
};
67 changes: 67 additions & 0 deletions Documentation/xilinx/eemi.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---------------------------------------------------------------------
Xilinx Zynq MPSoC EEMI Documentation
---------------------------------------------------------------------

Xilinx Zynq MPSoC Firmware Interface
-------------------------------------
The zynqmp-firmware node describes the interface to platform firmware.
ZynqMP has an interface to communicate with secure firmware. Firmware
driver provides an interface to firmware APIs. Interface APIs can be
used by any driver to communicate with PMC(Platform Management Controller).

Embedded Energy Management Interface (EEMI)
----------------------------------------------
The embedded energy management interface is used to allow software
components running across different processing clusters on a chip or
device to communicate with a power management controller (PMC) on a
device to issue or respond to power management requests.

EEMI ops is a structure containing all eemi APIs supported by Zynq MPSoC.
The zynqmp-firmware driver maintain all EEMI APIs in zynqmp_eemi_ops
structure. Any driver who want to communicate with PMC using EEMI APIs
can call zynqmp_pm_get_eemi_ops().

Example of EEMI ops:

/* zynqmp-firmware driver maintain all EEMI APIs */
struct zynqmp_eemi_ops {
int (*get_api_version)(u32 *version);
int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
};

static const struct zynqmp_eemi_ops eemi_ops = {
.get_api_version = zynqmp_pm_get_api_version,
.query_data = zynqmp_pm_query_data,
};

Example of EEMI ops usage:

static const struct zynqmp_eemi_ops *eemi_ops;
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;

eemi_ops = zynqmp_pm_get_eemi_ops();
if (!eemi_ops)
return -ENXIO;

ret = eemi_ops->query_data(qdata, ret_payload);

IOCTL
------
IOCTL API is for device control and configuration. It is not a system
IOCTL but it is an EEMI API. This API can be used by master to control
any device specific configuration. IOCTL definitions can be platform
specific. This API also manage shared device configuration.

The following IOCTL IDs are valid for device control:
- IOCTL_SET_PLL_FRAC_MODE 8
- IOCTL_GET_PLL_FRAC_MODE 9
- IOCTL_SET_PLL_FRAC_DATA 10
- IOCTL_GET_PLL_FRAC_DATA 11

Refer EEMI API guide [0] for IOCTL specific parameters and other EEMI APIs.

References
----------
[0] Embedded Energy Management Interface (EEMI) API guide:
https://www.xilinx.com/support/documentation/user_guides/ug1200-eemi-api.pdf
1 change: 1 addition & 0 deletions drivers/clk/Kconfig
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit e4c080a

Please sign in to comment.