162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Sun3 DMA routines added by Sam Creasey (sammy@sammy.net) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * VME support added by Sam Creasey 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * TODO: modify this driver to support multiple Sun3 SCSI VME boards 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Adapted from mac_scsinew.c: 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * Generic Macintosh NCR5380 driver 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Copyright 1998, Michael Schmitz <mschmitz@lbl.gov> 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * derived in part from: 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Generic Generic NCR5380 driver 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Copyright 1995, Russell King 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/types.h> 2762306a36Sopenharmony_ci#include <linux/delay.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/ioport.h> 3062306a36Sopenharmony_ci#include <linux/init.h> 3162306a36Sopenharmony_ci#include <linux/blkdev.h> 3262306a36Sopenharmony_ci#include <linux/platform_device.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <asm/io.h> 3562306a36Sopenharmony_ci#include <asm/dvma.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <scsi/scsi_host.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* minimum number of bytes to do dma on */ 4062306a36Sopenharmony_ci#define DMA_MIN_SIZE 129 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* Definitions for the core NCR5380 driver. */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define NCR5380_implementation_fields /* none */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define NCR5380_read(reg) in_8(hostdata->io + (reg)) 4762306a36Sopenharmony_ci#define NCR5380_write(reg, value) out_8(hostdata->io + (reg), value) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define NCR5380_queue_command sun3scsi_queue_command 5062306a36Sopenharmony_ci#define NCR5380_host_reset sun3scsi_host_reset 5162306a36Sopenharmony_ci#define NCR5380_abort sun3scsi_abort 5262306a36Sopenharmony_ci#define NCR5380_info sun3scsi_info 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define NCR5380_dma_xfer_len sun3scsi_dma_xfer_len 5562306a36Sopenharmony_ci#define NCR5380_dma_recv_setup sun3scsi_dma_count 5662306a36Sopenharmony_ci#define NCR5380_dma_send_setup sun3scsi_dma_count 5762306a36Sopenharmony_ci#define NCR5380_dma_residual sun3scsi_dma_residual 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#include "NCR5380.h" 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* dma regs start at regbase + 8, directly after the NCR regs */ 6262306a36Sopenharmony_cistruct sun3_dma_regs { 6362306a36Sopenharmony_ci unsigned short dma_addr_hi; /* vme only */ 6462306a36Sopenharmony_ci unsigned short dma_addr_lo; /* vme only */ 6562306a36Sopenharmony_ci unsigned short dma_count_hi; /* vme only */ 6662306a36Sopenharmony_ci unsigned short dma_count_lo; /* vme only */ 6762306a36Sopenharmony_ci unsigned short udc_data; /* udc dma data reg (obio only) */ 6862306a36Sopenharmony_ci unsigned short udc_addr; /* uda dma addr reg (obio only) */ 6962306a36Sopenharmony_ci unsigned short fifo_data; /* fifo data reg, 7062306a36Sopenharmony_ci * holds extra byte on odd dma reads 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci unsigned short fifo_count; 7362306a36Sopenharmony_ci unsigned short csr; /* control/status reg */ 7462306a36Sopenharmony_ci unsigned short bpack_hi; /* vme only */ 7562306a36Sopenharmony_ci unsigned short bpack_lo; /* vme only */ 7662306a36Sopenharmony_ci unsigned short ivect; /* vme only */ 7762306a36Sopenharmony_ci unsigned short fifo_count_hi; /* vme only */ 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* ucd chip specific regs - live in dvma space */ 8162306a36Sopenharmony_cistruct sun3_udc_regs { 8262306a36Sopenharmony_ci unsigned short rsel; /* select regs to load */ 8362306a36Sopenharmony_ci unsigned short addr_hi; /* high word of addr */ 8462306a36Sopenharmony_ci unsigned short addr_lo; /* low word */ 8562306a36Sopenharmony_ci unsigned short count; /* words to be xfer'd */ 8662306a36Sopenharmony_ci unsigned short mode_hi; /* high word of channel mode */ 8762306a36Sopenharmony_ci unsigned short mode_lo; /* low word of channel mode */ 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* addresses of the udc registers */ 9162306a36Sopenharmony_ci#define UDC_MODE 0x38 9262306a36Sopenharmony_ci#define UDC_CSR 0x2e /* command/status */ 9362306a36Sopenharmony_ci#define UDC_CHN_HI 0x26 /* chain high word */ 9462306a36Sopenharmony_ci#define UDC_CHN_LO 0x22 /* chain lo word */ 9562306a36Sopenharmony_ci#define UDC_CURA_HI 0x1a /* cur reg A high */ 9662306a36Sopenharmony_ci#define UDC_CURA_LO 0x0a /* cur reg A low */ 9762306a36Sopenharmony_ci#define UDC_CURB_HI 0x12 /* cur reg B high */ 9862306a36Sopenharmony_ci#define UDC_CURB_LO 0x02 /* cur reg B low */ 9962306a36Sopenharmony_ci#define UDC_MODE_HI 0x56 /* mode reg high */ 10062306a36Sopenharmony_ci#define UDC_MODE_LO 0x52 /* mode reg low */ 10162306a36Sopenharmony_ci#define UDC_COUNT 0x32 /* words to xfer */ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* some udc commands */ 10462306a36Sopenharmony_ci#define UDC_RESET 0 10562306a36Sopenharmony_ci#define UDC_CHN_START 0xa0 /* start chain */ 10662306a36Sopenharmony_ci#define UDC_INT_ENABLE 0x32 /* channel 1 int on */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* udc mode words */ 10962306a36Sopenharmony_ci#define UDC_MODE_HIWORD 0x40 11062306a36Sopenharmony_ci#define UDC_MODE_LSEND 0xc2 11162306a36Sopenharmony_ci#define UDC_MODE_LRECV 0xd2 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* udc reg selections */ 11462306a36Sopenharmony_ci#define UDC_RSEL_SEND 0x282 11562306a36Sopenharmony_ci#define UDC_RSEL_RECV 0x182 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* bits in csr reg */ 11862306a36Sopenharmony_ci#define CSR_DMA_ACTIVE 0x8000 11962306a36Sopenharmony_ci#define CSR_DMA_CONFLICT 0x4000 12062306a36Sopenharmony_ci#define CSR_DMA_BUSERR 0x2000 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define CSR_FIFO_EMPTY 0x400 /* fifo flushed? */ 12362306a36Sopenharmony_ci#define CSR_SDB_INT 0x200 /* sbc interrupt pending */ 12462306a36Sopenharmony_ci#define CSR_DMA_INT 0x100 /* dma interrupt pending */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define CSR_LEFT 0xc0 12762306a36Sopenharmony_ci#define CSR_LEFT_3 0xc0 12862306a36Sopenharmony_ci#define CSR_LEFT_2 0x80 12962306a36Sopenharmony_ci#define CSR_LEFT_1 0x40 13062306a36Sopenharmony_ci#define CSR_PACK_ENABLE 0x20 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define CSR_DMA_ENABLE 0x10 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define CSR_SEND 0x8 /* 1 = send 0 = recv */ 13562306a36Sopenharmony_ci#define CSR_FIFO 0x2 /* reset fifo */ 13662306a36Sopenharmony_ci#define CSR_INTR 0x4 /* interrupt enable */ 13762306a36Sopenharmony_ci#define CSR_SCSI 0x1 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define VME_DATA24 0x3d00 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciextern int sun3_map_test(unsigned long, char *); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int setup_can_queue = -1; 14462306a36Sopenharmony_cimodule_param(setup_can_queue, int, 0); 14562306a36Sopenharmony_cistatic int setup_cmd_per_lun = -1; 14662306a36Sopenharmony_cimodule_param(setup_cmd_per_lun, int, 0); 14762306a36Sopenharmony_cistatic int setup_sg_tablesize = -1; 14862306a36Sopenharmony_cimodule_param(setup_sg_tablesize, int, 0); 14962306a36Sopenharmony_cistatic int setup_hostid = -1; 15062306a36Sopenharmony_cimodule_param(setup_hostid, int, 0); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* ms to wait after hitting dma regs */ 15362306a36Sopenharmony_ci#define SUN3_DMA_DELAY 10 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* dvma buffer to allocate -- 32k should hopefully be more than sufficient */ 15662306a36Sopenharmony_ci#define SUN3_DVMA_BUFSIZE 0xe000 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic struct scsi_cmnd *sun3_dma_setup_done; 15962306a36Sopenharmony_cistatic volatile struct sun3_dma_regs *dregs; 16062306a36Sopenharmony_cistatic struct sun3_udc_regs *udc_regs; 16162306a36Sopenharmony_cistatic unsigned char *sun3_dma_orig_addr; 16262306a36Sopenharmony_cistatic unsigned long sun3_dma_orig_count; 16362306a36Sopenharmony_cistatic int sun3_dma_active; 16462306a36Sopenharmony_cistatic unsigned long last_residual; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#ifndef SUN3_SCSI_VME 16762306a36Sopenharmony_ci/* dma controller register access functions */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic inline unsigned short sun3_udc_read(unsigned char reg) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci unsigned short ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci dregs->udc_addr = UDC_CSR; 17462306a36Sopenharmony_ci udelay(SUN3_DMA_DELAY); 17562306a36Sopenharmony_ci ret = dregs->udc_data; 17662306a36Sopenharmony_ci udelay(SUN3_DMA_DELAY); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic inline void sun3_udc_write(unsigned short val, unsigned char reg) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci dregs->udc_addr = reg; 18462306a36Sopenharmony_ci udelay(SUN3_DMA_DELAY); 18562306a36Sopenharmony_ci dregs->udc_data = val; 18662306a36Sopenharmony_ci udelay(SUN3_DMA_DELAY); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci#endif 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci// safe bits for the CSR 19162306a36Sopenharmony_ci#define CSR_GOOD 0x060f 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic irqreturn_t scsi_sun3_intr(int irq, void *dev) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct Scsi_Host *instance = dev; 19662306a36Sopenharmony_ci unsigned short csr = dregs->csr; 19762306a36Sopenharmony_ci int handled = 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 20062306a36Sopenharmony_ci dregs->csr &= ~CSR_DMA_ENABLE; 20162306a36Sopenharmony_ci#endif 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if(csr & ~CSR_GOOD) { 20462306a36Sopenharmony_ci if (csr & CSR_DMA_BUSERR) 20562306a36Sopenharmony_ci shost_printk(KERN_ERR, instance, "bus error in DMA\n"); 20662306a36Sopenharmony_ci if (csr & CSR_DMA_CONFLICT) 20762306a36Sopenharmony_ci shost_printk(KERN_ERR, instance, "DMA conflict\n"); 20862306a36Sopenharmony_ci handled = 1; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if(csr & (CSR_SDB_INT | CSR_DMA_INT)) { 21262306a36Sopenharmony_ci NCR5380_intr(irq, dev); 21362306a36Sopenharmony_ci handled = 1; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return IRQ_RETVAL(handled); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* sun3scsi_dma_setup() -- initialize the dma controller for a read/write */ 22062306a36Sopenharmony_cistatic int sun3scsi_dma_setup(struct NCR5380_hostdata *hostdata, 22162306a36Sopenharmony_ci unsigned char *data, int count, int write_flag) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci void *addr; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if(sun3_dma_orig_addr != NULL) 22662306a36Sopenharmony_ci dvma_unmap(sun3_dma_orig_addr); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 22962306a36Sopenharmony_ci addr = (void *)dvma_map_vme((unsigned long) data, count); 23062306a36Sopenharmony_ci#else 23162306a36Sopenharmony_ci addr = (void *)dvma_map((unsigned long) data, count); 23262306a36Sopenharmony_ci#endif 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci sun3_dma_orig_addr = addr; 23562306a36Sopenharmony_ci sun3_dma_orig_count = count; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#ifndef SUN3_SCSI_VME 23862306a36Sopenharmony_ci dregs->fifo_count = 0; 23962306a36Sopenharmony_ci sun3_udc_write(UDC_RESET, UDC_CSR); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* reset fifo */ 24262306a36Sopenharmony_ci dregs->csr &= ~CSR_FIFO; 24362306a36Sopenharmony_ci dregs->csr |= CSR_FIFO; 24462306a36Sopenharmony_ci#endif 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* set direction */ 24762306a36Sopenharmony_ci if(write_flag) 24862306a36Sopenharmony_ci dregs->csr |= CSR_SEND; 24962306a36Sopenharmony_ci else 25062306a36Sopenharmony_ci dregs->csr &= ~CSR_SEND; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 25362306a36Sopenharmony_ci dregs->csr |= CSR_PACK_ENABLE; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci dregs->dma_addr_hi = ((unsigned long)addr >> 16); 25662306a36Sopenharmony_ci dregs->dma_addr_lo = ((unsigned long)addr & 0xffff); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci dregs->dma_count_hi = 0; 25962306a36Sopenharmony_ci dregs->dma_count_lo = 0; 26062306a36Sopenharmony_ci dregs->fifo_count_hi = 0; 26162306a36Sopenharmony_ci dregs->fifo_count = 0; 26262306a36Sopenharmony_ci#else 26362306a36Sopenharmony_ci /* byte count for fifo */ 26462306a36Sopenharmony_ci dregs->fifo_count = count; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci sun3_udc_write(UDC_RESET, UDC_CSR); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* reset fifo */ 26962306a36Sopenharmony_ci dregs->csr &= ~CSR_FIFO; 27062306a36Sopenharmony_ci dregs->csr |= CSR_FIFO; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if(dregs->fifo_count != count) { 27362306a36Sopenharmony_ci shost_printk(KERN_ERR, hostdata->host, 27462306a36Sopenharmony_ci "FIFO mismatch %04x not %04x\n", 27562306a36Sopenharmony_ci dregs->fifo_count, (unsigned int) count); 27662306a36Sopenharmony_ci NCR5380_dprint(NDEBUG_DMA, hostdata->host); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* setup udc */ 28062306a36Sopenharmony_ci udc_regs->addr_hi = (((unsigned long)(addr) & 0xff0000) >> 8); 28162306a36Sopenharmony_ci udc_regs->addr_lo = ((unsigned long)(addr) & 0xffff); 28262306a36Sopenharmony_ci udc_regs->count = count/2; /* count in words */ 28362306a36Sopenharmony_ci udc_regs->mode_hi = UDC_MODE_HIWORD; 28462306a36Sopenharmony_ci if(write_flag) { 28562306a36Sopenharmony_ci if(count & 1) 28662306a36Sopenharmony_ci udc_regs->count++; 28762306a36Sopenharmony_ci udc_regs->mode_lo = UDC_MODE_LSEND; 28862306a36Sopenharmony_ci udc_regs->rsel = UDC_RSEL_SEND; 28962306a36Sopenharmony_ci } else { 29062306a36Sopenharmony_ci udc_regs->mode_lo = UDC_MODE_LRECV; 29162306a36Sopenharmony_ci udc_regs->rsel = UDC_RSEL_RECV; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* announce location of regs block */ 29562306a36Sopenharmony_ci sun3_udc_write(((dvma_vtob(udc_regs) & 0xff0000) >> 8), 29662306a36Sopenharmony_ci UDC_CHN_HI); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci sun3_udc_write((dvma_vtob(udc_regs) & 0xffff), UDC_CHN_LO); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* set dma master on */ 30162306a36Sopenharmony_ci sun3_udc_write(0xd, UDC_MODE); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* interrupt enable */ 30462306a36Sopenharmony_ci sun3_udc_write(UDC_INT_ENABLE, UDC_CSR); 30562306a36Sopenharmony_ci#endif 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return count; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int sun3scsi_dma_count(struct NCR5380_hostdata *hostdata, 31262306a36Sopenharmony_ci unsigned char *data, int count) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci return count; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic inline int sun3scsi_dma_recv_setup(struct NCR5380_hostdata *hostdata, 31862306a36Sopenharmony_ci unsigned char *data, int count) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci return sun3scsi_dma_setup(hostdata, data, count, 0); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic inline int sun3scsi_dma_send_setup(struct NCR5380_hostdata *hostdata, 32462306a36Sopenharmony_ci unsigned char *data, int count) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci return sun3scsi_dma_setup(hostdata, data, count, 1); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int sun3scsi_dma_residual(struct NCR5380_hostdata *hostdata) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci return last_residual; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int sun3scsi_dma_xfer_len(struct NCR5380_hostdata *hostdata, 33562306a36Sopenharmony_ci struct scsi_cmnd *cmd) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci int wanted_len = NCR5380_to_ncmd(cmd)->this_residual; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (wanted_len < DMA_MIN_SIZE || blk_rq_is_passthrough(scsi_cmd_to_rq(cmd))) 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return wanted_len; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic inline int sun3scsi_dma_start(unsigned long count, unsigned char *data) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 34862306a36Sopenharmony_ci unsigned short csr; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci csr = dregs->csr; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dregs->dma_count_hi = (sun3_dma_orig_count >> 16); 35362306a36Sopenharmony_ci dregs->dma_count_lo = (sun3_dma_orig_count & 0xffff); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci dregs->fifo_count_hi = (sun3_dma_orig_count >> 16); 35662306a36Sopenharmony_ci dregs->fifo_count = (sun3_dma_orig_count & 0xffff); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci/* if(!(csr & CSR_DMA_ENABLE)) 35962306a36Sopenharmony_ci * dregs->csr |= CSR_DMA_ENABLE; 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci#else 36262306a36Sopenharmony_ci sun3_udc_write(UDC_CHN_START, UDC_CSR); 36362306a36Sopenharmony_ci#endif 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* clean up after our dma is done */ 36962306a36Sopenharmony_cistatic int sun3scsi_dma_finish(enum dma_data_direction data_dir) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci const bool write_flag = data_dir == DMA_TO_DEVICE; 37262306a36Sopenharmony_ci unsigned short __maybe_unused count; 37362306a36Sopenharmony_ci unsigned short fifo; 37462306a36Sopenharmony_ci int ret = 0; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci sun3_dma_active = 0; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 37962306a36Sopenharmony_ci dregs->csr &= ~CSR_DMA_ENABLE; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci fifo = dregs->fifo_count; 38262306a36Sopenharmony_ci if (write_flag) { 38362306a36Sopenharmony_ci if ((fifo > 0) && (fifo < sun3_dma_orig_count)) 38462306a36Sopenharmony_ci fifo++; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci last_residual = fifo; 38862306a36Sopenharmony_ci /* empty bytes from the fifo which didn't make it */ 38962306a36Sopenharmony_ci if ((!write_flag) && (dregs->csr & CSR_LEFT)) { 39062306a36Sopenharmony_ci unsigned char *vaddr; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci vaddr = (unsigned char *)dvma_vmetov(sun3_dma_orig_addr); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci vaddr += (sun3_dma_orig_count - fifo); 39562306a36Sopenharmony_ci vaddr--; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci switch (dregs->csr & CSR_LEFT) { 39862306a36Sopenharmony_ci case CSR_LEFT_3: 39962306a36Sopenharmony_ci *vaddr = (dregs->bpack_lo & 0xff00) >> 8; 40062306a36Sopenharmony_ci vaddr--; 40162306a36Sopenharmony_ci fallthrough; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci case CSR_LEFT_2: 40462306a36Sopenharmony_ci *vaddr = (dregs->bpack_hi & 0x00ff); 40562306a36Sopenharmony_ci vaddr--; 40662306a36Sopenharmony_ci fallthrough; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci case CSR_LEFT_1: 40962306a36Sopenharmony_ci *vaddr = (dregs->bpack_hi & 0xff00) >> 8; 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci#else 41462306a36Sopenharmony_ci // check to empty the fifo on a read 41562306a36Sopenharmony_ci if(!write_flag) { 41662306a36Sopenharmony_ci int tmo = 20000; /* .2 sec */ 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci while(1) { 41962306a36Sopenharmony_ci if(dregs->csr & CSR_FIFO_EMPTY) 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if(--tmo <= 0) { 42362306a36Sopenharmony_ci printk("sun3scsi: fifo failed to empty!\n"); 42462306a36Sopenharmony_ci return 1; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci udelay(10); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci dregs->udc_addr = 0x32; 43162306a36Sopenharmony_ci udelay(SUN3_DMA_DELAY); 43262306a36Sopenharmony_ci count = 2 * dregs->udc_data; 43362306a36Sopenharmony_ci udelay(SUN3_DMA_DELAY); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci fifo = dregs->fifo_count; 43662306a36Sopenharmony_ci last_residual = fifo; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* empty bytes from the fifo which didn't make it */ 43962306a36Sopenharmony_ci if((!write_flag) && (count - fifo) == 2) { 44062306a36Sopenharmony_ci unsigned short data; 44162306a36Sopenharmony_ci unsigned char *vaddr; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci data = dregs->fifo_data; 44462306a36Sopenharmony_ci vaddr = (unsigned char *)dvma_btov(sun3_dma_orig_addr); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci vaddr += (sun3_dma_orig_count - fifo); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci vaddr[-2] = (data & 0xff00) >> 8; 44962306a36Sopenharmony_ci vaddr[-1] = (data & 0xff); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci#endif 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci dvma_unmap(sun3_dma_orig_addr); 45462306a36Sopenharmony_ci sun3_dma_orig_addr = NULL; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 45762306a36Sopenharmony_ci dregs->dma_addr_hi = 0; 45862306a36Sopenharmony_ci dregs->dma_addr_lo = 0; 45962306a36Sopenharmony_ci dregs->dma_count_hi = 0; 46062306a36Sopenharmony_ci dregs->dma_count_lo = 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci dregs->fifo_count = 0; 46362306a36Sopenharmony_ci dregs->fifo_count_hi = 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci dregs->csr &= ~CSR_SEND; 46662306a36Sopenharmony_ci/* dregs->csr |= CSR_DMA_ENABLE; */ 46762306a36Sopenharmony_ci#else 46862306a36Sopenharmony_ci sun3_udc_write(UDC_RESET, UDC_CSR); 46962306a36Sopenharmony_ci dregs->fifo_count = 0; 47062306a36Sopenharmony_ci dregs->csr &= ~CSR_SEND; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* reset fifo */ 47362306a36Sopenharmony_ci dregs->csr &= ~CSR_FIFO; 47462306a36Sopenharmony_ci dregs->csr |= CSR_FIFO; 47562306a36Sopenharmony_ci#endif 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci sun3_dma_setup_done = NULL; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci#include "NCR5380.c" 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 48662306a36Sopenharmony_ci#define SUN3_SCSI_NAME "Sun3 NCR5380 VME SCSI" 48762306a36Sopenharmony_ci#define DRV_MODULE_NAME "sun3_scsi_vme" 48862306a36Sopenharmony_ci#else 48962306a36Sopenharmony_ci#define SUN3_SCSI_NAME "Sun3 NCR5380 SCSI" 49062306a36Sopenharmony_ci#define DRV_MODULE_NAME "sun3_scsi" 49162306a36Sopenharmony_ci#endif 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci#define PFX DRV_MODULE_NAME ": " 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic struct scsi_host_template sun3_scsi_template = { 49662306a36Sopenharmony_ci .module = THIS_MODULE, 49762306a36Sopenharmony_ci .proc_name = DRV_MODULE_NAME, 49862306a36Sopenharmony_ci .name = SUN3_SCSI_NAME, 49962306a36Sopenharmony_ci .info = sun3scsi_info, 50062306a36Sopenharmony_ci .queuecommand = sun3scsi_queue_command, 50162306a36Sopenharmony_ci .eh_abort_handler = sun3scsi_abort, 50262306a36Sopenharmony_ci .eh_host_reset_handler = sun3scsi_host_reset, 50362306a36Sopenharmony_ci .can_queue = 16, 50462306a36Sopenharmony_ci .this_id = 7, 50562306a36Sopenharmony_ci .sg_tablesize = 1, 50662306a36Sopenharmony_ci .cmd_per_lun = 2, 50762306a36Sopenharmony_ci .dma_boundary = PAGE_SIZE - 1, 50862306a36Sopenharmony_ci .cmd_size = sizeof(struct NCR5380_cmd), 50962306a36Sopenharmony_ci}; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int __init sun3_scsi_probe(struct platform_device *pdev) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct Scsi_Host *instance; 51462306a36Sopenharmony_ci struct NCR5380_hostdata *hostdata; 51562306a36Sopenharmony_ci int error; 51662306a36Sopenharmony_ci struct resource *irq, *mem; 51762306a36Sopenharmony_ci void __iomem *ioaddr; 51862306a36Sopenharmony_ci int host_flags = 0; 51962306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 52062306a36Sopenharmony_ci int i; 52162306a36Sopenharmony_ci#endif 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (setup_can_queue > 0) 52462306a36Sopenharmony_ci sun3_scsi_template.can_queue = setup_can_queue; 52562306a36Sopenharmony_ci if (setup_cmd_per_lun > 0) 52662306a36Sopenharmony_ci sun3_scsi_template.cmd_per_lun = setup_cmd_per_lun; 52762306a36Sopenharmony_ci if (setup_sg_tablesize > 0) 52862306a36Sopenharmony_ci sun3_scsi_template.sg_tablesize = setup_sg_tablesize; 52962306a36Sopenharmony_ci if (setup_hostid >= 0) 53062306a36Sopenharmony_ci sun3_scsi_template.this_id = setup_hostid & 7; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 53362306a36Sopenharmony_ci ioaddr = NULL; 53462306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 53562306a36Sopenharmony_ci unsigned char x; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci irq = platform_get_resource(pdev, IORESOURCE_IRQ, i); 53862306a36Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, i); 53962306a36Sopenharmony_ci if (!irq || !mem) 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci ioaddr = sun3_ioremap(mem->start, resource_size(mem), 54362306a36Sopenharmony_ci SUN3_PAGE_TYPE_VME16); 54462306a36Sopenharmony_ci dregs = (struct sun3_dma_regs *)(ioaddr + 8); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (sun3_map_test((unsigned long)dregs, &x)) { 54762306a36Sopenharmony_ci unsigned short oldcsr; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci oldcsr = dregs->csr; 55062306a36Sopenharmony_ci dregs->csr = 0; 55162306a36Sopenharmony_ci udelay(SUN3_DMA_DELAY); 55262306a36Sopenharmony_ci if (dregs->csr == 0x1400) 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci dregs->csr = oldcsr; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci iounmap(ioaddr); 55962306a36Sopenharmony_ci ioaddr = NULL; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci if (!ioaddr) 56262306a36Sopenharmony_ci return -ENODEV; 56362306a36Sopenharmony_ci#else 56462306a36Sopenharmony_ci irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 56562306a36Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 56662306a36Sopenharmony_ci if (!irq || !mem) 56762306a36Sopenharmony_ci return -ENODEV; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ioaddr = ioremap(mem->start, resource_size(mem)); 57062306a36Sopenharmony_ci dregs = (struct sun3_dma_regs *)(ioaddr + 8); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci udc_regs = dvma_malloc(sizeof(struct sun3_udc_regs)); 57362306a36Sopenharmony_ci if (!udc_regs) { 57462306a36Sopenharmony_ci pr_err(PFX "couldn't allocate DVMA memory!\n"); 57562306a36Sopenharmony_ci iounmap(ioaddr); 57662306a36Sopenharmony_ci return -ENOMEM; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci#endif 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci instance = scsi_host_alloc(&sun3_scsi_template, 58162306a36Sopenharmony_ci sizeof(struct NCR5380_hostdata)); 58262306a36Sopenharmony_ci if (!instance) { 58362306a36Sopenharmony_ci error = -ENOMEM; 58462306a36Sopenharmony_ci goto fail_alloc; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci instance->irq = irq->start; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci hostdata = shost_priv(instance); 59062306a36Sopenharmony_ci hostdata->base = mem->start; 59162306a36Sopenharmony_ci hostdata->io = ioaddr; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci error = NCR5380_init(instance, host_flags); 59462306a36Sopenharmony_ci if (error) 59562306a36Sopenharmony_ci goto fail_init; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci error = request_irq(instance->irq, scsi_sun3_intr, 0, 59862306a36Sopenharmony_ci "NCR5380", instance); 59962306a36Sopenharmony_ci if (error) { 60062306a36Sopenharmony_ci pr_err(PFX "scsi%d: IRQ %d not free, bailing out\n", 60162306a36Sopenharmony_ci instance->host_no, instance->irq); 60262306a36Sopenharmony_ci goto fail_irq; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci dregs->csr = 0; 60662306a36Sopenharmony_ci udelay(SUN3_DMA_DELAY); 60762306a36Sopenharmony_ci dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR; 60862306a36Sopenharmony_ci udelay(SUN3_DMA_DELAY); 60962306a36Sopenharmony_ci dregs->fifo_count = 0; 61062306a36Sopenharmony_ci#ifdef SUN3_SCSI_VME 61162306a36Sopenharmony_ci dregs->fifo_count_hi = 0; 61262306a36Sopenharmony_ci dregs->dma_addr_hi = 0; 61362306a36Sopenharmony_ci dregs->dma_addr_lo = 0; 61462306a36Sopenharmony_ci dregs->dma_count_hi = 0; 61562306a36Sopenharmony_ci dregs->dma_count_lo = 0; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci dregs->ivect = VME_DATA24 | (instance->irq & 0xff); 61862306a36Sopenharmony_ci#endif 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci NCR5380_maybe_reset_bus(instance); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci error = scsi_add_host(instance, NULL); 62362306a36Sopenharmony_ci if (error) 62462306a36Sopenharmony_ci goto fail_host; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci platform_set_drvdata(pdev, instance); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci scsi_scan_host(instance); 62962306a36Sopenharmony_ci return 0; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cifail_host: 63262306a36Sopenharmony_ci free_irq(instance->irq, instance); 63362306a36Sopenharmony_cifail_irq: 63462306a36Sopenharmony_ci NCR5380_exit(instance); 63562306a36Sopenharmony_cifail_init: 63662306a36Sopenharmony_ci scsi_host_put(instance); 63762306a36Sopenharmony_cifail_alloc: 63862306a36Sopenharmony_ci if (udc_regs) 63962306a36Sopenharmony_ci dvma_free(udc_regs); 64062306a36Sopenharmony_ci iounmap(ioaddr); 64162306a36Sopenharmony_ci return error; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic int __exit sun3_scsi_remove(struct platform_device *pdev) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci struct Scsi_Host *instance = platform_get_drvdata(pdev); 64762306a36Sopenharmony_ci struct NCR5380_hostdata *hostdata = shost_priv(instance); 64862306a36Sopenharmony_ci void __iomem *ioaddr = hostdata->io; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci scsi_remove_host(instance); 65162306a36Sopenharmony_ci free_irq(instance->irq, instance); 65262306a36Sopenharmony_ci NCR5380_exit(instance); 65362306a36Sopenharmony_ci scsi_host_put(instance); 65462306a36Sopenharmony_ci if (udc_regs) 65562306a36Sopenharmony_ci dvma_free(udc_regs); 65662306a36Sopenharmony_ci iounmap(ioaddr); 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic struct platform_driver sun3_scsi_driver = { 66162306a36Sopenharmony_ci .remove = __exit_p(sun3_scsi_remove), 66262306a36Sopenharmony_ci .driver = { 66362306a36Sopenharmony_ci .name = DRV_MODULE_NAME, 66462306a36Sopenharmony_ci }, 66562306a36Sopenharmony_ci}; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cimodule_platform_driver_probe(sun3_scsi_driver, sun3_scsi_probe); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRV_MODULE_NAME); 67062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 671