18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/types.h>
38c2ecf20Sopenharmony_ci#include <linux/mm.h>
48c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
58c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
68c2ecf20Sopenharmony_ci#include <linux/init.h>
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/page.h>
118c2ecf20Sopenharmony_ci#include <asm/mvme147hw.h>
128c2ecf20Sopenharmony_ci#include <asm/irq.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "scsi.h"
158c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
168c2ecf20Sopenharmony_ci#include "wd33c93.h"
178c2ecf20Sopenharmony_ci#include "mvme147.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic irqreturn_t mvme147_intr(int irq, void *data)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct Scsi_Host *instance = data;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	if (irq == MVME147_IRQ_SCSI_PORT)
248c2ecf20Sopenharmony_ci		wd33c93_intr(instance);
258c2ecf20Sopenharmony_ci	else
268c2ecf20Sopenharmony_ci		m147_pcc->dma_intr = 0x89;	/* Ack and enable ints */
278c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int dma_setup(struct scsi_cmnd *cmd, int dir_in)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct Scsi_Host *instance = cmd->device->host;
338c2ecf20Sopenharmony_ci	struct WD33C93_hostdata *hdata = shost_priv(instance);
348c2ecf20Sopenharmony_ci	unsigned char flags = 0x01;
358c2ecf20Sopenharmony_ci	unsigned long addr = virt_to_bus(cmd->SCp.ptr);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/* setup dma direction */
388c2ecf20Sopenharmony_ci	if (!dir_in)
398c2ecf20Sopenharmony_ci		flags |= 0x04;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* remember direction */
428c2ecf20Sopenharmony_ci	hdata->dma_dir = dir_in;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (dir_in) {
458c2ecf20Sopenharmony_ci		/* invalidate any cache */
468c2ecf20Sopenharmony_ci		cache_clear(addr, cmd->SCp.this_residual);
478c2ecf20Sopenharmony_ci	} else {
488c2ecf20Sopenharmony_ci		/* push any dirty cache */
498c2ecf20Sopenharmony_ci		cache_push(addr, cmd->SCp.this_residual);
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* start DMA */
538c2ecf20Sopenharmony_ci	m147_pcc->dma_bcr = cmd->SCp.this_residual | (1 << 24);
548c2ecf20Sopenharmony_ci	m147_pcc->dma_dadr = addr;
558c2ecf20Sopenharmony_ci	m147_pcc->dma_cntrl = flags;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* return success */
588c2ecf20Sopenharmony_ci	return 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
628c2ecf20Sopenharmony_ci		     int status)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	m147_pcc->dma_cntrl = 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct scsi_host_template mvme147_host_template = {
688c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
698c2ecf20Sopenharmony_ci	.proc_name		= "MVME147",
708c2ecf20Sopenharmony_ci	.name			= "MVME147 built-in SCSI",
718c2ecf20Sopenharmony_ci	.queuecommand		= wd33c93_queuecommand,
728c2ecf20Sopenharmony_ci	.eh_abort_handler	= wd33c93_abort,
738c2ecf20Sopenharmony_ci	.eh_host_reset_handler	= wd33c93_host_reset,
748c2ecf20Sopenharmony_ci	.show_info		= wd33c93_show_info,
758c2ecf20Sopenharmony_ci	.write_info		= wd33c93_write_info,
768c2ecf20Sopenharmony_ci	.can_queue		= CAN_QUEUE,
778c2ecf20Sopenharmony_ci	.this_id		= 7,
788c2ecf20Sopenharmony_ci	.sg_tablesize		= SG_ALL,
798c2ecf20Sopenharmony_ci	.cmd_per_lun		= CMD_PER_LUN,
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic struct Scsi_Host *mvme147_shost;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int __init mvme147_init(void)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	wd33c93_regs regs;
878c2ecf20Sopenharmony_ci	struct WD33C93_hostdata *hdata;
888c2ecf20Sopenharmony_ci	int error = -ENOMEM;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (!MACH_IS_MVME147)
918c2ecf20Sopenharmony_ci		return 0;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	mvme147_shost = scsi_host_alloc(&mvme147_host_template,
948c2ecf20Sopenharmony_ci			sizeof(struct WD33C93_hostdata));
958c2ecf20Sopenharmony_ci	if (!mvme147_shost)
968c2ecf20Sopenharmony_ci		goto err_out;
978c2ecf20Sopenharmony_ci	mvme147_shost->base = 0xfffe4000;
988c2ecf20Sopenharmony_ci	mvme147_shost->irq = MVME147_IRQ_SCSI_PORT;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	regs.SASR = (volatile unsigned char *)0xfffe4000;
1018c2ecf20Sopenharmony_ci	regs.SCMD = (volatile unsigned char *)0xfffe4001;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	hdata = shost_priv(mvme147_shost);
1048c2ecf20Sopenharmony_ci	hdata->no_sync = 0xff;
1058c2ecf20Sopenharmony_ci	hdata->fast = 0;
1068c2ecf20Sopenharmony_ci	hdata->dma_mode = CTRL_DMA;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	wd33c93_init(mvme147_shost, regs, dma_setup, dma_stop, WD33C93_FS_8_10);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	error = request_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr, 0,
1118c2ecf20Sopenharmony_ci			"MVME147 SCSI PORT", mvme147_shost);
1128c2ecf20Sopenharmony_ci	if (error)
1138c2ecf20Sopenharmony_ci		goto err_unregister;
1148c2ecf20Sopenharmony_ci	error = request_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr, 0,
1158c2ecf20Sopenharmony_ci			"MVME147 SCSI DMA", mvme147_shost);
1168c2ecf20Sopenharmony_ci	if (error)
1178c2ecf20Sopenharmony_ci		goto err_free_irq;
1188c2ecf20Sopenharmony_ci#if 0	/* Disabled; causes problems booting */
1198c2ecf20Sopenharmony_ci	m147_pcc->scsi_interrupt = 0x10;	/* Assert SCSI bus reset */
1208c2ecf20Sopenharmony_ci	udelay(100);
1218c2ecf20Sopenharmony_ci	m147_pcc->scsi_interrupt = 0x00;	/* Negate SCSI bus reset */
1228c2ecf20Sopenharmony_ci	udelay(2000);
1238c2ecf20Sopenharmony_ci	m147_pcc->scsi_interrupt = 0x40;	/* Clear bus reset interrupt */
1248c2ecf20Sopenharmony_ci#endif
1258c2ecf20Sopenharmony_ci	m147_pcc->scsi_interrupt = 0x09;	/* Enable interrupt */
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	m147_pcc->dma_cntrl = 0x00;	/* ensure DMA is stopped */
1288c2ecf20Sopenharmony_ci	m147_pcc->dma_intr = 0x89;	/* Ack and enable ints */
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	error = scsi_add_host(mvme147_shost, NULL);
1318c2ecf20Sopenharmony_ci	if (error)
1328c2ecf20Sopenharmony_ci		goto err_free_irq;
1338c2ecf20Sopenharmony_ci	scsi_scan_host(mvme147_shost);
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cierr_free_irq:
1378c2ecf20Sopenharmony_ci	free_irq(MVME147_IRQ_SCSI_PORT, mvme147_shost);
1388c2ecf20Sopenharmony_cierr_unregister:
1398c2ecf20Sopenharmony_ci	scsi_host_put(mvme147_shost);
1408c2ecf20Sopenharmony_cierr_out:
1418c2ecf20Sopenharmony_ci	return error;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic void __exit mvme147_exit(void)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	scsi_remove_host(mvme147_shost);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* XXX Make sure DMA is stopped! */
1498c2ecf20Sopenharmony_ci	free_irq(MVME147_IRQ_SCSI_PORT, mvme147_shost);
1508c2ecf20Sopenharmony_ci	free_irq(MVME147_IRQ_SCSI_DMA, mvme147_shost);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	scsi_host_put(mvme147_shost);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cimodule_init(mvme147_init);
1568c2ecf20Sopenharmony_cimodule_exit(mvme147_exit);
157