From patchwork Wed Sep 12 14:29:21 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gregory CLEMENT X-Patchwork-Id: 1444321 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (unknown [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id C1E2B4025E for ; Wed, 12 Sep 2012 14:52:55 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TBnxA-0002d4-QQ; Wed, 12 Sep 2012 14:30:08 +0000 Received: from mail.free-electrons.com ([88.190.12.23]) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TBnx1-0002cW-Nr for linux-arm-kernel@lists.infradead.org; Wed, 12 Sep 2012 14:30:03 +0000 Received: by mail.free-electrons.com (Postfix, from userid 106) id 9B641192; Wed, 12 Sep 2012 16:30:17 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-3.1 required=5.0 tests=ALL_TRUSTED,AWL,BAYES_00 shortcircuit=no autolearn=ham version=3.3.1 Received: from localhost (tra42-5-83-152-246-54.fbx.proxad.net [83.152.246.54]) by mail.free-electrons.com (Postfix) with ESMTPSA id 761C2166; Wed, 12 Sep 2012 16:30:03 +0200 (CEST) From: Gregory CLEMENT To: Mike Turquette , Jason Cooper , Andrew Lunn , Gregory Clement Subject: [PATCH 1/3] clk: mvebu: add armada-370-xp specific clocks Date: Wed, 12 Sep 2012 16:29:21 +0200 Message-Id: <1347460163-11946-2-git-send-email-gregory.clement@free-electrons.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1347460163-11946-1-git-send-email-gregory.clement@free-electrons.com> References: <1347460163-11946-1-git-send-email-gregory.clement@free-electrons.com> X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.4 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Lior Amsalem , Ike Pan , Nadav Haklai , Ian Molton , David Marlin , Yehuda Yitschak , Jani Monoses , Tawfik Bayouk , Dan Frazier , Eran Ben-Avi , Li Li , Leif Lindholm , Sebastian Hesselbarth , Arnd Bergmann , Jon Masters , devicetree-discuss@lists.ozlabs.org, Ben Dooks , linux-arm-kernel@lists.infradead.org, Thomas Petazzoni , Chris Van Hoof , Nicolas Pitre , Maen Suleiman , Shadi Ammouri , Olof Johansson X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Add Armada 370/XP specific clocks: core clocks and CPU clocks. The CPU clocks are only for Armada XP which can be SMP. The core clocks are clocks which have their rate set during reset. The code was written with the other SoCs of the mvebu family in mind. Adding them should be pretty straight forward once the binding to get the tclk frequency, the pclk frequency and the ratio with the children clocks will be added. Signed-off-by: Gregory CLEMENT --- .../devicetree/bindings/clock/mvebu-core-clock.txt | 41 +++ .../devicetree/bindings/clock/mvebu-cpu-clock.txt | 21 ++ drivers/clk/Makefile | 1 + drivers/clk/mvebu/Makefile | 2 + drivers/clk/mvebu/clk-core.c | 312 ++++++++++++++++++++ drivers/clk/mvebu/clk-core.h | 19 ++ drivers/clk/mvebu/clk-cpu.c | 144 +++++++++ drivers/clk/mvebu/clk-cpu.h | 19 ++ drivers/clk/mvebu/clk.c | 36 +++ 9 files changed, 595 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mvebu-core-clock.txt create mode 100644 Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt create mode 100644 drivers/clk/mvebu/Makefile create mode 100644 drivers/clk/mvebu/clk-core.c create mode 100644 drivers/clk/mvebu/clk-core.h create mode 100644 drivers/clk/mvebu/clk-cpu.c create mode 100644 drivers/clk/mvebu/clk-cpu.h create mode 100644 drivers/clk/mvebu/clk.c diff --git a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt new file mode 100644 index 0000000..80be3e7 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt @@ -0,0 +1,41 @@ +Device Tree Clock bindings for core clock of Marvell EBU platforms + +This is the binding for the "core" clock of the mvebu SoCs, the rate +of this clocs are fixed during reset. Their value or ratio are taken +from the Sample at Reset(SAR) register. + +Required properties: +- compatible : shall be one of the following: + "marvell,armada-370-core-clockctrl" - for a core clock from Armada 370 + "marvell,armada-xp-core-clockctrl" - for a core clock from Armada XP +- reg : Address and length of the SAR register set +- #clock-cells : should be set to 1. +- clock-output-names: A list of clock output names that i.MX28 CLKCTRL + provides. The full list of all valid clock names, IDs and + desciption is below. + Name ID Description + tclk 0 Peripheral clock + pclk 1 CPU clock + nbclk 2 L2 clock + hclk 3 DRAM control clock + dramclk 4 DDR clock + +coreclk: mvebu-sar@d0018230 { + #clock-cells = <1>; + reg = <0xd0018230 0x08>; + compatible = "marvell,armada-370-core-clockctrl"; + clock-output-names = + "tclk", /* 0 */ + "pclk", /* 1 */ + "nbclk", /* 2 */ + "hclk", /* 3 */ + "dramclk", /* 4 */ + "end_of_list"; +}; + +timer@d0020300 { + compatible = "marvell,armada-370-xp-timer"; + reg = <0xd0020300 0x30>; + interrupts = <37>, <38>, <39>, <40>; + clocks = <&coreclk 0>; +}; diff --git a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt new file mode 100644 index 0000000..62e7c56 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt @@ -0,0 +1,21 @@ +Device Tree Clock bindings for cpu clock of Marvell EBU platforms + +Required properties: +- compatible : shall be one of the following: + "marvell,armada-xp-cpu-clockctrl" - for cpu clocks from Armada XP +- reg : Address and length of the clock complex register set +- #clock-cells : should be set to 1. +- clocks : shall be the input parent clock phandle for the clock. + +cpuclk: clock-complex@d0018700 { + #clock-cells = <1>; + compatible = "marvell,armada-xp-cpu-clockctrl"; + reg = <0xd0018700 0xA0>; + clocks = <&coreclk 1>; +} + +cpu@0 { + compatible = "marvell,sheeva-v7"; + reg = <0>; + clocks = <&cpuclk 0>; +}; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 6327536..435e4c0 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o +obj-$(CONFIG_ARCH_MVEBU) += mvebu/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile new file mode 100644 index 0000000..de94a87 --- /dev/null +++ b/drivers/clk/mvebu/Makefile @@ -0,0 +1,2 @@ +obj-y += clk.o clk-core.o +obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c new file mode 100644 index 0000000..5792cb5 --- /dev/null +++ b/drivers/clk/mvebu/clk-core.c @@ -0,0 +1,312 @@ +/* + * Marvell EBU clock core handling defined at reset + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include + +/* Sample At Reset is a 64 bit bitfiled split in two register of 32 + * bits*/ + +#define SARL 0 /* Low part [0:31] */ +#define SARL_AXP_PCLK_FREQ_OPT 21 +#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7 +#define SARL_A370_PCLK_FREQ_OPT 11 +#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF +#define SARL_AXP_FAB_FREQ_OPT 24 +#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF +#define SARL_A370_FAB_FREQ_OPT 15 +#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F +#define SARL_A370_TCLK_FREQ_OPT 20 +#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1 +#define SARH 4 /* High part [32:63] */ +#define SARH_AXP_PCLK_FREQ_OPT (52-32) +#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1 +#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3 +#define SARH_AXP_FAB_FREQ_OPT (51-32) +#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1 +#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4 + +u32 *sar_reg; +int sar_reg_size; + +enum core_clk { + tclk, pclk, nbclk, hclk, dramclk, clk_max +}; + +struct core_clk_fn { + u32(*get_tclk_freq) (void); + u32(*get_pck_freq) (void); + const int *(*get_fab_freq_opt) (void); +}; + +/* Ratio between VCO and each of the member in the following order: + CPU clock, L2 clock, DRAM controler clock, DDR clcok */ +static const int reset_core_ratio[32][4] = { + [0x01] = {1, 2, 2, 2}, + [0x02] = {2, 2, 6, 3}, + [0x03] = {2, 2, 3, 3}, + [0x04] = {1, 2, 3, 3}, + [0x05] = {1, 2, 4, 2}, + [0x06] = {1, 1, 2, 2}, + [0x07] = {2, 3, 6, 6}, + [0x09] = {1, 2, 6, 3}, + [0x0A] = {2, 4, 10, 5}, + [0x0C] = {1, 2, 4, 4}, + [0x0F] = {2, 2, 5, 5}, + [0x13] = {1, 1, 2, 1}, + [0x14] = {2, 3, 6, 3}, + [0x1B] = {1, 1, 1, 1}, +}; + +static struct clk *clks[clk_max]; + +static struct clk_onecell_data clk_data; + +/* Frequency in MHz*/ +static u32 armada_370_pclk[] = { 400, 533, 667, 800, 1000, 1067, 1200 }; + +static u32 armada_xp_pclk[] = { 1000, 1066, 1200, 1333, 1500, 1666, + 1800, 2000, 667, 0, 800, 1600 +}; + +static u32 armada_370_tclk[] = { 166, 200 }; + +static const int *__init armada_370_get_fab_freq_opt(void) +{ + u8 fab_freq_opt = 0; + + fab_freq_opt = ((sar_reg[0] >> SARL_A370_FAB_FREQ_OPT) & + SARL_A370_FAB_FREQ_OPT_MASK); + + if (reset_core_ratio[fab_freq_opt][0] == 0) + return NULL; + else + return reset_core_ratio[fab_freq_opt]; +} + +static u32 __init armada_370_get_pck_freq(void) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((sar_reg[0] >> SARL_A370_PCLK_FREQ_OPT) & + SARL_A370_PCLK_FREQ_OPT_MASK); + if (cpu_freq_select > ARRAY_SIZE(armada_370_pclk)) { + pr_err("CPU freq select unsuported %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = armada_370_pclk[cpu_freq_select]; + + return cpu_freq * 1000 * 1000; +} + +static u32 __init armada_370_get_tclk_freq(void) +{ + u32 tclk_freq; + u8 tclk_freq_select = 0; + + tclk_freq_select = ((sar_reg[0] >> SARL_A370_TCLK_FREQ_OPT) & + SARL_A370_TCLK_FREQ_OPT_MASK); + if (tclk_freq_select > ARRAY_SIZE(armada_370_tclk)) { + pr_err("TCLK freq select unsuported %d\n", tclk_freq_select); + tclk_freq = 0; + } else + tclk_freq = armada_370_tclk[tclk_freq_select]; + + return tclk_freq * 1000 * 1000; +} + +static const int *__init armada_xp_get_fab_freq_opt(void) +{ + u8 fab_freq_opt = 0; + + fab_freq_opt = ((sar_reg[0] >> SARL_AXP_FAB_FREQ_OPT) & + SARL_AXP_FAB_FREQ_OPT_MASK); + /* The upper bit is not contiguous to the other ones + * and located in the high part of the SAR + * registers */ + fab_freq_opt |= (((sar_reg[1] >> SARH_AXP_FAB_FREQ_OPT) & + SARH_AXP_FAB_FREQ_OPT_MASK) + << SARH_AXP_FAB_FREQ_OPT_SHIFT); + + if (reset_core_ratio[fab_freq_opt][0] == 0) + return NULL; + else + return reset_core_ratio[fab_freq_opt]; +} + +static u32 __init armada_xp_get_pck_freq(void) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((sar_reg[0] >> SARL_AXP_PCLK_FREQ_OPT) & + SARL_AXP_PCLK_FREQ_OPT_MASK); + /* The upper bit is not contiguous to the other ones + * and located in the high part of the SAR + * registers */ + cpu_freq_select |= (((sar_reg[1] >> SARH_AXP_PCLK_FREQ_OPT) & + SARH_AXP_PCLK_FREQ_OPT_MASK) + << SARH_AXP_PCLK_FREQ_OPT_SHIFT); + if (cpu_freq_select > ARRAY_SIZE(armada_xp_pclk)) { + pr_err("CPU freq select unsuported: %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = armada_xp_pclk[cpu_freq_select]; + + return cpu_freq * 1000 * 1000; +} + +/* For Armada XP TCLK frequency is fix: 250MHz */ +static u32 __init armada_xp_get_tclk_freq(void) +{ + return 250 * 1000 * 1000; +} + +void __init of_core_clk_setup(struct device_node *node, + struct core_clk_fn clk_fn) +{ + struct clk *clk; + unsigned long rate; + const char *clk_name; + int i; + + /* clock 0 is tclk */ + of_property_read_string_index(node, "clock-output-names", tclk, + &clk_name); + rate = clk_fn.get_tclk_freq(); + if (rate != 0) + clk = clk_register_fixed_rate(NULL, clk_name, NULL, + CLK_IS_ROOT, rate); + else { + pr_err("Invalid freq for %s\n", clk_name); + return; + } + + if (WARN_ON(IS_ERR(clk))) + return; + clks[tclk] = clk; + + /* clock 1 is pclk */ + of_property_read_string_index(node, "clock-output-names", pclk, + &clk_name); + rate = clk_fn.get_pck_freq(); + if (rate != 0) + clk = clk_register_fixed_rate(NULL, clk_name, NULL, + CLK_IS_ROOT, rate); + else { + pr_err("Invalid freq for %s\n", clk_name); + return; + } + if (WARN_ON(IS_ERR(clk))) + return; + clks[pclk] = clk; + + /* the clocks 2 to 4 are nbclk, hclk and dramclk and are all + * derivated from the clock 1: pclk */ + + for (i = nbclk; i <= dramclk; i++) { + const int *ratio = clk_fn.get_fab_freq_opt(); + const char *parent_clk_name; + + of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + of_property_read_string_index(node, "clock-output-names", + pclk, &parent_clk_name); + + if (ratio != NULL) + clk = clk_register_fixed_factor(NULL, clk_name, + parent_clk_name, 0, + ratio[0], + ratio[i - nbclk + 1]); + else { + pr_err("Invalid clk ratio for %s\n", clk_name); + return; + } + + if (WARN_ON(IS_ERR(clk))) + return; + clks[i] = clk; + } + clk_data.clk_num = ARRAY_SIZE(clks); + clk_data.clks = clks; + of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data); +} + +static struct core_clk_fn armada_370_clk_fn = { + .get_tclk_freq = armada_370_get_tclk_freq, + .get_pck_freq = armada_370_get_pck_freq, + .get_fab_freq_opt = armada_370_get_fab_freq_opt, +}; + +static struct core_clk_fn armada_xp_clk_fn = { + .get_tclk_freq = armada_xp_get_tclk_freq, + .get_pck_freq = armada_xp_get_pck_freq, + .get_fab_freq_opt = armada_xp_get_fab_freq_opt, +}; + +static const __initconst struct of_device_id clk_match[] = { + { + .compatible = "marvell,armada-370-core-clockctrl", + .data = &armada_370_clk_fn, + }, + + { + .compatible = "marvell,armada-xp-core-clockctrl", + .data = &armada_xp_clk_fn, + }, + { + /* sentinel */ + } +}; + +void __init mvebu_core_clocks_init(void) +{ + struct device_node *np; + struct resource res; + void __iomem *sar_base; + int i; + + np = of_find_node_by_name(NULL, "mvebu-sar"); + + if (!np) + goto err; + + if (of_address_to_resource(np, 0, &res)) + goto err; + + sar_reg_size = resource_size(&res); + sar_reg = kmalloc(sar_reg_size, GFP_KERNEL); + + sar_base = ioremap(res.start, sar_reg_size); + if (sar_base == NULL) + goto err; + for (i = 0; i < sar_reg_size; i += sizeof(*sar_reg)) + sar_reg[i] = readl(sar_base + i); + + iounmap(sar_base); + + for_each_matching_node(np, clk_match) { + const struct of_device_id *match = of_match_node(clk_match, np); + struct core_clk_fn *clk_fn = match->data; + of_core_clk_setup(np, *clk_fn); + } + + return; +err: + pr_err("%s:SAR base adresse not set in DT\n", __func__); + return; +} diff --git a/drivers/clk/mvebu/clk-core.h b/drivers/clk/mvebu/clk-core.h new file mode 100644 index 0000000..a04f80b --- /dev/null +++ b/drivers/clk/mvebu/clk-core.h @@ -0,0 +1,19 @@ +/* + * * Marvell EBU clock core handling defined at reset + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_CORE_H +#define __MVEBU_CLK_CORE_H + +void __init of_core_clk_setup(struct device_node *node); +void __init mvebu_core_clocks_init(void); + +#endif diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c new file mode 100644 index 0000000..ffbcfa6 --- /dev/null +++ b/drivers/clk/mvebu/clk-cpu.c @@ -0,0 +1,144 @@ +/* + * Marvell MVEBU CPU clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 +#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC +#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F + +struct cpu_clk { + struct clk_hw hw; + int cpu; + const char *clk_name; + const char *parent_name; + void __iomem *reg_base; +}; + +#define to_cpu_clk(p) container_of(p, struct cpu_clk, hw) + +static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + u32 reg, div; + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + div = (reg >> (cpuclk->cpu * 8)) & SYS_CTRL_CLK_DIVIDER_MASK; + + return parent_rate / div; +} + +static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long *parent_rate) +{ + /* Valid ratio are 1:1, 1:2 and 1:3 */ + u32 div; + + div = *parent_rate / rate; + if (div == 0) + div = 1; + else if (div > 3) + div = 3; + + return *parent_rate / div; +} + +static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + u32 reg, div; + u32 reload_mask; + + div = parent_rate / rate; + reg = (readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET) + & (~(SYS_CTRL_CLK_DIVIDER_MASK << (cpuclk->cpu * 8)))) + | (div << (cpuclk->cpu * 8)); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + /* Set clock divider reload smooth bit mask */ + reload_mask = 1 << (20 + cpuclk->cpu); + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET) + | reload_mask; + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + /* Now trigger the clock update */ + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET) + | 1 << 24; + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + /* Wait for clocks to settle down then clear reload request */ + udelay(1000); + reg &= ~(reload_mask | 1 << 24); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + udelay(1000); + + return 0; +} + +static const struct clk_ops cpu_ops = { + .recalc_rate = clk_cpu_recalc_rate, + .round_rate = clk_cpu_round_rate, + .set_rate = clk_cpu_set_rate, +}; + +void __init of_cpu_clk_setup(struct device_node *node) +{ + struct cpu_clk *cpuclk; + void __iomem *clock_complex_base = of_iomap(node, 0); + int cpu; + if (clock_complex_base == NULL) { + pr_err("%s: clock-complex base register not set\n", + __func__); + return; + } + + cpuclk = kzalloc(4 * sizeof(*cpuclk), GFP_KERNEL); + if (WARN_ON(!cpuclk)) + return; + for (cpu = 0; cpu < 4; cpu++) { + struct clk_init_data init; + struct clk *clk; + struct clk *parent_clk; + char *clk_name = kzalloc(5, GFP_KERNEL); + + sprintf(clk_name, "cpu%d", cpu); + parent_clk = of_clk_get(node, 0); + + cpuclk[cpu].parent_name = __clk_get_name(parent_clk); + cpuclk[cpu].clk_name = clk_name; + cpuclk[cpu].cpu = cpu; + cpuclk[cpu].reg_base = clock_complex_base; + cpuclk[cpu].hw.init = &init; + + init.name = cpuclk[cpu].clk_name; + init.ops = &cpu_ops; + init.flags = 0; + init.parent_names = &cpuclk[cpu].parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &cpuclk[cpu].hw); + if (WARN_ON(IS_ERR(clk))) + goto bail_out; + of_clk_add_provider(node, of_clk_src_simple_get, clk); + } + return; +bail_out: + kfree(cpuclk); +} diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h new file mode 100644 index 0000000..5d28356 --- /dev/null +++ b/drivers/clk/mvebu/clk-cpu.h @@ -0,0 +1,19 @@ +/* + * Marvell MVEBU CPU clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_CPU_H +#define __MVEBU_CLK_CPU_H + +void __init of_cpu_clk_setup(struct device_node *node); +void __init mvebu_cpu_clocks_init(void); + +#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c new file mode 100644 index 0000000..0864df7 --- /dev/null +++ b/drivers/clk/mvebu/clk.c @@ -0,0 +1,36 @@ +/* + * Marvell EBU SoC clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include "clk-cpu.h" +#include "clk-core.h" + +static const __initconst struct of_device_id clk_match[] = { +#ifdef CONFIG_MVEBU_CLK_CPU + { + .compatible = "marvell,armada-xp-cpu-clockctrl", + .data = of_cpu_clk_setup, + }, +#endif + { + /* sentinel */ + } +}; + +void __init mvebu_clocks_init(void) +{ + mvebu_core_clocks_init(); + of_clk_init(clk_match); +}