From patchwork Thu Mar 12 21:55:52 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Maxime Coquelin X-Patchwork-Id: 449669 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 23EDB1400EA for ; Fri, 13 Mar 2015 08:56:56 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="verification failed; unprotected key" header.d=gmail.com header.i=@gmail.com header.b=Op2H9hPB; dkim-adsp=none (unprotected policy); dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756032AbbCLV4w (ORCPT ); Thu, 12 Mar 2015 17:56:52 -0400 Received: from mail-we0-f171.google.com ([74.125.82.171]:44853 "EHLO mail-we0-f171.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751724AbbCLV4j (ORCPT ); Thu, 12 Mar 2015 17:56:39 -0400 Received: by wesp10 with SMTP id p10so19336988wes.11; Thu, 12 Mar 2015 14:56:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=iU8tdV8flo8B0e8g6tYDquXuV17BG/QFwsGkRhu8iMI=; b=Op2H9hPBcz//fui5MznjMczJJ5LF6rKX4wP2LZ+cAQjYYBWpW+7YvhMmEp7hAXLH1F esNc/oLMTIUeansfXKzJinVTN3snOUSsWI04gGRqmGkKIaFzoGN//pK+3aSegU+0L3vV SEt4hyNFmXXjEGEbSdEENoA3pmSmU2HIOzdYrDMYaQaAcO94heTbhaxBVLjQe3C0SE9E 0nYLBEqudNpc32UGzRy4GjG4l6DVTMKmIuuU9b/kbkieIbFxzfY+mh0uSlHnnuQlBgkf lGtmQgfI2QZMHemYIc/d1OnUx1dOAGT9JOdDIhJqzWPWFD12w3O9jhGv4iUdWz8Jld+Y S4tA== X-Received: by 10.194.175.39 with SMTP id bx7mr93145055wjc.22.1426197397391; Thu, 12 Mar 2015 14:56:37 -0700 (PDT) Received: from lmecul0520.st.com. (246.208.139.88.rev.sfr.net. [88.139.208.246]) by mx.google.com with ESMTPSA id hl8sm148976wjb.38.2015.03.12.14.56.34 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 12 Mar 2015 14:56:36 -0700 (PDT) From: Maxime Coquelin X-Google-Original-From: Maxime Coquelin To: u.kleine-koenig@pengutronix.de, afaerber@suse.de, geert@linux-m68k.org, Rob Herring , Philipp Zabel , Linus Walleij , Arnd Bergmann , stefan@agner.ch, pmeerw@pmeerw.net, pebolle@tiscali.nl Cc: Jonathan Corbet , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Russell King , Daniel Lezcano , Thomas Gleixner , Greg Kroah-Hartman , Jiri Slaby , Andrew Morton , "David S. Miller" , Mauro Carvalho Chehab , Joe Perches , Antti Palosaari , Tejun Heo , Will Deacon , Nikolay Borisov , Rusty Russell , Kees Cook , Michal Marek , linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, linux-arch@vger.kernel.org, linux-api@vger.kernel.org, mcoquelin.stm32@gmail.com Subject: [PATCH v3 06/15] drivers: reset: Add STM32 reset driver Date: Thu, 12 Mar 2015 22:55:52 +0100 Message-Id: <1426197361-19290-7-git-send-email-maxime.coquelin@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1426197361-19290-1-git-send-email-maxime.coquelin@st.com> References: <1426197361-19290-1-git-send-email-maxime.coquelin@st.com> Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org From: Maxime Coquelin The STM32 MCUs family IP can be reset by accessing some shared registers. The specificity is that some reset lines are used by the timers. At timer initialization time, the timer has to be reset, that's why we cannot use a regular driver. Signed-off-by: Maxime Coquelin --- drivers/reset/Makefile | 1 + drivers/reset/reset-stm32.c | 125 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 drivers/reset/reset-stm32.c diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 157d421..aed12d1 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_RESET_CONTROLLER) += core.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o +obj-$(CONFIG_ARCH_STM32) += reset-stm32.o obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o obj-$(CONFIG_ARCH_STI) += sti/ diff --git a/drivers/reset/reset-stm32.c b/drivers/reset/reset-stm32.c new file mode 100644 index 0000000..0d389b1 --- /dev/null +++ b/drivers/reset/reset-stm32.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) Maxime Coquelin 2015 + * Author: Maxime Coquelin + * License terms: GNU General Public License (GPL), version 2 + * + * Heavily based on sunxi driver from Maxime Ripard. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct stm32_reset_data { + spinlock_t lock; + void __iomem *membase; + struct reset_controller_dev rcdev; +}; + +static int stm32_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct stm32_reset_data *data = container_of(rcdev, + struct stm32_reset_data, + rcdev); + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&data->lock, flags); + + reg = readl_relaxed(data->membase + (bank * 4)); + writel_relaxed(reg | BIT(offset), data->membase + (bank * 4)); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static int stm32_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct stm32_reset_data *data = container_of(rcdev, + struct stm32_reset_data, + rcdev); + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&data->lock, flags); + + reg = readl_relaxed(data->membase + (bank * 4)); + writel_relaxed(reg & ~BIT(offset), data->membase + (bank * 4)); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static struct reset_control_ops stm32_reset_ops = { + .assert = stm32_reset_assert, + .deassert = stm32_reset_deassert, +}; + +static const struct of_device_id stm32_reset_dt_ids[] = { + { .compatible = "st,stm32-rcc", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, sstm32_reset_dt_ids); + +static int stm32_reset_probe(struct platform_device *pdev) +{ + struct stm32_reset_data *data; + struct resource *res; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->membase)) + return PTR_ERR(data->membase); + + spin_lock_init(&data->lock); + + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = resource_size(res) * 8; + data->rcdev.ops = &stm32_reset_ops; + data->rcdev.of_node = pdev->dev.of_node; + + return reset_controller_register(&data->rcdev); +} + +static int stm32_reset_remove(struct platform_device *pdev) +{ + struct stm32_reset_data *data = platform_get_drvdata(pdev); + + reset_controller_unregister(&data->rcdev); + + return 0; +} + +static struct platform_driver stm32_reset_driver = { + .probe = stm32_reset_probe, + .remove = stm32_reset_remove, + .driver = { + .name = "stm32-rcc-reset", + .of_match_table = stm32_reset_dt_ids, + }, +}; +module_platform_driver(stm32_reset_driver); + +MODULE_AUTHOR("Maxime Coquelin "); +MODULE_DESCRIPTION("STM32 MCUs Reset Controller Driver"); +MODULE_LICENSE("GPL"); +