xref: /netbsd-src/sys/arch/hp300/stand/common/scsi.c (revision 27f0c681499e80e47617c3d2f851ec260fd1cf5a)
1 /*	$NetBSD: scsi.c,v 1.13 2024/05/09 15:11:11 tsutsui Exp $	*/
2 
3 /*
4  * This is reported to fix some odd failures when disklabeling
5  * SCSI disks in SYS_INST.
6  */
7 #define SLOWSCSI
8 
9 /*
10  * Copyright (c) 1988 University of Utah.
11  * Copyright (c) 1990, 1993
12  *	The Regents of the University of California.  All rights reserved.
13  *
14  * This code is derived from software contributed to Berkeley by
15  * Van Jacobson of Lawrence Berkeley Laboratory and the Systems
16  * Programming Group of the University of Utah Computer Science Department.
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
21  * 1. Redistributions of source code must retain the above copyright
22  *    notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  * 3. Neither the name of the University nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  *
42  * from: Utah $Hdr: scsi.c 1.3 90/01/27$
43  *
44  *	@(#)scsi.c	8.1 (Berkeley) 6/10/93
45  */
46 
47 /*
48  * SCSI bus driver for standalone programs.
49  */
50 
51 #include <sys/param.h>
52 #include <sys/reboot.h>
53 
54 #include <lib/libsa/stand.h>
55 
56 #define _IOCTL_
57 
58 #include <hp300/stand/common/device.h>
59 #include <hp300/stand/common/scsireg.h>
60 #include <hp300/stand/common/scsivar.h>
61 #include <hp300/stand/common/samachdep.h>
62 
63 static void scsireset(int);
64 static int issue_select(volatile struct scsidevice *, uint8_t, uint8_t);
65 static int wait_for_select(volatile struct scsidevice *hd);
66 static int ixfer_start(volatile struct scsidevice *, int, uint8_t, int);
67 static int ixfer_out(volatile struct scsidevice *, int, uint8_t *);
68 static int ixfer_in(volatile struct scsidevice *hd, int, uint8_t *);
69 static int scsiicmd(struct scsi_softc *, int, uint8_t *, int, uint8_t *, int,
70     uint8_t);
71 
72 struct	scsi_softc scsi_softc[NSCSI];
73 
74 
75 int scsi_cmd_wait = 50000;	/* use the "real" driver init_wait value */
76 int scsi_data_wait = 50000;	/* use the "real" driver init_wait value */
77 
78 void
scsiinit(void)79 scsiinit(void)
80 {
81 	struct hp_hw *hw;
82 	struct scsi_softc *hs;
83 	int i;
84 	static int waitset = 0;
85 
86 	i = 0;
87 	for (hw = sc_table; i < NSCSI && hw < &sc_table[MAXCTLRS]; hw++) {
88 		if (!HW_ISSCSI(hw))
89 			continue;
90 		hs = &scsi_softc[i];
91 		hs->sc_addr = hw->hw_kva;
92 		scsireset(i);
93 		if (howto & RB_ASKNAME)
94 			printf("scsi%d at sc%d\n", i, hw->hw_sc);
95 		hw->hw_pa = (void *) i;	/* XXX for autoconfig */
96 		hs->sc_alive = 1;
97 		i++;
98 	}
99 	/*
100 	 * Adjust the wait values
101 	 */
102 	if (!waitset) {
103 		scsi_cmd_wait *= cpuspeed;
104 		scsi_data_wait *= cpuspeed;
105 		waitset = 1;
106 	}
107 }
108 
109 int
scsialive(int unit)110 scsialive(int unit)
111 {
112 
113 	if (unit >= NSCSI || scsi_softc[unit].sc_alive == 0)
114 		return 0;
115 	return 1;
116 }
117 
118 static void
scsireset(int unit)119 scsireset(int unit)
120 {
121 	volatile struct scsidevice *hd;
122 	struct scsi_softc *hs;
123 	u_int i;
124 
125 	hs = &scsi_softc[unit];
126 	hd = (void *)hs->sc_addr;
127 	hd->scsi_id = 0xFF;
128 	DELAY(100);
129 	/*
130 	 * Disable interrupts then reset the FUJI chip.
131 	 */
132 	hd->scsi_csr  = 0;
133 	hd->scsi_sctl = SCTL_DISABLE | SCTL_CTRLRST;
134 	hd->scsi_scmd = 0;
135 	hd->scsi_tmod = 0;
136 	hd->scsi_pctl = 0;
137 	hd->scsi_temp = 0;
138 	hd->scsi_tch  = 0;
139 	hd->scsi_tcm  = 0;
140 	hd->scsi_tcl  = 0;
141 	hd->scsi_ints = 0;
142 
143 	/*
144 	 * Configure the FUJI chip with its SCSI address, all
145 	 * interrupts enabled & appropriate parity.
146 	 */
147 	i = (~hd->scsi_hconf) & 0x7;
148 	hs->sc_scsi_addr = 1 << i;
149 	hd->scsi_bdid = i;
150 	if (hd->scsi_hconf & HCONF_PARITY)
151 		hd->scsi_sctl = SCTL_DISABLE | SCTL_ABRT_ENAB |
152 				SCTL_SEL_ENAB | SCTL_RESEL_ENAB |
153 				SCTL_INTR_ENAB | SCTL_PARITY_ENAB;
154 	else
155 		hd->scsi_sctl = SCTL_DISABLE | SCTL_ABRT_ENAB |
156 				SCTL_SEL_ENAB | SCTL_RESEL_ENAB |
157 				SCTL_INTR_ENAB;
158 	hd->scsi_sctl &=~ SCTL_DISABLE;
159 }
160 
161 
162 void
scsiabort(struct scsi_softc * hs,volatile struct scsidevice * hd)163 scsiabort(struct scsi_softc *hs, volatile struct scsidevice *hd)
164 {
165 
166 	printf("scsi%d error: scsiabort\n", hs - scsi_softc);
167 
168 	scsireset(hs - scsi_softc);
169 	DELAY(1000000);
170 }
171 
172 static int
issue_select(volatile struct scsidevice * hd,uint8_t target,uint8_t our_addr)173 issue_select(volatile struct scsidevice *hd, uint8_t target, uint8_t our_addr)
174 {
175 
176 	if (hd->scsi_ssts & (SSTS_INITIATOR|SSTS_TARGET|SSTS_BUSY))
177 		return 1;
178 
179 	if (hd->scsi_ints & INTS_DISCON)
180 		hd->scsi_ints = INTS_DISCON;
181 
182 	hd->scsi_pctl = 0;
183 	hd->scsi_temp = (1 << target) | our_addr;
184 	/* select timeout is hardcoded to 250ms */
185 	hd->scsi_tch = 2;
186 	hd->scsi_tcm = 113;
187 	hd->scsi_tcl = 3;
188 
189 	hd->scsi_scmd = SCMD_SELECT;
190 	return 0;
191 }
192 
193 static int
wait_for_select(volatile struct scsidevice * hd)194 wait_for_select(volatile struct scsidevice *hd)
195 {
196 	int wait;
197 	uint8_t ints;
198 
199 	wait = scsi_data_wait;
200 	while ((ints = hd->scsi_ints) == 0) {
201 		if (--wait < 0)
202 			return 1;
203 		DELAY(1);
204 	}
205 	hd->scsi_ints = ints;
206 	return !(hd->scsi_ssts & SSTS_INITIATOR);
207 }
208 
209 static int
ixfer_start(volatile struct scsidevice * hd,int len,uint8_t phase,int wait)210 ixfer_start(volatile struct scsidevice *hd, int len, uint8_t phase, int wait)
211 {
212 
213 	hd->scsi_tch = len >> 16;
214 	hd->scsi_tcm = len >> 8;
215 	hd->scsi_tcl = len;
216 	hd->scsi_pctl = phase;
217 	hd->scsi_tmod = 0; /*XXX*/
218 	hd->scsi_scmd = SCMD_XFR | SCMD_PROG_XFR;
219 
220 	/* wait for xfer to start or svc_req interrupt */
221 	while ((hd->scsi_ssts & SSTS_BUSY) == 0) {
222 		if (hd->scsi_ints || --wait < 0)
223 			return 0;
224 		DELAY(1);
225 	}
226 	return 1;
227 }
228 
229 static int
ixfer_out(volatile struct scsidevice * hd,int len,uint8_t * buf)230 ixfer_out(volatile struct scsidevice *hd, int len, uint8_t *buf)
231 {
232 	int wait = scsi_data_wait;
233 
234 	for (; len > 0; --len) {
235 		while (hd->scsi_ssts & SSTS_DREG_FULL) {
236 			if (hd->scsi_ints || --wait < 0)
237 				return len;
238 			DELAY(1);
239 		}
240 		hd->scsi_dreg = *buf++;
241 	}
242 	return 0;
243 }
244 
245 static int
ixfer_in(volatile struct scsidevice * hd,int len,uint8_t * buf)246 ixfer_in(volatile struct scsidevice *hd, int len, uint8_t *buf)
247 {
248 	int wait = scsi_data_wait;
249 
250 	for (; len > 0; --len) {
251 		while (hd->scsi_ssts & SSTS_DREG_EMPTY) {
252 			if (hd->scsi_ints || --wait < 0) {
253 				while (! (hd->scsi_ssts & SSTS_DREG_EMPTY)) {
254 					*buf++ = hd->scsi_dreg;
255 					--len;
256 				}
257 				return len;
258 			}
259 			DELAY(1);
260 		}
261 		*buf++ = hd->scsi_dreg;
262 	}
263 	return len;
264 }
265 
266 static int
scsiicmd(struct scsi_softc * hs,int target,uint8_t * cbuf,int clen,uint8_t * buf,int len,uint8_t xferphase)267 scsiicmd(struct scsi_softc *hs, int target, uint8_t *cbuf, int clen,
268     uint8_t *buf, int len, uint8_t xferphase)
269 {
270 	volatile struct scsidevice *hd = (void *)hs->sc_addr;
271 	uint8_t phase, ints;
272 	int wait;
273 
274 	/* select the SCSI bus (it's an error if bus isn't free) */
275 	if (issue_select(hd, target, hs->sc_scsi_addr))
276 		return -2;
277 	if (wait_for_select(hd))
278 		return -2;
279 	/*
280 	 * Wait for a phase change (or error) then let the device
281 	 * sequence us through the various SCSI phases.
282 	 */
283 	hs->sc_stat = -1;
284 	phase = CMD_PHASE;
285 	while (1) {
286 		wait = scsi_cmd_wait;
287 		switch (phase) {
288 
289 		case CMD_PHASE:
290 			if (ixfer_start(hd, clen, phase, wait))
291 				if (ixfer_out(hd, clen, cbuf))
292 					goto abort;
293 			phase = xferphase;
294 			break;
295 
296 		case DATA_IN_PHASE:
297 			if (len <= 0)
298 				goto abort;
299 			wait = scsi_data_wait;
300 			if (ixfer_start(hd, len, phase, wait) ||
301 			    !(hd->scsi_ssts & SSTS_DREG_EMPTY))
302 				ixfer_in(hd, len, buf);
303 			phase = STATUS_PHASE;
304 			break;
305 
306 		case DATA_OUT_PHASE:
307 			if (len <= 0)
308 				goto abort;
309 			wait = scsi_data_wait;
310 			if (ixfer_start(hd, len, phase, wait))
311 				if (ixfer_out(hd, len, buf))
312 					goto abort;
313 			phase = STATUS_PHASE;
314 			break;
315 
316 		case STATUS_PHASE:
317 			wait = scsi_data_wait;
318 			if (ixfer_start(hd, sizeof(hs->sc_stat), phase, wait) ||
319 			    !(hd->scsi_ssts & SSTS_DREG_EMPTY))
320 				ixfer_in(hd, sizeof(hs->sc_stat),
321 				    (uint8_t *)&hs->sc_stat);
322 			phase = MESG_IN_PHASE;
323 			break;
324 
325 		case MESG_IN_PHASE:
326 			if (ixfer_start(hd, sizeof(hs->sc_msg), phase, wait) ||
327 			    !(hd->scsi_ssts & SSTS_DREG_EMPTY)) {
328 				ixfer_in(hd, sizeof(hs->sc_msg),
329 				    (uint8_t *)&hs->sc_msg);
330 				hd->scsi_scmd = SCMD_RST_ACK;
331 			}
332 			phase = BUS_FREE_PHASE;
333 			break;
334 
335 		case BUS_FREE_PHASE:
336 			goto out;
337 
338 		default:
339 			printf("scsi%d: unexpected scsi phase %d\n",
340 			       hs - scsi_softc, phase);
341 			goto abort;
342 		}
343 #ifdef SLOWSCSI
344 		/*
345 		 * XXX we have weird transient problems with booting from
346 		 * slow scsi disks on fast machines.  I have never been
347 		 * able to pin the problem down, but a large delay here
348 		 * seems to always work.
349 		 */
350 		DELAY(1000);
351 #endif
352 		/* wait for last command to complete */
353 		while ((ints = hd->scsi_ints) == 0) {
354 			if (--wait < 0)
355 				goto abort;
356 			DELAY(1);
357 		}
358 		hd->scsi_ints = ints;
359 		if (ints & INTS_SRV_REQ)
360 			phase = hd->scsi_psns & PHASE;
361 		else if (ints & INTS_DISCON)
362 			goto out;
363 		else if ((ints & INTS_CMD_DONE) == 0)
364 			goto abort;
365 	}
366 abort:
367 	scsiabort(hs, hd);
368 out:
369 	return hs->sc_stat;
370 }
371 
372 int
scsi_test_unit_rdy(int ctlr,int slave)373 scsi_test_unit_rdy(int ctlr, int slave)
374 {
375 	struct scsi_softc *hs = &scsi_softc[ctlr];
376 	static struct scsi_cdb6 cdb = { CMD_TEST_UNIT_READY };
377 
378 	return scsiicmd(hs, slave, (uint8_t *)&cdb, sizeof(cdb), NULL, 0,
379 	    STATUS_PHASE);
380 }
381 
382 int
scsi_request_sense(int ctlr,int slave,uint8_t * buf,unsigned int len)383 scsi_request_sense(int ctlr, int slave, uint8_t *buf, unsigned int len)
384 {
385 	struct scsi_softc *hs = &scsi_softc[ctlr];
386 	static struct scsi_cdb6 cdb = { CMD_REQUEST_SENSE };
387 
388 	cdb.len = len;
389 	return scsiicmd(hs, slave, (uint8_t *)&cdb, sizeof(cdb), buf, len,
390 	    DATA_IN_PHASE);
391 }
392 
393 int
scsi_read_capacity(int ctlr,int slave,uint8_t * buf,unsigned int len)394 scsi_read_capacity(int ctlr, int slave, uint8_t *buf, unsigned int len)
395 {
396 	struct scsi_softc *hs = &scsi_softc[ctlr];
397 	static struct scsi_cdb10 cdb = { CMD_READ_CAPACITY };
398 
399 	return scsiicmd(hs, slave, (uint8_t *)&cdb, sizeof(cdb), buf, len,
400 	    DATA_IN_PHASE);
401 }
402 
403 #ifdef SUPPORT_CD
404 int
scsi_inquiry(int ctlr,int slave,uint8_t * buf,unsigned int len)405 scsi_inquiry(int ctlr, int slave, uint8_t *buf, unsigned int len)
406 {
407 	struct scsi_softc *hs = &scsi_softc[ctlr];
408 	static struct scsi_cdb6 cdb = { CMD_INQUIRY };
409 
410 	cdb.len = len;
411 	return scsiicmd(hs, slave, (uint8_t *)&cdb, sizeof(cdb), buf, len,
412 	    DATA_IN_PHASE);
413 }
414 #endif
415 
416 int
scsi_tt_read(int ctlr,int slave,uint8_t * buf,u_int len,daddr_t blk,u_int nblk)417 scsi_tt_read(int ctlr, int slave, uint8_t *buf, u_int len, daddr_t blk,
418     u_int nblk)
419 {
420 	struct scsi_softc *hs = &scsi_softc[ctlr];
421 	struct scsi_cdb10 cdb;
422 
423 	memset(&cdb, 0, sizeof(cdb));
424 	cdb.cmd = CMD_READ_EXT;
425 	cdb.lbah = blk >> 24;
426 	cdb.lbahm = blk >> 16;
427 	cdb.lbalm = blk >> 8;
428 	cdb.lbal = blk;
429 	cdb.lenh = nblk >> (8 + DEV_BSHIFT);
430 	cdb.lenl = nblk >> DEV_BSHIFT;
431 	return scsiicmd(hs, slave, (uint8_t *)&cdb, sizeof(cdb), buf, len,
432 	    DATA_IN_PHASE);
433 }
434 
435 int
scsi_tt_write(int ctlr,int slave,uint8_t * buf,u_int len,daddr_t blk,u_int nblk)436 scsi_tt_write(int ctlr, int slave, uint8_t *buf, u_int len, daddr_t blk,
437     u_int nblk)
438 {
439 	struct scsi_softc *hs = &scsi_softc[ctlr];
440 	struct scsi_cdb10 cdb;
441 
442 	memset(&cdb, 0, sizeof(cdb));
443 	cdb.cmd = CMD_WRITE_EXT;
444 	cdb.lbah = blk >> 24;
445 	cdb.lbahm = blk >> 16;
446 	cdb.lbalm = blk >> 8;
447 	cdb.lbal = blk;
448 	cdb.lenh = nblk >> (8 + DEV_BSHIFT);
449 	cdb.lenl = nblk >> DEV_BSHIFT;
450 	return scsiicmd(hs, slave, (uint8_t *)&cdb, sizeof(cdb), buf, len,
451 	    DATA_OUT_PHASE);
452 }
453