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