18ee4aee5SEnric Balletbo i Serra // SPDX-License-Identifier: GPL-2.0 28ee4aee5SEnric Balletbo i Serra // RTC driver for ChromeOS Embedded Controller. 38ee4aee5SEnric Balletbo i Serra // 48ee4aee5SEnric Balletbo i Serra // Copyright (C) 2017 Google, Inc. 58ee4aee5SEnric Balletbo i Serra // Author: Stephen Barber <smbarber@chromium.org> 66f2a71a3SStephen Barber 76f2a71a3SStephen Barber #include <linux/kernel.h> 8*61c86d14STzung-Bi Shih #include <linux/mod_devicetable.h> 96f2a71a3SStephen Barber #include <linux/module.h> 10840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_commands.h> 11840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_proto.h> 126f2a71a3SStephen Barber #include <linux/platform_device.h> 136f2a71a3SStephen Barber #include <linux/rtc.h> 146f2a71a3SStephen Barber #include <linux/slab.h> 156f2a71a3SStephen Barber 166f2a71a3SStephen Barber #define DRV_NAME "cros-ec-rtc" 176f2a71a3SStephen Barber 18f27efee6SGuenter Roeck #define SECS_PER_DAY (24 * 60 * 60) 19f27efee6SGuenter Roeck 206f2a71a3SStephen Barber /** 216f2a71a3SStephen Barber * struct cros_ec_rtc - Driver data for EC RTC 226f2a71a3SStephen Barber * 236f2a71a3SStephen Barber * @cros_ec: Pointer to EC device 246f2a71a3SStephen Barber * @rtc: Pointer to RTC device 256f2a71a3SStephen Barber * @notifier: Notifier info for responding to EC events 266f2a71a3SStephen Barber * @saved_alarm: Alarm to restore when interrupts are reenabled 276f2a71a3SStephen Barber */ 286f2a71a3SStephen Barber struct cros_ec_rtc { 296f2a71a3SStephen Barber struct cros_ec_device *cros_ec; 306f2a71a3SStephen Barber struct rtc_device *rtc; 316f2a71a3SStephen Barber struct notifier_block notifier; 326f2a71a3SStephen Barber u32 saved_alarm; 336f2a71a3SStephen Barber }; 346f2a71a3SStephen Barber 356f2a71a3SStephen Barber static int cros_ec_rtc_get(struct cros_ec_device *cros_ec, u32 command, 366f2a71a3SStephen Barber u32 *response) 376f2a71a3SStephen Barber { 386f2a71a3SStephen Barber int ret; 396f2a71a3SStephen Barber struct { 406f2a71a3SStephen Barber struct cros_ec_command msg; 416f2a71a3SStephen Barber struct ec_response_rtc data; 426f2a71a3SStephen Barber } __packed msg; 436f2a71a3SStephen Barber 446f2a71a3SStephen Barber memset(&msg, 0, sizeof(msg)); 456f2a71a3SStephen Barber msg.msg.command = command; 466f2a71a3SStephen Barber msg.msg.insize = sizeof(msg.data); 476f2a71a3SStephen Barber 486f2a71a3SStephen Barber ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); 49f27efee6SGuenter Roeck if (ret < 0) 506f2a71a3SStephen Barber return ret; 516f2a71a3SStephen Barber 526f2a71a3SStephen Barber *response = msg.data.time; 536f2a71a3SStephen Barber 546f2a71a3SStephen Barber return 0; 556f2a71a3SStephen Barber } 566f2a71a3SStephen Barber 576f2a71a3SStephen Barber static int cros_ec_rtc_set(struct cros_ec_device *cros_ec, u32 command, 586f2a71a3SStephen Barber u32 param) 596f2a71a3SStephen Barber { 60f27efee6SGuenter Roeck int ret; 616f2a71a3SStephen Barber struct { 626f2a71a3SStephen Barber struct cros_ec_command msg; 636f2a71a3SStephen Barber struct ec_response_rtc data; 646f2a71a3SStephen Barber } __packed msg; 656f2a71a3SStephen Barber 666f2a71a3SStephen Barber memset(&msg, 0, sizeof(msg)); 676f2a71a3SStephen Barber msg.msg.command = command; 686f2a71a3SStephen Barber msg.msg.outsize = sizeof(msg.data); 696f2a71a3SStephen Barber msg.data.time = param; 706f2a71a3SStephen Barber 716f2a71a3SStephen Barber ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); 72f27efee6SGuenter Roeck if (ret < 0) 736f2a71a3SStephen Barber return ret; 746f2a71a3SStephen Barber return 0; 756f2a71a3SStephen Barber } 766f2a71a3SStephen Barber 776f2a71a3SStephen Barber /* Read the current time from the EC. */ 786f2a71a3SStephen Barber static int cros_ec_rtc_read_time(struct device *dev, struct rtc_time *tm) 796f2a71a3SStephen Barber { 806f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); 816f2a71a3SStephen Barber struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; 826f2a71a3SStephen Barber int ret; 836f2a71a3SStephen Barber u32 time; 846f2a71a3SStephen Barber 856f2a71a3SStephen Barber ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, &time); 866f2a71a3SStephen Barber if (ret) { 876f2a71a3SStephen Barber dev_err(dev, "error getting time: %d\n", ret); 886f2a71a3SStephen Barber return ret; 896f2a71a3SStephen Barber } 906f2a71a3SStephen Barber 916f2a71a3SStephen Barber rtc_time64_to_tm(time, tm); 926f2a71a3SStephen Barber 936f2a71a3SStephen Barber return 0; 946f2a71a3SStephen Barber } 956f2a71a3SStephen Barber 966f2a71a3SStephen Barber /* Set the current EC time. */ 976f2a71a3SStephen Barber static int cros_ec_rtc_set_time(struct device *dev, struct rtc_time *tm) 986f2a71a3SStephen Barber { 996f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); 1006f2a71a3SStephen Barber struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; 1016f2a71a3SStephen Barber int ret; 1020e843137SAlexandre Belloni time64_t time = rtc_tm_to_time64(tm); 1036f2a71a3SStephen Barber 1046f2a71a3SStephen Barber ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_VALUE, (u32)time); 1056f2a71a3SStephen Barber if (ret < 0) { 1066f2a71a3SStephen Barber dev_err(dev, "error setting time: %d\n", ret); 1076f2a71a3SStephen Barber return ret; 1086f2a71a3SStephen Barber } 1096f2a71a3SStephen Barber 1106f2a71a3SStephen Barber return 0; 1116f2a71a3SStephen Barber } 1126f2a71a3SStephen Barber 1136f2a71a3SStephen Barber /* Read alarm time from RTC. */ 1146f2a71a3SStephen Barber static int cros_ec_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 1156f2a71a3SStephen Barber { 1166f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); 1176f2a71a3SStephen Barber struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; 1186f2a71a3SStephen Barber int ret; 1196f2a71a3SStephen Barber u32 current_time, alarm_offset; 1206f2a71a3SStephen Barber 1216f2a71a3SStephen Barber /* 1226f2a71a3SStephen Barber * The EC host command for getting the alarm is relative (i.e. 5 1236f2a71a3SStephen Barber * seconds from now) whereas rtc_wkalrm is absolute. Get the current 1246f2a71a3SStephen Barber * RTC time first so we can calculate the relative time. 1256f2a71a3SStephen Barber */ 1266f2a71a3SStephen Barber ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); 1276f2a71a3SStephen Barber if (ret < 0) { 1286f2a71a3SStephen Barber dev_err(dev, "error getting time: %d\n", ret); 1296f2a71a3SStephen Barber return ret; 1306f2a71a3SStephen Barber } 1316f2a71a3SStephen Barber 1326f2a71a3SStephen Barber ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, &alarm_offset); 1336f2a71a3SStephen Barber if (ret < 0) { 1346f2a71a3SStephen Barber dev_err(dev, "error getting alarm: %d\n", ret); 1356f2a71a3SStephen Barber return ret; 1366f2a71a3SStephen Barber } 1376f2a71a3SStephen Barber 1386f2a71a3SStephen Barber rtc_time64_to_tm(current_time + alarm_offset, &alrm->time); 1396f2a71a3SStephen Barber 1406f2a71a3SStephen Barber return 0; 1416f2a71a3SStephen Barber } 1426f2a71a3SStephen Barber 1436f2a71a3SStephen Barber /* Set the EC's RTC alarm. */ 1446f2a71a3SStephen Barber static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 1456f2a71a3SStephen Barber { 1466f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); 1476f2a71a3SStephen Barber struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; 1486f2a71a3SStephen Barber int ret; 1496f2a71a3SStephen Barber time64_t alarm_time; 1506f2a71a3SStephen Barber u32 current_time, alarm_offset; 1516f2a71a3SStephen Barber 1526f2a71a3SStephen Barber /* 1536f2a71a3SStephen Barber * The EC host command for setting the alarm is relative 1546f2a71a3SStephen Barber * (i.e. 5 seconds from now) whereas rtc_wkalrm is absolute. 1556f2a71a3SStephen Barber * Get the current RTC time first so we can calculate the 1566f2a71a3SStephen Barber * relative time. 1576f2a71a3SStephen Barber */ 1586f2a71a3SStephen Barber ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); 1596f2a71a3SStephen Barber if (ret < 0) { 1606f2a71a3SStephen Barber dev_err(dev, "error getting time: %d\n", ret); 1616f2a71a3SStephen Barber return ret; 1626f2a71a3SStephen Barber } 1636f2a71a3SStephen Barber 1646f2a71a3SStephen Barber alarm_time = rtc_tm_to_time64(&alrm->time); 1656f2a71a3SStephen Barber 1666f2a71a3SStephen Barber if (alarm_time < 0 || alarm_time > U32_MAX) 1676f2a71a3SStephen Barber return -EINVAL; 1686f2a71a3SStephen Barber 1696f2a71a3SStephen Barber if (!alrm->enabled) { 1706f2a71a3SStephen Barber /* 1716f2a71a3SStephen Barber * If the alarm is being disabled, send an alarm 1726f2a71a3SStephen Barber * clear command. 1736f2a71a3SStephen Barber */ 1746f2a71a3SStephen Barber alarm_offset = EC_RTC_ALARM_CLEAR; 1756f2a71a3SStephen Barber cros_ec_rtc->saved_alarm = (u32)alarm_time; 1766f2a71a3SStephen Barber } else { 1776f2a71a3SStephen Barber /* Don't set an alarm in the past. */ 17872dd71f0SJeffy Chen if ((u32)alarm_time <= current_time) 17972dd71f0SJeffy Chen return -ETIME; 18072dd71f0SJeffy Chen 1816f2a71a3SStephen Barber alarm_offset = (u32)alarm_time - current_time; 1826f2a71a3SStephen Barber } 1836f2a71a3SStephen Barber 1846f2a71a3SStephen Barber ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, alarm_offset); 1856f2a71a3SStephen Barber if (ret < 0) { 186f27efee6SGuenter Roeck dev_err(dev, "error setting alarm in %u seconds: %d\n", 187f27efee6SGuenter Roeck alarm_offset, ret); 18800c3092dSGuenter Roeck /* 18900c3092dSGuenter Roeck * The EC code returns -EINVAL if the alarm time is too 19000c3092dSGuenter Roeck * far in the future. Convert it to the expected error code. 19100c3092dSGuenter Roeck */ 19200c3092dSGuenter Roeck if (ret == -EINVAL) 19300c3092dSGuenter Roeck ret = -ERANGE; 1946f2a71a3SStephen Barber return ret; 1956f2a71a3SStephen Barber } 1966f2a71a3SStephen Barber 1976f2a71a3SStephen Barber return 0; 1986f2a71a3SStephen Barber } 1996f2a71a3SStephen Barber 2006f2a71a3SStephen Barber static int cros_ec_rtc_alarm_irq_enable(struct device *dev, 2016f2a71a3SStephen Barber unsigned int enabled) 2026f2a71a3SStephen Barber { 2036f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); 2046f2a71a3SStephen Barber struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; 2056f2a71a3SStephen Barber int ret; 2066f2a71a3SStephen Barber u32 current_time, alarm_offset, alarm_value; 2076f2a71a3SStephen Barber 2086f2a71a3SStephen Barber ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); 2096f2a71a3SStephen Barber if (ret < 0) { 2106f2a71a3SStephen Barber dev_err(dev, "error getting time: %d\n", ret); 2116f2a71a3SStephen Barber return ret; 2126f2a71a3SStephen Barber } 2136f2a71a3SStephen Barber 2146f2a71a3SStephen Barber if (enabled) { 2156f2a71a3SStephen Barber /* Restore saved alarm if it's still in the future. */ 2166f2a71a3SStephen Barber if (cros_ec_rtc->saved_alarm < current_time) 2176f2a71a3SStephen Barber alarm_offset = EC_RTC_ALARM_CLEAR; 2186f2a71a3SStephen Barber else 2196f2a71a3SStephen Barber alarm_offset = cros_ec_rtc->saved_alarm - current_time; 2206f2a71a3SStephen Barber 2216f2a71a3SStephen Barber ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, 2226f2a71a3SStephen Barber alarm_offset); 2236f2a71a3SStephen Barber if (ret < 0) { 2246f2a71a3SStephen Barber dev_err(dev, "error restoring alarm: %d\n", ret); 2256f2a71a3SStephen Barber return ret; 2266f2a71a3SStephen Barber } 2276f2a71a3SStephen Barber } else { 2286f2a71a3SStephen Barber /* Disable alarm, saving the old alarm value. */ 2296f2a71a3SStephen Barber ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, 2306f2a71a3SStephen Barber &alarm_offset); 2316f2a71a3SStephen Barber if (ret < 0) { 2326f2a71a3SStephen Barber dev_err(dev, "error saving alarm: %d\n", ret); 2336f2a71a3SStephen Barber return ret; 2346f2a71a3SStephen Barber } 2356f2a71a3SStephen Barber 2366f2a71a3SStephen Barber alarm_value = current_time + alarm_offset; 2376f2a71a3SStephen Barber 2386f2a71a3SStephen Barber /* 2396f2a71a3SStephen Barber * If the current EC alarm is already past, we don't want 2406f2a71a3SStephen Barber * to set an alarm when we go through the alarm irq enable 2416f2a71a3SStephen Barber * path. 2426f2a71a3SStephen Barber */ 2436f2a71a3SStephen Barber if (alarm_value < current_time) 2446f2a71a3SStephen Barber cros_ec_rtc->saved_alarm = EC_RTC_ALARM_CLEAR; 2456f2a71a3SStephen Barber else 2466f2a71a3SStephen Barber cros_ec_rtc->saved_alarm = alarm_value; 2476f2a71a3SStephen Barber 2486f2a71a3SStephen Barber alarm_offset = EC_RTC_ALARM_CLEAR; 2496f2a71a3SStephen Barber ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, 2506f2a71a3SStephen Barber alarm_offset); 2516f2a71a3SStephen Barber if (ret < 0) { 2526f2a71a3SStephen Barber dev_err(dev, "error disabling alarm: %d\n", ret); 2536f2a71a3SStephen Barber return ret; 2546f2a71a3SStephen Barber } 2556f2a71a3SStephen Barber } 2566f2a71a3SStephen Barber 2576f2a71a3SStephen Barber return 0; 2586f2a71a3SStephen Barber } 2596f2a71a3SStephen Barber 2606f2a71a3SStephen Barber static int cros_ec_rtc_event(struct notifier_block *nb, 2616f2a71a3SStephen Barber unsigned long queued_during_suspend, 2626f2a71a3SStephen Barber void *_notify) 2636f2a71a3SStephen Barber { 2646f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc; 2656f2a71a3SStephen Barber struct rtc_device *rtc; 2666f2a71a3SStephen Barber struct cros_ec_device *cros_ec; 2676f2a71a3SStephen Barber u32 host_event; 2686f2a71a3SStephen Barber 2696f2a71a3SStephen Barber cros_ec_rtc = container_of(nb, struct cros_ec_rtc, notifier); 2706f2a71a3SStephen Barber rtc = cros_ec_rtc->rtc; 2716f2a71a3SStephen Barber cros_ec = cros_ec_rtc->cros_ec; 2726f2a71a3SStephen Barber 2736f2a71a3SStephen Barber host_event = cros_ec_get_host_event(cros_ec); 2746f2a71a3SStephen Barber if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) { 2756f2a71a3SStephen Barber rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); 2766f2a71a3SStephen Barber return NOTIFY_OK; 2776f2a71a3SStephen Barber } else { 2786f2a71a3SStephen Barber return NOTIFY_DONE; 2796f2a71a3SStephen Barber } 2806f2a71a3SStephen Barber } 2816f2a71a3SStephen Barber 2826f2a71a3SStephen Barber static const struct rtc_class_ops cros_ec_rtc_ops = { 2836f2a71a3SStephen Barber .read_time = cros_ec_rtc_read_time, 2846f2a71a3SStephen Barber .set_time = cros_ec_rtc_set_time, 2856f2a71a3SStephen Barber .read_alarm = cros_ec_rtc_read_alarm, 2866f2a71a3SStephen Barber .set_alarm = cros_ec_rtc_set_alarm, 2876f2a71a3SStephen Barber .alarm_irq_enable = cros_ec_rtc_alarm_irq_enable, 2886f2a71a3SStephen Barber }; 2896f2a71a3SStephen Barber 2906f2a71a3SStephen Barber #ifdef CONFIG_PM_SLEEP 2916f2a71a3SStephen Barber static int cros_ec_rtc_suspend(struct device *dev) 2926f2a71a3SStephen Barber { 2936f2a71a3SStephen Barber struct platform_device *pdev = to_platform_device(dev); 2946f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev); 2956f2a71a3SStephen Barber 2966f2a71a3SStephen Barber if (device_may_wakeup(dev)) 297d6752e18SStephen Boyd return enable_irq_wake(cros_ec_rtc->cros_ec->irq); 2986f2a71a3SStephen Barber 2996f2a71a3SStephen Barber return 0; 3006f2a71a3SStephen Barber } 3016f2a71a3SStephen Barber 3026f2a71a3SStephen Barber static int cros_ec_rtc_resume(struct device *dev) 3036f2a71a3SStephen Barber { 3046f2a71a3SStephen Barber struct platform_device *pdev = to_platform_device(dev); 3056f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev); 3066f2a71a3SStephen Barber 3076f2a71a3SStephen Barber if (device_may_wakeup(dev)) 308d6752e18SStephen Boyd return disable_irq_wake(cros_ec_rtc->cros_ec->irq); 3096f2a71a3SStephen Barber 3106f2a71a3SStephen Barber return 0; 3116f2a71a3SStephen Barber } 3126f2a71a3SStephen Barber #endif 3136f2a71a3SStephen Barber 3146f2a71a3SStephen Barber static SIMPLE_DEV_PM_OPS(cros_ec_rtc_pm_ops, cros_ec_rtc_suspend, 3156f2a71a3SStephen Barber cros_ec_rtc_resume); 3166f2a71a3SStephen Barber 3176f2a71a3SStephen Barber static int cros_ec_rtc_probe(struct platform_device *pdev) 3186f2a71a3SStephen Barber { 3196f2a71a3SStephen Barber struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); 3206f2a71a3SStephen Barber struct cros_ec_device *cros_ec = ec_dev->ec_dev; 3216f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc; 3226f2a71a3SStephen Barber struct rtc_time tm; 3236f2a71a3SStephen Barber int ret; 3246f2a71a3SStephen Barber 3256f2a71a3SStephen Barber cros_ec_rtc = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_rtc), 3266f2a71a3SStephen Barber GFP_KERNEL); 3276f2a71a3SStephen Barber if (!cros_ec_rtc) 3286f2a71a3SStephen Barber return -ENOMEM; 3296f2a71a3SStephen Barber 3306f2a71a3SStephen Barber platform_set_drvdata(pdev, cros_ec_rtc); 3316f2a71a3SStephen Barber cros_ec_rtc->cros_ec = cros_ec; 3326f2a71a3SStephen Barber 3336f2a71a3SStephen Barber /* Get initial time */ 3346f2a71a3SStephen Barber ret = cros_ec_rtc_read_time(&pdev->dev, &tm); 3356f2a71a3SStephen Barber if (ret) { 3366f2a71a3SStephen Barber dev_err(&pdev->dev, "failed to read RTC time\n"); 3376f2a71a3SStephen Barber return ret; 3386f2a71a3SStephen Barber } 3396f2a71a3SStephen Barber 3406f2a71a3SStephen Barber ret = device_init_wakeup(&pdev->dev, 1); 3416f2a71a3SStephen Barber if (ret) { 3426f2a71a3SStephen Barber dev_err(&pdev->dev, "failed to initialize wakeup\n"); 3436f2a71a3SStephen Barber return ret; 3446f2a71a3SStephen Barber } 3456f2a71a3SStephen Barber 3460e843137SAlexandre Belloni cros_ec_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); 3474fc0d13fSAlexandre Belloni if (IS_ERR(cros_ec_rtc->rtc)) 3484fc0d13fSAlexandre Belloni return PTR_ERR(cros_ec_rtc->rtc); 3496f2a71a3SStephen Barber 3500e843137SAlexandre Belloni cros_ec_rtc->rtc->ops = &cros_ec_rtc_ops; 3510e843137SAlexandre Belloni cros_ec_rtc->rtc->range_max = U32_MAX; 3520e843137SAlexandre Belloni 35300c3092dSGuenter Roeck /* 35400c3092dSGuenter Roeck * The RTC on some older Chromebooks can only handle alarms less than 35500c3092dSGuenter Roeck * 24 hours in the future. The only way to find out is to try to set an 35600c3092dSGuenter Roeck * alarm further in the future. If that fails, assume that the RTC 35700c3092dSGuenter Roeck * connected to the EC can only handle less than 24 hours of alarm 35800c3092dSGuenter Roeck * window. 35900c3092dSGuenter Roeck */ 36000c3092dSGuenter Roeck ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, SECS_PER_DAY * 2); 36100c3092dSGuenter Roeck if (ret == -EINVAL) 36200c3092dSGuenter Roeck cros_ec_rtc->rtc->alarm_offset_max = SECS_PER_DAY - 1; 36300c3092dSGuenter Roeck 36400c3092dSGuenter Roeck (void)cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, 36500c3092dSGuenter Roeck EC_RTC_ALARM_CLEAR); 36600c3092dSGuenter Roeck 367fdcfd854SBartosz Golaszewski ret = devm_rtc_register_device(cros_ec_rtc->rtc); 3680e843137SAlexandre Belloni if (ret) 3690e843137SAlexandre Belloni return ret; 3700e843137SAlexandre Belloni 3716f2a71a3SStephen Barber /* Get RTC events from the EC. */ 3726f2a71a3SStephen Barber cros_ec_rtc->notifier.notifier_call = cros_ec_rtc_event; 3736f2a71a3SStephen Barber ret = blocking_notifier_chain_register(&cros_ec->event_notifier, 3746f2a71a3SStephen Barber &cros_ec_rtc->notifier); 3756f2a71a3SStephen Barber if (ret) { 3766f2a71a3SStephen Barber dev_err(&pdev->dev, "failed to register notifier\n"); 3776f2a71a3SStephen Barber return ret; 3786f2a71a3SStephen Barber } 3796f2a71a3SStephen Barber 3806f2a71a3SStephen Barber return 0; 3816f2a71a3SStephen Barber } 3826f2a71a3SStephen Barber 3830d8742e6SUwe Kleine-König static void cros_ec_rtc_remove(struct platform_device *pdev) 3846f2a71a3SStephen Barber { 3856f2a71a3SStephen Barber struct cros_ec_rtc *cros_ec_rtc = platform_get_drvdata(pdev); 3866f2a71a3SStephen Barber struct device *dev = &pdev->dev; 3876f2a71a3SStephen Barber int ret; 3886f2a71a3SStephen Barber 3896f2a71a3SStephen Barber ret = blocking_notifier_chain_unregister( 3906f2a71a3SStephen Barber &cros_ec_rtc->cros_ec->event_notifier, 3916f2a71a3SStephen Barber &cros_ec_rtc->notifier); 3925c9f4144SUwe Kleine-König if (ret) 3936f2a71a3SStephen Barber dev_err(dev, "failed to unregister notifier\n"); 3946f2a71a3SStephen Barber } 3956f2a71a3SStephen Barber 396*61c86d14STzung-Bi Shih static const struct platform_device_id cros_ec_rtc_id[] = { 397*61c86d14STzung-Bi Shih { DRV_NAME, 0 }, 398*61c86d14STzung-Bi Shih {} 399*61c86d14STzung-Bi Shih }; 400*61c86d14STzung-Bi Shih MODULE_DEVICE_TABLE(platform, cros_ec_rtc_id); 401*61c86d14STzung-Bi Shih 4026f2a71a3SStephen Barber static struct platform_driver cros_ec_rtc_driver = { 4036f2a71a3SStephen Barber .probe = cros_ec_rtc_probe, 4040d8742e6SUwe Kleine-König .remove_new = cros_ec_rtc_remove, 4056f2a71a3SStephen Barber .driver = { 4066f2a71a3SStephen Barber .name = DRV_NAME, 4076f2a71a3SStephen Barber .pm = &cros_ec_rtc_pm_ops, 4086f2a71a3SStephen Barber }, 409*61c86d14STzung-Bi Shih .id_table = cros_ec_rtc_id, 4106f2a71a3SStephen Barber }; 4116f2a71a3SStephen Barber 4126f2a71a3SStephen Barber module_platform_driver(cros_ec_rtc_driver); 4136f2a71a3SStephen Barber 4146f2a71a3SStephen Barber MODULE_DESCRIPTION("RTC driver for Chrome OS ECs"); 4156f2a71a3SStephen Barber MODULE_AUTHOR("Stephen Barber <smbarber@chromium.org>"); 416e6988d23SEnric Balletbo i Serra MODULE_LICENSE("GPL v2"); 417