1/* $OpenBSD: pcppi.c,v 1.19 2022/04/06 18:59:28 naddy Exp $ */ 2/* $NetBSD: pcppi.c,v 1.1 1998/04/15 20:26:18 drochner Exp $ */ 3 4/* 5 * Copyright (c) 1996 Carnegie-Mellon University. 6 * All rights reserved. 7 * 8 * Author: Chris G. Demetriou 9 * 10 * Permission to use, copy, modify and distribute this software and 11 * its documentation is hereby granted, provided that both the copyright 12 * notice and this permission notice appear in all copies of the 13 * software, derivative works or modified versions, and any portions 14 * thereof, and that both notices appear in supporting documentation. 15 * 16 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 17 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 18 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 19 * 20 * Carnegie Mellon requests users of this software to return to 21 * 22 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 23 * School of Computer Science 24 * Carnegie Mellon University 25 * Pittsburgh PA 15213-3890 26 * 27 * any improvements or extensions that they make and grant Carnegie the 28 * rights to redistribute these changes. 29 */ 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/kernel.h> 34#include <sys/device.h> 35#include <sys/errno.h> 36#include <sys/timeout.h> 37 38#include <machine/bus.h> 39 40#include <dev/isa/isareg.h> 41#include <dev/isa/isavar.h> 42#include <dev/isa/pcppireg.h> 43#include <dev/isa/pcppivar.h> 44 45#include <dev/ic/i8253reg.h> 46 47#include "pckbd.h" 48#include "hidkbd.h" 49#if NPCKBD > 0 || NHIDKBD > 0 50#include <dev/ic/pckbcvar.h> 51#include <dev/pckbc/pckbdvar.h> 52#include <dev/hid/hidkbdvar.h> 53void pcppi_kbd_bell(void *, u_int, u_int, u_int, int); 54#endif 55 56struct pcppi_softc { 57 struct device sc_dv; 58 59 bus_space_tag_t sc_iot; 60 bus_space_handle_t sc_ppi_ioh, sc_pit1_ioh; 61 62 struct timeout sc_bell_timeout; 63 64 int sc_bellactive, sc_bellpitch; 65 int sc_slp; 66 int sc_timeout; 67}; 68 69int pcppi_match(struct device *, void *, void *); 70void pcppi_attach(struct device *, struct device *, void *); 71 72const struct cfattach pcppi_ca = { 73 sizeof(struct pcppi_softc), pcppi_match, pcppi_attach, 74}; 75 76struct cfdriver pcppi_cd = { 77 NULL, "pcppi", DV_DULL 78}; 79 80static void pcppi_bell_stop(void *); 81 82#define PCPPIPRI (PZERO - 1) 83 84int 85pcppi_match(struct device *parent, void *match, void *aux) 86{ 87 struct isa_attach_args *ia = aux; 88 bus_space_handle_t ppi_ioh, pit1_ioh; 89 int have_pit1, have_ppi, rv; 90 u_int8_t v, nv; 91 92 /* If values are hardwired to something that they can't be, punt. */ 93 if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_PPI) || 94 ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 || 95 ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK) 96 return (0); 97 98 rv = 0; 99 have_pit1 = have_ppi = 0; 100 101 if (bus_space_map(ia->ia_iot, IO_TIMER1, 4, 0, &pit1_ioh)) 102 goto lose; 103 have_pit1 = 1; 104 if (bus_space_map(ia->ia_iot, IO_PPI, 1, 0, &ppi_ioh)) 105 goto lose; 106 have_ppi = 1; 107 108 /* 109 * Check for existence of PPI. Realistically, this is either going to 110 * be here or nothing is going to be here. 111 * 112 * We don't want to have any chance of changing speaker output (which 113 * this test might, if it crashes in the middle, or something; 114 * normally it's too quick to produce anything audible), but 115 * many "combo chip" mock-PPI's don't seem to support the top bit 116 * of Port B as a settable bit. The bottom bit has to be settable, 117 * since the speaker driver hardware still uses it. 118 */ 119 v = bus_space_read_1(ia->ia_iot, ppi_ioh, 0); /* XXX */ 120 bus_space_write_1(ia->ia_iot, ppi_ioh, 0, v ^ 0x01); /* XXX */ 121 nv = bus_space_read_1(ia->ia_iot, ppi_ioh, 0); /* XXX */ 122 if (((nv ^ v) & 0x01) == 0x01) 123 rv = 1; 124 bus_space_write_1(ia->ia_iot, ppi_ioh, 0, v); /* XXX */ 125 nv = bus_space_read_1(ia->ia_iot, ppi_ioh, 0); /* XXX */ 126 if (((nv ^ v) & 0x01) != 0x00) { 127 rv = 0; 128 goto lose; 129 } 130 131 /* 132 * We assume that the programmable interval timer is there. 133 */ 134 135lose: 136 if (have_pit1) 137 bus_space_unmap(ia->ia_iot, pit1_ioh, 4); 138 if (have_ppi) 139 bus_space_unmap(ia->ia_iot, ppi_ioh, 1); 140 if (rv) { 141 ia->ia_iobase = IO_PPI; 142 ia->ia_iosize = 0x1; 143 ia->ia_msize = 0x0; 144 } 145 return (rv); 146} 147 148void 149pcppi_attach(struct device *parent, struct device *self, void *aux) 150{ 151 struct pcppi_softc *sc = (struct pcppi_softc *)self; 152 struct isa_attach_args *ia = aux; 153 bus_space_tag_t iot; 154 struct pcppi_attach_args pa; 155 156 timeout_set(&sc->sc_bell_timeout, pcppi_bell_stop, sc); 157 158 sc->sc_iot = iot = ia->ia_iot; 159 160 if (bus_space_map(iot, IO_TIMER1, 4, 0, &sc->sc_pit1_ioh) || 161 bus_space_map(iot, IO_PPI, 1, 0, &sc->sc_ppi_ioh)) 162 panic("pcppi_attach: couldn't map"); 163 164 printf("\n"); 165 166 sc->sc_bellactive = sc->sc_bellpitch = sc->sc_slp = 0; 167 168 /* Provide a beeper for the keyboard, if there isn't one already. */ 169#if NPCKBD > 0 170 pckbd_hookup_bell(pcppi_kbd_bell, sc); 171#endif 172#if NHIDKBD > 0 173 hidkbd_hookup_bell(pcppi_kbd_bell, sc); 174#endif 175 176 pa.pa_cookie = sc; 177 while (config_found(self, &pa, 0)) 178 ; 179} 180 181void 182pcppi_bell(pcppi_tag_t self, int pitch, int period_ms, int slp) 183{ 184 struct pcppi_softc *sc = self; 185 int s1, s2; 186 187 if (pitch < 0) 188 pitch = 0; 189 else if (pitch > INT_MAX - TIMER_FREQ) 190 pitch = INT_MAX - TIMER_FREQ; 191 192 if (period_ms < 0) 193 period_ms = 0; 194 else if (period_ms > INT_MAX / 1000) 195 period_ms = INT_MAX / 1000; 196 197 s1 = spltty(); /* ??? */ 198 if (sc->sc_bellactive) { 199 if (sc->sc_timeout) { 200 sc->sc_timeout = 0; 201 timeout_del(&sc->sc_bell_timeout); 202 } 203 if (sc->sc_slp) 204 wakeup(pcppi_bell_stop); 205 } 206 if (pitch == 0 || period_ms == 0) { 207 pcppi_bell_stop(sc); 208 sc->sc_bellpitch = 0; 209 splx(s1); 210 return; 211 } 212 if (!sc->sc_bellactive || sc->sc_bellpitch != pitch) { 213 s2 = splhigh(); 214 bus_space_write_1(sc->sc_iot, sc->sc_pit1_ioh, TIMER_MODE, 215 TIMER_SEL2 | TIMER_16BIT | TIMER_SQWAVE); 216 bus_space_write_1(sc->sc_iot, sc->sc_pit1_ioh, TIMER_CNTR2, 217 TIMER_DIV(pitch) % 256); 218 bus_space_write_1(sc->sc_iot, sc->sc_pit1_ioh, TIMER_CNTR2, 219 TIMER_DIV(pitch) / 256); 220 splx(s2); 221 /* enable speaker */ 222 bus_space_write_1(sc->sc_iot, sc->sc_ppi_ioh, 0, 223 bus_space_read_1(sc->sc_iot, sc->sc_ppi_ioh, 0) 224 | PIT_SPKR); 225 } 226 sc->sc_bellpitch = pitch; 227 228 sc->sc_bellactive = 1; 229 230 if (slp & PCPPI_BELL_POLL) { 231 delay(period_ms * 1000); 232 pcppi_bell_stop(sc); 233 } else { 234 sc->sc_timeout = 1; 235 timeout_add_msec(&sc->sc_bell_timeout, period_ms); 236 if (slp & PCPPI_BELL_SLEEP) { 237 sc->sc_slp = 1; 238 tsleep_nsec(pcppi_bell_stop, PCPPIPRI | PCATCH, "bell", 239 INFSLP); 240 sc->sc_slp = 0; 241 } 242 } 243 splx(s1); 244} 245 246static void 247pcppi_bell_stop(void *arg) 248{ 249 struct pcppi_softc *sc = arg; 250 int s; 251 252 s = spltty(); /* ??? */ 253 sc->sc_timeout = 0; 254 255 /* disable bell */ 256 bus_space_write_1(sc->sc_iot, sc->sc_ppi_ioh, 0, 257 bus_space_read_1(sc->sc_iot, sc->sc_ppi_ioh, 0) 258 & ~PIT_SPKR); 259 sc->sc_bellactive = 0; 260 if (sc->sc_slp) 261 wakeup(pcppi_bell_stop); 262 splx(s); 263} 264 265#if NPCKBD > 0 || NHIDKBD > 0 266void 267pcppi_kbd_bell(void *arg, u_int pitch, u_int period, u_int volume, int poll) 268{ 269 /* 270 * NB: volume ignored. 271 */ 272 pcppi_bell(arg, volume ? pitch : 0, period, 273 poll ? PCPPI_BELL_POLL : 0); 274} 275#endif /* NPCKBD > 0 || NHIDKBD > 0 */ 276