From patchwork Wed Jul 1 15:57:30 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Osmialowski X-Patchwork-Id: 490226 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 6AE4B1402A6 for ; Thu, 2 Jul 2015 02:02:26 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755058AbbGAQCY (ORCPT ); Wed, 1 Jul 2015 12:02:24 -0400 Received: from fish.king.net.pl ([79.190.246.46]:58098 "EHLO king.net.pl" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1754970AbbGAQBq (ORCPT ); Wed, 1 Jul 2015 12:01:46 -0400 Received: from localhost.localdomain (fish [127.0.0.1]) by king.net.pl (8.14.9/8.14.0) with ESMTP id t61Fvd9F014463 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Wed, 1 Jul 2015 17:57:39 +0200 Received: from localhost (newchief@localhost) by localhost.localdomain (8.14.9/8.14.9/Submit) with ESMTP id t61FvUE0014459; Wed, 1 Jul 2015 17:57:31 +0200 X-Authentication-Warning: localhost.localdomain: newchief owned process doing -bs Date: Wed, 1 Jul 2015 17:57:30 +0200 (CEST) From: Paul Osmialowski X-X-Sender: newchief@localhost.localdomain To: Arnd Bergmann cc: linux-arm-kernel@lists.infradead.org, Paul Osmialowski , Greg Kroah-Hartman , Ian Campbell , Jiri Slaby , Kumar Gala , Linus Walleij , Mark Rutland , Michael Turquette , Pawel Moll , Rob Herring , Russell King , Stephen Boyd , Vinod Koul , linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, devicetree@vger.kernel.org, dmaengine@vger.kernel.org, Nicolas Pitre , Sergei Poselenov , Paul Bolle , Jingchang Lu , Yuri Tikhonov , Rob Herring , Geert Uytterhoeven , Uwe Kleine-Koenig , Alexander Potashev , Frank Li , Thomas Gleixner , Anson Huang Subject: Re: [PATCH v2 3/9] arm: twr-k70f120m: clock driver for Kinetis SoC In-Reply-To: <28154735.0HeJqZBqop@wuerfel> Message-ID: References: <1435667250-28299-1-git-send-email-pawelo@king.net.pl> <1435667250-28299-4-git-send-email-pawelo@king.net.pl> <28154735.0HeJqZBqop@wuerfel> User-Agent: Alpine 2.00 (LNX 1167 2008-08-23) MIME-Version: 1.0 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org 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 >> --- >> .../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 > From 22e89bdbea639d8d4daba548927c661f39ff4551 Mon Sep 17 00:00:00 2001 From: Paul Osmialowski 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 --- .../devicetree/bindings/clock/kinetis-clock.txt | 65 +++ arch/arm/boot/dts/kinetis.dtsi | 29 ++ drivers/clk/Makefile | 1 + drivers/clk/clk-kinetis.c | 486 +++++++++++++++++++++ 4 files changed, 581 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 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..d8b5e7d --- /dev/null +++ b/drivers/clk/clk-kinetis.c @@ -0,0 +1,486 @@ +/* + * clk-kinetis.c - Clock driver for Kinetis K70 MCG + * + * Based on legacy pre-OF code by Alexander Potashev + * + * Copyright (C) 2015 Paul Osmialowski + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +enum { + KINETIS_CLK_MCGOUT = 0, + KINETIS_CLK_CCLK, + KINETIS_CLK_PCLK, + KINETIS_CLK_CCLK_GATE, + KINETIS_CLK_PCLK_GATE, + KINETIS_CLK_NUM +}; + +/* + * 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; + bool ok; + + 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; + + ok = true; + + if (!of_node_cmp(child->name, "fixed-rate-mcgout")) { + if (clk_nodes[KINETIS_CLK_MCGOUT]) + ok = false; + else + clk_nodes[KINETIS_CLK_MCGOUT] = child; + } else if (!of_node_cmp(child->name, "fixed-rate-cclk")) { + if (clk_nodes[KINETIS_CLK_CCLK]) + ok = false; + else + clk_nodes[KINETIS_CLK_CCLK] = child; + } else if (!of_node_cmp(child->name, "fixed-rate-pclk")) { + if (clk_nodes[KINETIS_CLK_PCLK]) + ok = false; + else + clk_nodes[KINETIS_CLK_PCLK] = child; + } else if (!of_node_cmp(child->name, "cclk-gate")) { + if (clk_nodes[KINETIS_CLK_CCLK_GATE]) + ok = false; + else + clk_nodes[KINETIS_CLK_CCLK_GATE] = child; + } else if (!of_node_cmp(child->name, "pclk-gate")) { + if (clk_nodes[KINETIS_CLK_PCLK_GATE]) + ok = false; + else + clk_nodes[KINETIS_CLK_PCLK_GATE] = child; + } else { + pr_err("unknown clock %s specified\n", child->name); + return; + } + + if (!ok) { + pr_err("more than one %s specified\n", child->name); + return; + } + } + + for (i = 0; i < KINETIS_CLK_NUM; i++) { + if (!(clk_nodes[i])) { + pr_err("One of: fixed-rate-mcgout, fixed-rate-cclk, " + "fixed-rate-pclk, cclk-gate, pclk-gate " + "NOT specified\n"); + return; + } + } + + if (kinetis_of_register_fixed_rate_root(clk_nodes[KINETIS_CLK_MCGOUT], + clock_val_mcgout)) + return; + + 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); +} + +CLK_OF_DECLARE(kinetis_mcg, "fsl,kinetis-cmu", kinetis_mcg_init); -- 2.3.6