18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Allwinner SoCs Reset Controller driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2013 Maxime Ripard
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/of_address.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/reset-controller.h>
178c2ecf20Sopenharmony_ci#include <linux/reset/reset-simple.h>
188c2ecf20Sopenharmony_ci#include <linux/reset/sunxi.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
218c2ecf20Sopenharmony_ci#include <linux/types.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int sunxi_reset_init(struct device_node *np)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct reset_simple_data *data;
268c2ecf20Sopenharmony_ci	struct resource res;
278c2ecf20Sopenharmony_ci	resource_size_t size;
288c2ecf20Sopenharmony_ci	int ret;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
318c2ecf20Sopenharmony_ci	if (!data)
328c2ecf20Sopenharmony_ci		return -ENOMEM;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	ret = of_address_to_resource(np, 0, &res);
358c2ecf20Sopenharmony_ci	if (ret)
368c2ecf20Sopenharmony_ci		goto err_alloc;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	size = resource_size(&res);
398c2ecf20Sopenharmony_ci	if (!request_mem_region(res.start, size, np->name)) {
408c2ecf20Sopenharmony_ci		ret = -EBUSY;
418c2ecf20Sopenharmony_ci		goto err_alloc;
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	data->membase = ioremap(res.start, size);
458c2ecf20Sopenharmony_ci	if (!data->membase) {
468c2ecf20Sopenharmony_ci		ret = -ENOMEM;
478c2ecf20Sopenharmony_ci		goto err_alloc;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	spin_lock_init(&data->lock);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	data->rcdev.owner = THIS_MODULE;
538c2ecf20Sopenharmony_ci	data->rcdev.nr_resets = size * 8;
548c2ecf20Sopenharmony_ci	data->rcdev.ops = &reset_simple_ops;
558c2ecf20Sopenharmony_ci	data->rcdev.of_node = np;
568c2ecf20Sopenharmony_ci	data->active_low = true;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return reset_controller_register(&data->rcdev);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cierr_alloc:
618c2ecf20Sopenharmony_ci	kfree(data);
628c2ecf20Sopenharmony_ci	return ret;
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * These are the reset controller we need to initialize early on in
678c2ecf20Sopenharmony_ci * our system, before we can even think of using a regular device
688c2ecf20Sopenharmony_ci * driver for it.
698c2ecf20Sopenharmony_ci * The controllers that we can register through the regular device
708c2ecf20Sopenharmony_ci * model are handled by the simple reset driver directly.
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_cistatic const struct of_device_id sunxi_early_reset_dt_ids[] __initconst = {
738c2ecf20Sopenharmony_ci	{ .compatible = "allwinner,sun6i-a31-ahb1-reset", },
748c2ecf20Sopenharmony_ci	{ /* sentinel */ },
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_civoid __init sun6i_reset_init(void)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct device_node *np;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	for_each_matching_node(np, sunxi_early_reset_dt_ids)
828c2ecf20Sopenharmony_ci		sunxi_reset_init(np);
838c2ecf20Sopenharmony_ci}
84