417 lines · c
1// SPDX-License-Identifier: GPL-2.02// RTC driver for ChromeOS Embedded Controller.3//4// Copyright (C) 2017 Google, Inc.5// Author: Stephen Barber <smbarber@chromium.org>6 7#include <linux/kernel.h>8#include <linux/mod_devicetable.h>9#include <linux/module.h>10#include <linux/platform_data/cros_ec_commands.h>11#include <linux/platform_data/cros_ec_proto.h>12#include <linux/platform_device.h>13#include <linux/rtc.h>14#include <linux/slab.h>15 16#define DRV_NAME "cros-ec-rtc"17 18#define SECS_PER_DAY (24 * 60 * 60)19 20/**21 * struct cros_ec_rtc - Driver data for EC RTC22 *23 * @cros_ec: Pointer to EC device24 * @rtc: Pointer to RTC device25 * @notifier: Notifier info for responding to EC events26 * @saved_alarm: Alarm to restore when interrupts are reenabled27 */28struct cros_ec_rtc {29 struct cros_ec_device *cros_ec;30 struct rtc_device *rtc;31 struct notifier_block notifier;32 u32 saved_alarm;33};34 35static int cros_ec_rtc_get(struct cros_ec_device *cros_ec, u32 command,36 u32 *response)37{38 int ret;39 struct {40 struct cros_ec_command msg;41 struct ec_response_rtc data;42 } __packed msg;43 44 memset(&msg, 0, sizeof(msg));45 msg.msg.command = command;46 msg.msg.insize = sizeof(msg.data);47 48 ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);49 if (ret < 0)50 return ret;51 52 *response = msg.data.time;53 54 return 0;55}56 57static int cros_ec_rtc_set(struct cros_ec_device *cros_ec, u32 command,58 u32 param)59{60 int ret;61 struct {62 struct cros_ec_command msg;63 struct ec_response_rtc data;64 } __packed msg;65 66 memset(&msg, 0, sizeof(msg));67 msg.msg.command = command;68 msg.msg.outsize = sizeof(msg.data);69 msg.data.time = param;70 71 ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);72 if (ret < 0)73 return ret;74 return 0;75}76 77/* Read the current time from the EC. */78static int cros_ec_rtc_read_time(struct device *dev, struct rtc_time *tm)79{80 struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);81 struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;82 int ret;83 u32 time;84 85 ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, &time);86 if (ret) {87 dev_err(dev, "error getting time: %d\n", ret);88 return ret;89 }90 91 rtc_time64_to_tm(time, tm);92 93 return 0;94}95 96/* Set the current EC time. */97static int cros_ec_rtc_set_time(struct device *dev, struct rtc_time *tm)98{99 struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);100 struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;101 int ret;102 time64_t time = rtc_tm_to_time64(tm);103 104 ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_VALUE, (u32)time);105 if (ret < 0) {106 dev_err(dev, "error setting time: %d\n", ret);107 return ret;108 }109 110 return 0;111}112 113/* Read alarm time from RTC. */114static int cros_ec_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)115{116 struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);117 struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;118 int ret;119 u32 current_time, alarm_offset;120 121 /*122 * The EC host command for getting the alarm is relative (i.e. 5123 * seconds from now) whereas rtc_wkalrm is absolute. Get the current124 * RTC time first so we can calculate the relative time.125 */126 ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time);127 if (ret < 0) {128 dev_err(dev, "error getting time: %d\n", ret);129 return ret;130 }131 132 ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, &alarm_offset);133 if (ret < 0) {134 dev_err(dev, "error getting alarm: %d\n", ret);135 return ret;136 }137 138 rtc_time64_to_tm(current_time + alarm_offset, &alrm->time);139 140 return 0;141}142 143/* Set the EC's RTC alarm. */144static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)145{146 struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);147 struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;148 int ret;149 time64_t alarm_time;150 u32 current_time, alarm_offset;151 152 /*153 * The EC host command for setting the alarm is relative154 * (i.e. 5 seconds from now) whereas rtc_wkalrm is absolute.155 * Get the current RTC time first so we can calculate the156 * relative time.157 */158 ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time);159 if (ret < 0) {160 dev_err(dev, "error getting time: %d\n", ret);161 return ret;162 }163 164 alarm_time = rtc_tm_to_time64(&alrm->time);165 166 if (alarm_time < 0 || alarm_time > U32_MAX)167 return -EINVAL;168 169 if (!alrm->enabled) {170 /*171 * If the alarm is being disabled, send an alarm172 * clear command.173 */174 alarm_offset = EC_RTC_ALARM_CLEAR;175 cros_ec_rtc->saved_alarm = (u32)alarm_time;176 } else {177 /* Don't set an alarm in the past. */178 if ((u32)alarm_time <= current_time)179 return -ETIME;180 181 alarm_offset = (u32)alarm_time - current_time;182 }183 184 ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, alarm_offset);185 if (ret < 0) {186 dev_err(dev, "error setting alarm in %u seconds: %d\n",187 alarm_offset, ret);188 /*189 * The EC code returns -EINVAL if the alarm time is too190 * far in the future. Convert it to the expected error code.191 */192 if (ret == -EINVAL)193 ret = -ERANGE;194 return ret;195 }196 197 return 0;198}199 200static int cros_ec_rtc_alarm_irq_enable(struct device *dev,201 unsigned int enabled)202{203 struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);204 struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;205 int ret;206 u32 current_time, alarm_offset, alarm_value;207 208 ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time);209 if (ret < 0) {210 dev_err(dev, "error getting time: %d\n", ret);211 return ret;212 }213 214 if (enabled) {215 /* Restore saved alarm if it's still in the future. */216 if (cros_ec_rtc->saved_alarm < current_time)217 alarm_offset = EC_RTC_ALARM_CLEAR;218 else219 alarm_offset = cros_ec_rtc->saved_alarm - current_time;220 221 ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM,222 alarm_offset);223 if (ret < 0) {224 dev_err(dev, "error restoring alarm: %d\n", ret);225 return ret;226 }227 } else {228 /* Disable alarm, saving the old alarm value. */229 ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM,230 &alarm_offset);231 if (ret < 0) {232 dev_err(dev, "error saving alarm: %d\n", ret);233 return ret;234 }235 236 alarm_value = current_time + alarm_offset;237 238 /*239 * If the current EC alarm is already past, we don't want240 * to set an alarm when we go through the alarm irq enable241 * path.242 */243 if (alarm_value < current_time)244 cros_ec_rtc->saved_alarm = EC_RTC_ALARM_CLEAR;245 else246 cros_ec_rtc->saved_alarm = alarm_value;247 248 alarm_offset = EC_RTC_ALARM_CLEAR;249 ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM,250 alarm_offset);251 if (ret < 0) {252 dev_err(dev, "error disabling alarm: %d\n", ret);253 return ret;254 }255 }256 257 return 0;258}259 260static int cros_ec_rtc_event(struct notifier_block *nb,261 unsigned long queued_during_suspend,262 void *_notify)263{264 struct cros_ec_rtc *cros_ec_rtc;265 struct rtc_device *rtc;266 struct cros_ec_device *cros_ec;267 u32 host_event;268 269 cros_ec_rtc = container_of(nb, struct cros_ec_rtc, notifier);270 rtc = cros_ec_rtc->rtc;271 cros_ec = cros_ec_rtc->cros_ec;272 273 host_event = cros_ec_get_host_event(cros_ec);274 if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) {275 rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);276 return NOTIFY_OK;277 } else {278 return NOTIFY_DONE;279 }280}281 282static const struct rtc_class_ops cros_ec_rtc_ops = {283 .read_time = cros_ec_rtc_read_time,284 .set_time = cros_ec_rtc_set_time,285 .read_alarm = cros_ec_rtc_read_alarm,286 .set_alarm = cros_ec_rtc_set_alarm,287 .alarm_irq_enable = cros_ec_rtc_alarm_irq_enable,288};289 290#ifdef CONFIG_PM_SLEEP291static int cros_ec_rtc_suspend(struct device *dev)292{293 struct platform_device *pdev = to_platform_device(dev);294 struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev);295 296 if (device_may_wakeup(dev))297 return enable_irq_wake(cros_ec_rtc->cros_ec->irq);298 299 return 0;300}301 302static int cros_ec_rtc_resume(struct device *dev)303{304 struct platform_device *pdev = to_platform_device(dev);305 struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev);306 307 if (device_may_wakeup(dev))308 return disable_irq_wake(cros_ec_rtc->cros_ec->irq);309 310 return 0;311}312#endif313 314static SIMPLE_DEV_PM_OPS(cros_ec_rtc_pm_ops, cros_ec_rtc_suspend,315 cros_ec_rtc_resume);316 317static int cros_ec_rtc_probe(struct platform_device *pdev)318{319 struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);320 struct cros_ec_device *cros_ec = ec_dev->ec_dev;321 struct cros_ec_rtc *cros_ec_rtc;322 struct rtc_time tm;323 int ret;324 325 cros_ec_rtc = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_rtc),326 GFP_KERNEL);327 if (!cros_ec_rtc)328 return -ENOMEM;329 330 platform_set_drvdata(pdev, cros_ec_rtc);331 cros_ec_rtc->cros_ec = cros_ec;332 333 /* Get initial time */334 ret = cros_ec_rtc_read_time(&pdev->dev, &tm);335 if (ret) {336 dev_err(&pdev->dev, "failed to read RTC time\n");337 return ret;338 }339 340 ret = device_init_wakeup(&pdev->dev, 1);341 if (ret) {342 dev_err(&pdev->dev, "failed to initialize wakeup\n");343 return ret;344 }345 346 cros_ec_rtc->rtc = devm_rtc_allocate_device(&pdev->dev);347 if (IS_ERR(cros_ec_rtc->rtc))348 return PTR_ERR(cros_ec_rtc->rtc);349 350 cros_ec_rtc->rtc->ops = &cros_ec_rtc_ops;351 cros_ec_rtc->rtc->range_max = U32_MAX;352 353 /*354 * The RTC on some older Chromebooks can only handle alarms less than355 * 24 hours in the future. The only way to find out is to try to set an356 * alarm further in the future. If that fails, assume that the RTC357 * connected to the EC can only handle less than 24 hours of alarm358 * window.359 */360 ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, SECS_PER_DAY * 2);361 if (ret == -EINVAL)362 cros_ec_rtc->rtc->alarm_offset_max = SECS_PER_DAY - 1;363 364 (void)cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM,365 EC_RTC_ALARM_CLEAR);366 367 ret = devm_rtc_register_device(cros_ec_rtc->rtc);368 if (ret)369 return ret;370 371 /* Get RTC events from the EC. */372 cros_ec_rtc->notifier.notifier_call = cros_ec_rtc_event;373 ret = blocking_notifier_chain_register(&cros_ec->event_notifier,374 &cros_ec_rtc->notifier);375 if (ret) {376 dev_err(&pdev->dev, "failed to register notifier\n");377 return ret;378 }379 380 return 0;381}382 383static void cros_ec_rtc_remove(struct platform_device *pdev)384{385 struct cros_ec_rtc *cros_ec_rtc = platform_get_drvdata(pdev);386 struct device *dev = &pdev->dev;387 int ret;388 389 ret = blocking_notifier_chain_unregister(390 &cros_ec_rtc->cros_ec->event_notifier,391 &cros_ec_rtc->notifier);392 if (ret)393 dev_err(dev, "failed to unregister notifier\n");394}395 396static const struct platform_device_id cros_ec_rtc_id[] = {397 { DRV_NAME, 0 },398 {}399};400MODULE_DEVICE_TABLE(platform, cros_ec_rtc_id);401 402static struct platform_driver cros_ec_rtc_driver = {403 .probe = cros_ec_rtc_probe,404 .remove_new = cros_ec_rtc_remove,405 .driver = {406 .name = DRV_NAME,407 .pm = &cros_ec_rtc_pm_ops,408 },409 .id_table = cros_ec_rtc_id,410};411 412module_platform_driver(cros_ec_rtc_driver);413 414MODULE_DESCRIPTION("RTC driver for Chrome OS ECs");415MODULE_AUTHOR("Stephen Barber <smbarber@chromium.org>");416MODULE_LICENSE("GPL v2");417