From patchwork Mon Aug 27 13:45:50 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ulf Hansson X-Patchwork-Id: 10974 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id E820523E29 for ; Mon, 27 Aug 2012 13:46:27 +0000 (UTC) Received: from mail-iy0-f180.google.com (mail-iy0-f180.google.com [209.85.210.180]) by fiordland.canonical.com (Postfix) with ESMTP id 278DAA18C50 for ; Mon, 27 Aug 2012 13:46:01 +0000 (UTC) Received: by iafj25 with SMTP id j25so5583945iaf.11 for ; Mon, 27 Aug 2012 06:46:27 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf:from:to:cc :subject:date:message-id:x-mailer:in-reply-to:references :mime-version:content-type:x-gm-message-state; bh=5JyghvF3e8PUSSTz5PEM2hYQVBcmwaYBX8vlL8W6nQk=; b=mrrNX2lIYbDuDf9z0lWUqrOjaVIkbvXYRIbJyaIEErrKO4WETJiNDXXb6980rk4g3c x29zcFRs4bMDzMsLI0k0ZslNpPIoL5TrWqL3Lf+Z9b+oGyCBU/ZybAR2vpziDs8nse4J JHE2ZVGnm+l+v8hOA6JFiowOJj58uIzO4I53Upu2tC6NHpQD/WtDxTOYBr8lttJuDab0 ZPgsHllAol0Bt81HbMUrWKag4X7b+FOXjxAQafcGDUEW/dqAclKBfawU6rywvytVKvlS 6YMEetSuCXLvVD/NSbMFPeinTMOWxEPlpe2yPA79hNbpxV66xR79zhwvbfJrrx3IKiL/ LUIQ== Received: by 10.50.207.106 with SMTP id lv10mr10336930igc.0.1346075186921; Mon, 27 Aug 2012 06:46:26 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.50.184.232 with SMTP id ex8csp441812igc; Mon, 27 Aug 2012 06:46:25 -0700 (PDT) Received: by 10.14.202.131 with SMTP id d3mr17959021eeo.32.1346075185137; Mon, 27 Aug 2012 06:46:25 -0700 (PDT) Received: from eu1sys200aog113.obsmtp.com (eu1sys200aog113.obsmtp.com. [207.126.144.135]) by mx.google.com with SMTP id f45si12052687eep.69.2012.08.27.06.46.17 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 27 Aug 2012 06:46:25 -0700 (PDT) Received-SPF: neutral (google.com: 207.126.144.135 is neither permitted nor denied by best guess record for domain of ulf.hansson@stericsson.com) client-ip=207.126.144.135; Authentication-Results: mx.google.com; spf=neutral (google.com: 207.126.144.135 is neither permitted nor denied by best guess record for domain of ulf.hansson@stericsson.com) smtp.mail=ulf.hansson@stericsson.com Received: from beta.dmz-eu.st.com ([164.129.1.35]) (using TLSv1) by eu1sys200aob113.postini.com ([207.126.147.11]) with SMTP ID DSNKUDt6KJtrOWrAAfE3pKylfLOdDypH31lv@postini.com; Mon, 27 Aug 2012 13:46:25 UTC Received: from zeta.dmz-eu.st.com (zeta.dmz-eu.st.com [164.129.230.9]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 5534B101; Mon, 27 Aug 2012 13:46:15 +0000 (GMT) Received: from relay1.stm.gmessaging.net (unknown [10.230.100.17]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id D2A684836; Mon, 27 Aug 2012 13:46:14 +0000 (GMT) Received: from exdcvycastm004.EQ1STM.local (alteon-source-exch [10.230.100.61]) (using TLSv1 with cipher RC4-MD5 (128/128 bits)) (Client CN "exdcvycastm004", Issuer "exdcvycastm004" (not verified)) by relay1.stm.gmessaging.net (Postfix) with ESMTPS id 2F3BD24C07C; Mon, 27 Aug 2012 15:46:07 +0200 (CEST) Received: from steludxu1397.lud.stericsson.com (10.230.100.153) by smtp.stericsson.com (10.230.100.2) with Microsoft SMTP Server (TLS) id 8.3.83.0; Mon, 27 Aug 2012 15:46:14 +0200 From: Ulf Hansson To: , Mike Turquette , Mike Turquette Cc: Linus Walleij , Lee Jones , Philippe Begnic , Par-Olof Hakansson , Arnd Bergmann , Ulf Hansson Subject: [PATCH V2 1/4] clk: ux500: Adapt PRCMU and PRCC clocks for common clk Date: Mon, 27 Aug 2012 15:45:50 +0200 Message-ID: <1346075153-14180-2-git-send-email-ulf.hansson@stericsson.com> X-Mailer: git-send-email 1.7.10 In-Reply-To: <1346075153-14180-1-git-send-email-ulf.hansson@stericsson.com> References: <1346075153-14180-1-git-send-email-ulf.hansson@stericsson.com> MIME-Version: 1.0 X-Gm-Message-State: ALoCoQlYWzfnmuhD6uJS0AEWQUVitdRn+4l9Pyx73NBvKE9g5s027FVNPZZm3vdDRtZEJEOPdM7b From: Ulf Hansson First version of common clock implementation of PRCMU clocks and PRCC clocks for ux500 platforms. Signed-off-by: Ulf Hansson --- Changes in v2: - Remove use of "__io_address" and use "ioremap" instead. --- drivers/clk/ux500/Makefile | 7 ++ drivers/clk/ux500/clk-prcc.c | 164 ++++++++++++++++++++++++++++ drivers/clk/ux500/clk-prcmu.c | 238 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/ux500/clk.h | 43 ++++++++ 4 files changed, 452 insertions(+) create mode 100644 drivers/clk/ux500/Makefile create mode 100644 drivers/clk/ux500/clk-prcc.c create mode 100644 drivers/clk/ux500/clk-prcmu.c create mode 100644 drivers/clk/ux500/clk.h diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile new file mode 100644 index 0000000..a3ccd1b --- /dev/null +++ b/drivers/clk/ux500/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for ux500 clocks +# + +# Clock types +obj-y += clk-prcc.o +obj-y += clk-prcmu.o diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c new file mode 100644 index 0000000..7eee7f7 --- /dev/null +++ b/drivers/clk/ux500/clk-prcc.c @@ -0,0 +1,164 @@ +/* + * PRCC clock implementation for ux500 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define PRCC_PCKEN 0x000 +#define PRCC_PCKDIS 0x004 +#define PRCC_KCKEN 0x008 +#define PRCC_KCKDIS 0x00C +#define PRCC_PCKSR 0x010 +#define PRCC_KCKSR 0x014 + +#define to_clk_prcc(_hw) container_of(_hw, struct clk_prcc, hw) + +struct clk_prcc { + struct clk_hw hw; + void __iomem *base; + u32 cg_sel; + int is_enabled; +}; + +/* PRCC clock operations. */ + +static int clk_prcc_pclk_enable(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + + writel(clk->cg_sel, (clk->base + PRCC_PCKEN)); + while (!(readl(clk->base + PRCC_PCKSR) & clk->cg_sel)) + cpu_relax(); + + clk->is_enabled = 1; + return 0; +} + +static void clk_prcc_pclk_disable(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + + writel(clk->cg_sel, (clk->base + PRCC_PCKDIS)); + clk->is_enabled = 0; +} + +static int clk_prcc_kclk_enable(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + + writel(clk->cg_sel, (clk->base + PRCC_KCKEN)); + while (!(readl(clk->base + PRCC_KCKSR) & clk->cg_sel)) + cpu_relax(); + + clk->is_enabled = 1; + return 0; +} + +static void clk_prcc_kclk_disable(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + + writel(clk->cg_sel, (clk->base + PRCC_KCKDIS)); + clk->is_enabled = 0; +} + +static int clk_prcc_is_enabled(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + return clk->is_enabled; +} + +static struct clk_ops clk_prcc_pclk_ops = { + .enable = clk_prcc_pclk_enable, + .disable = clk_prcc_pclk_disable, + .is_enabled = clk_prcc_is_enabled, +}; + +static struct clk_ops clk_prcc_kclk_ops = { + .enable = clk_prcc_kclk_enable, + .disable = clk_prcc_kclk_disable, + .is_enabled = clk_prcc_is_enabled, +}; + +static struct clk *clk_reg_prcc(const char *name, + const char *parent_name, + resource_size_t phy_base, + u32 cg_sel, + unsigned long flags, + struct clk_ops *clk_prcc_ops) +{ + struct clk_prcc *clk; + struct clk_init_data clk_prcc_init; + struct clk *clk_reg; + + if (!name) { + pr_err("clk_prcc: %s invalid arguments passed\n", __func__); + return ERR_PTR(-EINVAL); + } + + clk = kzalloc(sizeof(struct clk_prcc), GFP_KERNEL); + if (!clk) { + pr_err("clk_prcc: %s could not allocate clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + clk->base = ioremap(phy_base, SZ_4K); + if (!clk->base) + goto free_clk; + + clk->cg_sel = cg_sel; + clk->is_enabled = 1; + + clk_prcc_init.name = name; + clk_prcc_init.ops = clk_prcc_ops; + clk_prcc_init.flags = flags; + clk_prcc_init.parent_names = (parent_name ? &parent_name : NULL); + clk_prcc_init.num_parents = (parent_name ? 1 : 0); + clk->hw.init = &clk_prcc_init; + + clk_reg = clk_register(NULL, &clk->hw); + if (IS_ERR_OR_NULL(clk_reg)) + goto unmap_clk; + + return clk_reg; + +unmap_clk: + iounmap(clk->base); +free_clk: + kfree(clk); + pr_err("clk_prcc: %s failed to register clk\n", __func__); + return ERR_PTR(-ENOMEM); +} + +struct clk *clk_reg_prcc_pclk(const char *name, + const char *parent_name, + resource_size_t phy_base, + u32 cg_sel, + unsigned long flags) +{ + return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags, + &clk_prcc_pclk_ops); +} + +struct clk *clk_reg_prcc_kclk(const char *name, + const char *parent_name, + resource_size_t phy_base, + u32 cg_sel, + unsigned long flags) +{ + return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags, + &clk_prcc_kclk_ops); +} diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c new file mode 100644 index 0000000..1d779ad --- /dev/null +++ b/drivers/clk/ux500/clk-prcmu.c @@ -0,0 +1,238 @@ +/* + * PRCMU clock implementation for ux500 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include "clk.h" + +#define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw) + +struct clk_prcmu { + struct clk_hw hw; + u8 cg_sel; + int is_enabled; +}; + +/* PRCMU clock operations. */ + +static int clk_prcmu_prepare(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return prcmu_request_clock(clk->cg_sel, true); +} + +static void clk_prcmu_unprepare(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + if (prcmu_request_clock(clk->cg_sel, false)) + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + hw->init->name); +} + +static int clk_prcmu_enable(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + clk->is_enabled = 1; + return 0; +} + +static void clk_prcmu_disable(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + clk->is_enabled = 0; +} + +static int clk_prcmu_is_enabled(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return clk->is_enabled; +} + +static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return prcmu_clock_rate(clk->cg_sel); +} + +static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return prcmu_round_clock_rate(clk->cg_sel, rate); +} + +static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return prcmu_set_clock_rate(clk->cg_sel, rate); +} + +static int request_ape_opp100(bool enable) +{ + static int reqs; + int err = 0; + + if (enable) { + if (!reqs) + err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, + "clock", 100); + if (!err) + reqs++; + } else { + reqs--; + if (!reqs) + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + "clock"); + } + return err; +} + +static int clk_prcmu_opp_prepare(struct clk_hw *hw) +{ + int err; + struct clk_prcmu *clk = to_clk_prcmu(hw); + + err = request_ape_opp100(true); + if (err) { + pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n", + __func__, hw->init->name); + return err; + } + + err = prcmu_request_clock(clk->cg_sel, true); + if (err) + request_ape_opp100(false); + + return err; +} + +static void clk_prcmu_opp_unprepare(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + + if (prcmu_request_clock(clk->cg_sel, false)) + goto out_error; + if (request_ape_opp100(false)) + goto out_error; + return; + +out_error: + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + hw->init->name); +} + +static struct clk_ops clk_prcmu_scalable_ops = { + .prepare = clk_prcmu_prepare, + .unprepare = clk_prcmu_unprepare, + .enable = clk_prcmu_enable, + .disable = clk_prcmu_disable, + .is_enabled = clk_prcmu_is_enabled, + .recalc_rate = clk_prcmu_recalc_rate, + .round_rate = clk_prcmu_round_rate, + .set_rate = clk_prcmu_set_rate, +}; + +static struct clk_ops clk_prcmu_gate_ops = { + .prepare = clk_prcmu_prepare, + .unprepare = clk_prcmu_unprepare, + .enable = clk_prcmu_enable, + .disable = clk_prcmu_disable, + .is_enabled = clk_prcmu_is_enabled, + .recalc_rate = clk_prcmu_recalc_rate, +}; + +static struct clk_ops clk_prcmu_opp_gate_ops = { + .prepare = clk_prcmu_opp_prepare, + .unprepare = clk_prcmu_opp_unprepare, + .enable = clk_prcmu_enable, + .disable = clk_prcmu_disable, + .is_enabled = clk_prcmu_is_enabled, + .recalc_rate = clk_prcmu_recalc_rate, +}; + +static struct clk *clk_reg_prcmu(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long rate, + unsigned long flags, + struct clk_ops *clk_prcmu_ops) +{ + struct clk_prcmu *clk; + struct clk_init_data clk_prcmu_init; + struct clk *clk_reg; + + if (!name) { + pr_err("clk_prcmu: %s invalid arguments passed\n", __func__); + return ERR_PTR(-EINVAL); + } + + clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL); + if (!clk) { + pr_err("clk_prcmu: %s could not allocate clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + clk->cg_sel = cg_sel; + clk->is_enabled = 1; + /* "rate" can be used for changing the initial frequency */ + if (rate) + prcmu_set_clock_rate(cg_sel, rate); + + clk_prcmu_init.name = name; + clk_prcmu_init.ops = clk_prcmu_ops; + clk_prcmu_init.flags = flags; + clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL); + clk_prcmu_init.num_parents = (parent_name ? 1 : 0); + clk->hw.init = &clk_prcmu_init; + + clk_reg = clk_register(NULL, &clk->hw); + if (IS_ERR_OR_NULL(clk_reg)) + goto free_clk; + + return clk_reg; + +free_clk: + kfree(clk); + pr_err("clk_prcmu: %s failed to register clk\n", __func__); + return ERR_PTR(-ENOMEM); +} + +struct clk *clk_reg_prcmu_scalable(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long rate, + unsigned long flags) +{ + return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, + &clk_prcmu_scalable_ops); +} + +struct clk *clk_reg_prcmu_gate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags) +{ + return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, + &clk_prcmu_gate_ops); +} + +struct clk *clk_reg_prcmu_opp_gate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags) +{ + return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, + &clk_prcmu_opp_gate_ops); +} diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h new file mode 100644 index 0000000..32085aa --- /dev/null +++ b/drivers/clk/ux500/clk.h @@ -0,0 +1,43 @@ +/* + * Clocks for ux500 platforms + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __UX500_CLK_H +#define __UX500_CLK_H + +#include + +struct clk *clk_reg_prcc_pclk(const char *name, + const char *parent_name, + unsigned int phy_base, + u32 cg_sel, + unsigned long flags); + +struct clk *clk_reg_prcc_kclk(const char *name, + const char *parent_name, + unsigned int phy_base, + u32 cg_sel, + unsigned long flags); + +struct clk *clk_reg_prcmu_scalable(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long rate, + unsigned long flags); + +struct clk *clk_reg_prcmu_gate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags); + +struct clk *clk_reg_prcmu_opp_gate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags); + +#endif /* __UX500_CLK_H */