// SPDX-License-Identifier: GPL-2.0-only // SPDX-FileCopyrightText: 2022 Ahmad Fatoum, Pengutronix #define pr_fmt(fmt) "featctrl: " fmt #include #include #include #include #include /* List of registered feature controllers */ static LIST_HEAD(of_feature_controllers); /** * feature_controller_register() - Register a feature controller * @feat: Pointer to feature controller */ int feature_controller_register(struct feature_controller *feat) { struct device_node *np = dev_of_node(feat->dev); if (!np) return -EINVAL; list_add(&feat->list, &of_feature_controllers); dev_dbg(feat->dev, "Registering feature controller\n"); return 0; } EXPORT_SYMBOL_GPL(feature_controller_register); /** * featctrl_get_from_provider() - Look-up feature gate * @spec: OF phandle args to use for look-up * @gateid: ID of feature controller gate populated on successful lookup * * Looks for a feature controller under the node specified by @spec. * * Returns a valid pointer to struct feature_controller on success or ERR_PTR() * on failure. */ static struct feature_controller *featctrl_get_from_provider(struct of_phandle_args *spec, unsigned *gateid) { struct feature_controller *featctrl; int ret; if (!spec) return ERR_PTR(-EINVAL); ret = of_device_ensure_probed(spec->np); if (ret < 0) return ERR_PTR(ret); /* Check if we have such a controller in our array */ list_for_each_entry(featctrl, &of_feature_controllers, list) { if (dev_of_node(featctrl->dev) == spec->np) { *gateid = spec->args[0]; return featctrl; } } return ERR_PTR(-ENOENT); } /** * of_feature_controller_check - Check whether a feature controller gates the device * @np: Device node to check * * Parse device's OF node to find a feature controller specifier. If such is * found, checks it to determine whether device is gated. * * Returns FEATCTRL_GATED if a specified feature controller gates the device * and FEATCTRL_OKAY if none do. On error a negative error code is returned. */ int of_feature_controller_check(struct device_node *np) { struct of_phandle_args featctrl_args; struct feature_controller *featctrl; int ret, err = 0, i, ngates; ngates = of_count_phandle_with_args(np, "barebox,feature-gates", "#feature-cells"); if (ngates <= 0) return FEATCTRL_OKAY; for (i = 0; i < ngates; i++) { unsigned gateid = 0; ret = of_parse_phandle_with_args(np, "barebox,feature-gates", "#feature-cells", i, &featctrl_args); if (ret < 0) return ret; featctrl = featctrl_get_from_provider(&featctrl_args, &gateid); if (IS_ERR(featctrl)) { ret = PTR_ERR(featctrl); pr_debug("%s() failed to find feature controller: %pe\n", __func__, ERR_PTR(ret)); /* * Assume that missing featctrls are unresolved * dependency are report them as deferred */ return (ret == -ENOENT) ? -EPROBE_DEFER : ret; } ret = featctrl->check(featctrl, gateid); dev_dbg(featctrl->dev, "checking %s: %d\n", np->full_name, ret); if (ret == FEATCTRL_OKAY) return FEATCTRL_OKAY; if (ret != FEATCTRL_GATED) err = ret; } return err ?: FEATCTRL_GATED; } EXPORT_SYMBOL_GPL(of_feature_controller_check); static int of_featctrl_fixup(struct device_node *root, void *context) { struct device_node *srcnp, *dstnp; int err = 0; for_each_node_with_property(srcnp, "barebox,feature-gates") { int ret; ret = of_feature_controller_check(srcnp); if (ret < 0) err = ret; if (ret != FEATCTRL_GATED) continue; dstnp = of_get_node_by_reproducible_name(root, srcnp); if (!dstnp) { pr_debug("gated node %s not in fixup DT\n", srcnp->full_name); continue; } pr_debug("fixing up gating of node %s\n", dstnp->full_name); /* Convention is deleting non-existing CPUs, not disable them. */ if (of_property_match_string(srcnp, "device_type", "cpu") >= 0) of_delete_node(dstnp); else of_device_disable(dstnp); } return err; } static __maybe_unused int of_featctrl_fixup_register(void) { return of_register_fixup(of_featctrl_fixup, NULL); } #ifdef CONFIG_FEATURE_CONTROLLER_FIXUP device_initcall(of_featctrl_fixup_register); #endif