xref: /openbmc/linux/drivers/scsi/a2091.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds #include <linux/types.h>
31da177e4SLinus Torvalds #include <linux/init.h>
41da177e4SLinus Torvalds #include <linux/interrupt.h>
5c737e22cSGeert Uytterhoeven #include <linux/mm.h>
6c737e22cSGeert Uytterhoeven #include <linux/slab.h>
7c737e22cSGeert Uytterhoeven #include <linux/spinlock.h>
8c737e22cSGeert Uytterhoeven #include <linux/zorro.h>
9acf3368fSPaul Gortmaker #include <linux/module.h>
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <asm/page.h>
121da177e4SLinus Torvalds #include <asm/amigaints.h>
131da177e4SLinus Torvalds #include <asm/amigahw.h>
141da177e4SLinus Torvalds 
1553555fb7SBart Van Assche #include <scsi/scsi.h>
1653555fb7SBart Van Assche #include <scsi/scsi_cmnd.h>
1753555fb7SBart Van Assche #include <scsi/scsi_device.h>
1853555fb7SBart Van Assche #include <scsi/scsi_eh.h>
1953555fb7SBart Van Assche #include <scsi/scsi_tcq.h>
201da177e4SLinus Torvalds #include "wd33c93.h"
211da177e4SLinus Torvalds #include "a2091.h"
221da177e4SLinus Torvalds 
235880f486SAdrian Bunk 
2465c2784aSGeert Uytterhoeven struct a2091_hostdata {
2565c2784aSGeert Uytterhoeven 	struct WD33C93_hostdata wh;
2665c2784aSGeert Uytterhoeven 	struct a2091_scsiregs *regs;
27479accbbSMichael Schmitz 	struct device *dev;
2865c2784aSGeert Uytterhoeven };
2965c2784aSGeert Uytterhoeven 
30479accbbSMichael Schmitz #define DMA_DIR(d)   ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)
31479accbbSMichael Schmitz 
a2091_intr(int irq,void * data)32b1de6ab7SGeert Uytterhoeven static irqreturn_t a2091_intr(int irq, void *data)
331da177e4SLinus Torvalds {
34b1de6ab7SGeert Uytterhoeven 	struct Scsi_Host *instance = data;
3565c2784aSGeert Uytterhoeven 	struct a2091_hostdata *hdata = shost_priv(instance);
3665c2784aSGeert Uytterhoeven 	unsigned int status = hdata->regs->ISTR;
371da177e4SLinus Torvalds 	unsigned long flags;
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds 	if (!(status & (ISTR_INT_F | ISTR_INT_P)) || !(status & ISTR_INTS))
401da177e4SLinus Torvalds 		return IRQ_NONE;
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds 	spin_lock_irqsave(instance->host_lock, flags);
431da177e4SLinus Torvalds 	wd33c93_intr(instance);
441da177e4SLinus Torvalds 	spin_unlock_irqrestore(instance->host_lock, flags);
451da177e4SLinus Torvalds 	return IRQ_HANDLED;
461da177e4SLinus Torvalds }
471da177e4SLinus Torvalds 
dma_setup(struct scsi_cmnd * cmd,int dir_in)4865396410SHenrik Kretzschmar static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
491da177e4SLinus Torvalds {
50dbb2da55SBart Van Assche 	struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);
51479accbbSMichael Schmitz 	unsigned long len = scsi_pointer->this_residual;
526d1d5d43SGeert Uytterhoeven 	struct Scsi_Host *instance = cmd->device->host;
5365c2784aSGeert Uytterhoeven 	struct a2091_hostdata *hdata = shost_priv(instance);
5465c2784aSGeert Uytterhoeven 	struct WD33C93_hostdata *wh = &hdata->wh;
5565c2784aSGeert Uytterhoeven 	struct a2091_scsiregs *regs = hdata->regs;
561da177e4SLinus Torvalds 	unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
57479accbbSMichael Schmitz 	dma_addr_t addr;
58479accbbSMichael Schmitz 
59479accbbSMichael Schmitz 	addr = dma_map_single(hdata->dev, scsi_pointer->ptr,
60479accbbSMichael Schmitz 			      len, DMA_DIR(dir_in));
61479accbbSMichael Schmitz 	if (dma_mapping_error(hdata->dev, addr)) {
62479accbbSMichael Schmitz 		dev_warn(hdata->dev, "cannot map SCSI data block %p\n",
63479accbbSMichael Schmitz 			 scsi_pointer->ptr);
64479accbbSMichael Schmitz 		return 1;
65479accbbSMichael Schmitz 	}
66479accbbSMichael Schmitz 	scsi_pointer->dma_handle = addr;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	/* don't allow DMA if the physical address is bad */
6909bc85b0SGeert Uytterhoeven 	if (addr & A2091_XFER_MASK) {
70479accbbSMichael Schmitz 		/* drop useless mapping */
71479accbbSMichael Schmitz 		dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
72479accbbSMichael Schmitz 				 scsi_pointer->this_residual,
73479accbbSMichael Schmitz 				 DMA_DIR(dir_in));
74479accbbSMichael Schmitz 		scsi_pointer->dma_handle = (dma_addr_t) NULL;
75479accbbSMichael Schmitz 
76dbb2da55SBart Van Assche 		wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff;
7765c2784aSGeert Uytterhoeven 		wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len,
786d1d5d43SGeert Uytterhoeven 						GFP_KERNEL);
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 		/* can't allocate memory; use PIO */
8165c2784aSGeert Uytterhoeven 		if (!wh->dma_bounce_buffer) {
8265c2784aSGeert Uytterhoeven 			wh->dma_bounce_len = 0;
831da177e4SLinus Torvalds 			return 1;
841da177e4SLinus Torvalds 		}
851da177e4SLinus Torvalds 
86479accbbSMichael Schmitz 		if (!dir_in) {
87479accbbSMichael Schmitz 			/* copy to bounce buffer for a write */
88479accbbSMichael Schmitz 			memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr,
89479accbbSMichael Schmitz 			       scsi_pointer->this_residual);
90479accbbSMichael Schmitz 		}
91479accbbSMichael Schmitz 
92479accbbSMichael Schmitz 		/* will flush/invalidate cache for us */
93479accbbSMichael Schmitz 		addr = dma_map_single(hdata->dev, wh->dma_bounce_buffer,
94479accbbSMichael Schmitz 				      wh->dma_bounce_len, DMA_DIR(dir_in));
95479accbbSMichael Schmitz 		/* can't map buffer; use PIO */
96479accbbSMichael Schmitz 		if (dma_mapping_error(hdata->dev, addr)) {
97479accbbSMichael Schmitz 			dev_warn(hdata->dev, "cannot map bounce buffer %p\n",
98479accbbSMichael Schmitz 				 wh->dma_bounce_buffer);
99479accbbSMichael Schmitz 			return 1;
100479accbbSMichael Schmitz 		}
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 		/* the bounce buffer may not be in the first 16M of physmem */
1031da177e4SLinus Torvalds 		if (addr & A2091_XFER_MASK) {
1041da177e4SLinus Torvalds 			/* we could use chipmem... maybe later */
10565c2784aSGeert Uytterhoeven 			kfree(wh->dma_bounce_buffer);
10665c2784aSGeert Uytterhoeven 			wh->dma_bounce_buffer = NULL;
10765c2784aSGeert Uytterhoeven 			wh->dma_bounce_len = 0;
1081da177e4SLinus Torvalds 			return 1;
1091da177e4SLinus Torvalds 		}
1101da177e4SLinus Torvalds 
111479accbbSMichael Schmitz 		scsi_pointer->dma_handle = addr;
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	/* setup dma direction */
1151da177e4SLinus Torvalds 	if (!dir_in)
1161da177e4SLinus Torvalds 		cntr |= CNTR_DDIR;
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	/* remember direction */
11965c2784aSGeert Uytterhoeven 	wh->dma_dir = dir_in;
1201da177e4SLinus Torvalds 
121b1de6ab7SGeert Uytterhoeven 	regs->CNTR = cntr;
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	/* setup DMA *physical* address */
124b1de6ab7SGeert Uytterhoeven 	regs->ACR = addr;
1251da177e4SLinus Torvalds 
126479accbbSMichael Schmitz 	/* no more cache flush here - dma_map_single() takes care */
127479accbbSMichael Schmitz 
1281da177e4SLinus Torvalds 	/* start DMA */
129b1de6ab7SGeert Uytterhoeven 	regs->ST_DMA = 1;
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 	/* return success */
1321da177e4SLinus Torvalds 	return 0;
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds 
dma_stop(struct Scsi_Host * instance,struct scsi_cmnd * SCpnt,int status)13565396410SHenrik Kretzschmar static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
1361da177e4SLinus Torvalds 		     int status)
1371da177e4SLinus Torvalds {
138dbb2da55SBart Van Assche 	struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(SCpnt);
13965c2784aSGeert Uytterhoeven 	struct a2091_hostdata *hdata = shost_priv(instance);
14065c2784aSGeert Uytterhoeven 	struct WD33C93_hostdata *wh = &hdata->wh;
14165c2784aSGeert Uytterhoeven 	struct a2091_scsiregs *regs = hdata->regs;
1426d1d5d43SGeert Uytterhoeven 
1431da177e4SLinus Torvalds 	/* disable SCSI interrupts */
1441da177e4SLinus Torvalds 	unsigned short cntr = CNTR_PDMD;
1451da177e4SLinus Torvalds 
14665c2784aSGeert Uytterhoeven 	if (!wh->dma_dir)
1471da177e4SLinus Torvalds 		cntr |= CNTR_DDIR;
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	/* disable SCSI interrupts */
150b1de6ab7SGeert Uytterhoeven 	regs->CNTR = cntr;
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	/* flush if we were reading */
15365c2784aSGeert Uytterhoeven 	if (wh->dma_dir) {
154b1de6ab7SGeert Uytterhoeven 		regs->FLUSH = 1;
155b1de6ab7SGeert Uytterhoeven 		while (!(regs->ISTR & ISTR_FE_FLG))
1561da177e4SLinus Torvalds 			;
1571da177e4SLinus Torvalds 	}
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	/* clear a possible interrupt */
160b1de6ab7SGeert Uytterhoeven 	regs->CINT = 1;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	/* stop DMA */
163b1de6ab7SGeert Uytterhoeven 	regs->SP_DMA = 1;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	/* restore the CONTROL bits (minus the direction flag) */
166b1de6ab7SGeert Uytterhoeven 	regs->CNTR = CNTR_PDMD | CNTR_INTEN;
1671da177e4SLinus Torvalds 
168479accbbSMichael Schmitz 	dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
169479accbbSMichael Schmitz 			 scsi_pointer->this_residual,
170479accbbSMichael Schmitz 			 DMA_DIR(wh->dma_dir));
171479accbbSMichael Schmitz 
1721da177e4SLinus Torvalds 	/* copy from a bounce buffer, if necessary */
17365c2784aSGeert Uytterhoeven 	if (status && wh->dma_bounce_buffer) {
17465c2784aSGeert Uytterhoeven 		if (wh->dma_dir)
175dbb2da55SBart Van Assche 			memcpy(scsi_pointer->ptr, wh->dma_bounce_buffer,
176dbb2da55SBart Van Assche 			       scsi_pointer->this_residual);
17765c2784aSGeert Uytterhoeven 		kfree(wh->dma_bounce_buffer);
17865c2784aSGeert Uytterhoeven 		wh->dma_bounce_buffer = NULL;
17965c2784aSGeert Uytterhoeven 		wh->dma_bounce_len = 0;
1801da177e4SLinus Torvalds 	}
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds 
183*4412df38SBart Van Assche static const struct scsi_host_template a2091_scsi_template = {
184c737e22cSGeert Uytterhoeven 	.module			= THIS_MODULE,
1851da177e4SLinus Torvalds 	.name			= "Commodore A2091/A590 SCSI",
186408bb25bSAl Viro 	.show_info		= wd33c93_show_info,
187408bb25bSAl Viro 	.write_info		= wd33c93_write_info,
188c737e22cSGeert Uytterhoeven 	.proc_name		= "A2901",
1891da177e4SLinus Torvalds 	.queuecommand		= wd33c93_queuecommand,
1901da177e4SLinus Torvalds 	.eh_abort_handler	= wd33c93_abort,
1911da177e4SLinus Torvalds 	.eh_host_reset_handler	= wd33c93_host_reset,
1921da177e4SLinus Torvalds 	.can_queue		= CAN_QUEUE,
1931da177e4SLinus Torvalds 	.this_id		= 7,
1941da177e4SLinus Torvalds 	.sg_tablesize		= SG_ALL,
1951da177e4SLinus Torvalds 	.cmd_per_lun		= CMD_PER_LUN,
1964af14d11SChristoph Hellwig 	.dma_boundary		= PAGE_SIZE - 1,
197dbb2da55SBart Van Assche 	.cmd_size		= sizeof(struct scsi_pointer),
1981da177e4SLinus Torvalds };
1991da177e4SLinus Torvalds 
a2091_probe(struct zorro_dev * z,const struct zorro_device_id * ent)2006f039790SGreg Kroah-Hartman static int a2091_probe(struct zorro_dev *z, const struct zorro_device_id *ent)
2011da177e4SLinus Torvalds {
202c737e22cSGeert Uytterhoeven 	struct Scsi_Host *instance;
203c737e22cSGeert Uytterhoeven 	int error;
204c737e22cSGeert Uytterhoeven 	struct a2091_scsiregs *regs;
205c737e22cSGeert Uytterhoeven 	wd33c93_regs wdregs;
20665c2784aSGeert Uytterhoeven 	struct a2091_hostdata *hdata;
207c737e22cSGeert Uytterhoeven 
208479accbbSMichael Schmitz 	if (dma_set_mask_and_coherent(&z->dev, DMA_BIT_MASK(24))) {
209479accbbSMichael Schmitz 		dev_warn(&z->dev, "cannot use 24 bit DMA\n");
210479accbbSMichael Schmitz 		return -ENODEV;
211479accbbSMichael Schmitz 	}
212479accbbSMichael Schmitz 
213c737e22cSGeert Uytterhoeven 	if (!request_mem_region(z->resource.start, 256, "wd33c93"))
214c737e22cSGeert Uytterhoeven 		return -EBUSY;
215c737e22cSGeert Uytterhoeven 
216c737e22cSGeert Uytterhoeven 	instance = scsi_host_alloc(&a2091_scsi_template,
21765c2784aSGeert Uytterhoeven 				   sizeof(struct a2091_hostdata));
218c737e22cSGeert Uytterhoeven 	if (!instance) {
219c737e22cSGeert Uytterhoeven 		error = -ENOMEM;
220c737e22cSGeert Uytterhoeven 		goto fail_alloc;
221c737e22cSGeert Uytterhoeven 	}
222c737e22cSGeert Uytterhoeven 
223c737e22cSGeert Uytterhoeven 	instance->irq = IRQ_AMIGA_PORTS;
224c737e22cSGeert Uytterhoeven 	instance->unique_id = z->slotaddr;
225c737e22cSGeert Uytterhoeven 
2266112ea08SGeert Uytterhoeven 	regs = ZTWO_VADDR(z->resource.start);
227c737e22cSGeert Uytterhoeven 	regs->DAWR = DAWR_A2091;
228c737e22cSGeert Uytterhoeven 
229c737e22cSGeert Uytterhoeven 	wdregs.SASR = &regs->SASR;
230c737e22cSGeert Uytterhoeven 	wdregs.SCMD = &regs->SCMD;
231c737e22cSGeert Uytterhoeven 
232c737e22cSGeert Uytterhoeven 	hdata = shost_priv(instance);
233479accbbSMichael Schmitz 	hdata->dev = &z->dev;
23465c2784aSGeert Uytterhoeven 	hdata->wh.no_sync = 0xff;
23565c2784aSGeert Uytterhoeven 	hdata->wh.fast = 0;
23665c2784aSGeert Uytterhoeven 	hdata->wh.dma_mode = CTRL_DMA;
23765c2784aSGeert Uytterhoeven 	hdata->regs = regs;
238c737e22cSGeert Uytterhoeven 
239c737e22cSGeert Uytterhoeven 	wd33c93_init(instance, wdregs, dma_setup, dma_stop, WD33C93_FS_8_10);
240c737e22cSGeert Uytterhoeven 	error = request_irq(IRQ_AMIGA_PORTS, a2091_intr, IRQF_SHARED,
241c737e22cSGeert Uytterhoeven 			    "A2091 SCSI", instance);
242c737e22cSGeert Uytterhoeven 	if (error)
243c737e22cSGeert Uytterhoeven 		goto fail_irq;
244c737e22cSGeert Uytterhoeven 
245c737e22cSGeert Uytterhoeven 	regs->CNTR = CNTR_PDMD | CNTR_INTEN;
246c737e22cSGeert Uytterhoeven 
247c737e22cSGeert Uytterhoeven 	error = scsi_add_host(instance, NULL);
248c737e22cSGeert Uytterhoeven 	if (error)
249c737e22cSGeert Uytterhoeven 		goto fail_host;
250c737e22cSGeert Uytterhoeven 
251c737e22cSGeert Uytterhoeven 	zorro_set_drvdata(z, instance);
252c737e22cSGeert Uytterhoeven 
253c737e22cSGeert Uytterhoeven 	scsi_scan_host(instance);
254c737e22cSGeert Uytterhoeven 	return 0;
255c737e22cSGeert Uytterhoeven 
256c737e22cSGeert Uytterhoeven fail_host:
257c737e22cSGeert Uytterhoeven 	free_irq(IRQ_AMIGA_PORTS, instance);
258c737e22cSGeert Uytterhoeven fail_irq:
259c737e22cSGeert Uytterhoeven 	scsi_host_put(instance);
260c737e22cSGeert Uytterhoeven fail_alloc:
261c737e22cSGeert Uytterhoeven 	release_mem_region(z->resource.start, 256);
262c737e22cSGeert Uytterhoeven 	return error;
263c737e22cSGeert Uytterhoeven }
264c737e22cSGeert Uytterhoeven 
a2091_remove(struct zorro_dev * z)2656f039790SGreg Kroah-Hartman static void a2091_remove(struct zorro_dev *z)
266c737e22cSGeert Uytterhoeven {
267c737e22cSGeert Uytterhoeven 	struct Scsi_Host *instance = zorro_get_drvdata(z);
26865c2784aSGeert Uytterhoeven 	struct a2091_hostdata *hdata = shost_priv(instance);
269b1de6ab7SGeert Uytterhoeven 
27065c2784aSGeert Uytterhoeven 	hdata->regs->CNTR = 0;
271c737e22cSGeert Uytterhoeven 	scsi_remove_host(instance);
2721da177e4SLinus Torvalds 	free_irq(IRQ_AMIGA_PORTS, instance);
273c737e22cSGeert Uytterhoeven 	scsi_host_put(instance);
274c737e22cSGeert Uytterhoeven 	release_mem_region(z->resource.start, 256);
2751da177e4SLinus Torvalds }
2761da177e4SLinus Torvalds 
2776f039790SGreg Kroah-Hartman static struct zorro_device_id a2091_zorro_tbl[] = {
278c737e22cSGeert Uytterhoeven 	{ ZORRO_PROD_CBM_A590_A2091_1 },
279c737e22cSGeert Uytterhoeven 	{ ZORRO_PROD_CBM_A590_A2091_2 },
280c737e22cSGeert Uytterhoeven 	{ 0 }
281c737e22cSGeert Uytterhoeven };
282c737e22cSGeert Uytterhoeven MODULE_DEVICE_TABLE(zorro, a2091_zorro_tbl);
283c737e22cSGeert Uytterhoeven 
284c737e22cSGeert Uytterhoeven static struct zorro_driver a2091_driver = {
285c737e22cSGeert Uytterhoeven 	.name		= "a2091",
286c737e22cSGeert Uytterhoeven 	.id_table	= a2091_zorro_tbl,
287c737e22cSGeert Uytterhoeven 	.probe		= a2091_probe,
2886f039790SGreg Kroah-Hartman 	.remove		= a2091_remove,
289c737e22cSGeert Uytterhoeven };
290c737e22cSGeert Uytterhoeven 
a2091_init(void)291c737e22cSGeert Uytterhoeven static int __init a2091_init(void)
292c737e22cSGeert Uytterhoeven {
293c737e22cSGeert Uytterhoeven 	return zorro_register_driver(&a2091_driver);
294c737e22cSGeert Uytterhoeven }
295c737e22cSGeert Uytterhoeven module_init(a2091_init);
296c737e22cSGeert Uytterhoeven 
a2091_exit(void)297c737e22cSGeert Uytterhoeven static void __exit a2091_exit(void)
298c737e22cSGeert Uytterhoeven {
299c737e22cSGeert Uytterhoeven 	zorro_unregister_driver(&a2091_driver);
300c737e22cSGeert Uytterhoeven }
301c737e22cSGeert Uytterhoeven module_exit(a2091_exit);
302c737e22cSGeert Uytterhoeven 
303c737e22cSGeert Uytterhoeven MODULE_DESCRIPTION("Commodore A2091/A590 SCSI");
3041da177e4SLinus Torvalds MODULE_LICENSE("GPL");
305