brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · f57462c Raw
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, &current_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, &current_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, &current_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