1 1.13 skrll /* $NetBSD: ata_subr.c,v 1.13 2020/12/23 08:17:01 skrll Exp $ */ 2 1.1 jdolecek 3 1.1 jdolecek /* 4 1.1 jdolecek * Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved. 5 1.1 jdolecek * 6 1.1 jdolecek * Redistribution and use in source and binary forms, with or without 7 1.1 jdolecek * modification, are permitted provided that the following conditions 8 1.1 jdolecek * are met: 9 1.1 jdolecek * 1. Redistributions of source code must retain the above copyright 10 1.1 jdolecek * notice, this list of conditions and the following disclaimer. 11 1.1 jdolecek * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 jdolecek * notice, this list of conditions and the following disclaimer in the 13 1.1 jdolecek * documentation and/or other materials provided with the distribution. 14 1.1 jdolecek * 15 1.1 jdolecek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 1.1 jdolecek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 1.1 jdolecek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 1.1 jdolecek * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 1.1 jdolecek * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 1.1 jdolecek * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 1.1 jdolecek * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 1.1 jdolecek * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 1.1 jdolecek * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 1.1 jdolecek * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 1.1 jdolecek */ 26 1.1 jdolecek 27 1.1 jdolecek #include <sys/cdefs.h> 28 1.13 skrll __KERNEL_RCSID(0, "$NetBSD: ata_subr.c,v 1.13 2020/12/23 08:17:01 skrll Exp $"); 29 1.1 jdolecek 30 1.1 jdolecek #include "opt_ata.h" 31 1.1 jdolecek 32 1.1 jdolecek #include <sys/param.h> 33 1.1 jdolecek #include <sys/systm.h> 34 1.1 jdolecek #include <sys/kernel.h> 35 1.1 jdolecek #include <sys/device.h> 36 1.1 jdolecek #include <sys/conf.h> 37 1.1 jdolecek #include <sys/fcntl.h> 38 1.1 jdolecek #include <sys/proc.h> 39 1.1 jdolecek #include <sys/kthread.h> 40 1.1 jdolecek #include <sys/errno.h> 41 1.1 jdolecek #include <sys/ataio.h> 42 1.1 jdolecek #include <sys/kmem.h> 43 1.1 jdolecek #include <sys/intr.h> 44 1.1 jdolecek #include <sys/bus.h> 45 1.1 jdolecek #include <sys/once.h> 46 1.1 jdolecek #include <sys/bitops.h> 47 1.1 jdolecek 48 1.1 jdolecek #define ATABUS_PRIVATE 49 1.1 jdolecek 50 1.1 jdolecek #include <dev/ata/ataconf.h> 51 1.1 jdolecek #include <dev/ata/atareg.h> 52 1.1 jdolecek #include <dev/ata/atavar.h> 53 1.1 jdolecek #include <dev/ic/wdcvar.h> /* for PIOBM */ 54 1.1 jdolecek 55 1.1 jdolecek #define DEBUG_FUNCS 0x08 56 1.1 jdolecek #define DEBUG_PROBE 0x10 57 1.1 jdolecek #define DEBUG_DETACH 0x20 58 1.1 jdolecek #define DEBUG_XFERS 0x40 59 1.1 jdolecek #ifdef ATADEBUG 60 1.1 jdolecek extern int atadebug_mask; 61 1.1 jdolecek #define ATADEBUG_PRINT(args, level) \ 62 1.1 jdolecek if (atadebug_mask & (level)) \ 63 1.1 jdolecek printf args 64 1.1 jdolecek #else 65 1.1 jdolecek #define ATADEBUG_PRINT(args, level) 66 1.1 jdolecek #endif 67 1.1 jdolecek 68 1.7 jdolecek static void 69 1.1 jdolecek ata_queue_reset(struct ata_queue *chq) 70 1.1 jdolecek { 71 1.1 jdolecek /* make sure that we can use polled commands */ 72 1.7 jdolecek SIMPLEQ_INIT(&chq->queue_xfer); 73 1.1 jdolecek TAILQ_INIT(&chq->active_xfers); 74 1.1 jdolecek chq->queue_freeze = 0; 75 1.1 jdolecek chq->queue_active = 0; 76 1.1 jdolecek chq->active_xfers_used = 0; 77 1.1 jdolecek chq->queue_xfers_avail = __BIT(chq->queue_openings) - 1; 78 1.1 jdolecek } 79 1.1 jdolecek 80 1.1 jdolecek struct ata_xfer * 81 1.1 jdolecek ata_queue_hwslot_to_xfer(struct ata_channel *chp, int hwslot) 82 1.1 jdolecek { 83 1.1 jdolecek struct ata_queue *chq = chp->ch_queue; 84 1.1 jdolecek struct ata_xfer *xfer = NULL; 85 1.1 jdolecek 86 1.1 jdolecek ata_channel_lock(chp); 87 1.1 jdolecek 88 1.1 jdolecek KASSERTMSG(hwslot < chq->queue_openings, "hwslot %d > openings %d", 89 1.1 jdolecek hwslot, chq->queue_openings); 90 1.1 jdolecek KASSERTMSG((chq->active_xfers_used & __BIT(hwslot)) != 0, 91 1.1 jdolecek "hwslot %d not active", hwslot); 92 1.1 jdolecek 93 1.1 jdolecek /* Usually the first entry will be the one */ 94 1.1 jdolecek TAILQ_FOREACH(xfer, &chq->active_xfers, c_activechain) { 95 1.1 jdolecek if (xfer->c_slot == hwslot) 96 1.1 jdolecek break; 97 1.1 jdolecek } 98 1.1 jdolecek 99 1.1 jdolecek ata_channel_unlock(chp); 100 1.1 jdolecek 101 1.1 jdolecek KASSERTMSG((xfer != NULL), 102 1.1 jdolecek "%s: xfer with slot %d not found (active %x)", __func__, 103 1.1 jdolecek hwslot, chq->active_xfers_used); 104 1.1 jdolecek 105 1.1 jdolecek return xfer; 106 1.1 jdolecek } 107 1.1 jdolecek 108 1.1 jdolecek struct ata_xfer * 109 1.1 jdolecek ata_queue_get_active_xfer_locked(struct ata_channel *chp) 110 1.1 jdolecek { 111 1.1 jdolecek struct ata_xfer *xfer; 112 1.1 jdolecek 113 1.1 jdolecek KASSERT(mutex_owned(&chp->ch_lock)); 114 1.1 jdolecek xfer = TAILQ_FIRST(&chp->ch_queue->active_xfers); 115 1.1 jdolecek 116 1.1 jdolecek if (xfer && ISSET(xfer->c_flags, C_NCQ)) { 117 1.1 jdolecek /* Spurious call, never return NCQ xfer from this interface */ 118 1.1 jdolecek xfer = NULL; 119 1.1 jdolecek } 120 1.1 jdolecek 121 1.1 jdolecek return xfer; 122 1.1 jdolecek } 123 1.1 jdolecek 124 1.1 jdolecek /* 125 1.1 jdolecek * This interface is supposed only to be used when there is exactly 126 1.1 jdolecek * one outstanding command, when there is no information about the slot, 127 1.1 jdolecek * which triggered the command. ata_queue_hwslot_to_xfer() interface 128 1.1 jdolecek * is preferred in all NCQ cases. 129 1.1 jdolecek */ 130 1.1 jdolecek struct ata_xfer * 131 1.1 jdolecek ata_queue_get_active_xfer(struct ata_channel *chp) 132 1.1 jdolecek { 133 1.1 jdolecek struct ata_xfer *xfer = NULL; 134 1.1 jdolecek 135 1.1 jdolecek ata_channel_lock(chp); 136 1.1 jdolecek xfer = ata_queue_get_active_xfer_locked(chp); 137 1.1 jdolecek ata_channel_unlock(chp); 138 1.1 jdolecek 139 1.1 jdolecek return xfer; 140 1.1 jdolecek } 141 1.1 jdolecek 142 1.1 jdolecek struct ata_xfer * 143 1.1 jdolecek ata_queue_drive_active_xfer(struct ata_channel *chp, int drive) 144 1.1 jdolecek { 145 1.1 jdolecek struct ata_xfer *xfer = NULL; 146 1.1 jdolecek 147 1.1 jdolecek ata_channel_lock(chp); 148 1.1 jdolecek 149 1.1 jdolecek TAILQ_FOREACH(xfer, &chp->ch_queue->active_xfers, c_activechain) { 150 1.1 jdolecek if (xfer->c_drive == drive) 151 1.1 jdolecek break; 152 1.1 jdolecek } 153 1.1 jdolecek KASSERT(xfer != NULL); 154 1.1 jdolecek 155 1.1 jdolecek ata_channel_unlock(chp); 156 1.1 jdolecek 157 1.1 jdolecek return xfer; 158 1.1 jdolecek } 159 1.1 jdolecek 160 1.1 jdolecek struct ata_queue * 161 1.1 jdolecek ata_queue_alloc(uint8_t openings) 162 1.1 jdolecek { 163 1.1 jdolecek if (openings == 0) 164 1.1 jdolecek openings = 1; 165 1.1 jdolecek 166 1.1 jdolecek if (openings > ATA_MAX_OPENINGS) 167 1.1 jdolecek openings = ATA_MAX_OPENINGS; 168 1.1 jdolecek 169 1.7 jdolecek struct ata_queue *chq = kmem_zalloc(sizeof(*chq), KM_SLEEP); 170 1.1 jdolecek 171 1.1 jdolecek chq->queue_openings = openings; 172 1.1 jdolecek ata_queue_reset(chq); 173 1.1 jdolecek 174 1.1 jdolecek cv_init(&chq->queue_drain, "atdrn"); 175 1.1 jdolecek cv_init(&chq->queue_idle, "qidl"); 176 1.1 jdolecek 177 1.7 jdolecek cv_init(&chq->c_active, "ataact"); 178 1.7 jdolecek cv_init(&chq->c_cmd_finish, "atafin"); 179 1.1 jdolecek 180 1.1 jdolecek return chq; 181 1.1 jdolecek } 182 1.1 jdolecek 183 1.1 jdolecek void 184 1.1 jdolecek ata_queue_free(struct ata_queue *chq) 185 1.1 jdolecek { 186 1.1 jdolecek cv_destroy(&chq->queue_drain); 187 1.1 jdolecek cv_destroy(&chq->queue_idle); 188 1.1 jdolecek 189 1.7 jdolecek cv_destroy(&chq->c_active); 190 1.7 jdolecek cv_destroy(&chq->c_cmd_finish); 191 1.7 jdolecek 192 1.7 jdolecek kmem_free(chq, sizeof(*chq)); 193 1.1 jdolecek } 194 1.1 jdolecek 195 1.1 jdolecek void 196 1.1 jdolecek ata_channel_init(struct ata_channel *chp) 197 1.1 jdolecek { 198 1.1 jdolecek mutex_init(&chp->ch_lock, MUTEX_DEFAULT, IPL_BIO); 199 1.11 thorpej cv_init(&chp->ch_thr_idle, "atath"); 200 1.4 jdolecek 201 1.7 jdolecek callout_init(&chp->c_timo_callout, 0); /* XXX MPSAFE */ 202 1.7 jdolecek 203 1.4 jdolecek /* Optionally setup the queue, too */ 204 1.4 jdolecek if (chp->ch_queue == NULL) { 205 1.4 jdolecek chp->ch_queue = ata_queue_alloc(1); 206 1.4 jdolecek } 207 1.1 jdolecek } 208 1.1 jdolecek 209 1.1 jdolecek void 210 1.1 jdolecek ata_channel_destroy(struct ata_channel *chp) 211 1.1 jdolecek { 212 1.4 jdolecek if (chp->ch_queue != NULL) { 213 1.4 jdolecek ata_queue_free(chp->ch_queue); 214 1.4 jdolecek chp->ch_queue = NULL; 215 1.4 jdolecek } 216 1.4 jdolecek 217 1.7 jdolecek mutex_enter(&chp->ch_lock); 218 1.7 jdolecek callout_halt(&chp->c_timo_callout, &chp->ch_lock); 219 1.7 jdolecek callout_destroy(&chp->c_timo_callout); 220 1.7 jdolecek mutex_exit(&chp->ch_lock); 221 1.7 jdolecek 222 1.1 jdolecek mutex_destroy(&chp->ch_lock); 223 1.11 thorpej cv_destroy(&chp->ch_thr_idle); 224 1.1 jdolecek } 225 1.1 jdolecek 226 1.7 jdolecek void 227 1.7 jdolecek ata_timeout(void *v) 228 1.1 jdolecek { 229 1.7 jdolecek struct ata_channel *chp = v; 230 1.1 jdolecek struct ata_queue *chq = chp->ch_queue; 231 1.7 jdolecek struct ata_xfer *xfer, *nxfer; 232 1.7 jdolecek int s; 233 1.1 jdolecek 234 1.7 jdolecek s = splbio(); /* XXX MPSAFE */ 235 1.1 jdolecek 236 1.7 jdolecek callout_ack(&chp->c_timo_callout); 237 1.1 jdolecek 238 1.9 jdolecek if (chp->ch_flags & ATACH_RECOVERING) { 239 1.9 jdolecek /* Do nothing, recovery will requeue the xfers */ 240 1.12 jmcneill goto done; 241 1.9 jdolecek } 242 1.9 jdolecek 243 1.1 jdolecek /* 244 1.7 jdolecek * If there is a timeout, means the last enqueued command 245 1.7 jdolecek * timed out, and thus all commands timed out. 246 1.7 jdolecek * XXX locking 247 1.1 jdolecek */ 248 1.7 jdolecek TAILQ_FOREACH_SAFE(xfer, &chq->active_xfers, c_activechain, nxfer) { 249 1.7 jdolecek ATADEBUG_PRINT(("%s: slot %d\n", __func__, xfer->c_slot), 250 1.7 jdolecek DEBUG_FUNCS|DEBUG_XFERS); 251 1.7 jdolecek 252 1.7 jdolecek if (ata_timo_xfer_check(xfer)) { 253 1.7 jdolecek /* Already logged */ 254 1.7 jdolecek continue; 255 1.1 jdolecek } 256 1.1 jdolecek 257 1.7 jdolecek /* Mark as timed out. Do not print anything, wd(4) will. */ 258 1.7 jdolecek xfer->c_flags |= C_TIMEOU; 259 1.7 jdolecek xfer->ops->c_intr(xfer->c_chp, xfer, 0); 260 1.1 jdolecek } 261 1.1 jdolecek 262 1.12 jmcneill done: 263 1.7 jdolecek splx(s); 264 1.7 jdolecek } 265 1.1 jdolecek 266 1.7 jdolecek void 267 1.7 jdolecek ata_channel_lock(struct ata_channel *chp) 268 1.7 jdolecek { 269 1.7 jdolecek mutex_enter(&chp->ch_lock); 270 1.7 jdolecek } 271 1.1 jdolecek 272 1.7 jdolecek void 273 1.7 jdolecek ata_channel_unlock(struct ata_channel *chp) 274 1.7 jdolecek { 275 1.7 jdolecek mutex_exit(&chp->ch_lock); 276 1.7 jdolecek } 277 1.1 jdolecek 278 1.7 jdolecek void 279 1.7 jdolecek ata_channel_lock_owned(struct ata_channel *chp) 280 1.7 jdolecek { 281 1.7 jdolecek KASSERT(mutex_owned(&chp->ch_lock)); 282 1.1 jdolecek } 283 1.1 jdolecek 284 1.7 jdolecek #ifdef ATADEBUG 285 1.1 jdolecek void 286 1.7 jdolecek atachannel_debug(struct ata_channel *chp) 287 1.1 jdolecek { 288 1.1 jdolecek struct ata_queue *chq = chp->ch_queue; 289 1.1 jdolecek 290 1.7 jdolecek printf(" ch %s flags 0x%x ndrives %d\n", 291 1.7 jdolecek device_xname(chp->atabus), chp->ch_flags, chp->ch_ndrives); 292 1.7 jdolecek printf(" que: flags 0x%x avail 0x%x used 0x%x\n", 293 1.7 jdolecek chq->queue_flags, chq->queue_xfers_avail, chq->active_xfers_used); 294 1.7 jdolecek printf(" act %d freez %d open %u\n", 295 1.7 jdolecek chq->queue_active, chq->queue_freeze, chq->queue_openings); 296 1.7 jdolecek } 297 1.7 jdolecek #endif /* ATADEBUG */ 298 1.1 jdolecek 299 1.7 jdolecek bool 300 1.7 jdolecek ata_queue_alloc_slot(struct ata_channel *chp, uint8_t *c_slot, 301 1.7 jdolecek uint8_t drv_openings) 302 1.7 jdolecek { 303 1.7 jdolecek struct ata_queue *chq = chp->ch_queue; 304 1.7 jdolecek uint32_t avail, mask; 305 1.1 jdolecek 306 1.7 jdolecek KASSERT(mutex_owned(&chp->ch_lock)); 307 1.7 jdolecek KASSERT(chq->queue_active < chq->queue_openings); 308 1.1 jdolecek 309 1.13 skrll ATADEBUG_PRINT(("%s: channel %d qavail 0x%x qact %d\n", 310 1.7 jdolecek __func__, chp->ch_channel, 311 1.7 jdolecek chq->queue_xfers_avail, chq->queue_active), 312 1.7 jdolecek DEBUG_XFERS); 313 1.1 jdolecek 314 1.7 jdolecek mask = __BIT(MIN(chq->queue_openings, drv_openings)) - 1; 315 1.3 jdolecek 316 1.7 jdolecek avail = ffs32(chq->queue_xfers_avail & mask); 317 1.7 jdolecek if (avail == 0) 318 1.7 jdolecek return false; 319 1.1 jdolecek 320 1.7 jdolecek KASSERT(avail > 0); 321 1.7 jdolecek KASSERT(avail <= drv_openings); 322 1.1 jdolecek 323 1.7 jdolecek *c_slot = avail - 1; 324 1.7 jdolecek chq->queue_xfers_avail &= ~__BIT(*c_slot); 325 1.1 jdolecek 326 1.7 jdolecek KASSERT((chq->active_xfers_used & __BIT(*c_slot)) == 0); 327 1.7 jdolecek return true; 328 1.1 jdolecek } 329 1.1 jdolecek 330 1.1 jdolecek void 331 1.7 jdolecek ata_queue_free_slot(struct ata_channel *chp, uint8_t c_slot) 332 1.1 jdolecek { 333 1.7 jdolecek struct ata_queue *chq = chp->ch_queue; 334 1.7 jdolecek 335 1.7 jdolecek KASSERT(mutex_owned(&chp->ch_lock)); 336 1.7 jdolecek 337 1.7 jdolecek KASSERT((chq->active_xfers_used & __BIT(c_slot)) == 0); 338 1.7 jdolecek KASSERT((chq->queue_xfers_avail & __BIT(c_slot)) == 0); 339 1.7 jdolecek 340 1.7 jdolecek chq->queue_xfers_avail |= __BIT(c_slot); 341 1.1 jdolecek } 342 1.1 jdolecek 343 1.1 jdolecek void 344 1.7 jdolecek ata_queue_hold(struct ata_channel *chp) 345 1.1 jdolecek { 346 1.7 jdolecek struct ata_queue *chq = chp->ch_queue; 347 1.7 jdolecek 348 1.7 jdolecek KASSERT(mutex_owned(&chp->ch_lock)); 349 1.7 jdolecek 350 1.7 jdolecek chq->queue_hold |= chq->active_xfers_used; 351 1.7 jdolecek chq->active_xfers_used = 0; 352 1.1 jdolecek } 353 1.1 jdolecek 354 1.1 jdolecek void 355 1.7 jdolecek ata_queue_unhold(struct ata_channel *chp) 356 1.1 jdolecek { 357 1.7 jdolecek struct ata_queue *chq = chp->ch_queue; 358 1.7 jdolecek 359 1.1 jdolecek KASSERT(mutex_owned(&chp->ch_lock)); 360 1.7 jdolecek 361 1.7 jdolecek chq->active_xfers_used |= chq->queue_hold; 362 1.7 jdolecek chq->queue_hold = 0; 363 1.1 jdolecek } 364 1.5 jdolecek 365 1.7 jdolecek /* 366 1.7 jdolecek * Must be called with interrupts blocked. 367 1.7 jdolecek */ 368 1.7 jdolecek uint32_t 369 1.7 jdolecek ata_queue_active(struct ata_channel *chp) 370 1.5 jdolecek { 371 1.5 jdolecek struct ata_queue *chq = chp->ch_queue; 372 1.5 jdolecek 373 1.8 jdolecek if (chp->ch_flags & ATACH_DETACHED) 374 1.8 jdolecek return 0; 375 1.8 jdolecek 376 1.7 jdolecek return chq->active_xfers_used; 377 1.7 jdolecek } 378 1.5 jdolecek 379 1.7 jdolecek uint8_t 380 1.7 jdolecek ata_queue_openings(struct ata_channel *chp) 381 1.7 jdolecek { 382 1.7 jdolecek return chp->ch_queue->queue_openings; 383 1.5 jdolecek } 384