162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/types.h>
362306a36Sopenharmony_ci#include <linux/init.h>
462306a36Sopenharmony_ci#include <linux/interrupt.h>
562306a36Sopenharmony_ci#include <linux/mm.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/spinlock.h>
862306a36Sopenharmony_ci#include <linux/zorro.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <asm/page.h>
1262306a36Sopenharmony_ci#include <asm/amigaints.h>
1362306a36Sopenharmony_ci#include <asm/amigahw.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <scsi/scsi.h>
1662306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
1762306a36Sopenharmony_ci#include <scsi/scsi_device.h>
1862306a36Sopenharmony_ci#include <scsi/scsi_eh.h>
1962306a36Sopenharmony_ci#include <scsi/scsi_tcq.h>
2062306a36Sopenharmony_ci#include "wd33c93.h"
2162306a36Sopenharmony_ci#include "a2091.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct a2091_hostdata {
2562306a36Sopenharmony_ci	struct WD33C93_hostdata wh;
2662306a36Sopenharmony_ci	struct a2091_scsiregs *regs;
2762306a36Sopenharmony_ci	struct device *dev;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define DMA_DIR(d)   ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic irqreturn_t a2091_intr(int irq, void *data)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct Scsi_Host *instance = data;
3562306a36Sopenharmony_ci	struct a2091_hostdata *hdata = shost_priv(instance);
3662306a36Sopenharmony_ci	unsigned int status = hdata->regs->ISTR;
3762306a36Sopenharmony_ci	unsigned long flags;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (!(status & (ISTR_INT_F | ISTR_INT_P)) || !(status & ISTR_INTS))
4062306a36Sopenharmony_ci		return IRQ_NONE;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	spin_lock_irqsave(instance->host_lock, flags);
4362306a36Sopenharmony_ci	wd33c93_intr(instance);
4462306a36Sopenharmony_ci	spin_unlock_irqrestore(instance->host_lock, flags);
4562306a36Sopenharmony_ci	return IRQ_HANDLED;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int dma_setup(struct scsi_cmnd *cmd, int dir_in)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);
5162306a36Sopenharmony_ci	unsigned long len = scsi_pointer->this_residual;
5262306a36Sopenharmony_ci	struct Scsi_Host *instance = cmd->device->host;
5362306a36Sopenharmony_ci	struct a2091_hostdata *hdata = shost_priv(instance);
5462306a36Sopenharmony_ci	struct WD33C93_hostdata *wh = &hdata->wh;
5562306a36Sopenharmony_ci	struct a2091_scsiregs *regs = hdata->regs;
5662306a36Sopenharmony_ci	unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
5762306a36Sopenharmony_ci	dma_addr_t addr;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	addr = dma_map_single(hdata->dev, scsi_pointer->ptr,
6062306a36Sopenharmony_ci			      len, DMA_DIR(dir_in));
6162306a36Sopenharmony_ci	if (dma_mapping_error(hdata->dev, addr)) {
6262306a36Sopenharmony_ci		dev_warn(hdata->dev, "cannot map SCSI data block %p\n",
6362306a36Sopenharmony_ci			 scsi_pointer->ptr);
6462306a36Sopenharmony_ci		return 1;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci	scsi_pointer->dma_handle = addr;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* don't allow DMA if the physical address is bad */
6962306a36Sopenharmony_ci	if (addr & A2091_XFER_MASK) {
7062306a36Sopenharmony_ci		/* drop useless mapping */
7162306a36Sopenharmony_ci		dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
7262306a36Sopenharmony_ci				 scsi_pointer->this_residual,
7362306a36Sopenharmony_ci				 DMA_DIR(dir_in));
7462306a36Sopenharmony_ci		scsi_pointer->dma_handle = (dma_addr_t) NULL;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci		wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff;
7762306a36Sopenharmony_ci		wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len,
7862306a36Sopenharmony_ci						GFP_KERNEL);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		/* can't allocate memory; use PIO */
8162306a36Sopenharmony_ci		if (!wh->dma_bounce_buffer) {
8262306a36Sopenharmony_ci			wh->dma_bounce_len = 0;
8362306a36Sopenharmony_ci			return 1;
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		if (!dir_in) {
8762306a36Sopenharmony_ci			/* copy to bounce buffer for a write */
8862306a36Sopenharmony_ci			memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr,
8962306a36Sopenharmony_ci			       scsi_pointer->this_residual);
9062306a36Sopenharmony_ci		}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		/* will flush/invalidate cache for us */
9362306a36Sopenharmony_ci		addr = dma_map_single(hdata->dev, wh->dma_bounce_buffer,
9462306a36Sopenharmony_ci				      wh->dma_bounce_len, DMA_DIR(dir_in));
9562306a36Sopenharmony_ci		/* can't map buffer; use PIO */
9662306a36Sopenharmony_ci		if (dma_mapping_error(hdata->dev, addr)) {
9762306a36Sopenharmony_ci			dev_warn(hdata->dev, "cannot map bounce buffer %p\n",
9862306a36Sopenharmony_ci				 wh->dma_bounce_buffer);
9962306a36Sopenharmony_ci			return 1;
10062306a36Sopenharmony_ci		}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		/* the bounce buffer may not be in the first 16M of physmem */
10362306a36Sopenharmony_ci		if (addr & A2091_XFER_MASK) {
10462306a36Sopenharmony_ci			/* we could use chipmem... maybe later */
10562306a36Sopenharmony_ci			kfree(wh->dma_bounce_buffer);
10662306a36Sopenharmony_ci			wh->dma_bounce_buffer = NULL;
10762306a36Sopenharmony_ci			wh->dma_bounce_len = 0;
10862306a36Sopenharmony_ci			return 1;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		scsi_pointer->dma_handle = addr;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* setup dma direction */
11562306a36Sopenharmony_ci	if (!dir_in)
11662306a36Sopenharmony_ci		cntr |= CNTR_DDIR;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* remember direction */
11962306a36Sopenharmony_ci	wh->dma_dir = dir_in;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	regs->CNTR = cntr;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* setup DMA *physical* address */
12462306a36Sopenharmony_ci	regs->ACR = addr;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* no more cache flush here - dma_map_single() takes care */
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* start DMA */
12962306a36Sopenharmony_ci	regs->ST_DMA = 1;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* return success */
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
13662306a36Sopenharmony_ci		     int status)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(SCpnt);
13962306a36Sopenharmony_ci	struct a2091_hostdata *hdata = shost_priv(instance);
14062306a36Sopenharmony_ci	struct WD33C93_hostdata *wh = &hdata->wh;
14162306a36Sopenharmony_ci	struct a2091_scsiregs *regs = hdata->regs;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* disable SCSI interrupts */
14462306a36Sopenharmony_ci	unsigned short cntr = CNTR_PDMD;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (!wh->dma_dir)
14762306a36Sopenharmony_ci		cntr |= CNTR_DDIR;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* disable SCSI interrupts */
15062306a36Sopenharmony_ci	regs->CNTR = cntr;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* flush if we were reading */
15362306a36Sopenharmony_ci	if (wh->dma_dir) {
15462306a36Sopenharmony_ci		regs->FLUSH = 1;
15562306a36Sopenharmony_ci		while (!(regs->ISTR & ISTR_FE_FLG))
15662306a36Sopenharmony_ci			;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* clear a possible interrupt */
16062306a36Sopenharmony_ci	regs->CINT = 1;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* stop DMA */
16362306a36Sopenharmony_ci	regs->SP_DMA = 1;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* restore the CONTROL bits (minus the direction flag) */
16662306a36Sopenharmony_ci	regs->CNTR = CNTR_PDMD | CNTR_INTEN;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
16962306a36Sopenharmony_ci			 scsi_pointer->this_residual,
17062306a36Sopenharmony_ci			 DMA_DIR(wh->dma_dir));
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* copy from a bounce buffer, if necessary */
17362306a36Sopenharmony_ci	if (status && wh->dma_bounce_buffer) {
17462306a36Sopenharmony_ci		if (wh->dma_dir)
17562306a36Sopenharmony_ci			memcpy(scsi_pointer->ptr, wh->dma_bounce_buffer,
17662306a36Sopenharmony_ci			       scsi_pointer->this_residual);
17762306a36Sopenharmony_ci		kfree(wh->dma_bounce_buffer);
17862306a36Sopenharmony_ci		wh->dma_bounce_buffer = NULL;
17962306a36Sopenharmony_ci		wh->dma_bounce_len = 0;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic const struct scsi_host_template a2091_scsi_template = {
18462306a36Sopenharmony_ci	.module			= THIS_MODULE,
18562306a36Sopenharmony_ci	.name			= "Commodore A2091/A590 SCSI",
18662306a36Sopenharmony_ci	.show_info		= wd33c93_show_info,
18762306a36Sopenharmony_ci	.write_info		= wd33c93_write_info,
18862306a36Sopenharmony_ci	.proc_name		= "A2901",
18962306a36Sopenharmony_ci	.queuecommand		= wd33c93_queuecommand,
19062306a36Sopenharmony_ci	.eh_abort_handler	= wd33c93_abort,
19162306a36Sopenharmony_ci	.eh_host_reset_handler	= wd33c93_host_reset,
19262306a36Sopenharmony_ci	.can_queue		= CAN_QUEUE,
19362306a36Sopenharmony_ci	.this_id		= 7,
19462306a36Sopenharmony_ci	.sg_tablesize		= SG_ALL,
19562306a36Sopenharmony_ci	.cmd_per_lun		= CMD_PER_LUN,
19662306a36Sopenharmony_ci	.dma_boundary		= PAGE_SIZE - 1,
19762306a36Sopenharmony_ci	.cmd_size		= sizeof(struct scsi_pointer),
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int a2091_probe(struct zorro_dev *z, const struct zorro_device_id *ent)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct Scsi_Host *instance;
20362306a36Sopenharmony_ci	int error;
20462306a36Sopenharmony_ci	struct a2091_scsiregs *regs;
20562306a36Sopenharmony_ci	wd33c93_regs wdregs;
20662306a36Sopenharmony_ci	struct a2091_hostdata *hdata;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (dma_set_mask_and_coherent(&z->dev, DMA_BIT_MASK(24))) {
20962306a36Sopenharmony_ci		dev_warn(&z->dev, "cannot use 24 bit DMA\n");
21062306a36Sopenharmony_ci		return -ENODEV;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (!request_mem_region(z->resource.start, 256, "wd33c93"))
21462306a36Sopenharmony_ci		return -EBUSY;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	instance = scsi_host_alloc(&a2091_scsi_template,
21762306a36Sopenharmony_ci				   sizeof(struct a2091_hostdata));
21862306a36Sopenharmony_ci	if (!instance) {
21962306a36Sopenharmony_ci		error = -ENOMEM;
22062306a36Sopenharmony_ci		goto fail_alloc;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	instance->irq = IRQ_AMIGA_PORTS;
22462306a36Sopenharmony_ci	instance->unique_id = z->slotaddr;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	regs = ZTWO_VADDR(z->resource.start);
22762306a36Sopenharmony_ci	regs->DAWR = DAWR_A2091;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	wdregs.SASR = &regs->SASR;
23062306a36Sopenharmony_ci	wdregs.SCMD = &regs->SCMD;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	hdata = shost_priv(instance);
23362306a36Sopenharmony_ci	hdata->dev = &z->dev;
23462306a36Sopenharmony_ci	hdata->wh.no_sync = 0xff;
23562306a36Sopenharmony_ci	hdata->wh.fast = 0;
23662306a36Sopenharmony_ci	hdata->wh.dma_mode = CTRL_DMA;
23762306a36Sopenharmony_ci	hdata->regs = regs;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	wd33c93_init(instance, wdregs, dma_setup, dma_stop, WD33C93_FS_8_10);
24062306a36Sopenharmony_ci	error = request_irq(IRQ_AMIGA_PORTS, a2091_intr, IRQF_SHARED,
24162306a36Sopenharmony_ci			    "A2091 SCSI", instance);
24262306a36Sopenharmony_ci	if (error)
24362306a36Sopenharmony_ci		goto fail_irq;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	regs->CNTR = CNTR_PDMD | CNTR_INTEN;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	error = scsi_add_host(instance, NULL);
24862306a36Sopenharmony_ci	if (error)
24962306a36Sopenharmony_ci		goto fail_host;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	zorro_set_drvdata(z, instance);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	scsi_scan_host(instance);
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cifail_host:
25762306a36Sopenharmony_ci	free_irq(IRQ_AMIGA_PORTS, instance);
25862306a36Sopenharmony_cifail_irq:
25962306a36Sopenharmony_ci	scsi_host_put(instance);
26062306a36Sopenharmony_cifail_alloc:
26162306a36Sopenharmony_ci	release_mem_region(z->resource.start, 256);
26262306a36Sopenharmony_ci	return error;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void a2091_remove(struct zorro_dev *z)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct Scsi_Host *instance = zorro_get_drvdata(z);
26862306a36Sopenharmony_ci	struct a2091_hostdata *hdata = shost_priv(instance);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	hdata->regs->CNTR = 0;
27162306a36Sopenharmony_ci	scsi_remove_host(instance);
27262306a36Sopenharmony_ci	free_irq(IRQ_AMIGA_PORTS, instance);
27362306a36Sopenharmony_ci	scsi_host_put(instance);
27462306a36Sopenharmony_ci	release_mem_region(z->resource.start, 256);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic struct zorro_device_id a2091_zorro_tbl[] = {
27862306a36Sopenharmony_ci	{ ZORRO_PROD_CBM_A590_A2091_1 },
27962306a36Sopenharmony_ci	{ ZORRO_PROD_CBM_A590_A2091_2 },
28062306a36Sopenharmony_ci	{ 0 }
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(zorro, a2091_zorro_tbl);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic struct zorro_driver a2091_driver = {
28562306a36Sopenharmony_ci	.name		= "a2091",
28662306a36Sopenharmony_ci	.id_table	= a2091_zorro_tbl,
28762306a36Sopenharmony_ci	.probe		= a2091_probe,
28862306a36Sopenharmony_ci	.remove		= a2091_remove,
28962306a36Sopenharmony_ci};
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int __init a2091_init(void)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	return zorro_register_driver(&a2091_driver);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_cimodule_init(a2091_init);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic void __exit a2091_exit(void)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	zorro_unregister_driver(&a2091_driver);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_cimodule_exit(a2091_exit);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ciMODULE_DESCRIPTION("Commodore A2091/A590 SCSI");
30462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
305