1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c), Vaisala Oyj
4  */
5 
6 #include <common.h>
7 #include <dm.h>
8 #include <dm/device_compat.h>
9 #include <dm/devres.h>
10 #include <exports.h>
11 #include <reboot-mode/reboot-mode.h>
12 
dm_reboot_mode_update(struct udevice * dev)13 int dm_reboot_mode_update(struct udevice *dev)
14 {
15 	struct reboot_mode_ops *ops = reboot_mode_get_ops(dev);
16 	u32 rebootmode;
17 	int ret, i;
18 
19 	assert(ops);
20 
21 	if (!ops->get)
22 		return -ENOSYS;
23 
24 	ret = ops->get(dev, &rebootmode);
25 	if (ret < 0) {
26 		dev_err(dev, "Failed to retrieve the reboot mode value\n");
27 		return ret;
28 	}
29 
30 	const struct reboot_mode_uclass_platdata *plat_data =
31 		dev_get_uclass_plat(dev);
32 
33 	for (i = 0; i < plat_data->count; i++) {
34 		if (plat_data->modes[i].mode_id == rebootmode) {
35 			ret = env_set(plat_data->env_variable,
36 				      plat_data->modes[i].mode_name);
37 			if (ret) {
38 				dev_err(dev, "Failed to set env: %s\n",
39 					plat_data->env_variable);
40 				return ret;
41 			}
42 		}
43 	}
44 
45 	if (ops->set) {
46 		/* Clear the value */
47 		rebootmode = 0;
48 		ret = ops->set(dev, rebootmode);
49 		if (ret) {
50 			dev_err(dev, "Failed to clear the reboot mode\n");
51 			return ret;
52 		}
53 	}
54 
55 	return 0;
56 }
57 
dm_reboot_mode_pre_probe(struct udevice * dev)58 int dm_reboot_mode_pre_probe(struct udevice *dev)
59 {
60 	struct reboot_mode_uclass_platdata *plat_data;
61 
62 	plat_data = dev_get_uclass_plat(dev);
63 	if (!plat_data)
64 		return -EINVAL;
65 
66 #if CONFIG_IS_ENABLED(OF_CONTROL)
67 	const char *mode_prefix = "mode-";
68 	const int mode_prefix_len = strlen(mode_prefix);
69 	struct ofprop property;
70 	const u32 *propvalue;
71 	const char *propname;
72 
73 	plat_data->env_variable = dev_read_string(dev, "u-boot,env-variable");
74 	if (!plat_data->env_variable)
75 		plat_data->env_variable = "reboot-mode";
76 
77 	plat_data->count = 0;
78 
79 	dev_for_each_property(property, dev) {
80 		propvalue = dev_read_prop_by_prop(&property, &propname, NULL);
81 		if (!propvalue) {
82 			dev_err(dev, "Could not get the value for property %s\n",
83 				propname);
84 			return -EINVAL;
85 		}
86 
87 		if (!strncmp(propname, mode_prefix, mode_prefix_len))
88 			plat_data->count++;
89 	}
90 
91 	plat_data->modes = devm_kcalloc(dev, plat_data->count,
92 					sizeof(struct reboot_mode_mode), 0);
93 
94 	struct reboot_mode_mode *next = plat_data->modes;
95 
96 	dev_for_each_property(property, dev) {
97 		propvalue = dev_read_prop_by_prop(&property, &propname, NULL);
98 		if (!propvalue) {
99 			dev_err(dev, "Could not get the value for property %s\n",
100 				propname);
101 			return -EINVAL;
102 		}
103 
104 		if (!strncmp(propname, mode_prefix, mode_prefix_len)) {
105 			next->mode_name = &propname[mode_prefix_len];
106 			next->mode_id = fdt32_to_cpu(*propvalue);
107 
108 			next++;
109 		}
110 	}
111 #else
112 	if (!plat_data->env_variable)
113 		plat_data->env_variable = "reboot-mode";
114 
115 #endif
116 
117 	return 0;
118 }
119 
120 UCLASS_DRIVER(reboot_mode) = {
121 	.name	= "reboot-mode",
122 	.id	= UCLASS_REBOOT_MODE,
123 	.pre_probe	= dm_reboot_mode_pre_probe,
124 	.per_device_plat_auto =
125 		sizeof(struct reboot_mode_uclass_platdata),
126 };
127