1 // SPDX-License-Identifier: GPL-2.0+
5 * Copyright (C) 2023 System76
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/acpi.h>
13 #include <linux/hwmon.h>
14 #include <linux/hwmon-sysfs.h>
15 #include <linux/init.h>
16 #include <linux/input.h>
17 #include <linux/kernel.h>
18 #include <linux/leds.h>
19 #include <linux/module.h>
20 #include <linux/pci_ids.h>
21 #include <linux/power_supply.h>
22 #include <linux/sysfs.h>
23 #include <linux/types.h>
25 #include <acpi/battery.h>
33 struct system76_data
{
34 struct acpi_device
*acpi_dev
;
35 struct led_classdev ap_led
;
36 struct led_classdev kb_led
;
37 enum led_brightness kb_brightness
;
38 enum led_brightness kb_toggle_brightness
;
41 union acpi_object
*nfan
;
42 union acpi_object
*ntmp
;
43 struct input_dev
*input
;
45 enum kbled_type kbled_type
;
48 static const struct acpi_device_id device_ids
[] = {
52 MODULE_DEVICE_TABLE(acpi
, device_ids
);
54 // Array of keyboard LED brightness levels
55 static const enum led_brightness kb_levels
[] = {
64 // Array of keyboard LED colors in 24-bit RGB format
65 static const int kb_colors
[] = {
75 // Get a System76 ACPI device value by name
76 static int system76_get(struct system76_data
*data
, char *method
)
80 unsigned long long ret
= 0;
82 handle
= acpi_device_handle(data
->acpi_dev
);
83 status
= acpi_evaluate_integer(handle
, method
, NULL
, &ret
);
84 if (ACPI_SUCCESS(status
))
89 // Get a System76 ACPI device value by name with index
90 static int system76_get_index(struct system76_data
*data
, char *method
, int index
)
92 union acpi_object obj
;
93 struct acpi_object_list obj_list
;
96 unsigned long long ret
= 0;
98 obj
.type
= ACPI_TYPE_INTEGER
;
99 obj
.integer
.value
= index
;
101 obj_list
.pointer
= &obj
;
103 handle
= acpi_device_handle(data
->acpi_dev
);
104 status
= acpi_evaluate_integer(handle
, method
, &obj_list
, &ret
);
105 if (ACPI_SUCCESS(status
))
110 // Get a System76 ACPI device object by name
111 static int system76_get_object(struct system76_data
*data
, char *method
, union acpi_object
**obj
)
115 struct acpi_buffer buf
= { ACPI_ALLOCATE_BUFFER
, NULL
};
117 handle
= acpi_device_handle(data
->acpi_dev
);
118 status
= acpi_evaluate_object(handle
, method
, NULL
, &buf
);
119 if (ACPI_SUCCESS(status
)) {
127 // Get a name from a System76 ACPI device object
128 static char *system76_name(union acpi_object
*obj
, int index
)
130 if (obj
&& obj
->type
== ACPI_TYPE_PACKAGE
&& index
<= obj
->package
.count
) {
131 if (obj
->package
.elements
[index
].type
== ACPI_TYPE_STRING
)
132 return obj
->package
.elements
[index
].string
.pointer
;
138 // Set a System76 ACPI device value by name
139 static int system76_set(struct system76_data
*data
, char *method
, int value
)
141 union acpi_object obj
;
142 struct acpi_object_list obj_list
;
146 obj
.type
= ACPI_TYPE_INTEGER
;
147 obj
.integer
.value
= value
;
149 obj_list
.pointer
= &obj
;
150 handle
= acpi_device_handle(data
->acpi_dev
);
151 status
= acpi_evaluate_object(handle
, method
, &obj_list
, NULL
);
152 if (ACPI_SUCCESS(status
))
158 #define BATTERY_THRESHOLD_INVALID 0xFF
165 static ssize_t
battery_get_threshold(int which
, char *buf
)
167 struct acpi_object_list input
;
168 union acpi_object param
;
171 unsigned long long ret
= BATTERY_THRESHOLD_INVALID
;
173 handle
= ec_get_handle();
178 input
.pointer
= ¶m
;
179 // Start/stop selection
180 param
.type
= ACPI_TYPE_INTEGER
;
181 param
.integer
.value
= which
;
183 status
= acpi_evaluate_integer(handle
, "GBCT", &input
, &ret
);
184 if (ACPI_FAILURE(status
))
186 if (ret
== BATTERY_THRESHOLD_INVALID
)
189 return sysfs_emit(buf
, "%d\n", (int)ret
);
192 static ssize_t
battery_set_threshold(int which
, const char *buf
, size_t count
)
194 struct acpi_object_list input
;
195 union acpi_object params
[2];
201 handle
= ec_get_handle();
205 ret
= kstrtouint(buf
, 10, &value
);
213 input
.pointer
= params
;
214 // Start/stop selection
215 params
[0].type
= ACPI_TYPE_INTEGER
;
216 params
[0].integer
.value
= which
;
218 params
[1].type
= ACPI_TYPE_INTEGER
;
219 params
[1].integer
.value
= value
;
221 status
= acpi_evaluate_object(handle
, "SBCT", &input
, NULL
);
222 if (ACPI_FAILURE(status
))
228 static ssize_t
charge_control_start_threshold_show(struct device
*dev
,
229 struct device_attribute
*attr
, char *buf
)
231 return battery_get_threshold(THRESHOLD_START
, buf
);
234 static ssize_t
charge_control_start_threshold_store(struct device
*dev
,
235 struct device_attribute
*attr
, const char *buf
, size_t count
)
237 return battery_set_threshold(THRESHOLD_START
, buf
, count
);
240 static DEVICE_ATTR_RW(charge_control_start_threshold
);
242 static ssize_t
charge_control_end_threshold_show(struct device
*dev
,
243 struct device_attribute
*attr
, char *buf
)
245 return battery_get_threshold(THRESHOLD_END
, buf
);
248 static ssize_t
charge_control_end_threshold_store(struct device
*dev
,
249 struct device_attribute
*attr
, const char *buf
, size_t count
)
251 return battery_set_threshold(THRESHOLD_END
, buf
, count
);
254 static DEVICE_ATTR_RW(charge_control_end_threshold
);
256 static struct attribute
*system76_battery_attrs
[] = {
257 &dev_attr_charge_control_start_threshold
.attr
,
258 &dev_attr_charge_control_end_threshold
.attr
,
262 ATTRIBUTE_GROUPS(system76_battery
);
264 static int system76_battery_add(struct power_supply
*battery
, struct acpi_battery_hook
*hook
)
266 // System76 EC only supports 1 battery
267 if (strcmp(battery
->desc
->name
, "BAT0") != 0)
270 if (device_add_groups(&battery
->dev
, system76_battery_groups
))
276 static int system76_battery_remove(struct power_supply
*battery
, struct acpi_battery_hook
*hook
)
278 device_remove_groups(&battery
->dev
, system76_battery_groups
);
282 static struct acpi_battery_hook system76_battery_hook
= {
283 .add_battery
= system76_battery_add
,
284 .remove_battery
= system76_battery_remove
,
285 .name
= "System76 Battery Extension",
288 static void system76_battery_init(void)
290 battery_hook_register(&system76_battery_hook
);
293 static void system76_battery_exit(void)
295 battery_hook_unregister(&system76_battery_hook
);
298 // Get the airplane mode LED brightness
299 static enum led_brightness
ap_led_get(struct led_classdev
*led
)
301 struct system76_data
*data
;
304 data
= container_of(led
, struct system76_data
, ap_led
);
305 value
= system76_get(data
, "GAPL");
307 return (enum led_brightness
)value
;
312 // Set the airplane mode LED brightness
313 static int ap_led_set(struct led_classdev
*led
, enum led_brightness value
)
315 struct system76_data
*data
;
317 data
= container_of(led
, struct system76_data
, ap_led
);
318 return system76_set(data
, "SAPL", value
== LED_OFF
? 0 : 1);
321 // Get the last set keyboard LED brightness
322 static enum led_brightness
kb_led_get(struct led_classdev
*led
)
324 struct system76_data
*data
;
326 data
= container_of(led
, struct system76_data
, kb_led
);
327 return data
->kb_brightness
;
330 // Set the keyboard LED brightness
331 static int kb_led_set(struct led_classdev
*led
, enum led_brightness value
)
333 struct system76_data
*data
;
335 data
= container_of(led
, struct system76_data
, kb_led
);
336 data
->kb_brightness
= value
;
337 if (acpi_has_method(acpi_device_handle(data
->acpi_dev
), "GKBK")) {
338 return system76_set(data
, "SKBB", (int)data
->kb_brightness
);
340 return system76_set(data
, "SKBL", (int)data
->kb_brightness
);
344 // Get the last set keyboard LED color
345 static ssize_t
kb_led_color_show(
347 struct device_attribute
*dev_attr
,
350 struct led_classdev
*led
;
351 struct system76_data
*data
;
353 led
= dev_get_drvdata(dev
);
354 data
= container_of(led
, struct system76_data
, kb_led
);
355 return sysfs_emit(buf
, "%06X\n", data
->kb_color
);
358 // Set the keyboard LED color
359 static ssize_t
kb_led_color_store(
361 struct device_attribute
*dev_attr
,
365 struct led_classdev
*led
;
366 struct system76_data
*data
;
370 led
= dev_get_drvdata(dev
);
371 data
= container_of(led
, struct system76_data
, kb_led
);
372 ret
= kstrtouint(buf
, 16, &val
);
377 data
->kb_color
= (int)val
;
378 system76_set(data
, "SKBC", data
->kb_color
);
383 static struct device_attribute dev_attr_kb_led_color
= {
388 .show
= kb_led_color_show
,
389 .store
= kb_led_color_store
,
392 static struct attribute
*system76_kb_led_color_attrs
[] = {
393 &dev_attr_kb_led_color
.attr
,
397 ATTRIBUTE_GROUPS(system76_kb_led_color
);
399 // Notify that the keyboard LED was changed by hardware
400 static void kb_led_notify(struct system76_data
*data
)
402 led_classdev_notify_brightness_hw_changed(
408 // Read keyboard LED brightness as set by hardware
409 static void kb_led_hotkey_hardware(struct system76_data
*data
)
413 if (acpi_has_method(acpi_device_handle(data
->acpi_dev
), "GKBK")) {
414 value
= system76_get(data
, "GKBB");
416 value
= system76_get(data
, "GKBL");
421 data
->kb_brightness
= value
;
425 // Toggle the keyboard LED
426 static void kb_led_hotkey_toggle(struct system76_data
*data
)
428 if (data
->kb_brightness
> 0) {
429 data
->kb_toggle_brightness
= data
->kb_brightness
;
430 kb_led_set(&data
->kb_led
, 0);
432 kb_led_set(&data
->kb_led
, data
->kb_toggle_brightness
);
437 // Decrease the keyboard LED brightness
438 static void kb_led_hotkey_down(struct system76_data
*data
)
442 if (data
->kb_brightness
> 0) {
443 for (i
= ARRAY_SIZE(kb_levels
); i
> 0; i
--) {
444 if (kb_levels
[i
- 1] < data
->kb_brightness
) {
445 kb_led_set(&data
->kb_led
, kb_levels
[i
- 1]);
450 kb_led_set(&data
->kb_led
, data
->kb_toggle_brightness
);
455 // Increase the keyboard LED brightness
456 static void kb_led_hotkey_up(struct system76_data
*data
)
460 if (data
->kb_brightness
> 0) {
461 for (i
= 0; i
< ARRAY_SIZE(kb_levels
); i
++) {
462 if (kb_levels
[i
] > data
->kb_brightness
) {
463 kb_led_set(&data
->kb_led
, kb_levels
[i
]);
468 kb_led_set(&data
->kb_led
, data
->kb_toggle_brightness
);
473 // Cycle the keyboard LED color
474 static void kb_led_hotkey_color(struct system76_data
*data
)
478 if (data
->kbled_type
!= KBLED_RGB
)
481 if (data
->kb_brightness
> 0) {
482 for (i
= 0; i
< ARRAY_SIZE(kb_colors
); i
++) {
483 if (kb_colors
[i
] == data
->kb_color
)
487 if (i
>= ARRAY_SIZE(kb_colors
))
489 data
->kb_color
= kb_colors
[i
];
490 system76_set(data
, "SKBC", data
->kb_color
);
492 kb_led_set(&data
->kb_led
, data
->kb_toggle_brightness
);
497 static umode_t
thermal_is_visible(const void *drvdata
, enum hwmon_sensor_types type
,
498 u32 attr
, int channel
)
500 const struct system76_data
*data
= drvdata
;
505 if (system76_name(data
->nfan
, channel
))
510 if (system76_name(data
->ntmp
, channel
))
521 static int thermal_read(struct device
*dev
, enum hwmon_sensor_types type
, u32 attr
,
522 int channel
, long *val
)
524 struct system76_data
*data
= dev_get_drvdata(dev
);
529 if (attr
== hwmon_fan_input
) {
530 raw
= system76_get_index(data
, "GFAN", channel
);
533 *val
= (raw
>> 8) & 0xFFFF;
539 if (attr
== hwmon_pwm_input
) {
540 raw
= system76_get_index(data
, "GFAN", channel
);
549 if (attr
== hwmon_temp_input
) {
550 raw
= system76_get_index(data
, "GTMP", channel
);
565 static int thermal_read_string(struct device
*dev
, enum hwmon_sensor_types type
, u32 attr
,
566 int channel
, const char **str
)
568 struct system76_data
*data
= dev_get_drvdata(dev
);
572 if (attr
== hwmon_fan_label
) {
573 *str
= system76_name(data
->nfan
, channel
);
580 if (attr
== hwmon_temp_label
) {
581 *str
= system76_name(data
->ntmp
, channel
);
594 static const struct hwmon_ops thermal_ops
= {
595 .is_visible
= thermal_is_visible
,
596 .read
= thermal_read
,
597 .read_string
= thermal_read_string
,
600 // Allocate up to 8 fans and temperatures
601 static const struct hwmon_channel_info
* const thermal_channel_info
[] = {
602 HWMON_CHANNEL_INFO(fan
,
603 HWMON_F_INPUT
| HWMON_F_LABEL
,
604 HWMON_F_INPUT
| HWMON_F_LABEL
,
605 HWMON_F_INPUT
| HWMON_F_LABEL
,
606 HWMON_F_INPUT
| HWMON_F_LABEL
,
607 HWMON_F_INPUT
| HWMON_F_LABEL
,
608 HWMON_F_INPUT
| HWMON_F_LABEL
,
609 HWMON_F_INPUT
| HWMON_F_LABEL
,
610 HWMON_F_INPUT
| HWMON_F_LABEL
),
611 HWMON_CHANNEL_INFO(pwm
,
620 HWMON_CHANNEL_INFO(temp
,
621 HWMON_T_INPUT
| HWMON_T_LABEL
,
622 HWMON_T_INPUT
| HWMON_T_LABEL
,
623 HWMON_T_INPUT
| HWMON_T_LABEL
,
624 HWMON_T_INPUT
| HWMON_T_LABEL
,
625 HWMON_T_INPUT
| HWMON_T_LABEL
,
626 HWMON_T_INPUT
| HWMON_T_LABEL
,
627 HWMON_T_INPUT
| HWMON_T_LABEL
,
628 HWMON_T_INPUT
| HWMON_T_LABEL
),
632 static const struct hwmon_chip_info thermal_chip_info
= {
634 .info
= thermal_channel_info
,
637 static void input_key(struct system76_data
*data
, unsigned int code
)
639 input_report_key(data
->input
, code
, 1);
640 input_sync(data
->input
);
642 input_report_key(data
->input
, code
, 0);
643 input_sync(data
->input
);
646 // Handle ACPI notification
647 static void system76_notify(struct acpi_device
*acpi_dev
, u32 event
)
649 struct system76_data
*data
;
651 data
= acpi_driver_data(acpi_dev
);
654 kb_led_hotkey_hardware(data
);
657 kb_led_hotkey_toggle(data
);
660 kb_led_hotkey_down(data
);
663 kb_led_hotkey_up(data
);
666 kb_led_hotkey_color(data
);
669 input_key(data
, KEY_SCREENLOCK
);
674 // Add a System76 ACPI device
675 static int system76_add(struct acpi_device
*acpi_dev
)
677 struct system76_data
*data
;
680 data
= devm_kzalloc(&acpi_dev
->dev
, sizeof(*data
), GFP_KERNEL
);
683 acpi_dev
->driver_data
= data
;
684 data
->acpi_dev
= acpi_dev
;
686 // Some models do not run open EC firmware. Check for an ACPI method
687 // that only exists on open EC to guard functionality specific to it.
688 data
->has_open_ec
= acpi_has_method(acpi_device_handle(data
->acpi_dev
), "NFAN");
690 err
= system76_get(data
, "INIT");
693 data
->ap_led
.name
= "system76_acpi::airplane";
694 data
->ap_led
.flags
= LED_CORE_SUSPENDRESUME
;
695 data
->ap_led
.brightness_get
= ap_led_get
;
696 data
->ap_led
.brightness_set_blocking
= ap_led_set
;
697 data
->ap_led
.max_brightness
= 1;
698 data
->ap_led
.default_trigger
= "rfkill-none";
699 err
= devm_led_classdev_register(&acpi_dev
->dev
, &data
->ap_led
);
703 data
->kb_led
.name
= "system76_acpi::kbd_backlight";
704 data
->kb_led
.flags
= LED_BRIGHT_HW_CHANGED
| LED_CORE_SUSPENDRESUME
;
705 data
->kb_led
.brightness_get
= kb_led_get
;
706 data
->kb_led
.brightness_set_blocking
= kb_led_set
;
707 if (acpi_has_method(acpi_device_handle(data
->acpi_dev
), "GKBK")) {
708 // Use the new ACPI methods
709 data
->kbled_type
= system76_get(data
, "GKBK");
711 switch (data
->kbled_type
) {
713 // Nothing to do: Device will not be registered.
716 data
->kb_led
.max_brightness
= 255;
717 data
->kb_toggle_brightness
= 72;
720 data
->kb_led
.max_brightness
= 255;
721 data
->kb_led
.groups
= system76_kb_led_color_groups
;
722 data
->kb_toggle_brightness
= 72;
723 data
->kb_color
= 0xffffff;
724 system76_set(data
, "SKBC", data
->kb_color
);
728 // Use the old ACPI methods
729 if (acpi_has_method(acpi_device_handle(data
->acpi_dev
), "SKBC")) {
730 data
->kbled_type
= KBLED_RGB
;
731 data
->kb_led
.max_brightness
= 255;
732 data
->kb_led
.groups
= system76_kb_led_color_groups
;
733 data
->kb_toggle_brightness
= 72;
734 data
->kb_color
= 0xffffff;
735 system76_set(data
, "SKBC", data
->kb_color
);
737 data
->kbled_type
= KBLED_WHITE
;
738 data
->kb_led
.max_brightness
= 5;
742 if (data
->kbled_type
!= KBLED_NONE
) {
743 err
= devm_led_classdev_register(&acpi_dev
->dev
, &data
->kb_led
);
748 data
->input
= devm_input_allocate_device(&acpi_dev
->dev
);
752 data
->input
->name
= "System76 ACPI Hotkeys";
753 data
->input
->phys
= "system76_acpi/input0";
754 data
->input
->id
.bustype
= BUS_HOST
;
755 data
->input
->dev
.parent
= &acpi_dev
->dev
;
756 input_set_capability(data
->input
, EV_KEY
, KEY_SCREENLOCK
);
758 err
= input_register_device(data
->input
);
762 if (data
->has_open_ec
) {
763 err
= system76_get_object(data
, "NFAN", &data
->nfan
);
767 err
= system76_get_object(data
, "NTMP", &data
->ntmp
);
771 data
->therm
= devm_hwmon_device_register_with_info(&acpi_dev
->dev
,
772 "system76_acpi", data
, &thermal_chip_info
, NULL
);
773 err
= PTR_ERR_OR_ZERO(data
->therm
);
777 system76_battery_init();
783 if (data
->has_open_ec
) {
790 // Remove a System76 ACPI device
791 static void system76_remove(struct acpi_device
*acpi_dev
)
793 struct system76_data
*data
;
795 data
= acpi_driver_data(acpi_dev
);
797 if (data
->has_open_ec
) {
798 system76_battery_exit();
803 devm_led_classdev_unregister(&acpi_dev
->dev
, &data
->ap_led
);
804 devm_led_classdev_unregister(&acpi_dev
->dev
, &data
->kb_led
);
806 system76_get(data
, "FINI");
809 static struct acpi_driver system76_driver
= {
810 .name
= "System76 ACPI Driver",
815 .remove
= system76_remove
,
816 .notify
= system76_notify
,
819 module_acpi_driver(system76_driver
);
821 MODULE_DESCRIPTION("System76 ACPI Driver");
822 MODULE_AUTHOR("Jeremy Soller <jeremy@system76.com>");
823 MODULE_LICENSE("GPL");