All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] LoongArch: Add CPU HWMon platform driver
@ 2022-08-15 12:48 Huacai Chen
  2022-08-15 12:48 ` [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver Huacai Chen
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Huacai Chen @ 2022-08-15 12:48 UTC (permalink / raw)
  To: Arnd Bergmann, Huacai Chen, Rafael J . Wysocki, Len Brown,
	Robert Moore, Erik Kaneda
  Cc: loongarch, linux-arch, linux-acpi, Xuefeng Li, Jianmin Lv,
	Jiaxun Yang, Huacai Chen

This add CPU HWMon (temperature sensor) platform driver for Loongson-3.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/platform/Kconfig               |   3 +
 drivers/platform/Makefile              |   1 +
 drivers/platform/loongarch/Kconfig     |  26 ++++
 drivers/platform/loongarch/Makefile    |   1 +
 drivers/platform/loongarch/cpu_hwmon.c | 195 +++++++++++++++++++++++++
 5 files changed, 226 insertions(+)
 create mode 100644 drivers/platform/loongarch/Kconfig
 create mode 100644 drivers/platform/loongarch/Makefile
 create mode 100644 drivers/platform/loongarch/cpu_hwmon.c

diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index b437847b6237..9c68e2def2cb 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -2,6 +2,9 @@
 if MIPS
 source "drivers/platform/mips/Kconfig"
 endif
+if LOONGARCH
+source "drivers/platform/loongarch/Kconfig"
+endif
 
 source "drivers/platform/goldfish/Kconfig"
 
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index 4de08ef4ec9d..41640172975a 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -4,6 +4,7 @@
 #
 
 obj-$(CONFIG_X86)		+= x86/
+obj-$(CONFIG_LOONGARCH)		+= loongarch/
 obj-$(CONFIG_MELLANOX_PLATFORM)	+= mellanox/
 obj-$(CONFIG_MIPS)		+= mips/
 obj-$(CONFIG_OLPC_EC)		+= olpc/
diff --git a/drivers/platform/loongarch/Kconfig b/drivers/platform/loongarch/Kconfig
new file mode 100644
index 000000000000..a1542843b0ad
--- /dev/null
+++ b/drivers/platform/loongarch/Kconfig
@@ -0,0 +1,26 @@
+#
+# LoongArch Platform Specific Drivers
+#
+
+menuconfig LOONGARCH_PLATFORM_DEVICES
+	bool "LoongArch Platform Specific Device Drivers"
+	default LOONGARCH
+	help
+	  Say Y here to get to see options for device drivers of various
+	  LoongArch platforms, including vendor-specific laptop/desktop
+	  extension and hardware monitor drivers. This option itself does
+	  not add any kernel code.
+
+	  If you say N, all options in this submenu will be skipped and disabled.
+
+if LOONGARCH_PLATFORM_DEVICES
+
+config CPU_HWMON
+	bool "Loongson CPU HWMon Driver"
+	depends on MACH_LOONGSON64
+	select HWMON
+	default y
+	help
+	  Loongson-3A/3B/3C CPU HWMon (temperature sensor) driver.
+
+endif # LOONGARCH_PLATFORM_DEVICES
diff --git a/drivers/platform/loongarch/Makefile b/drivers/platform/loongarch/Makefile
new file mode 100644
index 000000000000..8dfd03924c37
--- /dev/null
+++ b/drivers/platform/loongarch/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o
diff --git a/drivers/platform/loongarch/cpu_hwmon.c b/drivers/platform/loongarch/cpu_hwmon.c
new file mode 100644
index 000000000000..3673c850f66c
--- /dev/null
+++ b/drivers/platform/loongarch/cpu_hwmon.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Loongson Technology Corporation Limited
+ */
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <asm/loongson.h>
+
+int loongson3_cpu_temp(int cpu)
+{
+	u32 reg;
+
+	reg = iocsr_read32(LOONGARCH_IOCSR_CPUTEMP) & 0xff;
+
+	return (int)((s8)reg) * 1000;
+}
+EXPORT_SYMBOL(loongson3_cpu_temp);
+
+static int nr_packages;
+static struct device *cpu_hwmon_dev;
+
+static ssize_t cpu_temp_label(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int id = (to_sensor_dev_attr(attr))->index - 1;
+	return sprintf(buf, "CPU %d Temperature\n", id);
+}
+
+static ssize_t get_cpu_temp(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int id = (to_sensor_dev_attr(attr))->index - 1;
+	int value = loongson3_cpu_temp(id);
+	return sprintf(buf, "%d\n", value);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, 0444, get_cpu_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_label, 0444, cpu_temp_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input, 0444, get_cpu_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_label, 0444, cpu_temp_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_input, 0444, get_cpu_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_label, 0444, cpu_temp_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp5_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp5_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp7_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp7_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp8_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp8_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp9_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp9_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp10_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp10_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp11_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp11_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp12_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp12_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp13_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp13_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp14_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp14_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp15_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp15_label, 0444, cpu_temp_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp16_input, 0444, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp16_label, 0444, cpu_temp_label, NULL, 4);
+
+static struct attribute *cpu_hwmon_attributes[] = {
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&sensor_dev_attr_temp1_label.dev_attr.attr,
+	&sensor_dev_attr_temp2_input.dev_attr.attr,
+	&sensor_dev_attr_temp2_label.dev_attr.attr,
+	&sensor_dev_attr_temp3_input.dev_attr.attr,
+	&sensor_dev_attr_temp3_label.dev_attr.attr,
+	&sensor_dev_attr_temp4_input.dev_attr.attr,
+	&sensor_dev_attr_temp4_label.dev_attr.attr,
+	&sensor_dev_attr_temp5_input.dev_attr.attr,
+	&sensor_dev_attr_temp5_label.dev_attr.attr,
+	&sensor_dev_attr_temp6_input.dev_attr.attr,
+	&sensor_dev_attr_temp6_label.dev_attr.attr,
+	&sensor_dev_attr_temp7_input.dev_attr.attr,
+	&sensor_dev_attr_temp7_label.dev_attr.attr,
+	&sensor_dev_attr_temp8_input.dev_attr.attr,
+	&sensor_dev_attr_temp8_label.dev_attr.attr,
+	&sensor_dev_attr_temp9_input.dev_attr.attr,
+	&sensor_dev_attr_temp9_label.dev_attr.attr,
+	&sensor_dev_attr_temp10_input.dev_attr.attr,
+	&sensor_dev_attr_temp10_label.dev_attr.attr,
+	&sensor_dev_attr_temp11_input.dev_attr.attr,
+	&sensor_dev_attr_temp11_label.dev_attr.attr,
+	&sensor_dev_attr_temp12_input.dev_attr.attr,
+	&sensor_dev_attr_temp12_label.dev_attr.attr,
+	&sensor_dev_attr_temp13_input.dev_attr.attr,
+	&sensor_dev_attr_temp13_label.dev_attr.attr,
+	&sensor_dev_attr_temp14_input.dev_attr.attr,
+	&sensor_dev_attr_temp14_label.dev_attr.attr,
+	&sensor_dev_attr_temp15_input.dev_attr.attr,
+	&sensor_dev_attr_temp15_label.dev_attr.attr,
+	&sensor_dev_attr_temp16_input.dev_attr.attr,
+	&sensor_dev_attr_temp16_label.dev_attr.attr,
+	NULL
+};
+static umode_t cpu_hwmon_is_visible(struct kobject *kobj,
+				    struct attribute *attr, int i)
+{
+	int id = i / 2;
+
+	if (id < nr_packages)
+		return attr->mode;
+	return 0;
+}
+
+static struct attribute_group cpu_hwmon_group = {
+	.attrs = cpu_hwmon_attributes,
+	.is_visible = cpu_hwmon_is_visible,
+};
+
+static const struct attribute_group *cpu_hwmon_groups[] = {
+	&cpu_hwmon_group,
+	NULL
+};
+
+static int cpu_initial_threshold = 72000;
+static int cpu_thermal_threshold = 96000;
+module_param(cpu_thermal_threshold, int, 0644);
+MODULE_PARM_DESC(cpu_thermal_threshold, "cpu thermal threshold (96000 (default))");
+
+static struct delayed_work thermal_work;
+
+static void do_thermal_timer(struct work_struct *work)
+{
+	int i, value, temp_max = 0;
+
+	for (i=0; i<nr_packages; i++) {
+		value = loongson3_cpu_temp(i);
+		if (value > temp_max)
+			temp_max = value;
+	}
+
+	if (temp_max <= cpu_thermal_threshold)
+		schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000));
+	else
+		orderly_poweroff(true);
+}
+
+static int __init loongson_hwmon_init(void)
+{
+	int i, value, temp_max = 0;
+
+	pr_info("Loongson Hwmon Enter...\n");
+
+	nr_packages = loongson_sysconf.nr_cpus /
+		loongson_sysconf.cores_per_package;
+
+	cpu_hwmon_dev = hwmon_device_register_with_groups(NULL, "cpu_hwmon",
+							  NULL, cpu_hwmon_groups);
+	if (IS_ERR(cpu_hwmon_dev)) {
+		pr_err("hwmon_device_register fail!\n");
+		return PTR_ERR(cpu_hwmon_dev);
+	}
+
+	for (i = 0; i < nr_packages; i++) {
+		value = loongson3_cpu_temp(i);
+		if (value > temp_max)
+			temp_max = value;
+	}
+
+	pr_info("Initial CPU temperature is %d (highest).\n", temp_max);
+	if (temp_max > cpu_initial_threshold)
+		cpu_thermal_threshold += temp_max - cpu_initial_threshold;
+
+	INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer);
+	schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000));
+
+	return 0;
+}
+
+static void __exit loongson_hwmon_exit(void)
+{
+	cancel_delayed_work_sync(&thermal_work);
+	hwmon_device_unregister(cpu_hwmon_dev);
+}
+
+module_init(loongson_hwmon_init);
+module_exit(loongson_hwmon_exit);
+
+MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>");
+MODULE_DESCRIPTION("Loongson CPU Hwmon driver");
+MODULE_LICENSE("GPL");
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver
  2022-08-15 12:48 [PATCH 1/2] LoongArch: Add CPU HWMon platform driver Huacai Chen
@ 2022-08-15 12:48 ` Huacai Chen
  2022-08-15 18:40   ` kernel test robot
  2022-08-15 19:11   ` Arnd Bergmann
  2022-08-15 17:58 ` [PATCH 1/2] LoongArch: Add CPU HWMon platform driver kernel test robot
  2022-08-16  6:59 ` Xi Ruoyao
  2 siblings, 2 replies; 8+ messages in thread
From: Huacai Chen @ 2022-08-15 12:48 UTC (permalink / raw)
  To: Arnd Bergmann, Huacai Chen, Rafael J . Wysocki, Len Brown,
	Robert Moore, Erik Kaneda
  Cc: loongarch, linux-arch, linux-acpi, Xuefeng Li, Jianmin Lv,
	Jiaxun Yang, Huacai Chen

From: Jianmin Lv <lvjianmin@loongson.cn>

This add ACPI-based generic laptop driver for Loongson-3. Some of the
codes are derived from drivers/platform/x86/thinkpad_acpi.c.

Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/platform/loongarch/Kconfig          |  18 +
 drivers/platform/loongarch/Makefile         |   1 +
 drivers/platform/loongarch/generic-laptop.c | 775 ++++++++++++++++++++
 3 files changed, 794 insertions(+)
 create mode 100644 drivers/platform/loongarch/generic-laptop.c

diff --git a/drivers/platform/loongarch/Kconfig b/drivers/platform/loongarch/Kconfig
index a1542843b0ad..086212d57251 100644
--- a/drivers/platform/loongarch/Kconfig
+++ b/drivers/platform/loongarch/Kconfig
@@ -23,4 +23,22 @@ config CPU_HWMON
 	help
 	  Loongson-3A/3B/3C CPU HWMon (temperature sensor) driver.
 
+config GENERIC_LAPTOP
+	tristate "Generic Loongson-3A Laptop Driver"
+	depends on MACH_LOONGSON64
+	depends on ACPI
+	depends on INPUT
+	select BACKLIGHT_CLASS_DEVICE
+	select BACKLIGHT_LCD_SUPPORT
+	select HWMON
+	select INPUT_EVDEV
+	select INPUT_SPARSEKMAP
+	select LCD_CLASS_DEVICE
+	select LEDS_CLASS
+	select POWER_SUPPLY
+	select VIDEO_OUTPUT_CONTROL
+	default y
+	help
+	  ACPI-based Loongson-3 family laptops generic driver.
+
 endif # LOONGARCH_PLATFORM_DEVICES
diff --git a/drivers/platform/loongarch/Makefile b/drivers/platform/loongarch/Makefile
index 8dfd03924c37..9d6f69f2319d 100644
--- a/drivers/platform/loongarch/Makefile
+++ b/drivers/platform/loongarch/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o
+obj-$(CONFIG_GENERIC_LAPTOP) += generic-laptop.o
diff --git a/drivers/platform/loongarch/generic-laptop.c b/drivers/platform/loongarch/generic-laptop.c
new file mode 100644
index 000000000000..90e37a02511d
--- /dev/null
+++ b/drivers/platform/loongarch/generic-laptop.c
@@ -0,0 +1,775 @@
+/*
+ *  Generic Loongson processor based LAPTOP/ALL-IN-ONE driver
+ *
+ *  Jianmin Lv <lvjianmin@loongson.cn>
+ *  Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2022 Loongson Technology Corporation Limited
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/acpi.h>
+#include <linux/uaccess.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/device.h>
+#include <linux/backlight.h>
+#include <acpi/video.h>
+
+/* ACPI HIDs */
+#define LOONGSON_ACPI_HKEY_HID	"LOON0000"
+#define LOONGSON_ACPI_EC_HID	"PNP0C09"
+
+/****************************************************************************
+ * Main driver
+ */
+
+#define ACPI_LAPTOP_VERSION "1.0"
+#define ACPI_LAPTOP_NAME "loongson-laptop"
+#define ACPI_LAPTOP_DESC "Loongson Laptop/all-in-one ACPI Driver"
+#define ACPI_LAPTOP_FILE ACPI_LAPTOP_NAME "_acpi"
+#define ACPI_LAPTOP_DRVR_NAME ACPI_LAPTOP_FILE
+#define ACPI_LAPTOP_ACPI_EVENT_PREFIX "loongson"
+/****************************************************************************
+ * Driver-wide structs and misc. variables
+ */
+
+struct generic_struct;
+
+struct generic_acpi_drv_struct {
+	u32 type;
+	acpi_handle *handle;
+	const struct acpi_device_id *hid;
+	struct acpi_device *device;
+	struct acpi_driver *driver;
+	void (*notify)(struct generic_struct *, u32);
+};
+
+struct generic_struct {
+	char *name;
+
+	int (*init)(struct generic_struct *);
+
+	struct generic_acpi_drv_struct *acpi;
+
+	struct {
+		u8 acpi_driver_registered;
+		u8 acpi_notify_installed;
+	} flags;
+};
+
+
+static struct {
+	u32 input_device_registered:1;
+} generic_features;
+
+static int hotkey_status_get(int *status);
+static int loongson_laptop_backlight_update(struct backlight_device *bd);
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * ACPI Helpers and device model
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/* ACPI basic handles */
+
+static int acpi_evalf(acpi_handle handle,
+		      int *res, char *method, char *fmt, ...);
+static acpi_handle ec_handle;
+
+#define GENERIC_HANDLE(object, parent, paths...)			\
+	static acpi_handle  object##_handle;			\
+	static const acpi_handle * const object##_parent __initconst =	\
+						&parent##_handle; \
+	static char *object##_paths[] __initdata = { paths }
+
+GENERIC_HANDLE(hkey, ec, "\\_SB.HKEY", "^HKEY", "HKEY",);
+
+/* ACPI device model */
+
+#define GENERIC_ACPIHANDLE_INIT(object) \
+	drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
+		object##_paths, ARRAY_SIZE(object##_paths))
+
+static void __init drv_acpi_handle_init(const char *name,
+			   acpi_handle *handle, const acpi_handle parent,
+			   char **paths, const int num_paths)
+{
+	int i;
+	acpi_status status;
+
+	for (i = 0; i < num_paths; i++) {
+		status = acpi_get_handle(parent, paths[i], handle);
+		if (ACPI_SUCCESS(status))
+			return;
+	}
+
+	*handle = NULL;
+}
+static acpi_status __init generic_acpi_handle_locate_callback(acpi_handle handle,
+					u32 level, void *context, void **return_value)
+{
+	*(acpi_handle *)return_value = handle;
+
+	return AE_CTRL_TERMINATE;
+}
+
+static void __init generic_acpi_handle_locate(const char *name,
+		const char *hid, acpi_handle *handle)
+{
+	acpi_status status;
+	acpi_handle device_found;
+
+	BUG_ON(!name || !hid || !handle);
+
+	*handle = NULL;
+
+	memset(&device_found, 0, sizeof(device_found));
+	status = acpi_get_devices(hid, generic_acpi_handle_locate_callback,
+				  (void *)name, &device_found);
+
+	if (ACPI_SUCCESS(status))
+		*handle = device_found;
+}
+
+static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct generic_struct *sub_driver = data;
+
+	if (!sub_driver || !sub_driver->acpi || !sub_driver->acpi->notify)
+		return;
+	sub_driver->acpi->notify(sub_driver, event);
+}
+
+static int __init setup_acpi_notify(struct generic_struct *sub_driver)
+{
+	acpi_status status;
+
+	BUG_ON(!sub_driver->acpi);
+
+	if (!*sub_driver->acpi->handle)
+		return 0;
+
+	sub_driver->acpi->device = acpi_fetch_acpi_dev(*sub_driver->acpi->handle);
+	if (!sub_driver->acpi->device) {
+		pr_err("acpi_fetch_acpi_dev(%s) failed\n", sub_driver->name);
+		return -ENODEV;
+	}
+
+	sub_driver->acpi->device->driver_data = sub_driver;
+	sprintf(acpi_device_class(sub_driver->acpi->device), "%s/%s",
+		ACPI_LAPTOP_ACPI_EVENT_PREFIX,
+		sub_driver->name);
+
+	status = acpi_install_notify_handler(*sub_driver->acpi->handle,
+			sub_driver->acpi->type, dispatch_acpi_notify, sub_driver);
+	if (ACPI_FAILURE(status)) {
+		if (status == AE_ALREADY_EXISTS) {
+			pr_notice("another device driver is already "
+				  "handling %s events\n", sub_driver->name);
+		} else {
+			pr_err("acpi_install_notify_handler(%s) failed: %s\n",
+			       sub_driver->name, acpi_format_exception(status));
+		}
+		return -ENODEV;
+	}
+	sub_driver->flags.acpi_notify_installed = 1;
+	return 0;
+}
+
+static int __init tpacpi_device_add(struct acpi_device *device)
+{
+	return 0;
+}
+
+static struct input_dev *generic_inputdev;
+
+#ifdef CONFIG_PM
+static int loongson_generic_suspend(struct device *dev)
+{
+	return 0;
+}
+static int loongson_generic_resume(struct device *dev)
+{
+	int status = 0;
+	struct key_entry ke;
+	struct backlight_device *bd;
+
+	/*
+	 * Only if the firmware supports SW_LID event model, we can handle the
+	 * event. This is for the consideration of development board without
+	 * EC.
+	 */
+	if (test_bit(SW_LID, generic_inputdev->swbit)) {
+		if (hotkey_status_get(&status))
+			return -EIO;
+		/*
+		 * The input device sw element records the last lid status.
+		 * When the system is awakened by other wake-up sources,
+		 * the lid event will also be reported. The judgment of
+		 * adding SW_LID bit which in sw element can avoid this
+		 * case.
+		 *
+		 * input system will drop lid event when current lid event
+		 * value and last lid status in the same data set,which
+		 * data set inclue zero set and no zero set. so laptop
+		 * driver doesn't report repeated events.
+		 *
+		 * Lid status is generally 0, but hardware exception is
+		 * considered. So add lid status confirmation.
+		 */
+		if (test_bit(SW_LID, generic_inputdev->sw) && !(status & (1 << SW_LID))) {
+			ke.type = KE_SW;
+			ke.sw.value = (u8)status;
+			ke.sw.code = SW_LID;
+			sparse_keymap_report_entry(generic_inputdev, &ke,
+					1, true);
+		}
+	}
+
+	bd = backlight_device_get_by_type(BACKLIGHT_PLATFORM);
+	if (bd) {
+		loongson_laptop_backlight_update(bd) ?
+		pr_warn("Loongson_backlight:resume brightness failed") :
+		pr_info("Loongson_backlight:resume brightness %d\n", bd->props.brightness);
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(loongson_generic_pm,
+		loongson_generic_suspend, loongson_generic_resume);
+#endif
+
+static int __init register_generic_subdriver(struct generic_struct *sub_driver)
+{
+	int rc;
+
+	BUG_ON(!sub_driver->acpi);
+
+	sub_driver->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
+	if (!sub_driver->acpi->driver) {
+		pr_err("failed to allocate memory for ibm->acpi->driver\n");
+		return -ENOMEM;
+	}
+
+	sprintf(sub_driver->acpi->driver->name, "%s_%s", ACPI_LAPTOP_NAME, sub_driver->name);
+	sub_driver->acpi->driver->ids = sub_driver->acpi->hid;
+	sub_driver->acpi->driver->ops.add = &tpacpi_device_add;
+#ifdef CONFIG_PM
+	sub_driver->acpi->driver->drv.pm = &loongson_generic_pm;
+#endif
+	rc = acpi_bus_register_driver(sub_driver->acpi->driver);
+	if (rc < 0) {
+		pr_err("acpi_bus_register_driver(%s) failed: %d\n",
+		       sub_driver->name, rc);
+		kfree(sub_driver->acpi->driver);
+		sub_driver->acpi->driver = NULL;
+	} else if (!rc)
+		sub_driver->flags.acpi_driver_registered = 1;
+
+	return rc;
+}
+
+/* Loongson generic laptop firmware event model */
+
+#define GENERIC_HOTKEY_MAP_MAX	64
+#define METHOD_NAME__KMAP	"KMAP"
+static struct key_entry hotkey_keycode_map[GENERIC_HOTKEY_MAP_MAX];
+
+static int hkey_map(void)
+{
+	u32 index;
+	acpi_status status;
+	struct acpi_buffer buf;
+	union acpi_object *pack;
+
+	buf.length = ACPI_ALLOCATE_BUFFER;
+	status = acpi_evaluate_object_typed(hkey_handle, METHOD_NAME__KMAP, NULL, &buf, ACPI_TYPE_PACKAGE);
+	if (status != AE_OK) {
+		printk(KERN_ERR ": ACPI exception: %s\n",
+				acpi_format_exception(status));
+		return -1;
+	}
+	pack = buf.pointer;
+	for (index = 0; index < pack->package.count; index++) {
+		union acpi_object *sub_pack = &pack->package.elements[index];
+		union acpi_object *element = &sub_pack->package.elements[0];
+
+		hotkey_keycode_map[index].type = element->integer.value;
+		element = &sub_pack->package.elements[1];
+		hotkey_keycode_map[index].code = element->integer.value;
+		element = &sub_pack->package.elements[2];
+		hotkey_keycode_map[index].keycode = element->integer.value;
+	}
+
+	return 0;
+}
+
+static int hotkey_backlight_set(bool enable)
+{
+	if (!acpi_evalf(hkey_handle, NULL, "VCBL", "vd", enable ? 1 : 0))
+		return -EIO;
+
+	return 0;
+}
+
+static int __init event_init(struct generic_struct *sub_driver)
+{
+	int ret;
+
+	GENERIC_ACPIHANDLE_INIT(hkey);
+	ret = hkey_map();
+	if (ret) {
+		printk(KERN_ERR "Fail to parse keymap from DSDT.\n");
+		return ret;
+	}
+
+	ret = sparse_keymap_setup(generic_inputdev, hotkey_keycode_map, NULL);
+	if (ret) {
+		printk(KERN_ERR "Fail to setup input device keymap\n");
+		input_free_device(generic_inputdev);
+
+		return ret;
+	}
+
+	/*
+	 * This hotkey driver handle backlight event when
+	 * acpi_video_get_backlight_type() gets acpi_backlight_vendor
+	 */
+	if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
+		hotkey_backlight_set(false);
+	else
+		hotkey_backlight_set(true);
+
+	printk("ACPI:enabling firmware HKEY event interface...\n");
+
+	return ret;
+}
+
+#define GENERIC_EVENT_TYPE_OFF		12
+#define GENERIC_EVENT_MASK		0xFFF
+#define TPACPI_MAX_ACPI_ARGS 3
+
+static int acpi_evalf(acpi_handle handle,
+		      int *res, char *method, char *fmt, ...)
+{
+	char res_type;
+	char *fmt0 = fmt;
+	va_list ap;
+	int success, quiet;
+	acpi_status status;
+	struct acpi_object_list params;
+	struct acpi_buffer result, *resultp;
+	union acpi_object in_objs[TPACPI_MAX_ACPI_ARGS], out_obj;
+
+	if (!*fmt) {
+		pr_err("acpi_evalf() called with empty format\n");
+		return 0;
+	}
+
+	if (*fmt == 'q') {
+		quiet = 1;
+		fmt++;
+	} else
+		quiet = 0;
+
+	res_type = *(fmt++);
+
+	params.count = 0;
+	params.pointer = &in_objs[0];
+
+	va_start(ap, fmt);
+	while (*fmt) {
+		char c = *(fmt++);
+		switch (c) {
+		case 'd':	/* int */
+			in_objs[params.count].integer.value = va_arg(ap, int);
+			in_objs[params.count++].type = ACPI_TYPE_INTEGER;
+			break;
+			/* add more types as needed */
+		default:
+			pr_err("acpi_evalf() called with invalid format character '%c'\n",
+			       c);
+			va_end(ap);
+			return 0;
+		}
+	}
+	va_end(ap);
+
+	if (res_type != 'v') {
+		result.length = sizeof(out_obj);
+		result.pointer = &out_obj;
+		resultp = &result;
+	} else
+		resultp = NULL;
+
+	status = acpi_evaluate_object(handle, method, &params, resultp);
+
+	switch (res_type) {
+	case 'd':		/* int */
+		success = (status == AE_OK &&
+			   out_obj.type == ACPI_TYPE_INTEGER);
+		if (success && res)
+			*res = out_obj.integer.value;
+		break;
+	case 'v':		/* void */
+		success = status == AE_OK;
+		break;
+		/* add more types as needed */
+	default:
+		pr_err("acpi_evalf() called with invalid format character '%c'\n",
+		       res_type);
+		return 0;
+	}
+
+	if (!success && !quiet)
+		pr_err("acpi_evalf(%s, %s, ...) failed: %s\n",
+		       method, fmt0, acpi_format_exception(status));
+
+	return success;
+}
+
+int ec_get_brightness(void)
+{
+	int status = 0;
+
+	if (!hkey_handle)
+		return -ENXIO;
+
+	if (!acpi_evalf(hkey_handle, &status, "ECBG", "d"))
+		return -EIO;
+
+	if (status < 0)
+		return status;
+
+	return status;
+}
+EXPORT_SYMBOL(ec_get_brightness);
+
+int ec_set_brightness(int level)
+{
+
+	int ret = 0;
+
+	if (!hkey_handle)
+		return -ENXIO;
+
+	if (!acpi_evalf(hkey_handle, NULL, "ECBS", "vd", level))
+		ret = -EIO;
+
+	return ret;
+}
+EXPORT_SYMBOL(ec_set_brightness);
+
+int ec_backlight_level(u8 level)
+{
+	int status = 0;
+
+	if (!hkey_handle)
+		return -ENXIO;
+
+	if (!acpi_evalf(hkey_handle, &status, "ECLL", "d"))
+		return -EIO;
+
+	if ((status < 0) || (level > status))
+		return status;
+
+	if (!acpi_evalf(hkey_handle, &status, "ECSL", "d"))
+		return -EIO;
+
+	if ((status < 0) || (level < status))
+		return status;
+
+	return level;
+}
+EXPORT_SYMBOL(ec_backlight_level);
+
+static int loongson_laptop_backlight_update(struct backlight_device *bd)
+{
+	int lvl = ec_backlight_level(bd->props.brightness);
+
+	if (lvl < 0)
+		return -EIO;
+	if (ec_set_brightness(lvl))
+		return -EIO;
+
+	return 0;
+}
+
+static int loongson_laptop_get_brightness(struct backlight_device *bd)
+{
+	u8 level;
+
+	level = ec_get_brightness();
+	if (level < 0)
+		return -EIO;
+
+	return level;
+}
+
+static const struct backlight_ops backlight_laptop_ops = {
+	.update_status = loongson_laptop_backlight_update,
+	.get_brightness = loongson_laptop_get_brightness,
+};
+
+static int laptop_backlight_register(void)
+{
+	int status = 0;
+	struct backlight_properties props;
+
+	memset(&props, 0, sizeof(props));
+	props.type = BACKLIGHT_PLATFORM;
+
+	if (!acpi_evalf(hkey_handle, &status, "ECLL", "d"))
+		return -EIO;
+
+	props.brightness = 1;
+	props.max_brightness = status;
+
+	backlight_device_register("loongson_laptop",
+				NULL, NULL, &backlight_laptop_ops, &props);
+
+	return 0;
+}
+
+int turn_on_backlight(void)
+{
+	int status;
+	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+	struct acpi_object_list args = { 1, &arg0 };
+
+	arg0.integer.value = 1;
+	status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL);
+	if (ACPI_FAILURE(status)) {
+		pr_info("Loongson lvds error: 0x%x\n", status);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+int turn_off_backlight(void)
+{
+	int status;
+	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+	struct acpi_object_list args = { 1, &arg0 };
+
+	arg0.integer.value = 0;
+	status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL);
+	if (ACPI_FAILURE(status)) {
+		pr_info("Loongson lvds error: 0x%x\n", status);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int hotkey_status_get(int *status)
+{
+	if (!acpi_evalf(hkey_handle, status, "GSWS", "d"))
+		return -EIO;
+
+	return 0;
+}
+
+static void event_notify(struct generic_struct *sub_driver, u32 event)
+{
+	struct key_entry *ke = NULL;
+	int scan_code = event & GENERIC_EVENT_MASK;
+	int type = (event >> GENERIC_EVENT_TYPE_OFF) & 0xF;
+
+	ke = sparse_keymap_entry_from_scancode(generic_inputdev, scan_code);
+	if (ke) {
+		if (type == KE_SW) {
+			int status = 0;
+
+			if (hotkey_status_get(&status))
+				return;
+
+			ke->sw.value = !!(status & (1 << ke->sw.code));
+		}
+		sparse_keymap_report_entry(generic_inputdev, ke, 1, true);
+	}
+}
+
+static const struct acpi_device_id loongson_htk_device_ids[] = {
+	{LOONGSON_ACPI_HKEY_HID, 0},
+	{"", 0},
+};
+
+static struct generic_acpi_drv_struct ec_event_acpidriver = {
+	.hid = loongson_htk_device_ids,
+	.notify = event_notify,
+	.handle = &hkey_handle,
+	.type = ACPI_DEVICE_NOTIFY,
+};
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Infrastructure
+ *
+ ****************************************************************************
+ ****************************************************************************/
+static int __init probe_for_generic(void)
+{
+	if (acpi_disabled)
+		return -ENODEV;
+
+	/* The EC handler is required */
+	generic_acpi_handle_locate("ec", LOONGSON_ACPI_EC_HID, &ec_handle);
+	if (!ec_handle) {
+		pr_err("Not yet supported Loongson Generic Laptop/All-in-one detected!\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void generic_exit(struct generic_struct *sub_driver)
+{
+
+	if (sub_driver->flags.acpi_notify_installed) {
+		BUG_ON(!sub_driver->acpi);
+		acpi_remove_notify_handler(*sub_driver->acpi->handle,
+					   sub_driver->acpi->type,
+					   dispatch_acpi_notify);
+		sub_driver->flags.acpi_notify_installed = 0;
+	}
+
+	if (sub_driver->flags.acpi_driver_registered) {
+		BUG_ON(!sub_driver->acpi);
+		acpi_bus_unregister_driver(sub_driver->acpi->driver);
+		kfree(sub_driver->acpi->driver);
+		sub_driver->acpi->driver = NULL;
+		sub_driver->flags.acpi_driver_registered = 0;
+	}
+
+}
+
+static int __init generic_subdriver_init(struct generic_struct *sub_driver)
+{
+	int ret;
+
+	BUG_ON(sub_driver == NULL);
+
+	if (sub_driver->init)
+		sub_driver->init(sub_driver);
+
+	if (sub_driver->acpi) {
+		if (sub_driver->acpi->hid) {
+			ret = register_generic_subdriver(sub_driver);
+			if (ret)
+				goto err_out;
+		}
+
+		if (sub_driver->acpi->notify) {
+			ret = setup_acpi_notify(sub_driver);
+			if (ret == -ENODEV) {
+				ret = 0;
+				goto err_out;
+			}
+			if (ret < 0)
+				goto err_out;
+		}
+	}
+
+	return 0;
+
+err_out:
+	generic_exit(sub_driver);
+	return (ret < 0) ? ret : 0;
+}
+
+/* Module init, exit, parameters */
+static struct generic_struct generic_sub_drivers[] __initdata = {
+	{
+		.name = "EC Event",
+		.init = event_init,
+		.acpi = &ec_event_acpidriver,
+	},
+};
+
+static void generic_acpi_laptop_exit(void);
+
+static int __init generic_acpi_laptop_init(void)
+{
+	int i, ret, status;
+
+	ret = probe_for_generic();
+	if (ret) {
+		generic_acpi_laptop_exit();
+		return ret;
+	}
+	generic_inputdev = input_allocate_device();
+	if (!generic_inputdev) {
+		pr_err("unable to allocate input device\n");
+		generic_acpi_laptop_exit();
+		return -ENOMEM;
+	}
+
+	/* Prepare input device, but don't register */
+	generic_inputdev->name =
+		"Loongson Generic Laptop/All-in-one Extra Buttons";
+	generic_inputdev->phys = ACPI_LAPTOP_DRVR_NAME "/input0";
+	generic_inputdev->id.bustype = BUS_HOST;
+	generic_inputdev->dev.parent = NULL;
+
+	/* Init subdrivers */
+	for (i = 0; i < ARRAY_SIZE(generic_sub_drivers); i++) {
+		ret = generic_subdriver_init(&generic_sub_drivers[i]);
+		if (ret < 0) {
+			generic_acpi_laptop_exit();
+			return ret;
+		}
+	}
+
+	ret = input_register_device(generic_inputdev);
+	if (ret < 0) {
+		pr_err("unable to register input device\n");
+		generic_acpi_laptop_exit();
+		return ret;
+	}
+
+	generic_features.input_device_registered = 1;
+
+	if (acpi_evalf(hkey_handle, &status, "ECBG", "d")) {
+		pr_info("Loongson Laptop used, init brightness is 0x%x\n", status);
+		ret = laptop_backlight_register();
+		if (ret < 0)
+			pr_err("Loongson Laptop: laptop-backlight device register failed\n");
+	}
+
+	return 0;
+}
+
+static void __exit generic_acpi_laptop_exit(void)
+{
+	if (generic_inputdev) {
+		if (generic_features.input_device_registered)
+			input_unregister_device(generic_inputdev);
+		else
+			input_free_device(generic_inputdev);
+	}
+}
+
+module_init(generic_acpi_laptop_init);
+module_exit(generic_acpi_laptop_exit);
+
+MODULE_ALIAS("platform:acpi-laptop");
+MODULE_AUTHOR("Jianmin Lv <lvjianmin@loongson.cn>");
+MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>");
+MODULE_DESCRIPTION(ACPI_LAPTOP_DESC);
+MODULE_VERSION(ACPI_LAPTOP_VERSION);
+MODULE_LICENSE("GPL");
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] LoongArch: Add CPU HWMon platform driver
  2022-08-15 12:48 [PATCH 1/2] LoongArch: Add CPU HWMon platform driver Huacai Chen
  2022-08-15 12:48 ` [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver Huacai Chen
@ 2022-08-15 17:58 ` kernel test robot
  2022-08-16  6:59 ` Xi Ruoyao
  2 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2022-08-15 17:58 UTC (permalink / raw)
  To: Huacai Chen, Arnd Bergmann, Huacai Chen, Rafael J . Wysocki,
	Len Brown, Robert Moore, Erik Kaneda
  Cc: kbuild-all, loongarch, linux-arch, linux-acpi, Xuefeng Li,
	Jianmin Lv, Jiaxun Yang

Hi Huacai,

I love your patch! Perhaps something to improve:

[auto build test WARNING on soc/for-next]
[also build test WARNING on linus/master v6.0-rc1 next-20220815]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Huacai-Chen/LoongArch-Add-CPU-HWMon-platform-driver/20220815-205142
base:   https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git for-next
config: loongarch-allyesconfig (https://download.01.org/0day-ci/archive/20220816/202208160121.FAZ06e7K-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/159dd6fa1dd6a1f121ca589031959f9ef7db640d
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Huacai-Chen/LoongArch-Add-CPU-HWMon-platform-driver/20220815-205142
        git checkout 159dd6fa1dd6a1f121ca589031959f9ef7db640d
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=loongarch SHELL=/bin/bash drivers/platform/loongarch/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/platform/loongarch/cpu_hwmon.c:13:5: warning: no previous prototype for 'loongson3_cpu_temp' [-Wmissing-prototypes]
      13 | int loongson3_cpu_temp(int cpu)
         |     ^~~~~~~~~~~~~~~~~~


vim +/loongson3_cpu_temp +13 drivers/platform/loongarch/cpu_hwmon.c

    12	
  > 13	int loongson3_cpu_temp(int cpu)
    14	{
    15		u32 reg;
    16	
    17		reg = iocsr_read32(LOONGARCH_IOCSR_CPUTEMP) & 0xff;
    18	
    19		return (int)((s8)reg) * 1000;
    20	}
    21	EXPORT_SYMBOL(loongson3_cpu_temp);
    22	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver
  2022-08-15 12:48 ` [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver Huacai Chen
@ 2022-08-15 18:40   ` kernel test robot
  2022-08-15 19:11   ` Arnd Bergmann
  1 sibling, 0 replies; 8+ messages in thread
From: kernel test robot @ 2022-08-15 18:40 UTC (permalink / raw)
  To: Huacai Chen, Arnd Bergmann, Huacai Chen, Rafael J . Wysocki,
	Len Brown, Robert Moore, Erik Kaneda
  Cc: kbuild-all, loongarch, linux-arch, linux-acpi, Xuefeng Li,
	Jianmin Lv, Jiaxun Yang

Hi Huacai,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on soc/for-next]
[also build test WARNING on linus/master v6.0-rc1 next-20220815]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Huacai-Chen/LoongArch-Add-CPU-HWMon-platform-driver/20220815-205142
base:   https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git for-next
config: loongarch-allyesconfig (https://download.01.org/0day-ci/archive/20220816/202208160225.IH6Wo0jU-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/a1d11c660bcd67e4483546b5b59b6cbe5c2a882f
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Huacai-Chen/LoongArch-Add-CPU-HWMon-platform-driver/20220815-205142
        git checkout a1d11c660bcd67e4483546b5b59b6cbe5c2a882f
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=loongarch SHELL=/bin/bash drivers/platform/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/platform/loongarch/generic-laptop.c:443:5: warning: no previous prototype for 'ec_get_brightness' [-Wmissing-prototypes]
     443 | int ec_get_brightness(void)
         |     ^~~~~~~~~~~~~~~~~
>> drivers/platform/loongarch/generic-laptop.c:460:5: warning: no previous prototype for 'ec_set_brightness' [-Wmissing-prototypes]
     460 | int ec_set_brightness(int level)
         |     ^~~~~~~~~~~~~~~~~
>> drivers/platform/loongarch/generic-laptop.c:475:5: warning: no previous prototype for 'ec_backlight_level' [-Wmissing-prototypes]
     475 | int ec_backlight_level(u8 level)
         |     ^~~~~~~~~~~~~~~~~~
>> drivers/platform/loongarch/generic-laptop.c:546:5: warning: no previous prototype for 'turn_on_backlight' [-Wmissing-prototypes]
     546 | int turn_on_backlight(void)
         |     ^~~~~~~~~~~~~~~~~
>> drivers/platform/loongarch/generic-laptop.c:562:5: warning: no previous prototype for 'turn_off_backlight' [-Wmissing-prototypes]
     562 | int turn_off_backlight(void)
         |     ^~~~~~~~~~~~~~~~~~


vim +/ec_get_brightness +443 drivers/platform/loongarch/generic-laptop.c

   442	
 > 443	int ec_get_brightness(void)
   444	{
   445		int status = 0;
   446	
   447		if (!hkey_handle)
   448			return -ENXIO;
   449	
   450		if (!acpi_evalf(hkey_handle, &status, "ECBG", "d"))
   451			return -EIO;
   452	
   453		if (status < 0)
   454			return status;
   455	
   456		return status;
   457	}
   458	EXPORT_SYMBOL(ec_get_brightness);
   459	
 > 460	int ec_set_brightness(int level)
   461	{
   462	
   463		int ret = 0;
   464	
   465		if (!hkey_handle)
   466			return -ENXIO;
   467	
   468		if (!acpi_evalf(hkey_handle, NULL, "ECBS", "vd", level))
   469			ret = -EIO;
   470	
   471		return ret;
   472	}
   473	EXPORT_SYMBOL(ec_set_brightness);
   474	
 > 475	int ec_backlight_level(u8 level)
   476	{
   477		int status = 0;
   478	
   479		if (!hkey_handle)
   480			return -ENXIO;
   481	
   482		if (!acpi_evalf(hkey_handle, &status, "ECLL", "d"))
   483			return -EIO;
   484	
   485		if ((status < 0) || (level > status))
   486			return status;
   487	
   488		if (!acpi_evalf(hkey_handle, &status, "ECSL", "d"))
   489			return -EIO;
   490	
   491		if ((status < 0) || (level < status))
   492			return status;
   493	
   494		return level;
   495	}
   496	EXPORT_SYMBOL(ec_backlight_level);
   497	
   498	static int loongson_laptop_backlight_update(struct backlight_device *bd)
   499	{
   500		int lvl = ec_backlight_level(bd->props.brightness);
   501	
   502		if (lvl < 0)
   503			return -EIO;
   504		if (ec_set_brightness(lvl))
   505			return -EIO;
   506	
   507		return 0;
   508	}
   509	
   510	static int loongson_laptop_get_brightness(struct backlight_device *bd)
   511	{
   512		u8 level;
   513	
   514		level = ec_get_brightness();
   515		if (level < 0)
   516			return -EIO;
   517	
   518		return level;
   519	}
   520	
   521	static const struct backlight_ops backlight_laptop_ops = {
   522		.update_status = loongson_laptop_backlight_update,
   523		.get_brightness = loongson_laptop_get_brightness,
   524	};
   525	
   526	static int laptop_backlight_register(void)
   527	{
   528		int status = 0;
   529		struct backlight_properties props;
   530	
   531		memset(&props, 0, sizeof(props));
   532		props.type = BACKLIGHT_PLATFORM;
   533	
   534		if (!acpi_evalf(hkey_handle, &status, "ECLL", "d"))
   535			return -EIO;
   536	
   537		props.brightness = 1;
   538		props.max_brightness = status;
   539	
   540		backlight_device_register("loongson_laptop",
   541					NULL, NULL, &backlight_laptop_ops, &props);
   542	
   543		return 0;
   544	}
   545	
 > 546	int turn_on_backlight(void)
   547	{
   548		int status;
   549		union acpi_object arg0 = { ACPI_TYPE_INTEGER };
   550		struct acpi_object_list args = { 1, &arg0 };
   551	
   552		arg0.integer.value = 1;
   553		status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL);
   554		if (ACPI_FAILURE(status)) {
   555			pr_info("Loongson lvds error: 0x%x\n", status);
   556			return -ENODEV;
   557		}
   558	
   559		return 0;
   560	}
   561	
 > 562	int turn_off_backlight(void)
   563	{
   564		int status;
   565		union acpi_object arg0 = { ACPI_TYPE_INTEGER };
   566		struct acpi_object_list args = { 1, &arg0 };
   567	
   568		arg0.integer.value = 0;
   569		status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL);
   570		if (ACPI_FAILURE(status)) {
   571			pr_info("Loongson lvds error: 0x%x\n", status);
   572			return -ENODEV;
   573		}
   574	
   575		return 0;
   576	}
   577	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver
  2022-08-15 12:48 ` [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver Huacai Chen
  2022-08-15 18:40   ` kernel test robot
@ 2022-08-15 19:11   ` Arnd Bergmann
  2022-08-16  8:55     ` Huacai Chen
  1 sibling, 1 reply; 8+ messages in thread
From: Arnd Bergmann @ 2022-08-15 19:11 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Huacai Chen, Rafael J . Wysocki, Len Brown,
	Robert Moore, Erik Kaneda, loongarch, linux-arch, linux-acpi,
	Xuefeng Li, Jianmin Lv, Jiaxun Yang

On Mon, Aug 15, 2022 at 2:48 PM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> From: Jianmin Lv <lvjianmin@loongson.cn>
>
> This add ACPI-based generic laptop driver for Loongson-3. Some of the
> codes are derived from drivers/platform/x86/thinkpad_acpi.c.
>
> Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn>
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> ---
>  drivers/platform/loongarch/Kconfig          |  18 +
>  drivers/platform/loongarch/Makefile         |   1 +
>  drivers/platform/loongarch/generic-laptop.c | 775 ++++++++++++++++++++
>  3 files changed, 794 insertions(+)
>  create mode 100644 drivers/platform/loongarch/generic-laptop.c
>
> diff --git a/drivers/platform/loongarch/Kconfig b/drivers/platform/loongarch/Kconfig
> index a1542843b0ad..086212d57251 100644
> --- a/drivers/platform/loongarch/Kconfig
> +++ b/drivers/platform/loongarch/Kconfig
> @@ -23,4 +23,22 @@ config CPU_HWMON
>         help
>           Loongson-3A/3B/3C CPU HWMon (temperature sensor) driver.
>
> +config GENERIC_LAPTOP
> +       tristate "Generic Loongson-3A Laptop Driver"
> +       depends on MACH_LOONGSON64
> +       depends on ACPI
> +       depends on INPUT
> +       select BACKLIGHT_CLASS_DEVICE
> +       select BACKLIGHT_LCD_SUPPORT
> +       select HWMON
> +       select INPUT_EVDEV
> +       select INPUT_SPARSEKMAP
> +       select LCD_CLASS_DEVICE
> +       select LEDS_CLASS
> +       select POWER_SUPPLY
> +       select VIDEO_OUTPUT_CONTROL
> +       default y
> +       help
> +         ACPI-based Loongson-3 family laptops generic driver.

It's rather bad style to 'select' entire subsystems from a device
driver. This may be
unavoidable in some cases, but please try to make it possible to build the
driver when some or all of the other subsystems are disabled. In a lot
of subsystems,
there is an API stub like

> +/****************************************************************************
> + ****************************************************************************
> + *
> + * ACPI Helpers and device model
> + *
> + ****************************************************************************
> + ****************************************************************************/

Try to follow the normal commenting style

> +/* ACPI basic handles */
> +
> +static int acpi_evalf(acpi_handle handle,
> +                     int *res, char *method, char *fmt, ...);
> +static acpi_handle ec_handle;

Instead of forward function declarations, try sorting the functions in
call order,
which has the benefit of making more sense to most readers.

> +#ifdef CONFIG_PM
> +static int loongson_generic_suspend(struct device *dev)
> +{
> +       return 0;
> +}
> +static int loongson_generic_resume(struct device *dev)
> +{
> +       int status = 0;
> +       struct key_entry ke;
> +       struct backlight_device *bd;
> +
> +       /*
> +        * Only if the firmware supports SW_LID event model, we can handle the
> +        * event. This is for the consideration of development board without
> +        * EC.
> +        */
> +       if (test_bit(SW_LID, generic_inputdev->swbit)) {
> +               if (hotkey_status_get(&status))
> +                       return -EIO;
> +               /*
> +                * The input device sw element records the last lid status.
> +                * When the system is awakened by other wake-up sources,
> +                * the lid event will also be reported. The judgment of
> +                * adding SW_LID bit which in sw element can avoid this
> +                * case.
> +                *
> +                * input system will drop lid event when current lid event
> +                * value and last lid status in the same data set,which
> +                * data set inclue zero set and no zero set. so laptop
> +                * driver doesn't report repeated events.
> +                *
> +                * Lid status is generally 0, but hardware exception is
> +                * considered. So add lid status confirmation.
> +                */
> +               if (test_bit(SW_LID, generic_inputdev->sw) && !(status & (1 << SW_LID))) {
> +                       ke.type = KE_SW;
> +                       ke.sw.value = (u8)status;
> +                       ke.sw.code = SW_LID;
> +                       sparse_keymap_report_entry(generic_inputdev, &ke,
> +                                       1, true);
> +               }
> +       }
> +
> +       bd = backlight_device_get_by_type(BACKLIGHT_PLATFORM);
> +       if (bd) {
> +               loongson_laptop_backlight_update(bd) ?
> +               pr_warn("Loongson_backlight:resume brightness failed") :
> +               pr_info("Loongson_backlight:resume brightness %d\n", bd->props.brightness);
> +       }
> +
> +       return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(loongson_generic_pm,
> +               loongson_generic_suspend, loongson_generic_resume);
> +#endif


Instead of the #ifdef, use the newer DEFINE_SIMPLE_DEV_PM_OPS() in place
of SIMPLE_DEV_PM_OPS() so the code will always be parsed but left out
when CONFIG_PM is disabled.

> +
> +static int __init register_generic_subdriver(struct generic_struct *sub_driver)
> +{
> +       int rc;
> +
> +       BUG_ON(!sub_driver->acpi);
> +
> +       sub_driver->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
> +       if (!sub_driver->acpi->driver) {
> +               pr_err("failed to allocate memory for ibm->acpi->driver\n");
> +               return -ENOMEM;
> +       }

Drivers should be statically allocated. Usually you want one 'struct
acpi_driver' or
'struct platform_driver' per file, so you can just use 'module_acpi_driver()'.

> +int ec_get_brightness(void)
> +{
> +       int status = 0;
> +
> +       if (!hkey_handle)
> +               return -ENXIO;
> +
> +       if (!acpi_evalf(hkey_handle, &status, "ECBG", "d"))
> +               return -EIO;
> +
> +       if (status < 0)
> +               return status;
> +
> +       return status;
> +}
> +EXPORT_SYMBOL(ec_get_brightness);

The name is too generic to have it in the global namespace for a platform
specific driver. Use a prefix to make it clear which driver this belongs to.

Not sure this function warrants an export though, it looks like you could
just have it in the caller module.

> +
> +int turn_off_backlight(void)
> +{
> +       int status;
> +       union acpi_object arg0 = { ACPI_TYPE_INTEGER };
> +       struct acpi_object_list args = { 1, &arg0 };
> +
> +       arg0.integer.value = 0;
> +       status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL);
> +       if (ACPI_FAILURE(status)) {
> +               pr_info("Loongson lvds error: 0x%x\n", status);
> +               return -ENODEV;
> +       }
> +
> +       return 0;
> +}

Again, the name is too generic for a global function.

I suspect that if you split out the backlight handling into a separate
driver, you can avoid
the 'select' statements completely and make that driver 'depends on
BACKLIGHT_CLASS_DEVICE'
or move it within the 'if BACKLIGHT_CLASS_DEVICE' section of
drivers/video/backlight/Kconfig.


> +static struct generic_acpi_drv_struct ec_event_acpidriver = {
> +       .hid = loongson_htk_device_ids,
> +       .notify = event_notify,
> +       .handle = &hkey_handle,
> +       .type = ACPI_DEVICE_NOTIFY,
> +};

Same here, this can probably just be an input driver in drivers/input.

       Arnd

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] LoongArch: Add CPU HWMon platform driver
  2022-08-15 12:48 [PATCH 1/2] LoongArch: Add CPU HWMon platform driver Huacai Chen
  2022-08-15 12:48 ` [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver Huacai Chen
  2022-08-15 17:58 ` [PATCH 1/2] LoongArch: Add CPU HWMon platform driver kernel test robot
@ 2022-08-16  6:59 ` Xi Ruoyao
  2 siblings, 0 replies; 8+ messages in thread
From: Xi Ruoyao @ 2022-08-16  6:59 UTC (permalink / raw)
  To: Huacai Chen, Arnd Bergmann, Huacai Chen, Rafael J . Wysocki,
	Len Brown, Robert Moore, Erik Kaneda
  Cc: loongarch, linux-arch, linux-acpi, Xuefeng Li, Jianmin Lv, Jiaxun Yang

It shows my CPU temperature is floating between 48 and 50 Celsius. 
Higher than I expected but I guess it's because we've not implemented
CPUFreq yet.

Tested-by: Xi Ruoyao <xry111@xry111.site>

On Mon, 2022-08-15 at 20:48 +0800, Huacai Chen wrote:
> This add CPU HWMon (temperature sensor) platform driver for Loongson-
> 3.
> 
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> ---
>  drivers/platform/Kconfig               |   3 +
>  drivers/platform/Makefile              |   1 +
>  drivers/platform/loongarch/Kconfig     |  26 ++++
>  drivers/platform/loongarch/Makefile    |   1 +
>  drivers/platform/loongarch/cpu_hwmon.c | 195
> +++++++++++++++++++++++++
>  5 files changed, 226 insertions(+)
>  create mode 100644 drivers/platform/loongarch/Kconfig
>  create mode 100644 drivers/platform/loongarch/Makefile
>  create mode 100644 drivers/platform/loongarch/cpu_hwmon.c
> 
> diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
> index b437847b6237..9c68e2def2cb 100644
> --- a/drivers/platform/Kconfig
> +++ b/drivers/platform/Kconfig
> @@ -2,6 +2,9 @@
>  if MIPS
>  source "drivers/platform/mips/Kconfig"
>  endif
> +if LOONGARCH
> +source "drivers/platform/loongarch/Kconfig"
> +endif
>  
>  source "drivers/platform/goldfish/Kconfig"
>  
> diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
> index 4de08ef4ec9d..41640172975a 100644
> --- a/drivers/platform/Makefile
> +++ b/drivers/platform/Makefile
> @@ -4,6 +4,7 @@
>  #
>  
>  obj-$(CONFIG_X86)              += x86/
> +obj-$(CONFIG_LOONGARCH)                += loongarch/
>  obj-$(CONFIG_MELLANOX_PLATFORM)        += mellanox/
>  obj-$(CONFIG_MIPS)             += mips/
>  obj-$(CONFIG_OLPC_EC)          += olpc/
> diff --git a/drivers/platform/loongarch/Kconfig
> b/drivers/platform/loongarch/Kconfig
> new file mode 100644
> index 000000000000..a1542843b0ad
> --- /dev/null
> +++ b/drivers/platform/loongarch/Kconfig
> @@ -0,0 +1,26 @@
> +#
> +# LoongArch Platform Specific Drivers
> +#
> +
> +menuconfig LOONGARCH_PLATFORM_DEVICES
> +       bool "LoongArch Platform Specific Device Drivers"
> +       default LOONGARCH
> +       help
> +         Say Y here to get to see options for device drivers of
> various
> +         LoongArch platforms, including vendor-specific
> laptop/desktop
> +         extension and hardware monitor drivers. This option itself
> does
> +         not add any kernel code.
> +
> +         If you say N, all options in this submenu will be skipped
> and disabled.
> +
> +if LOONGARCH_PLATFORM_DEVICES
> +
> +config CPU_HWMON
> +       bool "Loongson CPU HWMon Driver"
> +       depends on MACH_LOONGSON64
> +       select HWMON
> +       default y
> +       help
> +         Loongson-3A/3B/3C CPU HWMon (temperature sensor) driver.
> +
> +endif # LOONGARCH_PLATFORM_DEVICES
> diff --git a/drivers/platform/loongarch/Makefile
> b/drivers/platform/loongarch/Makefile
> new file mode 100644
> index 000000000000..8dfd03924c37
> --- /dev/null
> +++ b/drivers/platform/loongarch/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o
> diff --git a/drivers/platform/loongarch/cpu_hwmon.c
> b/drivers/platform/loongarch/cpu_hwmon.c
> new file mode 100644
> index 000000000000..3673c850f66c
> --- /dev/null
> +++ b/drivers/platform/loongarch/cpu_hwmon.c
> @@ -0,0 +1,195 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2022 Loongson Technology Corporation Limited
> + */
> +#include <linux/module.h>
> +#include <linux/reboot.h>
> +#include <linux/jiffies.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +
> +#include <asm/loongson.h>
> +
> +int loongson3_cpu_temp(int cpu)
> +{
> +       u32 reg;
> +
> +       reg = iocsr_read32(LOONGARCH_IOCSR_CPUTEMP) & 0xff;
> +
> +       return (int)((s8)reg) * 1000;
> +}
> +EXPORT_SYMBOL(loongson3_cpu_temp);
> +
> +static int nr_packages;
> +static struct device *cpu_hwmon_dev;
> +
> +static ssize_t cpu_temp_label(struct device *dev,
> +                       struct device_attribute *attr, char *buf)
> +{
> +       int id = (to_sensor_dev_attr(attr))->index - 1;
> +       return sprintf(buf, "CPU %d Temperature\n", id);
> +}
> +
> +static ssize_t get_cpu_temp(struct device *dev,
> +                       struct device_attribute *attr, char *buf)
> +{
> +       int id = (to_sensor_dev_attr(attr))->index - 1;
> +       int value = loongson3_cpu_temp(id);
> +       return sprintf(buf, "%d\n", value);
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, 0444, get_cpu_temp, NULL, 1);
> +static SENSOR_DEVICE_ATTR(temp1_label, 0444, cpu_temp_label, NULL,
> 1);
> +static SENSOR_DEVICE_ATTR(temp2_input, 0444, get_cpu_temp, NULL, 2);
> +static SENSOR_DEVICE_ATTR(temp2_label, 0444, cpu_temp_label, NULL,
> 2);
> +static SENSOR_DEVICE_ATTR(temp3_input, 0444, get_cpu_temp, NULL, 3);
> +static SENSOR_DEVICE_ATTR(temp3_label, 0444, cpu_temp_label, NULL,
> 3);
> +static SENSOR_DEVICE_ATTR(temp4_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp4_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp5_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp5_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp6_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp6_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp7_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp7_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp8_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp8_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp9_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp9_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp10_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp10_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp11_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp11_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp12_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp12_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp13_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp13_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp14_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp14_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp15_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp15_label, 0444, cpu_temp_label, NULL,
> 4);
> +static SENSOR_DEVICE_ATTR(temp16_input, 0444, get_cpu_temp, NULL, 4);
> +static SENSOR_DEVICE_ATTR(temp16_label, 0444, cpu_temp_label, NULL,
> 4);
> +
> +static struct attribute *cpu_hwmon_attributes[] = {
> +       &sensor_dev_attr_temp1_input.dev_attr.attr,
> +       &sensor_dev_attr_temp1_label.dev_attr.attr,
> +       &sensor_dev_attr_temp2_input.dev_attr.attr,
> +       &sensor_dev_attr_temp2_label.dev_attr.attr,
> +       &sensor_dev_attr_temp3_input.dev_attr.attr,
> +       &sensor_dev_attr_temp3_label.dev_attr.attr,
> +       &sensor_dev_attr_temp4_input.dev_attr.attr,
> +       &sensor_dev_attr_temp4_label.dev_attr.attr,
> +       &sensor_dev_attr_temp5_input.dev_attr.attr,
> +       &sensor_dev_attr_temp5_label.dev_attr.attr,
> +       &sensor_dev_attr_temp6_input.dev_attr.attr,
> +       &sensor_dev_attr_temp6_label.dev_attr.attr,
> +       &sensor_dev_attr_temp7_input.dev_attr.attr,
> +       &sensor_dev_attr_temp7_label.dev_attr.attr,
> +       &sensor_dev_attr_temp8_input.dev_attr.attr,
> +       &sensor_dev_attr_temp8_label.dev_attr.attr,
> +       &sensor_dev_attr_temp9_input.dev_attr.attr,
> +       &sensor_dev_attr_temp9_label.dev_attr.attr,
> +       &sensor_dev_attr_temp10_input.dev_attr.attr,
> +       &sensor_dev_attr_temp10_label.dev_attr.attr,
> +       &sensor_dev_attr_temp11_input.dev_attr.attr,
> +       &sensor_dev_attr_temp11_label.dev_attr.attr,
> +       &sensor_dev_attr_temp12_input.dev_attr.attr,
> +       &sensor_dev_attr_temp12_label.dev_attr.attr,
> +       &sensor_dev_attr_temp13_input.dev_attr.attr,
> +       &sensor_dev_attr_temp13_label.dev_attr.attr,
> +       &sensor_dev_attr_temp14_input.dev_attr.attr,
> +       &sensor_dev_attr_temp14_label.dev_attr.attr,
> +       &sensor_dev_attr_temp15_input.dev_attr.attr,
> +       &sensor_dev_attr_temp15_label.dev_attr.attr,
> +       &sensor_dev_attr_temp16_input.dev_attr.attr,
> +       &sensor_dev_attr_temp16_label.dev_attr.attr,
> +       NULL
> +};
> +static umode_t cpu_hwmon_is_visible(struct kobject *kobj,
> +                                   struct attribute *attr, int i)
> +{
> +       int id = i / 2;
> +
> +       if (id < nr_packages)
> +               return attr->mode;
> +       return 0;
> +}
> +
> +static struct attribute_group cpu_hwmon_group = {
> +       .attrs = cpu_hwmon_attributes,
> +       .is_visible = cpu_hwmon_is_visible,
> +};
> +
> +static const struct attribute_group *cpu_hwmon_groups[] = {
> +       &cpu_hwmon_group,
> +       NULL
> +};
> +
> +static int cpu_initial_threshold = 72000;
> +static int cpu_thermal_threshold = 96000;
> +module_param(cpu_thermal_threshold, int, 0644);
> +MODULE_PARM_DESC(cpu_thermal_threshold, "cpu thermal threshold (96000
> (default))");
> +
> +static struct delayed_work thermal_work;
> +
> +static void do_thermal_timer(struct work_struct *work)
> +{
> +       int i, value, temp_max = 0;
> +
> +       for (i=0; i<nr_packages; i++) {
> +               value = loongson3_cpu_temp(i);
> +               if (value > temp_max)
> +                       temp_max = value;
> +       }
> +
> +       if (temp_max <= cpu_thermal_threshold)
> +               schedule_delayed_work(&thermal_work,
> msecs_to_jiffies(5000));
> +       else
> +               orderly_poweroff(true);
> +}
> +
> +static int __init loongson_hwmon_init(void)
> +{
> +       int i, value, temp_max = 0;
> +
> +       pr_info("Loongson Hwmon Enter...\n");
> +
> +       nr_packages = loongson_sysconf.nr_cpus /
> +               loongson_sysconf.cores_per_package;
> +
> +       cpu_hwmon_dev = hwmon_device_register_with_groups(NULL,
> "cpu_hwmon",
> +                                                         NULL,
> cpu_hwmon_groups);
> +       if (IS_ERR(cpu_hwmon_dev)) {
> +               pr_err("hwmon_device_register fail!\n");
> +               return PTR_ERR(cpu_hwmon_dev);
> +       }
> +
> +       for (i = 0; i < nr_packages; i++) {
> +               value = loongson3_cpu_temp(i);
> +               if (value > temp_max)
> +                       temp_max = value;
> +       }
> +
> +       pr_info("Initial CPU temperature is %d (highest).\n",
> temp_max);
> +       if (temp_max > cpu_initial_threshold)
> +               cpu_thermal_threshold += temp_max -
> cpu_initial_threshold;
> +
> +       INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer);
> +       schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000));
> +
> +       return 0;
> +}
> +
> +static void __exit loongson_hwmon_exit(void)
> +{
> +       cancel_delayed_work_sync(&thermal_work);
> +       hwmon_device_unregister(cpu_hwmon_dev);
> +}
> +
> +module_init(loongson_hwmon_init);
> +module_exit(loongson_hwmon_exit);
> +
> +MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>");
> +MODULE_DESCRIPTION("Loongson CPU Hwmon driver");
> +MODULE_LICENSE("GPL");

-- 
Xi Ruoyao <xry111@xry111.site>
School of Aerospace Science and Technology, Xidian University

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver
  2022-08-15 19:11   ` Arnd Bergmann
@ 2022-08-16  8:55     ` Huacai Chen
  2022-08-16  9:05       ` Arnd Bergmann
  0 siblings, 1 reply; 8+ messages in thread
From: Huacai Chen @ 2022-08-16  8:55 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Huacai Chen, Rafael J . Wysocki, Len Brown, Robert Moore,
	Erik Kaneda, loongarch, linux-arch, ACPI Devel Maling List,
	Xuefeng Li, Jianmin Lv, Jiaxun Yang

Hi, Arnd,

On Tue, Aug 16, 2022 at 3:11 AM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Mon, Aug 15, 2022 at 2:48 PM Huacai Chen <chenhuacai@loongson.cn> wrote:
> >
> > From: Jianmin Lv <lvjianmin@loongson.cn>
> >
> > This add ACPI-based generic laptop driver for Loongson-3. Some of the
> > codes are derived from drivers/platform/x86/thinkpad_acpi.c.
> >
> > Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn>
> > Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> > ---
> >  drivers/platform/loongarch/Kconfig          |  18 +
> >  drivers/platform/loongarch/Makefile         |   1 +
> >  drivers/platform/loongarch/generic-laptop.c | 775 ++++++++++++++++++++
> >  3 files changed, 794 insertions(+)
> >  create mode 100644 drivers/platform/loongarch/generic-laptop.c
> >
> > diff --git a/drivers/platform/loongarch/Kconfig b/drivers/platform/loongarch/Kconfig
> > index a1542843b0ad..086212d57251 100644
> > --- a/drivers/platform/loongarch/Kconfig
> > +++ b/drivers/platform/loongarch/Kconfig
> > @@ -23,4 +23,22 @@ config CPU_HWMON
> >         help
> >           Loongson-3A/3B/3C CPU HWMon (temperature sensor) driver.
> >
> > +config GENERIC_LAPTOP
> > +       tristate "Generic Loongson-3A Laptop Driver"
> > +       depends on MACH_LOONGSON64
> > +       depends on ACPI
> > +       depends on INPUT
> > +       select BACKLIGHT_CLASS_DEVICE
> > +       select BACKLIGHT_LCD_SUPPORT
> > +       select HWMON
> > +       select INPUT_EVDEV
> > +       select INPUT_SPARSEKMAP
> > +       select LCD_CLASS_DEVICE
> > +       select LEDS_CLASS
> > +       select POWER_SUPPLY
> > +       select VIDEO_OUTPUT_CONTROL
> > +       default y
> > +       help
> > +         ACPI-based Loongson-3 family laptops generic driver.
>
> It's rather bad style to 'select' entire subsystems from a device
> driver. This may be
> unavoidable in some cases, but please try to make it possible to build the
> driver when some or all of the other subsystems are disabled. In a lot
> of subsystems,
> there is an API stub like
OK, the Kconfig should be cleaned up, I will remove those unneeded
lines, and convert some others to "depends on".

>
> > +/****************************************************************************
> > + ****************************************************************************
> > + *
> > + * ACPI Helpers and device model
> > + *
> > + ****************************************************************************
> > + ****************************************************************************/
>
> Try to follow the normal commenting style
OK, thanks.

>
> > +/* ACPI basic handles */
> > +
> > +static int acpi_evalf(acpi_handle handle,
> > +                     int *res, char *method, char *fmt, ...);
> > +static acpi_handle ec_handle;
>
> Instead of forward function declarations, try sorting the functions in
> call order,
> which has the benefit of making more sense to most readers.
OK, thanks.

>
> > +#ifdef CONFIG_PM
> > +static int loongson_generic_suspend(struct device *dev)
> > +{
> > +       return 0;
> > +}
> > +static int loongson_generic_resume(struct device *dev)
> > +{
> > +       int status = 0;
> > +       struct key_entry ke;
> > +       struct backlight_device *bd;
> > +
> > +       /*
> > +        * Only if the firmware supports SW_LID event model, we can handle the
> > +        * event. This is for the consideration of development board without
> > +        * EC.
> > +        */
> > +       if (test_bit(SW_LID, generic_inputdev->swbit)) {
> > +               if (hotkey_status_get(&status))
> > +                       return -EIO;
> > +               /*
> > +                * The input device sw element records the last lid status.
> > +                * When the system is awakened by other wake-up sources,
> > +                * the lid event will also be reported. The judgment of
> > +                * adding SW_LID bit which in sw element can avoid this
> > +                * case.
> > +                *
> > +                * input system will drop lid event when current lid event
> > +                * value and last lid status in the same data set,which
> > +                * data set inclue zero set and no zero set. so laptop
> > +                * driver doesn't report repeated events.
> > +                *
> > +                * Lid status is generally 0, but hardware exception is
> > +                * considered. So add lid status confirmation.
> > +                */
> > +               if (test_bit(SW_LID, generic_inputdev->sw) && !(status & (1 << SW_LID))) {
> > +                       ke.type = KE_SW;
> > +                       ke.sw.value = (u8)status;
> > +                       ke.sw.code = SW_LID;
> > +                       sparse_keymap_report_entry(generic_inputdev, &ke,
> > +                                       1, true);
> > +               }
> > +       }
> > +
> > +       bd = backlight_device_get_by_type(BACKLIGHT_PLATFORM);
> > +       if (bd) {
> > +               loongson_laptop_backlight_update(bd) ?
> > +               pr_warn("Loongson_backlight:resume brightness failed") :
> > +               pr_info("Loongson_backlight:resume brightness %d\n", bd->props.brightness);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static SIMPLE_DEV_PM_OPS(loongson_generic_pm,
> > +               loongson_generic_suspend, loongson_generic_resume);
> > +#endif
>
>
> Instead of the #ifdef, use the newer DEFINE_SIMPLE_DEV_PM_OPS() in place
> of SIMPLE_DEV_PM_OPS() so the code will always be parsed but left out
> when CONFIG_PM is disabled.
OK, thanks.

>
> > +
> > +static int __init register_generic_subdriver(struct generic_struct *sub_driver)
> > +{
> > +       int rc;
> > +
> > +       BUG_ON(!sub_driver->acpi);
> > +
> > +       sub_driver->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
> > +       if (!sub_driver->acpi->driver) {
> > +               pr_err("failed to allocate memory for ibm->acpi->driver\n");
> > +               return -ENOMEM;
> > +       }
>
> Drivers should be statically allocated. Usually you want one 'struct
> acpi_driver' or
> 'struct platform_driver' per file, so you can just use 'module_acpi_driver()'.
I found that "subdriver" in other laptop drivers also uses dynamical
allocation, because there may be various numbers of subdrivers. I want
to keep it, at least in the next version for review.

>
> > +int ec_get_brightness(void)
> > +{
> > +       int status = 0;
> > +
> > +       if (!hkey_handle)
> > +               return -ENXIO;
> > +
> > +       if (!acpi_evalf(hkey_handle, &status, "ECBG", "d"))
> > +               return -EIO;
> > +
> > +       if (status < 0)
> > +               return status;
> > +
> > +       return status;
> > +}
> > +EXPORT_SYMBOL(ec_get_brightness);
>
> The name is too generic to have it in the global namespace for a platform
> specific driver. Use a prefix to make it clear which driver this belongs to.
>
> Not sure this function warrants an export though, it looks like you could
> just have it in the caller module.
Yes, they should be static.

>
> > +
> > +int turn_off_backlight(void)
> > +{
> > +       int status;
> > +       union acpi_object arg0 = { ACPI_TYPE_INTEGER };
> > +       struct acpi_object_list args = { 1, &arg0 };
> > +
> > +       arg0.integer.value = 0;
> > +       status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL);
> > +       if (ACPI_FAILURE(status)) {
> > +               pr_info("Loongson lvds error: 0x%x\n", status);
> > +               return -ENODEV;
> > +       }
> > +
> > +       return 0;
> > +}
>
> Again, the name is too generic for a global function.
Yes, they should be renamed.

>
> I suspect that if you split out the backlight handling into a separate
> driver, you can avoid
> the 'select' statements completely and make that driver 'depends on
> BACKLIGHT_CLASS_DEVICE'
> or move it within the 'if BACKLIGHT_CLASS_DEVICE' section of
> drivers/video/backlight/Kconfig.
>
>
> > +static struct generic_acpi_drv_struct ec_event_acpidriver = {
> > +       .hid = loongson_htk_device_ids,
> > +       .notify = event_notify,
> > +       .handle = &hkey_handle,
> > +       .type = ACPI_DEVICE_NOTIFY,
> > +};
>
> Same here, this can probably just be an input driver in drivers/input.
It seems the existing "laptop drivers" are also complex drivers to
bind several "subdrivers" together.

Huacai
>
>        Arnd
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver
  2022-08-16  8:55     ` Huacai Chen
@ 2022-08-16  9:05       ` Arnd Bergmann
  0 siblings, 0 replies; 8+ messages in thread
From: Arnd Bergmann @ 2022-08-16  9:05 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Huacai Chen, Rafael J . Wysocki, Len Brown,
	Robert Moore, Erik Kaneda, loongarch, linux-arch,
	ACPI Devel Maling List, Xuefeng Li, Jianmin Lv, Jiaxun Yang,
	Hans de Goede, Mark Gross

On Tue, Aug 16, 2022 at 10:55 AM Huacai Chen <chenhuacai@kernel.org> wrote:
> On Tue, Aug 16, 2022 at 3:11 AM Arnd Bergmann <arnd@arndb.de> wrote:
> > On Mon, Aug 15, 2022 at 2:48 PM Huacai Chen <chenhuacai@loongson.cn> wrote:
> > > +
> > > +static int __init register_generic_subdriver(struct generic_struct *sub_driver)
> > > +{
> > > +       int rc;
> > > +
> > > +       BUG_ON(!sub_driver->acpi);
> > > +
> > > +       sub_driver->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
> > > +       if (!sub_driver->acpi->driver) {
> > > +               pr_err("failed to allocate memory for ibm->acpi->driver\n");
> > > +               return -ENOMEM;
> > > +       }
> >
> > Drivers should be statically allocated. Usually you want one 'struct
> > acpi_driver' or
> > 'struct platform_driver' per file, so you can just use 'module_acpi_driver()'.
> I found that "subdriver" in other laptop drivers also uses dynamical
> allocation, because there may be various numbers of subdrivers. I want
> to keep it, at least in the next version for review.

Fair enough, I'm not that familiar with drivers/platform/ conventions,
so this is
probably fine.  Adding the drivers/platform/x86 maintainers for clarification,
since your driver probably fits best into that general class regardless of the
CPU instruction set.

> > > +static struct generic_acpi_drv_struct ec_event_acpidriver = {
> > > +       .hid = loongson_htk_device_ids,
> > > +       .notify = event_notify,
> > > +       .handle = &hkey_handle,
> > > +       .type = ACPI_DEVICE_NOTIFY,
> > > +};
> >
> > Same here, this can probably just be an input driver in drivers/input.
>
> It seems the existing "laptop drivers" are also complex drivers to
> bind several "subdrivers" together.

Let's see what Hans and Mark think here as well. My feeling is that this
might be a little different. In the other laptop drivers you'd load a
module for a Dell/HP/Lenovo/Asus/Acer/... model and that has all the
bits that this vendor uses across the system, so a single driver makes
sense.

In embedded systems, you'd have SoC specific drivers that we tend
to put into the respective subsystems (backlight, led, input, ...) and
then users pick the drivers they want.

Loongarch is probably somewhere inbetween, since this is a chip
vendor rather than a laptop vendor, but you have all the same bits
here. The question is more about where we put it.

       Arnd

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2022-08-16  9:06 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-15 12:48 [PATCH 1/2] LoongArch: Add CPU HWMon platform driver Huacai Chen
2022-08-15 12:48 ` [PATCH 2/2] LoongArch: Add ACPI-based generic laptop driver Huacai Chen
2022-08-15 18:40   ` kernel test robot
2022-08-15 19:11   ` Arnd Bergmann
2022-08-16  8:55     ` Huacai Chen
2022-08-16  9:05       ` Arnd Bergmann
2022-08-15 17:58 ` [PATCH 1/2] LoongArch: Add CPU HWMon platform driver kernel test robot
2022-08-16  6:59 ` Xi Ruoyao

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.