xref: /linux/drivers/scsi/sun3_scsi.c (revision 14d739f640d894b4b5db1ab3e1df17b865825a42)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Sun3 DMA routines added by Sam Creasey (sammy@sammy.net)
51da177e4SLinus Torvalds  *
6757f5badSFinn Thain  * VME support added by Sam Creasey
7757f5badSFinn Thain  *
8757f5badSFinn Thain  * TODO: modify this driver to support multiple Sun3 SCSI VME boards
9757f5badSFinn Thain  *
101da177e4SLinus Torvalds  * Adapted from mac_scsinew.c:
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds /*
131da177e4SLinus Torvalds  * Generic Macintosh NCR5380 driver
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  * Copyright 1998, Michael Schmitz <mschmitz@lbl.gov>
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  * derived in part from:
181da177e4SLinus Torvalds  */
191da177e4SLinus Torvalds /*
201da177e4SLinus Torvalds  * Generic Generic NCR5380 driver
211da177e4SLinus Torvalds  *
221da177e4SLinus Torvalds  * Copyright 1995, Russell King
231da177e4SLinus Torvalds  */
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #include <linux/types.h>
261da177e4SLinus Torvalds #include <linux/delay.h>
271da177e4SLinus Torvalds #include <linux/module.h>
281da177e4SLinus Torvalds #include <linux/ioport.h>
291da177e4SLinus Torvalds #include <linux/init.h>
301da177e4SLinus Torvalds #include <linux/blkdev.h>
310d31f875SFinn Thain #include <linux/platform_device.h>
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds #include <asm/io.h>
341da177e4SLinus Torvalds #include <asm/dvma.h>
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds #include <scsi/scsi_host.h>
372231ef87SFinn Thain 
388dad0c51SFinn Thain /* minimum number of bytes to do dma on */
398dad0c51SFinn Thain #define DMA_MIN_SIZE                    129
402231ef87SFinn Thain 
41e63449c4SFinn Thain /* Definitions for the core NCR5380 driver. */
42e63449c4SFinn Thain 
432231ef87SFinn Thain #define NCR5380_implementation_fields   /* none */
442231ef87SFinn Thain 
4561e1ce58SFinn Thain #define NCR5380_read(reg)               in_8(hostdata->io + (reg))
4661e1ce58SFinn Thain #define NCR5380_write(reg, value)       out_8(hostdata->io + (reg), value)
472231ef87SFinn Thain 
482231ef87SFinn Thain #define NCR5380_queue_command           sun3scsi_queue_command
492231ef87SFinn Thain #define NCR5380_bus_reset               sun3scsi_bus_reset
502231ef87SFinn Thain #define NCR5380_abort                   sun3scsi_abort
512231ef87SFinn Thain #define NCR5380_info                    sun3scsi_info
522231ef87SFinn Thain 
534a98f896SFinn Thain #define NCR5380_dma_xfer_len            sun3scsi_dma_xfer_len
544a98f896SFinn Thain #define NCR5380_dma_recv_setup          sun3scsi_dma_count
554a98f896SFinn Thain #define NCR5380_dma_send_setup          sun3scsi_dma_count
564a98f896SFinn Thain #define NCR5380_dma_residual            sun3scsi_dma_residual
572231ef87SFinn Thain 
589f6620a3SFinn Thain #include "NCR5380.h"
591da177e4SLinus Torvalds 
60*14d739f6SFinn Thain /* dma regs start at regbase + 8, directly after the NCR regs */
61*14d739f6SFinn Thain struct sun3_dma_regs {
62*14d739f6SFinn Thain 	unsigned short dma_addr_hi; /* vme only */
63*14d739f6SFinn Thain 	unsigned short dma_addr_lo; /* vme only */
64*14d739f6SFinn Thain 	unsigned short dma_count_hi; /* vme only */
65*14d739f6SFinn Thain 	unsigned short dma_count_lo; /* vme only */
66*14d739f6SFinn Thain 	unsigned short udc_data; /* udc dma data reg (obio only) */
67*14d739f6SFinn Thain 	unsigned short udc_addr; /* uda dma addr reg (obio only) */
68*14d739f6SFinn Thain 	unsigned short fifo_data; /* fifo data reg,
69*14d739f6SFinn Thain 	                           * holds extra byte on odd dma reads
70*14d739f6SFinn Thain 	                           */
71*14d739f6SFinn Thain 	unsigned short fifo_count;
72*14d739f6SFinn Thain 	unsigned short csr; /* control/status reg */
73*14d739f6SFinn Thain 	unsigned short bpack_hi; /* vme only */
74*14d739f6SFinn Thain 	unsigned short bpack_lo; /* vme only */
75*14d739f6SFinn Thain 	unsigned short ivect; /* vme only */
76*14d739f6SFinn Thain 	unsigned short fifo_count_hi; /* vme only */
77*14d739f6SFinn Thain };
78*14d739f6SFinn Thain 
79*14d739f6SFinn Thain /* ucd chip specific regs - live in dvma space */
80*14d739f6SFinn Thain struct sun3_udc_regs {
81*14d739f6SFinn Thain 	unsigned short rsel; /* select regs to load */
82*14d739f6SFinn Thain 	unsigned short addr_hi; /* high word of addr */
83*14d739f6SFinn Thain 	unsigned short addr_lo; /* low word */
84*14d739f6SFinn Thain 	unsigned short count; /* words to be xfer'd */
85*14d739f6SFinn Thain 	unsigned short mode_hi; /* high word of channel mode */
86*14d739f6SFinn Thain 	unsigned short mode_lo; /* low word of channel mode */
87*14d739f6SFinn Thain };
88*14d739f6SFinn Thain 
89*14d739f6SFinn Thain /* addresses of the udc registers */
90*14d739f6SFinn Thain #define UDC_MODE 0x38
91*14d739f6SFinn Thain #define UDC_CSR 0x2e /* command/status */
92*14d739f6SFinn Thain #define UDC_CHN_HI 0x26 /* chain high word */
93*14d739f6SFinn Thain #define UDC_CHN_LO 0x22 /* chain lo word */
94*14d739f6SFinn Thain #define UDC_CURA_HI 0x1a /* cur reg A high */
95*14d739f6SFinn Thain #define UDC_CURA_LO 0x0a /* cur reg A low */
96*14d739f6SFinn Thain #define UDC_CURB_HI 0x12 /* cur reg B high */
97*14d739f6SFinn Thain #define UDC_CURB_LO 0x02 /* cur reg B low */
98*14d739f6SFinn Thain #define UDC_MODE_HI 0x56 /* mode reg high */
99*14d739f6SFinn Thain #define UDC_MODE_LO 0x52 /* mode reg low */
100*14d739f6SFinn Thain #define UDC_COUNT 0x32 /* words to xfer */
101*14d739f6SFinn Thain 
102*14d739f6SFinn Thain /* some udc commands */
103*14d739f6SFinn Thain #define UDC_RESET 0
104*14d739f6SFinn Thain #define UDC_CHN_START 0xa0 /* start chain */
105*14d739f6SFinn Thain #define UDC_INT_ENABLE 0x32 /* channel 1 int on */
106*14d739f6SFinn Thain 
107*14d739f6SFinn Thain /* udc mode words */
108*14d739f6SFinn Thain #define UDC_MODE_HIWORD 0x40
109*14d739f6SFinn Thain #define UDC_MODE_LSEND 0xc2
110*14d739f6SFinn Thain #define UDC_MODE_LRECV 0xd2
111*14d739f6SFinn Thain 
112*14d739f6SFinn Thain /* udc reg selections */
113*14d739f6SFinn Thain #define UDC_RSEL_SEND 0x282
114*14d739f6SFinn Thain #define UDC_RSEL_RECV 0x182
115*14d739f6SFinn Thain 
116*14d739f6SFinn Thain /* bits in csr reg */
117*14d739f6SFinn Thain #define CSR_DMA_ACTIVE 0x8000
118*14d739f6SFinn Thain #define CSR_DMA_CONFLICT 0x4000
119*14d739f6SFinn Thain #define CSR_DMA_BUSERR 0x2000
120*14d739f6SFinn Thain 
121*14d739f6SFinn Thain #define CSR_FIFO_EMPTY 0x400 /* fifo flushed? */
122*14d739f6SFinn Thain #define CSR_SDB_INT 0x200 /* sbc interrupt pending */
123*14d739f6SFinn Thain #define CSR_DMA_INT 0x100 /* dma interrupt pending */
124*14d739f6SFinn Thain 
125*14d739f6SFinn Thain #define CSR_LEFT 0xc0
126*14d739f6SFinn Thain #define CSR_LEFT_3 0xc0
127*14d739f6SFinn Thain #define CSR_LEFT_2 0x80
128*14d739f6SFinn Thain #define CSR_LEFT_1 0x40
129*14d739f6SFinn Thain #define CSR_PACK_ENABLE 0x20
130*14d739f6SFinn Thain 
131*14d739f6SFinn Thain #define CSR_DMA_ENABLE 0x10
132*14d739f6SFinn Thain 
133*14d739f6SFinn Thain #define CSR_SEND 0x8 /* 1 = send  0 = recv */
134*14d739f6SFinn Thain #define CSR_FIFO 0x2 /* reset fifo */
135*14d739f6SFinn Thain #define CSR_INTR 0x4 /* interrupt enable */
136*14d739f6SFinn Thain #define CSR_SCSI 0x1
137*14d739f6SFinn Thain 
138*14d739f6SFinn Thain #define VME_DATA24 0x3d00
1391da177e4SLinus Torvalds 
1402231ef87SFinn Thain extern int sun3_map_test(unsigned long, char *);
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds static int setup_can_queue = -1;
1431da177e4SLinus Torvalds module_param(setup_can_queue, int, 0);
1441da177e4SLinus Torvalds static int setup_cmd_per_lun = -1;
1451da177e4SLinus Torvalds module_param(setup_cmd_per_lun, int, 0);
1461da177e4SLinus Torvalds static int setup_sg_tablesize = -1;
1471da177e4SLinus Torvalds module_param(setup_sg_tablesize, int, 0);
1481da177e4SLinus Torvalds static int setup_hostid = -1;
1491da177e4SLinus Torvalds module_param(setup_hostid, int, 0);
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds /* ms to wait after hitting dma regs */
1521da177e4SLinus Torvalds #define SUN3_DMA_DELAY 10
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds /* dvma buffer to allocate -- 32k should hopefully be more than sufficient */
1551da177e4SLinus Torvalds #define SUN3_DVMA_BUFSIZE 0xe000
1561da177e4SLinus Torvalds 
1572231ef87SFinn Thain static struct scsi_cmnd *sun3_dma_setup_done;
1581da177e4SLinus Torvalds static volatile struct sun3_dma_regs *dregs;
1590d31f875SFinn Thain static struct sun3_udc_regs *udc_regs;
160d5f7e65dSFinn Thain static unsigned char *sun3_dma_orig_addr;
161d5f7e65dSFinn Thain static unsigned long sun3_dma_orig_count;
162d5f7e65dSFinn Thain static int sun3_dma_active;
163d5f7e65dSFinn Thain static unsigned long last_residual;
1641da177e4SLinus Torvalds 
165757f5badSFinn Thain #ifndef SUN3_SCSI_VME
1661da177e4SLinus Torvalds /* dma controller register access functions */
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds static inline unsigned short sun3_udc_read(unsigned char reg)
1691da177e4SLinus Torvalds {
1701da177e4SLinus Torvalds 	unsigned short ret;
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	dregs->udc_addr = UDC_CSR;
1731da177e4SLinus Torvalds 	udelay(SUN3_DMA_DELAY);
1741da177e4SLinus Torvalds 	ret = dregs->udc_data;
1751da177e4SLinus Torvalds 	udelay(SUN3_DMA_DELAY);
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	return ret;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds static inline void sun3_udc_write(unsigned short val, unsigned char reg)
1811da177e4SLinus Torvalds {
1821da177e4SLinus Torvalds 	dregs->udc_addr = reg;
1831da177e4SLinus Torvalds 	udelay(SUN3_DMA_DELAY);
1841da177e4SLinus Torvalds 	dregs->udc_data = val;
1851da177e4SLinus Torvalds 	udelay(SUN3_DMA_DELAY);
1861da177e4SLinus Torvalds }
187757f5badSFinn Thain #endif
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds // safe bits for the CSR
1901da177e4SLinus Torvalds #define CSR_GOOD 0x060f
1911da177e4SLinus Torvalds 
192cd46140aSFinn Thain static irqreturn_t scsi_sun3_intr(int irq, void *dev)
1931da177e4SLinus Torvalds {
194cd46140aSFinn Thain 	struct Scsi_Host *instance = dev;
1951da177e4SLinus Torvalds 	unsigned short csr = dregs->csr;
1961da177e4SLinus Torvalds 	int handled = 0;
1971da177e4SLinus Torvalds 
198757f5badSFinn Thain #ifdef SUN3_SCSI_VME
199757f5badSFinn Thain 	dregs->csr &= ~CSR_DMA_ENABLE;
200757f5badSFinn Thain #endif
201757f5badSFinn Thain 
2021da177e4SLinus Torvalds 	if(csr & ~CSR_GOOD) {
203cd46140aSFinn Thain 		if (csr & CSR_DMA_BUSERR)
204cd46140aSFinn Thain 			shost_printk(KERN_ERR, instance, "bus error in DMA\n");
205cd46140aSFinn Thain 		if (csr & CSR_DMA_CONFLICT)
206cd46140aSFinn Thain 			shost_printk(KERN_ERR, instance, "DMA conflict\n");
2071da177e4SLinus Torvalds 		handled = 1;
2081da177e4SLinus Torvalds 	}
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	if(csr & (CSR_SDB_INT | CSR_DMA_INT)) {
211cd46140aSFinn Thain 		NCR5380_intr(irq, dev);
2121da177e4SLinus Torvalds 		handled = 1;
2131da177e4SLinus Torvalds 	}
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 	return IRQ_RETVAL(handled);
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds /* sun3scsi_dma_setup() -- initialize the dma controller for a read/write */
2194a98f896SFinn Thain static int sun3scsi_dma_setup(struct NCR5380_hostdata *hostdata,
2204a98f896SFinn Thain                               unsigned char *data, int count, int write_flag)
2211da177e4SLinus Torvalds {
2221da177e4SLinus Torvalds 	void *addr;
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds 	if(sun3_dma_orig_addr != NULL)
2251da177e4SLinus Torvalds 		dvma_unmap(sun3_dma_orig_addr);
2261da177e4SLinus Torvalds 
227757f5badSFinn Thain #ifdef SUN3_SCSI_VME
228757f5badSFinn Thain 	addr = (void *)dvma_map_vme((unsigned long) data, count);
229757f5badSFinn Thain #else
2301da177e4SLinus Torvalds 	addr = (void *)dvma_map((unsigned long) data, count);
231757f5badSFinn Thain #endif
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	sun3_dma_orig_addr = addr;
2341da177e4SLinus Torvalds 	sun3_dma_orig_count = count;
235757f5badSFinn Thain 
236757f5badSFinn Thain #ifndef SUN3_SCSI_VME
2371da177e4SLinus Torvalds 	dregs->fifo_count = 0;
2381da177e4SLinus Torvalds 	sun3_udc_write(UDC_RESET, UDC_CSR);
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	/* reset fifo */
2411da177e4SLinus Torvalds 	dregs->csr &= ~CSR_FIFO;
2421da177e4SLinus Torvalds 	dregs->csr |= CSR_FIFO;
243757f5badSFinn Thain #endif
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	/* set direction */
2461da177e4SLinus Torvalds 	if(write_flag)
2471da177e4SLinus Torvalds 		dregs->csr |= CSR_SEND;
2481da177e4SLinus Torvalds 	else
2491da177e4SLinus Torvalds 		dregs->csr &= ~CSR_SEND;
2501da177e4SLinus Torvalds 
251757f5badSFinn Thain #ifdef SUN3_SCSI_VME
252757f5badSFinn Thain 	dregs->csr |= CSR_PACK_ENABLE;
253757f5badSFinn Thain 
254757f5badSFinn Thain 	dregs->dma_addr_hi = ((unsigned long)addr >> 16);
255757f5badSFinn Thain 	dregs->dma_addr_lo = ((unsigned long)addr & 0xffff);
256757f5badSFinn Thain 
257757f5badSFinn Thain 	dregs->dma_count_hi = 0;
258757f5badSFinn Thain 	dregs->dma_count_lo = 0;
259757f5badSFinn Thain 	dregs->fifo_count_hi = 0;
260757f5badSFinn Thain 	dregs->fifo_count = 0;
261757f5badSFinn Thain #else
2621da177e4SLinus Torvalds 	/* byte count for fifo */
2631da177e4SLinus Torvalds 	dregs->fifo_count = count;
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds 	sun3_udc_write(UDC_RESET, UDC_CSR);
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	/* reset fifo */
2681da177e4SLinus Torvalds 	dregs->csr &= ~CSR_FIFO;
2691da177e4SLinus Torvalds 	dregs->csr |= CSR_FIFO;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	if(dregs->fifo_count != count) {
2724a98f896SFinn Thain 		shost_printk(KERN_ERR, hostdata->host,
2734a98f896SFinn Thain 		             "FIFO mismatch %04x not %04x\n",
274cd46140aSFinn Thain 		             dregs->fifo_count, (unsigned int) count);
2754a98f896SFinn Thain 		NCR5380_dprint(NDEBUG_DMA, hostdata->host);
2761da177e4SLinus Torvalds 	}
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	/* setup udc */
2791da177e4SLinus Torvalds 	udc_regs->addr_hi = (((unsigned long)(addr) & 0xff0000) >> 8);
2801da177e4SLinus Torvalds 	udc_regs->addr_lo = ((unsigned long)(addr) & 0xffff);
2811da177e4SLinus Torvalds 	udc_regs->count = count/2; /* count in words */
2821da177e4SLinus Torvalds 	udc_regs->mode_hi = UDC_MODE_HIWORD;
2831da177e4SLinus Torvalds 	if(write_flag) {
2841da177e4SLinus Torvalds 		if(count & 1)
2851da177e4SLinus Torvalds 			udc_regs->count++;
2861da177e4SLinus Torvalds 		udc_regs->mode_lo = UDC_MODE_LSEND;
2871da177e4SLinus Torvalds 		udc_regs->rsel = UDC_RSEL_SEND;
2881da177e4SLinus Torvalds 	} else {
2891da177e4SLinus Torvalds 		udc_regs->mode_lo = UDC_MODE_LRECV;
2901da177e4SLinus Torvalds 		udc_regs->rsel = UDC_RSEL_RECV;
2911da177e4SLinus Torvalds 	}
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	/* announce location of regs block */
2941da177e4SLinus Torvalds 	sun3_udc_write(((dvma_vtob(udc_regs) & 0xff0000) >> 8),
2951da177e4SLinus Torvalds 		       UDC_CHN_HI);
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 	sun3_udc_write((dvma_vtob(udc_regs) & 0xffff), UDC_CHN_LO);
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds 	/* set dma master on */
3001da177e4SLinus Torvalds 	sun3_udc_write(0xd, UDC_MODE);
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 	/* interrupt enable */
3031da177e4SLinus Torvalds 	sun3_udc_write(UDC_INT_ENABLE, UDC_CSR);
304757f5badSFinn Thain #endif
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds        	return count;
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds }
3091da177e4SLinus Torvalds 
3104a98f896SFinn Thain static int sun3scsi_dma_count(struct NCR5380_hostdata *hostdata,
3114a98f896SFinn Thain                               unsigned char *data, int count)
3124a98f896SFinn Thain {
3134a98f896SFinn Thain 	return count;
3144a98f896SFinn Thain }
3154a98f896SFinn Thain 
3164a98f896SFinn Thain static inline int sun3scsi_dma_recv_setup(struct NCR5380_hostdata *hostdata,
3174a98f896SFinn Thain                                           unsigned char *data, int count)
3184a98f896SFinn Thain {
3194a98f896SFinn Thain 	return sun3scsi_dma_setup(hostdata, data, count, 0);
3204a98f896SFinn Thain }
3214a98f896SFinn Thain 
3224a98f896SFinn Thain static inline int sun3scsi_dma_send_setup(struct NCR5380_hostdata *hostdata,
3234a98f896SFinn Thain                                           unsigned char *data, int count)
3244a98f896SFinn Thain {
3254a98f896SFinn Thain 	return sun3scsi_dma_setup(hostdata, data, count, 1);
3264a98f896SFinn Thain }
3274a98f896SFinn Thain 
3284a98f896SFinn Thain static int sun3scsi_dma_residual(struct NCR5380_hostdata *hostdata)
3291da177e4SLinus Torvalds {
3301da177e4SLinus Torvalds 	return last_residual;
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
3334a98f896SFinn Thain static int sun3scsi_dma_xfer_len(struct NCR5380_hostdata *hostdata,
334e63449c4SFinn Thain                                  struct scsi_cmnd *cmd)
3351da177e4SLinus Torvalds {
3364a98f896SFinn Thain 	int wanted_len = cmd->SCp.this_residual;
3374a98f896SFinn Thain 
338e63449c4SFinn Thain 	if (wanted_len < DMA_MIN_SIZE || cmd->request->cmd_type != REQ_TYPE_FS)
3391da177e4SLinus Torvalds 		return 0;
340e63449c4SFinn Thain 
341e63449c4SFinn Thain 	return wanted_len;
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds static inline int sun3scsi_dma_start(unsigned long count, unsigned char *data)
3451da177e4SLinus Torvalds {
346757f5badSFinn Thain #ifdef SUN3_SCSI_VME
347757f5badSFinn Thain 	unsigned short csr;
3481da177e4SLinus Torvalds 
349757f5badSFinn Thain 	csr = dregs->csr;
350757f5badSFinn Thain 
351757f5badSFinn Thain 	dregs->dma_count_hi = (sun3_dma_orig_count >> 16);
352757f5badSFinn Thain 	dregs->dma_count_lo = (sun3_dma_orig_count & 0xffff);
353757f5badSFinn Thain 
354757f5badSFinn Thain 	dregs->fifo_count_hi = (sun3_dma_orig_count >> 16);
355757f5badSFinn Thain 	dregs->fifo_count = (sun3_dma_orig_count & 0xffff);
356757f5badSFinn Thain 
357757f5badSFinn Thain /*	if(!(csr & CSR_DMA_ENABLE))
358757f5badSFinn Thain  *		dregs->csr |= CSR_DMA_ENABLE;
359757f5badSFinn Thain  */
360757f5badSFinn Thain #else
3611da177e4SLinus Torvalds     sun3_udc_write(UDC_CHN_START, UDC_CSR);
362757f5badSFinn Thain #endif
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds     return 0;
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds /* clean up after our dma is done */
3681da177e4SLinus Torvalds static int sun3scsi_dma_finish(int write_flag)
3691da177e4SLinus Torvalds {
370757f5badSFinn Thain 	unsigned short __maybe_unused count;
3711da177e4SLinus Torvalds 	unsigned short fifo;
3721da177e4SLinus Torvalds 	int ret = 0;
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds 	sun3_dma_active = 0;
375757f5badSFinn Thain 
376757f5badSFinn Thain #ifdef SUN3_SCSI_VME
377757f5badSFinn Thain 	dregs->csr &= ~CSR_DMA_ENABLE;
378757f5badSFinn Thain 
379757f5badSFinn Thain 	fifo = dregs->fifo_count;
380757f5badSFinn Thain 	if (write_flag) {
381757f5badSFinn Thain 		if ((fifo > 0) && (fifo < sun3_dma_orig_count))
382757f5badSFinn Thain 			fifo++;
383757f5badSFinn Thain 	}
384757f5badSFinn Thain 
385757f5badSFinn Thain 	last_residual = fifo;
386757f5badSFinn Thain 	/* empty bytes from the fifo which didn't make it */
387757f5badSFinn Thain 	if ((!write_flag) && (dregs->csr & CSR_LEFT)) {
388757f5badSFinn Thain 		unsigned char *vaddr;
389757f5badSFinn Thain 
390757f5badSFinn Thain 		vaddr = (unsigned char *)dvma_vmetov(sun3_dma_orig_addr);
391757f5badSFinn Thain 
392757f5badSFinn Thain 		vaddr += (sun3_dma_orig_count - fifo);
393757f5badSFinn Thain 		vaddr--;
394757f5badSFinn Thain 
395757f5badSFinn Thain 		switch (dregs->csr & CSR_LEFT) {
396757f5badSFinn Thain 		case CSR_LEFT_3:
397757f5badSFinn Thain 			*vaddr = (dregs->bpack_lo & 0xff00) >> 8;
398757f5badSFinn Thain 			vaddr--;
399757f5badSFinn Thain 
400757f5badSFinn Thain 		case CSR_LEFT_2:
401757f5badSFinn Thain 			*vaddr = (dregs->bpack_hi & 0x00ff);
402757f5badSFinn Thain 			vaddr--;
403757f5badSFinn Thain 
404757f5badSFinn Thain 		case CSR_LEFT_1:
405757f5badSFinn Thain 			*vaddr = (dregs->bpack_hi & 0xff00) >> 8;
406757f5badSFinn Thain 			break;
407757f5badSFinn Thain 		}
408757f5badSFinn Thain 	}
409757f5badSFinn Thain #else
4101da177e4SLinus Torvalds 	// check to empty the fifo on a read
4111da177e4SLinus Torvalds 	if(!write_flag) {
4121da177e4SLinus Torvalds 		int tmo = 20000; /* .2 sec */
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 		while(1) {
4151da177e4SLinus Torvalds 			if(dregs->csr & CSR_FIFO_EMPTY)
4161da177e4SLinus Torvalds 				break;
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 			if(--tmo <= 0) {
4191da177e4SLinus Torvalds 				printk("sun3scsi: fifo failed to empty!\n");
4201da177e4SLinus Torvalds 				return 1;
4211da177e4SLinus Torvalds 			}
4221da177e4SLinus Torvalds 			udelay(10);
4231da177e4SLinus Torvalds 		}
4241da177e4SLinus Torvalds 	}
4251da177e4SLinus Torvalds 
426cd46140aSFinn Thain 	dregs->udc_addr = 0x32;
427cd46140aSFinn Thain 	udelay(SUN3_DMA_DELAY);
428cd46140aSFinn Thain 	count = 2 * dregs->udc_data;
429cd46140aSFinn Thain 	udelay(SUN3_DMA_DELAY);
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	fifo = dregs->fifo_count;
4321da177e4SLinus Torvalds 	last_residual = fifo;
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	/* empty bytes from the fifo which didn't make it */
4351da177e4SLinus Torvalds 	if((!write_flag) && (count - fifo) == 2) {
4361da177e4SLinus Torvalds 		unsigned short data;
4371da177e4SLinus Torvalds 		unsigned char *vaddr;
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 		data = dregs->fifo_data;
4401da177e4SLinus Torvalds 		vaddr = (unsigned char *)dvma_btov(sun3_dma_orig_addr);
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 		vaddr += (sun3_dma_orig_count - fifo);
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 		vaddr[-2] = (data & 0xff00) >> 8;
4451da177e4SLinus Torvalds 		vaddr[-1] = (data & 0xff);
4461da177e4SLinus Torvalds 	}
447757f5badSFinn Thain #endif
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	dvma_unmap(sun3_dma_orig_addr);
4501da177e4SLinus Torvalds 	sun3_dma_orig_addr = NULL;
451757f5badSFinn Thain 
452757f5badSFinn Thain #ifdef SUN3_SCSI_VME
453757f5badSFinn Thain 	dregs->dma_addr_hi = 0;
454757f5badSFinn Thain 	dregs->dma_addr_lo = 0;
455757f5badSFinn Thain 	dregs->dma_count_hi = 0;
456757f5badSFinn Thain 	dregs->dma_count_lo = 0;
457757f5badSFinn Thain 
458757f5badSFinn Thain 	dregs->fifo_count = 0;
459757f5badSFinn Thain 	dregs->fifo_count_hi = 0;
460757f5badSFinn Thain 
461757f5badSFinn Thain 	dregs->csr &= ~CSR_SEND;
462757f5badSFinn Thain /*	dregs->csr |= CSR_DMA_ENABLE; */
463757f5badSFinn Thain #else
4641da177e4SLinus Torvalds 	sun3_udc_write(UDC_RESET, UDC_CSR);
4651da177e4SLinus Torvalds 	dregs->fifo_count = 0;
4661da177e4SLinus Torvalds 	dregs->csr &= ~CSR_SEND;
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 	/* reset fifo */
4691da177e4SLinus Torvalds 	dregs->csr &= ~CSR_FIFO;
4701da177e4SLinus Torvalds 	dregs->csr |= CSR_FIFO;
471757f5badSFinn Thain #endif
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds 	sun3_dma_setup_done = NULL;
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	return ret;
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds }
4781da177e4SLinus Torvalds 
479e9db3198SFinn Thain #include "NCR5380.c"
4801da177e4SLinus Torvalds 
4810d31f875SFinn Thain #ifdef SUN3_SCSI_VME
4820d31f875SFinn Thain #define SUN3_SCSI_NAME          "Sun3 NCR5380 VME SCSI"
4830d31f875SFinn Thain #define DRV_MODULE_NAME         "sun3_scsi_vme"
4840d31f875SFinn Thain #else
4850d31f875SFinn Thain #define SUN3_SCSI_NAME          "Sun3 NCR5380 SCSI"
4860d31f875SFinn Thain #define DRV_MODULE_NAME         "sun3_scsi"
4870d31f875SFinn Thain #endif
4880d31f875SFinn Thain 
4890d31f875SFinn Thain #define PFX                     DRV_MODULE_NAME ": "
4900d31f875SFinn Thain 
4910d31f875SFinn Thain static struct scsi_host_template sun3_scsi_template = {
4920d31f875SFinn Thain 	.module			= THIS_MODULE,
4930d31f875SFinn Thain 	.proc_name		= DRV_MODULE_NAME,
4941da177e4SLinus Torvalds 	.name			= SUN3_SCSI_NAME,
4951da177e4SLinus Torvalds 	.info			= sun3scsi_info,
4961da177e4SLinus Torvalds 	.queuecommand		= sun3scsi_queue_command,
4971da177e4SLinus Torvalds 	.eh_abort_handler	= sun3scsi_abort,
4981da177e4SLinus Torvalds 	.eh_bus_reset_handler	= sun3scsi_bus_reset,
499d572f65fSFinn Thain 	.can_queue		= 16,
5001da177e4SLinus Torvalds 	.this_id		= 7,
501d572f65fSFinn Thain 	.sg_tablesize		= SG_NONE,
502d572f65fSFinn Thain 	.cmd_per_lun		= 2,
503aa2e2cb1SFinn Thain 	.use_clustering		= DISABLE_CLUSTERING,
50432b26a10SFinn Thain 	.cmd_size		= NCR5380_CMD_SIZE,
5051da177e4SLinus Torvalds };
5061da177e4SLinus Torvalds 
5070d31f875SFinn Thain static int __init sun3_scsi_probe(struct platform_device *pdev)
5080d31f875SFinn Thain {
5090d31f875SFinn Thain 	struct Scsi_Host *instance;
510820682b1SFinn Thain 	struct NCR5380_hostdata *hostdata;
5110d31f875SFinn Thain 	int error;
5120d31f875SFinn Thain 	struct resource *irq, *mem;
51361e1ce58SFinn Thain 	void __iomem *ioaddr;
514ca513fc9SFinn Thain 	int host_flags = 0;
5150d31f875SFinn Thain #ifdef SUN3_SCSI_VME
5160d31f875SFinn Thain 	int i;
5170d31f875SFinn Thain #endif
5181da177e4SLinus Torvalds 
5190d31f875SFinn Thain 	if (setup_can_queue > 0)
5200d31f875SFinn Thain 		sun3_scsi_template.can_queue = setup_can_queue;
5210d31f875SFinn Thain 	if (setup_cmd_per_lun > 0)
5220d31f875SFinn Thain 		sun3_scsi_template.cmd_per_lun = setup_cmd_per_lun;
5230d31f875SFinn Thain 	if (setup_sg_tablesize >= 0)
5240d31f875SFinn Thain 		sun3_scsi_template.sg_tablesize = setup_sg_tablesize;
5250d31f875SFinn Thain 	if (setup_hostid >= 0)
5260d31f875SFinn Thain 		sun3_scsi_template.this_id = setup_hostid & 7;
5271da177e4SLinus Torvalds 
5280d31f875SFinn Thain #ifdef SUN3_SCSI_VME
5290d31f875SFinn Thain 	ioaddr = NULL;
5300d31f875SFinn Thain 	for (i = 0; i < 2; i++) {
5310d31f875SFinn Thain 		unsigned char x;
5320d31f875SFinn Thain 
5330d31f875SFinn Thain 		irq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
5340d31f875SFinn Thain 		mem = platform_get_resource(pdev, IORESOURCE_MEM, i);
5350d31f875SFinn Thain 		if (!irq || !mem)
5360d31f875SFinn Thain 			break;
5370d31f875SFinn Thain 
5380d31f875SFinn Thain 		ioaddr = sun3_ioremap(mem->start, resource_size(mem),
5390d31f875SFinn Thain 		                      SUN3_PAGE_TYPE_VME16);
5400d31f875SFinn Thain 		dregs = (struct sun3_dma_regs *)(ioaddr + 8);
5410d31f875SFinn Thain 
5420d31f875SFinn Thain 		if (sun3_map_test((unsigned long)dregs, &x)) {
5430d31f875SFinn Thain 			unsigned short oldcsr;
5440d31f875SFinn Thain 
5450d31f875SFinn Thain 			oldcsr = dregs->csr;
5460d31f875SFinn Thain 			dregs->csr = 0;
5470d31f875SFinn Thain 			udelay(SUN3_DMA_DELAY);
5480d31f875SFinn Thain 			if (dregs->csr == 0x1400)
5490d31f875SFinn Thain 				break;
5500d31f875SFinn Thain 
5510d31f875SFinn Thain 			dregs->csr = oldcsr;
5520d31f875SFinn Thain 		}
5530d31f875SFinn Thain 
5540d31f875SFinn Thain 		iounmap(ioaddr);
5550d31f875SFinn Thain 		ioaddr = NULL;
5560d31f875SFinn Thain 	}
5570d31f875SFinn Thain 	if (!ioaddr)
5580d31f875SFinn Thain 		return -ENODEV;
5590d31f875SFinn Thain #else
5600d31f875SFinn Thain 	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
5610d31f875SFinn Thain 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5620d31f875SFinn Thain 	if (!irq || !mem)
5630d31f875SFinn Thain 		return -ENODEV;
5640d31f875SFinn Thain 
5650d31f875SFinn Thain 	ioaddr = ioremap(mem->start, resource_size(mem));
5660d31f875SFinn Thain 	dregs = (struct sun3_dma_regs *)(ioaddr + 8);
5670d31f875SFinn Thain 
5680d31f875SFinn Thain 	udc_regs = dvma_malloc(sizeof(struct sun3_udc_regs));
5690d31f875SFinn Thain 	if (!udc_regs) {
5700d31f875SFinn Thain 		pr_err(PFX "couldn't allocate DVMA memory!\n");
5710d31f875SFinn Thain 		iounmap(ioaddr);
5720d31f875SFinn Thain 		return -ENOMEM;
5730d31f875SFinn Thain 	}
5740d31f875SFinn Thain #endif
5750d31f875SFinn Thain 
5760d31f875SFinn Thain 	instance = scsi_host_alloc(&sun3_scsi_template,
5770d31f875SFinn Thain 	                           sizeof(struct NCR5380_hostdata));
5780d31f875SFinn Thain 	if (!instance) {
5790d31f875SFinn Thain 		error = -ENOMEM;
5800d31f875SFinn Thain 		goto fail_alloc;
5810d31f875SFinn Thain 	}
5820d31f875SFinn Thain 
5830d31f875SFinn Thain 	instance->irq = irq->start;
5840d31f875SFinn Thain 
585820682b1SFinn Thain 	hostdata = shost_priv(instance);
58661e1ce58SFinn Thain 	hostdata->base = mem->start;
58761e1ce58SFinn Thain 	hostdata->io = ioaddr;
588820682b1SFinn Thain 
5890ad0eff9SFinn Thain 	error = NCR5380_init(instance, host_flags);
5900ad0eff9SFinn Thain 	if (error)
5910ad0eff9SFinn Thain 		goto fail_init;
5920d31f875SFinn Thain 
5930d31f875SFinn Thain 	error = request_irq(instance->irq, scsi_sun3_intr, 0,
5940d31f875SFinn Thain 	                    "NCR5380", instance);
5950d31f875SFinn Thain 	if (error) {
5960d31f875SFinn Thain 		pr_err(PFX "scsi%d: IRQ %d not free, bailing out\n",
5970d31f875SFinn Thain 		       instance->host_no, instance->irq);
5980d31f875SFinn Thain 		goto fail_irq;
5990d31f875SFinn Thain 	}
6000d31f875SFinn Thain 
6010d31f875SFinn Thain 	dregs->csr = 0;
6020d31f875SFinn Thain 	udelay(SUN3_DMA_DELAY);
6030d31f875SFinn Thain 	dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR;
6040d31f875SFinn Thain 	udelay(SUN3_DMA_DELAY);
6050d31f875SFinn Thain 	dregs->fifo_count = 0;
6060d31f875SFinn Thain #ifdef SUN3_SCSI_VME
6070d31f875SFinn Thain 	dregs->fifo_count_hi = 0;
6080d31f875SFinn Thain 	dregs->dma_addr_hi = 0;
6090d31f875SFinn Thain 	dregs->dma_addr_lo = 0;
6100d31f875SFinn Thain 	dregs->dma_count_hi = 0;
6110d31f875SFinn Thain 	dregs->dma_count_lo = 0;
6120d31f875SFinn Thain 
6130d31f875SFinn Thain 	dregs->ivect = VME_DATA24 | (instance->irq & 0xff);
6140d31f875SFinn Thain #endif
6150d31f875SFinn Thain 
6169c3f0e2bSFinn Thain 	NCR5380_maybe_reset_bus(instance);
6170d31f875SFinn Thain 
6180d31f875SFinn Thain 	error = scsi_add_host(instance, NULL);
6190d31f875SFinn Thain 	if (error)
6200d31f875SFinn Thain 		goto fail_host;
6210d31f875SFinn Thain 
6220d31f875SFinn Thain 	platform_set_drvdata(pdev, instance);
6230d31f875SFinn Thain 
6240d31f875SFinn Thain 	scsi_scan_host(instance);
6250d31f875SFinn Thain 	return 0;
6260d31f875SFinn Thain 
6270d31f875SFinn Thain fail_host:
6280d31f875SFinn Thain 	free_irq(instance->irq, instance);
6290d31f875SFinn Thain fail_irq:
6300d31f875SFinn Thain 	NCR5380_exit(instance);
6310ad0eff9SFinn Thain fail_init:
6320d31f875SFinn Thain 	scsi_host_put(instance);
6330d31f875SFinn Thain fail_alloc:
6340d31f875SFinn Thain 	if (udc_regs)
6350d31f875SFinn Thain 		dvma_free(udc_regs);
63661e1ce58SFinn Thain 	iounmap(ioaddr);
6370d31f875SFinn Thain 	return error;
6380d31f875SFinn Thain }
6390d31f875SFinn Thain 
6400d31f875SFinn Thain static int __exit sun3_scsi_remove(struct platform_device *pdev)
6410d31f875SFinn Thain {
6420d31f875SFinn Thain 	struct Scsi_Host *instance = platform_get_drvdata(pdev);
64361e1ce58SFinn Thain 	struct NCR5380_hostdata *hostdata = shost_priv(instance);
64461e1ce58SFinn Thain 	void __iomem *ioaddr = hostdata->io;
6450d31f875SFinn Thain 
6460d31f875SFinn Thain 	scsi_remove_host(instance);
6470d31f875SFinn Thain 	free_irq(instance->irq, instance);
6480d31f875SFinn Thain 	NCR5380_exit(instance);
6490d31f875SFinn Thain 	scsi_host_put(instance);
6500d31f875SFinn Thain 	if (udc_regs)
6510d31f875SFinn Thain 		dvma_free(udc_regs);
65261e1ce58SFinn Thain 	iounmap(ioaddr);
6530d31f875SFinn Thain 	return 0;
6540d31f875SFinn Thain }
6550d31f875SFinn Thain 
6560d31f875SFinn Thain static struct platform_driver sun3_scsi_driver = {
6570d31f875SFinn Thain 	.remove = __exit_p(sun3_scsi_remove),
6580d31f875SFinn Thain 	.driver = {
6590d31f875SFinn Thain 		.name	= DRV_MODULE_NAME,
6600d31f875SFinn Thain 	},
6610d31f875SFinn Thain };
6620d31f875SFinn Thain 
6630d31f875SFinn Thain module_platform_driver_probe(sun3_scsi_driver, sun3_scsi_probe);
6640d31f875SFinn Thain 
6650d31f875SFinn Thain MODULE_ALIAS("platform:" DRV_MODULE_NAME);
6661da177e4SLinus Torvalds MODULE_LICENSE("GPL");
667