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 <reboot-mode/reboot-mode-rtc.h>
10 #include <reboot-mode/reboot-mode.h>
11 #include <rtc.h>
12 
13 DECLARE_GLOBAL_DATA_PTR;
14 
reboot_mode_get(struct udevice * dev,u32 * buf)15 static int reboot_mode_get(struct udevice *dev, u32 *buf)
16 {
17 	if (!buf)
18 		return -EINVAL;
19 
20 	int ret;
21 	u8 *val = (u8 *)buf;
22 	struct reboot_mode_rtc_platdata *plat_data;
23 
24 	plat_data = dev_get_plat(dev);
25 	if (!plat_data)
26 		return -EINVAL;
27 
28 	for (int i = 0; i < plat_data->size; i++) {
29 		ret = rtc_read8(plat_data->rtc, plat_data->addr + i);
30 		if (ret < 0)
31 			return ret;
32 
33 		val[i] = ret;
34 	}
35 
36 	if (plat_data->is_big_endian)
37 		*buf = __be32_to_cpu(*buf);
38 	else
39 		*buf = __le32_to_cpu(*buf);
40 
41 	return 0;
42 }
43 
reboot_mode_set(struct udevice * dev,u32 buf)44 static int reboot_mode_set(struct udevice *dev, u32 buf)
45 {
46 	int ret;
47 	u8 *val;
48 	struct reboot_mode_rtc_platdata *plat_data;
49 
50 	plat_data = dev_get_plat(dev);
51 	if (!plat_data)
52 		return -EINVAL;
53 
54 	if (plat_data->is_big_endian)
55 		buf = __cpu_to_be32(buf);
56 	else
57 		buf = __cpu_to_le32(buf);
58 
59 	val = (u8 *)&buf;
60 
61 	for (int i = 0; i < plat_data->size; i++) {
62 		ret = rtc_write8(plat_data->rtc, (plat_data->addr + i), val[i]);
63 		if (ret < 0)
64 			return ret;
65 	}
66 
67 	return 0;
68 }
69 
70 #if CONFIG_IS_ENABLED(OF_CONTROL)
reboot_mode_ofdata_to_platdata(struct udevice * dev)71 static int reboot_mode_ofdata_to_platdata(struct udevice *dev)
72 {
73 	struct ofnode_phandle_args phandle_args;
74 	struct reboot_mode_rtc_platdata *plat_data;
75 
76 	plat_data = dev_get_plat(dev);
77 	if (!plat_data)
78 		return -EINVAL;
79 
80 	if (dev_read_phandle_with_args(dev, "rtc", NULL, 0, 0, &phandle_args)) {
81 		dev_err(dev, "RTC device not specified\n");
82 		return -ENOENT;
83 	}
84 
85 	if (uclass_get_device_by_ofnode(UCLASS_RTC, phandle_args.node,
86 					&plat_data->rtc)) {
87 		dev_err(dev, "could not get the RTC device\n");
88 		return -ENODEV;
89 	}
90 
91 	plat_data->addr =
92 		dev_read_addr_size_index(dev, 0, (fdt_size_t *)&plat_data->size);
93 	if (plat_data->addr == FDT_ADDR_T_NONE) {
94 		dev_err(dev, "Invalid RTC address\n");
95 		return -EINVAL;
96 	}
97 	if (plat_data->size > sizeof(u32)) {
98 		dev_err(dev, "Invalid reg size\n");
99 		return -EINVAL;
100 	}
101 
102 	plat_data->is_big_endian = ofnode_read_bool(dev_ofnode(dev), "big-endian");
103 
104 	return 0;
105 }
106 
107 static const struct udevice_id reboot_mode_ids[] = {
108 	{ .compatible = "reboot-mode-rtc", 0 },
109 	{}
110 };
111 #endif
112 
113 static const struct reboot_mode_ops reboot_mode_rtc_ops = {
114 	.get = reboot_mode_get,
115 	.set = reboot_mode_set,
116 };
117 
118 U_BOOT_DRIVER(reboot_mode_rtc) = {
119 	.name = "reboot-mode-rtc",
120 	.id = UCLASS_REBOOT_MODE,
121 #if CONFIG_IS_ENABLED(OF_CONTROL)
122 	.of_match = reboot_mode_ids,
123 	.of_to_plat = reboot_mode_ofdata_to_platdata,
124 #endif
125 	.plat_auto = sizeof(struct reboot_mode_rtc_platdata),
126 	.ops = &reboot_mode_rtc_ops,
127 };
128