arm: Xilinx Zynq dt changes for v3.11

The branch contains:
 - DT uart handling cleanup
 - Support for zc706 and zed board
 - Removal of board compatible string
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.10 (GNU/Linux)
 
 iEYEABECAAYFAlG+ysgACgkQykllyylKDCENewCgk3LUwpMxepV1JOaNBmUJ0A09
 jtMAnA395sve+FBxYjfM+nSAmbnl5RYl
 =KBrZ
 -----END PGP SIGNATURE-----

Merge tag 'zynq-dt-for-3.11' of git://git.xilinx.com/linux-xlnx into next/dt

From Michal Simek:

arm: Xilinx Zynq dt changes for v3.11

The branch contains:
- DT uart handling cleanup
- Support for zc706 and zed board
- Removal of board compatible string

* tag 'zynq-dt-for-3.11' of git://git.xilinx.com/linux-xlnx:
  arm: dt: zynq: Add support for the zed platform
  arm: dt: zynq: Add support for the zc706 platform
  arm: dt: zynq: Use 'status' property for UART nodes
  arm: zynq: Remove board specific compatibility string
  clk: zynq: Remove deprecated clock code
  arm: zynq: Migrate platform to clock controller
  clk: zynq: Add clock controller driver
  clk: zynq: Factor out PLL driver

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2013-06-20 01:43:19 +02:00
commit a44bde66be
16 changed files with 1052 additions and 498 deletions

View file

@ -6,50 +6,99 @@ The purpose of this document is to document their usage.
See clock_bindings.txt for more information on the generic clock bindings. See clock_bindings.txt for more information on the generic clock bindings.
See Chapter 25 of Zynq TRM for more information about Zynq clocks. See Chapter 25 of Zynq TRM for more information about Zynq clocks.
== PLLs == == Clock Controller ==
The clock controller is a logical abstraction of Zynq's clock tree. It reads
Used to describe the ARM_PLL, DDR_PLL, and IO_PLL. required input clock frequencies from the devicetree and acts as clock provider
for all clock consumers of PS clocks.
Required properties: Required properties:
- #clock-cells : shall be 0 (only one clock is output from this node) - #clock-cells : Must be 1
- compatible : "xlnx,zynq-pll" - compatible : "xlnx,ps7-clkc"
- reg : pair of u32 values, which are the address offsets within the SLCR - ps-clk-frequency : Frequency of the oscillator providing ps_clk in HZ
of the relevant PLL_CTRL register and PLL_CFG register respectively (usually 33 MHz oscillators are used for Zynq platforms)
- clocks : phandle for parent clock. should be the phandle for ps_clk - clock-output-names : List of strings used to name the clock outputs. Shall be
a list of the outputs given below.
Optional properties: Optional properties:
- clock-output-names : name of the output clock - clocks : as described in the clock bindings
- clock-names : as described in the clock bindings
Clock inputs:
The following strings are optional parameters to the 'clock-names' property in
order to provide an optional (E)MIO clock source.
- swdt_ext_clk
- gem0_emio_clk
- gem1_emio_clk
- mio_clk_XX # with XX = 00..53
...
Clock outputs:
0: armpll
1: ddrpll
2: iopll
3: cpu_6or4x
4: cpu_3or2x
5: cpu_2x
6: cpu_1x
7: ddr2x
8: ddr3x
9: dci
10: lqspi
11: smc
12: pcap
13: gem0
14: gem1
15: fclk0
16: fclk1
17: fclk2
18: fclk3
19: can0
20: can1
21: sdio0
22: sdio1
23: uart0
24: uart1
25: spi0
26: spi1
27: dma
28: usb0_aper
29: usb1_aper
30: gem0_aper
31: gem1_aper
32: sdio0_aper
33: sdio1_aper
34: spi0_aper
35: spi1_aper
36: can0_aper
37: can1_aper
38: i2c0_aper
39: i2c1_aper
40: uart0_aper
41: uart1_aper
42: gpio_aper
43: lqspi_aper
44: smc_aper
45: swdt
46: dbg_trc
47: dbg_apb
Example: Example:
armpll: armpll { clkc: clkc {
#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>; #clock-cells = <1>;
compatible = "xlnx,zynq-periph-clock"; compatible = "xlnx,ps7-clkc";
clocks = <&iopll &armpll &ddrpll>; ps-clk-frequency = <33333333>;
reg = <0x154>; clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x",
clock-output-names = "uart0_ref_clk", "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x",
"uart1_ref_clk"; "dci", "lqspi", "smc", "pcap", "gem0", "gem1",
"fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1",
"sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1",
"dma", "usb0_aper", "usb1_aper", "gem0_aper",
"gem1_aper", "sdio0_aper", "sdio1_aper",
"spi0_aper", "spi1_aper", "can0_aper", "can1_aper",
"i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper",
"gpio_aper", "lqspi_aper", "smc_aper", "swdt",
"dbg_trc", "dbg_apb";
# optional props
clocks = <&clkc 16>, <&clk_foo>;
clock-names = "gem1_emio_clk", "can_mio_clk_23";
}; };

View file

@ -216,7 +216,9 @@ dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
wm8650-mid.dtb \ wm8650-mid.dtb \
wm8750-apc8750.dtb \ wm8750-apc8750.dtb \
wm8850-w70v2.dtb wm8850-w70v2.dtb
dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb \
zynq-zc706.dtb \
zynq-zed.dtb
targets += dtbs targets += dtbs
targets += $(dtb-y) targets += $(dtb-y)

View file

@ -49,16 +49,20 @@ L2: cache-controller {
uart0: uart@e0000000 { uart0: uart@e0000000 {
compatible = "xlnx,xuartps"; compatible = "xlnx,xuartps";
status = "disabled";
clocks = <&clkc 23>, <&clkc 40>;
clock-names = "ref_clk", "aper_clk";
reg = <0xE0000000 0x1000>; reg = <0xE0000000 0x1000>;
interrupts = <0 27 4>; interrupts = <0 27 4>;
clocks = <&uart_clk 0>;
}; };
uart1: uart@e0001000 { uart1: uart@e0001000 {
compatible = "xlnx,xuartps"; compatible = "xlnx,xuartps";
status = "disabled";
clocks = <&clkc 24>, <&clkc 41>;
clock-names = "ref_clk", "aper_clk";
reg = <0xE0001000 0x1000>; reg = <0xE0001000 0x1000>;
interrupts = <0 50 4>; interrupts = <0 50 4>;
clocks = <&uart_clk 1>;
}; };
slcr: slcr@f8000000 { slcr: slcr@f8000000 {
@ -69,50 +73,21 @@ clocks {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
ps_clk: ps_clk { clkc: clkc {
#clock-cells = <0>;
compatible = "fixed-clock";
/* clock-frequency set in board-specific file */
clock-output-names = "ps_clk";
};
armpll: armpll {
#clock-cells = <0>;
compatible = "xlnx,zynq-pll";
clocks = <&ps_clk>;
reg = <0x100 0x110>;
clock-output-names = "armpll";
};
ddrpll: ddrpll {
#clock-cells = <0>;
compatible = "xlnx,zynq-pll";
clocks = <&ps_clk>;
reg = <0x104 0x114>;
clock-output-names = "ddrpll";
};
iopll: iopll {
#clock-cells = <0>;
compatible = "xlnx,zynq-pll";
clocks = <&ps_clk>;
reg = <0x108 0x118>;
clock-output-names = "iopll";
};
uart_clk: uart_clk {
#clock-cells = <1>; #clock-cells = <1>;
compatible = "xlnx,zynq-periph-clock"; compatible = "xlnx,ps7-clkc";
clocks = <&iopll &armpll &ddrpll>; ps-clk-frequency = <33333333>;
reg = <0x154>; clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x",
clock-output-names = "uart0_ref_clk", "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x",
"uart1_ref_clk"; "dci", "lqspi", "smc", "pcap", "gem0", "gem1",
}; "fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1",
cpu_clk: cpu_clk { "sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1",
#clock-cells = <1>; "dma", "usb0_aper", "usb1_aper", "gem0_aper",
compatible = "xlnx,zynq-cpu-clock"; "gem1_aper", "sdio0_aper", "sdio1_aper",
clocks = <&iopll &armpll &ddrpll>; "spi0_aper", "spi1_aper", "can0_aper", "can1_aper",
reg = <0x120 0x1C4>; "i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper",
clock-output-names = "cpu_6x4x", "gpio_aper", "lqspi_aper", "smc_aper", "swdt",
"cpu_3x2x", "dbg_trc", "dbg_apb";
"cpu_2x",
"cpu_1x";
}; };
}; };
}; };
@ -121,9 +96,8 @@ ttc0: ttc0@f8001000 {
interrupt-parent = <&intc>; interrupt-parent = <&intc>;
interrupts = < 0 10 4 0 11 4 0 12 4 >; interrupts = < 0 10 4 0 11 4 0 12 4 >;
compatible = "cdns,ttc"; compatible = "cdns,ttc";
clocks = <&clkc 6>;
reg = <0xF8001000 0x1000>; reg = <0xF8001000 0x1000>;
clocks = <&cpu_clk 3>;
clock-names = "cpu_1x";
clock-ranges; clock-ranges;
}; };
@ -131,9 +105,8 @@ ttc1: ttc1@f8002000 {
interrupt-parent = <&intc>; interrupt-parent = <&intc>;
interrupts = < 0 37 4 0 38 4 0 39 4 >; interrupts = < 0 37 4 0 38 4 0 39 4 >;
compatible = "cdns,ttc"; compatible = "cdns,ttc";
clocks = <&clkc 6>;
reg = <0xF8002000 0x1000>; reg = <0xF8002000 0x1000>;
clocks = <&cpu_clk 3>;
clock-names = "cpu_1x";
clock-ranges; clock-ranges;
}; };
scutimer: scutimer@f8f00600 { scutimer: scutimer@f8f00600 {
@ -141,7 +114,7 @@ scutimer: scutimer@f8f00600 {
interrupts = < 1 13 0x301 >; interrupts = < 1 13 0x301 >;
compatible = "arm,cortex-a9-twd-timer"; compatible = "arm,cortex-a9-twd-timer";
reg = < 0xf8f00600 0x20 >; reg = < 0xf8f00600 0x20 >;
clocks = <&cpu_clk 1>; clocks = <&clkc 4>;
} ; } ;
}; };
}; };

View file

@ -24,11 +24,11 @@ memory {
}; };
chosen { chosen {
bootargs = "console=ttyPS1,115200 earlyprintk"; bootargs = "console=ttyPS0,115200 earlyprintk";
}; };
}; };
&ps_clk { &uart1 {
clock-frequency = <33333330>; status = "okay";
}; };

View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2011 Xilinx
* Copyright (C) 2012 National Instruments Corp.
* Copyright (C) 2013 Xilinx
*
* 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 ZC706 Development Board";
compatible = "xlnx,zynq-zc706", "xlnx,zynq-7000";
memory {
device_type = "memory";
reg = <0 0x40000000>;
};
chosen {
bootargs = "console=ttyPS0,115200 earlyprintk";
};
};
&uart1 {
status = "okay";
};

View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2011 Xilinx
* Copyright (C) 2012 National Instruments Corp.
* Copyright (C) 2013 Xilinx
*
* 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 Zed Development Board";
compatible = "xlnx,zynq-7000";
memory {
device_type = "memory";
reg = <0 0x20000000>;
};
chosen {
bootargs = "console=ttyPS0,115200 earlyprintk";
};
};
&uart1 {
status = "okay";
};

View file

@ -98,7 +98,6 @@ static void zynq_system_reset(char mode, const char *cmd)
} }
static const char * const zynq_dt_match[] = { static const char * const zynq_dt_match[] = {
"xlnx,zynq-zc702",
"xlnx,zynq-7000", "xlnx,zynq-7000",
NULL NULL
}; };

View file

@ -117,7 +117,7 @@ int __init zynq_slcr_init(void)
pr_info("%s mapped to %p\n", np->name, zynq_slcr_base); pr_info("%s mapped to %p\n", np->name, zynq_slcr_base);
xilinx_zynq_clocks_init(zynq_slcr_base); zynq_clock_init(zynq_slcr_base);
of_node_put(np); of_node_put(np);

View file

@ -27,7 +27,7 @@ obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o obj-$(CONFIG_ARCH_ZYNQ) += zynq/
obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ obj-$(CONFIG_PLAT_SAMSUNG) += samsung/

View file

@ -1,378 +0,0 @@
/*
* Copyright (c) 2012 National Instruments
*
* Josh Cartwright <josh.cartwright@ni.com>
*
* 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>
#include <linux/clk/zynq.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;
}
CLK_OF_DECLARE(zynq_pll, "xlnx,zynq-pll", zynq_pll_clk_setup);
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", &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;
}
CLK_OF_DECLARE(zynq_periph, "xlnx,zynq-periph-clock", zynq_periph_clk_setup);
/* 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;
}
CLK_OF_DECLARE(zynq_cpu, "xlnx,zynq-cpu-clock", zynq_cpu_clk_setup);
void __init xilinx_zynq_clocks_init(void __iomem *slcr)
{
slcr_base = slcr;
of_clk_init(NULL);
}

View file

@ -0,0 +1,3 @@
# Zynq clock specific Makefile
obj-$(CONFIG_ARCH_ZYNQ) += clkc.o pll.o

533
drivers/clk/zynq/clkc.c Normal file
View file

@ -0,0 +1,533 @@
/*
* Zynq clock controller
*
* Copyright (C) 2012 - 2013 Xilinx
*
* Sören Brinkmann <soren.brinkmann@xilinx.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2 as published by
* the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk/zynq.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/io.h>
static void __iomem *zynq_slcr_base_priv;
#define SLCR_ARMPLL_CTRL (zynq_slcr_base_priv + 0x100)
#define SLCR_DDRPLL_CTRL (zynq_slcr_base_priv + 0x104)
#define SLCR_IOPLL_CTRL (zynq_slcr_base_priv + 0x108)
#define SLCR_PLL_STATUS (zynq_slcr_base_priv + 0x10c)
#define SLCR_ARM_CLK_CTRL (zynq_slcr_base_priv + 0x120)
#define SLCR_DDR_CLK_CTRL (zynq_slcr_base_priv + 0x124)
#define SLCR_DCI_CLK_CTRL (zynq_slcr_base_priv + 0x128)
#define SLCR_APER_CLK_CTRL (zynq_slcr_base_priv + 0x12c)
#define SLCR_GEM0_CLK_CTRL (zynq_slcr_base_priv + 0x140)
#define SLCR_GEM1_CLK_CTRL (zynq_slcr_base_priv + 0x144)
#define SLCR_SMC_CLK_CTRL (zynq_slcr_base_priv + 0x148)
#define SLCR_LQSPI_CLK_CTRL (zynq_slcr_base_priv + 0x14c)
#define SLCR_SDIO_CLK_CTRL (zynq_slcr_base_priv + 0x150)
#define SLCR_UART_CLK_CTRL (zynq_slcr_base_priv + 0x154)
#define SLCR_SPI_CLK_CTRL (zynq_slcr_base_priv + 0x158)
#define SLCR_CAN_CLK_CTRL (zynq_slcr_base_priv + 0x15c)
#define SLCR_CAN_MIOCLK_CTRL (zynq_slcr_base_priv + 0x160)
#define SLCR_DBG_CLK_CTRL (zynq_slcr_base_priv + 0x164)
#define SLCR_PCAP_CLK_CTRL (zynq_slcr_base_priv + 0x168)
#define SLCR_FPGA0_CLK_CTRL (zynq_slcr_base_priv + 0x170)
#define SLCR_621_TRUE (zynq_slcr_base_priv + 0x1c4)
#define SLCR_SWDT_CLK_SEL (zynq_slcr_base_priv + 0x304)
#define NUM_MIO_PINS 54
enum zynq_clk {
armpll, ddrpll, iopll,
cpu_6or4x, cpu_3or2x, cpu_2x, cpu_1x,
ddr2x, ddr3x, dci,
lqspi, smc, pcap, gem0, gem1, fclk0, fclk1, fclk2, fclk3, can0, can1,
sdio0, sdio1, uart0, uart1, spi0, spi1, dma,
usb0_aper, usb1_aper, gem0_aper, gem1_aper,
sdio0_aper, sdio1_aper, spi0_aper, spi1_aper, can0_aper, can1_aper,
i2c0_aper, i2c1_aper, uart0_aper, uart1_aper, gpio_aper, lqspi_aper,
smc_aper, swdt, dbg_trc, dbg_apb, clk_max};
static struct clk *ps_clk;
static struct clk *clks[clk_max];
static struct clk_onecell_data clk_data;
static DEFINE_SPINLOCK(armpll_lock);
static DEFINE_SPINLOCK(ddrpll_lock);
static DEFINE_SPINLOCK(iopll_lock);
static DEFINE_SPINLOCK(armclk_lock);
static DEFINE_SPINLOCK(ddrclk_lock);
static DEFINE_SPINLOCK(dciclk_lock);
static DEFINE_SPINLOCK(gem0clk_lock);
static DEFINE_SPINLOCK(gem1clk_lock);
static DEFINE_SPINLOCK(canclk_lock);
static DEFINE_SPINLOCK(canmioclk_lock);
static DEFINE_SPINLOCK(dbgclk_lock);
static DEFINE_SPINLOCK(aperclk_lock);
static const char dummy_nm[] __initconst = "dummy_name";
static const char *armpll_parents[] __initdata = {"armpll_int", "ps_clk"};
static const char *ddrpll_parents[] __initdata = {"ddrpll_int", "ps_clk"};
static const char *iopll_parents[] __initdata = {"iopll_int", "ps_clk"};
static const char *gem0_mux_parents[] __initdata = {"gem0_div1", dummy_nm};
static const char *gem1_mux_parents[] __initdata = {"gem1_div1", dummy_nm};
static const char *can0_mio_mux2_parents[] __initdata = {"can0_gate",
"can0_mio_mux"};
static const char *can1_mio_mux2_parents[] __initdata = {"can1_gate",
"can1_mio_mux"};
static const char *dbg_emio_mux_parents[] __initdata = {"dbg_div",
dummy_nm};
static const char *dbgtrc_emio_input_names[] __initdata = {"trace_emio_clk"};
static const char *gem0_emio_input_names[] __initdata = {"gem0_emio_clk"};
static const char *gem1_emio_input_names[] __initdata = {"gem1_emio_clk"};
static const char *swdt_ext_clk_input_names[] __initdata = {"swdt_ext_clk"};
static void __init zynq_clk_register_fclk(enum zynq_clk fclk,
const char *clk_name, void __iomem *fclk_ctrl_reg,
const char **parents)
{
struct clk *clk;
char *mux_name;
char *div0_name;
char *div1_name;
spinlock_t *fclk_lock;
spinlock_t *fclk_gate_lock;
void __iomem *fclk_gate_reg = fclk_ctrl_reg + 8;
fclk_lock = kmalloc(sizeof(*fclk_lock), GFP_KERNEL);
if (!fclk_lock)
goto err;
fclk_gate_lock = kmalloc(sizeof(*fclk_gate_lock), GFP_KERNEL);
if (!fclk_gate_lock)
goto err;
spin_lock_init(fclk_lock);
spin_lock_init(fclk_gate_lock);
mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name);
div0_name = kasprintf(GFP_KERNEL, "%s_div0", clk_name);
div1_name = kasprintf(GFP_KERNEL, "%s_div1", clk_name);
clk = clk_register_mux(NULL, mux_name, parents, 4, 0,
fclk_ctrl_reg, 4, 2, 0, fclk_lock);
clk = clk_register_divider(NULL, div0_name, mux_name,
0, fclk_ctrl_reg, 8, 6, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, fclk_lock);
clk = clk_register_divider(NULL, div1_name, div0_name,
CLK_SET_RATE_PARENT, fclk_ctrl_reg, 20, 6,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
fclk_lock);
clks[fclk] = clk_register_gate(NULL, clk_name,
div1_name, CLK_SET_RATE_PARENT, fclk_gate_reg,
0, CLK_GATE_SET_TO_DISABLE, fclk_gate_lock);
kfree(mux_name);
kfree(div0_name);
kfree(div1_name);
return;
err:
clks[fclk] = ERR_PTR(-ENOMEM);
}
static void __init zynq_clk_register_periph_clk(enum zynq_clk clk0,
enum zynq_clk clk1, const char *clk_name0,
const char *clk_name1, void __iomem *clk_ctrl,
const char **parents, unsigned int two_gates)
{
struct clk *clk;
char *mux_name;
char *div_name;
spinlock_t *lock;
lock = kmalloc(sizeof(*lock), GFP_KERNEL);
if (!lock)
goto err;
spin_lock_init(lock);
mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name0);
div_name = kasprintf(GFP_KERNEL, "%s_div", clk_name0);
clk = clk_register_mux(NULL, mux_name, parents, 4, 0,
clk_ctrl, 4, 2, 0, lock);
clk = clk_register_divider(NULL, div_name, mux_name, 0, clk_ctrl, 8, 6,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, lock);
clks[clk0] = clk_register_gate(NULL, clk_name0, div_name,
CLK_SET_RATE_PARENT, clk_ctrl, 0, 0, lock);
if (two_gates)
clks[clk1] = clk_register_gate(NULL, clk_name1, div_name,
CLK_SET_RATE_PARENT, clk_ctrl, 1, 0, lock);
kfree(mux_name);
kfree(div_name);
return;
err:
clks[clk0] = ERR_PTR(-ENOMEM);
if (two_gates)
clks[clk1] = ERR_PTR(-ENOMEM);
}
static void __init zynq_clk_setup(struct device_node *np)
{
int i;
u32 tmp;
int ret;
struct clk *clk;
char *clk_name;
const char *clk_output_name[clk_max];
const char *cpu_parents[4];
const char *periph_parents[4];
const char *swdt_ext_clk_mux_parents[2];
const char *can_mio_mux_parents[NUM_MIO_PINS];
pr_info("Zynq clock init\n");
/* get clock output names from DT */
for (i = 0; i < clk_max; i++) {
if (of_property_read_string_index(np, "clock-output-names",
i, &clk_output_name[i])) {
pr_err("%s: clock output name not in DT\n", __func__);
BUG();
}
}
cpu_parents[0] = clk_output_name[armpll];
cpu_parents[1] = clk_output_name[armpll];
cpu_parents[2] = clk_output_name[ddrpll];
cpu_parents[3] = clk_output_name[iopll];
periph_parents[0] = clk_output_name[iopll];
periph_parents[1] = clk_output_name[iopll];
periph_parents[2] = clk_output_name[armpll];
periph_parents[3] = clk_output_name[ddrpll];
/* ps_clk */
ret = of_property_read_u32(np, "ps-clk-frequency", &tmp);
if (ret) {
pr_warn("ps_clk frequency not specified, using 33 MHz.\n");
tmp = 33333333;
}
ps_clk = clk_register_fixed_rate(NULL, "ps_clk", NULL, CLK_IS_ROOT,
tmp);
/* PLLs */
clk = clk_register_zynq_pll("armpll_int", "ps_clk", SLCR_ARMPLL_CTRL,
SLCR_PLL_STATUS, 0, &armpll_lock);
clks[armpll] = clk_register_mux(NULL, clk_output_name[armpll],
armpll_parents, 2, 0, SLCR_ARMPLL_CTRL, 4, 1, 0,
&armpll_lock);
clk = clk_register_zynq_pll("ddrpll_int", "ps_clk", SLCR_DDRPLL_CTRL,
SLCR_PLL_STATUS, 1, &ddrpll_lock);
clks[ddrpll] = clk_register_mux(NULL, clk_output_name[ddrpll],
ddrpll_parents, 2, 0, SLCR_DDRPLL_CTRL, 4, 1, 0,
&ddrpll_lock);
clk = clk_register_zynq_pll("iopll_int", "ps_clk", SLCR_IOPLL_CTRL,
SLCR_PLL_STATUS, 2, &iopll_lock);
clks[iopll] = clk_register_mux(NULL, clk_output_name[iopll],
iopll_parents, 2, 0, SLCR_IOPLL_CTRL, 4, 1, 0,
&iopll_lock);
/* CPU clocks */
tmp = readl(SLCR_621_TRUE) & 1;
clk = clk_register_mux(NULL, "cpu_mux", cpu_parents, 4, 0,
SLCR_ARM_CLK_CTRL, 4, 2, 0, &armclk_lock);
clk = clk_register_divider(NULL, "cpu_div", "cpu_mux", 0,
SLCR_ARM_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, &armclk_lock);
clks[cpu_6or4x] = clk_register_gate(NULL, clk_output_name[cpu_6or4x],
"cpu_div", CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
SLCR_ARM_CLK_CTRL, 24, 0, &armclk_lock);
clk = clk_register_fixed_factor(NULL, "cpu_3or2x_div", "cpu_div", 0,
1, 2);
clks[cpu_3or2x] = clk_register_gate(NULL, clk_output_name[cpu_3or2x],
"cpu_3or2x_div", CLK_IGNORE_UNUSED,
SLCR_ARM_CLK_CTRL, 25, 0, &armclk_lock);
clk = clk_register_fixed_factor(NULL, "cpu_2x_div", "cpu_div", 0, 1,
2 + tmp);
clks[cpu_2x] = clk_register_gate(NULL, clk_output_name[cpu_2x],
"cpu_2x_div", CLK_IGNORE_UNUSED, SLCR_ARM_CLK_CTRL,
26, 0, &armclk_lock);
clk = clk_register_fixed_factor(NULL, "cpu_1x_div", "cpu_div", 0, 1,
4 + 2 * tmp);
clks[cpu_1x] = clk_register_gate(NULL, clk_output_name[cpu_1x],
"cpu_1x_div", CLK_IGNORE_UNUSED, SLCR_ARM_CLK_CTRL, 27,
0, &armclk_lock);
/* Timers */
swdt_ext_clk_mux_parents[0] = clk_output_name[cpu_1x];
for (i = 0; i < ARRAY_SIZE(swdt_ext_clk_input_names); i++) {
int idx = of_property_match_string(np, "clock-names",
swdt_ext_clk_input_names[i]);
if (idx >= 0)
swdt_ext_clk_mux_parents[i + 1] =
of_clk_get_parent_name(np, idx);
else
swdt_ext_clk_mux_parents[i + 1] = dummy_nm;
}
clks[swdt] = clk_register_mux(NULL, clk_output_name[swdt],
swdt_ext_clk_mux_parents, 2, CLK_SET_RATE_PARENT,
SLCR_SWDT_CLK_SEL, 0, 1, 0, &gem0clk_lock);
/* DDR clocks */
clk = clk_register_divider(NULL, "ddr2x_div", "ddrpll", 0,
SLCR_DDR_CLK_CTRL, 26, 6, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, &ddrclk_lock);
clks[ddr2x] = clk_register_gate(NULL, clk_output_name[ddr2x],
"ddr2x_div", 0, SLCR_DDR_CLK_CTRL, 1, 0, &ddrclk_lock);
clk_prepare_enable(clks[ddr2x]);
clk = clk_register_divider(NULL, "ddr3x_div", "ddrpll", 0,
SLCR_DDR_CLK_CTRL, 20, 6, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, &ddrclk_lock);
clks[ddr3x] = clk_register_gate(NULL, clk_output_name[ddr3x],
"ddr3x_div", 0, SLCR_DDR_CLK_CTRL, 0, 0, &ddrclk_lock);
clk_prepare_enable(clks[ddr3x]);
clk = clk_register_divider(NULL, "dci_div0", "ddrpll", 0,
SLCR_DCI_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, &dciclk_lock);
clk = clk_register_divider(NULL, "dci_div1", "dci_div0",
CLK_SET_RATE_PARENT, SLCR_DCI_CLK_CTRL, 20, 6,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
&dciclk_lock);
clks[dci] = clk_register_gate(NULL, clk_output_name[dci], "dci_div1",
CLK_SET_RATE_PARENT, SLCR_DCI_CLK_CTRL, 0, 0,
&dciclk_lock);
clk_prepare_enable(clks[dci]);
/* Peripheral clocks */
for (i = fclk0; i <= fclk3; i++)
zynq_clk_register_fclk(i, clk_output_name[i],
SLCR_FPGA0_CLK_CTRL + 0x10 * (i - fclk0),
periph_parents);
zynq_clk_register_periph_clk(lqspi, 0, clk_output_name[lqspi], NULL,
SLCR_LQSPI_CLK_CTRL, periph_parents, 0);
zynq_clk_register_periph_clk(smc, 0, clk_output_name[smc], NULL,
SLCR_SMC_CLK_CTRL, periph_parents, 0);
zynq_clk_register_periph_clk(pcap, 0, clk_output_name[pcap], NULL,
SLCR_PCAP_CLK_CTRL, periph_parents, 0);
zynq_clk_register_periph_clk(sdio0, sdio1, clk_output_name[sdio0],
clk_output_name[sdio1], SLCR_SDIO_CLK_CTRL,
periph_parents, 1);
zynq_clk_register_periph_clk(uart0, uart1, clk_output_name[uart0],
clk_output_name[uart1], SLCR_UART_CLK_CTRL,
periph_parents, 1);
zynq_clk_register_periph_clk(spi0, spi1, clk_output_name[spi0],
clk_output_name[spi1], SLCR_SPI_CLK_CTRL,
periph_parents, 1);
for (i = 0; i < ARRAY_SIZE(gem0_emio_input_names); i++) {
int idx = of_property_match_string(np, "clock-names",
gem0_emio_input_names[i]);
if (idx >= 0)
gem0_mux_parents[i + 1] = of_clk_get_parent_name(np,
idx);
}
clk = clk_register_mux(NULL, "gem0_mux", periph_parents, 4, 0,
SLCR_GEM0_CLK_CTRL, 4, 2, 0, &gem0clk_lock);
clk = clk_register_divider(NULL, "gem0_div0", "gem0_mux", 0,
SLCR_GEM0_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, &gem0clk_lock);
clk = clk_register_divider(NULL, "gem0_div1", "gem0_div0",
CLK_SET_RATE_PARENT, SLCR_GEM0_CLK_CTRL, 20, 6,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
&gem0clk_lock);
clk = clk_register_mux(NULL, "gem0_emio_mux", gem0_mux_parents, 2, 0,
SLCR_GEM0_CLK_CTRL, 6, 1, 0, &gem0clk_lock);
clks[gem0] = clk_register_gate(NULL, clk_output_name[gem0],
"gem0_emio_mux", CLK_SET_RATE_PARENT,
SLCR_GEM0_CLK_CTRL, 0, 0, &gem0clk_lock);
for (i = 0; i < ARRAY_SIZE(gem1_emio_input_names); i++) {
int idx = of_property_match_string(np, "clock-names",
gem1_emio_input_names[i]);
if (idx >= 0)
gem1_mux_parents[i + 1] = of_clk_get_parent_name(np,
idx);
}
clk = clk_register_mux(NULL, "gem1_mux", periph_parents, 4, 0,
SLCR_GEM1_CLK_CTRL, 4, 2, 0, &gem1clk_lock);
clk = clk_register_divider(NULL, "gem1_div0", "gem1_mux", 0,
SLCR_GEM1_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, &gem1clk_lock);
clk = clk_register_divider(NULL, "gem1_div1", "gem1_div0",
CLK_SET_RATE_PARENT, SLCR_GEM1_CLK_CTRL, 20, 6,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
&gem1clk_lock);
clk = clk_register_mux(NULL, "gem1_emio_mux", gem1_mux_parents, 2, 0,
SLCR_GEM1_CLK_CTRL, 6, 1, 0, &gem1clk_lock);
clks[gem1] = clk_register_gate(NULL, clk_output_name[gem1],
"gem1_emio_mux", CLK_SET_RATE_PARENT,
SLCR_GEM1_CLK_CTRL, 0, 0, &gem1clk_lock);
tmp = strlen("mio_clk_00x");
clk_name = kmalloc(tmp, GFP_KERNEL);
for (i = 0; i < NUM_MIO_PINS; i++) {
int idx;
snprintf(clk_name, tmp, "mio_clk_%2.2d", i);
idx = of_property_match_string(np, "clock-names", clk_name);
if (idx >= 0)
can_mio_mux_parents[i] = of_clk_get_parent_name(np,
idx);
else
can_mio_mux_parents[i] = dummy_nm;
}
kfree(clk_name);
clk = clk_register_mux(NULL, "can_mux", periph_parents, 4, 0,
SLCR_CAN_CLK_CTRL, 4, 2, 0, &canclk_lock);
clk = clk_register_divider(NULL, "can_div0", "can_mux", 0,
SLCR_CAN_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, &canclk_lock);
clk = clk_register_divider(NULL, "can_div1", "can_div0",
CLK_SET_RATE_PARENT, SLCR_CAN_CLK_CTRL, 20, 6,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
&canclk_lock);
clk = clk_register_gate(NULL, "can0_gate", "can_div1",
CLK_SET_RATE_PARENT, SLCR_CAN_CLK_CTRL, 0, 0,
&canclk_lock);
clk = clk_register_gate(NULL, "can1_gate", "can_div1",
CLK_SET_RATE_PARENT, SLCR_CAN_CLK_CTRL, 1, 0,
&canclk_lock);
clk = clk_register_mux(NULL, "can0_mio_mux",
can_mio_mux_parents, 54, CLK_SET_RATE_PARENT,
SLCR_CAN_MIOCLK_CTRL, 0, 6, 0, &canmioclk_lock);
clk = clk_register_mux(NULL, "can1_mio_mux",
can_mio_mux_parents, 54, CLK_SET_RATE_PARENT,
SLCR_CAN_MIOCLK_CTRL, 16, 6, 0, &canmioclk_lock);
clks[can0] = clk_register_mux(NULL, clk_output_name[can0],
can0_mio_mux2_parents, 2, CLK_SET_RATE_PARENT,
SLCR_CAN_MIOCLK_CTRL, 6, 1, 0, &canmioclk_lock);
clks[can1] = clk_register_mux(NULL, clk_output_name[can1],
can1_mio_mux2_parents, 2, CLK_SET_RATE_PARENT,
SLCR_CAN_MIOCLK_CTRL, 22, 1, 0, &canmioclk_lock);
for (i = 0; i < ARRAY_SIZE(dbgtrc_emio_input_names); i++) {
int idx = of_property_match_string(np, "clock-names",
dbgtrc_emio_input_names[i]);
if (idx >= 0)
dbg_emio_mux_parents[i + 1] = of_clk_get_parent_name(np,
idx);
}
clk = clk_register_mux(NULL, "dbg_mux", periph_parents, 4, 0,
SLCR_DBG_CLK_CTRL, 4, 2, 0, &dbgclk_lock);
clk = clk_register_divider(NULL, "dbg_div", "dbg_mux", 0,
SLCR_DBG_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO, &dbgclk_lock);
clk = clk_register_mux(NULL, "dbg_emio_mux", dbg_emio_mux_parents, 2, 0,
SLCR_DBG_CLK_CTRL, 6, 1, 0, &dbgclk_lock);
clks[dbg_trc] = clk_register_gate(NULL, clk_output_name[dbg_trc],
"dbg_emio_mux", CLK_SET_RATE_PARENT, SLCR_DBG_CLK_CTRL,
0, 0, &dbgclk_lock);
clks[dbg_apb] = clk_register_gate(NULL, clk_output_name[dbg_apb],
clk_output_name[cpu_1x], 0, SLCR_DBG_CLK_CTRL, 1, 0,
&dbgclk_lock);
/* One gated clock for all APER clocks. */
clks[dma] = clk_register_gate(NULL, clk_output_name[dma],
clk_output_name[cpu_2x], 0, SLCR_APER_CLK_CTRL, 0, 0,
&aperclk_lock);
clks[usb0_aper] = clk_register_gate(NULL, clk_output_name[usb0_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 2, 0,
&aperclk_lock);
clks[usb1_aper] = clk_register_gate(NULL, clk_output_name[usb1_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 3, 0,
&aperclk_lock);
clks[gem0_aper] = clk_register_gate(NULL, clk_output_name[gem0_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 6, 0,
&aperclk_lock);
clks[gem1_aper] = clk_register_gate(NULL, clk_output_name[gem1_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 7, 0,
&aperclk_lock);
clks[sdio0_aper] = clk_register_gate(NULL, clk_output_name[sdio0_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 10, 0,
&aperclk_lock);
clks[sdio1_aper] = clk_register_gate(NULL, clk_output_name[sdio1_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 11, 0,
&aperclk_lock);
clks[spi0_aper] = clk_register_gate(NULL, clk_output_name[spi0_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 14, 0,
&aperclk_lock);
clks[spi1_aper] = clk_register_gate(NULL, clk_output_name[spi1_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 15, 0,
&aperclk_lock);
clks[can0_aper] = clk_register_gate(NULL, clk_output_name[can0_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 16, 0,
&aperclk_lock);
clks[can1_aper] = clk_register_gate(NULL, clk_output_name[can1_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 17, 0,
&aperclk_lock);
clks[i2c0_aper] = clk_register_gate(NULL, clk_output_name[i2c0_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 18, 0,
&aperclk_lock);
clks[i2c1_aper] = clk_register_gate(NULL, clk_output_name[i2c1_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 19, 0,
&aperclk_lock);
clks[uart0_aper] = clk_register_gate(NULL, clk_output_name[uart0_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 20, 0,
&aperclk_lock);
clks[uart1_aper] = clk_register_gate(NULL, clk_output_name[uart1_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 21, 0,
&aperclk_lock);
clks[gpio_aper] = clk_register_gate(NULL, clk_output_name[gpio_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 22, 0,
&aperclk_lock);
clks[lqspi_aper] = clk_register_gate(NULL, clk_output_name[lqspi_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 23, 0,
&aperclk_lock);
clks[smc_aper] = clk_register_gate(NULL, clk_output_name[smc_aper],
clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 24, 0,
&aperclk_lock);
for (i = 0; i < ARRAY_SIZE(clks); i++) {
if (IS_ERR(clks[i])) {
pr_err("Zynq clk %d: register failed with %ld\n",
i, PTR_ERR(clks[i]));
BUG();
}
}
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
}
CLK_OF_DECLARE(zynq_clkc, "xlnx,ps7-clkc", zynq_clk_setup);
void __init zynq_clock_init(void __iomem *slcr_base)
{
zynq_slcr_base_priv = slcr_base;
of_clk_init(NULL);
}

235
drivers/clk/zynq/pll.c Normal file
View file

@ -0,0 +1,235 @@
/*
* Zynq PLL driver
*
* Copyright (C) 2013 Xilinx
*
* Sören Brinkmann <soren.brinkmann@xilinx.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2 as published by
* the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/clk/zynq.h>
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include <linux/io.h>
/**
* struct zynq_pll
* @hw: Handle between common and hardware-specific interfaces
* @pll_ctrl: PLL control register
* @pll_status: PLL status register
* @lock: Register lock
* @lockbit: Indicates the associated PLL_LOCKED bit in the PLL status
* register.
*/
struct zynq_pll {
struct clk_hw hw;
void __iomem *pll_ctrl;
void __iomem *pll_status;
spinlock_t *lock;
u8 lockbit;
};
#define to_zynq_pll(_hw) container_of(_hw, struct zynq_pll, hw)
/* Register bitfield defines */
#define PLLCTRL_FBDIV_MASK 0x7f000
#define PLLCTRL_FBDIV_SHIFT 12
#define PLLCTRL_BPQUAL_MASK (1 << 3)
#define PLLCTRL_PWRDWN_MASK 2
#define PLLCTRL_PWRDWN_SHIFT 1
#define PLLCTRL_RESET_MASK 1
#define PLLCTRL_RESET_SHIFT 0
/**
* zynq_pll_round_rate() - Round a clock frequency
* @hw: Handle between common and hardware-specific interfaces
* @rate: Desired clock frequency
* @prate: Clock frequency of parent clock
* Returns frequency closest to @rate the hardware can generate.
*/
static long zynq_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
u32 fbdiv;
fbdiv = DIV_ROUND_CLOSEST(rate, *prate);
if (fbdiv < 13)
fbdiv = 13;
else if (fbdiv > 66)
fbdiv = 66;
return *prate * fbdiv;
}
/**
* zynq_pll_recalc_rate() - Recalculate clock frequency
* @hw: Handle between common and hardware-specific interfaces
* @parent_rate: Clock frequency of parent clock
* Returns current clock frequency.
*/
static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct zynq_pll *clk = to_zynq_pll(hw);
u32 fbdiv;
/*
* makes probably sense to redundantly save fbdiv in the struct
* zynq_pll to save the IO access.
*/
fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>
PLLCTRL_FBDIV_SHIFT;
return parent_rate * fbdiv;
}
/**
* zynq_pll_is_enabled - Check if a clock is enabled
* @hw: Handle between common and hardware-specific interfaces
* Returns 1 if the clock is enabled, 0 otherwise.
*
* Not sure this is a good idea, but since disabled means bypassed for
* this clock implementation we say we are always enabled.
*/
static int zynq_pll_is_enabled(struct clk_hw *hw)
{
unsigned long flags = 0;
u32 reg;
struct zynq_pll *clk = to_zynq_pll(hw);
spin_lock_irqsave(clk->lock, flags);
reg = readl(clk->pll_ctrl);
spin_unlock_irqrestore(clk->lock, flags);
return !(reg & (PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK));
}
/**
* zynq_pll_enable - Enable clock
* @hw: Handle between common and hardware-specific interfaces
* Returns 0 on success
*/
static int zynq_pll_enable(struct clk_hw *hw)
{
unsigned long flags = 0;
u32 reg;
struct zynq_pll *clk = to_zynq_pll(hw);
if (zynq_pll_is_enabled(hw))
return 0;
pr_info("PLL: enable\n");
/* Power up PLL and wait for lock */
spin_lock_irqsave(clk->lock, flags);
reg = readl(clk->pll_ctrl);
reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK);
writel(reg, clk->pll_ctrl);
while (!(readl(clk->pll_status) & (1 << clk->lockbit)))
;
spin_unlock_irqrestore(clk->lock, flags);
return 0;
}
/**
* zynq_pll_disable - Disable clock
* @hw: Handle between common and hardware-specific interfaces
* Returns 0 on success
*/
static void zynq_pll_disable(struct clk_hw *hw)
{
unsigned long flags = 0;
u32 reg;
struct zynq_pll *clk = to_zynq_pll(hw);
if (!zynq_pll_is_enabled(hw))
return;
pr_info("PLL: shutdown\n");
/* shut down PLL */
spin_lock_irqsave(clk->lock, flags);
reg = readl(clk->pll_ctrl);
reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK;
writel(reg, clk->pll_ctrl);
spin_unlock_irqrestore(clk->lock, flags);
}
static const struct clk_ops zynq_pll_ops = {
.enable = zynq_pll_enable,
.disable = zynq_pll_disable,
.is_enabled = zynq_pll_is_enabled,
.round_rate = zynq_pll_round_rate,
.recalc_rate = zynq_pll_recalc_rate
};
/**
* clk_register_zynq_pll() - Register PLL with the clock framework
* @np Pointer to the DT device node
*/
struct clk *clk_register_zynq_pll(const char *name, const char *parent,
void __iomem *pll_ctrl, void __iomem *pll_status, u8 lock_index,
spinlock_t *lock)
{
struct zynq_pll *pll;
struct clk *clk;
u32 reg;
const char *parent_arr[1] = {parent};
unsigned long flags = 0;
struct clk_init_data initd = {
.name = name,
.parent_names = parent_arr,
.ops = &zynq_pll_ops,
.num_parents = 1,
.flags = 0
};
pll = kmalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: Could not allocate Zynq PLL clk.\n", __func__);
return ERR_PTR(-ENOMEM);
}
/* Populate the struct */
pll->hw.init = &initd;
pll->pll_ctrl = pll_ctrl;
pll->pll_status = pll_status;
pll->lockbit = lock_index;
pll->lock = lock;
spin_lock_irqsave(pll->lock, flags);
reg = readl(pll->pll_ctrl);
reg &= ~PLLCTRL_BPQUAL_MASK;
writel(reg, pll->pll_ctrl);
spin_unlock_irqrestore(pll->lock, flags);
clk = clk_register(NULL, &pll->hw);
if (WARN_ON(IS_ERR(clk)))
goto free_pll;
return clk;
free_pll:
kfree(pll);
return clk;
}

View file

@ -51,6 +51,8 @@
#define TTC_CNT_CNTRL_DISABLE_MASK 0x1 #define TTC_CNT_CNTRL_DISABLE_MASK 0x1
#define TTC_CLK_CNTRL_CSRC_MASK (1 << 5) /* clock source */
/* /*
* Setup the timers to use pre-scaling, using a fixed value for now that will * Setup the timers to use pre-scaling, using a fixed value for now that will
* work across most input frequency, but it may need to be more dynamic * work across most input frequency, but it may need to be more dynamic
@ -396,8 +398,9 @@ static void __init ttc_timer_init(struct device_node *timer)
{ {
unsigned int irq; unsigned int irq;
void __iomem *timer_baseaddr; void __iomem *timer_baseaddr;
struct clk *clk; struct clk *clk_cs, *clk_ce;
static int initialized; static int initialized;
int clksel;
if (initialized) if (initialized)
return; return;
@ -421,14 +424,24 @@ static void __init ttc_timer_init(struct device_node *timer)
BUG(); BUG();
} }
clk = of_clk_get_by_name(timer, "cpu_1x"); clksel = __raw_readl(timer_baseaddr + TTC_CLK_CNTRL_OFFSET);
if (IS_ERR(clk)) { clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
clk_cs = of_clk_get(timer, clksel);
if (IS_ERR(clk_cs)) {
pr_err("ERROR: timer input clock not found\n"); pr_err("ERROR: timer input clock not found\n");
BUG(); BUG();
} }
ttc_setup_clocksource(clk, timer_baseaddr); clksel = __raw_readl(timer_baseaddr + 4 + TTC_CLK_CNTRL_OFFSET);
ttc_setup_clockevent(clk, timer_baseaddr + 4, irq); clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
clk_ce = of_clk_get(timer, clksel);
if (IS_ERR(clk_ce)) {
pr_err("ERROR: timer input clock not found\n");
BUG();
}
ttc_setup_clocksource(clk_cs, timer_baseaddr);
ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq);
pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq); pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
} }

View file

@ -14,6 +14,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serial_core.h> #include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/tty_flip.h> #include <linux/tty_flip.h>
#include <linux/console.h> #include <linux/console.h>
@ -138,6 +139,16 @@
#define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */ #define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */
#define XUARTPS_SR_RXTRIG 0x00000001 /* Rx Trigger */ #define XUARTPS_SR_RXTRIG 0x00000001 /* Rx Trigger */
/**
* struct xuartps - device data
* @refclk Reference clock
* @aperclk APB clock
*/
struct xuartps {
struct clk *refclk;
struct clk *aperclk;
};
/** /**
* xuartps_isr - Interrupt handler * xuartps_isr - Interrupt handler
* @irq: Irq number * @irq: Irq number
@ -936,34 +947,55 @@ static int xuartps_probe(struct platform_device *pdev)
int rc; int rc;
struct uart_port *port; struct uart_port *port;
struct resource *res, *res2; struct resource *res, *res2;
struct clk *clk; struct xuartps *xuartps_data;
clk = of_clk_get(pdev->dev.of_node, 0); xuartps_data = kzalloc(sizeof(*xuartps_data), GFP_KERNEL);
if (IS_ERR(clk)) { if (!xuartps_data)
dev_err(&pdev->dev, "no clock specified\n"); return -ENOMEM;
return PTR_ERR(clk);
xuartps_data->aperclk = clk_get(&pdev->dev, "aper_clk");
if (IS_ERR(xuartps_data->aperclk)) {
dev_err(&pdev->dev, "aper_clk clock not found.\n");
rc = PTR_ERR(xuartps_data->aperclk);
goto err_out_free;
}
xuartps_data->refclk = clk_get(&pdev->dev, "ref_clk");
if (IS_ERR(xuartps_data->refclk)) {
dev_err(&pdev->dev, "ref_clk clock not found.\n");
rc = PTR_ERR(xuartps_data->refclk);
goto err_out_clk_put_aper;
} }
rc = clk_prepare_enable(clk); rc = clk_prepare_enable(xuartps_data->aperclk);
if (rc) { if (rc) {
dev_err(&pdev->dev, "could not enable clock\n"); dev_err(&pdev->dev, "Unable to enable APER clock.\n");
return -EBUSY; goto err_out_clk_put;
}
rc = clk_prepare_enable(xuartps_data->refclk);
if (rc) {
dev_err(&pdev->dev, "Unable to enable device clock.\n");
goto err_out_clk_dis_aper;
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) if (!res) {
return -ENODEV; rc = -ENODEV;
goto err_out_clk_disable;
}
res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res2) if (!res2) {
return -ENODEV; rc = -ENODEV;
goto err_out_clk_disable;
}
/* Initialize the port structure */ /* Initialize the port structure */
port = xuartps_get_port(); port = xuartps_get_port();
if (!port) { if (!port) {
dev_err(&pdev->dev, "Cannot get uart_port structure\n"); dev_err(&pdev->dev, "Cannot get uart_port structure\n");
return -ENODEV; rc = -ENODEV;
goto err_out_clk_disable;
} else { } else {
/* Register the port. /* Register the port.
* This function also registers this device with the tty layer * This function also registers this device with the tty layer
@ -972,18 +1004,31 @@ static int xuartps_probe(struct platform_device *pdev)
port->mapbase = res->start; port->mapbase = res->start;
port->irq = res2->start; port->irq = res2->start;
port->dev = &pdev->dev; port->dev = &pdev->dev;
port->uartclk = clk_get_rate(clk); port->uartclk = clk_get_rate(xuartps_data->refclk);
port->private_data = clk; port->private_data = xuartps_data;
dev_set_drvdata(&pdev->dev, port); dev_set_drvdata(&pdev->dev, port);
rc = uart_add_one_port(&xuartps_uart_driver, port); rc = uart_add_one_port(&xuartps_uart_driver, port);
if (rc) { if (rc) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"uart_add_one_port() failed; err=%i\n", rc); "uart_add_one_port() failed; err=%i\n", rc);
dev_set_drvdata(&pdev->dev, NULL); dev_set_drvdata(&pdev->dev, NULL);
return rc; goto err_out_clk_disable;
} }
return 0; return 0;
} }
err_out_clk_disable:
clk_disable_unprepare(xuartps_data->refclk);
err_out_clk_dis_aper:
clk_disable_unprepare(xuartps_data->aperclk);
err_out_clk_put:
clk_put(xuartps_data->refclk);
err_out_clk_put_aper:
clk_put(xuartps_data->aperclk);
err_out_free:
kfree(xuartps_data);
return rc;
} }
/** /**
@ -995,14 +1040,18 @@ static int xuartps_probe(struct platform_device *pdev)
static int xuartps_remove(struct platform_device *pdev) static int xuartps_remove(struct platform_device *pdev)
{ {
struct uart_port *port = dev_get_drvdata(&pdev->dev); struct uart_port *port = dev_get_drvdata(&pdev->dev);
struct clk *clk = port->private_data; struct xuartps *xuartps_data = port->private_data;
int rc; int rc;
/* Remove the xuartps port from the serial core */ /* Remove the xuartps port from the serial core */
rc = uart_remove_one_port(&xuartps_uart_driver, port); rc = uart_remove_one_port(&xuartps_uart_driver, port);
dev_set_drvdata(&pdev->dev, NULL); dev_set_drvdata(&pdev->dev, NULL);
port->mapbase = 0; port->mapbase = 0;
clk_disable_unprepare(clk); clk_disable_unprepare(xuartps_data->refclk);
clk_disable_unprepare(xuartps_data->aperclk);
clk_put(xuartps_data->refclk);
clk_put(xuartps_data->aperclk);
kfree(xuartps_data);
return rc; return rc;
} }

View file

@ -1,4 +1,5 @@
/* /*
* Copyright (C) 2013 Xilinx Inc.
* Copyright (C) 2012 National Instruments * Copyright (C) 2012 National Instruments
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -19,6 +20,11 @@
#ifndef __LINUX_CLK_ZYNQ_H_ #ifndef __LINUX_CLK_ZYNQ_H_
#define __LINUX_CLK_ZYNQ_H_ #define __LINUX_CLK_ZYNQ_H_
void __init xilinx_zynq_clocks_init(void __iomem *slcr); #include <linux/spinlock.h>
void zynq_clock_init(void __iomem *slcr);
struct clk *clk_register_zynq_pll(const char *name, const char *parent,
void __iomem *pll_ctrl, void __iomem *pll_status, u8 lock_index,
spinlock_t *lock);
#endif #endif