From patchwork Wed Jul 17 13:45:31 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris BREZILLON X-Patchwork-Id: 2828618 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id BC946C0AB2 for ; Wed, 17 Jul 2013 13:47:57 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 6584120203 for ; Wed, 17 Jul 2013 13:47:56 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 559CE201FF for ; Wed, 17 Jul 2013 13:47:54 +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 1UzS3y-0000yV-MC; Wed, 17 Jul 2013 13:46:40 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UzS3P-0003Qn-JP; Wed, 17 Jul 2013 13:46:03 +0000 Received: from 12.mo1.mail-out.ovh.net ([87.98.162.229] helo=mo1.mail-out.ovh.net) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UzS3L-0003QD-C8 for linux-arm-kernel@lists.infradead.org; Wed, 17 Jul 2013 13:46:00 +0000 Received: from mail31.ha.ovh.net (b9.ovh.net [213.186.33.59]) by mo1.mail-out.ovh.net (Postfix) with SMTP id 377BCFF833B for ; Wed, 17 Jul 2013 15:45:37 +0200 (CEST) Received: from b0.ovh.net (HELO queueout) (213.186.33.50) by b0.ovh.net with SMTP; 17 Jul 2013 15:45:09 +0200 Received: from cha74-5-78-236-240-82.fbx.proxad.net (HELO localhost.localdomain) (b.brezillon@overkiz.com@78.236.240.82) by ns0.ovh.net with SMTP; 17 Jul 2013 15:45:08 +0200 From: Boris BREZILLON To: Nicolas Ferre , Ludovic Desroches , Jean-Christophe Plagniol-Villard , Mike Turquette X-Ovh-Mailout: 178.32.228.1 (mo1.mail-out.ovh.net) Subject: [PATCH v2 05/42] ARM: at91: add PMC system clocks Date: Wed, 17 Jul 2013 15:45:31 +0200 Message-Id: <1374068732-13894-1-git-send-email-b.brezillon@overkiz.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1374068069-13496-1-git-send-email-b.brezillon@overkiz.com> References: <1374068069-13496-1-git-send-email-b.brezillon@overkiz.com> X-Ovh-Tracer-Id: 11521052272471472236 X-Ovh-Remote: 78.236.240.82 (cha74-5-78-236-240-82.fbx.proxad.net) X-Ovh-Local: 213.186.33.20 (ns0.ovh.net) X-OVH-SPAMSTATE: OK X-OVH-SPAMSCORE: -100 X-OVH-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrfeeijedrvdegucetufdoteggodetrfcurfhrohhfihhlvgemucfqggfjnecuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmd X-Spam-Check: DONE|U 0.5/N X-VR-SPAMSTATE: OK X-VR-SPAMSCORE: -100 X-VR-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrfeeijedrvdegucetufdoteggodetrfcurfhrohhfihhlvgemucfqggfjnecuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmd X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130717_094559_609989_73BEBC80 X-CRM114-Status: GOOD ( 22.49 ) X-Spam-Score: -1.9 (-) Cc: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.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 X-Spam-Status: No, score=-4.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 | 184 +++++++++++++++++++++++++++++++++++++++++ include/linux/clk/at91.h | 3 + 3 files changed, 188 insertions(+) create mode 100644 drivers/clk/at91/clk-system.c 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..17e092a --- /dev/null +++ b/drivers/clk/at91/clk-system.c @@ -0,0 +1,184 @@ +/* + * 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, 1 << 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) & (1 << 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, 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 = NULL; + init.num_parents = 0; + /* + * CLK_IGNORE_UNUSED is used to avoid ddrck switch off. + * TODO : we should implement a driver supporting at91 ddr controller + * (see drivers/memory) which would request and enable the ddrck clock. + * When this is done we will be able to remove CLK_IGNORE_UNUSED flag. + */ + init.flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED; + + 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 *name; + struct device_node *sysclknp; + + num = of_get_child_count(np); + if (num > SYSTEM_MAX) + 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; + + i = 0; + for_each_child_of_node(np, sysclknp) { + name = sysclknp->name; + + if (of_property_read_u32(sysclknp, "id", &id)) + goto out_free_clks; + if (id >= SYSTEM_MAX) + goto out_free_clks; + + clk = at91_clk_register_system(name, id); + if (IS_ERR(clk)) + goto out_free_clks; + + clks[i] = clk; + ids[i] = id; + + i++; + } + + 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 324134c..518a568 100644 --- a/include/linux/clk/at91.h +++ b/include/linux/clk/at91.h @@ -259,4 +259,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, u8 id); + #endif