dect
/
linux-2.6
Archived
13
0
Fork 0

Merge branch 'zynq/multiplatform' of git://git.monstr.eu/linux-2.6-microblaze into next/multiplatform

From Michal Simek:

This branch depends on arm-soc devel/debug_ll_init branch because
we needed Rob's "ARM: implement debug_ll_io_init()"
(sha1: afaee03511ba8002b26a9c6b1fe7d6baf33eac86)
patch.

This branch also depends on zynq/dt branch because of previous major
zynq changes.
zynq/cleanup branch is subset of zynq/dt.

* 'zynq/multiplatform' of git://git.monstr.eu/linux-2.6-microblaze:
  ARM: zynq: Remove all unused mach headers
  ARM: zynq: add support for ARCH_MULTIPLATFORM
  ARM: zynq: make use of debug_ll_io_init()
  ARM: zynq: remove TTC early mapping
  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

Add/add conflict in arch/arm/Kconfig.debug.

Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2012-11-21 11:49:45 -08:00
commit 3f54db784a
23 changed files with 933 additions and 456 deletions

View File

@ -0,0 +1,55 @@
Device Tree Clock bindings for the Zynq 7000 EPP
The Zynq EPP has several different clk providers, each with there own bindings.
The purpose of this document is to document their usage.
See clock_bindings.txt for more information on the generic clock bindings.
See Chapter 25 of Zynq TRM for more information about Zynq clocks.
== PLLs ==
Used to describe the ARM_PLL, DDR_PLL, and IO_PLL.
Required properties:
- #clock-cells : shall be 0 (only one clock is output from this node)
- compatible : "xlnx,zynq-pll"
- reg : pair of u32 values, which are the address offsets within the SLCR
of the relevant PLL_CTRL register and PLL_CFG register respectively
- clocks : phandle for parent clock. should be the phandle for ps_clk
Optional properties:
- clock-output-names : name of the output clock
Example:
armpll: armpll {
#clock-cells = <0>;
compatible = "xlnx,zynq-pll";
clocks = <&ps_clk>;
reg = <0x100 0x110>;
clock-output-names = "armpll";
};
== Peripheral clocks ==
Describes clock node for the SDIO, SMC, SPI, QSPI, and UART clocks.
Required properties:
- #clock-cells : shall be 1
- compatible : "xlnx,zynq-periph-clock"
- reg : a single u32 value, describing the offset within the SLCR where
the CLK_CTRL register is found for this peripheral
- clocks : phandle for parent clocks. should hold phandles for
the IO_PLL, ARM_PLL, and DDR_PLL in order
- clock-output-names : names of the output clock(s). For peripherals that have
two output clocks (for example, the UART), two clocks
should be listed.
Example:
uart_clk: uart_clk {
#clock-cells = <1>;
compatible = "xlnx,zynq-periph-clock";
clocks = <&iopll &armpll &ddrpll>;
reg = <0x154>;
clock-output-names = "uart0_ref_clk",
"uart1_ref_clk";
};

View File

@ -960,18 +960,6 @@ config ARCH_VT8500_SINGLE
help
Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
config ARCH_ZYNQ
bool "Xilinx Zynq ARM Cortex A9 Platform"
select ARM_AMBA
select ARM_GIC
select CLKDEV_LOOKUP
select CPU_V7
select GENERIC_CLOCKEVENTS
select ICST
select MIGHT_HAVE_CACHE_L2X0
select USE_OF
help
Support for Xilinx Zynq ARM Cortex A9 Platform
endchoice
menu "Multiple platform selection"
@ -1134,6 +1122,8 @@ source "arch/arm/mach-vt8500/Kconfig"
source "arch/arm/mach-w90x900/Kconfig"
source "arch/arm/mach-zynq/Kconfig"
# Definitions to make life easier
config ARCH_ACORN
bool

View File

@ -132,6 +132,23 @@ choice
their output to UART1 serial port on DaVinci TNETV107X
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
bool "Kernel low-level debugging messages via footbridge serial port"
depends on FOOTBRIDGE
@ -456,6 +473,7 @@ config DEBUG_LL_INCLUDE
default "debug/vexpress.S" if DEBUG_VEXPRESS_UART0_DETECT || \
DEBUG_VEXPRESS_UART0_CA9 || DEBUG_VEXPRESS_UART0_RS1
default "debug/tegra.S" if DEBUG_TEGRA_UART
default "debug/zynq.S" if DEBUG_ZYNQ_UART0 || DEBUG_ZYNQ_UART1
default "mach/debug-macro.S"
config EARLY_PRINTK

View File

@ -199,7 +199,6 @@ machine-$(CONFIG_ARCH_ZYNQ) += zynq
plat-$(CONFIG_ARCH_MXC) += mxc
plat-$(CONFIG_ARCH_OMAP) += omap
plat-$(CONFIG_ARCH_S3C64XX) += samsung
plat-$(CONFIG_ARCH_ZYNQ) += versatile
plat-$(CONFIG_PLAT_IOP) += iop
plat-$(CONFIG_PLAT_ORION) += orion
plat-$(CONFIG_PLAT_PXA) += pxa

View File

@ -103,5 +103,6 @@ dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
wm8505-ref.dtb \
wm8650-mid.dtb
dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb
endif

View File

@ -0,0 +1,166 @@
/*
* 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>;
};
};
};
};

View File

@ -1,52 +0,0 @@
/*
* 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>;
};
};
};

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2011 Xilinx
* Copyright (C) 2012 National Instruments Corp.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/dts-v1/;
/include/ "zynq-7000.dtsi"
/ {
model = "Zynq ZC702 Development Board";
compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000";
memory {
device_type = "memory";
reg = <0x0 0x40000000>;
};
chosen {
bootargs = "console=ttyPS1,115200 earlyprintk";
};
};
&ps_clk {
clock-frequency = <33333330>;
};
&ttc0_0 {
status = "ok";
compatible = "xlnx,ttc-counter-clocksource";
};
&ttc0_1 {
status = "ok";
compatible = "xlnx,ttc-counter-clockevent";
};

View File

@ -1,5 +1,4 @@
/* arch/arm/mach-zynq/include/mach/debug-macro.S
*
/*
* Debugging macro include header
*
* Copyright (C) 2011 Xilinx
@ -13,9 +12,25 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define UART_CR_OFFSET 0x00 /* Control Register [8:0] */
#define UART_SR_OFFSET 0x2C /* Channel Status [11:0] */
#define UART_FIFO_OFFSET 0x30 /* FIFO [15:0] or [7:0] */
#include <mach/zynq_soc.h>
#include <mach/uart.h>
#define UART_SR_TXFULL 0x00000010 /* TX FIFO full */
#define UART_SR_TXEMPTY 0x00000008 /* TX FIFO empty */
#define UART0_PHYS 0xE0000000
#define UART1_PHYS 0xE0001000
#define UART_SIZE SZ_4K
#define UART_VIRT 0xF0001000
#if IS_ENABLED(CONFIG_DEBUG_ZYNQ_UART1)
# define LL_UART_PADDR UART1_PHYS
#else
# define LL_UART_PADDR UART0_PHYS
#endif
#define LL_UART_VADDR UART_VIRT
.macro addruart, rp, rv, tmp
ldr \rp, =LL_UART_PADDR @ physical

View File

@ -0,0 +1,13 @@
config ARCH_ZYNQ
bool "Xilinx Zynq ARM Cortex A9 Platform" if ARCH_MULTI_V7
select ARM_AMBA
select ARM_GIC
select COMMON_CLK
select CPU_V7
select GENERIC_CLOCKEVENTS
select ICST
select MIGHT_HAVE_CACHE_L2X0
select USE_OF
select SPARSE_IRQ
help
Support for Xilinx Zynq ARM Cortex A9 Platform

View File

@ -19,19 +19,21 @@
#include <linux/cpumask.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/clk/zynq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/mach-types.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/hardware/gic.h>
#include <asm/hardware/cache-l2x0.h>
#include <mach/zynq_soc.h>
#include <mach/clkdev.h>
#include "common.h"
static struct of_device_id zynq_of_bus_ids[] __initdata = {
@ -45,55 +47,57 @@ static struct of_device_id zynq_of_bus_ids[] __initdata = {
*/
static void __init xilinx_init_machine(void)
{
#ifdef CONFIG_CACHE_L2X0
/*
* 64KB way size, 8-way associativity, parity disabled
*/
l2x0_init(PL310_L2CC_BASE, 0x02060000, 0xF0F0FFFF);
#endif
l2x0_of_init(0x02060000, 0xF0F0FFFF);
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.
*/
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
* running include the GIC, UART and Timer Counter.
#define SCU_PERIPH_PHYS 0xF8F00000
#define SCU_PERIPH_SIZE SZ_8K
#define SCU_PERIPH_VIRT (VMALLOC_END - SCU_PERIPH_SIZE)
static struct map_desc scu_desc __initdata = {
.virtual = SCU_PERIPH_VIRT,
.pfn = __phys_to_pfn(SCU_PERIPH_PHYS),
.length = SCU_PERIPH_SIZE,
.type = MT_DEVICE,
};
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 map_desc io_desc[] __initdata = {
{
.virtual = TTC0_VIRT,
.pfn = __phys_to_pfn(TTC0_PHYS),
.length = SZ_4K,
.type = MT_DEVICE,
}, {
.virtual = SCU_PERIPH_VIRT,
.pfn = __phys_to_pfn(SCU_PERIPH_PHYS),
.length = SZ_8K,
.type = MT_DEVICE,
}, {
.virtual = PL310_L2CC_VIRT,
.pfn = __phys_to_pfn(PL310_L2CC_PHYS),
.length = SZ_4K,
.type = MT_DEVICE,
},
#ifdef CONFIG_DEBUG_LL
{
.virtual = UART0_VIRT,
.pfn = __phys_to_pfn(UART0_PHYS),
.length = SZ_4K,
.type = MT_DEVICE,
},
#endif
static struct sys_timer xttcpss_sys_timer = {
.init = xilinx_zynq_timer_init,
};
/**
@ -101,11 +105,13 @@ static struct map_desc io_desc[] __initdata = {
*/
static void __init xilinx_map_io(void)
{
iotable_init(io_desc, ARRAY_SIZE(io_desc));
debug_ll_io_init();
iotable_init(&scu_desc, 1);
}
static const char *xilinx_dt_match[] = {
"xlnx,zynq-ep107",
"xlnx,zynq-zc702",
"xlnx,zynq-7000",
NULL
};

View File

@ -17,8 +17,6 @@
#ifndef __MACH_ZYNQ_COMMON_H__
#define __MACH_ZYNQ_COMMON_H__
#include <asm/mach/time.h>
extern struct sys_timer xttcpss_sys_timer;
void __init xttcpss_timer_init(void);
#endif

View File

@ -1,32 +0,0 @@
/*
* arch/arm/mach-zynq/include/mach/clkdev.h
*
* Copyright (C) 2011 Xilinx, Inc.
*
* 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.
*
*/
#ifndef __MACH_CLKDEV_H__
#define __MACH_CLKDEV_H__
#include <plat/clock.h>
struct clk {
unsigned long rate;
const struct clk_ops *ops;
const struct icst_params *params;
void __iomem *vcoreg;
};
#define __clk_get(clk) ({ 1; })
#define __clk_put(clk) do { } while (0)
#endif

View File

@ -1,18 +0,0 @@
/* arch/arm/mach-zynq/include/mach/hardware.h
*
* 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.
*/
#ifndef __MACH_HARDWARE_H__
#define __MACH_HARDWARE_H__
#endif

View File

@ -1,21 +0,0 @@
/* arch/arm/mach-zynq/include/mach/irqs.h
*
* 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.
*/
#ifndef __MACH_IRQS_H
#define __MACH_IRQS_H
#define ARCH_NR_GPIOS 118
#define NR_IRQS (128 + ARCH_NR_GPIOS)
#endif

View File

@ -1,23 +0,0 @@
/* arch/arm/mach-zynq/include/mach/timex.h
*
* 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.
*/
#ifndef __MACH_TIMEX_H__
#define __MACH_TIMEX_H__
/* the following is needed for the system to build but will be removed
in the future, the value is not important but won't hurt
*/
#define CLOCK_TICK_RATE (100 * HZ)
#endif

View File

@ -1,25 +0,0 @@
/* arch/arm/mach-zynq/include/mach/uart.h
*
* 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.
*/
#ifndef __MACH_UART_H__
#define __MACH_UART_H__
#define UART_CR_OFFSET 0x00 /* Control Register [8:0] */
#define UART_SR_OFFSET 0x2C /* Channel Status [11:0] */
#define UART_FIFO_OFFSET 0x30 /* FIFO [15:0] or [7:0] */
#define UART_SR_TXFULL 0x00000010 /* TX FIFO full */
#define UART_SR_TXEMPTY 0x00000008 /* TX FIFO empty */
#endif

View File

@ -1,51 +0,0 @@
/* arch/arm/mach-zynq/include/mach/uncompress.h
*
* 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.
*/
#ifndef __MACH_UNCOMPRESS_H__
#define __MACH_UNCOMPRESS_H__
#include <linux/io.h>
#include <asm/processor.h>
#include <mach/zynq_soc.h>
#include <mach/uart.h>
void arch_decomp_setup(void)
{
}
static inline void flush(void)
{
/*
* Wait while the FIFO is not empty
*/
while (!(__raw_readl(IOMEM(LL_UART_PADDR + UART_SR_OFFSET)) &
UART_SR_TXEMPTY))
cpu_relax();
}
#define arch_decomp_wdog()
static void putc(char ch)
{
/*
* Wait for room in the FIFO, then write the char into the FIFO
*/
while (__raw_readl(IOMEM(LL_UART_PADDR + UART_SR_OFFSET)) &
UART_SR_TXFULL)
cpu_relax();
__raw_writel(ch, IOMEM(LL_UART_PADDR + UART_FIFO_OFFSET));
}
#endif

View File

@ -1,48 +0,0 @@
/* arch/arm/mach-zynq/include/mach/zynq_soc.h
*
* 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.
*/
#ifndef __MACH_XILINX_SOC_H__
#define __MACH_XILINX_SOC_H__
#define PERIPHERAL_CLOCK_RATE 2500000
/* For now, all mappings are flat (physical = virtual)
*/
#define UART0_PHYS 0xE0000000
#define UART0_VIRT UART0_PHYS
#define TTC0_PHYS 0xF8001000
#define TTC0_VIRT TTC0_PHYS
#define PL310_L2CC_PHYS 0xF8F02000
#define PL310_L2CC_VIRT PL310_L2CC_PHYS
#define SCU_PERIPH_PHYS 0xF8F00000
#define SCU_PERIPH_VIRT SCU_PERIPH_PHYS
/* The following are intended for the devices that are mapped early */
#define TTC0_BASE IOMEM(TTC0_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

View File

@ -23,32 +23,14 @@
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <asm/mach/time.h>
#include <mach/zynq_soc.h>
#include "common.h"
#define IRQ_TIMERCOUNTER0 42
/*
* This driver configures the 2 16-bit count-up timers as follows:
*
* T1: Timer 1, clocksource for generic timekeeping
* T2: Timer 2, clockevent source for hrtimers
* T3: Timer 3, <unused>
*
* The input frequency to the timer module for emulation is 2.5MHz which is
* common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
* the timers are clocked at 78.125KHz (12.8 us resolution).
*
* The input frequency to the timer module in silicon will be 200MHz. With the
* pre-scaler of 32, the timers are clocked at 6.25MHz (160ns resolution).
*/
#define XTTCPSS_CLOCKSOURCE 0 /* Timer 1 as a generic timekeeping */
#define XTTCPSS_CLOCKEVENT 1 /* Timer 2 as a clock event */
#define XTTCPSS_TIMER_BASE TTC0_BASE
#define XTTCPCC_EVENT_TIMER_IRQ (IRQ_TIMERCOUNTER0 + 1)
/*
* Timer Register Offset Definitions of Timer 1, Increment base address by 4
* and use same offsets for Timer 2
@ -65,9 +47,14 @@
#define XTTCPSS_CNT_CNTRL_DISABLE_MASK 0x1
/* Setup the timers to use pre-scaling */
#define TIMER_RATE (PERIPHERAL_CLOCK_RATE / 32)
/* 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
*/
#define PRESCALE_EXPONENT 11 /* 2 ^ PRESCALE_EXPONENT = PRESCALE */
#define PRESCALE 2048 /* The exponent must match this */
#define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1)
#define CLK_CNTRL_PRESCALE_EN 1
#define CNT_CNTRL_RESET (1<<4)
/**
* struct xttcpss_timer - This definition defines local timer structure
@ -75,11 +62,25 @@
* @base_addr: Base address of timer
**/
struct xttcpss_timer {
void __iomem *base_addr;
void __iomem *base_addr;
};
static struct xttcpss_timer timers[2];
static struct clock_event_device xttcpss_clockevent;
struct xttcpss_timer_clocksource {
struct xttcpss_timer xttc;
struct clocksource cs;
};
#define to_xttcpss_timer_clksrc(x) \
container_of(x, struct xttcpss_timer_clocksource, cs)
struct xttcpss_timer_clockevent {
struct xttcpss_timer xttc;
struct clock_event_device ce;
struct clk *clk;
};
#define to_xttcpss_timer_clkevent(x) \
container_of(x, struct xttcpss_timer_clockevent, ce)
/**
* xttcpss_set_interval - Set the timer interval value
@ -101,7 +102,7 @@ static void xttcpss_set_interval(struct xttcpss_timer *timer,
/* Reset the counter (0x10) so that it starts from 0, one-shot
mode makes this needed for timing to be right. */
ctrl_reg |= 0x10;
ctrl_reg |= CNT_CNTRL_RESET;
ctrl_reg &= ~XTTCPSS_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg, timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET);
}
@ -116,90 +117,31 @@ static void xttcpss_set_interval(struct xttcpss_timer *timer,
**/
static irqreturn_t xttcpss_clock_event_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = &xttcpss_clockevent;
struct xttcpss_timer *timer = dev_id;
struct xttcpss_timer_clockevent *xttce = dev_id;
struct xttcpss_timer *timer = &xttce->xttc;
/* Acknowledge the interrupt and call event handler */
__raw_writel(__raw_readl(timer->base_addr + XTTCPSS_ISR_OFFSET),
timer->base_addr + XTTCPSS_ISR_OFFSET);
evt->event_handler(evt);
xttce->ce.event_handler(&xttce->ce);
return IRQ_HANDLED;
}
static struct irqaction event_timer_irq = {
.name = "xttcpss clockevent",
.flags = IRQF_DISABLED | IRQF_TIMER,
.handler = xttcpss_clock_event_interrupt,
};
/**
* xttcpss_timer_hardware_init - Initialize the timer hardware
*
* Initialize the hardware to start the clock source, get the clock
* event timer ready to use, and hook up the interrupt.
**/
static void __init xttcpss_timer_hardware_init(void)
{
/* Setup the clock source counter to be an incrementing counter
* with no interrupt and it rolls over at 0xFFFF. Pre-scale
it by 32 also. Let it start running now.
*/
timers[XTTCPSS_CLOCKSOURCE].base_addr = XTTCPSS_TIMER_BASE;
__raw_writel(0x0, timers[XTTCPSS_CLOCKSOURCE].base_addr +
XTTCPSS_IER_OFFSET);
__raw_writel(0x9, timers[XTTCPSS_CLOCKSOURCE].base_addr +
XTTCPSS_CLK_CNTRL_OFFSET);
__raw_writel(0x10, timers[XTTCPSS_CLOCKSOURCE].base_addr +
XTTCPSS_CNT_CNTRL_OFFSET);
/* Setup the clock event timer to be an interval timer which
* is prescaled by 32 using the interval interrupt. Leave it
* disabled for now.
*/
timers[XTTCPSS_CLOCKEVENT].base_addr = XTTCPSS_TIMER_BASE + 4;
__raw_writel(0x23, timers[XTTCPSS_CLOCKEVENT].base_addr +
XTTCPSS_CNT_CNTRL_OFFSET);
__raw_writel(0x9, timers[XTTCPSS_CLOCKEVENT].base_addr +
XTTCPSS_CLK_CNTRL_OFFSET);
__raw_writel(0x1, timers[XTTCPSS_CLOCKEVENT].base_addr +
XTTCPSS_IER_OFFSET);
/* Setup IRQ the clock event timer */
event_timer_irq.dev_id = &timers[XTTCPSS_CLOCKEVENT];
setup_irq(XTTCPCC_EVENT_TIMER_IRQ, &event_timer_irq);
}
/**
* __raw_readl_cycles - Reads the timer counter register
* __xttc_clocksource_read - Reads the timer counter register
*
* returns: Current timer counter register value
**/
static cycle_t __raw_readl_cycles(struct clocksource *cs)
static cycle_t __xttc_clocksource_read(struct clocksource *cs)
{
struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKSOURCE];
struct xttcpss_timer *timer = &to_xttcpss_timer_clksrc(cs)->xttc;
return (cycle_t)__raw_readl(timer->base_addr +
XTTCPSS_COUNT_VAL_OFFSET);
}
/*
* Instantiate and initialize the clock source structure
*/
static struct clocksource clocksource_xttcpss = {
.name = "xttcpss_timer1",
.rating = 200, /* Reasonable clock source */
.read = __raw_readl_cycles,
.mask = CLOCKSOURCE_MASK(16),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
/**
* xttcpss_set_next_event - Sets the time interval for next event
*
@ -211,7 +153,8 @@ static struct clocksource clocksource_xttcpss = {
static int xttcpss_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKEVENT];
struct xttcpss_timer_clockevent *xttce = to_xttcpss_timer_clkevent(evt);
struct xttcpss_timer *timer = &xttce->xttc;
xttcpss_set_interval(timer, cycles);
return 0;
@ -226,12 +169,15 @@ static int xttcpss_set_next_event(unsigned long cycles,
static void xttcpss_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKEVENT];
struct xttcpss_timer_clockevent *xttce = to_xttcpss_timer_clkevent(evt);
struct xttcpss_timer *timer = &xttce->xttc;
u32 ctrl_reg;
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
xttcpss_set_interval(timer, TIMER_RATE / HZ);
xttcpss_set_interval(timer,
DIV_ROUND_CLOSEST(clk_get_rate(xttce->clk),
PRESCALE * HZ));
break;
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
@ -252,15 +198,106 @@ static void xttcpss_set_mode(enum clock_event_mode mode,
}
}
/*
* Instantiate and initialize the clock event structure
*/
static struct clock_event_device xttcpss_clockevent = {
.name = "xttcpss_timer2",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = xttcpss_set_next_event,
.set_mode = xttcpss_set_mode,
.rating = 200,
static void __init zynq_ttc_setup_clocksource(struct device_node *np,
void __iomem *base)
{
struct xttcpss_timer_clocksource *ttccs;
struct clk *clk;
int err;
u32 reg;
ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
if (WARN_ON(!ttccs))
return;
err = of_property_read_u32(np, "reg", &reg);
if (WARN_ON(err))
return;
clk = of_clk_get_by_name(np, "cpu_1x");
if (WARN_ON(IS_ERR(clk)))
return;
err = clk_prepare_enable(clk);
if (WARN_ON(err))
return;
ttccs->xttc.base_addr = base + reg * 4;
ttccs->cs.name = np->name;
ttccs->cs.rating = 200;
ttccs->cs.read = __xttc_clocksource_read;
ttccs->cs.mask = CLOCKSOURCE_MASK(16);
ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
__raw_writel(0x0, ttccs->xttc.base_addr + XTTCPSS_IER_OFFSET);
__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttccs->xttc.base_addr + XTTCPSS_CLK_CNTRL_OFFSET);
__raw_writel(CNT_CNTRL_RESET,
ttccs->xttc.base_addr + XTTCPSS_CNT_CNTRL_OFFSET);
err = clocksource_register_hz(&ttccs->cs, clk_get_rate(clk) / PRESCALE);
if (WARN_ON(err))
return;
}
static void __init zynq_ttc_setup_clockevent(struct device_node *np,
void __iomem *base)
{
struct xttcpss_timer_clockevent *ttcce;
int err, irq;
u32 reg;
ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
if (WARN_ON(!ttcce))
return;
err = of_property_read_u32(np, "reg", &reg);
if (WARN_ON(err))
return;
ttcce->xttc.base_addr = base + reg * 4;
ttcce->clk = of_clk_get_by_name(np, "cpu_1x");
if (WARN_ON(IS_ERR(ttcce->clk)))
return;
err = clk_prepare_enable(ttcce->clk);
if (WARN_ON(err))
return;
irq = irq_of_parse_and_map(np, 0);
if (WARN_ON(!irq))
return;
ttcce->ce.name = np->name;
ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
ttcce->ce.set_next_event = xttcpss_set_next_event;
ttcce->ce.set_mode = xttcpss_set_mode;
ttcce->ce.rating = 200;
ttcce->ce.irq = irq;
__raw_writel(0x23, ttcce->xttc.base_addr + XTTCPSS_CNT_CNTRL_OFFSET);
__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttcce->xttc.base_addr + XTTCPSS_CLK_CNTRL_OFFSET);
__raw_writel(0x1, ttcce->xttc.base_addr + XTTCPSS_IER_OFFSET);
err = request_irq(irq, xttcpss_clock_event_interrupt, IRQF_TIMER,
np->name, ttcce);
if (WARN_ON(err))
return;
clockevents_config_and_register(&ttcce->ce,
clk_get_rate(ttcce->clk) / PRESCALE,
1, 0xfffe);
}
static const __initconst struct of_device_id zynq_ttc_match[] = {
{ .compatible = "xlnx,ttc-counter-clocksource",
.data = zynq_ttc_setup_clocksource, },
{ .compatible = "xlnx,ttc-counter-clockevent",
.data = zynq_ttc_setup_clockevent, },
{}
};
/**
@ -269,30 +306,27 @@ static struct clock_event_device xttcpss_clockevent = {
* Initializes the timer hardware and register the clock source and clock event
* timers with Linux kernal timer framework
**/
static void __init xttcpss_timer_init(void)
void __init xttcpss_timer_init(void)
{
xttcpss_timer_hardware_init();
clocksource_register_hz(&clocksource_xttcpss, TIMER_RATE);
struct device_node *np;
/* Calculate the parameters to allow the clockevent to operate using
integer math
*/
clockevents_calc_mult_shift(&xttcpss_clockevent, TIMER_RATE, 4);
for_each_compatible_node(np, NULL, "xlnx,ttc") {
struct device_node *np_chld;
void __iomem *base;
xttcpss_clockevent.max_delta_ns =
clockevent_delta2ns(0xfffe, &xttcpss_clockevent);
xttcpss_clockevent.min_delta_ns =
clockevent_delta2ns(1, &xttcpss_clockevent);
base = of_iomap(np, 0);
if (WARN_ON(!base))
return;
/* Indicate that clock event is on 1st CPU as SMP boot needs it */
for_each_available_child_of_node(np, np_chld) {
int (*cb)(struct device_node *np, void __iomem *base);
const struct of_device_id *match;
xttcpss_clockevent.cpumask = cpumask_of(0);
clockevents_register_device(&xttcpss_clockevent);
match = of_match_node(zynq_ttc_match, np_chld);
if (match) {
cb = match->data;
cb(np_chld, base);
}
}
}
}
/*
* Instantiate and initialize the system timer structure
*/
struct sys_timer xttcpss_sys_timer = {
.init = xttcpss_timer_init,
};

View File

@ -19,6 +19,7 @@ endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o
# Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o

383
drivers/clk/clk-zynq.c Normal file
View File

@ -0,0 +1,383 @@
/*
* 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);
}

24
include/linux/clk/zynq.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2012 National Instruments
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __LINUX_CLK_ZYNQ_H_
#define __LINUX_CLK_ZYNQ_H_
void __init xilinx_zynq_clocks_init(void __iomem *slcr);
#endif