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