18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Oak Generic NCR5380 driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 1995-2002, Russell King
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/ioport.h>
108c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <asm/ecard.h>
148c2ecf20Sopenharmony_ci#include <asm/io.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define priv(host)			((struct NCR5380_hostdata *)(host)->hostdata)
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define NCR5380_read(reg)           readb(hostdata->io + ((reg) << 2))
218c2ecf20Sopenharmony_ci#define NCR5380_write(reg, value)   writeb(value, hostdata->io + ((reg) << 2))
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define NCR5380_dma_xfer_len		NCR5380_dma_xfer_none
248c2ecf20Sopenharmony_ci#define NCR5380_dma_recv_setup		oakscsi_pread
258c2ecf20Sopenharmony_ci#define NCR5380_dma_send_setup		oakscsi_pwrite
268c2ecf20Sopenharmony_ci#define NCR5380_dma_residual		NCR5380_dma_residual_none
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define NCR5380_queue_command		oakscsi_queue_command
298c2ecf20Sopenharmony_ci#define NCR5380_info			oakscsi_info
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define NCR5380_implementation_fields	/* none */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "../NCR5380.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#undef START_DMA_INITIATOR_RECEIVE_REG
368c2ecf20Sopenharmony_ci#define START_DMA_INITIATOR_RECEIVE_REG	(128 + 7)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define STAT	((128 + 16) << 2)
398c2ecf20Sopenharmony_ci#define DATA	((128 + 8) << 2)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline int oakscsi_pwrite(struct NCR5380_hostdata *hostdata,
428c2ecf20Sopenharmony_ci                                 unsigned char *addr, int len)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci  u8 __iomem *base = hostdata->io;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ciprintk("writing %p len %d\n",addr, len);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci  while(1)
498c2ecf20Sopenharmony_ci  {
508c2ecf20Sopenharmony_ci    int status;
518c2ecf20Sopenharmony_ci    while (((status = readw(base + STAT)) & 0x100)==0);
528c2ecf20Sopenharmony_ci  }
538c2ecf20Sopenharmony_ci  return 0;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic inline int oakscsi_pread(struct NCR5380_hostdata *hostdata,
578c2ecf20Sopenharmony_ci                                unsigned char *addr, int len)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci  u8 __iomem *base = hostdata->io;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciprintk("reading %p len %d\n", addr, len);
628c2ecf20Sopenharmony_ci  while(len > 0)
638c2ecf20Sopenharmony_ci  {
648c2ecf20Sopenharmony_ci    unsigned int status, timeout;
658c2ecf20Sopenharmony_ci    unsigned long b;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci    timeout = 0x01FFFFFF;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci    while (((status = readw(base + STAT)) & 0x100)==0)
708c2ecf20Sopenharmony_ci    {
718c2ecf20Sopenharmony_ci      timeout--;
728c2ecf20Sopenharmony_ci      if(status & 0x200 || !timeout)
738c2ecf20Sopenharmony_ci      {
748c2ecf20Sopenharmony_ci        printk("status = %08X\n", status);
758c2ecf20Sopenharmony_ci        return -1;
768c2ecf20Sopenharmony_ci      }
778c2ecf20Sopenharmony_ci    }
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci    if(len >= 128)
808c2ecf20Sopenharmony_ci    {
818c2ecf20Sopenharmony_ci      readsw(base + DATA, addr, 128);
828c2ecf20Sopenharmony_ci      addr += 128;
838c2ecf20Sopenharmony_ci      len -= 128;
848c2ecf20Sopenharmony_ci    }
858c2ecf20Sopenharmony_ci    else
868c2ecf20Sopenharmony_ci    {
878c2ecf20Sopenharmony_ci      b = (unsigned long) readw(base + DATA);
888c2ecf20Sopenharmony_ci      *addr ++ = b;
898c2ecf20Sopenharmony_ci      len -= 1;
908c2ecf20Sopenharmony_ci      if(len)
918c2ecf20Sopenharmony_ci        *addr ++ = b>>8;
928c2ecf20Sopenharmony_ci      len -= 1;
938c2ecf20Sopenharmony_ci    }
948c2ecf20Sopenharmony_ci  }
958c2ecf20Sopenharmony_ci  return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#undef STAT
998c2ecf20Sopenharmony_ci#undef DATA
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#include "../NCR5380.c"
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic struct scsi_host_template oakscsi_template = {
1048c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
1058c2ecf20Sopenharmony_ci	.name			= "Oak 16-bit SCSI",
1068c2ecf20Sopenharmony_ci	.info			= oakscsi_info,
1078c2ecf20Sopenharmony_ci	.queuecommand		= oakscsi_queue_command,
1088c2ecf20Sopenharmony_ci	.eh_abort_handler	= NCR5380_abort,
1098c2ecf20Sopenharmony_ci	.eh_host_reset_handler	= NCR5380_host_reset,
1108c2ecf20Sopenharmony_ci	.can_queue		= 16,
1118c2ecf20Sopenharmony_ci	.this_id		= 7,
1128c2ecf20Sopenharmony_ci	.sg_tablesize		= SG_ALL,
1138c2ecf20Sopenharmony_ci	.cmd_per_lun		= 2,
1148c2ecf20Sopenharmony_ci	.dma_boundary		= PAGE_SIZE - 1,
1158c2ecf20Sopenharmony_ci	.proc_name		= "oakscsi",
1168c2ecf20Sopenharmony_ci	.cmd_size		= NCR5380_CMD_SIZE,
1178c2ecf20Sopenharmony_ci	.max_sectors		= 128,
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
1238c2ecf20Sopenharmony_ci	int ret;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	ret = ecard_request_resources(ec);
1268c2ecf20Sopenharmony_ci	if (ret)
1278c2ecf20Sopenharmony_ci		goto out;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	host = scsi_host_alloc(&oakscsi_template, sizeof(struct NCR5380_hostdata));
1308c2ecf20Sopenharmony_ci	if (!host) {
1318c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1328c2ecf20Sopenharmony_ci		goto release;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	priv(host)->io = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
1368c2ecf20Sopenharmony_ci	                         ecard_resource_len(ec, ECARD_RES_MEMC));
1378c2ecf20Sopenharmony_ci	if (!priv(host)->io) {
1388c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1398c2ecf20Sopenharmony_ci		goto unreg;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	host->irq = NO_IRQ;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	ret = NCR5380_init(host, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP);
1458c2ecf20Sopenharmony_ci	if (ret)
1468c2ecf20Sopenharmony_ci		goto out_unmap;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	NCR5380_maybe_reset_bus(host);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	ret = scsi_add_host(host, &ec->dev);
1518c2ecf20Sopenharmony_ci	if (ret)
1528c2ecf20Sopenharmony_ci		goto out_exit;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	scsi_scan_host(host);
1558c2ecf20Sopenharmony_ci	goto out;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci out_exit:
1588c2ecf20Sopenharmony_ci	NCR5380_exit(host);
1598c2ecf20Sopenharmony_ci out_unmap:
1608c2ecf20Sopenharmony_ci	iounmap(priv(host)->io);
1618c2ecf20Sopenharmony_ci unreg:
1628c2ecf20Sopenharmony_ci	scsi_host_put(host);
1638c2ecf20Sopenharmony_ci release:
1648c2ecf20Sopenharmony_ci	ecard_release_resources(ec);
1658c2ecf20Sopenharmony_ci out:
1668c2ecf20Sopenharmony_ci	return ret;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void oakscsi_remove(struct expansion_card *ec)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct Scsi_Host *host = ecard_get_drvdata(ec);
1728c2ecf20Sopenharmony_ci	void __iomem *base = priv(host)->io;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	ecard_set_drvdata(ec, NULL);
1758c2ecf20Sopenharmony_ci	scsi_remove_host(host);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	NCR5380_exit(host);
1788c2ecf20Sopenharmony_ci	scsi_host_put(host);
1798c2ecf20Sopenharmony_ci	iounmap(base);
1808c2ecf20Sopenharmony_ci	ecard_release_resources(ec);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic const struct ecard_id oakscsi_cids[] = {
1848c2ecf20Sopenharmony_ci	{ MANU_OAK, PROD_OAK_SCSI },
1858c2ecf20Sopenharmony_ci	{ 0xffff, 0xffff }
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic struct ecard_driver oakscsi_driver = {
1898c2ecf20Sopenharmony_ci	.probe		= oakscsi_probe,
1908c2ecf20Sopenharmony_ci	.remove		= oakscsi_remove,
1918c2ecf20Sopenharmony_ci	.id_table	= oakscsi_cids,
1928c2ecf20Sopenharmony_ci	.drv = {
1938c2ecf20Sopenharmony_ci		.name		= "oakscsi",
1948c2ecf20Sopenharmony_ci	},
1958c2ecf20Sopenharmony_ci};
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int __init oakscsi_init(void)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	return ecard_register_driver(&oakscsi_driver);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void __exit oakscsi_exit(void)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	ecard_remove_driver(&oakscsi_driver);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cimodule_init(oakscsi_init);
2088c2ecf20Sopenharmony_cimodule_exit(oakscsi_exit);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King");
2118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Oak SCSI driver");
2128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2138c2ecf20Sopenharmony_ci
214