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