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