[DM/FEATURE] Support reset controller (#9630)
* [DM/FEATURE] Support reset controller Reset controllers are central units that control the reset signals to multiple peripherals. The reset controller API is split into two parts: 1. The consumer driver interface, which allows peripheral drivers to request control over their reset input signals 2. The reset controller driver interface which is used by drivers for reset controller devices to register their reset controls to provide them to the consumers. * [RESET/SIMPLE] Support simple reset Currently this driver supports: - Altera SoCFPGAs - ASPEED BMC SoCs - Bitmain BM1880 SoC - Realtek SoCs - RCC reset controller in STM32 MCUs - Allwinner SoCs - SiFive FU740 SoCs - Sophgo SoCs Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
parent
9afe6a5182
commit
1a24ae06f3
|
@ -26,6 +26,7 @@ rsource "phye/Kconfig"
|
|||
rsource "block/Kconfig"
|
||||
rsource "nvme/Kconfig"
|
||||
rsource "scsi/Kconfig"
|
||||
rsource "reset/Kconfig"
|
||||
rsource "virtio/Kconfig"
|
||||
rsource "dma/Kconfig"
|
||||
rsource "mfd/Kconfig"
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#ifndef __RESET_H__
|
||||
#define __RESET_H__
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <drivers/ofw.h>
|
||||
|
||||
#define RT_RESET_CONTROLLER_OBJ_NAME "RSTC"
|
||||
|
||||
struct rt_reset_control_ops;
|
||||
|
||||
struct rt_reset_controller
|
||||
{
|
||||
struct rt_object parent;
|
||||
|
||||
rt_list_t rstc_nodes;
|
||||
|
||||
const char *name;
|
||||
const struct rt_reset_control_ops *ops;
|
||||
|
||||
struct rt_ofw_node *ofw_node;
|
||||
void *priv;
|
||||
|
||||
struct rt_spinlock spinlock;
|
||||
};
|
||||
|
||||
struct rt_reset_control
|
||||
{
|
||||
rt_list_t list;
|
||||
|
||||
struct rt_reset_controller *rstcer;
|
||||
|
||||
int id;
|
||||
const char *con_id;
|
||||
rt_bool_t is_array;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct rt_reset_control_ops
|
||||
{
|
||||
/*
|
||||
* rt_ofw_cell_args return:
|
||||
* args[0] = rstc.id
|
||||
*/
|
||||
rt_err_t (*ofw_parse)(struct rt_reset_control *rstc, struct rt_ofw_cell_args *args);
|
||||
/* API */
|
||||
rt_err_t (*reset)(struct rt_reset_control *rstc);
|
||||
rt_err_t (*assert)(struct rt_reset_control *rstc);
|
||||
rt_err_t (*deassert)(struct rt_reset_control *rstc);
|
||||
int (*status)(struct rt_reset_control *rstc);
|
||||
};
|
||||
|
||||
rt_err_t rt_reset_controller_register(struct rt_reset_controller *rstcer);
|
||||
rt_err_t rt_reset_controller_unregister(struct rt_reset_controller *rstcer);
|
||||
|
||||
rt_err_t rt_reset_control_reset(struct rt_reset_control *rstc);
|
||||
rt_err_t rt_reset_control_assert(struct rt_reset_control *rstc);
|
||||
rt_err_t rt_reset_control_deassert(struct rt_reset_control *rstc);
|
||||
int rt_reset_control_status(struct rt_reset_control *rstc);
|
||||
|
||||
rt_ssize_t rt_reset_control_get_count(struct rt_device *dev);
|
||||
struct rt_reset_control *rt_reset_control_get_array(struct rt_device *dev);
|
||||
struct rt_reset_control *rt_reset_control_get_by_index(struct rt_device *dev, int index);
|
||||
struct rt_reset_control *rt_reset_control_get_by_name(struct rt_device *dev, const char *name);
|
||||
void rt_reset_control_put(struct rt_reset_control *rstc);
|
||||
|
||||
struct rt_reset_control *rt_ofw_get_reset_control_array(struct rt_ofw_node *np);
|
||||
struct rt_reset_control *rt_ofw_get_reset_control_by_index(struct rt_ofw_node *np, int index);
|
||||
struct rt_reset_control *rt_ofw_get_reset_control_by_name(struct rt_ofw_node *np, const char *name);
|
||||
|
||||
#endif /* __RESET_H__ */
|
|
@ -79,6 +79,10 @@ extern "C" {
|
|||
#include "drivers/pic.h"
|
||||
#endif /* RT_USING_PIC */
|
||||
|
||||
#ifdef RT_USING_RESET
|
||||
#include "drivers/reset.h"
|
||||
#endif /* RT_USING_RESET */
|
||||
|
||||
#ifdef RT_USING_SCSI
|
||||
#include "drivers/scsi.h"
|
||||
#endif /* RT_USING_SCSI */
|
||||
|
|
|
@ -71,6 +71,9 @@ static const struct ofw_obj_cmp_list ofw_obj_cmp_list[] =
|
|||
{
|
||||
#ifdef RT_USING_CLK
|
||||
{ "#clock-cells", RT_CLK_NODE_OBJ_NAME, sizeof(struct rt_clk_node) },
|
||||
#endif
|
||||
#ifdef RT_USING_RESET
|
||||
{ "#reset-cells", RT_RESET_CONTROLLER_OBJ_NAME, sizeof(struct rt_reset_controller) },
|
||||
#endif
|
||||
{ "#power-domain-cells", RT_POWER_DOMAIN_PROXY_OBJ_NAME, sizeof(struct rt_dm_power_domain_proxy) },
|
||||
{ "#power-domain-cells", RT_POWER_DOMAIN_OBJ_NAME, sizeof(struct rt_dm_power_domain) },
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
menuconfig RT_USING_RESET
|
||||
bool "Using Reset Controller support"
|
||||
depends on RT_USING_DM
|
||||
depends on RT_USING_OFW
|
||||
default n
|
||||
|
||||
config RT_RESET_SIMPLE
|
||||
bool "Simple Reset Controller Driver"
|
||||
depends on RT_USING_RESET
|
||||
default n
|
||||
|
||||
if RT_USING_RESET
|
||||
osource "$(SOC_DM_RESET_DIR)/Kconfig"
|
||||
endif
|
|
@ -0,0 +1,18 @@
|
|||
from building import *
|
||||
|
||||
group = []
|
||||
|
||||
if not GetDepend(['RT_USING_RESET']):
|
||||
Return('group')
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
CPPPATH = [cwd + '/../include']
|
||||
|
||||
src = ['reset.c']
|
||||
|
||||
if GetDepend(['RT_RESET_SIMPLE']):
|
||||
src += ['reset-simple.c']
|
||||
|
||||
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include "reset-simple.h"
|
||||
|
||||
struct reset_simple_data
|
||||
{
|
||||
rt_uint32_t reg_offset;
|
||||
rt_bool_t active_low;
|
||||
rt_bool_t status_active_low;
|
||||
};
|
||||
|
||||
#define raw_to_reset_simple(raw) rt_container_of(raw, struct reset_simple, parent)
|
||||
|
||||
static rt_err_t reset_simple_update(struct reset_simple *rsts, int id, rt_bool_t assert)
|
||||
{
|
||||
rt_uint32_t reg;
|
||||
rt_ubase_t level;
|
||||
int reg_width = sizeof(rt_uint32_t);
|
||||
int bank = id / (reg_width * 8);
|
||||
int offset = id % (reg_width * 8);
|
||||
|
||||
level = rt_spin_lock_irqsave(&rsts->lock);
|
||||
|
||||
reg = HWREG32(rsts->mmio_base + (bank * reg_width));
|
||||
|
||||
if (assert ^ rsts->active_low)
|
||||
{
|
||||
reg |= RT_BIT(offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
reg &= ~RT_BIT(offset);
|
||||
}
|
||||
|
||||
HWREG32(rsts->mmio_base + (bank * reg_width)) = reg;
|
||||
|
||||
rt_spin_unlock_irqrestore(&rsts->lock, level);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t reset_simple_assert(struct rt_reset_control *rstc)
|
||||
{
|
||||
struct reset_simple *rsts = raw_to_reset_simple(rstc);
|
||||
|
||||
return reset_simple_update(rsts, rstc->id, RT_TRUE);
|
||||
}
|
||||
|
||||
static rt_err_t reset_simple_deassert(struct rt_reset_control *rstc)
|
||||
{
|
||||
struct reset_simple *rsts = raw_to_reset_simple(rstc);
|
||||
|
||||
return reset_simple_update(rsts, rstc->id, RT_FALSE);
|
||||
}
|
||||
|
||||
static rt_err_t reset_simple_reset(struct rt_reset_control *rstc)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct reset_simple *rsts = raw_to_reset_simple(rstc);
|
||||
|
||||
if (!rsts->reset_us)
|
||||
{
|
||||
return -RT_ENOSYS;
|
||||
}
|
||||
|
||||
if ((err = reset_simple_assert(rstc)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
rt_hw_us_delay(rsts->reset_us + (rsts->reset_us >> 1));
|
||||
|
||||
return reset_simple_deassert(rstc);
|
||||
}
|
||||
|
||||
static int reset_simple_status(struct rt_reset_control *rstc)
|
||||
{
|
||||
rt_uint32_t value;
|
||||
int reg_width = sizeof(rt_uint32_t);
|
||||
int bank = rstc->id / (reg_width * 8);
|
||||
int offset = rstc->id % (reg_width * 8);
|
||||
struct reset_simple *rsts = raw_to_reset_simple(rstc);
|
||||
|
||||
value = HWREG32(rsts->mmio_base + (bank * reg_width));
|
||||
|
||||
return !(value & RT_BIT(offset)) ^ !rsts->status_active_low;
|
||||
}
|
||||
|
||||
const struct rt_reset_control_ops reset_simple_ops =
|
||||
{
|
||||
.reset = reset_simple_reset,
|
||||
.assert = reset_simple_assert,
|
||||
.deassert = reset_simple_deassert,
|
||||
.status = reset_simple_status,
|
||||
};
|
||||
|
||||
static rt_err_t reset_simple_probe(struct rt_platform_device *pdev)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct rt_reset_controller *rstcer;
|
||||
struct rt_device *dev = &pdev->parent;
|
||||
const struct reset_simple_data *rsts_data = pdev->id->data;
|
||||
struct reset_simple *rsts = rt_calloc(1, sizeof(*rsts));
|
||||
|
||||
if (!rsts)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
rsts->mmio_base = rt_dm_dev_iomap(dev, 0);
|
||||
|
||||
if (!rsts->mmio_base)
|
||||
{
|
||||
err = -RT_EIO;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
rt_spin_lock_init(&rsts->lock);
|
||||
|
||||
rstcer = &rsts->parent;
|
||||
|
||||
rstcer->priv = rsts;
|
||||
rstcer->ofw_node = dev->ofw_node;
|
||||
rstcer->ops = &reset_simple_ops;
|
||||
|
||||
if ((err = rt_reset_controller_register(rstcer)))
|
||||
{
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if (rsts_data)
|
||||
{
|
||||
rsts->mmio_base += rsts_data->reg_offset;
|
||||
rsts->active_low = rsts_data->active_low;
|
||||
rsts->status_active_low = rsts_data->status_active_low;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
|
||||
_fail:
|
||||
if (rsts->mmio_base)
|
||||
{
|
||||
rt_iounmap(rsts->mmio_base);
|
||||
}
|
||||
|
||||
rt_free(rsts);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct reset_simple_data reset_simple_socfpga =
|
||||
{
|
||||
.reg_offset = 0x20,
|
||||
.status_active_low = RT_TRUE,
|
||||
};
|
||||
|
||||
static const struct reset_simple_data reset_simple_active_low =
|
||||
{
|
||||
.active_low = RT_TRUE,
|
||||
.status_active_low = RT_TRUE,
|
||||
};
|
||||
|
||||
static const struct rt_ofw_node_id reset_simple_ofw_ids[] =
|
||||
{
|
||||
{ .compatible = "altr,stratix10-rst-mgr", .data = &reset_simple_socfpga },
|
||||
{ .compatible = "st,stm32-rcc", },
|
||||
{ .compatible = "allwinner,sun6i-a31-clock-reset", .data = &reset_simple_active_low },
|
||||
{ .compatible = "zte,zx296718-reset", .data = &reset_simple_active_low },
|
||||
{ .compatible = "aspeed,ast2400-lpc-reset" },
|
||||
{ .compatible = "aspeed,ast2500-lpc-reset" },
|
||||
{ .compatible = "aspeed,ast2600-lpc-reset" },
|
||||
{ .compatible = "bitmain,bm1880-reset", .data = &reset_simple_active_low },
|
||||
{ .compatible = "brcm,bcm4908-misc-pcie-reset", .data = &reset_simple_active_low },
|
||||
{ .compatible = "snps,dw-high-reset" },
|
||||
{ .compatible = "snps,dw-low-reset", .data = &reset_simple_active_low },
|
||||
{ .compatible = "sophgo,sg2042-reset", .data = &reset_simple_active_low },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct rt_platform_driver reset_simple_driver =
|
||||
{
|
||||
.name = "reset-simple",
|
||||
.ids = reset_simple_ofw_ids,
|
||||
|
||||
.probe = reset_simple_probe,
|
||||
};
|
||||
|
||||
static int reset_simple_register(void)
|
||||
{
|
||||
rt_platform_driver_register(&reset_simple_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_SUBSYS_EXPORT(reset_simple_register);
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#ifndef __RESET_SIMPLE_H__
|
||||
#define __RESET_SIMPLE_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
struct reset_simple
|
||||
{
|
||||
struct rt_reset_controller parent;
|
||||
|
||||
void *mmio_base;
|
||||
|
||||
/*
|
||||
* If true, bits are cleared to assert the reset.
|
||||
* Otherwise, bits are set to assert the reset.
|
||||
*/
|
||||
rt_bool_t active_low;
|
||||
/*
|
||||
* If true, bits read back as cleared while the reset is asserted.
|
||||
* Otherwise, bits read back as set while the reset is asserted.
|
||||
*/
|
||||
rt_bool_t status_active_low;
|
||||
|
||||
/*
|
||||
* Minimum delay in microseconds needed that needs to be
|
||||
* waited for between an assert and a deassert to reset the device.
|
||||
* If multiple consumers with different delay
|
||||
* requirements are connected to this controller, it must
|
||||
* be the largest minimum delay. 0 means that such a delay is
|
||||
* unknown and the reset operation is unsupported.
|
||||
*/
|
||||
rt_uint32_t reset_us;
|
||||
|
||||
/* protect registers during read-modify-write cycles */
|
||||
struct rt_spinlock lock;
|
||||
};
|
||||
|
||||
extern const struct rt_reset_control_ops reset_simple_ops;
|
||||
|
||||
#endif /* __RESET_SIMPLE_H__ */
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtservice.h>
|
||||
|
||||
#define DBG_TAG "rtdm.reset"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <drivers/ofw.h>
|
||||
#include <drivers/misc.h>
|
||||
#include <drivers/reset.h>
|
||||
#include <drivers/platform.h>
|
||||
|
||||
struct reset_control_array
|
||||
{
|
||||
struct rt_reset_control captain;
|
||||
|
||||
rt_size_t count;
|
||||
struct rt_reset_control *rstcs[];
|
||||
};
|
||||
|
||||
#define reset_control_to_array(rstc) rt_container_of(rstc, struct reset_control_array, captain)
|
||||
|
||||
rt_err_t rt_reset_controller_register(struct rt_reset_controller *rstcer)
|
||||
{
|
||||
if (!rstcer)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
#if RT_NAME_MAX > 0
|
||||
rt_strncpy(rstcer->parent.name, RT_RESET_CONTROLLER_OBJ_NAME, RT_NAME_MAX);
|
||||
#else
|
||||
rstcer->parent.name = RT_RESET_CONTROLLER_OBJ_NAME;
|
||||
#endif
|
||||
|
||||
rt_list_init(&rstcer->rstc_nodes);
|
||||
rt_spin_lock_init(&rstcer->spinlock);
|
||||
|
||||
if (rstcer->ofw_node)
|
||||
{
|
||||
if (!rt_ofw_data(rstcer->ofw_node))
|
||||
{
|
||||
rt_ofw_data(rstcer->ofw_node) = rstcer;
|
||||
}
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
rt_err_t rt_reset_controller_unregister(struct rt_reset_controller *rstcer)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
|
||||
if (!rstcer)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
rt_spin_lock(&rstcer->spinlock);
|
||||
|
||||
if (!rt_list_isempty(&rstcer->rstc_nodes))
|
||||
{
|
||||
err = -RT_EBUSY;
|
||||
goto _out_lock;
|
||||
}
|
||||
|
||||
_out_lock:
|
||||
rt_spin_unlock(&rstcer->spinlock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
rt_err_t rt_reset_control_reset(struct rt_reset_control *rstc)
|
||||
{
|
||||
rt_err_t err;
|
||||
|
||||
if (!rstc)
|
||||
{
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
if (rstc->rstcer->ops->reset)
|
||||
{
|
||||
if ((err = rstc->rstcer->ops->reset(rstc)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (rstc->is_array)
|
||||
{
|
||||
struct reset_control_array *rstc_arr = reset_control_to_array(rstc);
|
||||
|
||||
for (int i = 0; i < rstc_arr->count; ++i)
|
||||
{
|
||||
if ((err = rt_reset_control_reset(rstc_arr->rstcs[i])))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
rt_err_t rt_reset_control_assert(struct rt_reset_control *rstc)
|
||||
{
|
||||
rt_err_t err;
|
||||
|
||||
if (!rstc)
|
||||
{
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
if (rstc->rstcer->ops->assert)
|
||||
{
|
||||
if ((err = rstc->rstcer->ops->assert(rstc)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (rstc->is_array)
|
||||
{
|
||||
struct reset_control_array *rstc_arr = reset_control_to_array(rstc);
|
||||
|
||||
for (int i = 0; i < rstc_arr->count; ++i)
|
||||
{
|
||||
if ((err = rt_reset_control_assert(rstc_arr->rstcs[i])))
|
||||
{
|
||||
if (rstc->rstcer->ops->deassert)
|
||||
{
|
||||
rstc->rstcer->ops->deassert(rstc);
|
||||
}
|
||||
|
||||
while (i --> 0)
|
||||
{
|
||||
rt_reset_control_deassert(rstc_arr->rstcs[i]);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
rt_err_t rt_reset_control_deassert(struct rt_reset_control *rstc)
|
||||
{
|
||||
rt_err_t err;
|
||||
|
||||
if (!rstc)
|
||||
{
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
if (rstc->rstcer->ops->deassert)
|
||||
{
|
||||
if ((err = rstc->rstcer->ops->deassert(rstc)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (rstc->is_array)
|
||||
{
|
||||
struct reset_control_array *rstc_arr = reset_control_to_array(rstc);
|
||||
|
||||
for (int i = 0; i < rstc_arr->count; ++i)
|
||||
{
|
||||
if ((err = rt_reset_control_deassert(rstc_arr->rstcs[i])))
|
||||
{
|
||||
if (rstc->rstcer->ops->assert)
|
||||
{
|
||||
rstc->rstcer->ops->assert(rstc);
|
||||
}
|
||||
|
||||
while (i --> 0)
|
||||
{
|
||||
rt_reset_control_assert(rstc_arr->rstcs[i]);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
int rt_reset_control_status(struct rt_reset_control *rstc)
|
||||
{
|
||||
if (!rstc)
|
||||
{
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
if (rstc->rstcer->ops->status)
|
||||
{
|
||||
return rstc->rstcer->ops->status(rstc);
|
||||
}
|
||||
|
||||
return -RT_ENOSYS;
|
||||
}
|
||||
|
||||
static void reset_free(struct rt_reset_control *rstc)
|
||||
{
|
||||
if (rstc->is_array)
|
||||
{
|
||||
struct reset_control_array *rstc_arr = reset_control_to_array(rstc);
|
||||
|
||||
for (int i = 0; i < rstc_arr->count; ++i)
|
||||
{
|
||||
rt_reset_control_put(rstc_arr->rstcs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
rt_free(rstc);
|
||||
}
|
||||
|
||||
struct rt_reset_control *rt_reset_control_get_array(struct rt_device *dev)
|
||||
{
|
||||
return rt_ofw_get_reset_control_array(dev->ofw_node);
|
||||
}
|
||||
|
||||
struct rt_reset_control *rt_reset_control_get_by_index(struct rt_device *dev, int index)
|
||||
{
|
||||
return rt_ofw_get_reset_control_by_index(dev->ofw_node, index);
|
||||
}
|
||||
|
||||
struct rt_reset_control *rt_reset_control_get_by_name(struct rt_device *dev, const char *name)
|
||||
{
|
||||
return rt_ofw_get_reset_control_by_name(dev->ofw_node, name);
|
||||
}
|
||||
|
||||
void rt_reset_control_put(struct rt_reset_control *rstc)
|
||||
{
|
||||
struct rt_reset_controller *rstcer;
|
||||
|
||||
if (!rstc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rstcer = rstc->rstcer;
|
||||
|
||||
rt_spin_lock(&rstcer->spinlock);
|
||||
|
||||
rt_list_remove(&rstc->list);
|
||||
|
||||
rt_spin_unlock(&rstcer->spinlock);
|
||||
|
||||
reset_free(rstc);
|
||||
}
|
||||
|
||||
static struct rt_reset_control *ofw_get_reset_control(struct rt_ofw_node *np, int index,
|
||||
const char *name, rt_bool_t is_array)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
struct rt_reset_control *rstc;
|
||||
struct rt_ofw_cell_args reset_args = {};
|
||||
struct rt_reset_controller *rstcer = RT_NULL;
|
||||
|
||||
if (is_array)
|
||||
{
|
||||
rt_size_t rstc_nr;
|
||||
struct reset_control_array *rstc_arr;
|
||||
|
||||
rstc_nr = rt_ofw_count_phandle_cells(np, "resets", "#reset-cells");
|
||||
|
||||
if (!rstc_nr)
|
||||
{
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
rstc_arr = rt_calloc(1, sizeof(*rstc_arr) + sizeof(struct rt_reset_control *) * rstc_nr);
|
||||
|
||||
if (!rstc_arr)
|
||||
{
|
||||
LOG_E("No memory to create %s[%d] reset control",
|
||||
rt_ofw_node_full_name(np), index);
|
||||
|
||||
return rt_err_ptr(-RT_ENOMEM);
|
||||
}
|
||||
|
||||
rstc_arr->count = rstc_nr - 1;
|
||||
|
||||
for (int i = 0; i < rstc_arr->count; ++i)
|
||||
{
|
||||
rstc_arr->rstcs[i] = ofw_get_reset_control(np, i + 1, RT_NULL, RT_FALSE);
|
||||
|
||||
if (rt_is_err(rstc_arr->rstcs[i]))
|
||||
{
|
||||
err = rt_ptr_err(rstc_arr->rstcs[i]);
|
||||
|
||||
while (i --> 0)
|
||||
{
|
||||
rt_reset_control_put(rstc_arr->rstcs[i]);
|
||||
}
|
||||
|
||||
rt_free(rstc_arr);
|
||||
|
||||
return rt_err_ptr(err);
|
||||
}
|
||||
}
|
||||
|
||||
rstc = &rstc_arr->captain;
|
||||
rstc->is_array = RT_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
rstc = rt_calloc(1, sizeof(*rstc));
|
||||
|
||||
if (!rstc)
|
||||
{
|
||||
LOG_E("No memory to create %s[%d] reset control",
|
||||
rt_ofw_node_full_name(np), index);
|
||||
|
||||
return rt_err_ptr(-RT_ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rt_ofw_parse_phandle_cells(np, "resets", "#reset-cells", index, &reset_args))
|
||||
{
|
||||
void *rt_data;
|
||||
struct rt_object *obj;
|
||||
struct rt_ofw_node *reset_np = reset_args.data;
|
||||
|
||||
if (!rt_ofw_data(reset_np))
|
||||
{
|
||||
rt_platform_ofw_request(reset_np);
|
||||
}
|
||||
|
||||
rt_data = rt_ofw_data(reset_np);
|
||||
|
||||
if (rt_data && (obj = rt_ofw_parse_object(reset_args.data,
|
||||
RT_RESET_CONTROLLER_OBJ_NAME, "#reset-cells")))
|
||||
{
|
||||
rstcer = rt_container_of(obj, struct rt_reset_controller, parent);
|
||||
}
|
||||
|
||||
rt_ofw_node_put(reset_np);
|
||||
|
||||
if (!rstcer)
|
||||
{
|
||||
err = -RT_EINVAL;
|
||||
goto _fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not reset */
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if (!name && rt_ofw_prop_read_bool(np, "reset-names"))
|
||||
{
|
||||
rt_ofw_prop_read_string_index(np, "reset-names", index, &name);
|
||||
}
|
||||
|
||||
rstc->con_id = name;
|
||||
rstc->rstcer = rstcer;
|
||||
|
||||
if (rstcer->ops->ofw_parse)
|
||||
{
|
||||
err = rstcer->ops->ofw_parse(rstc, &reset_args);
|
||||
|
||||
if (err)
|
||||
{
|
||||
LOG_E("Parse %s reset control error = %s",
|
||||
rt_ofw_node_full_name(np), rt_strerror(err));
|
||||
|
||||
goto _fail;
|
||||
}
|
||||
}
|
||||
|
||||
rstc->id = reset_args.args[0];
|
||||
|
||||
rt_list_init(&rstc->list);
|
||||
|
||||
rt_spin_lock(&rstcer->spinlock);
|
||||
|
||||
rt_list_insert_after(&rstcer->rstc_nodes, &rstc->list);
|
||||
|
||||
rt_spin_unlock(&rstcer->spinlock);
|
||||
|
||||
return rstc;
|
||||
|
||||
_fail:
|
||||
if (rstc && !rstc->is_array)
|
||||
{
|
||||
rt_free(rstc);
|
||||
}
|
||||
|
||||
return rt_err_ptr(err);
|
||||
}
|
||||
|
||||
struct rt_reset_control *rt_ofw_get_reset_control_array(struct rt_ofw_node *np)
|
||||
{
|
||||
return ofw_get_reset_control(np, 0, RT_NULL, RT_TRUE);
|
||||
}
|
||||
|
||||
struct rt_reset_control *rt_ofw_get_reset_control_by_index(struct rt_ofw_node *np, int index)
|
||||
{
|
||||
return ofw_get_reset_control(np, index, RT_NULL, RT_FALSE);
|
||||
}
|
||||
|
||||
struct rt_reset_control *rt_ofw_get_reset_control_by_name(struct rt_ofw_node *np, const char *name)
|
||||
{
|
||||
if (np)
|
||||
{
|
||||
int index = rt_ofw_prop_index_of_string(np, "reset-names", name);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
return ofw_get_reset_control(np, index, name, RT_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return RT_NULL;
|
||||
}
|
Loading…
Reference in New Issue