1/* $OpenBSD: beep.c,v 1.11 2022/10/16 01:22:39 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 61struct beep_freq { 62 int freq; 63 u_int32_t reg; 64}; 65 66struct 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 77int beep_match(struct device *, void *, void *); 78void beep_attach(struct device *, struct device *, void *); 79void beep_setfreq(struct beep_softc *, int); 80 81const struct cfattach beep_ca = { 82 sizeof(struct beep_softc), beep_match, beep_attach 83}; 84 85struct cfdriver beep_cd = { 86 NULL, "beep", DV_DULL 87}; 88 89#if NHIDKBD > 0 90void beep_stop(void *); 91void beep_bell(void *, u_int, u_int, u_int, int); 92#endif 93 94int 95beep_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 104void 105beep_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 160void 161beep_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[i].reg >> 24) & 0xff); 192 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CNT_1, 193 (sc->sc_freqs[i].reg >> 16) & 0xff); 194 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CNT_2, 195 (sc->sc_freqs[i].reg >> 8) & 0xff); 196 bus_space_write_1(sc->sc_iot, sc->sc_ioh, BEEP_CNT_3, 197 (sc->sc_freqs[i].reg >> 0) & 0xff); 198} 199 200#if NHIDKBD > 0 201void 202beep_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 215void 216beep_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