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