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