diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 783215f026..1187175840 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -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" diff --git a/components/drivers/include/drivers/reset.h b/components/drivers/include/drivers/reset.h new file mode 100644 index 0000000000..6339fe931e --- /dev/null +++ b/components/drivers/include/drivers/reset.h @@ -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 +#include +#include + +#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__ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index aa56b6820c..34152cefb6 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.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 */ diff --git a/components/drivers/ofw/ofw.c b/components/drivers/ofw/ofw.c index 1698cb7386..76389f3416 100644 --- a/components/drivers/ofw/ofw.c +++ b/components/drivers/ofw/ofw.c @@ -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) }, diff --git a/components/drivers/reset/Kconfig b/components/drivers/reset/Kconfig new file mode 100644 index 0000000000..ccba575750 --- /dev/null +++ b/components/drivers/reset/Kconfig @@ -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 diff --git a/components/drivers/reset/SConscript b/components/drivers/reset/SConscript new file mode 100755 index 0000000000..8e64f78643 --- /dev/null +++ b/components/drivers/reset/SConscript @@ -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') diff --git a/components/drivers/reset/reset-simple.c b/components/drivers/reset/reset-simple.c new file mode 100644 index 0000000000..e66cb8efcc --- /dev/null +++ b/components/drivers/reset/reset-simple.c @@ -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); diff --git a/components/drivers/reset/reset-simple.h b/components/drivers/reset/reset-simple.h new file mode 100644 index 0000000000..8223d698c9 --- /dev/null +++ b/components/drivers/reset/reset-simple.h @@ -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 +#include + +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__ */ diff --git a/components/drivers/reset/reset.c b/components/drivers/reset/reset.c new file mode 100644 index 0000000000..290d2a7014 --- /dev/null +++ b/components/drivers/reset/reset.c @@ -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 +#include + +#define DBG_TAG "rtdm.reset" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include + +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; +}