From patchwork Fri Nov 15 18:19:04 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Maxime Ripard X-Patchwork-Id: 3189451 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id B1F759F432 for ; Fri, 15 Nov 2013 18:23:10 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B720C208B8 for ; Fri, 15 Nov 2013 18:23:09 +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 4FF7720939 for ; Fri, 15 Nov 2013 18:23:08 +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 1VhO1J-00010I-UP; Fri, 15 Nov 2013 18:21:30 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VhO0u-0004Cf-3y; Fri, 15 Nov 2013 18:21:04 +0000 Received: from top.free-electrons.com ([176.31.233.9] helo=mail.free-electrons.com) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VhO0I-00047i-OV for linux-arm-kernel@lists.infradead.org; Fri, 15 Nov 2013 18:20:31 +0000 Received: by mail.free-electrons.com (Postfix, from userid 106) id 8BBA2824; Fri, 15 Nov 2013 19:20:06 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: 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 Received: from localhost (AMontsouris-553-1-41-236.w92-151.abo.wanadoo.fr [92.151.208.236]) by mail.free-electrons.com (Postfix) with ESMTPSA id 7A0BF750; Fri, 15 Nov 2013 19:20:05 +0100 (CET) From: Maxime Ripard To: p.zabel@pengutronix.de Subject: [PATCH v4 1/4] reset: Add Allwinner SoCs Reset Controller Driver Date: Fri, 15 Nov 2013 19:19:04 +0100 Message-Id: <1384539547-29463-2-git-send-email-maxime.ripard@free-electrons.com> X-Mailer: git-send-email 1.8.4.2 In-Reply-To: <1384539547-29463-1-git-send-email-maxime.ripard@free-electrons.com> References: <1384539547-29463-1-git-send-email-maxime.ripard@free-electrons.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20131115_132027_125833_85C1334D X-CRM114-Status: GOOD ( 20.34 ) X-Spam-Score: -1.2 (-) Cc: Emilio Lopez , zhuzhenhua@allwinnertech.com, linux-sunxi@googlegroups.com, kevin.z.m.zh@gmail.com, sunny@allwinnertech.com, shuge@allwinnertech.com, Maxime Ripard , linux-arm-kernel@lists.infradead.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: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP The Allwinner A31 and most of the other Allwinner SoCs have an IP maintaining a few other IPs in the SoC in reset by default. Among these IPs are the A31's High Speed Timers, hence why we can't use the regular driver construct in every cases, and need to call the registering function directly during machine initialisation. Apart from this, the implementation is fairly straightforward, and could easily be moved to a generic MMIO-based reset controller driver if the need ever arise. Signed-off-by: Maxime Ripard --- drivers/reset/Makefile | 1 + drivers/reset/reset-sunxi.c | 175 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 drivers/reset/reset-sunxi.c diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 1e2d83f2b995..cc29832c9638 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_RESET_CONTROLLER) += core.o +obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c new file mode 100644 index 000000000000..695bd3496eba --- /dev/null +++ b/drivers/reset/reset-sunxi.c @@ -0,0 +1,175 @@ +/* + * Allwinner SoCs Reset Controller driver + * + * Copyright 2013 Maxime Ripard + * + * Maxime Ripard + * + * 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 +#include + +struct sunxi_reset_data { + spinlock_t lock; + void __iomem *membase; + struct reset_controller_dev rcdev; +}; + +static int sunxi_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct sunxi_reset_data *data = container_of(rcdev, + struct sunxi_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(data->membase + (bank * 4)); + writel(reg & ~BIT(offset), data->membase + (bank * 4)); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static int sunxi_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct sunxi_reset_data *data = container_of(rcdev, + struct sunxi_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(data->membase + (bank * 4)); + writel(reg | BIT(offset), data->membase + (bank * 4)); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static struct reset_control_ops sunxi_reset_ops = { + .assert = sunxi_reset_assert, + .deassert = sunxi_reset_deassert, +}; + +static int sunxi_reset_init(struct device_node *np) +{ + struct sunxi_reset_data *data; + struct resource res; + resource_size_t size; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + goto err_alloc; + + size = resource_size(&res); + if (!request_mem_region(res.start, size, np->name)) { + ret = -EBUSY; + goto err_alloc; + } + + data->membase = ioremap(res.start, size); + if (!data->membase) { + ret = -ENOMEM; + goto err_alloc; + } + + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = size * 32; + data->rcdev.ops = &sunxi_reset_ops; + data->rcdev.of_node = np; + reset_controller_register(&data->rcdev); + + return 0; + +err_alloc: + kfree(data); + return ret; +}; + +/* + * These are the reset controller we need to initialize early on in + * our system, before we can even think of using a regular device + * driver for it. + */ +static const struct of_device_id sunxi_early_reset_dt_ids[] __initdata = { + { .compatible = "allwinner,sun6i-a31-ahb1-reset", }, + { /* sentinel */ }, +}; + +void __init sun6i_reset_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, sunxi_early_reset_dt_ids) + sunxi_reset_init(np); +} + +/* + * And these are the controllers we can register through the regular + * device model. + */ +static const struct of_device_id sunxi_reset_dt_ids[] = { + { .compatible = "allwinner,sun6i-a31-clock-reset", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, sunxi_reset_dt_ids); + +static int sunxi_reset_probe(struct platform_device *pdev) +{ + return sunxi_reset_init(pdev->dev.of_node); +} + +static int sunxi_reset_remove(struct platform_device *pdev) +{ + struct sunxi_reset_data *data = platform_get_drvdata(pdev); + + reset_controller_unregister(&data->rcdev); + iounmap(data->membase); + kfree(data); + + return 0; +} + +static struct platform_driver sunxi_reset_driver = { + .probe = sunxi_reset_probe, + .remove = sunxi_reset_remove, + .driver = { + .name = "sunxi-reset", + .owner = THIS_MODULE, + .of_match_table = sunxi_reset_dt_ids, + }, +}; +module_platform_driver(sunxi_reset_driver); + +MODULE_AUTHOR("Maxime Ripard