1 /* $NetBSD: mlx_eisa.c,v 1.26 2016/09/27 03:33:32 pgoyette 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * EISA front-end for mlx(4) driver. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: mlx_eisa.c,v 1.26 2016/09/27 03:33:32 pgoyette Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/device.h> 42 #include <sys/module.h> 43 #include <sys/bus.h> 44 #include <sys/intr.h> 45 46 #include <dev/eisa/eisavar.h> 47 #include <dev/eisa/eisadevs.h> 48 49 #include <dev/ic/mlxreg.h> 50 #include <dev/ic/mlxio.h> 51 #include <dev/ic/mlxvar.h> 52 53 #include "ioconf.h" 54 55 #define MLX_EISA_SLOT_OFFSET 0x0c80 56 #define MLX_EISA_IOSIZE (0x0ce0 - MLX_EISA_SLOT_OFFSET) 57 #define MLX_EISA_CFG01 (0x0cc0 - MLX_EISA_SLOT_OFFSET) 58 #define MLX_EISA_CFG02 (0x0cc1 - MLX_EISA_SLOT_OFFSET) 59 #define MLX_EISA_CFG03 (0x0cc3 - MLX_EISA_SLOT_OFFSET) 60 #define MLX_EISA_CFG04 (0x0c8d - MLX_EISA_SLOT_OFFSET) 61 #define MLX_EISA_CFG05 (0x0c90 - MLX_EISA_SLOT_OFFSET) 62 #define MLX_EISA_CFG06 (0x0c91 - MLX_EISA_SLOT_OFFSET) 63 #define MLX_EISA_CFG07 (0x0c92 - MLX_EISA_SLOT_OFFSET) 64 #define MLX_EISA_CFG08 (0x0c93 - MLX_EISA_SLOT_OFFSET) 65 #define MLX_EISA_CFG09 (0x0c94 - MLX_EISA_SLOT_OFFSET) 66 #define MLX_EISA_CFG10 (0x0c95 - MLX_EISA_SLOT_OFFSET) 67 68 static void mlx_eisa_attach(device_t, device_t, void *); 69 static int mlx_eisa_match(device_t, cfdata_t, void *); 70 static int mlx_eisa_rescan(device_t, const char *, const int *); 71 72 static int mlx_v1_submit(struct mlx_softc *, struct mlx_ccb *); 73 static int mlx_v1_findcomplete(struct mlx_softc *, u_int *, u_int *); 74 static void mlx_v1_intaction(struct mlx_softc *, int); 75 static int mlx_v1_fw_handshake(struct mlx_softc *, int *, int *, int *); 76 #ifdef MLX_RESET 77 static int mlx_v1_reset(struct mlx_softc *); 78 #endif 79 80 CFATTACH_DECL3_NEW(mlx_eisa, sizeof(struct mlx_softc), 81 mlx_eisa_match, mlx_eisa_attach, NULL, NULL, mlx_eisa_rescan, NULL, 0); 82 83 static struct mlx_eisa_prod { 84 const char *mp_idstr; 85 int mp_nchan; 86 } const mlx_eisa_prod[] = { 87 { "MLX0070", 1 }, 88 { "MLX0071", 3 }, 89 { "MLX0072", 3 }, 90 { "MLX0073", 2 }, 91 { "MLX0074", 1 }, 92 { "MLX0075", 3 }, 93 { "MLX0076", 2 }, 94 { "MLX0077", 1 }, 95 }; 96 97 static int 98 mlx_eisa_match(device_t parent, cfdata_t match, 99 void *aux) 100 { 101 struct eisa_attach_args *ea; 102 int i; 103 104 ea = aux; 105 106 for (i = 0; i < sizeof(mlx_eisa_prod) / sizeof(mlx_eisa_prod[0]); i++) 107 if (strcmp(ea->ea_idstring, mlx_eisa_prod[i].mp_idstr) == 0) 108 return (1); 109 110 return (0); 111 } 112 113 static void 114 mlx_eisa_attach(device_t parent, device_t self, void *aux) 115 { 116 struct eisa_attach_args *ea; 117 bus_space_handle_t ioh; 118 eisa_chipset_tag_t ec; 119 eisa_intr_handle_t ih; 120 struct mlx_softc *mlx; 121 bus_space_tag_t iot; 122 const char *intrstr; 123 int irq, i, icfg; 124 char intrbuf[EISA_INTRSTR_LEN]; 125 126 ea = aux; 127 mlx = device_private(self); 128 iot = ea->ea_iot; 129 ec = ea->ea_ec; 130 131 if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) + 132 MLX_EISA_SLOT_OFFSET, MLX_EISA_IOSIZE, 0, &ioh)) { 133 aprint_error(": can't map i/o space\n"); 134 return; 135 } 136 137 mlx->mlx_dv = self; 138 mlx->mlx_iot = iot; 139 mlx->mlx_ioh = ioh; 140 mlx->mlx_dmat = ea->ea_dmat; 141 142 /* 143 * Map and establish the interrupt. 144 */ 145 icfg = bus_space_read_1(iot, ioh, MLX_EISA_CFG03); 146 147 switch (icfg & 0xf0) { 148 case 0xa0: 149 irq = 11; 150 break; 151 case 0xc0: 152 irq = 12; 153 break; 154 case 0xe0: 155 irq = 14; 156 break; 157 case 0x80: 158 irq = 15; 159 break; 160 default: 161 aprint_error(": controller on invalid IRQ\n"); 162 return; 163 } 164 165 if (eisa_intr_map(ec, irq, &ih)) { 166 aprint_error(": can't map interrupt (%d)\n", irq); 167 return; 168 } 169 170 intrstr = eisa_intr_string(ec, ih, intrbuf, sizeof(intrbuf)); 171 mlx->mlx_ih = eisa_intr_establish(ec, ih, 172 ((icfg & 0x08) != 0 ? IST_LEVEL : IST_EDGE), 173 IPL_BIO, mlx_intr, mlx); 174 if (mlx->mlx_ih == NULL) { 175 aprint_error(": can't establish interrupt"); 176 if (intrstr != NULL) 177 aprint_normal(" at %s", intrstr); 178 aprint_normal("\n"); 179 return; 180 } 181 182 for (i = 0; i < sizeof(mlx_eisa_prod) / sizeof(mlx_eisa_prod[0]); i++) 183 if (strcmp(ea->ea_idstring, mlx_eisa_prod[i].mp_idstr) == 0) { 184 mlx->mlx_ci.ci_nchan = mlx_eisa_prod[i].mp_nchan; 185 break; 186 } 187 mlx->mlx_ci.ci_iftype = 1; 188 189 mlx->mlx_submit = mlx_v1_submit; 190 mlx->mlx_findcomplete = mlx_v1_findcomplete; 191 mlx->mlx_intaction = mlx_v1_intaction; 192 mlx->mlx_fw_handshake = mlx_v1_fw_handshake; 193 #ifdef MLX_RESET 194 mlx->mlx_reset = mlx_v1_reset; 195 #endif 196 197 aprint_normal(": Mylex RAID\n"); 198 mlx_init(mlx, intrstr); 199 } 200 201 static int 202 mlx_eisa_rescan(device_t self, const char *attr, const int *flag) 203 { 204 205 return mlx_configure(device_private(self), 1); 206 } 207 208 /* 209 * ================= V1 interface linkage ================= 210 */ 211 212 /* 213 * Try to give (mc) to the controller. Returns 1 if successful, 0 on 214 * failure (the controller is not ready to take a command). 215 * 216 * Must be called at splbio or in a fashion that prevents reentry. 217 */ 218 static int 219 mlx_v1_submit(struct mlx_softc *mlx, struct mlx_ccb *mc) 220 { 221 222 /* Ready for our command? */ 223 if ((mlx_inb(mlx, MLX_V1REG_IDB) & MLX_V1_IDB_FULL) == 0) { 224 /* Copy mailbox data to window. */ 225 bus_space_write_region_1(mlx->mlx_iot, mlx->mlx_ioh, 226 MLX_V1REG_MAILBOX, mc->mc_mbox, 13); 227 bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, 228 MLX_V1REG_MAILBOX, 13, 229 BUS_SPACE_BARRIER_WRITE); 230 231 /* Post command. */ 232 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V1_IDB_FULL); 233 return (1); 234 } 235 236 return (0); 237 } 238 239 /* 240 * See if a command has been completed, if so acknowledge its completion and 241 * recover the slot number and status code. 242 * 243 * Must be called at splbio or in a fashion that prevents reentry. 244 */ 245 static int 246 mlx_v1_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status) 247 { 248 249 /* Status available? */ 250 if ((mlx_inb(mlx, MLX_V1REG_ODB) & MLX_V1_ODB_SAVAIL) != 0) { 251 *slot = mlx_inb(mlx, MLX_V1REG_MAILBOX + 0x0d); 252 *status = mlx_inw(mlx, MLX_V1REG_MAILBOX + 0x0e); 253 254 /* Acknowledge completion. */ 255 mlx_outb(mlx, MLX_V1REG_ODB, MLX_V1_ODB_SAVAIL); 256 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V1_IDB_SACK); 257 return (1); 258 } 259 260 return (0); 261 } 262 263 /* 264 * Enable/disable interrupts as requested. (No acknowledge required) 265 * 266 * Must be called at splbio or in a fashion that prevents reentry. 267 */ 268 static void 269 mlx_v1_intaction(struct mlx_softc *mlx, int action) 270 { 271 272 mlx_outb(mlx, MLX_V1REG_IE, action ? 1 : 0); 273 } 274 275 /* 276 * Poll for firmware error codes during controller initialisation. 277 * 278 * Returns 0 if initialisation is complete, 1 if still in progress but no 279 * error has been fetched, 2 if an error has been retrieved. 280 */ 281 static int 282 mlx_v1_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2) 283 { 284 u_int8_t fwerror; 285 286 /* 287 * First time around, enable the IDB interrupt and clear any 288 * hardware completion status. 289 */ 290 if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) { 291 mlx_outb(mlx, MLX_V1REG_ODB_EN, 1); 292 DELAY(1000); 293 mlx_outb(mlx, MLX_V1REG_ODB, 1); 294 DELAY(1000); 295 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V1_IDB_SACK); 296 DELAY(1000); 297 mlx->mlx_flags |= MLXF_FW_INITTED; 298 } 299 300 /* Init in progress? */ 301 if ((mlx_inb(mlx, MLX_V1REG_IDB) & MLX_V1_IDB_INIT_BUSY) == 0) 302 return (0); 303 304 /* Test error value. */ 305 fwerror = mlx_inb(mlx, MLX_V1REG_ODB); 306 307 if ((fwerror & MLX_V1_FWERROR_PEND) == 0) 308 return (1); 309 310 /* XXX Fetch status. */ 311 *error = fwerror & 0xf0; 312 *param1 = -1; 313 *param2 = -1; 314 315 /* Acknowledge. */ 316 mlx_outb(mlx, MLX_V1REG_ODB, fwerror); 317 318 return (2); 319 } 320 321 #ifdef MLX_RESET 322 /* 323 * Reset the controller. Return non-zero on failure. 324 */ 325 static int 326 mlx_v1_reset(struct mlx_softc *mlx) 327 { 328 int i; 329 330 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V1_IDB_SACK); 331 delay(1000000); 332 333 /* Wait up to 2 minutes for the bit to clear. */ 334 for (i = 120; i != 0; i--) { 335 delay(1000000); 336 if ((mlx_inb(mlx, MLX_V1REG_IDB) & MLX_V1_IDB_SACK) == 0) 337 break; 338 } 339 if (i == 0) 340 return (-1); 341 342 mlx_outb(mlx, MLX_V1REG_ODB, MLX_V1_ODB_RESET); 343 mlx_outb(mlx, MLX_V1REG_IDB, MLX_V1_IDB_RESET); 344 345 /* Wait up to 5 seconds for the bit to clear... */ 346 for (i = 5; i != 0; i--) { 347 delay(1000000); 348 if ((mlx_inb(mlx, MLX_V1REG_IDB) & MLX_V1_IDB_RESET) == 0) 349 break; 350 } 351 if (i == 0) 352 return (-1); 353 354 /* Wait up to 3 seconds for the other bit to clear... */ 355 for (i = 5; i != 0; i--) { 356 delay(1000000); 357 if ((mlx_inb(mlx, MLX_V1REG_ODB) & MLX_V1_ODB_RESET) == 0) 358 break; 359 } 360 if (i == 0) 361 return (-1); 362 363 return (0); 364 } 365 #endif /* MLX_RESET */ 366 367 MODULE(MODULE_CLASS_DRIVER, mlx_eisa, "mlx"); /* No eisa module yet! */ 368 369 #ifdef _MODULE 370 /* 371 * XXX Don't allow ioconf.c to redefine the "struct cfdriver cac_cd" 372 * XXX it will be defined in the common-code module 373 */ 374 #undef CFDRIVER_DECL 375 #define CFDRIVER_DECL(name, class, attr) 376 #include "ioconf.c" 377 #endif 378 379 static int 380 mlx_eisa_modcmd(modcmd_t cmd, void *opaque) 381 { 382 int error = 0; 383 384 #ifdef _MODULE 385 switch (cmd) { 386 case MODULE_CMD_INIT: 387 /* 388 * We skip over the first entry in cfdriver[] array 389 * since the cfdriver is attached by the common 390 * (non-attachment-specific) code. 391 */ 392 error = config_init_component(&cfdriver_ioconf_mlx_eisa[1], 393 cfattach_ioconf_mlx_eisa, cfdata_ioconf_mlx_eisa); 394 break; 395 case MODULE_CMD_FINI: 396 error = config_fini_component(&cfdriver_ioconf_mlx_eisa[1], 397 cfattach_ioconf_mlx_eisa, cfdata_ioconf_mlx_eisa); 398 break; 399 default: 400 error = ENOTTY; 401 break; 402 } 403 #endif 404 405 return error; 406 } 407