aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Crews <ncrews@chromium.org>2019-02-08 19:37:19 -0500
committerEnric Balletbo i Serra <enric.balletbo@collabora.com>2019-02-21 15:36:53 -0500
commit0d2f2a3da1f2a9ebeb66bb03073dd149fccf1bdd (patch)
tree1fb3c4114555aae7a7831adff13aa3f0ffd3f782
parentb787bb126cbcd73754bcbc055ae9f804ac576e4a (diff)
platform/chrome: wilco_ec: Add RTC driver
This Embedded Controller has an internal RTC that is exposed as a standard RTC class driver with read/write functionality. The driver is added to the drivers/rtc/ so that the maintainer of that directory will be able to comment on this change, as that maintainer is the expert on this system. In addition, the driver code is called indirectly after a corresponding device is registered from core.c, as opposed to core.c registering the driver callbacks directly. To test: > 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 > 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 <dlaurie@google.com> Signed-off-by: Nick Crews <ncrews@chromium.org> Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com> [Fix the sparse warning: symbol 'wilco_ec_rtc_read/write' was not declared] Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
-rw-r--r--drivers/platform/chrome/wilco_ec/core.c18
-rw-r--r--drivers/rtc/Kconfig11
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-wilco-ec.c177
-rw-r--r--include/linux/platform_data/wilco-ec.h2
5 files changed, 209 insertions, 0 deletions
diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c
index af5fd288b63b..05e1e2be1c91 100644
--- a/drivers/platform/chrome/wilco_ec/core.c
+++ b/drivers/platform/chrome/wilco_ec/core.c
@@ -42,6 +42,7 @@ static int wilco_ec_probe(struct platform_device *pdev)
42{ 42{
43 struct device *dev = &pdev->dev; 43 struct device *dev = &pdev->dev;
44 struct wilco_ec_device *ec; 44 struct wilco_ec_device *ec;
45 int ret;
45 46
46 ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL); 47 ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
47 if (!ec) 48 if (!ec)
@@ -78,13 +79,30 @@ static int wilco_ec_probe(struct platform_device *pdev)
78 PLATFORM_DEVID_AUTO, 79 PLATFORM_DEVID_AUTO,
79 NULL, 0); 80 NULL, 0);
80 81
82 /* Register a child device that will be found by the RTC driver. */
83 ec->rtc_pdev = platform_device_register_data(dev, "rtc-wilco-ec",
84 PLATFORM_DEVID_AUTO,
85 NULL, 0);
86 if (IS_ERR(ec->rtc_pdev)) {
87 dev_err(dev, "Failed to create RTC platform device\n");
88 ret = PTR_ERR(ec->rtc_pdev);
89 goto unregister_debugfs;
90 }
91
81 return 0; 92 return 0;
93
94unregister_debugfs:
95 if (ec->debugfs_pdev)
96 platform_device_unregister(ec->debugfs_pdev);
97 cros_ec_lpc_mec_destroy();
98 return ret;
82} 99}
83 100
84static int wilco_ec_remove(struct platform_device *pdev) 101static int wilco_ec_remove(struct platform_device *pdev)
85{ 102{
86 struct wilco_ec_device *ec = platform_get_drvdata(pdev); 103 struct wilco_ec_device *ec = platform_get_drvdata(pdev);
87 104
105 platform_device_unregister(ec->rtc_pdev);
88 if (ec->debugfs_pdev) 106 if (ec->debugfs_pdev)
89 platform_device_unregister(ec->debugfs_pdev); 107 platform_device_unregister(ec->debugfs_pdev);
90 108
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 225b0b8516f3..d5063c791515 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1814,4 +1814,15 @@ config RTC_DRV_GOLDFISH
1814 Goldfish is a code name for the virtual platform developed by Google 1814 Goldfish is a code name for the virtual platform developed by Google
1815 for Android emulation. 1815 for Android emulation.
1816 1816
1817config RTC_DRV_WILCO_EC
1818 tristate "Wilco EC RTC"
1819 depends on WILCO_EC
1820 default m
1821 help
1822 If you say yes here, you get read/write support for the Real Time
1823 Clock on the Wilco Embedded Controller (Wilco is a kind of Chromebook)
1824
1825 This can also be built as a module. If so, the module will
1826 be named "rtc_wilco_ec".
1827
1817endif # RTC_CLASS 1828endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index df022d820bee..6255ea78da25 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -172,6 +172,7 @@ obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
172obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o 172obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
173obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o 173obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
174obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o 174obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o
175obj-$(CONFIG_RTC_DRV_WILCO_EC) += rtc-wilco-ec.o
175obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o 176obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
176obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o 177obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
177obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o 178obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
diff --git a/drivers/rtc/rtc-wilco-ec.c b/drivers/rtc/rtc-wilco-ec.c
new file mode 100644
index 000000000000..e62bda0cb53e
--- /dev/null
+++ b/drivers/rtc/rtc-wilco-ec.c
@@ -0,0 +1,177 @@
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/**
25 * struct ec_rtc_read - Format of RTC returned by EC.
26 * @second: Second value (0..59)
27 * @minute: Minute value (0..59)
28 * @hour: Hour value (0..23)
29 * @day: Day value (1..31)
30 * @month: Month value (1..12)
31 * @year: Year value (full year % 100)
32 * @century: Century value (full year / 100)
33 *
34 * All values are presented in binary (not BCD).
35 */
36struct ec_rtc_read {
37 u8 second;
38 u8 minute;
39 u8 hour;
40 u8 day;
41 u8 month;
42 u8 year;
43 u8 century;
44} __packed;
45
46/**
47 * struct ec_rtc_write - Format of RTC sent to the EC.
48 * @param: EC_CMOS_TOD_WRITE
49 * @century: Century value (full year / 100)
50 * @year: Year value (full year % 100)
51 * @month: Month value (1..12)
52 * @day: Day value (1..31)
53 * @hour: Hour value (0..23)
54 * @minute: Minute value (0..59)
55 * @second: Second value (0..59)
56 * @weekday: Day of the week (0=Saturday)
57 *
58 * All values are presented in BCD.
59 */
60struct ec_rtc_write {
61 u8 param;
62 u8 century;
63 u8 year;
64 u8 month;
65 u8 day;
66 u8 hour;
67 u8 minute;
68 u8 second;
69 u8 weekday;
70} __packed;
71
72static int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm)
73{
74 struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
75 u8 param = EC_CMOS_TOD_READ;
76 struct ec_rtc_read rtc;
77 struct wilco_ec_message msg = {
78 .type = WILCO_EC_MSG_LEGACY,
79 .flags = WILCO_EC_FLAG_RAW_RESPONSE,
80 .command = EC_COMMAND_CMOS,
81 .request_data = &param,
82 .request_size = sizeof(param),
83 .response_data = &rtc,
84 .response_size = sizeof(rtc),
85 };
86 int ret;
87
88 ret = wilco_ec_mailbox(ec, &msg);
89 if (ret < 0)
90 return ret;
91
92 tm->tm_sec = rtc.second;
93 tm->tm_min = rtc.minute;
94 tm->tm_hour = rtc.hour;
95 tm->tm_mday = rtc.day;
96 tm->tm_mon = rtc.month - 1;
97 tm->tm_year = rtc.year + (rtc.century * 100) - 1900;
98 tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
99
100 /* Don't compute day of week, we don't need it. */
101 tm->tm_wday = -1;
102
103 return 0;
104}
105
106static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm)
107{
108 struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
109 struct ec_rtc_write rtc;
110 struct wilco_ec_message msg = {
111 .type = WILCO_EC_MSG_LEGACY,
112 .flags = WILCO_EC_FLAG_RAW_RESPONSE,
113 .command = EC_COMMAND_CMOS,
114 .request_data = &rtc,
115 .request_size = sizeof(rtc),
116 };
117 int year = tm->tm_year + 1900;
118 /*
119 * Convert from 0=Sunday to 0=Saturday for the EC
120 * We DO need to set weekday because the EC controls battery charging
121 * schedules that depend on the day of the week.
122 */
123 int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1;
124 int ret;
125
126 rtc.param = EC_CMOS_TOD_WRITE;
127 rtc.century = bin2bcd(year / 100);
128 rtc.year = bin2bcd(year % 100);
129 rtc.month = bin2bcd(tm->tm_mon + 1);
130 rtc.day = bin2bcd(tm->tm_mday);
131 rtc.hour = bin2bcd(tm->tm_hour);
132 rtc.minute = bin2bcd(tm->tm_min);
133 rtc.second = bin2bcd(tm->tm_sec);
134 rtc.weekday = bin2bcd(wday);
135
136 ret = wilco_ec_mailbox(ec, &msg);
137 if (ret < 0)
138 return ret;
139
140 return 0;
141}
142
143static const struct rtc_class_ops wilco_ec_rtc_ops = {
144 .read_time = wilco_ec_rtc_read,
145 .set_time = wilco_ec_rtc_write,
146};
147
148static int wilco_ec_rtc_probe(struct platform_device *pdev)
149{
150 struct rtc_device *rtc;
151
152 rtc = devm_rtc_allocate_device(&pdev->dev);
153 if (IS_ERR(rtc))
154 return PTR_ERR(rtc);
155
156 rtc->ops = &wilco_ec_rtc_ops;
157 /* EC only supports this century */
158 rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
159 rtc->range_max = RTC_TIMESTAMP_END_2099;
160 rtc->owner = THIS_MODULE;
161
162 return rtc_register_device(rtc);
163}
164
165static struct platform_driver wilco_ec_rtc_driver = {
166 .driver = {
167 .name = "rtc-wilco-ec",
168 },
169 .probe = wilco_ec_rtc_probe,
170};
171
172module_platform_driver(wilco_ec_rtc_driver);
173
174MODULE_ALIAS("platform:rtc-wilco-ec");
175MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
176MODULE_LICENSE("GPL v2");
177MODULE_DESCRIPTION("Wilco EC RTC driver");
diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h
index 5344975afa1a..446473a46b88 100644
--- a/include/linux/platform_data/wilco-ec.h
+++ b/include/linux/platform_data/wilco-ec.h
@@ -35,6 +35,7 @@
35 * is used to hold the request and the response. 35 * is used to hold the request and the response.
36 * @data_size: Size of the data buffer used for EC communication. 36 * @data_size: Size of the data buffer used for EC communication.
37 * @debugfs_pdev: The child platform_device used by the debugfs sub-driver. 37 * @debugfs_pdev: The child platform_device used by the debugfs sub-driver.
38 * @rtc_pdev: The child platform_device used by the RTC sub-driver.
38 */ 39 */
39struct wilco_ec_device { 40struct wilco_ec_device {
40 struct device *dev; 41 struct device *dev;
@@ -45,6 +46,7 @@ struct wilco_ec_device {
45 void *data_buffer; 46 void *data_buffer;
46 size_t data_size; 47 size_t data_size;
47 struct platform_device *debugfs_pdev; 48 struct platform_device *debugfs_pdev;
49 struct platform_device *rtc_pdev;
48}; 50};
49 51
50/** 52/**