1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * RTC interface for Wilco Embedded Controller with R/W abilities 4 * 5 * Copyright 2018 Google LLC 6 * 7 * The corresponding platform device is typically registered in 8 * drivers/platform/chrome/wilco_ec/core.c 9 */ 10 11 #include <linux/bcd.h> 12 #include <linux/err.h> 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/platform_device.h> 16 #include <linux/platform_data/wilco-ec.h> 17 #include <linux/rtc.h> 18 #include <linux/timekeeping.h> 19 20 #define EC_COMMAND_CMOS 0x7c 21 #define EC_CMOS_TOD_WRITE 0x02 22 #define EC_CMOS_TOD_READ 0x08 23 24 /* Message sent to the EC to request the current time. */ 25 struct ec_rtc_read_request { 26 u8 command; 27 u8 reserved; 28 u8 param; 29 } __packed; 30 static struct ec_rtc_read_request read_rq = { 31 .command = EC_COMMAND_CMOS, 32 .param = EC_CMOS_TOD_READ, 33 }; 34 35 /** 36 * struct ec_rtc_read_response - Format of RTC returned by EC. 37 * @reserved: Unused byte 38 * @second: Second value (0..59) 39 * @minute: Minute value (0..59) 40 * @hour: Hour value (0..23) 41 * @day: Day value (1..31) 42 * @month: Month value (1..12) 43 * @year: Year value (full year % 100) 44 * @century: Century value (full year / 100) 45 * 46 * All values are presented in binary (not BCD). 47 */ 48 struct ec_rtc_read_response { 49 u8 reserved; 50 u8 second; 51 u8 minute; 52 u8 hour; 53 u8 day; 54 u8 month; 55 u8 year; 56 u8 century; 57 } __packed; 58 59 /** 60 * struct ec_rtc_write_request - Format of RTC sent to the EC. 61 * @command: Always EC_COMMAND_CMOS 62 * @reserved: Unused byte 63 * @param: Always EC_CMOS_TOD_WRITE 64 * @century: Century value (full year / 100) 65 * @year: Year value (full year % 100) 66 * @month: Month value (1..12) 67 * @day: Day value (1..31) 68 * @hour: Hour value (0..23) 69 * @minute: Minute value (0..59) 70 * @second: Second value (0..59) 71 * @weekday: Day of the week (0=Saturday) 72 * 73 * All values are presented in BCD. 74 */ 75 struct ec_rtc_write_request { 76 u8 command; 77 u8 reserved; 78 u8 param; 79 u8 century; 80 u8 year; 81 u8 month; 82 u8 day; 83 u8 hour; 84 u8 minute; 85 u8 second; 86 u8 weekday; 87 } __packed; 88 89 static int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm) 90 { 91 struct wilco_ec_device *ec = dev_get_drvdata(dev->parent); 92 struct ec_rtc_read_response rtc; 93 struct wilco_ec_message msg; 94 int ret; 95 96 memset(&msg, 0, sizeof(msg)); 97 msg.type = WILCO_EC_MSG_LEGACY; 98 msg.request_data = &read_rq; 99 msg.request_size = sizeof(read_rq); 100 msg.response_data = &rtc; 101 msg.response_size = sizeof(rtc); 102 103 ret = wilco_ec_mailbox(ec, &msg); 104 if (ret < 0) 105 return ret; 106 107 tm->tm_sec = rtc.second; 108 tm->tm_min = rtc.minute; 109 tm->tm_hour = rtc.hour; 110 tm->tm_mday = rtc.day; 111 tm->tm_mon = rtc.month - 1; 112 tm->tm_year = rtc.year + (rtc.century * 100) - 1900; 113 tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); 114 115 /* Don't compute day of week, we don't need it. */ 116 tm->tm_wday = -1; 117 118 return 0; 119 } 120 121 static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm) 122 { 123 struct wilco_ec_device *ec = dev_get_drvdata(dev->parent); 124 struct ec_rtc_write_request rtc; 125 struct wilco_ec_message msg; 126 int year = tm->tm_year + 1900; 127 /* 128 * Convert from 0=Sunday to 0=Saturday for the EC 129 * We DO need to set weekday because the EC controls battery charging 130 * schedules that depend on the day of the week. 131 */ 132 int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1; 133 int ret; 134 135 rtc.command = EC_COMMAND_CMOS; 136 rtc.param = EC_CMOS_TOD_WRITE; 137 rtc.century = bin2bcd(year / 100); 138 rtc.year = bin2bcd(year % 100); 139 rtc.month = bin2bcd(tm->tm_mon + 1); 140 rtc.day = bin2bcd(tm->tm_mday); 141 rtc.hour = bin2bcd(tm->tm_hour); 142 rtc.minute = bin2bcd(tm->tm_min); 143 rtc.second = bin2bcd(tm->tm_sec); 144 rtc.weekday = bin2bcd(wday); 145 146 memset(&msg, 0, sizeof(msg)); 147 msg.type = WILCO_EC_MSG_LEGACY; 148 msg.request_data = &rtc; 149 msg.request_size = sizeof(rtc); 150 151 ret = wilco_ec_mailbox(ec, &msg); 152 if (ret < 0) 153 return ret; 154 155 return 0; 156 } 157 158 static const struct rtc_class_ops wilco_ec_rtc_ops = { 159 .read_time = wilco_ec_rtc_read, 160 .set_time = wilco_ec_rtc_write, 161 }; 162 163 static int wilco_ec_rtc_probe(struct platform_device *pdev) 164 { 165 struct rtc_device *rtc; 166 167 rtc = devm_rtc_allocate_device(&pdev->dev); 168 if (IS_ERR(rtc)) 169 return PTR_ERR(rtc); 170 171 rtc->ops = &wilco_ec_rtc_ops; 172 /* EC only supports this century */ 173 rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 174 rtc->range_max = RTC_TIMESTAMP_END_2099; 175 rtc->owner = THIS_MODULE; 176 177 return rtc_register_device(rtc); 178 } 179 180 static struct platform_driver wilco_ec_rtc_driver = { 181 .driver = { 182 .name = "rtc-wilco-ec", 183 }, 184 .probe = wilco_ec_rtc_probe, 185 }; 186 187 module_platform_driver(wilco_ec_rtc_driver); 188 189 MODULE_ALIAS("platform:rtc-wilco-ec"); 190 MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>"); 191 MODULE_LICENSE("GPL v2"); 192 MODULE_DESCRIPTION("Wilco EC RTC driver"); 193