Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * RTC interface for Wilco Embedded Controller with R/W abilities
0004  *
0005  * Copyright 2018 Google LLC
0006  *
0007  * The corresponding platform device is typically registered in
0008  * drivers/platform/chrome/wilco_ec/core.c
0009  */
0010 
0011 #include <linux/bcd.h>
0012 #include <linux/err.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/platform_data/wilco-ec.h>
0017 #include <linux/rtc.h>
0018 #include <linux/timekeeping.h>
0019 
0020 #define EC_COMMAND_CMOS         0x7c
0021 #define EC_CMOS_TOD_WRITE       0x02
0022 #define EC_CMOS_TOD_READ        0x08
0023 
0024 /* Message sent to the EC to request the current time. */
0025 struct ec_rtc_read_request {
0026     u8 command;
0027     u8 reserved;
0028     u8 param;
0029 } __packed;
0030 static struct ec_rtc_read_request read_rq = {
0031     .command = EC_COMMAND_CMOS,
0032     .param = EC_CMOS_TOD_READ,
0033 };
0034 
0035 /**
0036  * struct ec_rtc_read_response - Format of RTC returned by EC.
0037  * @reserved: Unused byte
0038  * @second: Second value (0..59)
0039  * @minute: Minute value (0..59)
0040  * @hour: Hour value (0..23)
0041  * @day: Day value (1..31)
0042  * @month: Month value (1..12)
0043  * @year: Year value (full year % 100)
0044  * @century: Century value (full year / 100)
0045  *
0046  * All values are presented in binary (not BCD).
0047  */
0048 struct ec_rtc_read_response {
0049     u8 reserved;
0050     u8 second;
0051     u8 minute;
0052     u8 hour;
0053     u8 day;
0054     u8 month;
0055     u8 year;
0056     u8 century;
0057 } __packed;
0058 
0059 /**
0060  * struct ec_rtc_write_request - Format of RTC sent to the EC.
0061  * @command: Always EC_COMMAND_CMOS
0062  * @reserved: Unused byte
0063  * @param: Always EC_CMOS_TOD_WRITE
0064  * @century: Century value (full year / 100)
0065  * @year: Year value (full year % 100)
0066  * @month: Month value (1..12)
0067  * @day: Day value (1..31)
0068  * @hour: Hour value (0..23)
0069  * @minute: Minute value (0..59)
0070  * @second: Second value (0..59)
0071  * @weekday: Day of the week (0=Saturday)
0072  *
0073  * All values are presented in BCD.
0074  */
0075 struct ec_rtc_write_request {
0076     u8 command;
0077     u8 reserved;
0078     u8 param;
0079     u8 century;
0080     u8 year;
0081     u8 month;
0082     u8 day;
0083     u8 hour;
0084     u8 minute;
0085     u8 second;
0086     u8 weekday;
0087 } __packed;
0088 
0089 static int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm)
0090 {
0091     struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
0092     struct ec_rtc_read_response rtc;
0093     struct wilco_ec_message msg;
0094     int ret;
0095 
0096     memset(&msg, 0, sizeof(msg));
0097     msg.type = WILCO_EC_MSG_LEGACY;
0098     msg.request_data = &read_rq;
0099     msg.request_size = sizeof(read_rq);
0100     msg.response_data = &rtc;
0101     msg.response_size = sizeof(rtc);
0102 
0103     ret = wilco_ec_mailbox(ec, &msg);
0104     if (ret < 0)
0105         return ret;
0106 
0107     tm->tm_sec  = rtc.second;
0108     tm->tm_min  = rtc.minute;
0109     tm->tm_hour = rtc.hour;
0110     tm->tm_mday = rtc.day;
0111     tm->tm_mon  = rtc.month - 1;
0112     tm->tm_year = rtc.year + (rtc.century * 100) - 1900;
0113     /* Ignore other tm fields, man rtc says userspace shouldn't use them. */
0114 
0115     if (rtc_valid_tm(tm)) {
0116         dev_err(dev, "Time from RTC is invalid: %ptRr\n", tm);
0117         return -EIO;
0118     }
0119 
0120     return 0;
0121 }
0122 
0123 static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm)
0124 {
0125     struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
0126     struct ec_rtc_write_request rtc;
0127     struct wilco_ec_message msg;
0128     int year = tm->tm_year + 1900;
0129     /*
0130      * Convert from 0=Sunday to 0=Saturday for the EC
0131      * We DO need to set weekday because the EC controls battery charging
0132      * schedules that depend on the day of the week.
0133      */
0134     int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1;
0135     int ret;
0136 
0137     rtc.command = EC_COMMAND_CMOS;
0138     rtc.param   = EC_CMOS_TOD_WRITE;
0139     rtc.century = bin2bcd(year / 100);
0140     rtc.year    = bin2bcd(year % 100);
0141     rtc.month   = bin2bcd(tm->tm_mon + 1);
0142     rtc.day     = bin2bcd(tm->tm_mday);
0143     rtc.hour    = bin2bcd(tm->tm_hour);
0144     rtc.minute  = bin2bcd(tm->tm_min);
0145     rtc.second  = bin2bcd(tm->tm_sec);
0146     rtc.weekday = bin2bcd(wday);
0147 
0148     memset(&msg, 0, sizeof(msg));
0149     msg.type = WILCO_EC_MSG_LEGACY;
0150     msg.request_data = &rtc;
0151     msg.request_size = sizeof(rtc);
0152 
0153     ret = wilco_ec_mailbox(ec, &msg);
0154     if (ret < 0)
0155         return ret;
0156 
0157     return 0;
0158 }
0159 
0160 static const struct rtc_class_ops wilco_ec_rtc_ops = {
0161     .read_time = wilco_ec_rtc_read,
0162     .set_time = wilco_ec_rtc_write,
0163 };
0164 
0165 static int wilco_ec_rtc_probe(struct platform_device *pdev)
0166 {
0167     struct rtc_device *rtc;
0168 
0169     rtc = devm_rtc_allocate_device(&pdev->dev);
0170     if (IS_ERR(rtc))
0171         return PTR_ERR(rtc);
0172 
0173     rtc->ops = &wilco_ec_rtc_ops;
0174     /* EC only supports this century */
0175     rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
0176     rtc->range_max = RTC_TIMESTAMP_END_2099;
0177     rtc->owner = THIS_MODULE;
0178 
0179     return devm_rtc_register_device(rtc);
0180 }
0181 
0182 static struct platform_driver wilco_ec_rtc_driver = {
0183     .driver = {
0184         .name = "rtc-wilco-ec",
0185     },
0186     .probe = wilco_ec_rtc_probe,
0187 };
0188 
0189 module_platform_driver(wilco_ec_rtc_driver);
0190 
0191 MODULE_ALIAS("platform:rtc-wilco-ec");
0192 MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
0193 MODULE_LICENSE("GPL v2");
0194 MODULE_DESCRIPTION("Wilco EC RTC driver");