From patchwork Mon Aug 5 16:12:20 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Santosh Shilimkar X-Patchwork-Id: 2838753 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 E0927BF535 for ; Mon, 5 Aug 2013 16:15:38 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E43F0200E0 for ; Mon, 5 Aug 2013 16:15:33 +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 1584D20237 for ; Mon, 5 Aug 2013 16:15:32 +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 1V6NQL-0001WG-QO; Mon, 05 Aug 2013 16:14:23 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1V6NPs-0008SC-EA; Mon, 05 Aug 2013 16:13:52 +0000 Received: from devils.ext.ti.com ([198.47.26.153]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1V6NOx-0008KZ-Lt for linux-arm-kernel@lists.infradead.org; Mon, 05 Aug 2013 16:13:07 +0000 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id r75GCWaM012919; Mon, 5 Aug 2013 11:12:32 -0500 Received: from DLEE71.ent.ti.com (dlee71.ent.ti.com [157.170.170.114]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id r75GCWek018779; Mon, 5 Aug 2013 11:12:32 -0500 Received: from dlelxv22.itg.ti.com (172.17.1.197) by DLEE71.ent.ti.com (157.170.170.114) with Microsoft SMTP Server id 14.2.342.3; Mon, 5 Aug 2013 11:12:32 -0500 Received: from ula0393909.am.dhcp.ti.com (ula0393909.am.dhcp.ti.com [158.218.103.117]) by dlelxv22.itg.ti.com (8.13.8/8.13.8) with ESMTP id r75GCVEK021067; Mon, 5 Aug 2013 11:12:32 -0500 From: Santosh Shilimkar To: Subject: [PATCH 1/8] clk: keystone: add Keystone PLL clock driver Date: Mon, 5 Aug 2013 12:12:20 -0400 Message-ID: <1375719147-7578-2-git-send-email-santosh.shilimkar@ti.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1375719147-7578-1-git-send-email-santosh.shilimkar@ti.com> References: <1375719147-7578-1-git-send-email-santosh.shilimkar@ti.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130805_121256_019857_B467E6EA X-CRM114-Status: GOOD ( 20.15 ) X-Spam-Score: -6.9 (------) Cc: Santosh Shilimkar , mturquette@linaro.org 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: , 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.2 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 Add the driver for the PLL IPs found on Keystone 2 devices. The main PLL IP typically has a multiplier, and a divider. The addtional PLL IPs like ARMPLL, DDRPLL and PAPLL are controlled by the memory mapped register where as the Main PLL is controlled by a PLL controller and memory map registers. This difference is handle using 'has_pll_cntrl' property. Cc: Mike Turquette Signed-off-by: Santosh Shilimkar --- .../devicetree/bindings/clock/keystone-pll.txt | 36 ++++ drivers/clk/keystone/pll.c | 197 ++++++++++++++++++++ include/linux/clk/keystone.h | 18 ++ 3 files changed, 251 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/keystone-pll.txt create mode 100644 drivers/clk/keystone/pll.c create mode 100644 include/linux/clk/keystone.h diff --git a/Documentation/devicetree/bindings/clock/keystone-pll.txt b/Documentation/devicetree/bindings/clock/keystone-pll.txt new file mode 100644 index 0000000..58f6470 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/keystone-pll.txt @@ -0,0 +1,36 @@ +Binding for keystone PLLs. The main PLL IP typically has a multiplier, +a divider and a post divider. The additional PLL IPs like ARMPLL, DDRPLL +and PAPLL are controlled by the memory mapped register where as the Main +PLL is controlled by a PLL controller. This difference is handle using +'pll_has_pllctrl' property. + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "keystone,pll-clk". +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : parent clock phandle +- reg - index 0 - PLLCTRL PLLM register address +- index 1 - MAINPLL_CTL0 register address +- pll_has_pllctrl - PLL is controlled by controller or memory mapped register +- pllm_lower_mask - pllm lower bit mask +- pllm_upper_mask - pllm upper bit mask +- plld_mask - plld mask +- fixed_postdiv - fixed post divider value + +Example: + clock { + #clock-cells = <0>; + compatible = "keystone,pll-clk"; + clocks = <&refclk>; + reg = <0x02310110 4 /* PLLCTRL PLLM */ + 0x02620328 4>; /* MAINPLL_CTL0 */ + pll_has_pllctrl; + pllm_lower_mask = <0x3f>; + pllm_upper_mask = <0x7f000>; + pllm_upper_shift = <6>; + plld_mask = <0x3f>; + fixed_postdiv = <2>; + }; diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c new file mode 100644 index 0000000..1453eea --- /dev/null +++ b/drivers/clk/keystone/pll.c @@ -0,0 +1,197 @@ +/* + * Main PLL clk driver for Keystone devices + * + * Copyright (C) 2013 Texas Instruments Inc. + * Murali Karicheri + * Santosh Shilimkar + * + * 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 +#include +#include +#include + +/** + * struct clk_pll_data - pll data structure + * @has_pllctrl: If set to non zero, lower 6 bits of multiplier is in pllm + * register of pll controller, else it is in the pll_ctrl0((bit 11-6) + * @phy_pllm: Physical address of PLLM in pll controller. Used when + * has_pllctrl is non zero. + * @phy_pll_ctl0: Physical address of PLL ctrl0. This could be that of + * Main PLL or any other PLLs in the device such as ARM PLL, DDR PLL + * or PA PLL available on keystone2. These PLLs are controlled by + * this register. Main PLL is controlled by a PLL controller. + * @pllm: PLL register map address + * @pll_ctl0: PLL controller map address + * @pllm_lower_mask: multiplier lower mask + * @pllm_upper_mask: multiplier upper mask + * @pllm_upper_shift: multiplier upper shift + * @plld_mask: divider mask + * @fixed_postdiv: Post divider + */ +struct clk_pll_data { + unsigned char has_pllctrl; + u32 phy_pllm; + u32 phy_pll_ctl0; + void __iomem *pllm; + void __iomem *pll_ctl0; + u32 pllm_lower_mask; + u32 pllm_upper_mask; + u32 pllm_upper_shift; + u32 plld_mask; + u32 fixed_postdiv; +}; + +/** + * struct clk_pll - Main pll clock + * @hw: clk_hw for the pll + * @pll_data: PLL driver specific data + */ +struct clk_pll { + struct clk_hw hw; + struct clk_pll_data *pll_data; +}; + +#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw) + +static unsigned long clk_pllclk_recalc(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct clk_pll_data *pll_data = pll->pll_data; + unsigned long rate = parent_rate; + u32 mult = 0, prediv, postdiv, val; + + /* + * get bits 0-5 of multiplier from pllctrl PLLM register + * if has_pllctrl is non zero */ + if (pll_data->has_pllctrl) { + val = readl(pll_data->pllm); + mult = (val & pll_data->pllm_lower_mask); + } + + /* bit6-12 of PLLM is in Main PLL control register */ + val = readl(pll_data->pll_ctl0); + mult |= ((val & pll_data->pllm_upper_mask) + >> pll_data->pllm_upper_shift); + prediv = (val & pll_data->plld_mask); + postdiv = pll_data->fixed_postdiv; + + rate /= (prediv + 1); + rate = (rate * (mult + 1)); + rate /= postdiv; + + return rate; +} + +static const struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pllclk_recalc, +}; + +static struct clk *clk_register_pll(struct device *dev, + const char *name, + const char *parent_name, + struct clk_pll_data *pll_data) +{ + struct clk_init_data init; + struct clk_pll *pll; + struct clk *clk; + + if (!pll_data) + return ERR_PTR(-ENODEV); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_pll_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + pll->pll_data = pll_data; + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) + goto out; + + return clk; +out: + kfree(pll); + return NULL; +} + +/** + * of_keystone_pll_clk_init - PLL initialisation via DT + * @node: device tree node for this clock + */ +void __init of_keystone_pll_clk_init(struct device_node *node) +{ + struct clk_pll_data *pll_data; + const char *parent_name; + struct clk *clk; + int temp; + + pll_data = kzalloc(sizeof(*pll_data), GFP_KERNEL); + WARN_ON(!pll_data); + + parent_name = of_clk_get_parent_name(node, 0); + + if (of_find_property(node, "pll_has_pllctrl", &temp)) { + /* PLL is controlled by the pllctrl */ + pll_data->has_pllctrl = 1; + pll_data->pllm = of_iomap(node, 0); + WARN_ON(!pll_data->pllm); + + pll_data->pll_ctl0 = of_iomap(node, 1); + WARN_ON(!pll_data->pll_ctl0); + + if (of_property_read_u32(node, "pllm_lower_mask", + &pll_data->pllm_lower_mask)) + goto out; + + } else { + /* PLL is controlled by the ctrl register */ + pll_data->has_pllctrl = 0; + pll_data->pll_ctl0 = of_iomap(node, 0); + } + + if (of_property_read_u32(node, "pllm_upper_mask", + &pll_data->pllm_upper_mask)) + goto out; + + if (of_property_read_u32(node, "pllm_upper_shift", + &pll_data->pllm_upper_shift)) + goto out; + + if (of_property_read_u32(node, "plld_mask", &pll_data->plld_mask)) + goto out; + + if (of_property_read_u32(node, "fixed_postdiv", + &pll_data->fixed_postdiv)) + goto out; + + clk = clk_register_pll(NULL, node->name, parent_name, + pll_data); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; + } +out: + pr_err("of_keystone_pll_clk_init - error initializing clk %s\n", + node->name); + kfree(pll_data); +} +EXPORT_SYMBOL_GPL(of_keystone_pll_clk_init); +CLK_OF_DECLARE(keystone_pll_clk, "keystone,pll-clk", of_keystone_pll_clk_init); diff --git a/include/linux/clk/keystone.h b/include/linux/clk/keystone.h new file mode 100644 index 0000000..1ade95d --- /dev/null +++ b/include/linux/clk/keystone.h @@ -0,0 +1,18 @@ +/* + * Keystone Clock driver header + * + * Copyright (C) 2013 Texas Instruments Inc. + * Santosh Shilimkar + * + * 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. + */ + +#ifndef __LINUX_CLK_KEYSTONE_H_ +#define __LINUX_CLK_KEYSTONE_H_ + +extern void of_keystone_pll_clk_init(struct device_node *node); + +#endif /* __LINUX_CLK_KEYSTONE_H_ */