1 /* $NetBSD: ld_aac.c,v 1.24 2010/07/27 19:31:55 jakllsch Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: ld_aac.c,v 1.24 2010/07/27 19:31:55 jakllsch Exp $"); 34 35 #include "rnd.h" 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/device.h> 41 #include <sys/buf.h> 42 #include <sys/bufq.h> 43 #include <sys/endian.h> 44 #include <sys/dkio.h> 45 #include <sys/disk.h> 46 #if NRND > 0 47 #include <sys/rnd.h> 48 #endif 49 50 #include <sys/bus.h> 51 52 #include <uvm/uvm_extern.h> 53 54 #include <dev/ldvar.h> 55 56 #include <dev/ic/aacreg.h> 57 #include <dev/ic/aacvar.h> 58 59 struct ld_aac_softc { 60 struct ld_softc sc_ld; 61 int sc_hwunit; 62 }; 63 64 static void ld_aac_attach(device_t, device_t, void *); 65 static void ld_aac_intr(struct aac_ccb *); 66 static int ld_aac_dobio(struct ld_aac_softc *, void *, int, daddr_t, int, 67 struct buf *); 68 static int ld_aac_dump(struct ld_softc *, void *, int, int); 69 static int ld_aac_match(device_t, cfdata_t, void *); 70 static int ld_aac_start(struct ld_softc *, struct buf *); 71 72 CFATTACH_DECL_NEW(ld_aac, sizeof(struct ld_aac_softc), 73 ld_aac_match, ld_aac_attach, NULL, NULL); 74 75 static int 76 ld_aac_match(device_t parent, cfdata_t match, void *aux) 77 { 78 79 return (1); 80 } 81 82 static void 83 ld_aac_attach(device_t parent, device_t self, void *aux) 84 { 85 struct aac_attach_args *aaca = aux; 86 struct ld_aac_softc *sc = device_private(self); 87 struct ld_softc *ld = &sc->sc_ld; 88 struct aac_softc *aac = device_private(parent); 89 struct aac_drive *hdr = &aac->sc_hdr[aaca->aaca_unit]; 90 91 ld->sc_dv = self; 92 93 sc->sc_hwunit = aaca->aaca_unit; 94 ld->sc_flags = LDF_ENABLED; 95 ld->sc_maxxfer = AAC_MAX_XFER(aac); 96 ld->sc_secperunit = hdr->hd_size; 97 ld->sc_secsize = AAC_SECTOR_SIZE; 98 ld->sc_maxqueuecnt = 99 (aac->sc_max_fibs - AAC_NCCBS_RESERVE) / aac->sc_nunits; 100 ld->sc_start = ld_aac_start; 101 ld->sc_dump = ld_aac_dump; 102 103 aprint_normal(": %s\n", 104 aac_describe_code(aac_container_types, hdr->hd_devtype)); 105 ldattach(ld); 106 } 107 108 static int 109 ld_aac_dobio(struct ld_aac_softc *sc, void *data, int datasize, daddr_t blkno, 110 int dowrite, struct buf *bp) 111 { 112 struct aac_blockread_response *brr; 113 struct aac_blockwrite_response *bwr; 114 struct aac_ccb *ac; 115 struct aac_softc *aac; 116 struct aac_fib *fib; 117 bus_dmamap_t xfer; 118 u_int32_t status; 119 u_int16_t size; 120 int s, rv, i; 121 122 aac = device_private(device_parent(sc->sc_ld.sc_dv)); 123 124 /* 125 * Allocate a command control block and map the data transfer. 126 */ 127 ac = aac_ccb_alloc(aac, (dowrite ? AAC_CCB_DATA_OUT : AAC_CCB_DATA_IN)); 128 if (ac == NULL) 129 return EBUSY; 130 ac->ac_data = data; 131 ac->ac_datalen = datasize; 132 133 if ((rv = aac_ccb_map(aac, ac)) != 0) { 134 aac_ccb_free(aac, ac); 135 return (rv); 136 } 137 138 /* 139 * Build the command. 140 */ 141 fib = ac->ac_fib; 142 143 fib->Header.XferState = htole32(AAC_FIBSTATE_HOSTOWNED | 144 AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_FROMHOST | 145 AAC_FIBSTATE_REXPECTED | AAC_FIBSTATE_NORM | 146 AAC_FIBSTATE_ASYNC | AAC_FIBSTATE_FAST_RESPONSE ); 147 148 if (aac->sc_quirks & AAC_QUIRK_RAW_IO) { 149 struct aac_raw_io *raw; 150 struct aac_sg_entryraw *sge; 151 struct aac_sg_tableraw *sgt; 152 153 raw = (struct aac_raw_io *)&fib->data[0]; 154 fib->Header.Command = htole16(RawIo); 155 raw->BlockNumber = htole64(blkno); 156 raw->ByteCount = htole32(datasize); 157 raw->ContainerId = htole16(sc->sc_hwunit); 158 raw->BpTotal = 0; 159 raw->BpComplete = 0; 160 size = sizeof(struct aac_raw_io); 161 sgt = &raw->SgMapRaw; 162 raw->Flags = (dowrite ? 0 : 1); 163 164 xfer = ac->ac_dmamap_xfer; 165 sgt->SgCount = xfer->dm_nsegs; 166 sge = sgt->SgEntryRaw; 167 168 for (i = 0; i < xfer->dm_nsegs; i++, sge++) { 169 sge->SgAddress = htole64(xfer->dm_segs[i].ds_addr); 170 sge->SgByteCount = htole32(xfer->dm_segs[i].ds_len); 171 sge->Next = 0; 172 sge->Prev = 0; 173 sge->Flags = 0; 174 } 175 size += xfer->dm_nsegs * sizeof(struct aac_sg_entryraw); 176 size = sizeof(fib->Header) + size; 177 fib->Header.Size = htole16(size); 178 } else if ((aac->sc_quirks & AAC_QUIRK_SG_64BIT) == 0) { 179 struct aac_blockread *br; 180 struct aac_blockwrite *bw; 181 struct aac_sg_entry *sge; 182 struct aac_sg_table *sgt; 183 184 fib->Header.Command = htole16(ContainerCommand); 185 if (dowrite) { 186 bw = (struct aac_blockwrite *)&fib->data[0]; 187 bw->Command = htole32(VM_CtBlockWrite); 188 bw->ContainerId = htole32(sc->sc_hwunit); 189 bw->BlockNumber = htole32(blkno); 190 bw->ByteCount = htole32(datasize); 191 bw->Stable = htole32(CUNSTABLE); 192 /* CSTABLE sometimes? FUA? */ 193 194 size = sizeof(struct aac_blockwrite); 195 sgt = &bw->SgMap; 196 } else { 197 br = (struct aac_blockread *)&fib->data[0]; 198 br->Command = htole32(VM_CtBlockRead); 199 br->ContainerId = htole32(sc->sc_hwunit); 200 br->BlockNumber = htole32(blkno); 201 br->ByteCount = htole32(datasize); 202 203 size = sizeof(struct aac_blockread); 204 sgt = &br->SgMap; 205 } 206 207 xfer = ac->ac_dmamap_xfer; 208 sgt->SgCount = xfer->dm_nsegs; 209 sge = sgt->SgEntry; 210 211 for (i = 0; i < xfer->dm_nsegs; i++, sge++) { 212 sge->SgAddress = htole32(xfer->dm_segs[i].ds_addr); 213 sge->SgByteCount = htole32(xfer->dm_segs[i].ds_len); 214 AAC_DPRINTF(AAC_D_IO, 215 ("#%d va %p pa %" PRIxPADDR " len %zx\n", 216 i, data, xfer->dm_segs[i].ds_addr, 217 xfer->dm_segs[i].ds_len)); 218 } 219 220 size += xfer->dm_nsegs * sizeof(struct aac_sg_entry); 221 size = sizeof(fib->Header) + size; 222 fib->Header.Size = htole16(size); 223 } else { 224 struct aac_blockread64 *br; 225 struct aac_blockwrite64 *bw; 226 struct aac_sg_entry64 *sge; 227 struct aac_sg_table64 *sgt; 228 229 fib->Header.Command = htole16(ContainerCommand64); 230 if (dowrite) { 231 bw = (struct aac_blockwrite64 *)&fib->data[0]; 232 bw->Command = htole32(VM_CtHostWrite64); 233 bw->BlockNumber = htole32(blkno); 234 bw->ContainerId = htole16(sc->sc_hwunit); 235 bw->SectorCount = htole16(datasize / AAC_BLOCK_SIZE); 236 bw->Pad = 0; 237 bw->Flags = 0; 238 239 size = sizeof(struct aac_blockwrite64); 240 sgt = &bw->SgMap64; 241 } else { 242 br = (struct aac_blockread64 *)&fib->data[0]; 243 br->Command = htole32(VM_CtHostRead64); 244 br->BlockNumber = htole32(blkno); 245 br->ContainerId = htole16(sc->sc_hwunit); 246 br->SectorCount = htole16(datasize / AAC_BLOCK_SIZE); 247 br->Pad = 0; 248 br->Flags = 0; 249 250 size = sizeof(struct aac_blockread64); 251 sgt = &br->SgMap64; 252 } 253 254 xfer = ac->ac_dmamap_xfer; 255 sgt->SgCount = xfer->dm_nsegs; 256 sge = sgt->SgEntry64; 257 258 for (i = 0; i < xfer->dm_nsegs; i++, sge++) { 259 /* 260 * XXX - This is probably an alignment issue on non-x86 261 * platforms since this is a packed array of 64/32-bit 262 * tuples, so every other SgAddress is 32-bit, but not 263 * 64-bit aligned. 264 */ 265 sge->SgAddress = htole64(xfer->dm_segs[i].ds_addr); 266 sge->SgByteCount = htole32(xfer->dm_segs[i].ds_len); 267 AAC_DPRINTF(AAC_D_IO, 268 ("#%d va %p pa %" PRIxPADDR " len %zx\n", 269 i, data, xfer->dm_segs[i].ds_addr, 270 xfer->dm_segs[i].ds_len)); 271 } 272 size += xfer->dm_nsegs * sizeof(struct aac_sg_entry64); 273 size = sizeof(fib->Header) + size; 274 fib->Header.Size = htole16(size); 275 } 276 277 if (bp == NULL) { 278 /* 279 * Polled commands must not sit on the software queue. Wait 280 * up to 30 seconds for the command to complete. 281 */ 282 s = splbio(); 283 rv = aac_ccb_poll(aac, ac, 30000); 284 aac_ccb_unmap(aac, ac); 285 aac_ccb_free(aac, ac); 286 splx(s); 287 288 if (rv == 0) { 289 if (dowrite) { 290 bwr = (struct aac_blockwrite_response *) 291 &ac->ac_fib->data[0]; 292 status = le32toh(bwr->Status); 293 } else { 294 brr = (struct aac_blockread_response *) 295 &ac->ac_fib->data[0]; 296 status = le32toh(brr->Status); 297 } 298 299 if (status != ST_OK) { 300 aprint_error_dev(sc->sc_ld.sc_dv, 301 "I/O error: %s\n", 302 aac_describe_code(aac_command_status_table, 303 status)); 304 rv = EIO; 305 } 306 } 307 } else { 308 ac->ac_device = (device_t)sc; 309 ac->ac_context = bp; 310 ac->ac_intr = ld_aac_intr; 311 aac_ccb_enqueue(aac, ac); 312 rv = 0; 313 } 314 315 return (rv); 316 } 317 318 static int 319 ld_aac_start(struct ld_softc *ld, struct buf *bp) 320 { 321 322 return (ld_aac_dobio((struct ld_aac_softc *)ld, bp->b_data, 323 bp->b_bcount, bp->b_rawblkno, (bp->b_flags & B_READ) == 0, bp)); 324 } 325 326 static void 327 ld_aac_intr(struct aac_ccb *ac) 328 { 329 struct aac_blockread_response *brr; 330 struct aac_blockwrite_response *bwr; 331 struct ld_aac_softc *sc; 332 struct aac_softc *aac; 333 struct buf *bp; 334 u_int32_t status; 335 336 bp = ac->ac_context; 337 sc = (struct ld_aac_softc *)ac->ac_device; 338 aac = device_private(device_parent(sc->sc_ld.sc_dv)); 339 340 if ((bp->b_flags & B_READ) != 0) { 341 brr = (struct aac_blockread_response *)&ac->ac_fib->data[0]; 342 status = le32toh(brr->Status); 343 } else { 344 bwr = (struct aac_blockwrite_response *)&ac->ac_fib->data[0]; 345 status = le32toh(bwr->Status); 346 } 347 348 aac_ccb_unmap(aac, ac); 349 aac_ccb_free(aac, ac); 350 351 if (status != ST_OK) { 352 bp->b_error = EIO; 353 bp->b_resid = bp->b_bcount; 354 355 aprint_error_dev(sc->sc_ld.sc_dv, "I/O error: %s\n", 356 aac_describe_code(aac_command_status_table, status)); 357 } else 358 bp->b_resid = 0; 359 360 lddone(&sc->sc_ld, bp); 361 } 362 363 static int 364 ld_aac_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt) 365 { 366 367 return (ld_aac_dobio((struct ld_aac_softc *)ld, data, 368 blkcnt * ld->sc_secsize, blkno, 1, NULL)); 369 } 370