xref: /openbmc/linux/drivers/scsi/a100u2w.c (revision 157fc774)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Initio A100 device driver for Linux.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (c) 1994-1998 Initio Corporation
51da177e4SLinus Torvalds  * Copyright (c) 2003-2004 Christoph Hellwig
61da177e4SLinus Torvalds  * All rights reserved.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
91da177e4SLinus Torvalds  * it under the terms of the GNU General Public License as published by
101da177e4SLinus Torvalds  * the Free Software Foundation; either version 2, or (at your option)
111da177e4SLinus Torvalds  * any later version.
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * This program is distributed in the hope that it will be useful,
141da177e4SLinus Torvalds  * but WITHOUT ANY WARRANTY; without even the implied warranty of
151da177e4SLinus Torvalds  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
161da177e4SLinus Torvalds  * GNU General Public License for more details.
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  * You should have received a copy of the GNU General Public License
191da177e4SLinus Torvalds  * along with this program; see the file COPYING.  If not, write to
201da177e4SLinus Torvalds  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
211da177e4SLinus Torvalds  *
221da177e4SLinus Torvalds  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
231da177e4SLinus Torvalds  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241da177e4SLinus Torvalds  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251da177e4SLinus Torvalds  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
261da177e4SLinus Torvalds  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271da177e4SLinus Torvalds  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281da177e4SLinus Torvalds  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291da177e4SLinus Torvalds  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301da177e4SLinus Torvalds  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311da177e4SLinus Torvalds  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321da177e4SLinus Torvalds  * SUCH DAMAGE.
331da177e4SLinus Torvalds  */
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds /*
361da177e4SLinus Torvalds  * Revision History:
371da177e4SLinus Torvalds  * 07/02/98 hl	- v.91n Initial drivers.
381da177e4SLinus Torvalds  * 09/14/98 hl - v1.01 Support new Kernel.
391da177e4SLinus Torvalds  * 09/22/98 hl - v1.01a Support reset.
401da177e4SLinus Torvalds  * 09/24/98 hl - v1.01b Fixed reset.
411da177e4SLinus Torvalds  * 10/05/98 hl - v1.02 split the source code and release.
421da177e4SLinus Torvalds  * 12/19/98 bv - v1.02a Use spinlocks for 2.1.95 and up
431da177e4SLinus Torvalds  * 01/31/99 bv - v1.02b Use mdelay instead of waitForPause
441da177e4SLinus Torvalds  * 08/08/99 bv - v1.02c Use waitForPause again.
451da177e4SLinus Torvalds  * 06/25/02 Doug Ledford <dledford@redhat.com> - v1.02d
461da177e4SLinus Torvalds  *          - Remove limit on number of controllers
471da177e4SLinus Torvalds  *          - Port to DMA mapping API
481da177e4SLinus Torvalds  *          - Clean up interrupt handler registration
491da177e4SLinus Torvalds  *          - Fix memory leaks
501da177e4SLinus Torvalds  *          - Fix allocation of scsi host structs and private data
511da177e4SLinus Torvalds  * 11/18/03 Christoph Hellwig <hch@lst.de>
521da177e4SLinus Torvalds  *	    - Port to new probing API
531da177e4SLinus Torvalds  *	    - Fix some more leaks in init failure cases
541da177e4SLinus Torvalds  * 9/28/04 Christoph Hellwig <hch@lst.de>
551da177e4SLinus Torvalds  *	    - merge the two source files
561da177e4SLinus Torvalds  *	    - remove internal queueing code
57fa195afeSAlan Cox  * 14/06/07 Alan Cox <alan@lxorguk.ukuu.org.uk>
584023c474SAlan Cox  *	 - Grand cleanup and Linuxisation
591da177e4SLinus Torvalds  */
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #include <linux/module.h>
621da177e4SLinus Torvalds #include <linux/errno.h>
631da177e4SLinus Torvalds #include <linux/delay.h>
641da177e4SLinus Torvalds #include <linux/interrupt.h>
651da177e4SLinus Torvalds #include <linux/pci.h>
661da177e4SLinus Torvalds #include <linux/init.h>
671da177e4SLinus Torvalds #include <linux/blkdev.h>
681da177e4SLinus Torvalds #include <linux/spinlock.h>
691da177e4SLinus Torvalds #include <linux/kernel.h>
701da177e4SLinus Torvalds #include <linux/string.h>
711da177e4SLinus Torvalds #include <linux/ioport.h>
72910638aeSMatthias Gehre #include <linux/dma-mapping.h>
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds #include <asm/io.h>
751da177e4SLinus Torvalds #include <asm/irq.h>
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds #include <scsi/scsi.h>
781da177e4SLinus Torvalds #include <scsi/scsi_cmnd.h>
791da177e4SLinus Torvalds #include <scsi/scsi_device.h>
801da177e4SLinus Torvalds #include <scsi/scsi_host.h>
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds #include "a100u2w.h"
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds 
854023c474SAlan Cox static struct orc_scb *__orc_alloc_scb(struct orc_host * host);
864023c474SAlan Cox static void inia100_scb_handler(struct orc_host *host, struct orc_scb *scb);
871da177e4SLinus Torvalds 
884023c474SAlan Cox static struct orc_nvram nvram, *nvramp = &nvram;
891da177e4SLinus Torvalds 
904023c474SAlan Cox static u8 default_nvram[64] =
911da177e4SLinus Torvalds {
921da177e4SLinus Torvalds /*----------header -------------*/
931da177e4SLinus Torvalds 	0x01,			/* 0x00: Sub System Vendor ID 0 */
941da177e4SLinus Torvalds 	0x11,			/* 0x01: Sub System Vendor ID 1 */
951da177e4SLinus Torvalds 	0x60,			/* 0x02: Sub System ID 0        */
961da177e4SLinus Torvalds 	0x10,			/* 0x03: Sub System ID 1        */
971da177e4SLinus Torvalds 	0x00,			/* 0x04: SubClass               */
981da177e4SLinus Torvalds 	0x01,			/* 0x05: Vendor ID 0            */
991da177e4SLinus Torvalds 	0x11,			/* 0x06: Vendor ID 1            */
1001da177e4SLinus Torvalds 	0x60,			/* 0x07: Device ID 0            */
1011da177e4SLinus Torvalds 	0x10,			/* 0x08: Device ID 1            */
1021da177e4SLinus Torvalds 	0x00,			/* 0x09: Reserved               */
1031da177e4SLinus Torvalds 	0x00,			/* 0x0A: Reserved               */
1041da177e4SLinus Torvalds 	0x01,			/* 0x0B: Revision of Data Structure     */
1051da177e4SLinus Torvalds 				/* -- Host Adapter Structure --- */
1061da177e4SLinus Torvalds 	0x01,			/* 0x0C: Number Of SCSI Channel */
1071da177e4SLinus Torvalds 	0x01,			/* 0x0D: BIOS Configuration 1   */
1081da177e4SLinus Torvalds 	0x00,			/* 0x0E: BIOS Configuration 2   */
1091da177e4SLinus Torvalds 	0x00,			/* 0x0F: BIOS Configuration 3   */
1101da177e4SLinus Torvalds 				/* --- SCSI Channel 0 Configuration --- */
1111da177e4SLinus Torvalds 	0x07,			/* 0x10: H/A ID                 */
1121da177e4SLinus Torvalds 	0x83,			/* 0x11: Channel Configuration  */
1131da177e4SLinus Torvalds 	0x20,			/* 0x12: MAX TAG per target     */
1141da177e4SLinus Torvalds 	0x0A,			/* 0x13: SCSI Reset Recovering time     */
1151da177e4SLinus Torvalds 	0x00,			/* 0x14: Channel Configuration4 */
1161da177e4SLinus Torvalds 	0x00,			/* 0x15: Channel Configuration5 */
1171da177e4SLinus Torvalds 				/* SCSI Channel 0 Target Configuration  */
1181da177e4SLinus Torvalds 				/* 0x16-0x25                    */
1191da177e4SLinus Torvalds 	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
1201da177e4SLinus Torvalds 	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
1211da177e4SLinus Torvalds 				/* --- SCSI Channel 1 Configuration --- */
1221da177e4SLinus Torvalds 	0x07,			/* 0x26: H/A ID                 */
1231da177e4SLinus Torvalds 	0x83,			/* 0x27: Channel Configuration  */
1241da177e4SLinus Torvalds 	0x20,			/* 0x28: MAX TAG per target     */
1251da177e4SLinus Torvalds 	0x0A,			/* 0x29: SCSI Reset Recovering time     */
1261da177e4SLinus Torvalds 	0x00,			/* 0x2A: Channel Configuration4 */
1271da177e4SLinus Torvalds 	0x00,			/* 0x2B: Channel Configuration5 */
1281da177e4SLinus Torvalds 				/* SCSI Channel 1 Target Configuration  */
1291da177e4SLinus Torvalds 				/* 0x2C-0x3B                    */
1301da177e4SLinus Torvalds 	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
1311da177e4SLinus Torvalds 	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
1321da177e4SLinus Torvalds 	0x00,			/* 0x3C: Reserved               */
1331da177e4SLinus Torvalds 	0x00,			/* 0x3D: Reserved               */
1341da177e4SLinus Torvalds 	0x00,			/* 0x3E: Reserved               */
1351da177e4SLinus Torvalds 	0x00			/* 0x3F: Checksum               */
1361da177e4SLinus Torvalds };
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 
wait_chip_ready(struct orc_host * host)1394023c474SAlan Cox static u8 wait_chip_ready(struct orc_host * host)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds 	int i;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	for (i = 0; i < 10; i++) {	/* Wait 1 second for report timeout     */
1444023c474SAlan Cox 		if (inb(host->base + ORC_HCTRL) & HOSTSTOP)	/* Wait HOSTSTOP set */
1451da177e4SLinus Torvalds 			return 1;
146d6aec1caSJia-Ju Bai 		msleep(100);
1474023c474SAlan Cox 	}
1484023c474SAlan Cox 	return 0;
1494023c474SAlan Cox }
1504023c474SAlan Cox 
wait_firmware_ready(struct orc_host * host)1514023c474SAlan Cox static u8 wait_firmware_ready(struct orc_host * host)
1524023c474SAlan Cox {
1534023c474SAlan Cox 	int i;
1544023c474SAlan Cox 
1554023c474SAlan Cox 	for (i = 0; i < 10; i++) {	/* Wait 1 second for report timeout     */
1564023c474SAlan Cox 		if (inb(host->base + ORC_HSTUS) & RREADY)		/* Wait READY set */
1574023c474SAlan Cox 			return 1;
158d6aec1caSJia-Ju Bai 		msleep(100);	/* wait 100ms before try again  */
1591da177e4SLinus Torvalds 	}
1601da177e4SLinus Torvalds 	return 0;
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds /***************************************************************************/
wait_scsi_reset_done(struct orc_host * host)1644023c474SAlan Cox static u8 wait_scsi_reset_done(struct orc_host * host)
1651da177e4SLinus Torvalds {
1661da177e4SLinus Torvalds 	int i;
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	for (i = 0; i < 10; i++) {	/* Wait 1 second for report timeout     */
1694023c474SAlan Cox 		if (!(inb(host->base + ORC_HCTRL) & SCSIRST))	/* Wait SCSIRST done */
1701da177e4SLinus Torvalds 			return 1;
1714023c474SAlan Cox 		mdelay(100);	/* wait 100ms before try again  */
1721da177e4SLinus Torvalds 	}
1731da177e4SLinus Torvalds 	return 0;
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds /***************************************************************************/
wait_HDO_off(struct orc_host * host)1774023c474SAlan Cox static u8 wait_HDO_off(struct orc_host * host)
1781da177e4SLinus Torvalds {
1791da177e4SLinus Torvalds 	int i;
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 	for (i = 0; i < 10; i++) {	/* Wait 1 second for report timeout     */
1824023c474SAlan Cox 		if (!(inb(host->base + ORC_HCTRL) & HDO))		/* Wait HDO off */
1831da177e4SLinus Torvalds 			return 1;
1844023c474SAlan Cox 		mdelay(100);	/* wait 100ms before try again  */
1851da177e4SLinus Torvalds 	}
1861da177e4SLinus Torvalds 	return 0;
1871da177e4SLinus Torvalds }
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds /***************************************************************************/
wait_hdi_set(struct orc_host * host,u8 * data)1904023c474SAlan Cox static u8 wait_hdi_set(struct orc_host * host, u8 * data)
1911da177e4SLinus Torvalds {
1921da177e4SLinus Torvalds 	int i;
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 	for (i = 0; i < 10; i++) {	/* Wait 1 second for report timeout     */
1954023c474SAlan Cox 		if ((*data = inb(host->base + ORC_HSTUS)) & HDI)
1961da177e4SLinus Torvalds 			return 1;	/* Wait HDI set */
1974023c474SAlan Cox 		mdelay(100);	/* wait 100ms before try again  */
1981da177e4SLinus Torvalds 	}
1991da177e4SLinus Torvalds 	return 0;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds /***************************************************************************/
orc_read_fwrev(struct orc_host * host)2034023c474SAlan Cox static unsigned short orc_read_fwrev(struct orc_host * host)
2041da177e4SLinus Torvalds {
2054023c474SAlan Cox 	u16 version;
2064023c474SAlan Cox 	u8 data;
2071da177e4SLinus Torvalds 
2084023c474SAlan Cox 	outb(ORC_CMD_VERSION, host->base + ORC_HDATA);
2094023c474SAlan Cox 	outb(HDO, host->base + ORC_HCTRL);
2104023c474SAlan Cox 	if (wait_HDO_off(host) == 0)	/* Wait HDO off   */
2111da177e4SLinus Torvalds 		return 0;
2121da177e4SLinus Torvalds 
2134023c474SAlan Cox 	if (wait_hdi_set(host, &data) == 0)	/* Wait HDI set   */
2141da177e4SLinus Torvalds 		return 0;
2154023c474SAlan Cox 	version = inb(host->base + ORC_HDATA);
2164023c474SAlan Cox 	outb(data, host->base + ORC_HSTUS);	/* Clear HDI            */
2171da177e4SLinus Torvalds 
2184023c474SAlan Cox 	if (wait_hdi_set(host, &data) == 0)	/* Wait HDI set   */
2191da177e4SLinus Torvalds 		return 0;
2204023c474SAlan Cox 	version |= inb(host->base + ORC_HDATA) << 8;
2214023c474SAlan Cox 	outb(data, host->base + ORC_HSTUS);	/* Clear HDI            */
2221da177e4SLinus Torvalds 
2234023c474SAlan Cox 	return version;
2241da177e4SLinus Torvalds }
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds /***************************************************************************/
orc_nv_write(struct orc_host * host,unsigned char address,unsigned char value)2274023c474SAlan Cox static u8 orc_nv_write(struct orc_host * host, unsigned char address, unsigned char value)
2281da177e4SLinus Torvalds {
2294023c474SAlan Cox 	outb(ORC_CMD_SET_NVM, host->base + ORC_HDATA);	/* Write command */
2304023c474SAlan Cox 	outb(HDO, host->base + ORC_HCTRL);
2314023c474SAlan Cox 	if (wait_HDO_off(host) == 0)	/* Wait HDO off   */
2321da177e4SLinus Torvalds 		return 0;
2331da177e4SLinus Torvalds 
2344023c474SAlan Cox 	outb(address, host->base + ORC_HDATA);	/* Write address */
2354023c474SAlan Cox 	outb(HDO, host->base + ORC_HCTRL);
2364023c474SAlan Cox 	if (wait_HDO_off(host) == 0)	/* Wait HDO off   */
2371da177e4SLinus Torvalds 		return 0;
2381da177e4SLinus Torvalds 
2394023c474SAlan Cox 	outb(value, host->base + ORC_HDATA);	/* Write value  */
2404023c474SAlan Cox 	outb(HDO, host->base + ORC_HCTRL);
2414023c474SAlan Cox 	if (wait_HDO_off(host) == 0)	/* Wait HDO off   */
2421da177e4SLinus Torvalds 		return 0;
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	return 1;
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds /***************************************************************************/
orc_nv_read(struct orc_host * host,u8 address,u8 * ptr)2484023c474SAlan Cox static u8 orc_nv_read(struct orc_host * host, u8 address, u8 *ptr)
2491da177e4SLinus Torvalds {
2504023c474SAlan Cox 	unsigned char data;
2511da177e4SLinus Torvalds 
2524023c474SAlan Cox 	outb(ORC_CMD_GET_NVM, host->base + ORC_HDATA);	/* Write command */
2534023c474SAlan Cox 	outb(HDO, host->base + ORC_HCTRL);
2544023c474SAlan Cox 	if (wait_HDO_off(host) == 0)	/* Wait HDO off   */
2551da177e4SLinus Torvalds 		return 0;
2561da177e4SLinus Torvalds 
2574023c474SAlan Cox 	outb(address, host->base + ORC_HDATA);	/* Write address */
2584023c474SAlan Cox 	outb(HDO, host->base + ORC_HCTRL);
2594023c474SAlan Cox 	if (wait_HDO_off(host) == 0)	/* Wait HDO off   */
2601da177e4SLinus Torvalds 		return 0;
2611da177e4SLinus Torvalds 
2624023c474SAlan Cox 	if (wait_hdi_set(host, &data) == 0)	/* Wait HDI set   */
2631da177e4SLinus Torvalds 		return 0;
2644023c474SAlan Cox 	*ptr = inb(host->base + ORC_HDATA);
2654023c474SAlan Cox 	outb(data, host->base + ORC_HSTUS);	/* Clear HDI    */
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	return 1;
2684023c474SAlan Cox 
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
2714023c474SAlan Cox /**
272c548a625SLee Jones  *	orc_exec_scb		-	Queue an SCB with the HA
2734023c474SAlan Cox  *	@host: host adapter the SCB belongs to
2744023c474SAlan Cox  *	@scb: SCB to queue for execution
2754023c474SAlan Cox  */
2764023c474SAlan Cox 
orc_exec_scb(struct orc_host * host,struct orc_scb * scb)2774023c474SAlan Cox static void orc_exec_scb(struct orc_host * host, struct orc_scb * scb)
2781da177e4SLinus Torvalds {
2794023c474SAlan Cox 	scb->status = ORCSCB_POST;
2804023c474SAlan Cox 	outb(scb->scbidx, host->base + ORC_PQUEUE);
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 
2844023c474SAlan Cox /**
2854023c474SAlan Cox  *	se2_rd_all	-	read SCSI parameters from EEPROM
2864023c474SAlan Cox  *	@host: Host whose EEPROM is being loaded
2874023c474SAlan Cox  *
2884023c474SAlan Cox  *	Read SCSI H/A configuration parameters from serial EEPROM
2894023c474SAlan Cox  */
2904023c474SAlan Cox 
se2_rd_all(struct orc_host * host)2914023c474SAlan Cox static int se2_rd_all(struct orc_host * host)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	int i;
2944023c474SAlan Cox 	u8 *np, chksum = 0;
2951da177e4SLinus Torvalds 
2964023c474SAlan Cox 	np = (u8 *) nvramp;
2971da177e4SLinus Torvalds 	for (i = 0; i < 64; i++, np++) {	/* <01> */
2984023c474SAlan Cox 		if (orc_nv_read(host, (u8) i, np) == 0)
2991da177e4SLinus Torvalds 			return -1;
3001da177e4SLinus Torvalds 	}
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 	/*------ Is ckecksum ok ? ------*/
3034023c474SAlan Cox 	np = (u8 *) nvramp;
3041da177e4SLinus Torvalds 	for (i = 0; i < 63; i++)
3051da177e4SLinus Torvalds 		chksum += *np++;
3061da177e4SLinus Torvalds 
3074023c474SAlan Cox 	if (nvramp->CheckSum != (u8) chksum)
3081da177e4SLinus Torvalds 		return -1;
3091da177e4SLinus Torvalds 	return 1;
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
3124023c474SAlan Cox /**
3134023c474SAlan Cox  *	se2_update_all		-	update the EEPROM
3144023c474SAlan Cox  *	@host: Host whose EEPROM is being updated
3154023c474SAlan Cox  *
3164023c474SAlan Cox  *	Update changed bytes in the EEPROM image.
3174023c474SAlan Cox  */
3184023c474SAlan Cox 
se2_update_all(struct orc_host * host)3194023c474SAlan Cox static void se2_update_all(struct orc_host * host)
3201da177e4SLinus Torvalds {				/* setup default pattern  */
3211da177e4SLinus Torvalds 	int i;
3224023c474SAlan Cox 	u8 *np, *np1, chksum = 0;
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	/* Calculate checksum first   */
3254023c474SAlan Cox 	np = (u8 *) default_nvram;
3261da177e4SLinus Torvalds 	for (i = 0; i < 63; i++)
3271da177e4SLinus Torvalds 		chksum += *np++;
3281da177e4SLinus Torvalds 	*np = chksum;
3291da177e4SLinus Torvalds 
3304023c474SAlan Cox 	np = (u8 *) default_nvram;
3314023c474SAlan Cox 	np1 = (u8 *) nvramp;
3321da177e4SLinus Torvalds 	for (i = 0; i < 64; i++, np++, np1++) {
3334023c474SAlan Cox 		if (*np != *np1)
3344023c474SAlan Cox 			orc_nv_write(host, (u8) i, *np);
3351da177e4SLinus Torvalds 	}
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds 
3384023c474SAlan Cox /**
3394023c474SAlan Cox  *	read_eeprom		-	load EEPROM
3404023c474SAlan Cox  *	@host: Host EEPROM to read
3414023c474SAlan Cox  *
3424023c474SAlan Cox  *	Read the EEPROM for a given host. If it is invalid or fails
3434023c474SAlan Cox  *	the restore the defaults and use them.
3444023c474SAlan Cox  */
3454023c474SAlan Cox 
read_eeprom(struct orc_host * host)3464023c474SAlan Cox static void read_eeprom(struct orc_host * host)
3471da177e4SLinus Torvalds {
3484023c474SAlan Cox 	if (se2_rd_all(host) != 1) {
3494023c474SAlan Cox 		se2_update_all(host);	/* setup default pattern        */
3504023c474SAlan Cox 		se2_rd_all(host);	/* load again                   */
3511da177e4SLinus Torvalds 	}
3521da177e4SLinus Torvalds }
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds 
3554023c474SAlan Cox /**
3564023c474SAlan Cox  *	orc_load_firmware	-	initialise firmware
3574023c474SAlan Cox  *	@host: Host to set up
3584023c474SAlan Cox  *
3594023c474SAlan Cox  *	Load the firmware from the EEPROM into controller SRAM. This
3604023c474SAlan Cox  *	is basically a 4K block copy and then a 4K block read to check
3614023c474SAlan Cox  *	correctness. The rest is convulted by the indirect interfaces
3624023c474SAlan Cox  *	in the hardware
3634023c474SAlan Cox  */
3644023c474SAlan Cox 
orc_load_firmware(struct orc_host * host)3654023c474SAlan Cox static u8 orc_load_firmware(struct orc_host * host)
3661da177e4SLinus Torvalds {
3674023c474SAlan Cox 	u32 data32;
3684023c474SAlan Cox 	u16 bios_addr;
3694023c474SAlan Cox 	u16 i;
3704023c474SAlan Cox 	u8 *data32_ptr, data;
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 
3734023c474SAlan Cox 	/* Set up the EEPROM for access */
3744023c474SAlan Cox 
3754023c474SAlan Cox 	data = inb(host->base + ORC_GCFG);
3764023c474SAlan Cox 	outb(data | EEPRG, host->base + ORC_GCFG);	/* Enable EEPROM programming */
3774023c474SAlan Cox 	outb(0x00, host->base + ORC_EBIOSADR2);
3784023c474SAlan Cox 	outw(0x0000, host->base + ORC_EBIOSADR0);
3794023c474SAlan Cox 	if (inb(host->base + ORC_EBIOSDATA) != 0x55) {
3804023c474SAlan Cox 		outb(data, host->base + ORC_GCFG);	/* Disable EEPROM programming */
3811da177e4SLinus Torvalds 		return 0;
3821da177e4SLinus Torvalds 	}
3834023c474SAlan Cox 	outw(0x0001, host->base + ORC_EBIOSADR0);
3844023c474SAlan Cox 	if (inb(host->base + ORC_EBIOSDATA) != 0xAA) {
3854023c474SAlan Cox 		outb(data, host->base + ORC_GCFG);	/* Disable EEPROM programming */
3861da177e4SLinus Torvalds 		return 0;
3871da177e4SLinus Torvalds 	}
3881da177e4SLinus Torvalds 
3894023c474SAlan Cox 	outb(PRGMRST | DOWNLOAD, host->base + ORC_RISCCTL);	/* Enable SRAM programming */
3904023c474SAlan Cox 	data32_ptr = (u8 *) & data32;
391987ff954SMikulas Patocka 	data32 = cpu_to_le32(0);		/* Initial FW address to 0 */
3924023c474SAlan Cox 	outw(0x0010, host->base + ORC_EBIOSADR0);
3934023c474SAlan Cox 	*data32_ptr = inb(host->base + ORC_EBIOSDATA);		/* Read from BIOS */
3944023c474SAlan Cox 	outw(0x0011, host->base + ORC_EBIOSADR0);
3954023c474SAlan Cox 	*(data32_ptr + 1) = inb(host->base + ORC_EBIOSDATA);	/* Read from BIOS */
3964023c474SAlan Cox 	outw(0x0012, host->base + ORC_EBIOSADR0);
3974023c474SAlan Cox 	*(data32_ptr + 2) = inb(host->base + ORC_EBIOSDATA);	/* Read from BIOS */
3984023c474SAlan Cox 	outw(*(data32_ptr + 2), host->base + ORC_EBIOSADR2);
399987ff954SMikulas Patocka 	outl(le32_to_cpu(data32), host->base + ORC_FWBASEADR);		/* Write FW address */
4004023c474SAlan Cox 
4014023c474SAlan Cox 	/* Copy the code from the BIOS to the SRAM */
4024023c474SAlan Cox 
40356d387ecSMikulas Patocka 	udelay(500);	/* Required on Sun Ultra 5 ... 350 -> failures */
404987ff954SMikulas Patocka 	bios_addr = (u16) le32_to_cpu(data32);	/* FW code locate at BIOS address + ? */
4054023c474SAlan Cox 	for (i = 0, data32_ptr = (u8 *) & data32;	/* Download the code    */
4061da177e4SLinus Torvalds 	     i < 0x1000;	/* Firmware code size = 4K      */
4074023c474SAlan Cox 	     i++, bios_addr++) {
4084023c474SAlan Cox 		outw(bios_addr, host->base + ORC_EBIOSADR0);
4094023c474SAlan Cox 		*data32_ptr++ = inb(host->base + ORC_EBIOSDATA);	/* Read from BIOS */
4101da177e4SLinus Torvalds 		if ((i % 4) == 3) {
411987ff954SMikulas Patocka 			outl(le32_to_cpu(data32), host->base + ORC_RISCRAM);	/* Write every 4 bytes */
4124023c474SAlan Cox 			data32_ptr = (u8 *) & data32;
4131da177e4SLinus Torvalds 		}
4141da177e4SLinus Torvalds 	}
4151da177e4SLinus Torvalds 
4164023c474SAlan Cox 	/* Go back and check they match */
4174023c474SAlan Cox 
4184023c474SAlan Cox 	outb(PRGMRST | DOWNLOAD, host->base + ORC_RISCCTL);	/* Reset program count 0 */
419b595076aSUwe Kleine-König 	bios_addr -= 0x1000;	/* Reset the BIOS address */
4204023c474SAlan Cox 	for (i = 0, data32_ptr = (u8 *) & data32;	/* Check the code       */
4211da177e4SLinus Torvalds 	     i < 0x1000;	/* Firmware code size = 4K      */
4224023c474SAlan Cox 	     i++, bios_addr++) {
4234023c474SAlan Cox 		outw(bios_addr, host->base + ORC_EBIOSADR0);
4244023c474SAlan Cox 		*data32_ptr++ = inb(host->base + ORC_EBIOSDATA);	/* Read from BIOS */
4251da177e4SLinus Torvalds 		if ((i % 4) == 3) {
426987ff954SMikulas Patocka 			if (inl(host->base + ORC_RISCRAM) != le32_to_cpu(data32)) {
4274023c474SAlan Cox 				outb(PRGMRST, host->base + ORC_RISCCTL);	/* Reset program to 0 */
4284023c474SAlan Cox 				outb(data, host->base + ORC_GCFG);	/*Disable EEPROM programming */
4291da177e4SLinus Torvalds 				return 0;
4301da177e4SLinus Torvalds 			}
4314023c474SAlan Cox 			data32_ptr = (u8 *) & data32;
4321da177e4SLinus Torvalds 		}
4331da177e4SLinus Torvalds 	}
4344023c474SAlan Cox 
4354023c474SAlan Cox 	/* Success */
4364023c474SAlan Cox 	outb(PRGMRST, host->base + ORC_RISCCTL);	/* Reset program to 0   */
4374023c474SAlan Cox 	outb(data, host->base + ORC_GCFG);	/* Disable EEPROM programming */
4381da177e4SLinus Torvalds 	return 1;
4391da177e4SLinus Torvalds }
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds /***************************************************************************/
setup_SCBs(struct orc_host * host)4424023c474SAlan Cox static void setup_SCBs(struct orc_host * host)
4431da177e4SLinus Torvalds {
4444023c474SAlan Cox 	struct orc_scb *scb;
4451da177e4SLinus Torvalds 	int i;
4464023c474SAlan Cox 	struct orc_extended_scb *escb;
4474023c474SAlan Cox 	dma_addr_t escb_phys;
4481da177e4SLinus Torvalds 
4494023c474SAlan Cox 	/* Setup SCB base and SCB Size registers */
4504023c474SAlan Cox 	outb(ORC_MAXQUEUE, host->base + ORC_SCBSIZE);	/* Total number of SCBs */
4514023c474SAlan Cox 	/* SCB base address 0      */
4524023c474SAlan Cox 	outl(host->scb_phys, host->base + ORC_SCBBASE0);
4534023c474SAlan Cox 	/* SCB base address 1      */
4544023c474SAlan Cox 	outl(host->scb_phys, host->base + ORC_SCBBASE1);
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 	/* setup scatter list address with one buffer */
4574023c474SAlan Cox 	scb = host->scb_virt;
4584023c474SAlan Cox 	escb = host->escb_virt;
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 	for (i = 0; i < ORC_MAXQUEUE; i++) {
4614023c474SAlan Cox 		escb_phys = (host->escb_phys + (sizeof(struct orc_extended_scb) * i));
462987ff954SMikulas Patocka 		scb->sg_addr = cpu_to_le32((u32) escb_phys);
463987ff954SMikulas Patocka 		scb->sense_addr = cpu_to_le32((u32) escb_phys);
4644023c474SAlan Cox 		scb->escb = escb;
4654023c474SAlan Cox 		scb->scbidx = i;
4664023c474SAlan Cox 		scb++;
4674023c474SAlan Cox 		escb++;
4684023c474SAlan Cox 	}
4691da177e4SLinus Torvalds }
4701da177e4SLinus Torvalds 
4714023c474SAlan Cox /**
4724023c474SAlan Cox  *	init_alloc_map		-	initialise allocation map
4734023c474SAlan Cox  *	@host: host map to configure
4744023c474SAlan Cox  *
4754023c474SAlan Cox  *	Initialise the allocation maps for this device. If the device
4764023c474SAlan Cox  *	is not quiescent the caller must hold the allocation lock
4774023c474SAlan Cox  */
4781da177e4SLinus Torvalds 
init_alloc_map(struct orc_host * host)4794023c474SAlan Cox static void init_alloc_map(struct orc_host * host)
4801da177e4SLinus Torvalds {
4814023c474SAlan Cox 	u8 i, j;
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	for (i = 0; i < MAX_CHANNELS; i++) {
4841da177e4SLinus Torvalds 		for (j = 0; j < 8; j++) {
4854023c474SAlan Cox 			host->allocation_map[i][j] = 0xffffffff;
4861da177e4SLinus Torvalds 		}
4871da177e4SLinus Torvalds 	}
4881da177e4SLinus Torvalds }
4891da177e4SLinus Torvalds 
4904023c474SAlan Cox /**
4914023c474SAlan Cox  *	init_orchid		-	initialise the host adapter
4924023c474SAlan Cox  *	@host:host adapter to initialise
4934023c474SAlan Cox  *
4943ad2f3fbSDaniel Mack  *	Initialise the controller and if necessary load the firmware.
4954023c474SAlan Cox  *
4964023c474SAlan Cox  *	Returns -1 if the initialisation fails.
4974023c474SAlan Cox  */
4984023c474SAlan Cox 
init_orchid(struct orc_host * host)4994023c474SAlan Cox static int init_orchid(struct orc_host * host)
5001da177e4SLinus Torvalds {
5014023c474SAlan Cox 	u8 *ptr;
5024023c474SAlan Cox 	u16 revision;
5034023c474SAlan Cox 	u8 i;
5041da177e4SLinus Torvalds 
5054023c474SAlan Cox 	init_alloc_map(host);
5064023c474SAlan Cox 	outb(0xFF, host->base + ORC_GIMSK);	/* Disable all interrupts */
5074023c474SAlan Cox 
5084023c474SAlan Cox 	if (inb(host->base + ORC_HSTUS) & RREADY) {	/* Orchid is ready */
5094023c474SAlan Cox 		revision = orc_read_fwrev(host);
5101da177e4SLinus Torvalds 		if (revision == 0xFFFF) {
5114023c474SAlan Cox 			outb(DEVRST, host->base + ORC_HCTRL);	/* Reset Host Adapter   */
5124023c474SAlan Cox 			if (wait_chip_ready(host) == 0)
5134023c474SAlan Cox 				return -1;
5144023c474SAlan Cox 			orc_load_firmware(host);	/* Download FW                  */
5154023c474SAlan Cox 			setup_SCBs(host);	/* Setup SCB base and SCB Size registers */
5164023c474SAlan Cox 			outb(0x00, host->base + ORC_HCTRL);	/* clear HOSTSTOP       */
5174023c474SAlan Cox 			if (wait_firmware_ready(host) == 0)
5184023c474SAlan Cox 				return -1;
5191da177e4SLinus Torvalds 			/* Wait for firmware ready     */
5201da177e4SLinus Torvalds 		} else {
5214023c474SAlan Cox 			setup_SCBs(host);	/* Setup SCB base and SCB Size registers */
5221da177e4SLinus Torvalds 		}
5231da177e4SLinus Torvalds 	} else {		/* Orchid is not Ready          */
5244023c474SAlan Cox 		outb(DEVRST, host->base + ORC_HCTRL);	/* Reset Host Adapter   */
5254023c474SAlan Cox 		if (wait_chip_ready(host) == 0)
5264023c474SAlan Cox 			return -1;
5274023c474SAlan Cox 		orc_load_firmware(host);	/* Download FW                  */
5284023c474SAlan Cox 		setup_SCBs(host);	/* Setup SCB base and SCB Size registers */
5294023c474SAlan Cox 		outb(HDO, host->base + ORC_HCTRL);	/* Do Hardware Reset &  */
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds 		/*     clear HOSTSTOP  */
5324023c474SAlan Cox 		if (wait_firmware_ready(host) == 0)		/* Wait for firmware ready      */
5334023c474SAlan Cox 			return -1;
5341da177e4SLinus Torvalds 	}
5351da177e4SLinus Torvalds 
5364023c474SAlan Cox 	/* Load an EEProm copy into RAM */
5374023c474SAlan Cox 	/* Assumes single threaded at this point */
5384023c474SAlan Cox 	read_eeprom(host);
5391da177e4SLinus Torvalds 
5404023c474SAlan Cox 	if (nvramp->revision != 1)
5414023c474SAlan Cox 		return -1;
5421da177e4SLinus Torvalds 
5434023c474SAlan Cox 	host->scsi_id = nvramp->scsi_id;
5444023c474SAlan Cox 	host->BIOScfg = nvramp->BIOSConfig1;
5454023c474SAlan Cox 	host->max_targets = MAX_TARGETS;
5464023c474SAlan Cox 	ptr = (u8 *) & (nvramp->Target00Config);
5474023c474SAlan Cox 	for (i = 0; i < 16; ptr++, i++) {
5484023c474SAlan Cox 		host->target_flag[i] = *ptr;
5494023c474SAlan Cox 		host->max_tags[i] = ORC_MAXTAGS;
5501da177e4SLinus Torvalds 	}
5511da177e4SLinus Torvalds 
5524023c474SAlan Cox 	if (nvramp->SCSI0Config & NCC_BUSRESET)
5534023c474SAlan Cox 		host->flags |= HCF_SCSI_RESET;
5544023c474SAlan Cox 	outb(0xFB, host->base + ORC_GIMSK);	/* enable RP FIFO interrupt     */
5554023c474SAlan Cox 	return 0;
5564023c474SAlan Cox }
5574023c474SAlan Cox 
5584023c474SAlan Cox /**
5594023c474SAlan Cox  *	orc_reset_scsi_bus		-	perform bus reset
5604023c474SAlan Cox  *	@host: host being reset
5614023c474SAlan Cox  *
5624023c474SAlan Cox  *	Perform a full bus reset on the adapter.
5634023c474SAlan Cox  */
5644023c474SAlan Cox 
orc_reset_scsi_bus(struct orc_host * host)5654023c474SAlan Cox static int orc_reset_scsi_bus(struct orc_host * host)
5661da177e4SLinus Torvalds {				/* I need Host Control Block Information */
5674023c474SAlan Cox 	unsigned long flags;
5681da177e4SLinus Torvalds 
5694023c474SAlan Cox 	spin_lock_irqsave(&host->allocation_lock, flags);
5701da177e4SLinus Torvalds 
5714023c474SAlan Cox 	init_alloc_map(host);
5721da177e4SLinus Torvalds 	/* reset scsi bus */
5734023c474SAlan Cox 	outb(SCSIRST, host->base + ORC_HCTRL);
5744023c474SAlan Cox 	/* FIXME: We can spend up to a second with the lock held and
5754023c474SAlan Cox 	   interrupts off here */
5764023c474SAlan Cox 	if (wait_scsi_reset_done(host) == 0) {
5774023c474SAlan Cox 		spin_unlock_irqrestore(&host->allocation_lock, flags);
5781da177e4SLinus Torvalds 		return FAILED;
5791da177e4SLinus Torvalds 	} else {
5804023c474SAlan Cox 		spin_unlock_irqrestore(&host->allocation_lock, flags);
5811da177e4SLinus Torvalds 		return SUCCESS;
5821da177e4SLinus Torvalds 	}
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds 
5854023c474SAlan Cox /**
5864023c474SAlan Cox  *	orc_device_reset	-	device reset handler
5874023c474SAlan Cox  *	@host: host to reset
5884023c474SAlan Cox  *	@cmd: command causing the reset
589c548a625SLee Jones  *	@target: target device
5904023c474SAlan Cox  *
5914023c474SAlan Cox  *	Reset registers, reset a hanging bus and kill active and disconnected
5924023c474SAlan Cox  *	commands for target w/o soft reset
5934023c474SAlan Cox  */
5941da177e4SLinus Torvalds 
orc_device_reset(struct orc_host * host,struct scsi_cmnd * cmd,unsigned int target)5954023c474SAlan Cox static int orc_device_reset(struct orc_host * host, struct scsi_cmnd *cmd, unsigned int target)
5964023c474SAlan Cox {				/* I need Host Control Block Information */
5974023c474SAlan Cox 	struct orc_scb *scb;
5984023c474SAlan Cox 	struct orc_extended_scb *escb;
5994023c474SAlan Cox 	struct orc_scb *host_scb;
6004023c474SAlan Cox 	u8 i;
6014023c474SAlan Cox 	unsigned long flags;
6024023c474SAlan Cox 
6034023c474SAlan Cox 	spin_lock_irqsave(&(host->allocation_lock), flags);
6044023c474SAlan Cox 	scb = (struct orc_scb *) NULL;
6054023c474SAlan Cox 	escb = (struct orc_extended_scb *) NULL;
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 	/* setup scatter list address with one buffer */
6084023c474SAlan Cox 	host_scb = host->scb_virt;
6091da177e4SLinus Torvalds 
6104023c474SAlan Cox 	/* FIXME: is this safe if we then fail to issue the reset or race
6114023c474SAlan Cox 	   a completion ? */
6124023c474SAlan Cox 	init_alloc_map(host);
6134023c474SAlan Cox 
6144023c474SAlan Cox 	/* Find the scb corresponding to the command */
6151da177e4SLinus Torvalds 	for (i = 0; i < ORC_MAXQUEUE; i++) {
6164023c474SAlan Cox 		escb = host_scb->escb;
6174023c474SAlan Cox 		if (host_scb->status && escb->srb == cmd)
6181da177e4SLinus Torvalds 			break;
6194023c474SAlan Cox 		host_scb++;
6201da177e4SLinus Torvalds 	}
6211da177e4SLinus Torvalds 
6221da177e4SLinus Torvalds 	if (i == ORC_MAXQUEUE) {
6234023c474SAlan Cox 		printk(KERN_ERR "Unable to Reset - No SCB Found\n");
6244023c474SAlan Cox 		spin_unlock_irqrestore(&(host->allocation_lock), flags);
6251da177e4SLinus Torvalds 		return FAILED;
6261da177e4SLinus Torvalds 	}
6271da177e4SLinus Torvalds 
6284023c474SAlan Cox 	/* Allocate a new SCB for the reset command to the firmware */
6294023c474SAlan Cox 	if ((scb = __orc_alloc_scb(host)) == NULL) {
6304023c474SAlan Cox 		/* Can't happen.. */
6314023c474SAlan Cox 		spin_unlock_irqrestore(&(host->allocation_lock), flags);
6324023c474SAlan Cox 		return FAILED;
6334023c474SAlan Cox 	}
6344023c474SAlan Cox 
63589546debSNick Andrew 	/* Reset device is handled by the firmware, we fill in an SCB and
6364023c474SAlan Cox 	   fire it at the controller, it does the rest */
6374023c474SAlan Cox 	scb->opcode = ORC_BUSDEVRST;
6384023c474SAlan Cox 	scb->target = target;
6394023c474SAlan Cox 	scb->hastat = 0;
6404023c474SAlan Cox 	scb->tastat = 0;
6414023c474SAlan Cox 	scb->status = 0x0;
6424023c474SAlan Cox 	scb->link = 0xFF;
6434023c474SAlan Cox 	scb->reserved0 = 0;
6444023c474SAlan Cox 	scb->reserved1 = 0;
645987ff954SMikulas Patocka 	scb->xferlen = cpu_to_le32(0);
646987ff954SMikulas Patocka 	scb->sg_len = cpu_to_le32(0);
6474023c474SAlan Cox 
6484023c474SAlan Cox 	escb->srb = NULL;
6494023c474SAlan Cox 	escb->srb = cmd;
6504023c474SAlan Cox 	orc_exec_scb(host, scb);	/* Start execute SCB            */
6514023c474SAlan Cox 	spin_unlock_irqrestore(&host->allocation_lock, flags);
6521da177e4SLinus Torvalds 	return SUCCESS;
6531da177e4SLinus Torvalds }
6541da177e4SLinus Torvalds 
6554023c474SAlan Cox /**
6564023c474SAlan Cox  *	__orc_alloc_scb		-		allocate an SCB
6574023c474SAlan Cox  *	@host: host to allocate from
6584023c474SAlan Cox  *
6594023c474SAlan Cox  *	Allocate an SCB and return a pointer to the SCB object. NULL
6604023c474SAlan Cox  *	is returned if no SCB is free. The caller must already hold
6614023c474SAlan Cox  *	the allocator lock at this point.
6624023c474SAlan Cox  */
6631da177e4SLinus Torvalds 
6644023c474SAlan Cox 
__orc_alloc_scb(struct orc_host * host)6654023c474SAlan Cox static struct orc_scb *__orc_alloc_scb(struct orc_host * host)
6661da177e4SLinus Torvalds {
6674023c474SAlan Cox 	u8 channel;
6684023c474SAlan Cox 	unsigned long idx;
6694023c474SAlan Cox 	u8 index;
6704023c474SAlan Cox 	u8 i;
6711da177e4SLinus Torvalds 
6724023c474SAlan Cox 	channel = host->index;
6731da177e4SLinus Torvalds 	for (i = 0; i < 8; i++) {
6741da177e4SLinus Torvalds 		for (index = 0; index < 32; index++) {
6754023c474SAlan Cox 			if ((host->allocation_map[channel][i] >> index) & 0x01) {
6764023c474SAlan Cox 				host->allocation_map[channel][i] &= ~(1 << index);
6771da177e4SLinus Torvalds 				idx = index + 32 * i;
67828aef2f7SAkinobu Mita 				/*
67928aef2f7SAkinobu Mita 				 * Translate the index to a structure instance
68028aef2f7SAkinobu Mita 				 */
68128aef2f7SAkinobu Mita 				return host->scb_virt + idx;
68228aef2f7SAkinobu Mita 			}
68328aef2f7SAkinobu Mita 		}
6841da177e4SLinus Torvalds 	}
6854023c474SAlan Cox 	return NULL;
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
6884023c474SAlan Cox /**
6894023c474SAlan Cox  *	orc_alloc_scb		-		allocate an SCB
6904023c474SAlan Cox  *	@host: host to allocate from
6914023c474SAlan Cox  *
6924023c474SAlan Cox  *	Allocate an SCB and return a pointer to the SCB object. NULL
6934023c474SAlan Cox  *	is returned if no SCB is free.
6944023c474SAlan Cox  */
6954023c474SAlan Cox 
orc_alloc_scb(struct orc_host * host)6964023c474SAlan Cox static struct orc_scb *orc_alloc_scb(struct orc_host * host)
6971da177e4SLinus Torvalds {
6984023c474SAlan Cox 	struct orc_scb *scb;
6994023c474SAlan Cox 	unsigned long flags;
7001da177e4SLinus Torvalds 
7014023c474SAlan Cox 	spin_lock_irqsave(&host->allocation_lock, flags);
7024023c474SAlan Cox 	scb = __orc_alloc_scb(host);
7034023c474SAlan Cox 	spin_unlock_irqrestore(&host->allocation_lock, flags);
7044023c474SAlan Cox 	return scb;
7051da177e4SLinus Torvalds }
7061da177e4SLinus Torvalds 
7074023c474SAlan Cox /**
7084023c474SAlan Cox  *	orc_release_scb			-	release an SCB
7094023c474SAlan Cox  *	@host: host owning the SCB
7104023c474SAlan Cox  *	@scb: SCB that is now free
7114023c474SAlan Cox  *
7124023c474SAlan Cox  *	Called to return a completed SCB to the allocation pool. Before
7134023c474SAlan Cox  *	calling the SCB must be out of use on both the host and the HA.
7144023c474SAlan Cox  */
7151da177e4SLinus Torvalds 
orc_release_scb(struct orc_host * host,struct orc_scb * scb)7164023c474SAlan Cox static void orc_release_scb(struct orc_host *host, struct orc_scb *scb)
7171da177e4SLinus Torvalds {
7184023c474SAlan Cox 	unsigned long flags;
7194023c474SAlan Cox 	u8 index, i, channel;
7201da177e4SLinus Torvalds 
7214023c474SAlan Cox 	spin_lock_irqsave(&(host->allocation_lock), flags);
7224023c474SAlan Cox 	channel = host->index;	/* Channel */
7234023c474SAlan Cox 	index = scb->scbidx;
7244023c474SAlan Cox 	i = index / 32;
7254023c474SAlan Cox 	index %= 32;
7264023c474SAlan Cox 	host->allocation_map[channel][i] |= (1 << index);
7274023c474SAlan Cox 	spin_unlock_irqrestore(&(host->allocation_lock), flags);
7281da177e4SLinus Torvalds }
7291da177e4SLinus Torvalds 
730c548a625SLee Jones /*
7314023c474SAlan Cox  *	orchid_abort_scb	-	abort a command
7324023c474SAlan Cox  *
7334023c474SAlan Cox  *	Abort a queued command that has been passed to the firmware layer
7344023c474SAlan Cox  *	if possible. This is all handled by the firmware. We aks the firmware
7354023c474SAlan Cox  *	and it either aborts the command or fails
7364023c474SAlan Cox  */
7371da177e4SLinus Torvalds 
orchid_abort_scb(struct orc_host * host,struct orc_scb * scb)7384023c474SAlan Cox static int orchid_abort_scb(struct orc_host * host, struct orc_scb * scb)
7394023c474SAlan Cox {
7404023c474SAlan Cox 	unsigned char data, status;
7414023c474SAlan Cox 
7424023c474SAlan Cox 	outb(ORC_CMD_ABORT_SCB, host->base + ORC_HDATA);	/* Write command */
7434023c474SAlan Cox 	outb(HDO, host->base + ORC_HCTRL);
7444023c474SAlan Cox 	if (wait_HDO_off(host) == 0)	/* Wait HDO off   */
7451da177e4SLinus Torvalds 		return 0;
7461da177e4SLinus Torvalds 
7474023c474SAlan Cox 	outb(scb->scbidx, host->base + ORC_HDATA);	/* Write address */
7484023c474SAlan Cox 	outb(HDO, host->base + ORC_HCTRL);
7494023c474SAlan Cox 	if (wait_HDO_off(host) == 0)	/* Wait HDO off   */
7501da177e4SLinus Torvalds 		return 0;
7511da177e4SLinus Torvalds 
7524023c474SAlan Cox 	if (wait_hdi_set(host, &data) == 0)	/* Wait HDI set   */
7531da177e4SLinus Torvalds 		return 0;
7544023c474SAlan Cox 	status = inb(host->base + ORC_HDATA);
7554023c474SAlan Cox 	outb(data, host->base + ORC_HSTUS);	/* Clear HDI    */
7561da177e4SLinus Torvalds 
7574023c474SAlan Cox 	if (status == 1)	/* 0 - Successfully               */
7581da177e4SLinus Torvalds 		return 0;	/* 1 - Fail                     */
7591da177e4SLinus Torvalds 	return 1;
7601da177e4SLinus Torvalds }
7611da177e4SLinus Torvalds 
inia100_abort_cmd(struct orc_host * host,struct scsi_cmnd * cmd)7624023c474SAlan Cox static int inia100_abort_cmd(struct orc_host * host, struct scsi_cmnd *cmd)
7631da177e4SLinus Torvalds {
7644023c474SAlan Cox 	struct orc_extended_scb *escb;
7654023c474SAlan Cox 	struct orc_scb *scb;
7664023c474SAlan Cox 	u8 i;
7674023c474SAlan Cox 	unsigned long flags;
7681da177e4SLinus Torvalds 
7694023c474SAlan Cox 	spin_lock_irqsave(&(host->allocation_lock), flags);
7701da177e4SLinus Torvalds 
7714023c474SAlan Cox 	scb = host->scb_virt;
7721da177e4SLinus Torvalds 
7734023c474SAlan Cox 	/* Walk the queue until we find the SCB that belongs to the command
7744023c474SAlan Cox 	   block. This isn't a performance critical path so a walk in the park
7754023c474SAlan Cox 	   here does no harm */
7764023c474SAlan Cox 
7774023c474SAlan Cox 	for (i = 0; i < ORC_MAXQUEUE; i++, scb++) {
7784023c474SAlan Cox 		escb = scb->escb;
7794023c474SAlan Cox 		if (scb->status && escb->srb == cmd) {
7804023c474SAlan Cox 			if (scb->tag_msg == 0) {
7814023c474SAlan Cox 				goto out;
7821da177e4SLinus Torvalds 			} else {
7834023c474SAlan Cox 				/* Issue an ABORT to the firmware */
7844023c474SAlan Cox 				if (orchid_abort_scb(host, scb)) {
7854023c474SAlan Cox 					escb->srb = NULL;
7864023c474SAlan Cox 					spin_unlock_irqrestore(&host->allocation_lock, flags);
7871da177e4SLinus Torvalds 					return SUCCESS;
7884023c474SAlan Cox 				} else
7894023c474SAlan Cox 					goto out;
7901da177e4SLinus Torvalds 			}
7911da177e4SLinus Torvalds 		}
7921da177e4SLinus Torvalds 	}
7934023c474SAlan Cox out:
7944023c474SAlan Cox 	spin_unlock_irqrestore(&host->allocation_lock, flags);
7951da177e4SLinus Torvalds 	return FAILED;
7961da177e4SLinus Torvalds }
7971da177e4SLinus Torvalds 
7984023c474SAlan Cox /**
7994023c474SAlan Cox  *	orc_interrupt		-	IRQ processing
8004023c474SAlan Cox  *	@host: Host causing the interrupt
8014023c474SAlan Cox  *
8024023c474SAlan Cox  *	This function is called from the IRQ handler and protected
8034023c474SAlan Cox  *	by the host lock. While the controller reports that there are
8044023c474SAlan Cox  *	scb's for processing we pull them off the controller, turn the
8054023c474SAlan Cox  *	index into a host address pointer to the scb and call the scb
8064023c474SAlan Cox  *	handler.
8074023c474SAlan Cox  *
8084023c474SAlan Cox  *	Returns IRQ_HANDLED if any SCBs were processed, IRQ_NONE otherwise
8094023c474SAlan Cox  */
8104023c474SAlan Cox 
orc_interrupt(struct orc_host * host)8114023c474SAlan Cox static irqreturn_t orc_interrupt(struct orc_host * host)
8121da177e4SLinus Torvalds {
8134023c474SAlan Cox 	u8 scb_index;
8144023c474SAlan Cox 	struct orc_scb *scb;
8151da177e4SLinus Torvalds 
8164023c474SAlan Cox 	/* Check if we have an SCB queued for servicing */
8174023c474SAlan Cox 	if (inb(host->base + ORC_RQUEUECNT) == 0)
8184023c474SAlan Cox 		return IRQ_NONE;
8191da177e4SLinus Torvalds 
8201da177e4SLinus Torvalds 	do {
8214023c474SAlan Cox 		/* Get the SCB index of the SCB to service */
8224023c474SAlan Cox 		scb_index = inb(host->base + ORC_RQUEUE);
8231da177e4SLinus Torvalds 
8244023c474SAlan Cox 		/* Translate it back to a host pointer */
8254023c474SAlan Cox 		scb = (struct orc_scb *) ((unsigned long) host->scb_virt + (unsigned long) (sizeof(struct orc_scb) * scb_index));
8264023c474SAlan Cox 		scb->status = 0x0;
8274023c474SAlan Cox 		/* Process the SCB */
8284023c474SAlan Cox 		inia100_scb_handler(host, scb);
8294023c474SAlan Cox 	} while (inb(host->base + ORC_RQUEUECNT));
8304023c474SAlan Cox 	return IRQ_HANDLED;
8311da177e4SLinus Torvalds }				/* End of I1060Interrupt() */
8321da177e4SLinus Torvalds 
8334023c474SAlan Cox /**
8344023c474SAlan Cox  *	inia100_build_scb	-	build SCB
8354023c474SAlan Cox  *	@host: host owing the control block
8364023c474SAlan Cox  *	@scb: control block to use
8374023c474SAlan Cox  *	@cmd: Mid layer command
8384023c474SAlan Cox  *
8394023c474SAlan Cox  *	Build a host adapter control block from the SCSI mid layer command
8404023c474SAlan Cox  */
8414023c474SAlan Cox 
inia100_build_scb(struct orc_host * host,struct orc_scb * scb,struct scsi_cmnd * cmd)8423a628b0fSMikulas Patocka static int inia100_build_scb(struct orc_host * host, struct orc_scb * scb, struct scsi_cmnd * cmd)
8431da177e4SLinus Torvalds {				/* Create corresponding SCB     */
844985c0a72SFUJITA Tomonori 	struct scatterlist *sg;
8454023c474SAlan Cox 	struct orc_sgent *sgent;		/* Pointer to SG list           */
8461da177e4SLinus Torvalds 	int i, count_sg;
8474023c474SAlan Cox 	struct orc_extended_scb *escb;
8481da177e4SLinus Torvalds 
8494023c474SAlan Cox 	/* Links between the escb, scb and Linux scsi midlayer cmd */
8504023c474SAlan Cox 	escb = scb->escb;
8514023c474SAlan Cox 	escb->srb = cmd;
8524023c474SAlan Cox 	sgent = NULL;
8531da177e4SLinus Torvalds 
8544023c474SAlan Cox 	/* Set up the SCB to do a SCSI command block */
8554023c474SAlan Cox 	scb->opcode = ORC_EXECSCSI;
8564023c474SAlan Cox 	scb->flags = SCF_NO_DCHK;	/* Clear done bit               */
8574023c474SAlan Cox 	scb->target = cmd->device->id;
8584023c474SAlan Cox 	scb->lun = cmd->device->lun;
8594023c474SAlan Cox 	scb->reserved0 = 0;
8604023c474SAlan Cox 	scb->reserved1 = 0;
861987ff954SMikulas Patocka 	scb->sg_len = cpu_to_le32(0);
8621da177e4SLinus Torvalds 
863987ff954SMikulas Patocka 	scb->xferlen = cpu_to_le32((u32) scsi_bufflen(cmd));
8644023c474SAlan Cox 	sgent = (struct orc_sgent *) & escb->sglist[0];
865985c0a72SFUJITA Tomonori 
8664023c474SAlan Cox 	count_sg = scsi_dma_map(cmd);
8673a628b0fSMikulas Patocka 	if (count_sg < 0)
8683a628b0fSMikulas Patocka 		return count_sg;
869a5db3341SMikulas Patocka 	BUG_ON(count_sg > TOTAL_SG_ENTRY);
8704023c474SAlan Cox 
8714023c474SAlan Cox 	/* Build the scatter gather lists */
872985c0a72SFUJITA Tomonori 	if (count_sg) {
873987ff954SMikulas Patocka 		scb->sg_len = cpu_to_le32((u32) (count_sg * 8));
8744023c474SAlan Cox 		scsi_for_each_sg(cmd, sg, count_sg, i) {
875987ff954SMikulas Patocka 			sgent->base = cpu_to_le32((u32) sg_dma_address(sg));
876987ff954SMikulas Patocka 			sgent->length = cpu_to_le32((u32) sg_dma_len(sg));
8774023c474SAlan Cox 			sgent++;
8781da177e4SLinus Torvalds 		}
8791da177e4SLinus Torvalds 	} else {
880987ff954SMikulas Patocka 		scb->sg_len = cpu_to_le32(0);
881987ff954SMikulas Patocka 		sgent->base = cpu_to_le32(0);
882987ff954SMikulas Patocka 		sgent->length = cpu_to_le32(0);
8831da177e4SLinus Torvalds 	}
884987ff954SMikulas Patocka 	scb->sg_addr = (u32) scb->sense_addr;	/* sense_addr is already little endian */
8854023c474SAlan Cox 	scb->hastat = 0;
8864023c474SAlan Cox 	scb->tastat = 0;
8874023c474SAlan Cox 	scb->link = 0xFF;
8884023c474SAlan Cox 	scb->sense_len = SENSE_SIZE;
8894023c474SAlan Cox 	scb->cdb_len = cmd->cmd_len;
8904023c474SAlan Cox 	if (scb->cdb_len >= IMAX_CDB) {
891d23684d2SColin Ian King 		printk("max cdb length= %x\n", cmd->cmd_len);
8924023c474SAlan Cox 		scb->cdb_len = IMAX_CDB;
8931da177e4SLinus Torvalds 	}
8949cb78c16SHannes Reinecke 	scb->ident = (u8)(cmd->device->lun & 0xff) | DISC_ALLOW;
8954023c474SAlan Cox 	if (cmd->device->tagged_supported) {	/* Tag Support                  */
8964023c474SAlan Cox 		scb->tag_msg = SIMPLE_QUEUE_TAG;	/* Do simple tag only   */
8971da177e4SLinus Torvalds 	} else {
8984023c474SAlan Cox 		scb->tag_msg = 0;	/* No tag support               */
8991da177e4SLinus Torvalds 	}
90064a87b24SBoaz Harrosh 	memcpy(scb->cdb, cmd->cmnd, scb->cdb_len);
9013a628b0fSMikulas Patocka 	return 0;
9021da177e4SLinus Torvalds }
9031da177e4SLinus Torvalds 
9044023c474SAlan Cox /**
905c548a625SLee Jones  *	inia100_queue_lck		-	queue command with host
9064023c474SAlan Cox  *	@cmd: Command block
9074023c474SAlan Cox  *
9084023c474SAlan Cox  *	Called by the mid layer to queue a command. Process the command
9094023c474SAlan Cox  *	block, build the host specific scb structures and if there is room
9104023c474SAlan Cox  *	queue the command down to the controller
9114023c474SAlan Cox  */
inia100_queue_lck(struct scsi_cmnd * cmd)912af049dfdSBart Van Assche static int inia100_queue_lck(struct scsi_cmnd *cmd)
9131da177e4SLinus Torvalds {
9144023c474SAlan Cox 	struct orc_scb *scb;
9154023c474SAlan Cox 	struct orc_host *host;		/* Point to Host adapter control block */
9161da177e4SLinus Torvalds 
9174023c474SAlan Cox 	host = (struct orc_host *) cmd->device->host->hostdata;
9181da177e4SLinus Torvalds 	/* Get free SCSI control block  */
9194023c474SAlan Cox 	if ((scb = orc_alloc_scb(host)) == NULL)
9201da177e4SLinus Torvalds 		return SCSI_MLQUEUE_HOST_BUSY;
9211da177e4SLinus Torvalds 
9223a628b0fSMikulas Patocka 	if (inia100_build_scb(host, scb, cmd)) {
9233a628b0fSMikulas Patocka 		orc_release_scb(host, scb);
9243a628b0fSMikulas Patocka 		return SCSI_MLQUEUE_HOST_BUSY;
9253a628b0fSMikulas Patocka 	}
9264023c474SAlan Cox 	orc_exec_scb(host, scb);	/* Start execute SCB            */
9274023c474SAlan Cox 	return 0;
9281da177e4SLinus Torvalds }
9291da177e4SLinus Torvalds 
DEF_SCSI_QCMD(inia100_queue)930f281233dSJeff Garzik static DEF_SCSI_QCMD(inia100_queue)
931f281233dSJeff Garzik 
9321da177e4SLinus Torvalds /*****************************************************************************
9331da177e4SLinus Torvalds  Function name  : inia100_abort
9341da177e4SLinus Torvalds  Description    : Abort a queued command.
9351da177e4SLinus Torvalds 	                 (commands that are on the bus can't be aborted easily)
9364023c474SAlan Cox  Input          : host  -       Pointer to host adapter structure
9371da177e4SLinus Torvalds  Output         : None.
9381da177e4SLinus Torvalds  Return         : pSRB  -       Pointer to SCSI request block.
9391da177e4SLinus Torvalds *****************************************************************************/
9404023c474SAlan Cox static int inia100_abort(struct scsi_cmnd * cmd)
9411da177e4SLinus Torvalds {
9424023c474SAlan Cox 	struct orc_host *host;
9431da177e4SLinus Torvalds 
9444023c474SAlan Cox 	host = (struct orc_host *) cmd->device->host->hostdata;
9454023c474SAlan Cox 	return inia100_abort_cmd(host, cmd);
9461da177e4SLinus Torvalds }
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds /*****************************************************************************
9491da177e4SLinus Torvalds  Function name  : inia100_reset
9501da177e4SLinus Torvalds  Description    : Reset registers, reset a hanging bus and
9511da177e4SLinus Torvalds                   kill active and disconnected commands for target w/o soft reset
9524023c474SAlan Cox  Input          : host  -       Pointer to host adapter structure
9531da177e4SLinus Torvalds  Output         : None.
9541da177e4SLinus Torvalds  Return         : pSRB  -       Pointer to SCSI request block.
9551da177e4SLinus Torvalds *****************************************************************************/
inia100_bus_reset(struct scsi_cmnd * cmd)9564023c474SAlan Cox static int inia100_bus_reset(struct scsi_cmnd * cmd)
9571da177e4SLinus Torvalds {				/* I need Host Control Block Information */
9584023c474SAlan Cox 	struct orc_host *host;
9594023c474SAlan Cox 	host = (struct orc_host *) cmd->device->host->hostdata;
9604023c474SAlan Cox 	return orc_reset_scsi_bus(host);
9611da177e4SLinus Torvalds }
9621da177e4SLinus Torvalds 
9631da177e4SLinus Torvalds /*****************************************************************************
9641da177e4SLinus Torvalds  Function name  : inia100_device_reset
9651da177e4SLinus Torvalds  Description    : Reset the device
9664023c474SAlan Cox  Input          : host  -       Pointer to host adapter structure
9671da177e4SLinus Torvalds  Output         : None.
9681da177e4SLinus Torvalds  Return         : pSRB  -       Pointer to SCSI request block.
9691da177e4SLinus Torvalds *****************************************************************************/
inia100_device_reset(struct scsi_cmnd * cmd)9704023c474SAlan Cox static int inia100_device_reset(struct scsi_cmnd * cmd)
9711da177e4SLinus Torvalds {				/* I need Host Control Block Information */
9724023c474SAlan Cox 	struct orc_host *host;
9734023c474SAlan Cox 	host = (struct orc_host *) cmd->device->host->hostdata;
9744023c474SAlan Cox 	return orc_device_reset(host, cmd, scmd_id(cmd));
9751da177e4SLinus Torvalds 
9761da177e4SLinus Torvalds }
9771da177e4SLinus Torvalds 
9784023c474SAlan Cox /**
9794023c474SAlan Cox  *	inia100_scb_handler	-	interrupt callback
9804023c474SAlan Cox  *	@host: Host causing the interrupt
9814023c474SAlan Cox  *	@scb: SCB the controller returned as needing processing
9824023c474SAlan Cox  *
9834023c474SAlan Cox  *	Perform completion processing on a control block. Do the conversions
9844023c474SAlan Cox  *	from host to SCSI midlayer error coding, save any sense data and
9854023c474SAlan Cox  *	the complete with the midlayer and recycle the scb.
9864023c474SAlan Cox  */
9871da177e4SLinus Torvalds 
inia100_scb_handler(struct orc_host * host,struct orc_scb * scb)9884023c474SAlan Cox static void inia100_scb_handler(struct orc_host *host, struct orc_scb *scb)
9894023c474SAlan Cox {
9904023c474SAlan Cox 	struct scsi_cmnd *cmd;	/* Pointer to SCSI request block */
9914023c474SAlan Cox 	struct orc_extended_scb *escb;
9924023c474SAlan Cox 
9934023c474SAlan Cox 	escb = scb->escb;
9944023c474SAlan Cox 	if ((cmd = (struct scsi_cmnd *) escb->srb) == NULL) {
9954023c474SAlan Cox 		printk(KERN_ERR "inia100_scb_handler: SRB pointer is empty\n");
9964023c474SAlan Cox 		orc_release_scb(host, scb);	/* Release SCB for current channel */
9971da177e4SLinus Torvalds 		return;
9981da177e4SLinus Torvalds 	}
9994023c474SAlan Cox 	escb->srb = NULL;
10001da177e4SLinus Torvalds 
10014023c474SAlan Cox 	switch (scb->hastat) {
10021da177e4SLinus Torvalds 	case 0x0:
10031da177e4SLinus Torvalds 	case 0xa:		/* Linked command complete without error and linked normally */
10041da177e4SLinus Torvalds 	case 0xb:		/* Linked command complete without error interrupt generated */
10054023c474SAlan Cox 		scb->hastat = 0;
10061da177e4SLinus Torvalds 		break;
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds 	case 0x11:		/* Selection time out-The initiator selection or target
10091da177e4SLinus Torvalds 				   reselection was not complete within the SCSI Time out period */
10104023c474SAlan Cox 		scb->hastat = DID_TIME_OUT;
10111da177e4SLinus Torvalds 		break;
10121da177e4SLinus Torvalds 
10131da177e4SLinus Torvalds 	case 0x14:		/* Target bus phase sequence failure-An invalid bus phase or bus
10141da177e4SLinus Torvalds 				   phase sequence was requested by the target. The host adapter
10151da177e4SLinus Torvalds 				   will generate a SCSI Reset Condition, notifying the host with
10161da177e4SLinus Torvalds 				   a SCRD interrupt */
10174023c474SAlan Cox 		scb->hastat = DID_RESET;
10181da177e4SLinus Torvalds 		break;
10191da177e4SLinus Torvalds 
10201da177e4SLinus Torvalds 	case 0x1a:		/* SCB Aborted. 07/21/98 */
10214023c474SAlan Cox 		scb->hastat = DID_ABORT;
10221da177e4SLinus Torvalds 		break;
10231da177e4SLinus Torvalds 
10241da177e4SLinus Torvalds 	case 0x12:		/* Data overrun/underrun-The target attempted to transfer more data
10251da177e4SLinus Torvalds 				   than was allocated by the Data Length field or the sum of the
10261da177e4SLinus Torvalds 				   Scatter / Gather Data Length fields. */
10271da177e4SLinus Torvalds 	case 0x13:		/* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
10281da177e4SLinus Torvalds 	case 0x16:		/* Invalid CCB Operation Code-The first byte of the CCB was invalid. */
10291da177e4SLinus Torvalds 
10301da177e4SLinus Torvalds 	default:
10314023c474SAlan Cox 		printk(KERN_DEBUG "inia100: %x %x\n", scb->hastat, scb->tastat);
10324023c474SAlan Cox 		scb->hastat = DID_ERROR;	/* Couldn't find any better */
10331da177e4SLinus Torvalds 		break;
10341da177e4SLinus Torvalds 	}
10351da177e4SLinus Torvalds 
10364023c474SAlan Cox 	if (scb->tastat == 2) {	/* Check condition              */
10374023c474SAlan Cox 		memcpy((unsigned char *) &cmd->sense_buffer[0],
10384023c474SAlan Cox 		   (unsigned char *) &escb->sglist[0], SENSE_SIZE);
10391da177e4SLinus Torvalds 	}
10404023c474SAlan Cox 	cmd->result = scb->tastat | (scb->hastat << 16);
10414023c474SAlan Cox 	scsi_dma_unmap(cmd);
1042e42be9e7SBart Van Assche 	scsi_done(cmd);		/* Notify system DONE           */
10434023c474SAlan Cox 	orc_release_scb(host, scb);	/* Release SCB for current channel */
10441da177e4SLinus Torvalds }
10451da177e4SLinus Torvalds 
10464023c474SAlan Cox /**
10474023c474SAlan Cox  *	inia100_intr		-	interrupt handler
10484023c474SAlan Cox  *	@irqno: Interrupt value
10494023c474SAlan Cox  *	@devid: Host adapter
10504023c474SAlan Cox  *
10514023c474SAlan Cox  *	Entry point for IRQ handling. All the real work is performed
10524023c474SAlan Cox  *	by orc_interrupt.
10531da177e4SLinus Torvalds  */
inia100_intr(int irqno,void * devid)10547d12e780SDavid Howells static irqreturn_t inia100_intr(int irqno, void *devid)
10551da177e4SLinus Torvalds {
10564023c474SAlan Cox 	struct Scsi_Host *shost = (struct Scsi_Host *)devid;
10574023c474SAlan Cox 	struct orc_host *host = (struct orc_host *)shost->hostdata;
10581da177e4SLinus Torvalds 	unsigned long flags;
10594023c474SAlan Cox 	irqreturn_t res;
10601da177e4SLinus Torvalds 
10614023c474SAlan Cox 	spin_lock_irqsave(shost->host_lock, flags);
10624023c474SAlan Cox 	res = orc_interrupt(host);
10634023c474SAlan Cox 	spin_unlock_irqrestore(shost->host_lock, flags);
10641da177e4SLinus Torvalds 
10654023c474SAlan Cox 	return res;
10661da177e4SLinus Torvalds }
10671da177e4SLinus Torvalds 
1068*157fc774SBart Van Assche static const struct scsi_host_template inia100_template = {
10691da177e4SLinus Torvalds 	.proc_name		= "inia100",
10701da177e4SLinus Torvalds 	.name			= inia100_REVID,
10711da177e4SLinus Torvalds 	.queuecommand		= inia100_queue,
10721da177e4SLinus Torvalds 	.eh_abort_handler	= inia100_abort,
10731da177e4SLinus Torvalds 	.eh_bus_reset_handler	= inia100_bus_reset,
10741da177e4SLinus Torvalds 	.eh_device_reset_handler = inia100_device_reset,
10751da177e4SLinus Torvalds 	.can_queue		= 1,
10761da177e4SLinus Torvalds 	.this_id		= 1,
10771da177e4SLinus Torvalds 	.sg_tablesize		= SG_ALL,
10781da177e4SLinus Torvalds };
10791da177e4SLinus Torvalds 
inia100_probe_one(struct pci_dev * pdev,const struct pci_device_id * id)10806f039790SGreg Kroah-Hartman static int inia100_probe_one(struct pci_dev *pdev,
10811da177e4SLinus Torvalds 			     const struct pci_device_id *id)
10821da177e4SLinus Torvalds {
10831da177e4SLinus Torvalds 	struct Scsi_Host *shost;
10844023c474SAlan Cox 	struct orc_host *host;
10851da177e4SLinus Torvalds 	unsigned long port, bios;
10861da177e4SLinus Torvalds 	int error = -ENODEV;
10871da177e4SLinus Torvalds 	u32 sz;
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 	if (pci_enable_device(pdev))
10901da177e4SLinus Torvalds 		goto out;
10914d431b18SChristoph Hellwig 	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
10921da177e4SLinus Torvalds 		printk(KERN_WARNING "Unable to set 32bit DMA "
10931da177e4SLinus Torvalds 				    "on inia100 adapter, ignoring.\n");
10941da177e4SLinus Torvalds 		goto out_disable_device;
10951da177e4SLinus Torvalds 	}
10961da177e4SLinus Torvalds 
10971da177e4SLinus Torvalds 	pci_set_master(pdev);
10981da177e4SLinus Torvalds 
10991da177e4SLinus Torvalds 	port = pci_resource_start(pdev, 0);
11001da177e4SLinus Torvalds 	if (!request_region(port, 256, "inia100")) {
11011da177e4SLinus Torvalds 		printk(KERN_WARNING "inia100: io port 0x%lx, is busy.\n", port);
11021da177e4SLinus Torvalds 		goto out_disable_device;
11031da177e4SLinus Torvalds 	}
11041da177e4SLinus Torvalds 
11054c3ee826SRobert P. J. Day 	/* <02> read from base address + 0x50 offset to get the bios value. */
11064023c474SAlan Cox 	bios = inw(port + 0x50);
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds 
11094023c474SAlan Cox 	shost = scsi_host_alloc(&inia100_template, sizeof(struct orc_host));
11101da177e4SLinus Torvalds 	if (!shost)
11111da177e4SLinus Torvalds 		goto out_release_region;
11121da177e4SLinus Torvalds 
11134023c474SAlan Cox 	host = (struct orc_host *)shost->hostdata;
11144023c474SAlan Cox 	host->pdev = pdev;
11154023c474SAlan Cox 	host->base = port;
11164023c474SAlan Cox 	host->BIOScfg = bios;
11174023c474SAlan Cox 	spin_lock_init(&host->allocation_lock);
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 	/* Get total memory needed for SCB */
11204023c474SAlan Cox 	sz = ORC_MAXQUEUE * sizeof(struct orc_scb);
1121750afb08SLuis Chamberlain 	host->scb_virt = dma_alloc_coherent(&pdev->dev, sz, &host->scb_phys,
11224d431b18SChristoph Hellwig 					    GFP_KERNEL);
11234023c474SAlan Cox 	if (!host->scb_virt) {
11241da177e4SLinus Torvalds 		printk("inia100: SCB memory allocation error\n");
11251da177e4SLinus Torvalds 		goto out_host_put;
11261da177e4SLinus Torvalds 	}
11271da177e4SLinus Torvalds 
11281da177e4SLinus Torvalds 	/* Get total memory needed for ESCB */
11294023c474SAlan Cox 	sz = ORC_MAXQUEUE * sizeof(struct orc_extended_scb);
1130750afb08SLuis Chamberlain 	host->escb_virt = dma_alloc_coherent(&pdev->dev, sz, &host->escb_phys,
11314d431b18SChristoph Hellwig 					     GFP_KERNEL);
11324023c474SAlan Cox 	if (!host->escb_virt) {
11331da177e4SLinus Torvalds 		printk("inia100: ESCB memory allocation error\n");
11341da177e4SLinus Torvalds 		goto out_free_scb_array;
11351da177e4SLinus Torvalds 	}
11361da177e4SLinus Torvalds 
11374023c474SAlan Cox 	if (init_orchid(host)) {	/* Initialize orchid chip */
11381da177e4SLinus Torvalds 		printk("inia100: initial orchid fail!!\n");
11391da177e4SLinus Torvalds 		goto out_free_escb_array;
11401da177e4SLinus Torvalds 	}
11411da177e4SLinus Torvalds 
11424023c474SAlan Cox 	shost->io_port = host->base;
11431da177e4SLinus Torvalds 	shost->n_io_port = 0xff;
11441da177e4SLinus Torvalds 	shost->can_queue = ORC_MAXQUEUE;
11451da177e4SLinus Torvalds 	shost->unique_id = shost->io_port;
11464023c474SAlan Cox 	shost->max_id = host->max_targets;
11471da177e4SLinus Torvalds 	shost->max_lun = 16;
11484023c474SAlan Cox 	shost->irq = pdev->irq;
11494023c474SAlan Cox 	shost->this_id = host->scsi_id;	/* Assign HCS index */
11501da177e4SLinus Torvalds 	shost->sg_tablesize = TOTAL_SG_ENTRY;
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds 	/* Initial orc chip           */
11531d6f359aSThomas Gleixner 	error = request_irq(pdev->irq, inia100_intr, IRQF_SHARED,
11541da177e4SLinus Torvalds 			"inia100", shost);
11551da177e4SLinus Torvalds 	if (error < 0) {
11561da177e4SLinus Torvalds 		printk(KERN_WARNING "inia100: unable to get irq %d\n",
11571da177e4SLinus Torvalds 				pdev->irq);
11581da177e4SLinus Torvalds 		goto out_free_escb_array;
11591da177e4SLinus Torvalds 	}
11601da177e4SLinus Torvalds 
11611da177e4SLinus Torvalds 	pci_set_drvdata(pdev, shost);
11621da177e4SLinus Torvalds 
11631da177e4SLinus Torvalds 	error = scsi_add_host(shost, &pdev->dev);
11641da177e4SLinus Torvalds 	if (error)
11651da177e4SLinus Torvalds 		goto out_free_irq;
11661da177e4SLinus Torvalds 
11671da177e4SLinus Torvalds 	scsi_scan_host(shost);
11681da177e4SLinus Torvalds 	return 0;
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds out_free_irq:
11711da177e4SLinus Torvalds         free_irq(shost->irq, shost);
11721da177e4SLinus Torvalds out_free_escb_array:
11734d431b18SChristoph Hellwig 	dma_free_coherent(&pdev->dev,
11744d431b18SChristoph Hellwig 			ORC_MAXQUEUE * sizeof(struct orc_extended_scb),
11754023c474SAlan Cox 			host->escb_virt, host->escb_phys);
11761da177e4SLinus Torvalds out_free_scb_array:
11774d431b18SChristoph Hellwig 	dma_free_coherent(&pdev->dev,
11784d431b18SChristoph Hellwig 			ORC_MAXQUEUE * sizeof(struct orc_scb),
11794023c474SAlan Cox 			host->scb_virt, host->scb_phys);
11801da177e4SLinus Torvalds out_host_put:
11811da177e4SLinus Torvalds 	scsi_host_put(shost);
11821da177e4SLinus Torvalds out_release_region:
11831da177e4SLinus Torvalds         release_region(port, 256);
11841da177e4SLinus Torvalds out_disable_device:
11851da177e4SLinus Torvalds 	pci_disable_device(pdev);
11861da177e4SLinus Torvalds out:
11871da177e4SLinus Torvalds 	return error;
11881da177e4SLinus Torvalds }
11891da177e4SLinus Torvalds 
inia100_remove_one(struct pci_dev * pdev)11906f039790SGreg Kroah-Hartman static void inia100_remove_one(struct pci_dev *pdev)
11911da177e4SLinus Torvalds {
11921da177e4SLinus Torvalds 	struct Scsi_Host *shost = pci_get_drvdata(pdev);
11934023c474SAlan Cox 	struct orc_host *host = (struct orc_host *)shost->hostdata;
11941da177e4SLinus Torvalds 
11951da177e4SLinus Torvalds 	scsi_remove_host(shost);
11961da177e4SLinus Torvalds 
11971da177e4SLinus Torvalds         free_irq(shost->irq, shost);
11984d431b18SChristoph Hellwig 	dma_free_coherent(&pdev->dev,
11994d431b18SChristoph Hellwig 			ORC_MAXQUEUE * sizeof(struct orc_extended_scb),
12004023c474SAlan Cox 			host->escb_virt, host->escb_phys);
12014d431b18SChristoph Hellwig 	dma_free_coherent(&pdev->dev,
12024d431b18SChristoph Hellwig 			ORC_MAXQUEUE * sizeof(struct orc_scb),
12034023c474SAlan Cox 			host->scb_virt, host->scb_phys);
12041da177e4SLinus Torvalds         release_region(shost->io_port, 256);
12051da177e4SLinus Torvalds 
12061da177e4SLinus Torvalds 	scsi_host_put(shost);
12071da177e4SLinus Torvalds }
12081da177e4SLinus Torvalds 
12091da177e4SLinus Torvalds static struct pci_device_id inia100_pci_tbl[] = {
12101da177e4SLinus Torvalds 	{PCI_VENDOR_ID_INIT, 0x1060, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
12111da177e4SLinus Torvalds 	{0,}
12121da177e4SLinus Torvalds };
12131da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, inia100_pci_tbl);
12141da177e4SLinus Torvalds 
12151da177e4SLinus Torvalds static struct pci_driver inia100_pci_driver = {
12161da177e4SLinus Torvalds 	.name		= "inia100",
12171da177e4SLinus Torvalds 	.id_table	= inia100_pci_tbl,
12181da177e4SLinus Torvalds 	.probe		= inia100_probe_one,
12196f039790SGreg Kroah-Hartman 	.remove		= inia100_remove_one,
12201da177e4SLinus Torvalds };
12211da177e4SLinus Torvalds 
12229407253fSYueHaibing module_pci_driver(inia100_pci_driver);
12231da177e4SLinus Torvalds 
12241da177e4SLinus Torvalds MODULE_DESCRIPTION("Initio A100U2W SCSI driver");
12251da177e4SLinus Torvalds MODULE_AUTHOR("Initio Corporation");
12261da177e4SLinus Torvalds MODULE_LICENSE("Dual BSD/GPL");
1227