148156Sjlemon/*- 257828Sjlemon * Copyright (c) 1999,2000 Jonathan Lemon 348156Sjlemon * All rights reserved. 448156Sjlemon * 548156Sjlemon # Derived from the original IDA Compaq RAID driver, which is 648156Sjlemon * Copyright (c) 1996, 1997, 1998, 1999 748156Sjlemon * Mark Dawson and David James. All rights reserved. 848156Sjlemon * 948156Sjlemon * Redistribution and use in source and binary forms, with or without 1048156Sjlemon * modification, are permitted provided that the following conditions 1148156Sjlemon * are met: 1248156Sjlemon * 1. Redistributions of source code must retain the above copyright 1348156Sjlemon * notice, this list of conditions and the following disclaimer. 1448156Sjlemon * 2. Redistributions in binary form must reproduce the above copyright 1548156Sjlemon * notice, this list of conditions and the following disclaimer in the 1648156Sjlemon * documentation and/or other materials provided with the distribution. 1748156Sjlemon * 1848156Sjlemon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1948156Sjlemon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2048156Sjlemon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2148156Sjlemon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2248156Sjlemon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2348156Sjlemon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2448156Sjlemon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2548156Sjlemon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2648156Sjlemon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2748156Sjlemon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2848156Sjlemon * SUCH DAMAGE. 2948156Sjlemon */ 3048156Sjlemon 31119418Sobrien#include <sys/cdefs.h> 32119418Sobrien__FBSDID("$FreeBSD$"); 33119418Sobrien 3448156Sjlemon/* 3548156Sjlemon * Generic driver for Compaq SMART RAID adapters. 3648156Sjlemon */ 3748156Sjlemon 3848156Sjlemon#include <sys/param.h> 3973113Sjlemon#include <sys/kernel.h> 4048156Sjlemon#include <sys/systm.h> 41239740Sjhb#include <sys/lock.h> 4248156Sjlemon#include <sys/malloc.h> 43239740Sjhb#include <sys/mutex.h> 44124540Smdodd#include <sys/stat.h> 4548156Sjlemon 4660041Sphk#include <sys/bio.h> 4748156Sjlemon#include <sys/bus.h> 48111337Sphk#include <sys/conf.h> 49124538Smdodd#include <sys/endian.h> 5048156Sjlemon 5148156Sjlemon#include <machine/bus.h> 5248156Sjlemon#include <sys/rman.h> 5348156Sjlemon 54112946Sphk#include <geom/geom_disk.h> 55112946Sphk 5648156Sjlemon#include <dev/ida/idareg.h> 5748156Sjlemon#include <dev/ida/idavar.h> 58124540Smdodd#include <dev/ida/idaio.h> 5948156Sjlemon 6048156Sjlemon/* prototypes */ 61239740Sjhbstatic int ida_alloc_qcbs(struct ida_softc *ida); 62239740Sjhbstatic void ida_done(struct ida_softc *ida, struct ida_qcb *qcb); 6348156Sjlemonstatic void ida_start(struct ida_softc *ida); 64239740Sjhbstatic void ida_startio(struct ida_softc *ida); 65239740Sjhbstatic void ida_startup(void *arg); 66239740Sjhbstatic void ida_timeout(void *arg); 6773113Sjlemonstatic int ida_wait(struct ida_softc *ida, struct ida_qcb *qcb); 6848156Sjlemon 69124540Smdoddstatic d_ioctl_t ida_ioctl; 70124540Smdoddstatic struct cdevsw ida_cdevsw = { 71126080Sphk .d_version = D_VERSION, 72124540Smdodd .d_ioctl = ida_ioctl, 73124540Smdodd .d_name = "ida", 74124540Smdodd}; 75124540Smdodd 7648156Sjlemonvoid 7748156Sjlemonida_free(struct ida_softc *ida) 7848156Sjlemon{ 7957828Sjlemon int i; 8048156Sjlemon 81239740Sjhb if (ida->ih != NULL) 82239740Sjhb bus_teardown_intr(ida->dev, ida->irq, ida->ih); 83239740Sjhb 84239740Sjhb mtx_lock(&ida->lock); 85138853Smdodd callout_stop(&ida->ch); 86239740Sjhb mtx_unlock(&ida->lock); 87239740Sjhb callout_drain(&ida->ch); 88138853Smdodd 89145024Smdodd if (ida->buffer_dmat) { 90239740Sjhb for (i = 0; i < IDA_QCB_MAX; i++) 91145024Smdodd bus_dmamap_destroy(ida->buffer_dmat, ida->qcbs[i].dmamap); 9248156Sjlemon bus_dma_tag_destroy(ida->buffer_dmat); 93145024Smdodd } 9448156Sjlemon 95145024Smdodd if (ida->hwqcb_dmat) { 96145024Smdodd if (ida->hwqcb_busaddr) 97145024Smdodd bus_dmamap_unload(ida->hwqcb_dmat, ida->hwqcb_dmamap); 98145024Smdodd if (ida->hwqcbs) 99145024Smdodd bus_dmamem_free(ida->hwqcb_dmat, ida->hwqcbs, 100145024Smdodd ida->hwqcb_dmamap); 10148156Sjlemon bus_dma_tag_destroy(ida->hwqcb_dmat); 102145024Smdodd } 10348156Sjlemon 10448156Sjlemon if (ida->qcbs != NULL) 10548156Sjlemon free(ida->qcbs, M_DEVBUF); 10648156Sjlemon 10748156Sjlemon if (ida->irq != NULL) 10848156Sjlemon bus_release_resource(ida->dev, ida->irq_res_type, 10948156Sjlemon 0, ida->irq); 11048156Sjlemon 11148156Sjlemon if (ida->parent_dmat != NULL) 11248156Sjlemon bus_dma_tag_destroy(ida->parent_dmat); 11348156Sjlemon 11448156Sjlemon if (ida->regs != NULL) 11548156Sjlemon bus_release_resource(ida->dev, ida->regs_res_type, 11648156Sjlemon ida->regs_res_id, ida->regs); 117239740Sjhb 118239740Sjhb mtx_destroy(&ida->lock); 11948156Sjlemon} 12048156Sjlemon 12148156Sjlemon/* 12248156Sjlemon * record bus address from bus_dmamap_load 12348156Sjlemon */ 12448156Sjlemonstatic void 125144991Smdoddida_dma_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 12648156Sjlemon{ 127144991Smdodd bus_addr_t *baddr; 12848156Sjlemon 129144991Smdodd baddr = (bus_addr_t *)arg; 130144991Smdodd *baddr = segs->ds_addr; 13148156Sjlemon} 13248156Sjlemon 13348156Sjlemonstatic __inline struct ida_qcb * 13448156Sjlemonida_get_qcb(struct ida_softc *ida) 13548156Sjlemon{ 13648156Sjlemon struct ida_qcb *qcb; 13748156Sjlemon 13848156Sjlemon if ((qcb = SLIST_FIRST(&ida->free_qcbs)) != NULL) { 13948156Sjlemon SLIST_REMOVE_HEAD(&ida->free_qcbs, link.sle); 140239740Sjhb bzero(qcb->hwqcb, sizeof(struct ida_hdr) + sizeof(struct ida_req)); 14148156Sjlemon } 14248156Sjlemon return (qcb); 14348156Sjlemon} 14448156Sjlemon 145239740Sjhbstatic __inline void 146239740Sjhbida_free_qcb(struct ida_softc *ida, struct ida_qcb *qcb) 147239740Sjhb{ 148239740Sjhb 149239740Sjhb qcb->state = QCB_FREE; 150239740Sjhb qcb->buf = NULL; 151239740Sjhb qcb->error = 0; 152239740Sjhb SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle); 153239740Sjhb} 154239740Sjhb 15557828Sjlemonstatic __inline bus_addr_t 15657828Sjlemonidahwqcbvtop(struct ida_softc *ida, struct ida_hardware_qcb *hwqcb) 15757828Sjlemon{ 15857828Sjlemon return (ida->hwqcb_busaddr + 15957828Sjlemon ((bus_addr_t)hwqcb - (bus_addr_t)ida->hwqcbs)); 16057828Sjlemon} 16157828Sjlemon 16257828Sjlemonstatic __inline struct ida_qcb * 16357828Sjlemonidahwqcbptov(struct ida_softc *ida, bus_addr_t hwqcb_addr) 16457828Sjlemon{ 16557828Sjlemon struct ida_hardware_qcb *hwqcb; 16657828Sjlemon 16757828Sjlemon hwqcb = (struct ida_hardware_qcb *) 16857828Sjlemon ((bus_addr_t)ida->hwqcbs + (hwqcb_addr - ida->hwqcb_busaddr)); 16957828Sjlemon return (hwqcb->qcb); 17057828Sjlemon} 17157828Sjlemon 172239740Sjhbstatic int 173239740Sjhbida_alloc_qcbs(struct ida_softc *ida) 17448156Sjlemon{ 17548156Sjlemon struct ida_qcb *qcb; 176239740Sjhb int error, i; 17748156Sjlemon 178239740Sjhb for (i = 0; i < IDA_QCB_MAX; i++) { 179239740Sjhb qcb = &ida->qcbs[i]; 18048156Sjlemon 181239740Sjhb error = bus_dmamap_create(ida->buffer_dmat, /*flags*/0, &qcb->dmamap); 182239740Sjhb if (error != 0) 183239740Sjhb return (error); 18448156Sjlemon 185239740Sjhb qcb->ida = ida; 186239740Sjhb qcb->flags = QCB_FREE; 187239740Sjhb qcb->hwqcb = &ida->hwqcbs[i]; 188239740Sjhb qcb->hwqcb->qcb = qcb; 189239740Sjhb qcb->hwqcb_busaddr = idahwqcbvtop(ida, qcb->hwqcb); 190239740Sjhb SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle); 191239740Sjhb } 192239740Sjhb return (0); 19348156Sjlemon} 19448156Sjlemon 19548156Sjlemonint 19648156Sjlemonida_init(struct ida_softc *ida) 19748156Sjlemon{ 198239740Sjhb struct ida_controller_info cinfo; 199239740Sjhb device_t child; 200239740Sjhb int error, i, unit; 20148156Sjlemon 20248156Sjlemon SLIST_INIT(&ida->free_qcbs); 20348156Sjlemon STAILQ_INIT(&ida->qcb_queue); 204144991Smdodd bioq_init(&ida->bio_queue); 20548156Sjlemon 20648156Sjlemon ida->qcbs = (struct ida_qcb *) 20769781Sdwmalone malloc(IDA_QCB_MAX * sizeof(struct ida_qcb), M_DEVBUF, 20869781Sdwmalone M_NOWAIT | M_ZERO); 20948156Sjlemon if (ida->qcbs == NULL) 21048156Sjlemon return (ENOMEM); 21148156Sjlemon 21248156Sjlemon /* 21348156Sjlemon * Create our DMA tags 21448156Sjlemon */ 21548156Sjlemon 21648156Sjlemon /* DMA tag for our hardware QCB structures */ 217138851Smdodd error = bus_dma_tag_create( 218138851Smdodd /* parent */ ida->parent_dmat, 219138851Smdodd /* alignment */ 1, 220138851Smdodd /* boundary */ 0, 221138851Smdodd /* lowaddr */ BUS_SPACE_MAXADDR, 222138851Smdodd /* highaddr */ BUS_SPACE_MAXADDR, 223138851Smdodd /* filter */ NULL, 224138851Smdodd /* filterarg */ NULL, 225138851Smdodd /* maxsize */ IDA_QCB_MAX * sizeof(struct ida_hardware_qcb), 226138851Smdodd /* nsegments */ 1, 227138851Smdodd /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, 228138851Smdodd /* flags */ 0, 229239740Sjhb /* lockfunc */ NULL, 230239740Sjhb /* lockarg */ NULL, 231138851Smdodd &ida->hwqcb_dmat); 23248156Sjlemon if (error) 233144991Smdodd return (ENOMEM); 23448156Sjlemon 23548156Sjlemon /* DMA tag for mapping buffers into device space */ 236138851Smdodd error = bus_dma_tag_create( 237138851Smdodd /* parent */ ida->parent_dmat, 238138851Smdodd /* alignment */ 1, 239138851Smdodd /* boundary */ 0, 240138851Smdodd /* lowaddr */ BUS_SPACE_MAXADDR, 241138851Smdodd /* highaddr */ BUS_SPACE_MAXADDR, 242138851Smdodd /* filter */ NULL, 243138851Smdodd /* filterarg */ NULL, 244281826Smav /* maxsize */ DFLTPHYS, 245138851Smdodd /* nsegments */ IDA_NSEG, 246138851Smdodd /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, 247138851Smdodd /* flags */ 0, 248138851Smdodd /* lockfunc */ busdma_lock_mutex, 249138851Smdodd /* lockarg */ &Giant, 250138851Smdodd &ida->buffer_dmat); 25148156Sjlemon if (error) 252144991Smdodd return (ENOMEM); 25348156Sjlemon 254144991Smdodd /* Allocation of hardware QCBs */ 25548156Sjlemon /* XXX allocation is rounded to hardware page size */ 25648156Sjlemon error = bus_dmamem_alloc(ida->hwqcb_dmat, 25748156Sjlemon (void **)&ida->hwqcbs, BUS_DMA_NOWAIT, &ida->hwqcb_dmamap); 25848156Sjlemon if (error) 259144991Smdodd return (ENOMEM); 26048156Sjlemon 261144991Smdodd /* And permanently map them in */ 262144991Smdodd bus_dmamap_load(ida->hwqcb_dmat, ida->hwqcb_dmamap, 26348156Sjlemon ida->hwqcbs, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb), 26448156Sjlemon ida_dma_map_cb, &ida->hwqcb_busaddr, /*flags*/0); 26548156Sjlemon 26648156Sjlemon bzero(ida->hwqcbs, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb)); 26748156Sjlemon 268239740Sjhb error = ida_alloc_qcbs(ida); 269239740Sjhb if (error) 270239740Sjhb return (error); 27148156Sjlemon 272239740Sjhb mtx_lock(&ida->lock); 27357828Sjlemon ida->cmd.int_enable(ida, 0); 27448156Sjlemon 27548156Sjlemon error = ida_command(ida, CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 27673113Sjlemon IDA_CONTROLLER, 0, DMA_DATA_IN); 27748156Sjlemon if (error) { 278239740Sjhb mtx_unlock(&ida->lock); 27948156Sjlemon device_printf(ida->dev, "CMD_GET_CTRL_INFO failed.\n"); 280239740Sjhb return (error); 28148156Sjlemon } 28248156Sjlemon 28348156Sjlemon device_printf(ida->dev, "drives=%d firm_rev=%c%c%c%c\n", 28448156Sjlemon cinfo.num_drvs, cinfo.firm_rev[0], cinfo.firm_rev[1], 28548156Sjlemon cinfo.firm_rev[2], cinfo.firm_rev[3]); 28648156Sjlemon 28763934Sjlemon if (ida->flags & IDA_FIRMWARE) { 28863934Sjlemon int data; 28948156Sjlemon 29063934Sjlemon error = ida_command(ida, CMD_START_FIRMWARE, 29173113Sjlemon &data, sizeof(data), IDA_CONTROLLER, 0, DMA_DATA_IN); 29263934Sjlemon if (error) { 293239740Sjhb mtx_unlock(&ida->lock); 29463934Sjlemon device_printf(ida->dev, "CMD_START_FIRMWARE failed.\n"); 295239740Sjhb return (error); 29663934Sjlemon } 29763934Sjlemon } 298239740Sjhb 299239740Sjhb ida->cmd.int_enable(ida, 1); 300239740Sjhb ida->flags |= IDA_ATTACHED; 301239740Sjhb mtx_unlock(&ida->lock); 30263934Sjlemon 303239740Sjhb for (i = 0; i < cinfo.num_drvs; i++) { 304239740Sjhb child = device_add_child(ida->dev, /*"idad"*/NULL, -1); 305239740Sjhb if (child != NULL) 306239740Sjhb device_set_ivars(child, (void *)(intptr_t)i); 307239740Sjhb } 308239740Sjhb 309239740Sjhb ida->ich.ich_func = ida_startup; 310239740Sjhb ida->ich.ich_arg = ida; 311239740Sjhb if (config_intrhook_establish(&ida->ich) != 0) { 312239740Sjhb device_delete_children(ida->dev); 313239740Sjhb device_printf(ida->dev, "Cannot establish configuration hook\n"); 314239740Sjhb return (error); 315239740Sjhb } 316239740Sjhb 317239740Sjhb unit = device_get_unit(ida->dev); 318239740Sjhb ida->ida_dev_t = make_dev(&ida_cdevsw, unit, 319124540Smdodd UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, 320239740Sjhb "ida%d", unit); 321124540Smdodd ida->ida_dev_t->si_drv1 = ida; 322124540Smdodd 323239740Sjhb return (0); 324239740Sjhb} 32548156Sjlemon 326239740Sjhbstatic void 327239740Sjhbida_startup(void *arg) 328239740Sjhb{ 329239740Sjhb struct ida_softc *ida; 330239740Sjhb 331239740Sjhb ida = arg; 332239740Sjhb 333239740Sjhb config_intrhook_disestablish(&ida->ich); 334239740Sjhb 335239740Sjhb mtx_lock(&Giant); 33648156Sjlemon bus_generic_attach(ida->dev); 337239740Sjhb mtx_unlock(&Giant); 33848156Sjlemon} 33948156Sjlemon 34057828Sjlemonint 34157828Sjlemonida_detach(device_t dev) 34257828Sjlemon{ 34357828Sjlemon struct ida_softc *ida; 344239740Sjhb int error; 34557828Sjlemon 346144991Smdodd ida = (struct ida_softc *)device_get_softc(dev); 34757828Sjlemon 348239740Sjhb error = bus_generic_detach(dev); 349239740Sjhb if (error) 350239740Sjhb return (error); 351239740Sjhb error = device_delete_children(dev); 352239740Sjhb if (error) 353239740Sjhb return (error); 354239740Sjhb 35557828Sjlemon /* 35657828Sjlemon * XXX 357144991Smdodd * before detaching, we must make sure that the system is 35857828Sjlemon * quiescent; nothing mounted, no pending activity. 35957828Sjlemon */ 36057828Sjlemon 36157828Sjlemon /* 36257828Sjlemon * XXX 36357828Sjlemon * now, how are we supposed to maintain a list of our drives? 36457828Sjlemon * iterate over our "child devices"? 36557828Sjlemon */ 36657828Sjlemon 367124540Smdodd destroy_dev(ida->ida_dev_t); 36857828Sjlemon ida_free(ida); 36957828Sjlemon return (error); 37057828Sjlemon} 37157828Sjlemon 37248156Sjlemonstatic void 373239740Sjhbida_data_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 37448156Sjlemon{ 375239740Sjhb struct ida_hardware_qcb *hwqcb; 376239740Sjhb struct ida_softc *ida; 377239740Sjhb struct ida_qcb *qcb; 378239740Sjhb bus_dmasync_op_t op; 37948156Sjlemon int i; 38048156Sjlemon 381239740Sjhb qcb = arg; 382239740Sjhb ida = qcb->ida; 383239740Sjhb if (!dumping) 384239740Sjhb mtx_assert(&ida->lock, MA_OWNED); 385239740Sjhb if (error) { 386239740Sjhb qcb->error = error; 387239740Sjhb ida_done(ida, qcb); 388239740Sjhb return; 389239740Sjhb } 390239740Sjhb 391239740Sjhb hwqcb = qcb->hwqcb; 392144991Smdodd hwqcb->hdr.size = htole16((sizeof(struct ida_req) + 393124538Smdodd sizeof(struct ida_sgb) * IDA_NSEG) >> 2); 39448156Sjlemon 39548156Sjlemon for (i = 0; i < nsegments; i++) { 396124538Smdodd hwqcb->seg[i].addr = htole32(segs[i].ds_addr); 397124538Smdodd hwqcb->seg[i].length = htole32(segs[i].ds_len); 39848156Sjlemon } 39948156Sjlemon hwqcb->req.sgcount = nsegments; 400239740Sjhb if (qcb->flags & DMA_DATA_TRANSFER) { 401239740Sjhb switch (qcb->flags & DMA_DATA_TRANSFER) { 402239740Sjhb case DMA_DATA_TRANSFER: 403239740Sjhb op = BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE; 404239740Sjhb break; 405239740Sjhb case DMA_DATA_IN: 406239740Sjhb op = BUS_DMASYNC_PREREAD; 407239740Sjhb break; 408239740Sjhb default: 409239740Sjhb KASSERT((qcb->flags & DMA_DATA_TRANSFER) == 410239740Sjhb DMA_DATA_OUT, ("bad DMA data flags")); 411239740Sjhb op = BUS_DMASYNC_PREWRITE; 412239740Sjhb break; 413239740Sjhb } 414239740Sjhb bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op); 415239740Sjhb } 416239740Sjhb bus_dmamap_sync(ida->hwqcb_dmat, ida->hwqcb_dmamap, 417239740Sjhb BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 418239740Sjhb 419239740Sjhb STAILQ_INSERT_TAIL(&ida->qcb_queue, qcb, link.stqe); 420239740Sjhb ida_start(ida); 421239740Sjhb ida->flags &= ~IDA_QFROZEN; 42248156Sjlemon} 42348156Sjlemon 424239740Sjhbstatic int 425239740Sjhbida_map_qcb(struct ida_softc *ida, struct ida_qcb *qcb, void *data, 426239740Sjhb bus_size_t datasize) 427239740Sjhb{ 428239740Sjhb int error, flags; 429239740Sjhb 430239740Sjhb if (ida->flags & IDA_INTERRUPTS) 431239740Sjhb flags = BUS_DMA_WAITOK; 432239740Sjhb else 433239740Sjhb flags = BUS_DMA_NOWAIT; 434239740Sjhb error = bus_dmamap_load(ida->buffer_dmat, qcb->dmamap, data, datasize, 435239740Sjhb ida_data_cb, qcb, flags); 436239740Sjhb if (error == EINPROGRESS) { 437239740Sjhb ida->flags |= IDA_QFROZEN; 438239740Sjhb error = 0; 439239740Sjhb } 440239740Sjhb return (error); 441239740Sjhb} 442239740Sjhb 44348156Sjlemonint 44448156Sjlemonida_command(struct ida_softc *ida, int command, void *data, int datasize, 44573113Sjlemon int drive, u_int32_t pblkno, int flags) 44648156Sjlemon{ 44748156Sjlemon struct ida_hardware_qcb *hwqcb; 44848156Sjlemon struct ida_qcb *qcb; 449239740Sjhb int error; 45048156Sjlemon 451239740Sjhb if (!dumping) 452239740Sjhb mtx_assert(&ida->lock, MA_OWNED); 45348156Sjlemon qcb = ida_get_qcb(ida); 45448156Sjlemon 45548156Sjlemon if (qcb == NULL) { 456239740Sjhb device_printf(ida->dev, "out of QCBs\n"); 45773113Sjlemon return (EAGAIN); 45848156Sjlemon } 45948156Sjlemon 460239740Sjhb qcb->flags = flags | IDA_COMMAND; 46148156Sjlemon hwqcb = qcb->hwqcb; 46263934Sjlemon hwqcb->hdr.drive = drive; 463124538Smdodd hwqcb->req.blkno = htole32(pblkno); 464124538Smdodd hwqcb->req.bcount = htole16(howmany(datasize, DEV_BSIZE)); 46548156Sjlemon hwqcb->req.command = command; 46648156Sjlemon 467239740Sjhb error = ida_map_qcb(ida, qcb, data, datasize); 468239740Sjhb if (error == 0) { 469239740Sjhb error = ida_wait(ida, qcb); 470239740Sjhb /* Don't free QCB on a timeout in case it later completes. */ 471239740Sjhb if (error) 472239740Sjhb return (error); 473239740Sjhb error = qcb->error; 474239740Sjhb } 47548156Sjlemon 47648156Sjlemon /* XXX should have status returned here? */ 47748156Sjlemon /* XXX have "status pointer" area in QCB? */ 47848156Sjlemon 479239740Sjhb ida_free_qcb(ida, qcb); 48073113Sjlemon return (error); 48148156Sjlemon} 48248156Sjlemon 48348156Sjlemonvoid 48459249Sphkida_submit_buf(struct ida_softc *ida, struct bio *bp) 48548156Sjlemon{ 486239740Sjhb mtx_lock(&ida->lock); 487144991Smdodd bioq_insert_tail(&ida->bio_queue, bp); 488239740Sjhb ida_startio(ida); 489239740Sjhb mtx_unlock(&ida->lock); 49048156Sjlemon} 49148156Sjlemon 49248156Sjlemonstatic void 493239740Sjhbida_startio(struct ida_softc *ida) 49448156Sjlemon{ 49548156Sjlemon struct ida_hardware_qcb *hwqcb; 49648156Sjlemon struct ida_qcb *qcb; 497239740Sjhb struct idad_softc *drv; 49859249Sphk struct bio *bp; 499239740Sjhb int error; 50048156Sjlemon 501239740Sjhb mtx_assert(&ida->lock, MA_OWNED); 502239740Sjhb for (;;) { 503239740Sjhb if (ida->flags & IDA_QFROZEN) 504239740Sjhb return; 505239740Sjhb bp = bioq_first(&ida->bio_queue); 506239740Sjhb if (bp == NULL) 507239740Sjhb return; /* no more buffers */ 50848156Sjlemon 509239740Sjhb qcb = ida_get_qcb(ida); 510239740Sjhb if (qcb == NULL) 511239740Sjhb return; /* out of resources */ 51248156Sjlemon 513239740Sjhb bioq_remove(&ida->bio_queue, bp); 514239740Sjhb qcb->buf = bp; 515239740Sjhb qcb->flags = bp->bio_cmd == BIO_READ ? DMA_DATA_IN : DMA_DATA_OUT; 51648156Sjlemon 517239740Sjhb hwqcb = qcb->hwqcb; 518239740Sjhb drv = bp->bio_driver1; 519239740Sjhb hwqcb->hdr.drive = drv->drive; 520239740Sjhb hwqcb->req.blkno = bp->bio_pblkno; 521239740Sjhb hwqcb->req.bcount = howmany(bp->bio_bcount, DEV_BSIZE); 522239740Sjhb hwqcb->req.command = bp->bio_cmd == BIO_READ ? CMD_READ : CMD_WRITE; 52348156Sjlemon 524239740Sjhb error = ida_map_qcb(ida, qcb, bp->bio_data, bp->bio_bcount); 525239740Sjhb if (error) { 526239740Sjhb qcb->error = error; 527239740Sjhb ida_done(ida, qcb); 528239740Sjhb } 52948156Sjlemon } 53048156Sjlemon} 53148156Sjlemon 53248156Sjlemonstatic void 53348156Sjlemonida_start(struct ida_softc *ida) 53448156Sjlemon{ 53548156Sjlemon struct ida_qcb *qcb; 53648156Sjlemon 537239740Sjhb if (!dumping) 538239740Sjhb mtx_assert(&ida->lock, MA_OWNED); 53948156Sjlemon while ((qcb = STAILQ_FIRST(&ida->qcb_queue)) != NULL) { 54057828Sjlemon if (ida->cmd.fifo_full(ida)) 54157828Sjlemon break; 54248156Sjlemon STAILQ_REMOVE_HEAD(&ida->qcb_queue, link.stqe); 54348156Sjlemon /* 54448156Sjlemon * XXX 545138853Smdodd * place the qcb on an active list? 54648156Sjlemon */ 547138853Smdodd 548138853Smdodd /* Set a timeout. */ 549239740Sjhb if (!ida->qactive && !dumping) 550138853Smdodd callout_reset(&ida->ch, hz * 5, ida_timeout, ida); 551138853Smdodd ida->qactive++; 552138853Smdodd 55348156Sjlemon qcb->state = QCB_ACTIVE; 55457828Sjlemon ida->cmd.submit(ida, qcb); 55548156Sjlemon } 55648156Sjlemon} 55748156Sjlemon 55873113Sjlemonstatic int 55973113Sjlemonida_wait(struct ida_softc *ida, struct ida_qcb *qcb) 56048156Sjlemon{ 56148156Sjlemon struct ida_qcb *qcb_done = NULL; 56248156Sjlemon bus_addr_t completed; 56373113Sjlemon int delay; 56448156Sjlemon 565239740Sjhb if (!dumping) 566239740Sjhb mtx_assert(&ida->lock, MA_OWNED); 56773113Sjlemon if (ida->flags & IDA_INTERRUPTS) { 568239740Sjhb if (mtx_sleep(qcb, &ida->lock, PRIBIO, "idacmd", 5 * hz)) { 569239740Sjhb qcb->state = QCB_TIMEDOUT; 57073113Sjlemon return (ETIMEDOUT); 571239740Sjhb } 57273113Sjlemon return (0); 57348156Sjlemon } 57448156Sjlemon 57573113Sjlemonagain: 57673113Sjlemon delay = 5 * 1000 * 100; /* 5 sec delay */ 57757828Sjlemon while ((completed = ida->cmd.done(ida)) == 0) { 578239740Sjhb if (delay-- == 0) { 579239740Sjhb qcb->state = QCB_TIMEDOUT; 58073113Sjlemon return (ETIMEDOUT); 581239740Sjhb } 58248156Sjlemon DELAY(10); 58348156Sjlemon } 58448156Sjlemon 58548156Sjlemon qcb_done = idahwqcbptov(ida, completed & ~3); 58648156Sjlemon if (qcb_done != qcb) 58773113Sjlemon goto again; 58848156Sjlemon ida_done(ida, qcb); 58973113Sjlemon return (0); 59048156Sjlemon} 59148156Sjlemon 59248156Sjlemonvoid 59348156Sjlemonida_intr(void *data) 59448156Sjlemon{ 59548156Sjlemon struct ida_softc *ida; 59648156Sjlemon struct ida_qcb *qcb; 59748156Sjlemon bus_addr_t completed; 59848156Sjlemon 59948156Sjlemon ida = (struct ida_softc *)data; 60048156Sjlemon 601239740Sjhb mtx_lock(&ida->lock); 602239740Sjhb if (ida->cmd.int_pending(ida) == 0) { 603239740Sjhb mtx_unlock(&ida->lock); 60448156Sjlemon return; /* not our interrupt */ 605239740Sjhb } 60648156Sjlemon 60757828Sjlemon while ((completed = ida->cmd.done(ida)) != 0) { 60848156Sjlemon qcb = idahwqcbptov(ida, completed & ~3); 60948156Sjlemon 61048156Sjlemon if (qcb == NULL || qcb->state != QCB_ACTIVE) { 61148156Sjlemon device_printf(ida->dev, 612106591Sjhb "ignoring completion %jx\n", (intmax_t)completed); 61348156Sjlemon continue; 61448156Sjlemon } 615124449Smdodd /* Handle "Bad Command List" errors. */ 616124449Smdodd if ((completed & 3) && (qcb->hwqcb->req.error == 0)) 617124449Smdodd qcb->hwqcb->req.error = CMD_REJECTED; 61848156Sjlemon ida_done(ida, qcb); 61948156Sjlemon } 620239740Sjhb ida_startio(ida); 621239740Sjhb mtx_unlock(&ida->lock); 62248156Sjlemon} 62348156Sjlemon 62448156Sjlemon/* 62548156Sjlemon * should switch out command type; may be status, not just I/O. 62648156Sjlemon */ 62748156Sjlemonstatic void 62848156Sjlemonida_done(struct ida_softc *ida, struct ida_qcb *qcb) 62948156Sjlemon{ 630239740Sjhb bus_dmasync_op_t op; 631239740Sjhb int active, error = 0; 63248156Sjlemon 63348156Sjlemon /* 63448156Sjlemon * finish up command 63548156Sjlemon */ 636239740Sjhb if (!dumping) 637239740Sjhb mtx_assert(&ida->lock, MA_OWNED); 638239740Sjhb active = (qcb->state != QCB_FREE); 639239740Sjhb if (qcb->flags & DMA_DATA_TRANSFER && active) { 640239740Sjhb switch (qcb->flags & DMA_DATA_TRANSFER) { 641239740Sjhb case DMA_DATA_TRANSFER: 642239740Sjhb op = BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE; 643239740Sjhb break; 644239740Sjhb case DMA_DATA_IN: 645239740Sjhb op = BUS_DMASYNC_POSTREAD; 646239740Sjhb break; 647239740Sjhb default: 648239740Sjhb KASSERT((qcb->flags & DMA_DATA_TRANSFER) == 649239740Sjhb DMA_DATA_OUT, ("bad DMA data flags")); 650239740Sjhb op = BUS_DMASYNC_POSTWRITE; 651239740Sjhb break; 652239740Sjhb } 65348156Sjlemon bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op); 65448156Sjlemon bus_dmamap_unload(ida->buffer_dmat, qcb->dmamap); 65548156Sjlemon } 656239740Sjhb if (active) 657239740Sjhb bus_dmamap_sync(ida->hwqcb_dmat, ida->hwqcb_dmamap, 658239740Sjhb BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 65948156Sjlemon 660124423Smdodd if (qcb->hwqcb->req.error & SOFT_ERROR) { 661124423Smdodd if (qcb->buf) 662124423Smdodd device_printf(ida->dev, "soft %s error\n", 663124423Smdodd qcb->buf->bio_cmd == BIO_READ ? 664124423Smdodd "read" : "write"); 665124423Smdodd else 666124423Smdodd device_printf(ida->dev, "soft error\n"); 667124423Smdodd } 66848156Sjlemon if (qcb->hwqcb->req.error & HARD_ERROR) { 66948156Sjlemon error = 1; 670124423Smdodd if (qcb->buf) 671124423Smdodd device_printf(ida->dev, "hard %s error\n", 672124423Smdodd qcb->buf->bio_cmd == BIO_READ ? 673124423Smdodd "read" : "write"); 674124423Smdodd else 675124423Smdodd device_printf(ida->dev, "hard error\n"); 67648156Sjlemon } 67748156Sjlemon if (qcb->hwqcb->req.error & CMD_REJECTED) { 67848156Sjlemon error = 1; 67948156Sjlemon device_printf(ida->dev, "invalid request\n"); 68048156Sjlemon } 681239740Sjhb if (qcb->error) { 682239740Sjhb error = 1; 683239740Sjhb device_printf(ida->dev, "request failed to map: %d\n", qcb->error); 684239740Sjhb } 68548156Sjlemon 68648156Sjlemon if (qcb->flags & IDA_COMMAND) { 68773113Sjlemon if (ida->flags & IDA_INTERRUPTS) 68848156Sjlemon wakeup(qcb); 689239740Sjhb if (qcb->state == QCB_TIMEDOUT) 690239740Sjhb ida_free_qcb(ida, qcb); 69148156Sjlemon } else { 692145023Smdodd KASSERT(qcb->buf != NULL, ("ida_done(): qcb->buf is NULL!")); 69348156Sjlemon if (error) 69459249Sphk qcb->buf->bio_flags |= BIO_ERROR; 69559485Smdodd idad_intr(qcb->buf); 696239740Sjhb ida_free_qcb(ida, qcb); 69748156Sjlemon } 69848156Sjlemon 699239740Sjhb if (!active) 700239740Sjhb return; 701239740Sjhb 702138853Smdodd ida->qactive--; 703138853Smdodd /* Reschedule or cancel timeout */ 704138853Smdodd if (ida->qactive) 705138853Smdodd callout_reset(&ida->ch, hz * 5, ida_timeout, ida); 706138853Smdodd else 707138853Smdodd callout_stop(&ida->ch); 70848156Sjlemon} 709124540Smdodd 710138853Smdoddstatic void 711239740Sjhbida_timeout(void *arg) 712138853Smdodd{ 713138853Smdodd struct ida_softc *ida; 714138853Smdodd 715138853Smdodd ida = (struct ida_softc *)arg; 716138853Smdodd device_printf(ida->dev, "%s() qactive %d\n", __func__, ida->qactive); 717138853Smdodd 718138853Smdodd if (ida->flags & IDA_INTERRUPTS) 719138853Smdodd device_printf(ida->dev, "IDA_INTERRUPTS\n"); 720138853Smdodd 721138853Smdodd device_printf(ida->dev, "\t R_CMD_FIFO: %08x\n" 722138853Smdodd "\t R_DONE_FIFO: %08x\n" 723138853Smdodd "\t R_INT_MASK: %08x\n" 724138853Smdodd "\t R_STATUS: %08x\n" 725138853Smdodd "\tR_INT_PENDING: %08x\n", 726138853Smdodd ida_inl(ida, R_CMD_FIFO), 727138853Smdodd ida_inl(ida, R_DONE_FIFO), 728138853Smdodd ida_inl(ida, R_INT_MASK), 729138853Smdodd ida_inl(ida, R_STATUS), 730138853Smdodd ida_inl(ida, R_INT_PENDING)); 731138853Smdodd 732138853Smdodd return; 733138853Smdodd} 734138853Smdodd 735124540Smdodd/* 736124540Smdodd * IOCTL stuff follows. 737124540Smdodd */ 738124540Smdoddstruct cmd_info { 739124540Smdodd int cmd; 740124540Smdodd int len; 741124540Smdodd int flags; 742124540Smdodd}; 743124540Smdoddstatic struct cmd_info *ida_cmd_lookup(int); 744124540Smdodd 745124540Smdoddstatic int 746130585Sphkida_ioctl (struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) 747124540Smdodd{ 748124540Smdodd struct ida_softc *sc; 749124540Smdodd struct ida_user_command *uc; 750124540Smdodd struct cmd_info *ci; 751124540Smdodd int len; 752124540Smdodd int flags; 753124540Smdodd int error; 754124540Smdodd int data; 755124540Smdodd void *daddr; 756124540Smdodd 757124540Smdodd sc = (struct ida_softc *)dev->si_drv1; 758124540Smdodd uc = (struct ida_user_command *)addr; 759124540Smdodd error = 0; 760124540Smdodd 761124540Smdodd switch (cmd) { 762124540Smdodd case IDAIO_COMMAND: 763124540Smdodd ci = ida_cmd_lookup(uc->command); 764124540Smdodd if (ci == NULL) { 765124540Smdodd error = EINVAL; 766124540Smdodd break; 767124540Smdodd } 768124540Smdodd len = ci->len; 769124540Smdodd flags = ci->flags; 770124540Smdodd if (len) 771124540Smdodd daddr = &uc->d.buf; 772124540Smdodd else { 773124540Smdodd daddr = &data; 774124540Smdodd len = sizeof(data); 775124540Smdodd } 776239740Sjhb mtx_lock(&sc->lock); 777124540Smdodd error = ida_command(sc, uc->command, daddr, len, 778124540Smdodd uc->drive, uc->blkno, flags); 779239740Sjhb mtx_unlock(&sc->lock); 780124540Smdodd break; 781124540Smdodd default: 782124540Smdodd error = ENOIOCTL; 783124540Smdodd break; 784124540Smdodd } 785124540Smdodd return (error); 786124540Smdodd} 787124540Smdodd 788124540Smdoddstatic struct cmd_info ci_list[] = { 789124540Smdodd { CMD_GET_LOG_DRV_INFO, 790124540Smdodd sizeof(struct ida_drive_info), DMA_DATA_IN }, 791124540Smdodd { CMD_GET_CTRL_INFO, 792124540Smdodd sizeof(struct ida_controller_info), DMA_DATA_IN }, 793124540Smdodd { CMD_SENSE_DRV_STATUS, 794124540Smdodd sizeof(struct ida_drive_status), DMA_DATA_IN }, 795124540Smdodd { CMD_START_RECOVERY, 0, 0 }, 796124540Smdodd { CMD_GET_PHYS_DRV_INFO, 797124540Smdodd sizeof(struct ida_phys_drv_info), DMA_DATA_TRANSFER }, 798124540Smdodd { CMD_BLINK_DRV_LEDS, 799124540Smdodd sizeof(struct ida_blink_drv_leds), DMA_DATA_OUT }, 800124540Smdodd { CMD_SENSE_DRV_LEDS, 801124540Smdodd sizeof(struct ida_blink_drv_leds), DMA_DATA_IN }, 802124540Smdodd { CMD_GET_LOG_DRV_EXT, 803124540Smdodd sizeof(struct ida_drive_info_ext), DMA_DATA_IN }, 804124540Smdodd { CMD_RESET_CTRL, 0, 0 }, 805124540Smdodd { CMD_GET_CONFIG, 0, 0 }, 806124540Smdodd { CMD_SET_CONFIG, 0, 0 }, 807124540Smdodd { CMD_LABEL_LOG_DRV, 808124540Smdodd sizeof(struct ida_label_logical), DMA_DATA_OUT }, 809124540Smdodd { CMD_SET_SURFACE_DELAY, 0, 0 }, 810124540Smdodd { CMD_SENSE_BUS_PARAMS, 0, 0 }, 811124540Smdodd { CMD_SENSE_SUBSYS_INFO, 0, 0 }, 812124540Smdodd { CMD_SENSE_SURFACE_ATS, 0, 0 }, 813124540Smdodd { CMD_PASSTHROUGH, 0, 0 }, 814124540Smdodd { CMD_RESET_SCSI_DEV, 0, 0 }, 815124540Smdodd { CMD_PAUSE_BG_ACT, 0, 0 }, 816124540Smdodd { CMD_RESUME_BG_ACT, 0, 0 }, 817124540Smdodd { CMD_START_FIRMWARE, 0, 0 }, 818124540Smdodd { CMD_SENSE_DRV_ERR_LOG, 0, 0 }, 819124540Smdodd { CMD_START_CPM, 0, 0 }, 820124540Smdodd { CMD_SENSE_CP, 0, 0 }, 821124540Smdodd { CMD_STOP_CPM, 0, 0 }, 822124540Smdodd { CMD_FLUSH_CACHE, 0, 0 }, 823124540Smdodd { CMD_ACCEPT_MEDIA_EXCH, 0, 0 }, 824124540Smdodd { 0, 0, 0 } 825124540Smdodd}; 826124540Smdodd 827124540Smdoddstatic struct cmd_info * 828124540Smdoddida_cmd_lookup (int command) 829124540Smdodd{ 830124540Smdodd struct cmd_info *ci; 831124540Smdodd 832124540Smdodd ci = ci_list; 833124540Smdodd while (ci->cmd) { 834124540Smdodd if (ci->cmd == command) 835124540Smdodd return (ci); 836124540Smdodd ci++; 837124540Smdodd } 838124540Smdodd return (NULL); 839124540Smdodd} 840