| /* Copyright (c) 2013, The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/spinlock.h> |
| #include <linux/semaphore.h> |
| #include <linux/kthread.h> |
| #include <linux/sched.h> |
| #include <linux/timer.h> |
| #include <linux/ctype.h> |
| #include <linux/uaccess.h> |
| #include <linux/errno.h> |
| |
| #include "wallclk.h" |
| |
| #define WALLCLK_SYSFS_MODULE_NAME "wallclk_sysfs" |
| |
| static struct kobject *wallclk_kobj; |
| |
| static ssize_t sfn_show(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| char *buf) |
| { |
| int rc; |
| |
| rc = wallclk_get_sfn(); |
| if (rc < 0) |
| return rc; |
| return snprintf(buf, 10, "%d\n", rc); |
| } |
| |
| static ssize_t sfn_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| u16 sfn; |
| int rc; |
| |
| if (kstrtou16(buf, 0, &sfn)) { |
| printk(KERN_ERR "%s: sfn input is not a valid u16 value\n", |
| WALLCLK_SYSFS_MODULE_NAME); |
| rc = -EINVAL; |
| goto out; |
| } |
| |
| rc = wallclk_set_sfn(sfn); |
| |
| if (rc) { |
| printk(KERN_ERR "%s: fail to set sfn\n", |
| WALLCLK_SYSFS_MODULE_NAME); |
| goto out; |
| } |
| rc = count; |
| |
| out: |
| return rc; |
| } |
| |
| static struct kobj_attribute sfn_attribute = |
| __ATTR(sfn, 0666, sfn_show, sfn_store); |
| |
| static ssize_t sfn_ref_show(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| char *buf) |
| { |
| int rc; |
| |
| rc = wallclk_get_sfn_ref(); |
| if (rc < 0) |
| return rc; |
| return snprintf(buf, 10, "%d\n", rc); |
| } |
| |
| static ssize_t sfn_ref_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| u16 sfn_ref; |
| int rc; |
| |
| if (kstrtou16(buf, 0, &sfn_ref)) { |
| printk(KERN_ERR "%s: sfn_ref input is not a valid u16 value\n", |
| WALLCLK_SYSFS_MODULE_NAME); |
| rc = -EINVAL; |
| goto out; |
| } |
| |
| rc = wallclk_set_sfn_ref(sfn_ref); |
| |
| if (rc) { |
| printk(KERN_ERR "%s: fail to set sfn_ref\n", |
| WALLCLK_SYSFS_MODULE_NAME); |
| goto out; |
| } |
| rc = count; |
| |
| out: |
| return rc; |
| } |
| |
| static struct kobj_attribute sfn_ref_attribute = |
| __ATTR(sfn_ref, 0666, sfn_ref_show, sfn_ref_store); |
| |
| static ssize_t reg_show(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| char *buf, |
| u32 offset) |
| { |
| int rc; |
| u32 val; |
| |
| rc = wallclk_reg_read(offset, &val); |
| if (rc) |
| return rc; |
| |
| return snprintf(buf, 20, "%08x\n", val); |
| } |
| |
| static ssize_t reg_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, |
| const size_t count, |
| u32 offset) |
| { |
| u32 v; |
| int rc; |
| |
| if (kstrtou32(buf, 0, &v)) { |
| printk(KERN_ERR "%s: input is not a valid u32 value\n", |
| WALLCLK_SYSFS_MODULE_NAME); |
| rc = -EINVAL; |
| goto out; |
| } |
| |
| rc = wallclk_reg_write(offset, v); |
| |
| if (rc) { |
| printk(KERN_ERR "%s: fail to set register(offset=0x%x)\n", |
| WALLCLK_SYSFS_MODULE_NAME, offset); |
| goto out; |
| } |
| rc = count; |
| |
| out: |
| return rc; |
| } |
| |
| static ssize_t ctrl_reg_show(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| char *buf) |
| { |
| return reg_show(kobj, attr, buf, CTRL_REG_OFFSET); |
| } |
| |
| static ssize_t ctrl_reg_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, |
| const size_t count) |
| { |
| return reg_store(kobj, attr, buf, count, CTRL_REG_OFFSET); |
| } |
| |
| static struct kobj_attribute ctrl_reg_attribute = |
| __ATTR(ctrl_reg, 0666, ctrl_reg_show, ctrl_reg_store); |
| |
| static ssize_t basetime0_reg_show(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| char *buf) |
| { |
| return reg_show(kobj, attr, buf, CLK_BASE_TIME0_OFFSET); |
| } |
| |
| static ssize_t basetime0_reg_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| return reg_store(kobj, attr, buf, count, CLK_BASE_TIME0_OFFSET); |
| } |
| |
| static struct kobj_attribute basetime0_reg_attribute = |
| __ATTR(base_time0_reg, 0666, basetime0_reg_show, basetime0_reg_store); |
| |
| static ssize_t basetime1_reg_show(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| char *buf) |
| { |
| return reg_show(kobj, attr, buf, CLK_BASE_TIME1_OFFSET); |
| } |
| |
| static ssize_t basetime1_reg_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| return reg_store(kobj, attr, buf, count, CLK_BASE_TIME1_OFFSET); |
| } |
| |
| static struct kobj_attribute basetime1_reg_attribute = |
| __ATTR(base_time1_reg, 0666, basetime1_reg_show, basetime1_reg_store); |
| |
| static ssize_t pulse_cnt_reg_show(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| char *buf) |
| { |
| return reg_show(kobj, attr, buf, PULSE_CNT_REG_OFFSET); |
| } |
| |
| static ssize_t pulse_cnt_reg_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| return reg_store(kobj, attr, buf, count, PULSE_CNT_REG_OFFSET); |
| } |
| |
| static struct kobj_attribute pulse_cnt_reg_attribute = |
| __ATTR(pulse_cnt_reg, 0666, pulse_cnt_reg_show, pulse_cnt_reg_store); |
| |
| static ssize_t clk_cnt_reg_show(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| char *buf) |
| { |
| return reg_show(kobj, attr, buf, CLK_CNT_REG_OFFSET); |
| } |
| |
| static ssize_t clk_cnt_reg_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| return reg_store(kobj, attr, buf, count, CLK_CNT_REG_OFFSET); |
| } |
| |
| static struct kobj_attribute clk_cnt_reg_attribute = |
| __ATTR(clock_cnt_reg, 0666, clk_cnt_reg_show, clk_cnt_reg_store); |
| |
| static ssize_t clk_cnt_snapshot_reg_show(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| char *buf) |
| { |
| return reg_show(kobj, attr, buf, CLK_CNT_SNAPSHOT_REG_OFFSET); |
| } |
| |
| static struct kobj_attribute clk_cnt_snapshot_reg_attribute = |
| __ATTR(clock_cnt_snapshot_reg, 0444, clk_cnt_snapshot_reg_show, NULL); |
| |
| static struct attribute *wallclk_attrs[] = { |
| &sfn_attribute.attr, |
| &sfn_ref_attribute.attr, |
| &ctrl_reg_attribute.attr, |
| &pulse_cnt_reg_attribute.attr, |
| &clk_cnt_snapshot_reg_attribute.attr, |
| &clk_cnt_reg_attribute.attr, |
| &basetime0_reg_attribute.attr, |
| &basetime1_reg_attribute.attr, |
| NULL |
| }; |
| |
| static struct attribute_group wallclk_attr_group = { |
| .attrs = wallclk_attrs, |
| }; |
| |
| static int __init wallclk_sysfs_init(void) |
| { |
| int rc; |
| |
| wallclk_kobj = kobject_create_and_add("wallclk", kernel_kobj); |
| if (!wallclk_kobj) { |
| printk(KERN_ERR "%s: failed to create kobject\n", |
| WALLCLK_SYSFS_MODULE_NAME); |
| rc = -ENOMEM; |
| goto out; |
| } |
| |
| rc = sysfs_create_group(wallclk_kobj, &wallclk_attr_group); |
| if (rc) { |
| kobject_put(wallclk_kobj); |
| printk(KERN_ERR "%s: failed to create sysfs group\n", |
| WALLCLK_SYSFS_MODULE_NAME); |
| } |
| |
| out: |
| return rc; |
| } |
| |
| static void __exit wallclk_sysfs_exit(void) |
| { |
| kobject_put(wallclk_kobj); |
| } |
| |
| module_init(wallclk_sysfs_init); |
| module_exit(wallclk_sysfs_exit); |
| |
| MODULE_DESCRIPTION("Wall clock SysFS"); |
| MODULE_LICENSE("GPL v2"); |