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 = ®s->SASR;
230c737e22cSGeert Uytterhoeven wdregs.SCMD = ®s->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