diff options
Diffstat (limited to 'drivers/rtc/rtc-wilco-ec.c')
-rw-r--r-- | drivers/rtc/rtc-wilco-ec.c | 177 |
1 files changed, 177 insertions, 0 deletions
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 | */ | ||
36 | struct 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 | */ | ||
60 | struct 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 | |||
72 | static 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 = ¶m, | ||
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 | |||
106 | static 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 | |||
143 | static 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 | |||
148 | static 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 | |||
165 | static struct platform_driver wilco_ec_rtc_driver = { | ||
166 | .driver = { | ||
167 | .name = "rtc-wilco-ec", | ||
168 | }, | ||
169 | .probe = wilco_ec_rtc_probe, | ||
170 | }; | ||
171 | |||
172 | module_platform_driver(wilco_ec_rtc_driver); | ||
173 | |||
174 | MODULE_ALIAS("platform:rtc-wilco-ec"); | ||
175 | MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>"); | ||
176 | MODULE_LICENSE("GPL v2"); | ||
177 | MODULE_DESCRIPTION("Wilco EC RTC driver"); | ||