/*      $NetBSD: vald_acpi.c,v 1.8 2021/12/07 21:37:36 andvar Exp $ */

/*-
* Copyright (c) 2002 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Masanori Kanaoka.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

/*
* Copyright 2001 Bill Sommerfeld.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*      This product includes software developed for the NetBSD Project by
*      Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
*    or promote products derived from this software without specific prior
*    written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

/*
* ACPI VALD Driver for Toshiba Libretto L3.
*      This driver is based on acpibat driver.
*/

/*
* Obtain information of Toshiba "GHCI" Method from next URL.
*           http://www.buzzard.org.uk/toshiba/docs.html
*           http://memebeam.org/toys/ToshibaAcpiDriver
*/

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vald_acpi.c,v 1.8 2021/12/07 21:37:36 andvar Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>

#include <dev/acpi/acpica.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>

#define _COMPONENT          ACPI_RESOURCE_COMPONENT
ACPI_MODULE_NAME            ("vald_acpi")

#define GHCI_WORDS 6
#define GHCI_FIFO_EMPTY  0x8c00
#define GHCI_NOT_SUPPORT  0x8000

#define GHCI_BACKLIGHT          0x0002
#define GHCI_ACADAPTOR          0x0003
#define GHCI_FAN                0x0004
#define GHCI_SYSTEM_EVENT_FIFO  0x0016
#define GHCI_DISPLAY_DEVICE     0x001C
#define GHCI_HOTKEY_EVENT       0x001E

#define GHCI_ON                 0x0001
#define GHCI_OFF                0x0000
#define GHCI_ENABLE             0x0001
#define GHCI_DISABLE            0x0000

#define GHCI_CRT                0x0002
#define GHCI_LCD                0x0001


struct vald_acpi_softc {
       device_t sc_dev;                /* base device glue */
       struct acpi_devnode *sc_node;   /* our ACPI devnode */
       int sc_flags;                   /* see below */

       ACPI_HANDLE lcd_handle;         /* lcd handle */
       int *lcd_level;                 /* lcd brightness table */
       int lcd_num;                    /* size of lcd brightness table */
       int lcd_index;                  /* index of lcd brightness table */

       ACPI_INTEGER sc_ac_status;      /* AC adaptor status when attach */
};

static const struct device_compatible_entry compat_data[] = {
       { .compat = "TOS6200" },
       DEVICE_COMPAT_EOL
};

#define LIBRIGHT_HOLD   0x00
#define LIBRIGHT_UP     0x01
#define LIBRIGHT_DOWN   0x02

static int      vald_acpi_match(device_t, cfdata_t, void *);
static void     vald_acpi_attach(device_t, device_t, void *);

static void     vald_acpi_event(void *);
static void     vald_acpi_notify_handler(ACPI_HANDLE, uint32_t, void *);

#define ACPI_NOTIFY_ValdStatusChanged   0x80


static ACPI_STATUS      vald_acpi_ghci_get(struct vald_acpi_softc *, uint32_t,
                                       uint32_t *, uint32_t *);
static ACPI_STATUS      vald_acpi_ghci_set(struct vald_acpi_softc *, uint32_t,
                                       uint32_t, uint32_t *);

static ACPI_STATUS      vald_acpi_libright_get_bus(ACPI_HANDLE, uint32_t,
                                       void *, void **);
static void             vald_acpi_libright_get(struct vald_acpi_softc *);
static void             vald_acpi_libright_set(struct vald_acpi_softc *, int);

static void             vald_acpi_video_switch(struct vald_acpi_softc *);
static void             vald_acpi_fan_switch(struct vald_acpi_softc *);

static ACPI_STATUS      vald_acpi_bcm_set(ACPI_HANDLE, uint32_t);
static ACPI_STATUS      vald_acpi_dssx_set(uint32_t);

CFATTACH_DECL_NEW(vald_acpi, sizeof(struct vald_acpi_softc),
   vald_acpi_match, vald_acpi_attach, NULL, NULL);

/*
* vald_acpi_match:
*
*      Autoconfiguration `match' routine.
*/
static int
vald_acpi_match(device_t parent, cfdata_t match, void *aux)
{
       struct acpi_attach_args *aa = aux;

       return acpi_compatible_match(aa, compat_data);
}

/*
* vald_acpi_attach:
*
*      Autoconfiguration `attach' routine.
*/
static void
vald_acpi_attach(device_t parent, device_t self, void *aux)
{
       struct vald_acpi_softc *sc = device_private(self);
       struct acpi_attach_args *aa = aux;
       ACPI_STATUS rv;
       uint32_t value, result;

       aprint_naive(": Toshiba VALD\n");
       aprint_normal(": Toshiba VALD\n");

       sc->sc_node = aa->aa_node;
       sc->sc_dev = self;

       /* Get AC adaptor status via _PSR. */
       rv = acpi_eval_integer(ACPI_ROOT_OBJECT, "\\_SB_.ADP1._PSR",
           &sc->sc_ac_status);
       if (ACPI_FAILURE(rv))
               aprint_error_dev(self, "Unable to evaluate _PSR: %s\n",
                   AcpiFormatException(rv));
       else
               aprint_verbose_dev(self, "AC adaptor %sconnected\n",
                   (sc->sc_ac_status == 0 ? "not ": ""));

       /* Get LCD backlight status. */
       rv = vald_acpi_ghci_get(sc, GHCI_BACKLIGHT, &value, &result);
       if (ACPI_SUCCESS(rv)) {
               if (result != 0)
                       aprint_error_dev(self,
                           "can't get backlight status error=%d\n", result);
               else
                       aprint_verbose_dev(self, "LCD backlight %s\n",
                           ((value == GHCI_ON) ? "on" : "off"));
       }

       /* Enable SystemEventFIFO,HotkeyEvent */
       rv = vald_acpi_ghci_set(sc, GHCI_SYSTEM_EVENT_FIFO, GHCI_ENABLE,
           &result);
       if (ACPI_SUCCESS(rv) && result != 0)
               aprint_error_dev(self,
                   "can't enable SystemEventFIFO error=%d\n", result);

       rv = vald_acpi_ghci_set(sc, GHCI_HOTKEY_EVENT, GHCI_ENABLE, &result);
       if (ACPI_SUCCESS(rv) && result != 0)
               aprint_error_dev(self, "can't enable HotkeyEvent error=%d\n",
                   result);

       /* Check SystemFIFO events. */
       vald_acpi_event(sc);

       /* Get LCD brightness level via _BCL. */
       vald_acpi_libright_get(sc);

       /* Set LCD brightness level via _BCM. */
       vald_acpi_libright_set(sc, LIBRIGHT_HOLD);

       /* enable vald notify */
       rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "ENAB", NULL, NULL);

       if (ACPI_SUCCESS(rv))
               (void)acpi_register_notify(sc->sc_node,
                   vald_acpi_notify_handler);
}

/*
* vald_acpi_notify_handler:
*
*      Notify handler.
*/
static void
vald_acpi_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *context)
{
       struct vald_acpi_softc *sc;
       device_t self = context;

       sc = device_private(self);

       switch (notify) {

       case ACPI_NOTIFY_ValdStatusChanged:
               (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, vald_acpi_event, sc);
               break;

       default:
               aprint_error_dev(sc->sc_dev,
                   "unknown notify 0x%02X\n", notify);
               break;
       }
}

/*
* vald_acpi_event:
*
*      Check hotkey event and do it, if event occur.
*/
static void
vald_acpi_event(void *arg)
{
       struct vald_acpi_softc *sc = arg;
       ACPI_STATUS rv;
       uint32_t value, result;

       while(1) {
               rv = vald_acpi_ghci_get(sc, GHCI_SYSTEM_EVENT_FIFO, &value,
                   &result);
               if (ACPI_SUCCESS(rv) && result == 0) {

                       switch (value) {
                       case 0x1c3: /* Fn + F9 */
                               break;
                       case 0x1c2: /* Fn + F8 */
                               vald_acpi_fan_switch(sc);
                               break;
                       case 0x1c1: /* Fn + F7 */
                               vald_acpi_libright_set(sc, LIBRIGHT_UP);
                               break;
                       case 0x1c0: /* Fn + F6 */
                               vald_acpi_libright_set(sc, LIBRIGHT_DOWN);
                               break;
                       case 0x1bf: /* Fn + F5 */
                               vald_acpi_video_switch(sc);
                               break;
                       default:
                               break;
                       }
               }
               if (ACPI_FAILURE(rv) || result == GHCI_FIFO_EMPTY)
                       break;
       }
}

/*
* vald_acpi_ghci_get:
*
*      Get value via "GHCI" Method.
*/
static ACPI_STATUS
vald_acpi_ghci_get(struct vald_acpi_softc *sc,
   uint32_t reg, uint32_t *value, uint32_t *result)
{
       ACPI_STATUS rv;
       ACPI_OBJECT Arg[GHCI_WORDS];
       ACPI_OBJECT_LIST ArgList;
       ACPI_OBJECT *param, *PrtElement;
       ACPI_BUFFER buf;
       int             i;

       for (i = 0; i < GHCI_WORDS; i++) {
               Arg[i].Type = ACPI_TYPE_INTEGER;
               Arg[i].Integer.Value = 0;
       }

       Arg[0].Integer.Value = 0xfe00;
       Arg[1].Integer.Value = reg;
       Arg[2].Integer.Value = 0;

       ArgList.Count = GHCI_WORDS;
       ArgList.Pointer = Arg;

       buf.Pointer = NULL;
       buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;

       rv = AcpiEvaluateObject(sc->sc_node->ad_handle,
           "GHCI", &ArgList, &buf);
       if (ACPI_FAILURE(rv)) {
               aprint_error_dev(sc->sc_dev, "failed to evaluate GHCI: %s\n",
                   AcpiFormatException(rv));
               return (rv);
       }

       *result = GHCI_NOT_SUPPORT;
       *value = 0;
       param = buf.Pointer;
       if (param->Type == ACPI_TYPE_PACKAGE) {
               PrtElement = param->Package.Elements;
               if (PrtElement->Type == ACPI_TYPE_INTEGER)
                       *result = PrtElement->Integer.Value;
               PrtElement++;
               PrtElement++;
               if (PrtElement->Type == ACPI_TYPE_INTEGER)
                       *value = PrtElement->Integer.Value;
       }

       if (buf.Pointer)
               ACPI_FREE(buf.Pointer);
       return (rv);
}

/*
* vald_acpi_ghci_set:
*
*      Set value via "GHCI" Method.
*/
static ACPI_STATUS
vald_acpi_ghci_set(struct vald_acpi_softc *sc,
   uint32_t reg, uint32_t value, uint32_t *result)
{
       ACPI_STATUS rv;
       ACPI_OBJECT Arg[GHCI_WORDS];
       ACPI_OBJECT_LIST ArgList;
       ACPI_OBJECT *param, *PrtElement;
       ACPI_BUFFER buf;
       int     i;


       for (i = 0; i < GHCI_WORDS; i++) {
               Arg[i].Type = ACPI_TYPE_INTEGER;
               Arg[i].Integer.Value = 0;
       }

       Arg[0].Integer.Value = 0xff00;
       Arg[1].Integer.Value = reg;
       Arg[2].Integer.Value = value;

       ArgList.Count = GHCI_WORDS;
       ArgList.Pointer = Arg;

       buf.Pointer = NULL;
       buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;

       rv = AcpiEvaluateObject(sc->sc_node->ad_handle,
           "GHCI", &ArgList, &buf);
       if (ACPI_FAILURE(rv)) {
               aprint_error_dev(sc->sc_dev, "failed to evaluate GHCI: %s\n",
                   AcpiFormatException(rv));
               return (rv);
       }

       *result = GHCI_NOT_SUPPORT;
       param = buf.Pointer;
       if (param->Type == ACPI_TYPE_PACKAGE) {
               PrtElement = param->Package.Elements;
               if (PrtElement->Type == ACPI_TYPE_INTEGER)
                       *result = PrtElement->Integer.Value;
       }

       if (buf.Pointer)
               ACPI_FREE(buf.Pointer);
       return (rv);
}

/*
* vald_acpi_libright_get_bus:
*
*      Get LCD brightness level via "_BCL" Method,
*      and save this handle.
*/
static ACPI_STATUS
vald_acpi_libright_get_bus(ACPI_HANDLE handle, uint32_t level,
   void *context, void **status)
{
       struct vald_acpi_softc *sc = context;
       ACPI_STATUS rv;
       ACPI_BUFFER buf;
       ACPI_OBJECT *param, *PrtElement;
       int i, *pi;

       rv = acpi_eval_struct(handle, "_BCL", &buf);
       if (ACPI_FAILURE(rv))
               return (AE_OK);

       sc->lcd_handle = handle;
       param = buf.Pointer;
       if (param->Type == ACPI_TYPE_PACKAGE) {
               printf("_BCL return: %d packages\n", param->Package.Count);

               sc->lcd_num = param->Package.Count;
               sc->lcd_level = ACPI_ALLOCATE(sizeof(int) * sc->lcd_num);
               if (sc->lcd_level == NULL) {
                       if (buf.Pointer)
                               ACPI_FREE(buf.Pointer);
                       return (AE_NO_MEMORY);
               }

               PrtElement = param->Package.Elements;
               pi = sc->lcd_level;
               for (i = 0; i < param->Package.Count; i++) {
                       if (PrtElement->Type == ACPI_TYPE_INTEGER) {
                               *pi = (unsigned)PrtElement->Integer.Value;
                               PrtElement++;
                               pi++;
                       }
               }
               if (sc->sc_ac_status == 1) /* AC adaptor on when attach */
                       sc->lcd_index = sc->lcd_num -1; /* MAX Brightness */
               else
                       sc->lcd_index = 3;

#ifdef ACPI_DEBUG
               pi = sc->lcd_level;
               printf("\t Full Power Level: %d\n", *pi);
               printf("\t on Battery Level: %d\n", *(pi+1));
               printf("\t Possible Level: ");
               for (i = 2;i < sc->lcd_num; i++)
                       printf(" %d", *(pi+i));
               printf("\n");
#endif
       }

       if (buf.Pointer)
               ACPI_FREE(buf.Pointer);
       return (AE_OK);
}

/*
* vald_acpi_libright_get:
*
*      Search node that have "_BCL" Method.
*/
static void
vald_acpi_libright_get(struct vald_acpi_softc *sc)
{
       ACPI_HANDLE parent;
       ACPI_STATUS rv;

       aprint_verbose_dev(sc->sc_dev, "get LCD brightness via _BCL\n");

#ifdef ACPI_DEBUG
       printf("acpi_libright_get: start\n");
#endif
       rv = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &parent);
       if (ACPI_FAILURE(rv))
               return;

       AcpiWalkNamespace(ACPI_TYPE_DEVICE, parent, 100,
           vald_acpi_libright_get_bus, NULL, sc, NULL);
}

/*
* vald_acpi_libright_set:
*
*      Figure up next status and set it.
*/
static void
vald_acpi_libright_set(struct vald_acpi_softc *sc, int UpDown)
{
       uint32_t backlight, backlight_new, result, bright;
       ACPI_STATUS rv;
       int *pi;

       /* Skip,if it does not support _BCL. */
       if (sc->lcd_handle == NULL)
               return;

       /* Get LCD backlight status. */
       rv = vald_acpi_ghci_get(sc, GHCI_BACKLIGHT, &backlight, &result);
       if (ACPI_FAILURE(rv) || result != 0)
               return;

       /* Figure up next status. */
       backlight_new = backlight;
       if (UpDown == LIBRIGHT_UP) {
               if (backlight == 1)
                       sc->lcd_index++;
               else {
                       /* backlight on */
                       backlight_new = 1;
                       sc->lcd_index = 2;
               }
       } else if (UpDown == LIBRIGHT_DOWN) {
               if ((backlight == 1) && (sc->lcd_index > 2))
                       sc->lcd_index--;
               else {
                       /* backlight off */
                       backlight_new = 0;
                       sc->lcd_index = 2;
               }
       }

       /* Check index value. */
       if (sc->lcd_index < 2)
               sc->lcd_index = 2; /* index Minimum Value */
       if (sc->lcd_index >= sc->lcd_num)
               sc->lcd_index = sc->lcd_num - 1;

       /* Set LCD backlight,if status is changed. */
       if (backlight_new != backlight) {
               rv = vald_acpi_ghci_set(sc, GHCI_BACKLIGHT, backlight_new,
                   &result);
               if (ACPI_SUCCESS(rv) && result != 0)
                       aprint_error_dev(sc->sc_dev,
                           "can't set LCD backlight %s error=%x\n",
                           ((backlight_new == 1) ? "on" : "off"), result);
       }

       if (backlight_new == 1) {

               pi = sc->lcd_level;
               bright = *(pi + sc->lcd_index);

               rv = vald_acpi_bcm_set(sc->lcd_handle, bright);
               if (ACPI_FAILURE(rv))
                       aprint_error_dev(sc->sc_dev,
                           "unable to evaluate _BCM: %s\n",
                           AcpiFormatException(rv));
       } else {
               bright = 0;
       }
#ifdef ACPI_DEBUG
       printf("LCD bright");
       printf(" %s", ((UpDown == LIBRIGHT_UP) ? "up":""));
       printf("%s\n", ((UpDown == LIBRIGHT_DOWN) ? "down":""));
       printf("\t acpi_libright_set: Set brightness to %d%%\n", bright);
#endif
}

/*
* vald_acpi_video_switch:
*
*      Get video status(LCD/CRT) and set new video status.
*/
static void
vald_acpi_video_switch(struct vald_acpi_softc *sc)
{
       ACPI_STATUS     rv;
       uint32_t        value, result;

       /* Get video status. */
       rv = vald_acpi_ghci_get(sc, GHCI_DISPLAY_DEVICE, &value, &result);
       if (ACPI_FAILURE(rv))
               return;
       if (result != 0) {
               aprint_error_dev(sc->sc_dev,
                   "can't get video status  error=%x\n", result);
               return;
       }

#ifdef ACPI_DEBUG
       printf("Toggle LCD/CRT\n");
       printf("\t Before switch, video status:   %s",
           (((value & GHCI_LCD) == GHCI_LCD) ? "LCD" : ""));
       printf("%s\n", (((value & GHCI_CRT) == GHCI_CRT) ? "CRT": ""));
#endif

       /* Toggle LCD/CRT */
       if (value & GHCI_LCD) {
               value &= ~GHCI_LCD;
               value |= GHCI_CRT;
       } else if (value & GHCI_CRT){
               value &= ~GHCI_CRT;
               value |= GHCI_LCD;
       }

       rv = vald_acpi_dssx_set(value);
       if (ACPI_FAILURE(rv))
               aprint_error_dev(sc->sc_dev, "unable to evaluate DSSX: %s\n",
                   AcpiFormatException(rv));

}

/*
* vald_acpi_bcm_set:
*
*      Set LCD brightness via "_BCM" Method.
*/
static ACPI_STATUS
vald_acpi_bcm_set(ACPI_HANDLE handle, uint32_t bright)
{
       ACPI_STATUS rv;
       ACPI_OBJECT Arg;
       ACPI_OBJECT_LIST ArgList;

       ArgList.Count = 1;
       ArgList.Pointer = &Arg;

       Arg.Type = ACPI_TYPE_INTEGER;
       Arg.Integer.Value = bright;

       rv = AcpiEvaluateObject(handle, "_BCM", &ArgList, NULL);
       return (rv);
}

/*
* vald_acpi_dssx_set:
*
*      Set value via "\\_SB_.VALX.DSSX" Method.
*/
static ACPI_STATUS
vald_acpi_dssx_set(uint32_t value)
{
       return acpi_eval_set_integer(NULL, "\\_SB_.VALX.DSSX", value);
}

/*
* vald_acpi_fan_switch:
*
*      Get FAN status and set new FAN status.
*/
static void
vald_acpi_fan_switch(struct vald_acpi_softc *sc)
{
       ACPI_STATUS rv;
       uint32_t value, result;

       /* Get FAN status */
       rv = vald_acpi_ghci_get(sc, GHCI_FAN, &value, &result);
       if (ACPI_FAILURE(rv))
               return;
       if (result != 0) {
               aprint_error_dev(sc->sc_dev, "can't get FAN status error=%d\n",
                   result);
               return;
       }

#ifdef ACPI_DEBUG
       printf("Toggle FAN on/off\n");
       printf("\t Before toggle, FAN status %s\n",
           (value == GHCI_OFF ? "off" : "on"));
#endif

       /* Toggle FAN on/off */
       if (value == GHCI_OFF)
               value = GHCI_ON;
       else
               value = GHCI_OFF;

       /* Set FAN new status. */
       rv = vald_acpi_ghci_set(sc, GHCI_FAN, value, &result);
       if (ACPI_FAILURE(rv))
               return;
       if (result != 0) {
               aprint_error_dev(sc->sc_dev, "can't set FAN status error=%d\n",
                   result);
               return;
       }

#ifdef ACPI_DEBUG
       printf("\t After toggle, FAN status %s\n",
           (value == GHCI_OFF ? "off" : "on"));
#endif
}