Re: [PATCH V2 2/2] LoongArch: Add ACPI-based generic laptop driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi, Rafael,

On Wed, Aug 24, 2022 at 9:25 PM Rafael J. Wysocki <rafael@xxxxxxxxxx> wrote:
>
> On Wed, Aug 24, 2022 at 6:13 AM Huacai Chen <chenhuacai@xxxxxxxxxx> wrote:
> >
> > Hi, Rafael,
> >
> > On Wed, Aug 24, 2022 at 12:49 AM Rafael J. Wysocki <rafael@xxxxxxxxxx> wrote:
> > >
> > > On Thu, Aug 18, 2022 at 6:23 AM Huacai Chen <chenhuacai@xxxxxxxxxxx> wrote:
> > > >
> > > > From: Jianmin Lv <lvjianmin@xxxxxxxxxxx>
> > > >
> > > > 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@xxxxxxxxxxx>
> > > > Signed-off-by: Huacai Chen <chenhuacai@xxxxxxxxxxx>
> > > > ---
> > > > V2: Fix problems pointed out by Arnd.
> > > >
> > > >  drivers/platform/loongarch/Kconfig          |  12 +
> > > >  drivers/platform/loongarch/Makefile         |   1 +
> > > >  drivers/platform/loongarch/generic-laptop.c | 747 ++++++++++++++++++++
> > > >  3 files changed, 760 insertions(+)
> > > >  create mode 100644 drivers/platform/loongarch/generic-laptop.c
> > > >
> > > > diff --git a/drivers/platform/loongarch/Kconfig b/drivers/platform/loongarch/Kconfig
> > > > index a1542843b0ad..61ed83227d36 100644
> > > > --- a/drivers/platform/loongarch/Kconfig
> > > > +++ b/drivers/platform/loongarch/Kconfig
> > > > @@ -23,4 +23,16 @@ config CPU_HWMON
> > > >         help
> > > >           Loongson-3A/3B/3C CPU HWMon (temperature sensor) driver.
> > > >
> > > > +config GENERIC_LAPTOP
> > > > +       tristate "Generic Loongson-3 Laptop Driver"
> > > > +       depends on ACPI
> > > > +       depends on BACKLIGHT_CLASS_DEVICE
> > > > +       depends on INPUT
> > > > +       depends on MACH_LOONGSON64
> > > > +       select INPUT_EVDEV
> > > > +       select INPUT_SPARSEKMAP
> > > > +       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..90c866f29702
> > > > --- /dev/null
> > > > +++ b/drivers/platform/loongarch/generic-laptop.c
> > > > @@ -0,0 +1,747 @@
> > > > +/*
> > > > + *  Generic Loongson processor based LAPTOP/ALL-IN-ONE driver
> > > > + *
> > > > + *  Jianmin Lv <lvjianmin@xxxxxxxxxxx>
> > > > + *  Huacai Chen <chenhuacai@xxxxxxxxxxx>
> > > > + *
> > > > + * 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);
> > > > +
> > > > +/* 1. ACPI Helpers and device model */
> > > > +
> > > > +/* ACPI basic handles */
> > > > +
> > > > +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;
> > > > +
> > > > +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 DEFINE_SIMPLE_DEV_PM_OPS(loongson_generic_pm,
> > > > +               loongson_generic_suspend, loongson_generic_resume);
> > > > +
> > > > +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);
> > >
> > > Please don't use acpi_driver here.  Use a platform driver instead and
> > > bind to the platform device created by the ACPI subsystem for your
> > > ACPI device object.
> > This is derived from register_tpacpi_subdriver() in thinkpad_acpi.c,
> > that means thinkpad also uses the wrong method?
>
> That driver is one of the known exceptions mentioned by me.
OK, could you please give me an example of binding for this type, thanks.

Huacai
>
> Thanks!
>




[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux