18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/drivers/acorn/scsi/powertec.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1997-2005 Russell King
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/string.h>
118c2ecf20Sopenharmony_ci#include <linux/ioport.h>
128c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
178c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/dma.h>
208c2ecf20Sopenharmony_ci#include <asm/ecard.h>
218c2ecf20Sopenharmony_ci#include <asm/io.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "../scsi.h"
248c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
258c2ecf20Sopenharmony_ci#include "fas216.h"
268c2ecf20Sopenharmony_ci#include "scsi.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <scsi/scsicam.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define POWERTEC_FAS216_OFFSET	0x3000
318c2ecf20Sopenharmony_ci#define POWERTEC_FAS216_SHIFT	6
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define POWERTEC_INTR_STATUS	0x2000
348c2ecf20Sopenharmony_ci#define POWERTEC_INTR_BIT	0x80
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define POWERTEC_RESET_CONTROL	0x1018
378c2ecf20Sopenharmony_ci#define POWERTEC_RESET_BIT	1
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define POWERTEC_TERM_CONTROL	0x2018
408c2ecf20Sopenharmony_ci#define POWERTEC_TERM_ENABLE	1
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define POWERTEC_INTR_CONTROL	0x101c
438c2ecf20Sopenharmony_ci#define POWERTEC_INTR_ENABLE	1
448c2ecf20Sopenharmony_ci#define POWERTEC_INTR_DISABLE	0
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define VERSION	"1.10 (19/01/2003 2.5.59)"
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Use term=0,1,0,0,0 to turn terminators on/off.
508c2ecf20Sopenharmony_ci * One entry per slot.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistatic int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define NR_SG	256
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistruct powertec_info {
578c2ecf20Sopenharmony_ci	FAS216_Info		info;
588c2ecf20Sopenharmony_ci	struct expansion_card	*ec;
598c2ecf20Sopenharmony_ci	void __iomem		*base;
608c2ecf20Sopenharmony_ci	unsigned int		term_ctl;
618c2ecf20Sopenharmony_ci	struct scatterlist	sg[NR_SG];
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* Prototype: void powertecscsi_irqenable(ec, irqnr)
658c2ecf20Sopenharmony_ci * Purpose  : Enable interrupts on Powertec SCSI card
668c2ecf20Sopenharmony_ci * Params   : ec    - expansion card structure
678c2ecf20Sopenharmony_ci *          : irqnr - interrupt number
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistatic void
708c2ecf20Sopenharmony_cipowertecscsi_irqenable(struct expansion_card *ec, int irqnr)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct powertec_info *info = ec->irq_data;
738c2ecf20Sopenharmony_ci	writeb(POWERTEC_INTR_ENABLE, info->base + POWERTEC_INTR_CONTROL);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* Prototype: void powertecscsi_irqdisable(ec, irqnr)
778c2ecf20Sopenharmony_ci * Purpose  : Disable interrupts on Powertec SCSI card
788c2ecf20Sopenharmony_ci * Params   : ec    - expansion card structure
798c2ecf20Sopenharmony_ci *          : irqnr - interrupt number
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_cistatic void
828c2ecf20Sopenharmony_cipowertecscsi_irqdisable(struct expansion_card *ec, int irqnr)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct powertec_info *info = ec->irq_data;
858c2ecf20Sopenharmony_ci	writeb(POWERTEC_INTR_DISABLE, info->base + POWERTEC_INTR_CONTROL);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic const expansioncard_ops_t powertecscsi_ops = {
898c2ecf20Sopenharmony_ci	.irqenable	= powertecscsi_irqenable,
908c2ecf20Sopenharmony_ci	.irqdisable	= powertecscsi_irqdisable,
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* Prototype: void powertecscsi_terminator_ctl(host, on_off)
948c2ecf20Sopenharmony_ci * Purpose  : Turn the Powertec SCSI terminators on or off
958c2ecf20Sopenharmony_ci * Params   : host   - card to turn on/off
968c2ecf20Sopenharmony_ci *          : on_off - !0 to turn on, 0 to turn off
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_cistatic void
998c2ecf20Sopenharmony_cipowertecscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct powertec_info *info = (struct powertec_info *)host->hostdata;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	info->term_ctl = on_off ? POWERTEC_TERM_ENABLE : 0;
1048c2ecf20Sopenharmony_ci	writeb(info->term_ctl, info->base + POWERTEC_TERM_CONTROL);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* Prototype: void powertecscsi_intr(irq, *dev_id, *regs)
1088c2ecf20Sopenharmony_ci * Purpose  : handle interrupts from Powertec SCSI card
1098c2ecf20Sopenharmony_ci * Params   : irq    - interrupt number
1108c2ecf20Sopenharmony_ci *	      dev_id - user-defined (Scsi_Host structure)
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_cistatic irqreturn_t powertecscsi_intr(int irq, void *dev_id)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct powertec_info *info = dev_id;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return fas216_intr(&info->info);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* Prototype: fasdmatype_t powertecscsi_dma_setup(host, SCpnt, direction, min_type)
1208c2ecf20Sopenharmony_ci * Purpose  : initialises DMA/PIO
1218c2ecf20Sopenharmony_ci * Params   : host      - host
1228c2ecf20Sopenharmony_ci *	      SCpnt     - command
1238c2ecf20Sopenharmony_ci *	      direction - DMA on to/off of card
1248c2ecf20Sopenharmony_ci *	      min_type  - minimum DMA support that we must have for this transfer
1258c2ecf20Sopenharmony_ci * Returns  : type of transfer to be performed
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_cistatic fasdmatype_t
1288c2ecf20Sopenharmony_cipowertecscsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp,
1298c2ecf20Sopenharmony_ci		       fasdmadir_t direction, fasdmatype_t min_type)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct powertec_info *info = (struct powertec_info *)host->hostdata;
1328c2ecf20Sopenharmony_ci	struct device *dev = scsi_get_device(host);
1338c2ecf20Sopenharmony_ci	int dmach = info->info.scsi.dma;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (info->info.ifcfg.capabilities & FASCAP_DMA &&
1368c2ecf20Sopenharmony_ci	    min_type == fasdma_real_all) {
1378c2ecf20Sopenharmony_ci		int bufs, map_dir, dma_dir;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		if (direction == DMA_OUT) {
1428c2ecf20Sopenharmony_ci			map_dir = DMA_TO_DEVICE;
1438c2ecf20Sopenharmony_ci			dma_dir = DMA_MODE_WRITE;
1448c2ecf20Sopenharmony_ci		} else {
1458c2ecf20Sopenharmony_ci			map_dir = DMA_FROM_DEVICE;
1468c2ecf20Sopenharmony_ci			dma_dir = DMA_MODE_READ;
1478c2ecf20Sopenharmony_ci		}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci		dma_map_sg(dev, info->sg, bufs, map_dir);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		disable_dma(dmach);
1528c2ecf20Sopenharmony_ci		set_dma_sg(dmach, info->sg, bufs);
1538c2ecf20Sopenharmony_ci		set_dma_mode(dmach, dma_dir);
1548c2ecf20Sopenharmony_ci		enable_dma(dmach);
1558c2ecf20Sopenharmony_ci		return fasdma_real_all;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/*
1598c2ecf20Sopenharmony_ci	 * If we're not doing DMA,
1608c2ecf20Sopenharmony_ci	 *  we'll do slow PIO
1618c2ecf20Sopenharmony_ci	 */
1628c2ecf20Sopenharmony_ci	return fasdma_pio;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/* Prototype: int powertecscsi_dma_stop(host, SCpnt)
1668c2ecf20Sopenharmony_ci * Purpose  : stops DMA/PIO
1678c2ecf20Sopenharmony_ci * Params   : host  - host
1688c2ecf20Sopenharmony_ci *	      SCpnt - command
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_cistatic void
1718c2ecf20Sopenharmony_cipowertecscsi_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct powertec_info *info = (struct powertec_info *)host->hostdata;
1748c2ecf20Sopenharmony_ci	if (info->info.scsi.dma != NO_DMA)
1758c2ecf20Sopenharmony_ci		disable_dma(info->info.scsi.dma);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* Prototype: const char *powertecscsi_info(struct Scsi_Host * host)
1798c2ecf20Sopenharmony_ci * Purpose  : returns a descriptive string about this interface,
1808c2ecf20Sopenharmony_ci * Params   : host - driver host structure to return info for.
1818c2ecf20Sopenharmony_ci * Returns  : pointer to a static buffer containing null terminated string.
1828c2ecf20Sopenharmony_ci */
1838c2ecf20Sopenharmony_ciconst char *powertecscsi_info(struct Scsi_Host *host)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct powertec_info *info = (struct powertec_info *)host->hostdata;
1868c2ecf20Sopenharmony_ci	static char string[150];
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	sprintf(string, "%s (%s) in slot %d v%s terminators o%s",
1898c2ecf20Sopenharmony_ci		host->hostt->name, info->info.scsi.type, info->ec->slot_no,
1908c2ecf20Sopenharmony_ci		VERSION, info->term_ctl ? "n" : "ff");
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return string;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
1968c2ecf20Sopenharmony_ci * Purpose  : Set a driver specific function
1978c2ecf20Sopenharmony_ci * Params   : host   - host to setup
1988c2ecf20Sopenharmony_ci *          : buffer - buffer containing string describing operation
1998c2ecf20Sopenharmony_ci *          : length - length of string
2008c2ecf20Sopenharmony_ci * Returns  : -EINVAL, or 0
2018c2ecf20Sopenharmony_ci */
2028c2ecf20Sopenharmony_cistatic int
2038c2ecf20Sopenharmony_cipowertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	int ret = length;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (length >= 12 && strncmp(buffer, "POWERTECSCSI", 12) == 0) {
2088c2ecf20Sopenharmony_ci		buffer += 12;
2098c2ecf20Sopenharmony_ci		length -= 12;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
2128c2ecf20Sopenharmony_ci			if (buffer[5] == '1')
2138c2ecf20Sopenharmony_ci				powertecscsi_terminator_ctl(host, 1);
2148c2ecf20Sopenharmony_ci			else if (buffer[5] == '0')
2158c2ecf20Sopenharmony_ci				powertecscsi_terminator_ctl(host, 0);
2168c2ecf20Sopenharmony_ci			else
2178c2ecf20Sopenharmony_ci				ret = -EINVAL;
2188c2ecf20Sopenharmony_ci		} else
2198c2ecf20Sopenharmony_ci			ret = -EINVAL;
2208c2ecf20Sopenharmony_ci	} else
2218c2ecf20Sopenharmony_ci		ret = -EINVAL;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return ret;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci/* Prototype: int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
2278c2ecf20Sopenharmony_ci *					int length, int host_no, int inout)
2288c2ecf20Sopenharmony_ci * Purpose  : Return information about the driver to a user process accessing
2298c2ecf20Sopenharmony_ci *	      the /proc filesystem.
2308c2ecf20Sopenharmony_ci * Params   : buffer  - a buffer to write information to
2318c2ecf20Sopenharmony_ci *	      start   - a pointer into this buffer set by this routine to the start
2328c2ecf20Sopenharmony_ci *		        of the required information.
2338c2ecf20Sopenharmony_ci *	      offset  - offset into information that we have read up to.
2348c2ecf20Sopenharmony_ci *	      length  - length of buffer
2358c2ecf20Sopenharmony_ci *	      inout   - 0 for reading, 1 for writing.
2368c2ecf20Sopenharmony_ci * Returns  : length of data written to buffer.
2378c2ecf20Sopenharmony_ci */
2388c2ecf20Sopenharmony_cistatic int powertecscsi_show_info(struct seq_file *m, struct Scsi_Host *host)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct powertec_info *info;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	info = (struct powertec_info *)host->hostdata;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	seq_printf(m, "PowerTec SCSI driver v%s\n", VERSION);
2458c2ecf20Sopenharmony_ci	fas216_print_host(&info->info, m);
2468c2ecf20Sopenharmony_ci	seq_printf(m, "Term    : o%s\n",
2478c2ecf20Sopenharmony_ci			info->term_ctl ? "n" : "ff");
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	fas216_print_stats(&info->info, m);
2508c2ecf20Sopenharmony_ci	fas216_print_devices(&info->info, m);
2518c2ecf20Sopenharmony_ci	return 0;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic ssize_t powertecscsi_show_term(struct device *dev, struct device_attribute *attr, char *buf)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct expansion_card *ec = ECARD_DEV(dev);
2578c2ecf20Sopenharmony_ci	struct Scsi_Host *host = ecard_get_drvdata(ec);
2588c2ecf20Sopenharmony_ci	struct powertec_info *info = (struct powertec_info *)host->hostdata;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", info->term_ctl ? 1 : 0);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic ssize_t
2648c2ecf20Sopenharmony_cipowertecscsi_store_term(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct expansion_card *ec = ECARD_DEV(dev);
2678c2ecf20Sopenharmony_ci	struct Scsi_Host *host = ecard_get_drvdata(ec);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (len > 1)
2708c2ecf20Sopenharmony_ci		powertecscsi_terminator_ctl(host, buf[0] != '0');
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return len;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR,
2768c2ecf20Sopenharmony_ci		   powertecscsi_show_term, powertecscsi_store_term);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic struct scsi_host_template powertecscsi_template = {
2798c2ecf20Sopenharmony_ci	.module				= THIS_MODULE,
2808c2ecf20Sopenharmony_ci	.show_info			= powertecscsi_show_info,
2818c2ecf20Sopenharmony_ci	.write_info			= powertecscsi_set_proc_info,
2828c2ecf20Sopenharmony_ci	.name				= "PowerTec SCSI",
2838c2ecf20Sopenharmony_ci	.info				= powertecscsi_info,
2848c2ecf20Sopenharmony_ci	.queuecommand			= fas216_queue_command,
2858c2ecf20Sopenharmony_ci	.eh_host_reset_handler		= fas216_eh_host_reset,
2868c2ecf20Sopenharmony_ci	.eh_bus_reset_handler		= fas216_eh_bus_reset,
2878c2ecf20Sopenharmony_ci	.eh_device_reset_handler	= fas216_eh_device_reset,
2888c2ecf20Sopenharmony_ci	.eh_abort_handler		= fas216_eh_abort,
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	.can_queue			= 8,
2918c2ecf20Sopenharmony_ci	.this_id			= 7,
2928c2ecf20Sopenharmony_ci	.sg_tablesize			= SG_MAX_SEGMENTS,
2938c2ecf20Sopenharmony_ci	.dma_boundary			= IOMD_DMA_BOUNDARY,
2948c2ecf20Sopenharmony_ci	.cmd_per_lun			= 2,
2958c2ecf20Sopenharmony_ci	.proc_name			= "powertec",
2968c2ecf20Sopenharmony_ci};
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int powertecscsi_probe(struct expansion_card *ec,
2998c2ecf20Sopenharmony_ci			      const struct ecard_id *id)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
3028c2ecf20Sopenharmony_ci	struct powertec_info *info;
3038c2ecf20Sopenharmony_ci	void __iomem *base;
3048c2ecf20Sopenharmony_ci	int ret;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	ret = ecard_request_resources(ec);
3078c2ecf20Sopenharmony_ci	if (ret)
3088c2ecf20Sopenharmony_ci		goto out;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);
3118c2ecf20Sopenharmony_ci	if (!base) {
3128c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3138c2ecf20Sopenharmony_ci		goto out_region;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	host = scsi_host_alloc(&powertecscsi_template,
3178c2ecf20Sopenharmony_ci			       sizeof (struct powertec_info));
3188c2ecf20Sopenharmony_ci	if (!host) {
3198c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3208c2ecf20Sopenharmony_ci		goto out_region;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	ecard_set_drvdata(ec, host);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	info = (struct powertec_info *)host->hostdata;
3268c2ecf20Sopenharmony_ci	info->base = base;
3278c2ecf20Sopenharmony_ci	powertecscsi_terminator_ctl(host, term[ec->slot_no]);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	info->ec = ec;
3308c2ecf20Sopenharmony_ci	info->info.scsi.io_base		= base + POWERTEC_FAS216_OFFSET;
3318c2ecf20Sopenharmony_ci	info->info.scsi.io_shift	= POWERTEC_FAS216_SHIFT;
3328c2ecf20Sopenharmony_ci	info->info.scsi.irq		= ec->irq;
3338c2ecf20Sopenharmony_ci	info->info.scsi.dma		= ec->dma;
3348c2ecf20Sopenharmony_ci	info->info.ifcfg.clockrate	= 40; /* MHz */
3358c2ecf20Sopenharmony_ci	info->info.ifcfg.select_timeout	= 255;
3368c2ecf20Sopenharmony_ci	info->info.ifcfg.asyncperiod	= 200; /* ns */
3378c2ecf20Sopenharmony_ci	info->info.ifcfg.sync_max_depth	= 7;
3388c2ecf20Sopenharmony_ci	info->info.ifcfg.cntl3		= CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
3398c2ecf20Sopenharmony_ci	info->info.ifcfg.disconnect_ok	= 1;
3408c2ecf20Sopenharmony_ci	info->info.ifcfg.wide_max_size	= 0;
3418c2ecf20Sopenharmony_ci	info->info.ifcfg.capabilities	= 0;
3428c2ecf20Sopenharmony_ci	info->info.dma.setup		= powertecscsi_dma_setup;
3438c2ecf20Sopenharmony_ci	info->info.dma.pseudo		= NULL;
3448c2ecf20Sopenharmony_ci	info->info.dma.stop		= powertecscsi_dma_stop;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	ec->irqaddr	= base + POWERTEC_INTR_STATUS;
3478c2ecf20Sopenharmony_ci	ec->irqmask	= POWERTEC_INTR_BIT;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	ecard_setirq(ec, &powertecscsi_ops, info);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	device_create_file(&ec->dev, &dev_attr_bus_term);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	ret = fas216_init(host);
3548c2ecf20Sopenharmony_ci	if (ret)
3558c2ecf20Sopenharmony_ci		goto out_free;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	ret = request_irq(ec->irq, powertecscsi_intr,
3588c2ecf20Sopenharmony_ci			  0, "powertec", info);
3598c2ecf20Sopenharmony_ci	if (ret) {
3608c2ecf20Sopenharmony_ci		printk("scsi%d: IRQ%d not free: %d\n",
3618c2ecf20Sopenharmony_ci		       host->host_no, ec->irq, ret);
3628c2ecf20Sopenharmony_ci		goto out_release;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (info->info.scsi.dma != NO_DMA) {
3668c2ecf20Sopenharmony_ci		if (request_dma(info->info.scsi.dma, "powertec")) {
3678c2ecf20Sopenharmony_ci			printk("scsi%d: DMA%d not free, using PIO\n",
3688c2ecf20Sopenharmony_ci			       host->host_no, info->info.scsi.dma);
3698c2ecf20Sopenharmony_ci			info->info.scsi.dma = NO_DMA;
3708c2ecf20Sopenharmony_ci		} else {
3718c2ecf20Sopenharmony_ci			set_dma_speed(info->info.scsi.dma, 180);
3728c2ecf20Sopenharmony_ci			info->info.ifcfg.capabilities |= FASCAP_DMA;
3738c2ecf20Sopenharmony_ci		}
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	ret = fas216_add(host, &ec->dev);
3778c2ecf20Sopenharmony_ci	if (ret == 0)
3788c2ecf20Sopenharmony_ci		goto out;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (info->info.scsi.dma != NO_DMA)
3818c2ecf20Sopenharmony_ci		free_dma(info->info.scsi.dma);
3828c2ecf20Sopenharmony_ci	free_irq(ec->irq, info);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci out_release:
3858c2ecf20Sopenharmony_ci	fas216_release(host);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci out_free:
3888c2ecf20Sopenharmony_ci	device_remove_file(&ec->dev, &dev_attr_bus_term);
3898c2ecf20Sopenharmony_ci	scsi_host_put(host);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci out_region:
3928c2ecf20Sopenharmony_ci	ecard_release_resources(ec);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci out:
3958c2ecf20Sopenharmony_ci	return ret;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic void powertecscsi_remove(struct expansion_card *ec)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	struct Scsi_Host *host = ecard_get_drvdata(ec);
4018c2ecf20Sopenharmony_ci	struct powertec_info *info = (struct powertec_info *)host->hostdata;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	ecard_set_drvdata(ec, NULL);
4048c2ecf20Sopenharmony_ci	fas216_remove(host);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	device_remove_file(&ec->dev, &dev_attr_bus_term);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (info->info.scsi.dma != NO_DMA)
4098c2ecf20Sopenharmony_ci		free_dma(info->info.scsi.dma);
4108c2ecf20Sopenharmony_ci	free_irq(ec->irq, info);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	fas216_release(host);
4138c2ecf20Sopenharmony_ci	scsi_host_put(host);
4148c2ecf20Sopenharmony_ci	ecard_release_resources(ec);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic const struct ecard_id powertecscsi_cids[] = {
4188c2ecf20Sopenharmony_ci	{ MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI },
4198c2ecf20Sopenharmony_ci	{ 0xffff, 0xffff },
4208c2ecf20Sopenharmony_ci};
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic struct ecard_driver powertecscsi_driver = {
4238c2ecf20Sopenharmony_ci	.probe		= powertecscsi_probe,
4248c2ecf20Sopenharmony_ci	.remove		= powertecscsi_remove,
4258c2ecf20Sopenharmony_ci	.id_table	= powertecscsi_cids,
4268c2ecf20Sopenharmony_ci	.drv = {
4278c2ecf20Sopenharmony_ci		.name		= "powertecscsi",
4288c2ecf20Sopenharmony_ci	},
4298c2ecf20Sopenharmony_ci};
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic int __init powertecscsi_init(void)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	return ecard_register_driver(&powertecscsi_driver);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic void __exit powertecscsi_exit(void)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	ecard_remove_driver(&powertecscsi_driver);
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cimodule_init(powertecscsi_init);
4428c2ecf20Sopenharmony_cimodule_exit(powertecscsi_exit);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King");
4458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Powertec SCSI driver");
4468c2ecf20Sopenharmony_cimodule_param_array(term, int, NULL, 0);
4478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(term, "SCSI bus termination");
4488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
449