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