1 /* $NetBSD: ld_aac.c,v 1.27 2012/10/27 17:18:21 chs 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.27 2012/10/27 17:18:21 chs Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/device.h> 39 #include <sys/buf.h> 40 #include <sys/bufq.h> 41 #include <sys/endian.h> 42 #include <sys/dkio.h> 43 #include <sys/disk.h> 44 #include <sys/rnd.h> 45 46 #include <sys/bus.h> 47 48 #include <dev/ldvar.h> 49 50 #include <dev/ic/aacreg.h> 51 #include <dev/ic/aacvar.h> 52 53 struct ld_aac_softc { 54 struct ld_softc sc_ld; 55 int sc_hwunit; 56 }; 57 58 static void ld_aac_attach(device_t, device_t, void *); 59 static void ld_aac_intr(struct aac_ccb *); 60 static int ld_aac_dobio(struct ld_aac_softc *, void *, int, daddr_t, int, 61 struct buf *); 62 static int ld_aac_dump(struct ld_softc *, void *, int, int); 63 static int ld_aac_match(device_t, cfdata_t, void *); 64 static int ld_aac_start(struct ld_softc *, struct buf *); 65 66 CFATTACH_DECL_NEW(ld_aac, sizeof(struct ld_aac_softc), 67 ld_aac_match, ld_aac_attach, NULL, NULL); 68 69 static int 70 ld_aac_match(device_t parent, cfdata_t match, void *aux) 71 { 72 73 return (1); 74 } 75 76 static void 77 ld_aac_attach(device_t parent, device_t self, void *aux) 78 { 79 struct aac_attach_args *aaca = aux; 80 struct ld_aac_softc *sc = device_private(self); 81 struct ld_softc *ld = &sc->sc_ld; 82 struct aac_softc *aac = device_private(parent); 83 struct aac_drive *hdr = &aac->sc_hdr[aaca->aaca_unit]; 84 85 ld->sc_dv = self; 86 87 sc->sc_hwunit = aaca->aaca_unit; 88 ld->sc_flags = LDF_ENABLED; 89 ld->sc_maxxfer = AAC_MAX_XFER(aac); 90 ld->sc_secperunit = hdr->hd_size; 91 ld->sc_secsize = AAC_SECTOR_SIZE; 92 ld->sc_maxqueuecnt = 93 (aac->sc_max_fibs - AAC_NCCBS_RESERVE) / aac->sc_nunits; 94 ld->sc_start = ld_aac_start; 95 ld->sc_dump = ld_aac_dump; 96 97 aprint_normal(": %s\n", 98 aac_describe_code(aac_container_types, hdr->hd_devtype)); 99 ldattach(ld); 100 } 101 102 static int 103 ld_aac_dobio(struct ld_aac_softc *sc, void *data, int datasize, daddr_t blkno, 104 int dowrite, struct buf *bp) 105 { 106 struct aac_blockread_response *brr; 107 struct aac_blockwrite_response *bwr; 108 struct aac_ccb *ac; 109 struct aac_softc *aac; 110 struct aac_fib *fib; 111 bus_dmamap_t xfer; 112 u_int32_t status; 113 u_int16_t size; 114 int s, rv, i; 115 116 aac = device_private(device_parent(sc->sc_ld.sc_dv)); 117 118 /* 119 * Allocate a command control block and map the data transfer. 120 */ 121 ac = aac_ccb_alloc(aac, (dowrite ? AAC_CCB_DATA_OUT : AAC_CCB_DATA_IN)); 122 if (ac == NULL) 123 return EBUSY; 124 ac->ac_data = data; 125 ac->ac_datalen = datasize; 126 127 if ((rv = aac_ccb_map(aac, ac)) != 0) { 128 aac_ccb_free(aac, ac); 129 return (rv); 130 } 131 132 /* 133 * Build the command. 134 */ 135 fib = ac->ac_fib; 136 137 fib->Header.XferState = htole32(AAC_FIBSTATE_HOSTOWNED | 138 AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_FROMHOST | 139 AAC_FIBSTATE_REXPECTED | AAC_FIBSTATE_NORM | 140 AAC_FIBSTATE_ASYNC | AAC_FIBSTATE_FAST_RESPONSE ); 141 142 if (aac->sc_quirks & AAC_QUIRK_RAW_IO) { 143 struct aac_raw_io *raw; 144 struct aac_sg_entryraw *sge; 145 struct aac_sg_tableraw *sgt; 146 147 raw = (struct aac_raw_io *)&fib->data[0]; 148 fib->Header.Command = htole16(RawIo); 149 raw->BlockNumber = htole64(blkno); 150 raw->ByteCount = htole32(datasize); 151 raw->ContainerId = htole16(sc->sc_hwunit); 152 raw->BpTotal = 0; 153 raw->BpComplete = 0; 154 size = sizeof(struct aac_raw_io); 155 sgt = &raw->SgMapRaw; 156 raw->Flags = (dowrite ? 0 : 1); 157 158 xfer = ac->ac_dmamap_xfer; 159 sgt->SgCount = xfer->dm_nsegs; 160 sge = sgt->SgEntryRaw; 161 162 for (i = 0; i < xfer->dm_nsegs; i++, sge++) { 163 sge->SgAddress = htole64(xfer->dm_segs[i].ds_addr); 164 sge->SgByteCount = htole32(xfer->dm_segs[i].ds_len); 165 sge->Next = 0; 166 sge->Prev = 0; 167 sge->Flags = 0; 168 } 169 size += xfer->dm_nsegs * sizeof(struct aac_sg_entryraw); 170 size = sizeof(fib->Header) + size; 171 fib->Header.Size = htole16(size); 172 } else if ((aac->sc_quirks & AAC_QUIRK_SG_64BIT) == 0) { 173 struct aac_blockread *br; 174 struct aac_blockwrite *bw; 175 struct aac_sg_entry *sge; 176 struct aac_sg_table *sgt; 177 178 fib->Header.Command = htole16(ContainerCommand); 179 if (dowrite) { 180 bw = (struct aac_blockwrite *)&fib->data[0]; 181 bw->Command = htole32(VM_CtBlockWrite); 182 bw->ContainerId = htole32(sc->sc_hwunit); 183 bw->BlockNumber = htole32(blkno); 184 bw->ByteCount = htole32(datasize); 185 bw->Stable = htole32(CUNSTABLE); 186 /* CSTABLE sometimes? FUA? */ 187 188 size = sizeof(struct aac_blockwrite); 189 sgt = &bw->SgMap; 190 } else { 191 br = (struct aac_blockread *)&fib->data[0]; 192 br->Command = htole32(VM_CtBlockRead); 193 br->ContainerId = htole32(sc->sc_hwunit); 194 br->BlockNumber = htole32(blkno); 195 br->ByteCount = htole32(datasize); 196 197 size = sizeof(struct aac_blockread); 198 sgt = &br->SgMap; 199 } 200 201 xfer = ac->ac_dmamap_xfer; 202 sgt->SgCount = xfer->dm_nsegs; 203 sge = sgt->SgEntry; 204 205 for (i = 0; i < xfer->dm_nsegs; i++, sge++) { 206 sge->SgAddress = htole32(xfer->dm_segs[i].ds_addr); 207 sge->SgByteCount = htole32(xfer->dm_segs[i].ds_len); 208 AAC_DPRINTF(AAC_D_IO, 209 ("#%d va %p pa %" PRIxPADDR " len %zx\n", 210 i, data, xfer->dm_segs[i].ds_addr, 211 xfer->dm_segs[i].ds_len)); 212 } 213 214 size += xfer->dm_nsegs * sizeof(struct aac_sg_entry); 215 size = sizeof(fib->Header) + size; 216 fib->Header.Size = htole16(size); 217 } else { 218 struct aac_blockread64 *br; 219 struct aac_blockwrite64 *bw; 220 struct aac_sg_entry64 *sge; 221 struct aac_sg_table64 *sgt; 222 223 fib->Header.Command = htole16(ContainerCommand64); 224 if (dowrite) { 225 bw = (struct aac_blockwrite64 *)&fib->data[0]; 226 bw->Command = htole32(VM_CtHostWrite64); 227 bw->BlockNumber = htole32(blkno); 228 bw->ContainerId = htole16(sc->sc_hwunit); 229 bw->SectorCount = htole16(datasize / AAC_BLOCK_SIZE); 230 bw->Pad = 0; 231 bw->Flags = 0; 232 233 size = sizeof(struct aac_blockwrite64); 234 sgt = &bw->SgMap64; 235 } else { 236 br = (struct aac_blockread64 *)&fib->data[0]; 237 br->Command = htole32(VM_CtHostRead64); 238 br->BlockNumber = htole32(blkno); 239 br->ContainerId = htole16(sc->sc_hwunit); 240 br->SectorCount = htole16(datasize / AAC_BLOCK_SIZE); 241 br->Pad = 0; 242 br->Flags = 0; 243 244 size = sizeof(struct aac_blockread64); 245 sgt = &br->SgMap64; 246 } 247 248 xfer = ac->ac_dmamap_xfer; 249 sgt->SgCount = xfer->dm_nsegs; 250 sge = sgt->SgEntry64; 251 252 for (i = 0; i < xfer->dm_nsegs; i++, sge++) { 253 /* 254 * XXX - This is probably an alignment issue on non-x86 255 * platforms since this is a packed array of 64/32-bit 256 * tuples, so every other SgAddress is 32-bit, but not 257 * 64-bit aligned. 258 */ 259 sge->SgAddress = htole64(xfer->dm_segs[i].ds_addr); 260 sge->SgByteCount = htole32(xfer->dm_segs[i].ds_len); 261 AAC_DPRINTF(AAC_D_IO, 262 ("#%d va %p pa %" PRIxPADDR " len %zx\n", 263 i, data, xfer->dm_segs[i].ds_addr, 264 xfer->dm_segs[i].ds_len)); 265 } 266 size += xfer->dm_nsegs * sizeof(struct aac_sg_entry64); 267 size = sizeof(fib->Header) + size; 268 fib->Header.Size = htole16(size); 269 } 270 271 if (bp == NULL) { 272 /* 273 * Polled commands must not sit on the software queue. Wait 274 * up to 30 seconds for the command to complete. 275 */ 276 s = splbio(); 277 rv = aac_ccb_poll(aac, ac, 30000); 278 aac_ccb_unmap(aac, ac); 279 aac_ccb_free(aac, ac); 280 splx(s); 281 282 if (rv == 0) { 283 if (dowrite) { 284 bwr = (struct aac_blockwrite_response *) 285 &ac->ac_fib->data[0]; 286 status = le32toh(bwr->Status); 287 } else { 288 brr = (struct aac_blockread_response *) 289 &ac->ac_fib->data[0]; 290 status = le32toh(brr->Status); 291 } 292 293 if (status != ST_OK) { 294 aprint_error_dev(sc->sc_ld.sc_dv, 295 "I/O error: %s\n", 296 aac_describe_code(aac_command_status_table, 297 status)); 298 rv = EIO; 299 } 300 } 301 } else { 302 ac->ac_device = sc->sc_ld.sc_dv; 303 ac->ac_context = bp; 304 ac->ac_intr = ld_aac_intr; 305 aac_ccb_enqueue(aac, ac); 306 rv = 0; 307 } 308 309 return (rv); 310 } 311 312 static int 313 ld_aac_start(struct ld_softc *ld, struct buf *bp) 314 { 315 316 return (ld_aac_dobio((struct ld_aac_softc *)ld, bp->b_data, 317 bp->b_bcount, bp->b_rawblkno, (bp->b_flags & B_READ) == 0, bp)); 318 } 319 320 static void 321 ld_aac_intr(struct aac_ccb *ac) 322 { 323 struct aac_blockread_response *brr; 324 struct aac_blockwrite_response *bwr; 325 struct ld_aac_softc *sc; 326 struct aac_softc *aac; 327 struct buf *bp; 328 u_int32_t status; 329 330 bp = ac->ac_context; 331 sc = device_private(ac->ac_device); 332 aac = device_private(device_parent(ac->ac_device)); 333 334 if ((bp->b_flags & B_READ) != 0) { 335 brr = (struct aac_blockread_response *)&ac->ac_fib->data[0]; 336 status = le32toh(brr->Status); 337 } else { 338 bwr = (struct aac_blockwrite_response *)&ac->ac_fib->data[0]; 339 status = le32toh(bwr->Status); 340 } 341 342 aac_ccb_unmap(aac, ac); 343 aac_ccb_free(aac, ac); 344 345 if (status != ST_OK) { 346 bp->b_error = EIO; 347 bp->b_resid = bp->b_bcount; 348 349 aprint_error_dev(sc->sc_ld.sc_dv, "I/O error: %s\n", 350 aac_describe_code(aac_command_status_table, status)); 351 } else 352 bp->b_resid = 0; 353 354 lddone(&sc->sc_ld, bp); 355 } 356 357 static int 358 ld_aac_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt) 359 { 360 361 return (ld_aac_dobio((struct ld_aac_softc *)ld, data, 362 blkcnt * ld->sc_secsize, blkno, 1, NULL)); 363 } 364