lkml.org 
[lkml]   [2015]   [Jun]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 6/9] arm: twr-k70f120m: clock source drivers for Kinetis SoC
    Date
    Based on K70P256M150SF3RM.pdf

    Signed-off-by: Paul Osmialowski <pawelo@king.net.pl>
    ---
    .../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@emcraft.com>
    + *
    + * 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@emcraft.com>
    + *
    + * Copyright (C) 2015 Paul Osmialowski <pawelo@king.net.pl>
    + *
    + * 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@emcraft.com>
    + *
    + * Copyright (C) 2015 Paul Osmialowski <pawelo@king.net.pl>
    + *
    + * 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


    \
     
     \ /
      Last update: 2015-06-24 00:21    [W:6.839 / U:0.572 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site