165793Smsmith/*- 265793Smsmith * Copyright (c) 2000 Michael Smith 381082Sscottl * Copyright (c) 2001 Scott Long 465793Smsmith * Copyright (c) 2000 BSDi 581082Sscottl * Copyright (c) 2001 Adaptec, Inc. 665793Smsmith * All rights reserved. 765793Smsmith * 865793Smsmith * Redistribution and use in source and binary forms, with or without 965793Smsmith * modification, are permitted provided that the following conditions 1065793Smsmith * are met: 1165793Smsmith * 1. Redistributions of source code must retain the above copyright 1265793Smsmith * notice, this list of conditions and the following disclaimer. 1365793Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1465793Smsmith * notice, this list of conditions and the following disclaimer in the 1565793Smsmith * documentation and/or other materials provided with the distribution. 1665793Smsmith * 1765793Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1865793Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1965793Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2065793Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2165793Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2265793Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2365793Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2465793Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2565793Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2665793Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2765793Smsmith * SUCH DAMAGE. 2865793Smsmith */ 2965793Smsmith 30119418Sobrien#include <sys/cdefs.h> 31119418Sobrien__FBSDID("$FreeBSD$"); 32119418Sobrien 3381151Sscottl#include "opt_aac.h" 3481151Sscottl 3565793Smsmith#include <sys/param.h> 3665793Smsmith#include <sys/systm.h> 3765793Smsmith#include <sys/kernel.h> 38129879Sphk#include <sys/module.h> 3965793Smsmith 4065793Smsmith#include <sys/bus.h> 4165793Smsmith#include <sys/conf.h> 4265793Smsmith#include <sys/disk.h> 4365793Smsmith 4482527Sscottl#include <vm/vm.h> 4582527Sscottl#include <vm/pmap.h> 4682527Sscottl 4782527Sscottl#include <machine/md_var.h> 4865793Smsmith#include <machine/bus.h> 4965793Smsmith#include <sys/rman.h> 5065793Smsmith 5165793Smsmith#include <dev/aac/aacreg.h> 52138635Sscottl#include <sys/aac_ioctl.h> 5365793Smsmith#include <dev/aac/aacvar.h> 5465793Smsmith 5565793Smsmith/* 5665793Smsmith * Interface to parent. 5765793Smsmith */ 5865793Smsmithstatic int aac_disk_probe(device_t dev); 5965793Smsmithstatic int aac_disk_attach(device_t dev); 6065793Smsmithstatic int aac_disk_detach(device_t dev); 6165793Smsmith 6265793Smsmith/* 6365793Smsmith * Interface to the device switch. 6465793Smsmith */ 65111525Sscottlstatic disk_open_t aac_disk_open; 66111525Sscottlstatic disk_close_t aac_disk_close; 67111525Sscottlstatic disk_strategy_t aac_disk_strategy; 68111220Sphkstatic dumper_t aac_disk_dump; 6965793Smsmith 7089112Smsmithstatic devclass_t aac_disk_devclass; 7165793Smsmith 7265793Smsmithstatic device_method_t aac_disk_methods[] = { 7383114Sscottl DEVMETHOD(device_probe, aac_disk_probe), 7483114Sscottl DEVMETHOD(device_attach, aac_disk_attach), 7583114Sscottl DEVMETHOD(device_detach, aac_disk_detach), 76251070Smarius DEVMETHOD_END 7765793Smsmith}; 7865793Smsmith 7965793Smsmithstatic driver_t aac_disk_driver = { 8083114Sscottl "aacd", 8183114Sscottl aac_disk_methods, 8283114Sscottl sizeof(struct aac_disk) 8365793Smsmith}; 8465793Smsmith 85251070SmariusDRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, NULL, NULL); 8665793Smsmith 8783114Sscottl/* 8865793Smsmith * Handle open from generic layer. 8965793Smsmith * 90206534Semaste * This is called by the diskslice code on first open in order to get the 9165793Smsmith * basic device geometry paramters. 9265793Smsmith */ 9365793Smsmithstatic int 94111525Sscottlaac_disk_open(struct disk *dp) 9565793Smsmith{ 9683114Sscottl struct aac_disk *sc; 9765793Smsmith 98177567Semaste fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); 9983114Sscottl 100111525Sscottl sc = (struct aac_disk *)dp->d_drv1; 10165793Smsmith 102109088Sscottl if (sc == NULL) { 103109088Sscottl printf("aac_disk_open: No Softc\n"); 10483114Sscottl return (ENXIO); 105109088Sscottl } 10665793Smsmith 10783114Sscottl /* check that the controller is up and running */ 108109088Sscottl if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND) { 109212773Semaste device_printf(sc->ad_controller->aac_dev, 110212773Semaste "Controller Suspended controller state = 0x%x\n", 111212773Semaste sc->ad_controller->aac_state); 11283114Sscottl return(ENXIO); 113109088Sscottl } 11465793Smsmith 11583114Sscottl sc->ad_flags |= AAC_DISK_OPEN; 11683114Sscottl return (0); 11765793Smsmith} 11865793Smsmith 11983114Sscottl/* 12065793Smsmith * Handle last close of the disk device. 12165793Smsmith */ 12265793Smsmithstatic int 123111525Sscottlaac_disk_close(struct disk *dp) 12465793Smsmith{ 12583114Sscottl struct aac_disk *sc; 12665793Smsmith 127177567Semaste fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); 12883114Sscottl 129111525Sscottl sc = (struct aac_disk *)dp->d_drv1; 13065793Smsmith 13183114Sscottl if (sc == NULL) 13283114Sscottl return (ENXIO); 13365793Smsmith 13483114Sscottl sc->ad_flags &= ~AAC_DISK_OPEN; 13583114Sscottl return (0); 13665793Smsmith} 13765793Smsmith 13883114Sscottl/* 13965793Smsmith * Handle an I/O request. 14065793Smsmith */ 14165793Smsmithstatic void 14265793Smsmithaac_disk_strategy(struct bio *bp) 14365793Smsmith{ 14483114Sscottl struct aac_disk *sc; 14565793Smsmith 146111525Sscottl sc = (struct aac_disk *)bp->bio_disk->d_drv1; 147177567Semaste fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); 14865793Smsmith 14983114Sscottl /* bogus disk? */ 15083114Sscottl if (sc == NULL) { 15183114Sscottl bp->bio_flags |= BIO_ERROR; 15283114Sscottl bp->bio_error = EINVAL; 15383114Sscottl biodone(bp); 15483114Sscottl return; 15583114Sscottl } 15682527Sscottl 15783114Sscottl /* do-nothing operation? */ 15883114Sscottl if (bp->bio_bcount == 0) { 15983114Sscottl bp->bio_resid = bp->bio_bcount; 16083114Sscottl biodone(bp); 16183114Sscottl return; 16283114Sscottl } 16365793Smsmith 16483114Sscottl /* perform accounting */ 16583114Sscottl 16683114Sscottl /* pass the bio to the controller - it can work out who we are */ 167133540Sscottl mtx_lock(&sc->ad_controller->aac_io_lock); 16883114Sscottl aac_submit_bio(bp); 169133540Sscottl mtx_unlock(&sc->ad_controller->aac_io_lock); 17065793Smsmith} 17165793Smsmith 17283114Sscottl/* 17395350Sscottl * Map the S/G elements for doing a dump. 17495350Sscottl */ 17595350Sscottlstatic void 17695350Sscottlaac_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 17795350Sscottl{ 17895350Sscottl struct aac_fib *fib; 17995350Sscottl struct aac_blockwrite *bw; 18095350Sscottl struct aac_sg_table *sg; 18195350Sscottl int i; 18295350Sscottl 18395350Sscottl fib = (struct aac_fib *)arg; 18495350Sscottl bw = (struct aac_blockwrite *)&fib->data[0]; 18595350Sscottl sg = &bw->SgMap; 18695350Sscottl 18795350Sscottl if (sg != NULL) { 18895350Sscottl sg->SgCount = nsegs; 18995350Sscottl for (i = 0; i < nsegs; i++) { 190116553Sscottl if (segs[i].ds_addr >= BUS_SPACE_MAXADDR_32BIT) 191116553Sscottl return; 19295350Sscottl sg->SgEntry[i].SgAddress = segs[i].ds_addr; 19395350Sscottl sg->SgEntry[i].SgByteCount = segs[i].ds_len; 19495350Sscottl } 19595350Sscottl fib->Header.Size = nsegs * sizeof(struct aac_sg_entry); 19695350Sscottl } 19795350Sscottl} 19895350Sscottl 19995350Sscottl/* 200177899Semaste * Map the S/G elements for doing a dump on 64-bit capable devices. 201177899Semaste */ 202177899Semastestatic void 203177899Semasteaac_dump_map_sg64(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 204177899Semaste{ 205177899Semaste struct aac_fib *fib; 206177899Semaste struct aac_blockwrite64 *bw; 207177899Semaste struct aac_sg_table64 *sg; 208177899Semaste int i; 209177899Semaste 210177899Semaste fib = (struct aac_fib *)arg; 211177899Semaste bw = (struct aac_blockwrite64 *)&fib->data[0]; 212177899Semaste sg = &bw->SgMap64; 213177899Semaste 214177899Semaste if (sg != NULL) { 215177899Semaste sg->SgCount = nsegs; 216177899Semaste for (i = 0; i < nsegs; i++) { 217177899Semaste sg->SgEntry64[i].SgAddress = segs[i].ds_addr; 218177899Semaste sg->SgEntry64[i].SgByteCount = segs[i].ds_len; 219177899Semaste } 220177899Semaste fib->Header.Size = nsegs * sizeof(struct aac_sg_entry64); 221177899Semaste } 222177899Semaste} 223177899Semaste 224177899Semaste/* 22582527Sscottl * Dump memory out to an array 22682527Sscottl * 227195614Sjkim * Send out one command at a time with up to maxio of data. 22882527Sscottl */ 22982527Sscottlstatic int 230111220Sphkaac_disk_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) 23182527Sscottl{ 23283114Sscottl struct aac_disk *ad; 23383114Sscottl struct aac_softc *sc; 23495350Sscottl struct aac_fib *fib; 235195614Sjkim size_t len, maxio; 23695350Sscottl int size; 23795350Sscottl static bus_dmamap_t dump_datamap; 23895350Sscottl static int first = 0; 239111220Sphk struct disk *dp; 240177899Semaste bus_dmamap_callback_t *callback; 241177899Semaste u_int32_t command; 24282527Sscottl 243111220Sphk dp = arg; 244111525Sscottl ad = dp->d_drv1; 24582527Sscottl 24683114Sscottl if (ad == NULL) 24795350Sscottl return (EINVAL); 24882527Sscottl 24983114Sscottl sc= ad->ad_controller; 25082527Sscottl 25195350Sscottl if (!first) { 25295350Sscottl first = 1; 25395350Sscottl if (bus_dmamap_create(sc->aac_buffer_dmat, 0, &dump_datamap)) { 254212773Semaste device_printf(sc->aac_dev, 255212773Semaste "bus_dmamap_create failed\n"); 25695350Sscottl return (ENOMEM); 25795350Sscottl } 25895350Sscottl } 25982527Sscottl 260130006Sscottl /* Skip aac_alloc_sync_fib(). We don't want to mess with sleep locks */ 261130006Sscottl fib = &sc->aac_common->ac_sync_fib; 26282527Sscottl 26395350Sscottl while (length > 0) { 264195614Sjkim maxio = sc->aac_max_sectors << 9; 265195614Sjkim len = (length > maxio) ? maxio : length; 266177899Semaste if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) { 267177899Semaste struct aac_blockwrite *bw; 268177899Semaste bw = (struct aac_blockwrite *)&fib->data[0]; 269177899Semaste bw->Command = VM_CtBlockWrite; 270177899Semaste bw->ContainerId = ad->ad_container->co_mntobj.ObjectId; 271177899Semaste bw->BlockNumber = offset / AAC_BLOCK_SIZE; 272177899Semaste bw->ByteCount = len; 273177899Semaste bw->Stable = CUNSTABLE; 274177899Semaste command = ContainerCommand; 275177899Semaste callback = aac_dump_map_sg; 276177899Semaste size = sizeof(struct aac_blockwrite); 277177899Semaste } else { 278177899Semaste struct aac_blockwrite64 *bw; 279177899Semaste bw = (struct aac_blockwrite64 *)&fib->data[0]; 280177899Semaste bw->Command = VM_CtHostWrite64; 281177899Semaste bw->ContainerId = ad->ad_container->co_mntobj.ObjectId; 282177899Semaste bw->BlockNumber = offset / AAC_BLOCK_SIZE; 283177899Semaste bw->SectorCount = len / AAC_BLOCK_SIZE; 284177899Semaste bw->Pad = 0; 285177899Semaste bw->Flags = 0; 286177899Semaste command = ContainerCommand64; 287177899Semaste callback = aac_dump_map_sg64; 288177899Semaste size = sizeof(struct aac_blockwrite64); 289177899Semaste } 290116553Sscottl 291116553Sscottl /* 292116553Sscottl * There really isn't any way to recover from errors or 293116553Sscottl * resource shortages here. Oh well. Because of that, don't 294116553Sscottl * bother trying to send the command from the callback; there 295116553Sscottl * is too much required context. 296116553Sscottl */ 297116553Sscottl if (bus_dmamap_load(sc->aac_buffer_dmat, dump_datamap, virtual, 298177899Semaste len, callback, fib, BUS_DMA_NOWAIT) != 0) 299145811Sscottl return (ENOMEM); 300116553Sscottl 30195350Sscottl bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap, 30295350Sscottl BUS_DMASYNC_PREWRITE); 30382527Sscottl 30495350Sscottl /* fib->Header.Size is set in aac_dump_map_sg */ 305177899Semaste size += fib->Header.Size; 30683114Sscottl 307177899Semaste if (aac_sync_fib(sc, command, 0, fib, size)) { 308212773Semaste device_printf(sc->aac_dev, 309212773Semaste "Error dumping block 0x%jx\n", 310212773Semaste (uintmax_t)physical); 31195350Sscottl return (EIO); 31283114Sscottl } 313116553Sscottl 314145811Sscottl bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap, 315145811Sscottl BUS_DMASYNC_POSTWRITE); 316145811Sscottl 317145811Sscottl bus_dmamap_unload(sc->aac_buffer_dmat, dump_datamap); 318145811Sscottl 31995350Sscottl length -= len; 32095350Sscottl offset += len; 321132771Skan virtual = (uint8_t *)virtual + len; 32283114Sscottl } 32382527Sscottl 32483114Sscottl return (0); 32582527Sscottl} 32682527Sscottl 32783114Sscottl/* 32865793Smsmith * Handle completion of an I/O request. 32965793Smsmith */ 33065793Smsmithvoid 33170393Smsmithaac_biodone(struct bio *bp) 33265793Smsmith{ 333177567Semaste fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); 33483114Sscottl 335240876Ssbruno if (bp->bio_flags & BIO_ERROR) { 336240876Ssbruno bp->bio_resid = bp->bio_bcount; 337103675Sphk disk_err(bp, "hard error", -1, 1); 338240876Ssbruno } 339111691Sscottl 34083114Sscottl biodone(bp); 34165793Smsmith} 34265793Smsmith 34383114Sscottl/* 34465793Smsmith * Stub only. 34565793Smsmith */ 34665793Smsmithstatic int 34765793Smsmithaac_disk_probe(device_t dev) 34865793Smsmith{ 34965793Smsmith 350177567Semaste fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); 35165793Smsmith 35283114Sscottl return (0); 35365793Smsmith} 35465793Smsmith 35583114Sscottl/* 35665793Smsmith * Attach a unit to the controller. 35765793Smsmith */ 35865793Smsmithstatic int 35965793Smsmithaac_disk_attach(device_t dev) 36065793Smsmith{ 36183114Sscottl struct aac_disk *sc; 36283114Sscottl 36383114Sscottl sc = (struct aac_disk *)device_get_softc(dev); 364177567Semaste fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); 36565793Smsmith 36683114Sscottl /* initialise our softc */ 36783114Sscottl sc->ad_controller = 36883114Sscottl (struct aac_softc *)device_get_softc(device_get_parent(dev)); 36983114Sscottl sc->ad_container = device_get_ivars(dev); 37083114Sscottl sc->ad_dev = dev; 37165793Smsmith 37283114Sscottl /* 37383114Sscottl * require that extended translation be enabled - other drivers read the 37483114Sscottl * disk! 37583114Sscottl */ 37683114Sscottl sc->ad_size = sc->ad_container->co_mntobj.Capacity; 377177619Semaste if (sc->ad_controller->flags & AAC_FLAGS_LBA_64BIT) 378177619Semaste sc->ad_size += (u_int64_t) 379177619Semaste sc->ad_container->co_mntobj.CapacityHigh << 32; 38083114Sscottl if (sc->ad_size >= (2 * 1024 * 1024)) { /* 2GB */ 38183114Sscottl sc->ad_heads = 255; 38283114Sscottl sc->ad_sectors = 63; 38383114Sscottl } else if (sc->ad_size >= (1 * 1024 * 1024)) { /* 1GB */ 38483114Sscottl sc->ad_heads = 128; 38583114Sscottl sc->ad_sectors = 32; 38683114Sscottl } else { 38783114Sscottl sc->ad_heads = 64; 38883114Sscottl sc->ad_sectors = 32; 38983114Sscottl } 39083114Sscottl sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors)); 39165793Smsmith 392177619Semaste device_printf(dev, "%juMB (%ju sectors)\n", 393177619Semaste (intmax_t)sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE), 394177619Semaste (intmax_t)sc->ad_size); 39565793Smsmith 39683114Sscottl /* attach a generic disk device to ourselves */ 39783114Sscottl sc->unit = device_get_unit(dev); 398125975Sphk sc->ad_disk = disk_alloc(); 399125975Sphk sc->ad_disk->d_drv1 = sc; 400251941Smarius sc->ad_disk->d_flags = DISKFLAG_UNMAPPED_BIO; 401125975Sphk sc->ad_disk->d_name = "aacd"; 402195614Sjkim sc->ad_disk->d_maxsize = sc->ad_controller->aac_max_sectors << 9; 403125975Sphk sc->ad_disk->d_open = aac_disk_open; 404125975Sphk sc->ad_disk->d_close = aac_disk_close; 405125975Sphk sc->ad_disk->d_strategy = aac_disk_strategy; 406125975Sphk sc->ad_disk->d_dump = aac_disk_dump; 407125975Sphk sc->ad_disk->d_sectorsize = AAC_BLOCK_SIZE; 408125975Sphk sc->ad_disk->d_mediasize = (off_t)sc->ad_size * AAC_BLOCK_SIZE; 409125975Sphk sc->ad_disk->d_fwsectors = sc->ad_sectors; 410125975Sphk sc->ad_disk->d_fwheads = sc->ad_heads; 411125975Sphk sc->ad_disk->d_unit = sc->unit; 412125975Sphk disk_create(sc->ad_disk, DISK_VERSION); 41381082Sscottl 41483114Sscottl return (0); 41565793Smsmith} 41665793Smsmith 41783114Sscottl/* 41865793Smsmith * Disconnect ourselves from the system. 41965793Smsmith */ 42065793Smsmithstatic int 42165793Smsmithaac_disk_detach(device_t dev) 42265793Smsmith{ 42383114Sscottl struct aac_disk *sc; 42465793Smsmith 42583114Sscottl sc = (struct aac_disk *)device_get_softc(dev); 426177567Semaste fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); 42765793Smsmith 42883114Sscottl if (sc->ad_flags & AAC_DISK_OPEN) 42983114Sscottl return(EBUSY); 43083114Sscottl 431125975Sphk disk_destroy(sc->ad_disk); 43265793Smsmith 43383114Sscottl return(0); 43465793Smsmith} 435