nixos/spec/reimaden/ac71/pdev.c

296 lines
6.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include "pr.h"
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#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);
}