Commit e56c60c3 authored by Olof Johansson's avatar Olof Johansson

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

From Michal Simek:
arm: Xilinx Zynq clock changes for v3.11

Change Xilinx Zynq DT clock description which reflects logical abstraction
of Zynq's clock tree.

- Refactor PLL driver
- Use new clock controller driver
- Change timer and uart drivers

* tag 'zynq-clk-for-3.11' of git://git.xilinx.com/linux-xlnx:
  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: default avatarOlof Johansson <olof@lixom.net>
parents f4902492 97c4e87d
......@@ -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 Chapter 25 of Zynq TRM for more information about Zynq clocks.
== PLLs ==
Used to describe the ARM_PLL, DDR_PLL, and IO_PLL.
== Clock Controller ==
The clock controller is a logical abstraction of Zynq's clock tree. It reads
required input clock frequencies from the devicetree and acts as clock provider
for all clock consumers of PS clocks.
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
- #clock-cells : Must be 1
- compatible : "xlnx,ps7-clkc"
- ps-clk-frequency : Frequency of the oscillator providing ps_clk in HZ
(usually 33 MHz oscillators are used for Zynq platforms)
- clock-output-names : List of strings used to name the clock outputs. Shall be
a list of the outputs given below.
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 ==
- clocks : as described in the clock bindings
- clock-names : as described in the clock bindings
Describes clock node for the SDIO, SMC, SPI, QSPI, and UART clocks.
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
...
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.
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:
uart_clk: uart_clk {
clkc: clkc {
#clock-cells = <1>;
compatible = "xlnx,zynq-periph-clock";
clocks = <&iopll &armpll &ddrpll>;
reg = <0x154>;
clock-output-names = "uart0_ref_clk",
"uart1_ref_clk";
compatible = "xlnx,ps7-clkc";
ps-clk-frequency = <33333333>;
clock-output-names = "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";
# optional props
clocks = <&clkc 16>, <&clk_foo>;
clock-names = "gem1_emio_clk", "can_mio_clk_23";
};
......@@ -49,16 +49,18 @@ L2: cache-controller {
uart0: uart@e0000000 {
compatible = "xlnx,xuartps";
clocks = <&clkc 23>, <&clkc 40>;
clock-names = "ref_clk", "aper_clk";
reg = <0xE0000000 0x1000>;
interrupts = <0 27 4>;
clocks = <&uart_clk 0>;
};
uart1: uart@e0001000 {
compatible = "xlnx,xuartps";
clocks = <&clkc 24>, <&clkc 41>;
clock-names = "ref_clk", "aper_clk";
reg = <0xE0001000 0x1000>;
interrupts = <0 50 4>;
clocks = <&uart_clk 1>;
};
slcr: slcr@f8000000 {
......@@ -69,50 +71,21 @@ clocks {
#address-cells = <1>;
#size-cells = <0>;
ps_clk: ps_clk {
#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>;
compatible = "xlnx,zynq-periph-clock";
clocks = <&iopll &armpll &ddrpll>;
reg = <0x154>;
clock-output-names = "uart0_ref_clk",
"uart1_ref_clk";
};
cpu_clk: cpu_clk {
clkc: clkc {
#clock-cells = <1>;
compatible = "xlnx,zynq-cpu-clock";
clocks = <&iopll &armpll &ddrpll>;
reg = <0x120 0x1C4>;
clock-output-names = "cpu_6x4x",
"cpu_3x2x",
"cpu_2x",
"cpu_1x";
compatible = "xlnx,ps7-clkc";
ps-clk-frequency = <33333333>;
clock-output-names = "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";
};
};
};
......@@ -121,9 +94,8 @@ ttc0: ttc0@f8001000 {
interrupt-parent = <&intc>;
interrupts = < 0 10 4 0 11 4 0 12 4 >;
compatible = "cdns,ttc";
clocks = <&clkc 6>;
reg = <0xF8001000 0x1000>;
clocks = <&cpu_clk 3>;
clock-names = "cpu_1x";
clock-ranges;
};
......@@ -131,9 +103,8 @@ ttc1: ttc1@f8002000 {
interrupt-parent = <&intc>;
interrupts = < 0 37 4 0 38 4 0 39 4 >;
compatible = "cdns,ttc";
clocks = <&clkc 6>;
reg = <0xF8002000 0x1000>;
clocks = <&cpu_clk 3>;
clock-names = "cpu_1x";
clock-ranges;
};
scutimer: scutimer@f8f00600 {
......@@ -141,7 +112,7 @@ scutimer: scutimer@f8f00600 {
interrupts = < 1 13 0x301 >;
compatible = "arm,cortex-a9-twd-timer";
reg = < 0xf8f00600 0x20 >;
clocks = <&cpu_clk 1>;
clocks = <&clkc 4>;
} ;
};
};
......@@ -28,7 +28,3 @@ chosen {
};
};
&ps_clk {
clock-frequency = <33333330>;
};
......@@ -117,7 +117,7 @@ int __init zynq_slcr_init(void)
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);
......
......@@ -27,7 +27,7 @@ obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/
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_PLAT_SAMSUNG) += samsung/
......
/*
* 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);
}
# Zynq clock specific Makefile
obj-$(CONFIG_ARCH_ZYNQ) += clkc.o pll.o
This diff is collapsed.
/*
* 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;
}
......@@ -51,6 +51,8 @@
#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
* 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;
void __iomem *timer_baseaddr;
struct clk *clk;
struct clk *clk_cs, *clk_ce;
static int initialized;
int clksel;
if (initialized)
return;
......@@ -421,14 +424,24 @@ static void __init ttc_timer_init(struct device_node *timer)
BUG();
}
clk = of_clk_get_by_name(timer, "cpu_1x");
if (IS_ERR(clk)) {
clksel = __raw_readl(timer_baseaddr + TTC_CLK_CNTRL_OFFSET);
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");
BUG();
}
clksel = __raw_readl(timer_baseaddr + 4 + TTC_CLK_CNTRL_OFFSET);
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, timer_baseaddr);
ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
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);
}
......
......@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/console.h>
......@@ -138,6 +139,16 @@
#define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */
#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
* @irq: Irq number
......@@ -936,34 +947,55 @@ static int xuartps_probe(struct platform_device *pdev)
int rc;
struct uart_port *port;
struct resource *res, *res2;
struct clk *clk;
struct xuartps *xuartps_data;
clk = of_clk_get(pdev->dev.of_node, 0);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "no clock specified\n");
return PTR_ERR(clk);
xuartps_data = kzalloc(sizeof(*xuartps_data), GFP_KERNEL);
if (!xuartps_data)
return -ENOMEM;
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) {
dev_err(&pdev->dev, "Unable to enable APER clock.\n");
goto err_out_clk_put;
}
rc = clk_prepare_enable(xuartps_data->refclk);
if (rc) {
dev_err(&pdev->dev, "could not enable clock\n");
return -EBUSY;
dev_err(&pdev->dev, "Unable to enable device clock.\n");
goto err_out_clk_dis_aper;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
if (!res) {
rc = -ENODEV;
goto err_out_clk_disable;
}
res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res2)
return -ENODEV;
if (!res2) {
rc = -ENODEV;
goto err_out_clk_disable;
}
/* Initialize the port structure */
port = xuartps_get_port();
if (!port) {
dev_err(&pdev->dev, "Cannot get uart_port structure\n");
return -ENODEV;
rc = -ENODEV;
goto err_out_clk_disable;
} else {
/* Register the port.
* 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->irq = res2->start;
port->dev = &pdev->dev;
port->uartclk = clk_get_rate(clk);
port->private_data = clk;
port->uartclk = clk_get_rate(xuartps_data->refclk);
port->private_data = xuartps_data;
dev_set_drvdata(&pdev->dev, port);
rc = uart_add_one_port(&xuartps_uart_driver, port);
if (rc) {
dev_err(&pdev->dev,
"uart_add_one_port() failed; err=%i\n", rc);
dev_set_drvdata(&pdev->dev, NULL);
return rc;
goto err_out_clk_disable;
}
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)
{
struct uart_port *port = dev_get_drvdata(&pdev->dev);
struct clk *clk = port->private_data;
struct xuartps *xuartps_data = port->private_data;
int rc;
/* Remove the xuartps port from the serial core */
rc = uart_remove_one_port(&xuartps_uart_driver, port);
dev_set_drvdata(&pdev->dev, NULL);
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;
}
......
/*
* Copyright (C) 2013 Xilinx Inc.
* Copyright (C) 2012 National Instruments
*
* This program is free software; you can redistribute it and/or modify
......@@ -19,6 +20,11 @@
#ifndef __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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment