| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /*- |
| 2 | * Copyright (c) 1999,2000 Michael Smith | |
| 3 | * Copyright (c) 2000 BSDi | |
| 4 | * All rights reserved. | |
| 5 | * | |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions | |
| 8 | * are met: | |
| 9 | * 1. Redistributions of source code must retain the above copyright | |
| 10 | * notice, this list of conditions and the following disclaimer. | |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer in the | |
| 13 | * documentation and/or other materials provided with the distribution. | |
| 14 | * | |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 25 | * SUCH DAMAGE. | |
| 26 | * | |
| 27 | * Copyright (c) 2002 Eric Moore | |
| 28 | * Copyright (c) 2002 LSI Logic Corporation | |
| 29 | * All rights reserved. | |
| 30 | * | |
| 31 | * Redistribution and use in source and binary forms, with or without | |
| 32 | * modification, are permitted provided that the following conditions | |
| 33 | * are met: | |
| 34 | * 1. Redistributions of source code must retain the above copyright | |
| 35 | * notice, this list of conditions and the following disclaimer. | |
| 36 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 37 | * notice, this list of conditions and the following disclaimer in the | |
| 38 | * documentation and/or other materials provided with the distribution. | |
| 39 | * 3. The party using or redistributing the source code and binary forms | |
| 40 | * agrees to the disclaimer below and the terms and conditions set forth | |
| 41 | * herein. | |
| 42 | * | |
| 43 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 44 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 45 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 46 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 47 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 48 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 49 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 50 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 51 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 52 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 53 | * SUCH DAMAGE. | |
| 54 | * | |
| 55 | * $FreeBSD: src/sys/dev/amr/amr.c,v 1.7.2.13 2003/01/15 13:41:18 emoore Exp $ | |
| d0600478 | 56 | * $DragonFly: src/sys/dev/raid/amr/amr.c,v 1.28 2008/09/16 14:08:48 swildner Exp $ |
| 984263bc MD |
57 | */ |
| 58 | ||
| 59 | /* | |
| 60 | * Driver for the AMI MegaRaid family of controllers. | |
| 61 | */ | |
| 62 | ||
| 63 | #include <sys/param.h> | |
| 64 | #include <sys/systm.h> | |
| 65 | #include <sys/malloc.h> | |
| 66 | #include <sys/kernel.h> | |
| 67 | ||
| 1f2de5d4 | 68 | #include "amr_compat.h" |
| 984263bc MD |
69 | #include <sys/bus.h> |
| 70 | #include <sys/conf.h> | |
| 71 | #include <sys/devicestat.h> | |
| 72 | #include <sys/disk.h> | |
| 73 | #include <sys/stat.h> | |
| 984263bc MD |
74 | #include <sys/rman.h> |
| 75 | ||
| 1f2de5d4 MD |
76 | #include <bus/pci/pcireg.h> |
| 77 | #include <bus/pci/pcivar.h> | |
| 984263bc | 78 | |
| 1f2de5d4 MD |
79 | #include "amrio.h" |
| 80 | #include "amrreg.h" | |
| 81 | #include "amrvar.h" | |
| 984263bc | 82 | #define AMR_DEFINE_TABLES |
| 1f2de5d4 | 83 | #include "amr_tables.h" |
| 984263bc MD |
84 | |
| 85 | #define AMR_CDEV_MAJOR 132 | |
| 86 | ||
| 87 | static d_open_t amr_open; | |
| 88 | static d_close_t amr_close; | |
| 89 | static d_ioctl_t amr_ioctl; | |
| 90 | ||
| fef8985e MD |
91 | static struct dev_ops amr_ops = { |
| 92 | { "amr", AMR_CDEV_MAJOR, 0 }, | |
| 93 | .d_open = amr_open, | |
| 94 | .d_close = amr_close, | |
| 95 | .d_ioctl = amr_ioctl | |
| 984263bc MD |
96 | }; |
| 97 | ||
| 98 | /* | |
| 99 | * Initialisation, bus interface. | |
| 100 | */ | |
| 101 | static void amr_startup(void *arg); | |
| 102 | ||
| 103 | /* | |
| 104 | * Command wrappers | |
| 105 | */ | |
| 106 | static int amr_query_controller(struct amr_softc *sc); | |
| 107 | static void *amr_enquiry(struct amr_softc *sc, size_t bufsize, | |
| 108 | u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual); | |
| 109 | static void amr_completeio(struct amr_command *ac); | |
| 110 | static int amr_support_ext_cdb(struct amr_softc *sc); | |
| 111 | ||
| 112 | /* | |
| 113 | * Command buffer allocation. | |
| 114 | */ | |
| 115 | static void amr_alloccmd_cluster(struct amr_softc *sc); | |
| 116 | static void amr_freecmd_cluster(struct amr_command_cluster *acc); | |
| 117 | ||
| 118 | /* | |
| 119 | * Command processing. | |
| 120 | */ | |
| 121 | static int amr_bio_command(struct amr_softc *sc, struct amr_command **acp); | |
| 122 | static int amr_wait_command(struct amr_command *ac); | |
| 123 | static int amr_getslot(struct amr_command *ac); | |
| 124 | static void amr_mapcmd(struct amr_command *ac); | |
| 125 | static void amr_unmapcmd(struct amr_command *ac); | |
| 126 | static int amr_start(struct amr_command *ac); | |
| 127 | static void amr_complete(void *context, int pending); | |
| 128 | ||
| 129 | /* | |
| 130 | * Status monitoring | |
| 131 | */ | |
| 132 | static void amr_periodic(void *data); | |
| 133 | ||
| 134 | /* | |
| 135 | * Interface-specific shims | |
| 136 | */ | |
| 137 | static int amr_quartz_submit_command(struct amr_softc *sc); | |
| 138 | static int amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave); | |
| 139 | static int amr_quartz_poll_command(struct amr_command *ac); | |
| 140 | ||
| 141 | static int amr_std_submit_command(struct amr_softc *sc); | |
| 142 | static int amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave); | |
| 143 | static int amr_std_poll_command(struct amr_command *ac); | |
| 144 | static void amr_std_attach_mailbox(struct amr_softc *sc); | |
| 145 | ||
| 146 | #ifdef AMR_BOARD_INIT | |
| 147 | static int amr_quartz_init(struct amr_softc *sc); | |
| 148 | static int amr_std_init(struct amr_softc *sc); | |
| 149 | #endif | |
| 150 | ||
| 151 | /* | |
| 152 | * Debugging | |
| 153 | */ | |
| 154 | static void amr_describe_controller(struct amr_softc *sc); | |
| 155 | #ifdef AMR_DEBUG | |
| 156 | #if 0 | |
| 157 | static void amr_printcommand(struct amr_command *ac); | |
| 158 | #endif | |
| 159 | #endif | |
| 160 | ||
| 32832096 MD |
161 | DECLARE_DUMMY_MODULE(amr); |
| 162 | ||
| 984263bc MD |
163 | /******************************************************************************** |
| 164 | ******************************************************************************** | |
| 165 | Inline Glue | |
| 166 | ******************************************************************************** | |
| 167 | ********************************************************************************/ | |
| 168 | ||
| 169 | /******************************************************************************** | |
| 170 | ******************************************************************************** | |
| 171 | Public Interfaces | |
| 172 | ******************************************************************************** | |
| 173 | ********************************************************************************/ | |
| 174 | ||
| 175 | /******************************************************************************** | |
| 176 | * Initialise the controller and softc. | |
| 177 | */ | |
| 178 | int | |
| 179 | amr_attach(struct amr_softc *sc) | |
| 180 | { | |
| 181 | ||
| 182 | debug_called(1); | |
| 183 | ||
| 184 | /* | |
| 185 | * Initialise per-controller queues. | |
| 186 | */ | |
| 187 | TAILQ_INIT(&sc->amr_completed); | |
| 188 | TAILQ_INIT(&sc->amr_freecmds); | |
| 189 | TAILQ_INIT(&sc->amr_cmd_clusters); | |
| 190 | TAILQ_INIT(&sc->amr_ready); | |
| 191 | bioq_init(&sc->amr_bioq); | |
| 192 | ||
| 42cdd4ab | 193 | #if defined(__FreeBSD__) && __FreeBSD_version >= 500005 |
| 984263bc MD |
194 | /* |
| 195 | * Initialise command-completion task. | |
| 196 | */ | |
| 197 | TASK_INIT(&sc->amr_task_complete, 0, amr_complete, sc); | |
| 198 | #endif | |
| 199 | ||
| 200 | debug(2, "queue init done"); | |
| 201 | ||
| 202 | /* | |
| 203 | * Configure for this controller type. | |
| 204 | */ | |
| 205 | if (AMR_IS_QUARTZ(sc)) { | |
| 206 | sc->amr_submit_command = amr_quartz_submit_command; | |
| 207 | sc->amr_get_work = amr_quartz_get_work; | |
| 208 | sc->amr_poll_command = amr_quartz_poll_command; | |
| 209 | } else { | |
| 210 | sc->amr_submit_command = amr_std_submit_command; | |
| 211 | sc->amr_get_work = amr_std_get_work; | |
| 212 | sc->amr_poll_command = amr_std_poll_command; | |
| fc6d0222 | 213 | amr_std_attach_mailbox(sc); |
| 984263bc MD |
214 | } |
| 215 | ||
| 216 | #ifdef AMR_BOARD_INIT | |
| 217 | if ((AMR_IS_QUARTZ(sc) ? amr_quartz_init(sc) : amr_std_init(sc)))) | |
| 218 | return(ENXIO); | |
| 219 | #endif | |
| 220 | ||
| 221 | /* | |
| 222 | * Quiz controller for features and limits. | |
| 223 | */ | |
| 224 | if (amr_query_controller(sc)) | |
| 225 | return(ENXIO); | |
| 226 | ||
| 227 | debug(2, "controller query complete"); | |
| 228 | ||
| 229 | /* | |
| 230 | * Attach our 'real' SCSI channels to CAM. | |
| 231 | */ | |
| 232 | if (amr_cam_attach(sc)) | |
| 233 | return(ENXIO); | |
| 234 | debug(2, "CAM attach done"); | |
| 235 | ||
| 236 | /* | |
| 237 | * Create the control device. | |
| 238 | */ | |
| fef8985e | 239 | sc->amr_dev_t = make_dev(&amr_ops, device_get_unit(sc->amr_dev), |
| e4c9c0c8 MD |
240 | UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, |
| 241 | "amr%d", device_get_unit(sc->amr_dev)); | |
| 984263bc | 242 | sc->amr_dev_t->si_drv1 = sc; |
| e4c9c0c8 | 243 | reference_dev(sc->amr_dev_t); |
| 984263bc MD |
244 | |
| 245 | /* | |
| 246 | * Schedule ourselves to bring the controller up once interrupts are | |
| 247 | * available. | |
| 248 | */ | |
| 249 | bzero(&sc->amr_ich, sizeof(struct intr_config_hook)); | |
| 250 | sc->amr_ich.ich_func = amr_startup; | |
| 251 | sc->amr_ich.ich_arg = sc; | |
| a1e26a0c | 252 | sc->amr_ich.ich_desc = "amr"; |
| 984263bc MD |
253 | if (config_intrhook_establish(&sc->amr_ich) != 0) { |
| 254 | device_printf(sc->amr_dev, "can't establish configuration hook\n"); | |
| 255 | return(ENOMEM); | |
| 256 | } | |
| 257 | ||
| 258 | /* | |
| 259 | * Print a little information about the controller. | |
| 260 | */ | |
| 261 | amr_describe_controller(sc); | |
| 262 | ||
| 263 | debug(2, "attach complete"); | |
| 264 | return(0); | |
| 265 | } | |
| 266 | ||
| 267 | /******************************************************************************** | |
| 268 | * Locate disk resources and attach children to them. | |
| 269 | */ | |
| 270 | static void | |
| 271 | amr_startup(void *arg) | |
| 272 | { | |
| 273 | struct amr_softc *sc = (struct amr_softc *)arg; | |
| 274 | struct amr_logdrive *dr; | |
| 275 | int i, error; | |
| 276 | ||
| 277 | debug_called(1); | |
| 0cdb68f0 | 278 | callout_init(&sc->amr_timeout); |
| 984263bc MD |
279 | |
| 280 | /* pull ourselves off the intrhook chain */ | |
| 281 | config_intrhook_disestablish(&sc->amr_ich); | |
| 282 | ||
| 283 | /* get up-to-date drive information */ | |
| 284 | if (amr_query_controller(sc)) { | |
| 285 | device_printf(sc->amr_dev, "can't scan controller for drives\n"); | |
| 286 | return; | |
| 287 | } | |
| 288 | ||
| 289 | /* iterate over available drives */ | |
| 290 | for (i = 0, dr = &sc->amr_drive[0]; (i < AMR_MAXLD) && (dr->al_size != 0xffffffff); i++, dr++) { | |
| 291 | /* are we already attached to this drive? */ | |
| 292 | if (dr->al_disk == 0) { | |
| 293 | /* generate geometry information */ | |
| 294 | if (dr->al_size > 0x200000) { /* extended translation? */ | |
| 295 | dr->al_heads = 255; | |
| 296 | dr->al_sectors = 63; | |
| 297 | } else { | |
| 298 | dr->al_heads = 64; | |
| 299 | dr->al_sectors = 32; | |
| 300 | } | |
| 301 | dr->al_cylinders = dr->al_size / (dr->al_heads * dr->al_sectors); | |
| 302 | ||
| 303 | dr->al_disk = device_add_child(sc->amr_dev, NULL, -1); | |
| 304 | if (dr->al_disk == 0) | |
| 305 | device_printf(sc->amr_dev, "device_add_child failed\n"); | |
| 306 | device_set_ivars(dr->al_disk, dr); | |
| 307 | } | |
| 308 | } | |
| 309 | ||
| 310 | if ((error = bus_generic_attach(sc->amr_dev)) != 0) | |
| 311 | device_printf(sc->amr_dev, "bus_generic_attach returned %d\n", error); | |
| 312 | ||
| 313 | /* mark controller back up */ | |
| 314 | sc->amr_state &= ~AMR_STATE_SHUTDOWN; | |
| 315 | ||
| 316 | /* interrupts will be enabled before we do anything more */ | |
| 317 | sc->amr_state |= AMR_STATE_INTEN; | |
| 318 | ||
| 319 | /* | |
| 320 | * Start the timeout routine. | |
| 321 | */ | |
| 9087698d | 322 | /* callout_reset(&sc->amr_timeout, hz, amr_periodic, sc); */ |
| 984263bc MD |
323 | |
| 324 | return; | |
| 325 | } | |
| 326 | ||
| 327 | /******************************************************************************* | |
| 328 | * Free resources associated with a controller instance | |
| 329 | */ | |
| 330 | void | |
| 331 | amr_free(struct amr_softc *sc) | |
| 332 | { | |
| 333 | struct amr_command_cluster *acc; | |
| 334 | ||
| 335 | /* detach from CAM */ | |
| 336 | amr_cam_detach(sc); | |
| 337 | ||
| 338 | /* cancel status timeout */ | |
| 0cdb68f0 | 339 | callout_stop(&sc->amr_timeout); |
| 984263bc MD |
340 | |
| 341 | /* throw away any command buffers */ | |
| 342 | while ((acc = TAILQ_FIRST(&sc->amr_cmd_clusters)) != NULL) { | |
| 343 | TAILQ_REMOVE(&sc->amr_cmd_clusters, acc, acc_link); | |
| 344 | amr_freecmd_cluster(acc); | |
| 345 | } | |
| 346 | ||
| 347 | /* destroy control device */ | |
| b13267a5 | 348 | if( sc->amr_dev_t != (cdev_t)NULL) |
| 984263bc | 349 | destroy_dev(sc->amr_dev_t); |
| cd29885a | 350 | dev_ops_remove_minor(&amr_ops, device_get_unit(sc->amr_dev)); |
| 984263bc MD |
351 | } |
| 352 | ||
| 353 | /******************************************************************************* | |
| 354 | * Receive a bio structure from a child device and queue it on a particular | |
| 355 | * disk resource, then poke the disk resource to start as much work as it can. | |
| 356 | */ | |
| 357 | int | |
| 358 | amr_submit_bio(struct amr_softc *sc, struct bio *bio) | |
| 359 | { | |
| 360 | debug_called(2); | |
| 361 | ||
| 362 | amr_enqueue_bio(sc, bio); | |
| 363 | amr_startio(sc); | |
| 364 | return(0); | |
| 365 | } | |
| 366 | ||
| 367 | /******************************************************************************** | |
| 368 | * Accept an open operation on the control device. | |
| 369 | */ | |
| 370 | static int | |
| fef8985e | 371 | amr_open(struct dev_open_args *ap) |
| 984263bc | 372 | { |
| b13267a5 | 373 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
374 | int unit = minor(dev); |
| 375 | struct amr_softc *sc = devclass_get_softc(devclass_find("amr"), unit); | |
| 376 | ||
| 377 | debug_called(1); | |
| 378 | ||
| 379 | sc->amr_state |= AMR_STATE_OPEN; | |
| 380 | return(0); | |
| 381 | } | |
| 382 | ||
| 383 | /******************************************************************************** | |
| 384 | * Accept the last close on the control device. | |
| 385 | */ | |
| 386 | static int | |
| fef8985e | 387 | amr_close(struct dev_close_args *ap) |
| 984263bc | 388 | { |
| b13267a5 | 389 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
390 | int unit = minor(dev); |
| 391 | struct amr_softc *sc = devclass_get_softc(devclass_find("amr"), unit); | |
| 392 | ||
| 393 | debug_called(1); | |
| 394 | ||
| 395 | sc->amr_state &= ~AMR_STATE_OPEN; | |
| 396 | return (0); | |
| 397 | } | |
| 398 | ||
| 399 | /******************************************************************************** | |
| 400 | * Handle controller-specific control operations. | |
| 401 | */ | |
| 402 | static int | |
| fef8985e | 403 | amr_ioctl(struct dev_ioctl_args *ap) |
| 984263bc | 404 | { |
| b13267a5 | 405 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc | 406 | struct amr_softc *sc = (struct amr_softc *)dev->si_drv1; |
| fef8985e MD |
407 | int *arg = (int *)ap->a_data; |
| 408 | struct amr_user_ioctl *au = (struct amr_user_ioctl *)ap->a_data; | |
| 984263bc MD |
409 | struct amr_command *ac; |
| 410 | struct amr_mailbox_ioctl *mbi; | |
| fef8985e | 411 | struct amr_passthrough *apt; |
| 984263bc MD |
412 | void *dp; |
| 413 | int error; | |
| 414 | ||
| 415 | debug_called(1); | |
| 416 | ||
| 417 | error = 0; | |
| 418 | dp = NULL; | |
| fef8985e | 419 | apt = NULL; |
| 984263bc | 420 | ac = NULL; |
| fef8985e | 421 | switch(ap->a_cmd) { |
| 984263bc MD |
422 | |
| 423 | case AMR_IO_VERSION: | |
| 424 | debug(1, "AMR_IO_VERSION"); | |
| 425 | *arg = AMR_IO_VERSION_NUMBER; | |
| 426 | break; | |
| 427 | ||
| 428 | case AMR_IO_COMMAND: | |
| 429 | debug(1, "AMR_IO_COMMAND 0x%x", au->au_cmd[0]); | |
| 430 | /* handle inbound data buffer */ | |
| 431 | if (au->au_length != 0) { | |
| 978400d3 | 432 | dp = kmalloc(au->au_length, M_DEVBUF, M_WAITOK); |
| 984263bc MD |
433 | if ((error = copyin(au->au_buffer, dp, au->au_length)) != 0) |
| 434 | break; | |
| 435 | debug(2, "copyin %ld bytes from %p -> %p", au->au_length, au->au_buffer, dp); | |
| 436 | } | |
| 437 | ||
| 438 | if ((ac = amr_alloccmd(sc)) == NULL) { | |
| 439 | error = ENOMEM; | |
| 440 | break; | |
| 441 | } | |
| 442 | ||
| 443 | /* handle SCSI passthrough command */ | |
| 444 | if (au->au_cmd[0] == AMR_CMD_PASS) { | |
| 978400d3 | 445 | apt = kmalloc(sizeof(*apt), M_DEVBUF, M_WAITOK | M_ZERO); |
| 984263bc MD |
446 | |
| 447 | /* copy cdb */ | |
| fef8985e MD |
448 | apt->ap_cdb_length = au->au_cmd[2]; |
| 449 | bcopy(&au->au_cmd[3], &apt->ap_cdb[0], apt->ap_cdb_length); | |
| 984263bc MD |
450 | |
| 451 | /* build passthrough */ | |
| fef8985e MD |
452 | apt->ap_timeout = au->au_cmd[apt->ap_cdb_length + 3] & 0x07; |
| 453 | apt->ap_ars = (au->au_cmd[apt->ap_cdb_length + 3] & 0x08) ? 1 : 0; | |
| 454 | apt->ap_islogical = (au->au_cmd[apt->ap_cdb_length + 3] & 0x80) ? 1 : 0; | |
| 455 | apt->ap_logical_drive_no = au->au_cmd[apt->ap_cdb_length + 4]; | |
| 456 | apt->ap_channel = au->au_cmd[apt->ap_cdb_length + 5]; | |
| 457 | apt->ap_scsi_id = au->au_cmd[apt->ap_cdb_length + 6]; | |
| 458 | apt->ap_request_sense_length = 14; | |
| 459 | apt->ap_data_transfer_length = au->au_length; | |
| 984263bc MD |
460 | /* XXX what about the request-sense area? does the caller want it? */ |
| 461 | ||
| 462 | /* build command */ | |
| fef8985e MD |
463 | ac->ac_data = apt; |
| 464 | ac->ac_length = sizeof(*apt); | |
| 984263bc MD |
465 | ac->ac_flags |= AMR_CMD_DATAOUT; |
| 466 | ac->ac_ccb_data = dp; | |
| 467 | ac->ac_ccb_length = au->au_length; | |
| 468 | if (au->au_direction & AMR_IO_READ) | |
| 469 | ac->ac_flags |= AMR_CMD_CCB_DATAIN; | |
| 470 | if (au->au_direction & AMR_IO_WRITE) | |
| 471 | ac->ac_flags |= AMR_CMD_CCB_DATAOUT; | |
| 472 | ||
| 473 | ac->ac_mailbox.mb_command = AMR_CMD_PASS; | |
| 474 | ||
| 475 | } else { | |
| 476 | /* direct command to controller */ | |
| 477 | mbi = (struct amr_mailbox_ioctl *)&ac->ac_mailbox; | |
| 478 | ||
| 479 | /* copy pertinent mailbox items */ | |
| 480 | mbi->mb_command = au->au_cmd[0]; | |
| 481 | mbi->mb_channel = au->au_cmd[1]; | |
| 482 | mbi->mb_param = au->au_cmd[2]; | |
| 483 | mbi->mb_pad[0] = au->au_cmd[3]; | |
| 484 | mbi->mb_drive = au->au_cmd[4]; | |
| 485 | ||
| 486 | /* build the command */ | |
| 487 | ac->ac_data = dp; | |
| 488 | ac->ac_length = au->au_length; | |
| 489 | if (au->au_direction & AMR_IO_READ) | |
| 490 | ac->ac_flags |= AMR_CMD_DATAIN; | |
| 491 | if (au->au_direction & AMR_IO_WRITE) | |
| 492 | ac->ac_flags |= AMR_CMD_DATAOUT; | |
| 493 | } | |
| 494 | ||
| 495 | /* run the command */ | |
| 496 | if ((error = amr_wait_command(ac)) != 0) | |
| 497 | break; | |
| 498 | ||
| 499 | /* copy out data and set status */ | |
| 500 | if (au->au_length != 0) | |
| 501 | error = copyout(dp, au->au_buffer, au->au_length); | |
| 502 | debug(2, "copyout %ld bytes from %p -> %p", au->au_length, dp, au->au_buffer); | |
| 503 | if (dp != NULL) | |
| 504 | debug(2, "%16d", (int)dp); | |
| 505 | au->au_status = ac->ac_status; | |
| 506 | break; | |
| 507 | ||
| 508 | default: | |
| 509 | debug(1, "unknown ioctl 0x%lx", cmd); | |
| 510 | error = ENOIOCTL; | |
| 511 | break; | |
| 512 | } | |
| 513 | ||
| 514 | if (dp != NULL) | |
| efda3bd0 | 515 | kfree(dp, M_DEVBUF); |
| fef8985e | 516 | if (apt != NULL) |
| efda3bd0 | 517 | kfree(apt, M_DEVBUF); |
| 984263bc MD |
518 | if (ac != NULL) |
| 519 | amr_releasecmd(ac); | |
| 520 | return(error); | |
| 521 | } | |
| 522 | ||
| 523 | /******************************************************************************** | |
| 524 | ******************************************************************************** | |
| 525 | Status Monitoring | |
| 526 | ******************************************************************************** | |
| 527 | ********************************************************************************/ | |
| 528 | ||
| 529 | /******************************************************************************** | |
| 530 | * Perform a periodic check of the controller status | |
| 531 | */ | |
| 532 | static void | |
| 533 | amr_periodic(void *data) | |
| 534 | { | |
| 535 | struct amr_softc *sc = (struct amr_softc *)data; | |
| 536 | ||
| 537 | debug_called(2); | |
| 538 | ||
| 539 | /* XXX perform periodic status checks here */ | |
| 540 | ||
| 541 | /* compensate for missed interrupts */ | |
| 542 | amr_done(sc); | |
| 543 | ||
| 544 | /* reschedule */ | |
| 0cdb68f0 | 545 | callout_reset(&sc->amr_timeout, hz, amr_periodic, sc); |
| 984263bc MD |
546 | } |
| 547 | ||
| 548 | /******************************************************************************** | |
| 549 | ******************************************************************************** | |
| 550 | Command Wrappers | |
| 551 | ******************************************************************************** | |
| 552 | ********************************************************************************/ | |
| 553 | ||
| 554 | /******************************************************************************** | |
| 555 | * Interrogate the controller for the operational parameters we require. | |
| 556 | */ | |
| 557 | static int | |
| 558 | amr_query_controller(struct amr_softc *sc) | |
| 559 | { | |
| 560 | struct amr_enquiry3 *aex; | |
| 561 | struct amr_prodinfo *ap; | |
| 562 | struct amr_enquiry *ae; | |
| 563 | int ldrv; | |
| 564 | ||
| 565 | /* | |
| 566 | * If we haven't found the real limit yet, let us have a couple of commands in | |
| 567 | * order to be able to probe. | |
| 568 | */ | |
| 569 | if (sc->amr_maxio == 0) | |
| 570 | sc->amr_maxio = 2; | |
| 571 | ||
| 572 | /* | |
| 573 | * Greater than 10 byte cdb support | |
| 574 | */ | |
| 575 | sc->support_ext_cdb = amr_support_ext_cdb(sc); | |
| 576 | ||
| 577 | if(sc->support_ext_cdb) { | |
| 578 | debug(2,"supports extended CDBs."); | |
| 579 | } | |
| 580 | ||
| 581 | /* | |
| 582 | * Try to issue an ENQUIRY3 command | |
| 583 | */ | |
| 584 | if ((aex = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3, | |
| 585 | AMR_CONFIG_ENQ3_SOLICITED_FULL)) != NULL) { | |
| 586 | ||
| 587 | /* | |
| 588 | * Fetch current state of logical drives. | |
| 589 | */ | |
| 590 | for (ldrv = 0; ldrv < aex->ae_numldrives; ldrv++) { | |
| 591 | sc->amr_drive[ldrv].al_size = aex->ae_drivesize[ldrv]; | |
| 592 | sc->amr_drive[ldrv].al_state = aex->ae_drivestate[ldrv]; | |
| 593 | sc->amr_drive[ldrv].al_properties = aex->ae_driveprop[ldrv]; | |
| 594 | debug(2, " drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size, | |
| 595 | sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties); | |
| 596 | } | |
| efda3bd0 | 597 | kfree(aex, M_DEVBUF); |
| 984263bc MD |
598 | |
| 599 | /* | |
| 600 | * Get product info for channel count. | |
| 601 | */ | |
| 602 | if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) == NULL) { | |
| 603 | device_printf(sc->amr_dev, "can't obtain product data from controller\n"); | |
| 604 | return(1); | |
| 605 | } | |
| 606 | sc->amr_maxdrives = 40; | |
| 607 | sc->amr_maxchan = ap->ap_nschan; | |
| 608 | sc->amr_maxio = ap->ap_maxio; | |
| 609 | sc->amr_type |= AMR_TYPE_40LD; | |
| efda3bd0 | 610 | kfree(ap, M_DEVBUF); |
| 984263bc MD |
611 | |
| 612 | } else { | |
| 613 | ||
| 614 | /* failed, try the 8LD ENQUIRY commands */ | |
| 615 | if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) == NULL) { | |
| 616 | if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) { | |
| 617 | device_printf(sc->amr_dev, "can't obtain configuration data from controller\n"); | |
| 618 | return(1); | |
| 619 | } | |
| 620 | ae->ae_signature = 0; | |
| 621 | } | |
| 622 | ||
| 623 | /* | |
| 624 | * Fetch current state of logical drives. | |
| 625 | */ | |
| 626 | for (ldrv = 0; ldrv < ae->ae_ldrv.al_numdrives; ldrv++) { | |
| 627 | sc->amr_drive[ldrv].al_size = ae->ae_ldrv.al_size[ldrv]; | |
| 628 | sc->amr_drive[ldrv].al_state = ae->ae_ldrv.al_state[ldrv]; | |
| 629 | sc->amr_drive[ldrv].al_properties = ae->ae_ldrv.al_properties[ldrv]; | |
| 630 | debug(2, " drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size, | |
| 631 | sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties); | |
| 632 | } | |
| 633 | ||
| 634 | sc->amr_maxdrives = 8; | |
| 635 | sc->amr_maxchan = ae->ae_adapter.aa_channels; | |
| 636 | sc->amr_maxio = ae->ae_adapter.aa_maxio; | |
| efda3bd0 | 637 | kfree(ae, M_DEVBUF); |
| 984263bc MD |
638 | } |
| 639 | ||
| 640 | /* | |
| 641 | * Mark remaining drives as unused. | |
| 642 | */ | |
| 643 | for (; ldrv < AMR_MAXLD; ldrv++) | |
| 644 | sc->amr_drive[ldrv].al_size = 0xffffffff; | |
| 645 | ||
| 646 | /* | |
| 647 | * Cap the maximum number of outstanding I/Os. AMI's Linux driver doesn't trust | |
| 648 | * the controller's reported value, and lockups have been seen when we do. | |
| 649 | */ | |
| 650 | sc->amr_maxio = imin(sc->amr_maxio, AMR_LIMITCMD); | |
| 651 | ||
| 652 | return(0); | |
| 653 | } | |
| 654 | ||
| 655 | /******************************************************************************** | |
| 656 | * Run a generic enquiry-style command. | |
| 657 | */ | |
| 658 | static void * | |
| 659 | amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual) | |
| 660 | { | |
| 661 | struct amr_command *ac; | |
| 662 | void *result; | |
| 663 | u_int8_t *mbox; | |
| 664 | int error; | |
| 665 | ||
| 666 | debug_called(1); | |
| 667 | ||
| 668 | error = 1; | |
| 669 | result = NULL; | |
| 670 | ||
| 671 | /* get ourselves a command buffer */ | |
| 672 | if ((ac = amr_alloccmd(sc)) == NULL) | |
| 673 | goto out; | |
| 674 | /* allocate the response structure */ | |
| efda3bd0 | 675 | result = kmalloc(bufsize, M_DEVBUF, M_INTWAIT); |
| 984263bc MD |
676 | /* set command flags */ |
| 677 | ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT; | |
| 678 | ||
| 679 | /* point the command at our data */ | |
| 680 | ac->ac_data = result; | |
| 681 | ac->ac_length = bufsize; | |
| 682 | ||
| 683 | /* build the command proper */ | |
| 684 | mbox = (u_int8_t *)&ac->ac_mailbox; /* XXX want a real structure for this? */ | |
| 685 | mbox[0] = cmd; | |
| 686 | mbox[2] = cmdsub; | |
| 687 | mbox[3] = cmdqual; | |
| 688 | ||
| 689 | /* can't assume that interrupts are going to work here, so play it safe */ | |
| 690 | if (sc->amr_poll_command(ac)) | |
| 691 | goto out; | |
| 692 | error = ac->ac_status; | |
| 693 | ||
| 694 | out: | |
| 695 | if (ac != NULL) | |
| 696 | amr_releasecmd(ac); | |
| 697 | if ((error != 0) && (result != NULL)) { | |
| efda3bd0 | 698 | kfree(result, M_DEVBUF); |
| 984263bc MD |
699 | result = NULL; |
| 700 | } | |
| 701 | return(result); | |
| 702 | } | |
| 703 | ||
| 704 | /******************************************************************************** | |
| 705 | * Flush the controller's internal cache, return status. | |
| 706 | */ | |
| 707 | int | |
| 708 | amr_flush(struct amr_softc *sc) | |
| 709 | { | |
| 710 | struct amr_command *ac; | |
| 711 | int error; | |
| 712 | ||
| 713 | /* get ourselves a command buffer */ | |
| 714 | error = 1; | |
| 715 | if ((ac = amr_alloccmd(sc)) == NULL) | |
| 716 | goto out; | |
| 717 | /* set command flags */ | |
| 718 | ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT; | |
| 719 | ||
| 720 | /* build the command proper */ | |
| 721 | ac->ac_mailbox.mb_command = AMR_CMD_FLUSH; | |
| 722 | ||
| 723 | /* we have to poll, as the system may be going down or otherwise damaged */ | |
| 724 | if (sc->amr_poll_command(ac)) | |
| 725 | goto out; | |
| 726 | error = ac->ac_status; | |
| 727 | ||
| 728 | out: | |
| 729 | if (ac != NULL) | |
| 730 | amr_releasecmd(ac); | |
| 731 | return(error); | |
| 732 | } | |
| 733 | ||
| 734 | /******************************************************************************** | |
| 735 | * Detect extented cdb >> greater than 10 byte cdb support | |
| 736 | * returns '1' means this support exist | |
| 737 | * returns '0' means this support doesn't exist | |
| 738 | */ | |
| 739 | static int | |
| 740 | amr_support_ext_cdb(struct amr_softc *sc) | |
| 741 | { | |
| 742 | struct amr_command *ac; | |
| 743 | u_int8_t *mbox; | |
| 744 | int error; | |
| 745 | ||
| 746 | /* get ourselves a command buffer */ | |
| 747 | error = 0; | |
| 748 | if ((ac = amr_alloccmd(sc)) == NULL) | |
| 749 | goto out; | |
| 750 | /* set command flags */ | |
| 751 | ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT; | |
| 752 | ||
| 753 | /* build the command proper */ | |
| 754 | mbox = (u_int8_t *)&ac->ac_mailbox; /* XXX want a real structure for this? */ | |
| 755 | mbox[0] = 0xA4; | |
| 756 | mbox[2] = 0x16; | |
| 757 | ||
| 758 | ||
| 759 | /* we have to poll, as the system may be going down or otherwise damaged */ | |
| 760 | if (sc->amr_poll_command(ac)) | |
| 761 | goto out; | |
| 762 | if( ac->ac_status == AMR_STATUS_SUCCESS ) { | |
| 763 | error = 1; | |
| 764 | } | |
| 765 | ||
| 766 | out: | |
| 767 | if (ac != NULL) | |
| 768 | amr_releasecmd(ac); | |
| 769 | return(error); | |
| 770 | } | |
| 771 | ||
| 772 | /******************************************************************************** | |
| 773 | * Try to find I/O work for the controller from one or more of the work queues. | |
| 774 | * | |
| 775 | * We make the assumption that if the controller is not ready to take a command | |
| 776 | * at some given time, it will generate an interrupt at some later time when | |
| 777 | * it is. | |
| 778 | */ | |
| 779 | void | |
| 780 | amr_startio(struct amr_softc *sc) | |
| 781 | { | |
| 782 | struct amr_command *ac; | |
| 783 | ||
| 784 | /* spin until something prevents us from doing any work */ | |
| 785 | for (;;) { | |
| 786 | ||
| 787 | /* try to get a ready command */ | |
| 788 | ac = amr_dequeue_ready(sc); | |
| 789 | ||
| 790 | /* if that failed, build a command from a bio */ | |
| 791 | if (ac == NULL) | |
| 792 | (void)amr_bio_command(sc, &ac); | |
| 793 | ||
| 794 | /* if that failed, build a command from a ccb */ | |
| 795 | if (ac == NULL) | |
| 796 | (void)amr_cam_command(sc, &ac); | |
| 797 | ||
| 798 | /* if we don't have anything to do, give up */ | |
| 799 | if (ac == NULL) | |
| 800 | break; | |
| 801 | ||
| 802 | /* try to give the command to the controller; if this fails save it for later and give up */ | |
| 803 | if (amr_start(ac)) { | |
| 804 | debug(2, "controller busy, command deferred"); | |
| 805 | amr_requeue_ready(ac); /* XXX schedule retry very soon? */ | |
| 806 | break; | |
| 807 | } | |
| 808 | } | |
| 809 | } | |
| 810 | ||
| 811 | /******************************************************************************** | |
| 812 | * Handle completion of an I/O command. | |
| 813 | */ | |
| 814 | static void | |
| 815 | amr_completeio(struct amr_command *ac) | |
| 816 | { | |
| 817 | struct amr_softc *sc = ac->ac_sc; | |
| 818 | ||
| 819 | if (ac->ac_status != AMR_STATUS_SUCCESS) { /* could be more verbose here? */ | |
| 81b5c339 MD |
820 | ac->ac_bio->bio_buf->b_error = EIO; |
| 821 | ac->ac_bio->bio_buf->b_flags |= B_ERROR; | |
| 984263bc MD |
822 | |
| 823 | device_printf(sc->amr_dev, "I/O error - 0x%x\n", ac->ac_status); | |
| 824 | /* amr_printcommand(ac);*/ | |
| 825 | } | |
| 826 | amrd_intr(ac->ac_bio); | |
| 827 | amr_releasecmd(ac); | |
| 828 | } | |
| 829 | ||
| 830 | /******************************************************************************** | |
| 831 | ******************************************************************************** | |
| 832 | Command Processing | |
| 833 | ******************************************************************************** | |
| 834 | ********************************************************************************/ | |
| 835 | ||
| 836 | /******************************************************************************** | |
| 837 | * Convert a bio off the top of the bio queue into a command. | |
| 838 | */ | |
| 839 | static int | |
| 840 | amr_bio_command(struct amr_softc *sc, struct amr_command **acp) | |
| 841 | { | |
| 842 | struct amr_command *ac; | |
| 843 | struct amrd_softc *amrd; | |
| 844 | struct bio *bio; | |
| 845 | int error; | |
| 846 | int blkcount; | |
| 847 | int driveno; | |
| 848 | int cmd; | |
| e0fc5693 | 849 | u_int64_t lba; |
| 984263bc MD |
850 | |
| 851 | ac = NULL; | |
| 852 | error = 0; | |
| 853 | ||
| 854 | /* get a bio to work on */ | |
| 855 | if ((bio = amr_dequeue_bio(sc)) == NULL) | |
| 856 | goto out; | |
| 857 | ||
| 858 | /* get a command */ | |
| 859 | if ((ac = amr_alloccmd(sc)) == NULL) { | |
| 860 | error = ENOMEM; | |
| 861 | goto out; | |
| 862 | } | |
| 863 | ||
| 864 | /* connect the bio to the command */ | |
| 865 | ac->ac_complete = amr_completeio; | |
| 866 | ac->ac_bio = bio; | |
| 81b5c339 MD |
867 | ac->ac_data = bio->bio_buf->b_data; |
| 868 | ac->ac_length = bio->bio_buf->b_bcount; | |
| b106cb48 | 869 | |
| d0600478 | 870 | switch (bio->bio_buf->b_cmd) { |
| b106cb48 | 871 | case BUF_CMD_READ: |
| 984263bc MD |
872 | ac->ac_flags |= AMR_CMD_DATAIN; |
| 873 | cmd = AMR_CMD_LREAD; | |
| b106cb48 MD |
874 | break; |
| 875 | case BUF_CMD_WRITE: | |
| 984263bc MD |
876 | ac->ac_flags |= AMR_CMD_DATAOUT; |
| 877 | cmd = AMR_CMD_LWRITE; | |
| b106cb48 MD |
878 | break; |
| 879 | case BUF_CMD_FLUSH: | |
| 880 | ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT; | |
| 881 | cmd = AMR_CMD_FLUSH; | |
| 882 | break; | |
| d0600478 SW |
883 | default: |
| 884 | cmd = 0; | |
| 885 | break; | |
| 984263bc | 886 | } |
| 81b5c339 | 887 | amrd = (struct amrd_softc *)bio->bio_driver_info; |
| 984263bc | 888 | driveno = amrd->amrd_drive - sc->amr_drive; |
| 81b5c339 | 889 | blkcount = (bio->bio_buf->b_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE; |
| b106cb48 | 890 | |
| 54078292 | 891 | lba = bio->bio_offset / AMR_BLKSIZE; |
| e0fc5693 | 892 | KKASSERT(lba < 0x100000000ULL); |
| 984263bc MD |
893 | |
| 894 | ac->ac_mailbox.mb_command = cmd; | |
| 895 | ac->ac_mailbox.mb_blkcount = blkcount; | |
| 54078292 | 896 | ac->ac_mailbox.mb_lba = lba; |
| 984263bc MD |
897 | ac->ac_mailbox.mb_drive = driveno; |
| 898 | /* we fill in the s/g related data when the command is mapped */ | |
| 899 | ||
| 54078292 | 900 | if ((lba + blkcount) > sc->amr_drive[driveno].al_size) |
| e0fc5693 | 901 | device_printf(sc->amr_dev, "I/O beyond end of unit (%lld,%d > %lu)\n", |
| 54078292 | 902 | lba, blkcount, |
| 984263bc MD |
903 | (u_long)sc->amr_drive[driveno].al_size); |
| 904 | ||
| 905 | out: | |
| 906 | if (error != 0) { | |
| 907 | if (ac != NULL) | |
| 908 | amr_releasecmd(ac); | |
| 909 | if (bio != NULL) /* this breaks ordering... */ | |
| 910 | amr_enqueue_bio(sc, bio); | |
| 911 | } | |
| 912 | *acp = ac; | |
| 913 | return(error); | |
| 914 | } | |
| 915 | ||
| 916 | /******************************************************************************** | |
| 917 | * Take a command, submit it to the controller and sleep until it completes | |
| 918 | * or fails. Interrupts must be enabled, returns nonzero on error. | |
| 919 | */ | |
| 920 | static int | |
| 921 | amr_wait_command(struct amr_command *ac) | |
| 922 | { | |
| 923 | int error, count; | |
| 924 | ||
| 925 | debug_called(1); | |
| 926 | ||
| 927 | ac->ac_complete = NULL; | |
| 928 | ac->ac_flags |= AMR_CMD_SLEEP; | |
| 929 | if ((error = amr_start(ac)) != 0) | |
| 930 | return(error); | |
| 931 | ||
| 932 | count = 0; | |
| 933 | /* XXX better timeout? */ | |
| 934 | while ((ac->ac_flags & AMR_CMD_BUSY) && (count < 30)) { | |
| 377d4740 | 935 | tsleep(ac, PCATCH, "amrwcmd", hz); |
| 984263bc MD |
936 | } |
| 937 | return(0); | |
| 938 | } | |
| 939 | ||
| 940 | /******************************************************************************** | |
| 941 | * Take a command, submit it to the controller and busy-wait for it to return. | |
| 942 | * Returns nonzero on error. Can be safely called with interrupts enabled. | |
| 943 | */ | |
| 944 | static int | |
| 945 | amr_std_poll_command(struct amr_command *ac) | |
| 946 | { | |
| 947 | struct amr_softc *sc = ac->ac_sc; | |
| 948 | int error, count; | |
| 949 | ||
| 950 | debug_called(2); | |
| 951 | ||
| 952 | ac->ac_complete = NULL; | |
| 953 | if ((error = amr_start(ac)) != 0) | |
| 954 | return(error); | |
| 955 | ||
| 956 | count = 0; | |
| 957 | do { | |
| 958 | /* | |
| 959 | * Poll for completion, although the interrupt handler may beat us to it. | |
| 960 | * Note that the timeout here is somewhat arbitrary. | |
| 961 | */ | |
| 962 | amr_done(sc); | |
| 963 | DELAY(1000); | |
| 964 | } while ((ac->ac_flags & AMR_CMD_BUSY) && (count++ < 1000)); | |
| 965 | if (!(ac->ac_flags & AMR_CMD_BUSY)) { | |
| 966 | error = 0; | |
| 967 | } else { | |
| 968 | /* XXX the slot is now marked permanently busy */ | |
| 969 | error = EIO; | |
| 970 | device_printf(sc->amr_dev, "polled command timeout\n"); | |
| 971 | } | |
| 972 | return(error); | |
| 973 | } | |
| 974 | ||
| 975 | /******************************************************************************** | |
| 976 | * Take a command, submit it to the controller and busy-wait for it to return. | |
| 977 | * Returns nonzero on error. Can be safely called with interrupts enabled. | |
| 978 | */ | |
| 979 | static int | |
| 980 | amr_quartz_poll_command(struct amr_command *ac) | |
| 981 | { | |
| 982 | struct amr_softc *sc = ac->ac_sc; | |
| 984263bc MD |
983 | int error,count; |
| 984 | ||
| 985 | debug_called(2); | |
| 986 | ||
| 987 | /* now we have a slot, we can map the command (unmapped in amr_complete) */ | |
| 988 | amr_mapcmd(ac); | |
| 989 | ||
| 7f2216bc | 990 | crit_enter(); |
| 984263bc | 991 | |
| 510931cd HP |
992 | if (sc->amr_state & AMR_STATE_INTEN) { |
| 993 | count=0; | |
| 994 | while (sc->amr_busyslots) { | |
| 995 | tsleep(sc, PCATCH, "amrpoll", hz); | |
| 996 | if(count++>10) { | |
| 997 | break; | |
| 998 | } | |
| 999 | } | |
| 1000 | ||
| 1001 | if(sc->amr_busyslots) { | |
| 1002 | device_printf(sc->amr_dev, "adapter is busy\n"); | |
| 7f2216bc | 1003 | crit_exit(); |
| 510931cd HP |
1004 | amr_unmapcmd(ac); |
| 1005 | ac->ac_status=0; | |
| 1006 | return(1); | |
| 1007 | } | |
| 984263bc MD |
1008 | } |
| 1009 | ||
| 1010 | bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE); | |
| 1011 | ||
| 1012 | /* clear the poll/ack fields in the mailbox */ | |
| 1013 | sc->amr_mailbox->mb_ident = 0xFE; | |
| 1014 | sc->amr_mailbox->mb_nstatus = 0xFF; | |
| 1015 | sc->amr_mailbox->mb_status = 0xFF; | |
| 1016 | sc->amr_mailbox->mb_poll = 0; | |
| 1017 | sc->amr_mailbox->mb_ack = 0; | |
| 1018 | sc->amr_mailbox->mb_busy = 1; | |
| 1019 | ||
| 1020 | AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT); | |
| 1021 | ||
| 1022 | while(sc->amr_mailbox->mb_nstatus == 0xFF); | |
| 1023 | while(sc->amr_mailbox->mb_status == 0xFF); | |
| 1024 | ac->ac_status=sc->amr_mailbox->mb_status; | |
| 1025 | error = (ac->ac_status !=AMR_STATUS_SUCCESS) ? 1:0; | |
| 1026 | while(sc->amr_mailbox->mb_poll != 0x77); | |
| 1027 | sc->amr_mailbox->mb_poll = 0; | |
| 1028 | sc->amr_mailbox->mb_ack = 0x77; | |
| 1029 | ||
| 1030 | /* acknowledge that we have the commands */ | |
| 1031 | AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK); | |
| 1032 | while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK); | |
| 1033 | ||
| 7f2216bc | 1034 | crit_exit(); |
| 984263bc MD |
1035 | |
| 1036 | /* unmap the command's data buffer */ | |
| 1037 | amr_unmapcmd(ac); | |
| 1038 | ||
| 1039 | return(error); | |
| 1040 | } | |
| 1041 | ||
| 1042 | /******************************************************************************** | |
| 1043 | * Get a free command slot for a command if it doesn't already have one. | |
| 1044 | * | |
| 1045 | * May be safely called multiple times for a given command. | |
| 1046 | */ | |
| 1047 | static int | |
| 1048 | amr_getslot(struct amr_command *ac) | |
| 1049 | { | |
| 1050 | struct amr_softc *sc = ac->ac_sc; | |
| 7f2216bc | 1051 | int slot, limit, error; |
| 984263bc MD |
1052 | |
| 1053 | debug_called(3); | |
| 1054 | ||
| 1055 | /* if the command already has a slot, don't try to give it another one */ | |
| 1056 | if (ac->ac_slot != 0) | |
| 1057 | return(0); | |
| 1058 | ||
| 1059 | /* enforce slot usage limit */ | |
| 1060 | limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4; | |
| 1061 | if (sc->amr_busyslots > limit) | |
| 1062 | return(EBUSY); | |
| 1063 | ||
| 1064 | /* | |
| 1065 | * Allocate a slot. XXX linear scan is slow | |
| 1066 | */ | |
| 1067 | error = EBUSY; | |
| 7f2216bc | 1068 | crit_enter(); |
| 984263bc MD |
1069 | for (slot = 0; slot < sc->amr_maxio; slot++) { |
| 1070 | if (sc->amr_busycmd[slot] == NULL) { | |
| 1071 | sc->amr_busycmd[slot] = ac; | |
| 1072 | sc->amr_busyslots++; | |
| 1073 | ac->ac_slot = slot; | |
| 1074 | error = 0; | |
| 1075 | break; | |
| 1076 | } | |
| 1077 | } | |
| 7f2216bc | 1078 | crit_exit(); |
| 984263bc MD |
1079 | |
| 1080 | return(error); | |
| 1081 | } | |
| 1082 | ||
| 1083 | /******************************************************************************** | |
| 1084 | * Map/unmap (ac)'s data in the controller's addressable space as required. | |
| 1085 | * | |
| 1086 | * These functions may be safely called multiple times on a given command. | |
| 1087 | */ | |
| 1088 | static void | |
| 1089 | amr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) | |
| 1090 | { | |
| 1091 | struct amr_command *ac = (struct amr_command *)arg; | |
| 1092 | struct amr_softc *sc = ac->ac_sc; | |
| 1093 | struct amr_sgentry *sg; | |
| 1094 | int i; | |
| 1095 | u_int8_t *sgc; | |
| 1096 | ||
| 1097 | debug_called(3); | |
| 1098 | ||
| 1099 | /* get base address of s/g table */ | |
| 1100 | sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG); | |
| 1101 | ||
| 1102 | /* save data physical address */ | |
| 1103 | ac->ac_dataphys = segs[0].ds_addr; | |
| 1104 | ||
| 1105 | /* for AMR_CMD_CONFIG the s/g count goes elsewhere */ | |
| 1106 | if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG) { | |
| 1107 | sgc = &(((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param); | |
| 1108 | } else { | |
| 1109 | sgc = &ac->ac_mailbox.mb_nsgelem; | |
| 1110 | } | |
| 1111 | ||
| 1112 | /* decide whether we need to populate the s/g table */ | |
| 1113 | if (nsegments < 2) { | |
| 1114 | *sgc = 0; | |
| 1115 | ac->ac_mailbox.mb_nsgelem = 0; | |
| 1116 | ac->ac_mailbox.mb_physaddr = ac->ac_dataphys; | |
| 1117 | } else { | |
| 1118 | ac->ac_mailbox.mb_nsgelem = nsegments; | |
| 1119 | *sgc = nsegments; | |
| 1120 | ac->ac_mailbox.mb_physaddr = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry)); | |
| 1121 | for (i = 0; i < nsegments; i++, sg++) { | |
| 1122 | sg->sg_addr = segs[i].ds_addr; | |
| 1123 | sg->sg_count = segs[i].ds_len; | |
| 1124 | } | |
| 1125 | } | |
| 1126 | } | |
| 1127 | ||
| 1128 | static void | |
| 1129 | amr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) | |
| 1130 | { | |
| 1131 | struct amr_command *ac = (struct amr_command *)arg; | |
| 1132 | struct amr_softc *sc = ac->ac_sc; | |
| 1133 | struct amr_sgentry *sg; | |
| 1134 | struct amr_passthrough *ap = (struct amr_passthrough *)ac->ac_data; | |
| 1135 | struct amr_ext_passthrough *aep = (struct amr_ext_passthrough *)ac->ac_data; | |
| 1136 | int i; | |
| 1137 | ||
| 1138 | /* get base address of s/g table */ | |
| 1139 | sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG); | |
| 1140 | ||
| 1141 | /* decide whether we need to populate the s/g table */ | |
| 1142 | if( ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS ) { | |
| 1143 | if (nsegments < 2) { | |
| 1144 | aep->ap_no_sg_elements = 0; | |
| 1145 | aep->ap_data_transfer_address = segs[0].ds_addr; | |
| 1146 | } else { | |
| 1147 | /* save s/g table information in passthrough */ | |
| 1148 | aep->ap_no_sg_elements = nsegments; | |
| 1149 | aep->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry)); | |
| 1150 | /* populate s/g table (overwrites previous call which mapped the passthrough) */ | |
| 1151 | for (i = 0; i < nsegments; i++, sg++) { | |
| 1152 | sg->sg_addr = segs[i].ds_addr; | |
| 1153 | sg->sg_count = segs[i].ds_len; | |
| 1154 | debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count); | |
| 1155 | } | |
| 1156 | } | |
| 1157 | debug(3, "slot %d %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot, | |
| 1158 | aep->ap_no_sg_elements, aep->ap_data_transfer_address, ac->ac_dataphys); | |
| 1159 | } else { | |
| 1160 | if (nsegments < 2) { | |
| 1161 | ap->ap_no_sg_elements = 0; | |
| 1162 | ap->ap_data_transfer_address = segs[0].ds_addr; | |
| 1163 | } else { | |
| 1164 | /* save s/g table information in passthrough */ | |
| 1165 | ap->ap_no_sg_elements = nsegments; | |
| 1166 | ap->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry)); | |
| 1167 | /* populate s/g table (overwrites previous call which mapped the passthrough) */ | |
| 1168 | for (i = 0; i < nsegments; i++, sg++) { | |
| 1169 | sg->sg_addr = segs[i].ds_addr; | |
| 1170 | sg->sg_count = segs[i].ds_len; | |
| 1171 | debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count); | |
| 1172 | } | |
| 1173 | } | |
| 1174 | debug(3, "slot %d %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot, | |
| 1175 | ap->ap_no_sg_elements, ap->ap_data_transfer_address, ac->ac_dataphys); | |
| 1176 | } | |
| 1177 | } | |
| 1178 | ||
| 1179 | static void | |
| 1180 | amr_mapcmd(struct amr_command *ac) | |
| 1181 | { | |
| 1182 | struct amr_softc *sc = ac->ac_sc; | |
| 1183 | ||
| 1184 | debug_called(3); | |
| 1185 | ||
| 1186 | /* if the command involves data at all, and hasn't been mapped */ | |
| 1187 | if (!(ac->ac_flags & AMR_CMD_MAPPED)) { | |
| 1188 | ||
| 1189 | if (ac->ac_data != NULL) { | |
| 1190 | /* map the data buffers into bus space and build the s/g list */ | |
| 1191 | bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, ac->ac_length, | |
| 1192 | amr_setup_dmamap, ac, 0); | |
| 1193 | if (ac->ac_flags & AMR_CMD_DATAIN) | |
| 1194 | bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREREAD); | |
| 1195 | if (ac->ac_flags & AMR_CMD_DATAOUT) | |
| 1196 | bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREWRITE); | |
| 1197 | } | |
| 1198 | ||
| 1199 | if (ac->ac_ccb_data != NULL) { | |
| 1200 | bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, ac->ac_ccb_data, ac->ac_ccb_length, | |
| 1201 | amr_setup_ccbmap, ac, 0); | |
| 1202 | if (ac->ac_flags & AMR_CMD_CCB_DATAIN) | |
| 1203 | bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREREAD); | |
| 1204 | if (ac->ac_flags & AMR_CMD_CCB_DATAOUT) | |
| 1205 | bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREWRITE); | |
| 1206 | } | |
| 1207 | ac->ac_flags |= AMR_CMD_MAPPED; | |
| 1208 | } | |
| 1209 | } | |
| 1210 | ||
| 1211 | static void | |
| 1212 | amr_unmapcmd(struct amr_command *ac) | |
| 1213 | { | |
| 1214 | struct amr_softc *sc = ac->ac_sc; | |
| 1215 | ||
| 1216 | debug_called(3); | |
| 1217 | ||
| 1218 | /* if the command involved data at all and was mapped */ | |
| 1219 | if (ac->ac_flags & AMR_CMD_MAPPED) { | |
| 1220 | ||
| 1221 | if (ac->ac_data != NULL) { | |
| 1222 | if (ac->ac_flags & AMR_CMD_DATAIN) | |
| 1223 | bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTREAD); | |
| 1224 | if (ac->ac_flags & AMR_CMD_DATAOUT) | |
| 1225 | bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTWRITE); | |
| 1226 | bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap); | |
| 1227 | } | |
| 1228 | ||
| 1229 | if (ac->ac_ccb_data != NULL) { | |
| 1230 | if (ac->ac_flags & AMR_CMD_CCB_DATAIN) | |
| 1231 | bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTREAD); | |
| 1232 | if (ac->ac_flags & AMR_CMD_CCB_DATAOUT) | |
| 1233 | bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTWRITE); | |
| 1234 | bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_ccb_dmamap); | |
| 1235 | } | |
| 1236 | ac->ac_flags &= ~AMR_CMD_MAPPED; | |
| 1237 | } | |
| 1238 | } | |
| 1239 | ||
| 1240 | /******************************************************************************** | |
| 1241 | * Take a command and give it to the controller, returns 0 if successful, or | |
| 1242 | * EBUSY if the command should be retried later. | |
| 1243 | */ | |
| 1244 | static int | |
| 1245 | amr_start(struct amr_command *ac) | |
| 1246 | { | |
| 1247 | struct amr_softc *sc = ac->ac_sc; | |
| 7f2216bc | 1248 | int done, i; |
| 984263bc MD |
1249 | |
| 1250 | debug_called(3); | |
| 1251 | ||
| 1252 | /* mark command as busy so that polling consumer can tell */ | |
| 1253 | ac->ac_flags |= AMR_CMD_BUSY; | |
| 1254 | ||
| 1255 | /* get a command slot (freed in amr_done) */ | |
| 1256 | if (amr_getslot(ac)) | |
| 1257 | return(EBUSY); | |
| 1258 | ||
| 1259 | /* now we have a slot, we can map the command (unmapped in amr_complete) */ | |
| 1260 | amr_mapcmd(ac); | |
| 1261 | ||
| 1262 | /* mark the new mailbox we are going to copy in as busy */ | |
| 1263 | ac->ac_mailbox.mb_busy = 1; | |
| 1264 | ||
| 1265 | /* clear the poll/ack fields in the mailbox */ | |
| 1266 | sc->amr_mailbox->mb_poll = 0; | |
| 1267 | sc->amr_mailbox->mb_ack = 0; | |
| 1268 | ||
| 1269 | /* | |
| 1270 | * Save the slot number so that we can locate this command when complete. | |
| 1271 | * Note that ident = 0 seems to be special, so we don't use it. | |
| 1272 | */ | |
| 1273 | ac->ac_mailbox.mb_ident = ac->ac_slot + 1; | |
| 1274 | ||
| 1275 | /* | |
| 1276 | * Spin waiting for the mailbox, give up after ~1 second. We expect the | |
| 1277 | * controller to be able to handle our I/O. | |
| 1278 | * | |
| 1279 | * XXX perhaps we should wait for less time, and count on the deferred command | |
| 1280 | * handling to deal with retries? | |
| 1281 | */ | |
| 1282 | debug(4, "wait for mailbox"); | |
| 1283 | for (i = 10000, done = 0; (i > 0) && !done; i--) { | |
| 7f2216bc | 1284 | crit_enter(); |
| 984263bc MD |
1285 | |
| 1286 | /* is the mailbox free? */ | |
| 1287 | if (sc->amr_mailbox->mb_busy == 0) { | |
| 1288 | debug(4, "got mailbox"); | |
| 1289 | sc->amr_mailbox64->mb64_segment = 0; | |
| 1290 | bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE); | |
| 1291 | done = 1; | |
| 1292 | ||
| 1293 | /* not free, spin waiting */ | |
| 1294 | } else { | |
| 1295 | debug(4, "busy flag %x\n", sc->amr_mailbox->mb_busy); | |
| 1296 | /* this is somewhat ugly */ | |
| 1297 | DELAY(100); | |
| 1298 | } | |
| 7f2216bc | 1299 | crit_exit(); |
| 984263bc MD |
1300 | } |
| 1301 | ||
| 1302 | /* | |
| 1303 | * Now give the command to the controller | |
| 1304 | */ | |
| 1305 | if (done) { | |
| 1306 | if (sc->amr_submit_command(sc)) { | |
| 1307 | /* the controller wasn't ready to take the command, forget that we tried to post it */ | |
| 1308 | sc->amr_mailbox->mb_busy = 0; | |
| 1309 | return(EBUSY); | |
| 1310 | } | |
| 1311 | debug(3, "posted command"); | |
| 1312 | return(0); | |
| 1313 | } | |
| 1314 | ||
| 1315 | /* | |
| 1316 | * The controller wouldn't take the command. Return the command as busy | |
| 1317 | * so that it is retried later. | |
| 1318 | */ | |
| 1319 | return(EBUSY); | |
| 1320 | } | |
| 1321 | ||
| 1322 | /******************************************************************************** | |
| 1323 | * Extract one or more completed commands from the controller (sc) | |
| 1324 | * | |
| 1325 | * Returns nonzero if any commands on the work queue were marked as completed. | |
| 1326 | */ | |
| 1327 | int | |
| 1328 | amr_done(struct amr_softc *sc) | |
| 1329 | { | |
| 1330 | struct amr_command *ac; | |
| 1331 | struct amr_mailbox mbox; | |
| 1332 | int i, idx, result; | |
| 1333 | ||
| 1334 | debug_called(3); | |
| 1335 | ||
| 1336 | /* See if there's anything for us to do */ | |
| 1337 | result = 0; | |
| 1338 | ||
| 1339 | /* loop collecting completed commands */ | |
| 1340 | for (;;) { | |
| 1341 | /* poll for a completed command's identifier and status */ | |
| 1342 | if (sc->amr_get_work(sc, &mbox)) { | |
| 1343 | result = 1; | |
| 1344 | ||
| 1345 | /* iterate over completed commands in this result */ | |
| 1346 | for (i = 0; i < mbox.mb_nstatus; i++) { | |
| 1347 | /* get pointer to busy command */ | |
| 1348 | idx = mbox.mb_completed[i] - 1; | |
| 1349 | ac = sc->amr_busycmd[idx]; | |
| 1350 | ||
| 1351 | /* really a busy command? */ | |
| 1352 | if (ac != NULL) { | |
| 1353 | ||
| 1354 | /* pull the command from the busy index */ | |
| 1355 | sc->amr_busycmd[idx] = NULL; | |
| 1356 | sc->amr_busyslots--; | |
| 1357 | ||
| 1358 | /* save status for later use */ | |
| 1359 | ac->ac_status = mbox.mb_status; | |
| 1360 | amr_enqueue_completed(ac); | |
| 1361 | debug(3, "completed command with status %x", mbox.mb_status); | |
| 1362 | } else { | |
| 1363 | device_printf(sc->amr_dev, "bad slot %d completed\n", idx); | |
| 1364 | } | |
| 1365 | } | |
| 1366 | } else { | |
| 1367 | break; /* no work */ | |
| 1368 | } | |
| 1369 | } | |
| 1370 | ||
| 1371 | /* if we've completed any commands, try posting some more */ | |
| 1372 | if (result) | |
| 1373 | amr_startio(sc); | |
| 1374 | ||
| 1375 | /* handle completion and timeouts */ | |
| 42cdd4ab | 1376 | #if defined(__FreeBSD__) && __FreeBSD_version >= 500005 |
| 984263bc MD |
1377 | if (sc->amr_state & AMR_STATE_INTEN) |
| 1378 | taskqueue_enqueue(taskqueue_swi, &sc->amr_task_complete); | |
| 1379 | else | |
| 1380 | #endif | |
| 1381 | amr_complete(sc, 0); | |
| 1382 | ||
| 1383 | return(result); | |
| 1384 | } | |
| 1385 | ||
| 1386 | /******************************************************************************** | |
| 1387 | * Do completion processing on done commands on (sc) | |
| 1388 | */ | |
| 1389 | static void | |
| 1390 | amr_complete(void *context, int pending) | |
| 1391 | { | |
| 1392 | struct amr_softc *sc = (struct amr_softc *)context; | |
| 1393 | struct amr_command *ac; | |
| 1394 | ||
| 1395 | debug_called(3); | |
| 1396 | ||
| 1397 | /* pull completed commands off the queue */ | |
| 1398 | for (;;) { | |
| 1399 | ac = amr_dequeue_completed(sc); | |
| 1400 | if (ac == NULL) | |
| 1401 | break; | |
| 1402 | ||
| 1403 | /* unmap the command's data buffer */ | |
| 1404 | amr_unmapcmd(ac); | |
| 1405 | ||
| 1406 | /* unbusy the command */ | |
| 1407 | ac->ac_flags &= ~AMR_CMD_BUSY; | |
| 1408 | ||
| 1409 | /* | |
| 1410 | * Is there a completion handler? | |
| 1411 | */ | |
| 1412 | if (ac->ac_complete != NULL) { | |
| 1413 | ac->ac_complete(ac); | |
| 1414 | ||
| 1415 | /* | |
| 1416 | * Is someone sleeping on this one? | |
| 1417 | */ | |
| 1418 | } else if (ac->ac_flags & AMR_CMD_SLEEP) { | |
| 1419 | wakeup(ac); | |
| 1420 | } | |
| 1421 | ||
| 1422 | if(!sc->amr_busyslots) { | |
| 1423 | wakeup(sc); | |
| 1424 | } | |
| 1425 | } | |
| 1426 | } | |
| 1427 | ||
| 1428 | /******************************************************************************** | |
| 1429 | ******************************************************************************** | |
| 1430 | Command Buffer Management | |
| 1431 | ******************************************************************************** | |
| 1432 | ********************************************************************************/ | |
| 1433 | ||
| 1434 | /******************************************************************************** | |
| 1435 | * Get a new command buffer. | |
| 1436 | * | |
| 1437 | * This may return NULL in low-memory cases. | |
| 1438 | * | |
| 1439 | * If possible, we recycle a command buffer that's been used before. | |
| 1440 | */ | |
| 1441 | struct amr_command * | |
| 1442 | amr_alloccmd(struct amr_softc *sc) | |
| 1443 | { | |
| 1444 | struct amr_command *ac; | |
| 1445 | ||
| 1446 | debug_called(3); | |
| 1447 | ||
| 1448 | ac = amr_dequeue_free(sc); | |
| 1449 | if (ac == NULL) { | |
| 1450 | amr_alloccmd_cluster(sc); | |
| 1451 | ac = amr_dequeue_free(sc); | |
| 1452 | } | |
| 1453 | if (ac == NULL) | |
| 1454 | return(NULL); | |
| 1455 | ||
| 1456 | /* clear out significant fields */ | |
| 1457 | ac->ac_slot = 0; | |
| 1458 | ac->ac_status = 0; | |
| 1459 | bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox)); | |
| 1460 | ac->ac_flags = 0; | |
| 1461 | ac->ac_bio = NULL; | |
| 1462 | ac->ac_data = NULL; | |
| 1463 | ac->ac_ccb_data = NULL; | |
| 1464 | ac->ac_complete = NULL; | |
| 1465 | return(ac); | |
| 1466 | } | |
| 1467 | ||
| 1468 | /******************************************************************************** | |
| 1469 | * Release a command buffer for recycling. | |
| 1470 | */ | |
| 1471 | void | |
| 1472 | amr_releasecmd(struct amr_command *ac) | |
| 1473 | { | |
| 1474 | debug_called(3); | |
| 1475 | ||
| 1476 | amr_enqueue_free(ac); | |
| 1477 | } | |
| 1478 | ||
| 1479 | /******************************************************************************** | |
| 1480 | * Allocate a new command cluster and initialise it. | |
| 1481 | */ | |
| 1482 | static void | |
| 1483 | amr_alloccmd_cluster(struct amr_softc *sc) | |
| 1484 | { | |
| 1485 | struct amr_command_cluster *acc; | |
| 1486 | struct amr_command *ac; | |
| 7f2216bc | 1487 | int i; |
| 984263bc | 1488 | |
| efda3bd0 | 1489 | acc = kmalloc(AMR_CMD_CLUSTERSIZE, M_DEVBUF, M_INTWAIT); |
| 7f2216bc | 1490 | crit_enter(); |
| 076ae0ab | 1491 | TAILQ_INSERT_TAIL(&sc->amr_cmd_clusters, acc, acc_link); |
| 7f2216bc | 1492 | crit_exit(); |
| 076ae0ab MD |
1493 | for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) { |
| 1494 | ac = &acc->acc_command[i]; | |
| 1495 | bzero(ac, sizeof(*ac)); | |
| 1496 | ac->ac_sc = sc; | |
| 1497 | if (!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap) && | |
| 1498 | !bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_ccb_dmamap)) | |
| 1499 | amr_releasecmd(ac); | |
| 984263bc MD |
1500 | } |
| 1501 | } | |
| 1502 | ||
| 1503 | /******************************************************************************** | |
| 1504 | * Free a command cluster | |
| 1505 | */ | |
| 1506 | static void | |
| 1507 | amr_freecmd_cluster(struct amr_command_cluster *acc) | |
| 1508 | { | |
| 1509 | struct amr_softc *sc = acc->acc_command[0].ac_sc; | |
| 1510 | int i; | |
| 1511 | ||
| 1512 | for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) | |
| 1513 | bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_dmamap); | |
| efda3bd0 | 1514 | kfree(acc, M_DEVBUF); |
| 984263bc MD |
1515 | } |
| 1516 | ||
| 1517 | /******************************************************************************** | |
| 1518 | ******************************************************************************** | |
| 1519 | Interface-specific Shims | |
| 1520 | ******************************************************************************** | |
| 1521 | ********************************************************************************/ | |
| 1522 | ||
| 1523 | /******************************************************************************** | |
| 1524 | * Tell the controller that the mailbox contains a valid command | |
| 1525 | */ | |
| 1526 | static int | |
| 1527 | amr_quartz_submit_command(struct amr_softc *sc) | |
| 1528 | { | |
| 1529 | debug_called(3); | |
| 1530 | ||
| 1531 | if (AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT) | |
| 1532 | return(EBUSY); | |
| 1533 | AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT); | |
| 1534 | return(0); | |
| 1535 | } | |
| 1536 | ||
| 1537 | static int | |
| 1538 | amr_std_submit_command(struct amr_softc *sc) | |
| 1539 | { | |
| 1540 | debug_called(3); | |
| 1541 | ||
| 1542 | if (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG) | |
| 1543 | return(EBUSY); | |
| 1544 | AMR_SPOST_COMMAND(sc); | |
| 1545 | return(0); | |
| 1546 | } | |
| 1547 | ||
| 1548 | /******************************************************************************** | |
| 1549 | * Claim any work that the controller has completed; acknowledge completion, | |
| 1550 | * save details of the completion in (mbsave) | |
| 1551 | */ | |
| 1552 | static int | |
| 1553 | amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave) | |
| 1554 | { | |
| 7f2216bc | 1555 | int worked; |
| 984263bc MD |
1556 | u_int32_t outd; |
| 1557 | ||
| 1558 | debug_called(3); | |
| 1559 | ||
| 1560 | worked = 0; | |
| 7f2216bc | 1561 | crit_enter(); |
| 984263bc MD |
1562 | |
| 1563 | /* work waiting for us? */ | |
| 1564 | if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) { | |
| 1565 | ||
| 1566 | /* save mailbox, which contains a list of completed commands */ | |
| 1567 | bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave)); | |
| 1568 | ||
| 1569 | /* acknowledge interrupt */ | |
| 1570 | AMR_QPUT_ODB(sc, AMR_QODB_READY); | |
| 1571 | ||
| 1572 | /* acknowledge that we have the commands */ | |
| 1573 | AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK); | |
| 1574 | ||
| 1575 | #ifndef AMR_QUARTZ_GOFASTER | |
| 1576 | /* | |
| 1577 | * This waits for the controller to notice that we've taken the | |
| 1578 | * command from it. It's very inefficient, and we shouldn't do it, | |
| 1579 | * but if we remove this code, we stop completing commands under | |
| 1580 | * load. | |
| 1581 | * | |
| 1582 | * Peter J says we shouldn't do this. The documentation says we | |
| 1583 | * should. Who is right? | |
| 1584 | */ | |
| 1585 | while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK) | |
| 1586 | ; /* XXX aiee! what if it dies? */ | |
| 1587 | #endif | |
| 1588 | ||
| 1589 | worked = 1; /* got some work */ | |
| 1590 | } | |
| 1591 | ||
| 7f2216bc | 1592 | crit_exit(); |
| 984263bc MD |
1593 | return(worked); |
| 1594 | } | |
| 1595 | ||
| 1596 | static int | |
| 1597 | amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave) | |
| 1598 | { | |
| 7f2216bc | 1599 | int worked; |
| 984263bc MD |
1600 | u_int8_t istat; |
| 1601 | ||
| 1602 | debug_called(3); | |
| 1603 | ||
| 1604 | worked = 0; | |
| 7f2216bc | 1605 | crit_enter(); |
| 984263bc MD |
1606 | |
| 1607 | /* check for valid interrupt status */ | |
| 1608 | istat = AMR_SGET_ISTAT(sc); | |
| 1609 | if ((istat & AMR_SINTR_VALID) != 0) { | |
| 1610 | AMR_SPUT_ISTAT(sc, istat); /* ack interrupt status */ | |
| 1611 | ||
| 1612 | /* save mailbox, which contains a list of completed commands */ | |
| 1613 | bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave)); | |
| 1614 | ||
| 1615 | AMR_SACK_INTERRUPT(sc); /* acknowledge we have the mailbox */ | |
| 1616 | worked = 1; | |
| 1617 | } | |
| 1618 | ||
| 7f2216bc | 1619 | crit_exit(); |
| 984263bc MD |
1620 | return(worked); |
| 1621 | } | |
| 1622 | ||
| 1623 | /******************************************************************************** | |
| 1624 | * Notify the controller of the mailbox location. | |
| 1625 | */ | |
| 1626 | static void | |
| 1627 | amr_std_attach_mailbox(struct amr_softc *sc) | |
| 1628 | { | |
| 1629 | ||
| 1630 | /* program the mailbox physical address */ | |
| 1631 | AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys & 0xff); | |
| 1632 | AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >> 8) & 0xff); | |
| 1633 | AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff); | |
| 1634 | AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff); | |
| 1635 | AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR); | |
| 1636 | ||
| 1637 | /* clear any outstanding interrupt and enable interrupts proper */ | |
| 1638 | AMR_SACK_INTERRUPT(sc); | |
| 1639 | AMR_SENABLE_INTR(sc); | |
| 1640 | } | |
| 1641 | ||
| 1642 | #ifdef AMR_BOARD_INIT | |
| 1643 | /******************************************************************************** | |
| 1644 | * Initialise the controller | |
| 1645 | */ | |
| 1646 | static int | |
| 1647 | amr_quartz_init(struct amr_softc *sc) | |
| 1648 | { | |
| 1649 | int status, ostatus; | |
| 1650 | ||
| 1651 | device_printf(sc->amr_dev, "initial init status %x\n", AMR_QGET_INITSTATUS(sc)); | |
| 1652 | ||
| 1653 | AMR_QRESET(sc); | |
| 1654 | ||
| 1655 | ostatus = 0xff; | |
| 1656 | while ((status = AMR_QGET_INITSTATUS(sc)) != AMR_QINIT_DONE) { | |
| 1657 | if (status != ostatus) { | |
| 1658 | device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_qinit, status)); | |
| 1659 | ostatus = status; | |
| 1660 | } | |
| 1661 | switch (status) { | |
| 1662 | case AMR_QINIT_NOMEM: | |
| 1663 | return(ENOMEM); | |
| 1664 | ||
| 1665 | case AMR_QINIT_SCAN: | |
| 1666 | /* XXX we could print channel/target here */ | |
| 1667 | break; | |
| 1668 | } | |
| 1669 | } | |
| 1670 | return(0); | |
| 1671 | } | |
| 1672 | ||
| 1673 | static int | |
| 1674 | amr_std_init(struct amr_softc *sc) | |
| 1675 | { | |
| 1676 | int status, ostatus; | |
| 1677 | ||
| 1678 | device_printf(sc->amr_dev, "initial init status %x\n", AMR_SGET_INITSTATUS(sc)); | |
| 1679 | ||
| 1680 | AMR_SRESET(sc); | |
| 1681 | ||
| 1682 | ostatus = 0xff; | |
| 1683 | while ((status = AMR_SGET_INITSTATUS(sc)) != AMR_SINIT_DONE) { | |
| 1684 | if (status != ostatus) { | |
| 1685 | device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_sinit, status)); | |
| 1686 | ostatus = status; | |
| 1687 | } | |
| 1688 | switch (status) { | |
| 1689 | case AMR_SINIT_NOMEM: | |
| 1690 | return(ENOMEM); | |
| 1691 | ||
| 1692 | case AMR_SINIT_INPROG: | |
| 1693 | /* XXX we could print channel/target here? */ | |
| 1694 | break; | |
| 1695 | } | |
| 1696 | } | |
| 1697 | return(0); | |
| 1698 | } | |
| 1699 | #endif | |
| 1700 | ||
| 1701 | /******************************************************************************** | |
| 1702 | ******************************************************************************** | |
| 1703 | Debugging | |
| 1704 | ******************************************************************************** | |
| 1705 | ********************************************************************************/ | |
| 1706 | ||
| 1707 | /******************************************************************************** | |
| 1708 | * Identify the controller and print some information about it. | |
| 1709 | */ | |
| 1710 | static void | |
| 1711 | amr_describe_controller(struct amr_softc *sc) | |
| 1712 | { | |
| 1713 | struct amr_prodinfo *ap; | |
| 1714 | struct amr_enquiry *ae; | |
| 1715 | char *prod; | |
| 1716 | ||
| 1717 | /* | |
| 1718 | * Try to get 40LD product info, which tells us what the card is labelled as. | |
| 1719 | */ | |
| 1720 | if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) != NULL) { | |
| 1721 | device_printf(sc->amr_dev, "<LSILogic %.80s> Firmware %.16s, BIOS %.16s, %dMB RAM\n", | |
| 1722 | ap->ap_product, ap->ap_firmware, ap->ap_bios, | |
| 1723 | ap->ap_memsize); | |
| 1724 | ||
| efda3bd0 | 1725 | kfree(ap, M_DEVBUF); |
| 984263bc MD |
1726 | return; |
| 1727 | } | |
| 1728 | ||
| 1729 | /* | |
| 1730 | * Try 8LD extended ENQUIRY to get controller signature, and use lookup table. | |
| 1731 | */ | |
| 1732 | if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) != NULL) { | |
| 1733 | prod = amr_describe_code(amr_table_adaptertype, ae->ae_signature); | |
| 1734 | ||
| 1735 | } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) != NULL) { | |
| 1736 | ||
| 1737 | /* | |
| 1738 | * Try to work it out based on the PCI signatures. | |
| 1739 | */ | |
| 1740 | switch (pci_get_device(sc->amr_dev)) { | |
| 1741 | case 0x9010: | |
| 1742 | prod = "Series 428"; | |
| 1743 | break; | |
| 1744 | case 0x9060: | |
| 1745 | prod = "Series 434"; | |
| 1746 | break; | |
| 1747 | default: | |
| 1748 | prod = "unknown controller"; | |
| 1749 | break; | |
| 1750 | } | |
| 1751 | } else { | |
| f3ac3f0c SW |
1752 | device_printf(sc->amr_dev, "<unsupported controller>\n"); |
| 1753 | return; | |
| 984263bc MD |
1754 | } |
| 1755 | ||
| 1756 | /* | |
| 1757 | * HP NetRaid controllers have a special encoding of the firmware and | |
| 1758 | * BIOS versions. The AMI version seems to have it as strings whereas | |
| 1759 | * the HP version does it with a leading uppercase character and two | |
| 1760 | * binary numbers. | |
| 1761 | */ | |
| 1762 | ||
| 1763 | if(ae->ae_adapter.aa_firmware[2] >= 'A' && | |
| 1764 | ae->ae_adapter.aa_firmware[2] <= 'Z' && | |
| 1765 | ae->ae_adapter.aa_firmware[1] < ' ' && | |
| 1766 | ae->ae_adapter.aa_firmware[0] < ' ' && | |
| 1767 | ae->ae_adapter.aa_bios[2] >= 'A' && | |
| 1768 | ae->ae_adapter.aa_bios[2] <= 'Z' && | |
| 1769 | ae->ae_adapter.aa_bios[1] < ' ' && | |
| 1770 | ae->ae_adapter.aa_bios[0] < ' ') { | |
| 1771 | ||
| 1772 | /* this looks like we have an HP NetRaid version of the MegaRaid */ | |
| 1773 | ||
| 1774 | if(ae->ae_signature == AMR_SIG_438) { | |
| 1775 | /* the AMI 438 is a NetRaid 3si in HP-land */ | |
| 1776 | prod = "HP NetRaid 3si"; | |
| 1777 | } | |
| 1778 | ||
| 1779 | device_printf(sc->amr_dev, "<%s> Firmware %c.%02d.%02d, BIOS %c.%02d.%02d, %dMB RAM\n", | |
| 1780 | prod, ae->ae_adapter.aa_firmware[2], | |
| 1781 | ae->ae_adapter.aa_firmware[1], | |
| 1782 | ae->ae_adapter.aa_firmware[0], | |
| 1783 | ae->ae_adapter.aa_bios[2], | |
| 1784 | ae->ae_adapter.aa_bios[1], | |
| 1785 | ae->ae_adapter.aa_bios[0], | |
| 1786 | ae->ae_adapter.aa_memorysize); | |
| 1787 | } else { | |
| 1788 | device_printf(sc->amr_dev, "<%s> Firmware %.4s, BIOS %.4s, %dMB RAM\n", | |
| 1789 | prod, ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios, | |
| 1790 | ae->ae_adapter.aa_memorysize); | |
| 1791 | } | |
| efda3bd0 | 1792 | kfree(ae, M_DEVBUF); |
| 984263bc MD |
1793 | } |
| 1794 | ||
| 510931cd | 1795 | int |
| e0fc5693 | 1796 | amr_dump_blocks(struct amr_softc *sc, int unit, u_int64_t lba, void *data, int blks) |
| 510931cd HP |
1797 | { |
| 1798 | ||
| 1799 | struct amr_command *ac; | |
| 1800 | int error = 1; | |
| 1801 | ||
| 1802 | debug_called(1); | |
| 1803 | ||
| 1804 | sc->amr_state &= ~AMR_STATE_INTEN; | |
| 1805 | ||
| 1806 | /* get ourselves a command buffer */ | |
| 1807 | if ((ac = amr_alloccmd(sc)) == NULL) | |
| 1808 | goto out; | |
| 1809 | /* set command flags */ | |
| 1810 | ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT; | |
| 1811 | ||
| 1812 | /* point the command at our data */ | |
| 1813 | ac->ac_data = data; | |
| 1814 | ac->ac_length = blks * AMR_BLKSIZE; | |
| 1815 | ||
| 1816 | /* build the command proper */ | |
| 1817 | ac->ac_mailbox.mb_command = AMR_CMD_LWRITE; | |
| 1818 | ac->ac_mailbox.mb_blkcount = blks; | |
| 1819 | ac->ac_mailbox.mb_lba = lba; | |
| 1820 | ac->ac_mailbox.mb_drive = unit; | |
| 1821 | ||
| 1822 | /* can't assume that interrupts are going to work here, so play it safe */ | |
| 1823 | if (sc->amr_poll_command(ac)) | |
| 1824 | goto out; | |
| 1825 | error = ac->ac_status; | |
| 1826 | ||
| 1827 | out: | |
| 1828 | if (ac != NULL) | |
| 1829 | amr_releasecmd(ac); | |
| 1830 | ||
| 1831 | sc->amr_state |= AMR_STATE_INTEN; | |
| 1832 | ||
| 1833 | return (error); | |
| 1834 | } | |
| 1835 | ||
| 1836 | ||
| 984263bc MD |
1837 | #ifdef AMR_DEBUG |
| 1838 | /******************************************************************************** | |
| 1839 | * Print the command (ac) in human-readable format | |
| 1840 | */ | |
| 1841 | #if 0 | |
| 1842 | static void | |
| 1843 | amr_printcommand(struct amr_command *ac) | |
| 1844 | { | |
| 1845 | struct amr_softc *sc = ac->ac_sc; | |
| 1846 | struct amr_sgentry *sg; | |
| 1847 | int i; | |
| 1848 | ||
| 1849 | device_printf(sc->amr_dev, "cmd %x ident %d drive %d\n", | |
| 1850 | ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive); | |
| 1851 | device_printf(sc->amr_dev, "blkcount %d lba %d\n", | |
| 1852 | ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba); | |
| 1853 | device_printf(sc->amr_dev, "virtaddr %p length %lu\n", ac->ac_data, (unsigned long)ac->ac_length); | |
| 1854 | device_printf(sc->amr_dev, "sg physaddr %08x nsg %d\n", | |
| 1855 | ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem); | |
| 1856 | device_printf(sc->amr_dev, "ccb %p bio %p\n", ac->ac_ccb_data, ac->ac_bio); | |
| 1857 | ||
| 1858 | /* get base address of s/g table */ | |
| 1859 | sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG); | |
| 1860 | for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++) | |
| 1861 | device_printf(sc->amr_dev, " %x/%d\n", sg->sg_addr, sg->sg_count); | |
| 1862 | } | |
| 1863 | #endif | |
| 1864 | #endif |