brintos

brintos / linux-shallow public Read only

0
0
Text · 9.0 KiB · 925d0bd Raw
364 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/scsi/arm/arxescsi.c4 *5 * Copyright (C) 1997-2000 Russell King, Stefan Hanske6 *7 * This driver is based on experimentation.  Hence, it may have made8 * assumptions about the particular card that I have available, and9 * may not be reliable!10 *11 * Changelog:12 *  30-08-1997	RMK	0.0.0	Created, READONLY version as cumana_2.c13 *  22-01-1998	RMK	0.0.1	Updated to 2.1.8014 *  15-04-1998	RMK	0.0.1	Only do PIO if FAS216 will allow it.15 *  11-06-1998 	SH	0.0.2   Changed to support ARXE 16-bit SCSI card16 *				enabled writing17 *  01-01-2000	SH	0.1.0   Added *real* pseudo dma writing18 *				(arxescsi_pseudo_dma_write)19 *  02-04-2000	RMK	0.1.1	Updated for new error handling code.20 *  22-10-2000  SH		Updated for new registering scheme.21 */22#include <linux/module.h>23#include <linux/blkdev.h>24#include <linux/kernel.h>25#include <linux/string.h>26#include <linux/ioport.h>27#include <linux/proc_fs.h>28#include <linux/unistd.h>29#include <linux/stat.h>30#include <linux/delay.h>31#include <linux/init.h>32#include <linux/interrupt.h>33 34#include <asm/dma.h>35#include <asm/io.h>36#include <asm/ecard.h>37 38#include <scsi/scsi.h>39#include <scsi/scsi_cmnd.h>40#include <scsi/scsi_device.h>41#include <scsi/scsi_eh.h>42#include <scsi/scsi_host.h>43#include <scsi/scsi_tcq.h>44#include "fas216.h"45 46struct arxescsi_info {47	FAS216_Info		info;48	struct expansion_card	*ec;49	void __iomem		*base;50};51 52#define DMADATA_OFFSET	(0x200)53 54#define DMASTAT_OFFSET	(0x600)55#define DMASTAT_DRQ	(1 << 0)56 57#define CSTATUS_IRQ	(1 << 0)58 59#define VERSION "1.10 (23/01/2003 2.5.57)"60 61/*62 * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type)63 * Purpose : initialises DMA/PIO64 * Params  : host      - host65 *	     SCpnt     - command66 *	     direction - DMA on to/off of card67 *	     min_type  - minimum DMA support that we must have for this transfer68 * Returns : 0 if we should not set CMD_WITHDMA for transfer info command69 */70static fasdmatype_t71arxescsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp,72		       fasdmadir_t direction, fasdmatype_t min_type)73{74	/*75	 * We don't do real DMA76	 */77	return fasdma_pseudo;78}79 80static void arxescsi_pseudo_dma_write(unsigned char *addr, void __iomem *base)81{82       __asm__ __volatile__(83       "               stmdb   sp!, {r0-r12}\n"84       "               mov     r3, %0\n"85       "               mov     r1, %1\n"86       "               add     r2, r1, #512\n"87       "               mov     r4, #256\n"88       ".loop_1:       ldmia   r3!, {r6, r8, r10, r12}\n"89       "               mov     r5, r6, lsl #16\n"90       "               mov     r7, r8, lsl #16\n"91       ".loop_2:       ldrb    r0, [r1, #1536]\n"92       "               tst     r0, #1\n"93       "               beq     .loop_2\n"94       "               stmia   r2, {r5-r8}\n\t"95       "               mov     r9, r10, lsl #16\n"96       "               mov     r11, r12, lsl #16\n"97       ".loop_3:       ldrb    r0, [r1, #1536]\n"98       "               tst     r0, #1\n"99       "               beq     .loop_3\n"100       "               stmia   r2, {r9-r12}\n"101       "               subs    r4, r4, #16\n"102       "               bne     .loop_1\n"103       "               ldmia   sp!, {r0-r12}\n"104       :105       : "r" (addr), "r" (base));106}107 108/*109 * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer)110 * Purpose : handles pseudo DMA111 * Params  : host      - host112 *	     SCpnt     - command113 *	     direction - DMA on to/off of card114 *	     transfer  - minimum number of bytes we expect to transfer115 */116static void117arxescsi_dma_pseudo(struct Scsi_Host *host, struct scsi_pointer *SCp,118		    fasdmadir_t direction, int transfer)119{120	struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;121	unsigned int length, error = 0;122	void __iomem *base = info->info.scsi.io_base;123	unsigned char *addr;124 125	length = SCp->this_residual;126	addr = SCp->ptr;127 128	if (direction == DMA_OUT) {129		unsigned int word;130		while (length > 256) {131			if (readb(base + 0x80) & STAT_INT) {132				error = 1;133				break;134			}135			arxescsi_pseudo_dma_write(addr, base);136			addr += 256;137			length -= 256;138		}139 140		if (!error)141			while (length > 0) {142				if (readb(base + 0x80) & STAT_INT)143					break;144	 145				if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))146					continue;147 148				word = *addr | *(addr + 1) << 8;149 150				writew(word, base + DMADATA_OFFSET);151				if (length > 1) {152					addr += 2;153					length -= 2;154				} else {155					addr += 1;156					length -= 1;157				}158			}159	}160	else {161		if (transfer && (transfer & 255)) {162			while (length >= 256) {163				if (readb(base + 0x80) & STAT_INT) {164					error = 1;165					break;166				}167	    168				if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))169					continue;170 171				readsw(base + DMADATA_OFFSET, addr, 256 >> 1);172				addr += 256;173				length -= 256;174			}175		}176 177		if (!(error))178			while (length > 0) {179				unsigned long word;180 181				if (readb(base + 0x80) & STAT_INT)182					break;183 184				if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))185					continue;186 187				word = readw(base + DMADATA_OFFSET);188				*addr++ = word;189				if (--length > 0) {190					*addr++ = word >> 8;191					length --;192				}193			}194	}195}196 197/*198 * Function: int arxescsi_dma_stop(host, SCpnt)199 * Purpose : stops DMA/PIO200 * Params  : host  - host201 *	     SCpnt - command202 */203static void arxescsi_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp)204{205	/*206	 * no DMA to stop207	 */208}209 210/*211 * Function: const char *arxescsi_info(struct Scsi_Host * host)212 * Purpose : returns a descriptive string about this interface,213 * Params  : host - driver host structure to return info for.214 * Returns : pointer to a static buffer containing null terminated string.215 */216static const char *arxescsi_info(struct Scsi_Host *host)217{218	struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;219	static char string[150];220 221	sprintf(string, "%s (%s) in slot %d v%s",222		host->hostt->name, info->info.scsi.type, info->ec->slot_no,223		VERSION);224 225	return string;226}227 228static int229arxescsi_show_info(struct seq_file *m, struct Scsi_Host *host)230{231	struct arxescsi_info *info;232	info = (struct arxescsi_info *)host->hostdata;233 234	seq_printf(m, "ARXE 16-bit SCSI driver v%s\n", VERSION);235	fas216_print_host(&info->info, m);236	fas216_print_stats(&info->info, m);237	fas216_print_devices(&info->info, m);238	return 0;239}240 241static const struct scsi_host_template arxescsi_template = {242	.show_info			= arxescsi_show_info,243	.name				= "ARXE SCSI card",244	.info				= arxescsi_info,245	.queuecommand			= fas216_noqueue_command,246	.eh_host_reset_handler		= fas216_eh_host_reset,247	.eh_bus_reset_handler		= fas216_eh_bus_reset,248	.eh_device_reset_handler	= fas216_eh_device_reset,249	.eh_abort_handler		= fas216_eh_abort,250	.cmd_size			= sizeof(struct fas216_cmd_priv),251	.can_queue			= 0,252	.this_id			= 7,253	.sg_tablesize			= SG_ALL,254	.dma_boundary			= PAGE_SIZE - 1,255	.proc_name			= "arxescsi",256};257 258static int arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)259{260	struct Scsi_Host *host;261	struct arxescsi_info *info;262	void __iomem *base;263	int ret;264 265	ret = ecard_request_resources(ec);266	if (ret)267		goto out;268 269	base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);270	if (!base) {271		ret = -ENOMEM;272		goto out_region;273	}274 275	host = scsi_host_alloc(&arxescsi_template, sizeof(struct arxescsi_info));276	if (!host) {277		ret = -ENOMEM;278		goto out_region;279	}280 281	info = (struct arxescsi_info *)host->hostdata;282	info->ec = ec;283	info->base = base;284 285	info->info.scsi.io_base		= base + 0x2000;286	info->info.scsi.irq		= 0;287	info->info.scsi.dma		= NO_DMA;288	info->info.scsi.io_shift	= 5;289	info->info.ifcfg.clockrate	= 24; /* MHz */290	info->info.ifcfg.select_timeout = 255;291	info->info.ifcfg.asyncperiod	= 200; /* ns */292	info->info.ifcfg.sync_max_depth	= 0;293	info->info.ifcfg.cntl3		= CNTL3_FASTSCSI | CNTL3_FASTCLK;294	info->info.ifcfg.disconnect_ok	= 0;295	info->info.ifcfg.wide_max_size	= 0;296	info->info.ifcfg.capabilities	= FASCAP_PSEUDODMA;297	info->info.dma.setup		= arxescsi_dma_setup;298	info->info.dma.pseudo		= arxescsi_dma_pseudo;299	info->info.dma.stop		= arxescsi_dma_stop;300		301	ec->irqaddr = base;302	ec->irqmask = CSTATUS_IRQ;303 304	ret = fas216_init(host);305	if (ret)306		goto out_unregister;307 308	ret = fas216_add(host, &ec->dev);309	if (ret == 0)310		goto out;311 312	fas216_release(host);313 out_unregister:314	scsi_host_put(host);315 out_region:316	ecard_release_resources(ec);317 out:318	return ret;319}320 321static void arxescsi_remove(struct expansion_card *ec)322{323	struct Scsi_Host *host = ecard_get_drvdata(ec);324 325	ecard_set_drvdata(ec, NULL);326	fas216_remove(host);327 328	fas216_release(host);329	scsi_host_put(host);330	ecard_release_resources(ec);331}332 333static const struct ecard_id arxescsi_cids[] = {334	{ MANU_ARXE, PROD_ARXE_SCSI },335	{ 0xffff, 0xffff },336};337 338static struct ecard_driver arxescsi_driver = {339	.probe		= arxescsi_probe,340	.remove		= arxescsi_remove,341	.id_table	= arxescsi_cids,342	.drv = {343		.name		= "arxescsi",344	},345};346 347static int __init init_arxe_scsi_driver(void)348{349	return ecard_register_driver(&arxescsi_driver);350}351 352static void __exit exit_arxe_scsi_driver(void)353{354	ecard_remove_driver(&arxescsi_driver);355}356 357module_init(init_arxe_scsi_driver);358module_exit(exit_arxe_scsi_driver);359 360MODULE_AUTHOR("Stefan Hanske");361MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines");362MODULE_LICENSE("GPL");363 364