brintos

brintos / linux-shallow public Read only

0
0
Text · 15.8 KiB · b831cb8 Raw
692 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ACPI Time and Alarm (TAD) Device Driver4 *5 * Copyright (C) 2018 Intel Corporation6 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>7 *8 * This driver is based on Section 9.18 of the ACPI 6.2 specification revision.9 *10 * It only supports the system wakeup capabilities of the TAD.11 *12 * Provided are sysfs attributes, available under the TAD platform device,13 * allowing user space to manage the AC and DC wakeup timers of the TAD:14 * set and read their values, set and check their expire timer wake policies,15 * check and clear their status and check the capabilities of the TAD reported16 * by AML.  The DC timer attributes are only present if the TAD supports a17 * separate DC alarm timer.18 *19 * The wakeup events handling and power management of the TAD is expected to20 * be taken care of by the ACPI PM domain attached to its platform device.21 */22 23#include <linux/acpi.h>24#include <linux/kernel.h>25#include <linux/module.h>26#include <linux/platform_device.h>27#include <linux/pm_runtime.h>28#include <linux/suspend.h>29 30MODULE_DESCRIPTION("ACPI Time and Alarm (TAD) Device Driver");31MODULE_LICENSE("GPL v2");32MODULE_AUTHOR("Rafael J. Wysocki");33 34/* ACPI TAD capability flags (ACPI 6.2, Section 9.18.2) */35#define ACPI_TAD_AC_WAKE	BIT(0)36#define ACPI_TAD_DC_WAKE	BIT(1)37#define ACPI_TAD_RT		BIT(2)38#define ACPI_TAD_RT_IN_MS	BIT(3)39#define ACPI_TAD_S4_S5__GWS	BIT(4)40#define ACPI_TAD_AC_S4_WAKE	BIT(5)41#define ACPI_TAD_AC_S5_WAKE	BIT(6)42#define ACPI_TAD_DC_S4_WAKE	BIT(7)43#define ACPI_TAD_DC_S5_WAKE	BIT(8)44 45/* ACPI TAD alarm timer selection */46#define ACPI_TAD_AC_TIMER	(u32)047#define ACPI_TAD_DC_TIMER	(u32)148 49/* Special value for disabled timer or expired timer wake policy. */50#define ACPI_TAD_WAKE_DISABLED	(~(u32)0)51 52struct acpi_tad_driver_data {53	u32 capabilities;54};55 56struct acpi_tad_rt {57	u16 year;  /* 1900 - 9999 */58	u8 month;  /* 1 - 12 */59	u8 day;    /* 1 - 31 */60	u8 hour;   /* 0 - 23 */61	u8 minute; /* 0 - 59 */62	u8 second; /* 0 - 59 */63	u8 valid;  /* 0 (failed) or 1 (success) for reads, 0 for writes */64	u16 msec;  /* 1 - 1000 */65	s16 tz;    /* -1440 to 1440 or 2047 (unspecified) */66	u8 daylight;67	u8 padding[3]; /* must be 0 */68} __packed;69 70static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt)71{72	acpi_handle handle = ACPI_HANDLE(dev);73	union acpi_object args[] = {74		{ .type = ACPI_TYPE_BUFFER, },75	};76	struct acpi_object_list arg_list = {77		.pointer = args,78		.count = ARRAY_SIZE(args),79	};80	unsigned long long retval;81	acpi_status status;82 83	if (rt->year < 1900 || rt->year > 9999 ||84	    rt->month < 1 || rt->month > 12 ||85	    rt->hour > 23 || rt->minute > 59 || rt->second > 59 ||86	    rt->tz < -1440 || (rt->tz > 1440 && rt->tz != 2047) ||87	    rt->daylight > 3)88		return -ERANGE;89 90	args[0].buffer.pointer = (u8 *)rt;91	args[0].buffer.length = sizeof(*rt);92 93	pm_runtime_get_sync(dev);94 95	status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval);96 97	pm_runtime_put_sync(dev);98 99	if (ACPI_FAILURE(status) || retval)100		return -EIO;101 102	return 0;103}104 105static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt)106{107	acpi_handle handle = ACPI_HANDLE(dev);108	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER };109	union acpi_object *out_obj;110	struct acpi_tad_rt *data;111	acpi_status status;112	int ret = -EIO;113 114	pm_runtime_get_sync(dev);115 116	status = acpi_evaluate_object(handle, "_GRT", NULL, &output);117 118	pm_runtime_put_sync(dev);119 120	if (ACPI_FAILURE(status))121		goto out_free;122 123	out_obj = output.pointer;124	if (out_obj->type != ACPI_TYPE_BUFFER)125		goto out_free;126 127	if (out_obj->buffer.length != sizeof(*rt))128		goto out_free;129 130	data = (struct acpi_tad_rt *)(out_obj->buffer.pointer);131	if (!data->valid)132		goto out_free;133 134	memcpy(rt, data, sizeof(*rt));135	ret = 0;136 137out_free:138	ACPI_FREE(output.pointer);139	return ret;140}141 142static char *acpi_tad_rt_next_field(char *s, int *val)143{144	char *p;145 146	p = strchr(s, ':');147	if (!p)148		return NULL;149 150	*p = '\0';151	if (kstrtoint(s, 10, val))152		return NULL;153 154	return p + 1;155}156 157static ssize_t time_store(struct device *dev, struct device_attribute *attr,158			  const char *buf, size_t count)159{160	struct acpi_tad_rt rt;161	char *str, *s;162	int val, ret = -ENODATA;163 164	str = kmemdup_nul(buf, count, GFP_KERNEL);165	if (!str)166		return -ENOMEM;167 168	s = acpi_tad_rt_next_field(str, &val);169	if (!s)170		goto out_free;171 172	rt.year = val;173 174	s = acpi_tad_rt_next_field(s, &val);175	if (!s)176		goto out_free;177 178	rt.month = val;179 180	s = acpi_tad_rt_next_field(s, &val);181	if (!s)182		goto out_free;183 184	rt.day = val;185 186	s = acpi_tad_rt_next_field(s, &val);187	if (!s)188		goto out_free;189 190	rt.hour = val;191 192	s = acpi_tad_rt_next_field(s, &val);193	if (!s)194		goto out_free;195 196	rt.minute = val;197 198	s = acpi_tad_rt_next_field(s, &val);199	if (!s)200		goto out_free;201 202	rt.second = val;203 204	s = acpi_tad_rt_next_field(s, &val);205	if (!s)206		goto out_free;207 208	rt.tz = val;209 210	if (kstrtoint(s, 10, &val))211		goto out_free;212 213	rt.daylight = val;214 215	rt.valid = 0;216	rt.msec = 0;217	memset(rt.padding, 0, 3);218 219	ret = acpi_tad_set_real_time(dev, &rt);220 221out_free:222	kfree(str);223	return ret ? ret : count;224}225 226static ssize_t time_show(struct device *dev, struct device_attribute *attr,227			 char *buf)228{229	struct acpi_tad_rt rt;230	int ret;231 232	ret = acpi_tad_get_real_time(dev, &rt);233	if (ret)234		return ret;235 236	return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n",237		       rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second,238		       rt.tz, rt.daylight);239}240 241static DEVICE_ATTR_RW(time);242 243static struct attribute *acpi_tad_time_attrs[] = {244	&dev_attr_time.attr,245	NULL,246};247static const struct attribute_group acpi_tad_time_attr_group = {248	.attrs	= acpi_tad_time_attrs,249};250 251static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id,252			     u32 value)253{254	acpi_handle handle = ACPI_HANDLE(dev);255	union acpi_object args[] = {256		{ .type = ACPI_TYPE_INTEGER, },257		{ .type = ACPI_TYPE_INTEGER, },258	};259	struct acpi_object_list arg_list = {260		.pointer = args,261		.count = ARRAY_SIZE(args),262	};263	unsigned long long retval;264	acpi_status status;265 266	args[0].integer.value = timer_id;267	args[1].integer.value = value;268 269	pm_runtime_get_sync(dev);270 271	status = acpi_evaluate_integer(handle, method, &arg_list, &retval);272 273	pm_runtime_put_sync(dev);274 275	if (ACPI_FAILURE(status) || retval)276		return -EIO;277 278	return 0;279}280 281static int acpi_tad_wake_write(struct device *dev, const char *buf, char *method,282			       u32 timer_id, const char *specval)283{284	u32 value;285 286	if (sysfs_streq(buf, specval)) {287		value = ACPI_TAD_WAKE_DISABLED;288	} else {289		int ret = kstrtou32(buf, 0, &value);290 291		if (ret)292			return ret;293 294		if (value == ACPI_TAD_WAKE_DISABLED)295			return -EINVAL;296	}297 298	return acpi_tad_wake_set(dev, method, timer_id, value);299}300 301static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method,302				  u32 timer_id, const char *specval)303{304	acpi_handle handle = ACPI_HANDLE(dev);305	union acpi_object args[] = {306		{ .type = ACPI_TYPE_INTEGER, },307	};308	struct acpi_object_list arg_list = {309		.pointer = args,310		.count = ARRAY_SIZE(args),311	};312	unsigned long long retval;313	acpi_status status;314 315	args[0].integer.value = timer_id;316 317	pm_runtime_get_sync(dev);318 319	status = acpi_evaluate_integer(handle, method, &arg_list, &retval);320 321	pm_runtime_put_sync(dev);322 323	if (ACPI_FAILURE(status))324		return -EIO;325 326	if ((u32)retval == ACPI_TAD_WAKE_DISABLED)327		return sprintf(buf, "%s\n", specval);328 329	return sprintf(buf, "%u\n", (u32)retval);330}331 332static const char *alarm_specval = "disabled";333 334static int acpi_tad_alarm_write(struct device *dev, const char *buf,335				u32 timer_id)336{337	return acpi_tad_wake_write(dev, buf, "_STV", timer_id, alarm_specval);338}339 340static ssize_t acpi_tad_alarm_read(struct device *dev, char *buf, u32 timer_id)341{342	return acpi_tad_wake_read(dev, buf, "_TIV", timer_id, alarm_specval);343}344 345static const char *policy_specval = "never";346 347static int acpi_tad_policy_write(struct device *dev, const char *buf,348				 u32 timer_id)349{350	return acpi_tad_wake_write(dev, buf, "_STP", timer_id, policy_specval);351}352 353static ssize_t acpi_tad_policy_read(struct device *dev, char *buf, u32 timer_id)354{355	return acpi_tad_wake_read(dev, buf, "_TIP", timer_id, policy_specval);356}357 358static int acpi_tad_clear_status(struct device *dev, u32 timer_id)359{360	acpi_handle handle = ACPI_HANDLE(dev);361	union acpi_object args[] = {362		{ .type = ACPI_TYPE_INTEGER, },363	};364	struct acpi_object_list arg_list = {365		.pointer = args,366		.count = ARRAY_SIZE(args),367	};368	unsigned long long retval;369	acpi_status status;370 371	args[0].integer.value = timer_id;372 373	pm_runtime_get_sync(dev);374 375	status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval);376 377	pm_runtime_put_sync(dev);378 379	if (ACPI_FAILURE(status) || retval)380		return -EIO;381 382	return 0;383}384 385static int acpi_tad_status_write(struct device *dev, const char *buf, u32 timer_id)386{387	int ret, value;388 389	ret = kstrtoint(buf, 0, &value);390	if (ret)391		return ret;392 393	if (value)394		return -EINVAL;395 396	return acpi_tad_clear_status(dev, timer_id);397}398 399static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id)400{401	acpi_handle handle = ACPI_HANDLE(dev);402	union acpi_object args[] = {403		{ .type = ACPI_TYPE_INTEGER, },404	};405	struct acpi_object_list arg_list = {406		.pointer = args,407		.count = ARRAY_SIZE(args),408	};409	unsigned long long retval;410	acpi_status status;411 412	args[0].integer.value = timer_id;413 414	pm_runtime_get_sync(dev);415 416	status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval);417 418	pm_runtime_put_sync(dev);419 420	if (ACPI_FAILURE(status))421		return -EIO;422 423	return sprintf(buf, "0x%02X\n", (u32)retval);424}425 426static ssize_t caps_show(struct device *dev, struct device_attribute *attr,427			 char *buf)428{429	struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);430 431	return sprintf(buf, "0x%02X\n", dd->capabilities);432}433 434static DEVICE_ATTR_RO(caps);435 436static ssize_t ac_alarm_store(struct device *dev, struct device_attribute *attr,437			      const char *buf, size_t count)438{439	int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_AC_TIMER);440 441	return ret ? ret : count;442}443 444static ssize_t ac_alarm_show(struct device *dev, struct device_attribute *attr,445			     char *buf)446{447	return acpi_tad_alarm_read(dev, buf, ACPI_TAD_AC_TIMER);448}449 450static DEVICE_ATTR_RW(ac_alarm);451 452static ssize_t ac_policy_store(struct device *dev, struct device_attribute *attr,453			       const char *buf, size_t count)454{455	int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_AC_TIMER);456 457	return ret ? ret : count;458}459 460static ssize_t ac_policy_show(struct device *dev, struct device_attribute *attr,461			      char *buf)462{463	return acpi_tad_policy_read(dev, buf, ACPI_TAD_AC_TIMER);464}465 466static DEVICE_ATTR_RW(ac_policy);467 468static ssize_t ac_status_store(struct device *dev, struct device_attribute *attr,469			       const char *buf, size_t count)470{471	int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_AC_TIMER);472 473	return ret ? ret : count;474}475 476static ssize_t ac_status_show(struct device *dev, struct device_attribute *attr,477			      char *buf)478{479	return acpi_tad_status_read(dev, buf, ACPI_TAD_AC_TIMER);480}481 482static DEVICE_ATTR_RW(ac_status);483 484static struct attribute *acpi_tad_attrs[] = {485	&dev_attr_caps.attr,486	&dev_attr_ac_alarm.attr,487	&dev_attr_ac_policy.attr,488	&dev_attr_ac_status.attr,489	NULL,490};491static const struct attribute_group acpi_tad_attr_group = {492	.attrs	= acpi_tad_attrs,493};494 495static ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr,496			      const char *buf, size_t count)497{498	int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_DC_TIMER);499 500	return ret ? ret : count;501}502 503static ssize_t dc_alarm_show(struct device *dev, struct device_attribute *attr,504			     char *buf)505{506	return acpi_tad_alarm_read(dev, buf, ACPI_TAD_DC_TIMER);507}508 509static DEVICE_ATTR_RW(dc_alarm);510 511static ssize_t dc_policy_store(struct device *dev, struct device_attribute *attr,512			       const char *buf, size_t count)513{514	int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_DC_TIMER);515 516	return ret ? ret : count;517}518 519static ssize_t dc_policy_show(struct device *dev, struct device_attribute *attr,520			      char *buf)521{522	return acpi_tad_policy_read(dev, buf, ACPI_TAD_DC_TIMER);523}524 525static DEVICE_ATTR_RW(dc_policy);526 527static ssize_t dc_status_store(struct device *dev, struct device_attribute *attr,528			       const char *buf, size_t count)529{530	int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_DC_TIMER);531 532	return ret ? ret : count;533}534 535static ssize_t dc_status_show(struct device *dev, struct device_attribute *attr,536			      char *buf)537{538	return acpi_tad_status_read(dev, buf, ACPI_TAD_DC_TIMER);539}540 541static DEVICE_ATTR_RW(dc_status);542 543static struct attribute *acpi_tad_dc_attrs[] = {544	&dev_attr_dc_alarm.attr,545	&dev_attr_dc_policy.attr,546	&dev_attr_dc_status.attr,547	NULL,548};549static const struct attribute_group acpi_tad_dc_attr_group = {550	.attrs	= acpi_tad_dc_attrs,551};552 553static int acpi_tad_disable_timer(struct device *dev, u32 timer_id)554{555	return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED);556}557 558static void acpi_tad_remove(struct platform_device *pdev)559{560	struct device *dev = &pdev->dev;561	acpi_handle handle = ACPI_HANDLE(dev);562	struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);563 564	device_init_wakeup(dev, false);565 566	pm_runtime_get_sync(dev);567 568	if (dd->capabilities & ACPI_TAD_DC_WAKE)569		sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group);570 571	sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group);572 573	acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);574	acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);575	if (dd->capabilities & ACPI_TAD_DC_WAKE) {576		acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER);577		acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER);578	}579 580	pm_runtime_put_sync(dev);581	pm_runtime_disable(dev);582	acpi_remove_cmos_rtc_space_handler(handle);583}584 585static int acpi_tad_probe(struct platform_device *pdev)586{587	struct device *dev = &pdev->dev;588	acpi_handle handle = ACPI_HANDLE(dev);589	struct acpi_tad_driver_data *dd;590	acpi_status status;591	unsigned long long caps;592	int ret;593 594	ret = acpi_install_cmos_rtc_space_handler(handle);595	if (ret < 0) {596		dev_info(dev, "Unable to install space handler\n");597		return -ENODEV;598	}599	/*600	 * Initialization failure messages are mostly about firmware issues, so601	 * print them at the "info" level.602	 */603	status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps);604	if (ACPI_FAILURE(status)) {605		dev_info(dev, "Unable to get capabilities\n");606		ret = -ENODEV;607		goto remove_handler;608	}609 610	if (!(caps & ACPI_TAD_AC_WAKE)) {611		dev_info(dev, "Unsupported capabilities\n");612		ret = -ENODEV;613		goto remove_handler;614	}615 616	if (!acpi_has_method(handle, "_PRW")) {617		dev_info(dev, "Missing _PRW\n");618		ret = -ENODEV;619		goto remove_handler;620	}621 622	dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);623	if (!dd) {624		ret = -ENOMEM;625		goto remove_handler;626	}627 628	dd->capabilities = caps;629	dev_set_drvdata(dev, dd);630 631	/*632	 * Assume that the ACPI PM domain has been attached to the device and633	 * simply enable system wakeup and runtime PM and put the device into634	 * runtime suspend.  Everything else should be taken care of by the ACPI635	 * PM domain callbacks.636	 */637	device_init_wakeup(dev, true);638	dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND |639				     DPM_FLAG_MAY_SKIP_RESUME);640	/*641	 * The platform bus type layer tells the ACPI PM domain powers up the642	 * device, so set the runtime PM status of it to "active".643	 */644	pm_runtime_set_active(dev);645	pm_runtime_enable(dev);646	pm_runtime_suspend(dev);647 648	ret = sysfs_create_group(&dev->kobj, &acpi_tad_attr_group);649	if (ret)650		goto fail;651 652	if (caps & ACPI_TAD_DC_WAKE) {653		ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group);654		if (ret)655			goto fail;656	}657 658	if (caps & ACPI_TAD_RT) {659		ret = sysfs_create_group(&dev->kobj, &acpi_tad_time_attr_group);660		if (ret)661			goto fail;662	}663 664	return 0;665 666fail:667	acpi_tad_remove(pdev);668	/* Don't fallthrough because cmos rtc space handler is removed in acpi_tad_remove() */669	return ret;670 671remove_handler:672	acpi_remove_cmos_rtc_space_handler(handle);673	return ret;674}675 676static const struct acpi_device_id acpi_tad_ids[] = {677	{"ACPI000E", 0},678	{}679};680 681static struct platform_driver acpi_tad_driver = {682	.driver = {683		.name = "acpi-tad",684		.acpi_match_table = acpi_tad_ids,685	},686	.probe = acpi_tad_probe,687	.remove_new = acpi_tad_remove,688};689MODULE_DEVICE_TABLE(acpi, acpi_tad_ids);690 691module_platform_driver(acpi_tad_driver);692