linux-gpio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Paul Osmialowski <pawelo-rhuoMcPwk82rDJvtcaxF/A@public.gmane.org>
To: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
Cc: Paul Osmialowski <pawelo-rhuoMcPwk82rDJvtcaxF/A@public.gmane.org>,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	Greg Kroah-Hartman
	<gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>,
	Ian Campbell
	<ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg@public.gmane.org>,
	Jiri Slaby <jslaby-AlSwsSmVLrQ@public.gmane.org>,
	Kumar Gala <galak-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>,
	Linus Walleij
	<linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>,
	Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>,
	Michael Turquette
	<mturquette-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>,
	Pawel Moll <pawel.moll-5wv7dgnIgG8@public.gmane.org>,
	Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
	Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>,
	Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>,
	Vinod Koul <vinod.koul-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-clk-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-serial-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	dmaengine-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Nicolas Pitre
	<nicolas.pitre-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>,
	Sergei Poselenov
	<sposelenov-qv+LCo8X3VpBDgjK7y7TUQ@public.gmane.org>,
	Paul Bolle <pebolle-IWqWACnzNjzz+pZb47iToQ@public.gmane.org>,
	Jingchang Lu
	<jingchang.lu-KZfg59tc24xl57MIdRCFDg@public.gmane.org>,
	Yuri
Subject: Re: [PATCH v2 3/9] arm: twr-k70f120m: clock driver for Kinetis SoC
Date: Thu, 2 Jul 2015 12:08:39 +0200 (CEST)	[thread overview]
Message-ID: <alpine.LNX.2.00.1507021206410.13210@localhost.localdomain> (raw)
In-Reply-To: <alpine.LNX.2.00.1507011756140.14440-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>

[-- Attachment #1: Type: TEXT/PLAIN, Size: 2984 bytes --]

Nah, I've found this code hard to maintain. I'm attaching simplified 
version.

Thanks,
Paul

On Wed, 1 Jul 2015, Paul Osmialowski wrote:

> Hi Arnd,
>
> Can you look at attached candidate for the third iteration? Is it any better 
> now?
>
> Thanks,
> Paul
>
> On Tue, 30 Jun 2015, Arnd Bergmann wrote:
>
>>  On Tuesday 30 June 2015 14:27:24 Paul Osmialowski wrote:
>> >  Based on K70P256M150SF3RM.pdf K70 Sub-Family Reference Manual, Rev. 3.
>> > 
>> >  Signed-off-by: Paul Osmialowski <pawelo-rhuoMcPwk82rDJvtcaxF/A@public.gmane.org>
>> >  ---
>> >   .../devicetree/bindings/clock/kinetis-clock.txt    |  63 +++
>> >   arch/arm/boot/dts/kinetis.dtsi                     |  36 ++
>> >   drivers/clk/Makefile                               |   1 +
>> >   drivers/clk/clk-kinetis.c                          | 463 
>> >   +++++++++++++++++++++
>> >   4 files changed, 563 insertions(+)
>> >   create mode 100644 
>> >   Documentation/devicetree/bindings/clock/kinetis-clock.txt
>> >   create mode 100644 drivers/clk/clk-kinetis.c
>> > 
>> >  diff --git a/Documentation/devicetree/bindings/clock/kinetis-clock.txt 
>> >  b/Documentation/devicetree/bindings/clock/kinetis-clock.txt
>> >  new file mode 100644
>> >  index 0000000..63af6a5
>> >  --- /dev/null
>> >  +++ b/Documentation/devicetree/bindings/clock/kinetis-clock.txt
>> >  @@ -0,0 +1,63 @@
>> >  +* Clock bindings for Freescale Kinetis SoC
>> >  +
>> >  +Required properties:
>> >  +- compatible: Should be "fsl,kinetis-cmu".
>> >  +- reg: Two address ranges, one for the Clock Genetator register set,
>> >  +	one for System Integration Module register set.
>> >  +- Set of clock devices: one fixed-rate-root, fixed-rate clocks and 
>> >  clock-gates.
>> >  +
>> >  +For clock-gate addresses see K70 Sub-Family Reference Manual, Rev. 3 
>> >  pg. 341
>> >  +and later. Notice that addresses are zero-based, so SIM_SCGC1 has 
>> >  address 0,
>> >  +SIM_SCGC2 has address 1 and so on. The second address component is the 
>> >  bit
>> >  +index.
>>
>>  Please document the sub-nodes that are allowed, and the format
>>  of the clock specifiers.
>> 
>> >  +
>> >  +Example:
>> >  +
>> >  +cmu@40064000 {
>> >  +	compatible = "fsl,kinetis-cmu";
>> >  +	reg = <0x40064000 0x14>, <0x40047000 0x1100>;
>> >  +
>> >  +	mcg_outclk: fixed-rate-root@mcgout {
>> >  +		device_type = "mcgout";
>> >  +		#clock-cells = <0>;
>> >  +	};
>> >  +
>> >  +	mcg_cclk: fixed-rate@cclk {
>>
>>  '@' is a reserved character here that is used before the address
>>  of the device, so this has to be a hexadecimal number without leading
>>  '0x', and it should match the 'reg' property of the device.
>> 
>> >  +		device_type = "cclk";
>> >  +		#clock-cells = <0>;
>> >  +		clocks = <&mcg_outclk>;
>> >  +	};
>>
>>  The device_type property here is not a standard identifier,
>>  and you don't list it as an optional or mandatory property.
>>
>>  Please remove it and instead use the compatible property, the
>>  name or the address.
>>
>>   Arnd
>> 
>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: TEXT/x-diff; name=0003-arm-twr-k70f120m-clock-driver-for-Kinetis-SoC.patch, Size: 17977 bytes --]

From ae43dcd17eec3eb2c3ad4d7cd514295d935655fe Mon Sep 17 00:00:00 2001
From: Paul Osmialowski <pawelo@king.net.pl>
Date: Mon, 29 Jun 2015 20:58:55 +0200
Subject: [PATCH 3/9] arm: twr-k70f120m: clock driver for Kinetis SoC

Based on K70P256M150SF3RM.pdf K70 Sub-Family Reference Manual, Rev. 3.

Signed-off-by: Paul Osmialowski <pawelo@king.net.pl>
---
 .../devicetree/bindings/clock/kinetis-clock.txt    |  65 +++
 arch/arm/boot/dts/kinetis.dtsi                     |  29 ++
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk-kinetis.c                          | 475 +++++++++++++++++++++
 4 files changed, 570 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/kinetis-clock.txt
 create mode 100644 drivers/clk/clk-kinetis.c

diff --git a/Documentation/devicetree/bindings/clock/kinetis-clock.txt b/Documentation/devicetree/bindings/clock/kinetis-clock.txt
new file mode 100644
index 0000000..e6c1cfa
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/kinetis-clock.txt
@@ -0,0 +1,65 @@
+* Clock bindings for Freescale Kinetis SoC
+
+Required properties:
+- compatible: Should be "fsl,kinetis-cmu".
+- reg: Two address ranges, one for the Multipurpose Clock Genetator (MCG)
+	register set, one for System Integration Module (SIM) register set.
+- Set of clock devices (obligatory):
+	- fixed-rate-mcgout
+		Required properties:
+		- #clock-cells: must be <0>.
+	- fixed-rate-cclk
+		Required properties:
+		- #clock-cells: must be <0>.
+	- fixed-rate-pclk
+		Required properties:
+		- #clock-cells: must be <0>.
+	- cclk-gate
+		Required properties:
+		- #clock-cells: must be <2> (see below).
+	- pclk-gate
+		Required properties:
+		- #clock-cells: must be <2> (see below).
+
+For clock gate addresses see K70 Sub-Family Reference Manual, Rev. 3 pg. 341
+and later. Notice that addresses are zero-based, so SIM_SCGC1 has address 0,
+SIM_SCGC2 has address 1 and so on. The second address component is the bit
+index.
+
+Example:
+
+cmu@40064000 {
+	compatible = "fsl,kinetis-cmu";
+	reg = <0x40064000 0x14>, <0x40047000 0x1100>;
+
+	mcg_outclk: fixed-rate-mcgout {
+		#clock-cells = <0>;
+	};
+
+	mcg_cclk: fixed-rate-cclk {
+		#clock-cells = <0>;
+	};
+
+	mcg_pclk: fixed-rate-pclk {
+		#clock-cells = <0>;
+	};
+
+	mcg_cclk_gate: cclk-gate {
+		#clock-cells = <2>;
+	};
+
+	mcg_pclk_gate: pclk-gate {
+		#clock-cells = <2>;
+	};
+};
+
+uart1: serial@4006b000 {
+	compatible = "fsl,kinetis-lpuart";
+	reg = <0x4006b000 0x1000>;
+	interrupts = <47>, <48>;
+	interrupt-names = "uart-stat", "uart-err";
+	clocks = <&mcg_cclk_gate 3 11>;
+	clock-names = "ipg";
+	dmas = <&edma 0 4>;
+	dma-names = "rx";
+};
diff --git a/arch/arm/boot/dts/kinetis.dtsi b/arch/arm/boot/dts/kinetis.dtsi
index 93d2a8a..42a11c7 100644
--- a/arch/arm/boot/dts/kinetis.dtsi
+++ b/arch/arm/boot/dts/kinetis.dtsi
@@ -3,3 +3,32 @@
  *
  */
 #include "armv7-m.dtsi"
+
+/ {
+	soc {
+		cmu@40064000 {
+			compatible = "fsl,kinetis-cmu";
+			reg = <0x40064000 0x14>, <0x40047000 0x1100>;
+
+			mcg_outclk: fixed-rate-mcgout {
+				#clock-cells = <0>;
+			};
+
+			mcg_cclk: fixed-rate-cclk {
+				#clock-cells = <0>;
+			};
+
+			mcg_pclk: fixed-rate-pclk {
+				#clock-cells = <0>;
+			};
+
+			mcg_cclk_gate: cclk-gate {
+				#clock-cells = <2>;
+			};
+
+			mcg_pclk_gate: pclk-gate {
+				#clock-cells = <2>;
+			};
+		};
+	};
+};
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 63418cf..412d76b 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..a6e8a28
--- /dev/null
+++ b/drivers/clk/clk-kinetis.c
@@ -0,0 +1,475 @@
+/*
+ * 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/io.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+
+enum kinetis_clk_ids {
+	KINETIS_CLK_MCGOUT = 0,
+	KINETIS_CLK_CCLK,
+	KINETIS_CLK_PCLK,
+	KINETIS_CLK_CCLK_GATE,
+	KINETIS_CLK_PCLK_GATE,
+	KINETIS_CLK_NUM
+};
+
+static const struct {
+	enum kinetis_clk_ids id;
+	const char *name;
+} kinetis_clks[KINETIS_CLK_NUM] = {
+	{ .id = KINETIS_CLK_MCGOUT, .name = "fixed-rate-mcgout", },
+	{ .id = KINETIS_CLK_CCLK, .name = "fixed-rate-cclk", },
+	{ .id = KINETIS_CLK_PCLK, .name = "fixed-rate-pclk", },
+	{ .id = KINETIS_CLK_CCLK_GATE, .name = "cclk-gate", },
+	{ .id = KINETIS_CLK_PCLK_GATE, .name = "pclk-gate", },
+};
+
+/*
+ * 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 */
+
+#define KINETIS_SIM_CG_NUMREGS	7
+
+/*
+ * System Integration Module (SIM) register map
+ *
+ * This map actually covers two hardware modules:
+ *     1. SIM low-power logic, at 0x40047000
+ *     2. System integration module (SIM), at 0x40048000
+ */
+struct kinetis_sim_regs {
+	u32 sopt1;	/* System Options Register 1 */
+	u32 rsv0[1024];
+	u32 sopt2;	/* System Options Register 2 */
+	u32 rsv1;
+	u32 sopt4;	/* System Options Register 4 */
+	u32 sopt5;	/* System Options Register 5 */
+	u32 sopt6;	/* System Options Register 6 */
+	u32 sopt7;	/* System Options Register 7 */
+	u32 rsv2[2];
+	u32 sdid;	/* System Device Identification Register */
+	u32 scgc[KINETIS_SIM_CG_NUMREGS];	/* Clock Gating Regs 1...7 */
+	u32 clkdiv1;	/* System Clock Divider Register 1 */
+	u32 clkdiv2;	/* System Clock Divider Register 2 */
+	u32 fcfg1;	/* Flash Configuration Register 1 */
+	u32 fcfg2;	/* Flash Configuration Register 2 */
+	u32 uidh;	/* Unique Identification Register High */
+	u32 uidmh;	/* Unique Identification Register Mid-High */
+	u32 uidml;	/* Unique Identification Register Mid Low */
+	u32 uidl;	/* Unique Identification Register Low */
+	u32 clkdiv3;	/* System Clock Divider Register 3 */
+	u32 clkdiv4;	/* System Clock Divider Register 4 */
+	u32 mcr;	/* Misc Control Register */
+};
+#define KINETIS_SIM_PTR(base, reg) \
+	(&(((struct kinetis_sim_regs *)(base))->reg))
+
+/*
+ * System Clock Divider Register 1
+ */
+/* Clock 1 output divider value (for the core/system clock) */
+#define KINETIS_SIM_CLKDIV1_OUTDIV1_BITS	28
+#define KINETIS_SIM_CLKDIV1_OUTDIV1_MSK \
+	(((1 << 4) - 1) << KINETIS_SIM_CLKDIV1_OUTDIV1_BITS)
+/* Clock 2 output divider value (for the peripheral clock) */
+#define KINETIS_SIM_CLKDIV1_OUTDIV2_BITS	24
+#define KINETIS_SIM_CLKDIV1_OUTDIV2_MSK \
+	(((1 << 4) - 1) << KINETIS_SIM_CLKDIV1_OUTDIV2_BITS)
+
+/*
+ * System Clock Divider Register 2
+ */
+/* USB HS clock divider fraction */
+#define KINETIS_SIM_CLKDIV2_USBHSFRAC_BIT	8
+#define KINETIS_SIM_CLKDIV2_USBHSFRAC_MSK \
+	(1 << KINETIS_SIM_CLKDIV2_USBHSFRAC_BIT)
+/* USB HS clock divider divisor */
+#define KINETIS_SIM_CLKDIV2_USBHSDIV_BIT	9
+#define KINETIS_SIM_CLKDIV2_USBHSDIV_MSK \
+	(7 << KINETIS_SIM_CLKDIV2_USBHSDIV_BIT)
+
+/*
+ * 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))
+
+struct kinetis_clk_gate {
+	const char *clk_gate_name;
+	struct clk *clk;
+	u32 reg;
+	u32 idx;
+	struct list_head clk_list;
+};
+
+struct kinetis_clk_gate_data {
+	const char *clk_parent_name;
+	void __iomem *sim;
+	struct list_head clk_gate_list;
+};
+
+static struct clk *kinetis_find_clk_gate(
+		struct kinetis_clk_gate_data *clk_gate_data, u32 reg, u32 idx)
+{
+	struct kinetis_clk_gate *gate;
+
+	list_for_each_entry(gate, &clk_gate_data->clk_gate_list, clk_list)
+		if ((gate->reg == reg) && (gate->idx == idx))
+			return gate->clk;
+
+	return NULL;
+}
+
+static struct clk *kinetis_clk_gate_get(struct of_phandle_args *clkspec,
+					void *data)
+{
+	struct kinetis_clk_gate_data *clk_gate_data = data;
+	struct kinetis_clk_gate *gate;
+	u32 reg = clkspec->args[0];
+	u32 idx = clkspec->args[1];
+	struct clk *clk;
+
+	clk = kinetis_find_clk_gate(clk_gate_data, reg, idx);
+	if (clk)
+		return clk;
+
+	gate = kzalloc(sizeof(struct kinetis_clk_gate), GFP_KERNEL);
+	if (!gate)
+		return ERR_PTR(-ENOMEM);
+	gate->clk_gate_name = kasprintf(GFP_KERNEL, "%s:%u:%u",
+				clkspec->np->full_name, reg, idx);
+
+	clk = clk_register_gate(NULL, gate->clk_gate_name,
+				clk_gate_data->clk_parent_name, 0,
+				KINETIS_SIM_PTR(clk_gate_data->sim, scgc[reg]),
+				idx, 0, NULL);
+	if (IS_ERR(clk)) {
+		pr_err("Cannot register gate to clock %s\n",
+				clk_gate_data->clk_parent_name);
+		kfree_const(gate->clk_gate_name);
+		kfree(gate);
+		return clk;
+	}
+
+	gate->clk = clk;
+	gate->reg = reg;
+	gate->idx = idx;
+
+	list_add(&gate->clk_list, &clk_gate_data->clk_gate_list);
+
+	return clk;
+}
+
+static int kinetis_of_register_fixed_rate_root(struct device_node *np,
+							u32 clock_val)
+{
+	struct clk *clk;
+	int ret;
+
+	clk = clk_register_fixed_rate(NULL, np->full_name, NULL, CLK_IS_ROOT,
+								    clock_val);
+	if (IS_ERR(clk)) {
+		pr_err("Could not register clock %s\n", np->full_name);
+		return PTR_ERR(clk);
+	}
+
+	ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+	if (ret < 0) {
+		pr_err("Could not add clock provider %s\n", np->full_name);
+		clk_unregister(clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int kinetis_of_register_fixed_rate(struct device_node *np,
+					struct device_node *parent_clk,
+					u32 clock_val)
+{
+	struct clk *clk;
+	int ret;
+
+	clk = clk_register_fixed_rate(NULL, np->full_name,
+					parent_clk->full_name, 0, clock_val);
+	if (IS_ERR(clk)) {
+		pr_err("Could not register clock %s\n", np->full_name);
+		return PTR_ERR(clk);
+	}
+
+	ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+	if (ret < 0) {
+		pr_err("Could not add clock provider %s\n", np->full_name);
+		clk_unregister(clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int kinetis_of_register_clk_gate(struct device_node *np,
+					struct device_node *parent_clk,
+					void __iomem *sim)
+{
+	struct kinetis_clk_gate_data *clk_gate;
+	int ret;
+
+	clk_gate = kzalloc(sizeof(struct kinetis_clk_gate_data), GFP_KERNEL);
+	if (!clk_gate)
+		return -ENOMEM;
+
+	clk_gate->clk_parent_name = parent_clk->full_name;
+	clk_gate->sim = sim;
+	INIT_LIST_HEAD(&clk_gate->clk_gate_list);
+
+	ret = of_clk_add_provider(np, kinetis_clk_gate_get, clk_gate);
+	if (ret < 0) {
+		pr_err("Could not add clock provider %s\n", np->full_name);
+		kfree(clk_gate);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __init kinetis_mcg_init(struct device_node *np)
+{
+	const int vco_div = 2;
+	const int vdiv_min = 16;
+	u32 clock_val_mcgout;
+	u32 clock_val_cclk;
+	u32 clock_val_pclk;
+	void __iomem *base;
+	void __iomem *sim;
+	int pll_sel;
+	int osc_sel;
+	unsigned long mcgout;
+	struct device_node *child;
+	struct device_node *clk_nodes[KINETIS_CLK_NUM];
+	int i;
+
+	for (i = 0; i < KINETIS_CLK_NUM; i++)
+		clk_nodes[i] = NULL;
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_err("Failed to map address range for kinetis,mcg node\n");
+		return;
+	}
+
+	sim = of_iomap(np, 1);
+	if (!sim) {
+		pr_err("Failed to map address range for kinetis SIM module\n");
+		iounmap(base);
+		return;
+	}
+
+	/*
+	 * Check whether PLL0 or PLL1 is used for MCGOUTCLK
+	 */
+	pll_sel = !!(ioread8(KINETIS_MCG_PTR(base, c11)) &
+				KINETIS_MCG_C11_PLLCS_MSK);
+
+	/*
+	 * Check whether OSC0 or OSC1 is used to source the main PLL
+	 */
+	if (pll_sel)
+		osc_sel = !!(ioread8(KINETIS_MCG_PTR(base, c11)) &
+				KINETIS_MCG_C11_PLLREFSEL1_MSK);
+	else
+		osc_sel = !!(ioread8(KINETIS_MCG_PTR(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 /= ((ioread8(KINETIS_MCG_PTR(base, c11)) &
+		  KINETIS_MCG_C11_PRDIV_MSK) >> KINETIS_MCG_C11_PRDIV_BITS) + 1;
+
+		/*
+		 * PLL1 multiplication factor (VDIV)
+		 */
+		mcgout *= ((ioread8(KINETIS_MCG_PTR(base, c12)) &
+		  KINETIS_MCG_C12_VDIV1_MSK) >> KINETIS_MCG_C12_VDIV1_BITS) +
+								    vdiv_min;
+	} else {
+		/*
+		 * PLL0 internal divider (PRDIV)
+		 */
+		mcgout /= ((ioread8(KINETIS_MCG_PTR(base, c5)) &
+			KINETIS_MCG_C5_PRDIV_MSK) >>
+			KINETIS_MCG_C5_PRDIV_BITS) + 1;
+
+		/*
+		 * PLL0 multiplication factor (VDIV)
+		 */
+		mcgout *= ((ioread8(KINETIS_MCG_PTR(base, c6)) &
+			KINETIS_MCG_C6_VDIV_MSK) >>
+			KINETIS_MCG_C6_VDIV_BITS) + vdiv_min;
+	}
+
+	/*
+	 * Apply the PLL output divider
+	 */
+	mcgout /= vco_div;
+
+	clock_val_mcgout = mcgout;
+
+	clock_val_cclk = mcgout /
+		(((ioread32(KINETIS_SIM_PTR(sim, clkdiv1)) &
+			KINETIS_SIM_CLKDIV1_OUTDIV1_MSK) >>
+				KINETIS_SIM_CLKDIV1_OUTDIV1_BITS) + 1);
+
+	/*
+	 * Peripheral (bus) clock
+	 */
+	clock_val_pclk = mcgout /
+		(((ioread32(KINETIS_SIM_PTR(sim, clkdiv1)) &
+			KINETIS_SIM_CLKDIV1_OUTDIV2_MSK) >>
+				KINETIS_SIM_CLKDIV1_OUTDIV2_BITS) + 1);
+
+	for_each_child_of_node(np, child) {
+		if (!of_device_is_available(child))
+			continue;
+
+		for (i = 0; i < KINETIS_CLK_NUM; i++) {
+			if (!of_node_cmp(child->name, kinetis_clks[i].name)) {
+				if (clk_nodes[kinetis_clks[i].id]) {
+					pr_err("more than one %s specified\n",
+								child->name);
+					goto fail;
+				} else
+					clk_nodes[kinetis_clks[i].id] = child;
+			}
+		}
+	}
+
+	for (i = 0; i < KINETIS_CLK_NUM; i++) {
+		if (!(clk_nodes[kinetis_clks[i].id])) {
+			pr_err("One of obligatory clocks NOT specified\n");
+			goto fail;
+		}
+	}
+
+	if (kinetis_of_register_fixed_rate_root(clk_nodes[KINETIS_CLK_MCGOUT],
+						clock_val_mcgout))
+		goto fail;
+
+	if (!(kinetis_of_register_fixed_rate(clk_nodes[KINETIS_CLK_CCLK],
+				clk_nodes[KINETIS_CLK_MCGOUT], clock_val_cclk)))
+		kinetis_of_register_clk_gate(clk_nodes[KINETIS_CLK_CCLK_GATE],
+				clk_nodes[KINETIS_CLK_CCLK], sim);
+
+	if (!(kinetis_of_register_fixed_rate(clk_nodes[KINETIS_CLK_PCLK],
+				clk_nodes[KINETIS_CLK_MCGOUT], clock_val_pclk)))
+		kinetis_of_register_clk_gate(clk_nodes[KINETIS_CLK_PCLK_GATE],
+				clk_nodes[KINETIS_CLK_PCLK], sim);
+
+	return;
+
+fail:
+
+	iounmap(sim);
+	iounmap(base);
+}
+
+CLK_OF_DECLARE(kinetis_mcg, "fsl,kinetis-cmu", kinetis_mcg_init);
-- 
2.3.6


  parent reply	other threads:[~2015-07-02 10:08 UTC|newest]

Thread overview: 45+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-30 12:27 [PATCH v2 0/9] [New BSP] Add initial support for Freescale Kinetis TWR-K70F120M development kit Paul Osmialowski
2015-06-30 12:27 ` [PATCH v2 2/9] arm: twr-k70f120m: basic support for Kinetis TWR-K70F120M Paul Osmialowski
2015-06-30 12:27 ` [PATCH v2 3/9] arm: twr-k70f120m: clock driver for Kinetis SoC Paul Osmialowski
2015-06-30 20:36   ` Arnd Bergmann
2015-07-01 15:57     ` Paul Osmialowski
     [not found]       ` <alpine.LNX.2.00.1507011756140.14440-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2015-07-02 10:08         ` Paul Osmialowski [this message]
2015-07-02 12:40           ` Arnd Bergmann
2015-07-02 21:42             ` Paul Osmialowski
2015-07-02 22:08               ` Thomas Gleixner
2015-07-03 17:40                 ` Paul Osmialowski
2015-07-04 19:54                 ` Arnd Bergmann
2015-07-04 21:50                   ` Paul Osmialowski
2015-07-06 20:57                     ` Paul Osmialowski
2015-07-24  3:42                     ` Michael Turquette
2015-07-26 20:24                       ` Paul Osmialowski
2015-07-28 16:03                         ` Michael Turquette
2015-07-28 20:30                           ` Paul Osmialowski
2015-07-29 23:05                             ` Michael Turquette
2015-07-30 21:40                               ` Paul Osmialowski
2015-08-01  0:58                                 ` Michael Turquette
2015-08-01 15:27                                   ` Paul Osmialowski
2015-08-05 19:27                                     ` Michael Turquette
2015-07-14  9:03   ` Linus Walleij
2015-07-15  7:31     ` Paul Osmialowski
2015-07-15 17:34       ` Paul Osmialowski
     [not found] ` <1435667250-28299-1-git-send-email-pawelo-rhuoMcPwk82rDJvtcaxF/A@public.gmane.org>
2015-06-30 12:27   ` [PATCH v2 1/9] arm: allow copying of vector table to internal SRAM memory Paul Osmialowski
2015-06-30 12:27   ` [PATCH v2 4/9] arm: twr-k70f120m: timer driver for Kinetis SoC Paul Osmialowski
     [not found]     ` <1435667250-28299-5-git-send-email-pawelo-rhuoMcPwk82rDJvtcaxF/A@public.gmane.org>
2015-06-30 20:43       ` Arnd Bergmann
2015-07-01 11:44         ` Paul Osmialowski
2015-07-05 14:39         ` Rob Herring
2015-07-01  7:51     ` Thomas Gleixner
2015-07-01  8:42       ` Paul Osmialowski
2015-07-01 13:28         ` Thomas Gleixner
2015-07-01 14:20           ` Paul Osmialowski
2015-07-14  8:59             ` Linus Walleij
2015-06-30 12:27   ` [PATCH v2 9/9] arm: twr-k70f120m: use Freescale lpuart driver with " Paul Osmialowski
2015-06-30 12:27 ` [PATCH v2 5/9] arm: twr-k70f120m: IOMUX driver for " Paul Osmialowski
2015-07-14  8:55   ` Linus Walleij
2015-06-30 12:27 ` [PATCH v2 6/9] arm: twr-k70f120m: extend Freescale eDMA driver with the ability to support " Paul Osmialowski
2015-07-05  6:45   ` Vinod Koul
2015-07-05  9:45     ` Paul Osmialowski
2015-06-30 12:27 ` [PATCH v2 7/9] arm: twr-k70f120m: use Freescale eDMA driver with " Paul Osmialowski
2015-06-30 20:49   ` Arnd Bergmann
2015-07-01  6:54     ` Paul Osmialowski
2015-06-30 12:27 ` [PATCH v2 8/9] arm: twr-k70f120m: extend Freescale lpuart driver with ability to support " Paul Osmialowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=alpine.LNX.2.00.1507021206410.13210@localhost.localdomain \
    --to=pawelo-rhuomcpwk82rdjvtcaxf/a@public.gmane.org \
    --cc=arnd-r2nGTMty4D4@public.gmane.org \
    --cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=dmaengine-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=galak-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org \
    --cc=gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org \
    --cc=ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg@public.gmane.org \
    --cc=jingchang.lu-KZfg59tc24xl57MIdRCFDg@public.gmane.org \
    --cc=jslaby-AlSwsSmVLrQ@public.gmane.org \
    --cc=linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
    --cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=linux-clk-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org \
    --cc=linux-serial-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=mark.rutland-5wv7dgnIgG8@public.gmane.org \
    --cc=mturquette-rdvid1DuHRBWk0Htik3J/w@public.gmane.org \
    --cc=nicolas.pitre-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
    --cc=pawel.moll-5wv7dgnIgG8@public.gmane.org \
    --cc=pebolle-IWqWACnzNjzz+pZb47iToQ@public.gmane.org \
    --cc=robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
    --cc=sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org \
    --cc=sposelenov-qv+LCo8X3VpBDgjK7y7TUQ@public.gmane.org \
    --cc=vinod.koul-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).