zynq clk support
Subsystems (7)
v2 cover zynq clk support
This set is a v2 of my original patchset "zynq COMMON_CLK support" [1].
This patchset is on top of the arm-next in the Xilinx tree[2], and also dependent on my patch "serial: xilinx_uartps: kill CONFIG_OF conditional", which I've removed from this patchset at Michal Simek's request.
For easy testing, I've made available a branch with my changes here[3].
Changes since v1:
- Dropped the moving of the TTC driver. This will be done in a later patchset.
- Fixed TTC periodic tick frequency calculation
- Updated basic peripheral clk to support devices with less than two derivative clks.
- Fixed dts issues in the uart and ttc (thanks to Lars-Peter Clausen and Michal Simek).
- Split out the creation of the clk drivers and the enabling of the drivers for the platform into separate patches.
- Updated clk initialization functions to 'bail out' when an error condition is hit.
- Addressed stylistic feedback from Lars-Peter Clausen.
---
Patch 1 performs a dts file split for zynq, creating a generic zynq-7000.dtsi snippet and a zynq-zc702.dts board specific description.
Patch 2 introduces the zynq clock drivers and bindings.
Patch 3 enables the zynq clock drivers when building for zynq, and adds updates the zynq device trees to use the bindings.
Patch 4 updates the xilinx uartps driver to acquire rate information via the clk bindings.
Patch 5 updates the xilinx ttc driver to acquire rate information via the clk bindings.
---
[1]: http://lists.infradead.org/pipermail/linux-arm-kernel/2012-October/129295.html
[2]: git://git.xilinx.com/linux-xlnx.git#arm-next
[3]: git://gi.teric.us/linux-zynq.git#of_clk_v2
---
Josh Cartwright (5):
ARM: zynq: dts: split up device tree
clk: Add support for fundamental zynq clks
ARM: zynq: use zynq clk bindings
serial: xilinx_uartps: get clock rate info from dts
ARM: zynq: add clk binding support to the ttc
.../devicetree/bindings/clock/zynq-7000.txt | 55 +++ arch/arm/Kconfig | 1 + arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/zynq-7000.dtsi | 166 +++++++++ arch/arm/boot/dts/zynq-ep107.dts | 70 ---- arch/arm/boot/dts/zynq-zc702.dts | 44 +++ arch/arm/mach-zynq/common.c | 14 +- arch/arm/mach-zynq/timer.c | 287 ++++++++------- drivers/clk/Makefile | 1 + drivers/clk/clk-zynq.c | 383 +++++++++++++++++++++ drivers/tty/serial/xilinx_uartps.c | 30 +- include/linux/clk/zynq.h | 24 ++ 12 files changed, 870 insertions(+), 206 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/zynq-7000.txt create mode 100644 arch/arm/boot/dts/zynq-7000.dtsi delete mode 100644 arch/arm/boot/dts/zynq-ep107.dts create mode 100644 arch/arm/boot/dts/zynq-zc702.dts create mode 100644 drivers/clk/clk-zynq.c create mode 100644 include/linux/clk/zynq.h
Signature · 1 line
1.8.0
PATCH v2 1/5 ARM: zynq: dts: split up device tree
The purpose of the created zynq-7000.dtsi file is to describe the hardware common to all Zynq 7000-based boards. Also, get rid of the zynq-ep107 device tree, since it is not hardware anyone can purchase.
Add a zc702 dts file based on the zynq-7000.dtsi. Add it to the dts/Makefile so it is built with the 'dtbs' target.
---
arch/arm/boot/dts/Makefile | 1 +
.../boot/dts/{zynq-ep107.dts => zynq-7000.dtsi} | 19 +++-----------
arch/arm/boot/dts/zynq-zc702.dts | 30 ++++++++++++++++++++++
arch/arm/mach-zynq/common.c | 3 ++-
4 files changed, 36 insertions(+), 17 deletions(-)
rename arch/arm/boot/dts/{zynq-ep107.dts => zynq-7000.dtsi} (79%)
create mode 100644 arch/arm/boot/dts/zynq-zc702.dtsarch/arm/boot/dts/Makefile +1 -0
@@ -103,5 +103,6 @@ dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \ dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \ wm8505-ref.dtb \ wm8650-mid.dtb +dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb endif
arch/arm/boot/dts/zynq-7000.dtsi renamed from arch/arm/boot/dts/zynq-ep107.dts +3 -16
@@ -10,29 +10,16 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/include/ "skeleton.dtsi" -/dts-v1/; / { - model = "Xilinx Zynq EP107"; - compatible = "xlnx,zynq-ep107"; - #address-cells = <1>; - #size-cells = <1>; - interrupt-parent = <&intc>; - - memory { - device_type = "memory"; - reg = <0x0 0x10000000>; - }; - - chosen { - bootargs = "console=ttyPS0,9600 root=/dev/ram rw initrd=0x800000,8M earlyprintk"; - linux,stdout-path = &uart0; - }; + compatible = "xlnx,zynq-7000"; amba { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; + interrupt-parent = <&intc>; ranges; intc: interrupt-controller at f8f01000 {
arch/arm/boot/dts/zynq-zc702.dts new file +30 -0
@@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 Xilinx + * Copyright (C) 2012 National Instruments Corp. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +/dts-v1/; +/include/ "zynq-7000.dtsi" + +/ { + model = "Zynq ZC702 Development Board"; + compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000"; + + memory { + device_type = "memory"; + reg = <0x0 0x40000000>; + }; + + chosen { + bootargs = "console=ttyPS1,115200 earlyprintk"; + }; + +};
arch/arm/mach-zynq/common.c +2 -1
@@ -115,7 +115,8 @@ static void __init xilinx_map_io(void) }static const char *xilinx_dt_match[] = { -"xlnx,zynq-ep107" , +"xlnx,zynq-zc702" , +"xlnx,zynq-7000" ,NULL };
Signature · 1 line
1.8.0
Re: [PATCH v2 1/5] ARM: zynq: dts: split up device tree
Hi Josh,
2012/10/31 Josh Cartwright <[email protected]>:
Quoted 8 lines
The purpose of the created
zynq-7000.dtsifile is to describe the hardware common to all Zynq 7000-based boards. Also, get rid of the zynq-ep107 device tree, since it is not hardware anyone can purchase.Add a zc702 dts file based on the
zynq-7000.dtsi. Add it to the dts/Makefile so it is built with the 'dtbs' target.Signed-off-by Josh Cartwright <[email protected]>
Quoted 8 lines
---
arch/arm/boot/dts/Makefile | 1 +
.../boot/dts/{zynq-ep107.dts => zynq-7000.dtsi} | 19 +++-----------
arch/arm/boot/dts/zynq-zc702.dts | 30 ++++++++++++++++++++++
arch/arm/mach-zynq/common.c | 3 ++-
4 files changed, 36 insertions(+), 17 deletions(-)
rename arch/arm/boot/dts/{zynq-ep107.dts => zynq-7000.dtsi} (79%)
create mode 100644 arch/arm/boot/dts/zynq-zc702.dtsWe have had discussion about this dtsi stuff and also based on our previous discussion
all trying to convince me that this solution is right solution. I still have doubts but I will apply it to arm-next tree and we can revisit it in future.
Thanks,
Michal
PATCH v2 2/5 clk: Add support for fundamental zynq clks
In reply to Josh CartwrightProvide simplified models for the necessary clocks on the zynq-7000 platform. Currently, the PLLs, the CPU clock network, and the basic peripheral clock networks (for SDIO, SMC, SPI, QSPI, UART) are modelled.
OF bindings are also provided and documented.
--- .../devicetree/bindings/clock/zynq-7000.txt | 55 +++ drivers/clk/clk-zynq.c | 383 +++++++++++++++++++++ include/linux/clk/zynq.h | 24 ++ 3 files changed, 462 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/zynq-7000.txt create mode 100644 drivers/clk/clk-zynq.c create mode 100644 include/linux/clk/zynq.h
Documentation/devicetree/bindings/clock/zynq-7000.txt new file +55 -0
@@ -0,0 +1,55 @@ +Device Tree Clock bindings for the Zynq 7000 EPP + +The Zynq EPP has several different clk providers, each with there own bindings. +The purpose of this document is to document their usage. + +See clock_bindings.txt for more information on the generic clock bindings. +See Chapter 25 of Zynq TRM for more information about Zynq clocks. + +== PLLs == + +Used to describe the ARM_PLL, DDR_PLL, and IO_PLL. + +Required properties: +- #clock-cells : shall be 0 (only one clock is output from this node) +- compatible : "xlnx,zynq-pll" +- reg : pair of u32 values, which are the address offsets within the SLCR + of the relevant PLL_CTRL register and PLL_CFG register respectively +- clocks : phandle for parent clock. should be the phandle for ps_clk + +Optional properties: +- clock-output-names : name of the output clock + +Example: + armpll: armpll { + #clock-cells = <0>; + compatible = "xlnx,zynq-pll"; + clocks = <&ps_clk>; + reg = <0x100 0x110>; + clock-output-names = "armpll"; + }; + +== Peripheral clocks == + +Describes clock node for the SDIO, SMC, SPI, QSPI, and UART clocks. + +Required properties: +- #clock-cells : shall be 1 +- compatible : "xlnx,zynq-periph-clock" +- reg : a single u32 value, describing the offset within the SLCR where + the CLK_CTRL register is found for this peripheral +- clocks : phandle for parent clocks. should hold phandles for + the IO_PLL, ARM_PLL, and DDR_PLL in order +- clock-output-names : names of the output clock(s). For peripherals that have + two output clocks (for example, the UART), two clocks + should be listed. + +Example: + uart_clk: uart_clk { + #clock-cells = <1>; + compatible = "xlnx,zynq-periph-clock"; + clocks = <&iopll &armpll &ddrpll>; + reg = <0x154>; + clock-output-names = "uart0_ref_clk", + "uart1_ref_clk"; + };
drivers/clk/clk-zynq.c new file +383 -0
383 lines not on this page · raw message
include/linux/clk/zynq.h new file +24 -0
@@ -0,0 +1,24 @@ +/* +* Copyright (C) 2012 National Instruments +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LINUX_CLK_ZYNQ_H_ +#define __LINUX_CLK_ZYNQ_H_ + +void __init xilinx_zynq_clocks_init (void __iomem *slcr); + +#endif
Signature · 1 line
1.8.0
Re: [PATCH v2 2/5] clk: Add support for fundamental zynq clks
One note below:
Quoted 171 lines · Josh Cartwright
Provide simplified models for the necessary clocks on the zynq-7000 platform. Currently, the PLLs, the CPU clock network, and the basic peripheral clock networks (for SDIO, SMC, SPI, QSPI, UART) are modelled.
OF bindings are also provided and documented.
Signed-off-by Josh Cartwright <[email protected]>--- .../devicetree/bindings/clock/zynq-7000.txt | 55 +++ drivers/clk/clk-zynq.c | 383 +++++++++++++++++++++ include/linux/clk/zynq.h | 24 ++ 3 files changed, 462 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/zynq-7000.txt create mode 100644 drivers/clk/clk-zynq.c create mode 100644 include/linux/clk/zynq.hDocumentation/devicetree/bindings/clock/zynq-7000.txt new file +55
-0@@ -0,0 +1,55 @@ +Device Tree Clock bindings for the Zynq 7000 EPP + +The Zynq EPP has several different clk providers, each with there own bindings. +The purpose of this document is to document their usage. + +See clock_bindings.txt for more information on the generic clock bindings. +See Chapter 25 of Zynq TRM for more information about Zynq clocks. + +== PLLs == + +Used to describe the ARM_PLL, DDR_PLL, and IO_PLL. + +Required properties: +- #clock-cells : shall be 0 (only one clock is output from this node) +- compatible : "xlnx,zynq-pll" +- reg : pair of u32 values, which are the address offsets within the SLCR + of the relevant PLL_CTRL register and PLL_CFG register respectively +- clocks : phandle for parent clock. should be the phandle for ps_clk + +Optional properties: +- clock-output-names : name of the output clock + +Example: + armpll: armpll { + #clock-cells = <0>; + compatible = "xlnx,zynq-pll"; + clocks = <&ps_clk>; + reg = <0x100 0x110>; + clock-output-names = "armpll"; + }; + +== Peripheral clocks == + +Describes clock node for the SDIO, SMC, SPI, QSPI, and UART clocks. + +Required properties: +- #clock-cells : shall be 1 +- compatible : "xlnx,zynq-periph-clock" +- reg : a single u32 value, describing the offset within the SLCR where + the CLK_CTRL register is found for this peripheral +- clocks : phandle for parent clocks. should hold phandles for + the IO_PLL, ARM_PLL, and DDR_PLL in order +- clock-output-names : names of the output clock(s). For peripherals that have + two output clocks (for example, the UART), two clocks + should be listed. + +Example: + uart_clk: uart_clk { + #clock-cells = <1>; + compatible = "xlnx,zynq-periph-clock"; + clocks = <&iopll &armpll &ddrpll>; + reg = <0x154>; + clock-output-names = "uart0_ref_clk", + "uart1_ref_clk"; + };drivers/clk/clk-zynq.c new file +88
-0@@ -0,0 +1,383 @@ +/* +* Copyright (c) 2012 National Instruments +* +* Josh Cartwright <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <linux/io.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/clk-provider.h> + +static void __iomem *slcr_base; + +struct zynq_pll_clk { +struct clk_hw hw; +void __iomem *pll_ctrl; +void __iomem *pll_cfg; +}; + +#define to_zynq_pll_clk (hw) container_of(hw, struct zynq_pll_clk, hw) + +#define CTRL_PLL_FDIV (x) ((x) >> 12) + +static unsigned long zynq_pll_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct zynq_pll_clk *pll =to_zynq_pll_clk (hw); +return parent_rate *CTRL_PLL_FDIV (ioread32 (pll->pll_ctrl)); +} + +static const struct clk_ops zynq_pll_clk_ops = { + .recalc_rate = zynq_pll_recalc_rate, +}; + +static void __init zynq_pll_clk_setup (struct device_node *np) +{ +struct clk_init_data init; +struct zynq_pll_clk *pll; +const char *parent_name; +struct clk *clk; +u32 regs[2 ]; +int ret; + + ret =of_property_read_u32_array (np,"reg" , regs,ARRAY_SIZE (regs)); +if (WARN_ON (ret)) +return ; + + pll =kzalloc (sizeof (*pll),GFP_KERNEL ); +if (WARN_ON (!pll)) +return ; + + pll->pll_ctrl = slcr_base + regs[0 ]; + pll->pll_cfg = slcr_base + regs[1 ]; + +of_property_read_string (np,"clock-output-names" , &init.name); + + init.ops = &zynq_pll_clk_ops; + parent_name =of_clk_get_parent_name (np,0 ); + init.parent_names = &parent_name; + init.num_parents =1 ; + + pll->hw.init = &init; + + clk =clk_register (NULL , &pll->hw); +if (WARN_ON (IS_ERR (clk))) +return ; + + ret =of_clk_add_provider (np, of_clk_src_simple_get, clk); +if (WARN_ON (ret)) +return ; +} + +struct zynq_periph_clk { +struct clk_hw hw; +struct clk_onecell_data onecell_data; +struct clk *gates[2 ];
drivers/clk/clk-zynq.c line 98
Quoted 10 lines
drivers/clk/clk-zynq.c new file +10
-0+void __iomem *clk_ctrl; +spinlock_t clkact_lock; +}; + +#define to_zynq_periph_clk (hw) container_of(hw, struct zynq_periph_clk, hw) + +static const u8 periph_clk_parent_map[] = { +0 ,0 ,1 ,2 +}; +#define PERIPH_CLK_CTRL_SRC (x) (periph_clk_parent_map[((x) & 3) >> 4])
I think this should be:
#define PERIPH_CLK_CTRL_SRC(x) (periph_clk_parent_map[((x) & 0x30) >> 4])
Quoted 317 lines
drivers/clk/clk-zynq.c new file +285
-0+#define PERIPH_CLK_CTRL_DIV (x) (((x) & 0x3F00) >> 8) + +static unsigned long zynq_periph_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct zynq_periph_clk *periph =to_zynq_periph_clk (hw); +return parent_rate /PERIPH_CLK_CTRL_DIV (ioread32 (periph->clk_ctrl)); +} + +static u8 zynq_periph_get_parent (struct clk_hw *hw) +{ +struct zynq_periph_clk *periph =to_zynq_periph_clk (hw); +return PERIPH_CLK_CTRL_SRC (ioread32 (periph->clk_ctrl)); +} + +static const struct clk_ops zynq_periph_clk_ops = { + .recalc_rate = zynq_periph_recalc_rate, + .get_parent = zynq_periph_get_parent, +}; + +static void __init zynq_periph_clk_setup (struct device_node *np) +{ +struct zynq_periph_clk *periph; +const char *parent_names[3 ]; +struct clk_init_data init; +int clk_num =0 , err; +const char *name; +struct clk *clk; +u32 reg; +int i; + + err =of_property_read_u32 (np,"reg" , ®); +if (WARN_ON (err)) +return ; + + periph =kzalloc (sizeof (*periph),GFP_KERNEL ); +if (WARN_ON (!periph)) +return ; + + periph->clk_ctrl = slcr_base + reg; +spin_lock_init (&periph->clkact_lock); + + init.name = np->name; + init.ops = &zynq_periph_clk_ops; +for (i =0 ; i <ARRAY_SIZE (parent_names); i++) + parent_names[i] =of_clk_get_parent_name (np, i); + init.parent_names = parent_names; + init.num_parents =ARRAY_SIZE (parent_names); + + periph->hw.init = &init; + + clk =clk_register (NULL , &periph->hw); +if (WARN_ON (IS_ERR (clk))) +return ; + + err =of_clk_add_provider (np, of_clk_src_simple_get, clk); +if (WARN_ON (err)) +return ; + + err =of_property_read_string_index (np,"clock-output-names" ,0 , + &name); +if (WARN_ON (err)) +return ; + + periph->gates[0 ] =clk_register_gate (NULL , name, np->name,0 , + periph->clk_ctrl,0 ,0 , + &periph->clkact_lock); +if (WARN_ON (IS_ERR (periph->gates[0 ]))) +return ; + clk_num++; + +/* some periph clks have 2 downstream gates */ + err =of_property_read_string_index (np,"clock-output-names" ,1 , + &name); +if (err != -ENODATA ) { + periph->gates[1 ] =clk_register_gate (NULL , name, np->name,0 , + periph->clk_ctrl,1 ,0 , + &periph->clkact_lock); +if (WARN_ON (IS_ERR (periph->gates[1 ]))) +return ; + clk_num++; + } + + periph->onecell_data.clks = periph->gates; + periph->onecell_data.clk_num = clk_num; + + err =of_clk_add_provider (np, of_clk_src_onecell_get, + &periph->onecell_data); +if (WARN_ON (err)) +return ; +} + +/* CPU Clock domain is modelled as a mux with 4 children subclks, whose +* derivative rates depend on CLK_621_TRUE +*/ + +struct zynq_cpu_clk { +struct clk_hw hw; +struct clk_onecell_data onecell_data; +struct clk *subclks[4 ]; +void __iomem *clk_ctrl; +spinlock_t clkact_lock; +}; + +#define to_zynq_cpu_clk (hw) container_of(hw, struct zynq_cpu_clk, hw) + +static const u8 zynq_cpu_clk_parent_map[] = { +1 ,1 ,2 ,0 +}; +#define CPU_CLK_SRCSEL (x) (zynq_cpu_clk_parent_map[(((x) & 0x30) >> 4)]) +#define CPU_CLK_CTRL_DIV (x) (((x) & 0x3F00) >> 8) + +static u8 zynq_cpu_clk_get_parent (struct clk_hw *hw) +{ +struct zynq_cpu_clk *cpuclk =to_zynq_cpu_clk (hw); +return CPU_CLK_SRCSEL (ioread32 (cpuclk->clk_ctrl)); +} + +static unsigned long zynq_cpu_clk_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct zynq_cpu_clk *cpuclk =to_zynq_cpu_clk (hw); +return parent_rate /CPU_CLK_CTRL_DIV (ioread32 (cpuclk->clk_ctrl)); +} + +static const struct clk_ops zynq_cpu_clk_ops = { + .get_parent = zynq_cpu_clk_get_parent, + .recalc_rate = zynq_cpu_clk_recalc_rate, +}; + +struct zynq_cpu_subclk { +struct clk_hw hw; +void __iomem *clk_621; +enum { +CPU_SUBCLK_6X4X , +CPU_SUBCLK_3X2X , +CPU_SUBCLK_2X , +CPU_SUBCLK_1X , + } which; +}; + +#define CLK_621_TRUE (x) ((x) & 1) + +#define to_zynq_cpu_subclk (hw) container_of(hw, struct zynq_cpu_subclk, hw); + +static unsigned long zynq_cpu_subclk_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +unsigned long uninitialized_var (rate ); +struct zynq_cpu_subclk *subclk; +bool is_621; + + subclk =to_zynq_cpu_subclk (hw) + is_621 = CLK_621_TRUE(ioread32(subclk->clk_621)); + +switch (subclk->which) { +case CPU_SUBCLK_6X4X : + rate = parent_rate; +break ; +case CPU_SUBCLK_3X2X : + rate = parent_rate /2 ; +break ; +case CPU_SUBCLK_2X : + rate = parent_rate / (is_621 ?3 :2 ); +break ; +case CPU_SUBCLK_1X : + rate = parent_rate / (is_621 ?6 :4 ); +break ; + }; + +return rate; +} + +static const struct clk_ops zynq_cpu_subclk_ops = { + .recalc_rate = zynq_cpu_subclk_recalc_rate, +}; + +static struct clk *zynq_cpu_subclk_setup (struct device_node *np,u8 which, +void __iomem *clk_621) +{ +struct zynq_cpu_subclk *subclk; +struct clk_init_data init; +struct clk *clk; +int err; + + err =of_property_read_string_index (np,"clock-output-names" , + which, &init.name); +if (WARN_ON (err)) + goto err_read_output_name; + + subclk =kzalloc (sizeof (*subclk),GFP_KERNEL ); +if (!subclk) + goto err_subclk_alloc; + + subclk->clk_621 = clk_621; + subclk->which = which; + + init.ops = &zynq_cpu_subclk_ops; + init.parent_names = &np->name; + init.num_parents =1 ; + + subclk->hw.init = &init; + + clk =clk_register (NULL , &subclk->hw); +if (WARN_ON (IS_ERR (clk))) + goto err_clk_register; + +return clk; + +err_clk_register: +kfree (subclk); +err_subclk_alloc: +err_read_output_name: +return ERR_PTR (-EINVAL ); +} + +static void __init zynq_cpu_clk_setup (struct device_node *np) +{ +struct zynq_cpu_clk *cpuclk; +const char *parent_names[3 ]; +struct clk_init_data init; +void __iomem *clk_621; +struct clk *clk; +u32 reg[2 ]; +int err; +int i; + + err =of_property_read_u32_array (np,"reg" , reg,ARRAY_SIZE (reg)); +if (WARN_ON (err)) +return ; + + cpuclk =kzalloc (sizeof (*cpuclk),GFP_KERNEL ); +if (WARN_ON (!cpuclk)) +return ; + + cpuclk->clk_ctrl = slcr_base + reg[0 ]; + clk_621 = slcr_base + reg[1 ]; +spin_lock_init (&cpuclk->clkact_lock); + + init.name = np->name; + init.ops = &zynq_cpu_clk_ops; +for (i =0 ; i <ARRAY_SIZE (parent_names); i++) + parent_names[i] =of_clk_get_parent_name (np, i); + init.parent_names = parent_names; + init.num_parents =ARRAY_SIZE (parent_names); + + cpuclk->hw.init = &init; + + clk =clk_register (NULL , &cpuclk->hw); +if (WARN_ON (IS_ERR (clk))) +return ; + + err =of_clk_add_provider (np, of_clk_src_simple_get, clk); +if (WARN_ON (err)) +return ; + +for (i =0 ; i <4 ; i++) { + cpuclk->subclks[i] =zynq_cpu_subclk_setup (np, i, clk_621); +if (WARN_ON (IS_ERR (cpuclk->subclks[i]))) +return ; + } + + cpuclk->onecell_data.clks = cpuclk->subclks; + cpuclk->onecell_data.clk_num = i; + + err =of_clk_add_provider (np, of_clk_src_onecell_get, + &cpuclk->onecell_data); +if (WARN_ON (err)) +return ; +} + +static const __initconst struct of_device_id zynq_clk_match[] = { + { .compatible ="fixed-clock" , .data = of_fixed_clk_setup, }, + { .compatible ="xlnx,zynq-pll" , .data = zynq_pll_clk_setup, }, + { .compatible ="xlnx,zynq-periph-clock" , + .data = zynq_periph_clk_setup, }, + { .compatible ="xlnx,zynq-cpu-clock" , .data = zynq_cpu_clk_setup, }, + {} +}; + +void __init xilinx_zynq_clocks_init (void __iomem *slcr) +{ + slcr_base = slcr; +of_clk_init (zynq_clk_match); +}include/linux/clk/zynq.h new file +24
-0@@ -0,0 +1,24 @@ +/* +* Copyright (C) 2012 National Instruments +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LINUX_CLK_ZYNQ_H_ +#define __LINUX_CLK_ZYNQ_H_ + +void __init xilinx_zynq_clocks_init (void __iomem *slcr); + +#endif Signature · 1 line
1.8.0
Soren
Re: [PATCH v2 2/5] clk: Add support for fundamental zynq clks
Quoted 3 lines · Soren Brinkmann
One note below:
On Wed, Oct 31, 2012 at 12:58:52PM -0600, Josh Cartwright wrote:
[..]
Quoted 2 lines
Quoted 2 lines
drivers/clk/clk-zynq.c new file +0
-0
[..]
Quoted 9 lines
Quoted 9 lines
drivers/clk/clk-zynq.c new file +9
-0+struct zynq_periph_clk { +struct clk_hw hw; +struct clk_onecell_data onecell_data; +struct clk *gates[2 ]; +void __iomem *clk_ctrl; +spinlock_t clkact_lock; +}; + +#define to_zynq_periph_clk (hw) container_of(hw, struct zynq_periph_clk, hw)
Quoted 8 lines
drivers/clk/clk-zynq.c line 98
Quoted 5 lines
drivers/clk/clk-zynq.c new file +5
-0+ +static const u8 periph_clk_parent_map[] = { +0 ,0 ,1 ,2 +}; +#define PERIPH_CLK_CTRL_SRC (x) (periph_clk_parent_map[((x) & 3) >> 4])I think this should be:
#definePERIPH_CLK_CTRL_SRC(x)(periph_clk_parent_map[((x) &0x30) >> 4])
Yes indeed it should be, thanks.
Michal- I'll just be following up with a v3 of this patch instead of spinning up the whole set to save myself some time. If you'd rather I spin up the whole set, let me know.
Thanks,
Josh
RE: [PATCH v2 2/5] clk: Add support for fundamental zynq clks
Quoted 36 lines
-----Original Message-----
From: Josh Cartwright [mailto:josh.cartwright at ni.com]
Sent: Tuesday, November 13, 2012 11:03 PM
To: Soren Brinkmann; Michal Simek
Cc: Mike Turquette; Rob Herring; linux-kernel at vger.kernel.org; linux-arm- kernel at lists.infradead.org; John Linn; arm at kernel.org
Subject: Re: [PATCH v2 2/5] clk: Add support for fundamental zynq clksQuoted 3 lines · Soren Brinkmann
One note below:
On Wed, Oct 31, 2012 at 12:58:52PM -0600, Josh Cartwright wrote:
[..]
Quoted 2 lines
Quoted 2 lines
drivers/clk/clk-zynq.c new file +0
-0[..]
Quoted 9 lines
drivers/clk/clk-zynq.c
Quoted 9 lines
drivers/clk/clk-zynq.c new file +9
-0+struct zynq_periph_clk { +struct clk_hw hw; +struct clk_onecell_data onecell_data; +struct clk *gates[2 ]; +void __iomem *clk_ctrl; +spinlock_t clkact_lock; +}; + +#define to_zynq_periph_clk (hw) container_of(hw, struct zynq_periph_clk,hw)
Quoted 8 lines
drivers/clk/clk-zynq.c line 98
Quoted 5 lines
drivers/clk/clk-zynq.c new file +5
-0+ +static const u8 periph_clk_parent_map[] = { +0 ,0 ,1 ,2 +}; +#define PERIPH_CLK_CTRL_SRC (x) (periph_clk_parent_map[((x) & 3) >> 4])I think this should be:
#definePERIPH_CLK_CTRL_SRC(x)(periph_clk_parent_map[((x) &0x30) >>
Quoted 7 lines
4])
Yes indeed it should be, thanks.
Michal- I'll just be following up with a v3 of this patch instead of spinning up the whole set to save myself some time. If you'd rather I spin up the whole set, let me know.
No problem with that.
thanks,
Michal
This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
PATCH v3 1/1 clk: Add support for fundamental zynq clks
In reply to Soren BrinkmannProvide simplified models for the necessary clocks on the zynq-7000 platform. Currently, the PLLs, the CPU clock network, and the basic peripheral clock networks (for SDIO, SMC, SPI, QSPI, UART) are modelled.
OF bindings are also provided and documented.
---
Michal-
Here is a v3 with a fix for the problem Soren Brinkmann spotted. Am I correct that your current plan is to merge this into your tree?
If so:
Mike-
Can we get your Acked-by on this one?
Thanks,
Josh
v3: - Fix PERIPH_CLK_CTRL_SRC macro (thanks to Soren Brinkmann)
.../devicetree/bindings/clock/zynq-7000.txt | 55 +++ drivers/clk/clk-zynq.c | 383 +++++++++++++++++++++ include/linux/clk/zynq.h | 24 ++ 3 files changed, 462 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/zynq-7000.txt create mode 100644 drivers/clk/clk-zynq.c create mode 100644 include/linux/clk/zynq.h
Documentation/devicetree/bindings/clock/zynq-7000.txt new file +55 -0
@@ -0,0 +1,55 @@ +Device Tree Clock bindings for the Zynq 7000 EPP + +The Zynq EPP has several different clk providers, each with there own bindings. +The purpose of this document is to document their usage. + +See clock_bindings.txt for more information on the generic clock bindings. +See Chapter 25 of Zynq TRM for more information about Zynq clocks. + +== PLLs == + +Used to describe the ARM_PLL, DDR_PLL, and IO_PLL. + +Required properties: +- #clock-cells : shall be 0 (only one clock is output from this node) +- compatible : "xlnx,zynq-pll" +- reg : pair of u32 values, which are the address offsets within the SLCR + of the relevant PLL_CTRL register and PLL_CFG register respectively +- clocks : phandle for parent clock. should be the phandle for ps_clk + +Optional properties: +- clock-output-names : name of the output clock + +Example: + armpll: armpll { + #clock-cells = <0>; + compatible = "xlnx,zynq-pll"; + clocks = <&ps_clk>; + reg = <0x100 0x110>; + clock-output-names = "armpll"; + }; + +== Peripheral clocks == + +Describes clock node for the SDIO, SMC, SPI, QSPI, and UART clocks. + +Required properties: +- #clock-cells : shall be 1 +- compatible : "xlnx,zynq-periph-clock" +- reg : a single u32 value, describing the offset within the SLCR where + the CLK_CTRL register is found for this peripheral +- clocks : phandle for parent clocks. should hold phandles for + the IO_PLL, ARM_PLL, and DDR_PLL in order +- clock-output-names : names of the output clock(s). For peripherals that have + two output clocks (for example, the UART), two clocks + should be listed. + +Example: + uart_clk: uart_clk { + #clock-cells = <1>; + compatible = "xlnx,zynq-periph-clock"; + clocks = <&iopll &armpll &ddrpll>; + reg = <0x154>; + clock-output-names = "uart0_ref_clk", + "uart1_ref_clk"; + };
drivers/clk/clk-zynq.c new file +383 -0
383 lines not on this page · raw message
include/linux/clk/zynq.h new file +24 -0
@@ -0,0 +1,24 @@ +/* +* Copyright (C) 2012 National Instruments +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LINUX_CLK_ZYNQ_H_ +#define __LINUX_CLK_ZYNQ_H_ + +void __init xilinx_zynq_clocks_init (void __iomem *slcr); + +#endif
Signature · 1 line
1.8.0
Re: [PATCH v3] clk: Add support for fundamental zynq clks
Quoting Josh Cartwright (2012-11-13 15:26:48)
Quoted 10 lines
Provide simplified models for the necessary clocks on the zynq-7000 platform. Currently, the PLLs, the CPU clock network, and the basic peripheral clock networks (for SDIO, SMC, SPI, QSPI, UART) are modelled.
OF bindings are also provided and documented.
Signed-off-by Josh Cartwright <[email protected]>---
Michal-
Quoted 9 lines
Here is a v3 with a fix for the problem Soren Brinkmann spotted. Am I correct that your current plan is to merge this into your tree?
If so:
Mike-
Can we get your Acked-by on this one?
Regards,
Mike
Quoted 495 lines
Thanks,
Joshv3: - Fix
PERIPH_CLK_CTRL_SRCmacro (thanks to Soren Brinkmann).../devicetree/bindings/clock/zynq-7000.txt | 55 +++ drivers/clk/clk-zynq.c | 383 +++++++++++++++++++++ include/linux/clk/zynq.h | 24 ++ 3 files changed, 462 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/zynq-7000.txt create mode 100644 drivers/clk/clk-zynq.c create mode 100644 include/linux/clk/zynq.hDocumentation/devicetree/bindings/clock/zynq-7000.txt new file +55
-0@@ -0,0 +1,55 @@ +Device Tree Clock bindings for the Zynq 7000 EPP + +The Zynq EPP has several different clk providers, each with there own bindings. +The purpose of this document is to document their usage. + +See clock_bindings.txt for more information on the generic clock bindings. +See Chapter 25 of Zynq TRM for more information about Zynq clocks. + +== PLLs == + +Used to describe the ARM_PLL, DDR_PLL, and IO_PLL. + +Required properties: +- #clock-cells : shall be 0 (only one clock is output from this node) +- compatible : "xlnx,zynq-pll" +- reg : pair of u32 values, which are the address offsets within the SLCR + of the relevant PLL_CTRL register and PLL_CFG register respectively +- clocks : phandle for parent clock. should be the phandle for ps_clk + +Optional properties: +- clock-output-names : name of the output clock + +Example: + armpll: armpll { + #clock-cells = <0>; + compatible = "xlnx,zynq-pll"; + clocks = <&ps_clk>; + reg = <0x100 0x110>; + clock-output-names = "armpll"; + }; + +== Peripheral clocks == + +Describes clock node for the SDIO, SMC, SPI, QSPI, and UART clocks. + +Required properties: +- #clock-cells : shall be 1 +- compatible : "xlnx,zynq-periph-clock" +- reg : a single u32 value, describing the offset within the SLCR where + the CLK_CTRL register is found for this peripheral +- clocks : phandle for parent clocks. should hold phandles for + the IO_PLL, ARM_PLL, and DDR_PLL in order +- clock-output-names : names of the output clock(s). For peripherals that have + two output clocks (for example, the UART), two clocks + should be listed. + +Example: + uart_clk: uart_clk { + #clock-cells = <1>; + compatible = "xlnx,zynq-periph-clock"; + clocks = <&iopll &armpll &ddrpll>; + reg = <0x154>; + clock-output-names = "uart0_ref_clk", + "uart1_ref_clk"; + };drivers/clk/clk-zynq.c new file +383
-0@@ -0,0 +1,383 @@ +/* +* Copyright (c) 2012 National Instruments +* +* Josh Cartwright <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <linux/io.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/clk-provider.h> + +static void __iomem *slcr_base; + +struct zynq_pll_clk { +struct clk_hw hw; +void __iomem *pll_ctrl; +void __iomem *pll_cfg; +}; + +#define to_zynq_pll_clk (hw) container_of(hw, struct zynq_pll_clk, hw) + +#define CTRL_PLL_FDIV (x) ((x) >> 12) + +static unsigned long zynq_pll_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct zynq_pll_clk *pll =to_zynq_pll_clk (hw); +return parent_rate *CTRL_PLL_FDIV (ioread32 (pll->pll_ctrl)); +} + +static const struct clk_ops zynq_pll_clk_ops = { + .recalc_rate = zynq_pll_recalc_rate, +}; + +static void __init zynq_pll_clk_setup (struct device_node *np) +{ +struct clk_init_data init; +struct zynq_pll_clk *pll; +const char *parent_name; +struct clk *clk; +u32 regs[2 ]; +int ret; + + ret =of_property_read_u32_array (np,"reg" , regs,ARRAY_SIZE (regs)); +if (WARN_ON (ret)) +return ; + + pll =kzalloc (sizeof (*pll),GFP_KERNEL ); +if (WARN_ON (!pll)) +return ; + + pll->pll_ctrl = slcr_base + regs[0 ]; + pll->pll_cfg = slcr_base + regs[1 ]; + +of_property_read_string (np,"clock-output-names" , &init.name); + + init.ops = &zynq_pll_clk_ops; + parent_name =of_clk_get_parent_name (np,0 ); + init.parent_names = &parent_name; + init.num_parents =1 ; + + pll->hw.init = &init; + + clk =clk_register (NULL , &pll->hw); +if (WARN_ON (IS_ERR (clk))) +return ; + + ret =of_clk_add_provider (np, of_clk_src_simple_get, clk); +if (WARN_ON (ret)) +return ; +} + +struct zynq_periph_clk { +struct clk_hw hw; +struct clk_onecell_data onecell_data; +struct clk *gates[2 ]; +void __iomem *clk_ctrl; +spinlock_t clkact_lock; +}; + +#define to_zynq_periph_clk (hw) container_of(hw, struct zynq_periph_clk, hw) + +static const u8 periph_clk_parent_map[] = { +0 ,0 ,1 ,2 +}; +#define PERIPH_CLK_CTRL_SRC (x) (periph_clk_parent_map[((x) & 0x30) >> 4]) +#define PERIPH_CLK_CTRL_DIV (x) (((x) & 0x3F00) >> 8) + +static unsigned long zynq_periph_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct zynq_periph_clk *periph =to_zynq_periph_clk (hw); +return parent_rate /PERIPH_CLK_CTRL_DIV (ioread32 (periph->clk_ctrl)); +} + +static u8 zynq_periph_get_parent (struct clk_hw *hw) +{ +struct zynq_periph_clk *periph =to_zynq_periph_clk (hw); +return PERIPH_CLK_CTRL_SRC (ioread32 (periph->clk_ctrl)); +} + +static const struct clk_ops zynq_periph_clk_ops = { + .recalc_rate = zynq_periph_recalc_rate, + .get_parent = zynq_periph_get_parent, +}; + +static void __init zynq_periph_clk_setup (struct device_node *np) +{ +struct zynq_periph_clk *periph; +const char *parent_names[3 ]; +struct clk_init_data init; +int clk_num =0 , err; +const char *name; +struct clk *clk; +u32 reg; +int i; + + err =of_property_read_u32 (np,"reg" , ®); +if (WARN_ON (err)) +return ; + + periph =kzalloc (sizeof (*periph),GFP_KERNEL ); +if (WARN_ON (!periph)) +return ; + + periph->clk_ctrl = slcr_base + reg; +spin_lock_init (&periph->clkact_lock); + + init.name = np->name; + init.ops = &zynq_periph_clk_ops; +for (i =0 ; i <ARRAY_SIZE (parent_names); i++) + parent_names[i] =of_clk_get_parent_name (np, i); + init.parent_names = parent_names; + init.num_parents =ARRAY_SIZE (parent_names); + + periph->hw.init = &init; + + clk =clk_register (NULL , &periph->hw); +if (WARN_ON (IS_ERR (clk))) +return ; + + err =of_clk_add_provider (np, of_clk_src_simple_get, clk); +if (WARN_ON (err)) +return ; + + err =of_property_read_string_index (np,"clock-output-names" ,0 , + &name); +if (WARN_ON (err)) +return ; + + periph->gates[0 ] =clk_register_gate (NULL , name, np->name,0 , + periph->clk_ctrl,0 ,0 , + &periph->clkact_lock); +if (WARN_ON (IS_ERR (periph->gates[0 ]))) +return ; + clk_num++; + +/* some periph clks have 2 downstream gates */ + err =of_property_read_string_index (np,"clock-output-names" ,1 , + &name); +if (err != -ENODATA ) { + periph->gates[1 ] =clk_register_gate (NULL , name, np->name,0 , + periph->clk_ctrl,1 ,0 , + &periph->clkact_lock); +if (WARN_ON (IS_ERR (periph->gates[1 ]))) +return ; + clk_num++; + } + + periph->onecell_data.clks = periph->gates; + periph->onecell_data.clk_num = clk_num; + + err =of_clk_add_provider (np, of_clk_src_onecell_get, + &periph->onecell_data); +if (WARN_ON (err)) +return ; +} + +/* CPU Clock domain is modelled as a mux with 4 children subclks, whose +* derivative rates depend on CLK_621_TRUE +*/ + +struct zynq_cpu_clk { +struct clk_hw hw; +struct clk_onecell_data onecell_data; +struct clk *subclks[4 ]; +void __iomem *clk_ctrl; +spinlock_t clkact_lock; +}; + +#define to_zynq_cpu_clk (hw) container_of(hw, struct zynq_cpu_clk, hw) + +static const u8 zynq_cpu_clk_parent_map[] = { +1 ,1 ,2 ,0 +}; +#define CPU_CLK_SRCSEL (x) (zynq_cpu_clk_parent_map[(((x) & 0x30) >> 4)]) +#define CPU_CLK_CTRL_DIV (x) (((x) & 0x3F00) >> 8) + +static u8 zynq_cpu_clk_get_parent (struct clk_hw *hw) +{ +struct zynq_cpu_clk *cpuclk =to_zynq_cpu_clk (hw); +return CPU_CLK_SRCSEL (ioread32 (cpuclk->clk_ctrl)); +} + +static unsigned long zynq_cpu_clk_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct zynq_cpu_clk *cpuclk =to_zynq_cpu_clk (hw); +return parent_rate /CPU_CLK_CTRL_DIV (ioread32 (cpuclk->clk_ctrl)); +} + +static const struct clk_ops zynq_cpu_clk_ops = { + .get_parent = zynq_cpu_clk_get_parent, + .recalc_rate = zynq_cpu_clk_recalc_rate, +}; + +struct zynq_cpu_subclk { +struct clk_hw hw; +void __iomem *clk_621; +enum { +CPU_SUBCLK_6X4X , +CPU_SUBCLK_3X2X , +CPU_SUBCLK_2X , +CPU_SUBCLK_1X , + } which; +}; + +#define CLK_621_TRUE (x) ((x) & 1) + +#define to_zynq_cpu_subclk (hw) container_of(hw, struct zynq_cpu_subclk, hw); + +static unsigned long zynq_cpu_subclk_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +unsigned long uninitialized_var (rate ); +struct zynq_cpu_subclk *subclk; +bool is_621; + + subclk =to_zynq_cpu_subclk (hw) + is_621 = CLK_621_TRUE(ioread32(subclk->clk_621)); + +switch (subclk->which) { +case CPU_SUBCLK_6X4X : + rate = parent_rate; +break ; +case CPU_SUBCLK_3X2X : + rate = parent_rate /2 ; +break ; +case CPU_SUBCLK_2X : + rate = parent_rate / (is_621 ?3 :2 ); +break ; +case CPU_SUBCLK_1X : + rate = parent_rate / (is_621 ?6 :4 ); +break ; + }; + +return rate; +} + +static const struct clk_ops zynq_cpu_subclk_ops = { + .recalc_rate = zynq_cpu_subclk_recalc_rate, +}; + +static struct clk *zynq_cpu_subclk_setup (struct device_node *np,u8 which, +void __iomem *clk_621) +{ +struct zynq_cpu_subclk *subclk; +struct clk_init_data init; +struct clk *clk; +int err; + + err =of_property_read_string_index (np,"clock-output-names" , + which, &init.name); +if (WARN_ON (err)) + goto err_read_output_name; + + subclk =kzalloc (sizeof (*subclk),GFP_KERNEL ); +if (!subclk) + goto err_subclk_alloc; + + subclk->clk_621 = clk_621; + subclk->which = which; + + init.ops = &zynq_cpu_subclk_ops; + init.parent_names = &np->name; + init.num_parents =1 ; + + subclk->hw.init = &init; + + clk =clk_register (NULL , &subclk->hw); +if (WARN_ON (IS_ERR (clk))) + goto err_clk_register; + +return clk; + +err_clk_register: +kfree (subclk); +err_subclk_alloc: +err_read_output_name: +return ERR_PTR (-EINVAL ); +} + +static void __init zynq_cpu_clk_setup (struct device_node *np) +{ +struct zynq_cpu_clk *cpuclk; +const char *parent_names[3 ]; +struct clk_init_data init; +void __iomem *clk_621; +struct clk *clk; +u32 reg[2 ]; +int err; +int i; + + err =of_property_read_u32_array (np,"reg" , reg,ARRAY_SIZE (reg)); +if (WARN_ON (err)) +return ; + + cpuclk =kzalloc (sizeof (*cpuclk),GFP_KERNEL ); +if (WARN_ON (!cpuclk)) +return ; + + cpuclk->clk_ctrl = slcr_base + reg[0 ]; + clk_621 = slcr_base + reg[1 ]; +spin_lock_init (&cpuclk->clkact_lock); + + init.name = np->name; + init.ops = &zynq_cpu_clk_ops; +for (i =0 ; i <ARRAY_SIZE (parent_names); i++) + parent_names[i] =of_clk_get_parent_name (np, i); + init.parent_names = parent_names; + init.num_parents =ARRAY_SIZE (parent_names); + + cpuclk->hw.init = &init; + + clk =clk_register (NULL , &cpuclk->hw); +if (WARN_ON (IS_ERR (clk))) +return ; + + err =of_clk_add_provider (np, of_clk_src_simple_get, clk); +if (WARN_ON (err)) +return ; + +for (i =0 ; i <4 ; i++) { + cpuclk->subclks[i] =zynq_cpu_subclk_setup (np, i, clk_621); +if (WARN_ON (IS_ERR (cpuclk->subclks[i]))) +return ; + } + + cpuclk->onecell_data.clks = cpuclk->subclks; + cpuclk->onecell_data.clk_num = i; + + err =of_clk_add_provider (np, of_clk_src_onecell_get, + &cpuclk->onecell_data); +if (WARN_ON (err)) +return ; +} + +static const __initconst struct of_device_id zynq_clk_match[] = { + { .compatible ="fixed-clock" , .data = of_fixed_clk_setup, }, + { .compatible ="xlnx,zynq-pll" , .data = zynq_pll_clk_setup, }, + { .compatible ="xlnx,zynq-periph-clock" , + .data = zynq_periph_clk_setup, }, + { .compatible ="xlnx,zynq-cpu-clock" , .data = zynq_cpu_clk_setup, }, + {} +}; + +void __init xilinx_zynq_clocks_init (void __iomem *slcr) +{ + slcr_base = slcr; +of_clk_init (zynq_clk_match); +}include/linux/clk/zynq.h new file +24
-0@@ -0,0 +1,24 @@ +/* +* Copyright (C) 2012 National Instruments +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LINUX_CLK_ZYNQ_H_ +#define __LINUX_CLK_ZYNQ_H_ + +void __init xilinx_zynq_clocks_init (void __iomem *slcr); + +#endif Signature · 1 line
1.8.0
Michal Simek
AppliedRE: [PATCH v3] clk: Add support for fundamental zynq clks
Quoted 24 lines
-----Original Message-----
From: Mike Turquette [mailto:mturquette at ti.com]
Sent: Wednesday, November 14, 2012 12:55 AM
To: Josh Cartwright; Michal Simek
Cc: Soren Brinkmann; Rob Herring; linux-kernel at vger.kernel.org; linux-arm- kernel at lists.infradead.org; John Linn; arm at kernel.org
Subject: Re: [PATCH v3] clk: Add support for fundamental zynq clksQuoting Josh Cartwright (2012-11-13 15:26:48)
Quoted 13 lines
Provide simplified models for the necessary clocks on the zynq-7000 platform. Currently, the PLLs, the CPU clock network, and the basic peripheral clock networks (for SDIO, SMC, SPI, QSPI, UART) are modelled.
OF bindings are also provided and documented.
Signed-off-by Josh Cartwright <[email protected]>---
Michal-Here is a v3 with a fix for the problem Soren Brinkmann spotted. Am I correct that your current plan is to merge this into your tree?
Quoted 8 lines
Quoted 6 lines
If so:
Mike-
Can we get your Acked-by on this one?
Acked-by Mike Turquette <[email protected]>
Applied.
Thanks,
Michal
A message that is not in the archive (sent off-list or not yet received): [email protected]
RE: [PATCH v2 2/5] clk: Add support for fundamental zynq clks
Quoted 199 lines
-----Original Message-----
From: Soren Brinkmann [mailto:soren.brinkmann at xilinx.com]
Sent: Friday, November 09, 2012 12:28 AM
To: Josh Cartwright
Cc: Mike Turquette; Rob Herring; linux-kernel at vger.kernel.org; linux-arm- kernel at lists.infradead.org; Michal Simek; John Linn; arm at kernel.org
Subject: Re: [PATCH v2 2/5] clk: Add support for fundamental zynq clksOne note below:
Quoted 24 lines · Josh Cartwright
Provide simplified models for the necessary clocks on the zynq-7000 platform. Currently, the PLLs, the CPU clock network, and the basic peripheral clock networks (for SDIO, SMC, SPI, QSPI, UART) are modelled.
OF bindings are also provided and documented.
Signed-off-by Josh Cartwright <[email protected]>--- .../devicetree/bindings/clock/zynq-7000.txt | 55 +++ drivers/clk/clk-zynq.c | 383 +++++++++++++++++++++ include/linux/clk/zynq.h | 24 ++ 3 files changed, 462 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/zynq-7000.txt create mode 100644 drivers/clk/clk-zynq.c create mode 100644
include/linux/clk/zynq.hDocumentation/devicetree/bindings/clock/zynq-7000.txt new file +0
-0Documentation/devicetree/bindings/clock/zynq-7000.txt line 3
Quoted 3 lines
Documentation/devicetree/bindings/clock/zynq-7000.txt new file +3
-0@@ -0,0 +1,55 @@ +Device Tree Clock bindings for the Zynq 7000 EPP + +The Zynq EPP has several different clk providers, each with there ownbindings.
Quoted 32 lines
Documentation/devicetree/bindings/clock/zynq-7000.txt new file +32
-0+The purpose of this document is to document their usage. + +See clock_bindings.txt for more information on the generic clock bindings. +See Chapter 25 of Zynq TRM for more information about Zynq clocks. + +== PLLs == + +Used to describe the ARM_PLL, DDR_PLL, and IO_PLL. + +Required properties: +- #clock-cells : shall be 0 (only one clock is output from this node) +- compatible : "xlnx,zynq-pll" +- reg : pair of u32 values, which are the address offsets within the SLCR + of the relevant PLL_CTRL register and PLL_CFG register +respectively +- clocks : phandle for parent clock. should be the phandle for +ps_clk + +Optional properties: +- clock-output-names : name of the output clock + +Example: + armpll: armpll { + #clock-cells = <0>; + compatible = "xlnx,zynq-pll"; + clocks = <&ps_clk>; + reg = <0x100 0x110>; + clock-output-names = "armpll"; + }; + +== Peripheral clocks == +Documentation/devicetree/bindings/clock/zynq-7000.txt
Quoted 10 lines
Documentation/devicetree/bindings/clock/zynq-7000.txt new file +10
-0+Describes clock node for the SDIO, SMC, SPI, QSPI, and UART clocks. + +Required properties: +- #clock-cells : shall be 1 +- compatible : "xlnx,zynq-periph-clock" +- reg : a single u32 value, describing the offset within the SLCR where + the CLK_CTRL register is found for this peripheral +- clocks : phandle for parent clocks. should hold phandles for + the IO_PLL, ARM_PLL, and DDR_PLL in order +- clock-output-names : names of the output clock(s). For peripherals thathave
Quoted 104 lines
Documentation/devicetree/bindings/clock/zynq-7000.txt new file +12
-0+ two output clocks (for example, the UART), two clocks + should be listed. + +Example: + uart_clk: uart_clk { + #clock-cells = <1>; + compatible = "xlnx,zynq-periph-clock"; + clocks = <&iopll &armpll &ddrpll>; + reg = <0x154>; + clock-output-names = "uart0_ref_clk", + "uart1_ref_clk"; + };drivers/clk/clk-zynq.c new file renamed from drivers/clk/clk-zynq.c +0
-0mode 100644 index 0000000..de8b586
drivers/clk/clk-zynq.c new file +87
-0@@ -0,0 +1,383 @@ +/* +* Copyright (c) 2012 National Instruments +* +* Josh Cartwright <[email protected]> +* +* This program is free software; you can redistribute it and/or +modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but +WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for +* more details. +* +* You should have received a copy of the GNU General Public License +along with +* this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <linux/io.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/clk-provider.h> + +static void __iomem *slcr_base; + +struct zynq_pll_clk { +struct clk_hw hw; +void __iomem *pll_ctrl; +void __iomem *pll_cfg; +}; + +#define to_zynq_pll_clk (hw) container_of(hw, struct zynq_pll_clk, hw) + +#define CTRL_PLL_FDIV (x) ((x) >> 12) + +static unsigned long zynq_pll_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct zynq_pll_clk *pll =to_zynq_pll_clk (hw); +return parent_rate *CTRL_PLL_FDIV (ioread32 (pll->pll_ctrl)); +} + +static const struct clk_ops zynq_pll_clk_ops = { + .recalc_rate = zynq_pll_recalc_rate, +}; + +static void __init zynq_pll_clk_setup (struct device_node *np) { +struct clk_init_data init; +struct zynq_pll_clk *pll; +const char *parent_name; +struct clk *clk; +u32 regs[2 ]; +int ret; + + ret =of_property_read_u32_array (np,"reg" , regs,ARRAY_SIZE (regs)); +if (WARN_ON (ret)) +return ; + + pll =kzalloc (sizeof (*pll),GFP_KERNEL ); +if (WARN_ON (!pll)) +return ; + + pll->pll_ctrl = slcr_base + regs[0 ]; + pll->pll_cfg = slcr_base + regs[1 ]; + +of_property_read_string (np,"clock-output-names" , &init.name); + + init.ops = &zynq_pll_clk_ops; + parent_name =of_clk_get_parent_name (np,0 ); + init.parent_names = &parent_name; + init.num_parents =1 ; + + pll->hw.init = &init; + + clk =clk_register (NULL , &pll->hw); +if (WARN_ON (IS_ERR (clk))) +return ; + + ret =of_clk_add_provider (np, of_clk_src_simple_get, clk); +if (WARN_ON (ret)) +return ; +}drivers/clk/clk-zynq.c line 97
Quoted 10 lines
drivers/clk/clk-zynq.c new file +10
-0+ +struct zynq_periph_clk { +struct clk_hw hw; +struct clk_onecell_data onecell_data; +struct clk *gates[2 ]; +void __iomem *clk_ctrl; +spinlock_t clkact_lock; +}; + +#define to_zynq_periph_clk (hw) container_of(hw, struct zynq_periph_clk,hw)
Quoted 9 lines
drivers/clk/clk-zynq.c line 98
Quoted 5 lines
drivers/clk/clk-zynq.c new file +5
-0+ +static const u8 periph_clk_parent_map[] = { +0 ,0 ,1 ,2 +}; +#define PERIPH_CLK_CTRL_SRC (x) (periph_clk_parent_map[((x) & 3) >> 4])I think this should be:
#definePERIPH_CLK_CTRL_SRC(x)(periph_clk_parent_map[((x) &0x30) >>
4])
Josh: Any comment?
Thanks,
Michal
PATCH v2 3/5 ARM: zynq: use zynq clk bindings
In reply to Josh CartwrightMake the Zynq platform use the newly created zynq clk bindings.
--- arch/arm/Kconfig | 1 + arch/arm/boot/dts/zynq-7000.dtsi | 56 ++++++++++++++++++++++++++++++++++++++++ arch/arm/boot/dts/zynq-zc702.dts | 4 +++ arch/arm/mach-zynq/common.c | 11 ++++++++ drivers/clk/Makefile | 1 + 5 files changed, 73 insertions(+)
arch/arm/Kconfig +1 -0
@@ -959,6 +959,7 @@ config ARCH_ZYNQ bool "Xilinx Zynq ARM Cortex A9 Platform" select ARM_AMBA select ARM_GIC + select COMMON_CLK select CPU_V7 select GENERIC_CLOCKEVENTS select ICST
arch/arm/boot/dts/zynq-7000.dtsi +56 -0
61 lines not on this page · raw message
arch/arm/boot/dts/zynq-zc702.dts +4 -0
@@ -28,3 +28,7 @@ }; }; + +&ps_clk { + clock-frequency = <33333330>; +};
arch/arm/mach-zynq/common.c +11 -0
@@ -19,6 +19,8 @@#include <linux/cpumask.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/clk/zynq.h> +#include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/of.h>
@@ -96,6 +98,15 @@ static struct map_desc io_desc[] __initdata = {static void __init xilinx_zynq_timer_init (void ) { +struct device_node *np; +void __iomem *slcr; + + np =of_find_compatible_node (NULL ,NULL ,"xlnx,zynq-slcr" ); + slcr =of_iomap (np,0 ); +WARN_ON (!slcr); + +xilinx_zynq_clocks_init (slcr); +xttcpss_timer_init (); }
drivers/clk/Makefile +1 -0
@@ -19,6 +19,7 @@ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o +obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o # Chip specific obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
Signature · 1 line
1.8.0
PATCH v2 4/5 serial: xilinx_uartps: get clock rate info from dts
In reply to Josh CartwrightAdd support for specifying clock information for the uart clk via the device tree. This eliminates the need to hardcode rates in the device tree.
--- arch/arm/boot/dts/zynq-7000.dtsi | 4 ++-- drivers/tty/serial/xilinx_uartps.c | 30 +++++++++++++++++------------- 2 files changed, 19 insertions(+), 15 deletions(-)
arch/arm/boot/dts/zynq-7000.dtsi +2 -2
@@ -44,14 +44,14 @@ compatible = "xlnx,xuartps"; reg = <0xE0000000 0x1000>; interrupts = <0 27 4>; - clock = <50000000>; + clocks = <&uart_clk 0>; }; uart1: uart at e0001000 { compatible = "xlnx,xuartps"; reg = <0xE0001000 0x1000>; interrupts = <0 50 4>; - clock = <50000000>; + clocks = <&uart_clk 1>; }; slcr: slcr at f8000000 {
drivers/tty/serial/xilinx_uartps.c +17 -13
@@ -17,6 +17,7 @@#include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/console.h> +#include <linux/clk.h> #include <linux/irq.h> #include <linux/io.h> #include <linux/of.h>
@@ -944,18 +945,20 @@ static int __devinit xuartps_probe(struct platform_device *pdev)int rc;struct uart_port *port;struct resource *res, *res2; -int clk =0 ; +struct clk *clk; -const unsigned int *prop; - - prop =of_get_property (pdev->dev.of_node,"clock" ,NULL ); -if (prop) - clk =be32_to_cpup (prop); + clk =of_clk_get (pdev->dev.of_node,0 );if (!clk) {dev_err (&pdev->dev,"no clock specified\n" );return -ENODEV ; } + rc =clk_prepare_enable (clk); +if (rc) { +dev_err (&pdev->dev,"could not enable clock\n" ); +return -EBUSY ; + } + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 );if (!res)return -ENODEV ;
@@ -978,7 +981,8 @@ static int __devinit xuartps_probe(struct platform_device *pdev) port->mapbase = res->start; port->irq = res2->start; port->dev = &pdev->dev; - port->uartclk = clk; + port->uartclk =clk_get_rate (clk); + port->private_data = clk;dev_set_drvdata (&pdev->dev, port); rc =uart_add_one_port (&xuartps_uart_driver, port);if (rc) {
@@ -1000,14 +1004,14 @@ static int __devinit xuartps_probe(struct platform_device *pdev) static int __devexit xuartps_remove(struct platform_device *pdev) {struct uart_port *port =dev_get_drvdata (&pdev->dev); -int rc =0 ; +struct clk *clk = port->private_data; +int rc;/* Remove the xuartps port from the serial core */ -if (port) { - rc =uart_remove_one_port (&xuartps_uart_driver, port); -dev_set_drvdata (&pdev->dev,NULL ); - port->mapbase =0 ; - } + rc =uart_remove_one_port (&xuartps_uart_driver, port); +dev_set_drvdata (&pdev->dev,NULL ); + port->mapbase =0 ; +clk_disable_unprepare (clk);return rc; }
Signature · 1 line
1.8.0
PATCH v2 5/5 ARM: zynq: add clk binding support to the ttc
In reply to Josh CartwrightAdd support for retrieving TTC configuration from device tree. This includes the ability to pull information about the driving clocks from the of_clk bindings.
--- arch/arm/boot/dts/zynq-7000.dtsi | 53 ++++++++ arch/arm/boot/dts/zynq-zc702.dts | 10 ++ arch/arm/mach-zynq/timer.c | 287 ++++++++++++++++++++++----------------- 3 files changed, 228 insertions(+), 122 deletions(-)
arch/arm/boot/dts/zynq-7000.dtsi +53 -0
@@ -109,5 +109,58 @@ }; }; }; + + ttc0: ttc0 at f8001000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "xlnx,ttc"; + reg = <0xF8001000 0x1000>; + clocks = <&cpu_clk 3>; + clock-names = "cpu_1x"; + clock-ranges; + + ttc0_0: ttc0.0 { + status = "disabled"; + reg = <0>; + interrupts = <0 10 4>; + }; + ttc0_1: ttc0.1 { + status = "disabled"; + reg = <1>; + interrupts = <0 11 4>; + }; + ttc0_2: ttc0.2 { + status = "disabled"; + reg = <2>; + interrupts = <0 12 4>; + }; + }; + + ttc1: ttc1 at f8002000 { + #interrupt-parent = <&intc>; + #address-cells = <1>; + #size-cells = <0>; + compatible = "xlnx,ttc"; + reg = <0xF8002000 0x1000>; + clocks = <&cpu_clk 3>; + clock-names = "cpu_1x"; + clock-ranges; + + ttc1_0: ttc1.0 { + status = "disabled"; + reg = <0>; + interrupts = <0 37 4>; + }; + ttc1_1: ttc1.1 { + status = "disabled"; + reg = <1>; + interrupts = <0 38 4>; + }; + ttc1_2: ttc1.2 { + status = "disabled"; + reg = <2>; + interrupts = <0 39 4>; + }; + }; }; };
arch/arm/boot/dts/zynq-zc702.dts +10 -0
@@ -32,3 +32,13 @@ &ps_clk { clock-frequency = <33333330>; }; + +&ttc0_0 { + status = "ok"; + compatible = "xlnx,ttc-counter-clocksource"; +}; + +&ttc0_1 { + status = "ok"; + compatible = "xlnx,ttc-counter-clockevent"; +};
arch/arm/mach-zynq/timer.c +165 -122
368 lines not on this page · raw message
Signature · 1 line
1.8.0
Hi Josh,
did you also have a look at what is available in the Xilinx git repository? There are also quite some patches to support common clock and port drivers over to using it.
It would be great if we could combine our efforts instead of ending up with competing solutions.
Thanks,
Soren