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