Commit 8ec1c811 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge branch 'arm-next' of git://git.xilinx.com/linux-xlnx into next/dt

From Michal Simek <michal.simek@xilinx.com>:

These are based on previous patches (arm-soc zynq/cleanup branch).
The branch is still based on rc3 but I have also tried to merged it
with the v3.7-rc5 and there is no issue.

* 'arm-next' of git://git.xilinx.com/linux-xlnx:
  ARM: zynq: add clk binding support to the ttc
  ARM: zynq: use zynq clk bindings
  clk: Add support for fundamental zynq clks
  ARM: zynq: dts: split up device tree
  ARM: zynq: Allow UART1 to be used as DEBUG_LL console.
  ARM: zynq: dts: add description of the second uart
  ARM: zynq: move arm-specific sys_timer out of ttc
  zynq: move static peripheral mappings
  zynq: remove use of CLKDEV_LOOKUP
  zynq: use pl310 device tree bindings
  zynq: use GIC device tree bindings
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents bac2f668 91dc985c
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";
};
...@@ -945,7 +945,7 @@ config ARCH_ZYNQ ...@@ -945,7 +945,7 @@ config ARCH_ZYNQ
bool "Xilinx Zynq ARM Cortex A9 Platform" bool "Xilinx Zynq ARM Cortex A9 Platform"
select ARM_AMBA select ARM_AMBA
select ARM_GIC select ARM_GIC
select CLKDEV_LOOKUP select COMMON_CLK
select CPU_V7 select CPU_V7
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select ICST select ICST
......
...@@ -132,6 +132,23 @@ choice ...@@ -132,6 +132,23 @@ choice
their output to UART1 serial port on DaVinci TNETV107X their output to UART1 serial port on DaVinci TNETV107X
devices. devices.
config DEBUG_ZYNQ_UART0
bool "Kernel low-level debugging on Xilinx Zynq using UART0"
depends on ARCH_ZYNQ
help
Say Y here if you want the debug print routines to direct
their output to UART0 on the Zynq platform.
config DEBUG_ZYNQ_UART1
bool "Kernel low-level debugging on Xilinx Zynq using UART1"
depends on ARCH_ZYNQ
help
Say Y here if you want the debug print routines to direct
their output to UART1 on the Zynq platform.
If you have a ZC702 board and want early boot messages to
appear on the USB serial adaptor, select this option.
config DEBUG_DC21285_PORT config DEBUG_DC21285_PORT
bool "Kernel low-level debugging messages via footbridge serial port" bool "Kernel low-level debugging messages via footbridge serial port"
depends on FOOTBRIDGE depends on FOOTBRIDGE
......
...@@ -198,7 +198,6 @@ machine-$(CONFIG_ARCH_ZYNQ) += zynq ...@@ -198,7 +198,6 @@ machine-$(CONFIG_ARCH_ZYNQ) += zynq
# by CONFIG_* macro name. # by CONFIG_* macro name.
plat-$(CONFIG_ARCH_OMAP) += omap plat-$(CONFIG_ARCH_OMAP) += omap
plat-$(CONFIG_ARCH_S3C64XX) += samsung plat-$(CONFIG_ARCH_S3C64XX) += samsung
plat-$(CONFIG_ARCH_ZYNQ) += versatile
plat-$(CONFIG_PLAT_IOP) += iop plat-$(CONFIG_PLAT_IOP) += iop
plat-$(CONFIG_PLAT_NOMADIK) += nomadik plat-$(CONFIG_PLAT_NOMADIK) += nomadik
plat-$(CONFIG_PLAT_ORION) += orion plat-$(CONFIG_PLAT_ORION) += orion
......
...@@ -105,5 +105,6 @@ dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \ ...@@ -105,5 +105,6 @@ dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \ dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
wm8505-ref.dtb \ wm8505-ref.dtb \
wm8650-mid.dtb wm8650-mid.dtb
dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb
endif endif
/*
* Copyright (C) 2011 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.
*/
/include/ "skeleton.dtsi"
/ {
compatible = "xlnx,zynq-7000";
amba {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
ranges;
intc: interrupt-controller@f8f01000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
#address-cells = <1>;
interrupt-controller;
reg = <0xF8F01000 0x1000>,
<0xF8F00100 0x100>;
};
L2: cache-controller {
compatible = "arm,pl310-cache";
reg = <0xF8F02000 0x1000>;
arm,data-latency = <2 3 2>;
arm,tag-latency = <2 3 2>;
cache-unified;
cache-level = <2>;
};
uart0: uart@e0000000 {
compatible = "xlnx,xuartps";
reg = <0xE0000000 0x1000>;
interrupts = <0 27 4>;
clock = <50000000>;
};
uart1: uart@e0001000 {
compatible = "xlnx,xuartps";
reg = <0xE0001000 0x1000>;
interrupts = <0 50 4>;
clock = <50000000>;
};
slcr: slcr@f8000000 {
compatible = "xlnx,zynq-slcr";
reg = <0xF8000000 0x1000>;
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 {
#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";
};
};
};
ttc0: ttc0@f8001000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "xlnx,ttc";
reg = <0xF8001000 0x1000>;
clocks = <&cpu_clk 3>;
clock-names = "cpu_1x";
clock-ranges;
ttc0_0: ttc0.0 {
status = "disabled";
reg = <0>;
interrupts = <0 10 4>;
};
ttc0_1: ttc0.1 {
status = "disabled";
reg = <1>;
interrupts = <0 11 4>;
};
ttc0_2: ttc0.2 {
status = "disabled";
reg = <2>;
interrupts = <0 12 4>;
};
};
ttc1: ttc1@f8002000 {
#interrupt-parent = <&intc>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "xlnx,ttc";
reg = <0xF8002000 0x1000>;
clocks = <&cpu_clk 3>;
clock-names = "cpu_1x";
clock-ranges;
ttc1_0: ttc1.0 {
status = "disabled";
reg = <0>;
interrupts = <0 37 4>;
};
ttc1_1: ttc1.1 {
status = "disabled";
reg = <1>;
interrupts = <0 38 4>;
};
ttc1_2: ttc1.2 {
status = "disabled";
reg = <2>;
interrupts = <0 39 4>;
};
};
};
};
/*
* Copyright (C) 2011 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/;
/ {
model = "Xilinx Zynq EP107";
compatible = "xlnx,zynq-ep107";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
memory {
device_type = "memory";
reg = <0x0 0x10000000>;
};
chosen {
bootargs = "console=ttyPS0,9600 root=/dev/ram rw initrd=0x800000,8M earlyprintk";
linux,stdout-path = &uart0;
};
amba {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
intc: interrupt-controller@f8f01000 {
interrupt-controller;
compatible = "arm,gic";
reg = <0xF8F01000 0x1000>;
#interrupt-cells = <2>;
};
uart0: uart@e0000000 {
compatible = "xlnx,xuartps";
reg = <0xE0000000 0x1000>;
interrupts = <59 0>;
clock = <50000000>;
};
};
};
/* /*
* arch/arm/mach-zynq/include/mach/clkdev.h * Copyright (C) 2011 Xilinx
* * Copyright (C) 2012 National Instruments Corp.
* Copyright (C) 2011 Xilinx, Inc.
* *
* This software is licensed under the terms of the GNU General Public * This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and * License version 2, as published by the Free Software Foundation, and
...@@ -11,22 +10,35 @@ ...@@ -11,22 +10,35 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*
*/ */
/dts-v1/;
/include/ "zynq-7000.dtsi"
#ifndef __MACH_CLKDEV_H__ / {
#define __MACH_CLKDEV_H__ model = "Zynq ZC702 Development Board";
compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000";
#include <plat/clock.h> memory {
device_type = "memory";
reg = <0x0 0x40000000>;
};
chosen {
bootargs = "console=ttyPS1,115200 earlyprintk";
};
struct clk {
unsigned long rate;
const struct clk_ops *ops;
const struct icst_params *params;
void __iomem *vcoreg;
}; };
#define __clk_get(clk) ({ 1; }) &ps_clk {
#define __clk_put(clk) do { } while (0) clock-frequency = <33333330>;
};
#endif &ttc0_0 {
status = "ok";
compatible = "xlnx,ttc-counter-clocksource";
};
&ttc0_1 {
status = "ok";
compatible = "xlnx,ttc-counter-clockevent";
};
...@@ -19,19 +19,21 @@ ...@@ -19,19 +19,21 @@
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk/zynq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of.h> #include <linux/of.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/hardware/gic.h> #include <asm/hardware/gic.h>
#include <asm/hardware/cache-l2x0.h> #include <asm/hardware/cache-l2x0.h>
#include <mach/zynq_soc.h> #include <mach/zynq_soc.h>
#include <mach/clkdev.h>
#include "common.h" #include "common.h"
static struct of_device_id zynq_of_bus_ids[] __initdata = { static struct of_device_id zynq_of_bus_ids[] __initdata = {
...@@ -45,22 +47,25 @@ static struct of_device_id zynq_of_bus_ids[] __initdata = { ...@@ -45,22 +47,25 @@ static struct of_device_id zynq_of_bus_ids[] __initdata = {
*/ */
static void __init xilinx_init_machine(void) static void __init xilinx_init_machine(void)
{ {
#ifdef CONFIG_CACHE_L2X0
/* /*
* 64KB way size, 8-way associativity, parity disabled * 64KB way size, 8-way associativity, parity disabled
*/ */
l2x0_init(PL310_L2CC_BASE, 0x02060000, 0xF0F0FFFF); l2x0_of_init(0x02060000, 0xF0F0FFFF);
#endif
of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL); of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL);
} }
static struct of_device_id irq_match[] __initdata = {
{ .compatible = "arm,cortex-a9-gic", .data = gic_of_init, },
{ }
};
/** /**
* xilinx_irq_init() - Interrupt controller initialization for the GIC. * xilinx_irq_init() - Interrupt controller initialization for the GIC.
*/ */
static void __init xilinx_irq_init(void) static void __init xilinx_irq_init(void)
{ {
gic_init(0, 29, SCU_GIC_DIST_BASE, SCU_GIC_CPU_BASE); of_irq_init(irq_match);
} }
/* The minimum devices needed to be mapped before the VM system is up and /* The minimum devices needed to be mapped before the VM system is up and
...@@ -71,31 +76,47 @@ static struct map_desc io_desc[] __initdata = { ...@@ -71,31 +76,47 @@ static struct map_desc io_desc[] __initdata = {
{ {
.virtual = TTC0_VIRT, .virtual = TTC0_VIRT,
.pfn = __phys_to_pfn(TTC0_PHYS), .pfn = __phys_to_pfn(TTC0_PHYS),
.length = SZ_4K, .length = TTC0_SIZE,
.type = MT_DEVICE, .type = MT_DEVICE,
}, { }, {
.virtual = SCU_PERIPH_VIRT, .virtual = SCU_PERIPH_VIRT,
.pfn = __phys_to_pfn(SCU_PERIPH_PHYS), .pfn = __phys_to_pfn(SCU_PERIPH_PHYS),
.length = SZ_8K, .length = SCU_PERIPH_SIZE,
.type = MT_DEVICE,
}, {
.virtual = PL310_L2CC_VIRT,
.pfn = __phys_to_pfn(PL310_L2CC_PHYS),
.length = SZ_4K,
.type = MT_DEVICE, .type = MT_DEVICE,
}, },
#ifdef CONFIG_DEBUG_LL #ifdef CONFIG_DEBUG_LL
{ {
.virtual = UART0_VIRT, .virtual = LL_UART_VADDR,
.pfn = __phys_to_pfn(UART0_PHYS), .pfn = __phys_to_pfn(LL_UART_PADDR),
.length = SZ_4K, .length = UART_SIZE,
.type = MT_DEVICE, .type = MT_DEVICE,
}, },
#endif #endif
}; };
static void __init xilinx_zynq_timer_init(void)
{
struct device_node *np;
void __iomem *slcr;
np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
slcr = of_iomap(np, 0);
WARN_ON(!slcr);
xilinx_zynq_clocks_init(slcr);
xttcpss_timer_init();
}
/*
* Instantiate and initialize the system timer structure
*/
static struct sys_timer xttcpss_sys_timer = {
.init = xilinx_zynq_timer_init,
};
/** /**
* xilinx_map_io() - Create memory mappings needed for early I/O. * xilinx_map_io() - Create memory mappings needed for early I/O.
*/ */
...@@ -105,7 +126,8 @@ static void __init xilinx_map_io(void) ...@@ -105,7 +126,8 @@ static void __init xilinx_map_io(void)
} }
static const char *xilinx_dt_match[] = { static const char *xilinx_dt_match[] = {
"xlnx,zynq-ep107", "xlnx,zynq-zc702",
"xlnx,zynq-7000",
NULL NULL
}; };
......
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
#ifndef __MACH_ZYNQ_COMMON_H__ #ifndef __MACH_ZYNQ_COMMON_H__
#define __MACH_ZYNQ_COMMON_H__ #define __MACH_ZYNQ_COMMON_H__
#include <asm/mach/time.h> void __init xttcpss_timer_init(void);
extern struct sys_timer xttcpss_sys_timer;
#endif #endif
...@@ -15,34 +15,39 @@ ...@@ -15,34 +15,39 @@
#ifndef __MACH_XILINX_SOC_H__ #ifndef __MACH_XILINX_SOC_H__
#define __MACH_XILINX_SOC_H__ #define __MACH_XILINX_SOC_H__
#include <asm/pgtable.h>
#define PERIPHERAL_CLOCK_RATE 2500000 #define PERIPHERAL_CLOCK_RATE 2500000
/* For now, all mappings are flat (physical = virtual) /* Static peripheral mappings are mapped at the top of the vmalloc region. The
* early uart mapping causes intermediate problems/failure at certain
* addresses, including the very top of the vmalloc region. Map it at an
* address that is known to work.
*/ */
#define UART0_PHYS 0xE0000000 #define UART0_PHYS 0xE0000000
#define UART0_VIRT UART0_PHYS #define UART1_PHYS 0xE0001000
#define UART_SIZE SZ_4K
#define TTC0_PHYS 0xF8001000 #define UART_VIRT 0xF0001000
#define TTC0_VIRT TTC0_PHYS
#define TTC0_PHYS 0xF8001000
#define PL310_L2CC_PHYS 0xF8F02000 #define TTC0_SIZE SZ_4K
#define PL310_L2CC_VIRT PL310_L2CC_PHYS #define TTC0_VIRT (VMALLOC_END - TTC0_SIZE)
#define SCU_PERIPH_PHYS 0xF8F00000
#define SCU_PERIPH_SIZE SZ_8K
#define SCU_PERIPH_VIRT (TTC0_VIRT - SCU_PERIPH_SIZE)
#if IS_ENABLED(CONFIG_DEBUG_ZYNQ_UART1)
# define LL_UART_PADDR UART1_PHYS
#else
# define LL_UART_PADDR UART0_PHYS
#endif
#define SCU_PERIPH_PHYS 0xF8F00000 #define LL_UART_VADDR UART_VIRT
#define SCU_PERIPH_VIRT SCU_PERIPH_PHYS
/* The following are intended for the devices that are mapped early */ /* The following are intended for the devices that are mapped early */
#define TTC0_BASE IOMEM(TTC0_VIRT) #define TTC0_BASE IOMEM(TTC0_VIRT)
#define SCU_PERIPH_BASE IOMEM(SCU_PERIPH_VIRT) #define SCU_PERIPH_BASE IOMEM(SCU_PERIPH_VIRT)
#define SCU_GIC_CPU_BASE (SCU_PERIPH_BASE + 0x100)
#define SCU_GIC_DIST_BASE (SCU_PERIPH_BASE + 0x1000)
#define PL310_L2CC_BASE IOMEM(PL310_L2CC_VIRT)
/*
* Mandatory for CONFIG_LL_DEBUG, UART is mapped virtual = physical
*/
#define LL_UART_PADDR UART0_PHYS
#define LL_UART_VADDR UART0_VIRT
#endif #endif
This diff is collapsed.
...@@ -19,6 +19,7 @@ endif ...@@ -19,6 +19,7 @@ endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
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
# Chip specific # Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
......
/*
* 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>
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", &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);
}
/*
* 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
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