1 /* $NetBSD: auxio.c,v 1.18 2007/10/17 19:57:28 garbled Exp $ */ 2 3 /* 4 * Copyright (c) 2000, 2001 Matthew R. Green 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 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * AUXIO registers support on the sbus & ebus2, used for the floppy driver 33 * and to control the system LED, for the BLINK option. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: auxio.c,v 1.18 2007/10/17 19:57:28 garbled Exp $"); 38 39 #include "opt_auxio.h" 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/kernel.h> 44 #include <sys/callout.h> 45 #include <sys/errno.h> 46 #include <sys/device.h> 47 #include <sys/malloc.h> 48 49 #include <machine/autoconf.h> 50 #include <machine/cpu.h> 51 52 #include <dev/ebus/ebusreg.h> 53 #include <dev/ebus/ebusvar.h> 54 #include <sparc64/dev/sbusvar.h> 55 #include <sparc64/dev/auxioreg.h> 56 #include <sparc64/dev/auxiovar.h> 57 58 /* 59 * on sun4u, auxio exists with one register (LED) on the sbus, and 5 60 * registers on the ebus2 (pci) (LED, PCIMODE, FREQUENCY, SCSI 61 * OSCILLATOR, and TEMP SENSE. 62 */ 63 64 struct auxio_softc { 65 struct device sc_dev; 66 67 /* parent's tag */ 68 bus_space_tag_t sc_tag; 69 70 /* handles to the various auxio regsiter sets */ 71 bus_space_handle_t sc_led; 72 bus_space_handle_t sc_pci; 73 bus_space_handle_t sc_freq; 74 bus_space_handle_t sc_scsi; 75 bus_space_handle_t sc_temp; 76 77 int sc_flags; 78 #define AUXIO_LEDONLY 0x1 79 #define AUXIO_EBUS 0x2 80 #define AUXIO_SBUS 0x4 81 }; 82 83 #define AUXIO_ROM_NAME "auxio" 84 85 void auxio_attach_common(struct auxio_softc *); 86 int auxio_ebus_match(struct device *, struct cfdata *, void *); 87 void auxio_ebus_attach(struct device *, struct device *, void *); 88 int auxio_sbus_match(struct device *, struct cfdata *, void *); 89 void auxio_sbus_attach(struct device *, struct device *, void *); 90 91 CFATTACH_DECL(auxio_ebus, sizeof(struct auxio_softc), 92 auxio_ebus_match, auxio_ebus_attach, NULL, NULL); 93 94 CFATTACH_DECL(auxio_sbus, sizeof(struct auxio_softc), 95 auxio_sbus_match, auxio_sbus_attach, NULL, NULL); 96 97 extern struct cfdriver auxio_cd; 98 99 #ifdef BLINK 100 static callout_t blink_ch; 101 static void auxio_blink(void *); 102 103 /* let someone disable it if it's already turned on; XXX sysctl? */ 104 int do_blink = 1; 105 106 static void 107 auxio_blink(void *x) 108 { 109 struct auxio_softc *sc = x; 110 int s; 111 uint32_t led; 112 113 if (do_blink == 0) 114 return; 115 116 s = splhigh(); 117 if (sc->sc_flags & AUXIO_EBUS) 118 led = le32toh(bus_space_read_4(sc->sc_tag, sc->sc_led, 0)); 119 else 120 led = bus_space_read_1(sc->sc_tag, sc->sc_led, 0); 121 122 led = led ^ AUXIO_LED_LED; 123 if (sc->sc_flags & AUXIO_EBUS) 124 bus_space_write_4(sc->sc_tag, sc->sc_led, 0, htole32(led)); 125 else 126 bus_space_write_1(sc->sc_tag, sc->sc_led, 0, led); 127 splx(s); 128 129 /* 130 * Blink rate is: 131 * full cycle every second if completely idle (loadav = 0) 132 * full cycle every 2 seconds if loadav = 1 133 * full cycle every 3 seconds if loadav = 2 134 * etc. 135 */ 136 s = (((averunnable.ldavg[0] + FSCALE) * hz) >> (FSHIFT + 1)); 137 callout_reset(&blink_ch, s, auxio_blink, sc); 138 } 139 #endif 140 141 void 142 auxio_attach_common(struct auxio_softc *sc) 143 { 144 #ifdef BLINK 145 static int do_once = 1; 146 147 /* only start one blinker */ 148 if (do_once) { 149 callout_init(&blink_ch, 0); 150 auxio_blink(sc); 151 do_once = 0; 152 } 153 #endif 154 printf("\n"); 155 } 156 157 int 158 auxio_ebus_match(struct device *parent, struct cfdata *cf, void *aux) 159 { 160 struct ebus_attach_args *ea = aux; 161 162 return (strcmp(AUXIO_ROM_NAME, ea->ea_name) == 0); 163 } 164 165 void 166 auxio_ebus_attach(struct device *parent, struct device *self, void *aux) 167 { 168 struct auxio_softc *sc = (struct auxio_softc *)self; 169 struct ebus_attach_args *ea = aux; 170 171 sc->sc_tag = ea->ea_bustag; 172 173 if (ea->ea_nreg < 1) { 174 printf(": no registers??\n"); 175 return; 176 } 177 178 if (ea->ea_nreg != 5) { 179 printf(": not 5 (%d) registers, only setting led", 180 ea->ea_nreg); 181 sc->sc_flags = AUXIO_LEDONLY|AUXIO_EBUS; 182 } else if (ea->ea_nvaddr == 5) { 183 sc->sc_flags = AUXIO_EBUS; 184 185 sparc_promaddr_to_handle(sc->sc_tag, 186 ea->ea_vaddr[1], &sc->sc_pci); 187 sparc_promaddr_to_handle(sc->sc_tag, 188 ea->ea_vaddr[2], &sc->sc_freq); 189 sparc_promaddr_to_handle(sc->sc_tag, 190 ea->ea_vaddr[3], &sc->sc_scsi); 191 sparc_promaddr_to_handle(sc->sc_tag, 192 ea->ea_vaddr[4], &sc->sc_temp); 193 } else { 194 sc->sc_flags = AUXIO_EBUS; 195 bus_space_map(sc->sc_tag, EBUS_ADDR_FROM_REG(&ea->ea_reg[1]), 196 ea->ea_reg[1].size, 0, &sc->sc_pci); 197 bus_space_map(sc->sc_tag, EBUS_ADDR_FROM_REG(&ea->ea_reg[2]), 198 ea->ea_reg[2].size, 0, &sc->sc_freq); 199 bus_space_map(sc->sc_tag, EBUS_ADDR_FROM_REG(&ea->ea_reg[3]), 200 ea->ea_reg[3].size, 0, &sc->sc_scsi); 201 bus_space_map(sc->sc_tag, EBUS_ADDR_FROM_REG(&ea->ea_reg[4]), 202 ea->ea_reg[4].size, 0, &sc->sc_temp); 203 } 204 205 if (ea->ea_nvaddr > 0) { 206 sparc_promaddr_to_handle(sc->sc_tag, 207 ea->ea_vaddr[0], &sc->sc_led); 208 } else { 209 bus_space_map(sc->sc_tag, EBUS_ADDR_FROM_REG(&ea->ea_reg[0]), 210 ea->ea_reg[0].size, 0, &sc->sc_led); 211 } 212 213 auxio_attach_common(sc); 214 } 215 216 int 217 auxio_sbus_match(struct device *parent, struct cfdata *cf, void *aux) 218 { 219 struct sbus_attach_args *sa = aux; 220 221 return (strcmp(AUXIO_ROM_NAME, sa->sa_name) == 0); 222 } 223 224 void 225 auxio_sbus_attach(struct device *parent, struct device *self, void *aux) 226 { 227 struct auxio_softc *sc = (struct auxio_softc *)self; 228 struct sbus_attach_args *sa = aux; 229 230 sc->sc_tag = sa->sa_bustag; 231 232 if (sa->sa_nreg < 1) { 233 printf(": no registers??\n"); 234 return; 235 } 236 237 if (sa->sa_nreg != 1) { 238 printf(": not 1 (%d/%d) registers??", sa->sa_nreg, 239 sa->sa_npromvaddrs); 240 return; 241 } 242 243 /* sbus auxio only has one set of registers */ 244 sc->sc_flags = AUXIO_LEDONLY|AUXIO_SBUS; 245 if (sa->sa_npromvaddrs > 0) { 246 sbus_promaddr_to_handle(sc->sc_tag, 247 sa->sa_promvaddr, &sc->sc_led); 248 } else { 249 sbus_bus_map(sc->sc_tag, sa->sa_slot, sa->sa_offset, 250 sa->sa_size, 0, &sc->sc_led); 251 } 252 253 auxio_attach_common(sc); 254 } 255 256 int 257 auxio_fd_control(u_int32_t bits) 258 { 259 struct auxio_softc *sc; 260 u_int32_t led; 261 262 if (auxio_cd.cd_ndevs == 0) { 263 return ENXIO; 264 } 265 266 /* 267 * XXX This does not handle > 1 auxio correctly. 268 * We'll assume the floppy drive is tied to first auxio found. 269 */ 270 sc = (struct auxio_softc *)auxio_cd.cd_devs[0]; 271 if (sc->sc_flags & AUXIO_EBUS) 272 led = le32toh(bus_space_read_4(sc->sc_tag, sc->sc_led, 0)); 273 else 274 led = bus_space_read_1(sc->sc_tag, sc->sc_led, 0); 275 276 led = (led & ~AUXIO_LED_FLOPPY_MASK) | bits; 277 278 if (sc->sc_flags & AUXIO_EBUS) 279 bus_space_write_4(sc->sc_tag, sc->sc_led, 0, htole32(led)); 280 else 281 bus_space_write_1(sc->sc_tag, sc->sc_led, 0, led); 282 283 return 0; 284 } 285