From patchwork Wed Aug 4 22:12:47 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benoit Cousson X-Patchwork-Id: 117160 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o74MCvL3010074 for ; Wed, 4 Aug 2010 22:13:01 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756588Ab0HDWNA (ORCPT ); Wed, 4 Aug 2010 18:13:00 -0400 Received: from comal.ext.ti.com ([198.47.26.152]:46321 "EHLO comal.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756576Ab0HDWM7 (ORCPT ); Wed, 4 Aug 2010 18:12:59 -0400 Received: from dlep36.itg.ti.com ([157.170.170.91]) by comal.ext.ti.com (8.13.7/8.13.7) with ESMTP id o74MCvPI004297 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 4 Aug 2010 17:12:57 -0500 Received: from localhost.localdomain (localhost [127.0.0.1]) by dlep36.itg.ti.com (8.13.8/8.13.8) with ESMTP id o74MCmkr001940; Wed, 4 Aug 2010 17:12:52 -0500 (CDT) From: Benoit Cousson To: linux-omap@vger.kernel.org, khilman@deeprootsystems.com, paul@pwsan.com Cc: rnayak@ti.com, santosh.shilimkar@ti.com, Benoit Cousson Subject: [PATCH 2/3] OMAP: hwmod: Add hardreset management support Date: Thu, 5 Aug 2010 00:12:47 +0200 Message-Id: <1280959968-23933-3-git-send-email-b-cousson@ti.com> X-Mailer: git-send-email 1.6.1.3 In-Reply-To: <1280959968-23933-1-git-send-email-b-cousson@ti.com> References: <1280959968-23933-1-git-send-email-b-cousson@ti.com> Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Wed, 04 Aug 2010 22:13:02 +0000 (UTC) diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 100115f..53b08e3 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -50,6 +50,7 @@ #include #include #include +#include #include "cm.h" @@ -909,6 +910,15 @@ int _omap_hwmod_enable(struct omap_hwmod *oh) pr_debug("omap_hwmod: %s: enabling\n", oh->name); + /* + * If an IP contains only one HW reset line, then de-assert it in order + * to allow to enable the clocks. Otherwise the PRCM will return + * Intransition status, and the init will failed. + */ + if ((oh->_state == _HWMOD_STATE_INITIALIZED || + oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1) + omap_hwmod_hardreset_deassert(oh, oh->rst_lines[0].name); + /* XXX mux balls */ _add_initiator_dep(oh, mpu_oh); @@ -982,6 +992,12 @@ static int _shutdown(struct omap_hwmod *oh) if (oh->class->sysc) _sysc_shutdown(oh); + /* + * If an IP contains only one HW reset line, then assert it + * before disabling the clocks and shutting down the IP. + */ + if (oh->rst_lines_cnt == 1) + omap_hwmod_hardreset_assert(oh, oh->rst_lines[0].name); /* clocks and deps are already disabled in idle */ if (oh->_state == _HWMOD_STATE_ENABLED) { @@ -1040,6 +1056,16 @@ static int _setup(struct omap_hwmod *oh, void *data) oh->_state = _HWMOD_STATE_INITIALIZED; + /* + * In the case of hwmod with hardreset that should not be + * de-assert at boot time, we have to keep the module + * initialized, because we cannot enable it properly with the + * reset asserted. Exit without warning because that behavior is + * expected. + */ + if ((oh->flags & HWMOD_INIT_NO_RESET) && oh->rst_lines_cnt == 1) + return 0; + r = _omap_hwmod_enable(oh); if (r) { pr_warning("omap_hwmod: %s: cannot be enabled (%d)\n", @@ -1450,6 +1476,160 @@ int omap_hwmod_reset(struct omap_hwmod *oh) return r; } +/* offset between the reset control and the reset status registers */ +#define RST_CTRL_ST_OFFSET 4 + +/** + * _lookup_reset - reset the hwmod + * @oh: struct omap_hwmod * + * @name: name of the reset line in the context of this hwmod + * + * Return the bit position of the reset line that match the + * input name. Return -ENOENT if not found. + */ +u8 _lookup_reset(struct omap_hwmod *oh, const char *name) +{ + int i; + + for (i = 0; i < oh->rst_lines_cnt; i++) { + const char *rst_line = oh->rst_lines[i].name; + if (!strcmp(rst_line, name)) { + u8 shift = oh->rst_lines[i].rst_shift; + pr_debug("omap_hwmod: %s: _lookup_reset: %s: %d\n", + oh->name, rst_line, shift); + + return shift; + } + } + + return -ENOENT; +} + +/** + * omap_hwmod_reset_assert - assert the HW reset line of submodules + * contained in the hwmod module. + * @oh: struct omap_hwmod * + * @name: name of the reset line to lookup and assert + * + * Some IP like dsp, ipu or iva contain processor that require + * an HW reset line to be assert / deassert in order to enable fully + * the IP. + */ +int omap_hwmod_hardreset_assert(struct omap_hwmod *oh, const char *name) +{ + u8 shift; + u32 mask; + + if (!oh) + return -EINVAL; + + /* XXX For the moment only OMAP4 is supported */ + if (!cpu_is_omap44xx()) { + pr_warning("%s only supported on OMAP4\n", __func__); + return -EINVAL; + } + + shift = _lookup_reset(oh, name); + if (IS_ERR_VALUE(shift)) + return shift; + + mask = 1 << shift; + omap4_prm_rmw_reg_bits(mask, mask, oh->prcm.omap4.rstctrl_reg); + + return 0; +} + +/** + * omap_hwmod_hardreset_deassert - deassert the HW reset line of submodules + * contained in the hwmod module. + * @oh: struct omap_hwmod * + * @name: name of the reset line to look up and deassert + * + * Some IP like dsp, ipu or iva contain processor that require + * an HW reset line to be assert / deassert in order to enable fully + * the IP. + */ +int omap_hwmod_hardreset_deassert(struct omap_hwmod *oh, const char *name) +{ + u8 shift; + u32 mask; + int c = 0; + + if (!oh) + return -EINVAL; + + /* XXX For the moment only OMAP4 is supported */ + if (!cpu_is_omap44xx()) { + pr_warning("%s only supported on OMAP4\n", __func__); + return -EINVAL; + } + + shift = _lookup_reset(oh, name); + if (IS_ERR_VALUE(shift)) + return shift; + + mask = 1 << shift; + + /* Check the current status to avoid de-asserting the line twice */ + if (omap4_prm_read_bits_shift(oh->prcm.omap4.rstctrl_reg, mask) == 0) { + pr_warning("omap_hwmod: %s: reset already de-asserted\n", + oh->name); + return 0; + } + + /* Clear the reset status by writing 1 to the status bit */ + omap4_prm_rmw_reg_bits(0xffffffff, mask, oh->prcm.omap4.rstctrl_reg + + RST_CTRL_ST_OFFSET); + /* de-assert the reset control line */ + omap4_prm_rmw_reg_bits(mask, 0, oh->prcm.omap4.rstctrl_reg); + /* wait the status to be set */ + omap_test_timeout(omap4_prm_read_bits_shift(oh->prcm.omap4.rstctrl_reg + + RST_CTRL_ST_OFFSET, mask), + MAX_MODULE_RESET_WAIT, c); + + if (c == MAX_MODULE_RESET_WAIT) { + pr_warning("omap_hwmod: %s: failed to reset in %d usec\n", + oh->name, MAX_MODULE_RESET_WAIT); + return -EBUSY; + } else { + pr_debug("omap_hwmod: %s: reset %s in %d usec\n", + oh->name, name, c); + } + + return 0; +} + +/** + * omap_hwmod_hw_reset_state - read the HW reset line state of submodules + * contained in the hwmod module + * @oh: struct omap_hwmod * + * @name: name of the reset line to look up and read + * + * Return the state of the reset line. + */ +int omap_hwmod_hardreset_state(struct omap_hwmod *oh, const char *name) +{ + u8 shift; + u32 mask; + + if (!oh) + return -EINVAL; + + /* XXX For the moment only OMAP4 is supported */ + if (!cpu_is_omap44xx()) { + pr_warning("%s only supported on OMAP4\n", __func__); + return -EINVAL; + } + + shift = _lookup_reset(oh, name); + if (IS_ERR_VALUE(shift)) + return shift; + + mask = 1 << shift; + + return omap4_prm_read_bits_shift(oh->prcm.omap4.rstctrl_reg, mask); +} + /** * omap_hwmod_count_resources - count number of struct resources needed by hwmod * @oh: struct omap_hwmod * diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h index 5506d80..5941183 100644 --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h @@ -108,6 +108,19 @@ struct omap_hwmod_dma_info { }; /** + * struct omap_hwmod_rst_info - IPs reset lines use by hwmod + * @name: name of the reset line (module local name) + * @rst_shift: Offset of the reset bit + * + * @name should be something short, e.g., "cpu0" or "rst". It is defined + * locally to the hwmod. + */ +struct omap_hwmod_rst_info { + const char *name; + u8 rst_shift; +}; + +/** * struct omap_hwmod_opt_clk - optional clocks used by this hwmod * @role: "sys", "32k", "tv", etc -- for use in clk_get() * @clk: opt clock: OMAP clock name @@ -327,10 +340,12 @@ struct omap_hwmod_omap2_prcm { /** * struct omap_hwmod_omap4_prcm - OMAP4-specific PRCM data * @clkctrl_reg: PRCM address of the clock control register + * @rstctrl_reg: adress of the XXX_RSTCTRL register located in the PRM * @submodule_wkdep_bit: bit shift of the WKDEP range */ struct omap_hwmod_omap4_prcm { void __iomem *clkctrl_reg; + void __iomem *rstctrl_reg; u8 submodule_wkdep_bit; }; @@ -449,6 +464,7 @@ struct omap_hwmod { struct omap_device *od; struct omap_hwmod_irq_info *mpu_irqs; struct omap_hwmod_dma_info *sdma_reqs; + struct omap_hwmod_rst_info *rst_lines; union { struct omap_hwmod_omap2_prcm omap2; struct omap_hwmod_omap4_prcm omap4; @@ -469,6 +485,7 @@ struct omap_hwmod { u8 response_lat; u8 mpu_irqs_cnt; u8 sdma_reqs_cnt; + u8 rst_lines_cnt; u8 opt_clks_cnt; u8 masters_cnt; u8 slaves_cnt; @@ -495,6 +512,10 @@ int omap_hwmod_shutdown(struct omap_hwmod *oh); int omap_hwmod_enable_clocks(struct omap_hwmod *oh); int omap_hwmod_disable_clocks(struct omap_hwmod *oh); +int omap_hwmod_hardreset_assert(struct omap_hwmod *oh, const char *name); +int omap_hwmod_hardreset_deassert(struct omap_hwmod *oh, const char *name); +int omap_hwmod_hardreset_state(struct omap_hwmod *oh, const char *name); + int omap_hwmod_set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode); int omap_hwmod_reset(struct omap_hwmod *oh);