1 /* $OpenBSD: beep.c,v 1.12 2024/11/02 10:36:47 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Jason L. Wright (jason@thought.net) 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Driver for beeper device on BBC machines (Blade 1k, 2k, etc) 31 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/kernel.h> 36 #include <sys/device.h> 37 #include <sys/conf.h> 38 #include <sys/timeout.h> 39 40 #include <machine/bus.h> 41 #include <machine/autoconf.h> 42 #include <machine/openfirm.h> 43 44 #include <sparc64/dev/ebusreg.h> 45 #include <sparc64/dev/ebusvar.h> 46 47 #include "hidkbd.h" 48 #if NHIDKBD > 0 49 #include <dev/hid/hidkbdvar.h> 50 #endif 51 52 #define BEEP_CTRL 0 53 #define BEEP_CNT_0 2 54 #define BEEP_CNT_1 3 55 #define BEEP_CNT_2 4 56 #define BEEP_CNT_3 5 57 58 #define BEEP_CTRL_ON 0x01 59 #define BEEP_CTRL_OFF 0x00 60 61 struct beep_freq { 62 int freq; 63 u_int32_t reg; 64 }; 65 66 struct beep_softc { 67 struct device sc_dev; 68 bus_space_tag_t sc_iot; 69 bus_space_handle_t sc_ioh; 70 int sc_clk; 71 struct beep_freq sc_freqs[9]; 72 struct timeout sc_to; 73 int sc_belltimeout; 74 int sc_bellactive; 75 }; 76 77 int beep_match(struct device *, void *, void *); 78 void beep_attach(struct device *, struct device *, void *); 79 void beep_setfreq(struct beep_softc *, int); 80 81 const struct cfattach beep_ca = { 82 sizeof(struct beep_softc), beep_match, beep_attach 83 }; 84 85 struct cfdriver beep_cd = { 86 NULL, "beep", DV_DULL 87 }; 88 89 #if NHIDKBD > 0 90 void beep_stop(void *); 91 void beep_bell(void *, u_int, u_int, u_int, int); 92 #endif 93 94 int 95 beep_match(struct device *parent, void *match, void *aux) 96 { 97 struct ebus_attach_args *ea = aux; 98 99 if (strcmp(ea->ea_name, "beep") == 0) 100 return (1); 101 return (0); 102 } 103 104 void 105 beep_attach(struct device *parent, struct device *self, void *aux) 106 { 107 struct beep_softc *sc = (void *)self; 108 struct ebus_attach_args *ea = aux; 109 int i; 110 111 sc->sc_iot = ea->ea_memtag; 112 113 /* Use prom address if available, otherwise map it. */ 114 if (ea->ea_nvaddrs) { 115 if (bus_space_map(sc->sc_iot, ea->ea_vaddrs[0], 0, 116 BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh)) { 117 printf(": can't map PROM register space\n"); 118 return; 119 } 120 } else if (ebus_bus_map(sc->sc_iot, 0, 121 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size, 0, 0, 122 &sc->sc_ioh) != 0) { 123 printf(": can't map register space\n"); 124 return; 125 } 126 127 /* The bbc,beep is clocked at half the BBC frequency */ 128 sc->sc_clk = getpropint(findroot(), "clock-frequency", 0); 129 sc->sc_clk /= 2; 130 131 /* 132 * Compute the frequence table based on the scalar and base 133 * board clock speed. 134 */ 135 for (i = 0; i < 9; i++) { 136 sc->sc_freqs[i].reg = 1 << (18 - i); 137 sc->sc_freqs[i].freq = sc->sc_clk / sc->sc_freqs[i].reg; 138 } 139 140 /* set beep at around 1200hz */ 141 beep_setfreq(sc, 1200); 142 143 #if 0 144 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CTRL, 145 BEEP_CTRL_ON); 146 for (i = 0; i < 1000; i++) 147 DELAY(1000); 148 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CTRL, 149 BEEP_CTRL_OFF); 150 #endif 151 152 printf(": clock %sMHz\n", clockfreq(sc->sc_clk)); 153 154 #if NHIDKBD > 0 155 timeout_set(&sc->sc_to, beep_stop, sc); 156 hidkbd_hookup_bell(beep_bell, sc); 157 #endif 158 } 159 160 void 161 beep_setfreq(struct beep_softc *sc, int freq) 162 { 163 int i, n, selected = -1; 164 165 n = sizeof(sc->sc_freqs)/sizeof(sc->sc_freqs[0]); 166 167 if (freq < sc->sc_freqs[0].freq) 168 selected = 0; 169 if (freq > sc->sc_freqs[n - 1].freq) 170 selected = n - 1; 171 172 for (i = 1; selected == -1 && i < n; i++) { 173 if (sc->sc_freqs[i].freq == freq) 174 selected = i; 175 else if (sc->sc_freqs[i].freq > freq) { 176 int diff1, diff2; 177 178 diff1 = freq - sc->sc_freqs[i - 1].freq; 179 diff2 = sc->sc_freqs[i].freq - freq; 180 if (diff1 < diff2) 181 selected = i - 1; 182 else 183 selected = i; 184 } 185 } 186 187 if (selected == -1) 188 selected = 0; 189 190 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CNT_0, 191 (sc->sc_freqs[selected].reg >> 24) & 0xff); 192 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CNT_1, 193 (sc->sc_freqs[selected].reg >> 16) & 0xff); 194 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CNT_2, 195 (sc->sc_freqs[selected].reg >> 8) & 0xff); 196 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CNT_3, 197 (sc->sc_freqs[selected].reg >> 0) & 0xff); 198 } 199 200 #if NHIDKBD > 0 201 void 202 beep_stop(void *vsc) 203 { 204 struct beep_softc *sc = vsc; 205 int s; 206 207 s = spltty(); 208 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CTRL, 209 BEEP_CTRL_OFF); 210 sc->sc_bellactive = 0; 211 sc->sc_belltimeout = 0; 212 splx(s); 213 } 214 215 void 216 beep_bell(void *vsc, u_int pitch, u_int period, u_int volume, int poll) 217 { 218 struct beep_softc *sc = vsc; 219 int s; 220 221 s = spltty(); 222 if (sc->sc_bellactive) { 223 if (sc->sc_belltimeout == 0) 224 timeout_del(&sc->sc_to); 225 } 226 if (pitch == 0 || period == 0 || volume == 0) { 227 beep_stop(sc); 228 splx(s); 229 return; 230 } 231 if (!sc->sc_bellactive) { 232 sc->sc_bellactive = 1; 233 sc->sc_belltimeout = 1; 234 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CTRL, 235 BEEP_CTRL_ON); 236 timeout_add_msec(&sc->sc_to, period); 237 } 238 splx(s); 239 } 240 #endif /* NHIDKBD > 0 */ 241