From patchwork Fri Jul 26 10:05:31 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Haojian Zhuang X-Patchwork-Id: 2833985 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 26DBAC0319 for ; Fri, 26 Jul 2013 11:10:42 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 82A0A20125 for ; Fri, 26 Jul 2013 11:10:40 +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 B003E20107 for ; Fri, 26 Jul 2013 11:10:38 +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 1V2f0l-00024f-RH; Fri, 26 Jul 2013 10:12:37 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1V2f06-0003zZ-Mq; Fri, 26 Jul 2013 10:11:54 +0000 Received: from mail-pd0-x231.google.com ([2607:f8b0:400e:c02::231]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1V2f02-0003xy-Lh for linux-arm-kernel@lists.infradead.org; Fri, 26 Jul 2013 10:11:51 +0000 Received: by mail-pd0-f177.google.com with SMTP id u11so2733199pdi.8 for ; Fri, 26 Jul 2013 03:11:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=4Hs7oC8gVD/PTXAo01Bi4zs4EBJwV7bj9ZJrZxs/8z0=; b=Ngwdo/URgFGliZBvNbdUwEdt89rPisIIOCgkTzwPne67D6GptEZzlUPozJLp+A6s3W bLDMW7oSGqVt+c9QB9B637K1kQqiKUA6H0qsT8egiuG1WULayAPaDzPlddiMUgCEDFmn 8nFpX1NBriAsGhQF6QZhsAXkhQVir85hXuaEdERuwQp7EiXeoutZ3h9a6zQSm6Vzv4K4 yRyFL2vZKYjquvp9XOvBjILvAeOPaLBX7/t2dJzNr2coJtYPzEJBYUivQ67hEdlfqqfU VyU7EkbsGe42vY0CVRHDpf0rnUO4tV8VsD4uyuW1jWzsicvdhaWSl5J+6r5HEdPMOR/h PFsg== X-Received: by 10.68.231.200 with SMTP id ti8mr52628353pbc.46.1374833489679; Fri, 26 Jul 2013 03:11:29 -0700 (PDT) Received: from localhost.localdomain ([67.229.68.101]) by mx.google.com with ESMTPSA id kc8sm59263070pbc.18.2013.07.26.03.11.04 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 26 Jul 2013 03:11:29 -0700 (PDT) From: Haojian Zhuang To: tglx@linutronix.de, arnd@arndb.de, linux-arm-kernel@lists.infradead.org, chao.xie@marvell.com, john.stultz@linaro.org, mturquette@linaro.org, eric.y.miao@gmail.com, linux@arm.linux.org.uk, olof@lixom.net Subject: [PATCH v6 09/11] clk: mmp: parse clock from dts Date: Fri, 26 Jul 2013 18:05:31 +0800 Message-Id: <1374833133-21119-10-git-send-email-haojian.zhuang@gmail.com> X-Mailer: git-send-email 1.8.1.2 In-Reply-To: <1374833133-21119-1-git-send-email-haojian.zhuang@gmail.com> References: <1374833133-21119-1-git-send-email-haojian.zhuang@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130726_061150_903151_BE863470 X-CRM114-Status: GOOD ( 28.69 ) X-Spam-Score: -2.0 (--) Cc: Haojian Zhuang 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=-5.5 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, T_DKIM_INVALID, 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 Parse clock information from DTS file for mach-mmp. Changelog: v6: 1. Remove marvell string from properties in clock driver. 2. Append document for clock binding device tree. 3. Use apbc to replace apbcp. Since the main difference is register base. v5: 1. Replace clk_register_mux() by clk_register_mux_table(). Signed-off-by: Haojian Zhuang --- .../devicetree/bindings/clock/mmp-clock.txt | 51 ++++ arch/arm/mach-mmp/mmp-dt.c | 2 + drivers/clk/mmp/Makefile | 2 +- drivers/clk/mmp/clk-mmp.c | 300 +++++++++++++++++++++ 4 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/clock/mmp-clock.txt create mode 100644 drivers/clk/mmp/clk-mmp.c diff --git a/Documentation/devicetree/bindings/clock/mmp-clock.txt b/Documentation/devicetree/bindings/clock/mmp-clock.txt new file mode 100644 index 0000000..5fe52a2 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mmp-clock.txt @@ -0,0 +1,51 @@ +Device Tree Clock bindings for arch-mmp + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties for fixed rate clocks: + - compatible : should be "marvell,mmp-fixed-clkrate". + - clocks : should be the input parent clock phandle for the clock. This + should be the reference clock. + - clock-output-names : should be reference name. + - #clock-cells : from common clock binding; should be set to 0. + - clock-frequency : frequency of the fixed rate clock + +Required properties for fixed factor clocks: + - compatible : should be "marvell,mmp-fixed-clkfactor". + - clocks : should be the input parent clock phandle for the clock. This + should be the reference clock. + - clock-output-names : should be reference name. + - #clock-cells : from common clock binding; should be set to 0. + +Required properties for mux clocks: + - compatible : should be "marvell,mmp-clkmux". + - clocks : should be the input parent clock phandle for the clock. This + should be the reference clock. + - clock-output-names : should be reference name. + - #clock-cells : from common clock binding; should be set to 0. + - mmp-clk-reg : mux register offset & mask bits. + - mmp-clk-sel : array of mux select bits + +Required properties for divider clocks: + - compatible : should be "marvell,mmp-apmu-clkdiv". + - clocks : should be the input parent clock phandle for the clock. This + should be the reference clock. + - clock-output-names : should be reference name. + - #clock-cells : from common clock binding; should be set to 0. + - mmp-clk-reg : divider register offset & mask bits. + +Required properties for gate clocks: + - compatible : should be "marvell,mmp-apbc-clk". + - clocks : should be the input parent clock phandle for the clock. This + should be the reference clock. + - clock-output-names : should be reference name. + - #clock-cells : from common clock binding; should be set to 0. + - mmp-clk-reg : gate register offset & mask bits. + +Optional properties for gate clocks: + - mmp-clk-delay : delay between enabling function clock and apb clock + - mmp-apbc-power-ctrl : enable apbc power control bit + +For example: diff --git a/arch/arm/mach-mmp/mmp-dt.c b/arch/arm/mach-mmp/mmp-dt.c index cca529c..f109bf6 100644 --- a/arch/arm/mach-mmp/mmp-dt.c +++ b/arch/arm/mach-mmp/mmp-dt.c @@ -9,6 +9,7 @@ * publishhed by the Free Software Foundation. */ +#include #include #include #include @@ -48,6 +49,7 @@ static void __init pxa168_dt_init(void) static void __init pxa910_dt_init(void) { + of_clk_init(NULL); of_platform_populate(NULL, of_default_bus_match_table, pxa910_auxdata_lookup, NULL); } diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile index 392d780..83182e5 100644 --- a/drivers/clk/mmp/Makefile +++ b/drivers/clk/mmp/Makefile @@ -2,7 +2,7 @@ # Makefile for mmp specific clk # -obj-y += clk-apbc.o clk-apmu.o clk-frac.o +obj-y += clk-apbc.o clk-apmu.o clk-frac.o clk-mmp.o obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o diff --git a/drivers/clk/mmp/clk-mmp.c b/drivers/clk/mmp/clk-mmp.c new file mode 100644 index 0000000..50c16a0 --- /dev/null +++ b/drivers/clk/mmp/clk-mmp.c @@ -0,0 +1,300 @@ +/* + * Marvell MMP clock driver + * + * Copyright (c) 2012-2013 Linaro Limited. + * + * Author: Haojian Zhuang + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +enum { + MMP_MPMU = 0, + MMP_APMU, + MMP_APBC, + MMP_APBCP, + MMP_APB, + MMP_MAX, +}; + +static void __iomem *mmp_clk_base[MMP_MAX]; + +static DEFINE_SPINLOCK(mmp_clk_lock); + +static const struct of_device_id mmp_of_match[] = { + { .compatible = "marvell,mmp-mpmu", .data = (void *)MMP_MPMU, }, + { .compatible = "marvell,mmp-apmu", .data = (void *)MMP_APMU, }, + { .compatible = "marvell,mmp-apbc", .data = (void *)MMP_APBC, }, + { .compatible = "marvell,mmp-apbcp", .data = (void *)MMP_APBCP, }, + { .compatible = "mrvl,apb-bus", .data = (void *)MMP_APB, }, +}; + +void __iomem __init *mmp_init_clocks(struct device_node *np) +{ + struct device_node *parent; + const struct of_device_id *match; + void __iomem *ret = NULL; + int i; + + parent = of_get_parent(np); + if (!parent) + goto out; + match = of_match_node(mmp_of_match, parent); + if (!match) + goto out; + + i = (unsigned int)match->data; + switch (i) { + case MMP_MPMU: + case MMP_APMU: + case MMP_APBC: + case MMP_APBCP: + case MMP_APB: + if (!mmp_clk_base[i]) { + ret = of_iomap(parent, 0); + WARN_ON(!ret); + mmp_clk_base[i] = ret; + } else { + ret = mmp_clk_base[i]; + } + break; + default: + goto out; + } +out: + return ret; +} + +static void __init mmp_fixed_rate_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, *parent_name; + int rate; + + if (of_property_read_u32(np, "clock-frequency", &rate)) + return; + + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + + /* this node has only one parent */ + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + clk = clk_register_fixed_rate(NULL, clk_name, parent_name, 0, rate); + if (IS_ERR(clk)) + return; + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(mmp_fixed_rate, "marvell,mmp-fixed-clkrate", + mmp_fixed_rate_setup); + +static void __init mmp_fixed_factor_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, *parent_name; + u32 data[2]; + + if (of_property_read_u32_array(np, "mmp-fixed-factor", + &data[0], 2)) + return; + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + + /* this node has only one parent */ + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, + data[0], data[1]); + if (IS_ERR(clk)) + return; + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(mmp_fixed_factor, "marvell,mmp-fixed-clkfactor", + mmp_fixed_factor_setup); + +static void __init mmp_apmu_div_setup(struct device_node *np) +{ + u32 data[2]; + u8 shift, width; + void __iomem *reg, *base; + const char *parent_name, *clk_name; + struct clk *clk; + + base = mmp_init_clocks(np); + if (!base) + return; + + if (of_property_read_u32_array(np, "mmp-clk-reg", &data[0], 2)) + return; + reg = base + data[0]; + shift = ffs(data[1]) - 1; + width = fls(data[1]) - ffs(data[1]) + 1; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + + clk = clk_register_divider(NULL, clk_name, parent_name, + CLK_SET_RATE_PARENT, reg, shift, width, 0, + &mmp_clk_lock); + if (IS_ERR(clk)) + return; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +} +CLK_OF_DECLARE(mmp_div, "marvell,mmp-apmu-clkdiv", mmp_apmu_div_setup); + +static int __init mmp_parse_mux(struct device_node *np, + u8 *num_parents, + u32 *clk_sel) +{ + int i, cnt, ret; + + /* get the count of items in mux */ + for (i = 0, cnt = 0; ; i++, cnt++) { + /* parent's #clock-cells property is always 0 */ + if (!of_parse_phandle(np, "clocks", i)) + break; + } + + for (i = 0; i < cnt; i++) { + if (!of_clk_get_parent_name(np, i)) + break; + } + *num_parents = cnt; + clk_sel = kzalloc(sizeof(u32 *) * cnt, GFP_KERNEL); + if (!clk_sel) + return -ENOMEM; + ret = of_property_read_u32_array(np, "mmp-clk-sel", clk_sel, cnt); + if (ret) + goto err; + return 0; +err: + kfree(clk_sel); + return ret; +} + +static void __init mmp_mux_setup(struct device_node *np) +{ + u32 data[2], mask, *clk_sel = NULL, mux_flags = 0; + u8 shift, num_parents; + void __iomem *reg, *base; + const char **parent_names; + const char *clk_name; + struct clk *clk; + int i, ret; + + base = mmp_init_clocks(np); + if (!base) + return; + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + if (of_property_read_u32_array(np, "mmp-clk-reg", &data[0], 2)) + return; + ret = mmp_parse_mux(np, &num_parents, clk_sel); + if (ret) + return; + + reg = base + data[0]; + shift = ffs(data[1]) - 1; + mask = data[1] >> shift; + + parent_names = kzalloc(sizeof(char *) * num_parents, GFP_KERNEL); + if (!parent_names) + goto err; + for (i = 0; i < num_parents; i++) + parent_names[i] = of_clk_get_parent_name(np, i); + clk = clk_register_mux_table(NULL, clk_name, parent_names, num_parents, + CLK_SET_RATE_PARENT, reg, shift, mask, + mux_flags, clk_sel, &mmp_clk_lock); + if (IS_ERR(clk)) + goto err_clk; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err_clk: + kfree(parent_names); +err: + kfree(clk_sel); +} +CLK_OF_DECLARE(mmp_mux, "marvell,mmp-clkmux", mmp_mux_setup); + +#define APBC_NO_BUS_CTRL BIT(0) +#define APBC_POWER_CTRL BIT(1) + +static void __init mmp_apbc_setup(struct device_node *np) +{ + u32 data[2], delay, apbc_flags, clkdev; + void __iomem *reg, *base; + const char **parent_names; + const char *clk_name = NULL; + struct clk *clk; + + base = mmp_init_clocks(np); + if (!base) + return; + + if (of_property_read_string(np, "clock-names", &clk_name)) + clkdev = 1; + else { + of_property_read_string(np, "clock-output-names", &clk_name); + clkdev = 0; + } + + if (of_property_read_u32_array(np, "mmp-clk-reg", &data[0], 2)) + return; + /* If mmp-clk-delay property isn't defined, set delay as 10us */ + if (of_property_read_u32(np, "mmp-clk-delay", &delay)) + delay = 10; + if (of_get_property(np, "mmp-apbc-power-ctrl", NULL)) + apbc_flags |= APBC_POWER_CTRL; + + reg = base + data[0]; + + /* only has the fixed parent */ + parent_names = kzalloc(sizeof(char *), GFP_KERNEL); + if (!parent_names) + return; + parent_names[0] = of_clk_get_parent_name(np, 0); + + clk = mmp_clk_register_apbc(clk_name, parent_names[0], + reg, delay, 0, &mmp_clk_lock); + if (IS_ERR(clk)) { + kfree(parent_names); + return; + } + if (clkdev) + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(mmp_apbc, "marvell,mmp-apbc-clk", mmp_apbc_setup);