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