From patchwork Mon Oct 14 19:23:26 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Maxime Ripard X-Patchwork-Id: 3040281 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 CE2B69F2B6 for ; Mon, 14 Oct 2013 20:53:00 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D4466201F5 for ; Mon, 14 Oct 2013 20:52:59 +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 B406620158 for ; Mon, 14 Oct 2013 20:52:58 +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 1VVp71-0006gs-Bc; Mon, 14 Oct 2013 20:51:35 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VVp6e-000465-Ow; Mon, 14 Oct 2013 20:51:12 +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 1VVp5u-0003yU-SY for linux-arm-kernel@lists.infradead.org; Mon, 14 Oct 2013 20:50:30 +0000 Received: by mail.free-electrons.com (Postfix, from userid 106) id A1B568D8; Mon, 14 Oct 2013 22:50:14 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-4.7 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from localhost (189.58.26.109.rev.sfr.net [109.26.58.189]) by mail.free-electrons.com (Postfix) with ESMTPSA id 1CEB381C; Mon, 14 Oct 2013 22:50:14 +0200 (CEST) From: Maxime Ripard To: p.zabel@pengutronix.de Subject: [PATCH v2 1/4] reset: Add Allwinner A31 Reset Controller Driver Date: Mon, 14 Oct 2013 21:23:26 +0200 Message-Id: <1381778609-4191-2-git-send-email-maxime.ripard@free-electrons.com> X-Mailer: git-send-email 1.8.4 In-Reply-To: <1381778609-4191-1-git-send-email-maxime.ripard@free-electrons.com> References: <1381778609-4191-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-20131014_165027_268065_7BADB3D0 X-CRM114-Status: GOOD ( 19.01 ) X-Spam-Score: -1.7 (-) Cc: Emilio Lopez , 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 has an IP maintaining a few other IPs in the SoC in reset by default. Among these IPs are the High Speed Timers, hence why we can't use the regular driver construct, and need to call the registering function directly during machine initialisation. Signed-off-by: Maxime Ripard --- drivers/reset/Makefile | 1 + drivers/reset/reset-sunxi.c | 156 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 drivers/reset/reset-sunxi.c diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 1e2d83f..f216b74 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_RESET_CONTROLLER) += core.o +obj-$(CONFIG_ARCH_SUNXI) += reset-sun6i.o diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c new file mode 100644 index 0000000..5bbd59a --- /dev/null +++ b/drivers/reset/reset-sunxi.c @@ -0,0 +1,156 @@ +/* + * 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 + +struct sunxi_reset_data { + 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; + u32 reg = readl(data->membase + (bank * 4)); + + writel(reg & ~BIT(offset), data->membase + (bank * 4)); + + 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; + u32 reg = readl(data->membase + (bank * 4)); + + writel(reg | BIT(offset), data->membase + (bank * 4)); + + 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); + 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,sun4i-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