From patchwork Mon Jun 17 08:56:13 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Haojian Zhuang X-Patchwork-Id: 17939 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ye0-f197.google.com (mail-ye0-f197.google.com [209.85.213.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id F1E962100B for ; Mon, 17 Jun 2013 08:56:36 +0000 (UTC) Received: by mail-ye0-f197.google.com with SMTP id q4sf3088010yen.8 for ; Mon, 17 Jun 2013 01:56:36 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:x-beenthere:x-forwarded-to:x-forwarded-for :delivered-to:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:x-gm-message-state:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-google-group-id:list-post:list-help:list-archive:list-unsubscribe; bh=Y2A457GsLe0b1skobQbMtQskrHMN3cHbewtiW53DKpY=; b=aGTB2EOYMEmnnrFZscEjpnP6t9WfhO/emMiidvZKvLvuN7tQxkyjytwltn1mybSOHz gACTVi/Gty2X2y7ZBWDRPxOa5gkO9ZEwrcqTKS0xzAB9mSMGClrNDQN7RF1aK6jZ9qyk tccmUWtwI1RRSx/H/UUJKUFY8y4nAnx523tg9uyGZM/Fj4XT0tB+e4TpKCHYHkdnnwMP 8R+/r/qcM/Fyq2m5xOQYb0FKK9IiP05jeP4znC4wnSqgqv6Ohw/UNrKJ6eBvcpGAjdIz /mULwVH9J+exJIIzHjzSgjHekB8j63onrG+O7w6Nimibq1AfcwPJEkNE59UbJHmjnb2g mSyw== X-Received: by 10.224.57.65 with SMTP id b1mr3968508qah.2.1371459396769; Mon, 17 Jun 2013 01:56:36 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.49.86.232 with SMTP id s8ls948508qez.94.gmail; Mon, 17 Jun 2013 01:56:36 -0700 (PDT) X-Received: by 10.220.83.138 with SMTP id f10mr4226546vcl.7.1371459396654; Mon, 17 Jun 2013 01:56:36 -0700 (PDT) Received: from mail-vc0-f170.google.com (mail-vc0-f170.google.com [209.85.220.170]) by mx.google.com with ESMTPS id ej7si2719479vcb.78.2013.06.17.01.56.36 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 17 Jun 2013 01:56:36 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.220.170 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.220.170; Received: by mail-vc0-f170.google.com with SMTP id hf12so1788344vcb.1 for ; Mon, 17 Jun 2013 01:56:36 -0700 (PDT) X-Received: by 10.58.255.229 with SMTP id at5mr4139065ved.44.1371459396531; Mon, 17 Jun 2013 01:56:36 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.58.165.8 with SMTP id yu8csp26242veb; Mon, 17 Jun 2013 01:56:35 -0700 (PDT) X-Received: by 10.66.4.10 with SMTP id g10mr12205821pag.217.1371459395015; Mon, 17 Jun 2013 01:56:35 -0700 (PDT) Received: from mail-pb0-x22a.google.com (mail-pb0-x22a.google.com [2607:f8b0:400e:c01::22a]) by mx.google.com with ESMTPS id e8si5899536pao.78.2013.06.17.01.56.34 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 17 Jun 2013 01:56:35 -0700 (PDT) Received-SPF: neutral (google.com: 2607:f8b0:400e:c01::22a is neither permitted nor denied by best guess record for domain of haojian.zhuang@linaro.org) client-ip=2607:f8b0:400e:c01::22a; Received: by mail-pb0-f42.google.com with SMTP id un1so2555514pbc.29 for ; Mon, 17 Jun 2013 01:56:34 -0700 (PDT) X-Received: by 10.66.26.194 with SMTP id n2mr12012201pag.220.1371459394546; Mon, 17 Jun 2013 01:56:34 -0700 (PDT) Received: from localhost.localdomain ([27.115.121.40]) by mx.google.com with ESMTPSA id v7sm13051315pbq.32.2013.06.17.01.56.28 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 17 Jun 2013 01:56:33 -0700 (PDT) From: Haojian Zhuang To: arnd@arndb.de, linux@arm.linux.org.uk, linus.walleij@linaro.org, olof@lixom.net, rob.herring@calxeda.com, linux-arm-kernel@lists.infradead.org, tglx@linutronix.de, john.stultz@linaro.org, mturquette@linaro.org Cc: patches@linaro.org, Haojian Zhuang Subject: [PATCH v5 1/4] clk: hi3xxx: add clock support Date: Mon, 17 Jun 2013 16:56:13 +0800 Message-Id: <1371459376-25438-2-git-send-email-haojian.zhuang@linaro.org> X-Mailer: git-send-email 1.8.1.2 In-Reply-To: <1371459376-25438-1-git-send-email-haojian.zhuang@linaro.org> References: <1371459376-25438-1-git-send-email-haojian.zhuang@linaro.org> X-Gm-Message-State: ALoCoQnRqP4evQ91OGkb1BdhHDGh2+MSMgJHjrzJM2lypVKIWPviPUeW+Jy3faSCAdmSVTqW5K4s X-Original-Sender: haojian.zhuang@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.170 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Add clock support with device tree on Hisilicon SoC. Signed-off-by: Haojian Zhuang Cc: Mike Turquette --- .../devicetree/bindings/clock/hisilicon.txt | 66 ++++ drivers/clk/Makefile | 1 + drivers/clk/clk-hi3xxx.c | 414 +++++++++++++++++++++ 3 files changed, 481 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/hisilicon.txt create mode 100644 drivers/clk/clk-hi3xxx.c diff --git a/Documentation/devicetree/bindings/clock/hisilicon.txt b/Documentation/devicetree/bindings/clock/hisilicon.txt new file mode 100644 index 0000000..7f99805 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/hisilicon.txt @@ -0,0 +1,66 @@ +Device Tree Clock bindings for arch-hi3xxx + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties for mux clocks: + - compatible : Shall be "hisilicon,hi3620-clk-mux". + - clocks : shall be the input parent clock phandle for the clock. This should + be the reference clock. + - clock-output-names : shall be reference name. + - #clock-cells : from common clock binding; shall be set to 0. + - hisilicon,clkmux-reg : array of mux register offset & mask bits + - hisilicon,clkmux-table : array of mux select bits + +Required properties for Hi3620 gate clocks: + - compatible : Shall be "hisilicon,hi3620-clk-gate". + - clocks : shall be the input parent clock phandle for the clock. This should + be the reference clock. + - clock-output-names : shall be reference name. + - #clock-cells : from common clock binding; shall be set to 0. + - hisilicon,hi3620-clkgate : array of enable register offset & enable bits + - hisilicon,hi3620-clkreset : array of reset register offset & enable bits + +Required properties for clock divider: + - compatible : Shall be "hisilicon,hi3620-clk-div". + - clocks : shall be the input parent clock phandle for the clock. This should + be the reference clock. + - clock-output-names : shall be reference name. + - #clock-cells : from common clock binding; shall be set to 0. + - #hisilicon,clkdiv-table-cells : the number of parameters after phandle in + hisilicon,clkdiv-table property. + - hisilicon,clkdiv-table : list of value that are used to configure clock + divider. They're value of phandle, index & divider value. + - hisilicon,clkdiv : array of divider register offset & mask bits. + +Required properties for gate clocks: + - compatible : Shall be "hisilicon,clk-gate". + - clocks : shall be the input parent clock phandle for the clock. This should + be the reference clock. + - clock-output-names : shall be reference name. + - #clock-cells : from common clock binding; shall be set to 0. + - hisilicon,clkgate-inverted : bool value. True means that set-to-disable. + +For example: + timclk1: clkgate@38 { + compatible = "hisilicon,clk-gate"; + #clock-cells = <0>; + clocks = <&refclk_timer1>; + clock-output-names = "timclk1"; + hisilicon,clkgate-inverted; + hisilicon,clkgate = <0 18>; + }; + + dtable: clkdiv@0 { + #hisilicon,clkdiv-table-cells = <2>; + }; + + div_cfgaxi: clkdiv@2 { + compatible = "hisilicon,hi3620-clk-div"; + #clock-cells = <0>; + clocks = <&div_shareaxi>; + clock-output-names = "cfgAXI_div"; + hisilicon,clkdiv-table = <&dtable 0x01 2>; + hisilicon,clkdiv = <0x100 0x60>; + }; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 137d3e7..522e8d1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o +obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3xxx.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ diff --git a/drivers/clk/clk-hi3xxx.c b/drivers/clk/clk-hi3xxx.c new file mode 100644 index 0000000..14c2f80 --- /dev/null +++ b/drivers/clk/clk-hi3xxx.c @@ -0,0 +1,414 @@ +/* + * Hisilicon clock driver + * + * Copyright (c) 2012-2013 Hisilicon Limited. + * Copyright (c) 2012-2013 Linaro Limited. + * + * Author: Haojian Zhuang + * Xin Li + * + * 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 +#include +#include + +#define HI3620_DISABLE_OFF 0x4 +#define HI3620_STATUS_OFF 0x8 + +enum { + HI3620_SCTRL, + HI3XXX_MAX, +}; + +struct hi3620_periclk { + struct clk_hw hw; + void __iomem *enable; /* enable register */ + void __iomem *reset; /* reset register */ + u32 ebits; /* bits in enable/disable register */ + u32 rbits; /* bits in reset/unreset register */ + spinlock_t *lock; +}; + +struct hs_clk { + void __iomem *pmctrl; + void __iomem *sctrl; + spinlock_t lock; +}; + +static void __iomem *hi3xxx_clk_base[HI3XXX_MAX]; + +static DEFINE_SPINLOCK(hi3xxx_clk_lock); + +static const struct of_device_id hi3xxx_of_match[] = { + { .compatible = "hisilicon,sctrl", .data = (void *)HI3620_SCTRL, }, +}; + +static void __iomem __init *hi3xxx_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(hi3xxx_of_match, parent); + if (!match) + goto out; + + i = (unsigned int)match->data; + switch (i) { + case HI3620_SCTRL: + if (!hi3xxx_clk_base[i]) { + ret = of_iomap(parent, 0); + WARN_ON(!ret); + hi3xxx_clk_base[i] = ret; + } else { + ret = hi3xxx_clk_base[i]; + } + break; + default: + goto out; + } +out: + return ret; +} + +static int hi3620_clkgate_prepare(struct clk_hw *hw) +{ + struct hi3620_periclk *pclk; + unsigned long flags = 0; + + pclk = container_of(hw, struct hi3620_periclk, hw); + + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + if (pclk->reset) { + writel_relaxed(pclk->rbits, pclk->reset + HI3620_DISABLE_OFF); + readl_relaxed(pclk->reset + HI3620_STATUS_OFF); + } + if (pclk->lock) + spin_unlock_irqrestore(pclk->lock, flags); + return 0; +} + +static int hi3620_clkgate_enable(struct clk_hw *hw) +{ + struct hi3620_periclk *pclk; + unsigned long flags = 0; + + pclk = container_of(hw, struct hi3620_periclk, hw); + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + writel_relaxed(pclk->ebits, pclk->enable); + readl_relaxed(pclk->enable + HI3620_STATUS_OFF); + if (pclk->lock) + spin_unlock_irqrestore(pclk->lock, flags); + return 0; +} + +static void hi3620_clkgate_disable(struct clk_hw *hw) +{ + struct hi3620_periclk *pclk; + unsigned long flags = 0; + + pclk = container_of(hw, struct hi3620_periclk, hw); + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + writel_relaxed(pclk->ebits, pclk->enable + HI3620_DISABLE_OFF); + readl_relaxed(pclk->enable + HI3620_STATUS_OFF); + if (pclk->lock) + spin_unlock_irqrestore(pclk->lock, flags); +} + +static struct clk_ops hi3620_clkgate_ops = { + .prepare = hi3620_clkgate_prepare, + .enable = hi3620_clkgate_enable, + .disable = hi3620_clkgate_disable, +}; + +static void __init hi3620_clkgate_setup(struct device_node *np) +{ + struct hi3620_periclk *pclk; + struct clk_init_data *init; + struct clk *clk; + const char *clk_name, *name, **parent_names; + u32 rdata[2], gdata[2]; + void __iomem *base; + + base = hi3xxx_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, "hisilicon,hi3620-clkgate", + &gdata[0], 2)) + return; + + /* gate 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); + + pclk = kzalloc(sizeof(*pclk), GFP_KERNEL); + if (!pclk) + goto err_pclk; + + init = kzalloc(sizeof(*init), GFP_KERNEL); + if (!init) + goto err_init; + init->name = kstrdup(clk_name, GFP_KERNEL); + init->ops = &hi3620_clkgate_ops; + init->flags = CLK_SET_RATE_PARENT; + init->parent_names = parent_names; + init->num_parents = 1; + + if (!of_property_read_u32_array(np, "hisilicon,hi3620-clkreset", + &rdata[0], 2)) { + pclk->reset = base + rdata[0]; + pclk->rbits = rdata[1]; + } + pclk->enable = base + gdata[0]; + pclk->ebits = gdata[1]; + pclk->lock = &hi3xxx_clk_lock; + pclk->hw.init = init; + + clk = clk_register(NULL, &pclk->hw); + if (IS_ERR(clk)) + goto err_clk; + if (!of_property_read_string(np, "clock-names", &name)) + clk_register_clkdev(clk, name, NULL); + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err_clk: + kfree(init); +err_init: + kfree(pclk); +err_pclk: + kfree(parent_names); +} +CLK_OF_DECLARE(hi3620_gate, "hisilicon,hi3620-clk-gate", hi3620_clkgate_setup) + +static int __init hi3xxx_parse_mux(struct device_node *np, + u8 *num_parents, + u32 *table) +{ + 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)) + return -ENOENT; + } + *num_parents = cnt; + table = kzalloc(sizeof(u32 *) * cnt, GFP_KERNEL); + if (!table) + return -ENOMEM; + ret = of_property_read_u32_array(np, "hisilicon,clkmux-table", + table, cnt); + if (ret) + goto err; + return 0; +err: + kfree(table); + return ret; +} + +static void __init clkmux_setup(struct device_node *np, int mode) +{ + struct clk *clk; + const char *clk_name, **parent_names = NULL; + u32 rdata[2], mask, *table = NULL; + u8 num_parents, shift, clk_mux_flags = 0; + void __iomem *reg, *base; + int i, ret; + + base = hi3xxx_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, "hisilicon,clkmux-reg", + &rdata[0], 2)) + return; + ret = hi3xxx_parse_mux(np, &num_parents, table); + if (ret) + return; + + 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); + + reg = base + rdata[0]; + shift = ffs(rdata[1]) - 1; + mask = rdata[1] >> shift; + if (mode) + clk_mux_flags = CLK_MUX_HIWORD_MASK; + clk = clk_register_mux_table(NULL, clk_name, parent_names, num_parents, + CLK_SET_RATE_PARENT, reg, shift, mask, + clk_mux_flags, table, &hi3xxx_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(table); +} + +static void __init hi3620_clkmux_setup(struct device_node *np) +{ + clkmux_setup(np, 1); +} +CLK_OF_DECLARE(hi3620_mux, "hisilicon,hi3620-clk-mux", hi3620_clkmux_setup) + +static void __init hi3xxx_clkmux_setup(struct device_node *np) +{ + clkmux_setup(np, 0); +} +CLK_OF_DECLARE(hi3xxx_mux, "hisilicon,clk-mux", hi3xxx_clkmux_setup) + +static void __init hs_clkgate_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, **parent_names, *name; + unsigned long flags = 0; + u32 data[2]; + void __iomem *base; + + base = hi3xxx_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, "hisilicon,clkgate", + &data[0], 2)) + return; + if (of_property_read_bool(np, "hisilicon,clkgate-inverted")) + flags = CLK_GATE_SET_TO_DISABLE; + /* gate 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 = clk_register_gate(NULL, clk_name, parent_names[0], 0, + base + data[0], (u8)data[1], flags, + &hi3xxx_clk_lock); + if (IS_ERR(clk)) + goto err; + if (!of_property_read_string(np, "clock-names", &name)) + clk_register_clkdev(clk, name, NULL); + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err: + kfree(parent_names); +} +CLK_OF_DECLARE(hs_gate, "hisilicon,clk-gate", hs_clkgate_setup) + +void __init hi3620_clkdiv_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, **parent_names; + struct clk_div_table *table; + unsigned int table_num; + int i; + u32 data[2]; + u8 shift, width; + const char *propname = "hisilicon,clkdiv-table"; + const char *cellname = "#hisilicon,clkdiv-table-cells"; + struct of_phandle_args div_table; + void __iomem *reg, *base; + + base = hi3xxx_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, "hisilicon,clkdiv", + &data[0], 2)) + return; + + /*process the div_table*/ + for (i = 0; ; i++) { + if (of_parse_phandle_with_args(np, propname, cellname, + i, &div_table)) + break; + } + + /*table ends with <0, 0>, so plus one to table_num*/ + table_num = i + 1; + + table = kzalloc(sizeof(struct clk_div_table) * table_num, GFP_KERNEL); + if (!table) + return ; + + for (i = 0; ; i++) { + if (of_parse_phandle_with_args(np, propname, cellname, + i, &div_table)) + break; + + table[i].val = div_table.args[0]; + table[i].div = div_table.args[1]; + } + + /* gate only has the fixed parent */ + parent_names = kzalloc(sizeof(char *), GFP_KERNEL); + if (!parent_names) + goto err_par; + parent_names[0] = of_clk_get_parent_name(np, 0); + reg = base + data[0]; + shift = ffs(data[1]) - 1; + width = fls(data[1]) - ffs(data[1]) + 1; + clk = clk_register_divider_table(NULL, clk_name, parent_names[0], 0, + reg, shift, width, + CLK_DIVIDER_HIWORD_MASK, + table, &hi3xxx_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_par: + kfree(table); +} +CLK_OF_DECLARE(hi3620_div, "hisilicon,hi3620-clk-div", hi3620_clkdiv_setup)