forked from pobrn/qc71_laptop
-
Notifications
You must be signed in to change notification settings - Fork 2
/
battery.c
140 lines (105 loc) · 3.47 KB
/
battery.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: GPL-2.0
/*
* kc57_battery.c - A charging limit driver for KC57 laptop
*
* based on qc71_laptop: https://github.com/pobrn/qc71_laptop
*
* Copyright (C) 2020-2023 Barnabás Pőcze <[email protected]>
* Copyright (C) 2023-2024 nuvole <[email protected]>
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/power_supply.h>
#include <acpi/battery.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/version.h>
#include "kc57_battery.h"
/* ========================================================================== */
#if IS_ENABLED(CONFIG_ACPI_BATTERY)
static bool battery_hook_registered;
static bool nobattery;
module_param(nobattery, bool, 0444);
MODULE_PARM_DESC(nobattery, "do not expose battery related controls (default=false)");
/* ========================================================================== */
static ssize_t charge_control_end_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int status, r_status;
r_status = uniwill_read_ec_ram(BATT_CHARGE_CTRL_ADDR, (u8 *)&status);
if (r_status < 0)
return r_status;
status &= BATT_CHARGE_CTRL_VALUE_MASK;
if (status == 0)
status = 100;
return sprintf(buf, "%d\n", status);
}
static ssize_t charge_control_end_threshold_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int status, value, r_status;
if (kstrtoint(buf, 10, &value) || !(1 <= value && value <= 100))
return -EINVAL;
r_status = uniwill_read_ec_ram(BATT_CHARGE_CTRL_ADDR, (u8 *)&status);
if (r_status < 0)
return r_status;
if (value == 100)
value = 0;
status = (status & ~BATT_CHARGE_CTRL_VALUE_MASK) | value;
status = uniwill_write_ec_ram(BATT_CHARGE_CTRL_ADDR, status & 0xFF);
if (status < 0)
return status;
return count;
}
static DEVICE_ATTR_RW(charge_control_end_threshold);
static struct attribute *kc57_laptop_batt_attrs[] = {
&dev_attr_charge_control_end_threshold.attr,
NULL
};
ATTRIBUTE_GROUPS(kc57_laptop_batt);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0)
static int kc57_laptop_batt_add(struct power_supply *battery, struct acpi_battery_hook *hook)
#else
static int kc57_laptop_batt_add(struct power_supply *battery)
#endif
{
if (strcmp(battery->desc->name, "BAT0") != 0)
return 0;
return device_add_groups(&battery->dev, kc57_laptop_batt_groups);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0)
static int kc57_laptop_batt_remove(struct power_supply *battery, struct acpi_battery_hook *hook)
#else
static int kc57_laptop_batt_remove(struct power_supply *battery)
#endif
{
if (strcmp(battery->desc->name, "BAT0") != 0)
return 0;
device_remove_groups(&battery->dev, kc57_laptop_batt_groups);
return 0;
}
static struct acpi_battery_hook kc57_laptop_batt_hook = {
.add_battery = kc57_laptop_batt_add,
.remove_battery = kc57_laptop_batt_remove,
.name = "KC57 laptop battery extension",
};
static int __init kc57_battery_setup(void)
{
if (nobattery)
return -ENODEV;
battery_hook_register(&kc57_laptop_batt_hook);
battery_hook_registered = true;
return 0;
}
static void kc57_battery_cleanup(void)
{
if (battery_hook_registered)
battery_hook_unregister(&kc57_laptop_batt_hook);
}
#endif /* IS_ENABLED(CONFIG_ACPI_BATTERY) */
module_init(kc57_battery_setup);
module_exit(kc57_battery_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("nuvole <[email protected]>");
MODULE_DESCRIPTION("Charging limit driver for KC57 laptop");