brintos

brintos / linux-shallow public Read only

0
0
Text · 13.4 KiB · e460068 Raw
525 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  linux/drivers/acorn/scsi/cumana_2.c4 *5 *  Copyright (C) 1997-2005 Russell King6 *7 *  Changelog:8 *   30-08-1997	RMK	0.0.0	Created, READONLY version.9 *   22-01-1998	RMK	0.0.1	Updated to 2.1.80.10 *   15-04-1998	RMK	0.0.1	Only do PIO if FAS216 will allow it.11 *   02-05-1998	RMK	0.0.2	Updated & added DMA support.12 *   27-06-1998	RMK		Changed asm/delay.h to linux/delay.h13 *   18-08-1998	RMK	0.0.3	Fixed synchronous transfer depth.14 *   02-04-2000	RMK	0.0.4	Updated for new error handling code.15 */16#include <linux/module.h>17#include <linux/blkdev.h>18#include <linux/kernel.h>19#include <linux/string.h>20#include <linux/ioport.h>21#include <linux/proc_fs.h>22#include <linux/delay.h>23#include <linux/interrupt.h>24#include <linux/init.h>25#include <linux/dma-mapping.h>26#include <linux/pgtable.h>27 28#include <asm/dma.h>29#include <asm/ecard.h>30#include <asm/io.h>31 32#include <scsi/scsi.h>33#include <scsi/scsi_cmnd.h>34#include <scsi/scsi_device.h>35#include <scsi/scsi_eh.h>36#include <scsi/scsi_host.h>37#include <scsi/scsi_tcq.h>38#include "fas216.h"39#include "arm_scsi.h"40 41#include <scsi/scsicam.h>42 43#define CUMANASCSI2_STATUS		(0x0000)44#define STATUS_INT			(1 << 0)45#define STATUS_DRQ			(1 << 1)46#define STATUS_LATCHED			(1 << 3)47 48#define CUMANASCSI2_ALATCH		(0x0014)49#define ALATCH_ENA_INT			(3)50#define ALATCH_DIS_INT			(2)51#define ALATCH_ENA_TERM			(5)52#define ALATCH_DIS_TERM			(4)53#define ALATCH_ENA_BIT32		(11)54#define ALATCH_DIS_BIT32		(10)55#define ALATCH_ENA_DMA			(13)56#define ALATCH_DIS_DMA			(12)57#define ALATCH_DMA_OUT			(15)58#define ALATCH_DMA_IN			(14)59 60#define CUMANASCSI2_PSEUDODMA		(0x0200)61 62#define CUMANASCSI2_FAS216_OFFSET	(0x0300)63#define CUMANASCSI2_FAS216_SHIFT	264 65/*66 * Version67 */68#define VERSION "1.00 (13/11/2002 2.5.47)"69 70/*71 * Use term=0,1,0,0,0 to turn terminators on/off72 */73static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };74 75#define NR_SG	25676 77struct cumanascsi2_info {78	FAS216_Info		info;79	struct expansion_card	*ec;80	void __iomem		*base;81	unsigned int		terms;		/* Terminator state	*/82	struct scatterlist	sg[NR_SG];	/* Scatter DMA list	*/83};84 85#define CSTATUS_IRQ	(1 << 0)86#define CSTATUS_DRQ	(1 << 1)87 88/* Prototype: void cumanascsi_2_irqenable(ec, irqnr)89 * Purpose  : Enable interrupts on Cumana SCSI 2 card90 * Params   : ec    - expansion card structure91 *          : irqnr - interrupt number92 */93static void94cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr)95{96	struct cumanascsi2_info *info = ec->irq_data;97	writeb(ALATCH_ENA_INT, info->base + CUMANASCSI2_ALATCH);98}99 100/* Prototype: void cumanascsi_2_irqdisable(ec, irqnr)101 * Purpose  : Disable interrupts on Cumana SCSI 2 card102 * Params   : ec    - expansion card structure103 *          : irqnr - interrupt number104 */105static void106cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr)107{108	struct cumanascsi2_info *info = ec->irq_data;109	writeb(ALATCH_DIS_INT, info->base + CUMANASCSI2_ALATCH);110}111 112static const expansioncard_ops_t cumanascsi_2_ops = {113	.irqenable	= cumanascsi_2_irqenable,114	.irqdisable	= cumanascsi_2_irqdisable,115};116 117/* Prototype: void cumanascsi_2_terminator_ctl(host, on_off)118 * Purpose  : Turn the Cumana SCSI 2 terminators on or off119 * Params   : host   - card to turn on/off120 *          : on_off - !0 to turn on, 0 to turn off121 */122static void123cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off)124{125	struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;126 127	if (on_off) {128		info->terms = 1;129		writeb(ALATCH_ENA_TERM, info->base + CUMANASCSI2_ALATCH);130	} else {131		info->terms = 0;132		writeb(ALATCH_DIS_TERM, info->base + CUMANASCSI2_ALATCH);133	}134}135 136/* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs)137 * Purpose  : handle interrupts from Cumana SCSI 2 card138 * Params   : irq    - interrupt number139 *	      dev_id - user-defined (Scsi_Host structure)140 */141static irqreturn_t142cumanascsi_2_intr(int irq, void *dev_id)143{144	struct cumanascsi2_info *info = dev_id;145 146	return fas216_intr(&info->info);147}148 149/* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type)150 * Purpose  : initialises DMA/PIO151 * Params   : host      - host152 *	      SCpnt     - command153 *	      direction - DMA on to/off of card154 *	      min_type  - minimum DMA support that we must have for this transfer155 * Returns  : type of transfer to be performed156 */157static fasdmatype_t158cumanascsi_2_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp,159		       fasdmadir_t direction, fasdmatype_t min_type)160{161	struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;162	struct device *dev = scsi_get_device(host);163	int dmach = info->info.scsi.dma;164 165	writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH);166 167	if (dmach != NO_DMA &&168	    (min_type == fasdma_real_all || SCp->this_residual >= 512)) {169		int bufs, map_dir, dma_dir, alatch_dir;170 171		bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);172 173		if (direction == DMA_OUT) {174			map_dir = DMA_TO_DEVICE;175			dma_dir = DMA_MODE_WRITE;176			alatch_dir = ALATCH_DMA_OUT;177		} else {178			map_dir = DMA_FROM_DEVICE;179			dma_dir = DMA_MODE_READ;180			alatch_dir = ALATCH_DMA_IN;181		}182 183		dma_map_sg(dev, info->sg, bufs, map_dir);184 185		disable_dma(dmach);186		set_dma_sg(dmach, info->sg, bufs);187		writeb(alatch_dir, info->base + CUMANASCSI2_ALATCH);188		set_dma_mode(dmach, dma_dir);189		enable_dma(dmach);190		writeb(ALATCH_ENA_DMA, info->base + CUMANASCSI2_ALATCH);191		writeb(ALATCH_DIS_BIT32, info->base + CUMANASCSI2_ALATCH);192		return fasdma_real_all;193	}194 195	/*196	 * If we're not doing DMA,197	 *  we'll do pseudo DMA198	 */199	return fasdma_pio;200}201 202/*203 * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer)204 * Purpose  : handles pseudo DMA205 * Params   : host      - host206 *	      SCpnt     - command207 *	      direction - DMA on to/off of card208 *	      transfer  - minimum number of bytes we expect to transfer209 */210static void211cumanascsi_2_dma_pseudo(struct Scsi_Host *host, struct scsi_pointer *SCp,212			fasdmadir_t direction, int transfer)213{214	struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;215	unsigned int length;216	unsigned char *addr;217 218	length = SCp->this_residual;219	addr = SCp->ptr;220 221	if (direction == DMA_OUT)222#if 0223		while (length > 1) {224			unsigned long word;225			unsigned int status = readb(info->base + CUMANASCSI2_STATUS);226 227			if (status & STATUS_INT)228				goto end;229 230			if (!(status & STATUS_DRQ))231				continue;232 233			word = *addr | *(addr + 1) << 8;234			writew(word, info->base + CUMANASCSI2_PSEUDODMA);235			addr += 2;236			length -= 2;237		}238#else239		printk ("PSEUDO_OUT???\n");240#endif241	else {242		if (transfer && (transfer & 255)) {243			while (length >= 256) {244				unsigned int status = readb(info->base + CUMANASCSI2_STATUS);245 246				if (status & STATUS_INT)247					return;248	    249				if (!(status & STATUS_DRQ))250					continue;251 252				readsw(info->base + CUMANASCSI2_PSEUDODMA,253				       addr, 256 >> 1);254				addr += 256;255				length -= 256;256			}257		}258 259		while (length > 0) {260			unsigned long word;261			unsigned int status = readb(info->base + CUMANASCSI2_STATUS);262 263			if (status & STATUS_INT)264				return;265 266			if (!(status & STATUS_DRQ))267				continue;268 269			word = readw(info->base + CUMANASCSI2_PSEUDODMA);270			*addr++ = word;271			if (--length > 0) {272				*addr++ = word >> 8;273				length --;274			}275		}276	}277}278 279/* Prototype: int cumanascsi_2_dma_stop(host, SCpnt)280 * Purpose  : stops DMA/PIO281 * Params   : host  - host282 *	      SCpnt - command283 */284static void285cumanascsi_2_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp)286{287	struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;288	if (info->info.scsi.dma != NO_DMA) {289		writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH);290		disable_dma(info->info.scsi.dma);291	}292}293 294/* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host)295 * Purpose  : returns a descriptive string about this interface,296 * Params   : host - driver host structure to return info for.297 * Returns  : pointer to a static buffer containing null terminated string.298 */299static const char *cumanascsi_2_info(struct Scsi_Host *host)300{301	struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;302	static char string[150];303 304	sprintf(string, "%s (%s) in slot %d v%s terminators o%s",305		host->hostt->name, info->info.scsi.type, info->ec->slot_no,306		VERSION, info->terms ? "n" : "ff");307 308	return string;309}310 311/* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)312 * Purpose  : Set a driver specific function313 * Params   : host   - host to setup314 *          : buffer - buffer containing string describing operation315 *          : length - length of string316 * Returns  : -EINVAL, or 0317 */318static int319cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)320{321	int ret = length;322 323	if (length >= 11 && strncmp(buffer, "CUMANASCSI2", 11) == 0) {324		buffer += 11;325		length -= 11;326 327		if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {328			if (buffer[5] == '1')329				cumanascsi_2_terminator_ctl(host, 1);330			else if (buffer[5] == '0')331				cumanascsi_2_terminator_ctl(host, 0);332			else333				ret = -EINVAL;334		} else {335			ret = -EINVAL;336		}337	} else {338		ret = -EINVAL;339	}340 341	return ret;342}343 344static int cumanascsi_2_show_info(struct seq_file *m, struct Scsi_Host *host)345{346	struct cumanascsi2_info *info;347	info = (struct cumanascsi2_info *)host->hostdata;348 349	seq_printf(m, "Cumana SCSI II driver v%s\n", VERSION);350	fas216_print_host(&info->info, m);351	seq_printf(m, "Term    : o%s\n",352			info->terms ? "n" : "ff");353 354	fas216_print_stats(&info->info, m);355	fas216_print_devices(&info->info, m);356	return 0;357}358 359static const struct scsi_host_template cumanascsi2_template = {360	.module				= THIS_MODULE,361	.show_info			= cumanascsi_2_show_info,362	.write_info			= cumanascsi_2_set_proc_info,363	.name				= "Cumana SCSI II",364	.info				= cumanascsi_2_info,365	.queuecommand			= fas216_queue_command,366	.eh_host_reset_handler		= fas216_eh_host_reset,367	.eh_bus_reset_handler		= fas216_eh_bus_reset,368	.eh_device_reset_handler	= fas216_eh_device_reset,369	.eh_abort_handler		= fas216_eh_abort,370	.cmd_size			= sizeof(struct fas216_cmd_priv),371	.can_queue			= 1,372	.this_id			= 7,373	.sg_tablesize			= SG_MAX_SEGMENTS,374	.dma_boundary			= IOMD_DMA_BOUNDARY,375	.proc_name			= "cumanascsi2",376};377 378static int cumanascsi2_probe(struct expansion_card *ec,379			     const struct ecard_id *id)380{381	struct Scsi_Host *host;382	struct cumanascsi2_info *info;383	void __iomem *base;384	int ret;385 386	ret = ecard_request_resources(ec);387	if (ret)388		goto out;389 390	base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);391	if (!base) {392		ret = -ENOMEM;393		goto out_region;394	}395 396	host = scsi_host_alloc(&cumanascsi2_template,397			       sizeof(struct cumanascsi2_info));398	if (!host) {399		ret = -ENOMEM;400		goto out_region;401	}402 403	ecard_set_drvdata(ec, host);404 405	info = (struct cumanascsi2_info *)host->hostdata;406	info->ec	= ec;407	info->base	= base;408 409	cumanascsi_2_terminator_ctl(host, term[ec->slot_no]);410 411	info->info.scsi.io_base		= base + CUMANASCSI2_FAS216_OFFSET;412	info->info.scsi.io_shift	= CUMANASCSI2_FAS216_SHIFT;413	info->info.scsi.irq		= ec->irq;414	info->info.scsi.dma		= ec->dma;415	info->info.ifcfg.clockrate	= 40; /* MHz */416	info->info.ifcfg.select_timeout	= 255;417	info->info.ifcfg.asyncperiod	= 200; /* ns */418	info->info.ifcfg.sync_max_depth	= 7;419	info->info.ifcfg.cntl3		= CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;420	info->info.ifcfg.disconnect_ok	= 1;421	info->info.ifcfg.wide_max_size	= 0;422	info->info.ifcfg.capabilities	= FASCAP_PSEUDODMA;423	info->info.dma.setup		= cumanascsi_2_dma_setup;424	info->info.dma.pseudo		= cumanascsi_2_dma_pseudo;425	info->info.dma.stop		= cumanascsi_2_dma_stop;426 427	ec->irqaddr	= info->base + CUMANASCSI2_STATUS;428	ec->irqmask	= STATUS_INT;429 430	ecard_setirq(ec, &cumanascsi_2_ops, info);431 432	ret = fas216_init(host);433	if (ret)434		goto out_free;435 436	ret = request_irq(ec->irq, cumanascsi_2_intr,437			  0, "cumanascsi2", info);438	if (ret) {439		printk("scsi%d: IRQ%d not free: %d\n",440		       host->host_no, ec->irq, ret);441		goto out_release;442	}443 444	if (info->info.scsi.dma != NO_DMA) {445		if (request_dma(info->info.scsi.dma, "cumanascsi2")) {446			printk("scsi%d: DMA%d not free, using PIO\n",447			       host->host_no, info->info.scsi.dma);448			info->info.scsi.dma = NO_DMA;449		} else {450			set_dma_speed(info->info.scsi.dma, 180);451			info->info.ifcfg.capabilities |= FASCAP_DMA;452		}453	}454 455	ret = fas216_add(host, &ec->dev);456	if (ret == 0)457		goto out;458 459	if (info->info.scsi.dma != NO_DMA)460		free_dma(info->info.scsi.dma);461	free_irq(ec->irq, info);462 463 out_release:464	fas216_release(host);465 466 out_free:467	scsi_host_put(host);468 469 out_region:470	ecard_release_resources(ec);471 472 out:473	return ret;474}475 476static void cumanascsi2_remove(struct expansion_card *ec)477{478	struct Scsi_Host *host = ecard_get_drvdata(ec);479	struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;480 481	ecard_set_drvdata(ec, NULL);482	fas216_remove(host);483 484	if (info->info.scsi.dma != NO_DMA)485		free_dma(info->info.scsi.dma);486	free_irq(ec->irq, info);487 488	fas216_release(host);489	scsi_host_put(host);490	ecard_release_resources(ec);491}492 493static const struct ecard_id cumanascsi2_cids[] = {494	{ MANU_CUMANA, PROD_CUMANA_SCSI_2 },495	{ 0xffff, 0xffff },496};497 498static struct ecard_driver cumanascsi2_driver = {499	.probe		= cumanascsi2_probe,500	.remove		= cumanascsi2_remove,501	.id_table	= cumanascsi2_cids,502	.drv = {503		.name		= "cumanascsi2",504	},505};506 507static int __init cumanascsi2_init(void)508{509	return ecard_register_driver(&cumanascsi2_driver);510}511 512static void __exit cumanascsi2_exit(void)513{514	ecard_remove_driver(&cumanascsi2_driver);515}516 517module_init(cumanascsi2_init);518module_exit(cumanascsi2_exit);519 520MODULE_AUTHOR("Russell King");521MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines");522module_param_array(term, int, NULL, 0);523MODULE_PARM_DESC(term, "SCSI bus termination");524MODULE_LICENSE("GPL");525