Index: src/sys/arch/sparc64/dev/envmon_ebus.c =================================================================== RCS file: src/sys/arch/sparc64/dev/envmon_ebus.c diff -N src/sys/arch/sparc64/dev/envmon_ebus.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/sys/arch/sparc64/dev/envmon_ebus.c 30 Jul 2026 13:49:15 -0000 @@ -0,0 +1,351 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * 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. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +#define ENVMON_MACHINE_V245 0xf5 +#define ENVMON_MACHINE_V215 0xd7 + +#define ENVMON_PIC_MAX_TEMPS 1 + +struct envmon_epic_fans { + const uint8_t reg; + const char *desc; +}; +static struct envmon_epic_fans envmon_epic_fans_table[] = { + { ENVMON_EPC_FAN_FT0_F0, "FT0.F0" }, + { ENVMON_EPC_FAN_FT1_F0, "FT1.F0" }, + { ENVMON_EPC_FAN_FT2_F0, "FT2.F0" }, + { ENVMON_EPC_FAN_FT3_F0, "FT3.F0" }, + { ENVMON_EPC_FAN_FT4_F0, "FT4.F0" }, + { ENVMON_EPC_FAN_FT5_F0, "FT5.F0" }, + { ENVMON_EPC_FAN_FT0_F1, "FT0.F1" }, + { ENVMON_EPC_FAN_FT1_F1, "FT1.F1" }, + { ENVMON_EPC_FAN_FT2_F1, "FT2.F1" }, + { ENVMON_EPC_FAN_FT3_F1, "FT3.F1" }, + { ENVMON_EPC_FAN_FT4_F1, "FT4.F1" }, + { ENVMON_EPC_FAN_FT5_F1, "FT5.F1" }, +}; +#define ENVMON_EPC_MAX_FANS (sizeof(envmon_epic_fans_table) / \ + sizeof(envmon_epic_fans_table[0])) +#define ENVMON_EPC_DIV_V245 5625 +#define ENVMON_EPC_DIV_V215 11250 +#define ENVMON_EPC_CRIT_SPEED_V245 2022 /* From ALOM */ +#define ENVMON_EPC_CRIT_SPEED_V215 5012 /* From ALOM */ +#define ENVMON_EPC_VAL_TO_SPEED(div, val) ((div * 60) / val) + +struct envmon_epic_led_value { + uint8_t reg, v_on; + const char *desc; +}; +static struct envmon_epic_led_value envmon_epic_led_value_table[] = { + { ENVMON_EPC_LED6, ENVMON_EPC_LED6_LOC, "locator" }, + { ENVMON_EPC_LED6, ENVMON_EPC_LED6_FLT, "fault" }, + { ENVMON_EPC_LED6, ENVMON_EPC_LED6_FLT_FSH, "fault_flash" }, + { ENVMON_EPC_LED6, ENVMON_EPC_LED6_PWR, "power" }, + { ENVMON_EPC_LED6, ENVMON_EPC_LED6_TF_FLT, "top_fan_fault" }, + { ENVMON_EPC_LED6, ENVMON_EPC_LED6_TF_FLT_FSH, "top_fan_fault_flash" }, +}; +#define ENVMON_MAX_LEDS (sizeof(envmon_epic_led_value_table) / \ + sizeof(envmon_epic_led_value_table[0])) + +struct envmon_epic_led { + void *cookie; + struct led_device *led; + uint8_t reg, v_on; +}; + +#define ENVMON_MAX_SENSORS MAX(ENVMON_PIC_MAX_TEMPS, ENVMON_EPC_MAX_FANS) + +struct envmon_softc { + device_t sc_dev; + + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + + int sc_machine_type; + uint8_t sc_fan_reg[ENVMON_EPC_MAX_FANS]; + struct sysmon_envsys *sc_sme; + envsys_data_t sc_sensor[ENVMON_MAX_SENSORS]; + struct envmon_epic_led sc_leds[ENVMON_MAX_LEDS]; +}; + +static int envmon_ebus_match(device_t, cfdata_t, void *); +static void envmon_ebus_attach(device_t, device_t, void *); +static void envmon_pic_attach(struct envmon_softc *, int); +static void envmon_epic_attach(struct envmon_softc *, int); +void envmon_pic_refresh(struct sysmon_envsys *, envsys_data_t *); +void envmon_epic_refresh(struct sysmon_envsys *, envsys_data_t *); +int envmon_epic_get_led(void *); +void envmon_epic_set_led(void *, int); + +CFATTACH_DECL_NEW(envmon_ebus, sizeof(struct envmon_softc), + envmon_ebus_match, envmon_ebus_attach, NULL, NULL); + +#define envmon_read(sc, reg) \ + bus_space_read_1(sc->sc_bst, sc->sc_bsh, reg) +#define envmon_write(sc, reg, val) \ + bus_space_write_1(sc->sc_bst, sc->sc_bsh, reg, val) + +static int +envmon_ebus_match(device_t parent, cfdata_t cf, void *aux) +{ + struct ebus_attach_args *ea = aux; + char *compat; + + if (strcmp("env-monitor", ea->ea_name) != 0) + return 0; + + compat = prom_getpropstring(ea->ea_node, "compatible"); + if (compat != NULL && (!strcmp(compat, "SUNW,ebus-pic16f747-env") || + !strcmp(compat, "epic"))) + return 1; + + return 0; +} + +static void +envmon_ebus_attach(device_t parent, device_t self, void *aux) +{ + struct envmon_softc *sc = device_private(self); + struct ebus_attach_args *ea = aux; + char compat[32]; + int sz; + + sc->sc_dev = self; + sc->sc_bst = ea->ea_bustag; + + sz = ea->ea_reg[0].size; + + if (bus_space_map(sc->sc_bst, + EBUS_ADDR_FROM_REG(&ea->ea_reg[0]), + sz, 0, + &sc->sc_bsh) != 0) { + aprint_error(": can't map register\n"); + return; + } + + sc->sc_sme = sysmon_envsys_create(); + sc->sc_sme->sme_name = device_xname(self); + sc->sc_sme->sme_cookie = sc; + + OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)); + + if (!strcmp(compat, "SUNW,ebus-pic16f747-env")) { + envmon_pic_attach(sc, ea->ea_node); + } + if (!strcmp(compat, "epic")) { + envmon_epic_attach(sc, ea->ea_node); + } +} + +/* + * We only add the front panel temperature here. + * Other U45 sensors are handled by the ADT7462 driver. + */ +static void +envmon_pic_attach(struct envmon_softc *sc, int node) +{ + int32_t vers; + + vers = prom_getpropint(node, "version", 0); + aprint_normal(": pic16f747 (ver. %d)\n", vers); + + sc->sc_sme->sme_refresh = envmon_pic_refresh; + strlcpy(sc->sc_sensor[0].desc, "Front_panel", + sizeof(sc->sc_sensor[0].desc)); + sc->sc_sensor[0].units = ENVSYS_STEMP; + sc->sc_sensor[0].state = ENVSYS_SINVALID; + sc->sc_sensor[0].flags = ENVSYS_FHAS_ENTROPY; + if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[0])) { + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + aprint_error_dev(sc->sc_dev, + "unable to attach sensor to sysmon\n"); + return; + } + if (sysmon_envsys_register(sc->sc_sme)) { + aprint_error_dev(sc->sc_dev, + "unable to register with sysmon\n"); + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + return; + } + + return; +} + +static void +envmon_epic_attach(struct envmon_softc *sc, int node) +{ + const char *vers; + uint16_t fan_mask; + int i; + + vers = prom_getpropstring(node, "version"); + aprint_normal(": epic (ver. %s)\n", vers); + + /* Fans */ + if (!strcmp(machine_model, "SUNW,Sun-Fire-V245")) { + sc->sc_machine_type = ENVMON_MACHINE_V245; + fan_mask = 0x003f; /* 6 fans */ + } else { /* V215 */ + sc->sc_machine_type = ENVMON_MACHINE_V215; + fan_mask = 0x0fff; /* 12 fans */ + } + + sc->sc_sme->sme_refresh = envmon_epic_refresh; + for (i = 0; i < ENVMON_EPC_MAX_FANS; i++) { + if (!(fan_mask & (1 << i))) + continue; + + strlcpy(sc->sc_sensor[i].desc, envmon_epic_fans_table[i].desc, + sizeof(sc->sc_sensor[i].desc)); + sc->sc_sensor[i].units = ENVSYS_SFANRPM; + sc->sc_sensor[i].state = ENVSYS_SINVALID; + sc->sc_sensor[i].flags = ENVSYS_FMONCRITICAL; + sc->sc_fan_reg[i] = envmon_epic_fans_table[i].reg; + if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[i])) { + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + aprint_error_dev(sc->sc_dev, + "unable to attach sensor to sysmon\n"); + return; + } + } + if (sysmon_envsys_register(sc->sc_sme)) { + aprint_error_dev(sc->sc_dev, + "unable to register with sysmon\n"); + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + return; + } + + /* LED's */ + for (i = 0; i < ENVMON_MAX_LEDS; i++) { + sc->sc_leds[i].cookie = sc; + sc->sc_leds[i].reg = envmon_epic_led_value_table[i].reg; + sc->sc_leds[i].v_on = envmon_epic_led_value_table[i].v_on; + led_attach(envmon_epic_led_value_table[i].desc, + &sc->sc_leds[i], envmon_epic_get_led, envmon_epic_set_led); + } + return; +} + +void +envmon_pic_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) +{ + struct envmon_softc *sc = sme->sme_cookie; + uint8_t val; + + envmon_write(sc, ENVMON_ADDR, ENVMON_PIC_TEMP_FP); + val = envmon_read(sc, ENVMON_DATA); + edata->value_cur = ENVMON_PIC_TEMP_BASE + val * 1000000; + edata->state = ENVSYS_SVALID; +} + +void +envmon_epic_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) +{ + struct envmon_softc *sc = sme->sme_cookie; + uint8_t val; + int div, crit; + + switch (sc->sc_machine_type) { + case ENVMON_MACHINE_V215: + div = ENVMON_EPC_DIV_V215; + crit = ENVMON_EPC_CRIT_SPEED_V215; + break; + case ENVMON_MACHINE_V245: + default: + div = ENVMON_EPC_DIV_V245; + crit = ENVMON_EPC_CRIT_SPEED_V245; + break; + } + + envmon_write(sc, ENVMON_ADDR, sc->sc_fan_reg[edata->sensor]); + val = envmon_read(sc, ENVMON_DATA); + if (val == 0xff || val == 0x0) + edata->value_cur = 0; + else { + edata->value_cur = ENVMON_EPC_VAL_TO_SPEED(div, val); + } + if (edata->value_cur < crit) + edata->state = ENVSYS_SCRITICAL; + else + edata->state = ENVSYS_SVALID; + return; +} + +int +envmon_epic_get_led(void *cookie) +{ + struct envmon_epic_led *l = cookie; + struct envmon_softc *sc = l->cookie; + uint8_t val; + + envmon_write(sc, ENVMON_ADDR, l->reg); + val = envmon_read(sc, ENVMON_DATA); + return ((val & l->v_on) == l->v_on); +} + +void +envmon_epic_set_led(void *cookie, int val) +{ + struct envmon_epic_led *l = cookie; + struct envmon_softc *sc = l->cookie; + uint8_t oldval, newval; + + envmon_write(sc, ENVMON_ADDR, l->reg); + oldval = envmon_read(sc, ENVMON_DATA); + newval = oldval & ~(l->v_on); + newval |= val ? l->v_on : 0x0; + if (newval != oldval) + envmon_write(sc, ENVMON_DATA, newval); +} Index: src/sys/arch/sparc64/dev/envmon_ebusreg.h =================================================================== RCS file: src/sys/arch/sparc64/dev/envmon_ebusreg.h diff -N src/sys/arch/sparc64/dev/envmon_ebusreg.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/sys/arch/sparc64/dev/envmon_ebusreg.h 30 Jul 2026 13:49:16 -0000 @@ -0,0 +1,100 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * 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. + */ + +/* + * Register definitions for "env-monitor": + * "SUNW,ebus-pic16f747-env" on U45/U25 + * "epic" on V245/V215 + * Registers are indirectly accessed through a address and data register. + * + * Register contents are mostly copies of registers from other chips on + * the i2c bus. On V245/V215, they appear to be only from the chip at + * i2c addr 0x5a. On the U45, from the adm7462 and lm95221. + * However, some registers contain sensor data not available elsewhere, + * e.g. front panel temperature on U45. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#define ENVMON_COMPAT_PIC "SUNW,ebus-pic16f747-env" +#define ENVMON_COMPAT_EPIC "epic" + +#define ENVMON_DATA 0x40 /* Read/write register data */ +#define ENVMON_ADDR 0x41 /* Register address */ + +/* U45/U25 registers */ +#define ENVMON_PIC_FAN0 0x00 /* Fan 0 speed */ +#define ENVMON_PIC_FAN1 0x01 /* Fan 1 speed */ +#define ENVMON_PIC_FAN2 0x02 /* Fan 2 speed */ +#define ENVMON_PIC_FAN3 0x03 /* Fan 3 speed */ +#define ENVMON_PIC_FAN4 0x04 /* Fan 4 speed */ + +#define ENVMON_PIC_TEMP_ADT 0x06 /* ADT7462 temperature */ +#define ENVMON_PIC_TEMP_CPU0 0x07 /* CPU 0 temperature */ +#define ENVMON_PIC_TEMP_CPU1 0x08 /* CPU 1 temperature */ +#define ENVMON_PIC_TEMP_MB 0x09 /* Motherboard temperature */ +#define ENVMON_PIC_TEMP_LM 0x0a /* LM95221 temperature */ +#define ENVMON_PIC_TEMP_FIRE 0x0b /* FireASIC temperature */ +#define ENVMON_PIC_TEMP_LSI 0x0c /* LSI1064 temperature */ +#define ENVMON_PIC_TEMP_FP 0x0d /* Front Panel temperature */ +#define ENVMON_PIC_TEMP_BASE 209150000 /* -64'C */ + +#define ENVMON_PIC_VOLT_15_1 0x0f /* 1.5v #1 */ +#define ENVMON_PIC_VOLT_15_2 0x10 /* 1.5v #2 */ +#define ENVMON_PIC_SCALE_1_5V 7800 + +#define ENVMON_PIC_TEMP_PSU 0x13 /* Power Supply temperature */ + +/* V245/V215 registers */ +#define ENVMON_EPC_FW 0x05 /* Firmware version */ +#define ENVMON_EPC_LED6 0x06 /* Locator / power LED's */ +#define ENVMON_EPC_FAN_FT0_F0 0x0e /* Fan tray 0 fan 0 */ +#define ENVMON_EPC_FAN_FT1_F0 0x0f /* Fan tray 1 fan 0 */ +#define ENVMON_EPC_FAN_FT2_F0 0x10 /* Fan tray 2 fan 0 */ +#define ENVMON_EPC_FAN_FT3_F0 0x11 /* Fan tray 3 fan 0 */ +#define ENVMON_EPC_FAN_FT4_F0 0x12 /* Fan tray 4 fan 0 */ +#define ENVMON_EPC_FAN_FT5_F0 0x13 /* Fan tray 5 fan 0 */ +#define ENVMON_EPC_FAN_FT0_F1 0x14 /* Fan tray 0 fan 1 (V215 only) */ +#define ENVMON_EPC_FAN_FT1_F1 0x15 /* Fan tray 1 fan 1 (V215 only) */ +#define ENVMON_EPC_FAN_FT2_F1 0x16 /* Fan tray 2 fan 1 (V215 only) */ +#define ENVMON_EPC_FAN_FT3_F1 0x17 /* Fan tray 3 fan 1 (V215 only) */ +#define ENVMON_EPC_FAN_FT4_F1 0x18 /* Fan tray 4 fan 1 (V215 only) */ +#define ENVMON_EPC_FAN_FT5_F1 0x19 /* Fan tray 5 fan 1 (V215 only) */ + +/* 0x06: Front panel LED's */ +#define ENVMON_EPC_LED6_LOC 0x01 /* Locator */ +#define ENVMON_EPC_LED6_LOC2 0x02 /* Locator too */ +#define ENVMON_EPC_LED6_FLT 0x04 /* Fault */ +#define ENVMON_EPC_LED6_FLT_FSH 0x08 /* Fault flashing */ +#define ENVMON_EPC_LED6_PWR 0x10 /* Power */ +#define ENVMON_EPC_LED6_TF_FLT 0x40 /* Top fan fault */ +#define ENVMON_EPC_LED6_TF_FLT_FSH 0x80 /* Top fan fault flashing */ Index: src/share/man/man4/man4.sparc64/envmon.4 =================================================================== RCS file: src/share/man/man4/man4.sparc64/envmon.4 diff -N src/share/man/man4/man4.sparc64/envmon.4 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/share/man/man4/man4.sparc64/envmon.4 30 Jul 2026 13:49:16 -0000 @@ -0,0 +1,68 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2026 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Julian Coleman. +.\" +.\" 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. +.\" +.Dd July 30, 2026 +.Dt ENVMON 4 +.Os +.Sh NAME +.Nm auxfan +.Nd Sun Ultra and Fire environmental monitor +.Sh SYNOPSIS +.Cd "envmon* at ebus?" +.Sh DESCRIPTION +The +.Nm +driver provides support for the +.Ql env-monitor +sensor found on Sun Ultra 25/45 and Fire V215/V245 machines. +On the Ultra machines, the sensor reports the front panel temperature. +On the Fire machines, the sensor reports the front fan tray fan speeds, +and controls the front and rear status LED's. +.Pp +Temperature and fan speeds are reported via the +.Xr envsys 4 +framework, whilst LED's are exposed via the +.Xr led 4 +framework. +.Sh SEE ALSO +.Xr envsys 4 , +.Xr iic 4 , +.Xr intro 4 +.Xr led 4 , +.Xr envstat 8 , +.Sh HISTORY +The +.Nm +driver first appeared in +.Nx 11.1 . +.Sh AUTHORS +The +.Nm +driver was written by +.An Julian Coleman Aq Mt jcoleman@NetBSD.org .