Thread (1 message) 1 message, 1 author, 2015-06-23

[PATCH 6/9] arm: twr-k70f120m: clock source drivers for Kinetis SoC

DORMANTno replies

From: Paul Osmialowski <hidden>
Date: 2015-06-23 21:19:44
Subsystem: arm port, clocksource, clockevent drivers, common clk framework, open firmware and flattened device tree bindings, the rest · Maintainers: Russell King, Daniel Lezcano, Thomas Gleixner, Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Torvalds

Possibly related (same subject, not in this thread)

Based on K70P256M150SF3RM.pdf

Signed-off-by: Paul Osmialowski <pawelo-rhuoMcPwk82rDJvtcaxF/A@public.gmane.org>
---
 .../devicetree/bindings/clock/kinetis-clock.txt    |  25 ++
 .../bindings/timer/fsl,kinetis-pit-timer.txt       |  18 ++
 arch/arm/Kconfig                                   |   1 +
 arch/arm/boot/dts/kinetis-twr-k70f120m.dts         |   4 +
 arch/arm/boot/dts/kinetis.dtsi                     |  50 ++++
 arch/arm/mach-kinetis/include/mach/power.h         |  83 ++++++
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk-kinetis.c                          | 226 ++++++++++++++++
 drivers/clocksource/Kconfig                        |   5 +
 drivers/clocksource/Makefile                       |   1 +
 drivers/clocksource/timer-kinetis.c                | 294 +++++++++++++++++++++
 include/dt-bindings/clock/kinetis-mcg.h            |  10 +
 12 files changed, 718 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/kinetis-clock.txt
 create mode 100644 Documentation/devicetree/bindings/timer/fsl,kinetis-pit-timer.txt
 create mode 100644 arch/arm/mach-kinetis/include/mach/power.h
 create mode 100644 drivers/clk/clk-kinetis.c
 create mode 100644 drivers/clocksource/timer-kinetis.c
 create mode 100644 include/dt-bindings/clock/kinetis-mcg.h
diff --git a/Documentation/devicetree/bindings/clock/kinetis-clock.txt b/Documentation/devicetree/bindings/clock/kinetis-clock.txt
new file mode 100644
index 0000000..9c9c4fe
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/kinetis-clock.txt
@@ -0,0 +1,25 @@
+* Clock bindings for Freescale Kinetis SoC
+
+Required properties:
+- compatible: Should be "fsl,kinetis-cmu".
+- reg: Address and length of the register set.
+- #clock-cells: Should be <1>.
+
+Example:
+
+mcg: cmu@40064000 {
+	compatible = "fsl,kinetis-cmu";
+	reg = <0x40064000 0x14>;
+	#clock-cells = <1>;
+};
+
+uart1: serial@4006b000 {
+	compatible = "fsl,kinetis-lpuart";
+	reg = <0x4006b000 0x1000>;
+	interrupts = <47>, <48>;
+	interrupt-names = "uart-stat", "uart-err";
+	clocks = <&mcg CLOCK_UART1>;
+	clock-names = "ipg";
+	dmas = <&edma 0 4>;
+	dma-names = "rx";
+};
diff --git a/Documentation/devicetree/bindings/timer/fsl,kinetis-pit-timer.txt b/Documentation/devicetree/bindings/timer/fsl,kinetis-pit-timer.txt
new file mode 100644
index 0000000..49dddf6
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/fsl,kinetis-pit-timer.txt
@@ -0,0 +1,18 @@
+Freescale Kinetis SoC Periodic Interrupt Timer (PIT)
+
+Required properties:
+
+- compatible: Should be "fsl,kinetis-pit-timer".
+- reg: Specifies base physical address and size of the register sets for the
+  clock event device.
+- interrupts: Should be the clock event device interrupt.
+- clocks: The clocks provided by the SoC to drive the timer.
+
+Example:
+
+pit0: timer@40037100 {
+	compatible = "fsl,kinetis-pit-timer";
+	reg = <0x40037100 0x10>;
+	interrupts = <68>;
+	clocks = <&mcg CLOCK_PIT>;
+};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 747cdea..8630aff 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -979,6 +979,7 @@ config ARCH_KINETIS
 	select CPU_CORTEXM3
 	select ARM_CPU_IDLE_QUIRKS
 	select ARMV7M_SYSTICK
+	select CLKSRC_KINETIS
 	select ZLIB_INFLATE_STACK_SAVING if ZLIB_INFLATE
 	help
 	  This enables support for the Freescale Kinetis MCUs
diff --git a/arch/arm/boot/dts/kinetis-twr-k70f120m.dts b/arch/arm/boot/dts/kinetis-twr-k70f120m.dts
index edccf37..a6efc29 100644
--- a/arch/arm/boot/dts/kinetis-twr-k70f120m.dts
+++ b/arch/arm/boot/dts/kinetis-twr-k70f120m.dts
@@ -14,3 +14,7 @@
 		reg = <0x8000000 0x8000000>;
 	};
 };
+
+&pit0 {
+	status = "ok";
+};
diff --git a/arch/arm/boot/dts/kinetis.dtsi b/arch/arm/boot/dts/kinetis.dtsi
index 93d2a8a..770760f 100644
--- a/arch/arm/boot/dts/kinetis.dtsi
+++ b/arch/arm/boot/dts/kinetis.dtsi
@@ -3,3 +3,53 @@
  *
  */
 #include "armv7-m.dtsi"
+#include "dt-bindings/clock/kinetis-mcg.h"
+
+/ {
+	aliases {
+		pit0 = &pit0;
+		pit1 = &pit1;
+		pit2 = &pit2;
+		pit3 = &pit3;
+	};
+
+	soc {
+		pit0: timer@40037100 {
+			compatible = "fsl,kinetis-pit-timer";
+			reg = <0x40037100 0x10>;
+			interrupts = <68>;
+			clocks = <&mcg CLOCK_PIT>;
+			status = "disabled";
+		};
+
+		pit1: timer@40037110 {
+			compatible = "fsl,kinetis-pit-timer";
+			reg = <0x40037110 0x10>;
+			interrupts = <69>;
+			clocks = <&mcg CLOCK_PIT>;
+			status = "disabled";
+		};
+
+		pit2: timer@40037120 {
+			compatible = "fsl,kinetis-pit-timer";
+			reg = <0x40037120 0x10>;
+			interrupts = <70>;
+			clocks = <&mcg CLOCK_PIT>;
+			status = "disabled";
+		};
+
+		pit3: timer@40037130 {
+			compatible = "fsl,kinetis-pit-timer";
+			reg = <0x40037130 0x10>;
+			interrupts = <71>;
+			clocks = <&mcg CLOCK_PIT>;
+			status = "disabled";
+		};
+
+		mcg: cmu@40064000 {
+			compatible = "fsl,kinetis-cmu";
+			reg = <0x40064000 0x14>;
+			#clock-cells = <1>;
+		};
+	};
+};
diff --git a/arch/arm/mach-kinetis/include/mach/power.h b/arch/arm/mach-kinetis/include/mach/power.h
new file mode 100644
index 0000000..e67bd4e
--- /dev/null
+++ b/arch/arm/mach-kinetis/include/mach/power.h
@@ -0,0 +1,83 @@
+/*
+ * (C) Copyright 2011, 2012
+ * Emcraft Systems, <www.emcraft.com>
+ * Alexander Potashev <aspotashev-qv+LCo8X3VpBDgjK7y7TUQ@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#ifndef _MACH_KINETIS_POWER_H
+#define _MACH_KINETIS_POWER_H
+
+/*
+ * Pack the SIM_SCGC[] register index and the bit index in that register into
+ * a single word. This is similar to the implementation of `dev_t` in
+ * the Linux kernel with its `MAJOR(dev)`, `MINOR(dev)` and
+ * `MKDEV(major,minor)` macros.
+ *
+ * This is useful when you want to have an array of `kinetis_clock_gate_t`s:
+ * you do not have to use a 2-dimensional array or a real structure.
+ */
+typedef u32 kinetis_clock_gate_t;
+#define KINETIS_CG_IDX_BITS	16
+#define KINETIS_CG_IDX_MASK	((1U << KINETIS_CG_IDX_BITS) - 1)
+/*
+ * Extract the register number and the bit index from a `kinetis_clock_gate_t`.
+ * The register number counts from 0,
+ * i.e. the register number for SIM_SCGC7 is 6.
+ */
+#define KINETIS_CG_REG(gate)	((unsigned int) ((gate) >> KINETIS_CG_IDX_BITS))
+#define KINETIS_CG_IDX(gate)	((unsigned int) ((gate) & KINETIS_CG_IDX_MASK))
+/*
+ * Build a `kinetis_clock_gate_t` from a register number and a bit index
+ */
+#define KINETIS_MKCG(reg, idx) \
+	(((kinetis_clock_gate_t)(reg) << KINETIS_CG_IDX_BITS) | \
+	(kinetis_clock_gate_t)(idx))
+
+/*
+ * Clock gates for the modules inside the MCU
+ */
+/* UARTs */
+#define KINETIS_CG_UART0	KINETIS_MKCG(3, 10)	/* SIM_SCGC4[10] */
+#define KINETIS_CG_UART1	KINETIS_MKCG(3, 11)	/* SIM_SCGC4[11] */
+#define KINETIS_CG_UART2	KINETIS_MKCG(3, 12)	/* SIM_SCGC4[12] */
+#define KINETIS_CG_UART3	KINETIS_MKCG(3, 13)	/* SIM_SCGC4[13] */
+#define KINETIS_CG_UART4	KINETIS_MKCG(0, 10)	/* SIM_SCGC1[10] */
+#define KINETIS_CG_UART5	KINETIS_MKCG(0, 11)	/* SIM_SCGC1[11] */
+/* Ports */
+#define KINETIS_CG_PORTA	KINETIS_MKCG(4, 9)	/* SIM_SCGC5[9] */
+#define KINETIS_CG_PORTB	KINETIS_MKCG(4, 10)	/* SIM_SCGC5[10] */
+#define KINETIS_CG_PORTC	KINETIS_MKCG(4, 11)	/* SIM_SCGC5[11] */
+#define KINETIS_CG_PORTD	KINETIS_MKCG(4, 12)	/* SIM_SCGC5[12] */
+#define KINETIS_CG_PORTE	KINETIS_MKCG(4, 13)	/* SIM_SCGC5[13] */
+#define KINETIS_CG_PORTF	KINETIS_MKCG(4, 14)	/* SIM_SCGC5[14] */
+/* ENET */
+#define KINETIS_CG_ENET		KINETIS_MKCG(1, 0)	/* SIM_SCGC2[0] */
+/* Periodic Interrupt Timer (PIT) */
+#define KINETIS_CG_PIT		KINETIS_MKCG(5, 23)	/* SIM_SCGC6[23] */
+/* LCD Controller */
+#define KINETIS_CG_LCDC		KINETIS_MKCG(2, 22)	/* SIM_SCGC3[22] */
+/* DMA controller and DMA request multiplexer */
+#define KINETIS_CG_DMA		KINETIS_MKCG(6, 1)	/* SIM_SCGC7[1] */
+#define KINETIS_CG_DMAMUX0	KINETIS_MKCG(5, 1)	/* SIM_SCGC6[1] */
+#define KINETIS_CG_DMAMUX1	KINETIS_MKCG(5, 2)	/* SIM_SCGC6[2] */
+/* USB High Speed */
+#define KINETIS_CG_USBHS	KINETIS_MKCG(5, 20)	/* SIM_SCGC6[20] */
+/* USB Full Speed */
+#define KINETIS_CG_USBFS	KINETIS_MKCG(3, 18)	/* SIM_SCGC4[18] */
+/* ADC modules */
+#define KINETIS_CG_ADC0		KINETIS_MKCG(5, 27)	/* SIM_SCGC6[27] */
+#define KINETIS_CG_ADC1		KINETIS_MKCG(2, 27)	/* SIM_SCGC3[27] */
+#define KINETIS_CG_ADC2		KINETIS_MKCG(5, 28)	/* SIM_SCGC6[28] */
+#define KINETIS_CG_ADC3		KINETIS_MKCG(2, 28)	/* SIM_SCGC3[28] */
+/* ESDHC */
+#define KINETIS_CG_ESDHC	KINETIS_MKCG(2, 17)	/* SIM_SCGC3[17] */
+/* SPI */
+#define KINETIS_CG_SPI0		KINETIS_MKCG(5, 12)	/* SIM_SCGC6[12] */
+#define KINETIS_CG_SPI1		KINETIS_MKCG(5, 13)	/* SIM_SCGC6[13] */
+#define KINETIS_CG_SPI2		KINETIS_MKCG(2, 12)	/* SIM_SCGC3[12] */
+
+#endif /*_MACH_KINETIS_POWER_H */
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index b241c17..58718ed 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE706)	+= clk-cdce706.o
 obj-$(CONFIG_ARCH_CLPS711X)		+= clk-clps711x.o
 obj-$(CONFIG_ARCH_EFM32)		+= clk-efm32gg.o
 obj-$(CONFIG_ARCH_HIGHBANK)		+= clk-highbank.o
+obj-$(CONFIG_ARCH_KINETIS)		+= clk-kinetis.o
 obj-$(CONFIG_MACH_LOONGSON32)		+= clk-ls1x.o
 obj-$(CONFIG_COMMON_CLK_MAX_GEN)	+= clk-max-gen.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
diff --git a/drivers/clk/clk-kinetis.c b/drivers/clk/clk-kinetis.c
new file mode 100644
index 0000000..dea1054
--- /dev/null
+++ b/drivers/clk/clk-kinetis.c
@@ -0,0 +1,226 @@
+/*
+ * clk-kinetis.c - Clock driver for Kinetis K70 MCG
+ *
+ * Based on legacy pre-OF code by Alexander Potashev <aspotashev-qv+LCo8X3VpBDgjK7y7TUQ@public.gmane.org>
+ *
+ * Copyright (C) 2015 Paul Osmialowski <pawelo-rhuoMcPwk82rDJvtcaxF/A@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/err.h>
+#include <mach/kinetis.h>
+#include <mach/power.h>
+
+#include <dt-bindings/clock/kinetis-mcg.h>
+
+/*
+ * Frequencies on OSC0 (EXTAL0/XTAL0) and OSC1 (EXTAL1/XTAL1)
+ *
+ * These frequencies should be set to the same values as in U-Boot.
+ */
+#define KINETIS_OSC0_RATE	50000000	/* 50 MHz */
+#define KINETIS_OSC1_RATE	12000000	/* 12 MHz */
+
+/*
+ * MCG Control 5 Register
+ */
+/* PLL External Reference Divider */
+#define KINETIS_MCG_C5_PRDIV_BITS	0
+#define KINETIS_MCG_C5_PRDIV_MSK \
+	(((1 << 3) - 1) << KINETIS_MCG_C5_PRDIV_BITS)
+/* PLL Stop Enable */
+#define KINETIS_MCG_C5_PLLSTEN_MSK	(1 << 5)
+/* PLL Clock Enable */
+#define KINETIS_MCG_C5_PLLCLKEN_MSK	(1 << 6)
+/* PLL External Reference Select (for K70@120MHz) */
+#define KINETIS_MCG_C5_PLLREFSEL_BIT	7
+#define KINETIS_MCG_C5_PLLREFSEL_MSK	(1 << KINETIS_MCG_C5_PLLREFSEL_BIT)
+/*
+ * MCG Control 6 Register
+ */
+/* VCO Divider */
+#define KINETIS_MCG_C6_VDIV_BITS	0
+#define KINETIS_MCG_C6_VDIV_MSK \
+	(((1 << 5) - 1) << KINETIS_MCG_C6_VDIV_BITS)
+/* PLL Select */
+#define KINETIS_MCG_C6_PLLS_MSK		(1 << 6)
+/*
+ * MCG Control 11 Register
+ */
+/* PLL1 External Reference Divider */
+#define KINETIS_MCG_C11_PRDIV_BITS	0
+#define KINETIS_MCG_C11_PRDIV_MSK \
+	(((1 << 3) - 1) << KINETIS_MCG_C11_PRDIV_BITS)
+/* PLL Clock Select: PLL0 or PLL1 */
+#define KINETIS_MCG_C11_PLLCS_MSK	(1 << 4)
+/* PLL1 Stop Enable */
+#define KINETIS_MCG_C11_PLLSTEN1_MSK	(1 << 5)
+/* PLL1 Clock Enable */
+#define KINETIS_MCG_C11_PLLCLKEN1_MSK	(1 << 6)
+/* PLL1 External Reference Select (for K70@120MHz) */
+#define KINETIS_MCG_C11_PLLREFSEL1_BIT	7
+#define KINETIS_MCG_C11_PLLREFSEL1_MSK	(1 << KINETIS_MCG_C11_PLLREFSEL1_BIT)
+/*
+ * MCG Control 12 Register
+ */
+/* VCO1 Divider */
+#define KINETIS_MCG_C12_VDIV1_BITS	0
+#define KINETIS_MCG_C12_VDIV1_MSK \
+	(((1 << 5) - 1) << KINETIS_MCG_C12_VDIV1_BITS)
+
+/*
+ * Multipurpose Clock Generator (MCG) register map
+ *
+ * See Chapter 25 of the K70 Reference Manual
+ */
+struct kinetis_mcg_regs {
+	u8 c1;		/* MCG Control 1 Register */
+	u8 c2;		/* MCG Control 2 Register */
+	u8 c3;		/* MCG Control 3 Register */
+	u8 c4;		/* MCG Control 4 Register */
+	u8 c5;		/* MCG Control 5 Register */
+	u8 c6;		/* MCG Control 6 Register */
+	u8 status;	/* MCG Status Register */
+	u8 rsv0;
+	u8 atc;		/* MCG Auto Trim Control Register */
+	u8 rsv1;
+	u8 atcvh;	/* MCG Auto Trim Compare Value High Register */
+	u8 atcvl;	/* MCG Auto Trim Compare Value Low Register */
+	u8 c7;		/* MCG Control 7 Register */
+	u8 c8;		/* MCG Control 8 Register */
+	u8 rsv2;
+	u8 c10;		/* MCG Control 10 Register */
+	u8 c11;		/* MCG Control 11 Register */
+	u8 c12;		/* MCG Control 12 Register */
+	u8 status2;	/* MCG Status 2 Register */
+	u8 rsv3;
+};
+
+#define KINETIS_MCG_PTR(base, reg) \
+	(&(((struct kinetis_mcg_regs *)(base))->reg))
+#define KINETIS_MCG_RD(base, reg) readb_relaxed(KINETIS_MCG_PTR(base, reg))
+#define KINETIS_MCG_WR(base, reg, val) \
+	writeb_relaxed((val), KINETIS_MCG_PTR(base, reg))
+#define KINETIS_MCG_ISSET(base, reg, mask) \
+	(KINETIS_MCG_RD(base, reg) & (mask))
+
+static struct clk *clk[CLOCK_END];
+static struct clk_onecell_data clk_data = {
+	.clks = clk,
+	.clk_num = ARRAY_SIZE(clk),
+};
+
+static void __init kinetis_mcg_init(struct device_node *np)
+{
+	const int vco_div = 2;
+	const int vdiv_min = 16;
+	u32 clock_val[CLOCK_END];
+	int i;
+	void __iomem *base;
+	int pll_sel;
+	int osc_sel;
+	unsigned long mcgout;
+
+	for (i = 0; i < ARRAY_SIZE(clk); ++i)
+		clk[i] = ERR_PTR(-ENOENT);
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_warn("Failed to map address range for kinetis,mcg node\n");
+		return;
+	}
+
+	/*
+	 * Check whether PLL0 or PLL1 is used for MCGOUTCLK
+	 */
+	pll_sel = !!(KINETIS_MCG_ISSET(base, c11, KINETIS_MCG_C11_PLLCS_MSK));
+
+	/*
+	 * Check whether OSC0 or OSC1 is used to source the main PLL
+	 */
+	if (pll_sel)
+		osc_sel = !!(KINETIS_MCG_ISSET(base, c11,
+				KINETIS_MCG_C11_PLLREFSEL1_MSK));
+	else
+		osc_sel = !!(KINETIS_MCG_ISSET(base, c5,
+				KINETIS_MCG_C5_PLLREFSEL_MSK));
+
+	/*
+	 * Start with the MCG input clock
+	 */
+	mcgout = osc_sel ? KINETIS_OSC1_RATE : KINETIS_OSC0_RATE;
+
+	/*
+	 * Apply dividers and multipliers of the selected PLL
+	 */
+	if (pll_sel) {
+		/*
+		 * PLL1 internal divider (PRDIV)
+		 */
+		mcgout /= ((KINETIS_MCG_RD(base, c11) &
+		  KINETIS_MCG_C11_PRDIV_MSK) >> KINETIS_MCG_C11_PRDIV_BITS) + 1;
+		/*
+		 * PLL1 multiplication factor (VDIV)
+		 */
+		mcgout *= ((KINETIS_MCG_RD(base, c12) &
+		  KINETIS_MCG_C12_VDIV1_MSK) >> KINETIS_MCG_C12_VDIV1_BITS) +
+								    vdiv_min;
+	} else {
+		/*
+		 * PLL0 internal divider (PRDIV)
+		 */
+		mcgout /= ((KINETIS_MCG_RD(base, c5) &
+			KINETIS_MCG_C5_PRDIV_MSK) >>
+			KINETIS_MCG_C5_PRDIV_BITS) + 1;
+		/*
+		 * PLL0 multiplication factor (VDIV)
+		 */
+		mcgout *= ((KINETIS_MCG_RD(base, c6) &
+			KINETIS_MCG_C6_VDIV_MSK) >>
+			KINETIS_MCG_C6_VDIV_BITS) + vdiv_min;
+	}
+
+	/*
+	 * Apply the PLL output divider
+	 */
+	mcgout /= vco_div;
+
+	clock_val[CLOCK_MCGOUTCLK] = mcgout;
+
+	clock_val[CLOCK_CCLK] = mcgout /
+		(((KINETIS_SIM_RD(clkdiv1) & KINETIS_SIM_CLKDIV1_OUTDIV1_MSK) >>
+		KINETIS_SIM_CLKDIV1_OUTDIV1_BITS) + 1);
+
+	/*
+	 * Peripheral (bus) clock
+	 */
+	clock_val[CLOCK_PCLK] = mcgout /
+		(((KINETIS_SIM_RD(clkdiv1) & KINETIS_SIM_CLKDIV1_OUTDIV2_MSK) >>
+		KINETIS_SIM_CLKDIV1_OUTDIV2_BITS) + 1);
+
+	clk[CLOCK_MCGOUTCLK] = clk_register_fixed_rate(NULL, "MCGOUTCLK",
+			NULL, CLK_IS_ROOT, clock_val[CLOCK_MCGOUTCLK]);
+
+	clk[CLOCK_CCLK] = clk_register_fixed_rate(NULL, "CCLK", "MCGOUTCLK",
+			0, clock_val[CLOCK_CCLK]);
+
+	clk[CLOCK_PCLK] = clk_register_fixed_rate(NULL, "PCLK", "MCGOUTCLK",
+			0, clock_val[CLOCK_PCLK]);
+
+	clk[CLOCK_PIT] = clk_register_gate(NULL, "PIT", "PCLK", 0,
+			KINETIS_SIM_PTR(scgc[KINETIS_CG_REG(KINETIS_CG_PIT)]),
+			KINETIS_CG_IDX(KINETIS_CG_PIT), 0, NULL);
+
+	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
+
+CLK_OF_DECLARE(kinetis_mcg, "fsl,kinetis-cmu", kinetis_mcg_init);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 0f1c77e..1d2ecde 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -106,6 +106,11 @@ config CLKSRC_EFM32
 	  Support to use the timers of EFM32 SoCs as clock source and clock
 	  event device.
 
+config CLKSRC_KINETIS
+	bool "Clocksource for Kinetis SoCs"
+	depends on OF && ARM && ARCH_KINETIS
+	select CLKSRC_OF
+
 config CLKSRC_LPC32XX
 	bool
 	select CLKSRC_MMIO
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index f1ae0e7..6da77a8 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_ARCH_NSPIRE)	+= zevio-timer.o
 obj-$(CONFIG_ARCH_BCM_MOBILE)	+= bcm_kona_timer.o
 obj-$(CONFIG_CADENCE_TTC_TIMER)	+= cadence_ttc_timer.o
 obj-$(CONFIG_CLKSRC_EFM32)	+= time-efm32.o
+obj-$(CONFIG_CLKSRC_KINETIS)	+= timer-kinetis.o
 obj-$(CONFIG_CLKSRC_STM32)	+= timer-stm32.o
 obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o
 obj-$(CONFIG_CLKSRC_LPC32XX)	+= time-lpc32xx.o
diff --git a/drivers/clocksource/timer-kinetis.c b/drivers/clocksource/timer-kinetis.c
new file mode 100644
index 0000000..634f365
--- /dev/null
+++ b/drivers/clocksource/timer-kinetis.c
@@ -0,0 +1,294 @@
+/*
+ * timer-kinetis.c - Timer driver for Kinetis K70
+ *
+ * Based on legacy pre-OF code by Alexander Potashev <aspotashev-qv+LCo8X3VpBDgjK7y7TUQ@public.gmane.org>
+ *
+ * Copyright (C) 2015 Paul Osmialowski <pawelo-rhuoMcPwk82rDJvtcaxF/A@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/clk.h>
+#include <linux/sched.h>
+#include <mach/kinetis.h>
+
+#define KINETIS_PIT_CHANNELS 4
+
+#define KINETIS_PIT0_IRQ 68
+#define KINETIS_PIT1_IRQ 69
+#define KINETIS_PIT2_IRQ 70
+#define KINETIS_PIT3_IRQ 71
+
+/*
+ * PIT Timer Control Register
+ */
+/* Timer Interrupt Enable Bit */
+#define KINETIS_PIT_TCTRL_TIE_MSK	(1 << 1)
+/* Timer Enable Bit */
+#define KINETIS_PIT_TCTRL_TEN_MSK	(1 << 0)
+/*
+ * PIT Timer Flag Register
+ */
+/* Timer Interrupt Flag */
+#define KINETIS_PIT_TFLG_TIF_MSK	(1 << 0)
+
+/*
+ * PIT control registers base
+ */
+#define KINETIS_PIT_BASE		(KINETIS_AIPS0PERIPH_BASE + 0x00037000)
+#define KINETIS_PIT_MCR			IOMEM(KINETIS_PIT_BASE + 0x0)
+
+/*
+ * Periodic Interrupt Timer (PIT) registers
+ */
+struct kinetis_pit_channel_regs {
+	u32 ldval;	/* Timer Load Value Register */
+	u32 cval;	/* Current Timer Value Register */
+	u32 tctrl;	/* Timer Control Register */
+	u32 tflg;	/* Timer Flag Register */
+};
+
+#define KINETIS_PIT_PTR(base, reg) \
+	(&(((struct kinetis_pit_channel_regs *)(base))->reg))
+#define KINETIS_PIT_RD(base, reg) readl(KINETIS_PIT_PTR(base, reg))
+#define KINETIS_PIT_WR(base, reg, val) \
+	writel((val), KINETIS_PIT_PTR(base, reg))
+#define KINETIS_PIT_SET(base, reg, mask) \
+	KINETIS_PIT_WR(base, reg, (KINETIS_PIT_RD(base, reg)) | (mask))
+#define KINETIS_PIT_RESET(base, reg, mask) \
+	KINETIS_PIT_WR(base, reg, (KINETIS_PIT_RD(base, reg)) & (~(mask)))
+
+struct kinetis_clock_event_ddata {
+	struct clock_event_device evtdev;
+	void __iomem *base;
+};
+
+/*
+ * Enable or disable a PIT channel
+ */
+static void kinetis_pit_enable(void __iomem *base, int enable)
+{
+	if (enable)
+		KINETIS_PIT_SET(base, tctrl, KINETIS_PIT_TCTRL_TEN_MSK);
+	else
+		KINETIS_PIT_RESET(base, tctrl, KINETIS_PIT_TCTRL_TEN_MSK);
+}
+
+/*
+ * Initialize a PIT channel, but do not enable it
+ */
+static void kinetis_pit_init(void __iomem *base, u32 ticks)
+{
+	/*
+	 * Enable the PIT module clock
+	 */
+	writel(0, KINETIS_PIT_MCR);
+
+	KINETIS_PIT_WR(base, tctrl, 0);
+	KINETIS_PIT_WR(base, tflg, KINETIS_PIT_TFLG_TIF_MSK);
+	KINETIS_PIT_WR(base, ldval, ticks);
+	KINETIS_PIT_WR(base, cval, 0);
+	KINETIS_PIT_WR(base, tctrl, KINETIS_PIT_TCTRL_TIE_MSK);
+}
+
+/*
+ * Clock event device set mode function
+ */
+static void kinetis_clockevent_tmr_set_mode(
+	enum clock_event_mode mode, struct clock_event_device *clk)
+{
+	struct kinetis_clock_event_ddata *pit =
+		container_of(clk, struct kinetis_clock_event_ddata, evtdev);
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		kinetis_pit_enable(pit->base, 1);
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	default:
+		kinetis_pit_enable(pit->base, 0);
+	}
+}
+
+/*
+ * Configure the timer to generate an interrupt in the specified amount of ticks
+ */
+static int kinetis_clockevent_tmr_set_next_event(
+	unsigned long delta, struct clock_event_device *c)
+{
+	struct kinetis_clock_event_ddata *pit =
+		container_of(c, struct kinetis_clock_event_ddata, evtdev);
+	unsigned long flags;
+
+	raw_local_irq_save(flags);
+	kinetis_pit_init(pit->base, delta);
+	kinetis_pit_enable(pit->base, 1);
+	raw_local_irq_restore(flags);
+
+	return 0;
+}
+
+static struct kinetis_clock_event_ddata
+		kinetis_clockevent_tmrs[KINETIS_PIT_CHANNELS] = {
+	{
+		.evtdev = {
+			.name		= "fsl,kinetis-pit-timer0",
+			.rating		= 200,
+			.features	=
+			    CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+			.set_mode	= kinetis_clockevent_tmr_set_mode,
+			.set_next_event	= kinetis_clockevent_tmr_set_next_event,
+		},
+	},
+	{
+		.evtdev = {
+			.name		= "fsl,kinetis-pit-timer1",
+		},
+	},
+	{
+		.evtdev = {
+			.name		= "fsl,kinetis-pit-timer2",
+		},
+	},
+	{
+		.evtdev = {
+			.name		= "fsl,kinetis-pit-timer3",
+		},
+	},
+};
+
+/*
+ * Timer IRQ handler
+ */
+static irqreturn_t kinetis_clockevent_tmr_irq_handler(int irq, void *dev_id)
+{
+	struct kinetis_clock_event_ddata *tmr = dev_id;
+
+	KINETIS_PIT_WR(tmr->base, tflg, KINETIS_PIT_TFLG_TIF_MSK);
+
+	tmr->evtdev.event_handler(&(tmr->evtdev));
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * System timer IRQ action
+ */
+static struct irqaction kinetis_clockevent_irqaction[KINETIS_PIT_CHANNELS] = {
+	{
+		.name = "Kinetis Kernel Time Tick (pit0)",
+		.flags = IRQF_TIMER | IRQF_IRQPOLL,
+		.dev_id = &kinetis_clockevent_tmrs[0],
+		.handler = kinetis_clockevent_tmr_irq_handler,
+	}, {
+		.name = "Kinetis Kernel Time Tick (pit1)",
+		.flags = IRQF_TIMER | IRQF_IRQPOLL,
+		.dev_id = &kinetis_clockevent_tmrs[1],
+		.handler = kinetis_clockevent_tmr_irq_handler,
+	}, {
+		.name = "Kinetis Kernel Time Tick (pit2)",
+		.flags = IRQF_TIMER | IRQF_IRQPOLL,
+		.dev_id = &kinetis_clockevent_tmrs[2],
+		.handler = kinetis_clockevent_tmr_irq_handler,
+	}, {
+		.name = "Kinetis Kernel Time Tick (pit3)",
+		.flags = IRQF_TIMER | IRQF_IRQPOLL,
+		.dev_id = &kinetis_clockevent_tmrs[3],
+		.handler = kinetis_clockevent_tmr_irq_handler,
+	},
+};
+
+static void __init kinetis_clockevent_init(struct device_node *np)
+{
+	const u64 max_delay_in_sec = 5;
+	struct clk *clk;
+	void __iomem *base;
+	unsigned long rate;
+	int irq, chan;
+
+	clk = of_clk_get(np, 0);
+	if (IS_ERR(clk)) {
+		pr_err("failed to get clock for clockevent\n");
+		return;
+	}
+
+	if (clk_prepare_enable(clk)) {
+		pr_err("failed to enable timer clock for clockevent\n");
+		goto err_clk_enable;
+	}
+
+	rate = clk_get_rate(clk);
+	if (!(rate / HZ)) {
+		pr_err("failed to get proper clock rate for clockevent\n");
+		goto err_clk_enable;
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_err("failed to get map registers for clockevent\n");
+		goto err_iomap;
+	}
+
+	irq = irq_of_parse_and_map(np, 0);
+	if (irq <= 0) {
+		pr_err("failed to get irq for clockevent\n");
+		goto err_get_irq;
+	}
+
+	chan = of_alias_get_id(np, "pit");
+	if ((chan < 0) || (chan >= KINETIS_PIT_CHANNELS)) {
+		pr_err("failed to calculate channel number for clockevent\n");
+		goto err_get_irq;
+	}
+	kinetis_clockevent_tmrs[chan].base = base;
+
+	/*
+	 * Set the fields required for the set_next_event method
+	 * (tickless kernel support)
+	 */
+	clockevents_calc_mult_shift(&(kinetis_clockevent_tmrs[chan].evtdev),
+					rate, max_delay_in_sec);
+	kinetis_clockevent_tmrs[chan].evtdev.max_delta_ns =
+					max_delay_in_sec * NSEC_PER_SEC;
+	kinetis_clockevent_tmrs[chan].evtdev.min_delta_ns =
+			clockevent_delta2ns(0xf,
+				&(kinetis_clockevent_tmrs[chan].evtdev));
+
+	clockevents_register_device(&(kinetis_clockevent_tmrs[chan].evtdev));
+
+	kinetis_pit_init(base, (rate / HZ) - 1);
+	kinetis_pit_enable(base, 1);
+
+	setup_irq(irq, &(kinetis_clockevent_irqaction[chan]));
+
+	return;
+
+err_get_irq:
+
+	iounmap(base);
+err_iomap:
+
+	clk_disable_unprepare(clk);
+err_clk_enable:
+
+	clk_put(clk);
+}
+
+CLOCKSOURCE_OF_DECLARE(kinetis_pit_timer, "fsl,kinetis-pit-timer",
+		       kinetis_clockevent_init);
diff --git a/include/dt-bindings/clock/kinetis-mcg.h b/include/dt-bindings/clock/kinetis-mcg.h
new file mode 100644
index 0000000..681732f
--- /dev/null
+++ b/include/dt-bindings/clock/kinetis-mcg.h
@@ -0,0 +1,10 @@
+#ifndef _DT_BINDINGS_CLOCK_KINETIS_MCG_H
+#define _DT_BINDINGS_CLOCK_KINETIS_MCG_H
+
+#define CLOCK_MCGOUTCLK		 0
+#define CLOCK_CCLK		 1
+#define CLOCK_PCLK		 2
+#define CLOCK_PIT		 3
+#define CLOCK_END		 4
+
+#endif /* _DT_BINDINGS_CLOCK_KINETIS_MCG_H */
-- 
2.3.6

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help