1139749Simp/*-
232801Sjulian *       Copyright (c) 1997 by Simon Shapiro
332801Sjulian *       All Rights Reserved
432801Sjulian *
532801Sjulian * Redistribution and use in source and binary forms, with or without
632801Sjulian * modification, are permitted provided that the following conditions
732801Sjulian * are met:
832801Sjulian * 1. Redistributions of source code must retain the above copyright
932801Sjulian *    notice, this list of conditions, and the following disclaimer,
1032801Sjulian *    without modification, immediately at the beginning of the file.
1132801Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1232801Sjulian *    notice, this list of conditions and the following disclaimer in the
1332801Sjulian *    documentation and/or other materials provided with the distribution.
1432801Sjulian * 3. The name of the author may not be used to endorse or promote products
1532801Sjulian *    derived from this software without specific prior written permission.
1632801Sjulian *
1732801Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1832801Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1932801Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2032801Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2132801Sjulian * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2232801Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2332801Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2432801Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2532801Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2632801Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2732801Sjulian * SUCH DAMAGE.
2832801Sjulian */
2932801Sjulian
30119418Sobrien#include <sys/cdefs.h>
31119418Sobrien__FBSDID("$FreeBSD: releng/10.3/sys/dev/dpt/dpt_scsi.c 281826 2015-04-21 11:27:50Z mav $");
32119418Sobrien
3339234Sgibbs/*
3432801Sjulian * dpt_scsi.c: SCSI dependant code for the DPT driver
3532801Sjulian *
3632801Sjulian * credits:	Assisted by Mike Neuffer in the early low level DPT code
3732801Sjulian *		Thanx to Mark Salyzyn of DPT for his assistance.
3832801Sjulian *		Special thanx to Justin Gibbs for invaluable help in
3932801Sjulian *		making this driver look and work like a FreeBSD component.
4032801Sjulian *		Last but not least, many thanx to UCB and the FreeBSD
4132801Sjulian *		team for creating and maintaining such a wonderful O/S.
4232801Sjulian *
4339234Sgibbs * TODO:     * Add ISA probe code.
4439234Sgibbs *	     * Add driver-level RAID-0. This will allow interoperability with
4538115Seivind *	       NiceTry, M$-Doze, Win-Dog, Slowlaris, etc., in recognizing RAID
4632801Sjulian *	       arrays that span controllers (Wow!).
4732801Sjulian */
4832801Sjulian
4932801Sjulian#define _DPT_C_
5032801Sjulian
5132801Sjulian#include "opt_dpt.h"
52166091Smarius#include "opt_eisa.h"
53166091Smarius
5432801Sjulian#include <sys/param.h>
5532801Sjulian#include <sys/systm.h>
56241593Sjhb#include <sys/conf.h>
5750135Smsmith#include <sys/eventhandler.h>
5832801Sjulian#include <sys/malloc.h>
5932801Sjulian#include <sys/kernel.h>
6032801Sjulian
6159078Smdodd#include <sys/bus.h>
6259078Smdodd
6339234Sgibbs#include <machine/bus.h>
6459078Smdodd
65112780Smdodd#include <machine/resource.h>
66112780Smdodd#include <sys/rman.h>
6732801Sjulian
68112780Smdodd
6939234Sgibbs#include <cam/cam.h>
7039234Sgibbs#include <cam/cam_ccb.h>
7139234Sgibbs#include <cam/cam_sim.h>
7239234Sgibbs#include <cam/cam_xpt_sim.h>
7339234Sgibbs#include <cam/cam_debug.h>
7439234Sgibbs#include <cam/scsi/scsi_all.h>
7539234Sgibbs#include <cam/scsi/scsi_message.h>
7639234Sgibbs
7732801Sjulian#include <vm/vm.h>
7832801Sjulian#include <vm/pmap.h>
7932801Sjulian
8039234Sgibbs#include <dev/dpt/dpt.h>
8138115Seivind
8239234Sgibbs/* dpt_isa.c, dpt_eisa.c, and dpt_pci.c need this in a central place */
83112780Smdodddevclass_t	dpt_devclass;
8432801Sjulian
8539234Sgibbs#define microtime_now dpt_time_now()
8634480Sjulian
8739234Sgibbs#define dpt_inl(dpt, port)				\
88241593Sjhb	bus_read_4((dpt)->io_res, (dpt)->io_offset + port)
8939234Sgibbs#define dpt_inb(dpt, port)				\
90241593Sjhb	bus_read_1((dpt)->io_res, (dpt)->io_offset + port)
9139234Sgibbs#define dpt_outl(dpt, port, value)			\
92241593Sjhb	bus_write_4((dpt)->io_res, (dpt)->io_offset + port, value)
9339234Sgibbs#define dpt_outb(dpt, port, value)			\
94241593Sjhb	bus_write_1((dpt)->io_res, (dpt)->io_offset + port, value)
9534480Sjulian
9639234Sgibbs/*
9739234Sgibbs * These will have to be setup by parameters passed at boot/load time. For
9839234Sgibbs * perfromance reasons, we make them constants for the time being.
9939234Sgibbs */
10039234Sgibbs#define	dpt_min_segs	DPT_MAX_SEGS
10139234Sgibbs#define	dpt_max_segs	DPT_MAX_SEGS
10232801Sjulian
10339234Sgibbs/* Definitions for our use of the SIM private CCB area */
10439234Sgibbs#define ccb_dccb_ptr spriv_ptr0
10539234Sgibbs#define ccb_dpt_ptr spriv_ptr1
10638115Seivind
10739234Sgibbs/* ================= Private Inline Function declarations ===================*/
10839234Sgibbsstatic __inline int		dpt_just_reset(dpt_softc_t * dpt);
10939234Sgibbsstatic __inline int		dpt_raid_busy(dpt_softc_t * dpt);
110166091Smarius#ifdef DEV_EISA
11152042Smdoddstatic __inline int		dpt_pio_wait (u_int32_t, u_int, u_int, u_int);
112166091Smarius#endif
11339234Sgibbsstatic __inline int		dpt_wait(dpt_softc_t *dpt, u_int bits,
11439234Sgibbs					 u_int state);
11539234Sgibbsstatic __inline struct dpt_ccb* dptgetccb(struct dpt_softc *dpt);
11639234Sgibbsstatic __inline void		dptfreeccb(struct dpt_softc *dpt,
11739234Sgibbs					   struct dpt_ccb *dccb);
118106527Sjhbstatic __inline bus_addr_t	dptccbvtop(struct dpt_softc *dpt,
11939234Sgibbs					   struct dpt_ccb *dccb);
12032801Sjulian
12139234Sgibbsstatic __inline int		dpt_send_immediate(dpt_softc_t *dpt,
12239234Sgibbs						   eata_ccb_t *cmd_block,
12339234Sgibbs						   u_int32_t cmd_busaddr,
12439234Sgibbs						   u_int retries,
12539234Sgibbs						   u_int ifc, u_int code,
12639234Sgibbs						   u_int code2);
12732801Sjulian
12839234Sgibbs/* ==================== Private Function declarations =======================*/
12939234Sgibbsstatic void		dptmapmem(void *arg, bus_dma_segment_t *segs,
13039234Sgibbs				  int nseg, int error);
13132801Sjulian
13239234Sgibbsstatic struct sg_map_node*
13339234Sgibbs			dptallocsgmap(struct dpt_softc *dpt);
13432801Sjulian
13539234Sgibbsstatic int		dptallocccbs(dpt_softc_t *dpt);
13632801Sjulian
13739234Sgibbsstatic int		dpt_get_conf(dpt_softc_t *dpt, dpt_ccb_t *dccb,
13839234Sgibbs				     u_int32_t dccb_busaddr, u_int size,
13939234Sgibbs				     u_int page, u_int target, int extent);
14039234Sgibbsstatic void		dpt_detect_cache(dpt_softc_t *dpt, dpt_ccb_t *dccb,
14139234Sgibbs					 u_int32_t dccb_busaddr,
14239234Sgibbs					 u_int8_t *buff);
14332801Sjulian
14439234Sgibbsstatic void		dpt_poll(struct cam_sim *sim);
145241593Sjhbstatic void		dpt_intr_locked(dpt_softc_t *dpt);
14632801Sjulian
14739234Sgibbsstatic void		dptexecuteccb(void *arg, bus_dma_segment_t *dm_segs,
14839234Sgibbs				      int nseg, int error);
14932801Sjulian
15039234Sgibbsstatic void		dpt_action(struct cam_sim *sim, union ccb *ccb);
15132801Sjulian
15239234Sgibbsstatic int		dpt_send_eata_command(dpt_softc_t *dpt, eata_ccb_t *cmd,
15339234Sgibbs					      u_int32_t cmd_busaddr,
15439234Sgibbs					      u_int command, u_int retries,
15539234Sgibbs					      u_int ifc, u_int code,
15639234Sgibbs					      u_int code2);
15739234Sgibbsstatic void		dptprocesserror(dpt_softc_t *dpt, dpt_ccb_t *dccb,
15839234Sgibbs					union ccb *ccb, u_int hba_stat,
15939234Sgibbs					u_int scsi_stat, u_int32_t resid);
16032801Sjulian
16139234Sgibbsstatic void		dpttimeout(void *arg);
16250107Smsmithstatic void		dptshutdown(void *arg, int howto);
16334480Sjulian
16439234Sgibbs/* ================= Private Inline Function definitions ====================*/
16539234Sgibbsstatic __inline int
16639234Sgibbsdpt_just_reset(dpt_softc_t * dpt)
16739234Sgibbs{
16839234Sgibbs	if ((dpt_inb(dpt, 2) == 'D')
16939234Sgibbs	 && (dpt_inb(dpt, 3) == 'P')
17039234Sgibbs	 && (dpt_inb(dpt, 4) == 'T')
17139234Sgibbs	 && (dpt_inb(dpt, 5) == 'H'))
17239234Sgibbs		return (1);
17339234Sgibbs	else
17439234Sgibbs		return (0);
17539234Sgibbs}
17632801Sjulian
17739234Sgibbsstatic __inline int
17839234Sgibbsdpt_raid_busy(dpt_softc_t * dpt)
17939234Sgibbs{
18039234Sgibbs	if ((dpt_inb(dpt, 0) == 'D')
18139234Sgibbs	 && (dpt_inb(dpt, 1) == 'P')
18239234Sgibbs	 && (dpt_inb(dpt, 2) == 'T'))
18339234Sgibbs		return (1);
18439234Sgibbs	else
18539234Sgibbs		return (0);
18639234Sgibbs}
18732801Sjulian
188166091Smarius#ifdef DEV_EISA
18939234Sgibbsstatic __inline int
19052042Smdodddpt_pio_wait (u_int32_t base, u_int reg, u_int bits, u_int state)
19152042Smdodd{
19252042Smdodd	int   i;
19352042Smdodd	u_int c;
19452042Smdodd
19552042Smdodd	for (i = 0; i < 20000; i++) {	/* wait 20ms for not busy */
19652042Smdodd		c = inb(base + reg) & bits;
19752042Smdodd		if (!(c == state))
19852042Smdodd			return (0);
19952042Smdodd		else
20052042Smdodd			DELAY(50);
20152042Smdodd	}
20252042Smdodd	return (-1);
20352042Smdodd}
204166091Smarius#endif
20552042Smdodd
20652042Smdoddstatic __inline int
20739234Sgibbsdpt_wait(dpt_softc_t *dpt, u_int bits, u_int state)
20839234Sgibbs{
20939234Sgibbs	int   i;
21039234Sgibbs	u_int c;
21132801Sjulian
21239234Sgibbs	for (i = 0; i < 20000; i++) {	/* wait 20ms for not busy */
21339234Sgibbs		c = dpt_inb(dpt, HA_RSTATUS) & bits;
21439234Sgibbs		if (c == state)
21539234Sgibbs			return (0);
21639234Sgibbs		else
21739234Sgibbs			DELAY(50);
21839234Sgibbs	}
21939234Sgibbs	return (-1);
22039234Sgibbs}
22132801Sjulian
22239234Sgibbsstatic __inline struct dpt_ccb*
22339234Sgibbsdptgetccb(struct dpt_softc *dpt)
22439234Sgibbs{
22539234Sgibbs	struct	dpt_ccb* dccb;
22632801Sjulian
227241593Sjhb	if (!dumping)
228241593Sjhb		mtx_assert(&dpt->lock, MA_OWNED);
22939234Sgibbs	if ((dccb = SLIST_FIRST(&dpt->free_dccb_list)) != NULL) {
23039234Sgibbs		SLIST_REMOVE_HEAD(&dpt->free_dccb_list, links);
23139234Sgibbs		dpt->free_dccbs--;
23239234Sgibbs	} else if (dpt->total_dccbs < dpt->max_dccbs) {
23339234Sgibbs		dptallocccbs(dpt);
23439234Sgibbs		dccb = SLIST_FIRST(&dpt->free_dccb_list);
23539234Sgibbs		if (dccb == NULL)
236241593Sjhb			device_printf(dpt->dev, "Can't malloc DCCB\n");
23739234Sgibbs		else {
23839234Sgibbs			SLIST_REMOVE_HEAD(&dpt->free_dccb_list, links);
23939234Sgibbs			dpt->free_dccbs--;
24039234Sgibbs		}
24139234Sgibbs	}
24239234Sgibbs
24339234Sgibbs	return (dccb);
24439234Sgibbs}
24539234Sgibbs
24639234Sgibbsstatic __inline void
24739234Sgibbsdptfreeccb(struct dpt_softc *dpt, struct dpt_ccb *dccb)
24832801Sjulian{
24932801Sjulian
250241593Sjhb	if (!dumping)
251241593Sjhb		mtx_assert(&dpt->lock, MA_OWNED);
25239234Sgibbs	if ((dccb->state & DCCB_ACTIVE) != 0)
25339234Sgibbs		LIST_REMOVE(&dccb->ccb->ccb_h, sim_links.le);
25439234Sgibbs	if ((dccb->state & DCCB_RELEASE_SIMQ) != 0)
25539234Sgibbs		dccb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
25639234Sgibbs	else if (dpt->resource_shortage != 0
25739234Sgibbs	 && (dccb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {
25839234Sgibbs		dccb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
25939234Sgibbs		dpt->resource_shortage = FALSE;
26039234Sgibbs	}
26139234Sgibbs	dccb->state = DCCB_FREE;
26239234Sgibbs	SLIST_INSERT_HEAD(&dpt->free_dccb_list, dccb, links);
26339234Sgibbs	++dpt->free_dccbs;
26439234Sgibbs}
26539234Sgibbs
266106527Sjhbstatic __inline bus_addr_t
26739234Sgibbsdptccbvtop(struct dpt_softc *dpt, struct dpt_ccb *dccb)
26832801Sjulian{
26939234Sgibbs	return (dpt->dpt_ccb_busbase
27039234Sgibbs	      + (u_int32_t)((caddr_t)dccb - (caddr_t)dpt->dpt_dccbs));
27139234Sgibbs}
27232801Sjulian
27339234Sgibbsstatic __inline struct dpt_ccb *
274106527Sjhbdptccbptov(struct dpt_softc *dpt, bus_addr_t busaddr)
27539234Sgibbs{
27639234Sgibbs	return (dpt->dpt_dccbs
27739234Sgibbs	     +  ((struct dpt_ccb *)busaddr
27839234Sgibbs	       - (struct dpt_ccb *)dpt->dpt_ccb_busbase));
27939234Sgibbs}
28032801Sjulian
28139234Sgibbs/*
28239234Sgibbs * Send a command for immediate execution by the DPT
28339234Sgibbs * See above function for IMPORTANT notes.
28439234Sgibbs */
28539234Sgibbsstatic __inline int
28639234Sgibbsdpt_send_immediate(dpt_softc_t *dpt, eata_ccb_t *cmd_block,
28739234Sgibbs		   u_int32_t cmd_busaddr, u_int retries,
28839234Sgibbs		   u_int ifc, u_int code, u_int code2)
28939234Sgibbs{
29039234Sgibbs	return (dpt_send_eata_command(dpt, cmd_block, cmd_busaddr,
29139234Sgibbs				      EATA_CMD_IMMEDIATE, retries, ifc,
29239234Sgibbs				      code, code2));
29339234Sgibbs}
29439234Sgibbs
29539234Sgibbs
29639234Sgibbs/* ===================== Private Function definitions =======================*/
29732801Sjulianstatic void
29839234Sgibbsdptmapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error)
29932801Sjulian{
30039234Sgibbs	bus_addr_t *busaddrp;
30139234Sgibbs
30239234Sgibbs	busaddrp = (bus_addr_t *)arg;
30339234Sgibbs	*busaddrp = segs->ds_addr;
30432801Sjulian}
30532801Sjulian
30639234Sgibbsstatic struct sg_map_node *
30739234Sgibbsdptallocsgmap(struct dpt_softc *dpt)
30839234Sgibbs{
30939234Sgibbs	struct sg_map_node *sg_map;
31032801Sjulian
31139234Sgibbs	sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
31239234Sgibbs
31339234Sgibbs	if (sg_map == NULL)
31439234Sgibbs		return (NULL);
31539234Sgibbs
31639234Sgibbs	/* Allocate S/G space for the next batch of CCBS */
31739234Sgibbs	if (bus_dmamem_alloc(dpt->sg_dmat, (void **)&sg_map->sg_vaddr,
31839234Sgibbs			     BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) {
31939234Sgibbs		free(sg_map, M_DEVBUF);
32039234Sgibbs		return (NULL);
32139234Sgibbs	}
32239234Sgibbs
32342018Seivind	(void)bus_dmamap_load(dpt->sg_dmat, sg_map->sg_dmamap, sg_map->sg_vaddr,
32442018Seivind			      PAGE_SIZE, dptmapmem, &sg_map->sg_physaddr,
32542018Seivind			      /*flags*/0);
32641996Seivind
32739234Sgibbs	SLIST_INSERT_HEAD(&dpt->sg_maps, sg_map, links);
32839234Sgibbs
32939234Sgibbs	return (sg_map);
33039234Sgibbs}
33139234Sgibbs
33239234Sgibbs/*
33339234Sgibbs * Allocate another chunk of CCB's. Return count of entries added.
33439234Sgibbs */
33539234Sgibbsstatic int
33639234Sgibbsdptallocccbs(dpt_softc_t *dpt)
33732801Sjulian{
33839234Sgibbs	struct dpt_ccb *next_ccb;
33939234Sgibbs	struct sg_map_node *sg_map;
34039234Sgibbs	bus_addr_t physaddr;
34139234Sgibbs	dpt_sg_t *segs;
34239234Sgibbs	int newcount;
34339234Sgibbs	int i;
34432801Sjulian
345241593Sjhb	if (!dumping)
346241593Sjhb		mtx_assert(&dpt->lock, MA_OWNED);
34739234Sgibbs	next_ccb = &dpt->dpt_dccbs[dpt->total_dccbs];
34839234Sgibbs
34939234Sgibbs	if (next_ccb == dpt->dpt_dccbs) {
35039234Sgibbs		/*
35139234Sgibbs		 * First time through.  Re-use the S/G
35239234Sgibbs		 * space we allocated for initialization
35339234Sgibbs		 * CCBS.
35439234Sgibbs		 */
35539234Sgibbs		sg_map = SLIST_FIRST(&dpt->sg_maps);
35632801Sjulian	} else {
35739234Sgibbs		sg_map = dptallocsgmap(dpt);
35832801Sjulian	}
35939234Sgibbs
36039234Sgibbs	if (sg_map == NULL)
36139234Sgibbs		return (0);
36239234Sgibbs
36339234Sgibbs	segs = sg_map->sg_vaddr;
36439234Sgibbs	physaddr = sg_map->sg_physaddr;
36539234Sgibbs
36639234Sgibbs	newcount = (PAGE_SIZE / (dpt->sgsize * sizeof(dpt_sg_t)));
36739234Sgibbs	for (i = 0; dpt->total_dccbs < dpt->max_dccbs && i < newcount; i++) {
36839234Sgibbs		int error;
36939234Sgibbs
37039234Sgibbs		error = bus_dmamap_create(dpt->buffer_dmat, /*flags*/0,
37139234Sgibbs					  &next_ccb->dmamap);
37239234Sgibbs		if (error != 0)
37339234Sgibbs			break;
374241593Sjhb		callout_init_mtx(&next_ccb->timer, &dpt->lock, 0);
37539234Sgibbs		next_ccb->sg_list = segs;
37639234Sgibbs		next_ccb->sg_busaddr = htonl(physaddr);
37739234Sgibbs		next_ccb->eata_ccb.cp_dataDMA = htonl(physaddr);
37839234Sgibbs		next_ccb->eata_ccb.cp_statDMA = htonl(dpt->sp_physaddr);
37939234Sgibbs		next_ccb->eata_ccb.cp_reqDMA =
38039234Sgibbs		    htonl(dptccbvtop(dpt, next_ccb)
38139234Sgibbs			+ offsetof(struct dpt_ccb, sense_data));
38239234Sgibbs		next_ccb->eata_ccb.cp_busaddr = dpt->dpt_ccb_busend;
38339234Sgibbs		next_ccb->state = DCCB_FREE;
38439515Sgibbs		next_ccb->tag = dpt->total_dccbs;
38539234Sgibbs		SLIST_INSERT_HEAD(&dpt->free_dccb_list, next_ccb, links);
38639553Sgibbs		segs += dpt->sgsize;
38739234Sgibbs		physaddr += (dpt->sgsize * sizeof(dpt_sg_t));
38839234Sgibbs		dpt->dpt_ccb_busend += sizeof(*next_ccb);
38939234Sgibbs		next_ccb++;
39039234Sgibbs		dpt->total_dccbs++;
39139234Sgibbs	}
39239234Sgibbs	return (i);
39332801Sjulian}
39432801Sjulian
395166091Smarius#ifdef DEV_EISA
39652042Smdodddpt_conf_t *
39752042Smdodddpt_pio_get_conf (u_int32_t base)
39852042Smdodd{
39952042Smdodd	static dpt_conf_t *	conf;
40052042Smdodd	u_int16_t *		p;
40152042Smdodd	int			i;
40252042Smdodd
40352042Smdodd	/*
40452042Smdodd	 * Allocate a dpt_conf_t
40552042Smdodd	 */
40652042Smdodd	if (!conf) {
40752042Smdodd		conf = (dpt_conf_t *)malloc(sizeof(dpt_conf_t),
408241593Sjhb						 M_DEVBUF, M_NOWAIT | M_ZERO);
40952042Smdodd	}
41052042Smdodd
41152042Smdodd	/*
41252042Smdodd	 * If we didn't get one then we probably won't ever get one.
41352042Smdodd	 */
41452042Smdodd	if (!conf) {
41552042Smdodd		printf("dpt: unable to allocate dpt_conf_t\n");
41652042Smdodd		return (NULL);
41752042Smdodd	}
41852042Smdodd
41952042Smdodd	/*
42052042Smdodd	 * Reset the controller.
42152042Smdodd	 */
42252042Smdodd	outb((base + HA_WCOMMAND), EATA_CMD_RESET);
42352042Smdodd
42452042Smdodd	/*
42552042Smdodd	 * Wait for the controller to become ready.
42659078Smdodd	 * For some reason there can be -no- delays after calling reset
42759078Smdodd	 * before we wait on ready status.
42852042Smdodd	 */
42952042Smdodd	if (dpt_pio_wait(base, HA_RSTATUS, HA_SBUSY, 0)) {
43052042Smdodd		printf("dpt: timeout waiting for controller to become ready\n");
43152042Smdodd		return (NULL);
43252042Smdodd	}
43352042Smdodd
43452042Smdodd	if (dpt_pio_wait(base, HA_RAUXSTAT, HA_ABUSY, 0)) {
43552042Smdodd		printf("dpt: timetout waiting for adapter ready.\n");
43652042Smdodd		return (NULL);
43752042Smdodd	}
43852042Smdodd
43952042Smdodd	/*
44052042Smdodd	 * Send the PIO_READ_CONFIG command.
44152042Smdodd	 */
44252042Smdodd	outb((base + HA_WCOMMAND), EATA_CMD_PIO_READ_CONFIG);
44352042Smdodd
44452042Smdodd	/*
44552042Smdodd	 * Read the data into the struct.
44652042Smdodd	 */
44752042Smdodd	p = (u_int16_t *)conf;
44852042Smdodd	for (i = 0; i < (sizeof(dpt_conf_t) / 2); i++) {
44952042Smdodd
45052042Smdodd		if (dpt_pio_wait(base, HA_RSTATUS, HA_SDRQ, 0)) {
451112780Smdodd			if (bootverbose)
452112780Smdodd				printf("dpt: timeout in data read.\n");
45352042Smdodd			return (NULL);
45452042Smdodd		}
45552042Smdodd
45659078Smdodd		(*p) = inw(base + HA_RDATA);
45752042Smdodd		p++;
45852042Smdodd	}
45952042Smdodd
46052042Smdodd	if (inb(base + HA_RSTATUS) & HA_SERROR) {
461112780Smdodd		if (bootverbose)
462112780Smdodd			printf("dpt: error reading configuration data.\n");
46352042Smdodd		return (NULL);
46452042Smdodd	}
46552042Smdodd
46652042Smdodd#define BE_EATA_SIGNATURE	0x45415441
46752042Smdodd#define LE_EATA_SIGNATURE	0x41544145
46852042Smdodd
46952042Smdodd	/*
47052042Smdodd	 * Test to see if we have a valid card.
47152042Smdodd	 */
47252042Smdodd	if ((conf->signature == BE_EATA_SIGNATURE) ||
47352042Smdodd	    (conf->signature == LE_EATA_SIGNATURE)) {
47452042Smdodd
47552042Smdodd		while (inb(base + HA_RSTATUS) & HA_SDRQ) {
47652042Smdodd 			inw(base + HA_RDATA);
47752042Smdodd		}
47852042Smdodd
47952042Smdodd		return (conf);
48052042Smdodd	}
48152042Smdodd	return (NULL);
48252042Smdodd}
483166091Smarius#endif
48452042Smdodd
48539234Sgibbs/*
48639234Sgibbs * Read a configuration page into the supplied dpt_cont_t buffer.
48739234Sgibbs */
48839234Sgibbsstatic int
48939234Sgibbsdpt_get_conf(dpt_softc_t *dpt, dpt_ccb_t *dccb, u_int32_t dccb_busaddr,
49039234Sgibbs	     u_int size, u_int page, u_int target, int extent)
49132801Sjulian{
49239234Sgibbs	eata_ccb_t *cp;
49332801Sjulian
49440419Sgibbs	u_int8_t   status;
49539234Sgibbs
49639234Sgibbs	int	   ndx;
49739234Sgibbs	int	   result;
49839234Sgibbs
499241593Sjhb	mtx_assert(&dpt->lock, MA_OWNED);
50039234Sgibbs	cp = &dccb->eata_ccb;
50164355Speter	bzero((void *)(uintptr_t)(volatile void *)dpt->sp, sizeof(*dpt->sp));
50239234Sgibbs
50339234Sgibbs	cp->Interpret = 1;
50439234Sgibbs	cp->DataIn = 1;
50539234Sgibbs	cp->Auto_Req_Sen = 1;
50639234Sgibbs	cp->reqlen = sizeof(struct scsi_sense_data);
50739234Sgibbs
50839234Sgibbs	cp->cp_id = target;
50939234Sgibbs	cp->cp_LUN = 0;		/* In the EATA packet */
51039234Sgibbs	cp->cp_lun = 0;		/* In the SCSI command */
51139234Sgibbs
51239234Sgibbs	cp->cp_scsi_cmd = INQUIRY;
51339234Sgibbs	cp->cp_len = size;
51439234Sgibbs
51539234Sgibbs	cp->cp_extent = extent;
51639234Sgibbs
51739234Sgibbs	cp->cp_page = page;
51839234Sgibbs	cp->cp_channel = 0;	/* DNC, Interpret mode is set */
51939234Sgibbs	cp->cp_identify = 1;
52039234Sgibbs	cp->cp_datalen = htonl(size);
52139234Sgibbs
52239234Sgibbs	/*
52339234Sgibbs	 * This could be a simple for loop, but we suspected the compiler To
52439234Sgibbs	 * have optimized it a bit too much. Wait for the controller to
52539234Sgibbs	 * become ready
52639234Sgibbs	 */
52739234Sgibbs	while (((status = dpt_inb(dpt, HA_RSTATUS)) != (HA_SREADY | HA_SSC)
52839234Sgibbs	     && (status != (HA_SREADY | HA_SSC | HA_SERROR))
52939234Sgibbs	     && (status != (HA_SDRDY | HA_SERROR | HA_SDRQ)))
53039234Sgibbs	    || (dpt_wait(dpt, HA_SBUSY, 0))) {
53139234Sgibbs
53239234Sgibbs		/*
53339234Sgibbs		 * RAID Drives still Spinning up? (This should only occur if
53439234Sgibbs		 * the DPT controller is in a NON PC (PCI?) platform).
53539234Sgibbs		 */
53639234Sgibbs		if (dpt_raid_busy(dpt)) {
537241593Sjhb			device_printf(dpt->dev,
538241593Sjhb			    "WARNING: Get_conf() RSUS failed.\n");
53939234Sgibbs			return (0);
54039234Sgibbs		}
54132801Sjulian	}
54239234Sgibbs
54339234Sgibbs	DptStat_Reset_BUSY(dpt->sp);
54439234Sgibbs
54539234Sgibbs	/*
54639234Sgibbs	 * XXXX We might want to do something more clever than aborting at
54739234Sgibbs	 * this point, like resetting (rebooting) the controller and trying
54839234Sgibbs	 * again.
54939234Sgibbs	 */
55039234Sgibbs	if ((result = dpt_send_eata_command(dpt, cp, dccb_busaddr,
55139234Sgibbs					    EATA_CMD_DMA_SEND_CP,
55239234Sgibbs					    10000, 0, 0, 0)) != 0) {
553241593Sjhb		device_printf(dpt->dev,
554241593Sjhb		       "WARNING: Get_conf() failed (%d) to send "
55539234Sgibbs		       "EATA_CMD_DMA_READ_CONFIG\n",
556241593Sjhb		       result);
55739234Sgibbs		return (0);
55839234Sgibbs	}
55939234Sgibbs	/* Wait for two seconds for a response.  This can be slow  */
56039234Sgibbs	for (ndx = 0;
56139234Sgibbs	     (ndx < 20000)
56239234Sgibbs	     && !((status = dpt_inb(dpt, HA_RAUXSTAT)) & HA_AIRQ);
56339234Sgibbs	     ndx++) {
56439234Sgibbs		DELAY(50);
56539234Sgibbs	}
56639234Sgibbs
56739234Sgibbs	/* Grab the status and clear interrupts */
56839234Sgibbs	status = dpt_inb(dpt, HA_RSTATUS);
56939234Sgibbs
57039234Sgibbs	/*
57139234Sgibbs	 * Check the status carefully.  Return only if the
57239234Sgibbs	 * command was successful.
57339234Sgibbs	 */
57439234Sgibbs	if (((status & HA_SERROR) == 0)
57539234Sgibbs	 && (dpt->sp->hba_stat == 0)
57639234Sgibbs	 && (dpt->sp->scsi_stat == 0)
57739234Sgibbs	 && (dpt->sp->residue_len == 0))
57839234Sgibbs		return (0);
57959078Smdodd
58059078Smdodd	if (dpt->sp->scsi_stat == SCSI_STATUS_CHECK_COND)
58159078Smdodd		return (0);
58259078Smdodd
58339234Sgibbs	return (1);
58432801Sjulian}
58532801Sjulian
58639234Sgibbs/* Detect Cache parameters and size */
58739234Sgibbsstatic void
58839234Sgibbsdpt_detect_cache(dpt_softc_t *dpt, dpt_ccb_t *dccb, u_int32_t dccb_busaddr,
58939234Sgibbs		 u_int8_t *buff)
59032801Sjulian{
59139234Sgibbs	eata_ccb_t *cp;
59239234Sgibbs	u_int8_t   *param;
59339234Sgibbs	int	    bytes;
59439234Sgibbs	int	    result;
59539234Sgibbs	int	    ndx;
59639234Sgibbs	u_int8_t    status;
59739234Sgibbs
598241593Sjhb	mtx_assert(&dpt->lock, MA_OWNED);
599241593Sjhb
60039234Sgibbs	/*
60139234Sgibbs	 * Default setting, for best perfromance..
60239234Sgibbs	 * This is what virtually all cards default to..
60339234Sgibbs	 */
60439234Sgibbs	dpt->cache_type = DPT_CACHE_WRITEBACK;
60539234Sgibbs	dpt->cache_size = 0;
60639234Sgibbs
60739234Sgibbs	cp = &dccb->eata_ccb;
60864355Speter	bzero((void *)(uintptr_t)(volatile void *)dpt->sp, sizeof(dpt->sp));
60939234Sgibbs	bzero(buff, 512);
61039234Sgibbs
61139234Sgibbs	/* Setup the command structure */
61239234Sgibbs	cp->Interpret = 1;
61339234Sgibbs	cp->DataIn = 1;
61439234Sgibbs	cp->Auto_Req_Sen = 1;
61539234Sgibbs	cp->reqlen = sizeof(struct scsi_sense_data);
61639234Sgibbs
61739234Sgibbs	cp->cp_id = 0;		/* who cares?  The HBA will interpret.. */
61839234Sgibbs	cp->cp_LUN = 0;		/* In the EATA packet */
61939234Sgibbs	cp->cp_lun = 0;		/* In the SCSI command */
62039234Sgibbs	cp->cp_channel = 0;
62139234Sgibbs
62239234Sgibbs	cp->cp_scsi_cmd = EATA_CMD_DMA_SEND_CP;
62339234Sgibbs	cp->cp_len = 56;
62439234Sgibbs
62539234Sgibbs	cp->cp_extent = 0;
62639234Sgibbs	cp->cp_page = 0;
62739234Sgibbs	cp->cp_identify = 1;
62839234Sgibbs	cp->cp_dispri = 1;
62939234Sgibbs
63039234Sgibbs	/*
63139234Sgibbs	 * Build the EATA Command Packet structure
63239234Sgibbs	 * for a Log Sense Command.
63339234Sgibbs	 */
63439234Sgibbs	cp->cp_cdb[0] = 0x4d;
63539234Sgibbs	cp->cp_cdb[1] = 0x0;
63639234Sgibbs	cp->cp_cdb[2] = 0x40 | 0x33;
63739234Sgibbs	cp->cp_cdb[7] = 1;
63839234Sgibbs
63939234Sgibbs	cp->cp_datalen = htonl(512);
64039234Sgibbs
64139234Sgibbs	result = dpt_send_eata_command(dpt, cp, dccb_busaddr,
64239234Sgibbs				       EATA_CMD_DMA_SEND_CP,
64339234Sgibbs				       10000, 0, 0, 0);
64439234Sgibbs	if (result != 0) {
645241593Sjhb		device_printf(dpt->dev,
646241593Sjhb		       "WARNING: detect_cache() failed (%d) to send "
647241593Sjhb		       "EATA_CMD_DMA_SEND_CP\n", result);
64839234Sgibbs		return;
64932801Sjulian	}
65039234Sgibbs	/* Wait for two seconds for a response.  This can be slow... */
65139234Sgibbs	for (ndx = 0;
65239234Sgibbs	     (ndx < 20000) &&
65339234Sgibbs	     !((status = dpt_inb(dpt, HA_RAUXSTAT)) & HA_AIRQ);
65439234Sgibbs	     ndx++) {
65539234Sgibbs		DELAY(50);
65639234Sgibbs	}
65739234Sgibbs
65839234Sgibbs	/* Grab the status and clear interrupts */
65939234Sgibbs	status = dpt_inb(dpt, HA_RSTATUS);
66039234Sgibbs
66139234Sgibbs	/*
66239234Sgibbs	 * Sanity check
66339234Sgibbs	 */
66439234Sgibbs	if (buff[0] != 0x33) {
66539234Sgibbs		return;
66639234Sgibbs	}
66739234Sgibbs	bytes = DPT_HCP_LENGTH(buff);
66839234Sgibbs	param = DPT_HCP_FIRST(buff);
66939234Sgibbs
67039234Sgibbs	if (DPT_HCP_CODE(param) != 1) {
67139234Sgibbs		/*
67239234Sgibbs		 * DPT Log Page layout error
67339234Sgibbs		 */
674241593Sjhb		device_printf(dpt->dev, "NOTICE: Log Page (1) layout error\n");
67539234Sgibbs		return;
67639234Sgibbs	}
67739234Sgibbs	if (!(param[4] & 0x4)) {
67839234Sgibbs		dpt->cache_type = DPT_NO_CACHE;
67939234Sgibbs		return;
68039234Sgibbs	}
68139234Sgibbs	while (DPT_HCP_CODE(param) != 6) {
68239234Sgibbs		param = DPT_HCP_NEXT(param);
68339234Sgibbs		if ((param < buff)
68439234Sgibbs		 || (param >= &buff[bytes])) {
68539234Sgibbs			return;
68639234Sgibbs		}
68739234Sgibbs	}
68839234Sgibbs
68939234Sgibbs	if (param[4] & 0x2) {
69039234Sgibbs		/*
69139234Sgibbs		 * Cache disabled
69239234Sgibbs		 */
69339234Sgibbs		dpt->cache_type = DPT_NO_CACHE;
69439234Sgibbs		return;
69539234Sgibbs	}
69639234Sgibbs
69739234Sgibbs	if (param[4] & 0x4) {
69839234Sgibbs		dpt->cache_type = DPT_CACHE_WRITETHROUGH;
69939234Sgibbs	}
70039234Sgibbs
70139234Sgibbs	/* XXX This isn't correct.  This log parameter only has two bytes.... */
70239234Sgibbs#if 0
70339234Sgibbs	dpt->cache_size = param[5]
70439234Sgibbs			| (param[6] << 8)
70539234Sgibbs			| (param[7] << 16)
70639234Sgibbs			| (param[8] << 24);
70739234Sgibbs#endif
70832801Sjulian}
70932801Sjulian
71039234Sgibbsstatic void
71139234Sgibbsdpt_poll(struct cam_sim *sim)
71232801Sjulian{
713241593Sjhb	dpt_intr_locked(cam_sim_softc(sim));
71439234Sgibbs}
71539234Sgibbs
71639234Sgibbsstatic void
71739234Sgibbsdptexecuteccb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
71839234Sgibbs{
71939234Sgibbs	struct	 dpt_ccb *dccb;
72039234Sgibbs	union	 ccb *ccb;
72139234Sgibbs	struct	 dpt_softc *dpt;
72239234Sgibbs
72339234Sgibbs	dccb = (struct dpt_ccb *)arg;
72439234Sgibbs	ccb = dccb->ccb;
72539234Sgibbs	dpt = (struct dpt_softc *)ccb->ccb_h.ccb_dpt_ptr;
726241605Spluknet	if (!dumping)
727241605Spluknet		mtx_assert(&dpt->lock, MA_OWNED);
72839234Sgibbs
72939234Sgibbs	if (error != 0) {
73039234Sgibbs		if (error != EFBIG)
731241593Sjhb			device_printf(dpt->dev,
732241593Sjhb			       "Unexepected error 0x%x returned from "
733241593Sjhb			       "bus_dmamap_load\n", error);
73439234Sgibbs		if (ccb->ccb_h.status == CAM_REQ_INPROG) {
73539234Sgibbs			xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
73639234Sgibbs			ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN;
73739234Sgibbs		}
73839234Sgibbs		dptfreeccb(dpt, dccb);
73939234Sgibbs		xpt_done(ccb);
74039234Sgibbs		return;
74139234Sgibbs	}
74239234Sgibbs
74339234Sgibbs	if (nseg != 0) {
74439234Sgibbs		dpt_sg_t *sg;
74539234Sgibbs		bus_dma_segment_t *end_seg;
746115343Sscottl		bus_dmasync_op_t op;
74739234Sgibbs
74839234Sgibbs		end_seg = dm_segs + nseg;
74939234Sgibbs
75039234Sgibbs		/* Copy the segments into our SG list */
75139234Sgibbs		sg = dccb->sg_list;
75239234Sgibbs		while (dm_segs < end_seg) {
75339234Sgibbs			sg->seg_len = htonl(dm_segs->ds_len);
75439234Sgibbs			sg->seg_addr = htonl(dm_segs->ds_addr);
75539234Sgibbs			sg++;
75639234Sgibbs			dm_segs++;
75739234Sgibbs		}
75839234Sgibbs
75939234Sgibbs		if (nseg > 1) {
76039234Sgibbs			dccb->eata_ccb.scatter = 1;
76139234Sgibbs			dccb->eata_ccb.cp_dataDMA = dccb->sg_busaddr;
76239234Sgibbs			dccb->eata_ccb.cp_datalen =
76339234Sgibbs			    htonl(nseg * sizeof(dpt_sg_t));
76439234Sgibbs		} else {
76539234Sgibbs			dccb->eata_ccb.cp_dataDMA = dccb->sg_list[0].seg_addr;
76639234Sgibbs			dccb->eata_ccb.cp_datalen = dccb->sg_list[0].seg_len;
76739234Sgibbs		}
76839234Sgibbs
76939234Sgibbs		if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
77039234Sgibbs			op = BUS_DMASYNC_PREREAD;
77139234Sgibbs		else
77239234Sgibbs			op = BUS_DMASYNC_PREWRITE;
77339234Sgibbs
77439234Sgibbs		bus_dmamap_sync(dpt->buffer_dmat, dccb->dmamap, op);
77539234Sgibbs
77632801Sjulian	} else {
77739234Sgibbs		dccb->eata_ccb.cp_dataDMA = 0;
77839234Sgibbs		dccb->eata_ccb.cp_datalen = 0;
77932801Sjulian	}
78039234Sgibbs
78139234Sgibbs	/*
78239234Sgibbs	 * Last time we need to check if this CCB needs to
78339234Sgibbs	 * be aborted.
78439234Sgibbs	 */
78539234Sgibbs	if (ccb->ccb_h.status != CAM_REQ_INPROG) {
78639234Sgibbs		if (nseg != 0)
78739234Sgibbs			bus_dmamap_unload(dpt->buffer_dmat, dccb->dmamap);
78839234Sgibbs		dptfreeccb(dpt, dccb);
78939234Sgibbs		xpt_done(ccb);
79039234Sgibbs		return;
79139234Sgibbs	}
79239234Sgibbs
79339234Sgibbs	dccb->state |= DCCB_ACTIVE;
79439234Sgibbs	ccb->ccb_h.status |= CAM_SIM_QUEUED;
79539234Sgibbs	LIST_INSERT_HEAD(&dpt->pending_ccb_list, &ccb->ccb_h, sim_links.le);
796275982Ssmh	callout_reset_sbt(&dccb->timer, SBT_1MS * ccb->ccb_h.timeout, 0,
797275982Ssmh	    dpttimeout, dccb, 0);
79839234Sgibbs	if (dpt_send_eata_command(dpt, &dccb->eata_ccb,
79939234Sgibbs				  dccb->eata_ccb.cp_busaddr,
80039234Sgibbs				  EATA_CMD_DMA_SEND_CP, 0, 0, 0, 0) != 0) {
80139234Sgibbs		ccb->ccb_h.status = CAM_NO_HBA; /* HBA dead or just busy?? */
80239234Sgibbs		if (nseg != 0)
80339234Sgibbs			bus_dmamap_unload(dpt->buffer_dmat, dccb->dmamap);
80439234Sgibbs		dptfreeccb(dpt, dccb);
80539234Sgibbs		xpt_done(ccb);
80639234Sgibbs	}
80732801Sjulian}
80832801Sjulian
80939234Sgibbsstatic void
81039234Sgibbsdpt_action(struct cam_sim *sim, union ccb *ccb)
81132801Sjulian{
81239234Sgibbs	struct	  dpt_softc *dpt;
81339234Sgibbs
81439234Sgibbs	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("dpt_action\n"));
81539234Sgibbs
81639234Sgibbs	dpt = (struct dpt_softc *)cam_sim_softc(sim);
817241593Sjhb	mtx_assert(&dpt->lock, MA_OWNED);
81839234Sgibbs
81939234Sgibbs	if ((dpt->state & DPT_HA_SHUTDOWN_ACTIVE) != 0) {
82039234Sgibbs		xpt_print_path(ccb->ccb_h.path);
82139234Sgibbs		printf("controller is shutdown. Aborting CCB.\n");
82239234Sgibbs		ccb->ccb_h.status = CAM_NO_HBA;
82339234Sgibbs		xpt_done(ccb);
82432801Sjulian		return;
82532801Sjulian	}
82639234Sgibbs
82739234Sgibbs	switch (ccb->ccb_h.func_code) {
82839234Sgibbs	/* Common cases first */
82939234Sgibbs	case XPT_SCSI_IO:	/* Execute the requested I/O operation */
83039234Sgibbs	{
83139234Sgibbs		struct	ccb_scsiio *csio;
83239234Sgibbs		struct	ccb_hdr *ccbh;
83339234Sgibbs		struct	dpt_ccb *dccb;
83439234Sgibbs		struct	eata_ccb *eccb;
83539234Sgibbs
83639234Sgibbs		csio = &ccb->csio;
83739234Sgibbs		ccbh = &ccb->ccb_h;
83839234Sgibbs		/* Max CDB length is 12 bytes */
83939234Sgibbs		if (csio->cdb_len > 12) {
84039234Sgibbs			ccb->ccb_h.status = CAM_REQ_INVALID;
84139234Sgibbs			xpt_done(ccb);
84239234Sgibbs			return;
84339234Sgibbs		}
84439234Sgibbs		if ((dccb = dptgetccb(dpt)) == NULL) {
84539234Sgibbs			dpt->resource_shortage = 1;
84639234Sgibbs			xpt_freeze_simq(sim, /*count*/1);
84739234Sgibbs			ccb->ccb_h.status = CAM_REQUEUE_REQ;
84839234Sgibbs			xpt_done(ccb);
84939234Sgibbs			return;
85039234Sgibbs		}
85139234Sgibbs		eccb = &dccb->eata_ccb;
85239234Sgibbs
85339234Sgibbs		/* Link dccb and ccb so we can find one from the other */
85439234Sgibbs		dccb->ccb = ccb;
85539234Sgibbs		ccb->ccb_h.ccb_dccb_ptr = dccb;
85639234Sgibbs		ccb->ccb_h.ccb_dpt_ptr = dpt;
85739234Sgibbs
85839234Sgibbs		/*
85939234Sgibbs		 * Explicitly set all flags so that the compiler can
86039553Sgibbs		 * be smart about setting them.
86139234Sgibbs		 */
86239234Sgibbs		eccb->SCSI_Reset = 0;
86339234Sgibbs		eccb->HBA_Init = 0;
86439234Sgibbs		eccb->Auto_Req_Sen = (ccb->ccb_h.flags & CAM_DIS_AUTOSENSE)
86539234Sgibbs				   ? 0 : 1;
86639234Sgibbs		eccb->scatter = 0;
86739234Sgibbs		eccb->Quick = 0;
86839234Sgibbs		eccb->Interpret =
86939234Sgibbs		    ccb->ccb_h.target_id == dpt->hostid[cam_sim_bus(sim)]
87039234Sgibbs		    ? 1 : 0;
87139234Sgibbs		eccb->DataOut = (ccb->ccb_h.flags & CAM_DIR_OUT) ? 1 : 0;
87239234Sgibbs		eccb->DataIn = (ccb->ccb_h.flags & CAM_DIR_IN) ? 1 : 0;
87339234Sgibbs		eccb->reqlen = csio->sense_len;
87439234Sgibbs		eccb->cp_id = ccb->ccb_h.target_id;
87539234Sgibbs		eccb->cp_channel = cam_sim_bus(sim);
87639234Sgibbs		eccb->cp_LUN = ccb->ccb_h.target_lun;
87739234Sgibbs		eccb->cp_luntar = 0;
87839234Sgibbs		eccb->cp_dispri = (ccb->ccb_h.flags & CAM_DIS_DISCONNECT)
87939234Sgibbs				? 0 : 1;
88039234Sgibbs		eccb->cp_identify = 1;
88139515Sgibbs
88240418Sgibbs		if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0
88340418Sgibbs		 && csio->tag_action != CAM_TAG_ACTION_NONE) {
88439515Sgibbs			eccb->cp_msg[0] = csio->tag_action;
88539515Sgibbs			eccb->cp_msg[1] = dccb->tag;
88639515Sgibbs		} else {
88739515Sgibbs			eccb->cp_msg[0] = 0;
88839515Sgibbs			eccb->cp_msg[1] = 0;
88939515Sgibbs		}
89039234Sgibbs		eccb->cp_msg[2] = 0;
89139234Sgibbs
89239234Sgibbs		if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) {
89339234Sgibbs			if ((ccb->ccb_h.flags & CAM_CDB_PHYS) == 0) {
89439234Sgibbs				bcopy(csio->cdb_io.cdb_ptr,
89539234Sgibbs				      eccb->cp_cdb, csio->cdb_len);
89639234Sgibbs			} else {
89739234Sgibbs				/* I guess I could map it in... */
89839234Sgibbs				ccb->ccb_h.status = CAM_REQ_INVALID;
89939234Sgibbs				dptfreeccb(dpt, dccb);
90039234Sgibbs				xpt_done(ccb);
90139234Sgibbs				return;
90239234Sgibbs			}
90339234Sgibbs		} else {
90439234Sgibbs			bcopy(csio->cdb_io.cdb_bytes,
90539234Sgibbs			      eccb->cp_cdb, csio->cdb_len);
90639234Sgibbs		}
90739234Sgibbs		/*
90839234Sgibbs		 * If we have any data to send with this command,
90939234Sgibbs		 * map it into bus space.
91039234Sgibbs		 */
91139234Sgibbs	        /* Only use S/G if there is a transfer */
91239234Sgibbs		if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
913246713Skib			int error;
914246713Skib
915246713Skib			error = bus_dmamap_load_ccb(dpt->buffer_dmat,
916246713Skib						    dccb->dmamap,
917246713Skib						    ccb,
918246713Skib						    dptexecuteccb,
919246713Skib						    dccb, /*flags*/0);
920246713Skib			if (error == EINPROGRESS) {
92139234Sgibbs				/*
922246713Skib				 * So as to maintain ordering,
923246713Skib				 * freeze the controller queue
924246713Skib				 * until our mapping is
925246713Skib				 * returned.
92639234Sgibbs				 */
927246713Skib				xpt_freeze_simq(sim, 1);
928246713Skib				dccb->state |= CAM_RELEASE_SIMQ;
92939234Sgibbs			}
93039234Sgibbs		} else {
93139234Sgibbs			/*
93239234Sgibbs			 * XXX JGibbs.
93339234Sgibbs			 * Does it want them both on or both off?
93439234Sgibbs			 * CAM_DIR_NONE is both on, so this code can
93539234Sgibbs			 * be removed if this is also what the DPT
93639234Sgibbs			 * exptects.
93739234Sgibbs			 */
93839234Sgibbs			eccb->DataOut = 0;
93939234Sgibbs			eccb->DataIn = 0;
94039234Sgibbs			dptexecuteccb(dccb, NULL, 0, 0);
94139234Sgibbs		}
94239234Sgibbs		break;
94339234Sgibbs	}
94439234Sgibbs	case XPT_RESET_DEV:	/* Bus Device Reset the specified SCSI device */
94539234Sgibbs	case XPT_ABORT:			/* Abort the specified CCB */
94639234Sgibbs		/* XXX Implement */
94739234Sgibbs		ccb->ccb_h.status = CAM_REQ_INVALID;
94839234Sgibbs		xpt_done(ccb);
94939234Sgibbs		break;
95039234Sgibbs	case XPT_SET_TRAN_SETTINGS:
95139234Sgibbs	{
95239234Sgibbs		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
95339234Sgibbs		xpt_done(ccb);
95439234Sgibbs		break;
95539234Sgibbs	}
95639234Sgibbs	case XPT_GET_TRAN_SETTINGS:
95739234Sgibbs	/* Get default/user set transfer settings for the target */
95839234Sgibbs	{
959163816Smjacob		struct	ccb_trans_settings *cts = &ccb->cts;
960163816Smjacob		struct ccb_trans_settings_scsi *scsi =
961163816Smjacob		    &cts->proto_specific.scsi;
962163816Smjacob		struct ccb_trans_settings_spi *spi =
963163816Smjacob		    &cts->xport_specific.spi;
964163816Smjacob
965163816Smjacob		cts->protocol = PROTO_SCSI;
966163816Smjacob		cts->protocol_version = SCSI_REV_2;
967163816Smjacob		cts->transport = XPORT_SPI;
968163816Smjacob		cts->transport_version = 2;
96939234Sgibbs
970163816Smjacob		if (cts->type == CTS_TYPE_USER_SETTINGS) {
971163816Smjacob			spi->flags = CTS_SPI_FLAGS_DISC_ENB;
972163816Smjacob			spi->bus_width = (dpt->max_id > 7)
973163816Smjacob				       ? MSG_EXT_WDTR_BUS_8_BIT
974163816Smjacob				       : MSG_EXT_WDTR_BUS_16_BIT;
975163816Smjacob			spi->sync_period = 25; /* 10MHz */
976163816Smjacob			if (spi->sync_period != 0)
977163816Smjacob				spi->sync_offset = 15;
978163816Smjacob			scsi->flags = CTS_SCSI_FLAGS_TAG_ENB;
979163816Smjacob
980163816Smjacob			spi->valid = CTS_SPI_VALID_SYNC_RATE
981163816Smjacob				| CTS_SPI_VALID_SYNC_OFFSET
982163816Smjacob				| CTS_SPI_VALID_BUS_WIDTH
983163816Smjacob				| CTS_SPI_VALID_DISC;
984163816Smjacob			scsi->valid = CTS_SCSI_VALID_TQ;
985163816Smjacob			ccb->ccb_h.status = CAM_REQ_CMP;
986163816Smjacob		} else {
987163816Smjacob			ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
988163816Smjacob		}
98939234Sgibbs		xpt_done(ccb);
99039234Sgibbs		break;
99139234Sgibbs	}
99239234Sgibbs	case XPT_CALC_GEOMETRY:
99339234Sgibbs	{
99439234Sgibbs		/*
99539234Sgibbs		 * XXX Use Adaptec translation until I find out how to
99639234Sgibbs		 *     get this information from the card.
99739234Sgibbs		 */
998116351Snjl		cam_calc_geometry(&ccb->ccg, /*extended*/1);
99939234Sgibbs		xpt_done(ccb);
100039234Sgibbs		break;
100139234Sgibbs	}
100239234Sgibbs	case XPT_RESET_BUS:		/* Reset the specified SCSI bus */
100339234Sgibbs	{
100439234Sgibbs		/* XXX Implement */
100539234Sgibbs		ccb->ccb_h.status = CAM_REQ_CMP;
100639234Sgibbs		xpt_done(ccb);
100739234Sgibbs		break;
100839234Sgibbs	}
100939234Sgibbs	case XPT_TERM_IO:		/* Terminate the I/O process */
101039234Sgibbs		/* XXX Implement */
101139234Sgibbs		ccb->ccb_h.status = CAM_REQ_INVALID;
101239234Sgibbs		xpt_done(ccb);
101339234Sgibbs		break;
101439234Sgibbs	case XPT_PATH_INQ:		/* Path routing inquiry */
101539234Sgibbs	{
101639234Sgibbs		struct ccb_pathinq *cpi = &ccb->cpi;
101739234Sgibbs
101839234Sgibbs		cpi->version_num = 1;
101939234Sgibbs		cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE;
102039234Sgibbs		if (dpt->max_id > 7)
102139234Sgibbs			cpi->hba_inquiry |= PI_WIDE_16;
102239234Sgibbs		cpi->target_sprt = 0;
102339234Sgibbs		cpi->hba_misc = 0;
102439234Sgibbs		cpi->hba_eng_cnt = 0;
102539234Sgibbs		cpi->max_target = dpt->max_id;
102639234Sgibbs		cpi->max_lun = dpt->max_lun;
102739234Sgibbs		cpi->initiator_id = dpt->hostid[cam_sim_bus(sim)];
102839234Sgibbs		cpi->bus_id = cam_sim_bus(sim);
102946581Sken		cpi->base_transfer_speed = 3300;
103039234Sgibbs		strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
103139234Sgibbs		strncpy(cpi->hba_vid, "DPT", HBA_IDLEN);
103239234Sgibbs		strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
103339234Sgibbs		cpi->unit_number = cam_sim_unit(sim);
1034163816Smjacob                cpi->transport = XPORT_SPI;
1035163816Smjacob                cpi->transport_version = 2;
1036163816Smjacob                cpi->protocol = PROTO_SCSI;
1037163816Smjacob                cpi->protocol_version = SCSI_REV_2;
103839234Sgibbs		cpi->ccb_h.status = CAM_REQ_CMP;
103939234Sgibbs		xpt_done(ccb);
104039234Sgibbs		break;
104139234Sgibbs	}
104239234Sgibbs	default:
104339234Sgibbs		ccb->ccb_h.status = CAM_REQ_INVALID;
104439234Sgibbs		xpt_done(ccb);
104539234Sgibbs		break;
104639234Sgibbs	}
104732801Sjulian}
104832801Sjulian
104939234Sgibbs/*
105039234Sgibbs * This routine will try to send an EATA command to the DPT HBA.
105139234Sgibbs * It will, by default, try 20,000 times, waiting 50us between tries.
105239234Sgibbs * It returns 0 on success and 1 on failure.
105339234Sgibbs */
105439234Sgibbsstatic int
105539234Sgibbsdpt_send_eata_command(dpt_softc_t *dpt, eata_ccb_t *cmd_block,
105639234Sgibbs		      u_int32_t cmd_busaddr, u_int command, u_int retries,
105739234Sgibbs		      u_int ifc, u_int code, u_int code2)
105832801Sjulian{
105939234Sgibbs	u_int	loop;
106039234Sgibbs
106139234Sgibbs	if (!retries)
106239234Sgibbs		retries = 20000;
106332801Sjulian
106439234Sgibbs	/*
106539234Sgibbs	 * I hate this polling nonsense. Wish there was a way to tell the DPT
106639234Sgibbs	 * to go get commands at its own pace,  or to interrupt when ready.
106739234Sgibbs	 * In the mean time we will measure how many itterations it really
106839234Sgibbs	 * takes.
106939234Sgibbs	 */
107039234Sgibbs	for (loop = 0; loop < retries; loop++) {
107139234Sgibbs		if ((dpt_inb(dpt, HA_RAUXSTAT) & HA_ABUSY) == 0)
107239234Sgibbs			break;
107332801Sjulian		else
107432801Sjulian			DELAY(50);
107532801Sjulian	}
107639234Sgibbs
107739234Sgibbs	if (loop < retries) {
107839234Sgibbs#ifdef DPT_MEASURE_PERFORMANCE
107939234Sgibbs		if (loop > dpt->performance.max_eata_tries)
108039234Sgibbs			dpt->performance.max_eata_tries = loop;
108139234Sgibbs
108239234Sgibbs		if (loop < dpt->performance.min_eata_tries)
108339234Sgibbs			dpt->performance.min_eata_tries = loop;
108439234Sgibbs#endif
108539234Sgibbs	} else {
108639234Sgibbs#ifdef DPT_MEASURE_PERFORMANCE
108739234Sgibbs		++dpt->performance.command_too_busy;
108839234Sgibbs#endif
108939234Sgibbs		return (1);
109039234Sgibbs	}
109139234Sgibbs
109239234Sgibbs	/* The controller is alive, advance the wedge timer */
109339234Sgibbs#ifdef DPT_RESET_HBA
109439234Sgibbs	dpt->last_contact = microtime_now;
109539234Sgibbs#endif
109639234Sgibbs
109739234Sgibbs	if (cmd_block == NULL)
109839234Sgibbs		cmd_busaddr = 0;
109939234Sgibbs#if (BYTE_ORDER == BIG_ENDIAN)
110039234Sgibbs	else {
110139234Sgibbs		cmd_busaddr = ((cmd_busaddr >> 24) & 0xFF)
110239234Sgibbs			    | ((cmd_busaddr >> 16) & 0xFF)
110339234Sgibbs			    | ((cmd_busaddr >> 8) & 0xFF)
110439234Sgibbs			    | (cmd_busaddr & 0xFF);
110539234Sgibbs	}
110639234Sgibbs#endif
110739234Sgibbs	/* And now the address */
110839234Sgibbs	dpt_outl(dpt, HA_WDMAADDR, cmd_busaddr);
110939234Sgibbs
111039234Sgibbs	if (command == EATA_CMD_IMMEDIATE) {
111139234Sgibbs		if (cmd_block == NULL) {
111239234Sgibbs			dpt_outb(dpt, HA_WCODE2, code2);
111339234Sgibbs			dpt_outb(dpt, HA_WCODE, code);
111439234Sgibbs		}
111539234Sgibbs		dpt_outb(dpt, HA_WIFC, ifc);
111639234Sgibbs	}
111739234Sgibbs	dpt_outb(dpt, HA_WCOMMAND, command);
111839234Sgibbs
111939234Sgibbs	return (0);
112032801Sjulian}
112132801Sjulian
112239234Sgibbs
112339234Sgibbs/* ==================== Exported Function definitions =======================*/
1124112780Smdoddvoid
1125112780Smdodddpt_alloc(device_t dev)
112632801Sjulian{
112759078Smdodd	dpt_softc_t	*dpt = device_get_softc(dev);
112839234Sgibbs	int    i;
112939234Sgibbs
1130241593Sjhb	mtx_init(&dpt->lock, "dpt", NULL, MTX_DEF);
113139234Sgibbs	SLIST_INIT(&dpt->free_dccb_list);
113239234Sgibbs	LIST_INIT(&dpt->pending_ccb_list);
113339234Sgibbs	for (i = 0; i < MAX_CHANNELS; i++)
113439234Sgibbs		dpt->resetlevel[i] = DPT_HA_OK;
113539234Sgibbs
113639234Sgibbs#ifdef DPT_MEASURE_PERFORMANCE
113739234Sgibbs	dpt_reset_performance(dpt);
113839234Sgibbs#endif /* DPT_MEASURE_PERFORMANCE */
1139112780Smdodd	return;
114039234Sgibbs}
114139234Sgibbs
114239234Sgibbsvoid
114339234Sgibbsdpt_free(struct dpt_softc *dpt)
114439234Sgibbs{
114539234Sgibbs	switch (dpt->init_level) {
114639234Sgibbs	default:
114739234Sgibbs	case 5:
114839234Sgibbs		bus_dmamap_unload(dpt->dccb_dmat, dpt->dccb_dmamap);
114939234Sgibbs	case 4:
115039234Sgibbs		bus_dmamem_free(dpt->dccb_dmat, dpt->dpt_dccbs,
115139234Sgibbs				dpt->dccb_dmamap);
115239234Sgibbs		bus_dmamap_destroy(dpt->dccb_dmat, dpt->dccb_dmamap);
115339234Sgibbs	case 3:
115439234Sgibbs		bus_dma_tag_destroy(dpt->dccb_dmat);
115539234Sgibbs	case 2:
115639234Sgibbs		bus_dma_tag_destroy(dpt->buffer_dmat);
115739234Sgibbs	case 1:
115839234Sgibbs	{
115939234Sgibbs		struct sg_map_node *sg_map;
116039234Sgibbs
116139234Sgibbs		while ((sg_map = SLIST_FIRST(&dpt->sg_maps)) != NULL) {
116239234Sgibbs			SLIST_REMOVE_HEAD(&dpt->sg_maps, links);
116339234Sgibbs			bus_dmamap_unload(dpt->sg_dmat,
116439234Sgibbs					  sg_map->sg_dmamap);
116539234Sgibbs			bus_dmamem_free(dpt->sg_dmat, sg_map->sg_vaddr,
116639234Sgibbs					sg_map->sg_dmamap);
116739234Sgibbs			free(sg_map, M_DEVBUF);
116839234Sgibbs		}
116939234Sgibbs		bus_dma_tag_destroy(dpt->sg_dmat);
117039234Sgibbs	}
117139234Sgibbs	case 0:
117239234Sgibbs		break;
117339234Sgibbs	}
1174241593Sjhb	mtx_destroy(&dpt->lock);
117539234Sgibbs}
117639234Sgibbs
1177112780Smdoddint
1178112780Smdodddpt_alloc_resources (device_t dev)
1179112780Smdodd{
1180112780Smdodd	dpt_softc_t *	dpt;
1181112780Smdodd	int		error;
1182112780Smdodd
1183112780Smdodd	dpt = device_get_softc(dev);
1184112780Smdodd
1185127135Snjl	dpt->io_res = bus_alloc_resource_any(dev, dpt->io_type, &dpt->io_rid,
1186127135Snjl					     RF_ACTIVE);
1187112780Smdodd	if (dpt->io_res == NULL) {
1188112780Smdodd		device_printf(dev, "No I/O space?!\n");
1189112780Smdodd		error = ENOMEM;
1190112780Smdodd		goto bad;
1191112780Smdodd	}
1192112780Smdodd
1193127135Snjl	dpt->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &dpt->irq_rid,
1194127135Snjl					      RF_ACTIVE);
1195112780Smdodd	if (dpt->irq_res == NULL) {
1196112780Smdodd		device_printf(dev, "No IRQ!\n");
1197112780Smdodd		error = ENOMEM;
1198112780Smdodd		goto bad;
1199112780Smdodd	}
1200112780Smdodd
1201112780Smdodd	return (0);
1202112780Smdoddbad:
1203112780Smdodd	return(error);
1204112780Smdodd}
1205112780Smdodd
1206112780Smdodd
1207112780Smdoddvoid
1208112780Smdodddpt_release_resources (device_t dev)
1209112780Smdodd{
1210112780Smdodd	struct dpt_softc *	dpt;
1211112780Smdodd
1212112780Smdodd	dpt = device_get_softc(dev);
1213112780Smdodd
1214112780Smdodd	if (dpt->ih)
1215112780Smdodd		bus_teardown_intr(dev, dpt->irq_res, dpt->ih);
1216112780Smdodd        if (dpt->io_res)
1217112780Smdodd                bus_release_resource(dev, dpt->io_type, dpt->io_rid, dpt->io_res);
1218112780Smdodd        if (dpt->irq_res)
1219112780Smdodd                bus_release_resource(dev, SYS_RES_IRQ, dpt->irq_rid, dpt->irq_res);
1220112780Smdodd        if (dpt->drq_res)
1221112780Smdodd                bus_release_resource(dev, SYS_RES_DRQ, dpt->drq_rid, dpt->drq_res);
1222112780Smdodd
1223112780Smdodd	return;
1224112780Smdodd}
1225112780Smdodd
122641996Seivindstatic u_int8_t string_sizes[] =
122739234Sgibbs{
122839234Sgibbs	sizeof(((dpt_inq_t*)NULL)->vendor),
122939234Sgibbs	sizeof(((dpt_inq_t*)NULL)->modelNum),
123039234Sgibbs	sizeof(((dpt_inq_t*)NULL)->firmware),
123139234Sgibbs	sizeof(((dpt_inq_t*)NULL)->protocol),
123239234Sgibbs};
123339234Sgibbs
123439234Sgibbsint
123539234Sgibbsdpt_init(struct dpt_softc *dpt)
123639234Sgibbs{
123739234Sgibbs	dpt_conf_t  conf;
123839234Sgibbs	struct	    sg_map_node *sg_map;
123939234Sgibbs	dpt_ccb_t  *dccb;
124039234Sgibbs	u_int8_t   *strp;
124139234Sgibbs	int	    index;
124239234Sgibbs	int	    i;
124339234Sgibbs	int	    retval;
124439234Sgibbs
124541996Seivind	dpt->init_level = 0;
124641996Seivind	SLIST_INIT(&dpt->sg_maps);
1247241593Sjhb	mtx_lock(&dpt->lock);
124841996Seivind
124939234Sgibbs#ifdef DPT_RESET_BOARD
1250241593Sjhb	device_printf(dpt->dev, "resetting HBA\n");
125139234Sgibbs	dpt_outb(dpt, HA_WCOMMAND, EATA_CMD_RESET);
125239234Sgibbs	DELAY(750000);
125339234Sgibbs	/* XXX Shouldn't we poll a status register or something??? */
125439234Sgibbs#endif
125539234Sgibbs	/* DMA tag for our S/G structures.  We allocate in page sized chunks */
1256112782Smdodd	if (bus_dma_tag_create(	/* parent	*/ dpt->parent_dmat,
1257112782Smdodd				/* alignment	*/ 1,
1258112782Smdodd				/* boundary	*/ 0,
1259112782Smdodd				/* lowaddr	*/ BUS_SPACE_MAXADDR,
1260112782Smdodd				/* highaddr	*/ BUS_SPACE_MAXADDR,
1261112782Smdodd				/* filter	*/ NULL,
1262112782Smdodd				/* filterarg	*/ NULL,
1263112782Smdodd				/* maxsize	*/ PAGE_SIZE,
1264112782Smdodd				/* nsegments	*/ 1,
1265112782Smdodd				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
1266112782Smdodd				/* flags	*/ 0,
1267241593Sjhb				/* lockfunc	*/ NULL,
1268241593Sjhb				/* lockarg	*/ NULL,
1269112782Smdodd				&dpt->sg_dmat) != 0) {
127039234Sgibbs		goto error_exit;
127139234Sgibbs        }
127239234Sgibbs
127339234Sgibbs	dpt->init_level++;
127439234Sgibbs
127539234Sgibbs	/*
127639234Sgibbs	 * We allocate our DPT ccbs as a contiguous array of bus dma'able
127739234Sgibbs	 * memory.  To get the allocation size, we need to know how many
127839234Sgibbs	 * ccbs the card supports.  This requires a ccb.  We solve this
127939234Sgibbs	 * chicken and egg problem by allocating some re-usable S/G space
128039234Sgibbs	 * up front, and treating it as our status packet, CCB, and target
128139234Sgibbs	 * memory space for these commands.
128239234Sgibbs	 */
128339234Sgibbs	sg_map = dptallocsgmap(dpt);
128439234Sgibbs	if (sg_map == NULL)
128539234Sgibbs		goto error_exit;
128639234Sgibbs
128739234Sgibbs	dpt->sp = (volatile dpt_sp_t *)sg_map->sg_vaddr;
128864355Speter	dccb = (struct dpt_ccb *)(uintptr_t)(volatile void *)&dpt->sp[1];
128939234Sgibbs	bzero(dccb, sizeof(*dccb));
129039234Sgibbs	dpt->sp_physaddr = sg_map->sg_physaddr;
129139234Sgibbs	dccb->eata_ccb.cp_dataDMA =
129239234Sgibbs	    htonl(sg_map->sg_physaddr + sizeof(dpt_sp_t) + sizeof(*dccb));
129339234Sgibbs	dccb->eata_ccb.cp_busaddr = ~0;
129439234Sgibbs	dccb->eata_ccb.cp_statDMA = htonl(dpt->sp_physaddr);
129539234Sgibbs	dccb->eata_ccb.cp_reqDMA = htonl(dpt->sp_physaddr + sizeof(*dccb)
129639234Sgibbs				       + offsetof(struct dpt_ccb, sense_data));
129739234Sgibbs
129839234Sgibbs	/* Okay.  Fetch our config */
129939234Sgibbs	bzero(&dccb[1], sizeof(conf)); /* data area */
130039234Sgibbs	retval = dpt_get_conf(dpt, dccb, sg_map->sg_physaddr + sizeof(dpt_sp_t),
130139234Sgibbs			      sizeof(conf), 0xc1, 7, 1);
130239234Sgibbs
130339234Sgibbs	if (retval != 0) {
1304241593Sjhb		device_printf(dpt->dev, "Failed to get board configuration\n");
1305241593Sjhb		goto error_exit;
130639234Sgibbs	}
130739234Sgibbs	bcopy(&dccb[1], &conf, sizeof(conf));
130839234Sgibbs
130939234Sgibbs	bzero(&dccb[1], sizeof(dpt->board_data));
131039234Sgibbs	retval = dpt_get_conf(dpt, dccb, sg_map->sg_physaddr + sizeof(dpt_sp_t),
131139234Sgibbs			      sizeof(dpt->board_data), 0, conf.scsi_id0, 0);
131239234Sgibbs	if (retval != 0) {
1313241593Sjhb		device_printf(dpt->dev, "Failed to get inquiry information\n");
1314241593Sjhb		goto error_exit;
131539234Sgibbs	}
131639234Sgibbs	bcopy(&dccb[1], &dpt->board_data, sizeof(dpt->board_data));
131739234Sgibbs
131839234Sgibbs	dpt_detect_cache(dpt, dccb, sg_map->sg_physaddr + sizeof(dpt_sp_t),
131939234Sgibbs			 (u_int8_t *)&dccb[1]);
132039234Sgibbs
132139234Sgibbs	switch (ntohl(conf.splen)) {
132239234Sgibbs	case DPT_EATA_REVA:
132339234Sgibbs		dpt->EATA_revision = 'a';
132439234Sgibbs		break;
132539234Sgibbs	case DPT_EATA_REVB:
132639234Sgibbs		dpt->EATA_revision = 'b';
132739234Sgibbs		break;
132839234Sgibbs	case DPT_EATA_REVC:
132939234Sgibbs		dpt->EATA_revision = 'c';
133039234Sgibbs		break;
133139234Sgibbs	case DPT_EATA_REVZ:
133239234Sgibbs		dpt->EATA_revision = 'z';
133339234Sgibbs		break;
133439234Sgibbs	default:
133539234Sgibbs		dpt->EATA_revision = '?';
133639234Sgibbs	}
133739234Sgibbs
133839234Sgibbs	dpt->max_id	 = conf.MAX_ID;
133939234Sgibbs	dpt->max_lun	 = conf.MAX_LUN;
134039234Sgibbs	dpt->irq	 = conf.IRQ;
134139234Sgibbs	dpt->dma_channel = (8 - conf.DMA_channel) & 7;
134239234Sgibbs	dpt->channels	 = conf.MAX_CHAN + 1;
134339234Sgibbs	dpt->state	|= DPT_HA_OK;
134439234Sgibbs	if (conf.SECOND)
134539234Sgibbs		dpt->primary = FALSE;
134632801Sjulian	else
134739234Sgibbs		dpt->primary = TRUE;
134839234Sgibbs
134939234Sgibbs	dpt->more_support = conf.MORE_support;
135039234Sgibbs
135139234Sgibbs	if (strncmp(dpt->board_data.firmware, "07G0", 4) >= 0)
135239234Sgibbs		dpt->immediate_support = 1;
135339234Sgibbs	else
135439234Sgibbs		dpt->immediate_support = 0;
135539234Sgibbs
135639234Sgibbs	dpt->broken_INQUIRY = FALSE;
135739234Sgibbs
135839234Sgibbs	dpt->cplen = ntohl(conf.cplen);
135939234Sgibbs	dpt->cppadlen = ntohs(conf.cppadlen);
136039234Sgibbs	dpt->max_dccbs = ntohs(conf.queuesiz);
136139234Sgibbs
136239515Sgibbs	if (dpt->max_dccbs > 256) {
1363241593Sjhb		device_printf(dpt->dev, "Max CCBs reduced from %d to "
1364241593Sjhb		       "256 due to tag algorithm\n", dpt->max_dccbs);
136539515Sgibbs		dpt->max_dccbs = 256;
136639515Sgibbs	}
136739515Sgibbs
136839234Sgibbs	dpt->hostid[0] = conf.scsi_id0;
136939234Sgibbs	dpt->hostid[1] = conf.scsi_id1;
137039234Sgibbs	dpt->hostid[2] = conf.scsi_id2;
137139234Sgibbs
137239553Sgibbs	if (conf.SG_64K)
137339553Sgibbs		dpt->sgsize = 8192;
137439553Sgibbs	else
137539234Sgibbs		dpt->sgsize = ntohs(conf.SGsiz);
137639234Sgibbs
137739553Sgibbs	/* We can only get 64k buffers, so don't bother to waste space. */
137839553Sgibbs	if (dpt->sgsize < 17 || dpt->sgsize > 32)
137939553Sgibbs		dpt->sgsize = 32;
138039553Sgibbs
138139234Sgibbs	if (dpt->sgsize > dpt_max_segs)
138239234Sgibbs		dpt->sgsize = dpt_max_segs;
138339234Sgibbs
138439234Sgibbs	/* DMA tag for mapping buffers into device visible space. */
1385112782Smdodd	if (bus_dma_tag_create(	/* parent	*/ dpt->parent_dmat,
1386112782Smdodd				/* alignment	*/ 1,
1387112782Smdodd				/* boundary	*/ 0,
1388112782Smdodd				/* lowaddr	*/ BUS_SPACE_MAXADDR,
1389112782Smdodd				/* highaddr	*/ BUS_SPACE_MAXADDR,
1390112782Smdodd				/* filter	*/ NULL,
1391112782Smdodd				/* filterarg	*/ NULL,
1392281826Smav				/* maxsize	*/ DFLTPHYS,
1393112782Smdodd				/* nsegments	*/ dpt->sgsize,
1394112782Smdodd				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
1395112782Smdodd				/* flags	*/ BUS_DMA_ALLOCNOW,
1396117126Sscottl				/* lockfunc	*/ busdma_lock_mutex,
1397241593Sjhb				/* lockarg	*/ &dpt->lock,
1398112782Smdodd				&dpt->buffer_dmat) != 0) {
1399241593Sjhb		device_printf(dpt->dev,
1400241593Sjhb		    "bus_dma_tag_create(...,dpt->buffer_dmat) failed\n");
140139234Sgibbs		goto error_exit;
140239234Sgibbs	}
140339234Sgibbs
140439234Sgibbs	dpt->init_level++;
140539234Sgibbs
140639234Sgibbs	/* DMA tag for our ccb structures and interrupt status packet */
1407112782Smdodd	if (bus_dma_tag_create(	/* parent	*/ dpt->parent_dmat,
1408112782Smdodd				/* alignment	*/ 1,
1409112782Smdodd				/* boundary	*/ 0,
1410112782Smdodd				/* lowaddr	*/ BUS_SPACE_MAXADDR,
1411112782Smdodd				/* highaddr	*/ BUS_SPACE_MAXADDR,
1412112782Smdodd				/* filter	*/ NULL,
1413112782Smdodd				/* filterarg	*/ NULL,
1414112782Smdodd				/* maxsize	*/ (dpt->max_dccbs *
1415112782Smdodd						    sizeof(struct dpt_ccb)) +
1416112782Smdodd						    sizeof(dpt_sp_t),
1417112782Smdodd				/* nsegments	*/ 1,
1418112782Smdodd				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
1419112782Smdodd				/* flags	*/ 0,
1420241593Sjhb				/* lockfunc	*/ NULL,
1421241593Sjhb				/* lockarg	*/ NULL,
1422112782Smdodd				&dpt->dccb_dmat) != 0) {
1423241593Sjhb		device_printf(dpt->dev,
1424241593Sjhb		    "bus_dma_tag_create(...,dpt->dccb_dmat) failed\n");
142539234Sgibbs		goto error_exit;
142639234Sgibbs        }
142739234Sgibbs
142839234Sgibbs	dpt->init_level++;
142939234Sgibbs
143039234Sgibbs	/* Allocation for our ccbs and interrupt status packet */
143139234Sgibbs	if (bus_dmamem_alloc(dpt->dccb_dmat, (void **)&dpt->dpt_dccbs,
143239234Sgibbs			     BUS_DMA_NOWAIT, &dpt->dccb_dmamap) != 0) {
1433241593Sjhb		device_printf(dpt->dev,
1434241593Sjhb		    "bus_dmamem_alloc(dpt->dccb_dmat,...) failed\n");
143539234Sgibbs		goto error_exit;
143639234Sgibbs	}
143739234Sgibbs
143839234Sgibbs	dpt->init_level++;
143939234Sgibbs
144039234Sgibbs	/* And permanently map them */
144139234Sgibbs	bus_dmamap_load(dpt->dccb_dmat, dpt->dccb_dmamap,
144239234Sgibbs       			dpt->dpt_dccbs,
144339234Sgibbs			(dpt->max_dccbs * sizeof(struct dpt_ccb))
144439234Sgibbs			+ sizeof(dpt_sp_t),
144539234Sgibbs			dptmapmem, &dpt->dpt_ccb_busbase, /*flags*/0);
144639234Sgibbs
144739553Sgibbs	/* Clear them out. */
144839553Sgibbs	bzero(dpt->dpt_dccbs,
144939553Sgibbs	      (dpt->max_dccbs * sizeof(struct dpt_ccb)) + sizeof(dpt_sp_t));
145039553Sgibbs
145139234Sgibbs	dpt->dpt_ccb_busend = dpt->dpt_ccb_busbase;
145239234Sgibbs
145339234Sgibbs	dpt->sp = (dpt_sp_t*)&dpt->dpt_dccbs[dpt->max_dccbs];
145439234Sgibbs	dpt->sp_physaddr = dpt->dpt_ccb_busbase
145539234Sgibbs			 + (dpt->max_dccbs * sizeof(dpt_ccb_t));
145639234Sgibbs	dpt->init_level++;
145739234Sgibbs
145839234Sgibbs	/* Allocate our first batch of ccbs */
145959078Smdodd	if (dptallocccbs(dpt) == 0) {
1460241593Sjhb		device_printf(dpt->dev, "dptallocccbs(dpt) == 0\n");
1461241593Sjhb		mtx_unlock(&dpt->lock);
146239234Sgibbs		return (2);
146359078Smdodd	}
146439234Sgibbs
146539234Sgibbs	/* Prepare for Target Mode */
146639234Sgibbs	dpt->target_mode_enabled = 1;
146739234Sgibbs
146839234Sgibbs	/* Nuke excess spaces from inquiry information */
146939234Sgibbs	strp = dpt->board_data.vendor;
147039234Sgibbs	for (i = 0; i < sizeof(string_sizes); i++) {
147139234Sgibbs		index = string_sizes[i] - 1;
147239234Sgibbs		while (index && (strp[index] == ' '))
147339234Sgibbs			strp[index--] = '\0';
147439234Sgibbs		strp += string_sizes[i];
147539234Sgibbs	}
147639234Sgibbs
1477241593Sjhb	device_printf(dpt->dev, "%.8s %.16s FW Rev. %.4s, ",
1478241593Sjhb	       dpt->board_data.vendor,
147939234Sgibbs	       dpt->board_data.modelNum, dpt->board_data.firmware);
148039234Sgibbs
148139234Sgibbs	printf("%d channel%s, ", dpt->channels, dpt->channels > 1 ? "s" : "");
148239234Sgibbs
148339234Sgibbs	if (dpt->cache_type != DPT_NO_CACHE
148439234Sgibbs	 && dpt->cache_size != 0) {
148539234Sgibbs		printf("%s Cache, ",
148639234Sgibbs		       dpt->cache_type == DPT_CACHE_WRITETHROUGH
148739234Sgibbs		     ? "Write-Through" : "Write-Back");
148839234Sgibbs	}
148939234Sgibbs
149039234Sgibbs	printf("%d CCBs\n", dpt->max_dccbs);
1491241593Sjhb	mtx_unlock(&dpt->lock);
149239234Sgibbs	return (0);
149339234Sgibbs
149439234Sgibbserror_exit:
1495241593Sjhb	mtx_unlock(&dpt->lock);
149639234Sgibbs	return (1);
149732801Sjulian}
149832801Sjulian
149939234Sgibbsint
150039234Sgibbsdpt_attach(dpt_softc_t *dpt)
150132801Sjulian{
150239234Sgibbs	struct cam_devq *devq;
150339234Sgibbs	int i;
150439234Sgibbs
150539234Sgibbs	/*
150639234Sgibbs	 * Create the device queue for our SIM.
150739234Sgibbs	 */
150839234Sgibbs	devq = cam_simq_alloc(dpt->max_dccbs);
150939234Sgibbs	if (devq == NULL)
151032801Sjulian		return (0);
151139234Sgibbs
1512241593Sjhb	mtx_lock(&dpt->lock);
151339234Sgibbs	for (i = 0; i < dpt->channels; i++) {
151439234Sgibbs		/*
151539234Sgibbs		 * Construct our SIM entry
151639234Sgibbs		 */
151739234Sgibbs		dpt->sims[i] = cam_sim_alloc(dpt_action, dpt_poll, "dpt",
1518241593Sjhb		    dpt, device_get_unit(dpt->dev), &dpt->lock,
1519168752Sscottl					     /*untagged*/2,
152039234Sgibbs					     /*tagged*/dpt->max_dccbs, devq);
1521124612Smdodd		if (dpt->sims[i] == NULL) {
1522124612Smdodd			if (i == 0)
1523124612Smdodd				cam_simq_free(devq);
1524124612Smdodd			else
1525124612Smdodd				printf(	"%s(): Unable to attach bus %d "
1526124612Smdodd					"due to resource shortage\n",
1527124612Smdodd					__func__, i);
1528124612Smdodd			break;
1529124612Smdodd		}
1530124612Smdodd
1531170872Sscottl		if (xpt_bus_register(dpt->sims[i], dpt->dev, i) != CAM_SUCCESS){
153239234Sgibbs			cam_sim_free(dpt->sims[i], /*free_devq*/i == 0);
1533124612Smdodd			dpt->sims[i] = NULL;
153439234Sgibbs			break;
153539234Sgibbs		}
153639234Sgibbs
153739234Sgibbs		if (xpt_create_path(&dpt->paths[i], /*periph*/NULL,
153839234Sgibbs				    cam_sim_path(dpt->sims[i]),
153939234Sgibbs				    CAM_TARGET_WILDCARD,
154039234Sgibbs				    CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
154139234Sgibbs			xpt_bus_deregister(cam_sim_path(dpt->sims[i]));
154239234Sgibbs			cam_sim_free(dpt->sims[i], /*free_devq*/i == 0);
1543124612Smdodd			dpt->sims[i] = NULL;
154439234Sgibbs			break;
154539234Sgibbs		}
154639234Sgibbs
154739234Sgibbs	}
1548241593Sjhb	mtx_unlock(&dpt->lock);
154939234Sgibbs	if (i > 0)
155050107Smsmith		EVENTHANDLER_REGISTER(shutdown_final, dptshutdown,
155150107Smsmith				      dpt, SHUTDOWN_PRI_DEFAULT);
155239234Sgibbs	return (i);
155332801Sjulian}
155432801Sjulian
1555112780Smdoddint
1556112780Smdodddpt_detach (device_t dev)
1557112780Smdodd{
1558112780Smdodd	struct dpt_softc *	dpt;
1559112780Smdodd	int			i;
156039234Sgibbs
1561112780Smdodd	dpt = device_get_softc(dev);
1562112780Smdodd
1563241593Sjhb	mtx_lock(&dpt->lock);
1564112780Smdodd	for (i = 0; i < dpt->channels; i++) {
1565112780Smdodd#if 0
1566112780Smdodd	        xpt_async(AC_LOST_DEVICE, dpt->paths[i], NULL);
1567112780Smdodd#endif
1568112780Smdodd        	xpt_free_path(dpt->paths[i]);
1569112780Smdodd        	xpt_bus_deregister(cam_sim_path(dpt->sims[i]));
1570112780Smdodd        	cam_sim_free(dpt->sims[i], /*free_devq*/TRUE);
1571112780Smdodd	}
1572241593Sjhb	mtx_unlock(&dpt->lock);
1573112780Smdodd
1574112780Smdodd	dptshutdown((void *)dpt, SHUTDOWN_PRI_DEFAULT);
1575112780Smdodd
1576112780Smdodd	dpt_release_resources(dev);
1577112780Smdodd
1578112780Smdodd	dpt_free(dpt);
1579112780Smdodd
1580112780Smdodd	return (0);
1581112780Smdodd}
1582112780Smdodd
158339234Sgibbs/*
158439234Sgibbs * This is the interrupt handler for the DPT driver.
158539234Sgibbs */
158639234Sgibbsvoid
158739234Sgibbsdpt_intr(void *arg)
158839234Sgibbs{
158939234Sgibbs	dpt_softc_t    *dpt;
1590241593Sjhb
1591241593Sjhb	dpt = arg;
1592241593Sjhb	mtx_lock(&dpt->lock);
1593241593Sjhb	dpt_intr_locked(dpt);
1594241593Sjhb	mtx_unlock(&dpt->lock);
1595241593Sjhb}
1596241593Sjhb
1597241593Sjhbvoid
1598241593Sjhbdpt_intr_locked(dpt_softc_t *dpt)
1599241593Sjhb{
160039234Sgibbs	dpt_ccb_t      *dccb;
160139234Sgibbs	union ccb      *ccb;
160239234Sgibbs	u_int		status;
160339234Sgibbs	u_int		aux_status;
160439234Sgibbs	u_int		hba_stat;
160539234Sgibbs	u_int		scsi_stat;
160639234Sgibbs	u_int32_t	residue_len;	/* Number of bytes not transferred */
160739234Sgibbs
160839234Sgibbs	/* First order of business is to check if this interrupt is for us */
160939234Sgibbs	while (((aux_status = dpt_inb(dpt, HA_RAUXSTAT)) & HA_AIRQ) != 0) {
161039234Sgibbs
161139234Sgibbs		/*
161239234Sgibbs		 * What we want to do now, is to capture the status, all of it,
161339234Sgibbs		 * move it where it belongs, wake up whoever sleeps waiting to
161439234Sgibbs		 * process this result, and get out of here.
161539234Sgibbs		 */
161639234Sgibbs		if (dpt->sp->ccb_busaddr < dpt->dpt_ccb_busbase
161739234Sgibbs		 || dpt->sp->ccb_busaddr >= dpt->dpt_ccb_busend) {
1618241593Sjhb			device_printf(dpt->dev,
1619241593Sjhb			    "Encountered bogus status packet\n");
162039234Sgibbs			status = dpt_inb(dpt, HA_RSTATUS);
162139234Sgibbs			return;
162239234Sgibbs		}
162339234Sgibbs
162439234Sgibbs		dccb = dptccbptov(dpt, dpt->sp->ccb_busaddr);
162539234Sgibbs
162639234Sgibbs		dpt->sp->ccb_busaddr = ~0;
162739234Sgibbs
162839234Sgibbs		/* Ignore status packets with EOC not set */
162939234Sgibbs		if (dpt->sp->EOC == 0) {
1630241593Sjhb			device_printf(dpt->dev,
1631241593Sjhb			       "ERROR: Request %d received with "
163239234Sgibbs			       "clear EOC.\n     Marking as LOST.\n",
1633241593Sjhb			       dccb->transaction_id);
163439234Sgibbs
163539234Sgibbs			/* This CLEARS the interrupt! */
163639234Sgibbs			status = dpt_inb(dpt, HA_RSTATUS);
163739234Sgibbs			continue;
163839234Sgibbs		}
163939234Sgibbs		dpt->sp->EOC = 0;
164039234Sgibbs
164139234Sgibbs		/*
164239234Sgibbs		 * Double buffer the status information so the hardware can
164339234Sgibbs		 * work on updating the status packet while we decifer the
164439234Sgibbs		 * one we were just interrupted for.
164539234Sgibbs		 * According to Mark Salyzyn, we only need few pieces of it.
164639234Sgibbs		 */
164739234Sgibbs		hba_stat = dpt->sp->hba_stat;
164839234Sgibbs		scsi_stat = dpt->sp->scsi_stat;
164939234Sgibbs		residue_len = dpt->sp->residue_len;
165039234Sgibbs
165139234Sgibbs		/* Clear interrupts, check for error */
165239234Sgibbs		if ((status = dpt_inb(dpt, HA_RSTATUS)) & HA_SERROR) {
165339234Sgibbs			/*
165439234Sgibbs			 * Error Condition. Check for magic cookie. Exit
165539234Sgibbs			 * this test on earliest sign of non-reset condition
165639234Sgibbs			 */
165739234Sgibbs
165839234Sgibbs			/* Check that this is not a board reset interrupt */
165939234Sgibbs			if (dpt_just_reset(dpt)) {
1660241593Sjhb				device_printf(dpt->dev, "HBA rebooted.\n"
166139234Sgibbs				       "      All transactions should be "
1662241593Sjhb				       "resubmitted\n");
166339234Sgibbs
1664241593Sjhb				device_printf(dpt->dev,
1665241593Sjhb				       ">>---->>  This is incomplete, "
1666241593Sjhb				       "fix me....  <<----<<");
166739234Sgibbs				panic("DPT Rebooted");
166839234Sgibbs
166939234Sgibbs			}
167039234Sgibbs		}
167139234Sgibbs		/* Process CCB */
167239234Sgibbs		ccb = dccb->ccb;
1673241593Sjhb		callout_stop(&dccb->timer);
167439234Sgibbs		if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
1675115343Sscottl			bus_dmasync_op_t op;
167639234Sgibbs
167739234Sgibbs			if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
167839234Sgibbs				op = BUS_DMASYNC_POSTREAD;
167939234Sgibbs			else
168039234Sgibbs				op = BUS_DMASYNC_POSTWRITE;
168139234Sgibbs			bus_dmamap_sync(dpt->buffer_dmat, dccb->dmamap, op);
168239234Sgibbs			bus_dmamap_unload(dpt->buffer_dmat, dccb->dmamap);
168339234Sgibbs		}
168439234Sgibbs
168539234Sgibbs		/* Common Case inline... */
168639234Sgibbs		if (hba_stat == HA_NO_ERROR) {
168739234Sgibbs			ccb->csio.scsi_status = scsi_stat;
168839234Sgibbs			ccb->ccb_h.status = 0;
168939234Sgibbs			switch (scsi_stat) {
169039234Sgibbs			case SCSI_STATUS_OK:
169139234Sgibbs				ccb->ccb_h.status |= CAM_REQ_CMP;
169239234Sgibbs				break;
169339234Sgibbs			case SCSI_STATUS_CHECK_COND:
169439234Sgibbs			case SCSI_STATUS_CMD_TERMINATED:
169539234Sgibbs				bcopy(&dccb->sense_data, &ccb->csio.sense_data,
169639234Sgibbs				      ccb->csio.sense_len);
169739234Sgibbs				ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
169839234Sgibbs				/* FALLTHROUGH */
169939234Sgibbs			default:
170039234Sgibbs				ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
170139234Sgibbs				/* XXX Freeze DevQ */
170239234Sgibbs				break;
170339234Sgibbs			}
170439234Sgibbs			ccb->csio.resid = residue_len;
170539234Sgibbs			dptfreeccb(dpt, dccb);
170639234Sgibbs			xpt_done(ccb);
170739234Sgibbs		} else {
170839234Sgibbs			dptprocesserror(dpt, dccb, ccb, hba_stat, scsi_stat,
170939234Sgibbs					residue_len);
171039234Sgibbs		}
171139234Sgibbs	}
171239234Sgibbs}
171339234Sgibbs
171439234Sgibbsstatic void
171539234Sgibbsdptprocesserror(dpt_softc_t *dpt, dpt_ccb_t *dccb, union ccb *ccb,
171639234Sgibbs		u_int hba_stat, u_int scsi_stat, u_int32_t resid)
171739234Sgibbs{
171839234Sgibbs	ccb->csio.resid = resid;
171939234Sgibbs	switch (hba_stat) {
172039234Sgibbs	case HA_ERR_SEL_TO:
172139234Sgibbs		ccb->ccb_h.status = CAM_SEL_TIMEOUT;
172239234Sgibbs		break;
172339234Sgibbs	case HA_ERR_CMD_TO:
172439234Sgibbs		ccb->ccb_h.status = CAM_CMD_TIMEOUT;
172539234Sgibbs		break;
172639234Sgibbs	case HA_SCSIBUS_RESET:
172739234Sgibbs	case HA_HBA_POWER_UP:	/* Similar effect to a bus reset??? */
172839234Sgibbs		ccb->ccb_h.status = CAM_SCSI_BUS_RESET;
172939234Sgibbs		break;
173039234Sgibbs	case HA_CP_ABORTED:
173139234Sgibbs	case HA_CP_RESET:	/* XXX ??? */
173239234Sgibbs	case HA_CP_ABORT_NA:	/* XXX ??? */
173339234Sgibbs	case HA_CP_RESET_NA:	/* XXX ??? */
173439515Sgibbs		if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)
173539515Sgibbs			ccb->ccb_h.status = CAM_REQ_ABORTED;
173639234Sgibbs		break;
173739234Sgibbs	case HA_PCI_PARITY:
173839234Sgibbs	case HA_PCI_MABORT:
173939234Sgibbs	case HA_PCI_TABORT:
174039234Sgibbs	case HA_PCI_STABORT:
174139234Sgibbs	case HA_BUS_PARITY:
174239234Sgibbs	case HA_PARITY_ERR:
174339234Sgibbs	case HA_ECC_ERR:
174439234Sgibbs		ccb->ccb_h.status = CAM_UNCOR_PARITY;
174539234Sgibbs		break;
174639234Sgibbs	case HA_UNX_MSGRJCT:
174739234Sgibbs		ccb->ccb_h.status = CAM_MSG_REJECT_REC;
174839234Sgibbs		break;
174939234Sgibbs	case HA_UNX_BUSPHASE:
175039234Sgibbs		ccb->ccb_h.status = CAM_SEQUENCE_FAIL;
175139234Sgibbs		break;
175239234Sgibbs	case HA_UNX_BUS_FREE:
175339234Sgibbs		ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
175439234Sgibbs		break;
175539234Sgibbs	case HA_SCSI_HUNG:
175639234Sgibbs	case HA_RESET_STUCK:
175739234Sgibbs		/*
175839234Sgibbs		 * Dead???  Can the controller get unstuck
175939234Sgibbs		 * from these conditions
176039234Sgibbs		 */
176139234Sgibbs		ccb->ccb_h.status = CAM_NO_HBA;
176239234Sgibbs		break;
176339234Sgibbs	case HA_RSENSE_FAIL:
176439234Sgibbs		ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
176539234Sgibbs		break;
176639234Sgibbs	default:
1767241593Sjhb		device_printf(dpt->dev, "Undocumented Error %x\n", hba_stat);
176840134Sgibbs		printf("Please mail this message to shimon@simon-shapiro.org\n");
176939234Sgibbs		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
177039234Sgibbs		break;
177139234Sgibbs	}
177239234Sgibbs	dptfreeccb(dpt, dccb);
177339234Sgibbs	xpt_done(ccb);
177439234Sgibbs}
177539234Sgibbs
177639234Sgibbsstatic void
177739234Sgibbsdpttimeout(void *arg)
177839234Sgibbs{
177939234Sgibbs	struct dpt_ccb	 *dccb;
178039234Sgibbs	union  ccb	 *ccb;
178139234Sgibbs	struct dpt_softc *dpt;
178239234Sgibbs
178339234Sgibbs	dccb = (struct dpt_ccb *)arg;
178439234Sgibbs	ccb = dccb->ccb;
178539234Sgibbs	dpt = (struct dpt_softc *)ccb->ccb_h.ccb_dpt_ptr;
1786241593Sjhb	mtx_assert(&dpt->lock, MA_OWNED);
178739234Sgibbs	xpt_print_path(ccb->ccb_h.path);
178839515Sgibbs	printf("CCB %p - timed out\n", (void *)dccb);
178939234Sgibbs
179040134Sgibbs	/*
1791160964Syar	 * Try to clear any pending jobs.  FreeBSD will lose interrupts,
179240134Sgibbs	 * leaving the controller suspended, and commands timed-out.
179340134Sgibbs	 * By calling the interrupt handler, any command thus stuck will be
179440134Sgibbs	 * completed.
179540134Sgibbs	 */
1796241593Sjhb	dpt_intr_locked(dpt);
179740134Sgibbs
179839234Sgibbs	if ((dccb->state & DCCB_ACTIVE) == 0) {
179939234Sgibbs		xpt_print_path(ccb->ccb_h.path);
180039515Sgibbs		printf("CCB %p - timed out CCB already completed\n",
180139515Sgibbs		       (void *)dccb);
180239234Sgibbs		return;
180339234Sgibbs	}
180439234Sgibbs
180540134Sgibbs	/* Abort this particular command.  Leave all others running */
180639234Sgibbs	dpt_send_immediate(dpt, &dccb->eata_ccb, dccb->eata_ccb.cp_busaddr,
180739234Sgibbs			   /*retries*/20000, EATA_SPECIFIC_ABORT, 0, 0);
180839234Sgibbs	ccb->ccb_h.status = CAM_CMD_TIMEOUT;
180939234Sgibbs}
181039234Sgibbs
181139234Sgibbs/*
181239234Sgibbs * Shutdown the controller and ensure that the cache is completely flushed.
181350107Smsmith * Called from the shutdown_final event after all disk access has completed.
181439234Sgibbs */
181539234Sgibbsstatic void
181650107Smsmithdptshutdown(void *arg, int howto)
181739234Sgibbs{
181839234Sgibbs	dpt_softc_t *dpt;
181939234Sgibbs
182039234Sgibbs	dpt = (dpt_softc_t *)arg;
182139234Sgibbs
1822241593Sjhb	device_printf(dpt->dev,
1823241593Sjhb	    "Shutting down (mode %x) HBA.	Please wait...\n", howto);
182439234Sgibbs
182539234Sgibbs	/*
182639234Sgibbs	 * What we do for a shutdown, is give the DPT early power loss warning
182739234Sgibbs	 */
1828241593Sjhb	mtx_lock(&dpt->lock);
182939234Sgibbs	dpt_send_immediate(dpt, NULL, 0, EATA_POWER_OFF_WARN, 0, 0, 0);
1830241593Sjhb	mtx_unlock(&dpt->lock);
183139234Sgibbs	DELAY(1000 * 1000 * 5);
1832241593Sjhb	device_printf(dpt->dev, "Controller was warned of shutdown and is now "
1833241593Sjhb	       "disabled\n");
183439234Sgibbs}
183539234Sgibbs
183639234Sgibbs/*============================================================================*/
183739234Sgibbs
183839234Sgibbs#if 0
183938115Seivind#ifdef DPT_RESET_HBA
184038115Seivind
184138115Seivind/*
184238115Seivind**	Function name : dpt_reset_hba
184338115Seivind**
184438115Seivind**	Description : Reset the HBA and properly discard all pending work
184539234Sgibbs**	Input :       Softc
184638115Seivind**	Output :      Nothing
184738115Seivind*/
184838115Seivindstatic void
184938147Seivinddpt_reset_hba(dpt_softc_t *dpt)
185038115Seivind{
185139234Sgibbs	eata_ccb_t       *ccb;
185239234Sgibbs	dpt_ccb_t         dccb, *dccbp;
185339234Sgibbs	int               result;
185439234Sgibbs	struct scsi_xfer *xs;
1855241593Sjhb
1856241593Sjhb	mtx_assert(&dpt->lock, MA_OWNED);
1857241593Sjhb
185839234Sgibbs	/* Prepare a control block.  The SCSI command part is immaterial */
185939234Sgibbs	dccb.xs = NULL;
186039234Sgibbs	dccb.flags = 0;
186139234Sgibbs	dccb.state = DPT_CCB_STATE_NEW;
186239234Sgibbs	dccb.std_callback = NULL;
186339234Sgibbs	dccb.wrbuff_callback = NULL;
186438115Seivind
186539234Sgibbs	ccb = &dccb.eata_ccb;
186639234Sgibbs	ccb->CP_OpCode = EATA_CMD_RESET;
186739234Sgibbs	ccb->SCSI_Reset = 0;
186839234Sgibbs	ccb->HBA_Init = 1;
186939234Sgibbs	ccb->Auto_Req_Sen = 1;
187039234Sgibbs	ccb->cp_id = 0; /* Should be ignored */
187139234Sgibbs	ccb->DataIn = 1;
187239234Sgibbs	ccb->DataOut = 0;
187339234Sgibbs	ccb->Interpret = 1;
187439234Sgibbs	ccb->reqlen = htonl(sizeof(struct scsi_sense_data));
187539234Sgibbs	ccb->cp_statDMA = htonl(vtophys(&ccb->cp_statDMA));
187639234Sgibbs	ccb->cp_reqDMA = htonl(vtophys(&ccb->cp_reqDMA));
187739234Sgibbs	ccb->cp_viraddr = (u_int32_t) & ccb;
187838115Seivind
187939234Sgibbs	ccb->cp_msg[0] = HA_IDENTIFY_MSG | HA_DISCO_RECO;
188039234Sgibbs	ccb->cp_scsi_cmd = 0;  /* Should be ignored */
188138115Seivind
188239234Sgibbs	/* Lock up the submitted queue.  We are very persistant here */
188339234Sgibbs	while (dpt->queue_status & DPT_SUBMITTED_QUEUE_ACTIVE) {
188439234Sgibbs		DELAY(100);
188539234Sgibbs	}
188638115Seivind
188739234Sgibbs	dpt->queue_status |= DPT_SUBMITTED_QUEUE_ACTIVE;
188838115Seivind
188939234Sgibbs	/* Send the RESET message */
189039234Sgibbs	if ((result = dpt_send_eata_command(dpt, &dccb.eata_ccb,
189139234Sgibbs					    EATA_CMD_RESET, 0, 0, 0, 0)) != 0) {
1892241593Sjhb		device_printf(dpt->dev, "Failed to send the RESET message.\n"
1893241593Sjhb		       "     Trying cold boot (ouch!)\n");
189438115Seivind
189538115Seivind
189639234Sgibbs		if ((result = dpt_send_eata_command(dpt, &dccb.eata_ccb,
189739234Sgibbs						    EATA_COLD_BOOT, 0, 0,
189839234Sgibbs						    0, 0)) != 0) {
1899241593Sjhb			panic("%s:  Faild to cold boot the HBA\n",
1900241593Sjhb			    device_get_nameunit(dpt->dev));
190139234Sgibbs		}
190238115Seivind#ifdef DPT_MEASURE_PERFORMANCE
190339234Sgibbs		dpt->performance.cold_boots++;
190438115Seivind#endif /* DPT_MEASURE_PERFORMANCE */
190539234Sgibbs	}
190638115Seivind
190738115Seivind#ifdef DPT_MEASURE_PERFORMANCE
190838115Seivind	dpt->performance.warm_starts++;
190938115Seivind#endif /* DPT_MEASURE_PERFORMANCE */
191038115Seivind
1911241593Sjhb	device_printf(dpt->dev,
1912241593Sjhb	    "Aborting pending requests.  O/S should re-submit\n");
191338115Seivind
191439234Sgibbs	while ((dccbp = TAILQ_FIRST(&dpt->completed_ccbs)) != NULL) {
191539234Sgibbs		struct scsi_xfer *xs = dccbp->xs;
191638115Seivind
191739234Sgibbs		/* Not all transactions have xs structs */
191839234Sgibbs		if (xs != NULL) {
191939234Sgibbs			/* Tell the kernel proper this did not complete well */
192039234Sgibbs			xs->error |= XS_SELTIMEOUT;
192139234Sgibbs			xs->flags |= SCSI_ITSDONE;
192239234Sgibbs			scsi_done(xs);
192339234Sgibbs		}
192438115Seivind
192539234Sgibbs		dpt_Qremove_submitted(dpt, dccbp);
192638115Seivind
192739234Sgibbs		/* Remember, Callbacks are NOT in the standard queue */
192839234Sgibbs		if (dccbp->std_callback != NULL) {
192939234Sgibbs			(dccbp->std_callback)(dpt, dccbp->eata_ccb.cp_channel,
193039234Sgibbs					       dccbp);
193139234Sgibbs		} else {
193239234Sgibbs			dpt_Qpush_free(dpt, dccbp);
193339234Sgibbs		}
193438115Seivind	}
193539234Sgibbs
1936241593Sjhb	device_printf(dpt->dev, "reset done aborting all pending commands\n");
193739234Sgibbs	dpt->queue_status &= ~DPT_SUBMITTED_QUEUE_ACTIVE;
193838115Seivind}
193938115Seivind
194038115Seivind#endif /* DPT_RESET_HBA */
194138115Seivind
194239234Sgibbs/*
194332801Sjulian * Build a Command Block for target mode READ/WRITE BUFFER,
194432801Sjulian * with the ``sync'' bit ON.
194532801Sjulian *
194632801Sjulian * Although the length and offset are 24 bit fields in the command, they cannot
194732801Sjulian * exceed 8192 bytes, so we take them as short integers andcheck their range.
194839234Sgibbs * If they are sensless, we round them to zero offset, maximum length and
194939234Sgibbs * complain.
195032801Sjulian */
195132801Sjulian
195232801Sjulianstatic void
195332801Sjuliandpt_target_ccb(dpt_softc_t * dpt, int bus, u_int8_t target, u_int8_t lun,
195432801Sjulian	       dpt_ccb_t * ccb, int mode, u_int8_t command,
195532801Sjulian	       u_int16_t length, u_int16_t offset)
195632801Sjulian{
195732801Sjulian	eata_ccb_t     *cp;
195832801Sjulian
1959241593Sjhb	mtx_assert(&dpt->lock, MA_OWNED);
196032801Sjulian	if ((length + offset) > DPT_MAX_TARGET_MODE_BUFFER_SIZE) {
1961241593Sjhb		device_printf(dpt->dev,
1962241593Sjhb		    "Length of %d, and offset of %d are wrong\n",
1963241593Sjhb		    length, offset);
196432801Sjulian		length = DPT_MAX_TARGET_MODE_BUFFER_SIZE;
196532801Sjulian		offset = 0;
196632801Sjulian	}
196732801Sjulian	ccb->xs = NULL;
196832801Sjulian	ccb->flags = 0;
196932801Sjulian	ccb->state = DPT_CCB_STATE_NEW;
197032801Sjulian	ccb->std_callback = (ccb_callback) dpt_target_done;
197132801Sjulian	ccb->wrbuff_callback = NULL;
197232801Sjulian
197332801Sjulian	cp = &ccb->eata_ccb;
197432801Sjulian	cp->CP_OpCode = EATA_CMD_DMA_SEND_CP;
197532801Sjulian	cp->SCSI_Reset = 0;
197632801Sjulian	cp->HBA_Init = 0;
197732801Sjulian	cp->Auto_Req_Sen = 1;
197832801Sjulian	cp->cp_id = target;
197932801Sjulian	cp->DataIn = 1;
198032801Sjulian	cp->DataOut = 0;
198132801Sjulian	cp->Interpret = 0;
198232801Sjulian	cp->reqlen = htonl(sizeof(struct scsi_sense_data));
198332801Sjulian	cp->cp_statDMA = htonl(vtophys(&cp->cp_statDMA));
198432801Sjulian	cp->cp_reqDMA = htonl(vtophys(&cp->cp_reqDMA));
198532801Sjulian	cp->cp_viraddr = (u_int32_t) & ccb;
198632801Sjulian
198732801Sjulian	cp->cp_msg[0] = HA_IDENTIFY_MSG | HA_DISCO_RECO;
198832801Sjulian
198932801Sjulian	cp->cp_scsi_cmd = command;
199032801Sjulian	cp->cp_cdb[1] = (u_int8_t) (mode & SCSI_TM_MODE_MASK);
199132801Sjulian	cp->cp_lun = lun;	/* Order is important here! */
199232801Sjulian	cp->cp_cdb[2] = 0x00;	/* Buffer Id, only 1 :-( */
199332801Sjulian	cp->cp_cdb[3] = (length >> 16) & 0xFF;	/* Buffer offset MSB */
199432801Sjulian	cp->cp_cdb[4] = (length >> 8) & 0xFF;
199532801Sjulian	cp->cp_cdb[5] = length & 0xFF;
199632801Sjulian	cp->cp_cdb[6] = (length >> 16) & 0xFF;	/* Length MSB */
199732801Sjulian	cp->cp_cdb[7] = (length >> 8) & 0xFF;
199832801Sjulian	cp->cp_cdb[8] = length & 0xFF;	/* Length LSB */
199932801Sjulian	cp->cp_cdb[9] = 0;	/* No sync, no match bits */
200032801Sjulian
200139234Sgibbs	/*
200232801Sjulian	 * This could be optimized to live in dpt_register_buffer.
200339234Sgibbs	 * We keep it here, just in case the kernel decides to reallocate pages
200432801Sjulian	 */
200532801Sjulian	if (dpt_scatter_gather(dpt, ccb, DPT_RW_BUFFER_SIZE,
200632801Sjulian			       dpt->rw_buffer[bus][target][lun])) {
2007241593Sjhb		device_printf(dpt->dev, "Failed to setup Scatter/Gather for "
2008241593Sjhb		       "Target-Mode buffer\n");
200932801Sjulian	}
201032801Sjulian}
201132801Sjulian
201232801Sjulian/* Setup a target mode READ command */
201332801Sjulian
201432801Sjulianstatic void
201532801Sjuliandpt_set_target(int redo, dpt_softc_t * dpt,
201632801Sjulian	       u_int8_t bus, u_int8_t target, u_int8_t lun, int mode,
201732801Sjulian	       u_int16_t length, u_int16_t offset, dpt_ccb_t * ccb)
201832801Sjulian{
201932801Sjulian
2020241593Sjhb	mtx_assert(&dpt->lock, MA_OWNED);
202132801Sjulian	if (dpt->target_mode_enabled) {
202232801Sjulian		if (!redo)
202332801Sjulian			dpt_target_ccb(dpt, bus, target, lun, ccb, mode,
202432801Sjulian				       SCSI_TM_READ_BUFFER, length, offset);
202532801Sjulian
202632801Sjulian		ccb->transaction_id = ++dpt->commands_processed;
202732801Sjulian
202832801Sjulian#ifdef DPT_MEASURE_PERFORMANCE
202938115Seivind		dpt->performance.command_count[ccb->eata_ccb.cp_scsi_cmd]++;
203038115Seivind		ccb->command_started = microtime_now;
203132801Sjulian#endif
203232801Sjulian		dpt_Qadd_waiting(dpt, ccb);
203332801Sjulian		dpt_sched_queue(dpt);
203432801Sjulian	} else {
2035241593Sjhb		device_printf(dpt->dev,
2036241593Sjhb		    "Target Mode Request, but Target Mode is OFF\n");
203732801Sjulian	}
203832801Sjulian}
203932801Sjulian
204039234Sgibbs/*
204132801Sjulian * Schedule a buffer to be sent to another target.
204238115Seivind * The work will be scheduled and the callback provided will be called when
204338115Seivind * the work is actually done.
204432801Sjulian *
204538115Seivind * Please NOTE:  ``Anyone'' can send a buffer, but only registered clients
204638115Seivind * get notified of receipt of buffers.
204732801Sjulian */
204832801Sjulian
204934480Sjulianint
205039234Sgibbsdpt_send_buffer(int unit, u_int8_t channel, u_int8_t target, u_int8_t lun,
205139234Sgibbs		u_int8_t mode, u_int16_t length, u_int16_t offset, void *data,
205232801Sjulian		buff_wr_done callback)
205332801Sjulian{
205432801Sjulian	dpt_softc_t    *dpt;
205532801Sjulian	dpt_ccb_t      *ccb = NULL;
205632801Sjulian
205732801Sjulian	/* This is an external call.  Be a bit paranoid */
2058241593Sjhb	dpt = devclass_get_device(dpt_devclass, unit);
2059241593Sjhb	if (dpt == NULL)
2060241593Sjhb		return (INVALID_UNIT);
206132801Sjulian
2062241593Sjhb	mtx_lock(&dpt->lock);
206332801Sjulian	if (dpt->target_mode_enabled) {
206432801Sjulian		if ((channel >= dpt->channels) || (target > dpt->max_id) ||
206532801Sjulian		    (lun > dpt->max_lun)) {
2066241593Sjhb			mtx_unlock(&dpt->lock);
206732801Sjulian			return (INVALID_SENDER);
206832801Sjulian		}
206932801Sjulian		if ((dpt->rw_buffer[channel][target][lun] == NULL) ||
2070241593Sjhb		    (dpt->buffer_receiver[channel][target][lun] == NULL)) {
2071241593Sjhb			mtx_unlock(&dpt->lock);
207232801Sjulian			return (NOT_REGISTERED);
2073241593Sjhb		}
207432801Sjulian
207532801Sjulian		/* Process the free list */
207632801Sjulian		if ((TAILQ_EMPTY(&dpt->free_ccbs)) && dpt_alloc_freelist(dpt)) {
2077241593Sjhb			device_printf(dpt->dev,
2078241593Sjhb			    "ERROR: Cannot allocate any more free CCB's.\n"
2079241593Sjhb			    "             Please try later\n");
2080241593Sjhb			mtx_unlock(&dpt->lock);
208132801Sjulian			return (NO_RESOURCES);
208232801Sjulian		}
208332801Sjulian		/* Now grab the newest CCB */
208432801Sjulian		if ((ccb = dpt_Qpop_free(dpt)) == NULL) {
2085241593Sjhb			mtx_unlock(&dpt->lock);
2086241593Sjhb			panic("%s: Got a NULL CCB from pop_free()\n",
2087241593Sjhb			    device_get_nameunit(dpt->dev));
208832801Sjulian		}
208932801Sjulian
209032801Sjulian		bcopy(dpt->rw_buffer[channel][target][lun] + offset, data, length);
209138115Seivind		dpt_target_ccb(dpt, channel, target, lun, ccb, mode,
209238115Seivind					   SCSI_TM_WRITE_BUFFER,
209338115Seivind					   length, offset);
209438115Seivind		ccb->std_callback = (ccb_callback) callback; /* Potential trouble */
209532801Sjulian
209632801Sjulian		ccb->transaction_id = ++dpt->commands_processed;
209732801Sjulian
209832801Sjulian#ifdef DPT_MEASURE_PERFORMANCE
209938115Seivind		dpt->performance.command_count[ccb->eata_ccb.cp_scsi_cmd]++;
210038115Seivind		ccb->command_started = microtime_now;
210132801Sjulian#endif
210232801Sjulian		dpt_Qadd_waiting(dpt, ccb);
210332801Sjulian		dpt_sched_queue(dpt);
210432801Sjulian
2105241593Sjhb		mtx_unlock(&dpt->lock);
210632801Sjulian		return (0);
210732801Sjulian	}
2108241593Sjhb	mtx_unlock(&dpt->lock);
210932801Sjulian	return (DRIVER_DOWN);
211032801Sjulian}
211132801Sjulian
211232801Sjulianstatic void
211332801Sjuliandpt_target_done(dpt_softc_t * dpt, int bus, dpt_ccb_t * ccb)
211432801Sjulian{
211532801Sjulian	eata_ccb_t     *cp;
211632801Sjulian
211732801Sjulian	cp = &ccb->eata_ccb;
211832801Sjulian
211938561Sgibbs	/*
212032801Sjulian	 * Remove the CCB from the waiting queue.
212132801Sjulian	 *  We do NOT put it back on the free, etc., queues as it is a special
212232801Sjulian	 * ccb, owned by the dpt_softc of this unit.
212332801Sjulian	 */
212432801Sjulian	dpt_Qremove_completed(dpt, ccb);
212532801Sjulian
212632801Sjulian#define br_channel           (ccb->eata_ccb.cp_channel)
212732801Sjulian#define br_target            (ccb->eata_ccb.cp_id)
212832801Sjulian#define br_lun               (ccb->eata_ccb.cp_LUN)
212932801Sjulian#define br_index	     [br_channel][br_target][br_lun]
213032801Sjulian#define read_buffer_callback (dpt->buffer_receiver br_index )
213132801Sjulian#define	read_buffer	     (dpt->rw_buffer[br_channel][br_target][br_lun])
213232801Sjulian#define cb(offset)           (ccb->eata_ccb.cp_cdb[offset])
213332801Sjulian#define br_offset            ((cb(3) << 16) | (cb(4) << 8) | cb(5))
213432801Sjulian#define br_length            ((cb(6) << 16) | (cb(7) << 8) | cb(8))
213532801Sjulian
213632801Sjulian	/* Different reasons for being here, you know... */
213732801Sjulian	switch (ccb->eata_ccb.cp_scsi_cmd) {
213832801Sjulian	case SCSI_TM_READ_BUFFER:
213932801Sjulian		if (read_buffer_callback != NULL) {
214032801Sjulian			/* This is a buffer generated by a kernel process */
2141241593Sjhb			read_buffer_callback(device_get_unit(dpt->dev),
2142241593Sjhb					     br_channel, br_target, br_lun,
214332801Sjulian					     read_buffer,
214432801Sjulian					     br_offset, br_length);
214532801Sjulian		} else {
214632801Sjulian			/*
214732801Sjulian			 * This is a buffer waited for by a user (sleeping)
214832801Sjulian			 * command
214932801Sjulian			 */
215032801Sjulian			wakeup(ccb);
215132801Sjulian		}
215232801Sjulian
215332801Sjulian		/* We ALWAYS re-issue the same command; args are don't-care  */
215432801Sjulian		dpt_set_target(1, 0, 0, 0, 0, 0, 0, 0, 0);
215532801Sjulian		break;
215632801Sjulian
215732801Sjulian	case SCSI_TM_WRITE_BUFFER:
2158241593Sjhb		(ccb->wrbuff_callback) (device_get_unit(dpt->dev), br_channel,
2159241593Sjhb					br_target, br_offset, br_length,
216032801Sjulian					br_lun, ccb->status_packet.hba_stat);
216132801Sjulian		break;
216232801Sjulian	default:
2163241593Sjhb		device_printf(dpt->dev,
2164241593Sjhb		    "%s is an unsupported command for target mode\n",
2165241593Sjhb		    scsi_cmd_name(ccb->eata_ccb.cp_scsi_cmd));
216632801Sjulian	}
216732801Sjulian	dpt->target_ccb[br_channel][br_target][br_lun] = NULL;
216832801Sjulian	dpt_Qpush_free(dpt, ccb);
216932801Sjulian}
217032801Sjulian
217132801Sjulian
217239234Sgibbs/*
217332801Sjulian * Use this function to register a client for a buffer read target operation.
217432801Sjulian * The function you register will be called every time a buffer is received
217532801Sjulian * by the target mode code.
217632801Sjulian */
217734480Sjuliandpt_rb_t
217839234Sgibbsdpt_register_buffer(int unit, u_int8_t channel, u_int8_t target, u_int8_t lun,
217939234Sgibbs		    u_int8_t mode, u_int16_t length, u_int16_t offset,
218039234Sgibbs		    dpt_rec_buff callback, dpt_rb_op_t op)
218132801Sjulian{
218232801Sjulian	dpt_softc_t    *dpt;
218332801Sjulian	dpt_ccb_t      *ccb = NULL;
218432801Sjulian	int             ospl;
218532801Sjulian
2186241593Sjhb	dpt = devclass_get_device(dpt_devclass, unit);
2187241593Sjhb	if (dpt == NULL)
2188241593Sjhb		return (INVALID_UNIT);
2189241593Sjhb	mtx_lock(&dpt->lock);
219032801Sjulian
2191241593Sjhb	if (dpt->state & DPT_HA_SHUTDOWN_ACTIVE) {
2192241593Sjhb		mtx_unlock(&dpt->lock);
219332801Sjulian		return (DRIVER_DOWN);
2194241593Sjhb	}
219532801Sjulian
219632801Sjulian	if ((channel > (dpt->channels - 1)) || (target > (dpt->max_id - 1)) ||
2197241593Sjhb	    (lun > (dpt->max_lun - 1))) {
2198241593Sjhb		mtx_unlock(&dpt->lock);
219932801Sjulian		return (INVALID_SENDER);
2200241593Sjhb	}
220132801Sjulian
220232801Sjulian	if (dpt->buffer_receiver[channel][target][lun] == NULL) {
220332801Sjulian		if (op == REGISTER_BUFFER) {
220432801Sjulian			/* Assign the requested callback */
220532801Sjulian			dpt->buffer_receiver[channel][target][lun] = callback;
220632801Sjulian			/* Get a CCB */
220732801Sjulian
220832801Sjulian			/* Process the free list */
220932801Sjulian			if ((TAILQ_EMPTY(&dpt->free_ccbs)) && dpt_alloc_freelist(dpt)) {
2210241593Sjhb				device_printf(dpt->dev,
2211241593Sjhb				    "ERROR: Cannot allocate any more free CCB's.\n"
2212241593Sjhb				    "             Please try later\n");
2213241593Sjhb				mtx_unlock(&dpt->lock);
221432801Sjulian				return (NO_RESOURCES);
221532801Sjulian			}
221632801Sjulian			/* Now grab the newest CCB */
221732801Sjulian			if ((ccb = dpt_Qpop_free(dpt)) == NULL) {
2218241593Sjhb				mtx_unlock(&dpt->lock);
2219241593Sjhb				panic("%s: Got a NULL CCB from pop_free()\n",
2220241593Sjhb				    device_get_nameunit(dpt->dev));
222132801Sjulian			}
222232801Sjulian
222332801Sjulian			/* Clean up the leftover of the previous tenant */
222432801Sjulian			ccb->status = DPT_CCB_STATE_NEW;
222532801Sjulian			dpt->target_ccb[channel][target][lun] = ccb;
222632801Sjulian
222739234Sgibbs			dpt->rw_buffer[channel][target][lun] =
222839234Sgibbs				malloc(DPT_RW_BUFFER_SIZE, M_DEVBUF, M_NOWAIT);
222932801Sjulian			if (dpt->rw_buffer[channel][target][lun] == NULL) {
2230241593Sjhb				device_printf(dpt->dev, "Failed to allocate "
2231241593Sjhb				       "Target-Mode buffer\n");
223232801Sjulian				dpt_Qpush_free(dpt, ccb);
2233241593Sjhb				mtx_unlock(&dpt->lock);
223432801Sjulian				return (NO_RESOURCES);
223532801Sjulian			}
223639234Sgibbs			dpt_set_target(0, dpt, channel, target, lun, mode,
223739234Sgibbs				       length, offset, ccb);
2238241593Sjhb			mtx_unlock(&dpt->lock);
223932801Sjulian			return (SUCCESSFULLY_REGISTERED);
2240241593Sjhb		} else {
2241241593Sjhb			mtx_unlock(&dpt->lock);
224232801Sjulian			return (NOT_REGISTERED);
2243241593Sjhb		}
224432801Sjulian	} else {
224532801Sjulian		if (op == REGISTER_BUFFER) {
2246241593Sjhb			if (dpt->buffer_receiver[channel][target][lun] == callback) {
2247241593Sjhb				mtx_unlock(&dpt->lock);
224832801Sjulian				return (ALREADY_REGISTERED);
2249241593Sjhb			} else {
2250241593Sjhb				mtx_unlock(&dpt->lock);
225132801Sjulian				return (REGISTERED_TO_ANOTHER);
2252241593Sjhb			}
225332801Sjulian		} else {
225432801Sjulian			if (dpt->buffer_receiver[channel][target][lun] == callback) {
225532801Sjulian				dpt->buffer_receiver[channel][target][lun] = NULL;
225632801Sjulian				dpt_Qpush_free(dpt, ccb);
225732801Sjulian				free(dpt->rw_buffer[channel][target][lun], M_DEVBUF);
2258241593Sjhb				mtx_unlock(&dpt->lock);
225932801Sjulian				return (SUCCESSFULLY_REGISTERED);
2260241593Sjhb			} else {
2261241593Sjhb				mtx_unlock(&dpt->lock);
226232801Sjulian				return (INVALID_CALLBACK);
2263241593Sjhb			}
226432801Sjulian		}
226532801Sjulian
226632801Sjulian	}
2267241593Sjhb	mtx_unlock(&dpt->lock);
226832801Sjulian}
226932801Sjulian
227032801Sjulian/* Return the state of the blinking DPT LED's */
227132801Sjulianu_int8_t
227232801Sjuliandpt_blinking_led(dpt_softc_t * dpt)
227332801Sjulian{
227432801Sjulian	int             ndx;
227532801Sjulian	u_int32_t       state;
227632801Sjulian	u_int32_t       previous;
227732801Sjulian	u_int8_t        result;
227832801Sjulian
2279241593Sjhb	mtx_assert(&dpt->lock, MA_OWNED);
228032801Sjulian	result = 0;
228132801Sjulian
228232801Sjulian	for (ndx = 0, state = 0, previous = 0;
228332801Sjulian	     (ndx < 10) && (state != previous);
228432801Sjulian	     ndx++) {
228532801Sjulian		previous = state;
228632801Sjulian		state = dpt_inl(dpt, 1);
228732801Sjulian	}
228832801Sjulian
228932801Sjulian	if ((state == previous) && (state == DPT_BLINK_INDICATOR))
229032801Sjulian		result = dpt_inb(dpt, 5);
229132801Sjulian
229232801Sjulian	return (result);
229332801Sjulian}
229432801Sjulian
229539234Sgibbs/*
229632801Sjulian * Execute a command which did not come from the kernel's SCSI layer.
229732801Sjulian * The only way to map user commands to bus and target is to comply with the
229832801Sjulian * standard DPT wire-down scheme:
229932801Sjulian */
230032801Sjulianint
230132801Sjuliandpt_user_cmd(dpt_softc_t * dpt, eata_pt_t * user_cmd,
230232801Sjulian	     caddr_t cmdarg, int minor_no)
230332801Sjulian{
230439234Sgibbs	dpt_ccb_t *ccb;
230539234Sgibbs	void	  *data;
230639234Sgibbs	int	   channel, target, lun;
230739234Sgibbs	int	   huh;
230839234Sgibbs	int	   result;
230939234Sgibbs	int	   submitted;
231032801Sjulian
2311241593Sjhb	mtx_assert(&dpt->lock, MA_OWNED);
231232801Sjulian	data = NULL;
231332801Sjulian	channel = minor2hba(minor_no);
231432801Sjulian	target = minor2target(minor_no);
231532801Sjulian	lun = minor2lun(minor_no);
231632801Sjulian
231732801Sjulian	if ((channel > (dpt->channels - 1))
231839234Sgibbs	 || (target > dpt->max_id)
231939234Sgibbs	 || (lun > dpt->max_lun))
232032801Sjulian		return (ENXIO);
232132801Sjulian
232232801Sjulian	if (target == dpt->sc_scsi_link[channel].adapter_targ) {
232332801Sjulian		/* This one is for the controller itself */
232432801Sjulian		if ((user_cmd->eataID[0] != 'E')
232539234Sgibbs		 || (user_cmd->eataID[1] != 'A')
232639234Sgibbs		 || (user_cmd->eataID[2] != 'T')
232739234Sgibbs		 || (user_cmd->eataID[3] != 'A')) {
232832801Sjulian			return (ENXIO);
232932801Sjulian		}
233032801Sjulian	}
233132801Sjulian	/* Get a DPT CCB, so we can prepare a command */
233232801Sjulian
233332801Sjulian	/* Process the free list */
233432801Sjulian	if ((TAILQ_EMPTY(&dpt->free_ccbs)) && dpt_alloc_freelist(dpt)) {
2335241593Sjhb		device_printf(dpt->dev,
2336241593Sjhb		    "ERROR: Cannot allocate any more free CCB's.\n"
2337241593Sjhb		    "             Please try later\n");
233832801Sjulian		return (EFAULT);
233932801Sjulian	}
234032801Sjulian	/* Now grab the newest CCB */
234132801Sjulian	if ((ccb = dpt_Qpop_free(dpt)) == NULL) {
2342241593Sjhb		panic("%s: Got a NULL CCB from pop_free()\n",
2343241593Sjhb		    device_get_nameunit(dpt->dev));
234432801Sjulian	} else {
234532801Sjulian		/* Clean up the leftover of the previous tenant */
234632801Sjulian		ccb->status = DPT_CCB_STATE_NEW;
234732801Sjulian	}
234832801Sjulian
234932801Sjulian	bcopy((caddr_t) & user_cmd->command_packet, (caddr_t) & ccb->eata_ccb,
235032801Sjulian	      sizeof(eata_ccb_t));
235132801Sjulian
235232801Sjulian	/* We do not want to do user specified scatter/gather.  Why?? */
235332801Sjulian	if (ccb->eata_ccb.scatter == 1)
235432801Sjulian		return (EINVAL);
235532801Sjulian
235632801Sjulian	ccb->eata_ccb.Auto_Req_Sen = 1;
235732801Sjulian	ccb->eata_ccb.reqlen = htonl(sizeof(struct scsi_sense_data));
235832801Sjulian	ccb->eata_ccb.cp_datalen = htonl(sizeof(ccb->eata_ccb.cp_datalen));
235932801Sjulian	ccb->eata_ccb.cp_dataDMA = htonl(vtophys(ccb->eata_ccb.cp_dataDMA));
236032801Sjulian	ccb->eata_ccb.cp_statDMA = htonl(vtophys(&ccb->eata_ccb.cp_statDMA));
236132801Sjulian	ccb->eata_ccb.cp_reqDMA = htonl(vtophys(&ccb->eata_ccb.cp_reqDMA));
236232801Sjulian	ccb->eata_ccb.cp_viraddr = (u_int32_t) & ccb;
236332801Sjulian
236432801Sjulian	if (ccb->eata_ccb.DataIn || ccb->eata_ccb.DataOut) {
236532801Sjulian		/* Data I/O is involved in this command.  Alocate buffer */
236632801Sjulian		if (ccb->eata_ccb.cp_datalen > PAGE_SIZE) {
236732801Sjulian			data = contigmalloc(ccb->eata_ccb.cp_datalen,
2368111119Simp					    M_TEMP, M_WAITOK, 0, ~0,
236932801Sjulian					    ccb->eata_ccb.cp_datalen,
237032801Sjulian					    0x10000);
237132801Sjulian		} else {
237232801Sjulian			data = malloc(ccb->eata_ccb.cp_datalen, M_TEMP,
2373111119Simp				      M_WAITOK);
237432801Sjulian		}
237532801Sjulian
237632801Sjulian		if (data == NULL) {
2377241593Sjhb			device_printf(dpt->dev, "Cannot allocate %d bytes "
2378241593Sjhb			       "for EATA command\n",
237932801Sjulian			       ccb->eata_ccb.cp_datalen);
238032801Sjulian			return (EFAULT);
238132801Sjulian		}
238232801Sjulian#define usr_cmd_DMA (caddr_t)user_cmd->command_packet.cp_dataDMA
238332801Sjulian		if (ccb->eata_ccb.DataIn == 1) {
238432801Sjulian			if (copyin(usr_cmd_DMA,
238532801Sjulian				   data, ccb->eata_ccb.cp_datalen) == -1)
238632801Sjulian				return (EFAULT);
238732801Sjulian		}
238832801Sjulian	} else {
238932801Sjulian		/* No data I/O involved here.  Make sure the DPT knows that */
239032801Sjulian		ccb->eata_ccb.cp_datalen = 0;
239132801Sjulian		data = NULL;
239232801Sjulian	}
239332801Sjulian
239432801Sjulian	if (ccb->eata_ccb.FWNEST == 1)
239532801Sjulian		ccb->eata_ccb.FWNEST = 0;
239632801Sjulian
239732801Sjulian	if (ccb->eata_ccb.cp_datalen != 0) {
239832801Sjulian		if (dpt_scatter_gather(dpt, ccb, ccb->eata_ccb.cp_datalen,
239932801Sjulian				       data) != 0) {
240032801Sjulian			if (data != NULL)
240132801Sjulian				free(data, M_TEMP);
240232801Sjulian			return (EFAULT);
240332801Sjulian		}
240432801Sjulian	}
240532801Sjulian	/**
240632801Sjulian	 * We are required to quiet a SCSI bus.
240732801Sjulian	 * since we do not queue comands on a bus basis,
240832801Sjulian	 * we wait for ALL commands on a controller to complete.
240932801Sjulian	 * In the mean time, sched_queue() will not schedule new commands.
241032801Sjulian	 */
241132801Sjulian	if ((ccb->eata_ccb.cp_cdb[0] == MULTIFUNCTION_CMD)
241232801Sjulian	    && (ccb->eata_ccb.cp_cdb[2] == BUS_QUIET)) {
241332801Sjulian		/* We wait for ALL traffic for this HBa to subside */
241432801Sjulian		dpt->state |= DPT_HA_QUIET;
241532801Sjulian
241632801Sjulian		while ((submitted = dpt->submitted_ccbs_count) != 0) {
2417241593Sjhb			huh = mtx_sleep((void *) dpt, &dpt->lock,
2418241593Sjhb			    PCATCH | PRIBIO, "dptqt", 100 * hz);
241932801Sjulian			switch (huh) {
242032801Sjulian			case 0:
242132801Sjulian				/* Wakeup call received */
242232801Sjulian				break;
242332801Sjulian			case EWOULDBLOCK:
242432801Sjulian				/* Timer Expired */
242532801Sjulian				break;
242632801Sjulian			default:
242732801Sjulian				/* anything else */
242832801Sjulian				break;
242932801Sjulian			}
243032801Sjulian		}
243132801Sjulian	}
243232801Sjulian	/* Resume normal operation */
243332801Sjulian	if ((ccb->eata_ccb.cp_cdb[0] == MULTIFUNCTION_CMD)
243432801Sjulian	    && (ccb->eata_ccb.cp_cdb[2] == BUS_UNQUIET)) {
243532801Sjulian		dpt->state &= ~DPT_HA_QUIET;
243632801Sjulian	}
243732801Sjulian	/**
243832801Sjulian	 * Schedule the command and submit it.
243932801Sjulian	 * We bypass dpt_sched_queue, as it will block on DPT_HA_QUIET
244032801Sjulian	 */
244132801Sjulian	ccb->xs = NULL;
244232801Sjulian	ccb->flags = 0;
244332801Sjulian	ccb->eata_ccb.Auto_Req_Sen = 1;	/* We always want this feature */
244432801Sjulian
244532801Sjulian	ccb->transaction_id = ++dpt->commands_processed;
244632801Sjulian	ccb->std_callback = (ccb_callback) dpt_user_cmd_done;
244732801Sjulian	ccb->result = (u_int32_t) & cmdarg;
244832801Sjulian	ccb->data = data;
244932801Sjulian
245032801Sjulian#ifdef DPT_MEASURE_PERFORMANCE
245138115Seivind	++dpt->performance.command_count[ccb->eata_ccb.cp_scsi_cmd];
245238115Seivind	ccb->command_started = microtime_now;
245332801Sjulian#endif
245432801Sjulian	dpt_Qadd_waiting(dpt, ccb);
245532801Sjulian
245632801Sjulian	dpt_sched_queue(dpt);
245732801Sjulian
245832801Sjulian	/* Wait for the command to complete */
2459241593Sjhb	(void) mtx_sleep((void *) ccb, &dpt->lock, PCATCH | PRIBIO, "dptucw",
2460241593Sjhb	    100 * hz);
246132801Sjulian
246232801Sjulian	/* Free allocated memory */
246332801Sjulian	if (data != NULL)
246432801Sjulian		free(data, M_TEMP);
246532801Sjulian
246632801Sjulian	return (0);
246732801Sjulian}
246832801Sjulian
246932801Sjulianstatic void
247032801Sjuliandpt_user_cmd_done(dpt_softc_t * dpt, int bus, dpt_ccb_t * ccb)
247132801Sjulian{
247232801Sjulian	u_int32_t       result;
247332801Sjulian	caddr_t         cmd_arg;
247432801Sjulian
2475241593Sjhb	mtx_unlock(&dpt->lock);
2476241593Sjhb
247732801Sjulian	/**
247832801Sjulian	 * If Auto Request Sense is on, copyout the sense struct
247932801Sjulian	 */
248038231Sbde#define usr_pckt_DMA 	(caddr_t)(intptr_t)ntohl(ccb->eata_ccb.cp_reqDMA)
248132801Sjulian#define usr_pckt_len	ntohl(ccb->eata_ccb.cp_datalen)
248232801Sjulian	if (ccb->eata_ccb.Auto_Req_Sen == 1) {
248332801Sjulian		if (copyout((caddr_t) & ccb->sense_data, usr_pckt_DMA,
248432801Sjulian			    sizeof(struct scsi_sense_data))) {
2485241593Sjhb			mtx_lock(&dpt->lock);
248632801Sjulian			ccb->result = EFAULT;
248732801Sjulian			dpt_Qpush_free(dpt, ccb);
248832801Sjulian			wakeup(ccb);
248932801Sjulian			return;
249032801Sjulian		}
249132801Sjulian	}
249232801Sjulian	/* If DataIn is on, copyout the data */
249332801Sjulian	if ((ccb->eata_ccb.DataIn == 1)
249432801Sjulian	    && (ccb->status_packet.hba_stat == HA_NO_ERROR)) {
249532801Sjulian		if (copyout(ccb->data, usr_pckt_DMA, usr_pckt_len)) {
2496241593Sjhb			mtx_lock(&dpt->lock);
249732801Sjulian			dpt_Qpush_free(dpt, ccb);
249832801Sjulian			ccb->result = EFAULT;
249932801Sjulian
250032801Sjulian			wakeup(ccb);
250132801Sjulian			return;
250232801Sjulian		}
250332801Sjulian	}
250432801Sjulian	/* Copyout the status */
250532801Sjulian	result = ccb->status_packet.hba_stat;
250632801Sjulian	cmd_arg = (caddr_t) ccb->result;
250732801Sjulian
250832801Sjulian	if (copyout((caddr_t) & result, cmd_arg, sizeof(result))) {
2509241593Sjhb		mtx_lock(&dpt->lock);
251032801Sjulian		dpt_Qpush_free(dpt, ccb);
251132801Sjulian		ccb->result = EFAULT;
251232801Sjulian		wakeup(ccb);
251332801Sjulian		return;
251432801Sjulian	}
2515241593Sjhb	mtx_lock(&dpt->lock);
251632801Sjulian	/* Put the CCB back in the freelist */
251732801Sjulian	ccb->state |= DPT_CCB_STATE_COMPLETED;
251832801Sjulian	dpt_Qpush_free(dpt, ccb);
251932801Sjulian
252032801Sjulian	/* Free allocated memory */
252132801Sjulian	return;
252232801Sjulian}
252332801Sjulian
252432801Sjulian#endif
2525