diff options
Diffstat (limited to 'drivers/acpi/acpi_tad.c')
| -rw-r--r-- | drivers/acpi/acpi_tad.c | 201 | 
1 files changed, 201 insertions, 0 deletions
| diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index e99c4ed7e677..33a4bcdaa4d7 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -52,6 +52,201 @@ struct acpi_tad_driver_data {  	u32 capabilities;  }; +struct acpi_tad_rt { +	u16 year;  /* 1900 - 9999 */ +	u8 month;  /* 1 - 12 */ +	u8 day;    /* 1 - 31 */ +	u8 hour;   /* 0 - 23 */ +	u8 minute; /* 0 - 59 */ +	u8 second; /* 0 - 59 */ +	u8 valid;  /* 0 (failed) or 1 (success) for reads, 0 for writes */ +	u16 msec;  /* 1 - 1000 */ +	s16 tz;    /* -1440 to 1440 or 2047 (unspecified) */ +	u8 daylight; +	u8 padding[3]; /* must be 0 */ +} __packed; + +static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) +{ +	acpi_handle handle = ACPI_HANDLE(dev); +	union acpi_object args[] = { +		{ .type = ACPI_TYPE_BUFFER, }, +	}; +	struct acpi_object_list arg_list = { +		.pointer = args, +		.count = ARRAY_SIZE(args), +	}; +	unsigned long long retval; +	acpi_status status; + +	if (rt->year < 1900 || rt->year > 9999 || +	    rt->month < 1 || rt->month > 12 || +	    rt->hour > 23 || rt->minute > 59 || rt->second > 59 || +	    rt->tz < -1440 || (rt->tz > 1440 && rt->tz != 2047) || +	    rt->daylight > 3) +		return -ERANGE; + +	args[0].buffer.pointer = (u8 *)rt; +	args[0].buffer.length = sizeof(*rt); + +	pm_runtime_get_sync(dev); + +	status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval); + +	pm_runtime_put_sync(dev); + +	if (ACPI_FAILURE(status) || retval) +		return -EIO; + +	return 0; +} + +static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt) +{ +	acpi_handle handle = ACPI_HANDLE(dev); +	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER }; +	union acpi_object *out_obj; +	struct acpi_tad_rt *data; +	acpi_status status; +	int ret = -EIO; + +	pm_runtime_get_sync(dev); + +	status = acpi_evaluate_object(handle, "_GRT", NULL, &output); + +	pm_runtime_put_sync(dev); + +	if (ACPI_FAILURE(status)) +		goto out_free; + +	out_obj = output.pointer; +	if (out_obj->type != ACPI_TYPE_BUFFER) +		goto out_free; + +	if (out_obj->buffer.length != sizeof(*rt)) +		goto out_free; + +	data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); +	if (!data->valid) +		goto out_free; + +	memcpy(rt, data, sizeof(*rt)); +	ret = 0; + +out_free: +	ACPI_FREE(output.pointer); +	return ret; +} + +static char *acpi_tad_rt_next_field(char *s, int *val) +{ +	char *p; + +	p = strchr(s, ':'); +	if (!p) +		return NULL; + +	*p = '\0'; +	if (kstrtoint(s, 10, val)) +		return NULL; + +	return p + 1; +} + +static ssize_t time_store(struct device *dev, struct device_attribute *attr, +			  const char *buf, size_t count) +{ +	struct acpi_tad_rt rt; +	char *str, *s; +	int val, ret = -ENODATA; + +	str = kmemdup_nul(buf, count, GFP_KERNEL); +	if (!str) +		return -ENOMEM; + +	s = acpi_tad_rt_next_field(str, &val); +	if (!s) +		goto out_free; + +	rt.year = val; + +	s = acpi_tad_rt_next_field(s, &val); +	if (!s) +		goto out_free; + +	rt.month = val; + +	s = acpi_tad_rt_next_field(s, &val); +	if (!s) +		goto out_free; + +	rt.day = val; + +	s = acpi_tad_rt_next_field(s, &val); +	if (!s) +		goto out_free; + +	rt.hour = val; + +	s = acpi_tad_rt_next_field(s, &val); +	if (!s) +		goto out_free; + +	rt.minute = val; + +	s = acpi_tad_rt_next_field(s, &val); +	if (!s) +		goto out_free; + +	rt.second = val; + +	s = acpi_tad_rt_next_field(s, &val); +	if (!s) +		goto out_free; + +	rt.tz = val; + +	if (kstrtoint(s, 10, &val)) +		goto out_free; + +	rt.daylight = val; + +	rt.valid = 0; +	rt.msec = 0; +	memset(rt.padding, 0, 3); + +	ret = acpi_tad_set_real_time(dev, &rt); + +out_free: +	kfree(str); +	return ret ? ret : count; +} + +static ssize_t time_show(struct device *dev, struct device_attribute *attr, +			 char *buf) +{ +	struct acpi_tad_rt rt; +	int ret; + +	ret = acpi_tad_get_real_time(dev, &rt); +	if (ret) +		return ret; + +	return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", +		       rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second, +		       rt.tz, rt.daylight); +} + +static DEVICE_ATTR(time, S_IRUSR | S_IWUSR, time_show, time_store); + +static struct attribute *acpi_tad_time_attrs[] = { +	&dev_attr_time.attr, +	NULL, +}; +static const struct attribute_group acpi_tad_time_attr_group = { +	.attrs	= acpi_tad_time_attrs, +}; +  static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id,  			     u32 value)  { @@ -448,6 +643,12 @@ static int acpi_tad_probe(struct platform_device *pdev)  			goto fail;  	} +	if (caps & ACPI_TAD_RT) { +		ret = sysfs_create_group(&dev->kobj, &acpi_tad_time_attr_group); +		if (ret) +			goto fail; +	} +  	return 0;  fail: |