From patchwork Fri Jun 7 14:24:13 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris BREZILLON X-Patchwork-Id: 2687171 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) by patchwork1.kernel.org (Postfix) with ESMTP id 31F6C40077 for ; Fri, 7 Jun 2013 14:39:19 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1UkxoM-0001gp-Ga; Fri, 07 Jun 2013 14:38:39 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Ukxnx-0006WL-3y; Fri, 07 Jun 2013 14:38:13 +0000 Received: from 17.mo1.mail-out.ovh.net ([87.98.179.142] helo=mo1.mail-out.ovh.net) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Ukxnt-0006VI-Er for linux-arm-kernel@lists.infradead.org; Fri, 07 Jun 2013 14:38:10 +0000 Received: from mail640.ha.ovh.net (b9.ovh.net [213.186.33.59]) by mo1.mail-out.ovh.net (Postfix) with SMTP id 607ADFFAA89 for ; Fri, 7 Jun 2013 16:37:47 +0200 (CEST) Received: from b0.ovh.net (HELO queueout) (213.186.33.50) by b0.ovh.net with SMTP; 7 Jun 2013 16:35:54 +0200 Received: from unknown (HELO bbrezillon-laptop) (80.245.18.66) by ns0.ovh.net with SMTP; 7 Jun 2013 16:35:53 +0200 Received: from bbrezillon by bbrezillon-laptop with local (Exim 4.76) (envelope-from ) id 1UkxnV-0004Ux-Jg; Fri, 07 Jun 2013 16:37:45 +0200 From: Boris BREZILLON To: Mike Turquette , Jean-Christophe Plagniol-Villard , Nicolas Ferre , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Grant Likely , Rob Herring X-Ovh-Mailout: 178.32.228.1 (mo1.mail-out.ovh.net) Subject: [RFC PATCH 05/50] ARM: at91: add PMC system clocks Date: Fri, 7 Jun 2013 16:24:13 +0200 Message-Id: <1370615115-16979-6-git-send-email-b.brezillon@overkiz.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1370615115-16979-1-git-send-email-b.brezillon@overkiz.com> References: <1370615115-16979-1-git-send-email-b.brezillon@overkiz.com> X-Ovh-Tracer-Id: 17278341447218345132 X-Ovh-Remote: 80.245.18.66 () X-Ovh-Local: 213.186.33.20 (ns0.ovh.net) X-OVH-SPAMSTATE: OK X-OVH-SPAMSCORE: -100 X-OVH-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrfeeiiedrgedtucetufdoteggodetrfcurfhrohhfihhlvgemucfqggfjnecuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmd X-Spam-Check: DONE|U 0.5/N X-VR-SPAMSTATE: OK X-VR-SPAMSCORE: -100 X-VR-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrfeeiiedrgedtucetufdoteggodetrfcurfhrohhfihhlvgemucfqggfjnecuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmd X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130607_103809_703073_571F3D10 X-CRM114-Status: GOOD ( 21.54 ) X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [87.98.179.142 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: devicetree-discuss@lists.ozlabs.org, Boris BREZILLON X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org This is the at91 system clock implementation using common clk framework. Some peripheral needs to enable a "system" clock in order to work properly. Each system clock is given an id which is the bit offset used in SCER/SCDR registers. Signed-off-by: Boris BREZILLON --- drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-system.c | 189 +++++++++++++++++++++++++++++++++++++++++ include/linux/clk/at91.h | 3 + 3 files changed, 193 insertions(+) diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index d41f616..13e5714 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,3 +3,4 @@ # obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o +obj-y += clk-system.o diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c new file mode 100644 index 0000000..21af468 --- /dev/null +++ b/drivers/clk/at91/clk-system.c @@ -0,0 +1,189 @@ +/* + * drivers/clk/at91/clk-system.c + * + * Copyright (C) 2013 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#define SYSTEM_MAX 32 + +#define to_clk_system(hw) container_of(hw, struct clk_system, hw) +struct clk_system { + struct clk_hw hw; + u8 id; +}; + +static int clk_system_enable(struct clk_hw *hw) +{ + struct clk_system *sys = to_clk_system(hw); + at91_pmc_write(AT91_PMC_SCER, sys->id); + return 0; +} + +static void clk_system_disable(struct clk_hw *hw) +{ + struct clk_system *sys = to_clk_system(hw); + at91_pmc_write(AT91_PMC_SCDR, 1 << sys->id); +} + +static int clk_system_is_enabled(struct clk_hw *hw) +{ + struct clk_system *sys = to_clk_system(hw); + return !!(at91_pmc_read(AT91_PMC_SCSR) & (sys->id)); +} + +static const struct clk_ops system_ops = { + .enable = clk_system_enable, + .disable = clk_system_disable, + .is_enabled = clk_system_is_enabled, +}; + +struct clk * __init +at91_clk_register_system(const char *name, const char *parent_name, u8 id) +{ + struct clk_system *sys; + struct clk *clk = NULL; + struct clk_init_data init; + + id &= 31; + + sys = kzalloc(sizeof(*sys), GFP_KERNEL); + if (!sys) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &system_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + init.flags = 0; + + sys->id = id; + sys->hw.init = &init; + + clk = clk_register(NULL, &sys->hw); + + if (IS_ERR(clk)) + kfree(sys); + + return clk; +} + +#if defined(CONFIG_OF) +struct clk_system_data { + struct clk **clks; + u8 *ids; + unsigned int clk_num; +}; + +static struct clk * __init +of_clk_src_system_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk_system_data *clk_data = data; + unsigned int id = clkspec->args[0]; + int i; + + if (id >= SYSTEM_MAX) + goto err; + + for (i = 0; i < clk_data->clk_num; i++) { + if (clk_data->ids[i] == id) + return clk_data->clks[i]; + } + +err: + pr_err("%s: invalid clock id %d\n", __func__, id); + return ERR_PTR(-EINVAL); +} + +static void __init +of_at91_clk_sys_setup(struct device_node *np) +{ + int i; + int num; + u32 id; + struct clk *clk; + u8 *ids; + struct clk **clks; + struct clk_system_data *clktab; + const char *parent_name; + const char *name = np->name; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + if (!of_get_property(np, "ids", &num)) + return; + num /= 4; + if (num > SYSTEM_MAX) + return; + + if (of_count_phandle_with_args(np, "clocks", "#clock-cells") != num) + return; + + if (of_property_count_strings(np, "clock-output-names") != num) + return; + + clktab = kzalloc(sizeof(*clktab), GFP_KERNEL); + if (!clktab) + return; + + ids = kzalloc(num * sizeof(*ids), GFP_KERNEL); + if (!ids) + goto out_free_clktab; + + clks = kzalloc(num * sizeof(*clks), GFP_KERNEL); + if (!clks) + goto out_free_ids; + + for (i = 0; i < num; i++) { + of_property_read_u32_index(np, "ids", i, &id); + if (id >= SYSTEM_MAX) + goto out_free_clks; + of_property_read_string_index(np, "clock-output-names", + i, &name); + parent_name = of_clk_get_parent_name(np, i); + if (!parent_name) + goto out_free_clks; + + clk = at91_clk_register_system(name, parent_name, id); + if (IS_ERR(clk)) + goto out_free_clks; + + clks[i] = clk; + ids[i] = id; + } + + clktab->clk_num = num; + clktab->clks = clks; + clktab->ids = ids; + of_clk_add_provider(np, of_clk_src_system_get, clktab); + return; + +out_free_clks: + kfree(clks); +out_free_ids: + kfree(ids); +out_free_clktab: + kfree(clktab); +} + +static void __init of_at91rm9200_clk_sys_setup(struct device_node *np) +{ + of_at91_clk_sys_setup(np); +} +CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system", + of_at91rm9200_clk_sys_setup); +#endif diff --git a/include/linux/clk/at91.h b/include/linux/clk/at91.h index a960e2f..c886226 100644 --- a/include/linux/clk/at91.h +++ b/include/linux/clk/at91.h @@ -256,4 +256,7 @@ at91_clk_register_master(const char *name, int num_parents, struct clk_master_characteristics *characteristics); +struct clk * __init +at91_clk_register_system(const char *name, const char *parent_name, u8 id); + #endif