162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2018, Intel Corporation
462306a36Sopenharmony_ci * Copied from reset-sunxi.c
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/err.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/of_address.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/reset-controller.h>
1462306a36Sopenharmony_ci#include <linux/reset/reset-simple.h>
1562306a36Sopenharmony_ci#include <linux/reset/socfpga.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/spinlock.h>
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define SOCFPGA_NR_BANKS	8
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int a10_reset_init(struct device_node *np)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct reset_simple_data *data;
2562306a36Sopenharmony_ci	struct resource res;
2662306a36Sopenharmony_ci	resource_size_t size;
2762306a36Sopenharmony_ci	int ret;
2862306a36Sopenharmony_ci	u32 reg_offset = 0x10;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
3162306a36Sopenharmony_ci	if (!data)
3262306a36Sopenharmony_ci		return -ENOMEM;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	ret = of_address_to_resource(np, 0, &res);
3562306a36Sopenharmony_ci	if (ret)
3662306a36Sopenharmony_ci		goto err_alloc;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	size = resource_size(&res);
3962306a36Sopenharmony_ci	if (!request_mem_region(res.start, size, np->name)) {
4062306a36Sopenharmony_ci		ret = -EBUSY;
4162306a36Sopenharmony_ci		goto err_alloc;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	data->membase = ioremap(res.start, size);
4562306a36Sopenharmony_ci	if (!data->membase) {
4662306a36Sopenharmony_ci		ret = -ENOMEM;
4762306a36Sopenharmony_ci		goto release_region;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (of_property_read_u32(np, "altr,modrst-offset", &reg_offset))
5162306a36Sopenharmony_ci		pr_warn("missing altr,modrst-offset property, assuming 0x10\n");
5262306a36Sopenharmony_ci	data->membase += reg_offset;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	spin_lock_init(&data->lock);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	data->rcdev.owner = THIS_MODULE;
5762306a36Sopenharmony_ci	data->rcdev.nr_resets = SOCFPGA_NR_BANKS * 32;
5862306a36Sopenharmony_ci	data->rcdev.ops = &reset_simple_ops;
5962306a36Sopenharmony_ci	data->rcdev.of_node = np;
6062306a36Sopenharmony_ci	data->status_active_low = true;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	ret = reset_controller_register(&data->rcdev);
6362306a36Sopenharmony_ci	if (ret)
6462306a36Sopenharmony_ci		pr_err("unable to register device\n");
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return ret;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cirelease_region:
6962306a36Sopenharmony_ci	release_mem_region(res.start, size);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cierr_alloc:
7262306a36Sopenharmony_ci	kfree(data);
7362306a36Sopenharmony_ci	return ret;
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * These are the reset controller we need to initialize early on in
7862306a36Sopenharmony_ci * our system, before we can even think of using a regular device
7962306a36Sopenharmony_ci * driver for it.
8062306a36Sopenharmony_ci * The controllers that we can register through the regular device
8162306a36Sopenharmony_ci * model are handled by the simple reset driver directly.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistatic const struct of_device_id socfpga_early_reset_dt_ids[] __initconst = {
8462306a36Sopenharmony_ci	{ .compatible = "altr,rst-mgr", },
8562306a36Sopenharmony_ci	{ /* sentinel */ },
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_civoid __init socfpga_reset_init(void)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct device_node *np;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	for_each_matching_node(np, socfpga_early_reset_dt_ids)
9362306a36Sopenharmony_ci		a10_reset_init(np);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/*
9762306a36Sopenharmony_ci * The early driver is problematic, because it doesn't register
9862306a36Sopenharmony_ci * itself as a driver. This causes certain device links to prevent
9962306a36Sopenharmony_ci * consumer devices from probing. The hacky solution is to register
10062306a36Sopenharmony_ci * an empty driver, whose only job is to attach itself to the reset
10162306a36Sopenharmony_ci * manager and call probe.
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_cistatic const struct of_device_id socfpga_reset_dt_ids[] = {
10462306a36Sopenharmony_ci	{ .compatible = "altr,rst-mgr", },
10562306a36Sopenharmony_ci	{ /* sentinel */ },
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int reset_simple_probe(struct platform_device *pdev)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic struct platform_driver reset_socfpga_driver = {
11462306a36Sopenharmony_ci	.probe	= reset_simple_probe,
11562306a36Sopenharmony_ci	.driver = {
11662306a36Sopenharmony_ci		.name		= "socfpga-reset",
11762306a36Sopenharmony_ci		.of_match_table	= socfpga_reset_dt_ids,
11862306a36Sopenharmony_ci	},
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_cibuiltin_platform_driver(reset_socfpga_driver);
121