1/* $NetBSD: lasi.c,v 1.5 2021/08/07 16:18:55 thorpej Exp $ */ 2 3/* $OpenBSD: lasi.c,v 1.4 2001/06/09 03:57:19 mickey Exp $ */ 4 5/* 6 * Copyright (c) 1998-2003 Michael Shalayeff 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 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 OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, 22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 * THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__KERNEL_RCSID(0, "$NetBSD: lasi.c,v 1.5 2021/08/07 16:18:55 thorpej Exp $"); 33 34#undef LASIDEBUG 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/device.h> 39#include <sys/reboot.h> 40 41#include <sys/bus.h> 42#include <machine/iomod.h> 43#include <machine/autoconf.h> 44 45#include <hppa/dev/cpudevs.h> 46 47#include <hppa/gsc/gscbusvar.h> 48 49struct lasi_hwr { 50 uint32_t lasi_power; 51#define LASI_BLINK 0x01 52#define LASI_OFF 0x02 53 uint32_t lasi_error; 54 uint32_t lasi_version; 55 uint32_t lasi_reset; 56 uint32_t lasi_arbmask; 57}; 58 59struct lasi_trs { 60 uint32_t lasi_irr; /* int requset register */ 61 uint32_t lasi_imr; /* int mask register */ 62 uint32_t lasi_ipr; /* int pending register */ 63 uint32_t lasi_icr; /* int command? register */ 64 uint32_t lasi_iar; /* int acquire? register */ 65}; 66 67#define LASI_BANK_SZ 0x200000 68#define LASI_REG_INT 0x100000 69#define LASI_REG_MISC 0x10c000 70 71struct lasi_softc { 72 device_t sc_dev; 73 74 struct hppa_interrupt_register sc_ir; 75 76 struct lasi_hwr volatile *sc_hw; 77 struct lasi_trs volatile *sc_trs; 78}; 79 80int lasimatch(device_t, cfdata_t, void *); 81void lasiattach(device_t, device_t, void *); 82 83CFATTACH_DECL_NEW(lasi, sizeof(struct lasi_softc), 84 lasimatch, lasiattach, NULL, NULL); 85 86extern struct cfdriver lasi_cd; 87 88void lasi_cold_hook(int); 89 90/* 91 * Before a module is matched, this fixes up its gsc_attach_args. 92 */ 93static void lasi_fix_args(void *, struct gsc_attach_args *); 94static void 95lasi_fix_args(void *_sc, struct gsc_attach_args *ga) 96{ 97 struct lasi_softc *sc = _sc; 98 hppa_hpa_t module_offset; 99 100 /* 101 * Determine this module's interrupt bit. 102 */ 103 module_offset = ga->ga_hpa - (hppa_hpa_t) sc->sc_trs; 104 ga->ga_irq = HPPACF_IRQ_UNDEF; 105#define LASI_IRQ(off, irq) if (module_offset == off) ga->ga_irq = irq 106 LASI_IRQ(0x2000, 7); /* lpt */ 107 LASI_IRQ(0x4000, 13); /* harmony */ 108 LASI_IRQ(0x4040, 16); /* harmony telephone0 */ 109 LASI_IRQ(0x4060, 17); /* harmony telephone1 */ 110 LASI_IRQ(0x5000, 5); /* com */ 111 LASI_IRQ(0x6000, 9); /* osiop */ 112 LASI_IRQ(0x7000, 8); /* ie */ 113 LASI_IRQ(0x8000, 26); /* pckbc */ 114 LASI_IRQ(0xa000, 20); /* fdc */ 115#undef LASI_IRQ 116 117 /* 118 * If this is the Ethernet adapter, get its Ethernet address. 119 */ 120 if (module_offset == 0x7000) { 121 pdcproc_lan_station_id(ga->ga_ether_address, 122 sizeof(ga->ga_ether_address), ga->ga_hpa); 123 } 124} 125 126int 127lasimatch(device_t parent, cfdata_t cf, void *aux) 128{ 129 struct confargs *ca = aux; 130 131 if (ca->ca_type.iodc_type != HPPA_TYPE_BHA || 132 ca->ca_type.iodc_sv_model != HPPA_BHA_LASI) 133 return 0; 134 135 /* 136 * Forcibly mask the HPA down to the start of the LASI 137 * chip address space. 138 */ 139 ca->ca_hpa &= ~(LASI_BANK_SZ - 1); 140 141 return 1; 142} 143 144void 145lasiattach(device_t parent, device_t self, void *aux) 146{ 147 struct confargs *ca = aux; 148 struct lasi_softc *sc = device_private(self); 149 struct gsc_attach_args ga; 150 struct cpu_info *ci = &cpus[0]; 151 bus_space_handle_t ioh; 152 int s; 153 154 sc->sc_dev = self; 155 /* 156 * Map the LASI interrupt registers. 157 */ 158 if (bus_space_map(ca->ca_iot, ca->ca_hpa + LASI_REG_INT, 159 sizeof(struct lasi_trs), 0, &ioh)) { 160 aprint_error(": can't map interrupt registers\n"); 161 return; 162 } 163 sc->sc_trs = (struct lasi_trs *)ioh; 164 165 /* 166 * Map the LASI miscellaneous registers. 167 */ 168 if (bus_space_map(ca->ca_iot, ca->ca_hpa + LASI_REG_MISC, 169 sizeof(struct lasi_hwr), 0, &ioh)) { 170 aprint_error(": can't map misc registers\n"); 171 return; 172 } 173 sc->sc_hw = (struct lasi_hwr *)ioh; 174 175 /* XXX should we reset the chip here? */ 176 177 aprint_normal(": rev %d.%d\n", (sc->sc_hw->lasi_version & 0xf0) >> 4, 178 sc->sc_hw->lasi_version & 0xf); 179 180 ca->ca_irq = hppa_intr_allocate_bit(&ci->ci_ir, ca->ca_irq); 181 if (ca->ca_irq == HPPACF_IRQ_UNDEF) { 182 aprint_error(": can't allocate interrupt\n"); 183 return; 184 } 185 186 /* interrupts guts */ 187 s = splhigh(); 188 sc->sc_trs->lasi_iar = ci->ci_hpa | (31 - ca->ca_irq); 189 sc->sc_trs->lasi_icr = 0; 190 sc->sc_trs->lasi_imr = 0; 191 (void)sc->sc_trs->lasi_irr; 192 193 /* Establish the interrupt register. */ 194 hppa_interrupt_register_establish(ci, &sc->sc_ir); 195 sc->sc_ir.ir_name = device_xname(self); 196 sc->sc_ir.ir_mask = &sc->sc_trs->lasi_imr; 197 sc->sc_ir.ir_req = &sc->sc_trs->lasi_irr; 198 splx(s); 199 200 /* Attach the GSC bus. */ 201 ga.ga_ca = *ca; /* clone from us */ 202 if (strcmp(device_xname(parent), "mainbus0") == 0) { 203 ga.ga_dp.dp_bc[0] = ga.ga_dp.dp_bc[1]; 204 ga.ga_dp.dp_bc[1] = ga.ga_dp.dp_bc[2]; 205 ga.ga_dp.dp_bc[2] = ga.ga_dp.dp_bc[3]; 206 ga.ga_dp.dp_bc[3] = ga.ga_dp.dp_bc[4]; 207 ga.ga_dp.dp_bc[4] = ga.ga_dp.dp_bc[5]; 208 ga.ga_dp.dp_bc[5] = ga.ga_dp.dp_mod; 209 ga.ga_dp.dp_mod = 0; 210 } 211 212 ga.ga_name = "gsc"; 213 ga.ga_ir = &sc->sc_ir; 214 ga.ga_fix_args = lasi_fix_args; 215 ga.ga_fix_args_cookie = sc; 216 ga.ga_scsi_target = 7; /* XXX */ 217 config_found(self, &ga, gscprint, CFARGS_NONE); 218 219 /* could be already set by power(4) */ 220 if (!cold_hook) 221 cold_hook = lasi_cold_hook; 222} 223 224void 225lasi_cold_hook(int on) 226{ 227 struct lasi_softc *sc = device_private(lasi_cd.cd_devs[0]); 228 229 if (!sc) 230 return; 231 232 switch (on) { 233 case HPPA_COLD_COLD: 234 sc->sc_hw->lasi_power = LASI_BLINK; 235 break; 236 case HPPA_COLD_HOT: 237 sc->sc_hw->lasi_power = 0; 238 break; 239 case HPPA_COLD_OFF: 240 sc->sc_hw->lasi_power = LASI_OFF; 241 break; 242 } 243} 244