summaryrefslogtreecommitdiffstats
path: root/drivers/base/featctrl.c
blob: abe21698ede743d4b0992bb515acbaf8fb284447 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: 2022 Ahmad Fatoum, Pengutronix

#define pr_fmt(fmt) "featctrl: " fmt

#include <common.h>
#include <driver.h>
#include <errno.h>
#include <of.h>

#include <featctrl.h>

/* 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