1 /* $NetBSD: mlx_eisa.c,v 1.6 2001/12/18 13:38:48 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * EISA front-end for mlx(4) driver. 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: mlx_eisa.c,v 1.6 2001/12/18 13:38:48 ad Exp $"); 45 46 #include <sys/param.h> 47 #include <sys/systm.h> 48 #include <sys/device.h> 49 50 #include <machine/bus.h> 51 #include <machine/intr.h> 52 53 #include <dev/eisa/eisavar.h> 54 #include <dev/eisa/eisadevs.h> 55 56 #include <dev/ic/mlxreg.h> 57 #include <dev/ic/mlxio.h> 58 #include <dev/ic/mlxvar.h> 59 60 #define MLX_EISA_SLOT_OFFSET 0x0c80 61 #define MLX_EISA_IOSIZE (0x0ce0 - MLX_EISA_SLOT_OFFSET) 62 #define MLX_EISA_IOCONF1 (0x0cc1 - MLX_EISA_SLOT_OFFSET) 63 #define MLX_EISA_IOCONF2 (0x0cc3 - MLX_EISA_SLOT_OFFSET) 64 65 static void mlx_eisa_attach(struct device *, struct device *, void *); 66 static int mlx_eisa_match(struct device *, struct cfdata *, void *); 67 68 static int mlx_v1_submit(struct mlx_softc *, struct mlx_ccb *); 69 static int mlx_v1_findcomplete(struct mlx_softc *, u_int *, u_int *); 70 static void mlx_v1_intaction(struct mlx_softc *, int); 71 static int mlx_v1_fw_handshake(struct mlx_softc *, int *, int *, int *); 72 #ifdef MLX_RESET 73 static int mlx_v1_reset(struct mlx_softc *); 74 #endif 75 76 struct cfattach mlx_eisa_ca = { 77 sizeof(struct mlx_softc), mlx_eisa_match, mlx_eisa_attach 78 }; 79 80 static const char * const mlx_eisa_prod[] = { 81 "MLX0070", 82 "MLX0071", 83 "MLX0072", 84 "MLX0073", 85 "MLX0074", 86 "MLX0075", 87 "MLX0076", 88 "MLX0077", 89 }; 90 91 static int 92 mlx_eisa_match(struct device *parent, struct cfdata *match, void *aux) 93 { 94 struct eisa_attach_args *ea; 95 int i; 96 97 ea = aux; 98 99 for (i = 0; i < sizeof(mlx_eisa_prod) / sizeof(mlx_eisa_prod[0]); i++) 100 if (strcmp(ea->ea_idstring, mlx_eisa_prod[i]) == 0) 101 return (1); 102 103 return (0); 104 } 105 106 static void 107 mlx_eisa_attach(struct device *parent, struct device *self, void *aux) 108 { 109 struct eisa_attach_args *ea; 110 bus_space_handle_t ioh; 111 eisa_chipset_tag_t ec; 112 eisa_intr_handle_t ih; 113 struct mlx_softc *mlx; 114 bus_space_tag_t iot; 115 const char *intrstr; 116 int irq, le; 117 118 ea = aux; 119 mlx = (struct mlx_softc *)self; 120 iot = ea->ea_iot; 121 ec = ea->ea_ec; 122 123 if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) + 124 MLX_EISA_SLOT_OFFSET, MLX_EISA_IOSIZE, 0, &ioh)) { 125 printf("can't map i/o space\n"); 126 return; 127 } 128 129 mlx->mlx_iot = iot; 130 mlx->mlx_ioh = ioh; 131 mlx->mlx_dmat = ea->ea_dmat; 132 133 /* 134 * Map and establish the interrupt. 135 */ 136 switch (bus_space_read_1(iot, ioh, MLX_EISA_IOCONF1) & 0xf0) { 137 case 0xa0: 138 irq = 11; 139 break; 140 case 0xc0: 141 irq = 12; 142 break; 143 case 0xe0: 144 irq = 14; 145 break; 146 case 0x80: 147 irq = 15; 148 break; 149 default: 150 printf("controller on invalid IRQ\n"); 151 return; 152 } 153 154 if (eisa_intr_map(ec, irq, &ih)) { 155 printf("can't map interrupt (%d)\n", irq); 156 return; 157 } 158 159 if ((bus_space_read_1(iot, ioh, MLX_EISA_IOCONF1) & 0x08) != 0) 160 le = IST_LEVEL; 161 else 162 le = IST_EDGE; 163 164 intrstr = eisa_intr_string(ec, ih); 165 mlx->mlx_ih = eisa_intr_establish(ec, ih, le, IPL_BIO, mlx_intr, mlx); 166 if (mlx->mlx_ih == NULL) { 167 printf("can't establish interrupt"); 168 if (intrstr != NULL) 169 printf(" at %s", intrstr); 170 printf("\n"); 171 return; 172 } 173 174 mlx->mlx_flags = MLXF_EISA; 175 176 mlx->mlx_submit = mlx_v1_submit; 177 mlx->mlx_findcomplete = mlx_v1_findcomplete; 178 mlx->mlx_intaction = mlx_v1_intaction; 179 mlx->mlx_fw_handshake = mlx_v1_fw_handshake; 180 #ifdef MLX_RESET 181 mlx->mlx_reset = mlx_v1_reset; 182 #endif 183 184 printf(": Mylex RAID\n"); 185 mlx_init(mlx, intrstr); 186 } 187 188 /* 189 * ================= V1 interface linkage ================= 190 */ 191 192 /* 193 * Try to give (mc) to the controller. Returns 1 if successful, 0 on 194 * failure (the controller is not ready to take a command). 195 * 196 * Must be called at splbio or in a fashion that prevents reentry. 197 */ 198 static int 199 mlx_v1_submit(struct mlx_softc *mlx, struct mlx_ccb *mc) 200 { 201 202 /* Ready for our command? */ 203 if ((mlx_inb(mlx, MLX_V1REG_IDB) & MLX_V1_IDB_FULL) == 0) { 204 /* Copy mailbox data to window. */ 205 bus_space_write_region_1(mlx->mlx_iot, mlx->mlx_ioh, 206 MLX_V1REG_MAILBOX, mc->mc_mbox, MLX_V1_MAILBOX_LEN); 207 bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, 208 MLX_V1REG_MAILBOX, MLX_V1_MAILBOX_LEN, 209 BUS_SPACE_BARRIER_WRITE); 210 211 /* Post command. */ 212 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V3_IDB_FULL); 213 return (1); 214 } 215 216 return (0); 217 } 218 219 /* 220 * See if a command has been completed, if so acknowledge its completion and 221 * recover the slot number and status code. 222 * 223 * Must be called at splbio or in a fashion that prevents reentry. 224 */ 225 static int 226 mlx_v1_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status) 227 { 228 229 /* Status available? */ 230 if ((mlx_inb(mlx, MLX_V3REG_ODB) & MLX_V3_ODB_SAVAIL) != 0) { 231 *slot = mlx_inb(mlx, MLX_V1REG_MAILBOX + 0x0d); 232 *status = mlx_inw(mlx, MLX_V1REG_MAILBOX + 0x0e); 233 234 /* Acknowledge completion. */ 235 mlx_outb(mlx, MLX_V1REG_ODB, MLX_V1_ODB_SAVAIL); 236 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V1_IDB_SACK); 237 return (1); 238 } 239 240 return (0); 241 } 242 243 /* 244 * Enable/disable interrupts as requested. (No acknowledge required) 245 * 246 * Must be called at splbio or in a fashion that prevents reentry. 247 */ 248 static void 249 mlx_v1_intaction(struct mlx_softc *mlx, int action) 250 { 251 252 mlx_outb(mlx, MLX_V1REG_IE, action ? 1 : 0); 253 } 254 255 /* 256 * Poll for firmware error codes during controller initialisation. 257 * 258 * Returns 0 if initialisation is complete, 1 if still in progress but no 259 * error has been fetched, 2 if an error has been retrieved. 260 */ 261 static int 262 mlx_v1_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2) 263 { 264 u_int8_t fwerror; 265 266 /* 267 * First time around, enable the IDB interrupt and clear any 268 * hardware completion status. 269 */ 270 if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) { 271 mlx_outb(mlx, MLX_V1REG_ODB_EN, 1); 272 DELAY(1000); 273 mlx_outb(mlx, MLX_V1REG_ODB, 1); 274 DELAY(1000); 275 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V1_IDB_SACK); 276 DELAY(1000); 277 mlx->mlx_flags |= MLXF_FW_INITTED; 278 } 279 280 /* Init in progress? */ 281 if ((mlx_inb(mlx, MLX_V1REG_IDB) & MLX_V1_IDB_INIT_BUSY) == 0) 282 return (0); 283 284 /* Test error value. */ 285 fwerror = mlx_inb(mlx, MLX_V1REG_ODB); 286 287 if ((fwerror & MLX_V1_FWERROR_PEND) == 0) 288 return (1); 289 290 /* XXX Fetch status. */ 291 *error = fwerror & 0xf0; 292 *param1 = -1; 293 *param2 = -1; 294 295 /* Acknowledge. */ 296 mlx_outb(mlx, MLX_V1REG_ODB, fwerror); 297 298 return (2); 299 } 300 301 #ifdef MLX_RESET 302 /* 303 * Reset the controller. Return non-zero on failure. 304 */ 305 static int 306 mlx_v1_reset(struct mlx_softc *mlx) 307 { 308 int i; 309 310 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V1_IDB_SACK); 311 delay(1000000); 312 313 /* Wait up to 2 minutes for the bit to clear. */ 314 for (i = 120; i != 0; i--) { 315 delay(1000000); 316 if ((mlx_inb(mlx, MLX_V1REG_IDB) & MLX_V1_IDB_SACK) == 0) 317 break; 318 } 319 if (i == 0) 320 return (-1); 321 322 mlx_outb(mlx, MLX_V1REG_ODB, MLX_V1_ODB_RESET); 323 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V1_IDB_RESET); 324 325 /* Wait up to 5 seconds for the bit to clear... */ 326 for (i = 5; i != 0; i--) { 327 delay(1000000); 328 if ((mlx_inb(mlx, MLX_V1REG_IDB) & MLX_V1_IDB_RESET) == 0) 329 break; 330 } 331 if (i == 0) 332 return (-1); 333 334 /* Wait up to 3 seconds for the other bit to clear... */ 335 for (i = 5; i != 0; i--) { 336 delay(1000000); 337 if ((mlx_inb(mlx, MLX_V1REG_ODB) & MLX_V1_ODB_RESET) == 0) 338 break; 339 } 340 if (i == 0) 341 return (-1); 342 343 return (0); 344 } 345 #endif /* MLX_RESET */ 346