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