// SPDX-License-Identifier: GPL-2.0 #include "pr.h" #include #include #include #include #include #include "util.h" #include "ec.h" #include "misc.h" #include "pdev.h" /* ========================================================================== */ struct platform_device *ac71_platform_dev; /* ========================================================================== */ static ssize_t fan_reduced_duty_cycle_show(struct device *dev, struct device_attribute *attr, char *buf) { int status = ec_read_byte(BIOS_CTRL_3_ADDR); if (status < 0) return status; return sprintf(buf, "%d\n", !!(status & BIOS_CTRL_3_FAN_REDUCED_DUTY_CYCLE)); } static ssize_t fan_reduced_duty_cycle_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int status; bool value; if (kstrtobool(buf, &value)) return -EINVAL; status = ec_read_byte(BIOS_CTRL_3_ADDR); if (status < 0) return status; status = SET_BIT(status, BIOS_CTRL_3_FAN_REDUCED_DUTY_CYCLE, value); status = ec_write_byte(BIOS_CTRL_3_ADDR, status); if (status < 0) return status; return count; } static ssize_t fan_always_on_show(struct device *dev, struct device_attribute *attr, char *buf) { int status = ec_read_byte(BIOS_CTRL_3_ADDR); if (status < 0) return status; return sprintf(buf, "%d\n", !!(status & BIOS_CTRL_3_FAN_ALWAYS_ON)); } static ssize_t fan_always_on_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int status; bool value; if (kstrtobool(buf, &value)) return -EINVAL; status = ec_read_byte(BIOS_CTRL_3_ADDR); if (status < 0) return status; status = SET_BIT(status, BIOS_CTRL_3_FAN_ALWAYS_ON, value); status = ec_write_byte(BIOS_CTRL_3_ADDR, status); if (status < 0) return status; return count; } static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf) { int status = ac71_fn_lock_get_state(); if (status < 0) return status; return sprintf(buf, "%d\n", status); } static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int status; bool value; if (kstrtobool(buf, &value)) return -EINVAL; status = ac71_fn_lock_set_state(value); if (status < 0) return status; return count; } static ssize_t fn_lock_switch_show(struct device *dev, struct device_attribute *attr, char *buf) { int status = ec_read_byte(AP_BIOS_BYTE_ADDR); if (status < 0) return status; return sprintf(buf, "%d\n", !!(status & AP_BIOS_BYTE_FN_LOCK_SWITCH)); } static ssize_t fn_lock_switch_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int status; bool value; if (kstrtobool(buf, &value)) return -EINVAL; status = ec_read_byte(AP_BIOS_BYTE_ADDR); if (status < 0) return status; status = SET_BIT(status, AP_BIOS_BYTE_FN_LOCK_SWITCH, value); status = ec_write_byte(AP_BIOS_BYTE_ADDR, status); if (status < 0) return status; return count; } static ssize_t manual_control_show(struct device *dev, struct device_attribute *attr, char *buf) { int status = ec_read_byte(CTRL_1_ADDR); if (status < 0) return status; return sprintf(buf, "%d\n", !!(status & CTRL_1_MANUAL_MODE)); } static ssize_t manual_control_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int status; bool value; if (kstrtobool(buf, &value)) return -EINVAL; status = ec_read_byte(CTRL_1_ADDR); if (status < 0) return status; status = SET_BIT(status, CTRL_1_MANUAL_MODE, value); status = ec_write_byte(CTRL_1_ADDR, status); if (status < 0) return status; return count; } static ssize_t super_key_lock_show(struct device *dev, struct device_attribute *attr, char *buf) { int status = ec_read_byte(STATUS_1_ADDR); if (status < 0) return status; return sprintf(buf, "%d\n", !!(status & STATUS_1_SUPER_KEY_LOCK)); } static ssize_t super_key_lock_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int status; bool value; if (kstrtobool(buf, &value)) return -EINVAL; status = ec_read_byte(STATUS_1_ADDR); if (status < 0) return status; if (value != !!(status & STATUS_1_SUPER_KEY_LOCK)) { status = ec_write_byte(TRIGGER_1_ADDR, TRIGGER_1_SUPER_KEY_LOCK); if (status < 0) return status; } return count; } /* ========================================================================== */ static DEVICE_ATTR_RW(fn_lock); static DEVICE_ATTR_RW(fn_lock_switch); static DEVICE_ATTR_RW(fan_always_on); static DEVICE_ATTR_RW(fan_reduced_duty_cycle); static DEVICE_ATTR_RW(manual_control); static DEVICE_ATTR_RW(super_key_lock); static struct attribute *ac71_attrs[] = { &dev_attr_fn_lock.attr, &dev_attr_fn_lock_switch.attr, &dev_attr_fan_always_on.attr, &dev_attr_fan_reduced_duty_cycle.attr, &dev_attr_manual_control.attr, &dev_attr_super_key_lock.attr, NULL }; /* ========================================================================== */ static umode_t ac71_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { bool ok = false; if (attr == &dev_attr_fn_lock.attr || attr == &dev_attr_fn_lock_switch.attr) ok = true; else if (attr == &dev_attr_fan_always_on.attr || attr == &dev_attr_fan_reduced_duty_cycle.attr) ok = true; else if (attr == &dev_attr_manual_control.attr) ok = true; else if (attr == &dev_attr_super_key_lock.attr) ok = false; return ok ? attr->mode : 0; } /* ========================================================================== */ static const struct attribute_group ac71_group = { .is_visible = ac71_attr_is_visible, .attrs = ac71_attrs, }; static const struct attribute_group *ac71_groups[] = { &ac71_group, NULL }; /* ========================================================================== */ int __init ac71_pdev_setup(void) { int err; ac71_platform_dev = platform_device_alloc(KBUILD_MODNAME, PLATFORM_DEVID_NONE); if (!ac71_platform_dev) { err = -ENOMEM; goto out; } ac71_platform_dev->dev.groups = ac71_groups; err = platform_device_add(ac71_platform_dev); if (err) { platform_device_put(ac71_platform_dev); ac71_platform_dev = NULL; } out: return err; } void ac71_pdev_cleanup(void) { /* checks for IS_ERR_OR_NULL() */ platform_device_unregister(ac71_platform_dev); }