ld_aac.c revision 1.27 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