From: Duncan Laurie <dlau...@google.com> This Embedded Controller has an internal RTC that is exposed as a standard RTC class driver with read/write functionality.
> hwclock --show --rtc /dev/rtc1 2007-12-31 16:01:20.460959-08:00 > hwclock --systohc --rtc /dev/rtc1 > hwclock --show --rtc /dev/rtc1 2018-11-29 17:08:00.780793-08:00 Signed-off-by: Duncan Laurie <dlau...@google.com> Signed-off-by: Nick Crews <ncr...@google.com> --- drivers/platform/chrome/Makefile | 3 +- drivers/platform/chrome/wilco_ec.h | 29 ++++ drivers/platform/chrome/wilco_ec_mailbox.c | 15 ++ drivers/platform/chrome/wilco_ec_rtc.c | 163 +++++++++++++++++++++ 4 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/chrome/wilco_ec_rtc.c diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index e8603bc5b095..5ca484c2d0d7 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -13,5 +13,6 @@ cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o -wilco_ec-objs := wilco_ec_mailbox.o wilco_ec_sysfs.o +wilco_ec-objs := wilco_ec_mailbox.o wilco_ec_rtc.o \ + wilco_ec_sysfs.o obj-$(CONFIG_WILCO_EC) += wilco_ec.o diff --git a/drivers/platform/chrome/wilco_ec.h b/drivers/platform/chrome/wilco_ec.h index 0b3dec4e2830..eee5c514e720 100644 --- a/drivers/platform/chrome/wilco_ec.h +++ b/drivers/platform/chrome/wilco_ec.h @@ -19,6 +19,7 @@ #include <linux/device.h> #include <linux/kernel.h> +#include <linux/rtc.h> /* Normal commands have a maximum 32 bytes of data */ #define EC_MAILBOX_DATA_SIZE 32 @@ -55,6 +56,7 @@ enum wilco_ec_msg_type { * @data_buffer: Buffer used for EC communication. The same buffer * is used to hold the request and the response. * @data_size: Size of the data buffer used for EC communication. + * @rtc: RTC device handler. */ struct wilco_ec_device { struct device *dev; @@ -64,6 +66,7 @@ struct wilco_ec_device { struct resource *io_packet; void *data_buffer; size_t data_size; + struct rtc_device *rtc; }; /** @@ -114,4 +117,30 @@ int wilco_ec_sysfs_init(struct wilco_ec_device *ec); */ void wilco_ec_sysfs_remove(struct wilco_ec_device *ec); +/** + * wilco_ec_rtc_read() - Fill RTC time structure with values from the EC. + * @dev: EC device. + * @tm: Returns RTC time from EC. + * + * Return: 0 for success or negative error code on failure. + */ +int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm); + +/** + * wilco_ec_rtc_write() - Write EC time/date from RTC time structure. + * @dev: EC device. + * @tm: RTC time to write to EC. + * + * Return: 0 for success or negative error code on failure. + */ +int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm); + +/** + * wilco_ec_rtc_sync() - Write EC time/date from system time. + * @dev: EC device. + * + * Return: 0 for success or negative error code on failure. + */ +int wilco_ec_rtc_sync(struct device *dev); + #endif /* WILCO_EC_H */ diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c b/drivers/platform/chrome/wilco_ec_mailbox.c index 1cb34b7280fd..2f093a281a30 100644 --- a/drivers/platform/chrome/wilco_ec_mailbox.c +++ b/drivers/platform/chrome/wilco_ec_mailbox.c @@ -33,6 +33,7 @@ #include <linux/mfd/cros_ec_lpc_mec.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/rtc.h> #include "wilco_ec.h" /* Version of mailbox interface */ @@ -325,6 +326,11 @@ static struct resource *wilco_get_resource(struct platform_device *pdev, return res; } +static const struct rtc_class_ops wilco_ec_rtc_ops = { + .read_time = wilco_ec_rtc_read, + .set_time = wilco_ec_rtc_write, +}; + static int wilco_ec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -355,6 +361,15 @@ static int wilco_ec_probe(struct platform_device *pdev) cros_ec_lpc_mec_init(ec->io_packet->start, ec->io_packet->start + EC_MAILBOX_DATA_SIZE); + /* Install RTC driver */ + ec->rtc = devm_rtc_device_register(ec->dev, "wilco_ec", + &wilco_ec_rtc_ops, THIS_MODULE); + if (IS_ERR(ec->rtc)) { + dev_err(dev, "Failed to install RTC driver\n"); + cros_ec_lpc_mec_destroy(); + return PTR_ERR(ec->rtc); + } + /* Create sysfs attributes for userspace interaction */ if (wilco_ec_sysfs_init(ec) < 0) { dev_err(dev, "Failed to create sysfs attributes\n"); diff --git a/drivers/platform/chrome/wilco_ec_rtc.c b/drivers/platform/chrome/wilco_ec_rtc.c new file mode 100644 index 000000000000..3e36403d0cb8 --- /dev/null +++ b/drivers/platform/chrome/wilco_ec_rtc.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * wilco_ec_rtc - RTC interface for Wilco Embedded Controller + * + * Copyright 2018 Google LLC + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/bcd.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/timekeeping.h> +#include "wilco_ec.h" + +#define EC_COMMAND_CMOS 0x7c +#define EC_CMOS_TOD_WRITE 0x02 +#define EC_CMOS_TOD_READ 0x08 + +/** + * struct ec_rtc_read - Format of RTC returned by EC. + * @second: Second value (0..59) + * @minute: Minute value (0..59) + * @hour: Hour value (0..23) + * @day: Day value (1..31) + * @month: Month value (1..12) + * @year: Year value (full year % 100) + * @century: Century value (full year / 100) + * + * All values are presented in binary (not BCD). + */ +struct ec_rtc_read { + u8 second; + u8 minute; + u8 hour; + u8 day; + u8 month; + u8 year; + u8 century; +} __packed; + +/** + * struct ec_rtc_write - Format of RTC sent to the EC. + * @param: EC_CMOS_TOD_WRITE + * @century: Century value (full year / 100) + * @year: Year value (full year % 100) + * @month: Month value (1..12) + * @day: Day value (1..31) + * @hour: Hour value (0..23) + * @minute: Minute value (0..59) + * @second: Second value (0..59) + * @weekday: Day of the week (0=Saturday) + * + * All values are presented in BCD. + */ +struct ec_rtc_write { + u8 param; + u8 century; + u8 year; + u8 month; + u8 day; + u8 hour; + u8 minute; + u8 second; + u8 weekday; +} __packed; + +int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm) +{ + struct wilco_ec_device *ec = dev_get_drvdata(dev); + u8 param = EC_CMOS_TOD_READ; + struct ec_rtc_read rtc; + struct wilco_ec_message msg = { + .type = WILCO_EC_MSG_LEGACY, + .flags = WILCO_EC_FLAG_RAW_RESPONSE, + .command = EC_COMMAND_CMOS, + .request_data = ¶m, + .request_size = sizeof(param), + .response_data = &rtc, + .response_size = sizeof(rtc), + }; + struct rtc_time calc_tm; + unsigned long time; + int ret; + + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) { + dev_err(dev, "Failed to read EC RTC\n"); + return ret; + } + + tm->tm_sec = rtc.second; + tm->tm_min = rtc.minute; + tm->tm_hour = rtc.hour; + tm->tm_mday = rtc.day; + /* + * The RTC stores the month value as 1-12 but the kernel expects 0-11, + * so ensure invalid/zero month value from RTC is not converted to -1. + */ + tm->tm_mon = rtc.month ? rtc.month - 1 : 0; + tm->tm_year = rtc.year + (rtc.century * 100) - 1900; + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + + /* Compute day of week */ + rtc_tm_to_time(tm, &time); + rtc_time_to_tm(time, &calc_tm); + tm->tm_wday = calc_tm.tm_wday; + + return 0; +} + +int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm) +{ + struct wilco_ec_device *ec = dev_get_drvdata(dev); + struct ec_rtc_write rtc; + struct wilco_ec_message msg = { + .type = WILCO_EC_MSG_LEGACY, + .flags = WILCO_EC_FLAG_RAW_RESPONSE, + .command = EC_COMMAND_CMOS, + .request_data = &rtc, + .request_size = sizeof(rtc), + }; + int year = tm->tm_year + 1900; + /* Convert from 0=Sunday to 0=Saturday for the EC */ + int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1; + int ret; + + rtc.param = EC_CMOS_TOD_WRITE; + rtc.century = bin2bcd(year / 100); + rtc.year = bin2bcd(year % 100); + rtc.month = bin2bcd(tm->tm_mon + 1); + rtc.day = bin2bcd(tm->tm_mday); + rtc.hour = bin2bcd(tm->tm_hour); + rtc.minute = bin2bcd(tm->tm_min); + rtc.second = bin2bcd(tm->tm_sec); + rtc.weekday = bin2bcd(wday); + + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) { + dev_err(dev, "Failed to write EC RTC\n"); + return ret; + } + + return 0; +} + +int wilco_ec_rtc_sync(struct device *dev) +{ + struct rtc_time tm; + + rtc_time64_to_tm(ktime_get_real_seconds(), &tm); + + return wilco_ec_rtc_write(dev, &tm); +} -- 2.20.0.405.gbc1bbc6f85-goog