xref: /netbsd-src/sys/dev/ic/ld_icp.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: ld_icp.c,v 1.24 2010/11/13 13:52:01 uebayasi 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 /*
33  * ICP-Vortex "GDT" front-end for ld(4) driver.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ld_icp.c,v 1.24 2010/11/13 13:52:01 uebayasi Exp $");
38 
39 #include "rnd.h"
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/device.h>
45 #include <sys/buf.h>
46 #include <sys/bufq.h>
47 #include <sys/endian.h>
48 #include <sys/dkio.h>
49 #include <sys/disk.h>
50 #if NRND > 0
51 #include <sys/rnd.h>
52 #endif
53 
54 #include <sys/bus.h>
55 
56 #include <dev/ldvar.h>
57 
58 #include <dev/ic/icpreg.h>
59 #include <dev/ic/icpvar.h>
60 
61 struct ld_icp_softc {
62 	struct	ld_softc sc_ld;
63 	int	sc_hwunit;
64 };
65 
66 void	ld_icp_attach(device_t, device_t, void *);
67 int	ld_icp_detach(device_t, int);
68 int	ld_icp_dobio(struct ld_icp_softc *, void *, int, int, int,
69 		     struct buf *);
70 int	ld_icp_dump(struct ld_softc *, void *, int, int);
71 int	ld_icp_flush(struct ld_softc *, int);
72 void	ld_icp_intr(struct icp_ccb *);
73 int	ld_icp_match(device_t, cfdata_t, void *);
74 int	ld_icp_start(struct ld_softc *, struct buf *);
75 
76 void	ld_icp_adjqparam(device_t, int);
77 
78 CFATTACH_DECL_NEW(ld_icp, sizeof(struct ld_icp_softc),
79     ld_icp_match, ld_icp_attach, ld_icp_detach, NULL);
80 
81 static const struct icp_servicecb ld_icp_servicecb = {
82 	ld_icp_adjqparam,
83 };
84 
85 int
86 ld_icp_match(device_t parent, cfdata_t match, void *aux)
87 {
88 	struct icp_attach_args *icpa;
89 
90 	icpa = aux;
91 
92 	return (icpa->icpa_unit < ICPA_UNIT_SCSI);
93 }
94 
95 void
96 ld_icp_attach(device_t parent, device_t self, void *aux)
97 {
98 	struct icp_attach_args *icpa = aux;
99 	struct ld_icp_softc *sc = device_private(self);
100 	struct ld_softc *ld = &sc->sc_ld;
101 	struct icp_softc *icp = device_private(parent);
102 	struct icp_cachedrv *cd = &icp->icp_cdr[icpa->icpa_unit];
103 	struct icp_cdevinfo *cdi;
104 	const char *str;
105 	int t;
106 
107 	ld->sc_dv = self;
108 
109 	icp_register_servicecb(icp, icpa->icpa_unit, &ld_icp_servicecb);
110 
111 	sc->sc_hwunit = icpa->icpa_unit;
112 	ld->sc_maxxfer = ICP_MAX_XFER;
113 	ld->sc_secsize = ICP_SECTOR_SIZE;
114 	ld->sc_start = ld_icp_start;
115 	ld->sc_dump = ld_icp_dump;
116 	ld->sc_flush = ld_icp_flush;
117 	ld->sc_secperunit = cd->cd_size;
118 	ld->sc_flags = LDF_ENABLED;
119 	ld->sc_maxqueuecnt = icp->icp_openings;
120 
121 	if (!icp_cmd(icp, ICP_CACHESERVICE, ICP_IOCTL, ICP_CACHE_DRV_INFO,
122 	    sc->sc_hwunit, sizeof(struct icp_cdevinfo))) {
123 		aprint_error(": unable to retrieve device info\n");
124 		ld->sc_flags = LDF_ENABLED;
125 		goto out;
126 	}
127 	cdi = (struct icp_cdevinfo *)icp->icp_scr;
128 
129 	aprint_normal(": <%.8s>, ", cdi->ld_name);
130 	t = le32toh(cdi->ld_dtype) >> 16;
131 
132 	/*
133 	 * Print device type.
134 	 */
135 	if (le32toh(cdi->ld_dcnt) > 1 || le32toh(cdi->ld_slave) != -1)
136 		str = "RAID-1";
137 	else if (t == 0)
138 		str = "JBOD";
139 	else if (t == 1)
140 		str = "RAID-0";
141 	else if (t == 2)
142 		str = "Chain";
143 	else
144 		str = "unknown type";
145 
146 	aprint_normal("type: %s, ", str);
147 
148 	/*
149 	 * Print device status.
150 	 */
151 	if (t > 2)
152 		str = "missing";
153 	else if ((cdi->ld_error & 1) != 0) {
154 		str = "fault";
155 		ld->sc_flags = LDF_ENABLED;
156 	} else if ((cdi->ld_error & 2) != 0)
157 		str = "invalid";
158 	else {
159 		str = "optimal";
160 		ld->sc_flags = LDF_ENABLED;
161 	}
162 
163 	aprint_normal("status: %s\n", str);
164 
165  out:
166 	ldattach(ld);
167 }
168 
169 int
170 ld_icp_detach(device_t dv, int flags)
171 {
172 	int rv;
173 
174 	if ((rv = ldbegindetach((struct ld_softc *)dv, flags)) != 0)
175 		return (rv);
176 	ldenddetach((struct ld_softc *) dv);
177 
178 	return (0);
179 }
180 
181 int
182 ld_icp_dobio(struct ld_icp_softc *sc, void *data, int datasize, int blkno,
183 	     int dowrite, struct buf *bp)
184 {
185 	struct icp_cachecmd *cc;
186 	struct icp_ccb *ic;
187 	struct icp_softc *icp;
188 	int s, rv;
189 
190 	icp = device_private(device_parent(sc->sc_ld.sc_dv));
191 
192 	/*
193 	 * Allocate a command control block.
194 	 */
195 	if (__predict_false((ic = icp_ccb_alloc(icp)) == NULL))
196 		return (EAGAIN);
197 
198 	/*
199 	 * Map the data transfer.
200 	 */
201 	cc = &ic->ic_cmd.cmd_packet.cc;
202 	ic->ic_sg = cc->cc_sg;
203 	ic->ic_service = ICP_CACHESERVICE;
204 
205 	rv = icp_ccb_map(icp, ic, data, datasize,
206 	    dowrite ? IC_XFER_OUT : IC_XFER_IN);
207 	if (rv != 0) {
208 		icp_ccb_free(icp, ic);
209 		return (rv);
210 	}
211 
212 	/*
213 	 * Build the command.
214 	 */
215 	ic->ic_cmd.cmd_opcode = htole16((dowrite ? ICP_WRITE : ICP_READ));
216 	cc->cc_deviceno = htole16(sc->sc_hwunit);
217 	cc->cc_blockno = htole32(blkno);
218 	cc->cc_blockcnt = htole32(datasize / ICP_SECTOR_SIZE);
219 	cc->cc_addr = ~0;	/* scatter gather */
220 	cc->cc_nsgent = htole32(ic->ic_nsgent);
221 
222 	ic->ic_cmdlen = (u_long)ic->ic_sg - (u_long)&ic->ic_cmd +
223 	    ic->ic_nsgent * sizeof(*ic->ic_sg);
224 
225 	/*
226 	 * Fire it off to the controller.
227 	 */
228 	if (bp == NULL) {
229 		s = splbio();
230 		rv = icp_ccb_poll(icp, ic, 10000);
231 		icp_ccb_unmap(icp, ic);
232 		icp_ccb_free(icp, ic);
233 		splx(s);
234 	} else {
235  		ic->ic_intr = ld_icp_intr;
236 		ic->ic_context = bp;
237 		ic->ic_dv = sc->sc_ld.sc_dv;
238 		icp_ccb_enqueue(icp, ic);
239 	}
240 
241 	return (rv);
242 }
243 
244 int
245 ld_icp_start(struct ld_softc *ld, struct buf *bp)
246 {
247 
248 	return (ld_icp_dobio((struct ld_icp_softc *)ld, bp->b_data,
249 	    bp->b_bcount, bp->b_rawblkno, (bp->b_flags & B_READ) == 0, bp));
250 }
251 
252 int
253 ld_icp_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt)
254 {
255 
256 	return (ld_icp_dobio((struct ld_icp_softc *)ld, data,
257 	    blkcnt * ld->sc_secsize, blkno, 1, NULL));
258 }
259 
260 int
261 ld_icp_flush(struct ld_softc *ld, int flags)
262 {
263 	struct ld_icp_softc *sc;
264 	struct icp_softc *icp;
265 	struct icp_cachecmd *cc;
266 	struct icp_ccb *ic;
267 	int rv;
268 
269 	sc = (struct ld_icp_softc *)ld;
270 	icp = device_private(device_parent(ld->sc_dv));
271 
272 	ic = icp_ccb_alloc_wait(icp);
273 	ic->ic_cmd.cmd_opcode = htole16(ICP_FLUSH);
274 
275 	cc = &ic->ic_cmd.cmd_packet.cc;
276 	cc->cc_deviceno = htole16(sc->sc_hwunit);
277 	cc->cc_blockno = htole32(1);
278 	cc->cc_blockcnt = 0;
279 	cc->cc_addr = 0;
280 	cc->cc_nsgent = 0;
281 
282 	ic->ic_cmdlen = (u_long)&cc->cc_sg - (u_long)&ic->ic_cmd;
283 	ic->ic_service = ICP_CACHESERVICE;
284 
285 	rv = icp_ccb_wait(icp, ic, 30000);
286 	icp_ccb_free(icp, ic);
287 
288 	return (rv);
289 }
290 
291 void
292 ld_icp_intr(struct icp_ccb *ic)
293 {
294 	struct buf *bp;
295 	struct ld_icp_softc *sc;
296 	struct icp_softc *icp;
297 
298 	bp = ic->ic_context;
299 	sc = (struct ld_icp_softc *)device_private(ic->ic_dv);
300 	icp = device_private(device_parent(sc->sc_ld.sc_dv));
301 
302 	if (ic->ic_status != ICP_S_OK) {
303 		aprint_error_dev(ic->ic_dv, "request failed; status=0x%04x\n",
304 		    ic->ic_status);
305 		bp->b_error = EIO;
306 		bp->b_resid = bp->b_bcount;
307 
308 		icp->icp_evt.size = sizeof(icp->icp_evt.eu.sync);
309 		icp->icp_evt.eu.sync.ionode = device_unit(&icp->icp_dv);
310 		icp->icp_evt.eu.sync.service = icp->icp_service;
311 		icp->icp_evt.eu.sync.status = icp->icp_status;
312 		icp->icp_evt.eu.sync.info = icp->icp_info;
313 		icp->icp_evt.eu.sync.hostdrive = sc->sc_hwunit;
314 		if (icp->icp_status >= 0x8000)
315 			icp_store_event(icp, GDT_ES_SYNC, 0, &icp->icp_evt);
316 		else
317 			icp_store_event(icp, GDT_ES_SYNC, icp->icp_service,
318 			    &icp->icp_evt);
319 	} else
320 		bp->b_resid = 0;
321 
322 	icp_ccb_unmap(icp, ic);
323 	icp_ccb_free(icp, ic);
324 	lddone(&sc->sc_ld, bp);
325 }
326 
327 void
328 ld_icp_adjqparam(device_t dv, int openings)
329 {
330 
331 	ldadjqparam((struct ld_softc *) dv, openings);
332 }
333