// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. #include "reset-group.h" #include #include #include #include #include struct camrtc_reset_group { struct device *device; const char *group_name; int nresets; struct reset_control *resets[]; }; static void camrtc_reset_group_release(struct device *dev, void *res) { const struct camrtc_reset_group *grp = res; int i; for (i = 0; i < grp->nresets; i++) { if (grp->resets[i]) reset_control_put(grp->resets[i]); } } struct camrtc_reset_group *camrtc_reset_group_get( struct device *dev, const char *group_name) { struct camrtc_reset_group *grp; struct device_node *np; const char *group_property; size_t group_name_len; int index; int ret; if (!dev || !dev->of_node) return ERR_PTR(-EINVAL); np = dev->of_node; group_property = group_name ? group_name : "reset-names"; group_name_len = group_name ? strlen(group_name) : 0; ret = of_property_count_strings(np, group_property); if (ret < 0) return ERR_PTR(-ENOENT); grp = devres_alloc(camrtc_reset_group_release, offsetof(struct camrtc_reset_group, resets[ret]) + group_name_len + 1, GFP_KERNEL); if (!grp) return ERR_PTR(-ENOMEM); grp->nresets = ret; grp->device = dev; grp->group_name = (char *)&grp->resets[grp->nresets]; memcpy((char *)grp->group_name, group_name, group_name_len); for (index = 0; index < grp->nresets; index++) { char const *name; struct reset_control *reset; ret = of_property_read_string_index(np, group_property, index, &name); if (ret < 0) goto error; reset = of_reset_control_get(np, name); if (IS_ERR(reset)) { ret = PTR_ERR(reset); goto error; } grp->resets[index] = reset; } devres_add(dev, grp); return grp; error: devres_free(grp); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(camrtc_reset_group_get); static void camrtc_reset_group_error( const struct camrtc_reset_group *grp, char const *op, int index, int error) { const char *name = "unnamed"; int ret; ret = of_property_read_string_index(grp->device->of_node, grp->group_name, index, &name); if (ret < 0) dev_warn(grp->device, "Cannot find reset in %s\n", grp->group_name); dev_warn(grp->device, "%s reset %s (at %s[%d]): %d\n", op, name, grp->group_name, index, error); } void camrtc_reset_group_assert(const struct camrtc_reset_group *grp) { int index, index0, err; if (IS_ERR_OR_NULL(grp)) return; for (index = 1; index <= grp->nresets; index++) { index0 = grp->nresets - index; err = reset_control_assert(grp->resets[index0]); if (err < 0) camrtc_reset_group_error(grp, "assert", index0, err); } } EXPORT_SYMBOL_GPL(camrtc_reset_group_assert); int camrtc_reset_group_deassert(const struct camrtc_reset_group *grp) { int index, err; if (!grp) return 0; if (IS_ERR(grp)) return -ENODEV; for (index = 0; index < grp->nresets; index++) { err = reset_control_deassert(grp->resets[index]); if (err < 0) { camrtc_reset_group_error(grp, "deassert", index, err); return err; } } return 0; } EXPORT_SYMBOL_GPL(camrtc_reset_group_deassert); MODULE_LICENSE("GPL v2");