Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2)  * Copyright (C) 2010 Dell Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  * Louis Davis <louis_davis@dell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  * Jim Dailey <jim_dailey@dell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  * This program is free software; you can redistribute it and/or modify
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7)  * it under the terms of the GNU General Public License as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8)  * published by the Free Software Foundation.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12) #include <linux/acpi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13) #include <linux/leds.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17) MODULE_AUTHOR("Louis Davis/Jim Dailey");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) MODULE_DESCRIPTION("Dell LED Control Driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) #define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24) /* Error Result Codes: */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25) #define INVALID_DEVICE_ID	250
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) #define INVALID_PARAMETER	251
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) #define INVALID_BUFFER		252
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) #define INTERFACE_ERROR		253
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) #define UNSUPPORTED_COMMAND	254
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) #define UNSPECIFIED_ERROR	255
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) /* Device ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) #define DEVICE_ID_PANEL_BACK	1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) /* LED Commands */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) #define CMD_LED_ON	16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) #define CMD_LED_OFF	17
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) #define CMD_LED_BLINK	18
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) struct bios_args {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) 	unsigned char length;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) 	unsigned char result_code;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) 	unsigned char device_id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) 	unsigned char command;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) 	unsigned char on_time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) 	unsigned char off_time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) static int dell_led_perform_fn(u8 length, u8 result_code, u8 device_id,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) 			       u8 command, u8 on_time, u8 off_time)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) 	struct bios_args *bios_return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) 	struct acpi_buffer input;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) 	union acpi_object *obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) 	acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) 	u8 return_code;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) 	struct bios_args args = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) 		.length = length,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) 		.result_code = result_code,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) 		.device_id = device_id,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) 		.command = command,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) 		.on_time = on_time,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) 		.off_time = off_time
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) 	};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) 	input.length = sizeof(struct bios_args);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 	input.pointer = &args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 	status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 0, 1, &input, &output);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 	if (ACPI_FAILURE(status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 		return status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 	obj = output.pointer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 	if (!obj)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 	if (obj->type != ACPI_TYPE_BUFFER) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) 		kfree(obj);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) 	bios_return = ((struct bios_args *)obj->buffer.pointer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 	return_code = bios_return->result_code;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) 	kfree(obj);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 	return return_code;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) static int led_on(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 	return dell_led_perform_fn(3,	/* Length of command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 		INTERFACE_ERROR,	/* Init to  INTERFACE_ERROR */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 		DEVICE_ID_PANEL_BACK,	/* Device ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 		CMD_LED_ON,		/* Command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 		0,			/* not used */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) 		0);			/* not used */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) static int led_off(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 	return dell_led_perform_fn(3,	/* Length of command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 		INTERFACE_ERROR,	/* Init to  INTERFACE_ERROR */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 		DEVICE_ID_PANEL_BACK,	/* Device ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 		CMD_LED_OFF,		/* Command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 		0,			/* not used */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 		0);			/* not used */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) static int led_blink(unsigned char on_eighths, unsigned char off_eighths)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 	return dell_led_perform_fn(5,	/* Length of command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) 		INTERFACE_ERROR,	/* Init to  INTERFACE_ERROR */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 		DEVICE_ID_PANEL_BACK,	/* Device ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) 		CMD_LED_BLINK,		/* Command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 		on_eighths,		/* blink on in eigths of a second */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) 		off_eighths);		/* blink off in eights of a second */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) static void dell_led_set(struct led_classdev *led_cdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 			 enum led_brightness value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	if (value == LED_OFF)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) 		led_off();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) 		led_on();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) static int dell_led_blink(struct led_classdev *led_cdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 			  unsigned long *delay_on, unsigned long *delay_off)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 	unsigned long on_eighths;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 	unsigned long off_eighths;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 	 * The Dell LED delay is based on 125ms intervals.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 	 * Need to round up to next interval.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 	on_eighths = DIV_ROUND_UP(*delay_on, 125);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 	on_eighths = clamp_t(unsigned long, on_eighths, 1, 255);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 	*delay_on = on_eighths * 125;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 	off_eighths = DIV_ROUND_UP(*delay_off, 125);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 	off_eighths = clamp_t(unsigned long, off_eighths, 1, 255);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 	*delay_off = off_eighths * 125;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 	led_blink(on_eighths, off_eighths);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) static struct led_classdev dell_led = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	.name		= "dell::lid",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 	.brightness	= LED_OFF,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 	.max_brightness = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 	.brightness_set = dell_led_set,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 	.blink_set	= dell_led_blink,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 	.flags		= LED_CORE_SUSPENDRESUME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) static int __init dell_led_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 	int error = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 	if (!wmi_has_guid(DELL_LED_BIOS_GUID))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 		return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) 	error = led_off();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 	if (error != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 		return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 	return led_classdev_register(NULL, &dell_led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) static void __exit dell_led_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 	led_classdev_unregister(&dell_led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 	led_off();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) module_init(dell_led_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) module_exit(dell_led_exit);