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) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  * Asus Wireless Radio Control Driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  * Copyright (C) 2015-2016 Endless Mobile, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11) #include <linux/types.h>
^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/input.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14) #include <linux/pci_ids.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15) #include <linux/leds.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17) struct hswc_params {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) 	u8 on;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) 	u8 off;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) 	u8 status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23) struct asus_wireless_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24) 	struct input_dev *idev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25) 	struct acpi_device *adev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) 	const struct hswc_params *hswc_params;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) 	struct workqueue_struct *wq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) 	struct work_struct led_work;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) 	struct led_classdev led;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) 	int led_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) static const struct hswc_params atk4001_id_params = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) 	.on = 0x0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) 	.off = 0x1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) 	.status = 0x2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) static const struct hswc_params atk4002_id_params = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) 	.on = 0x5,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) 	.off = 0x4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) 	.status = 0x2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) static const struct acpi_device_id device_ids[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) 	{"ATK4001", (kernel_ulong_t)&atk4001_id_params},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) 	{"ATK4002", (kernel_ulong_t)&atk4002_id_params},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) 	{"", 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) MODULE_DEVICE_TABLE(acpi, device_ids);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) static acpi_status asus_wireless_method(acpi_handle handle, const char *method,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) 					int param, u64 *ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) 	struct acpi_object_list p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) 	union acpi_object obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) 	acpi_status s;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) 	acpi_handle_debug(handle, "Evaluating method %s, parameter %#x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) 			  method, param);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) 	obj.type = ACPI_TYPE_INTEGER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) 	obj.integer.value = param;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) 	p.count = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) 	p.pointer = &obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) 	s = acpi_evaluate_integer(handle, (acpi_string) method, &p, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 	if (ACPI_FAILURE(s))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) 		acpi_handle_err(handle,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 				"Failed to eval method %s, param %#x (%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 				method, param, s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 		acpi_handle_debug(handle, "%s returned %#llx\n", method, *ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 	return s;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) static enum led_brightness led_state_get(struct led_classdev *led)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 	struct asus_wireless_data *data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) 	acpi_status s;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 	u64 ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 	data = container_of(led, struct asus_wireless_data, led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) 	s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 				 data->hswc_params->status, &ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) 	if (ACPI_SUCCESS(s) && ret == data->hswc_params->on)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) 		return LED_FULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) 	return LED_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) static void led_state_update(struct work_struct *work)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 	struct asus_wireless_data *data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 	u64 ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 	data = container_of(work, struct asus_wireless_data, led_work);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 	asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 			     data->led_state, &ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) static void led_state_set(struct led_classdev *led, enum led_brightness value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 	struct asus_wireless_data *data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 	data = container_of(led, struct asus_wireless_data, led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 	data->led_state = value == LED_OFF ? data->hswc_params->off :
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 					     data->hswc_params->on;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 	queue_work(data->wq, &data->led_work);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) static void asus_wireless_notify(struct acpi_device *adev, u32 event)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 	struct asus_wireless_data *data = acpi_driver_data(adev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) 	dev_dbg(&adev->dev, "event=%#x\n", event);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 	if (event != 0x88) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) 		dev_notice(&adev->dev, "Unknown ASHS event: %#x\n", event);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 		return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 	input_report_key(data->idev, KEY_RFKILL, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 	input_sync(data->idev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 	input_report_key(data->idev, KEY_RFKILL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 	input_sync(data->idev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) static int asus_wireless_add(struct acpi_device *adev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) 	struct asus_wireless_data *data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) 	const struct acpi_device_id *id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 	int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 	data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 	if (!data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 		return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 	adev->driver_data = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 	data->adev = adev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 	data->idev = devm_input_allocate_device(&adev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 	if (!data->idev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 		return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 	data->idev->name = "Asus Wireless Radio Control";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 	data->idev->phys = "asus-wireless/input0";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 	data->idev->id.bustype = BUS_HOST;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 	data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 	set_bit(EV_KEY, data->idev->evbit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 	set_bit(KEY_RFKILL, data->idev->keybit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 	err = input_register_device(data->idev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 	if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 		return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 	for (id = device_ids; id->id[0]; id++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 		if (!strcmp((char *) id->id, acpi_device_hid(adev))) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 			data->hswc_params =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 				(const struct hswc_params *)id->driver_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 			break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 	if (!data->hswc_params)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 	data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 	if (!data->wq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 		return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 	INIT_WORK(&data->led_work, led_state_update);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 	data->led.name = "asus-wireless::airplane";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 	data->led.brightness_set = led_state_set;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 	data->led.brightness_get = led_state_get;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 	data->led.flags = LED_CORE_SUSPENDRESUME;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 	data->led.max_brightness = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 	data->led.default_trigger = "rfkill-none";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) 	err = devm_led_classdev_register(&adev->dev, &data->led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 	if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 		destroy_workqueue(data->wq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 	return err;
^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 int asus_wireless_remove(struct acpi_device *adev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 	struct asus_wireless_data *data = acpi_driver_data(adev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 	if (data->wq) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 		devm_led_classdev_unregister(&adev->dev, &data->led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 		destroy_workqueue(data->wq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) static struct acpi_driver asus_wireless_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 	.name = "Asus Wireless Radio Control Driver",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 	.class = "hotkey",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 	.ids = device_ids,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 	.ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 		.add = asus_wireless_add,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 		.remove = asus_wireless_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 		.notify = asus_wireless_notify,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 	},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) module_acpi_driver(asus_wireless_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) MODULE_DESCRIPTION("Asus Wireless Radio Control Driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) MODULE_AUTHOR("João Paulo Rechi Vita <jprvita@gmail.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) MODULE_LICENSE("GPL");