/* * drivers/power/axp/axp-sysfs.c * (C) Copyright 2010-2016 * Allwinner Technology Co., Ltd. * Pannan * * axp sysfs for users * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include "axp-core.h" static int axp_num; static ssize_t axp_num_show(struct class *class, struct class_attribute *attr, char *buf) { return sprintf(buf, "pmu%d\n", axp_num); } static ssize_t axp_num_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int val, err; err = kstrtoint(buf, 16, &val); if (err) return err; axp_num = val; if ((axp_num >= AXP_ONLINE_SUM) || (axp_num < 0)) return -EINVAL; return count; } static ssize_t axp_name_show(struct class *class, struct class_attribute *attr, char *buf) { return sprintf(buf, "%s\n", get_pmu_cur_name(axp_num)); } static u8 axp_reg_addr; static ssize_t axp_reg_show(struct class *class, struct class_attribute *attr, char *buf) { u8 val; struct axp_dev *cur_axp_dev = get_pmu_cur_dev(axp_num); if (cur_axp_dev == NULL) return sprintf(buf, "invalid parameters\n"); if (cur_axp_dev->is_dummy) return sprintf(buf, "unsupported\n"); axp_regmap_read(cur_axp_dev->regmap, axp_reg_addr, &val); return sprintf(buf, "%s:REG[0x%x]=0x%x\n", get_pmu_cur_name(axp_num), axp_reg_addr, val); } static ssize_t axp_reg_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { s32 tmp; u8 val; int err; struct axp_dev *cur_axp_dev = get_pmu_cur_dev(axp_num); if (cur_axp_dev == NULL) { pr_warn("invalid parameters\n"); return -EINVAL; } if (cur_axp_dev->is_dummy) { pr_err("unsupported\n"); return -EINVAL; } err = kstrtoint(buf, 16, &tmp); if (err) return err; if (tmp < 256) { axp_reg_addr = tmp; } else { val = tmp & 0x00FF; axp_reg_addr = (tmp >> 8) & 0x00FF; axp_regmap_write(cur_axp_dev->regmap, axp_reg_addr, val); } return count; } static u32 axp_regs_data_size = 2; static ssize_t axp_regs_show(struct class *class, struct class_attribute *attr, char *buf) { u8 val; s32 count = 0, i = 0; struct axp_dev *cur_axp_dev = get_pmu_cur_dev(axp_num); if (cur_axp_dev == NULL) return sprintf(buf, "invalid parameters\n"); if (cur_axp_dev->is_dummy) return sprintf(buf, "unsupported\n"); for (i = 0; i < axp_regs_data_size; i++) { axp_regmap_read(cur_axp_dev->regmap, axp_reg_addr+i, &val); count += sprintf(buf+count, "%s:REG[0x%x]=0x%x\n", get_pmu_cur_name(axp_num), axp_reg_addr+i, val); } return count; } static ssize_t axp_regs_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { u32 data1 = 0; u8 val[2]; char *endp; struct axp_dev *cur_axp_dev = get_pmu_cur_dev(axp_num); if (cur_axp_dev == NULL) { pr_warn("invalid parameters\n"); return -EINVAL; } if (cur_axp_dev->is_dummy) { pr_err("unsupported\n"); return -EINVAL; } data1 = simple_strtoul(buf, &endp, 16); if (*endp != ' ') { pr_err("%s: %d\n", __func__, __LINE__); return -EINVAL; } buf = endp + 1; axp_regs_data_size = simple_strtoul(buf, &endp, 10); if (data1 < 256) { axp_reg_addr = data1; } else { axp_reg_addr = (data1 >> 16) & 0xFF; val[0] = (data1 >> 8) & 0xFF; val[1] = data1 & 0xFF; axp_regmap_writes(cur_axp_dev->regmap, axp_reg_addr, 2, val); } return count; } int axp_debug_mask; static ssize_t debugmask_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int val, err; err = kstrtoint(buf, 16, &val); if (err) return err; axp_debug_mask = val; return count; } static ssize_t debugmask_show(struct class *class, struct class_attribute *attr, char *buf) { char *s = buf; char *end = (char *)((ptrdiff_t)buf + (ptrdiff_t)PAGE_SIZE); s += scnprintf(s, end - s, "%s\n", "1: SPLY 2: REGU 4: INT 8: CHG"); s += scnprintf(s, end - s, "debug_mask=%d\n", axp_debug_mask); return s - buf; } static ssize_t add_sys_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int name_len; const char *arg; char sys_name[20] = {0}; /* Find length of lock name */ arg = buf; while (*arg && !isspace(*arg)) arg++; name_len = arg - buf; if ((!name_len) && (name_len > 20)) goto bad_arg; strncpy(sys_name, buf, name_len); axp_add_sys_pwr_dm(sys_name); return count; bad_arg: pr_err("%s para error.\n", __func__); return count; } static ssize_t del_sys_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int name_len; const char *arg; char sys_name[20] = {0}; /* Find length of lock name */ arg = buf; while (*arg && !isspace(*arg)) arg++; name_len = arg - buf; if ((!name_len) && (name_len > 20)) goto bad_arg; strncpy(sys_name, buf, name_len); axp_del_sys_pwr_dm(sys_name); return count; bad_arg: pr_err("%s para error.\n", __func__); return count; } static ssize_t get_sys_show(struct class *class, struct class_attribute *attr, char *buf) { ssize_t count = 0; count += sprintf(buf+count, "0x%x\n", axp_get_sys_pwr_dm_mask()); return count; } char check_sys_name[20] = {0}; static ssize_t check_sys_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int name_len; const char *arg; /* Find length of lock name */ arg = buf; while (*arg && !isspace(*arg)) arg++; name_len = arg - buf; if ((!name_len) && (name_len > 20)) goto bad_arg; strncpy(check_sys_name, buf, name_len); return count; bad_arg: pr_err("%s para error.\n", __func__); return count; } static ssize_t check_sys_show(struct class *class, struct class_attribute *attr, char *buf) { s32 sys_id_conut = 0, ret = 0; ssize_t count = 0; char ldo_name[20] = {0}; if (check_sys_name == NULL) { pr_err("please input sys name\n"); return -1; } sys_id_conut = axp_check_sys_id(check_sys_name); if (sys_id_conut < 0) { pr_err("%s: %s not sys id.\n", __func__, check_sys_name); return -1; } ret = axp_get_ldo_name(check_sys_name, (char *)&ldo_name); if (ret < 0) { pr_err("%s: get ldo name for id: %s failed\n", __func__, check_sys_name); return -1; } ret = axp_is_sys_pwr_dm_active(sys_id_conut); count += sprintf(buf+count, "%d\n", ret); return count; } static struct class_attribute axp_class_attrs[] = { __ATTR(axp_name, S_IRUGO, axp_name_show, NULL), __ATTR(axp_num, S_IRUGO|S_IWUSR, axp_num_show, axp_num_store), __ATTR(axp_reg, S_IRUGO|S_IWUSR, axp_reg_show, axp_reg_store), __ATTR(axp_regs, S_IRUGO|S_IWUSR, axp_regs_show, axp_regs_store), __ATTR(debug_mask, S_IRUGO|S_IWUSR, debugmask_show, debugmask_store), __ATTR(sys_pwr_add, S_IWUSR, NULL, add_sys_store), __ATTR(sys_pwr_del, S_IWUSR, NULL, del_sys_store), __ATTR(sys_pwr_get, S_IRUGO, get_sys_show, NULL), __ATTR(sys_pwr_check, S_IRUGO|S_IWUSR, check_sys_show, check_sys_store), __ATTR_NULL }; struct class axp_class = { .name = "axp", .class_attrs = axp_class_attrs, }; #ifdef CONFIG_AXP_TWI_USED #include #include static int axp_reboot_notifier_call(struct notifier_block *this, unsigned long code, void *_cmd) { disable_cpufreq(); return NOTIFY_DONE; } static struct notifier_block axp_reboot_notifier = { .notifier_call = axp_reboot_notifier_call, }; #endif static int __init axp_sysfs_init(void) { int status; #ifdef CONFIG_AXP_TWI_USED register_reboot_notifier(&axp_reboot_notifier); #endif status = class_register(&axp_class); if (status < 0) pr_err("%s,%d err, status:%d\n", __func__, __LINE__, status); return status; } arch_initcall(axp_sysfs_init); static void __exit axp_sysfs_exit(void) { class_unregister(&axp_class); } module_exit(axp_sysfs_exit); MODULE_DESCRIPTION("ALLWINNERTECH axp sysfs"); MODULE_AUTHOR("pannan"); MODULE_LICENSE("GPL");