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