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