1 /* $OpenBSD: pcppi.c,v 1.13 2016/01/08 15:54:13 jcs 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> 53 void pcppi_kbd_bell(void *, u_int, u_int, u_int, int); 54 #endif 55 56 struct 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 69 int pcppi_match(struct device *, void *, void *); 70 void pcppi_attach(struct device *, struct device *, void *); 71 72 struct cfattach pcppi_ca = { 73 sizeof(struct pcppi_softc), pcppi_match, pcppi_attach, 74 }; 75 76 struct cfdriver pcppi_cd = { 77 NULL, "pcppi", DV_DULL 78 }; 79 80 static void pcppi_bell_stop(void *); 81 82 #define PCPPIPRI (PZERO - 1) 83 84 int 85 pcppi_match(parent, match, aux) 86 struct device *parent; 87 void *match; 88 void *aux; 89 { 90 struct isa_attach_args *ia = aux; 91 bus_space_handle_t ppi_ioh, pit1_ioh; 92 int have_pit1, have_ppi, rv; 93 u_int8_t v, nv; 94 95 /* If values are hardwired to something that they can't be, punt. */ 96 if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_PPI) || 97 ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 || 98 ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK) 99 return (0); 100 101 rv = 0; 102 have_pit1 = have_ppi = 0; 103 104 if (bus_space_map(ia->ia_iot, IO_TIMER1, 4, 0, &pit1_ioh)) 105 goto lose; 106 have_pit1 = 1; 107 if (bus_space_map(ia->ia_iot, IO_PPI, 1, 0, &ppi_ioh)) 108 goto lose; 109 have_ppi = 1; 110 111 /* 112 * Check for existence of PPI. Realistically, this is either going to 113 * be here or nothing is going to be here. 114 * 115 * We don't want to have any chance of changing speaker output (which 116 * this test might, if it crashes in the middle, or something; 117 * normally it's be to quick to produce anthing audible), but 118 * many "combo chip" mock-PPI's don't seem to support the top bit 119 * of Port B as a settable bit. The bottom bit has to be settable, 120 * since the speaker driver hardware still uses it. 121 */ 122 v = bus_space_read_1(ia->ia_iot, ppi_ioh, 0); /* XXX */ 123 bus_space_write_1(ia->ia_iot, ppi_ioh, 0, v ^ 0x01); /* XXX */ 124 nv = bus_space_read_1(ia->ia_iot, ppi_ioh, 0); /* XXX */ 125 if (((nv ^ v) & 0x01) == 0x01) 126 rv = 1; 127 bus_space_write_1(ia->ia_iot, ppi_ioh, 0, v); /* XXX */ 128 nv = bus_space_read_1(ia->ia_iot, ppi_ioh, 0); /* XXX */ 129 if (((nv ^ v) & 0x01) != 0x00) { 130 rv = 0; 131 goto lose; 132 } 133 134 /* 135 * We assume that the programmable interval timer is there. 136 */ 137 138 lose: 139 if (have_pit1) 140 bus_space_unmap(ia->ia_iot, pit1_ioh, 4); 141 if (have_ppi) 142 bus_space_unmap(ia->ia_iot, ppi_ioh, 1); 143 if (rv) { 144 ia->ia_iobase = IO_PPI; 145 ia->ia_iosize = 0x1; 146 ia->ia_msize = 0x0; 147 } 148 return (rv); 149 } 150 151 void 152 pcppi_attach(parent, self, aux) 153 struct device *parent, *self; 154 void *aux; 155 { 156 struct pcppi_softc *sc = (struct pcppi_softc *)self; 157 struct isa_attach_args *ia = aux; 158 bus_space_tag_t iot; 159 struct pcppi_attach_args pa; 160 161 timeout_set(&sc->sc_bell_timeout, pcppi_bell_stop, sc); 162 163 sc->sc_iot = iot = ia->ia_iot; 164 165 if (bus_space_map(iot, IO_TIMER1, 4, 0, &sc->sc_pit1_ioh) || 166 bus_space_map(iot, IO_PPI, 1, 0, &sc->sc_ppi_ioh)) 167 panic("pcppi_attach: couldn't map"); 168 169 printf("\n"); 170 171 sc->sc_bellactive = sc->sc_bellpitch = sc->sc_slp = 0; 172 173 /* Provide a beeper for the keyboard, if there isn't one already. */ 174 #if NPCKBD > 0 175 pckbd_hookup_bell(pcppi_kbd_bell, sc); 176 #endif 177 #if NHIDKBD > 0 178 hidkbd_hookup_bell(pcppi_kbd_bell, sc); 179 #endif 180 181 pa.pa_cookie = sc; 182 while (config_found(self, &pa, 0)) 183 ; 184 } 185 186 void 187 pcppi_bell(self, pitch, period, slp) 188 pcppi_tag_t self; 189 int pitch, period; 190 int slp; 191 { 192 struct pcppi_softc *sc = self; 193 int s1, s2; 194 195 s1 = spltty(); /* ??? */ 196 if (sc->sc_bellactive) { 197 if (sc->sc_timeout) { 198 sc->sc_timeout = 0; 199 timeout_del(&sc->sc_bell_timeout); 200 } 201 if (sc->sc_slp) 202 wakeup(pcppi_bell_stop); 203 } 204 if (pitch == 0 || period == 0) { 205 pcppi_bell_stop(sc); 206 sc->sc_bellpitch = 0; 207 splx(s1); 208 return; 209 } 210 if (!sc->sc_bellactive || sc->sc_bellpitch != pitch) { 211 s2 = splhigh(); 212 bus_space_write_1(sc->sc_iot, sc->sc_pit1_ioh, TIMER_MODE, 213 TIMER_SEL2 | TIMER_16BIT | TIMER_SQWAVE); 214 bus_space_write_1(sc->sc_iot, sc->sc_pit1_ioh, TIMER_CNTR2, 215 TIMER_DIV(pitch) % 256); 216 bus_space_write_1(sc->sc_iot, sc->sc_pit1_ioh, TIMER_CNTR2, 217 TIMER_DIV(pitch) / 256); 218 splx(s2); 219 /* enable speaker */ 220 bus_space_write_1(sc->sc_iot, sc->sc_ppi_ioh, 0, 221 bus_space_read_1(sc->sc_iot, sc->sc_ppi_ioh, 0) 222 | PIT_SPKR); 223 } 224 sc->sc_bellpitch = pitch; 225 226 sc->sc_bellactive = 1; 227 228 if (slp & PCPPI_BELL_POLL) { 229 delay((period * 1000000) / hz); 230 pcppi_bell_stop(sc); 231 } else { 232 sc->sc_timeout = 1; 233 timeout_add(&sc->sc_bell_timeout, period); 234 if (slp & PCPPI_BELL_SLEEP) { 235 sc->sc_slp = 1; 236 tsleep(pcppi_bell_stop, PCPPIPRI | PCATCH, "bell", 0); 237 sc->sc_slp = 0; 238 } 239 } 240 splx(s1); 241 } 242 243 static void 244 pcppi_bell_stop(arg) 245 void *arg; 246 { 247 struct pcppi_softc *sc = arg; 248 int s; 249 250 s = spltty(); /* ??? */ 251 sc->sc_timeout = 0; 252 253 /* disable bell */ 254 bus_space_write_1(sc->sc_iot, sc->sc_ppi_ioh, 0, 255 bus_space_read_1(sc->sc_iot, sc->sc_ppi_ioh, 0) 256 & ~PIT_SPKR); 257 sc->sc_bellactive = 0; 258 if (sc->sc_slp) 259 wakeup(pcppi_bell_stop); 260 splx(s); 261 } 262 263 #if NPCKBD > 0 || NHIDKBD > 0 264 void 265 pcppi_kbd_bell(arg, pitch, period, volume, poll) 266 void *arg; 267 u_int pitch, period, volume; 268 int poll; 269 { 270 /* 271 * Comes in as ms, goes out as ticks; volume ignored. 272 */ 273 pcppi_bell(arg, volume ? pitch : 0, (period * hz) / 1000, 274 poll ? PCPPI_BELL_POLL : 0); 275 } 276 #endif /* NPCKBD > 0 || NHIDKBD > 0 */ 277