brintos

brintos / linux-shallow public Read only

0
0
Text · 15.8 KiB · 99be9da Raw
647 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  linux/drivers/acorn/scsi/eesox.c4 *5 *  Copyright (C) 1997-2005 Russell King6 *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 *   01-10-1997	RMK		Created, READONLY version13 *   15-02-1998	RMK		READ/WRITE version14 *				added DMA support and hardware definitions15 *   14-03-1998	RMK		Updated DMA support16 *				Added terminator control17 *   15-04-1998	RMK		Only do PIO if FAS216 will allow it.18 *   27-06-1998	RMK		Changed asm/delay.h to linux/delay.h19 *   02-04-2000	RMK	0.0.3	Fixed NO_IRQ/NO_DMA problem, updated for new20 *				error handling code.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/delay.h>29#include <linux/interrupt.h>30#include <linux/init.h>31#include <linux/dma-mapping.h>32#include <linux/pgtable.h>33 34#include <asm/io.h>35#include <asm/dma.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#include "arm_scsi.h"46 47#include <scsi/scsicam.h>48 49#define EESOX_FAS216_OFFSET	0x300050#define EESOX_FAS216_SHIFT	551 52#define EESOX_DMASTAT		0x280053#define EESOX_STAT_INTR		0x0154#define EESOX_STAT_DMA		0x0255 56#define EESOX_CONTROL		0x280057#define EESOX_INTR_ENABLE	0x0458#define EESOX_TERM_ENABLE	0x0259#define EESOX_RESET		0x0160 61#define EESOX_DMADATA		0x380062 63#define VERSION "1.10 (17/01/2003 2.5.59)"64 65/*66 * Use term=0,1,0,0,0 to turn terminators on/off67 */68static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };69 70#define NR_SG	25671 72struct eesoxscsi_info {73	FAS216_Info		info;74	struct expansion_card	*ec;75	void __iomem		*base;76	void __iomem		*ctl_port;77	unsigned int		control;78	struct scatterlist	sg[NR_SG];	/* Scatter DMA list	*/79};80 81/* Prototype: void eesoxscsi_irqenable(ec, irqnr)82 * Purpose  : Enable interrupts on EESOX SCSI card83 * Params   : ec    - expansion card structure84 *          : irqnr - interrupt number85 */86static void87eesoxscsi_irqenable(struct expansion_card *ec, int irqnr)88{89	struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data;90 91	info->control |= EESOX_INTR_ENABLE;92 93	writeb(info->control, info->ctl_port);94}95 96/* Prototype: void eesoxscsi_irqdisable(ec, irqnr)97 * Purpose  : Disable interrupts on EESOX SCSI card98 * Params   : ec    - expansion card structure99 *          : irqnr - interrupt number100 */101static void102eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr)103{104	struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data;105 106	info->control &= ~EESOX_INTR_ENABLE;107 108	writeb(info->control, info->ctl_port);109}110 111static const expansioncard_ops_t eesoxscsi_ops = {112	.irqenable	= eesoxscsi_irqenable,113	.irqdisable	= eesoxscsi_irqdisable,114};115 116/* Prototype: void eesoxscsi_terminator_ctl(*host, on_off)117 * Purpose  : Turn the EESOX SCSI terminators on or off118 * Params   : host   - card to turn on/off119 *          : on_off - !0 to turn on, 0 to turn off120 */121static void122eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off)123{124	struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;125	unsigned long flags;126 127	spin_lock_irqsave(host->host_lock, flags);128	if (on_off)129		info->control |= EESOX_TERM_ENABLE;130	else131		info->control &= ~EESOX_TERM_ENABLE;132 133	writeb(info->control, info->ctl_port);134	spin_unlock_irqrestore(host->host_lock, flags);135}136 137/* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs)138 * Purpose  : handle interrupts from EESOX SCSI card139 * Params   : irq    - interrupt number140 *	      dev_id - user-defined (Scsi_Host structure)141 */142static irqreturn_t143eesoxscsi_intr(int irq, void *dev_id)144{145	struct eesoxscsi_info *info = dev_id;146 147	return fas216_intr(&info->info);148}149 150/* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type)151 * Purpose  : initialises DMA/PIO152 * Params   : host      - host153 *	      SCpnt     - command154 *	      direction - DMA on to/off of card155 *	      min_type  - minimum DMA support that we must have for this transfer156 * Returns  : type of transfer to be performed157 */158static fasdmatype_t159eesoxscsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp,160		       fasdmadir_t direction, fasdmatype_t min_type)161{162	struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;163	struct device *dev = scsi_get_device(host);164	int dmach = info->info.scsi.dma;165 166	if (dmach != NO_DMA &&167	    (min_type == fasdma_real_all || SCp->this_residual >= 512)) {168		int bufs, map_dir, dma_dir;169 170		bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);171 172		if (direction == DMA_OUT) {173			map_dir = DMA_TO_DEVICE;174			dma_dir = DMA_MODE_WRITE;175		} else {176			map_dir = DMA_FROM_DEVICE;177			dma_dir = DMA_MODE_READ;178		}179 180		dma_map_sg(dev, info->sg, bufs, map_dir);181 182		disable_dma(dmach);183		set_dma_sg(dmach, info->sg, bufs);184		set_dma_mode(dmach, dma_dir);185		enable_dma(dmach);186		return fasdma_real_all;187	}188	/*189	 * We don't do DMA, we only do slow PIO190	 *191	 * Some day, we will do Pseudo DMA192	 */193	return fasdma_pseudo;194}195 196static void eesoxscsi_buffer_in(void *buf, int length, void __iomem *base)197{198	const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET;199	const void __iomem *reg_dmastat = base + EESOX_DMASTAT;200	const void __iomem *reg_dmadata = base + EESOX_DMADATA;201	register const unsigned long mask = 0xffff;202 203	do {204		unsigned int status;205 206		/*207		 * Interrupt request?208		 */209		status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT));210		if (status & STAT_INT)211			break;212 213		/*214		 * DMA request active?215		 */216		status = readb(reg_dmastat);217		if (!(status & EESOX_STAT_DMA))218			continue;219 220		/*221		 * Get number of bytes in FIFO222		 */223		status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF;224		if (status > 16)225			status = 16;226		if (status > length)227			status = length;228 229		/*230		 * Align buffer.231		 */232		if (((u32)buf) & 2 && status >= 2) {233			*(u16 *)buf = readl(reg_dmadata);234			buf += 2;235			status -= 2;236			length -= 2;237		}238 239		if (status >= 8) {240			unsigned long l1, l2;241 242			l1 = readl(reg_dmadata) & mask;243			l1 |= readl(reg_dmadata) << 16;244			l2 = readl(reg_dmadata) & mask;245			l2 |= readl(reg_dmadata) << 16;246			*(u32 *)buf = l1;247			buf += 4;248			*(u32 *)buf = l2;249			buf += 4;250			length -= 8;251			continue;252		}253 254		if (status >= 4) {255			unsigned long l1;256 257			l1 = readl(reg_dmadata) & mask;258			l1 |= readl(reg_dmadata) << 16;259 260			*(u32 *)buf = l1;261			buf += 4;262			length -= 4;263			continue;264		}265 266		if (status >= 2) {267			*(u16 *)buf = readl(reg_dmadata);268			buf += 2;269			length -= 2;270		}271	} while (length);272}273 274static void eesoxscsi_buffer_out(void *buf, int length, void __iomem *base)275{276	const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET;277	const void __iomem *reg_dmastat = base + EESOX_DMASTAT;278	void __iomem *reg_dmadata = base + EESOX_DMADATA;279 280	do {281		unsigned int status;282 283		/*284		 * Interrupt request?285		 */286		status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT));287		if (status & STAT_INT)288			break;289 290		/*291		 * DMA request active?292		 */293		status = readb(reg_dmastat);294		if (!(status & EESOX_STAT_DMA))295			continue;296 297		/*298		 * Get number of bytes in FIFO299		 */300		status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF;301		if (status > 16)302			status = 16;303		status = 16 - status;304		if (status > length)305			status = length;306		status &= ~1;307 308		/*309		 * Align buffer.310		 */311		if (((u32)buf) & 2 && status >= 2) {312			writel(*(u16 *)buf << 16, reg_dmadata);313			buf += 2;314			status -= 2;315			length -= 2;316		}317 318		if (status >= 8) {319			unsigned long l1, l2;320 321			l1 = *(u32 *)buf;322			buf += 4;323			l2 = *(u32 *)buf;324			buf += 4;325 326			writel(l1 << 16, reg_dmadata);327			writel(l1, reg_dmadata);328			writel(l2 << 16, reg_dmadata);329			writel(l2, reg_dmadata);330			length -= 8;331			continue;332		}333 334		if (status >= 4) {335			unsigned long l1;336 337			l1 = *(u32 *)buf;338			buf += 4;339 340			writel(l1 << 16, reg_dmadata);341			writel(l1, reg_dmadata);342			length -= 4;343			continue;344		}345 346		if (status >= 2) {347			writel(*(u16 *)buf << 16, reg_dmadata);348			buf += 2;349			length -= 2;350		}351	} while (length);352}353 354static void355eesoxscsi_dma_pseudo(struct Scsi_Host *host, struct scsi_pointer *SCp,356		     fasdmadir_t dir, int transfer_size)357{358	struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;359	if (dir == DMA_IN) {360		eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, info->base);361	} else {362		eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, info->base);363	}364}365 366/* Prototype: int eesoxscsi_dma_stop(host, SCpnt)367 * Purpose  : stops DMA/PIO368 * Params   : host  - host369 *	      SCpnt - command370 */371static void372eesoxscsi_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp)373{374	struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;375	if (info->info.scsi.dma != NO_DMA)376		disable_dma(info->info.scsi.dma);377}378 379/* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host)380 * Purpose  : returns a descriptive string about this interface,381 * Params   : host - driver host structure to return info for.382 * Returns  : pointer to a static buffer containing null terminated string.383 */384static const char *eesoxscsi_info(struct Scsi_Host *host)385{386	struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;387	static char string[150];388 389	sprintf(string, "%s (%s) in slot %d v%s terminators o%s",390		host->hostt->name, info->info.scsi.type, info->ec->slot_no,391		VERSION, info->control & EESOX_TERM_ENABLE ? "n" : "ff");392 393	return string;394}395 396/* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)397 * Purpose  : Set a driver specific function398 * Params   : host   - host to setup399 *          : buffer - buffer containing string describing operation400 *          : length - length of string401 * Returns  : -EINVAL, or 0402 */403static int404eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)405{406	int ret = length;407 408	if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) {409		buffer += 9;410		length -= 9;411 412		if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {413			if (buffer[5] == '1')414				eesoxscsi_terminator_ctl(host, 1);415			else if (buffer[5] == '0')416				eesoxscsi_terminator_ctl(host, 0);417			else418				ret = -EINVAL;419		} else420			ret = -EINVAL;421	} else422		ret = -EINVAL;423 424	return ret;425}426 427static int eesoxscsi_show_info(struct seq_file *m, struct Scsi_Host *host)428{429	struct eesoxscsi_info *info;430 431	info = (struct eesoxscsi_info *)host->hostdata;432 433	seq_printf(m, "EESOX SCSI driver v%s\n", VERSION);434	fas216_print_host(&info->info, m);435	seq_printf(m, "Term    : o%s\n",436			info->control & EESOX_TERM_ENABLE ? "n" : "ff");437 438	fas216_print_stats(&info->info, m);439	fas216_print_devices(&info->info, m);440	return 0;441}442 443static ssize_t eesoxscsi_show_term(struct device *dev, struct device_attribute *attr, char *buf)444{445	struct expansion_card *ec = ECARD_DEV(dev);446	struct Scsi_Host *host = ecard_get_drvdata(ec);447	struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;448 449	return sprintf(buf, "%d\n", info->control & EESOX_TERM_ENABLE ? 1 : 0);450}451 452static ssize_t eesoxscsi_store_term(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)453{454	struct expansion_card *ec = ECARD_DEV(dev);455	struct Scsi_Host *host = ecard_get_drvdata(ec);456	struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;457	unsigned long flags;458 459	if (len > 1) {460		spin_lock_irqsave(host->host_lock, flags);461		if (buf[0] != '0') {462			info->control |= EESOX_TERM_ENABLE;463		} else {464			info->control &= ~EESOX_TERM_ENABLE;465		}466		writeb(info->control, info->ctl_port);467		spin_unlock_irqrestore(host->host_lock, flags);468	}469 470	return len;471}472 473static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR,474		   eesoxscsi_show_term, eesoxscsi_store_term);475 476static const struct scsi_host_template eesox_template = {477	.module				= THIS_MODULE,478	.show_info			= eesoxscsi_show_info,479	.write_info			= eesoxscsi_set_proc_info,480	.name				= "EESOX SCSI",481	.info				= eesoxscsi_info,482	.queuecommand			= fas216_queue_command,483	.eh_host_reset_handler		= fas216_eh_host_reset,484	.eh_bus_reset_handler		= fas216_eh_bus_reset,485	.eh_device_reset_handler	= fas216_eh_device_reset,486	.eh_abort_handler		= fas216_eh_abort,487	.cmd_size			= sizeof(struct fas216_cmd_priv),488	.can_queue			= 1,489	.this_id			= 7,490	.sg_tablesize			= SG_MAX_SEGMENTS,491	.dma_boundary			= IOMD_DMA_BOUNDARY,492	.proc_name			= "eesox",493};494 495static int eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id)496{497	struct Scsi_Host *host;498	struct eesoxscsi_info *info;499	void __iomem *base;500	int ret;501 502	ret = ecard_request_resources(ec);503	if (ret)504		goto out;505 506	base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);507	if (!base) {508		ret = -ENOMEM;509		goto out_region;510	}511 512	host = scsi_host_alloc(&eesox_template,513			       sizeof(struct eesoxscsi_info));514	if (!host) {515		ret = -ENOMEM;516		goto out_region;517	}518 519	ecard_set_drvdata(ec, host);520 521	info = (struct eesoxscsi_info *)host->hostdata;522	info->ec	= ec;523	info->base	= base;524	info->ctl_port	= base + EESOX_CONTROL;525	info->control	= term[ec->slot_no] ? EESOX_TERM_ENABLE : 0;526	writeb(info->control, info->ctl_port);527 528	info->info.scsi.io_base		= base + EESOX_FAS216_OFFSET;529	info->info.scsi.io_shift	= EESOX_FAS216_SHIFT;530	info->info.scsi.irq		= ec->irq;531	info->info.scsi.dma		= ec->dma;532	info->info.ifcfg.clockrate	= 40; /* MHz */533	info->info.ifcfg.select_timeout	= 255;534	info->info.ifcfg.asyncperiod	= 200; /* ns */535	info->info.ifcfg.sync_max_depth	= 7;536	info->info.ifcfg.cntl3		= CNTL3_FASTSCSI | CNTL3_FASTCLK;537	info->info.ifcfg.disconnect_ok	= 1;538	info->info.ifcfg.wide_max_size	= 0;539	info->info.ifcfg.capabilities	= FASCAP_PSEUDODMA;540	info->info.dma.setup		= eesoxscsi_dma_setup;541	info->info.dma.pseudo		= eesoxscsi_dma_pseudo;542	info->info.dma.stop		= eesoxscsi_dma_stop;543 544	ec->irqaddr	= base + EESOX_DMASTAT;545	ec->irqmask	= EESOX_STAT_INTR;546 547	ecard_setirq(ec, &eesoxscsi_ops, info);548 549	device_create_file(&ec->dev, &dev_attr_bus_term);550 551	ret = fas216_init(host);552	if (ret)553		goto out_free;554 555	ret = request_irq(ec->irq, eesoxscsi_intr, 0, "eesoxscsi", info);556	if (ret) {557		printk("scsi%d: IRQ%d not free: %d\n",558		       host->host_no, ec->irq, ret);559		goto out_remove;560	}561 562	if (info->info.scsi.dma != NO_DMA) {563		if (request_dma(info->info.scsi.dma, "eesox")) {564			printk("scsi%d: DMA%d not free, DMA disabled\n",565			       host->host_no, info->info.scsi.dma);566			info->info.scsi.dma = NO_DMA;567		} else {568			set_dma_speed(info->info.scsi.dma, 180);569			info->info.ifcfg.capabilities |= FASCAP_DMA;570			info->info.ifcfg.cntl3 |= CNTL3_BS8;571		}572	}573 574	ret = fas216_add(host, &ec->dev);575	if (ret == 0)576		goto out;577 578	if (info->info.scsi.dma != NO_DMA)579		free_dma(info->info.scsi.dma);580	free_irq(ec->irq, info);581 582 out_remove:583	fas216_remove(host);584 585 out_free:586	device_remove_file(&ec->dev, &dev_attr_bus_term);587	scsi_host_put(host);588 589 out_region:590	ecard_release_resources(ec);591 592 out:593	return ret;594}595 596static void eesoxscsi_remove(struct expansion_card *ec)597{598	struct Scsi_Host *host = ecard_get_drvdata(ec);599	struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;600 601	ecard_set_drvdata(ec, NULL);602	fas216_remove(host);603 604	if (info->info.scsi.dma != NO_DMA)605		free_dma(info->info.scsi.dma);606	free_irq(ec->irq, info);607 608	device_remove_file(&ec->dev, &dev_attr_bus_term);609 610	fas216_release(host);611	scsi_host_put(host);612	ecard_release_resources(ec);613}614 615static const struct ecard_id eesoxscsi_cids[] = {616	{ MANU_EESOX, PROD_EESOX_SCSI2 },617	{ 0xffff, 0xffff },618};619 620static struct ecard_driver eesoxscsi_driver = {621	.probe		= eesoxscsi_probe,622	.remove		= eesoxscsi_remove,623	.id_table	= eesoxscsi_cids,624	.drv = {625		.name		= "eesoxscsi",626	},627};628 629static int __init eesox_init(void)630{631	return ecard_register_driver(&eesoxscsi_driver);632}633 634static void __exit eesox_exit(void)635{636	ecard_remove_driver(&eesoxscsi_driver);637}638 639module_init(eesox_init);640module_exit(eesox_exit);641 642MODULE_AUTHOR("Russell King");643MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines");644module_param_array(term, int, NULL, 0);645MODULE_PARM_DESC(term, "SCSI bus termination");646MODULE_LICENSE("GPL");647