version 1.20, 2021/08/07 16:19:11 |
version 1.21, 2022/01/14 22:25:49 |
Line 60 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 60 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/systm.h> |
#include <sys/systm.h> |
#include <sys/device.h> |
#include <sys/device.h> |
#include <sys/kmem.h> |
#include <sys/kmem.h> |
|
#include <sys/workqueue.h> |
|
|
#include <dev/i2c/i2cvar.h> |
#include <dev/i2c/i2cvar.h> |
#include <dev/i2c/ihidev.h> |
#include <dev/i2c/ihidev.h> |
Line 110 static int ihidev_detach(device_t, int); |
|
Line 110 static int ihidev_detach(device_t, int); |
|
CFATTACH_DECL_NEW(ihidev, sizeof(struct ihidev_softc), |
CFATTACH_DECL_NEW(ihidev, sizeof(struct ihidev_softc), |
ihidev_match, ihidev_attach, ihidev_detach, NULL); |
ihidev_match, ihidev_attach, ihidev_detach, NULL); |
|
|
static bool ihiddev_intr_init(struct ihidev_softc *); |
static bool ihidev_intr_init(struct ihidev_softc *); |
static void ihiddev_intr_fini(struct ihidev_softc *); |
static void ihidev_intr_fini(struct ihidev_softc *); |
|
|
static bool ihidev_suspend(device_t, const pmf_qual_t *); |
static bool ihidev_suspend(device_t, const pmf_qual_t *); |
static bool ihidev_resume(device_t, const pmf_qual_t *); |
static bool ihidev_resume(device_t, const pmf_qual_t *); |
static int ihidev_hid_command(struct ihidev_softc *, int, void *, bool); |
static int ihidev_hid_command(struct ihidev_softc *, int, void *, bool); |
static int ihidev_intr(void *); |
static int ihidev_intr(void *); |
static void ihidev_softintr(void *); |
static void ihidev_work(struct work *, void *); |
static int ihidev_reset(struct ihidev_softc *, bool); |
static int ihidev_reset(struct ihidev_softc *, bool); |
static int ihidev_hid_desc_parse(struct ihidev_softc *); |
static int ihidev_hid_desc_parse(struct ihidev_softc *); |
|
|
Line 160 ihidev_attach(device_t parent, device_t |
|
Line 160 ihidev_attach(device_t parent, device_t |
|
sc->sc_dev = self; |
sc->sc_dev = self; |
sc->sc_tag = ia->ia_tag; |
sc->sc_tag = ia->ia_tag; |
sc->sc_addr = ia->ia_addr; |
sc->sc_addr = ia->ia_addr; |
mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM); |
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); |
|
|
sc->sc_phandle = ia->ia_cookie; |
sc->sc_phandle = ia->ia_cookie; |
if (ia->ia_cookietype != I2C_COOKIE_ACPI) { |
if (ia->ia_cookietype != I2C_COOKIE_ACPI) { |
aprint_error(": unsupported device tree type\n"); |
aprint_error(": unsupported device tree type\n"); |
return; |
return; |
} |
} |
if (! ihidev_acpi_get_info(sc)) { |
if (!ihidev_acpi_get_info(sc)) { |
return; |
return; |
} |
} |
|
|
Line 208 ihidev_attach(device_t parent, device_t |
|
Line 208 ihidev_attach(device_t parent, device_t |
|
repsz)); |
repsz)); |
} |
} |
sc->sc_ibuf = kmem_zalloc(sc->sc_isize, KM_SLEEP); |
sc->sc_ibuf = kmem_zalloc(sc->sc_isize, KM_SLEEP); |
if (! ihiddev_intr_init(sc)) { |
if (!ihidev_intr_init(sc)) { |
return; |
return; |
} |
} |
|
|
Line 245 ihidev_attach(device_t parent, device_t |
|
Line 245 ihidev_attach(device_t parent, device_t |
|
} |
} |
|
|
/* power down until we're opened */ |
/* power down until we're opened */ |
if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF, false)) { |
if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF, |
|
false)) { |
aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
return; |
return; |
} |
} |
|
|
ihidev_detach(device_t self, int flags) |
ihidev_detach(device_t self, int flags) |
{ |
{ |
struct ihidev_softc *sc = device_private(self); |
struct ihidev_softc *sc = device_private(self); |
|
int error; |
|
|
|
error = config_detach_children(self, flags); |
|
if (error) |
|
return error; |
|
|
|
pmf_device_deregister(self); |
|
ihidev_intr_fini(sc); |
|
|
|
if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF, |
|
true)) |
|
aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
|
|
mutex_enter(&sc->sc_intr_lock); |
|
ihiddev_intr_fini(sc); |
|
if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
|
&I2C_HID_POWER_OFF, true)) |
|
aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
|
mutex_exit(&sc->sc_intr_lock); |
|
if (sc->sc_ibuf != NULL) { |
if (sc->sc_ibuf != NULL) { |
kmem_free(sc->sc_ibuf, sc->sc_isize); |
kmem_free(sc->sc_ibuf, sc->sc_isize); |
sc->sc_ibuf = NULL; |
sc->sc_ibuf = NULL; |
Line 272 ihidev_detach(device_t self, int flags) |
|
Line 279 ihidev_detach(device_t self, int flags) |
|
if (sc->sc_report != NULL) |
if (sc->sc_report != NULL) |
kmem_free(sc->sc_report, sc->sc_reportlen); |
kmem_free(sc->sc_report, sc->sc_reportlen); |
|
|
pmf_device_deregister(self); |
mutex_destroy(&sc->sc_lock); |
return (0); |
|
|
return 0; |
} |
} |
|
|
static bool |
static bool |
Line 281 ihidev_suspend(device_t self, const pmf_ |
|
Line 289 ihidev_suspend(device_t self, const pmf_ |
|
{ |
{ |
struct ihidev_softc *sc = device_private(self); |
struct ihidev_softc *sc = device_private(self); |
|
|
mutex_enter(&sc->sc_intr_lock); |
mutex_enter(&sc->sc_lock); |
if (sc->sc_refcnt > 0) { |
if (sc->sc_refcnt > 0) { |
printf("ihidev power off\n"); |
printf("ihidev power off\n"); |
if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
&I2C_HID_POWER_OFF, true)) |
&I2C_HID_POWER_OFF, true)) |
aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
} |
} |
mutex_exit(&sc->sc_intr_lock); |
mutex_exit(&sc->sc_lock); |
return true; |
return true; |
} |
} |
|
|
Line 297 ihidev_resume(device_t self, const pmf_q |
|
Line 305 ihidev_resume(device_t self, const pmf_q |
|
{ |
{ |
struct ihidev_softc *sc = device_private(self); |
struct ihidev_softc *sc = device_private(self); |
|
|
mutex_enter(&sc->sc_intr_lock); |
mutex_enter(&sc->sc_lock); |
if (sc->sc_refcnt > 0) { |
if (sc->sc_refcnt > 0) { |
printf("ihidev power reset\n"); |
printf("ihidev power reset\n"); |
ihidev_reset(sc, true); |
ihidev_reset(sc, true); |
} |
} |
mutex_exit(&sc->sc_intr_lock); |
mutex_exit(&sc->sc_lock); |
return true; |
return true; |
} |
} |
|
|
Line 646 ihidev_hid_desc_parse(struct ihidev_soft |
|
Line 654 ihidev_hid_desc_parse(struct ihidev_soft |
|
} |
} |
|
|
static bool |
static bool |
ihiddev_intr_init(struct ihidev_softc *sc) |
ihidev_intr_init(struct ihidev_softc *sc) |
{ |
{ |
#if NACPICA > 0 |
#if NACPICA > 0 |
ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; |
ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; |
Line 682 ihiddev_intr_init(struct ihidev_softc *s |
|
Line 690 ihiddev_intr_init(struct ihidev_softc *s |
|
aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", |
aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", |
acpi_intr_string(sc->sc_ih, buf, sizeof(buf))); |
acpi_intr_string(sc->sc_ih, buf, sizeof(buf))); |
|
|
sc->sc_sih = softint_establish(SOFTINT_SERIAL, ihidev_softintr, sc); |
if (workqueue_create(&sc->sc_wq, device_xname(sc->sc_dev), ihidev_work, |
if (sc->sc_sih == NULL) { |
sc, PRI_NONE, IPL_TTY, WQ_MPSAFE)) { |
aprint_error_dev(sc->sc_dev, |
aprint_error_dev(sc->sc_dev, |
"can't establish soft interrupt\n"); |
"can't establish workqueue\n"); |
return false; |
return false; |
} |
} |
|
sc->sc_work_pending = 0; |
|
|
return true; |
return true; |
#else |
#else |
Line 697 ihiddev_intr_init(struct ihidev_softc *s |
|
Line 706 ihiddev_intr_init(struct ihidev_softc *s |
|
} |
} |
|
|
static void |
static void |
ihiddev_intr_fini(struct ihidev_softc *sc) |
ihidev_intr_fini(struct ihidev_softc *sc) |
{ |
{ |
#if NACPICA > 0 |
#if NACPICA > 0 |
if (sc->sc_ih != NULL) { |
if (sc->sc_ih != NULL) { |
acpi_intr_disestablish(sc->sc_ih); |
acpi_intr_disestablish(sc->sc_ih); |
} |
} |
if (sc->sc_sih != NULL) { |
if (sc->sc_wq != NULL) { |
softint_disestablish(sc->sc_sih); |
workqueue_destroy(sc->sc_wq); |
} |
} |
#endif |
#endif |
} |
} |
Line 736 ihidev_intr(void *arg) |
|
Line 745 ihidev_intr(void *arg) |
|
{ |
{ |
struct ihidev_softc * const sc = arg; |
struct ihidev_softc * const sc = arg; |
|
|
mutex_enter(&sc->sc_intr_lock); |
|
|
|
/* |
/* |
* Schedule our soft interrupt handler. If we're using a level- |
* Schedule our work. If we're using a level-triggered |
* triggered interrupt, we have to mask it off while we wait |
* interrupt, we have to mask it off while we wait for service. |
* for service. |
|
*/ |
*/ |
softint_schedule(sc->sc_sih); |
if (atomic_swap_uint(&sc->sc_work_pending, 1) == 0) |
|
workqueue_enqueue(sc->sc_wq, &sc->sc_work, NULL); |
ihidev_intr_mask(sc); |
ihidev_intr_mask(sc); |
|
|
mutex_exit(&sc->sc_intr_lock); |
|
|
|
return 1; |
return 1; |
} |
} |
|
|
static void |
static void |
ihidev_softintr(void *arg) |
ihidev_work(struct work *wk, void *arg) |
{ |
{ |
struct ihidev_softc * const sc = arg; |
struct ihidev_softc * const sc = arg; |
struct ihidev *scd; |
struct ihidev *scd; |
Line 761 ihidev_softintr(void *arg) |
|
Line 766 ihidev_softintr(void *arg) |
|
u_char *p; |
u_char *p; |
u_int rep = 0; |
u_int rep = 0; |
|
|
mutex_enter(&sc->sc_intr_lock); |
atomic_store_relaxed(&sc->sc_work_pending, 0); |
|
|
|
mutex_enter(&sc->sc_lock); |
|
|
iic_acquire_bus(sc->sc_tag, 0); |
iic_acquire_bus(sc->sc_tag, 0); |
res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, |
res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, |
sc->sc_ibuf, sc->sc_isize, 0); |
sc->sc_ibuf, sc->sc_isize, 0); |
iic_release_bus(sc->sc_tag, 0); |
iic_release_bus(sc->sc_tag, 0); |
mutex_exit(&sc->sc_intr_lock); |
|
|
|
if (res != 0) |
if (res != 0) |
goto out; |
goto out; |
|
|
Line 807 ihidev_softintr(void *arg) |
|
Line 813 ihidev_softintr(void *arg) |
|
scd->sc_intr(scd, p, psize); |
scd->sc_intr(scd, p, psize); |
|
|
out: |
out: |
|
mutex_exit(&sc->sc_lock); |
|
|
/* |
/* |
* If our interrupt is level-triggered, re-enable it now. |
* If our interrupt is level-triggered, re-enable it now. |
*/ |
*/ |
Line 837 ihidev_print(void *aux, const char *pnp) |
|
Line 845 ihidev_print(void *aux, const char *pnp) |
|
|
|
if (iha->reportid == IHIDEV_CLAIM_ALLREPORTID) |
if (iha->reportid == IHIDEV_CLAIM_ALLREPORTID) |
return (QUIET); |
return (QUIET); |
|
|
if (pnp) |
if (pnp) |
aprint_normal("hid at %s", pnp); |
aprint_normal("hid at %s", pnp); |
|
|
Line 974 ihidev_set_report(struct device *dev, in |
|
Line 982 ihidev_set_report(struct device *dev, in |
|
|
|
static bool |
static bool |
ihidev_acpi_get_info(struct ihidev_softc *sc) |
ihidev_acpi_get_info(struct ihidev_softc *sc) |
{ |
{ |
ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; |
ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; |
ACPI_STATUS status; |
ACPI_STATUS status; |
ACPI_INTEGER val; |
ACPI_INTEGER val; |