From 5658a68fb578df8c9b9dd3071b2a56bf92c15324 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 20 Sep 2012 15:04:33 +0200 Subject: ARM i.MX25: Add devicetree This adds a i.MX25 dtsi file along with the i.MX25 clock tree documentation. The devicetree should be fairly complete for: - uart - fec - i2c - spi - pwm - nand - gpio - wdog - esdhc - flexcan The more exotic devices currently miss clock bindings. Signed-off-by: Sascha Hauer Acked-by: Shawn Guo --- .../devicetree/bindings/clock/imx25-clock.txt | 162 +++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/imx25-clock.txt (limited to 'Documentation/devicetree/bindings/clock') diff --git a/Documentation/devicetree/bindings/clock/imx25-clock.txt b/Documentation/devicetree/bindings/clock/imx25-clock.txt new file mode 100644 index 000000000000..c2a3525ecb4e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx25-clock.txt @@ -0,0 +1,162 @@ +* Clock bindings for Freescale i.MX25 + +Required properties: +- compatible: Should be "fsl,imx25-ccm" +- reg: Address and length of the register set +- interrupts: Should contain CCM interrupt +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. The following is a full list of i.MX25 +clocks and IDs. + + Clock ID + --------------------------- + dummy 0 + osc 1 + mpll 2 + upll 3 + mpll_cpu_3_4 4 + cpu_sel 5 + cpu 6 + ahb 7 + usb_div 8 + ipg 9 + per0_sel 10 + per1_sel 11 + per2_sel 12 + per3_sel 13 + per4_sel 14 + per5_sel 15 + per6_sel 16 + per7_sel 17 + per8_sel 18 + per9_sel 19 + per10_sel 20 + per11_sel 21 + per12_sel 22 + per13_sel 23 + per14_sel 24 + per15_sel 25 + per0 26 + per1 27 + per2 28 + per3 29 + per4 30 + per5 31 + per6 32 + per7 33 + per8 34 + per9 35 + per10 36 + per11 37 + per12 38 + per13 39 + per14 40 + per15 41 + csi_ipg_per 42 + epit_ipg_per 43 + esai_ipg_per 44 + esdhc1_ipg_per 45 + esdhc2_ipg_per 46 + gpt_ipg_per 47 + i2c_ipg_per 48 + lcdc_ipg_per 49 + nfc_ipg_per 50 + owire_ipg_per 51 + pwm_ipg_per 52 + sim1_ipg_per 53 + sim2_ipg_per 54 + ssi1_ipg_per 55 + ssi2_ipg_per 56 + uart_ipg_per 57 + ata_ahb 58 + reserved 59 + csi_ahb 60 + emi_ahb 61 + esai_ahb 62 + esdhc1_ahb 63 + esdhc2_ahb 64 + fec_ahb 65 + lcdc_ahb 66 + rtic_ahb 67 + sdma_ahb 68 + slcdc_ahb 69 + usbotg_ahb 70 + reserved 71 + reserved 72 + reserved 73 + reserved 74 + can1_ipg 75 + can2_ipg 76 + csi_ipg 77 + cspi1_ipg 78 + cspi2_ipg 79 + cspi3_ipg 80 + dryice_ipg 81 + ect_ipg 82 + epit1_ipg 83 + epit2_ipg 84 + reserved 85 + esdhc1_ipg 86 + esdhc2_ipg 87 + fec_ipg 88 + reserved 89 + reserved 90 + reserved 91 + gpt1_ipg 92 + gpt2_ipg 93 + gpt3_ipg 94 + gpt4_ipg 95 + reserved 96 + reserved 97 + reserved 98 + iim_ipg 99 + reserved 100 + reserved 101 + kpp_ipg 102 + lcdc_ipg 103 + reserved 104 + pwm1_ipg 105 + pwm2_ipg 106 + pwm3_ipg 107 + pwm4_ipg 108 + rngb_ipg 109 + reserved 110 + scc_ipg 111 + sdma_ipg 112 + sim1_ipg 113 + sim2_ipg 114 + slcdc_ipg 115 + spba_ipg 116 + ssi1_ipg 117 + ssi2_ipg 118 + tsc_ipg 119 + uart1_ipg 120 + uart2_ipg 121 + uart3_ipg 122 + uart4_ipg 123 + uart5_ipg 124 + reserved 125 + wdt_ipg 126 + +Examples: + +clks: ccm@53f80000 { + compatible = "fsl,imx25-ccm"; + reg = <0x53f80000 0x4000>; + interrupts = <31>; + clock-output-names = ... + "uart_ipg", + "uart_serial", + ...; +}; + +uart1: serial@43f90000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x43f90000 0x4000>; + interrupts = <45>; + clocks = <&clks 79>, <&clks 50>; + clock-names = "ipg", "per"; + status = "disabled"; +}; -- cgit v1.2.3-59-g8ed1b From 45aa2c27ada4829bc91bd80455bc67059ac06bc5 Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Tue, 13 Nov 2012 17:26:48 -0600 Subject: clk: Add support for fundamental zynq clks 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 Signed-off-by: Soren Brinkmann Acked-by: Mike Turquette Acked-by: Michal Simek --- .../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 (limited to 'Documentation/devicetree/bindings/clock') diff --git a/Documentation/devicetree/bindings/clock/zynq-7000.txt b/Documentation/devicetree/bindings/clock/zynq-7000.txt new file mode 100644 index 000000000000..23ae1db1bc13 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/zynq-7000.txt @@ -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"; + }; diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c new file mode 100644 index 000000000000..37a30514fd66 --- /dev/null +++ b/drivers/clk/clk-zynq.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2012 National Instruments + * + * Josh Cartwright + * + * 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 . + */ +#include +#include +#include +#include +#include + +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); +} diff --git a/include/linux/clk/zynq.h b/include/linux/clk/zynq.h new file mode 100644 index 000000000000..56be7cd9aa8b --- /dev/null +++ b/include/linux/clk/zynq.h @@ -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 -- cgit v1.2.3-59-g8ed1b