1/*	$NetBSD: scsi.c,v 1.9 2007/03/04 05:59:50 christos 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
63static void scsireset(int);
64static int issue_select(volatile struct scsidevice *, uint8_t, uint8_t);
65static int wait_for_select(volatile struct scsidevice *hd);
66static int ixfer_start(volatile struct scsidevice *, int, uint8_t, int);
67static int ixfer_out(volatile struct scsidevice *, int, uint8_t *);
68static int ixfer_in(volatile struct scsidevice *hd, int, uint8_t *);
69static int scsiicmd(struct scsi_softc *, int, uint8_t *, int, uint8_t *, int,
70    uint8_t);
71
72struct	scsi_softc scsi_softc[NSCSI];
73
74
75int scsi_cmd_wait = 50000;	/* use the "real" driver init_wait value */
76int scsi_data_wait = 50000;	/* use the "real" driver init_wait value */
77
78void
79scsiinit(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
109int
110scsialive(int unit)
111{
112
113	if (unit >= NSCSI || scsi_softc[unit].sc_alive == 0)
114		return 0;
115	return 1;
116}
117
118static void
119scsireset(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
162void
163scsiabort(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
172static int
173issue_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 2ms */
185	hd->scsi_tch = 0;
186	hd->scsi_tcm = 32;
187	hd->scsi_tcl = 4;
188
189	hd->scsi_scmd = SCMD_SELECT;
190	return 0;
191}
192
193static int
194wait_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
209static int
210ixfer_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
229static int
230ixfer_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
245static int
246ixfer_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
266static int
267scsiicmd(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	}
366abort:
367	scsiabort(hs, hd);
368out:
369	return hs->sc_stat;
370}
371
372int
373scsi_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
382int
383scsi_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
393int
394scsi_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
403int
404scsi_tt_read(int ctlr, int slave, uint8_t *buf, u_int len, daddr_t blk,
405    u_int nblk)
406{
407	struct scsi_softc *hs = &scsi_softc[ctlr];
408	struct scsi_cdb10 cdb;
409
410	memset(&cdb, 0, sizeof(cdb));
411	cdb.cmd = CMD_READ_EXT;
412	cdb.lbah = blk >> 24;
413	cdb.lbahm = blk >> 16;
414	cdb.lbalm = blk >> 8;
415	cdb.lbal = blk;
416	cdb.lenh = nblk >> (8 + DEV_BSHIFT);
417	cdb.lenl = nblk >> DEV_BSHIFT;
418	return scsiicmd(hs, slave, (uint8_t *)&cdb, sizeof(cdb), buf, len,
419	    DATA_IN_PHASE);
420}
421
422int
423scsi_tt_write(int ctlr, int slave, uint8_t *buf, u_int len, daddr_t blk,
424    u_int nblk)
425{
426	struct scsi_softc *hs = &scsi_softc[ctlr];
427	struct scsi_cdb10 cdb;
428
429	memset(&cdb, 0, sizeof(cdb));
430	cdb.cmd = CMD_WRITE_EXT;
431	cdb.lbah = blk >> 24;
432	cdb.lbahm = blk >> 16;
433	cdb.lbalm = blk >> 8;
434	cdb.lbal = blk;
435	cdb.lenh = nblk >> (8 + DEV_BSHIFT);
436	cdb.lenl = nblk >> DEV_BSHIFT;
437	return scsiicmd(hs, slave, (uint8_t *)&cdb, sizeof(cdb), buf, len,
438	    DATA_OUT_PHASE);
439}
440