2018-11-14 02:50:48 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2018, Intel Corporation
|
|
|
|
* Copied from reset-sunxi.c
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_address.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/reset-controller.h>
|
2020-05-27 23:47:31 +08:00
|
|
|
#include <linux/reset/reset-simple.h>
|
2018-12-13 19:24:36 +08:00
|
|
|
#include <linux/reset/socfpga.h>
|
2018-11-14 02:50:48 +08:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
|
|
|
|
#define SOCFPGA_NR_BANKS 8
|
|
|
|
|
|
|
|
static int a10_reset_init(struct device_node *np)
|
|
|
|
{
|
|
|
|
struct reset_simple_data *data;
|
|
|
|
struct resource res;
|
|
|
|
resource_size_t size;
|
|
|
|
int ret;
|
|
|
|
u32 reg_offset = 0x10;
|
|
|
|
|
|
|
|
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;
|
2020-11-10 03:21:41 +08:00
|
|
|
goto release_region;
|
2018-11-14 02:50:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (of_property_read_u32(np, "altr,modrst-offset", ®_offset))
|
|
|
|
pr_warn("missing altr,modrst-offset property, assuming 0x10\n");
|
|
|
|
data->membase += reg_offset;
|
|
|
|
|
|
|
|
spin_lock_init(&data->lock);
|
|
|
|
|
|
|
|
data->rcdev.owner = THIS_MODULE;
|
|
|
|
data->rcdev.nr_resets = SOCFPGA_NR_BANKS * 32;
|
|
|
|
data->rcdev.ops = &reset_simple_ops;
|
|
|
|
data->rcdev.of_node = np;
|
|
|
|
data->status_active_low = true;
|
|
|
|
|
2020-11-10 03:21:41 +08:00
|
|
|
ret = reset_controller_register(&data->rcdev);
|
|
|
|
if (ret)
|
|
|
|
pr_err("unable to register device\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
release_region:
|
|
|
|
release_mem_region(res.start, size);
|
2018-11-14 02:50:48 +08:00
|
|
|
|
|
|
|
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.
|
|
|
|
* The controllers that we can register through the regular device
|
|
|
|
* model are handled by the simple reset driver directly.
|
|
|
|
*/
|
|
|
|
static const struct of_device_id socfpga_early_reset_dt_ids[] __initconst = {
|
|
|
|
{ .compatible = "altr,rst-mgr", },
|
|
|
|
{ /* sentinel */ },
|
|
|
|
};
|
|
|
|
|
|
|
|
void __init socfpga_reset_init(void)
|
|
|
|
{
|
|
|
|
struct device_node *np;
|
|
|
|
|
|
|
|
for_each_matching_node(np, socfpga_early_reset_dt_ids)
|
|
|
|
a10_reset_init(np);
|
|
|
|
}
|
2021-09-20 20:41:41 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The early driver is problematic, because it doesn't register
|
|
|
|
* itself as a driver. This causes certain device links to prevent
|
|
|
|
* consumer devices from probing. The hacky solution is to register
|
|
|
|
* an empty driver, whose only job is to attach itself to the reset
|
|
|
|
* manager and call probe.
|
|
|
|
*/
|
|
|
|
static const struct of_device_id socfpga_reset_dt_ids[] = {
|
|
|
|
{ .compatible = "altr,rst-mgr", },
|
|
|
|
{ /* sentinel */ },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int reset_simple_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver reset_socfpga_driver = {
|
|
|
|
.probe = reset_simple_probe,
|
|
|
|
.driver = {
|
|
|
|
.name = "socfpga-reset",
|
|
|
|
.of_match_table = socfpga_reset_dt_ids,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
builtin_platform_driver(reset_socfpga_driver);
|