From patchwork Wed Nov 19 10:32:21 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Carlo Caione X-Patchwork-Id: 5335971 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 38BC8C11AC for ; Wed, 19 Nov 2014 10:37:55 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id AA9022018E for ; Wed, 19 Nov 2014 10:37:53 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9A751200E9 for ; Wed, 19 Nov 2014 10:37:52 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Xr2bY-0001KK-EX; Wed, 19 Nov 2014 10:35:20 +0000 Received: from mail-wg0-x234.google.com ([2a00:1450:400c:c00::234]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Xr2ZC-0005ix-Om for linux-arm-kernel@lists.infradead.org; Wed, 19 Nov 2014 10:32:57 +0000 Received: by mail-wg0-f52.google.com with SMTP id a1so414054wgh.39 for ; Wed, 19 Nov 2014 02:32:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=KB2UwG6/lVDkqVvHIOe3b/0QYe2l3hZqWBGK3o355oA=; b=iya7y0tn0S9SULr3CjzoUEWQB7dBycrSJOBeytl54nccedvLpppUQRJ5n0UVkuO2ld YTh2tafXg+H8jeKKxKVDM/9dvL+usX1V9ev3/T+U6k5JFiEZx2EbKDC3uJbSHwvfxJAI NcUviZFOSAouiK0KN1R76to3HaKuGhhWFbP9ekwjTzQNOWdLGArUBfFSJtPhltAtEIkc wphoJ1zem3pgENjlAnZHI3xJFTDHPWUaBzTECkHDZKuk3icNmip4azbJCezCFYXvq7Bl J3f8GyY4yTY7I97gb+3VzjV7NuEVHfFRBvowGbEo2FCONmvczIXvInVedqcqKXKh+m6T 1JXg== X-Received: by 10.194.77.4 with SMTP id o4mr51331380wjw.41.1416393152804; Wed, 19 Nov 2014 02:32:32 -0800 (PST) Received: from localhost.localdomain (host146-114-dynamic.0-87-r.retail.telecomitalia.it. [87.0.114.146]) by mx.google.com with ESMTPSA id dc8sm1623493wib.7.2014.11.19.02.32.31 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 19 Nov 2014 02:32:32 -0800 (PST) From: Carlo Caione To: mturquette@linaro.org, linux-arm-kernel@lists.infradead.org, b.galvani@gmail.com, jerry.cao@amlogic.com, victor.wan@amlogic.com, eric.hankinson@leandogsoftware.com, maxime.ripard@free-electrons.com, emilio@elopez.com.ar, robh+dt@kernel.org, p.zabel@pengutronix.de Subject: [PATCH 2/4] ARM: meson: add reset controller Date: Wed, 19 Nov 2014 11:32:21 +0100 Message-Id: <1416393143-20434-3-git-send-email-carlo@caione.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1416393143-20434-1-git-send-email-carlo@caione.org> References: <1416393143-20434-1-git-send-email-carlo@caione.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20141119_023255_172000_6B1A6734 X-CRM114-Status: GOOD ( 20.34 ) X-Spam-Score: -0.7 (/) Cc: Carlo Caione X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 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=-2.5 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_LOW, T_DKIM_INVALID, T_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 Modules and submodules within Meson6 and Meson8 SoCs can be disabled by shutting off the clock. The control for these clocks comes from several registers that are mapped contiguously except a special register that controls peripherals in the AO (Always-On) power domain. The reset controller manages both the cases according to the reset ID. Signed-off-by: Carlo Caione --- drivers/clk/meson/Makefile | 1 + drivers/clk/meson/clkc.h | 15 ++++ drivers/clk/meson/meson6-clkc.c | 18 +++++ drivers/clk/meson/rstc.c | 152 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 drivers/clk/meson/rstc.c diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 8f102e4..a962d57 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -5,3 +5,4 @@ obj-y += clkc.o obj-y += meson6-clkc.o obj-y += clk-pll.o +obj-$(CONFIG_RESET_CONTROLLER) += rstc.o diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h index 6c0f611..41e92df 100644 --- a/drivers/clk/meson/clkc.h +++ b/drivers/clk/meson/clkc.h @@ -157,3 +157,18 @@ void meson_clk_register_pll_div(struct pll_div_conf *pll_div_conf, void meson_clk_register_clks(struct clk_conf *clk_confs, unsigned int nr_confs, void __iomem *clk_base); + +#ifdef CONFIG_RESET_CONTROLLER +void meson_register_rstc(struct device_node *np, unsigned int num_regs, + void __iomem *ao_base, void __iomem *ot_base, + unsigned int ao_off_id, u8 flags); +#else +static inline void meson_register_rstc(struct device_node *np, + unsigned int num_regs, + void __iomem *ao_base, + void __iomem *ot_base, + unsigned int ao_off_id, u8 flags) +{ +} +#endif +#endif /* __CLKC_H */ diff --git a/drivers/clk/meson/meson6-clkc.c b/drivers/clk/meson/meson6-clkc.c index 9f809e1..177c6fa 100644 --- a/drivers/clk/meson/meson6-clkc.c +++ b/drivers/clk/meson/meson6-clkc.c @@ -30,9 +30,16 @@ #define MESON6_REG_DPLL_VID2 0x011c #define MESON6_REG_HPLL 0x0270 #define MESON6_REG_HHI_MPEG 0x0174 +#define MESON6_REG_RSTC 0x0140 #define MESON6_XTAL "xtal" +/* + * Every reset ID >= 163 is mapped to the AO domain register + */ +#define MESON6_RSTC_N_REGS 6 +#define MESON6_AO_OFF ((MESON6_RSTC_N_REGS - 1) * BITS_PER_LONG + 3) + static struct pll_conf dpll_conf = PLL_CONF2(750, 1512, PARM(0x00, 0, 9), PARM(0x00, 9, 5)); @@ -129,6 +136,7 @@ static struct clk_conf meson_clk_confs[] __initdata = { static void __init meson_clkc_init(struct device_node *np) { void __iomem *clk_base; + void __iomem *ao_base; u32 xtal_rate; /* XTAL */ @@ -156,5 +164,15 @@ static void __init meson_clkc_init(struct device_node *np) clk_base); meson_clk_register_pll_divs(meson_pll_divs, ARRAY_SIZE(meson_pll_divs), clk_base); + + /* Reset controller */ + ao_base = of_iomap(np, 2); + if (!ao_base) { + pr_warn("%s: Unable to map ao domain base for reset controller\n", __func__); + return; + } + + meson_register_rstc(np, MESON6_RSTC_N_REGS, ao_base, + clk_base + MESON6_REG_RSTC, MESON6_AO_OFF, 0); } CLK_OF_DECLARE(meson6_clock, "amlogic,meson6-clkc", meson_clkc_init); diff --git a/drivers/clk/meson/rstc.c b/drivers/clk/meson/rstc.c new file mode 100644 index 0000000..493f789 --- /dev/null +++ b/drivers/clk/meson/rstc.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014 Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +#include +#include +#include +#include + +#include "clkc.h" + +/* + * Modules and submodules within the chip can be reset by disabling the + * clock and enabling it again. + * The modules in the AO (Always-On) domain are controlled by a different + * register mapped in a different memory region accessed by the ao_base. + * + */ + +struct meson_rstc { + struct reset_controller_dev rcdev; + void __iomem *ao_base; + void __iomem *ot_base; + unsigned int num_regs; + unsigned int ao_off_id; + u8 flags; + spinlock_t lock; +}; + +static int meson_rstc_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct meson_rstc *rstc = container_of(rcdev, + struct meson_rstc, + rcdev); + int bank = id / BITS_PER_LONG; + int offset; + void __iomem *rstc_mem; + unsigned long flags; + u32 reg; + + /* + * The higher IDs are used for the AO domain register + */ + if (id >= rstc->ao_off_id) { + offset = id - rstc->ao_off_id; + rstc_mem = rstc->ao_base; + } else { + offset = id % BITS_PER_LONG; + rstc_mem = rstc->ot_base + (bank << 2); + } + + spin_lock_irqsave(&rstc->lock, flags); + + reg = readl(rstc_mem); + writel(reg & ~BIT(offset), rstc_mem); + + spin_unlock_irqrestore(&rstc->lock, flags); + + return 0; +} + +static int meson_rstc_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct meson_rstc *rstc = container_of(rcdev, + struct meson_rstc, + rcdev); + int bank = id / BITS_PER_LONG; + int offset; + void __iomem *rstc_mem; + unsigned long flags; + u32 reg; + + if (id >= rstc->ao_off_id) { + offset = id - rstc->ao_off_id; + rstc_mem = rstc->ao_base; + } else { + offset = id % BITS_PER_LONG; + rstc_mem = rstc->ot_base + (bank << 2); + } + + spin_lock_irqsave(&rstc->lock, flags); + + reg = readl(rstc_mem); + writel(reg | BIT(offset), rstc_mem); + + spin_unlock_irqrestore(&rstc->lock, flags); + + return 0; + +} + +static int meson_rstc_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + int err; + + err = meson_rstc_assert(rcdev, id); + if (err) + return err; + + return meson_rstc_deassert(rcdev, id); +} + +static struct reset_control_ops meson_rstc_ops = { + .assert = meson_rstc_assert, + .deassert = meson_rstc_deassert, + .reset = meson_rstc_reset, +}; + +void __init meson_register_rstc(struct device_node *np, unsigned int num_regs, + void __iomem *ao_base, void __iomem *ot_base, + unsigned int ao_off_id, u8 flags) +{ + struct meson_rstc *rstc; + int ret; + + rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return; + + spin_lock_init(&rstc->lock); + + rstc->ao_base = ao_base; + rstc->ot_base = ot_base; + rstc->num_regs = num_regs; + rstc->flags = flags; + + rstc->rcdev.owner = THIS_MODULE; + rstc->rcdev.nr_resets = num_regs * BITS_PER_LONG; + rstc->rcdev.of_node = np; + rstc->rcdev.ops = &meson_rstc_ops; + + ret = reset_controller_register(&rstc->rcdev); + if (ret) { + pr_err("%s: could not register reset controller: %d\n", + __func__, ret); + kfree(rstc); + } +}