From 251df1a41e0dd2b0e1dbdbada2ba906ea536ce6c Mon Sep 17 00:00:00 2001 From: macallan Date: Thu, 14 May 2009 01:10:19 +0000 Subject: [PATCH] add a driver for the Indy's front panel buttons. - power button presses are reported to sysmon - volume control buttons are reported to PMF TODO: add better key repeat code. The hardware keeps firing interrupts at us whenever a button is down and not on - say - status change. If we don't clear the interrupt we'll be fired on until someone clears it. We should probably disable the entire interrupt and occasionally poll for button release. --- sys/arch/sgimips/hpc/files.hpc | 5 +- sys/arch/sgimips/hpc/hpc.c | 10 ++- sys/arch/sgimips/hpc/panel.c | 149 +++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 sys/arch/sgimips/hpc/panel.c diff --git a/sys/arch/sgimips/hpc/files.hpc b/sys/arch/sgimips/hpc/files.hpc index 4b0cd608b6ca..083faaa0b522 100644 --- a/sys/arch/sgimips/hpc/files.hpc +++ b/sys/arch/sgimips/hpc/files.hpc @@ -1,4 +1,4 @@ -# $NetBSD: files.hpc,v 1.13 2009/02/12 06:33:57 rumble Exp $ +# $NetBSD: files.hpc,v 1.14 2009/05/14 01:10:19 macallan Exp $ device sq: arp, ether, ifnet attach sq at hpc @@ -25,3 +25,6 @@ device pi1ppc: parport attach pi1ppc at hpc file arch/sgimips/hpc/pi1ppc.c pi1ppc +device panel : sysmon_power, sysmon_taskq +attach panel at hpc +file arch/sgimips/hpc/panel.c panel diff --git a/sys/arch/sgimips/hpc/hpc.c b/sys/arch/sgimips/hpc/hpc.c index 71537e2b12c1..0b4117aa561d 100644 --- a/sys/arch/sgimips/hpc/hpc.c +++ b/sys/arch/sgimips/hpc/hpc.c @@ -1,4 +1,4 @@ -/* $NetBSD: hpc.c,v 1.61 2009/02/12 06:33:57 rumble Exp $ */ +/* $NetBSD: hpc.c,v 1.62 2009/05/14 01:10:19 macallan Exp $ */ /* * Copyright (c) 2000 Soren S. Jorvang @@ -35,7 +35,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: hpc.c,v 1.61 2009/02/12 06:33:57 rumble Exp $"); +__KERNEL_RCSID(0, "$NetBSD: hpc.c,v 1.62 2009/05/14 01:10:19 macallan Exp $"); #include #include @@ -202,6 +202,12 @@ static const struct hpc_device hpc3_devices[] = { -1, HPCDEV_IP22 | HPCDEV_IP24 }, + { "panel", /* Indy front panel */ + HPC_BASE_ADDRESS_0, + HPC3_PBUS_CH6_DEVREGS + IOC_PANEL, 0, + 9, + HPCDEV_IP24 }, + { NULL, 0, 0, 0, diff --git a/sys/arch/sgimips/hpc/panel.c b/sys/arch/sgimips/hpc/panel.c new file mode 100644 index 000000000000..bbbe928e98e2 --- /dev/null +++ b/sys/arch/sgimips/hpc/panel.c @@ -0,0 +1,149 @@ +/* $NetBSD: panel.c,v 1.1 2009/05/14 01:10:19 macallan Exp $ */ + +/*- + * Copyright (c) 2009 Michael Lorenz + * 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. + * + * 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: panel.c,v 1.1 2009/05/14 01:10:19 macallan Exp $"); + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include + +struct panel_softc { + device_t sc_dev; + struct sysmon_pswitch sc_pbutton; + bus_space_tag_t sc_tag; + bus_space_handle_t sc_hreg; + int sc_last, sc_fired; +}; + +static int panel_match(device_t, cfdata_t, void *); +static void panel_attach(device_t, device_t, void *); + +static int panel_intr(void *); +static void panel_powerbutton(void *); + +CFATTACH_DECL_NEW(panel, sizeof(struct panel_softc), + panel_match, + panel_attach, + NULL, + NULL); + +static int +panel_match(device_t parent, cfdata_t match, void *aux) +{ + if (mach_type == MACH_SGI_IP22) + return 1; + + return 0; +} + +static void +panel_attach(device_t parent, device_t self, void *aux) +{ + struct panel_softc *sc; + struct hpc_attach_args *haa; + + sc = device_private(self); + sc->sc_dev = self; + haa = aux; + sc->sc_tag = haa->ha_st; + sc->sc_fired = 0; + + aprint_normal("\n"); + if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_devoff, + 0x1, /* just a single register */ + &sc->sc_hreg)) { + aprint_error(": unable to map panel register\n"); + return; + } + + if ((cpu_intr_establish(haa->ha_irq, IPL_BIO, + panel_intr, sc)) == NULL) { + printf(": unable to establish interrupt!\n"); + return; + } + + sc->sc_last = 0; + + sysmon_task_queue_init(); + memset(&sc->sc_pbutton, 0, sizeof(struct sysmon_pswitch)); + sc->sc_pbutton.smpsw_name = device_xname(sc->sc_dev); + sc->sc_pbutton.smpsw_type = PSWITCH_TYPE_POWER; + if (sysmon_pswitch_register(&sc->sc_pbutton) != 0) + aprint_error_dev(sc->sc_dev, + "unable to register power button with sysmon\n"); + pmf_device_register(self, NULL, NULL); +} + +static int +panel_intr(void *cookie) +{ + struct panel_softc *sc = cookie; + uint8_t reg; + + reg = bus_space_read_1(sc->sc_tag, sc->sc_hreg, 0); + bus_space_write_1(sc->sc_tag, sc->sc_hreg, 0, + IOC_PANEL_VDOWN_IRQ | IOC_PANEL_VUP_IRQ | IOC_PANEL_POWER_IRQ); + if ((reg & IOC_PANEL_POWER_IRQ) == 0) { + if (!sc->sc_fired) + sysmon_task_queue_sched(0, panel_powerbutton, sc); + sc->sc_fired = 1; + } + if (time_second == sc->sc_last) + return 1; + sc->sc_last = time_second; + if ((reg & IOC_PANEL_VDOWN_HOLD) == 0) + pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN); + if ((reg & IOC_PANEL_VUP_HOLD) == 0) + pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP); + + return 1; +} + +static void +panel_powerbutton(void *cookie) +{ + struct panel_softc *sc = cookie; + + sysmon_pswitch_event(&sc->sc_pbutton, PSWITCH_EVENT_PRESSED); +}